@agfpd/iapeer 0.2.76 → 0.2.78

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 +47 -48
  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 +20 -13
  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 +14 -8
  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 -213
  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,288 +0,0 @@
1
- // provisionPeer — one-call peer creation: profile + registry (+ infra plist with a
2
- // PINNED launcher). All registry/plist writes go under IAPEER_ROOT /
3
- // IAPEER_LAUNCHAGENTS_DIR temp dirs so the suite never touches the live fleet.
4
-
5
- import { afterEach, describe, expect, test } from 'bun:test'
6
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs'
7
- import { tmpdir } from 'os'
8
- import { join } from 'path'
9
- import { provisionPeer } from './index.ts'
10
- import { peerProfilePath } from '../storage/index.ts'
11
- import { readPeerProfile } from '../identity/index.ts'
12
- import { findPeer, readPeersIndex } from '../registry/index.ts'
13
- import { isFoundationOwnedPlist, launchdPlistPath } from '../launch/index.ts'
14
-
15
- const roots: string[] = []
16
- function mkTmp(): string {
17
- const d = mkdtempSync(join(tmpdir(), 'iapeer-prov-'))
18
- roots.push(d)
19
- return d
20
- }
21
- afterEach(() => {
22
- while (roots.length) rmSync(roots.pop()!, { recursive: true, force: true })
23
- })
24
-
25
- function fakeBinDir(name = 'notifier-runtime'): { dir: string; bin: string } {
26
- const dir = mkTmp()
27
- const bin = join(dir, name)
28
- writeFileSync(bin, '#!/bin/sh\nexit 0\n', { mode: 0o755 })
29
- return { dir, bin }
30
- }
31
- function envFor(root: string, path?: string): NodeJS.ProcessEnv {
32
- return {
33
- IAPEER_ROOT: join(root, 'iapeer'),
34
- IAPEER_LAUNCHAGENTS_DIR: join(root, 'LA'),
35
- HOME: root,
36
- ...(path ? { PATH: path } : {}),
37
- } as NodeJS.ProcessEnv
38
- }
39
-
40
- describe('provisionPeer', () => {
41
- test('infra (notifier): writes profile + registry + plist with a pinned abs launcher', async () => {
42
- const root = mkTmp()
43
- const { dir: bindir, bin } = fakeBinDir()
44
- const env = envFor(root, bindir)
45
- const cwd = join(root, 'timer') // basename → personality 'timer'
46
-
47
- const r = await provisionPeer({ cwd, runtime: 'notifier', env })
48
-
49
- expect(r.personality).toBe('timer')
50
- expect(r.intelligence).toBe('absent') // notifier zone default
51
- expect(r.runtimeBin).toBe(bin)
52
- // local profile
53
- expect(existsSync(peerProfilePath(cwd))).toBe(true)
54
- expect(readPeerProfile(cwd)!.runtime).toBe('notifier')
55
- // registry entry (daemon reads this for tool-list / findPeer / wake)
56
- expect(findPeer(readPeersIndex({ env }), 'timer')?.cwd).toBe(cwd)
57
- // plist pins the launcher to an abs path (launchd minimal PATH safe)
58
- const plist = launchdPlistPath('timer', env)
59
- expect(existsSync(plist)).toBe(true)
60
- expect(isFoundationOwnedPlist(plist)).toBe(true)
61
- const xml = readFileSync(plist, 'utf8')
62
- expect(xml).toContain('<key>NOTIFIER_RUNTIME_BIN</key>')
63
- expect(xml).toContain(`<string>${bin}</string>`)
64
- })
65
-
66
- test('infra with unresolvable launcher → REFUSED (no profile, no registry, no plist)', async () => {
67
- const root = mkTmp()
68
- const env = envFor(root, join(root, 'empty')) // PATH dir without notifier-runtime
69
- const cwd = join(root, 'timer')
70
-
71
- await expect(provisionPeer({ cwd, runtime: 'notifier', env })).rejects.toThrow(/not found on PATH|launcher/i)
72
-
73
- expect(existsSync(peerProfilePath(cwd))).toBe(false)
74
- expect(findPeer(readPeersIndex({ env }), 'timer')).toBeNull()
75
- expect(existsSync(launchdPlistPath('timer', env))).toBe(false)
76
- })
77
-
78
- test('warm-on-demand (claude): profile + registry, NO plist (no launcher needed)', async () => {
79
- const root = mkTmp()
80
- const env = envFor(root)
81
- const cwd = join(root, 'worker')
82
-
83
- const r = await provisionPeer({ cwd, runtime: 'claude', env })
84
-
85
- expect(r.plistPath).toBeUndefined()
86
- expect(r.runtimeBin).toBeUndefined()
87
- expect(existsSync(peerProfilePath(cwd))).toBe(true)
88
- expect(findPeer(readPeersIndex({ env }), 'worker')?.runtime).toBe('claude')
89
- expect(existsSync(launchdPlistPath('worker', env))).toBe(false)
90
- })
91
-
92
- test('explicit personality override (not basename) provisions under that name', async () => {
93
- const root = mkTmp()
94
- const { dir: bindir } = fakeBinDir()
95
- const env = envFor(root, bindir)
96
- const cwd = join(root, 'some-dir')
97
-
98
- const r = await provisionPeer({ cwd, runtime: 'notifier', personality: 'timer', env })
99
-
100
- expect(r.personality).toBe('timer')
101
- expect(existsSync(launchdPlistPath('timer', env))).toBe(true)
102
- expect(findPeer(readPeersIndex({ env }), 'timer')?.cwd).toBe(cwd)
103
- })
104
- })
105
-
106
- // ─────────────────────────────────────────────────────────────────────────────
107
- // wake_policy:"ephemeral" provision warnings (M2 edge cases — warn, not refuse)
108
- // ─────────────────────────────────────────────────────────────────────────────
109
-
110
- describe('provisionPeer ephemeral warnings', () => {
111
- test('ephemeral + interfaces.telegram → WARN (ephemeral wins; human dialogue should not die-after-reply)', async () => {
112
- const root = mkTmp()
113
- const env = envFor(root)
114
- const cwd = join(root, 'wtg')
115
- // pre-existing profile (provision returns it unchanged) carrying the bad combo
116
- mkdirSync(join(cwd, '.iapeer'), { recursive: true })
117
- writeFileSync(
118
- peerProfilePath(cwd),
119
- JSON.stringify({
120
- personality: 'wtg',
121
- runtime: 'claude',
122
- intelligence: 'natural',
123
- interfaces: { telegram: { user_id: 1 } },
124
- wake_policy: 'ephemeral',
125
- }),
126
- )
127
- const warns: string[] = []
128
- await provisionPeer({ cwd, runtime: 'claude', env, warn: m => warns.push(m) })
129
- expect(warns.some(w => /ephemeral/.test(w) && /telegram/.test(w))).toBe(true)
130
- })
131
-
132
- test('plain ephemeral worker (claude, no telegram) → NO ephemeral warn', async () => {
133
- const root = mkTmp()
134
- const env = envFor(root)
135
- const cwd = join(root, 'weph')
136
- mkdirSync(join(cwd, '.iapeer'), { recursive: true })
137
- writeFileSync(
138
- peerProfilePath(cwd),
139
- JSON.stringify({ personality: 'weph', runtime: 'claude', wake_policy: 'ephemeral' }),
140
- )
141
- const warns: string[] = []
142
- await provisionPeer({ cwd, runtime: 'claude', env, warn: m => warns.push(m) })
143
- expect(warns.filter(w => /ephemeral/.test(w))).toEqual([])
144
- })
145
-
146
- test('ephemeral + always-on infra runtime → WARN (H4: launchd owns it, the policy is inert)', async () => {
147
- const root = mkTmp()
148
- const { dir: bindir } = fakeBinDir()
149
- const env = envFor(root, bindir)
150
- const cwd = join(root, 'winf')
151
- mkdirSync(join(cwd, '.iapeer'), { recursive: true })
152
- writeFileSync(
153
- peerProfilePath(cwd),
154
- JSON.stringify({ personality: 'winf', runtime: 'notifier', wake_policy: 'ephemeral' }),
155
- )
156
- const warns: string[] = []
157
- await provisionPeer({ cwd, runtime: 'notifier', env, warn: m => warns.push(m) })
158
- expect(warns.some(w => /ephemeral/.test(w) && /inert|launchd/i.test(w))).toBe(true)
159
- })
160
- })
161
-
162
- // ─────────────────────────────────────────────────────────────────────────────
163
- // Birth-time native-memory lever (slot contract §Native-память): an OCCUPIED
164
- // slot gates the automatic lever at provision; empty slot → untouched.
165
- // ─────────────────────────────────────────────────────────────────────────────
166
-
167
- describe('provisionPeer birth-time native-memory lever', () => {
168
- test('slot OCCUPIED → claude+codex levers written + codex cwd pre-trusted', async () => {
169
- const root = mkTmp()
170
- const env = envFor(root)
171
- // occupy the slot (as the provider's init would)
172
- mkdirSync(join(root, 'iapeer'), { recursive: true })
173
- writeFileSync(
174
- join(root, 'iapeer', 'memory-provider.json'),
175
- JSON.stringify({ provider: 'iapeer-memory', package: '@agfpd/iapeer-memory', version: '0.1.0', registeredAt: 'x' }),
176
- )
177
- const cwd = join(root, 'wnat')
178
- mkdirSync(join(cwd, '.iapeer'), { recursive: true })
179
- writeFileSync(
180
- peerProfilePath(cwd),
181
- JSON.stringify({ personality: 'wnat', runtime: 'claude', runtimes: ['claude', 'codex'] }),
182
- )
183
- await provisionPeer({ cwd, runtime: 'claude', env })
184
- expect(JSON.parse(readFileSync(join(cwd, '.claude', 'settings.json'), 'utf8')).autoMemoryEnabled).toBe(false)
185
- expect(readFileSync(join(cwd, '.codex', 'config.toml'), 'utf8')).toContain('memories = false')
186
- // codex newborn pre-trusted in the GLOBAL codex config (HOME-scoped → temp root).
187
- // Codex keys trust on the RESOLVED real path (macOS /var → /private/var), so the
188
- // entry must carry realpath(cwd) — the literal-path miss was a live-caught bug.
189
- const { realpathSync } = await import('fs')
190
- expect(readFileSync(join(root, '.codex', 'config.toml'), 'utf8')).toContain(`[projects."${realpathSync(cwd)}"]`)
191
- })
192
-
193
- test('slot EMPTY → native memory untouched (legitimate without a provider)', async () => {
194
- const root = mkTmp()
195
- const env = envFor(root)
196
- const cwd = join(root, 'wbare')
197
- await provisionPeer({ cwd, runtime: 'claude', env })
198
- expect(existsSync(join(cwd, '.claude', 'settings.json'))).toBe(false)
199
- expect(existsSync(join(cwd, '.codex', 'config.toml'))).toBe(false)
200
- })
201
-
202
- test('LEGACY v1.1 plugin block in the declaration → ignored entirely (form removed 11.06, ADR-017); native lever still applies', async () => {
203
- const root = mkTmp()
204
- const env = envFor(root)
205
- mkdirSync(join(root, 'iapeer'), { recursive: true })
206
- writeFileSync(
207
- join(root, 'iapeer', 'memory-provider.json'),
208
- JSON.stringify({
209
- provider: 'iapeer-memory',
210
- package: '@agfpd/iapeer-memory',
211
- version: '0.1.0',
212
- registeredAt: 'x',
213
- // a stale v1.1 declaration on a legacy host — the core no longer parses it
214
- plugin: { name: 'iapeer-memory', marketplace: 'agfpd', marketplaceRef: 'agfpd/agfpd-marketplace' },
215
- }),
216
- )
217
- const cwd = join(root, 'wplug')
218
- const warns: string[] = []
219
- const r = await provisionPeer({ cwd, runtime: 'claude', env, warn: m => warns.push(m) })
220
- expect(r.personality).toBe('wplug')
221
- expect(warns).toEqual([]) // no install attempt, no warn — the block is unknown noise
222
- // the native lever still applied (same slot-gated block)
223
- expect(JSON.parse(readFileSync(join(cwd, '.claude', 'settings.json'), 'utf8')).autoMemoryEnabled).toBe(false)
224
- })
225
-
226
- test('v1.2: slot with provision command → birth shells into it per runtime (occasion=birth); a legacy plugin block alongside is inert', async () => {
227
- const root = mkTmp()
228
- const env = envFor(root)
229
- mkdirSync(join(root, 'iapeer'), { recursive: true })
230
- const journal = join(root, 'journal.txt')
231
- const script = join(root, 'fake-provider.sh')
232
- const { chmodSync } = await import('fs')
233
- writeFileSync(script, `#!/bin/sh\nprintf '%s\\n' "$@" >> '${journal}'\n`)
234
- chmodSync(script, 0o755)
235
- writeFileSync(
236
- join(root, 'iapeer', 'memory-provider.json'),
237
- JSON.stringify({
238
- provider: 'iapeer-memory',
239
- package: '@agfpd/iapeer-memory',
240
- version: '0.2.0',
241
- registeredAt: 'x',
242
- // legacy plugin block alongside (stale declaration) — must be inert
243
- plugin: { name: 'iapeer-memory', marketplace: 'agfpd', marketplaceRef: 'agfpd/agfpd-marketplace' },
244
- provision: {
245
- command: script,
246
- args: ['provision-peer', '--cwd', '{cwd}', '--runtime', '{runtime}', '--personality', '{personality}', '--occasion', '{occasion}'],
247
- },
248
- }),
249
- )
250
- const cwd = join(root, 'wprov')
251
- const warns: string[] = []
252
- const r = await provisionPeer({ cwd, runtime: 'claude', env, warn: m => warns.push(m) })
253
- expect(r.personality).toBe('wprov')
254
- expect(warns).toEqual([]) // provision ok → no warn at all
255
- const j = readFileSync(journal, 'utf8').split('\n')
256
- expect(j[0]).toBe('provision-peer')
257
- expect(j.slice(1, 9)).toEqual(['--cwd', cwd, '--runtime', 'claude', '--personality', 'wprov', '--occasion', 'birth'])
258
- })
259
-
260
- test('v1.2: provision command FAILS → LOUD warn (provider-side repair), provision of the peer still SUCCEEDS (best-effort)', async () => {
261
- const root = mkTmp()
262
- const env = envFor(root)
263
- mkdirSync(join(root, 'iapeer'), { recursive: true })
264
- const script = join(root, 'fake-provider.sh')
265
- const { chmodSync } = await import('fs')
266
- writeFileSync(script, `#!/bin/sh\necho 'vault offline' >&2\nexit 9\n`)
267
- chmodSync(script, 0o755)
268
- writeFileSync(
269
- join(root, 'iapeer', 'memory-provider.json'),
270
- JSON.stringify({
271
- provider: 'iapeer-memory',
272
- package: '@agfpd/iapeer-memory',
273
- version: '0.2.0',
274
- registeredAt: 'x',
275
- provision: { command: script, args: ['{occasion}'] },
276
- }),
277
- )
278
- const cwd = join(root, 'wfail')
279
- const warns: string[] = []
280
- const r = await provisionPeer({ cwd, runtime: 'claude', env, warn: m => warns.push(m) })
281
- expect(r.personality).toBe('wfail') // birth completed despite the provider hiccup
282
- const w = warns.find(x => x.includes('memory provision'))
283
- expect(w).toBeDefined()
284
- expect(w).toContain('FAILED')
285
- expect(w).toContain('vault offline')
286
- expect(w).toContain("re-run the provider's provision") // provider-side repair, no core verb named
287
- })
288
- })