@chainingintention/pi-web-cn 1.202608.8 → 1.202608.10
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 +2 -0
- package/dist/cli.js +123 -72
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/{CodeViewer-CWKEeX0r.js → CodeViewer-DCl56K0Z.js} +2 -2
- package/dist/client/assets/{TerminalPanel-BmA7Ayzk.js → TerminalPanel-DCvoEjUF.js} +6 -6
- package/dist/client/assets/{index-D6j67Izj.js → index-CMvms5cF.js} +534 -512
- package/dist/client/index.html +1 -1
- package/dist/config.js +39 -18
- package/dist/config.js.map +1 -1
- package/dist/nativeServices/serviceAction.js +128 -0
- package/dist/nativeServices/serviceAction.js.map +1 -0
- package/dist/pi-web-plugins/git/browser/git-contract.js +108 -0
- package/dist/pi-web-plugins/git/browser/git-panel.js +609 -0
- package/dist/pi-web-plugins/git/browser/gitFileList.js +43 -0
- package/dist/pi-web-plugins/git/browser/gitFileShared.js +16 -0
- package/dist/pi-web-plugins/git/browser/gitFileTree.js +81 -0
- package/dist/pi-web-plugins/git/browser/gitFileViewPreference.js +35 -0
- package/dist/pi-web-plugins/git/browser/gitRoute.js +42 -0
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +10 -0
- package/dist/pi-web-plugins/git/browser/unifiedDiff.js +301 -0
- package/dist/pi-web-plugins/git/git-backend.js +433 -0
- package/dist/pi-web-plugins/git/package.json +15 -0
- package/dist/pi-web-plugins/git/server-plugin.js +176 -0
- package/dist/pi-web-plugins/info/infoInternals.js +1 -1
- package/dist/pi-web-plugins/info/pi-web-plugin.js +2 -2
- package/dist/pi-web-plugins/relays/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/relays/relayDiscovery.js +128 -23
- package/dist/pi-web-plugins/relays/relaysPanelElement.js +138 -16
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +1 -1
- package/dist/piWebVersionReport.js +63 -4
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +40 -18
- package/dist/pluginRecoveryCli.js +172 -0
- package/dist/pluginRecoveryCli.js.map +1 -0
- package/dist/server/activity/workspaceActivityService.js +7 -1
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +49 -23
- package/dist/server/app.js.map +1 -1
- package/dist/server/audit/managementAuditStore.js +4 -2
- package/dist/server/audit/managementAuditStore.js.map +1 -1
- package/dist/server/configRoutes.js +4 -5
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/machines/machineClient.js +23 -4
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +162 -8
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/managementEmbed.js +6 -0
- package/dist/server/managementEmbed.js.map +1 -1
- package/dist/server/piWebPluginCatalog.js +441 -0
- package/dist/server/piWebPluginCatalog.js.map +1 -0
- package/dist/server/piWebPluginLifecycle.js +162 -0
- package/dist/server/piWebPluginLifecycle.js.map +1 -0
- package/dist/server/piWebPluginService.js +55 -7
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/plugins/pluginBackendProxyRoutes.js +80 -0
- package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -0
- package/dist/server/plugins/serverPluginExec.js +184 -0
- package/dist/server/plugins/serverPluginExec.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +480 -0
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -0
- package/dist/server/realtime/sessionEventHub.js +13 -0
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/realtime/sessionEventScope.js +8 -1
- package/dist/server/realtime/sessionEventScope.js.map +1 -1
- package/dist/server/requestCancellation.js +28 -0
- package/dist/server/requestCancellation.js.map +1 -0
- package/dist/server/sessiond/agentProcessEnvironment.js +61 -0
- package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -0
- package/dist/server/sessiond/managementProjectResolver.js +16 -0
- package/dist/server/sessiond/managementProjectResolver.js.map +1 -0
- package/dist/server/sessiond/pluginBackendRoutes.js +78 -0
- package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionServiceDependencies.js +0 -1
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
- package/dist/server/sessiond/sessiondStateOwnership.js +139 -0
- package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -0
- package/dist/server/sessiond/workspaceCatalogRoutes.js +38 -0
- package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -0
- package/dist/server/sessiond/workspaceRemovalRoutes.js +56 -0
- package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -0
- package/dist/server/sessiond.js +98 -11
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/authService.js +15 -51
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/dockerEnvironmentFacts.js +181 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
- package/dist/server/sessions/managementAgentTools.js +12 -4
- package/dist/server/sessions/managementAgentTools.js.map +1 -1
- package/dist/server/sessions/managementSandbox.js +5 -5
- package/dist/server/sessions/managementSandbox.js.map +1 -1
- package/dist/server/sessions/piSessionManagerGateway.js +129 -2
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +268 -119
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +15 -1
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionEnvironmentFacts.js +48 -0
- package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -0
- package/dist/server/sessions/sessionFileFormat.js +27 -0
- package/dist/server/sessions/sessionFileFormat.js.map +1 -0
- package/dist/server/sessions/sessionFileHeader.js +82 -23
- package/dist/server/sessions/sessionFileHeader.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +16 -2
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionSummaryScanner.js +491 -0
- package/dist/server/sessions/sessionSummaryScanner.js.map +1 -0
- package/dist/server/sessions/spawnSubsessionTool.js +4 -8
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/spawnTargetResolver.js +2 -2
- package/dist/server/sessions/spawnTargetResolver.js.map +1 -1
- package/dist/server/status/machineStatusRoutes.js +10 -0
- package/dist/server/status/machineStatusRoutes.js.map +1 -0
- package/dist/server/status/machineStatusService.js +115 -0
- package/dist/server/status/machineStatusService.js.map +1 -0
- package/dist/server/status/workspaceAttribution.js +71 -0
- package/dist/server/status/workspaceAttribution.js.map +1 -0
- package/dist/server/terminalProxyRoutes.js.map +1 -1
- package/dist/server/workbench/skillSync.js +9 -1
- package/dist/server/workbench/skillSync.js.map +1 -1
- package/dist/server/workspaceExplorerRoutes.js +12 -25
- package/dist/server/workspaceExplorerRoutes.js.map +1 -1
- package/dist/server/workspaces/effectivePathAccess.js +3 -2
- package/dist/server/workspaces/effectivePathAccess.js.map +1 -1
- package/dist/server/workspaces/fileContentService.js +17 -9
- package/dist/server/workspaces/fileContentService.js.map +1 -1
- package/dist/server/workspaces/fileOperationService.js +4 -17
- package/dist/server/workspaces/fileOperationService.js.map +1 -1
- package/dist/server/workspaces/filePreviewResponseHeaders.js +18 -0
- package/dist/server/workspaces/filePreviewResponseHeaders.js.map +1 -0
- package/dist/server/workspaces/filePreviewResponsePolicy.js +55 -0
- package/dist/server/workspaces/filePreviewResponsePolicy.js.map +1 -0
- package/dist/server/workspaces/filePreviewService.js +81 -0
- package/dist/server/workspaces/filePreviewService.js.map +1 -0
- package/dist/server/workspaces/projectWorkspaceCwds.js +2 -2
- package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +416 -0
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -0
- package/dist/server/workspaces/workspaceCatalog.js +87 -0
- package/dist/server/workspaces/workspaceCatalog.js.map +1 -0
- package/dist/server/workspaces/workspaceContext.js +5 -5
- package/dist/server/workspaces/workspaceContext.js.map +1 -1
- package/dist/server/workspaces/workspaceDeletionRoutes.js +114 -99
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
- package/dist/server/workspaces/workspaceProviderRegistry.js +650 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -0
- package/dist/server/workspaces/workspaceRemovalService.js +258 -0
- package/dist/server/workspaces/workspaceRemovalService.js.map +1 -0
- package/dist/server/workspaces/workspaceRouteContext.js +2 -1
- package/dist/server/workspaces/workspaceRouteContext.js.map +1 -1
- package/dist/server/workspaces/workspaceRouteErrors.js +7 -0
- package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -0
- package/dist/server/workspaces/worktreePreRemoveHook.js +39 -0
- package/dist/server/workspaces/worktreePreRemoveHook.js.map +1 -0
- package/dist/server-plugin-api.d.ts +115 -0
- package/dist/server-plugin-api.js +2 -0
- package/dist/server-plugin-api.js.map +1 -0
- package/dist/serverPluginRecovery.js +138 -0
- package/dist/serverPluginRecovery.js.map +1 -0
- package/dist/sessiond/config.js +6 -0
- package/dist/sessiond/config.js.map +1 -1
- package/dist/sessiond/sessionDaemonClient.js +7 -6
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/apiTypes.js +2 -0
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +2 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +28 -2
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/machineStatus.js +60 -0
- package/dist/shared/machineStatus.js.map +1 -0
- package/dist/shared/pluginApiTypes.d.ts +155 -0
- package/dist/shared/pluginBackendProtocol.js +110 -0
- package/dist/shared/pluginBackendProtocol.js.map +1 -0
- package/dist/shared/pluginIds.js +3 -0
- package/dist/shared/pluginIds.js.map +1 -1
- package/dist/shared/pluginRecoveryCommands.js +14 -0
- package/dist/shared/pluginRecoveryCommands.js.map +1 -0
- package/dist/shared/workspaceFiles.js +41 -2
- package/dist/shared/workspaceFiles.js.map +1 -1
- package/dist/shared/workspaceRemovalProtocol.js +24 -0
- package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
- package/docs/config.md +12 -5
- package/docs/plugins.md +15 -21
- package/package.json +27 -8
- package/server-plugin-api.d.ts +1 -0
- package/skills/README.md +12 -0
- package/skills/relay/SKILL.md +115 -0
- package/dist/client/assets/UnifiedDiffViewer-2DACwxFY.js +0 -39
- package/dist/plugin-api/unstable.d.ts +0 -22
- package/dist/server/sessions/parentSessionLocator.js +0 -75
- package/dist/server/sessions/parentSessionLocator.js.map +0 -1
- package/dist/server/workspaces/imagePreviewService.js +0 -40
- package/dist/server/workspaces/imagePreviewService.js.map +0 -1
- package/dist/shared/apiTypes.d.ts +0 -1323
- package/dist/shared/thinkingLevels.d.ts +0 -29
- package/plugin-api/unstable.d.ts +0 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** Maximum UTF-8 JSON input accepted from a browser plugin. */
|
|
2
|
+
export const PLUGIN_BACKEND_JSON_MAX_BYTES = 256 * 1024;
|
|
3
|
+
/** Bounded result allowance demonstrated by Git's existing 2 MiB command-output limit. */
|
|
4
|
+
export const PLUGIN_BACKEND_RESPONSE_JSON_MAX_BYTES = 8 * 1024 * 1024;
|
|
5
|
+
/** Envelope allowance for the active backend revision and JSON field names. */
|
|
6
|
+
export const PLUGIN_BACKEND_REQUEST_BODY_MAX_BYTES = PLUGIN_BACKEND_JSON_MAX_BYTES + 4 * 1024;
|
|
7
|
+
/** Allowance for an attributed error envelope around a bounded result. */
|
|
8
|
+
export const PLUGIN_BACKEND_RESPONSE_BODY_MAX_BYTES = PLUGIN_BACKEND_RESPONSE_JSON_MAX_BYTES + 4 * 1024;
|
|
9
|
+
/** Bounded provider callback deadline inside sessiond. */
|
|
10
|
+
export const PLUGIN_BACKEND_REQUEST_TIMEOUT_MS = 10_000;
|
|
11
|
+
/** End-to-end sessiond deadline, including owner re-resolution and validation. */
|
|
12
|
+
export const PLUGIN_BACKEND_DISPATCH_TIMEOUT_MS = 25_000;
|
|
13
|
+
/** Remote gateway deadline, including workspace owner re-resolution. */
|
|
14
|
+
export const PLUGIN_BACKEND_FEDERATION_TIMEOUT_MS = 30_000;
|
|
15
|
+
export const PLUGIN_BACKEND_OPERATION_MAX_LENGTH = 128;
|
|
16
|
+
export const PLUGIN_BACKEND_REVISION_MAX_LENGTH = 512;
|
|
17
|
+
const OPERATION_PATTERN = /^[a-z][a-z0-9.-]*$/u;
|
|
18
|
+
const MAX_JSON_DEPTH = 64;
|
|
19
|
+
export function isPluginBackendOperation(value) {
|
|
20
|
+
return value.length <= PLUGIN_BACKEND_OPERATION_MAX_LENGTH && OPERATION_PATTERN.test(value);
|
|
21
|
+
}
|
|
22
|
+
export function requirePluginBackendOperation(value) {
|
|
23
|
+
if (!isPluginBackendOperation(value)) {
|
|
24
|
+
throw new Error(`Plugin backend operation must match ${String(OPERATION_PATTERN)} and be at most ${String(PLUGIN_BACKEND_OPERATION_MAX_LENGTH)} characters`);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
export function requirePluginBackendRevision(value) {
|
|
29
|
+
if (typeof value !== "string" || value === "" || value.length > PLUGIN_BACKEND_REVISION_MAX_LENGTH) {
|
|
30
|
+
throw new Error(`Plugin backend revision must be a non-empty string of at most ${String(PLUGIN_BACKEND_REVISION_MAX_LENGTH)} characters`);
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
/** Clone and freeze a runtime value after enforcing the JSON and byte contract. */
|
|
35
|
+
export function cloneBoundedPluginBackendJson(value, label, maxBytes = PLUGIN_BACKEND_JSON_MAX_BYTES) {
|
|
36
|
+
const cloned = cloneJsonValue(value, new Set(), label, 0);
|
|
37
|
+
const serialized = JSON.stringify(cloned);
|
|
38
|
+
if (utf8ByteLength(serialized) > maxBytes) {
|
|
39
|
+
throw new Error(`${label} exceeds the ${String(maxBytes)} byte limit`);
|
|
40
|
+
}
|
|
41
|
+
return cloned;
|
|
42
|
+
}
|
|
43
|
+
export function serializeBoundedPluginBackendJson(value, label, maxBytes = PLUGIN_BACKEND_JSON_MAX_BYTES) {
|
|
44
|
+
return JSON.stringify(cloneBoundedPluginBackendJson(value, label, maxBytes));
|
|
45
|
+
}
|
|
46
|
+
export function parseBoundedPluginBackendJson(text, label, maxBytes = PLUGIN_BACKEND_JSON_MAX_BYTES) {
|
|
47
|
+
if (utf8ByteLength(text) > maxBytes) {
|
|
48
|
+
throw new Error(`${label} exceeds the ${String(maxBytes)} byte limit`);
|
|
49
|
+
}
|
|
50
|
+
let value;
|
|
51
|
+
try {
|
|
52
|
+
value = JSON.parse(text);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw new Error(`${label} must be valid JSON`, { cause: error });
|
|
56
|
+
}
|
|
57
|
+
return cloneBoundedPluginBackendJson(value, label, maxBytes);
|
|
58
|
+
}
|
|
59
|
+
export function parsePluginBackendRequestEnvelope(value) {
|
|
60
|
+
if (!isPlainRecord(value))
|
|
61
|
+
throw new Error("Plugin backend request body must be an object");
|
|
62
|
+
return Object.freeze({
|
|
63
|
+
revision: requirePluginBackendRevision(value["revision"]),
|
|
64
|
+
input: cloneBoundedPluginBackendJson(value["input"], "Plugin backend request input"),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export function utf8ByteLength(value) {
|
|
68
|
+
return new TextEncoder().encode(value).byteLength;
|
|
69
|
+
}
|
|
70
|
+
function cloneJsonValue(value, ancestors, label, depth) {
|
|
71
|
+
if (depth > MAX_JSON_DEPTH)
|
|
72
|
+
throw new Error(`${label} exceeds the maximum JSON depth of ${String(MAX_JSON_DEPTH)}`);
|
|
73
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
74
|
+
return value;
|
|
75
|
+
if (typeof value === "number") {
|
|
76
|
+
if (!Number.isFinite(value))
|
|
77
|
+
throw new Error(`${label} must contain only finite JSON numbers`);
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
if (ancestors.has(value))
|
|
82
|
+
throw new Error(`${label} must not contain cycles`);
|
|
83
|
+
ancestors.add(value);
|
|
84
|
+
const output = value.map((child) => cloneJsonValue(child, ancestors, label, depth + 1));
|
|
85
|
+
ancestors.delete(value);
|
|
86
|
+
Object.freeze(output);
|
|
87
|
+
return output;
|
|
88
|
+
}
|
|
89
|
+
if (!isPlainRecord(value))
|
|
90
|
+
throw new Error(`${label} must contain only JSON values`);
|
|
91
|
+
if (ancestors.has(value))
|
|
92
|
+
throw new Error(`${label} must not contain cycles`);
|
|
93
|
+
ancestors.add(value);
|
|
94
|
+
const output = {};
|
|
95
|
+
for (const [key, child] of Object.entries(value)) {
|
|
96
|
+
defineJsonProperty(output, key, cloneJsonValue(child, ancestors, label, depth + 1));
|
|
97
|
+
}
|
|
98
|
+
ancestors.delete(value);
|
|
99
|
+
return Object.freeze(output);
|
|
100
|
+
}
|
|
101
|
+
function defineJsonProperty(record, key, value) {
|
|
102
|
+
Object.defineProperty(record, key, { value, enumerable: true, configurable: true, writable: true });
|
|
103
|
+
}
|
|
104
|
+
function isPlainRecord(value) {
|
|
105
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
106
|
+
return false;
|
|
107
|
+
const prototype = Object.getPrototypeOf(value);
|
|
108
|
+
return prototype === Object.prototype || prototype === null;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=pluginBackendProtocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pluginBackendProtocol.js","sourceRoot":"","sources":["../../src/shared/pluginBackendProtocol.ts"],"names":[],"mappings":"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"}
|
package/dist/shared/pluginIds.js
CHANGED
|
@@ -2,4 +2,7 @@ export const piWebPluginIdPattern = /^[a-z][a-z0-9.-]*$/u;
|
|
|
2
2
|
export function isPiWebPluginId(value) {
|
|
3
3
|
return piWebPluginIdPattern.test(value);
|
|
4
4
|
}
|
|
5
|
+
export function isReservedPiWebPluginId(value) {
|
|
6
|
+
return value === "core";
|
|
7
|
+
}
|
|
5
8
|
//# sourceMappingURL=pluginIds.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pluginIds.js","sourceRoot":"","sources":["../../src/shared/pluginIds.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,qBAAqB,CAAC;AAE1D,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC1C,CAAC"}
|
|
1
|
+
{"version":3,"file":"pluginIds.js","sourceRoot":"","sources":["../../src/shared/pluginIds.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,qBAAqB,CAAC;AAE1D,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAa;IACnD,OAAO,KAAK,KAAK,MAAM,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { isPiWebPluginId } from "./pluginIds.js";
|
|
2
|
+
/** Secret-free commands accepted by the offline recovery CLI. */
|
|
3
|
+
export const PI_WEB_PLUGIN_RECOVERY_COMMANDS = Object.freeze({
|
|
4
|
+
showSafeStart: "pi-web plugins safe-start show",
|
|
5
|
+
bundledOnly: "pi-web plugins safe-start set bundled-only --restart",
|
|
6
|
+
noServerPlugins: "pi-web plugins safe-start set none --restart",
|
|
7
|
+
clearSafeStart: "pi-web plugins safe-start clear --restart",
|
|
8
|
+
});
|
|
9
|
+
export function pluginDisableRecoveryCommand(pluginId) {
|
|
10
|
+
if (!isPiWebPluginId(pluginId))
|
|
11
|
+
throw new Error(`Invalid PI WEB plugin id: ${pluginId}`);
|
|
12
|
+
return `pi-web plugins disable ${pluginId} --restart`;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=pluginRecoveryCommands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pluginRecoveryCommands.js","sourceRoot":"","sources":["../../src/shared/pluginRecoveryCommands.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,iEAAiE;AACjE,MAAM,CAAC,MAAM,+BAA+B,GAA0C,MAAM,CAAC,MAAM,CAAC;IAClG,aAAa,EAAE,gCAAgC;IAC/C,WAAW,EAAE,sDAAsD;IACnE,eAAe,EAAE,8CAA8C;IAC/D,cAAc,EAAE,2CAA2C;CAC5D,CAAC,CAAC;AAEH,MAAM,UAAU,4BAA4B,CAAC,QAAgB;IAC3D,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,EAAE,CAAC,CAAC;IACzF,OAAO,0BAA0B,QAAQ,YAAY,CAAC;AACxD,CAAC"}
|
|
@@ -1,3 +1,42 @@
|
|
|
1
|
-
export const
|
|
2
|
-
export const
|
|
1
|
+
export const MAX_INLINE_PREVIEW_BYTES = 10 * 1024 * 1024;
|
|
2
|
+
export const MAX_INLINE_PREVIEW_LABEL = "10 MB";
|
|
3
|
+
export const MAX_WORKSPACE_FILE_CONTENT_BYTES = 512 * 1024;
|
|
4
|
+
// Extension classification is shared by JSON source reads, streamed previews,
|
|
5
|
+
// and proxy response policy. Keep this an allowlist: only classifications with
|
|
6
|
+
// a preview MIME type may be served as browser-rendered bytes.
|
|
7
|
+
//
|
|
8
|
+
// `source` decides what a JSON file read carries: "text" formats keep capped
|
|
9
|
+
// literal UTF-8 source so the viewer can offer Raw mode, while "stream" formats
|
|
10
|
+
// stay out of JSON and are only ever served as preview bytes.
|
|
11
|
+
const WORKSPACE_FILE_CLASSIFICATIONS = {
|
|
12
|
+
".avif": { mediaType: "image", source: "stream", previewMimeType: "image/avif" },
|
|
13
|
+
".bmp": { mediaType: "image", source: "stream", previewMimeType: "image/bmp" },
|
|
14
|
+
".gif": { mediaType: "image", source: "stream", previewMimeType: "image/gif" },
|
|
15
|
+
".ico": { mediaType: "image", source: "stream", previewMimeType: "image/x-icon" },
|
|
16
|
+
".jpeg": { mediaType: "image", source: "stream", previewMimeType: "image/jpeg" },
|
|
17
|
+
".jpg": { mediaType: "image", source: "stream", previewMimeType: "image/jpeg" },
|
|
18
|
+
".png": { mediaType: "image", source: "stream", previewMimeType: "image/png" },
|
|
19
|
+
// SVG is markup: it previews as an image but also has readable source, so it
|
|
20
|
+
// keeps literal text for Raw mode.
|
|
21
|
+
".svg": { mediaType: "image", source: "text", previewMimeType: "image/svg+xml" },
|
|
22
|
+
".webp": { mediaType: "image", source: "stream", previewMimeType: "image/webp" },
|
|
23
|
+
".htm": { mediaType: "html", source: "text", previewMimeType: "text/html; charset=utf-8" },
|
|
24
|
+
".html": { mediaType: "html", source: "text", previewMimeType: "text/html; charset=utf-8" },
|
|
25
|
+
".pdf": { mediaType: "pdf", source: "stream", previewMimeType: "application/pdf" },
|
|
26
|
+
".md": { mediaType: "markdown", source: "text" },
|
|
27
|
+
".markdown": { mediaType: "markdown", source: "text" },
|
|
28
|
+
};
|
|
29
|
+
export function classifyWorkspaceFile(path) {
|
|
30
|
+
const slashIndex = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
31
|
+
const dotIndex = path.lastIndexOf(".");
|
|
32
|
+
if (dotIndex <= slashIndex)
|
|
33
|
+
return undefined;
|
|
34
|
+
const extension = path.slice(dotIndex).toLowerCase();
|
|
35
|
+
return WORKSPACE_FILE_CLASSIFICATIONS[extension];
|
|
36
|
+
}
|
|
37
|
+
/** Return the leaf filename for either POSIX or Windows-style workspace paths. */
|
|
38
|
+
export function workspaceFileName(path) {
|
|
39
|
+
const separatorIndex = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
40
|
+
return path.slice(separatorIndex + 1);
|
|
41
|
+
}
|
|
3
42
|
//# sourceMappingURL=workspaceFiles.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaceFiles.js","sourceRoot":"","sources":["../../src/shared/workspaceFiles.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"workspaceFiles.js","sourceRoot":"","sources":["../../src/shared/workspaceFiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Small JSON request carrying the host-issued confirmation precondition. */
|
|
2
|
+
export const WORKSPACE_REMOVAL_REQUEST_BODY_MAX_BYTES = 4 * 1024;
|
|
3
|
+
/** One sessiond-owned deadline across owner resolution, validation, and planning. */
|
|
4
|
+
export const WORKSPACE_REMOVAL_OPERATION_TIMEOUT_MS = 25_000;
|
|
5
|
+
/** Leaves time for cancellation to reach remote web/sessiond before gateway timeout. */
|
|
6
|
+
export const WORKSPACE_REMOVAL_FEDERATION_TIMEOUT_MS = 30_000;
|
|
7
|
+
export const WORKSPACE_REMOVAL_PRECONDITION_MAX_LENGTH = 256;
|
|
8
|
+
export function parseWorkspaceRemovalRequest(value) {
|
|
9
|
+
if (!isRecord(value))
|
|
10
|
+
throw new Error("Workspace removal request must be an object");
|
|
11
|
+
return { precondition: requireWorkspaceRemovalPrecondition(value["precondition"]) };
|
|
12
|
+
}
|
|
13
|
+
export function requireWorkspaceRemovalPrecondition(value) {
|
|
14
|
+
if (typeof value !== "string"
|
|
15
|
+
|| value === ""
|
|
16
|
+
|| value.length > WORKSPACE_REMOVAL_PRECONDITION_MAX_LENGTH) {
|
|
17
|
+
throw new Error(`Workspace removal precondition must be a non-empty string of at most ${String(WORKSPACE_REMOVAL_PRECONDITION_MAX_LENGTH)} characters`);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=workspaceRemovalProtocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspaceRemovalProtocol.js","sourceRoot":"","sources":["../../src/shared/workspaceRemovalProtocol.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,wCAAwC,GAAG,CAAC,GAAG,IAAI,CAAC;AACjE,qFAAqF;AACrF,MAAM,CAAC,MAAM,sCAAsC,GAAG,MAAM,CAAC;AAC7D,wFAAwF;AACxF,MAAM,CAAC,MAAM,uCAAuC,GAAG,MAAM,CAAC;AAC9D,MAAM,CAAC,MAAM,yCAAyC,GAAG,GAAG,CAAC;AAE7D,MAAM,UAAU,4BAA4B,CAAC,KAAc;IACzD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACrF,OAAO,EAAE,YAAY,EAAE,mCAAmC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;AACtF,CAAC;AAED,MAAM,UAAU,mCAAmC,CAAC,KAAc;IAChE,IACE,OAAO,KAAK,KAAK,QAAQ;WACtB,KAAK,KAAK,EAAE;WACZ,KAAK,CAAC,MAAM,GAAG,yCAAyC,EAC3D,CAAC;QACD,MAAM,IAAI,KAAK,CACb,wEAAwE,MAAM,CAAC,yCAAyC,CAAC,aAAa,CACvI,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,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"}
|
package/docs/config.md
CHANGED
|
@@ -40,7 +40,7 @@ defaults → global config file → environment overrides
|
|
|
40
40
|
|
|
41
41
|
Supported project-local settings are then applied for that project's workspaces. For upload defaults, `<project>/.pi-web/config.json` overrides the global value.
|
|
42
42
|
|
|
43
|
-
Environment overrides include `PI_WEB_HOST`, `PI_WEB_PORT` / `PORT`, `PI_WEB_ALLOWED_HOSTS`, `PI_WEB_MAX_UPLOAD_BYTES`, `PI_WEB_AGENT_COMMAND`, `PI_WEB_AGENT_DIR`, `PI_WEB_AGENT_SESSION_DIR`, `PI_CODING_AGENT_DIR` / `PI_CODING_AGENT_SESSION_DIR`
|
|
43
|
+
Environment overrides include `PI_WEB_HOST`, `PI_WEB_PORT` / `PORT`, `PI_WEB_ALLOWED_HOSTS`, `PI_WEB_MAX_UPLOAD_BYTES`, `PI_WEB_AGENT_COMMAND`, `PI_WEB_AGENT_DIR`, `PI_WEB_AGENT_SESSION_DIR`, `PI_CODING_AGENT_DIR` / `PI_CODING_AGENT_SESSION_DIR`, `PI_WEB_ENVIRONMENT_FACTS`, `PI_WEB_SPAWN_SESSIONS`, `PI_WEB_SUBSESSIONS`, and `PI_WEB_ASK_USER`.
|
|
44
44
|
|
|
45
45
|
Process restarts depend on the key:
|
|
46
46
|
|
|
@@ -72,7 +72,7 @@ Process restarts depend on the key:
|
|
|
72
72
|
"dir": "~/agent-profiles/research"
|
|
73
73
|
},
|
|
74
74
|
"spawnSessions": true,
|
|
75
|
-
"subsessions":
|
|
75
|
+
"subsessions": true,
|
|
76
76
|
"askUser": true,
|
|
77
77
|
"extensionDialogsTimeoutMs": 300000,
|
|
78
78
|
"workbenchIntegration": {
|
|
@@ -147,8 +147,9 @@ Rows with JSON key `—` are runtime-only environment variables, not config-file
|
|
|
147
147
|
| Companion CLI command | `agent.command` | `PI_WEB_AGENT_COMMAND` | Global/session daemon | Not supported locally | Restart session daemon on that machine; affects doctor/status/update checks |
|
|
148
148
|
| Agent profile state directory | `agent.dir` | `PI_WEB_AGENT_DIR` (`PI_CODING_AGENT_DIR` for Pi compatibility) | Global/session daemon | Not supported locally | Restart session daemon on that machine; affects auth, models, settings, sessions, Pi packages, and Pi-package-backed PI WEB plugins |
|
|
149
149
|
| Agent can spawn sessions | `spawnSessions` | `PI_WEB_SPAWN_SESSIONS` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
|
|
150
|
-
| Tracked subsessions
|
|
150
|
+
| Tracked subsessions | `subsessions` | `PI_WEB_SUBSESSIONS` | Global/session daemon | Not supported locally; also requires `spawnSessions` | Restart session daemon on that machine |
|
|
151
151
|
| Agent can post question forms | `askUser` | `PI_WEB_ASK_USER` | Global/session daemon | Not supported locally | Restart session daemon on that machine |
|
|
152
|
+
| Ordinary-session environment facts | `environmentFacts` | `PI_WEB_ENVIRONMENT_FACTS` | Global/session daemon | Not supported locally | Restart session daemon on that machine; affects newly started sessions |
|
|
152
153
|
| 扩展对话框自动取消超时 | `extensionDialogsTimeoutMs` | — | 全局/会话守护进程 | 不支持项目级配置 | 重启对应机器上的会话守护进程 |
|
|
153
154
|
| Workbench/MCP integration | `workbenchIntegration.*` | `PI_WEB_WORKBENCH_URL`, `PI_WEB_MCP_URL`, `PI_WEB_WORKBENCH_TIMEOUT_MS`, `PI_WEB_MCP_TIMEOUT_MS`, `PI_WEB_SKILL_BUNDLE_MAX_BYTES`, `PI_WEB_SKILL_FILE_MAX_BYTES`, `PI_WEB_SKILL_FILE_COUNT_MAX` | Web/API + session daemon | Not supported locally | Restart web/API and session daemon; both must match |
|
|
154
155
|
| Ordinary tool audit | `auditLog.normalMode.*` | — | Global/session daemon | Not supported locally | Restart session daemon |
|
|
@@ -264,6 +265,8 @@ The per-request size limit is still controlled by `maxUploadBytes` / `PI_WEB_MAX
|
|
|
264
265
|
|
|
265
266
|
### Pi-compatible agent profile and companion CLI
|
|
266
267
|
|
|
268
|
+
PI WEB embeds Pi Coding Agent `0.84.x` or newer compatible runtime packages. The session daemon owns one resolved Pi state directory and refuses to start a second daemon against the same `PI_WEB_DATA_DIR`; use distinct data and endpoint variables for another instance.
|
|
269
|
+
|
|
267
270
|
`agent.command` selects the Pi-compatible companion CLI used by `pi-web doctor` and, when it can be generated safely, package-managed update commands. It defaults to `pi`. This setting does **not** replace the embedded runtime: every session continues to use PI WEB's bundled Pi SDK.
|
|
268
271
|
|
|
269
272
|
`agent.dir` selects the Pi-compatible state profile used for auth providers, models, settings, sessions, Pi packages, and Pi-package-backed PI WEB plugin discovery. It defaults to `~/.pi/agent` only for a canonical Pi companion command. The directory must use the data layout supported by the bundled Pi SDK; PI WEB does not load or convert incompatible fork formats, migrate profile data, or repartition PI WEB-managed archives when the profile changes.
|
|
@@ -279,7 +282,9 @@ The per-request size limit is still controlled by `maxUploadBytes` / `PI_WEB_MAX
|
|
|
279
282
|
|
|
280
283
|
An alternate command always requires an explicit state directory. The command must be a safe bare executable name such as `pi-lab` or a host-absolute executable path such as `/opt/pi/bin/pi`; relative paths, shell expressions, and launcher strings are rejected. The state directory must be host-absolute or start with `~`. In a federated save, the gateway transports Unix and Windows absolute paths without reinterpreting them, and the target machine validates and returns the persisted profile.
|
|
281
284
|
|
|
282
|
-
Environment variables take precedence over the config file.
|
|
285
|
+
Environment variables take precedence over the config file. For the state directory, precedence is `PI_WEB_AGENT_DIR` > `PI_CODING_AGENT_DIR` > `agent.dir` > the Pi default. For session storage, `PI_WEB_AGENT_SESSION_DIR` remains first for compatibility, followed by `PI_CODING_AGENT_SESSION_DIR`. The resolved directory is exported as `PI_CODING_AGENT_DIR` to embedded Pi processes.
|
|
286
|
+
|
|
287
|
+
`environmentFacts` controls factual system-prompt sections appended only to ordinary sessions. These describe PI WEB session nesting and, when the daemon runs in Docker, the observed container filesystem, persistent mounts, host access, and development mount shape. `PI_WEB_ENVIRONMENT_FACTS` takes precedence and accepts `0|1|true|false`; the default is enabled. Set it to `false` to disable all such sections, restart the session daemon, and start a new session. Existing sessions retain the prompt they started with. Management-embed sessions do not receive these ordinary-session facts.
|
|
283
288
|
|
|
284
289
|
The session daemon resolves the persisted desired values plus its environment once at startup. That secret-free active profile stays fixed for the daemon lifetime. **Settings → Session daemon** saves command and directory together as desired configuration and shows whether the profile is active, needs a restart, or cannot be compared. Until the daemon restarts, sessions, Pi package operations, Pi-package-backed PI WEB plugin discovery, status/install detection, and update planning continue to use the daemon-owned active profile; a web/API restart recovers that same active profile instead of applying the newly saved values.
|
|
285
290
|
|
|
@@ -335,10 +340,12 @@ To turn the background refresh off entirely, set `PI_WEB_OFFLINE` or `PI_OFFLINE
|
|
|
335
340
|
|
|
336
341
|
`spawnSessions` controls whether agents receive the `spawn_session` tool. It defaults to `true`; set it to `false` if you do not want an agent to start independent PI WEB sessions.
|
|
337
342
|
|
|
338
|
-
`subsessions`
|
|
343
|
+
`subsessions` controls whether agents receive the tracked-subsession tools: `spawn_subsession`, `list_subsessions`, `check_subsession`, `read_subsession`, and `yield_to_subsessions`. It defaults to `true` and also requires `spawnSessions` to be enabled.
|
|
339
344
|
|
|
340
345
|
Tracked subsessions are join-oriented. Calling `spawn_subsession` returns immediately, so the parent can continue independent work while the child runs. Work whose result the parent does not need to join belongs in the fire-and-forget `spawn_session` tool instead.
|
|
341
346
|
|
|
347
|
+
A tracked subsession always runs in the spawning session's working directory, so it stays in that workspace's session tree next to its parent. `spawn_subsession` takes no `cwd`. For work in another workspace, use `spawn_session` to create an independent session there.
|
|
348
|
+
|
|
342
349
|
At a join point, after finishing its independent work, the parent calls `yield_to_subsessions` alone as the final action in its tool batch. Pi ends a tool batch early only when every result in that batch is terminating. If any tracked child is still working, the action ends the current agent run so the parent becomes idle. If none are working, it does not end the run and clearly reports that there is nothing to wait for.
|
|
343
350
|
|
|
344
351
|
A completion notice wakes an idle parent or queues behind in-flight work. Each notice lists any other tracked children still working, so the parent can continue work or call `yield_to_subsessions` again at the next join point. Further notices arrive automatically; do not poll. The notice includes the child's final output when it fits. If that output is too long, PI WEB omits it entirely instead of adding a truncated duplicate to the parent's context and directs the parent to retrieve it with `check_subsession`.
|
package/docs/plugins.md
CHANGED
|
@@ -133,7 +133,7 @@ Module shape excerpt:
|
|
|
133
133
|
|
|
134
134
|
```js
|
|
135
135
|
export default {
|
|
136
|
-
apiVersion:
|
|
136
|
+
apiVersion: 2,
|
|
137
137
|
name: "Info Plugin",
|
|
138
138
|
activate: ({ html, svg }) => ({
|
|
139
139
|
contributions: {
|
|
@@ -301,6 +301,8 @@ Review task configs before running them, especially in shared projects. Workspac
|
|
|
301
301
|
|
|
302
302
|
A relay is a directory of markdown notes under `.pi-web/relays/<name>/` in the workspace root — the convention used by the Relay method for chaining agent sessions. The tab lists each relay's documents with `status.md`, `charter.md`, and `log.md` first (in that order), followed by any other files alphabetically, and opens `status.md` by default. Markdown documents render as sanitized HTML; other files render as preformatted text, and binary files have no preview. Truncated documents show a notice, and **Refresh** re-scans the workspace and reloads the open document.
|
|
303
303
|
|
|
304
|
+
Documents in subfolders are listed too. Folders appear as chips in the document strip, and expanding one inserts its files inline after it. Expanding a folder collapses its siblings at the same level, while collapsing the folder that contains the open document keeps the selection and highlights that folder. Relay trees deeper than five levels, larger than 200 documents, or containing more than 50 folders are listed partially with a notice.
|
|
305
|
+
|
|
304
306
|
With several relays, a picker pre-selects the most recently modified one; a single relay opens directly. A workspace without `.pi-web/relays/` shows an empty state explaining the convention. The tab never creates, edits, or deletes relay files.
|
|
305
307
|
|
|
306
308
|
Relays is enabled by default. To hide it, disable `relays` in **Settings → PI WEB plugins** or set:
|
|
@@ -409,14 +411,15 @@ The entry module must default-export a plugin object:
|
|
|
409
411
|
|
|
410
412
|
```ts
|
|
411
413
|
interface PiWebPlugin {
|
|
412
|
-
apiVersion:
|
|
414
|
+
apiVersion: 2;
|
|
413
415
|
name: string;
|
|
414
416
|
activate: (context: PluginActivationContext) => PluginActivationResult;
|
|
415
417
|
}
|
|
416
418
|
|
|
417
419
|
interface PluginActivationContext {
|
|
418
|
-
apiVersion:
|
|
420
|
+
apiVersion: 2;
|
|
419
421
|
pluginId: string;
|
|
422
|
+
runtimePluginId?: string;
|
|
420
423
|
html: typeof import("lit").html;
|
|
421
424
|
svg: typeof import("lit").svg;
|
|
422
425
|
}
|
|
@@ -430,7 +433,7 @@ Example:
|
|
|
430
433
|
|
|
431
434
|
```js
|
|
432
435
|
export default {
|
|
433
|
-
apiVersion:
|
|
436
|
+
apiVersion: 2,
|
|
434
437
|
name: "My Plugin",
|
|
435
438
|
activate: ({ pluginId, html }) => ({
|
|
436
439
|
contributions: {
|
|
@@ -444,6 +447,8 @@ export default {
|
|
|
444
447
|
|
|
445
448
|
`activate()` is called once when the UI loads the plugin. Keep it cheap: define contributions there, but move expensive or async work into actions, custom elements, or explicit user interactions.
|
|
446
449
|
|
|
450
|
+
Browser API v2 is a deliberate compatibility break. PI WEB rejects browser API v1 modules instead of silently adapting them. Migrate by setting `apiVersion: 2`, using `pluginId` for the stable package/provider identity, and using `runtimePluginId` when a host-qualified contribution id is required. The former `@chainingintention/pi-web-cn/plugin-api/unstable` path is no longer exported; rely on the documented stable helpers instead.
|
|
451
|
+
|
|
447
452
|
The plugin id comes from `package.json`, not from the JavaScript module. Contribution ids are local to the plugin and PI WEB qualifies them internally as:
|
|
448
453
|
|
|
449
454
|
```text
|
|
@@ -535,7 +540,7 @@ Notes:
|
|
|
535
540
|
- `enabled` is evaluated when the action palette asks for actions.
|
|
536
541
|
- `selectWorkspaceTool()` expects a qualified panel id such as `my-plugin:workspace.info`.
|
|
537
542
|
- `openTerminal()` switches to the built-in terminal panel. Pass `{ terminalId }` to deep-link to a specific terminal.
|
|
538
|
-
- Only fields documented here and declared in `plugin-api.d.ts` are stable public plugin API.
|
|
543
|
+
- Only fields documented here and declared in `plugin-api.d.ts` are stable public browser plugin API. Runtime-only fields and private HTTP routes are not compatibility surfaces.
|
|
539
544
|
|
|
540
545
|
### Prompt editor API
|
|
541
546
|
|
|
@@ -760,7 +765,7 @@ if (!customElements.get("my-workspace-badge")) {
|
|
|
760
765
|
}
|
|
761
766
|
|
|
762
767
|
export default {
|
|
763
|
-
apiVersion:
|
|
768
|
+
apiVersion: 2,
|
|
764
769
|
name: "My Plugin",
|
|
765
770
|
activate: ({ html }) => ({
|
|
766
771
|
contributions: {
|
|
@@ -995,18 +1000,7 @@ Review command strings carefully. They are trusted shell commands executed in th
|
|
|
995
1000
|
|
|
996
1001
|
PI WEB's `/api/...` HTTP and WebSocket routes and runtime-only fields are private implementation details. They exist because plugins are trusted browser code, and because some capabilities may be evaluated there before they are designed as stable helpers.
|
|
997
1002
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
```ts
|
|
1001
|
-
import type { WorkspacePanelContext } from "@chainingintention/pi-web-cn/plugin-api";
|
|
1002
|
-
import type { UnstableWorkspacePanelContext } from "@chainingintention/pi-web-cn/plugin-api/unstable";
|
|
1003
|
-
|
|
1004
|
-
function unstableContext(context: WorkspacePanelContext) {
|
|
1005
|
-
return context as WorkspacePanelContext & UnstableWorkspacePanelContext;
|
|
1006
|
-
}
|
|
1007
|
-
```
|
|
1008
|
-
|
|
1009
|
-
Unstable APIs are not covered by the v1 compatibility promise. Prefer documented helpers whenever they exist.
|
|
1003
|
+
The stable browser API is the documented helper surface and the type-only `@chainingintention/pi-web-cn/plugin-api` export. The stable server API is the separate type-only `@chainingintention/pi-web-cn/server-plugin-api` export. The former browser `plugin-api/unstable` entry is not part of API v2 and is no longer exported. Code that deliberately depends on another private runtime surface must keep that dependency local and expect to revisit it after PI WEB upgrades.
|
|
1010
1004
|
|
|
1011
1005
|
## Async data and caching
|
|
1012
1006
|
|
|
@@ -1024,7 +1018,7 @@ If you are an AI agent building or editing a PI WEB plugin, follow this checklis
|
|
|
1024
1018
|
|
|
1025
1019
|
1. Create or update a plugin folder with `package.json` and a JavaScript module such as `pi-web-plugin.js`.
|
|
1026
1020
|
2. Use the single supported package metadata shape: `piWeb.plugins` array with `{ id, module, machineSpecific? }` entries.
|
|
1027
|
-
3. Default-export `{ apiVersion:
|
|
1021
|
+
3. Default-export `{ apiVersion: 2, name, activate }` from a browser module. Server modules use server-plugin API v1.
|
|
1028
1022
|
4. Return `{ contributions: { actions, workspacePanels, workspaceLabels } }` from `activate()`.
|
|
1029
1023
|
5. Use ids matching `^[a-z][a-z0-9.-]*$`.
|
|
1030
1024
|
6. Use the activation context's `html` function for Lit templates.
|
|
@@ -1034,7 +1028,7 @@ If you are an AI agent building or editing a PI WEB plugin, follow this checklis
|
|
|
1034
1028
|
10. Add workspace labels for compact inline metadata.
|
|
1035
1029
|
11. Return arrays from workspace label `items()`; return an empty array to render nothing.
|
|
1036
1030
|
12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, `state.piWebStatus`, and `prompt`.
|
|
1037
|
-
13. Do not fetch PI WEB `/api/...` endpoints directly unless you intentionally accept private API churn; prefer documented helpers
|
|
1031
|
+
13. Do not fetch PI WEB `/api/...` endpoints directly unless you intentionally accept private API churn; prefer documented helpers such as the paired workspace `backend` bridge.
|
|
1038
1032
|
14. Treat plugins as trusted code and avoid reading or displaying secrets unless intentional.
|
|
1039
1033
|
15. After local edits, tell the user to hard reload the browser and check the console for plugin errors.
|
|
1040
1034
|
|
|
@@ -1056,7 +1050,7 @@ Common issues:
|
|
|
1056
1050
|
|
|
1057
1051
|
- invalid plugin id or contribution id;
|
|
1058
1052
|
- missing default export;
|
|
1059
|
-
- missing `apiVersion:
|
|
1053
|
+
- missing browser `apiVersion: 2`, `name`, or `activate` function;
|
|
1060
1054
|
- missing `package.json` or incorrect `piWeb.plugins` metadata;
|
|
1061
1055
|
- legacy shortcuts such as `piWeb.plugin`, string plugin entries, or no-`package.json` fallback;
|
|
1062
1056
|
- duplicate plugin ids; later duplicates are skipped rather than renamed;
|
package/package.json
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chainingintention/pi-web-cn",
|
|
3
|
-
"version": "1.202608.
|
|
3
|
+
"version": "1.202608.10",
|
|
4
4
|
"description": "Remote web UI and browser control plane for persistent Pi Coding Agent sessions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Federico Jaramillo Martinez",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
"./plugin-api": {
|
|
10
|
+
"types": "./dist/plugin-api.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./server-plugin-api": {
|
|
13
|
+
"types": "./dist/server-plugin-api.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"typesVersions": {
|
|
17
|
+
"*": {
|
|
18
|
+
"plugin-api": [
|
|
19
|
+
"dist/plugin-api.d.ts"
|
|
20
|
+
],
|
|
21
|
+
"server-plugin-api": [
|
|
22
|
+
"dist/server-plugin-api.d.ts"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
8
26
|
"bin": {
|
|
9
27
|
"pi-web": "dist/cli.js",
|
|
10
28
|
"pi-web-server": "dist/server/index.js",
|
|
@@ -16,12 +34,13 @@
|
|
|
16
34
|
"install.sh",
|
|
17
35
|
"README.md",
|
|
18
36
|
"LICENSE",
|
|
37
|
+
"skills/relay/SKILL.md",
|
|
19
38
|
"extensions",
|
|
20
39
|
"docs/plugins.md",
|
|
21
40
|
"docs/config.md",
|
|
22
41
|
"docs/assets",
|
|
23
42
|
"plugin-api.d.ts",
|
|
24
|
-
"plugin-api
|
|
43
|
+
"server-plugin-api.d.ts"
|
|
25
44
|
],
|
|
26
45
|
"scripts": {
|
|
27
46
|
"dev": "bash -c 'trap \"kill 0\" EXIT; npm run dev:sessiond & npm run dev:web & npm run dev:client & wait'",
|
|
@@ -84,9 +103,9 @@
|
|
|
84
103
|
},
|
|
85
104
|
"devDependencies": {
|
|
86
105
|
"@changesets/cli": "^2.31.0",
|
|
87
|
-
"@earendil-works/pi-agent-core": "^0.
|
|
88
|
-
"@earendil-works/pi-ai": "^0.
|
|
89
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
106
|
+
"@earendil-works/pi-agent-core": "^0.84.1",
|
|
107
|
+
"@earendil-works/pi-ai": "^0.84.1",
|
|
108
|
+
"@earendil-works/pi-coding-agent": "^0.84.1",
|
|
90
109
|
"@eslint/js": "^10.0.1",
|
|
91
110
|
"@types/node": "^24.13.3",
|
|
92
111
|
"@types/ws": "^8.18.1",
|
|
@@ -119,9 +138,9 @@
|
|
|
119
138
|
"homepage": "https://github.com/nayuto-wakusei/pi-web-ChineseTranslation#readme",
|
|
120
139
|
"packageManager": "npm@11.11.0",
|
|
121
140
|
"peerDependencies": {
|
|
122
|
-
"@earendil-works/pi-agent-core": ">=0.
|
|
123
|
-
"@earendil-works/pi-ai": ">=0.
|
|
124
|
-
"@earendil-works/pi-coding-agent": ">=0.
|
|
141
|
+
"@earendil-works/pi-agent-core": ">=0.84.0",
|
|
142
|
+
"@earendil-works/pi-ai": ">=0.84.0",
|
|
143
|
+
"@earendil-works/pi-coding-agent": ">=0.84.0"
|
|
125
144
|
},
|
|
126
145
|
"keywords": [
|
|
127
146
|
"pi-package",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dist/server-plugin-api.js";
|
package/skills/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# skills
|
|
2
|
+
|
|
3
|
+
Distributable agent skills developed alongside pi-web.
|
|
4
|
+
|
|
5
|
+
## relay
|
|
6
|
+
|
|
7
|
+
Execute a long or complex plan as a chain of independent sessions, each running
|
|
8
|
+
one well-sized leg and handing off to the next via `spawn_session`.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx skills add jmfederico/pi-web --skill relay -a pi -g
|
|
12
|
+
```
|