open-research-protocol 0.4.14 → 0.4.16
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/AGENT_INTEGRATION.md +50 -0
- package/README.md +273 -144
- package/bin/orp.js +14 -1
- package/cli/orp.py +14846 -9925
- package/docs/AGENT_LOOP.md +13 -0
- package/docs/AGENT_MODES.md +79 -0
- package/docs/CANONICAL_CLI_BOUNDARY.md +15 -0
- package/docs/EXCHANGE.md +94 -0
- package/docs/LAUNCH_KIT.md +107 -0
- package/docs/ORP_HOSTED_WORKSPACE_CONTRACT.md +295 -0
- package/docs/ORP_PUBLIC_LAUNCH_CHECKLIST.md +5 -0
- package/docs/START_HERE.md +567 -0
- package/package.json +4 -2
- package/packages/lifeops-orp/README.md +67 -0
- package/packages/lifeops-orp/package.json +48 -0
- package/packages/lifeops-orp/src/index.d.ts +106 -0
- package/packages/lifeops-orp/src/index.js +7 -0
- package/packages/lifeops-orp/src/mapping.js +309 -0
- package/packages/lifeops-orp/src/workspace.js +108 -0
- package/packages/lifeops-orp/test/orp.test.js +187 -0
- package/packages/orp-workspace-launcher/README.md +82 -0
- package/packages/orp-workspace-launcher/package.json +39 -0
- package/packages/orp-workspace-launcher/src/commands.js +77 -0
- package/packages/orp-workspace-launcher/src/core-plan.js +506 -0
- package/packages/orp-workspace-launcher/src/hosted-state.js +208 -0
- package/packages/orp-workspace-launcher/src/index.js +82 -0
- package/packages/orp-workspace-launcher/src/ledger.js +745 -0
- package/packages/orp-workspace-launcher/src/list.js +488 -0
- package/packages/orp-workspace-launcher/src/orp-command.js +126 -0
- package/packages/orp-workspace-launcher/src/orp.js +912 -0
- package/packages/orp-workspace-launcher/src/registry.js +558 -0
- package/packages/orp-workspace-launcher/src/slot.js +188 -0
- package/packages/orp-workspace-launcher/src/sync.js +363 -0
- package/packages/orp-workspace-launcher/src/tabs.js +166 -0
- package/packages/orp-workspace-launcher/test/commands.test.js +164 -0
- package/packages/orp-workspace-launcher/test/core-plan.test.js +253 -0
- package/packages/orp-workspace-launcher/test/fixtures/smoke-notes.txt +2 -0
- package/packages/orp-workspace-launcher/test/fixtures/workspace-manifest.json +17 -0
- package/packages/orp-workspace-launcher/test/ledger.test.js +244 -0
- package/packages/orp-workspace-launcher/test/list.test.js +299 -0
- package/packages/orp-workspace-launcher/test/orp-command.test.js +44 -0
- package/packages/orp-workspace-launcher/test/orp.test.js +224 -0
- package/packages/orp-workspace-launcher/test/tabs.test.js +168 -0
- package/scripts/orp-kernel-agent-pilot.py +10 -1
- package/scripts/orp-kernel-agent-replication.py +10 -1
- package/scripts/orp-kernel-canonical-continuation.py +10 -1
- package/scripts/orp-kernel-continuation-pilot.py +10 -1
- package/scripts/render-terminal-demo.py +416 -0
- package/spec/v1/exchange-report.schema.json +105 -0
- package/spec/v1/hosted-workspace-event.schema.json +102 -0
- package/spec/v1/hosted-workspace.schema.json +332 -0
- package/spec/v1/workspace.schema.json +108 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
chooseImplicitMainCandidate,
|
|
9
|
+
loadWorkspaceSource,
|
|
10
|
+
resolveWorkspaceSelectorFromCollections,
|
|
11
|
+
resolveWorkspaceWatchTargets,
|
|
12
|
+
} from "../src/orp.js";
|
|
13
|
+
import { setWorkspaceSlot } from "../src/registry.js";
|
|
14
|
+
|
|
15
|
+
async function makeTempDir() {
|
|
16
|
+
return fs.mkdtemp(path.join(os.tmpdir(), "orp-workspace-selector-"));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
test("resolveWorkspaceSelectorFromCollections matches hosted ideas by saved workspace id and title slug", () => {
|
|
20
|
+
const ideas = [
|
|
21
|
+
{
|
|
22
|
+
id: "ef86bd4c-35b0-454c-ac90-9136a006b0af",
|
|
23
|
+
title: "Terminal paths and codex sessions 03-26-2026",
|
|
24
|
+
notes: `
|
|
25
|
+
\`\`\`orp-workspace
|
|
26
|
+
{
|
|
27
|
+
"version": "1",
|
|
28
|
+
"workspaceId": "workspace-main-1",
|
|
29
|
+
"title": "Main Cody 1",
|
|
30
|
+
"tabs": [
|
|
31
|
+
{ "title": "orp", "path": "/Volumes/Code_2TB/code/orp" }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
\`\`\`
|
|
35
|
+
`,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const byWorkspaceId = resolveWorkspaceSelectorFromCollections("workspace-main-1", { ideas });
|
|
40
|
+
assert.equal(byWorkspaceId?.kind, "hosted-idea");
|
|
41
|
+
assert.equal(byWorkspaceId?.idea?.id, "ef86bd4c-35b0-454c-ac90-9136a006b0af");
|
|
42
|
+
|
|
43
|
+
const byTitleSlug = resolveWorkspaceSelectorFromCollections("main-cody-1", { ideas });
|
|
44
|
+
assert.equal(byTitleSlug?.title, "Main Cody 1");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("resolveWorkspaceSelectorFromCollections can match local tracked workspaces by title", () => {
|
|
48
|
+
const resolved = resolveWorkspaceSelectorFromCollections("ORP Main", {
|
|
49
|
+
localWorkspaces: [
|
|
50
|
+
{
|
|
51
|
+
manifestPath: "/tmp/orp-main.json",
|
|
52
|
+
workspaceId: "orp-main",
|
|
53
|
+
title: "ORP Main",
|
|
54
|
+
status: "ok",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
assert.equal(resolved?.kind, "workspace-file");
|
|
60
|
+
assert.equal(resolved?.manifestPath, "/tmp/orp-main.json");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("resolveWorkspaceSelectorFromCollections prefers hosted saved workspaces over local duplicates", () => {
|
|
64
|
+
const ideas = [
|
|
65
|
+
{
|
|
66
|
+
id: "idea-main",
|
|
67
|
+
title: "ORP Main",
|
|
68
|
+
notes: `
|
|
69
|
+
\`\`\`orp-workspace
|
|
70
|
+
{
|
|
71
|
+
"version": "1",
|
|
72
|
+
"workspaceId": "orp-main",
|
|
73
|
+
"title": "ORP Main",
|
|
74
|
+
"tabs": [
|
|
75
|
+
{ "path": "/Volumes/Code_2TB/code/orp" }
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
\`\`\`
|
|
79
|
+
`,
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
const localWorkspaces = [
|
|
83
|
+
{
|
|
84
|
+
manifestPath: "/tmp/orp-main.json",
|
|
85
|
+
workspaceId: "orp-main",
|
|
86
|
+
title: "ORP Main",
|
|
87
|
+
status: "ok",
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const resolved = resolveWorkspaceSelectorFromCollections("orp-main", {
|
|
92
|
+
ideas,
|
|
93
|
+
localWorkspaces,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
assert.equal(resolved?.kind, "hosted-idea");
|
|
97
|
+
assert.equal(resolved?.idea?.id, "idea-main");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("resolveWorkspaceSelectorFromCollections raises on ambiguous hosted titles", () => {
|
|
101
|
+
const ideas = [
|
|
102
|
+
{
|
|
103
|
+
id: "idea-1",
|
|
104
|
+
title: "Main Cody 1",
|
|
105
|
+
notes: `
|
|
106
|
+
/Volumes/Code_2TB/code/orp
|
|
107
|
+
`,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "idea-2",
|
|
111
|
+
title: "Main Cody 1",
|
|
112
|
+
notes: `
|
|
113
|
+
/Volumes/Code_2TB/code/orp-web-app
|
|
114
|
+
`,
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
assert.throws(
|
|
119
|
+
() => resolveWorkspaceSelectorFromCollections("Main Cody 1", { ideas }),
|
|
120
|
+
/ambiguous/i,
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("loadWorkspaceSource resolves the main slot to an explicit local manifest", async () => {
|
|
125
|
+
const tempDir = await makeTempDir();
|
|
126
|
+
const env = {
|
|
127
|
+
...process.env,
|
|
128
|
+
XDG_CONFIG_HOME: path.join(tempDir, "config"),
|
|
129
|
+
};
|
|
130
|
+
const manifestPath = path.join(tempDir, "main-cody-1.json");
|
|
131
|
+
await fs.writeFile(
|
|
132
|
+
manifestPath,
|
|
133
|
+
`${JSON.stringify(
|
|
134
|
+
{
|
|
135
|
+
version: "1",
|
|
136
|
+
workspaceId: "main-cody-1",
|
|
137
|
+
title: "main-cody-1",
|
|
138
|
+
tabs: [{ path: "/Volumes/Code_2TB/code/orp" }],
|
|
139
|
+
},
|
|
140
|
+
null,
|
|
141
|
+
2,
|
|
142
|
+
)}\n`,
|
|
143
|
+
"utf8",
|
|
144
|
+
);
|
|
145
|
+
await setWorkspaceSlot(
|
|
146
|
+
"main",
|
|
147
|
+
{
|
|
148
|
+
kind: "workspace-file",
|
|
149
|
+
workspaceId: "main-cody-1",
|
|
150
|
+
title: "main-cody-1",
|
|
151
|
+
manifestPath,
|
|
152
|
+
},
|
|
153
|
+
{ env },
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const source = await loadWorkspaceSource({
|
|
157
|
+
ideaId: "main",
|
|
158
|
+
env,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
assert.equal(source.sourceType, "workspace-file");
|
|
162
|
+
assert.equal(source.sourcePath, path.resolve(manifestPath));
|
|
163
|
+
assert.equal(source.workspaceManifest?.workspaceId, "main-cody-1");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("chooseImplicitMainCandidate prefers the single hosted-backed workspace over generated local captures", () => {
|
|
167
|
+
const candidate = chooseImplicitMainCandidate([
|
|
168
|
+
{
|
|
169
|
+
kind: "hosted-idea",
|
|
170
|
+
workspaceId: "idea-ef86bd4c-35b0-454c-ac90-9136a006b0af",
|
|
171
|
+
title: "Terminal paths and codex sessions 03-26-2026",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
kind: "workspace-file",
|
|
175
|
+
workspaceId: "captured-iterm-window-20260401t032225z",
|
|
176
|
+
title: "captured-iterm-window-20260401t032225z",
|
|
177
|
+
manifestPath: "/tmp/captured-1.json",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
kind: "workspace-file",
|
|
181
|
+
workspaceId: "captured-iterm-window-20260401t032215z",
|
|
182
|
+
title: "captured-iterm-window-20260401t032215z",
|
|
183
|
+
manifestPath: "/tmp/captured-2.json",
|
|
184
|
+
},
|
|
185
|
+
]);
|
|
186
|
+
|
|
187
|
+
assert.equal(candidate?.kind, "hosted-idea");
|
|
188
|
+
assert.equal(candidate?.workspaceId, "idea-ef86bd4c-35b0-454c-ac90-9136a006b0af");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("resolveWorkspaceWatchTargets syncs idea-bridge hosted workspaces through the linked idea", () => {
|
|
192
|
+
const targets = resolveWorkspaceWatchTargets(
|
|
193
|
+
{
|
|
194
|
+
sourceType: "hosted-workspace",
|
|
195
|
+
hostedWorkspace: {
|
|
196
|
+
workspace_id: "idea-ef86bd4c-35b0-454c-ac90-9136a006b0af",
|
|
197
|
+
source_kind: "idea_bridge",
|
|
198
|
+
linkedIdea: {
|
|
199
|
+
ideaId: "ef86bd4c-35b0-454c-ac90-9136a006b0af",
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{},
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
assert.equal(targets.hostedWorkspaceId, null);
|
|
207
|
+
assert.equal(targets.syncIdeaSelector, "ef86bd4c-35b0-454c-ac90-9136a006b0af");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("resolveWorkspaceWatchTargets preserves true hosted workspace ids", () => {
|
|
211
|
+
const targets = resolveWorkspaceWatchTargets(
|
|
212
|
+
{
|
|
213
|
+
sourceType: "hosted-workspace",
|
|
214
|
+
hostedWorkspace: {
|
|
215
|
+
workspace_id: "ws_orp_main",
|
|
216
|
+
source_kind: "hosted",
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
{},
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
assert.equal(targets.hostedWorkspaceId, "ws_orp_main");
|
|
223
|
+
assert.equal(targets.syncIdeaSelector, null);
|
|
224
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
import { buildWorkspaceTabsReport, parseWorkspaceSource, parseWorkspaceTabsArgs, runWorkspaceTabs } from "../src/index.js";
|
|
8
|
+
|
|
9
|
+
async function makeTempDir() {
|
|
10
|
+
return fs.mkdtemp(path.join(os.tmpdir(), "orp-workspace-tabs-"));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function captureStdout(fn) {
|
|
14
|
+
const chunks = [];
|
|
15
|
+
const originalWrite = process.stdout.write;
|
|
16
|
+
process.stdout.write = (chunk, encoding, callback) => {
|
|
17
|
+
chunks.push(typeof chunk === "string" ? chunk : chunk.toString(encoding || "utf8"));
|
|
18
|
+
if (typeof callback === "function") {
|
|
19
|
+
callback();
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const code = await fn();
|
|
26
|
+
return {
|
|
27
|
+
code,
|
|
28
|
+
stdout: chunks.join(""),
|
|
29
|
+
};
|
|
30
|
+
} finally {
|
|
31
|
+
process.stdout.write = originalWrite;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test("parseWorkspaceTabsArgs accepts JSON and workspace selectors", () => {
|
|
36
|
+
const parsed = parseWorkspaceTabsArgs([
|
|
37
|
+
"--workspace-file",
|
|
38
|
+
"/tmp/workspace.json",
|
|
39
|
+
"--json",
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
assert.equal(parsed.workspaceFile, "/tmp/workspace.json");
|
|
43
|
+
assert.equal(parsed.json, true);
|
|
44
|
+
assert.throws(() => parseWorkspaceTabsArgs(["idea-123", "idea-456"]), /unexpected argument/);
|
|
45
|
+
assert.throws(() => parseWorkspaceTabsArgs(["--wat"]), /missing value|unknown option/);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("buildWorkspaceTabsReport keeps duplicate titles unique and exposes generic resume metadata", () => {
|
|
49
|
+
const parsed = parseWorkspaceSource({
|
|
50
|
+
sourceType: "hosted-idea",
|
|
51
|
+
sourceLabel: "Workspace idea",
|
|
52
|
+
title: "Workspace idea",
|
|
53
|
+
notes: `
|
|
54
|
+
/Volumes/Code_2TB/code/collaboration: codex resume abc-123
|
|
55
|
+
/Volumes/Code_2TB/code/anthropic-lab: claude resume claude-456
|
|
56
|
+
/Volumes/Code_2TB/code/collaboration
|
|
57
|
+
`,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const report = buildWorkspaceTabsReport(
|
|
61
|
+
{
|
|
62
|
+
sourceType: "hosted-idea",
|
|
63
|
+
sourceLabel: "Workspace idea",
|
|
64
|
+
title: "Workspace idea",
|
|
65
|
+
},
|
|
66
|
+
parsed,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
assert.match(report.workspaceId, /^workspace-/);
|
|
70
|
+
assert.equal(report.tabCount, 3);
|
|
71
|
+
assert.equal(report.tabs[0]?.title, "collaboration");
|
|
72
|
+
assert.equal(report.tabs[0]?.resumeCommand, "codex resume abc-123");
|
|
73
|
+
assert.equal(
|
|
74
|
+
report.tabs[0]?.restartCommand,
|
|
75
|
+
"cd '/Volumes/Code_2TB/code/collaboration' && codex resume abc-123",
|
|
76
|
+
);
|
|
77
|
+
assert.equal(report.tabs[0]?.codexSessionId, "abc-123");
|
|
78
|
+
assert.equal(report.tabs[1]?.title, "anthropic-lab");
|
|
79
|
+
assert.equal(report.tabs[1]?.resumeCommand, "claude resume claude-456");
|
|
80
|
+
assert.equal(
|
|
81
|
+
report.tabs[1]?.restartCommand,
|
|
82
|
+
"cd '/Volumes/Code_2TB/code/anthropic-lab' && claude resume claude-456",
|
|
83
|
+
);
|
|
84
|
+
assert.equal(report.tabs[1]?.claudeSessionId, "claude-456");
|
|
85
|
+
assert.equal(report.tabs[2]?.title, "collaboration (2)");
|
|
86
|
+
assert.equal(report.tabs[2]?.codexSessionId, null);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("runWorkspaceTabs prints JSON without launch commands", async () => {
|
|
90
|
+
const tempDir = await makeTempDir();
|
|
91
|
+
const manifestPath = path.join(tempDir, "workspace.json");
|
|
92
|
+
await fs.writeFile(
|
|
93
|
+
manifestPath,
|
|
94
|
+
`${JSON.stringify(
|
|
95
|
+
{
|
|
96
|
+
version: "1",
|
|
97
|
+
workspaceId: "orp-main",
|
|
98
|
+
title: "ORP Main",
|
|
99
|
+
tabs: [
|
|
100
|
+
{
|
|
101
|
+
title: "orp",
|
|
102
|
+
path: "/Volumes/Code_2TB/code/orp",
|
|
103
|
+
resumeCommand: "claude resume claude-999",
|
|
104
|
+
resumeTool: "claude",
|
|
105
|
+
resumeSessionId: "claude-999",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
title: "web",
|
|
109
|
+
path: "/Volumes/Code_2TB/code/orp-web-app",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
null,
|
|
114
|
+
2,
|
|
115
|
+
)}\n`,
|
|
116
|
+
"utf8",
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const { code, stdout } = await captureStdout(() => runWorkspaceTabs(["--workspace-file", manifestPath, "--json"]));
|
|
120
|
+
const parsed = JSON.parse(stdout);
|
|
121
|
+
|
|
122
|
+
assert.equal(code, 0);
|
|
123
|
+
assert.equal(parsed.workspaceId, "orp-main");
|
|
124
|
+
assert.equal(parsed.tabCount, 2);
|
|
125
|
+
assert.equal(parsed.tabs[0]?.title, "orp");
|
|
126
|
+
assert.equal(parsed.tabs[0]?.resumeCommand, "claude resume claude-999");
|
|
127
|
+
assert.equal(parsed.tabs[0]?.restartCommand, "cd '/Volumes/Code_2TB/code/orp' && claude resume claude-999");
|
|
128
|
+
assert.equal(parsed.tabs[0]?.claudeSessionId, "claude-999");
|
|
129
|
+
assert.equal(parsed.tabs[1]?.title, "web");
|
|
130
|
+
assert.ok(!stdout.includes('"command"'));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("buildWorkspaceTabsReport canonicalizes Claude resume commands from tool and session metadata", () => {
|
|
134
|
+
const parsed = parseWorkspaceSource({
|
|
135
|
+
sourceType: "workspace-file",
|
|
136
|
+
sourceLabel: "/tmp/workspace.json",
|
|
137
|
+
title: "workspace",
|
|
138
|
+
workspaceManifest: {
|
|
139
|
+
version: "1",
|
|
140
|
+
workspaceId: "orp-main",
|
|
141
|
+
tabs: [
|
|
142
|
+
{
|
|
143
|
+
title: "anthropic-lab",
|
|
144
|
+
path: "/Volumes/Code_2TB/code/anthropic-lab",
|
|
145
|
+
resumeTool: "claude",
|
|
146
|
+
resumeSessionId: "claude-456",
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
notes: "",
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const report = buildWorkspaceTabsReport(
|
|
154
|
+
{
|
|
155
|
+
sourceType: "workspace-file",
|
|
156
|
+
sourceLabel: "/tmp/workspace.json",
|
|
157
|
+
title: "workspace",
|
|
158
|
+
},
|
|
159
|
+
parsed,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
assert.equal(report.tabs[0]?.resumeCommand, "claude --resume claude-456");
|
|
163
|
+
assert.equal(
|
|
164
|
+
report.tabs[0]?.restartCommand,
|
|
165
|
+
"cd '/Volumes/Code_2TB/code/anthropic-lab' && claude --resume claude-456",
|
|
166
|
+
);
|
|
167
|
+
assert.equal(report.tabs[0]?.claudeSessionId, "claude-456");
|
|
168
|
+
});
|
|
@@ -542,7 +542,16 @@ def _gather_metadata(model: str) -> dict[str, Any]:
|
|
|
542
542
|
package_version = _read_json(REPO_ROOT / "package.json")["version"]
|
|
543
543
|
commit = subprocess.run(["git", "rev-parse", "HEAD"], cwd=str(REPO_ROOT), capture_output=True, text=True, check=True).stdout.strip()
|
|
544
544
|
branch = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=str(REPO_ROOT), capture_output=True, text=True, check=True).stdout.strip()
|
|
545
|
-
|
|
545
|
+
try:
|
|
546
|
+
codex_version = subprocess.run(
|
|
547
|
+
["codex", "--version"],
|
|
548
|
+
cwd=str(REPO_ROOT),
|
|
549
|
+
capture_output=True,
|
|
550
|
+
text=True,
|
|
551
|
+
check=True,
|
|
552
|
+
).stdout.strip()
|
|
553
|
+
except (FileNotFoundError, subprocess.CalledProcessError):
|
|
554
|
+
codex_version = "unavailable"
|
|
546
555
|
return {
|
|
547
556
|
"generated_at_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
548
557
|
"repo_commit": commit,
|
|
@@ -51,7 +51,16 @@ def _gather_metadata(model: str, repeats: int) -> dict[str, Any]:
|
|
|
51
51
|
package_version = json.loads((REPO_ROOT / "package.json").read_text(encoding="utf-8"))["version"]
|
|
52
52
|
commit = subprocess.run(["git", "rev-parse", "HEAD"], cwd=str(REPO_ROOT), capture_output=True, text=True, check=True).stdout.strip()
|
|
53
53
|
branch = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=str(REPO_ROOT), capture_output=True, text=True, check=True).stdout.strip()
|
|
54
|
-
|
|
54
|
+
try:
|
|
55
|
+
codex_version = subprocess.run(
|
|
56
|
+
["codex", "--version"],
|
|
57
|
+
cwd=str(REPO_ROOT),
|
|
58
|
+
capture_output=True,
|
|
59
|
+
text=True,
|
|
60
|
+
check=True,
|
|
61
|
+
).stdout.strip()
|
|
62
|
+
except (FileNotFoundError, subprocess.CalledProcessError):
|
|
63
|
+
codex_version = "unavailable"
|
|
55
64
|
return {
|
|
56
65
|
"generated_at_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
57
66
|
"repo_commit": commit,
|
|
@@ -276,7 +276,16 @@ def _gather_metadata(model: str) -> dict[str, Any]:
|
|
|
276
276
|
package_version = json.loads((REPO_ROOT / "package.json").read_text(encoding="utf-8"))["version"]
|
|
277
277
|
commit = subprocess.run(["git", "rev-parse", "HEAD"], cwd=str(REPO_ROOT), capture_output=True, text=True, check=True).stdout.strip()
|
|
278
278
|
branch = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=str(REPO_ROOT), capture_output=True, text=True, check=True).stdout.strip()
|
|
279
|
-
|
|
279
|
+
try:
|
|
280
|
+
codex_version = subprocess.run(
|
|
281
|
+
["codex", "--version"],
|
|
282
|
+
cwd=str(REPO_ROOT),
|
|
283
|
+
capture_output=True,
|
|
284
|
+
text=True,
|
|
285
|
+
check=True,
|
|
286
|
+
).stdout.strip()
|
|
287
|
+
except (FileNotFoundError, subprocess.CalledProcessError):
|
|
288
|
+
codex_version = "unavailable"
|
|
280
289
|
return {
|
|
281
290
|
"generated_at_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
282
291
|
"repo_commit": commit,
|
|
@@ -278,7 +278,16 @@ def _gather_metadata(model: str) -> dict[str, Any]:
|
|
|
278
278
|
package_version = json.loads((REPO_ROOT / "package.json").read_text(encoding="utf-8"))["version"]
|
|
279
279
|
commit = subprocess.run(["git", "rev-parse", "HEAD"], cwd=str(REPO_ROOT), capture_output=True, text=True, check=True).stdout.strip()
|
|
280
280
|
branch = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=str(REPO_ROOT), capture_output=True, text=True, check=True).stdout.strip()
|
|
281
|
-
|
|
281
|
+
try:
|
|
282
|
+
codex_version = subprocess.run(
|
|
283
|
+
["codex", "--version"],
|
|
284
|
+
cwd=str(REPO_ROOT),
|
|
285
|
+
capture_output=True,
|
|
286
|
+
text=True,
|
|
287
|
+
check=True,
|
|
288
|
+
).stdout.strip()
|
|
289
|
+
except (FileNotFoundError, subprocess.CalledProcessError):
|
|
290
|
+
codex_version = "unavailable"
|
|
282
291
|
return {
|
|
283
292
|
"generated_at_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
284
293
|
"repo_commit": commit,
|