@nextclaw/companion 0.1.1-beta.0 → 0.1.1-beta.2

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 (64) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/{src/launcher/index.js → electron/launcher.js} +2 -2
  3. package/dist/{src → electron}/main.js +3 -3
  4. package/dist/{src/preload/index.js → electron/preload.js} +1 -6
  5. package/dist/{src → electron}/services/companion-application.service.js +14 -16
  6. package/dist/{src/stores/companion-runtime-state.store.js → electron/services/companion-runtime-state-persistence.service.js} +3 -3
  7. package/dist/{src/stores/companion-window-position.store.js → electron/services/companion-window-position-persistence.service.js} +4 -4
  8. package/dist/{src → electron}/services/companion-window.service.js +15 -26
  9. package/dist/electron/types/companion-shell.types.js +2 -0
  10. package/dist/src/app/app.js +17 -0
  11. package/dist/src/app/components/companion-shell.container.js +20 -0
  12. package/dist/src/app/main.js +10 -0
  13. package/dist/src/app/providers/companion-presenter.provider.js +17 -0
  14. package/dist/src/managers/companion-runtime.manager.js +152 -0
  15. package/dist/src/managers/companion-runtime.manager.test.js +45 -0
  16. package/dist/src/managers/companion-shell.manager.js +22 -0
  17. package/dist/src/presenters/companion.presenter.js +37 -0
  18. package/dist/src/services/companion-runtime-client.service.js +16 -68
  19. package/dist/src/stores/companion-runtime.store.js +27 -0
  20. package/dist/src/stores/companion-shell.store.js +18 -0
  21. package/dist/ui/assets/dist-D4AJsX_N.js +1 -0
  22. package/dist/ui/assets/index-BHBNwOpX.js +8 -0
  23. package/dist/ui/assets/index-DPctoCcE.css +1 -0
  24. package/dist/ui/index.html +13 -0
  25. package/{src/launcher/index.ts → electron/launcher.ts} +2 -2
  26. package/{src → electron}/main.ts +4 -3
  27. package/electron/preload.ts +7 -0
  28. package/{src → electron}/services/companion-application.service.ts +16 -20
  29. package/{src/stores/companion-runtime-state.store.ts → electron/services/companion-runtime-state-persistence.service.ts} +1 -1
  30. package/{src/stores/companion-window-position.store.ts → electron/services/companion-window-position-persistence.service.ts} +3 -3
  31. package/{src → electron}/services/companion-window.service.ts +23 -30
  32. package/electron/types/companion-shell.types.ts +8 -0
  33. package/index.html +12 -0
  34. package/module-structure.config.json +25 -0
  35. package/package.json +21 -9
  36. package/postcss.config.mjs +6 -0
  37. package/scripts/smoke.mjs +4 -3
  38. package/src/app/app.tsx +17 -0
  39. package/src/app/components/companion-shell.container.tsx +67 -0
  40. package/src/app/index.css +37 -0
  41. package/src/app/main.tsx +15 -0
  42. package/src/app/providers/companion-presenter.provider.tsx +27 -0
  43. package/src/managers/companion-runtime.manager.test.ts +56 -0
  44. package/src/managers/companion-runtime.manager.ts +191 -0
  45. package/src/managers/companion-shell.manager.ts +21 -0
  46. package/src/module-structure.config.json +22 -0
  47. package/src/presenters/companion.presenter.ts +36 -0
  48. package/src/services/companion-runtime-client.service.ts +21 -84
  49. package/src/stores/companion-runtime.store.ts +51 -0
  50. package/src/stores/companion-shell.store.ts +27 -0
  51. package/src/types/companion-bridge.types.d.ts +11 -0
  52. package/src/types/companion.types.ts +0 -10
  53. package/tailwind.config.mjs +32 -0
  54. package/tsconfig.json +2 -1
  55. package/vite.config.mts +16 -0
  56. package/dist/src/companion-session-view.service.test.js +0 -27
  57. package/dist/src/services/companion-session-view.service.js +0 -52
  58. package/dist/src/utils/companion-renderer-html.utils.js +0 -194
  59. package/src/companion-session-view.service.test.ts +0 -37
  60. package/src/preload/index.ts +0 -13
  61. package/src/services/companion-session-view.service.ts +0 -63
  62. package/src/utils/companion-renderer-html.utils.ts +0 -192
  63. /package/dist/{src → electron}/services/companion-tray.service.js +0 -0
  64. /package/{src → electron}/services/companion-tray.service.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # @nextclaw/companion
