@nextclaw/kernel 0.6.24 → 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 +2388 -1550
- package/dist/index.js.map +1 -1
- package/package.json +11 -10
package/dist/index.js
CHANGED
|
@@ -3,14 +3,15 @@ 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
10
|
import { appendFileSync, chmodSync, constants, createReadStream, 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";
|
|
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";
|
|
@@ -1232,7 +1233,7 @@ const SESSION_ACTIVITY_PREVIEW_STATUS_KINDS = new Set([
|
|
|
1232
1233
|
"run-failed",
|
|
1233
1234
|
"run-interrupted"
|
|
1234
1235
|
]);
|
|
1235
|
-
function isRecord$
|
|
1236
|
+
function isRecord$16(value) {
|
|
1236
1237
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1237
1238
|
}
|
|
1238
1239
|
function readOptionalString$12(value) {
|
|
@@ -1241,7 +1242,7 @@ function readOptionalString$12(value) {
|
|
|
1241
1242
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
1242
1243
|
}
|
|
1243
1244
|
function readSessionActivityPreviewMetadata(value) {
|
|
1244
|
-
if (!isRecord$
|
|
1245
|
+
if (!isRecord$16(value)) return null;
|
|
1245
1246
|
const state = value.state;
|
|
1246
1247
|
const timestamp = readOptionalString$12(value.timestamp);
|
|
1247
1248
|
if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
|
|
@@ -1335,7 +1336,7 @@ var SessionActivityPreviewEventService = class {
|
|
|
1335
1336
|
//#endregion
|
|
1336
1337
|
//#region src/managers/agent-run-session-command.manager.ts
|
|
1337
1338
|
const CONTINUATION_PROMPT = "Continue from where you stopped. Preserve completed work and avoid repeating it.";
|
|
1338
|
-
function isRecord$
|
|
1339
|
+
function isRecord$15(value) {
|
|
1339
1340
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1340
1341
|
}
|
|
1341
1342
|
function hasSendableMessageContent(message) {
|
|
@@ -1343,7 +1344,7 @@ function hasSendableMessageContent(message) {
|
|
|
1343
1344
|
}
|
|
1344
1345
|
function readSessionActivityState(metadata) {
|
|
1345
1346
|
const preview = metadata?.[SESSION_ACTIVITY_PREVIEW_METADATA_KEY];
|
|
1346
|
-
return isRecord$
|
|
1347
|
+
return isRecord$15(preview) && typeof preview.state === "string" ? preview.state : null;
|
|
1347
1348
|
}
|
|
1348
1349
|
var AgentRunSessionCommandManager = class {
|
|
1349
1350
|
pendingCommands = /* @__PURE__ */ new Map();
|
|
@@ -2504,6 +2505,318 @@ var AutomationManager = class extends CronService {
|
|
|
2504
2505
|
}
|
|
2505
2506
|
};
|
|
2506
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
|
|
2507
2820
|
//#region src/managers/channel.manager.ts
|
|
2508
2821
|
var ChannelManager = class {
|
|
2509
2822
|
channels = {};
|
|
@@ -4551,11 +4864,11 @@ var ExtensionManager = class {
|
|
|
4551
4864
|
//#endregion
|
|
4552
4865
|
//#region src/utils/model-message-vision.utils.ts
|
|
4553
4866
|
const IMAGE_OMITTED_TEXT = "[Image omitted: the selected model is not configured for vision input.]";
|
|
4554
|
-
function isRecord$
|
|
4867
|
+
function isRecord$14(value) {
|
|
4555
4868
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4556
4869
|
}
|
|
4557
4870
|
function isImageContentPart(value) {
|
|
4558
|
-
if (!isRecord$
|
|
4871
|
+
if (!isRecord$14(value)) return false;
|
|
4559
4872
|
const type = value.type;
|
|
4560
4873
|
return type === "image_url" || type === "input_image";
|
|
4561
4874
|
}
|
|
@@ -4571,7 +4884,7 @@ function normalizeContentWithoutVision(content) {
|
|
|
4571
4884
|
};
|
|
4572
4885
|
});
|
|
4573
4886
|
if (!sawImage) return content;
|
|
4574
|
-
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);
|
|
4575
4888
|
if (textParts.length === parts.length) return textParts.join("\n\n");
|
|
4576
4889
|
return parts;
|
|
4577
4890
|
}
|
|
@@ -5206,7 +5519,8 @@ var LlmUsageManager = class {
|
|
|
5206
5519
|
};
|
|
5207
5520
|
//#endregion
|
|
5208
5521
|
//#region src/stores/inbox-delivery.store.ts
|
|
5209
|
-
const INBOX_DELIVERY_STORE_VERSION =
|
|
5522
|
+
const INBOX_DELIVERY_STORE_VERSION = 2;
|
|
5523
|
+
const LEGACY_INBOX_DELIVERY_STORE_VERSION = 1;
|
|
5210
5524
|
var InboxDeliveryStoreError = class extends Error {
|
|
5211
5525
|
constructor(message) {
|
|
5212
5526
|
super(message);
|
|
@@ -5243,15 +5557,19 @@ var InboxDeliveryStore = class {
|
|
|
5243
5557
|
};
|
|
5244
5558
|
parseStoreFile = (source) => {
|
|
5245
5559
|
const value = JSON.parse(source);
|
|
5246
|
-
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");
|
|
5247
5561
|
return {
|
|
5248
5562
|
version: INBOX_DELIVERY_STORE_VERSION,
|
|
5249
|
-
deliveries: value.deliveries.map((delivery) =>
|
|
5563
|
+
deliveries: value.deliveries.map((delivery) => this.toCurrentDelivery(delivery))
|
|
5250
5564
|
};
|
|
5251
5565
|
};
|
|
5252
|
-
isDelivery = (value) => {
|
|
5566
|
+
isDelivery = (value, legacy) => {
|
|
5253
5567
|
if (!this.isRecord(value) || !this.isSource(value.source)) return false;
|
|
5254
|
-
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);
|
|
5255
5573
|
};
|
|
5256
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);
|
|
5257
5575
|
isOptionalString = (value) => value === null || typeof value === "string";
|
|
@@ -5309,8 +5627,7 @@ var InboxDeliveryManager = class {
|
|
|
5309
5627
|
updatedAt: now,
|
|
5310
5628
|
presentedAt: null,
|
|
5311
5629
|
readAt: null,
|
|
5312
|
-
archivedAt: null
|
|
5313
|
-
conversationSessionId: null
|
|
5630
|
+
archivedAt: null
|
|
5314
5631
|
};
|
|
5315
5632
|
const deliveries = await this.store.list();
|
|
5316
5633
|
await this.store.save([delivery, ...deliveries]);
|
|
@@ -5337,41 +5654,6 @@ var InboxDeliveryManager = class {
|
|
|
5337
5654
|
this.publishChange(deliveryId, "delete");
|
|
5338
5655
|
return true;
|
|
5339
5656
|
});
|
|
5340
|
-
continueInChat = async (deliveryId) => await this.mutate(async () => {
|
|
5341
|
-
const deliveries = await this.store.list();
|
|
5342
|
-
const index = deliveries.findIndex(({ id }) => id === deliveryId);
|
|
5343
|
-
if (index < 0) throw this.notFound(deliveryId);
|
|
5344
|
-
const current = deliveries[index];
|
|
5345
|
-
const existingSession = current.conversationSessionId ? await this.options.sessionManager.getSessionRecord(current.conversationSessionId) : null;
|
|
5346
|
-
let sessionId = current.conversationSessionId;
|
|
5347
|
-
let created = false;
|
|
5348
|
-
if (!sessionId || !existingSession) {
|
|
5349
|
-
sessionId = (await this.options.sessionManager.createSession({
|
|
5350
|
-
sourceSessionMetadata: {},
|
|
5351
|
-
metadataOverrides: { [INBOX_DELIVERY_SESSION_METADATA_KEY]: current.id },
|
|
5352
|
-
task: `Continue discussing inbox delivery: ${current.title}`,
|
|
5353
|
-
title: current.title,
|
|
5354
|
-
agentId: current.source.agentId ?? void 0
|
|
5355
|
-
})).sessionId;
|
|
5356
|
-
created = true;
|
|
5357
|
-
}
|
|
5358
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5359
|
-
const delivery = {
|
|
5360
|
-
...current,
|
|
5361
|
-
updatedAt: now,
|
|
5362
|
-
presentedAt: current.presentedAt ?? now,
|
|
5363
|
-
readAt: current.readAt ?? now,
|
|
5364
|
-
conversationSessionId: sessionId
|
|
5365
|
-
};
|
|
5366
|
-
deliveries[index] = delivery;
|
|
5367
|
-
await this.store.save(deliveries);
|
|
5368
|
-
this.publishChange(deliveryId, "upsert");
|
|
5369
|
-
return {
|
|
5370
|
-
delivery: structuredClone(delivery),
|
|
5371
|
-
sessionId,
|
|
5372
|
-
created
|
|
5373
|
-
};
|
|
5374
|
-
});
|
|
5375
5657
|
applyStateAction = (delivery, action, now) => {
|
|
5376
5658
|
switch (action) {
|
|
5377
5659
|
case "present": return {
|
|
@@ -5427,6 +5709,300 @@ var InboxDeliveryManager = class {
|
|
|
5427
5709
|
};
|
|
5428
5710
|
};
|
|
5429
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
|
|
5430
6006
|
//#region src/managers/mcp.manager.ts
|
|
5431
6007
|
var McpManager = class {
|
|
5432
6008
|
currentMcpConfig;
|
|
@@ -5521,7 +6097,7 @@ function safeNcpSessionFilename(value) {
|
|
|
5521
6097
|
function normalizeNcpAgentId(agentId) {
|
|
5522
6098
|
return agentId?.trim().toLowerCase() || void 0;
|
|
5523
6099
|
}
|
|
5524
|
-
function isRecord$
|
|
6100
|
+
function isRecord$13(value) {
|
|
5525
6101
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5526
6102
|
}
|
|
5527
6103
|
function toIsoString(value, fallback) {
|
|
@@ -5651,7 +6227,7 @@ function mergeReplayCompletedToolResults(message, toolResultsByCallId) {
|
|
|
5651
6227
|
} : message;
|
|
5652
6228
|
}
|
|
5653
6229
|
function readLegacyContextCompactionMessageId(message) {
|
|
5654
|
-
const checkpoint = isRecord$
|
|
6230
|
+
const checkpoint = isRecord$13(message?.metadata?.checkpoint) ? message.metadata.checkpoint : null;
|
|
5655
6231
|
const checkpointId = typeof checkpoint?.id === "string" ? checkpoint.id : "";
|
|
5656
6232
|
const coveredCount = checkpoint?.coveredSessionMessageCount;
|
|
5657
6233
|
const legacyId = `${message?.sessionId}:service:context-compaction:${checkpointId}`;
|
|
@@ -5683,11 +6259,11 @@ function readReplayMessageId(event) {
|
|
|
5683
6259
|
return readMessageFromSummaryEvent(event)?.id ?? null;
|
|
5684
6260
|
}
|
|
5685
6261
|
function readEventSessionId$2(event) {
|
|
5686
|
-
const sessionId = ("payload" in event && isRecord$
|
|
6262
|
+
const sessionId = ("payload" in event && isRecord$13(event.payload) ? event.payload : null)?.sessionId;
|
|
5687
6263
|
return typeof sessionId === "string" ? sessionId : "";
|
|
5688
6264
|
}
|
|
5689
6265
|
function readReplayPayloadTimestamp(event) {
|
|
5690
|
-
const payload = "payload" in event && isRecord$
|
|
6266
|
+
const payload = "payload" in event && isRecord$13(event.payload) ? event.payload : null;
|
|
5691
6267
|
const timestamp = typeof payload?.timestamp === "string" ? payload.timestamp : "";
|
|
5692
6268
|
return Number.isFinite(Date.parse(timestamp)) ? new Date(timestamp).toISOString() : null;
|
|
5693
6269
|
}
|
|
@@ -6498,1424 +7074,1621 @@ function isPanelAppAgentCapability(value) {
|
|
|
6498
7074
|
return PANEL_APP_AGENT_CAPABILITIES.includes(value);
|
|
6499
7075
|
}
|
|
6500
7076
|
//#endregion
|
|
6501
|
-
//#region src/
|
|
6502
|
-
const
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
}
|
|
6512
|
-
issue = (params) => {
|
|
6513
|
-
const claims = {
|
|
6514
|
-
panelAppId: params.panelAppId,
|
|
6515
|
-
sourceName: params.sourceName,
|
|
6516
|
-
sourcePath: params.sourcePath,
|
|
6517
|
-
expiresAt: this.now() + this.ttlMs,
|
|
6518
|
-
nonce: randomBytes(12).toString("base64url")
|
|
6519
|
-
};
|
|
6520
|
-
const payload = Buffer.from(JSON.stringify(claims), "utf8").toString("base64url");
|
|
6521
|
-
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)
|
|
6522
7087
|
};
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
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")
|
|
6529
7108
|
};
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
7109
|
+
}
|
|
7110
|
+
function readPanelAppMeta(html) {
|
|
7111
|
+
return {
|
|
7112
|
+
...readPanelAppMetaField(html, "title"),
|
|
7113
|
+
...readPanelAppMetaField(html, "description"),
|
|
7114
|
+
...readPanelAppMetaField(html, "icon")
|
|
6535
7115
|
};
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
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);
|
|
6543
7140
|
}
|
|
6544
|
-
}
|
|
6545
|
-
};
|
|
6546
|
-
function isPanelAppAssetTokenClaims(value) {
|
|
6547
|
-
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
|
+
}
|
|
6548
7142
|
}
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
const
|
|
6552
|
-
const
|
|
6553
|
-
|
|
6554
|
-
constructor(panelsPath) {
|
|
6555
|
-
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"));
|
|
6556
7148
|
}
|
|
6557
|
-
load = async () => {
|
|
6558
|
-
try {
|
|
6559
|
-
const parsed = JSON.parse(await readFile(this.getStatePath(), "utf8"));
|
|
6560
|
-
return this.normalizeStateFile(parsed).apps;
|
|
6561
|
-
} catch (error) {
|
|
6562
|
-
if (this.isMissingFileError(error) || error instanceof SyntaxError) return {};
|
|
6563
|
-
throw error;
|
|
6564
|
-
}
|
|
6565
|
-
};
|
|
6566
|
-
updatePreferences = async (id, preferences) => {
|
|
6567
|
-
const apps = await this.load();
|
|
6568
|
-
const next = { ...apps[id] ?? {} };
|
|
6569
|
-
if (typeof preferences.favorite === "boolean") next.favorite = preferences.favorite;
|
|
6570
|
-
apps[id] = next;
|
|
6571
|
-
await this.persist(apps);
|
|
6572
|
-
return next;
|
|
6573
|
-
};
|
|
6574
|
-
recordOpened = async (id, openedAt = /* @__PURE__ */ new Date()) => {
|
|
6575
|
-
const apps = await this.load();
|
|
6576
|
-
const current = apps[id] ?? {};
|
|
6577
|
-
const next = {
|
|
6578
|
-
...current,
|
|
6579
|
-
lastOpenedAt: openedAt.toISOString(),
|
|
6580
|
-
openCount: Math.max(0, current.openCount ?? 0) + 1
|
|
6581
|
-
};
|
|
6582
|
-
apps[id] = next;
|
|
6583
|
-
await this.persist(apps);
|
|
6584
|
-
return next;
|
|
6585
|
-
};
|
|
6586
|
-
deleteEntry = async (id) => {
|
|
6587
|
-
const apps = await this.load();
|
|
6588
|
-
if (!(id in apps)) return;
|
|
6589
|
-
delete apps[id];
|
|
6590
|
-
await this.persist(apps);
|
|
6591
|
-
};
|
|
6592
|
-
persist = async (apps) => {
|
|
6593
|
-
const statePath = this.getStatePath();
|
|
6594
|
-
const tempPath = `${statePath}.${randomUUID()}.tmp`;
|
|
6595
|
-
const stateFile = {
|
|
6596
|
-
version: PANEL_APP_STATE_VERSION,
|
|
6597
|
-
apps
|
|
6598
|
-
};
|
|
6599
|
-
await mkdir(dirname(statePath), { recursive: true });
|
|
6600
|
-
try {
|
|
6601
|
-
await writeFile(tempPath, `${JSON.stringify(stateFile, null, 2)}\n`, "utf8");
|
|
6602
|
-
await rename(tempPath, statePath);
|
|
6603
|
-
} catch (error) {
|
|
6604
|
-
await rm(tempPath, { force: true }).catch(() => void 0);
|
|
6605
|
-
throw error;
|
|
6606
|
-
}
|
|
6607
|
-
};
|
|
6608
|
-
getStatePath = () => join(this.panelsPath, PANEL_APP_STATE_FILE);
|
|
6609
|
-
normalizeStateFile = (value) => {
|
|
6610
|
-
if (!this.isRecord(value) || !this.isRecord(value.apps)) return {
|
|
6611
|
-
version: PANEL_APP_STATE_VERSION,
|
|
6612
|
-
apps: {}
|
|
6613
|
-
};
|
|
6614
|
-
return {
|
|
6615
|
-
version: PANEL_APP_STATE_VERSION,
|
|
6616
|
-
apps: Object.fromEntries(Object.entries(value.apps).flatMap(([id, entry]) => {
|
|
6617
|
-
if (!this.isRecord(entry)) return [];
|
|
6618
|
-
return [[id, this.normalizeStateEntry(entry)]];
|
|
6619
|
-
}))
|
|
6620
|
-
};
|
|
6621
|
-
};
|
|
6622
|
-
normalizeStateEntry = (entry) => {
|
|
6623
|
-
const normalized = {};
|
|
6624
|
-
if (typeof entry.favorite === "boolean") normalized.favorite = entry.favorite;
|
|
6625
|
-
if (typeof entry.lastOpenedAt === "string") normalized.lastOpenedAt = entry.lastOpenedAt;
|
|
6626
|
-
if (typeof entry.openCount === "number") normalized.openCount = entry.openCount;
|
|
6627
|
-
return normalized;
|
|
6628
|
-
};
|
|
6629
|
-
isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6630
|
-
isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
6631
|
-
};
|
|
6632
|
-
//#endregion
|
|
6633
|
-
//#region src/stores/panel-app-capability-grant.store.ts
|
|
6634
|
-
const EMPTY_GRANTS$2 = {
|
|
6635
|
-
version: 1,
|
|
6636
|
-
grants: {}
|
|
6637
|
-
};
|
|
6638
|
-
var PanelAppCapabilityGrantStore = class {
|
|
6639
|
-
constructor(filePath) {
|
|
6640
|
-
this.filePath = filePath;
|
|
6641
|
-
}
|
|
6642
|
-
isGranted = async (caller, capability) => {
|
|
6643
|
-
const data = await this.load();
|
|
6644
|
-
return Boolean(data.grants[getCallerKey(caller)]?.capabilities[capability]);
|
|
6645
|
-
};
|
|
6646
|
-
grant = async (params) => {
|
|
6647
|
-
const { caller, capability, grantedAt } = params;
|
|
6648
|
-
const data = await this.load();
|
|
6649
|
-
const callerKey = getCallerKey(caller);
|
|
6650
|
-
const callerGrants = data.grants[callerKey] ?? { capabilities: {} };
|
|
6651
|
-
callerGrants.capabilities[capability] = { grantedAt };
|
|
6652
|
-
data.grants[callerKey] = callerGrants;
|
|
6653
|
-
await this.save(data);
|
|
6654
|
-
return {
|
|
6655
|
-
caller,
|
|
6656
|
-
capability,
|
|
6657
|
-
grantedAt
|
|
6658
|
-
};
|
|
6659
|
-
};
|
|
6660
|
-
deleteCaller = async (caller) => {
|
|
6661
|
-
const data = await this.load();
|
|
6662
|
-
const callerKey = getCallerKey(caller);
|
|
6663
|
-
if (!(callerKey in data.grants)) return;
|
|
6664
|
-
delete data.grants[callerKey];
|
|
6665
|
-
await this.save(data);
|
|
6666
|
-
};
|
|
6667
|
-
load = async () => {
|
|
6668
|
-
try {
|
|
6669
|
-
return normalizeStoreData$2(JSON.parse(await readFile(this.filePath, "utf8")));
|
|
6670
|
-
} catch (error) {
|
|
6671
|
-
if (isMissingFileError$3(error)) return structuredClone(EMPTY_GRANTS$2);
|
|
6672
|
-
throw error;
|
|
6673
|
-
}
|
|
6674
|
-
};
|
|
6675
|
-
save = async (data) => {
|
|
6676
|
-
await mkdir(dirname(this.filePath), { recursive: true });
|
|
6677
|
-
await writeFile(this.filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
6678
|
-
};
|
|
6679
|
-
};
|
|
6680
|
-
function normalizeStoreData$2(value) {
|
|
6681
|
-
if (!isRecord$11(value) || value.version !== 1 || !isRecord$11(value.grants)) return structuredClone(EMPTY_GRANTS$2);
|
|
6682
|
-
const grants = {};
|
|
6683
|
-
for (const [callerKey, callerValue] of Object.entries(value.grants)) {
|
|
6684
|
-
if (!isRecord$11(callerValue) || !isRecord$11(callerValue.capabilities)) continue;
|
|
6685
|
-
const capabilities = {};
|
|
6686
|
-
for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$11(grantValue) && typeof grantValue.grantedAt === "string") capabilities[capability] = { grantedAt: grantValue.grantedAt };
|
|
6687
|
-
grants[callerKey] = { capabilities };
|
|
6688
|
-
}
|
|
6689
|
-
return {
|
|
6690
|
-
version: 1,
|
|
6691
|
-
grants
|
|
6692
|
-
};
|
|
6693
7149
|
}
|
|
6694
|
-
function
|
|
6695
|
-
|
|
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;
|
|
6696
7153
|
}
|
|
6697
|
-
function
|
|
6698
|
-
return
|
|
7154
|
+
function readPanelAppServiceActions(html) {
|
|
7155
|
+
return parseTokenList(readMetaContent(html, "nextclaw-panel-actions", "attribute"));
|
|
6699
7156
|
}
|
|
6700
|
-
function
|
|
6701
|
-
return
|
|
7157
|
+
function readPanelAppCapabilities(html) {
|
|
7158
|
+
return parseTokenList(readMetaContent(html, "nextclaw-panel-capabilities", "attribute"));
|
|
6702
7159
|
}
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
grants: {},
|
|
6707
|
-
version: 1
|
|
6708
|
-
};
|
|
6709
|
-
var PanelAppClientGrantStore = class {
|
|
6710
|
-
constructor(filePath) {
|
|
6711
|
-
this.filePath = filePath;
|
|
6712
|
-
}
|
|
6713
|
-
isGranted = async (appId) => {
|
|
6714
|
-
const data = await this.load();
|
|
6715
|
-
return Boolean(data.grants[appId]);
|
|
6716
|
-
};
|
|
6717
|
-
grant = async (params) => {
|
|
6718
|
-
const data = await this.load();
|
|
6719
|
-
data.grants[params.appId] = { grantedAt: params.grantedAt };
|
|
6720
|
-
await this.save(data);
|
|
6721
|
-
return params;
|
|
6722
|
-
};
|
|
6723
|
-
revoke = async (appId) => {
|
|
6724
|
-
const data = await this.load();
|
|
6725
|
-
if (!data.grants[appId]) return;
|
|
6726
|
-
delete data.grants[appId];
|
|
6727
|
-
await this.save(data);
|
|
6728
|
-
};
|
|
6729
|
-
load = async () => {
|
|
6730
|
-
try {
|
|
6731
|
-
return normalizeStoreData$1(JSON.parse(await readFile(this.filePath, "utf8")));
|
|
6732
|
-
} catch (error) {
|
|
6733
|
-
if (isMissingFileError$2(error)) return structuredClone(EMPTY_GRANTS$1);
|
|
6734
|
-
throw error;
|
|
6735
|
-
}
|
|
6736
|
-
};
|
|
6737
|
-
save = async (data) => {
|
|
6738
|
-
await mkdir(dirname(this.filePath), { recursive: true });
|
|
6739
|
-
await writeFile(this.filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
6740
|
-
};
|
|
6741
|
-
};
|
|
6742
|
-
function normalizeStoreData$1(value) {
|
|
6743
|
-
if (!isRecord$10(value) || value.version !== 1 || !isRecord$10(value.grants)) return structuredClone(EMPTY_GRANTS$1);
|
|
6744
|
-
const grants = {};
|
|
6745
|
-
for (const [appId, grant] of Object.entries(value.grants)) if (isRecord$10(grant) && typeof grant.grantedAt === "string") grants[appId] = { grantedAt: grant.grantedAt };
|
|
6746
|
-
return {
|
|
6747
|
-
grants,
|
|
6748
|
-
version: 1
|
|
6749
|
-
};
|
|
7160
|
+
function parseTokenList(content) {
|
|
7161
|
+
if (!content) return [];
|
|
7162
|
+
return [...new Set(content.split(/[,\s]+/).map((entry) => entry.trim()).filter(Boolean))];
|
|
6750
7163
|
}
|
|
6751
|
-
function
|
|
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) {
|
|
6752
7184
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
6753
7185
|
}
|
|
6754
|
-
function
|
|
6755
|
-
return
|
|
7186
|
+
function normalizeTextValue(value) {
|
|
7187
|
+
return decodeHtmlAttribute(value ?? "").replace(/\s+/g, " ").trim() || void 0;
|
|
6756
7188
|
}
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
function readEventCorrelationId(event) {
|
|
6760
|
-
if (!("payload" in event)) return;
|
|
6761
|
-
const correlationId = "correlationId" in event.payload ? event.payload.correlationId : void 0;
|
|
6762
|
-
return typeof correlationId === "string" && correlationId.length > 0 ? correlationId : void 0;
|
|
7189
|
+
function normalizeAttributeValue(value) {
|
|
7190
|
+
return decodeHtmlAttribute(value ?? "").trim() || void 0;
|
|
6763
7191
|
}
|
|
6764
|
-
function
|
|
6765
|
-
if (!
|
|
6766
|
-
|
|
6767
|
-
return typeof sessionId === "string" && sessionId.length > 0 ? sessionId : void 0;
|
|
7192
|
+
function normalizeIconHref(value) {
|
|
7193
|
+
if (!value) return;
|
|
7194
|
+
if (value.startsWith("data:image/") || value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/")) return value;
|
|
6768
7195
|
}
|
|
6769
|
-
function
|
|
6770
|
-
|
|
6771
|
-
const runId = "runId" in event.payload ? event.payload.runId : void 0;
|
|
6772
|
-
return typeof runId === "string" && runId.length > 0 ? runId : void 0;
|
|
7196
|
+
function decodeHtmlAttribute(value) {
|
|
7197
|
+
return value.replace(/"/g, "\"").replace(/"/g, "\"").replace(/'/g, "'").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
6773
7198
|
}
|
|
6774
|
-
|
|
6775
|
-
|
|
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);
|
|
6776
7202
|
}
|
|
6777
|
-
function
|
|
6778
|
-
|
|
6779
|
-
const eventCorrelationId = readEventCorrelationId(event);
|
|
6780
|
-
if (eventCorrelationId) return eventCorrelationId === correlationId;
|
|
6781
|
-
if (!sessionId || !runId) return false;
|
|
6782
|
-
return readEventSessionId(event) === sessionId && readEventRunId(event) === runId;
|
|
7203
|
+
function isPanelAppFileName(fileName) {
|
|
7204
|
+
return hasSafeBaseName(fileName) && fileName.endsWith(".panel.html");
|
|
6783
7205
|
}
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
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
|
|
6799
7234
|
};
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
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
|
|
6807
7328
|
};
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
return await new Promise((resolve) => {
|
|
6818
|
-
this.waiters.push(resolve);
|
|
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
|
|
6819
7338
|
});
|
|
6820
|
-
}
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
var AgentRunObserver = class {
|
|
6824
|
-
queue = new AgentRunEventQueue();
|
|
6825
|
-
unsubscribe;
|
|
6826
|
-
abortCleanup;
|
|
6827
|
-
completedMessage;
|
|
6828
|
-
disposed = false;
|
|
6829
|
-
runId;
|
|
6830
|
-
sessionId;
|
|
6831
|
-
constructor(options) {
|
|
6832
|
-
this.options = options;
|
|
6833
|
-
this.unsubscribe = options.eventBus.on(eventKeys.ncpEvent, this.handleEvent);
|
|
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;
|
|
6834
7342
|
}
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
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;
|
|
6852
7379
|
}
|
|
6853
|
-
|
|
6854
|
-
|
|
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;
|
|
6855
7383
|
};
|
|
6856
|
-
|
|
6857
|
-
|
|
7384
|
+
findPackageSourceBySourceName = async (sourceName) => {
|
|
7385
|
+
return (await this.listPackageComponentSources()).find((component) => component.kind === "panel" && component.sourcePath.endsWith(`/${sourceName}`));
|
|
6858
7386
|
};
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
this.
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
if (
|
|
6870
|
-
if (event.type === NcpEventType.RunError) throw new Error(event.payload.error ?? options.runErrorMessage ?? "NCP run failed.");
|
|
6871
|
-
if (event.type === NcpEventType.RunFinished) {
|
|
6872
|
-
if (!this.completedMessage) throw new Error(options.missingCompletedMessageError ?? "NCP run completed without a final assistant message.");
|
|
6873
|
-
return this.completedMessage;
|
|
6874
|
-
}
|
|
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;
|
|
6875
7398
|
}
|
|
6876
|
-
|
|
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
|
+
});
|
|
6877
7408
|
};
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
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");
|
|
6884
7419
|
};
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
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
|
+
}
|
|
6895
7450
|
};
|
|
7451
|
+
listPackageComponentSources = async () => await this.params.listPackageComponentSources?.() ?? [];
|
|
6896
7452
|
};
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
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;
|
|
6900
7466
|
}
|
|
6901
|
-
|
|
6902
|
-
|
|
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;
|
|
6903
7493
|
};
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
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) => {
|
|
6907
7533
|
try {
|
|
6908
|
-
const
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
completedMessage,
|
|
6914
|
-
text: extractTextFromNcpMessage(completedMessage)
|
|
6915
|
-
};
|
|
6916
|
-
} finally {
|
|
6917
|
-
observer.dispose();
|
|
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");
|
|
6918
7539
|
}
|
|
6919
7540
|
};
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
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 () => {
|
|
6923
7554
|
try {
|
|
6924
|
-
const
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
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;
|
|
6929
7560
|
}
|
|
6930
7561
|
};
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
});
|
|
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;
|
|
7567
|
+
await this.persist(apps);
|
|
7568
|
+
return next;
|
|
6939
7569
|
};
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
}
|
|
7570
|
+
recordOpened = async (id, openedAt = /* @__PURE__ */ new Date()) => {
|
|
7571
|
+
const apps = await this.load();
|
|
7572
|
+
const current = apps[id] ?? {};
|
|
7573
|
+
const next = {
|
|
7574
|
+
...current,
|
|
7575
|
+
lastOpenedAt: openedAt.toISOString(),
|
|
7576
|
+
openCount: Math.max(0, current.openCount ?? 0) + 1
|
|
7577
|
+
};
|
|
7578
|
+
apps[id] = next;
|
|
7579
|
+
await this.persist(apps);
|
|
7580
|
+
return next;
|
|
7581
|
+
};
|
|
7582
|
+
deleteEntry = async (id) => {
|
|
7583
|
+
const apps = await this.load();
|
|
7584
|
+
if (!(id in apps)) return;
|
|
7585
|
+
delete apps[id];
|
|
7586
|
+
await this.persist(apps);
|
|
7587
|
+
};
|
|
7588
|
+
persist = async (apps) => {
|
|
7589
|
+
const statePath = this.getStatePath();
|
|
7590
|
+
const tempPath = `${statePath}.${randomUUID()}.tmp`;
|
|
7591
|
+
const stateFile = {
|
|
7592
|
+
version: PANEL_APP_STATE_VERSION,
|
|
7593
|
+
apps
|
|
7594
|
+
};
|
|
7595
|
+
await mkdir(dirname(statePath), { recursive: true });
|
|
7596
|
+
try {
|
|
7597
|
+
await writeFile(tempPath, `${JSON.stringify(stateFile, null, 2)}\n`, "utf8");
|
|
7598
|
+
await rename(tempPath, statePath);
|
|
7599
|
+
} catch (error) {
|
|
7600
|
+
await rm(tempPath, { force: true }).catch(() => void 0);
|
|
7601
|
+
throw error;
|
|
7602
|
+
}
|
|
7603
|
+
};
|
|
7604
|
+
getStatePath = () => join(this.panelsPath, PANEL_APP_STATE_FILE);
|
|
7605
|
+
normalizeStateFile = (value) => {
|
|
7606
|
+
if (!this.isRecord(value) || !this.isRecord(value.apps)) return {
|
|
7607
|
+
version: PANEL_APP_STATE_VERSION,
|
|
7608
|
+
apps: {}
|
|
7609
|
+
};
|
|
7610
|
+
return {
|
|
7611
|
+
version: PANEL_APP_STATE_VERSION,
|
|
7612
|
+
apps: Object.fromEntries(Object.entries(value.apps).flatMap(([id, entry]) => {
|
|
7613
|
+
if (!this.isRecord(entry)) return [];
|
|
7614
|
+
return [[id, this.normalizeStateEntry(entry)]];
|
|
7615
|
+
}))
|
|
7616
|
+
};
|
|
6948
7617
|
};
|
|
7618
|
+
normalizeStateEntry = (entry) => {
|
|
7619
|
+
const normalized = {};
|
|
7620
|
+
if (typeof entry.favorite === "boolean") normalized.favorite = entry.favorite;
|
|
7621
|
+
if (typeof entry.lastOpenedAt === "string") normalized.lastOpenedAt = entry.lastOpenedAt;
|
|
7622
|
+
if (typeof entry.openCount === "number") normalized.openCount = entry.openCount;
|
|
7623
|
+
return normalized;
|
|
7624
|
+
};
|
|
7625
|
+
isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7626
|
+
isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
6949
7627
|
};
|
|
6950
7628
|
//#endregion
|
|
6951
|
-
//#region src/
|
|
6952
|
-
const
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
description = "Submit the structured object result for this request.";
|
|
6956
|
-
constructor(contract) {
|
|
6957
|
-
this.contract = contract;
|
|
6958
|
-
}
|
|
6959
|
-
get parameters() {
|
|
6960
|
-
return this.contract.schema;
|
|
6961
|
-
}
|
|
6962
|
-
validateArgs = (args) => validateToolArgs(args, this.contract.schema);
|
|
6963
|
-
execute = async (args) => args;
|
|
7629
|
+
//#region src/stores/panel-app-capability-grant.store.ts
|
|
7630
|
+
const EMPTY_GRANTS$2 = {
|
|
7631
|
+
version: 1,
|
|
7632
|
+
grants: {}
|
|
6964
7633
|
};
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
const
|
|
6971
|
-
|
|
6972
|
-
const peerId = input.peerId.trim();
|
|
6973
|
-
const prompt = input.prompt.trim();
|
|
6974
|
-
if (!peerId || !prompt || !isRecord$9(input.schema)) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "invalid generateObject request");
|
|
6975
|
-
if (prompt.length > PANEL_APP_AGENT_MAX_PROMPT_CHARS) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "generateObject prompt is too large");
|
|
6976
|
-
if (stringifyJsonValue(input.context ?? null).length > PANEL_APP_AGENT_MAX_CONTEXT_CHARS) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "generateObject context is too large");
|
|
6977
|
-
return {
|
|
6978
|
-
context: input.context,
|
|
6979
|
-
peerId,
|
|
6980
|
-
prompt,
|
|
6981
|
-
schema: input.schema,
|
|
6982
|
-
timeoutMs: normalizeTimeoutMs(input.timeoutMs),
|
|
6983
|
-
title: input.title?.trim() || void 0
|
|
7634
|
+
var PanelAppCapabilityGrantStore = class {
|
|
7635
|
+
constructor(filePath) {
|
|
7636
|
+
this.filePath = filePath;
|
|
7637
|
+
}
|
|
7638
|
+
isGranted = async (caller, capability) => {
|
|
7639
|
+
const data = await this.load();
|
|
7640
|
+
return Boolean(data.grants[getCallerKey(caller)]?.capabilities[capability]);
|
|
6984
7641
|
};
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
},
|
|
6999
|
-
parts: [{
|
|
7000
|
-
type: "text",
|
|
7001
|
-
text: buildGenerateObjectPrompt(bridgeSession, request)
|
|
7002
|
-
}],
|
|
7003
|
-
role: "user",
|
|
7004
|
-
status: "final",
|
|
7005
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7642
|
+
grant = async (params) => {
|
|
7643
|
+
const { caller, capability, grantedAt } = params;
|
|
7644
|
+
const data = await this.load();
|
|
7645
|
+
const callerKey = getCallerKey(caller);
|
|
7646
|
+
const callerGrants = data.grants[callerKey] ?? { capabilities: {} };
|
|
7647
|
+
callerGrants.capabilities[capability] = { grantedAt };
|
|
7648
|
+
data.grants[callerKey] = callerGrants;
|
|
7649
|
+
await this.save(data);
|
|
7650
|
+
return {
|
|
7651
|
+
caller,
|
|
7652
|
+
capability,
|
|
7653
|
+
grantedAt
|
|
7654
|
+
};
|
|
7006
7655
|
};
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
});
|
|
7014
|
-
let resultToolCallId = null;
|
|
7015
|
-
try {
|
|
7016
|
-
while (true) {
|
|
7017
|
-
const next = await Promise.race([iterator.next(), timeout]);
|
|
7018
|
-
if (next.done) break;
|
|
7019
|
-
const result = readStructuredResultEvent(next.value, resultToolCallId);
|
|
7020
|
-
if (result.matchedToolCallId) resultToolCallId = result.matchedToolCallId;
|
|
7021
|
-
if (result.submitted) return result.content;
|
|
7022
|
-
throwIfTerminalError(next.value);
|
|
7023
|
-
}
|
|
7024
|
-
} finally {
|
|
7025
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
7026
|
-
await iterator.return?.(void 0);
|
|
7027
|
-
}
|
|
7028
|
-
throw new PanelAppError("AGENT_OBJECT_RESULT_NOT_SUBMITTED", "agent did not submit a structured object result");
|
|
7029
|
-
}
|
|
7030
|
-
function withPanelAppAgentMetadata(payload, bridgeSession) {
|
|
7031
|
-
const panelAppMetadata = createPanelAppAgentMetadata(bridgeSession);
|
|
7032
|
-
const peerId = readOptionalPeerId(payload.peerId);
|
|
7033
|
-
const sessionId = readOptionalSessionId(payload.sessionId);
|
|
7034
|
-
if (peerId && sessionId) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "agent send request cannot include both sessionId and peerId");
|
|
7035
|
-
const metadata = {
|
|
7036
|
-
...payload.metadata ?? {},
|
|
7037
|
-
...panelAppMetadata
|
|
7656
|
+
deleteCaller = async (caller) => {
|
|
7657
|
+
const data = await this.load();
|
|
7658
|
+
const callerKey = getCallerKey(caller);
|
|
7659
|
+
if (!(callerKey in data.grants)) return;
|
|
7660
|
+
delete data.grants[callerKey];
|
|
7661
|
+
await this.save(data);
|
|
7038
7662
|
};
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7663
|
+
load = async () => {
|
|
7664
|
+
try {
|
|
7665
|
+
return normalizeStoreData$2(JSON.parse(await readFile(this.filePath, "utf8")));
|
|
7666
|
+
} catch (error) {
|
|
7667
|
+
if (isMissingFileError$3(error)) return structuredClone(EMPTY_GRANTS$2);
|
|
7668
|
+
throw error;
|
|
7669
|
+
}
|
|
7045
7670
|
};
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
...structuredClone(payload.message),
|
|
7050
|
-
metadata: {
|
|
7051
|
-
...payload.message.metadata ?? {},
|
|
7052
|
-
...panelAppMetadata
|
|
7053
|
-
}
|
|
7054
|
-
},
|
|
7055
|
-
metadata,
|
|
7056
|
-
peerId,
|
|
7057
|
-
sessionId
|
|
7671
|
+
save = async (data) => {
|
|
7672
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
7673
|
+
await writeFile(this.filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
7058
7674
|
};
|
|
7059
|
-
}
|
|
7060
|
-
function
|
|
7675
|
+
};
|
|
7676
|
+
function normalizeStoreData$2(value) {
|
|
7677
|
+
if (!isRecord$11(value) || value.version !== 1 || !isRecord$11(value.grants)) return structuredClone(EMPTY_GRANTS$2);
|
|
7678
|
+
const grants = {};
|
|
7679
|
+
for (const [callerKey, callerValue] of Object.entries(value.grants)) {
|
|
7680
|
+
if (!isRecord$11(callerValue) || !isRecord$11(callerValue.capabilities)) continue;
|
|
7681
|
+
const capabilities = {};
|
|
7682
|
+
for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$11(grantValue) && typeof grantValue.grantedAt === "string") capabilities[capability] = { grantedAt: grantValue.grantedAt };
|
|
7683
|
+
grants[callerKey] = { capabilities };
|
|
7684
|
+
}
|
|
7061
7685
|
return {
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
panel_app_id: bridgeSession.appId,
|
|
7065
|
-
source_kind: "panel_app"
|
|
7686
|
+
version: 1,
|
|
7687
|
+
grants
|
|
7066
7688
|
};
|
|
7067
7689
|
}
|
|
7068
|
-
function
|
|
7069
|
-
|
|
7070
|
-
return Math.min(PANEL_APP_AGENT_MAX_TIMEOUT_MS, Math.max(1e3, Math.trunc(timeoutMs)));
|
|
7071
|
-
}
|
|
7072
|
-
function readOptionalPeerId(value) {
|
|
7073
|
-
if (typeof value !== "string") return;
|
|
7074
|
-
const peerId = value.trim();
|
|
7075
|
-
if (!peerId) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "agent peerId cannot be empty");
|
|
7076
|
-
return peerId;
|
|
7690
|
+
function getCallerKey(caller) {
|
|
7691
|
+
return `${caller.surface}:${caller.appId}`;
|
|
7077
7692
|
}
|
|
7078
|
-
function
|
|
7079
|
-
|
|
7080
|
-
const sessionId = value.trim();
|
|
7081
|
-
if (!sessionId) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "agent sessionId cannot be empty");
|
|
7082
|
-
return sessionId;
|
|
7693
|
+
function isRecord$11(value) {
|
|
7694
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7083
7695
|
}
|
|
7084
|
-
function
|
|
7085
|
-
return
|
|
7086
|
-
"Panel App generateObject request",
|
|
7087
|
-
"",
|
|
7088
|
-
`Panel App ID: ${bridgeSession.appId}`,
|
|
7089
|
-
`Peer ID: ${request.peerId}`,
|
|
7090
|
-
"",
|
|
7091
|
-
"Context JSON:",
|
|
7092
|
-
stringifyJsonValue(request.context ?? null),
|
|
7093
|
-
"",
|
|
7094
|
-
"Task:",
|
|
7095
|
-
request.prompt,
|
|
7096
|
-
"",
|
|
7097
|
-
"Result contract:",
|
|
7098
|
-
`Call the ${STRUCTURED_RESULT_TOOL_NAME} tool exactly once with an object matching the provided schema.`,
|
|
7099
|
-
"Do not use natural language as the result."
|
|
7100
|
-
].join("\n");
|
|
7696
|
+
function isMissingFileError$3(error) {
|
|
7697
|
+
return Boolean(error) && typeof error === "object" && error.code === "ENOENT";
|
|
7101
7698
|
}
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7699
|
+
//#endregion
|
|
7700
|
+
//#region src/stores/panel-app-client-grant.store.ts
|
|
7701
|
+
const EMPTY_GRANTS$1 = {
|
|
7702
|
+
grants: {},
|
|
7703
|
+
version: 1
|
|
7704
|
+
};
|
|
7705
|
+
var PanelAppClientGrantStore = class {
|
|
7706
|
+
constructor(filePath) {
|
|
7707
|
+
this.filePath = filePath;
|
|
7708
|
+
}
|
|
7709
|
+
isGranted = async (appId) => {
|
|
7710
|
+
const data = await this.load();
|
|
7711
|
+
return Boolean(data.grants[appId]);
|
|
7106
7712
|
};
|
|
7107
|
-
|
|
7108
|
-
|
|
7713
|
+
grant = async (params) => {
|
|
7714
|
+
const data = await this.load();
|
|
7715
|
+
data.grants[params.appId] = { grantedAt: params.grantedAt };
|
|
7716
|
+
await this.save(data);
|
|
7717
|
+
return params;
|
|
7718
|
+
};
|
|
7719
|
+
revoke = async (appId) => {
|
|
7720
|
+
const data = await this.load();
|
|
7721
|
+
if (!data.grants[appId]) return;
|
|
7722
|
+
delete data.grants[appId];
|
|
7723
|
+
await this.save(data);
|
|
7724
|
+
};
|
|
7725
|
+
load = async () => {
|
|
7726
|
+
try {
|
|
7727
|
+
return normalizeStoreData$1(JSON.parse(await readFile(this.filePath, "utf8")));
|
|
7728
|
+
} catch (error) {
|
|
7729
|
+
if (isMissingFileError$2(error)) return structuredClone(EMPTY_GRANTS$1);
|
|
7730
|
+
throw error;
|
|
7731
|
+
}
|
|
7732
|
+
};
|
|
7733
|
+
save = async (data) => {
|
|
7734
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
7735
|
+
await writeFile(this.filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
7736
|
+
};
|
|
7737
|
+
};
|
|
7738
|
+
function normalizeStoreData$1(value) {
|
|
7739
|
+
if (!isRecord$10(value) || value.version !== 1 || !isRecord$10(value.grants)) return structuredClone(EMPTY_GRANTS$1);
|
|
7740
|
+
const grants = {};
|
|
7741
|
+
for (const [appId, grant] of Object.entries(value.grants)) if (isRecord$10(grant) && typeof grant.grantedAt === "string") grants[appId] = { grantedAt: grant.grantedAt };
|
|
7109
7742
|
return {
|
|
7110
|
-
|
|
7111
|
-
|
|
7743
|
+
grants,
|
|
7744
|
+
version: 1
|
|
7112
7745
|
};
|
|
7113
7746
|
}
|
|
7114
|
-
function
|
|
7115
|
-
|
|
7116
|
-
if (content.error.code === "invalid_tool_arguments") throw new PanelAppError("AGENT_OBJECT_RESULT_SCHEMA_INVALID", "agent object result did not match the schema");
|
|
7117
|
-
throw new PanelAppError("AGENT_OBJECT_REQUEST_FAILED", typeof content.error.message === "string" ? content.error.message : "agent object request failed");
|
|
7747
|
+
function isRecord$10(value) {
|
|
7748
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7118
7749
|
}
|
|
7119
|
-
function
|
|
7120
|
-
|
|
7121
|
-
if (event.type === NcpEventType.RunError) throw new PanelAppError("AGENT_OBJECT_REQUEST_FAILED", event.payload.error ?? "agent object request failed");
|
|
7122
|
-
if (event.type === NcpEventType.RunFinished) throw new PanelAppError("AGENT_OBJECT_RESULT_NOT_SUBMITTED", "agent did not submit a structured object result");
|
|
7750
|
+
function isMissingFileError$2(error) {
|
|
7751
|
+
return isRecord$10(error) && error.code === "ENOENT";
|
|
7123
7752
|
}
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7753
|
+
//#endregion
|
|
7754
|
+
//#region src/services/agent-run-client.service.ts
|
|
7755
|
+
function readEventCorrelationId(event) {
|
|
7756
|
+
if (!("payload" in event)) return;
|
|
7757
|
+
const correlationId = "correlationId" in event.payload ? event.payload.correlationId : void 0;
|
|
7758
|
+
return typeof correlationId === "string" && correlationId.length > 0 ? correlationId : void 0;
|
|
7130
7759
|
}
|
|
7131
|
-
function
|
|
7132
|
-
|
|
7760
|
+
function readEventSessionId(event) {
|
|
7761
|
+
if (!("payload" in event)) return;
|
|
7762
|
+
const sessionId = "sessionId" in event.payload ? event.payload.sessionId : void 0;
|
|
7763
|
+
return typeof sessionId === "string" && sessionId.length > 0 ? sessionId : void 0;
|
|
7133
7764
|
}
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7765
|
+
function readEventRunId(event) {
|
|
7766
|
+
if (!("payload" in event)) return;
|
|
7767
|
+
const runId = "runId" in event.payload ? event.payload.runId : void 0;
|
|
7768
|
+
return typeof runId === "string" && runId.length > 0 ? runId : void 0;
|
|
7769
|
+
}
|
|
7770
|
+
function isTerminalEvent(event) {
|
|
7771
|
+
return event.type === NcpEventType.MessageFailed || event.type === NcpEventType.RunError || event.type === NcpEventType.RunFinished;
|
|
7772
|
+
}
|
|
7773
|
+
function isRunEventMatch(params) {
|
|
7774
|
+
const { correlationId, event, runId, sessionId } = params;
|
|
7775
|
+
const eventCorrelationId = readEventCorrelationId(event);
|
|
7776
|
+
if (eventCorrelationId) return eventCorrelationId === correlationId;
|
|
7777
|
+
if (!sessionId || !runId) return false;
|
|
7778
|
+
return readEventSessionId(event) === sessionId && readEventRunId(event) === runId;
|
|
7779
|
+
}
|
|
7780
|
+
var AgentRunEventQueue = class {
|
|
7781
|
+
values = [];
|
|
7782
|
+
waiters = [];
|
|
7783
|
+
closed = false;
|
|
7784
|
+
push = (value) => {
|
|
7785
|
+
if (this.closed) return;
|
|
7786
|
+
const waiter = this.waiters.shift();
|
|
7787
|
+
if (waiter) {
|
|
7788
|
+
waiter({
|
|
7789
|
+
done: false,
|
|
7790
|
+
value
|
|
7791
|
+
});
|
|
7792
|
+
return;
|
|
7793
|
+
}
|
|
7794
|
+
this.values.push(value);
|
|
7143
7795
|
};
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
requestId: randomUUID()
|
|
7796
|
+
close = () => {
|
|
7797
|
+
if (this.closed) return;
|
|
7798
|
+
this.closed = true;
|
|
7799
|
+
while (this.waiters.length > 0) this.waiters.shift()?.({
|
|
7800
|
+
done: true,
|
|
7801
|
+
value: void 0
|
|
7151
7802
|
});
|
|
7152
|
-
return { result: await waitForPanelAppStructuredResult(this.requireAgentRunClient(), {
|
|
7153
|
-
payload: {
|
|
7154
|
-
message,
|
|
7155
|
-
metadata: {
|
|
7156
|
-
...createPanelAppAgentMetadata(bridgeSession),
|
|
7157
|
-
panel_app_peer_id: request.peerId
|
|
7158
|
-
},
|
|
7159
|
-
peerId: request.peerId
|
|
7160
|
-
},
|
|
7161
|
-
timeoutMs: request.timeoutMs
|
|
7162
|
-
}) };
|
|
7163
7803
|
};
|
|
7164
|
-
|
|
7165
|
-
if (
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7804
|
+
next = async () => {
|
|
7805
|
+
if (this.values.length > 0) return {
|
|
7806
|
+
done: false,
|
|
7807
|
+
value: this.values.shift()
|
|
7808
|
+
};
|
|
7809
|
+
if (this.closed) return {
|
|
7810
|
+
done: true,
|
|
7811
|
+
value: void 0
|
|
7812
|
+
};
|
|
7813
|
+
return await new Promise((resolve) => {
|
|
7814
|
+
this.waiters.push(resolve);
|
|
7171
7815
|
});
|
|
7172
7816
|
};
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7817
|
+
[Symbol.asyncIterator] = () => ({ next: this.next });
|
|
7818
|
+
};
|
|
7819
|
+
var AgentRunObserver = class {
|
|
7820
|
+
queue = new AgentRunEventQueue();
|
|
7821
|
+
unsubscribe;
|
|
7822
|
+
abortCleanup;
|
|
7823
|
+
completedMessage;
|
|
7824
|
+
disposed = false;
|
|
7825
|
+
runId;
|
|
7826
|
+
sessionId;
|
|
7827
|
+
constructor(options) {
|
|
7828
|
+
this.options = options;
|
|
7829
|
+
this.unsubscribe = options.eventBus.on(eventKeys.ncpEvent, this.handleEvent);
|
|
7830
|
+
}
|
|
7831
|
+
attachHandle = (handle) => {
|
|
7832
|
+
this.sessionId = handle.sessionId;
|
|
7833
|
+
this.runId = handle.runId ?? void 0;
|
|
7834
|
+
const { abortSignal } = this.options;
|
|
7835
|
+
if (!abortSignal) return;
|
|
7836
|
+
const abort = () => {
|
|
7837
|
+
this.options.ingress.handle({
|
|
7838
|
+
type: ingressKeys.agentRun.abort,
|
|
7839
|
+
payload: {
|
|
7840
|
+
sessionId: handle.sessionId,
|
|
7841
|
+
correlationId: this.options.correlationId
|
|
7842
|
+
}
|
|
7843
|
+
}, { source: "agent-run-client" });
|
|
7844
|
+
};
|
|
7845
|
+
if (abortSignal.aborted) {
|
|
7846
|
+
abort();
|
|
7847
|
+
return;
|
|
7848
|
+
}
|
|
7849
|
+
abortSignal.addEventListener("abort", abort, { once: true });
|
|
7850
|
+
this.abortCleanup = () => abortSignal.removeEventListener("abort", abort);
|
|
7176
7851
|
};
|
|
7177
|
-
|
|
7178
|
-
|
|
7852
|
+
stream = async function* () {
|
|
7853
|
+
for await (const event of this.queue) yield event;
|
|
7179
7854
|
};
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7855
|
+
waitForReply = async (options = {}) => {
|
|
7856
|
+
for await (const event of this.queue) {
|
|
7857
|
+
if (event.type === NcpEventType.MessageTextDelta) {
|
|
7858
|
+
options.onAssistantDelta?.(event.payload.delta);
|
|
7859
|
+
continue;
|
|
7860
|
+
}
|
|
7861
|
+
if (event.type === NcpEventType.MessageCompleted) {
|
|
7862
|
+
this.completedMessage = event.payload.message;
|
|
7863
|
+
continue;
|
|
7864
|
+
}
|
|
7865
|
+
if (event.type === NcpEventType.MessageFailed) throw new Error(event.payload.error.message);
|
|
7866
|
+
if (event.type === NcpEventType.RunError) throw new Error(event.payload.error ?? options.runErrorMessage ?? "NCP run failed.");
|
|
7867
|
+
if (event.type === NcpEventType.RunFinished) {
|
|
7868
|
+
if (!this.completedMessage) throw new Error(options.missingCompletedMessageError ?? "NCP run completed without a final assistant message.");
|
|
7869
|
+
return this.completedMessage;
|
|
7870
|
+
}
|
|
7871
|
+
}
|
|
7872
|
+
throw new Error(options.missingCompletedMessageError ?? "NCP run completed without a final assistant message.");
|
|
7191
7873
|
};
|
|
7192
|
-
|
|
7193
|
-
if (
|
|
7194
|
-
|
|
7874
|
+
dispose = () => {
|
|
7875
|
+
if (this.disposed) return;
|
|
7876
|
+
this.disposed = true;
|
|
7877
|
+
this.abortCleanup?.();
|
|
7878
|
+
this.unsubscribe();
|
|
7879
|
+
this.queue.close();
|
|
7880
|
+
};
|
|
7881
|
+
handleEvent = (event) => {
|
|
7882
|
+
if (this.disposed || !isRunEventMatch({
|
|
7883
|
+
correlationId: this.options.correlationId,
|
|
7884
|
+
event,
|
|
7885
|
+
runId: this.runId,
|
|
7886
|
+
sessionId: this.sessionId
|
|
7887
|
+
})) return;
|
|
7888
|
+
this.options.onEvent?.(event);
|
|
7889
|
+
this.queue.push(event);
|
|
7890
|
+
if (isTerminalEvent(event)) this.queue.close();
|
|
7891
|
+
};
|
|
7892
|
+
};
|
|
7893
|
+
var AgentRunClient = class {
|
|
7894
|
+
constructor(options) {
|
|
7895
|
+
this.options = options;
|
|
7896
|
+
}
|
|
7897
|
+
send = async (input) => {
|
|
7898
|
+
return await this.sendWithCorrelation(input, randomUUID());
|
|
7899
|
+
};
|
|
7900
|
+
sendAndWaitForReply = async (input, options = {}) => {
|
|
7901
|
+
const correlationId = randomUUID();
|
|
7902
|
+
const observer = this.prepareObserver(correlationId, options);
|
|
7903
|
+
try {
|
|
7904
|
+
const handle = await this.sendWithCorrelation(input, correlationId);
|
|
7905
|
+
observer.attachHandle(handle);
|
|
7906
|
+
const completedMessage = await observer.waitForReply(options);
|
|
7907
|
+
return {
|
|
7908
|
+
handle,
|
|
7909
|
+
completedMessage,
|
|
7910
|
+
text: extractTextFromNcpMessage(completedMessage)
|
|
7911
|
+
};
|
|
7912
|
+
} finally {
|
|
7913
|
+
observer.dispose();
|
|
7914
|
+
}
|
|
7915
|
+
};
|
|
7916
|
+
sendAndStreamEvents = async function* (input, options = {}) {
|
|
7917
|
+
const correlationId = randomUUID();
|
|
7918
|
+
const observer = this.prepareObserver(correlationId, options);
|
|
7919
|
+
try {
|
|
7920
|
+
const handle = await this.sendWithCorrelation(input, correlationId);
|
|
7921
|
+
observer.attachHandle(handle);
|
|
7922
|
+
for await (const event of observer.stream()) yield event;
|
|
7923
|
+
} finally {
|
|
7924
|
+
observer.dispose();
|
|
7925
|
+
}
|
|
7926
|
+
};
|
|
7927
|
+
prepareObserver = (correlationId, options) => {
|
|
7928
|
+
return new AgentRunObserver({
|
|
7929
|
+
abortSignal: options.abortSignal,
|
|
7930
|
+
correlationId,
|
|
7931
|
+
eventBus: this.options.eventBus,
|
|
7932
|
+
ingress: this.options.ingress,
|
|
7933
|
+
onEvent: options.onEvent
|
|
7934
|
+
});
|
|
7935
|
+
};
|
|
7936
|
+
sendWithCorrelation = async (input, correlationId) => {
|
|
7937
|
+
return await this.options.ingress.handle({
|
|
7938
|
+
type: ingressKeys.agentRun.send,
|
|
7939
|
+
payload: {
|
|
7940
|
+
...input,
|
|
7941
|
+
correlationId
|
|
7942
|
+
}
|
|
7943
|
+
}, { source: "agent-run-client" });
|
|
7195
7944
|
};
|
|
7196
7945
|
};
|
|
7197
7946
|
//#endregion
|
|
7198
|
-
//#region src/
|
|
7199
|
-
const
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
(
|
|
7204
|
-
|
|
7205
|
-
const rawWindowName = typeof window.name === "string" ? window.name : "";
|
|
7206
|
-
if (!rawWindowName.startsWith(contract.windowNamePrefix)) {
|
|
7207
|
-
return;
|
|
7208
|
-
}
|
|
7209
|
-
window.name = "";
|
|
7210
|
-
let params;
|
|
7211
|
-
try {
|
|
7212
|
-
params = JSON.parse(rawWindowName.slice(contract.windowNamePrefix.length));
|
|
7213
|
-
} catch {
|
|
7214
|
-
return;
|
|
7215
|
-
}
|
|
7216
|
-
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
7217
|
-
return;
|
|
7218
|
-
}
|
|
7219
|
-
const freezeJson = (value) => {
|
|
7220
|
-
if (!value || typeof value !== "object" || Object.isFrozen(value)) {
|
|
7221
|
-
return value;
|
|
7222
|
-
}
|
|
7223
|
-
Object.values(value).forEach(freezeJson);
|
|
7224
|
-
return Object.freeze(value);
|
|
7225
|
-
};
|
|
7226
|
-
const existing = window.nextclaw && typeof window.nextclaw === "object"
|
|
7227
|
-
? window.nextclaw
|
|
7228
|
-
: {};
|
|
7229
|
-
Object.defineProperty(window, "nextclaw", {
|
|
7230
|
-
configurable: true,
|
|
7231
|
-
value: {
|
|
7232
|
-
...existing,
|
|
7233
|
-
params: freezeJson(params)
|
|
7234
|
-
}
|
|
7235
|
-
});
|
|
7236
|
-
})();
|
|
7237
|
-
`.trim();
|
|
7238
|
-
}
|
|
7239
|
-
function injectUiContentParamsBootstrap(html) {
|
|
7240
|
-
if (html.includes(UI_CONTENT_PARAMS_BOOTSTRAP_MARKER)) return html;
|
|
7241
|
-
const script = `<script>${getUiContentParamsBootstrapScript()}<\/script>`;
|
|
7242
|
-
const headMatch = /<head(?:\s[^>]*)?>/i.exec(html);
|
|
7243
|
-
if (headMatch?.index !== void 0) {
|
|
7244
|
-
const insertAt = headMatch.index + headMatch[0].length;
|
|
7245
|
-
return `${html.slice(0, insertAt)}${script}${html.slice(insertAt)}`;
|
|
7947
|
+
//#region src/tools/structured-result.tools.ts
|
|
7948
|
+
const STRUCTURED_RESULT_TOOL_NAME = "nextclaw_submit_result";
|
|
7949
|
+
var StructuredResultSubmitTool = class {
|
|
7950
|
+
name = STRUCTURED_RESULT_TOOL_NAME;
|
|
7951
|
+
description = "Submit the structured object result for this request.";
|
|
7952
|
+
constructor(contract) {
|
|
7953
|
+
this.contract = contract;
|
|
7246
7954
|
}
|
|
7247
|
-
|
|
7248
|
-
|
|
7955
|
+
get parameters() {
|
|
7956
|
+
return this.contract.schema;
|
|
7957
|
+
}
|
|
7958
|
+
validateArgs = (args) => validateToolArgs(args, this.contract.schema);
|
|
7959
|
+
execute = async (args) => args;
|
|
7960
|
+
};
|
|
7249
7961
|
//#endregion
|
|
7250
|
-
//#region src/utils/panel-app-
|
|
7251
|
-
const
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
if (
|
|
7262
|
-
searchParams.get(inlineHostContract.displayModeSearchParam) !== inlineHostContract.displayMode ||
|
|
7263
|
-
searchParams.get(inlineHostContract.placementSearchParam) !== inlineHostContract.placement
|
|
7264
|
-
) {
|
|
7265
|
-
return;
|
|
7266
|
-
}
|
|
7267
|
-
const start = () => {
|
|
7268
|
-
const { body, documentElement } = window.document;
|
|
7269
|
-
if (!documentElement) {
|
|
7270
|
-
return;
|
|
7271
|
-
}
|
|
7272
|
-
let lastHeight = 0;
|
|
7273
|
-
const reportHeight = () => {
|
|
7274
|
-
const height = readInlineContentHeight(body, documentElement);
|
|
7275
|
-
if (height > 0 && height !== lastHeight) {
|
|
7276
|
-
lastHeight = height;
|
|
7277
|
-
window.parent.postMessage({ type: inlineHostContract.contentHeightMessageType, height }, "*");
|
|
7278
|
-
}
|
|
7279
|
-
};
|
|
7280
|
-
if (typeof window.ResizeObserver === "function") {
|
|
7281
|
-
const observer = new window.ResizeObserver(reportHeight);
|
|
7282
|
-
observer.observe(documentElement);
|
|
7283
|
-
if (body) {
|
|
7284
|
-
observer.observe(body);
|
|
7285
|
-
}
|
|
7286
|
-
}
|
|
7287
|
-
window.addEventListener("load", reportHeight);
|
|
7288
|
-
reportHeight();
|
|
7289
|
-
};
|
|
7290
|
-
if (window.document.readyState === "loading") {
|
|
7291
|
-
window.document.addEventListener("DOMContentLoaded", start, { once: true });
|
|
7292
|
-
return;
|
|
7293
|
-
}
|
|
7294
|
-
start();
|
|
7295
|
-
}`.trim();
|
|
7296
|
-
}
|
|
7297
|
-
function getPanelAppScrollSurfaceHelpersScript() {
|
|
7298
|
-
return `
|
|
7299
|
-
function readScrollPosition(element) {
|
|
7300
|
-
const x = element ? element.scrollLeft : window.scrollX;
|
|
7301
|
-
const y = element ? element.scrollTop : window.scrollY;
|
|
7302
|
-
if (!Number.isFinite(x) || !Number.isFinite(y) || x < 0 || y < 0) {
|
|
7303
|
-
return null;
|
|
7304
|
-
}
|
|
7305
|
-
return { x, y };
|
|
7306
|
-
}
|
|
7307
|
-
|
|
7308
|
-
function getScrollSurface(target) {
|
|
7309
|
-
const root = window.document.scrollingElement;
|
|
7310
|
-
if (
|
|
7311
|
-
!target ||
|
|
7312
|
-
target === window.document ||
|
|
7313
|
-
target === root ||
|
|
7314
|
-
target === window.document.documentElement ||
|
|
7315
|
-
target === window.document.body
|
|
7316
|
-
) {
|
|
7317
|
-
return { kind: "document" };
|
|
7318
|
-
}
|
|
7319
|
-
if (!target.parentElement || !target.children || typeof target.scrollTop !== "number") {
|
|
7320
|
-
return null;
|
|
7321
|
-
}
|
|
7322
|
-
const path = [];
|
|
7323
|
-
let element = target;
|
|
7324
|
-
while (element && element !== window.document.body) {
|
|
7325
|
-
const parent = element.parentElement;
|
|
7326
|
-
if (!parent || !parent.children) {
|
|
7327
|
-
return null;
|
|
7328
|
-
}
|
|
7329
|
-
const index = Array.prototype.indexOf.call(parent.children, element);
|
|
7330
|
-
const tagName = typeof element.tagName === "string" ? element.tagName.toLowerCase() : "";
|
|
7331
|
-
if (index < 0 || !tagName) {
|
|
7332
|
-
return null;
|
|
7333
|
-
}
|
|
7334
|
-
path.unshift({ index, tagName });
|
|
7335
|
-
element = parent;
|
|
7336
|
-
}
|
|
7337
|
-
return element === window.document.body && path.length > 0 ? { kind: "element", path } : null;
|
|
7338
|
-
}
|
|
7339
|
-
|
|
7340
|
-
function resolveScrollSurface(target) {
|
|
7341
|
-
if (target.kind === "document") {
|
|
7342
|
-
return null;
|
|
7343
|
-
}
|
|
7344
|
-
let element = window.document.body;
|
|
7345
|
-
for (const segment of target.path) {
|
|
7346
|
-
const child = element?.children?.[segment.index];
|
|
7347
|
-
if (!child || child.tagName?.toLowerCase() !== segment.tagName) {
|
|
7348
|
-
return undefined;
|
|
7349
|
-
}
|
|
7350
|
-
element = child;
|
|
7351
|
-
}
|
|
7352
|
-
return element;
|
|
7353
|
-
}
|
|
7354
|
-
|
|
7355
|
-
function isScrollTarget(value) {
|
|
7356
|
-
if (!value || typeof value !== "object") {
|
|
7357
|
-
return false;
|
|
7358
|
-
}
|
|
7359
|
-
if (value.kind === "document") {
|
|
7360
|
-
return true;
|
|
7361
|
-
}
|
|
7362
|
-
return value.kind === "element" &&
|
|
7363
|
-
Array.isArray(value.path) &&
|
|
7364
|
-
value.path.length > 0 &&
|
|
7365
|
-
value.path.length <= 30 &&
|
|
7366
|
-
value.path.every((segment) =>
|
|
7367
|
-
segment &&
|
|
7368
|
-
Number.isInteger(segment.index) &&
|
|
7369
|
-
segment.index >= 0 &&
|
|
7370
|
-
segment.index <= 1000 &&
|
|
7371
|
-
typeof segment.tagName === "string" &&
|
|
7372
|
-
segment.tagName.length > 0 &&
|
|
7373
|
-
segment.tagName.length <= 32
|
|
7374
|
-
);
|
|
7375
|
-
}`.trim();
|
|
7376
|
-
}
|
|
7377
|
-
function getPanelAppScrollRestorationScript() {
|
|
7378
|
-
return `
|
|
7379
|
-
function installScrollRestoration() {
|
|
7380
|
-
const scrollContract = ${JSON.stringify(PANEL_APP_SCROLL_RESTORATION_CONTRACT)};
|
|
7381
|
-
const inlineHostContract = ${JSON.stringify(PANEL_APP_INLINE_HOST_CONTRACT)};
|
|
7382
|
-
const searchParams = new URLSearchParams(window.location.search);
|
|
7383
|
-
if (
|
|
7384
|
-
searchParams.get(inlineHostContract.displayModeSearchParam) === inlineHostContract.displayMode &&
|
|
7385
|
-
searchParams.get(inlineHostContract.placementSearchParam) === inlineHostContract.placement
|
|
7386
|
-
) {
|
|
7387
|
-
return;
|
|
7388
|
-
}
|
|
7389
|
-
let isScrollReportScheduled = false;
|
|
7390
|
-
let latestScrollSurface = null;
|
|
7391
|
-
|
|
7392
|
-
${getPanelAppScrollSurfaceHelpersScript()}
|
|
7393
|
-
|
|
7394
|
-
function reportScroll() {
|
|
7395
|
-
isScrollReportScheduled = false;
|
|
7396
|
-
const surface = latestScrollSurface;
|
|
7397
|
-
latestScrollSurface = null;
|
|
7398
|
-
if (!surface) {
|
|
7399
|
-
return;
|
|
7400
|
-
}
|
|
7401
|
-
const position = readScrollPosition(resolveScrollSurface(surface));
|
|
7402
|
-
if (!position) {
|
|
7403
|
-
return;
|
|
7404
|
-
}
|
|
7405
|
-
window.parent.postMessage({
|
|
7406
|
-
type: scrollContract.scrollMessageType,
|
|
7407
|
-
version: scrollContract.version,
|
|
7408
|
-
target: surface,
|
|
7409
|
-
...position,
|
|
7410
|
-
}, "*");
|
|
7411
|
-
}
|
|
7412
|
-
|
|
7413
|
-
function scheduleScrollReport(event) {
|
|
7414
|
-
const surface = getScrollSurface(event?.target);
|
|
7415
|
-
if (!surface) {
|
|
7416
|
-
return;
|
|
7417
|
-
}
|
|
7418
|
-
latestScrollSurface = surface;
|
|
7419
|
-
if (isScrollReportScheduled) {
|
|
7420
|
-
return;
|
|
7421
|
-
}
|
|
7422
|
-
isScrollReportScheduled = true;
|
|
7423
|
-
if (typeof window.requestAnimationFrame === "function") {
|
|
7424
|
-
window.requestAnimationFrame(reportScroll);
|
|
7425
|
-
return;
|
|
7426
|
-
}
|
|
7427
|
-
reportScroll();
|
|
7428
|
-
}
|
|
7429
|
-
|
|
7430
|
-
function applyScrollPosition(target, x, y) {
|
|
7431
|
-
const element = resolveScrollSurface(target);
|
|
7432
|
-
if (element === undefined) {
|
|
7433
|
-
return false;
|
|
7434
|
-
}
|
|
7435
|
-
if (element && typeof element.scrollTo === "function") {
|
|
7436
|
-
element.scrollTo(x, y);
|
|
7437
|
-
} else if (element) {
|
|
7438
|
-
element.scrollLeft = x;
|
|
7439
|
-
element.scrollTop = y;
|
|
7440
|
-
} else {
|
|
7441
|
-
window.scrollTo(x, y);
|
|
7442
|
-
}
|
|
7443
|
-
const position = readScrollPosition(element);
|
|
7444
|
-
return position && Math.abs(position.x - x) <= 1 && Math.abs(position.y - y) <= 1;
|
|
7445
|
-
}
|
|
7446
|
-
|
|
7447
|
-
function restoreScroll(target, x, y) {
|
|
7448
|
-
if (applyScrollPosition(target, x, y)) {
|
|
7449
|
-
return;
|
|
7450
|
-
}
|
|
7451
|
-
let resizeObserver;
|
|
7452
|
-
let mutationObserver;
|
|
7453
|
-
let timeoutId;
|
|
7454
|
-
const stop = () => {
|
|
7455
|
-
resizeObserver?.disconnect();
|
|
7456
|
-
mutationObserver?.disconnect();
|
|
7457
|
-
if (timeoutId !== undefined && typeof window.clearTimeout === "function") {
|
|
7458
|
-
window.clearTimeout(timeoutId);
|
|
7459
|
-
}
|
|
7460
|
-
};
|
|
7461
|
-
const retry = () => {
|
|
7462
|
-
if (applyScrollPosition(target, x, y)) {
|
|
7463
|
-
stop();
|
|
7464
|
-
}
|
|
7465
|
-
};
|
|
7466
|
-
if (typeof window.ResizeObserver === "function") {
|
|
7467
|
-
resizeObserver = new window.ResizeObserver(retry);
|
|
7468
|
-
resizeObserver.observe(window.document.documentElement);
|
|
7469
|
-
if (window.document.body) {
|
|
7470
|
-
resizeObserver.observe(window.document.body);
|
|
7471
|
-
}
|
|
7472
|
-
}
|
|
7473
|
-
if (typeof window.MutationObserver === "function" && window.document.body) {
|
|
7474
|
-
mutationObserver = new window.MutationObserver(retry);
|
|
7475
|
-
mutationObserver.observe(window.document.body, { childList: true, subtree: true });
|
|
7476
|
-
}
|
|
7477
|
-
if (typeof window.setTimeout === "function") {
|
|
7478
|
-
timeoutId = window.setTimeout(stop, 10000);
|
|
7479
|
-
}
|
|
7480
|
-
}
|
|
7481
|
-
|
|
7482
|
-
if (typeof window.document.addEventListener === "function") {
|
|
7483
|
-
window.document.addEventListener("scroll", scheduleScrollReport, true);
|
|
7484
|
-
}
|
|
7485
|
-
window.addEventListener("message", (event) => {
|
|
7486
|
-
const data = event.data;
|
|
7487
|
-
if (
|
|
7488
|
-
event.source !== window.parent ||
|
|
7489
|
-
!data ||
|
|
7490
|
-
data.type !== scrollContract.restoreScrollMessageType ||
|
|
7491
|
-
data.version !== scrollContract.version ||
|
|
7492
|
-
!isScrollTarget(data.target) ||
|
|
7493
|
-
!Number.isFinite(data.x) ||
|
|
7494
|
-
!Number.isFinite(data.y) ||
|
|
7495
|
-
data.x < 0 ||
|
|
7496
|
-
data.y < 0
|
|
7497
|
-
) {
|
|
7498
|
-
return;
|
|
7499
|
-
}
|
|
7500
|
-
restoreScroll(data.target, data.x, data.y);
|
|
7501
|
-
});
|
|
7502
|
-
}`.trim();
|
|
7503
|
-
}
|
|
7504
|
-
function injectPanelAppBridgeScript(html, params) {
|
|
7505
|
-
if (html.includes(PANEL_APP_BRIDGE_MARKER)) return html;
|
|
7506
|
-
const script = `<script>${getPanelAppBridgeScript(params)}<\/script>`;
|
|
7507
|
-
const headMatch = /<head(?:\s[^>]*)?>/i.exec(html);
|
|
7508
|
-
if (headMatch?.index !== void 0) {
|
|
7509
|
-
const insertAt = headMatch.index + headMatch[0].length;
|
|
7510
|
-
return `${html.slice(0, insertAt)}${script}${html.slice(insertAt)}`;
|
|
7511
|
-
}
|
|
7512
|
-
return `${script}${html}`;
|
|
7513
|
-
}
|
|
7514
|
-
function getPanelAppBridgeScript(params = {
|
|
7515
|
-
appId: "",
|
|
7516
|
-
runtimeToken: ""
|
|
7517
|
-
}) {
|
|
7518
|
-
const appId = JSON.stringify(params.appId);
|
|
7519
|
-
const runtimeToken = JSON.stringify(params.runtimeToken);
|
|
7520
|
-
return `
|
|
7521
|
-
${getUiContentParamsBootstrapScript()}
|
|
7522
|
-
(() => {
|
|
7523
|
-
const requestType = "nextclaw:panel-app-service-actions:request";
|
|
7524
|
-
const responseType = "nextclaw:panel-app-service-actions:response";
|
|
7525
|
-
const appId = ${appId};
|
|
7526
|
-
const runtimeToken = ${runtimeToken};
|
|
7527
|
-
const pending = new Map();
|
|
7528
|
-
let counter = 0;
|
|
7529
|
-
|
|
7530
|
-
function createRequestId() {
|
|
7531
|
-
counter += 1;
|
|
7532
|
-
return "panel-bridge-" + Date.now().toString(36) + "-" + counter.toString(36);
|
|
7533
|
-
}
|
|
7534
|
-
|
|
7535
|
-
function request(method, payload) {
|
|
7536
|
-
const requestId = createRequestId();
|
|
7537
|
-
return new Promise((resolve, reject) => {
|
|
7538
|
-
pending.set(requestId, { method, resolve, reject });
|
|
7539
|
-
window.parent.postMessage({ type: requestType, requestId, appId, runtimeToken, method, payload }, "*");
|
|
7540
|
-
});
|
|
7541
|
-
}
|
|
7542
|
-
|
|
7543
|
-
${getPanelAppInlineContentHeightReporterScript()}
|
|
7544
|
-
${getPanelAppScrollRestorationScript()}
|
|
7545
|
-
|
|
7546
|
-
function resolveApiFetchUrl(input) {
|
|
7547
|
-
const raw = typeof input === "string" || input instanceof URL ? input.toString() : input?.url;
|
|
7548
|
-
if (typeof raw !== "string") {
|
|
7549
|
-
return null;
|
|
7550
|
-
}
|
|
7551
|
-
try {
|
|
7552
|
-
const url = new URL(raw, window.location.href);
|
|
7553
|
-
return url.origin === window.location.origin && url.pathname.startsWith("/api/") ? url : null;
|
|
7554
|
-
} catch {
|
|
7555
|
-
return null;
|
|
7556
|
-
}
|
|
7557
|
-
}
|
|
7558
|
-
|
|
7559
|
-
function createFetchInitWithRuntimeToken(input, init) {
|
|
7560
|
-
if (!resolveApiFetchUrl(input)) {
|
|
7561
|
-
return init;
|
|
7562
|
-
}
|
|
7563
|
-
const headers = new Headers(init?.headers || (typeof input === "object" && input ? input.headers : undefined));
|
|
7564
|
-
if (!headers.has("x-nextclaw-panel-bridge-session")) {
|
|
7565
|
-
headers.set("x-nextclaw-panel-bridge-session", runtimeToken);
|
|
7566
|
-
}
|
|
7567
|
-
return { ...init, headers };
|
|
7568
|
-
}
|
|
7569
|
-
|
|
7570
|
-
const nativeFetch = window.fetch?.bind(window);
|
|
7571
|
-
if (nativeFetch) {
|
|
7572
|
-
window.fetch = (input, init) => nativeFetch(input, createFetchInitWithRuntimeToken(input, init));
|
|
7573
|
-
}
|
|
7574
|
-
|
|
7575
|
-
function unwrapServiceActionResult(result) {
|
|
7576
|
-
if (!result || typeof result !== "object") {
|
|
7577
|
-
return result;
|
|
7578
|
-
}
|
|
7579
|
-
if (Object.prototype.hasOwnProperty.call(result, "structuredContent") && result.structuredContent !== undefined) {
|
|
7580
|
-
return result.structuredContent;
|
|
7581
|
-
}
|
|
7582
|
-
const content = Array.isArray(result.content) ? result.content : undefined;
|
|
7583
|
-
if (content && content.length === 1 && content[0]?.type === "text" && typeof content[0].text === "string") {
|
|
7584
|
-
try {
|
|
7585
|
-
return JSON.parse(content[0].text);
|
|
7586
|
-
} catch {
|
|
7587
|
-
return content[0].text;
|
|
7588
|
-
}
|
|
7589
|
-
}
|
|
7590
|
-
return result;
|
|
7591
|
-
}
|
|
7592
|
-
|
|
7593
|
-
function resolveBridgeData(entry, data) {
|
|
7594
|
-
if (entry.method === "list") {
|
|
7595
|
-
return Array.isArray(data.data?.actions) ? data.data.actions : [];
|
|
7596
|
-
}
|
|
7597
|
-
if (entry.method === "invoke") {
|
|
7598
|
-
return unwrapServiceActionResult(data.data?.result);
|
|
7599
|
-
}
|
|
7600
|
-
if (entry.method === "agent.generateObject") {
|
|
7601
|
-
return data.data?.result;
|
|
7602
|
-
}
|
|
7603
|
-
return data.data;
|
|
7604
|
-
}
|
|
7605
|
-
|
|
7606
|
-
window.addEventListener("message", (event) => {
|
|
7607
|
-
const data = event.data;
|
|
7608
|
-
if (!data || data.type !== responseType || typeof data.requestId !== "string") {
|
|
7609
|
-
return;
|
|
7610
|
-
}
|
|
7611
|
-
const entry = pending.get(data.requestId);
|
|
7612
|
-
if (!entry) {
|
|
7613
|
-
return;
|
|
7614
|
-
}
|
|
7615
|
-
pending.delete(data.requestId);
|
|
7616
|
-
if (data.ok) {
|
|
7617
|
-
entry.resolve(resolveBridgeData(entry, data));
|
|
7618
|
-
return;
|
|
7619
|
-
}
|
|
7620
|
-
const error = new Error(data.error?.message || "NextClaw panel bridge request failed.");
|
|
7621
|
-
error.code = data.error?.code;
|
|
7622
|
-
error.details = data.error?.details;
|
|
7623
|
-
entry.reject(error);
|
|
7624
|
-
});
|
|
7625
|
-
|
|
7626
|
-
const existing = window.nextclaw && typeof window.nextclaw === "object" ? window.nextclaw : {};
|
|
7627
|
-
Object.defineProperty(window, "nextclaw", {
|
|
7628
|
-
configurable: true,
|
|
7629
|
-
value: {
|
|
7630
|
-
...existing,
|
|
7631
|
-
serviceActions: {
|
|
7632
|
-
list: () => request("list", {}),
|
|
7633
|
-
invoke: (actionId, input) => request("invoke", { actionId, input }),
|
|
7634
|
-
requestGrant: (actionId) => request("requestGrant", { actionId }),
|
|
7635
|
-
revokeGrant: (actionId) => request("revokeGrant", { actionId })
|
|
7636
|
-
},
|
|
7637
|
-
agent: {
|
|
7638
|
-
send: (input) => request("agent.send", { request: input }),
|
|
7639
|
-
generateObject: (input) => request("agent.generateObject", { input })
|
|
7640
|
-
}
|
|
7641
|
-
}
|
|
7642
|
-
});
|
|
7643
|
-
installInlineContentHeightReporter();
|
|
7644
|
-
installScrollRestoration();
|
|
7645
|
-
})();
|
|
7646
|
-
`.trim();
|
|
7647
|
-
}
|
|
7648
|
-
//#endregion
|
|
7649
|
-
//#region src/utils/panel-app-client-injection.utils.ts
|
|
7650
|
-
const PANEL_APP_CLIENT_MARKER = "nextclaw:panel-app-client:init";
|
|
7651
|
-
const PANEL_APP_CLIENT_SDK_PATH = "/api/panel-app-client-sdk.js";
|
|
7652
|
-
function injectPanelAppClientScript(html, params) {
|
|
7653
|
-
if (html.includes(PANEL_APP_CLIENT_MARKER)) return html;
|
|
7654
|
-
const script = [`<script src="${PANEL_APP_CLIENT_SDK_PATH}" crossorigin="anonymous"><\/script>`, `<script>${getPanelAppClientInitScript(params)}<\/script>`].join("");
|
|
7655
|
-
const headMatch = /<head(?:\s[^>]*)?>/i.exec(html);
|
|
7656
|
-
if (headMatch?.index !== void 0) {
|
|
7657
|
-
const insertAt = headMatch.index + headMatch[0].length;
|
|
7658
|
-
return `${html.slice(0, insertAt)}${script}${html.slice(insertAt)}`;
|
|
7659
|
-
}
|
|
7660
|
-
return `${script}${html}`;
|
|
7661
|
-
}
|
|
7662
|
-
function getPanelAppClientInitScript(params) {
|
|
7663
|
-
return `
|
|
7664
|
-
(() => {
|
|
7665
|
-
const marker = "${PANEL_APP_CLIENT_MARKER}";
|
|
7666
|
-
if (typeof window.NextClawClient !== "function") {
|
|
7667
|
-
console.error("[NextClaw] Panel App client SDK failed to load.");
|
|
7668
|
-
return;
|
|
7669
|
-
}
|
|
7670
|
-
if (typeof window.createNextClawAppClient !== "function") {
|
|
7671
|
-
console.error("[NextClaw] Panel App client projection failed to load.");
|
|
7672
|
-
return;
|
|
7673
|
-
}
|
|
7674
|
-
const existing = window.nextclaw && typeof window.nextclaw === "object" ? window.nextclaw : {};
|
|
7675
|
-
const hostClient = new window.NextClawClient({
|
|
7676
|
-
baseUrl: window.location.origin,
|
|
7677
|
-
headers: {
|
|
7678
|
-
"x-nextclaw-panel-bridge-session": ${JSON.stringify(params.runtimeToken)}
|
|
7679
|
-
}
|
|
7680
|
-
});
|
|
7681
|
-
const client = window.createNextClawAppClient(hostClient);
|
|
7682
|
-
Object.defineProperty(window, "nextclaw", {
|
|
7683
|
-
configurable: true,
|
|
7684
|
-
value: {
|
|
7685
|
-
...existing,
|
|
7686
|
-
client
|
|
7687
|
-
}
|
|
7688
|
-
});
|
|
7689
|
-
Object.defineProperty(window.nextclaw, "__clientInitMarker", {
|
|
7690
|
-
configurable: true,
|
|
7691
|
-
enumerable: false,
|
|
7692
|
-
value: marker
|
|
7693
|
-
});
|
|
7694
|
-
})();
|
|
7695
|
-
`.trim();
|
|
7696
|
-
}
|
|
7697
|
-
//#endregion
|
|
7698
|
-
//#region src/utils/panel-app-manifest.utils.ts
|
|
7699
|
-
const PANEL_APP_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
7700
|
-
function parsePanelAppManifest(html) {
|
|
7701
|
-
return {
|
|
7702
|
-
...readHtmlTitle(html),
|
|
7703
|
-
...readStandardIcon(html),
|
|
7704
|
-
...readPanelAppMeta(html),
|
|
7705
|
-
capabilities: readPanelAppCapabilities(html),
|
|
7706
|
-
client: false,
|
|
7707
|
-
serviceActions: readPanelAppServiceActions(html)
|
|
7708
|
-
};
|
|
7709
|
-
}
|
|
7710
|
-
function parsePanelAppFolderManifest(raw) {
|
|
7711
|
-
let parsed;
|
|
7712
|
-
try {
|
|
7713
|
-
parsed = JSON.parse(raw);
|
|
7714
|
-
} catch (error) {
|
|
7715
|
-
throw new Error(`panel-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
7716
|
-
}
|
|
7717
|
-
if (!isRecord$8(parsed)) throw new Error("panel-app.json must contain an object.");
|
|
7718
|
-
const id = readOptionalString$8(parsed, "id");
|
|
7719
|
-
if (id && !PANEL_APP_ID_PATTERN.test(id)) throw new Error("panel app id must be kebab-case.");
|
|
7962
|
+
//#region src/utils/panel-app-agent.utils.ts
|
|
7963
|
+
const PANEL_APP_AGENT_DEFAULT_TIMEOUT_MS = 6e4;
|
|
7964
|
+
const PANEL_APP_AGENT_MAX_TIMEOUT_MS = 12e4;
|
|
7965
|
+
const PANEL_APP_AGENT_MAX_PROMPT_CHARS = 2e4;
|
|
7966
|
+
const PANEL_APP_AGENT_MAX_CONTEXT_CHARS = 8e4;
|
|
7967
|
+
function normalizePanelAppGenerateObjectInput(input) {
|
|
7968
|
+
const peerId = input.peerId.trim();
|
|
7969
|
+
const prompt = input.prompt.trim();
|
|
7970
|
+
if (!peerId || !prompt || !isRecord$9(input.schema)) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "invalid generateObject request");
|
|
7971
|
+
if (prompt.length > PANEL_APP_AGENT_MAX_PROMPT_CHARS) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "generateObject prompt is too large");
|
|
7972
|
+
if (stringifyJsonValue(input.context ?? null).length > PANEL_APP_AGENT_MAX_CONTEXT_CHARS) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "generateObject context is too large");
|
|
7720
7973
|
return {
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
client: readOptionalBoolean$2(parsed, "client"),
|
|
7728
|
-
serviceActions: readStringArray$1(parsed.actions, "actions")
|
|
7974
|
+
context: input.context,
|
|
7975
|
+
peerId,
|
|
7976
|
+
prompt,
|
|
7977
|
+
schema: input.schema,
|
|
7978
|
+
timeoutMs: normalizeTimeoutMs(input.timeoutMs),
|
|
7979
|
+
title: input.title?.trim() || void 0
|
|
7729
7980
|
};
|
|
7730
7981
|
}
|
|
7731
|
-
function
|
|
7982
|
+
function createPanelAppGenerateObjectMessage(params) {
|
|
7983
|
+
const { bridgeSession, request, requestId } = params;
|
|
7732
7984
|
return {
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7985
|
+
id: `panel-app-agent-message-${randomUUID()}`,
|
|
7986
|
+
metadata: {
|
|
7987
|
+
...createPanelAppAgentMetadata(bridgeSession),
|
|
7988
|
+
panel_app_peer_id: request.peerId,
|
|
7989
|
+
structured_result: {
|
|
7990
|
+
request_id: requestId,
|
|
7991
|
+
schema: structuredClone(request.schema),
|
|
7992
|
+
tool_name: STRUCTURED_RESULT_TOOL_NAME
|
|
7993
|
+
}
|
|
7994
|
+
},
|
|
7995
|
+
parts: [{
|
|
7996
|
+
type: "text",
|
|
7997
|
+
text: buildGenerateObjectPrompt(bridgeSession, request)
|
|
7998
|
+
}],
|
|
7999
|
+
role: "user",
|
|
8000
|
+
status: "final",
|
|
8001
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7736
8002
|
};
|
|
7737
8003
|
}
|
|
7738
|
-
function
|
|
7739
|
-
const
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
}
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
}
|
|
7754
|
-
function readMetaContent(html, name, valueKind) {
|
|
7755
|
-
const metaTags = html.matchAll(/<meta\s+([^>]*?)>/gi);
|
|
7756
|
-
for (const tag of metaTags) {
|
|
7757
|
-
const attributes = tag[1] ?? "";
|
|
7758
|
-
if (readHtmlAttribute(attributes, "name") === name) {
|
|
7759
|
-
const content = readHtmlAttribute(attributes, "content");
|
|
7760
|
-
return valueKind === "attribute" ? normalizeAttributeValue(content) : normalizeTextValue(content);
|
|
8004
|
+
async function waitForPanelAppStructuredResult(agentRunClient, params) {
|
|
8005
|
+
const iterator = agentRunClient.sendAndStreamEvents(params.payload)[Symbol.asyncIterator]();
|
|
8006
|
+
let timeoutId;
|
|
8007
|
+
const timeout = new Promise((_, reject) => {
|
|
8008
|
+
timeoutId = setTimeout(() => reject(new PanelAppError("AGENT_OBJECT_RESULT_TIMEOUT", "agent object result timed out")), params.timeoutMs);
|
|
8009
|
+
});
|
|
8010
|
+
let resultToolCallId = null;
|
|
8011
|
+
try {
|
|
8012
|
+
while (true) {
|
|
8013
|
+
const next = await Promise.race([iterator.next(), timeout]);
|
|
8014
|
+
if (next.done) break;
|
|
8015
|
+
const result = readStructuredResultEvent(next.value, resultToolCallId);
|
|
8016
|
+
if (result.matchedToolCallId) resultToolCallId = result.matchedToolCallId;
|
|
8017
|
+
if (result.submitted) return result.content;
|
|
8018
|
+
throwIfTerminalError(next.value);
|
|
7761
8019
|
}
|
|
8020
|
+
} finally {
|
|
8021
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
8022
|
+
await iterator.return?.(void 0);
|
|
7762
8023
|
}
|
|
8024
|
+
throw new PanelAppError("AGENT_OBJECT_RESULT_NOT_SUBMITTED", "agent did not submit a structured object result");
|
|
7763
8025
|
}
|
|
7764
|
-
function
|
|
7765
|
-
const
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
}
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
}
|
|
7793
|
-
function readOptionalBoolean$2(record, key) {
|
|
7794
|
-
const value = record[key];
|
|
7795
|
-
if (value === void 0) return false;
|
|
7796
|
-
if (typeof value !== "boolean") throw new Error(`panel app ${key} must be a boolean.`);
|
|
7797
|
-
return value;
|
|
7798
|
-
}
|
|
7799
|
-
function readStringArray$1(value, key) {
|
|
7800
|
-
if (value === void 0) return [];
|
|
7801
|
-
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`panel app ${key} must be a string array.`);
|
|
7802
|
-
return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))];
|
|
7803
|
-
}
|
|
7804
|
-
function isRecord$8(value) {
|
|
7805
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7806
|
-
}
|
|
7807
|
-
function normalizeTextValue(value) {
|
|
7808
|
-
return decodeHtmlAttribute(value ?? "").replace(/\s+/g, " ").trim() || void 0;
|
|
7809
|
-
}
|
|
7810
|
-
function normalizeAttributeValue(value) {
|
|
7811
|
-
return decodeHtmlAttribute(value ?? "").trim() || void 0;
|
|
7812
|
-
}
|
|
7813
|
-
function normalizeIconHref(value) {
|
|
7814
|
-
if (!value) return;
|
|
7815
|
-
if (value.startsWith("data:image/") || value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/")) return value;
|
|
7816
|
-
}
|
|
7817
|
-
function decodeHtmlAttribute(value) {
|
|
7818
|
-
return value.replace(/"/g, "\"").replace(/"/g, "\"").replace(/'/g, "'").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
7819
|
-
}
|
|
7820
|
-
const PANEL_APP_FOLDER_MANIFEST_FILE_NAME = "panel-app.json";
|
|
7821
|
-
function isPanelAppSourceEntry(entry) {
|
|
7822
|
-
return entry.isFile() && isPanelAppFileName(entry.name) || entry.isDirectory() && isPanelAppDirName(entry.name);
|
|
8026
|
+
function withPanelAppAgentMetadata(payload, bridgeSession) {
|
|
8027
|
+
const panelAppMetadata = createPanelAppAgentMetadata(bridgeSession);
|
|
8028
|
+
const peerId = readOptionalPeerId(payload.peerId);
|
|
8029
|
+
const sessionId = readOptionalSessionId(payload.sessionId);
|
|
8030
|
+
if (peerId && sessionId) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "agent send request cannot include both sessionId and peerId");
|
|
8031
|
+
const metadata = {
|
|
8032
|
+
...payload.metadata ?? {},
|
|
8033
|
+
...panelAppMetadata
|
|
8034
|
+
};
|
|
8035
|
+
if (peerId) metadata.panel_app_peer_id = peerId;
|
|
8036
|
+
if (Array.isArray(payload.content)) return {
|
|
8037
|
+
content: structuredClone(payload.content),
|
|
8038
|
+
metadata,
|
|
8039
|
+
peerId,
|
|
8040
|
+
sessionId
|
|
8041
|
+
};
|
|
8042
|
+
if (!payload.message) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "agent send request is required");
|
|
8043
|
+
return {
|
|
8044
|
+
message: {
|
|
8045
|
+
...structuredClone(payload.message),
|
|
8046
|
+
metadata: {
|
|
8047
|
+
...payload.message.metadata ?? {},
|
|
8048
|
+
...panelAppMetadata
|
|
8049
|
+
}
|
|
8050
|
+
},
|
|
8051
|
+
metadata,
|
|
8052
|
+
peerId,
|
|
8053
|
+
sessionId
|
|
8054
|
+
};
|
|
7823
8055
|
}
|
|
7824
|
-
function
|
|
7825
|
-
return
|
|
8056
|
+
function createPanelAppAgentMetadata(bridgeSession) {
|
|
8057
|
+
return {
|
|
8058
|
+
agent_peer_scope: `panel-app:${bridgeSession.appId}`,
|
|
8059
|
+
panel_app_bridge_request_id: randomUUID(),
|
|
8060
|
+
panel_app_id: bridgeSession.appId,
|
|
8061
|
+
source_kind: "panel_app"
|
|
8062
|
+
};
|
|
7826
8063
|
}
|
|
7827
|
-
function
|
|
7828
|
-
|
|
8064
|
+
function normalizeTimeoutMs(timeoutMs) {
|
|
8065
|
+
if (!Number.isFinite(timeoutMs) || typeof timeoutMs !== "number") return PANEL_APP_AGENT_DEFAULT_TIMEOUT_MS;
|
|
8066
|
+
return Math.min(PANEL_APP_AGENT_MAX_TIMEOUT_MS, Math.max(1e3, Math.trunc(timeoutMs)));
|
|
7829
8067
|
}
|
|
7830
|
-
function
|
|
7831
|
-
|
|
8068
|
+
function readOptionalPeerId(value) {
|
|
8069
|
+
if (typeof value !== "string") return;
|
|
8070
|
+
const peerId = value.trim();
|
|
8071
|
+
if (!peerId) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "agent peerId cannot be empty");
|
|
8072
|
+
return peerId;
|
|
7832
8073
|
}
|
|
7833
|
-
function
|
|
7834
|
-
|
|
8074
|
+
function readOptionalSessionId(value) {
|
|
8075
|
+
if (typeof value !== "string") return;
|
|
8076
|
+
const sessionId = value.trim();
|
|
8077
|
+
if (!sessionId) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "agent sessionId cannot be empty");
|
|
8078
|
+
return sessionId;
|
|
7835
8079
|
}
|
|
7836
|
-
function
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
8080
|
+
function buildGenerateObjectPrompt(bridgeSession, request) {
|
|
8081
|
+
return [
|
|
8082
|
+
"Panel App generateObject request",
|
|
8083
|
+
"",
|
|
8084
|
+
`Panel App ID: ${bridgeSession.appId}`,
|
|
8085
|
+
`Peer ID: ${request.peerId}`,
|
|
8086
|
+
"",
|
|
8087
|
+
"Context JSON:",
|
|
8088
|
+
stringifyJsonValue(request.context ?? null),
|
|
8089
|
+
"",
|
|
8090
|
+
"Task:",
|
|
8091
|
+
request.prompt,
|
|
8092
|
+
"",
|
|
8093
|
+
"Result contract:",
|
|
8094
|
+
`Call the ${STRUCTURED_RESULT_TOOL_NAME} tool exactly once with an object matching the provided schema.`,
|
|
8095
|
+
"Do not use natural language as the result."
|
|
8096
|
+
].join("\n");
|
|
7847
8097
|
}
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
8098
|
+
function readStructuredResultEvent(event, resultToolCallId) {
|
|
8099
|
+
if (event.type === NcpEventType.MessageToolCallStart && event.payload.toolName === "nextclaw_submit_result") return {
|
|
8100
|
+
matchedToolCallId: event.payload.toolCallId,
|
|
8101
|
+
submitted: false
|
|
8102
|
+
};
|
|
8103
|
+
if (event.type !== NcpEventType.MessageToolCallResult || event.payload.toolCallId !== resultToolCallId) return { submitted: false };
|
|
8104
|
+
assertToolResultContent(event.payload.content);
|
|
7852
8105
|
return {
|
|
7853
|
-
|
|
7854
|
-
|
|
8106
|
+
content: event.payload.content,
|
|
8107
|
+
submitted: true
|
|
7855
8108
|
};
|
|
7856
8109
|
}
|
|
7857
|
-
function
|
|
7858
|
-
if (!
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
const resolvedPath = resolve(rootPath, normalizedPath);
|
|
7862
|
-
const pathFromRoot = relative(resolve(rootPath), resolvedPath);
|
|
7863
|
-
if (pathFromRoot.startsWith("..") || isAbsolute(pathFromRoot)) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
7864
|
-
return resolvedPath;
|
|
8110
|
+
function assertToolResultContent(content) {
|
|
8111
|
+
if (!isRecord$9(content) || content.ok !== false || !isRecord$9(content.error)) return;
|
|
8112
|
+
if (content.error.code === "invalid_tool_arguments") throw new PanelAppError("AGENT_OBJECT_RESULT_SCHEMA_INVALID", "agent object result did not match the schema");
|
|
8113
|
+
throw new PanelAppError("AGENT_OBJECT_REQUEST_FAILED", typeof content.error.message === "string" ? content.error.message : "agent object request failed");
|
|
7865
8114
|
}
|
|
7866
|
-
function
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
default: return "application/octet-stream";
|
|
8115
|
+
function throwIfTerminalError(event) {
|
|
8116
|
+
if (event.type === NcpEventType.MessageFailed) throw new PanelAppError("AGENT_OBJECT_REQUEST_FAILED", event.payload.error.message);
|
|
8117
|
+
if (event.type === NcpEventType.RunError) throw new PanelAppError("AGENT_OBJECT_REQUEST_FAILED", event.payload.error ?? "agent object request failed");
|
|
8118
|
+
if (event.type === NcpEventType.RunFinished) throw new PanelAppError("AGENT_OBJECT_RESULT_NOT_SUBMITTED", "agent did not submit a structured object result");
|
|
8119
|
+
}
|
|
8120
|
+
function stringifyJsonValue(value) {
|
|
8121
|
+
try {
|
|
8122
|
+
return JSON.stringify(value, null, 2);
|
|
8123
|
+
} catch {
|
|
8124
|
+
throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "value is not JSON serializable");
|
|
7877
8125
|
}
|
|
7878
8126
|
}
|
|
7879
|
-
function
|
|
7880
|
-
|
|
7881
|
-
if (icon.startsWith("data:image/") || icon.startsWith("http://") || icon.startsWith("https://") || icon.startsWith("/") || isLikelyTextIcon(icon)) return icon;
|
|
7882
|
-
return `/api/panel-apps/${encodeURIComponent(id)}/assets/${encodePanelAppAssetPath(icon)}`;
|
|
8127
|
+
function isRecord$9(value) {
|
|
8128
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7883
8129
|
}
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
8130
|
+
//#endregion
|
|
8131
|
+
//#region src/services/panel-app-agent-bridge.service.ts
|
|
8132
|
+
var PanelAppAgentBridgeService = class {
|
|
8133
|
+
constructor(params) {
|
|
8134
|
+
this.params = params;
|
|
8135
|
+
}
|
|
8136
|
+
sendAgentMessage = async (bridgeSession, payload) => {
|
|
8137
|
+
await this.assertAgentCapabilityGranted(bridgeSession, "agent:send");
|
|
8138
|
+
return await this.requireAgentRunClient().send(withPanelAppAgentMetadata(payload, bridgeSession));
|
|
8139
|
+
};
|
|
8140
|
+
generateAgentObject = async (bridgeSession, input) => {
|
|
8141
|
+
await this.assertAgentCapabilityGranted(bridgeSession, "agent:generateObject");
|
|
8142
|
+
const request = normalizePanelAppGenerateObjectInput(input);
|
|
8143
|
+
const message = createPanelAppGenerateObjectMessage({
|
|
8144
|
+
bridgeSession,
|
|
8145
|
+
request,
|
|
8146
|
+
requestId: randomUUID()
|
|
8147
|
+
});
|
|
8148
|
+
return { result: await waitForPanelAppStructuredResult(this.requireAgentRunClient(), {
|
|
8149
|
+
payload: {
|
|
8150
|
+
message,
|
|
8151
|
+
metadata: {
|
|
8152
|
+
...createPanelAppAgentMetadata(bridgeSession),
|
|
8153
|
+
panel_app_peer_id: request.peerId
|
|
8154
|
+
},
|
|
8155
|
+
peerId: request.peerId
|
|
8156
|
+
},
|
|
8157
|
+
timeoutMs: request.timeoutMs
|
|
8158
|
+
}) };
|
|
8159
|
+
};
|
|
8160
|
+
grantAgentCapability = async (bridgeSession, capability) => {
|
|
8161
|
+
if (!isPanelAppAgentCapability(capability)) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "unknown panel app agent capability");
|
|
8162
|
+
this.assertDeclaredCapability(bridgeSession, capability);
|
|
8163
|
+
return await this.params.createCapabilityGrantStore().grant({
|
|
8164
|
+
caller: bridgeSession.caller,
|
|
8165
|
+
capability,
|
|
8166
|
+
grantedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8167
|
+
});
|
|
8168
|
+
};
|
|
8169
|
+
assertAgentCapabilityGranted = async (bridgeSession, capability) => {
|
|
8170
|
+
this.assertDeclaredCapability(bridgeSession, capability);
|
|
8171
|
+
if (!await this.params.createCapabilityGrantStore().isGranted(bridgeSession.caller, capability)) throw new PanelAppError("AUTHORIZATION_REQUIRED", `This panel app needs permission to use ${capability}.`);
|
|
8172
|
+
};
|
|
8173
|
+
assertDeclaredCapability = (bridgeSession, capability) => {
|
|
8174
|
+
if (!bridgeSession.declaredCapabilities.includes(capability)) throw new PanelAppError("PANEL_APP_CAPABILITY_NOT_DECLARED", this.describeMissingAgentCapability(bridgeSession.declaredCapabilities, capability));
|
|
8175
|
+
};
|
|
8176
|
+
describeMissingAgentCapability = (declaredCapabilities, capability) => {
|
|
8177
|
+
const declared = declaredCapabilities.length > 0 ? declaredCapabilities.join(", ") : "none";
|
|
8178
|
+
const valid = PANEL_APP_AGENT_CAPABILITIES.join(", ");
|
|
8179
|
+
const hint = declaredCapabilities.includes(capability.replace(":", ".")) ? ` Use ${capability}, not ${capability.replace(":", ".")}.` : "";
|
|
8180
|
+
return [
|
|
8181
|
+
`panel app did not declare ${capability}.`,
|
|
8182
|
+
`Declared: ${declared}.`,
|
|
8183
|
+
`Valid capabilities: ${valid}.`,
|
|
8184
|
+
`Declare it with nextclaw-panel-capabilities or panel-app.json capabilities.`,
|
|
8185
|
+
hint.trim()
|
|
8186
|
+
].filter(Boolean).join(" ");
|
|
8187
|
+
};
|
|
8188
|
+
requireAgentRunClient = () => {
|
|
8189
|
+
if (!this.params.agentRunClient) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "panel app agent client is not configured");
|
|
8190
|
+
return this.params.agentRunClient;
|
|
8191
|
+
};
|
|
8192
|
+
};
|
|
8193
|
+
//#endregion
|
|
8194
|
+
//#region src/utils/ui-content-params-injection.utils.ts
|
|
8195
|
+
const UI_CONTENT_PARAMS_BOOTSTRAP_MARKER = "nextclaw:content-params:bootstrap";
|
|
8196
|
+
function getUiContentParamsBootstrapScript() {
|
|
8197
|
+
return `
|
|
8198
|
+
/* ${UI_CONTENT_PARAMS_BOOTSTRAP_MARKER} */
|
|
8199
|
+
(() => {
|
|
8200
|
+
const contract = ${JSON.stringify(UI_CONTENT_PARAMS_HOST_CONTRACT)};
|
|
8201
|
+
const rawWindowName = typeof window.name === "string" ? window.name : "";
|
|
8202
|
+
if (!rawWindowName.startsWith(contract.windowNamePrefix)) {
|
|
8203
|
+
return;
|
|
8204
|
+
}
|
|
8205
|
+
window.name = "";
|
|
8206
|
+
let params;
|
|
8207
|
+
try {
|
|
8208
|
+
params = JSON.parse(rawWindowName.slice(contract.windowNamePrefix.length));
|
|
8209
|
+
} catch {
|
|
8210
|
+
return;
|
|
8211
|
+
}
|
|
8212
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
8213
|
+
return;
|
|
8214
|
+
}
|
|
8215
|
+
const freezeJson = (value) => {
|
|
8216
|
+
if (!value || typeof value !== "object" || Object.isFrozen(value)) {
|
|
8217
|
+
return value;
|
|
8218
|
+
}
|
|
8219
|
+
Object.values(value).forEach(freezeJson);
|
|
8220
|
+
return Object.freeze(value);
|
|
8221
|
+
};
|
|
8222
|
+
const existing = window.nextclaw && typeof window.nextclaw === "object"
|
|
8223
|
+
? window.nextclaw
|
|
8224
|
+
: {};
|
|
8225
|
+
Object.defineProperty(window, "nextclaw", {
|
|
8226
|
+
configurable: true,
|
|
8227
|
+
value: {
|
|
8228
|
+
...existing,
|
|
8229
|
+
params: freezeJson(params)
|
|
8230
|
+
}
|
|
8231
|
+
});
|
|
8232
|
+
})();
|
|
8233
|
+
`.trim();
|
|
7891
8234
|
}
|
|
7892
|
-
function
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
8235
|
+
function injectUiContentParamsBootstrap(html) {
|
|
8236
|
+
if (html.includes(UI_CONTENT_PARAMS_BOOTSTRAP_MARKER)) return html;
|
|
8237
|
+
const script = `<script>${getUiContentParamsBootstrapScript()}<\/script>`;
|
|
8238
|
+
const headMatch = /<head(?:\s[^>]*)?>/i.exec(html);
|
|
8239
|
+
if (headMatch?.index !== void 0) {
|
|
8240
|
+
const insertAt = headMatch.index + headMatch[0].length;
|
|
8241
|
+
return `${html.slice(0, insertAt)}${script}${html.slice(insertAt)}`;
|
|
8242
|
+
}
|
|
8243
|
+
return `${script}${html}`;
|
|
7898
8244
|
}
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
8245
|
+
//#endregion
|
|
8246
|
+
//#region src/utils/panel-app-bridge.utils.ts
|
|
8247
|
+
const PANEL_APP_BRIDGE_MARKER = "nextclaw:panel-app-service-actions:request";
|
|
8248
|
+
function getPanelAppInlineContentHeightReporterScript() {
|
|
8249
|
+
return `
|
|
8250
|
+
function installInlineContentHeightReporter() {
|
|
8251
|
+
const inlineHostContract = ${JSON.stringify(PANEL_APP_INLINE_HOST_CONTRACT)};
|
|
8252
|
+
const readInlineContentHeight = ${readInlineContentHeight.toString()};
|
|
8253
|
+
if (!window.location || !window.document) {
|
|
8254
|
+
return;
|
|
8255
|
+
}
|
|
8256
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
8257
|
+
if (
|
|
8258
|
+
searchParams.get(inlineHostContract.displayModeSearchParam) !== inlineHostContract.displayMode ||
|
|
8259
|
+
searchParams.get(inlineHostContract.placementSearchParam) !== inlineHostContract.placement
|
|
8260
|
+
) {
|
|
8261
|
+
return;
|
|
8262
|
+
}
|
|
8263
|
+
const start = () => {
|
|
8264
|
+
const { body, documentElement } = window.document;
|
|
8265
|
+
if (!documentElement) {
|
|
8266
|
+
return;
|
|
8267
|
+
}
|
|
8268
|
+
let lastHeight = 0;
|
|
8269
|
+
const reportHeight = () => {
|
|
8270
|
+
const height = readInlineContentHeight(body, documentElement);
|
|
8271
|
+
if (height > 0 && height !== lastHeight) {
|
|
8272
|
+
lastHeight = height;
|
|
8273
|
+
window.parent.postMessage({ type: inlineHostContract.contentHeightMessageType, height }, "*");
|
|
8274
|
+
}
|
|
8275
|
+
};
|
|
8276
|
+
if (typeof window.ResizeObserver === "function") {
|
|
8277
|
+
const observer = new window.ResizeObserver(reportHeight);
|
|
8278
|
+
observer.observe(documentElement);
|
|
8279
|
+
if (body) {
|
|
8280
|
+
observer.observe(body);
|
|
8281
|
+
}
|
|
8282
|
+
}
|
|
8283
|
+
window.addEventListener("load", reportHeight);
|
|
8284
|
+
reportHeight();
|
|
8285
|
+
};
|
|
8286
|
+
if (window.document.readyState === "loading") {
|
|
8287
|
+
window.document.addEventListener("DOMContentLoaded", start, { once: true });
|
|
8288
|
+
return;
|
|
8289
|
+
}
|
|
8290
|
+
start();
|
|
8291
|
+
}`.trim();
|
|
8292
|
+
}
|
|
8293
|
+
function getPanelAppScrollSurfaceHelpersScript() {
|
|
8294
|
+
return `
|
|
8295
|
+
function readScrollPosition(element) {
|
|
8296
|
+
const x = element ? element.scrollLeft : window.scrollX;
|
|
8297
|
+
const y = element ? element.scrollTop : window.scrollY;
|
|
8298
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || x < 0 || y < 0) {
|
|
8299
|
+
return null;
|
|
8300
|
+
}
|
|
8301
|
+
return { x, y };
|
|
8302
|
+
}
|
|
8303
|
+
|
|
8304
|
+
function getScrollSurface(target) {
|
|
8305
|
+
const root = window.document.scrollingElement;
|
|
8306
|
+
if (
|
|
8307
|
+
!target ||
|
|
8308
|
+
target === window.document ||
|
|
8309
|
+
target === root ||
|
|
8310
|
+
target === window.document.documentElement ||
|
|
8311
|
+
target === window.document.body
|
|
8312
|
+
) {
|
|
8313
|
+
return { kind: "document" };
|
|
8314
|
+
}
|
|
8315
|
+
if (!target.parentElement || !target.children || typeof target.scrollTop !== "number") {
|
|
8316
|
+
return null;
|
|
8317
|
+
}
|
|
8318
|
+
const path = [];
|
|
8319
|
+
let element = target;
|
|
8320
|
+
while (element && element !== window.document.body) {
|
|
8321
|
+
const parent = element.parentElement;
|
|
8322
|
+
if (!parent || !parent.children) {
|
|
8323
|
+
return null;
|
|
8324
|
+
}
|
|
8325
|
+
const index = Array.prototype.indexOf.call(parent.children, element);
|
|
8326
|
+
const tagName = typeof element.tagName === "string" ? element.tagName.toLowerCase() : "";
|
|
8327
|
+
if (index < 0 || !tagName) {
|
|
8328
|
+
return null;
|
|
8329
|
+
}
|
|
8330
|
+
path.unshift({ index, tagName });
|
|
8331
|
+
element = parent;
|
|
8332
|
+
}
|
|
8333
|
+
return element === window.document.body && path.length > 0 ? { kind: "element", path } : null;
|
|
8334
|
+
}
|
|
8335
|
+
|
|
8336
|
+
function resolveScrollSurface(target) {
|
|
8337
|
+
if (target.kind === "document") {
|
|
8338
|
+
return null;
|
|
8339
|
+
}
|
|
8340
|
+
let element = window.document.body;
|
|
8341
|
+
for (const segment of target.path) {
|
|
8342
|
+
const child = element?.children?.[segment.index];
|
|
8343
|
+
if (!child || child.tagName?.toLowerCase() !== segment.tagName) {
|
|
8344
|
+
return undefined;
|
|
8345
|
+
}
|
|
8346
|
+
element = child;
|
|
8347
|
+
}
|
|
8348
|
+
return element;
|
|
8349
|
+
}
|
|
8350
|
+
|
|
8351
|
+
function isScrollTarget(value) {
|
|
8352
|
+
if (!value || typeof value !== "object") {
|
|
8353
|
+
return false;
|
|
8354
|
+
}
|
|
8355
|
+
if (value.kind === "document") {
|
|
8356
|
+
return true;
|
|
8357
|
+
}
|
|
8358
|
+
return value.kind === "element" &&
|
|
8359
|
+
Array.isArray(value.path) &&
|
|
8360
|
+
value.path.length > 0 &&
|
|
8361
|
+
value.path.length <= 30 &&
|
|
8362
|
+
value.path.every((segment) =>
|
|
8363
|
+
segment &&
|
|
8364
|
+
Number.isInteger(segment.index) &&
|
|
8365
|
+
segment.index >= 0 &&
|
|
8366
|
+
segment.index <= 1000 &&
|
|
8367
|
+
typeof segment.tagName === "string" &&
|
|
8368
|
+
segment.tagName.length > 0 &&
|
|
8369
|
+
segment.tagName.length <= 32
|
|
8370
|
+
);
|
|
8371
|
+
}`.trim();
|
|
7902
8372
|
}
|
|
7903
|
-
function
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
8373
|
+
function getPanelAppScrollRestorationScript() {
|
|
8374
|
+
return `
|
|
8375
|
+
function installScrollRestoration() {
|
|
8376
|
+
const scrollContract = ${JSON.stringify(PANEL_APP_SCROLL_RESTORATION_CONTRACT)};
|
|
8377
|
+
const inlineHostContract = ${JSON.stringify(PANEL_APP_INLINE_HOST_CONTRACT)};
|
|
8378
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
8379
|
+
if (
|
|
8380
|
+
searchParams.get(inlineHostContract.displayModeSearchParam) === inlineHostContract.displayMode &&
|
|
8381
|
+
searchParams.get(inlineHostContract.placementSearchParam) === inlineHostContract.placement
|
|
8382
|
+
) {
|
|
8383
|
+
return;
|
|
8384
|
+
}
|
|
8385
|
+
let isScrollReportScheduled = false;
|
|
8386
|
+
let latestScrollSurface = null;
|
|
8387
|
+
|
|
8388
|
+
${getPanelAppScrollSurfaceHelpersScript()}
|
|
8389
|
+
|
|
8390
|
+
function reportScroll() {
|
|
8391
|
+
isScrollReportScheduled = false;
|
|
8392
|
+
const surface = latestScrollSurface;
|
|
8393
|
+
latestScrollSurface = null;
|
|
8394
|
+
if (!surface) {
|
|
8395
|
+
return;
|
|
8396
|
+
}
|
|
8397
|
+
const position = readScrollPosition(resolveScrollSurface(surface));
|
|
8398
|
+
if (!position) {
|
|
8399
|
+
return;
|
|
8400
|
+
}
|
|
8401
|
+
window.parent.postMessage({
|
|
8402
|
+
type: scrollContract.scrollMessageType,
|
|
8403
|
+
version: scrollContract.version,
|
|
8404
|
+
target: surface,
|
|
8405
|
+
...position,
|
|
8406
|
+
}, "*");
|
|
8407
|
+
}
|
|
8408
|
+
|
|
8409
|
+
function scheduleScrollReport(event) {
|
|
8410
|
+
const surface = getScrollSurface(event?.target);
|
|
8411
|
+
if (!surface) {
|
|
8412
|
+
return;
|
|
8413
|
+
}
|
|
8414
|
+
latestScrollSurface = surface;
|
|
8415
|
+
if (isScrollReportScheduled) {
|
|
8416
|
+
return;
|
|
8417
|
+
}
|
|
8418
|
+
isScrollReportScheduled = true;
|
|
8419
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
8420
|
+
window.requestAnimationFrame(reportScroll);
|
|
8421
|
+
return;
|
|
8422
|
+
}
|
|
8423
|
+
reportScroll();
|
|
8424
|
+
}
|
|
8425
|
+
|
|
8426
|
+
function applyScrollPosition(target, x, y) {
|
|
8427
|
+
const element = resolveScrollSurface(target);
|
|
8428
|
+
if (element === undefined) {
|
|
8429
|
+
return false;
|
|
8430
|
+
}
|
|
8431
|
+
if (element && typeof element.scrollTo === "function") {
|
|
8432
|
+
element.scrollTo(x, y);
|
|
8433
|
+
} else if (element) {
|
|
8434
|
+
element.scrollLeft = x;
|
|
8435
|
+
element.scrollTop = y;
|
|
8436
|
+
} else {
|
|
8437
|
+
window.scrollTo(x, y);
|
|
8438
|
+
}
|
|
8439
|
+
const position = readScrollPosition(element);
|
|
8440
|
+
return position && Math.abs(position.x - x) <= 1 && Math.abs(position.y - y) <= 1;
|
|
8441
|
+
}
|
|
8442
|
+
|
|
8443
|
+
function restoreScroll(target, x, y) {
|
|
8444
|
+
if (applyScrollPosition(target, x, y)) {
|
|
8445
|
+
return;
|
|
8446
|
+
}
|
|
8447
|
+
let resizeObserver;
|
|
8448
|
+
let mutationObserver;
|
|
8449
|
+
let timeoutId;
|
|
8450
|
+
const stop = () => {
|
|
8451
|
+
resizeObserver?.disconnect();
|
|
8452
|
+
mutationObserver?.disconnect();
|
|
8453
|
+
if (timeoutId !== undefined && typeof window.clearTimeout === "function") {
|
|
8454
|
+
window.clearTimeout(timeoutId);
|
|
8455
|
+
}
|
|
8456
|
+
};
|
|
8457
|
+
const retry = () => {
|
|
8458
|
+
if (applyScrollPosition(target, x, y)) {
|
|
8459
|
+
stop();
|
|
8460
|
+
}
|
|
8461
|
+
};
|
|
8462
|
+
if (typeof window.ResizeObserver === "function") {
|
|
8463
|
+
resizeObserver = new window.ResizeObserver(retry);
|
|
8464
|
+
resizeObserver.observe(window.document.documentElement);
|
|
8465
|
+
if (window.document.body) {
|
|
8466
|
+
resizeObserver.observe(window.document.body);
|
|
8467
|
+
}
|
|
8468
|
+
}
|
|
8469
|
+
if (typeof window.MutationObserver === "function" && window.document.body) {
|
|
8470
|
+
mutationObserver = new window.MutationObserver(retry);
|
|
8471
|
+
mutationObserver.observe(window.document.body, { childList: true, subtree: true });
|
|
8472
|
+
}
|
|
8473
|
+
if (typeof window.setTimeout === "function") {
|
|
8474
|
+
timeoutId = window.setTimeout(stop, 10000);
|
|
8475
|
+
}
|
|
8476
|
+
}
|
|
8477
|
+
|
|
8478
|
+
if (typeof window.document.addEventListener === "function") {
|
|
8479
|
+
window.document.addEventListener("scroll", scheduleScrollReport, true);
|
|
8480
|
+
}
|
|
8481
|
+
window.addEventListener("message", (event) => {
|
|
8482
|
+
const data = event.data;
|
|
8483
|
+
if (
|
|
8484
|
+
event.source !== window.parent ||
|
|
8485
|
+
!data ||
|
|
8486
|
+
data.type !== scrollContract.restoreScrollMessageType ||
|
|
8487
|
+
data.version !== scrollContract.version ||
|
|
8488
|
+
!isScrollTarget(data.target) ||
|
|
8489
|
+
!Number.isFinite(data.x) ||
|
|
8490
|
+
!Number.isFinite(data.y) ||
|
|
8491
|
+
data.x < 0 ||
|
|
8492
|
+
data.y < 0
|
|
8493
|
+
) {
|
|
8494
|
+
return;
|
|
8495
|
+
}
|
|
8496
|
+
restoreScroll(data.target, data.x, data.y);
|
|
8497
|
+
});
|
|
8498
|
+
}`.trim();
|
|
7907
8499
|
}
|
|
7908
|
-
function
|
|
7909
|
-
|
|
8500
|
+
function injectPanelAppBridgeScript(html, params) {
|
|
8501
|
+
if (html.includes(PANEL_APP_BRIDGE_MARKER)) return html;
|
|
8502
|
+
const script = `<script>${getPanelAppBridgeScript(params)}<\/script>`;
|
|
8503
|
+
const headMatch = /<head(?:\s[^>]*)?>/i.exec(html);
|
|
8504
|
+
if (headMatch?.index !== void 0) {
|
|
8505
|
+
const insertAt = headMatch.index + headMatch[0].length;
|
|
8506
|
+
return `${html.slice(0, insertAt)}${script}${html.slice(insertAt)}`;
|
|
8507
|
+
}
|
|
8508
|
+
return `${script}${html}`;
|
|
7910
8509
|
}
|
|
7911
|
-
function
|
|
7912
|
-
|
|
8510
|
+
function getPanelAppBridgeScript(params = {
|
|
8511
|
+
appId: "",
|
|
8512
|
+
runtimeToken: ""
|
|
8513
|
+
}) {
|
|
8514
|
+
const appId = JSON.stringify(params.appId);
|
|
8515
|
+
const runtimeToken = JSON.stringify(params.runtimeToken);
|
|
8516
|
+
return `
|
|
8517
|
+
${getUiContentParamsBootstrapScript()}
|
|
8518
|
+
(() => {
|
|
8519
|
+
const requestType = "nextclaw:panel-app-service-actions:request";
|
|
8520
|
+
const responseType = "nextclaw:panel-app-service-actions:response";
|
|
8521
|
+
const appId = ${appId};
|
|
8522
|
+
const runtimeToken = ${runtimeToken};
|
|
8523
|
+
const pending = new Map();
|
|
8524
|
+
let counter = 0;
|
|
8525
|
+
|
|
8526
|
+
function createRequestId() {
|
|
8527
|
+
counter += 1;
|
|
8528
|
+
return "panel-bridge-" + Date.now().toString(36) + "-" + counter.toString(36);
|
|
8529
|
+
}
|
|
8530
|
+
|
|
8531
|
+
function request(method, payload) {
|
|
8532
|
+
const requestId = createRequestId();
|
|
8533
|
+
return new Promise((resolve, reject) => {
|
|
8534
|
+
pending.set(requestId, { method, resolve, reject });
|
|
8535
|
+
window.parent.postMessage({ type: requestType, requestId, appId, runtimeToken, method, payload }, "*");
|
|
8536
|
+
});
|
|
8537
|
+
}
|
|
8538
|
+
|
|
8539
|
+
${getPanelAppInlineContentHeightReporterScript()}
|
|
8540
|
+
${getPanelAppScrollRestorationScript()}
|
|
8541
|
+
|
|
8542
|
+
function resolveApiFetchUrl(input) {
|
|
8543
|
+
const raw = typeof input === "string" || input instanceof URL ? input.toString() : input?.url;
|
|
8544
|
+
if (typeof raw !== "string") {
|
|
8545
|
+
return null;
|
|
8546
|
+
}
|
|
8547
|
+
try {
|
|
8548
|
+
const url = new URL(raw, window.location.href);
|
|
8549
|
+
return url.origin === window.location.origin && url.pathname.startsWith("/api/") ? url : null;
|
|
8550
|
+
} catch {
|
|
8551
|
+
return null;
|
|
8552
|
+
}
|
|
8553
|
+
}
|
|
8554
|
+
|
|
8555
|
+
function createFetchInitWithRuntimeToken(input, init) {
|
|
8556
|
+
if (!resolveApiFetchUrl(input)) {
|
|
8557
|
+
return init;
|
|
8558
|
+
}
|
|
8559
|
+
const headers = new Headers(init?.headers || (typeof input === "object" && input ? input.headers : undefined));
|
|
8560
|
+
if (!headers.has("x-nextclaw-panel-bridge-session")) {
|
|
8561
|
+
headers.set("x-nextclaw-panel-bridge-session", runtimeToken);
|
|
8562
|
+
}
|
|
8563
|
+
return { ...init, headers };
|
|
8564
|
+
}
|
|
8565
|
+
|
|
8566
|
+
const nativeFetch = window.fetch?.bind(window);
|
|
8567
|
+
if (nativeFetch) {
|
|
8568
|
+
window.fetch = (input, init) => nativeFetch(input, createFetchInitWithRuntimeToken(input, init));
|
|
8569
|
+
}
|
|
8570
|
+
|
|
8571
|
+
function unwrapServiceActionResult(result) {
|
|
8572
|
+
if (!result || typeof result !== "object") {
|
|
8573
|
+
return result;
|
|
8574
|
+
}
|
|
8575
|
+
if (Object.prototype.hasOwnProperty.call(result, "structuredContent") && result.structuredContent !== undefined) {
|
|
8576
|
+
return result.structuredContent;
|
|
8577
|
+
}
|
|
8578
|
+
const content = Array.isArray(result.content) ? result.content : undefined;
|
|
8579
|
+
if (content && content.length === 1 && content[0]?.type === "text" && typeof content[0].text === "string") {
|
|
8580
|
+
try {
|
|
8581
|
+
return JSON.parse(content[0].text);
|
|
8582
|
+
} catch {
|
|
8583
|
+
return content[0].text;
|
|
8584
|
+
}
|
|
8585
|
+
}
|
|
8586
|
+
return result;
|
|
8587
|
+
}
|
|
8588
|
+
|
|
8589
|
+
function resolveBridgeData(entry, data) {
|
|
8590
|
+
if (entry.method === "list") {
|
|
8591
|
+
return Array.isArray(data.data?.actions) ? data.data.actions : [];
|
|
8592
|
+
}
|
|
8593
|
+
if (entry.method === "invoke") {
|
|
8594
|
+
return unwrapServiceActionResult(data.data?.result);
|
|
8595
|
+
}
|
|
8596
|
+
if (entry.method === "agent.generateObject") {
|
|
8597
|
+
return data.data?.result;
|
|
8598
|
+
}
|
|
8599
|
+
return data.data;
|
|
8600
|
+
}
|
|
8601
|
+
|
|
8602
|
+
window.addEventListener("message", (event) => {
|
|
8603
|
+
const data = event.data;
|
|
8604
|
+
if (!data || data.type !== responseType || typeof data.requestId !== "string") {
|
|
8605
|
+
return;
|
|
8606
|
+
}
|
|
8607
|
+
const entry = pending.get(data.requestId);
|
|
8608
|
+
if (!entry) {
|
|
8609
|
+
return;
|
|
8610
|
+
}
|
|
8611
|
+
pending.delete(data.requestId);
|
|
8612
|
+
if (data.ok) {
|
|
8613
|
+
entry.resolve(resolveBridgeData(entry, data));
|
|
8614
|
+
return;
|
|
8615
|
+
}
|
|
8616
|
+
const error = new Error(data.error?.message || "NextClaw panel bridge request failed.");
|
|
8617
|
+
error.code = data.error?.code;
|
|
8618
|
+
error.details = data.error?.details;
|
|
8619
|
+
entry.reject(error);
|
|
8620
|
+
});
|
|
8621
|
+
|
|
8622
|
+
const existing = window.nextclaw && typeof window.nextclaw === "object" ? window.nextclaw : {};
|
|
8623
|
+
Object.defineProperty(window, "nextclaw", {
|
|
8624
|
+
configurable: true,
|
|
8625
|
+
value: {
|
|
8626
|
+
...existing,
|
|
8627
|
+
serviceActions: {
|
|
8628
|
+
list: () => request("list", {}),
|
|
8629
|
+
invoke: (actionId, input) => request("invoke", { actionId, input }),
|
|
8630
|
+
requestGrant: (actionId) => request("requestGrant", { actionId }),
|
|
8631
|
+
revokeGrant: (actionId) => request("revokeGrant", { actionId })
|
|
8632
|
+
},
|
|
8633
|
+
agent: {
|
|
8634
|
+
send: (input) => request("agent.send", { request: input }),
|
|
8635
|
+
generateObject: (input) => request("agent.generateObject", { input })
|
|
8636
|
+
}
|
|
8637
|
+
}
|
|
8638
|
+
});
|
|
8639
|
+
installInlineContentHeightReporter();
|
|
8640
|
+
installScrollRestoration();
|
|
8641
|
+
})();
|
|
8642
|
+
`.trim();
|
|
7913
8643
|
}
|
|
7914
|
-
|
|
7915
|
-
|
|
8644
|
+
//#endregion
|
|
8645
|
+
//#region src/utils/panel-app-client-injection.utils.ts
|
|
8646
|
+
const PANEL_APP_CLIENT_MARKER = "nextclaw:panel-app-client:init";
|
|
8647
|
+
const PANEL_APP_CLIENT_SDK_PATH = "/api/panel-app-client-sdk.js";
|
|
8648
|
+
function injectPanelAppClientScript(html, params) {
|
|
8649
|
+
if (html.includes(PANEL_APP_CLIENT_MARKER)) return html;
|
|
8650
|
+
const script = [`<script src="${PANEL_APP_CLIENT_SDK_PATH}" crossorigin="anonymous"><\/script>`, `<script>${getPanelAppClientInitScript(params)}<\/script>`].join("");
|
|
8651
|
+
const headMatch = /<head(?:\s[^>]*)?>/i.exec(html);
|
|
8652
|
+
if (headMatch?.index !== void 0) {
|
|
8653
|
+
const insertAt = headMatch.index + headMatch[0].length;
|
|
8654
|
+
return `${html.slice(0, insertAt)}${script}${html.slice(insertAt)}`;
|
|
8655
|
+
}
|
|
8656
|
+
return `${script}${html}`;
|
|
7916
8657
|
}
|
|
7917
|
-
function
|
|
7918
|
-
return
|
|
8658
|
+
function getPanelAppClientInitScript(params) {
|
|
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();
|
|
7919
8692
|
}
|
|
7920
8693
|
//#endregion
|
|
7921
8694
|
//#region src/services/panel-app-source.service.ts
|
|
@@ -8016,84 +8789,6 @@ function isMissingFileError$1(error) {
|
|
|
8016
8789
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
8017
8790
|
}
|
|
8018
8791
|
//#endregion
|
|
8019
|
-
//#region src/utils/panel-app-time.utils.ts
|
|
8020
|
-
function resolvePanelAppCreatedAt(fileStat) {
|
|
8021
|
-
return fileStat.birthtimeMs > 0 ? fileStat.birthtime.toISOString() : fileStat.mtime.toISOString();
|
|
8022
|
-
}
|
|
8023
|
-
function resolvePanelAppActivityMs(entry) {
|
|
8024
|
-
return Math.max(new Date(entry.lastOpenedAt ?? 0).getTime(), new Date(entry.createdAt).getTime(), new Date(entry.updatedAt).getTime());
|
|
8025
|
-
}
|
|
8026
|
-
//#endregion
|
|
8027
|
-
//#region src/utils/panel-app-content-source.utils.ts
|
|
8028
|
-
async function readPanelAppContentSource(params) {
|
|
8029
|
-
const { createAssetBaseHref, id, panelsPath, sourceService } = params;
|
|
8030
|
-
return await readResolvedPanelAppContentSource(await sourceService.resolveSource(panelsPath, id), createAssetBaseHref);
|
|
8031
|
-
}
|
|
8032
|
-
async function readPanelAppContentSourceByPath(params) {
|
|
8033
|
-
const { createAssetBaseHref, path, sourceService } = params;
|
|
8034
|
-
return await readResolvedPanelAppContentSource(await sourceService.resolveSourcePath(path), createAssetBaseHref);
|
|
8035
|
-
}
|
|
8036
|
-
async function readPanelAppContentSourceByIdOrPath(params) {
|
|
8037
|
-
const { sourcePath, ...standardSourceParams } = params;
|
|
8038
|
-
return sourcePath ? await readPanelAppContentSourceByPath({
|
|
8039
|
-
createAssetBaseHref: params.createAssetBaseHref,
|
|
8040
|
-
path: sourcePath,
|
|
8041
|
-
sourceService: params.sourceService
|
|
8042
|
-
}) : await readPanelAppContentSource(standardSourceParams);
|
|
8043
|
-
}
|
|
8044
|
-
async function readResolvedPanelAppContentSource(source, createAssetBaseHref) {
|
|
8045
|
-
const html = await readFile(source.entryPath, "utf8");
|
|
8046
|
-
const manifest = source.manifest ?? parsePanelAppManifest(html);
|
|
8047
|
-
const sourceId = encodePanelAppId(source.sourceName);
|
|
8048
|
-
return {
|
|
8049
|
-
appId: resolvePanelAppAppId(source, manifest),
|
|
8050
|
-
sourceId,
|
|
8051
|
-
source,
|
|
8052
|
-
manifest,
|
|
8053
|
-
html,
|
|
8054
|
-
htmlWithBase: source.kind === "folder" ? injectPanelAppAssetBase(html, createAssetBaseHref(source)) : html
|
|
8055
|
-
};
|
|
8056
|
-
}
|
|
8057
|
-
async function readPanelAppContentSourceByIdOrAppId(params) {
|
|
8058
|
-
const { appIdOrSourceId, createAssetBaseHref, panelsPath, sourceService } = params;
|
|
8059
|
-
try {
|
|
8060
|
-
return await readPanelAppContentSource({
|
|
8061
|
-
createAssetBaseHref,
|
|
8062
|
-
id: appIdOrSourceId,
|
|
8063
|
-
panelsPath,
|
|
8064
|
-
sourceService
|
|
8065
|
-
});
|
|
8066
|
-
} catch (error) {
|
|
8067
|
-
if (!isPanelAppError(error)) throw error;
|
|
8068
|
-
if (error.code !== "PANEL_APP_INVALID_ID" && error.code !== "PANEL_APP_NOT_FOUND") throw error;
|
|
8069
|
-
}
|
|
8070
|
-
const sources = await sourceService.listSources(panelsPath);
|
|
8071
|
-
for (const source of sources) {
|
|
8072
|
-
const html = await readFile(source.entryPath, "utf8");
|
|
8073
|
-
if (resolvePanelAppAppId(source, source.manifest ?? parsePanelAppManifest(html)) === appIdOrSourceId) return await readPanelAppContentSource({
|
|
8074
|
-
createAssetBaseHref,
|
|
8075
|
-
id: encodePanelAppId(source.sourceName),
|
|
8076
|
-
panelsPath,
|
|
8077
|
-
sourceService
|
|
8078
|
-
});
|
|
8079
|
-
}
|
|
8080
|
-
throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
8081
|
-
}
|
|
8082
|
-
function resolvePanelAppAppId(source, manifest) {
|
|
8083
|
-
return manifest.id ?? encodePanelAppId(source.sourceName);
|
|
8084
|
-
}
|
|
8085
|
-
async function assertPanelAppDeclaresClient(params) {
|
|
8086
|
-
const { appId, panelsPath, sourceService } = params;
|
|
8087
|
-
const sources = await sourceService.listSources(panelsPath);
|
|
8088
|
-
for (const source of sources) {
|
|
8089
|
-
const manifest = source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8"));
|
|
8090
|
-
if (resolvePanelAppAppId(source, manifest) !== appId) continue;
|
|
8091
|
-
if (!manifest.client) throw new PanelAppError("PANEL_APP_CLIENT_NOT_DECLARED", "panel app did not declare client access");
|
|
8092
|
-
return;
|
|
8093
|
-
}
|
|
8094
|
-
throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
8095
|
-
}
|
|
8096
|
-
//#endregion
|
|
8097
8792
|
//#region src/managers/panel-app.manager.ts
|
|
8098
8793
|
const PANEL_APP_CONTENT_BASE_PATH = "/api/panel-apps";
|
|
8099
8794
|
const PANEL_APP_TOKENIZED_ASSET_BASE_PATH = "/api/panel-app-assets";
|
|
@@ -8107,6 +8802,8 @@ var PanelAppManager = class {
|
|
|
8107
8802
|
agentBridgeService;
|
|
8108
8803
|
assetTokenService = new PanelAppAssetTokenService();
|
|
8109
8804
|
sourceService = new PanelAppSourceService();
|
|
8805
|
+
packageStateManager;
|
|
8806
|
+
entryPresenter;
|
|
8110
8807
|
constructor(params) {
|
|
8111
8808
|
this.params = params;
|
|
8112
8809
|
this.agentRunClient = params.agentRunClient ?? (params.eventBus && params.ingress ? new AgentRunClient({
|
|
@@ -8117,16 +8814,31 @@ var PanelAppManager = class {
|
|
|
8117
8814
|
agentRunClient: this.agentRunClient,
|
|
8118
8815
|
createCapabilityGrantStore: this.createCapabilityGrantStore
|
|
8119
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
|
+
});
|
|
8120
8832
|
}
|
|
8121
8833
|
listPanelApps = async () => {
|
|
8122
8834
|
const workspacePath = this.getWorkspacePath();
|
|
8123
8835
|
const panelsPath = this.getPanelsPath(workspacePath);
|
|
8124
|
-
const sources = await this.
|
|
8836
|
+
const sources = await this.packageStateManager.listSources();
|
|
8125
8837
|
const appState = await this.createStateStore(panelsPath).load();
|
|
8126
8838
|
return {
|
|
8127
8839
|
workspacePath,
|
|
8128
8840
|
panelsPath,
|
|
8129
|
-
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)
|
|
8130
8842
|
};
|
|
8131
8843
|
};
|
|
8132
8844
|
getPanelAppContent = async (id, sourcePath) => {
|
|
@@ -8201,12 +8913,7 @@ var PanelAppManager = class {
|
|
|
8201
8913
|
return session;
|
|
8202
8914
|
};
|
|
8203
8915
|
createPanelAppBridgeSession = async (params) => {
|
|
8204
|
-
const resolved = await
|
|
8205
|
-
appIdOrSourceId: params.id,
|
|
8206
|
-
createAssetBaseHref: this.createAssetBaseHref,
|
|
8207
|
-
panelsPath: this.getPanelsPath(this.getWorkspacePath()),
|
|
8208
|
-
sourceService: this.sourceService
|
|
8209
|
-
});
|
|
8916
|
+
const resolved = await this.packageStateManager.readContentSourceByIdOrAppId(params.id);
|
|
8210
8917
|
return this.createPanelAppRuntimeTokenSession({
|
|
8211
8918
|
appId: resolved.appId,
|
|
8212
8919
|
clientDeclared: resolved.manifest.client,
|
|
@@ -8215,11 +8922,7 @@ var PanelAppManager = class {
|
|
|
8215
8922
|
});
|
|
8216
8923
|
};
|
|
8217
8924
|
grantPanelAppClient = async (appId) => {
|
|
8218
|
-
await
|
|
8219
|
-
appId,
|
|
8220
|
-
panelsPath: this.getPanelsPath(this.getWorkspacePath()),
|
|
8221
|
-
sourceService: this.sourceService
|
|
8222
|
-
});
|
|
8925
|
+
await this.packageStateManager.assertDeclaresClient(appId);
|
|
8223
8926
|
return await this.createClientGrantStore().grant({
|
|
8224
8927
|
appId,
|
|
8225
8928
|
grantedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8253,17 +8956,19 @@ var PanelAppManager = class {
|
|
|
8253
8956
|
const fileName = await this.resolvePanelAppFileName(id);
|
|
8254
8957
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
8255
8958
|
const state = await this.createStateStore(panelsPath).updatePreferences(encodePanelAppId(fileName), preferences);
|
|
8256
|
-
return await this.
|
|
8959
|
+
return await this.entryPresenter.build(await this.packageStateManager.resolveSource(encodePanelAppId(fileName)), state, await this.packageStateManager.findPackageSourceBySourceName(fileName));
|
|
8257
8960
|
};
|
|
8258
8961
|
recordPanelAppOpened = async (id) => {
|
|
8259
8962
|
const fileName = await this.resolvePanelAppFileName(id);
|
|
8260
8963
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
8261
8964
|
const state = await this.createStateStore(panelsPath).recordOpened(encodePanelAppId(fileName));
|
|
8262
|
-
return await this.
|
|
8965
|
+
return await this.entryPresenter.build(await this.packageStateManager.resolveSource(encodePanelAppId(fileName)), state, await this.packageStateManager.findPackageSourceBySourceName(fileName));
|
|
8263
8966
|
};
|
|
8264
8967
|
deletePanelApp = async (id) => {
|
|
8265
8968
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
8266
|
-
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}`);
|
|
8267
8972
|
const panelAppId = encodePanelAppId(source.sourceName);
|
|
8268
8973
|
const appId = resolvePanelAppAppId(source, source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8")));
|
|
8269
8974
|
await rm(source.sourcePath, { recursive: source.kind === "folder" });
|
|
@@ -8293,36 +8998,12 @@ var PanelAppManager = class {
|
|
|
8293
8998
|
createStateStore = (panelsPath) => new PanelAppStateStore(panelsPath);
|
|
8294
8999
|
createCapabilityGrantStore = () => new PanelAppCapabilityGrantStore(join(this.getPanelsPath(this.getWorkspacePath()), PANEL_APP_CAPABILITY_GRANTS_FILE_NAME));
|
|
8295
9000
|
createClientGrantStore = () => new PanelAppClientGrantStore(join(this.getPanelsPath(this.getWorkspacePath()), PANEL_APP_CLIENT_GRANTS_FILE_NAME));
|
|
8296
|
-
buildPanelAppEntry = async (source, state) => {
|
|
8297
|
-
const manifest = source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8"));
|
|
8298
|
-
const id = encodePanelAppId(source.sourceName);
|
|
8299
|
-
const appId = resolvePanelAppAppId(source, manifest);
|
|
8300
|
-
const createdAt = resolvePanelAppCreatedAt(source.sourceStat);
|
|
8301
|
-
const updatedAt = source.sourceStat.mtime.toISOString();
|
|
8302
|
-
const entry = {
|
|
8303
|
-
id,
|
|
8304
|
-
appId,
|
|
8305
|
-
fileName: source.sourceName,
|
|
8306
|
-
kind: source.kind,
|
|
8307
|
-
title: manifest.title ?? toPanelAppTitle(source.sourceName),
|
|
8308
|
-
contentPath: `${PANEL_APP_CONTENT_BASE_PATH}/${encodeURIComponent(id)}/content`,
|
|
8309
|
-
createdAt,
|
|
8310
|
-
updatedAt,
|
|
8311
|
-
sizeBytes: source.sourceStat.size,
|
|
8312
|
-
favorite: state.favorite ?? false,
|
|
8313
|
-
clientDeclared: manifest.client,
|
|
8314
|
-
clientGranted: await this.isPanelAppClientGranted(appId, manifest.client),
|
|
8315
|
-
openCount: state.openCount ?? 0
|
|
8316
|
-
};
|
|
8317
|
-
if (manifest.description) entry.description = manifest.description;
|
|
8318
|
-
if (manifest.icon) entry.icon = source.kind === "folder" ? resolvePanelAppIconUrl(id, manifest.icon) : manifest.icon;
|
|
8319
|
-
if (state.lastOpenedAt) entry.lastOpenedAt = state.lastOpenedAt;
|
|
8320
|
-
return entry;
|
|
8321
|
-
};
|
|
8322
9001
|
resolvePanelAppFileName = async (id) => {
|
|
8323
|
-
return (await this.
|
|
9002
|
+
return (await this.packageStateManager.resolveSource(id)).sourceName;
|
|
8324
9003
|
};
|
|
8325
|
-
|
|
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);
|
|
8326
9007
|
deleteExpiredBridgeSessions = () => {
|
|
8327
9008
|
const now = Date.now();
|
|
8328
9009
|
for (const [token, session] of this.bridgeSessions) if (new Date(session.expiresAt).getTime() <= now) this.bridgeSessions.delete(token);
|
|
@@ -8778,7 +9459,7 @@ var McpServiceAppRuntimeService = class {
|
|
|
8778
9459
|
command: manifest.command,
|
|
8779
9460
|
args: manifest.args,
|
|
8780
9461
|
cwd: app.dirPath,
|
|
8781
|
-
env: createRuntimeChildEnv(process.env),
|
|
9462
|
+
env: createRuntimeChildEnv(process.env, this.createAppRuntimeEnv(app)),
|
|
8782
9463
|
stderr: "pipe"
|
|
8783
9464
|
},
|
|
8784
9465
|
scope: {
|
|
@@ -8791,6 +9472,15 @@ var McpServiceAppRuntimeService = class {
|
|
|
8791
9472
|
}
|
|
8792
9473
|
}
|
|
8793
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
|
+
};
|
|
8794
9484
|
toServiceAction = (manifest, tool) => {
|
|
8795
9485
|
const actionId = buildServiceActionId(manifest.id, tool.toolName);
|
|
8796
9486
|
const manifestAction = manifest.actions[tool.toolName];
|
|
@@ -8885,12 +9575,12 @@ var ServiceActionGrantStore = class {
|
|
|
8885
9575
|
};
|
|
8886
9576
|
};
|
|
8887
9577
|
function normalizeStoreData(value) {
|
|
8888
|
-
if (!isRecord$
|
|
9578
|
+
if (!isRecord$8(value) || value.version !== 1 || !isRecord$8(value.grants)) return structuredClone(EMPTY_GRANTS);
|
|
8889
9579
|
const grants = {};
|
|
8890
9580
|
for (const [callerKey, callerValue] of Object.entries(value.grants)) {
|
|
8891
|
-
if (!isRecord$
|
|
9581
|
+
if (!isRecord$8(callerValue) || !isRecord$8(callerValue.actions)) continue;
|
|
8892
9582
|
const actions = {};
|
|
8893
|
-
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] = {
|
|
8894
9584
|
grantedAt: actionValue.grantedAt,
|
|
8895
9585
|
risk: actionValue.risk
|
|
8896
9586
|
};
|
|
@@ -8904,11 +9594,11 @@ function normalizeStoreData(value) {
|
|
|
8904
9594
|
function isServiceActionRisk(value) {
|
|
8905
9595
|
return value === "read" || value === "write" || value === "external" || value === "dangerous";
|
|
8906
9596
|
}
|
|
8907
|
-
function isRecord$
|
|
9597
|
+
function isRecord$8(value) {
|
|
8908
9598
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8909
9599
|
}
|
|
8910
9600
|
function isMissingFileError(error) {
|
|
8911
|
-
return isRecord$
|
|
9601
|
+
return isRecord$8(error) && error.code === "ENOENT";
|
|
8912
9602
|
}
|
|
8913
9603
|
//#endregion
|
|
8914
9604
|
//#region src/utils/service-app-manifest.utils.ts
|
|
@@ -8933,7 +9623,7 @@ function parseServiceAppManifest(raw) {
|
|
|
8933
9623
|
} catch (error) {
|
|
8934
9624
|
throw new Error(`service-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
8935
9625
|
}
|
|
8936
|
-
if (!isRecord$
|
|
9626
|
+
if (!isRecord$7(parsed)) throw new Error("service-app.json must contain an object.");
|
|
8937
9627
|
const id = readRequiredString$6(parsed, "id");
|
|
8938
9628
|
if (!SERVICE_APP_ID_PATTERN.test(id)) throw new Error("service app id must be kebab-case.");
|
|
8939
9629
|
const protocol = readOptionalString$7(parsed, "protocol") ?? "mcp";
|
|
@@ -8951,16 +9641,16 @@ function parseServiceAppManifest(raw) {
|
|
|
8951
9641
|
}
|
|
8952
9642
|
function readManifestActions(value) {
|
|
8953
9643
|
if (value === void 0) throw new Error("service app actions are required.");
|
|
8954
|
-
if (!isRecord$
|
|
9644
|
+
if (!isRecord$7(value)) throw new Error("service app actions must be an object.");
|
|
8955
9645
|
if (Object.keys(value).length === 0) throw new Error("service app actions cannot be empty.");
|
|
8956
9646
|
const actions = {};
|
|
8957
9647
|
for (const [name, action] of Object.entries(value)) {
|
|
8958
9648
|
if (!name.trim()) throw new Error("service app action name cannot be empty.");
|
|
8959
|
-
if (!isRecord$
|
|
9649
|
+
if (!isRecord$7(action)) throw new Error(`service app action ${name} must be an object.`);
|
|
8960
9650
|
const risk = readOptionalString$7(action, "risk");
|
|
8961
9651
|
if (risk !== void 0 && !SERVICE_ACTION_RISKS.has(risk)) throw new Error(`service app action ${name} has invalid risk.`);
|
|
8962
9652
|
const inputSchema = action.inputSchema;
|
|
8963
|
-
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.`);
|
|
8964
9654
|
actions[name] = {
|
|
8965
9655
|
risk,
|
|
8966
9656
|
title: readOptionalString$7(action, "title"),
|
|
@@ -8988,7 +9678,7 @@ function readStringArray(value, key) {
|
|
|
8988
9678
|
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`service app ${key} must be a string array.`);
|
|
8989
9679
|
return value;
|
|
8990
9680
|
}
|
|
8991
|
-
function isRecord$
|
|
9681
|
+
function isRecord$7(value) {
|
|
8992
9682
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8993
9683
|
}
|
|
8994
9684
|
//#endregion
|
|
@@ -9046,10 +9736,13 @@ var ServiceAppManager = class {
|
|
|
9046
9736
|
const workspacePath = this.getWorkspacePath();
|
|
9047
9737
|
const serviceAppsPath = this.getServiceAppsPath(workspacePath);
|
|
9048
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)));
|
|
9049
9742
|
return {
|
|
9050
9743
|
workspacePath,
|
|
9051
9744
|
serviceAppsPath,
|
|
9052
|
-
entries:
|
|
9745
|
+
entries: [...workspaceEntries, ...packageEntries].filter((entry) => Boolean(entry)).sort((left, right) => left.title.localeCompare(right.title))
|
|
9053
9746
|
};
|
|
9054
9747
|
};
|
|
9055
9748
|
getServiceApp = async (appId) => {
|
|
@@ -9127,6 +9820,7 @@ var ServiceAppManager = class {
|
|
|
9127
9820
|
};
|
|
9128
9821
|
deleteServiceApp = async (appId) => {
|
|
9129
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}`);
|
|
9130
9824
|
await this.runtimeService.restart(record.id);
|
|
9131
9825
|
await rm(record.dirPath, { recursive: true });
|
|
9132
9826
|
await this.createGrantStore().revokeActionsByPrefix(`${record.id}.`);
|
|
@@ -9138,6 +9832,31 @@ var ServiceAppManager = class {
|
|
|
9138
9832
|
dispose = async () => {
|
|
9139
9833
|
await this.runtimeService.dispose();
|
|
9140
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
|
+
};
|
|
9141
9860
|
withGrantState = async (action, params) => {
|
|
9142
9861
|
if (!params.caller) return action;
|
|
9143
9862
|
const granted = await this.createGrantStore().isGranted(params.caller, action.id);
|
|
@@ -9162,14 +9881,23 @@ var ServiceAppManager = class {
|
|
|
9162
9881
|
return await this.requireServiceApp(appId);
|
|
9163
9882
|
};
|
|
9164
9883
|
requireServiceApp = async (appId) => {
|
|
9165
|
-
|
|
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
|
+
}
|
|
9166
9894
|
try {
|
|
9167
9895
|
if (!(await stat(dirPath)).isDirectory()) throw new ServiceAppError("SERVICE_APP_NOT_FOUND", "service app not found");
|
|
9168
9896
|
const manifest = await readServiceAppManifest(dirPath);
|
|
9169
9897
|
if (manifest.id !== appId) throw new ServiceAppError("SERVICE_APP_INVALID_MANIFEST", "service app manifest id must match directory name");
|
|
9170
9898
|
return {
|
|
9171
9899
|
manifest,
|
|
9172
|
-
record: this.toServiceAppRecord(dirPath, manifest)
|
|
9900
|
+
record: this.toServiceAppRecord(dirPath, manifest, packageSource)
|
|
9173
9901
|
};
|
|
9174
9902
|
} catch (error) {
|
|
9175
9903
|
if (isServiceAppError(error)) throw error;
|
|
@@ -9181,7 +9909,7 @@ var ServiceAppManager = class {
|
|
|
9181
9909
|
const workspacePath = this.getWorkspacePath();
|
|
9182
9910
|
const serviceAppsPath = this.getServiceAppsPath(workspacePath);
|
|
9183
9911
|
const dirNames = await this.listServiceAppDirNames(serviceAppsPath);
|
|
9184
|
-
|
|
9912
|
+
const workspaceEntries = await Promise.all(dirNames.map(async (dirName) => {
|
|
9185
9913
|
const dirPath = join(serviceAppsPath, dirName);
|
|
9186
9914
|
try {
|
|
9187
9915
|
const manifest = await readServiceAppManifest(dirPath);
|
|
@@ -9192,7 +9920,19 @@ var ServiceAppManager = class {
|
|
|
9192
9920
|
} catch {
|
|
9193
9921
|
return null;
|
|
9194
9922
|
}
|
|
9195
|
-
}))
|
|
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));
|
|
9196
9936
|
};
|
|
9197
9937
|
buildServiceAppRecord = async (serviceAppsPath, dirName) => {
|
|
9198
9938
|
const dirPath = join(serviceAppsPath, dirName);
|
|
@@ -9214,7 +9954,7 @@ var ServiceAppManager = class {
|
|
|
9214
9954
|
};
|
|
9215
9955
|
}
|
|
9216
9956
|
};
|
|
9217
|
-
toServiceAppRecord = (dirPath, manifest) => {
|
|
9957
|
+
toServiceAppRecord = (dirPath, manifest, packageSource) => {
|
|
9218
9958
|
const runtimeStatus = this.runtimeService.getStatus(manifest.id);
|
|
9219
9959
|
return {
|
|
9220
9960
|
id: manifest.id,
|
|
@@ -9231,7 +9971,12 @@ var ServiceAppManager = class {
|
|
|
9231
9971
|
lastError: runtimeStatus.lastError,
|
|
9232
9972
|
lastStartedAt: runtimeStatus.lastStartedAt,
|
|
9233
9973
|
lastReadyAt: runtimeStatus.lastReadyAt,
|
|
9234
|
-
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
|
|
9235
9980
|
};
|
|
9236
9981
|
};
|
|
9237
9982
|
assertCaller = (caller) => {
|
|
@@ -9244,6 +9989,31 @@ var ServiceAppManager = class {
|
|
|
9244
9989
|
getWorkspacePath = () => getWorkspacePathFromConfig(this.params.configManager.config);
|
|
9245
9990
|
getServiceAppsPath = (workspacePath) => join(workspacePath, DEFAULT_SERVICE_APPS_DIR);
|
|
9246
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
|
+
};
|
|
9247
10017
|
listServiceAppDirNames = async (serviceAppsPath) => {
|
|
9248
10018
|
try {
|
|
9249
10019
|
return (await readdir(serviceAppsPath, { withFileTypes: true })).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
|
|
@@ -9457,7 +10227,7 @@ function parseFrontmatterBlock(raw) {
|
|
|
9457
10227
|
function parseYamlFrontmatter(raw) {
|
|
9458
10228
|
try {
|
|
9459
10229
|
const parsed = parse(raw);
|
|
9460
|
-
return isRecord$
|
|
10230
|
+
return isRecord$6(parsed) ? parsed : {};
|
|
9461
10231
|
} catch (error) {
|
|
9462
10232
|
const message = error instanceof Error ? error.message : String(error);
|
|
9463
10233
|
throw new Error(`Invalid SKILL.md frontmatter: ${message}`);
|
|
@@ -9469,7 +10239,7 @@ function readString$5(record, ...names) {
|
|
|
9469
10239
|
}
|
|
9470
10240
|
function readLocalizedTextMap(record, ...names) {
|
|
9471
10241
|
const value = readValue(record, names);
|
|
9472
|
-
if (!isRecord$
|
|
10242
|
+
if (!isRecord$6(value)) return;
|
|
9473
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()]));
|
|
9474
10244
|
return Object.keys(localized).length > 0 ? localized : void 0;
|
|
9475
10245
|
}
|
|
@@ -9493,7 +10263,7 @@ function normalizeFrontmatterKey(raw) {
|
|
|
9493
10263
|
function normalizeLocaleTag(raw) {
|
|
9494
10264
|
return raw.trim().toLowerCase();
|
|
9495
10265
|
}
|
|
9496
|
-
function isRecord$
|
|
10266
|
+
function isRecord$6(value) {
|
|
9497
10267
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9498
10268
|
}
|
|
9499
10269
|
//#endregion
|
|
@@ -9681,7 +10451,7 @@ var NcpAgentUnfinishedRunStore = class {
|
|
|
9681
10451
|
} catch {
|
|
9682
10452
|
continue;
|
|
9683
10453
|
}
|
|
9684
|
-
if (!isRecord$
|
|
10454
|
+
if (!isRecord$13(parsed) || parsed._type !== "event" || !isRecord$13(parsed.event)) continue;
|
|
9685
10455
|
activeRun = applyNcpAgentRunLifecycleEvent(sessionId, activeRun, parsed.event);
|
|
9686
10456
|
}
|
|
9687
10457
|
return activeRun;
|
|
@@ -9702,11 +10472,11 @@ function isNcpAgentSessionMessageProjectionBoundaryEvent(event) {
|
|
|
9702
10472
|
function serializeNcpAgentSessionJournalEntry(entry) {
|
|
9703
10473
|
const serialized = JSON.stringify(entry);
|
|
9704
10474
|
if (!serialized) throw new Error("ncp agent session journal entry serialization produced empty output");
|
|
9705
|
-
if (!isRecord$
|
|
10475
|
+
if (!isRecord$13(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
|
|
9706
10476
|
return serialized;
|
|
9707
10477
|
}
|
|
9708
10478
|
function attachNcpAgentSessionJournalTimestamp(event, timestamp) {
|
|
9709
|
-
if (!("payload" in event) || !isRecord$
|
|
10479
|
+
if (!("payload" in event) || !isRecord$13(event.payload)) return event;
|
|
9710
10480
|
return {
|
|
9711
10481
|
...event,
|
|
9712
10482
|
payload: {
|
|
@@ -9731,7 +10501,7 @@ var NcpAgentSessionJournalParser = class {
|
|
|
9731
10501
|
this.applyMetadata(parsed);
|
|
9732
10502
|
return;
|
|
9733
10503
|
}
|
|
9734
|
-
if (parsed._type === "event" && isRecord$
|
|
10504
|
+
if (parsed._type === "event" && isRecord$13(parsed.event)) this.applyEvent(parsed, lineEndOffset);
|
|
9735
10505
|
};
|
|
9736
10506
|
finish = () => ({
|
|
9737
10507
|
metadata: this.metadata,
|
|
@@ -9745,14 +10515,14 @@ var NcpAgentSessionJournalParser = class {
|
|
|
9745
10515
|
parseLine = (line, index) => {
|
|
9746
10516
|
try {
|
|
9747
10517
|
const parsed = JSON.parse(line);
|
|
9748
|
-
return isRecord$
|
|
10518
|
+
return isRecord$13(parsed) ? parsed : null;
|
|
9749
10519
|
} catch (error) {
|
|
9750
10520
|
console.warn(`[ncp-agent-session-journal] skipped corrupted journal line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
|
|
9751
10521
|
return null;
|
|
9752
10522
|
}
|
|
9753
10523
|
};
|
|
9754
10524
|
applyMetadata = (entry) => {
|
|
9755
|
-
this.metadata = isRecord$
|
|
10525
|
+
this.metadata = isRecord$13(entry.metadata) ? structuredClone(entry.metadata) : {};
|
|
9756
10526
|
this.agentId = normalizeNcpAgentId(typeof entry.agent_id === "string" ? entry.agent_id : void 0);
|
|
9757
10527
|
this.createdAt = toIsoString(entry.created_at, this.createdAt);
|
|
9758
10528
|
this.updatedAt = toIsoString(entry.updated_at, this.updatedAt);
|
|
@@ -9799,7 +10569,7 @@ var NcpAgentSessionMetadataStore = class {
|
|
|
9799
10569
|
read = async (sessionId, activitySnapshot) => {
|
|
9800
10570
|
try {
|
|
9801
10571
|
const parsed = JSON.parse(await readFile(this.metadataPath(sessionId), "utf-8"));
|
|
9802
|
-
if (!isRecord$
|
|
10572
|
+
if (!isRecord$13(parsed) || parsed._type !== "metadata" || !isRecord$13(parsed.metadata)) throw new Error(`invalid ncp agent session metadata sidecar: ${sessionId}`);
|
|
9803
10573
|
const createdAt = toIsoString(parsed.created_at, activitySnapshot.createdAt);
|
|
9804
10574
|
const agentId = normalizeNcpAgentId(typeof parsed.agent_id === "string" ? parsed.agent_id : void 0);
|
|
9805
10575
|
return {
|
|
@@ -9869,7 +10639,7 @@ function deduplicateNcpAgentSessionTailMessages(messages) {
|
|
|
9869
10639
|
function isNcpAgentSessionMessageProjectionMeta(value, sessionId) {
|
|
9870
10640
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
9871
10641
|
const meta = value;
|
|
9872
|
-
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));
|
|
9873
10643
|
}
|
|
9874
10644
|
function readActiveAssistantMessageId(messages) {
|
|
9875
10645
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
@@ -9892,12 +10662,12 @@ function mergePendingCompactionMessageIds(current, messages) {
|
|
|
9892
10662
|
}
|
|
9893
10663
|
return pending;
|
|
9894
10664
|
}
|
|
9895
|
-
function isRecord$
|
|
10665
|
+
function isRecord$5(value) {
|
|
9896
10666
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9897
10667
|
}
|
|
9898
10668
|
function readCompactionStatus(message) {
|
|
9899
10669
|
const checkpoint = message.metadata?.checkpoint;
|
|
9900
|
-
if (message.metadata?.nextclaw_timeline_kind !== "context_compaction" || !isRecord$
|
|
10670
|
+
if (message.metadata?.nextclaw_timeline_kind !== "context_compaction" || !isRecord$5(checkpoint)) return null;
|
|
9901
10671
|
return typeof checkpoint.status === "string" ? checkpoint.status : null;
|
|
9902
10672
|
}
|
|
9903
10673
|
//#endregion
|
|
@@ -11914,7 +12684,7 @@ var CurrentSessionContextProvider = class {
|
|
|
11914
12684
|
const MAX_EXCERPT_COUNT = 16;
|
|
11915
12685
|
const MAX_EXCERPT_CHARACTERS$1 = 8e3;
|
|
11916
12686
|
const MAX_TOTAL_CONTEXT_CHARACTERS$1 = 96e3;
|
|
11917
|
-
function isRecord$
|
|
12687
|
+
function isRecord$4(value) {
|
|
11918
12688
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
11919
12689
|
}
|
|
11920
12690
|
function readString$3(value) {
|
|
@@ -11925,11 +12695,11 @@ function escapeAttribute$1(value) {
|
|
|
11925
12695
|
}
|
|
11926
12696
|
function readConversationExcerpts(metadata) {
|
|
11927
12697
|
const raw = metadata?.[CHAT_INLINE_TOKENS_METADATA_KEY];
|
|
11928
|
-
const entries = isRecord$
|
|
12698
|
+
const entries = isRecord$4(raw) && raw.schemaVersion === CHAT_INLINE_TOKENS_SCHEMA_VERSION && Array.isArray(raw.items) ? raw.items : [];
|
|
11929
12699
|
const excerpts = [];
|
|
11930
12700
|
const seen = /* @__PURE__ */ new Set();
|
|
11931
12701
|
for (const entry of entries) {
|
|
11932
|
-
if (!isRecord$
|
|
12702
|
+
if (!isRecord$4(entry) || entry.kind !== CHAT_CONVERSATION_EXCERPT_TOKEN_KIND) continue;
|
|
11933
12703
|
const key = readString$3(entry.key);
|
|
11934
12704
|
const messageId = readString$3(entry.messageId);
|
|
11935
12705
|
const label = readString$3(entry.label);
|
|
@@ -11974,27 +12744,63 @@ var ConversationExcerptContextProvider = class {
|
|
|
11974
12744
|
};
|
|
11975
12745
|
};
|
|
11976
12746
|
//#endregion
|
|
11977
|
-
//#region src/contributions/context-provider/providers/
|
|
11978
|
-
|
|
11979
|
-
|
|
11980
|
-
|
|
11981
|
-
|
|
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;
|
|
11982
12768
|
}
|
|
11983
12769
|
provide = async (request) => {
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
|
|
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")];
|
|
11998
12804
|
};
|
|
11999
12805
|
};
|
|
12000
12806
|
//#endregion
|
|
@@ -12851,7 +13657,7 @@ var ContextProviderContribution = class {
|
|
|
12851
13657
|
new SkillsContextProvider(context),
|
|
12852
13658
|
createSessionOrchestrationContextProvider(),
|
|
12853
13659
|
new ExecutionPolicyContextProvider(context),
|
|
12854
|
-
new
|
|
13660
|
+
new SystemObjectReferenceContextProvider(this.kernel.assetStore),
|
|
12855
13661
|
new CurrentSessionContextProvider(context),
|
|
12856
13662
|
new ReplyFormatContextProvider()
|
|
12857
13663
|
]) this.cleanups.push(this.kernel.contextProviderManager.register(provider));
|
|
@@ -14207,6 +15013,10 @@ var ToolProviderContribution = class {
|
|
|
14207
15013
|
};
|
|
14208
15014
|
//#endregion
|
|
14209
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
|
+
}
|
|
14210
15020
|
function resolveKernelSessionsDir(options) {
|
|
14211
15021
|
const homeDir = options.homeDir?.trim();
|
|
14212
15022
|
if (homeDir) return ensureDir(resolve(expandHome(homeDir), "sessions"));
|
|
@@ -14255,6 +15065,7 @@ var NextclawKernel = class {
|
|
|
14255
15065
|
control;
|
|
14256
15066
|
skills;
|
|
14257
15067
|
automation;
|
|
15068
|
+
appPackageManager;
|
|
14258
15069
|
channels;
|
|
14259
15070
|
sessionRequests;
|
|
14260
15071
|
sessionSearch;
|
|
@@ -14262,6 +15073,7 @@ var NextclawKernel = class {
|
|
|
14262
15073
|
mcpManager;
|
|
14263
15074
|
sessionManager;
|
|
14264
15075
|
inboxDeliveryManager;
|
|
15076
|
+
systemObjectReferenceManager;
|
|
14265
15077
|
panelAppManager;
|
|
14266
15078
|
preferenceManager;
|
|
14267
15079
|
projectManager;
|
|
@@ -14316,16 +15128,26 @@ var NextclawKernel = class {
|
|
|
14316
15128
|
});
|
|
14317
15129
|
this.inboxDeliveryManager = new InboxDeliveryManager({
|
|
14318
15130
|
eventBus: this.eventBus,
|
|
14319
|
-
sessionManager: this.sessionManager,
|
|
14320
15131
|
storePath: resolveKernelInboxDeliveryStorePath(options)
|
|
14321
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
|
+
});
|
|
14322
15139
|
this.panelAppManager = new PanelAppManager({
|
|
14323
15140
|
configManager: this.configManager,
|
|
14324
15141
|
eventBus: this.eventBus,
|
|
14325
|
-
ingress: this.ingress
|
|
15142
|
+
ingress: this.ingress,
|
|
15143
|
+
listPackageComponentSources: this.appPackageManager.listActiveComponentSources
|
|
14326
15144
|
});
|
|
14327
15145
|
this.preferenceManager = new PreferenceManager({ storePath: resolveKernelPreferenceStorePath(options) });
|
|
14328
|
-
this.serviceAppManager = new ServiceAppManager({
|
|
15146
|
+
this.serviceAppManager = new ServiceAppManager({
|
|
15147
|
+
configManager: this.configManager,
|
|
15148
|
+
listPackageComponentSources: this.appPackageManager.listActiveComponentSources
|
|
15149
|
+
});
|
|
15150
|
+
this.installAppPackageRuntimeHooks();
|
|
14329
15151
|
this.extensions = new ExtensionManager({
|
|
14330
15152
|
configManager: this.configManager,
|
|
14331
15153
|
eventBus: this.eventBus,
|
|
@@ -14365,6 +15187,22 @@ var NextclawKernel = class {
|
|
|
14365
15187
|
new ContextWindowContribution(this)
|
|
14366
15188
|
];
|
|
14367
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
|
+
};
|
|
14368
15206
|
listSessionTypes = (params) => this.agentRuntimeManager.listSessionTypes(params);
|
|
14369
15207
|
isSessionRunning = (sessionId) => this.sessionRunManager.isSessionRunning(sessionId);
|
|
14370
15208
|
provideGatewayController = (gatewayController) => {
|
|
@@ -14965,6 +15803,6 @@ function resolveLegacyEventType(message) {
|
|
|
14965
15803
|
return `message.${role || "other"}`;
|
|
14966
15804
|
}
|
|
14967
15805
|
//#endregion
|
|
14968
|
-
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 };
|
|
14969
15807
|
|
|
14970
15808
|
//# sourceMappingURL=index.js.map
|