@coderook/cli 0.37.0 → 0.38.2

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.
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.commandWorkspace = commandWorkspace;
4
+ exports.commandWorkspaceMcp = commandWorkspaceMcp;
5
+ const promises_1 = require("node:fs/promises");
6
+ const node_readline_1 = require("node:readline");
7
+ const api_js_1 = require("./api.js");
8
+ const config_js_1 = require("./config.js");
9
+ async function commandWorkspace(parsed) {
10
+ const [operation, projectRef, workspace, runId] = parsed.positional;
11
+ if (!operation || !projectRef)
12
+ throw new Error("Use cbx workspace <operation> <project> [workspace]. See cbx help workspace.");
13
+ const project = await (0, api_js_1.findProject)(projectRef);
14
+ if (!project)
15
+ throw new Error(`No accessible project ${projectRef}`);
16
+ const root = `/v1/repositories/${encodeURIComponent(project.id)}`;
17
+ const flag = (name) => {
18
+ const value = parsed.flags.get(name);
19
+ return typeof value === "string" ? value : undefined;
20
+ };
21
+ const integer = (name) => {
22
+ const value = flag(name);
23
+ if (value === undefined)
24
+ return undefined;
25
+ const n = Number(value);
26
+ if (!Number.isSafeInteger(n) || n < 0)
27
+ throw new Error(`${name} must be a non-negative integer`);
28
+ return n;
29
+ };
30
+ let data = {};
31
+ const source = flag("data-file");
32
+ if (source) {
33
+ const raw = JSON.parse(await (0, promises_1.readFile)(source, "utf8"));
34
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
35
+ throw new Error("The data file must contain a JSON object");
36
+ data = raw;
37
+ }
38
+ const key = flag("key") ?? data.operationKey;
39
+ const revision = integer("revision") ?? data.expectedRevision;
40
+ let path = `${root}/workspaces`, method = "GET", body;
41
+ if (operation === "open") {
42
+ method = "POST";
43
+ body = {
44
+ ...data,
45
+ name: flag("name") ?? data.name,
46
+ track: flag("track") ?? data.track,
47
+ baseVersionId: flag("base-version") ?? data.baseVersionId,
48
+ operationKey: key,
49
+ };
50
+ }
51
+ else if (operation === "recipe") {
52
+ if (!workspace)
53
+ throw new Error("Name the recipe after the project");
54
+ path = `${root}/workspace-recipes/${encodeURIComponent(workspace)}`;
55
+ method = "PUT";
56
+ body = data;
57
+ }
58
+ else if (operation !== "list") {
59
+ if (!workspace)
60
+ throw new Error("Supply the remote workspace ID; this command does not use a local project folder");
61
+ path += `/${encodeURIComponent(workspace)}`;
62
+ if (operation === "read" || operation === "files") {
63
+ const q = new URLSearchParams();
64
+ for (const [name, value] of Object.entries({
65
+ path: flag("path"),
66
+ revision: integer("revision"),
67
+ startLine: integer("start-line"),
68
+ lines: integer("lines"),
69
+ offset: integer("offset"),
70
+ }))
71
+ if (value !== undefined)
72
+ q.set(name, String(value));
73
+ path += `/files?${q}`;
74
+ }
75
+ else if (operation === "search")
76
+ path += `/search?${new URLSearchParams({ q: flag("query") ?? "", offset: String(integer("offset") ?? 0) })}`;
77
+ else if (operation === "diff")
78
+ path += `/diff?offset=${integer("offset") ?? 0}`;
79
+ else if (operation === "patch") {
80
+ path += "/changes";
81
+ method = "POST";
82
+ body = { ...data, operationKey: key, expectedRevision: revision };
83
+ }
84
+ else if (operation === "save") {
85
+ path += "/save";
86
+ method = "POST";
87
+ body = {
88
+ ...data,
89
+ operationKey: key,
90
+ expectedRevision: revision,
91
+ message: flag("message") ?? flag("m") ?? data.message,
92
+ };
93
+ }
94
+ else if (operation === "run" ||
95
+ operation === "deploy" ||
96
+ operation === "preview") {
97
+ path += "/runs";
98
+ method = "POST";
99
+ body = {
100
+ ...data,
101
+ operationKey: key,
102
+ expectedRevision: revision,
103
+ recipe: flag("recipe") ?? data.recipe,
104
+ ...(operation === "deploy"
105
+ ? {
106
+ target: flag("target") ?? data.target,
107
+ savedVersionId: flag("version") ?? data.savedVersionId,
108
+ }
109
+ : {}),
110
+ };
111
+ }
112
+ else if (operation === "logs" ||
113
+ operation === "runs" ||
114
+ operation === "cancel") {
115
+ if (!runId)
116
+ throw new Error("Supply the job ID after the workspace ID");
117
+ path += `/runs/${encodeURIComponent(runId)}`;
118
+ if (operation === "cancel") {
119
+ path += "/cancel";
120
+ method = "POST";
121
+ body = {};
122
+ }
123
+ }
124
+ else if (operation === "handoff" || operation === "close") {
125
+ method = "PATCH";
126
+ body = {
127
+ ...data,
128
+ expectedRevision: revision,
129
+ note: flag("note") ?? data.note,
130
+ ...(operation === "close" ? { archived: true } : {}),
131
+ };
132
+ }
133
+ else if (operation !== "show")
134
+ throw new Error(`Unknown workspace operation ${operation}`);
135
+ }
136
+ const result = await (0, api_js_1.callApi)(path, {
137
+ method,
138
+ ...(body === undefined ? {} : { body }),
139
+ });
140
+ console.log(JSON.stringify(result, null, 2));
141
+ return 0;
142
+ }
143
+ /** Explicit remote/write-capable alternative; the original cbx mcp stays read-only. */
144
+ async function commandWorkspaceMcp() {
145
+ const lines = (0, node_readline_1.createInterface)({ input: process.stdin, crlfDelay: Infinity });
146
+ for await (const line of lines) {
147
+ if (!line.trim())
148
+ continue;
149
+ let request;
150
+ try {
151
+ if (Buffer.byteLength(line) > 2 * 1024 * 1024)
152
+ throw new Error("MCP request exceeds 2 MiB");
153
+ request = JSON.parse(line);
154
+ }
155
+ catch {
156
+ process.stdout.write(JSON.stringify({
157
+ jsonrpc: "2.0",
158
+ id: null,
159
+ error: { code: -32700, message: "Invalid or oversized JSON request" },
160
+ }) + "\n");
161
+ continue;
162
+ }
163
+ try {
164
+ const token = await (0, config_js_1.loadToken)();
165
+ if (!token)
166
+ throw new Error("Sign in with cbx sign-in before using remote workspace tools");
167
+ const response = await fetch(`${(0, config_js_1.apiOrigin)()}/mcp`, {
168
+ method: "POST",
169
+ headers: {
170
+ authorization: `Bearer ${token}`,
171
+ "content-type": "application/json",
172
+ },
173
+ body: line,
174
+ signal: AbortSignal.timeout(180000),
175
+ });
176
+ if (response.status === 202)
177
+ continue;
178
+ const output = await response.json();
179
+ if (!response.ok && request.id !== undefined)
180
+ throw new Error("CodeRook refused the MCP connection; check sign-in and workspace scopes");
181
+ process.stdout.write(JSON.stringify(output) + "\n");
182
+ }
183
+ catch (error) {
184
+ if (request.id !== undefined)
185
+ process.stdout.write(JSON.stringify({
186
+ jsonrpc: "2.0",
187
+ id: request.id,
188
+ error: {
189
+ code: -32001,
190
+ message: error instanceof Error
191
+ ? error.message
192
+ : "Remote workspace request failed",
193
+ },
194
+ }) + "\n");
195
+ }
196
+ }
197
+ return 0;
198
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coderook/cli",
3
- "version": "0.37.0",
3
+ "version": "0.38.2",
4
4
  "description": "The command line for CodeRook (coderook.com): version control and project hosting for projects of any size. Every save is a compressed snapshot, private or public, never used to train AI.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://coderook.com",
@@ -52,7 +52,8 @@
52
52
  "test:race": "node test/race-attempts.mjs",
53
53
  "test:runner": "node test/runner-live.mjs",
54
54
  "sync:plugin": "node scripts/sync-plugin-version.mjs",
55
- "test:track": "node test/get-track.mjs"
55
+ "test:track": "node test/get-track.mjs",
56
+ "package:remote": "node ../scripts/package-cli-release.mjs"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@types/node": "24.10.1",