2
2
 
3
+ ## 0.1.1-beta.2
4
+
5
+ ### Patch Changes
6
+
7
+ - @nextclaw/client-sdk@0.1.1-beta.2
8
+
9
+ ## 0.1.1-beta.1
10
+
11
+ ### Patch Changes
12
+
13
+ - Auto-generated patch release for packages with meaningful drift after their latest version commit.
14
+
15
+ Packages:
16
+ - @nextclaw/agent-chat
17
+ - @nextclaw/app-runtime
18
+ - @nextclaw/app-sdk
19
+ - @nextclaw/channel-plugin-dingtalk
20
+ - @nextclaw/channel-plugin-discord
21
+ - @nextclaw/channel-plugin-email
22
+ - @nextclaw/channel-plugin-mochat
23
+ - @nextclaw/channel-plugin-qq
24
+ - @nextclaw/channel-plugin-slack
25
+ - @nextclaw/channel-plugin-telegram
26
+ - @nextclaw/channel-plugin-wecom
27
+ - @nextclaw/channel-plugin-whatsapp
28
+ - @nextclaw/channel-runtime
29
+ - @nextclaw/client-sdk
30
+ - @nextclaw/companion
31
+ - @nextclaw/kernel
32
+ - @nextclaw/mcp
33
+ - @nextclaw/ncp-agent-runtime
34
+ - @nextclaw/ncp-http-agent-client
35
+ - @nextclaw/ncp-http-agent-server
36
+ - @nextclaw/ncp-mcp
37
+ - @nextclaw/ncp-react
38
+ - @nextclaw/nextclaw-hermes-acp-bridge
39
+ - @nextclaw/nextclaw-ncp-runtime-adapter-hermes-http
40
+ - @nextclaw/nextclaw-ncp-runtime-claude-code-sdk
41
+ - @nextclaw/nextclaw-ncp-runtime-codex-sdk
42
+ - @nextclaw/nextclaw-ncp-runtime-http-client
43
+ - @nextclaw/nextclaw-ncp-runtime-plugin-claude-code-sdk
44
+ - @nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk
45
+ - @nextclaw/nextclaw-ncp-runtime-stdio-client
46
+ - @nextclaw/openclaw-compat
47
+ - @nextclaw/remote
48
+
49
+ - Updated dependencies
50
+ - @nextclaw/client-sdk@0.1.1-beta.1
51
+
3
52
  ## 0.1.1-beta.0
4
53
 
5
54
  ### Patch Changes
@@ -6,10 +6,10 @@ const node_module_1 = require("node:module");
6
6
  const node_path_1 = require("node:path");
7
7
  const loadModule = (0, node_module_1.createRequire)(__filename);
8
8
  const electronBinary = loadModule("electron");
9
- const appRoot = (0, node_path_1.resolve)(__dirname, "..", "..", "..");
9
+ const mainEntryPath = (0, node_path_1.resolve)(__dirname, "main.js");
10
10
  const env = { ...process.env };
11
11
  delete env.ELECTRON_RUN_AS_NODE;
