@spexcode/spec-cli 0.7.0-next.6 → 0.7.0-next.7
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/dist/harness.d.ts +1 -0
- package/dist/harness.js +25 -2
- package/dist/materialize.js +54 -1
- package/package.json +7 -7
package/dist/harness.d.ts
CHANGED
package/dist/harness.js
CHANGED
|
@@ -18,7 +18,8 @@ import { runtimeRoot, mainCheckout, readConfig, sessionArtifactPath, spexcodeHom
|
|
|
18
18
|
import { git } from '@spexcode/spec-core';
|
|
19
19
|
import { shQuote } from './sh.js';
|
|
20
20
|
import { detachedRuntimeGenerationToken, migrateLegacyDetachedRuntimeReceipt, processStartToken, verifyDetachedRuntime } from '@spexcode/spec-core';
|
|
21
|
-
import { codexGenerationEndpoints, codexGenerationSocketPath, currentCodexGeneration, legacyCodexGenerationEndpoint, readCodexGenerationLedger, prepareCodexGenerationClose, resolveCodexGenerationForClose, resolveCodexGenerationForSession } from './codex-runtime-generations.js';
|
|
21
|
+
import { codexGenerationEndpoints, codexGenerationSocketPath, currentCodexGeneration, legacyCodexGenerationEndpoint, readCodexGenerationLedger, prepareCodexGenerationClose, resolveCodexGenerationForClose, resolveCodexGenerationForResume, resolveCodexGenerationForSession } from './codex-runtime-generations.js';
|
|
22
|
+
import { spawnDetachedRuntime } from './runtime-ownership.js';
|
|
22
23
|
import { writeFileIfChanged } from './file-write.js';
|
|
23
24
|
import { claudeTranscript, codexRolloutPath, codexTranscript, opencodeTranscript, piTranscript, unsupportedTranscript } from '@spexcode/transcript';
|
|
24
25
|
import { harnessIdentity, HARNESS_IDENTITIES } from '@spexcode/spec-core';
|
|
@@ -2282,7 +2283,21 @@ async function deliverViaCodexAppServer(rec, text) {
|
|
|
2282
2283
|
// the socket is PER-PROJECT (the runtime root), shared by every worktree's thread; the owned thread id on
|
|
2283
2284
|
// the record picks out THIS session's thread.
|
|
2284
2285
|
const runtimeDir = rec.runtimeDir ?? runtimeRoot();
|
|
2285
|
-
|
|
2286
|
+
let endpoint = rec.harnessSessionId ? codexEndpointForRecord(rec, runtimeDir) : currentCodexGeneration(runtimeDir);
|
|
2287
|
+
// A generation may be reclaimed after rotation or a host restart while the session record remains valid.
|
|
2288
|
+
// Repair that stale route at the delivery boundary using the same exact-thread re-pin used by resume, so an
|
|
2289
|
+
// accepted message is not left indefinitely in the queue just because no later human resume was requested.
|
|
2290
|
+
if ((!endpoint || !existsSync(endpoint.socketPath)) && rec.harnessSessionId) {
|
|
2291
|
+
const command = codexBaseCmd(rec.launchCmd || 'codex');
|
|
2292
|
+
const env = { ...process.env };
|
|
2293
|
+
for (const key of sessionIdentityEnvVars())
|
|
2294
|
+
delete env[key];
|
|
2295
|
+
const start = async (candidate) => {
|
|
2296
|
+
await spawnDetachedRuntime({ cwd: runtimeDir, logFile: candidate.logFile, pidFile: candidate.pidFile,
|
|
2297
|
+
receiptFile: candidate.receiptFile, command, args: ['app-server', '--listen', `unix://${candidate.socketPath}`], env });
|
|
2298
|
+
};
|
|
2299
|
+
endpoint = await resolveCodexGenerationForResume(runtimeDir, rec.session, rec.harnessSessionId, start);
|
|
2300
|
+
}
|
|
2286
2301
|
if (!endpoint)
|
|
2287
2302
|
return { ok: false, error: `no exact Codex generation binding for session ${rec.session} — immediate poke unavailable` };
|
|
2288
2303
|
const sock = endpoint.socketPath;
|
|
@@ -2297,6 +2312,14 @@ async function deliverViaCodexAppServer(rec, text) {
|
|
|
2297
2312
|
return { ok: false, error: `${r.error} — immediate poke unavailable` };
|
|
2298
2313
|
threadId = r.threadId;
|
|
2299
2314
|
}
|
|
2315
|
+
const delivered = await sendCodexAppServerTurn(sock, threadId, text, rec.worktreePath, rec.mid);
|
|
2316
|
+
if (delivered.ok || rec.harness !== 'codex-headless' || !/not loaded in the app-server/u.test(delivered.error || ''))
|
|
2317
|
+
return delivered;
|
|
2318
|
+
// Headless Codex has no TUI resume step. An idle thread can be evicted from the shared server's loaded set;
|
|
2319
|
+
// reload the exact rollout, then retry the same turn once. This is idempotent and does not create a new thread.
|
|
2320
|
+
const reopened = await codexReopenThread(sock, threadId);
|
|
2321
|
+
if (!reopened.ok)
|
|
2322
|
+
return { ok: false, error: `${delivered.error}; ${reopened.error}` };
|
|
2300
2323
|
return sendCodexAppServerTurn(sock, threadId, text, rec.worktreePath, rec.mid);
|
|
2301
2324
|
}
|
|
2302
2325
|
// idempotent replace of the content between sentinels; the user's own content above/below is preserved. The
|
package/dist/materialize.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFileSync, mkdirSync, readFileSync, existsSync, readdirSync, renameSync, rmSync, rmdirSync } from 'node:fs';
|
|
1
|
+
import { writeFileSync, mkdirSync, readFileSync, existsSync, readdirSync, renameSync, rmSync, rmdirSync, copyFileSync, chmodSync } from 'node:fs';
|
|
2
2
|
import { join, dirname, relative } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
@@ -37,6 +37,58 @@ const DISPATCH = join(PKG, 'hooks', 'dispatch.sh');
|
|
|
37
37
|
// CLI code and keeps the source-workspace mid-merge guard (one line + exit 75), so every hook callback
|
|
38
38
|
// inherits both.
|
|
39
39
|
const SPEX = join(PKG, 'bin', 'spex.mjs');
|
|
40
|
+
const CORE_TEMPLATE = join(PKG, 'templates', 'spec', 'project', '.plugins', 'core');
|
|
41
|
+
// Core hook handlers are shipped executable protocol, not adopter-owned plugin variants. Reconcile only the
|
|
42
|
+
// known `core/` subtree before compiling the manifest so a project seeded by an older toolchain cannot keep
|
|
43
|
+
// invoking a retired lifecycle writer. User plugins live outside this allowlist and are never enumerated.
|
|
44
|
+
function refreshCorePluginHandlers(proj) {
|
|
45
|
+
if (!existsSync(CORE_TEMPLATE))
|
|
46
|
+
return [];
|
|
47
|
+
const specDir = join(proj, '.spec');
|
|
48
|
+
const roots = existsSync(specDir)
|
|
49
|
+
? readdirSync(specDir, { withFileTypes: true })
|
|
50
|
+
.filter((entry) => entry.isDirectory() && existsSync(join(specDir, entry.name, '.plugins', 'core')))
|
|
51
|
+
.map((entry) => join(specDir, entry.name, '.plugins', 'core'))
|
|
52
|
+
: [];
|
|
53
|
+
const handlers = [];
|
|
54
|
+
const walk = (dir, prefix = '') => {
|
|
55
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
56
|
+
const rel = join(prefix, entry.name);
|
|
57
|
+
if (entry.isDirectory())
|
|
58
|
+
walk(join(dir, entry.name), rel);
|
|
59
|
+
else if (entry.isFile() && entry.name.endsWith('.sh'))
|
|
60
|
+
handlers.push(rel);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
walk(CORE_TEMPLATE);
|
|
64
|
+
const refreshed = [];
|
|
65
|
+
for (const root of roots)
|
|
66
|
+
for (const rel of handlers) {
|
|
67
|
+
const source = join(CORE_TEMPLATE, rel);
|
|
68
|
+
const dest = join(root, rel);
|
|
69
|
+
let current = null;
|
|
70
|
+
try {
|
|
71
|
+
current = readFileSync(dest);
|
|
72
|
+
}
|
|
73
|
+
catch { }
|
|
74
|
+
if (current?.equals(readFileSync(source)))
|
|
75
|
+
continue;
|
|
76
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
77
|
+
const temp = `${dest}.spexcode-${process.pid}`;
|
|
78
|
+
try {
|
|
79
|
+
copyFileSync(source, temp);
|
|
80
|
+
chmodSync(temp, 0o755);
|
|
81
|
+
renameSync(temp, dest);
|
|
82
|
+
refreshed.push(relative(proj, dest));
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
rmSync(temp, { force: true });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (refreshed.length)
|
|
89
|
+
console.log(`✓ refreshed core plugin handlers (${refreshed.join(', ')})`);
|
|
90
|
+
return refreshed;
|
|
91
|
+
}
|
|
40
92
|
// the manifest + content-hash marker + plugin-folder ledger land in the materialized TREE's own slot of the
|
|
41
93
|
// GLOBAL per-project store (layout.treeSlotDir — trees/<enc-worktree>), NOT the worktree and NOT one shared
|
|
42
94
|
// per-project file: each is a pure function of ONE tree's .plugins, and the old single slot let the last-
|
|
@@ -321,6 +373,7 @@ export function dematerialize(proj = process.cwd(), arts = { skills: [], agents:
|
|
|
321
373
|
// the whole pay-per-change materialize. proj defaults to cwd. Its receipt is populated at each successful
|
|
322
374
|
// write so callers report the actual selected footprint instead of maintaining a second artifact inventory.
|
|
323
375
|
export function materialize(proj = process.cwd()) {
|
|
376
|
+
refreshCorePluginHandlers(proj);
|
|
324
377
|
const rt = treeSlotDir(proj); // this tree's slot in the global store, not the worktree
|
|
325
378
|
mkdirSync(rt, { recursive: true });
|
|
326
379
|
const planted = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/spec-cli",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SpexCode CLI + server. The root spexcode package delegates to this compiled package; dashboard assets live in @spexcode/spec-dashboard.",
|
|
6
6
|
"bin": {
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"test": "tsx --import ../scripts/test-home.mjs --test src/*.test.ts"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@spexcode/session-application": "0.7.0-next.
|
|
39
|
-
"@spexcode/session-selflaunch": "0.7.0-next.
|
|
40
|
-
"@spexcode/spec-core": "0.7.0-next.
|
|
41
|
-
"@spexcode/spec-eval": "0.7.0-next.
|
|
42
|
-
"@spexcode/spec-forge": "0.7.0-next.
|
|
43
|
-
"@spexcode/transcript": "0.7.0-next.
|
|
38
|
+
"@spexcode/session-application": "0.7.0-next.7",
|
|
39
|
+
"@spexcode/session-selflaunch": "0.7.0-next.7",
|
|
40
|
+
"@spexcode/spec-core": "0.7.0-next.7",
|
|
41
|
+
"@spexcode/spec-eval": "0.7.0-next.7",
|
|
42
|
+
"@spexcode/spec-forge": "0.7.0-next.7",
|
|
43
|
+
"@spexcode/transcript": "0.7.0-next.7",
|
|
44
44
|
"smol-toml": "^1.8.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|