skydive-cli 0.1.0-beta.342 → 0.1.0-beta.351

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -70,6 +70,26 @@ skydive portal status
70
70
 
71
71
  Global flags: `--json`, `--quiet`, `--api-url <url>`.
72
72
 
73
+ ## Workspaces
74
+
75
+ Every `skydive` command acts inside one **workspace** — the org/team boundary
76
+ that owns your agents, conversations, and secrets. If your account belongs to
77
+ several workspaces (say, your company org and a personal one), the same agent
78
+ name can exist in both, so which workspace is active determines what every
79
+ command sees.
80
+
81
+ - `skydive workspace list` — workspaces on your account, with the active one
82
+ marked.
83
+ - `skydive workspace switch [workspace]` — change the active workspace. All
84
+ commands — `agents`, `secrets`, `keys`, `chat`, `conversations` — follow it.
85
+ - `skydive auth status` — shows who you're logged in as and which workspace is
86
+ active.
87
+ - In the chat TUI, `ctrl+w` switches workspace from the agent picker.
88
+
89
+ The active workspace is stored in the CLI config file and persists across
90
+ invocations. If a command can't find an agent you know exists, check
91
+ `skydive auth status` first — you're probably in the wrong workspace.
92
+
73
93
  ## `skydive secrets`
74
94
 
75
95
  Manage the environment secrets injected into an agent's sandbox. All
@@ -250,7 +270,7 @@ environments, and driving agents from other tools:
250
270
  ```sh
251
271
  skydive portal open # connect and stay in the foreground (ctrl+c to close)
252
272
  skydive portal open --agent grace # …and grant that agent on first connect
253
- skydive portal open --cwd ~/some/repo # working directory for the agent's commands
273
+ skydive portal open --cwd ~/some/repo # working directory for the agent's commands (a starting point, not a boundary — see above)
254
274
  skydive portal grant --agent grace # allow an agent (registers this machine if needed)
255
275
  skydive portal revoke --agent grace # remove an agent's access
256
276
  skydive portal status # machines, connection state, granted agents
package/dist/js/bin.mjs CHANGED
@@ -16,7 +16,7 @@ import zlib from "node:zlib";
16
16
  import os from "node:os";
17
17
 
18
18
  //#region package.json
19
- var version$1 = "0.1.0-beta.342";
19
+ var version$1 = "0.1.0-beta.351";
20
20
 
21
21
  //#endregion
22
22
  //#region src/types.ts
@@ -2622,7 +2622,7 @@ const chatCommand = {
2622
2622
  printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
2623
2623
  process.exit(1);
2624
2624
  }
2625
- const { runChat } = await import("./boot-BcuOicbb.mjs");
2625
+ const { runChat } = await import("./boot-C2plQe9S.mjs");
2626
2626
  await runChat({
2627
2627
  appUrl,
2628
2628
  sessionToken: session.value.sessionToken,
@@ -3416,7 +3416,7 @@ const switchCommand = {
3416
3416
  printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
3417
3417
  process.exit(1);
3418
3418
  }
3419
- const { runWorkspacePicker } = await import("./boot-BcuOicbb.mjs");
3419
+ const { runWorkspacePicker } = await import("./boot-C2plQe9S.mjs");
3420
3420
  await runWorkspacePicker(session);
3421
3421
  return;
3422
3422
  }
@@ -5285,21 +5285,75 @@ function formatShellContext(command, output, exitCode) {
5285
5285
  }
5286
5286
 
5287
5287
  //#endregion
5288
- //#region src/chat/sandbox/client.ts
5289
- /**
5290
- * Client for the api's `/api/v1/sandbox/stream` WebSocket: relays a `/sandbox`
5291
- * session into the agent's own sandbox — a live interactive PTY or a one-shot
5292
- * streamed exec. Mirrors the transport shape of `portal/client.ts`, but dials a
5293
- * different endpoint and speaks the sandbox-stream frame protocol (see the
5294
- * server's sandbox-stream-ws.ts).
5295
- */
5296
- const T = {
5288
+ //#region ../sandbox-stream-protocol/src/index.ts
5289
+ const SANDBOX_STREAM_PATH = "/api/v1/sandbox/stream";
5290
+ const FRAME = {
5297
5291
  DATA: 1,
5298
5292
  EXIT: 2,
5299
5293
  ERROR: 3,
5300
5294
  INPUT: 16,
5301
5295
  RESIZE: 17
5302
5296
  };
5297
+ const MAX_INPUT_BYTES = 1 * 1024 * 1024;
5298
+ /** Query params for the upgrade URL, from a spec. Inverse of {@link parseStreamSpec}. */
5299
+ function streamSpecToQuery(spec) {
5300
+ if (spec.mode === "pty") return {
5301
+ agentId: spec.agentId,
5302
+ mode: "pty",
5303
+ cols: String(spec.cols),
5304
+ rows: String(spec.rows)
5305
+ };
5306
+ return {
5307
+ agentId: spec.agentId,
5308
+ mode: "exec",
5309
+ command: spec.command
5310
+ };
5311
+ }
5312
+ function withType(type, payload) {
5313
+ const frame = new Uint8Array(1 + payload.length);
5314
+ frame[0] = type;
5315
+ frame.set(payload, 1);
5316
+ return frame;
5317
+ }
5318
+ /** client → server: keystroke bytes for the pty stdin. */
5319
+ function encodeInput(data) {
5320
+ return withType(FRAME.INPUT, data);
5321
+ }
5322
+ /** client → server: the client terminal was resized. */
5323
+ function encodeResize(cols, rows) {
5324
+ const frame = new Uint8Array(5);
5325
+ frame[0] = FRAME.RESIZE;
5326
+ const view = new DataView(frame.buffer);
5327
+ view.setUint16(1, cols & 65535);
5328
+ view.setUint16(3, rows & 65535);
5329
+ return frame;
5330
+ }
5331
+ const view = (frame) => new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
5332
+ /**
5333
+ * Decode a frame the server sent. Returns null for an empty, unknown, or
5334
+ * truncated frame — a peer speaking a newer protocol must not crash us.
5335
+ */
5336
+ function decodeServerFrame(frame) {
5337
+ const payload = frame.subarray(1);
5338
+ switch (frame[0]) {
5339
+ case FRAME.DATA: return {
5340
+ type: "data",
5341
+ payload
5342
+ };
5343
+ case FRAME.EXIT: return {
5344
+ type: "exit",
5345
+ code: payload.length >= 4 ? view(frame).getInt32(1) : 0
5346
+ };
5347
+ case FRAME.ERROR: return {
5348
+ type: "error",
5349
+ message: new TextDecoder().decode(payload)
5350
+ };
5351
+ default: return null;
5352
+ }
5353
+ }
5354
+
5355
+ //#endregion
5356
+ //#region src/chat/sandbox/client.ts
5303
5357
  function wsBase(appUrl) {
5304
5358
  const base = appUrl.replace(/\/+$/, "");
5305
5359
  if (base.startsWith("https://")) return `wss://${base.slice(8)}`;
