13 Commits

Author SHA1 Message Date
8912212d8e build: Fix 32-bit.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-02-27 19:10:48 -05:00
6a346bf940 http: Move websocket send to http, obvs. 2025-02-27 19:07:01 -05:00
b5bdae4611 js: prettier
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 29m58s
2025-02-27 15:00:37 -05:00
6590da5793 js: Just delete some placeholder comments around code that I am trying to delete. 2025-02-27 15:00:02 -05:00
eb2b426ec7 js: Clean up some oddness in old code as I struggle to replace it. 2025-02-27 14:28:07 -05:00
4864a0411f js: This RPC info does not need to be global. 2025-02-27 13:52:24 -05:00
68590cae33 http: Avoid a potential null dereference.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 30m0s
2025-02-27 11:25:38 -05:00
abf2bbaec2 js: Very minor trimming. 2025-02-27 10:01:59 -05:00
0da7e2722f js: Add a place to start moving imports to C. 2025-02-27 09:58:15 -05:00
60d4b06057 http: Prevent reentering tf_http_destroy. 2025-02-27 08:52:42 -05:00
4c3df34950 build: Let's start work on 0.0.29.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 30m9s
2025-02-26 20:04:17 -05:00
7737e60b52 build: ios => CFBundleVersion=10.
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2025-02-26 19:44:00 -05:00
71e816bc13 build: nix => 0.0.28. 2025-02-26 19:43:20 -05:00
14 changed files with 178 additions and 213 deletions

View File

@ -16,9 +16,9 @@ MAKEFLAGS += --no-builtin-rules
## LD := Linker. ## LD := Linker.
## ANDROID_SDK := Path to the Android SDK. ## ANDROID_SDK := Path to the Android SDK.
VERSION_CODE := 33 VERSION_CODE := 34
VERSION_CODE_IOS := 9 VERSION_CODE_IOS := 11
VERSION_NUMBER := 0.0.28 VERSION_NUMBER := 0.0.29-wip
VERSION_NAME := This program kills fascists. VERSION_NAME := This program kills fascists.
IPHONEOS_VERSION_MIN=14.0 IPHONEOS_VERSION_MIN=14.0

View File

