@nextclaw/service 0.1.11 → 0.1.13

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 (72) hide show
  1. package/dist/cli/commands/agent/agent-runtime.utils.d.ts +1 -2
  2. package/dist/cli/commands/agent/agent-runtime.utils.js +6 -50
  3. package/dist/cli/commands/agent/cli-agent-runner.utils.d.ts +0 -2
  4. package/dist/cli/commands/agent/cli-agent-runner.utils.js +7 -9
  5. package/dist/cli/commands/agent/services/agent-commands.service.js +0 -1
  6. package/dist/cli/commands/cron/services/cron-local.service.d.ts +1 -2
  7. package/dist/cli/commands/skills/index.js +1 -1
  8. package/dist/cli/commands/skills/marketplace-client.d.ts +11 -1
  9. package/dist/cli/commands/skills/marketplace-client.js +39 -1
  10. package/dist/cli/commands/skills/marketplace-command-options.utils.d.ts +1 -1
  11. package/dist/cli/commands/skills/marketplace.metadata.d.ts +3 -12
  12. package/dist/cli/commands/skills/marketplace.metadata.js +1 -87
  13. package/dist/cli/commands/skills/{marketplace.service.d.ts → marketplace.utils.d.ts} +1 -1
  14. package/dist/cli/commands/skills/{marketplace.service.js → marketplace.utils.js} +11 -47
  15. package/dist/cli/commands/skills/skills-query.service.d.ts +4 -37
  16. package/dist/cli/commands/skills/skills-query.service.js +16 -98
  17. package/dist/cli/commands/usage/services/llm-usage-command.service.d.ts +3 -5
  18. package/dist/cli/commands/usage/services/llm-usage-command.service.js +16 -26
  19. package/dist/commands/channel/channel-list-view.service.d.ts +0 -1
  20. package/dist/commands/channel/channel-list-view.service.js +6 -23
  21. package/dist/commands/channel/index.js +0 -1
  22. package/dist/commands/plugin/index.d.ts +2 -4
  23. package/dist/commands/plugin/index.js +8 -20
  24. package/dist/commands/plugin/{plugin-command-utils.d.ts → plugin-command.utils.d.ts} +1 -2
  25. package/dist/commands/plugin/{plugin-command-utils.js → plugin-command.utils.js} +2 -4
  26. package/dist/commands/plugin/{plugin-mutation-actions.d.ts → plugin-mutation-actions.utils.d.ts} +1 -1
  27. package/dist/commands/plugin/{plugin-mutation-actions.js → plugin-mutation-actions.utils.js} +2 -2
  28. package/dist/commands/service/services/autostart/linux-systemd-autostart.service.js +1 -1
  29. package/dist/commands/service/services/autostart/macos-launch-agent-autostart.service.js +1 -1
  30. package/dist/commands/service/services/autostart/windows-task-autostart.service.js +1 -1
  31. package/dist/launcher/npm-runtime-launcher.service.js +1 -1
  32. package/dist/service-runtime.service.d.ts +1 -1
  33. package/dist/service-runtime.service.js +7 -15
  34. package/dist/shared/controllers/gateway.controller.d.ts +3 -11
  35. package/dist/shared/controllers/gateway.controller.js +24 -180
  36. package/dist/shared/services/gateway/managers/gateway-plugin.manager.d.ts +2 -9
  37. package/dist/shared/services/gateway/managers/gateway-plugin.manager.js +30 -88
  38. package/dist/shared/services/gateway/nextclaw-app.service.d.ts +2 -7
  39. package/dist/shared/services/gateway/nextclaw-app.service.js +6 -16
  40. package/dist/shared/services/gateway/nextclaw-gateway-runtime.service.d.ts +4 -9
  41. package/dist/shared/services/gateway/nextclaw-gateway-runtime.service.js +12 -46
  42. package/dist/shared/services/gateway/{cron-job-handler.service.d.ts → utils/cron-job-handler.utils.d.ts} +3 -6
  43. package/dist/shared/services/gateway/utils/cron-job-handler.utils.js +57 -0
  44. package/dist/shared/services/marketplace/service-marketplace-installer.service.js +3 -3
  45. package/dist/shared/services/plugin/utils/plugin-runtime-bridge.utils.js +1 -1
  46. package/dist/shared/services/runtime/runtime-command.service.js +2 -2
  47. package/dist/shared/services/runtime/service-managed-startup.service.js +1 -1
  48. package/dist/shared/services/ui/companion-runtime.service.js +1 -1
  49. package/dist/shared/services/workspace/workspace-manager.service.js +8 -10
  50. package/dist/shared/utils/cli.utils.js +1 -1
  51. package/package.json +20 -20
  52. package/dist/cli/commands/usage/services/llm-usage-query.service.d.ts +0 -43
  53. package/dist/cli/commands/usage/services/llm-usage-query.service.js +0 -85
  54. package/dist/commands/plugin/development-source/dev-plugin-overrides.utils.d.ts +0 -18
  55. package/dist/commands/plugin/development-source/dev-plugin-overrides.utils.js +0 -111
  56. package/dist/commands/plugin/development-source/first-party-plugin-load-paths.utils.d.ts +0 -9
  57. package/dist/commands/plugin/development-source/first-party-plugin-load-paths.utils.js +0 -183
  58. package/dist/commands/plugin/plugin-extension-registry.d.ts +0 -10
  59. package/dist/commands/plugin/plugin-extension-registry.js +0 -35
  60. package/dist/commands/plugin/plugin-registry-loader.utils.d.ts +0 -14
  61. package/dist/commands/plugin/plugin-registry-loader.utils.js +0 -43
  62. package/dist/commands/plugin/plugin-reload.d.ts +0 -13
  63. package/dist/commands/plugin/plugin-reload.js +0 -42
  64. package/dist/shared/services/extensions/extension-lifecycle.service.d.ts +0 -63
  65. package/dist/shared/services/extensions/extension-lifecycle.service.js +0 -174
  66. package/dist/shared/services/extensions/service-extension-runtime.service.d.ts +0 -52
  67. package/dist/shared/services/extensions/service-extension-runtime.service.js +0 -325
  68. package/dist/shared/services/gateway/cron-job-handler.service.js +0 -100
  69. package/dist/shared/services/runtime/utils/skills-loader.utils.d.ts +0 -12
  70. package/dist/shared/services/runtime/utils/skills-loader.utils.js +0 -9
  71. package/dist/shared/services/session/service-deferred-ncp-agent.service.d.ts +0 -14
  72. package/dist/shared/services/session/service-deferred-ncp-agent.service.js +0 -85
