@pome-sh/cli 0.42.4 → 0.43.1
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/README.md +238 -117
- package/dist/build-info.json +3 -3
- package/dist/{checks-BKMTBJSO.js → checks-V442KL5O.js} +9 -14
- package/dist/{chunk-RPPF2KDQ.js → chunk-4DXTT3RV.js} +7 -7
- package/dist/{chunk-6KJC4BTO.js → chunk-5KFDRR53.js} +2 -2
- package/dist/{chunk-5NPGY73F.js → chunk-64AWQJ7R.js} +2 -2
- package/dist/{chunk-STMGQ7GU.js → chunk-A6AM6KS4.js} +1405 -172
- package/dist/{chunk-NNXVR46L.js → chunk-CS7O2ZXB.js} +114 -2
- package/dist/{chunk-VPXIGCZW.js → chunk-GQSNU3YP.js} +1 -1
- package/dist/{chunk-HRAD7MRX.js → chunk-TWURH7YM.js} +34 -10
- package/dist/{runTrialGroup-BP446HIE.js → runTrialGroup-HYGGBLMM.js} +20 -10
- package/dist/{server-GDK4TBTL.js → server-2WHSHTUD.js} +2 -2
- package/dist/src/cli/main.js +488 -226
- package/dist/{src-Z4C4SSDR.js → src-DBOHIQNB.js} +2 -2
- package/dist/{src-FUPOFZI4.js → src-FRPUTB2H.js} +2 -2
- package/dist/{src-UY4Y75BD.js → src-G6CMFUR6.js} +2 -2
- package/dist/{src-SNSK5ENY.js → src-SJDGRXC5.js} +2 -2
- package/dist/{src-V3RUKDMQ.js → src-VBXONWR3.js} +2 -2
- package/dist/twinHarness-33MAVAXV.js +6 -0
- package/dist/{twinSeed-X6VHBOLM.js → twinSeed-J7UAY3GM.js} +1 -1
- package/dist/{twinStart-QQU6OIO4.js → twinStart-3Q74IURZ.js} +21 -17
- package/package.json +1 -2
- package/dist/agent-Q2UTRJYL.js +0 -408
- package/dist/chunk-46POYRLY.js +0 -1240
- package/dist/chunk-DFOQGAKS.js +0 -116
- package/dist/chunk-JNXZBK3O.js +0 -17
- package/dist/chunk-WERG4AUH.js +0 -462
- package/dist/chunk-ZX4WNSZ5.js +0 -71
- package/dist/runDemo-DRDMCG25.js +0 -408
- package/dist/twinHarness-ATYQTDA5.js +0 -6
package/dist/chunk-DFOQGAKS.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { HostedOrchError, manifestSchema, deriveAgentSlug, SLUG_RE } from './chunk-NNXVR46L.js';
|
|
2
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { resolve, join, dirname } from 'node:path';
|
|
4
|
-
import { stringify, parse } from 'yaml';
|
|
5
|
-
|
|
6
|
-
var MANIFEST_JSON = "pome.json";
|
|
7
|
-
var MANIFEST_YAML = "pome.yaml";
|
|
8
|
-
var MANIFEST_YML = "pome.yml";
|
|
9
|
-
var MANIFEST_FILES = [MANIFEST_JSON, MANIFEST_YAML, MANIFEST_YML];
|
|
10
|
-
var SCHEMA_URL = "https://pome.sh/schemas/v1/pome.json";
|
|
11
|
-
var YAML_SCHEMA_COMMENT = `# yaml-language-server: $schema=${SCHEMA_URL}`;
|
|
12
|
-
function formatFor(fileName) {
|
|
13
|
-
return fileName === MANIFEST_JSON ? "json" : "yaml";
|
|
14
|
-
}
|
|
15
|
-
async function findManifestPath(startDir = process.cwd()) {
|
|
16
|
-
let dir = resolve(startDir);
|
|
17
|
-
for (; ; ) {
|
|
18
|
-
const present = [];
|
|
19
|
-
for (const fileName of MANIFEST_FILES) {
|
|
20
|
-
if (await fileExists(join(dir, fileName))) present.push(fileName);
|
|
21
|
-
}
|
|
22
|
-
if (present.length > 1) {
|
|
23
|
-
throw new HostedOrchError(
|
|
24
|
-
`Multiple pome manifests in ${dir}: ${present.join(", ")}. Keep exactly one (pome.json is canonical).`
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
if (present.length === 1) {
|
|
28
|
-
const fileName = present[0];
|
|
29
|
-
return { path: join(dir, fileName), format: formatFor(fileName) };
|
|
30
|
-
}
|
|
31
|
-
const parent = dirname(dir);
|
|
32
|
-
if (parent === dir) return null;
|
|
33
|
-
dir = parent;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
async function readManifest(startDir = process.cwd()) {
|
|
37
|
-
const found = await findManifestPath(startDir);
|
|
38
|
-
if (!found) return null;
|
|
39
|
-
const text = await readFile(found.path, "utf8");
|
|
40
|
-
const raw = parseManifestText(text, found);
|
|
41
|
-
const manifest = validateManifest(raw, found.path);
|
|
42
|
-
return { ...found, manifest, raw };
|
|
43
|
-
}
|
|
44
|
-
async function readRequiredManifest(startDir = process.cwd()) {
|
|
45
|
-
const read = await readManifest(startDir);
|
|
46
|
-
if (!read) {
|
|
47
|
-
throw new HostedOrchError(
|
|
48
|
-
`No pome manifest found (${MANIFEST_JSON} or ${MANIFEST_YAML}). Run \`pome init\` first.`
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
return read;
|
|
52
|
-
}
|
|
53
|
-
async function writeManifest(path, format, data) {
|
|
54
|
-
if (format === "json") {
|
|
55
|
-
await writeFile(path, `${JSON.stringify(data, null, 2)}
|
|
56
|
-
`);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const { $schema: _dropped, ...body } = data;
|
|
60
|
-
await writeFile(path, `${YAML_SCHEMA_COMMENT}
|
|
61
|
-
${stringify(body)}`);
|
|
62
|
-
}
|
|
63
|
-
function parseManifestText(text, found) {
|
|
64
|
-
let parsed;
|
|
65
|
-
try {
|
|
66
|
-
parsed = found.format === "json" ? JSON.parse(text) : parse(text);
|
|
67
|
-
} catch (err) {
|
|
68
|
-
throw new HostedOrchError(
|
|
69
|
-
`${found.path} is not valid ${found.format.toUpperCase()}: ${err instanceof Error ? err.message : String(err)}`
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
73
|
-
throw new HostedOrchError(`${found.path} is not a ${found.format} object`);
|
|
74
|
-
}
|
|
75
|
-
return parsed;
|
|
76
|
-
}
|
|
77
|
-
function validateManifest(raw, path) {
|
|
78
|
-
const result = manifestSchema.safeParse(raw);
|
|
79
|
-
if (result.success) return result.data;
|
|
80
|
-
const slugIssue = result.error.issues.find(
|
|
81
|
-
(issue) => issue.path[0] === "agent" && issue.path[1] === "slug"
|
|
82
|
-
);
|
|
83
|
-
if (slugIssue) {
|
|
84
|
-
throw new HostedOrchError(slugErrorMessage(raw, path));
|
|
85
|
-
}
|
|
86
|
-
const summary = result.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
|
|
87
|
-
throw new HostedOrchError(`Invalid pome manifest ${path}: ${summary}`);
|
|
88
|
-
}
|
|
89
|
-
function slugErrorMessage(raw, path) {
|
|
90
|
-
const agent = typeof raw.agent === "object" && raw.agent !== null ? raw.agent : {};
|
|
91
|
-
const name = typeof agent.name === "string" ? agent.name : "";
|
|
92
|
-
const badSlug = typeof agent.slug === "string" ? agent.slug : "";
|
|
93
|
-
const suggestion = deriveAgentSlug(name || badSlug);
|
|
94
|
-
const base = `Invalid agent.slug in ${path}: must match ${SLUG_RE} (lowercase kebab-case, max 64 chars).`;
|
|
95
|
-
return suggestion.length > 0 ? `${base} Did you mean "${suggestion}"?` : base;
|
|
96
|
-
}
|
|
97
|
-
function normalizeManifestTwins(manifestTwins) {
|
|
98
|
-
if (manifestTwins === void 0) return void 0;
|
|
99
|
-
const out = /* @__PURE__ */ new Set();
|
|
100
|
-
for (const twin of manifestTwins) {
|
|
101
|
-
const norm = twin.trim().toLowerCase();
|
|
102
|
-
if (norm.length > 0) out.add(norm);
|
|
103
|
-
}
|
|
104
|
-
return out.size > 0 ? [...out] : void 0;
|
|
105
|
-
}
|
|
106
|
-
async function fileExists(path) {
|
|
107
|
-
try {
|
|
108
|
-
await readFile(path, "utf8");
|
|
109
|
-
return true;
|
|
110
|
-
} catch (err) {
|
|
111
|
-
if (err.code === "ENOENT") return false;
|
|
112
|
-
throw err;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export { MANIFEST_JSON, findManifestPath, normalizeManifestTwins, readManifest, readRequiredManifest, writeManifest };
|
package/dist/chunk-JNXZBK3O.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { randomBytes } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
// src/demo/ids.ts
|
|
4
|
-
var NANOID_ALPHABET = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
5
|
-
function nanoid(size = 21) {
|
|
6
|
-
const bytes = randomBytes(size);
|
|
7
|
-
let id = "";
|
|
8
|
-
for (let i = 0; i < size; i += 1) {
|
|
9
|
-
id += NANOID_ALPHABET[bytes[i] & 63];
|
|
10
|
-
}
|
|
11
|
-
return id;
|
|
12
|
-
}
|
|
13
|
-
function newGroupId() {
|
|
14
|
-
return `grp_${nanoid(21)}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export { newGroupId };
|
package/dist/chunk-WERG4AUH.js
DELETED
|
@@ -1,462 +0,0 @@
|
|
|
1
|
-
import { parseTaskFile, seedStateForTwin, runAgentCommand, writeRunArtifactsCore } from './chunk-STMGQ7GU.js';
|
|
2
|
-
import { createRecorder, bootTwin } from './chunk-5NPGY73F.js';
|
|
3
|
-
import { getAvailablePort } from './chunk-XDU6TD4O.js';
|
|
4
|
-
import { buildEgressAllowlist, readBlockedEgress } from './chunk-CBFKZZBR.js';
|
|
5
|
-
import { eventSchema } from './chunk-6KJC4BTO.js';
|
|
6
|
-
import { redactSecrets, redactEvent } from './chunk-SG6ZTIMT.js';
|
|
7
|
-
import { serve } from '@hono/node-server';
|
|
8
|
-
import { randomUUID, randomBytes } from 'node:crypto';
|
|
9
|
-
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
|
10
|
-
import { join, sep, resolve, dirname } from 'node:path';
|
|
11
|
-
import { sign } from 'hono/jwt';
|
|
12
|
-
import { spawn } from 'node:child_process';
|
|
13
|
-
import { existsSync } from 'node:fs';
|
|
14
|
-
import { fileURLToPath } from 'node:url';
|
|
15
|
-
|
|
16
|
-
var LISTENING_REGEX = /listening on 127\.0\.0\.1:(\d+)/;
|
|
17
|
-
async function spawnCaptureServerChild(options) {
|
|
18
|
-
const execPath = options.execPath ?? process.execPath;
|
|
19
|
-
const binArgs = options.binArgs ?? defaultBinArgs(options.eventsOut, options.allowHosts, options.egressOut);
|
|
20
|
-
const readyTimeoutMs = options.readyTimeoutMs ?? 5e3;
|
|
21
|
-
const shutdownGraceMs = options.shutdownGraceMs ?? 5e3;
|
|
22
|
-
const child = spawn(execPath, binArgs, {
|
|
23
|
-
stdio: ["ignore", "ignore", "pipe"],
|
|
24
|
-
env: process.env
|
|
25
|
-
});
|
|
26
|
-
let stderrBuf = "";
|
|
27
|
-
let resolved = false;
|
|
28
|
-
const readyPromise = new Promise((resolve2, reject) => {
|
|
29
|
-
const onData = (chunk) => {
|
|
30
|
-
stderrBuf += chunk.toString("utf8");
|
|
31
|
-
if (stderrBuf.length > 8192) stderrBuf = stderrBuf.slice(-8192);
|
|
32
|
-
const match = LISTENING_REGEX.exec(stderrBuf);
|
|
33
|
-
if (match) {
|
|
34
|
-
resolved = true;
|
|
35
|
-
cleanup();
|
|
36
|
-
resolve2(Number(match[1]));
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
const onExit = (code, signal) => {
|
|
40
|
-
if (resolved) return;
|
|
41
|
-
cleanup();
|
|
42
|
-
const detail = stderrBuf.trim().slice(0, 2048);
|
|
43
|
-
reject(
|
|
44
|
-
new Error(
|
|
45
|
-
`pome capture-server exited (code=${code} signal=${signal}) before printing the listening line.${detail ? `
|
|
46
|
-
stderr: ${detail}` : ""}`
|
|
47
|
-
)
|
|
48
|
-
);
|
|
49
|
-
};
|
|
50
|
-
const timer = setTimeout(() => {
|
|
51
|
-
if (resolved) return;
|
|
52
|
-
cleanup();
|
|
53
|
-
reject(
|
|
54
|
-
new Error(
|
|
55
|
-
`pome capture-server did not start listening within ${readyTimeoutMs}ms.${stderrBuf ? `
|
|
56
|
-
stderr: ${stderrBuf.trim().slice(0, 2048)}` : ""}`
|
|
57
|
-
)
|
|
58
|
-
);
|
|
59
|
-
}, readyTimeoutMs);
|
|
60
|
-
const cleanup = () => {
|
|
61
|
-
clearTimeout(timer);
|
|
62
|
-
child.stderr?.off("data", onData);
|
|
63
|
-
child.off("exit", onExit);
|
|
64
|
-
};
|
|
65
|
-
child.stderr?.on("data", onData);
|
|
66
|
-
child.once("exit", onExit);
|
|
67
|
-
});
|
|
68
|
-
let port;
|
|
69
|
-
try {
|
|
70
|
-
port = await readyPromise;
|
|
71
|
-
} catch (err) {
|
|
72
|
-
if (!child.killed) child.kill("SIGKILL");
|
|
73
|
-
throw err;
|
|
74
|
-
}
|
|
75
|
-
child.stderr?.on("data", () => {
|
|
76
|
-
});
|
|
77
|
-
const pid = child.pid;
|
|
78
|
-
if (pid === void 0) {
|
|
79
|
-
child.kill("SIGKILL");
|
|
80
|
-
throw new Error("pome capture-server child has no pid after boot");
|
|
81
|
-
}
|
|
82
|
-
return {
|
|
83
|
-
port,
|
|
84
|
-
pid,
|
|
85
|
-
shutdown: makeShutdown(child, shutdownGraceMs)
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
function defaultBinArgs(eventsOut, allowHosts, egressOut) {
|
|
89
|
-
const script = process.argv[1];
|
|
90
|
-
if (typeof script !== "string" || script.length === 0) {
|
|
91
|
-
throw new Error(
|
|
92
|
-
"Cannot determine pome binary path (process.argv[1] is empty). Pass `binArgs` explicitly."
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
return [script, "capture-server", "--port", "0", "--events-out", eventsOut, ...egressArgs(allowHosts, egressOut)];
|
|
96
|
-
}
|
|
97
|
-
function egressArgs(allowHosts, egressOut) {
|
|
98
|
-
const args = [];
|
|
99
|
-
if (allowHosts && allowHosts.length > 0) args.push("--allow", allowHosts.join(","));
|
|
100
|
-
if (egressOut) args.push("--egress-out", egressOut);
|
|
101
|
-
return args;
|
|
102
|
-
}
|
|
103
|
-
function makeShutdown(child, graceMs) {
|
|
104
|
-
let done = null;
|
|
105
|
-
return () => {
|
|
106
|
-
if (done) return done;
|
|
107
|
-
done = new Promise((resolve2) => {
|
|
108
|
-
if (child.exitCode !== null || child.signalCode !== null) {
|
|
109
|
-
resolve2();
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const onExit = () => {
|
|
113
|
-
clearTimeout(killTimer);
|
|
114
|
-
resolve2();
|
|
115
|
-
};
|
|
116
|
-
child.once("exit", onExit);
|
|
117
|
-
try {
|
|
118
|
-
child.kill("SIGTERM");
|
|
119
|
-
} catch {
|
|
120
|
-
child.off("exit", onExit);
|
|
121
|
-
resolve2();
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const killTimer = setTimeout(() => {
|
|
125
|
-
if (child.exitCode === null && child.signalCode === null) {
|
|
126
|
-
try {
|
|
127
|
-
child.kill("SIGKILL");
|
|
128
|
-
} catch {
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}, graceMs);
|
|
132
|
-
});
|
|
133
|
-
return done;
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
function resolveTwinHttpParents(rows) {
|
|
137
|
-
const eventIdByToolUseId = /* @__PURE__ */ new Map();
|
|
138
|
-
for (const row of rows) {
|
|
139
|
-
if (row.kind === "ToolUseEvent") eventIdByToolUseId.set(row.tool_use_id, row.event_id);
|
|
140
|
-
}
|
|
141
|
-
if (eventIdByToolUseId.size === 0) return rows;
|
|
142
|
-
return rows.map((row) => {
|
|
143
|
-
if (row.kind !== "TwinHttpEvent") return row;
|
|
144
|
-
if (row.parent_event_id != null) return row;
|
|
145
|
-
const causingToolUseId = row.tool_call_id ?? row.correlation_id ?? null;
|
|
146
|
-
if (causingToolUseId === null) return row;
|
|
147
|
-
const parentEventId = eventIdByToolUseId.get(causingToolUseId);
|
|
148
|
-
return parentEventId === void 0 ? row : { ...row, parent_event_id: parentEventId };
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
async function mergeAdapterSignalsIntoEvents(signalsPath, eventsJsonlPath) {
|
|
152
|
-
let rawSignals;
|
|
153
|
-
try {
|
|
154
|
-
rawSignals = await readFile(signalsPath, "utf8");
|
|
155
|
-
} catch {
|
|
156
|
-
return { appended: 0, dropped: 0 };
|
|
157
|
-
}
|
|
158
|
-
let dropped = 0;
|
|
159
|
-
const signalRows = [];
|
|
160
|
-
for (const line of rawSignals.split("\n")) {
|
|
161
|
-
if (line.length === 0) continue;
|
|
162
|
-
let parsed;
|
|
163
|
-
try {
|
|
164
|
-
parsed = JSON.parse(line);
|
|
165
|
-
} catch {
|
|
166
|
-
dropped += 1;
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
const result = eventSchema.safeParse(parsed);
|
|
170
|
-
if (!result.success) {
|
|
171
|
-
dropped += 1;
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
signalRows.push(redactEvent(result.data));
|
|
175
|
-
}
|
|
176
|
-
if (signalRows.length === 0) return { appended: 0, dropped };
|
|
177
|
-
let rawEvents;
|
|
178
|
-
try {
|
|
179
|
-
rawEvents = await readFile(eventsJsonlPath, "utf8");
|
|
180
|
-
} catch {
|
|
181
|
-
rawEvents = "";
|
|
182
|
-
}
|
|
183
|
-
const eventRows = [];
|
|
184
|
-
const unparseablePassthrough = [];
|
|
185
|
-
for (const line of rawEvents.split("\n")) {
|
|
186
|
-
if (line.length === 0) continue;
|
|
187
|
-
let parsed;
|
|
188
|
-
try {
|
|
189
|
-
parsed = JSON.parse(line);
|
|
190
|
-
} catch {
|
|
191
|
-
unparseablePassthrough.push(line);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
const result = eventSchema.safeParse(parsed);
|
|
195
|
-
if (result.success) {
|
|
196
|
-
eventRows.push(redactEvent(result.data));
|
|
197
|
-
} else {
|
|
198
|
-
unparseablePassthrough.push(line);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
const merged = resolveTwinHttpParents(eventRows.concat(signalRows));
|
|
202
|
-
merged.sort((a, b) => a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0);
|
|
203
|
-
const sortedJsonl = merged.map((r) => JSON.stringify(r)).join("\n");
|
|
204
|
-
const head = unparseablePassthrough.length > 0 ? unparseablePassthrough.join("\n") + "\n" : "";
|
|
205
|
-
await writeFile(eventsJsonlPath, head + sortedJsonl + "\n");
|
|
206
|
-
return { appended: signalRows.length, dropped };
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// src/runner/runTaskCore.ts
|
|
210
|
-
async function writeRunNoScore(input) {
|
|
211
|
-
const artifacts = await writeRunArtifactsCore({
|
|
212
|
-
artifactsDir: input.artifactsDir,
|
|
213
|
-
runId: input.runId,
|
|
214
|
-
scenario: input.scenario,
|
|
215
|
-
startedAt: input.startedAt,
|
|
216
|
-
completedAt: input.completedAt,
|
|
217
|
-
stdout: input.agentStdout,
|
|
218
|
-
stderr: input.agentStderr,
|
|
219
|
-
exitCode: input.agentExitCode,
|
|
220
|
-
events: input.events,
|
|
221
|
-
stateInitial: input.stateInitial,
|
|
222
|
-
stateFinal: input.stateFinal
|
|
223
|
-
});
|
|
224
|
-
const exitCode = input.agentExitCode !== 0 || input.agentTimedOut ? 3 : 0;
|
|
225
|
-
return { artifacts, exitCode };
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// src/runner/runTask.ts
|
|
229
|
-
function closeServer(server) {
|
|
230
|
-
return new Promise((resolvePromise, reject) => {
|
|
231
|
-
server.close((err) => err ? reject(err) : resolvePromise());
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
async function runTask(options) {
|
|
235
|
-
const scenario = await parseTaskFile(options.taskPath);
|
|
236
|
-
const runId = `run_${randomUUID()}`;
|
|
237
|
-
const artifactsDir = options.artifactsDir ?? "runs";
|
|
238
|
-
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
239
|
-
const writeRun = writeRunNoScore;
|
|
240
|
-
const runDir = join(artifactsDir, scenario.slug, runId);
|
|
241
|
-
await mkdir(runDir, { recursive: true });
|
|
242
|
-
const signalsPath = join(runDir, "signals.jsonl");
|
|
243
|
-
await writeFile(signalsPath, "");
|
|
244
|
-
const signalsPathForEnv = sep === "\\" ? signalsPath.replace(/\\/g, "/") : signalsPath;
|
|
245
|
-
const eventsJsonlPath = join(runDir, "events.jsonl");
|
|
246
|
-
await writeFile(eventsJsonlPath, "");
|
|
247
|
-
const egressAllowHosts = buildEgressAllowlist(process.env, {
|
|
248
|
-
extraHosts: options.egressExtraHosts
|
|
249
|
-
});
|
|
250
|
-
const egressJsonlPath = join(runDir, "egress.jsonl");
|
|
251
|
-
const captureServer = options.noCapture ? null : await spawnCaptureServerChild({
|
|
252
|
-
eventsOut: resolve(eventsJsonlPath),
|
|
253
|
-
allowHosts: egressAllowHosts,
|
|
254
|
-
egressOut: resolve(egressJsonlPath),
|
|
255
|
-
execPath: options.captureServerCommand?.execPath,
|
|
256
|
-
binArgs: options.captureServerCommand ? [
|
|
257
|
-
...options.captureServerCommand.prefixArgs,
|
|
258
|
-
"capture-server",
|
|
259
|
-
"--port",
|
|
260
|
-
"0",
|
|
261
|
-
"--events-out",
|
|
262
|
-
resolve(eventsJsonlPath),
|
|
263
|
-
...egressArgs(egressAllowHosts, resolve(egressJsonlPath))
|
|
264
|
-
] : void 0
|
|
265
|
-
});
|
|
266
|
-
if (captureServer) options.onCaptureServerSpawned?.(captureServer.pid);
|
|
267
|
-
const collectBlockedEgress = async () => {
|
|
268
|
-
if (!captureServer) return [];
|
|
269
|
-
await captureServer.shutdown();
|
|
270
|
-
return readBlockedEgress(egressJsonlPath);
|
|
271
|
-
};
|
|
272
|
-
const twins = scenario.config.twins.length ? scenario.config.twins : ["github"];
|
|
273
|
-
const primaryTwin = twins[0];
|
|
274
|
-
const isMultiTwin = twins.length > 1;
|
|
275
|
-
const sid = runId;
|
|
276
|
-
const authSecret = process.env.TWIN_AUTH_SECRET ?? randomBytes(32).toString("hex");
|
|
277
|
-
process.env.TWIN_AUTH_SECRET = authSecret;
|
|
278
|
-
const recorder = createRecorder({ eventsPath: eventsJsonlPath });
|
|
279
|
-
const booted = [];
|
|
280
|
-
const twinEnv = {};
|
|
281
|
-
const tokenEnvNames = /* @__PURE__ */ new Set();
|
|
282
|
-
const stateInitialByTwin = {};
|
|
283
|
-
let mergedClaims = {};
|
|
284
|
-
for (const twin of twins) {
|
|
285
|
-
const port = await getAvailablePort();
|
|
286
|
-
const baseUrl = `http://127.0.0.1:${port}`;
|
|
287
|
-
const harness = await bootTwin({
|
|
288
|
-
twin,
|
|
289
|
-
seedState: seedStateForTwin(scenario, twin),
|
|
290
|
-
runId,
|
|
291
|
-
twinBaseUrl: baseUrl,
|
|
292
|
-
recorder
|
|
293
|
-
});
|
|
294
|
-
stateInitialByTwin[twin] = await harness.exportState();
|
|
295
|
-
const sessionBase = `${baseUrl}/s/${sid}`;
|
|
296
|
-
twinEnv[`POME_${harness.envName}_REST_URL`] = sessionBase;
|
|
297
|
-
twinEnv[`POME_${harness.envName}_MCP_URL`] = `${sessionBase}/mcp`;
|
|
298
|
-
if (harness.tokenEnvName) tokenEnvNames.add(harness.tokenEnvName);
|
|
299
|
-
mergedClaims = { ...mergedClaims, ...harness.extraClaims ?? {} };
|
|
300
|
-
const server = serve({ fetch: harness.app.fetch, port, hostname: "127.0.0.1" });
|
|
301
|
-
booted.push({ twin, harness, server });
|
|
302
|
-
}
|
|
303
|
-
const stateInitial = stateInitialByTwin[primaryTwin];
|
|
304
|
-
const token = await sign(
|
|
305
|
-
{
|
|
306
|
-
sid,
|
|
307
|
-
team_id: "tm_local",
|
|
308
|
-
// The self-host agent acts as `pome-agent`, the collaborator the twin
|
|
309
|
-
// seeds onto every repo. Without a login claim the REST merge gate
|
|
310
|
-
// (which checks the authenticated user has push access) rejects merges —
|
|
311
|
-
// the MCP merge path calls the domain directly and never hit this, so it
|
|
312
|
-
// was invisible until a scripted REST agent needed to merge.
|
|
313
|
-
login: "pome-agent",
|
|
314
|
-
// Twin-supplied claims (e.g. Stripe's `account_id`, so the token resolves
|
|
315
|
-
// to the account the seed data lives in) — merged across all twins.
|
|
316
|
-
// github/slack supply none.
|
|
317
|
-
...mergedClaims,
|
|
318
|
-
exp: Math.floor(Date.now() / 1e3) + Math.max(scenario.config.timeout * 2, 600)
|
|
319
|
-
},
|
|
320
|
-
authSecret
|
|
321
|
-
);
|
|
322
|
-
const proxyEnv = captureServer ? {
|
|
323
|
-
// Agent's outbound traffic flows through the capture-server.
|
|
324
|
-
// Twin traffic is localhost, NO_PROXY excludes it so it stays a
|
|
325
|
-
// TwinHttpEvent (recorded in-process) and isn't double-counted as a
|
|
326
|
-
// proxy-captured LlmCallEvent.
|
|
327
|
-
HTTP_PROXY: `http://127.0.0.1:${captureServer.port}`,
|
|
328
|
-
HTTPS_PROXY: `http://127.0.0.1:${captureServer.port}`,
|
|
329
|
-
NO_PROXY: "127.0.0.1,localhost"
|
|
330
|
-
} : {};
|
|
331
|
-
for (const name of tokenEnvNames) twinEnv[name] = token;
|
|
332
|
-
const env = {
|
|
333
|
-
POME_TASK: scenario.prompt,
|
|
334
|
-
POME_TWIN_NAMES: twins.join(","),
|
|
335
|
-
...twinEnv,
|
|
336
|
-
POME_AUTH_TOKEN: token,
|
|
337
|
-
POME_RUN_ID: runId,
|
|
338
|
-
POME_ARTIFACTS_DIR: runDir,
|
|
339
|
-
POME_ADAPTER_SIGNALS_PATH: signalsPathForEnv,
|
|
340
|
-
...options.extraAgentEnv ?? {},
|
|
341
|
-
...proxyEnv
|
|
342
|
-
};
|
|
343
|
-
const captureFinalStates = async () => {
|
|
344
|
-
const finals = {};
|
|
345
|
-
for (const { twin, harness } of booted) {
|
|
346
|
-
finals[twin] = await harness.exportState();
|
|
347
|
-
}
|
|
348
|
-
if (isMultiTwin) {
|
|
349
|
-
for (const { twin } of booted) {
|
|
350
|
-
if (twin === primaryTwin) continue;
|
|
351
|
-
try {
|
|
352
|
-
await writeFile(
|
|
353
|
-
join(runDir, `state_final.${twin}.json`),
|
|
354
|
-
`${JSON.stringify(redactSecrets(finals[twin]), null, 2)}
|
|
355
|
-
`
|
|
356
|
-
);
|
|
357
|
-
} catch {
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
return finals[primaryTwin];
|
|
362
|
-
};
|
|
363
|
-
try {
|
|
364
|
-
const preflight = await runAgentCommand({
|
|
365
|
-
command: options.agentCommand,
|
|
366
|
-
env,
|
|
367
|
-
// Cold agent startup can exceed 10s on CI; cap preflight below full scenario
|
|
368
|
-
// timeout but leave enough headroom for toolchain startup.
|
|
369
|
-
timeoutSeconds: Math.min(60, scenario.config.timeout),
|
|
370
|
-
preflight: true
|
|
371
|
-
});
|
|
372
|
-
if (preflight.exitCode !== 0) {
|
|
373
|
-
await recorder.flush?.();
|
|
374
|
-
const stateFinal2 = await captureFinalStates();
|
|
375
|
-
const { artifacts: artifacts2, exitCode: exitCode2 } = await writeRun({
|
|
376
|
-
artifactsDir,
|
|
377
|
-
runId,
|
|
378
|
-
scenario,
|
|
379
|
-
startedAt,
|
|
380
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
381
|
-
agentStdout: preflight.stdout,
|
|
382
|
-
agentStderr: preflight.stderr,
|
|
383
|
-
agentExitCode: preflight.exitCode,
|
|
384
|
-
agentTimedOut: preflight.timedOut ?? false,
|
|
385
|
-
events: recorder.events(),
|
|
386
|
-
stateInitial,
|
|
387
|
-
stateFinal: stateFinal2
|
|
388
|
-
});
|
|
389
|
-
await mergeAdapterSignalsIntoEvents(signalsPath, eventsJsonlPath);
|
|
390
|
-
void exitCode2;
|
|
391
|
-
const blockedEgress2 = await collectBlockedEgress();
|
|
392
|
-
return { scenario, runId, artifacts: artifacts2, agent: preflight, exitCode: 3, blockedEgress: blockedEgress2 };
|
|
393
|
-
}
|
|
394
|
-
const agent = await runAgentCommand({
|
|
395
|
-
command: options.agentCommand,
|
|
396
|
-
env,
|
|
397
|
-
timeoutSeconds: scenario.config.timeout
|
|
398
|
-
});
|
|
399
|
-
await recorder.flush?.();
|
|
400
|
-
const stateFinal = await captureFinalStates();
|
|
401
|
-
const { artifacts, exitCode } = await writeRun({
|
|
402
|
-
artifactsDir,
|
|
403
|
-
runId,
|
|
404
|
-
scenario,
|
|
405
|
-
startedAt,
|
|
406
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
407
|
-
agentStdout: agent.stdout,
|
|
408
|
-
agentStderr: agent.stderr,
|
|
409
|
-
agentExitCode: agent.exitCode,
|
|
410
|
-
agentTimedOut: agent.timedOut ?? false,
|
|
411
|
-
events: recorder.events(),
|
|
412
|
-
stateInitial,
|
|
413
|
-
stateFinal
|
|
414
|
-
});
|
|
415
|
-
await mergeAdapterSignalsIntoEvents(signalsPath, eventsJsonlPath);
|
|
416
|
-
const blockedEgress = await collectBlockedEgress();
|
|
417
|
-
return { scenario, runId, artifacts, agent, exitCode, blockedEgress };
|
|
418
|
-
} finally {
|
|
419
|
-
if (captureServer) await captureServer.shutdown();
|
|
420
|
-
await Promise.all(
|
|
421
|
-
booted.map(async ({ server, harness }) => {
|
|
422
|
-
await closeServer(server);
|
|
423
|
-
await harness.close();
|
|
424
|
-
})
|
|
425
|
-
);
|
|
426
|
-
await recorder.close?.();
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
function resolvePackageRoot(importMetaUrl) {
|
|
430
|
-
let dir = dirname(fileURLToPath(importMetaUrl));
|
|
431
|
-
for (let i = 0; i < 6; i++) {
|
|
432
|
-
if (existsSync(join(dir, "package.json"))) return dir;
|
|
433
|
-
const parent = resolve(dir, "..");
|
|
434
|
-
if (parent === dir) break;
|
|
435
|
-
dir = parent;
|
|
436
|
-
}
|
|
437
|
-
return void 0;
|
|
438
|
-
}
|
|
439
|
-
function assetPath(...segments) {
|
|
440
|
-
const root = resolvePackageRoot(import.meta.url);
|
|
441
|
-
if (!root) {
|
|
442
|
-
throw new Error(
|
|
443
|
-
`Could not locate the @pome-sh/cli package root (no enclosing package.json) while resolving the packaged asset ${segments.join("/")}.`
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
const candidate = join(root, "assets", ...segments);
|
|
447
|
-
if (!existsSync(candidate)) {
|
|
448
|
-
throw new Error(
|
|
449
|
-
`Packaged asset not found at ${candidate}. This is a packaging bug \u2014 \`assets/\` must be listed in cli/package.json \`files\`.`
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
return candidate;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// src/demo/task.ts
|
|
456
|
-
var DEMO_TASK_NAME = "first-run-demo";
|
|
457
|
-
var DEMO_REPO = "acme/api";
|
|
458
|
-
function demoTaskPath() {
|
|
459
|
-
return assetPath("demo", "first-run-demo.md");
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
export { DEMO_REPO, DEMO_TASK_NAME, assetPath, demoTaskPath, resolvePackageRoot, runTask };
|
package/dist/chunk-ZX4WNSZ5.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
// src/demo/capacity.ts
|
|
2
|
-
var DemoCapacityError = class extends Error {
|
|
3
|
-
constructor(kind, message) {
|
|
4
|
-
super(message);
|
|
5
|
-
this.kind = kind;
|
|
6
|
-
this.name = "DemoCapacityError";
|
|
7
|
-
}
|
|
8
|
-
kind;
|
|
9
|
-
};
|
|
10
|
-
function capacityKindFrom(status, detailsKind) {
|
|
11
|
-
if (typeof detailsKind === "string") {
|
|
12
|
-
switch (detailsKind) {
|
|
13
|
-
case "session_llm_call_cap":
|
|
14
|
-
return "session_llm_call_cap";
|
|
15
|
-
case "demo_ip_llm_cap":
|
|
16
|
-
return "demo_ip_llm_cap";
|
|
17
|
-
case "daily_model_cap":
|
|
18
|
-
return "daily_model_cap";
|
|
19
|
-
case "daily_judge_cap":
|
|
20
|
-
return "daily_judge_cap";
|
|
21
|
-
case "ai_gateway_disabled":
|
|
22
|
-
case "demo_model_unpriced":
|
|
23
|
-
return "gateway_unavailable";
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (status === 402 || status === 429) return "unknown_capacity";
|
|
27
|
-
if (status === 503) return "gateway_unavailable";
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
function capacityLabel(kind) {
|
|
31
|
-
switch (kind) {
|
|
32
|
-
case "demo_ip_mint_cap":
|
|
33
|
-
case "demo_ip_llm_cap":
|
|
34
|
-
return "the demo hit today's limit for this network \u2014 try again tomorrow, or sign up for a free account";
|
|
35
|
-
case "daily_model_cap":
|
|
36
|
-
return "the demo's daily model budget is exhausted \u2014 try again tomorrow, or sign up for a free account";
|
|
37
|
-
case "daily_judge_cap":
|
|
38
|
-
return "the demo's daily evaluation budget is exhausted \u2014 try again tomorrow, or sign up for a free account";
|
|
39
|
-
case "session_llm_call_cap":
|
|
40
|
-
return "this trial hit its model-call ceiling";
|
|
41
|
-
case "gateway_unavailable":
|
|
42
|
-
return "the demo model gateway is unavailable right now \u2014 try again shortly";
|
|
43
|
-
case "demo_mint_quota":
|
|
44
|
-
case "unknown_capacity":
|
|
45
|
-
return "the demo is at capacity today \u2014 try again tomorrow";
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
var CAPACITY_STDERR_PREFIX = "POME_DEMO_CAPACITY:";
|
|
49
|
-
function capacityMarkerLine(kind) {
|
|
50
|
-
return `${CAPACITY_STDERR_PREFIX}${kind}`;
|
|
51
|
-
}
|
|
52
|
-
function parseCapacityMarker(stderr) {
|
|
53
|
-
const match = stderr.match(
|
|
54
|
-
new RegExp(`${CAPACITY_STDERR_PREFIX}([a-z_]+)`, "m")
|
|
55
|
-
);
|
|
56
|
-
if (!match) return null;
|
|
57
|
-
const kind = match[1];
|
|
58
|
-
const known = [
|
|
59
|
-
"demo_ip_mint_cap",
|
|
60
|
-
"demo_mint_quota",
|
|
61
|
-
"session_llm_call_cap",
|
|
62
|
-
"demo_ip_llm_cap",
|
|
63
|
-
"daily_model_cap",
|
|
64
|
-
"daily_judge_cap",
|
|
65
|
-
"gateway_unavailable",
|
|
66
|
-
"unknown_capacity"
|
|
67
|
-
];
|
|
68
|
-
return known.includes(kind) ? kind : "unknown_capacity";
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export { DemoCapacityError, capacityKindFrom, capacityLabel, capacityMarkerLine, parseCapacityMarker };
|