nextclaw 0.16.0 → 0.16.2
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/dist/cli/index.js +355 -968
- package/package.json +8 -8
- package/ui-dist/assets/{ChannelsList-BqsOYnXz.js → ChannelsList-CKl1Zg8f.js} +3 -3
- package/ui-dist/assets/ChatPage-BJgO27mk.js +37 -0
- package/ui-dist/assets/{DocBrowser-BmL0QXBZ.js → DocBrowser-DYRBs4-z.js} +1 -1
- package/ui-dist/assets/{LogoBadge-C1HiPZPf.js → LogoBadge-33Qlv3Hg.js} +1 -1
- package/ui-dist/assets/MarketplacePage-B8BZVtjV.js +49 -0
- package/ui-dist/assets/{McpMarketplacePage-CLHFnNBd.js → McpMarketplacePage-BRuE5fJJ.js} +2 -2
- package/ui-dist/assets/{ModelConfig-LQSR58tc.js → ModelConfig-BiFblwO-.js} +1 -1
- package/ui-dist/assets/ProvidersList-9goRgHE4.js +1 -0
- package/ui-dist/assets/RemoteAccessPage-5vCxZPS6.js +1 -0
- package/ui-dist/assets/RuntimeConfig-BmDFHBdW.js +1 -0
- package/ui-dist/assets/{SearchConfig-Chzo_JGs.js → SearchConfig-CJx5CKwG.js} +1 -1
- package/ui-dist/assets/{SecretsConfig-CEIbjZYA.js → SecretsConfig-B91efXoK.js} +2 -2
- package/ui-dist/assets/SessionsConfig-CbFPVmx3.js +2 -0
- package/ui-dist/assets/index-BtAuUyww.css +1 -0
- package/ui-dist/assets/index-COJomMe9.js +8 -0
- package/ui-dist/assets/{label-GACO2RzW.js → label-BnSDpjhL.js} +1 -1
- package/ui-dist/assets/ncp-session-adapter-w8ZHprab.js +1 -0
- package/ui-dist/assets/{page-layout-DjXaK3A3.js → page-layout-B1RIu5-r.js} +1 -1
- package/ui-dist/assets/popover-ChzbCIfO.js +1 -0
- package/ui-dist/assets/security-config-eYa6Ovfa.js +1 -0
- package/ui-dist/assets/skeleton-D4Eyop0R.js +1 -0
- package/ui-dist/assets/{status-dot-IWEBezqb.js → status-dot-CrCw5tkJ.js} +1 -1
- package/ui-dist/assets/{switch-DCHAJSrA.js → switch-C3vVTpfU.js} +1 -1
- package/ui-dist/assets/tabs-custom-Ilrgt6n1.js +1 -0
- package/ui-dist/assets/useConfirmDialog-BeaFLDO8.js +1 -0
- package/ui-dist/assets/{vendor-CNhxtHCf.js → vendor-waGu-koL.js} +101 -86
- package/ui-dist/index.html +3 -3
- package/ui-dist/assets/ChatPage-CJBYKR-Y.js +0 -38
- package/ui-dist/assets/MarketplacePage-BIRP0NRS.js +0 -49
- package/ui-dist/assets/ProvidersList-CwI-mxah.js +0 -1
- package/ui-dist/assets/RemoteAccessPage-Cw5BqZb6.js +0 -1
- package/ui-dist/assets/RuntimeConfig-DbowSRAb.js +0 -1
- package/ui-dist/assets/SessionsConfig-BR8GfGWL.js +0 -2
- package/ui-dist/assets/chat-message-CPG7zxRR.js +0 -3
- package/ui-dist/assets/index-j6A_-1b6.js +0 -8
- package/ui-dist/assets/index-kaPUhd-8.css +0 -1
- package/ui-dist/assets/popover-DTaFiTmU.js +0 -1
- package/ui-dist/assets/security-config-Dk-yoKvK.js +0 -1
- package/ui-dist/assets/skeleton-Dm2xOBSA.js +0 -1
- package/ui-dist/assets/tabs-custom-DKSbDSB9.js +0 -1
- package/ui-dist/assets/useConfirmDialog-ByJ8A8n7.js +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadConfig as loadConfig19,
|
|
11
11
|
saveConfig as saveConfig11,
|
|
12
12
|
getConfigPath as getConfigPath11,
|
|
13
|
-
getDataDir as
|
|
13
|
+
getDataDir as getDataDir10,
|
|
14
14
|
getWorkspacePath as getWorkspacePath12,
|
|
15
15
|
expandHome as expandHome2,
|
|
16
16
|
MessageBus as MessageBus3,
|
|
@@ -27,8 +27,8 @@ import {
|
|
|
27
27
|
resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints3,
|
|
28
28
|
setPluginRuntimeBridge as setPluginRuntimeBridge3
|
|
29
29
|
} from "@nextclaw/openclaw-compat";
|
|
30
|
-
import { existsSync as
|
|
31
|
-
import { join as
|
|
30
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
31
|
+
import { join as join9, resolve as resolve14 } from "path";
|
|
32
32
|
import { createInterface as createInterface3 } from "readline";
|
|
33
33
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
34
34
|
import { spawn as spawn4 } from "child_process";
|
|
@@ -75,9 +75,9 @@ var RestartCoordinator = class {
|
|
|
75
75
|
message: "Restart already scheduled; skipping duplicate request."
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
-
const
|
|
78
|
+
const delay2 = typeof request.delayMs === "number" && Number.isFinite(request.delayMs) ? Math.max(0, Math.floor(request.delayMs)) : 100;
|
|
79
79
|
this.exitScheduled = true;
|
|
80
|
-
this.deps.scheduleProcessExit(
|
|
80
|
+
this.deps.scheduleProcessExit(delay2, reason);
|
|
81
81
|
return {
|
|
82
82
|
status: "exit-scheduled",
|
|
83
83
|
message: `Restart scheduled (${reason}).`
|
|
@@ -197,6 +197,46 @@ function parseSessionKey(sessionKey) {
|
|
|
197
197
|
};
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
// src/cli/startup-trace.ts
|
|
201
|
+
var STARTUP_TRACE_ENABLED = process.env.NEXTCLAW_STARTUP_TRACE === "1";
|
|
202
|
+
var STARTUP_TRACE_ORIGIN_MS = Date.now();
|
|
203
|
+
function formatFields(fields) {
|
|
204
|
+
if (!fields) {
|
|
205
|
+
return "";
|
|
206
|
+
}
|
|
207
|
+
const parts = Object.entries(fields).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}=${String(value)}`);
|
|
208
|
+
return parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
209
|
+
}
|
|
210
|
+
function logStartupTrace(step, fields) {
|
|
211
|
+
if (!STARTUP_TRACE_ENABLED) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const elapsedMs = Date.now() - STARTUP_TRACE_ORIGIN_MS;
|
|
215
|
+
console.log(`[startup-trace] +${elapsedMs}ms ${step}${formatFields(fields)}`);
|
|
216
|
+
}
|
|
217
|
+
function measureStartupSync(step, fn, fields) {
|
|
218
|
+
const startedAt = Date.now();
|
|
219
|
+
try {
|
|
220
|
+
return fn();
|
|
221
|
+
} finally {
|
|
222
|
+
logStartupTrace(step, {
|
|
223
|
+
...fields,
|
|
224
|
+
duration_ms: Date.now() - startedAt
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function measureStartupAsync(step, fn, fields) {
|
|
229
|
+
const startedAt = Date.now();
|
|
230
|
+
try {
|
|
231
|
+
return await fn();
|
|
232
|
+
} finally {
|
|
233
|
+
logStartupTrace(step, {
|
|
234
|
+
...fields,
|
|
235
|
+
duration_ms: Date.now() - startedAt
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
200
240
|
// src/cli/skills/marketplace.ts
|
|
201
241
|
import { cpSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
202
242
|
import { basename, dirname, isAbsolute, join, relative, resolve as resolve3 } from "path";
|
|
@@ -3953,12 +3993,13 @@ import {
|
|
|
3953
3993
|
startPluginChannelGateways as startPluginChannelGateways2,
|
|
3954
3994
|
stopPluginChannelGateways as stopPluginChannelGateways2
|
|
3955
3995
|
} from "@nextclaw/openclaw-compat";
|
|
3956
|
-
import { appendFileSync, closeSync as closeSync2, cpSync as cpSync2, existsSync as
|
|
3957
|
-
import { dirname as dirname3, join as
|
|
3996
|
+
import { appendFileSync, closeSync as closeSync2, cpSync as cpSync2, existsSync as existsSync11, mkdirSync as mkdirSync5, openSync as openSync2 } from "fs";
|
|
3997
|
+
import { dirname as dirname3, join as join7, resolve as resolve12 } from "path";
|
|
3958
3998
|
import { spawn as spawn3 } from "child_process";
|
|
3959
3999
|
import { request as httpRequest } from "http";
|
|
3960
4000
|
import { request as httpsRequest } from "https";
|
|
3961
4001
|
import { createServer as createNetServer2 } from "net";
|
|
4002
|
+
import { setImmediate as waitForNextTick } from "timers/promises";
|
|
3962
4003
|
import chokidar from "chokidar";
|
|
3963
4004
|
|
|
3964
4005
|
// src/cli/missing-provider.ts
|
|
@@ -6893,11 +6934,11 @@ var GatewayControllerImpl = class {
|
|
|
6893
6934
|
await this.deps.requestRestart(options);
|
|
6894
6935
|
return;
|
|
6895
6936
|
}
|
|
6896
|
-
const
|
|
6937
|
+
const delay2 = typeof options?.delayMs === "number" && Number.isFinite(options.delayMs) ? Math.max(0, options.delayMs) : 100;
|
|
6897
6938
|
console.log(`Gateway restart requested via tool${options?.reason ? ` (${options.reason})` : ""}.`);
|
|
6898
6939
|
setTimeout(() => {
|
|
6899
6940
|
process.exit(0);
|
|
6900
|
-
},
|
|
6941
|
+
}, delay2);
|
|
6901
6942
|
}
|
|
6902
6943
|
status() {
|
|
6903
6944
|
return {
|
|
@@ -7776,30 +7817,67 @@ var {
|
|
|
7776
7817
|
saveConfig: saveConfig10,
|
|
7777
7818
|
SessionManager
|
|
7778
7819
|
} = NextclawCore;
|
|
7820
|
+
function createGatewayShellContext(params) {
|
|
7821
|
+
const runtimeConfigPath = getConfigPath9();
|
|
7822
|
+
const config2 = resolveConfigSecrets3(loadConfig17(), { configPath: runtimeConfigPath });
|
|
7823
|
+
const workspace = getWorkspacePath10(config2.agents.defaults.workspace);
|
|
7824
|
+
const cronStorePath = join6(getDataDir8(), "cron", "jobs.json");
|
|
7825
|
+
const cron2 = new CronService2(cronStorePath);
|
|
7826
|
+
const uiConfig = resolveUiConfig(config2, params.uiOverrides);
|
|
7827
|
+
const uiStaticDir = params.uiStaticDir === void 0 ? resolveUiStaticDir() : params.uiStaticDir;
|
|
7828
|
+
const remoteModule = createManagedRemoteModuleForUi({
|
|
7829
|
+
loadConfig: () => resolveConfigSecrets3(loadConfig17(), { configPath: runtimeConfigPath }),
|
|
7830
|
+
uiConfig
|
|
7831
|
+
});
|
|
7832
|
+
return {
|
|
7833
|
+
runtimeConfigPath,
|
|
7834
|
+
config: config2,
|
|
7835
|
+
workspace,
|
|
7836
|
+
cron: cron2,
|
|
7837
|
+
uiConfig,
|
|
7838
|
+
uiStaticDir,
|
|
7839
|
+
remoteModule
|
|
7840
|
+
};
|
|
7841
|
+
}
|
|
7779
7842
|
function createGatewayStartupContext(params) {
|
|
7780
7843
|
const state = {};
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
state.
|
|
7786
|
-
state.
|
|
7844
|
+
const shellContext = params.shellContext ?? createGatewayShellContext({
|
|
7845
|
+
uiOverrides: params.uiOverrides,
|
|
7846
|
+
uiStaticDir: params.uiStaticDir
|
|
7847
|
+
});
|
|
7848
|
+
state.runtimeConfigPath = shellContext.runtimeConfigPath;
|
|
7849
|
+
state.config = shellContext.config;
|
|
7850
|
+
state.workspace = shellContext.workspace;
|
|
7851
|
+
state.cron = shellContext.cron;
|
|
7852
|
+
state.uiConfig = shellContext.uiConfig;
|
|
7853
|
+
state.uiStaticDir = shellContext.uiStaticDir;
|
|
7854
|
+
state.remoteModule = shellContext.remoteModule;
|
|
7855
|
+
state.pluginRegistry = measureStartupSync(
|
|
7856
|
+
"service.gateway_context.load_plugin_registry",
|
|
7857
|
+
() => loadPluginRegistry(state.config, state.workspace)
|
|
7858
|
+
);
|
|
7859
|
+
state.pluginChannelBindings = measureStartupSync(
|
|
7860
|
+
"service.gateway_context.get_plugin_channel_bindings",
|
|
7861
|
+
() => getPluginChannelBindings4(state.pluginRegistry)
|
|
7862
|
+
);
|
|
7863
|
+
state.extensionRegistry = measureStartupSync(
|
|
7864
|
+
"service.gateway_context.to_extension_registry",
|
|
7865
|
+
() => toExtensionRegistry(state.pluginRegistry)
|
|
7866
|
+
);
|
|
7787
7867
|
logPluginDiagnostics(state.pluginRegistry);
|
|
7788
7868
|
state.bus = new MessageBus();
|
|
7789
7869
|
const provider = params.allowMissingProvider === true ? params.makeProvider(state.config, { allowMissing: true }) : params.makeProvider(state.config);
|
|
7790
|
-
state.providerManager =
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
state.
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
uiConfig: state.uiConfig
|
|
7802
|
-
});
|
|
7870
|
+
state.providerManager = measureStartupSync(
|
|
7871
|
+
"service.gateway_context.provider_manager",
|
|
7872
|
+
() => new ProviderManager({
|
|
7873
|
+
defaultProvider: provider ?? params.makeMissingProvider(state.config),
|
|
7874
|
+
config: state.config
|
|
7875
|
+
})
|
|
7876
|
+
);
|
|
7877
|
+
state.sessionManager = measureStartupSync(
|
|
7878
|
+
"service.gateway_context.session_manager",
|
|
7879
|
+
() => new SessionManager(state.workspace)
|
|
7880
|
+
);
|
|
7803
7881
|
if (!provider) {
|
|
7804
7882
|
console.warn(
|
|
7805
7883
|
"Warning: No API key configured. The gateway is running, but agent replies are disabled until provider config is set."
|
|
@@ -7811,62 +7889,71 @@ function createGatewayStartupContext(params) {
|
|
|
7811
7889
|
state.sessionManager,
|
|
7812
7890
|
state.extensionRegistry.channels
|
|
7813
7891
|
);
|
|
7814
|
-
state.reloader =
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7892
|
+
state.reloader = measureStartupSync(
|
|
7893
|
+
"service.gateway_context.config_reloader",
|
|
7894
|
+
() => new ConfigReloader({
|
|
7895
|
+
initialConfig: state.config,
|
|
7896
|
+
channels: channels2,
|
|
7897
|
+
bus: state.bus,
|
|
7898
|
+
sessionManager: state.sessionManager,
|
|
7899
|
+
providerManager: state.providerManager,
|
|
7900
|
+
makeProvider: (nextConfig) => params.makeProvider(nextConfig, { allowMissing: true }) ?? params.makeMissingProvider(nextConfig),
|
|
7901
|
+
loadConfig: () => resolveConfigSecrets3(loadConfig17(), { configPath: state.runtimeConfigPath }),
|
|
7902
|
+
resolveChannelConfig: (nextConfig) => resolveChannelConfigView(nextConfig, state.pluginChannelBindings),
|
|
7903
|
+
getExtensionChannels: () => state.extensionRegistry.channels,
|
|
7904
|
+
onRestartRequired: (paths) => {
|
|
7905
|
+
void params.requestRestart({
|
|
7906
|
+
reason: `config reload requires restart: ${paths.join(", ")}`,
|
|
7907
|
+
manualMessage: `Config changes require restart: ${paths.join(", ")}`,
|
|
7908
|
+
strategy: "background-service-or-manual"
|
|
7909
|
+
});
|
|
7910
|
+
}
|
|
7911
|
+
})
|
|
7912
|
+
);
|
|
7832
7913
|
state.applyLiveConfigReload = async () => {
|
|
7833
7914
|
await state.reloader.applyReloadPlan(resolveConfigSecrets3(loadConfig17(), { configPath: state.runtimeConfigPath }));
|
|
7834
7915
|
};
|
|
7835
|
-
state.gatewayController =
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
bus: state.bus,
|
|
7853
|
-
providerManager: state.providerManager,
|
|
7854
|
-
sessionManager: state.sessionManager,
|
|
7855
|
-
config: state.config,
|
|
7856
|
-
cronService: state.cron,
|
|
7857
|
-
restrictToWorkspace: state.config.tools.restrictToWorkspace,
|
|
7858
|
-
searchConfig: state.config.search,
|
|
7859
|
-
execConfig: state.config.tools.exec,
|
|
7860
|
-
contextConfig: state.config.agents.context,
|
|
7861
|
-
gatewayController: state.gatewayController,
|
|
7862
|
-
extensionRegistry: state.extensionRegistry,
|
|
7863
|
-
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
7864
|
-
registry: state.pluginRegistry,
|
|
7865
|
-
channel,
|
|
7866
|
-
cfg: resolveConfigSecrets3(loadConfig17(), { configPath: state.runtimeConfigPath }),
|
|
7867
|
-
accountId
|
|
7916
|
+
state.gatewayController = measureStartupSync(
|
|
7917
|
+
"service.gateway_context.gateway_controller",
|
|
7918
|
+
() => new GatewayControllerImpl({
|
|
7919
|
+
reloader: state.reloader,
|
|
7920
|
+
cron: state.cron,
|
|
7921
|
+
sessionManager: state.sessionManager,
|
|
7922
|
+
getConfigPath: getConfigPath9,
|
|
7923
|
+
saveConfig: saveConfig10,
|
|
7924
|
+
requestRestart: async (options) => {
|
|
7925
|
+
await params.requestRestart({
|
|
7926
|
+
reason: options?.reason ?? "gateway tool restart",
|
|
7927
|
+
manualMessage: "Restart the gateway to apply changes.",
|
|
7928
|
+
strategy: "background-service-or-exit",
|
|
7929
|
+
delayMs: options?.delayMs,
|
|
7930
|
+
silentOnServiceRestart: true
|
|
7931
|
+
});
|
|
7932
|
+
}
|
|
7868
7933
|
})
|
|
7869
|
-
|
|
7934
|
+
);
|
|
7935
|
+
state.runtimePool = measureStartupSync(
|
|
7936
|
+
"service.gateway_context.runtime_pool",
|
|
7937
|
+
() => new GatewayAgentRuntimePool({
|
|
7938
|
+
bus: state.bus,
|
|
7939
|
+
providerManager: state.providerManager,
|
|
7940
|
+
sessionManager: state.sessionManager,
|
|
7941
|
+
config: state.config,
|
|
7942
|
+
cronService: state.cron,
|
|
7943
|
+
restrictToWorkspace: state.config.tools.restrictToWorkspace,
|
|
7944
|
+
searchConfig: state.config.search,
|
|
7945
|
+
execConfig: state.config.tools.exec,
|
|
7946
|
+
contextConfig: state.config.agents.context,
|
|
7947
|
+
gatewayController: state.gatewayController,
|
|
7948
|
+
extensionRegistry: state.extensionRegistry,
|
|
7949
|
+
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
7950
|
+
registry: state.pluginRegistry,
|
|
7951
|
+
channel,
|
|
7952
|
+
cfg: resolveConfigSecrets3(loadConfig17(), { configPath: state.runtimeConfigPath }),
|
|
7953
|
+
accountId
|
|
7954
|
+
})
|
|
7955
|
+
})
|
|
7956
|
+
);
|
|
7870
7957
|
state.cron.onJob = createCronJobHandler({ runtimePool: state.runtimePool, bus: state.bus });
|
|
7871
7958
|
state.heartbeat = new HeartbeatService(
|
|
7872
7959
|
state.workspace,
|
|
@@ -7978,755 +8065,13 @@ function createDeferredUiNcpAgent(basePath = DEFAULT_BASE_PATH) {
|
|
|
7978
8065
|
};
|
|
7979
8066
|
}
|
|
7980
8067
|
|
|
7981
|
-
// src/cli/commands/service-ui-chat-runtime.ts
|
|
7982
|
-
import { parseAgentScopedSessionKey as parseAgentScopedSessionKey2 } from "@nextclaw/core";
|
|
7983
|
-
function createServiceUiChatRuntime(params) {
|
|
7984
|
-
return {
|
|
7985
|
-
listSessionTypes: async () => {
|
|
7986
|
-
const options = params.runtimePool.listAvailableEngineKinds().map((value) => ({
|
|
7987
|
-
value,
|
|
7988
|
-
label: resolveUiSessionTypeLabel(value)
|
|
7989
|
-
}));
|
|
7990
|
-
return {
|
|
7991
|
-
defaultType: "native",
|
|
7992
|
-
options
|
|
7993
|
-
};
|
|
7994
|
-
},
|
|
7995
|
-
getCapabilities: async (request) => {
|
|
7996
|
-
const sessionKey = typeof request.sessionKey === "string" && request.sessionKey.trim().length > 0 ? request.sessionKey.trim() : `ui:capability:${Date.now().toString(36)}`;
|
|
7997
|
-
const capability = params.runtimePool.supportsTurnAbort({
|
|
7998
|
-
sessionKey,
|
|
7999
|
-
agentId: typeof request.agentId === "string" ? request.agentId : void 0,
|
|
8000
|
-
channel: "ui",
|
|
8001
|
-
chatId: "web-ui",
|
|
8002
|
-
metadata: {}
|
|
8003
|
-
});
|
|
8004
|
-
return {
|
|
8005
|
-
stopSupported: capability.supported,
|
|
8006
|
-
...capability.reason ? { stopReason: capability.reason } : {}
|
|
8007
|
-
};
|
|
8008
|
-
},
|
|
8009
|
-
processTurn: async (request) => {
|
|
8010
|
-
const resolved = resolveChatTurnParams(request);
|
|
8011
|
-
const reply = await params.runtimePool.processDirect({
|
|
8012
|
-
content: request.message,
|
|
8013
|
-
sessionKey: resolved.sessionKey,
|
|
8014
|
-
channel: resolved.channel,
|
|
8015
|
-
chatId: resolved.chatId,
|
|
8016
|
-
agentId: resolved.inferredAgentId,
|
|
8017
|
-
metadata: resolved.metadata
|
|
8018
|
-
});
|
|
8019
|
-
return buildTurnResult({
|
|
8020
|
-
reply,
|
|
8021
|
-
sessionKey: resolved.sessionKey,
|
|
8022
|
-
inferredAgentId: resolved.inferredAgentId,
|
|
8023
|
-
model: resolved.model
|
|
8024
|
-
});
|
|
8025
|
-
},
|
|
8026
|
-
startTurnRun: async (request) => {
|
|
8027
|
-
return params.runCoordinator.startRun(request);
|
|
8028
|
-
},
|
|
8029
|
-
listRuns: async (request) => {
|
|
8030
|
-
return params.runCoordinator.listRuns(request);
|
|
8031
|
-
},
|
|
8032
|
-
getRun: async (request) => {
|
|
8033
|
-
return params.runCoordinator.getRun(request);
|
|
8034
|
-
},
|
|
8035
|
-
streamRun: async function* (request) {
|
|
8036
|
-
for await (const event of params.runCoordinator.streamRun(request)) {
|
|
8037
|
-
yield event;
|
|
8038
|
-
}
|
|
8039
|
-
},
|
|
8040
|
-
stopTurn: async (request) => {
|
|
8041
|
-
return await params.runCoordinator.stopRun(request);
|
|
8042
|
-
},
|
|
8043
|
-
processTurnStream: async function* (request) {
|
|
8044
|
-
const run = params.runCoordinator.startRun(request);
|
|
8045
|
-
for await (const event of params.runCoordinator.streamRun({ runId: run.runId })) {
|
|
8046
|
-
yield event;
|
|
8047
|
-
}
|
|
8048
|
-
}
|
|
8049
|
-
};
|
|
8050
|
-
}
|
|
8051
|
-
function resolveChatTurnParams(params) {
|
|
8052
|
-
const sessionKey = typeof params.sessionKey === "string" && params.sessionKey.trim().length > 0 ? params.sessionKey.trim() : `ui:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`;
|
|
8053
|
-
const inferredAgentId = typeof params.agentId === "string" && params.agentId.trim().length > 0 ? params.agentId.trim() : parseAgentScopedSessionKey2(sessionKey)?.agentId;
|
|
8054
|
-
const model = typeof params.model === "string" && params.model.trim().length > 0 ? params.model.trim() : void 0;
|
|
8055
|
-
const metadata = params.metadata && typeof params.metadata === "object" && !Array.isArray(params.metadata) ? { ...params.metadata } : {};
|
|
8056
|
-
if (model) {
|
|
8057
|
-
metadata.model = model;
|
|
8058
|
-
}
|
|
8059
|
-
const runId = typeof params.runId === "string" && params.runId.trim().length > 0 ? params.runId.trim() : void 0;
|
|
8060
|
-
return {
|
|
8061
|
-
runId,
|
|
8062
|
-
sessionKey,
|
|
8063
|
-
inferredAgentId,
|
|
8064
|
-
model,
|
|
8065
|
-
metadata,
|
|
8066
|
-
channel: typeof params.channel === "string" && params.channel.trim().length > 0 ? params.channel : "ui",
|
|
8067
|
-
chatId: typeof params.chatId === "string" && params.chatId.trim().length > 0 ? params.chatId : "web-ui"
|
|
8068
|
-
};
|
|
8069
|
-
}
|
|
8070
|
-
function buildTurnResult(params) {
|
|
8071
|
-
return {
|
|
8072
|
-
reply: params.reply,
|
|
8073
|
-
sessionKey: params.sessionKey,
|
|
8074
|
-
...params.inferredAgentId ? { agentId: params.inferredAgentId } : {},
|
|
8075
|
-
...params.model ? { model: params.model } : {}
|
|
8076
|
-
};
|
|
8077
|
-
}
|
|
8078
|
-
function resolveUiSessionTypeLabel(sessionType) {
|
|
8079
|
-
if (sessionType === "native") {
|
|
8080
|
-
return "Native";
|
|
8081
|
-
}
|
|
8082
|
-
return sessionType.trim().split(/[-_]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ") || sessionType;
|
|
8083
|
-
}
|
|
8084
|
-
|
|
8085
|
-
// src/cli/commands/ui-chat-run-coordinator.ts
|
|
8086
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
8087
|
-
import { join as join7 } from "path";
|
|
8088
|
-
import {
|
|
8089
|
-
getDataDir as getDataDir9,
|
|
8090
|
-
parseAgentScopedSessionKey as parseAgentScopedSessionKey3,
|
|
8091
|
-
safeFilename
|
|
8092
|
-
} from "@nextclaw/core";
|
|
8093
|
-
var RUNS_DIR = join7(getDataDir9(), "runs");
|
|
8094
|
-
var NON_TERMINAL_STATES = /* @__PURE__ */ new Set(["queued", "running"]);
|
|
8095
|
-
var DEFAULT_SESSION_TYPE = "native";
|
|
8096
|
-
var SESSION_TYPE_METADATA_KEY = "session_type";
|
|
8097
|
-
function createRunId() {
|
|
8098
|
-
const now = Date.now().toString(36);
|
|
8099
|
-
const rand = Math.random().toString(36).slice(2, 10);
|
|
8100
|
-
return `run-${now}-${rand}`;
|
|
8101
|
-
}
|
|
8102
|
-
function isRecord6(value) {
|
|
8103
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8104
|
-
}
|
|
8105
|
-
function readOptionalString3(value) {
|
|
8106
|
-
if (typeof value !== "string") {
|
|
8107
|
-
return void 0;
|
|
8108
|
-
}
|
|
8109
|
-
const trimmed = value.trim();
|
|
8110
|
-
return trimmed || void 0;
|
|
8111
|
-
}
|
|
8112
|
-
function isAbortError(error) {
|
|
8113
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
8114
|
-
return true;
|
|
8115
|
-
}
|
|
8116
|
-
if (error instanceof Error) {
|
|
8117
|
-
if (error.name === "AbortError") {
|
|
8118
|
-
return true;
|
|
8119
|
-
}
|
|
8120
|
-
const message = error.message.toLowerCase();
|
|
8121
|
-
if (message.includes("aborted") || message.includes("abort")) {
|
|
8122
|
-
return true;
|
|
8123
|
-
}
|
|
8124
|
-
}
|
|
8125
|
-
return false;
|
|
8126
|
-
}
|
|
8127
|
-
function cloneEvent(event) {
|
|
8128
|
-
return JSON.parse(JSON.stringify(event));
|
|
8129
|
-
}
|
|
8130
|
-
function hasToolSessionEvent(run) {
|
|
8131
|
-
for (const item of run.events) {
|
|
8132
|
-
if (item.type !== "session_event") {
|
|
8133
|
-
continue;
|
|
8134
|
-
}
|
|
8135
|
-
const message = item.event?.message;
|
|
8136
|
-
if (!message || typeof message.role !== "string") {
|
|
8137
|
-
continue;
|
|
8138
|
-
}
|
|
8139
|
-
const role = message.role.trim().toLowerCase();
|
|
8140
|
-
if (role === "tool" || role === "tool_result" || role === "toolresult" || role === "function") {
|
|
8141
|
-
return true;
|
|
8142
|
-
}
|
|
8143
|
-
if (role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
|
|
8144
|
-
return true;
|
|
8145
|
-
}
|
|
8146
|
-
}
|
|
8147
|
-
return false;
|
|
8148
|
-
}
|
|
8149
|
-
var UiChatRunCoordinator = class {
|
|
8150
|
-
constructor(options) {
|
|
8151
|
-
this.options = options;
|
|
8152
|
-
mkdirSync5(RUNS_DIR, { recursive: true });
|
|
8153
|
-
this.loadPersistedRuns();
|
|
8154
|
-
}
|
|
8155
|
-
runs = /* @__PURE__ */ new Map();
|
|
8156
|
-
sessionRuns = /* @__PURE__ */ new Map();
|
|
8157
|
-
startRun(input) {
|
|
8158
|
-
const request = this.resolveRequest(input);
|
|
8159
|
-
const resolvedSessionType = this.resolveSessionTypeForRun(request.sessionKey, request.metadata);
|
|
8160
|
-
request.metadata[SESSION_TYPE_METADATA_KEY] = resolvedSessionType;
|
|
8161
|
-
const stopCapability = this.options.runtimePool.supportsTurnAbort({
|
|
8162
|
-
sessionKey: request.sessionKey,
|
|
8163
|
-
agentId: request.agentId,
|
|
8164
|
-
channel: request.channel,
|
|
8165
|
-
chatId: request.chatId,
|
|
8166
|
-
metadata: request.metadata
|
|
8167
|
-
});
|
|
8168
|
-
const run = {
|
|
8169
|
-
runId: request.runId,
|
|
8170
|
-
sessionKey: request.sessionKey,
|
|
8171
|
-
...request.agentId ? { agentId: request.agentId } : {},
|
|
8172
|
-
...request.model ? { model: request.model, requestedModel: request.model } : {},
|
|
8173
|
-
state: "queued",
|
|
8174
|
-
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8175
|
-
stopSupported: stopCapability.supported,
|
|
8176
|
-
...stopCapability.reason ? { stopReason: stopCapability.reason } : {},
|
|
8177
|
-
events: [],
|
|
8178
|
-
waiters: /* @__PURE__ */ new Set(),
|
|
8179
|
-
abortController: null,
|
|
8180
|
-
cancelRequested: false
|
|
8181
|
-
};
|
|
8182
|
-
this.runs.set(run.runId, run);
|
|
8183
|
-
this.bindRunToSession(run.sessionKey, run.runId);
|
|
8184
|
-
this.syncSessionRunState(run);
|
|
8185
|
-
this.persistRun(run);
|
|
8186
|
-
this.emitRunUpdated(run);
|
|
8187
|
-
void this.executeRun(run, request);
|
|
8188
|
-
return this.toRunView(run);
|
|
8189
|
-
}
|
|
8190
|
-
getRun(params) {
|
|
8191
|
-
const run = this.getRunRecord(params.runId);
|
|
8192
|
-
return run ? this.toRunView(run) : null;
|
|
8193
|
-
}
|
|
8194
|
-
listRuns(params = {}) {
|
|
8195
|
-
const sessionKey = readOptionalString3(params.sessionKey);
|
|
8196
|
-
const stateFilter = Array.isArray(params.states) && params.states.length > 0 ? new Set(params.states) : null;
|
|
8197
|
-
const limit = Number.isFinite(params.limit) ? Math.max(0, Math.trunc(params.limit)) : 0;
|
|
8198
|
-
const records = Array.from(this.runs.values()).filter((run) => {
|
|
8199
|
-
if (sessionKey && run.sessionKey !== sessionKey) {
|
|
8200
|
-
return false;
|
|
8201
|
-
}
|
|
8202
|
-
if (stateFilter && !stateFilter.has(run.state)) {
|
|
8203
|
-
return false;
|
|
8204
|
-
}
|
|
8205
|
-
return true;
|
|
8206
|
-
}).sort((left, right) => Date.parse(right.requestedAt) - Date.parse(left.requestedAt));
|
|
8207
|
-
const total = records.length;
|
|
8208
|
-
const runs = (limit > 0 ? records.slice(0, limit) : records).map((run) => this.toRunView(run));
|
|
8209
|
-
return { runs, total };
|
|
8210
|
-
}
|
|
8211
|
-
async *streamRun(params) {
|
|
8212
|
-
const run = this.getRunRecord(params.runId);
|
|
8213
|
-
if (!run) {
|
|
8214
|
-
throw new Error(`chat run not found: ${params.runId}`);
|
|
8215
|
-
}
|
|
8216
|
-
let cursor = Number.isFinite(params.fromEventIndex) ? Math.max(0, Math.trunc(params.fromEventIndex)) : 0;
|
|
8217
|
-
while (true) {
|
|
8218
|
-
while (cursor < run.events.length) {
|
|
8219
|
-
yield cloneEvent(run.events[cursor]);
|
|
8220
|
-
cursor += 1;
|
|
8221
|
-
}
|
|
8222
|
-
if (!NON_TERMINAL_STATES.has(run.state)) {
|
|
8223
|
-
return;
|
|
8224
|
-
}
|
|
8225
|
-
if (params.signal?.aborted) {
|
|
8226
|
-
return;
|
|
8227
|
-
}
|
|
8228
|
-
await this.waitForRunUpdate(run, params.signal);
|
|
8229
|
-
}
|
|
8230
|
-
}
|
|
8231
|
-
async stopRun(params) {
|
|
8232
|
-
const runId = readOptionalString3(params.runId) ?? "";
|
|
8233
|
-
if (!runId) {
|
|
8234
|
-
return {
|
|
8235
|
-
stopped: false,
|
|
8236
|
-
runId: "",
|
|
8237
|
-
reason: "runId is required"
|
|
8238
|
-
};
|
|
8239
|
-
}
|
|
8240
|
-
const run = this.getRunRecord(runId);
|
|
8241
|
-
if (!run) {
|
|
8242
|
-
return {
|
|
8243
|
-
stopped: false,
|
|
8244
|
-
runId,
|
|
8245
|
-
...readOptionalString3(params.sessionKey) ? { sessionKey: readOptionalString3(params.sessionKey) } : {},
|
|
8246
|
-
reason: "run not found or already completed"
|
|
8247
|
-
};
|
|
8248
|
-
}
|
|
8249
|
-
const requestedSessionKey = readOptionalString3(params.sessionKey);
|
|
8250
|
-
if (requestedSessionKey && requestedSessionKey !== run.sessionKey) {
|
|
8251
|
-
return {
|
|
8252
|
-
stopped: false,
|
|
8253
|
-
runId,
|
|
8254
|
-
sessionKey: run.sessionKey,
|
|
8255
|
-
reason: "session key mismatch"
|
|
8256
|
-
};
|
|
8257
|
-
}
|
|
8258
|
-
if (!run.stopSupported) {
|
|
8259
|
-
return {
|
|
8260
|
-
stopped: false,
|
|
8261
|
-
runId,
|
|
8262
|
-
sessionKey: run.sessionKey,
|
|
8263
|
-
reason: run.stopReason ?? "run stop is not supported"
|
|
8264
|
-
};
|
|
8265
|
-
}
|
|
8266
|
-
if (!NON_TERMINAL_STATES.has(run.state)) {
|
|
8267
|
-
return {
|
|
8268
|
-
stopped: false,
|
|
8269
|
-
runId,
|
|
8270
|
-
sessionKey: run.sessionKey,
|
|
8271
|
-
reason: `run already ${run.state}`
|
|
8272
|
-
};
|
|
8273
|
-
}
|
|
8274
|
-
run.cancelRequested = true;
|
|
8275
|
-
this.finalizeRunAsStopped(run);
|
|
8276
|
-
if (run.abortController) {
|
|
8277
|
-
run.abortController.abort(new Error("chat turn stopped by user"));
|
|
8278
|
-
}
|
|
8279
|
-
return {
|
|
8280
|
-
stopped: true,
|
|
8281
|
-
runId,
|
|
8282
|
-
sessionKey: run.sessionKey
|
|
8283
|
-
};
|
|
8284
|
-
}
|
|
8285
|
-
resolveRequest(input) {
|
|
8286
|
-
const message = readOptionalString3(input.message) ?? "";
|
|
8287
|
-
const sessionKey = readOptionalString3(input.sessionKey) ?? `ui:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`;
|
|
8288
|
-
const explicitAgentId = readOptionalString3(input.agentId);
|
|
8289
|
-
const parsedAgentId = parseAgentScopedSessionKey3(sessionKey)?.agentId;
|
|
8290
|
-
const agentId = explicitAgentId ?? readOptionalString3(parsedAgentId);
|
|
8291
|
-
const model = readOptionalString3(input.model);
|
|
8292
|
-
const metadata = isRecord6(input.metadata) ? { ...input.metadata } : {};
|
|
8293
|
-
if (model) {
|
|
8294
|
-
metadata.model = model;
|
|
8295
|
-
}
|
|
8296
|
-
const runId = readOptionalString3(input.runId) ?? createRunId();
|
|
8297
|
-
return {
|
|
8298
|
-
runId,
|
|
8299
|
-
message,
|
|
8300
|
-
sessionKey,
|
|
8301
|
-
...agentId ? { agentId } : {},
|
|
8302
|
-
...model ? { model } : {},
|
|
8303
|
-
metadata,
|
|
8304
|
-
channel: readOptionalString3(input.channel) ?? "ui",
|
|
8305
|
-
chatId: readOptionalString3(input.chatId) ?? "web-ui"
|
|
8306
|
-
};
|
|
8307
|
-
}
|
|
8308
|
-
readRequestedSessionType(metadata) {
|
|
8309
|
-
const value = readOptionalString3(metadata.session_type) ?? readOptionalString3(metadata.sessionType);
|
|
8310
|
-
return value ? value.toLowerCase() : void 0;
|
|
8311
|
-
}
|
|
8312
|
-
readStoredSessionType(metadata) {
|
|
8313
|
-
const value = readOptionalString3(metadata[SESSION_TYPE_METADATA_KEY]);
|
|
8314
|
-
return value ? value.toLowerCase() : DEFAULT_SESSION_TYPE;
|
|
8315
|
-
}
|
|
8316
|
-
countUserMessages(session) {
|
|
8317
|
-
return session.messages.reduce((count, message) => {
|
|
8318
|
-
const role = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
|
|
8319
|
-
return role === "user" ? count + 1 : count;
|
|
8320
|
-
}, 0);
|
|
8321
|
-
}
|
|
8322
|
-
ensureSessionTypeAvailable(sessionType) {
|
|
8323
|
-
const available = new Set(this.options.runtimePool.listAvailableEngineKinds());
|
|
8324
|
-
available.add(DEFAULT_SESSION_TYPE);
|
|
8325
|
-
if (!available.has(sessionType)) {
|
|
8326
|
-
throw new Error(
|
|
8327
|
-
`session type "${sessionType}" is unavailable. Re-enable the related plugin or create a native session.`
|
|
8328
|
-
);
|
|
8329
|
-
}
|
|
8330
|
-
}
|
|
8331
|
-
resolveSessionTypeForRun(sessionKey, metadata) {
|
|
8332
|
-
const requestedType = this.readRequestedSessionType(metadata);
|
|
8333
|
-
const session = this.options.sessionManager.getOrCreate(sessionKey);
|
|
8334
|
-
const storedType = this.readStoredSessionType(session.metadata);
|
|
8335
|
-
const userMessageCount = this.countUserMessages(session);
|
|
8336
|
-
const locked = userMessageCount > 0;
|
|
8337
|
-
if (locked) {
|
|
8338
|
-
if (requestedType && requestedType !== storedType) {
|
|
8339
|
-
throw new Error(
|
|
8340
|
-
`session type is locked to "${storedType}" after the first user message; create a new session to switch type`
|
|
8341
|
-
);
|
|
8342
|
-
}
|
|
8343
|
-
this.ensureSessionTypeAvailable(storedType);
|
|
8344
|
-
if (session.metadata[SESSION_TYPE_METADATA_KEY] !== storedType) {
|
|
8345
|
-
session.metadata[SESSION_TYPE_METADATA_KEY] = storedType;
|
|
8346
|
-
session.updatedAt = /* @__PURE__ */ new Date();
|
|
8347
|
-
this.options.sessionManager.save(session);
|
|
8348
|
-
}
|
|
8349
|
-
return storedType;
|
|
8350
|
-
}
|
|
8351
|
-
const nextType = requestedType ?? storedType;
|
|
8352
|
-
this.ensureSessionTypeAvailable(nextType);
|
|
8353
|
-
if (session.metadata[SESSION_TYPE_METADATA_KEY] !== nextType) {
|
|
8354
|
-
session.metadata[SESSION_TYPE_METADATA_KEY] = nextType;
|
|
8355
|
-
session.updatedAt = /* @__PURE__ */ new Date();
|
|
8356
|
-
this.options.sessionManager.save(session);
|
|
8357
|
-
}
|
|
8358
|
-
return nextType;
|
|
8359
|
-
}
|
|
8360
|
-
async executeRun(run, request) {
|
|
8361
|
-
if (run.cancelRequested) {
|
|
8362
|
-
this.finalizeRunAsStopped(run);
|
|
8363
|
-
return;
|
|
8364
|
-
}
|
|
8365
|
-
this.transitionState(run, "running");
|
|
8366
|
-
const abortController = run.stopSupported ? new AbortController() : null;
|
|
8367
|
-
run.abortController = abortController;
|
|
8368
|
-
const assistantDeltaParts = [];
|
|
8369
|
-
try {
|
|
8370
|
-
const reply = await this.options.runtimePool.processDirect({
|
|
8371
|
-
content: request.message,
|
|
8372
|
-
sessionKey: request.sessionKey,
|
|
8373
|
-
channel: request.channel,
|
|
8374
|
-
chatId: request.chatId,
|
|
8375
|
-
agentId: request.agentId,
|
|
8376
|
-
metadata: request.metadata,
|
|
8377
|
-
...abortController ? { abortSignal: abortController.signal } : {},
|
|
8378
|
-
onAssistantDelta: (delta) => {
|
|
8379
|
-
if (run.cancelRequested) {
|
|
8380
|
-
return;
|
|
8381
|
-
}
|
|
8382
|
-
if (typeof delta !== "string" || delta.length === 0) {
|
|
8383
|
-
return;
|
|
8384
|
-
}
|
|
8385
|
-
assistantDeltaParts.push(delta);
|
|
8386
|
-
this.pushStreamEvent(run, { type: "delta", delta });
|
|
8387
|
-
},
|
|
8388
|
-
onSessionEvent: (event) => {
|
|
8389
|
-
if (run.cancelRequested) {
|
|
8390
|
-
return;
|
|
8391
|
-
}
|
|
8392
|
-
this.pushStreamEvent(run, {
|
|
8393
|
-
type: "session_event",
|
|
8394
|
-
event: this.mapSessionEvent(event)
|
|
8395
|
-
});
|
|
8396
|
-
}
|
|
8397
|
-
});
|
|
8398
|
-
if (run.cancelRequested) {
|
|
8399
|
-
this.finalizeRunAsStopped(run);
|
|
8400
|
-
return;
|
|
8401
|
-
}
|
|
8402
|
-
this.pushStreamEvent(run, {
|
|
8403
|
-
type: "final",
|
|
8404
|
-
result: {
|
|
8405
|
-
reply,
|
|
8406
|
-
sessionKey: request.sessionKey,
|
|
8407
|
-
...request.agentId ? { agentId: request.agentId } : {},
|
|
8408
|
-
...request.model ? { model: request.model } : {}
|
|
8409
|
-
}
|
|
8410
|
-
});
|
|
8411
|
-
run.reply = reply;
|
|
8412
|
-
this.transitionState(run, "completed");
|
|
8413
|
-
} catch (error) {
|
|
8414
|
-
const aborted = (abortController?.signal.aborted ?? false) || isAbortError(error);
|
|
8415
|
-
if (aborted) {
|
|
8416
|
-
if (run.cancelRequested) {
|
|
8417
|
-
this.finalizeRunAsStopped(run);
|
|
8418
|
-
return;
|
|
8419
|
-
}
|
|
8420
|
-
const partialReply = assistantDeltaParts.join("");
|
|
8421
|
-
const shouldPersistPartialReply = partialReply.trim().length > 0 && !hasToolSessionEvent(run);
|
|
8422
|
-
if (shouldPersistPartialReply) {
|
|
8423
|
-
this.persistAbortedAssistantReply(run.sessionKey, partialReply);
|
|
8424
|
-
}
|
|
8425
|
-
this.pushStreamEvent(run, {
|
|
8426
|
-
type: "final",
|
|
8427
|
-
result: {
|
|
8428
|
-
reply: partialReply,
|
|
8429
|
-
sessionKey: request.sessionKey,
|
|
8430
|
-
...request.agentId ? { agentId: request.agentId } : {},
|
|
8431
|
-
...request.model ? { model: request.model } : {}
|
|
8432
|
-
}
|
|
8433
|
-
});
|
|
8434
|
-
run.reply = partialReply;
|
|
8435
|
-
this.transitionState(run, "aborted", {
|
|
8436
|
-
error: abortController?.signal.reason instanceof Error ? abortController.signal.reason.message : readOptionalString3(abortController?.signal.reason)
|
|
8437
|
-
});
|
|
8438
|
-
return;
|
|
8439
|
-
}
|
|
8440
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
8441
|
-
this.pushStreamEvent(run, {
|
|
8442
|
-
type: "error",
|
|
8443
|
-
error: errorMessage
|
|
8444
|
-
});
|
|
8445
|
-
this.transitionState(run, "failed", { error: errorMessage });
|
|
8446
|
-
} finally {
|
|
8447
|
-
run.abortController = null;
|
|
8448
|
-
this.persistRun(run);
|
|
8449
|
-
this.notifyRunWaiters(run);
|
|
8450
|
-
}
|
|
8451
|
-
}
|
|
8452
|
-
transitionState(run, next, options = {}) {
|
|
8453
|
-
run.state = next;
|
|
8454
|
-
if (next === "running") {
|
|
8455
|
-
run.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8456
|
-
}
|
|
8457
|
-
if (!NON_TERMINAL_STATES.has(next)) {
|
|
8458
|
-
run.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8459
|
-
}
|
|
8460
|
-
if (options.error) {
|
|
8461
|
-
run.error = options.error;
|
|
8462
|
-
} else if (next === "completed") {
|
|
8463
|
-
run.error = void 0;
|
|
8464
|
-
}
|
|
8465
|
-
this.syncSessionRunState(run);
|
|
8466
|
-
this.persistRun(run);
|
|
8467
|
-
this.emitRunUpdated(run);
|
|
8468
|
-
this.notifyRunWaiters(run);
|
|
8469
|
-
}
|
|
8470
|
-
finalizeRunAsStopped(run) {
|
|
8471
|
-
const partialReply = this.collectAssistantReplyFromEvents(run.events);
|
|
8472
|
-
if (!this.hasFinalStreamEvent(run.events)) {
|
|
8473
|
-
this.pushStreamEvent(run, {
|
|
8474
|
-
type: "final",
|
|
8475
|
-
result: {
|
|
8476
|
-
reply: partialReply,
|
|
8477
|
-
sessionKey: run.sessionKey,
|
|
8478
|
-
...run.agentId ? { agentId: run.agentId } : {},
|
|
8479
|
-
...run.model ? { model: run.model } : {}
|
|
8480
|
-
}
|
|
8481
|
-
});
|
|
8482
|
-
}
|
|
8483
|
-
run.reply = partialReply;
|
|
8484
|
-
const shouldPersistPartialReply = partialReply.trim().length > 0 && !hasToolSessionEvent(run);
|
|
8485
|
-
if (shouldPersistPartialReply) {
|
|
8486
|
-
this.persistAbortedAssistantReply(run.sessionKey, partialReply);
|
|
8487
|
-
}
|
|
8488
|
-
if (run.state !== "aborted") {
|
|
8489
|
-
this.transitionState(run, "aborted", {
|
|
8490
|
-
error: "chat turn stopped by user"
|
|
8491
|
-
});
|
|
8492
|
-
}
|
|
8493
|
-
}
|
|
8494
|
-
hasFinalStreamEvent(events) {
|
|
8495
|
-
return events.some((event) => event.type === "final");
|
|
8496
|
-
}
|
|
8497
|
-
collectAssistantReplyFromEvents(events) {
|
|
8498
|
-
const chunks = [];
|
|
8499
|
-
for (const event of events) {
|
|
8500
|
-
if (event.type === "delta" && event.delta.length > 0) {
|
|
8501
|
-
chunks.push(event.delta);
|
|
8502
|
-
}
|
|
8503
|
-
}
|
|
8504
|
-
return chunks.join("");
|
|
8505
|
-
}
|
|
8506
|
-
pushStreamEvent(run, event) {
|
|
8507
|
-
run.events.push(event);
|
|
8508
|
-
this.persistRun(run);
|
|
8509
|
-
this.notifyRunWaiters(run);
|
|
8510
|
-
this.emitRunUpdated(run);
|
|
8511
|
-
}
|
|
8512
|
-
waitForRunUpdate(run, signal) {
|
|
8513
|
-
return new Promise((resolve15) => {
|
|
8514
|
-
const wake = () => {
|
|
8515
|
-
cleanup();
|
|
8516
|
-
resolve15();
|
|
8517
|
-
};
|
|
8518
|
-
const onAbort = () => {
|
|
8519
|
-
cleanup();
|
|
8520
|
-
resolve15();
|
|
8521
|
-
};
|
|
8522
|
-
const cleanup = () => {
|
|
8523
|
-
run.waiters.delete(wake);
|
|
8524
|
-
signal?.removeEventListener("abort", onAbort);
|
|
8525
|
-
};
|
|
8526
|
-
run.waiters.add(wake);
|
|
8527
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
8528
|
-
});
|
|
8529
|
-
}
|
|
8530
|
-
notifyRunWaiters(run) {
|
|
8531
|
-
if (run.waiters.size === 0) {
|
|
8532
|
-
return;
|
|
8533
|
-
}
|
|
8534
|
-
const waiters = Array.from(run.waiters);
|
|
8535
|
-
run.waiters.clear();
|
|
8536
|
-
for (const wake of waiters) {
|
|
8537
|
-
wake();
|
|
8538
|
-
}
|
|
8539
|
-
}
|
|
8540
|
-
bindRunToSession(sessionKey, runId) {
|
|
8541
|
-
const existing = this.sessionRuns.get(sessionKey);
|
|
8542
|
-
if (existing) {
|
|
8543
|
-
existing.add(runId);
|
|
8544
|
-
return;
|
|
8545
|
-
}
|
|
8546
|
-
this.sessionRuns.set(sessionKey, /* @__PURE__ */ new Set([runId]));
|
|
8547
|
-
}
|
|
8548
|
-
syncSessionRunState(run) {
|
|
8549
|
-
const session = this.options.sessionManager.getOrCreate(run.sessionKey);
|
|
8550
|
-
const metadata = session.metadata;
|
|
8551
|
-
metadata.ui_last_run_id = run.runId;
|
|
8552
|
-
metadata.ui_last_run_state = run.state;
|
|
8553
|
-
metadata.ui_last_run_updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
8554
|
-
if (NON_TERMINAL_STATES.has(run.state)) {
|
|
8555
|
-
metadata.ui_active_run_id = run.runId;
|
|
8556
|
-
metadata.ui_run_state = run.state;
|
|
8557
|
-
metadata.ui_run_requested_at = run.requestedAt;
|
|
8558
|
-
if (run.startedAt) {
|
|
8559
|
-
metadata.ui_run_started_at = run.startedAt;
|
|
8560
|
-
}
|
|
8561
|
-
} else if (metadata.ui_active_run_id === run.runId) {
|
|
8562
|
-
delete metadata.ui_active_run_id;
|
|
8563
|
-
delete metadata.ui_run_state;
|
|
8564
|
-
delete metadata.ui_run_requested_at;
|
|
8565
|
-
delete metadata.ui_run_started_at;
|
|
8566
|
-
}
|
|
8567
|
-
session.updatedAt = /* @__PURE__ */ new Date();
|
|
8568
|
-
this.options.sessionManager.save(session);
|
|
8569
|
-
}
|
|
8570
|
-
persistAbortedAssistantReply(sessionKey, partialReply) {
|
|
8571
|
-
const session = this.options.sessionManager.getOrCreate(sessionKey);
|
|
8572
|
-
const latest = session.messages.length > 0 ? session.messages[session.messages.length - 1] : null;
|
|
8573
|
-
if (latest?.role === "assistant" && typeof latest.content === "string" && latest.content === partialReply) {
|
|
8574
|
-
return;
|
|
8575
|
-
}
|
|
8576
|
-
this.options.sessionManager.addMessage(session, "assistant", partialReply);
|
|
8577
|
-
this.options.sessionManager.save(session);
|
|
8578
|
-
}
|
|
8579
|
-
mapSessionEvent(event) {
|
|
8580
|
-
const raw = event.data?.message;
|
|
8581
|
-
const messageRecord = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
8582
|
-
const message = messageRecord && typeof messageRecord.role === "string" ? {
|
|
8583
|
-
role: messageRecord.role,
|
|
8584
|
-
content: messageRecord.content,
|
|
8585
|
-
timestamp: typeof messageRecord.timestamp === "string" ? messageRecord.timestamp : event.timestamp,
|
|
8586
|
-
...typeof messageRecord.name === "string" ? { name: messageRecord.name } : {},
|
|
8587
|
-
...typeof messageRecord.tool_call_id === "string" ? { tool_call_id: messageRecord.tool_call_id } : {},
|
|
8588
|
-
...Array.isArray(messageRecord.tool_calls) ? { tool_calls: messageRecord.tool_calls } : {},
|
|
8589
|
-
...typeof messageRecord.reasoning_content === "string" ? { reasoning_content: messageRecord.reasoning_content } : {}
|
|
8590
|
-
} : void 0;
|
|
8591
|
-
return {
|
|
8592
|
-
seq: event.seq,
|
|
8593
|
-
type: event.type,
|
|
8594
|
-
timestamp: event.timestamp,
|
|
8595
|
-
...message ? { message } : {}
|
|
8596
|
-
};
|
|
8597
|
-
}
|
|
8598
|
-
emitRunUpdated(run) {
|
|
8599
|
-
this.options.onRunUpdated?.(this.toRunView(run));
|
|
8600
|
-
}
|
|
8601
|
-
toRunView(run) {
|
|
8602
|
-
return {
|
|
8603
|
-
runId: run.runId,
|
|
8604
|
-
sessionKey: run.sessionKey,
|
|
8605
|
-
...run.agentId ? { agentId: run.agentId } : {},
|
|
8606
|
-
...run.model ? { model: run.model } : {},
|
|
8607
|
-
state: run.state,
|
|
8608
|
-
requestedAt: run.requestedAt,
|
|
8609
|
-
...run.startedAt ? { startedAt: run.startedAt } : {},
|
|
8610
|
-
...run.completedAt ? { completedAt: run.completedAt } : {},
|
|
8611
|
-
stopSupported: run.stopSupported,
|
|
8612
|
-
...run.stopReason ? { stopReason: run.stopReason } : {},
|
|
8613
|
-
...run.error ? { error: run.error } : {},
|
|
8614
|
-
...typeof run.reply === "string" ? { reply: run.reply } : {},
|
|
8615
|
-
eventCount: run.events.length
|
|
8616
|
-
};
|
|
8617
|
-
}
|
|
8618
|
-
getRunPath(runId) {
|
|
8619
|
-
return join7(RUNS_DIR, `${safeFilename(runId)}.json`);
|
|
8620
|
-
}
|
|
8621
|
-
persistRun(run) {
|
|
8622
|
-
const persisted = {
|
|
8623
|
-
runId: run.runId,
|
|
8624
|
-
sessionKey: run.sessionKey,
|
|
8625
|
-
...run.agentId ? { agentId: run.agentId } : {},
|
|
8626
|
-
...run.model ? { model: run.model } : {},
|
|
8627
|
-
state: run.state,
|
|
8628
|
-
requestedAt: run.requestedAt,
|
|
8629
|
-
...run.startedAt ? { startedAt: run.startedAt } : {},
|
|
8630
|
-
...run.completedAt ? { completedAt: run.completedAt } : {},
|
|
8631
|
-
stopSupported: run.stopSupported,
|
|
8632
|
-
...run.stopReason ? { stopReason: run.stopReason } : {},
|
|
8633
|
-
...run.error ? { error: run.error } : {},
|
|
8634
|
-
...typeof run.reply === "string" ? { reply: run.reply } : {},
|
|
8635
|
-
events: run.events
|
|
8636
|
-
};
|
|
8637
|
-
writeFileSync5(this.getRunPath(run.runId), `${JSON.stringify(persisted, null, 2)}
|
|
8638
|
-
`);
|
|
8639
|
-
}
|
|
8640
|
-
loadPersistedRuns() {
|
|
8641
|
-
if (!existsSync11(RUNS_DIR)) {
|
|
8642
|
-
return;
|
|
8643
|
-
}
|
|
8644
|
-
for (const entry of readdirSync2(RUNS_DIR, { withFileTypes: true })) {
|
|
8645
|
-
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
8646
|
-
continue;
|
|
8647
|
-
}
|
|
8648
|
-
const path2 = join7(RUNS_DIR, entry.name);
|
|
8649
|
-
try {
|
|
8650
|
-
const parsed = JSON.parse(readFileSync9(path2, "utf-8"));
|
|
8651
|
-
const runId = readOptionalString3(parsed.runId);
|
|
8652
|
-
const sessionKey = readOptionalString3(parsed.sessionKey);
|
|
8653
|
-
if (!runId || !sessionKey) {
|
|
8654
|
-
continue;
|
|
8655
|
-
}
|
|
8656
|
-
const state = this.normalizeRunState(parsed.state);
|
|
8657
|
-
const events = Array.isArray(parsed.events) ? parsed.events : [];
|
|
8658
|
-
const run = {
|
|
8659
|
-
runId,
|
|
8660
|
-
sessionKey,
|
|
8661
|
-
...readOptionalString3(parsed.agentId) ? { agentId: readOptionalString3(parsed.agentId) } : {},
|
|
8662
|
-
...readOptionalString3(parsed.model) ? { model: readOptionalString3(parsed.model) } : {},
|
|
8663
|
-
state,
|
|
8664
|
-
requestedAt: readOptionalString3(parsed.requestedAt) ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
8665
|
-
...readOptionalString3(parsed.startedAt) ? { startedAt: readOptionalString3(parsed.startedAt) } : {},
|
|
8666
|
-
...readOptionalString3(parsed.completedAt) ? { completedAt: readOptionalString3(parsed.completedAt) } : {},
|
|
8667
|
-
stopSupported: Boolean(parsed.stopSupported),
|
|
8668
|
-
...readOptionalString3(parsed.stopReason) ? { stopReason: readOptionalString3(parsed.stopReason) } : {},
|
|
8669
|
-
...readOptionalString3(parsed.error) ? { error: readOptionalString3(parsed.error) } : {},
|
|
8670
|
-
...typeof parsed.reply === "string" ? { reply: parsed.reply } : {},
|
|
8671
|
-
events,
|
|
8672
|
-
waiters: /* @__PURE__ */ new Set(),
|
|
8673
|
-
abortController: null,
|
|
8674
|
-
cancelRequested: false
|
|
8675
|
-
};
|
|
8676
|
-
if (NON_TERMINAL_STATES.has(run.state)) {
|
|
8677
|
-
run.state = "failed";
|
|
8678
|
-
run.error = run.error ?? "run interrupted by service restart";
|
|
8679
|
-
run.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8680
|
-
this.persistRun(run);
|
|
8681
|
-
}
|
|
8682
|
-
this.runs.set(run.runId, run);
|
|
8683
|
-
this.bindRunToSession(run.sessionKey, run.runId);
|
|
8684
|
-
} catch {
|
|
8685
|
-
}
|
|
8686
|
-
}
|
|
8687
|
-
}
|
|
8688
|
-
normalizeRunState(value) {
|
|
8689
|
-
if (value === "queued" || value === "running" || value === "completed" || value === "failed" || value === "aborted") {
|
|
8690
|
-
return value;
|
|
8691
|
-
}
|
|
8692
|
-
return "failed";
|
|
8693
|
-
}
|
|
8694
|
-
getRunRecord(runId) {
|
|
8695
|
-
const normalized = readOptionalString3(runId);
|
|
8696
|
-
if (!normalized) {
|
|
8697
|
-
return null;
|
|
8698
|
-
}
|
|
8699
|
-
return this.runs.get(normalized) ?? null;
|
|
8700
|
-
}
|
|
8701
|
-
};
|
|
8702
|
-
|
|
8703
8068
|
// src/cli/commands/service-gateway-startup.ts
|
|
8704
8069
|
async function startUiShell(params) {
|
|
8070
|
+
logStartupTrace("service.start_ui_shell.begin");
|
|
8705
8071
|
if (!params.uiConfig.enabled) {
|
|
8706
8072
|
return null;
|
|
8707
8073
|
}
|
|
8708
8074
|
let publishUiEvent = null;
|
|
8709
|
-
params.runtimePool.setSystemSessionUpdatedHandler(({ sessionKey, message }) => {
|
|
8710
|
-
if (!publishUiEvent) {
|
|
8711
|
-
return;
|
|
8712
|
-
}
|
|
8713
|
-
if (!message.chatId.startsWith("ui:")) {
|
|
8714
|
-
return;
|
|
8715
|
-
}
|
|
8716
|
-
publishUiEvent({
|
|
8717
|
-
type: "session.updated",
|
|
8718
|
-
payload: {
|
|
8719
|
-
sessionKey
|
|
8720
|
-
}
|
|
8721
|
-
});
|
|
8722
|
-
});
|
|
8723
|
-
const runCoordinator = new UiChatRunCoordinator({
|
|
8724
|
-
runtimePool: params.runtimePool,
|
|
8725
|
-
sessionManager: params.sessionManager,
|
|
8726
|
-
onRunUpdated: (run) => {
|
|
8727
|
-
publishUiEvent?.({ type: "run.updated", payload: { run } });
|
|
8728
|
-
}
|
|
8729
|
-
});
|
|
8730
8075
|
const deferredNcpAgent = createDeferredUiNcpAgent();
|
|
8731
8076
|
const uiServer = startUiServer({
|
|
8732
8077
|
host: params.uiConfig.host,
|
|
@@ -8740,11 +8085,7 @@ async function startUiShell(params) {
|
|
|
8740
8085
|
remoteAccess: params.remoteAccess,
|
|
8741
8086
|
getPluginChannelBindings: params.getPluginChannelBindings,
|
|
8742
8087
|
getPluginUiMetadata: params.getPluginUiMetadata,
|
|
8743
|
-
ncpAgent: deferredNcpAgent.agent
|
|
8744
|
-
chatRuntime: createServiceUiChatRuntime({
|
|
8745
|
-
runtimePool: params.runtimePool,
|
|
8746
|
-
runCoordinator
|
|
8747
|
-
})
|
|
8088
|
+
ncpAgent: deferredNcpAgent.agent
|
|
8748
8089
|
});
|
|
8749
8090
|
publishUiEvent = uiServer.publish;
|
|
8750
8091
|
const uiUrl = `http://${uiServer.host}:${uiServer.port}`;
|
|
@@ -8755,23 +8096,34 @@ async function startUiShell(params) {
|
|
|
8755
8096
|
if (params.openBrowserWindow) {
|
|
8756
8097
|
openBrowser(uiUrl);
|
|
8757
8098
|
}
|
|
8099
|
+
logStartupTrace("service.start_ui_shell.ready", {
|
|
8100
|
+
host: uiServer.host,
|
|
8101
|
+
port: uiServer.port
|
|
8102
|
+
});
|
|
8758
8103
|
return {
|
|
8759
|
-
deferredNcpAgent
|
|
8104
|
+
deferredNcpAgent,
|
|
8105
|
+
publish: (event) => {
|
|
8106
|
+
publishUiEvent?.(event);
|
|
8107
|
+
}
|
|
8760
8108
|
};
|
|
8761
8109
|
}
|
|
8762
8110
|
async function startDeferredGatewayStartup(params) {
|
|
8111
|
+
logStartupTrace("service.deferred_startup.begin");
|
|
8763
8112
|
if (params.uiStartup) {
|
|
8764
8113
|
try {
|
|
8765
|
-
const ncpAgent = await
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8114
|
+
const ncpAgent = await measureStartupAsync(
|
|
8115
|
+
"service.deferred_startup.create_ui_ncp_agent",
|
|
8116
|
+
async () => await createUiNcpAgent({
|
|
8117
|
+
bus: params.bus,
|
|
8118
|
+
providerManager: params.providerManager,
|
|
8119
|
+
sessionManager: params.sessionManager,
|
|
8120
|
+
cronService: params.cronService,
|
|
8121
|
+
gatewayController: params.gatewayController,
|
|
8122
|
+
getConfig: params.getConfig,
|
|
8123
|
+
getExtensionRegistry: params.getExtensionRegistry,
|
|
8124
|
+
resolveMessageToolHints: ({ channel, accountId }) => params.resolveMessageToolHints({ channel, accountId })
|
|
8125
|
+
})
|
|
8126
|
+
);
|
|
8775
8127
|
params.onNcpAgentReady(ncpAgent);
|
|
8776
8128
|
params.uiStartup.deferredNcpAgent.activate(ncpAgent);
|
|
8777
8129
|
console.log("\u2713 UI NCP agent: ready");
|
|
@@ -8779,10 +8131,11 @@ async function startDeferredGatewayStartup(params) {
|
|
|
8779
8131
|
console.error(`UI NCP agent startup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
8780
8132
|
}
|
|
8781
8133
|
}
|
|
8782
|
-
await params.startPluginGateways
|
|
8783
|
-
await params.startChannels
|
|
8784
|
-
await params.wakeFromRestartSentinel
|
|
8134
|
+
await measureStartupAsync("service.deferred_startup.start_plugin_gateways", params.startPluginGateways);
|
|
8135
|
+
await measureStartupAsync("service.deferred_startup.start_channels", params.startChannels);
|
|
8136
|
+
await measureStartupAsync("service.deferred_startup.wake_restart_sentinel", params.wakeFromRestartSentinel);
|
|
8785
8137
|
console.log("\u2713 Deferred startup: plugin gateways and channels settled");
|
|
8138
|
+
logStartupTrace("service.deferred_startup.end");
|
|
8786
8139
|
}
|
|
8787
8140
|
async function runGatewayRuntimeLoop(params) {
|
|
8788
8141
|
let startupTask = null;
|
|
@@ -8799,6 +8152,18 @@ async function runGatewayRuntimeLoop(params) {
|
|
|
8799
8152
|
}
|
|
8800
8153
|
}
|
|
8801
8154
|
|
|
8155
|
+
// src/cli/commands/service-ui-shell-grace.ts
|
|
8156
|
+
import { setTimeout as delay } from "timers/promises";
|
|
8157
|
+
var DEFAULT_UI_SHELL_GRACE_MS = 3e3;
|
|
8158
|
+
async function waitForUiShellGraceWindow(uiStartup) {
|
|
8159
|
+
if (!uiStartup) {
|
|
8160
|
+
return;
|
|
8161
|
+
}
|
|
8162
|
+
await measureStartupAsync("service.ui_shell_grace_window", async () => {
|
|
8163
|
+
await delay(DEFAULT_UI_SHELL_GRACE_MS);
|
|
8164
|
+
});
|
|
8165
|
+
}
|
|
8166
|
+
|
|
8802
8167
|
// src/cli/commands/service.ts
|
|
8803
8168
|
var {
|
|
8804
8169
|
APP_NAME: APP_NAME3,
|
|
@@ -8812,7 +8177,7 @@ var {
|
|
|
8812
8177
|
MessageBus: MessageBus2,
|
|
8813
8178
|
resolveConfigSecrets: resolveConfigSecrets4,
|
|
8814
8179
|
SessionManager: SessionManager2,
|
|
8815
|
-
parseAgentScopedSessionKey:
|
|
8180
|
+
parseAgentScopedSessionKey: parseAgentScopedSessionKey2
|
|
8816
8181
|
} = NextclawCore2;
|
|
8817
8182
|
function createSkillsLoader(workspace) {
|
|
8818
8183
|
const ctor = NextclawCore2.SkillsLoader;
|
|
@@ -8828,19 +8193,67 @@ var ServiceCommands = class {
|
|
|
8828
8193
|
applyLiveConfigReload = null;
|
|
8829
8194
|
liveUiNcpAgent = null;
|
|
8830
8195
|
async startGateway(options = {}) {
|
|
8196
|
+
logStartupTrace("service.start_gateway.begin");
|
|
8831
8197
|
this.applyLiveConfigReload = null;
|
|
8832
8198
|
this.liveUiNcpAgent = null;
|
|
8833
|
-
const
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
let { pluginRegistry, pluginChannelBindings, extensionRegistry } = gateway;
|
|
8199
|
+
const shellContext = measureStartupSync(
|
|
8200
|
+
"service.create_gateway_shell_context",
|
|
8201
|
+
() => createGatewayShellContext({ uiOverrides: options.uiOverrides, uiStaticDir: options.uiStaticDir })
|
|
8202
|
+
);
|
|
8203
|
+
const applyLiveConfigReload = async () => {
|
|
8204
|
+
await this.applyLiveConfigReload?.();
|
|
8205
|
+
};
|
|
8206
|
+
let pluginChannelBindings = [];
|
|
8207
|
+
let pluginUiMetadata = [];
|
|
8843
8208
|
let pluginGatewayHandles = [];
|
|
8209
|
+
const marketplaceInstaller = new ServiceMarketplaceInstaller({
|
|
8210
|
+
applyLiveConfigReload,
|
|
8211
|
+
runCliSubcommand: (args) => this.runCliSubcommand(args),
|
|
8212
|
+
installBuiltinSkill: (slug, force) => this.installBuiltinMarketplaceSkill(slug, force)
|
|
8213
|
+
}).createInstaller();
|
|
8214
|
+
const remoteAccess = createRemoteAccessHost({
|
|
8215
|
+
serviceCommands: this,
|
|
8216
|
+
requestRestart: this.deps.requestRestart,
|
|
8217
|
+
uiConfig: shellContext.uiConfig,
|
|
8218
|
+
remoteModule: shellContext.remoteModule
|
|
8219
|
+
});
|
|
8220
|
+
const uiStartup = await measureStartupAsync(
|
|
8221
|
+
"service.start_ui_shell",
|
|
8222
|
+
async () => await startUiShell({
|
|
8223
|
+
uiConfig: shellContext.uiConfig,
|
|
8224
|
+
uiStaticDir: shellContext.uiStaticDir,
|
|
8225
|
+
cronService: shellContext.cron,
|
|
8226
|
+
getConfig: () => resolveConfigSecrets4(loadConfig18(), { configPath: shellContext.runtimeConfigPath }),
|
|
8227
|
+
configPath: getConfigPath10(),
|
|
8228
|
+
productVersion: getPackageVersion(),
|
|
8229
|
+
getPluginChannelBindings: () => pluginChannelBindings,
|
|
8230
|
+
getPluginUiMetadata: () => pluginUiMetadata,
|
|
8231
|
+
marketplace: { apiBaseUrl: process.env.NEXTCLAW_MARKETPLACE_API_BASE, installer: marketplaceInstaller },
|
|
8232
|
+
remoteAccess,
|
|
8233
|
+
openBrowserWindow: shellContext.uiConfig.open,
|
|
8234
|
+
applyLiveConfigReload
|
|
8235
|
+
})
|
|
8236
|
+
);
|
|
8237
|
+
await waitForUiShellGraceWindow(uiStartup);
|
|
8238
|
+
await waitForNextTick();
|
|
8239
|
+
const gateway = measureStartupSync(
|
|
8240
|
+
"service.create_gateway_startup_context",
|
|
8241
|
+
() => createGatewayStartupContext({
|
|
8242
|
+
shellContext,
|
|
8243
|
+
uiOverrides: options.uiOverrides,
|
|
8244
|
+
allowMissingProvider: options.allowMissingProvider,
|
|
8245
|
+
uiStaticDir: options.uiStaticDir,
|
|
8246
|
+
makeProvider: (config2, providerOptions) => providerOptions?.allowMissing === true ? this.makeProvider(config2, { allowMissing: true }) : this.makeProvider(config2),
|
|
8247
|
+
makeMissingProvider: (config2) => this.makeMissingProvider(config2),
|
|
8248
|
+
requestRestart: (params) => this.deps.requestRestart(params)
|
|
8249
|
+
})
|
|
8250
|
+
);
|
|
8251
|
+
this.applyLiveConfigReload = gateway.applyLiveConfigReload;
|
|
8252
|
+
let { pluginRegistry, extensionRegistry } = gateway;
|
|
8253
|
+
pluginChannelBindings = gateway.pluginChannelBindings;
|
|
8254
|
+
pluginUiMetadata = getPluginUiMetadataFromRegistry(pluginRegistry);
|
|
8255
|
+
uiStartup?.publish({ type: "config.updated", payload: { path: "channels" } });
|
|
8256
|
+
uiStartup?.publish({ type: "config.updated", payload: { path: "plugins" } });
|
|
8844
8257
|
gateway.reloader.setApplyAgentRuntimeConfig((nextConfig) => gateway.runtimePool.applyRuntimeConfig(nextConfig));
|
|
8845
8258
|
gateway.reloader.setReloadPlugins(async ({ config: nextConfig, changedPaths }) => {
|
|
8846
8259
|
const result = await reloadServicePlugins({
|
|
@@ -8873,49 +8286,19 @@ var ServiceCommands = class {
|
|
|
8873
8286
|
runtimeConfigPath: gateway.runtimeConfigPath,
|
|
8874
8287
|
pluginChannelBindings
|
|
8875
8288
|
});
|
|
8876
|
-
if (gateway.reloader.getChannels().enabledChannels.length) {
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
uiConfig: gateway.uiConfig,
|
|
8890
|
-
remoteModule: gateway.remoteModule
|
|
8891
|
-
});
|
|
8892
|
-
const uiStartup = await startUiShell({
|
|
8893
|
-
uiConfig: gateway.uiConfig,
|
|
8894
|
-
uiStaticDir: gateway.uiStaticDir,
|
|
8895
|
-
cronService: gateway.cron,
|
|
8896
|
-
runtimePool: gateway.runtimePool,
|
|
8897
|
-
sessionManager: gateway.sessionManager,
|
|
8898
|
-
bus: gateway.bus,
|
|
8899
|
-
getConfig: () => resolveConfigSecrets4(loadConfig18(), { configPath: gateway.runtimeConfigPath }),
|
|
8900
|
-
configPath: getConfigPath10(),
|
|
8901
|
-
productVersion: getPackageVersion(),
|
|
8902
|
-
getPluginChannelBindings: () => pluginChannelBindings,
|
|
8903
|
-
getPluginUiMetadata: () => getPluginUiMetadataFromRegistry(pluginRegistry),
|
|
8904
|
-
marketplace: {
|
|
8905
|
-
apiBaseUrl: process.env.NEXTCLAW_MARKETPLACE_API_BASE,
|
|
8906
|
-
installer: marketplaceInstaller
|
|
8907
|
-
},
|
|
8908
|
-
remoteAccess,
|
|
8909
|
-
openBrowserWindow: gateway.uiConfig.open,
|
|
8910
|
-
applyLiveConfigReload: this.applyLiveConfigReload ?? void 0
|
|
8911
|
-
});
|
|
8912
|
-
await startGatewaySupportServices({
|
|
8913
|
-
cronJobs: gateway.cron.status().jobs,
|
|
8914
|
-
remoteModule: gateway.remoteModule,
|
|
8915
|
-
watchConfigFile: () => this.watchConfigFile(gateway.reloader),
|
|
8916
|
-
startCron: () => gateway.cron.start(),
|
|
8917
|
-
startHeartbeat: () => gateway.heartbeat.start()
|
|
8918
|
-
});
|
|
8289
|
+
if (gateway.reloader.getChannels().enabledChannels.length) console.log(`\u2713 Channels enabled: ${gateway.reloader.getChannels().enabledChannels.join(", ")}`);
|
|
8290
|
+
else console.log("Warning: No channels enabled");
|
|
8291
|
+
await measureStartupAsync(
|
|
8292
|
+
"service.start_gateway_support_services",
|
|
8293
|
+
async () => await startGatewaySupportServices({
|
|
8294
|
+
cronJobs: gateway.cron.status().jobs,
|
|
8295
|
+
remoteModule: gateway.remoteModule,
|
|
8296
|
+
watchConfigFile: () => this.watchConfigFile(gateway.reloader),
|
|
8297
|
+
startCron: () => gateway.cron.start(),
|
|
8298
|
+
startHeartbeat: () => gateway.heartbeat.start()
|
|
8299
|
+
})
|
|
8300
|
+
);
|
|
8301
|
+
logStartupTrace("service.start_gateway.runtime_loop_begin");
|
|
8919
8302
|
await runGatewayRuntimeLoop({
|
|
8920
8303
|
runtimePool: gateway.runtimePool,
|
|
8921
8304
|
startDeferredStartup: () => startDeferredGatewayStartup({
|
|
@@ -8964,6 +8347,7 @@ var ServiceCommands = class {
|
|
|
8964
8347
|
setPluginRuntimeBridge2(null);
|
|
8965
8348
|
}
|
|
8966
8349
|
});
|
|
8350
|
+
logStartupTrace("service.start_gateway.end");
|
|
8967
8351
|
}
|
|
8968
8352
|
normalizeOptionalString(value) {
|
|
8969
8353
|
if (typeof value !== "string") {
|
|
@@ -9058,7 +8442,7 @@ var ServiceCommands = class {
|
|
|
9058
8442
|
}
|
|
9059
8443
|
const sessionKey = sentinelSessionKey ?? fallbackSessionKey ?? "cli:default";
|
|
9060
8444
|
const parsedSession = parseSessionKey(sessionKey);
|
|
9061
|
-
const parsedAgentSession =
|
|
8445
|
+
const parsedAgentSession = parseAgentScopedSessionKey2(sessionKey);
|
|
9062
8446
|
const parsedSessionRoute = parsedSession && parsedSession.channel !== "agent" ? parsedSession : null;
|
|
9063
8447
|
const context = payload.deliveryContext;
|
|
9064
8448
|
const channel = this.normalizeOptionalString(context?.channel) ?? parsedSessionRoute?.channel ?? this.normalizeOptionalString((params.sessionManager.getIfExists(sessionKey)?.metadata ?? {}).last_channel);
|
|
@@ -9168,7 +8552,7 @@ var ServiceCommands = class {
|
|
|
9168
8552
|
}
|
|
9169
8553
|
const logPath = resolveServiceLogPath();
|
|
9170
8554
|
const logDir = resolve12(logPath, "..");
|
|
9171
|
-
|
|
8555
|
+
mkdirSync5(logDir, { recursive: true });
|
|
9172
8556
|
const logFd = openSync2(logPath, "a");
|
|
9173
8557
|
const readinessTimeoutMs = this.resolveStartupTimeoutMs(options.startupTimeoutMs);
|
|
9174
8558
|
const quickPhaseTimeoutMs = Math.min(8e3, readinessTimeoutMs);
|
|
@@ -9557,9 +8941,9 @@ var ServiceCommands = class {
|
|
|
9557
8941
|
}
|
|
9558
8942
|
installBuiltinMarketplaceSkill(slug, force) {
|
|
9559
8943
|
const workspace = getWorkspacePath11(loadConfig18().agents.defaults.workspace);
|
|
9560
|
-
const destination =
|
|
9561
|
-
const destinationSkillFile =
|
|
9562
|
-
if (
|
|
8944
|
+
const destination = join7(workspace, "skills", slug);
|
|
8945
|
+
const destinationSkillFile = join7(destination, "SKILL.md");
|
|
8946
|
+
if (existsSync11(destinationSkillFile) && !force) {
|
|
9563
8947
|
return {
|
|
9564
8948
|
message: `${slug} is already installed`
|
|
9565
8949
|
};
|
|
@@ -9567,14 +8951,14 @@ var ServiceCommands = class {
|
|
|
9567
8951
|
const loader = createSkillsLoader(workspace);
|
|
9568
8952
|
const builtin = (loader?.listSkills(false) ?? []).find((skill) => skill.name === slug && skill.source === "builtin");
|
|
9569
8953
|
if (!builtin) {
|
|
9570
|
-
if (
|
|
8954
|
+
if (existsSync11(destinationSkillFile)) {
|
|
9571
8955
|
return {
|
|
9572
8956
|
message: `${slug} is already installed`
|
|
9573
8957
|
};
|
|
9574
8958
|
}
|
|
9575
8959
|
return null;
|
|
9576
8960
|
}
|
|
9577
|
-
|
|
8961
|
+
mkdirSync5(join7(workspace, "skills"), { recursive: true });
|
|
9578
8962
|
cpSync2(dirname3(builtin.path), destination, { recursive: true, force: true });
|
|
9579
8963
|
return {
|
|
9580
8964
|
message: `Installed skill: ${slug}`
|
|
@@ -9634,11 +9018,11 @@ ${stderr}`.trim();
|
|
|
9634
9018
|
};
|
|
9635
9019
|
|
|
9636
9020
|
// src/cli/workspace.ts
|
|
9637
|
-
import { cpSync as cpSync3, existsSync as
|
|
9021
|
+
import { cpSync as cpSync3, existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync9, readdirSync as readdirSync2, rmSync as rmSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
9638
9022
|
import { createRequire as createRequire2 } from "module";
|
|
9639
|
-
import { dirname as dirname4, join as
|
|
9023
|
+
import { dirname as dirname4, join as join8, resolve as resolve13 } from "path";
|
|
9640
9024
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
9641
|
-
import { APP_NAME as APP_NAME4, getDataDir as
|
|
9025
|
+
import { APP_NAME as APP_NAME4, getDataDir as getDataDir9 } from "@nextclaw/core";
|
|
9642
9026
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
9643
9027
|
var WorkspaceManager = class {
|
|
9644
9028
|
constructor(logo) {
|
|
@@ -9666,30 +9050,30 @@ var WorkspaceManager = class {
|
|
|
9666
9050
|
{ source: "memory/MEMORY.md", target: "memory/MEMORY.md" }
|
|
9667
9051
|
];
|
|
9668
9052
|
for (const entry of templateFiles) {
|
|
9669
|
-
const filePath =
|
|
9670
|
-
if (!force &&
|
|
9053
|
+
const filePath = join8(workspace, entry.target);
|
|
9054
|
+
if (!force && existsSync12(filePath)) {
|
|
9671
9055
|
continue;
|
|
9672
9056
|
}
|
|
9673
|
-
const templatePath =
|
|
9674
|
-
if (!
|
|
9057
|
+
const templatePath = join8(templateDir, entry.source);
|
|
9058
|
+
if (!existsSync12(templatePath)) {
|
|
9675
9059
|
console.warn(`Warning: Template file missing: ${templatePath}`);
|
|
9676
9060
|
continue;
|
|
9677
9061
|
}
|
|
9678
|
-
const raw =
|
|
9062
|
+
const raw = readFileSync9(templatePath, "utf-8");
|
|
9679
9063
|
const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME4);
|
|
9680
|
-
|
|
9681
|
-
|
|
9064
|
+
mkdirSync6(dirname4(filePath), { recursive: true });
|
|
9065
|
+
writeFileSync5(filePath, content);
|
|
9682
9066
|
created.push(entry.target);
|
|
9683
9067
|
}
|
|
9684
|
-
const memoryDir =
|
|
9685
|
-
if (!
|
|
9686
|
-
|
|
9687
|
-
created.push(
|
|
9068
|
+
const memoryDir = join8(workspace, "memory");
|
|
9069
|
+
if (!existsSync12(memoryDir)) {
|
|
9070
|
+
mkdirSync6(memoryDir, { recursive: true });
|
|
9071
|
+
created.push(join8("memory", ""));
|
|
9688
9072
|
}
|
|
9689
|
-
const skillsDir =
|
|
9690
|
-
if (!
|
|
9691
|
-
|
|
9692
|
-
created.push(
|
|
9073
|
+
const skillsDir = join8(workspace, "skills");
|
|
9074
|
+
if (!existsSync12(skillsDir)) {
|
|
9075
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
9076
|
+
created.push(join8("skills", ""));
|
|
9693
9077
|
}
|
|
9694
9078
|
const seeded = this.seedBuiltinSkills(skillsDir, { force });
|
|
9695
9079
|
if (seeded > 0) {
|
|
@@ -9704,16 +9088,16 @@ var WorkspaceManager = class {
|
|
|
9704
9088
|
}
|
|
9705
9089
|
const force = Boolean(options.force);
|
|
9706
9090
|
let seeded = 0;
|
|
9707
|
-
for (const entry of
|
|
9091
|
+
for (const entry of readdirSync2(sourceDir, { withFileTypes: true })) {
|
|
9708
9092
|
if (!entry.isDirectory()) {
|
|
9709
9093
|
continue;
|
|
9710
9094
|
}
|
|
9711
|
-
const src =
|
|
9712
|
-
if (!
|
|
9095
|
+
const src = join8(sourceDir, entry.name);
|
|
9096
|
+
if (!existsSync12(join8(src, "SKILL.md"))) {
|
|
9713
9097
|
continue;
|
|
9714
9098
|
}
|
|
9715
|
-
const dest =
|
|
9716
|
-
if (!force &&
|
|
9099
|
+
const dest = join8(targetDir, entry.name);
|
|
9100
|
+
if (!force && existsSync12(dest)) {
|
|
9717
9101
|
continue;
|
|
9718
9102
|
}
|
|
9719
9103
|
try {
|
|
@@ -9731,12 +9115,12 @@ var WorkspaceManager = class {
|
|
|
9731
9115
|
const require3 = createRequire2(import.meta.url);
|
|
9732
9116
|
const entry = require3.resolve("@nextclaw/core");
|
|
9733
9117
|
const pkgRoot = resolve13(dirname4(entry), "..");
|
|
9734
|
-
const distSkills =
|
|
9735
|
-
if (
|
|
9118
|
+
const distSkills = join8(pkgRoot, "dist", "skills");
|
|
9119
|
+
if (existsSync12(distSkills)) {
|
|
9736
9120
|
return distSkills;
|
|
9737
9121
|
}
|
|
9738
|
-
const srcSkills =
|
|
9739
|
-
if (
|
|
9122
|
+
const srcSkills = join8(pkgRoot, "src", "agent", "skills");
|
|
9123
|
+
if (existsSync12(srcSkills)) {
|
|
9740
9124
|
return srcSkills;
|
|
9741
9125
|
}
|
|
9742
9126
|
return null;
|
|
@@ -9751,17 +9135,17 @@ var WorkspaceManager = class {
|
|
|
9751
9135
|
}
|
|
9752
9136
|
const cliDir = resolve13(fileURLToPath4(new URL(".", import.meta.url)));
|
|
9753
9137
|
const pkgRoot = resolve13(cliDir, "..", "..");
|
|
9754
|
-
const candidates = [
|
|
9138
|
+
const candidates = [join8(pkgRoot, "templates")];
|
|
9755
9139
|
for (const candidate of candidates) {
|
|
9756
|
-
if (
|
|
9140
|
+
if (existsSync12(candidate)) {
|
|
9757
9141
|
return candidate;
|
|
9758
9142
|
}
|
|
9759
9143
|
}
|
|
9760
9144
|
return null;
|
|
9761
9145
|
}
|
|
9762
9146
|
getBridgeDir() {
|
|
9763
|
-
const userBridge =
|
|
9764
|
-
if (
|
|
9147
|
+
const userBridge = join8(getDataDir9(), "bridge");
|
|
9148
|
+
if (existsSync12(join8(userBridge, "dist", "index.js"))) {
|
|
9765
9149
|
return userBridge;
|
|
9766
9150
|
}
|
|
9767
9151
|
if (!which("npm")) {
|
|
@@ -9770,12 +9154,12 @@ var WorkspaceManager = class {
|
|
|
9770
9154
|
}
|
|
9771
9155
|
const cliDir = resolve13(fileURLToPath4(new URL(".", import.meta.url)));
|
|
9772
9156
|
const pkgRoot = resolve13(cliDir, "..", "..");
|
|
9773
|
-
const pkgBridge =
|
|
9774
|
-
const srcBridge =
|
|
9157
|
+
const pkgBridge = join8(pkgRoot, "bridge");
|
|
9158
|
+
const srcBridge = join8(pkgRoot, "..", "..", "bridge");
|
|
9775
9159
|
let source = null;
|
|
9776
|
-
if (
|
|
9160
|
+
if (existsSync12(join8(pkgBridge, "package.json"))) {
|
|
9777
9161
|
source = pkgBridge;
|
|
9778
|
-
} else if (
|
|
9162
|
+
} else if (existsSync12(join8(srcBridge, "package.json"))) {
|
|
9779
9163
|
source = srcBridge;
|
|
9780
9164
|
}
|
|
9781
9165
|
if (!source) {
|
|
@@ -9783,8 +9167,8 @@ var WorkspaceManager = class {
|
|
|
9783
9167
|
process.exit(1);
|
|
9784
9168
|
}
|
|
9785
9169
|
console.log(`${this.logo} Setting up bridge...`);
|
|
9786
|
-
|
|
9787
|
-
if (
|
|
9170
|
+
mkdirSync6(resolve13(userBridge, ".."), { recursive: true });
|
|
9171
|
+
if (existsSync12(userBridge)) {
|
|
9788
9172
|
rmSync6(userBridge, { recursive: true, force: true });
|
|
9789
9173
|
}
|
|
9790
9174
|
cpSync3(source, userBridge, {
|
|
@@ -9840,42 +9224,44 @@ var CliRuntime = class {
|
|
|
9840
9224
|
remote;
|
|
9841
9225
|
diagnosticsCommands;
|
|
9842
9226
|
constructor(options = {}) {
|
|
9227
|
+
logStartupTrace("cli.runtime.constructor.begin");
|
|
9843
9228
|
this.logo = options.logo ?? LOGO;
|
|
9844
|
-
this.workspaceManager = new WorkspaceManager(this.logo);
|
|
9845
|
-
this.serviceCommands = new ServiceCommands({
|
|
9229
|
+
this.workspaceManager = measureStartupSync("cli.runtime.workspace_manager", () => new WorkspaceManager(this.logo));
|
|
9230
|
+
this.serviceCommands = measureStartupSync("cli.runtime.service_commands", () => new ServiceCommands({
|
|
9846
9231
|
requestRestart: (params) => this.requestRestart(params)
|
|
9847
|
-
});
|
|
9848
|
-
this.configCommands = new ConfigCommands({
|
|
9232
|
+
}));
|
|
9233
|
+
this.configCommands = measureStartupSync("cli.runtime.config_commands", () => new ConfigCommands({
|
|
9849
9234
|
requestRestart: (params) => this.requestRestart(params)
|
|
9850
|
-
});
|
|
9851
|
-
this.mcpCommands = new McpCommands();
|
|
9852
|
-
this.secretsCommands = new SecretsCommands({
|
|
9235
|
+
}));
|
|
9236
|
+
this.mcpCommands = measureStartupSync("cli.runtime.mcp_commands", () => new McpCommands());
|
|
9237
|
+
this.secretsCommands = measureStartupSync("cli.runtime.secrets_commands", () => new SecretsCommands({
|
|
9853
9238
|
requestRestart: (params) => this.requestRestart(params)
|
|
9854
|
-
});
|
|
9855
|
-
this.pluginCommands = new PluginCommands();
|
|
9856
|
-
this.channelCommands = new ChannelCommands({
|
|
9239
|
+
}));
|
|
9240
|
+
this.pluginCommands = measureStartupSync("cli.runtime.plugin_commands", () => new PluginCommands());
|
|
9241
|
+
this.channelCommands = measureStartupSync("cli.runtime.channel_commands", () => new ChannelCommands({
|
|
9857
9242
|
logo: this.logo,
|
|
9858
9243
|
getBridgeDir: () => this.workspaceManager.getBridgeDir(),
|
|
9859
9244
|
requestRestart: (params) => this.requestRestart(params)
|
|
9860
|
-
});
|
|
9861
|
-
this.cronCommands = new CronCommands();
|
|
9862
|
-
this.platformAuthCommands = new PlatformAuthCommands();
|
|
9863
|
-
this.remoteCommands = new RemoteCommands();
|
|
9864
|
-
this.remote = new RemoteRuntimeActions({
|
|
9245
|
+
}));
|
|
9246
|
+
this.cronCommands = measureStartupSync("cli.runtime.cron_commands", () => new CronCommands());
|
|
9247
|
+
this.platformAuthCommands = measureStartupSync("cli.runtime.platform_auth_commands", () => new PlatformAuthCommands());
|
|
9248
|
+
this.remoteCommands = measureStartupSync("cli.runtime.remote_commands", () => new RemoteCommands());
|
|
9249
|
+
this.remote = measureStartupSync("cli.runtime.remote_runtime_actions", () => new RemoteRuntimeActions({
|
|
9865
9250
|
appName: APP_NAME5,
|
|
9866
9251
|
initAuto: (source) => this.init({ source, auto: true }),
|
|
9867
9252
|
remoteCommands: this.remoteCommands,
|
|
9868
9253
|
restartBackgroundService: (reason) => this.restartBackgroundService(reason),
|
|
9869
9254
|
hasRunningManagedService: hasRunningNextclawManagedService
|
|
9870
|
-
});
|
|
9871
|
-
this.diagnosticsCommands = new DiagnosticsCommands({ logo: this.logo });
|
|
9872
|
-
this.restartCoordinator = new RestartCoordinator({
|
|
9255
|
+
}));
|
|
9256
|
+
this.diagnosticsCommands = measureStartupSync("cli.runtime.diagnostics_commands", () => new DiagnosticsCommands({ logo: this.logo }));
|
|
9257
|
+
this.restartCoordinator = measureStartupSync("cli.runtime.restart_coordinator", () => new RestartCoordinator({
|
|
9873
9258
|
readServiceState,
|
|
9874
9259
|
isProcessRunning,
|
|
9875
9260
|
currentPid: () => process.pid,
|
|
9876
9261
|
restartBackgroundService: async (reason) => this.restartBackgroundService(reason),
|
|
9877
9262
|
scheduleProcessExit: (delayMs, reason) => this.scheduleProcessExit(delayMs, reason)
|
|
9878
|
-
});
|
|
9263
|
+
}));
|
|
9264
|
+
logStartupTrace("cli.runtime.constructor.end");
|
|
9879
9265
|
}
|
|
9880
9266
|
get version() {
|
|
9881
9267
|
return getPackageVersion();
|
|
@@ -9933,7 +9319,7 @@ var CliRuntime = class {
|
|
|
9933
9319
|
const delayMs = typeof params.delayMs === "number" && Number.isFinite(params.delayMs) ? Math.max(0, Math.floor(params.delayMs)) : 100;
|
|
9934
9320
|
const cliPath = process.env.NEXTCLAW_SELF_RELAUNCH_CLI?.trim() || fileURLToPath5(new URL("./index.js", import.meta.url));
|
|
9935
9321
|
const startArgs = [cliPath, "start", "--ui-port", String(uiPort)];
|
|
9936
|
-
const serviceStatePath = resolve14(
|
|
9322
|
+
const serviceStatePath = resolve14(getDataDir10(), "run", "service.json");
|
|
9937
9323
|
const helperScript = [
|
|
9938
9324
|
'const { spawnSync } = require("node:child_process");',
|
|
9939
9325
|
'const { readFileSync } = require("node:fs");',
|
|
@@ -10065,9 +9451,9 @@ var CliRuntime = class {
|
|
|
10065
9451
|
const createdConfig = initializeConfigIfMissing(configPath);
|
|
10066
9452
|
const config2 = loadConfig19();
|
|
10067
9453
|
const workspaceSetting = config2.agents.defaults.workspace;
|
|
10068
|
-
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ?
|
|
10069
|
-
const workspaceExisted =
|
|
10070
|
-
|
|
9454
|
+
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join9(getDataDir10(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
|
|
9455
|
+
const workspaceExisted = existsSync13(workspacePath);
|
|
9456
|
+
mkdirSync7(workspacePath, { recursive: true });
|
|
10071
9457
|
const templateResult = this.workspaceManager.createWorkspaceTemplates(
|
|
10072
9458
|
workspacePath,
|
|
10073
9459
|
{ force }
|
|
@@ -10258,10 +9644,10 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
|
|
|
10258
9644
|
`${this.logo} Interactive mode (type exit or Ctrl+C to quit)
|
|
10259
9645
|
`
|
|
10260
9646
|
);
|
|
10261
|
-
const historyFile =
|
|
9647
|
+
const historyFile = join9(getDataDir10(), "history", "cli_history");
|
|
10262
9648
|
const historyDir = resolve14(historyFile, "..");
|
|
10263
|
-
|
|
10264
|
-
const history =
|
|
9649
|
+
mkdirSync7(historyDir, { recursive: true });
|
|
9650
|
+
const history = existsSync13(historyFile) ? readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean) : [];
|
|
10265
9651
|
const rl = createInterface3({
|
|
10266
9652
|
input: process.stdin,
|
|
10267
9653
|
output: process.stdout
|
|
@@ -10270,7 +9656,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
|
|
|
10270
9656
|
const merged = history.concat(
|
|
10271
9657
|
rl.history ?? []
|
|
10272
9658
|
);
|
|
10273
|
-
|
|
9659
|
+
writeFileSync6(historyFile, merged.join("\n"));
|
|
10274
9660
|
process.exit(0);
|
|
10275
9661
|
});
|
|
10276
9662
|
let running = true;
|
|
@@ -10496,8 +9882,9 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
|
|
|
10496
9882
|
};
|
|
10497
9883
|
|
|
10498
9884
|
// src/cli/index.ts
|
|
9885
|
+
logStartupTrace("cli.index.module_loaded");
|
|
10499
9886
|
var program = new Command();
|
|
10500
|
-
var runtime = new CliRuntime({ logo: LOGO });
|
|
9887
|
+
var runtime = measureStartupSync("cli.runtime.construct", () => new CliRuntime({ logo: LOGO }));
|
|
10501
9888
|
program.name(APP_NAME6).description(`${LOGO} ${APP_NAME6} - ${APP_TAGLINE}`).version(getPackageVersion(), "-v, --version", "show version");
|
|
10502
9889
|
program.command("onboard").description(`Initialize ${APP_NAME6} configuration and workspace`).action(async () => runtime.onboard());
|
|
10503
9890
|
program.command("init").description(`Initialize ${APP_NAME6} configuration and workspace`).option("-f, --force", "Overwrite existing template files").action(async (opts) => runtime.init({ force: Boolean(opts.force) }));
|