@vibecodr/cli 0.2.8 → 0.2.9

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.
@@ -42,12 +42,12 @@ async function readStdin() {
42
42
  }
43
43
  return Buffer.concat(chunks).toString("utf8");
44
44
  }
45
- export async function callToolWithRetry(context, toolName, input, allowLogin) {
45
+ export async function callToolWithRetry(context, toolName, input, allowLogin, options) {
46
46
  const { profileName, serverUrl } = await context.tokenManager.resolveProfile(context.globalOptions);
47
47
  const existingSession = await context.tokenManager.getSession(profileName, serverUrl);
48
48
  try {
49
49
  return {
50
- result: await context.runtimeClient.callTool(serverUrl, existingSession?.accessToken, toolName, input),
50
+ result: await context.runtimeClient.callTool(serverUrl, existingSession?.accessToken, toolName, input, options),
51
51
  ...(existingSession ? { session: existingSession } : {})
52
52
  };
53
53
  }
@@ -57,7 +57,7 @@ export async function callToolWithRetry(context, toolName, input, allowLogin) {
57
57
  if (error.machineCode === "auth.required" && existingSession?.refreshToken) {
58
58
  const refreshed = await context.tokenManager.refresh(profileName, existingSession);
59
59
  return {
60
- result: await context.runtimeClient.callTool(serverUrl, refreshed.session.accessToken, toolName, input),
60
+ result: await context.runtimeClient.callTool(serverUrl, refreshed.session.accessToken, toolName, input, options),
61
61
  session: refreshed.session
62
62
  };
63
63
  }
@@ -68,7 +68,7 @@ export async function callToolWithRetry(context, toolName, input, allowLogin) {
68
68
  });
69
69
  const nextSession = await context.tokenManager.getSession(profileName, serverUrl);
70
70
  return {
71
- result: await context.runtimeClient.callTool(serverUrl, nextSession?.accessToken, toolName, input),
71
+ result: await context.runtimeClient.callTool(serverUrl, nextSession?.accessToken, toolName, input, options),
72
72
  ...(nextSession ? { session: nextSession } : {})
73
73
  };
74
74
  }
@@ -10,6 +10,7 @@ const CREATE_STAGED_UPLOAD_TOOL_NAME = "create_staged_upload";
10
10
  const COMPLETE_STAGED_UPLOAD_TOOL_NAME = "complete_staged_upload";
11
11
  const ABORT_STAGED_UPLOAD_TOOL_NAME = "abort_staged_upload";
12
12
  const SOURCE_ZIP_CONTENT_TYPE = "application/zip";
13
+ const DEFAULT_STAGED_UPLOAD_TIMEOUT_SECONDS = 600;
13
14
  const SOURCE_ZIP_CONTENT_TYPES = new Set(["application/zip", "application/x-zip-compressed"]);
14
15
  const COVER_IMAGE_CONTENT_TYPES = new Set(["image/png", "image/jpeg", "image/webp", "image/avif"]);
15
16
  const AVATAR_IMAGE_CONTENT_TYPES = new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
@@ -79,7 +80,7 @@ function readCompleteResult(result, fallbackUploadId) {
79
80
  }
80
81
  function parseUploadArgs(args) {
81
82
  const { flags, positionals } = parseFlags(args, {
82
- valueFlags: ["zip", "image", "file", "kind", "content-type", "idempotency-key", "root-hint", "entry-hint"],
83
+ valueFlags: ["zip", "image", "file", "kind", "content-type", "idempotency-key", "root-hint", "entry-hint", "timeout-sec"],
83
84
  booleanFlags: ["no-login"]
84
85
  });
85
86
  const zipPath = readString(flags["zip"]);
@@ -98,6 +99,7 @@ function parseUploadArgs(args) {
98
99
  const rootHint = readString(flags["root-hint"]);
99
100
  const entryHint = readString(flags["entry-hint"]);
100
101
  const contentType = readString(flags["content-type"]);
102
+ const timeoutSeconds = parseUploadTimeoutSeconds(flags["timeout-sec"]);
101
103
  const rawKind = readString(flags["kind"]);
102
104
  let kind = imagePath ? "cover_image" : "source_zip";
103
105
  if (rawKind) {
@@ -116,9 +118,22 @@ function parseUploadArgs(args) {
116
118
  ...(idempotencyKey ? { idempotencyKey } : {}),
117
119
  ...(rootHint ? { rootHint } : {}),
118
120
  ...(entryHint ? { entryHint } : {}),
121
+ timeoutSeconds,
119
122
  allowLogin: flags["no-login"] !== true,
120
123
  };
121
124
  }
125
+ function parseUploadTimeoutSeconds(value) {
126
+ if (value === undefined)
127
+ return DEFAULT_STAGED_UPLOAD_TIMEOUT_SECONDS;
128
+ if (typeof value !== "string" || !value.trim()) {
129
+ throw new CliError("usage.upload_timeout_invalid", "--timeout-sec must be a positive number of seconds.", EXIT_CODES.usage);
130
+ }
131
+ const parsed = Number(value);
132
+ if (!Number.isFinite(parsed) || parsed <= 0) {
133
+ throw new CliError("usage.upload_timeout_invalid", "--timeout-sec must be a positive number of seconds.", EXIT_CODES.usage);
134
+ }
135
+ return parsed;
136
+ }
122
137
  function sha256Hex(bytes) {
123
138
  return createHash("sha256").update(bytes).digest("hex");
124
139
  }
@@ -158,9 +173,9 @@ function toBlob(bytes, contentType) {
158
173
  new Uint8Array(buffer).set(bytes);
159
174
  return new Blob([buffer], { type: contentType });
160
175
  }
161
- async function abortBestEffort(context, uploadId, allowLogin) {
176
+ async function abortBestEffort(context, uploadId, allowLogin, timeoutSeconds) {
162
177
  try {
163
- await callToolWithRetry(context, ABORT_STAGED_UPLOAD_TOOL_NAME, { uploadId }, allowLogin);
178
+ await callToolWithRetry(context, ABORT_STAGED_UPLOAD_TOOL_NAME, { uploadId }, allowLogin, { timeoutSeconds });
164
179
  }
165
180
  catch {
166
181
  // Best-effort cleanup only. Preserve the upload failure as the surfaced error.
@@ -177,7 +192,7 @@ async function putBytesToStagedUpload(input) {
177
192
  }
178
193
  }
179
194
  export async function runUploadCommand(args, context) {
180
- if (showHelpIfRequested(args, context, "Usage: vibecodr upload --zip <path> [--idempotency-key <key>] [--root-hint <path>] [--entry-hint <path>] [--no-login]\n vibecodr upload --image <path> [--kind cover_image|avatar_image] [--content-type <mime>] [--no-login]"))
195
+ if (showHelpIfRequested(args, context, "Usage: vibecodr upload --zip <path> [--idempotency-key <key>] [--root-hint <path>] [--entry-hint <path>] [--timeout-sec <n>] [--no-login]\n vibecodr upload --image <path> [--kind cover_image|avatar_image] [--content-type <mime>] [--timeout-sec <n>] [--no-login]"))
181
196
  return;
182
197
  const input = parseUploadArgs(args);
183
198
  const fileInfo = await stat(input.filePath).catch((error) => {
@@ -193,6 +208,7 @@ export async function runUploadCommand(args, context) {
193
208
  const fileName = basename(input.filePath) || (input.kind === "source_zip" ? "source.zip" : "image");
194
209
  const contentType = inferContentType(input.kind, fileName, input.contentType);
195
210
  const hash = sha256Hex(bytes);
211
+ const mcpRequestOptions = { timeoutSeconds: input.timeoutSeconds };
196
212
  const { result: createResult } = await callToolWithRetry(context, CREATE_STAGED_UPLOAD_TOOL_NAME, {
197
213
  kind: input.kind,
198
214
  fileName,
@@ -201,7 +217,7 @@ export async function runUploadCommand(args, context) {
201
217
  sha256: hash,
202
218
  createdBySurface: input.kind === "source_zip" ? "cli.upload.zip" : "cli.upload.image",
203
219
  ...(input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}),
204
- }, input.allowLogin);
220
+ }, input.allowLogin, mcpRequestOptions);
205
221
  const created = readCreateResult(createResult);
206
222
  try {
207
223
  await putBytesToStagedUpload({
@@ -214,7 +230,7 @@ export async function runUploadCommand(args, context) {
214
230
  uploadId: created.uploadId,
215
231
  sizeBytes: bytes.byteLength,
216
232
  sha256: hash,
217
- }, input.allowLogin);
233
+ }, input.allowLogin, mcpRequestOptions);
218
234
  const completed = readCompleteResult(completeResult, created.uploadId);
219
235
  const quickPublishPayload = {
220
236
  ...(input.kind === "source_zip"
@@ -264,7 +280,7 @@ export async function runUploadCommand(args, context) {
264
280
  ]);
265
281
  }
266
282
  catch (error) {
267
- await abortBestEffort(context, created.uploadId, input.allowLogin);
283
+ await abortBestEffort(context, created.uploadId, input.allowLogin, input.timeoutSeconds);
268
284
  throw error;
269
285
  }
270
286
  }
@@ -13,8 +13,8 @@ export const CLIENT_INFO = {
13
13
  name: "vibecodr-mcp",
14
14
  version: packageVersion
15
15
  };
16
- export function resolveToolRequestTimeoutMs(args) {
17
- const raw = args["timeoutSeconds"];
16
+ export function resolveToolRequestTimeoutMs(args, options) {
17
+ const raw = options?.timeoutSeconds ?? args["timeoutSeconds"];
18
18
  if (raw === undefined)
19
19
  return undefined;
20
20
  if (typeof raw !== "number" || !Number.isFinite(raw))
@@ -32,9 +32,9 @@ export class McpRuntimeClient {
32
32
  return result.tools;
33
33
  });
34
34
  }
35
- async callTool(serverUrl, accessToken, name, args) {
35
+ async callTool(serverUrl, accessToken, name, args, options) {
36
36
  return await this.withClient(serverUrl, accessToken, async (client) => {
37
- const timeout = resolveToolRequestTimeoutMs(args);
37
+ const timeout = resolveToolRequestTimeoutMs(args, options);
38
38
  return await client.callTool({
39
39
  name,
40
40
  arguments: args
package/docs/commands.md CHANGED
@@ -70,9 +70,9 @@ Known mutating tools require explicit confirmation through `--confirm`. The CLI
70
70
 
71
71
  Syntax:
72
72
 
73
- `vibecodr upload --zip <path> [--idempotency-key <key>] [--root-hint <path>] [--entry-hint <path>] [--no-login]`
73
+ `vibecodr upload --zip <path> [--idempotency-key <key>] [--root-hint <path>] [--entry-hint <path>] [--timeout-sec <n>] [--no-login]`
74
74
 
75
- `vibecodr upload --image <path> [--kind cover_image|avatar_image] [--content-type <mime>] [--no-login]`
75
+ `vibecodr upload --image <path> [--kind cover_image|avatar_image] [--content-type <mime>] [--timeout-sec <n>] [--no-login]`
76
76
 
77
77
  Stages a local ZIP or image through Vibecodr's API-owned upload session flow. The CLI asks the MCP gateway for a short-lived direct R2 PUT URL, uploads the bytes directly to R2, completes server-side verification, and prints safe identifiers only.
78
78
 
@@ -80,6 +80,8 @@ ZIP uploads print a `quickPublishPayload` snippet using `payload.importMode: "st
80
80
 
81
81
  Cover images support PNG, JPEG, WebP, and AVIF. Avatar images support PNG, JPEG, WebP, and GIF.
82
82
 
83
+ Staged upload MCP setup and completion calls use a longer client-side wait by default so large ZIP verification does not fail only because the local CLI stopped waiting. Use `--timeout-sec <n>` only when a slower network needs a different local wait; this value is transport behavior and is not forwarded as a server tool argument.
84
+
83
85
  The presigned URL is a bearer credential and is never printed in command output. Legacy `zip_import` / `fileBase64` remains a compatibility path for small payloads, not the preferred CLI path for whole repos or launch images.
84
86
 
85
87
  ### `pulse-setup`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecodr/cli",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Vibecodr CLI for login, live MCP tool discovery, and live tool invocation.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",