things-api 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/deputy/VERSION +1 -0
- package/deputy/helpers-Info.plist +23 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/CodeResources +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/Info.plist +20 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/MacOS/things-reader +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/_CodeSignature/CodeResources +115 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Info.plist +23 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/MacOS/things-deputy +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/_CodeSignature/CodeResources +125 -0
- package/deputy/reader/Info.plist +20 -0
- package/deputy/reader/entitlements.plist +15 -0
- package/deputy/reader/main.swift +491 -0
- package/deputy/src/main.swift +133 -0
- package/deputy/src/osascript.swift +102 -0
- package/deputy/src/server.swift +350 -0
- package/deputy/src/sqlite.swift +121 -0
- package/dist/cli/commands/doctor.js +55 -2
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/helpers.d.ts +17 -0
- package/dist/cli/commands/helpers.js +148 -0
- package/dist/cli/commands/helpers.js.map +1 -0
- package/dist/cli/commands/op-result.d.ts +15 -0
- package/dist/cli/commands/op-result.js +39 -0
- package/dist/cli/commands/op-result.js.map +1 -0
- package/dist/cli/commands/writes.js +22 -3
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/help.js +4 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/helpers-check.d.ts +10 -0
- package/dist/cli/helpers-check.js +59 -0
- package/dist/cli/helpers-check.js.map +1 -0
- package/dist/cli/main.js +22 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/client.js +16 -2
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +31 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.js +1 -1
- package/dist/db/baselines/db-v26.js +8 -4
- package/dist/db/baselines/db-v26.js.map +1 -1
- package/dist/db/baselines/db-v27.d.ts +42 -0
- package/dist/db/baselines/db-v27.js +47 -0
- package/dist/db/baselines/db-v27.js.map +1 -0
- package/dist/db/baselines/index.js +2 -1
- package/dist/db/baselines/index.js.map +1 -1
- package/dist/db/locate.d.ts +4 -1
- package/dist/db/locate.js +3 -1
- package/dist/db/locate.js.map +1 -1
- package/dist/db/schema.d.ts +1 -1
- package/dist/db/schema.js +9 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/deputy/bridge-worker.d.ts +1 -0
- package/dist/deputy/bridge-worker.js +79 -0
- package/dist/deputy/bridge-worker.js.map +1 -0
- package/dist/deputy/bridge.d.ts +14 -0
- package/dist/deputy/bridge.js +70 -0
- package/dist/deputy/bridge.js.map +1 -0
- package/dist/deputy/client.d.ts +19 -0
- package/dist/deputy/client.js +104 -0
- package/dist/deputy/client.js.map +1 -0
- package/dist/deputy/db-facade.d.ts +18 -0
- package/dist/deputy/db-facade.js +71 -0
- package/dist/deputy/db-facade.js.map +1 -0
- package/dist/deputy/files.d.ts +1 -0
- package/dist/deputy/files.js +16 -0
- package/dist/deputy/files.js.map +1 -0
- package/dist/deputy/install.d.ts +105 -0
- package/dist/deputy/install.js +467 -0
- package/dist/deputy/install.js.map +1 -0
- package/dist/deputy/notice.d.ts +6 -0
- package/dist/deputy/notice.js +28 -0
- package/dist/deputy/notice.js.map +1 -0
- package/dist/deputy/notices.d.ts +31 -0
- package/dist/deputy/notices.js +94 -0
- package/dist/deputy/notices.js.map +1 -0
- package/dist/deputy/osa.d.ts +21 -0
- package/dist/deputy/osa.js +72 -0
- package/dist/deputy/osa.js.map +1 -0
- package/dist/deputy/protocol.d.ts +86 -0
- package/dist/deputy/protocol.js +106 -0
- package/dist/deputy/protocol.js.map +1 -0
- package/dist/deputy/routing.d.ts +63 -0
- package/dist/deputy/routing.js +338 -0
- package/dist/deputy/routing.js.map +1 -0
- package/dist/deputy/shortcuts-exec.d.ts +10 -0
- package/dist/deputy/shortcuts-exec.js +50 -0
- package/dist/deputy/shortcuts-exec.js.map +1 -0
- package/dist/diagnose.d.ts +43 -1
- package/dist/diagnose.js +64 -2
- package/dist/diagnose.js.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +25 -7
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/mappers.d.ts +12 -3
- package/dist/model/mappers.js +6 -1
- package/dist/model/mappers.js.map +1 -1
- package/dist/model/recurrence.d.ts +5 -1
- package/dist/model/recurrence.js +19 -1
- package/dist/model/recurrence.js.map +1 -1
- package/dist/model/template-projection.d.ts +26 -0
- package/dist/model/template-projection.js +113 -0
- package/dist/model/template-projection.js.map +1 -0
- package/dist/op-result.d.ts +46 -0
- package/dist/op-result.js +156 -0
- package/dist/op-result.js.map +1 -0
- package/dist/read/detail.js +7 -1
- package/dist/read/detail.js.map +1 -1
- package/dist/read/queries.js +8 -1
- package/dist/read/queries.js.map +1 -1
- package/dist/read/views.d.ts +6 -4
- package/dist/read/views.js +60 -18
- package/dist/read/views.js.map +1 -1
- package/dist/sync-health.js +6 -2
- package/dist/sync-health.js.map +1 -1
- package/dist/write/accessibility-probe.js +4 -2
- package/dist/write/accessibility-probe.js.map +1 -1
- package/dist/write/automation-probe.js +5 -1
- package/dist/write/automation-probe.js.map +1 -1
- package/dist/write/availability.js +7 -3
- package/dist/write/availability.js.map +1 -1
- package/dist/write/commands.js +58 -13
- package/dist/write/commands.js.map +1 -1
- package/dist/write/experimental.d.ts +19 -0
- package/dist/write/experimental.js +50 -0
- package/dist/write/experimental.js.map +1 -1
- package/dist/write/move.js +14 -6
- package/dist/write/move.js.map +1 -1
- package/dist/write/operations.d.ts +28 -4
- package/dist/write/pipeline.d.ts +2 -0
- package/dist/write/pipeline.js +59 -2
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.js +33 -15
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/promote-clone.js +187 -19
- package/dist/write/promote-clone.js.map +1 -1
- package/dist/write/reorder.js +76 -27
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/repeat-anchor.d.ts +30 -0
- package/dist/write/repeat-anchor.js +166 -1
- package/dist/write/repeat-anchor.js.map +1 -1
- package/dist/write/repeat-rule.js +10 -0
- package/dist/write/repeat-rule.js.map +1 -1
- package/dist/write/vectors/applescript.js +7 -16
- package/dist/write/vectors/applescript.js.map +1 -1
- package/dist/write/vectors/session-reachability.d.ts +55 -0
- package/dist/write/vectors/session-reachability.js +56 -8
- package/dist/write/vectors/session-reachability.js.map +1 -1
- package/dist/write/vectors/shortcuts.js +5 -14
- package/dist/write/vectors/shortcuts.js.map +1 -1
- package/dist/write/vectors/simulator.d.ts +16 -1
- package/dist/write/vectors/simulator.js +61 -10
- package/dist/write/vectors/simulator.js.map +1 -1
- package/dist/write/vectors/types.d.ts +67 -1
- package/dist/write/vectors/ui-recipes.js +128 -38
- package/dist/write/vectors/ui-recipes.js.map +1 -1
- package/dist/write/vectors/ui.d.ts +76 -0
- package/dist/write/vectors/ui.js +443 -37
- package/dist/write/vectors/ui.js.map +1 -1
- package/dist/write/verify/poller.d.ts +13 -0
- package/dist/write/verify/poller.js +38 -0
- package/dist/write/verify/poller.js.map +1 -1
- package/package.json +8 -1
- package/scripts/build-helpers.sh +104 -0
- package/scripts/deputy-cert-setup.sh +60 -0
- package/skills/things-cli/SKILL.md +8 -3
- package/skills/things-cli/references/errors.md +4 -0
- package/skills/things-cli/references/repeating.md +69 -0
package/dist/db/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM,EAAE;QACN,MAAM;QACN,MAAM;QACN,QAAQ;QACR,UAAU;QACV,SAAS;QACT,OAAO;QACP,OAAO;QACP,cAAc;QACd,sBAAsB;QACtB,OAAO;QACP,WAAW;QACX,aAAa;QACb,cAAc;QACd,UAAU;QACV,yBAAyB;QACzB,OAAO;QACP,YAAY;QACZ,yBAAyB;QACzB,MAAM;QACN,SAAS;QACT,SAAS;QACT,2BAA2B;QAC3B,+BAA+B;QAC/B,qBAAqB;QACrB,yBAAyB;QACzB,uBAAuB;QACvB,oBAAoB;QACpB,2BAA2B;QAC3B,4BAA4B;QAC5B,UAAU;KACX;IACD,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC;IAC7C,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC;IACnE,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE;QACf,MAAM;QACN,OAAO;QACP,QAAQ;QACR,UAAU;QACV,OAAO;QACP,MAAM;QACN,cAAc;QACd,sBAAsB;KACvB;IACD,UAAU,EAAE;QACV,MAAM;QACN,8BAA8B;QAC9B,oBAAoB;QACpB,aAAa;QACb,eAAe;KAChB;IACD,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;CACd,CAAC;AAIX,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAoB,CAAC;AAE3E;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,aAAa,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,4BAA4B;IACtD,eAAe,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,8BAA8B;IAC1D,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,2BAA2B;IACtD,oBAAoB,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,kBAAkB;IAChD,gBAAgB,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACxB,wBAAwB,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;CAC3B,CAAC;AAEX,iFAAiF;AACjF,MAAM,UAAU,CAAC,CAAC,UAAkB;IAClC,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;AACjD,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,CAAC,KAAoB;IAC7C,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC"}
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM,EAAE;QACN,MAAM;QACN,MAAM;QACN,QAAQ;QACR,UAAU;QACV,SAAS;QACT,OAAO;QACP,OAAO;QACP,cAAc;QACd,sBAAsB;QACtB,OAAO;QACP,WAAW;QACX,aAAa;QACb,cAAc;QACd,UAAU;QACV,yBAAyB;QACzB,OAAO;QACP,YAAY;QACZ,yBAAyB;QACzB,MAAM;QACN,SAAS;QACT,SAAS;QACT,2BAA2B;QAC3B,+BAA+B;QAC/B,qBAAqB;QACrB,yBAAyB;QACzB,uBAAuB;QACvB,oBAAoB;QACpB,2BAA2B;QAC3B,4BAA4B;QAC5B,0EAA0E;QAC1E,sEAAsE;QACtE,2EAA2E;QAC3E,4EAA4E;QAC5E,sEAAsE;QACtE,wEAAwE;QACxE,iEAAiE;QACjE,+BAA+B;QAC/B,2BAA2B;QAC3B,UAAU;KACX;IACD,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC;IAC7C,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC;IACnE,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE;QACf,MAAM;QACN,OAAO;QACP,QAAQ;QACR,UAAU;QACV,OAAO;QACP,MAAM;QACN,cAAc;QACd,sBAAsB;KACvB;IACD,UAAU,EAAE;QACV,MAAM;QACN,8BAA8B;QAC9B,oBAAoB;QACpB,aAAa;QACb,eAAe;KAChB;IACD,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;CACd,CAAC;AAIX,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAoB,CAAC;AAE3E;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,aAAa,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,4BAA4B;IACtD,eAAe,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,8BAA8B;IAC1D,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,2BAA2B;IACtD,oBAAoB,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,kBAAkB;IAChD,gBAAgB,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACxB,wBAAwB,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;CAC3B,CAAC;AAEX,iFAAiF;AACjF,MAAM,UAAU,CAAC,CAAC,UAAkB;IAClC,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;AACjD,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,CAAC,KAAoB;IAC7C,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker half of the synchronous deputy bridge (see bridge.ts). Owns the
|
|
3
|
+
* long-lived socket to the deputy and services one request at a time: the main
|
|
4
|
+
* thread posts a request on the message port and blocks in Atomics.wait; this
|
|
5
|
+
* worker performs the socket round-trip, posts the response back, and bumps the
|
|
6
|
+
* shared counter to wake the main thread.
|
|
7
|
+
*/
|
|
8
|
+
import { connect } from "node:net";
|
|
9
|
+
import { workerData } from "node:worker_threads";
|
|
10
|
+
const { socketPath, sab, port } = workerData;
|
|
11
|
+
const flag = new Int32Array(sab);
|
|
12
|
+
let socket = null;
|
|
13
|
+
let buffer = "";
|
|
14
|
+
let pendingLine = null;
|
|
15
|
+
let pendingError = null;
|
|
16
|
+
function resetSocket(err) {
|
|
17
|
+
socket = null;
|
|
18
|
+
buffer = "";
|
|
19
|
+
const reject = pendingError;
|
|
20
|
+
pendingLine = null;
|
|
21
|
+
pendingError = null;
|
|
22
|
+
reject?.(err);
|
|
23
|
+
}
|
|
24
|
+
function ensureSocket() {
|
|
25
|
+
if (socket !== null)
|
|
26
|
+
return Promise.resolve(socket);
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const sock = connect(socketPath);
|
|
29
|
+
sock.setEncoding("utf8");
|
|
30
|
+
sock.once("connect", () => {
|
|
31
|
+
socket = sock;
|
|
32
|
+
resolve(sock);
|
|
33
|
+
});
|
|
34
|
+
sock.on("data", (chunk) => {
|
|
35
|
+
buffer += chunk;
|
|
36
|
+
const nl = buffer.indexOf("\n");
|
|
37
|
+
if (nl < 0)
|
|
38
|
+
return;
|
|
39
|
+
const line = buffer.slice(0, nl);
|
|
40
|
+
buffer = buffer.slice(nl + 1);
|
|
41
|
+
const deliver = pendingLine;
|
|
42
|
+
pendingLine = null;
|
|
43
|
+
pendingError = null;
|
|
44
|
+
deliver?.(line);
|
|
45
|
+
});
|
|
46
|
+
sock.on("error", (err) => {
|
|
47
|
+
if (socket === null)
|
|
48
|
+
reject(err);
|
|
49
|
+
resetSocket(err);
|
|
50
|
+
});
|
|
51
|
+
sock.on("close", () => {
|
|
52
|
+
resetSocket(new Error("deputy closed the connection"));
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async function roundTrip(request) {
|
|
57
|
+
const sock = await ensureSocket();
|
|
58
|
+
const line = await new Promise((resolve, reject) => {
|
|
59
|
+
pendingLine = resolve;
|
|
60
|
+
pendingError = reject;
|
|
61
|
+
sock.write(`${JSON.stringify(request)}\n`);
|
|
62
|
+
});
|
|
63
|
+
return JSON.parse(line);
|
|
64
|
+
}
|
|
65
|
+
port.on("message", ({ bridgeId, request }) => {
|
|
66
|
+
void roundTrip(request)
|
|
67
|
+
.then((response) => ({ bridgeId, response: response }))
|
|
68
|
+
.catch((err) => ({
|
|
69
|
+
bridgeId,
|
|
70
|
+
error: err instanceof Error ? err.message : String(err),
|
|
71
|
+
}))
|
|
72
|
+
.then((envelope) => {
|
|
73
|
+
port.postMessage(envelope);
|
|
74
|
+
Atomics.add(flag, 0, 1);
|
|
75
|
+
Atomics.notify(flag, 0);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
port.start();
|
|
79
|
+
//# sourceMappingURL=bridge-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-worker.js","sourceRoot":"","sources":["../../src/deputy/bridge-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class DeputySyncBridge {
|
|
2
|
+
private readonly worker;
|
|
3
|
+
private readonly flag;
|
|
4
|
+
private readonly port;
|
|
5
|
+
private seq;
|
|
6
|
+
constructor(socketPath: string);
|
|
7
|
+
/**
|
|
8
|
+
* Send one request and block until its response (or the deadline). Late
|
|
9
|
+
* responses from a previously timed-out request are drained and discarded by
|
|
10
|
+
* the id match, so one timeout never poisons the next call.
|
|
11
|
+
*/
|
|
12
|
+
request(payload: Record<string, unknown>, timeoutMs: number): Record<string, unknown>;
|
|
13
|
+
close(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous bridge to the deputy for callers that cannot await — the
|
|
3
|
+
* DatabaseSync-shaped read facade and the sync consent probes. A dedicated
|
|
4
|
+
* worker thread owns the socket; the main thread posts a request, blocks in
|
|
5
|
+
* Atomics.wait on a shared counter, then drains the port with
|
|
6
|
+
* receiveMessageOnPort. One request is in flight at a time by construction
|
|
7
|
+
* (the main thread is blocked while it waits).
|
|
8
|
+
*
|
|
9
|
+
* The bridge must NOT be used for long-running osascript drives — blocking the
|
|
10
|
+
* event loop would suspend the drive watchdog and signal handlers. Async
|
|
11
|
+
* dispatch rides client.ts instead.
|
|
12
|
+
*/
|
|
13
|
+
import { MessageChannel, receiveMessageOnPort, Worker, } from "node:worker_threads";
|
|
14
|
+
import { DeputyRequestError } from "./protocol.js";
|
|
15
|
+
export class DeputySyncBridge {
|
|
16
|
+
worker;
|
|
17
|
+
flag;
|
|
18
|
+
port;
|
|
19
|
+
seq = 0;
|
|
20
|
+
constructor(socketPath) {
|
|
21
|
+
const sab = new SharedArrayBuffer(4);
|
|
22
|
+
this.flag = new Int32Array(sab);
|
|
23
|
+
const channel = new MessageChannel();
|
|
24
|
+
this.port = channel.port1;
|
|
25
|
+
// Live TS source runs the .ts twin; the published build runs the emitted
|
|
26
|
+
// .js next to this module (same trick as src/cli/version.ts).
|
|
27
|
+
const ext = import.meta.url.endsWith(".ts") ? "ts" : "js";
|
|
28
|
+
this.worker = new Worker(new URL(`./bridge-worker.${ext}`, import.meta.url), {
|
|
29
|
+
workerData: { socketPath, sab, port: channel.port2 },
|
|
30
|
+
transferList: [channel.port2],
|
|
31
|
+
});
|
|
32
|
+
// The bridge must never keep the CLI process alive on its own.
|
|
33
|
+
this.worker.unref();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Send one request and block until its response (or the deadline). Late
|
|
37
|
+
* responses from a previously timed-out request are drained and discarded by
|
|
38
|
+
* the id match, so one timeout never poisons the next call.
|
|
39
|
+
*/
|
|
40
|
+
request(payload, timeoutMs) {
|
|
41
|
+
const id = `s${++this.seq}`;
|
|
42
|
+
let seen = Atomics.load(this.flag, 0);
|
|
43
|
+
// oxlint-disable-next-line require-post-message-target-origin -- worker_threads port, no targetOrigin
|
|
44
|
+
this.port.postMessage({ bridgeId: id, request: payload });
|
|
45
|
+
const deadline = Date.now() + timeoutMs;
|
|
46
|
+
for (;;) {
|
|
47
|
+
let received = receiveMessageOnPort(this.port);
|
|
48
|
+
while (received !== undefined) {
|
|
49
|
+
const message = received.message;
|
|
50
|
+
if (message.bridgeId === id) {
|
|
51
|
+
if (message.error !== undefined) {
|
|
52
|
+
throw new DeputyRequestError("bridge-io", message.error);
|
|
53
|
+
}
|
|
54
|
+
return message.response ?? {};
|
|
55
|
+
}
|
|
56
|
+
received = receiveMessageOnPort(this.port);
|
|
57
|
+
}
|
|
58
|
+
const remaining = deadline - Date.now();
|
|
59
|
+
if (remaining <= 0) {
|
|
60
|
+
throw new DeputyRequestError("bridge-timeout", `the deputy did not answer within ${timeoutMs}ms`);
|
|
61
|
+
}
|
|
62
|
+
Atomics.wait(this.flag, 0, seen, remaining);
|
|
63
|
+
seen = Atomics.load(this.flag, 0);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
close() {
|
|
67
|
+
void this.worker.terminate();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/deputy/bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class DeputyAsyncClient {
|
|
2
|
+
private socket;
|
|
3
|
+
private buffer;
|
|
4
|
+
private seq;
|
|
5
|
+
private readonly pending;
|
|
6
|
+
private readonly socketPath;
|
|
7
|
+
constructor(socketPath: string);
|
|
8
|
+
private ensureSocket;
|
|
9
|
+
private onData;
|
|
10
|
+
private failAll;
|
|
11
|
+
/**
|
|
12
|
+
* Send one request; resolves with the raw response object (protocol errors
|
|
13
|
+
* included — the caller inspects `ok`). Rejects only on transport failure or
|
|
14
|
+
* client-side deadline (`timeoutMs` should exceed any deputy-side timeout so
|
|
15
|
+
* the deputy's own honest answer wins).
|
|
16
|
+
*/
|
|
17
|
+
request(payload: Record<string, unknown>, timeoutMs: number): Promise<Record<string, unknown>>;
|
|
18
|
+
close(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async deputy client for event-loop-friendly callers: the applescript vector
|
|
3
|
+
* and the ui vector's step runner. Long osascript drives ride this client so
|
|
4
|
+
* timers (drive watchdog) and signal handlers keep running; synchronous
|
|
5
|
+
* callers ride bridge.ts instead. One shared socket, requests matched by id.
|
|
6
|
+
*/
|
|
7
|
+
import { connect } from "node:net";
|
|
8
|
+
import { DeputyRequestError } from "./protocol.js";
|
|
9
|
+
export class DeputyAsyncClient {
|
|
10
|
+
socket = null;
|
|
11
|
+
buffer = "";
|
|
12
|
+
seq = 0;
|
|
13
|
+
pending = new Map();
|
|
14
|
+
socketPath;
|
|
15
|
+
constructor(socketPath) {
|
|
16
|
+
this.socketPath = socketPath;
|
|
17
|
+
}
|
|
18
|
+
ensureSocket() {
|
|
19
|
+
if (this.socket !== null)
|
|
20
|
+
return Promise.resolve(this.socket);
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const sock = connect(this.socketPath);
|
|
23
|
+
sock.setEncoding("utf8");
|
|
24
|
+
sock.once("connect", () => {
|
|
25
|
+
this.socket = sock;
|
|
26
|
+
// An idle deputy connection must never keep the CLI process alive.
|
|
27
|
+
sock.unref();
|
|
28
|
+
resolve(sock);
|
|
29
|
+
});
|
|
30
|
+
sock.on("data", (chunk) => this.onData(chunk));
|
|
31
|
+
sock.on("error", (err) => {
|
|
32
|
+
if (this.socket === null)
|
|
33
|
+
reject(err);
|
|
34
|
+
this.failAll(err);
|
|
35
|
+
});
|
|
36
|
+
sock.on("close", () => this.failAll(new Error("deputy closed the connection")));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
onData(chunk) {
|
|
40
|
+
this.buffer += chunk;
|
|
41
|
+
for (;;) {
|
|
42
|
+
const nl = this.buffer.indexOf("\n");
|
|
43
|
+
if (nl < 0)
|
|
44
|
+
return;
|
|
45
|
+
const line = this.buffer.slice(0, nl);
|
|
46
|
+
this.buffer = this.buffer.slice(nl + 1);
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
parsed = JSON.parse(line);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const id = typeof parsed["id"] === "string" ? parsed["id"] : null;
|
|
55
|
+
const entry = id !== null ? this.pending.get(id) : undefined;
|
|
56
|
+
if (id === null || entry === undefined)
|
|
57
|
+
continue;
|
|
58
|
+
this.pending.delete(id);
|
|
59
|
+
clearTimeout(entry.timer);
|
|
60
|
+
entry.resolve(parsed);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
failAll(err) {
|
|
64
|
+
this.socket = null;
|
|
65
|
+
this.buffer = "";
|
|
66
|
+
const waiting = [...this.pending.values()];
|
|
67
|
+
this.pending.clear();
|
|
68
|
+
for (const entry of waiting) {
|
|
69
|
+
clearTimeout(entry.timer);
|
|
70
|
+
entry.reject(err);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Send one request; resolves with the raw response object (protocol errors
|
|
75
|
+
* included — the caller inspects `ok`). Rejects only on transport failure or
|
|
76
|
+
* client-side deadline (`timeoutMs` should exceed any deputy-side timeout so
|
|
77
|
+
* the deputy's own honest answer wins).
|
|
78
|
+
*/
|
|
79
|
+
async request(payload, timeoutMs) {
|
|
80
|
+
const sock = await this.ensureSocket();
|
|
81
|
+
const id = `a${++this.seq}`;
|
|
82
|
+
// Wake the socket handle while a request is in flight (see unref above).
|
|
83
|
+
sock.ref();
|
|
84
|
+
try {
|
|
85
|
+
return await new Promise((resolve, reject) => {
|
|
86
|
+
const timer = setTimeout(() => {
|
|
87
|
+
this.pending.delete(id);
|
|
88
|
+
reject(new DeputyRequestError("client-timeout", `the deputy did not answer within ${timeoutMs}ms`));
|
|
89
|
+
}, timeoutMs);
|
|
90
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
91
|
+
sock.write(`${JSON.stringify({ ...payload, id })}\n`);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (this.pending.size === 0)
|
|
96
|
+
sock.unref();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
close() {
|
|
100
|
+
this.socket?.destroy();
|
|
101
|
+
this.socket = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/deputy/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A DatabaseSync-shaped READ facade over the deputy's `sql` verb. The read
|
|
3
|
+
* layer types against node:sqlite's DatabaseSync and only ever prepares
|
|
4
|
+
* SELECT/PRAGMA statements consumed via `.all()` / `.get()`; this facade
|
|
5
|
+
* implements exactly that surface synchronously (through the bridge) and
|
|
6
|
+
* throws a teaching error on anything else — never a silent no-op. WAL
|
|
7
|
+
* freshness is preserved: the deputy holds one read-only connection outside
|
|
8
|
+
* any transaction, so every statement sees a fresh snapshot exactly like a
|
|
9
|
+
* local connection (docs/design/architecture.md §0).
|
|
10
|
+
*/
|
|
11
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
12
|
+
/**
|
|
13
|
+
* Build the facade. The cast is confined to this one construction site: the
|
|
14
|
+
* object structurally implements the read subset, a Proxy turns any other
|
|
15
|
+
* member access into a teaching error, and symbol probes (inspect, iterators)
|
|
16
|
+
* stay undefined so logging never explodes.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createDeputyDbFacade(env?: NodeJS.ProcessEnv): DatabaseSync;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { reviveRow } from "./protocol.js";
|
|
2
|
+
import { fileSyncRequest } from "./routing.js";
|
|
3
|
+
const SQL_TIMEOUT_MS = 15_000;
|
|
4
|
+
function normalizeParams(params, sql) {
|
|
5
|
+
return params.map((param, index) => {
|
|
6
|
+
if (param === null || typeof param === "string" || typeof param === "number")
|
|
7
|
+
return param;
|
|
8
|
+
if (typeof param === "bigint") {
|
|
9
|
+
if (param > BigInt(Number.MAX_SAFE_INTEGER) || param < BigInt(Number.MIN_SAFE_INTEGER)) {
|
|
10
|
+
throw new TypeError(`deputy sql: bigint parameter ${index} exceeds JSON-safe range`);
|
|
11
|
+
}
|
|
12
|
+
return Number(param);
|
|
13
|
+
}
|
|
14
|
+
throw new TypeError(`deputy sql: unsupported ${typeof param} parameter at index ${index} for: ${sql.slice(0, 80)}`);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function queryRows(sql, params, env) {
|
|
18
|
+
// Rides the granted reader when present, else the deputy (routing.ts).
|
|
19
|
+
const res = fileSyncRequest({ verb: "sql", sql, params: normalizeParams(params, sql) }, SQL_TIMEOUT_MS, env);
|
|
20
|
+
return res["rows"].map(reviveRow);
|
|
21
|
+
}
|
|
22
|
+
function unsupported(member) {
|
|
23
|
+
return () => {
|
|
24
|
+
throw new Error(`DatabaseSync.${member} is not available on a deputy-routed connection — the deputy brokers read-only prepare/all/get only. Disable the helpers (THINGS_API_HELPERS=false) for local access.`);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build the facade. The cast is confined to this one construction site: the
|
|
29
|
+
* object structurally implements the read subset, a Proxy turns any other
|
|
30
|
+
* member access into a teaching error, and symbol probes (inspect, iterators)
|
|
31
|
+
* stay undefined so logging never explodes.
|
|
32
|
+
*/
|
|
33
|
+
export function createDeputyDbFacade(env = process.env) {
|
|
34
|
+
const base = {
|
|
35
|
+
prepare(sql) {
|
|
36
|
+
const statement = {
|
|
37
|
+
all(...params) {
|
|
38
|
+
return queryRows(sql, params, env);
|
|
39
|
+
},
|
|
40
|
+
get(...params) {
|
|
41
|
+
return queryRows(sql, params, env)[0];
|
|
42
|
+
},
|
|
43
|
+
run: unsupported("prepare(...).run (writes never touch the database directly)"),
|
|
44
|
+
iterate: unsupported("prepare(...).iterate"),
|
|
45
|
+
expandedSQL: sql,
|
|
46
|
+
sourceSQL: sql,
|
|
47
|
+
};
|
|
48
|
+
return new Proxy(statement, {
|
|
49
|
+
get(target, prop, receiver) {
|
|
50
|
+
if (typeof prop === "symbol" || prop in target)
|
|
51
|
+
return Reflect.get(target, prop, receiver);
|
|
52
|
+
return unsupported(`prepare(...).${String(prop)}`)();
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
exec: unsupported("exec"),
|
|
57
|
+
close() {
|
|
58
|
+
// The bridge outlives individual connections; nothing to release here.
|
|
59
|
+
},
|
|
60
|
+
isOpen: true,
|
|
61
|
+
isTransaction: false,
|
|
62
|
+
};
|
|
63
|
+
return new Proxy(base, {
|
|
64
|
+
get(target, prop, receiver) {
|
|
65
|
+
if (typeof prop === "symbol" || prop in target)
|
|
66
|
+
return Reflect.get(target, prop, receiver);
|
|
67
|
+
return unsupported(String(prop))();
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=db-facade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-facade.js","sourceRoot":"","sources":["../../src/deputy/db-facade.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function readContainerFileSync(path: string): Buffer;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container-scoped file reads (Things prefs plists for sync-health and URL-
|
|
3
|
+
* scheme availability). The group container is TCC-protected like the
|
|
4
|
+
* database, so in deputy mode these reads ride the deputy's `read-file` verb —
|
|
5
|
+
* which the deputy confines to the container subtree. Direct mode is a plain
|
|
6
|
+
* readFileSync, byte-identical to the pre-deputy behavior.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { deputyFilesActive, fileSyncRequest } from "./routing.js";
|
|
10
|
+
export function readContainerFileSync(path) {
|
|
11
|
+
if (!deputyFilesActive())
|
|
12
|
+
return readFileSync(path);
|
|
13
|
+
const res = fileSyncRequest({ verb: "read-file", path }, 10_000);
|
|
14
|
+
return Buffer.from(res["b64"], "base64");
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/deputy/files.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAElE,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,IAAI,CAAC,iBAAiB,EAAE;QAAE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;IACjE,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAW,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { HelpersMode } from "../config.ts";
|
|
2
|
+
import { type DeputyHello } from "./protocol.ts";
|
|
3
|
+
export declare function deputyPlistPath(): string;
|
|
4
|
+
export declare function readerPlistPath(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Where an install looks for the bundle when the caller names no path, in
|
|
7
|
+
* preference order: `deputy/prebuilt` — the signed + notarized bundle the
|
|
8
|
+
* release workflow stages into the published tarball, so an npm install needs
|
|
9
|
+
* neither Xcode nor a certificate — then `deputy/build`, the output of
|
|
10
|
+
* scripts/build-helpers.sh in a source checkout.
|
|
11
|
+
*/
|
|
12
|
+
export declare function helpersBundleCandidates(): string[];
|
|
13
|
+
/**
|
|
14
|
+
* The first candidate bundle that carries a deputy executable; null when none
|
|
15
|
+
* does. Both candidates are READ only — nothing in the `things helpers` path
|
|
16
|
+
* ever writes inside the package directory, so the CLI works from a read-only
|
|
17
|
+
* package root (an npx cache); BUILDING a bundle there is the one operation
|
|
18
|
+
* that needs a writable checkout, and `--bundle <path>` covers a prebuilt one.
|
|
19
|
+
*/
|
|
20
|
+
export declare function helpersDefaultBuildPath(): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* The INSTALLED bundle's version, read prompt-free from its Info.plist
|
|
23
|
+
* (CFBundleShortVersionString, stamped from deputy/VERSION at build time).
|
|
24
|
+
* Null when nothing is installed or the plist is unreadable/unstamped. This is
|
|
25
|
+
* the version a passive upgrade notice compares against
|
|
26
|
+
* {@link EXPECTED_HELPERS_VERSION} without needing a running helper.
|
|
27
|
+
*/
|
|
28
|
+
export declare function installedHelpersVersion(env?: NodeJS.ProcessEnv): string | null;
|
|
29
|
+
export interface DeputySigning {
|
|
30
|
+
state: "signed" | "adhoc" | "unsigned" | "unknown";
|
|
31
|
+
authority: string | null;
|
|
32
|
+
}
|
|
33
|
+
/** codesign facts about a binary (diagnostic output — may name mechanisms). */
|
|
34
|
+
export declare function deputySigningInfo(binaryPath: string): DeputySigning;
|
|
35
|
+
export interface HelpersInstallResult {
|
|
36
|
+
bundlePath: string;
|
|
37
|
+
plistPath: string;
|
|
38
|
+
stateDir: string;
|
|
39
|
+
signing: DeputySigning;
|
|
40
|
+
readerInstalled: boolean;
|
|
41
|
+
/** Reader grant state after install: null when the reader is absent or never answered. */
|
|
42
|
+
readerGranted: boolean | null;
|
|
43
|
+
warnings: string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Install (or reinstall) the helpers: copy the built bundle to its stable
|
|
47
|
+
* path, write both LaunchAgent plists, and (re)bootstrap them under launchd.
|
|
48
|
+
*/
|
|
49
|
+
export declare function installHelpers(options?: {
|
|
50
|
+
bundlePath?: string;
|
|
51
|
+
}, env?: NodeJS.ProcessEnv): HelpersInstallResult;
|
|
52
|
+
export interface HelpersUninstallResult {
|
|
53
|
+
removed: string[];
|
|
54
|
+
}
|
|
55
|
+
/** Stop both halves and remove LaunchAgents + the installed bundle (state — tokens, logs, the reader's grant — is kept). */
|
|
56
|
+
export declare function uninstallHelpers(env?: NodeJS.ProcessEnv): HelpersUninstallResult;
|
|
57
|
+
/** Restart the launchd-managed helpers (picks up a rebuilt installed bundle). */
|
|
58
|
+
export declare function restartHelpers(): void;
|
|
59
|
+
/**
|
|
60
|
+
* The one-time grant ceremony: open the reader in `--grant` mode (the panel
|
|
61
|
+
* must be presented by the SANDBOXED process — that is what makes the grant
|
|
62
|
+
* durable) and wait for the bookmark to land, confirmed via the reader's
|
|
63
|
+
* handshake. The panel opens INSIDE the Things data folder when it exists, so
|
|
64
|
+
* accepting is the only click. Interactive by design; returns when granted or
|
|
65
|
+
* on timeout, and verifies the database actually resolves inside the granted
|
|
66
|
+
* scope so a wrong-folder grant reports loudly instead of half-working.
|
|
67
|
+
*/
|
|
68
|
+
export declare function grantReader(env?: NodeJS.ProcessEnv): {
|
|
69
|
+
granted: boolean;
|
|
70
|
+
detail: string;
|
|
71
|
+
};
|
|
72
|
+
export interface DeputyHalfStatus {
|
|
73
|
+
plistInstalled: boolean;
|
|
74
|
+
loaded: boolean;
|
|
75
|
+
running: boolean;
|
|
76
|
+
socketPath: string;
|
|
77
|
+
/**
|
|
78
|
+
* The socket file exists but no handshake came back (a dead process that
|
|
79
|
+
* left its socket behind, or one wedged mid-request). Distinct from plain
|
|
80
|
+
* "not running": the remedy is `things helpers restart`, not an install.
|
|
81
|
+
*/
|
|
82
|
+
hungSocket: boolean;
|
|
83
|
+
hello: DeputyHello | null;
|
|
84
|
+
signing: DeputySigning | null;
|
|
85
|
+
detail: string;
|
|
86
|
+
}
|
|
87
|
+
export interface ReaderHalfStatus extends DeputyHalfStatus {
|
|
88
|
+
installed: boolean;
|
|
89
|
+
granted: boolean;
|
|
90
|
+
}
|
|
91
|
+
export interface HelpersStatus {
|
|
92
|
+
/** The configured routing mode (`helpers-enabled`), not what it resolved to. */
|
|
93
|
+
mode: HelpersMode;
|
|
94
|
+
bundleInstalled: boolean;
|
|
95
|
+
/** The installed bundle's version (Info.plist), null when nothing is installed. */
|
|
96
|
+
installedVersion: string | null;
|
|
97
|
+
deputy: DeputyHalfStatus;
|
|
98
|
+
reader: ReaderHalfStatus;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Prompt-free status for both halves: launchd load state, a live handshake
|
|
102
|
+
* when each socket answers, and the installed bundle's signing facts. Works
|
|
103
|
+
* with routing disabled — inspect first, enable after.
|
|
104
|
+
*/
|
|
105
|
+
export declare function helpersStatus(mode: HelpersMode, env?: NodeJS.ProcessEnv): HelpersStatus;
|