@agfpd/iapeer 0.2.77 → 0.2.79

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.
Files changed (113) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +2 -2
  3. package/package.json +21 -2
  4. package/src/cli/index.ts +95 -78
  5. package/src/connect/index.ts +4 -4
  6. package/src/core/constants.ts +3 -3
  7. package/src/core/normalize.ts +2 -2
  8. package/src/create/index.ts +1 -1
  9. package/src/daemon/index.ts +5 -5
  10. package/src/daemon/main.ts +9 -9
  11. package/src/enable/index.ts +19 -18
  12. package/src/enable/provisionCommand.ts +10 -10
  13. package/src/identity/index.ts +2 -2
  14. package/src/identity/profileStandard.ts +8 -8
  15. package/src/init/index.ts +10 -10
  16. package/src/install/index.ts +2 -2
  17. package/src/install/signing.ts +3 -4
  18. package/src/launch/adapters/claude.ts +7 -7
  19. package/src/launch/adapters/codex.ts +5 -6
  20. package/src/launch/canary.ts +8 -8
  21. package/src/launch/cmdlog.ts +3 -3
  22. package/src/launch/composeSystemPrompt.ts +2 -1
  23. package/src/launch/index.ts +5 -6
  24. package/src/launch/invocation.ts +1 -1
  25. package/src/launch/launchd.ts +14 -15
  26. package/src/launch/launchdRun.ts +6 -6
  27. package/src/launch/nativeMemory.ts +9 -9
  28. package/src/launch/ptyHost.ts +1 -1
  29. package/src/launch/readyGateModel.ts +3 -3
  30. package/src/launch/types.ts +7 -7
  31. package/src/lifecycle/eventlog.ts +2 -2
  32. package/src/lifecycle/index.ts +21 -21
  33. package/src/lifecycle/queue.ts +1 -1
  34. package/src/onboard/index.ts +19 -13
  35. package/src/onboard/memory.ts +5 -5
  36. package/src/onboard/steps.ts +8 -9
  37. package/src/provision/index.ts +2 -3
  38. package/src/runtime/deploy.ts +2 -2
  39. package/src/runtime/index.ts +4 -4
  40. package/src/shadow/gate.ts +6 -6
  41. package/src/shadow/index.ts +2 -2
  42. package/src/status/index.ts +11 -11
  43. package/src/storage/index.ts +1 -1
  44. package/src/supervisor/daemon.ts +20 -13
  45. package/src/transport/index.ts +11 -11
  46. package/src/update/index.ts +4 -4
  47. package/src/cli/cli.test.ts +0 -603
  48. package/src/cli/listTui.test.ts +0 -71
  49. package/src/codec/codec.test.ts +0 -271
  50. package/src/connect/connect.test.ts +0 -163
  51. package/src/core/constants.test.ts +0 -32
  52. package/src/core/normalize.test.ts +0 -98
  53. package/src/create/create.test.ts +0 -143
  54. package/src/daemon/daemon-http.test.ts +0 -114
  55. package/src/daemon/daemon.test.ts +0 -267
  56. package/src/daemon/main.test.ts +0 -413
  57. package/src/enable/enable.test.ts +0 -95
  58. package/src/enable/provisionCommand.test.ts +0 -107
  59. package/src/identity/identity.test.ts +0 -279
  60. package/src/identity/profileStandard.test.ts +0 -204
  61. package/src/init/init.test.ts +0 -215
  62. package/src/init/runtime-resolve.test.ts +0 -49
  63. package/src/install/install.test.ts +0 -66
  64. package/src/install/signing.test.ts +0 -84
  65. package/src/launch/bootdeliver.test.ts +0 -108
  66. package/src/launch/bootstrap.test.ts +0 -56
  67. package/src/launch/canary.test.ts +0 -346
  68. package/src/launch/cmdlog.test.ts +0 -94
  69. package/src/launch/codexHooksTrust.test.ts +0 -248
  70. package/src/launch/composeSystemPrompt.layers.test.ts +0 -445
  71. package/src/launch/composeSystemPrompt.test.ts +0 -145
  72. package/src/launch/hostComposerOccupied.test.ts +0 -63
  73. package/src/launch/invocation.test.ts +0 -174
  74. package/src/launch/launch.test.ts +0 -359
  75. package/src/launch/launchd.test.ts +0 -499
  76. package/src/launch/nativeMemory.test.ts +0 -193
  77. package/src/launch/ptyHost.test.ts +0 -38
  78. package/src/launch/readyGateModel.test.ts +0 -72
  79. package/src/launch/sockdir.test.ts +0 -74
  80. package/src/lifecycle/eventlog.test.ts +0 -114
  81. package/src/lifecycle/lifecycle.test.ts +0 -1056
  82. package/src/lifecycle/queue.test.ts +0 -185
  83. package/src/onboard/memory.test.ts +0 -157
  84. package/src/onboard/onboard.test.ts +0 -39
  85. package/src/onboard/steps.test.ts +0 -201
  86. package/src/provision/provision.test.ts +0 -288
  87. package/src/registry/registry.test.ts +0 -432
  88. package/src/runtime/runtime.test.ts +0 -226
  89. package/src/runtime/update.test.ts +0 -192
  90. package/src/shadow/enumerate-skip.test.ts +0 -67
  91. package/src/shadow/gate.test.ts +0 -286
  92. package/src/shadow/install.test.ts +0 -153
  93. package/src/shadow/shadow.test.ts +0 -42
  94. package/src/status/status.test.ts +0 -208
  95. package/src/storage/peers-home.test.ts +0 -34
  96. package/src/storage/rotatelog.test.ts +0 -44
  97. package/src/storage/storage.test.ts +0 -65
  98. package/src/supervisor/attached-marker.test.ts +0 -84
  99. package/src/supervisor/boot.test.ts +0 -66
  100. package/src/supervisor/client.attach.test.ts +0 -107
  101. package/src/supervisor/daemon.test.ts +0 -213
  102. package/src/supervisor/deliver.test.ts +0 -58
  103. package/src/supervisor/panelog.test.ts +0 -95
  104. package/src/supervisor/protocol.test.ts +0 -222
  105. package/src/supervisor/selfargv.test.ts +0 -22
  106. package/src/supervisor/spinner-strip.test.ts +0 -52
  107. package/src/transport/compactDone.test.ts +0 -144
  108. package/src/transport/composerQueue.test.ts +0 -204
  109. package/src/transport/hostControl.test.ts +0 -87
  110. package/src/transport/hostDeliver.test.ts +0 -137
  111. package/src/transport/hostLive.test.ts +0 -50
  112. package/src/transport/routesend-wake.test.ts +0 -357
  113. package/src/update/update.test.ts +0 -208
