things-api 0.18.0 → 0.19.1
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 +18 -9
- package/deputy/AppIcon.icns +0 -0
- package/deputy/VERSION +1 -1
- package/deputy/helpers-Info.plist +4 -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 +3 -1
- 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/Resources/AppIcon.icns +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/_CodeSignature/CodeResources +15 -2
- package/deputy/prebuilt/Things API Helper.app/Contents/Info.plist +5 -1
- package/deputy/prebuilt/Things API Helper.app/Contents/MacOS/things-deputy +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Resources/AppIcon.icns +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/_CodeSignature/CodeResources +14 -2
- package/deputy/reader/Info.plist +2 -0
- package/deputy/reader/entitlements.plist +30 -0
- package/deputy/reader/main.swift +129 -40
- package/deputy/src/server.swift +25 -2
- package/deputy/src/tcc.swift +58 -0
- package/dist/audit/schema.d.ts +30 -1
- package/dist/audit/schema.js.map +1 -1
- package/dist/capability.d.ts +322 -0
- package/dist/capability.js +706 -0
- package/dist/capability.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +13 -1
- package/dist/cli/commands/doctor.js +141 -5
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/helpers.js +189 -51
- package/dist/cli/commands/helpers.js.map +1 -1
- package/dist/cli/commands/mcp.js +8 -0
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/op-result.js +6 -0
- package/dist/cli/commands/op-result.js.map +1 -1
- package/dist/cli/commands/reads.js +47 -1
- package/dist/cli/commands/reads.js.map +1 -1
- package/dist/cli/commands/repeat-flags.js +34 -2
- package/dist/cli/commands/repeat-flags.js.map +1 -1
- package/dist/cli/commands/setup.d.ts +9 -0
- package/dist/cli/commands/setup.js +76 -83
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/writes.d.ts +1 -1
- package/dist/cli/commands/writes.js +311 -142
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/dry-run.d.ts +1 -1
- package/dist/cli/dry-run.js +1 -1
- package/dist/cli/glyphs.d.ts +23 -4
- package/dist/cli/glyphs.js +46 -19
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +49 -27
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/interrupt.d.ts +54 -6
- package/dist/cli/interrupt.js +78 -12
- package/dist/cli/interrupt.js.map +1 -1
- package/dist/cli/main.js +8 -5
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/period.d.ts +3 -1
- package/dist/cli/period.js +1 -1
- package/dist/cli/period.js.map +1 -1
- package/dist/cli/read-driver.js +26 -1
- package/dist/cli/read-driver.js.map +1 -1
- package/dist/cli/render.d.ts +31 -4
- package/dist/cli/render.js +127 -15
- package/dist/cli/render.js.map +1 -1
- package/dist/client.d.ts +23 -3
- package/dist/client.js +36 -13
- package/dist/client.js.map +1 -1
- package/dist/contracts.d.ts +8 -4
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deputy/install.d.ts +234 -6
- package/dist/deputy/install.js +843 -31
- package/dist/deputy/install.js.map +1 -1
- package/dist/deputy/notices.js +2 -2
- package/dist/deputy/notices.js.map +1 -1
- package/dist/deputy/protocol.d.ts +59 -5
- package/dist/deputy/protocol.js +48 -11
- package/dist/deputy/protocol.js.map +1 -1
- package/dist/deputy/routing.d.ts +37 -0
- package/dist/deputy/routing.js +181 -27
- package/dist/deputy/routing.js.map +1 -1
- package/dist/deputy/wake.d.ts +61 -0
- package/dist/deputy/wake.js +149 -0
- package/dist/deputy/wake.js.map +1 -0
- package/dist/diagnose.d.ts +19 -4
- package/dist/diagnose.js +64 -8
- package/dist/diagnose.js.map +1 -1
- package/dist/direct-setup.d.ts +104 -0
- package/dist/direct-setup.js +658 -0
- package/dist/direct-setup.js.map +1 -0
- package/dist/host-access.d.ts +75 -0
- package/dist/host-access.js +177 -0
- package/dist/host-access.js.map +1 -0
- package/dist/index.d.ts +20 -5
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +27 -1
- package/dist/mcp/server.js +308 -109
- package/dist/mcp/server.js.map +1 -1
- package/dist/op-result.d.ts +9 -0
- package/dist/op-result.js +13 -1
- package/dist/op-result.js.map +1 -1
- package/dist/read/filter-contract.d.ts +1 -1
- package/dist/read/filter-contract.js +4 -0
- package/dist/read/filter-contract.js.map +1 -1
- package/dist/read/shape.js +4 -0
- package/dist/read/shape.js.map +1 -1
- package/dist/read/views.d.ts +29 -0
- package/dist/read/views.js +60 -0
- package/dist/read/views.js.map +1 -1
- package/dist/session-grant.d.ts +48 -0
- package/dist/session-grant.js +185 -0
- package/dist/session-grant.js.map +1 -0
- package/dist/surface-copy.d.ts +19 -3
- package/dist/surface-copy.js +19 -3
- package/dist/surface-copy.js.map +1 -1
- package/dist/wizard.d.ts +58 -0
- package/dist/wizard.js +174 -0
- package/dist/wizard.js.map +1 -0
- package/dist/write/availability.d.ts +1 -16
- package/dist/write/availability.js +10 -65
- package/dist/write/availability.js.map +1 -1
- package/dist/write/batch.d.ts +20 -0
- package/dist/write/batch.js +111 -21
- package/dist/write/batch.js.map +1 -1
- package/dist/write/capabilities.d.ts +9 -0
- package/dist/write/capabilities.js +2 -0
- package/dist/write/capabilities.js.map +1 -1
- package/dist/write/clear-reminder.js +1 -1
- package/dist/write/clear-reminder.js.map +1 -1
- package/dist/write/commands.d.ts +22 -7
- package/dist/write/commands.js +231 -254
- package/dist/write/commands.js.map +1 -1
- package/dist/write/failure-hints.d.ts +32 -7
- package/dist/write/failure-hints.js +9 -14
- package/dist/write/failure-hints.js.map +1 -1
- package/dist/write/guards.js +22 -4
- package/dist/write/guards.js.map +1 -1
- package/dist/write/lock.d.ts +16 -13
- package/dist/write/lock.js +55 -0
- package/dist/write/lock.js.map +1 -1
- package/dist/write/make-repeating-project.js +9 -13
- package/dist/write/make-repeating-project.js.map +1 -1
- package/dist/write/move.js +9 -12
- package/dist/write/move.js.map +1 -1
- package/dist/write/operations.d.ts +51 -29
- package/dist/write/operations.js +16 -0
- package/dist/write/operations.js.map +1 -1
- package/dist/write/opid.d.ts +72 -14
- package/dist/write/opid.js +152 -8
- package/dist/write/opid.js.map +1 -1
- package/dist/write/param-schema.d.ts +80 -0
- package/dist/write/param-schema.js +754 -0
- package/dist/write/param-schema.js.map +1 -0
- package/dist/write/pipeline.d.ts +110 -5
- package/dist/write/pipeline.js +288 -14
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +38 -16
- package/dist/write/pre-state.js +117 -22
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/promote-clone.js +658 -444
- package/dist/write/promote-clone.js.map +1 -1
- package/dist/write/reorder.js +25 -8
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/repeat-collateral.d.ts +31 -0
- package/dist/write/repeat-collateral.js +153 -0
- package/dist/write/repeat-collateral.js.map +1 -0
- package/dist/write/repeat-rule.d.ts +28 -1
- package/dist/write/repeat-rule.js +54 -4
- package/dist/write/repeat-rule.js.map +1 -1
- package/dist/write/resolution-timestamps.d.ts +11 -0
- package/dist/write/resolution-timestamps.js +108 -40
- package/dist/write/resolution-timestamps.js.map +1 -1
- package/dist/write/reversibility.js +4 -0
- package/dist/write/reversibility.js.map +1 -1
- package/dist/write/template-mutation.d.ts +137 -0
- package/dist/write/template-mutation.js +598 -0
- package/dist/write/template-mutation.js.map +1 -0
- package/dist/write/undo.js +10 -20
- package/dist/write/undo.js.map +1 -1
- package/dist/write/update-fields.d.ts +151 -0
- package/dist/write/update-fields.js +427 -0
- package/dist/write/update-fields.js.map +1 -0
- package/dist/write/vectors/applescript.js +6 -10
- package/dist/write/vectors/applescript.js.map +1 -1
- package/dist/write/vectors/registry.d.ts +1 -1
- package/dist/write/vectors/registry.js +10 -2
- package/dist/write/vectors/registry.js.map +1 -1
- package/dist/write/vectors/shortcuts.js +2 -2
- package/dist/write/vectors/shortcuts.js.map +1 -1
- package/dist/write/vectors/simulator.js +15 -4
- package/dist/write/vectors/simulator.js.map +1 -1
- package/dist/write/vectors/types.d.ts +268 -1
- package/dist/write/vectors/ui-certification.js +63 -13
- package/dist/write/vectors/ui-certification.js.map +1 -1
- package/dist/write/vectors/ui-chord.d.ts +144 -0
- package/dist/write/vectors/ui-chord.js +428 -0
- package/dist/write/vectors/ui-chord.js.map +1 -0
- package/dist/write/vectors/ui-drag.d.ts +8 -4
- package/dist/write/vectors/ui-drag.js +2 -0
- package/dist/write/vectors/ui-drag.js.map +1 -1
- package/dist/write/vectors/ui-recipes.d.ts +70 -3
- package/dist/write/vectors/ui-recipes.js +482 -44
- package/dist/write/vectors/ui-recipes.js.map +1 -1
- package/dist/write/vectors/ui.d.ts +280 -10
- package/dist/write/vectors/ui.js +1068 -77
- package/dist/write/vectors/ui.js.map +1 -1
- package/dist/write/vectors/url-scheme.js +3 -0
- package/dist/write/vectors/url-scheme.js.map +1 -1
- package/dist/write/verify/delta.d.ts +62 -0
- package/dist/write/verify/delta.js +36 -2
- package/dist/write/verify/delta.js.map +1 -1
- package/dist/write/verify/poller.d.ts +4 -1
- package/dist/write/verify/poller.js +14 -0
- package/dist/write/verify/poller.js.map +1 -1
- package/package.json +2 -1
- package/schema/envelope.schema.json +6 -4
- package/scripts/build-helpers.sh +5 -0
- package/skills/things-cli/SKILL.md +5 -3
- package/skills/things-cli/references/contracts.md +1 -0
- package/skills/things-cli/references/data-model.md +3 -0
- package/skills/things-cli/references/errors.md +1 -1
- package/skills/things-cli/references/gui.md +1 -0
- package/skills/things-cli/references/repeating.md +6 -0
- package/skills/things-cli/references/tag-cleanup.md +64 -0
package/dist/audit/schema.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DeltaSpec, OccurrenceResolution } from "../write/verify/delta.ts";
|
|
1
2
|
export interface AuditRecord {
|
|
2
3
|
v: 1;
|
|
3
4
|
ts: string;
|
|
@@ -41,13 +42,41 @@ export interface AuditRecord {
|
|
|
41
42
|
* bumped) without a separate read. Absent on writes made without the flag.
|
|
42
43
|
*/
|
|
43
44
|
preModDates?: Record<string, number | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Template-target composite disclosure (ADDITIVE): which occurrence a
|
|
47
|
+
* `complete`/`cancel`/`update --exception` aimed at a repeating series
|
|
48
|
+
* actually wrote, and whether the composite minted it. Recorded on the
|
|
49
|
+
* composite's SUMMARY record only — the one record its `opId` keys — so an
|
|
50
|
+
* idempotency replay can hand the caller the same two uuids the original
|
|
51
|
+
* call returned without re-reading the database.
|
|
52
|
+
*/
|
|
53
|
+
occurrence?: OccurrenceResolution;
|
|
54
|
+
/**
|
|
55
|
+
* AMBIGUOUS-OUTCOME reconciliation key (ADDITIVE): the expected-state
|
|
56
|
+
* assertion this attempt was verifying, recorded on `verify-failed:timeout`
|
|
57
|
+
* records only — the one result class where the change may or may not have
|
|
58
|
+
* landed. A resubmission carrying the same `opId` re-evaluates THIS assertion
|
|
59
|
+
* against current state to decide whether the timed-out change is there
|
|
60
|
+
* (replay it as already-applied) or absent (execute normally), so the presence
|
|
61
|
+
* test is the attempt's OWN oracle rather than a per-operation guess
|
|
62
|
+
* (`src/write/opid.ts`). Absent on every other record — and an absent one is a
|
|
63
|
+
* refusal to guess, never an assumption either way.
|
|
64
|
+
*/
|
|
65
|
+
expected?: DeltaSpec;
|
|
44
66
|
/** Normalized requested delta (params as given, post-normalization). */
|
|
45
67
|
requested: Record<string, unknown>;
|
|
46
68
|
/** Asserted-field subset of the pre-state (null when target didn't exist). */
|
|
47
69
|
pre: Record<string, unknown> | null;
|
|
48
70
|
/** Post-verify observation (best-effort on failure). */
|
|
49
71
|
observed: Record<string, unknown> | null;
|
|
50
|
-
result: "intent" | "ok" | "verify-failed:timeout" | "verify-failed:mismatch" | "verify-failed:silent-noop"
|
|
72
|
+
result: "intent" | "ok" | "verify-failed:timeout" | "verify-failed:mismatch" | "verify-failed:silent-noop"
|
|
73
|
+
/** A GUI drive stopped because the Things window was unreachable/unresponsive (#512). */
|
|
74
|
+
| "verify-failed:ui-unreachable"
|
|
75
|
+
/**
|
|
76
|
+
* The requested change landed, but a field the caller never named moved with it
|
|
77
|
+
* and nothing in the operation's vocabulary attributes the movement (CGRD1).
|
|
78
|
+
*/
|
|
79
|
+
| "verify-failed:collateral" | `blocked:${string}` | "unsupported";
|
|
51
80
|
verify: {
|
|
52
81
|
attempts: number;
|
|
53
82
|
elapsedMs: number;
|
package/dist/audit/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/audit/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/audit/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA+FzC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CACvB,MAA0E;IAE1E,IAAI,MAAM,CAAC,GAAG,EAAE,IAAI,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IACzD,yEAAyE;IACzE,wEAAwE;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9B,MAAM,CAAC,EAAE;QACT,MAAM,CAAC,EAAE;QACT,MAAM,CAAC,KAAK;QACZ,MAAM,CAAC,IAAI;QACX,MAAM,CAAC,IAAI,IAAI,EAAE;KAClB,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChF,OAAO,KAAK,MAAM,EAAE,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { type TargetWake } from "./deputy/wake.ts";
|
|
2
|
+
import { type HostAccessDeps, type HostApp } from "./host-access.ts";
|
|
3
|
+
export { fdaGranted, type FdaVerdict, type HostApp, hostApp, hostDisplayName, tccDbPath, } from "./host-access.ts";
|
|
4
|
+
/** The Things application's bundle identifier — the Automation grant's target. */
|
|
5
|
+
export { THINGS_BUNDLE_ID } from "./deputy/wake.ts";
|
|
6
|
+
/** How reads may reach the live library, if at all. */
|
|
7
|
+
export type ReadCapabilityMode =
|
|
8
|
+
/** A caller-supplied database path — outside the doctrine entirely (Article VI). */
|
|
9
|
+
"explicit-db"
|
|
10
|
+
/** Reads ride the sandboxed reader; the read itself is the capability check. */
|
|
11
|
+
| "helpers"
|
|
12
|
+
/** The helpers are expected here but cannot serve — refuse, never fall back. */
|
|
13
|
+
| "helpers-unavailable"
|
|
14
|
+
/** No helpers, but the host app holds Full Disk Access. */
|
|
15
|
+
| "direct-fda"
|
|
16
|
+
/** A ceremony witnessed an app-data grant that is still live for this app instance. */
|
|
17
|
+
| "session-grant"
|
|
18
|
+
/** Nothing can open the container — the caller must run a setup ceremony. */
|
|
19
|
+
| "none";
|
|
20
|
+
/** How GUI-driving may be delivered, if at all (Article IV). */
|
|
21
|
+
export type UiCapabilityMode =
|
|
22
|
+
/** The helper pair holds Accessibility + Automation→System Events. */
|
|
23
|
+
"helpers"
|
|
24
|
+
/** The lab's documented in-guest escape (see {@link UI_DIRECT_ESCAPE_ENV}). */
|
|
25
|
+
| "direct-escape"
|
|
26
|
+
/** GUI-driving is switched off in config — nothing has been asked for yet. */
|
|
27
|
+
| "config-disabled"
|
|
28
|
+
/** No deputy answers, so there is no identity that could hold the grants. */
|
|
29
|
+
| "helpers-missing"
|
|
30
|
+
/** System Events is down and would not start — a liveness fault, not a grant one. */
|
|
31
|
+
| "target-unreachable"
|
|
32
|
+
/** The deputy answers but the `--gui` tier is incomplete. */
|
|
33
|
+
| "tier-incomplete";
|
|
34
|
+
/**
|
|
35
|
+
* Where Things' OWN in-app authorization for the URL scheme stands (URLEN1).
|
|
36
|
+
*
|
|
37
|
+
* This is not a macOS consent class at all — `open -g things:///…` is a
|
|
38
|
+
* LaunchServices dispatch that needs no grant. The gate is the app's: Settings
|
|
39
|
+
* ▸ General ▸ "Enable Things URLs", recorded as `uriSchemeEnabled` in the
|
|
40
|
+
* group-container preferences plist. Three states, all measured.
|
|
41
|
+
*/
|
|
42
|
+
export type UrlSchemeCapabilityMode =
|
|
43
|
+
/** `uriSchemeEnabled = 1` — URL commands execute. */
|
|
44
|
+
"enabled"
|
|
45
|
+
/** `uriSchemeEnabled = 0` — the app drops URL MUTATIONS with no dialog at all. */
|
|
46
|
+
| "disabled"
|
|
47
|
+
/** No key: nobody has answered the app's first-use dialog on this machine. */
|
|
48
|
+
| "never-asked"
|
|
49
|
+
/** The plist cannot be reached prompt-free — never resolved by dispatching. */
|
|
50
|
+
| "unreadable";
|
|
51
|
+
/** How app automation may be delivered, if at all. */
|
|
52
|
+
export type WriteCapabilityMode =
|
|
53
|
+
/** The deputy is onboarded; Apple Events are sent under the helper identity. */
|
|
54
|
+
"deputy"
|
|
55
|
+
/** Things is not running, so where the deputy's grant stands cannot be read. */
|
|
56
|
+
| "deputy-target-dormant"
|
|
57
|
+
/** macOS records an Automation grant for the host app against Things. */
|
|
58
|
+
| "direct-granted"
|
|
59
|
+
/** The lab's documented in-guest escape (see {@link WRITE_DIRECT_ESCAPE_ENV}). */
|
|
60
|
+
| "direct-escape"
|
|
61
|
+
/** macOS records a REFUSAL. It will not re-ask; the human must re-arm it. */
|
|
62
|
+
| "direct-denied"
|
|
63
|
+
/** No record either way, or none readable — never resolved by prompting. */
|
|
64
|
+
| "direct-unknown";
|
|
65
|
+
export interface Capability<Mode> {
|
|
66
|
+
mode: Mode;
|
|
67
|
+
/** One sentence of provenance, for `doctor` and for refusal copy. */
|
|
68
|
+
detail: string;
|
|
69
|
+
/**
|
|
70
|
+
* The remediation lines a refusal prints, in the order they should be
|
|
71
|
+
* offered. Empty when the capability is present.
|
|
72
|
+
*/
|
|
73
|
+
remediation: string[];
|
|
74
|
+
/** The identity a direct-mode grant would attach to. */
|
|
75
|
+
host: HostApp;
|
|
76
|
+
}
|
|
77
|
+
export type ReadCapability = Capability<ReadCapabilityMode>;
|
|
78
|
+
export type WriteCapability = Capability<WriteCapabilityMode>;
|
|
79
|
+
export type UiCapability = Capability<UiCapabilityMode>;
|
|
80
|
+
export type UrlSchemeCapability = Capability<UrlSchemeCapabilityMode>;
|
|
81
|
+
/** True when this verdict permits opening the live container. */
|
|
82
|
+
export declare function readAllowed(capability: ReadCapability): boolean;
|
|
83
|
+
/** True when this verdict permits driving the Things window. */
|
|
84
|
+
export declare function uiAllowed(capability: UiCapability): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* True when a URL-scheme MUTATION may be dispatched.
|
|
87
|
+
*
|
|
88
|
+
* `unreadable` is permissive, and deliberately so — the asymmetry with the
|
|
89
|
+
* write gate is the point. There, an unknown standing is refused because
|
|
90
|
+
* resolving it means sending the Apple Event that IS the dialog. Here,
|
|
91
|
+
* dispatching costs nothing on a machine whose answer is already "enabled",
|
|
92
|
+
* which is every settled machine; only the two states we have positively READ
|
|
93
|
+
* as not-enabled are refused. What catches the unreadable case instead is the
|
|
94
|
+
* read-after-write verify plus its likely-cause hint (src/write/failure-hints.ts).
|
|
95
|
+
*/
|
|
96
|
+
export declare function urlSchemeAllowed(capability: UrlSchemeCapability): boolean;
|
|
97
|
+
/** True when app automation may be dispatched. */
|
|
98
|
+
export declare function writeAllowed(capability: WriteCapability): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Injection seams. Every one of these defaults to a real, prompt-free probe;
|
|
101
|
+
* tests replace them so no test ever touches the host's TCC state.
|
|
102
|
+
*/
|
|
103
|
+
export interface CapabilityDeps extends HostAccessDeps {
|
|
104
|
+
/** Are this process's database reads actually being served by the reader? */
|
|
105
|
+
helpersServing?: () => boolean;
|
|
106
|
+
/** Are the helpers expected on this machine (enabled, and installed under auto)? */
|
|
107
|
+
helpersExpected?: () => boolean;
|
|
108
|
+
/** Why the helpers are not serving, for the loud refusal. */
|
|
109
|
+
helpersReason?: () => string | null;
|
|
110
|
+
/** The deputy's `automation.things` standing, from its handshake. */
|
|
111
|
+
deputyAutomation?: () => string | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* The deputy's GUI-driving standing, from the same handshake: whether it is
|
|
114
|
+
* Accessibility-trusted and where its Automation→System Events grant stands.
|
|
115
|
+
* `null` means no deputy answered at all.
|
|
116
|
+
*/
|
|
117
|
+
deputyGuiStanding?: () => {
|
|
118
|
+
axTrusted: boolean | undefined;
|
|
119
|
+
systemEvents: string | undefined;
|
|
120
|
+
} | null;
|
|
121
|
+
/**
|
|
122
|
+
* Start System Events and re-read its Automation standing, for the one state
|
|
123
|
+
* where the handshake reports liveness instead of authorization
|
|
124
|
+
* (`not-running`). Prompt-free — see ./deputy/wake.ts for why the launch must
|
|
125
|
+
* come before the determination.
|
|
126
|
+
*/
|
|
127
|
+
wakeSystemEvents?: () => TargetWake;
|
|
128
|
+
/**
|
|
129
|
+
* Start Things and re-read its Automation standing — the same liveness step
|
|
130
|
+
* for the AppleScript vector's own target (#617). Consulted ONLY on the
|
|
131
|
+
* dispatch path: a survey never launches the user's app.
|
|
132
|
+
*/
|
|
133
|
+
wakeThings?: () => TargetWake;
|
|
134
|
+
/**
|
|
135
|
+
* Report a woken target's real standing back to the routing layer, which
|
|
136
|
+
* defers its `auto` onboarding gate while Things is closed
|
|
137
|
+
* (./deputy/routing.ts, {@link settleDeputyAutomation}).
|
|
138
|
+
*/
|
|
139
|
+
settleDeputyAutomation?: (standing: string | undefined) => void;
|
|
140
|
+
/** Is GUI-driving switched on in config (`ui-enabled`)? */
|
|
141
|
+
uiEnabled?: () => boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Read one Automation row out of TCC.db. Returns the raw `auth_value`, or
|
|
144
|
+
* null when there is no row / the file cannot be read.
|
|
145
|
+
*/
|
|
146
|
+
automationAuthValue?: (client: string, target: string) => number | null;
|
|
147
|
+
/** Resolve a running app's display name from its bundle id (LaunchServices). */
|
|
148
|
+
lookupAppName?: (bundleId: string) => string | null;
|
|
149
|
+
/**
|
|
150
|
+
* The read standing {@link urlSchemeCapability} consults before it touches
|
|
151
|
+
* the group container. Defaults to a live {@link readCapability} call.
|
|
152
|
+
*/
|
|
153
|
+
readStanding?: () => ReadCapability;
|
|
154
|
+
/** Raw bytes of the Things group-container preferences plist. Throws when unreachable. */
|
|
155
|
+
readPrefsPlist?: () => Buffer;
|
|
156
|
+
/** Pull `uriSchemeEnabled` out of those bytes. Throws when the key is absent. */
|
|
157
|
+
extractUriSchemeEnabled?: (plistBytes: Buffer) => string;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Can this process read the live Things library, and on whose authority?
|
|
161
|
+
*
|
|
162
|
+
* Stateless: every call re-derives the verdict, because a grant can appear or
|
|
163
|
+
* vanish between one invocation and the next and a cached "yes" would be a
|
|
164
|
+
* stored onboarding flag by another name. The common case costs one `open(2)`.
|
|
165
|
+
*/
|
|
166
|
+
export declare function readCapability(options?: {
|
|
167
|
+
dbPath?: string;
|
|
168
|
+
}, deps?: CapabilityDeps): ReadCapability;
|
|
169
|
+
/** Thrown when a read is refused for want of capability (Article II). */
|
|
170
|
+
export declare class ReadCapabilityError extends Error {
|
|
171
|
+
readonly remediation: string[];
|
|
172
|
+
readonly capability: ReadCapability;
|
|
173
|
+
constructor(capability: ReadCapability);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The LAB's escape hatch for the AppleScript vector — the write-side twin of
|
|
177
|
+
* {@link UI_DIRECT_ESCAPE_ENV}, and just as deliberately not consumer surface.
|
|
178
|
+
*
|
|
179
|
+
* A guest shell in a golden clone descends from sshd, not from an application
|
|
180
|
+
* bundle, so `hostApp()` finds no `__CFBundleIdentifier` and macOS has no
|
|
181
|
+
* identity to have recorded an Automation grant against. The verdict is
|
|
182
|
+
* therefore `direct-unknown` in every clone, which blocks every AppleScript-
|
|
183
|
+
* vector verb and every composite carrying an AppleScript leg. What the clone
|
|
184
|
+
* actually has is an in-guest Automation grant on the runner's own processes
|
|
185
|
+
* (the same AXVM1 layer the ui escape leans on), so the block is an artefact of
|
|
186
|
+
* UNKNOWABILITY, not of a missing grant. Setting this to `1` says so.
|
|
187
|
+
*
|
|
188
|
+
* Bounded, and the bound is the point: it is consulted ONLY on the
|
|
189
|
+
* bundle-id-less path. A host that has an identity is answered from its TCC row
|
|
190
|
+
* as it always was, so the escape can never mask a recorded refusal
|
|
191
|
+
* (`direct-denied`) or manufacture a grant for a real user's terminal.
|
|
192
|
+
* Documented in docs/lab/harness.md and exported by the lab's guest
|
|
193
|
+
* environment; nothing consumer-facing mentions it.
|
|
194
|
+
*/
|
|
195
|
+
export declare const WRITE_DIRECT_ESCAPE_ENV = "THINGS_API_WRITE_DIRECT";
|
|
196
|
+
/**
|
|
197
|
+
* What a {@link writeCapability} verdict is FOR — the one thing that decides
|
|
198
|
+
* whether a dormant Things may be started while the verdict is taken (#617).
|
|
199
|
+
*
|
|
200
|
+
* The asymmetry with the GUI preflight is deliberate. System Events is a
|
|
201
|
+
* headless macOS component nobody sees, so {@link uiCapability} wakes it for
|
|
202
|
+
* every caller. Things is the user's own app: starting it is visible, so only a
|
|
203
|
+
* caller that is ABOUT TO DRIVE IT may do so.
|
|
204
|
+
*/
|
|
205
|
+
export interface WriteCapabilityOptions {
|
|
206
|
+
/**
|
|
207
|
+
* - `survey` (the default) — `doctor`, the MCP startup bake, `--dry-run`.
|
|
208
|
+
* Launches nothing and reports a closed Things as the liveness state
|
|
209
|
+
* `deputy-target-dormant`.
|
|
210
|
+
* - `dispatch` — the write gate and the two setup ceremonies, which are about
|
|
211
|
+
* to send Things an Apple Event anyway. A dormant target is started in the
|
|
212
|
+
* background first and its standing re-read, which is also what keeps the
|
|
213
|
+
* operation at tier 0/1: an Apple Event to a CLOSED Things auto-launches it
|
|
214
|
+
* WITH focus steal (A40/A41), a background pre-launch does not.
|
|
215
|
+
*/
|
|
216
|
+
purpose?: "survey" | "dispatch";
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* May this process drive Things over Apple Events, and on whose authority?
|
|
220
|
+
*
|
|
221
|
+
* The deputy wins when it is onboarded (its own handshake reports the grant it
|
|
222
|
+
* holds). Otherwise the host app's own Automation record is read out of TCC —
|
|
223
|
+
* granted, denied, or, when there is no record at all, `direct-unknown`. That
|
|
224
|
+
* last state is deliberately NOT resolved here: resolving it means sending a
|
|
225
|
+
* real Apple Event, which is what raises the dialog, and Article I reserves
|
|
226
|
+
* that for `things setup`.
|
|
227
|
+
*
|
|
228
|
+
* LIVENESS BEFORE AUTHORIZATION (#617). The deputy's `not-running` is the
|
|
229
|
+
* ask-false determination having no answer for a CLOSED Things — a fact about
|
|
230
|
+
* the app's process, not about the grant. Two rules follow, and both matter:
|
|
231
|
+
*
|
|
232
|
+
* - while the deputy is standing, that value NEVER falls through to the direct
|
|
233
|
+
* host branch. A silent direct engagement would put consent back on the
|
|
234
|
+
* terminal (the routing doctrine's no-fallback rule), and on a machine with
|
|
235
|
+
* no host record it would refuse a fully onboarded user with "run
|
|
236
|
+
* `things setup`" — the #610 false-onboarding loop, one vector over;
|
|
237
|
+
* - a `dispatch` caller resolves it by STARTING Things (a background
|
|
238
|
+
* LaunchServices dispatch, never an Apple Event) and re-reading the
|
|
239
|
+
* standing. Only what comes back is an authorization fact.
|
|
240
|
+
*/
|
|
241
|
+
export declare function writeCapability(options?: WriteCapabilityOptions, deps?: CapabilityDeps): WriteCapability;
|
|
242
|
+
/** Thrown when app automation is refused for want of capability (Article II). */
|
|
243
|
+
export declare class WriteCapabilityError extends Error {
|
|
244
|
+
readonly remediation: string[];
|
|
245
|
+
readonly capability: WriteCapability;
|
|
246
|
+
constructor(capability: WriteCapability);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* The LAB's escape hatch for the ui vector, and deliberately not consumer
|
|
250
|
+
* surface (its write-side twin is {@link WRITE_DIRECT_ESCAPE_ENV}). The VM lab and
|
|
251
|
+
* the guest e2e bundle drive the UI vector DIRECT — the in-guest Accessibility
|
|
252
|
+
* grant is held by the runner's own processes (the AXVM1 layer), there is no
|
|
253
|
+
* helper bundle in a disposable clone, and there is nobody to answer a dialog
|
|
254
|
+
* either. Setting this to `1` restores direct UI-vector availability for that
|
|
255
|
+
* one situation. It is documented in docs/lab/harness.md and exported by the
|
|
256
|
+
* lab's guest environment; nothing consumer-facing mentions it, and it does not
|
|
257
|
+
* bypass `ui.enabled` — a lab clone still sets that key explicitly.
|
|
258
|
+
*/
|
|
259
|
+
export declare const UI_DIRECT_ESCAPE_ENV = "THINGS_API_UI_DIRECT";
|
|
260
|
+
/**
|
|
261
|
+
* May this process drive the Things WINDOW, and on whose authority?
|
|
262
|
+
*
|
|
263
|
+
* Article IV admits exactly one provenance: the helper pair. Accessibility on a
|
|
264
|
+
* general-purpose host app (a terminal, an agent harness, an MCP host) has a
|
|
265
|
+
* blast radius far beyond Things, churns with every host update, and has no
|
|
266
|
+
* sane story at all over ssh — so direct AX is unsupported, and a refusal here
|
|
267
|
+
* names the config knob and `things helpers setup --gui` rather than raising an
|
|
268
|
+
* Accessibility prompt against whatever happens to be running us.
|
|
269
|
+
*
|
|
270
|
+
* Every answer is prompt-free: the config key is a file read, and the deputy's
|
|
271
|
+
* `hello` carries `AXIsProcessTrusted()` plus its own `AEDeterminePermission`
|
|
272
|
+
* verdict for System Events. The one state that needs more than a read is a
|
|
273
|
+
* DORMANT System Events, which macOS reaps whenever it has been idle: the
|
|
274
|
+
* target is started in the background — never by sending it an event — and the
|
|
275
|
+
* determination re-read, so `not-running` resolves to the truth instead of
|
|
276
|
+
* masquerading as a missing grant (./deputy/wake.ts).
|
|
277
|
+
*/
|
|
278
|
+
export declare function uiCapability(deps?: CapabilityDeps): UiCapability;
|
|
279
|
+
/**
|
|
280
|
+
* Has Things been authorized to act on `things:///` commands, and how do we know?
|
|
281
|
+
*
|
|
282
|
+
* Stateless per invocation, like every other verdict here: the setting is the
|
|
283
|
+
* user's to flip at any moment, and a cached "yes" would be a stored onboarding
|
|
284
|
+
* flag by another name. The common case costs one file read plus one `plutil`.
|
|
285
|
+
*
|
|
286
|
+
* WHY THIS IS GATED ON THE READ STANDING. The plist lives inside the Things
|
|
287
|
+
* group container, which is the same `kTCCServiceSystemPolicyAppData` class as
|
|
288
|
+
* the database — so the open is itself what would raise the app-data modal on a
|
|
289
|
+
* machine that holds no standing. This function therefore never opens it
|
|
290
|
+
* speculatively: it asks {@link readCapability} first and reports `unreadable`
|
|
291
|
+
* unless the container is already reachable (helpers, FDA, or a live session
|
|
292
|
+
* grant). Where the helpers are serving, the read rides the reader's own
|
|
293
|
+
* security-scoped bookmark over the container, so the prefs plist is inside the
|
|
294
|
+
* granted subtree and no host grant is involved at all.
|
|
295
|
+
*
|
|
296
|
+
* MEASURED, all three states (URLEN1, golden-v4 / Things 3.23):
|
|
297
|
+
*
|
|
298
|
+
* - `1` — URL mutations execute.
|
|
299
|
+
* - `0` — every mutating verb (`add`, token-bearing `update`, the `json`
|
|
300
|
+
* batch) is dropped in TOTAL SILENCE: zero row delta, no dialog, no window
|
|
301
|
+
* of any kind, and nothing to wait for. Navigation URLs (`things:///show`)
|
|
302
|
+
* still work, so this gate covers mutations only.
|
|
303
|
+
* - absent — nobody has answered the app's own first-use "Things URL Scheme"
|
|
304
|
+
* dialog (Cancel / Enable). The dispatched request PARKS behind that dialog
|
|
305
|
+
* rather than being dropped, which is what #611 saw: with nobody at the
|
|
306
|
+
* machine, the verify window expired and the write reported a silent no-op.
|
|
307
|
+
*/
|
|
308
|
+
export declare function urlSchemeCapability(deps?: CapabilityDeps): UrlSchemeCapability;
|
|
309
|
+
/** Thrown when GUI-driving is refused for want of capability (Article IV). */
|
|
310
|
+
export declare class UiCapabilityError extends Error {
|
|
311
|
+
readonly remediation: string[];
|
|
312
|
+
readonly capability: UiCapability;
|
|
313
|
+
constructor(capability: UiCapability);
|
|
314
|
+
}
|
|
315
|
+
/** Thrown when a URL-scheme mutation is refused for want of the app's own authorization. */
|
|
316
|
+
export declare class UrlSchemeCapabilityError extends Error {
|
|
317
|
+
readonly remediation: string[];
|
|
318
|
+
readonly capability: UrlSchemeCapability;
|
|
319
|
+
constructor(capability: UrlSchemeCapability);
|
|
320
|
+
}
|
|
321
|
+
/** Test seam: forget the one memo this module keeps (the host's display name). */
|
|
322
|
+
export declare function resetCapabilityForTests(): void;
|