@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.
- package/CHANGELOG.md +43 -0
- package/dist/{src/launcher/index.js → electron/launcher.js} +2 -2
- package/dist/{src → electron}/main.js +3 -3
- package/dist/{src/preload/index.js → electron/preload.js} +1 -6
- package/dist/{src → electron}/services/companion-application.service.js +14 -16
- package/dist/{src/stores/companion-runtime-state.store.js → electron/services/companion-runtime-state-persistence.service.js} +3 -3
- package/dist/{src/stores/companion-window-position.store.js → electron/services/companion-window-position-persistence.service.js} +4 -4
- package/dist/{src → electron}/services/companion-window.service.js +15 -26
- package/dist/electron/types/companion-shell.types.js +2 -0
- package/dist/src/app/app.js +17 -0
- package/dist/src/app/components/companion-shell.container.js +20 -0
- package/dist/src/app/main.js +10 -0
- package/dist/src/app/providers/companion-presenter.provider.js +17 -0
- package/dist/src/managers/companion-runtime.manager.js +152 -0
- package/dist/src/managers/companion-runtime.manager.test.js +45 -0
- package/dist/src/managers/companion-shell.manager.js +22 -0
- package/dist/src/presenters/companion.presenter.js +37 -0
- package/dist/src/services/companion-runtime-client.service.js +16 -68
- package/dist/src/stores/companion-runtime.store.js +27 -0
- package/dist/src/stores/companion-shell.store.js +18 -0
- package/dist/ui/assets/dist-D4AJsX_N.js +1 -0
- package/dist/ui/assets/index-BHBNwOpX.js +8 -0
- package/dist/ui/assets/index-DPctoCcE.css +1 -0
- package/dist/ui/index.html +13 -0
- package/{src/launcher/index.ts → electron/launcher.ts} +2 -2
- package/{src → electron}/main.ts +4 -3
- package/electron/preload.ts +7 -0
- package/{src → electron}/services/companion-application.service.ts +16 -20
- package/{src/stores/companion-runtime-state.store.ts → electron/services/companion-runtime-state-persistence.service.ts} +1 -1
- package/{src/stores/companion-window-position.store.ts → electron/services/companion-window-position-persistence.service.ts} +3 -3
- package/{src → electron}/services/companion-window.service.ts +23 -30
- package/electron/types/companion-shell.types.ts +8 -0
- package/index.html +12 -0
- package/module-structure.config.json +25 -0
- package/package.json +21 -9
- package/postcss.config.mjs +6 -0
- package/scripts/smoke.mjs +4 -3
- package/src/app/app.tsx +17 -0
- package/src/app/components/companion-shell.container.tsx +67 -0
- package/src/app/index.css +37 -0
- package/src/app/main.tsx +15 -0
- package/src/app/providers/companion-presenter.provider.tsx +27 -0
- package/src/managers/companion-runtime.manager.test.ts +56 -0
- package/src/managers/companion-runtime.manager.ts +191 -0
- package/src/managers/companion-shell.manager.ts +21 -0
- package/src/module-structure.config.json +22 -0
- package/src/presenters/companion.presenter.ts +36 -0
- package/src/services/companion-runtime-client.service.ts +21 -84
- package/src/stores/companion-runtime.store.ts +51 -0
- package/src/stores/companion-shell.store.ts +27 -0
- package/src/types/companion-bridge.types.d.ts +11 -0
- package/src/types/companion.types.ts +0 -10
- package/tailwind.config.mjs +32 -0
- package/tsconfig.json +2 -1
- package/vite.config.mts +16 -0
- package/dist/src/companion-session-view.service.test.js +0 -27
- package/dist/src/services/companion-session-view.service.js +0 -52
- package/dist/src/utils/companion-renderer-html.utils.js +0 -194
- package/src/companion-session-view.service.test.ts +0 -37
- package/src/preload/index.ts +0 -13
- package/src/services/companion-session-view.service.ts +0 -63
- package/src/utils/companion-renderer-html.utils.ts +0 -192
- /package/dist/{src → electron}/services/companion-tray.service.js +0 -0
- /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
|
|
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, [
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
14
|
-
runtimeStateStore;
|
|
12
|
+
runtimeStatePersistenceService;
|
|
15
13
|
windowService;
|
|
16
14
|
trayService;
|
|
17
15
|
quitting = false;
|
|
18
16
|
constructor(options) {
|
|
19
17
|
this.options = options;
|
|
20
|
-
|
|
21
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
21
|
+
exports.CompanionRuntimeStatePersistenceService = CompanionRuntimeStatePersistenceService;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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
|
|
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
|
|
27
|
+
return new CompanionWindowPositionPersistenceService((0, node_path_1.resolve)(userDataPath, "companion-window.json"));
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
exports.
|
|
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
|
-
|
|
8
|
-
positionStore;
|
|
6
|
+
options;
|
|
9
7
|
window = null;
|
|
10
|
-
|
|
11
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
53
|
+
this.options.onQuit();
|
|
61
54
|
return null;
|
|
62
55
|
});
|
|
63
|
-
electron_1.ipcMain.
|
|
64
|
-
electron_1.ipcMain.
|
|
65
|
-
|
|
66
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
89
|
+
this.options.positionPersistenceService.write({
|
|
101
90
|
x: bounds.x,
|
|
102
91
|
y: bounds.y
|
|
103
92
|
});
|
|
@@ -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();
|