@nextclaw/kernel 0.6.23 → 0.6.25
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/index.d.ts +162 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1459 -600
- package/dist/index.js.map +1 -1
- package/package.json +15 -14
package/dist/index.js
CHANGED
|
@@ -3,20 +3,22 @@ import { n as getUnsignedUpdateManifest, r as serializeUnsignedUpdateManifest, t
|
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ConfigSchema, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER, EditFileTool, ExecTool, ExtensionChannelAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, ProviderModelDiscoveryService, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SILENT_REPLY_TOKEN, SessionProjectContextResolver, SessionSearchService, SkillsLoader, THINKING_LEVELS, ViewImageTool, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAgentProfile, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createRuntimeChildEnv, createTypingStopControlMessage, diffConfigPaths, ensureDir, estimateInputTokens, evaluateSilentReply, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, isNextclawControlMessage, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeAgentProfileId, normalizeInlineSecretRefs, normalizeModelThinkingCapability, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, removeAgentProfile, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveEffectiveAgentProfiles, resolveNextclawSelfManageGuidePaths, resolveProviderRuntime, resolveSessionProjectContext, resolveSessionWorkspacePath, resolveThinkingLevel, safeFilename, sanitizeOutboundAssistantContent, saveConfig, summarizeSessionRequestTask, toExtensionConfigView, updateAgentProfile } from "@nextclaw/core";
|
|
5
5
|
import { NCP_AI_EXECUTION_METADATA_KEY, NCP_INTERNAL_VISIBILITY_METADATA_KEY, NcpEventType, createUnavailableNcpAiExecutionMetadata, isHiddenNcpMessage, normalizeAssistantText, readNcpAiExecutionMetadata, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
|
|
6
|
-
import { CHAT_CONTINUATION_TARGET_MESSAGE_METADATA_KEY, CHAT_CONVERSATION_EXCERPT_TOKEN_KIND, CHAT_INLINE_TOKENS_METADATA_KEY, CHAT_INLINE_TOKENS_SCHEMA_VERSION, CHAT_PROJECT_TOKEN_KIND, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_EXCERPT_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, EventBus,
|
|
7
|
-
import { LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
|
|
6
|
+
import { CHAT_CONTINUATION_TARGET_MESSAGE_METADATA_KEY, CHAT_CONVERSATION_EXCERPT_TOKEN_KIND, CHAT_INLINE_TOKENS_METADATA_KEY, CHAT_INLINE_TOKENS_SCHEMA_VERSION, CHAT_PROJECT_TOKEN_KIND, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_SYSTEM_OBJECT_TOKEN_KIND, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_EXCERPT_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, EventBus, Ingress, PANEL_APP_INLINE_HOST_CONTRACT, PANEL_APP_SCROLL_RESTORATION_CONTRACT, SYSTEM_OBJECT_REFERENCE_DEFAULT_LIMIT, SYSTEM_OBJECT_REFERENCE_MAX_LIMIT, SYSTEM_OBJECT_TYPE_CRON_JOB, SYSTEM_OBJECT_TYPE_INBOX_DELIVERY, UI_CONTENT_PARAMS_HOST_CONTRACT, createSystemObjectReferenceUri, eventKeys, ingressKeys, isRuntimeDefaultModelValue, isSilentReplyNcpMessage, normalizeRuntimeModelSelectionMode, parseSystemObjectReferenceUri, readInlineContentHeight, readSystemObjectResolvedReference, readUiContentParams } from "@nextclaw/shared";
|
|
7
|
+
import { LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, isTextLikeAsset, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
|
|
8
8
|
import { createHash, createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
9
9
|
import { catchError, filter, from, lastValueFrom, tap } from "rxjs";
|
|
10
|
-
import { appendFileSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
12
|
-
import { execFileSync, spawn } from "node:child_process";
|
|
10
|
+
import { appendFileSync, chmodSync, constants, createReadStream, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
import path, { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
13
12
|
import { access, appendFile, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
13
|
+
import { AppHomeService, AppInstallationService, AppManifestService, AppRegistryService, isAppComponentManifestBundle } from "@nextclaw/app-runtime";
|
|
14
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
15
16
|
import { BUILTIN_PROVIDER_PLUGINS } from "@nextclaw/runtime";
|
|
16
17
|
import { McpRegistryService, McpServerLifecycleManager } from "@nextclaw/mcp";
|
|
17
18
|
import { McpNcpToolRegistryAdapter } from "@nextclaw/ncp-mcp";
|
|
18
19
|
import { DefaultNcpAgentConversationStateManager, insertMessageByTimeline } from "@nextclaw/ncp-toolkit";
|
|
19
20
|
import { parse } from "yaml";
|
|
21
|
+
import { createInterface } from "node:readline";
|
|
20
22
|
import { HttpRuntimeConfigResolver, HttpRuntimeNcpAgentRuntime } from "@nextclaw/nextclaw-ncp-runtime-http-client";
|
|
21
23
|
import { StdioRuntimeConfigResolver, StdioRuntimeNcpAgentRuntime, probeStdioRuntime } from "@nextclaw/nextclaw-ncp-runtime-stdio-client";
|
|
22
24
|
import { DefaultNcpAgentRuntime } from "@nextclaw/ncp-agent-runtime-next";
|
|
@@ -1231,7 +1233,7 @@ const SESSION_ACTIVITY_PREVIEW_STATUS_KINDS = new Set([
|
|
|
1231
1233
|
"run-failed",
|
|
1232
1234
|
"run-interrupted"
|
|
1233
1235
|
]);
|
|
1234
|
-
function isRecord$
|
|
1236
|
+
function isRecord$16(value) {
|
|
1235
1237
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1236
1238
|
}
|
|
1237
1239
|
function readOptionalString$12(value) {
|
|
@@ -1240,7 +1242,7 @@ function readOptionalString$12(value) {
|
|
|
1240
1242
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
1241
1243
|
}
|
|
1242
1244
|
function readSessionActivityPreviewMetadata(value) {
|
|
1243
|
-
if (!isRecord$
|
|
1245
|
+
if (!isRecord$16(value)) return null;
|
|
1244
1246
|
const state = value.state;
|
|
1245
1247
|
const timestamp = readOptionalString$12(value.timestamp);
|
|
1246
1248
|
if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
|
|
@@ -1334,7 +1336,7 @@ var SessionActivityPreviewEventService = class {
|
|
|
1334
1336
|
//#endregion
|
|
1335
1337
|
//#region src/managers/agent-run-session-command.manager.ts
|
|
1336
1338
|
const CONTINUATION_PROMPT = "Continue from where you stopped. Preserve completed work and avoid repeating it.";
|
|
1337
|
-
function isRecord$
|
|
1339
|
+
function isRecord$15(value) {
|
|
1338
1340
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1339
1341
|
}
|
|
1340
1342
|
function hasSendableMessageContent(message) {
|
|
@@ -1342,7 +1344,7 @@ function hasSendableMessageContent(message) {
|
|
|
1342
1344
|
}
|
|
1343
1345
|
function readSessionActivityState(metadata) {
|
|
1344
1346
|
const preview = metadata?.[SESSION_ACTIVITY_PREVIEW_METADATA_KEY];
|
|
1345
|
-
return isRecord$
|
|
1347
|
+
return isRecord$15(preview) && typeof preview.state === "string" ? preview.state : null;
|
|
1346
1348
|
}
|
|
1347
1349
|
var AgentRunSessionCommandManager = class {
|
|
1348
1350
|
pendingCommands = /* @__PURE__ */ new Map();
|
|
@@ -2503,6 +2505,318 @@ var AutomationManager = class extends CronService {
|
|
|
2503
2505
|
}
|
|
2504
2506
|
};
|
|
2505
2507
|
//#endregion
|
|
2508
|
+
//#region src/types/app-package.types.ts
|
|
2509
|
+
var AppPackageError = class extends Error {
|
|
2510
|
+
constructor(code, message) {
|
|
2511
|
+
super(message);
|
|
2512
|
+
this.code = code;
|
|
2513
|
+
this.name = "AppPackageError";
|
|
2514
|
+
}
|
|
2515
|
+
};
|
|
2516
|
+
function isAppPackageError(error) {
|
|
2517
|
+
return error instanceof AppPackageError;
|
|
2518
|
+
}
|
|
2519
|
+
//#endregion
|
|
2520
|
+
//#region src/utils/app-engine-version.utils.ts
|
|
2521
|
+
function satisfiesAppEngineVersion(version, range) {
|
|
2522
|
+
const parsedVersion = parseVersion(version);
|
|
2523
|
+
const normalizedRange = range.trim();
|
|
2524
|
+
if (!parsedVersion || !normalizedRange) return false;
|
|
2525
|
+
return normalizedRange.split(/\s*\|\|\s*/).some((alternative) => satisfiesAlternative(parsedVersion, alternative.trim()));
|
|
2526
|
+
}
|
|
2527
|
+
function satisfiesAlternative(version, range) {
|
|
2528
|
+
const hyphenRange = /^(\S+)\s+-\s+(\S+)$/.exec(range);
|
|
2529
|
+
if (hyphenRange) {
|
|
2530
|
+
const lower = parseVersion(hyphenRange[1] ?? "");
|
|
2531
|
+
const upper = parseVersion(hyphenRange[2] ?? "");
|
|
2532
|
+
return Boolean(lower && upper && compareVersions(version, lower) >= 0 && compareVersions(version, upper) <= 0);
|
|
2533
|
+
}
|
|
2534
|
+
const comparators = range.split(/\s+/).filter(Boolean);
|
|
2535
|
+
return comparators.length > 0 && comparators.every((comparator) => satisfiesComparator(version, comparator));
|
|
2536
|
+
}
|
|
2537
|
+
function satisfiesComparator(version, comparator) {
|
|
2538
|
+
if (comparator === "*" || comparator.toLowerCase() === "x") return true;
|
|
2539
|
+
if (comparator.startsWith("^")) {
|
|
2540
|
+
const lower = parseVersion(comparator.slice(1));
|
|
2541
|
+
if (!lower) return false;
|
|
2542
|
+
const upper = lower.major > 0 ? {
|
|
2543
|
+
major: lower.major + 1,
|
|
2544
|
+
minor: 0,
|
|
2545
|
+
patch: 0,
|
|
2546
|
+
prerelease: []
|
|
2547
|
+
} : lower.minor > 0 ? {
|
|
2548
|
+
major: 0,
|
|
2549
|
+
minor: lower.minor + 1,
|
|
2550
|
+
patch: 0,
|
|
2551
|
+
prerelease: []
|
|
2552
|
+
} : {
|
|
2553
|
+
major: 0,
|
|
2554
|
+
minor: 0,
|
|
2555
|
+
patch: lower.patch + 1,
|
|
2556
|
+
prerelease: []
|
|
2557
|
+
};
|
|
2558
|
+
return compareVersions(version, lower) >= 0 && compareVersions(version, upper) < 0;
|
|
2559
|
+
}
|
|
2560
|
+
if (comparator.startsWith("~")) {
|
|
2561
|
+
const lower = parseVersion(comparator.slice(1));
|
|
2562
|
+
if (!lower) return false;
|
|
2563
|
+
const upper = {
|
|
2564
|
+
major: lower.major,
|
|
2565
|
+
minor: lower.minor + 1,
|
|
2566
|
+
patch: 0,
|
|
2567
|
+
prerelease: []
|
|
2568
|
+
};
|
|
2569
|
+
return compareVersions(version, lower) >= 0 && compareVersions(version, upper) < 0;
|
|
2570
|
+
}
|
|
2571
|
+
if (/[xX*]/.test(comparator)) {
|
|
2572
|
+
const parts = comparator.replace(/^v/, "").split(".");
|
|
2573
|
+
const expected = [
|
|
2574
|
+
version.major,
|
|
2575
|
+
version.minor,
|
|
2576
|
+
version.patch
|
|
2577
|
+
];
|
|
2578
|
+
return parts.every((part, index) => /^(x|\*)$/i.test(part) || Number(part) === expected[index]);
|
|
2579
|
+
}
|
|
2580
|
+
const match = /^(>=|<=|>|<|=)?\s*(.+)$/.exec(comparator);
|
|
2581
|
+
const target = parseVersion(match?.[2] ?? "");
|
|
2582
|
+
if (!target) return false;
|
|
2583
|
+
const compared = compareVersions(version, target);
|
|
2584
|
+
switch (match?.[1] ?? "=") {
|
|
2585
|
+
case ">=": return compared >= 0;
|
|
2586
|
+
case "<=": return compared <= 0;
|
|
2587
|
+
case ">": return compared > 0;
|
|
2588
|
+
case "<": return compared < 0;
|
|
2589
|
+
default: return compared === 0;
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
function parseVersion(raw) {
|
|
2593
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(raw.trim());
|
|
2594
|
+
if (!match) return null;
|
|
2595
|
+
return {
|
|
2596
|
+
major: Number(match[1]),
|
|
2597
|
+
minor: Number(match[2]),
|
|
2598
|
+
patch: Number(match[3]),
|
|
2599
|
+
prerelease: match[4] ? match[4].split(".").map((part) => /^\d+$/.test(part) ? Number(part) : part) : []
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2602
|
+
function compareVersions(left, right) {
|
|
2603
|
+
for (const field of [
|
|
2604
|
+
"major",
|
|
2605
|
+
"minor",
|
|
2606
|
+
"patch"
|
|
2607
|
+
]) if (left[field] !== right[field]) return left[field] > right[field] ? 1 : -1;
|
|
2608
|
+
if (left.prerelease.length === 0 || right.prerelease.length === 0) return Number(left.prerelease.length === 0) - Number(right.prerelease.length === 0);
|
|
2609
|
+
const length = Math.max(left.prerelease.length, right.prerelease.length);
|
|
2610
|
+
for (let index = 0; index < length; index += 1) {
|
|
2611
|
+
const leftPart = left.prerelease[index];
|
|
2612
|
+
const rightPart = right.prerelease[index];
|
|
2613
|
+
if (leftPart === void 0 || rightPart === void 0) return leftPart === void 0 ? -1 : 1;
|
|
2614
|
+
if (leftPart === rightPart) continue;
|
|
2615
|
+
if (typeof leftPart === "number" && typeof rightPart === "number") return leftPart > rightPart ? 1 : -1;
|
|
2616
|
+
if (typeof leftPart === "number") return -1;
|
|
2617
|
+
if (typeof rightPart === "number") return 1;
|
|
2618
|
+
return leftPart.localeCompare(rightPart);
|
|
2619
|
+
}
|
|
2620
|
+
return 0;
|
|
2621
|
+
}
|
|
2622
|
+
//#endregion
|
|
2623
|
+
//#region src/managers/app-package.manager.ts
|
|
2624
|
+
const EMPTY_RUNTIME_HOOKS = {
|
|
2625
|
+
assertCanActivate: async () => void 0,
|
|
2626
|
+
beforeDeactivate: async () => void 0,
|
|
2627
|
+
beforeUninstall: async () => void 0
|
|
2628
|
+
};
|
|
2629
|
+
var AppPackageManager = class {
|
|
2630
|
+
installationService;
|
|
2631
|
+
manifestService = new AppManifestService();
|
|
2632
|
+
registryService;
|
|
2633
|
+
runtimeHooks = EMPTY_RUNTIME_HOOKS;
|
|
2634
|
+
builtInBootstrapPromise;
|
|
2635
|
+
constructor(params) {
|
|
2636
|
+
this.params = params;
|
|
2637
|
+
const appHomeService = new AppHomeService(params.appHomeDirectory);
|
|
2638
|
+
this.installationService = new AppInstallationService(appHomeService);
|
|
2639
|
+
this.registryService = new AppRegistryService(appHomeService);
|
|
2640
|
+
}
|
|
2641
|
+
installRuntimeHooks = (hooks) => {
|
|
2642
|
+
this.runtimeHooks = hooks;
|
|
2643
|
+
};
|
|
2644
|
+
listPackages = async () => {
|
|
2645
|
+
await this.ensureBuiltInPackages();
|
|
2646
|
+
const records = await this.registryService.listApps();
|
|
2647
|
+
return { entries: await Promise.all(records.map(async (record) => await this.toPackageView(await this.installationService.info(record.appId)))) };
|
|
2648
|
+
};
|
|
2649
|
+
getPackage = async (appId) => {
|
|
2650
|
+
await this.ensureBuiltInPackages();
|
|
2651
|
+
try {
|
|
2652
|
+
return await this.toPackageView(await this.installationService.info(appId));
|
|
2653
|
+
} catch (error) {
|
|
2654
|
+
if (error instanceof Error && error.message.includes("未找到已安装应用")) throw new AppPackageError("APP_PACKAGE_NOT_FOUND", error.message);
|
|
2655
|
+
throw error;
|
|
2656
|
+
}
|
|
2657
|
+
};
|
|
2658
|
+
listActiveComponentSources = async () => {
|
|
2659
|
+
await this.ensureBuiltInPackages();
|
|
2660
|
+
return (await this.registryService.listApps()).filter((record) => record.enabled).flatMap((record) => {
|
|
2661
|
+
const version = record.installedVersions[record.activeVersion];
|
|
2662
|
+
if (!version || version.manifestSchemaVersion !== 2) return [];
|
|
2663
|
+
return (version.components ?? []).map((component) => ({
|
|
2664
|
+
kind: component.kind,
|
|
2665
|
+
id: component.id,
|
|
2666
|
+
packageId: record.appId,
|
|
2667
|
+
packageVersion: record.activeVersion,
|
|
2668
|
+
sourcePath: component.componentDirectory,
|
|
2669
|
+
manifestPath: component.manifestPath,
|
|
2670
|
+
dataDirectory: record.dataDirectory
|
|
2671
|
+
}));
|
|
2672
|
+
});
|
|
2673
|
+
};
|
|
2674
|
+
install = async (source, registryUrl) => {
|
|
2675
|
+
const result = await this.installationService.install(source, { registryUrl });
|
|
2676
|
+
return await this.getPackage(result.appId);
|
|
2677
|
+
};
|
|
2678
|
+
enable = async (appId) => {
|
|
2679
|
+
const app = await this.getPackage(appId);
|
|
2680
|
+
if (app.enabled) return app;
|
|
2681
|
+
await this.assertEngineCompatibility(appId);
|
|
2682
|
+
const sources = this.toComponentSources(app);
|
|
2683
|
+
await this.runtimeHooks.assertCanActivate(sources);
|
|
2684
|
+
await this.installationService.setEnabled(appId, true);
|
|
2685
|
+
return await this.getPackage(appId);
|
|
2686
|
+
};
|
|
2687
|
+
disable = async (appId) => {
|
|
2688
|
+
const app = await this.getPackage(appId);
|
|
2689
|
+
if (!app.enabled) return app;
|
|
2690
|
+
await this.runtimeHooks.beforeDeactivate(this.toComponentSources(app));
|
|
2691
|
+
await this.installationService.setEnabled(appId, false);
|
|
2692
|
+
return await this.getPackage(appId);
|
|
2693
|
+
};
|
|
2694
|
+
update = async (appId, options = {}) => {
|
|
2695
|
+
const current = await this.getPackage(appId);
|
|
2696
|
+
if (current.enabled) await this.runtimeHooks.beforeDeactivate(this.toComponentSources(current));
|
|
2697
|
+
const result = await this.installationService.update(appId, options);
|
|
2698
|
+
try {
|
|
2699
|
+
await this.assertEngineCompatibility(appId);
|
|
2700
|
+
const updated = await this.getPackage(appId);
|
|
2701
|
+
if (updated.enabled) await this.runtimeHooks.assertCanActivate(this.toComponentSources(updated));
|
|
2702
|
+
return {
|
|
2703
|
+
package: updated,
|
|
2704
|
+
result
|
|
2705
|
+
};
|
|
2706
|
+
} catch (error) {
|
|
2707
|
+
if (result.updated) await this.installationService.rollback(appId, result.previousVersion);
|
|
2708
|
+
throw error;
|
|
2709
|
+
}
|
|
2710
|
+
};
|
|
2711
|
+
rollback = async (appId, version) => {
|
|
2712
|
+
const current = await this.getPackage(appId);
|
|
2713
|
+
if (current.enabled) await this.runtimeHooks.beforeDeactivate(this.toComponentSources(current));
|
|
2714
|
+
const result = await this.installationService.rollback(appId, version);
|
|
2715
|
+
try {
|
|
2716
|
+
await this.assertEngineCompatibility(appId);
|
|
2717
|
+
const rolledBack = await this.getPackage(appId);
|
|
2718
|
+
if (rolledBack.enabled) await this.runtimeHooks.assertCanActivate(this.toComponentSources(rolledBack));
|
|
2719
|
+
return {
|
|
2720
|
+
package: rolledBack,
|
|
2721
|
+
result
|
|
2722
|
+
};
|
|
2723
|
+
} catch (error) {
|
|
2724
|
+
if (result.rolledBack) await this.installationService.rollback(appId, result.previousVersion);
|
|
2725
|
+
throw error;
|
|
2726
|
+
}
|
|
2727
|
+
};
|
|
2728
|
+
uninstall = async (appId, purgeData) => {
|
|
2729
|
+
const current = await this.getPackage(appId);
|
|
2730
|
+
if (current.enabled) await this.runtimeHooks.beforeDeactivate(this.toComponentSources(current));
|
|
2731
|
+
await this.runtimeHooks.beforeUninstall(this.toComponentSources(current));
|
|
2732
|
+
return await this.installationService.uninstall(appId, purgeData);
|
|
2733
|
+
};
|
|
2734
|
+
ensureBuiltInPackages = async () => {
|
|
2735
|
+
if (!this.params.builtInAppsDirectory) return;
|
|
2736
|
+
this.builtInBootstrapPromise ??= this.installBuiltInPackages();
|
|
2737
|
+
await this.builtInBootstrapPromise;
|
|
2738
|
+
};
|
|
2739
|
+
installBuiltInPackages = async () => {
|
|
2740
|
+
const builtInDirectory = path.resolve(this.params.builtInAppsDirectory);
|
|
2741
|
+
let entries;
|
|
2742
|
+
try {
|
|
2743
|
+
entries = await readdir(builtInDirectory, { withFileTypes: true });
|
|
2744
|
+
} catch (error) {
|
|
2745
|
+
if (this.isMissingFileError(error)) return;
|
|
2746
|
+
throw error;
|
|
2747
|
+
}
|
|
2748
|
+
for (const entry of entries.filter((item) => item.isDirectory() && !item.name.startsWith("."))) {
|
|
2749
|
+
const appDirectory = path.join(builtInDirectory, entry.name);
|
|
2750
|
+
const manifest = await this.manifestService.load(appDirectory);
|
|
2751
|
+
if (!isAppComponentManifestBundle(manifest)) continue;
|
|
2752
|
+
if ((await this.registryService.getApp(manifest.manifest.id))?.installedVersions[manifest.manifest.version]) continue;
|
|
2753
|
+
await this.installationService.install(appDirectory);
|
|
2754
|
+
}
|
|
2755
|
+
};
|
|
2756
|
+
toPackageView = async (info) => {
|
|
2757
|
+
const activeVersion = info.installedVersions.find((version) => version.version === info.activeVersion);
|
|
2758
|
+
if (!activeVersion) throw new AppPackageError("APP_PACKAGE_OPERATION_FAILED", `应用 ${info.appId} 缺少激活版本 ${info.activeVersion}。`);
|
|
2759
|
+
const packagePresentation = await this.readManifestPresentation(path.join(activeVersion.installDirectory, "manifest.json"));
|
|
2760
|
+
return {
|
|
2761
|
+
id: info.appId,
|
|
2762
|
+
name: info.name,
|
|
2763
|
+
description: info.description,
|
|
2764
|
+
nameI18n: packagePresentation.nameI18n,
|
|
2765
|
+
descriptionI18n: packagePresentation.descriptionI18n,
|
|
2766
|
+
activeVersion: info.activeVersion,
|
|
2767
|
+
installedVersions: info.installedVersions.map((version) => version.version),
|
|
2768
|
+
enabled: info.enabled,
|
|
2769
|
+
builtIn: this.isBuiltInSource(activeVersion.sourceRef),
|
|
2770
|
+
primaryPanelId: activeVersion.primaryPanelId,
|
|
2771
|
+
components: await Promise.all((activeVersion.components ?? []).map(async (component) => ({
|
|
2772
|
+
kind: component.kind,
|
|
2773
|
+
id: component.id,
|
|
2774
|
+
packageId: info.appId,
|
|
2775
|
+
packageVersion: info.activeVersion,
|
|
2776
|
+
sourcePath: component.componentDirectory,
|
|
2777
|
+
manifestPath: component.manifestPath,
|
|
2778
|
+
dataDirectory: info.dataDirectory,
|
|
2779
|
+
...await this.readManifestPresentation(component.manifestPath)
|
|
2780
|
+
}))),
|
|
2781
|
+
dataDirectory: info.dataDirectory
|
|
2782
|
+
};
|
|
2783
|
+
};
|
|
2784
|
+
readManifestPresentation = async (manifestPath) => {
|
|
2785
|
+
const candidate = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
2786
|
+
return {
|
|
2787
|
+
...typeof candidate.title === "string" ? { title: candidate.title } : {},
|
|
2788
|
+
...typeof candidate.description === "string" ? { description: candidate.description } : {},
|
|
2789
|
+
...typeof candidate.icon === "string" ? { icon: candidate.icon } : {},
|
|
2790
|
+
...this.readLocalizedField(candidate, "nameI18n"),
|
|
2791
|
+
...this.readLocalizedField(candidate, "titleI18n"),
|
|
2792
|
+
...this.readLocalizedField(candidate, "descriptionI18n")
|
|
2793
|
+
};
|
|
2794
|
+
};
|
|
2795
|
+
readLocalizedField = (candidate, field) => {
|
|
2796
|
+
const value = candidate[field];
|
|
2797
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
2798
|
+
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
2799
|
+
return entries.length > 0 ? { [field]: Object.fromEntries(entries) } : {};
|
|
2800
|
+
};
|
|
2801
|
+
toComponentSources = (app) => app.components.map((component) => ({ ...component }));
|
|
2802
|
+
assertEngineCompatibility = async (appId) => {
|
|
2803
|
+
const productVersion = this.params.productVersion?.trim();
|
|
2804
|
+
if (!productVersion) return;
|
|
2805
|
+
const info = await this.installationService.info(appId);
|
|
2806
|
+
const activeVersion = info.installedVersions.find((version) => version.version === info.activeVersion);
|
|
2807
|
+
if (!activeVersion) throw new AppPackageError("APP_PACKAGE_OPERATION_FAILED", `应用 ${appId} 缺少激活版本 ${info.activeVersion}。`);
|
|
2808
|
+
const manifestBundle = await this.manifestService.load(activeVersion.installDirectory);
|
|
2809
|
+
const engineRange = manifestBundle.manifest.schemaVersion === 2 ? manifestBundle.manifest.engines?.nextclaw?.trim() : void 0;
|
|
2810
|
+
if (engineRange && !satisfiesAppEngineVersion(productVersion, engineRange)) throw new AppPackageError("APP_PACKAGE_INCOMPATIBLE", `应用 ${appId}@${info.activeVersion} 要求 NextClaw ${engineRange},当前版本为 ${productVersion}。`);
|
|
2811
|
+
};
|
|
2812
|
+
isBuiltInSource = (sourceRef) => {
|
|
2813
|
+
if (!this.params.builtInAppsDirectory) return false;
|
|
2814
|
+
const relative = path.relative(path.resolve(this.params.builtInAppsDirectory), path.resolve(sourceRef));
|
|
2815
|
+
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
2816
|
+
};
|
|
2817
|
+
isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
2818
|
+
};
|
|
2819
|
+
//#endregion
|
|
2506
2820
|
//#region src/managers/channel.manager.ts
|
|
2507
2821
|
var ChannelManager = class {
|
|
2508
2822
|
channels = {};
|
|
@@ -2901,9 +3215,9 @@ var ConfigManager = class {
|
|
|
2901
3215
|
required: true,
|
|
2902
3216
|
automatic: false,
|
|
2903
3217
|
changedPaths: [...plan.restartRequired],
|
|
2904
|
-
message: `Config saved.
|
|
3218
|
+
message: `Config saved. Run nextclaw restart in an external terminal to apply: ${plan.restartRequired.join(", ")}.`
|
|
2905
3219
|
} : null;
|
|
2906
|
-
const message = changedPaths.length === 0 ? "Config already matched the requested state." : pendingRestart ? changedPaths.length > plan.restartRequired.length ? "Config saved. Supported changes were applied immediately; restart
|
|
3220
|
+
const message = changedPaths.length === 0 ? "Config already matched the requested state." : pendingRestart ? changedPaths.length > plan.restartRequired.length ? "Config saved. Supported changes were applied immediately; run nextclaw restart in an external terminal to apply the rest." : "Config saved. Run nextclaw restart in an external terminal to apply changes." : "Config saved and applied.";
|
|
2907
3221
|
return {
|
|
2908
3222
|
ok: true,
|
|
2909
3223
|
note: note ?? null,
|
|
@@ -4550,11 +4864,11 @@ var ExtensionManager = class {
|
|
|
4550
4864
|
//#endregion
|
|
4551
4865
|
//#region src/utils/model-message-vision.utils.ts
|
|
4552
4866
|
const IMAGE_OMITTED_TEXT = "[Image omitted: the selected model is not configured for vision input.]";
|
|
4553
|
-
function isRecord$
|
|
4867
|
+
function isRecord$14(value) {
|
|
4554
4868
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4555
4869
|
}
|
|
4556
4870
|
function isImageContentPart(value) {
|
|
4557
|
-
if (!isRecord$
|
|
4871
|
+
if (!isRecord$14(value)) return false;
|
|
4558
4872
|
const type = value.type;
|
|
4559
4873
|
return type === "image_url" || type === "input_image";
|
|
4560
4874
|
}
|
|
@@ -4570,7 +4884,7 @@ function normalizeContentWithoutVision(content) {
|
|
|
4570
4884
|
};
|
|
4571
4885
|
});
|
|
4572
4886
|
if (!sawImage) return content;
|
|
4573
|
-
const textParts = parts.filter((part) => isRecord$
|
|
4887
|
+
const textParts = parts.filter((part) => isRecord$14(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text.trim()).filter(Boolean);
|
|
4574
4888
|
if (textParts.length === parts.length) return textParts.join("\n\n");
|
|
4575
4889
|
return parts;
|
|
4576
4890
|
}
|
|
@@ -5205,7 +5519,8 @@ var LlmUsageManager = class {
|
|
|
5205
5519
|
};
|
|
5206
5520
|
//#endregion
|
|
5207
5521
|
//#region src/stores/inbox-delivery.store.ts
|
|
5208
|
-
const INBOX_DELIVERY_STORE_VERSION =
|
|
5522
|
+
const INBOX_DELIVERY_STORE_VERSION = 2;
|
|
5523
|
+
const LEGACY_INBOX_DELIVERY_STORE_VERSION = 1;
|
|
5209
5524
|
var InboxDeliveryStoreError = class extends Error {
|
|
5210
5525
|
constructor(message) {
|
|
5211
5526
|
super(message);
|
|
@@ -5242,15 +5557,19 @@ var InboxDeliveryStore = class {
|
|
|
5242
5557
|
};
|
|
5243
5558
|
parseStoreFile = (source) => {
|
|
5244
5559
|
const value = JSON.parse(source);
|
|
5245
|
-
if (!this.isRecord(value) || value.version !== INBOX_DELIVERY_STORE_VERSION || !Array.isArray(value.deliveries) || !value.deliveries.every(this.isDelivery)) throw new InboxDeliveryStoreError("inbox delivery store has an unsupported structure");
|
|
5560
|
+
if (!this.isRecord(value) || value.version !== INBOX_DELIVERY_STORE_VERSION && value.version !== LEGACY_INBOX_DELIVERY_STORE_VERSION || !Array.isArray(value.deliveries) || !value.deliveries.every((delivery) => this.isDelivery(delivery, value.version === LEGACY_INBOX_DELIVERY_STORE_VERSION))) throw new InboxDeliveryStoreError("inbox delivery store has an unsupported structure");
|
|
5246
5561
|
return {
|
|
5247
5562
|
version: INBOX_DELIVERY_STORE_VERSION,
|
|
5248
|
-
deliveries: value.deliveries.map((delivery) =>
|
|
5563
|
+
deliveries: value.deliveries.map((delivery) => this.toCurrentDelivery(delivery))
|
|
5249
5564
|
};
|
|
5250
5565
|
};
|
|
5251
|
-
isDelivery = (value) => {
|
|
5566
|
+
isDelivery = (value, legacy) => {
|
|
5252
5567
|
if (!this.isRecord(value) || !this.isSource(value.source)) return false;
|
|
5253
|
-
return typeof value.id === "string" && typeof value.title === "string" && (value.summary === null || typeof value.summary === "string") && typeof value.content === "string" && (value.contentType === "markdown" || value.contentType === "html") && typeof value.createdAt === "string" && typeof value.updatedAt === "string" && this.isOptionalTimestamp(value.presentedAt) && this.isOptionalTimestamp(value.readAt) && this.isOptionalTimestamp(value.archivedAt) && (value.conversationSessionId === null || typeof value.conversationSessionId === "string");
|
|
5568
|
+
return typeof value.id === "string" && typeof value.title === "string" && (value.summary === null || typeof value.summary === "string") && typeof value.content === "string" && (value.contentType === "markdown" || value.contentType === "html") && typeof value.createdAt === "string" && typeof value.updatedAt === "string" && this.isOptionalTimestamp(value.presentedAt) && this.isOptionalTimestamp(value.readAt) && this.isOptionalTimestamp(value.archivedAt) && (!legacy || value.conversationSessionId === null || typeof value.conversationSessionId === "string");
|
|
5569
|
+
};
|
|
5570
|
+
toCurrentDelivery = (delivery) => {
|
|
5571
|
+
const { conversationSessionId: _legacySessionId, ...current } = delivery;
|
|
5572
|
+
return structuredClone(current);
|
|
5254
5573
|
};
|
|
5255
5574
|
isSource = (value) => this.isRecord(value) && value.kind === "agent" && this.isOptionalString(value.agentId) && this.isOptionalString(value.sessionId) && this.isOptionalString(value.toolCallId) && this.isOptionalString(value.filePath);
|
|
5256
5575
|
isOptionalString = (value) => value === null || typeof value === "string";
|
|
@@ -5308,8 +5627,7 @@ var InboxDeliveryManager = class {
|
|
|
5308
5627
|
updatedAt: now,
|
|
5309
5628
|
presentedAt: null,
|
|
5310
5629
|
readAt: null,
|
|
5311
|
-
archivedAt: null
|
|
5312
|
-
conversationSessionId: null
|
|
5630
|
+
archivedAt: null
|
|
5313
5631
|
};
|
|
5314
5632
|
const deliveries = await this.store.list();
|
|
5315
5633
|
await this.store.save([delivery, ...deliveries]);
|
|
@@ -5336,41 +5654,6 @@ var InboxDeliveryManager = class {
|
|
|
5336
5654
|
this.publishChange(deliveryId, "delete");
|
|
5337
5655
|
return true;
|
|
5338
5656
|
});
|
|
5339
|
-
continueInChat = async (deliveryId) => await this.mutate(async () => {
|
|
5340
|
-
const deliveries = await this.store.list();
|
|
5341
|
-
const index = deliveries.findIndex(({ id }) => id === deliveryId);
|
|
5342
|
-
if (index < 0) throw this.notFound(deliveryId);
|
|
5343
|
-
const current = deliveries[index];
|
|
5344
|
-
const existingSession = current.conversationSessionId ? await this.options.sessionManager.getSessionRecord(current.conversationSessionId) : null;
|
|
5345
|
-
let sessionId = current.conversationSessionId;
|
|
5346
|
-
let created = false;
|
|
5347
|
-
if (!sessionId || !existingSession) {
|
|
5348
|
-
sessionId = (await this.options.sessionManager.createSession({
|
|
5349
|
-
sourceSessionMetadata: {},
|
|
5350
|
-
metadataOverrides: { [INBOX_DELIVERY_SESSION_METADATA_KEY]: current.id },
|
|
5351
|
-
task: `Continue discussing inbox delivery: ${current.title}`,
|
|
5352
|
-
title: current.title,
|
|
5353
|
-
agentId: current.source.agentId ?? void 0
|
|
5354
|
-
})).sessionId;
|
|
5355
|
-
created = true;
|
|
5356
|
-
}
|
|
5357
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5358
|
-
const delivery = {
|
|
5359
|
-
...current,
|
|
5360
|
-
updatedAt: now,
|
|
5361
|
-
presentedAt: current.presentedAt ?? now,
|
|
5362
|
-
readAt: current.readAt ?? now,
|
|
5363
|
-
conversationSessionId: sessionId
|
|
5364
|
-
};
|
|
5365
|
-
deliveries[index] = delivery;
|
|
5366
|
-
await this.store.save(deliveries);
|
|
5367
|
-
this.publishChange(deliveryId, "upsert");
|
|
5368
|
-
return {
|
|
5369
|
-
delivery: structuredClone(delivery),
|
|
5370
|
-
sessionId,
|
|
5371
|
-
created
|
|
5372
|
-
};
|
|
5373
|
-
});
|
|
5374
5657
|
applyStateAction = (delivery, action, now) => {
|
|
5375
5658
|
switch (action) {
|
|
5376
5659
|
case "present": return {
|
|
@@ -5426,6 +5709,300 @@ var InboxDeliveryManager = class {
|
|
|
5426
5709
|
};
|
|
5427
5710
|
};
|
|
5428
5711
|
//#endregion
|
|
5712
|
+
//#region src/managers/system-object-reference.manager.ts
|
|
5713
|
+
const MAX_SYSTEM_OBJECT_SNAPSHOT_BYTES = 1024 * 1024;
|
|
5714
|
+
var SystemObjectReferenceError = class extends Error {
|
|
5715
|
+
constructor(code, message) {
|
|
5716
|
+
super(message);
|
|
5717
|
+
this.code = code;
|
|
5718
|
+
this.name = "SystemObjectReferenceError";
|
|
5719
|
+
}
|
|
5720
|
+
};
|
|
5721
|
+
function isSystemObjectReferenceError(error) {
|
|
5722
|
+
return error instanceof SystemObjectReferenceError;
|
|
5723
|
+
}
|
|
5724
|
+
function normalizedSearchText(value) {
|
|
5725
|
+
return value.trim().toLocaleLowerCase();
|
|
5726
|
+
}
|
|
5727
|
+
function scoreSearchItem(item, query) {
|
|
5728
|
+
if (!query) return 1;
|
|
5729
|
+
const fields = [
|
|
5730
|
+
item.label,
|
|
5731
|
+
item.objectId,
|
|
5732
|
+
item.description ?? ""
|
|
5733
|
+
].map(normalizedSearchText).filter(Boolean);
|
|
5734
|
+
if (fields.some((field) => field === query)) return 400;
|
|
5735
|
+
if (fields.some((field) => field.startsWith(query))) return 300;
|
|
5736
|
+
if (fields.some((field) => field.split(/\s+/).some((word) => word.startsWith(query)))) return 200;
|
|
5737
|
+
return fields.some((field) => field.includes(query)) ? 100 : 0;
|
|
5738
|
+
}
|
|
5739
|
+
function normalizeLimit(limit) {
|
|
5740
|
+
if (limit === void 0) return SYSTEM_OBJECT_REFERENCE_DEFAULT_LIMIT;
|
|
5741
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > SYSTEM_OBJECT_REFERENCE_MAX_LIMIT) throw new SystemObjectReferenceError("SYSTEM_OBJECT_INVALID_REFERENCE", `limit must be an integer between 1 and ${SYSTEM_OBJECT_REFERENCE_MAX_LIMIT}`);
|
|
5742
|
+
return limit;
|
|
5743
|
+
}
|
|
5744
|
+
const SYSTEM_OBJECT_GROUP_ICONS = new Set([
|
|
5745
|
+
"calendar-clock",
|
|
5746
|
+
"file",
|
|
5747
|
+
"inbox"
|
|
5748
|
+
]);
|
|
5749
|
+
function normalizeDisplayText(value, field) {
|
|
5750
|
+
const defaultText = value.default.trim();
|
|
5751
|
+
if (!defaultText) throw new Error(`System object provider group ${field} must be non-empty.`);
|
|
5752
|
+
const translations = Object.fromEntries(Object.entries(value.translations ?? {}).map(([language, text]) => [language.trim(), text.trim()]).filter(([language, text]) => language && text));
|
|
5753
|
+
return {
|
|
5754
|
+
default: defaultText,
|
|
5755
|
+
...Object.keys(translations).length > 0 ? { translations } : {}
|
|
5756
|
+
};
|
|
5757
|
+
}
|
|
5758
|
+
function normalizeProviderGroup(group) {
|
|
5759
|
+
const objectType = group.objectType.trim();
|
|
5760
|
+
if (!objectType || !SYSTEM_OBJECT_GROUP_ICONS.has(group.icon) || !Number.isSafeInteger(group.order)) throw new Error(`System object provider group is invalid: ${objectType}`);
|
|
5761
|
+
return {
|
|
5762
|
+
objectType,
|
|
5763
|
+
label: normalizeDisplayText(group.label, "label"),
|
|
5764
|
+
description: normalizeDisplayText(group.description, "description"),
|
|
5765
|
+
icon: group.icon,
|
|
5766
|
+
order: group.order
|
|
5767
|
+
};
|
|
5768
|
+
}
|
|
5769
|
+
function safeSnapshotFileName(label, suffix) {
|
|
5770
|
+
return `${label.trim().replace(/[^\p{L}\p{N}._-]+/gu, "-").replace(/^-+|-+$/g, "") || "system-object"}.${suffix}`;
|
|
5771
|
+
}
|
|
5772
|
+
function truncate(value, maxLength = 180) {
|
|
5773
|
+
const normalized = value?.replace(/\s+/g, " ").trim() ?? "";
|
|
5774
|
+
if (!normalized) return null;
|
|
5775
|
+
return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 1)}…`;
|
|
5776
|
+
}
|
|
5777
|
+
function toIsoTime(value) {
|
|
5778
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
5779
|
+
const date = new Date(value);
|
|
5780
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
5781
|
+
}
|
|
5782
|
+
var SystemObjectReferenceManager = class {
|
|
5783
|
+
providers = /* @__PURE__ */ new Map();
|
|
5784
|
+
resolvedCache = /* @__PURE__ */ new Map();
|
|
5785
|
+
constructor(assetStore, providers = []) {
|
|
5786
|
+
this.assetStore = assetStore;
|
|
5787
|
+
providers.forEach((provider) => this.registerProvider(provider));
|
|
5788
|
+
}
|
|
5789
|
+
registerProvider = (provider) => {
|
|
5790
|
+
const group = normalizeProviderGroup(provider.group);
|
|
5791
|
+
const objectType = group.objectType;
|
|
5792
|
+
if (this.providers.has(objectType)) throw new Error(`System object provider is invalid or already registered: ${objectType}`);
|
|
5793
|
+
const registeredProvider = {
|
|
5794
|
+
...provider,
|
|
5795
|
+
group
|
|
5796
|
+
};
|
|
5797
|
+
this.providers.set(objectType, registeredProvider);
|
|
5798
|
+
return () => {
|
|
5799
|
+
if (this.providers.get(objectType) === registeredProvider) this.providers.delete(objectType);
|
|
5800
|
+
};
|
|
5801
|
+
};
|
|
5802
|
+
listReferences = async (params = {}) => {
|
|
5803
|
+
const query = normalizedSearchText(params.query ?? "");
|
|
5804
|
+
const limit = normalizeLimit(params.limit);
|
|
5805
|
+
const requestedObjectType = params.objectType?.trim();
|
|
5806
|
+
if (params.objectType !== void 0 && !requestedObjectType) throw new SystemObjectReferenceError("SYSTEM_OBJECT_INVALID_REFERENCE", "objectType must be a non-empty registered system object type");
|
|
5807
|
+
const requestedProvider = requestedObjectType ? this.providers.get(requestedObjectType) : void 0;
|
|
5808
|
+
if (requestedObjectType && !requestedProvider) throw new SystemObjectReferenceError("SYSTEM_OBJECT_INVALID_REFERENCE", `system object provider is not registered: ${requestedObjectType}`);
|
|
5809
|
+
const providers = requestedProvider ? [requestedProvider] : [...this.providers.values()];
|
|
5810
|
+
const collator = new Intl.Collator(void 0, {
|
|
5811
|
+
numeric: true,
|
|
5812
|
+
sensitivity: "base"
|
|
5813
|
+
});
|
|
5814
|
+
const shouldIncludeItems = Boolean(requestedObjectType || query);
|
|
5815
|
+
const visibleGroups = (await Promise.all(providers.map(async (provider) => {
|
|
5816
|
+
const matches = (await provider.list()).map((item) => {
|
|
5817
|
+
this.assertListItemContract(item, provider.group.objectType);
|
|
5818
|
+
return {
|
|
5819
|
+
item,
|
|
5820
|
+
score: scoreSearchItem(item, query)
|
|
5821
|
+
};
|
|
5822
|
+
}).filter(({ score }) => score > 0).sort((left, right) => right.score - left.score || right.item.updatedAt.localeCompare(left.item.updatedAt) || collator.compare(left.item.label, right.item.label));
|
|
5823
|
+
return {
|
|
5824
|
+
...structuredClone(provider.group),
|
|
5825
|
+
items: shouldIncludeItems ? matches.slice(0, limit).map(({ item }) => structuredClone(item)) : [],
|
|
5826
|
+
total: matches.length
|
|
5827
|
+
};
|
|
5828
|
+
}))).filter((group) => !query || group.total > 0).sort((left, right) => left.order - right.order || collator.compare(left.label.default, right.label.default));
|
|
5829
|
+
return {
|
|
5830
|
+
groups: visibleGroups,
|
|
5831
|
+
total: visibleGroups.reduce((total, group) => total + group.total, 0)
|
|
5832
|
+
};
|
|
5833
|
+
};
|
|
5834
|
+
resolveReference = async (uri) => {
|
|
5835
|
+
const parsed = parseSystemObjectReferenceUri(uri);
|
|
5836
|
+
if (!parsed) throw new SystemObjectReferenceError("SYSTEM_OBJECT_INVALID_REFERENCE", `invalid system object reference: ${uri}`);
|
|
5837
|
+
const provider = this.providers.get(parsed.objectType);
|
|
5838
|
+
if (!provider) throw new SystemObjectReferenceError("SYSTEM_OBJECT_INVALID_REFERENCE", `system object provider is not registered: ${parsed.objectType}`);
|
|
5839
|
+
const source = await provider.resolve(parsed.objectId);
|
|
5840
|
+
if (!source) throw new SystemObjectReferenceError("SYSTEM_OBJECT_NOT_FOUND", `system object not found: ${uri}`);
|
|
5841
|
+
this.assertSnapshotContract(source, parsed);
|
|
5842
|
+
const bytes = new TextEncoder().encode(source.content);
|
|
5843
|
+
if (bytes.byteLength > MAX_SYSTEM_OBJECT_SNAPSHOT_BYTES) throw new SystemObjectReferenceError("SYSTEM_OBJECT_PROVIDER_CONTRACT", `system object snapshot exceeds ${MAX_SYSTEM_OBJECT_SNAPSHOT_BYTES} bytes: ${uri}`);
|
|
5844
|
+
const version = createHash("sha256").update(bytes).digest("hex");
|
|
5845
|
+
const cacheKey = `${uri}@${version}`;
|
|
5846
|
+
const cached = this.resolvedCache.get(cacheKey);
|
|
5847
|
+
if (cached && await this.assetStore.statRecord(cached.assetUri)) return structuredClone(cached);
|
|
5848
|
+
const asset = await this.assetStore.putBytes({
|
|
5849
|
+
bytes,
|
|
5850
|
+
fileName: source.fileName,
|
|
5851
|
+
mimeType: source.mimeType
|
|
5852
|
+
});
|
|
5853
|
+
const resolved = {
|
|
5854
|
+
...structuredClone(source.item),
|
|
5855
|
+
version,
|
|
5856
|
+
assetUri: asset.uri,
|
|
5857
|
+
fileName: asset.fileName,
|
|
5858
|
+
mimeType: asset.mimeType,
|
|
5859
|
+
sizeBytes: asset.sizeBytes
|
|
5860
|
+
};
|
|
5861
|
+
this.resolvedCache.set(cacheKey, resolved);
|
|
5862
|
+
return structuredClone(resolved);
|
|
5863
|
+
};
|
|
5864
|
+
assertSnapshotContract = (source, parsed) => {
|
|
5865
|
+
const expectedUri = createSystemObjectReferenceUri(parsed.objectType, parsed.objectId);
|
|
5866
|
+
if (source.item.uri !== expectedUri || source.item.objectType !== parsed.objectType || source.item.objectId !== parsed.objectId || !source.item.label.trim() || !source.fileName.trim() || !source.content.trim() || !isTextLikeAsset({
|
|
5867
|
+
mimeType: source.mimeType,
|
|
5868
|
+
fileName: source.fileName
|
|
5869
|
+
})) throw new SystemObjectReferenceError("SYSTEM_OBJECT_PROVIDER_CONTRACT", `system object provider returned an invalid snapshot: ${expectedUri}`);
|
|
5870
|
+
};
|
|
5871
|
+
assertListItemContract = (item, objectType) => {
|
|
5872
|
+
const expectedUri = createSystemObjectReferenceUri(objectType, item.objectId);
|
|
5873
|
+
if (item.objectType !== objectType || item.uri !== expectedUri || !item.label.trim() || !item.updatedAt.trim()) throw new SystemObjectReferenceError("SYSTEM_OBJECT_PROVIDER_CONTRACT", `system object provider returned an invalid list item: ${item.uri}`);
|
|
5874
|
+
};
|
|
5875
|
+
};
|
|
5876
|
+
function createInboxDeliverySystemObjectProvider(manager) {
|
|
5877
|
+
const toItem = (delivery) => ({
|
|
5878
|
+
uri: createSystemObjectReferenceUri(SYSTEM_OBJECT_TYPE_INBOX_DELIVERY, delivery.id),
|
|
5879
|
+
objectType: SYSTEM_OBJECT_TYPE_INBOX_DELIVERY,
|
|
5880
|
+
objectId: delivery.id,
|
|
5881
|
+
label: delivery.title,
|
|
5882
|
+
description: truncate(delivery.summary),
|
|
5883
|
+
updatedAt: delivery.updatedAt
|
|
5884
|
+
});
|
|
5885
|
+
return {
|
|
5886
|
+
group: {
|
|
5887
|
+
objectType: SYSTEM_OBJECT_TYPE_INBOX_DELIVERY,
|
|
5888
|
+
label: {
|
|
5889
|
+
default: "Inbox Reports",
|
|
5890
|
+
translations: {
|
|
5891
|
+
en: "Inbox Reports",
|
|
5892
|
+
zh: "收件箱报告"
|
|
5893
|
+
}
|
|
5894
|
+
},
|
|
5895
|
+
description: {
|
|
5896
|
+
default: "Reference reports and delivered content saved by NextClaw.",
|
|
5897
|
+
translations: {
|
|
5898
|
+
en: "Reference reports and delivered content saved by NextClaw.",
|
|
5899
|
+
zh: "引用由 NextClaw 保存的报告和送达内容。"
|
|
5900
|
+
}
|
|
5901
|
+
},
|
|
5902
|
+
icon: "inbox",
|
|
5903
|
+
order: 100
|
|
5904
|
+
},
|
|
5905
|
+
list: async () => (await manager.listDeliveries()).deliveries.map((delivery) => toItem(delivery)),
|
|
5906
|
+
resolve: async (objectId) => {
|
|
5907
|
+
const delivery = await manager.getDelivery(objectId);
|
|
5908
|
+
if (!delivery) return null;
|
|
5909
|
+
const sourceLines = [
|
|
5910
|
+
`# ${delivery.title}`,
|
|
5911
|
+
"",
|
|
5912
|
+
`- Object: ${createSystemObjectReferenceUri(SYSTEM_OBJECT_TYPE_INBOX_DELIVERY, delivery.id)}`,
|
|
5913
|
+
`- Created: ${delivery.createdAt}`,
|
|
5914
|
+
`- Source agent: ${delivery.source.agentId ?? "unknown"}`
|
|
5915
|
+
];
|
|
5916
|
+
if (delivery.summary) sourceLines.push("", "## Summary", "", delivery.summary);
|
|
5917
|
+
sourceLines.push("", "## Content", "", delivery.content);
|
|
5918
|
+
return {
|
|
5919
|
+
item: toItem(delivery),
|
|
5920
|
+
content: sourceLines.join("\n"),
|
|
5921
|
+
fileName: safeSnapshotFileName(delivery.title, "md"),
|
|
5922
|
+
mimeType: "text/markdown"
|
|
5923
|
+
};
|
|
5924
|
+
}
|
|
5925
|
+
};
|
|
5926
|
+
}
|
|
5927
|
+
function createCronJobSystemObjectProvider(automation) {
|
|
5928
|
+
const toItem = (job) => ({
|
|
5929
|
+
uri: createSystemObjectReferenceUri(SYSTEM_OBJECT_TYPE_CRON_JOB, job.id),
|
|
5930
|
+
objectType: SYSTEM_OBJECT_TYPE_CRON_JOB,
|
|
5931
|
+
objectId: job.id,
|
|
5932
|
+
label: job.name,
|
|
5933
|
+
description: truncate(job.payload.message),
|
|
5934
|
+
updatedAt: new Date(job.updatedAtMs).toISOString()
|
|
5935
|
+
});
|
|
5936
|
+
return {
|
|
5937
|
+
group: {
|
|
5938
|
+
objectType: SYSTEM_OBJECT_TYPE_CRON_JOB,
|
|
5939
|
+
label: {
|
|
5940
|
+
default: "Scheduled Tasks",
|
|
5941
|
+
translations: {
|
|
5942
|
+
en: "Scheduled Tasks",
|
|
5943
|
+
zh: "定时任务"
|
|
5944
|
+
}
|
|
5945
|
+
},
|
|
5946
|
+
description: {
|
|
5947
|
+
default: "Reference task schedules, instructions, and recent run state.",
|
|
5948
|
+
translations: {
|
|
5949
|
+
en: "Reference task schedules, instructions, and recent run state.",
|
|
5950
|
+
zh: "引用任务计划、指令和最近运行状态。"
|
|
5951
|
+
}
|
|
5952
|
+
},
|
|
5953
|
+
icon: "calendar-clock",
|
|
5954
|
+
order: 200
|
|
5955
|
+
},
|
|
5956
|
+
list: () => automation.listJobs(true).map(toItem),
|
|
5957
|
+
resolve: (objectId) => {
|
|
5958
|
+
const job = automation.listJobs(true).find(({ id }) => id === objectId);
|
|
5959
|
+
if (!job) return null;
|
|
5960
|
+
const content = [
|
|
5961
|
+
`# ${job.name}`,
|
|
5962
|
+
"",
|
|
5963
|
+
`- Object: ${createSystemObjectReferenceUri(SYSTEM_OBJECT_TYPE_CRON_JOB, job.id)}`,
|
|
5964
|
+
`- Enabled: ${job.enabled ? "yes" : "no"}`,
|
|
5965
|
+
`- Delete after run: ${job.deleteAfterRun ? "yes" : "no"}`,
|
|
5966
|
+
`- Created: ${new Date(job.createdAtMs).toISOString()}`,
|
|
5967
|
+
`- Updated: ${new Date(job.updatedAtMs).toISOString()}`,
|
|
5968
|
+
`- Next run: ${toIsoTime(job.state.nextRunAtMs) ?? "none"}`,
|
|
5969
|
+
`- Last run: ${toIsoTime(job.state.lastRunAtMs) ?? "none"}`,
|
|
5970
|
+
`- Last status: ${job.state.lastStatus ?? "none"}`,
|
|
5971
|
+
"",
|
|
5972
|
+
"## Schedule",
|
|
5973
|
+
"",
|
|
5974
|
+
"```json",
|
|
5975
|
+
JSON.stringify(job.schedule, null, 2),
|
|
5976
|
+
"```",
|
|
5977
|
+
"",
|
|
5978
|
+
"## Payload",
|
|
5979
|
+
"",
|
|
5980
|
+
job.payload.message,
|
|
5981
|
+
"",
|
|
5982
|
+
"```json",
|
|
5983
|
+
JSON.stringify({
|
|
5984
|
+
kind: job.payload.kind ?? "agent_turn",
|
|
5985
|
+
agentId: job.payload.agentId ?? null,
|
|
5986
|
+
sessionId: job.payload.sessionId ?? null
|
|
5987
|
+
}, null, 2),
|
|
5988
|
+
"```",
|
|
5989
|
+
...job.state.lastError ? [
|
|
5990
|
+
"",
|
|
5991
|
+
"## Last error",
|
|
5992
|
+
"",
|
|
5993
|
+
job.state.lastError
|
|
5994
|
+
] : []
|
|
5995
|
+
].join("\n");
|
|
5996
|
+
return {
|
|
5997
|
+
item: toItem(job),
|
|
5998
|
+
content,
|
|
5999
|
+
fileName: safeSnapshotFileName(job.name, "md"),
|
|
6000
|
+
mimeType: "text/markdown"
|
|
6001
|
+
};
|
|
6002
|
+
}
|
|
6003
|
+
};
|
|
6004
|
+
}
|
|
6005
|
+
//#endregion
|
|
5429
6006
|
//#region src/managers/mcp.manager.ts
|
|
5430
6007
|
var McpManager = class {
|
|
5431
6008
|
currentMcpConfig;
|
|
@@ -5520,7 +6097,7 @@ function safeNcpSessionFilename(value) {
|
|
|
5520
6097
|
function normalizeNcpAgentId(agentId) {
|
|
5521
6098
|
return agentId?.trim().toLowerCase() || void 0;
|
|
5522
6099
|
}
|
|
5523
|
-
function isRecord$
|
|
6100
|
+
function isRecord$13(value) {
|
|
5524
6101
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5525
6102
|
}
|
|
5526
6103
|
function toIsoString(value, fallback) {
|
|
@@ -5650,7 +6227,7 @@ function mergeReplayCompletedToolResults(message, toolResultsByCallId) {
|
|
|
5650
6227
|
} : message;
|
|
5651
6228
|
}
|
|
5652
6229
|
function readLegacyContextCompactionMessageId(message) {
|
|
5653
|
-
const checkpoint = isRecord$
|
|
6230
|
+
const checkpoint = isRecord$13(message?.metadata?.checkpoint) ? message.metadata.checkpoint : null;
|
|
5654
6231
|
const checkpointId = typeof checkpoint?.id === "string" ? checkpoint.id : "";
|
|
5655
6232
|
const coveredCount = checkpoint?.coveredSessionMessageCount;
|
|
5656
6233
|
const legacyId = `${message?.sessionId}:service:context-compaction:${checkpointId}`;
|
|
@@ -5682,11 +6259,11 @@ function readReplayMessageId(event) {
|
|
|
5682
6259
|
return readMessageFromSummaryEvent(event)?.id ?? null;
|
|
5683
6260
|
}
|
|
5684
6261
|
function readEventSessionId$2(event) {
|
|
5685
|
-
const sessionId = ("payload" in event && isRecord$
|
|
6262
|
+
const sessionId = ("payload" in event && isRecord$13(event.payload) ? event.payload : null)?.sessionId;
|
|
5686
6263
|
return typeof sessionId === "string" ? sessionId : "";
|
|
5687
6264
|
}
|
|
5688
6265
|
function readReplayPayloadTimestamp(event) {
|
|
5689
|
-
const payload = "payload" in event && isRecord$
|
|
6266
|
+
const payload = "payload" in event && isRecord$13(event.payload) ? event.payload : null;
|
|
5690
6267
|
const timestamp = typeof payload?.timestamp === "string" ? payload.timestamp : "";
|
|
5691
6268
|
return Number.isFinite(Date.parse(timestamp)) ? new Date(timestamp).toISOString() : null;
|
|
5692
6269
|
}
|
|
@@ -6497,76 +7074,496 @@ function isPanelAppAgentCapability(value) {
|
|
|
6497
7074
|
return PANEL_APP_AGENT_CAPABILITIES.includes(value);
|
|
6498
7075
|
}
|
|
6499
7076
|
//#endregion
|
|
6500
|
-
//#region src/
|
|
6501
|
-
const
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
}
|
|
6511
|
-
issue = (params) => {
|
|
6512
|
-
const claims = {
|
|
6513
|
-
panelAppId: params.panelAppId,
|
|
6514
|
-
sourceName: params.sourceName,
|
|
6515
|
-
sourcePath: params.sourcePath,
|
|
6516
|
-
expiresAt: this.now() + this.ttlMs,
|
|
6517
|
-
nonce: randomBytes(12).toString("base64url")
|
|
6518
|
-
};
|
|
6519
|
-
const payload = Buffer.from(JSON.stringify(claims), "utf8").toString("base64url");
|
|
6520
|
-
return `${payload}.${this.sign(payload)}`;
|
|
7077
|
+
//#region src/utils/panel-app-manifest.utils.ts
|
|
7078
|
+
const PANEL_APP_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
7079
|
+
function parsePanelAppManifest(html) {
|
|
7080
|
+
return {
|
|
7081
|
+
...readHtmlTitle(html),
|
|
7082
|
+
...readStandardIcon(html),
|
|
7083
|
+
...readPanelAppMeta(html),
|
|
7084
|
+
capabilities: readPanelAppCapabilities(html),
|
|
7085
|
+
client: false,
|
|
7086
|
+
serviceActions: readPanelAppServiceActions(html)
|
|
6521
7087
|
};
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
7088
|
+
}
|
|
7089
|
+
function parsePanelAppFolderManifest(raw) {
|
|
7090
|
+
let parsed;
|
|
7091
|
+
try {
|
|
7092
|
+
parsed = JSON.parse(raw);
|
|
7093
|
+
} catch (error) {
|
|
7094
|
+
throw new Error(`panel-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
7095
|
+
}
|
|
7096
|
+
if (!isRecord$12(parsed)) throw new Error("panel-app.json must contain an object.");
|
|
7097
|
+
const id = readOptionalString$8(parsed, "id");
|
|
7098
|
+
if (id && !PANEL_APP_ID_PATTERN.test(id)) throw new Error("panel app id must be kebab-case.");
|
|
7099
|
+
return {
|
|
7100
|
+
id,
|
|
7101
|
+
title: readRequiredString$7(parsed, "title"),
|
|
7102
|
+
description: readOptionalString$8(parsed, "description"),
|
|
7103
|
+
icon: readOptionalString$8(parsed, "icon"),
|
|
7104
|
+
entry: readRequiredString$7(parsed, "entry"),
|
|
7105
|
+
capabilities: readStringArray$1(parsed.capabilities, "capabilities"),
|
|
7106
|
+
client: readOptionalBoolean$2(parsed, "client"),
|
|
7107
|
+
serviceActions: readStringArray$1(parsed.actions, "actions")
|
|
6528
7108
|
};
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
7109
|
+
}
|
|
7110
|
+
function readPanelAppMeta(html) {
|
|
7111
|
+
return {
|
|
7112
|
+
...readPanelAppMetaField(html, "title"),
|
|
7113
|
+
...readPanelAppMetaField(html, "description"),
|
|
7114
|
+
...readPanelAppMetaField(html, "icon")
|
|
6534
7115
|
};
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
7116
|
+
}
|
|
7117
|
+
function readPanelAppMetaField(html, field) {
|
|
7118
|
+
const content = readMetaContent(html, `nextclaw-panel-${field}`, field === "icon" ? "attribute" : "text");
|
|
7119
|
+
const manifest = {};
|
|
7120
|
+
if (content) manifest[field] = content;
|
|
7121
|
+
return manifest;
|
|
7122
|
+
}
|
|
7123
|
+
function readHtmlTitle(html) {
|
|
7124
|
+
const title = normalizeTextValue(html.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1]);
|
|
7125
|
+
return title ? { title } : {};
|
|
7126
|
+
}
|
|
7127
|
+
function readStandardIcon(html) {
|
|
7128
|
+
const icon = readLinkHref(html, (relTokens) => relTokens.includes("icon"));
|
|
7129
|
+
const appleTouchIcon = readLinkHref(html, (relTokens) => relTokens.some((token) => token === "apple-touch-icon" || token === "apple-touch-icon-precomposed"));
|
|
7130
|
+
const href = normalizeIconHref(icon ?? appleTouchIcon);
|
|
7131
|
+
return href ? { icon: href } : {};
|
|
7132
|
+
}
|
|
7133
|
+
function readMetaContent(html, name, valueKind) {
|
|
7134
|
+
const metaTags = html.matchAll(/<meta\s+([^>]*?)>/gi);
|
|
7135
|
+
for (const tag of metaTags) {
|
|
7136
|
+
const attributes = tag[1] ?? "";
|
|
7137
|
+
if (readHtmlAttribute(attributes, "name") === name) {
|
|
7138
|
+
const content = readHtmlAttribute(attributes, "content");
|
|
7139
|
+
return valueKind === "attribute" ? normalizeAttributeValue(content) : normalizeTextValue(content);
|
|
6542
7140
|
}
|
|
6543
|
-
}
|
|
6544
|
-
};
|
|
6545
|
-
function isPanelAppAssetTokenClaims(value) {
|
|
6546
|
-
return typeof value === "object" && value !== null && "panelAppId" in value && "sourceName" in value && "sourcePath" in value && "expiresAt" in value && "nonce" in value && typeof value.panelAppId === "string" && typeof value.sourceName === "string" && typeof value.sourcePath === "string" && typeof value.expiresAt === "number" && typeof value.nonce === "string";
|
|
7141
|
+
}
|
|
6547
7142
|
}
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
const
|
|
6551
|
-
const
|
|
6552
|
-
|
|
6553
|
-
constructor(panelsPath) {
|
|
6554
|
-
this.panelsPath = panelsPath;
|
|
7143
|
+
function readLinkHref(html, matchesRel) {
|
|
7144
|
+
const linkTags = html.matchAll(/<link\s+([^>]*?)>/gi);
|
|
7145
|
+
for (const tag of linkTags) {
|
|
7146
|
+
const attributes = tag[1] ?? "";
|
|
7147
|
+
if (matchesRel((readHtmlAttribute(attributes, "rel") ?? "").toLowerCase().split(/\s+/).filter(Boolean))) return normalizeAttributeValue(readHtmlAttribute(attributes, "href"));
|
|
6555
7148
|
}
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
7149
|
+
}
|
|
7150
|
+
function readHtmlAttribute(attributes, attribute) {
|
|
7151
|
+
const match = attributes.match(new RegExp(`(?:^|\\s)${attribute}\\s*=\\s*(["'])(.*?)\\1`, "i"));
|
|
7152
|
+
return match?.[2] ? decodeHtmlAttribute(match[2]) : void 0;
|
|
7153
|
+
}
|
|
7154
|
+
function readPanelAppServiceActions(html) {
|
|
7155
|
+
return parseTokenList(readMetaContent(html, "nextclaw-panel-actions", "attribute"));
|
|
7156
|
+
}
|
|
7157
|
+
function readPanelAppCapabilities(html) {
|
|
7158
|
+
return parseTokenList(readMetaContent(html, "nextclaw-panel-capabilities", "attribute"));
|
|
7159
|
+
}
|
|
7160
|
+
function parseTokenList(content) {
|
|
7161
|
+
if (!content) return [];
|
|
7162
|
+
return [...new Set(content.split(/[,\s]+/).map((entry) => entry.trim()).filter(Boolean))];
|
|
7163
|
+
}
|
|
7164
|
+
function readRequiredString$7(record, key) {
|
|
7165
|
+
const value = readOptionalString$8(record, key);
|
|
7166
|
+
if (!value) throw new Error(`panel app ${key} is required.`);
|
|
7167
|
+
return value;
|
|
7168
|
+
}
|
|
7169
|
+
function readOptionalString$8(record, key) {
|
|
7170
|
+
return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
|
|
7171
|
+
}
|
|
7172
|
+
function readOptionalBoolean$2(record, key) {
|
|
7173
|
+
const value = record[key];
|
|
7174
|
+
if (value === void 0) return false;
|
|
7175
|
+
if (typeof value !== "boolean") throw new Error(`panel app ${key} must be a boolean.`);
|
|
7176
|
+
return value;
|
|
7177
|
+
}
|
|
7178
|
+
function readStringArray$1(value, key) {
|
|
7179
|
+
if (value === void 0) return [];
|
|
7180
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`panel app ${key} must be a string array.`);
|
|
7181
|
+
return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))];
|
|
7182
|
+
}
|
|
7183
|
+
function isRecord$12(value) {
|
|
7184
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7185
|
+
}
|
|
7186
|
+
function normalizeTextValue(value) {
|
|
7187
|
+
return decodeHtmlAttribute(value ?? "").replace(/\s+/g, " ").trim() || void 0;
|
|
7188
|
+
}
|
|
7189
|
+
function normalizeAttributeValue(value) {
|
|
7190
|
+
return decodeHtmlAttribute(value ?? "").trim() || void 0;
|
|
7191
|
+
}
|
|
7192
|
+
function normalizeIconHref(value) {
|
|
7193
|
+
if (!value) return;
|
|
7194
|
+
if (value.startsWith("data:image/") || value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/")) return value;
|
|
7195
|
+
}
|
|
7196
|
+
function decodeHtmlAttribute(value) {
|
|
7197
|
+
return value.replace(/"/g, "\"").replace(/"/g, "\"").replace(/'/g, "'").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
7198
|
+
}
|
|
7199
|
+
const PANEL_APP_FOLDER_MANIFEST_FILE_NAME = "panel-app.json";
|
|
7200
|
+
function isPanelAppSourceEntry(entry) {
|
|
7201
|
+
return entry.isFile() && isPanelAppFileName(entry.name) || entry.isDirectory() && isPanelAppDirName(entry.name);
|
|
7202
|
+
}
|
|
7203
|
+
function isPanelAppFileName(fileName) {
|
|
7204
|
+
return hasSafeBaseName(fileName) && fileName.endsWith(".panel.html");
|
|
7205
|
+
}
|
|
7206
|
+
function isPanelAppDirName(dirName) {
|
|
7207
|
+
return hasSafeBaseName(dirName) && dirName.endsWith(".panel");
|
|
7208
|
+
}
|
|
7209
|
+
function toPanelAppTitle(sourceName) {
|
|
7210
|
+
return sourceName.replace(new RegExp(`${escapeRegExp(".panel.html")}$`), "").replace(new RegExp(`${escapeRegExp(".panel")}$`), "").replace(/[-_]+/g, " ").trim() || sourceName;
|
|
7211
|
+
}
|
|
7212
|
+
function encodePanelAppId(sourceName) {
|
|
7213
|
+
return Buffer.from(sourceName, "utf8").toString("base64url");
|
|
7214
|
+
}
|
|
7215
|
+
function decodePanelAppId(id) {
|
|
7216
|
+
const normalizedId = id.trim();
|
|
7217
|
+
if (!normalizedId) throw new PanelAppError("PANEL_APP_INVALID_ID", "panel app id is required");
|
|
7218
|
+
let sourceName = "";
|
|
7219
|
+
try {
|
|
7220
|
+
sourceName = Buffer.from(normalizedId, "base64url").toString("utf8");
|
|
7221
|
+
} catch {
|
|
7222
|
+
throw new PanelAppError("PANEL_APP_INVALID_ID", "invalid panel app id");
|
|
7223
|
+
}
|
|
7224
|
+
if (encodePanelAppId(sourceName) !== normalizedId || !isPanelAppFileName(sourceName) && !isPanelAppDirName(sourceName)) throw new PanelAppError("PANEL_APP_INVALID_ID", "invalid panel app id");
|
|
7225
|
+
return sourceName;
|
|
7226
|
+
}
|
|
7227
|
+
async function readPanelAppFolderManifest(dirPath, dirName) {
|
|
7228
|
+
const manifest = parsePanelAppFolderManifest(await readFile(join(dirPath, PANEL_APP_FOLDER_MANIFEST_FILE_NAME), "utf8"));
|
|
7229
|
+
const expectedId = dirName.slice(0, -6);
|
|
7230
|
+
if (manifest.id && manifest.id !== expectedId) throw new PanelAppError("PANEL_APP_MANIFEST_INVALID", "panel app manifest id must match the directory name");
|
|
7231
|
+
return {
|
|
7232
|
+
...manifest,
|
|
7233
|
+
id: expectedId
|
|
7234
|
+
};
|
|
7235
|
+
}
|
|
7236
|
+
function resolvePanelAppRelativePath(rootPath, relativePath) {
|
|
7237
|
+
if (!relativePath.trim() || relativePath.includes("\0") || isAbsolute(relativePath)) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
7238
|
+
const normalizedPath = normalize(relativePath);
|
|
7239
|
+
if (normalizedPath === "." || normalizedPath.startsWith("..")) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
7240
|
+
const resolvedPath = resolve(rootPath, normalizedPath);
|
|
7241
|
+
const pathFromRoot = relative(resolve(rootPath), resolvedPath);
|
|
7242
|
+
if (pathFromRoot.startsWith("..") || isAbsolute(pathFromRoot)) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
7243
|
+
return resolvedPath;
|
|
7244
|
+
}
|
|
7245
|
+
function resolvePanelAppAssetContentType(path) {
|
|
7246
|
+
switch (extname(path).toLowerCase()) {
|
|
7247
|
+
case ".css": return "text/css; charset=utf-8";
|
|
7248
|
+
case ".js":
|
|
7249
|
+
case ".mjs": return "application/javascript; charset=utf-8";
|
|
7250
|
+
case ".json": return "application/json; charset=utf-8";
|
|
7251
|
+
case ".png": return "image/png";
|
|
7252
|
+
case ".svg": return "image/svg+xml; charset=utf-8";
|
|
7253
|
+
case ".webp": return "image/webp";
|
|
7254
|
+
case ".txt": return "text/plain; charset=utf-8";
|
|
7255
|
+
default: return "application/octet-stream";
|
|
7256
|
+
}
|
|
7257
|
+
}
|
|
7258
|
+
function resolvePanelAppIconUrl(id, icon) {
|
|
7259
|
+
if (!icon) return;
|
|
7260
|
+
if (icon.startsWith("data:image/") || icon.startsWith("http://") || icon.startsWith("https://") || icon.startsWith("/") || isLikelyTextIcon(icon)) return icon;
|
|
7261
|
+
return `/api/panel-apps/${encodeURIComponent(id)}/assets/${encodePanelAppAssetPath(icon)}`;
|
|
7262
|
+
}
|
|
7263
|
+
function injectPanelAppAssetBase(html, baseHref) {
|
|
7264
|
+
const base = `<base href="${baseHref}">`;
|
|
7265
|
+
return injectLocalScriptCrossOrigin((() => {
|
|
7266
|
+
if (/<base\b/i.test(html)) return html;
|
|
7267
|
+
if (/<head[^>]*>/i.test(html)) return html.replace(/<head[^>]*>/i, (head) => `${head}${base}`);
|
|
7268
|
+
return `${base}${html}`;
|
|
7269
|
+
})());
|
|
7270
|
+
}
|
|
7271
|
+
function injectLocalScriptCrossOrigin(html) {
|
|
7272
|
+
return html.replace(/<script\b(?=[^>]*\bsrc\s*=)(?![^>]*\bcrossorigin\b)[^>]*>/gi, (tag) => {
|
|
7273
|
+
const src = extractScriptSrc(tag);
|
|
7274
|
+
if (!src || !isLocalScriptSrc(src)) return tag;
|
|
7275
|
+
return `${tag.slice(0, -1)} crossorigin="anonymous">`;
|
|
7276
|
+
});
|
|
7277
|
+
}
|
|
7278
|
+
function extractScriptSrc(tag) {
|
|
7279
|
+
const match = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tag);
|
|
7280
|
+
return match?.[1] ?? match?.[2] ?? match?.[3];
|
|
7281
|
+
}
|
|
7282
|
+
function isLocalScriptSrc(src) {
|
|
7283
|
+
const value = src.trim();
|
|
7284
|
+
if (!value || value.startsWith("//")) return false;
|
|
7285
|
+
return !/^(?:https?|data|blob|javascript):/i.test(value);
|
|
7286
|
+
}
|
|
7287
|
+
function encodePanelAppAssetPath(path) {
|
|
7288
|
+
return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
7289
|
+
}
|
|
7290
|
+
function hasSafeBaseName(name) {
|
|
7291
|
+
return !name.includes("/") && !name.includes("\\") && !name.includes("\0");
|
|
7292
|
+
}
|
|
7293
|
+
function isLikelyTextIcon(icon) {
|
|
7294
|
+
return !icon.includes("/") && !icon.includes(".") && icon.length <= 4;
|
|
7295
|
+
}
|
|
7296
|
+
function escapeRegExp(value) {
|
|
7297
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7298
|
+
}
|
|
7299
|
+
//#endregion
|
|
7300
|
+
//#region src/utils/panel-app-content-source.utils.ts
|
|
7301
|
+
async function readPanelAppContentSource(params) {
|
|
7302
|
+
const { createAssetBaseHref, id, panelsPath, sourceService } = params;
|
|
7303
|
+
return await readResolvedPanelAppContentSource(await sourceService.resolveSource(panelsPath, id), createAssetBaseHref);
|
|
7304
|
+
}
|
|
7305
|
+
async function readPanelAppContentSourceByPath(params) {
|
|
7306
|
+
const { createAssetBaseHref, path, sourceService } = params;
|
|
7307
|
+
return await readResolvedPanelAppContentSource(await sourceService.resolveSourcePath(path), createAssetBaseHref);
|
|
7308
|
+
}
|
|
7309
|
+
async function readPanelAppContentSourceByIdOrPath(params) {
|
|
7310
|
+
const { sourcePath, ...standardSourceParams } = params;
|
|
7311
|
+
return sourcePath ? await readPanelAppContentSourceByPath({
|
|
7312
|
+
createAssetBaseHref: params.createAssetBaseHref,
|
|
7313
|
+
path: sourcePath,
|
|
7314
|
+
sourceService: params.sourceService
|
|
7315
|
+
}) : await readPanelAppContentSource(standardSourceParams);
|
|
7316
|
+
}
|
|
7317
|
+
async function readResolvedPanelAppContentSource(source, createAssetBaseHref) {
|
|
7318
|
+
const html = await readFile(source.entryPath, "utf8");
|
|
7319
|
+
const manifest = source.manifest ?? parsePanelAppManifest(html);
|
|
7320
|
+
const sourceId = encodePanelAppId(source.sourceName);
|
|
7321
|
+
return {
|
|
7322
|
+
appId: resolvePanelAppAppId(source, manifest),
|
|
7323
|
+
sourceId,
|
|
7324
|
+
source,
|
|
7325
|
+
manifest,
|
|
7326
|
+
html,
|
|
7327
|
+
htmlWithBase: source.kind === "folder" ? injectPanelAppAssetBase(html, createAssetBaseHref(source)) : html
|
|
7328
|
+
};
|
|
7329
|
+
}
|
|
7330
|
+
async function readPanelAppContentSourceByIdOrAppId(params) {
|
|
7331
|
+
const { appIdOrSourceId, createAssetBaseHref, panelsPath, sourceService } = params;
|
|
7332
|
+
try {
|
|
7333
|
+
return await readPanelAppContentSource({
|
|
7334
|
+
createAssetBaseHref,
|
|
7335
|
+
id: appIdOrSourceId,
|
|
7336
|
+
panelsPath,
|
|
7337
|
+
sourceService
|
|
7338
|
+
});
|
|
7339
|
+
} catch (error) {
|
|
7340
|
+
if (!isPanelAppError(error)) throw error;
|
|
7341
|
+
if (error.code !== "PANEL_APP_INVALID_ID" && error.code !== "PANEL_APP_NOT_FOUND") throw error;
|
|
7342
|
+
}
|
|
7343
|
+
const sources = await sourceService.listSources(panelsPath);
|
|
7344
|
+
for (const source of sources) {
|
|
7345
|
+
const html = await readFile(source.entryPath, "utf8");
|
|
7346
|
+
if (resolvePanelAppAppId(source, source.manifest ?? parsePanelAppManifest(html)) === appIdOrSourceId) return await readPanelAppContentSource({
|
|
7347
|
+
createAssetBaseHref,
|
|
7348
|
+
id: encodePanelAppId(source.sourceName),
|
|
7349
|
+
panelsPath,
|
|
7350
|
+
sourceService
|
|
7351
|
+
});
|
|
7352
|
+
}
|
|
7353
|
+
throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
7354
|
+
}
|
|
7355
|
+
function resolvePanelAppAppId(source, manifest) {
|
|
7356
|
+
return manifest.id ?? encodePanelAppId(source.sourceName);
|
|
7357
|
+
}
|
|
7358
|
+
//#endregion
|
|
7359
|
+
//#region src/managers/panel-app-package-state.manager.ts
|
|
7360
|
+
var PanelAppPackageStateManager = class {
|
|
7361
|
+
constructor(params) {
|
|
7362
|
+
this.params = params;
|
|
7363
|
+
}
|
|
7364
|
+
listSources = async () => {
|
|
7365
|
+
const panelsPath = this.params.getPanelsPath();
|
|
7366
|
+
const workspaceSources = await this.params.sourceService.listSources(panelsPath);
|
|
7367
|
+
const packageSources = (await this.listPackageComponentSources()).filter((component) => component.kind === "panel");
|
|
7368
|
+
const resolvedPackageSources = await Promise.all(packageSources.map(async (packageSource) => ({
|
|
7369
|
+
source: await this.params.sourceService.resolveSourcePath(packageSource.sourcePath),
|
|
7370
|
+
packageSource
|
|
7371
|
+
})));
|
|
7372
|
+
return [...workspaceSources.map((source) => ({ source })), ...resolvedPackageSources];
|
|
7373
|
+
};
|
|
7374
|
+
resolveSource = async (id) => {
|
|
7375
|
+
try {
|
|
7376
|
+
return await this.params.sourceService.resolveSource(this.params.getPanelsPath(), id);
|
|
7377
|
+
} catch (error) {
|
|
7378
|
+
if (!isPanelAppError(error) || error.code !== "PANEL_APP_NOT_FOUND") throw error;
|
|
7379
|
+
}
|
|
7380
|
+
const match = (await this.listSources()).find(({ source, packageSource }) => packageSource && (encodePanelAppId(source.sourceName) === id || packageSource.id === id));
|
|
7381
|
+
if (!match) throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
7382
|
+
return match.source;
|
|
7383
|
+
};
|
|
7384
|
+
findPackageSourceBySourceName = async (sourceName) => {
|
|
7385
|
+
return (await this.listPackageComponentSources()).find((component) => component.kind === "panel" && component.sourcePath.endsWith(`/${sourceName}`));
|
|
7386
|
+
};
|
|
7387
|
+
readContentSourceByIdOrAppId = async (id) => {
|
|
7388
|
+
const panelsPath = this.params.getPanelsPath();
|
|
7389
|
+
try {
|
|
7390
|
+
return await readPanelAppContentSourceByIdOrAppId({
|
|
7391
|
+
appIdOrSourceId: id,
|
|
7392
|
+
createAssetBaseHref: this.params.createAssetBaseHref,
|
|
7393
|
+
panelsPath,
|
|
7394
|
+
sourceService: this.params.sourceService
|
|
7395
|
+
});
|
|
7396
|
+
} catch (error) {
|
|
7397
|
+
if (!isPanelAppError(error) || error.code !== "PANEL_APP_NOT_FOUND") throw error;
|
|
7398
|
+
}
|
|
7399
|
+
const packageSource = (await this.listPackageComponentSources()).find((component) => component.kind === "panel" && component.id === id);
|
|
7400
|
+
if (!packageSource) throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
7401
|
+
return await readPanelAppContentSourceByIdOrPath({
|
|
7402
|
+
createAssetBaseHref: this.params.createAssetBaseHref,
|
|
7403
|
+
id,
|
|
7404
|
+
panelsPath,
|
|
7405
|
+
sourcePath: packageSource.sourcePath,
|
|
7406
|
+
sourceService: this.params.sourceService
|
|
7407
|
+
});
|
|
7408
|
+
};
|
|
7409
|
+
assertDeclaresClient = async (appId) => {
|
|
7410
|
+
const sources = await this.listSources();
|
|
7411
|
+
for (const { source } of sources) {
|
|
7412
|
+
const manifest = source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8"));
|
|
7413
|
+
if (resolvePanelAppAppId(source, manifest) === appId) {
|
|
7414
|
+
if (!manifest.client) throw new PanelAppError("PANEL_APP_CLIENT_NOT_DECLARED", "panel app did not declare client access");
|
|
7415
|
+
return;
|
|
7416
|
+
}
|
|
7417
|
+
}
|
|
7418
|
+
throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
7419
|
+
};
|
|
7420
|
+
assertCanActivate = async (components) => {
|
|
7421
|
+
const panelComponents = components.filter((component) => component.kind === "panel");
|
|
7422
|
+
if (panelComponents.length === 0) return;
|
|
7423
|
+
const workspaceSources = await this.params.sourceService.listSources(this.params.getPanelsPath());
|
|
7424
|
+
const workspaceIds = /* @__PURE__ */ new Set();
|
|
7425
|
+
for (const source of workspaceSources) {
|
|
7426
|
+
const manifest = source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8"));
|
|
7427
|
+
workspaceIds.add(resolvePanelAppAppId(source, manifest));
|
|
7428
|
+
}
|
|
7429
|
+
const activePackageSources = await this.listPackageComponentSources();
|
|
7430
|
+
for (const component of panelComponents) {
|
|
7431
|
+
const conflictsWithPackage = activePackageSources.some((active) => active.kind === "panel" && active.id === component.id && active.packageId !== component.packageId);
|
|
7432
|
+
if (workspaceIds.has(component.id) || conflictsWithPackage) throw new AppPackageError("APP_PACKAGE_CONFLICT", `Panel component id 冲突:${component.id}`);
|
|
7433
|
+
}
|
|
7434
|
+
};
|
|
7435
|
+
deactivate = (components) => {
|
|
7436
|
+
for (const component of components) if (component.kind === "panel") this.params.deleteBridgeSessions(component.id);
|
|
7437
|
+
};
|
|
7438
|
+
removeState = async (components) => {
|
|
7439
|
+
const panelsPath = this.params.getPanelsPath();
|
|
7440
|
+
for (const component of components) {
|
|
7441
|
+
if (component.kind !== "panel") continue;
|
|
7442
|
+
this.params.deleteBridgeSessions(component.id);
|
|
7443
|
+
await this.params.createStateStore(panelsPath).deleteEntry(encodePanelAppId(basename(component.sourcePath)));
|
|
7444
|
+
await this.params.createCapabilityGrantStore().deleteCaller({
|
|
7445
|
+
surface: "panel-app",
|
|
7446
|
+
appId: component.id
|
|
7447
|
+
});
|
|
7448
|
+
await this.params.createClientGrantStore().revoke(component.id);
|
|
7449
|
+
}
|
|
7450
|
+
};
|
|
7451
|
+
listPackageComponentSources = async () => await this.params.listPackageComponentSources?.() ?? [];
|
|
7452
|
+
};
|
|
7453
|
+
//#endregion
|
|
7454
|
+
//#region src/utils/panel-app-time.utils.ts
|
|
7455
|
+
function resolvePanelAppCreatedAt(fileStat) {
|
|
7456
|
+
return fileStat.birthtimeMs > 0 ? fileStat.birthtime.toISOString() : fileStat.mtime.toISOString();
|
|
7457
|
+
}
|
|
7458
|
+
function resolvePanelAppActivityMs(entry) {
|
|
7459
|
+
return Math.max(new Date(entry.lastOpenedAt ?? 0).getTime(), new Date(entry.createdAt).getTime(), new Date(entry.updatedAt).getTime());
|
|
7460
|
+
}
|
|
7461
|
+
//#endregion
|
|
7462
|
+
//#region src/presenters/panel-app-entry.presenter.ts
|
|
7463
|
+
var PanelAppEntryPresenter = class {
|
|
7464
|
+
constructor(params) {
|
|
7465
|
+
this.params = params;
|
|
7466
|
+
}
|
|
7467
|
+
build = async (source, state, packageSource) => {
|
|
7468
|
+
const manifest = source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8"));
|
|
7469
|
+
const id = encodePanelAppId(source.sourceName);
|
|
7470
|
+
const appId = resolvePanelAppAppId(source, manifest);
|
|
7471
|
+
const entry = {
|
|
7472
|
+
id,
|
|
7473
|
+
appId,
|
|
7474
|
+
fileName: source.sourceName,
|
|
7475
|
+
kind: source.kind,
|
|
7476
|
+
title: manifest.title ?? toPanelAppTitle(source.sourceName),
|
|
7477
|
+
contentPath: packageSource ? `${this.params.contentBasePath}/${encodeURIComponent(id)}/content?${new URLSearchParams({ path: source.sourcePath })}` : `${this.params.contentBasePath}/${encodeURIComponent(id)}/content`,
|
|
7478
|
+
createdAt: resolvePanelAppCreatedAt(source.sourceStat),
|
|
7479
|
+
updatedAt: source.sourceStat.mtime.toISOString(),
|
|
7480
|
+
sizeBytes: source.sourceStat.size,
|
|
7481
|
+
favorite: state.favorite ?? false,
|
|
7482
|
+
clientDeclared: manifest.client,
|
|
7483
|
+
clientGranted: await this.params.isClientGranted(appId, manifest.client),
|
|
7484
|
+
openCount: state.openCount ?? 0,
|
|
7485
|
+
sourceKind: packageSource ? "package" : "workspace",
|
|
7486
|
+
packageId: packageSource?.packageId,
|
|
7487
|
+
packageVersion: packageSource?.packageVersion
|
|
7488
|
+
};
|
|
7489
|
+
if (manifest.description) entry.description = manifest.description;
|
|
7490
|
+
if (manifest.icon) entry.icon = source.kind === "folder" ? packageSource ? `${this.params.createAssetBaseHref(source)}${manifest.icon.replace(/^\/+/, "")}` : resolvePanelAppIconUrl(id, manifest.icon) : manifest.icon;
|
|
7491
|
+
if (state.lastOpenedAt) entry.lastOpenedAt = state.lastOpenedAt;
|
|
7492
|
+
return entry;
|
|
7493
|
+
};
|
|
7494
|
+
compare = (left, right) => resolvePanelAppActivityMs(right) - resolvePanelAppActivityMs(left) || Number(right.favorite) - Number(left.favorite) || left.title.localeCompare(right.title);
|
|
7495
|
+
};
|
|
7496
|
+
//#endregion
|
|
7497
|
+
//#region src/services/panel-app-asset-token.service.ts
|
|
7498
|
+
const PANEL_APP_ASSET_TOKEN_TTL_MS = 7200 * 1e3;
|
|
7499
|
+
var PanelAppAssetTokenService = class {
|
|
7500
|
+
now;
|
|
7501
|
+
secret;
|
|
7502
|
+
ttlMs;
|
|
7503
|
+
constructor(params = {}) {
|
|
7504
|
+
this.now = params.now ?? Date.now;
|
|
7505
|
+
this.secret = params.secret ?? randomBytes(32);
|
|
7506
|
+
this.ttlMs = params.ttlMs ?? PANEL_APP_ASSET_TOKEN_TTL_MS;
|
|
7507
|
+
}
|
|
7508
|
+
issue = (params) => {
|
|
7509
|
+
const claims = {
|
|
7510
|
+
panelAppId: params.panelAppId,
|
|
7511
|
+
sourceName: params.sourceName,
|
|
7512
|
+
sourcePath: params.sourcePath,
|
|
7513
|
+
expiresAt: this.now() + this.ttlMs,
|
|
7514
|
+
nonce: randomBytes(12).toString("base64url")
|
|
7515
|
+
};
|
|
7516
|
+
const payload = Buffer.from(JSON.stringify(claims), "utf8").toString("base64url");
|
|
7517
|
+
return `${payload}.${this.sign(payload)}`;
|
|
7518
|
+
};
|
|
7519
|
+
verify = (token) => {
|
|
7520
|
+
const [payload, signature, ...rest] = token.trim().split(".");
|
|
7521
|
+
if (!payload || !signature || rest.length > 0 || !this.matchesSignature(payload, signature)) throw new PanelAppError("PANEL_APP_ASSET_TOKEN_INVALID", "invalid panel app asset token");
|
|
7522
|
+
const claims = this.parseClaims(payload);
|
|
7523
|
+
if (claims.expiresAt <= this.now()) throw new PanelAppError("PANEL_APP_ASSET_TOKEN_EXPIRED", "panel app asset token expired");
|
|
7524
|
+
return claims;
|
|
7525
|
+
};
|
|
7526
|
+
sign = (payload) => createHmac("sha256", this.secret).update(payload).digest("base64url");
|
|
7527
|
+
matchesSignature = (payload, signature) => {
|
|
7528
|
+
const expected = Buffer.from(this.sign(payload), "utf8");
|
|
7529
|
+
const actual = Buffer.from(signature, "utf8");
|
|
7530
|
+
return expected.length === actual.length && timingSafeEqual(expected, actual);
|
|
7531
|
+
};
|
|
7532
|
+
parseClaims = (payload) => {
|
|
7533
|
+
try {
|
|
7534
|
+
const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
7535
|
+
if (!isPanelAppAssetTokenClaims(claims)) throw new Error("invalid panel app asset token claims");
|
|
7536
|
+
return claims;
|
|
7537
|
+
} catch {
|
|
7538
|
+
throw new PanelAppError("PANEL_APP_ASSET_TOKEN_INVALID", "invalid panel app asset token");
|
|
7539
|
+
}
|
|
7540
|
+
};
|
|
7541
|
+
};
|
|
7542
|
+
function isPanelAppAssetTokenClaims(value) {
|
|
7543
|
+
return typeof value === "object" && value !== null && "panelAppId" in value && "sourceName" in value && "sourcePath" in value && "expiresAt" in value && "nonce" in value && typeof value.panelAppId === "string" && typeof value.sourceName === "string" && typeof value.sourcePath === "string" && typeof value.expiresAt === "number" && typeof value.nonce === "string";
|
|
7544
|
+
}
|
|
7545
|
+
//#endregion
|
|
7546
|
+
//#region src/stores/panel-app-state.store.ts
|
|
7547
|
+
const PANEL_APP_STATE_FILE = ".panel-apps.state.json";
|
|
7548
|
+
const PANEL_APP_STATE_VERSION = 1;
|
|
7549
|
+
var PanelAppStateStore = class {
|
|
7550
|
+
constructor(panelsPath) {
|
|
7551
|
+
this.panelsPath = panelsPath;
|
|
7552
|
+
}
|
|
7553
|
+
load = async () => {
|
|
7554
|
+
try {
|
|
7555
|
+
const parsed = JSON.parse(await readFile(this.getStatePath(), "utf8"));
|
|
7556
|
+
return this.normalizeStateFile(parsed).apps;
|
|
7557
|
+
} catch (error) {
|
|
7558
|
+
if (this.isMissingFileError(error) || error instanceof SyntaxError) return {};
|
|
7559
|
+
throw error;
|
|
7560
|
+
}
|
|
7561
|
+
};
|
|
7562
|
+
updatePreferences = async (id, preferences) => {
|
|
7563
|
+
const apps = await this.load();
|
|
7564
|
+
const next = { ...apps[id] ?? {} };
|
|
7565
|
+
if (typeof preferences.favorite === "boolean") next.favorite = preferences.favorite;
|
|
7566
|
+
apps[id] = next;
|
|
6570
7567
|
await this.persist(apps);
|
|
6571
7568
|
return next;
|
|
6572
7569
|
};
|
|
@@ -7659,262 +8656,39 @@ function injectPanelAppClientScript(html, params) {
|
|
|
7659
8656
|
return `${script}${html}`;
|
|
7660
8657
|
}
|
|
7661
8658
|
function getPanelAppClientInitScript(params) {
|
|
7662
|
-
return `
|
|
7663
|
-
(() => {
|
|
7664
|
-
const marker = "${PANEL_APP_CLIENT_MARKER}";
|
|
7665
|
-
if (typeof window.NextClawClient !== "function") {
|
|
7666
|
-
console.error("[NextClaw] Panel App client SDK failed to load.");
|
|
7667
|
-
return;
|
|
7668
|
-
}
|
|
7669
|
-
if (typeof window.createNextClawAppClient !== "function") {
|
|
7670
|
-
console.error("[NextClaw] Panel App client projection failed to load.");
|
|
7671
|
-
return;
|
|
7672
|
-
}
|
|
7673
|
-
const existing = window.nextclaw && typeof window.nextclaw === "object" ? window.nextclaw : {};
|
|
7674
|
-
const hostClient = new window.NextClawClient({
|
|
7675
|
-
baseUrl: window.location.origin,
|
|
7676
|
-
headers: {
|
|
7677
|
-
"x-nextclaw-panel-bridge-session": ${JSON.stringify(params.runtimeToken)}
|
|
7678
|
-
}
|
|
7679
|
-
});
|
|
7680
|
-
const client = window.createNextClawAppClient(hostClient);
|
|
7681
|
-
Object.defineProperty(window, "nextclaw", {
|
|
7682
|
-
configurable: true,
|
|
7683
|
-
value: {
|
|
7684
|
-
...existing,
|
|
7685
|
-
client
|
|
7686
|
-
}
|
|
7687
|
-
});
|
|
7688
|
-
Object.defineProperty(window.nextclaw, "__clientInitMarker", {
|
|
7689
|
-
configurable: true,
|
|
7690
|
-
enumerable: false,
|
|
7691
|
-
value: marker
|
|
7692
|
-
});
|
|
7693
|
-
})();
|
|
7694
|
-
`.trim();
|
|
7695
|
-
}
|
|
7696
|
-
//#endregion
|
|
7697
|
-
//#region src/utils/panel-app-manifest.utils.ts
|
|
7698
|
-
const PANEL_APP_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
7699
|
-
function parsePanelAppManifest(html) {
|
|
7700
|
-
return {
|
|
7701
|
-
...readHtmlTitle(html),
|
|
7702
|
-
...readStandardIcon(html),
|
|
7703
|
-
...readPanelAppMeta(html),
|
|
7704
|
-
capabilities: readPanelAppCapabilities(html),
|
|
7705
|
-
client: false,
|
|
7706
|
-
serviceActions: readPanelAppServiceActions(html)
|
|
7707
|
-
};
|
|
7708
|
-
}
|
|
7709
|
-
function parsePanelAppFolderManifest(raw) {
|
|
7710
|
-
let parsed;
|
|
7711
|
-
try {
|
|
7712
|
-
parsed = JSON.parse(raw);
|
|
7713
|
-
} catch (error) {
|
|
7714
|
-
throw new Error(`panel-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
7715
|
-
}
|
|
7716
|
-
if (!isRecord$8(parsed)) throw new Error("panel-app.json must contain an object.");
|
|
7717
|
-
const id = readOptionalString$8(parsed, "id");
|
|
7718
|
-
if (id && !PANEL_APP_ID_PATTERN.test(id)) throw new Error("panel app id must be kebab-case.");
|
|
7719
|
-
return {
|
|
7720
|
-
id,
|
|
7721
|
-
title: readRequiredString$7(parsed, "title"),
|
|
7722
|
-
description: readOptionalString$8(parsed, "description"),
|
|
7723
|
-
icon: readOptionalString$8(parsed, "icon"),
|
|
7724
|
-
entry: readRequiredString$7(parsed, "entry"),
|
|
7725
|
-
capabilities: readStringArray$1(parsed.capabilities, "capabilities"),
|
|
7726
|
-
client: readOptionalBoolean$2(parsed, "client"),
|
|
7727
|
-
serviceActions: readStringArray$1(parsed.actions, "actions")
|
|
7728
|
-
};
|
|
7729
|
-
}
|
|
7730
|
-
function readPanelAppMeta(html) {
|
|
7731
|
-
return {
|
|
7732
|
-
...readPanelAppMetaField(html, "title"),
|
|
7733
|
-
...readPanelAppMetaField(html, "description"),
|
|
7734
|
-
...readPanelAppMetaField(html, "icon")
|
|
7735
|
-
};
|
|
7736
|
-
}
|
|
7737
|
-
function readPanelAppMetaField(html, field) {
|
|
7738
|
-
const content = readMetaContent(html, `nextclaw-panel-${field}`, field === "icon" ? "attribute" : "text");
|
|
7739
|
-
const manifest = {};
|
|
7740
|
-
if (content) manifest[field] = content;
|
|
7741
|
-
return manifest;
|
|
7742
|
-
}
|
|
7743
|
-
function readHtmlTitle(html) {
|
|
7744
|
-
const title = normalizeTextValue(html.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1]);
|
|
7745
|
-
return title ? { title } : {};
|
|
7746
|
-
}
|
|
7747
|
-
function readStandardIcon(html) {
|
|
7748
|
-
const icon = readLinkHref(html, (relTokens) => relTokens.includes("icon"));
|
|
7749
|
-
const appleTouchIcon = readLinkHref(html, (relTokens) => relTokens.some((token) => token === "apple-touch-icon" || token === "apple-touch-icon-precomposed"));
|
|
7750
|
-
const href = normalizeIconHref(icon ?? appleTouchIcon);
|
|
7751
|
-
return href ? { icon: href } : {};
|
|
7752
|
-
}
|
|
7753
|
-
function readMetaContent(html, name, valueKind) {
|
|
7754
|
-
const metaTags = html.matchAll(/<meta\s+([^>]*?)>/gi);
|
|
7755
|
-
for (const tag of metaTags) {
|
|
7756
|
-
const attributes = tag[1] ?? "";
|
|
7757
|
-
if (readHtmlAttribute(attributes, "name") === name) {
|
|
7758
|
-
const content = readHtmlAttribute(attributes, "content");
|
|
7759
|
-
return valueKind === "attribute" ? normalizeAttributeValue(content) : normalizeTextValue(content);
|
|
7760
|
-
}
|
|
7761
|
-
}
|
|
7762
|
-
}
|
|
7763
|
-
function readLinkHref(html, matchesRel) {
|
|
7764
|
-
const linkTags = html.matchAll(/<link\s+([^>]*?)>/gi);
|
|
7765
|
-
for (const tag of linkTags) {
|
|
7766
|
-
const attributes = tag[1] ?? "";
|
|
7767
|
-
if (matchesRel((readHtmlAttribute(attributes, "rel") ?? "").toLowerCase().split(/\s+/).filter(Boolean))) return normalizeAttributeValue(readHtmlAttribute(attributes, "href"));
|
|
7768
|
-
}
|
|
7769
|
-
}
|
|
7770
|
-
function readHtmlAttribute(attributes, attribute) {
|
|
7771
|
-
const match = attributes.match(new RegExp(`(?:^|\\s)${attribute}\\s*=\\s*(["'])(.*?)\\1`, "i"));
|
|
7772
|
-
return match?.[2] ? decodeHtmlAttribute(match[2]) : void 0;
|
|
7773
|
-
}
|
|
7774
|
-
function readPanelAppServiceActions(html) {
|
|
7775
|
-
return parseTokenList(readMetaContent(html, "nextclaw-panel-actions", "attribute"));
|
|
7776
|
-
}
|
|
7777
|
-
function readPanelAppCapabilities(html) {
|
|
7778
|
-
return parseTokenList(readMetaContent(html, "nextclaw-panel-capabilities", "attribute"));
|
|
7779
|
-
}
|
|
7780
|
-
function parseTokenList(content) {
|
|
7781
|
-
if (!content) return [];
|
|
7782
|
-
return [...new Set(content.split(/[,\s]+/).map((entry) => entry.trim()).filter(Boolean))];
|
|
7783
|
-
}
|
|
7784
|
-
function readRequiredString$7(record, key) {
|
|
7785
|
-
const value = readOptionalString$8(record, key);
|
|
7786
|
-
if (!value) throw new Error(`panel app ${key} is required.`);
|
|
7787
|
-
return value;
|
|
7788
|
-
}
|
|
7789
|
-
function readOptionalString$8(record, key) {
|
|
7790
|
-
return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
|
|
7791
|
-
}
|
|
7792
|
-
function readOptionalBoolean$2(record, key) {
|
|
7793
|
-
const value = record[key];
|
|
7794
|
-
if (value === void 0) return false;
|
|
7795
|
-
if (typeof value !== "boolean") throw new Error(`panel app ${key} must be a boolean.`);
|
|
7796
|
-
return value;
|
|
7797
|
-
}
|
|
7798
|
-
function readStringArray$1(value, key) {
|
|
7799
|
-
if (value === void 0) return [];
|
|
7800
|
-
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`panel app ${key} must be a string array.`);
|
|
7801
|
-
return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))];
|
|
7802
|
-
}
|
|
7803
|
-
function isRecord$8(value) {
|
|
7804
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7805
|
-
}
|
|
7806
|
-
function normalizeTextValue(value) {
|
|
7807
|
-
return decodeHtmlAttribute(value ?? "").replace(/\s+/g, " ").trim() || void 0;
|
|
7808
|
-
}
|
|
7809
|
-
function normalizeAttributeValue(value) {
|
|
7810
|
-
return decodeHtmlAttribute(value ?? "").trim() || void 0;
|
|
7811
|
-
}
|
|
7812
|
-
function normalizeIconHref(value) {
|
|
7813
|
-
if (!value) return;
|
|
7814
|
-
if (value.startsWith("data:image/") || value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/")) return value;
|
|
7815
|
-
}
|
|
7816
|
-
function decodeHtmlAttribute(value) {
|
|
7817
|
-
return value.replace(/"/g, "\"").replace(/"/g, "\"").replace(/'/g, "'").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
7818
|
-
}
|
|
7819
|
-
const PANEL_APP_FOLDER_MANIFEST_FILE_NAME = "panel-app.json";
|
|
7820
|
-
function isPanelAppSourceEntry(entry) {
|
|
7821
|
-
return entry.isFile() && isPanelAppFileName(entry.name) || entry.isDirectory() && isPanelAppDirName(entry.name);
|
|
7822
|
-
}
|
|
7823
|
-
function isPanelAppFileName(fileName) {
|
|
7824
|
-
return hasSafeBaseName(fileName) && fileName.endsWith(".panel.html");
|
|
7825
|
-
}
|
|
7826
|
-
function isPanelAppDirName(dirName) {
|
|
7827
|
-
return hasSafeBaseName(dirName) && dirName.endsWith(".panel");
|
|
7828
|
-
}
|
|
7829
|
-
function toPanelAppTitle(sourceName) {
|
|
7830
|
-
return sourceName.replace(new RegExp(`${escapeRegExp(".panel.html")}$`), "").replace(new RegExp(`${escapeRegExp(".panel")}$`), "").replace(/[-_]+/g, " ").trim() || sourceName;
|
|
7831
|
-
}
|
|
7832
|
-
function encodePanelAppId(sourceName) {
|
|
7833
|
-
return Buffer.from(sourceName, "utf8").toString("base64url");
|
|
7834
|
-
}
|
|
7835
|
-
function decodePanelAppId(id) {
|
|
7836
|
-
const normalizedId = id.trim();
|
|
7837
|
-
if (!normalizedId) throw new PanelAppError("PANEL_APP_INVALID_ID", "panel app id is required");
|
|
7838
|
-
let sourceName = "";
|
|
7839
|
-
try {
|
|
7840
|
-
sourceName = Buffer.from(normalizedId, "base64url").toString("utf8");
|
|
7841
|
-
} catch {
|
|
7842
|
-
throw new PanelAppError("PANEL_APP_INVALID_ID", "invalid panel app id");
|
|
7843
|
-
}
|
|
7844
|
-
if (encodePanelAppId(sourceName) !== normalizedId || !isPanelAppFileName(sourceName) && !isPanelAppDirName(sourceName)) throw new PanelAppError("PANEL_APP_INVALID_ID", "invalid panel app id");
|
|
7845
|
-
return sourceName;
|
|
7846
|
-
}
|
|
7847
|
-
async function readPanelAppFolderManifest(dirPath, dirName) {
|
|
7848
|
-
const manifest = parsePanelAppFolderManifest(await readFile(join(dirPath, PANEL_APP_FOLDER_MANIFEST_FILE_NAME), "utf8"));
|
|
7849
|
-
const expectedId = dirName.slice(0, -6);
|
|
7850
|
-
if (manifest.id && manifest.id !== expectedId) throw new PanelAppError("PANEL_APP_MANIFEST_INVALID", "panel app manifest id must match the directory name");
|
|
7851
|
-
return {
|
|
7852
|
-
...manifest,
|
|
7853
|
-
id: expectedId
|
|
7854
|
-
};
|
|
7855
|
-
}
|
|
7856
|
-
function resolvePanelAppRelativePath(rootPath, relativePath) {
|
|
7857
|
-
if (!relativePath.trim() || relativePath.includes("\0") || isAbsolute(relativePath)) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
7858
|
-
const normalizedPath = normalize(relativePath);
|
|
7859
|
-
if (normalizedPath === "." || normalizedPath.startsWith("..")) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
7860
|
-
const resolvedPath = resolve(rootPath, normalizedPath);
|
|
7861
|
-
const pathFromRoot = relative(resolve(rootPath), resolvedPath);
|
|
7862
|
-
if (pathFromRoot.startsWith("..") || isAbsolute(pathFromRoot)) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
7863
|
-
return resolvedPath;
|
|
7864
|
-
}
|
|
7865
|
-
function resolvePanelAppAssetContentType(path) {
|
|
7866
|
-
switch (extname(path).toLowerCase()) {
|
|
7867
|
-
case ".css": return "text/css; charset=utf-8";
|
|
7868
|
-
case ".js":
|
|
7869
|
-
case ".mjs": return "application/javascript; charset=utf-8";
|
|
7870
|
-
case ".json": return "application/json; charset=utf-8";
|
|
7871
|
-
case ".png": return "image/png";
|
|
7872
|
-
case ".svg": return "image/svg+xml; charset=utf-8";
|
|
7873
|
-
case ".webp": return "image/webp";
|
|
7874
|
-
case ".txt": return "text/plain; charset=utf-8";
|
|
7875
|
-
default: return "application/octet-stream";
|
|
7876
|
-
}
|
|
7877
|
-
}
|
|
7878
|
-
function resolvePanelAppIconUrl(id, icon) {
|
|
7879
|
-
if (!icon) return;
|
|
7880
|
-
if (icon.startsWith("data:image/") || icon.startsWith("http://") || icon.startsWith("https://") || icon.startsWith("/") || isLikelyTextIcon(icon)) return icon;
|
|
7881
|
-
return `/api/panel-apps/${encodeURIComponent(id)}/assets/${encodePanelAppAssetPath(icon)}`;
|
|
7882
|
-
}
|
|
7883
|
-
function injectPanelAppAssetBase(html, baseHref) {
|
|
7884
|
-
const base = `<base href="${baseHref}">`;
|
|
7885
|
-
return injectLocalScriptCrossOrigin((() => {
|
|
7886
|
-
if (/<base\b/i.test(html)) return html;
|
|
7887
|
-
if (/<head[^>]*>/i.test(html)) return html.replace(/<head[^>]*>/i, (head) => `${head}${base}`);
|
|
7888
|
-
return `${base}${html}`;
|
|
7889
|
-
})());
|
|
7890
|
-
}
|
|
7891
|
-
function injectLocalScriptCrossOrigin(html) {
|
|
7892
|
-
return html.replace(/<script\b(?=[^>]*\bsrc\s*=)(?![^>]*\bcrossorigin\b)[^>]*>/gi, (tag) => {
|
|
7893
|
-
const src = extractScriptSrc(tag);
|
|
7894
|
-
if (!src || !isLocalScriptSrc(src)) return tag;
|
|
7895
|
-
return `${tag.slice(0, -1)} crossorigin="anonymous">`;
|
|
7896
|
-
});
|
|
7897
|
-
}
|
|
7898
|
-
function extractScriptSrc(tag) {
|
|
7899
|
-
const match = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tag);
|
|
7900
|
-
return match?.[1] ?? match?.[2] ?? match?.[3];
|
|
7901
|
-
}
|
|
7902
|
-
function isLocalScriptSrc(src) {
|
|
7903
|
-
const value = src.trim();
|
|
7904
|
-
if (!value || value.startsWith("//")) return false;
|
|
7905
|
-
return !/^(?:https?|data|blob|javascript):/i.test(value);
|
|
7906
|
-
}
|
|
7907
|
-
function encodePanelAppAssetPath(path) {
|
|
7908
|
-
return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
7909
|
-
}
|
|
7910
|
-
function hasSafeBaseName(name) {
|
|
7911
|
-
return !name.includes("/") && !name.includes("\\") && !name.includes("\0");
|
|
7912
|
-
}
|
|
7913
|
-
function isLikelyTextIcon(icon) {
|
|
7914
|
-
return !icon.includes("/") && !icon.includes(".") && icon.length <= 4;
|
|
7915
|
-
}
|
|
7916
|
-
function escapeRegExp(value) {
|
|
7917
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8659
|
+
return `
|
|
8660
|
+
(() => {
|
|
8661
|
+
const marker = "${PANEL_APP_CLIENT_MARKER}";
|
|
8662
|
+
if (typeof window.NextClawClient !== "function") {
|
|
8663
|
+
console.error("[NextClaw] Panel App client SDK failed to load.");
|
|
8664
|
+
return;
|
|
8665
|
+
}
|
|
8666
|
+
if (typeof window.createNextClawAppClient !== "function") {
|
|
8667
|
+
console.error("[NextClaw] Panel App client projection failed to load.");
|
|
8668
|
+
return;
|
|
8669
|
+
}
|
|
8670
|
+
const existing = window.nextclaw && typeof window.nextclaw === "object" ? window.nextclaw : {};
|
|
8671
|
+
const hostClient = new window.NextClawClient({
|
|
8672
|
+
baseUrl: window.location.origin,
|
|
8673
|
+
headers: {
|
|
8674
|
+
"x-nextclaw-panel-bridge-session": ${JSON.stringify(params.runtimeToken)}
|
|
8675
|
+
}
|
|
8676
|
+
});
|
|
8677
|
+
const client = window.createNextClawAppClient(hostClient);
|
|
8678
|
+
Object.defineProperty(window, "nextclaw", {
|
|
8679
|
+
configurable: true,
|
|
8680
|
+
value: {
|
|
8681
|
+
...existing,
|
|
8682
|
+
client
|
|
8683
|
+
}
|
|
8684
|
+
});
|
|
8685
|
+
Object.defineProperty(window.nextclaw, "__clientInitMarker", {
|
|
8686
|
+
configurable: true,
|
|
8687
|
+
enumerable: false,
|
|
8688
|
+
value: marker
|
|
8689
|
+
});
|
|
8690
|
+
})();
|
|
8691
|
+
`.trim();
|
|
7918
8692
|
}
|
|
7919
8693
|
//#endregion
|
|
7920
8694
|
//#region src/services/panel-app-source.service.ts
|
|
@@ -8015,84 +8789,6 @@ function isMissingFileError$1(error) {
|
|
|
8015
8789
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
8016
8790
|
}
|
|
8017
8791
|
//#endregion
|
|
8018
|
-
//#region src/utils/panel-app-time.utils.ts
|
|
8019
|
-
function resolvePanelAppCreatedAt(fileStat) {
|
|
8020
|
-
return fileStat.birthtimeMs > 0 ? fileStat.birthtime.toISOString() : fileStat.mtime.toISOString();
|
|
8021
|
-
}
|
|
8022
|
-
function resolvePanelAppActivityMs(entry) {
|
|
8023
|
-
return Math.max(new Date(entry.lastOpenedAt ?? 0).getTime(), new Date(entry.createdAt).getTime(), new Date(entry.updatedAt).getTime());
|
|
8024
|
-
}
|
|
8025
|
-
//#endregion
|
|
8026
|
-
//#region src/utils/panel-app-content-source.utils.ts
|
|
8027
|
-
async function readPanelAppContentSource(params) {
|
|
8028
|
-
const { createAssetBaseHref, id, panelsPath, sourceService } = params;
|
|
8029
|
-
return await readResolvedPanelAppContentSource(await sourceService.resolveSource(panelsPath, id), createAssetBaseHref);
|
|
8030
|
-
}
|
|
8031
|
-
async function readPanelAppContentSourceByPath(params) {
|
|
8032
|
-
const { createAssetBaseHref, path, sourceService } = params;
|
|
8033
|
-
return await readResolvedPanelAppContentSource(await sourceService.resolveSourcePath(path), createAssetBaseHref);
|
|
8034
|
-
}
|
|
8035
|
-
async function readPanelAppContentSourceByIdOrPath(params) {
|
|
8036
|
-
const { sourcePath, ...standardSourceParams } = params;
|
|
8037
|
-
return sourcePath ? await readPanelAppContentSourceByPath({
|
|
8038
|
-
createAssetBaseHref: params.createAssetBaseHref,
|
|
8039
|
-
path: sourcePath,
|
|
8040
|
-
sourceService: params.sourceService
|
|
8041
|
-
}) : await readPanelAppContentSource(standardSourceParams);
|
|
8042
|
-
}
|
|
8043
|
-
async function readResolvedPanelAppContentSource(source, createAssetBaseHref) {
|
|
8044
|
-
const html = await readFile(source.entryPath, "utf8");
|
|
8045
|
-
const manifest = source.manifest ?? parsePanelAppManifest(html);
|
|
8046
|
-
const sourceId = encodePanelAppId(source.sourceName);
|
|
8047
|
-
return {
|
|
8048
|
-
appId: resolvePanelAppAppId(source, manifest),
|
|
8049
|
-
sourceId,
|
|
8050
|
-
source,
|
|
8051
|
-
manifest,
|
|
8052
|
-
html,
|
|
8053
|
-
htmlWithBase: source.kind === "folder" ? injectPanelAppAssetBase(html, createAssetBaseHref(source)) : html
|
|
8054
|
-
};
|
|
8055
|
-
}
|
|
8056
|
-
async function readPanelAppContentSourceByIdOrAppId(params) {
|
|
8057
|
-
const { appIdOrSourceId, createAssetBaseHref, panelsPath, sourceService } = params;
|
|
8058
|
-
try {
|
|
8059
|
-
return await readPanelAppContentSource({
|
|
8060
|
-
createAssetBaseHref,
|
|
8061
|
-
id: appIdOrSourceId,
|
|
8062
|
-
panelsPath,
|
|
8063
|
-
sourceService
|
|
8064
|
-
});
|
|
8065
|
-
} catch (error) {
|
|
8066
|
-
if (!isPanelAppError(error)) throw error;
|
|
8067
|
-
if (error.code !== "PANEL_APP_INVALID_ID" && error.code !== "PANEL_APP_NOT_FOUND") throw error;
|
|
8068
|
-
}
|
|
8069
|
-
const sources = await sourceService.listSources(panelsPath);
|
|
8070
|
-
for (const source of sources) {
|
|
8071
|
-
const html = await readFile(source.entryPath, "utf8");
|
|
8072
|
-
if (resolvePanelAppAppId(source, source.manifest ?? parsePanelAppManifest(html)) === appIdOrSourceId) return await readPanelAppContentSource({
|
|
8073
|
-
createAssetBaseHref,
|
|
8074
|
-
id: encodePanelAppId(source.sourceName),
|
|
8075
|
-
panelsPath,
|
|
8076
|
-
sourceService
|
|
8077
|
-
});
|
|
8078
|
-
}
|
|
8079
|
-
throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
8080
|
-
}
|
|
8081
|
-
function resolvePanelAppAppId(source, manifest) {
|
|
8082
|
-
return manifest.id ?? encodePanelAppId(source.sourceName);
|
|
8083
|
-
}
|
|
8084
|
-
async function assertPanelAppDeclaresClient(params) {
|
|
8085
|
-
const { appId, panelsPath, sourceService } = params;
|
|
8086
|
-
const sources = await sourceService.listSources(panelsPath);
|
|
8087
|
-
for (const source of sources) {
|
|
8088
|
-
const manifest = source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8"));
|
|
8089
|
-
if (resolvePanelAppAppId(source, manifest) !== appId) continue;
|
|
8090
|
-
if (!manifest.client) throw new PanelAppError("PANEL_APP_CLIENT_NOT_DECLARED", "panel app did not declare client access");
|
|
8091
|
-
return;
|
|
8092
|
-
}
|
|
8093
|
-
throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
8094
|
-
}
|
|
8095
|
-
//#endregion
|
|
8096
8792
|
//#region src/managers/panel-app.manager.ts
|
|
8097
8793
|
const PANEL_APP_CONTENT_BASE_PATH = "/api/panel-apps";
|
|
8098
8794
|
const PANEL_APP_TOKENIZED_ASSET_BASE_PATH = "/api/panel-app-assets";
|
|
@@ -8106,6 +8802,8 @@ var PanelAppManager = class {
|
|
|
8106
8802
|
agentBridgeService;
|
|
8107
8803
|
assetTokenService = new PanelAppAssetTokenService();
|
|
8108
8804
|
sourceService = new PanelAppSourceService();
|
|
8805
|
+
packageStateManager;
|
|
8806
|
+
entryPresenter;
|
|
8109
8807
|
constructor(params) {
|
|
8110
8808
|
this.params = params;
|
|
8111
8809
|
this.agentRunClient = params.agentRunClient ?? (params.eventBus && params.ingress ? new AgentRunClient({
|
|
@@ -8116,16 +8814,31 @@ var PanelAppManager = class {
|
|
|
8116
8814
|
agentRunClient: this.agentRunClient,
|
|
8117
8815
|
createCapabilityGrantStore: this.createCapabilityGrantStore
|
|
8118
8816
|
});
|
|
8817
|
+
this.packageStateManager = new PanelAppPackageStateManager({
|
|
8818
|
+
sourceService: this.sourceService,
|
|
8819
|
+
getPanelsPath: () => this.getPanelsPath(this.getWorkspacePath()),
|
|
8820
|
+
listPackageComponentSources: params.listPackageComponentSources,
|
|
8821
|
+
createAssetBaseHref: this.createAssetBaseHref,
|
|
8822
|
+
deleteBridgeSessions: this.deleteBridgeSessionsByPanelAppId,
|
|
8823
|
+
createStateStore: this.createStateStore,
|
|
8824
|
+
createCapabilityGrantStore: this.createCapabilityGrantStore,
|
|
8825
|
+
createClientGrantStore: this.createClientGrantStore
|
|
8826
|
+
});
|
|
8827
|
+
this.entryPresenter = new PanelAppEntryPresenter({
|
|
8828
|
+
contentBasePath: PANEL_APP_CONTENT_BASE_PATH,
|
|
8829
|
+
createAssetBaseHref: this.createAssetBaseHref,
|
|
8830
|
+
isClientGranted: this.isPanelAppClientGranted
|
|
8831
|
+
});
|
|
8119
8832
|
}
|
|
8120
8833
|
listPanelApps = async () => {
|
|
8121
8834
|
const workspacePath = this.getWorkspacePath();
|
|
8122
8835
|
const panelsPath = this.getPanelsPath(workspacePath);
|
|
8123
|
-
const sources = await this.
|
|
8836
|
+
const sources = await this.packageStateManager.listSources();
|
|
8124
8837
|
const appState = await this.createStateStore(panelsPath).load();
|
|
8125
8838
|
return {
|
|
8126
8839
|
workspacePath,
|
|
8127
8840
|
panelsPath,
|
|
8128
|
-
entries: (await Promise.all(sources.map((source) => this.
|
|
8841
|
+
entries: (await Promise.all(sources.map(({ source, packageSource }) => this.entryPresenter.build(source, appState[encodePanelAppId(source.sourceName)] ?? {}, packageSource)))).sort(this.entryPresenter.compare)
|
|
8129
8842
|
};
|
|
8130
8843
|
};
|
|
8131
8844
|
getPanelAppContent = async (id, sourcePath) => {
|
|
@@ -8200,12 +8913,7 @@ var PanelAppManager = class {
|
|
|
8200
8913
|
return session;
|
|
8201
8914
|
};
|
|
8202
8915
|
createPanelAppBridgeSession = async (params) => {
|
|
8203
|
-
const resolved = await
|
|
8204
|
-
appIdOrSourceId: params.id,
|
|
8205
|
-
createAssetBaseHref: this.createAssetBaseHref,
|
|
8206
|
-
panelsPath: this.getPanelsPath(this.getWorkspacePath()),
|
|
8207
|
-
sourceService: this.sourceService
|
|
8208
|
-
});
|
|
8916
|
+
const resolved = await this.packageStateManager.readContentSourceByIdOrAppId(params.id);
|
|
8209
8917
|
return this.createPanelAppRuntimeTokenSession({
|
|
8210
8918
|
appId: resolved.appId,
|
|
8211
8919
|
clientDeclared: resolved.manifest.client,
|
|
@@ -8214,11 +8922,7 @@ var PanelAppManager = class {
|
|
|
8214
8922
|
});
|
|
8215
8923
|
};
|
|
8216
8924
|
grantPanelAppClient = async (appId) => {
|
|
8217
|
-
await
|
|
8218
|
-
appId,
|
|
8219
|
-
panelsPath: this.getPanelsPath(this.getWorkspacePath()),
|
|
8220
|
-
sourceService: this.sourceService
|
|
8221
|
-
});
|
|
8925
|
+
await this.packageStateManager.assertDeclaresClient(appId);
|
|
8222
8926
|
return await this.createClientGrantStore().grant({
|
|
8223
8927
|
appId,
|
|
8224
8928
|
grantedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8252,17 +8956,19 @@ var PanelAppManager = class {
|
|
|
8252
8956
|
const fileName = await this.resolvePanelAppFileName(id);
|
|
8253
8957
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
8254
8958
|
const state = await this.createStateStore(panelsPath).updatePreferences(encodePanelAppId(fileName), preferences);
|
|
8255
|
-
return await this.
|
|
8959
|
+
return await this.entryPresenter.build(await this.packageStateManager.resolveSource(encodePanelAppId(fileName)), state, await this.packageStateManager.findPackageSourceBySourceName(fileName));
|
|
8256
8960
|
};
|
|
8257
8961
|
recordPanelAppOpened = async (id) => {
|
|
8258
8962
|
const fileName = await this.resolvePanelAppFileName(id);
|
|
8259
8963
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
8260
8964
|
const state = await this.createStateStore(panelsPath).recordOpened(encodePanelAppId(fileName));
|
|
8261
|
-
return await this.
|
|
8965
|
+
return await this.entryPresenter.build(await this.packageStateManager.resolveSource(encodePanelAppId(fileName)), state, await this.packageStateManager.findPackageSourceBySourceName(fileName));
|
|
8262
8966
|
};
|
|
8263
8967
|
deletePanelApp = async (id) => {
|
|
8264
8968
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
8265
|
-
const source = await this.
|
|
8969
|
+
const source = await this.packageStateManager.resolveSource(id);
|
|
8970
|
+
const packageSource = await this.packageStateManager.findPackageSourceBySourceName(source.sourceName);
|
|
8971
|
+
if (packageSource) throw new PanelAppError("PANEL_APP_MANAGED_SOURCE", `package panel must be managed through Apps: ${packageSource.packageId}`);
|
|
8266
8972
|
const panelAppId = encodePanelAppId(source.sourceName);
|
|
8267
8973
|
const appId = resolvePanelAppAppId(source, source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8")));
|
|
8268
8974
|
await rm(source.sourcePath, { recursive: source.kind === "folder" });
|
|
@@ -8292,36 +8998,12 @@ var PanelAppManager = class {
|
|
|
8292
8998
|
createStateStore = (panelsPath) => new PanelAppStateStore(panelsPath);
|
|
8293
8999
|
createCapabilityGrantStore = () => new PanelAppCapabilityGrantStore(join(this.getPanelsPath(this.getWorkspacePath()), PANEL_APP_CAPABILITY_GRANTS_FILE_NAME));
|
|
8294
9000
|
createClientGrantStore = () => new PanelAppClientGrantStore(join(this.getPanelsPath(this.getWorkspacePath()), PANEL_APP_CLIENT_GRANTS_FILE_NAME));
|
|
8295
|
-
buildPanelAppEntry = async (source, state) => {
|
|
8296
|
-
const manifest = source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8"));
|
|
8297
|
-
const id = encodePanelAppId(source.sourceName);
|
|
8298
|
-
const appId = resolvePanelAppAppId(source, manifest);
|
|
8299
|
-
const createdAt = resolvePanelAppCreatedAt(source.sourceStat);
|
|
8300
|
-
const updatedAt = source.sourceStat.mtime.toISOString();
|
|
8301
|
-
const entry = {
|
|
8302
|
-
id,
|
|
8303
|
-
appId,
|
|
8304
|
-
fileName: source.sourceName,
|
|
8305
|
-
kind: source.kind,
|
|
8306
|
-
title: manifest.title ?? toPanelAppTitle(source.sourceName),
|
|
8307
|
-
contentPath: `${PANEL_APP_CONTENT_BASE_PATH}/${encodeURIComponent(id)}/content`,
|
|
8308
|
-
createdAt,
|
|
8309
|
-
updatedAt,
|
|
8310
|
-
sizeBytes: source.sourceStat.size,
|
|
8311
|
-
favorite: state.favorite ?? false,
|
|
8312
|
-
clientDeclared: manifest.client,
|
|
8313
|
-
clientGranted: await this.isPanelAppClientGranted(appId, manifest.client),
|
|
8314
|
-
openCount: state.openCount ?? 0
|
|
8315
|
-
};
|
|
8316
|
-
if (manifest.description) entry.description = manifest.description;
|
|
8317
|
-
if (manifest.icon) entry.icon = source.kind === "folder" ? resolvePanelAppIconUrl(id, manifest.icon) : manifest.icon;
|
|
8318
|
-
if (state.lastOpenedAt) entry.lastOpenedAt = state.lastOpenedAt;
|
|
8319
|
-
return entry;
|
|
8320
|
-
};
|
|
8321
9001
|
resolvePanelAppFileName = async (id) => {
|
|
8322
|
-
return (await this.
|
|
9002
|
+
return (await this.packageStateManager.resolveSource(id)).sourceName;
|
|
8323
9003
|
};
|
|
8324
|
-
|
|
9004
|
+
assertCanActivatePackageComponents = async (components) => await this.packageStateManager.assertCanActivate(components);
|
|
9005
|
+
deactivatePackageComponents = (components) => this.packageStateManager.deactivate(components);
|
|
9006
|
+
removePackageComponentState = async (components) => await this.packageStateManager.removeState(components);
|
|
8325
9007
|
deleteExpiredBridgeSessions = () => {
|
|
8326
9008
|
const now = Date.now();
|
|
8327
9009
|
for (const [token, session] of this.bridgeSessions) if (new Date(session.expiresAt).getTime() <= now) this.bridgeSessions.delete(token);
|
|
@@ -8777,7 +9459,7 @@ var McpServiceAppRuntimeService = class {
|
|
|
8777
9459
|
command: manifest.command,
|
|
8778
9460
|
args: manifest.args,
|
|
8779
9461
|
cwd: app.dirPath,
|
|
8780
|
-
env: createRuntimeChildEnv(process.env),
|
|
9462
|
+
env: createRuntimeChildEnv(process.env, this.createAppRuntimeEnv(app)),
|
|
8781
9463
|
stderr: "pipe"
|
|
8782
9464
|
},
|
|
8783
9465
|
scope: {
|
|
@@ -8790,6 +9472,15 @@ var McpServiceAppRuntimeService = class {
|
|
|
8790
9472
|
}
|
|
8791
9473
|
}
|
|
8792
9474
|
});
|
|
9475
|
+
createAppRuntimeEnv = (app) => {
|
|
9476
|
+
if (app.sourceKind !== "package" || !app.packageId || !app.packageVersion || !app.packageDirectory || !app.dataDirectory) return {};
|
|
9477
|
+
return {
|
|
9478
|
+
NEXTCLAW_APP_ID: app.packageId,
|
|
9479
|
+
NEXTCLAW_APP_VERSION: app.packageVersion,
|
|
9480
|
+
NEXTCLAW_APP_DATA_DIR: app.dataDirectory,
|
|
9481
|
+
NEXTCLAW_APP_PACKAGE_DIR: app.packageDirectory
|
|
9482
|
+
};
|
|
9483
|
+
};
|
|
8793
9484
|
toServiceAction = (manifest, tool) => {
|
|
8794
9485
|
const actionId = buildServiceActionId(manifest.id, tool.toolName);
|
|
8795
9486
|
const manifestAction = manifest.actions[tool.toolName];
|
|
@@ -8884,12 +9575,12 @@ var ServiceActionGrantStore = class {
|
|
|
8884
9575
|
};
|
|
8885
9576
|
};
|
|
8886
9577
|
function normalizeStoreData(value) {
|
|
8887
|
-
if (!isRecord$
|
|
9578
|
+
if (!isRecord$8(value) || value.version !== 1 || !isRecord$8(value.grants)) return structuredClone(EMPTY_GRANTS);
|
|
8888
9579
|
const grants = {};
|
|
8889
9580
|
for (const [callerKey, callerValue] of Object.entries(value.grants)) {
|
|
8890
|
-
if (!isRecord$
|
|
9581
|
+
if (!isRecord$8(callerValue) || !isRecord$8(callerValue.actions)) continue;
|
|
8891
9582
|
const actions = {};
|
|
8892
|
-
for (const [actionId, actionValue] of Object.entries(callerValue.actions)) if (isRecord$
|
|
9583
|
+
for (const [actionId, actionValue] of Object.entries(callerValue.actions)) if (isRecord$8(actionValue) && typeof actionValue.grantedAt === "string" && isServiceActionRisk(actionValue.risk)) actions[actionId] = {
|
|
8893
9584
|
grantedAt: actionValue.grantedAt,
|
|
8894
9585
|
risk: actionValue.risk
|
|
8895
9586
|
};
|
|
@@ -8903,11 +9594,11 @@ function normalizeStoreData(value) {
|
|
|
8903
9594
|
function isServiceActionRisk(value) {
|
|
8904
9595
|
return value === "read" || value === "write" || value === "external" || value === "dangerous";
|
|
8905
9596
|
}
|
|
8906
|
-
function isRecord$
|
|
9597
|
+
function isRecord$8(value) {
|
|
8907
9598
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8908
9599
|
}
|
|
8909
9600
|
function isMissingFileError(error) {
|
|
8910
|
-
return isRecord$
|
|
9601
|
+
return isRecord$8(error) && error.code === "ENOENT";
|
|
8911
9602
|
}
|
|
8912
9603
|
//#endregion
|
|
8913
9604
|
//#region src/utils/service-app-manifest.utils.ts
|
|
@@ -8932,7 +9623,7 @@ function parseServiceAppManifest(raw) {
|
|
|
8932
9623
|
} catch (error) {
|
|
8933
9624
|
throw new Error(`service-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
8934
9625
|
}
|
|
8935
|
-
if (!isRecord$
|
|
9626
|
+
if (!isRecord$7(parsed)) throw new Error("service-app.json must contain an object.");
|
|
8936
9627
|
const id = readRequiredString$6(parsed, "id");
|
|
8937
9628
|
if (!SERVICE_APP_ID_PATTERN.test(id)) throw new Error("service app id must be kebab-case.");
|
|
8938
9629
|
const protocol = readOptionalString$7(parsed, "protocol") ?? "mcp";
|
|
@@ -8950,16 +9641,16 @@ function parseServiceAppManifest(raw) {
|
|
|
8950
9641
|
}
|
|
8951
9642
|
function readManifestActions(value) {
|
|
8952
9643
|
if (value === void 0) throw new Error("service app actions are required.");
|
|
8953
|
-
if (!isRecord$
|
|
9644
|
+
if (!isRecord$7(value)) throw new Error("service app actions must be an object.");
|
|
8954
9645
|
if (Object.keys(value).length === 0) throw new Error("service app actions cannot be empty.");
|
|
8955
9646
|
const actions = {};
|
|
8956
9647
|
for (const [name, action] of Object.entries(value)) {
|
|
8957
9648
|
if (!name.trim()) throw new Error("service app action name cannot be empty.");
|
|
8958
|
-
if (!isRecord$
|
|
9649
|
+
if (!isRecord$7(action)) throw new Error(`service app action ${name} must be an object.`);
|
|
8959
9650
|
const risk = readOptionalString$7(action, "risk");
|
|
8960
9651
|
if (risk !== void 0 && !SERVICE_ACTION_RISKS.has(risk)) throw new Error(`service app action ${name} has invalid risk.`);
|
|
8961
9652
|
const inputSchema = action.inputSchema;
|
|
8962
|
-
if (inputSchema !== void 0 && !isRecord$
|
|
9653
|
+
if (inputSchema !== void 0 && !isRecord$7(inputSchema)) throw new Error(`service app action ${name} inputSchema must be an object.`);
|
|
8963
9654
|
actions[name] = {
|
|
8964
9655
|
risk,
|
|
8965
9656
|
title: readOptionalString$7(action, "title"),
|
|
@@ -8987,7 +9678,7 @@ function readStringArray(value, key) {
|
|
|
8987
9678
|
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`service app ${key} must be a string array.`);
|
|
8988
9679
|
return value;
|
|
8989
9680
|
}
|
|
8990
|
-
function isRecord$
|
|
9681
|
+
function isRecord$7(value) {
|
|
8991
9682
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8992
9683
|
}
|
|
8993
9684
|
//#endregion
|
|
@@ -9045,10 +9736,13 @@ var ServiceAppManager = class {
|
|
|
9045
9736
|
const workspacePath = this.getWorkspacePath();
|
|
9046
9737
|
const serviceAppsPath = this.getServiceAppsPath(workspacePath);
|
|
9047
9738
|
const dirNames = await this.listServiceAppDirNames(serviceAppsPath);
|
|
9739
|
+
const workspaceEntries = await Promise.all(dirNames.map((dirName) => this.buildServiceAppRecord(serviceAppsPath, dirName)));
|
|
9740
|
+
const packageSources = (await this.listPackageComponentSources()).filter((component) => component.kind === "service");
|
|
9741
|
+
const packageEntries = await Promise.all(packageSources.map((source) => this.buildServiceAppRecordFromPackage(source)));
|
|
9048
9742
|
return {
|
|
9049
9743
|
workspacePath,
|
|
9050
9744
|
serviceAppsPath,
|
|
9051
|
-
entries:
|
|
9745
|
+
entries: [...workspaceEntries, ...packageEntries].filter((entry) => Boolean(entry)).sort((left, right) => left.title.localeCompare(right.title))
|
|
9052
9746
|
};
|
|
9053
9747
|
};
|
|
9054
9748
|
getServiceApp = async (appId) => {
|
|
@@ -9126,6 +9820,7 @@ var ServiceAppManager = class {
|
|
|
9126
9820
|
};
|
|
9127
9821
|
deleteServiceApp = async (appId) => {
|
|
9128
9822
|
const { record } = await this.requireServiceApp(appId);
|
|
9823
|
+
if (record.sourceKind === "package") throw new ServiceAppError("SERVICE_APP_MANAGED_SOURCE", `package service must be managed through Apps: ${record.packageId}`);
|
|
9129
9824
|
await this.runtimeService.restart(record.id);
|
|
9130
9825
|
await rm(record.dirPath, { recursive: true });
|
|
9131
9826
|
await this.createGrantStore().revokeActionsByPrefix(`${record.id}.`);
|
|
@@ -9137,6 +9832,31 @@ var ServiceAppManager = class {
|
|
|
9137
9832
|
dispose = async () => {
|
|
9138
9833
|
await this.runtimeService.dispose();
|
|
9139
9834
|
};
|
|
9835
|
+
assertCanActivatePackageComponents = async (components) => {
|
|
9836
|
+
const serviceComponents = components.filter((component) => component.kind === "service");
|
|
9837
|
+
if (serviceComponents.length === 0) return;
|
|
9838
|
+
const workspacePath = this.getServiceAppsPath(this.getWorkspacePath());
|
|
9839
|
+
const workspaceIds = /* @__PURE__ */ new Set();
|
|
9840
|
+
for (const dirName of await this.listServiceAppDirNames(workspacePath)) try {
|
|
9841
|
+
workspaceIds.add((await readServiceAppManifest(join(workspacePath, dirName))).id);
|
|
9842
|
+
} catch {
|
|
9843
|
+
continue;
|
|
9844
|
+
}
|
|
9845
|
+
const activePackageSources = await this.listPackageComponentSources();
|
|
9846
|
+
for (const component of serviceComponents) {
|
|
9847
|
+
const conflictsWithPackage = activePackageSources.some((active) => active.kind === "service" && active.id === component.id && active.packageId !== component.packageId);
|
|
9848
|
+
if (workspaceIds.has(component.id) || conflictsWithPackage) throw new AppPackageError("APP_PACKAGE_CONFLICT", `Service component id 冲突:${component.id}`);
|
|
9849
|
+
}
|
|
9850
|
+
};
|
|
9851
|
+
deactivatePackageComponents = async (components) => {
|
|
9852
|
+
const serviceIds = components.filter((component) => component.kind === "service").map((component) => component.id);
|
|
9853
|
+
await Promise.all(serviceIds.map(async (serviceId) => await this.runtimeService.restart(serviceId)));
|
|
9854
|
+
};
|
|
9855
|
+
removePackageComponentGrants = async (components) => {
|
|
9856
|
+
const serviceIds = components.filter((component) => component.kind === "service").map((component) => component.id);
|
|
9857
|
+
const grantStore = this.createGrantStore();
|
|
9858
|
+
for (const serviceId of serviceIds) await grantStore.revokeActionsByPrefix(`${serviceId}.`);
|
|
9859
|
+
};
|
|
9140
9860
|
withGrantState = async (action, params) => {
|
|
9141
9861
|
if (!params.caller) return action;
|
|
9142
9862
|
const granted = await this.createGrantStore().isGranted(params.caller, action.id);
|
|
@@ -9161,14 +9881,23 @@ var ServiceAppManager = class {
|
|
|
9161
9881
|
return await this.requireServiceApp(appId);
|
|
9162
9882
|
};
|
|
9163
9883
|
requireServiceApp = async (appId) => {
|
|
9164
|
-
|
|
9884
|
+
let dirPath = join(this.getServiceAppsPath(this.getWorkspacePath()), appId);
|
|
9885
|
+
let packageSource;
|
|
9886
|
+
try {
|
|
9887
|
+
if (!(await stat(dirPath)).isDirectory()) throw new ServiceAppError("SERVICE_APP_NOT_FOUND", "service app not found");
|
|
9888
|
+
} catch (error) {
|
|
9889
|
+
if (!this.isMissingFileError(error)) throw error;
|
|
9890
|
+
packageSource = (await this.listPackageComponentSources()).find((component) => component.kind === "service" && component.id === appId);
|
|
9891
|
+
if (!packageSource) throw new ServiceAppError("SERVICE_APP_NOT_FOUND", "service app not found");
|
|
9892
|
+
dirPath = packageSource.sourcePath;
|
|
9893
|
+
}
|
|
9165
9894
|
try {
|
|
9166
9895
|
if (!(await stat(dirPath)).isDirectory()) throw new ServiceAppError("SERVICE_APP_NOT_FOUND", "service app not found");
|
|
9167
9896
|
const manifest = await readServiceAppManifest(dirPath);
|
|
9168
9897
|
if (manifest.id !== appId) throw new ServiceAppError("SERVICE_APP_INVALID_MANIFEST", "service app manifest id must match directory name");
|
|
9169
9898
|
return {
|
|
9170
9899
|
manifest,
|
|
9171
|
-
record: this.toServiceAppRecord(dirPath, manifest)
|
|
9900
|
+
record: this.toServiceAppRecord(dirPath, manifest, packageSource)
|
|
9172
9901
|
};
|
|
9173
9902
|
} catch (error) {
|
|
9174
9903
|
if (isServiceAppError(error)) throw error;
|
|
@@ -9180,7 +9909,7 @@ var ServiceAppManager = class {
|
|
|
9180
9909
|
const workspacePath = this.getWorkspacePath();
|
|
9181
9910
|
const serviceAppsPath = this.getServiceAppsPath(workspacePath);
|
|
9182
9911
|
const dirNames = await this.listServiceAppDirNames(serviceAppsPath);
|
|
9183
|
-
|
|
9912
|
+
const workspaceEntries = await Promise.all(dirNames.map(async (dirName) => {
|
|
9184
9913
|
const dirPath = join(serviceAppsPath, dirName);
|
|
9185
9914
|
try {
|
|
9186
9915
|
const manifest = await readServiceAppManifest(dirPath);
|
|
@@ -9191,7 +9920,19 @@ var ServiceAppManager = class {
|
|
|
9191
9920
|
} catch {
|
|
9192
9921
|
return null;
|
|
9193
9922
|
}
|
|
9194
|
-
}))
|
|
9923
|
+
}));
|
|
9924
|
+
const packageEntries = await Promise.all((await this.listPackageComponentSources()).filter((component) => component.kind === "service").map(async (component) => {
|
|
9925
|
+
try {
|
|
9926
|
+
const manifest = await readServiceAppManifest(component.sourcePath);
|
|
9927
|
+
return {
|
|
9928
|
+
manifest,
|
|
9929
|
+
record: this.toServiceAppRecord(component.sourcePath, manifest, component)
|
|
9930
|
+
};
|
|
9931
|
+
} catch {
|
|
9932
|
+
return null;
|
|
9933
|
+
}
|
|
9934
|
+
}));
|
|
9935
|
+
return [...workspaceEntries, ...packageEntries].filter((entry) => Boolean(entry));
|
|
9195
9936
|
};
|
|
9196
9937
|
buildServiceAppRecord = async (serviceAppsPath, dirName) => {
|
|
9197
9938
|
const dirPath = join(serviceAppsPath, dirName);
|
|
@@ -9213,7 +9954,7 @@ var ServiceAppManager = class {
|
|
|
9213
9954
|
};
|
|
9214
9955
|
}
|
|
9215
9956
|
};
|
|
9216
|
-
toServiceAppRecord = (dirPath, manifest) => {
|
|
9957
|
+
toServiceAppRecord = (dirPath, manifest, packageSource) => {
|
|
9217
9958
|
const runtimeStatus = this.runtimeService.getStatus(manifest.id);
|
|
9218
9959
|
return {
|
|
9219
9960
|
id: manifest.id,
|
|
@@ -9230,7 +9971,12 @@ var ServiceAppManager = class {
|
|
|
9230
9971
|
lastError: runtimeStatus.lastError,
|
|
9231
9972
|
lastStartedAt: runtimeStatus.lastStartedAt,
|
|
9232
9973
|
lastReadyAt: runtimeStatus.lastReadyAt,
|
|
9233
|
-
lastFailedAt: runtimeStatus.lastFailedAt
|
|
9974
|
+
lastFailedAt: runtimeStatus.lastFailedAt,
|
|
9975
|
+
sourceKind: packageSource ? "package" : "workspace",
|
|
9976
|
+
packageId: packageSource?.packageId,
|
|
9977
|
+
packageVersion: packageSource?.packageVersion,
|
|
9978
|
+
packageDirectory: packageSource ? join(packageSource.sourcePath, "..", "..") : void 0,
|
|
9979
|
+
dataDirectory: packageSource?.dataDirectory
|
|
9234
9980
|
};
|
|
9235
9981
|
};
|
|
9236
9982
|
assertCaller = (caller) => {
|
|
@@ -9243,6 +9989,31 @@ var ServiceAppManager = class {
|
|
|
9243
9989
|
getWorkspacePath = () => getWorkspacePathFromConfig(this.params.configManager.config);
|
|
9244
9990
|
getServiceAppsPath = (workspacePath) => join(workspacePath, DEFAULT_SERVICE_APPS_DIR);
|
|
9245
9991
|
createGrantStore = () => new ServiceActionGrantStore(join(this.getServiceAppsPath(this.getWorkspacePath()), SERVICE_ACTION_GRANTS_FILE_NAME));
|
|
9992
|
+
listPackageComponentSources = async () => await this.params.listPackageComponentSources?.() ?? [];
|
|
9993
|
+
buildServiceAppRecordFromPackage = async (source) => {
|
|
9994
|
+
try {
|
|
9995
|
+
const manifest = await readServiceAppManifest(source.sourcePath);
|
|
9996
|
+
if (manifest.id !== source.id) throw new Error(`service component id mismatch: ${source.id}`);
|
|
9997
|
+
return this.toServiceAppRecord(source.sourcePath, manifest, source);
|
|
9998
|
+
} catch (error) {
|
|
9999
|
+
return {
|
|
10000
|
+
id: source.id,
|
|
10001
|
+
title: toTitle(source.id),
|
|
10002
|
+
dirPath: source.sourcePath,
|
|
10003
|
+
manifestPath: source.manifestPath,
|
|
10004
|
+
cwd: source.sourcePath,
|
|
10005
|
+
enabled: false,
|
|
10006
|
+
protocol: "mcp",
|
|
10007
|
+
status: "failed",
|
|
10008
|
+
lastError: error instanceof Error ? error.message : String(error),
|
|
10009
|
+
sourceKind: "package",
|
|
10010
|
+
packageId: source.packageId,
|
|
10011
|
+
packageVersion: source.packageVersion,
|
|
10012
|
+
packageDirectory: join(source.sourcePath, "..", ".."),
|
|
10013
|
+
dataDirectory: source.dataDirectory
|
|
10014
|
+
};
|
|
10015
|
+
}
|
|
10016
|
+
};
|
|
9246
10017
|
listServiceAppDirNames = async (serviceAppsPath) => {
|
|
9247
10018
|
try {
|
|
9248
10019
|
return (await readdir(serviceAppsPath, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
|
|
@@ -9456,7 +10227,7 @@ function parseFrontmatterBlock(raw) {
|
|
|
9456
10227
|
function parseYamlFrontmatter(raw) {
|
|
9457
10228
|
try {
|
|
9458
10229
|
const parsed = parse(raw);
|
|
9459
|
-
return isRecord$
|
|
10230
|
+
return isRecord$6(parsed) ? parsed : {};
|
|
9460
10231
|
} catch (error) {
|
|
9461
10232
|
const message = error instanceof Error ? error.message : String(error);
|
|
9462
10233
|
throw new Error(`Invalid SKILL.md frontmatter: ${message}`);
|
|
@@ -9468,7 +10239,7 @@ function readString$5(record, ...names) {
|
|
|
9468
10239
|
}
|
|
9469
10240
|
function readLocalizedTextMap(record, ...names) {
|
|
9470
10241
|
const value = readValue(record, names);
|
|
9471
|
-
if (!isRecord$
|
|
10242
|
+
if (!isRecord$6(value)) return;
|
|
9472
10243
|
const localized = Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0).map(([locale, text]) => [normalizeLocaleTag(locale), text.trim()]));
|
|
9473
10244
|
return Object.keys(localized).length > 0 ? localized : void 0;
|
|
9474
10245
|
}
|
|
@@ -9492,7 +10263,7 @@ function normalizeFrontmatterKey(raw) {
|
|
|
9492
10263
|
function normalizeLocaleTag(raw) {
|
|
9493
10264
|
return raw.trim().toLowerCase();
|
|
9494
10265
|
}
|
|
9495
|
-
function isRecord$
|
|
10266
|
+
function isRecord$6(value) {
|
|
9496
10267
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9497
10268
|
}
|
|
9498
10269
|
//#endregion
|
|
@@ -9639,23 +10410,61 @@ function resolveAgentHandoffDepth(metadata) {
|
|
|
9639
10410
|
}
|
|
9640
10411
|
//#endregion
|
|
9641
10412
|
//#region src/utils/ncp-agent-unfinished-run.utils.ts
|
|
9642
|
-
function
|
|
9643
|
-
|
|
9644
|
-
|
|
9645
|
-
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
...event.payload.startedAt ? { startedAt: event.payload.startedAt } : {}
|
|
9651
|
-
};
|
|
9652
|
-
continue;
|
|
9653
|
-
}
|
|
9654
|
-
if (activeRun && (event.type === NcpEventType.RunFinished || event.type === NcpEventType.RunError || event.type === NcpEventType.MessageAbort) && (!event.payload.runId || !activeRun.runId || event.payload.runId === activeRun.runId)) activeRun = null;
|
|
9655
|
-
}
|
|
10413
|
+
function applyNcpAgentRunLifecycleEvent(sessionId, activeRun, event) {
|
|
10414
|
+
if (event.type === NcpEventType.RunStarted) return {
|
|
10415
|
+
sessionId,
|
|
10416
|
+
...event.payload.messageId ? { messageId: event.payload.messageId } : {},
|
|
10417
|
+
...event.payload.runId ? { runId: event.payload.runId } : {},
|
|
10418
|
+
...event.payload.startedAt ? { startedAt: event.payload.startedAt } : {}
|
|
10419
|
+
};
|
|
10420
|
+
if (activeRun && (event.type === NcpEventType.RunFinished || event.type === NcpEventType.RunError || event.type === NcpEventType.MessageAbort) && (!event.payload.runId || !activeRun.runId || event.payload.runId === activeRun.runId)) return null;
|
|
9656
10421
|
return activeRun;
|
|
9657
10422
|
}
|
|
9658
10423
|
//#endregion
|
|
10424
|
+
//#region src/stores/ncp-agent-unfinished-run.store.ts
|
|
10425
|
+
var NcpAgentUnfinishedRunStore = class {
|
|
10426
|
+
constructor(journalDir, listSessionIds) {
|
|
10427
|
+
this.journalDir = journalDir;
|
|
10428
|
+
this.listSessionIds = listSessionIds;
|
|
10429
|
+
}
|
|
10430
|
+
list = async () => {
|
|
10431
|
+
const runs = [];
|
|
10432
|
+
for (const sessionId of await this.listSessionIds()) {
|
|
10433
|
+
const run = await this.read(sessionId);
|
|
10434
|
+
if (run) runs.push(run);
|
|
10435
|
+
}
|
|
10436
|
+
return runs;
|
|
10437
|
+
};
|
|
10438
|
+
read = async (sessionId) => {
|
|
10439
|
+
const input = createReadStream(this.sessionPath(sessionId), { encoding: "utf-8" });
|
|
10440
|
+
const lines = createInterface({
|
|
10441
|
+
input,
|
|
10442
|
+
crlfDelay: Infinity
|
|
10443
|
+
});
|
|
10444
|
+
let activeRun = null;
|
|
10445
|
+
try {
|
|
10446
|
+
for await (const line of lines) {
|
|
10447
|
+
if (!line.trim()) continue;
|
|
10448
|
+
let parsed;
|
|
10449
|
+
try {
|
|
10450
|
+
parsed = JSON.parse(line);
|
|
10451
|
+
} catch {
|
|
10452
|
+
continue;
|
|
10453
|
+
}
|
|
10454
|
+
if (!isRecord$13(parsed) || parsed._type !== "event" || !isRecord$13(parsed.event)) continue;
|
|
10455
|
+
activeRun = applyNcpAgentRunLifecycleEvent(sessionId, activeRun, parsed.event);
|
|
10456
|
+
}
|
|
10457
|
+
return activeRun;
|
|
10458
|
+
} catch {
|
|
10459
|
+
return null;
|
|
10460
|
+
} finally {
|
|
10461
|
+
lines.close();
|
|
10462
|
+
input.destroy();
|
|
10463
|
+
}
|
|
10464
|
+
};
|
|
10465
|
+
sessionPath = (sessionId) => join(this.journalDir, `${safeNcpSessionFilename(sessionId.replace(/:/g, "_"))}.jsonl`);
|
|
10466
|
+
};
|
|
10467
|
+
//#endregion
|
|
9659
10468
|
//#region src/utils/ncp-agent-session-journal-entry.utils.ts
|
|
9660
10469
|
function isNcpAgentSessionMessageProjectionBoundaryEvent(event) {
|
|
9661
10470
|
return event.type === NcpEventType.MessageSent || event.type === NcpEventType.MessageCompleted || event.type === NcpEventType.MessageAbort || event.type === NcpEventType.RunFinished || event.type === NcpEventType.RunError || event.type === "session.snapshot.message";
|
|
@@ -9663,11 +10472,11 @@ function isNcpAgentSessionMessageProjectionBoundaryEvent(event) {
|
|
|
9663
10472
|
function serializeNcpAgentSessionJournalEntry(entry) {
|
|
9664
10473
|
const serialized = JSON.stringify(entry);
|
|
9665
10474
|
if (!serialized) throw new Error("ncp agent session journal entry serialization produced empty output");
|
|
9666
|
-
if (!isRecord$
|
|
10475
|
+
if (!isRecord$13(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
|
|
9667
10476
|
return serialized;
|
|
9668
10477
|
}
|
|
9669
10478
|
function attachNcpAgentSessionJournalTimestamp(event, timestamp) {
|
|
9670
|
-
if (!("payload" in event) || !isRecord$
|
|
10479
|
+
if (!("payload" in event) || !isRecord$13(event.payload)) return event;
|
|
9671
10480
|
return {
|
|
9672
10481
|
...event,
|
|
9673
10482
|
payload: {
|
|
@@ -9692,7 +10501,7 @@ var NcpAgentSessionJournalParser = class {
|
|
|
9692
10501
|
this.applyMetadata(parsed);
|
|
9693
10502
|
return;
|
|
9694
10503
|
}
|
|
9695
|
-
if (parsed._type === "event" && isRecord$
|
|
10504
|
+
if (parsed._type === "event" && isRecord$13(parsed.event)) this.applyEvent(parsed, lineEndOffset);
|
|
9696
10505
|
};
|
|
9697
10506
|
finish = () => ({
|
|
9698
10507
|
metadata: this.metadata,
|
|
@@ -9706,14 +10515,14 @@ var NcpAgentSessionJournalParser = class {
|
|
|
9706
10515
|
parseLine = (line, index) => {
|
|
9707
10516
|
try {
|
|
9708
10517
|
const parsed = JSON.parse(line);
|
|
9709
|
-
return isRecord$
|
|
10518
|
+
return isRecord$13(parsed) ? parsed : null;
|
|
9710
10519
|
} catch (error) {
|
|
9711
10520
|
console.warn(`[ncp-agent-session-journal] skipped corrupted journal line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
|
|
9712
10521
|
return null;
|
|
9713
10522
|
}
|
|
9714
10523
|
};
|
|
9715
10524
|
applyMetadata = (entry) => {
|
|
9716
|
-
this.metadata = isRecord$
|
|
10525
|
+
this.metadata = isRecord$13(entry.metadata) ? structuredClone(entry.metadata) : {};
|
|
9717
10526
|
this.agentId = normalizeNcpAgentId(typeof entry.agent_id === "string" ? entry.agent_id : void 0);
|
|
9718
10527
|
this.createdAt = toIsoString(entry.created_at, this.createdAt);
|
|
9719
10528
|
this.updatedAt = toIsoString(entry.updated_at, this.updatedAt);
|
|
@@ -9740,25 +10549,6 @@ function parseNcpAgentSessionJournal(raw) {
|
|
|
9740
10549
|
return parser.finish();
|
|
9741
10550
|
}
|
|
9742
10551
|
//#endregion
|
|
9743
|
-
//#region src/stores/ncp-agent-unfinished-run.store.ts
|
|
9744
|
-
var NcpAgentUnfinishedRunStore = class {
|
|
9745
|
-
constructor(journalDir, listSessionIds) {
|
|
9746
|
-
this.journalDir = journalDir;
|
|
9747
|
-
this.listSessionIds = listSessionIds;
|
|
9748
|
-
}
|
|
9749
|
-
list = async () => {
|
|
9750
|
-
return (await Promise.all((await this.listSessionIds()).map(this.read))).filter((run) => run !== null);
|
|
9751
|
-
};
|
|
9752
|
-
read = async (sessionId) => {
|
|
9753
|
-
try {
|
|
9754
|
-
return readUnfinishedNcpAgentRun(sessionId, parseNcpAgentSessionJournal(await readFile(this.sessionPath(sessionId), "utf-8")).events);
|
|
9755
|
-
} catch {
|
|
9756
|
-
return null;
|
|
9757
|
-
}
|
|
9758
|
-
};
|
|
9759
|
-
sessionPath = (sessionId) => join(this.journalDir, `${safeNcpSessionFilename(sessionId.replace(/:/g, "_"))}.jsonl`);
|
|
9760
|
-
};
|
|
9761
|
-
//#endregion
|
|
9762
10552
|
//#region src/stores/ncp-agent-session-metadata.store.ts
|
|
9763
10553
|
var NcpAgentSessionMetadataStore = class {
|
|
9764
10554
|
constructor(journalDir) {
|
|
@@ -9779,7 +10569,7 @@ var NcpAgentSessionMetadataStore = class {
|
|
|
9779
10569
|
read = async (sessionId, activitySnapshot) => {
|
|
9780
10570
|
try {
|
|
9781
10571
|
const parsed = JSON.parse(await readFile(this.metadataPath(sessionId), "utf-8"));
|
|
9782
|
-
if (!isRecord$
|
|
10572
|
+
if (!isRecord$13(parsed) || parsed._type !== "metadata" || !isRecord$13(parsed.metadata)) throw new Error(`invalid ncp agent session metadata sidecar: ${sessionId}`);
|
|
9783
10573
|
const createdAt = toIsoString(parsed.created_at, activitySnapshot.createdAt);
|
|
9784
10574
|
const agentId = normalizeNcpAgentId(typeof parsed.agent_id === "string" ? parsed.agent_id : void 0);
|
|
9785
10575
|
return {
|
|
@@ -9849,7 +10639,7 @@ function deduplicateNcpAgentSessionTailMessages(messages) {
|
|
|
9849
10639
|
function isNcpAgentSessionMessageProjectionMeta(value, sessionId) {
|
|
9850
10640
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
9851
10641
|
const meta = value;
|
|
9852
|
-
return meta.version === 6 && meta.sessionId === sessionId && Number.isSafeInteger(meta.total) && Number.isSafeInteger(meta.projectedJournalOffset) && Number.isSafeInteger(meta.dataBytes) && (meta.activeMessageId === null || typeof meta.activeMessageId === "string") && Array.isArray(meta.pendingCompactionMessageIds) && meta.pendingCompactionMessageIds.every((id) => typeof id === "string") && (meta.contextWindow === null || isRecord$
|
|
10642
|
+
return meta.version === 6 && meta.sessionId === sessionId && Number.isSafeInteger(meta.total) && Number.isSafeInteger(meta.projectedJournalOffset) && Number.isSafeInteger(meta.dataBytes) && (meta.activeMessageId === null || typeof meta.activeMessageId === "string") && Array.isArray(meta.pendingCompactionMessageIds) && meta.pendingCompactionMessageIds.every((id) => typeof id === "string") && (meta.contextWindow === null || isRecord$5(meta.contextWindow));
|
|
9853
10643
|
}
|
|
9854
10644
|
function readActiveAssistantMessageId(messages) {
|
|
9855
10645
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
@@ -9872,12 +10662,12 @@ function mergePendingCompactionMessageIds(current, messages) {
|
|
|
9872
10662
|
}
|
|
9873
10663
|
return pending;
|
|
9874
10664
|
}
|
|
9875
|
-
function isRecord$
|
|
10665
|
+
function isRecord$5(value) {
|
|
9876
10666
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9877
10667
|
}
|
|
9878
10668
|
function readCompactionStatus(message) {
|
|
9879
10669
|
const checkpoint = message.metadata?.checkpoint;
|
|
9880
|
-
if (message.metadata?.nextclaw_timeline_kind !== "context_compaction" || !isRecord$
|
|
10670
|
+
if (message.metadata?.nextclaw_timeline_kind !== "context_compaction" || !isRecord$5(checkpoint)) return null;
|
|
9881
10671
|
return typeof checkpoint.status === "string" ? checkpoint.status : null;
|
|
9882
10672
|
}
|
|
9883
10673
|
//#endregion
|
|
@@ -11894,7 +12684,7 @@ var CurrentSessionContextProvider = class {
|
|
|
11894
12684
|
const MAX_EXCERPT_COUNT = 16;
|
|
11895
12685
|
const MAX_EXCERPT_CHARACTERS$1 = 8e3;
|
|
11896
12686
|
const MAX_TOTAL_CONTEXT_CHARACTERS$1 = 96e3;
|
|
11897
|
-
function isRecord$
|
|
12687
|
+
function isRecord$4(value) {
|
|
11898
12688
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
11899
12689
|
}
|
|
11900
12690
|
function readString$3(value) {
|
|
@@ -11905,11 +12695,11 @@ function escapeAttribute$1(value) {
|
|
|
11905
12695
|
}
|
|
11906
12696
|
function readConversationExcerpts(metadata) {
|
|
11907
12697
|
const raw = metadata?.[CHAT_INLINE_TOKENS_METADATA_KEY];
|
|
11908
|
-
const entries = isRecord$
|
|
12698
|
+
const entries = isRecord$4(raw) && raw.schemaVersion === CHAT_INLINE_TOKENS_SCHEMA_VERSION && Array.isArray(raw.items) ? raw.items : [];
|
|
11909
12699
|
const excerpts = [];
|
|
11910
12700
|
const seen = /* @__PURE__ */ new Set();
|
|
11911
12701
|
for (const entry of entries) {
|
|
11912
|
-
if (!isRecord$
|
|
12702
|
+
if (!isRecord$4(entry) || entry.kind !== CHAT_CONVERSATION_EXCERPT_TOKEN_KIND) continue;
|
|
11913
12703
|
const key = readString$3(entry.key);
|
|
11914
12704
|
const messageId = readString$3(entry.messageId);
|
|
11915
12705
|
const label = readString$3(entry.label);
|
|
@@ -11954,27 +12744,63 @@ var ConversationExcerptContextProvider = class {
|
|
|
11954
12744
|
};
|
|
11955
12745
|
};
|
|
11956
12746
|
//#endregion
|
|
11957
|
-
//#region src/contributions/context-provider/providers/
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
12747
|
+
//#region src/contributions/context-provider/providers/system-object-reference-context.provider.ts
|
|
12748
|
+
function isRecord$3(value) {
|
|
12749
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
12750
|
+
}
|
|
12751
|
+
function readSystemObjectReferences(metadata) {
|
|
12752
|
+
const raw = metadata?.[CHAT_INLINE_TOKENS_METADATA_KEY];
|
|
12753
|
+
if (!isRecord$3(raw) || raw.schemaVersion !== CHAT_INLINE_TOKENS_SCHEMA_VERSION || !Array.isArray(raw.items)) return [];
|
|
12754
|
+
const references = [];
|
|
12755
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12756
|
+
for (const item of raw.items) {
|
|
12757
|
+
if (!isRecord$3(item) || item.kind !== CHAT_SYSTEM_OBJECT_TOKEN_KIND) continue;
|
|
12758
|
+
const reference = readSystemObjectResolvedReference(item.reference);
|
|
12759
|
+
if (!reference || seen.has(`${reference.uri}@${reference.version}`)) continue;
|
|
12760
|
+
seen.add(`${reference.uri}@${reference.version}`);
|
|
12761
|
+
references.push(reference);
|
|
12762
|
+
}
|
|
12763
|
+
return references;
|
|
12764
|
+
}
|
|
12765
|
+
var SystemObjectReferenceContextProvider = class {
|
|
12766
|
+
constructor(assetStore) {
|
|
12767
|
+
this.assetStore = assetStore;
|
|
11962
12768
|
}
|
|
11963
12769
|
provide = async (request) => {
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
12770
|
+
const references = readSystemObjectReferences(request.message.metadata ?? request.metadata);
|
|
12771
|
+
if (references.length === 0) return [];
|
|
12772
|
+
return [[
|
|
12773
|
+
"## Explicit System Object References",
|
|
12774
|
+
"The user visibly referenced these immutable NextClaw-managed object snapshots in the current message.",
|
|
12775
|
+
"",
|
|
12776
|
+
(await Promise.all(references.map(async (reference) => {
|
|
12777
|
+
const asset = await this.assetStore.statRecord(reference.assetUri);
|
|
12778
|
+
if (!asset || asset.sha256 !== reference.version || asset.sizeBytes !== reference.sizeBytes || asset.fileName !== reference.fileName || asset.mimeType !== reference.mimeType || !isTextLikeAsset({
|
|
12779
|
+
mimeType: asset.mimeType,
|
|
12780
|
+
fileName: asset.fileName
|
|
12781
|
+
})) return [
|
|
12782
|
+
`### ${reference.label}`,
|
|
12783
|
+
`Object: ${reference.uri}`,
|
|
12784
|
+
`Version: ${reference.version}`,
|
|
12785
|
+
"Snapshot unavailable. Do not fall back to the live object."
|
|
12786
|
+
].join("\n");
|
|
12787
|
+
const bytes = await this.assetStore.readAssetBytes(reference.assetUri);
|
|
12788
|
+
if (!bytes) return [
|
|
12789
|
+
`### ${reference.label}`,
|
|
12790
|
+
`Object: ${reference.uri}`,
|
|
12791
|
+
`Version: ${reference.version}`,
|
|
12792
|
+
"Snapshot unavailable. Do not fall back to the live object."
|
|
12793
|
+
].join("\n");
|
|
12794
|
+
return [
|
|
12795
|
+
`### ${reference.label}`,
|
|
12796
|
+
`Object: ${reference.uri}`,
|
|
12797
|
+
`Type: ${reference.objectType}`,
|
|
12798
|
+
`Version: ${reference.version}`,
|
|
12799
|
+
"",
|
|
12800
|
+
bytes.toString("utf8")
|
|
12801
|
+
].join("\n");
|
|
12802
|
+
}))).join("\n\n")
|
|
12803
|
+
].join("\n")];
|
|
11978
12804
|
};
|
|
11979
12805
|
};
|
|
11980
12806
|
//#endregion
|
|
@@ -12051,23 +12877,24 @@ const createCliQuickReferenceContextProvider = () => {
|
|
|
12051
12877
|
return staticBlock([
|
|
12052
12878
|
`## ${APP_NAME} CLI Quick Reference`,
|
|
12053
12879
|
`${APP_NAME} is controlled via subcommands. Do not invent commands.`,
|
|
12054
|
-
"To manage the
|
|
12055
|
-
`- ${appLower}
|
|
12056
|
-
`- ${appLower}
|
|
12057
|
-
`- ${appLower}
|
|
12058
|
-
`- ${appLower}
|
|
12059
|
-
`
|
|
12880
|
+
"To manage the background service:",
|
|
12881
|
+
`- ${appLower} status`,
|
|
12882
|
+
`- ${appLower} start`,
|
|
12883
|
+
`- ${appLower} restart`,
|
|
12884
|
+
`- ${appLower} stop`,
|
|
12885
|
+
`The \`${appLower} gateway\` command starts a foreground gateway; it has no lifecycle subcommands.`,
|
|
12886
|
+
`If unsure, ask the user to run \`${appLower} help\` and paste the output.`
|
|
12060
12887
|
]);
|
|
12061
12888
|
};
|
|
12062
12889
|
const createSelfUpdateContextProvider = () => staticBlock([
|
|
12063
12890
|
`## ${APP_NAME} Self-Update`,
|
|
12064
12891
|
"Get Updates (self-update) is ONLY allowed when the user explicitly asks for it.",
|
|
12065
12892
|
"Do not run config.apply or update.run unless the user explicitly requests an update or config change; if it's not explicit, ask first.",
|
|
12066
|
-
"Actions: config.get, config.schema, config.apply (validate + write full config
|
|
12893
|
+
"Actions: config.get, config.schema, config.apply (validate + write full config), config.patch (merge config), update.run (update the runtime and relaunch the service).",
|
|
12067
12894
|
"When patching config, copy enum values exactly from config.schema; never invent new variants.",
|
|
12068
12895
|
"session.dmScope legal values are exactly: main | per-peer | per-channel-peer | per-account-channel-peer.",
|
|
12069
12896
|
"If an enum/path is uncertain, stop and call config.schema first; do not guess.",
|
|
12070
|
-
`
|
|
12897
|
+
`If a config change requires restart, tell the user to run \`${APP_NAME.toLowerCase()} restart\` in an external terminal. Do not run it from the active agent session.`
|
|
12071
12898
|
]);
|
|
12072
12899
|
const createReplyTagsContextProvider = () => staticBlock([
|
|
12073
12900
|
"## Reply Tags",
|
|
@@ -12830,7 +13657,7 @@ var ContextProviderContribution = class {
|
|
|
12830
13657
|
new SkillsContextProvider(context),
|
|
12831
13658
|
createSessionOrchestrationContextProvider(),
|
|
12832
13659
|
new ExecutionPolicyContextProvider(context),
|
|
12833
|
-
new
|
|
13660
|
+
new SystemObjectReferenceContextProvider(this.kernel.assetStore),
|
|
12834
13661
|
new CurrentSessionContextProvider(context),
|
|
12835
13662
|
new ReplyFormatContextProvider()
|
|
12836
13663
|
]) this.cleanups.push(this.kernel.contextProviderManager.register(provider));
|
|
@@ -14186,6 +15013,10 @@ var ToolProviderContribution = class {
|
|
|
14186
15013
|
};
|
|
14187
15014
|
//#endregion
|
|
14188
15015
|
//#region src/app/nextclaw-kernel.ts
|
|
15016
|
+
function resolveKernelAppHomeDirectory(options) {
|
|
15017
|
+
const homeDir = options.homeDir?.trim();
|
|
15018
|
+
return resolve(homeDir ? expandHome(homeDir) : getDataDir(), "apps");
|
|
15019
|
+
}
|
|
14189
15020
|
function resolveKernelSessionsDir(options) {
|
|
14190
15021
|
const homeDir = options.homeDir?.trim();
|
|
14191
15022
|
if (homeDir) return ensureDir(resolve(expandHome(homeDir), "sessions"));
|
|
@@ -14234,6 +15065,7 @@ var NextclawKernel = class {
|
|
|
14234
15065
|
control;
|
|
14235
15066
|
skills;
|
|
14236
15067
|
automation;
|
|
15068
|
+
appPackageManager;
|
|
14237
15069
|
channels;
|
|
14238
15070
|
sessionRequests;
|
|
14239
15071
|
sessionSearch;
|
|
@@ -14241,6 +15073,7 @@ var NextclawKernel = class {
|
|
|
14241
15073
|
mcpManager;
|
|
14242
15074
|
sessionManager;
|
|
14243
15075
|
inboxDeliveryManager;
|
|
15076
|
+
systemObjectReferenceManager;
|
|
14244
15077
|
panelAppManager;
|
|
14245
15078
|
preferenceManager;
|
|
14246
15079
|
projectManager;
|
|
@@ -14295,16 +15128,26 @@ var NextclawKernel = class {
|
|
|
14295
15128
|
});
|
|
14296
15129
|
this.inboxDeliveryManager = new InboxDeliveryManager({
|
|
14297
15130
|
eventBus: this.eventBus,
|
|
14298
|
-
sessionManager: this.sessionManager,
|
|
14299
15131
|
storePath: resolveKernelInboxDeliveryStorePath(options)
|
|
14300
15132
|
});
|
|
15133
|
+
this.systemObjectReferenceManager = new SystemObjectReferenceManager(this.assetStore, [createInboxDeliverySystemObjectProvider(this.inboxDeliveryManager), createCronJobSystemObjectProvider(this.automation)]);
|
|
15134
|
+
this.appPackageManager = new AppPackageManager({
|
|
15135
|
+
appHomeDirectory: resolveKernelAppHomeDirectory(options),
|
|
15136
|
+
builtInAppsDirectory: options.builtInAppsDirectory,
|
|
15137
|
+
productVersion: options.productVersion
|
|
15138
|
+
});
|
|
14301
15139
|
this.panelAppManager = new PanelAppManager({
|
|
14302
15140
|
configManager: this.configManager,
|
|
14303
15141
|
eventBus: this.eventBus,
|
|
14304
|
-
ingress: this.ingress
|
|
15142
|
+
ingress: this.ingress,
|
|
15143
|
+
listPackageComponentSources: this.appPackageManager.listActiveComponentSources
|
|
14305
15144
|
});
|
|
14306
15145
|
this.preferenceManager = new PreferenceManager({ storePath: resolveKernelPreferenceStorePath(options) });
|
|
14307
|
-
this.serviceAppManager = new ServiceAppManager({
|
|
15146
|
+
this.serviceAppManager = new ServiceAppManager({
|
|
15147
|
+
configManager: this.configManager,
|
|
15148
|
+
listPackageComponentSources: this.appPackageManager.listActiveComponentSources
|
|
15149
|
+
});
|
|
15150
|
+
this.installAppPackageRuntimeHooks();
|
|
14308
15151
|
this.extensions = new ExtensionManager({
|
|
14309
15152
|
configManager: this.configManager,
|
|
14310
15153
|
eventBus: this.eventBus,
|
|
@@ -14344,6 +15187,22 @@ var NextclawKernel = class {
|
|
|
14344
15187
|
new ContextWindowContribution(this)
|
|
14345
15188
|
];
|
|
14346
15189
|
}
|
|
15190
|
+
installAppPackageRuntimeHooks = () => {
|
|
15191
|
+
this.appPackageManager.installRuntimeHooks({
|
|
15192
|
+
assertCanActivate: async (sources) => {
|
|
15193
|
+
await this.panelAppManager.assertCanActivatePackageComponents(sources);
|
|
15194
|
+
await this.serviceAppManager.assertCanActivatePackageComponents(sources);
|
|
15195
|
+
},
|
|
15196
|
+
beforeDeactivate: async (sources) => {
|
|
15197
|
+
this.panelAppManager.deactivatePackageComponents(sources);
|
|
15198
|
+
await this.serviceAppManager.deactivatePackageComponents(sources);
|
|
15199
|
+
},
|
|
15200
|
+
beforeUninstall: async (sources) => {
|
|
15201
|
+
await this.panelAppManager.removePackageComponentState(sources);
|
|
15202
|
+
await this.serviceAppManager.removePackageComponentGrants(sources);
|
|
15203
|
+
}
|
|
15204
|
+
});
|
|
15205
|
+
};
|
|
14347
15206
|
listSessionTypes = (params) => this.agentRuntimeManager.listSessionTypes(params);
|
|
14348
15207
|
isSessionRunning = (sessionId) => this.sessionRunManager.isSessionRunning(sessionId);
|
|
14349
15208
|
provideGatewayController = (gatewayController) => {
|
|
@@ -14944,6 +15803,6 @@ function resolveLegacyEventType(message) {
|
|
|
14944
15803
|
return `message.${role || "other"}`;
|
|
14945
15804
|
}
|
|
14946
15805
|
//#endregion
|
|
14947
|
-
export { AUTOMATIC_UPDATE_CHECK_INTERVAL_MS, AccessManager, AgentManager, AgentRunClient, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_CONTINUATION_TEXT, CONTEXT_COMPACTION_PROJECTION_KIND, CONTEXT_COMPACTION_PROJECTION_METADATA_KEY, CONTEXT_COMPACTION_SYSTEM_PREAMBLE, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionJournalRecoveryService, ContextCompactionPreflightService, DEFAULT_AGENT_RUNTIME_ENTRY_ID, DEFAULT_SERVICE_ACTION_RISK, ExtensionManager, GatewayInboundProcessor, InboxDeliveryError, InboxDeliveryManager, LlmProviderManager, LlmUsageManager, LlmUsageStore, MAX_INBOX_DELIVERY_CONTENT_LENGTH, McpManager, McpServiceAppRuntimeService, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawKernel, PANEL_APP_AGENT_CAPABILITIES, PROJECT_TEMPLATE_IDS, PROVIDER_MODEL_CATALOG_REFRESH_INTERVAL_MS, PanelAppAssetTokenService, PanelAppError, PanelAppManager, PreferenceError, PreferenceManager, ProjectError, ProjectManager, ProviderManagerNcpLLMApi, ProviderModelCatalogManager, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionContextCompactionError, SessionContextCompactionManager, SessionManager, SessionMessageCursorError, SessionRequestManager, SessionSettingsError, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionModelProjection, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildServiceActionId, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createLlmUsageRecord, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getAutomaticUpdateCheckDelay, getServiceActionName, getServiceAppManifestPath, getUiContentParamsBootstrapScript, getUnsignedUpdateManifest, hasLlmUsageTelemetry, injectUiContentParamsBootstrap, isContextCompactionProjectionMessage, isContextCompactionTimelineMessage, isContextWindowSnapshot, isInboxDeliveryError, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isProjectError, isReplyCapableChannel, isServiceAppError, isSessionContextCompactionError, isSessionMessageCursorError, isSessionSettingsError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, readContextCompactionCheckpoint, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveAutomaticUpdateCheckIntervalMs, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, waitForAgentRuntimeSessionReply };
|
|
15806
|
+
export { AUTOMATIC_UPDATE_CHECK_INTERVAL_MS, AccessManager, AgentManager, AgentRunClient, AppPackageError, AppPackageManager, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_CONTINUATION_TEXT, CONTEXT_COMPACTION_PROJECTION_KIND, CONTEXT_COMPACTION_PROJECTION_METADATA_KEY, CONTEXT_COMPACTION_SYSTEM_PREAMBLE, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionJournalRecoveryService, ContextCompactionPreflightService, DEFAULT_AGENT_RUNTIME_ENTRY_ID, DEFAULT_SERVICE_ACTION_RISK, ExtensionManager, GatewayInboundProcessor, InboxDeliveryError, InboxDeliveryManager, LlmProviderManager, LlmUsageManager, LlmUsageStore, MAX_INBOX_DELIVERY_CONTENT_LENGTH, McpManager, McpServiceAppRuntimeService, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawKernel, PANEL_APP_AGENT_CAPABILITIES, PROJECT_TEMPLATE_IDS, PROVIDER_MODEL_CATALOG_REFRESH_INTERVAL_MS, PanelAppAssetTokenService, PanelAppError, PanelAppManager, PreferenceError, PreferenceManager, ProjectError, ProjectManager, ProviderManagerNcpLLMApi, ProviderModelCatalogManager, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppError, ServiceAppManager, SessionContextCompactionError, SessionContextCompactionManager, SessionManager, SessionMessageCursorError, SessionRequestManager, SessionSettingsError, SkillManager, SystemObjectReferenceError, SystemObjectReferenceManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionModelProjection, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildServiceActionId, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextCompactionMessageId, createContextWindowSignature, createCronJobSystemObjectProvider, createInboxDeliverySystemObjectProvider, createLlmUsageRecord, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getAutomaticUpdateCheckDelay, getServiceActionName, getServiceAppManifestPath, getUiContentParamsBootstrapScript, getUnsignedUpdateManifest, hasLlmUsageTelemetry, injectUiContentParamsBootstrap, isAppPackageError, isContextCompactionProjectionMessage, isContextCompactionTimelineMessage, isContextWindowSnapshot, isInboxDeliveryError, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isProjectError, isReplyCapableChannel, isServiceAppError, isSessionContextCompactionError, isSessionMessageCursorError, isSessionSettingsError, isSystemObjectReferenceError, listExtensionChannelIds, listServiceAppManifestActions, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseServiceAppManifest, parseSkillFrontmatter, readContextCompactionCheckpoint, readContextWindowEventSessionId, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceAppManifest, resolveAgentRuntimeEntries, resolveAutomaticUpdateCheckIntervalMs, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, waitForAgentRuntimeSessionReply };
|
|
14948
15807
|
|
|
14949
15808
|
//# sourceMappingURL=index.js.map
|