@jmfederico/pi-web 1.202610.0 → 1.202610.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -134
- package/dist/cli.js +23 -4
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/index-DUsqiytv.js +4408 -0
- package/dist/client/assets/vendor-editor-core-C2PJX3uW.js +10 -0
- package/dist/client/assets/{vendor-editor-languages-DOCw3vc9.js → vendor-editor-languages-CS6n1J8n.js} +1 -1
- package/dist/client/index.html +4 -48
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/pi-packages/captains-log/dist/browser/index.js +34 -34
- package/dist/pi-packages/captains-log/package.json +5 -5
- package/dist/pi-packages/relays/extensions/index.js +60 -0
- package/dist/pi-packages/relays/package.json +10 -1
- package/dist/pi-packages/relays/pi-web-plugin.js +31 -1
- package/dist/pi-packages/relays/prompts/relay-worktree.md +6 -16
- package/dist/pi-packages/relays/prompts/relay.md +6 -33
- package/dist/pi-packages/relays/relayDispatch.js +67 -0
- package/dist/pi-packages/relays/relayIdentity.js +37 -0
- package/dist/pi-packages/relays/relaysPanelElement.js +37 -12
- package/dist/pi-packages/relays/skills/relay/SKILL.md +24 -68
- package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +40 -219
- package/dist/pi-packages/workflows/browser/pi-web-plugin.js +33 -0
- package/dist/pi-packages/workflows/package.json +22 -0
- package/dist/pi-packages/workflows/prompts/worktree.md +12 -0
- package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +53 -53
- package/dist/pi-web-plugins/git/browser/git-panel.js +0 -1
- package/dist/pi-web-plugins/git/server-plugin.js +11 -0
- package/dist/pi-web-plugins/git/workspace-discovery-notify.cjs +14 -0
- package/dist/pi-web-plugins/git/workspaceDiscoveryHooks.js +180 -0
- package/dist/pi-web-plugins/info/infoInternals.js +3 -3
- package/dist/pi-web-plugins/info/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +1077 -414
- package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +30 -30
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +2 -2
- package/dist/pi-web-plugins/updates/updatesLogic.js +6 -5
- package/dist/piWebUpdate.js +405 -0
- package/dist/piWebUpdate.js.map +1 -0
- package/dist/piWebVersionReport.js +43 -1
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +229 -8
- package/dist/server/activity/workspaceActivityService.js +4 -1
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +5 -0
- package/dist/server/app.js.map +1 -1
- package/dist/server/configRoutes.js +6 -0
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/knownAutoInstallPiPackages.js +6 -0
- package/dist/server/knownAutoInstallPiPackages.js.map +1 -1
- package/dist/server/navigationPreferenceRoutes.js +39 -0
- package/dist/server/navigationPreferenceRoutes.js.map +1 -0
- package/dist/server/piWebStatus.js +19 -25
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/plugins/projectPluginSettings.js +95 -0
- package/dist/server/plugins/projectPluginSettings.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +26 -3
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
- package/dist/server/sessiond/autoInstallPiPackages.js +3 -3
- package/dist/server/sessiond/workspaceCatalogRoutes.js +3 -1
- package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -1
- package/dist/server/sessiond/workspaceNotificationRoutes.js +41 -0
- package/dist/server/sessiond/workspaceNotificationRoutes.js.map +1 -0
- package/dist/server/sessiond.js +40 -7
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/askUserDomain.js +232 -0
- package/dist/server/sessions/askUserDomain.js.map +1 -0
- package/dist/server/sessions/askUserTool.js +17 -17
- package/dist/server/sessions/askUserTool.js.map +1 -1
- package/dist/server/sessions/builtinExtensionFactories.js +13 -0
- package/dist/server/sessions/builtinExtensionFactories.js.map +1 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js +8 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -1
- package/dist/server/sessions/extensionSessionDispatch.js +76 -0
- package/dist/server/sessions/extensionSessionDispatch.js.map +1 -0
- package/dist/server/sessions/globalProviderPolicy.js +94 -18
- package/dist/server/sessions/globalProviderPolicy.js.map +1 -1
- package/dist/server/sessions/messageHistoryTarget.js +61 -0
- package/dist/server/sessions/messageHistoryTarget.js.map +1 -0
- package/dist/server/sessions/piSessionManagerGateway.js +23 -24
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +604 -266
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionArchiveStore.js +5 -1
- package/dist/server/sessions/sessionArchiveStore.js.map +1 -1
- package/dist/server/sessions/sessionArchiveTree.js +2 -2
- package/dist/server/sessions/sessionArchiveTree.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +9 -3
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionImageDimensions.js +31 -0
- package/dist/server/sessions/sessionImageDimensions.js.map +1 -0
- package/dist/server/sessions/sessionMediaIndex.js +7 -1
- package/dist/server/sessions/sessionMediaIndex.js.map +1 -1
- package/dist/server/sessions/sessionMetadata.js +51 -0
- package/dist/server/sessions/sessionMetadata.js.map +1 -0
- package/dist/server/sessions/sessionNameGenerator.js +8 -28
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +10 -2
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionSummaryScanner.js +62 -5
- package/dist/server/sessions/sessionSummaryScanner.js.map +1 -1
- package/dist/server/sessions/spawnSessionTool.js +3 -1
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +27 -17
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/subsessionCompletionDelivery.js +15 -0
- package/dist/server/sessions/subsessionCompletionDelivery.js.map +1 -0
- package/dist/server/sessions/subsessionCompletionInbox.js +134 -0
- package/dist/server/sessions/subsessionCompletionInbox.js.map +1 -0
- package/dist/server/sessions/subsessionProvenance.js +27 -0
- package/dist/server/sessions/subsessionProvenance.js.map +1 -0
- package/dist/server/sessions/transcriptMarkdown.js +48 -0
- package/dist/server/sessions/transcriptMarkdown.js.map +1 -0
- package/dist/server/sessions/transcriptMessages.js +3 -0
- package/dist/server/sessions/transcriptMessages.js.map +1 -1
- package/dist/server/status/machineStatusService.js +17 -5
- package/dist/server/status/machineStatusService.js.map +1 -1
- package/dist/server/storage/navigationPreferenceStore.js +137 -0
- package/dist/server/storage/navigationPreferenceStore.js.map +1 -0
- package/dist/server/workspaces/projectPiWebConfig.js +88 -6
- package/dist/server/workspaces/projectPiWebConfig.js.map +1 -1
- package/dist/server/workspaces/workspaceChangeNotifier.js +12 -0
- package/dist/server/workspaces/workspaceChangeNotifier.js.map +1 -0
- package/dist/server/workspaces/workspaceNotificationCommand.js +64 -0
- package/dist/server/workspaces/workspaceNotificationCommand.js.map +1 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js +55 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
- package/dist/server-plugin-api.d.ts +18 -0
- package/dist/server-plugin-api.js.map +1 -1
- package/dist/sessiond/sessionDaemonClient.js +3 -3
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/apiTypes.js +2 -2
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/federatedRoutes.js +5 -0
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/machineStatus.js +11 -2
- package/dist/shared/machineStatus.js.map +1 -1
- package/dist/shared/navigationPreferences.js +6 -0
- package/dist/shared/navigationPreferences.js.map +1 -0
- package/dist/shared/pluginApiTypes.d.ts +2 -0
- package/dist/shared/sessionMedia.js +6 -0
- package/dist/shared/sessionMedia.js.map +1 -1
- package/dist/shared/streamingBehavior.js +7 -0
- package/dist/shared/streamingBehavior.js.map +1 -0
- package/dist/shared/themePreference.js +23 -0
- package/dist/shared/themePreference.js.map +1 -0
- package/dist/workspaceNotificationCli.js +95 -0
- package/dist/workspaceNotificationCli.js.map +1 -0
- package/docs/config.md +100 -85
- package/docs/plugins.md +109 -30
- package/examples/session-bridge-plugin/README.md +1 -1
- package/examples/session-bridge-plugin/package.json +3 -3
- package/examples/workspace-provider-plugin/package.json +1 -1
- package/package.json +36 -36
- package/dist/client/assets/index-_1GwJfSs.js +0 -4176
- package/dist/client/assets/vendor-editor-core-YWog6mAR.js +0 -10
- package/dist/server/sessions/pendingAskStore.js +0 -275
- package/dist/server/sessions/pendingAskStore.js.map +0 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
export const SUBSESSION_COMPLETION_ENTRY_TYPE = "pi-web.subsession.completed";
|
|
2
|
+
export const SUBSESSION_RECEIPT_ENTRY_TYPE = "pi-web.subsession.received";
|
|
3
|
+
export const SUBSESSION_NOTIFICATION_CUSTOM_TYPE = "subsession.completion";
|
|
4
|
+
/** One owner for delivery through tools, run boundaries, and idle-parent wakes. */
|
|
5
|
+
export class SubsessionCompletionInbox {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.completions = new Map();
|
|
8
|
+
this.received = new Set();
|
|
9
|
+
this.claimed = new Set();
|
|
10
|
+
}
|
|
11
|
+
record(completion) {
|
|
12
|
+
this.completions.set(completion.completionId, completion);
|
|
13
|
+
}
|
|
14
|
+
latest(sessionId) {
|
|
15
|
+
let latest;
|
|
16
|
+
for (const completion of this.completions.values()) {
|
|
17
|
+
if (completion.sessionId === sessionId)
|
|
18
|
+
latest = completion;
|
|
19
|
+
}
|
|
20
|
+
return latest;
|
|
21
|
+
}
|
|
22
|
+
pending() {
|
|
23
|
+
return [...this.completions.values()].filter(({ completionId }) => !this.received.has(completionId) && !this.claimed.has(completionId));
|
|
24
|
+
}
|
|
25
|
+
claim() {
|
|
26
|
+
const pending = this.pending();
|
|
27
|
+
for (const { completionId } of pending)
|
|
28
|
+
this.claimed.add(completionId);
|
|
29
|
+
return pending;
|
|
30
|
+
}
|
|
31
|
+
receive(completionIds) {
|
|
32
|
+
for (const id of completionIds) {
|
|
33
|
+
this.received.add(id);
|
|
34
|
+
this.claimed.delete(id);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
hasReceived(completionId) {
|
|
38
|
+
return this.received.has(completionId);
|
|
39
|
+
}
|
|
40
|
+
release(completionIds) {
|
|
41
|
+
for (const id of completionIds)
|
|
42
|
+
this.claimed.delete(id);
|
|
43
|
+
}
|
|
44
|
+
releaseClaims() {
|
|
45
|
+
this.claimed.clear();
|
|
46
|
+
}
|
|
47
|
+
forgetChild(sessionId) {
|
|
48
|
+
for (const [id, completion] of this.completions) {
|
|
49
|
+
if (completion.sessionId !== sessionId)
|
|
50
|
+
continue;
|
|
51
|
+
this.completions.delete(id);
|
|
52
|
+
this.received.delete(id);
|
|
53
|
+
this.claimed.delete(id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Receipts follow the active parent branch; rewinding before a receipt restores unread results. */
|
|
57
|
+
restoreReceipts(entries, releaseClaims = true) {
|
|
58
|
+
this.received.clear();
|
|
59
|
+
if (releaseClaims)
|
|
60
|
+
this.claimed.clear();
|
|
61
|
+
const toolResults = new Map();
|
|
62
|
+
for (const [index, entry] of entries.entries()) {
|
|
63
|
+
if (!isRecord(entry) || entry["type"] !== "message" || !isRecord(entry["message"]))
|
|
64
|
+
continue;
|
|
65
|
+
const message = entry["message"];
|
|
66
|
+
if (message["role"] === "toolResult" && message["isError"] !== true && typeof message["toolCallId"] === "string") {
|
|
67
|
+
toolResults.set(message["toolCallId"], index);
|
|
68
|
+
const nested = message["nestedCalls"];
|
|
69
|
+
if (isRecord(nested) && Array.isArray(nested["calls"])) {
|
|
70
|
+
for (const call of nested["calls"]) {
|
|
71
|
+
if (isRecord(call) && call["status"] === "ok" && typeof call["id"] === "string")
|
|
72
|
+
toolResults.set(call["id"], index);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
for (const [index, entry] of entries.entries()) {
|
|
78
|
+
if (!isRecord(entry))
|
|
79
|
+
continue;
|
|
80
|
+
if (entry["type"] === "custom" && entry["customType"] === SUBSESSION_RECEIPT_ENTRY_TYPE) {
|
|
81
|
+
const data = entry["data"];
|
|
82
|
+
// A tool receipt only counts after its successful result was committed.
|
|
83
|
+
// A crash, failed result write, or rewind between them must not lose an answer.
|
|
84
|
+
if (isRecord(data) && typeof data["toolCallId"] === "string" && (toolResults.get(data["toolCallId"]) ?? -1) <= index)
|
|
85
|
+
continue;
|
|
86
|
+
this.receive(completionReceiptIds(data));
|
|
87
|
+
}
|
|
88
|
+
else if (entry["type"] === "custom_message" && entry["customType"] === SUBSESSION_NOTIFICATION_CUSTOM_TYPE) {
|
|
89
|
+
this.receive(completionReceiptIds(entry["details"]));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export function completionReceiptIds(value) {
|
|
95
|
+
if (!isRecord(value) || !Array.isArray(value["completionIds"]))
|
|
96
|
+
return [];
|
|
97
|
+
return value["completionIds"].filter((id) => typeof id === "string");
|
|
98
|
+
}
|
|
99
|
+
export function persistedSubsessionCompletions(entries) {
|
|
100
|
+
return entries.flatMap((entry) => {
|
|
101
|
+
if (!isRecord(entry) || entry["type"] !== "custom" || entry["customType"] !== SUBSESSION_COMPLETION_ENTRY_TYPE)
|
|
102
|
+
return [];
|
|
103
|
+
const data = entry["data"];
|
|
104
|
+
if (!isRecord(data) || typeof data["completionId"] !== "string" || typeof data["sessionId"] !== "string"
|
|
105
|
+
|| typeof data["cwd"] !== "string" || (data["status"] !== "idle" && data["status"] !== "error")
|
|
106
|
+
|| typeof data["finalText"] !== "string" || typeof data["messageCount"] !== "number"
|
|
107
|
+
|| !Number.isSafeInteger(data["messageCount"]) || data["messageCount"] < 0)
|
|
108
|
+
return [];
|
|
109
|
+
return [{
|
|
110
|
+
completionId: data["completionId"], sessionId: data["sessionId"], cwd: data["cwd"], status: data["status"],
|
|
111
|
+
finalText: data["finalText"], messageCount: data["messageCount"],
|
|
112
|
+
...(typeof data["errorDetail"] === "string" ? { errorDetail: data["errorDetail"] } : {}),
|
|
113
|
+
}];
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/** Keep oversized answers available for deliberate inspection, rather than repeating partial output. */
|
|
117
|
+
export function renderSubsessionCompletion(completion) {
|
|
118
|
+
const { sessionId, status, finalText, errorDetail } = completion;
|
|
119
|
+
const error = errorDetail === undefined ? "" : `Error: ${errorDetail}\n\n`;
|
|
120
|
+
const output = finalText.length > 2000
|
|
121
|
+
? `Output from subsession ${sessionId} was too long for this completion notice and was omitted. Call check_subsession with sessionId "${sessionId}" to retrieve the final output.`
|
|
122
|
+
: `--- SUBSESSION OUTPUT: ${sessionId} ---\n${finalText === "" ? "(no output)" : finalText}`;
|
|
123
|
+
return `Subsession ${sessionId} stopped working (${status}).\n\n${error}${output}`;
|
|
124
|
+
}
|
|
125
|
+
export function renderSubsessionCompletions(completions, workingIds) {
|
|
126
|
+
const next = workingIds.length === 0
|
|
127
|
+
? "No other tracked subsessions are working."
|
|
128
|
+
: `Still working: ${workingIds.join(", ")}. Continue working, or call yield_to_subsessions alone and last at the next join point. Further unread results arrive automatically; do not poll.`;
|
|
129
|
+
return `${completions.map(renderSubsessionCompletion).join("\n\n")}\n\n${next}`;
|
|
130
|
+
}
|
|
131
|
+
function isRecord(value) {
|
|
132
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=subsessionCompletionInbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subsessionCompletionInbox.js","sourceRoot":"","sources":["../../../src/server/sessions/subsessionCompletionInbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { isRecord } from "./sessionFileFormat.js";
|
|
2
|
+
export function parsePersistedChildSubsessionLink(entry) {
|
|
3
|
+
return parseLink(entry, "pi-web.subsession.spawned");
|
|
4
|
+
}
|
|
5
|
+
export function parsePersistedParentSubsessionLink(entry) {
|
|
6
|
+
const link = parseLink(entry, "pi-web.subsession.link");
|
|
7
|
+
if (link === undefined || !isRecord(entry) || !isRecord(entry["data"]))
|
|
8
|
+
return undefined;
|
|
9
|
+
const spawnedSessionFile = nonEmptyString(entry["data"]["spawnedSessionFile"]);
|
|
10
|
+
const cwd = nonEmptyString(entry["data"]["cwd"]);
|
|
11
|
+
return {
|
|
12
|
+
...link,
|
|
13
|
+
...(spawnedSessionFile === undefined ? {} : { spawnedSessionFile }),
|
|
14
|
+
...(cwd === undefined ? {} : { cwd }),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function parseLink(entry, customType) {
|
|
18
|
+
if (!isRecord(entry) || entry["type"] !== "custom" || entry["customType"] !== customType || !isRecord(entry["data"]))
|
|
19
|
+
return undefined;
|
|
20
|
+
const spawnedBySessionId = nonEmptyString(entry["data"]["spawnedBySessionId"]);
|
|
21
|
+
const spawnedSessionId = nonEmptyString(entry["data"]["spawnedSessionId"]);
|
|
22
|
+
return spawnedBySessionId === undefined || spawnedSessionId === undefined ? undefined : { spawnedBySessionId, spawnedSessionId };
|
|
23
|
+
}
|
|
24
|
+
function nonEmptyString(value) {
|
|
25
|
+
return typeof value === "string" && value !== "" ? value : undefined;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=subsessionProvenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subsessionProvenance.js","sourceRoot":"","sources":["../../../src/server/sessions/subsessionProvenance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAclD,MAAM,UAAU,iCAAiC,CAAC,KAAc;IAC9D,OAAO,SAAS,CAAC,KAAK,EAAE,2BAA2B,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,KAAc;IAC/D,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;IACxD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzF,MAAM,kBAAkB,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC/E,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,OAAO;QACL,GAAG,IAAI;QACP,GAAG,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC;QACnE,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,KAAc,EAAE,UAAkB;IACnD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,UAAU,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACvI,MAAM,kBAAkB,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC/E,MAAM,gBAAgB,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC3E,OAAO,kBAAkB,KAAK,SAAS,IAAI,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,CAAC;AACnI,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Browser-only projection: never replace canonical content or pass this result back to Pi. */
|
|
2
|
+
export function projectTranscriptMarkdown(message, transformers, onError) {
|
|
3
|
+
if (!isRecord(message) || transformers.length === 0)
|
|
4
|
+
return message;
|
|
5
|
+
const role = message["role"];
|
|
6
|
+
if (role !== "user" && role !== "assistant")
|
|
7
|
+
return message;
|
|
8
|
+
const transform = (text, messageType) => {
|
|
9
|
+
let result = text;
|
|
10
|
+
for (const [index, transformer] of transformers.entries()) {
|
|
11
|
+
try {
|
|
12
|
+
// No terminal geometry exists in the browser. Use a fixed, positive width.
|
|
13
|
+
const next = transformer(result, { messageType, isStreaming: false, availableWidth: 80 });
|
|
14
|
+
if (typeof next !== "string")
|
|
15
|
+
throw new TypeError("Markdown transformer must return a string");
|
|
16
|
+
result = next;
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
onError(error, index);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
};
|
|
24
|
+
const content = message["content"];
|
|
25
|
+
if (typeof content === "string") {
|
|
26
|
+
const displayText = transform(content, role);
|
|
27
|
+
return displayText === content ? message : { ...message, displayText };
|
|
28
|
+
}
|
|
29
|
+
if (!Array.isArray(content))
|
|
30
|
+
return message;
|
|
31
|
+
const projected = content.map((part) => {
|
|
32
|
+
if (!isRecord(part))
|
|
33
|
+
return part;
|
|
34
|
+
const type = part["type"];
|
|
35
|
+
const text = type === "text" ? part["text"] : type === "thinking" && role === "assistant" ? part["thinking"] ?? part["text"] : undefined;
|
|
36
|
+
if (typeof text !== "string")
|
|
37
|
+
return part;
|
|
38
|
+
const displayText = transform(text, type === "thinking" ? "assistant-thinking" : role);
|
|
39
|
+
if (displayText === text)
|
|
40
|
+
return part;
|
|
41
|
+
return { ...part, displayText };
|
|
42
|
+
});
|
|
43
|
+
return projected.some((part, index) => part !== content[index]) ? { ...message, content: projected } : message;
|
|
44
|
+
}
|
|
45
|
+
function isRecord(value) {
|
|
46
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=transcriptMarkdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcriptMarkdown.js","sourceRoot":"","sources":["../../../src/server/sessions/transcriptMarkdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -43,6 +43,9 @@ export function annotateAssistantThinkingLevel(message, thinkingLevel) {
|
|
|
43
43
|
return message;
|
|
44
44
|
if (!isRecord(message) || message["role"] !== "assistant")
|
|
45
45
|
return message;
|
|
46
|
+
// Pi records the physical model's level; the selected level is only a fallback.
|
|
47
|
+
if (typeof message["thinkingLevel"] === "string" && message["thinkingLevel"] !== "")
|
|
48
|
+
return message;
|
|
46
49
|
return { ...message, thinkingLevel };
|
|
47
50
|
}
|
|
48
51
|
function isRecord(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcriptMessages.js","sourceRoot":"","sources":["../../../src/server/sessions/transcriptMessages.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transcriptMessages.js","sourceRoot":"","sources":["../../../src/server/sessions/transcriptMessages.ts"],"names":[],"mappings":"AAEA,2FAA2F;AAC3F,MAAM,UAAU,0BAA0B,CAAC,OAA2B;IACpE,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,aAAiC,CAAC;IACtC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAC/B,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,uBAAuB,EAAE,CAAC;YAC9C,IAAI,OAAO,KAAK,CAAC,eAAe,CAAC,KAAK,QAAQ;gBAAE,aAAa,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC;YACvF,SAAS;QACX,CAAC;QACD,IAAI,OAAgB,CAAC;QACrB,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,SAAS;YAAE,OAAO,GAAG,8BAA8B,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;aACtG,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,gBAAgB,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,IAAI;YAAE,OAAO,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;aACzL,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,YAAY;YAAE,OAAO,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,yBAAyB,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC;aAC1J,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,gBAAgB;YAAE,OAAO,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,sBAAsB,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC;;YAC/J,SAAS;QACd,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,+EAA+E;YAC/E,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;YAC/B,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC;YAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,MAAM,iBAAiB,GAAsB;gBAC3C,GAAG,OAAO;gBACV,GAAG,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACtE,CAAC;YACF,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACnC,CAAC;;YAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,8BAA8B,CAAC,OAAgB,EAAE,aAAiC;IAChG,IAAI,aAAa,KAAK,SAAS,IAAI,aAAa,KAAK,EAAE,IAAI,aAAa,KAAK,KAAK;QAAE,OAAO,OAAO,CAAC;IACnG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,WAAW;QAAE,OAAO,OAAO,CAAC;IAC1E,gFAAgF;IAChF,IAAI,OAAO,OAAO,CAAC,eAAe,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC,KAAK,EAAE;QAAE,OAAO,OAAO,CAAC;IACpG,OAAO,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,CAAC;AACvC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -19,6 +19,7 @@ export class MachineStatusService {
|
|
|
19
19
|
machine: {},
|
|
20
20
|
projects: {},
|
|
21
21
|
workspaces: {},
|
|
22
|
+
sessions: {},
|
|
22
23
|
unattributed: {},
|
|
23
24
|
generatedAt: new Date().toISOString(),
|
|
24
25
|
};
|
|
@@ -86,7 +87,16 @@ export class MachineStatusService {
|
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
async computeTree() {
|
|
89
|
-
const
|
|
90
|
+
const activity = this.dependencies.activity.snapshot();
|
|
91
|
+
const unread = this.dependencies.unread.catalogSnapshot();
|
|
92
|
+
const flagsByCwd = this.flagsByCwd(activity.workspaces, unread.sessions);
|
|
93
|
+
const sessions = new FlagAccumulator();
|
|
94
|
+
for (const session of activity.sessions) {
|
|
95
|
+
sessions.add(session.sessionId, { [CORE_STATUS_FLAGS.working]: true });
|
|
96
|
+
}
|
|
97
|
+
for (const session of unread.sessions) {
|
|
98
|
+
sessions.add(session.sessionId, { [CORE_STATUS_FLAGS.unread]: true });
|
|
99
|
+
}
|
|
90
100
|
const attributions = await this.dependencies.attribution.attribute(flagsByCwd.keys());
|
|
91
101
|
const projects = new FlagAccumulator();
|
|
92
102
|
const workspaces = new FlagAccumulator();
|
|
@@ -111,6 +121,7 @@ export class MachineStatusService {
|
|
|
111
121
|
]),
|
|
112
122
|
projects: rolledProjects,
|
|
113
123
|
workspaces: rolledWorkspaces,
|
|
124
|
+
sessions: sessions.rollUp(),
|
|
114
125
|
unattributed: rolledUnattributed,
|
|
115
126
|
};
|
|
116
127
|
}
|
|
@@ -119,7 +130,7 @@ export class MachineStatusService {
|
|
|
119
130
|
* cwd that stops being active disappears instead of turning into empty flags,
|
|
120
131
|
* which is what makes structural comparison a sound change test.
|
|
121
132
|
*/
|
|
122
|
-
flagsByCwd() {
|
|
133
|
+
flagsByCwd(activities, unreadSessions) {
|
|
123
134
|
const flagsByCwd = new Map();
|
|
124
135
|
const setFlag = (cwd, flagId) => {
|
|
125
136
|
if (cwd === "")
|
|
@@ -128,13 +139,13 @@ export class MachineStatusService {
|
|
|
128
139
|
flags[flagId] = true;
|
|
129
140
|
flagsByCwd.set(cwd, flags);
|
|
130
141
|
};
|
|
131
|
-
for (const activity of
|
|
142
|
+
for (const activity of activities) {
|
|
132
143
|
if (activity.hasSessionActivity)
|
|
133
144
|
setFlag(activity.cwd, CORE_STATUS_FLAGS.working);
|
|
134
145
|
if (activity.hasTerminalActivity)
|
|
135
146
|
setFlag(activity.cwd, CORE_STATUS_FLAGS.terminal);
|
|
136
147
|
}
|
|
137
|
-
for (const session of
|
|
148
|
+
for (const session of unreadSessions) {
|
|
138
149
|
setFlag(session.cwd, CORE_STATUS_FLAGS.unread);
|
|
139
150
|
}
|
|
140
151
|
return flagsByCwd;
|
|
@@ -161,7 +172,8 @@ function isSameStatusTree(left, right) {
|
|
|
161
172
|
return isSameFlags(left.machine, right.machine)
|
|
162
173
|
&& isSameFlags(left.unattributed, right.unattributed)
|
|
163
174
|
&& isSameNodes(left.projects, right.projects)
|
|
164
|
-
&& isSameNodes(left.workspaces, right.workspaces)
|
|
175
|
+
&& isSameNodes(left.workspaces, right.workspaces)
|
|
176
|
+
&& isSameNodes(left.sessions, right.sessions ?? {});
|
|
165
177
|
}
|
|
166
178
|
function isSameNodes(left, right) {
|
|
167
179
|
const leftIds = Object.keys(left);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machineStatusService.js","sourceRoot":"","sources":["../../../src/server/status/machineStatusService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,iBAAiB,EACjB,iBAAiB,GAGlB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"machineStatusService.js","sourceRoot":"","sources":["../../../src/server/status/machineStatusService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { piWebDataDir } from "../../config.js";
|
|
5
|
+
export function navigationPreferenceStorePath(env = process.env, cwd = process.cwd()) {
|
|
6
|
+
return join(piWebDataDir(env, cwd), "navigation-preferences.json");
|
|
7
|
+
}
|
|
8
|
+
export class InvalidNavigationPreferenceError extends Error {
|
|
9
|
+
}
|
|
10
|
+
export class NavigationPreferenceConflictError extends Error {
|
|
11
|
+
constructor(current) {
|
|
12
|
+
super("Navigation preference revision conflict");
|
|
13
|
+
this.current = current;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function parseNavigationPreference(value) {
|
|
17
|
+
if (!isRecord(value) || !Array.isArray(value["ids"])) {
|
|
18
|
+
throw new InvalidNavigationPreferenceError("Navigation preference must contain an ids array");
|
|
19
|
+
}
|
|
20
|
+
const ids = value["ids"];
|
|
21
|
+
if (!ids.every((id) => typeof id === "string" && id.trim() !== "")) {
|
|
22
|
+
throw new InvalidNavigationPreferenceError("Navigation IDs must be non-empty strings");
|
|
23
|
+
}
|
|
24
|
+
if (new Set(ids).size !== ids.length) {
|
|
25
|
+
throw new InvalidNavigationPreferenceError("Navigation IDs must be unique");
|
|
26
|
+
}
|
|
27
|
+
const revision = value["revision"];
|
|
28
|
+
if (revision !== undefined && (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 0)) {
|
|
29
|
+
throw new InvalidNavigationPreferenceError("Navigation revision must be a non-negative safe integer");
|
|
30
|
+
}
|
|
31
|
+
return { ids: [...ids], ...(revision === undefined ? {} : { revision }) };
|
|
32
|
+
}
|
|
33
|
+
function isRecord(value) {
|
|
34
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
function parseScope(value) {
|
|
37
|
+
if (isRecord(value)) {
|
|
38
|
+
if (value["kind"] === "projects")
|
|
39
|
+
return { kind: "projects" };
|
|
40
|
+
const projectId = value["projectId"];
|
|
41
|
+
if (typeof projectId === "string" && projectId !== "") {
|
|
42
|
+
if (value["kind"] === "workspaces")
|
|
43
|
+
return { kind: "workspaces", projectId };
|
|
44
|
+
const workspaceId = value["workspaceId"];
|
|
45
|
+
if (value["kind"] === "session-pins" && typeof workspaceId === "string" && workspaceId !== "") {
|
|
46
|
+
return { kind: "session-pins", projectId, workspaceId };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
throw new Error("Invalid navigation preference scope in state file");
|
|
51
|
+
}
|
|
52
|
+
function scopeKey(scope) {
|
|
53
|
+
// Tuple encoding avoids collisions for opaque IDs containing separators.
|
|
54
|
+
return JSON.stringify(scope.kind === "projects" ? [scope.kind]
|
|
55
|
+
: scope.kind === "workspaces" ? [scope.kind, scope.projectId]
|
|
56
|
+
: [scope.kind, scope.projectId, scope.workspaceId]);
|
|
57
|
+
}
|
|
58
|
+
/** Web/API-owned managed state. IDs are opaque: do not query sessiond or prune offline/stale entries. */
|
|
59
|
+
export class NavigationPreferenceStore {
|
|
60
|
+
constructor(filePath = navigationPreferenceStorePath()) {
|
|
61
|
+
this.filePath = filePath;
|
|
62
|
+
this.writes = Promise.resolve();
|
|
63
|
+
}
|
|
64
|
+
async get(scope) {
|
|
65
|
+
await this.writes;
|
|
66
|
+
const entry = (await this.read()).entries.find((entry) => scopeKey(entry.scope) === scopeKey(scope));
|
|
67
|
+
return { ids: [...(entry?.ids ?? [])], revision: entry?.revision ?? 0 };
|
|
68
|
+
}
|
|
69
|
+
async put(scope, value) {
|
|
70
|
+
const preference = parseNavigationPreference(value);
|
|
71
|
+
let revision = 0;
|
|
72
|
+
const operation = this.writes.then(async () => {
|
|
73
|
+
const data = await this.read();
|
|
74
|
+
const current = data.entries.find((entry) => scopeKey(entry.scope) === scopeKey(scope));
|
|
75
|
+
const currentRevision = current?.revision ?? 0;
|
|
76
|
+
if (preference.revision !== undefined && preference.revision !== currentRevision) {
|
|
77
|
+
throw new NavigationPreferenceConflictError({ ids: [...(current?.ids ?? [])], revision: currentRevision });
|
|
78
|
+
}
|
|
79
|
+
revision = currentRevision + 1;
|
|
80
|
+
if (!Number.isSafeInteger(revision))
|
|
81
|
+
throw new Error("Navigation preference revision exhausted");
|
|
82
|
+
data.entries = data.entries.filter((entry) => scopeKey(entry.scope) !== scopeKey(scope));
|
|
83
|
+
// Keep cleared entries so stale conditional writes cannot resurrect old preferences.
|
|
84
|
+
data.entries.push({ scope, ids: preference.ids, revision });
|
|
85
|
+
await this.write(data);
|
|
86
|
+
});
|
|
87
|
+
// A failed write must reach its caller without poisoning later operations.
|
|
88
|
+
this.writes = operation.catch(() => undefined);
|
|
89
|
+
await operation;
|
|
90
|
+
return { ids: [...preference.ids], revision };
|
|
91
|
+
}
|
|
92
|
+
async read() {
|
|
93
|
+
let text;
|
|
94
|
+
try {
|
|
95
|
+
text = await readFile(this.filePath, "utf8");
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
99
|
+
return { version: 1, entries: [] };
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
const value = JSON.parse(text);
|
|
103
|
+
if (!isRecord(value) || value["version"] !== 1 || !Array.isArray(value["entries"])) {
|
|
104
|
+
throw new Error("Invalid navigation preference state file");
|
|
105
|
+
}
|
|
106
|
+
const entries = value["entries"].map((entry) => {
|
|
107
|
+
if (!isRecord(entry))
|
|
108
|
+
throw new Error("Invalid navigation preference entry");
|
|
109
|
+
// Disk corruption is a server error, not a client validation failure.
|
|
110
|
+
try {
|
|
111
|
+
const preference = parseNavigationPreference(entry);
|
|
112
|
+
if (preference.revision === undefined)
|
|
113
|
+
throw new Error("Missing navigation preference revision");
|
|
114
|
+
return { scope: parseScope(entry["scope"]), ids: preference.ids, revision: preference.revision };
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
throw new Error("Invalid navigation preference state file", { cause: error });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
if (new Set(entries.map((entry) => scopeKey(entry.scope))).size !== entries.length) {
|
|
121
|
+
throw new Error("Duplicate navigation preference scopes in state file");
|
|
122
|
+
}
|
|
123
|
+
return { version: 1, entries };
|
|
124
|
+
}
|
|
125
|
+
async write(data) {
|
|
126
|
+
await mkdir(dirname(this.filePath), { recursive: true });
|
|
127
|
+
const temporary = `${this.filePath}.${randomUUID()}.tmp`;
|
|
128
|
+
try {
|
|
129
|
+
await writeFile(temporary, `${JSON.stringify(data, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
130
|
+
await rename(temporary, this.filePath);
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
await rm(temporary, { force: true });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=navigationPreferenceStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigationPreferenceStore.js","sourceRoot":"","sources":["../../../src/server/storage/navigationPreferenceStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { open, stat } from "node:fs/promises";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { effectiveAttachmentsConfig, effectiveUploadsConfig, parseAttachmentsConfig, parsePathAccessConfig, parseUploadsConfig } from "../../config.js";
|
|
5
|
+
import { isPiWebPluginId, piWebPluginIdPattern } from "../../shared/pluginIds.js";
|
|
4
6
|
export const PROJECT_PI_WEB_CONFIG_PATH = ".pi-web/config.json";
|
|
5
|
-
export async function loadProjectPiWebConfig(projectPath) {
|
|
7
|
+
export async function loadProjectPiWebConfig(projectPath, signal) {
|
|
6
8
|
const path = join(projectPath, PROJECT_PI_WEB_CONFIG_PATH);
|
|
7
9
|
try {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
signal?.throwIfAborted();
|
|
11
|
+
// Follow symlinks, but reject devices/directories/FIFOs before opening them.
|
|
12
|
+
if (!(await stat(path)).isFile())
|
|
13
|
+
throw new Error(`PI WEB project config must be a regular file: ${path}`);
|
|
14
|
+
signal?.throwIfAborted();
|
|
15
|
+
// A path can change after stat. Nonblocking open prevents a replacement FIFO
|
|
16
|
+
// from occupying a worker; validate and read only this same descriptor.
|
|
17
|
+
const handle = await open(path, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
18
|
+
try {
|
|
19
|
+
signal?.throwIfAborted();
|
|
20
|
+
if (!(await handle.stat()).isFile())
|
|
21
|
+
throw new Error(`PI WEB project config must be a regular file: ${path}`);
|
|
22
|
+
signal?.throwIfAborted();
|
|
23
|
+
const content = await handle.readFile({ encoding: "utf8", signal });
|
|
24
|
+
signal?.throwIfAborted();
|
|
25
|
+
const parsed = JSON.parse(content);
|
|
26
|
+
if (!isRecord(parsed))
|
|
27
|
+
throw new Error(`PI WEB project config must be a JSON object: ${path}`);
|
|
28
|
+
return { path, exists: true, config: parseProjectPiWebConfig(parsed, path) };
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
await handle.close();
|
|
32
|
+
}
|
|
12
33
|
}
|
|
13
34
|
catch (error) {
|
|
35
|
+
signal?.throwIfAborted();
|
|
14
36
|
if (isNodeErrorWithCode(error, "ENOENT"))
|
|
15
37
|
return { path, exists: false, config: {} };
|
|
16
38
|
throw error;
|
|
@@ -39,8 +61,68 @@ function parseProjectPiWebConfig(value, path) {
|
|
|
39
61
|
...(value["pathAccess"] !== undefined ? { pathAccess: parsePathAccessConfig(value["pathAccess"], path) } : {}),
|
|
40
62
|
...(value["uploads"] !== undefined ? { uploads: parseUploadsConfig(value["uploads"], path) } : {}),
|
|
41
63
|
...(value["attachments"] !== undefined ? { attachments: parseAttachmentsConfig(value["attachments"], path) } : {}),
|
|
64
|
+
...(value["plugins"] !== undefined ? { plugins: parseProjectPlugins(value["plugins"], path) } : {}),
|
|
42
65
|
};
|
|
43
66
|
}
|
|
67
|
+
function parseProjectPlugins(value, path) {
|
|
68
|
+
if (!isRecord(value))
|
|
69
|
+
throw new Error(`PI WEB project config plugins must be an object: ${path}`);
|
|
70
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([pluginId, config]) => {
|
|
71
|
+
if (!isPiWebPluginId(pluginId))
|
|
72
|
+
throw new Error(`PI WEB project config plugin ids must match ${piWebPluginIdPattern.source}: ${path}`);
|
|
73
|
+
if (!isRecord(config))
|
|
74
|
+
throw new Error(`PI WEB project config plugin entries must be objects: ${path}`);
|
|
75
|
+
if (Object.hasOwn(config, "enabled")) {
|
|
76
|
+
throw new Error(`PI WEB project config plugins.${pluginId}.enabled is not supported; plugin activation is global: ${path}`);
|
|
77
|
+
}
|
|
78
|
+
const settings = config["settings"];
|
|
79
|
+
return [pluginId, Object.freeze(settings === undefined ? {} : {
|
|
80
|
+
settings: snapshotPluginSettings(settings, `PI WEB project config plugins.${pluginId}.settings: ${path}`),
|
|
81
|
+
})];
|
|
82
|
+
})));
|
|
83
|
+
}
|
|
84
|
+
/** Validates only JSON shape; setting meanings belong to the owning plugin. */
|
|
85
|
+
export function snapshotPluginSettings(value, label) {
|
|
86
|
+
if (!isPlainRecord(value))
|
|
87
|
+
throw new Error(`${label} must be a JSON object`);
|
|
88
|
+
return snapshotJsonRecord(value, new Set(), label);
|
|
89
|
+
}
|
|
90
|
+
function snapshotJsonRecord(value, ancestors, label) {
|
|
91
|
+
if (ancestors.has(value))
|
|
92
|
+
throw new Error(`${label} must not contain cycles`);
|
|
93
|
+
ancestors.add(value);
|
|
94
|
+
const output = Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshotJsonValue(item, ancestors, label)]));
|
|
95
|
+
ancestors.delete(value);
|
|
96
|
+
return Object.freeze(output);
|
|
97
|
+
}
|
|
98
|
+
function snapshotJsonValue(value, ancestors, label) {
|
|
99
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
100
|
+
return value;
|
|
101
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
102
|
+
return value;
|
|
103
|
+
if (Array.isArray(value)) {
|
|
104
|
+
if (ancestors.has(value))
|
|
105
|
+
throw new Error(`${label} must not contain cycles`);
|
|
106
|
+
ancestors.add(value);
|
|
107
|
+
const output = [];
|
|
108
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
109
|
+
if (!Object.hasOwn(value, index))
|
|
110
|
+
throw new Error(`${label} must not contain sparse arrays`);
|
|
111
|
+
output[index] = snapshotJsonValue(value[index], ancestors, label);
|
|
112
|
+
}
|
|
113
|
+
ancestors.delete(value);
|
|
114
|
+
return Object.freeze(output);
|
|
115
|
+
}
|
|
116
|
+
if (isPlainRecord(value))
|
|
117
|
+
return snapshotJsonRecord(value, ancestors, label);
|
|
118
|
+
throw new Error(`${label} must contain only JSON values`);
|
|
119
|
+
}
|
|
120
|
+
function isPlainRecord(value) {
|
|
121
|
+
if (!isRecord(value))
|
|
122
|
+
return false;
|
|
123
|
+
const prototype = Object.getPrototypeOf(value);
|
|
124
|
+
return prototype === Object.prototype || prototype === null;
|
|
125
|
+
}
|
|
44
126
|
function parseProjectConfigVersion(value, path) {
|
|
45
127
|
if (value !== 1)
|
|
46
128
|
throw new Error(`PI WEB project config version must be 1: ${path}`);
|