@nextclaw/service 0.1.7 → 0.1.9

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 (83) hide show
  1. package/dist/cli/commands/agent/agent-runtime.utils.js +2 -2
  2. package/dist/cli/commands/agent/cli-agent-runner.utils.js +2 -2
  3. package/dist/cli/commands/agent/index.js +1 -1
  4. package/dist/cli/commands/config/services/config-commands.service.js +1 -1
  5. package/dist/cli/commands/cron/services/cron-commands.service.js +2 -2
  6. package/dist/cli/commands/cron/services/cron-local.service.d.ts +1 -1
  7. package/dist/cli/commands/cron/services/cron-local.service.js +1 -1
  8. package/dist/cli/commands/restart/index.js +2 -2
  9. package/dist/cli/commands/skills/marketplace.metadata.js +1 -1
  10. package/dist/cli/commands/skills/marketplace.service.js +2 -2
  11. package/dist/cli/commands/skills/skills-query.service.js +1 -1
  12. package/dist/commands/channel/index.js +2 -2
  13. package/dist/commands/plugin/development-source/dev-plugin-overrides.utils.js +3 -3
  14. package/dist/commands/plugin/development-source/{first-party-plugin-load-paths.d.ts → first-party-plugin-load-paths.utils.d.ts} +1 -1
  15. package/dist/commands/plugin/development-source/{first-party-plugin-load-paths.js → first-party-plugin-load-paths.utils.js} +3 -3
  16. package/dist/commands/plugin/index.d.ts +1 -1
  17. package/dist/commands/plugin/index.js +5 -5
  18. package/dist/commands/plugin/plugin-command-utils.d.ts +1 -1
  19. package/dist/commands/plugin/plugin-mutation-actions.js +2 -2
  20. package/dist/commands/plugin/{plugin-registry-loader.d.ts → plugin-registry-loader.utils.d.ts} +1 -1
  21. package/dist/commands/plugin/{plugin-registry-loader.js → plugin-registry-loader.utils.js} +2 -2
  22. package/dist/commands/remote/services/remote-service-control.service.js +1 -1
  23. package/dist/commands/remote/utils/remote-runtime-support.utils.js +1 -1
  24. package/dist/commands/service/services/autostart/host-autostart-runtime.service.js +2 -2
  25. package/dist/commands/service/services/autostart/linux-systemd-autostart.service.js +2 -2
  26. package/dist/commands/service/services/autostart/macos-launch-agent-autostart.service.js +2 -2
  27. package/dist/commands/service/services/autostart/windows-task-autostart.service.js +2 -2
  28. package/dist/index.d.ts +2 -2
  29. package/dist/launcher/npm-runtime-bundle-layout.store.js +1 -1
  30. package/dist/launcher/npm-runtime-bundle.service.js +1 -1
  31. package/dist/launcher/npm-runtime-bundle.types.d.ts +1 -1
  32. package/dist/launcher/npm-runtime-launcher.service.js +5 -5
  33. package/dist/launcher/npm-runtime-update-command.service.js +4 -3
  34. package/dist/launcher/npm-runtime-update-source.service.js +2 -2
  35. package/dist/launcher/npm-runtime-update-state.store.js +1 -1
  36. package/dist/launcher/npm-runtime-update.manager.d.ts +2 -2
  37. package/dist/launcher/npm-runtime-update.manager.js +8 -4
  38. package/dist/launcher/npm-runtime-update.service.d.ts +1 -1
  39. package/dist/launcher/npm-runtime-update.service.js +3 -3
  40. package/dist/service-runtime.service.d.ts +0 -2
  41. package/dist/service-runtime.service.js +31 -33
  42. package/dist/shared/controllers/gateway.controller.js +7 -9
  43. package/dist/shared/services/extensions/extension-lifecycle.service.js +1 -1
  44. package/dist/shared/services/extensions/service-extension-runtime.service.d.ts +1 -1
  45. package/dist/shared/services/extensions/service-extension-runtime.service.js +7 -6
  46. package/dist/shared/services/gateway/gateway-restart-wake.service.js +2 -1
  47. package/dist/shared/services/gateway/managers/gateway-plugin.manager.js +17 -3
  48. package/dist/shared/services/gateway/managers/gateway-remote.manager.d.ts +2 -2
  49. package/dist/shared/services/gateway/managers/gateway-remote.manager.js +1 -1
  50. package/dist/shared/services/gateway/nextclaw-gateway-runtime.service.d.ts +1 -1
  51. package/dist/shared/services/gateway/nextclaw-gateway-runtime.service.js +10 -10
  52. package/dist/shared/services/gateway/service-bootstrap-status.service.d.ts +1 -1
  53. package/dist/shared/services/gateway/service-startup-support.service.d.ts +1 -1
  54. package/dist/shared/services/marketplace/service-marketplace-installer.service.js +2 -2
  55. package/dist/shared/services/plugin/utils/plugin-runtime-bridge.utils.js +1 -1
  56. package/dist/shared/services/restart/restart-sentinel.service.js +1 -1
  57. package/dist/shared/services/runtime/runtime-command.service.d.ts +3 -0
  58. package/dist/shared/services/runtime/runtime-command.service.js +27 -3
  59. package/dist/shared/services/runtime/service-managed-startup.service.d.ts +4 -28
  60. package/dist/shared/services/runtime/service-managed-startup.service.js +61 -65
  61. package/dist/shared/services/runtime/utils/managed-service-routing.utils.d.ts +30 -0
  62. package/dist/shared/services/runtime/utils/managed-service-routing.utils.js +62 -0
  63. package/dist/shared/services/runtime/utils/service-remote-runtime.utils.js +2 -2
  64. package/dist/shared/services/session/service-deferred-ncp-agent.service.d.ts +1 -1
  65. package/dist/shared/services/ui/companion-runtime.service.d.ts +2 -0
  66. package/dist/shared/services/ui/companion-runtime.service.js +40 -8
  67. package/dist/shared/services/ui/local-ui-discovery.service.js +1 -1
  68. package/dist/shared/services/ui/npm-runtime-update-host.service.js +2 -2
  69. package/dist/shared/services/ui/runtime-control-host.service.js +1 -1
  70. package/dist/shared/services/ui/service-remote-access.service.js +1 -1
  71. package/dist/shared/services/workspace/workspace-manager.service.js +3 -3
  72. package/dist/shared/stores/companion-runtime.store.js +1 -1
  73. package/dist/shared/stores/local-ui-runtime.store.js +1 -1
  74. package/dist/shared/stores/managed-service-state.store.js +1 -1
  75. package/dist/shared/types/cli.types.d.ts +2 -14
  76. package/dist/shared/utils/cli.utils.js +2 -2
  77. package/dist/shared/utils/marketplace/cli-subcommand-launch.utils.js +1 -1
  78. package/dist/shared/utils/package/package-manifest.utils.js +2 -2
  79. package/package.json +21 -19
  80. package/dist/cli/commands/companion/index.d.ts +0 -15
  81. package/dist/cli/commands/companion/index.js +0 -24
  82. package/dist/cli/commands/companion/services/companion-process.service.d.ts +0 -17
  83. package/dist/cli/commands/companion/services/companion-process.service.js +0 -49