@@ -1,174 +0,0 @@
1
- import { readFileSync, readdirSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { spawn } from "node:child_process";
4
- import { readFile, readdir } from "node:fs/promises";
5
- //#region src/shared/services/extensions/extension-lifecycle.service.ts
6
- const EXTENSION_MANIFEST_FILE = "nextclaw.extension.json";
7
- function readString(value) {
8
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
9
- }
10
- function readStringArray(value) {
11
- if (!Array.isArray(value)) return;
12
- return value.every((item) => typeof item === "string") ? value : void 0;
13
- }
14
- function readStringRecord(value) {
15
- if (!value || typeof value !== "object" || Array.isArray(value)) return;
16
- const entries = Object.entries(value);
17
- if (!entries.every(([, item]) => typeof item === "string")) return;
18
- return Object.fromEntries(entries);
19
- }
20
- function sanitizeExtensionNodeOptions(value) {
21
- if (!value?.trim()) return;
22
- const tokens = value.split(/\s+/).filter(Boolean);
23
- const sanitized = [];
24
- for (let index = 0; index < tokens.length; index += 1) {
25
- const token = tokens[index];
26
- if (token === "--conditions=development" || token === "-C=development") continue;
27
- if ((token === "--conditions" || token === "-C") && tokens[index + 1] === "development") {
28
- index += 1;
29
- continue;
30
- }
31
- sanitized.push(token);
32
- }
33
- return sanitized.length > 0 ? sanitized.join(" ") : void 0;
34
- }
35
- function toManifest(value, rootDir) {
36
- if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("extension manifest must be an object");
37
- const record = value;
38
- const server = record.server;
39
- if (!server || typeof server !== "object" || Array.isArray(server)) throw new Error("extension manifest server is required");
40
- const serverRecord = server;
41
- const id = readString(record.id);
42
- const command = readString(serverRecord.command);
43
- if (!id) throw new Error("extension manifest id is required");
44
- if (serverRecord.type !== "stdio") throw new Error("extension server.type must be stdio");
45
- if (!command) throw new Error("extension server.command is required");
46
- return {
47
- id,
48
- rootDir,
49
- ...readString(record.name) ? { name: readString(record.name) } : {},
50
- ...readString(record.version) ? { version: readString(record.version) } : {},
51
- server: {
52
- type: "stdio",
53
- command,
54
- ...readStringArray(serverRecord.args) ? { args: readStringArray(serverRecord.args) } : {},
55
- ...readStringRecord(serverRecord.env) ? { env: readStringRecord(serverRecord.env) } : {}
56
- },
57
- ...record.contributes && typeof record.contributes === "object" && !Array.isArray(record.contributes) ? { contributes: record.contributes } : {}
58
- };
59
- }
60
- var ExtensionManifestDiscoveryService = class {
61
- discover = async (roots) => {
62
- const manifests = [];
63
- for (const root of roots) manifests.push(...await this.discoverRoot(root));
64
- return manifests;
65
- };
66
- discoverSync = (roots) => {
67
- const manifests = [];
68
- for (const root of roots) manifests.push(...this.discoverRootSync(root));
69
- return manifests;
70
- };
71
- discoverRoot = async (root) => {
72
- const directManifest = await this.readManifestIfExists(join(root, EXTENSION_MANIFEST_FILE));
73
- if (directManifest) return [directManifest];
74
- const entries = await readdir(root, { withFileTypes: true }).catch(() => []);
75
- return (await Promise.all(entries.filter((entry) => entry.isDirectory()).map((entry) => this.readManifestIfExists(join(root, entry.name, EXTENSION_MANIFEST_FILE))))).filter((manifest) => Boolean(manifest));
76
- };
77
- discoverRootSync = (root) => {
78
- const directManifest = this.readManifestIfExistsSync(join(root, EXTENSION_MANIFEST_FILE));
79
- if (directManifest) return [directManifest];
80
- return this.readDirectoriesSync(root).map((entry) => this.readManifestIfExistsSync(join(root, entry, EXTENSION_MANIFEST_FILE))).filter((manifest) => Boolean(manifest));
81
- };
82
- readManifestIfExists = async (path) => {
83
- try {
84
- return toManifest(JSON.parse(await readFile(path, "utf-8")), dirname(path));
85
- } catch (error) {
86
- if (error.code === "ENOENT") return null;
87
- throw error;
88
- }
89
- };
90
- readManifestIfExistsSync = (path) => {
91
- try {
92
- return toManifest(JSON.parse(readFileSync(path, "utf-8")), dirname(path));
93
- } catch (error) {
94
- if (error.code === "ENOENT") return null;
95
- throw error;
96
- }
97
- };
98
- readDirectoriesSync = (root) => {
99
- try {
100
- return readdirSync(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
101
- } catch {
102
- return [];
103
- }
104
- };
105
- };
106
- var ExtensionLifecycleService = class {
107
- processes = /* @__PURE__ */ new Map();
108
- spawnProcess;
109
- logger;
110
- constructor(options) {
111
- this.options = options;
112
- this.spawnProcess = options.spawnProcess ?? spawn;
113
- this.logger = options.logger ?? console;
114
- }
115
- startAll = async (manifests) => {
116
- const started = [];
117
- for (const manifest of manifests) try {
118
- started.push(this.start(manifest));
119
- } catch (error) {
120
- this.logger.warn(`Extension ${manifest.id} failed to start: ${error instanceof Error ? error.message : String(error)}`);
121
- }
122
- return started;
123
- };
124
- start = (manifest) => {
125
- const existing = this.processes.get(manifest.id);
126
- if (existing) return existing;
127
- const child = this.spawnProcess(manifest.server.command === "node" || manifest.server.command === "node.exe" ? process.execPath : manifest.server.command, manifest.server.args ?? [], {
128
- cwd: manifest.rootDir,
129
- env: {
130
- ...process.env,
131
- NODE_OPTIONS: sanitizeExtensionNodeOptions(process.env.NODE_OPTIONS),
132
- ...manifest.server.env,
133
- NEXTCLAW_EXTENSION_ID: manifest.id,
134
- NEXTCLAW_EXTENSION_ENDPOINT: this.options.endpoint,
135
- NEXTCLAW_EXTENSION_TOKEN: this.options.token
136
- },
137
- stdio: [
138
- "ignore",
139
- "ignore",
140
- "inherit"
141
- ],
142
- windowsHide: true
143
- });
144
- const running = {
145
- manifest,
146
- process: child
147
- };
148
- this.processes.set(manifest.id, running);
149
- child.once("exit", () => {
150
- if (this.processes.get(manifest.id)?.process === child) this.processes.delete(manifest.id);
151
- this.logger.warn(`Extension ${manifest.id} exited.`);
152
- });
153
- child.once("error", (error) => {
154
- this.logger.warn(`Extension ${manifest.id} failed: ${error.message}`);
155
- });
156
- return running;
157
- };
158
- stopAll = async () => {
159
- const running = Array.from(this.processes.values());
160
- this.processes.clear();
161
- await Promise.all(running.map((entry) => this.stopProcess(entry.process)));
162
- };
163
- list = () => Array.from(this.processes.values());
164
- stopProcess = async (child) => {
165
- if (child.exitCode !== null || child.signalCode !== null) return;
166
- await new Promise((resolve) => {
167
- child.once("exit", () => resolve());
168
- child.kill();
169
- setTimeout(resolve, 1e3).unref();
170
- });
171
- };
172
- };
173
- //#endregion
174
- export { ExtensionLifecycleService, ExtensionManifestDiscoveryService };
@@ -1,52 +0,0 @@
1
- import { ExtensionLifecycleService, ExtensionManifestDiscoveryService, RunningExtensionProcess } from "./extension-lifecycle.service.js";
2
- import { NextclawGatewayRuntime } from "../gateway/nextclaw-gateway-runtime.service.js";
3
- import * as NextclawCore from "@nextclaw/core";
4
- import { PluginChannelBinding, PluginUiMetadata } from "@nextclaw/openclaw-compat";
5
- import { Ingress } from "@nextclaw/shared";
6
-
7
- //#region src/shared/services/extensions/service-extension-runtime.service.d.ts
8
- type Config$1 = NextclawCore.Config;
9
- type ExtensionRuntimeContributions = {
10
- channelBindings: PluginChannelBinding[];
11
- uiMetadata: PluginUiMetadata[];
12
- };
13
- declare function resolveBuiltinExtensionManifestRoots(): string[];
14
- declare function resolveExtensionManifestRoots(params: {
15
- config: Config$1;
16
- workspace: string;
17
- }): string[];
18
- declare function startDiscoveredExtensions(params: {
19
- config: Config$1;
20
- workspace: string;
21
- endpoint: string;
22
- token: string;
23
- discovery?: ExtensionManifestDiscoveryService;
24
- lifecycle?: ExtensionLifecycleService;
25
- }): Promise<{
26
- lifecycle: ExtensionLifecycleService;
27
- running: RunningExtensionProcess[];
28
- }>;
29
- declare class ServiceExtensionRuntime {
30
- private readonly gateway;
31
- readonly token: `${string}-${string}-${string}-${string}-${string}`;
32
- private lifecycle;
33
- private manifests;
34
- private readonly pendingRequests;
35
- constructor(gateway: NextclawGatewayRuntime);
36
- readonly registerIngressHandlers: (ingress: Ingress) => void;
37
- readonly loadContributions: () => Promise<ExtensionRuntimeContributions>;
38
- readonly start: () => Promise<void>;
39
- readonly stop: () => Promise<void>;
40
- private readonly handleChannelConfigGet;
41
- private readonly handleChannelMessageSubmit;
42
- private readonly handleExtensionResponse;
43
- private readonly discoverManifests;
44
- private readonly toContributions;
45
- private readonly readConfigUiHints;
46
- private readonly createChannelAuth;
47
- private readonly createChannelOutbound;
48
- private readonly requestExtension;
49
- private readonly assertAuthorized;
50
- }
51
- //#endregion
52
- export { ServiceExtensionRuntime, resolveBuiltinExtensionManifestRoots, resolveExtensionManifestRoots, startDiscoveredExtensions };
@@ -1,325 +0,0 @@
1
- import { resolveDevFirstPartyPluginDir } from "../../../commands/plugin/development-source/first-party-plugin-load-paths.utils.js";
2
- import { ExtensionLifecycleService, ExtensionManifestDiscoveryService } from "./extension-lifecycle.service.js";
3
- import { createRequire } from "node:module";
4
- import { getDataPath } from "@nextclaw/core";
5
- import { existsSync, readFileSync } from "node:fs";
6
- import { dirname, join, resolve } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
- import { randomUUID } from "node:crypto";
9
- //#region src/shared/services/extensions/service-extension-runtime.service.ts
10
- const EXTENSION_CONFIG_GET_INGRESS_TYPE = "extension.channel.config.get";
11
- const EXTENSION_MESSAGE_SUBMIT_INGRESS_TYPE = "extension.channel.message.submit";
12
- const EXTENSION_REQUEST_EVENT_TYPE = "extension.request";
13
- const EXTENSION_RESPONSE_INGRESS_TYPE = "extension.response";
14
- const serviceRequire = createRequire(import.meta.url);
15
- const EXTENSION_REQUEST_TIMEOUT_MS = 6e4;
16
- var ExtensionChannelClient = class {
17
- constructor(params) {
18
- this.params = params;
19
- }
20
- login = async ({ accountId, baseUrl, verbose }) => await this.params.request({
21
- extensionId: this.params.extensionId,
22
- kind: "channel.auth.login",
23
- payload: {
24
- channelId: this.params.channelId,
25
- accountId,
26
- baseUrl,
27
- verbose
28
- }
29
- });
30
- start = async (params) => await this.params.request({
31
- extensionId: this.params.extensionId,
32
- kind: "channel.auth.start",
33
- payload: {
34
- channelId: this.params.channelId,
35
- accountId: params.accountId,
36
- baseUrl: params.baseUrl,
37
- domain: params.domain
38
- }
39
- });
40
- poll = async ({ sessionId }) => await this.params.request({
41
- extensionId: this.params.extensionId,
42
- kind: "channel.auth.poll",
43
- payload: {
44
- channelId: this.params.channelId,
45
- sessionId
46
- }
47
- });
48
- sendText = async ({ to, text, accountId }) => await this.params.request({
49
- extensionId: this.params.extensionId,
50
- kind: "channel.outbound.sendText",
51
- payload: {
52
- channelId: this.params.channelId,
53
- to,
54
- text,
55
- accountId
56
- }
57
- });
58
- };
59
- function uniquePaths(paths) {
60
- const seen = /* @__PURE__ */ new Set();
61
- const unique = [];
62
- for (const path of paths) {
63
- const normalized = resolve(path);
64
- if (seen.has(normalized)) continue;
65
- seen.add(normalized);
66
- unique.push(normalized);
67
- }
68
- return unique;
69
- }
70
- function findExtensionManifestRoot(startPath) {
71
- let current = resolve(startPath);
72
- while (true) {
73
- if (existsSync(join(current, "nextclaw.extension.json"))) return current;
74
- const parent = dirname(current);
75
- if (parent === current) return;
76
- current = parent;
77
- }
78
- }
79
- function readBuiltinExtensionPackages() {
80
- if (process.env.NEXTCLAW_DISABLE_BUILTIN_EXTENSIONS === "1") return [];
81
- const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), "../../../..", "package.json");
82
- try {
83
- const packages = JSON.parse(readFileSync(packageJsonPath, "utf-8")).nextclaw?.builtinExtensions;
84
- return Array.isArray(packages) ? packages.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
85
- } catch {
86
- return [];
87
- }
88
- }
89
- function resolveBuiltinExtensionManifestRoots() {
90
- const roots = [];
91
- for (const packageName of readBuiltinExtensionPackages()) try {
92
- const root = findExtensionManifestRoot(dirname(serviceRequire.resolve(packageName)));
93
- if (root) roots.push(root);
94
- } catch {}
95
- return uniquePaths(roots);
96
- }
97
- function readRecord(value) {
98
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
99
- }
100
- function readRequiredString(value, name) {
101
- if (typeof value !== "string" || !value.trim()) throw new Error(`${name} is required`);
102
- return value.trim();
103
- }
104
- function readTextContent(value) {
105
- const content = readRecord(value);
106
- if (content.type !== "text" || typeof content.text !== "string") throw new Error("only text channel messages are supported by the first ingress bridge");
107
- return content.text;
108
- }
109
- function readOptionalString(value) {
110
- if (typeof value !== "string") return;
111
- return value.trim() || void 0;
112
- }
113
- function readOptionalNumber(value) {
114
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
115
- }
116
- function readInboundAttachments(value) {
117
- if (!Array.isArray(value)) return [];
118
- return value.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))).map((entry) => ({
119
- ...readOptionalString(entry.id) ? { id: readOptionalString(entry.id) } : {},
120
- ...readOptionalString(entry.name) ? { name: readOptionalString(entry.name) } : {},
121
- ...readOptionalString(entry.path) ? { path: readOptionalString(entry.path) } : {},
122
- ...readOptionalString(entry.url) ? { url: readOptionalString(entry.url) } : {},
123
- ...readOptionalString(entry.assetUri) ? { assetUri: readOptionalString(entry.assetUri) } : {},
124
- ...readOptionalString(entry.mimeType) ? { mimeType: readOptionalString(entry.mimeType) } : {},
125
- ...readOptionalNumber(entry.size) !== void 0 ? { size: readOptionalNumber(entry.size) } : {},
126
- ...readOptionalString(entry.source) ? { source: readOptionalString(entry.source) } : {},
127
- ...entry.status === "ready" || entry.status === "remote-only" ? { status: entry.status } : {},
128
- ...readOptionalString(entry.errorCode) ? { errorCode: readOptionalString(entry.errorCode) } : {}
129
- }));
130
- }
131
- function toInboundMessage(payload) {
132
- const metadata = readRecord(payload.metadata);
133
- return {
134
- channel: readRequiredString(payload.channelId, "channelId"),
135
- chatId: readRequiredString(payload.conversationId, "conversationId"),
136
- senderId: readRequiredString(payload.senderId, "senderId"),
137
- content: readTextContent(payload.content),
138
- timestamp: /* @__PURE__ */ new Date(),
139
- attachments: readInboundAttachments(payload.attachments),
140
- metadata
141
- };
142
- }
143
- function resolveExtensionManifestRoots(params) {
144
- const devExtensionsDir = resolveDevFirstPartyPluginDir(process.env.NEXTCLAW_DEV_FIRST_PARTY_PLUGIN_DIR);
145
- return uniquePaths([
146
- join(getDataPath(), "extensions"),
147
- join(params.workspace, ".nextclaw", "extensions"),
148
- ...devExtensionsDir ? [devExtensionsDir] : [],
149
- ...resolveBuiltinExtensionManifestRoots(),
150
- ...params.config.plugins.load?.paths ?? []
151
- ]);
152
- }
153
- async function startDiscoveredExtensions(params) {
154
- const { config, discovery: providedDiscovery, endpoint, lifecycle: providedLifecycle, token, workspace } = params;
155
- const discovery = providedDiscovery ?? new ExtensionManifestDiscoveryService();
156
- const lifecycle = providedLifecycle ?? new ExtensionLifecycleService({
157
- endpoint,
158
- token
159
- });
160
- const manifests = await discovery.discover(resolveExtensionManifestRoots({
161
- config,
162
- workspace
163
- }));
164
- return {
165
- lifecycle,
166
- running: await lifecycle.startAll(manifests)
167
- };
168
- }
169
- var ServiceExtensionRuntime = class {
170
- token = randomUUID();
171
- lifecycle = null;
172
- manifests = [];
173
- pendingRequests = /* @__PURE__ */ new Map();
174
- constructor(gateway) {
175
- this.gateway = gateway;
176
- }
177
- registerIngressHandlers = (ingress) => {
178
- ingress.addHandler(EXTENSION_CONFIG_GET_INGRESS_TYPE, this.handleChannelConfigGet);
179
- ingress.addHandler(EXTENSION_MESSAGE_SUBMIT_INGRESS_TYPE, this.handleChannelMessageSubmit);
180
- ingress.addHandler(EXTENSION_RESPONSE_INGRESS_TYPE, this.handleExtensionResponse);
181
- };
182
- loadContributions = async () => {
183
- this.manifests = await this.discoverManifests();
184
- return this.toContributions(this.manifests);
185
- };
186
- start = async () => {
187
- const endpoint = this.gateway.uiStartup.endpoint || null;
188
- if (!endpoint) return;
189
- const lifecycle = this.lifecycle ?? new ExtensionLifecycleService({
190
- endpoint,
191
- token: this.token
192
- });
193
- const manifests = this.manifests.length > 0 ? this.manifests : await this.discoverManifests();
194
- const running = await lifecycle.startAll(manifests);
195
- this.lifecycle = lifecycle;
196
- if (running.length > 0) console.log(`✓ Extensions started: ${running.map((entry) => entry.manifest.id).join(", ")}`);
197
- };
198
- stop = async () => {
199
- await this.lifecycle?.stopAll();
200
- this.lifecycle = null;
201
- for (const [requestId, request] of this.pendingRequests) {
202
- clearTimeout(request.timeout);
203
- request.reject(/* @__PURE__ */ new Error(`Extension request cancelled: ${requestId}`));
204
- }
205
- this.pendingRequests.clear();
206
- };
207
- handleChannelConfigGet = (envelope, context) => {
208
- this.assertAuthorized(context);
209
- const channelId = readRequiredString(readRecord(envelope.payload).channelId, "channelId");
210
- return { config: this.gateway.configManager.loadConfig().channels[channelId] ?? {} };
211
- };
212
- handleChannelMessageSubmit = async (envelope, context) => {
213
- this.assertAuthorized(context);
214
- await this.gateway.messageBus.publishInbound(toInboundMessage(readRecord(envelope.payload)));
215
- return { accepted: true };
216
- };
217
- handleExtensionResponse = (envelope, context) => {
218
- this.assertAuthorized(context);
219
- const payload = readRecord(envelope.payload);
220
- const requestId = readRequiredString(payload.requestId, "requestId");
221
- const pending = this.pendingRequests.get(requestId);
222
- if (!pending) return { accepted: false };
223
- this.pendingRequests.delete(requestId);
224
- clearTimeout(pending.timeout);
225
- if (payload.ok === false) {
226
- const error = readRecord(payload.error);
227
- const message = typeof error.message === "string" && error.message.trim() ? error.message.trim() : "Extension request failed";
228
- pending.reject(new Error(message));
229
- return { accepted: true };
230
- }
231
- pending.resolve(payload.data);
232
- return { accepted: true };
233
- };
234
- discoverManifests = async () => {
235
- return await new ExtensionManifestDiscoveryService().discover(resolveExtensionManifestRoots({
236
- config: this.gateway.configManager.loadConfig(),
237
- workspace: this.gateway.workspace
238
- }));
239
- };
240
- toContributions = (manifests) => {
241
- const channelBindings = [];
242
- const uiMetadata = [];
243
- for (const manifest of manifests) {
244
- const channels = manifest.contributes?.channels ?? [];
245
- for (const channel of channels) {
246
- const channelId = readOptionalString(channel.id);
247
- if (!channelId) continue;
248
- const configUiHints = this.readConfigUiHints(channel.configUiHints);
249
- channelBindings.push({
250
- pluginId: manifest.id,
251
- channelId,
252
- channel: {
253
- id: channelId,
254
- meta: {
255
- label: channel.name ?? channelId,
256
- selectionLabel: channel.name ?? channelId,
257
- ...channel.description ? { blurb: channel.description } : {},
258
- ...channel.meta ?? {}
259
- },
260
- ...channel.configSchema ? { configSchema: {
261
- schema: channel.configSchema,
262
- ...configUiHints ? { uiHints: configUiHints } : {}
263
- } } : {},
264
- ...channel.auth ? { auth: this.createChannelAuth(manifest.id, channelId) } : {},
265
- ...channel.outbound?.text ? { outbound: this.createChannelOutbound(manifest.id, channelId) } : {}
266
- }
267
- });
268
- uiMetadata.push({
269
- id: manifest.id,
270
- configSchema: channel.configSchema,
271
- ...configUiHints ? { configUiHints } : {}
272
- });
273
- }
274
- }
275
- return {
276
- channelBindings,
277
- uiMetadata
278
- };
279
- };
280
- readConfigUiHints = (value) => {
281
- if (!value || typeof value !== "object" || Array.isArray(value)) return;
282
- return value;
283
- };
284
- createChannelAuth = (extensionId, channelId) => new ExtensionChannelClient({
285
- extensionId,
286
- channelId,
287
- request: this.requestExtension
288
- });
289
- createChannelOutbound = (extensionId, channelId) => new ExtensionChannelClient({
290
- extensionId,
291
- channelId,
292
- request: this.requestExtension
293
- });
294
- requestExtension = async (params) => {
295
- const requestId = randomUUID();
296
- const result = new Promise((resolvePromise, rejectPromise) => {
297
- const timeout = setTimeout(() => {
298
- this.pendingRequests.delete(requestId);
299
- rejectPromise(/* @__PURE__ */ new Error(`Extension request timed out: ${params.kind}`));
300
- }, EXTENSION_REQUEST_TIMEOUT_MS);
301
- this.pendingRequests.set(requestId, {
302
- resolve: (value) => resolvePromise(value),
303
- reject: rejectPromise,
304
- timeout
305
- });
306
- });
307
- this.gateway.appEventBus.emitEnvelope({
308
- type: EXTENSION_REQUEST_EVENT_TYPE,
309
- payload: {
310
- requestId,
311
- extensionId: params.extensionId,
312
- kind: params.kind,
313
- payload: params.payload
314
- },
315
- emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
316
- source: "backend"
317
- });
318
- return await result;
319
- };
320
- assertAuthorized = (context) => {
321
- if (context.token !== this.token) throw new Error("Unauthorized ingress token");
322
- };
323
- };
324
- //#endregion
325
- export { ServiceExtensionRuntime, resolveBuiltinExtensionManifestRoots, resolveExtensionManifestRoots, startDiscoveredExtensions };
@@ -1,100 +0,0 @@
1
- import { NcpEventType } from "@nextclaw/ncp";
2
- //#region src/shared/services/gateway/cron-job-handler.service.ts
3
- function normalizeOptionalString(value) {
4
- if (typeof value !== "string") return;
5
- return value.trim() || void 0;
6
- }
7
- function buildCronSessionMetadata(params) {
8
- const { job, agentId, accountId } = params;
9
- const channel = normalizeOptionalString(job.payload.channel) ?? "cli";
10
- const chatId = normalizeOptionalString(job.payload.to) ?? "direct";
11
- const metadata = {
12
- agentId,
13
- agent_id: agentId,
14
- channel,
15
- chatId,
16
- chat_id: chatId,
17
- label: job.name,
18
- cron_job_id: job.id,
19
- cron_job_name: job.name,
20
- session_origin: "cron"
21
- };
22
- if (accountId) {
23
- metadata.accountId = accountId;
24
- metadata.account_id = accountId;
25
- }
26
- return metadata;
27
- }
28
- function buildCronUserMessage(params) {
29
- const { sessionId, content, metadata } = params;
30
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
31
- return {
32
- id: `${sessionId}:user:cron:${timestamp}`,
33
- sessionId,
34
- role: "user",
35
- status: "final",
36
- timestamp,
37
- parts: [{
38
- type: "text",
39
- text: content
40
- }],
41
- metadata: structuredClone(metadata)
42
- };
43
- }
44
- function extractMessageText(message) {
45
- return message.parts.flatMap((part) => {
46
- if (part.type === "text" || part.type === "rich-text") return [part.text];
47
- return [];
48
- }).map((text) => text.trim()).filter((text) => text.length > 0).join("\n\n");
49
- }
50
- async function runJobOverNcp(params) {
51
- const { agent, sessionId, message, metadata, missingCompletedMessageError, runErrorMessage } = params;
52
- let completedMessage;
53
- for await (const event of agent.runApi.send({
54
- sessionId,
55
- message,
56
- metadata
57
- })) {
58
- if (event.type === NcpEventType.MessageFailed) throw new Error(event.payload.error.message);
59
- if (event.type === NcpEventType.RunError) throw new Error(event.payload.error ?? runErrorMessage);
60
- if (event.type === NcpEventType.MessageCompleted) completedMessage = event.payload.message;
61
- }
62
- if (!completedMessage) throw new Error(missingCompletedMessageError);
63
- return extractMessageText(completedMessage);
64
- }
65
- function createCronJobHandler(params) {
66
- return async (job) => {
67
- const ncpAgent = params.resolveNcpAgent();
68
- if (!ncpAgent) throw new Error("NCP agent is not ready for cron execution.");
69
- const accountId = normalizeOptionalString(job.payload.accountId);
70
- const agentId = normalizeOptionalString(job.payload.agentId) ?? "main";
71
- const sessionId = normalizeOptionalString(job.payload.sessionId) ?? `cron:${job.id}`;
72
- const metadata = buildCronSessionMetadata({
73
- job,
74
- agentId,
75
- accountId
76
- });
77
- const response = await runJobOverNcp({
78
- agent: ncpAgent,
79
- sessionId,
80
- message: buildCronUserMessage({
81
- sessionId,
82
- content: job.payload.message,
83
- metadata
84
- }),
85
- metadata,
86
- missingCompletedMessageError: "cron job completed without a final assistant message",
87
- runErrorMessage: "cron job failed"
88
- });
89
- if (job.payload.deliver && job.payload.to) await params.bus.publishOutbound({
90
- channel: job.payload.channel ?? "cli",
91
- chatId: job.payload.to,
92
- content: response,
93
- media: [],
94
- metadata
95
- });
96
- return response;
97
- };
98
- }
99
- //#endregion
100
- export { createCronJobHandler };
@@ -1,12 +0,0 @@
1
- //#region src/shared/services/runtime/utils/skills-loader.utils.d.ts
2
- type SkillInfo = {
3
- name: string;
4
- path: string;
5
- source: "workspace" | "builtin";
6
- };
7
- type SkillsLoaderInstance = {
8
- listSkills: (filterUnavailable?: boolean) => SkillInfo[];
9
- };
10
- declare function createSkillsLoader(workspace: string): SkillsLoaderInstance | null;
11
- //#endregion
12
- export { createSkillsLoader };
@@ -1,9 +0,0 @@
1
- import * as NextclawCore from "@nextclaw/core";
2
- //#region src/shared/services/runtime/utils/skills-loader.utils.ts
3
- function createSkillsLoader(workspace) {
4
- const ctor = NextclawCore.SkillsLoader;
5
- if (!ctor) return null;
6
- return new ctor(workspace);
7
- }
8
- //#endregion
9
- export { createSkillsLoader };
@@ -1,14 +0,0 @@
1
- import { AgentRuntimeHandle } from "@nextclaw/kernel";
2
- import { UiNcpAgent } from "@nextclaw/server";
3
-
4
- //#region src/shared/services/session/service-deferred-ncp-agent.service.d.ts
5
- type DeferredUiNcpAgentController = {
6
- agent: UiNcpAgent;
7
- activate: (agent: AgentRuntimeHandle) => void;
8
- clear: () => void;
9
- close: () => Promise<void>;
10
- isReady: () => boolean;
11
- };
12
- declare function createDeferredUiNcpAgent(basePath?: string): DeferredUiNcpAgentController;
13
- //#endregion
14
- export { DeferredUiNcpAgentController, createDeferredUiNcpAgent };