replicas-cli 0.2.121 → 0.2.122

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.
@@ -7489,6 +7489,27 @@ var SANDBOX_PATHS = {
7489
7489
  REPLICAS_MCPS_FILE: "/home/ubuntu/.replicas/mcps.json"
7490
7490
  };
7491
7491
 
7492
+ // ../shared/src/urls.ts
7493
+ function getWorkspaceDashboardUrl(workspaceId, options = {}) {
7494
+ const { encodeWorkspaceId = true, mode } = options;
7495
+ const workspaceIdValue = encodeWorkspaceId ? encodeURIComponent(workspaceId) : workspaceId;
7496
+ const modeParam = mode ? `?mode=${encodeURIComponent(mode)}` : "";
7497
+ return `https://www.replicas.dev/dashboard/workspaces/${workspaceIdValue}${modeParam}`;
7498
+ }
7499
+ var PR_URL_REGEX = /github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/;
7500
+ function parsePrUrl(url) {
7501
+ const match = url.match(PR_URL_REGEX);
7502
+ if (!match) return null;
7503
+ const [, owner, repo, numberStr] = match;
7504
+ const number = Number.parseInt(numberStr, 10);
7505
+ if (!Number.isFinite(number)) return null;
7506
+ return { owner, repo, number };
7507
+ }
7508
+ function extractPrNumber(url) {
7509
+ const parsed = parsePrUrl(url);
7510
+ return parsed ? String(parsed.number) : null;
7511
+ }
7512
+
7492
7513
  // ../shared/src/replicas-config.ts
7493
7514
  var import_yaml = __toESM(require_dist());
7494
7515
 
@@ -7544,21 +7565,6 @@ var GITHUB_TRIGGER = {
7544
7565
  ]
7545
7566
  };
7546
7567
 
7547
- // ../shared/src/urls.ts
7548
- var PR_URL_REGEX = /github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/;
7549
- function parsePrUrl(url) {
7550
- const match = url.match(PR_URL_REGEX);
7551
- if (!match) return null;
7552
- const [, owner, repo, numberStr] = match;
7553
- const number = Number.parseInt(numberStr, 10);
7554
- if (!Number.isFinite(number)) return null;
7555
- return { owner, repo, number };
7556
- }
7557
- function extractPrNumber(url) {
7558
- const parsed = parsePrUrl(url);
7559
- return parsed ? String(parsed.number) : null;
7560
- }
7561
-
7562
7568
  // ../shared/src/prompts.ts
7563
7569
  var REPLICAS_INSTRUCTIONS_TAG = "replicas_instructions";
7564
7570
  function removeTag(text, tag) {
@@ -8257,6 +8263,14 @@ function createMockWorkspaceRecord(organizationId, name, environmentId) {
8257
8263
  };
8258
8264
  }
8259
8265
 
8266
+ // ../shared/src/object-store/types.ts
8267
+ var MEDIA_KIND = {
8268
+ IMAGE: "image",
8269
+ VIDEO: "video",
8270
+ AUDIO: "audio"
8271
+ };
8272
+ var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
8273
+
8260
8274
  // ../shared/src/pricing.ts
