@clawos-dev/clawd 0.2.602 → 0.2.603

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/cli.cjs +297 -125
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -64276,6 +64276,8 @@ var GATEWAY_ROUTES = {
64276
64276
  session: (personaId2) => `/p/${encodeURIComponent(personaId2)}/session`,
64277
64277
  sessionSend: (personaId2, sessionId) => `/p/${encodeURIComponent(personaId2)}/session/${encodeURIComponent(sessionId)}`,
64278
64278
  sessionMirror: (personaId2, sessionId) => `/p/${encodeURIComponent(personaId2)}/session/${encodeURIComponent(sessionId)}/mirror`,
64279
+ eventSession: (personaId2) => `/p/${encodeURIComponent(personaId2)}/session/events`,
64280
+ eventSend: (personaId2, sessionId) => `/p/${encodeURIComponent(personaId2)}/session/events/${encodeURIComponent(sessionId)}`,
64279
64281
  // 调用方自己那块盘的用量(design 2026-09-19-cloud-volume-usage §3.3):同样按人、不挂 persona
64280
64282
  volume: () => "/me/volume",
64281
64283
  // 云 persona 运行看板的数据(design 2026-09-23-cloud-persona-metrics §3.6):只给发布者与管理员(gateway 问中心,design 2026-09-24-cloud-persona-conversations §3.2)
@@ -64396,6 +64398,22 @@ var CloudGatewayClient = class {
64396
64398
  logger: this.deps.logger
64397
64399
  });
64398
64400
  }
