@ricsam/r5dctl 0.0.137 → 0.0.138
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 +34 -5
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/shell.cjs +101 -210
- package/dist/mjs/cli.mjs +33 -5
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/shell.mjs +101 -199
- package/dist/types/cli.d.ts +3 -1
- package/dist/types/shell.d.ts +4 -4
- package/package.json +2 -2
package/dist/cjs/cli.cjs
CHANGED
|
@@ -92,6 +92,7 @@ __export(cli_exports, {
|
|
|
92
92
|
renderEnvValues: () => renderEnvValues,
|
|
93
93
|
renderProcessHistory: () => renderProcessHistory,
|
|
94
94
|
renderProcessInspection: () => renderProcessInspection,
|
|
95
|
+
renderProcessInventory: () => renderProcessInventory,
|
|
95
96
|
renderProcessList: () => renderProcessList,
|
|
96
97
|
renderProcessLog: () => renderProcessLog,
|
|
97
98
|
renderSessionClaimList: () => renderSessionClaimList,
|
|
@@ -1989,6 +1990,9 @@ function renderWorkspaceStatus(status) {
|
|
|
1989
1990
|
`Remediation: ${status.incident ? `${status.incident.kind} ${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`,
|
|
1990
1991
|
...status.workers.flatMap((worker) => [
|
|
1991
1992
|
`Worker ${worker.workerLabel}: ${worker.configured ? "configured" : "configuring"}; head ${worker.localHead ?? "(none)"}; pending checkouts ${worker.pendingCheckouts.length}`,
|
|
1993
|
+
...(worker.accountWorkspaces ?? []).map(
|
|
1994
|
+
(workspace) => ` Account workspace ${workspace.sessionId}: ${workspace.initialized ? "initialized" : "not initialized"}; head ${workspace.head ?? "(none)"}`
|
|
1995
|
+
),
|
|
1992
1996
|
...worker.hydrationRecovery ? [
|
|
1993
1997
|
` Interrupted hydration ${worker.hydrationRecovery.transactionId}: files and snapshots preserved; execution is fenced.`,
|
|
1994
1998
|
` Evidence: ${worker.hydrationRecovery.transactionPath}`,
|
|
@@ -2037,11 +2041,13 @@ function renderBranchList(branches) {
|
|
|
2037
2041
|
}
|
|
2038
2042
|
function renderWorkspaceSync(result) {
|
|
2039
2043
|
const head = result.publishedHead ?? result.localHead ?? result.startingHead ?? "(none)";
|
|
2044
|
+
const accountWorkspaces = result.affectedSessionIds?.length ? `
|
|
2045
|
+
Account workspaces changed: ${result.affectedSessionIds.join(", ")}` : "";
|
|
2040
2046
|
const discarded = result.discardedPaths.length > 0 ? `
|
|
2041
2047
|
Discarded paths: ${result.discardedPaths.join(", ")}` : "";
|
|
2042
2048
|
const error = result.error ? `
|
|
2043
2049
|
Error: ${result.error}` : "";
|
|
2044
|
-
return `Workspace sync ${result.outcome} on ${result.workerLabel}; HEAD ${head}; ${result.diffSizeBytes} bytes.${discarded}${error}
|
|
2050
|
+
return `Workspace sync ${result.outcome} on ${result.workerLabel}; HEAD ${head}; ${result.diffSizeBytes} bytes.${accountWorkspaces}${discarded}${error}
|
|
2045
2051
|
`;
|
|
2046
2052
|
}
|
|
2047
2053
|
function renderSessionDescription(session) {
|
|
@@ -2099,6 +2105,15 @@ function formatProcessLane(process2) {
|
|
|
2099
2105
|
function renderProcessHistory(processes, nowMs = Date.now()) {
|
|
2100
2106
|
return processes.length === 0 ? "No process history.\n" : renderProcessList(processes, nowMs);
|
|
2101
2107
|
}
|
|
2108
|
+
function renderProcessInventory(inventory, history = false, nowMs = Date.now()) {
|
|
2109
|
+
const rows = inventory.processes.length || inventory.complete ? history ? renderProcessHistory(inventory.processes, nowMs) : renderProcessList(inventory.processes, nowMs) : `No ${history ? "process history" : "active processes"} reported by available workers.
|
|
2110
|
+
`;
|
|
2111
|
+
if (inventory.complete) return rows;
|
|
2112
|
+
const unavailable = inventory.unavailableWorkers.map((worker) => `${worker.workerLabel ?? "default worker"} (${worker.sessionIds.length} conversations)`).join(", ");
|
|
2113
|
+
return `${rows}
|
|
2114
|
+
Process status unavailable: ${unavailable}. These workers may still have active processes; reconnect them before inspecting or stopping their commands.
|
|
2115
|
+
`;
|
|
2116
|
+
}
|
|
2102
2117
|
function renderProcessLog(log) {
|
|
2103
2118
|
const sections = [];
|
|
2104
2119
|
for (const [name, stream] of [
|
|
@@ -3051,13 +3066,25 @@ async function executeR5dctlCommand(client, json, args) {
|
|
|
3051
3066
|
return;
|
|
3052
3067
|
}
|
|
3053
3068
|
if (first === "ps" && second === "list") {
|
|
3054
|
-
const
|
|
3055
|
-
|
|
3069
|
+
const filters = parsePsListArgs(args.slice(2));
|
|
3070
|
+
if (json) {
|
|
3071
|
+
const processes = await client.processes.list(filters);
|
|
3072
|
+
write(processes, renderProcessList(processes));
|
|
3073
|
+
} else {
|
|
3074
|
+
const inventory = await client.processes.inventory(filters);
|
|
3075
|
+
write(inventory, renderProcessInventory(inventory));
|
|
3076
|
+
}
|
|
3056
3077
|
return;
|
|
3057
3078
|
}
|
|
3058
3079
|
if (first === "ps" && second === "history") {
|
|
3059
|
-
const
|
|
3060
|
-
|
|
3080
|
+
const filters = parsePsHistoryArgs(args.slice(2));
|
|
3081
|
+
if (json) {
|
|
3082
|
+
const processes = await client.processes.history(filters);
|
|
3083
|
+
write(processes, renderProcessHistory(processes));
|
|
3084
|
+
} else {
|
|
3085
|
+
const inventory = await client.processes.historyInventory(filters);
|
|
3086
|
+
write(inventory, renderProcessInventory(inventory, true));
|
|
3087
|
+
}
|
|
3061
3088
|
return;
|
|
3062
3089
|
}
|
|
3063
3090
|
if (first === "ps" && second === "inspect") {
|
|
@@ -4016,6 +4043,7 @@ async function runCommand(argv) {
|
|
|
4016
4043
|
credential: clientOptions.token ?? clientOptions.apiKey ?? "",
|
|
4017
4044
|
projectId: project.id,
|
|
4018
4045
|
branchName: options.branch,
|
|
4046
|
+
sessionId: options.session,
|
|
4019
4047
|
command: execution.command
|
|
4020
4048
|
});
|
|
4021
4049
|
}
|
|
@@ -4120,6 +4148,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
4120
4148
|
renderEnvValues,
|
|
4121
4149
|
renderProcessHistory,
|
|
4122
4150
|
renderProcessInspection,
|
|
4151
|
+
renderProcessInventory,
|
|
4123
4152
|
renderProcessList,
|
|
4124
4153
|
renderProcessLog,
|
|
4125
4154
|
renderSessionClaimList,
|
package/dist/cjs/package.json
CHANGED
package/dist/cjs/shell.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,229 +15,122 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
var shell_exports = {};
|
|
30
20
|
__export(shell_exports, {
|
|
31
|
-
createShellWebSocketUrl: () => createShellWebSocketUrl,
|
|
32
21
|
runR5dctlShell: () => runR5dctlShell
|
|
33
22
|
});
|
|
34
23
|
module.exports = __toCommonJS(shell_exports);
|
|
35
|
-
var
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return fallback;
|
|
39
|
-
}
|
|
40
|
-
return Math.max(1, Math.min(Math.floor(value), 500));
|
|
41
|
-
}
|
|
42
|
-
function createShellWebSocketUrl(baseUrl) {
|
|
43
|
-
const url = new URL("/ws", baseUrl);
|
|
44
|
-
if (url.protocol === "https:") {
|
|
45
|
-
url.protocol = "wss:";
|
|
46
|
-
} else if (url.protocol === "http:") {
|
|
47
|
-
url.protocol = "ws:";
|
|
48
|
-
} else {
|
|
49
|
-
throw new Error(`Unsupported r5d base URL protocol: ${url.protocol}`);
|
|
50
|
-
}
|
|
51
|
-
return url.toString();
|
|
52
|
-
}
|
|
53
|
-
function decodeMessage(data) {
|
|
54
|
-
if (typeof data === "string") {
|
|
55
|
-
return data;
|
|
56
|
-
}
|
|
57
|
-
if (Buffer.isBuffer(data)) {
|
|
58
|
-
return data.toString("utf8");
|
|
59
|
-
}
|
|
60
|
-
if (data instanceof ArrayBuffer) {
|
|
61
|
-
return Buffer.from(data).toString("utf8");
|
|
62
|
-
}
|
|
63
|
-
if (ArrayBuffer.isView(data)) {
|
|
64
|
-
return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString("utf8");
|
|
65
|
-
}
|
|
66
|
-
if (Array.isArray(data)) {
|
|
67
|
-
return Buffer.concat(data).toString("utf8");
|
|
68
|
-
}
|
|
69
|
-
return String(data);
|
|
70
|
-
}
|
|
24
|
+
var import_node_crypto = require("node:crypto");
|
|
25
|
+
var import_node_os = require("node:os");
|
|
26
|
+
const terminalSize = (value, fallback) => Number.isFinite(value) && value > 0 ? Math.min(500, Math.max(1, Math.floor(value))) : fallback;
|
|
71
27
|
async function runR5dctlShell(options) {
|
|
72
|
-
const stdin = options.stdin ?? process.stdin;
|
|
73
|
-
const
|
|
74
|
-
const signalTarget = options.signalTarget ?? process;
|
|
28
|
+
const stdin = options.stdin ?? process.stdin, stdout = options.stdout ?? process.stdout;
|
|
29
|
+
const signals = options.signalTarget ?? process;
|
|
75
30
|
const interactive = options.command === void 0;
|
|
76
|
-
if (interactive && !stdin.isTTY)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
31
|
+
if (interactive && !stdin.isTTY) throw new Error("Interactive shell requires a TTY. Use `shell -c <command>` for non-interactive use.");
|
|
32
|
+
if (!options.credential) throw new Error("Authentication required. Run `r5dctl auth login` or provide a token or API key.");
|
|
33
|
+
const url = new URL(
|
|
34
|
+
`/api/product/projects/${encodeURIComponent(options.projectId)}/branches/${encodeURIComponent(options.branchName)}/terminal`,
|
|
35
|
+
options.baseUrl
|
|
36
|
+
);
|
|
37
|
+
if (!["http:", "https:"].includes(url.protocol)) throw new Error("Unsupported r5d base URL protocol");
|
|
38
|
+
const performFetch = options.fetch ?? fetch;
|
|
39
|
+
async function request(body, query = "") {
|
|
40
|
+
const serialized = body === void 0 ? void 0 : JSON.stringify(body);
|
|
41
|
+
for (let attempt = 0; ; attempt++) {
|
|
42
|
+
let response;
|
|
43
|
+
try {
|
|
44
|
+
response = await performFetch(url + query, {
|
|
45
|
+
method: body === void 0 ? "GET" : "POST",
|
|
46
|
+
redirect: "error",
|
|
47
|
+
headers: {
|
|
48
|
+
Authorization: `Bearer ${options.credential}`,
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
...options.sessionId ? { "x-r5d-session-id": options.sessionId } : {}
|
|
51
|
+
},
|
|
52
|
+
body: serialized
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (attempt >= 3) throw error;
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * (attempt + 1)));
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (response.status >= 500 && attempt < 3) {
|
|
60
|
+
await response.body?.cancel();
|
|
61
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * (attempt + 1)));
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const result = await response.json();
|
|
65
|
+
if (!response.ok) throw new Error(result.error ?? `Terminal request failed (${response.status})`);
|
|
66
|
+
return result;
|
|
86
67
|
}
|
|
68
|
+
}
|
|
69
|
+
const opened = await request({
|
|
70
|
+
method: "open",
|
|
71
|
+
id: (0, import_node_crypto.randomUUID)(),
|
|
72
|
+
cols: terminalSize(stdout.columns, 80),
|
|
73
|
+
rows: terminalSize(stdout.rows, 24),
|
|
74
|
+
...options.command === void 0 ? {} : { command: options.command }
|
|
87
75
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
send({ type: "shell_pty_close", ptyId });
|
|
129
|
-
ptyId = void 0;
|
|
130
|
-
}
|
|
131
|
-
finish({ exitCode: 143 });
|
|
132
|
-
};
|
|
133
|
-
const cleanup = () => {
|
|
134
|
-
socket.off("message", onMessage);
|
|
135
|
-
socket.off("error", onSocketError);
|
|
136
|
-
socket.off("close", onSocketClose);
|
|
137
|
-
if (inputAttached) {
|
|
138
|
-
stdin.off("data", onInput);
|
|
139
|
-
stdin.off("end", onInputEnd);
|
|
140
|
-
}
|
|
141
|
-
signalTarget.off("SIGWINCH", sendResize);
|
|
142
|
-
signalTarget.off("SIGINT", onSigint);
|
|
143
|
-
signalTarget.off("SIGTERM", onSigterm);
|
|
144
|
-
if (stdin.isTTY && stdin.setRawMode) {
|
|
145
|
-
stdin.setRawMode(priorRawMode);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
const finish = (result) => {
|
|
149
|
-
if (settled) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
settled = true;
|
|
153
|
-
cleanup();
|
|
154
|
-
if (socket.readyState === import_ws.default.OPEN || socket.readyState === import_ws.default.CONNECTING) {
|
|
155
|
-
socket.close(1e3, "r5dctl shell finished");
|
|
156
|
-
}
|
|
157
|
-
if ("error" in result) {
|
|
158
|
-
reject(result.error);
|
|
159
|
-
} else {
|
|
160
|
-
resolve(result.exitCode);
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
function attachInput() {
|
|
164
|
-
if (inputAttached) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
inputAttached = true;
|
|
168
|
-
if (stdin.isTTY && stdin.setRawMode) {
|
|
169
|
-
stdin.setRawMode(true);
|
|
170
|
-
}
|
|
171
|
-
stdin.on("data", onInput);
|
|
172
|
-
stdin.on("end", onInputEnd);
|
|
173
|
-
signalTarget.on("SIGWINCH", sendResize);
|
|
174
|
-
signalTarget.on("SIGTERM", onSigterm);
|
|
175
|
-
if (!stdin.isTTY) {
|
|
176
|
-
signalTarget.on("SIGINT", onSigint);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
function onMessage(data) {
|
|
180
|
-
let message;
|
|
181
|
-
try {
|
|
182
|
-
const parsed = JSON.parse(decodeMessage(data));
|
|
183
|
-
if (!parsed || typeof parsed !== "object" || !("type" in parsed)) {
|
|
184
|
-
throw new Error("Invalid message");
|
|
76
|
+
if (!opened.ptyId) throw new Error("Terminal did not acknowledge its durable run identity");
|
|
77
|
+
const ptyId = opened.ptyId, priorRaw = stdin.isRaw ?? false;
|
|
78
|
+
let pending = Promise.resolve(), failure, stopped = false;
|
|
79
|
+
const send = (command) => {
|
|
80
|
+
const body = { ...command, id: (0, import_node_crypto.randomUUID)(), ptyId };
|
|
81
|
+
pending = pending.then(() => request(body)).catch((error) => {
|
|
82
|
+
failure = error;
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
const input = (chunk) => send({ method: "input", data: Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk });
|
|
86
|
+
const inputEnd = () => {
|
|
87
|
+
if (!interactive) send({ method: "input", data: "" });
|
|
88
|
+
};
|
|
89
|
+
const resize = () => send({ method: "resize", cols: terminalSize(stdout.columns, 80), rows: terminalSize(stdout.rows, 24) });
|
|
90
|
+
const interrupt = () => send({ method: "input", data: "" });
|
|
91
|
+
const terminate = () => {
|
|
92
|
+
send({ method: "close" });
|
|
93
|
+
stopped = true;
|
|
94
|
+
};
|
|
95
|
+
try {
|
|
96
|
+
if (stdin.isTTY && stdin.setRawMode) stdin.setRawMode(true);
|
|
97
|
+
stdin.on("data", input);
|
|
98
|
+
stdin.on("end", inputEnd);
|
|
99
|
+
signals.on("SIGWINCH", resize);
|
|
100
|
+
signals.on("SIGTERM", terminate);
|
|
101
|
+
if (!stdin.isTTY) signals.on("SIGINT", interrupt);
|
|
102
|
+
let cursor = 0;
|
|
103
|
+
while (!stopped) {
|
|
104
|
+
if (failure) throw failure;
|
|
105
|
+
const page = await request(
|
|
106
|
+
void 0,
|
|
107
|
+
`?ptyId=${encodeURIComponent(ptyId)}&after=${cursor}`
|
|
108
|
+
);
|
|
109
|
+
if (!Number.isSafeInteger(page.nextCursor) || page.nextCursor < cursor) throw new Error("Invalid terminal output cursor");
|
|
110
|
+
for (const event of page.events) {
|
|
111
|
+
if (event.type === "output") stdout.write(event.data);
|
|
112
|
+
else if (event.type === "exit") {
|
|
113
|
+
await pending;
|
|
114
|
+
if (failure) throw failure;
|
|
115
|
+
return event.exitCode ?? (event.signal ? 128 + (typeof event.signal === "number" ? event.signal : import_node_os.constants.signals[event.signal] ?? 0) : 1);
|
|
185
116
|
}
|
|
186
|
-
message = parsed;
|
|
187
|
-
} catch {
|
|
188
|
-
finish({ error: new Error("Received an invalid response from r5d.dev") });
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
if (message.type === "connected") {
|
|
192
|
-
if (!openRequested) {
|
|
193
|
-
openRequested = true;
|
|
194
|
-
send({
|
|
195
|
-
type: "shell_pty_open",
|
|
196
|
-
projectId: options.projectId,
|
|
197
|
-
branchName: options.branchName,
|
|
198
|
-
cols: clampTerminalSize(stdout.columns, 80),
|
|
199
|
-
rows: clampTerminalSize(stdout.rows, 24),
|
|
200
|
-
...options.command === void 0 ? {} : { command: options.command }
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
if (message.type === "shell_pty_opened") {
|
|
206
|
-
ptyId = message.ptyId;
|
|
207
|
-
attachInput();
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (message.type === "shell_pty_output") {
|
|
211
|
-
if (message.ptyId === ptyId) {
|
|
212
|
-
stdout.write(message.data);
|
|
213
|
-
}
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
if (message.type === "shell_pty_exit") {
|
|
217
|
-
if (message.ptyId === ptyId) {
|
|
218
|
-
ptyId = void 0;
|
|
219
|
-
finish({ exitCode: message.exitCode });
|
|
220
|
-
}
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
if (message.type === "shell_pty_error" && (!message.ptyId || message.ptyId === ptyId)) {
|
|
224
|
-
finish({ error: new Error(message.message) });
|
|
225
117
|
}
|
|
118
|
+
cursor = page.nextCursor;
|
|
119
|
+
await new Promise((resolve) => setTimeout(resolve, options.pollIntervalMs ?? 150));
|
|
226
120
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
socket.on("close", onSocketClose);
|
|
239
|
-
});
|
|
121
|
+
await pending;
|
|
122
|
+
if (failure) throw failure;
|
|
123
|
+
return 143;
|
|
124
|
+
} finally {
|
|
125
|
+
stdin.off("data", input);
|
|
126
|
+
stdin.off("end", inputEnd);
|
|
127
|
+
signals.off("SIGWINCH", resize);
|
|
128
|
+
signals.off("SIGTERM", terminate);
|
|
129
|
+
signals.off("SIGINT", interrupt);
|
|
130
|
+
if (stdin.isTTY && stdin.setRawMode) stdin.setRawMode(priorRaw);
|
|
131
|
+
}
|
|
240
132
|
}
|
|
241
133
|
// Annotate the CommonJS export names for ESM import in node:
|
|
242
134
|
0 && (module.exports = {
|
|
243
|
-
createShellWebSocketUrl,
|
|
244
135
|
runR5dctlShell
|
|
245
136
|
});
|
package/dist/mjs/cli.mjs
CHANGED
|
@@ -1879,6 +1879,9 @@ function renderWorkspaceStatus(status) {
|
|
|
1879
1879
|
`Remediation: ${status.incident ? `${status.incident.kind} ${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`,
|
|
1880
1880
|
...status.workers.flatMap((worker) => [
|
|
1881
1881
|
`Worker ${worker.workerLabel}: ${worker.configured ? "configured" : "configuring"}; head ${worker.localHead ?? "(none)"}; pending checkouts ${worker.pendingCheckouts.length}`,
|
|
1882
|
+
...(worker.accountWorkspaces ?? []).map(
|
|
1883
|
+
(workspace) => ` Account workspace ${workspace.sessionId}: ${workspace.initialized ? "initialized" : "not initialized"}; head ${workspace.head ?? "(none)"}`
|
|
1884
|
+
),
|
|
1882
1885
|
...worker.hydrationRecovery ? [
|
|
1883
1886
|
` Interrupted hydration ${worker.hydrationRecovery.transactionId}: files and snapshots preserved; execution is fenced.`,
|
|
1884
1887
|
` Evidence: ${worker.hydrationRecovery.transactionPath}`,
|
|
@@ -1927,11 +1930,13 @@ function renderBranchList(branches) {
|
|
|
1927
1930
|
}
|
|
1928
1931
|
function renderWorkspaceSync(result) {
|
|
1929
1932
|
const head = result.publishedHead ?? result.localHead ?? result.startingHead ?? "(none)";
|
|
1933
|
+
const accountWorkspaces = result.affectedSessionIds?.length ? `
|
|
1934
|
+
Account workspaces changed: ${result.affectedSessionIds.join(", ")}` : "";
|
|
1930
1935
|
const discarded = result.discardedPaths.length > 0 ? `
|
|
1931
1936
|
Discarded paths: ${result.discardedPaths.join(", ")}` : "";
|
|
1932
1937
|
const error = result.error ? `
|
|
1933
1938
|
Error: ${result.error}` : "";
|
|
1934
|
-
return `Workspace sync ${result.outcome} on ${result.workerLabel}; HEAD ${head}; ${result.diffSizeBytes} bytes.${discarded}${error}
|
|
1939
|
+
return `Workspace sync ${result.outcome} on ${result.workerLabel}; HEAD ${head}; ${result.diffSizeBytes} bytes.${accountWorkspaces}${discarded}${error}
|
|
1935
1940
|
`;
|
|
1936
1941
|
}
|
|
1937
1942
|
function renderSessionDescription(session) {
|
|
@@ -1989,6 +1994,15 @@ function formatProcessLane(process2) {
|
|
|
1989
1994
|
function renderProcessHistory(processes, nowMs = Date.now()) {
|
|
1990
1995
|
return processes.length === 0 ? "No process history.\n" : renderProcessList(processes, nowMs);
|
|
1991
1996
|
}
|
|
1997
|
+
function renderProcessInventory(inventory, history = false, nowMs = Date.now()) {
|
|
1998
|
+
const rows = inventory.processes.length || inventory.complete ? history ? renderProcessHistory(inventory.processes, nowMs) : renderProcessList(inventory.processes, nowMs) : `No ${history ? "process history" : "active processes"} reported by available workers.
|
|
1999
|
+
`;
|
|
2000
|
+
if (inventory.complete) return rows;
|
|
2001
|
+
const unavailable = inventory.unavailableWorkers.map((worker) => `${worker.workerLabel ?? "default worker"} (${worker.sessionIds.length} conversations)`).join(", ");
|
|
2002
|
+
return `${rows}
|
|
2003
|
+
Process status unavailable: ${unavailable}. These workers may still have active processes; reconnect them before inspecting or stopping their commands.
|
|
2004
|
+
`;
|
|
2005
|
+
}
|
|
1992
2006
|
function renderProcessLog(log) {
|
|
1993
2007
|
const sections = [];
|
|
1994
2008
|
for (const [name, stream] of [
|
|
@@ -2941,13 +2955,25 @@ async function executeR5dctlCommand(client, json, args) {
|
|
|
2941
2955
|
return;
|
|
2942
2956
|
}
|
|
2943
2957
|
if (first === "ps" && second === "list") {
|
|
2944
|
-
const
|
|
2945
|
-
|
|
2958
|
+
const filters = parsePsListArgs(args.slice(2));
|
|
2959
|
+
if (json) {
|
|
2960
|
+
const processes = await client.processes.list(filters);
|
|
2961
|
+
write(processes, renderProcessList(processes));
|
|
2962
|
+
} else {
|
|
2963
|
+
const inventory = await client.processes.inventory(filters);
|
|
2964
|
+
write(inventory, renderProcessInventory(inventory));
|
|
2965
|
+
}
|
|
2946
2966
|
return;
|
|
2947
2967
|
}
|
|
2948
2968
|
if (first === "ps" && second === "history") {
|
|
2949
|
-
const
|
|
2950
|
-
|
|
2969
|
+
const filters = parsePsHistoryArgs(args.slice(2));
|
|
2970
|
+
if (json) {
|
|
2971
|
+
const processes = await client.processes.history(filters);
|
|
2972
|
+
write(processes, renderProcessHistory(processes));
|
|
2973
|
+
} else {
|
|
2974
|
+
const inventory = await client.processes.historyInventory(filters);
|
|
2975
|
+
write(inventory, renderProcessInventory(inventory, true));
|
|
2976
|
+
}
|
|
2951
2977
|
return;
|
|
2952
2978
|
}
|
|
2953
2979
|
if (first === "ps" && second === "inspect") {
|
|
@@ -3906,6 +3932,7 @@ async function runCommand(argv) {
|
|
|
3906
3932
|
credential: clientOptions.token ?? clientOptions.apiKey ?? "",
|
|
3907
3933
|
projectId: project.id,
|
|
3908
3934
|
branchName: options.branch,
|
|
3935
|
+
sessionId: options.session,
|
|
3909
3936
|
command: execution.command
|
|
3910
3937
|
});
|
|
3911
3938
|
}
|
|
@@ -4009,6 +4036,7 @@ export {
|
|
|
4009
4036
|
renderEnvValues,
|
|
4010
4037
|
renderProcessHistory,
|
|
4011
4038
|
renderProcessInspection,
|
|
4039
|
+
renderProcessInventory,
|
|
4012
4040
|
renderProcessList,
|
|
4013
4041
|
renderProcessLog,
|
|
4014
4042
|
renderSessionClaimList,
|
package/dist/mjs/package.json
CHANGED
package/dist/mjs/shell.mjs
CHANGED
|
@@ -1,210 +1,112 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return fallback;
|
|
5
|
-
}
|
|
6
|
-
return Math.max(1, Math.min(Math.floor(value), 500));
|
|
7
|
-
}
|
|
8
|
-
function createShellWebSocketUrl(baseUrl) {
|
|
9
|
-
const url = new URL("/ws", baseUrl);
|
|
10
|
-
if (url.protocol === "https:") {
|
|
11
|
-
url.protocol = "wss:";
|
|
12
|
-
} else if (url.protocol === "http:") {
|
|
13
|
-
url.protocol = "ws:";
|
|
14
|
-
} else {
|
|
15
|
-
throw new Error(`Unsupported r5d base URL protocol: ${url.protocol}`);
|
|
16
|
-
}
|
|
17
|
-
return url.toString();
|
|
18
|
-
}
|
|
19
|
-
function decodeMessage(data) {
|
|
20
|
-
if (typeof data === "string") {
|
|
21
|
-
return data;
|
|
22
|
-
}
|
|
23
|
-
if (Buffer.isBuffer(data)) {
|
|
24
|
-
return data.toString("utf8");
|
|
25
|
-
}
|
|
26
|
-
if (data instanceof ArrayBuffer) {
|
|
27
|
-
return Buffer.from(data).toString("utf8");
|
|
28
|
-
}
|
|
29
|
-
if (ArrayBuffer.isView(data)) {
|
|
30
|
-
return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString("utf8");
|
|
31
|
-
}
|
|
32
|
-
if (Array.isArray(data)) {
|
|
33
|
-
return Buffer.concat(data).toString("utf8");
|
|
34
|
-
}
|
|
35
|
-
return String(data);
|
|
36
|
-
}
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { constants } from "node:os";
|
|
3
|
+
const terminalSize = (value, fallback) => Number.isFinite(value) && value > 0 ? Math.min(500, Math.max(1, Math.floor(value))) : fallback;
|
|
37
4
|
async function runR5dctlShell(options) {
|
|
38
|
-
const stdin = options.stdin ?? process.stdin;
|
|
39
|
-
const
|
|
40
|
-
const signalTarget = options.signalTarget ?? process;
|
|
5
|
+
const stdin = options.stdin ?? process.stdin, stdout = options.stdout ?? process.stdout;
|
|
6
|
+
const signals = options.signalTarget ?? process;
|
|
41
7
|
const interactive = options.command === void 0;
|
|
42
|
-
if (interactive && !stdin.isTTY)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
8
|
+
if (interactive && !stdin.isTTY) throw new Error("Interactive shell requires a TTY. Use `shell -c <command>` for non-interactive use.");
|
|
9
|
+
if (!options.credential) throw new Error("Authentication required. Run `r5dctl auth login` or provide a token or API key.");
|
|
10
|
+
const url = new URL(
|
|
11
|
+
`/api/product/projects/${encodeURIComponent(options.projectId)}/branches/${encodeURIComponent(options.branchName)}/terminal`,
|
|
12
|
+
options.baseUrl
|
|
13
|
+
);
|
|
14
|
+
if (!["http:", "https:"].includes(url.protocol)) throw new Error("Unsupported r5d base URL protocol");
|
|
15
|
+
const performFetch = options.fetch ?? fetch;
|
|
16
|
+
async function request(body, query = "") {
|
|
17
|
+
const serialized = body === void 0 ? void 0 : JSON.stringify(body);
|
|
18
|
+
for (let attempt = 0; ; attempt++) {
|
|
19
|
+
let response;
|
|
20
|
+
try {
|
|
21
|
+
response = await performFetch(url + query, {
|
|
22
|
+
method: body === void 0 ? "GET" : "POST",
|
|
23
|
+
redirect: "error",
|
|
24
|
+
headers: {
|
|
25
|
+
Authorization: `Bearer ${options.credential}`,
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
...options.sessionId ? { "x-r5d-session-id": options.sessionId } : {}
|
|
28
|
+
},
|
|
29
|
+
body: serialized
|
|
30
|
+
});
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (attempt >= 3) throw error;
|
|
33
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * (attempt + 1)));
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (response.status >= 500 && attempt < 3) {
|
|
37
|
+
await response.body?.cancel();
|
|
38
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * (attempt + 1)));
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const result = await response.json();
|
|
42
|
+
if (!response.ok) throw new Error(result.error ?? `Terminal request failed (${response.status})`);
|
|
43
|
+
return result;
|
|
52
44
|
}
|
|
45
|
+
}
|
|
46
|
+
const opened = await request({
|
|
47
|
+
method: "open",
|
|
48
|
+
id: randomUUID(),
|
|
49
|
+
cols: terminalSize(stdout.columns, 80),
|
|
50
|
+
rows: terminalSize(stdout.rows, 24),
|
|
51
|
+
...options.command === void 0 ? {} : { command: options.command }
|
|
53
52
|
});
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
send({ type: "shell_pty_close", ptyId });
|
|
95
|
-
ptyId = void 0;
|
|
96
|
-
}
|
|
97
|
-
finish({ exitCode: 143 });
|
|
98
|
-
};
|
|
99
|
-
const cleanup = () => {
|
|
100
|
-
socket.off("message", onMessage);
|
|
101
|
-
socket.off("error", onSocketError);
|
|
102
|
-
socket.off("close", onSocketClose);
|
|
103
|
-
if (inputAttached) {
|
|
104
|
-
stdin.off("data", onInput);
|
|
105
|
-
stdin.off("end", onInputEnd);
|
|
106
|
-
}
|
|
107
|
-
signalTarget.off("SIGWINCH", sendResize);
|
|
108
|
-
signalTarget.off("SIGINT", onSigint);
|
|
109
|
-
signalTarget.off("SIGTERM", onSigterm);
|
|
110
|
-
if (stdin.isTTY && stdin.setRawMode) {
|
|
111
|
-
stdin.setRawMode(priorRawMode);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
const finish = (result) => {
|
|
115
|
-
if (settled) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
settled = true;
|
|
119
|
-
cleanup();
|
|
120
|
-
if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
|
|
121
|
-
socket.close(1e3, "r5dctl shell finished");
|
|
122
|
-
}
|
|
123
|
-
if ("error" in result) {
|
|
124
|
-
reject(result.error);
|
|
125
|
-
} else {
|
|
126
|
-
resolve(result.exitCode);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
function attachInput() {
|
|
130
|
-
if (inputAttached) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
inputAttached = true;
|
|
134
|
-
if (stdin.isTTY && stdin.setRawMode) {
|
|
135
|
-
stdin.setRawMode(true);
|
|
136
|
-
}
|
|
137
|
-
stdin.on("data", onInput);
|
|
138
|
-
stdin.on("end", onInputEnd);
|
|
139
|
-
signalTarget.on("SIGWINCH", sendResize);
|
|
140
|
-
signalTarget.on("SIGTERM", onSigterm);
|
|
141
|
-
if (!stdin.isTTY) {
|
|
142
|
-
signalTarget.on("SIGINT", onSigint);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
function onMessage(data) {
|
|
146
|
-
let message;
|
|
147
|
-
try {
|
|
148
|
-
const parsed = JSON.parse(decodeMessage(data));
|
|
149
|
-
if (!parsed || typeof parsed !== "object" || !("type" in parsed)) {
|
|
150
|
-
throw new Error("Invalid message");
|
|
53
|
+
if (!opened.ptyId) throw new Error("Terminal did not acknowledge its durable run identity");
|
|
54
|
+
const ptyId = opened.ptyId, priorRaw = stdin.isRaw ?? false;
|
|
55
|
+
let pending = Promise.resolve(), failure, stopped = false;
|
|
56
|
+
const send = (command) => {
|
|
57
|
+
const body = { ...command, id: randomUUID(), ptyId };
|
|
58
|
+
pending = pending.then(() => request(body)).catch((error) => {
|
|
59
|
+
failure = error;
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const input = (chunk) => send({ method: "input", data: Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk });
|
|
63
|
+
const inputEnd = () => {
|
|
64
|
+
if (!interactive) send({ method: "input", data: "" });
|
|
65
|
+
};
|
|
66
|
+
const resize = () => send({ method: "resize", cols: terminalSize(stdout.columns, 80), rows: terminalSize(stdout.rows, 24) });
|
|
67
|
+
const interrupt = () => send({ method: "input", data: "" });
|
|
68
|
+
const terminate = () => {
|
|
69
|
+
send({ method: "close" });
|
|
70
|
+
stopped = true;
|
|
71
|
+
};
|
|
72
|
+
try {
|
|
73
|
+
if (stdin.isTTY && stdin.setRawMode) stdin.setRawMode(true);
|
|
74
|
+
stdin.on("data", input);
|
|
75
|
+
stdin.on("end", inputEnd);
|
|
76
|
+
signals.on("SIGWINCH", resize);
|
|
77
|
+
signals.on("SIGTERM", terminate);
|
|
78
|
+
if (!stdin.isTTY) signals.on("SIGINT", interrupt);
|
|
79
|
+
let cursor = 0;
|
|
80
|
+
while (!stopped) {
|
|
81
|
+
if (failure) throw failure;
|
|
82
|
+
const page = await request(
|
|
83
|
+
void 0,
|
|
84
|
+
`?ptyId=${encodeURIComponent(ptyId)}&after=${cursor}`
|
|
85
|
+
);
|
|
86
|
+
if (!Number.isSafeInteger(page.nextCursor) || page.nextCursor < cursor) throw new Error("Invalid terminal output cursor");
|
|
87
|
+
for (const event of page.events) {
|
|
88
|
+
if (event.type === "output") stdout.write(event.data);
|
|
89
|
+
else if (event.type === "exit") {
|
|
90
|
+
await pending;
|
|
91
|
+
if (failure) throw failure;
|
|
92
|
+
return event.exitCode ?? (event.signal ? 128 + (typeof event.signal === "number" ? event.signal : constants.signals[event.signal] ?? 0) : 1);
|
|
151
93
|
}
|
|
152
|
-
message = parsed;
|
|
153
|
-
} catch {
|
|
154
|
-
finish({ error: new Error("Received an invalid response from r5d.dev") });
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
if (message.type === "connected") {
|
|
158
|
-
if (!openRequested) {
|
|
159
|
-
openRequested = true;
|
|
160
|
-
send({
|
|
161
|
-
type: "shell_pty_open",
|
|
162
|
-
projectId: options.projectId,
|
|
163
|
-
branchName: options.branchName,
|
|
164
|
-
cols: clampTerminalSize(stdout.columns, 80),
|
|
165
|
-
rows: clampTerminalSize(stdout.rows, 24),
|
|
166
|
-
...options.command === void 0 ? {} : { command: options.command }
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
if (message.type === "shell_pty_opened") {
|
|
172
|
-
ptyId = message.ptyId;
|
|
173
|
-
attachInput();
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (message.type === "shell_pty_output") {
|
|
177
|
-
if (message.ptyId === ptyId) {
|
|
178
|
-
stdout.write(message.data);
|
|
179
|
-
}
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (message.type === "shell_pty_exit") {
|
|
183
|
-
if (message.ptyId === ptyId) {
|
|
184
|
-
ptyId = void 0;
|
|
185
|
-
finish({ exitCode: message.exitCode });
|
|
186
|
-
}
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
if (message.type === "shell_pty_error" && (!message.ptyId || message.ptyId === ptyId)) {
|
|
190
|
-
finish({ error: new Error(message.message) });
|
|
191
94
|
}
|
|
95
|
+
cursor = page.nextCursor;
|
|
96
|
+
await new Promise((resolve) => setTimeout(resolve, options.pollIntervalMs ?? 150));
|
|
192
97
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
socket.on("close", onSocketClose);
|
|
205
|
-
});
|
|
98
|
+
await pending;
|
|
99
|
+
if (failure) throw failure;
|
|
100
|
+
return 143;
|
|
101
|
+
} finally {
|
|
102
|
+
stdin.off("data", input);
|
|
103
|
+
stdin.off("end", inputEnd);
|
|
104
|
+
signals.off("SIGWINCH", resize);
|
|
105
|
+
signals.off("SIGTERM", terminate);
|
|
106
|
+
signals.off("SIGINT", interrupt);
|
|
107
|
+
if (stdin.isTTY && stdin.setRawMode) stdin.setRawMode(priorRaw);
|
|
108
|
+
}
|
|
206
109
|
}
|
|
207
110
|
export {
|
|
208
|
-
createShellWebSocketUrl,
|
|
209
111
|
runR5dctlShell
|
|
210
112
|
};
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R5dctlApiError, R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessLog, type R5dctlProcessRun, type R5dctlWorkerCapacityReport, type R5dctlBranch, type R5dctlBranchDeletionStatus, type R5dctlBranchDescription, type R5dctlBranchOperation, type R5dctlSessionSummary, type R5dctlCheckoutPathResponse, type R5dctlSessionClaim, type R5dctlSessionClaimListResponse, type R5dctlSessionClaimScope, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorkingTreeMode, type R5dctlWorktreeSource, type R5dctlPromptDelivery, type R5dctlMessageDelivery, type R5dctlSessionMode, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
|
|
1
|
+
import { R5dctlApiError, R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlProcessInspection, type R5dctlProcessInventory, type R5dctlProcessListInput, type R5dctlProcessLog, type R5dctlProcessRun, type R5dctlWorkerCapacityReport, type R5dctlBranch, type R5dctlBranchDeletionStatus, type R5dctlBranchDescription, type R5dctlBranchOperation, type R5dctlSessionSummary, type R5dctlCheckoutPathResponse, type R5dctlSessionClaim, type R5dctlSessionClaimListResponse, type R5dctlSessionClaimScope, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorkingTreeMode, type R5dctlWorktreeSource, type R5dctlPromptDelivery, type R5dctlMessageDelivery, type R5dctlSessionMode, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
|
|
2
2
|
import { type AccountCommand } from "./account";
|
|
3
3
|
export type GlobalOptions = {
|
|
4
4
|
baseUrl?: string;
|
|
@@ -320,6 +320,8 @@ export declare function renderProcessList(processes: R5dctlProcessRun[], nowMs?:
|
|
|
320
320
|
/** `general`, `control`, `queued:general`, or `-` when the worker reported nothing for this run. */
|
|
321
321
|
export declare function formatProcessLane(process: Pick<R5dctlProcessRun, "admission" | "commandClass">): string;
|
|
322
322
|
export declare function renderProcessHistory(processes: R5dctlProcessRun[], nowMs?: number): string;
|
|
323
|
+
/** Partial human output never labels unreachable workers as having no effects. */
|
|
324
|
+
export declare function renderProcessInventory(inventory: R5dctlProcessInventory, history?: boolean, nowMs?: number): string;
|
|
323
325
|
/** Streams go to stdout under `==> name <==` headers; the selection summary is returned separately for stderr. */
|
|
324
326
|
export declare function renderProcessLog(log: R5dctlProcessLog): {
|
|
325
327
|
text: string;
|
package/dist/types/shell.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Readable, Writable } from "node:stream";
|
|
2
|
-
import WebSocket, { type ClientOptions } from "ws";
|
|
3
2
|
type ShellInput = Readable & {
|
|
4
3
|
isTTY?: boolean;
|
|
5
4
|
isRaw?: boolean;
|
|
@@ -9,18 +8,19 @@ type ShellOutput = Writable & {
|
|
|
9
8
|
columns?: number;
|
|
10
9
|
rows?: number;
|
|
11
10
|
};
|
|
12
|
-
export type ShellWebSocket = Pick<WebSocket, "readyState" | "send" | "close" | "on" | "off">;
|
|
13
11
|
export type RunR5dctlShellOptions = {
|
|
14
12
|
baseUrl: string;
|
|
15
13
|
credential: string;
|
|
16
14
|
projectId: string;
|
|
17
15
|
branchName: string;
|
|
16
|
+
sessionId?: string;
|
|
18
17
|
command?: string;
|
|
19
18
|
stdin?: ShellInput;
|
|
20
19
|
stdout?: ShellOutput;
|
|
21
20
|
signalTarget?: Pick<NodeJS.EventEmitter, "on" | "off">;
|
|
22
|
-
|
|
21
|
+
fetch?: typeof fetch;
|
|
22
|
+
pollIntervalMs?: number;
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
/** Original CLI behavior, using durable HTTP PTY handles instead of process-owned WebSockets. */
|
|
25
25
|
export declare function runR5dctlShell(options: RunR5dctlShellOptions): Promise<number>;
|
|
26
26
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/r5dctl",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.138",
|
|
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.
|
|
29
|
+
"@ricsam/r5d-api": "^0.0.138",
|
|
30
30
|
"dotenv": "^17",
|
|
31
31
|
"qrcode": "^1.5.4",
|
|
32
32
|
"ws": "^8.18.3"
|