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

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 +43 -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,48 @@
1
1
  # @nextclaw/companion
2
2
 
3
+ ## 0.1.1-beta.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Auto-generated patch release for packages with meaningful drift after their latest version commit.
8
+
9
+ Packages:
10
+ - @nextclaw/agent-chat
11
+ - @nextclaw/app-runtime
12
+ - @nextclaw/app-sdk
13
+ - @nextclaw/channel-plugin-dingtalk
14
+ - @nextclaw/channel-plugin-discord
15
+ - @nextclaw/channel-plugin-email
16
+ - @nextclaw/channel-plugin-mochat
17
+ - @nextclaw/channel-plugin-qq
18
+ - @nextclaw/channel-plugin-slack
19
+ - @nextclaw/channel-plugin-telegram
20
+ - @nextclaw/channel-plugin-wecom
21
+ - @nextclaw/channel-plugin-whatsapp
22
+ - @nextclaw/channel-runtime
23
+ - @nextclaw/client-sdk
24
+ - @nextclaw/companion
25
+ - @nextclaw/kernel
26
+ - @nextclaw/mcp
27
+ - @nextclaw/ncp-agent-runtime
28
+ - @nextclaw/ncp-http-agent-client
29
+ - @nextclaw/ncp-http-agent-server
30
+ - @nextclaw/ncp-mcp
31
+ - @nextclaw/ncp-react
32
+ - @nextclaw/nextclaw-hermes-acp-bridge
33
+ - @nextclaw/nextclaw-ncp-runtime-adapter-hermes-http
34
+ - @nextclaw/nextclaw-ncp-runtime-claude-code-sdk
35
+ - @nextclaw/nextclaw-ncp-runtime-codex-sdk
36
+ - @nextclaw/nextclaw-ncp-runtime-http-client
37
+ - @nextclaw/nextclaw-ncp-runtime-plugin-claude-code-sdk
38
+ - @nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk
39
+ - @nextclaw/nextclaw-ncp-runtime-stdio-client
40
+ - @nextclaw/openclaw-compat
41
+ - @nextclaw/remote
42
+
43
+ - Updated dependencies
44
+ - @nextclaw/client-sdk@0.1.1-beta.1
45
+
3
46
  ## 0.1.1-beta.0
4
47
 
5
48
  ### 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();