@@ -1,248 +0,0 @@
1
- // codex hooks trust pre-seed — the deterministic replacement for the "Hooks
2
- // need review" modal. The HARD anchor here: the GOLDEN hashes — three values
3
- // codex-cli 0.138.0 itself granted live ("Trust all and continue", smoke
4
- // 11.06, isolated CODEX_HOME). If the re-implemented algorithm drifts from
5
- // these, we would seed state codex ignores → hooks silently skipped fleet-wide.
6
-
7
- import { afterEach, describe, expect, test } from 'bun:test'
8
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs'
9
- import { tmpdir } from 'os'
10
- import { join } from 'path'
11
- import { realpathSync } from 'fs'
12
- import {
13
- checkCodexHooksTrust,
14
- commandHookHash,
15
- computeHooksTrustEntries,
16
- preSeedCodexHooksTrust,
17
- removeCodexHooksTrustUnder,
18
- } from './codexHooksTrust.ts'
19
- import { codexGlobalConfigPath } from './nativeMemory.ts'
20
-
21
- const dirs: string[] = []
22
- function mkTmp(): string {
23
- const d = mkdtempSync(join(tmpdir(), 'iapeer-hookstrust-'))
24
- dirs.push(d)
25
- return d
26
- }
27
- afterEach(() => {
28
- while (dirs.length) rmSync(dirs.pop()!, { recursive: true, force: true })
29
- })
30
-
31
- /** Env pointing the codex global config into an isolated CODEX_HOME. */
32
- function isolatedEnv(): { env: NodeJS.ProcessEnv; configPath: string } {
33
- const home = mkTmp()
34
- const env = { CODEX_HOME: home } as NodeJS.ProcessEnv
35
- return { env, configPath: codexGlobalConfigPath(env) }
36
- }
37
-
38
- function writeHooksJson(dir: string, content: unknown): string {
39
- const path = join(dir, 'hooks.json')
40
- writeFileSync(path, JSON.stringify(content, null, 2))
41
- return path
42
- }
43
-
44
- // ─── golden hashes (granted LIVE by codex-cli 0.138.0) ──────────────────────
45
-
46
- describe('commandHookHash — golden values from live-granted trust', () => {
47
- test('session_start, no matcher, default timeout (V1 smoke hook)', () => {
48
- expect(
49
- commandHookHash('session_start', undefined, {
50
- command: '/tmp/iapeer-codex-hooks-smoke/bin/stamp.sh user-hooks-json-sessionstart',
51
- }),
52
- ).toBe('sha256:c21b34240dd15d7184bf391f13e820399cb4b8e97c22347689316f8d02207391')
53
- })
54
-
55
- test('session_start, no matcher, default timeout (V2 smoke hook)', () => {
56
- expect(
57
- commandHookHash('session_start', undefined, {
58
- command: '/tmp/iapeer-codex-hooks-smoke/bin/stamp.sh user-hooks-json-sessionstart-V2',
59
- }),
60
- ).toBe('sha256:5727103dc6b7409ae2a811561e28de70b625a2bdd28f8715a73e6542836f34fe')
61
- })
62
-
63
- test('post_tool_use, no matcher, default timeout (project-local smoke hook)', () => {
64
- expect(
65
- commandHookHash('post_tool_use', undefined, {
66
- command: '/tmp/iapeer-codex-hooks-smoke/bin/stamp.sh proj-hooks-json-posttooluse',
67
- }),
68
- ).toBe('sha256:86f96ab754e05851c6f2f9b162bd99c70359099cdb6c93c114d0e6df42971030')
69
- })
70
-
71
- test('normalization knobs shift the hash: timeout, matcher, statusMessage', () => {
72
- const base = commandHookHash('post_tool_use', undefined, { command: 'x' })
73
- expect(commandHookHash('post_tool_use', undefined, { command: 'x', timeout: 30 })).not.toBe(base)
74
- expect(commandHookHash('post_tool_use', 'Write|Edit', { command: 'x' })).not.toBe(base)
75
- expect(commandHookHash('post_tool_use', undefined, { command: 'x', statusMessage: 's' })).not.toBe(base)
76
- // explicit default timeout hashes the SAME as omitted (both normalize to 600)
77
- expect(commandHookHash('post_tool_use', undefined, { command: 'x', timeout: 600 })).toBe(base)
78
- // timeout 0 clamps to 1 (upstream .max(1)), distinct from the default
79
- expect(commandHookHash('post_tool_use', undefined, { command: 'x', timeout: 0 })).toBe(
80
- commandHookHash('post_tool_use', undefined, { command: 'x', timeout: 1 }),
81
- )
82
- })
83
- })
84
-
85
- // ─── hooks.json → entries ────────────────────────────────────────────────────
86
-
87
- describe('computeHooksTrustEntries', () => {
88
- test('keys carry source:event_snake:group:handler; matcher passes through', () => {
89
- const entries = computeHooksTrustEntries(
90
- JSON.stringify({
91
- hooks: {
92
- PostToolUse: [
93
- { matcher: 'Write', hooks: [{ type: 'command', command: 'a' }] },
94
- { hooks: [{ type: 'command', command: 'b' }, { type: 'command', command: 'c' }] },
95
- ],
96
- },
97
- }),
98
- '/some/hooks.json',
99
- )
100
- expect(entries.map(e => e.key)).toEqual([
101
- '/some/hooks.json:post_tool_use:0:0',
102
- '/some/hooks.json:post_tool_use:1:0',
103
- '/some/hooks.json:post_tool_use:1:1',
104
- ])
105
- expect(entries[0]!.hash).toBe(commandHookHash('post_tool_use', 'Write', { command: 'a' }))
106
- expect(entries[1]!.hash).toBe(commandHookHash('post_tool_use', undefined, { command: 'b' }))
107
- })
108
-
109
- test('skipped handlers (async / prompt / empty) consume their index, get no entry', () => {
110
- const entries = computeHooksTrustEntries(
111
- JSON.stringify({
112
- hooks: {
113
- SessionStart: [
114
- {
115
- hooks: [
116
- { type: 'command', command: 'first', async: true }, // h=0 skipped
117
- { type: 'prompt' }, // h=1 skipped
118
- { type: 'command', command: ' ' }, // h=2 skipped (empty)
119
- { type: 'command', command: 'real' }, // h=3 KEPT
120
- ],
121
- },
122
- ],
123
- },
124
- }),
125
- '/p/hooks.json',
126
- )
127
- expect(entries).toHaveLength(1)
128
- expect(entries[0]!.key).toBe('/p/hooks.json:session_start:0:3')
129
- })
130
-
131
- test('UserPromptSubmit/Stop force the matcher OFF (upstream matcher_pattern_for_event)', () => {
132
- const entries = computeHooksTrustEntries(
133
- JSON.stringify({
134
- hooks: {
135
- Stop: [{ matcher: 'ignored', hooks: [{ type: 'command', command: 'x' }] }],
136
- },
137
- }),
138
- '/p/hooks.json',
139
- )
140
- expect(entries[0]!.hash).toBe(commandHookHash('stop', undefined, { command: 'x' }))
141
- })
142
-
143
- test('unknown event / malformed shapes are loud errors', () => {
144
- expect(() => computeHooksTrustEntries(JSON.stringify({ hooks: { NotAnEvent: [] } }), '/p')).toThrow(
145
- /unknown hook event/,
146
- )
147
- expect(() => computeHooksTrustEntries('not json', '/p')).toThrow(/not valid JSON/)
148
- expect(() => computeHooksTrustEntries(JSON.stringify({}), '/p')).toThrow(/"hooks" object/)
149
- })
150
- })
151
-
152
- // ─── pre-seed (state surgery) ────────────────────────────────────────────────
153
-
154
- describe('preSeedCodexHooksTrust', () => {
155
- test('absent config → sections appended; second run → already', () => {
156
- const { env, configPath } = isolatedEnv()
157
- const proj = mkTmp()
158
- const hooksPath = writeHooksJson(proj, {
159
- hooks: { SessionStart: [{ hooks: [{ type: 'command', command: '/x/stamp.sh' }] }] },
160
- })
161
- const first = preSeedCodexHooksTrust(hooksPath, env)
162
- expect(first.state).toBe('written')
163
- expect(first.entries).toHaveLength(1)
164
- const real = realpathSync(hooksPath)
165
- const text = readFileSync(configPath, 'utf8')
166
- expect(text).toContain(`[hooks.state."${real}:session_start:0:0"]`)
167
- expect(text).toContain(`trusted_hash = "${first.entries[0]!.hash}"`)
168
- expect(preSeedCodexHooksTrust(hooksPath, env).state).toBe('already')
169
- })
170
-
171
- test('NO-CLOBBER: foreign sections survive; changed hook updates IN PLACE', () => {
172
- const { env, configPath } = isolatedEnv()
173
- const proj = mkTmp()
174
- const hooksPath = writeHooksJson(proj, {
175
- hooks: { SessionStart: [{ hooks: [{ type: 'command', command: 'one' }] }] },
176
- })
177
- mkdirSync(join(env.CODEX_HOME!), { recursive: true })
178
- const foreign = '[projects."/keep/me"]\ntrust_level = "trusted"\n\n[features]\nmemories = false\n'
179
- writeFileSync(configPath, foreign)
180
- preSeedCodexHooksTrust(hooksPath, env)
181
- // change the command → hash changes → same section updated, not duplicated
182
- writeHooksJson(proj, { hooks: { SessionStart: [{ hooks: [{ type: 'command', command: 'two' }] }] } })
183
- const second = preSeedCodexHooksTrust(hooksPath, env)
184
- expect(second.state).toBe('written')
185
- const text = readFileSync(configPath, 'utf8')
186
- expect(text).toContain('[projects."/keep/me"]')
187
- expect(text).toContain('memories = false')
188
- const real = realpathSync(hooksPath)
189
- const headers = text.split('\n').filter(l => l.includes(`${real}:session_start:0:0`))
190
- expect(headers).toHaveLength(1) // updated in place — no duplicate section
191
- expect(text).toContain(`trusted_hash = "${second.entries[0]!.hash}"`)
192
- })
193
-
194
- test('missing hooks.json → failed (loud), config untouched', () => {
195
- const { env, configPath } = isolatedEnv()
196
- const o = preSeedCodexHooksTrust('/nonexistent/hooks.json', env)
197
- expect(o.state).toBe('failed')
198
- expect(existsSync(configPath)).toBe(false)
199
- })
200
- })
201
-
202
- // ─── check (read-only drift detector) ────────────────────────────────────────
203
-
204
- describe('checkCodexHooksTrust', () => {
205
- test('missing → trusted after seed → drift after edit without re-seed', () => {
206
- const { env } = isolatedEnv()
207
- const proj = mkTmp()
208
- const hooksPath = writeHooksJson(proj, {
209
- hooks: { PostToolUse: [{ hooks: [{ type: 'command', command: 'stamp' }] }] },
210
- })
211
- expect(checkCodexHooksTrust(hooksPath, env).checks[0]!.status).toBe('missing')
212
- preSeedCodexHooksTrust(hooksPath, env)
213
- expect(checkCodexHooksTrust(hooksPath, env).checks[0]!.status).toBe('trusted')
214
- writeHooksJson(proj, { hooks: { PostToolUse: [{ hooks: [{ type: 'command', command: 'edited' }] }] } })
215
- const drifted = checkCodexHooksTrust(hooksPath, env).checks[0]!
216
- expect(drifted.status).toBe('drift')
217
- expect(drifted.found).toBeDefined()
218
- })
219
- })
220
-
221
- // ─── remove (reap-side cleanup) ──────────────────────────────────────────────
222
-
223
- describe('removeCodexHooksTrustUnder', () => {
224
- test('drops sections under the cwd, keeps everything else', () => {
225
- const { env, configPath } = isolatedEnv()
226
- const peerCwd = mkTmp()
227
- const hooksPath = writeHooksJson(peerCwd, {
228
- hooks: { SessionStart: [{ hooks: [{ type: 'command', command: 'x' }] }] },
229
- })
230
- preSeedCodexHooksTrust(hooksPath, env)
231
- // a foreign hooks.state entry from ANOTHER peer must survive
232
- const foreign = '[hooks.state."/other/peer/hooks.json:session_start:0:0"]\ntrusted_hash = "sha256:keep"\n'
233
- writeFileSync(configPath, readFileSync(configPath, 'utf8') + foreign)
234
- const o = removeCodexHooksTrustUnder(peerCwd, env)
235
- expect(o.state).toBe('written')
236
- expect(o.removed).toHaveLength(1)
237
- const text = readFileSync(configPath, 'utf8')
238
- expect(text).not.toContain(realpathSync(hooksPath))
239
- expect(text).toContain('/other/peer/hooks.json')
240
- // idempotent
241
- expect(removeCodexHooksTrustUnder(peerCwd, env).state).toBe('already')
242
- })
243
-
244
- test('no config / no matches → already', () => {
245
- const { env } = isolatedEnv()
246
- expect(removeCodexHooksTrustUnder('/nope', env).state).toBe('already')
247
- })
248
- })