@agfpd/iapeer 0.2.40 → 0.2.41
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/package.json +1 -1
- package/src/init/index.ts +38 -0
- package/src/init/init.test.ts +42 -0
package/package.json
CHANGED
package/src/init/index.ts
CHANGED
|
@@ -172,6 +172,41 @@ export function writeCodexMcpConfig(daemonUrl: string, options: StorageOptions =
|
|
|
172
172
|
return { path, added: true }
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Idempotently ensure codex's startup update-check DIALOG is disabled host-wide:
|
|
177
|
+
* `check_for_update_on_startup = false` as a TOP-LEVEL key in config.toml.
|
|
178
|
+
*
|
|
179
|
+
* Why (incident 11.06, codex-linus): the interactive "✨ Update available! …
|
|
180
|
+
* Press enter to continue" dialog is a BOOT GATE — a daemon-woken codex session
|
|
181
|
+
* sits on it until a human attaches. The wake reported never-became-ready after
|
|
182
|
+
* 4+ minutes and a notifier alert of the EMERGENCY contour (mining farm) was
|
|
183
|
+
* lost; the session came alive only from a manual attach. Warm-on-demand peers
|
|
184
|
+
* must reach ready hands-free — codex updates stay operator-driven (`codex
|
|
185
|
+
* update` / brew), never a boot-time question to a headless session.
|
|
186
|
+
*
|
|
187
|
+
* Mechanics: the key is a documented top-level config.toml option (present in
|
|
188
|
+
* the binary's own config schema; suppression PROVEN LIVE 11.06 on codex 0.138
|
|
189
|
+
* with the 0.139 update pending — control boot showed the dialog, the keyed
|
|
190
|
+
* boot went straight past it). A TOML top-level key must precede the first
|
|
191
|
+
* table header, so the key is PREPENDED — appending would land it inside the
|
|
192
|
+
* last [section]. An EXISTING `check_for_update_on_startup` of either value is
|
|
193
|
+
* respected (never override an explicit operator choice).
|
|
194
|
+
*/
|
|
195
|
+
export function ensureCodexUpdateCheckDisabled(
|
|
196
|
+
options: StorageOptions = {},
|
|
197
|
+
): { path: string; changed: boolean } {
|
|
198
|
+
const path = codexConfigPath(options)
|
|
199
|
+
let existing = ''
|
|
200
|
+
try {
|
|
201
|
+
existing = readFileSync(path, 'utf8')
|
|
202
|
+
} catch {
|
|
203
|
+
/* no config yet → create it */
|
|
204
|
+
}
|
|
205
|
+
if (/^\s*check_for_update_on_startup\s*=/m.test(existing)) return { path, changed: false }
|
|
206
|
+
writeFileAtomic(path, `check_for_update_on_startup = false\n${existing}`, 0o644)
|
|
207
|
+
return { path, changed: true }
|
|
208
|
+
}
|
|
209
|
+
|
|
175
210
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
176
211
|
// Local doctrine template (<cwd>/.iapeer/IAPEER.md — personality/role; human fills)
|
|
177
212
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -304,6 +339,9 @@ export async function initPeer(opts: InitPeerOptions): Promise<InitPeerResult> {
|
|
|
304
339
|
let codexMcpConfigPath: string | undefined
|
|
305
340
|
if (provisioned.runtime === 'codex' || hasCodexMarker(provisioned.cwd)) {
|
|
306
341
|
codexMcpConfigPath = writeCodexMcpConfig(daemonUrl, { env }).path
|
|
342
|
+
// Boot-gate hygiene (incident 11.06): a codex peer must never sit on the
|
|
343
|
+
// interactive update dialog at wake — disable the startup check host-wide.
|
|
344
|
+
ensureCodexUpdateCheckDisabled({ env })
|
|
307
345
|
}
|
|
308
346
|
|
|
309
347
|
const doctrine = ensureDoctrineTemplate(provisioned.cwd)
|
package/src/init/init.test.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
CODEX_BEARER_ENV_VAR,
|
|
10
10
|
IAPEER_MCP_SERVER_NAME,
|
|
11
11
|
codexConfigPath,
|
|
12
|
+
ensureCodexUpdateCheckDisabled,
|
|
12
13
|
ensureDoctrineTemplate,
|
|
13
14
|
initPeer,
|
|
14
15
|
resolveDaemonMcpUrl,
|
|
@@ -169,3 +170,44 @@ describe('initPeer orchestration', () => {
|
|
|
169
170
|
expect(readFileSync(join(cwd, '.iapeer', 'IAPEER.md'), 'utf8')).toBe('custom doctrine')
|
|
170
171
|
})
|
|
171
172
|
})
|
|
173
|
+
|
|
174
|
+
describe('ensureCodexUpdateCheckDisabled (boot-gate hygiene — incident 11.06 codex-linus)', () => {
|
|
175
|
+
test('PREPENDS the top-level key BEFORE the first [section] (TOML top-level scope)', () => {
|
|
176
|
+
const codexHome = mkdtempSync(join(tmpdir(), 'iapeer-codexhome3-'))
|
|
177
|
+
try {
|
|
178
|
+
const env: NodeJS.ProcessEnv = { ...process.env, CODEX_HOME: codexHome }
|
|
179
|
+
writeFileSync(codexConfigPath({ env }), 'model = "gpt-5.5"\n\n[projects."/x"]\ntrust_level = "trusted"\n')
|
|
180
|
+
const { changed } = ensureCodexUpdateCheckDisabled({ env })
|
|
181
|
+
expect(changed).toBe(true)
|
|
182
|
+
const toml = readFileSync(codexConfigPath({ env }), 'utf8')
|
|
183
|
+
expect(toml.startsWith('check_for_update_on_startup = false\n')).toBe(true)
|
|
184
|
+
expect(toml).toContain('[projects."/x"]') // existing config preserved
|
|
185
|
+
// idempotent: second run is a no-op
|
|
186
|
+
expect(ensureCodexUpdateCheckDisabled({ env }).changed).toBe(false)
|
|
187
|
+
expect((toml.match(/check_for_update_on_startup/g) ?? []).length).toBe(1)
|
|
188
|
+
} finally {
|
|
189
|
+
rmSync(codexHome, { recursive: true, force: true })
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
test('respects an EXPLICIT existing value (never overrides the operator)', () => {
|
|
193
|
+
const codexHome = mkdtempSync(join(tmpdir(), 'iapeer-codexhome4-'))
|
|
194
|
+
try {
|
|
195
|
+
const env: NodeJS.ProcessEnv = { ...process.env, CODEX_HOME: codexHome }
|
|
196
|
+
writeFileSync(codexConfigPath({ env }), 'check_for_update_on_startup = true\n')
|
|
197
|
+
expect(ensureCodexUpdateCheckDisabled({ env }).changed).toBe(false)
|
|
198
|
+
expect(readFileSync(codexConfigPath({ env }), 'utf8')).toContain('check_for_update_on_startup = true')
|
|
199
|
+
} finally {
|
|
200
|
+
rmSync(codexHome, { recursive: true, force: true })
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
test('absent config → created with just the key', () => {
|
|
204
|
+
const codexHome = mkdtempSync(join(tmpdir(), 'iapeer-codexhome5-'))
|
|
205
|
+
try {
|
|
206
|
+
const env: NodeJS.ProcessEnv = { ...process.env, CODEX_HOME: codexHome }
|
|
207
|
+
expect(ensureCodexUpdateCheckDisabled({ env }).changed).toBe(true)
|
|
208
|
+
expect(readFileSync(codexConfigPath({ env }), 'utf8')).toBe('check_for_update_on_startup = false\n')
|
|
209
|
+
} finally {
|
|
210
|
+
rmSync(codexHome, { recursive: true, force: true })
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
})
|