opencami 1.8.2 → 1.8.3

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
@@ -15,16 +15,28 @@ A beautiful web client for [OpenClaw](https://github.com/openclaw/openclaw).
15
15
  curl -fsSL https://opencami.xyz/install.sh | bash
16
16
  ```
17
17
 
18
- Or via npm:
18
+ Then open `http://localhost:3000` (or your configured host/port).
19
+
20
+ ---
21
+
22
+ ## Install (curl)
23
+
24
+ The recommended install flow is:
25
+
26
+ ```bash
27
+ curl -fsSL https://opencami.xyz/install.sh | bash
28
+ ```
29
+
30
+ This installs OpenCami and prints next-step instructions for required environment variables.
31
+
32
+ Alternative install:
19
33
 
20
34
  ```bash
21
35
  npm install -g opencami
22
36
  opencami
23
37
  ```
24
38
 
25
- Opens your browser to the chat interface.
26
-
27
- ### Options
39
+ CLI options:
28
40
 
29
41
  | Flag | Description | Default |
30
42
  |------|-------------|---------|
@@ -33,13 +45,159 @@ Opens your browser to the chat interface.
33
45
  | `--host` | Bind address | `localhost` |
34
46
  | `--no-open` | Don't open browser | — |
35
47
 
36
- ### Docker
48
+ > Note: `--gateway` sets `OPENCLAW_GATEWAY` internally. For predictable deployments, set `CLAWDBOT_GATEWAY_URL` explicitly in environment.
49
+
50
+ ---
51
+
52
+ ## Configuration (OpenCami environment)
53
+
54
+ Set these in your shell, service manager, or container environment.
55
+
56
+ ### Required
57
+
58
+ ```bash
59
+ CLAWDBOT_GATEWAY_URL=ws://127.0.0.1:18789
60
+ # pick ONE auth method:
61
+ CLAWDBOT_GATEWAY_TOKEN=...
62
+ # or
63
+ CLAWDBOT_GATEWAY_PASSWORD=...
64
+ ```
65
+
66
+ ### Required for remote Tailnet / origin allowlist setups
67
+
68
+ ```bash
69
+ # Must exactly match the browser origin used to access OpenCami
70
+ # Example: https://openclaw-server.tailXXXX.ts.net:3001
71
+ OPENCAMI_ORIGIN=https://openclaw-server.tailXXXX.ts.net:3001
72
+ ```
73
+
74
+ ### Optional
75
+
76
+ ```bash
77
+ # Enables compatibility fallback if strict device-auth connect fails
78
+ # Default is strict mode (fallback disabled)
79
+ OPENCAMI_DEVICE_AUTH_FALLBACK=true
80
+
81
+ FILES_ROOT=/path/to/workspace
82
+ OPENAI_API_KEY=sk-...
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Remote Tailnet setup (OpenClaw + OpenCami)
88
+
89
+ If OpenCami loads remotely but you see **"origin not allowed"** or gateway connect failures, configure both sides.
90
+
91
+ ### 1) OpenClaw gateway config (`gateway.controlUi.allowedOrigins`)
92
+
93
+ In your OpenClaw config, allow the exact OpenCami browser origin:
94
+
95
+ ```json
96
+ {
97
+ "gateway": {
98
+ "controlUi": {
99
+ "allowedOrigins": [
100
+ "https://openclaw-server.tailXXXX.ts.net:3001"
101
+ ]
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### 2) OpenCami server env (`OPENCAMI_ORIGIN`)
108
+
109
+ Set `OPENCAMI_ORIGIN` to the same exact value:
110
+
111
+ ```bash
112
+ OPENCAMI_ORIGIN=https://openclaw-server.tailXXXX.ts.net:3001
113
+ ```
114
+
115
+ ### 3) Restart gateway
116
+
117
+ ```bash
118
+ openclaw gateway restart
119
+ ```
120
+
121
+ > If you access OpenCami via multiple hostnames/ports, each distinct origin must be listed in `allowedOrigins`.
122
+
123
+ ---
124
+
125
+ ## Device auth notes (strict vs fallback)
126
+
127
+ OpenCami uses strict device-auth-compatible connect params by default.
128
+
129
+ - **Strict mode (default):** `OPENCAMI_DEVICE_AUTH_FALLBACK` unset/false
130
+ - **Fallback mode (compatibility):** set `OPENCAMI_DEVICE_AUTH_FALLBACK=true`
131
+
132
+ Fallback mode retries connect without device identity metadata if strict handshake fails.
133
+ Use fallback only when needed for compatibility, and prefer strict mode long-term.
134
+
135
+ ---
136
+
137
+ ## Security notes
138
+
139
+ - Prefer `wss://` for remote connections.
140
+ - Prefer token auth (`CLAWDBOT_GATEWAY_TOKEN`) over password.
141
+ - Keep `allowedOrigins` minimal (exact origins only, no wildcards).
142
+ - Treat `OPENCAMI_DEVICE_AUTH_FALLBACK=true` as temporary compatibility mode.
143
+ - Do **not** expose OpenCami directly to the public internet without TLS + access controls.
144
+ - For Tailnet deployments, limit Tailnet device/user access.
145
+
146
+ ---
147
+
148
+ ## Troubleshooting
149
+
150
+ ### "origin not allowed"
151
+
152
+ Cause: gateway rejected browser origin.
153
+
154
+ Fix:
155
+ 1. Add origin to `gateway.controlUi.allowedOrigins`
156
+ 2. Set identical `OPENCAMI_ORIGIN` in OpenCami env
157
+ 3. Restart gateway (`openclaw gateway restart`)
158
+
159
+ ### Missing scope `operator.read`
160
+
161
+ Cause: gateway auth succeeded but token/permissions did not include required operator scope.
162
+
163
+ Fix:
164
+ - Use a token/password with operator access
165
+ - Verify gateway auth/scopes in OpenClaw
166
+ - Reconnect after updating credentials
167
+
168
+ ### Pairing required / device auth connect issues
169
+
170
+ If strict connect fails in your deployment:
171
+
172
+ ```bash
173
+ OPENCAMI_DEVICE_AUTH_FALLBACK=true
174
+ ```
175
+
176
+ Then restart OpenCami and retry. Keep this as a compatibility fallback, not the default.
177
+
178
+ ### Can’t connect to gateway at all
179
+
180
+ Checks:
181
+
182
+ ```bash
183
+ openclaw gateway status
184
+ echo "$CLAWDBOT_GATEWAY_URL"
185
+ echo "$CLAWDBOT_GATEWAY_TOKEN"
186
+ ```
187
+
188
+ Also verify URL scheme (`ws://` local, `wss://` remote).
189
+
190
+ ---
191
+
192
+ ## Docker
37
193
 
38
194
  ```bash
39
195
  docker build -t opencami .
40
196
  docker run -p 3000:3000 opencami
41
197
  ```
42
198
 
199
+ ---
200
+
43
201
  ## Features
44
202
 
45
203
  ### 💬 Chat & Communication
package/bin/opencami.js CHANGED
@@ -18,6 +18,9 @@ function getArg(name, def) {
18
18
  const port = parseInt(getArg('port', '3000'), 10);
19
19
  const host = getArg('host', '127.0.0.1');
20
20
  const gateway = getArg('gateway', 'ws://127.0.0.1:18789');
21
+ const token = getArg('token', '');
22
+ const password = getArg('password', '');
23
+ const origin = getArg('origin', '');
21
24
  const noOpen = args.includes('--no-open');
22
25
 
23
26
  if (args.includes('--help') || args.includes('-h')) {
@@ -27,17 +30,23 @@ if (args.includes('--help') || args.includes('-h')) {
27
30
  Usage: opencami [options]
28
31
 
29
32
  Options:
30
- --port <n> Port to listen on (default: 3000)
31
- --host <addr> Host to bind to (default: 127.0.0.1)
32
- --gateway <url> OpenClaw gateway URL (default: ws://127.0.0.1:18789)
33
- --no-open Don't open browser on start
34
- -h, --help Show this help
33
+ --port <n> Port to listen on (default: 3000)
34
+ --host <addr> Host to bind to (default: 127.0.0.1)
35
+ --gateway <url> OpenClaw gateway URL (default: ws://127.0.0.1:18789)
36
+ --token <token> Gateway token (sets CLAWDBOT_GATEWAY_TOKEN)
37
+ --password <pw> Gateway password (sets CLAWDBOT_GATEWAY_PASSWORD)
38
+ --origin <url> Origin to send in backend WS (sets OPENCAMI_ORIGIN)
39
+ --no-open Don't open browser on start
40
+ -h, --help Show this help
35
41
  `);
36
42
  process.exit(0);
37
43
  }
38
44
 
39
45
  // Set gateway env for the app
40
- process.env.OPENCLAW_GATEWAY = gateway;
46
+ process.env.CLAWDBOT_GATEWAY_URL = gateway;
47
+ if (token) process.env.CLAWDBOT_GATEWAY_TOKEN = token;
48
+ if (password) process.env.CLAWDBOT_GATEWAY_PASSWORD = password;
49
+ if (origin) process.env.OPENCAMI_ORIGIN = origin;
41
50
 
42
51
  const MIME_TYPES = {
43
52
  '.html': 'text/html',
@@ -19,7 +19,7 @@ import { u as useChatSettings$1 } from "./index-Dl2BOKP7.js";
19
19
  import { create } from "zustand";
20
20
  import { persist } from "zustand/middleware";
21
21
  import { createPortal } from "react-dom";
22
- import { a as Route } from "./router-bN_iTo0B.js";
22
+ import { a as Route } from "./router-DCjikH21.js";
23
23
  function deriveFriendlyIdFromKey(key) {
24
24
  if (!key) return "main";
25
25
  const trimmed = key.trim();
@@ -1681,7 +1681,7 @@ function areSidebarSessionsEqual(prev, next) {
1681
1681
  return true;
1682
1682
  }
1683
1683
  const SettingsDialog = lazy(
1684
- () => import("./settings-dialog-BUOrQN3Z.js").then((m) => ({ default: m.SettingsDialog }))
1684
+ () => import("./settings-dialog-ClKFnZ1x.js").then((m) => ({ default: m.SettingsDialog }))
1685
1685
  );
1686
1686
  const SessionExportDialog = lazy(
1687
1687
  () => import("./session-export-dialog-C53RRAah.js").then((m) => ({
@@ -6596,7 +6596,7 @@ const KeyboardShortcutsDialog = lazy(
6596
6596
  }))
6597
6597
  );
6598
6598
  const SearchDialog = lazy(
6599
- () => import("./search-dialog-DReM5ZD2.js").then((m) => ({
6599
+ () => import("./search-dialog-BnwiXpdA.js").then((m) => ({
6600
6600
  default: m.SearchDialog
6601
6601
  }))
6602
6602
  );
@@ -1,13 +1,13 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useEffect } from "react";
3
- import { R as Route } from "./router-bN_iTo0B.js";
3
+ import { R as Route } from "./router-DCjikH21.js";
4
4
  import "@tanstack/react-router";
5
5
  import "@tanstack/react-query";
6
6
  import "node:crypto";
7
- import "ws";
8
7
  import "node:fs";
9
- import "node:path";
10
8
  import "node:os";
9
+ import "node:path";
10
+ import "ws";
11
11
  import "@tanstack/router-core/ssr/client";
12
12
  import "node:stream";
13
13
  import "node:child_process";
@@ -1,11 +1,11 @@
1
1
  import { createRootRoute, Outlet, HeadContent, Scripts, createFileRoute, lazyRouteComponent, redirect, createRouter } from "@tanstack/react-router";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
4
- import { randomUUID } from "node:crypto";
5
- import WebSocket from "ws";
6
- import { readFileSync, existsSync } from "node:fs";
7
- import path, { join, resolve, relative, extname } from "node:path";
4
+ import crypto, { randomUUID } from "node:crypto";
5
+ import fs, { readFileSync, existsSync } from "node:fs";
8
6
  import os, { homedir } from "node:os";
7
+ import path, { join, resolve, relative, extname } from "node:path";
8
+ import WebSocket from "ws";
9
9
  import { json } from "@tanstack/router-core/ssr/client";
10
10
  import { PassThrough, Readable } from "node:stream";
11
11
  import { execSync } from "node:child_process";
@@ -369,11 +369,11 @@ const $$splitComponentImporter$2 = () => import("./agents-CmQ4vvXm.js");
369
369
  const Route$t = createFileRoute("/agents")({
370
370
  component: lazyRouteComponent($$splitComponentImporter$2, "component")
371
371
  });
372
- const $$splitComponentImporter$1 = () => import("./index-C2hVqxBl.js");
372
+ const $$splitComponentImporter$1 = () => import("./index-Bw-bA_2M.js");
373
373
  const Route$s = createFileRoute("/")({
374
374
  component: lazyRouteComponent($$splitComponentImporter$1, "component")
375
375
  });
376
- const $$splitComponentImporter = () => import("./_sessionKey-Bq_fl7uv.js").then((n) => n.$);
376
+ const $$splitComponentImporter = () => import("./_sessionKey-C9o7YfxA.js").then((n) => n.$);
377
377
  const Route$r = createFileRoute("/chat/$sessionKey")({
378
378
  component: lazyRouteComponent($$splitComponentImporter, "component")
379
379
  });
@@ -388,24 +388,150 @@ function getGatewayConfig() {
388
388
  }
389
389
  return { url, token, password };
390
390
  }
391
- function buildConnectParams(token, password) {
391
+ const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
392
+ function base64UrlEncode(buf) {
393
+ return buf.toString("base64").replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/g, "");
394
+ }
395
+ function derivePublicKeyRaw(publicKeyPem) {
396
+ const spki = crypto.createPublicKey(publicKeyPem).export({ type: "spki", format: "der" });
397
+ if (spki.length === ED25519_SPKI_PREFIX.length + 32 && spki.subarray(0, ED25519_SPKI_PREFIX.length).equals(ED25519_SPKI_PREFIX)) {
398
+ return spki.subarray(ED25519_SPKI_PREFIX.length);
399
+ }
400
+ return spki;
401
+ }
402
+ function fingerprintPublicKey(publicKeyPem) {
403
+ const raw = derivePublicKeyRaw(publicKeyPem);
404
+ return crypto.createHash("sha256").update(raw).digest("hex");
405
+ }
406
+ function ensureDir(filePath) {
407
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
408
+ }
409
+ function resolveDeviceIdentityPath() {
410
+ return path.join(os.homedir(), ".opencami", "identity", "device.json");
411
+ }
412
+ function loadOrCreateDeviceIdentity(filePath = resolveDeviceIdentityPath()) {
413
+ try {
414
+ if (fs.existsSync(filePath)) {
415
+ const raw = fs.readFileSync(filePath, "utf8");
416
+ const parsed = JSON.parse(raw);
417
+ if (parsed?.version === 1 && typeof parsed.deviceId === "string" && typeof parsed.publicKeyPem === "string" && typeof parsed.privateKeyPem === "string") {
418
+ const derivedId = fingerprintPublicKey(parsed.publicKeyPem);
419
+ if (derivedId && derivedId !== parsed.deviceId) {
420
+ const updated = { ...parsed, deviceId: derivedId };
421
+ fs.writeFileSync(filePath, `${JSON.stringify(updated, null, 2)}
422
+ `, { mode: 384 });
423
+ try {
424
+ fs.chmodSync(filePath, 384);
425
+ } catch {
426
+ }
427
+ return { deviceId: derivedId, publicKeyPem: parsed.publicKeyPem, privateKeyPem: parsed.privateKeyPem };
428
+ }
429
+ return { deviceId: parsed.deviceId, publicKeyPem: parsed.publicKeyPem, privateKeyPem: parsed.privateKeyPem };
430
+ }
431
+ }
432
+ } catch {
433
+ }
434
+ const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
435
+ const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString();
436
+ const privateKeyPem = privateKey.export({ type: "pkcs8", format: "pem" }).toString();
437
+ const deviceId = fingerprintPublicKey(publicKeyPem);
438
+ ensureDir(filePath);
439
+ const stored = {
440
+ version: 1,
441
+ deviceId,
442
+ publicKeyPem,
443
+ privateKeyPem,
444
+ createdAtMs: Date.now()
445
+ };
446
+ fs.writeFileSync(filePath, `${JSON.stringify(stored, null, 2)}
447
+ `, { mode: 384 });
448
+ try {
449
+ fs.chmodSync(filePath, 384);
450
+ } catch {
451
+ }
452
+ return { deviceId, publicKeyPem, privateKeyPem };
453
+ }
454
+ function signDevicePayload(privateKeyPem, payload) {
455
+ const key = crypto.createPrivateKey(privateKeyPem);
456
+ return base64UrlEncode(crypto.sign(null, Buffer.from(payload, "utf8"), key));
457
+ }
458
+ function publicKeyRawBase64UrlFromPem(publicKeyPem) {
459
+ return base64UrlEncode(derivePublicKeyRaw(publicKeyPem));
460
+ }
461
+ function buildDeviceAuthPayload(args) {
462
+ const scopes = args.scopes.join(",");
463
+ const token = args.token ?? "";
464
+ return ["v2", args.deviceId, args.clientId, args.clientMode, args.role, scopes, String(args.signedAtMs), token, args.nonce].join("|");
465
+ }
466
+ function loadOrCreateInstanceId() {
467
+ const filePath = path.join(os.homedir(), ".opencami", "identity", "instance-id.txt");
468
+ try {
469
+ if (fs.existsSync(filePath)) {
470
+ const v2 = fs.readFileSync(filePath, "utf8").trim();
471
+ if (v2) return v2;
472
+ }
473
+ } catch {
474
+ }
475
+ const v = randomUUID();
476
+ ensureDir(filePath);
477
+ fs.writeFileSync(filePath, `${v}
478
+ `, { mode: 384 });
479
+ try {
480
+ fs.chmodSync(filePath, 384);
481
+ } catch {
482
+ }
483
+ return v;
484
+ }
485
+ function buildConnectParams(token, password, nonce) {
486
+ const clientId = "openclaw-control-ui";
487
+ const clientMode = "webchat";
488
+ const role = "operator";
489
+ const scopes = ["operator.read", "operator.write"];
490
+ if (!nonce) {
491
+ throw new Error(
492
+ "OpenClaw did not send connect.challenge nonce in time. If you are connecting cross-origin, ensure your origin is allowed (gateway.controlUi.allowedOrigins)."
493
+ );
494
+ }
495
+ const identity = loadOrCreateDeviceIdentity();
496
+ const signedAtMs = Date.now();
497
+ const payload = buildDeviceAuthPayload({
498
+ deviceId: identity.deviceId,
499
+ clientId,
500
+ clientMode,
501
+ role,
502
+ scopes,
503
+ signedAtMs,
504
+ token: token || null,
505
+ nonce
506
+ });
507
+ const signature = signDevicePayload(identity.privateKeyPem, payload);
392
508
  return {
393
509
  minProtocol: 3,
394
510
  maxProtocol: 3,
395
511
  client: {
396
- id: "gateway-client",
512
+ id: clientId,
397
513
  displayName: "OpenCami",
398
514
  version: "dev",
399
515
  platform: process.platform,
400
- mode: "ui",
401
- instanceId: randomUUID()
516
+ mode: clientMode,
517
+ instanceId: loadOrCreateInstanceId()
402
518
  },
519
+ caps: [],
403
520
  auth: {
404
521
  token: token || void 0,
405
522
  password: password || void 0
406
523
  },
407
524
  role: "operator",
408
- scopes: ["operator.read", "operator.write", "operator.admin"]
525
+ scopes,
526
+ device: {
527
+ id: identity.deviceId,
528
+ publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem),
529
+ signature,
530
+ signedAt: signedAtMs,
531
+ nonce
532
+ },
533
+ userAgent: `opencami/${process.env.npm_package_version ?? "dev"} (node ${process.version})`,
534
+ locale: process.env.LANG || "en"
409
535
  };
410
536
  }
411
537
  class PersistentGatewayConnection {
@@ -440,7 +566,8 @@ class PersistentGatewayConnection {
440
566
  async _connect() {
441
567
  if (this.destroyed) throw new Error("Connection destroyed");
442
568
  const { url, token, password } = getGatewayConfig();
443
- const ws = new WebSocket(url);
569
+ const origin = process.env.OPENCAMI_ORIGIN?.trim();
570
+ const ws = origin ? new WebSocket(url, { headers: { Origin: origin } }) : new WebSocket(url);
444
571
  this.ws = ws;
445
572
  await new Promise((resolve2, reject) => {
446
573
  const onOpen = () => {
@@ -458,19 +585,93 @@ class PersistentGatewayConnection {
458
585
  ws.on("open", onOpen);
459
586
  ws.on("error", onError);
460
587
  });
588
+ const nonce = await new Promise((resolve2) => {
589
+ let done = false;
590
+ const timer = setTimeout(() => {
591
+ if (done) return;
592
+ done = true;
593
+ resolve2("");
594
+ }, 3e3);
595
+ const onMessage = (data) => {
596
+ try {
597
+ const str = typeof data === "string" ? data : data.toString();
598
+ const parsed = JSON.parse(str);
599
+ if (parsed.type === "event" && parsed.event === "connect.challenge") {
600
+ const n = parsed.payload?.nonce;
601
+ if (typeof n === "string" && n.length > 0) {
602
+ clearTimeout(timer);
603
+ ws.off("message", onMessage);
604
+ if (done) return;
605
+ done = true;
606
+ resolve2(n);
607
+ }
608
+ }
609
+ } catch {
610
+ }
611
+ };
612
+ ws.on("message", onMessage);
613
+ });
461
614
  ws.on("message", (data) => this._onMessage(data));
462
615
  ws.on("close", () => this._onClose());
463
616
  ws.on("error", () => {
464
617
  });
465
618
  const connectId = randomUUID();
466
- const connectParams = buildConnectParams(token, password);
467
- ws.send(JSON.stringify({
468
- type: "req",
469
- id: connectId,
470
- method: "connect",
471
- params: connectParams
472
- }));
473
- await this._waitForRes(connectId, 1e4);
619
+ const shouldFallback = process.env.OPENCAMI_DEVICE_AUTH_FALLBACK === "1" || process.env.OPENCAMI_DEVICE_AUTH_FALLBACK === "true";
620
+ try {
621
+ const connectParams = buildConnectParams(token, password, nonce);
622
+ ws.send(
623
+ JSON.stringify({
624
+ type: "req",
625
+ id: connectId,
626
+ method: "connect",
627
+ params: connectParams
628
+ })
629
+ );
630
+ const hello = await this._waitForRes(connectId, 1e4);
631
+ const grantedScopes = hello?.auth?.scopes;
632
+ if (Array.isArray(grantedScopes) && !grantedScopes.includes("operator.read")) {
633
+ throw new Error(
634
+ `Gateway connected but missing required scope: operator.read (granted: ${grantedScopes.join(", ")})`
635
+ );
636
+ }
637
+ } catch (err) {
638
+ if (!shouldFallback) throw err;
639
+ console.warn(
640
+ "[gateway-ws] Device auth connect failed; retrying without device identity (fallback enabled):",
641
+ err instanceof Error ? err.message : err
642
+ );
643
+ const fallbackId = randomUUID();
644
+ const fallbackParams = {
645
+ minProtocol: 3,
646
+ maxProtocol: 3,
647
+ client: {
648
+ id: "openclaw-control-ui",
649
+ displayName: "OpenCami",
650
+ version: "dev",
651
+ platform: process.platform,
652
+ mode: "webchat",
653
+ instanceId: loadOrCreateInstanceId()
654
+ },
655
+ caps: [],
656
+ auth: {
657
+ token: token || void 0,
658
+ password: password || void 0
659
+ },
660
+ role: "operator",
661
+ scopes: ["operator.read", "operator.write"],
662
+ userAgent: `opencami/${process.env.npm_package_version ?? "dev"} (node ${process.version})`,
663
+ locale: process.env.LANG || "en"
664
+ };
665
+ ws.send(
666
+ JSON.stringify({
667
+ type: "req",
668
+ id: fallbackId,
669
+ method: "connect",
670
+ params: fallbackParams
671
+ })
672
+ );
673
+ await this._waitForRes(fallbackId, 1e4);
674
+ }
474
675
  this.connected = true;
475
676
  this.reconnectDelay = 1e3;
476
677
  console.log("[gateway-ws] Persistent connection established");
@@ -5,7 +5,7 @@ import { HugeiconsIcon } from "@hugeicons/react";
5
5
  import { Search01Icon, Cancel01Icon, Loading03Icon } from "@hugeicons/core-free-icons";
6
6
  import { D as DialogRoot, a as DialogContent } from "./use-file-explorer-state-s7CS50ho.js";
7
7
  import { useQueryClient } from "@tanstack/react-query";
8
- import { c as chatQueryKeys } from "./_sessionKey-Bq_fl7uv.js";
8
+ import { c as chatQueryKeys } from "./_sessionKey-C9o7YfxA.js";
9
9
  import { c as cn } from "./button-CwY2OHFj.js";
10
10
  import "@base-ui/react/dialog";
11
11
  import "zustand";
@@ -26,12 +26,12 @@ import "remark-gfm";
26
26
  import "./index-Dl2BOKP7.js";
27
27
  import "zustand/middleware";
28
28
  import "react-dom";
29
- import "./router-bN_iTo0B.js";
29
+ import "./router-DCjikH21.js";
30
30
  import "node:crypto";
31
- import "ws";
32
31
  import "node:fs";
33
- import "node:path";
34
32
  import "node:os";
33
+ import "node:path";
34
+ import "ws";
35
35
  import "@tanstack/router-core/ssr/client";
36
36
  import "node:stream";
37
37
  import "node:child_process";
@@ -8,7 +8,7 @@ import { D as DialogRoot, a as DialogContent, b as DialogTitle, c as DialogDescr
8
8
  import { S as Switch } from "./switch-BbkUeVDV.js";
9
9
  import { T as Tabs, a as TabsList, b as TabsTab } from "./tabs-DDFZob0m.js";
10
10
  import { u as useChatSettings } from "./index-Dl2BOKP7.js";
11
- import { u as useLlmSettings, g as getLlmProviderDefaults } from "./_sessionKey-Bq_fl7uv.js";
11
+ import { u as useLlmSettings, g as getLlmProviderDefaults } from "./_sessionKey-C9o7YfxA.js";
12
12
  import "@base-ui/react/merge-props";
13
13
  import "@base-ui/react/use-render";
14
14
  import "class-variance-authority";
@@ -35,12 +35,12 @@ import "react-markdown";
35
35
  import "remark-breaks";
36
36
  import "remark-gfm";
37
37
  import "react-dom";
38
- import "./router-bN_iTo0B.js";
38
+ import "./router-DCjikH21.js";
39
39
  import "node:crypto";
40
- import "ws";
41
40
  import "node:fs";
42
- import "node:path";
43
41
  import "node:os";
42
+ import "node:path";
43
+ import "ws";
44
44
  import "@tanstack/router-core/ssr/client";
45
45
  import "node:stream";
46
46
  import "node:child_process";
@@ -656,7 +656,7 @@ function getStartResponseHeaders(opts) {
656
656
  let entriesPromise;
657
657
  let manifestPromise;
658
658
  async function loadEntries() {
659
- const routerEntry = await import("./assets/router-bN_iTo0B.js").then((n) => n.r);
659
+ const routerEntry = await import("./assets/router-DCjikH21.js").then((n) => n.r);
660
660
  const startEntry = await import("./assets/start-HYkvq4Ni.js");
661
661
  return { startEntry, routerEntry };
662
662
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencami",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "type": "module",
5
5
  "description": "OpenCami - A beautiful web client for OpenClaw",
6
6
  "bin": {