12
- const child = (0, node_child_process_1.spawn)(electronBinary, [appRoot, ...process.argv.slice(2)], {
12
+ const child = (0, node_child_process_1.spawn)(electronBinary, [mainEntryPath, ...process.argv.slice(2)], {
13
13
  stdio: "inherit",
14
14
  env
15
15
  });
@@ -13,10 +13,10 @@ function resolveBaseUrl(argv) {
13
13
  return process.env.NEXTCLAW_COMPANION_BASE_URL?.trim() || "http://127.0.0.1:55667";
14
14
  }
15
15
  async function main() {
16
- const application = new companion_application_service_js_1.CompanionApplicationService({
16
+ const options = {
17
17
  baseUrl: resolveBaseUrl(process.argv.slice(1)),
18
18
  runtimeStatePath: process.env.NEXTCLAW_COMPANION_RUNTIME_STATE_PATH?.trim() || undefined
19
- });
20
- await application.run();
19
+ };
20
+ await new companion_application_service_js_1.CompanionApplicationService(options).run();
21
21
  }
22
22
  void main();
@@ -2,12 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const electron_1 = require("electron");
4
4
  electron_1.contextBridge.exposeInMainWorld("nextclawCompanion", {
5
- onView: (listener) => {
6
- electron_1.ipcRenderer.on("companion:view", (_event, view) => {
7
- listener(view);
8
- });
9
- },
10
5
  open: () => electron_1.ipcRenderer.invoke("companion:open"),
11
6
  quit: () => electron_1.ipcRenderer.invoke("companion:quit"),
12
- ready: () => electron_1.ipcRenderer.send("companion:ready")
7
+ getBootstrap: () => electron_1.ipcRenderer.invoke("companion:get-bootstrap")
13
8
  });
@@ -3,26 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CompanionApplicationService = void 0;
4
4
  const electron_1 = require("electron");
5
5
  const node_path_1 = require("node:path");
6
- const companion_runtime_state_store_js_1 = require("../stores/companion-runtime-state.store.js");
7
- const companion_window_position_store_js_1 = require("../stores/companion-window-position.store.js");
8
- const companion_runtime_client_service_js_1 = require("./companion-runtime-client.service.js");
6
+ const companion_runtime_state_persistence_service_js_1 = require("./companion-runtime-state-persistence.service.js");
9
7
  const companion_tray_service_js_1 = require("./companion-tray.service.js");
8
+ const companion_window_position_persistence_service_js_1 = require("./companion-window-position-persistence.service.js");
10
9
  const companion_window_service_js_1 = require("./companion-window.service.js");
11
10
  class CompanionApplicationService {
12
11
  options;
13
- runtimeClient;
14
- runtimeStateStore;
12
+ runtimeStatePersistenceService;
15
13
  windowService;
16
14
  trayService;
17
15
  quitting = false;
18
16
  constructor(options) {
19
17
  this.options = options;
20
- const preloadPath = (0, node_path_1.resolve)(__dirname, "..", "preload", "index.js");
21
- this.runtimeClient = new companion_runtime_client_service_js_1.CompanionRuntimeClientService(options.baseUrl);
22
- this.runtimeStateStore = options.runtimeStatePath
23
- ? new companion_runtime_state_store_js_1.CompanionRuntimeStateStore(options.runtimeStatePath)
18
+ this.runtimeStatePersistenceService = options.runtimeStatePath
19
+ ? new companion_runtime_state_persistence_service_js_1.CompanionRuntimeStatePersistenceService(options.runtimeStatePath)
24
20
  : null;
25
- this.windowService = new companion_window_service_js_1.CompanionWindowService(preloadPath, companion_window_position_store_js_1.CompanionWindowPositionStore.fromUserData(electron_1.app.getPath("userData")));
21
+ this.windowService = new companion_window_service_js_1.CompanionWindowService({
22
+ preloadPath: (0, node_path_1.resolve)(__dirname, "..", "preload.js"),
23
+ rendererEntryPath: (0, node_path_1.resolve)(__dirname, "..", "..", "ui", "index.html"),
24
+ positionPersistenceService: companion_window_position_persistence_service_js_1.CompanionWindowPositionPersistenceService.fromUserData(electron_1.app.getPath("userData")),
25
+ baseUrl: options.baseUrl,
26
+ onQuit: () => this.quit()
27
+ });
26
28
  this.trayService = new companion_tray_service_js_1.CompanionTrayService(options.baseUrl, () => this.windowService.toggleVisibility(), () => this.quit());
27
29
  }
28
30
  run = async () => {
@@ -36,16 +38,15 @@ class CompanionApplicationService {
36
38
  electron_1.app.on("window-all-closed", () => undefined);
37
39
  electron_1.app.on("before-quit", () => {
38
40
  this.quitting = true;
39
- this.runtimeClient.stop();
40
41
  this.trayService.destroy();
41
42
  this.windowService.destroy();
42
- this.runtimeStateStore?.clear();
43
+ this.runtimeStatePersistenceService?.clear();
43
44
  });
44
45
  electron_1.app.on("activate", () => {
45
46
  this.windowService.show();
46
47
  });
47
48
  await electron_1.app.whenReady();
48
- this.runtimeStateStore?.write({
49
+ this.runtimeStatePersistenceService?.write({
49
50
  pid: process.pid,
50
51
  startedAt: new Date().toISOString(),
51
52
  baseUrl: this.options.baseUrl
@@ -53,9 +54,6 @@ class CompanionApplicationService {
53
54
  await this.windowService.create();
54
55
  this.windowService.show();
55
56
  this.trayService.create();
56
- await this.runtimeClient.start((view) => {
57
- this.windowService.updateView(view);
58
- });
59
57
  };
60
58
  quit = () => {
61
59
  if (this.quitting) {
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CompanionRuntimeStateStore = void 0;
3
+ exports.CompanionRuntimeStatePersistenceService = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
- class CompanionRuntimeStateStore {
6
+ class CompanionRuntimeStatePersistenceService {
7
7
  filePath;
8
8
  constructor(filePath) {
9
9
  this.filePath = filePath;
@@ -18,4 +18,4 @@ class CompanionRuntimeStateStore {
18
18
  }
19
19
  };
20
20
  }
21
- exports.CompanionRuntimeStateStore = CompanionRuntimeStateStore;
21
+ exports.CompanionRuntimeStatePersistenceService = CompanionRuntimeStatePersistenceService;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CompanionWindowPositionStore = void 0;
3
+ exports.CompanionWindowPositionPersistenceService = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
- class CompanionWindowPositionStore {
6
+ class CompanionWindowPositionPersistenceService {
7
7
  filePath;
8
8
  constructor(filePath) {
9
9
  this.filePath = filePath;
@@ -24,7 +24,7 @@ class CompanionWindowPositionStore {
24
24
  (0, node_fs_1.writeFileSync)(this.filePath, JSON.stringify(bounds, null, 2));
25
25
  };
26
26
  static fromUserData = (userDataPath) => {
27
- return new CompanionWindowPositionStore((0, node_path_1.resolve)(userDataPath, "companion-window.json"));
27
+ return new CompanionWindowPositionPersistenceService((0, node_path_1.resolve)(userDataPath, "companion-window.json"));
28
28
  };
29
29
  }
30
- exports.CompanionWindowPositionStore = CompanionWindowPositionStore;
30
+ exports.CompanionWindowPositionPersistenceService = CompanionWindowPositionPersistenceService;
@@ -2,21 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CompanionWindowService = void 0;
4
4
  const electron_1 = require("electron");
5
- const companion_renderer_html_utils_js_1 = require("../utils/companion-renderer-html.utils.js");
6
5
  class CompanionWindowService {
7
- preloadPath;
8
- positionStore;
6
+ options;
9
7
  window = null;
10
- currentView = null;
11
- constructor(preloadPath, positionStore) {
12
- this.preloadPath = preloadPath;
13
- this.positionStore = positionStore;
8
+ constructor(options) {
9
+ this.options = options;
14
10
  }
15
11
  create = async () => {
16
12
  if (this.window) {
17
13
  return;
18
14
  }
19
- const bounds = this.positionStore.read();
15
+ const bounds = this.options.positionPersistenceService.read();
20
16
  this.window = new electron_1.BrowserWindow({
21
17
  width: 112,
22
18
  height: 132,
@@ -30,7 +26,7 @@ class CompanionWindowService {
30
26
  skipTaskbar: true,
31
27
  hasShadow: false,
32
28
  webPreferences: {
33
- preload: this.preloadPath,
29
+ preload: this.options.preloadPath,
34
30
  contextIsolation: true,
35
31
  nodeIntegration: false
36
32
  }
@@ -49,24 +45,21 @@ class CompanionWindowService {
49
45
  });
50
46
  electron_1.ipcMain.removeHandler("companion:open");
51
47
  electron_1.ipcMain.handle("companion:open", async () => {
52
- const openUrl = this.currentView?.openUrl;
53
- if (openUrl) {
54
- await electron_1.shell.openExternal(openUrl);
55
- }
48
+ await electron_1.shell.openExternal(this.options.baseUrl);
56
49
  return null;
57
50
  });
58
51
  electron_1.ipcMain.removeHandler("companion:quit");
59
52
  electron_1.ipcMain.handle("companion:quit", async () => {
60
- electron_1.app.quit();
53
+ this.options.onQuit();
61
54
  return null;
62
55
  });
63
- electron_1.ipcMain.removeAllListeners("companion:ready");
64
- electron_1.ipcMain.on("companion:ready", () => {
65
- if (this.currentView) {
66
- this.window?.webContents.send("companion:view", this.currentView);
67
- }
56
+ electron_1.ipcMain.removeHandler("companion:get-bootstrap");
57
+ electron_1.ipcMain.handle("companion:get-bootstrap", async () => {
58
+ return {
59
+ baseUrl: this.options.baseUrl
60
+ };
68
61
  });
69
- await this.window.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent((0, companion_renderer_html_utils_js_1.renderCompanionHtml)())}`);
62
+ await this.window.loadFile(this.options.rendererEntryPath);
70
63
  };
71
64
  show = () => {
72
65
  this.window?.showInactive();
@@ -81,14 +74,10 @@ class CompanionWindowService {
81
74
  }
82
75
  this.window.showInactive();
83
76
  };
84
- updateView = (view) => {
85
- this.currentView = view;
86
- this.window?.webContents.send("companion:view", view);
87
- };
88
77
  destroy = () => {
89
78
  electron_1.ipcMain.removeHandler("companion:open");
90
79
  electron_1.ipcMain.removeHandler("companion:quit");
91
- electron_1.ipcMain.removeAllListeners("companion:ready");
80
+ electron_1.ipcMain.removeHandler("companion:get-bootstrap");
92
81
  this.window?.destroy();
93
82
  this.window = null;
94
83
  };
@@ -97,7 +86,7 @@ class CompanionWindowService {
97
86
  if (!bounds) {
98
87
  return;
99
88
  }
100
- this.positionStore.write({
89
+ this.options.positionPersistenceService.write({
101
90
  x: bounds.x,
102
91
  y: bounds.y
103
92
  });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.App = App;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const companion_shell_container_js_1 = require("./components/companion-shell.container.js");
7
+ const companion_presenter_provider_js_1 = require("./providers/companion-presenter.provider.js");
8
+ function App() {
9
+ const presenter = (0, companion_presenter_provider_js_1.usePresenter)();
10
+ (0, react_1.useEffect)(() => {
11
+ void presenter.bootstrap();
12
+ return () => {
13
+ presenter.shutdown();
14
+ };
15
+ }, [presenter]);
16
+ return (0, jsx_runtime_1.jsx)(companion_shell_container_js_1.CompanionShellContainer, {});
17
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CompanionShellContainer = CompanionShellContainer;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const lucide_react_1 = require("lucide-react");
6
+ const companion_runtime_store_js_1 = require("../../stores/companion-runtime.store.js");
7
+ const companion_shell_store_js_1 = require("../../stores/companion-shell.store.js");
8
+ const companion_presenter_provider_js_1 = require("../providers/companion-presenter.provider.js");
9
+ function CompanionShellContainer() {
10
+ const presenter = (0, companion_presenter_provider_js_1.usePresenter)();
11
+ const view = (0, companion_runtime_store_js_1.useCompanionRuntimeStore)((state) => state.snapshot.currentView);
12
+ const connectionState = (0, companion_runtime_store_js_1.useCompanionRuntimeStore)((state) => state.snapshot.connectionState);
13
+ const bootstrapped = (0, companion_shell_store_js_1.useCompanionShellStore)((state) => state.snapshot.bootstrapped);
14
+ const statusColorClass = connectionState === "running"
15
+ ? "bg-success"
16
+ : connectionState === "offline"
17
+ ? "bg-danger"
18
+ : "bg-slate-300";
19
+ return ((0, jsx_runtime_1.jsx)("div", { className: "h-screen w-screen overflow-hidden bg-transparent", children: (0, jsx_runtime_1.jsx)("div", { className: "grid h-full place-items-center", children: (0, jsx_runtime_1.jsxs)("div", { className: "grid w-[112px] justify-items-center gap-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "drag-region relative grid h-24 w-24 place-items-center overflow-hidden rounded-[28px] border border-white/70 bg-white/90 shadow-companion backdrop-blur-xl", children: [(0, jsx_runtime_1.jsxs)("div", { className: "no-drag absolute right-1.5 top-1.5 z-20 flex gap-1", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Open NextClaw", onClick: () => void presenter.companionShellManager.open(), className: "grid h-5 w-5 place-items-center rounded-full bg-slate-900/10 text-slate-700 transition hover:bg-slate-900/15", children: (0, jsx_runtime_1.jsx)(lucide_react_1.ExternalLink, { className: "h-3 w-3" }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Quit Companion", onClick: () => void presenter.companionShellManager.quit(), className: "grid h-5 w-5 place-items-center rounded-full bg-slate-900/10 text-slate-700 transition hover:bg-slate-900/15", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "h-3 w-3" }) })] }), view.avatarUrl ? ((0, jsx_runtime_1.jsx)("img", { src: view.avatarUrl, alt: "", className: "h-full w-full object-cover" })) : ((0, jsx_runtime_1.jsx)("span", { className: "grid h-full w-full place-items-center bg-gradient-to-b from-white to-slate-200 text-[28px] font-bold text-slate-800", children: (view.title || "NC").slice(0, 2).toUpperCase() })), (0, jsx_runtime_1.jsx)("span", { className: `absolute bottom-2 right-2 h-3.5 w-3.5 rounded-full border-2 border-white ${statusColorClass}` })] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => void presenter.companionShellManager.open(), className: "no-drag w-full rounded-xl px-1 text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "truncate text-[12px] font-semibold leading-4 text-slate-900", children: bootstrapped ? view.title : "NextClaw" }), (0, jsx_runtime_1.jsx)("div", { className: "truncate pt-0.5 text-[11px] leading-4 text-slate-500", children: bootstrapped ? view.subtitle : "Connecting" })] })] }) }) }));
20
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ const react_1 = require("react");
5
+ const client_1 = require("react-dom/client");
6
+ const app_js_1 = require("./app.js");
7
+ const companion_presenter_provider_js_1 = require("./providers/companion-presenter.provider.js");
8
+ const companion_presenter_js_1 = require("../presenters/companion.presenter.js");
9
+ require("./index.css");
10
+ (0, client_1.createRoot)(document.getElementById("root")).render((0, jsx_runtime_1.jsx)(react_1.StrictMode, { children: (0, jsx_runtime_1.jsx)(companion_presenter_provider_js_1.CompanionPresenterProvider, { presenter: companion_presenter_js_1.companionPresenter, children: (0, jsx_runtime_1.jsx)(app_js_1.App, {}) }) }));
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CompanionPresenterProvider = CompanionPresenterProvider;
4
+ exports.usePresenter = usePresenter;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const CompanionPresenterContext = (0, react_1.createContext)(null);
8
+ function CompanionPresenterProvider({ presenter, children }) {
9
+ return ((0, jsx_runtime_1.jsx)(CompanionPresenterContext.Provider, { value: presenter, children: children }));
10
+ }
11
+ function usePresenter() {
12
+ const presenter = (0, react_1.useContext)(CompanionPresenterContext);
13
+ if (!presenter) {
14
+ throw new Error("usePresenter must be used inside CompanionPresenterProvider");
15
+ }
16
+ return presenter;
17
+ }
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CompanionRuntimeManager = void 0;
4
+ const companion_runtime_client_service_js_1 = require("../services/companion-runtime-client.service.js");
5
+ const companion_runtime_store_js_1 = require("../stores/companion-runtime.store.js");
6
+ class CompanionRuntimeManager {
7
+ clientService = null;
8
+ baseUrl = null;
9
+ refreshTimer = null;
10
+ subscription = null;
11
+ started = false;
12
+ start = async (baseUrl) => {
13
+ if (this.started && this.baseUrl === baseUrl) {
14
+ return;
15
+ }
16
+ this.stop();
17
+ this.baseUrl = baseUrl;
18
+ this.clientService = new companion_runtime_client_service_js_1.CompanionRuntimeClientService(baseUrl);
19
+ await this.clientService.initialize();
20
+ companion_runtime_store_js_1.useCompanionRuntimeStore.getState().setSnapshot((0, companion_runtime_store_js_1.createInitialCompanionRuntimeSnapshot)(baseUrl));
21
+ await this.refresh();
22
+ this.subscription = this.ensureClientService().subscribeToSessions(async () => {
23
+ await this.refresh();
24
+ }, {
25
+ reconnectDelayMs: 1000,
26
+ onError: async () => {
27
+ await this.refresh();
28
+ }
29
+ });
30
+ this.refreshTimer = setInterval(() => {
31
+ void this.refresh();
32
+ }, 10000);
33
+ this.started = true;
34
+ };
35
+ stop = () => {
36
+ this.subscription?.close();
37
+ this.subscription = null;
38
+ if (this.refreshTimer !== null) {
39
+ clearInterval(this.refreshTimer);
40
+ this.refreshTimer = null;
41
+ }
42
+ this.clientService = null;
43
+ this.baseUrl = null;
44
+ this.started = false;
45
+ companion_runtime_store_js_1.useCompanionRuntimeStore.getState().reset();
46
+ };
47
+ getSnapshot = () => {
48
+ return companion_runtime_store_js_1.useCompanionRuntimeStore.getState().snapshot;
49
+ };
50
+ syncRuntimeSnapshot = ({ agents, sessions }) => {
51
+ const baseUrl = this.ensureBaseUrl();
52
+ const currentView = this.resolveCurrentView({
53
+ baseUrl,
54
+ agents,
55
+ sessions
56
+ });
57
+ const connectionState = currentView.state === "running" ? "running" : "idle";
58
+ const snapshot = {
59
+ baseUrl,
60
+ agents,
61
+ sessions,
62
+ connectionState,
63
+ offlineReason: null,
64
+ currentView
65
+ };
66
+ companion_runtime_store_js_1.useCompanionRuntimeStore.getState().setSnapshot(snapshot);
67
+ return snapshot;
68
+ };
69
+ applyOfflineState = (reason) => {
70
+ const baseUrl = this.baseUrl ?? companion_runtime_store_js_1.useCompanionRuntimeStore.getState().snapshot.baseUrl ?? "http://127.0.0.1:55667";
71
+ const snapshot = {
72
+ baseUrl,
73
+ agents: [],
74
+ sessions: [],
75
+ connectionState: "offline",
76
+ offlineReason: reason ?? null,
77
+ currentView: {
78
+ state: "offline",
79
+ title: "NextClaw",
80
+ subtitle: reason?.summary?.trim() || "Runtime unavailable",
81
+ openUrl: baseUrl
82
+ }
83
+ };
84
+ companion_runtime_store_js_1.useCompanionRuntimeStore.getState().setSnapshot(snapshot);
85
+ return snapshot;
86
+ };
87
+ refresh = async () => {
88
+ try {
89
+ const [agents, sessions] = await Promise.all([
90
+ this.ensureClientService().listAgents(),
91
+ this.ensureClientService().listSessions()
92
+ ]);
93
+ this.syncRuntimeSnapshot({
94
+ agents,
95
+ sessions: sessions.sessions
96
+ });
97
+ }
98
+ catch (error) {
99
+ this.applyOfflineState(error instanceof Error
100
+ ? { summary: this.summarizeOfflineError(error.message) }
101
+ : undefined);
102
+ }
103
+ };
104
+ ensureClientService = () => {
105
+ if (!this.clientService) {
106
+ this.clientService = new companion_runtime_client_service_js_1.CompanionRuntimeClientService(this.ensureBaseUrl());
107
+ }
108
+ return this.clientService;
109
+ };
110
+ ensureBaseUrl = () => {
111
+ if (!this.baseUrl) {
112
+ throw new Error("CompanionRuntimeManager baseUrl has not been initialized.");
113
+ }
114
+ return this.baseUrl;
115
+ };
116
+ resolveCurrentView = ({ baseUrl, agents, sessions }) => {
117
+ const runningSession = [...sessions]
118
+ .filter((session) => session.status === "running")
119
+ .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
120
+ if (!runningSession) {
121
+ return {
122
+ state: "idle",
123
+ title: "NextClaw",
124
+ subtitle: "No active agent",
125
+ openUrl: baseUrl
126
+ };
127
+ }
128
+ const agent = agents.find((entry) => entry.id === runningSession.agentId) ?? null;
129
+ return {
130
+ state: "running",
131
+ title: agent?.displayName?.trim() || runningSession.agentId || "Active Agent",
132
+ subtitle: runningSession.sessionId,
133
+ avatarUrl: runningSession.agentId
134
+ ? agent?.avatarUrl?.trim() || this.ensureClientService().resolveAvatarUrl(runningSession.agentId)
135
+ : undefined,
136
+ sessionId: runningSession.sessionId,
137
+ agentId: runningSession.agentId,
138
+ openUrl: baseUrl
139
+ };
140
+ };
141
+ summarizeOfflineError = (message) => {
142
+ if (/fetch failed/i.test(message)) {
143
+ return "Cannot reach runtime";
144
+ }
145
+ if (/timed out/i.test(message)) {
146
+ return "Runtime timeout";
147
+ }
148
+ const trimmed = message.trim();
149
+ return trimmed.length > 28 ? `${trimmed.slice(0, 25)}...` : trimmed || "Runtime unavailable";
150
+ };
151
+ }
152
+ exports.CompanionRuntimeManager = CompanionRuntimeManager;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const companion_runtime_store_js_1 = require("../stores/companion-runtime.store.js");
5
+ const companion_runtime_manager_js_1 = require("./companion-runtime.manager.js");
6
+ (0, vitest_1.describe)("CompanionRuntimeManager", () => {
7
+ (0, vitest_1.beforeEach)(() => {
8
+ companion_runtime_store_js_1.useCompanionRuntimeStore.getState().reset();
9
+ });
10
+ (0, vitest_1.it)("prefers the most recently updated running session", () => {
11
+ const manager = new companion_runtime_manager_js_1.CompanionRuntimeManager();
12
+ const baseUrl = "http://127.0.0.1:55667";
13
+ manager.baseUrl = baseUrl;
14
+ const snapshot = manager.syncRuntimeSnapshot({
15
+ agents: [{ id: "writer", displayName: "Writer" }],
16
+ sessions: [
17
+ { sessionId: "older", agentId: "writer", messageCount: 1, updatedAt: "2026-05-05T00:00:00.000Z", status: "running" },
18
+ { sessionId: "newer", agentId: "writer", messageCount: 2, updatedAt: "2026-05-06T00:00:00.000Z", status: "running" }
19
+ ]
20
+ });
21
+ (0, vitest_1.expect)(snapshot.currentView.sessionId).toBe("newer");
22
+ (0, vitest_1.expect)(snapshot.currentView.title).toBe("Writer");
23
+ (0, vitest_1.expect)(snapshot.connectionState).toBe("running");
24
+ });
25
+ (0, vitest_1.it)("falls back to an idle shell view when no running session exists", () => {
26
+ const manager = new companion_runtime_manager_js_1.CompanionRuntimeManager();
27
+ const baseUrl = "http://127.0.0.1:55667";
28
+ manager.baseUrl = baseUrl;
29
+ const snapshot = manager.syncRuntimeSnapshot({
30
+ agents: [],
31
+ sessions: []
32
+ });
33
+ (0, vitest_1.expect)(snapshot.currentView.state).toBe("idle");
34
+ (0, vitest_1.expect)(snapshot.currentView.subtitle).toBe("No active agent");
35
+ (0, vitest_1.expect)(snapshot.connectionState).toBe("idle");
36
+ });
37
+ (0, vitest_1.it)("produces an offline snapshot with a readable reason", () => {
38
+ const manager = new companion_runtime_manager_js_1.CompanionRuntimeManager();
39
+ manager.baseUrl = "http://127.0.0.1:55667";
40
+ const snapshot = manager.applyOfflineState({ summary: "Cannot reach runtime" });
41
+ (0, vitest_1.expect)(snapshot.connectionState).toBe("offline");
42
+ (0, vitest_1.expect)(snapshot.currentView.state).toBe("offline");
43
+ (0, vitest_1.expect)(snapshot.currentView.subtitle).toBe("Cannot reach runtime");
44
+ });
45
+ });
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CompanionShellManager = void 0;
4
+ const companion_shell_store_js_1 = require("../stores/companion-shell.store.js");
5
+ class CompanionShellManager {
6
+ bootstrap = async () => {
7
+ const bootstrap = await window.nextclawCompanion.getBootstrap();
8
+ const snapshot = {
9
+ baseUrl: bootstrap.baseUrl,
10
+ bootstrapped: true
11
+ };
12
+ companion_shell_store_js_1.useCompanionShellStore.getState().setSnapshot(snapshot);
13
+ return snapshot;
14
+ };
15
+ open = async () => {
16
+ await window.nextclawCompanion.open();
17
+ };
18
+ quit = async () => {
19
+ await window.nextclawCompanion.quit();
20
+ };
21
+ }
22
+ exports.CompanionShellManager = CompanionShellManager;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.companionPresenter = exports.CompanionPresenter = void 0;
4
+ const companion_runtime_manager_js_1 = require("../managers/companion-runtime.manager.js");
5
+ const companion_shell_manager_js_1 = require("../managers/companion-shell.manager.js");
6
+ class CompanionPresenter {
7
+ companionRuntimeManager = new companion_runtime_manager_js_1.CompanionRuntimeManager();
8
+ companionShellManager = new companion_shell_manager_js_1.CompanionShellManager();
9
+ bootstrapPromise = null;
10
+ bootstrapped = false;
11
+ bootstrap = async () => {
12
+ if (this.bootstrapped) {
13
+ return;
14
+ }
15
+ if (this.bootstrapPromise) {
16
+ return await this.bootstrapPromise;
17
+ }
18
+ this.bootstrapPromise = (async () => {
19
+ const shellSnapshot = await this.companionShellManager.bootstrap();
20
+ await this.companionRuntimeManager.start(shellSnapshot.baseUrl);
21
+ this.bootstrapped = true;
22
+ })();
23
+ try {
24
+ await this.bootstrapPromise;
25
+ }
26
+ finally {
27
+ this.bootstrapPromise = null;
28
+ }
29
+ };
30
+ shutdown = () => {
31
+ this.companionRuntimeManager.stop();
32
+ this.bootstrapped = false;
33
+ this.bootstrapPromise = null;
34
+ };
35
+ }
36
+ exports.CompanionPresenter = CompanionPresenter;
37
+ exports.companionPresenter = new CompanionPresenter();