@ -1,53 +1,26 @@
import * as core from './core.js'; import * as core from './core.js';
let g_next_id = 1;
let g_calls = {};
let gSessionIndex = 0; let gSessionIndex = 0;
/**
* TODOC
* @returns
*/
function makeSessionId() {
return 'session_' + (gSessionIndex++).toString();
}
/**
* TODOC
* @returns
*/
function App() { function App() {
this._on_output = null;
this._send_queue = []; this._send_queue = [];
this.calls = {};
this._next_call_id = 1;
return this; return this;
} }
/**
* TODOC
* @param {*} callback
*/
App.prototype.readOutput = function (callback) {
this._on_output = callback;
};
/**
* TODOC
* @param {*} api
* @returns
*/
App.prototype.makeFunction = function (api) { App.prototype.makeFunction = function (api) {
let self = this; let self = this;
let result = function () { let result = function () {
let id = g_next_id++; let id = self._next_call_id++;
while (!id || g_calls[id]) { while (!id || self.calls[id]) {
id = g_next_id++; id = self._next_call_id++;
} }
let promise = new Promise(function (resolve, reject) { let promise = new Promise(function (resolve, reject) {
g_calls[id] = {resolve: resolve, reject: reject}; self.calls[id] = {resolve: resolve, reject: reject};
}); });
let message = { let message = {
message: 'tfrpc', action: 'tfrpc',
method: api[0], method: api[0],
params: [...arguments], params: [...arguments],
id: id, id: id,
@ -59,10 +32,6 @@ App.prototype.makeFunction = function (api) {
return result; return result;
}; };
/**
* TODOC
* @param {*} message
*/
App.prototype.send = function (message) { App.prototype.send = function (message) {
if (this._send_queue) { if (this._send_queue) {
if (this._on_output) { if (this._on_output) {
@ -77,11 +46,6 @@ App.prototype.send = function (message) {
} }
}; };
/**
* TODOC
* @param {*} request
* @param {*} response
*/
exports.app_socket = async function socket(request, response) { exports.app_socket = async function socket(request, response) {
let process; let process;
let options = {}; let options = {};
@ -102,10 +66,16 @@ exports.app_socket = async function socket(request, response) {
try { try {
message = JSON.parse(event.data); message = JSON.parse(event.data);
} catch (error) { } catch (error) {
print('ERROR', error, event.data, event.data.length, event.opCode); print(
'WebSocket error:',
error,
event.data,
event.data.length,
event.opCode
);
return; return;
} }
if (message.action == 'hello') { if (!process && message.action == 'hello') {
let packageOwner; let packageOwner;
let packageName; let packageName;
let blobId; let blobId;
@ -122,7 +92,7 @@ exports.app_socket = async function socket(request, response) {
if (!blobId) { if (!blobId) {
response.send( response.send(
JSON.stringify({ JSON.stringify({
message: 'tfrpc', action: 'tfrpc',
method: 'error', method: 'error',
params: [message.path + ' not found'], params: [message.path + ' not found'],
id: -1, id: -1,
@ -163,7 +133,7 @@ exports.app_socket = async function socket(request, response) {
options.packageOwner = packageOwner; options.packageOwner = packageOwner;
options.packageName = packageName; options.packageName = packageName;
options.url = message.url; options.url = message.url;
let sessionId = makeSessionId(); let sessionId = 'session_' + (gSessionIndex++).toString();
if (blobId) { if (blobId) {
if (message.edit_only) { if (message.edit_only) {
response.send( response.send(
@ -175,9 +145,24 @@ exports.app_socket = async function socket(request, response) {
} }
} }
if (process) { if (process) {
process.app.readOutput(function (message) { process.client_api.tfrpc = function (message) {
if (message.id) {
let calls = process?.app?.calls;
if (calls) {
let call = calls[message.id];
if (call) {
if (message.error !== undefined) {
call.reject(message.error);
} else {
call.resolve(message.result);
}
delete calls[message.id];
}
}
}
};
process.app._on_output = (message) =>
response.send(JSON.stringify(message), 0x1); response.send(JSON.stringify(message), 0x1);
});
process.app.send(); process.app.send();
} }
@ -206,26 +191,13 @@ exports.app_socket = async function socket(request, response) {
if (process && process.timeout > 0) { if (process && process.timeout > 0) {
setTimeout(ping, process.timeout); setTimeout(ping, process.timeout);
} }
} else if (message.action == 'resetPermission') {
if (process) {
process.resetPermission(message.permission);
}
} else if (message.action == 'setActiveIdentity') {
process.setActiveIdentity(message.identity);
} else if (message.action == 'createIdentity') {
await process.createIdentity();
} else if (message.message == 'tfrpc') {
if (message.id && g_calls[message.id]) {
if (message.error !== undefined) {
g_calls[message.id].reject(message.error);
} else {
g_calls[message.id].resolve(message.result);
}
delete g_calls[message.id];
}
} else { } else {
if (process && process.eventHandlers['message']) { if (process) {
await core.invoke(process.eventHandlers['message'], [message]); if (process.client_api[message.action]) {
process.client_api[message.action](message);
} else if (process.eventHandlers['message']) {
await core.invoke(process.eventHandlers['message'], [message]);
}
} }
} }
} else if (event.opCode == 0x8) { } else if (event.opCode == 0x8) {

View File

@ -1325,7 +1325,7 @@ function _receive_websocket_message(message) {
line.append(key, message.stats[key]); line.append(key, message.stats[key]);
} }
} }
} else if (message && message.message === 'tfrpc' && message.method) { } else if (message && message.action === 'tfrpc' && message.method) {
let api = k_api[message.method]; let api = k_api[message.method];
let id = message.id; let id = message.id;
let params = message.params; let params = message.params;
@ -1333,14 +1333,14 @@ function _receive_websocket_message(message) {
Promise.resolve(api.func(...params)) Promise.resolve(api.func(...params))
.then(function (result) { .then(function (result) {
send({ send({
message: 'tfrpc', action: 'tfrpc',
id: id, id: id,
result: result, result: result,
}); });
}) })
.catch(function (error) { .catch(function (error) {
send({ send({
message: 'tfrpc', action: 'tfrpc',
id: id, id: id,
error: error, error: error,
}); });

View File

@ -3,31 +3,22 @@ import * as http from './http.js';
let gProcesses = {}; let gProcesses = {};
let gStatsTimer = false; let gStatsTimer = false;
let kPingInterval = 60 * 1000; let g_handler_index = 0;
/** const k_ping_interval = 60 * 1000;
* TODOC
* @param {*} out function printError(error) {
* @param {*} error
*/
function printError(out, error) {
if (error.stackTrace) { if (error.stackTrace) {
out.print(error.fileName + ':' + error.lineNumber + ': ' + error.message); print(error.fileName + ':' + error.lineNumber + ': ' + error.message);
out.print(error.stackTrace); print(error.stackTrace);
} else { } else {
for (let [k, v] of Object.entries(error)) { for (let [k, v] of Object.entries(error)) {
out.print(k, v); print(k, v);
} }
out.print(error.toString()); print(error.toString());
} }
} }
/**
* TODOC
* @param {*} handlers
* @param {*} argv
* @returns
*/
function invoke(handlers, argv) { function invoke(handlers, argv) {
let promises = []; let promises = [];
if (handlers) { if (handlers) {
@ -48,12 +39,6 @@ function invoke(handlers, argv) {
return Promise.all(promises); return Promise.all(promises);
} }
/**
* TODOC
* @param {*} eventName
* @param {*} argv
* @returns
*/
function broadcastEvent(eventName, argv) { function broadcastEvent(eventName, argv) {
let promises = []; let promises = [];
for (let process of Object.values(gProcesses)) { for (let process of Object.values(gProcesses)) {
@ -64,11 +49,6 @@ function broadcastEvent(eventName, argv) {
return Promise.all(promises); return Promise.all(promises);
} }
/**
* TODOC
* @param {*} message
* @returns
*/
function broadcast(message) { function broadcast(message) {
let sender = this; let sender = this;
let promises = []; let promises = [];
@ -85,12 +65,6 @@ function broadcast(message) {
return Promise.all(promises); return Promise.all(promises);
} }
/**
* TODOC
* @param {String} eventName
* @param {*} argv
* @returns
*/
function broadcastAppEventToUser( function broadcastAppEventToUser(
user, user,
packageOwner, packageOwner,
@ -113,12 +87,6 @@ function broadcastAppEventToUser(
return Promise.all(promises); return Promise.all(promises);
} }
/**
* TODOC
* @param {*} caller
* @param {*} process
* @returns
*/
function getUser(caller, process) { function getUser(caller, process) {
return { return {
key: process.key, key: process.key,
@ -129,12 +97,6 @@ function getUser(caller, process) {
}; };
} }
/**
* TODOC
* @param {*} user
* @param {*} process
* @returns
*/
async function getApps(user, process) { async function getApps(user, process) {
if ( if (
process.credentials && process.credentials &&
@ -161,28 +123,13 @@ async function getApps(user, process) {
return {}; return {};
} }
/**
* TODOC
* @param {*} from
* @param {*} to
* @param {*} message
* @returns
*/
function postMessageInternal(from, to, message) { function postMessageInternal(from, to, message) {
if (to.eventHandlers['message']) { if (to.eventHandlers['message']) {
return invoke(to.eventHandlers['message'], [getUser(from, from), message]); return invoke(to.eventHandlers['message'], [getUser(from, from), message]);
} }
} }
/**
* TODOC
* @param {*} blobId
* @param {*} key
* @param {*} options
* @returns
*/
async function getProcessBlob(blobId, key, options) { async function getProcessBlob(blobId, key, options) {
// TODO(tasiaiso): break this down ?
let process = gProcesses[key]; let process = gProcesses[key];
if (!process && !(options && 'create' in options && !options.create)) { if (!process && !(options && 'create' in options && !options.create)) {
let resolveReady; let resolveReady;
@ -201,7 +148,7 @@ async function getProcessBlob(blobId, key, options) {
} }
process.lastActive = Date.now(); process.lastActive = Date.now();
process.lastPing = null; process.lastPing = null;
process.timeout = kPingInterval; process.timeout = k_ping_interval;
process.ready = new Promise(function (resolve, reject) { process.ready = new Promise(function (resolve, reject) {
resolveReady = resolve; resolveReady = resolve;
rejectReady = reject; rejectReady = reject;
@ -461,10 +408,10 @@ async function getProcessBlob(blobId, key, options) {
if (process.app) { if (process.app) {
process.app.makeFunction(['error'])(error); process.app.makeFunction(['error'])(error);
} else { } else {
printError({print: print}, error); printError(error);
} }
} catch (e) { } catch (e) {
printError({print: print}, error); printError(error);
} }
}; };
imports.ssb = Object.fromEntries( imports.ssb = Object.fromEntries(
@ -649,17 +596,26 @@ async function getProcessBlob(blobId, key, options) {
permissions: await imports.core.permissionsGranted(), permissions: await imports.core.permissionsGranted(),
}); });
}; };
process.resetPermission = async function resetPermission(permission) { process.client_api = {
let user = process?.credentials?.session?.name; createIdentity: function () {
await ssb.setUserPermission( return process.createIdentity();
user, },
options?.packageOwner, resetPermission: async function resetPermission(message) {
options?.packageName, let user = process?.credentials?.session?.name;
permission, await ssb.setUserPermission(
undefined user,
); options?.packageOwner,
return process.sendPermissions(); options?.packageName,
message.permission,
undefined
);
return process.sendPermissions();
},
setActiveIdentity: function setActiveIdentity(message) {
return process.setActiveIdentity(message.identity);
},
}; };
ssb.registerImports(imports, process);
process.task.setImports(imports); process.task.setImports(imports);
process.task.activate(); process.task.activate();
let source = await ssb.blobGet(blobId); let source = await ssb.blobGet(blobId);
@ -686,7 +642,7 @@ async function getProcessBlob(blobId, key, options) {
); );
} }
} catch (e) { } catch (e) {
printError({print: print}, e); printError(e);
} }
broadcastEvent('onSessionBegin', [getUser(process, process)]); broadcastEvent('onSessionBegin', [getUser(process, process)]);
if (process.app) { if (process.app) {
@ -700,14 +656,10 @@ async function getProcessBlob(blobId, key, options) {
sendStats(); sendStats();
} }
} catch (error) { } catch (error) {
if (process.app) { if (process?.app && process?.task?.onError) {
if (process?.task?.onError) { process.task.onError(error);
process.task.onError(error);
} else {
printError({print: print}, error);
}
} else { } else {
printError({print: print}, error); printError(error);
} }
rejectReady(error); rejectReady(error);
} }
@ -727,9 +679,6 @@ ssb.addEventListener('connections', function () {
broadcastEvent('onConnectionsChanged', []); broadcastEvent('onConnectionsChanged', []);
}); });
/**
* TODOC
*/
async function loadSettings() { async function loadSettings() {
let data = {}; let data = {};
try { try {
@ -748,9 +697,6 @@ async function loadSettings() {
return data; return data;
} }
/**
* TODOC
*/
function sendStats() { function sendStats() {
let apps = Object.values(gProcesses) let apps = Object.values(gProcesses)
.filter((process) => process.app) .filter((process) => process.app)
@ -766,8 +712,6 @@ function sendStats() {
} }
} }
let g_handler_index = 0;
exports.callAppHandler = async function callAppHandler( exports.callAppHandler = async function callAppHandler(
response, response,
app_blob_id, app_blob_id,

View File

@ -25,14 +25,14 @@
}: }:
pkgs.stdenv.mkDerivation rec { pkgs.stdenv.mkDerivation rec {
pname = "tildefriends"; pname = "tildefriends";
version = "0.0.27.1"; version = "0.0.28";
src = pkgs.fetchFromGitea { src = pkgs.fetchFromGitea {
domain = "dev.tildefriends.net"; domain = "dev.tildefriends.net";
owner = "cory"; owner = "cory";
repo = "tildefriends"; repo = "tildefriends";
rev = "v${version}"; rev = "v${version}";
hash = "sha256-3t1m9ZomQF3DteWyALJWrnCq0EAROEK8shKXh6Ao38c="; hash = "sha256-vcLJCXgIrjC37t9oavK2QMRcMJljghuzKDYxQ4nyTcE=";
fetchSubmodules = true; fetchSubmodules = true;
}; };

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unprompted.tildefriends" package="com.unprompted.tildefriends"
android:versionCode="33" android:versionCode="34"
android:versionName="0.0.28"> android:versionName="0.0.29-wip">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application

19
src/api.js.c Normal file
View File

@ -0,0 +1,19 @@
#include "api.js.h"
#include "log.h"
#include <quickjs.h>
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
return JS_UNDEFINED;
}
void tf_api_register(JSContext* context)
{
JSValue global = JS_GetGlobalObject(context);
JSValue ssb = JS_GetPropertyStr(context, global, "ssb");
JS_SetPropertyStr(context, ssb, "registerImports", JS_NewCFunction(context, _tf_api_register_imports, "registerImports", 2));
JS_FreeValue(context, ssb);
JS_FreeValue(context, global);
}

18
src/api.js.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
/**
** \defgroup api_js JS API
** Functions that are ultimately exposed to apps.
** @{
*/
/** A JS context. */
typedef struct JSContext JSContext;
/**
** Register JS API functions.
** @param context The JS context.
*/
void tf_api_register(JSContext* context);
/** @} */

View File

@ -84,6 +84,7 @@ typedef struct _tf_http_listener_t
typedef struct _tf_http_t typedef struct _tf_http_t
{ {
bool is_shutting_down; bool is_shutting_down;
bool is_in_destroy;
tf_http_listener_t** listeners; tf_http_listener_t** listeners;
int listeners_count; int listeners_count;
@ -395,7 +396,7 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
if (fin) if (fin)
{ {
if (connection->request->on_message) if (connection->request && connection->request->on_message)
{ {
tf_trace_begin(connection->http->trace, connection->trace_name ? connection->trace_name : "websocket"); tf_trace_begin(connection->http->trace, connection->trace_name ? connection->trace_name : "websocket");
connection->request->on_message(connection->request, connection->fragment_length ? connection->fragment_op_code : op_code, connection->request->on_message(connection->request, connection->fragment_length ? connection->fragment_op_code : op_code,
@ -786,7 +787,13 @@ static void _http_free_listener_on_close(uv_handle_t* handle)
void tf_http_destroy(tf_http_t* http) void tf_http_destroy(tf_http_t* http)
{ {
if (http->is_in_destroy)
{
return;
}
http->is_shutting_down = true; http->is_shutting_down = true;
http->is_in_destroy = true;
for (int i = 0; i < http->connections_count; i++) for (int i = 0; i < http->connections_count; i++)
{ {
@ -845,6 +852,10 @@ void tf_http_destroy(tf_http_t* http)
tf_free(http); tf_free(http);
} }
else
{
http->is_in_destroy = false;
}
} }
const char* tf_http_status_text(int status) const char* tf_http_status_text(int status)
@ -963,9 +974,42 @@ static void _http_write(tf_http_connection_t* connection, const void* data, size
} }
} }
void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size) void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size)
{ {
_http_write(request->connection, data, size); uint8_t* copy = tf_malloc(size + 16);
bool fin = true;
size_t header = 1;
copy[0] = (fin ? (1 << 7) : 0) | (op_code & 0xf);
if (size < 126)
{
copy[1] = size;
header += 1;
}
else if (size < (1 << 16))
{
copy[1] = 126;
copy[2] = (size >> 8) & 0xff;
copy[3] = (size >> 0) & 0xff;
header += 3;
}
else
{
uint32_t high = ((uint64_t)size >> 32) & 0xffffffff;
uint32_t low = (size >> 0) & 0xffffffff;
copy[1] = 127;
copy[2] = (high >> 24) & 0xff;
copy[3] = (high >> 16) & 0xff;
copy[4] = (high >> 8) & 0xff;
copy[5] = (high >> 0) & 0xff;
copy[6] = (low >> 24) & 0xff;
copy[7] = (low >> 16) & 0xff;
copy[8] = (low >> 8) & 0xff;
copy[9] = (low >> 0) & 0xff;
header += 9;
}
memcpy(copy + header, data, size);
_http_write(request->connection, copy, header + size);
tf_free(copy);
} }
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length) void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length)

View File

@ -209,10 +209,11 @@ const char* tf_http_get_cookie(const char* cookie_header, const char* name);
** Send a websocket message. ** Send a websocket message.
** @param request The HTTP request which was previously updated to a websocket ** @param request The HTTP request which was previously updated to a websocket
** session with tf_http_request_websocket_upgrade(). ** session with tf_http_request_websocket_upgrade().
** @param op_code Websocket op code.
** @param data The message data. ** @param data The message data.
** @param size The size of data. ** @param size The size of data.
*/ */
void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size); void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size);
/** /**
** Upgrade an HTTP request to a websocket session. ** Upgrade an HTTP request to a websocket session.

View File

@ -152,44 +152,9 @@ static JSValue _httpd_response_send(JSContext* context, JSValueConst this_val, i
tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id); tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
int opcode = 0x1; int opcode = 0x1;
JS_ToInt32(context, &opcode, argv[1]); JS_ToInt32(context, &opcode, argv[1]);
uint64_t length = 0; size_t length = 0;
size_t length_size = 0; const char* message = JS_ToCStringLen(context, &length, argv[0]);
const char* message = JS_ToCStringLen(context, &length_size, argv[0]); tf_http_request_websocket_send(request, opcode, message, length);
length = length_size;
uint8_t* copy = tf_malloc(length + 16);
bool fin = true;
size_t header = 1;
copy[0] = (fin ? (1 << 7) : 0) | (opcode & 0xf);
if (length < 126)
{
copy[1] = length;
header += 1;
}
else if (length < (1 << 16))
{
copy[1] = 126;
copy[2] = (length >> 8) & 0xff;
copy[3] = (length >> 0) & 0xff;
header += 3;
}
else
{
uint32_t high = (length >> 32) & 0xffffffff;
uint32_t low = (length >> 0) & 0xffffffff;
copy[1] = 127;
copy[2] = (high >> 24) & 0xff;
copy[3] = (high >> 16) & 0xff;
copy[4] = (high >> 8) & 0xff;
copy[5] = (high >> 0) & 0xff;
copy[6] = (low >> 24) & 0xff;
copy[7] = (low >> 16) & 0xff;
copy[8] = (low >> 8) & 0xff;
copy[9] = (low >> 0) & 0xff;
header += 9;
}
memcpy(copy + header, message, length);
tf_http_request_send(request, copy, header + length);
tf_free(copy);
JS_FreeCString(context, message); JS_FreeCString(context, message);
return JS_UNDEFINED; return JS_UNDEFINED;
} }

View File

@ -13,13 +13,13 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.0.28</string> <string>0.0.29</string>
<key>CFBundleSupportedPlatforms</key> <key>CFBundleSupportedPlatforms</key>
<array> <array>
<string>iPhoneOS</string> <string>iPhoneOS</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>9</string> <string>11</string>
<key>DTPlatformName</key> <key>DTPlatformName</key>
<string>iphoneos</string> <string>iphoneos</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>

View File

@ -1,5 +1,6 @@
#include "task.h" #include "task.h"
#include "api.js.h"
#include "bcrypt.js.h" #include "bcrypt.js.h"
#include "database.js.h" #include "database.js.h"
#include "file.js.h" #include "file.js.h"
@ -1697,6 +1698,7 @@ void tf_task_activate(tf_task_t* task)
task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path, task->_network_key); task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path, task->_network_key);
tf_ssb_set_trace(task->_ssb, task->_trace); tf_ssb_set_trace(task->_ssb, task->_trace);
tf_ssb_register(context, task->_ssb); tf_ssb_register(context, task->_ssb);
tf_api_register(context);
tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task); tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task);
if (task->_args) if (task->_args)

View File

@ -1,2 +1,2 @@
#define VERSION_NUMBER "0.0.28" #define VERSION_NUMBER "0.0.29-wip"
#define VERSION_NAME "This program kills fascists." #define VERSION_NAME "This program kills fascists."