skydive-cli 0.1.0-beta.347 → 0.1.0-beta.353

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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.347";
19
+ var version$1 = "0.1.0-beta.353";
20
20
 
21
21
  //#endregion
22
22
  //#region src/types.ts
@@ -1033,15 +1033,6 @@ function createRestClient({ appUrl, sessionToken }) {
1033
1033
  ...input,
1034
1034
  clientSurface
1035
1035
  }, sendResultSchema),
1036
- activeRun: async ({ conversationId }) => {
1037
- const { run } = await get(`/api/v1/chat/active-run?${new URLSearchParams({ conversationId }).toString()}`, activeRunResponseSchema);
1038
- if (!run) return null;
1039
- return {
1040
- runId: run.runId,
1041
- agentId: run.agentId ?? "",
1042
- agentName: run.agentName ?? ""
1043
- };
1044
- },
1045
1036
  cancelRun: async ({ runId }) => {
1046
1037
  await post(`/api/v1/chat/runs/${encodeURIComponent(runId)}/cancel`, {}, z.object({ ok: z.boolean() }));
1047
1038
  },
@@ -1236,11 +1227,6 @@ const finalizeResponseSchema = z.object({
1236
1227
  mediaType: z.string(),
1237
1228
  sizeBytes: z.number().nullable().optional()
1238
1229
  });
1239
- const activeRunResponseSchema = z.object({ run: z.object({
1240
- runId: z.string(),
1241
- agentId: z.string().nullish(),
1242
- agentName: z.string().nullish()
1243
- }).nullable() });
1244
1230
  const oauthConnectResponseSchema = z.object({ connectLink: z.string() }).passthrough();
1245
1231
  const externalOauthConnectResponseSchema = z.object({ authorizationUrl: z.string().optional() }).passthrough();
1246
1232
  const runStreamEventSchema = z.union([z.object({
@@ -1252,11 +1238,14 @@ const runStreamEventSchema = z.union([z.object({
1252
1238
  error: z.string().nullish()
1253
1239
  })]);
1254
1240
  const streamErrorSchema = z.object({ error: z.string() });
1255
- const conversationStreamEventSchema = z.object({
1241
+ const conversationStreamEventSchema = z.discriminatedUnion("kind", [z.object({
1256
1242
  kind: z.literal("conversation"),
1257
1243
  id: z.string(),
1258
1244
  title: z.string().nullable()
1259
- });
1245
+ }), z.object({
1246
+ kind: z.literal("run"),
1247
+ runId: z.string()
1248
+ })]);
1260
1249
 
1261
1250
  //#endregion
1262
1251
  //#region src/commands/session.ts
@@ -2622,7 +2611,7 @@ const chatCommand = {
2622
2611
  printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
2623
2612
  process.exit(1);
2624
2613
  }
2625
- const { runChat } = await import("./boot-BcuOicbb.mjs");
2614
+ const { runChat } = await import("./boot-DLHvjtxn.mjs");
2626
2615
  await runChat({
2627
2616
  appUrl,
2628
2617
  sessionToken: session.value.sessionToken,
@@ -3416,7 +3405,7 @@ const switchCommand = {
3416
3405
  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
3406
  process.exit(1);
3418
3407
  }
3419
- const { runWorkspacePicker } = await import("./boot-BcuOicbb.mjs");
3408
+ const { runWorkspacePicker } = await import("./boot-DLHvjtxn.mjs");
3420
3409
  await runWorkspacePicker(session);
3421
3410
  return;
3422
3411
  }
@@ -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
  };
@@ -5594,6 +5648,7 @@ function ChatScreen({ agent, conversation }) {
5594
5648
  inputRef.current = input;
5595
5649
  const credPromptRef = useRef(credPrompt);
5596
5650
  credPromptRef.current = credPrompt;
5651
+ const seenRunsRef = useRef(/* @__PURE__ */ new Set());
5597
5652
  const shellSessionRef = useRef(null);
5598
5653
  const hostState = grantPrompt || credPrompt ? "blocked" : run.kind === "idle" ? "idle" : "working";
5599
5654
  useEffect(() => {
@@ -5667,6 +5722,7 @@ function ChatScreen({ agent, conversation }) {
5667
5722
  }, [rest, initialConversationId]);
5668
5723
  const attachToRun = useCallback((runId, runAgentName) => {
5669
5724
  if (!rest) return;
5725
+ seenRunsRef.current.add(runId);
5670
5726
  const abort = new AbortController();
5671
5727
  const responsePreview = createResponsePreview();
5672
5728
  const streamAgentName = runAgentName ?? agent.name;
@@ -5727,6 +5783,12 @@ function ChatScreen({ agent, conversation }) {
5727
5783
  conversationId,
5728
5784
  signal: abort.signal,
5729
5785
  onEvent: (event) => {
5786
+ if (event.kind === "run") {
5787
+ if (seenRunsRef.current.has(event.runId)) return;
5788
+ if (runRef.current.kind !== "idle") return;
5789
+ attachToRun(event.runId);
5790
+ return;
5791
+ }
5730
5792
  if (event.title) setChatTitle(event.title);
5731
5793
  }
5732
5794
  }).catch(() => {});
@@ -5734,22 +5796,9 @@ function ChatScreen({ agent, conversation }) {
5734
5796
  }, [
5735
5797
  rest,
5736
5798
  conversationId,
5737
- setChatTitle
5799
+ setChatTitle,
5800
+ attachToRun
5738
5801
  ]);
5739
- useEffect(() => {
5740
- if (!rest || !conversationId) return;
5741
- let cancelled = false;
5742
- (async () => {
5743
- try {
5744
- const active = await rest.activeRun({ conversationId });
5745
- if (cancelled || !active) return;
5746
- attachToRun(active.runId, active.agentName || null);
5747
- } catch (_error) {}
5748
- })();
5749
- return () => {
5750
- cancelled = true;
5751
- };
5752
- }, []);
5753
5802
  const sendContent = useCallback(async (content, staged, opts) => {
5754
5803
  if (!rest) return;
5755
5804
  const trimmed = content.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.1.0-beta.347",
3
+ "version": "0.1.0-beta.353",
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",