8261
8275
  var PLANS = {
8262
8276
  hobby: {
@@ -8689,14 +8703,6 @@ var SOURCE_CONFIG = {
8689
8703
  plan_quote: { label: "Plan", color: "#66bb6a" }
8690
8704
  };
8691
8705
 
8692
- // ../shared/src/object-store/types.ts
8693
- var MEDIA_KIND = {
8694
- IMAGE: "image",
8695
- VIDEO: "video",
8696
- AUDIO: "audio"
8697
- };
8698
- var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
8699
-
8700
8706
  export {
8701
8707
  readConfig,
8702
8708
  writeConfig,
@@ -8711,6 +8717,7 @@ export {
8711
8717
  getValidToken,
8712
8718
  getCurrentUser,
8713
8719
  SANDBOX_PATHS,
8720
+ getWorkspaceDashboardUrl,
8714
8721
  extractPrNumber,
8715
8722
  REPLICAS_CONFIG_FILENAMES,
8716
8723
  getInitialChatId,
@@ -8722,5 +8729,7 @@ export {
8722
8729
  parseUserMessage,
8723
8730
  SOURCE_CONFIG,
8724
8731
  buildGroups,
8725
- createMockWorkspaceRecord
8732
+ createMockWorkspaceRecord,
8733
+ MEDIA_KIND,
8734
+ MEDIA_KINDS
8726
8735
  };
package/dist/index.mjs CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  import {
3
3
  GITHUB_TRIGGER,
4
+ MEDIA_KIND,
5
+ MEDIA_KINDS,
4
6
  REPLICAS_CONFIG_FILENAMES,
5
7
  SANDBOX_PATHS,
6
8
  deleteConfig,
@@ -8,6 +10,7 @@ import {
8
10
  getIdeCommand,
9
11
  getOrganizationId,
10
12
  getValidToken,
13
+ getWorkspaceDashboardUrl,
11
14
  isAgentMode,
12
15
  isAuthenticated,
13
16
  parseAgentEvents,
@@ -16,7 +19,7 @@ import {
16
19
  setIdeCommand,
17
20
  setOrganizationId,
18
21
  writeConfig
19
- } from "./chunk-CPVJVKSG.mjs";
22
+ } from "./chunk-BY7XUH2G.mjs";
20
23
  import "./chunk-FFDYI4OH.mjs";
21
24
 
22
25
  // src/index.ts
@@ -88,8 +91,8 @@ async function orgAuthenticatedFetch(url, options) {
88
91
  }
89
92
  const headers = {
90
93
  "Authorization": `Bearer ${token}`,
91
- "Replicas-Org-Id": organizationId,
92
94
  "Content-Type": "application/json",
95
+ "Replicas-Org-Id": organizationId,
93
96
  ...options?.headers || {}
94
97
  };
95
98
  const absoluteUrl = `${MONOLITH_URL}${url}`;
@@ -2305,21 +2308,24 @@ import chalk17 from "chalk";
2305
2308
  // src/lib/agent-api.ts
2306
2309
  var MONOLITH_URL3 = process.env.MONOLITH_URL || process.env.REPLICAS_MONOLITH_URL || "https://api.replicas.dev";
2307
2310
  var ENGINE_PORT = process.env.REPLICAS_ENGINE_PORT || "3737";
2308
- async function agentFetch(path4, options) {
2311
+ async function agentFetch(path5, options) {
2309
2312
  const config2 = readAgentConfig();
2310
2313
  if (!config2) {
2311
2314
  throw new Error("Agent mode config not found");
2312
2315
  }
2316
+ const isFormData = options?.body instanceof FormData;
2313
2317
  const headers = {
2314
2318
  "Authorization": `Bearer ${config2.engine_secret}`,
2315
2319
  "X-Workspace-Id": config2.workspace_id,
2316
- "Content-Type": "application/json",
2320
+ // FormData sets its own multipart Content-Type with boundary.
2321
+ ...isFormData ? {} : { "Content-Type": "application/json" },
2317
2322
  ...options?.headers || {}
2318
2323
  };
2319
- const response = await fetch(`${MONOLITH_URL3}${path4}`, {
2324
+ const body = options?.body === void 0 ? void 0 : isFormData ? options.body : JSON.stringify(options.body);
2325
+ const response = await fetch(`${MONOLITH_URL3}${path5}`, {
2320
2326
  ...options,
2321
2327
  headers,
2322
- body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0
2328
+ body
2323
2329
  });
2324
2330
  if (!response.ok) {
2325
2331
  const error = await response.json().catch(() => ({ error: "Request failed" }));
@@ -2327,7 +2333,7 @@ async function agentFetch(path4, options) {
2327
2333
  }
2328
2334
  return response.json();
2329
2335
  }
2330
- async function engineFetch(path4, options) {
2336
+ async function engineFetch(path5, options) {
2331
2337
  const config2 = readAgentConfig();
2332
2338
  if (!config2) {
2333
2339
  throw new Error("Agent mode config not found");
@@ -2337,7 +2343,7 @@ async function engineFetch(path4, options) {
2337
2343
  "Content-Type": "application/json",
2338
2344
  ...options?.headers || {}
2339
2345
  };
2340
- const response = await fetch(`http://localhost:${ENGINE_PORT}${path4}`, {
2346
+ const response = await fetch(`http://localhost:${ENGINE_PORT}${path5}`, {
2341
2347
  ...options,
2342
2348
  headers,
2343
2349
  body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0
@@ -2406,6 +2412,76 @@ async function previewAddCommand(workspaceId, options) {
2406
2412
  console.log(chalk17.green(`Preview created: ${result.preview.publicUrl}`));
2407
2413
  }
2408
2414
 
2415
+ // src/commands/media.ts
2416
+ import fs3 from "fs";
2417
+ import path4 from "path";
2418
+ var MONOLITH_URL4 = process.env.MONOLITH_URL || process.env.REPLICAS_MONOLITH_URL || "https://api.replicas.dev";
2419
+ var EXT_INFO = {
2420
+ png: { kind: MEDIA_KIND.IMAGE, contentType: "image/png" },
2421
+ jpg: { kind: MEDIA_KIND.IMAGE, contentType: "image/jpeg" },
2422
+ jpeg: { kind: MEDIA_KIND.IMAGE, contentType: "image/jpeg" },
2423
+ webp: { kind: MEDIA_KIND.IMAGE, contentType: "image/webp" },
2424
+ svg: { kind: MEDIA_KIND.IMAGE, contentType: "image/svg+xml" },
2425
+ mp4: { kind: MEDIA_KIND.VIDEO, contentType: "video/mp4" },
2426
+ webm: { kind: MEDIA_KIND.VIDEO, contentType: "video/webm" },
2427
+ mp3: { kind: MEDIA_KIND.AUDIO, contentType: "audio/mpeg" },
2428
+ wav: { kind: MEDIA_KIND.AUDIO, contentType: "audio/wav" }
2429
+ };
2430
+ function resolveKind(ext, override) {
2431
+ if (override) {
2432
+ if (!MEDIA_KINDS.includes(override)) {
2433
+ throw new Error(`Invalid kind '${override}'. Must be one of: ${MEDIA_KINDS.join(", ")}`);
2434
+ }
2435
+ return override;
2436
+ }
2437
+ const inferred = EXT_INFO[ext]?.kind;
2438
+ if (!inferred) {
2439
+ throw new Error(`Cannot infer kind from extension '.${ext}'. Pass --kind explicitly.`);
2440
+ }
2441
+ return inferred;
2442
+ }
2443
+ async function mediaUploadCommand(filePath, options) {
2444
+ const absPath = path4.resolve(filePath);
2445
+ if (!fs3.existsSync(absPath)) {
2446
+ throw new Error(`File not found: ${absPath}`);
2447
+ }
2448
+ const stat = fs3.statSync(absPath);
2449
+ if (!stat.isFile()) {
2450
+ throw new Error(`Not a file: ${absPath}`);
2451
+ }
2452
+ if (stat.size === 0) {
2453
+ throw new Error("File is empty");
2454
+ }
2455
+ const fileName = path4.basename(absPath);
2456
+ const ext = path4.extname(absPath).slice(1).toLowerCase();
2457
+ const kind = resolveKind(ext, options.kind);
2458
+ const contentType = EXT_INFO[ext]?.contentType ?? "application/octet-stream";
2459
+ const form = new FormData();
2460
+ form.append("file", new Blob([fs3.readFileSync(absPath)], { type: contentType }), fileName);
2461
+ form.append("kind", kind);
2462
+ if (options.sessionId) form.append("session_id", options.sessionId);
2463
+ const { media } = await agentFetch("/v1/engine/media", {
2464
+ method: "POST",
2465
+ body: form
2466
+ });
2467
+ console.log(`![${fileName}](${MONOLITH_URL4}/v1/media/${media.id})`);
2468
+ const config2 = readAgentConfig();
2469
+ if (config2?.workspace_id) {
2470
+ console.log(`View in Replicas: ${getWorkspaceDashboardUrl(config2.workspace_id, { mode: "media" })}`);
2471
+ }
2472
+ }
2473
+ async function mediaListCommand(options) {
2474
+ const qs = new URLSearchParams();
2475
+ if (options.kind) qs.set("kind", options.kind);
2476
+ if (options.limit) qs.set("limit", options.limit);
2477
+ if (options.page) qs.set("page", options.page);
2478
+ const query = qs.toString() ? `?${qs.toString()}` : "";
2479
+ const { media } = await agentFetch(`/v1/engine/media${query}`);
2480
+ for (const m of media) {
2481
+ console.log(`${MONOLITH_URL4}/v1/media/${m.id} ${m.kind} ${m.size} ${m.created_at}`);
2482
+ }
2483
+ }
2484
+
2409
2485
  // src/commands/interactive.ts
2410
2486
  import chalk18 from "chalk";
2411
2487
  async function interactiveCommand() {
@@ -2421,12 +2497,12 @@ async function interactiveCommand() {
2421
2497
  );
2422
2498
  }
2423
2499
  console.log(chalk18.gray("Starting interactive mode..."));
2424
- const { launchInteractive } = await import("./interactive-P7CS3HLU.mjs");
2500
+ const { launchInteractive } = await import("./interactive-OHZFZVXH.mjs");
2425
2501
  await launchInteractive();
2426
2502
  }
2427
2503
 
2428
2504
  // src/index.ts
2429
- var CLI_VERSION = "0.2.121";
2505
+ var CLI_VERSION = "0.2.122";
2430
2506
  var program = new Command();
2431
2507
  program.name("replicas").description("CLI for managing Replicas workspaces").version(CLI_VERSION);
2432
2508
  program.command("login").description("Authenticate with your Replicas account").action(async () => {
@@ -2839,12 +2915,36 @@ if (isAgentMode()) {
2839
2915
  });
2840
2916
  }
2841
2917
  if (isAgentMode()) {
2842
- const previewCmd = program.commands.find((cmd) => cmd.name() === "preview");
2918
+ const media = program.command("media").description("Share workspace media (screenshots, videos, audio) inline in chat");
2919
+ media.command("upload <file>").description("Upload a screenshot, video, or audio file. Prints a markdown embed for the assistant reply.").option("-k, --kind <kind>", "Media kind: image, video, or audio (inferred from extension if omitted)").option("-s, --session-id <id>", "Session ID to associate the asset with").action(async (file, options) => {
2920
+ try {
2921
+ await mediaUploadCommand(file, options);
2922
+ } catch (error) {
2923
+ if (error instanceof Error) {
2924
+ console.error(chalk19.red(`
2925
+ \u2717 ${error.message}
2926
+ `));
2927
+ }
2928
+ process.exit(1);
2929
+ }
2930
+ });
2931
+ media.command("list").description("List media uploaded for this workspace").option("-k, --kind <kind>", "Filter by kind: image, video, or audio").option("-l, --limit <n>", "Page size (default 50, max 200)").option("-p, --page <n>", "Page number (default 1)").action(async (options) => {
2932
+ try {
2933
+ await mediaListCommand(options);
2934
+ } catch (error) {
2935
+ if (error instanceof Error) {
2936
+ console.error(chalk19.red(`
2937
+ \u2717 ${error.message}
2938
+ `));
2939
+ }
2940
+ process.exit(1);
2941
+ }
2942
+ });
2943
+ const allowed = /* @__PURE__ */ new Set(["preview", "media"]);
2944
+ const kept = program.commands.filter((cmd) => allowed.has(cmd.name()));
2843
2945
  const cmds = program.commands;
2844
2946
  cmds.length = 0;
2845
- if (previewCmd) {
2846
- cmds.push(previewCmd);
2847
- }
2947
+ cmds.push(...kept);
2848
2948
  }
2849
2949
  var versionCheckPromise = checkForUpdates(CLI_VERSION);
2850
2950
  program.parse();
@@ -12,7 +12,7 @@ import {
12
12
  isAgentBackendEvent,
13
13
  parseAgentEvents,
14
14
  parseUserMessage
15
- } from "./chunk-CPVJVKSG.mjs";
15
+ } from "./chunk-BY7XUH2G.mjs";
16
16
  import "./chunk-FFDYI4OH.mjs";
17
17
 
18
18
  // src/interactive/index.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.121",
3
+ "version": "0.2.122",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.mjs",
6
6
  "bin": {