open-research-protocol 0.4.16 → 0.4.18

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.
@@ -97,6 +97,32 @@ test("parseWorkspaceAddTabArgs accepts explicit resume metadata", () => {
97
97
  assert.equal(parsed.json, true);
98
98
  });
99
99
 
100
+ test("parseWorkspaceAddTabArgs resolves --here and --current-codex", () => {
101
+ const originalThreadId = process.env.CODEX_THREAD_ID;
102
+ const originalCwd = process.cwd();
103
+ process.env.CODEX_THREAD_ID = "019d4f24-c8ba-78b2-a726-48b1ce9f0fe9";
104
+ process.chdir("/Volumes/Code_2TB/code/orp");
105
+ try {
106
+ const parsed = parseWorkspaceAddTabArgs([
107
+ "main",
108
+ "--here",
109
+ "--current-codex",
110
+ ]);
111
+
112
+ assert.equal(parsed.ideaId, "main");
113
+ assert.equal(parsed.path, "/Volumes/Code_2TB/code/orp");
114
+ assert.equal(parsed.resumeTool, "codex");
115
+ assert.equal(parsed.resumeSessionId, "019d4f24-c8ba-78b2-a726-48b1ce9f0fe9");
116
+ } finally {
117
+ process.chdir(originalCwd);
118
+ if (originalThreadId == null) {
119
+ delete process.env.CODEX_THREAD_ID;
120
+ } else {
121
+ process.env.CODEX_THREAD_ID = originalThreadId;
122
+ }
123
+ }
124
+ });
125
+
100
126
  test("parseWorkspaceCreateArgs validates slug titles and optional seed metadata", () => {
101
127
  const parsed = parseWorkspaceCreateArgs([
102
128
  "main-cody-1",
@@ -144,6 +170,63 @@ test("addTabToManifest canonicalizes Claude resume commands from tool plus sessi
144
170
  assert.equal(result.manifest.tabs[2]?.sessionId, "claude-456");
145
171
  });
146
172
 
173
+ test("addTabToManifest updates an existing matching tab instead of appending a duplicate", () => {
174
+ const result = addTabToManifest(sampleManifest(), {
175
+ path: "/Volumes/Code_2TB/code/orp",
176
+ title: "orp",
177
+ resumeTool: "codex",
178
+ resumeSessionId: "019d4f24-c8ba-78b2-a726-48b1ce9f0fe9",
179
+ });
180
+
181
+ assert.equal(result.added, false);
182
+ assert.equal(result.updated, true);
183
+ assert.equal(result.mutation, "updated");
184
+ assert.equal(result.manifest.tabs.length, 2);
185
+ assert.equal(result.manifest.tabs[0]?.resumeCommand, "codex resume 019d4f24-c8ba-78b2-a726-48b1ce9f0fe9");
186
+ });
187
+
188
+ test("addTabToManifest preserves existing resume metadata when no new resume is provided", () => {
189
+ const result = addTabToManifest(sampleManifest(), {
190
+ path: "/Volumes/Code_2TB/code/frg-site",
191
+ title: "frg-site",
192
+ });
193
+
194
+ assert.equal(result.added, false);
195
+ assert.equal(result.updated, false);
196
+ assert.equal(result.unchanged, true);
197
+ assert.equal(result.mutation, "unchanged");
198
+ assert.equal(result.manifest.tabs.length, 2);
199
+ assert.equal(result.manifest.tabs[1]?.resumeCommand, "codex resume 019d348d-5031-78e1-9840-a66deaac33ae");
200
+ });
201
+
202
+ test("addTabToManifest asks for a title when multiple saved tabs share a path", () => {
203
+ const manifest = normalizeWorkspaceManifest({
204
+ version: "1",
205
+ workspaceId: "main-cody-1",
206
+ title: "main-cody-1",
207
+ tabs: [
208
+ {
209
+ title: "longevity-research",
210
+ path: "/Volumes/Code_2TB/code/longevity-research",
211
+ },
212
+ {
213
+ title: "longevity-research (2)",
214
+ path: "/Volumes/Code_2TB/code/longevity-research",
215
+ },
216
+ ],
217
+ });
218
+
219
+ assert.throws(
220
+ () =>
221
+ addTabToManifest(manifest, {
222
+ path: "/Volumes/Code_2TB/code/longevity-research",
223
+ resumeTool: "codex",
224
+ resumeSessionId: "019d4f24-c8ba-78b2-a726-48b1ce9f0fe9",
225
+ }),
226
+ /Multiple saved tabs already use this path/,
227
+ );
228
+ });
229
+
147
230
  test("removeTabsFromManifest can target a saved tab by path and resume session id", () => {
148
231
  const result = removeTabsFromManifest(sampleManifest(), {
149
232
  path: "/Volumes/Code_2TB/code/frg-site",
@@ -186,6 +269,42 @@ test("runWorkspaceAddTab updates a local workspace manifest file", async () => {
186
269
  });
187
270
  });
188
271
 
272
+ test("runWorkspaceAddTab upserts an existing tab and returns the rendered recovery command", async () => {
273
+ await withTempConfigHome(async () => {
274
+ const tempDir = await makeTempDir();
275
+ const manifestPath = path.join(tempDir, "workspace.json");
276
+ await fs.writeFile(manifestPath, `${JSON.stringify(sampleManifest(), null, 2)}\n`, "utf8");
277
+
278
+ const { code, stdout } = await captureStdout(() =>
279
+ runWorkspaceAddTab([
280
+ "--workspace-file",
281
+ manifestPath,
282
+ "--path",
283
+ "/Volumes/Code_2TB/code/orp",
284
+ "--resume-tool",
285
+ "codex",
286
+ "--resume-session-id",
287
+ "019d4f24-c8ba-78b2-a726-48b1ce9f0fe9",
288
+ "--json",
289
+ ]),
290
+ );
291
+ const payload = JSON.parse(stdout);
292
+ const saved = JSON.parse(await fs.readFile(manifestPath, "utf8"));
293
+
294
+ assert.equal(code, 0);
295
+ assert.equal(payload.action, "add-tab");
296
+ assert.equal(payload.mutation, "updated");
297
+ assert.equal(payload.tabCount, 2);
298
+ assert.equal(payload.tab.resumeCommand, "codex resume 019d4f24-c8ba-78b2-a726-48b1ce9f0fe9");
299
+ assert.equal(
300
+ payload.tab.restartCommand,
301
+ "cd '/Volumes/Code_2TB/code/orp' && codex resume 019d4f24-c8ba-78b2-a726-48b1ce9f0fe9",
302
+ );
303
+ assert.equal(saved.tabs.length, 2);
304
+ assert.equal(saved.tabs[0]?.resumeCommand, "codex resume 019d4f24-c8ba-78b2-a726-48b1ce9f0fe9");
305
+ });
306
+ });
307
+
189
308
  test("runWorkspaceRemoveTab updates a local workspace manifest file", async () => {
190
309
  await withTempConfigHome(async () => {
191
310
  const tempDir = await makeTempDir();
@@ -29,10 +29,16 @@ test("runOrpWorkspaceCommand shows the ledger-first help surface", async () => {
29
29
  const { code, stdout } = await captureStdout(() => runOrpWorkspaceCommand(["-h"]));
30
30
 
31
31
  assert.equal(code, 0);
32
+ assert.match(stdout, /orp workspace list \[--json\]/);
33
+ assert.match(stdout, /orp workspace add-tab <name-or-id>/);
34
+ assert.match(stdout, /--here/);
35
+ assert.match(stdout, /--current-codex/);
36
+ assert.match(stdout, /orp workspace remove-tab <name-or-id>/);
32
37
  assert.match(stdout, /orp workspace ledger <name-or-id>/);
33
38
  assert.match(stdout, /orp workspace ledger add <name-or-id>/);
34
39
  assert.match(stdout, /orp workspace ledger remove <name-or-id>/);
35
40
  assert.match(stdout, /orp workspace tabs <name-or-id>/);
41
+ assert.match(stdout, /Compatibility alias for the same tabs\/add\/remove ledger flow/);
36
42
  });
37
43
 
38
44
  test("runOrpWorkspaceCommand routes ledger help to the tabs help surface", async () => {
@@ -75,12 +75,12 @@ SCENES = [
75
75
  "output_font": "small",
76
76
  "line_height": 30,
77
77
  "output": [
78
- ("ORP 0.4.13", ACCENT),
78
+ ("ORP 0.4.16", ACCENT),
79
79
  ("Agent-first CLI for workspace ledgers, secrets, scheduling, and research workflows.", INK),
80
80
  ("Repo", SKY),
81
- (" root: /Volumes/Code_2TB/code/orp", INK),
81
+ (" root: ~/code/open-research-protocol", INK),
82
82
  (" config: orp.yml (missing)", ACCENT),
83
- (" git: yes, branch=main, commit=4cde66c", SOFT),
83
+ (" git: yes, branch=main, commit=1a2b3c4", SOFT),
84
84
  ("Daily Loop", SKY),
85
85
  (" orp workspace tabs main", ACCENT),
86
86
  (' orp secrets add --alias <alias> --label "<label>" --provider <provider>', INK),