@ricsam/r5dctl 0.0.36 → 0.0.38

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/cjs/cli.cjs CHANGED
@@ -49,6 +49,8 @@ __export(cli_exports, {
49
49
  renderProcessHistory: () => renderProcessHistory,
50
50
  renderProcessInspection: () => renderProcessInspection,
51
51
  renderProcessList: () => renderProcessList,
52
+ renderWorkspaceStatus: () => renderWorkspaceStatus,
53
+ renderWorkspaceSync: () => renderWorkspaceSync,
52
54
  resolveCommandExecution: () => resolveCommandExecution,
53
55
  runR5dctlCli: () => runR5dctlCli,
54
56
  summarizeEnvData: () => summarizeEnvData
@@ -132,6 +134,17 @@ const SHARED_HELP_ENTRIES = [
132
134
  },
133
135
  { section: "auth", usage: "auth api-key list", description: "List API keys for the current account." },
134
136
  { section: "auth", usage: "auth api-key revoke <key-id>", description: "Revoke an API key." },
137
+ { section: "workspace", usage: "workspace status", description: "Inspect the per-user canonical workspace and active remediation." },
138
+ {
139
+ section: "workspace",
140
+ usage: "workspace sync [--worker <label>]",
141
+ description: "Publish a connected worker into the canonical workspace now."
142
+ },
143
+ {
144
+ section: "workspace",
145
+ usage: "workspace reset [--worker <label>] --confirm",
146
+ description: "Destructively discard a blocked remediation candidate and restore canonical state."
147
+ },
135
148
  { section: "projects", usage: "get projects", description: "List projects you can access." },
136
149
  { section: "projects", usage: "describe project <namespace/name|id>", description: "Show details for a project." },
137
150
  { section: "projects", usage: "update project <namespace/name|id> --mode <greenfield|prod>", description: "Update project settings." },
@@ -158,6 +171,11 @@ const SHARED_HELP_ENTRIES = [
158
171
  usage: "-p <project> get sessions [--branch <branch>]",
159
172
  description: "List sessions for a project, optionally filtered by branch."
160
173
  },
174
+ {
175
+ section: "sessions",
176
+ usage: "sessions recent [--limit <n>]",
177
+ description: "List the most recently active sessions across your projects."
178
+ },
161
179
  {
162
180
  section: "sessions",
163
181
  usage: "-p <project> create session -b <branch> [--name <name>]",
@@ -229,6 +247,7 @@ const SHARED_HELP_ENTRIES = [
229
247
  ];
230
248
  const HELP_SECTION_ORDER = [
231
249
  "auth",
250
+ "workspace",
232
251
  "projects",
233
252
  "branches",
234
253
  "envs",
@@ -240,6 +259,7 @@ const HELP_SECTION_ORDER = [
240
259
  ];
241
260
  const HELP_SECTION_TITLES = {
242
261
  auth: "Auth",
262
+ workspace: "Canonical workspace",
243
263
  projects: "Projects",
244
264
  branches: "Branches",
245
265
  envs: "Environment variables",
@@ -850,6 +870,35 @@ function parsePsHistoryArgs(args) {
850
870
  }
851
871
  return { ...parsePsListArgs(filterArgs), ...limit !== void 0 ? { limit } : {} };
852
872
  }
873
+ function parseWorkspaceWorkerArgs(args, options = {}) {
874
+ let worker;
875
+ let confirmed = false;
876
+ for (let index = 0; index < args.length; index += 1) {
877
+ const arg = args[index];
878
+ if (arg === "--worker" || arg?.startsWith("--worker=")) {
879
+ if (worker) throw new Error("Use only one --worker value");
880
+ worker = requireValue(arg === "--worker" ? args[++index] : parseLongOptionWithEquals(arg, "--worker"), "Missing value for --worker");
881
+ continue;
882
+ }
883
+ if (arg === "--confirm" && options.allowConfirm) {
884
+ confirmed = true;
885
+ continue;
886
+ }
887
+ throw new Error(`Unknown workspace argument: ${arg ?? ""}`.trim());
888
+ }
889
+ if (options.allowConfirm && !confirmed) throw new Error("workspace reset requires --confirm");
890
+ return { ...worker ? { worker } : {}, ...confirmed ? { confirmed: true } : {} };
891
+ }
892
+ function parseRecentSessionArgs(args) {
893
+ if (args.length === 0) return {};
894
+ if (args.length > 2 || args[0] !== "--limit" && !args[0]?.startsWith("--limit=")) {
895
+ throw new Error(`Unknown sessions recent argument: ${args[0] ?? ""}`.trim());
896
+ }
897
+ const raw = args[0] === "--limit" ? args[1] : parseLongOptionWithEquals(args[0], "--limit");
898
+ const limit = Number(requireValue(raw, "Missing value for --limit"));
899
+ if (!Number.isInteger(limit) || limit < 1 || limit > 200) throw new Error("--limit must be an integer between 1 and 200");
900
+ return { limit };
901
+ }
853
902
  async function openInBrowser(url) {
854
903
  if (process.platform === "darwin") {
855
904
  (0, import_node_child_process.spawn)("open", [url], { stdio: "ignore", detached: true }).unref();
@@ -988,6 +1037,29 @@ function renderSessionList(sessions) {
988
1037
  return `${sessions.map((session) => `${session.id} ${session.branchName} ${session.name ?? "(unnamed)"}`).join("\n")}
989
1038
  `;
990
1039
  }
1040
+ function renderRecentSessionList(sessions) {
1041
+ if (sessions.length === 0) return "No recent sessions.\n";
1042
+ return `${sessions.map((session) => `${session.id} ${session.projectPath}/${session.branchName} ${session.name ?? "(unnamed)"}`).join("\n")}
1043
+ `;
1044
+ }
1045
+ function renderWorkspaceStatus(status) {
1046
+ const lines = [
1047
+ `Workspace HEAD: ${status.head ?? "(not initialized)"}`,
1048
+ `Latest sync: ${status.latestSync ? `${status.latestSync.outcome} on ${status.latestSync.workerLabel}` : "(none)"}`,
1049
+ `Remediation: ${status.incident ? `${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`
1050
+ ];
1051
+ return `${lines.join("\n")}
1052
+ `;
1053
+ }
1054
+ function renderWorkspaceSync(result) {
1055
+ const head = result.publishedHead ?? result.candidateHead ?? result.startingHead ?? "(none)";
1056
+ const discarded = result.discardedPaths.length > 0 ? `
1057
+ Discarded paths: ${result.discardedPaths.join(", ")}` : "";
1058
+ const error = result.error ? `
1059
+ Error: ${result.error}` : "";
1060
+ return `Workspace sync ${result.outcome} on ${result.workerLabel}; HEAD ${head}; ${result.diffSizeBytes} bytes.${discarded}${error}
1061
+ `;
1062
+ }
991
1063
  function renderSessionDescription(session) {
992
1064
  return [`Session: ${session.id}`, `Project: ${session.projectPath}`, `Branch: ${session.branchName}`].join("\n") + "\n";
993
1065
  }
@@ -1055,6 +1127,9 @@ function renderProcessInspection(process2) {
1055
1127
  `Completed: ${process2.completedAt ?? "(running)"}`,
1056
1128
  `Exit code: ${process2.exitCode ?? "(none)"}`,
1057
1129
  `Error: ${process2.error ?? "(none)"}`,
1130
+ `Workspace sync: ${process2.workspaceSync ? `${process2.workspaceSync.outcome} (${process2.workspaceSync.attemptId})` : "(pending or not observed)"}`,
1131
+ ...process2.workspaceSync?.affectedProjects.length ? [`Repositories observed during synchronization: ${process2.workspaceSync.affectedProjects.join(", ")}`] : [],
1132
+ ...process2.workspaceSync?.discardedPaths.length ? [`Discarded paths: ${process2.workspaceSync.discardedPaths.join(", ")}`] : [],
1058
1133
  `Log: ${process2.logPath}`,
1059
1134
  "",
1060
1135
  `stdout tail${process2.stdoutTailTruncated ? " (truncated)" : ""}:`,
@@ -1334,6 +1409,23 @@ async function executeR5dctlCommand(client, json, args) {
1334
1409
  write(result, "API key revoked.\n");
1335
1410
  return;
1336
1411
  }
1412
+ if (first === "workspace" && second === "status") {
1413
+ if (args.length > 2) throw new Error(`Unexpected workspace status argument: ${args[2]}`);
1414
+ const status = await client.workspace.status();
1415
+ write(status, renderWorkspaceStatus(status));
1416
+ return;
1417
+ }
1418
+ if (first === "workspace" && second === "sync") {
1419
+ const result = await client.workspace.sync(parseWorkspaceWorkerArgs(args.slice(2)));
1420
+ write(result, renderWorkspaceSync(result));
1421
+ return;
1422
+ }
1423
+ if (first === "workspace" && second === "reset") {
1424
+ const parsed = parseWorkspaceWorkerArgs(args.slice(2), { allowConfirm: true });
1425
+ const result = await client.workspace.reset({ worker: parsed.worker, confirmed: true });
1426
+ write(result, renderWorkspaceSync(result));
1427
+ return;
1428
+ }
1337
1429
  if (first === "ps" && second === "list") {
1338
1430
  const processes = await client.processes.list(parsePsListArgs(args.slice(2)));
1339
1431
  write(processes, renderProcessList(processes));
@@ -1356,6 +1448,11 @@ async function executeR5dctlCommand(client, json, args) {
1356
1448
  `);
1357
1449
  return;
1358
1450
  }
1451
+ if (first === "sessions" && second === "recent") {
1452
+ const sessions = await client.sessions.recent(parseRecentSessionArgs(args.slice(2)));
1453
+ write(sessions, renderRecentSessionList(sessions));
1454
+ return;
1455
+ }
1359
1456
  if (first === "projects" && second === "list" || first === "get" && second === "projects") {
1360
1457
  const projects = await client.projects.list();
1361
1458
  write(projects, renderProjectList(projects));
@@ -1593,7 +1690,7 @@ function resolveCommandExecution(options, rest) {
1593
1690
  pluginArgs: [...normalizedRest, "--help"]
1594
1691
  };
1595
1692
  }
1596
- if (command === "projects" || command === "sessions") {
1693
+ if (command === "projects" || command === "sessions" || command === "workspace") {
1597
1694
  return {
1598
1695
  kind: "plugin",
1599
1696
  pluginArgs: normalizedRest
@@ -2135,6 +2232,8 @@ async function main(argv = process.argv.slice(2)) {
2135
2232
  renderProcessHistory,
2136
2233
  renderProcessInspection,
2137
2234
  renderProcessList,
2235
+ renderWorkspaceStatus,
2236
+ renderWorkspaceSync,
2138
2237
  resolveCommandExecution,
2139
2238
  runR5dctlCli,
2140
2239
  summarizeEnvData
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/cli.mjs CHANGED
@@ -79,6 +79,17 @@ const SHARED_HELP_ENTRIES = [
79
79
  },
80
80
  { section: "auth", usage: "auth api-key list", description: "List API keys for the current account." },
81
81
  { section: "auth", usage: "auth api-key revoke <key-id>", description: "Revoke an API key." },
82
+ { section: "workspace", usage: "workspace status", description: "Inspect the per-user canonical workspace and active remediation." },
83
+ {
84
+ section: "workspace",
85
+ usage: "workspace sync [--worker <label>]",
86
+ description: "Publish a connected worker into the canonical workspace now."
87
+ },
88
+ {
89
+ section: "workspace",
90
+ usage: "workspace reset [--worker <label>] --confirm",
91
+ description: "Destructively discard a blocked remediation candidate and restore canonical state."
92
+ },
82
93
  { section: "projects", usage: "get projects", description: "List projects you can access." },
83
94
  { section: "projects", usage: "describe project <namespace/name|id>", description: "Show details for a project." },
84
95
  { section: "projects", usage: "update project <namespace/name|id> --mode <greenfield|prod>", description: "Update project settings." },
@@ -105,6 +116,11 @@ const SHARED_HELP_ENTRIES = [
105
116
  usage: "-p <project> get sessions [--branch <branch>]",
106
117
  description: "List sessions for a project, optionally filtered by branch."
107
118
  },
119
+ {
120
+ section: "sessions",
121
+ usage: "sessions recent [--limit <n>]",
122
+ description: "List the most recently active sessions across your projects."
123
+ },
108
124
  {
109
125
  section: "sessions",
110
126
  usage: "-p <project> create session -b <branch> [--name <name>]",
@@ -176,6 +192,7 @@ const SHARED_HELP_ENTRIES = [
176
192
  ];
177
193
  const HELP_SECTION_ORDER = [
178
194
  "auth",
195
+ "workspace",
179
196
  "projects",
180
197
  "branches",
181
198
  "envs",
@@ -187,6 +204,7 @@ const HELP_SECTION_ORDER = [
187
204
  ];
188
205
  const HELP_SECTION_TITLES = {
189
206
  auth: "Auth",
207
+ workspace: "Canonical workspace",
190
208
  projects: "Projects",
191
209
  branches: "Branches",
192
210
  envs: "Environment variables",
@@ -797,6 +815,35 @@ function parsePsHistoryArgs(args) {
797
815
  }
798
816
  return { ...parsePsListArgs(filterArgs), ...limit !== void 0 ? { limit } : {} };
799
817
  }
818
+ function parseWorkspaceWorkerArgs(args, options = {}) {
819
+ let worker;
820
+ let confirmed = false;
821
+ for (let index = 0; index < args.length; index += 1) {
822
+ const arg = args[index];
823
+ if (arg === "--worker" || arg?.startsWith("--worker=")) {
824
+ if (worker) throw new Error("Use only one --worker value");
825
+ worker = requireValue(arg === "--worker" ? args[++index] : parseLongOptionWithEquals(arg, "--worker"), "Missing value for --worker");
826
+ continue;
827
+ }
828
+ if (arg === "--confirm" && options.allowConfirm) {
829
+ confirmed = true;
830
+ continue;
831
+ }
832
+ throw new Error(`Unknown workspace argument: ${arg ?? ""}`.trim());
833
+ }
834
+ if (options.allowConfirm && !confirmed) throw new Error("workspace reset requires --confirm");
835
+ return { ...worker ? { worker } : {}, ...confirmed ? { confirmed: true } : {} };
836
+ }
837
+ function parseRecentSessionArgs(args) {
838
+ if (args.length === 0) return {};
839
+ if (args.length > 2 || args[0] !== "--limit" && !args[0]?.startsWith("--limit=")) {
840
+ throw new Error(`Unknown sessions recent argument: ${args[0] ?? ""}`.trim());
841
+ }
842
+ const raw = args[0] === "--limit" ? args[1] : parseLongOptionWithEquals(args[0], "--limit");
843
+ const limit = Number(requireValue(raw, "Missing value for --limit"));
844
+ if (!Number.isInteger(limit) || limit < 1 || limit > 200) throw new Error("--limit must be an integer between 1 and 200");
845
+ return { limit };
846
+ }
800
847
  async function openInBrowser(url) {
801
848
  if (process.platform === "darwin") {
802
849
  spawn("open", [url], { stdio: "ignore", detached: true }).unref();
@@ -935,6 +982,29 @@ function renderSessionList(sessions) {
935
982
  return `${sessions.map((session) => `${session.id} ${session.branchName} ${session.name ?? "(unnamed)"}`).join("\n")}
936
983
  `;
937
984
  }
985
+ function renderRecentSessionList(sessions) {
986
+ if (sessions.length === 0) return "No recent sessions.\n";
987
+ return `${sessions.map((session) => `${session.id} ${session.projectPath}/${session.branchName} ${session.name ?? "(unnamed)"}`).join("\n")}
988
+ `;
989
+ }
990
+ function renderWorkspaceStatus(status) {
991
+ const lines = [
992
+ `Workspace HEAD: ${status.head ?? "(not initialized)"}`,
993
+ `Latest sync: ${status.latestSync ? `${status.latestSync.outcome} on ${status.latestSync.workerLabel}` : "(none)"}`,
994
+ `Remediation: ${status.incident ? `${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`
995
+ ];
996
+ return `${lines.join("\n")}
997
+ `;
998
+ }
999
+ function renderWorkspaceSync(result) {
1000
+ const head = result.publishedHead ?? result.candidateHead ?? result.startingHead ?? "(none)";
1001
+ const discarded = result.discardedPaths.length > 0 ? `
1002
+ Discarded paths: ${result.discardedPaths.join(", ")}` : "";
1003
+ const error = result.error ? `
1004
+ Error: ${result.error}` : "";
1005
+ return `Workspace sync ${result.outcome} on ${result.workerLabel}; HEAD ${head}; ${result.diffSizeBytes} bytes.${discarded}${error}
1006
+ `;
1007
+ }
938
1008
  function renderSessionDescription(session) {
939
1009
  return [`Session: ${session.id}`, `Project: ${session.projectPath}`, `Branch: ${session.branchName}`].join("\n") + "\n";
940
1010
  }
@@ -1002,6 +1072,9 @@ function renderProcessInspection(process2) {
1002
1072
  `Completed: ${process2.completedAt ?? "(running)"}`,
1003
1073
  `Exit code: ${process2.exitCode ?? "(none)"}`,
1004
1074
  `Error: ${process2.error ?? "(none)"}`,
1075
+ `Workspace sync: ${process2.workspaceSync ? `${process2.workspaceSync.outcome} (${process2.workspaceSync.attemptId})` : "(pending or not observed)"}`,
1076
+ ...process2.workspaceSync?.affectedProjects.length ? [`Repositories observed during synchronization: ${process2.workspaceSync.affectedProjects.join(", ")}`] : [],
1077
+ ...process2.workspaceSync?.discardedPaths.length ? [`Discarded paths: ${process2.workspaceSync.discardedPaths.join(", ")}`] : [],
1005
1078
  `Log: ${process2.logPath}`,
1006
1079
  "",
1007
1080
  `stdout tail${process2.stdoutTailTruncated ? " (truncated)" : ""}:`,
@@ -1281,6 +1354,23 @@ async function executeR5dctlCommand(client, json, args) {
1281
1354
  write(result, "API key revoked.\n");
1282
1355
  return;
1283
1356
  }
1357
+ if (first === "workspace" && second === "status") {
1358
+ if (args.length > 2) throw new Error(`Unexpected workspace status argument: ${args[2]}`);
1359
+ const status = await client.workspace.status();
1360
+ write(status, renderWorkspaceStatus(status));
1361
+ return;
1362
+ }
1363
+ if (first === "workspace" && second === "sync") {
1364
+ const result = await client.workspace.sync(parseWorkspaceWorkerArgs(args.slice(2)));
1365
+ write(result, renderWorkspaceSync(result));
1366
+ return;
1367
+ }
1368
+ if (first === "workspace" && second === "reset") {
1369
+ const parsed = parseWorkspaceWorkerArgs(args.slice(2), { allowConfirm: true });
1370
+ const result = await client.workspace.reset({ worker: parsed.worker, confirmed: true });
1371
+ write(result, renderWorkspaceSync(result));
1372
+ return;
1373
+ }
1284
1374
  if (first === "ps" && second === "list") {
1285
1375
  const processes = await client.processes.list(parsePsListArgs(args.slice(2)));
1286
1376
  write(processes, renderProcessList(processes));
@@ -1303,6 +1393,11 @@ async function executeR5dctlCommand(client, json, args) {
1303
1393
  `);
1304
1394
  return;
1305
1395
  }
1396
+ if (first === "sessions" && second === "recent") {
1397
+ const sessions = await client.sessions.recent(parseRecentSessionArgs(args.slice(2)));
1398
+ write(sessions, renderRecentSessionList(sessions));
1399
+ return;
1400
+ }
1306
1401
  if (first === "projects" && second === "list" || first === "get" && second === "projects") {
1307
1402
  const projects = await client.projects.list();
1308
1403
  write(projects, renderProjectList(projects));
@@ -1540,7 +1635,7 @@ function resolveCommandExecution(options, rest) {
1540
1635
  pluginArgs: [...normalizedRest, "--help"]
1541
1636
  };
1542
1637
  }
1543
- if (command === "projects" || command === "sessions") {
1638
+ if (command === "projects" || command === "sessions" || command === "workspace") {
1544
1639
  return {
1545
1640
  kind: "plugin",
1546
1641
  pluginArgs: normalizedRest
@@ -2081,6 +2176,8 @@ export {
2081
2176
  renderProcessHistory,
2082
2177
  renderProcessInspection,
2083
2178
  renderProcessList,
2179
+ renderWorkspaceStatus,
2180
+ renderWorkspaceSync,
2084
2181
  resolveCommandExecution,
2085
2182
  runR5dctlCli,
2086
2183
  summarizeEnvData
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "type": "module"
5
5
  }
@@ -1,4 +1,4 @@
1
- import { type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessRun, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
1
+ import { type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessRun, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
2
2
  export type GlobalOptions = {
3
3
  baseUrl?: string;
4
4
  json: boolean;
@@ -62,6 +62,8 @@ export declare function parseShellArgs(args: string[]): {
62
62
  };
63
63
  export declare function parsePsListArgs(args: string[]): R5dctlProcessListInput;
64
64
  export declare function parsePsHistoryArgs(args: string[]): R5dctlProcessListInput;
65
+ export declare function renderWorkspaceStatus(status: R5dctlWorkspaceStatus): string;
66
+ export declare function renderWorkspaceSync(result: R5dctlWorkspaceSyncResult): string;
65
67
  export declare function formatProcessAge(startedAt: string, nowMs?: number): string;
66
68
  export declare function renderProcessList(processes: R5dctlProcessRun[], nowMs?: number): string;
67
69
  export declare function renderProcessHistory(processes: R5dctlProcessRun[], nowMs?: number): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/cli.cjs",
6
6
  "module": "./dist/mjs/cli.mjs",
@@ -26,7 +26,7 @@
26
26
  "r5dctl": "dist/cjs/main.cjs"
27
27
  },
28
28
  "dependencies": {
29
- "@ricsam/r5d-api": "^0.0.36",
29
+ "@ricsam/r5d-api": "^0.0.38",
30
30
  "dotenv": "^17",
31
31
  "ws": "^8.18.3"
32
32
  },