@@ -1,71 +1,19 @@
1
+ import { createTopLevelNextclawCommandEnv } from "../../utils/top-level-nextclaw-command-env.utils.js";
1
2
  import { isProcessRunning, openBrowser, resolveServiceLogPath, resolveUiApiBase, resolveUiConfig, waitForExit } from "../../utils/cli.utils.js";
2
- import { localUiRuntimeStore } from "../../stores/local-ui-runtime.store.js";
3
3
  import { managedServiceStateStore } from "../../stores/managed-service-state.store.js";
4
- import { probeHealthEndpoint } from "../../utils/service-port-probe.utils.js";
4
+ import { localUiRuntimeStore } from "../../stores/local-ui-runtime.store.js";
5
5
  import { resolveCliSubcommandLaunch } from "../../utils/marketplace/cli-subcommand-launch.utils.js";
6
- import { createTopLevelNextclawCommandEnv } from "../../utils/top-level-nextclaw-command-env.utils.js";
7
6
  import { writeInitialManagedServiceState, writeReadyManagedServiceState } from "./utils/service-remote-runtime.utils.js";
8
- import { spawn } from "node:child_process";
9
- import { dirname } from "node:path";
7
+ import { resolveManagedServiceReadySnapshot, resolveManagedServiceUiBinding, resolveSessionRouteCandidate } from "./utils/managed-service-routing.utils.js";
8
+ import { probeHealthEndpoint } from "../../utils/service-port-probe.utils.js";
10
9
  import * as NextclawCore from "@nextclaw/core";
11
10
  import { FileLogSink } from "@nextclaw/core";
12
11
  import { mkdirSync } from "node:fs";
12
+ import { dirname } from "node:path";
13
+ import { spawn } from "node:child_process";
13
14
  //#region src/shared/services/runtime/service-managed-startup.service.ts
14
15
  const { APP_NAME: APP_NAME$1, loadConfig: loadConfig$1 } = NextclawCore;
15
- function resolveManagedServiceReadySnapshot(params) {
16
- const { snapshot, readLocalUiRuntimeState, isProcessRunningFn: providedIsProcessRunningFn } = params;
17
- const localUiRuntimeState = (readLocalUiRuntimeState ?? localUiRuntimeStore.read)();
18
- const isProcessRunningFn = providedIsProcessRunningFn ?? isProcessRunning;
19
- if (!localUiRuntimeState || typeof localUiRuntimeState.pid !== "number" || !Number.isFinite(localUiRuntimeState.pid) || localUiRuntimeState.uiPort !== snapshot.uiPort || !isProcessRunningFn(localUiRuntimeState.pid)) return snapshot;
20
- return {
21
- ...snapshot,
22
- pid: localUiRuntimeState.pid,
23
- uiUrl: localUiRuntimeState.uiUrl,
24
- apiUrl: localUiRuntimeState.apiUrl,
25
- uiHost: localUiRuntimeState.uiHost ?? snapshot.uiHost,
26
- uiPort: localUiRuntimeState.uiPort ?? snapshot.uiPort
27
- };
28
- }
29
- function toObjectRecord(value) {
30
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
31
- return value;
32
- }
33
- function hasSessionRoutingMetadata(params) {
34
- const context = toObjectRecord(params.metadata.last_delivery_context) ?? {};
35
- const hasPrimaryRoute = Boolean(params.normalizeOptionalString(context.channel)) && Boolean(params.normalizeOptionalString(context.chatId));
36
- const hasFallbackRoute = Boolean(params.normalizeOptionalString(params.metadata.last_channel)) && Boolean(params.normalizeOptionalString(params.metadata.last_to));
37
- return hasPrimaryRoute || hasFallbackRoute;
38
- }
39
- function resolveManagedServiceUiBinding(state) {
40
- try {
41
- const parsed = new URL(state.uiUrl);
42
- const parsedPort = Number(parsed.port || 80);
43
- return {
44
- host: state.uiHost ?? parsed.hostname,
45
- port: Number.isFinite(parsedPort) ? parsedPort : state.uiPort ?? 55667
46
- };
47
- } catch {
48
- return {
49
- host: state.uiHost ?? "127.0.0.1",
50
- port: state.uiPort ?? 55667
51
- };
52
- }
53
- }
54
- function resolveSessionRouteCandidate(params) {
55
- const sessionRecord = toObjectRecord(params.session);
56
- const key = params.normalizeOptionalString(sessionRecord?.key);
57
- if (!key || key.startsWith("cli:")) return null;
58
- if (!hasSessionRoutingMetadata({
59
- metadata: toObjectRecord(sessionRecord?.metadata) ?? {},
60
- normalizeOptionalString: params.normalizeOptionalString
61
- })) return null;
62
- const updatedAtRaw = params.normalizeOptionalString(sessionRecord?.updated_at);
63
- const updatedAt = updatedAtRaw ? Date.parse(updatedAtRaw) : NaN;
64
- return {
65
- key,
66
- updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0
67
- };
68
- }
16
+ const serviceStartupLogger = NextclawCore.getAppLogger("service.startup");
69
17
  function spawnManagedService(params) {
70
18
  const { appName, config, uiConfig, uiUrl, apiUrl, healthUrl, startupTimeoutMs, resolveStartupTimeoutMs, appendStartupStage, printStartupFailureDiagnostics, resolveServiceLogPath } = params;
71
19
  const logPath = resolveServiceLogPath();
@@ -119,6 +67,22 @@ function spawnManagedService(params) {
119
67
  readinessTimeoutMs,
120
68
  snapshot
121
69
  });
