replicas-cli 0.2.182 → 0.2.183

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.
Files changed (2) hide show
  1. package/dist/index.mjs +671 -152
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -7316,7 +7316,7 @@ var require_dist = __commonJS({
7316
7316
  // src/index.ts
7317
7317
  import "dotenv/config";
7318
7318
  import { Command, InvalidArgumentError } from "commander";
7319
- import chalk21 from "chalk";
7319
+ import chalk22 from "chalk";
7320
7320
 
7321
7321
  // src/commands/login.ts
7322
7322
  import http from "http";
@@ -7603,7 +7603,7 @@ function generateState() {
7603
7603
  }
7604
7604
  async function loginCommand() {
7605
7605
  const state = generateState();
7606
- return new Promise((resolve, reject) => {
7606
+ return new Promise((resolve2, reject) => {
7607
7607
  let authTimeout;
7608
7608
  let hasHandledCallback = false;
7609
7609
  let lastRedirectUrl = null;
@@ -7740,7 +7740,7 @@ async function loginCommand() {
7740
7740
  setImmediate(() => {
7741
7741
  server.closeAllConnections?.();
7742
7742
  server.close();
7743
- resolve();
7743
+ resolve2();
7744
7744
  });
7745
7745
  } catch (error2) {
7746
7746
  const errorUrl = `${WEB_APP_URL}/cli-login/error?message=${encodeURIComponent("Failed to verify authentication.")}`;
@@ -7843,13 +7843,13 @@ import chalk5 from "chalk";
7843
7843
  import { spawn } from "child_process";
7844
7844
  var SSH_OPTIONS = ["-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"];
7845
7845
  async function connectSSH(token, host, proxyCommand) {
7846
- return new Promise((resolve, reject) => {
7846
+ return new Promise((resolve2, reject) => {
7847
7847
  const sshArgs = proxyCommand ? [...SSH_OPTIONS, "-o", `ProxyCommand=${proxyCommand}`, `${token}@${host}`] : [...SSH_OPTIONS, `${token}@${host}`];
7848
7848
  const ssh = spawn("ssh", sshArgs, {
7849
7849
  stdio: "inherit"
7850
7850
  });
7851
7851
  ssh.on("close", () => {
7852
- resolve();
7852
+ resolve2();
7853
7853
  });
7854
7854
  ssh.on("error", reject);
7855
7855
  });
@@ -8105,8 +8105,200 @@ function extractPrNumber(url) {
8105
8105
  return parsed ? String(parsed.number) : null;
8106
8106
  }
8107
8107
 
8108
+ // ../shared/src/default-skills/replicas-agent/abilities/computer.ts
8109
+ var SECTION = `### Computer use (Linux desktop control)
8110
+ Drive the workspace's Linux desktop - open a browser, click, type, scroll, screenshot, record - and surface a live noVNC viewer to the user via the \`replicas computer\` CLI. Every Replicas workspace boots with Xvfb / openbox / x11vnc / noVNC pre-installed.
8111
+
8112
+ **Reference:** \`references/COMPUTER-USE.md\`
8113
+
8114
+ Use this when:
8115
+ - A task requires interacting with a website, web app, or desktop application that has no usable API
8116
+ - You want the user to watch the agent work - \`replicas computer start\` exposes a \`Desktop\` tab in the dashboard
8117
+ - You're testing UI changes in a browser before reporting them as done
8118
+ - You want to record a screen capture of a task as proof to share back`;
8119
+ var REFERENCE = `# Computer use (Linux desktop control)
8120
+
8121
+ Every Replicas workspace boots with a full Linux desktop stack - Xvfb (1920\xD71080), openbox, tint2, x11vnc, noVNC, ffmpeg, and Google Chrome. You drive it through the \`replicas computer\` CLI.
8122
+
8123
+ Use this for anything the user can't reasonably do via an API - clicking around web apps, filling forms, testing UI changes, dragging files between desktop apps, recording a walkthrough.
8124
+
8125
+ ## When to use it
8126
+
8127
+ - **Prefer real APIs first.** If a task has a CLI or HTTP API (GitHub, Linear, Slack, Replicas itself), use that. Driving a UI is slower, flakier, and less auditable.
8128
+ - **Use it when there's no API**: testing a frontend you just changed, navigating a vendor portal, demonstrating a flow on video.
8129
+ - **Use it when the user wants to watch.** \`replicas computer start\` exposes a live \`Desktop\` tab in the dashboard - they can watch you work in real time.
8130
+
8131
+ ## Never use raw \`xdotool\` / \`scrot\` / \`ffmpeg\` directly
8132
+
8133
+ The CLI is the canonical surface. Chat transcripts that show \`DISPLAY=:99 xdotool key Return\` are noisy and break when the underlying stack changes. Always call the CLI:
8134
+
8135
+ \`\`\`bash
8136
+ # DO
8137
+ replicas computer key Return
8138
+ replicas computer screenshot /tmp/state.png
8139
+
8140
+ # DON'T
8141
+ DISPLAY=:99 xdotool key Return
8142
+ scrot /tmp/state.png
8143
+ \`\`\`
8144
+
8145
+ ## Quickstart
8146
+
8147
+ \`\`\`bash
8148
+ # 1) Make the desktop visible to the user (creates an authenticated noVNC
8149
+ # preview on port 6080, prints the viewer URL, adds a "Desktop" tab to the
8150
+ # dashboard).
8151
+ replicas computer start
8152
+
8153
+ # 2) Launch a browser on the workspace display.
8154
+ replicas computer launch chrome
8155
+
8156
+ # 3) Take a screenshot so you can see what's there.
8157
+ replicas computer screenshot /tmp/state.png
8158
+ # (Read the PNG yourself before deciding where to click.)
8159
+
8160
+ # 4) Drive the UI.
8161
+ replicas computer key ctrl+l # focus address bar
8162
+ replicas computer type "https://news.ycombinator.com"
8163
+ replicas computer key Return
8164
+ replicas computer click 521 700 # click coordinates from the screenshot
8165
+ replicas computer scroll down --amount 5
8166
+
8167
+ # 5) (Optional) Record a screencap to share back.
8168
+ replicas computer record start /tmp/demo.mp4 --fps 60
8169
+ # ... do stuff ...
8170
+ replicas computer record stop
8171
+ replicas media upload /tmp/demo.mp4
8172
+
8173
+ # 6) Tear down the live preview when done (services keep running for next time).
8174
+ replicas computer stop
8175
+ \`\`\`
8176
+
8177
+ ## Command reference
8178
+
8179
+ ### \`replicas computer start [--port N] [--display :N] [--size WxH]\`
8180
+ Ensures all desktop services are running and creates an authenticated noVNC preview. Prints the viewer URL (\`https://<port>-<hash>.tryreplicas.com/\`). The Replicas dashboard automatically shows a \`Desktop\` tab while this preview is live - point the user at it instead of pasting the URL.
8181
+
8182
+ Idempotent - safe to call repeatedly. Use it as the first computer-use command in any session.
8183
+
8184
+ ### \`replicas computer stop [--port N]\`
8185
+ Tears down the noVNC preview (the \`Desktop\` tab disappears). The underlying Xvfb / openbox / x11vnc / browser keep running so the next \`start\` is instant.
8186
+
8187
+ ### \`replicas computer status\`
8188
+ Prints which desktop services are running and the active preview URL (if any). Useful for debugging when a tool call seems to be doing nothing.
8189
+
8190
+ ### \`replicas computer screenshot <path>\`
8191
+ Captures the current desktop to a PNG at the given path. Read the file (e.g. with your Read tool) to see what's on screen - coordinates from the screenshot drive subsequent \`click\` / \`move\` / \`drag\` calls.
8192
+
8193
+ ### \`replicas computer click <x> <y> [--button N] [--double] [--modifiers ctrl+shift]\`
8194
+ Move to (x, y) and click. Default is left-click (button 1); pass \`--button 3\` for right-click. \`--modifiers\` holds keys during the click (e.g. ctrl-click a link to open in a new tab).
8195
+
8196
+ ### \`replicas computer move <x> <y>\`
8197
+ Move the mouse without clicking. Useful for hovering tooltips.
8198
+
8199
+ ### \`replicas computer type <text> [--delay MS]\`
8200
+ Type a literal string into the focused field. Default per-character delay is 12ms (~80 wpm) - feels human and avoids breaking apps that debounce input. Bump \`--delay 30\` for stricter apps.
8201
+
8202
+ For key combos (not literal text), use \`key\`. \`type "ctrl+l"\` will literally type the seven characters \`c t r l + l\`.
8203
+
8204
+ ### \`replicas computer key <combo>\`
8205
+ Press a single key or combo. Examples: \`Return\`, \`Escape\`, \`Tab\`, \`ctrl+l\`, \`ctrl+shift+t\`, \`alt+Left\`, \`Page_Down\`, \`Home\`. Syntax matches \`xdotool key\`.
8206
+
8207
+ ### \`replicas computer scroll <up|down|left|right> [--amount N] [--x X --y Y]\`
8208
+ Scroll the wheel. Pass \`--x\` / \`--y\` to hover before scrolling (otherwise scrolls wherever the cursor currently is). Default amount is 3 wheel ticks.
8209
+
8210
+ ### \`replicas computer drag <fromX> <fromY> <toX> <toY>\`
8211
+ Press left mouse at (fromX, fromY), drag to (toX, toY), release. For things like dragging a file onto an upload zone.
8212
+
8213
+ ### \`replicas computer launch <app> [args...]\`
8214
+ Spawns an app on the workspace display. Built-in aliases:
8215
+ - \`chrome\` - Google Chrome with a clean profile and sane flags
8216
+ - \`chromium\` - Chromium variant
8217
+ - \`firefox\` - Firefox
8218
+ - \`terminal\` - xfce4-terminal (modern, themed); \`xterm\` for the classic
8219
+ - \`notepad\` / \`editor\` - mousepad (lightweight GTK text editor)
8220
+ - \`files\` / \`filemanager\` - thunar (file manager)
8221
+ Anything else gets \`exec\`'d verbatim, so \`replicas computer launch xeyes\` works if xeyes is installed.
8222
+
8223
+ ### \`replicas computer record start <path> [--fps N]\`
8224
+ Starts an ffmpeg screen recorder. Output is a fragmented MP4 (still playable if the workspace dies mid-record). Default 60fps; drop to 30 if the workspace is CPU-constrained.
8225
+
8226
+ Only one recording at a time. Re-running \`start\` while one is active fails - call \`stop\` first.
8227
+
8228
+ ### \`replicas computer record stop\`
8229
+ SIGINTs ffmpeg, waits for it to finalize the MP4, prints the output path. Upload it with \`replicas media upload <path>\` to share it.
8230
+
8231
+ ## Patterns
8232
+
8233
+ ### Action / screenshot loop
8234
+ You are blind between tool calls. After any action that changes the screen, take a screenshot before deciding the next coordinate:
8235
+
8236
+ \`\`\`bash
8237
+ replicas computer click 521 700
8238
+ sleep 2 # let the page settle
8239
+ replicas computer screenshot /tmp/after-click.png
8240
+ # read /tmp/after-click.png, decide next click
8241
+ \`\`\`
8242
+
8243
+ \`sleep\` is a regular shell sleep - there's no \`replicas computer wait\` command, but you can mix shell sleeps freely.
8244
+
8245
+ ### Typing into an address bar
8246
+ \`\`\`bash
8247
+ replicas computer key ctrl+l # focus address bar
8248
+ replicas computer type "https://example.com"
8249
+ replicas computer key Return
8250
+ sleep 3 # wait for page load
8251
+ replicas computer screenshot /tmp/loaded.png
8252
+ \`\`\`
8253
+
8254
+ ### Coordinates from screenshots
8255
+ The display is 1920\xD71080. Screenshot pixels map 1:1 to click coordinates - if your Read tool shows a button at pixel (520, 700), click \`replicas computer click 520 700\`. **No translation needed.** Modern image-reading models often imagine the screenshot is at a different resolution; trust the \`xdpyinfo\` value (\`replicas computer status\` shows the real size).
8256
+
8257
+ ### Letting the user watch
8258
+ Always start the desktop session with \`replicas computer start\` *before* doing anything visual, even if you don't need the URL yourself. The Desktop tab appears in their dashboard. They get to watch and intervene if needed.
8259
+
8260
+ ### Recording a deliverable
8261
+ For tasks the user wants proof of:
8262
+ \`\`\`bash
8263
+ replicas computer start # makes it visible live too
8264
+ replicas computer record start /tmp/walkthrough.mp4
8265
+ # ... your work ...
8266
+ replicas computer record stop
8267
+ replicas media upload /tmp/walkthrough.mp4
8268
+ \`\`\`
8269
+ Then embed the printed \`![\u2026](\u2026)\` line in your chat reply. See \`MEDIA.md\`.
8270
+
8271
+ ### Cleaning up
8272
+ Call \`replicas computer stop\` when you're done with the visual demo so the live preview URL goes away. The services keep running so the next \`start\` is instant.
8273
+
8274
+ ## Failure modes
8275
+
8276
+ - **"Desktop services script missing"**: workspace image is older than this skill. Tell the user - nothing you can do from the CLI side.
8277
+ - **\`xdotool ... failed: Can't open display\`**: Xvfb didn't come up. \`replicas computer status\` will show which service is dead. Re-running any CLI command auto-attempts to start it.
8278
+ - **Browser doesn't appear after \`launch chrome\`**: give it 1-2s, then screenshot. Chrome cold-start on the virtual display takes ~500ms but bigger pages take longer.
8279
+ - **Live preview shows static / black screen**: the browser may have crashed. \`replicas computer status\` should show no Chrome process - re-launch.
8280
+
8281
+ ## What gets baked in vs. lazy
8282
+
8283
+ | Component | Where |
8284
+ |---|---|
8285
+ | \`xvfb\`, \`openbox\`, \`tint2\`, \`x11vnc\`, \`websockify\`, \`xdotool\`, \`scrot\`, \`ffmpeg\`, \`google-chrome\` | Baked into the workspace image |
8286
+ | Xvfb / openbox / tint2 / x11vnc / websockify processes | Started at workspace boot (\`replicas-start-desktop-services\`) |
8287
+ | noVNC preview URL (port 6080) | **Lazy** - created by \`replicas computer start\` |
8288
+ | Dashboard \`Desktop\` tab | Appears when the preview URL is live, disappears on \`stop\` |
8289
+
8290
+ You can call any of the input tools (\`click\`, \`type\`, \`screenshot\`, etc.) without first calling \`start\` - they'll work since the daemons are running. \`start\` is only needed when you want the user to see the live stream.
8291
+ `;
8292
+ var COMPUTER_ABILITY = {
8293
+ label: "Computer use",
8294
+ description: "Drive the workspace's Linux desktop and stream a live noVNC viewer.",
8295
+ bullet: "- Driving the workspace Linux desktop (open a browser, click, type, scroll, screenshot, record) and exposing a live noVNC viewer in the dashboard",
8296
+ section: SECTION,
8297
+ referenceFile: { name: "COMPUTER-USE.md", content: REFERENCE }
8298
+ };
8299
+
8108
8300
  // ../shared/src/default-skills/replicas-agent/abilities/docker.ts
8109
- var SECTION = `### Docker
8301
+ var SECTION2 = `### Docker
8110
8302
  Start and use the Docker daemon in Replicas workspaces. Docker is pre-installed but the daemon does not auto-start.
8111
8303
 
8112
8304
  **Reference:** \`references/DOCKER.md\`
@@ -8115,7 +8307,7 @@ Use this when:
8115
8307
  - You need to run \`docker\` or \`docker compose\` commands
8116
8308
  - You need to build or run Docker containers
8117
8309
  - Your task involves containerized services or Docker-based workflows`;
8118
- var REFERENCE = `# Docker
8310
+ var REFERENCE2 = `# Docker
8119
8311
 
8120
8312
  Docker is pre-installed in Replicas workspaces, but the daemon does **not** auto-start. You must start it manually before running any \`docker\` or \`docker compose\` commands.
8121
8313
 
@@ -8144,12 +8336,12 @@ var DOCKER_ABILITY = {
8144
8336
  label: "Docker",
8145
8337
  description: "Start the daemon, build/run containers, drive `docker compose`.",
8146
8338
  bullet: "- Using Docker (starting the daemon, running containers, docker compose)",
8147
- section: SECTION,
8148
- referenceFile: { name: "DOCKER.md", content: REFERENCE }
8339
+ section: SECTION2,
8340
+ referenceFile: { name: "DOCKER.md", content: REFERENCE2 }
8149
8341
  };
8150
8342
 
8151
8343
  // ../shared/src/default-skills/replicas-agent/abilities/github.ts
8152
- var SECTION2 = `### GitHub
8344
+ var SECTION3 = `### GitHub
8153
8345
  Use the pre-authenticated \`gh\` CLI for pull requests, issues, actions, and API calls.
8154
8346
 
8155
8347
  **Reference:** \`references/GITHUB.md\`
@@ -8159,7 +8351,7 @@ Use this when:
8159
8351
  - You need to interact with GitHub issues or actions
8160
8352
  - You need to use the GitHub API for advanced operations
8161
8353
  - You need to include images in PR descriptions`;
8162
- var REFERENCE2 = `# GitHub Integration
8354
+ var REFERENCE3 = `# GitHub Integration
8163
8355
 
8164
8356
  This guide covers how to interact with GitHub from within your Replicas workspace.
8165
8357
 
@@ -8285,12 +8477,12 @@ var GITHUB_ABILITY = {
8285
8477
  label: "GitHub",
8286
8478
  description: "Pre-authenticated `gh` CLI for PRs, issues, releases, and GraphQL.",
8287
8479
  bullet: "- Interacting with GitHub (creating PRs, managing issues, using the API, etc.)",
8288
- section: SECTION2,
8289
- referenceFile: { name: "GITHUB.md", content: REFERENCE2 }
8480
+ section: SECTION3,
8481
+ referenceFile: { name: "GITHUB.md", content: REFERENCE3 }
8290
8482
  };
8291
8483
 
8292
8484
  // ../shared/src/default-skills/replicas-agent/abilities/google.ts
8293
- var SECTION3 = `### Google Workspace (Docs, Sheets, Forms, Drive)
8485
+ var SECTION4 = `### Google Workspace (Docs, Sheets, Forms, Drive)
8294
8486
  Create and edit Google Docs, Sheets, and Forms via the Replicas gateway. Files are owned by Replicas \u2014 the integration cannot access pre-existing Google content created outside of it.
8295
8487
 
8296
8488
  **Reference:** \`references/GOOGLE.md\`
@@ -8299,7 +8491,7 @@ Use this when:
8299
8491
  - You need to create or edit a Google Doc, Sheet, or Form
8300
8492
  - You need to share, rename, move, or delete a Replicas-created Google file
8301
8493
  - You need to read responses from a Replicas-created Google Form`;
8302
- var REFERENCE3 = `# Google Workspace (Docs, Sheets, Forms, Drive)
8494
+ var REFERENCE4 = `# Google Workspace (Docs, Sheets, Forms, Drive)
8303
8495
 
8304
8496
  This guide covers how to create and edit Google Docs, Sheets, and Forms \u2014 plus do basic Drive file operations \u2014 from inside a Replicas workspace, using the monolith as a gateway to Google's APIs.
8305
8497
 
@@ -8560,12 +8752,12 @@ var GOOGLE_ABILITY = {
8560
8752
  label: "Google Workspace",
8561
8753
  description: "Create / edit Docs, Sheets, Forms, and Drive files via the Replicas gateway.",
8562
8754
  bullet: "- Interacting with Google Workspace (creating and editing Docs, Sheets, and Forms, sharing files, reading form responses, etc.)",
8563
- section: SECTION3,
8564
- referenceFile: { name: "GOOGLE.md", content: REFERENCE3 }
8755
+ section: SECTION4,
8756
+ referenceFile: { name: "GOOGLE.md", content: REFERENCE4 }
8565
8757
  };
8566
8758
 
8567
8759
  // ../shared/src/default-skills/replicas-agent/abilities/linear.ts
8568
- var SECTION4 = `### Linear
8760
+ var SECTION5 = `### Linear
8569
8761
  Fetch issues, update state, add comments, and search via the Linear GraphQL API.
8570
8762
 
8571
8763
  **Reference:** \`references/LINEAR.md\`
@@ -8574,7 +8766,7 @@ Use this when:
8574
8766
  - You encounter a Linear issue link and need to understand the task
8575
8767
  - You need to update an issue's state (e.g. mark as done)
8576
8768
  - You need to comment on or search for Linear issues`;
8577
- var REFERENCE4 = `# Linear Integration
8769
+ var REFERENCE5 = `# Linear Integration
8578
8770
 
8579
8771
  This guide covers how to interact with Linear from within your Replicas workspace.
8580
8772
 
@@ -8649,12 +8841,12 @@ var LINEAR_ABILITY = {
8649
8841
  label: "Linear",
8650
8842
  description: "Fetch issues, post comments, update states via the Linear GraphQL API.",
8651
8843
  bullet: "- Interacting with Linear (fetching issues, updating state, commenting, etc.)",
8652
- section: SECTION4,
8653
- referenceFile: { name: "LINEAR.md", content: REFERENCE4 }
8844
+ section: SECTION5,
8845
+ referenceFile: { name: "LINEAR.md", content: REFERENCE5 }
8654
8846
  };
8655
8847
 
8656
8848
  // ../shared/src/default-skills/replicas-agent/abilities/media.ts
8657
- var SECTION5 = `### Media
8849
+ var SECTION6 = `### Media
8658
8850
  Share screenshots, screen recordings, generated diagrams, and audio clips inline in the Replicas chat (and as references in external messages).
8659
8851
 
8660
8852
  **Reference:** \`references/MEDIA.md\`
@@ -8663,7 +8855,7 @@ Use this when:
8663
8855
  - You produce a screenshot, recording, generated image, or audio clip the user should see
8664
8856
  - You record video output (browser automation, screen capture) \u2014 including the recommended aspect ratio and FPS
8665
8857
  - You need to embed media in a Slack/Linear/GitHub message AND keep a referenceable copy in the Replicas dashboard`;
8666
- var REFERENCE5 = `# Media (Screenshots, Recordings, Audio)
8858
+ var REFERENCE6 = `# Media (Screenshots, Recordings, Audio)
8667
8859
 
8668
8860
  This guide covers how to share screenshots, screen recordings, generated diagrams, and audio clips with the user inline in the Replicas chat.
8669
8861
 
@@ -8777,12 +8969,12 @@ var MEDIA_ABILITY = {
8777
8969
  label: "Media",
8778
8970
  description: "Share screenshots, recordings, generated images, and audio clips.",
8779
8971
  bullet: "- Sharing media (screenshots, screen recordings, generated diagrams, audio clips) \u2014 including in your Replicas chat reply and to external platforms",
8780
- section: SECTION5,
8781
- referenceFile: { name: "MEDIA.md", content: REFERENCE5 }
8972
+ section: SECTION6,
8973
+ referenceFile: { name: "MEDIA.md", content: REFERENCE6 }
8782
8974
  };
8783
8975
 
8784
8976
  // ../shared/src/default-skills/replicas-agent/abilities/previews.ts
8785
- var SECTION6 = `### Previews
8977
+ var SECTION7 = `### Previews
8786
8978
  Expose locally running services (web apps, APIs, databases) as public preview URLs so humans can interact with them directly.
8787
8979
 
8788
8980
  **Reference:** \`references/PREVIEWS.md\`
@@ -8791,7 +8983,7 @@ Use this when:
8791
8983
  - You need to start a service that a human should view or interact with
8792
8984
  - The task involves UI work that benefits from human review
8793
8985
  - You are verifying frontend/backend integrations visually`;
8794
- var REFERENCE6 = `# Preview URLs
8986
+ var REFERENCE7 = `# Preview URLs
8795
8987
 
8796
8988
  When you run services on ports \u2014 such as a web app, API server, or database \u2014 humans may want to interact with them directly. You can expose your locally running services as public preview URLs.
8797
8989
 
@@ -8873,12 +9065,12 @@ var PREVIEWS_ABILITY = {
8873
9065
  label: "Previews",
8874
9066
  description: "Expose locally running services on public preview URLs for humans.",
8875
9067
  bullet: "- Creating preview URLs for locally running services",
8876
- section: SECTION6,
8877
- referenceFile: { name: "PREVIEWS.md", content: REFERENCE6 }
9068
+ section: SECTION7,
9069
+ referenceFile: { name: "PREVIEWS.md", content: REFERENCE7 }
8878
9070
  };
8879
9071
 
8880
9072
  // ../shared/src/default-skills/replicas-agent/abilities/replicas.ts
8881
- var SECTION7 = `### Replicas (in-workspace CLI)
9073
+ var SECTION8 = `### Replicas (in-workspace CLI)
8882
9074
  Take action *with* Replicas itself \u2014 manage automations, environments (variables, files), repos, and \`replicas.json\` config \u2014 using the pre-installed, pre-authenticated \`replicas\` CLI.
8883
9075
 
8884
9076
  **Reference:** \`references/REPLICAS.md\`
@@ -8888,7 +9080,7 @@ Use this when:
8888
9080
  - The user asks you to manage environments, environment variables, or environment files
8889
9081
  - The user asks "what envs / repos / automations do I have?"
8890
9082
  - The user asks you to scaffold a \`replicas.json\` / \`replicas.yaml\` in a repo`;
8891
- var REFERENCE7 = `# Replicas (in-workspace CLI)
9083
+ var REFERENCE8 = `# Replicas (in-workspace CLI)
8892
9084
 
8893
9085
  This guide covers how to take action *with* Replicas itself from inside a Replicas workspace \u2014 managing automations, environments (and their variables/files), repos, previews, and the user's \`replicas.json\` config \u2014 using the pre-installed \`replicas\` CLI.
8894
9086
 
@@ -9082,13 +9274,13 @@ var REPLICAS_ABILITY = {
9082
9274
  description: "Teach the agent about Replicas itself \u2014 automations, environments, the in-workspace CLI.",
9083
9275
  // No bullet — help_instructions covers the `replicas` CLI surface in detail.
9084
9276
  bullet: "",
9085
- section: SECTION7,
9086
- referenceFile: { name: "REPLICAS.md", content: REFERENCE7 },
9277
+ section: SECTION8,
9278
+ referenceFile: { name: "REPLICAS.md", content: REFERENCE8 },
9087
9279
  locked: true
9088
9280
  };
9089
9281
 
9090
9282
  // ../shared/src/default-skills/replicas-agent/abilities/slack.ts
9091
- var SECTION8 = `### Slack
9283
+ var SECTION9 = `### Slack
9092
9284
  Send messages, read threads, search conversations, and upload files via the Slack Web API.
9093
9285
 
9094
9286
  **Reference:** \`references/SLACK.md\`
@@ -9098,7 +9290,7 @@ Use this when:
9098
9290
  - You need to read or fetch a Slack conversation
9099
9291
  - You encounter a Slack message link and need to retrieve its content
9100
9292
  - The task asks you to notify, update, or communicate via Slack`;
9101
- var REFERENCE8 = `# Slack Integration
9293
+ var REFERENCE9 = `# Slack Integration
9102
9294
 
9103
9295
  This guide covers how to interact with Slack from within your Replicas workspace.
9104
9296
 
@@ -9171,13 +9363,14 @@ var SLACK_ABILITY = {
9171
9363
  label: "Slack",
9172
9364
  description: "Send messages, read threads, search conversations, upload files.",
9173
9365
  bullet: "- Interacting with Slack (sending messages, reading threads, etc.)",
9174
- section: SECTION8,
9175
- referenceFile: { name: "SLACK.md", content: REFERENCE8 }
9366
+ section: SECTION9,
9367
+ referenceFile: { name: "SLACK.md", content: REFERENCE9 }
9176
9368
  };
9177
9369
 
9178
9370
  // ../shared/src/default-skills/replicas-agent/registry.ts
9179
9371
  var REPLICAS_AGENT_ABILITY_REGISTRY = {
9180
9372
  replicas: REPLICAS_ABILITY,
9373
+ computer: COMPUTER_ABILITY,
9181
9374
  docker: DOCKER_ABILITY,
9182
9375
  github: GITHUB_ABILITY,
9183
9376
  google: GOOGLE_ABILITY,
@@ -9222,6 +9415,14 @@ var HOOK_EXEC_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
9222
9415
  // ../shared/src/replicas-config.ts
9223
9416
  var REPLICAS_CONFIG_FILENAMES = ["replicas.json", "replicas.yaml", "replicas.yml"];
9224
9417
 
9418
+ // ../shared/src/engine/environment.ts
9419
+ var DESKTOP_NOVNC_PORT = 6080;
9420
+ function getDesktopViewerUrl(publicUrl) {
9421
+ return `${publicUrl.replace(/\/$/, "")}/`;
9422
+ }
9423
+ var DESKTOP_VIEWER_WIDTH = 1920;
9424
+ var DESKTOP_VIEWER_HEIGHT = 1080;
9425
+
9225
9426
  // ../shared/src/engine/types.ts
9226
9427
  var DEFAULT_CHAT_TITLES = {
9227
9428
  claude: "Claude Code",
@@ -10746,7 +10947,7 @@ async function exchangeCodeForTokens(code, codeVerifier) {
10746
10947
  };
10747
10948
  }
10748
10949
  function startCallbackServer(expectedState, codeVerifier) {
10749
- return new Promise((resolve, reject) => {
10950
+ return new Promise((resolve2, reject) => {
10750
10951
  const server = http2.createServer(async (req, res) => {
10751
10952
  try {
10752
10953
  if (!req.url) {
@@ -10789,7 +10990,7 @@ function startCallbackServer(expectedState, codeVerifier) {
10789
10990
  res.writeHead(302, { "Location": `${WEB_APP_URL2}/codex/oauth/success` });
10790
10991
  res.end();
10791
10992
  server.close();
10792
- resolve(tokens);
10993
+ resolve2(tokens);
10793
10994
  } catch (tokenError) {
10794
10995
  const errorMessage = encodeURIComponent(tokenError instanceof Error ? tokenError.message : "Unknown error");
10795
10996
  res.writeHead(302, { "Location": `${WEB_APP_URL2}/codex/oauth/error?message=${errorMessage}` });
@@ -10823,7 +11024,7 @@ async function runCodexOAuthFlow() {
10823
11024
  const state = generateState2();
10824
11025
  const authUrl = buildAuthorizationUrl(pkce.codeChallenge, state);
10825
11026
  const tokensPromise = startCallbackServer(state, pkce.codeVerifier);
10826
- await new Promise((resolve) => setTimeout(resolve, 500));
11027
+ await new Promise((resolve2) => setTimeout(resolve2, 500));
10827
11028
  console.log("If the browser does not open automatically, visit:");
10828
11029
  console.log(authUrl);
10829
11030
  console.log();
@@ -10912,10 +11113,10 @@ async function promptForAuthorizationCode(instruction) {
10912
11113
  input: process.stdin,
10913
11114
  output: process.stdout
10914
11115
  });
10915
- return new Promise((resolve) => {
11116
+ return new Promise((resolve2) => {
10916
11117
  rl.question(instruction, (answer) => {
10917
11118
  rl.close();
10918
- resolve(answer.trim());
11119
+ resolve2(answer.trim());
10919
11120
  });
10920
11121
  });
10921
11122
  }
@@ -12274,20 +12475,31 @@ async function engineFetch(path6, options) {
12274
12475
  }
12275
12476
 
12276
12477
  // src/commands/preview.ts
12277
- function parsePort(port) {
12478
+ function parsePreviewPort(port) {
12278
12479
  const portNum = parseInt(port, 10);
12279
12480
  if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
12280
12481
  throw new Error("Port must be a number between 1 and 65535");
12281
12482
  }
12282
12483
  return portNum;
12283
12484
  }
12485
+ function createAgentPreview(port, authenticated) {
12486
+ return agentFetch("/v1/previews", {
12487
+ method: "POST",
12488
+ body: { port, authenticated }
12489
+ });
12490
+ }
12491
+ function listAgentPreviews() {
12492
+ return engineFetch("/previews");
12493
+ }
12494
+ function deleteAgentPreview(port) {
12495
+ return agentFetch(`/v1/previews/${port}`, {
12496
+ method: "DELETE"
12497
+ });
12498
+ }
12284
12499
  async function previewCreateCommand(port, options) {
12285
- const portNum = parsePort(port);
12500
+ const portNum = parsePreviewPort(port);
12286
12501
  if (isAgentMode()) {
12287
- const result = await agentFetch("/v1/previews", {
12288
- method: "POST",
12289
- body: { port: portNum, authenticated: options.authenticated ?? false }
12290
- });
12502
+ const result = await createAgentPreview(portNum, options.authenticated ?? false);
12291
12503
  console.log(result.preview.publicUrl);
12292
12504
  } else {
12293
12505
  throw new Error("In human mode, use: replicas preview add <workspaceId> --port <port>");
@@ -12295,7 +12507,7 @@ async function previewCreateCommand(port, options) {
12295
12507
  }
12296
12508
  async function previewListCommand(workspaceId) {
12297
12509
  if (isAgentMode()) {
12298
- const result = await engineFetch("/previews");
12510
+ const result = await listAgentPreviews();
12299
12511
  if (result.previews.length === 0) {
12300
12512
  console.log("No active previews");
12301
12513
  return;
@@ -12320,7 +12532,7 @@ async function previewListCommand(workspaceId) {
12320
12532
  }
12321
12533
  }
12322
12534
  async function previewAddCommand(workspaceId, options) {
12323
- const portNum = parsePort(options.port);
12535
+ const portNum = parsePreviewPort(options.port);
12324
12536
  const result = await orgAuthenticatedFetch(
12325
12537
  `/v1/workspaces/${workspaceId}/previews`,
12326
12538
  {
@@ -12331,14 +12543,12 @@ async function previewAddCommand(workspaceId, options) {
12331
12543
  console.log(chalk18.green(`Preview created: ${result.preview.publicUrl}`));
12332
12544
  }
12333
12545
  async function previewDeleteCommand(port) {
12334
- const portNum = parsePort(port);
12335
- await agentFetch(`/v1/previews/${portNum}`, {
12336
- method: "DELETE"
12337
- });
12546
+ const portNum = parsePreviewPort(port);
12547
+ await deleteAgentPreview(portNum);
12338
12548
  console.log(`Preview deleted on port ${portNum}`);
12339
12549
  }
12340
12550
  async function previewRemoveCommand(workspaceId, options) {
12341
- const portNum = parsePort(options.port);
12551
+ const portNum = parsePreviewPort(options.port);
12342
12552
  await orgAuthenticatedFetch(
12343
12553
  `/v1/workspaces/${workspaceId}/previews/${portNum}`,
12344
12554
  { method: "DELETE" }
@@ -12444,8 +12654,289 @@ async function mediaListCommand(options) {
12444
12654
  }
12445
12655
  }
12446
12656
 
12447
- // src/commands/interactive.ts
12657
+ // src/commands/computer.ts
12658
+ import { spawn as spawn3, spawnSync } from "child_process";
12659
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
12660
+ import { dirname, isAbsolute, resolve } from "path";
12448
12661
  import chalk19 from "chalk";
12662
+ var STATE_DIR = process.env.REPLICAS_DESKTOP_STATE_DIR || "/tmp/replicas-computer";
12663
+ var DEFAULT_DISPLAY = process.env.REPLICAS_DESKTOP_DISPLAY || ":99";
12664
+ var NOVNC_PORT = process.env.REPLICAS_DESKTOP_NOVNC_PORT ? parseInt(process.env.REPLICAS_DESKTOP_NOVNC_PORT, 10) : DESKTOP_NOVNC_PORT;
12665
+ var SERVICES_SCRIPT = "/usr/local/bin/replicas-start-desktop-services";
12666
+ function fail(msg) {
12667
+ throw new Error(msg);
12668
+ }
12669
+ function ensureServicesRunning() {
12670
+ if (!existsSync(SERVICES_SCRIPT)) {
12671
+ fail(
12672
+ `Desktop services script missing at ${SERVICES_SCRIPT}. The workspace image is out of date \u2014 Xvfb / openbox / x11vnc / websockify must be installed and \`replicas-start-desktop-services\` baked in.`
12673
+ );
12674
+ }
12675
+ const r = spawnSync("bash", [SERVICES_SCRIPT], { stdio: "pipe" });
12676
+ if (r.status !== 0) {
12677
+ fail(`Failed to start desktop services: ${r.stderr?.toString() || "unknown error"}`);
12678
+ }
12679
+ }
12680
+ function withDisplay(env = process.env) {
12681
+ return { ...env, DISPLAY: DEFAULT_DISPLAY };
12682
+ }
12683
+ function runDisplayCmd(bin, args) {
12684
+ ensureServicesRunning();
12685
+ const r = spawnSync(bin, args, { env: withDisplay(), stdio: "pipe" });
12686
+ if (r.status !== 0) {
12687
+ fail(`${bin} ${args.join(" ")} failed: ${r.stderr?.toString().trim() || `exit ${r.status}`}`);
12688
+ }
12689
+ return r.stdout?.toString() ?? "";
12690
+ }
12691
+ function parseCoord(value, label) {
12692
+ const n = Number.parseInt(value, 10);
12693
+ if (!Number.isFinite(n)) fail(`${label} must be an integer (got "${value}")`);
12694
+ return n;
12695
+ }
12696
+ function resolvePath(p) {
12697
+ return isAbsolute(p) ? p : resolve(process.cwd(), p);
12698
+ }
12699
+ async function computerStartCommand(options) {
12700
+ if (options.display) process.env.REPLICAS_DESKTOP_DISPLAY = options.display;
12701
+ if (options.size) {
12702
+ const m = /^(\d+)x(\d+)$/.exec(options.size);
12703
+ if (!m) fail(`--size must be WIDTHxHEIGHT (got "${options.size}")`);
12704
+ process.env.REPLICAS_DESKTOP_WIDTH = m[1];
12705
+ process.env.REPLICAS_DESKTOP_HEIGHT = m[2];
12706
+ }
12707
+ ensureServicesRunning();
12708
+ const port = options.port ? parsePreviewPort(options.port) : NOVNC_PORT;
12709
+ let publicUrl;
12710
+ try {
12711
+ const result = await createAgentPreview(port, true);
12712
+ publicUrl = result.preview.publicUrl;
12713
+ } catch (err) {
12714
+ const msg = err instanceof Error ? err.message : String(err);
12715
+ if (!/409/.test(msg)) throw err;
12716
+ const list = await listAgentPreviews();
12717
+ const existing = list.previews.find((p) => p.port === port);
12718
+ if (!existing) throw err;
12719
+ publicUrl = existing.publicUrl;
12720
+ }
12721
+ const viewerUrl = getDesktopViewerUrl(publicUrl);
12722
+ mkdirSync(STATE_DIR, { recursive: true });
12723
+ writeFileSync(
12724
+ `${STATE_DIR}/preview.json`,
12725
+ JSON.stringify({ port, publicUrl, viewerUrl }, null, 2)
12726
+ );
12727
+ console.log(viewerUrl);
12728
+ console.error(chalk19.dim(`Desktop preview ready (port ${port}, authenticated).`));
12729
+ console.error(chalk19.dim(`The Replicas dashboard will show a "Desktop" tab while this preview is live.`));
12730
+ }
12731
+ async function computerStopCommand(options) {
12732
+ const port = options.port ? parsePreviewPort(options.port) : NOVNC_PORT;
12733
+ await deleteAgentPreview(port);
12734
+ rmSync(`${STATE_DIR}/preview.json`, { force: true });
12735
+ console.log(`Desktop preview stopped on port ${port}`);
12736
+ }
12737
+ async function computerStatusCommand() {
12738
+ const procs = ["Xvfb", "openbox", "tint2", "x11vnc", "websockify"];
12739
+ for (const p of procs) {
12740
+ const r = spawnSync("pgrep", ["-af", p], { stdio: "pipe" });
12741
+ const running = r.status === 0 && !!r.stdout?.toString().trim();
12742
+ console.log(` ${running ? chalk19.green("\u25CF") : chalk19.red("\u25CB")} ${p}`);
12743
+ }
12744
+ const previewFile = `${STATE_DIR}/preview.json`;
12745
+ if (existsSync(previewFile)) {
12746
+ try {
12747
+ const meta = JSON.parse(readFileSync(previewFile, "utf8"));
12748
+ console.log(` ${chalk19.cyan("preview")}: ${meta.viewerUrl}`);
12749
+ } catch {
12750
+ }
12751
+ } else {
12752
+ console.log(` ${chalk19.dim("preview: not started (run `replicas computer start`)")}`);
12753
+ }
12754
+ }
12755
+ async function computerScreenshotCommand(path6) {
12756
+ const target = resolvePath(path6);
12757
+ mkdirSync(dirname(target), { recursive: true });
12758
+ runDisplayCmd("scrot", ["-o", target]);
12759
+ console.log(target);
12760
+ }
12761
+ async function computerClickCommand(xStr, yStr, options) {
12762
+ const x = parseCoord(xStr, "x");
12763
+ const y = parseCoord(yStr, "y");
12764
+ const button = options.button ?? "1";
12765
+ const args = ["mousemove", "--sync", String(x), String(y)];
12766
+ if (options.modifiers) {
12767
+ for (const mod of options.modifiers.split("+")) {
12768
+ args.push("keydown", mod);
12769
+ }
12770
+ }
12771
+ if (options.double) {
12772
+ args.push("click", "--repeat", "2", "--delay", "50", button);
12773
+ } else {
12774
+ args.push("click", button);
12775
+ }
12776
+ if (options.modifiers) {
12777
+ for (const mod of options.modifiers.split("+").reverse()) {
12778
+ args.push("keyup", mod);
12779
+ }
12780
+ }
12781
+ runDisplayCmd("xdotool", args);
12782
+ console.log(`clicked ${button === "1" ? "left" : button === "2" ? "middle" : button === "3" ? "right" : `button ${button}`} at (${x},${y})${options.double ? " x2" : ""}`);
12783
+ }
12784
+ async function computerMoveCommand(xStr, yStr) {
12785
+ const x = parseCoord(xStr, "x");
12786
+ const y = parseCoord(yStr, "y");
12787
+ runDisplayCmd("xdotool", ["mousemove", "--sync", String(x), String(y)]);
12788
+ console.log(`moved to (${x},${y})`);
12789
+ }
12790
+ async function computerTypeCommand(text, options) {
12791
+ const delay = options.delay ? parseCoord(options.delay, "--delay") : 12;
12792
+ runDisplayCmd("xdotool", ["type", "--delay", String(delay), "--", text]);
12793
+ console.log(`typed ${text.length} char${text.length === 1 ? "" : "s"}`);
12794
+ }
12795
+ async function computerKeyCommand(combo) {
12796
+ runDisplayCmd("xdotool", ["key", "--", combo]);
12797
+ console.log(`pressed ${combo}`);
12798
+ }
12799
+ async function computerScrollCommand(direction, options) {
12800
+ const dir = direction.toLowerCase();
12801
+ const buttonMap = { up: "4", down: "5", left: "6", right: "7" };
12802
+ const button = buttonMap[dir];
12803
+ if (!button) fail(`direction must be one of up|down|left|right (got "${direction}")`);
12804
+ const amount = options.amount ? parseCoord(options.amount, "--amount") : 3;
12805
+ const args = [];
12806
+ if (options.x && options.y) {
12807
+ args.push("mousemove", "--sync", options.x, options.y);
12808
+ }
12809
+ args.push("click", "--repeat", String(amount), "--delay", "30", button);
12810
+ runDisplayCmd("xdotool", args);
12811
+ console.log(`scrolled ${dir} x${amount}`);
12812
+ }
12813
+ async function computerDragCommand(fx, fy, tx, ty) {
12814
+ const fromX = parseCoord(fx, "fromX");
12815
+ const fromY = parseCoord(fy, "fromY");
12816
+ const toX = parseCoord(tx, "toX");
12817
+ const toY = parseCoord(ty, "toY");
12818
+ runDisplayCmd("xdotool", [
12819
+ "mousemove",
12820
+ "--sync",
12821
+ String(fromX),
12822
+ String(fromY),
12823
+ "mousedown",
12824
+ "1",
12825
+ "mousemove",
12826
+ "--sync",
12827
+ String(toX),
12828
+ String(toY),
12829
+ "mouseup",
12830
+ "1"
12831
+ ]);
12832
+ console.log(`dragged (${fromX},${fromY}) -> (${toX},${toY})`);
12833
+ }
12834
+ var APP_ALIASES = {
12835
+ chrome: ["google-chrome", "--no-first-run", "--no-default-browser-check", "--start-maximized", "--user-data-dir=/tmp/replicas-computer/chrome-profile"],
12836
+ chromium: ["chromium", "--no-first-run", "--no-default-browser-check"],
12837
+ firefox: ["firefox"],
12838
+ terminal: ["xfce4-terminal"],
12839
+ xterm: ["xterm"],
12840
+ notepad: ["mousepad"],
12841
+ editor: ["mousepad"],
12842
+ files: ["thunar"],
12843
+ filemanager: ["thunar"]
12844
+ };
12845
+ async function computerLaunchCommand(app, args) {
12846
+ ensureServicesRunning();
12847
+ const baseArgs = APP_ALIASES[app] ?? [app];
12848
+ const bin = baseArgs[0];
12849
+ const fullArgs = [...baseArgs.slice(1), ...args];
12850
+ const child = spawn3(bin, fullArgs, {
12851
+ env: withDisplay(),
12852
+ detached: true,
12853
+ stdio: "ignore"
12854
+ });
12855
+ child.unref();
12856
+ console.log(`launched ${bin} (pid ${child.pid})`);
12857
+ }
12858
+ var RECORD_PID_FILE = `${STATE_DIR}/ffmpeg.pid`;
12859
+ async function computerRecordStartCommand(path6, options) {
12860
+ ensureServicesRunning();
12861
+ if (existsSync(RECORD_PID_FILE)) {
12862
+ const pid = parseInt(readFileSync(RECORD_PID_FILE, "utf8").trim(), 10);
12863
+ if (Number.isFinite(pid)) {
12864
+ let alive = false;
12865
+ try {
12866
+ process.kill(pid, 0);
12867
+ alive = true;
12868
+ } catch {
12869
+ }
12870
+ if (alive) fail(`recording already in progress (pid ${pid}). run \`replicas computer record stop\` first.`);
12871
+ }
12872
+ }
12873
+ const target = resolvePath(path6);
12874
+ mkdirSync(dirname(target), { recursive: true });
12875
+ const fps = options.fps ? parseCoord(options.fps, "--fps") : 60;
12876
+ const width = process.env.REPLICAS_DESKTOP_WIDTH || String(DESKTOP_VIEWER_WIDTH);
12877
+ const height = process.env.REPLICAS_DESKTOP_HEIGHT || String(DESKTOP_VIEWER_HEIGHT);
12878
+ const child = spawn3("ffmpeg", [
12879
+ "-y",
12880
+ "-hide_banner",
12881
+ "-loglevel",
12882
+ "warning",
12883
+ "-f",
12884
+ "x11grab",
12885
+ "-framerate",
12886
+ String(fps),
12887
+ "-video_size",
12888
+ `${width}x${height}`,
12889
+ "-draw_mouse",
12890
+ "1",
12891
+ "-i",
12892
+ DEFAULT_DISPLAY,
12893
+ "-c:v",
12894
+ "libx264",
12895
+ "-preset",
12896
+ "ultrafast",
12897
+ "-tune",
12898
+ "zerolatency",
12899
+ "-crf",
12900
+ "23",
12901
+ "-pix_fmt",
12902
+ "yuv420p",
12903
+ "-movflags",
12904
+ "+faststart+frag_keyframe+empty_moov",
12905
+ target
12906
+ ], { detached: true, stdio: "ignore" });
12907
+ child.unref();
12908
+ if (!child.pid) fail("failed to launch ffmpeg");
12909
+ mkdirSync(STATE_DIR, { recursive: true });
12910
+ writeFileSync(RECORD_PID_FILE, String(child.pid));
12911
+ writeFileSync(`${STATE_DIR}/recording-path.txt`, target);
12912
+ console.log(target);
12913
+ }
12914
+ async function computerRecordStopCommand() {
12915
+ if (!existsSync(RECORD_PID_FILE)) fail("no recording in progress");
12916
+ const pid = parseInt(readFileSync(RECORD_PID_FILE, "utf8").trim(), 10);
12917
+ if (!Number.isFinite(pid)) fail("invalid recording pidfile");
12918
+ try {
12919
+ process.kill(pid, "SIGINT");
12920
+ } catch {
12921
+ }
12922
+ for (let i = 0; i < 30; i++) {
12923
+ try {
12924
+ process.kill(pid, 0);
12925
+ } catch {
12926
+ break;
12927
+ }
12928
+ await new Promise((r) => setTimeout(r, 200));
12929
+ }
12930
+ rmSync(RECORD_PID_FILE, { force: true });
12931
+ const pathFile = `${STATE_DIR}/recording-path.txt`;
12932
+ if (existsSync(pathFile)) {
12933
+ console.log(readFileSync(pathFile, "utf8").trim());
12934
+ rmSync(pathFile, { force: true });
12935
+ }
12936
+ }
12937
+
12938
+ // src/commands/interactive.ts
12939
+ import chalk20 from "chalk";
12449
12940
 
12450
12941
  // src/interactive/index.tsx
12451
12942
  import { createCliRenderer } from "@opentui/core";
@@ -12898,7 +13389,7 @@ function useWorkspaceEvents(workspaceId, enabled = true) {
12898
13389
  while (!cancelled) {
12899
13390
  await connect();
12900
13391
  if (cancelled) break;
12901
- await new Promise((resolve) => setTimeout(resolve, 1e3));
13392
+ await new Promise((resolve2) => setTimeout(resolve2, 1e3));
12902
13393
  }
12903
13394
  };
12904
13395
  run().catch(() => setConnected(false));
@@ -15565,13 +16056,13 @@ async function interactiveCommand() {
15565
16056
  'No organization selected. Please run "replicas org switch" to select an organization.'
15566
16057
  );
15567
16058
  }
15568
- console.log(chalk19.gray("Starting interactive mode..."));
16059
+ console.log(chalk20.gray("Starting interactive mode..."));
15569
16060
  await launchInteractive();
15570
16061
  }
15571
16062
 
15572
16063
  // src/commands/environment.ts
15573
16064
  import fs5 from "fs";
15574
- import chalk20 from "chalk";
16065
+ import chalk21 from "chalk";
15575
16066
  import prompts5 from "prompts";
15576
16067
  var UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
15577
16068
  function maskValue(value) {
@@ -15584,38 +16075,38 @@ async function resolveEnvironmentId(input) {
15584
16075
  const response = await orgAuthenticatedFetch("/v1/environments");
15585
16076
  const env = response.environments.find((e) => e.name === input);
15586
16077
  if (!env) {
15587
- console.log(chalk20.red(`Environment not found: ${input}`));
16078
+ console.log(chalk21.red(`Environment not found: ${input}`));
15588
16079
  const available = response.environments.map((e) => e.name).join(", ");
15589
- console.log(chalk20.gray(`Available: ${available || "(none)"}`));
16080
+ console.log(chalk21.gray(`Available: ${available || "(none)"}`));
15590
16081
  process.exit(1);
15591
16082
  }
15592
16083
  return env.id;
15593
16084
  }
15594
16085
  function printEnvironment(env) {
15595
- console.log(chalk20.white(` ${env.name}${env.is_global ? chalk20.gray(" (global)") : ""}`));
15596
- console.log(chalk20.gray(` ID: ${env.id}`));
16086
+ console.log(chalk21.white(` ${env.name}${env.is_global ? chalk21.gray(" (global)") : ""}`));
16087
+ console.log(chalk21.gray(` ID: ${env.id}`));
15597
16088
  if (env.description) {
15598
- console.log(chalk20.gray(` Description: ${env.description}`));
16089
+ console.log(chalk21.gray(` Description: ${env.description}`));
15599
16090
  }
15600
16091
  if (env.repository_id) {
15601
- console.log(chalk20.gray(` Repository: ${env.repository_id}`));
16092
+ console.log(chalk21.gray(` Repository: ${env.repository_id}`));
15602
16093
  } else if (env.repository_set_id) {
15603
- console.log(chalk20.gray(` Repository Set: ${env.repository_set_id}`));
16094
+ console.log(chalk21.gray(` Repository Set: ${env.repository_set_id}`));
15604
16095
  }
15605
16096
  if (env.variable_count !== void 0) {
15606
- console.log(chalk20.gray(` Variables: ${env.variable_count}, Files: ${env.file_count ?? 0}, Skills: ${env.skill_count ?? 0}, MCPs: ${env.mcp_count ?? 0}`));
16097
+ console.log(chalk21.gray(` Variables: ${env.variable_count}, Files: ${env.file_count ?? 0}, Skills: ${env.skill_count ?? 0}, MCPs: ${env.mcp_count ?? 0}`));
15607
16098
  }
15608
- console.log(chalk20.gray(` Updated: ${formatDate2(env.updated_at)}`));
16099
+ console.log(chalk21.gray(` Updated: ${formatDate2(env.updated_at)}`));
15609
16100
  console.log();
15610
16101
  }
15611
16102
  async function environmentListCommand() {
15612
16103
  ensureOrgApiAuthenticated();
15613
16104
  const response = await orgAuthenticatedFetch("/v1/environments");
15614
16105
  if (response.environments.length === 0) {
15615
- console.log(chalk20.yellow("\nNo environments found.\n"));
16106
+ console.log(chalk21.yellow("\nNo environments found.\n"));
15616
16107
  return;
15617
16108
  }
15618
- console.log(chalk20.green(`
16109
+ console.log(chalk21.green(`
15619
16110
  Environments (${response.environments.length}):
15620
16111
  `));
15621
16112
  for (const env of response.environments) {
@@ -15626,7 +16117,7 @@ async function environmentGetCommand(idOrName) {
15626
16117
  ensureOrgApiAuthenticated();
15627
16118
  const id = await resolveEnvironmentId(idOrName);
15628
16119
  const response = await orgAuthenticatedFetch(`/v1/environments/${id}`);
15629
- console.log(chalk20.green(`
16120
+ console.log(chalk21.green(`
15630
16121
  Environment: ${response.environment.name}
15631
16122
  `));
15632
16123
  printEnvironment(response.environment);
@@ -15642,7 +16133,7 @@ async function environmentCreateCommand(name, options) {
15642
16133
  validate: (v) => v.trim() ? true : "Name is required"
15643
16134
  });
15644
16135
  if (!r.name) {
15645
- console.log(chalk20.yellow("\nCancelled."));
16136
+ console.log(chalk21.yellow("\nCancelled."));
15646
16137
  return;
15647
16138
  }
15648
16139
  envName = r.name;
@@ -15655,8 +16146,8 @@ async function environmentCreateCommand(name, options) {
15655
16146
  const repos2 = await orgAuthenticatedFetch("/v1/repositories");
15656
16147
  const repo = repos2.repositories.find((r) => r.name === options.repository);
15657
16148
  if (!repo) {
15658
- console.log(chalk20.red(`Repository not found: ${options.repository}`));
15659
- console.log(chalk20.gray(`Available: ${repos2.repositories.map((r) => r.name).join(", ")}`));
16149
+ console.log(chalk21.red(`Repository not found: ${options.repository}`));
16150
+ console.log(chalk21.gray(`Available: ${repos2.repositories.map((r) => r.name).join(", ")}`));
15660
16151
  process.exit(1);
15661
16152
  }
15662
16153
  repositoryId = repo.id;
@@ -15686,9 +16177,9 @@ async function environmentCreateCommand(name, options) {
15686
16177
  method: "POST",
15687
16178
  body
15688
16179
  });
15689
- console.log(chalk20.green(`
16180
+ console.log(chalk21.green(`
15690
16181
  Created environment: ${response.environment.name}`));
15691
- console.log(chalk20.gray(` ID: ${response.environment.id}
16182
+ console.log(chalk21.gray(` ID: ${response.environment.id}
15692
16183
  `));
15693
16184
  }
15694
16185
  async function environmentEditCommand(idOrName, options) {
@@ -15707,21 +16198,21 @@ async function environmentEditCommand(idOrName, options) {
15707
16198
  const repos2 = await orgAuthenticatedFetch("/v1/repositories");
15708
16199
  const repo = repos2.repositories.find((r) => r.name === options.repository);
15709
16200
  if (!repo) {
15710
- console.log(chalk20.red(`Repository not found: ${options.repository}`));
16201
+ console.log(chalk21.red(`Repository not found: ${options.repository}`));
15711
16202
  process.exit(1);
15712
16203
  }
15713
16204
  body.repository_id = repo.id;
15714
16205
  }
15715
16206
  }
15716
16207
  if (Object.keys(body).length === 0) {
15717
- console.log(chalk20.yellow("\nNo changes specified. Pass --name, --description, --repository, or --system-prompt."));
16208
+ console.log(chalk21.yellow("\nNo changes specified. Pass --name, --description, --repository, or --system-prompt."));
15718
16209
  return;
15719
16210
  }
15720
16211
  const response = await orgAuthenticatedFetch(`/v1/environments/${id}`, {
15721
16212
  method: "PATCH",
15722
16213
  body
15723
16214
  });
15724
- console.log(chalk20.green(`
16215
+ console.log(chalk21.green(`
15725
16216
  Updated environment: ${response.environment.name}
15726
16217
  `));
15727
16218
  }
@@ -15736,20 +16227,20 @@ async function environmentDeleteCommand(idOrName, options) {
15736
16227
  initial: false
15737
16228
  });
15738
16229
  if (!r.confirm) {
15739
- console.log(chalk20.yellow("\nCancelled."));
16230
+ console.log(chalk21.yellow("\nCancelled."));
15740
16231
  return;
15741
16232
  }
15742
16233
  }
15743
16234
  await orgAuthenticatedFetch(`/v1/environments/${id}`, { method: "DELETE" });
15744
- console.log(chalk20.green(`
16235
+ console.log(chalk21.green(`
15745
16236
  Deleted environment ${idOrName}.
15746
16237
  `));
15747
16238
  }
15748
16239
  function printVariable(v, reveal) {
15749
- console.log(chalk20.white(` ${v.key}`));
15750
- console.log(chalk20.gray(` ID: ${v.id}`));
15751
- console.log(chalk20.gray(` Value: ${reveal ? v.value : maskValue(v.value)}`));
15752
- console.log(chalk20.gray(` Updated: ${formatDate2(v.updated_at)}`));
16240
+ console.log(chalk21.white(` ${v.key}`));
16241
+ console.log(chalk21.gray(` ID: ${v.id}`));
16242
+ console.log(chalk21.gray(` Value: ${reveal ? v.value : maskValue(v.value)}`));
16243
+ console.log(chalk21.gray(` Updated: ${formatDate2(v.updated_at)}`));
15753
16244
  console.log();
15754
16245
  }
15755
16246
  async function envVarsListCommand(envIdOrName, options) {
@@ -15759,14 +16250,14 @@ async function envVarsListCommand(envIdOrName, options) {
15759
16250
  `/v1/environments/${id}/variables`
15760
16251
  );
15761
16252
  if (response.environment_variables.length === 0) {
15762
- console.log(chalk20.yellow("\nNo variables.\n"));
16253
+ console.log(chalk21.yellow("\nNo variables.\n"));
15763
16254
  return;
15764
16255
  }
15765
- console.log(chalk20.green(`
16256
+ console.log(chalk21.green(`
15766
16257
  Variables (${response.environment_variables.length}):
15767
16258
  `));
15768
16259
  if (!options.reveal) {
15769
- console.log(chalk20.gray(" Values are masked. Pass --reveal to show full values.\n"));
16260
+ console.log(chalk21.gray(" Values are masked. Pass --reveal to show full values.\n"));
15770
16261
  }
15771
16262
  for (const v of response.environment_variables) printVariable(v, !!options.reveal);
15772
16263
  }
@@ -15783,7 +16274,7 @@ async function envVarsSetCommand(envIdOrName, key, value) {
15783
16274
  `/v1/environments/${id}/variables/${match.id}`,
15784
16275
  { method: "PATCH", body: body2 }
15785
16276
  );
15786
- console.log(chalk20.green(`
16277
+ console.log(chalk21.green(`
15787
16278
  Updated variable ${response2.environment_variable.key}.
15788
16279
  `));
15789
16280
  return;
@@ -15797,7 +16288,7 @@ Updated variable ${response2.environment_variable.key}.
15797
16288
  `/v1/environments/${id}/variables`,
15798
16289
  { method: "POST", body }
15799
16290
  );
15800
- console.log(chalk20.green(`
16291
+ console.log(chalk21.green(`
15801
16292
  Created variable ${response.environment_variable.key}.
15802
16293
  `));
15803
16294
  }
@@ -15811,7 +16302,7 @@ async function envVarsDeleteCommand(envIdOrName, keyOrId, options) {
15811
16302
  );
15812
16303
  const match = existing.environment_variables.find((v) => v.key === keyOrId);
15813
16304
  if (!match) {
15814
- console.log(chalk20.red(`Variable not found: ${keyOrId}`));
16305
+ console.log(chalk21.red(`Variable not found: ${keyOrId}`));
15815
16306
  process.exit(1);
15816
16307
  }
15817
16308
  variableId = match.id;
@@ -15824,23 +16315,23 @@ async function envVarsDeleteCommand(envIdOrName, keyOrId, options) {
15824
16315
  initial: false
15825
16316
  });
15826
16317
  if (!r.confirm) {
15827
- console.log(chalk20.yellow("\nCancelled."));
16318
+ console.log(chalk21.yellow("\nCancelled."));
15828
16319
  return;
15829
16320
  }
15830
16321
  }
15831
16322
  await orgAuthenticatedFetch(`/v1/environments/${id}/variables/${variableId}`, {
15832
16323
  method: "DELETE"
15833
16324
  });
15834
- console.log(chalk20.green(`
16325
+ console.log(chalk21.green(`
15835
16326
  Deleted variable ${keyOrId}.
15836
16327
  `));
15837
16328
  }
15838
16329
  function printFile(f) {
15839
- console.log(chalk20.white(` ${f.path}`));
15840
- console.log(chalk20.gray(` ID: ${f.id}`));
15841
- console.log(chalk20.gray(` Name: ${f.name}`));
15842
- console.log(chalk20.gray(` Size: ${f.content.length} bytes`));
15843
- console.log(chalk20.gray(` Updated: ${formatDate2(f.updated_at)}`));
16330
+ console.log(chalk21.white(` ${f.path}`));
16331
+ console.log(chalk21.gray(` ID: ${f.id}`));
16332
+ console.log(chalk21.gray(` Name: ${f.name}`));
16333
+ console.log(chalk21.gray(` Size: ${f.content.length} bytes`));
16334
+ console.log(chalk21.gray(` Updated: ${formatDate2(f.updated_at)}`));
15844
16335
  console.log();
15845
16336
  }
15846
16337
  async function envFilesListCommand(envIdOrName) {
@@ -15850,10 +16341,10 @@ async function envFilesListCommand(envIdOrName) {
15850
16341
  `/v1/environments/${id}/files`
15851
16342
  );
15852
16343
  if (response.environment_files.length === 0) {
15853
- console.log(chalk20.yellow("\nNo files.\n"));
16344
+ console.log(chalk21.yellow("\nNo files.\n"));
15854
16345
  return;
15855
16346
  }
15856
- console.log(chalk20.green(`
16347
+ console.log(chalk21.green(`
15857
16348
  Files (${response.environment_files.length}):
15858
16349
  `));
15859
16350
  for (const f of response.environment_files) printFile(f);
@@ -15884,7 +16375,7 @@ async function envFilesSetCommand(envIdOrName, destinationPath, options) {
15884
16375
  `/v1/environments/${id}/files/${match.id}`,
15885
16376
  { method: "PATCH", body: body2 }
15886
16377
  );
15887
- console.log(chalk20.green(`
16378
+ console.log(chalk21.green(`
15888
16379
  Updated file ${response2.environment_file.path}.
15889
16380
  `));
15890
16381
  return;
@@ -15899,7 +16390,7 @@ Updated file ${response2.environment_file.path}.
15899
16390
  `/v1/environments/${id}/files`,
15900
16391
  { method: "POST", body }
15901
16392
  );
15902
- console.log(chalk20.green(`
16393
+ console.log(chalk21.green(`
15903
16394
  Created file ${response.environment_file.path}.
15904
16395
  `));
15905
16396
  }
@@ -15913,7 +16404,7 @@ async function envFilesDeleteCommand(envIdOrName, pathOrId, options) {
15913
16404
  );
15914
16405
  const match = existing.environment_files.find((f) => f.path === pathOrId);
15915
16406
  if (!match) {
15916
- console.log(chalk20.red(`File not found: ${pathOrId}`));
16407
+ console.log(chalk21.red(`File not found: ${pathOrId}`));
15917
16408
  process.exit(1);
15918
16409
  }
15919
16410
  fileId = match.id;
@@ -15926,20 +16417,20 @@ async function envFilesDeleteCommand(envIdOrName, pathOrId, options) {
15926
16417
  initial: false
15927
16418
  });
15928
16419
  if (!r.confirm) {
15929
- console.log(chalk20.yellow("\nCancelled."));
16420
+ console.log(chalk21.yellow("\nCancelled."));
15930
16421
  return;
15931
16422
  }
15932
16423
  }
15933
16424
  await orgAuthenticatedFetch(`/v1/environments/${id}/files/${fileId}`, {
15934
16425
  method: "DELETE"
15935
16426
  });
15936
- console.log(chalk20.green(`
16427
+ console.log(chalk21.green(`
15937
16428
  Deleted file ${pathOrId}.
15938
16429
  `));
15939
16430
  }
15940
16431
 
15941
16432
  // src/index.ts
15942
- var CLI_VERSION = "0.2.182";
16433
+ var CLI_VERSION = "0.2.183";
15943
16434
  function parseBooleanOption(value) {
15944
16435
  if (value === "true") return true;
15945
16436
  if (value === "false") return false;
@@ -15952,7 +16443,7 @@ program.command("login").description("Authenticate with your Replicas account").
15952
16443
  await loginCommand();
15953
16444
  } catch (error) {
15954
16445
  if (error instanceof Error) {
15955
- console.error(chalk21.red(`
16446
+ console.error(chalk22.red(`
15956
16447
  \u2717 ${error.message}
15957
16448
  `));
15958
16449
  }
@@ -15964,7 +16455,7 @@ program.command("init").description("Create a replicas.json or replicas.yaml con
15964
16455
  initCommand(options);
15965
16456
  } catch (error) {
15966
16457
  if (error instanceof Error) {
15967
- console.error(chalk21.red(`
16458
+ console.error(chalk22.red(`
15968
16459
  \u2717 ${error.message}
15969
16460
  `));
15970
16461
  }
@@ -15976,7 +16467,7 @@ program.command("logout").description("Clear stored credentials").action(() => {
15976
16467
  logoutCommand();
15977
16468
  } catch (error) {
15978
16469
  if (error instanceof Error) {
15979
- console.error(chalk21.red(`
16470
+ console.error(chalk22.red(`
15980
16471
  \u2717 ${error.message}
15981
16472
  `));
15982
16473
  }
@@ -15988,7 +16479,7 @@ program.command("whoami").description("Display current authenticated user").acti
15988
16479
  await whoamiCommand();
15989
16480
  } catch (error) {
15990
16481
  if (error instanceof Error) {
15991
- console.error(chalk21.red(`
16482
+ console.error(chalk22.red(`
15992
16483
  \u2717 ${error.message}
15993
16484
  `));
15994
16485
  }
@@ -16000,7 +16491,7 @@ program.command("codex-auth").description("Authenticate Replicas with your Codex
16000
16491
  await codexAuthCommand(options);
16001
16492
  } catch (error) {
16002
16493
  if (error instanceof Error) {
16003
- console.error(chalk21.red(`
16494
+ console.error(chalk22.red(`
16004
16495
  \u2717 ${error.message}
16005
16496
  `));
16006
16497
  }
@@ -16012,7 +16503,7 @@ program.command("claude-auth").description("Authenticate Replicas with your Clau
16012
16503
  await claudeAuthCommand(options);
16013
16504
  } catch (error) {
16014
16505
  if (error instanceof Error) {
16015
- console.error(chalk21.red(`
16506
+ console.error(chalk22.red(`
16016
16507
  \u2717 ${error.message}
16017
16508
  `));
16018
16509
  }
@@ -16025,7 +16516,7 @@ org.command("switch").description("Switch to a different organization").action(a
16025
16516
  await orgSwitchCommand();
16026
16517
  } catch (error) {
16027
16518
  if (error instanceof Error) {
16028
- console.error(chalk21.red(`
16519
+ console.error(chalk22.red(`
16029
16520
  \u2717 ${error.message}
16030
16521
  `));
16031
16522
  }
@@ -16037,7 +16528,7 @@ org.action(async () => {
16037
16528
  await orgCommand();
16038
16529
  } catch (error) {
16039
16530
  if (error instanceof Error) {
16040
- console.error(chalk21.red(`
16531
+ console.error(chalk22.red(`
16041
16532
  \u2717 ${error.message}
16042
16533
  `));
16043
16534
  }
@@ -16049,7 +16540,7 @@ program.command("connect <workspace-name>").description("Connect to a workspace
16049
16540
  await connectCommand(workspaceName);
16050
16541
  } catch (error) {
16051
16542
  if (error instanceof Error) {
16052
- console.error(chalk21.red(`
16543
+ console.error(chalk22.red(`
16053
16544
  \u2717 ${error.message}
16054
16545
  `));
16055
16546
  }
@@ -16061,7 +16552,7 @@ program.command("code <workspace-name>").description("Open a workspace in VSCode
16061
16552
  await codeCommand(workspaceName);
16062
16553
  } catch (error) {
16063
16554
  if (error instanceof Error) {
16064
- console.error(chalk21.red(`
16555
+ console.error(chalk22.red(`
16065
16556
  \u2717 ${error.message}
16066
16557
  `));
16067
16558
  }
@@ -16074,7 +16565,7 @@ config.command("get <key>").description("Get a configuration value").action(asyn
16074
16565
  await configGetCommand(key);
16075
16566
  } catch (error) {
16076
16567
  if (error instanceof Error) {
16077
- console.error(chalk21.red(`
16568
+ console.error(chalk22.red(`
16078
16569
  \u2717 ${error.message}
16079
16570
  `));
16080
16571
  }
@@ -16086,7 +16577,7 @@ config.command("set <key> <value>").description("Set a configuration value").act
16086
16577
  await configSetCommand(key, value);
16087
16578
  } catch (error) {
16088
16579
  if (error instanceof Error) {
16089
- console.error(chalk21.red(`
16580
+ console.error(chalk22.red(`
16090
16581
  \u2717 ${error.message}
16091
16582
  `));
16092
16583
  }
@@ -16098,7 +16589,7 @@ config.command("list").description("List all configuration values").action(async
16098
16589
  await configListCommand();
16099
16590
  } catch (error) {
16100
16591
  if (error instanceof Error) {
16101
- console.error(chalk21.red(`
16592
+ console.error(chalk22.red(`
16102
16593
  \u2717 ${error.message}
16103
16594
  `));
16104
16595
  }
@@ -16110,7 +16601,7 @@ program.command("list").description("List all replicas").option("-p, --page <pag
16110
16601
  await replicaListCommand(options);
16111
16602
  } catch (error) {
16112
16603
  if (error instanceof Error) {
16113
- console.error(chalk21.red(`
16604
+ console.error(chalk22.red(`
16114
16605
  \u2717 ${error.message}
16115
16606
  `));
16116
16607
  }
@@ -16122,7 +16613,7 @@ program.command("get <id>").description("Get replica details by ID").action(asyn
16122
16613
  await replicaGetCommand(id);
16123
16614
  } catch (error) {
16124
16615
  if (error instanceof Error) {
16125
- console.error(chalk21.red(`
16616
+ console.error(chalk22.red(`
16126
16617
  \u2717 ${error.message}
16127
16618
  `));
16128
16619
  }
@@ -16134,7 +16625,7 @@ program.command("create [name]").description("Create a new replica").option("-m,
16134
16625
  await replicaCreateCommand(name, options);
16135
16626
  } catch (error) {
16136
16627
  if (error instanceof Error) {
16137
- console.error(chalk21.red(`
16628
+ console.error(chalk22.red(`
16138
16629
  \u2717 ${error.message}
16139
16630
  `));
16140
16631
  }
@@ -16146,7 +16637,7 @@ program.command("send <id>").description("Send a message to a replica").option("
16146
16637
  await replicaSendCommand(id, options);
16147
16638
  } catch (error) {
16148
16639
  if (error instanceof Error) {
16149
- console.error(chalk21.red(`
16640
+ console.error(chalk22.red(`
16150
16641
  \u2717 ${error.message}
16151
16642
  `));
16152
16643
  }
@@ -16158,7 +16649,7 @@ program.command("delete <id>").description("Delete a replica").option("-f, --for
16158
16649
  await replicaDeleteCommand(id, options);
16159
16650
  } catch (error) {
16160
16651
  if (error instanceof Error) {
16161
- console.error(chalk21.red(`
16652
+ console.error(chalk22.red(`
16162
16653
  \u2717 ${error.message}
16163
16654
  `));
16164
16655
  }
@@ -16170,7 +16661,7 @@ program.command("read <id>").description("Read conversation history of a replica
16170
16661
  await replicaReadCommand(id, options);
16171
16662
  } catch (error) {
16172
16663
  if (error instanceof Error) {
16173
- console.error(chalk21.red(`
16664
+ console.error(chalk22.red(`
16174
16665
  \u2717 ${error.message}
16175
16666
  `));
16176
16667
  }
@@ -16183,7 +16674,7 @@ automation.command("list").description("List all automations").option("-p, --pag
16183
16674
  await automationListCommand(options);
16184
16675
  } catch (error) {
16185
16676
  if (error instanceof Error) {
16186
- console.error(chalk21.red(`
16677
+ console.error(chalk22.red(`
16187
16678
  \u2717 ${error.message}
16188
16679
  `));
16189
16680
  }
@@ -16195,7 +16686,7 @@ automation.command("get <id>").description("Get automation details by ID").actio
16195
16686
  await automationGetCommand(id);
16196
16687
  } catch (error) {
16197
16688
  if (error instanceof Error) {
16198
- console.error(chalk21.red(`
16689
+ console.error(chalk22.red(`
16199
16690
  \u2717 ${error.message}
16200
16691
  `));
16201
16692
  }
@@ -16210,7 +16701,7 @@ automation.command("create [name]").description("Create a new automation").optio
16210
16701
  });
16211
16702
  } catch (error) {
16212
16703
  if (error instanceof Error) {
16213
- console.error(chalk21.red(`
16704
+ console.error(chalk22.red(`
16214
16705
  \u2717 ${error.message}
16215
16706
  `));
16216
16707
  }
@@ -16222,7 +16713,7 @@ automation.command("edit <id>").description("Edit an existing automation").optio
16222
16713
  await automationEditCommand(id, options);
16223
16714
  } catch (error) {
16224
16715
  if (error instanceof Error) {
16225
- console.error(chalk21.red(`
16716
+ console.error(chalk22.red(`
16226
16717
  \u2717 ${error.message}
16227
16718
  `));
16228
16719
  }
@@ -16234,7 +16725,7 @@ automation.command("run <id>").description("Manually trigger an automation (cron
16234
16725
  await automationRunCommand(id);
16235
16726
  } catch (error) {
16236
16727
  if (error instanceof Error) {
16237
- console.error(chalk21.red(`
16728
+ console.error(chalk22.red(`
16238
16729
  \u2717 ${error.message}
16239
16730
  `));
16240
16731
  }
@@ -16246,7 +16737,7 @@ automation.command("delete <id>").description("Delete an automation").option("-f
16246
16737
  await automationDeleteCommand(id, options);
16247
16738
  } catch (error) {
16248
16739
  if (error instanceof Error) {
16249
- console.error(chalk21.red(`
16740
+ console.error(chalk22.red(`
16250
16741
  \u2717 ${error.message}
16251
16742
  `));
16252
16743
  }
@@ -16258,7 +16749,7 @@ automation.action(async () => {
16258
16749
  await automationListCommand({});
16259
16750
  } catch (error) {
16260
16751
  if (error instanceof Error) {
16261
- console.error(chalk21.red(`
16752
+ console.error(chalk22.red(`
16262
16753
  \u2717 ${error.message}
16263
16754
  `));
16264
16755
  }
@@ -16271,7 +16762,7 @@ repos.command("list").description("List all repositories").action(async () => {
16271
16762
  await repositoriesListCommand();
16272
16763
  } catch (error) {
16273
16764
  if (error instanceof Error) {
16274
- console.error(chalk21.red(`
16765
+ console.error(chalk22.red(`
16275
16766
  \u2717 ${error.message}
16276
16767
  `));
16277
16768
  }
@@ -16283,7 +16774,7 @@ repos.action(async () => {
16283
16774
  await repositoriesListCommand();
16284
16775
  } catch (error) {
16285
16776
  if (error instanceof Error) {
16286
- console.error(chalk21.red(`
16777
+ console.error(chalk22.red(`
16287
16778
  \u2717 ${error.message}
16288
16779
  `));
16289
16780
  }
@@ -16296,7 +16787,7 @@ environment.command("list").description("List all environments").action(async ()
16296
16787
  await environmentListCommand();
16297
16788
  } catch (error) {
16298
16789
  if (error instanceof Error) {
16299
- console.error(chalk21.red(`
16790
+ console.error(chalk22.red(`
16300
16791
  \u2717 ${error.message}
16301
16792
  `));
16302
16793
  }
@@ -16308,7 +16799,7 @@ environment.command("get <id-or-name>").description('Get an environment by ID or
16308
16799
  await environmentGetCommand(idOrName);
16309
16800
  } catch (error) {
16310
16801
  if (error instanceof Error) {
16311
- console.error(chalk21.red(`
16802
+ console.error(chalk22.red(`
16312
16803
  \u2717 ${error.message}
16313
16804
  `));
16314
16805
  }
@@ -16320,7 +16811,7 @@ environment.command("create [name]").description("Create a new environment").opt
16320
16811
  await environmentCreateCommand(name, options);
16321
16812
  } catch (error) {
16322
16813
  if (error instanceof Error) {
16323
- console.error(chalk21.red(`
16814
+ console.error(chalk22.red(`
16324
16815
  \u2717 ${error.message}
16325
16816
  `));
16326
16817
  }
@@ -16332,7 +16823,7 @@ environment.command("edit <id-or-name>").description("Edit an environment").opti
16332
16823
  await environmentEditCommand(idOrName, options);
16333
16824
  } catch (error) {
16334
16825
  if (error instanceof Error) {
16335
- console.error(chalk21.red(`
16826
+ console.error(chalk22.red(`
16336
16827
  \u2717 ${error.message}
16337
16828
  `));
16338
16829
  }
@@ -16344,7 +16835,7 @@ environment.command("delete <id-or-name>").description("Delete an environment").
16344
16835
  await environmentDeleteCommand(idOrName, options);
16345
16836
  } catch (error) {
16346
16837
  if (error instanceof Error) {
16347
- console.error(chalk21.red(`
16838
+ console.error(chalk22.red(`
16348
16839
  \u2717 ${error.message}
16349
16840
  `));
16350
16841
  }
@@ -16357,7 +16848,7 @@ envVars.command("list <env>").description("List variables in an environment (val
16357
16848
  await envVarsListCommand(env, options);
16358
16849
  } catch (error) {
16359
16850
  if (error instanceof Error) {
16360
- console.error(chalk21.red(`
16851
+ console.error(chalk22.red(`
16361
16852
  \u2717 ${error.message}
16362
16853
  `));
16363
16854
  }
@@ -16369,7 +16860,7 @@ envVars.command("set <env> <key> <value>").description("Create or update a varia
16369
16860
  await envVarsSetCommand(env, key, value);
16370
16861
  } catch (error) {
16371
16862
  if (error instanceof Error) {
16372
- console.error(chalk21.red(`
16863
+ console.error(chalk22.red(`
16373
16864
  \u2717 ${error.message}
16374
16865
  `));
16375
16866
  }
@@ -16381,7 +16872,7 @@ envVars.command("delete <env> <key-or-id>").description("Delete a variable by ke
16381
16872
  await envVarsDeleteCommand(env, keyOrId, options);
16382
16873
  } catch (error) {
16383
16874
  if (error instanceof Error) {
16384
- console.error(chalk21.red(`
16875
+ console.error(chalk22.red(`
16385
16876
  \u2717 ${error.message}
16386
16877
  `));
16387
16878
  }
@@ -16394,7 +16885,7 @@ envFiles.command("list <env>").description("List files in an environment").actio
16394
16885
  await envFilesListCommand(env);
16395
16886
  } catch (error) {
16396
16887
  if (error instanceof Error) {
16397
- console.error(chalk21.red(`
16888
+ console.error(chalk22.red(`
16398
16889
  \u2717 ${error.message}
16399
16890
  `));
16400
16891
  }
@@ -16406,7 +16897,7 @@ envFiles.command("set <env> <destination-path>").description("Create or update a
16406
16897
  await envFilesSetCommand(env, destinationPath, options);
16407
16898
  } catch (error) {
16408
16899
  if (error instanceof Error) {
16409
- console.error(chalk21.red(`
16900
+ console.error(chalk22.red(`
16410
16901
  \u2717 ${error.message}
16411
16902
  `));
16412
16903
  }
@@ -16418,7 +16909,7 @@ envFiles.command("delete <env> <path-or-id>").description("Delete a file by dest
16418
16909
  await envFilesDeleteCommand(env, pathOrId, options);
16419
16910
  } catch (error) {
16420
16911
  if (error instanceof Error) {
16421
- console.error(chalk21.red(`
16912
+ console.error(chalk22.red(`
16422
16913
  \u2717 ${error.message}
16423
16914
  `));
16424
16915
  }
@@ -16430,7 +16921,7 @@ environment.action(async () => {
16430
16921
  await environmentListCommand();
16431
16922
  } catch (error) {
16432
16923
  if (error instanceof Error) {
16433
- console.error(chalk21.red(`
16924
+ console.error(chalk22.red(`
16434
16925
  \u2717 ${error.message}
16435
16926
  `));
16436
16927
  }
@@ -16442,7 +16933,7 @@ program.command("interact").alias("i").description("Launch the interactive termi
16442
16933
  await interactiveCommand();
16443
16934
  } catch (error) {
16444
16935
  if (error instanceof Error) {
16445
- console.error(chalk21.red(`
16936
+ console.error(chalk22.red(`
16446
16937
  \u2717 ${error.message}
16447
16938
  `));
16448
16939
  }
@@ -16493,7 +16984,7 @@ if (isAgentMode()) {
16493
16984
  await previewAddCommand(workspaceId, options);
16494
16985
  } catch (error) {
16495
16986
  if (error instanceof Error) {
16496
- console.error(chalk21.red(`
16987
+ console.error(chalk22.red(`
16497
16988
  \u2717 ${error.message}
16498
16989
  `));
16499
16990
  }
@@ -16505,7 +16996,7 @@ if (isAgentMode()) {
16505
16996
  await previewListCommand(workspaceId);
16506
16997
  } catch (error) {
16507
16998
  if (error instanceof Error) {
16508
- console.error(chalk21.red(`
16999
+ console.error(chalk22.red(`
16509
17000
  \u2717 ${error.message}
16510
17001
  `));
16511
17002
  }
@@ -16517,7 +17008,7 @@ if (isAgentMode()) {
16517
17008
  await previewRemoveCommand(workspaceId, options);
16518
17009
  } catch (error) {
16519
17010
  if (error instanceof Error) {
16520
- console.error(chalk21.red(`
17011
+ console.error(chalk22.red(`
16521
17012
  \u2717 ${error.message}
16522
17013
  `));
16523
17014
  }
@@ -16532,7 +17023,7 @@ if (isAgentMode()) {
16532
17023
  await mediaUploadCommand(files, options);
16533
17024
  } catch (error) {
16534
17025
  if (error instanceof Error) {
16535
- console.error(chalk21.red(`
17026
+ console.error(chalk22.red(`
16536
17027
  \u2717 ${error.message}
16537
17028
  `));
16538
17029
  }
@@ -16544,19 +17035,47 @@ if (isAgentMode()) {
16544
17035
  await mediaListCommand(options);
16545
17036
  } catch (error) {
16546
17037
  if (error instanceof Error) {
16547
- console.error(chalk21.red(`
17038
+ console.error(chalk22.red(`
16548
17039
  \u2717 ${error.message}
16549
17040
  `));
16550
17041
  }
16551
17042
  process.exit(1);
16552
17043
  }
16553
17044
  });
17045
+ const computer = program.command("computer").description("Drive the workspace Linux desktop (mouse, keyboard, screenshots, screen recording, live preview)");
17046
+ const wrap = (fn) => async (...args) => {
17047
+ try {
17048
+ await fn(...args);
17049
+ } catch (error) {
17050
+ if (error instanceof Error) {
17051
+ console.error(chalk22.red(`
17052
+ \u2717 ${error.message}
17053
+ `));
17054
+ }
17055
+ process.exit(1);
17056
+ }
17057
+ };
17058
+ computer.command("start").description("Ensure the desktop stack is running and create an authenticated noVNC preview URL. Prints the viewer URL.").option("--port <port>", "noVNC port to expose (default: 6080)").option("--display <id>", "X display to use (default: :99)").option("--size <WxH>", "Display size, e.g. 1920x1080 (default: 1920x1080)").action(wrap((options) => computerStartCommand(options)));
17059
+ computer.command("stop").description("Tear down the noVNC preview. Desktop services stay running for the next start.").option("--port <port>", "noVNC port to unregister (default: 6080)").action(wrap((options) => computerStopCommand(options)));
17060
+ computer.command("status").description("Show which desktop services are running and the active preview URL").action(wrap(() => computerStatusCommand()));
17061
+ computer.command("screenshot <path>").description("Capture the current desktop to a PNG file. Use the path with `replicas media upload` to share it.").action(wrap((path6) => computerScreenshotCommand(path6)));
17062
+ computer.command("click <x> <y>").description("Move to (x, y) and click. Coordinates are in pixels on the workspace display.").option("-b, --button <n>", "Mouse button (1=left, 2=middle, 3=right). Default 1.").option("--double", "Double-click instead of single-click").option("--modifiers <mods>", "Hold modifier keys during the click, e.g. ctrl or ctrl+shift").action(wrap((x, y, options) => computerClickCommand(x, y, options)));
17063
+ computer.command("move <x> <y>").description("Move the mouse to (x, y) without clicking").action(wrap((x, y) => computerMoveCommand(x, y)));
17064
+ computer.command("type <text>").description("Type a literal string into the focused field. Use `key` for key combos like ctrl+l.").option("--delay <ms>", "Per-character delay in ms (default 12 \u2248 80 wpm)").action(wrap((text, options) => computerTypeCommand(text, options)));
17065
+ computer.command("key <combo>").description("Press a key combo, e.g. Return, Escape, ctrl+l, ctrl+shift+t. Same syntax as xdotool key.").action(wrap((combo) => computerKeyCommand(combo)));
17066
+ computer.command("scroll <direction>").description("Scroll up | down | left | right. Optionally provide --x / --y to hover before scrolling.").option("--amount <n>", "Wheel ticks (default 3)").option("--x <x>", "Hover x before scrolling").option("--y <y>", "Hover y before scrolling").action(wrap((direction, options) => computerScrollCommand(direction, options)));
17067
+ computer.command("drag <fromX> <fromY> <toX> <toY>").description("Press the left mouse button at (fromX, fromY), drag to (toX, toY), release.").action(wrap((fx, fy, tx, ty) => computerDragCommand(fx, fy, tx, ty)));
17068
+ computer.command("launch <app> [args...]").description("Launch an app on the workspace display. Aliases: chrome, chromium, firefox, terminal.").action(wrap((app, args) => computerLaunchCommand(app, args)));
17069
+ const record = computer.command("record").description("Screen-record the workspace display to an MP4 (1080p 60fps libx264).");
17070
+ record.command("start <path>").description("Start recording to <path>. Output is fragmented MP4 (safe if the workspace dies mid-record).").option("--fps <n>", "Frame rate (default 60)").action(wrap((path6, options) => computerRecordStartCommand(path6, options)));
17071
+ record.command("stop").description("Stop the active recording and finalize the MP4. Prints the output path.").action(wrap(() => computerRecordStopCommand()));
16554
17072
  const allowed = /* @__PURE__ */ new Set([
16555
17073
  "init",
16556
17074
  "whoami",
16557
17075
  "connect",
16558
17076
  "preview",
16559
17077
  "media",
17078
+ "computer",
16560
17079
  "automation",
16561
17080
  "repos",
16562
17081
  "environment"