@ricsam/r5dctl 0.0.22 → 0.0.23
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 +4 -0
- package/dist/cjs/cli.cjs +76 -10
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/shell.cjs +245 -0
- package/dist/mjs/cli.mjs +75 -10
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/shell.mjs +210 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/shell.d.ts +26 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -67,8 +67,12 @@ r5dctl -s <session-id> prompt --mode plan --model max "..."
|
|
|
67
67
|
r5dctl -s <session-id> answer-questions -a1 "Recipe Collection" -a2 "Both Manual & AI"
|
|
68
68
|
r5dctl -s <session-id> apply-required-envs -e backend/KEY=value -e frontend/KEY=value
|
|
69
69
|
|
|
70
|
+
r5dctl -p <project> -b <branch> shell
|
|
71
|
+
r5dctl -p <project> -b <branch> shell -c "bun test"
|
|
70
72
|
```
|
|
71
73
|
|
|
74
|
+
`shell` connects to the same live worker PTY as the web Shell tab. Without `-c` it attaches the local terminal interactively. With `-c`/`--command`, it runs the supplied shell expression, streams combined PTY output, and exits with the remote command's status. Because command output uses a PTY, stdout and stderr are combined and programs may emit colors or other terminal control sequences.
|
|
75
|
+
|
|
72
76
|
`get envs` reports names and whether values are set, but never prints the values. Prefer `--from-env-file` for bulk imports so secret values do not appear in shell arguments or history. Dotenv imports target backend envs by default; use `--target frontend` when needed. Empty dotenv values are skipped unless `--include-empty` is passed, and `--only-missing` preserves values that are already set.
|
|
73
77
|
|
|
74
78
|
Conversation output is human-readable by default and excludes both the system prompt and tool definitions. `--raw` keeps the transcript layout but renders structured message and tool parts as JSON, `--tools` includes the provider-shaped tool definitions, and `--system` includes system prompt messages. The global `--json` flag remains separate and prints the complete API response envelope.
|
package/dist/cjs/cli.cjs
CHANGED
|
@@ -37,6 +37,7 @@ __export(cli_exports, {
|
|
|
37
37
|
parseGlobalArgs: () => parseGlobalArgs,
|
|
38
38
|
parsePromptArgs: () => parsePromptArgs,
|
|
39
39
|
parseSetEnvArgs: () => parseSetEnvArgs,
|
|
40
|
+
parseShellArgs: () => parseShellArgs,
|
|
40
41
|
readDotenvFile: () => readDotenvFile,
|
|
41
42
|
renderConversationResponse: () => renderConversationResponse,
|
|
42
43
|
resolveCommandExecution: () => resolveCommandExecution,
|
|
@@ -51,6 +52,7 @@ var import_node_child_process = require("node:child_process");
|
|
|
51
52
|
var import_promises = require("node:timers/promises");
|
|
52
53
|
var import_dotenv = require("dotenv");
|
|
53
54
|
var import_r5d_api = require("@ricsam/r5d-api");
|
|
55
|
+
var import_shell = require("./shell.cjs");
|
|
54
56
|
const CHAT_MODES = /* @__PURE__ */ new Set([
|
|
55
57
|
"ask",
|
|
56
58
|
"plan",
|
|
@@ -83,12 +85,21 @@ const CLI_ONLY_HELP_ENTRIES = [
|
|
|
83
85
|
section: "auth",
|
|
84
86
|
usage: "auth login [--no-open]",
|
|
85
87
|
description: "Start the browser login flow."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
section: "shell",
|
|
91
|
+
usage: "-p <project> -b <branch> shell [-c <command>]",
|
|
92
|
+
description: "Open an interactive worker shell or run one command through a PTY."
|
|
86
93
|
}
|
|
87
94
|
];
|
|
88
95
|
const CLI_ONLY_COMMAND_HELP = [
|
|
89
96
|
{
|
|
90
97
|
path: ["auth", "login"],
|
|
91
98
|
usage: "auth login [--no-open]"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
path: ["shell"],
|
|
102
|
+
usage: "-p <project> -b <branch> shell [-c <command>]"
|
|
92
103
|
}
|
|
93
104
|
];
|
|
94
105
|
const SHARED_HELP_ENTRIES = [
|
|
@@ -170,13 +181,14 @@ const SHARED_HELP_ENTRIES = [
|
|
|
170
181
|
{ section: "merges", usage: "-p <project> continue-merge <target-branch>", description: "Commit a resolved merge." },
|
|
171
182
|
{ section: "merges", usage: "-p <project> abort-merge <target-branch>", description: "Abort an in-progress merge." }
|
|
172
183
|
];
|
|
173
|
-
const HELP_SECTION_ORDER = ["auth", "projects", "branches", "envs", "sessions", "agents", "merges"];
|
|
184
|
+
const HELP_SECTION_ORDER = ["auth", "projects", "branches", "envs", "sessions", "shell", "agents", "merges"];
|
|
174
185
|
const HELP_SECTION_TITLES = {
|
|
175
186
|
auth: "Auth",
|
|
176
187
|
projects: "Projects",
|
|
177
188
|
branches: "Branches",
|
|
178
189
|
envs: "Environment variables",
|
|
179
190
|
sessions: "Sessions",
|
|
191
|
+
shell: "Shell",
|
|
180
192
|
agents: "Agents",
|
|
181
193
|
merges: "Merges"
|
|
182
194
|
};
|
|
@@ -725,6 +737,33 @@ function parsePromptArgs(args) {
|
|
|
725
737
|
message
|
|
726
738
|
};
|
|
727
739
|
}
|
|
740
|
+
function parseShellArgs(args) {
|
|
741
|
+
let command;
|
|
742
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
743
|
+
const arg = args[index];
|
|
744
|
+
if (!arg) {
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
if (arg === "-c" || arg === "--command") {
|
|
748
|
+
if (command !== void 0) {
|
|
749
|
+
throw new Error("Use only one -c/--command value");
|
|
750
|
+
}
|
|
751
|
+
command = requireValue(args[index + 1], `Missing value for ${arg}`);
|
|
752
|
+
index += 1;
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
const inlineCommand = parseLongOptionWithEquals(arg, "--command");
|
|
756
|
+
if (inlineCommand !== void 0) {
|
|
757
|
+
if (command !== void 0) {
|
|
758
|
+
throw new Error("Use only one -c/--command value");
|
|
759
|
+
}
|
|
760
|
+
command = requireValue(inlineCommand, "Missing value for --command");
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
throw new Error(arg.startsWith("-") ? `Unknown shell flag: ${arg}` : `Unexpected shell value: ${arg}`);
|
|
764
|
+
}
|
|
765
|
+
return command === void 0 ? {} : { command };
|
|
766
|
+
}
|
|
728
767
|
async function openInBrowser(url) {
|
|
729
768
|
if (process.platform === "darwin") {
|
|
730
769
|
(0, import_node_child_process.spawn)("open", [url], { stdio: "ignore", detached: true }).unref();
|
|
@@ -1573,6 +1612,21 @@ function resolveCommandExecution(options, rest) {
|
|
|
1573
1612
|
pluginArgs: ["conversation", sessionId, ...commandArgs]
|
|
1574
1613
|
};
|
|
1575
1614
|
}
|
|
1615
|
+
if (command === "shell") {
|
|
1616
|
+
if (!options.project) {
|
|
1617
|
+
throw new Error("--project/-p is required for `shell`");
|
|
1618
|
+
}
|
|
1619
|
+
if (!options.branch) {
|
|
1620
|
+
throw new Error("--branch/-b is required for `shell`");
|
|
1621
|
+
}
|
|
1622
|
+
if (options.json) {
|
|
1623
|
+
throw new Error("--json is not supported for `shell`");
|
|
1624
|
+
}
|
|
1625
|
+
return {
|
|
1626
|
+
kind: "shell",
|
|
1627
|
+
...parseShellArgs(commandArgs)
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1576
1630
|
if (command === "prompt") {
|
|
1577
1631
|
const sessionId = options.session;
|
|
1578
1632
|
if (!sessionId) {
|
|
@@ -1696,31 +1750,43 @@ async function runCommand(argv) {
|
|
|
1696
1750
|
const { options, rest } = parseGlobalArgs(argv);
|
|
1697
1751
|
if (options.version) {
|
|
1698
1752
|
printVersion();
|
|
1699
|
-
return;
|
|
1753
|
+
return 0;
|
|
1700
1754
|
}
|
|
1701
1755
|
if (options.help || rest.length === 0) {
|
|
1702
1756
|
printHelp();
|
|
1703
|
-
return;
|
|
1757
|
+
return 0;
|
|
1704
1758
|
}
|
|
1705
1759
|
const config = parseConfig(options.configPath);
|
|
1706
|
-
const
|
|
1760
|
+
const clientOptions = resolveClientOptions(options, config);
|
|
1761
|
+
const client = new import_r5d_api.R5dctlClient(clientOptions);
|
|
1707
1762
|
const execution = resolveCommandExecution(options, rest);
|
|
1708
1763
|
if (execution.kind === "auth-login") {
|
|
1709
1764
|
await handleAuthLogin(client, options, execution.commandArgs, config);
|
|
1710
|
-
return;
|
|
1765
|
+
return 0;
|
|
1711
1766
|
}
|
|
1712
1767
|
if (execution.kind === "auth-api-key-create") {
|
|
1713
1768
|
await handleAuthApiKeyCreate(client, options, config, execution.commandArgs);
|
|
1714
|
-
return;
|
|
1769
|
+
return 0;
|
|
1715
1770
|
}
|
|
1716
1771
|
if (execution.kind === "cli-help") {
|
|
1717
1772
|
process.stdout.write(execution.text);
|
|
1718
|
-
return;
|
|
1773
|
+
return 0;
|
|
1774
|
+
}
|
|
1775
|
+
if (execution.kind === "shell") {
|
|
1776
|
+
const project = await client.projects.describe(options.project);
|
|
1777
|
+
return await (0, import_shell.runR5dctlShell)({
|
|
1778
|
+
baseUrl: clientOptions.baseUrl ?? "https://r5d.dev",
|
|
1779
|
+
credential: clientOptions.token ?? clientOptions.apiKey ?? "",
|
|
1780
|
+
projectId: project.id,
|
|
1781
|
+
branchName: options.branch,
|
|
1782
|
+
command: execution.command
|
|
1783
|
+
});
|
|
1719
1784
|
}
|
|
1720
1785
|
await executeR5dctlCommand(client, options.json, execution.pluginArgs);
|
|
1721
1786
|
if (execution.clearAuthOnSuccess) {
|
|
1722
1787
|
writeConfig(options.configPath, clearAuthFromConfig(config, options));
|
|
1723
1788
|
}
|
|
1789
|
+
return 0;
|
|
1724
1790
|
}
|
|
1725
1791
|
function extractApiErrorMessage(error) {
|
|
1726
1792
|
if (typeof error.body === "object" && error.body !== null) {
|
|
@@ -1733,8 +1799,7 @@ function extractApiErrorMessage(error) {
|
|
|
1733
1799
|
}
|
|
1734
1800
|
async function runR5dctlCli(argv) {
|
|
1735
1801
|
try {
|
|
1736
|
-
await runCommand(argv);
|
|
1737
|
-
return 0;
|
|
1802
|
+
return await runCommand(argv);
|
|
1738
1803
|
} catch (error) {
|
|
1739
1804
|
if (error instanceof import_r5d_api.R5dctlApiError) {
|
|
1740
1805
|
process.stderr.write(`${extractApiErrorMessage(error)}
|
|
@@ -1749,7 +1814,7 @@ async function runR5dctlCli(argv) {
|
|
|
1749
1814
|
async function main(argv = process.argv.slice(2)) {
|
|
1750
1815
|
const exitCode = await runR5dctlCli(argv);
|
|
1751
1816
|
if (exitCode !== 0) {
|
|
1752
|
-
process.
|
|
1817
|
+
process.exitCode = exitCode;
|
|
1753
1818
|
}
|
|
1754
1819
|
}
|
|
1755
1820
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1763,6 +1828,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
1763
1828
|
parseGlobalArgs,
|
|
1764
1829
|
parsePromptArgs,
|
|
1765
1830
|
parseSetEnvArgs,
|
|
1831
|
+
parseShellArgs,
|
|
1766
1832
|
readDotenvFile,
|
|
1767
1833
|
renderConversationResponse,
|
|
1768
1834
|
resolveCommandExecution,
|
package/dist/cjs/package.json
CHANGED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
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
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var shell_exports = {};
|
|
30
|
+
__export(shell_exports, {
|
|
31
|
+
createShellWebSocketUrl: () => createShellWebSocketUrl,
|
|
32
|
+
runR5dctlShell: () => runR5dctlShell
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(shell_exports);
|
|
35
|
+
var import_ws = __toESM(require("ws"), 1);
|
|
36
|
+
function clampTerminalSize(value, fallback) {
|
|
37
|
+
if (!Number.isFinite(value) || (value ?? 0) <= 0) {
|
|
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
|
+
}
|
|
71
|
+
async function runR5dctlShell(options) {
|
|
72
|
+
const stdin = options.stdin ?? process.stdin;
|
|
73
|
+
const stdout = options.stdout ?? process.stdout;
|
|
74
|
+
const signalTarget = options.signalTarget ?? process;
|
|
75
|
+
const interactive = options.command === void 0;
|
|
76
|
+
if (interactive && !stdin.isTTY) {
|
|
77
|
+
throw new Error("Interactive shell requires a TTY. Use `shell -c <command>` for non-interactive use.");
|
|
78
|
+
}
|
|
79
|
+
if (!options.credential) {
|
|
80
|
+
throw new Error("Authentication required. Run `r5dctl auth login` or provide a token or API key.");
|
|
81
|
+
}
|
|
82
|
+
const createWebSocket = options.createWebSocket ?? ((url, clientOptions) => new import_ws.default(url, clientOptions));
|
|
83
|
+
const socket = createWebSocket(createShellWebSocketUrl(options.baseUrl), {
|
|
84
|
+
headers: {
|
|
85
|
+
Authorization: `Bearer ${options.credential}`
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return await new Promise((resolve, reject) => {
|
|
89
|
+
let ptyId;
|
|
90
|
+
let openRequested = false;
|
|
91
|
+
let settled = false;
|
|
92
|
+
let inputAttached = false;
|
|
93
|
+
const priorRawMode = stdin.isRaw ?? false;
|
|
94
|
+
const send = (message) => {
|
|
95
|
+
if (socket.readyState !== import_ws.default.OPEN) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
socket.send(JSON.stringify(message));
|
|
99
|
+
};
|
|
100
|
+
const sendResize = () => {
|
|
101
|
+
if (!ptyId) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
send({
|
|
105
|
+
type: "shell_pty_resize",
|
|
106
|
+
ptyId,
|
|
107
|
+
cols: clampTerminalSize(stdout.columns, 80),
|
|
108
|
+
rows: clampTerminalSize(stdout.rows, 24)
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
const onInput = (chunk) => {
|
|
112
|
+
if (ptyId) {
|
|
113
|
+
send({ type: "shell_pty_input", ptyId, data: Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk });
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const onInputEnd = () => {
|
|
117
|
+
if (!interactive && ptyId) {
|
|
118
|
+
send({ type: "shell_pty_input", ptyId, data: "" });
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const onSigint = () => {
|
|
122
|
+
if (ptyId) {
|
|
123
|
+
send({ type: "shell_pty_input", ptyId, data: "" });
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const onSigterm = () => {
|
|
127
|
+
if (ptyId) {
|
|
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");
|
|
185
|
+
}
|
|
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
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function onSocketError(error) {
|
|
228
|
+
finish({ error: new Error(`r5d.dev shell connection failed: ${error.message}`) });
|
|
229
|
+
}
|
|
230
|
+
function onSocketClose(code, reason) {
|
|
231
|
+
if (!settled) {
|
|
232
|
+
const suffix = reason.length > 0 ? `: ${reason.toString("utf8")}` : "";
|
|
233
|
+
finish({ error: new Error(`r5d.dev shell connection closed (${code})${suffix}`) });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
socket.on("message", onMessage);
|
|
237
|
+
socket.on("error", onSocketError);
|
|
238
|
+
socket.on("close", onSocketClose);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
242
|
+
0 && (module.exports = {
|
|
243
|
+
createShellWebSocketUrl,
|
|
244
|
+
runR5dctlShell
|
|
245
|
+
});
|
package/dist/mjs/cli.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
R5dctlApiError,
|
|
9
9
|
R5dctlClient
|
|
10
10
|
} from "@ricsam/r5d-api";
|
|
11
|
+
import { runR5dctlShell } from "./shell.mjs";
|
|
11
12
|
const CHAT_MODES = /* @__PURE__ */ new Set([
|
|
12
13
|
"ask",
|
|
13
14
|
"plan",
|
|
@@ -40,12 +41,21 @@ const CLI_ONLY_HELP_ENTRIES = [
|
|
|
40
41
|
section: "auth",
|
|
41
42
|
usage: "auth login [--no-open]",
|
|
42
43
|
description: "Start the browser login flow."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
section: "shell",
|
|
47
|
+
usage: "-p <project> -b <branch> shell [-c <command>]",
|
|
48
|
+
description: "Open an interactive worker shell or run one command through a PTY."
|
|
43
49
|
}
|
|
44
50
|
];
|
|
45
51
|
const CLI_ONLY_COMMAND_HELP = [
|
|
46
52
|
{
|
|
47
53
|
path: ["auth", "login"],
|
|
48
54
|
usage: "auth login [--no-open]"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
path: ["shell"],
|
|
58
|
+
usage: "-p <project> -b <branch> shell [-c <command>]"
|
|
49
59
|
}
|
|
50
60
|
];
|
|
51
61
|
const SHARED_HELP_ENTRIES = [
|
|
@@ -127,13 +137,14 @@ const SHARED_HELP_ENTRIES = [
|
|
|
127
137
|
{ section: "merges", usage: "-p <project> continue-merge <target-branch>", description: "Commit a resolved merge." },
|
|
128
138
|
{ section: "merges", usage: "-p <project> abort-merge <target-branch>", description: "Abort an in-progress merge." }
|
|
129
139
|
];
|
|
130
|
-
const HELP_SECTION_ORDER = ["auth", "projects", "branches", "envs", "sessions", "agents", "merges"];
|
|
140
|
+
const HELP_SECTION_ORDER = ["auth", "projects", "branches", "envs", "sessions", "shell", "agents", "merges"];
|
|
131
141
|
const HELP_SECTION_TITLES = {
|
|
132
142
|
auth: "Auth",
|
|
133
143
|
projects: "Projects",
|
|
134
144
|
branches: "Branches",
|
|
135
145
|
envs: "Environment variables",
|
|
136
146
|
sessions: "Sessions",
|
|
147
|
+
shell: "Shell",
|
|
137
148
|
agents: "Agents",
|
|
138
149
|
merges: "Merges"
|
|
139
150
|
};
|
|
@@ -682,6 +693,33 @@ function parsePromptArgs(args) {
|
|
|
682
693
|
message
|
|
683
694
|
};
|
|
684
695
|
}
|
|
696
|
+
function parseShellArgs(args) {
|
|
697
|
+
let command;
|
|
698
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
699
|
+
const arg = args[index];
|
|
700
|
+
if (!arg) {
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (arg === "-c" || arg === "--command") {
|
|
704
|
+
if (command !== void 0) {
|
|
705
|
+
throw new Error("Use only one -c/--command value");
|
|
706
|
+
}
|
|
707
|
+
command = requireValue(args[index + 1], `Missing value for ${arg}`);
|
|
708
|
+
index += 1;
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
const inlineCommand = parseLongOptionWithEquals(arg, "--command");
|
|
712
|
+
if (inlineCommand !== void 0) {
|
|
713
|
+
if (command !== void 0) {
|
|
714
|
+
throw new Error("Use only one -c/--command value");
|
|
715
|
+
}
|
|
716
|
+
command = requireValue(inlineCommand, "Missing value for --command");
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
throw new Error(arg.startsWith("-") ? `Unknown shell flag: ${arg}` : `Unexpected shell value: ${arg}`);
|
|
720
|
+
}
|
|
721
|
+
return command === void 0 ? {} : { command };
|
|
722
|
+
}
|
|
685
723
|
async function openInBrowser(url) {
|
|
686
724
|
if (process.platform === "darwin") {
|
|
687
725
|
spawn("open", [url], { stdio: "ignore", detached: true }).unref();
|
|
@@ -1530,6 +1568,21 @@ function resolveCommandExecution(options, rest) {
|
|
|
1530
1568
|
pluginArgs: ["conversation", sessionId, ...commandArgs]
|
|
1531
1569
|
};
|
|
1532
1570
|
}
|
|
1571
|
+
if (command === "shell") {
|
|
1572
|
+
if (!options.project) {
|
|
1573
|
+
throw new Error("--project/-p is required for `shell`");
|
|
1574
|
+
}
|
|
1575
|
+
if (!options.branch) {
|
|
1576
|
+
throw new Error("--branch/-b is required for `shell`");
|
|
1577
|
+
}
|
|
1578
|
+
if (options.json) {
|
|
1579
|
+
throw new Error("--json is not supported for `shell`");
|
|
1580
|
+
}
|
|
1581
|
+
return {
|
|
1582
|
+
kind: "shell",
|
|
1583
|
+
...parseShellArgs(commandArgs)
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1533
1586
|
if (command === "prompt") {
|
|
1534
1587
|
const sessionId = options.session;
|
|
1535
1588
|
if (!sessionId) {
|
|
@@ -1653,31 +1706,43 @@ async function runCommand(argv) {
|
|
|
1653
1706
|
const { options, rest } = parseGlobalArgs(argv);
|
|
1654
1707
|
if (options.version) {
|
|
1655
1708
|
printVersion();
|
|
1656
|
-
return;
|
|
1709
|
+
return 0;
|
|
1657
1710
|
}
|
|
1658
1711
|
if (options.help || rest.length === 0) {
|
|
1659
1712
|
printHelp();
|
|
1660
|
-
return;
|
|
1713
|
+
return 0;
|
|
1661
1714
|
}
|
|
1662
1715
|
const config = parseConfig(options.configPath);
|
|
1663
|
-
const
|
|
1716
|
+
const clientOptions = resolveClientOptions(options, config);
|
|
1717
|
+
const client = new R5dctlClient(clientOptions);
|
|
1664
1718
|
const execution = resolveCommandExecution(options, rest);
|
|
1665
1719
|
if (execution.kind === "auth-login") {
|
|
1666
1720
|
await handleAuthLogin(client, options, execution.commandArgs, config);
|
|
1667
|
-
return;
|
|
1721
|
+
return 0;
|
|
1668
1722
|
}
|
|
1669
1723
|
if (execution.kind === "auth-api-key-create") {
|
|
1670
1724
|
await handleAuthApiKeyCreate(client, options, config, execution.commandArgs);
|
|
1671
|
-
return;
|
|
1725
|
+
return 0;
|
|
1672
1726
|
}
|
|
1673
1727
|
if (execution.kind === "cli-help") {
|
|
1674
1728
|
process.stdout.write(execution.text);
|
|
1675
|
-
return;
|
|
1729
|
+
return 0;
|
|
1730
|
+
}
|
|
1731
|
+
if (execution.kind === "shell") {
|
|
1732
|
+
const project = await client.projects.describe(options.project);
|
|
1733
|
+
return await runR5dctlShell({
|
|
1734
|
+
baseUrl: clientOptions.baseUrl ?? "https://r5d.dev",
|
|
1735
|
+
credential: clientOptions.token ?? clientOptions.apiKey ?? "",
|
|
1736
|
+
projectId: project.id,
|
|
1737
|
+
branchName: options.branch,
|
|
1738
|
+
command: execution.command
|
|
1739
|
+
});
|
|
1676
1740
|
}
|
|
1677
1741
|
await executeR5dctlCommand(client, options.json, execution.pluginArgs);
|
|
1678
1742
|
if (execution.clearAuthOnSuccess) {
|
|
1679
1743
|
writeConfig(options.configPath, clearAuthFromConfig(config, options));
|
|
1680
1744
|
}
|
|
1745
|
+
return 0;
|
|
1681
1746
|
}
|
|
1682
1747
|
function extractApiErrorMessage(error) {
|
|
1683
1748
|
if (typeof error.body === "object" && error.body !== null) {
|
|
@@ -1690,8 +1755,7 @@ function extractApiErrorMessage(error) {
|
|
|
1690
1755
|
}
|
|
1691
1756
|
async function runR5dctlCli(argv) {
|
|
1692
1757
|
try {
|
|
1693
|
-
await runCommand(argv);
|
|
1694
|
-
return 0;
|
|
1758
|
+
return await runCommand(argv);
|
|
1695
1759
|
} catch (error) {
|
|
1696
1760
|
if (error instanceof R5dctlApiError) {
|
|
1697
1761
|
process.stderr.write(`${extractApiErrorMessage(error)}
|
|
@@ -1706,7 +1770,7 @@ async function runR5dctlCli(argv) {
|
|
|
1706
1770
|
async function main(argv = process.argv.slice(2)) {
|
|
1707
1771
|
const exitCode = await runR5dctlCli(argv);
|
|
1708
1772
|
if (exitCode !== 0) {
|
|
1709
|
-
process.
|
|
1773
|
+
process.exitCode = exitCode;
|
|
1710
1774
|
}
|
|
1711
1775
|
}
|
|
1712
1776
|
export {
|
|
@@ -1719,6 +1783,7 @@ export {
|
|
|
1719
1783
|
parseGlobalArgs,
|
|
1720
1784
|
parsePromptArgs,
|
|
1721
1785
|
parseSetEnvArgs,
|
|
1786
|
+
parseShellArgs,
|
|
1722
1787
|
readDotenvFile,
|
|
1723
1788
|
renderConversationResponse,
|
|
1724
1789
|
resolveCommandExecution,
|
package/dist/mjs/package.json
CHANGED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import WebSocket, {} from "ws";
|
|
2
|
+
function clampTerminalSize(value, fallback) {
|
|
3
|
+
if (!Number.isFinite(value) || (value ?? 0) <= 0) {
|
|
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
|
+
}
|
|
37
|
+
async function runR5dctlShell(options) {
|
|
38
|
+
const stdin = options.stdin ?? process.stdin;
|
|
39
|
+
const stdout = options.stdout ?? process.stdout;
|
|
40
|
+
const signalTarget = options.signalTarget ?? process;
|
|
41
|
+
const interactive = options.command === void 0;
|
|
42
|
+
if (interactive && !stdin.isTTY) {
|
|
43
|
+
throw new Error("Interactive shell requires a TTY. Use `shell -c <command>` for non-interactive use.");
|
|
44
|
+
}
|
|
45
|
+
if (!options.credential) {
|
|
46
|
+
throw new Error("Authentication required. Run `r5dctl auth login` or provide a token or API key.");
|
|
47
|
+
}
|
|
48
|
+
const createWebSocket = options.createWebSocket ?? ((url, clientOptions) => new WebSocket(url, clientOptions));
|
|
49
|
+
const socket = createWebSocket(createShellWebSocketUrl(options.baseUrl), {
|
|
50
|
+
headers: {
|
|
51
|
+
Authorization: `Bearer ${options.credential}`
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return await new Promise((resolve, reject) => {
|
|
55
|
+
let ptyId;
|
|
56
|
+
let openRequested = false;
|
|
57
|
+
let settled = false;
|
|
58
|
+
let inputAttached = false;
|
|
59
|
+
const priorRawMode = stdin.isRaw ?? false;
|
|
60
|
+
const send = (message) => {
|
|
61
|
+
if (socket.readyState !== WebSocket.OPEN) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
socket.send(JSON.stringify(message));
|
|
65
|
+
};
|
|
66
|
+
const sendResize = () => {
|
|
67
|
+
if (!ptyId) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
send({
|
|
71
|
+
type: "shell_pty_resize",
|
|
72
|
+
ptyId,
|
|
73
|
+
cols: clampTerminalSize(stdout.columns, 80),
|
|
74
|
+
rows: clampTerminalSize(stdout.rows, 24)
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const onInput = (chunk) => {
|
|
78
|
+
if (ptyId) {
|
|
79
|
+
send({ type: "shell_pty_input", ptyId, data: Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk });
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const onInputEnd = () => {
|
|
83
|
+
if (!interactive && ptyId) {
|
|
84
|
+
send({ type: "shell_pty_input", ptyId, data: "" });
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const onSigint = () => {
|
|
88
|
+
if (ptyId) {
|
|
89
|
+
send({ type: "shell_pty_input", ptyId, data: "" });
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const onSigterm = () => {
|
|
93
|
+
if (ptyId) {
|
|
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");
|
|
151
|
+
}
|
|
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
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function onSocketError(error) {
|
|
194
|
+
finish({ error: new Error(`r5d.dev shell connection failed: ${error.message}`) });
|
|
195
|
+
}
|
|
196
|
+
function onSocketClose(code, reason) {
|
|
197
|
+
if (!settled) {
|
|
198
|
+
const suffix = reason.length > 0 ? `: ${reason.toString("utf8")}` : "";
|
|
199
|
+
finish({ error: new Error(`r5d.dev shell connection closed (${code})${suffix}`) });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
socket.on("message", onMessage);
|
|
203
|
+
socket.on("error", onSocketError);
|
|
204
|
+
socket.on("close", onSocketClose);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
export {
|
|
208
|
+
createShellWebSocketUrl,
|
|
209
|
+
runR5dctlShell
|
|
210
|
+
};
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -26,6 +26,9 @@ type CommandExecutionPlan = {
|
|
|
26
26
|
} | {
|
|
27
27
|
kind: "auth-api-key-create";
|
|
28
28
|
commandArgs: string[];
|
|
29
|
+
} | {
|
|
30
|
+
kind: "shell";
|
|
31
|
+
command?: string;
|
|
29
32
|
} | {
|
|
30
33
|
kind: "cli-help";
|
|
31
34
|
text: string;
|
|
@@ -55,6 +58,9 @@ export declare function parsePromptArgs(args: string[]): {
|
|
|
55
58
|
model: ModelTier;
|
|
56
59
|
message: string;
|
|
57
60
|
};
|
|
61
|
+
export declare function parseShellArgs(args: string[]): {
|
|
62
|
+
command?: string;
|
|
63
|
+
};
|
|
58
64
|
export type EnvSummaryData = Partial<Record<R5dctlEnvTarget, Record<string, {
|
|
59
65
|
optional: boolean;
|
|
60
66
|
description: string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
import WebSocket, { type ClientOptions } from "ws";
|
|
3
|
+
type ShellInput = Readable & {
|
|
4
|
+
isTTY?: boolean;
|
|
5
|
+
isRaw?: boolean;
|
|
6
|
+
setRawMode?: (mode: boolean) => unknown;
|
|
7
|
+
};
|
|
8
|
+
type ShellOutput = Writable & {
|
|
9
|
+
columns?: number;
|
|
10
|
+
rows?: number;
|
|
11
|
+
};
|
|
12
|
+
export type ShellWebSocket = Pick<WebSocket, "readyState" | "send" | "close" | "on" | "off">;
|
|
13
|
+
export type RunR5dctlShellOptions = {
|
|
14
|
+
baseUrl: string;
|
|
15
|
+
credential: string;
|
|
16
|
+
projectId: string;
|
|
17
|
+
branchName: string;
|
|
18
|
+
command?: string;
|
|
19
|
+
stdin?: ShellInput;
|
|
20
|
+
stdout?: ShellOutput;
|
|
21
|
+
signalTarget?: Pick<NodeJS.Process, "on" | "off">;
|
|
22
|
+
createWebSocket?: (url: string, options: ClientOptions) => ShellWebSocket;
|
|
23
|
+
};
|
|
24
|
+
export declare function createShellWebSocketUrl(baseUrl: string): string;
|
|
25
|
+
export declare function runR5dctlShell(options: RunR5dctlShellOptions): Promise<number>;
|
|
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.23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/cjs/cli.cjs",
|
|
6
6
|
"module": "./dist/mjs/cli.mjs",
|
|
@@ -26,8 +26,9 @@
|
|
|
26
26
|
"r5dctl": "dist/cjs/main.cjs"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ricsam/r5d-api": "^0.0.
|
|
30
|
-
"dotenv": "^17"
|
|
29
|
+
"@ricsam/r5d-api": "^0.0.23",
|
|
30
|
+
"dotenv": "^17",
|
|
31
|
+
"ws": "^8.18.3"
|
|
31
32
|
},
|
|
32
33
|
"files": [
|
|
33
34
|
"dist",
|