@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/config-view.js
CHANGED
|
@@ -47,7 +47,11 @@ export function renderConfigList(config, options = {}) {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
lines.push('');
|
|
50
|
-
|
|
50
|
+
const command = 'notifai config explain <key>';
|
|
51
|
+
const help = `${glyph.arrow} ${command} for what one of these means and how to change it.`;
|
|
52
|
+
for (const line of wrap(help, columns)) {
|
|
53
|
+
lines.push(style.dim(line.replace(command, style.code(command))));
|
|
54
|
+
}
|
|
51
55
|
return lines;
|
|
52
56
|
}
|
|
53
57
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-view.js","sourceRoot":"","sources":["../../src/ui/config-view.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"config-view.js","sourceRoot":"","sources":["../../src/ui/config-view.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raidiant/notifai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Send native phone notifications from agents and local programs",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@clack/prompts": "^1.7.0",
|
|
17
|
-
"@raidiant/notifai-protocol": "0.
|
|
17
|
+
"@raidiant/notifai-protocol": "0.4.0",
|
|
18
18
|
"commander": "^14.0.0",
|
|
19
19
|
"picocolors": "^1.1.1",
|
|
20
20
|
"smol-toml": "^1.4.0"
|
|
@@ -54,6 +54,6 @@
|
|
|
54
54
|
"build": "node ../../scripts/clean-dist.mjs && tsc -p tsconfig.json",
|
|
55
55
|
"pretest": "pnpm run build",
|
|
56
56
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
57
|
-
"test": "
|
|
57
|
+
"test": "node ../../scripts/run-cli-tests.mjs"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/commands.test.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
mkdtempSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
symlinkSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from 'node:fs'
|
|
2
11
|
import os from 'node:os'
|
|
3
12
|
import path from 'node:path'
|
|
4
13
|
import { fileURLToPath } from 'node:url'
|
|
@@ -10,7 +19,7 @@ import type {
|
|
|
10
19
|
SubmissionReceipt,
|
|
11
20
|
SubmitNotificationRequestT,
|
|
12
21
|
} from '@raidiant/notifai-protocol'
|
|
13
|
-
import { describe, expect, it } from 'vitest'
|
|
22
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
14
23
|
import { ApiCallError, NetworkError, type ApiClient } from './client.js'
|
|
15
24
|
import type { ClaudeWakeAdapters } from './claude-wake.js'
|
|
16
25
|
import {
|
|
@@ -51,7 +60,7 @@ import {
|
|
|
51
60
|
} from './install-hooks.js'
|
|
52
61
|
import { readSessionState, writeProjectSession, writeSessionState } from './hooks.js'
|
|
53
62
|
import { nativeSkills as realNativeSkills, type NativeSkill, type NativeSkills, type SkillScope } from './native-skills.js'
|
|
54
|
-
import { CONFIG_KEYS, loadConfig, personalProjectConfigPath } from './config.js'
|
|
63
|
+
import { CONFIG_KEYS, loadConfig, personalProjectConfigPath, stateDir } from './config.js'
|
|
55
64
|
import { createLogger, logsDiskUsage, readLogRecords } from './logging.js'
|
|
56
65
|
import { hookAdapterPath, inspectHookAdapter } from './hook-adapter.js'
|
|
57
66
|
import type { Tone } from './ui/theme.js'
|
|
@@ -161,6 +170,29 @@ class InteractiveIo extends CapturedIo {
|
|
|
161
170
|
}
|
|
162
171
|
|
|
163
172
|
function trustInstalledCodexHooks(cwd: string, env: NodeJS.ProcessEnv): void {
|
|
173
|
+
const home = env['HOME']
|
|
174
|
+
if (home === undefined || home === '') {
|
|
175
|
+
throw new Error(
|
|
176
|
+
'trustInstalledCodexHooks requires env.HOME; refusing to use the real home directory',
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
const resolvedHome = path.resolve(home)
|
|
180
|
+
const tmpRoot = path.resolve(os.tmpdir())
|
|
181
|
+
const underTmp = resolvedHome === tmpRoot || resolvedHome.startsWith(`${tmpRoot}${path.sep}`)
|
|
182
|
+
if (!underTmp) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`trustInstalledCodexHooks will only write under ${tmpRoot}, not ${resolvedHome}`,
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
const codexHome = env['CODEX_HOME'] !== undefined && env['CODEX_HOME'] !== ''
|
|
188
|
+
? path.resolve(env['CODEX_HOME'])
|
|
189
|
+
: path.join(resolvedHome, '.codex')
|
|
190
|
+
if (codexHome !== tmpRoot && !codexHome.startsWith(`${tmpRoot}${path.sep}`)) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`trustInstalledCodexHooks will only write under ${tmpRoot}, not ${codexHome}`,
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
const file = path.join(codexHome, 'config.toml')
|
|
164
196
|
const installations = findInstallations(cwd, env).filter(
|
|
165
197
|
(installation) => installation.harness === 'codex',
|
|
166
198
|
)
|
|
@@ -170,9 +202,11 @@ function trustInstalledCodexHooks(cwd: string, env: NodeJS.ProcessEnv): void {
|
|
|
170
202
|
return `[hooks.state.${JSON.stringify(key)}]\ntrusted_hash = ${JSON.stringify(codexHookIdentityHash(handler))}\n`
|
|
171
203
|
}),
|
|
172
204
|
)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
205
|
+
mkdirSync(path.dirname(file), { recursive: true })
|
|
206
|
+
// Trust lives in the same config.toml as inline [hooks]. Overwriting the
|
|
207
|
+
// file would delete the global handlers this helper is trying to trust.
|
|
208
|
+
const existing = existsSync(file) ? readFileSync(file, 'utf8').trimEnd() : ''
|
|
209
|
+
writeFileSync(file, [existing, ...sections].filter((block) => block.length > 0).join('\n') + '\n')
|
|
176
210
|
}
|
|
177
211
|
|
|
178
212
|
function makeDeps(io: CapturedIo, client: ApiClient): CommandDeps {
|
|
@@ -215,6 +249,7 @@ function isolatedEnv(cwd: string): NodeJS.ProcessEnv {
|
|
|
215
249
|
HOME: home,
|
|
216
250
|
CLAUDE_CONFIG_DIR: path.join(home, '.claude'),
|
|
217
251
|
CODEX_HOME: path.join(home, '.codex'),
|
|
252
|
+
OPENCODE_CONFIG_DIR: path.join(home, '.config', 'opencode'),
|
|
218
253
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
219
254
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
220
255
|
}
|
|
@@ -1178,7 +1213,7 @@ describe('Cursor hook commands', () => {
|
|
|
1178
1213
|
it('installs native Cursor hooks with bounded chained answer continuations', () => {
|
|
1179
1214
|
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-cursor-install-'))
|
|
1180
1215
|
const io = new CapturedIo()
|
|
1181
|
-
const deps = { ...makeDeps(io, {} as ApiClient), cwd }
|
|
1216
|
+
const deps = { ...makeDeps(io, {} as ApiClient), cwd, env: isolatedEnv(cwd) }
|
|
1182
1217
|
|
|
1183
1218
|
expect(
|
|
1184
1219
|
hooksInstallCommand(deps, { harness: 'cursor', execPath, scriptPath }),
|
|
@@ -1221,6 +1256,7 @@ describe('Cursor hook commands', () => {
|
|
|
1221
1256
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
1222
1257
|
CODEX_HOME: path.join(cwd, 'codex'),
|
|
1223
1258
|
CLAUDE_CONFIG_DIR: path.join(cwd, 'claude'),
|
|
1259
|
+
OPENCODE_CONFIG_DIR: path.join(cwd, 'opencode'),
|
|
1224
1260
|
},
|
|
1225
1261
|
store: { load: () => null, save: () => {}, clear: () => {}, describe: () => 'empty store' },
|
|
1226
1262
|
}
|
|
@@ -1240,7 +1276,7 @@ describe('Cursor hook commands', () => {
|
|
|
1240
1276
|
it('uninstalls only Notifai Cursor hooks and preserves foreign hooks', () => {
|
|
1241
1277
|
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-cursor-uninstall-'))
|
|
1242
1278
|
const io = new CapturedIo()
|
|
1243
|
-
const deps = { ...makeDeps(io, {} as ApiClient), cwd }
|
|
1279
|
+
const deps = { ...makeDeps(io, {} as ApiClient), cwd, env: isolatedEnv(cwd) }
|
|
1244
1280
|
expect(
|
|
1245
1281
|
hooksInstallCommand(deps, { harness: 'cursor', execPath, scriptPath }),
|
|
1246
1282
|
).toBe(EXIT.ok)
|
|
@@ -1299,7 +1335,7 @@ describe('Codex hook representation', () => {
|
|
|
1299
1335
|
expect(io.outLines.join('\n')).toMatch(/Stop and the existing one will both fire/i)
|
|
1300
1336
|
})
|
|
1301
1337
|
|
|
1302
|
-
it('
|
|
1338
|
+
it('preserves a foreign inline Stop while collapsing our duplicate definition', async () => {
|
|
1303
1339
|
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-codex-collapse-'))
|
|
1304
1340
|
const toml = writeInlineStop(cwd, 'gdh-stop')
|
|
1305
1341
|
const json = path.join(cwd, '.codex', 'hooks.json')
|
|
@@ -1353,7 +1389,7 @@ describe('harness activation guidance', () => {
|
|
|
1353
1389
|
it('does not require a Claude Code restart for project hook files', () => {
|
|
1354
1390
|
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-claude-activation-'))
|
|
1355
1391
|
const io = new CapturedIo()
|
|
1356
|
-
const deps = { ...makeDeps(io, {} as ApiClient), cwd }
|
|
1392
|
+
const deps = { ...makeDeps(io, {} as ApiClient), cwd, env: isolatedEnv(cwd) }
|
|
1357
1393
|
|
|
1358
1394
|
expect(
|
|
1359
1395
|
hooksInstallCommand(deps, { harness: 'claude-code', execPath, scriptPath }),
|
|
@@ -1368,7 +1404,7 @@ describe('harness activation guidance', () => {
|
|
|
1368
1404
|
it('does not invent a Codex hook trust gate', () => {
|
|
1369
1405
|
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-codex-activation-'))
|
|
1370
1406
|
const io = new CapturedIo()
|
|
1371
|
-
const deps = { ...makeDeps(io, {} as ApiClient), cwd }
|
|
1407
|
+
const deps = { ...makeDeps(io, {} as ApiClient), cwd, env: isolatedEnv(cwd) }
|
|
1372
1408
|
|
|
1373
1409
|
expect(hooksInstallCommand(deps, { harness: 'codex', execPath, scriptPath })).toBe(
|
|
1374
1410
|
EXIT.ok,
|
|
@@ -1382,7 +1418,7 @@ describe('harness activation guidance', () => {
|
|
|
1382
1418
|
it('names the installed harness in the close, never a different one', () => {
|
|
1383
1419
|
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-cursor-close-'))
|
|
1384
1420
|
const io = new CapturedIo()
|
|
1385
|
-
const deps = { ...makeDeps(io, {} as ApiClient), cwd }
|
|
1421
|
+
const deps = { ...makeDeps(io, {} as ApiClient), cwd, env: isolatedEnv(cwd) }
|
|
1386
1422
|
|
|
1387
1423
|
expect(hooksInstallCommand(deps, { harness: 'cursor', execPath, scriptPath })).toBe(EXIT.ok)
|
|
1388
1424
|
|
|
@@ -1481,7 +1517,11 @@ describe('harness activation guidance', () => {
|
|
|
1481
1517
|
const deps = {
|
|
1482
1518
|
...makeDeps(io, {} as ApiClient),
|
|
1483
1519
|
cwd,
|
|
1484
|
-
env: {
|
|
1520
|
+
env: {
|
|
1521
|
+
HOME: path.join(cwd, 'home'),
|
|
1522
|
+
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
1523
|
+
OPENCODE_CONFIG_DIR: path.join(cwd, 'opencode-home'),
|
|
1524
|
+
},
|
|
1485
1525
|
}
|
|
1486
1526
|
|
|
1487
1527
|
expect(hooksInstallCommand(deps, { harness: 'opencode', execPath, scriptPath })).toBe(
|
|
@@ -1557,10 +1597,11 @@ describe('stable hook installation', () => {
|
|
|
1557
1597
|
).toBe(EXIT.ok)
|
|
1558
1598
|
const global = readFileSync(path.join(env.CLAUDE_CONFIG_DIR, 'settings.json'), 'utf8')
|
|
1559
1599
|
expect(global).toBe(firstDefinition)
|
|
1600
|
+
expect(readFileSync(local, 'utf8')).not.toContain('--owner notifai')
|
|
1560
1601
|
|
|
1561
1602
|
io.outLines = []
|
|
1562
1603
|
await doctorCommand(deps, {})
|
|
1563
|
-
expect(io.outLines.join('\n')).toMatch(/hooks \(duplicates\)
|
|
1604
|
+
expect(io.outLines.join('\n')).not.toMatch(/hooks \(duplicates\)/)
|
|
1564
1605
|
|
|
1565
1606
|
expect(hooksUninstallCommand(deps, { harness: 'claude-code' })).toBe(EXIT.ok)
|
|
1566
1607
|
expect(existsSync(hookAdapterPath(deps.hookAdapterHome))).toBe(true)
|
|
@@ -1570,6 +1611,27 @@ describe('stable hook installation', () => {
|
|
|
1570
1611
|
),
|
|
1571
1612
|
).toHaveLength(1)
|
|
1572
1613
|
})
|
|
1614
|
+
|
|
1615
|
+
it('does not add a project copy when global hooks already cover the machine', () => {
|
|
1616
|
+
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-hooks-global-enough-'))
|
|
1617
|
+
const io = new CapturedIo()
|
|
1618
|
+
const env = isolatedEnv(cwd)
|
|
1619
|
+
const deps = { ...makeDeps(io, {} as ApiClient), cwd, env }
|
|
1620
|
+
const execPath = process.execPath
|
|
1621
|
+
const scriptPath = fileURLToPath(import.meta.url)
|
|
1622
|
+
|
|
1623
|
+
expect(hooksInstallCommand(deps, { harness: 'codex', global: true, execPath, scriptPath })).toBe(
|
|
1624
|
+
EXIT.ok,
|
|
1625
|
+
)
|
|
1626
|
+
io.outLines = []
|
|
1627
|
+
expect(hooksInstallCommand(deps, { harness: 'codex', execPath, scriptPath })).toBe(EXIT.ok)
|
|
1628
|
+
expect(existsSync(path.join(cwd, '.codex', 'config.toml'))).toBe(false)
|
|
1629
|
+
expect(io.outLines.join('\n')).toMatch(/already cover this machine/)
|
|
1630
|
+
expect(io.outLines.join('\n')).toMatch(/uninstall --harness codex --global/)
|
|
1631
|
+
expect(
|
|
1632
|
+
findInstallations(cwd, env, deps.hookAdapterHome).filter((item) => item.harness === 'codex'),
|
|
1633
|
+
).toEqual([expect.objectContaining({ global: true })])
|
|
1634
|
+
})
|
|
1573
1635
|
})
|
|
1574
1636
|
|
|
1575
1637
|
describe('projectSlugFrom', () => {
|
|
@@ -2094,6 +2156,7 @@ describe('init', () => {
|
|
|
2094
2156
|
platform: 'ios' as const,
|
|
2095
2157
|
permission_status: 'authorized',
|
|
2096
2158
|
registration_healthy: true,
|
|
2159
|
+
reply_protocol_version: 1,
|
|
2097
2160
|
last_seen_at: '2026-08-05T18:00:00.000Z',
|
|
2098
2161
|
}
|
|
2099
2162
|
|
|
@@ -2467,6 +2530,7 @@ describe('init', () => {
|
|
|
2467
2530
|
expect(wired).toEqual(['claude-code', 'codex'])
|
|
2468
2531
|
expect(existsSync(path.join(cwd, '.claude', 'settings.local.json'))).toBe(true)
|
|
2469
2532
|
expect(existsSync(path.join(cwd, '.codex', 'hooks.json'))).toBe(true)
|
|
2533
|
+
expect(existsSync(path.join(cwd, '.codex', 'config.toml'))).toBe(false)
|
|
2470
2534
|
expect(io.outLines.join('\n')).toContain('Installed claude-code hooks')
|
|
2471
2535
|
expect(io.outLines.join('\n')).toContain('Installed codex hooks')
|
|
2472
2536
|
})
|
|
@@ -2632,10 +2696,12 @@ describe('init', () => {
|
|
|
2632
2696
|
...makeDeps(io, client),
|
|
2633
2697
|
cwd,
|
|
2634
2698
|
env: {
|
|
2699
|
+
HOME: path.join(cwd, 'home'),
|
|
2635
2700
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
2636
2701
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
2637
2702
|
CODEX_HOME: path.join(cwd, 'codex'),
|
|
2638
2703
|
CLAUDE_CONFIG_DIR: path.join(cwd, 'claude'),
|
|
2704
|
+
OPENCODE_CONFIG_DIR: path.join(cwd, 'opencode'),
|
|
2639
2705
|
},
|
|
2640
2706
|
}
|
|
2641
2707
|
|
|
@@ -2725,6 +2791,9 @@ describe('init', () => {
|
|
|
2725
2791
|
expect(io.notes.some((n) => n.message.includes('I will wait up to 10 minutes'))).toBe(true)
|
|
2726
2792
|
expect(io.spinnerEvents).toContain('stop:iPhone is ready to receive')
|
|
2727
2793
|
expect(io.spinnerEvents).toContain('stop:Receipt observed from iPhone')
|
|
2794
|
+
expect(io.outLines.join('\n')).toContain(
|
|
2795
|
+
"Companion Receipt (the app's delivery confirmation) observed from iPhone.",
|
|
2796
|
+
)
|
|
2728
2797
|
expect(io.outLines.join('\n')).toContain('All set.')
|
|
2729
2798
|
expect(submitCalls).toBe(1)
|
|
2730
2799
|
expect(submittedDraft?.draft.event).toBe('setup_verified')
|
|
@@ -2867,6 +2936,8 @@ describe('init', () => {
|
|
|
2867
2936
|
expect(io.outLines.join('\n')).toContain('Next: Delivery proof')
|
|
2868
2937
|
expect(io.outLines.join('\n')).toContain('Provider accepted the notification')
|
|
2869
2938
|
expect(io.outLines.join('\n')).toContain('Proof may still arrive')
|
|
2939
|
+
const proofDir = path.join(stateDir(deps.env), 'setup-proofs')
|
|
2940
|
+
expect(readdirSync(proofDir).some((name) => name.endsWith('.json'))).toBe(true)
|
|
2870
2941
|
|
|
2871
2942
|
io.outLines = []
|
|
2872
2943
|
io.errLines = []
|
|
@@ -3276,6 +3347,27 @@ describe('an outage is not an answer', () => {
|
|
|
3276
3347
|
describe('asking before the hooks have ever run', () => {
|
|
3277
3348
|
const execPath = process.execPath
|
|
3278
3349
|
const scriptPath = fileURLToPath(import.meta.url)
|
|
3350
|
+
const scratch: string[] = []
|
|
3351
|
+
|
|
3352
|
+
function scratchDir(prefix: string): string {
|
|
3353
|
+
const dir = mkdtempSync(path.join(os.tmpdir(), prefix))
|
|
3354
|
+
scratch.push(dir)
|
|
3355
|
+
return dir
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3358
|
+
afterEach(() => {
|
|
3359
|
+
while (scratch.length > 0) {
|
|
3360
|
+
rmSync(scratch.pop()!, { recursive: true, force: true })
|
|
3361
|
+
}
|
|
3362
|
+
})
|
|
3363
|
+
|
|
3364
|
+
it('refuses to write Codex trust outside the isolated test account', () => {
|
|
3365
|
+
expect(() => trustInstalledCodexHooks(os.tmpdir(), {})).toThrow(/requires env.HOME/)
|
|
3366
|
+
expect(() => trustInstalledCodexHooks(os.tmpdir(), { HOME: path.dirname(os.tmpdir()) })).toThrow(
|
|
3367
|
+
/will only write under/,
|
|
3368
|
+
)
|
|
3369
|
+
expect(os.homedir()).toBe(process.env['NOTIFAI_TEST_HOME'])
|
|
3370
|
+
})
|
|
3279
3371
|
|
|
3280
3372
|
it('names the active Codex harness instead of unrelated installed adapters', () => {
|
|
3281
3373
|
const cwd = mkdtempSync(path.join(os.tmpdir(), 'notifai-active-codex-missing-'))
|
|
@@ -3283,6 +3375,7 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3283
3375
|
const env = {
|
|
3284
3376
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
3285
3377
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
3378
|
+
HOME: path.join(cwd, 'home'),
|
|
3286
3379
|
CODEX_HOME: path.join(cwd, 'codex-home'),
|
|
3287
3380
|
CODEX_THREAD_ID: 'codex-current-thread',
|
|
3288
3381
|
CLAUDE_CONFIG_DIR: path.join(cwd, 'claude-home'),
|
|
@@ -3307,6 +3400,7 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3307
3400
|
const env = {
|
|
3308
3401
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
3309
3402
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
3403
|
+
HOME: path.join(cwd, 'home'),
|
|
3310
3404
|
CODEX_HOME: path.join(cwd, 'codex-home'),
|
|
3311
3405
|
CODEX_THREAD_ID: 'codex-current-thread',
|
|
3312
3406
|
}
|
|
@@ -3325,11 +3419,12 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3325
3419
|
})
|
|
3326
3420
|
|
|
3327
3421
|
it('registers only when the active Codex thread owns the project pointer', () => {
|
|
3328
|
-
const cwd =
|
|
3422
|
+
const cwd = scratchDir('notifai-active-codex-matching-')
|
|
3329
3423
|
const io = new CapturedIo()
|
|
3330
3424
|
const env = {
|
|
3331
3425
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
3332
3426
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
3427
|
+
HOME: path.join(cwd, 'home'),
|
|
3333
3428
|
CODEX_HOME: path.join(cwd, 'codex-home'),
|
|
3334
3429
|
CODEX_THREAD_ID: 'codex-current-thread',
|
|
3335
3430
|
}
|
|
@@ -3469,11 +3564,12 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3469
3564
|
})
|
|
3470
3565
|
|
|
3471
3566
|
it('makes one work-resumption commitment per offered answer part of the asking turn', () => {
|
|
3472
|
-
const cwd =
|
|
3567
|
+
const cwd = scratchDir('notifai-ask-commitment-')
|
|
3473
3568
|
const io = new CapturedIo()
|
|
3474
3569
|
const env = {
|
|
3475
3570
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
3476
3571
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
3572
|
+
HOME: path.join(cwd, 'home'),
|
|
3477
3573
|
CODEX_HOME: path.join(cwd, 'codex-home'),
|
|
3478
3574
|
CODEX_THREAD_ID: 'codex-commitment-thread',
|
|
3479
3575
|
}
|
|
@@ -3504,11 +3600,12 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3504
3600
|
})
|
|
3505
3601
|
|
|
3506
3602
|
it('requires a concrete fallback for free-text answers before the turn ends', () => {
|
|
3507
|
-
const cwd =
|
|
3603
|
+
const cwd = scratchDir('notifai-ask-free-text-')
|
|
3508
3604
|
const io = new CapturedIo()
|
|
3509
3605
|
const env = {
|
|
3510
3606
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
3511
3607
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
3608
|
+
HOME: path.join(cwd, 'home'),
|
|
3512
3609
|
CODEX_HOME: path.join(cwd, 'codex-home'),
|
|
3513
3610
|
CODEX_THREAD_ID: 'codex-free-text-thread',
|
|
3514
3611
|
}
|
|
@@ -3527,11 +3624,12 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3527
3624
|
})
|
|
3528
3625
|
|
|
3529
3626
|
it('refuses a Codex question when the installed Stop definition is no longer trusted', async () => {
|
|
3530
|
-
const cwd =
|
|
3627
|
+
const cwd = scratchDir('notifai-codex-stale-trust-')
|
|
3531
3628
|
const io = new CapturedIo()
|
|
3532
3629
|
const env = {
|
|
3533
3630
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
3534
3631
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
3632
|
+
HOME: path.join(cwd, 'home'),
|
|
3535
3633
|
CODEX_HOME: path.join(cwd, 'codex-home'),
|
|
3536
3634
|
CODEX_THREAD_ID: 'codex-current-thread',
|
|
3537
3635
|
}
|
|
@@ -3542,7 +3640,7 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3542
3640
|
.find((installation) => installation.harness === 'codex')
|
|
3543
3641
|
?.handlers.find((handler) => handler.event === 'Stop')
|
|
3544
3642
|
expect(stop).toBeDefined()
|
|
3545
|
-
const configFile = path.join(env.CODEX_HOME
|
|
3643
|
+
const configFile = path.join(env.CODEX_HOME!, 'config.toml')
|
|
3546
3644
|
writeFileSync(
|
|
3547
3645
|
configFile,
|
|
3548
3646
|
readFileSync(configFile, 'utf8').replace(codexHookIdentityHash(stop!), 'sha256:obsolete'),
|
|
@@ -3566,6 +3664,7 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3566
3664
|
const env = {
|
|
3567
3665
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
3568
3666
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
3667
|
+
HOME: path.join(cwd, 'home'),
|
|
3569
3668
|
CODEX_HOME: path.join(cwd, 'codex-home'),
|
|
3570
3669
|
CODEX_THREAD_ID: 'codex-current-thread',
|
|
3571
3670
|
CLAUDE_CONFIG_DIR: path.join(cwd, 'claude-home'),
|
|
@@ -3674,7 +3773,7 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3674
3773
|
})
|
|
3675
3774
|
|
|
3676
3775
|
it('rejects duplicate active Codex definitions before registration', () => {
|
|
3677
|
-
const cwd =
|
|
3776
|
+
const cwd = scratchDir('notifai-active-codex-duplicate-')
|
|
3678
3777
|
const io = new CapturedIo()
|
|
3679
3778
|
const env = {
|
|
3680
3779
|
HOME: path.join(cwd, 'home'),
|
|
@@ -3685,9 +3784,13 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3685
3784
|
}
|
|
3686
3785
|
const deps = { ...makeDeps(io, {} as ApiClient), cwd, env, now: () => 42 }
|
|
3687
3786
|
expect(hooksInstallCommand(deps, { harness: 'codex', execPath, scriptPath })).toBe(EXIT.ok)
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3787
|
+
mkdirSync(env.CODEX_HOME!, { recursive: true })
|
|
3788
|
+
applyPlan(path.join(env.CODEX_HOME!, 'config.toml'), {
|
|
3789
|
+
hooks: buildHookConfig({
|
|
3790
|
+
adapterPath: hookAdapterPath(deps.hookAdapterHome),
|
|
3791
|
+
harness: 'codex',
|
|
3792
|
+
}),
|
|
3793
|
+
})
|
|
3691
3794
|
trustInstalledCodexHooks(cwd, env)
|
|
3692
3795
|
writeSessionState('codex-current-thread', env, { last_prompt_at: 42, last_stop_at: 41 })
|
|
3693
3796
|
writeProjectSession(cwd, env, 'codex-current-thread', 42, 'codex')
|
|
@@ -3791,6 +3894,7 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3791
3894
|
const env = {
|
|
3792
3895
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
3793
3896
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
3897
|
+
HOME: path.join(cwd, 'home'),
|
|
3794
3898
|
CODEX_HOME: path.join(cwd, 'codex-home'),
|
|
3795
3899
|
CLAUDE_CONFIG_DIR: path.join(cwd, 'claude-home'),
|
|
3796
3900
|
CLAUDECODE: '1',
|
|
@@ -3813,6 +3917,7 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3813
3917
|
XDG_CONFIG_HOME: path.join(cwd, 'config'),
|
|
3814
3918
|
XDG_STATE_HOME: path.join(cwd, 'state'),
|
|
3815
3919
|
CLAUDE_CONFIG_DIR: path.join(cwd, 'claude-home'),
|
|
3920
|
+
HOME: path.join(cwd, 'home'),
|
|
3816
3921
|
CODEX_HOME: path.join(cwd, 'codex-home'),
|
|
3817
3922
|
CLAUDECODE: '1',
|
|
3818
3923
|
CLAUDE_CODE_SESSION_ID: 'claude-current',
|
|
@@ -3995,7 +4100,12 @@ describe('asking before the hooks have ever run', () => {
|
|
|
3995
4100
|
...isolatedEnv(cwd),
|
|
3996
4101
|
CODEX_THREAD_ID: '9f1c2b3a-4d5e-6f70-8192-a3b4c5d6e7f8',
|
|
3997
4102
|
}
|
|
3998
|
-
const deps = {
|
|
4103
|
+
const deps = {
|
|
4104
|
+
...makeDeps(io, {} as ApiClient),
|
|
4105
|
+
cwd,
|
|
4106
|
+
env,
|
|
4107
|
+
hookPlatform: 'darwin' as NodeJS.Platform,
|
|
4108
|
+
}
|
|
3999
4109
|
expect(hooksInstallCommand(deps, { harness: 'codex', execPath, scriptPath })).toBe(EXIT.ok)
|
|
4000
4110
|
|
|
4001
4111
|
io.outLines = []
|
|
@@ -4006,14 +4116,8 @@ describe('asking before the hooks have ever run', () => {
|
|
|
4006
4116
|
io.outLines = []
|
|
4007
4117
|
await doctorCommand(deps, {})
|
|
4008
4118
|
const out = io.outLines.join('\n')
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
expect(out).toContain('can prove a stopped thread unowned before resuming it')
|
|
4012
|
-
} else {
|
|
4013
|
-
// Linux has no non-blocking `flock` through Node's `open(2)` flags, so
|
|
4014
|
-
// the gate stays shut and doctor says so rather than implying a wake.
|
|
4015
|
-
expect(out).toContain('nothing can be resumed')
|
|
4016
|
-
}
|
|
4119
|
+
expect(out).toMatch(/ok\s+hooks \(wake route\)/)
|
|
4120
|
+
expect(out).toContain('can prove a stopped thread unowned before resuming it')
|
|
4017
4121
|
})
|
|
4018
4122
|
|
|
4019
4123
|
it('documents the failing exit contract in doctor JSON', async () => {
|
|
@@ -4050,7 +4154,7 @@ describe('asking before the hooks have ever run', () => {
|
|
|
4050
4154
|
const deps = {
|
|
4051
4155
|
...makeDeps(io, {} as ApiClient),
|
|
4052
4156
|
cwd,
|
|
4053
|
-
env:
|
|
4157
|
+
env: isolatedEnv(cwd),
|
|
4054
4158
|
}
|
|
4055
4159
|
|
|
4056
4160
|
expect(hooksInstallCommand(deps, { harness: 'cursor', execPath, scriptPath })).toBe(EXIT.ok)
|
|
@@ -4087,7 +4191,13 @@ describe('asking before the hooks have ever run', () => {
|
|
|
4087
4191
|
const deps = {
|
|
4088
4192
|
...makeDeps(io, {} as ApiClient),
|
|
4089
4193
|
cwd,
|
|
4090
|
-
env: {
|
|
4194
|
+
env: {
|
|
4195
|
+
HOME: path.join(cwd, 'home'),
|
|
4196
|
+
XDG_STATE_HOME: cwd,
|
|
4197
|
+
CODEX_HOME: path.join(cwd, 'none'),
|
|
4198
|
+
CLAUDE_CONFIG_DIR: path.join(cwd, 'none'),
|
|
4199
|
+
OPENCODE_CONFIG_DIR: path.join(cwd, 'none'),
|
|
4200
|
+
},
|
|
4091
4201
|
}
|
|
4092
4202
|
|
|
4093
4203
|
expect(askCommand(deps, 'Ship it?', {})).toBe(EXIT.usage)
|