@@ -5317,26 +5371,25 @@ var SandboxStream = class SandboxStream {
5317
5371
  this.ws = ws;
5318
5372
  ws.on("message", (data, isBinary) => {
5319
5373
  if (!isBinary) return;
5320
- const buf = toBuffer(data);
5321
- if (buf.length === 0) return;
5322
- const body = buf.subarray(1);
5323
- switch (buf[0]) {
5324
- case T.DATA:
5374
+ const frame = decodeServerFrame(toBuffer(data));
5375
+ if (!frame) return;
5376
+ switch (frame.type) {
5377
+ case "data":
5325
5378
  onEvent({
5326
5379
  type: "data",
5327
- bytes: new Uint8Array(body)
5380
+ bytes: new Uint8Array(frame.payload)
5328
5381
  });
5329
5382
  break;
5330
- case T.EXIT:
5383
+ case "exit":
5331
5384
  onEvent({
5332
5385
  type: "exit",
5333
- code: body.length >= 4 ? body.readInt32BE(0) : 0
5386
+ code: frame.code
5334
5387
  });
5335
5388
  break;
5336
- case T.ERROR:
5389
+ case "error":
5337
5390
  onEvent({
5338
5391
  type: "error",
5339
- message: body.toString("utf8")
5392
+ message: frame.message
5340
5393
  });
5341
5394
  break;
5342
5395
  }
@@ -5350,16 +5403,12 @@ var SandboxStream = class SandboxStream {
5350
5403
  /** Feed keystroke bytes to the pty stdin. */
5351
5404
  sendInput(data) {
5352
5405
  if (this.closed || this.ws.readyState !== WebSocket.OPEN) return;
5353
- this.ws.send(Buffer.concat([Buffer.from([T.INPUT]), Buffer.from(data)]));
5406
+ this.ws.send(encodeInput(data));
5354
5407
  }
5355
5408
  /** Notify the pty of a terminal resize. */
5356
5409
  resize(cols, rows) {
5357
5410
  if (this.closed || this.ws.readyState !== WebSocket.OPEN) return;
5358
- const b = Buffer.allocUnsafe(5);
5359
- b[0] = T.RESIZE;
5360
- b.writeUInt16BE(cols & 65535, 1);
5361
- b.writeUInt16BE(rows & 65535, 3);
5362
- this.ws.send(b);
5411
+ this.ws.send(encodeResize(cols, rows));
5363
5412
  }
5364
5413
  close() {
5365
5414
  this.closed = true;
@@ -5367,13 +5416,18 @@ var SandboxStream = class SandboxStream {
5367
5416
  }
5368
5417
  /** Open a stream. `mode` is 'pty' (interactive) or 'exec' (one-shot). */
5369
5418
  static open(opts) {
5370
- const url = new URL(`${wsBase(opts.appUrl)}/api/v1/sandbox/stream`);
5371
- url.searchParams.set("agentId", opts.agentId);
5372
- url.searchParams.set("mode", opts.mode);
5373
- if (opts.mode === "pty") {
5374
- url.searchParams.set("cols", String(opts.cols));
5375
- url.searchParams.set("rows", String(opts.rows));
5376
- } else url.searchParams.set("command", opts.command);
5419
+ const spec = opts.mode === "pty" ? {
5420
+ mode: "pty",
5421
+ agentId: opts.agentId,
5422
+ cols: opts.cols,
5423
+ rows: opts.rows
5424
+ } : {
5425
+ mode: "exec",
5426
+ agentId: opts.agentId,
5427
+ command: opts.command
5428
+ };
5429
+ const url = new URL(`${wsBase(opts.appUrl)}${SANDBOX_STREAM_PATH}`);
5430
+ for (const [key, value] of Object.entries(streamSpecToQuery(spec))) url.searchParams.set(key, value);
5377
5431
  return new SandboxStream(new WebSocket(url.toString(), { headers: { authorization: `Bearer ${opts.sessionToken}` } }), opts.onEvent);
5378
5432
  }
5379
5433
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.1.0-beta.342",
3
+ "version": "0.1.0-beta.351",
4
4
  "description": "Skydive CLI — manage AI agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",
@@ -46,6 +46,7 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@createinc/anyone-portal-protocol": "0.0.0",
49
+ "@createinc/anyone-sandbox-stream-protocol": "0.0.0",
49
50
  "@createinc/tsconfig": "0.0.0",
50
51
  "@types/bun": "^1.3.14",
51
52
  "@types/node": "^24.0.0",