@nextclaw/kernel 0.1.15-beta.7 → 0.1.16
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 +21 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +300 -114
- package/dist/index.js.map +1 -1
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { catchError, from, lastValueFrom, tap } from "rxjs";
|
|
|
9
9
|
import { appendFileSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
11
11
|
import { access, appendFile, mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
12
|
-
import { basename, delimiter, dirname, join, relative, resolve } from "node:path";
|
|
12
|
+
import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve } from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
import { BUILTIN_PROVIDER_PLUGINS } from "@nextclaw/runtime";
|
|
15
15
|
import { McpRegistryService, McpServerLifecycleManager } from "@nextclaw/mcp";
|
|
@@ -572,7 +572,7 @@ var AgentRunContextCompactionManager = class {
|
|
|
572
572
|
//#region src/managers/agent-run-request.manager.ts
|
|
573
573
|
function toAgentRunRequest(envelope) {
|
|
574
574
|
const metadata = envelope.metadata ?? {};
|
|
575
|
-
const peerId = readOptionalString$
|
|
575
|
+
const peerId = readOptionalString$9(envelope.peerId);
|
|
576
576
|
const requestMetadata = {
|
|
577
577
|
agentRuntimeId: metadata.agentRuntimeId,
|
|
578
578
|
agentId: metadata.agentId,
|
|
@@ -585,7 +585,7 @@ function toAgentRunRequest(envelope) {
|
|
|
585
585
|
thinkingEffort: metadata.thinkingEffort
|
|
586
586
|
};
|
|
587
587
|
if (Array.isArray(envelope.content)) {
|
|
588
|
-
const sessionId = readOptionalString$
|
|
588
|
+
const sessionId = readOptionalString$9(envelope.sessionId);
|
|
589
589
|
if (sessionId && peerId) throw new Error("agent-run.send cannot accept both sessionId and peerId.");
|
|
590
590
|
return {
|
|
591
591
|
...requestMetadata,
|
|
@@ -603,8 +603,8 @@ function toAgentRunRequest(envelope) {
|
|
|
603
603
|
}
|
|
604
604
|
const sourceMessage = envelope.message;
|
|
605
605
|
if (!sourceMessage) throw new Error("Invalid agent run send request.");
|
|
606
|
-
const envelopeSessionId = readOptionalString$
|
|
607
|
-
const messageSessionId = readOptionalString$
|
|
606
|
+
const envelopeSessionId = readOptionalString$9(envelope.sessionId);
|
|
607
|
+
const messageSessionId = readOptionalString$9(sourceMessage.sessionId);
|
|
608
608
|
const sessionId = envelopeSessionId ?? messageSessionId;
|
|
609
609
|
if (sessionId && peerId) throw new Error("agent-run.send cannot accept both sessionId and peerId.");
|
|
610
610
|
const message = {
|
|
@@ -623,7 +623,7 @@ function toAgentRunRequest(envelope) {
|
|
|
623
623
|
message
|
|
624
624
|
};
|
|
625
625
|
}
|
|
626
|
-
function readOptionalString$
|
|
626
|
+
function readOptionalString$9(value) {
|
|
627
627
|
if (typeof value !== "string") return;
|
|
628
628
|
return value.trim() || void 0;
|
|
629
629
|
}
|
|
@@ -1593,7 +1593,7 @@ const runtimeRequire = createRequire(import.meta.url);
|
|
|
1593
1593
|
function readString$6(value) {
|
|
1594
1594
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1595
1595
|
}
|
|
1596
|
-
function readStringArray$
|
|
1596
|
+
function readStringArray$2(value) {
|
|
1597
1597
|
return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
|
|
1598
1598
|
}
|
|
1599
1599
|
function readStringRecord(value) {
|
|
@@ -1652,7 +1652,7 @@ function toManifest(value, rootDir) {
|
|
|
1652
1652
|
server: {
|
|
1653
1653
|
type: "stdio",
|
|
1654
1654
|
command,
|
|
1655
|
-
...readStringArray$
|
|
1655
|
+
...readStringArray$2(server.args) ? { args: readStringArray$2(server.args) } : {},
|
|
1656
1656
|
...readStringRecord(server.env) ? { env: readStringRecord(server.env) } : {}
|
|
1657
1657
|
},
|
|
1658
1658
|
...record.contributes && typeof record.contributes === "object" && !Array.isArray(record.contributes) ? { contributes: record.contributes } : {}
|
|
@@ -1753,7 +1753,7 @@ function readString$5(value) {
|
|
|
1753
1753
|
function readRecord$2(value) {
|
|
1754
1754
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1755
1755
|
}
|
|
1756
|
-
function readRequiredString$
|
|
1756
|
+
function readRequiredString$4(value, name) {
|
|
1757
1757
|
const trimmed = readString$5(value);
|
|
1758
1758
|
if (!trimmed) throw new Error(`${name} is required`);
|
|
1759
1759
|
return trimmed;
|
|
@@ -1784,9 +1784,9 @@ function readInboundAttachments(value) {
|
|
|
1784
1784
|
function toInboundMessage(value) {
|
|
1785
1785
|
const payload = readRecord$2(value);
|
|
1786
1786
|
return {
|
|
1787
|
-
channel: readRequiredString$
|
|
1788
|
-
chatId: readRequiredString$
|
|
1789
|
-
senderId: readRequiredString$
|
|
1787
|
+
channel: readRequiredString$4(payload.channelId, "channelId"),
|
|
1788
|
+
chatId: readRequiredString$4(payload.conversationId, "conversationId"),
|
|
1789
|
+
senderId: readRequiredString$4(payload.senderId, "senderId"),
|
|
1790
1790
|
content: readTextContent(payload.content),
|
|
1791
1791
|
timestamp: /* @__PURE__ */ new Date(),
|
|
1792
1792
|
attachments: readInboundAttachments(payload.attachments),
|
|
@@ -1808,7 +1808,7 @@ function normalizeAuthPollResult(value) {
|
|
|
1808
1808
|
if (!value) return null;
|
|
1809
1809
|
const record = readRecord$2(value);
|
|
1810
1810
|
return {
|
|
1811
|
-
channel: readRequiredString$
|
|
1811
|
+
channel: readRequiredString$4(record.channel, "channel"),
|
|
1812
1812
|
status: record.status,
|
|
1813
1813
|
message: readString$5(record.message),
|
|
1814
1814
|
nextPollMs: readOptionalNumber(record.nextPollMs),
|
|
@@ -1955,7 +1955,7 @@ var ExtensionRuntimeService = class {
|
|
|
1955
1955
|
};
|
|
1956
1956
|
handleChannelConfigGet = (envelope, context) => {
|
|
1957
1957
|
this.assertAuthorized(context);
|
|
1958
|
-
const channelId = readRequiredString$
|
|
1958
|
+
const channelId = readRequiredString$4(readRecord$2(envelope.payload).channelId, "channelId");
|
|
1959
1959
|
return { config: this.options.getConfig().channels[channelId] ?? {} };
|
|
1960
1960
|
};
|
|
1961
1961
|
handleChannelMessageSubmit = async (envelope, context) => {
|
|
@@ -1970,9 +1970,9 @@ var ExtensionRuntimeService = class {
|
|
|
1970
1970
|
handleChannelCommandExecute = async (envelope, context) => {
|
|
1971
1971
|
this.assertAuthorized(context);
|
|
1972
1972
|
const payload = readRecord$2(envelope.payload);
|
|
1973
|
-
const channel = readRequiredString$
|
|
1974
|
-
const chatId = readRequiredString$
|
|
1975
|
-
const senderId = readRequiredString$
|
|
1973
|
+
const channel = readRequiredString$4(payload.channelId, "channelId");
|
|
1974
|
+
const chatId = readRequiredString$4(payload.conversationId, "conversationId");
|
|
1975
|
+
const senderId = readRequiredString$4(payload.senderId, "senderId");
|
|
1976
1976
|
const metadata = readRecord$2(payload.metadata);
|
|
1977
1977
|
const config = this.options.getConfig();
|
|
1978
1978
|
const registry = new CommandRegistry(config, this.options.sessionManager);
|
|
@@ -1999,7 +1999,7 @@ var ExtensionRuntimeService = class {
|
|
|
1999
1999
|
content: "",
|
|
2000
2000
|
ephemeral: true
|
|
2001
2001
|
};
|
|
2002
|
-
return await registry.execute(readRequiredString$
|
|
2002
|
+
return await registry.execute(readRequiredString$4(payload.commandName, "commandName"), readRecord$2(payload.args), {
|
|
2003
2003
|
channel,
|
|
2004
2004
|
chatId,
|
|
2005
2005
|
senderId,
|
|
@@ -2009,7 +2009,7 @@ var ExtensionRuntimeService = class {
|
|
|
2009
2009
|
handleExtensionResponse = (envelope, context) => {
|
|
2010
2010
|
this.assertAuthorized(context);
|
|
2011
2011
|
const payload = readRecord$2(envelope.payload);
|
|
2012
|
-
const requestId = readRequiredString$
|
|
2012
|
+
const requestId = readRequiredString$4(payload.requestId, "requestId");
|
|
2013
2013
|
const pending = this.pendingRequests.get(requestId);
|
|
2014
2014
|
if (!pending) return { accepted: false };
|
|
2015
2015
|
this.pendingRequests.delete(requestId);
|
|
@@ -2183,11 +2183,11 @@ var ExtensionManager = class {
|
|
|
2183
2183
|
//#endregion
|
|
2184
2184
|
//#region src/utils/model-message-vision.utils.ts
|
|
2185
2185
|
const IMAGE_OMITTED_TEXT = "[Image omitted: the selected model is not configured for vision input.]";
|
|
2186
|
-
function isRecord$
|
|
2186
|
+
function isRecord$10(value) {
|
|
2187
2187
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2188
2188
|
}
|
|
2189
2189
|
function isImageContentPart(value) {
|
|
2190
|
-
if (!isRecord$
|
|
2190
|
+
if (!isRecord$10(value)) return false;
|
|
2191
2191
|
const type = value.type;
|
|
2192
2192
|
return type === "image_url" || type === "input_image";
|
|
2193
2193
|
}
|
|
@@ -2203,7 +2203,7 @@ function normalizeContentWithoutVision(content) {
|
|
|
2203
2203
|
};
|
|
2204
2204
|
});
|
|
2205
2205
|
if (!sawImage) return content;
|
|
2206
|
-
const textParts = parts.filter((part) => isRecord$
|
|
2206
|
+
const textParts = parts.filter((part) => isRecord$10(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text.trim()).filter(Boolean);
|
|
2207
2207
|
if (textParts.length === parts.length) return textParts.join("\n\n");
|
|
2208
2208
|
return parts;
|
|
2209
2209
|
}
|
|
@@ -2698,7 +2698,7 @@ function safeNcpSessionFilename(value) {
|
|
|
2698
2698
|
function normalizeNcpAgentId(agentId) {
|
|
2699
2699
|
return agentId?.trim().toLowerCase() || void 0;
|
|
2700
2700
|
}
|
|
2701
|
-
function isRecord$
|
|
2701
|
+
function isRecord$9(value) {
|
|
2702
2702
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2703
2703
|
}
|
|
2704
2704
|
function toIsoString(value, fallback) {
|
|
@@ -2809,11 +2809,11 @@ function createAgentPeerSessionIdentity(params) {
|
|
|
2809
2809
|
}
|
|
2810
2810
|
function resolveAgentPeerScope(params) {
|
|
2811
2811
|
const metadata = params.metadata ?? {};
|
|
2812
|
-
const explicitScope = readOptionalString$
|
|
2812
|
+
const explicitScope = readOptionalString$8(metadata["agent_peer_scope"]) ?? readOptionalString$8(metadata.agentPeerScope);
|
|
2813
2813
|
if (explicitScope) return explicitScope;
|
|
2814
|
-
return `agent:${readOptionalString$
|
|
2814
|
+
return `agent:${readOptionalString$8(params.agentId) ?? BUILTIN_MAIN_AGENT_ID}:${readOptionalString$8(params.channel) ?? readOptionalString$8(metadata.channel) ?? "agent-run"}:${readOptionalString$8(metadata.accountId) ?? readOptionalString$8(metadata.account_id) ?? "default"}`;
|
|
2815
2815
|
}
|
|
2816
|
-
function readOptionalString$
|
|
2816
|
+
function readOptionalString$8(value) {
|
|
2817
2817
|
if (typeof value !== "string") return;
|
|
2818
2818
|
return value.trim() || void 0;
|
|
2819
2819
|
}
|
|
@@ -2832,7 +2832,7 @@ function applyLimit(items, limit) {
|
|
|
2832
2832
|
if (!Number.isFinite(limit) || typeof limit !== "number" || limit <= 0) return items;
|
|
2833
2833
|
return items.slice(0, Math.trunc(limit));
|
|
2834
2834
|
}
|
|
2835
|
-
function readOptionalString$
|
|
2835
|
+
function readOptionalString$7(value) {
|
|
2836
2836
|
if (typeof value !== "string") return null;
|
|
2837
2837
|
const trimmed = value.trim();
|
|
2838
2838
|
return trimmed.length > 0 ? trimmed : null;
|
|
@@ -2883,7 +2883,7 @@ function mergeMetadataOverrides(metadata, overrides) {
|
|
|
2883
2883
|
}
|
|
2884
2884
|
function resolveSessionType(params) {
|
|
2885
2885
|
const { metadata, runtime, sessionType } = params;
|
|
2886
|
-
return readOptionalString$
|
|
2886
|
+
return readOptionalString$7(runtime) ?? readOptionalString$7(metadata.runtime) ?? readOptionalString$7(sessionType) ?? readOptionalString$7(metadata.session_type) ?? DEFAULT_SESSION_TYPE;
|
|
2887
2887
|
}
|
|
2888
2888
|
function applySessionOverrides(params) {
|
|
2889
2889
|
const { lifecycle, metadata, model, parentSessionId, projectRoot, requestId, sessionType, thinkingLevel, title } = params;
|
|
@@ -2893,15 +2893,15 @@ function applySessionOverrides(params) {
|
|
|
2893
2893
|
metadata[CHILD_SESSION_LIFECYCLE_METADATA_KEY] = lifecycle;
|
|
2894
2894
|
if (parentSessionId) metadata[CHILD_SESSION_PARENT_METADATA_KEY] = parentSessionId;
|
|
2895
2895
|
if (requestId) metadata[CHILD_SESSION_REQUEST_METADATA_KEY] = requestId;
|
|
2896
|
-
if (readOptionalString$
|
|
2896
|
+
if (readOptionalString$7(model)) {
|
|
2897
2897
|
metadata.model = model?.trim();
|
|
2898
2898
|
metadata.preferred_model = model?.trim();
|
|
2899
2899
|
}
|
|
2900
|
-
if (readOptionalString$
|
|
2900
|
+
if (readOptionalString$7(thinkingLevel)) {
|
|
2901
2901
|
metadata.thinking = thinkingLevel?.trim();
|
|
2902
2902
|
metadata.preferred_thinking = thinkingLevel?.trim();
|
|
2903
2903
|
}
|
|
2904
|
-
if (readOptionalString$
|
|
2904
|
+
if (readOptionalString$7(projectRoot)) metadata.project_root = projectRoot?.trim();
|
|
2905
2905
|
}
|
|
2906
2906
|
function isSessionSummaryRefreshEvent(event) {
|
|
2907
2907
|
switch (event.type) {
|
|
@@ -2941,9 +2941,9 @@ var SessionManager = class {
|
|
|
2941
2941
|
const { agentId: requestedAgentId, metadataOverrides, model, parentSessionId: rawParentSessionId, projectRoot, requestId: rawRequestId, runtime, sessionId: requestedSessionId, sessionType: requestedSessionType, sourceSessionId, sourceSessionMetadata, task, thinkingLevel, title: requestedTitle } = params;
|
|
2942
2942
|
const sourceRecord = sourceSessionId ? await this.getSessionRecord(sourceSessionId) : null;
|
|
2943
2943
|
const metadata = cloneInheritedMetadata(sourceSessionMetadata);
|
|
2944
|
-
const title = readOptionalString$
|
|
2945
|
-
const parentSessionId = readOptionalString$
|
|
2946
|
-
const requestId = readOptionalString$
|
|
2944
|
+
const title = readOptionalString$7(requestedTitle) ?? summarizeTask(task);
|
|
2945
|
+
const parentSessionId = readOptionalString$7(rawParentSessionId);
|
|
2946
|
+
const requestId = readOptionalString$7(rawRequestId);
|
|
2947
2947
|
const sessionType = resolveSessionType({
|
|
2948
2948
|
runtime,
|
|
2949
2949
|
sessionType: requestedSessionType,
|
|
@@ -2962,8 +2962,8 @@ var SessionManager = class {
|
|
|
2962
2962
|
});
|
|
2963
2963
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2964
2964
|
const nextMetadata = mergeMetadataOverrides(metadata, metadataOverrides);
|
|
2965
|
-
const agentId = readOptionalString$
|
|
2966
|
-
const sessionId = readOptionalString$
|
|
2965
|
+
const agentId = readOptionalString$7(requestedAgentId) ?? readOptionalString$7(sourceRecord?.agentId) ?? BUILTIN_MAIN_AGENT_ID;
|
|
2966
|
+
const sessionId = readOptionalString$7(requestedSessionId) ?? buildSessionId();
|
|
2967
2967
|
const record = {
|
|
2968
2968
|
sessionId,
|
|
2969
2969
|
...agentId ? { agentId } : {},
|
|
@@ -3068,14 +3068,14 @@ var SessionManager = class {
|
|
|
3068
3068
|
createAgentRunSession = async (params) => {
|
|
3069
3069
|
const { agentId, agentRuntimeId: requestedAgentRuntimeId, channel, metadata, model, peerId: rawPeerId, projectRoot, sessionId, task, thinkingEffort } = params;
|
|
3070
3070
|
const agentRuntimeId = requestedAgentRuntimeId ?? "native";
|
|
3071
|
-
const peerId = readOptionalString$
|
|
3071
|
+
const peerId = readOptionalString$7(rawPeerId);
|
|
3072
3072
|
const peerIdentity = peerId ? createAgentPeerSessionIdentity({
|
|
3073
3073
|
agentId,
|
|
3074
3074
|
channel,
|
|
3075
3075
|
metadata,
|
|
3076
3076
|
peerId
|
|
3077
3077
|
}) : void 0;
|
|
3078
|
-
const requestedSessionId = readOptionalString$
|
|
3078
|
+
const requestedSessionId = readOptionalString$7(sessionId);
|
|
3079
3079
|
const created = await this.createSession({
|
|
3080
3080
|
sourceSessionMetadata: {},
|
|
3081
3081
|
sessionId: requestedSessionId ?? peerIdentity?.sessionId,
|
|
@@ -3291,7 +3291,7 @@ var PanelAppCapabilityGrantStore = class {
|
|
|
3291
3291
|
try {
|
|
3292
3292
|
return normalizeStoreData$1(JSON.parse(await readFile(this.filePath, "utf8")));
|
|
3293
3293
|
} catch (error) {
|
|
3294
|
-
if (isMissingFileError$
|
|
3294
|
+
if (isMissingFileError$2(error)) return structuredClone(EMPTY_GRANTS$1);
|
|
3295
3295
|
throw error;
|
|
3296
3296
|
}
|
|
3297
3297
|
};
|
|
@@ -3301,12 +3301,12 @@ var PanelAppCapabilityGrantStore = class {
|
|
|
3301
3301
|
};
|
|
3302
3302
|
};
|
|
3303
3303
|
function normalizeStoreData$1(value) {
|
|
3304
|
-
if (!isRecord$
|
|
3304
|
+
if (!isRecord$8(value) || value.version !== 1 || !isRecord$8(value.grants)) return structuredClone(EMPTY_GRANTS$1);
|
|
3305
3305
|
const grants = {};
|
|
3306
3306
|
for (const [callerKey, callerValue] of Object.entries(value.grants)) {
|
|
3307
|
-
if (!isRecord$
|
|
3307
|
+
if (!isRecord$8(callerValue) || !isRecord$8(callerValue.capabilities)) continue;
|
|
3308
3308
|
const capabilities = {};
|
|
3309
|
-
for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$
|
|
3309
|
+
for (const [capability, grantValue] of Object.entries(callerValue.capabilities)) if (isRecord$8(grantValue) && typeof grantValue.grantedAt === "string") capabilities[capability] = { grantedAt: grantValue.grantedAt };
|
|
3310
3310
|
grants[callerKey] = { capabilities };
|
|
3311
3311
|
}
|
|
3312
3312
|
return {
|
|
@@ -3317,10 +3317,10 @@ function normalizeStoreData$1(value) {
|
|
|
3317
3317
|
function getCallerKey(caller) {
|
|
3318
3318
|
return `${caller.surface}:${caller.appId}`;
|
|
3319
3319
|
}
|
|
3320
|
-
function isRecord$
|
|
3320
|
+
function isRecord$8(value) {
|
|
3321
3321
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3322
3322
|
}
|
|
3323
|
-
function isMissingFileError$
|
|
3323
|
+
function isMissingFileError$2(error) {
|
|
3324
3324
|
return Boolean(error) && typeof error === "object" && error.code === "ENOENT";
|
|
3325
3325
|
}
|
|
3326
3326
|
//#endregion
|
|
@@ -3335,8 +3335,9 @@ var PanelAppError = class extends Error {
|
|
|
3335
3335
|
function isPanelAppError(error) {
|
|
3336
3336
|
return error instanceof PanelAppError;
|
|
3337
3337
|
}
|
|
3338
|
+
const PANEL_APP_AGENT_CAPABILITIES = ["agent:send", "agent:generateObject"];
|
|
3338
3339
|
function isPanelAppAgentCapability(value) {
|
|
3339
|
-
return value
|
|
3340
|
+
return PANEL_APP_AGENT_CAPABILITIES.includes(value);
|
|
3340
3341
|
}
|
|
3341
3342
|
//#endregion
|
|
3342
3343
|
//#region src/services/agent-run-client.service.ts
|
|
@@ -3611,6 +3612,7 @@ function getPanelAppBridgeScript() {
|
|
|
3611
3612
|
}
|
|
3612
3613
|
//#endregion
|
|
3613
3614
|
//#region src/utils/panel-app-manifest.utils.ts
|
|
3615
|
+
const PANEL_APP_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
3614
3616
|
function parsePanelAppManifest(html) {
|
|
3615
3617
|
return {
|
|
3616
3618
|
...readHtmlTitle(html),
|
|
@@ -3620,6 +3622,26 @@ function parsePanelAppManifest(html) {
|
|
|
3620
3622
|
serviceActions: readPanelAppServiceActions(html)
|
|
3621
3623
|
};
|
|
3622
3624
|
}
|
|
3625
|
+
function parsePanelAppFolderManifest(raw) {
|
|
3626
|
+
let parsed;
|
|
3627
|
+
try {
|
|
3628
|
+
parsed = JSON.parse(raw);
|
|
3629
|
+
} catch (error) {
|
|
3630
|
+
throw new Error(`panel-app.json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
3631
|
+
}
|
|
3632
|
+
if (!isRecord$7(parsed)) throw new Error("panel-app.json must contain an object.");
|
|
3633
|
+
const id = readOptionalString$6(parsed, "id");
|
|
3634
|
+
if (id && !PANEL_APP_ID_PATTERN.test(id)) throw new Error("panel app id must be kebab-case.");
|
|
3635
|
+
return {
|
|
3636
|
+
id,
|
|
3637
|
+
title: readRequiredString$3(parsed, "title"),
|
|
3638
|
+
description: readOptionalString$6(parsed, "description"),
|
|
3639
|
+
icon: readOptionalString$6(parsed, "icon"),
|
|
3640
|
+
entry: readRequiredString$3(parsed, "entry"),
|
|
3641
|
+
capabilities: readStringArray$1(parsed.capabilities, "capabilities"),
|
|
3642
|
+
serviceActions: readStringArray$1(parsed.actions, "actions")
|
|
3643
|
+
};
|
|
3644
|
+
}
|
|
3623
3645
|
function readPanelAppMeta(html) {
|
|
3624
3646
|
return {
|
|
3625
3647
|
...readPanelAppMetaField(html, "title"),
|
|
@@ -3674,6 +3696,22 @@ function parseTokenList(content) {
|
|
|
3674
3696
|
if (!content) return [];
|
|
3675
3697
|
return [...new Set(content.split(/[,\s]+/).map((entry) => entry.trim()).filter(Boolean))];
|
|
3676
3698
|
}
|
|
3699
|
+
function readRequiredString$3(record, key) {
|
|
3700
|
+
const value = readOptionalString$6(record, key);
|
|
3701
|
+
if (!value) throw new Error(`panel app ${key} is required.`);
|
|
3702
|
+
return value;
|
|
3703
|
+
}
|
|
3704
|
+
function readOptionalString$6(record, key) {
|
|
3705
|
+
return typeof record[key] === "string" && record[key].trim() ? record[key].trim() : void 0;
|
|
3706
|
+
}
|
|
3707
|
+
function readStringArray$1(value, key) {
|
|
3708
|
+
if (value === void 0) return [];
|
|
3709
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`panel app ${key} must be a string array.`);
|
|
3710
|
+
return [...new Set(value.map((entry) => entry.trim()).filter(Boolean))];
|
|
3711
|
+
}
|
|
3712
|
+
function isRecord$7(value) {
|
|
3713
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3714
|
+
}
|
|
3677
3715
|
function normalizeTextValue(value) {
|
|
3678
3716
|
return decodeHtmlAttribute(value ?? "").replace(/\s+/g, " ").trim() || void 0;
|
|
3679
3717
|
}
|
|
@@ -3687,6 +3725,169 @@ function normalizeIconHref(value) {
|
|
|
3687
3725
|
function decodeHtmlAttribute(value) {
|
|
3688
3726
|
return value.replace(/"/g, "\"").replace(/"/g, "\"").replace(/'/g, "'").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
3689
3727
|
}
|
|
3728
|
+
const PANEL_APP_FOLDER_MANIFEST_FILE_NAME = "panel-app.json";
|
|
3729
|
+
function isPanelAppSourceEntry(entry) {
|
|
3730
|
+
return entry.isFile() && isPanelAppFileName(entry.name) || entry.isDirectory() && isPanelAppDirName(entry.name);
|
|
3731
|
+
}
|
|
3732
|
+
function isPanelAppFileName(fileName) {
|
|
3733
|
+
return hasSafeBaseName(fileName) && fileName.endsWith(".panel.html");
|
|
3734
|
+
}
|
|
3735
|
+
function isPanelAppDirName(dirName) {
|
|
3736
|
+
return hasSafeBaseName(dirName) && dirName.endsWith(".panel");
|
|
3737
|
+
}
|
|
3738
|
+
function toPanelAppTitle(sourceName) {
|
|
3739
|
+
return sourceName.replace(new RegExp(`${escapeRegExp(".panel.html")}$`), "").replace(new RegExp(`${escapeRegExp(".panel")}$`), "").replace(/[-_]+/g, " ").trim() || sourceName;
|
|
3740
|
+
}
|
|
3741
|
+
function encodePanelAppId(sourceName) {
|
|
3742
|
+
return Buffer.from(sourceName, "utf8").toString("base64url");
|
|
3743
|
+
}
|
|
3744
|
+
function decodePanelAppId(id) {
|
|
3745
|
+
const normalizedId = id.trim();
|
|
3746
|
+
if (!normalizedId) throw new PanelAppError("PANEL_APP_INVALID_ID", "panel app id is required");
|
|
3747
|
+
let sourceName = "";
|
|
3748
|
+
try {
|
|
3749
|
+
sourceName = Buffer.from(normalizedId, "base64url").toString("utf8");
|
|
3750
|
+
} catch {
|
|
3751
|
+
throw new PanelAppError("PANEL_APP_INVALID_ID", "invalid panel app id");
|
|
3752
|
+
}
|
|
3753
|
+
if (encodePanelAppId(sourceName) !== normalizedId || !isPanelAppFileName(sourceName) && !isPanelAppDirName(sourceName)) throw new PanelAppError("PANEL_APP_INVALID_ID", "invalid panel app id");
|
|
3754
|
+
return sourceName;
|
|
3755
|
+
}
|
|
3756
|
+
async function readPanelAppFolderManifest(dirPath, dirName) {
|
|
3757
|
+
const manifest = parsePanelAppFolderManifest(await readFile(join(dirPath, PANEL_APP_FOLDER_MANIFEST_FILE_NAME), "utf8"));
|
|
3758
|
+
const expectedId = dirName.slice(0, -6);
|
|
3759
|
+
if (manifest.id && manifest.id !== expectedId) throw new PanelAppError("PANEL_APP_MANIFEST_INVALID", "panel app manifest id must match the directory name");
|
|
3760
|
+
return {
|
|
3761
|
+
...manifest,
|
|
3762
|
+
id: expectedId
|
|
3763
|
+
};
|
|
3764
|
+
}
|
|
3765
|
+
function resolvePanelAppRelativePath(rootPath, relativePath) {
|
|
3766
|
+
if (!relativePath.trim() || relativePath.includes("\0") || isAbsolute(relativePath)) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
3767
|
+
const normalizedPath = normalize(relativePath);
|
|
3768
|
+
if (normalizedPath === "." || normalizedPath.startsWith("..")) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
3769
|
+
const resolvedPath = resolve(rootPath, normalizedPath);
|
|
3770
|
+
const pathFromRoot = relative(resolve(rootPath), resolvedPath);
|
|
3771
|
+
if (pathFromRoot.startsWith("..") || isAbsolute(pathFromRoot)) throw new PanelAppError("PANEL_APP_INVALID_ASSET_PATH", "invalid panel app asset path");
|
|
3772
|
+
return resolvedPath;
|
|
3773
|
+
}
|
|
3774
|
+
function resolvePanelAppAssetContentType(path) {
|
|
3775
|
+
switch (extname(path).toLowerCase()) {
|
|
3776
|
+
case ".css": return "text/css; charset=utf-8";
|
|
3777
|
+
case ".js":
|
|
3778
|
+
case ".mjs": return "application/javascript; charset=utf-8";
|
|
3779
|
+
case ".json": return "application/json; charset=utf-8";
|
|
3780
|
+
case ".png": return "image/png";
|
|
3781
|
+
case ".svg": return "image/svg+xml; charset=utf-8";
|
|
3782
|
+
case ".webp": return "image/webp";
|
|
3783
|
+
case ".txt": return "text/plain; charset=utf-8";
|
|
3784
|
+
default: return "application/octet-stream";
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
function resolvePanelAppIconUrl(id, icon) {
|
|
3788
|
+
if (!icon) return;
|
|
3789
|
+
if (icon.startsWith("data:image/") || icon.startsWith("http://") || icon.startsWith("https://") || icon.startsWith("/") || isLikelyTextIcon(icon)) return icon;
|
|
3790
|
+
return `/api/panel-apps/${encodeURIComponent(id)}/assets/${encodePanelAppAssetPath(icon)}`;
|
|
3791
|
+
}
|
|
3792
|
+
function injectPanelAppAssetBase(html, id) {
|
|
3793
|
+
const base = `<base href="/api/panel-apps/${encodeURIComponent(id)}/assets/">`;
|
|
3794
|
+
if (/<base\b/i.test(html)) return html;
|
|
3795
|
+
if (/<head[^>]*>/i.test(html)) return html.replace(/<head[^>]*>/i, (head) => `${head}${base}`);
|
|
3796
|
+
return `${base}${html}`;
|
|
3797
|
+
}
|
|
3798
|
+
function encodePanelAppAssetPath(path) {
|
|
3799
|
+
return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
3800
|
+
}
|
|
3801
|
+
function hasSafeBaseName(name) {
|
|
3802
|
+
return !name.includes("/") && !name.includes("\\") && !name.includes("\0");
|
|
3803
|
+
}
|
|
3804
|
+
function isLikelyTextIcon(icon) {
|
|
3805
|
+
return !icon.includes("/") && !icon.includes(".") && icon.length <= 4;
|
|
3806
|
+
}
|
|
3807
|
+
function escapeRegExp(value) {
|
|
3808
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3809
|
+
}
|
|
3810
|
+
//#endregion
|
|
3811
|
+
//#region src/services/panel-app-source.service.ts
|
|
3812
|
+
var PanelAppSourceService = class {
|
|
3813
|
+
listSources = async (panelsPath) => {
|
|
3814
|
+
try {
|
|
3815
|
+
const entries = await readdir(panelsPath, { withFileTypes: true });
|
|
3816
|
+
const sources = [];
|
|
3817
|
+
for (const entry of entries.filter(isPanelAppSourceEntry)) try {
|
|
3818
|
+
sources.push(await this.readSource(panelsPath, entry.name));
|
|
3819
|
+
} catch (error) {
|
|
3820
|
+
if (!isPanelAppError(error) || error.code !== "PANEL_APP_NOT_FOUND") throw error;
|
|
3821
|
+
}
|
|
3822
|
+
return sources;
|
|
3823
|
+
} catch (error) {
|
|
3824
|
+
if (isPanelAppError(error)) throw error;
|
|
3825
|
+
if (isMissingFileError$1(error)) return [];
|
|
3826
|
+
throw new PanelAppError("PANEL_APP_READ_FAILED", error instanceof Error ? error.message : String(error));
|
|
3827
|
+
}
|
|
3828
|
+
};
|
|
3829
|
+
resolveSource = async (panelsPath, id) => {
|
|
3830
|
+
const sourceName = decodePanelAppId(id);
|
|
3831
|
+
try {
|
|
3832
|
+
return await this.readSource(panelsPath, sourceName);
|
|
3833
|
+
} catch (error) {
|
|
3834
|
+
if (isPanelAppError(error)) throw error;
|
|
3835
|
+
if (isMissingFileError$1(error)) throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
3836
|
+
throw new PanelAppError("PANEL_APP_READ_FAILED", error instanceof Error ? error.message : String(error));
|
|
3837
|
+
}
|
|
3838
|
+
};
|
|
3839
|
+
getAsset = async (panelsPath, id, assetPath) => {
|
|
3840
|
+
const source = await this.resolveSource(panelsPath, id);
|
|
3841
|
+
if (source.kind !== "folder") throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app asset not found");
|
|
3842
|
+
const filePath = resolvePanelAppRelativePath(source.sourcePath, assetPath);
|
|
3843
|
+
try {
|
|
3844
|
+
if (!(await stat(filePath)).isFile()) throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app asset not found");
|
|
3845
|
+
return {
|
|
3846
|
+
content: await readFile(filePath),
|
|
3847
|
+
contentType: resolvePanelAppAssetContentType(filePath)
|
|
3848
|
+
};
|
|
3849
|
+
} catch (error) {
|
|
3850
|
+
if (isPanelAppError(error)) throw error;
|
|
3851
|
+
if (isMissingFileError$1(error)) throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app asset not found");
|
|
3852
|
+
throw new PanelAppError("PANEL_APP_READ_FAILED", error instanceof Error ? error.message : String(error));
|
|
3853
|
+
}
|
|
3854
|
+
};
|
|
3855
|
+
readSource = async (panelsPath, sourceName) => {
|
|
3856
|
+
const sourcePath = join(panelsPath, sourceName);
|
|
3857
|
+
const sourceStat = await stat(sourcePath);
|
|
3858
|
+
if (sourceStat.isFile() && isPanelAppFileName(sourceName)) return {
|
|
3859
|
+
kind: "single-file",
|
|
3860
|
+
sourceName,
|
|
3861
|
+
sourcePath,
|
|
3862
|
+
entryPath: sourcePath,
|
|
3863
|
+
sourceStat
|
|
3864
|
+
};
|
|
3865
|
+
if (sourceStat.isDirectory() && isPanelAppDirName(sourceName)) return await this.readFolderSource(sourcePath, sourceName, sourceStat);
|
|
3866
|
+
throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
3867
|
+
};
|
|
3868
|
+
readFolderSource = async (sourcePath, sourceName, sourceStat) => {
|
|
3869
|
+
try {
|
|
3870
|
+
const manifest = await readPanelAppFolderManifest(sourcePath, sourceName);
|
|
3871
|
+
const entryPath = resolvePanelAppRelativePath(sourcePath, manifest.entry);
|
|
3872
|
+
if (!(await stat(entryPath)).isFile()) throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app entry not found");
|
|
3873
|
+
return {
|
|
3874
|
+
kind: "folder",
|
|
3875
|
+
sourceName,
|
|
3876
|
+
sourcePath,
|
|
3877
|
+
entryPath,
|
|
3878
|
+
manifest,
|
|
3879
|
+
sourceStat
|
|
3880
|
+
};
|
|
3881
|
+
} catch (error) {
|
|
3882
|
+
if (isPanelAppError(error)) throw error;
|
|
3883
|
+
if (isMissingFileError$1(error)) throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
3884
|
+
throw new PanelAppError("PANEL_APP_MANIFEST_INVALID", error instanceof Error ? error.message : String(error));
|
|
3885
|
+
}
|
|
3886
|
+
};
|
|
3887
|
+
};
|
|
3888
|
+
function isMissingFileError$1(error) {
|
|
3889
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
3890
|
+
}
|
|
3690
3891
|
//#endregion
|
|
3691
3892
|
//#region src/utils/panel-app-time.utils.ts
|
|
3692
3893
|
function resolvePanelAppCreatedAt(fileStat) {
|
|
@@ -3881,13 +4082,13 @@ function isRecord$6(value) {
|
|
|
3881
4082
|
}
|
|
3882
4083
|
//#endregion
|
|
3883
4084
|
//#region src/managers/panel-app.manager.ts
|
|
3884
|
-
const PANEL_APP_FILE_SUFFIX = ".panel.html";
|
|
3885
4085
|
const PANEL_APP_CONTENT_BASE_PATH = "/api/panel-apps";
|
|
3886
4086
|
const PANEL_APP_CONTENT_TYPE = "text/html; charset=utf-8";
|
|
3887
4087
|
const PANEL_APP_CAPABILITY_GRANTS_FILE_NAME = ".panel-app-capability-grants.json";
|
|
3888
4088
|
var PanelAppManager = class {
|
|
3889
4089
|
bridgeSessions = /* @__PURE__ */ new Map();
|
|
3890
4090
|
agentRunClient;
|
|
4091
|
+
sourceService = new PanelAppSourceService();
|
|
3891
4092
|
constructor(params) {
|
|
3892
4093
|
this.params = params;
|
|
3893
4094
|
this.agentRunClient = params.agentRunClient ?? (params.eventBus && params.ingress ? new AgentRunClient({
|
|
@@ -3898,25 +4099,26 @@ var PanelAppManager = class {
|
|
|
3898
4099
|
listPanelApps = async () => {
|
|
3899
4100
|
const workspacePath = this.getWorkspacePath();
|
|
3900
4101
|
const panelsPath = this.getPanelsPath(workspacePath);
|
|
3901
|
-
const
|
|
4102
|
+
const sources = await this.sourceService.listSources(panelsPath);
|
|
3902
4103
|
const appState = await this.createStateStore(panelsPath).load();
|
|
3903
4104
|
return {
|
|
3904
4105
|
workspacePath,
|
|
3905
4106
|
panelsPath,
|
|
3906
|
-
entries: (await Promise.all(
|
|
4107
|
+
entries: (await Promise.all(sources.map((source) => this.buildPanelAppEntry(source, appState[encodePanelAppId(source.sourceName)] ?? {})))).sort(this.comparePanelApps)
|
|
3907
4108
|
};
|
|
3908
4109
|
};
|
|
3909
4110
|
getPanelAppContent = async (id) => {
|
|
3910
|
-
const
|
|
3911
|
-
const filePath = join(this.getPanelsPath(this.getWorkspacePath()), fileName);
|
|
4111
|
+
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
3912
4112
|
try {
|
|
3913
|
-
|
|
3914
|
-
const html = await readFile(
|
|
3915
|
-
const manifest = parsePanelAppManifest(html);
|
|
4113
|
+
const source = await this.sourceService.resolveSource(panelsPath, id);
|
|
4114
|
+
const html = await readFile(source.entryPath, "utf8");
|
|
4115
|
+
const manifest = source.manifest ?? parsePanelAppManifest(html);
|
|
4116
|
+
const sourceId = encodePanelAppId(source.sourceName);
|
|
4117
|
+
const htmlWithBase = source.kind === "folder" ? injectPanelAppAssetBase(html, sourceId) : html;
|
|
3916
4118
|
return {
|
|
3917
|
-
id:
|
|
3918
|
-
fileName,
|
|
3919
|
-
html: injectPanelAppBridgeScript(
|
|
4119
|
+
id: sourceId,
|
|
4120
|
+
fileName: source.sourceName,
|
|
4121
|
+
html: injectPanelAppBridgeScript(htmlWithBase),
|
|
3920
4122
|
capabilities: manifest.capabilities,
|
|
3921
4123
|
contentType: PANEL_APP_CONTENT_TYPE,
|
|
3922
4124
|
serviceActions: manifest.serviceActions
|
|
@@ -3927,6 +4129,10 @@ var PanelAppManager = class {
|
|
|
3927
4129
|
throw new PanelAppError("PANEL_APP_READ_FAILED", error instanceof Error ? error.message : String(error));
|
|
3928
4130
|
}
|
|
3929
4131
|
};
|
|
4132
|
+
getPanelAppAsset = async (id, assetPath) => {
|
|
4133
|
+
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
4134
|
+
return await this.sourceService.getAsset(panelsPath, id, assetPath);
|
|
4135
|
+
};
|
|
3930
4136
|
getPanelAppBridgeScript = () => getPanelAppBridgeScript();
|
|
3931
4137
|
createPanelAppBridgeSession = async (params) => {
|
|
3932
4138
|
const content = await this.getPanelAppContent(params.id);
|
|
@@ -3996,20 +4202,20 @@ var PanelAppManager = class {
|
|
|
3996
4202
|
updatePanelAppPreferences = async (id, preferences) => {
|
|
3997
4203
|
const fileName = await this.resolvePanelAppFileName(id);
|
|
3998
4204
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
3999
|
-
const state = await this.createStateStore(panelsPath).updatePreferences(
|
|
4000
|
-
return await this.buildPanelAppEntry(panelsPath, fileName, state);
|
|
4205
|
+
const state = await this.createStateStore(panelsPath).updatePreferences(encodePanelAppId(fileName), preferences);
|
|
4206
|
+
return await this.buildPanelAppEntry(await this.sourceService.resolveSource(panelsPath, encodePanelAppId(fileName)), state);
|
|
4001
4207
|
};
|
|
4002
4208
|
recordPanelAppOpened = async (id) => {
|
|
4003
4209
|
const fileName = await this.resolvePanelAppFileName(id);
|
|
4004
4210
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
4005
|
-
const state = await this.createStateStore(panelsPath).recordOpened(
|
|
4006
|
-
return await this.buildPanelAppEntry(panelsPath, fileName, state);
|
|
4211
|
+
const state = await this.createStateStore(panelsPath).recordOpened(encodePanelAppId(fileName));
|
|
4212
|
+
return await this.buildPanelAppEntry(await this.sourceService.resolveSource(panelsPath, encodePanelAppId(fileName)), state);
|
|
4007
4213
|
};
|
|
4008
4214
|
deletePanelApp = async (id) => {
|
|
4009
|
-
const fileName = await this.resolvePanelAppFileName(id);
|
|
4010
4215
|
const panelsPath = this.getPanelsPath(this.getWorkspacePath());
|
|
4011
|
-
const
|
|
4012
|
-
|
|
4216
|
+
const source = await this.sourceService.resolveSource(panelsPath, id);
|
|
4217
|
+
const panelAppId = encodePanelAppId(source.sourceName);
|
|
4218
|
+
await rm(source.sourcePath, { recursive: source.kind === "folder" });
|
|
4013
4219
|
await this.createStateStore(panelsPath).deleteEntry(panelAppId);
|
|
4014
4220
|
await this.createCapabilityGrantStore().deleteCaller({
|
|
4015
4221
|
surface: "panel-app",
|
|
@@ -4018,7 +4224,7 @@ var PanelAppManager = class {
|
|
|
4018
4224
|
this.deleteBridgeSessionsByPanelAppId(panelAppId);
|
|
4019
4225
|
return {
|
|
4020
4226
|
deleted: true,
|
|
4021
|
-
fileName,
|
|
4227
|
+
fileName: source.sourceName,
|
|
4022
4228
|
id: panelAppId
|
|
4023
4229
|
};
|
|
4024
4230
|
};
|
|
@@ -4027,7 +4233,19 @@ var PanelAppManager = class {
|
|
|
4027
4233
|
if (!await this.createCapabilityGrantStore().isGranted(bridgeSession.caller, capability)) throw new PanelAppError("AUTHORIZATION_REQUIRED", `This panel app needs permission to use ${capability}.`);
|
|
4028
4234
|
};
|
|
4029
4235
|
assertDeclaredCapability = (bridgeSession, capability) => {
|
|
4030
|
-
if (!bridgeSession.declaredCapabilities.includes(capability)) throw new PanelAppError("PANEL_APP_CAPABILITY_NOT_DECLARED",
|
|
4236
|
+
if (!bridgeSession.declaredCapabilities.includes(capability)) throw new PanelAppError("PANEL_APP_CAPABILITY_NOT_DECLARED", this.describeMissingAgentCapability(bridgeSession.declaredCapabilities, capability));
|
|
4237
|
+
};
|
|
4238
|
+
describeMissingAgentCapability = (declaredCapabilities, capability) => {
|
|
4239
|
+
const declared = declaredCapabilities.length > 0 ? declaredCapabilities.join(", ") : "none";
|
|
4240
|
+
const valid = PANEL_APP_AGENT_CAPABILITIES.join(", ");
|
|
4241
|
+
const hint = declaredCapabilities.includes(capability.replace(":", ".")) ? ` Use ${capability}, not ${capability.replace(":", ".")}.` : "";
|
|
4242
|
+
return [
|
|
4243
|
+
`panel app did not declare ${capability}.`,
|
|
4244
|
+
`Declared: ${declared}.`,
|
|
4245
|
+
`Valid capabilities: ${valid}.`,
|
|
4246
|
+
`Declare it with nextclaw-panel-capabilities or panel-app.json capabilities.`,
|
|
4247
|
+
hint.trim()
|
|
4248
|
+
].filter(Boolean).join(" ");
|
|
4031
4249
|
};
|
|
4032
4250
|
requireAgentRunClient = () => {
|
|
4033
4251
|
if (!this.agentRunClient) throw new PanelAppError("PANEL_APP_AGENT_REQUEST_INVALID", "panel app agent client is not configured");
|
|
@@ -4037,63 +4255,31 @@ var PanelAppManager = class {
|
|
|
4037
4255
|
getPanelsPath = (workspacePath) => join(workspacePath, DEFAULT_PANELS_DIR);
|
|
4038
4256
|
createStateStore = (panelsPath) => new PanelAppStateStore(panelsPath);
|
|
4039
4257
|
createCapabilityGrantStore = () => new PanelAppCapabilityGrantStore(join(this.getPanelsPath(this.getWorkspacePath()), PANEL_APP_CAPABILITY_GRANTS_FILE_NAME));
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
throw new PanelAppError("PANEL_APP_READ_FAILED", error instanceof Error ? error.message : String(error));
|
|
4046
|
-
}
|
|
4047
|
-
};
|
|
4048
|
-
buildPanelAppEntry = async (panelsPath, fileName, state) => {
|
|
4049
|
-
const filePath = join(panelsPath, fileName);
|
|
4050
|
-
const [fileStat, html] = await Promise.all([stat(filePath), readFile(filePath, "utf8")]);
|
|
4051
|
-
const manifest = parsePanelAppManifest(html);
|
|
4052
|
-
const id = this.encodePanelAppId(fileName);
|
|
4053
|
-
const createdAt = resolvePanelAppCreatedAt(fileStat);
|
|
4054
|
-
const updatedAt = fileStat.mtime.toISOString();
|
|
4258
|
+
buildPanelAppEntry = async (source, state) => {
|
|
4259
|
+
const manifest = source.manifest ?? parsePanelAppManifest(await readFile(source.entryPath, "utf8"));
|
|
4260
|
+
const id = encodePanelAppId(source.sourceName);
|
|
4261
|
+
const createdAt = resolvePanelAppCreatedAt(source.sourceStat);
|
|
4262
|
+
const updatedAt = source.sourceStat.mtime.toISOString();
|
|
4055
4263
|
const entry = {
|
|
4056
4264
|
id,
|
|
4057
|
-
fileName,
|
|
4058
|
-
|
|
4265
|
+
fileName: source.sourceName,
|
|
4266
|
+
kind: source.kind,
|
|
4267
|
+
title: manifest.title ?? toPanelAppTitle(source.sourceName),
|
|
4059
4268
|
contentPath: `${PANEL_APP_CONTENT_BASE_PATH}/${encodeURIComponent(id)}/content`,
|
|
4060
4269
|
createdAt,
|
|
4061
4270
|
updatedAt,
|
|
4062
|
-
sizeBytes:
|
|
4271
|
+
sizeBytes: source.sourceStat.size,
|
|
4063
4272
|
favorite: state.favorite ?? false,
|
|
4064
4273
|
openCount: state.openCount ?? 0
|
|
4065
4274
|
};
|
|
4066
4275
|
if (manifest.description) entry.description = manifest.description;
|
|
4067
|
-
if (manifest.icon) entry.icon = manifest.icon;
|
|
4276
|
+
if (manifest.icon) entry.icon = source.kind === "folder" ? resolvePanelAppIconUrl(id, manifest.icon) : manifest.icon;
|
|
4068
4277
|
if (state.lastOpenedAt) entry.lastOpenedAt = state.lastOpenedAt;
|
|
4069
4278
|
return entry;
|
|
4070
4279
|
};
|
|
4071
4280
|
resolvePanelAppFileName = async (id) => {
|
|
4072
|
-
|
|
4073
|
-
const filePath = join(this.getPanelsPath(this.getWorkspacePath()), fileName);
|
|
4074
|
-
try {
|
|
4075
|
-
if ((await stat(filePath)).isFile()) return fileName;
|
|
4076
|
-
} catch (error) {
|
|
4077
|
-
if (this.isMissingFileError(error)) throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
4078
|
-
throw new PanelAppError("PANEL_APP_READ_FAILED", error instanceof Error ? error.message : String(error));
|
|
4079
|
-
}
|
|
4080
|
-
throw new PanelAppError("PANEL_APP_NOT_FOUND", "panel app not found");
|
|
4081
|
-
};
|
|
4082
|
-
encodePanelAppId = (fileName) => Buffer.from(fileName, "utf8").toString("base64url");
|
|
4083
|
-
decodePanelAppId = (id) => {
|
|
4084
|
-
const normalizedId = id.trim();
|
|
4085
|
-
if (!normalizedId) throw new PanelAppError("PANEL_APP_INVALID_ID", "panel app id is required");
|
|
4086
|
-
let fileName = "";
|
|
4087
|
-
try {
|
|
4088
|
-
fileName = Buffer.from(normalizedId, "base64url").toString("utf8");
|
|
4089
|
-
} catch {
|
|
4090
|
-
throw new PanelAppError("PANEL_APP_INVALID_ID", "invalid panel app id");
|
|
4091
|
-
}
|
|
4092
|
-
if (this.encodePanelAppId(fileName) !== normalizedId || !this.isPanelAppFileName(fileName)) throw new PanelAppError("PANEL_APP_INVALID_ID", "invalid panel app id");
|
|
4093
|
-
return fileName;
|
|
4281
|
+
return (await this.sourceService.resolveSource(this.getPanelsPath(this.getWorkspacePath()), id)).sourceName;
|
|
4094
4282
|
};
|
|
4095
|
-
isPanelAppFileName = (fileName) => fileName.endsWith(PANEL_APP_FILE_SUFFIX) && !fileName.includes("/") && !fileName.includes("\\") && !fileName.includes("\0");
|
|
4096
|
-
toPanelAppTitle = (fileName) => fileName.slice(0, -11).replace(/[-_]+/g, " ").trim() || fileName;
|
|
4097
4283
|
comparePanelApps = (left, right) => resolvePanelAppActivityMs(right) - resolvePanelAppActivityMs(left) || Number(right.favorite) - Number(left.favorite) || left.title.localeCompare(right.title);
|
|
4098
4284
|
deleteExpiredBridgeSessions = () => {
|
|
4099
4285
|
const now = Date.now();
|
|
@@ -4993,7 +5179,7 @@ var NcpAgentSessionMetadataStore = class {
|
|
|
4993
5179
|
read = async (sessionId, activitySnapshot) => {
|
|
4994
5180
|
try {
|
|
4995
5181
|
const parsed = JSON.parse(await readFile(this.metadataPath(sessionId), "utf-8"));
|
|
4996
|
-
if (!isRecord$
|
|
5182
|
+
if (!isRecord$9(parsed) || parsed._type !== "metadata" || !isRecord$9(parsed.metadata)) throw new Error(`invalid ncp agent session metadata sidecar: ${sessionId}`);
|
|
4997
5183
|
const createdAt = toIsoString(parsed.created_at, activitySnapshot.createdAt);
|
|
4998
5184
|
const agentId = normalizeNcpAgentId(typeof parsed.agent_id === "string" ? parsed.agent_id : void 0);
|
|
4999
5185
|
return {
|
|
@@ -5094,7 +5280,7 @@ var NcpAgentSessionSummaryIndexStore = class {
|
|
|
5094
5280
|
function serializeJournalEntry(entry) {
|
|
5095
5281
|
const serialized = JSON.stringify(entry);
|
|
5096
5282
|
if (!serialized) throw new Error("ncp agent session journal entry serialization produced empty output");
|
|
5097
|
-
if (!isRecord$
|
|
5283
|
+
if (!isRecord$9(JSON.parse(serialized))) throw new Error("ncp agent session journal entry serialization produced a non-object entry");
|
|
5098
5284
|
return serialized;
|
|
5099
5285
|
}
|
|
5100
5286
|
var NcpAgentSessionJournalStore = class {
|
|
@@ -5310,15 +5496,15 @@ var NcpAgentSessionJournalStore = class {
|
|
|
5310
5496
|
console.warn(`[ncp-agent-session-journal] skipped corrupted journal line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
|
|
5311
5497
|
continue;
|
|
5312
5498
|
}
|
|
5313
|
-
if (!isRecord$
|
|
5499
|
+
if (!isRecord$9(parsed)) continue;
|
|
5314
5500
|
if (parsed._type === "metadata") {
|
|
5315
|
-
metadata = isRecord$
|
|
5501
|
+
metadata = isRecord$9(parsed.metadata) ? structuredClone(parsed.metadata) : {};
|
|
5316
5502
|
agentId = normalizeNcpAgentId(typeof parsed.agent_id === "string" ? parsed.agent_id : void 0);
|
|
5317
5503
|
createdAt = toIsoString(parsed.created_at, createdAt);
|
|
5318
5504
|
updatedAt = toIsoString(parsed.updated_at, updatedAt);
|
|
5319
5505
|
continue;
|
|
5320
5506
|
}
|
|
5321
|
-
if (parsed._type === "event" && isRecord$
|
|
5507
|
+
if (parsed._type === "event" && isRecord$9(parsed.event)) {
|
|
5322
5508
|
const seq = Number(parsed.seq);
|
|
5323
5509
|
nextSeq = Math.max(nextSeq, Number.isFinite(seq) ? Math.trunc(seq) + 1 : nextSeq);
|
|
5324
5510
|
updatedAt = toIsoString(parsed.timestamp, updatedAt);
|
|
@@ -8575,6 +8761,6 @@ function resolveLegacyEventType(message) {
|
|
|
8575
8761
|
return `message.${role || "other"}`;
|
|
8576
8762
|
}
|
|
8577
8763
|
//#endregion
|
|
8578
|
-
export { AgentManager, AgentRunClient, AgentRuntimeRegistry, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionPreflightService, ContextWindowPreviewManager, DEFAULT_AGENT_RUNTIME_ENTRY_ID, ExtensionManager, GatewayInboundProcessor, LlmProviderManager, LlmUsageManager, LlmUsageStore, McpManager, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawKernel, PanelAppError, PanelAppManager, ProviderManagerNcpLLMApi, ServiceAppError, ServiceAppManager, SessionManager, SessionRequestManager, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildSessionOrchestrationSection, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextWindowSignature, createLlmUsageRecord, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseSkillFrontmatter, projectNcpMessagesWithContextCompaction, readContextWindowEventSessionId, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveNextclawNcpRunContext, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, upsertContextCompactionTimelineMessage, waitForAgentRuntimeSessionReply };
|
|
8764
|
+
export { AgentManager, AgentRunClient, AgentRuntimeRegistry, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_TIMELINE_KIND, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionPreflightService, ContextWindowPreviewManager, DEFAULT_AGENT_RUNTIME_ENTRY_ID, ExtensionManager, GatewayInboundProcessor, LlmProviderManager, LlmUsageManager, LlmUsageStore, McpManager, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawKernel, PANEL_APP_AGENT_CAPABILITIES, PanelAppError, PanelAppManager, ProviderManagerNcpLLMApi, ServiceAppError, ServiceAppManager, SessionManager, SessionRequestManager, SkillManager, UpdateManifestReader, buildAgentRunSendPayload, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildSessionOrchestrationSection, createAgentRuntimeSessionRequestDispatcher, createAssetTools, createContextWindowSignature, createLlmUsageRecord, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, getUnsignedUpdateManifest, hasLlmUsageTelemetry, isContextCompactionTimelineMessage, isContextWindowSnapshot, isPanelAppAgentCapability, isPanelAppError, isReplyCapableChannel, isServiceAppError, listExtensionChannelIds, normalizeAgentRuntimeSessionTypeIcon, normalizeLlmUsageModel, normalizeOptionalString, parseSkillFrontmatter, projectNcpMessagesWithContextCompaction, readContextWindowEventSessionId, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, resolveAgentRuntimeEntries, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolveNextclawNcpRunContext, resolveSessionChannelContext, runGatewayInboundLoop, sanitizeLlmUsage, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, stripSkillFrontmatter, syncSessionThinkingPreference, toNcpMessages, upsertContextCompactionTimelineMessage, waitForAgentRuntimeSessionReply };
|
|
8579
8765
|
|
|
8580
8766
|
//# sourceMappingURL=index.js.map
|