@vwork/cli 0.1.6 → 0.1.8

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/apps.js CHANGED
@@ -1,6 +1,28 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
1
3
  import { stringFlag } from "./config.js";
2
- export async function runAppCommand(command, client) {
4
+ export async function runAppCommand(command, client, cwd = process.cwd()) {
3
5
  const action = command.path[1];
6
+ if (action === "firebase") {
7
+ const subcommand = command.path[2];
8
+ const target = requiredPosition(command, 0, "app id or slug");
9
+ const app = await client.request("GET", `/apps/lookup/${encodeURIComponent(target)}`);
10
+ const path = `/apps/${encodeURIComponent(String(app.id))}/auth/firebase`;
11
+ if (subcommand === "get") {
12
+ return { value: await client.request("GET", path) };
13
+ }
14
+ if (subcommand === "set") {
15
+ return {
16
+ value: await client.request("PUT", path, {
17
+ service_account_json: readFileSync(resolve(cwd, requiredFlag(command, "file")), "utf8").trim()
18
+ })
19
+ };
20
+ }
21
+ if (subcommand === "delete") {
22
+ return { value: await client.request("DELETE", path) };
23
+ }
24
+ throw new Error(`Unknown apps firebase command: ${subcommand ?? ""}`);
25
+ }
4
26
  if (action === "list") {
5
27
  const body = await client.request("GET", "/apps");
6
28
  return { rows: body.apps.map((app) => ({ id: app.id, slug: app.slug, host: app.api_host })) };
package/dist/codegen.js CHANGED
@@ -52,7 +52,7 @@ export async function runCodegenCommand(command, client, config) {
52
52
  };
53
53
  try {
54
54
  return {
55
- value: await client.request("POST", `${appPath}/functions/${encodeURIComponent(functionName)}/codegen`, requestBody)
55
+ value: await client.request("POST", `${appPath}/functions/${encodeURIComponent(functionName)}/${command.flags.project === true ? "codegen-project" : "codegen"}`, requestBody)
56
56
  };
57
57
  }
58
58
  catch (error) {
@@ -87,6 +87,12 @@ export async function runCodegenCommand(command, client, config) {
87
87
  const bytes = extractBytes(bundle);
88
88
  return { value: { run_id: runId, bundle_downloadable: true, bytes: bytes.length } };
89
89
  }
90
+ if (group === "runs" && action === "stop") {
91
+ const runId = requiredFlag(command, "run");
92
+ return {
93
+ value: await client.request("POST", `${appPath}/code-generations/${encodeURIComponent(runId)}/stop`, {})
94
+ };
95
+ }
90
96
  throw new Error(`Unknown codegen command: ${command.path.join(" ")}`);
91
97
  }
92
98
  async function resolveApp(command, client, config) {
@@ -149,11 +155,48 @@ function sleep(ms) {
149
155
  return new Promise((resolve) => setTimeout(resolve, ms));
150
156
  }
151
157
  function mcpConfigSnippet(command, config) {
152
- const workspace = stringFlag(command.flags.workspace) ?? process.cwd();
158
+ const workspace = stringFlag(command.flags.workspace);
153
159
  const appId = stringFlag(command.flags.app) ?? config.appId;
160
+ const apiUrl = stringFlag(command.flags["api-url"]) ?? config.apiUrl;
161
+ const schema = stringFlag(command.flags.schema) ?? config.schema;
162
+ const env = {
163
+ VWORK_API_URL: apiUrl,
164
+ VWORK_MCP_WRITE_MODE: "codegen",
165
+ ...(appId ? { VWORK_APP_ID: appId } : {}),
166
+ VWORK_SCHEMA: schema
167
+ };
168
+ if (!workspace) {
169
+ const remoteUrl = remoteMcpUrl(apiUrl, {
170
+ mode: "codegen",
171
+ ...(appId ? { app: appId } : {}),
172
+ schema
173
+ });
174
+ return {
175
+ server: "vwork-remote-mcp",
176
+ transport: "streamable-http",
177
+ remote_url: remoteUrl,
178
+ auth_header: "Authorization: Bearer ${VWORK_API_KEY}",
179
+ install_hint: "No MCP npm package is required for the default remote server.",
180
+ cli_install_hint: [
181
+ "npm install -g @vwork/cli --registry https://registry.npmjs.org",
182
+ "pnpm install -g @vwork/cli --registry https://registry.npmjs.org"
183
+ ],
184
+ codex_config: {
185
+ mcp_servers: {
186
+ vwork: {
187
+ type: "http",
188
+ url: remoteUrl,
189
+ headers: {
190
+ Authorization: "Bearer ${VWORK_API_KEY}"
191
+ }
192
+ }
193
+ }
194
+ }
195
+ };
196
+ }
154
197
  return {
155
- server: "mcp-vwork",
156
- install_hint: "pnpm --filter @vwork/mcp-vwork build",
198
+ server: "mcp-vwork-local",
199
+ install_hint: "Repository developers only. Use the remote MCP server for normal clients.",
157
200
  cli_install_hint: [
158
201
  "npm install -g @vwork/cli --registry https://registry.npmjs.org",
159
202
  "pnpm install -g @vwork/cli --registry https://registry.npmjs.org"
@@ -163,17 +206,21 @@ function mcpConfigSnippet(command, config) {
163
206
  vwork: {
164
207
  command: "pnpm",
165
208
  args: ["--dir", workspace, "--filter", "@vwork/mcp-vwork", "dev"],
166
- env: {
167
- VWORK_API_URL: stringFlag(command.flags["api-url"]) ?? config.apiUrl,
168
- VWORK_MCP_WRITE_MODE: "codegen",
169
- ...(appId ? { VWORK_APP_ID: appId } : {}),
170
- VWORK_SCHEMA: stringFlag(command.flags.schema) ?? config.schema
171
- }
209
+ env
172
210
  }
173
211
  }
174
212
  }
175
213
  };
176
214
  }
215
+ function remoteMcpUrl(apiUrl, params) {
216
+ const url = new URL(apiUrl);
217
+ url.pathname = url.pathname.replace(/\/+$/, "").replace(/\/api$/, "") + "/mcp";
218
+ url.search = "";
219
+ for (const [key, value] of Object.entries(params)) {
220
+ url.searchParams.set(key, value);
221
+ }
222
+ return url.toString();
223
+ }
177
224
  function extractBytes(response) {
178
225
  return response instanceof Uint8Array ? response : response.bytes;
179
226
  }
package/dist/command.js CHANGED
@@ -1,5 +1,5 @@
1
- const COMMAND_ROOTS = new Set(["apps", "db", "migrations", "functions", "codegen", "secrets", "kv", "queues", "runtime", "observability"]);
2
- const BOOLEAN_FLAGS = new Set(["yes", "verbose", "help", "no-auth", "repair"]);
1
+ const COMMAND_ROOTS = new Set(["apps", "db", "migrations", "functions", "codegen", "secrets", "kv", "queues", "observability"]);
2
+ const BOOLEAN_FLAGS = new Set(["yes", "verbose", "help", "no-auth", "project", "repair"]);
3
3
  export function parseCommand(argv) {
4
4
  const path = [];
5
5
  const positionals = [];
@@ -46,12 +46,18 @@ export function parseCommand(argv) {
46
46
  return { path, positionals, flags };
47
47
  }
48
48
  function maxPathLength(path) {
49
+ if (path[0] === "apps" && path[1] === "firebase")
50
+ return 3;
49
51
  if (path[0] === "db")
50
52
  return 3;
51
53
  if (path[0] === "codegen")
52
54
  return 3;
53
55
  if (path[0] === "kv")
54
56
  return 3;
57
+ if (path[0] === "functions" && path[1] === "firebase-access")
58
+ return 3;
59
+ if (path[0] === "functions" && path[1] === "bundle")
60
+ return 2;
55
61
  if (path[0] === "functions" && path[1] === "previews")
56
62
  return 3;
57
63
  if (path[0] === "functions" && path[1] === "routes")
package/dist/functions.js CHANGED
@@ -1,11 +1,26 @@
1
- import { readFileSync } from "node:fs";
2
- import { resolve } from "node:path";
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { mkdtemp, rm } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { bundleFunctionProject, collectFunctionProject } from "@vwork/function-bundler";
3
7
  import { stringFlag } from "./config.js";
4
8
  export async function runFunctionCommand(command, client, config, cwd) {
5
9
  const action = command.path[1];
6
10
  if (action === "validate") {
7
11
  return { value: await client.request("POST", "/apps/functions/validate-source", { source_code: sourceInput(command, cwd) }) };
8
12
  }
13
+ if (action === "bundle") {
14
+ const projectPath = requiredPosition(command, 0, "project path");
15
+ const absoluteProjectPath = resolve(cwd, projectPath);
16
+ const out = resolve(cwd, stringFlag(command.flags.out) ?? `${projectPath}/dist/index.js`);
17
+ const snapshot = await collectFunctionProject(absoluteProjectPath);
18
+ const result = await bundleSnapshot(snapshot);
19
+ mkdirSync(dirname(out), { recursive: true });
20
+ writeFileSync(out, result.code, "utf8");
21
+ writeFileSync(`${out}.manifest.json`, JSON.stringify(result.manifest, null, 2), "utf8");
22
+ return { value: { path: out, bytes: result.codeSize, hash: result.codeHash } };
23
+ }
9
24
  const app = await resolveApp(command, client, config);
10
25
  const appPath = `/apps/${encodeURIComponent(String(app.id))}`;
11
26
  if (action === "previews") {
@@ -17,10 +32,55 @@ export async function runFunctionCommand(command, client, config, cwd) {
17
32
  if (action === "queue-bindings") {
18
33
  return runFunctionQueueBindingsCommand(command, client, appPath);
19
34
  }
35
+ if (action === "firebase-access") {
36
+ return runFunctionFirebaseAccessCommand(command, client, appPath);
37
+ }
20
38
  if (action === "list") {
21
39
  const body = await client.request("GET", `${appPath}/functions`);
22
40
  return { rows: body.functions };
23
41
  }
42
+ if (action === "pull") {
43
+ const name = requiredPosition(command, 0, "function name");
44
+ const out = stringFlag(command.flags.out) ?? command.positionals[1];
45
+ if (!out)
46
+ throw new Error("Missing --out or output directory");
47
+ const outputDir = resolve(cwd, out);
48
+ assertWritablePullTarget(outputDir, command.flags.yes === true);
49
+ const tree = await client.request("GET", functionPath(appPath, name, "working-tree"));
50
+ const files = await readRemoteWorkingTreeFiles(client, appPath, name, tree);
51
+ for (const file of files) {
52
+ const target = resolveSourceOutputPath(outputDir, file.path);
53
+ mkdirSync(dirname(target), { recursive: true });
54
+ writeFileSync(target, file.content, "utf8");
55
+ }
56
+ return {
57
+ value: {
58
+ path: outputDir,
59
+ function: name,
60
+ files: files.length,
61
+ base_commit_id: tree.base_commit_id ?? null,
62
+ dirty: Boolean(tree.dirty)
63
+ }
64
+ };
65
+ }
66
+ if (action === "status") {
67
+ const name = requiredPosition(command, 0, "function name");
68
+ const projectPath = command.positionals[1] ?? stringFlag(command.flags.out);
69
+ if (!projectPath)
70
+ throw new Error("Missing project directory");
71
+ const projectRoot = resolve(cwd, projectPath);
72
+ const [local, tree] = await Promise.all([
73
+ collectFunctionProject(projectRoot),
74
+ client.request("GET", functionPath(appPath, name, "working-tree"))
75
+ ]);
76
+ const remoteFiles = await readRemoteWorkingTreeFiles(client, appPath, name, tree);
77
+ return {
78
+ value: functionProjectStatus(projectRoot, local.files, remoteFiles, {
79
+ baseCommitId: tree.base_commit_id ?? null,
80
+ dirty: Boolean(tree.dirty)
81
+ })
82
+ };
83
+ }
24
84
  if (action === "inspect") {
25
85
  const name = requiredPosition(command, 0, "function name");
26
86
  const body = await client.request("GET", `${appPath}/functions`);
@@ -39,6 +99,26 @@ export async function runFunctionCommand(command, client, config, cwd) {
39
99
  }
40
100
  if (action === "deploy") {
41
101
  const name = requiredPosition(command, 0, "function name");
102
+ const projectPath = command.positionals[1];
103
+ if (projectPath && !stringFlag(command.flags.bundle)) {
104
+ const absoluteProjectPath = resolve(cwd, projectPath);
105
+ const snapshot = await collectFunctionProject(absoluteProjectPath);
106
+ const bundled = await bundleSnapshot(snapshot);
107
+ return {
108
+ value: await client.request("POST", functionPath(appPath, name, "deploy"), {
109
+ source_files: snapshot.files.map((file) => ({
110
+ path: file.path,
111
+ language: file.language,
112
+ content: file.content
113
+ })),
114
+ bundled_code: bundled.code,
115
+ build_manifest: bundled.manifest
116
+ })
117
+ };
118
+ }
119
+ if (stringFlag(command.flags.bundle)) {
120
+ return { value: await client.request("POST", functionPath(appPath, name, "deploy"), { bundled_code: sourceInput(command, cwd) }) };
121
+ }
42
122
  return { value: await client.request("POST", functionPath(appPath, name, "deploy"), { source_code: sourceInput(command, cwd) }) };
43
123
  }
44
124
  if (action === "artifact") {
@@ -63,6 +143,22 @@ export async function runFunctionCommand(command, client, config, cwd) {
63
143
  if (artifact) {
64
144
  return { value: await client.request("POST", `${functionPath(appPath, name, "artifacts")}/${encodeURIComponent(artifact)}/invoke`, { body }) };
65
145
  }
146
+ const projectPath = command.positionals[1];
147
+ if (projectPath && !stringFlag(command.flags.file) && !stringFlag(command.flags["source-code"]) && !stringFlag(command.flags.bundle)) {
148
+ const absoluteProjectPath = resolve(cwd, projectPath);
149
+ const snapshot = await collectFunctionProject(absoluteProjectPath);
150
+ const bundled = await bundleSnapshot(snapshot);
151
+ const entrypoint = snapshot.files.find((file) => file.path === snapshot.entrypoint);
152
+ if (!entrypoint)
153
+ throw new Error(`Missing function entrypoint: ${snapshot.entrypoint}`);
154
+ return {
155
+ value: await client.request("POST", functionPath(appPath, name, "draft-invoke"), {
156
+ source_code: entrypoint.content,
157
+ bundled_code: bundled.code,
158
+ body
159
+ })
160
+ };
161
+ }
66
162
  return { value: await client.request("POST", functionPath(appPath, name, "draft-invoke"), { source_code: sourceInput(command, cwd), body }) };
67
163
  }
68
164
  if (action === "delete") {
@@ -77,6 +173,15 @@ export async function runFunctionCommand(command, client, config, cwd) {
77
173
  }
78
174
  throw new Error(`Unknown functions command: ${action ?? ""}`);
79
175
  }
176
+ async function bundleSnapshot(snapshot) {
177
+ const workspaceRoot = await mkdtemp(join(tmpdir(), "vwork-cli-function-bundle-"));
178
+ try {
179
+ return await bundleFunctionProject({ snapshot, workspaceRoot, installDependencies: true });
180
+ }
181
+ finally {
182
+ await rm(workspaceRoot, { recursive: true, force: true });
183
+ }
184
+ }
80
185
  async function runPreviewCommand(command, client, appPath) {
81
186
  const action = command.path[2];
82
187
  const name = requiredPosition(command, 0, "function name");
@@ -157,6 +262,18 @@ async function runFunctionQueueBindingsCommand(command, client, appPath) {
157
262
  }
158
263
  throw new Error(`Unknown functions queue-bindings command: ${action ?? ""}`);
159
264
  }
265
+ async function runFunctionFirebaseAccessCommand(command, client, appPath) {
266
+ const action = command.path[2];
267
+ const name = requiredPosition(command, 0, "function name");
268
+ const basePath = functionPath(appPath, name, "auth/firebase");
269
+ if (action === "get") {
270
+ return { value: await client.request("GET", basePath) };
271
+ }
272
+ if (action === "enable" || action === "disable") {
273
+ return { value: await client.request("PUT", basePath, { enabled: action === "enable" }) };
274
+ }
275
+ throw new Error(`Unknown functions firebase-access command: ${action ?? ""}`);
276
+ }
160
277
  async function resolveApp(command, client, config) {
161
278
  const target = stringFlag(command.flags.app) ?? config.appId;
162
279
  if (!target)
@@ -166,6 +283,67 @@ async function resolveApp(command, client, config) {
166
283
  function functionPath(appPath, name, suffix) {
167
284
  return `${appPath}/functions/${encodeURIComponent(name)}/${suffix}`;
168
285
  }
286
+ async function readRemoteWorkingTreeFiles(client, appPath, functionName, tree) {
287
+ const files = [];
288
+ for (const file of tree.files) {
289
+ const query = new URLSearchParams({ path: file.path });
290
+ files.push(await client.request("GET", `${functionPath(appPath, functionName, "working-tree/files")}?${query.toString()}`));
291
+ }
292
+ files.sort((a, b) => a.path.localeCompare(b.path));
293
+ return files;
294
+ }
295
+ function assertWritablePullTarget(outputDir, overwrite) {
296
+ if (!existsSync(outputDir)) {
297
+ mkdirSync(outputDir, { recursive: true });
298
+ return;
299
+ }
300
+ if (!overwrite && readdirSync(outputDir).length > 0) {
301
+ throw new Error(`Output directory is not empty: ${outputDir}. Pass --yes to overwrite matching files.`);
302
+ }
303
+ }
304
+ function resolveSourceOutputPath(root, path) {
305
+ const normalized = path.replaceAll("\\", "/").replace(/^\/+/, "");
306
+ if (!normalized || normalized.startsWith("../") || normalized.includes("/../") || /^[a-zA-Z]:/.test(path)) {
307
+ throw new Error(`Invalid source path from server: ${path}`);
308
+ }
309
+ return resolve(root, normalized);
310
+ }
311
+ function functionProjectStatus(projectRoot, localFiles, remoteFiles, remoteState) {
312
+ const localByPath = new Map(localFiles.map((file) => [file.path, file]));
313
+ const remoteByPath = new Map(remoteFiles.map((file) => [file.path, file]));
314
+ const changed = [];
315
+ const missingLocal = [];
316
+ const extraLocal = [];
317
+ for (const remote of remoteFiles) {
318
+ const local = localByPath.get(remote.path);
319
+ if (!local) {
320
+ missingLocal.push(remote.path);
321
+ continue;
322
+ }
323
+ if (sha256(local.content) !== sha256(remote.content)) {
324
+ changed.push(remote.path);
325
+ }
326
+ }
327
+ for (const local of localFiles) {
328
+ if (!remoteByPath.has(local.path)) {
329
+ extraLocal.push(local.path);
330
+ }
331
+ }
332
+ return {
333
+ path: projectRoot,
334
+ base_commit_id: remoteState.baseCommitId,
335
+ remote_dirty: remoteState.dirty,
336
+ local_files: localFiles.length,
337
+ remote_files: remoteFiles.length,
338
+ changed,
339
+ missing_local: missingLocal,
340
+ extra_local: extraLocal,
341
+ clean: changed.length === 0 && missingLocal.length === 0 && extraLocal.length === 0
342
+ };
343
+ }
344
+ function sha256(value) {
345
+ return createHash("sha256").update(value).digest("hex");
346
+ }
169
347
  function sourceInput(command, cwd) {
170
348
  const inline = stringFlag(command.flags["source-code"]);
171
349
  if (inline)
package/dist/index.js CHANGED
File without changes
@@ -81,6 +81,7 @@ export class VWorkCliCommand extends Command {
81
81
  yes: Flags.boolean({ description: "Confirm the operation" }),
82
82
  verbose: Flags.boolean({ description: "Enable verbose output" }),
83
83
  "no-auth": Flags.boolean({ description: "Disable function auth requirement" }),
84
+ project: Flags.boolean({ description: "Generate multi-file function project source" }),
84
85
  repair: Flags.boolean({ description: "Enable codegen repair attempts" }),
85
86
  "draft-invoke-body": Flags.string({ description: "Draft invocation request body JSON object for codegen validation" }),
86
87
  "draft-invoke-bindings": Flags.string({ description: "Draft invocation binding overrides JSON object for codegen validation" }),
@@ -194,7 +195,7 @@ function parsedFlags(flags) {
194
195
  }
195
196
  async function dispatchCommand(command, client, config, cwd) {
196
197
  if (command.path[0] === "apps")
197
- return runAppCommand(command, client);
198
+ return runAppCommand(command, client, cwd);
198
199
  if (command.path[0] === "codegen")
199
200
  return runCodegenCommand(command, client, config);
200
201
  if (command.path[0] === "db")
@@ -224,12 +225,18 @@ function normalizeApiBaseUrl(value) {
224
225
  return value.replace(/\/+$/, "");
225
226
  }
226
227
  function maxPathLength(path) {
228
+ if (path[0] === "apps" && path[1] === "firebase")
229
+ return 3;
227
230
  if (path[0] === "db")
228
231
  return 3;
229
232
  if (path[0] === "codegen")
230
233
  return 3;
231
234
  if (path[0] === "kv")
232
235
  return 3;
236
+ if (path[0] === "functions" && path[1] === "firebase-access")
237
+ return 3;
238
+ if (path[0] === "functions" && path[1] === "bundle")
239
+ return 2;
233
240
  if (path[0] === "functions" && path[1] === "previews")
234
241
  return 3;
235
242
  if (path[0] === "functions" && path[1] === "routes")
@@ -245,15 +252,14 @@ function helpText() {
245
252
  "Usage: vwork <resource> <command> [flags]",
246
253
  "",
247
254
  "Resources:",
248
- " apps list, inspect, create, delete, bind-data-source",
255
+ " apps list, inspect, create, delete, bind-data-source, firebase",
249
256
  " db tables list, sql exec, sql preview, rows select|insert|update|delete",
250
257
  " migrations list, runs, create, up, down, delete",
251
- " functions list, inspect, create, deploy, invoke, artifacts, artifact, publish, rollback, metrics, previews, validate",
252
- " codegen mobile from-schema, function generate, runs list|download|verify, skill install-curl",
258
+ " functions list, inspect, create, deploy, bundle, invoke, artifacts, artifact, publish, rollback, metrics, previews, firebase-access, validate",
259
+ " codegen mobile from-schema, function generate, runs list|download|verify|stop, skill install-curl",
253
260
  " secrets list",
254
261
  " kv namespaces list|create, entries list, stats, metrics",
255
262
  " queues list, create, update, delete",
256
- " runtime latest, publish",
257
263
  " observability access-logs",
258
264
  " login authenticate with VWork",
259
265
  " whoami show the authenticated VWork user",
@@ -273,7 +279,10 @@ function resourceHelpText(resource) {
273
279
  "inspect <app>",
274
280
  "create --name <name> --slug <slug> [--host <host>] --data-source <id>",
275
281
  "delete <app>",
276
- "bind-data-source <app> --data-source <id>"
282
+ "bind-data-source <app> --data-source <id>",
283
+ "firebase get <app>",
284
+ "firebase set <app> --file <service-account.json>",
285
+ "firebase delete <app>"
277
286
  ],
278
287
  db: [
279
288
  "tables list --app <app>",
@@ -295,8 +304,12 @@ function resourceHelpText(resource) {
295
304
  functions: [
296
305
  "list --app <app>",
297
306
  "inspect <name> --app <app>",
307
+ "pull <name> --app <app> --out <dir>",
308
+ "status <name> <path> --app <app>",
298
309
  "create <name> --app <app> --file <path>",
310
+ "deploy <name> <path> --app <app>",
299
311
  "deploy <name> --app <app> --bundle <path>",
312
+ "bundle <project-dir> --out <dist/index.js>",
300
313
  "invoke <name> --app <app> --file <path>",
301
314
  "artifact <name> --app <app> --file <path>",
302
315
  "artifacts <name> --app <app>",
@@ -304,18 +317,22 @@ function resourceHelpText(resource) {
304
317
  "rollback <name> --app <app> --artifact <id>",
305
318
  "metrics <name> --app <app>",
306
319
  "previews create <name> --app <app> --artifact <id>",
320
+ "firebase-access get <name> --app <app>",
321
+ "firebase-access enable <name> --app <app>",
322
+ "firebase-access disable <name> --app <app>",
307
323
  "queue-bindings list <name> --app <app>",
308
324
  "queue-bindings replace <name> --app <app> --queue <queue_id:mode>",
309
325
  "validate --file <path>"
310
326
  ],
311
327
  codegen: [
312
328
  "mobile from-schema --app <app> [--schema <schema>] [--table <table>] [--prompt <text>]",
313
- "function generate --app <app> --function <name> --prompt <text> [--source-code <code>] [--binding <name>...] [--repair] [--draft-invoke-body <json>] [--draft-invoke-bindings <json>]",
329
+ "function generate --app <app> --function <name> --prompt <text> [--project] [--source-code <code>] [--binding <name>...] [--repair] [--draft-invoke-body <json>] [--draft-invoke-bindings <json>]",
314
330
  "skill install-curl [--base-url <raw-base-url>]",
315
331
  "mcp config-snippet [--workspace <repo-path>] [--api-url <url>] [--app <app>] [--schema <schema>]",
316
332
  "runs list --app <app>",
317
333
  "runs download --app <app> --run <id> --out <zip>",
318
- "runs verify --app <app> --run <id>"
334
+ "runs verify --app <app> --run <id>",
335
+ "runs stop --app <app> --run <id>"
319
336
  ],
320
337
  secrets: [
321
338
  "list --app <app>"
@@ -333,10 +350,6 @@ function resourceHelpText(resource) {
333
350
  "update <queue-id> --app <app> [--batch-size <n>] [--dead-letter-enabled true|false]",
334
351
  "delete <queue-id> --app <app>"
335
352
  ],
336
- runtime: [
337
- "latest",
338
- "publish"
339
- ],
340
353
  observability: [
341
354
  "access-logs [--source <source>] [--app <app>] [--limit <n>]"
342
355
  ],
package/dist/runtime.js CHANGED
@@ -1,11 +1,5 @@
1
1
  export async function runRuntimeCommand(command, client) {
2
- const action = command.path[1];
3
- if (action === "latest") {
4
- const body = await client.request("GET", "/runtime-config/latest");
5
- return { value: body.runtime_config };
6
- }
7
- if (action === "publish") {
8
- return { value: await client.request("POST", "/runtime-config/publish") };
9
- }
10
- throw new Error(`Unknown runtime command: ${action ?? ""}`);
2
+ void command;
3
+ void client;
4
+ throw new Error("Runtime commands are not exposed in VWork CLI. Use function and observability commands for user-facing runtime data.");
11
5
  }
package/package.json CHANGED
@@ -1,31 +1,32 @@
1
- {
2
- "name": "@vwork/cli",
3
- "version": "0.1.6",
4
- "type": "module",
5
- "files": [
6
- "dist/*.js",
7
- "dist/auth/*.js",
8
- "package.json"
9
- ],
10
- "publishConfig": {
11
- "registry": "https://registry.npmjs.org/"
12
- },
13
- "bin": {
14
- "vwork": "dist/index.js"
15
- },
16
- "scripts": {
17
- "dev": "tsx src/index.ts",
18
- "build": "tsc -p tsconfig.json",
19
- "test": "pnpm --filter @vwork/platform-client build && tsx --test src/test/*.test.ts",
20
- "typecheck": "pnpm --filter @vwork/platform-client build && tsc -p tsconfig.json --noEmit"
21
- },
22
- "dependencies": {
23
- "@oclif/core": "^4.11.7",
24
- "@vwork/platform-client": "^0.1.0"
25
- },
26
- "devDependencies": {
27
- "@types/node": "^22.10.2",
28
- "tsx": "^4.19.2",
29
- "typescript": "^5.7.2"
30
- }
31
- }
1
+ {
2
+ "name": "@vwork/cli",
3
+ "version": "0.1.8",
4
+ "type": "module",
5
+ "files": [
6
+ "dist/*.js",
7
+ "dist/auth/*.js",
8
+ "package.json"
9
+ ],
10
+ "publishConfig": {
11
+ "registry": "https://registry.npmjs.org/"
12
+ },
13
+ "bin": {
14
+ "vwork": "dist/index.js"
15
+ },
16
+ "dependencies": {
17
+ "@oclif/core": "^4.11.7",
18
+ "@vwork/platform-client": "^0.1.0",
19
+ "@vwork/function-bundler": "0.1.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.10.2",
23
+ "tsx": "^4.19.2",
24
+ "typescript": "^5.7.2"
25
+ },
26
+ "scripts": {
27
+ "dev": "tsx src/index.ts",
28
+ "build": "tsc -p tsconfig.json",
29
+ "test": "pnpm --filter @vwork/function-bundler build && pnpm --filter @vwork/platform-client build && tsx --test src/test/*.test.ts",
30
+ "typecheck": "pnpm --filter @vwork/function-bundler build && pnpm --filter @vwork/platform-client build && tsc -p tsconfig.json --noEmit"
31
+ }
32
+ }
package/dist/client.js DELETED
@@ -1,45 +0,0 @@
1
- export class PlatformApiError extends Error {
2
- code;
3
- status;
4
- requestId;
5
- constructor(input) {
6
- super(input.message);
7
- this.code = input.code;
8
- this.status = input.status;
9
- this.requestId = input.requestId;
10
- }
11
- }
12
- export class PlatformClient {
13
- apiUrl;
14
- trustedUserId;
15
- fetchImpl;
16
- constructor(input) {
17
- this.apiUrl = input.apiUrl.replace(/\/+$/, "");
18
- this.trustedUserId = input.trustedUserId;
19
- this.fetchImpl = input.fetch ?? fetch;
20
- }
21
- async request(method, path, body) {
22
- const headers = new Headers({ accept: "application/json" });
23
- if (body !== undefined)
24
- headers.set("content-type", "application/json");
25
- if (this.trustedUserId)
26
- headers.set("x-vwork-cli-user-id", this.trustedUserId);
27
- const response = await this.fetchImpl(new Request(`${this.apiUrl}${path}`, {
28
- method,
29
- headers,
30
- body: body === undefined ? undefined : JSON.stringify(body)
31
- }));
32
- const text = await response.text();
33
- const parsed = text ? JSON.parse(text) : null;
34
- if (!response.ok) {
35
- const error = parsed && typeof parsed === "object" && "error" in parsed ? parsed.error : {};
36
- throw new PlatformApiError({
37
- code: error.code ?? "PLATFORM_API_ERROR",
38
- message: error.message ?? `Platform API request failed with status ${response.status}`,
39
- status: response.status,
40
- requestId: response.headers.get("x-request-id")
41
- });
42
- }
43
- return parsed;
44
- }
45
- }