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
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type OsaLang = "applescript" | "javascript";
|
|
2
|
+
export interface OsaExecResult {
|
|
3
|
+
exitCode: number;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
timedOut?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Async osascript for vector dispatch and ui steps (never blocks the event loop). */
|
|
9
|
+
export declare function osaExec(script: string, options: {
|
|
10
|
+
lang?: OsaLang;
|
|
11
|
+
timeoutMs: number;
|
|
12
|
+
}): Promise<OsaExecResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Sync osascript for the consent probes. Returns stdout on success; throws an
|
|
15
|
+
* execFileSync-SHAPED error otherwise (status/stderr/killed/signal), so the
|
|
16
|
+
* probes' TCC classification regexes (-1743 denied, -1712/kill pending) read
|
|
17
|
+
* identically on both paths. In deputy mode the classification is answered
|
|
18
|
+
* about the DEPUTY's grants — exactly the right question, since it is the
|
|
19
|
+
* deputy that will send every AppleEvent.
|
|
20
|
+
*/
|
|
21
|
+
export declare function osaExecSync(script: string, timeoutMs: number): string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single osascript execution seam. Every osascript the library runs —
|
|
3
|
+
* vector dispatch, ui steps, consent probes — flows through one of these two
|
|
4
|
+
* functions, which route to the deputy when it is active and run direct
|
|
5
|
+
* (execFile/execFileSync, byte-identical to the pre-deputy behavior)
|
|
6
|
+
* otherwise. `open`-based primitives (URL scheme, reveal) are consent-free by
|
|
7
|
+
* design and never route.
|
|
8
|
+
*/
|
|
9
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
10
|
+
import {} from "./protocol.js";
|
|
11
|
+
import { deputyAsyncRequest, deputyRouting, deputySyncRequest } from "./routing.js";
|
|
12
|
+
/** Deputy-side kill fires at timeoutMs; the client deadline adds grace so the deputy's honest answer wins. */
|
|
13
|
+
const CLIENT_GRACE_MS = 5000;
|
|
14
|
+
function osaArgs(script, lang) {
|
|
15
|
+
return lang === "javascript" ? ["-l", "JavaScript", "-e", script] : ["-e", script];
|
|
16
|
+
}
|
|
17
|
+
function fromDeputy(res) {
|
|
18
|
+
const r = res;
|
|
19
|
+
return {
|
|
20
|
+
exitCode: r.timedOut === true || r.signal !== undefined
|
|
21
|
+
? r.exitCode === 0
|
|
22
|
+
? 1
|
|
23
|
+
: r.exitCode
|
|
24
|
+
: r.exitCode,
|
|
25
|
+
stdout: r.stdout,
|
|
26
|
+
stderr: r.stderr,
|
|
27
|
+
...(r.timedOut === true && { timedOut: true }),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Async osascript for vector dispatch and ui steps (never blocks the event loop). */
|
|
31
|
+
export async function osaExec(script, options) {
|
|
32
|
+
const lang = options.lang ?? "applescript";
|
|
33
|
+
if (deputyRouting().active) {
|
|
34
|
+
const res = await deputyAsyncRequest({ verb: "osascript", script, lang, timeoutMs: options.timeoutMs }, options.timeoutMs + CLIENT_GRACE_MS);
|
|
35
|
+
return fromDeputy(res);
|
|
36
|
+
}
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
execFile("osascript", osaArgs(script, lang), { timeout: options.timeoutMs }, (err, stdout, stderr) => {
|
|
39
|
+
const timedOut = err !== null && err.killed === true;
|
|
40
|
+
resolve({
|
|
41
|
+
exitCode: err === null ? 0 : (err.code ?? 1),
|
|
42
|
+
stdout: String(stdout),
|
|
43
|
+
stderr: String(stderr),
|
|
44
|
+
...(timedOut && { timedOut: true }),
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Sync osascript for the consent probes. Returns stdout on success; throws an
|
|
51
|
+
* execFileSync-SHAPED error otherwise (status/stderr/killed/signal), so the
|
|
52
|
+
* probes' TCC classification regexes (-1743 denied, -1712/kill pending) read
|
|
53
|
+
* identically on both paths. In deputy mode the classification is answered
|
|
54
|
+
* about the DEPUTY's grants — exactly the right question, since it is the
|
|
55
|
+
* deputy that will send every AppleEvent.
|
|
56
|
+
*/
|
|
57
|
+
export function osaExecSync(script, timeoutMs) {
|
|
58
|
+
if (!deputyRouting().active) {
|
|
59
|
+
return execFileSync("osascript", ["-e", script], { encoding: "utf8", timeout: timeoutMs });
|
|
60
|
+
}
|
|
61
|
+
const res = deputySyncRequest({ verb: "osascript", script, lang: "applescript", timeoutMs }, timeoutMs + CLIENT_GRACE_MS);
|
|
62
|
+
if (res.exitCode === 0 && res.timedOut !== true && res.signal === undefined)
|
|
63
|
+
return res.stdout;
|
|
64
|
+
const error = new Error(`Command failed: osascript\n${res.stderr}`);
|
|
65
|
+
error.status = res.timedOut === true ? null : res.exitCode;
|
|
66
|
+
error.stdout = res.stdout;
|
|
67
|
+
error.stderr = res.stderr;
|
|
68
|
+
error.killed = res.timedOut === true;
|
|
69
|
+
error.signal = res.timedOut === true || res.signal !== undefined ? "SIGTERM" : null;
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=osa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"osa.js","sourceRoot":"","sources":["../../src/deputy/osa.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAwB,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAWpF,8GAA8G;AAC9G,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B,SAAS,OAAO,CAAC,MAAc,EAAE,IAAa;IAC5C,OAAO,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,UAAU,CAAC,GAA4B;IAC9C,MAAM,CAAC,GAAG,GAAiC,CAAC;IAC5C,OAAO;QACL,QAAQ,EACN,CAAC,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;YAC3C,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC;gBAChB,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,CAAC,CAAC,QAAQ;YACd,CAAC,CAAC,CAAC,CAAC,QAAQ;QAChB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,GAAG,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,MAAc,EACd,OAA8C;IAE9C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,aAAa,CAAC;IAC3C,IAAI,aAAa,EAAE,CAAC,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAClC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EACjE,OAAO,CAAC,SAAS,GAAG,eAAe,CACpC,CAAC;QACF,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,QAAQ,CACN,WAAW,EACX,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EACrB,EAAE,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,EAC9B,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACtB,MAAM,QAAQ,GAAG,GAAG,KAAK,IAAI,IAAK,GAA4B,CAAC,MAAM,KAAK,IAAI,CAAC;YAC/E,OAAO,CAAC;gBACN,QAAQ,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,GAAyB,CAAC,IAAI,IAAI,CAAC,CAAC;gBACnE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;gBACtB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;gBACtB,GAAG,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;aACpC,CAAC,CAAC;QACL,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,SAAiB;IAC3D,IAAI,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,CAAC;QAC5B,OAAO,YAAY,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAC7F,CAAC;IACD,MAAM,GAAG,GAAG,iBAAiB,CAC3B,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,EAC7D,SAAS,GAAG,eAAe,CACE,CAAC;IAChC,IAAI,GAAG,CAAC,QAAQ,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC;IAC/F,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,8BAA8B,GAAG,CAAC,MAAM,EAAE,CAMjE,CAAC;IACF,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC3D,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1B,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1B,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC;IACrC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,MAAM,KAAK,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export declare const DEPUTY_PROTOCOL_VERSION = 1;
|
|
2
|
+
/**
|
|
3
|
+
* The helpers version this package expects — the helpers are versioned on
|
|
4
|
+
* their OWN line (deputy/VERSION), decoupled from the package version, so a
|
|
5
|
+
* package release whose helper sources are unchanged never nags for (or
|
|
6
|
+
* forces) a reinstall of a byte-equivalent bundle. Bump deputy/VERSION with
|
|
7
|
+
* any helper-source change; a drift test asserts this constant matches it.
|
|
8
|
+
* The PROTOCOL version above remains the hard compatibility gate.
|
|
9
|
+
*/
|
|
10
|
+
export declare const EXPECTED_HELPERS_VERSION = "1.1.0";
|
|
11
|
+
/** The outer helper bundle's identifier (Things API Helper.app) — TCC + BTM identity. */
|
|
12
|
+
export declare const HELPERS_BUNDLE_ID = "com.pixelcog.things-api-helper";
|
|
13
|
+
/** launchd label (and signing identifier) of the broker. */
|
|
14
|
+
export declare const DEPUTY_LAUNCHD_LABEL = "com.pixelcog.things-deputy";
|
|
15
|
+
/**
|
|
16
|
+
* The deputy derives its own state dir from the same THINGS_API_STATE_DIR /
|
|
17
|
+
* XDG_STATE_HOME precedence as the library (deputy/src/main.swift mirrors
|
|
18
|
+
* this), so pointing both sides at one env is all a test needs.
|
|
19
|
+
*/
|
|
20
|
+
export declare function deputyStateDir(env?: NodeJS.ProcessEnv): string;
|
|
21
|
+
export declare function deputySocketPath(env?: NodeJS.ProcessEnv): string;
|
|
22
|
+
export declare function deputyTokenPath(env?: NodeJS.ProcessEnv): string;
|
|
23
|
+
/**
|
|
24
|
+
* The directory `helpers install` owns WHOLESALE: it is deleted and recreated
|
|
25
|
+
* on every install, so any previous layout (however old) is erased without
|
|
26
|
+
* dedicated migration logic, and the fresh inodes reset the kernel's
|
|
27
|
+
* per-vnode code-signature cache (copying over an executed inode makes every
|
|
28
|
+
* future exec die with SIGKILL — observed live 2026-08-21).
|
|
29
|
+
*/
|
|
30
|
+
export declare function helpersInstallDir(env?: NodeJS.ProcessEnv): string;
|
|
31
|
+
/** Where `helpers install` places the bundle. */
|
|
32
|
+
export declare function helpersInstalledBundlePath(env?: NodeJS.ProcessEnv): string;
|
|
33
|
+
/** The installed deputy executable (the bundle's main executable). */
|
|
34
|
+
export declare function deputyInstalledBinaryPath(env?: NodeJS.ProcessEnv): string;
|
|
35
|
+
/** The installed nested reader app. */
|
|
36
|
+
export declare function readerInstalledAppPath(env?: NodeJS.ProcessEnv): string;
|
|
37
|
+
/** launchd label (and bundle identifier) of the sandboxed reader. */
|
|
38
|
+
export declare const READER_LAUNCHD_LABEL = "com.pixelcog.things-reader";
|
|
39
|
+
/**
|
|
40
|
+
* The reader's state lives in its App Sandbox container home — the OS picks
|
|
41
|
+
* the path from the bundle identifier. THINGS_API_READER_DIR overrides for
|
|
42
|
+
* tests (a mock reader is just a socket; no sandbox involved).
|
|
43
|
+
*/
|
|
44
|
+
export declare function readerContainerDir(env?: NodeJS.ProcessEnv): string;
|
|
45
|
+
export declare function readerSocketPath(env?: NodeJS.ProcessEnv): string;
|
|
46
|
+
export declare function readerTokenPath(env?: NodeJS.ProcessEnv): string;
|
|
47
|
+
/** Reader handshake: DeputyHello plus the grant state. */
|
|
48
|
+
export interface ReaderHello extends DeputyHello {
|
|
49
|
+
role: "reader";
|
|
50
|
+
/** False until the one-time open-panel ceremony has granted the folder. */
|
|
51
|
+
granted: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface DeputyErrorShape {
|
|
54
|
+
code: string;
|
|
55
|
+
message: string;
|
|
56
|
+
}
|
|
57
|
+
/** A protocol-level failure (the deputy answered `ok:false`, or never answered). */
|
|
58
|
+
export declare class DeputyRequestError extends Error {
|
|
59
|
+
readonly code: string;
|
|
60
|
+
constructor(code: string, message: string);
|
|
61
|
+
}
|
|
62
|
+
export interface DeputyHello {
|
|
63
|
+
protocol: number;
|
|
64
|
+
deputyVersion: string;
|
|
65
|
+
pid: number;
|
|
66
|
+
/** Reader only: its cached container-db resolution (absent on the deputy). */
|
|
67
|
+
dbPath?: string | null;
|
|
68
|
+
uptimeMs: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* osascript outcome as the deputy reports it. `ok:true` at the protocol level
|
|
72
|
+
* means "the deputy ran osascript" — a failing script is exitCode ≠ 0 here,
|
|
73
|
+
* exactly as execFile would report it, so vector-side classification logic
|
|
74
|
+
* reads the same signals on both paths.
|
|
75
|
+
*/
|
|
76
|
+
export interface DeputyOsaResult {
|
|
77
|
+
exitCode: number;
|
|
78
|
+
stdout: string;
|
|
79
|
+
stderr: string;
|
|
80
|
+
timedOut?: boolean;
|
|
81
|
+
/** Present when osascript died to a signal (number, e.g. 15 for SIGTERM). */
|
|
82
|
+
signal?: number;
|
|
83
|
+
}
|
|
84
|
+
export type DeputyRow = Record<string, unknown>;
|
|
85
|
+
/** BLOB columns cross the wire as `{ "$b64": <base64> }`; revive to Uint8Array. */
|
|
86
|
+
export declare function reviveRow(row: DeputyRow): DeputyRow;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* things-deputy wire protocol: shared constants, request/response shapes, and
|
|
3
|
+
* path resolution for the broker's socket, token, and state directory.
|
|
4
|
+
*
|
|
5
|
+
* The helpers (deputy/src, deputy/reader) are deliberately dumb privileged
|
|
6
|
+
* proxies — the deputy runs fixed-shape osascript/shortcuts (mutations), the
|
|
7
|
+
* sandboxed reader serves read-only SQL and scoped file reads — so that macOS
|
|
8
|
+
* permission grants attach to their stable signed identities instead of
|
|
9
|
+
* whichever agent harness invokes the CLI. All product logic stays here in the
|
|
10
|
+
* library; the protocol therefore carries raw primitives, never operations.
|
|
11
|
+
* See docs/design/agent-daemon.md (§β1, §3b).
|
|
12
|
+
*/
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { stateDir } from "../paths.js";
|
|
16
|
+
export const DEPUTY_PROTOCOL_VERSION = 1;
|
|
17
|
+
/**
|
|
18
|
+
* The helpers version this package expects — the helpers are versioned on
|
|
19
|
+
* their OWN line (deputy/VERSION), decoupled from the package version, so a
|
|
20
|
+
* package release whose helper sources are unchanged never nags for (or
|
|
21
|
+
* forces) a reinstall of a byte-equivalent bundle. Bump deputy/VERSION with
|
|
22
|
+
* any helper-source change; a drift test asserts this constant matches it.
|
|
23
|
+
* The PROTOCOL version above remains the hard compatibility gate.
|
|
24
|
+
*/
|
|
25
|
+
export const EXPECTED_HELPERS_VERSION = "1.1.0";
|
|
26
|
+
/** The outer helper bundle's identifier (Things API Helper.app) — TCC + BTM identity. */
|
|
27
|
+
export const HELPERS_BUNDLE_ID = "com.pixelcog.things-api-helper";
|
|
28
|
+
/** launchd label (and signing identifier) of the broker. */
|
|
29
|
+
export const DEPUTY_LAUNCHD_LABEL = "com.pixelcog.things-deputy";
|
|
30
|
+
/**
|
|
31
|
+
* The deputy derives its own state dir from the same THINGS_API_STATE_DIR /
|
|
32
|
+
* XDG_STATE_HOME precedence as the library (deputy/src/main.swift mirrors
|
|
33
|
+
* this), so pointing both sides at one env is all a test needs.
|
|
34
|
+
*/
|
|
35
|
+
export function deputyStateDir(env = process.env) {
|
|
36
|
+
return join(stateDir(env), "deputy");
|
|
37
|
+
}
|
|
38
|
+
export function deputySocketPath(env = process.env) {
|
|
39
|
+
return join(deputyStateDir(env), "deputy.sock");
|
|
40
|
+
}
|
|
41
|
+
export function deputyTokenPath(env = process.env) {
|
|
42
|
+
return join(deputyStateDir(env), "token");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The directory `helpers install` owns WHOLESALE: it is deleted and recreated
|
|
46
|
+
* on every install, so any previous layout (however old) is erased without
|
|
47
|
+
* dedicated migration logic, and the fresh inodes reset the kernel's
|
|
48
|
+
* per-vnode code-signature cache (copying over an executed inode makes every
|
|
49
|
+
* future exec die with SIGKILL — observed live 2026-08-21).
|
|
50
|
+
*/
|
|
51
|
+
export function helpersInstallDir(env = process.env) {
|
|
52
|
+
return join(deputyStateDir(env), "bin");
|
|
53
|
+
}
|
|
54
|
+
/** Where `helpers install` places the bundle. */
|
|
55
|
+
export function helpersInstalledBundlePath(env = process.env) {
|
|
56
|
+
return join(helpersInstallDir(env), "Things API Helper.app");
|
|
57
|
+
}
|
|
58
|
+
/** The installed deputy executable (the bundle's main executable). */
|
|
59
|
+
export function deputyInstalledBinaryPath(env = process.env) {
|
|
60
|
+
return join(helpersInstalledBundlePath(env), "Contents/MacOS/things-deputy");
|
|
61
|
+
}
|
|
62
|
+
/** The installed nested reader app. */
|
|
63
|
+
export function readerInstalledAppPath(env = process.env) {
|
|
64
|
+
return join(helpersInstalledBundlePath(env), "Contents/Helpers/things-reader.app");
|
|
65
|
+
}
|
|
66
|
+
/** launchd label (and bundle identifier) of the sandboxed reader. */
|
|
67
|
+
export const READER_LAUNCHD_LABEL = "com.pixelcog.things-reader";
|
|
68
|
+
/**
|
|
69
|
+
* The reader's state lives in its App Sandbox container home — the OS picks
|
|
70
|
+
* the path from the bundle identifier. THINGS_API_READER_DIR overrides for
|
|
71
|
+
* tests (a mock reader is just a socket; no sandbox involved).
|
|
72
|
+
*/
|
|
73
|
+
export function readerContainerDir(env = process.env) {
|
|
74
|
+
const explicit = env["THINGS_API_READER_DIR"];
|
|
75
|
+
if (explicit !== undefined && explicit !== "")
|
|
76
|
+
return explicit;
|
|
77
|
+
return join(homedir(), "Library/Containers", READER_LAUNCHD_LABEL, "Data");
|
|
78
|
+
}
|
|
79
|
+
export function readerSocketPath(env = process.env) {
|
|
80
|
+
return join(readerContainerDir(env), "reader.sock");
|
|
81
|
+
}
|
|
82
|
+
export function readerTokenPath(env = process.env) {
|
|
83
|
+
return join(readerContainerDir(env), "token");
|
|
84
|
+
}
|
|
85
|
+
/** A protocol-level failure (the deputy answered `ok:false`, or never answered). */
|
|
86
|
+
export class DeputyRequestError extends Error {
|
|
87
|
+
code;
|
|
88
|
+
constructor(code, message) {
|
|
89
|
+
super(message);
|
|
90
|
+
this.name = "DeputyRequestError";
|
|
91
|
+
this.code = code;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** BLOB columns cross the wire as `{ "$b64": <base64> }`; revive to Uint8Array. */
|
|
95
|
+
export function reviveRow(row) {
|
|
96
|
+
for (const [key, value] of Object.entries(row)) {
|
|
97
|
+
if (value !== null &&
|
|
98
|
+
typeof value === "object" &&
|
|
99
|
+
"$b64" in value &&
|
|
100
|
+
typeof value.$b64 === "string") {
|
|
101
|
+
row[key] = new Uint8Array(Buffer.from(value.$b64, "base64"));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return row;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/deputy/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type HelpersMode } from "../config.ts";
|
|
2
|
+
import { type DeputyHello, type ReaderHello } from "./protocol.ts";
|
|
3
|
+
export interface DeputyRouting {
|
|
4
|
+
/** True when requests are actually flowing to the deputy. */
|
|
5
|
+
readonly active: boolean;
|
|
6
|
+
/** Why routing is inactive ("disabled", or the activation failure). */
|
|
7
|
+
readonly reason: string | null;
|
|
8
|
+
/** Handshake result when active. */
|
|
9
|
+
readonly hello: DeputyHello | null;
|
|
10
|
+
}
|
|
11
|
+
/** Test seam: forget the per-process activation memo (and close transports). */
|
|
12
|
+
export declare function resetDeputyRoutingForTests(): void;
|
|
13
|
+
/** The per-process routing decision (activated lazily on first ask). */
|
|
14
|
+
export declare function deputyRouting(env?: NodeJS.ProcessEnv): DeputyRouting;
|
|
15
|
+
/** The reader transport's state (activated lazily; test seam via reset). */
|
|
16
|
+
export declare function readerRouting(env?: NodeJS.ProcessEnv): {
|
|
17
|
+
active: boolean;
|
|
18
|
+
granted: boolean;
|
|
19
|
+
hello: ReaderHello | null;
|
|
20
|
+
reason: string | null;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* What routing actually RESOLVED to in this process: the configured mode plus
|
|
24
|
+
* whether each half is carrying traffic. Activates both halves (memoized), so
|
|
25
|
+
* it reports the same decision every other call in this process sees — doctor's
|
|
26
|
+
* helpers section reads it rather than re-deriving one.
|
|
27
|
+
*/
|
|
28
|
+
export interface HelpersRouting {
|
|
29
|
+
mode: HelpersMode;
|
|
30
|
+
/** Automation verbs (osascript/shortcuts) ride the deputy. */
|
|
31
|
+
automation: boolean;
|
|
32
|
+
/** File verbs (sql/read-file/locate) ride the granted reader. */
|
|
33
|
+
files: boolean;
|
|
34
|
+
/** Why automation is not routed (null when it is). */
|
|
35
|
+
deputyReason: string | null;
|
|
36
|
+
/** Why file verbs are not routed (null when they are). */
|
|
37
|
+
readerReason: string | null;
|
|
38
|
+
}
|
|
39
|
+
export declare function helpersRouting(env?: NodeJS.ProcessEnv): HelpersRouting;
|
|
40
|
+
/** True when file verbs ride the reader (present, protocol-matched, granted). */
|
|
41
|
+
export declare function deputyFilesActive(env?: NodeJS.ProcessEnv): boolean;
|
|
42
|
+
/** Blocking FILE-verb round-trip via the granted reader. */
|
|
43
|
+
export declare function fileSyncRequest(fields: Record<string, unknown>, timeoutMs: number, env?: NodeJS.ProcessEnv): Record<string, unknown>;
|
|
44
|
+
/** Blocking round-trip via the bridge (facade + sync probes only). */
|
|
45
|
+
export declare function deputySyncRequest(fields: Record<string, unknown>, timeoutMs: number, env?: NodeJS.ProcessEnv): Record<string, unknown>;
|
|
46
|
+
/** Event-loop-friendly round-trip (vector dispatch). */
|
|
47
|
+
export declare function deputyAsyncRequest(fields: Record<string, unknown>, timeoutMs: number, env?: NodeJS.ProcessEnv): Promise<Record<string, unknown>>;
|
|
48
|
+
/**
|
|
49
|
+
* The reader-resolved container database path, or null when no granted reader
|
|
50
|
+
* is active (the caller then locates locally, exactly as before the helpers
|
|
51
|
+
* existed). The reader's scope is grant-checked, never prompted — a locate
|
|
52
|
+
* through it can refuse but can never stall on a consent dialog.
|
|
53
|
+
*/
|
|
54
|
+
export declare function deputyDbPath(env?: NodeJS.ProcessEnv): string | null;
|
|
55
|
+
/**
|
|
56
|
+
* Should DATABASE access route through the deputy? Only for the default
|
|
57
|
+
* container database: an explicit dbPath option or THINGS_DB env names a
|
|
58
|
+
* caller-chosen file (lab clones, fixtures) the deputy knows nothing about —
|
|
59
|
+
* those always open locally, deputy or no deputy.
|
|
60
|
+
*/
|
|
61
|
+
export declare function deputyRoutesDb(options: {
|
|
62
|
+
dbPath?: string;
|
|
63
|
+
} | undefined, env?: NodeJS.ProcessEnv): boolean;
|