64401
+ async openEventSession(personaId2, opts) {
64402
+ const { apiKey, gatewayUrl } = await this.credentials();
64403
+ return openHttpEventSession({
64404
+ fetchImpl: this.fetchImpl,
64405
+ gatewayUrl,
64406
+ apiKey,
64407
+ personaId: personaId2,
64408
+ engineSessionId: opts.toolSessionId,
64409
+ instructions: opts.instructions,
64410
+ purpose: opts.purpose,
64411
+ expectedEngine: opts.expectedEngine,
64412
+ effort: opts.effort,
64413
+ personaMode: opts.personaMode,
64414
+ logger: this.deps.logger
64415
+ });
64416
+ }
64399
64417
  /**
64400
64418
  * 看板数据(design 2026-09-23-cloud-persona-metrics §3.8):GET /metrics/<persona>?window&entry,**原样回**状态码与 body——
64401
64419
  * 形状真源在 gateway 的 cloud-persona-metrics,daemon 只是替浏览器拿本机 api key 转一跳。拿不到 key / 连不上 / 超时 → 抛(路由折成 502)。
@@ -64433,6 +64451,149 @@ var CloudGatewayClient = class {
64433
64451
  return CloudVolumeUsageSchema.parse(body.volume);
64434
64452
  }
64435
64453
  };
64454
+ function openHttpEventSession(opts) {
64455
+ const { fetchImpl, gatewayUrl, apiKey, personaId: personaId2, engineSessionId, instructions, purpose, effort, personaMode, expectedEngine, logger } = opts;
64456
+ const headers = { authorization: `Bearer ${apiKey}`, "content-type": "application/json" };
64457
+ const abort = new AbortController();
64458
+ const eventCbs = [];
64459
+ const controlCbs = [];
64460
+ const closeCbs = [];
64461
+ const earlyEvents = [];
64462
+ const earlyControls = [];
64463
+ let closed = false;
64464
+ let readyControl = null;
64465
+ let errorControl = null;
64466
+ let sessionId = null;
64467
+ let resolveReady;
64468
+ let rejectReady;
64469
+ const ready = new Promise((resolve9, reject) => {
64470
+ resolveReady = resolve9;
64471
+ rejectReady = reject;
64472
+ });
64473
+ void ready.catch(() => {
64474
+ });
64475
+ let sendChain = Promise.resolve();
64476
+ const emitControl = (control) => {
64477
+ if (control.kind === "ready") readyControl = control;
64478
+ if (control.kind === "error") errorControl = control;
64479
+ if (!controlCbs.length) earlyControls.push(control);
64480
+ for (const cb of controlCbs) cb(control);
64481
+ };
64482
+ const finish = (message) => {
64483
+ if (closed) return;
64484
+ if (message) emitControl({ kind: "error", message });
64485
+ closed = true;
64486
+ rejectReady(new Error(message ?? "event session closed"));
64487
+ abort.abort();
64488
+ for (const cb of closeCbs) cb();
64489
+ };
64490
+ void (async () => {
64491
+ try {
64492
+ const res = await fetchImpl(gatewayUrl + GATEWAY_ROUTES.eventSession(personaId2), {
64493
+ method: "POST",
64494
+ headers,
64495
+ body: JSON.stringify({ expectedEngine, ...engineSessionId ? { engineSessionId } : {}, ...instructions ? { instructions } : {}, ...purpose ? { purpose } : {}, ...effort ? { effort } : {}, ...personaMode ? { personaMode } : {} }),
64496
+ signal: abort.signal
64497
+ });
64498
+ if (!res.ok || !res.body) {
64499
+ let message = `gateway \u56DE ${res.status}`;
64500
+ try {
64501
+ const body = await res.json();
64502
+ if (typeof body.message === "string") message = body.message;
64503
+ if (body.error === "SESSION_VERSION_EXPIRED" && !message.includes("SESSION_VERSION_EXPIRED")) message = `SESSION_VERSION_EXPIRED: ${message}`;
64504
+ } catch {
64505
+ }
64506
+ finish(message);
64507
+ return;
64508
+ }
64509
+ const reader = res.body.getReader();
64510
+ const decoder = new TextDecoder();
64511
+ let pending = "";
64512
+ for (; ; ) {
64513
+ const { value, done } = await reader.read();
64514
+ if (done) break;
64515
+ pending += decoder.decode(value, { stream: true });
64516
+ const lines = pending.split("\n");
64517
+ pending = lines.pop() ?? "";
64518
+ for (const line of lines) {
64519
+ if (!line.trim()) continue;
64520
+ const data = JSON.parse(line);
64521
+ if (data.clawd === "events") {
64522
+ const events = ParsedEventSchema.array().parse(data.events);
64523
+ if (!eventCbs.length) earlyEvents.push(events);
64524
+ for (const cb of eventCbs) cb(events);
64525
+ continue;
64526
+ }
64527
+ const control = parseControlLine(line);
64528
+ if (!control) throw new Error("gateway event stream contains an unknown line");
64529
+ if (control.kind === "ready") {
64530
+ if (!control.session) throw new Error("gateway ready line has no session id");
64531
+ if (control.engine !== expectedEngine) {
64532
+ finish(control.engine ? `SESSION_VERSION_EXPIRED: \u5F53\u524D Pod \u662F ${control.engine}\uFF0C\u8FD9\u6761\u4F1A\u8BDD\u9700\u8981 ${expectedEngine}` : "gateway \u7684 ready \u63A7\u5236\u884C\u6CA1\u5E26 engine\uFF0Cgateway \u592A\u8001\uFF0C\u5148\u5347 gateway");
64533
+ return;
64534
+ }
64535
+ sessionId = control.session;
64536
+ resolveReady();
64537
+ }
64538
+ emitControl(control);
64539
+ if (control.kind === "error") {
64540
+ finish();
64541
+ return;
64542
+ }
64543
+ }
64544
+ }
64545
+ finish();
64546
+ } catch (e) {
64547
+ if (!closed) {
64548
+ logger?.warn("cloud event stream failed", { message: e.message });
64549
+ finish(`\u4E91\u4E0A\u4E8B\u4EF6\u901A\u9053\u51FA\u9519\uFF1A${e.message}`);
64550
+ }
64551
+ }
64552
+ })();
64553
+ return {
64554
+ send(command) {
64555
+ const next = sendChain.then(async () => {
64556
+ await ready;
64557
+ if (closed || !sessionId) throw new Error("event session closed");
64558
+ const res = await fetchImpl(gatewayUrl + GATEWAY_ROUTES.eventSend(personaId2, sessionId), { method: "POST", headers, body: JSON.stringify(command) });
64559
+ if (!res.ok) {
64560
+ let message = `gateway \u56DE ${res.status}`;
64561
+ try {
64562
+ const body = await res.json();
64563
+ if (typeof body.message === "string") message = body.message;
64564
+ } catch {
64565
+ }
64566
+ finish(message);
64567
+ throw new Error(message);
64568
+ }
64569
+ });
64570
+ sendChain = next.catch(() => {
64571
+ });
64572
+ return next;
64573
+ },
64574
+ onEvents(cb) {
64575
+ const first = !eventCbs.length;
64576
+ eventCbs.push(cb);
64577
+ if (first) for (const events of earlyEvents.splice(0)) cb(events);
64578
+ },
64579
+ onControl(cb) {
64580
+ const first = !controlCbs.length;
64581
+ controlCbs.push(cb);
64582
+ if (first) for (const control of earlyControls.splice(0)) cb(control);
64583
+ else {
64584
+ if (readyControl) cb(readyControl);
64585
+ if (errorControl) cb(errorControl);
64586
+ }
64587
+ },
64588
+ onClose(cb) {
64589
+ closeCbs.push(cb);
64590
+ if (closed) cb();
64591
+ },
64592
+ close() {
64593
+ finish();
64594
+ }
64595
+ };
64596
+ }
64436
64597
  function parseControlLine(line) {
64437
64598
  let o2;
64438
64599
  try {
@@ -64653,54 +64814,26 @@ init_protocol();
64653
64814
 
64654
64815
  // src/tools/codex-cloud-session.ts
64655
64816
  var import_node_fs38 = __toESM(require("fs"), 1);
64656
- function cloudThreadParams(ctx, paths) {
64657
- const { model: _model, ...base } = threadStartParams(ctx);
64658
- const developerInstructions = ctx.titleOnly ? "\u53EA\u6839\u636E\u7528\u6237\u6D88\u606F\u8FD4\u56DE\u7B80\u77ED\u6807\u9898\u3002\u4E0D\u8981\u8C03\u7528\u5DE5\u5177\u3001\u8BFB\u53D6\u6587\u4EF6\u3001\u8BBF\u95EE\u7F51\u7EDC\u6216\u4FEE\u6539\u4EFB\u4F55\u5185\u5BB9\u3002" : [ctx.extraSystemPrompt, workDirPrompt(paths.workDir, ctx.personaMode ?? "owner")].filter((x2) => Boolean(x2)).join("\n\n");
64659
- return { ...base, ...paths.model ? { model: paths.model } : {}, cwd: paths.personaDir, approvalPolicy: "never", sandbox: ctx.titleOnly ? "read-only" : "danger-full-access", developerInstructions };
64660
- }
64661
- function deferredLineStream() {
64662
- let target = null;
64663
- let closed = false;
64664
- const pending = [];
64665
- const lineCbs = [];
64666
- const closeCbs = [];
64667
- const finish = () => {
64668
- if (closed) return;
64669
- closed = true;
64670
- for (const cb of closeCbs) cb();
64671
- };
64672
- return {
64673
- attach(t2) {
64674
- if (closed) {
64675
- t2.close();
64676
- return;
64677
- }
64678
- target = t2;
64679
- t2.onLine((l2) => {
64680
- for (const cb of lineCbs) cb(l2);
64681
- });
64682
- t2.onClose(finish);
64683
- for (const line of pending.splice(0)) t2.write(line);
64684
- },
64685
- write(line) {
64686
- if (closed) return;
64687
- if (target) target.write(line);
64688
- else pending.push(line);
64689
- },
64690
- onLine(cb) {
64691
- lineCbs.push(cb);
64692
- },
64693
- onClose(cb) {
64694
- closeCbs.push(cb);
64695
- if (closed) cb();
64696
- },
64697
- close() {
64698
- if (closed) return;
64699
- target?.close();
64700
- finish();
64701
- }
64702
- };
64817
+
64818
+ // src/tools/cloud-portable-input.ts
64819
+ async function portableCloudInput(text, resolvers) {
64820
+ const markers = [...text.matchAll(/\[attachment:image:(https?:\/\/[^\]]+)\]/g)];
64821
+ let result = text;
64822
+ for (const match of markers) {
64823
+ const url = match[1];
64824
+ let dataUrl;
64825
+ const codex = resolvers.resolveCodexImage?.(url);
64826
+ if (codex?.type === "image" && codex.url.startsWith("data:")) dataUrl = codex.url;
64827
+ if (!dataUrl && resolvers.fetchPiImage) {
64828
+ const pi = await resolvers.fetchPiImage(url).catch(() => null);
64829
+ if (pi) dataUrl = `data:${pi.mimeType};base64,${pi.data}`;
64830
+ }
64831
+ if (dataUrl) result = result.replace(match[0], `[attachment:image:${dataUrl}]`);
64832
+ }
64833
+ return result;
64703
64834
  }
64835
+
64836
+ // src/tools/codex-cloud-session.ts
64704
64837
  function cloudImageInput(deps) {
64705
64838
  return (url) => {
64706
64839
  const secret = deps.getSignSecret?.();
@@ -64719,73 +64852,84 @@ function cloudImageInput(deps) {
64719
64852
  }
64720
64853
  function createCloudCodexSession(ctx, sink, deps) {
64721
64854
  const personaId2 = personaIdFromCloudCwd(ctx.cwd);
64722
- const stream = deferredLineStream();
64723
- let resolvePaths;
64724
- let rejectPaths;
64725
- const paths = new Promise((resolve9, reject) => {
64726
- resolvePaths = resolve9;
64727
- rejectPaths = reject;
64728
- });
64729
- paths.catch(() => {
64730
- });
64731
- stream.onClose(() => rejectPaths(new Error("\u4E91\u4E0A\u901A\u9053\u5728 ready \u4E4B\u524D\u5173\u4E86\uFF0C\u62FF\u4E0D\u5230 Pod \u8DEF\u5F84")));
64732
64855
  let channel = null;
64733
- const driver = driveCodexThread({
64734
- ctx,
64735
- sink,
64736
- stream,
64737
- logger: deps.logger,
64738
- threadParams: async (client) => {
64739
- const p3 = await paths;
64740
- const params = cloudThreadParams(ctx, p3);
64741
- if (!ctx.toolSessionId || p3.model) return params;
64742
- const config = await client.request("config/read", {});
64743
- if (config?.config?.model) return { ...params, model: config.config.model };
64744
- const list = await client.request("model/list", {});
64745
- const selected = list?.data?.find((m3) => m3.isDefault);
64746
- const current = selected?.id || selected?.model;
64747
- if (!current) throw new Error("\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D Codex \u9ED8\u8BA4\u6A21\u578B\uFF0C\u62D2\u7EDD\u6CBF\u7528\u65E7\u4F1A\u8BDD\u6A21\u578B");
64748
- return { ...params, model: current };
64749
- },
64750
- imageInput: cloudImageInput(deps),
64751
- // skills 在 Pod 里由 persona 目录自带,本机路径对它没意义
64752
- // 「让对端走人」= 断开下行流,gateway 收到就删 Job;信号对 Pod 没有意义
64753
- kill: () => stream.close(),
64754
- // 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
64755
- onTurnStarting: () => channel?.markNextLineTurnStart(),
64756
- // 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
64757
- onTurnCompleted: (outcome, afterFirstByteMs) => {
64758
- if (!ctx.titleOnly) void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} });
64759
- },
64760
- now: deps.now
64761
- });
64856
+ let closed = false;
64857
+ let exited = false;
64858
+ const exit = () => {
64859
+ if (!exited) {
64860
+ exited = true;
64861
+ sink.onExit(null);
64862
+ }
64863
+ };
64762
64864
  if (!personaId2) {
64763
64865
  queueMicrotask(() => {
64764
64866
  sink.onError(`cloud session has no persona id (cwd=${ctx.cwd})`);
64765
- stream.close();
64867
+ exit();
64766
64868
  });
64767
- return driver.session;
64768
- }
64769
- void deps.client.openSession(personaId2, { expectedEngine: "codex", ...ctx.titleOnly ? { purpose: "title" } : {} }).then(
64770
- (opened) => {
64771
- channel = opened;
64869
+ return { startTurn() {
64870
+ }, respondPermission() {
64871
+ }, interrupt() {
64872
+ }, async stop() {
64873
+ } };
64874
+ }
64875
+ const opened = deps.client.openEventSession(personaId2, {
64876
+ expectedEngine: "codex",
64877
+ ...ctx.toolSessionId ? { toolSessionId: ctx.toolSessionId } : {},
64878
+ ...ctx.extraSystemPrompt ? { instructions: ctx.extraSystemPrompt } : {},
64879
+ ...ctx.effort ? { effort: ctx.effort } : {},
64880
+ ...ctx.personaMode ? { personaMode: ctx.personaMode } : {},
64881
+ ...ctx.titleOnly ? { purpose: "title" } : {}
64882
+ }).then(
64883
+ (opened2) => {
64884
+ channel = opened2;
64885
+ channel.onEvents((events) => sink.pushEvents(events));
64772
64886
  channel.onControl((c2) => {
64773
- if (c2.kind === "ready") {
64774
- resolvePaths({ personaDir: c2.personaDir, workDir: c2.workDir, model: c2.model });
64775
- } else if (c2.kind === "queued") {
64887
+ if (c2.kind === "queued") {
64776
64888
  sink.pushEvents([{ kind: "meta-text", text: `\u4E91\u4E0A\u673A\u5668\u6709 ${c2.running} \u4E2A\u4EFB\u52A1\u5728\u8DD1\uFF0C\u7B49\u4E00\u4E2A\u7ED3\u675F\u5C31\u8F6E\u5230\u4F60` }]);
64777
64889
  } else if (c2.kind === "error") {
64778
64890
  sink.onError(`\u4E91\u4E0A persona \u8D77\u4E0D\u6765\uFF1A${c2.message}`);
64779
64891
  }
64780
64892
  });
64781
- stream.attach(channel);
64893
+ channel.onClose(exit);
64894
+ if (closed) channel.close();
64895
+ return channel;
64782
64896
  },
64783
64897
  (e) => {
64784
64898
  sink.onError(e instanceof CloudApiKeyRequiredError ? e.message : `\u8FDE\u4E0D\u4E0A\u4E91 gateway\uFF1A${e.message}`);
64785
- stream.close();
64899
+ exit();
64900
+ throw e;
64786
64901
  }
64787
64902
  );
64788
- return driver.session;
64903
+ void opened.catch(() => {
64904
+ });
64905
+ let sendChain = Promise.resolve();
64906
+ const send2 = (command) => {
64907
+ sendChain = sendChain.then(command).catch((e) => {
64908
+ if (!closed && !exited) sink.onError(`\u4E91\u4E0A\u4F1A\u8BDD\u4E0A\u884C\u5931\u8D25\uFF1A${e.message}`);
64909
+ });
64910
+ };
64911
+ return {
64912
+ startTurn(text) {
64913
+ send2(async () => {
64914
+ const ch = await opened;
64915
+ if (closed) return;
64916
+ const portable = await portableCloudInput(text, { resolveCodexImage: cloudImageInput(deps) });
64917
+ await ch.send({ command: "turn", text: portable });
64918
+ });
64919
+ },
64920
+ respondPermission(requestId, allow) {
64921
+ send2(async () => (await opened).send({ command: "permission", requestId, allow }));
64922
+ },
64923
+ interrupt() {
64924
+ send2(async () => (await opened).send({ command: "interrupt" }));
64925
+ },
64926
+ async stop() {
64927
+ closed = true;
64928
+ const ch = await opened.catch(() => null);
64929
+ ch?.close();
64930
+ exit();
64931
+ }
64932
+ };
64789
64933
  }
64790
64934
 
64791
64935
  // src/tools/codex-cloud.ts
@@ -65473,34 +65617,30 @@ function cloudPiInstructions(ctx) {
65473
65617
  function createCloudPiSession(ctx, sink, deps) {
65474
65618
  const personaId2 = personaIdFromCloudCwd(ctx.cwd);
65475
65619
  const sessionId = ctx.toolSessionId ?? (0, import_node_crypto13.randomUUID)();
65476
- const stream = deferredLineStream();
65477
65620
  let channel = null;
65478
- const driver = drivePiSession({
65479
- sink,
65480
- stream,
65481
- sessionId,
65482
- logger: deps.logger,
65483
- fetchImage: deps.fetchImage ?? makeImageFetcher(),
65484
- // 「让对端走人」= 断开下行流,gateway 收到就删 Pod;信号对 Pod 没有意义
65485
- kill: () => stream.close(),
65486
- // 云 persona 运行看板(design 2026-09-23-cloud-persona-metrics §3.7):提问那一行带「一轮开始」头,轮末镜像顺带报这一轮的结果
65487
- onTurnStarting: () => channel?.markNextLineTurnStart(),
65488
- // 轮末镜像带上这一轮的结果与首字节(design 2026-09-23-cloud-persona-metrics §3.7)
65489
- onTurnCompleted: (outcome, afterFirstByteMs) => {
65490
- if (!ctx.titleOnly) void channel?.mirror({ outcome, ...afterFirstByteMs !== void 0 ? { afterFirstByteMs } : {} });
65491
- },
65492
- now: deps.now
65493
- });
65621
+ let closed = false;
65622
+ let exited = false;
65623
+ const exit = () => {
65624
+ if (!exited) {
65625
+ exited = true;
65626
+ sink.onExit(null);
65627
+ }
65628
+ };
65494
65629
  if (!personaId2) {
65495
65630
  queueMicrotask(() => {
65496
65631
  sink.onError(`cloud session has no persona id (cwd=${ctx.cwd})`);
65497
- stream.close();
65632
+ exit();
65498
65633
  });
65499
- return driver.session;
65500
- }
65501
- void deps.client.openSession(personaId2, { toolSessionId: sessionId, instructions: ctx.titleOnly ? "" : cloudPiInstructions(ctx), expectedEngine: "pi", ...ctx.titleOnly ? { purpose: "title" } : {} }).then(
65502
- (opened) => {
65503
- channel = opened;
65634
+ return { startTurn() {
65635
+ }, respondPermission() {
65636
+ }, interrupt() {
65637
+ }, async stop() {
65638
+ } };
65639
+ }
65640
+ const opened = deps.client.openEventSession(personaId2, { expectedEngine: "pi", toolSessionId: sessionId, instructions: ctx.titleOnly ? "" : cloudPiInstructions(ctx), ...ctx.titleOnly ? { purpose: "title" } : {} }).then(
65641
+ (opened2) => {
65642
+ channel = opened2;
65643
+ channel.onEvents((events) => sink.pushEvents(events));
65504
65644
  channel.onControl((c2) => {
65505
65645
  if (c2.kind === "queued") {
65506
65646
  sink.pushEvents([{ kind: "meta-text", text: `\u4E91\u4E0A\u673A\u5668\u6709 ${c2.running} \u4E2A\u4EFB\u52A1\u5728\u8DD1\uFF0C\u7B49\u4E00\u4E2A\u7ED3\u675F\u5C31\u8F6E\u5230\u4F60` }]);
@@ -65508,14 +65648,46 @@ function createCloudPiSession(ctx, sink, deps) {
65508
65648
  sink.onError(`\u4E91\u4E0A persona \u8D77\u4E0D\u6765\uFF1A${c2.message}`);
65509
65649
  }
65510
65650
  });
65511
- stream.attach(channel);
65651
+ channel.onClose(exit);
65652
+ if (closed) channel.close();
65653
+ return channel;
65512
65654
  },
65513
65655
  (e) => {
65514
65656
  sink.onError(e instanceof CloudApiKeyRequiredError ? e.message : `\u8FDE\u4E0D\u4E0A\u4E91 gateway\uFF1A${e.message}`);
65515
- stream.close();
65657
+ exit();
65658
+ throw e;
65516
65659
  }
65517
65660
  );
65518
- return driver.session;
65661
+ void opened.catch(() => {
65662
+ });
65663
+ let sendChain = Promise.resolve();
65664
+ const send2 = (command) => {
65665
+ sendChain = sendChain.then(command).catch((e) => {
65666
+ if (!closed && !exited) sink.onError(`\u4E91\u4E0A\u4F1A\u8BDD\u4E0A\u884C\u5931\u8D25\uFF1A${e.message}`);
65667
+ });
65668
+ };
65669
+ return {
65670
+ startTurn(text) {
65671
+ send2(async () => {
65672
+ const ch = await opened;
65673
+ if (closed) return;
65674
+ const portable = await portableCloudInput(text, { fetchPiImage: deps.fetchImage ?? makeImageFetcher() });
65675
+ await ch.send({ command: "turn", text: portable });
65676
+ });
65677
+ },
65678
+ respondPermission(requestId, allow) {
65679
+ send2(async () => (await opened).send({ command: "permission", requestId, allow }));
65680
+ },
65681
+ interrupt() {
65682
+ send2(async () => (await opened).send({ command: "interrupt" }));
65683
+ },
65684
+ async stop() {
65685
+ closed = true;
65686
+ const ch = await opened.catch(() => null);
65687
+ ch?.close();
65688
+ exit();
65689
+ }
65690
+ };
65519
65691
  }
65520
65692
 
65521
65693
  // src/tools/pi-cloud.ts
@@ -73092,7 +73264,7 @@ function computeMethodAccess(args) {
73092
73264
  }
73093
73265
 
73094
73266
  // src/version.ts
73095
- var version = "0.2.602".length > 0 ? "0.2.602" : "dev";
73267
+ var version = "0.2.603".length > 0 ? "0.2.603" : "dev";
73096
73268
 
73097
73269
  // src/cli-probe/probe.ts
73098
73270
  var fs83 = __toESM(require("fs"), 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.602",
3
+ "version": "0.2.603",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",