@jmfederico/pi-web 1.202610.3 → 1.202610.5
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/cli.js +1 -1
- package/dist/client/assets/classic-CyM97Akm.css +54 -0
- package/dist/client/assets/graphite-DquaqFXU.css +54 -0
- package/dist/client/assets/index-DPS1kTH7.js +4389 -0
- package/dist/client/assets/paper-CB1oLuRr.css +54 -0
- package/dist/client/assets/pi-web-dark-D_KtbM95.css +54 -0
- package/dist/client/assets/pi-web-light-DSNLMIBI.css +54 -0
- package/dist/client/index.html +2 -2
- package/dist/config.js +56 -0
- package/dist/config.js.map +1 -1
- package/dist/pi-packages/captains-log/dist/browser/index.js +13 -17
- package/dist/pi-packages/captains-log/dist/browser/panel.js +7 -11
- package/dist/pi-packages/relays/extensions/index.js +1 -0
- package/dist/pi-packages/relays/pi-web-plugin.js +21 -1
- package/dist/pi-packages/relays/relaysPanelElement.js +36 -32
- package/dist/pi-packages/workflows/browser/pi-web-plugin.js +35 -27
- package/dist/pi-packages/workflows/browser/worktreeDialog.js +156 -0
- package/dist/pi-packages/workflows/package.json +1 -0
- package/dist/pi-packages/workflows/prompts/worktree.md +11 -2
- package/dist/pi-packages/workflows/server-plugin.js +579 -0
- package/dist/pi-packages/workflows/vite.config.mjs +17 -0
- package/dist/pi-web-plugins/code-highlight/browser/pi-web-plugin.js +22 -0
- package/dist/pi-web-plugins/code-highlight/package.json +13 -0
- package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +119 -162
- package/dist/pi-web-plugins/git/browser/git-panel.js +61 -46
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +25 -3
- package/dist/pi-web-plugins/git/git-backend.js +13 -1
- package/dist/pi-web-plugins/git/server-plugin.js +8 -1
- package/dist/pi-web-plugins/mermaid/browser/pi-web-plugin.js +8 -3
- package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +50 -29
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +11 -11
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +21 -1
- package/dist/pi-web-plugins/workspace-tasks/tasksPanelElement.js +35 -19
- package/dist/piWebUpdate.js +32 -9
- package/dist/piWebUpdate.js.map +1 -1
- package/dist/plugin-api.d.ts +174 -45
- package/dist/server/browserMessageProjection.js +1 -1
- package/dist/server/browserMessageProjection.js.map +1 -1
- package/dist/server/configRoutes.js +7 -1
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +3 -2
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/piWebPluginService.js +7 -1
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/plugins/pluginBackendChannelProxyAdmission.js +1 -1
- package/dist/server/plugins/pluginBackendChannelProxyAdmission.js.map +1 -1
- package/dist/server/plugins/pluginBackendChannelProxyRoutes.js +31 -38
- package/dist/server/plugins/pluginBackendChannelProxyRoutes.js.map +1 -1
- package/dist/server/plugins/pluginBackendProxyRoutes.js +38 -45
- package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -1
- package/dist/server/plugins/pluginBackendRegistry.js +58 -34
- package/dist/server/plugins/pluginBackendRegistry.js.map +1 -1
- package/dist/server/plugins/serverPluginCatalogCapability.js +106 -0
- package/dist/server/plugins/serverPluginCatalogCapability.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +21 -8
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
- package/dist/server/sessiond/pluginBackendChannelRoutes.js +24 -14
- package/dist/server/sessiond/pluginBackendChannelRoutes.js.map +1 -1
- package/dist/server/sessiond/pluginBackendRoutes.js +51 -47
- package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -1
- package/dist/server/sessiond/pluginCatalogRoutes.js +37 -0
- package/dist/server/sessiond/pluginCatalogRoutes.js.map +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js +17 -0
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionServiceDependencies.js +1 -0
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
- package/dist/server/sessiond.js +24 -4
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/authRoutes.js +1 -0
- package/dist/server/sessions/authRoutes.js.map +1 -1
- package/dist/server/sessions/authService.js +13 -0
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/builtinExtensionFactories.js +15 -1
- package/dist/server/sessions/builtinExtensionFactories.js.map +1 -1
- package/dist/server/sessions/companionBackend.js +286 -0
- package/dist/server/sessions/companionBackend.js.map +1 -0
- package/dist/server/sessions/mcpAuthFlowService.js +237 -0
- package/dist/server/sessions/mcpAuthFlowService.js.map +1 -0
- package/dist/server/sessions/mcpAuthRoutes.js +76 -0
- package/dist/server/sessions/mcpAuthRoutes.js.map +1 -0
- package/dist/server/sessions/messageHistoryTarget.js +18 -1
- package/dist/server/sessions/messageHistoryTarget.js.map +1 -1
- package/dist/server/sessions/piSessionEventConnections.js +5 -2
- package/dist/server/sessions/piSessionEventConnections.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +279 -22
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +3 -17
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionNameGenerator.js +4 -4
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
- package/dist/server/sessions/spawnSessionTool.js +1 -0
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +1 -0
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server-plugin-api.d.ts +65 -15
- package/dist/server-plugin-api.js +25 -0
- package/dist/server-plugin-api.js.map +1 -1
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/federatedRoutes.js +17 -5
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/pluginApiTypes.d.ts +53 -0
- package/dist/shared/pluginBackendProtocol.js +56 -1
- package/dist/shared/pluginBackendProtocol.js.map +1 -1
- package/dist/shared/pluginCatalogTypes.d.ts +49 -0
- package/dist/shared/pluginCatalogTypes.js +156 -0
- package/dist/shared/pluginCatalogTypes.js.map +1 -0
- package/docs/config.md +68 -2
- package/docs/plugins.md +382 -20
- package/examples/session-bridge-plugin/src/browser/index.ts +1 -1
- package/package.json +7 -12
- package/dist/client/assets/index-BAl5IbYx.js +0 -4429
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
//#region src/shared/pluginCatalogTypes.ts
|
|
2
|
+
var PI_WEB_HOST_CATALOG_CAPABILITY = Object.freeze({
|
|
3
|
+
pluginId: "pi-web.host",
|
|
4
|
+
id: "catalog",
|
|
5
|
+
version: 1,
|
|
6
|
+
parse: snapshotCatalog
|
|
7
|
+
});
|
|
8
|
+
function snapshotCatalog(value) {
|
|
9
|
+
const catalog = record(value);
|
|
10
|
+
if (catalog["version"] !== 1) throw invalid();
|
|
11
|
+
const projects = methods(catalog["projects"]);
|
|
12
|
+
const workspaces = methods(catalog["workspaces"]);
|
|
13
|
+
const sessions = methods(catalog["sessions"]);
|
|
14
|
+
return Object.freeze({
|
|
15
|
+
version: 1,
|
|
16
|
+
projects: Object.freeze({
|
|
17
|
+
list: async (options) => array(await projects.list(options), snapshotProject),
|
|
18
|
+
get: async (projectId, options) => {
|
|
19
|
+
const id = text(projectId);
|
|
20
|
+
const result = snapshotProject(await projects.get(id, options));
|
|
21
|
+
if (result.id !== id) throw invalid();
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
}),
|
|
25
|
+
workspaces: Object.freeze({
|
|
26
|
+
list: async (projectId, options) => {
|
|
27
|
+
const id = text(projectId);
|
|
28
|
+
return array(await workspaces.list(id, options), (row) => workspaceFor(row, id));
|
|
29
|
+
},
|
|
30
|
+
get: async (selection, options) => {
|
|
31
|
+
const requested = snapshotSelection(selection);
|
|
32
|
+
const result = workspaceFor(await workspaces.get(requested, options), requested.projectId);
|
|
33
|
+
if (result.id !== requested.workspaceId) throw invalid();
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
}),
|
|
37
|
+
sessions: Object.freeze({
|
|
38
|
+
list: async (selection, options) => array(await sessions.list(snapshotSelection(selection), options), snapshotSession),
|
|
39
|
+
get: async (selection, options) => {
|
|
40
|
+
const requested = snapshotSelection(selection, true);
|
|
41
|
+
const result = snapshotSession(await sessions.get(requested, options));
|
|
42
|
+
if (result.id !== requested.sessionId) throw invalid();
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function methods(value) {
|
|
49
|
+
const owner = record(value);
|
|
50
|
+
const list = owner["list"];
|
|
51
|
+
const get = owner["get"];
|
|
52
|
+
if (typeof list !== "function" || typeof get !== "function") throw invalid();
|
|
53
|
+
return {
|
|
54
|
+
list: (...args) => Promise.resolve(Reflect.apply(list, owner, args)),
|
|
55
|
+
get: (...args) => Promise.resolve(Reflect.apply(get, owner, args))
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function snapshotProject(value) {
|
|
59
|
+
const row = record(value);
|
|
60
|
+
return Object.freeze({
|
|
61
|
+
id: text(row["id"]),
|
|
62
|
+
name: text(row["name"]),
|
|
63
|
+
path: text(row["path"])
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function workspaceFor(value, projectId) {
|
|
67
|
+
const row = record(value);
|
|
68
|
+
if (row["projectId"] !== projectId || typeof row["isMain"] !== "boolean") throw invalid();
|
|
69
|
+
let provider;
|
|
70
|
+
if (row["provider"] !== void 0) {
|
|
71
|
+
const source = record(row["provider"]);
|
|
72
|
+
const capabilities = record(source["capabilities"]);
|
|
73
|
+
if (typeof capabilities["remove"] !== "boolean") throw invalid();
|
|
74
|
+
provider = Object.freeze({
|
|
75
|
+
pluginId: text(source["pluginId"]),
|
|
76
|
+
capabilities: Object.freeze({ remove: capabilities["remove"] }),
|
|
77
|
+
...source["metadata"] === void 0 ? {} : { metadata: jsonObject(source["metadata"]) }
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const removal = row["removal"] === void 0 ? void 0 : record(row["removal"]);
|
|
81
|
+
return Object.freeze({
|
|
82
|
+
id: text(row["id"]),
|
|
83
|
+
projectId,
|
|
84
|
+
path: text(row["path"]),
|
|
85
|
+
label: text(row["label"]),
|
|
86
|
+
isMain: row["isMain"],
|
|
87
|
+
...provider === void 0 ? {} : { provider },
|
|
88
|
+
...removal === void 0 ? {} : { removal: Object.freeze({
|
|
89
|
+
actionLabel: text(removal["actionLabel"]),
|
|
90
|
+
confirmation: text(removal["confirmation"])
|
|
91
|
+
}) }
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function snapshotSession(value) {
|
|
95
|
+
const row = record(value);
|
|
96
|
+
const runtime = row["runtime"];
|
|
97
|
+
if (typeof row["archived"] !== "boolean" || runtime !== "not-hosted" && runtime !== "idle" && runtime !== "busy") throw invalid();
|
|
98
|
+
let metadata;
|
|
99
|
+
if (row["metadata"] !== void 0) metadata = Object.freeze(Object.fromEntries(Object.entries(record(row["metadata"])).map(([key, data]) => [key, jsonObject(data)])));
|
|
100
|
+
if (row["name"] !== void 0 && typeof row["name"] !== "string") throw invalid();
|
|
101
|
+
return Object.freeze({
|
|
102
|
+
id: text(row["id"]),
|
|
103
|
+
cwd: text(row["cwd"]),
|
|
104
|
+
archived: row["archived"],
|
|
105
|
+
runtime,
|
|
106
|
+
...row["name"] === void 0 ? {} : { name: row["name"] },
|
|
107
|
+
...metadata === void 0 ? {} : { metadata }
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function snapshotSelection(value, session = false) {
|
|
111
|
+
const row = record(value);
|
|
112
|
+
const keys = session ? [
|
|
113
|
+
"projectId",
|
|
114
|
+
"workspaceId",
|
|
115
|
+
"sessionId"
|
|
116
|
+
] : ["projectId", "workspaceId"];
|
|
117
|
+
if (Reflect.ownKeys(row).some((key) => typeof key !== "string" || !keys.includes(key))) throw invalid();
|
|
118
|
+
return Object.freeze({
|
|
119
|
+
projectId: text(row["projectId"]),
|
|
120
|
+
workspaceId: text(row["workspaceId"]),
|
|
121
|
+
...session ? { sessionId: text(row["sessionId"]) } : {}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function array(value, snapshot) {
|
|
125
|
+
if (!Array.isArray(value)) throw invalid();
|
|
126
|
+
return Object.freeze(value.map(snapshot));
|
|
127
|
+
}
|
|
128
|
+
function record(value) {
|
|
129
|
+
if (!isRecord(value)) throw invalid();
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
function isRecord(value) {
|
|
133
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
134
|
+
const prototype = Object.getPrototypeOf(value);
|
|
135
|
+
return prototype === Object.prototype || prototype === null;
|
|
136
|
+
}
|
|
137
|
+
function text(value) {
|
|
138
|
+
if (typeof value !== "string" || value.trim() === "") throw invalid();
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
function jsonObject(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
142
|
+
const source = record(value);
|
|
143
|
+
if (ancestors.has(source)) throw invalid();
|
|
144
|
+
ancestors.add(source);
|
|
145
|
+
const result = Object.freeze(Object.fromEntries(Object.entries(source).map(([key, data]) => [key, json(data, ancestors)])));
|
|
146
|
+
ancestors.delete(source);
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
function json(value, ancestors) {
|
|
150
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
151
|
+
if (Array.isArray(value)) {
|
|
152
|
+
if (ancestors.has(value)) throw invalid();
|
|
153
|
+
ancestors.add(value);
|
|
154
|
+
const result = Object.freeze(value.map((item) => json(item, ancestors)));
|
|
155
|
+
ancestors.delete(value);
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
return jsonObject(value, ancestors);
|
|
159
|
+
}
|
|
160
|
+
function invalid() {
|
|
161
|
+
return /* @__PURE__ */ new Error("Invalid PI WEB host catalog capability v1 data");
|
|
162
|
+
}
|
|
163
|
+
Object.freeze({
|
|
164
|
+
pluginId: "pi-web.host",
|
|
165
|
+
id: "workspaces",
|
|
166
|
+
version: 1,
|
|
167
|
+
parse: snapshotPiWebHostWorkspacesV1
|
|
168
|
+
});
|
|
169
|
+
/** Exact PI sessions v1 capability supplied separately for each declaring server plugin. */
|
|
170
|
+
var PI_WEB_HOST_PI_SESSIONS_CAPABILITY = Object.freeze({
|
|
171
|
+
pluginId: "pi-web.host",
|
|
172
|
+
id: "pi-sessions",
|
|
173
|
+
version: 1,
|
|
174
|
+
parse: snapshotPiWebHostPiSessionsV1
|
|
175
|
+
});
|
|
176
|
+
Object.freeze({
|
|
177
|
+
pluginId: "pi-web.host",
|
|
178
|
+
id: "pi-session-events",
|
|
179
|
+
version: 1,
|
|
180
|
+
parse: snapshotPiWebHostPiSessionEventsV1
|
|
181
|
+
});
|
|
182
|
+
function snapshotPiWebHostWorkspacesV1(value) {
|
|
183
|
+
if (typeof value !== "object" || value === null) throw invalidPiWebHostWorkspaces();
|
|
184
|
+
const version = Reflect.get(value, "version");
|
|
185
|
+
const resolve = Reflect.get(value, "resolve");
|
|
186
|
+
if (version !== 1 || typeof resolve !== "function") throw invalidPiWebHostWorkspaces();
|
|
187
|
+
return Object.freeze({
|
|
188
|
+
version: 1,
|
|
189
|
+
resolve: async (selection) => {
|
|
190
|
+
const requested = snapshotPiWebHostWorkspaceSelection(selection);
|
|
191
|
+
return snapshotPiWebHostWorkspaceAuthority(await Reflect.apply(resolve, value, [requested]), requested);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function snapshotPiWebHostPiSessionEventsV1(value) {
|
|
196
|
+
if (typeof value !== "object" || value === null) throw new Error("Invalid PI session events capability");
|
|
197
|
+
const connect = Reflect.get(value, "connect");
|
|
198
|
+
if (Reflect.get(value, "version") !== 1 || typeof connect !== "function") throw new Error("Invalid PI session events capability");
|
|
199
|
+
return Object.freeze({
|
|
200
|
+
version: 1,
|
|
201
|
+
connect: async (selection) => {
|
|
202
|
+
const workspace = snapshotPiWebHostWorkspaceSelection({
|
|
203
|
+
projectId: selection.projectId,
|
|
204
|
+
workspaceId: selection.workspaceId
|
|
205
|
+
});
|
|
206
|
+
const sessionId = selection.sessionId;
|
|
207
|
+
if (typeof sessionId !== "string" || sessionId.trim() === "" || sessionId.length > 512) throw new Error("Invalid hosted session id");
|
|
208
|
+
const connection = await Reflect.apply(connect, value, [{
|
|
209
|
+
...workspace,
|
|
210
|
+
sessionId
|
|
211
|
+
}]);
|
|
212
|
+
if (typeof connection !== "object" || connection === null) throw new Error("Invalid PI session events connection");
|
|
213
|
+
const signal = Reflect.get(connection, "signal");
|
|
214
|
+
const on = Reflect.get(connection, "on");
|
|
215
|
+
const emit = Reflect.get(connection, "emit");
|
|
216
|
+
const close = Reflect.get(connection, "close");
|
|
217
|
+
if (!(signal instanceof AbortSignal) || typeof on !== "function" || typeof emit !== "function" || typeof close !== "function") throw new Error("Invalid PI session events connection");
|
|
218
|
+
return Object.freeze({
|
|
219
|
+
signal,
|
|
220
|
+
on: (channel, handler) => {
|
|
221
|
+
const unsubscribe = Reflect.apply(on, connection, [channel, handler]);
|
|
222
|
+
if (typeof unsubscribe !== "function") throw new Error("Invalid PI event unsubscribe function");
|
|
223
|
+
return () => {
|
|
224
|
+
Reflect.apply(unsubscribe, void 0, []);
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
emit: (channel, data) => {
|
|
228
|
+
Reflect.apply(emit, connection, [channel, data]);
|
|
229
|
+
},
|
|
230
|
+
close: () => {
|
|
231
|
+
Reflect.apply(close, connection, []);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function snapshotPiWebHostPiSessionsV1(value) {
|
|
238
|
+
if (typeof value !== "object" || value === null) throw invalidPiWebHostPiSessions();
|
|
239
|
+
const version = Reflect.get(value, "version");
|
|
240
|
+
const run = Reflect.get(value, "run");
|
|
241
|
+
const create = Reflect.get(value, "create");
|
|
242
|
+
if (version !== 1 || typeof run !== "function" || typeof create !== "function") throw invalidPiWebHostPiSessions();
|
|
243
|
+
return Object.freeze({
|
|
244
|
+
version: 1,
|
|
245
|
+
create: async (input) => {
|
|
246
|
+
const requested = snapshotPiWebHostWorkspaceSelection(input);
|
|
247
|
+
const result = await Reflect.apply(create, value, [requested]);
|
|
248
|
+
if (!isPublicJsonObject(result)) throw new Error("PI WEB host PI sessions capability v1 must return a created session object");
|
|
249
|
+
const sessionId = requiredBoundedPiSessionString(Reflect.get(result, "sessionId"), "returned sessionId", 512);
|
|
250
|
+
return Object.freeze({ sessionId });
|
|
251
|
+
},
|
|
252
|
+
run: async (input) => {
|
|
253
|
+
const requested = snapshotPiWebHostPiSessionRunInput(input);
|
|
254
|
+
return snapshotPiWebHostPiSessionRun(await Reflect.apply(run, value, [requested]));
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
function snapshotPiWebHostPiSessionRunInput(value) {
|
|
259
|
+
if (!isPublicJsonObject(value)) throw new Error("PI WEB host PI sessions capability v1 run input must be an object");
|
|
260
|
+
const supportedKeys = /* @__PURE__ */ new Set([
|
|
261
|
+
"projectId",
|
|
262
|
+
"workspaceId",
|
|
263
|
+
"prompt"
|
|
264
|
+
]);
|
|
265
|
+
for (const key of Reflect.ownKeys(value)) if (typeof key !== "string" || !supportedKeys.has(key)) throw new Error(`Unsupported PI WEB host PI sessions capability v1 run input field: ${String(key)}`);
|
|
266
|
+
if (!Object.hasOwn(value, "projectId") || !Object.hasOwn(value, "workspaceId") || !Object.hasOwn(value, "prompt")) throw new Error("PI WEB host PI sessions capability v1 run input must contain projectId, workspaceId, and prompt");
|
|
267
|
+
const projectId = requiredBoundedPiSessionString(Reflect.get(value, "projectId"), "projectId", 512);
|
|
268
|
+
const workspaceId = requiredBoundedPiSessionString(Reflect.get(value, "workspaceId"), "workspaceId", 512);
|
|
269
|
+
const prompt = requiredBoundedPiSessionUtf8String(Reflect.get(value, "prompt"), "prompt", 65536);
|
|
270
|
+
return Object.freeze({
|
|
271
|
+
projectId,
|
|
272
|
+
workspaceId,
|
|
273
|
+
prompt
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
function snapshotPiWebHostPiSessionRun(value) {
|
|
277
|
+
if (!isPublicJsonObject(value)) throw new Error("PI WEB host PI sessions capability v1 must return a run object");
|
|
278
|
+
const sessionId = requiredBoundedPiSessionString(Reflect.get(value, "sessionId"), "returned sessionId", 512);
|
|
279
|
+
const completionValue = Reflect.get(value, "completion");
|
|
280
|
+
if (!isPromiseLike(completionValue)) throw new Error("PI WEB host PI sessions capability v1 run must expose completion");
|
|
281
|
+
const completion = Promise.resolve(completionValue).then(snapshotPiWebHostPiSessionRunCompletion);
|
|
282
|
+
return Object.freeze({
|
|
283
|
+
sessionId,
|
|
284
|
+
completion
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function snapshotPiWebHostPiSessionRunCompletion(value) {
|
|
288
|
+
if (!isPublicJsonObject(value)) throw new Error("PI WEB host PI sessions capability v1 completion must be an object");
|
|
289
|
+
const status = Reflect.get(value, "status");
|
|
290
|
+
if (status === "completed" || status === "cancelled") return Object.freeze({ status });
|
|
291
|
+
if (status !== "failed") throw new Error("PI WEB host PI sessions capability v1 completion status is invalid");
|
|
292
|
+
const error = requiredBoundedPiSessionUtf8String(Reflect.get(value, "error"), "completion error", 4096);
|
|
293
|
+
return Object.freeze({
|
|
294
|
+
status,
|
|
295
|
+
error
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function snapshotPiWebHostWorkspaceSelection(value) {
|
|
299
|
+
if (!isPublicJsonObject(value)) throw new Error("PI WEB host workspaces capability v1 selection must be an object");
|
|
300
|
+
const supportedKeys = /* @__PURE__ */ new Set(["projectId", "workspaceId"]);
|
|
301
|
+
for (const key of Reflect.ownKeys(value)) if (typeof key !== "string" || !supportedKeys.has(key)) throw new Error(`Unsupported PI WEB host workspaces capability v1 selection field: ${String(key)}`);
|
|
302
|
+
if (!Object.hasOwn(value, "projectId") || !Object.hasOwn(value, "workspaceId")) throw new Error("PI WEB host workspaces capability v1 selection must contain projectId and workspaceId");
|
|
303
|
+
const projectId = requiredPublicString(Reflect.get(value, "projectId"), "selection projectId");
|
|
304
|
+
const workspaceId = requiredPublicString(Reflect.get(value, "workspaceId"), "selection workspaceId");
|
|
305
|
+
return Object.freeze({
|
|
306
|
+
projectId,
|
|
307
|
+
workspaceId
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
function snapshotPiWebHostWorkspaceAuthority(value, requested) {
|
|
311
|
+
if (!isPublicJsonObject(value)) throw new Error("PI WEB host workspaces capability v1 must return an authority object");
|
|
312
|
+
const project = snapshotPiWebHostProject(Reflect.get(value, "project"));
|
|
313
|
+
const workspace = snapshotPiWebHostWorkspace(Reflect.get(value, "workspace"));
|
|
314
|
+
if (workspace.projectId !== project.id) throw new Error("PI WEB host workspaces capability v1 returned mismatched project and workspace scopes");
|
|
315
|
+
if (project.id !== requested.projectId || workspace.id !== requested.workspaceId) throw new Error("PI WEB host workspaces capability v1 returned authority outside the requested selection");
|
|
316
|
+
return Object.freeze({
|
|
317
|
+
project,
|
|
318
|
+
workspace
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
function snapshotPiWebHostProject(value) {
|
|
322
|
+
if (!isPublicJsonObject(value)) throw new Error("PI WEB host workspaces capability v1 project must be an object");
|
|
323
|
+
const id = requiredPublicString(Reflect.get(value, "id"), "project id");
|
|
324
|
+
const name = requiredPublicString(Reflect.get(value, "name"), "project name");
|
|
325
|
+
const path = requiredPublicString(Reflect.get(value, "path"), "project path");
|
|
326
|
+
return Object.freeze({
|
|
327
|
+
id,
|
|
328
|
+
name,
|
|
329
|
+
path
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function snapshotPiWebHostWorkspace(value) {
|
|
333
|
+
if (!isPublicJsonObject(value)) throw new Error("PI WEB host workspaces capability v1 workspace must be an object");
|
|
334
|
+
const id = requiredPublicString(Reflect.get(value, "id"), "workspace id");
|
|
335
|
+
const projectId = requiredPublicString(Reflect.get(value, "projectId"), "workspace projectId");
|
|
336
|
+
const path = requiredPublicString(Reflect.get(value, "path"), "workspace path");
|
|
337
|
+
const label = requiredPublicString(Reflect.get(value, "label"), "workspace label");
|
|
338
|
+
const isMain = Reflect.get(value, "isMain");
|
|
339
|
+
if (typeof isMain !== "boolean") throw new Error("PI WEB host workspaces capability v1 workspace isMain must be a boolean");
|
|
340
|
+
const providerValue = Reflect.get(value, "provider");
|
|
341
|
+
const provider = providerValue === void 0 ? void 0 : snapshotPiWebHostWorkspaceProvider(providerValue);
|
|
342
|
+
return Object.freeze({
|
|
343
|
+
id,
|
|
344
|
+
projectId,
|
|
345
|
+
path,
|
|
346
|
+
label,
|
|
347
|
+
isMain,
|
|
348
|
+
...provider === void 0 ? {} : { provider }
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
function snapshotPiWebHostWorkspaceProvider(value) {
|
|
352
|
+
if (!isPublicJsonObject(value)) throw new Error("PI WEB host workspaces capability v1 workspace provider must be an object");
|
|
353
|
+
const pluginId = requiredPublicString(Reflect.get(value, "pluginId"), "workspace provider pluginId");
|
|
354
|
+
const capabilitiesValue = Reflect.get(value, "capabilities");
|
|
355
|
+
if (!isPublicJsonObject(capabilitiesValue)) throw new Error("PI WEB host workspaces capability v1 workspace provider capabilities are invalid");
|
|
356
|
+
const remove = Reflect.get(capabilitiesValue, "remove");
|
|
357
|
+
if (typeof remove !== "boolean") throw new Error("PI WEB host workspaces capability v1 workspace provider capabilities are invalid");
|
|
358
|
+
const capabilities = Object.freeze({ remove });
|
|
359
|
+
const metadataValue = Reflect.get(value, "metadata");
|
|
360
|
+
const metadata = metadataValue === void 0 ? void 0 : clonePublicJsonObject(metadataValue, "workspace provider metadata");
|
|
361
|
+
return Object.freeze({
|
|
362
|
+
pluginId,
|
|
363
|
+
capabilities,
|
|
364
|
+
...metadata === void 0 ? {} : { metadata }
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
function clonePublicJsonObject(value, label) {
|
|
368
|
+
if (!isPublicJsonObject(value)) throw new Error(`PI WEB host workspaces capability v1 ${label} must be a JSON object`);
|
|
369
|
+
return clonePublicJsonRecord(value, /* @__PURE__ */ new Set(), label);
|
|
370
|
+
}
|
|
371
|
+
function clonePublicJsonRecord(value, ancestors, label) {
|
|
372
|
+
if (ancestors.has(value)) throw new Error(`PI WEB host workspaces capability v1 ${label} must not contain cycles`);
|
|
373
|
+
ancestors.add(value);
|
|
374
|
+
try {
|
|
375
|
+
const output = {};
|
|
376
|
+
for (const key of Object.keys(value)) Object.defineProperty(output, key, {
|
|
377
|
+
value: clonePublicJsonValue(value[key], ancestors, label),
|
|
378
|
+
enumerable: true,
|
|
379
|
+
configurable: true,
|
|
380
|
+
writable: true
|
|
381
|
+
});
|
|
382
|
+
return Object.freeze(output);
|
|
383
|
+
} finally {
|
|
384
|
+
ancestors.delete(value);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function clonePublicJsonValue(value, ancestors, label) {
|
|
388
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
389
|
+
if (typeof value === "number") {
|
|
390
|
+
if (!Number.isFinite(value)) throw new Error(`PI WEB host workspaces capability v1 ${label} must contain only finite JSON numbers`);
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
if (Array.isArray(value)) {
|
|
394
|
+
if (ancestors.has(value)) throw new Error(`PI WEB host workspaces capability v1 ${label} must not contain cycles`);
|
|
395
|
+
ancestors.add(value);
|
|
396
|
+
try {
|
|
397
|
+
const output = [];
|
|
398
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
399
|
+
if (!Object.hasOwn(value, index)) throw new Error(`PI WEB host workspaces capability v1 ${label} must not contain sparse arrays`);
|
|
400
|
+
output.push(clonePublicJsonValue(value[index], ancestors, label));
|
|
401
|
+
}
|
|
402
|
+
return Object.freeze(output);
|
|
403
|
+
} finally {
|
|
404
|
+
ancestors.delete(value);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (isPublicJsonObject(value)) return clonePublicJsonRecord(value, ancestors, label);
|
|
408
|
+
throw new Error(`PI WEB host workspaces capability v1 ${label} must contain only JSON values`);
|
|
409
|
+
}
|
|
410
|
+
function requiredPublicString(value, label) {
|
|
411
|
+
if (typeof value !== "string" || value === "") throw new Error(`PI WEB host workspaces capability v1 ${label} must be a non-empty string`);
|
|
412
|
+
return value;
|
|
413
|
+
}
|
|
414
|
+
function requiredBoundedPiSessionString(value, label, maxLength) {
|
|
415
|
+
if (typeof value !== "string" || value === "") throw new Error(`PI WEB host PI sessions capability v1 ${label} must be a non-empty string`);
|
|
416
|
+
if (value.length > maxLength) throw new Error(`PI WEB host PI sessions capability v1 ${label} must be at most ${String(maxLength)} characters`);
|
|
417
|
+
return value;
|
|
418
|
+
}
|
|
419
|
+
function requiredBoundedPiSessionUtf8String(value, label, maxBytes) {
|
|
420
|
+
if (typeof value !== "string" || value === "") throw new Error(`PI WEB host PI sessions capability v1 ${label} must be a non-empty string`);
|
|
421
|
+
if (publicStringExceedsUtf8ByteLimit(value, maxBytes)) throw new Error(`PI WEB host PI sessions capability v1 ${label} must be at most ${String(maxBytes)} UTF-8 bytes`);
|
|
422
|
+
return value;
|
|
423
|
+
}
|
|
424
|
+
function publicStringExceedsUtf8ByteLimit(value, maxBytes) {
|
|
425
|
+
let bytes = 0;
|
|
426
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
427
|
+
const codePoint = value.codePointAt(index);
|
|
428
|
+
if (codePoint === void 0) break;
|
|
429
|
+
if (codePoint > 65535) index += 1;
|
|
430
|
+
bytes += codePoint <= 127 ? 1 : codePoint <= 2047 ? 2 : codePoint <= 65535 ? 3 : 4;
|
|
431
|
+
if (bytes > maxBytes) return true;
|
|
432
|
+
}
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
function isPromiseLike(value) {
|
|
436
|
+
return typeof value === "object" && value !== null || typeof value === "function" ? typeof Reflect.get(value, "then") === "function" : false;
|
|
437
|
+
}
|
|
438
|
+
function isPublicJsonObject(value) {
|
|
439
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
440
|
+
const prototype = Object.getPrototypeOf(value);
|
|
441
|
+
return prototype === Object.prototype || prototype === null;
|
|
442
|
+
}
|
|
443
|
+
function invalidPiWebHostWorkspaces() {
|
|
444
|
+
return /* @__PURE__ */ new Error("PI WEB host workspaces capability v1 must expose resolve");
|
|
445
|
+
}
|
|
446
|
+
function invalidPiWebHostPiSessions() {
|
|
447
|
+
return /* @__PURE__ */ new Error("PI WEB host PI sessions capability v1 must expose create and run");
|
|
448
|
+
}
|
|
449
|
+
//#endregion
|
|
450
|
+
//#region pi-packages/workflows/server-plugin.ts
|
|
451
|
+
var GIT_LOCAL_ENV_VARS = Object.freeze([
|
|
452
|
+
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
453
|
+
"GIT_COMMON_DIR",
|
|
454
|
+
"GIT_CONFIG",
|
|
455
|
+
"GIT_CONFIG_COUNT",
|
|
456
|
+
"GIT_CONFIG_PARAMETERS",
|
|
457
|
+
"GIT_DIR",
|
|
458
|
+
"GIT_GRAFT_FILE",
|
|
459
|
+
"GIT_IMPLICIT_WORK_TREE",
|
|
460
|
+
"GIT_INDEX_FILE",
|
|
461
|
+
"GIT_NO_REPLACE_OBJECTS",
|
|
462
|
+
"GIT_OBJECT_DIRECTORY",
|
|
463
|
+
"GIT_PREFIX",
|
|
464
|
+
"GIT_QUARANTINE_PATH",
|
|
465
|
+
"GIT_REPLACE_REF_BASE",
|
|
466
|
+
"GIT_SHALLOW_FILE",
|
|
467
|
+
"GIT_WORK_TREE"
|
|
468
|
+
]);
|
|
469
|
+
var plugin = {
|
|
470
|
+
apiVersion: 5,
|
|
471
|
+
name: "Workflows",
|
|
472
|
+
requires: [PI_WEB_HOST_CATALOG_CAPABILITY, PI_WEB_HOST_PI_SESSIONS_CAPABILITY],
|
|
473
|
+
activate(context) {
|
|
474
|
+
let catalog;
|
|
475
|
+
let sessions;
|
|
476
|
+
return {
|
|
477
|
+
start({ capabilities }) {
|
|
478
|
+
catalog = capabilities.resolve(PI_WEB_HOST_CATALOG_CAPABILITY);
|
|
479
|
+
sessions = capabilities.resolve(PI_WEB_HOST_PI_SESSIONS_CAPABILITY);
|
|
480
|
+
},
|
|
481
|
+
peer: { async request({ scope, operation, input, signal }) {
|
|
482
|
+
if (scope.kind !== "project") throw new Error("Workflows requires project scope");
|
|
483
|
+
if (operation !== "prepare" && operation !== "dispatch") throw new Error("Unknown Workflows operation");
|
|
484
|
+
const details = operation === "dispatch" ? dispatchInput(input) : void 0;
|
|
485
|
+
if (catalog === void 0 || sessions === void 0) throw new Error("Workflows has not started");
|
|
486
|
+
const workspaces = await catalog.workspaces.list(scope.project.id, { signal });
|
|
487
|
+
if (workspaces.some((workspace) => workspace.projectId !== scope.project.id)) throw new Error("Catalog workspace is outside the scoped project");
|
|
488
|
+
const mains = workspaces.filter((workspace) => workspace.isMain);
|
|
489
|
+
const main = mains[0];
|
|
490
|
+
if (mains.length !== 1 || main === void 0) throw new Error("Project must have exactly one main workspace");
|
|
491
|
+
const git = (args) => runGit(context, main.path, args, signal);
|
|
492
|
+
if ((await git(["rev-parse", "--is-inside-work-tree"])).trim() !== "true" || (await git(["rev-parse", "--is-bare-repository"])).trim() !== "false") throw new Error("Main workspace must be a non-bare Git worktree");
|
|
493
|
+
if (details === void 0) {
|
|
494
|
+
const refs = await git([
|
|
495
|
+
"for-each-ref",
|
|
496
|
+
"--format=%(refname)",
|
|
497
|
+
"refs/heads/",
|
|
498
|
+
"refs/remotes/"
|
|
499
|
+
]);
|
|
500
|
+
const branches = [...new Set(refs.split(/\r?\n/u).filter(Boolean).map((ref) => ref.replace(/^refs\/(?:heads|remotes)\//u, "")))].sort();
|
|
501
|
+
const current = (await git([
|
|
502
|
+
"rev-parse",
|
|
503
|
+
"--abbrev-ref",
|
|
504
|
+
"HEAD"
|
|
505
|
+
])).trim();
|
|
506
|
+
if (current === "") throw new Error("Git returned an empty current branch");
|
|
507
|
+
return {
|
|
508
|
+
workspaceId: main.id,
|
|
509
|
+
workspacePath: main.path,
|
|
510
|
+
branches,
|
|
511
|
+
defaultBase: current
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
await git([
|
|
515
|
+
"rev-parse",
|
|
516
|
+
"--verify",
|
|
517
|
+
"--end-of-options",
|
|
518
|
+
`${details.base}^{commit}`
|
|
519
|
+
]);
|
|
520
|
+
signal.throwIfAborted();
|
|
521
|
+
const run = await sessions.run({
|
|
522
|
+
projectId: scope.project.id,
|
|
523
|
+
workspaceId: main.id,
|
|
524
|
+
prompt: worktreeCommand(details.base, details.description)
|
|
525
|
+
});
|
|
526
|
+
const logDetails = {
|
|
527
|
+
projectId: scope.project.id,
|
|
528
|
+
workspaceId: main.id,
|
|
529
|
+
sessionId: run.sessionId
|
|
530
|
+
};
|
|
531
|
+
run.completion.then((completion) => {
|
|
532
|
+
if (completion.status === "failed") context.logger.error("Workflows initial worktree task failed", {
|
|
533
|
+
...logDetails,
|
|
534
|
+
error: completion.error
|
|
535
|
+
});
|
|
536
|
+
else if (completion.status === "cancelled") context.logger.warn("Workflows initial worktree task cancelled", logDetails);
|
|
537
|
+
}, (error) => {
|
|
538
|
+
context.logger.error("Workflows initial worktree task completion rejected", {
|
|
539
|
+
...logDetails,
|
|
540
|
+
error: error instanceof Error ? error.message : String(error)
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
return {
|
|
544
|
+
workspaceId: main.id,
|
|
545
|
+
sessionId: run.sessionId
|
|
546
|
+
};
|
|
547
|
+
} }
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
function dispatchInput(input) {
|
|
552
|
+
if (typeof input !== "object" || input === null || Array.isArray(input) || Object.keys(input).some((key) => key !== "base" && key !== "description")) throw new Error("Dispatch input must contain only base and description");
|
|
553
|
+
return {
|
|
554
|
+
base: boundedString(Reflect.get(input, "base"), "base", 1024),
|
|
555
|
+
description: boundedString(Reflect.get(input, "description"), "description", 16384)
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function boundedString(value, name, maxBytes) {
|
|
559
|
+
if (typeof value !== "string" || value.trim() === "" || value.includes("\0") || Buffer.byteLength(value, "utf8") > maxBytes) throw new Error(`Invalid ${name}: expected non-empty text of at most ${String(maxBytes)} UTF-8 bytes without NUL`);
|
|
560
|
+
return value;
|
|
561
|
+
}
|
|
562
|
+
function worktreeCommand(base, description) {
|
|
563
|
+
return `/worktree '${`base: ${base}\n\ndescription:\n${description}`.replaceAll("'", "'\"'\"'")}'`;
|
|
564
|
+
}
|
|
565
|
+
async function runGit(context, cwd, args, signal) {
|
|
566
|
+
const result = await context.execFile({
|
|
567
|
+
file: "git",
|
|
568
|
+
cwd,
|
|
569
|
+
args,
|
|
570
|
+
unsetEnv: GIT_LOCAL_ENV_VARS,
|
|
571
|
+
timeoutMs: 1e4,
|
|
572
|
+
signal
|
|
573
|
+
});
|
|
574
|
+
if (result.stdoutTruncated || result.stderrTruncated) throw new Error("Git output exceeded the host limit");
|
|
575
|
+
if (result.exitCode !== 0 || result.signal !== null) throw new Error(`Git command failed (${result.signal ?? String(result.exitCode)}): ${result.stderr.trim()}`);
|
|
576
|
+
return result.stdout;
|
|
577
|
+
}
|
|
578
|
+
//#endregion
|
|
579
|
+
export { plugin as default };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
|
|
4
|
+
// Bundle the public host capability tokens so the shipped package does not
|
|
5
|
+
// require a second installed copy of PI WEB at runtime.
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
build: {
|
|
8
|
+
emptyOutDir: false,
|
|
9
|
+
lib: {
|
|
10
|
+
entry: fileURLToPath(new URL("./server-plugin.ts", import.meta.url)),
|
|
11
|
+
formats: ["es"],
|
|
12
|
+
fileName: () => "server-plugin.js",
|
|
13
|
+
},
|
|
14
|
+
target: "node22",
|
|
15
|
+
minify: false,
|
|
16
|
+
},
|
|
17
|
+
});
|