@hachej/boring-ui-cli 0.1.35 → 0.1.37
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/server/cli.js +19 -579
- package/dist/server/modeApps.js +626 -0
- package/dist/server/pluginDiscovery.js +25 -4
- package/dist/server/pluginFrontRuntime.js +86 -17
- package/package.json +6 -5
- package/public/assets/{DebugDrawer-CmfCaxFo.js → DebugDrawer-DnJihn7d.js} +1 -1
- package/public/assets/{_baseUniq-C4cssnmI.js → _baseUniq-Css5uykF.js} +1 -1
- package/public/assets/{arc-wdyRM9Dj.js → arc-CnfZXaWe.js} +1 -1
- package/public/assets/{architectureDiagram-Q4EWVU46-BxGI7D7J.js → architectureDiagram-Q4EWVU46-BANSd98t.js} +1 -1
- package/public/assets/{blockDiagram-DXYQGD6D-Dn29DmW4.js → blockDiagram-DXYQGD6D-Cd6iEbZF.js} +1 -1
- package/public/assets/{c4Diagram-AHTNJAMY-C1tj_DPA.js → c4Diagram-AHTNJAMY-9WJupXFB.js} +1 -1
- package/public/assets/channel-Cn1SBioe.js +1 -0
- package/public/assets/{chunk-4BX2VUAB-CHT4yrPF.js → chunk-4BX2VUAB-DEE8UItM.js} +1 -1
- package/public/assets/{chunk-4TB4RGXK-DnESVf52.js → chunk-4TB4RGXK-Bf4S3aFF.js} +1 -1
- package/public/assets/{chunk-55IACEB6-CMiNXUlN.js → chunk-55IACEB6-rumvIfCQ.js} +1 -1
- package/public/assets/{chunk-EDXVE4YY-DdgwYS0K.js → chunk-EDXVE4YY-B41LsR8o.js} +1 -1
- package/public/assets/{chunk-FMBD7UC4-BAOIovY_.js → chunk-FMBD7UC4-Cq0MvVHl.js} +1 -1
- package/public/assets/{chunk-OYMX7WX6-Cbvpbjce.js → chunk-OYMX7WX6-AEfKb-L7.js} +1 -1
- package/public/assets/{chunk-QZHKN3VN-CoKffRLQ.js → chunk-QZHKN3VN-BnF9Mgjw.js} +1 -1
- package/public/assets/{chunk-YZCP3GAM-C7-eq7rZ.js → chunk-YZCP3GAM-CVhtG4Yd.js} +1 -1
- package/public/assets/classDiagram-6PBFFD2Q-B7iAGzAt.js +1 -0
- package/public/assets/classDiagram-v2-HSJHXN6E-B7iAGzAt.js +1 -0
- package/public/assets/clone-CKRPa4Cx.js +1 -0
- package/public/assets/{cose-bilkent-S5V4N54A-C1S6rZwh.js → cose-bilkent-S5V4N54A-BAlza9SS.js} +1 -1
- package/public/assets/{dagre-KV5264BT-Ct0pKZ0k.js → dagre-KV5264BT-DXC-wg6w.js} +1 -1
- package/public/assets/{diagram-5BDNPKRD-YH70HUnu.js → diagram-5BDNPKRD-DQ_JZ2v6.js} +1 -1
- package/public/assets/{diagram-G4DWMVQ6-C4odz4kl.js → diagram-G4DWMVQ6-BV1_oXdH.js} +1 -1
- package/public/assets/{diagram-MMDJMWI5-BmJzg7kb.js → diagram-MMDJMWI5-CBT9iuoD.js} +1 -1
- package/public/assets/{diagram-TYMM5635-C3ojF6WX.js → diagram-TYMM5635-2gLQa-5c.js} +1 -1
- package/public/assets/{erDiagram-SMLLAGMA-CCv0YvhQ.js → erDiagram-SMLLAGMA-BlACgWIF.js} +1 -1
- package/public/assets/{flowDiagram-DWJPFMVM-cOU32gSH.js → flowDiagram-DWJPFMVM-C3a4Cakw.js} +1 -1
- package/public/assets/{ganttDiagram-T4ZO3ILL-DBfxNCML.js → ganttDiagram-T4ZO3ILL-BvO6eI9n.js} +1 -1
- package/public/assets/{gitGraphDiagram-UUTBAWPF-BpeXvr1_.js → gitGraphDiagram-UUTBAWPF-CNRpcuUc.js} +1 -1
- package/public/assets/{graph-nhMaYPvi.js → graph-BGcM9Vra.js} +1 -1
- package/public/assets/{highlighted-body-OFNGDK62--BzSfCvR.js → highlighted-body-OFNGDK62-DFAlA8ty.js} +1 -1
- package/public/assets/{index-BMuBL8Qv.js → index-DuQz_ooq.js} +476 -471
- package/public/assets/index-o6MvaI3V.css +1 -0
- package/public/assets/{infoDiagram-42DDH7IO-TgQUdnii.js → infoDiagram-42DDH7IO-tqWZWhv9.js} +1 -1
- package/public/assets/{ishikawaDiagram-UXIWVN3A-BTf1ZnbI.js → ishikawaDiagram-UXIWVN3A-BSrhE5rT.js} +1 -1
- package/public/assets/{journeyDiagram-VCZTEJTY-CE5ZvQD-.js → journeyDiagram-VCZTEJTY-p4zWU15s.js} +1 -1
- package/public/assets/{kanban-definition-6JOO6SKY-DdedFuqF.js → kanban-definition-6JOO6SKY-Bh1g0QjZ.js} +1 -1
- package/public/assets/{layout-Cih797KL.js → layout-gyEZgedN.js} +1 -1
- package/public/assets/{linear-C5C4CuFZ.js → linear-D_hzHorE.js} +1 -1
- package/public/assets/{min-7u-C0OaV.js → min-DuaweV_G.js} +1 -1
- package/public/assets/{mindmap-definition-QFDTVHPH-OBZRxcri.js → mindmap-definition-QFDTVHPH-Bd9cPaCZ.js} +1 -1
- package/public/assets/{pieDiagram-DEJITSTG-D8ztumiz.js → pieDiagram-DEJITSTG-BtC28nvk.js} +1 -1
- package/public/assets/{quadrantDiagram-34T5L4WZ-CNNRXkDc.js → quadrantDiagram-34T5L4WZ-D4l6_MzD.js} +1 -1
- package/public/assets/{requirementDiagram-MS252O5E-Cwbaa0Qx.js → requirementDiagram-MS252O5E-C1iUHr18.js} +1 -1
- package/public/assets/{sankeyDiagram-XADWPNL6-DBmviVXl.js → sankeyDiagram-XADWPNL6-OVDB_8ZR.js} +1 -1
- package/public/assets/{sequenceDiagram-FGHM5R23-ppAZl-uV.js → sequenceDiagram-FGHM5R23-BDavACVC.js} +1 -1
- package/public/assets/{stateDiagram-FHFEXIEX-C168rmPR.js → stateDiagram-FHFEXIEX-q8RwQFTP.js} +1 -1
- package/public/assets/stateDiagram-v2-QKLJ7IA2-BoiN-2KM.js +1 -0
- package/public/assets/{timeline-definition-GMOUNBTQ-CXM9zfSi.js → timeline-definition-GMOUNBTQ-y0_Knxi9.js} +1 -1
- package/public/assets/{vennDiagram-DHZGUBPP-GDYdqb9p.js → vennDiagram-DHZGUBPP-DbnSIyoN.js} +1 -1
- package/public/assets/{wardley-RL74JXVD-z_pLyMzD.js → wardley-RL74JXVD-CIj9kuqP.js} +1 -1
- package/public/assets/{wardleyDiagram-NUSXRM2D-ClQNpAKT.js → wardleyDiagram-NUSXRM2D--fILgFoZ.js} +1 -1
- package/public/assets/{xychartDiagram-5P7HB3ND-BThhBVRv.js → xychartDiagram-5P7HB3ND-YFiXEsiS.js} +1 -1
- package/public/index.html +2 -2
- package/public/assets/channel-zS0VgreV.js +0 -1
- package/public/assets/classDiagram-6PBFFD2Q-DF_m3Xeb.js +0 -1
- package/public/assets/classDiagram-v2-HSJHXN6E-DF_m3Xeb.js +0 -1
- package/public/assets/clone-DcXiz7Vf.js +0 -1
- package/public/assets/index-CR04sFQU.css +0 -1
- package/public/assets/stateDiagram-v2-QKLJ7IA2-D6wQK0MH.js +0 -1
package/dist/server/cli.js
CHANGED
|
@@ -1,64 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
3
|
import {
|
|
4
|
-
existsSync
|
|
5
|
-
readFileSync
|
|
4
|
+
existsSync
|
|
6
5
|
} from "node:fs";
|
|
7
|
-
import {
|
|
8
|
-
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { basename, join, resolve } from "node:path";
|
|
10
7
|
import { parseArgs } from "node:util";
|
|
11
8
|
import { createLocalWorkspaceRegistry } from "./localWorkspaces.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return "0.0.0";
|
|
26
|
-
}
|
|
27
|
-
})();
|
|
28
|
-
function httpError(message, statusCode) {
|
|
29
|
-
const error = new Error(message);
|
|
30
|
-
error.statusCode = statusCode;
|
|
31
|
-
return error;
|
|
32
|
-
}
|
|
33
|
-
function firstString(value) {
|
|
34
|
-
if (typeof value === "string") return value;
|
|
35
|
-
if (!Array.isArray(value)) return void 0;
|
|
36
|
-
return value.find((item) => typeof item === "string");
|
|
37
|
-
}
|
|
38
|
-
function resolveWorkspaceIdFromRequest(request) {
|
|
39
|
-
const headers = request.headers ?? {};
|
|
40
|
-
const headerValue = headers["x-boring-workspace-id"] ?? Object.entries(headers).find(([key]) => key.toLowerCase() === "x-boring-workspace-id")?.[1];
|
|
41
|
-
const query = request.query;
|
|
42
|
-
const raw = firstString(headerValue) ?? firstString(query?.workspaceId) ?? "";
|
|
43
|
-
const workspaceId = raw.trim();
|
|
44
|
-
if (!workspaceId) throw httpError("workspace id is required", 400);
|
|
45
|
-
if (workspaceId.includes("\0") || workspaceId.includes("/") || workspaceId.includes("\\") || workspaceId.includes("..") || isAbsolute(workspaceId)) {
|
|
46
|
-
throw httpError("invalid workspace id", 400);
|
|
47
|
-
}
|
|
48
|
-
return workspaceId;
|
|
49
|
-
}
|
|
50
|
-
function toCoreWorkspace(workspace) {
|
|
51
|
-
return {
|
|
52
|
-
id: workspace.id,
|
|
53
|
-
name: workspace.name,
|
|
54
|
-
slug: workspace.id,
|
|
55
|
-
isDefault: false,
|
|
56
|
-
createdAt: workspace.createdAt,
|
|
57
|
-
updatedAt: workspace.updatedAt,
|
|
58
|
-
unavailable: !workspace.available,
|
|
59
|
-
path: workspace.path
|
|
60
|
-
};
|
|
61
|
-
}
|
|
9
|
+
import {
|
|
10
|
+
MODE_MAP,
|
|
11
|
+
createFolderModeApp,
|
|
12
|
+
createWorkspacesModeApp
|
|
13
|
+
} from "./modeApps.js";
|
|
14
|
+
import {
|
|
15
|
+
createBoringUiCliRuntimePlugin,
|
|
16
|
+
createFolderModeApp as createFolderModeApp2,
|
|
17
|
+
createWorkspacesModeApp as createWorkspacesModeApp2,
|
|
18
|
+
provisionCliWorkspaceRuntime as provisionCliWorkspaceRuntime2,
|
|
19
|
+
resolveBoringUiPluginCliPackageRoot
|
|
20
|
+
} from "./modeApps.js";
|
|
21
|
+
import { resolveBoringUiCliPackageRoot as resolveBoringUiCliPackageRoot2 } from "./pluginDiscovery.js";
|
|
62
22
|
function openBrowser(url) {
|
|
63
23
|
try {
|
|
64
24
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
@@ -66,59 +26,6 @@ function openBrowser(url) {
|
|
|
66
26
|
} catch {
|
|
67
27
|
}
|
|
68
28
|
}
|
|
69
|
-
function resolveBoringUiCliPackageRoot() {
|
|
70
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
71
|
-
return resolve(__dirname, "..", "..");
|
|
72
|
-
}
|
|
73
|
-
function isUsableBoringUiPluginCliPackageRoot(candidate) {
|
|
74
|
-
try {
|
|
75
|
-
const pkg = JSON.parse(readFileSync(join(candidate, "package.json"), "utf8"));
|
|
76
|
-
return pkg.name === PLUGIN_CLI_PACKAGE_NAME && existsSync(join(candidate, "dist", "bin.js"));
|
|
77
|
-
} catch {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function resolveBoringUiPluginCliPackageRoot() {
|
|
82
|
-
const cliRoot = resolveBoringUiCliPackageRoot();
|
|
83
|
-
const candidate = resolve(cliRoot, "..", "plugin-cli");
|
|
84
|
-
return isUsableBoringUiPluginCliPackageRoot(candidate) ? candidate : null;
|
|
85
|
-
}
|
|
86
|
-
function createBoringUiCliRuntimePlugin() {
|
|
87
|
-
const useLocal = process.env.BORING_USE_LOCAL_PACKAGES === "1";
|
|
88
|
-
const packageRoot = useLocal ? resolveBoringUiPluginCliPackageRoot() : null;
|
|
89
|
-
return {
|
|
90
|
-
id: "boring-ui-plugin-cli-runtime",
|
|
91
|
-
provisioning: {
|
|
92
|
-
nodePackages: [{
|
|
93
|
-
id: "boring-ui-plugin-cli",
|
|
94
|
-
packageName: PLUGIN_CLI_PACKAGE_NAME,
|
|
95
|
-
...packageRoot ? { packageRoot } : { version: CLI_VERSION },
|
|
96
|
-
expectedBins: ["boring-ui-plugin"]
|
|
97
|
-
}]
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
async function provisionCliWorkspaceRuntime(opts) {
|
|
102
|
-
if (opts.provisionWorkspace === false) return void 0;
|
|
103
|
-
const agent = await import("@hachej/boring-agent/server");
|
|
104
|
-
const runtimeLayout = opts.runtimeLayout ?? agent.getBoringAgentRuntimePaths(opts.workspaceRoot);
|
|
105
|
-
const adapter = opts.adapter ?? opts.modeAdapter?.createProvisioningAdapter?.(runtimeLayout) ?? agent.resolveMode(opts.mode).createProvisioningAdapter?.(runtimeLayout);
|
|
106
|
-
if (!adapter) {
|
|
107
|
-
throw new Error(`runtime mode ${opts.mode} does not support workspace provisioning`);
|
|
108
|
-
}
|
|
109
|
-
const result = await agent.provisionWorkspaceRuntime({
|
|
110
|
-
plugins: [createBoringUiCliRuntimePlugin(), ...opts.plugins ?? []],
|
|
111
|
-
adapter,
|
|
112
|
-
runtimeLayout
|
|
113
|
-
});
|
|
114
|
-
return {
|
|
115
|
-
...result,
|
|
116
|
-
env: {
|
|
117
|
-
...result.env,
|
|
118
|
-
BORING_AGENT_WORKSPACE_LOCAL_PLUGIN_ROOTS: opts.mode === "direct" || opts.mode === "local" ? "1" : "0"
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
29
|
function ensureFrontendBuilt(publicDir) {
|
|
123
30
|
if (existsSync(join(publicDir, "index.html"))) return;
|
|
124
31
|
console.error("\nError: boring-ui frontend not found.");
|
|
@@ -176,172 +83,6 @@ async function checkAuth() {
|
|
|
176
83
|
const registry = ModelRegistry.create(authStorage);
|
|
177
84
|
return registry.getAvailable().length;
|
|
178
85
|
}
|
|
179
|
-
const FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID = "folder";
|
|
180
|
-
const RUNTIME_PLUGIN_TRUST_LABEL = "Trusted local runtime plugins";
|
|
181
|
-
const RUNTIME_PLUGIN_TRUST_DESCRIPTION = "Loads plugin UI code from trusted local Pi extension roots through the CLI-owned runtime module host.";
|
|
182
|
-
function createRuntimePluginDiagnosticsStore() {
|
|
183
|
-
const byWorkspace = /* @__PURE__ */ new Map();
|
|
184
|
-
function upsert(workspaceId, pluginId) {
|
|
185
|
-
const workspace = byWorkspace.get(workspaceId) ?? /* @__PURE__ */ new Map();
|
|
186
|
-
byWorkspace.set(workspaceId, workspace);
|
|
187
|
-
const existing = workspace.get(pluginId) ?? {
|
|
188
|
-
workspaceId,
|
|
189
|
-
pluginId,
|
|
190
|
-
recent: []
|
|
191
|
-
};
|
|
192
|
-
workspace.set(pluginId, existing);
|
|
193
|
-
return existing;
|
|
194
|
-
}
|
|
195
|
-
return {
|
|
196
|
-
record(diagnostic) {
|
|
197
|
-
if (!diagnostic.workspaceId || !diagnostic.pluginId) return;
|
|
198
|
-
const entry = upsert(diagnostic.workspaceId, diagnostic.pluginId);
|
|
199
|
-
const now = Date.now();
|
|
200
|
-
entry.lastDiagnostic = diagnostic;
|
|
201
|
-
entry.recent = [...entry.recent, diagnostic].slice(-12);
|
|
202
|
-
if (diagnostic.revision !== void 0) entry.revision = diagnostic.revision;
|
|
203
|
-
if (diagnostic.requestedPath) entry.lastRequestedPath = diagnostic.requestedPath;
|
|
204
|
-
if (diagnostic.resolvedPath) entry.lastResolvedPath = diagnostic.resolvedPath;
|
|
205
|
-
const details = diagnostic.details ?? {};
|
|
206
|
-
if (typeof details.rootDir === "string") entry.rootDir = details.rootDir;
|
|
207
|
-
if (typeof details.entryUrl === "string") entry.entryUrl = details.entryUrl;
|
|
208
|
-
if (typeof diagnostic.requestedPath === "string") entry.frontEntrySubpath = diagnostic.requestedPath;
|
|
209
|
-
if (diagnostic.stage === "track" && diagnostic.outcome === "tracked") {
|
|
210
|
-
entry.lastErrorCode = void 0;
|
|
211
|
-
entry.lastErrorMessage = void 0;
|
|
212
|
-
entry.lastErrorStage = void 0;
|
|
213
|
-
}
|
|
214
|
-
if (diagnostic.stage === "cache") entry.lastRequestAt = now;
|
|
215
|
-
if (diagnostic.stage === "transform" && diagnostic.outcome === "served") {
|
|
216
|
-
entry.lastTransformAt = now;
|
|
217
|
-
entry.lastTransformDurationMs = diagnostic.durationMs;
|
|
218
|
-
}
|
|
219
|
-
if (diagnostic.stage === "serve" && diagnostic.outcome === "served") {
|
|
220
|
-
entry.lastServeAt = now;
|
|
221
|
-
entry.lastServeDurationMs = diagnostic.durationMs;
|
|
222
|
-
entry.lastErrorCode = void 0;
|
|
223
|
-
entry.lastErrorMessage = void 0;
|
|
224
|
-
entry.lastErrorStage = void 0;
|
|
225
|
-
}
|
|
226
|
-
if (diagnostic.outcome === "rejected") {
|
|
227
|
-
entry.lastRejectedAt = now;
|
|
228
|
-
entry.lastErrorCode = diagnostic.code;
|
|
229
|
-
entry.lastErrorMessage = diagnostic.msg;
|
|
230
|
-
entry.lastErrorStage = diagnostic.stage;
|
|
231
|
-
}
|
|
232
|
-
if (diagnostic.stage === "cleanup" && diagnostic.outcome === "disposed") {
|
|
233
|
-
entry.lastDisposedAt = now;
|
|
234
|
-
}
|
|
235
|
-
},
|
|
236
|
-
snapshot(workspaceId) {
|
|
237
|
-
return [...byWorkspace.get(workspaceId)?.values() ?? []].map((entry) => ({ ...entry, recent: [...entry.recent] })).sort((a, b) => a.pluginId.localeCompare(b.pluginId));
|
|
238
|
-
},
|
|
239
|
-
disposeWorkspace(workspaceId) {
|
|
240
|
-
byWorkspace.delete(workspaceId);
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
function syncRuntimeHostFromPluginEvents(runtimeHost, workspaceId, events) {
|
|
245
|
-
for (const event of events) {
|
|
246
|
-
if (event.type === "boring.plugin.unload" || event.type === "boring.plugin.load" && !event.frontTarget) {
|
|
247
|
-
runtimeHost.untrackPlugin(workspaceId, event.id);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
function buildRuntimePluginDiagnosticsResponse(args) {
|
|
252
|
-
const byPlugin = /* @__PURE__ */ new Map();
|
|
253
|
-
for (const plugin of args.loaded) {
|
|
254
|
-
byPlugin.set(plugin.id, {
|
|
255
|
-
id: plugin.id,
|
|
256
|
-
...plugin.version ? { version: plugin.version } : {},
|
|
257
|
-
...plugin.rootDir ? { rootDir: plugin.rootDir } : {},
|
|
258
|
-
...plugin.frontPath ? { frontPath: plugin.frontPath } : {},
|
|
259
|
-
...plugin.frontTarget ? { frontTarget: plugin.frontTarget } : {},
|
|
260
|
-
...plugin.revision !== void 0 ? { serverLoadedRevision: plugin.revision } : {}
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
for (const error of args.errors) {
|
|
264
|
-
const current = byPlugin.get(error.id) ?? { id: error.id };
|
|
265
|
-
byPlugin.set(error.id, {
|
|
266
|
-
...current,
|
|
267
|
-
serverError: error.message
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
for (const hostEntry of args.host) {
|
|
271
|
-
const current = byPlugin.get(hostEntry.pluginId) ?? { id: hostEntry.pluginId };
|
|
272
|
-
byPlugin.set(hostEntry.pluginId, {
|
|
273
|
-
...current,
|
|
274
|
-
...current.rootDir ? {} : hostEntry.rootDir ? { rootDir: hostEntry.rootDir } : {},
|
|
275
|
-
...current.frontPath ? {} : hostEntry.frontEntrySubpath ? { frontPath: hostEntry.frontEntrySubpath } : {},
|
|
276
|
-
...current.frontTarget ? {} : hostEntry.entryUrl ? { frontTarget: { kind: "native", entryUrl: hostEntry.entryUrl, revision: hostEntry.revision ?? 0, trust: "local-trusted-native" } } : {},
|
|
277
|
-
host: hostEntry
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
return {
|
|
281
|
-
workspaceId: args.workspaceId,
|
|
282
|
-
plugins: [...byPlugin.values()].sort((a, b) => a.id.localeCompare(b.id))
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
async function createFolderModeApp(opts) {
|
|
286
|
-
const workspaceRoot = resolve(opts.workspaceRoot);
|
|
287
|
-
const projectName = opts.projectName ?? (basename(workspaceRoot) || "workspace");
|
|
288
|
-
const [{ createWorkspaceAgentServer, readWorkspacePluginPackageRuntimePlugins }, { createPluginFrontRuntimeHost }, pluginDiscovery] = await Promise.all([
|
|
289
|
-
import("@hachej/boring-workspace/app/server"),
|
|
290
|
-
import("./pluginFrontRuntime.js"),
|
|
291
|
-
import("./pluginDiscovery.js")
|
|
292
|
-
]);
|
|
293
|
-
const diagnosticsStore = createRuntimePluginDiagnosticsStore();
|
|
294
|
-
const runtimeHost = await createPluginFrontRuntimeHost({
|
|
295
|
-
onDiagnostic: (diagnostic) => diagnosticsStore.record(diagnostic)
|
|
296
|
-
});
|
|
297
|
-
const pluginDirs = pluginDiscovery.resolveCliBoringPluginDirs(workspaceRoot);
|
|
298
|
-
const runtimeProvisioning = await provisionCliWorkspaceRuntime({
|
|
299
|
-
workspaceRoot,
|
|
300
|
-
mode: opts.mode,
|
|
301
|
-
provisionWorkspace: opts.provisionWorkspace,
|
|
302
|
-
plugins: readWorkspacePluginPackageRuntimePlugins(pluginDirs)
|
|
303
|
-
});
|
|
304
|
-
const app = await createWorkspaceAgentServer({
|
|
305
|
-
workspaceRoot,
|
|
306
|
-
mode: opts.mode,
|
|
307
|
-
logger: false,
|
|
308
|
-
provisionWorkspace: false,
|
|
309
|
-
runtimeProvisioning,
|
|
310
|
-
additionalBoringPluginDirs: pluginDirs,
|
|
311
|
-
boringPluginFrontTargetResolver: runtimeHost.createFrontTargetResolver(FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID),
|
|
312
|
-
boringPluginIncludeLegacyFrontUrl: false
|
|
313
|
-
});
|
|
314
|
-
await runtimeHost.registerRoutes(app);
|
|
315
|
-
const folderAssetManager = app.__boringAssetManager;
|
|
316
|
-
const closeFolderRuntimeCleanup = folderAssetManager?.subscribe((event) => {
|
|
317
|
-
syncRuntimeHostFromPluginEvents(runtimeHost, FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID, [event]);
|
|
318
|
-
});
|
|
319
|
-
if (closeFolderRuntimeCleanup) {
|
|
320
|
-
app.addHook("onClose", async () => {
|
|
321
|
-
closeFolderRuntimeCleanup();
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
app.get("/api/v1/runtime-plugin-diagnostics", async () => {
|
|
325
|
-
const manager = app.__boringAssetManager;
|
|
326
|
-
return buildRuntimePluginDiagnosticsResponse({
|
|
327
|
-
workspaceId: FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID,
|
|
328
|
-
loaded: manager?.inspectLoaded() ?? [],
|
|
329
|
-
errors: manager?.getErrors() ?? [],
|
|
330
|
-
host: diagnosticsStore.snapshot(FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID)
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
app.get("/api/v1/workspace/meta", async () => ({
|
|
334
|
-
workspaceId: "default",
|
|
335
|
-
workspaceRoot,
|
|
336
|
-
projectName,
|
|
337
|
-
version: CLI_VERSION,
|
|
338
|
-
runtimePluginFrontLoadingEnabled: true,
|
|
339
|
-
runtimePluginTrustLabel: RUNTIME_PLUGIN_TRUST_LABEL,
|
|
340
|
-
runtimePluginTrustDescription: RUNTIME_PLUGIN_TRUST_DESCRIPTION,
|
|
341
|
-
runtimePluginDiagnosticsEnabled: true
|
|
342
|
-
}));
|
|
343
|
-
return app;
|
|
344
|
-
}
|
|
345
86
|
async function startFolderMode(opts) {
|
|
346
87
|
const workspaceRoot = process.env.BORING_AGENT_WORKSPACE_ROOT ?? resolve(opts.folderArg ?? process.cwd());
|
|
347
88
|
const projectName = basename(resolve(workspaceRoot)) || "workspace";
|
|
@@ -367,307 +108,6 @@ ${projectName}`);
|
|
|
367
108
|
`);
|
|
368
109
|
openBrowser(url);
|
|
369
110
|
}
|
|
370
|
-
async function createWorkspacesModeApp(opts) {
|
|
371
|
-
const [workspaceAppServer, workspaceServer, agentServer, fastifyModule, { createPluginFrontRuntimeHost }, pluginDiscovery] = await Promise.all([
|
|
372
|
-
import("@hachej/boring-workspace/app/server"),
|
|
373
|
-
import("@hachej/boring-workspace/server"),
|
|
374
|
-
import("@hachej/boring-agent/server"),
|
|
375
|
-
import("fastify"),
|
|
376
|
-
import("./pluginFrontRuntime.js"),
|
|
377
|
-
import("./pluginDiscovery.js")
|
|
378
|
-
]);
|
|
379
|
-
const registry = createLocalWorkspaceRegistry(opts.registryPath);
|
|
380
|
-
const app = fastifyModule.default({ logger: false, bodyLimit: 16 * 1024 * 1024 });
|
|
381
|
-
const diagnosticsStore = createRuntimePluginDiagnosticsStore();
|
|
382
|
-
const runtimeHost = await createPluginFrontRuntimeHost({
|
|
383
|
-
onDiagnostic: (diagnostic) => diagnosticsStore.record(diagnostic)
|
|
384
|
-
});
|
|
385
|
-
await runtimeHost.registerRoutes(app);
|
|
386
|
-
const bridges = /* @__PURE__ */ new Map();
|
|
387
|
-
const workspaceEventClosers = /* @__PURE__ */ new Map();
|
|
388
|
-
const pluginRuntimes = /* @__PURE__ */ new Map();
|
|
389
|
-
const pluginPiSnapshots = /* @__PURE__ */ new Map();
|
|
390
|
-
const runtimeProvisioningByWorkspace = /* @__PURE__ */ new Map();
|
|
391
|
-
function getBridge(workspaceId) {
|
|
392
|
-
let bridge = bridges.get(workspaceId);
|
|
393
|
-
if (!bridge) {
|
|
394
|
-
bridge = workspaceServer.createInMemoryBridge();
|
|
395
|
-
bridges.set(workspaceId, bridge);
|
|
396
|
-
}
|
|
397
|
-
return bridge;
|
|
398
|
-
}
|
|
399
|
-
async function requireWorkspace(workspaceId) {
|
|
400
|
-
const workspace = await registry.get(workspaceId);
|
|
401
|
-
if (!workspace) throw httpError("unknown workspace", 404);
|
|
402
|
-
if (!workspace.available) throw httpError("workspace folder unavailable", 409);
|
|
403
|
-
return workspace;
|
|
404
|
-
}
|
|
405
|
-
async function workspaceFromRequest(request) {
|
|
406
|
-
return await requireWorkspace(resolveWorkspaceIdFromRequest(request));
|
|
407
|
-
}
|
|
408
|
-
function pluginRuntimeKey(workspace) {
|
|
409
|
-
return `${workspace.id}:${workspace.path}`;
|
|
410
|
-
}
|
|
411
|
-
function syncLoadedPluginPiSnapshot(workspace, manager) {
|
|
412
|
-
pluginPiSnapshots.set(pluginRuntimeKey(workspace), manager.inspectLoadedPiSnapshot());
|
|
413
|
-
}
|
|
414
|
-
function getOrCreatePluginRuntime(workspace) {
|
|
415
|
-
const key = pluginRuntimeKey(workspace);
|
|
416
|
-
let runtime = pluginRuntimes.get(key);
|
|
417
|
-
if (!runtime) {
|
|
418
|
-
const manager = pluginDiscovery.createCliPluginAssetManager(workspace.path, {
|
|
419
|
-
frontTargetResolver: runtimeHost.createFrontTargetResolver(workspace.id),
|
|
420
|
-
includeLegacyFrontUrl: false
|
|
421
|
-
});
|
|
422
|
-
runtime = {
|
|
423
|
-
manager,
|
|
424
|
-
ensureLoaded: manager.load().then(() => {
|
|
425
|
-
syncLoadedPluginPiSnapshot(workspace, manager);
|
|
426
|
-
})
|
|
427
|
-
};
|
|
428
|
-
pluginRuntimes.set(key, runtime);
|
|
429
|
-
}
|
|
430
|
-
return runtime;
|
|
431
|
-
}
|
|
432
|
-
async function getLoadedPluginRuntime(workspace) {
|
|
433
|
-
const runtime = getOrCreatePluginRuntime(workspace);
|
|
434
|
-
await runtime.ensureLoaded;
|
|
435
|
-
return runtime;
|
|
436
|
-
}
|
|
437
|
-
function getLoadedPluginPiSnapshot(workspace) {
|
|
438
|
-
return pluginPiSnapshots.get(pluginRuntimeKey(workspace)) ?? {
|
|
439
|
-
additionalSkillPaths: [],
|
|
440
|
-
packages: [],
|
|
441
|
-
extensionPaths: []
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
async function disposeWorkspaceRuntime(workspace) {
|
|
445
|
-
for (const close of workspaceEventClosers.get(workspace.id) ?? []) {
|
|
446
|
-
try {
|
|
447
|
-
close();
|
|
448
|
-
} catch {
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
workspaceEventClosers.delete(workspace.id);
|
|
452
|
-
const runtimeKey = pluginRuntimeKey(workspace);
|
|
453
|
-
pluginRuntimes.delete(runtimeKey);
|
|
454
|
-
pluginPiSnapshots.delete(runtimeKey);
|
|
455
|
-
runtimeProvisioningByWorkspace.delete(workspace.id);
|
|
456
|
-
bridges.delete(workspace.id);
|
|
457
|
-
diagnosticsStore.disposeWorkspace(workspace.id);
|
|
458
|
-
await runtimeHost.disposeWorkspace(workspace.id);
|
|
459
|
-
}
|
|
460
|
-
function reloadDiagnostics(scan) {
|
|
461
|
-
return scan.errors.map((error) => ({
|
|
462
|
-
source: "workspaces-plugin-manager",
|
|
463
|
-
message: error.message,
|
|
464
|
-
pluginId: error.id
|
|
465
|
-
}));
|
|
466
|
-
}
|
|
467
|
-
app.get("/api/v1/local-workspaces", async () => ({
|
|
468
|
-
workspaces: await registry.list()
|
|
469
|
-
}));
|
|
470
|
-
app.post("/api/v1/local-workspaces", async (request, reply) => {
|
|
471
|
-
const body = request.body;
|
|
472
|
-
if (typeof body?.path !== "string" || !body.path.trim()) {
|
|
473
|
-
return reply.code(400).send({ error: "workspace path is required" });
|
|
474
|
-
}
|
|
475
|
-
try {
|
|
476
|
-
const workspace = await registry.add(body.path, {
|
|
477
|
-
name: typeof body.name === "string" ? body.name : void 0
|
|
478
|
-
});
|
|
479
|
-
return { workspace };
|
|
480
|
-
} catch (error) {
|
|
481
|
-
return reply.code(400).send({
|
|
482
|
-
error: error instanceof Error ? error.message : "unable to add workspace"
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
});
|
|
486
|
-
app.delete("/api/v1/local-workspaces/:id", async (request, reply) => {
|
|
487
|
-
const { id } = request.params;
|
|
488
|
-
const workspace = await registry.get(id);
|
|
489
|
-
await registry.remove(id);
|
|
490
|
-
if (workspace) await disposeWorkspaceRuntime(workspace);
|
|
491
|
-
return reply.send({ ok: true });
|
|
492
|
-
});
|
|
493
|
-
app.get("/api/v1/workspaces", async () => ({
|
|
494
|
-
workspaces: (await registry.list()).map(toCoreWorkspace)
|
|
495
|
-
}));
|
|
496
|
-
app.get("/api/v1/workspaces/:id", async (request, reply) => {
|
|
497
|
-
const { id } = request.params;
|
|
498
|
-
const workspace = await registry.get(id);
|
|
499
|
-
if (!workspace) return reply.code(404).send({ error: "workspace not found" });
|
|
500
|
-
return { workspace: toCoreWorkspace(workspace), role: "owner" };
|
|
501
|
-
});
|
|
502
|
-
await app.register(agentServer.registerAgentRoutes, {
|
|
503
|
-
mode: opts.mode,
|
|
504
|
-
systemPromptAppend: workspaceAppServer.buildWorkspaceContextPrompt(),
|
|
505
|
-
getSystemPromptDynamic: async ({ workspaceId }) => {
|
|
506
|
-
const workspace = await requireWorkspace(workspaceId);
|
|
507
|
-
await getLoadedPluginRuntime(workspace);
|
|
508
|
-
return getLoadedPluginPiSnapshot(workspace).systemPromptAppend;
|
|
509
|
-
},
|
|
510
|
-
getWorkspaceId: async (request) => (await workspaceFromRequest(request)).id,
|
|
511
|
-
getWorkspaceRoot: async (workspaceId) => (await requireWorkspace(workspaceId)).path,
|
|
512
|
-
getSessionNamespace: async ({ workspaceId }) => `local-workspace-${workspaceId}`,
|
|
513
|
-
provisionRuntime: async ({ workspaceId, workspaceRoot, runtimeMode, runtimeLayout, provisioningAdapter }) => {
|
|
514
|
-
if (runtimeProvisioningByWorkspace.has(workspaceId)) {
|
|
515
|
-
return runtimeProvisioningByWorkspace.get(workspaceId);
|
|
516
|
-
}
|
|
517
|
-
const provisioned = await provisionCliWorkspaceRuntime({
|
|
518
|
-
workspaceRoot,
|
|
519
|
-
mode: runtimeMode,
|
|
520
|
-
provisionWorkspace: opts.provisionWorkspace,
|
|
521
|
-
adapter: provisioningAdapter,
|
|
522
|
-
runtimeLayout,
|
|
523
|
-
plugins: workspaceAppServer.readWorkspacePluginPackageRuntimePlugins(pluginDiscovery.resolveCliBoringPluginDirs(workspaceRoot))
|
|
524
|
-
});
|
|
525
|
-
runtimeProvisioningByWorkspace.set(workspaceId, provisioned);
|
|
526
|
-
return provisioned;
|
|
527
|
-
},
|
|
528
|
-
beforeReload: async ({ workspaceId }) => {
|
|
529
|
-
const workspace = await requireWorkspace(workspaceId);
|
|
530
|
-
const runtime = await getLoadedPluginRuntime(workspace);
|
|
531
|
-
const scan = await runtime.manager.load();
|
|
532
|
-
syncLoadedPluginPiSnapshot(workspace, runtime.manager);
|
|
533
|
-
syncRuntimeHostFromPluginEvents(runtimeHost, workspaceId, scan.events);
|
|
534
|
-
return {
|
|
535
|
-
restart_warnings: workspaceServer.collectRestartWarnings(scan.events),
|
|
536
|
-
diagnostics: reloadDiagnostics(scan)
|
|
537
|
-
};
|
|
538
|
-
},
|
|
539
|
-
getPi: async ({ workspaceId, workspaceRoot }) => {
|
|
540
|
-
const workspace = await requireWorkspace(workspaceId);
|
|
541
|
-
await getLoadedPluginRuntime(workspace);
|
|
542
|
-
return {
|
|
543
|
-
additionalSkillPaths: [join(workspaceRoot, ".agents", "skills")],
|
|
544
|
-
packages: [],
|
|
545
|
-
extensionPaths: [],
|
|
546
|
-
getHotReloadableResources: () => getLoadedPluginPiSnapshot(workspace)
|
|
547
|
-
};
|
|
548
|
-
},
|
|
549
|
-
getExtraTools: async ({ workspaceId, workspaceRoot, workspaceFsCapability }) => [
|
|
550
|
-
...workspaceServer.createWorkspaceUiTools(getBridge(workspaceId), {
|
|
551
|
-
workspaceRoot: workspaceFsCapability === "strong" ? workspaceRoot : void 0
|
|
552
|
-
})
|
|
553
|
-
]
|
|
554
|
-
});
|
|
555
|
-
await app.register(workspaceServer.uiRoutes, {
|
|
556
|
-
getWorkspaceId: async (request) => (await workspaceFromRequest(request)).id,
|
|
557
|
-
getBridge: async (request) => getBridge((await workspaceFromRequest(request)).id)
|
|
558
|
-
});
|
|
559
|
-
app.get("/api/v1/runtime-plugin-diagnostics", async (request) => {
|
|
560
|
-
const workspace = await workspaceFromRequest(request);
|
|
561
|
-
const runtime = await getLoadedPluginRuntime(workspace);
|
|
562
|
-
return buildRuntimePluginDiagnosticsResponse({
|
|
563
|
-
workspaceId: workspace.id,
|
|
564
|
-
loaded: runtime.manager.inspectLoaded(),
|
|
565
|
-
errors: runtime.manager.getErrors(),
|
|
566
|
-
host: diagnosticsStore.snapshot(workspace.id)
|
|
567
|
-
});
|
|
568
|
-
});
|
|
569
|
-
app.get("/api/v1/agent-plugins", async (request) => {
|
|
570
|
-
const workspace = await workspaceFromRequest(request);
|
|
571
|
-
const runtime = await getLoadedPluginRuntime(workspace);
|
|
572
|
-
return runtime.manager.list();
|
|
573
|
-
});
|
|
574
|
-
app.get("/api/v1/agent-plugins/:id/error", async (request, reply) => {
|
|
575
|
-
const workspace = await workspaceFromRequest(request);
|
|
576
|
-
const runtime = await getLoadedPluginRuntime(workspace);
|
|
577
|
-
const { id } = request.params;
|
|
578
|
-
const error = runtime.manager.getError(id);
|
|
579
|
-
if (error == null) return reply.code(404).send({ error: "not_found" });
|
|
580
|
-
return reply.type("text/plain").send(error);
|
|
581
|
-
});
|
|
582
|
-
app.get("/api/v1/agent-plugins/events", async (request, reply) => {
|
|
583
|
-
const workspace = await workspaceFromRequest(request);
|
|
584
|
-
const runtime = await getLoadedPluginRuntime(workspace);
|
|
585
|
-
const manager = runtime.manager;
|
|
586
|
-
reply.hijack();
|
|
587
|
-
const res = reply.raw;
|
|
588
|
-
res.statusCode = 200;
|
|
589
|
-
res.setHeader("Content-Type", "text/event-stream");
|
|
590
|
-
res.setHeader("Cache-Control", "no-cache, no-transform");
|
|
591
|
-
res.setHeader("Connection", "keep-alive");
|
|
592
|
-
res.setHeader("X-Accel-Buffering", "no");
|
|
593
|
-
res.flushHeaders?.();
|
|
594
|
-
const write = (eventName, payload) => {
|
|
595
|
-
try {
|
|
596
|
-
res.write(`event: ${eventName}
|
|
597
|
-
`);
|
|
598
|
-
res.write(`data: ${JSON.stringify(payload)}
|
|
599
|
-
|
|
600
|
-
`);
|
|
601
|
-
} catch {
|
|
602
|
-
}
|
|
603
|
-
};
|
|
604
|
-
const liveQueue = [];
|
|
605
|
-
let replaying = true;
|
|
606
|
-
const unsubscribe = manager.subscribe((event) => {
|
|
607
|
-
if (event.type === "boring.plugin.unload" || event.type === "boring.plugin.load" && !event.frontTarget) {
|
|
608
|
-
runtimeHost.untrackPlugin(workspace.id, event.id);
|
|
609
|
-
}
|
|
610
|
-
const payload = {
|
|
611
|
-
...event,
|
|
612
|
-
workspaceId: workspace.id,
|
|
613
|
-
replay: false
|
|
614
|
-
};
|
|
615
|
-
if (replaying) {
|
|
616
|
-
liveQueue.push({ eventName: event.type, payload });
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
write(event.type, payload);
|
|
620
|
-
});
|
|
621
|
-
for (const plugin of manager.list()) {
|
|
622
|
-
write("boring.plugin.load", {
|
|
623
|
-
type: "boring.plugin.load",
|
|
624
|
-
id: plugin.id,
|
|
625
|
-
boring: plugin.boring,
|
|
626
|
-
version: plugin.version,
|
|
627
|
-
revision: plugin.revision,
|
|
628
|
-
...plugin.frontTarget ? { frontTarget: plugin.frontTarget } : {},
|
|
629
|
-
workspaceId: workspace.id,
|
|
630
|
-
replay: true
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
write("boring.plugin.replay-complete", {
|
|
634
|
-
type: "boring.plugin.replay-complete",
|
|
635
|
-
workspaceId: workspace.id,
|
|
636
|
-
replay: true
|
|
637
|
-
});
|
|
638
|
-
replaying = false;
|
|
639
|
-
for (const event of liveQueue) write(event.eventName, event.payload);
|
|
640
|
-
const heartbeat = setInterval(() => {
|
|
641
|
-
try {
|
|
642
|
-
res.write(": heartbeat\n\n");
|
|
643
|
-
} catch {
|
|
644
|
-
}
|
|
645
|
-
}, 25e3);
|
|
646
|
-
const closeStream = () => {
|
|
647
|
-
clearInterval(heartbeat);
|
|
648
|
-
unsubscribe();
|
|
649
|
-
workspaceEventClosers.get(workspace.id)?.delete(closeStream);
|
|
650
|
-
try {
|
|
651
|
-
res.end();
|
|
652
|
-
} catch {
|
|
653
|
-
}
|
|
654
|
-
};
|
|
655
|
-
const closers = workspaceEventClosers.get(workspace.id) ?? /* @__PURE__ */ new Set();
|
|
656
|
-
closers.add(closeStream);
|
|
657
|
-
workspaceEventClosers.set(workspace.id, closers);
|
|
658
|
-
request.raw.on("close", closeStream);
|
|
659
|
-
});
|
|
660
|
-
app.get("/api/v1/workspace/meta", async () => ({
|
|
661
|
-
projectName: "Boring UI",
|
|
662
|
-
workspacesMode: true,
|
|
663
|
-
version: CLI_VERSION,
|
|
664
|
-
runtimePluginFrontLoadingEnabled: true,
|
|
665
|
-
runtimePluginTrustLabel: RUNTIME_PLUGIN_TRUST_LABEL,
|
|
666
|
-
runtimePluginTrustDescription: RUNTIME_PLUGIN_TRUST_DESCRIPTION,
|
|
667
|
-
runtimePluginDiagnosticsEnabled: true
|
|
668
|
-
}));
|
|
669
|
-
return app;
|
|
670
|
-
}
|
|
671
111
|
async function startWorkspacesMode(opts) {
|
|
672
112
|
const app = await createWorkspacesModeApp({ mode: opts.mode });
|
|
673
113
|
const registry = createLocalWorkspaceRegistry();
|
|
@@ -786,11 +226,11 @@ async function runCli(options) {
|
|
|
786
226
|
}
|
|
787
227
|
export {
|
|
788
228
|
createBoringUiCliRuntimePlugin,
|
|
789
|
-
createFolderModeApp,
|
|
790
|
-
createWorkspacesModeApp,
|
|
791
|
-
provisionCliWorkspaceRuntime,
|
|
229
|
+
createFolderModeApp2 as createFolderModeApp,
|
|
230
|
+
createWorkspacesModeApp2 as createWorkspacesModeApp,
|
|
231
|
+
provisionCliWorkspaceRuntime2 as provisionCliWorkspaceRuntime,
|
|
792
232
|
registerStatic,
|
|
793
|
-
resolveBoringUiCliPackageRoot,
|
|
233
|
+
resolveBoringUiCliPackageRoot2 as resolveBoringUiCliPackageRoot,
|
|
794
234
|
resolveBoringUiPluginCliPackageRoot,
|
|
795
235
|
runCli
|
|
796
236
|
};
|