things-api 0.19.2 → 0.19.4
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/prebuilt/Things API Helper.app/Contents/CodeResources +0 -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/MacOS/things-deputy +0 -0
- package/dist/audit/schema.d.ts +14 -0
- package/dist/audit/schema.js.map +1 -1
- package/dist/cli/commands/op-result.js +13 -1
- package/dist/cli/commands/op-result.js.map +1 -1
- package/dist/cli/commands/writes.js +51 -7
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +7 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/op-result.d.ts +9 -0
- package/dist/op-result.js +5 -0
- package/dist/op-result.js.map +1 -1
- package/dist/ui-state.d.ts +1 -1
- package/dist/ui-state.js +33 -8
- package/dist/ui-state.js.map +1 -1
- package/dist/write/batch.js +4 -1
- package/dist/write/batch.js.map +1 -1
- package/dist/write/clone.js +12 -6
- package/dist/write/clone.js.map +1 -1
- package/dist/write/disclosures.d.ts +263 -0
- package/dist/write/disclosures.js +265 -0
- package/dist/write/disclosures.js.map +1 -0
- package/dist/write/heading.js +4 -7
- package/dist/write/heading.js.map +1 -1
- package/dist/write/make-repeating-project.js +2 -1
- package/dist/write/make-repeating-project.js.map +1 -1
- package/dist/write/opid.js +5 -3
- package/dist/write/opid.js.map +1 -1
- package/dist/write/pipeline.d.ts +39 -1
- package/dist/write/pipeline.js +49 -23
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/promote-clone.js +46 -36
- package/dist/write/promote-clone.js.map +1 -1
- package/dist/write/reorder.js +45 -39
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/resolution-timestamps.js +4 -1
- package/dist/write/resolution-timestamps.js.map +1 -1
- package/dist/write/spawn-expectation.d.ts +135 -0
- package/dist/write/spawn-expectation.js +237 -0
- package/dist/write/spawn-expectation.js.map +1 -0
- package/dist/write/template-mutation.js +20 -33
- package/dist/write/template-mutation.js.map +1 -1
- package/dist/write/vectors/types.d.ts +12 -0
- package/dist/write/vectors/ui-recipes.js +11 -1
- package/dist/write/vectors/ui-recipes.js.map +1 -1
- package/dist/write/vectors/ui-state.d.ts +136 -6
- package/dist/write/vectors/ui-state.js +372 -70
- package/dist/write/vectors/ui-state.js.map +1 -1
- package/dist/write/vectors/ui.d.ts +62 -2
- package/dist/write/vectors/ui.js +401 -151
- package/dist/write/vectors/ui.js.map +1 -1
- package/dist/write/verify/delta.d.ts +17 -2
- package/dist/write/verify/delta.js +46 -16
- package/dist/write/verify/delta.js.map +1 -1
- package/dist/write/verify/poller.d.ts +3 -2
- package/dist/write/verify/poller.js +3 -1
- package/dist/write/verify/poller.js.map +1 -1
- package/package.json +1 -1
- package/skills/things-cli/SKILL.md +1 -1
|
@@ -65,6 +65,39 @@ export type UiSheetForm = "none" | "attached" | "detached";
|
|
|
65
65
|
* `missing value`), `sheetForm` and `sheetDepth` bound.
|
|
66
66
|
*/
|
|
67
67
|
export declare const AX_DIALOG_SHELL_SNIPPET = "\t\tset shellRef to missing value\n\t\tset sheetForm to \"none\"\n\t\tset sheetDepth to 0\n\t\ttry\n\t\t\t-- positional-ok: the one attached sheet a window can present, which is\n\t\t\t-- the BOTTOM of any stack; the walk below climbs to the top.\n\t\t\tset shellRef to sheet 1 of (first window whose subrole is \"AXStandardWindow\")\n\t\t\tset sheetForm to \"attached\"\n\t\t\tset sheetDepth to 1\n\t\tend try\n\t\tif shellRef is missing value then\n\t\t\ttry\n\t\t\t\tset dws to (windows whose subrole is \"AXUnknown\" and size is not {40, 40})\n\t\t\t\tif (count of dws) > 0 then\n\t\t\t\t\tset shellRef to item 1 of dws\n\t\t\t\t\tset sheetForm to \"detached\"\n\t\t\t\t\tset sheetDepth to 1\n\t\t\t\tend if\n\t\t\tend try\n\t\tend if\n\t\tif shellRef is not missing value then\n\t\t\trepeat 6 times\n\t\t\t\tset nested to missing value\n\t\t\t\ttry\n\t\t\t\t\tif (exists sheet 1 of shellRef) then set nested to sheet 1 of shellRef\n\t\t\t\tend try\n\t\t\t\tif nested is missing value then exit repeat\n\t\t\t\tset shellRef to nested\n\t\t\t\tset sheetDepth to sheetDepth + 1\n\t\t\tend repeat\n\t\tend if";
|
|
68
|
+
/**
|
|
69
|
+
* The census's individually-budgeted probes, in the order they run. The first
|
|
70
|
+
* three are DECISION-CRITICAL — the guard, the drive preflight and the cleanup
|
|
71
|
+
* ladder all decide on them — and the last two are DECORATION for the refusal
|
|
72
|
+
* copy. That order is the point: everything a caller acts on is proven before a
|
|
73
|
+
* single unaddressed query is attempted.
|
|
74
|
+
*
|
|
75
|
+
* - `running` — is there a Things process at all (a name-keyed lookup);
|
|
76
|
+
* - `frontmost` — `frontmost of process "Things3"`, ADDRESSED: the one fact a
|
|
77
|
+
* keystroke guard actually needs;
|
|
78
|
+
* - `dialog` — the dialog shell, its form, its stack depth and its control
|
|
79
|
+
* census, all addressed inside `process "Things3"`;
|
|
80
|
+
* - `frontapp` — WHICH other application owns the screen. This is the whole
|
|
81
|
+
* process table enumerated (`first application process whose
|
|
82
|
+
* frontmost is true`), so it runs only when the addressed
|
|
83
|
+
* `frontmost` probe already said the answer is not Things, and
|
|
84
|
+
* only for the sentence that names the thief;
|
|
85
|
+
* - `focus` — the focused element's role, through
|
|
86
|
+
* `AXFocusedUIElement`. MEASURED at ~3.5x the cost of every
|
|
87
|
+
* addressed probe in this vector even on a bare clone
|
|
88
|
+
* (docs/lab/fgrd2-census-hardening.md §2), and it decides
|
|
89
|
+
* nothing — so it goes last, inside its own budget, and its
|
|
90
|
+
* absence costs a clause in a sentence.
|
|
91
|
+
*/
|
|
92
|
+
export type UiProbe = "running" | "frontmost" | "dialog" | "frontapp" | "focus";
|
|
93
|
+
/**
|
|
94
|
+
* The transport deadline for one census hop — the backstop under the in-script
|
|
95
|
+
* budgets, not the mechanism. Deliberately far below the 15s step timeout that
|
|
96
|
+
* bounded the old single-shot script: nothing here may take this long, and if
|
|
97
|
+
* it does the caller must hear about it while the drive can still be aborted
|
|
98
|
+
* cleanly rather than after four of them have gone by.
|
|
99
|
+
*/
|
|
100
|
+
export declare const CENSUS_TIMEOUT_MS = 12000;
|
|
68
101
|
/** Who owns keyboard focus. Role only — never the element's value or title (see PRIVACY). */
|
|
69
102
|
export interface UiFocusOwner {
|
|
70
103
|
/** The frontmost application's process name (e.g. "Things3"). */
|
|
@@ -105,9 +138,30 @@ export interface UiState {
|
|
|
105
138
|
* of a SECURE SYSTEM MODAL (a macOS privacy/consent dialog), which belongs to
|
|
106
139
|
* no application's Accessibility tree. Everything else in the census is then
|
|
107
140
|
* "what could still be proven", never a guess.
|
|
141
|
+
*
|
|
142
|
+
* A probe that TIMED OUT does not set this: "macOS refused to describe the
|
|
143
|
+
* screen" and "the screen did not answer in time" are different facts with
|
|
144
|
+
* different remediations, and #629 is what conflating them costs.
|
|
108
145
|
*/
|
|
109
146
|
inspectable: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* The probes that did not answer within their budget. Non-empty means the
|
|
149
|
+
* corresponding fields are UNPROVEN, not false — every caller reads them that
|
|
150
|
+
* way, and a critical probe here aborts a drive instead of being retried.
|
|
151
|
+
*/
|
|
152
|
+
stalledProbes: UiProbe[];
|
|
153
|
+
/**
|
|
154
|
+
* The probes that answered with an error rather than a value (the surface is
|
|
155
|
+
* there but would not describe itself — a secure modal, a process that exited
|
|
156
|
+
* between two reads). Reported for the same reason: what could not be proven
|
|
157
|
+
* is named, never guessed at.
|
|
158
|
+
*/
|
|
159
|
+
failedProbes: UiProbe[];
|
|
110
160
|
}
|
|
161
|
+
/** Did a probe the caller has to DECIDE on fail to answer? */
|
|
162
|
+
export declare function censusUnverifiable(state: UiState | null): boolean;
|
|
163
|
+
/** Name the probes that did not answer, for a diagnostic someone has to act on. */
|
|
164
|
+
export declare function describeUnprovenProbes(state: UiState): string;
|
|
111
165
|
/** A recognizable token in the script so a test runner can key off the ui-state command. */
|
|
112
166
|
export declare const UI_STATE_MARKER = "-- ui-state census (read-only)";
|
|
113
167
|
/** The label every ui-state dispatch carries (one stable command shape). */
|
|
@@ -115,12 +169,78 @@ export declare const UI_STATE_LABEL = "read the window and focus state";
|
|
|
115
169
|
/** The Things application's process name — the frontmost value that means "us". */
|
|
116
170
|
export declare const THINGS_PROCESS = "Things3";
|
|
117
171
|
/**
|
|
118
|
-
* The census script
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
172
|
+
* The census script — ADDRESSED probes, each on its own Apple-event budget
|
|
173
|
+
* (issue #629).
|
|
174
|
+
*
|
|
175
|
+
* WHAT CHANGED, AND WHY IT HAD TO. The 0.19.2 census was one unbounded script
|
|
176
|
+
* that opened with two UNADDRESSED queries: `first application process whose
|
|
177
|
+
* frontmost is true` (the entire process table enumerated) and `value of
|
|
178
|
+
* attribute "AXFocusedUIElement"` on whatever that returned (a system-wide
|
|
179
|
+
* focused-element resolution). Everything else the ui vector runs — every step
|
|
180
|
+
* the field incident's log shows succeeding, inside the very sheet the census
|
|
181
|
+
* could not describe — is addressed: `tell process "Things3" to …`. So the one
|
|
182
|
+
* script that stalled was the one script that left the addressed style, and it
|
|
183
|
+
* stalled on the critical path of BOTH the per-step guard and the cleanup that
|
|
184
|
+
* was supposed to recover from it. Rebuilt here so that:
|
|
185
|
+
*
|
|
186
|
+
* - the decision-critical facts are ADDRESSED and are proven FIRST;
|
|
187
|
+
* - every read carries `with timeout of ${PROBE_TIMEOUT_S} seconds`, so a
|
|
188
|
+
* surface that will not answer costs seconds, not the caller's whole
|
|
189
|
+
* deadline;
|
|
190
|
+
* - a critical probe that does not answer STOPS the census then and there —
|
|
191
|
+
* the remaining probes cannot change what the caller must now do (abort and
|
|
192
|
+
* clean up), so waiting for them is pure latency;
|
|
193
|
+
* - the two unaddressed queries survive only as DECORATION, last, skipped
|
|
194
|
+
* entirely when the addressed probes already answered the question they
|
|
195
|
+
* were there to answer.
|
|
196
|
+
*
|
|
197
|
+
* Every probe still degrades one field rather than failing the census, and what
|
|
198
|
+
* could not be proven is NAMED (`stalled=` / `failed=`) instead of silently
|
|
199
|
+
* reading as a clean "nothing is open".
|
|
122
200
|
*/
|
|
123
201
|
export declare function axUiStateScript(): string;
|
|
202
|
+
/** The stderr line prefix carrying a folded guard's census record. */
|
|
203
|
+
export declare const GUARD_LOG_PREFIX = "#FGCENSUS ";
|
|
204
|
+
/** The separator joining that record's fields on its single line. */
|
|
205
|
+
export declare const GUARD_LOG_SEP = " ~|~ ";
|
|
206
|
+
/**
|
|
207
|
+
* What a folded guard RAISES when it refuses. Deliberately a machine tag rather
|
|
208
|
+
* than a sentence: the refusal a caller reads is still built by
|
|
209
|
+
* {@link judgeFocusGuard} in TypeScript, from the census this same hop logged, so
|
|
210
|
+
* there is exactly ONE place the wording lives and the in-script judgement can
|
|
211
|
+
* never drift from it.
|
|
212
|
+
*/
|
|
213
|
+
export declare const GUARD_REFUSED_TAG = "#FGREFUSE";
|
|
214
|
+
/**
|
|
215
|
+
* The census as the IN-SCRIPT PRELUDE of the hop it guards (DRVLAT1, issue #633).
|
|
216
|
+
*
|
|
217
|
+
* The per-step focus guard used to be its own osascript round-trip: census hop,
|
|
218
|
+
* then keystroke hop. That is both a hop of latency per typed control AND a
|
|
219
|
+
* TOCTOU window — the screen can change between the census that approved the
|
|
220
|
+
* keystroke and the keystroke itself. Prepending the census to the very script
|
|
221
|
+
* that types closes both: the probes and the input now run in one process, in
|
|
222
|
+
* order, with nothing dispatched in between.
|
|
223
|
+
*
|
|
224
|
+
* The prelude LOGS its census record (stderr, one line) and then judges it
|
|
225
|
+
* IN-SCRIPT, raising {@link GUARD_REFUSED_TAG} — a bare tag — when the input must
|
|
226
|
+
* not be sent. The caller recovers the logged record, parses it into the same
|
|
227
|
+
* {@link UiState} the stand-alone census yields, and asks {@link judgeFocusGuard}
|
|
228
|
+
* for the sentence. So the DECISION is made before the keystroke, in-script, and
|
|
229
|
+
* the WORDING is still single-sourced in TypeScript.
|
|
230
|
+
*
|
|
231
|
+
* `expectedSheet` is the dialog the drive has observed itself driving; pass null
|
|
232
|
+
* where no dialog invariant applies.
|
|
233
|
+
*/
|
|
234
|
+
export declare function axFocusGuardPrelude(expectedSheet: UiSheetKind | null): string;
|
|
235
|
+
/**
|
|
236
|
+
* Recover a folded guard's census from the hop's stderr: the parsed state (null
|
|
237
|
+
* when no record was logged) and the stderr with that line removed, so a failure
|
|
238
|
+
* message a caller reads never carries the machinery.
|
|
239
|
+
*/
|
|
240
|
+
export declare function parseGuardLog(stderr: string): {
|
|
241
|
+
state: UiState | null;
|
|
242
|
+
stderr: string;
|
|
243
|
+
};
|
|
124
244
|
/**
|
|
125
245
|
* Parse the census record into a {@link UiState}. Returns null when the output
|
|
126
246
|
* carries none of the expected keys (a transport error, or a script that never
|
|
@@ -131,8 +251,11 @@ export declare function parseUiState(stdout: string): UiState | null;
|
|
|
131
251
|
* Read the census through the injected runner. Returns null on a transport
|
|
132
252
|
* failure — an UNKNOWN state, which every caller treats fail-closed (the guard
|
|
133
253
|
* refuses; the cleanup path does not send a blind Escape).
|
|
254
|
+
*
|
|
255
|
+
* `timeoutMs` defaults to {@link CENSUS_TIMEOUT_MS}: a census is not a drive
|
|
256
|
+
* step and must not borrow a drive step's patience (issue #629).
|
|
134
257
|
*/
|
|
135
|
-
export declare function readUiState(run: (command: UiCommand, timeoutMs: number) => Promise<UiRunResult>, timeoutMs
|
|
258
|
+
export declare function readUiState(run: (command: UiCommand, timeoutMs: number) => Promise<UiRunResult>, timeoutMs?: number): Promise<UiState | null>;
|
|
136
259
|
/**
|
|
137
260
|
* How to name the surface that owns the screen, for a refusal someone has to
|
|
138
261
|
* act on. Names the APPLICATION and the focused element's ROLE — never its
|
|
@@ -141,7 +264,14 @@ export declare function readUiState(run: (command: UiCommand, timeoutMs: number)
|
|
|
141
264
|
* at the keyboard has to look at the screen themselves.
|
|
142
265
|
*/
|
|
143
266
|
export declare function describeFocusOwner(state: UiState | null): string;
|
|
144
|
-
/**
|
|
267
|
+
/**
|
|
268
|
+
* A one-line human summary of the census, for a diagnostic line or a warning.
|
|
269
|
+
*
|
|
270
|
+
* Reports WHAT EACH PROBE PROVED. A stalled probe leaves its clause reading
|
|
271
|
+
* "could not be determined" and is named at the end — never omitted, and never
|
|
272
|
+
* rendered as its default (issue #629: a census that could not see the open
|
|
273
|
+
* Repeat sheet used to report a clean screen).
|
|
274
|
+
*/
|
|
145
275
|
export declare function describeUiState(state: UiState): string;
|
|
146
276
|
/**
|
|
147
277
|
* The warning an OPEN Things dialog earns, wherever it is reported.
|