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,467 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* things-helpers lifecycle: install/uninstall the launchd LaunchAgents for
|
|
3
|
+
* both helper halves, restart them, run the reader's grant ceremony, and
|
|
4
|
+
* report status. launchd owns both processes end-to-end (RunAtLoad +
|
|
5
|
+
* KeepAlive) — there is never a detached or self-daemonized process, and a
|
|
6
|
+
* crashed helper relaunches with its identity (and therefore its macOS
|
|
7
|
+
* permission grants) intact.
|
|
8
|
+
*
|
|
9
|
+
* Both halves ship inside ONE bundle (Things API Helper.app): things-deputy
|
|
10
|
+
* (automation, unsandboxed) is the bundle's main executable and the sandboxed
|
|
11
|
+
* things-reader.app nests under Contents/Helpers with its own bundle identity
|
|
12
|
+
* — that identity (com.pixelcog.things-reader) keys the user's security-scoped
|
|
13
|
+
* bookmark grant and must never change.
|
|
14
|
+
*/
|
|
15
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
16
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { dirname, join } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { THINGS_GROUP_CONTAINER } from "../db/locate.js";
|
|
21
|
+
import { DeputySyncBridge } from "./bridge.js";
|
|
22
|
+
import { DEPUTY_LAUNCHD_LABEL, HELPERS_BUNDLE_ID, READER_LAUNCHD_LABEL, DEPUTY_PROTOCOL_VERSION, deputyInstalledBinaryPath, deputySocketPath, deputyStateDir, deputyTokenPath, helpersInstallDir, helpersInstalledBundlePath, readerInstalledAppPath, readerSocketPath, readerTokenPath, } from "./protocol.js";
|
|
23
|
+
export function deputyPlistPath() {
|
|
24
|
+
return join(homedir(), "Library/LaunchAgents", `${DEPUTY_LAUNCHD_LABEL}.plist`);
|
|
25
|
+
}
|
|
26
|
+
export function readerPlistPath() {
|
|
27
|
+
return join(homedir(), "Library/LaunchAgents", `${READER_LAUNCHD_LABEL}.plist`);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Where an install looks for the bundle when the caller names no path, in
|
|
31
|
+
* preference order: `deputy/prebuilt` — the signed + notarized bundle the
|
|
32
|
+
* release workflow stages into the published tarball, so an npm install needs
|
|
33
|
+
* neither Xcode nor a certificate — then `deputy/build`, the output of
|
|
34
|
+
* scripts/build-helpers.sh in a source checkout.
|
|
35
|
+
*/
|
|
36
|
+
export function helpersBundleCandidates() {
|
|
37
|
+
return [
|
|
38
|
+
fileURLToPath(new URL("../../deputy/prebuilt/Things API Helper.app", import.meta.url)),
|
|
39
|
+
fileURLToPath(new URL("../../deputy/build/Things API Helper.app", import.meta.url)),
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The first candidate bundle that carries a deputy executable; null when none
|
|
44
|
+
* does. Both candidates are READ only — nothing in the `things helpers` path
|
|
45
|
+
* ever writes inside the package directory, so the CLI works from a read-only
|
|
46
|
+
* package root (an npx cache); BUILDING a bundle there is the one operation
|
|
47
|
+
* that needs a writable checkout, and `--bundle <path>` covers a prebuilt one.
|
|
48
|
+
*/
|
|
49
|
+
export function helpersDefaultBuildPath() {
|
|
50
|
+
return (helpersBundleCandidates().find((path) => existsSync(join(path, "Contents/MacOS/things-deputy"))) ?? null);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The INSTALLED bundle's version, read prompt-free from its Info.plist
|
|
54
|
+
* (CFBundleShortVersionString, stamped from deputy/VERSION at build time).
|
|
55
|
+
* Null when nothing is installed or the plist is unreadable/unstamped. This is
|
|
56
|
+
* the version a passive upgrade notice compares against
|
|
57
|
+
* {@link EXPECTED_HELPERS_VERSION} without needing a running helper.
|
|
58
|
+
*/
|
|
59
|
+
export function installedHelpersVersion(env = process.env) {
|
|
60
|
+
const plist = join(helpersInstalledBundlePath(env), "Contents/Info.plist");
|
|
61
|
+
try {
|
|
62
|
+
const xml = readFileSync(plist, "utf8");
|
|
63
|
+
const match = /<key>CFBundleShortVersionString<\/key>\s*<string>([^<]*)<\/string>/.exec(xml);
|
|
64
|
+
const value = match?.[1]?.trim();
|
|
65
|
+
return value !== undefined && value !== "" ? value : null;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function readerLaunchTarget() {
|
|
72
|
+
return `gui/${process.getuid?.() ?? 501}/${READER_LAUNCHD_LABEL}`;
|
|
73
|
+
}
|
|
74
|
+
function readerExecPath(appPath) {
|
|
75
|
+
return join(appPath, "Contents/MacOS/things-reader");
|
|
76
|
+
}
|
|
77
|
+
function renderReaderPlist(appPath) {
|
|
78
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
79
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
80
|
+
<plist version="1.0">
|
|
81
|
+
<dict>
|
|
82
|
+
<key>Label</key>
|
|
83
|
+
<string>${READER_LAUNCHD_LABEL}</string>
|
|
84
|
+
<key>ProgramArguments</key>
|
|
85
|
+
<array>
|
|
86
|
+
<string>${readerExecPath(appPath)}</string>
|
|
87
|
+
<string>--serve</string>
|
|
88
|
+
</array>
|
|
89
|
+
<key>RunAtLoad</key>
|
|
90
|
+
<true/>
|
|
91
|
+
<key>KeepAlive</key>
|
|
92
|
+
<true/>
|
|
93
|
+
<!-- Background Task Management groups the login item under the helper
|
|
94
|
+
bundle's display name ("Things API Helper") instead of the signing
|
|
95
|
+
certificate's personal name. -->
|
|
96
|
+
<key>AssociatedBundleIdentifiers</key>
|
|
97
|
+
<array>
|
|
98
|
+
<string>${HELPERS_BUNDLE_ID}</string>
|
|
99
|
+
</array>
|
|
100
|
+
</dict>
|
|
101
|
+
</plist>
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
104
|
+
function launchTarget() {
|
|
105
|
+
return `gui/${process.getuid?.() ?? 501}/${DEPUTY_LAUNCHD_LABEL}`;
|
|
106
|
+
}
|
|
107
|
+
function launchctl(args) {
|
|
108
|
+
try {
|
|
109
|
+
// stderr must be captured, never inherited: a routine negative probe
|
|
110
|
+
// ("Could not find service … in domain") is a state we REPORT, not noise
|
|
111
|
+
// the child gets to print over our own output.
|
|
112
|
+
//
|
|
113
|
+
// The timeout must clear the helpers' DRAIN bound: `bootout`/`kickstart -k`
|
|
114
|
+
// block until the old process exits, and a helper with a request in flight
|
|
115
|
+
// takes up to HELPERS_DRAIN_TIMEOUT_MS to finish it before exiting.
|
|
116
|
+
const output = execFileSync("launchctl", args, {
|
|
117
|
+
encoding: "utf8",
|
|
118
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
119
|
+
timeout: 30_000,
|
|
120
|
+
});
|
|
121
|
+
return { ok: true, output };
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
const e = err;
|
|
125
|
+
return { ok: false, output: `${e.stdout ?? ""}${e.stderr ?? ""}` || (e.message ?? "failed") };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function renderPlist(binaryPath, stateDir) {
|
|
129
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
130
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
131
|
+
<plist version="1.0">
|
|
132
|
+
<dict>
|
|
133
|
+
<key>Label</key>
|
|
134
|
+
<string>${DEPUTY_LAUNCHD_LABEL}</string>
|
|
135
|
+
<key>ProgramArguments</key>
|
|
136
|
+
<array>
|
|
137
|
+
<string>${binaryPath}</string>
|
|
138
|
+
<string>--state-dir</string>
|
|
139
|
+
<string>${stateDir}</string>
|
|
140
|
+
</array>
|
|
141
|
+
<key>RunAtLoad</key>
|
|
142
|
+
<true/>
|
|
143
|
+
<key>KeepAlive</key>
|
|
144
|
+
<true/>
|
|
145
|
+
<!-- Interactive: the deputy fronts GUI accessibility work; keep it out of
|
|
146
|
+
background-QoS throttling so drives stay as fast as direct execution. -->
|
|
147
|
+
<key>ProcessType</key>
|
|
148
|
+
<string>Interactive</string>
|
|
149
|
+
<key>StandardErrorPath</key>
|
|
150
|
+
<string>${stateDir}/deputy.stderr.log</string>
|
|
151
|
+
<!-- Background Task Management groups the login item under the helper
|
|
152
|
+
bundle's display name ("Things API Helper") instead of the signing
|
|
153
|
+
certificate's personal name. -->
|
|
154
|
+
<key>AssociatedBundleIdentifiers</key>
|
|
155
|
+
<array>
|
|
156
|
+
<string>${HELPERS_BUNDLE_ID}</string>
|
|
157
|
+
</array>
|
|
158
|
+
</dict>
|
|
159
|
+
</plist>
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
162
|
+
/** codesign facts about a binary (diagnostic output — may name mechanisms). */
|
|
163
|
+
export function deputySigningInfo(binaryPath) {
|
|
164
|
+
// spawnSync, not execFileSync: codesign prints its details on STDERR while
|
|
165
|
+
// exiting 0, and execFileSync only surfaces stderr on the failure path — the
|
|
166
|
+
// success case must read both streams.
|
|
167
|
+
const res = spawnSync("codesign", ["-dvv", binaryPath], { encoding: "utf8", timeout: 10_000 });
|
|
168
|
+
const out = `${res.stdout ?? ""}${res.stderr ?? ""}`;
|
|
169
|
+
if (res.error !== undefined || out === "")
|
|
170
|
+
return { state: "unknown", authority: null };
|
|
171
|
+
if (/not signed at all/i.test(out))
|
|
172
|
+
return { state: "unsigned", authority: null };
|
|
173
|
+
if (/Signature=adhoc/i.test(out))
|
|
174
|
+
return { state: "adhoc", authority: null };
|
|
175
|
+
const authority = /Authority=(.+)/.exec(out)?.[1]?.trim() ?? null;
|
|
176
|
+
return authority !== null
|
|
177
|
+
? { state: "signed", authority }
|
|
178
|
+
: { state: "unknown", authority: null };
|
|
179
|
+
}
|
|
180
|
+
/** One handshake against the reader's socket; null when it cannot complete. */
|
|
181
|
+
function readerHelloProbe(env) {
|
|
182
|
+
const socketPath = readerSocketPath(env);
|
|
183
|
+
const tokenPath = readerTokenPath(env);
|
|
184
|
+
if (!existsSync(socketPath) || !existsSync(tokenPath))
|
|
185
|
+
return null;
|
|
186
|
+
const token = readFileSync(tokenPath, "utf8").trim();
|
|
187
|
+
const bridge = new DeputySyncBridge(socketPath);
|
|
188
|
+
try {
|
|
189
|
+
const res = bridge.request({ v: DEPUTY_PROTOCOL_VERSION, token, verb: "hello" }, 2000);
|
|
190
|
+
return res["ok"] === true ? res : null;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
bridge.close();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Install (or reinstall) the helpers: copy the built bundle to its stable
|
|
201
|
+
* path, write both LaunchAgent plists, and (re)bootstrap them under launchd.
|
|
202
|
+
*/
|
|
203
|
+
export function installHelpers(options = {}, env = process.env) {
|
|
204
|
+
const source = options.bundlePath ?? helpersDefaultBuildPath();
|
|
205
|
+
if (source === null) {
|
|
206
|
+
throw new Error(`helpers bundle not found — looked for ${helpersBundleCandidates().join(" and ")}. ` +
|
|
207
|
+
`Build it first: bash scripts/build-helpers.sh`);
|
|
208
|
+
}
|
|
209
|
+
if (!existsSync(join(source, "Contents/MacOS/things-deputy"))) {
|
|
210
|
+
throw new Error(`helpers bundle not found at ${source} — build it first: bash scripts/build-helpers.sh`);
|
|
211
|
+
}
|
|
212
|
+
const readerInBuild = existsSync(join(source, "Contents/Helpers/things-reader.app"));
|
|
213
|
+
const installDir = helpersInstallDir(env);
|
|
214
|
+
const bundlePath = helpersInstalledBundlePath(env);
|
|
215
|
+
const stateDir = deputyStateDir(env);
|
|
216
|
+
// Stop both halves before replacing the bundle (ignore "not loaded"), then
|
|
217
|
+
// recreate the install dir from scratch: install owns bin/ WHOLESALE — a
|
|
218
|
+
// fresh copy every time erases any previous layout without migration logic
|
|
219
|
+
// and resets the kernel's per-vnode code-signature cache.
|
|
220
|
+
launchctl(["bootout", launchTarget()]);
|
|
221
|
+
launchctl(["bootout", readerLaunchTarget()]);
|
|
222
|
+
rmSync(installDir, { recursive: true, force: true });
|
|
223
|
+
mkdirSync(installDir, { recursive: true, mode: 0o700 });
|
|
224
|
+
cpSync(source, bundlePath, { recursive: true });
|
|
225
|
+
const plistPath = deputyPlistPath();
|
|
226
|
+
mkdirSync(dirname(plistPath), { recursive: true });
|
|
227
|
+
writeFileSync(plistPath, renderPlist(deputyInstalledBinaryPath(env), stateDir));
|
|
228
|
+
const warnings = [];
|
|
229
|
+
const boot = launchctl(["bootstrap", `gui/${process.getuid?.() ?? 501}`, plistPath]);
|
|
230
|
+
if (!boot.ok) {
|
|
231
|
+
warnings.push(`launchctl bootstrap failed: ${boot.output.trim()}`);
|
|
232
|
+
}
|
|
233
|
+
const signing = deputySigningInfo(deputyInstalledBinaryPath(env));
|
|
234
|
+
if (signing.state !== "signed") {
|
|
235
|
+
warnings.push(`bundle is ${signing.state} — macOS permission grants will NOT survive rebuilds. ` +
|
|
236
|
+
`Mint the persistent certificate once (scripts/deputy-cert-setup.sh), rebuild, reinstall.`);
|
|
237
|
+
}
|
|
238
|
+
let readerGranted = null;
|
|
239
|
+
if (readerInBuild) {
|
|
240
|
+
const readerPlist = readerPlistPath();
|
|
241
|
+
writeFileSync(readerPlist, renderReaderPlist(readerInstalledAppPath(env)));
|
|
242
|
+
const readerBoot = launchctl(["bootstrap", `gui/${process.getuid?.() ?? 501}`, readerPlist]);
|
|
243
|
+
if (!readerBoot.ok) {
|
|
244
|
+
warnings.push(`reader launchctl bootstrap failed: ${readerBoot.output.trim()}`);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// Report the ACTUAL grant state instead of a "run it if you have not"
|
|
248
|
+
// hedge — the reader knows, so ask it (bounded: it just booted).
|
|
249
|
+
const deadline = Date.now() + 4000;
|
|
250
|
+
while (Date.now() < deadline) {
|
|
251
|
+
const hello = readerHelloProbe(env);
|
|
252
|
+
if (hello !== null) {
|
|
253
|
+
readerGranted = hello.granted === true;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
syncSleepMs(250);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
warnings.push("the bundle was built without things-reader (no Apple-issued signing identity?) — file reads run direct. Build with an Apple-chain identity, reinstall, then run `things helpers grant`.");
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
bundlePath,
|
|
265
|
+
plistPath,
|
|
266
|
+
stateDir,
|
|
267
|
+
signing,
|
|
268
|
+
readerInstalled: readerInBuild,
|
|
269
|
+
readerGranted,
|
|
270
|
+
warnings,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/** Stop both halves and remove LaunchAgents + the installed bundle (state — tokens, logs, the reader's grant — is kept). */
|
|
274
|
+
export function uninstallHelpers(env = process.env) {
|
|
275
|
+
const removed = [];
|
|
276
|
+
launchctl(["bootout", launchTarget()]);
|
|
277
|
+
launchctl(["bootout", readerLaunchTarget()]);
|
|
278
|
+
for (const path of [deputyPlistPath(), readerPlistPath()]) {
|
|
279
|
+
if (existsSync(path)) {
|
|
280
|
+
rmSync(path);
|
|
281
|
+
removed.push(path);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const installDir = helpersInstallDir(env);
|
|
285
|
+
if (existsSync(installDir)) {
|
|
286
|
+
rmSync(installDir, { recursive: true });
|
|
287
|
+
removed.push(installDir);
|
|
288
|
+
}
|
|
289
|
+
return { removed };
|
|
290
|
+
}
|
|
291
|
+
/** Restart the launchd-managed helpers (picks up a rebuilt installed bundle). */
|
|
292
|
+
export function restartHelpers() {
|
|
293
|
+
const res = launchctl(["kickstart", "-k", launchTarget()]);
|
|
294
|
+
if (!res.ok) {
|
|
295
|
+
throw new Error(`launchctl kickstart failed (${res.output.trim() || "unknown"}) — are the helpers installed? Run: things helpers install`);
|
|
296
|
+
}
|
|
297
|
+
// Reader restart is best-effort: it may legitimately not be installed.
|
|
298
|
+
launchctl(["kickstart", "-k", readerLaunchTarget()]);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* The one-time grant ceremony: open the reader in `--grant` mode (the panel
|
|
302
|
+
* must be presented by the SANDBOXED process — that is what makes the grant
|
|
303
|
+
* durable) and wait for the bookmark to land, confirmed via the reader's
|
|
304
|
+
* handshake. The panel opens INSIDE the Things data folder when it exists, so
|
|
305
|
+
* accepting is the only click. Interactive by design; returns when granted or
|
|
306
|
+
* on timeout, and verifies the database actually resolves inside the granted
|
|
307
|
+
* scope so a wrong-folder grant reports loudly instead of half-working.
|
|
308
|
+
*/
|
|
309
|
+
export function grantReader(env = process.env) {
|
|
310
|
+
const appPath = readerInstalledAppPath(env);
|
|
311
|
+
if (!existsSync(appPath)) {
|
|
312
|
+
return {
|
|
313
|
+
granted: false,
|
|
314
|
+
detail: "things-reader is not installed — run `things helpers install` first",
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
const thingsContainer = join(homedir(), THINGS_GROUP_CONTAINER);
|
|
318
|
+
const startDir = existsSync(thingsContainer)
|
|
319
|
+
? thingsContainer
|
|
320
|
+
: join(homedir(), "Library/Group Containers");
|
|
321
|
+
try {
|
|
322
|
+
execFileSync("open", ["-W", appPath, "--args", "--grant", startDir], {
|
|
323
|
+
stdio: "ignore",
|
|
324
|
+
timeout: 300_000,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
return {
|
|
329
|
+
granted: false,
|
|
330
|
+
detail: `could not open the grant panel: ${err instanceof Error ? err.message : String(err)}`,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// The serving reader re-checks its bookmark per request — no restart needed.
|
|
334
|
+
const socketPath = readerSocketPath(env);
|
|
335
|
+
const tokenPath = readerTokenPath(env);
|
|
336
|
+
const deadline = Date.now() + 15_000;
|
|
337
|
+
let detail = "the reader is not running — `things helpers status`";
|
|
338
|
+
while (Date.now() < deadline) {
|
|
339
|
+
if (existsSync(socketPath) && existsSync(tokenPath)) {
|
|
340
|
+
const token = readFileSync(tokenPath, "utf8").trim();
|
|
341
|
+
const bridge = new DeputySyncBridge(socketPath);
|
|
342
|
+
try {
|
|
343
|
+
const res = bridge.request({ v: DEPUTY_PROTOCOL_VERSION, token, verb: "hello" }, 2000);
|
|
344
|
+
if (res["ok"] === true && res.granted === true) {
|
|
345
|
+
const locate = bridge.request({ v: DEPUTY_PROTOCOL_VERSION, token, verb: "locate" }, 5000);
|
|
346
|
+
bridge.close();
|
|
347
|
+
if (locate["ok"] === true)
|
|
348
|
+
return { granted: true, detail: "granted" };
|
|
349
|
+
return {
|
|
350
|
+
granted: false,
|
|
351
|
+
detail: "the grant landed, but the Things database was not found inside the granted folder — rerun `things helpers grant` and grant the Things data folder",
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
detail = "the panel closed but no grant landed (canceled?) — rerun `things helpers grant`";
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
detail = "the reader socket is not answering — `things helpers status`";
|
|
358
|
+
}
|
|
359
|
+
finally {
|
|
360
|
+
bridge.close();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
syncSleepMs(500);
|
|
364
|
+
}
|
|
365
|
+
return { granted: false, detail };
|
|
366
|
+
}
|
|
367
|
+
function syncSleepMs(ms) {
|
|
368
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Prompt-free status for both halves: launchd load state, a live handshake
|
|
372
|
+
* when each socket answers, and the installed bundle's signing facts. Works
|
|
373
|
+
* with routing disabled — inspect first, enable after.
|
|
374
|
+
*/
|
|
375
|
+
export function helpersStatus(mode, env = process.env) {
|
|
376
|
+
const binaryPath = deputyInstalledBinaryPath(env);
|
|
377
|
+
const binaryInstalled = existsSync(binaryPath);
|
|
378
|
+
const socketPath = deputySocketPath(env);
|
|
379
|
+
let hello = null;
|
|
380
|
+
let detail = "";
|
|
381
|
+
const socketPresent = existsSync(socketPath) && existsSync(deputyTokenPath(env));
|
|
382
|
+
if (socketPresent) {
|
|
383
|
+
const token = readFileSync(deputyTokenPath(env), "utf8").trim();
|
|
384
|
+
const bridge = new DeputySyncBridge(socketPath);
|
|
385
|
+
try {
|
|
386
|
+
const res = bridge.request({ v: DEPUTY_PROTOCOL_VERSION, token, verb: "hello" }, 2000);
|
|
387
|
+
if (res["ok"] === true) {
|
|
388
|
+
hello = res;
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
detail = `handshake refused: ${JSON.stringify(res["error"])}`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
detail = `socket present but not answering: ${err instanceof Error ? err.message : String(err)}`;
|
|
396
|
+
}
|
|
397
|
+
finally {
|
|
398
|
+
bridge.close();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
detail = "not running (no socket)";
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
mode,
|
|
406
|
+
bundleInstalled: binaryInstalled,
|
|
407
|
+
installedVersion: installedHelpersVersion(env),
|
|
408
|
+
deputy: {
|
|
409
|
+
plistInstalled: existsSync(deputyPlistPath()),
|
|
410
|
+
loaded: launchctl(["print", launchTarget()]).ok,
|
|
411
|
+
running: hello !== null,
|
|
412
|
+
socketPath,
|
|
413
|
+
hungSocket: socketPresent && hello === null,
|
|
414
|
+
hello,
|
|
415
|
+
signing: binaryInstalled ? deputySigningInfo(binaryPath) : null,
|
|
416
|
+
detail: hello !== null ? "running" : detail,
|
|
417
|
+
},
|
|
418
|
+
reader: readerStatus(env),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function readerStatus(env) {
|
|
422
|
+
const appPath = readerInstalledAppPath(env);
|
|
423
|
+
const installed = existsSync(appPath);
|
|
424
|
+
const loaded = launchctl(["print", readerLaunchTarget()]).ok;
|
|
425
|
+
const socketPath = readerSocketPath(env);
|
|
426
|
+
const tokenPath = readerTokenPath(env);
|
|
427
|
+
let running = false;
|
|
428
|
+
let granted = false;
|
|
429
|
+
let hello = null;
|
|
430
|
+
let detail = "not running (no socket)";
|
|
431
|
+
const socketPresent = existsSync(socketPath) && existsSync(tokenPath);
|
|
432
|
+
if (socketPresent) {
|
|
433
|
+
const token = readFileSync(tokenPath, "utf8").trim();
|
|
434
|
+
const bridge = new DeputySyncBridge(socketPath);
|
|
435
|
+
try {
|
|
436
|
+
const res = bridge.request({ v: DEPUTY_PROTOCOL_VERSION, token, verb: "hello" }, 2000);
|
|
437
|
+
if (res["ok"] === true) {
|
|
438
|
+
running = true;
|
|
439
|
+
hello = res;
|
|
440
|
+
granted = res.granted === true;
|
|
441
|
+
detail = granted ? "running, granted" : "running, NOT granted (things helpers grant)";
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
detail = `handshake refused: ${JSON.stringify(res["error"])}`;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
catch (err) {
|
|
448
|
+
detail = `socket present but not answering: ${err instanceof Error ? err.message : String(err)}`;
|
|
449
|
+
}
|
|
450
|
+
finally {
|
|
451
|
+
bridge.close();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return {
|
|
455
|
+
installed,
|
|
456
|
+
plistInstalled: existsSync(readerPlistPath()),
|
|
457
|
+
loaded,
|
|
458
|
+
running,
|
|
459
|
+
granted,
|
|
460
|
+
socketPath,
|
|
461
|
+
hungSocket: socketPresent && !running,
|
|
462
|
+
hello,
|
|
463
|
+
signing: installed ? deputySigningInfo(readerExecPath(appPath)) : null,
|
|
464
|
+
detail,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/deputy/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Emit one helpers notice on stderr; later calls this process are dropped. */
|
|
2
|
+
export declare function emitHelpersNotice(message: string, write?: (s: string) => void): void;
|
|
3
|
+
/** True once this process has spent its one notice. */
|
|
4
|
+
export declare function helpersNoticeSpent(): boolean;
|
|
5
|
+
/** Test seam: forget that a notice was emitted. */
|
|
6
|
+
export declare function resetHelpersNoticeForTests(): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single helpers stderr channel: AT MOST ONE line per process, whichever
|
|
3
|
+
* condition speaks first.
|
|
4
|
+
*
|
|
5
|
+
* Two producers share it — the routing layer (degradation caught at
|
|
6
|
+
* activation, src/deputy/routing.ts) and the CLI's passive install/upgrade
|
|
7
|
+
* check (src/cli/helpers-check.ts). One process can hit both (an installed but
|
|
8
|
+
* stale helper that also fails to answer), and two stacked lines about the same
|
|
9
|
+
* broken helper are noise, not information: the first one already names a
|
|
10
|
+
* remedy that resolves the state.
|
|
11
|
+
*/
|
|
12
|
+
let noticed = false;
|
|
13
|
+
/** Emit one helpers notice on stderr; later calls this process are dropped. */
|
|
14
|
+
export function emitHelpersNotice(message, write = (s) => void process.stderr.write(s)) {
|
|
15
|
+
if (noticed)
|
|
16
|
+
return;
|
|
17
|
+
noticed = true;
|
|
18
|
+
write(`things-api helpers: ${message}\n`);
|
|
19
|
+
}
|
|
20
|
+
/** True once this process has spent its one notice. */
|
|
21
|
+
export function helpersNoticeSpent() {
|
|
22
|
+
return noticed;
|
|
23
|
+
}
|
|
24
|
+
/** Test seam: forget that a notice was emitted. */
|
|
25
|
+
export function resetHelpersNoticeForTests() {
|
|
26
|
+
noticed = false;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=notice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notice.js","sourceRoot":"","sources":["../../src/deputy/notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,IAAI,OAAO,GAAG,KAAK,CAAC;AAEpB,+EAA+E;AAC/E,MAAM,UAAU,iBAAiB,CAC/B,OAAe,EACf,QAA6B,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAEhE,IAAI,OAAO;QAAE,OAAO;IACpB,OAAO,GAAG,IAAI,CAAC;IACf,KAAK,CAAC,uBAAuB,OAAO,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,kBAAkB;IAChC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,0BAA0B;IACxC,OAAO,GAAG,KAAK,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** How long the absence hint stays quiet after being shown once. */
|
|
2
|
+
export declare const HELPERS_HINT_INTERVAL_DAYS = 14;
|
|
3
|
+
export type HelpersNoticeKind = "version-skew" | "absent-hint";
|
|
4
|
+
export interface HelpersNotice {
|
|
5
|
+
kind: HelpersNoticeKind;
|
|
6
|
+
/** The one line to print (no prefix — the notice sink adds it). */
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
export interface HelpersNoticeOptions {
|
|
10
|
+
env?: NodeJS.ProcessEnv;
|
|
11
|
+
/** Wall clock (ms) for the hint throttle; defaults to now. */
|
|
12
|
+
now?: number;
|
|
13
|
+
/** Host platform; the helpers are macOS-only, so nothing fires elsewhere. */
|
|
14
|
+
platform?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Test seam. Defaults to the real provenance: true when `helpers-enabled` is
|
|
17
|
+
* neither stored nor forced by the environment (nobody has ever answered the
|
|
18
|
+
* routing question on this host).
|
|
19
|
+
*/
|
|
20
|
+
modeUntouched?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** When the absence hint was last shown (ms), or null when never/unreadable. */
|
|
23
|
+
export declare function lastHelpersHintAt(env?: NodeJS.ProcessEnv): number | null;
|
|
24
|
+
/** Record that the absence hint was shown, restarting its throttle window. */
|
|
25
|
+
export declare function markHelpersHintShown(env?: NodeJS.ProcessEnv, now?: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* The notice this machine has earned, or null when it has nothing to say.
|
|
28
|
+
* Skew outranks the hint (they are mutually exclusive anyway: one needs an
|
|
29
|
+
* installed bundle, the other its absence).
|
|
30
|
+
*/
|
|
31
|
+
export declare function computeHelpersNotice(options?: HelpersNoticeOptions): HelpersNotice | null;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PASSIVE helpers notices — what a human sees on a command they were
|
|
3
|
+
* running anyway, without asking about the helpers at all (the skill-drift
|
|
4
|
+
* precedent, src/cli/skill-check.ts).
|
|
5
|
+
*
|
|
6
|
+
* Two conditions, both computed from disk with no handshake and no prompt:
|
|
7
|
+
*
|
|
8
|
+
* - `version-skew`: routing is on (`auto`/`true`), a bundle IS installed, and
|
|
9
|
+
* its version differs from the one this package expects. The remedy is a
|
|
10
|
+
* rebuild + reinstall — the installed bundle is a COPY, so restarting it
|
|
11
|
+
* would only restart the stale code.
|
|
12
|
+
* - `absent-hint`: nothing is installed, the routing mode has never been set,
|
|
13
|
+
* and the hint has not been shown for {@link HELPERS_HINT_INTERVAL_DAYS}
|
|
14
|
+
* days. This one is a suggestion, not a fault, so it is throttled by a stamp
|
|
15
|
+
* in the state dir and suppressible like the rest.
|
|
16
|
+
*
|
|
17
|
+
* Emission (gating, kill switch, the one-line-per-process budget) belongs to
|
|
18
|
+
* the caller — src/cli/helpers-check.ts. These functions only decide.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { getConfigKey, loadConfig } from "../config.js";
|
|
23
|
+
import { stateDir } from "../paths.js";
|
|
24
|
+
import { installedHelpersVersion } from "./install.js";
|
|
25
|
+
import { EXPECTED_HELPERS_VERSION, helpersInstalledBundlePath } from "./protocol.js";
|
|
26
|
+
/** How long the absence hint stays quiet after being shown once. */
|
|
27
|
+
export const HELPERS_HINT_INTERVAL_DAYS = 14;
|
|
28
|
+
function hintStampPath(env) {
|
|
29
|
+
return join(stateDir(env), "helpers-hint.json");
|
|
30
|
+
}
|
|
31
|
+
/** When the absence hint was last shown (ms), or null when never/unreadable. */
|
|
32
|
+
export function lastHelpersHintAt(env = process.env) {
|
|
33
|
+
try {
|
|
34
|
+
const raw = JSON.parse(readFileSync(hintStampPath(env), "utf8"));
|
|
35
|
+
const parsed = typeof raw.shownAt === "string" ? Date.parse(raw.shownAt) : NaN;
|
|
36
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Record that the absence hint was shown, restarting its throttle window. */
|
|
43
|
+
export function markHelpersHintShown(env = process.env, now = Date.now()) {
|
|
44
|
+
try {
|
|
45
|
+
mkdirSync(stateDir(env), { recursive: true });
|
|
46
|
+
writeFileSync(hintStampPath(env), `${JSON.stringify({ shownAt: new Date(now).toISOString() }, null, 2)}\n`);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// A hint that cannot remember itself is still better than a broken command.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The notice this machine has earned, or null when it has nothing to say.
|
|
54
|
+
* Skew outranks the hint (they are mutually exclusive anyway: one needs an
|
|
55
|
+
* installed bundle, the other its absence).
|
|
56
|
+
*/
|
|
57
|
+
export function computeHelpersNotice(options = {}) {
|
|
58
|
+
const env = options.env ?? process.env;
|
|
59
|
+
const platform = options.platform ?? process.platform;
|
|
60
|
+
if (platform !== "darwin")
|
|
61
|
+
return null;
|
|
62
|
+
const mode = loadConfig(env).helpersMode;
|
|
63
|
+
if (mode === "false")
|
|
64
|
+
return null;
|
|
65
|
+
const installed = existsSync(helpersInstalledBundlePath(env));
|
|
66
|
+
if (installed) {
|
|
67
|
+
const version = installedHelpersVersion(env);
|
|
68
|
+
if (version === null || version === EXPECTED_HELPERS_VERSION)
|
|
69
|
+
return null;
|
|
70
|
+
return {
|
|
71
|
+
kind: "version-skew",
|
|
72
|
+
text: `helpers v${version} installed, this package expects v${EXPECTED_HELPERS_VERSION} — ` +
|
|
73
|
+
"rebuild with `bash scripts/build-helpers.sh` and rerun `things helpers install` " +
|
|
74
|
+
"(THINGS_API_NO_HELPERS_CHECK=1 silences this)",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Nothing installed: a suggestion, and only for a host that has never
|
|
78
|
+
// expressed an opinion — an explicit `auto`/`true` owner is mid-setup, not
|
|
79
|
+
// in need of an introduction.
|
|
80
|
+
const untouched = options.modeUntouched ?? getConfigKey("helpers-enabled", env)?.source === "default";
|
|
81
|
+
if (!untouched)
|
|
82
|
+
return null;
|
|
83
|
+
const last = lastHelpersHintAt(env);
|
|
84
|
+
const now = options.now ?? Date.now();
|
|
85
|
+
if (last !== null && now - last < HELPERS_HINT_INTERVAL_DAYS * 86_400_000)
|
|
86
|
+
return null;
|
|
87
|
+
return {
|
|
88
|
+
kind: "absent-hint",
|
|
89
|
+
text: "database reads and app automation run in THIS process, so macOS permission grants " +
|
|
90
|
+
"follow whichever terminal or agent runtime started it — `things helpers install` moves " +
|
|
91
|
+
"them onto two stable helpers (THINGS_API_NO_HELPERS_CHECK=1 silences this)",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=notices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notices.js","sourceRoot":"","sources":["../../src/deputy/notices.ts"],"names":[],"mappings":"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"}
|