70
+ serviceStartupLogger.info("runtime.process.started", {
71
+ runtimeKind: "managed-service",
72
+ childPid: child.pid,
73
+ uiUrl,
74
+ apiUrl,
75
+ uiHost: uiConfig.host,
76
+ uiPort: uiConfig.port,
77
+ entrypoint: `${cliLaunch.command} ${childArgs.join(" ")}`
78
+ });
79
+ serviceStartupLogger.info("service_state.written", {
80
+ runtimeKind: "managed-service",
81
+ childPid: child.pid,
82
+ statePath: managedServiceStateStore.path,
83
+ uiUrl,
84
+ apiUrl
85
+ });
122
86
  return {
123
87
  child,
124
88
  logPath,
@@ -162,6 +126,7 @@ async function reportManagedServiceStart(params) {
162
126
  var ManagedServiceCommandService = class {
163
127
  loggingRuntime = NextclawCore.getLoggingRuntime();
164
128
  serviceLogger = this.loggingRuntime.getLogger("service");
129
+ startupLogger = this.serviceLogger.child("startup");
165
130
  constructor(deps) {
166
131
  this.deps = deps;
167
132
  }
@@ -237,6 +202,13 @@ var ManagedServiceCommandService = class {
237
202
  return;
238
203
  }
239
204
  console.log(`Stopping ${APP_NAME$1} (PID ${state.pid})...`);
205
+ this.serviceLogger.info("runtime.process.stop_requested", {
206
+ runtimeKind: "managed-service",
207
+ pid: state.pid,
208
+ reason: "nextclaw-stop-command",
209
+ uiUrl: state.uiUrl,
210
+ apiUrl: state.apiUrl
211
+ });
240
212
  try {
241
213
  process.kill(state.pid, "SIGTERM");
242
214
  } catch (error) {
@@ -244,6 +216,14 @@ var ManagedServiceCommandService = class {
244
216
  return;
245
217
  }
246
218
  if (!await waitForExit(state.pid, 3e3)) {
219
+ this.serviceLogger.warn("runtime.process.stop_requested", {
220
+ runtimeKind: "managed-service",
221
+ pid: state.pid,
222
+ reason: "nextclaw-stop-command-force",
223
+ signal: "SIGKILL",
224
+ uiUrl: state.uiUrl,
225
+ apiUrl: state.apiUrl
226
+ });
247
227
  try {
248
228
  process.kill(state.pid, "SIGKILL");
249
229
  } catch (error) {
@@ -254,6 +234,13 @@ var ManagedServiceCommandService = class {
254
234
  }
255
235
  managedServiceStateStore.clear();
256
236
  localUiRuntimeStore.clearIfOwnedByProcess(state.pid);
237
+ this.serviceLogger.info("service_state.cleared", {
238
+ runtimeKind: "managed-service",
239
+ pid: state.pid,
240
+ statePath: managedServiceStateStore.path,
241
+ uiUrl: state.uiUrl,
242
+ apiUrl: state.apiUrl
243
+ });
257
244
  console.log(`✓ ${APP_NAME$1} stopped`);
258
245
  };
259
246
  handleExistingManagedService = async (params) => {
@@ -348,13 +335,22 @@ var ManagedServiceCommandService = class {
348
335
  }
349
336
  startup.child.unref();
350
337
  const readySnapshot = resolveManagedServiceReadySnapshot({ snapshot: startup.snapshot });
338
+ const state = writeReadyManagedServiceState({
339
+ readinessTimeoutMs: startup.readinessTimeoutMs,
340
+ readiness,
341
+ snapshot: readySnapshot
342
+ });
343
+ this.startupLogger.info("runtime.process.ready", {
344
+ runtimeKind: "managed-service",
345
+ pid: state.pid,
346
+ uiUrl: state.uiUrl,
347
+ apiUrl: state.apiUrl,
348
+ startupState: state.startupState,
349
+ readinessTimeoutMs: startup.readinessTimeoutMs
350
+ });
351
351
  await reportManagedServiceStart({
352
352
  appName: APP_NAME$1,
353
- state: writeReadyManagedServiceState({
354
- readinessTimeoutMs: startup.readinessTimeoutMs,
355
- readiness,
356
- snapshot: readySnapshot
357
- }),
353
+ state,
358
354
  uiConfig,
359
355
  uiUrl,
360
356
  apiUrl,
@@ -401,7 +397,7 @@ var ManagedServiceCommandService = class {
401
397
  };
402
398
  appendStartupStage = (logPath, message) => {
403
399
  try {
404
- this.serviceLogger.child("startup").info(message, { logPath });
400
+ this.startupLogger.info(message, { logPath });
405
401
  } catch (error) {
406
402
  const detail = error instanceof Error ? error.message : String(error);
407
403
  console.error(`Warning: failed to write startup diagnostics log (${logPath}): ${detail}`);
@@ -0,0 +1,30 @@
1
+ import { ManagedServiceState } from "../../../stores/managed-service-state.store.js";
2
+ import { localUiRuntimeStore } from "../../../stores/local-ui-runtime.store.js";
3
+
4
+ //#region src/shared/services/runtime/utils/managed-service-routing.utils.d.ts
5
+ type ManagedServiceSnapshot = {
6
+ pid: number;
7
+ uiUrl: string;
8
+ apiUrl: string;
9
+ uiHost: string;
10
+ uiPort: number;
11
+ logPath: string;
12
+ };
13
+ declare function resolveManagedServiceReadySnapshot(params: {
14
+ snapshot: ManagedServiceSnapshot;
15
+ readLocalUiRuntimeState?: typeof localUiRuntimeStore.read;
16
+ isProcessRunningFn?: (pid: number) => boolean;
17
+ }): ManagedServiceSnapshot;
18
+ declare function resolveManagedServiceUiBinding(state: ManagedServiceState): {
19
+ host: string;
20
+ port: number;
21
+ };
22
+ declare function resolveSessionRouteCandidate(params: {
23
+ session: unknown;
24
+ normalizeOptionalString: (value: unknown) => string | undefined;
25
+ }): {
26
+ key: string;
27
+ updatedAt: number;
28
+ } | null;
29
+ //#endregion
30
+ export { ManagedServiceSnapshot, resolveManagedServiceReadySnapshot, resolveManagedServiceUiBinding, resolveSessionRouteCandidate };
@@ -0,0 +1,62 @@
1
+ import { isProcessRunning } from "../../../utils/cli.utils.js";
2
+ import "../../../stores/managed-service-state.store.js";
3
+ import { localUiRuntimeStore } from "../../../stores/local-ui-runtime.store.js";
4
+ //#region src/shared/services/runtime/utils/managed-service-routing.utils.ts
5
+ function resolveManagedServiceReadySnapshot(params) {
6
+ const { snapshot, readLocalUiRuntimeState, isProcessRunningFn: providedIsProcessRunningFn } = params;
7
+ const localUiRuntimeState = (readLocalUiRuntimeState ?? localUiRuntimeStore.read)();
8
+ const isProcessRunningFn = providedIsProcessRunningFn ?? isProcessRunning;
9
+ if (!localUiRuntimeState || typeof localUiRuntimeState.pid !== "number" || !Number.isFinite(localUiRuntimeState.pid) || localUiRuntimeState.uiPort !== snapshot.uiPort || !isProcessRunningFn(localUiRuntimeState.pid)) return snapshot;
10
+ return {
11
+ ...snapshot,
12
+ pid: localUiRuntimeState.pid,
13
+ uiUrl: localUiRuntimeState.uiUrl,
14
+ apiUrl: localUiRuntimeState.apiUrl,
15
+ uiHost: localUiRuntimeState.uiHost ?? snapshot.uiHost,
16
+ uiPort: localUiRuntimeState.uiPort ?? snapshot.uiPort
17
+ };
18
+ }
19
+ function toObjectRecord(value) {
20
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
21
+ return value;
22
+ }
23
+ function hasSessionRoutingMetadata(params) {
24
+ const { metadata, normalizeOptionalString } = params;
25
+ const context = toObjectRecord(metadata.last_delivery_context) ?? {};
26
+ const hasPrimaryRoute = Boolean(normalizeOptionalString(context.channel)) && Boolean(normalizeOptionalString(context.chatId));
27
+ const hasFallbackRoute = Boolean(normalizeOptionalString(metadata.last_channel)) && Boolean(normalizeOptionalString(metadata.last_to));
28
+ return hasPrimaryRoute || hasFallbackRoute;
29
+ }
30
+ function resolveManagedServiceUiBinding(state) {
31
+ try {
32
+ const parsed = new URL(state.uiUrl);
33
+ const parsedPort = Number(parsed.port || 80);
34
+ return {
35
+ host: state.uiHost ?? parsed.hostname,
36
+ port: Number.isFinite(parsedPort) ? parsedPort : state.uiPort ?? 55667
37
+ };
38
+ } catch {
39
+ return {
40
+ host: state.uiHost ?? "127.0.0.1",
41
+ port: state.uiPort ?? 55667
42
+ };
43
+ }
44
+ }
45
+ function resolveSessionRouteCandidate(params) {
46
+ const { session, normalizeOptionalString } = params;
47
+ const sessionRecord = toObjectRecord(session);
48
+ const key = normalizeOptionalString(sessionRecord?.key);
49
+ if (!key || key.startsWith("cli:")) return null;
50
+ if (!hasSessionRoutingMetadata({
51
+ metadata: toObjectRecord(sessionRecord?.metadata) ?? {},
52
+ normalizeOptionalString
53
+ })) return null;
54
+ const updatedAtRaw = normalizeOptionalString(sessionRecord?.updated_at);
55
+ const updatedAt = updatedAtRaw ? Date.parse(updatedAtRaw) : NaN;
56
+ return {
57
+ key,
58
+ updatedAt: Number.isFinite(updatedAt) ? updatedAt : 0
59
+ };
60
+ }
61
+ //#endregion
62
+ export { resolveManagedServiceReadySnapshot, resolveManagedServiceUiBinding, resolveSessionRouteCandidate };
@@ -1,10 +1,10 @@
1
1
  import { isProcessRunning, resolveUiApiBase } from "../../../utils/cli.utils.js";
2
2
  import { managedServiceStateStore } from "../../../stores/managed-service-state.store.js";
3
3
  import { buildNextclawConfiguredRemoteState, createNextclawRemoteConnector, createNextclawRemoteStatusStore } from "../../../../commands/remote/utils/remote-runtime-support.utils.js";
4
- import { dirname, resolve } from "node:path";
5
4
  import { getDataDir } from "@nextclaw/core";
6
- import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
7
5
  import { RemoteServiceModule } from "@nextclaw/remote";
6
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
7
+ import { dirname, resolve } from "node:path";
8
8
  //#region src/shared/services/runtime/utils/service-remote-runtime.utils.ts
9
9
  function resolveRemoteOwnershipLockPath() {
10
10
  return resolve(getDataDir(), "run", "remote-owner.lock.json");
@@ -1,5 +1,5 @@
1
- import { UiNcpAgent } from "@nextclaw/server";
2
1
  import { AgentRuntimeHandle } from "@nextclaw/kernel";
2
+ import { UiNcpAgent } from "@nextclaw/server";
3
3
 
4
4
  //#region src/shared/services/session/service-deferred-ncp-agent.service.d.ts
5
5
  type DeferredUiNcpAgentController = {
@@ -9,6 +9,7 @@ declare class CompanionRuntimeService {
9
9
  constructor(runtimeStore?: CompanionRuntimeStore, uiDiscoveryService?: LocalUiDiscoveryService);
10
10
  readonly getRunningState: () => CompanionRuntimeState | null;
11
11
  readonly resolveDiscoveredBaseUrl: () => string | null;
12
+ readonly isAvailable: () => boolean;
12
13
  readonly applyConfig: (config: Config) => Promise<void>;
13
14
  readonly updateEnabled: (enabled: boolean, options?: {
14
15
  baseUrl?: string;
@@ -25,6 +26,7 @@ declare class CompanionRuntimeService {
25
26
  readonly printStarted: (state: CompanionRuntimeState) => void;
26
27
  readonly printStopped: (stopped: boolean) => void;
27
28
  private readonly killProcess;
29
+ private readonly ensureStartedIfAvailable;
28
30
  private readonly resolveLaunchSpec;
29
31
  private readonly waitForRunningState;
30
32
  }
@@ -1,13 +1,22 @@
1
1
  import { isProcessRunning } from "../../utils/cli.utils.js";
2
- import { companionRuntimeStore } from "../../stores/companion-runtime.store.js";
3
2
  import { localUiDiscoveryService } from "./local-ui-discovery.service.js";
3
+ import { companionRuntimeStore } from "../../stores/companion-runtime.store.js";
4
4
  import { createRequire } from "node:module";
5
- import { spawn } from "node:child_process";
6
- import { dirname, resolve } from "node:path";
7
5
  import { APP_NAME, getConfigPath, loadConfig, saveConfig } from "@nextclaw/core";
8
6
  import { existsSync } from "node:fs";
7
+ import { dirname, resolve } from "node:path";
8
+ import { spawn } from "node:child_process";
9
9
  //#region src/shared/services/ui/companion-runtime.service.ts
10
10
  const require = createRequire(import.meta.url);
11
+ var CompanionUnavailableError = class extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = "CompanionUnavailableError";
15
+ }
16
+ };
17
+ function isCompanionUnavailableError(error) {
18
+ return error instanceof CompanionUnavailableError;
19
+ }
11
20
  var CompanionRuntimeService = class {
12
21
  constructor(runtimeStore = companionRuntimeStore, uiDiscoveryService = localUiDiscoveryService) {
13
22
  this.runtimeStore = runtimeStore;
@@ -25,12 +34,21 @@ var CompanionRuntimeService = class {
25
34
  resolveDiscoveredBaseUrl = () => {
26
35
  return this.uiDiscoveryService.resolveApiBase();
27
36
  };
37
+ isAvailable = () => {
38
+ try {
39
+ this.resolveLaunchSpec();
40
+ return true;
41
+ } catch (error) {
42
+ if (isCompanionUnavailableError(error)) return false;
43
+ throw error;
44
+ }
45
+ };
28
46
  applyConfig = async (config) => {
29
47
  if (!config.companion.enabled) {
30
48
  await this.ensureStopped();
31
49
  return;
32
50
  }
33
- await this.ensureStarted({ baseUrl: this.uiDiscoveryService.resolveLocalOrigin(config) });
51
+ await this.ensureStartedIfAvailable({ baseUrl: this.uiDiscoveryService.resolveLocalOrigin(config) });
34
52
  };
35
53
  updateEnabled = async (enabled, options = {}) => {
36
54
  const config = loadConfig(getConfigPath());
@@ -45,11 +63,11 @@ var CompanionRuntimeService = class {
45
63
  if (enabled) {
46
64
  const explicitBaseUrl = options.baseUrl?.trim();
47
65
  if (explicitBaseUrl) {
48
- await this.ensureStarted({ baseUrl: explicitBaseUrl });
66
+ await this.ensureStartedIfAvailable({ baseUrl: explicitBaseUrl });
49
67
  return next;
50
68
  }
51
69
  const discoveredBaseUrl = this.uiDiscoveryService.resolveApiBase();
52
- if (discoveredBaseUrl) await this.ensureStarted({ baseUrl: discoveredBaseUrl });
70
+ if (discoveredBaseUrl) await this.ensureStartedIfAvailable({ baseUrl: discoveredBaseUrl });
53
71
  return next;
54
72
  }
55
73
  await this.ensureStopped();
@@ -120,11 +138,25 @@ var CompanionRuntimeService = class {
120
138
  killProcess = (pid, force) => {
121
139
  process.kill(pid, force ? "SIGKILL" : "SIGTERM");
122
140
  };
141
+ ensureStartedIfAvailable = async (options) => {
142
+ try {
143
+ return await this.ensureStarted(options);
144
+ } catch (error) {
145
+ if (!isCompanionUnavailableError(error)) throw error;
146
+ console.warn(error.message);
147
+ return null;
148
+ }
149
+ };
123
150
  resolveLaunchSpec = () => {
124
- const packageJsonPath = require.resolve("@nextclaw/companion/package.json");
151
+ let packageJsonPath;
152
+ try {
153
+ packageJsonPath = require.resolve("@nextclaw/companion/package.json");
154
+ } catch {
155
+ throw new CompanionUnavailableError("@nextclaw/companion is not installed. Install it separately to use the companion shell.");
156
+ }
125
157
  const packageRoot = dirname(packageJsonPath);
126
158
  const mainPath = resolve(packageRoot, "dist", "src", "main.js");
127
- if (!existsSync(mainPath)) throw new Error(`Companion app build is missing at ${mainPath}. Build @nextclaw/companion first.`);
159
+ if (!existsSync(mainPath)) throw new CompanionUnavailableError(`Companion app build is missing at ${mainPath}. Install or build @nextclaw/companion separately to use the companion shell.`);
128
160
  return {
129
161
  command: createRequire(packageJsonPath)("electron"),
130
162
  args: [packageRoot]
@@ -1,6 +1,6 @@
1
1
  import { isProcessRunning } from "../../utils/cli.utils.js";
2
- import { localUiRuntimeStore } from "../../stores/local-ui-runtime.store.js";
3
2
  import { managedServiceStateStore } from "../../stores/managed-service-state.store.js";
3
+ import { localUiRuntimeStore } from "../../stores/local-ui-runtime.store.js";
4
4
  import { resolveLocalUiBaseUrl } from "@nextclaw/core";
5
5
  //#region src/shared/services/ui/local-ui-discovery.service.ts
6
6
  var LocalUiDiscoveryService = class {
@@ -1,9 +1,9 @@
1
1
  import { NpmRuntimeBundleLayoutStore } from "../../../launcher/npm-runtime-bundle-layout.store.js";
2
2
  import { NpmRuntimeBundleService } from "../../../launcher/npm-runtime-bundle.service.js";
3
- import { NpmRuntimeUpdateSourceService } from "../../../launcher/npm-runtime-update-source.service.js";
4
- import { NpmRuntimeUpdateStateStore } from "../../../launcher/npm-runtime-update-state.store.js";
5
3
  import { NpmRuntimeUpdateManager } from "../../../launcher/npm-runtime-update.manager.js";
6
4
  import { NpmRuntimeUpdateService } from "../../../launcher/npm-runtime-update.service.js";
5
+ import { NpmRuntimeUpdateSourceService } from "../../../launcher/npm-runtime-update-source.service.js";
6
+ import { NpmRuntimeUpdateStateStore } from "../../../launcher/npm-runtime-update-state.store.js";
7
7
  import { NextclawDistributionService } from "../runtime/nextclaw-distribution.service.js";
8
8
  import { requestManagedServiceRestart } from "./service-remote-access.service.js";
9
9
  import { eventKeys } from "@nextclaw/shared";
@@ -1,6 +1,6 @@
1
- import { pendingRestartStore } from "../../stores/pending-restart.store.js";
2
1
  import { controlRemoteService, resolveRemoteServiceView } from "../../../commands/remote/services/remote-service-control.service.js";
3
2
  import { requestManagedServiceRestart } from "./service-remote-access.service.js";
3
+ import { pendingRestartStore } from "../../stores/pending-restart.store.js";
4
4
  //#region src/shared/services/ui/runtime-control-host.service.ts
5
5
  const MANAGED_SERVICE_OWNER_LABEL = "Managed local service";
6
6
  const DESKTOP_APP_ONLY_REASON = "App restart is only available in the desktop shell.";
@@ -1,7 +1,7 @@
1
- import { RemoteAccessHost } from "../../../commands/remote/services/remote-access-host.service.js";
2
1
  import { RemoteCommands } from "../../../commands/remote/index.js";
3
2
  import { PlatformAuthCommands } from "../../../commands/platform-auth/services/platform-auth-commands.service.js";
4
3
  import "../../../commands/platform-auth/index.js";
4
+ import { RemoteAccessHost } from "../../../commands/remote/services/remote-access-host.service.js";
5
5
  //#region src/shared/services/ui/service-remote-access.service.ts
6
6
  function requestManagedServiceRestart(requestRestart, options = {}) {
7
7
  const uiPort = typeof options.uiPort === "number" && Number.isFinite(options.uiPort) ? Math.floor(options.uiPort) : void 0;
@@ -1,9 +1,9 @@
1
1
  import { findExecutableOnPath } from "../../utils/cli.utils.js";
2
- import { spawnSync } from "node:child_process";
3
- import { fileURLToPath } from "node:url";
4
- import { dirname, join, resolve } from "node:path";
5
2
  import { APP_NAME, getDataDir } from "@nextclaw/core";
6
3
  import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { spawnSync } from "node:child_process";
7
7
  //#region src/shared/services/workspace/workspace-manager.service.ts
8
8
  var WorkspaceManager = class {
9
9
  constructor(logo) {
@@ -1,6 +1,6 @@
1
- import { resolve } from "node:path";
2
1
  import { getDataDir } from "@nextclaw/core";
3
2
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
4
  //#region src/shared/stores/companion-runtime.store.ts
5
5
  var CompanionRuntimeStore = class {
6
6
  get path() {
@@ -1,7 +1,7 @@
1
1
  import { resolveUiApiBase } from "../utils/cli.utils.js";
2
- import { resolve } from "node:path";
3
2
  import { getDataDir } from "@nextclaw/core";
4
3
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
5
  //#region src/shared/stores/local-ui-runtime.store.ts
6
6
  var LocalUiRuntimeStore = class {
7
7
  get path() {
@@ -1,6 +1,6 @@
1
- import { resolve } from "node:path";
2
1
  import { getDataDir } from "@nextclaw/core";
3
2
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
4
  //#region src/shared/stores/managed-service-state.store.ts
5
5
  var ManagedServiceStateStore = class {
6
6
  get path() {
@@ -16,19 +16,6 @@ type StartCommandOptions = {
16
16
  open?: boolean;
17
17
  startTimeout?: string | number;
18
18
  };
19
- type CompanionStartCommandOptions = {
20
- baseUrl?: string;
21
- };
22
- type CompanionEnableCommandOptions = {
23
- baseUrl?: string;
24
- };
25
- type CompanionDisableCommandOptions = Record<string, never>;
26
- type CompanionStatusCommandOptions = {
27
- json?: boolean;
28
- };
29
- type CompanionStopCommandOptions = {
30
- force?: boolean;
31
- };
32
19
  type ServiceAutostartCommandOptions = {
33
20
  user?: boolean;
34
21
  system?: boolean;
@@ -69,6 +56,7 @@ type MarketplaceSkillsRecommendCommandOptions = {
69
56
  type UpdateCommandOptions = {
70
57
  check?: boolean;
71
58
  download?: boolean;
59
+ downloadOnly?: boolean;
72
60
  apply?: boolean;
73
61
  channel?: string;
74
62
  manifestUrl?: string;
@@ -292,4 +280,4 @@ type RequestRestartParams = {
292
280
  mode?: "execute" | "notify";
293
281
  };
294
282
  //#endregion
295
- export { AccountCommandOptions, AccountSetUsernameCommandOptions, AgentCommandOptions, AgentsListCommandOptions, AgentsNewCommandOptions, AgentsRemoveCommandOptions, AgentsRuntimesCommandOptions, AgentsUpdateCommandOptions, ChannelsAddOptions, ChannelsLoginOptions, CompanionDisableCommandOptions, CompanionEnableCommandOptions, CompanionStartCommandOptions, CompanionStatusCommandOptions, CompanionStopCommandOptions, ConfigGetOptions, ConfigSetOptions, CronAddOptions, DoctorCommandOptions, GatewayCommandOptions, HealthProbe, LoginCommandOptions, LogsTailCommandOptions, MarketplaceSkillsRecommendCommandOptions, MarketplaceSkillsSearchCommandOptions, McpAddCommandOptions, McpDoctorOptions, McpListOptions, PluginsInfoOptions, PluginsInstallOptions, PluginsListOptions, PluginsUninstallOptions, type RemoteConnectCommandOptions, type RemoteDoctorCommandOptions, type RemoteEnableCommandOptions, type RemoteStatusCommandOptions, RequestRestartParams, RuntimeStatusReport, SecretsApplyOptions, SecretsAuditOptions, SecretsConfigureOptions, SecretsReloadOptions, ServiceAutostartCommandOptions, SkillsInfoCommandOptions, SkillsInstalledCommandOptions, StartCommandOptions, StatusCommandOptions, UiCommandOptions, UpdateCommandOptions, UsageCommandOptions };
283
+ export { AccountCommandOptions, AccountSetUsernameCommandOptions, AgentCommandOptions, AgentsListCommandOptions, AgentsNewCommandOptions, AgentsRemoveCommandOptions, AgentsRuntimesCommandOptions, AgentsUpdateCommandOptions, ChannelsAddOptions, ChannelsLoginOptions, ConfigGetOptions, ConfigSetOptions, CronAddOptions, DoctorCommandOptions, GatewayCommandOptions, HealthProbe, LoginCommandOptions, LogsTailCommandOptions, MarketplaceSkillsRecommendCommandOptions, MarketplaceSkillsSearchCommandOptions, McpAddCommandOptions, McpDoctorOptions, McpListOptions, PluginsInfoOptions, PluginsInstallOptions, PluginsListOptions, PluginsUninstallOptions, type RemoteConnectCommandOptions, type RemoteDoctorCommandOptions, type RemoteEnableCommandOptions, type RemoteStatusCommandOptions, RequestRestartParams, RuntimeStatusReport, SecretsApplyOptions, SecretsAuditOptions, SecretsConfigureOptions, SecretsReloadOptions, ServiceAutostartCommandOptions, SkillsInfoCommandOptions, SkillsInstalledCommandOptions, StartCommandOptions, StatusCommandOptions, UiCommandOptions, UpdateCommandOptions, UsageCommandOptions };
@@ -1,8 +1,8 @@
1
1
  import { getPackageVersion } from "./package/package-manifest.utils.js";
2
- import { spawn, spawnSync } from "node:child_process";
3
- import { join, resolve } from "node:path";
4
2
  import { createExternalCommandEnv, getLogsPath, resolveLocalUiBaseUrl } from "@nextclaw/core";
5
3
  import { existsSync } from "node:fs";
4
+ import { join, resolve } from "node:path";
5
+ import { spawn, spawnSync } from "node:child_process";
6
6
  import { isIP } from "node:net";
7
7
  //#region src/shared/utils/cli.utils.ts
8
8
  function resolveUiConfig(config, overrides) {
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
- import { fileURLToPath } from "node:url";
3
2
  import { extname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
4
  //#region src/shared/utils/marketplace/cli-subcommand-launch.utils.ts
5
5
  const require = createRequire(import.meta.url);
6
6
  const TYPESCRIPT_EXTENSIONS = new Set([
@@ -1,7 +1,7 @@
1
- import { fileURLToPath } from "node:url";
2
- import { join, resolve } from "node:path";
3
1
  import { getPackageVersion as getPackageVersion$1 } from "@nextclaw/core";
4
2
  import { readFileSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
5
  //#region src/shared/utils/package/package-manifest.utils.ts
6
6
  function readPackageManifest(pkgPath) {
7
7
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/service",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "private": false,
5
5
  "description": "NextClaw long-running service host and runtime lifecycle.",
6
6
  "type": "module",
@@ -26,6 +26,7 @@
26
26
  ],
27
27
  "nextclaw": {
28
28
  "builtinExtensions": [
29
+ "@nextclaw/channel-extension-feishu",
29
30
  "@nextclaw/channel-extension-weixin"
30
31
  ]
31
32
  },
@@ -34,23 +35,24 @@
34
35
  "commander": "^12.1.0",
35
36
  "jszip": "^3.10.1",
36
37
  "yaml": "^2.8.1",
37
- "@nextclaw/channel-extension-weixin": "0.1.3",
38
- "@nextclaw/kernel": "0.1.5",
39
- "@nextclaw/core": "0.12.16",
40
- "@nextclaw/ncp": "0.5.9",
41
- "@nextclaw/ncp-agent-runtime": "0.3.19",
42
- "@nextclaw/mcp": "0.1.81",
43
- "@nextclaw/ncp-mcp": "0.1.83",
44
- "@nextclaw/ncp-http-agent-server": "0.3.21",
45
- "@nextclaw/nextclaw-hermes-acp-bridge": "0.1.8",
46
- "@nextclaw/nextclaw-ncp-runtime-http-client": "0.1.8",
47
- "@nextclaw/ncp-toolkit": "0.5.14",
48
- "@nextclaw/nextclaw-ncp-runtime-stdio-client": "0.1.9",
49
- "@nextclaw/openclaw-compat": "1.0.16",
50
- "@nextclaw/remote": "0.1.93",
51
- "@nextclaw/shared": "0.1.3",
52
- "@nextclaw/server": "0.12.16",
53
- "@nextclaw/runtime": "0.2.48"
38
+ "@nextclaw/channel-extension-feishu": "0.1.1",
39
+ "@nextclaw/core": "0.12.17",
40
+ "@nextclaw/channel-extension-weixin": "0.1.4",
41
+ "@nextclaw/ncp-agent-runtime": "0.3.20",
42
+ "@nextclaw/ncp": "0.5.10",
43
+ "@nextclaw/ncp-mcp": "0.1.84",
44
+ "@nextclaw/ncp-http-agent-server": "0.3.22",
45
+ "@nextclaw/nextclaw-hermes-acp-bridge": "0.1.9",
46
+ "@nextclaw/ncp-toolkit": "0.5.15",
47
+ "@nextclaw/mcp": "0.1.82",
48
+ "@nextclaw/kernel": "0.1.6",
49
+ "@nextclaw/nextclaw-ncp-runtime-stdio-client": "0.1.10",
50
+ "@nextclaw/nextclaw-ncp-runtime-http-client": "0.1.9",
51
+ "@nextclaw/openclaw-compat": "1.0.17",
52
+ "@nextclaw/runtime": "0.2.49",
53
+ "@nextclaw/shared": "0.1.4",
54
+ "@nextclaw/remote": "0.1.94",
55
+ "@nextclaw/server": "0.12.17"
54
56
  },
55
57
  "devDependencies": {
56
58
  "@types/node": "^20.17.6",
@@ -60,7 +62,7 @@
60
62
  "vitest": "^4.1.2"
61
63
  },
62
64
  "scripts": {
63
- "build": "tsdown $(find src -name '*.ts' ! -name '*.test.ts' ! -path '*/tests/*') --dts --clean --target es2022 --no-fixedExtension --unbundle",
65
+ "build": "tsdown \"src/**/*.ts\" \"!src/**/*.test.ts\" \"!src/**/tests/**\" --dts --clean --target es2022 --no-fixedExtension --unbundle",
64
66
  "lint": "eslint .",
65
67
  "tsc": "tsc -p tsconfig.json",
66
68
  "test": "vitest"
@@ -1,15 +0,0 @@
1
- import { CompanionDisableCommandOptions, CompanionEnableCommandOptions, CompanionStartCommandOptions, CompanionStatusCommandOptions, CompanionStopCommandOptions } from "../../../shared/types/cli.types.js";
2
- import { CompanionProcessService } from "./services/companion-process.service.js";
3
-
4
- //#region src/cli/commands/companion/index.d.ts
5
- declare class CompanionCommands {
6
- private readonly companionProcessService;
7
- constructor(companionProcessService?: CompanionProcessService);
8
- readonly start: (options?: CompanionStartCommandOptions) => Promise<void>;
9
- readonly enable: (options?: CompanionEnableCommandOptions) => Promise<void>;
10
- readonly disable: (options?: CompanionDisableCommandOptions) => Promise<void>;
11
- readonly status: (options?: CompanionStatusCommandOptions) => Promise<void>;
12
- readonly stop: (options?: CompanionStopCommandOptions) => Promise<void>;
13
- }
14
- //#endregion
15
- export { CompanionCommands };