@raidiant/notifai 1.0.0 → 1.1.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/CHANGELOG.md +26 -1
- package/README.md +17 -7
- package/dist/commands.d.ts +2 -0
- package/dist/commands.js +43 -38
- package/dist/commands.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.js +5 -9
- package/dist/config.js.map +1 -1
- package/dist/credentials.d.ts +54 -3
- package/dist/credentials.js +237 -18
- package/dist/credentials.js.map +1 -1
- package/dist/file-lock.js +15 -6
- package/dist/file-lock.js.map +1 -1
- package/dist/hook-adapter.d.ts +6 -2
- package/dist/hook-adapter.js +110 -29
- package/dist/hook-adapter.js.map +1 -1
- package/dist/hooks.js +2 -1
- package/dist/hooks.js.map +1 -1
- package/dist/install-hooks.d.ts +54 -22
- package/dist/install-hooks.js +125 -56
- package/dist/install-hooks.js.map +1 -1
- package/dist/native-skills.js +5 -10
- package/dist/native-skills.js.map +1 -1
- package/dist/opencode-plugin.d.ts +7 -2
- package/dist/opencode-plugin.js +142 -4
- package/dist/opencode-plugin.js.map +1 -1
- package/dist/platform.d.ts +73 -0
- package/dist/platform.js +143 -0
- package/dist/platform.js.map +1 -0
- package/dist/ui/config-view.js +5 -1
- package/dist/ui/config-view.js.map +1 -1
- package/package.json +3 -3
- package/src/commands.test.ts +144 -34
- package/src/commands.ts +65 -35
- package/src/config.ts +11 -9
- package/src/credentials.test.ts +291 -0
- package/src/credentials.ts +274 -17
- package/src/file-lock.ts +34 -9
- package/src/hook-adapter.test.ts +136 -0
- package/src/hook-adapter.ts +120 -28
- package/src/hooks.test.ts +27 -1
- package/src/hooks.ts +2 -1
- package/src/install-hooks.test.ts +188 -27
- package/src/install-hooks.ts +177 -52
- package/src/native-skills.ts +5 -10
- package/src/opencode-plugin.ts +148 -3
- package/src/platform.test.ts +152 -0
- package/src/platform.ts +173 -0
- package/src/ui/config-view.ts +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -8,8 +8,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
9
9
|
and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
10
10
|
|
|
11
|
+
## [1.1.0](https://github.com/Raidiant-io/notifai/compare/v1.0.1...v1.1.0) (2026-08-13)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* **cli:** support Windows and Linux harness workflows ([779ab6b](https://github.com/Raidiant-io/notifai/commit/779ab6b075900a7a431b809ddcb715fd80836688))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **cli:** cover Windows terminal behavior ([cda7abd](https://github.com/Raidiant-io/notifai/commit/cda7abd134744b2cac44204dd3eb1b20b9038edc))
|
|
22
|
+
* **cli:** isolate Codex hooks and reply routing ([#1](https://github.com/Raidiant-io/notifai/issues/1)) ([d060b6a](https://github.com/Raidiant-io/notifai/commit/d060b6a18b5937d82f74619a6c1bc711243be26c))
|
|
23
|
+
* **cli:** keep Codex hook install on ~/.codex, ignore CODEX_HOME ([3f75f13](https://github.com/Raidiant-io/notifai/commit/3f75f131c24534d3813a336bb72422f78e24a972))
|
|
24
|
+
* **cli:** preserve Codex hook trust on reinstall ([#3](https://github.com/Raidiant-io/notifai/issues/3)) ([5e2c2f4](https://github.com/Raidiant-io/notifai/commit/5e2c2f4d618276573f810a989f41a89ab1053564))
|
|
25
|
+
* **cli:** report observed setup receipts ([99d04bb](https://github.com/Raidiant-io/notifai/commit/99d04bbd487681db3023468fbd88b77a1ba13ceb))
|
|
26
|
+
* **cli:** stabilize Linux portability checks ([1462693](https://github.com/Raidiant-io/notifai/commit/146269306ae3dfc91f3559cce802460d3e058925))
|
|
27
|
+
|
|
11
28
|
## [Unreleased]
|
|
12
29
|
|
|
30
|
+
## [1.0.1] - 2026-08-13
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Codex hook installation writes inline `[hooks]` in `config.toml`. It does not create the legacy `hooks.json`.
|
|
35
|
+
- Hook installation is one scope: project or machine-global, not both. A project install is a no-op when global already covers the machine.
|
|
36
|
+
|
|
13
37
|
## [1.0.0] - 2026-08-13
|
|
14
38
|
|
|
15
39
|
### Removed
|
|
@@ -146,7 +170,8 @@ and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
|
146
170
|
|
|
147
171
|
- First public release of the `notifai` CLI under Apache-2.0.
|
|
148
172
|
|
|
149
|
-
[unreleased]: https://github.com/Raidiant-io/notifai/compare/v1.0.
|
|
173
|
+
[unreleased]: https://github.com/Raidiant-io/notifai/compare/v1.0.1...HEAD
|
|
174
|
+
[1.0.1]: https://github.com/Raidiant-io/notifai/compare/v1.0.0...v1.0.1
|
|
150
175
|
[1.0.0]: https://github.com/Raidiant-io/notifai/compare/v0.5.1...v1.0.0
|
|
151
176
|
[0.5.1]: https://github.com/Raidiant-io/notifai/compare/v0.5.0...v0.5.1
|
|
152
177
|
[0.5.0]: https://github.com/Raidiant-io/notifai/compare/v0.4.0...v0.5.0
|
package/README.md
CHANGED
|
@@ -56,6 +56,10 @@ For a harness that cannot resume an idle agent turn, `notifai send
|
|
|
56
56
|
|
|
57
57
|
## Agent harnesses
|
|
58
58
|
|
|
59
|
+
The CLI and managed hook adapters support macOS, Linux, and Windows. Receiving
|
|
60
|
+
still requires the current iPhone or macOS Companion App; native Windows and
|
|
61
|
+
Linux Companion Apps are not shipped yet.
|
|
62
|
+
|
|
59
63
|
```sh
|
|
60
64
|
notifai hooks install
|
|
61
65
|
```
|
|
@@ -67,13 +71,19 @@ or add one it did not see. This is what lets an agent's question reach
|
|
|
67
71
|
your phone without the agent having to cooperate — no question detection,
|
|
68
72
|
no state in a context window that compaction will eat.
|
|
69
73
|
|
|
70
|
-
Every harness definition calls one stable user-level adapter
|
|
71
|
-
`~/.notifai/bin/hook-adapter
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
Every harness definition calls one stable user-level adapter under the account
|
|
75
|
+
home (`~/.notifai/bin/hook-adapter` on macOS/Linux and the corresponding user
|
|
76
|
+
profile path on Windows). Node, package manager, CLI version, checkout,
|
|
77
|
+
configuration directories, and notification preferences are resolved behind
|
|
78
|
+
it, so upgrades and configuration changes do not rewrite trusted hook
|
|
79
|
+
definitions. Windows invokes its JavaScript adapter through the registered Node
|
|
80
|
+
executable; macOS/Linux retain the POSIX adapter and its stable command bytes.
|
|
81
|
+
Claude Code Stop returns immediately into the live inbox route on macOS/Linux;
|
|
82
|
+
on Windows, where upstream exposes no inbox socket, Stop stays open and returns
|
|
83
|
+
the accepted answer through the harness continuation. The first migration may
|
|
84
|
+
require one Codex `/hooks` approval; later repairs keep the same identity.
|
|
85
|
+
Codex's Stop definition omits its timeout so the host default remains
|
|
86
|
+
authoritative; prompt-submit and session-end retain fixed short limits.
|
|
77
87
|
|
|
78
88
|
`notifai doctor` compares Codex trust on a best-effort basis against Codex's
|
|
79
89
|
current persisted representation. Notifai never writes that trust store;
|
package/dist/commands.d.ts
CHANGED
|
@@ -61,6 +61,8 @@ export interface CommandDeps {
|
|
|
61
61
|
hookAdapterHome?: string;
|
|
62
62
|
/** Test seam; production uses this process's Node and CLI paths. */
|
|
63
63
|
hookInstallTarget?: HookAdapterTarget;
|
|
64
|
+
/** Test seam; production uses process.platform for adapter format and quoting. */
|
|
65
|
+
hookPlatform?: NodeJS.Platform;
|
|
64
66
|
/** Test seam; production uses fetch against base_url. */
|
|
65
67
|
clientFactory?: (baseUrl: string, bearer: string | null, options?: ClientOptions) => ApiClient;
|
|
66
68
|
/** Test seam for bounded polling without wall-clock sleeps. */
|
package/dist/commands.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
2
|
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
4
3
|
import os from 'node:os';
|
|
5
4
|
import path from 'node:path';
|
|
@@ -7,7 +6,7 @@ import { parse as parseToml, stringify as stringifyToml } from 'smol-toml';
|
|
|
7
6
|
import { CAPABILITIES_V1, QUESTION_TEXT_MAX_LENGTH, REPLY_MAX_QUESTIONS, REPLY_MAX_WINDOW_SECONDS, validateDraft, } from '@raidiant/notifai-protocol';
|
|
8
7
|
import { sha256Hex } from '@raidiant/notifai-protocol/node';
|
|
9
8
|
import { ApiCallError, NetworkError, createClient, isRetryableReplyPollError, } from './client.js';
|
|
10
|
-
import { BOOLEAN_CONFIG_KEYS, CONFIG_KEYS, LOG_LEVELS, NUMERIC_CONFIG_KEYS, configBounds, configDefaultValue, findProjectConfigPath, personalProjectConfigPath, globalConfigPath, loadConfig, sessionConfigPath, } from './config.js';
|
|
9
|
+
import { BOOLEAN_CONFIG_KEYS, CONFIG_KEYS, LOG_LEVELS, NUMERIC_CONFIG_KEYS, configBounds, configDefaultValue, findProjectConfigPath, personalProjectConfigPath, globalConfigPath, loadConfig, sessionConfigPath, stateDir, } from './config.js';
|
|
11
10
|
import { acceptedValues, configInfo } from './config-schema.js';
|
|
12
11
|
import { packageVersion, skillsSource } from './release.js';
|
|
13
12
|
import { atomicWriteFileSync } from './atomic-file.js';
|
|
@@ -23,6 +22,7 @@ import { codexWakeRoute, inspectCodexResume, } from './codex-wake.js';
|
|
|
23
22
|
import { inspectHookAdapter, installHookAdapter, isNpxAdapterTarget, } from './hook-adapter.js';
|
|
24
23
|
import { isOurOpencodePlugin, opencodePluginSource, } from './opencode-plugin.js';
|
|
25
24
|
import { CHOICE_USAGE, buildDraft, formatReceipt, parseChoices, receiptExitCode, slugify, } from './send.js';
|
|
25
|
+
import { openUrl } from './platform.js';
|
|
26
26
|
/** The invocation's logger, or one that discards everything. */
|
|
27
27
|
export function log(deps) {
|
|
28
28
|
return deps.logger ?? nullLogger();
|
|
@@ -1128,6 +1128,8 @@ function stopWakeRoute(deps, harness, sessionId, cwd) {
|
|
|
1128
1128
|
if (sessionId === undefined)
|
|
1129
1129
|
return undefined;
|
|
1130
1130
|
if (harness === 'claude-code') {
|
|
1131
|
+
if ((deps.hookPlatform ?? process.platform) === 'win32')
|
|
1132
|
+
return undefined;
|
|
1131
1133
|
return claudeWakeRoute({
|
|
1132
1134
|
sessionId,
|
|
1133
1135
|
cwd,
|
|
@@ -1316,7 +1318,7 @@ export function askCommand(deps, question, flags) {
|
|
|
1316
1318
|
const { active, contested } = resolveActiveHarness(deps.env, deps.cwd, now);
|
|
1317
1319
|
let sessionId;
|
|
1318
1320
|
if (active !== null) {
|
|
1319
|
-
const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome);
|
|
1321
|
+
const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform);
|
|
1320
1322
|
const activeInstalled = installations.some((installation) => installation.harness === active.harness);
|
|
1321
1323
|
if (!activeInstalled) {
|
|
1322
1324
|
for (const line of diagnoseActiveHarnessSession(active, 'not-installed', installations, contested)) {
|
|
@@ -1518,11 +1520,11 @@ function activeQuestionRouteProblems(deps, active, installations) {
|
|
|
1518
1520
|
}
|
|
1519
1521
|
}
|
|
1520
1522
|
}
|
|
1521
|
-
for (const problem of inspectHookAdapter(deps.hookAdapterHome).problems) {
|
|
1523
|
+
for (const problem of inspectHookAdapter(deps.hookAdapterHome, deps.hookPlatform).problems) {
|
|
1522
1524
|
problems.push(problem);
|
|
1523
1525
|
}
|
|
1524
1526
|
for (const installation of matching)
|
|
1525
|
-
problems.push(...stopShapeProblems(installation));
|
|
1527
|
+
problems.push(...stopShapeProblems(installation, deps.hookPlatform));
|
|
1526
1528
|
problems.push(...codexTrustProblems(matching, deps.env));
|
|
1527
1529
|
return [...new Set(problems)];
|
|
1528
1530
|
}
|
|
@@ -1574,7 +1576,7 @@ function diagnoseActiveHarnessSession(active, problem, installations, contested
|
|
|
1574
1576
|
* checked after a prompt before assuming that a new session is required.
|
|
1575
1577
|
*/
|
|
1576
1578
|
function diagnoseMissingSession(deps) {
|
|
1577
|
-
const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome);
|
|
1579
|
+
const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform);
|
|
1578
1580
|
if (installations.length === 0) {
|
|
1579
1581
|
return [
|
|
1580
1582
|
'Could not tell which harness session this is: no Notifai hooks are installed for this project.',
|
|
@@ -1687,21 +1689,38 @@ export function hooksInstallCommand(deps, flags) {
|
|
|
1687
1689
|
return EXIT.usage;
|
|
1688
1690
|
const adapterTarget = resolveHookAdapterTarget(deps, flags);
|
|
1689
1691
|
const scriptPath = flags.scriptPath ?? fileHookInstallTarget(adapterTarget)?.scriptPath ?? process.argv[1] ?? 'notifai';
|
|
1692
|
+
const hookPlatform = deps.hookPlatform ?? process.platform;
|
|
1693
|
+
const nodePath = adapterTarget.execPath;
|
|
1690
1694
|
let adapterPath;
|
|
1691
1695
|
try {
|
|
1692
|
-
adapterPath = installHookAdapter(adapterTarget, deps.hookAdapterHome).path;
|
|
1696
|
+
adapterPath = installHookAdapter(adapterTarget, deps.hookAdapterHome, hookPlatform).path;
|
|
1693
1697
|
}
|
|
1694
1698
|
catch (err) {
|
|
1695
1699
|
deps.io.err(`Could not prepare the stable hook adapter: ${String(err)}`);
|
|
1696
1700
|
return EXIT.failed;
|
|
1697
1701
|
}
|
|
1698
|
-
const
|
|
1702
|
+
const wantGlobal = flags.global === true;
|
|
1703
|
+
const existing = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform).filter((installation) => installation.harness === harness);
|
|
1704
|
+
const otherScope = existing.filter((installation) => installation.global !== wantGlobal);
|
|
1705
|
+
if (!wantGlobal && otherScope.some((installation) => installation.global)) {
|
|
1706
|
+
const globalFile = otherScope.find((installation) => installation.global)?.file;
|
|
1707
|
+
deps.io.out(`${HARNESS_LABELS[harness]} hooks already cover this machine (${globalFile}). This project does not need its own copy. To wire only this project: notifai hooks uninstall --harness ${harness} --global && notifai hooks install --harness ${harness}`);
|
|
1708
|
+
return EXIT.ok;
|
|
1709
|
+
}
|
|
1710
|
+
if (wantGlobal && otherScope.some((installation) => !installation.global)) {
|
|
1711
|
+
if (hooksUninstallCommand(deps, { ...flags, global: false, harness }) !== EXIT.ok) {
|
|
1712
|
+
return EXIT.failed;
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
const file = settingsFile(harness, wantGlobal, deps.cwd, deps.env, hookPlatform);
|
|
1699
1716
|
// OpenCode's adapter is a generated plugin module rather than a handler
|
|
1700
1717
|
// merged into a settings document, so it owns the whole file.
|
|
1701
1718
|
if (harness === 'opencode') {
|
|
1702
1719
|
return installOpencodePlugin(deps, file, {
|
|
1703
1720
|
adapterPath,
|
|
1704
1721
|
timeoutSeconds: BLOCKING_STOP_TIMEOUT_SECONDS,
|
|
1722
|
+
platform: hookPlatform,
|
|
1723
|
+
nodePath,
|
|
1705
1724
|
});
|
|
1706
1725
|
}
|
|
1707
1726
|
if (harness === 'cursor') {
|
|
@@ -1711,6 +1730,8 @@ export function hooksInstallCommand(deps, flags) {
|
|
|
1711
1730
|
const result = mergeCursorHooks(document, buildCursorHookConfig({
|
|
1712
1731
|
adapterPath,
|
|
1713
1732
|
harness: 'cursor',
|
|
1733
|
+
platform: hookPlatform,
|
|
1734
|
+
nodePath,
|
|
1714
1735
|
}), scriptPath);
|
|
1715
1736
|
applyPlan(file, result.document);
|
|
1716
1737
|
return result;
|
|
@@ -1732,6 +1753,8 @@ export function hooksInstallCommand(deps, flags) {
|
|
|
1732
1753
|
const result = mergeHooks(document, buildHookConfig({
|
|
1733
1754
|
adapterPath,
|
|
1734
1755
|
harness,
|
|
1756
|
+
platform: hookPlatform,
|
|
1757
|
+
nodePath,
|
|
1735
1758
|
}), scriptPath);
|
|
1736
1759
|
applyPlan(file, result.document);
|
|
1737
1760
|
return result;
|
|
@@ -1805,7 +1828,7 @@ export function hooksUninstallCommand(deps, flags) {
|
|
|
1805
1828
|
if (!harness)
|
|
1806
1829
|
return EXIT.usage;
|
|
1807
1830
|
const scriptPath = flags.scriptPath ?? process.argv[1] ?? 'notifai';
|
|
1808
|
-
const file = settingsFile(harness, flags.global ?? false, deps.cwd, deps.env);
|
|
1831
|
+
const file = settingsFile(harness, flags.global ?? false, deps.cwd, deps.env, deps.hookPlatform);
|
|
1809
1832
|
if (harness === 'opencode') {
|
|
1810
1833
|
try {
|
|
1811
1834
|
return withTargetFileLock(file, () => {
|
|
@@ -2502,9 +2525,7 @@ function setupProofPath(deps) {
|
|
|
2502
2525
|
// the resolved absolute path is still a stable local identity for it.
|
|
2503
2526
|
}
|
|
2504
2527
|
const digest = createHash('sha256').update(projectDir).digest('hex').slice(0, 32);
|
|
2505
|
-
|
|
2506
|
-
const base = xdg && xdg !== '' ? xdg : path.join(os.homedir(), '.local', 'state');
|
|
2507
|
-
return path.join(base, 'notifai', 'setup-proofs', `${digest}.json`);
|
|
2528
|
+
return path.join(stateDir(deps.env), 'setup-proofs', `${digest}.json`);
|
|
2508
2529
|
}
|
|
2509
2530
|
function readSetupProof(deps) {
|
|
2510
2531
|
const file = setupProofPath(deps);
|
|
@@ -2803,6 +2824,7 @@ async function runSetupProof(deps) {
|
|
|
2803
2824
|
const observed = observedCompanionReceipt(snapshot, proof.device_id);
|
|
2804
2825
|
if (observed) {
|
|
2805
2826
|
spinner?.stop(`Receipt observed from ${observed.delivery.device_name}`);
|
|
2827
|
+
deps.io.out(`Companion Receipt (the app's delivery confirmation) observed from ${observed.delivery.device_name}.`);
|
|
2806
2828
|
return 'closed';
|
|
2807
2829
|
}
|
|
2808
2830
|
lastError = null;
|
|
@@ -3546,7 +3568,7 @@ function remedyLine(state) {
|
|
|
3546
3568
|
* is, is each check's own call (`HookCheck`).
|
|
3547
3569
|
*/
|
|
3548
3570
|
function hookStates(deps) {
|
|
3549
|
-
const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome);
|
|
3571
|
+
const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform);
|
|
3550
3572
|
const active = activeHarnessSession(deps.env, deps.cwd, (deps.now ?? Date.now)());
|
|
3551
3573
|
const config = loadLoggedConfig(deps, { cwd: deps.cwd, env: deps.env });
|
|
3552
3574
|
const settings = {
|
|
@@ -3634,12 +3656,12 @@ function hookStates(deps) {
|
|
|
3634
3656
|
* hashes into `trusted_hash`, and an untrusted handler is simply not run.
|
|
3635
3657
|
* - Everything else blocks its turn and needs a ceiling above the wait.
|
|
3636
3658
|
*/
|
|
3637
|
-
function stopShapeProblems(installation) {
|
|
3659
|
+
function stopShapeProblems(installation, platform = process.platform) {
|
|
3638
3660
|
if (installation.harness === 'codex')
|
|
3639
3661
|
return [];
|
|
3640
3662
|
const problems = [];
|
|
3641
3663
|
for (const handler of installation.handlers.filter((entry) => handlerEvent(entry.command) === 'stop')) {
|
|
3642
|
-
if (installation.harness === 'claude-code') {
|
|
3664
|
+
if (installation.harness === 'claude-code' && platform !== 'win32') {
|
|
3643
3665
|
if (handler.async !== true) {
|
|
3644
3666
|
problems.push(`${installation.file} declares a blocking Stop handler; the Claude Code wake route needs \`async: true\` so the turn ends while the waiter runs`);
|
|
3645
3667
|
}
|
|
@@ -3656,7 +3678,7 @@ function stopShapeProblems(installation) {
|
|
|
3656
3678
|
}
|
|
3657
3679
|
function hookChecks(deps) {
|
|
3658
3680
|
const checks = [];
|
|
3659
|
-
const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome);
|
|
3681
|
+
const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform);
|
|
3660
3682
|
// Not having hooks is a setup someone chose, not a fault: `send` works
|
|
3661
3683
|
// without them. A setup that cannot work is what deserves to go red.
|
|
3662
3684
|
if (installations.length === 0) {
|
|
@@ -3753,7 +3775,7 @@ function hookChecks(deps) {
|
|
|
3753
3775
|
: `${stale.join('; ')} — rerun \`notifai hooks install\` to drop ${stale.length === 1 ? 'it' : 'them'}`,
|
|
3754
3776
|
});
|
|
3755
3777
|
const adapterProblems = installations.flatMap((installation) => (installation.problems ?? []).map((problem) => `${installation.file}: ${problem}`));
|
|
3756
|
-
const sharedAdapter = inspectHookAdapter(deps.hookAdapterHome);
|
|
3778
|
+
const sharedAdapter = inspectHookAdapter(deps.hookAdapterHome, deps.hookPlatform);
|
|
3757
3779
|
adapterProblems.push(...sharedAdapter.problems);
|
|
3758
3780
|
if (adapterProblems.length > 0) {
|
|
3759
3781
|
checks.push({
|
|
@@ -3781,7 +3803,7 @@ function hookChecks(deps) {
|
|
|
3781
3803
|
},
|
|
3782
3804
|
}),
|
|
3783
3805
|
});
|
|
3784
|
-
const shapeProblems = installations.flatMap((installation) => stopShapeProblems(installation).map((problem) => `${problem} — run \`notifai hooks install --harness ${installation.harness}${installation.global ? ' --global' : ''}\``));
|
|
3806
|
+
const shapeProblems = installations.flatMap((installation) => stopShapeProblems(installation, deps.hookPlatform).map((problem) => `${problem} — run \`notifai hooks install --harness ${installation.harness}${installation.global ? ' --global' : ''}\``));
|
|
3785
3807
|
checks.push({
|
|
3786
3808
|
name: 'hooks (stop shape)',
|
|
3787
3809
|
ok: shapeProblems.length === 0,
|
|
@@ -3950,7 +3972,7 @@ function wakeRouteCheck(deps, active, activeInstallations) {
|
|
|
3950
3972
|
if (active.harness === 'claude-code') {
|
|
3951
3973
|
const readiness = inspectClaudeInbox({
|
|
3952
3974
|
pid: deps.claudeSourcePid ?? claudeSessionPid(deps.env),
|
|
3953
|
-
platform: process.platform,
|
|
3975
|
+
platform: deps.hookPlatform ?? process.platform,
|
|
3954
3976
|
readDescriptor: deps.claudeWake?.readDescriptor ?? systemClaudeWakeAdapters(deps.env).readDescriptor,
|
|
3955
3977
|
socketExists: (socketPath) => existsSync(socketPath),
|
|
3956
3978
|
});
|
|
@@ -3965,7 +3987,7 @@ function wakeRouteCheck(deps, active, activeInstallations) {
|
|
|
3965
3987
|
}
|
|
3966
3988
|
if (active.harness === 'codex') {
|
|
3967
3989
|
const readiness = inspectCodexResume(deps.env, {
|
|
3968
|
-
platform: process.platform,
|
|
3990
|
+
platform: deps.hookPlatform ?? process.platform,
|
|
3969
3991
|
directoryExists: (directory) => existsSync(directory),
|
|
3970
3992
|
});
|
|
3971
3993
|
return {
|
|
@@ -4120,24 +4142,7 @@ export function realIo(env = process.env) {
|
|
|
4120
4142
|
return log.error(message);
|
|
4121
4143
|
}
|
|
4122
4144
|
},
|
|
4123
|
-
openUrl
|
|
4124
|
-
try {
|
|
4125
|
-
if (process.platform === 'darwin') {
|
|
4126
|
-
spawn('open', [url], { stdio: 'ignore', detached: true }).unref();
|
|
4127
|
-
}
|
|
4128
|
-
else if (process.platform === 'win32') {
|
|
4129
|
-
// `start` is a cmd builtin; spawn('start', …) cannot work on Windows.
|
|
4130
|
-
// Empty title argument keeps URLs with special characters intact.
|
|
4131
|
-
spawn('cmd', ['/c', 'start', '', url], { stdio: 'ignore', detached: true }).unref();
|
|
4132
|
-
}
|
|
4133
|
-
else {
|
|
4134
|
-
spawn('xdg-open', [url], { stdio: 'ignore', detached: true }).unref();
|
|
4135
|
-
}
|
|
4136
|
-
}
|
|
4137
|
-
catch {
|
|
4138
|
-
// Browser opening is best-effort; the URL is printed anyway.
|
|
4139
|
-
}
|
|
4140
|
-
},
|
|
4145
|
+
openUrl,
|
|
4141
4146
|
};
|
|
4142
4147
|
}
|
|
4143
4148
|
//# sourceMappingURL=commands.js.map
|