@nextclaw/service 0.1.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/LICENSE +21 -0
- package/dist/cli/commands/agent/agent-runtime.utils.d.ts +15 -0
- package/dist/cli/commands/agent/agent-runtime.utils.js +85 -0
- package/dist/cli/commands/agent/cli-agent-runner.utils.d.ts +21 -0
- package/dist/cli/commands/agent/cli-agent-runner.utils.js +89 -0
- package/dist/cli/commands/agent/index.d.ts +3 -0
- package/dist/cli/commands/agent/index.js +3 -0
- package/dist/cli/commands/agent/services/agent-commands.service.d.ts +17 -0
- package/dist/cli/commands/agent/services/agent-commands.service.js +112 -0
- package/dist/cli/commands/companion/index.d.ts +15 -0
- package/dist/cli/commands/companion/index.js +24 -0
- package/dist/cli/commands/companion/services/companion-process.service.d.ts +17 -0
- package/dist/cli/commands/companion/services/companion-process.service.js +49 -0
- package/dist/cli/commands/config/index.d.ts +2 -0
- package/dist/cli/commands/config/index.js +2 -0
- package/dist/cli/commands/config/services/config-commands.service.d.ts +18 -0
- package/dist/cli/commands/config/services/config-commands.service.js +133 -0
- package/dist/cli/commands/cron/index.d.ts +2 -0
- package/dist/cli/commands/cron/index.js +2 -0
- package/dist/cli/commands/cron/services/cron-commands.service.d.ts +22 -0
- package/dist/cli/commands/cron/services/cron-commands.service.js +107 -0
- package/dist/cli/commands/cron/services/cron-local.service.d.ts +25 -0
- package/dist/cli/commands/cron/services/cron-local.service.js +95 -0
- package/dist/cli/commands/cron/utils/cron-job.utils.d.ts +31 -0
- package/dist/cli/commands/cron/utils/cron-job.utils.js +15 -0
- package/dist/cli/commands/diagnostics/index.d.ts +2 -0
- package/dist/cli/commands/diagnostics/index.js +2 -0
- package/dist/cli/commands/diagnostics/services/diagnostics-commands.service.d.ts +22 -0
- package/dist/cli/commands/diagnostics/services/diagnostics-commands.service.js +319 -0
- package/dist/cli/commands/diagnostics/utils/diagnostics-render.utils.d.ts +23 -0
- package/dist/cli/commands/diagnostics/utils/diagnostics-render.utils.js +66 -0
- package/dist/cli/commands/gateway/index.d.ts +14 -0
- package/dist/cli/commands/gateway/index.js +15 -0
- package/dist/cli/commands/logs/index.d.ts +12 -0
- package/dist/cli/commands/logs/index.js +29 -0
- package/dist/cli/commands/mcp/index.d.ts +14 -0
- package/dist/cli/commands/mcp/index.js +193 -0
- package/dist/cli/commands/restart/index.d.ts +20 -0
- package/dist/cli/commands/restart/index.js +88 -0
- package/dist/cli/commands/secrets/index.d.ts +22 -0
- package/dist/cli/commands/secrets/index.js +280 -0
- package/dist/cli/commands/serve/index.d.ts +14 -0
- package/dist/cli/commands/serve/index.js +19 -0
- package/dist/cli/commands/skills/index.d.ts +26 -0
- package/dist/cli/commands/skills/index.js +147 -0
- package/dist/cli/commands/skills/marketplace-client.d.ts +31 -0
- package/dist/cli/commands/skills/marketplace-client.js +84 -0
- package/dist/cli/commands/skills/marketplace-command-options.utils.d.ts +25 -0
- package/dist/cli/commands/skills/marketplace-command-options.utils.js +31 -0
- package/dist/cli/commands/skills/marketplace-identity.utils.d.ts +14 -0
- package/dist/cli/commands/skills/marketplace-identity.utils.js +77 -0
- package/dist/cli/commands/skills/marketplace-network-retry.d.ts +4 -0
- package/dist/cli/commands/skills/marketplace-network-retry.js +32 -0
- package/dist/cli/commands/skills/marketplace.metadata.d.ts +29 -0
- package/dist/cli/commands/skills/marketplace.metadata.js +158 -0
- package/dist/cli/commands/skills/marketplace.service.d.ts +46 -0
- package/dist/cli/commands/skills/marketplace.service.js +238 -0
- package/dist/cli/commands/skills/skills-query.service.d.ts +141 -0
- package/dist/cli/commands/skills/skills-query.service.js +212 -0
- package/dist/cli/commands/start/index.d.ts +18 -0
- package/dist/cli/commands/start/index.js +25 -0
- package/dist/cli/commands/stop/index.d.ts +12 -0
- package/dist/cli/commands/stop/index.js +11 -0
- package/dist/cli/commands/ui/index.d.ts +14 -0
- package/dist/cli/commands/ui/index.js +17 -0
- package/dist/cli/commands/usage/index.d.ts +2 -0
- package/dist/cli/commands/usage/index.js +2 -0
- package/dist/cli/commands/usage/services/llm-usage-command.service.d.ts +22 -0
- package/dist/cli/commands/usage/services/llm-usage-command.service.js +160 -0
- package/dist/cli/commands/usage/services/llm-usage-query.service.d.ts +43 -0
- package/dist/cli/commands/usage/services/llm-usage-query.service.js +85 -0
- package/dist/commands/channel/channel-config-view.d.ts +7 -0
- package/dist/commands/channel/channel-config-view.js +7 -0
- package/dist/commands/channel/index.d.ts +28 -0
- package/dist/commands/channel/index.js +224 -0
- package/dist/commands/platform-auth/index.d.ts +2 -0
- package/dist/commands/platform-auth/index.js +2 -0
- package/dist/commands/platform-auth/services/account-status.service.d.ts +18 -0
- package/dist/commands/platform-auth/services/account-status.service.js +34 -0
- package/dist/commands/platform-auth/services/platform-auth-commands.service.d.ts +77 -0
- package/dist/commands/platform-auth/services/platform-auth-commands.service.js +295 -0
- package/dist/commands/platform-auth/utils/payload.utils.d.ts +28 -0
- package/dist/commands/platform-auth/utils/payload.utils.js +87 -0
- package/dist/commands/plugin/development-source/dev-plugin-overrides.utils.d.ts +18 -0
- package/dist/commands/plugin/development-source/dev-plugin-overrides.utils.js +111 -0
- package/dist/commands/plugin/development-source/first-party-plugin-load-paths.d.ts +9 -0
- package/dist/commands/plugin/development-source/first-party-plugin-load-paths.js +183 -0
- package/dist/commands/plugin/index.d.ts +30 -0
- package/dist/commands/plugin/index.js +266 -0
- package/dist/commands/plugin/plugin-command-utils.d.ts +13 -0
- package/dist/commands/plugin/plugin-command-utils.js +37 -0
- package/dist/commands/plugin/plugin-extension-registry.d.ts +10 -0
- package/dist/commands/plugin/plugin-extension-registry.js +35 -0
- package/dist/commands/plugin/plugin-mutation-actions.d.ts +15 -0
- package/dist/commands/plugin/plugin-mutation-actions.js +162 -0
- package/dist/commands/plugin/plugin-registry-loader.d.ts +15 -0
- package/dist/commands/plugin/plugin-registry-loader.js +43 -0
- package/dist/commands/plugin/plugin-reload.d.ts +13 -0
- package/dist/commands/plugin/plugin-reload.js +42 -0
- package/dist/commands/remote/index.d.ts +47 -0
- package/dist/commands/remote/index.js +174 -0
- package/dist/commands/remote/services/remote-access-host.service.d.ts +41 -0
- package/dist/commands/remote/services/remote-access-host.service.js +126 -0
- package/dist/commands/remote/services/remote-runtime-support.service.d.ts +15 -0
- package/dist/commands/remote/services/remote-runtime-support.service.js +79 -0
- package/dist/commands/remote/services/remote-service-control.service.d.ts +33 -0
- package/dist/commands/remote/services/remote-service-control.service.js +188 -0
- package/dist/commands/remote/utils/platform-api-base.utils.d.ts +14 -0
- package/dist/commands/remote/utils/platform-api-base.utils.js +39 -0
- package/dist/commands/service/index.d.ts +16 -0
- package/dist/commands/service/index.js +31 -0
- package/dist/commands/service/services/autostart/host-autostart-command.service.d.ts +29 -0
- package/dist/commands/service/services/autostart/host-autostart-command.service.js +158 -0
- package/dist/commands/service/services/autostart/host-autostart-runtime.service.d.ts +23 -0
- package/dist/commands/service/services/autostart/host-autostart-runtime.service.js +53 -0
- package/dist/commands/service/services/autostart/host-autostart.service.d.ts +41 -0
- package/dist/commands/service/services/autostart/host-autostart.service.js +48 -0
- package/dist/commands/service/services/autostart/linux-systemd-autostart.service.d.ts +48 -0
- package/dist/commands/service/services/autostart/linux-systemd-autostart.service.js +433 -0
- package/dist/commands/service/services/autostart/macos-launch-agent-autostart.service.d.ts +54 -0
- package/dist/commands/service/services/autostart/macos-launch-agent-autostart.service.js +405 -0
- package/dist/commands/service/services/autostart/windows-task-autostart.service.d.ts +54 -0
- package/dist/commands/service/services/autostart/windows-task-autostart.service.js +403 -0
- package/dist/commands/service/types/autostart/host-autostart.types.d.ts +64 -0
- package/dist/commands/service/types/autostart/host-autostart.types.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/launcher/npm-runtime-bundle-layout.store.d.ts +23 -0
- package/dist/launcher/npm-runtime-bundle-layout.store.js +37 -0
- package/dist/launcher/npm-runtime-bundle-manifest.service.d.ts +9 -0
- package/dist/launcher/npm-runtime-bundle-manifest.service.js +39 -0
- package/dist/launcher/npm-runtime-bundle.service.d.ts +47 -0
- package/dist/launcher/npm-runtime-bundle.service.js +150 -0
- package/dist/launcher/npm-runtime-bundle.types.d.ts +49 -0
- package/dist/launcher/npm-runtime-bundle.types.js +1 -0
- package/dist/launcher/npm-runtime-launcher.service.d.ts +19 -0
- package/dist/launcher/npm-runtime-launcher.service.js +57 -0
- package/dist/launcher/npm-runtime-update-command.service.d.ts +12 -0
- package/dist/launcher/npm-runtime-update-command.service.js +87 -0
- package/dist/launcher/npm-runtime-update-source.service.d.ts +19 -0
- package/dist/launcher/npm-runtime-update-source.service.js +57 -0
- package/dist/launcher/npm-runtime-update-state.store.d.ts +17 -0
- package/dist/launcher/npm-runtime-update-state.store.js +92 -0
- package/dist/launcher/npm-runtime-update.manager.d.ts +42 -0
- package/dist/launcher/npm-runtime-update.manager.js +179 -0
- package/dist/launcher/npm-runtime-update.service.d.ts +54 -0
- package/dist/launcher/npm-runtime-update.service.js +183 -0
- package/dist/service-runtime.service.d.ts +91 -0
- package/dist/service-runtime.service.js +392 -0
- package/dist/shared/controllers/gateway.controller.d.ts +61 -0
- package/dist/shared/controllers/gateway.controller.js +318 -0
- package/dist/shared/services/extensions/extension-lifecycle.service.d.ts +56 -0
- package/dist/shared/services/extensions/extension-lifecycle.service.js +143 -0
- package/dist/shared/services/extensions/service-extension-runtime.service.d.ts +51 -0
- package/dist/shared/services/extensions/service-extension-runtime.service.js +338 -0
- package/dist/shared/services/gateway/cron-job-handler.service.d.ts +26 -0
- package/dist/shared/services/gateway/cron-job-handler.service.js +100 -0
- package/dist/shared/services/gateway/gateway-restart-wake.service.d.ts +12 -0
- package/dist/shared/services/gateway/gateway-restart-wake.service.js +91 -0
- package/dist/shared/services/gateway/managers/gateway-plugin.manager.d.ts +37 -0
- package/dist/shared/services/gateway/managers/gateway-plugin.manager.js +218 -0
- package/dist/shared/services/gateway/managers/gateway-remote.manager.d.ts +20 -0
- package/dist/shared/services/gateway/managers/gateway-remote.manager.js +25 -0
- package/dist/shared/services/gateway/nextclaw-app.service.d.ts +22 -0
- package/dist/shared/services/gateway/nextclaw-app.service.js +53 -0
- package/dist/shared/services/gateway/nextclaw-gateway-runtime.service.d.ts +89 -0
- package/dist/shared/services/gateway/nextclaw-gateway-runtime.service.js +337 -0
- package/dist/shared/services/gateway/service-bootstrap-status.d.ts +33 -0
- package/dist/shared/services/gateway/service-bootstrap-status.js +152 -0
- package/dist/shared/services/gateway/service-startup-support.service.d.ts +42 -0
- package/dist/shared/services/gateway/service-startup-support.service.js +96 -0
- package/dist/shared/services/gateway/utils/gateway-runtime-lifecycle.utils.d.ts +9 -0
- package/dist/shared/services/gateway/utils/gateway-runtime-lifecycle.utils.js +10 -0
- package/dist/shared/services/marketplace/service-marketplace-installer.service.d.ts +31 -0
- package/dist/shared/services/marketplace/service-marketplace-installer.service.js +99 -0
- package/dist/shared/services/marketplace/service-mcp-marketplace-ops.d.ts +39 -0
- package/dist/shared/services/marketplace/service-mcp-marketplace-ops.js +67 -0
- package/dist/shared/services/plugin/utils/plugin-dev-hot-reload.utils.d.ts +24 -0
- package/dist/shared/services/plugin/utils/plugin-dev-hot-reload.utils.js +117 -0
- package/dist/shared/services/plugin/utils/plugin-runtime-bridge.utils.d.ts +6 -0
- package/dist/shared/services/plugin/utils/plugin-runtime-bridge.utils.js +96 -0
- package/dist/shared/services/restart/restart-coordinator.service.d.ts +30 -0
- package/dist/shared/services/restart/restart-coordinator.service.js +51 -0
- package/dist/shared/services/restart/restart-sentinel.service.d.ts +39 -0
- package/dist/shared/services/restart/restart-sentinel.service.js +88 -0
- package/dist/shared/services/restart/runtime-restart-request.service.d.ts +24 -0
- package/dist/shared/services/restart/runtime-restart-request.service.js +42 -0
- package/dist/shared/services/runtime/runtime-command.service.d.ts +37 -0
- package/dist/shared/services/runtime/runtime-command.service.js +163 -0
- package/dist/shared/services/runtime/runtime-config-init.service.d.ts +4 -0
- package/dist/shared/services/runtime/runtime-config-init.service.js +10 -0
- package/dist/shared/services/runtime/service-managed-startup.service.d.ts +146 -0
- package/dist/shared/services/runtime/service-managed-startup.service.js +426 -0
- package/dist/shared/services/runtime/service-remote-runtime.service.d.ts +53 -0
- package/dist/shared/services/runtime/service-remote-runtime.service.js +173 -0
- package/dist/shared/services/runtime/utils/skills-loader.utils.d.ts +12 -0
- package/dist/shared/services/runtime/utils/skills-loader.utils.js +9 -0
- package/dist/shared/services/session/service-deferred-ncp-agent.service.d.ts +14 -0
- package/dist/shared/services/session/service-deferred-ncp-agent.service.js +85 -0
- package/dist/shared/services/ui/companion-runtime.service.d.ts +33 -0
- package/dist/shared/services/ui/companion-runtime.service.js +145 -0
- package/dist/shared/services/ui/local-ui-discovery.service.d.ts +19 -0
- package/dist/shared/services/ui/local-ui-discovery.service.js +41 -0
- package/dist/shared/services/ui/npm-runtime-update-host.service.d.ts +40 -0
- package/dist/shared/services/ui/npm-runtime-update-host.service.js +181 -0
- package/dist/shared/services/ui/runtime-control-host.service.d.ts +28 -0
- package/dist/shared/services/ui/runtime-control-host.service.js +89 -0
- package/dist/shared/services/ui/service-remote-access.service.d.ts +25 -0
- package/dist/shared/services/ui/service-remote-access.service.js +38 -0
- package/dist/shared/services/ui/ui-bridge-api.service.d.ts +16 -0
- package/dist/shared/services/ui/ui-bridge-api.service.js +43 -0
- package/dist/shared/services/workspace/workspace-manager.service.d.ts +19 -0
- package/dist/shared/services/workspace/workspace-manager.service.js +135 -0
- package/dist/shared/stores/companion-runtime.store.d.ts +15 -0
- package/dist/shared/stores/companion-runtime.store.js +27 -0
- package/dist/shared/stores/local-ui-runtime.store.d.ts +25 -0
- package/dist/shared/stores/local-ui-runtime.store.js +54 -0
- package/dist/shared/stores/managed-service-state.store.d.ts +28 -0
- package/dist/shared/stores/managed-service-state.store.js +38 -0
- package/dist/shared/stores/pending-restart.store.d.ts +21 -0
- package/dist/shared/stores/pending-restart.store.js +35 -0
- package/dist/shared/types/cli.types.d.ts +295 -0
- package/dist/shared/types/cli.types.js +1 -0
- package/dist/shared/utils/cli.utils.d.ts +34 -0
- package/dist/shared/utils/cli.utils.js +262 -0
- package/dist/shared/utils/config-path.d.ts +15 -0
- package/dist/shared/utils/config-path.js +167 -0
- package/dist/shared/utils/marketplace/cli-subcommand-launch.utils.d.ts +16 -0
- package/dist/shared/utils/marketplace/cli-subcommand-launch.utils.js +46 -0
- package/dist/shared/utils/marketplace/service-marketplace-helpers.utils.d.ts +9 -0
- package/dist/shared/utils/marketplace/service-marketplace-helpers.utils.js +33 -0
- package/dist/shared/utils/package/package-manifest.utils.d.ts +8 -0
- package/dist/shared/utils/package/package-manifest.utils.js +48 -0
- package/dist/shared/utils/runtime-helpers.d.ts +14 -0
- package/dist/shared/utils/runtime-helpers.js +26 -0
- package/dist/shared/utils/service-port-probe.utils.d.ts +41 -0
- package/dist/shared/utils/service-port-probe.utils.js +164 -0
- package/dist/shared/utils/startup-trace.d.ts +7 -0
- package/dist/shared/utils/startup-trace.js +37 -0
- package/dist/shared/utils/top-level-nextclaw-command-env.utils.d.ts +4 -0
- package/dist/shared/utils/top-level-nextclaw-command-env.utils.js +10 -0
- package/package.json +68 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
//#region src/shared/utils/config-path.ts
|
|
2
|
+
function isIndexSegment(raw) {
|
|
3
|
+
return /^[0-9]+$/.test(raw);
|
|
4
|
+
}
|
|
5
|
+
function parseConfigPath(raw) {
|
|
6
|
+
const trimmed = raw.trim();
|
|
7
|
+
if (!trimmed) return [];
|
|
8
|
+
const parts = [];
|
|
9
|
+
let current = "";
|
|
10
|
+
let i = 0;
|
|
11
|
+
while (i < trimmed.length) {
|
|
12
|
+
const ch = trimmed[i];
|
|
13
|
+
if (ch === "\\") {
|
|
14
|
+
const next = trimmed[i + 1];
|
|
15
|
+
if (next) current += next;
|
|
16
|
+
i += 2;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (ch === ".") {
|
|
20
|
+
if (current) parts.push(current);
|
|
21
|
+
current = "";
|
|
22
|
+
i += 1;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (ch === "[") {
|
|
26
|
+
if (current) parts.push(current);
|
|
27
|
+
current = "";
|
|
28
|
+
const close = trimmed.indexOf("]", i);
|
|
29
|
+
if (close === -1) throw new Error(`Invalid path (missing "]"): ${raw}`);
|
|
30
|
+
const inside = trimmed.slice(i + 1, close).trim();
|
|
31
|
+
if (!inside) throw new Error(`Invalid path (empty "[]"): ${raw}`);
|
|
32
|
+
parts.push(inside);
|
|
33
|
+
i = close + 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
current += ch;
|
|
37
|
+
i += 1;
|
|
38
|
+
}
|
|
39
|
+
if (current) parts.push(current);
|
|
40
|
+
return parts.map((part) => part.trim()).filter(Boolean);
|
|
41
|
+
}
|
|
42
|
+
function parseRequiredConfigPath(raw) {
|
|
43
|
+
const parsedPath = parseConfigPath(raw);
|
|
44
|
+
if (parsedPath.length === 0) throw new Error("Path is empty.");
|
|
45
|
+
return parsedPath;
|
|
46
|
+
}
|
|
47
|
+
function parseConfigSetValue(raw, opts) {
|
|
48
|
+
const trimmed = raw.trim();
|
|
49
|
+
if (opts.json) return JSON.parse(trimmed);
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(trimmed);
|
|
52
|
+
} catch {
|
|
53
|
+
return raw;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function getAtConfigPath(root, pathSegments) {
|
|
57
|
+
let current = root;
|
|
58
|
+
for (const segment of pathSegments) {
|
|
59
|
+
if (!current || typeof current !== "object") return { found: false };
|
|
60
|
+
if (Array.isArray(current)) {
|
|
61
|
+
if (!isIndexSegment(segment)) return { found: false };
|
|
62
|
+
const index = Number.parseInt(segment, 10);
|
|
63
|
+
if (!Number.isFinite(index) || index < 0 || index >= current.length) return { found: false };
|
|
64
|
+
current = current[index];
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const record = current;
|
|
68
|
+
if (!Object.prototype.hasOwnProperty.call(record, segment)) return { found: false };
|
|
69
|
+
current = record[segment];
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
found: true,
|
|
73
|
+
value: current
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function setAtConfigPath(root, pathSegments, value) {
|
|
77
|
+
let current = root;
|
|
78
|
+
for (let i = 0; i < pathSegments.length - 1; i += 1) {
|
|
79
|
+
const segment = pathSegments[i];
|
|
80
|
+
const next = pathSegments[i + 1];
|
|
81
|
+
const nextIsIndex = Boolean(next && isIndexSegment(next));
|
|
82
|
+
if (Array.isArray(current)) {
|
|
83
|
+
current = getOrCreateArraySegment(current, segment, nextIsIndex, pathSegments.slice(0, i));
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
current = getOrCreateObjectSegment(current, segment, nextIsIndex);
|
|
87
|
+
}
|
|
88
|
+
setConfigPathValue(current, pathSegments[pathSegments.length - 1], value, pathSegments.slice(0, -1));
|
|
89
|
+
}
|
|
90
|
+
function ensureWritableArrayIndex(current, index, parentPath) {
|
|
91
|
+
if (!Number.isFinite(index) || index < 0) throw new Error(`Invalid array index "${index}"`);
|
|
92
|
+
if (index <= current.length) return;
|
|
93
|
+
const parentPathExpr = formatConfigPath(parentPath);
|
|
94
|
+
const parentLabel = parentPathExpr ? `"${parentPathExpr}"` : "the root array";
|
|
95
|
+
throw new Error(`Cannot set sparse array index ${index} under ${parentLabel}. Set indices in order.`);
|
|
96
|
+
}
|
|
97
|
+
function getOrCreateArraySegment(current, segment, nextIsIndex, parentPath) {
|
|
98
|
+
const index = parseArrayIndexSegment(segment);
|
|
99
|
+
ensureWritableArrayIndex(current, index, parentPath);
|
|
100
|
+
const existing = current[index];
|
|
101
|
+
if (!existing || typeof existing !== "object") current[index] = nextIsIndex ? [] : {};
|
|
102
|
+
return current[index];
|
|
103
|
+
}
|
|
104
|
+
function getOrCreateObjectSegment(current, segment, nextIsIndex) {
|
|
105
|
+
if (!current || typeof current !== "object") throw new Error(`Cannot traverse into "${segment}" (not an object)`);
|
|
106
|
+
const record = current;
|
|
107
|
+
const existing = record[segment];
|
|
108
|
+
if (!existing || typeof existing !== "object") record[segment] = nextIsIndex ? [] : {};
|
|
109
|
+
return record[segment];
|
|
110
|
+
}
|
|
111
|
+
function setConfigPathValue(current, last, value, parentPath) {
|
|
112
|
+
if (Array.isArray(current)) {
|
|
113
|
+
const index = parseArrayIndexSegment(last);
|
|
114
|
+
ensureWritableArrayIndex(current, index, parentPath);
|
|
115
|
+
current[index] = value;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!current || typeof current !== "object") throw new Error(`Cannot set "${last}" (parent is not an object)`);
|
|
119
|
+
current[last] = value;
|
|
120
|
+
}
|
|
121
|
+
function parseArrayIndexSegment(segment) {
|
|
122
|
+
if (!isIndexSegment(segment)) throw new Error(`Expected numeric index for array segment "${segment}"`);
|
|
123
|
+
return Number.parseInt(segment, 10);
|
|
124
|
+
}
|
|
125
|
+
function formatConfigPath(pathSegments) {
|
|
126
|
+
let expr = "";
|
|
127
|
+
for (const segment of pathSegments) {
|
|
128
|
+
if (isIndexSegment(segment)) {
|
|
129
|
+
expr += `[${segment}]`;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
expr += expr ? `.${segment}` : segment;
|
|
133
|
+
}
|
|
134
|
+
return expr;
|
|
135
|
+
}
|
|
136
|
+
function unsetAtConfigPath(root, pathSegments) {
|
|
137
|
+
let current = root;
|
|
138
|
+
for (let i = 0; i < pathSegments.length - 1; i += 1) {
|
|
139
|
+
const segment = pathSegments[i];
|
|
140
|
+
if (!current || typeof current !== "object") return false;
|
|
141
|
+
if (Array.isArray(current)) {
|
|
142
|
+
if (!isIndexSegment(segment)) return false;
|
|
143
|
+
const index = Number.parseInt(segment, 10);
|
|
144
|
+
if (!Number.isFinite(index) || index < 0 || index >= current.length) return false;
|
|
145
|
+
current = current[index];
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const record = current;
|
|
149
|
+
if (!Object.prototype.hasOwnProperty.call(record, segment)) return false;
|
|
150
|
+
current = record[segment];
|
|
151
|
+
}
|
|
152
|
+
const last = pathSegments[pathSegments.length - 1];
|
|
153
|
+
if (Array.isArray(current)) {
|
|
154
|
+
if (!isIndexSegment(last)) return false;
|
|
155
|
+
const index = Number.parseInt(last, 10);
|
|
156
|
+
if (!Number.isFinite(index) || index < 0 || index >= current.length) return false;
|
|
157
|
+
current.splice(index, 1);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
if (!current || typeof current !== "object") return false;
|
|
161
|
+
const record = current;
|
|
162
|
+
if (!Object.prototype.hasOwnProperty.call(record, last)) return false;
|
|
163
|
+
delete record[last];
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
export { getAtConfigPath, isIndexSegment, parseConfigPath, parseConfigSetValue, parseRequiredConfigPath, setAtConfigPath, unsetAtConfigPath };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/shared/utils/marketplace/cli-subcommand-launch.utils.d.ts
|
|
2
|
+
declare const resolveCliSubcommandEntry: (params: {
|
|
3
|
+
argvEntry?: string;
|
|
4
|
+
importMetaUrl: string;
|
|
5
|
+
}) => string;
|
|
6
|
+
declare const resolveCliSubcommandLaunch: (params: {
|
|
7
|
+
argvEntry?: string;
|
|
8
|
+
importMetaUrl: string;
|
|
9
|
+
cliArgs: string[];
|
|
10
|
+
nodePath?: string;
|
|
11
|
+
}) => {
|
|
12
|
+
command: string;
|
|
13
|
+
args: string[];
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { resolveCliSubcommandEntry, resolveCliSubcommandLaunch };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { extname, resolve } from "node:path";
|
|
4
|
+
//#region src/shared/utils/marketplace/cli-subcommand-launch.utils.ts
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const TYPESCRIPT_EXTENSIONS = new Set([
|
|
7
|
+
".ts",
|
|
8
|
+
".tsx",
|
|
9
|
+
".mts",
|
|
10
|
+
".cts"
|
|
11
|
+
]);
|
|
12
|
+
const isTypeScriptEntry = (entry) => TYPESCRIPT_EXTENSIONS.has(extname(entry).toLowerCase());
|
|
13
|
+
const resolveTsxCliEntry = () => require.resolve("tsx/cli");
|
|
14
|
+
const resolveCliAppEntryFromImportMeta = (importMetaUrl) => {
|
|
15
|
+
const modulePath = fileURLToPath(importMetaUrl);
|
|
16
|
+
const cliRootIndex = modulePath.replace(/\\/g, "/").lastIndexOf("/cli/");
|
|
17
|
+
if (cliRootIndex === -1) return fileURLToPath(new URL("../../../app/index.js", importMetaUrl));
|
|
18
|
+
const extension = extname(modulePath) || ".js";
|
|
19
|
+
return resolve(modulePath.slice(0, cliRootIndex + 5), "app", `index${extension}`);
|
|
20
|
+
};
|
|
21
|
+
const resolveCliSubcommandEntry = (params) => {
|
|
22
|
+
const argvEntry = params.argvEntry?.trim();
|
|
23
|
+
if (argvEntry) return resolve(argvEntry);
|
|
24
|
+
return resolveCliAppEntryFromImportMeta(params.importMetaUrl);
|
|
25
|
+
};
|
|
26
|
+
const resolveCliSubcommandLaunch = (params) => {
|
|
27
|
+
const cliEntry = resolveCliSubcommandEntry({
|
|
28
|
+
argvEntry: params.argvEntry,
|
|
29
|
+
importMetaUrl: params.importMetaUrl
|
|
30
|
+
});
|
|
31
|
+
const command = params.nodePath?.trim() || process.execPath;
|
|
32
|
+
if (isTypeScriptEntry(cliEntry)) return {
|
|
33
|
+
command,
|
|
34
|
+
args: [
|
|
35
|
+
resolveTsxCliEntry(),
|
|
36
|
+
cliEntry,
|
|
37
|
+
...params.cliArgs
|
|
38
|
+
]
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
command,
|
|
42
|
+
args: [cliEntry, ...params.cliArgs]
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
export { resolveCliSubcommandEntry, resolveCliSubcommandLaunch };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/shared/utils/marketplace/service-marketplace-helpers.utils.d.ts
|
|
2
|
+
declare const pickUserFacingCommandSummary: (output: string, fallback: string) => string;
|
|
3
|
+
declare const buildMarketplaceSkillInstallArgs: (params: {
|
|
4
|
+
slug: string;
|
|
5
|
+
workspace: string;
|
|
6
|
+
force?: boolean;
|
|
7
|
+
}) => string[];
|
|
8
|
+
//#endregion
|
|
9
|
+
export { buildMarketplaceSkillInstallArgs, pickUserFacingCommandSummary };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/shared/utils/marketplace/service-marketplace-helpers.utils.ts
|
|
2
|
+
const containsAbsoluteFsPath = (line) => {
|
|
3
|
+
const normalized = line.trim();
|
|
4
|
+
if (!normalized) return false;
|
|
5
|
+
const lowered = normalized.toLowerCase();
|
|
6
|
+
if (lowered.includes("http://") || lowered.includes("https://")) return false;
|
|
7
|
+
if (/^[A-Za-z]:\\/.test(normalized)) return true;
|
|
8
|
+
return /(?:^|\s)(?:~\/|\/[^\s]+)/.test(normalized);
|
|
9
|
+
};
|
|
10
|
+
const pickUserFacingCommandSummary = (output, fallback) => {
|
|
11
|
+
const lines = output.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
12
|
+
if (lines.length === 0) return fallback;
|
|
13
|
+
const visibleLines = lines.filter((line) => {
|
|
14
|
+
if (/^(path|install path|source path|destination|location)\s*:/i.test(line)) return false;
|
|
15
|
+
if (containsAbsoluteFsPath(line)) return false;
|
|
16
|
+
return true;
|
|
17
|
+
});
|
|
18
|
+
if (visibleLines.length === 0) return fallback;
|
|
19
|
+
return [...visibleLines].reverse().find((line) => /\b(installed|enabled|disabled|uninstalled|published|updated|already installed|removed)\b/i.test(line)) ?? visibleLines[visibleLines.length - 1] ?? fallback;
|
|
20
|
+
};
|
|
21
|
+
const buildMarketplaceSkillInstallArgs = (params) => {
|
|
22
|
+
const args = [
|
|
23
|
+
"skills",
|
|
24
|
+
"install",
|
|
25
|
+
params.slug,
|
|
26
|
+
"--workdir",
|
|
27
|
+
params.workspace
|
|
28
|
+
];
|
|
29
|
+
if (params.force) args.push("--force");
|
|
30
|
+
return args;
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { buildMarketplaceSkillInstallArgs, pickUserFacingCommandSummary };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/shared/utils/package/package-manifest.utils.d.ts
|
|
2
|
+
declare function findNearestPackageManifest(startDir: string, expectedName?: string): {
|
|
3
|
+
rootDir: string;
|
|
4
|
+
version?: string;
|
|
5
|
+
} | null;
|
|
6
|
+
declare function getPackageVersion(importMetaUrl?: string): string;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { findNearestPackageManifest, getPackageVersion };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { getPackageVersion as getPackageVersion$1 } from "@nextclaw/core";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
//#region src/shared/utils/package/package-manifest.utils.ts
|
|
6
|
+
function readPackageManifest(pkgPath) {
|
|
7
|
+
try {
|
|
8
|
+
const raw = readFileSync(pkgPath, "utf-8");
|
|
9
|
+
return JSON.parse(raw);
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function findNearestPackageManifest(startDir, expectedName) {
|
|
15
|
+
let current = resolve(startDir);
|
|
16
|
+
while (current.length > 0) {
|
|
17
|
+
const parsed = readPackageManifest(join(current, "package.json"));
|
|
18
|
+
if (parsed && (!expectedName || parsed.name === expectedName)) return {
|
|
19
|
+
rootDir: current,
|
|
20
|
+
version: typeof parsed.version === "string" ? parsed.version : void 0
|
|
21
|
+
};
|
|
22
|
+
const parent = resolve(current, "..");
|
|
23
|
+
if (parent === current) break;
|
|
24
|
+
current = parent;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function findWorkspacePackageManifest(startDir, packageName) {
|
|
29
|
+
let current = resolve(startDir);
|
|
30
|
+
while (current.length > 0) {
|
|
31
|
+
const rootDir = join(current, "packages", packageName);
|
|
32
|
+
const parsed = readPackageManifest(join(rootDir, "package.json"));
|
|
33
|
+
if (parsed?.name === packageName) return {
|
|
34
|
+
rootDir,
|
|
35
|
+
version: typeof parsed.version === "string" ? parsed.version : void 0
|
|
36
|
+
};
|
|
37
|
+
const parent = resolve(current, "..");
|
|
38
|
+
if (parent === current) break;
|
|
39
|
+
current = parent;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function getPackageVersion(importMetaUrl = import.meta.url) {
|
|
44
|
+
const cliDir = resolve(fileURLToPath(new URL(".", importMetaUrl)));
|
|
45
|
+
return findNearestPackageManifest(cliDir, "nextclaw")?.version ?? findWorkspacePackageManifest(cliDir, "nextclaw")?.version ?? findNearestPackageManifest(cliDir)?.version ?? getPackageVersion$1();
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { findNearestPackageManifest, getPackageVersion };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Config } from "@nextclaw/core";
|
|
2
|
+
|
|
3
|
+
//#region src/shared/utils/runtime-helpers.d.ts
|
|
4
|
+
declare function resolveSkillsInstallWorkdir(params: {
|
|
5
|
+
explicitWorkdir?: string;
|
|
6
|
+
configuredWorkspace?: string;
|
|
7
|
+
}): string;
|
|
8
|
+
declare function parseStartTimeoutMs(value: string | number | undefined): number | undefined;
|
|
9
|
+
declare function resolveManagedServiceUiOverrides(params: {
|
|
10
|
+
uiPort: string | number | undefined;
|
|
11
|
+
forcedPublicHost: string;
|
|
12
|
+
}): Partial<Config["ui"]>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { parseStartTimeoutMs, resolveManagedServiceUiOverrides, resolveSkillsInstallWorkdir };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { expandHome, getWorkspacePath } from "@nextclaw/core";
|
|
2
|
+
//#region src/shared/utils/runtime-helpers.ts
|
|
3
|
+
function resolveSkillsInstallWorkdir(params) {
|
|
4
|
+
if (params.explicitWorkdir) return expandHome(params.explicitWorkdir);
|
|
5
|
+
return getWorkspacePath(params.configuredWorkspace);
|
|
6
|
+
}
|
|
7
|
+
function parseStartTimeoutMs(value) {
|
|
8
|
+
if (value === void 0) return;
|
|
9
|
+
const parsed = Number(value);
|
|
10
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
11
|
+
console.error("Invalid --start-timeout value. Provide milliseconds (e.g. 45000).");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
return Math.floor(parsed);
|
|
15
|
+
}
|
|
16
|
+
function resolveManagedServiceUiOverrides(params) {
|
|
17
|
+
const uiOverrides = {
|
|
18
|
+
enabled: true,
|
|
19
|
+
host: params.forcedPublicHost,
|
|
20
|
+
open: false
|
|
21
|
+
};
|
|
22
|
+
if (params.uiPort) uiOverrides.port = Number(params.uiPort);
|
|
23
|
+
return uiOverrides;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { parseStartTimeoutMs, resolveManagedServiceUiOverrides, resolveSkillsInstallWorkdir };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Config } from "@nextclaw/core";
|
|
2
|
+
|
|
3
|
+
//#region src/shared/utils/service-port-probe.utils.d.ts
|
|
4
|
+
declare function checkPortAvailability(params: {
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
available: boolean;
|
|
9
|
+
detail: string;
|
|
10
|
+
}>;
|
|
11
|
+
declare function probeHealthEndpoint(healthUrl: string): Promise<{
|
|
12
|
+
healthy: boolean;
|
|
13
|
+
error: string | null;
|
|
14
|
+
}>;
|
|
15
|
+
type UiTargetProbeResult = {
|
|
16
|
+
state: "available";
|
|
17
|
+
availabilityDetail: string;
|
|
18
|
+
probeError: null;
|
|
19
|
+
} | {
|
|
20
|
+
state: "healthy-existing";
|
|
21
|
+
availabilityDetail: string;
|
|
22
|
+
probeError: null;
|
|
23
|
+
} | {
|
|
24
|
+
state: "occupied-unhealthy";
|
|
25
|
+
availabilityDetail: string;
|
|
26
|
+
probeError: string | null;
|
|
27
|
+
};
|
|
28
|
+
declare function inspectUiTarget(params: {
|
|
29
|
+
host: string;
|
|
30
|
+
port: number;
|
|
31
|
+
healthUrl: string;
|
|
32
|
+
checkPortAvailabilityFn?: typeof checkPortAvailability;
|
|
33
|
+
probeHealthEndpointFn?: typeof probeHealthEndpoint;
|
|
34
|
+
}): Promise<UiTargetProbeResult>;
|
|
35
|
+
declare function describeUnmanagedHealthyTargetMessage(params: {
|
|
36
|
+
uiOverrides: Partial<Config["ui"]>;
|
|
37
|
+
checkPortAvailabilityFn?: typeof checkPortAvailability;
|
|
38
|
+
probeHealthEndpointFn?: typeof probeHealthEndpoint;
|
|
39
|
+
}): Promise<string | null>;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { UiTargetProbeResult, checkPortAvailability, describeUnmanagedHealthyTargetMessage, inspectUiTarget, probeHealthEndpoint };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { resolveUiApiBase, resolveUiConfig } from "./cli.utils.js";
|
|
2
|
+
import { managedServiceStateStore } from "../stores/managed-service-state.store.js";
|
|
3
|
+
import { APP_NAME, loadConfig } from "@nextclaw/core";
|
|
4
|
+
import { createServer } from "node:net";
|
|
5
|
+
import { request } from "node:http";
|
|
6
|
+
import { request as request$1 } from "node:https";
|
|
7
|
+
//#region src/shared/utils/service-port-probe.utils.ts
|
|
8
|
+
function getHeaderValue(headers, key) {
|
|
9
|
+
const value = headers[key];
|
|
10
|
+
if (typeof value === "string") {
|
|
11
|
+
const normalized = value.trim();
|
|
12
|
+
return normalized.length > 0 ? normalized : null;
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
const joined = value.map((item) => item.trim()).filter(Boolean).join(", ");
|
|
16
|
+
return joined.length > 0 ? joined : null;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
function formatProbeBodySnippet(raw, maxLength = 180) {
|
|
21
|
+
const normalized = raw.replace(/\s+/g, " ").trim();
|
|
22
|
+
if (!normalized) return null;
|
|
23
|
+
const clipped = normalized.length > maxLength ? `${normalized.slice(0, maxLength)}...` : normalized;
|
|
24
|
+
return JSON.stringify(clipped);
|
|
25
|
+
}
|
|
26
|
+
async function checkPortAvailability(params) {
|
|
27
|
+
return await new Promise((resolve) => {
|
|
28
|
+
const server = createServer();
|
|
29
|
+
server.once("error", (error) => {
|
|
30
|
+
resolve({
|
|
31
|
+
available: false,
|
|
32
|
+
detail: `bind failed on ${params.host}:${params.port} (${String(error)})`
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
server.listen(params.port, params.host, () => {
|
|
36
|
+
server.close(() => {
|
|
37
|
+
resolve({
|
|
38
|
+
available: true,
|
|
39
|
+
detail: `bind ok on ${params.host}:${params.port}`
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function probeHealthEndpoint(healthUrl) {
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = new URL(healthUrl);
|
|
49
|
+
} catch {
|
|
50
|
+
return {
|
|
51
|
+
healthy: false,
|
|
52
|
+
error: "invalid health URL"
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const requestImpl = parsed.protocol === "https:" ? request$1 : request;
|
|
56
|
+
return await new Promise((resolve) => {
|
|
57
|
+
const req = requestImpl({
|
|
58
|
+
protocol: parsed.protocol,
|
|
59
|
+
hostname: parsed.hostname,
|
|
60
|
+
port: parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80,
|
|
61
|
+
method: "GET",
|
|
62
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
63
|
+
timeout: 1e3,
|
|
64
|
+
headers: { Accept: "application/json" }
|
|
65
|
+
}, (res) => {
|
|
66
|
+
const chunks = [];
|
|
67
|
+
res.on("data", (chunk) => {
|
|
68
|
+
if (typeof chunk === "string") {
|
|
69
|
+
chunks.push(Buffer.from(chunk));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
chunks.push(chunk);
|
|
73
|
+
});
|
|
74
|
+
res.on("end", () => {
|
|
75
|
+
const responseText = Buffer.concat(chunks).toString("utf-8");
|
|
76
|
+
if ((res.statusCode ?? 0) < 200 || (res.statusCode ?? 0) >= 300) {
|
|
77
|
+
const serverHeader = getHeaderValue(res.headers, "server");
|
|
78
|
+
const contentType = getHeaderValue(res.headers, "content-type");
|
|
79
|
+
const bodySnippet = formatProbeBodySnippet(responseText);
|
|
80
|
+
const details = [`http ${res.statusCode ?? "unknown"}`];
|
|
81
|
+
if (serverHeader) details.push(`server=${serverHeader}`);
|
|
82
|
+
if (contentType) details.push(`content-type=${contentType}`);
|
|
83
|
+
if (bodySnippet) details.push(`body=${bodySnippet}`);
|
|
84
|
+
resolve({
|
|
85
|
+
healthy: false,
|
|
86
|
+
error: details.join("; ")
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const payload = JSON.parse(responseText);
|
|
92
|
+
if (!(payload?.ok === true && payload?.data?.status === "ok")) {
|
|
93
|
+
resolve({
|
|
94
|
+
healthy: false,
|
|
95
|
+
error: "health payload not ok"
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
resolve({
|
|
100
|
+
healthy: true,
|
|
101
|
+
error: null
|
|
102
|
+
});
|
|
103
|
+
} catch {
|
|
104
|
+
resolve({
|
|
105
|
+
healthy: false,
|
|
106
|
+
error: "invalid health JSON response"
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
req.on("timeout", () => {
|
|
112
|
+
req.destroy(/* @__PURE__ */ new Error("probe timeout"));
|
|
113
|
+
});
|
|
114
|
+
req.on("error", (error) => {
|
|
115
|
+
resolve({
|
|
116
|
+
healthy: false,
|
|
117
|
+
error: error.message || String(error)
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
req.end();
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async function inspectUiTarget(params) {
|
|
124
|
+
const { checkPortAvailabilityFn, healthUrl, host, port, probeHealthEndpointFn } = params;
|
|
125
|
+
const availability = await (checkPortAvailabilityFn ?? checkPortAvailability)({
|
|
126
|
+
host,
|
|
127
|
+
port
|
|
128
|
+
});
|
|
129
|
+
if (availability.available) return {
|
|
130
|
+
state: "available",
|
|
131
|
+
availabilityDetail: availability.detail,
|
|
132
|
+
probeError: null
|
|
133
|
+
};
|
|
134
|
+
const probe = await (probeHealthEndpointFn ?? probeHealthEndpoint)(healthUrl);
|
|
135
|
+
if (probe.healthy) return {
|
|
136
|
+
state: "healthy-existing",
|
|
137
|
+
availabilityDetail: availability.detail,
|
|
138
|
+
probeError: null
|
|
139
|
+
};
|
|
140
|
+
return {
|
|
141
|
+
state: "occupied-unhealthy",
|
|
142
|
+
availabilityDetail: availability.detail,
|
|
143
|
+
probeError: probe.error
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async function describeUnmanagedHealthyTargetMessage(params) {
|
|
147
|
+
const uiConfig = resolveUiConfig(loadConfig(), params.uiOverrides);
|
|
148
|
+
const healthUrl = `${resolveUiApiBase(uiConfig.host, uiConfig.port)}/api/health`;
|
|
149
|
+
if ((await inspectUiTarget({
|
|
150
|
+
host: uiConfig.host,
|
|
151
|
+
port: uiConfig.port,
|
|
152
|
+
healthUrl,
|
|
153
|
+
checkPortAvailabilityFn: params.checkPortAvailabilityFn,
|
|
154
|
+
probeHealthEndpointFn: params.probeHealthEndpointFn
|
|
155
|
+
})).state !== "healthy-existing") return null;
|
|
156
|
+
return [
|
|
157
|
+
`Target UI health: ${healthUrl}`,
|
|
158
|
+
`A healthy ${APP_NAME} service is already responding on this port, but it is not tracked by ${managedServiceStateStore.path}.`,
|
|
159
|
+
`${APP_NAME} restart only stops the background service recorded in managed state; it will not auto-kill Docker or other external listeners.`,
|
|
160
|
+
`Fix: stop that external service first or rerun with --ui-port <port>.`
|
|
161
|
+
].join("\n");
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
export { checkPortAvailability, describeUnmanagedHealthyTargetMessage, inspectUiTarget, probeHealthEndpoint };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/shared/utils/startup-trace.d.ts
|
|
2
|
+
type TraceFields = Record<string, string | number | boolean | null | undefined>;
|
|
3
|
+
declare function logStartupTrace(step: string, fields?: TraceFields): void;
|
|
4
|
+
declare function measureStartupSync<T>(step: string, fn: () => T, fields?: TraceFields): T;
|
|
5
|
+
declare function measureStartupAsync<T>(step: string, fn: () => Promise<T>, fields?: TraceFields): Promise<T>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { logStartupTrace, measureStartupAsync, measureStartupSync };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/shared/utils/startup-trace.ts
|
|
2
|
+
const STARTUP_TRACE_ENABLED = process.env.NEXTCLAW_STARTUP_TRACE === "1";
|
|
3
|
+
const STARTUP_TRACE_ORIGIN_MS = Date.now();
|
|
4
|
+
function formatFields(fields) {
|
|
5
|
+
if (!fields) return "";
|
|
6
|
+
const parts = Object.entries(fields).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}=${String(value)}`);
|
|
7
|
+
return parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
8
|
+
}
|
|
9
|
+
function logStartupTrace(step, fields) {
|
|
10
|
+
if (!STARTUP_TRACE_ENABLED) return;
|
|
11
|
+
const elapsedMs = Date.now() - STARTUP_TRACE_ORIGIN_MS;
|
|
12
|
+
console.log(`[startup-trace] +${elapsedMs}ms ${step}${formatFields(fields)}`);
|
|
13
|
+
}
|
|
14
|
+
function measureStartupSync(step, fn, fields) {
|
|
15
|
+
const startedAt = Date.now();
|
|
16
|
+
try {
|
|
17
|
+
return fn();
|
|
18
|
+
} finally {
|
|
19
|
+
logStartupTrace(step, {
|
|
20
|
+
...fields,
|
|
21
|
+
duration_ms: Date.now() - startedAt
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function measureStartupAsync(step, fn, fields) {
|
|
26
|
+
const startedAt = Date.now();
|
|
27
|
+
try {
|
|
28
|
+
return await fn();
|
|
29
|
+
} finally {
|
|
30
|
+
logStartupTrace(step, {
|
|
31
|
+
...fields,
|
|
32
|
+
duration_ms: Date.now() - startedAt
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { logStartupTrace, measureStartupAsync, measureStartupSync };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createExternalCommandEnv } from "@nextclaw/core";
|
|
2
|
+
//#region src/shared/utils/top-level-nextclaw-command-env.utils.ts
|
|
3
|
+
const NEXTCLAW_RUNTIME_BUNDLE_ENV_KEYS = ["NEXTCLAW_RUNTIME_BUNDLE_CHILD", "NEXTCLAW_DISABLE_RUNTIME_BUNDLE_LAUNCHER"];
|
|
4
|
+
function createTopLevelNextclawCommandEnv(baseEnv = process.env, extraEnv = {}) {
|
|
5
|
+
const env = createExternalCommandEnv(baseEnv, extraEnv);
|
|
6
|
+
for (const key of NEXTCLAW_RUNTIME_BUNDLE_ENV_KEYS) delete env[key];
|
|
7
|
+
return env;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { createTopLevelNextclawCommandEnv };
|