@computesdk/loops 0.1.0 → 0.1.1
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/API.md +5 -2
- package/README.md +6 -1
- package/bin/loops.mjs +15 -2
- package/dist/assets/{abnfDiagram-VCTEODGH-DPUvPEpH.js → abnfDiagram-VCTEODGH-CceVg1jC.js} +1 -1
- package/dist/assets/{arc-C71M1We_.js → arc-CurbUKc7.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-C3PLmrM3.js → architectureDiagram-5GKGNRK7-BG6bMida.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-DFshwmKE.js → blockDiagram-I7D4REHJ-B11cpZbJ.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-Dh29QjGI.js → c4Diagram-7LVT6UL2-DiXBjlUf.js} +1 -1
- package/dist/assets/channel-Cy2zP8Uh.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-qZ7USlsH.js → chunk-2Q5K7J3B-DMtau_ZO.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-YQpRymiv.js → chunk-5VM5RSS4-CkDw9Dwr.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-D2O95m9O.js → chunk-F27PBJKO-DduXbVaR.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-CftSjfdl.js → chunk-IMKFNOWR-CA3FEpcX.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-_1wtK-EO.js → chunk-JWPE2WC7-_aPFiznH.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-DJ2Xmp8X.js → chunk-POPQ4Y6H-BJ1FxtOa.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-CAtSHcje.js → chunk-SVP7TREG-BISx4Qsv.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-1BASqzB5.js → chunk-TICWLB2K-jN0ScesT.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-CWs6lP_i.js → chunk-XXDRQBXY-Bp2w1xsm.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-BHTKitbG.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-BHTKitbG.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-9dCpEIjz.js → cose-bilkent-JH36ORCC-RJxH4pHE.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-EFt_R8HZ.js → cynefin-OW5HDTMX-RQb2d53E.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-BuManjb1.js → cynefinDiagram-5FMLGOSQ-BkWuDtvv.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-MovLAGVL.js → dagre-GXQ25YYZ-Dw_p1MY7.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-BiJw5LQ6.js → diagram-S7CK7UJ4-CNvafPnD.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-DRsX_xa6.js → diagram-UQ7AKVKN-Ca50sGck.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-B7m6F8Dm.js → diagram-VSXAHHWV-B9EkCAhx.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-x4wioeVA.js → diagram-VX7I27RA-Be2j3KkX.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-0KYjyvJF.js → diagram-Z3DM3KII-Iki9IPof.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-BZXmt3oU.js → ebnfDiagram-PWID7BFC-C_DacKX3.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-CrfcYtD6.js → erDiagram-RLTQ6QDP-CN090mdT.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-BqudKoEy.js → flowDiagram-HODETNUW-z9R5Hzp_.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-OBWjueb8.js → ganttDiagram-EL5Y4UJY-Ctsoo8fA.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-B30e199R.js → gitGraphDiagram-WWUBYQGX-BAfnTLBI.js} +1 -1
- package/dist/assets/{highlighted-body-JLMTFACH-DapGkHdb.js → highlighted-body-JLMTFACH-JGCpsH-e.js} +1 -1
- package/dist/assets/{index-C_bKTXmh.js → index-DkxpcXIf.js} +123 -123
- package/dist/assets/{index-hKoeppfe.css → index-NzHlk7dF.css} +1 -1
- package/dist/assets/{infoDiagram-27XIBGKW-DPwVvB4K.js → infoDiagram-27XIBGKW-BMe5_1z8.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-B0fvRdQL.js → ishikawaDiagram-5VMMS53U-BRSqbbUo.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-CZBLF7ld.js → journeyDiagram-3NMN7TZE-WkAorMt-.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-wTBJWh9T.js → kanban-definition-UXKFOSKX-aa6aiAN2.js} +1 -1
- package/dist/assets/{layout-DYBs-gVA.js → layout-BDC_N0BU.js} +1 -1
- package/dist/assets/{linear-DTE3zVP_.js → linear-CznrJ42T.js} +1 -1
- package/dist/assets/{mindmap-definition-YA3MSWOX-tV3HPpzU.js → mindmap-definition-YA3MSWOX-CW6ybBUC.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-B6Yznbb5.js → pegDiagram-XKGWAZYB-Dr3qzrfb.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-B6QW8pJH.js → pieDiagram-E7YTZNPT-BkgMYztr.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-DgHGTtj0.js → quadrantDiagram-AXDQQJYC-BTswMqBf.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-CgqVFEBK.js → railroadDiagram-O6MQD6OU-DVCTkg3c.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-FmXMY-6j.js → requirementDiagram-BXWQKSXE-DDSFlh61.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-AVMEYVKy.js → sankeyDiagram-P5KCCOFB-DHP89LFt.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-uOObReYi.js → sequenceDiagram-WJ2MYXX4-CUcSveH3.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-BPOPqqxk.js → sizeCapture-INFHLROL-fGlaZz_7.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-C1lXHNdu.js → stateDiagram-D77RDMKH-DqcVPNAG.js} +1 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-_t9xbbjh.js +1 -0
- package/dist/assets/{swimlanes-42K2YHIH-C52GOi05.js → swimlanes-42K2YHIH-L-pgkzCN.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-6eL6UTv7.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-T0BhAPL-.js → timeline-definition-24CTP7MA-C__pbpoz.js} +1 -1
- package/dist/assets/{vennDiagram-4TSXK5OY-DSGCn4iB.js → vennDiagram-4TSXK5OY-B4f-azPR.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-BaCYx_03.js → wardleyDiagram-VM6X3IG4-01ZelHA6.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-CgPwXvFv.js → xychartDiagram-S5SC5T6Z-CNfj8huj.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/server.mjs +265 -147
- package/package.json +1 -1
- package/dist/assets/channel-6AEG2wiQ.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-WtwW3JUM.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-WtwW3JUM.js +0 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-BHg8O_NZ.js +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-DLB_mWZL.js +0 -8
package/dist/server.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// server/index.ts
|
|
2
|
-
import
|
|
2
|
+
import fs8 from "node:fs";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import http from "node:http";
|
|
5
|
-
import
|
|
5
|
+
import path8 from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { execFile, execFileSync as execFileSync2, spawn as
|
|
7
|
+
import { execFile, execFileSync as execFileSync2, spawn as spawn6 } from "node:child_process";
|
|
8
8
|
|
|
9
9
|
// ../../node_modules/.pnpm/hono@4.13.14/node_modules/hono/dist/request/constants.js
|
|
10
10
|
var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
|
|
@@ -79,26 +79,26 @@ var throwNestingLimitExceeded = () => {
|
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
// ../../node_modules/.pnpm/hono@4.13.14/node_modules/hono/dist/utils/url.js
|
|
82
|
-
var splitPath = (
|
|
83
|
-
const paths =
|
|
82
|
+
var splitPath = (path9) => {
|
|
83
|
+
const paths = path9.split("/");
|
|
84
84
|
if (paths[0] === "") paths.shift();
|
|
85
85
|
return paths;
|
|
86
86
|
};
|
|
87
87
|
var splitRoutingPath = (routePath) => {
|
|
88
|
-
const { groups, path:
|
|
89
|
-
const paths = splitPath(
|
|
88
|
+
const { groups, path: path9 } = extractGroupsFromPath(routePath);
|
|
89
|
+
const paths = splitPath(path9);
|
|
90
90
|
return replaceGroupMarks(paths, groups);
|
|
91
91
|
};
|
|
92
|
-
var extractGroupsFromPath = (
|
|
92
|
+
var extractGroupsFromPath = (path9) => {
|
|
93
93
|
const groups = [];
|
|
94
|
-
|
|
94
|
+
path9 = path9.replace(/\{[^}]+\}/g, (match2, index) => {
|
|
95
95
|
const mark = `@${index}`;
|
|
96
96
|
groups.push([mark, match2]);
|
|
97
97
|
return mark;
|
|
98
98
|
});
|
|
99
99
|
return {
|
|
100
100
|
groups,
|
|
101
|
-
path:
|
|
101
|
+
path: path9
|
|
102
102
|
};
|
|
103
103
|
};
|
|
104
104
|
var replaceGroupMarks = (paths, groups) => {
|
|
@@ -161,8 +161,8 @@ var getPath = (request) => {
|
|
|
161
161
|
const queryIndex = url.indexOf("?", i);
|
|
162
162
|
const hashIndex = url.indexOf("#", i);
|
|
163
163
|
const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);
|
|
164
|
-
const
|
|
165
|
-
return tryDecodeURI(
|
|
164
|
+
const path9 = url.slice(start, end);
|
|
165
|
+
return tryDecodeURI(path9.includes("%25") ? path9.replace(/%25/g, "%2525") : path9);
|
|
166
166
|
} else if (charCode === 63 || charCode === 35) break;
|
|
167
167
|
}
|
|
168
168
|
return url.slice(start, i);
|
|
@@ -175,9 +175,9 @@ var mergePath = (base, sub, ...rest) => {
|
|
|
175
175
|
if (rest.length) sub = mergePath(sub, ...rest);
|
|
176
176
|
return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
|
|
177
177
|
};
|
|
178
|
-
var checkOptionalParameter = (
|
|
179
|
-
if (
|
|
180
|
-
const segments =
|
|
178
|
+
var checkOptionalParameter = (path9) => {
|
|
179
|
+
if (path9.charCodeAt(path9.length - 1) !== 63 || !path9.includes(":")) return null;
|
|
180
|
+
const segments = path9.split("/");
|
|
181
181
|
const results = [];
|
|
182
182
|
let basePath = "";
|
|
183
183
|
segments.forEach((segment) => {
|
|
@@ -288,9 +288,9 @@ var HonoRequest = class {
|
|
|
288
288
|
*/
|
|
289
289
|
path;
|
|
290
290
|
bodyCache = {};
|
|
291
|
-
constructor(request,
|
|
291
|
+
constructor(request, path9 = "/", matchResult = [[]]) {
|
|
292
292
|
this.raw = request;
|
|
293
|
-
this.path =
|
|
293
|
+
this.path = path9;
|
|
294
294
|
this.#matchResult = matchResult;
|
|
295
295
|
}
|
|
296
296
|
param(key) {
|
|
@@ -1020,8 +1020,8 @@ var Hono = class Hono2 {
|
|
|
1020
1020
|
return this;
|
|
1021
1021
|
};
|
|
1022
1022
|
});
|
|
1023
|
-
this.on = (method,
|
|
1024
|
-
for (const p of [
|
|
1023
|
+
this.on = (method, path9, ...handlers) => {
|
|
1024
|
+
for (const p of [path9].flat()) {
|
|
1025
1025
|
this.#path = p;
|
|
1026
1026
|
for (const m of [method].flat()) {
|
|
1027
1027
|
const methodName = m.toUpperCase();
|
|
@@ -1075,8 +1075,8 @@ var Hono = class Hono2 {
|
|
|
1075
1075
|
* app.route("/api", app2) // GET /api/user
|
|
1076
1076
|
* ```
|
|
1077
1077
|
*/
|
|
1078
|
-
route(
|
|
1079
|
-
const subApp = this.basePath(
|
|
1078
|
+
route(path9, app2) {
|
|
1079
|
+
const subApp = this.basePath(path9);
|
|
1080
1080
|
app2.routes.map((r) => {
|
|
1081
1081
|
let handler;
|
|
1082
1082
|
if (app2.errorHandler === errorHandler) handler = r.handler;
|
|
@@ -1101,9 +1101,9 @@ var Hono = class Hono2 {
|
|
|
1101
1101
|
* const api = new Hono().basePath('/api')
|
|
1102
1102
|
* ```
|
|
1103
1103
|
*/
|
|
1104
|
-
basePath(
|
|
1104
|
+
basePath(path9) {
|
|
1105
1105
|
const subApp = this.#clone();
|
|
1106
|
-
subApp._basePath = mergePath(this._basePath,
|
|
1106
|
+
subApp._basePath = mergePath(this._basePath, path9);
|
|
1107
1107
|
return subApp;
|
|
1108
1108
|
}
|
|
1109
1109
|
/**
|
|
@@ -1179,7 +1179,7 @@ var Hono = class Hono2 {
|
|
|
1179
1179
|
* })
|
|
1180
1180
|
* ```
|
|
1181
1181
|
*/
|
|
1182
|
-
mount(
|
|
1182
|
+
mount(path9, applicationHandler, options) {
|
|
1183
1183
|
let replaceRequest;
|
|
1184
1184
|
let optionHandler;
|
|
1185
1185
|
if (options) {
|
|
@@ -1202,7 +1202,7 @@ var Hono = class Hono2 {
|
|
|
1202
1202
|
return [c.env, executionContext];
|
|
1203
1203
|
};
|
|
1204
1204
|
replaceRequest ||= (() => {
|
|
1205
|
-
const mergedPath = mergePath(this._basePath,
|
|
1205
|
+
const mergedPath = mergePath(this._basePath, path9);
|
|
1206
1206
|
const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
|
|
1207
1207
|
return (request) => {
|
|
1208
1208
|
const url = new URL(request.url);
|
|
@@ -1215,18 +1215,18 @@ var Hono = class Hono2 {
|
|
|
1215
1215
|
if (res) return res;
|
|
1216
1216
|
await next();
|
|
1217
1217
|
};
|
|
1218
|
-
this.#addRoute("ALL", mergePath(
|
|
1218
|
+
this.#addRoute("ALL", mergePath(path9, "*"), handler);
|
|
1219
1219
|
return this;
|
|
1220
1220
|
}
|
|
1221
|
-
#addRoute(method,
|
|
1222
|
-
|
|
1221
|
+
#addRoute(method, path9, handler, baseRoutePath) {
|
|
1222
|
+
path9 = mergePath(this._basePath, path9);
|
|
1223
1223
|
const r = {
|
|
1224
1224
|
basePath: baseRoutePath !== void 0 ? mergePath(this._basePath, baseRoutePath) : this._basePath,
|
|
1225
|
-
path:
|
|
1225
|
+
path: path9,
|
|
1226
1226
|
method,
|
|
1227
1227
|
handler
|
|
1228
1228
|
};
|
|
1229
|
-
this.router.add(method,
|
|
1229
|
+
this.router.add(method, path9, [handler, r]);
|
|
1230
1230
|
this.routes.push(r);
|
|
1231
1231
|
}
|
|
1232
1232
|
#handleError(err, c) {
|
|
@@ -1235,10 +1235,10 @@ var Hono = class Hono2 {
|
|
|
1235
1235
|
}
|
|
1236
1236
|
#dispatch(request, executionCtx, env, method) {
|
|
1237
1237
|
if (method === "HEAD") return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
|
|
1238
|
-
const
|
|
1239
|
-
const matchResult = this.router.match(method,
|
|
1238
|
+
const path9 = this.getPath(request, { env });
|
|
1239
|
+
const matchResult = this.router.match(method, path9);
|
|
1240
1240
|
const c = new Context(request, {
|
|
1241
|
-
path:
|
|
1241
|
+
path: path9,
|
|
1242
1242
|
matchResult,
|
|
1243
1243
|
env,
|
|
1244
1244
|
executionCtx,
|
|
@@ -1326,19 +1326,19 @@ var createNullObject = () => /* @__PURE__ */ Object.create(null);
|
|
|
1326
1326
|
|
|
1327
1327
|
// ../../node_modules/.pnpm/hono@4.13.14/node_modules/hono/dist/router/reg-exp-router/matcher.js
|
|
1328
1328
|
var emptyParam = [];
|
|
1329
|
-
function match(method,
|
|
1329
|
+
function match(method, path9) {
|
|
1330
1330
|
const matchers = this.buildAllMatchers();
|
|
1331
|
-
const match2 = ((method2,
|
|
1331
|
+
const match2 = ((method2, path10) => {
|
|
1332
1332
|
const matcher = matchers[method2] || matchers["ALL"];
|
|
1333
|
-
const staticMatch = matcher[2][
|
|
1333
|
+
const staticMatch = matcher[2][path10];
|
|
1334
1334
|
if (staticMatch) return staticMatch;
|
|
1335
|
-
const match3 =
|
|
1335
|
+
const match3 = path10.match(matcher[0]);
|
|
1336
1336
|
if (!match3) return [[], emptyParam];
|
|
1337
1337
|
const index = match3.indexOf("", 1);
|
|
1338
1338
|
return [matcher[1][index], match3];
|
|
1339
1339
|
});
|
|
1340
1340
|
this.match = match2;
|
|
1341
|
-
return match2(method,
|
|
1341
|
+
return match2(method, path9);
|
|
1342
1342
|
}
|
|
1343
1343
|
|
|
1344
1344
|
// ../../node_modules/.pnpm/hono@4.13.14/node_modules/hono/dist/router/reg-exp-router/node.js
|
|
@@ -1428,14 +1428,14 @@ var Trie = class {
|
|
|
1428
1428
|
#root = new Node();
|
|
1429
1429
|
#index = 0;
|
|
1430
1430
|
paths = createNullObject();
|
|
1431
|
-
insert(
|
|
1431
|
+
insert(path9, isStatic) {
|
|
1432
1432
|
if (isStatic) {
|
|
1433
|
-
this.#root.insert(
|
|
1433
|
+
this.#root.insert(path9.split(""), 0, [], this.#context, true);
|
|
1434
1434
|
return;
|
|
1435
1435
|
}
|
|
1436
1436
|
const paramAssoc = [];
|
|
1437
1437
|
const groups = [];
|
|
1438
|
-
let markedPath =
|
|
1438
|
+
let markedPath = path9;
|
|
1439
1439
|
for (let i = 0; ; ) {
|
|
1440
1440
|
let replaced = false;
|
|
1441
1441
|
markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => {
|
|
@@ -1456,7 +1456,7 @@ var Trie = class {
|
|
|
1456
1456
|
}
|
|
1457
1457
|
}
|
|
1458
1458
|
this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false);
|
|
1459
|
-
this.paths[
|
|
1459
|
+
this.paths[path9] = [this.#index++, paramAssoc];
|
|
1460
1460
|
}
|
|
1461
1461
|
buildRegExp() {
|
|
1462
1462
|
let regexp = this.#root.buildRegExpStr();
|
|
@@ -1489,11 +1489,11 @@ var Trie = class {
|
|
|
1489
1489
|
|
|
1490
1490
|
// ../../node_modules/.pnpm/hono@4.13.14/node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1491
1491
|
var wildcardRegExpCache = createNullObject();
|
|
1492
|
-
function buildWildcardRegExp(
|
|
1493
|
-
return wildcardRegExpCache[
|
|
1492
|
+
function buildWildcardRegExp(path9) {
|
|
1493
|
+
return wildcardRegExpCache[path9] ??= new RegExp(`^${path9.replace(/\/:[^/{}]+(?:\{\[\^\/]\+})?(?=[/{]|$)|\/?\*$|([.\\+*[^\]$()?{}|])/g, (match2, metaChar) => metaChar ? `\\${metaChar}` : match2 === "/*" ? TAIL_WILDCARD_REG_EXP_STR : match2 === "*" ? ".*" : `/:${LABEL_REG_EXP_STR}`)}$`);
|
|
1494
1494
|
}
|
|
1495
|
-
function findMiddleware(middleware,
|
|
1496
|
-
for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) if (buildWildcardRegExp(k).test(
|
|
1495
|
+
function findMiddleware(middleware, path9) {
|
|
1496
|
+
for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) if (buildWildcardRegExp(k).test(path9)) return [...middleware[k]];
|
|
1497
1497
|
}
|
|
1498
1498
|
var RegExpRouter = class {
|
|
1499
1499
|
name = "RegExpRouter";
|
|
@@ -1505,14 +1505,14 @@ var RegExpRouter = class {
|
|
|
1505
1505
|
this.#routes = { ["ALL"]: createNullObject() };
|
|
1506
1506
|
this.#tries = { ["ALL"]: new Trie() };
|
|
1507
1507
|
}
|
|
1508
|
-
#insertPath(method,
|
|
1508
|
+
#insertPath(method, path9) {
|
|
1509
1509
|
try {
|
|
1510
|
-
this.#tries[method].insert(
|
|
1510
|
+
this.#tries[method].insert(path9, !/\*|\/:/.test(path9));
|
|
1511
1511
|
} catch (e) {
|
|
1512
|
-
throw e === PATH_ERROR ? new UnsupportedPathError(
|
|
1512
|
+
throw e === PATH_ERROR ? new UnsupportedPathError(path9) : e;
|
|
1513
1513
|
}
|
|
1514
1514
|
}
|
|
1515
|
-
add(method,
|
|
1515
|
+
add(method, path9, handler) {
|
|
1516
1516
|
const middleware = this.#middleware;
|
|
1517
1517
|
const routes = this.#routes;
|
|
1518
1518
|
if (!middleware) throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
|
|
@@ -1526,24 +1526,24 @@ var RegExpRouter = class {
|
|
|
1526
1526
|
}
|
|
1527
1527
|
}
|
|
1528
1528
|
}
|
|
1529
|
-
if (
|
|
1529
|
+
if (path9 === "/*") path9 = "*";
|
|
1530
1530
|
const methods = method === "ALL" ? Object.keys(middleware) : [method];
|
|
1531
|
-
if (/\*$/.test(
|
|
1532
|
-
const re = buildWildcardRegExp(
|
|
1533
|
-
for (const m of methods) if (!middleware[m][
|
|
1534
|
-
this.#insertPath(m,
|
|
1535
|
-
middleware[m][
|
|
1531
|
+
if (/\*$/.test(path9)) {
|
|
1532
|
+
const re = buildWildcardRegExp(path9);
|
|
1533
|
+
for (const m of methods) if (!middleware[m][path9]) {
|
|
1534
|
+
this.#insertPath(m, path9);
|
|
1535
|
+
middleware[m][path9] = findMiddleware(middleware[m], path9) || findMiddleware(middleware["ALL"], path9) || [];
|
|
1536
1536
|
}
|
|
1537
|
-
for (const handlerMap of [middleware, routes]) for (const m of methods) for (const p in handlerMap[m]) re.test(p) && handlerMap[m][p].push([handler,
|
|
1537
|
+
for (const handlerMap of [middleware, routes]) for (const m of methods) for (const p in handlerMap[m]) re.test(p) && handlerMap[m][p].push([handler, path9]);
|
|
1538
1538
|
return;
|
|
1539
1539
|
}
|
|
1540
|
-
const paths = checkOptionalParameter(
|
|
1541
|
-
for (const
|
|
1542
|
-
if (!routes[m][
|
|
1543
|
-
this.#insertPath(m,
|
|
1544
|
-
routes[m][
|
|
1540
|
+
const paths = checkOptionalParameter(path9) || [path9];
|
|
1541
|
+
for (const path10 of paths) for (const m of methods) {
|
|
1542
|
+
if (!routes[m][path10]) {
|
|
1543
|
+
this.#insertPath(m, path10);
|
|
1544
|
+
routes[m][path10] = findMiddleware(middleware[m], path10) || findMiddleware(middleware["ALL"], path10) || [];
|
|
1545
1545
|
}
|
|
1546
|
-
routes[m][
|
|
1546
|
+
routes[m][path10].push([handler, path10]);
|
|
1547
1547
|
}
|
|
1548
1548
|
}
|
|
1549
1549
|
match = match;
|
|
@@ -1561,11 +1561,11 @@ var RegExpRouter = class {
|
|
|
1561
1561
|
const staticMap = createNullObject();
|
|
1562
1562
|
const handlerData = [];
|
|
1563
1563
|
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
|
|
1564
|
-
for (const r of [middleware, routes]) for (const
|
|
1565
|
-
const handlers = r[
|
|
1566
|
-
const pathData = trie.paths[
|
|
1564
|
+
for (const r of [middleware, routes]) for (const path9 in r) {
|
|
1565
|
+
const handlers = r[path9];
|
|
1566
|
+
const pathData = trie.paths[path9];
|
|
1567
1567
|
if (!pathData) {
|
|
1568
|
-
staticMap[
|
|
1568
|
+
staticMap[path9] = [handlers.map(([h]) => [h, createNullObject()]), emptyParam];
|
|
1569
1569
|
continue;
|
|
1570
1570
|
}
|
|
1571
1571
|
handlerData[pathData[0]] = handlers.map(([h, handlerPath]) => [h, trie.paths[handlerPath][1].reduceRight((map, [key], i) => {
|
|
@@ -1589,15 +1589,15 @@ var SmartRouter = class {
|
|
|
1589
1589
|
constructor(init) {
|
|
1590
1590
|
this.#routers = init.routers;
|
|
1591
1591
|
}
|
|
1592
|
-
add(method,
|
|
1592
|
+
add(method, path9, handler) {
|
|
1593
1593
|
if (!this.#routes) throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
|
|
1594
1594
|
this.#routes.push([
|
|
1595
1595
|
method,
|
|
1596
|
-
|
|
1596
|
+
path9,
|
|
1597
1597
|
handler
|
|
1598
1598
|
]);
|
|
1599
1599
|
}
|
|
1600
|
-
match(method,
|
|
1600
|
+
match(method, path9) {
|
|
1601
1601
|
if (!this.#routes) throw new Error("Fatal error");
|
|
1602
1602
|
const routers = this.#routers;
|
|
1603
1603
|
const routes = this.#routes;
|
|
@@ -1608,7 +1608,7 @@ var SmartRouter = class {
|
|
|
1608
1608
|
const router = routers[i];
|
|
1609
1609
|
try {
|
|
1610
1610
|
for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) router.add(...routes[i2]);
|
|
1611
|
-
res = router.match(method,
|
|
1611
|
+
res = router.match(method, path9);
|
|
1612
1612
|
} catch (e) {
|
|
1613
1613
|
if (e instanceof UnsupportedPathError) continue;
|
|
1614
1614
|
throw e;
|
|
@@ -1637,9 +1637,9 @@ var Node3 = class Node4 {
|
|
|
1637
1637
|
#patterns = [];
|
|
1638
1638
|
#pattern;
|
|
1639
1639
|
#params = emptyParams;
|
|
1640
|
-
insert(method,
|
|
1640
|
+
insert(method, path9, handler) {
|
|
1641
1641
|
let curNode = this;
|
|
1642
|
-
const parts = splitRoutingPath(
|
|
1642
|
+
const parts = splitRoutingPath(path9);
|
|
1643
1643
|
const possibleKeys = /* @__PURE__ */ new Set();
|
|
1644
1644
|
let i = 0;
|
|
1645
1645
|
for (const p of parts) {
|
|
@@ -1675,11 +1675,11 @@ var Node3 = class Node4 {
|
|
|
1675
1675
|
}
|
|
1676
1676
|
}
|
|
1677
1677
|
}
|
|
1678
|
-
search(method,
|
|
1678
|
+
search(method, path9) {
|
|
1679
1679
|
const handlerSets = [];
|
|
1680
1680
|
this.#params = emptyParams;
|
|
1681
1681
|
let curNodes = [this];
|
|
1682
|
-
const parts = splitPath(
|
|
1682
|
+
const parts = splitPath(path9);
|
|
1683
1683
|
const curNodesQueue = [];
|
|
1684
1684
|
const len = parts.length;
|
|
1685
1685
|
let partOffsets = null;
|
|
@@ -1715,13 +1715,13 @@ var Node3 = class Node4 {
|
|
|
1715
1715
|
if (matcher !== true) {
|
|
1716
1716
|
if (!partOffsets) {
|
|
1717
1717
|
partOffsets = [];
|
|
1718
|
-
let offset =
|
|
1718
|
+
let offset = path9[0] === "/" ? 1 : 0;
|
|
1719
1719
|
for (let p = 0; p < len; p++) {
|
|
1720
1720
|
partOffsets[p] = offset;
|
|
1721
1721
|
offset += parts[p].length + 1;
|
|
1722
1722
|
}
|
|
1723
1723
|
}
|
|
1724
|
-
const restPathString =
|
|
1724
|
+
const restPathString = path9.slice(partOffsets[i]);
|
|
1725
1725
|
const m = matcher.exec(restPathString);
|
|
1726
1726
|
if (m) {
|
|
1727
1727
|
if (!Object.hasOwn(params, name)) params[name] = m[0];
|
|
@@ -1762,11 +1762,11 @@ var Node3 = class Node4 {
|
|
|
1762
1762
|
var TrieRouter = class {
|
|
1763
1763
|
name = "TrieRouter";
|
|
1764
1764
|
#node = new Node3();
|
|
1765
|
-
add(method,
|
|
1766
|
-
for (const result of checkOptionalParameter(
|
|
1765
|
+
add(method, path9, handler) {
|
|
1766
|
+
for (const result of checkOptionalParameter(path9) || [path9]) this.#node.insert(method, result, handler);
|
|
1767
1767
|
}
|
|
1768
|
-
match(method,
|
|
1769
|
-
return this.#node.search(method,
|
|
1768
|
+
match(method, path9) {
|
|
1769
|
+
return this.#node.search(method, path9);
|
|
1770
1770
|
}
|
|
1771
1771
|
};
|
|
1772
1772
|
|
|
@@ -3449,8 +3449,8 @@ import path3 from "node:path";
|
|
|
3449
3449
|
import { spawn as spawn2 } from "node:child_process";
|
|
3450
3450
|
var lives = /* @__PURE__ */ new Map();
|
|
3451
3451
|
var IDLE_MS = 10 * 6e4;
|
|
3452
|
-
function serveCommand(cwd,
|
|
3453
|
-
for (const f of [path3.join(cwd, ".pi", "loops.json"), path3.join(
|
|
3452
|
+
function serveCommand(cwd, agentDir3 = process.env.PI_AGENT_DIR ?? path3.join(process.env.HOME ?? "", ".pi", "agent")) {
|
|
3453
|
+
for (const f of [path3.join(cwd, ".pi", "loops.json"), path3.join(agentDir3, "loops.json")]) {
|
|
3454
3454
|
try {
|
|
3455
3455
|
const j = JSON.parse(fs3.readFileSync(f, "utf8"));
|
|
3456
3456
|
if (typeof j?.serve === "string" && j.serve.trim()) return j.serve.trim();
|
|
@@ -4085,11 +4085,101 @@ async function doctor(cwd) {
|
|
|
4085
4085
|
return { checks, verdict: fatal ? "will-fail" : checks.every((c) => c.ok) ? "ready" : "incomplete" };
|
|
4086
4086
|
}
|
|
4087
4087
|
|
|
4088
|
-
//
|
|
4088
|
+
// ../pi-loops/lib/credentials.ts
|
|
4089
4089
|
import fs6 from "node:fs";
|
|
4090
|
+
import os3 from "node:os";
|
|
4090
4091
|
import path6 from "node:path";
|
|
4092
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
4093
|
+
var PROVIDERS = [
|
|
4094
|
+
{ id: "particle", label: "Particle AI" },
|
|
4095
|
+
{ id: "morph", label: "Morph" },
|
|
4096
|
+
{ id: "openrouter", label: "OpenRouter" },
|
|
4097
|
+
{ id: "anthropic", label: "Anthropic" },
|
|
4098
|
+
{ id: "openai", label: "OpenAI" },
|
|
4099
|
+
{ id: "deepseek", label: "DeepSeek" }
|
|
4100
|
+
];
|
|
4101
|
+
var agentDir2 = () => process.env.PI_AGENT_DIR ?? path6.join(os3.homedir(), ".pi", "agent");
|
|
4102
|
+
var authFile = (dir = agentDir2()) => path6.join(dir, "auth.json");
|
|
4103
|
+
var envVar = (provider) => `${provider.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_API_KEY`;
|
|
4104
|
+
function credentialStatus(dir = agentDir2()) {
|
|
4105
|
+
let auth = {};
|
|
4106
|
+
try {
|
|
4107
|
+
const raw2 = fs6.readFileSync(authFile(dir), "utf8");
|
|
4108
|
+
auth = JSON.parse(raw2) ?? {};
|
|
4109
|
+
} catch {
|
|
4110
|
+
}
|
|
4111
|
+
return PROVIDERS.map((p) => {
|
|
4112
|
+
const env = envVar(p.id);
|
|
4113
|
+
const entry = auth?.[p.id];
|
|
4114
|
+
const inAuth = !!(entry && typeof entry === "object" && (entry.key || entry.type === "oauth"));
|
|
4115
|
+
return { id: p.id, label: p.label, env, configured: inAuth || !!process.env[env], source: inAuth ? "auth" : process.env[env] ? "env" : "none" };
|
|
4116
|
+
});
|
|
4117
|
+
}
|
|
4118
|
+
var chain = Promise.resolve();
|
|
4119
|
+
function queued(fn) {
|
|
4120
|
+
const next = chain.then(fn, fn);
|
|
4121
|
+
chain = next.catch(() => {
|
|
4122
|
+
});
|
|
4123
|
+
return next;
|
|
4124
|
+
}
|
|
4125
|
+
async function writeApiKey(provider, key, dir = agentDir2()) {
|
|
4126
|
+
const trimmed = key.trim();
|
|
4127
|
+
if (!trimmed) throw new Error("a key is required");
|
|
4128
|
+
if (!PROVIDERS.some((p) => p.id === provider)) throw new Error(`unknown provider "${provider}"`);
|
|
4129
|
+
return queued(async () => {
|
|
4130
|
+
const file = authFile(dir);
|
|
4131
|
+
let auth = {};
|
|
4132
|
+
try {
|
|
4133
|
+
const raw2 = fs6.readFileSync(file, "utf8");
|
|
4134
|
+
const parsed = raw2.trim() ? JSON.parse(raw2) : {};
|
|
4135
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) auth = parsed;
|
|
4136
|
+
} catch {
|
|
4137
|
+
}
|
|
4138
|
+
auth[provider] = { ...auth[provider] && typeof auth[provider] === "object" ? auth[provider] : {}, type: "api_key", key: trimmed };
|
|
4139
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
4140
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
4141
|
+
fs6.writeFileSync(tmp, JSON.stringify(auth, null, 2) + "\n", { mode: 384 });
|
|
4142
|
+
fs6.renameSync(tmp, file);
|
|
4143
|
+
try {
|
|
4144
|
+
fs6.chmodSync(file, 384);
|
|
4145
|
+
} catch {
|
|
4146
|
+
}
|
|
4147
|
+
return file;
|
|
4148
|
+
});
|
|
4149
|
+
}
|
|
4150
|
+
async function verifyModel({ pi, model, cwd, timeoutMs = 6e4 }) {
|
|
4151
|
+
const args = ["--print", "--no-session", "--model", model, "Reply with the single word: ok"];
|
|
4152
|
+
return new Promise((resolve) => {
|
|
4153
|
+
let out = "";
|
|
4154
|
+
const child = spawn5(pi, args, { cwd, stdio: ["ignore", "pipe", "ignore"] });
|
|
4155
|
+
const timer = setTimeout(() => {
|
|
4156
|
+
try {
|
|
4157
|
+
child.kill("SIGKILL");
|
|
4158
|
+
} catch {
|
|
4159
|
+
}
|
|
4160
|
+
resolve({ ok: false, detail: `no answer in ${Math.round(timeoutMs / 1e3)}s` });
|
|
4161
|
+
}, timeoutMs);
|
|
4162
|
+
child.stdout.on("data", (b) => {
|
|
4163
|
+
out += String(b);
|
|
4164
|
+
});
|
|
4165
|
+
child.on("error", (e) => {
|
|
4166
|
+
clearTimeout(timer);
|
|
4167
|
+
resolve({ ok: false, detail: `could not run ${pi}: ${e.message}` });
|
|
4168
|
+
});
|
|
4169
|
+
child.on("close", (code) => {
|
|
4170
|
+
clearTimeout(timer);
|
|
4171
|
+
const said = out.split("\n").map((l) => l.trim()).filter(Boolean).slice(-2).join(" \xB7 ").slice(-160);
|
|
4172
|
+
if (code === 0 && /\bok\b/i.test(said)) return resolve({ ok: true, detail: `answered: ${said}` });
|
|
4173
|
+
resolve({ ok: false, detail: `exit ${code ?? "?"}${said ? ` \u2014 ${said}` : ""}`.trim() });
|
|
4174
|
+
});
|
|
4175
|
+
});
|
|
4176
|
+
}
|
|
4177
|
+
|
|
4178
|
+
// server/workspaces.ts
|
|
4179
|
+
import fs7 from "node:fs";
|
|
4180
|
+
import path7 from "node:path";
|
|
4091
4181
|
import { execFileSync } from "node:child_process";
|
|
4092
|
-
var slug = (p) =>
|
|
4182
|
+
var slug = (p) => path7.basename(p).replace(/[^a-z0-9._-]+/gi, "-").toLowerCase() || "repo";
|
|
4093
4183
|
var Workspaces = class {
|
|
4094
4184
|
/** Where workspaces.json and the per-repo session dirs live. */
|
|
4095
4185
|
root;
|
|
@@ -4099,15 +4189,15 @@ var Workspaces = class {
|
|
|
4099
4189
|
chatMap;
|
|
4100
4190
|
constructor({ root, cwd }) {
|
|
4101
4191
|
this.root = root;
|
|
4102
|
-
this.file =
|
|
4103
|
-
|
|
4192
|
+
this.file = path7.join(root, "workspaces.json");
|
|
4193
|
+
fs7.mkdirSync(root, { recursive: true });
|
|
4104
4194
|
let data = { workspaces: [], chats: {} };
|
|
4105
|
-
if (
|
|
4195
|
+
if (fs7.existsSync(this.file)) {
|
|
4106
4196
|
try {
|
|
4107
|
-
data = JSON.parse(
|
|
4197
|
+
data = JSON.parse(fs7.readFileSync(this.file, "utf8"));
|
|
4108
4198
|
} catch {
|
|
4109
4199
|
try {
|
|
4110
|
-
|
|
4200
|
+
fs7.renameSync(this.file, `${this.file}.corrupt-${Date.now()}`);
|
|
4111
4201
|
} catch {
|
|
4112
4202
|
}
|
|
4113
4203
|
console.error(`[workspaces] could not read ${this.file}; kept a copy and rebuilding from sessions`);
|
|
@@ -4124,8 +4214,8 @@ var Workspaces = class {
|
|
|
4124
4214
|
this.data.chats = Object.fromEntries(this.chatMap);
|
|
4125
4215
|
try {
|
|
4126
4216
|
const tmp = `${this.file}.tmp`;
|
|
4127
|
-
|
|
4128
|
-
|
|
4217
|
+
fs7.writeFileSync(tmp, JSON.stringify(this.data, null, 2) + "\n");
|
|
4218
|
+
fs7.renameSync(tmp, this.file);
|
|
4129
4219
|
} catch {
|
|
4130
4220
|
}
|
|
4131
4221
|
}
|
|
@@ -4140,18 +4230,18 @@ var Workspaces = class {
|
|
|
4140
4230
|
}
|
|
4141
4231
|
/** Add (or re-find) a repo. `persist: false` is used for the startup cwd. */
|
|
4142
4232
|
add(dir, { persist = true } = {}) {
|
|
4143
|
-
const abs =
|
|
4233
|
+
const abs = path7.resolve(dir);
|
|
4144
4234
|
const existing = [...this.byId.values()].find((w) => w.path === abs);
|
|
4145
4235
|
if (existing) return existing;
|
|
4146
|
-
if (!
|
|
4147
|
-
const st =
|
|
4236
|
+
if (!fs7.existsSync(abs) || !fs7.statSync(abs).isDirectory()) throw new Error(`${abs} is not a directory`);
|
|
4237
|
+
const st = fs7.statSync(abs);
|
|
4148
4238
|
if (!st.isDirectory()) throw new Error(`${abs} is not a directory`);
|
|
4149
4239
|
let id = slug(abs);
|
|
4150
4240
|
for (let i = 2; this.byId.has(id); i++) id = `${slug(abs)}-${i}`;
|
|
4151
|
-
const legacy =
|
|
4152
|
-
const sessionDir =
|
|
4153
|
-
const ws = { id, path: abs, sessionDir, title:
|
|
4154
|
-
|
|
4241
|
+
const legacy = path7.join(abs, ".pi", "loops-ui", "sessions");
|
|
4242
|
+
const sessionDir = fs7.existsSync(legacy) ? legacy : path7.join(this.root, "workspaces", id, "sessions");
|
|
4243
|
+
const ws = { id, path: abs, sessionDir, title: path7.basename(abs) || abs };
|
|
4244
|
+
fs7.mkdirSync(ws.sessionDir, { recursive: true });
|
|
4155
4245
|
this.byId.set(id, ws);
|
|
4156
4246
|
if (persist) {
|
|
4157
4247
|
this.data.workspaces = [...this.data.workspaces.filter((w) => w.path !== abs), { id, path: abs }];
|
|
@@ -4175,25 +4265,25 @@ var Workspaces = class {
|
|
|
4175
4265
|
* workspace. This runs on startup, so a lost or stale `workspaces.json` is a cache miss, not lost history.
|
|
4176
4266
|
*/
|
|
4177
4267
|
recoverSessions() {
|
|
4178
|
-
const root =
|
|
4268
|
+
const root = path7.join(this.root, "workspaces");
|
|
4179
4269
|
let ids = [];
|
|
4180
4270
|
try {
|
|
4181
|
-
ids =
|
|
4271
|
+
ids = fs7.readdirSync(root);
|
|
4182
4272
|
} catch {
|
|
4183
4273
|
return;
|
|
4184
4274
|
}
|
|
4185
4275
|
for (const id of ids) {
|
|
4186
|
-
const dir =
|
|
4276
|
+
const dir = path7.join(root, id, "sessions");
|
|
4187
4277
|
let files = [];
|
|
4188
4278
|
try {
|
|
4189
|
-
files =
|
|
4279
|
+
files = fs7.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
|
|
4190
4280
|
} catch {
|
|
4191
4281
|
continue;
|
|
4192
4282
|
}
|
|
4193
4283
|
let cwd = null;
|
|
4194
4284
|
for (const f of files) {
|
|
4195
4285
|
try {
|
|
4196
|
-
const head = JSON.parse(
|
|
4286
|
+
const head = JSON.parse(fs7.readFileSync(path7.join(dir, f), "utf8").split("\n")[0]);
|
|
4197
4287
|
if (typeof head?.cwd === "string" && head.cwd) {
|
|
4198
4288
|
cwd = head.cwd;
|
|
4199
4289
|
break;
|
|
@@ -4248,8 +4338,8 @@ var Workspaces = class {
|
|
|
4248
4338
|
};
|
|
4249
4339
|
|
|
4250
4340
|
// server/index.ts
|
|
4251
|
-
var HERE =
|
|
4252
|
-
var APP_ROOT =
|
|
4341
|
+
var HERE = path8.dirname(fileURLToPath(import.meta.url));
|
|
4342
|
+
var APP_ROOT = path8.resolve(HERE, "..");
|
|
4253
4343
|
var arg = (name, fallback) => {
|
|
4254
4344
|
const i = process.argv.indexOf(`--${name}`);
|
|
4255
4345
|
return i >= 0 ? process.argv[i + 1] : process.env[`LOOPS_UI_${name.toUpperCase().replace(/-/g, "_")}`] ?? fallback;
|
|
@@ -4257,16 +4347,16 @@ var arg = (name, fallback) => {
|
|
|
4257
4347
|
var PORT = Number(arg("port", "8001"));
|
|
4258
4348
|
var HOST = arg("host", "127.0.0.1");
|
|
4259
4349
|
var LOOPBACK = ["127.0.0.1", "localhost", "::1"].includes(HOST);
|
|
4260
|
-
var CWD =
|
|
4350
|
+
var CWD = path8.resolve(arg("cwd", process.cwd()));
|
|
4261
4351
|
var DEV = process.argv.includes("--dev") || process.env.NODE_ENV === "development";
|
|
4262
4352
|
var VITE = arg("vite", "http://localhost:5173");
|
|
4263
4353
|
var PI = arg("pi", "pi");
|
|
4264
|
-
var DATA =
|
|
4354
|
+
var DATA = path8.resolve(arg("data", process.env.LOOPS_UI_DATA ?? path8.join(process.env.HOME ?? process.cwd(), ".pi", "loops-ui")));
|
|
4265
4355
|
var MODEL = arg("model", void 0) || null;
|
|
4266
|
-
var EXTENSIONS = (arg("extensions",
|
|
4356
|
+
var EXTENSIONS = (arg("extensions", path8.resolve(APP_ROOT, "../pi-loops/extensions/loop.ts")) ?? "").split(",").filter(Boolean);
|
|
4267
4357
|
function prerequisites() {
|
|
4268
4358
|
const out = [];
|
|
4269
|
-
const onPath = PI.includes("/") || /^[A-Za-z]:\\/.test(PI) ?
|
|
4359
|
+
const onPath = PI.includes("/") || /^[A-Za-z]:\\/.test(PI) ? fs8.existsSync(PI) : (() => {
|
|
4270
4360
|
try {
|
|
4271
4361
|
execFileSync2(process.platform === "win32" ? "where" : "which", [PI], { stdio: ["ignore", "pipe", "ignore"] });
|
|
4272
4362
|
return true;
|
|
@@ -4276,18 +4366,18 @@ function prerequisites() {
|
|
|
4276
4366
|
})();
|
|
4277
4367
|
if (!onPath) out.push({ name: "pi", detail: `\`${PI}\` is not on PATH`, fix: "install Pi, or pass --pi <path to the pi binary>" });
|
|
4278
4368
|
for (const e of EXTENSIONS) {
|
|
4279
|
-
if (!
|
|
4369
|
+
if (!fs8.existsSync(e)) out.push({ name: "extension", detail: `${e} does not exist`, fix: "pi install npm:@computesdk/pi-loops, or pass --extensions <path to extensions/loop.ts>" });
|
|
4280
4370
|
}
|
|
4281
4371
|
return out;
|
|
4282
4372
|
}
|
|
4283
4373
|
var PROBLEMS = prerequisites();
|
|
4284
|
-
|
|
4374
|
+
fs8.mkdirSync(DATA, { recursive: true });
|
|
4285
4375
|
var TOKEN = (() => {
|
|
4286
4376
|
const explicit = arg("token", void 0);
|
|
4287
|
-
const file =
|
|
4377
|
+
const file = path8.join(DATA, "token");
|
|
4288
4378
|
const stored = (() => {
|
|
4289
4379
|
try {
|
|
4290
|
-
return
|
|
4380
|
+
return fs8.readFileSync(file, "utf8").trim() || null;
|
|
4291
4381
|
} catch {
|
|
4292
4382
|
return null;
|
|
4293
4383
|
}
|
|
@@ -4295,7 +4385,7 @@ var TOKEN = (() => {
|
|
|
4295
4385
|
if (explicit) {
|
|
4296
4386
|
if (!LOOPBACK) {
|
|
4297
4387
|
try {
|
|
4298
|
-
|
|
4388
|
+
fs8.writeFileSync(file, explicit + "\n", { mode: 384 });
|
|
4299
4389
|
} catch {
|
|
4300
4390
|
}
|
|
4301
4391
|
}
|
|
@@ -4304,7 +4394,7 @@ var TOKEN = (() => {
|
|
|
4304
4394
|
if (stored) return stored;
|
|
4305
4395
|
if (LOOPBACK) return null;
|
|
4306
4396
|
const made = randomBytes(24).toString("base64url");
|
|
4307
|
-
|
|
4397
|
+
fs8.writeFileSync(file, made + "\n", { mode: 384 });
|
|
4308
4398
|
return made;
|
|
4309
4399
|
})();
|
|
4310
4400
|
if (!LOOPBACK && !TOKEN) {
|
|
@@ -4324,7 +4414,7 @@ var manager = new ChatManager({
|
|
|
4324
4414
|
});
|
|
4325
4415
|
var wsOf = (chat) => chat ? workspaces.ofChat(chat) : workspaces.get();
|
|
4326
4416
|
var wsOfRun = (run3) => {
|
|
4327
|
-
const byIndex = workspaces.list().find((w) =>
|
|
4417
|
+
const byIndex = workspaces.list().find((w) => fs8.existsSync(path8.join(w.path, STATE_DIR, `${run3}.json`)));
|
|
4328
4418
|
if (byIndex) return byIndex;
|
|
4329
4419
|
const chat = manager.findRun(run3);
|
|
4330
4420
|
return chat ? workspaces.ofChat(chat.id) : workspaces.get();
|
|
@@ -4352,7 +4442,7 @@ var watchOf = (wsId) => {
|
|
|
4352
4442
|
if (!w) {
|
|
4353
4443
|
const read = () => {
|
|
4354
4444
|
try {
|
|
4355
|
-
return JSON.parse(
|
|
4445
|
+
return JSON.parse(fs8.readFileSync(path8.join(ws.path, ".pi", "loops.json"), "utf8"));
|
|
4356
4446
|
} catch {
|
|
4357
4447
|
return {};
|
|
4358
4448
|
}
|
|
@@ -4360,7 +4450,7 @@ var watchOf = (wsId) => {
|
|
|
4360
4450
|
w = { cwd: ws.path, accepted: read() };
|
|
4361
4451
|
watches.set(ws.id, w);
|
|
4362
4452
|
try {
|
|
4363
|
-
|
|
4453
|
+
fs8.watch(path8.join(ws.path, ".pi"), (_e, name) => {
|
|
4364
4454
|
if (name && name !== "loops.json") return;
|
|
4365
4455
|
clearTimeout(watchTimers.get(ws.id));
|
|
4366
4456
|
watchTimers.set(ws.id, setTimeout(() => checkConfig(ws.id), 120));
|
|
@@ -4382,10 +4472,10 @@ function notifyProposals() {
|
|
|
4382
4472
|
}
|
|
4383
4473
|
function checkConfig(wsId) {
|
|
4384
4474
|
const w = watchOf(wsId);
|
|
4385
|
-
const CONFIG =
|
|
4475
|
+
const CONFIG = path8.join(w.cwd, ".pi", "loops.json");
|
|
4386
4476
|
const raw2 = (() => {
|
|
4387
4477
|
try {
|
|
4388
|
-
return
|
|
4478
|
+
return fs8.readFileSync(CONFIG, "utf8");
|
|
4389
4479
|
} catch {
|
|
4390
4480
|
return "";
|
|
4391
4481
|
}
|
|
@@ -4406,16 +4496,29 @@ function checkConfig(wsId) {
|
|
|
4406
4496
|
notifyProposals();
|
|
4407
4497
|
}
|
|
4408
4498
|
var inside = (root, p) => {
|
|
4409
|
-
const abs =
|
|
4410
|
-
return abs === root || abs.startsWith(root +
|
|
4499
|
+
const abs = path8.resolve(root, p);
|
|
4500
|
+
return abs === root || abs.startsWith(root + path8.sep) ? abs : null;
|
|
4411
4501
|
};
|
|
4412
4502
|
var stateOf = (run3) => manager.findRun(run3)?.stateFor(run3) ?? readState(wsOfRun(run3).path, run3);
|
|
4413
4503
|
var app = new Hono3();
|
|
4504
|
+
var OWN_HOSTS = /* @__PURE__ */ new Set([`127.0.0.1:${PORT}`, `localhost:${PORT}`, `[::1]:${PORT}`]);
|
|
4505
|
+
var OWN_ORIGINS = new Set([...OWN_HOSTS].map((h) => `http://${h}`));
|
|
4414
4506
|
var ALLOWED_ORIGINS = (arg("allow-origin", "") ?? "").split(",").map((o) => o.trim()).filter(Boolean);
|
|
4415
|
-
var originAllowed = (origin) =>
|
|
4507
|
+
var originAllowed = (origin) => {
|
|
4508
|
+
if (ALLOWED_ORIGINS.includes("*")) return true;
|
|
4509
|
+
if (ALLOWED_ORIGINS.length) return ALLOWED_ORIGINS.includes(origin);
|
|
4510
|
+
return !LOOPBACK || OWN_ORIGINS.has(origin);
|
|
4511
|
+
};
|
|
4416
4512
|
app.use("*", async (c, next) => {
|
|
4513
|
+
const host = c.req.header("host") ?? "";
|
|
4514
|
+
if (LOOPBACK && !OWN_HOSTS.has(host)) {
|
|
4515
|
+
return c.text(`this server answers as ${[...OWN_HOSTS].join(", ")}, not "${host}"`, 403);
|
|
4516
|
+
}
|
|
4417
4517
|
const origin = c.req.header("origin");
|
|
4418
|
-
if (origin && originAllowed(origin)) {
|
|
4518
|
+
if (origin && !originAllowed(origin)) {
|
|
4519
|
+
return c.json({ error: "origin not allowed", origin }, 403);
|
|
4520
|
+
}
|
|
4521
|
+
if (origin) {
|
|
4419
4522
|
c.header("access-control-allow-origin", origin);
|
|
4420
4523
|
c.header("access-control-allow-headers", "authorization, content-type, last-event-id");
|
|
4421
4524
|
c.header("access-control-allow-methods", "GET, POST, OPTIONS");
|
|
@@ -4438,6 +4541,21 @@ app.use("/api/v1/*", async (c, next) => {
|
|
|
4438
4541
|
});
|
|
4439
4542
|
var api = new Hono3();
|
|
4440
4543
|
api.get("/health", (c) => c.json({ ok: PROBLEMS.length === 0, problems: PROBLEMS, cwd: CWD, data: DATA, pi: PI, extensions: EXTENSIONS, workspaces: workspaces.list() }));
|
|
4544
|
+
api.get("/credentials", (c) => c.json({ dir: agentDir2(), providers: credentialStatus() }));
|
|
4545
|
+
api.post("/credentials", async (c) => {
|
|
4546
|
+
const body = await c.req.json().catch(() => ({}));
|
|
4547
|
+
const provider = String(body?.provider ?? "");
|
|
4548
|
+
const key = String(body?.key ?? "").trim();
|
|
4549
|
+
if (!PROVIDERS.some((p) => p.id === provider)) return c.json({ error: `unknown provider "${provider}" \u2014 one of ${PROVIDERS.map((p) => p.id).join(", ")}` }, 400);
|
|
4550
|
+
if (!key) return c.json({ error: "a key is required" }, 400);
|
|
4551
|
+
try {
|
|
4552
|
+
const file = await writeApiKey(provider, key);
|
|
4553
|
+
const verified = body?.model ? await verifyModel({ pi: PI, model: String(body.model), cwd: CWD }) : null;
|
|
4554
|
+
return c.json({ ok: true, provider, file, configured: credentialStatus().find((s) => s.id === provider)?.configured ?? true, verified });
|
|
4555
|
+
} catch (e) {
|
|
4556
|
+
return c.json({ error: String(e.message ?? e).slice(0, 200) }, 400);
|
|
4557
|
+
}
|
|
4558
|
+
});
|
|
4441
4559
|
api.get("/chats", (c) => c.json(chatsOf(c.req.query("ws"))));
|
|
4442
4560
|
api.get("/chats/:id/events", async (c) => {
|
|
4443
4561
|
const id = c.req.param("id");
|
|
@@ -4480,9 +4598,9 @@ api.post("/chat/delete", async (c) => {
|
|
|
4480
4598
|
const file = chat?.sessionFile ?? null;
|
|
4481
4599
|
await manager.close(String(id));
|
|
4482
4600
|
workspaces.unbindChat(String(id));
|
|
4483
|
-
if (file &&
|
|
4601
|
+
if (file && fs8.existsSync(file)) {
|
|
4484
4602
|
try {
|
|
4485
|
-
|
|
4603
|
+
fs8.rmSync(file);
|
|
4486
4604
|
} catch {
|
|
4487
4605
|
}
|
|
4488
4606
|
}
|
|
@@ -4492,9 +4610,9 @@ api.get("/workspaces", (c) => c.json(workspaces.list()));
|
|
|
4492
4610
|
api.post("/workspaces", async (c) => {
|
|
4493
4611
|
const body = await c.req.json().catch(() => ({}));
|
|
4494
4612
|
try {
|
|
4495
|
-
const dir =
|
|
4613
|
+
const dir = path8.resolve(String(body.path ?? "").replace(/^~(?=\/|$)/, process.env.HOME ?? "~"));
|
|
4496
4614
|
if (!dir || dir === "/") throw new Error("a path is required");
|
|
4497
|
-
if (!
|
|
4615
|
+
if (!fs8.existsSync(dir) || !fs8.statSync(dir).isDirectory()) throw new Error(`${dir} is not a directory`);
|
|
4498
4616
|
const ws = workspaces.add(dir);
|
|
4499
4617
|
watchOf(ws.id);
|
|
4500
4618
|
return c.json({ ok: true, workspace: ws, loopable: Workspaces.loopable(ws.path) });
|
|
@@ -4578,7 +4696,7 @@ api.get("/session", async (c) => {
|
|
|
4578
4696
|
if (!run3 || !name) return c.json([]);
|
|
4579
4697
|
const state = stateOf(run3);
|
|
4580
4698
|
const file = state?.rounds?.flatMap((r) => r.attempts).find((a) => a.label === name)?.session ?? state?.rounds?.flatMap((r) => r.attempts).find((a) => a.label?.toLowerCase() === name.toLowerCase())?.session;
|
|
4581
|
-
if (!file || !
|
|
4699
|
+
if (!file || !fs8.existsSync(file)) return c.json([]);
|
|
4582
4700
|
const chat = await manager.open(c.req.query("chat") ?? manager.list()[0]?.id ?? "");
|
|
4583
4701
|
return c.json(chat.sessionItems(file));
|
|
4584
4702
|
});
|
|
@@ -4597,21 +4715,21 @@ api.get("/shot", (c) => {
|
|
|
4597
4715
|
const repo = wsOfRun(run3).path;
|
|
4598
4716
|
if (file.startsWith(STATE_DIR)) {
|
|
4599
4717
|
const abs2 = inside(repo, file);
|
|
4600
|
-
if (!abs2 || !
|
|
4601
|
-
return new Response(
|
|
4718
|
+
if (!abs2 || !fs8.existsSync(abs2)) return c.notFound();
|
|
4719
|
+
return new Response(fs8.readFileSync(abs2), { headers: { "content-type": abs2.endsWith(".jpg") || abs2.endsWith(".jpeg") ? "image/jpeg" : "image/png", "cache-control": "no-cache" } });
|
|
4602
4720
|
}
|
|
4603
4721
|
const dir = attempt === "CURRENT" ? state?.best?.dir ?? null : state?.rounds?.flatMap((r) => r.attempts).find((a) => a.label === attempt || a.label?.toLowerCase() === attempt.toLowerCase())?.dir;
|
|
4604
4722
|
if (!dir) return c.notFound();
|
|
4605
|
-
const abs = inside(repo,
|
|
4606
|
-
if (!abs || !
|
|
4607
|
-
return new Response(
|
|
4723
|
+
const abs = inside(repo, path8.join(dir, file));
|
|
4724
|
+
if (!abs || !fs8.existsSync(abs)) return c.notFound();
|
|
4725
|
+
return new Response(fs8.readFileSync(abs), { headers: { "content-type": abs.endsWith(".jpg") || abs.endsWith(".jpeg") ? "image/jpeg" : "image/png", "cache-control": "no-cache" } });
|
|
4608
4726
|
});
|
|
4609
4727
|
api.get("/sheet", (c) => {
|
|
4610
4728
|
const file = c.req.query("f") ?? "";
|
|
4611
|
-
const ws = workspaces.list().find((w) =>
|
|
4729
|
+
const ws = workspaces.list().find((w) => fs8.existsSync(path8.join(w.path, file))) ?? workspaces.get();
|
|
4612
4730
|
const abs = inside(ws.path, file);
|
|
4613
|
-
if (!abs || !abs.includes(STATE_DIR) || !
|
|
4614
|
-
return new Response(
|
|
4731
|
+
if (!abs || !abs.includes(STATE_DIR) || !fs8.existsSync(abs)) return c.notFound();
|
|
4732
|
+
return new Response(fs8.readFileSync(abs), { headers: { "content-type": "image/png", "cache-control": "no-cache" } });
|
|
4615
4733
|
});
|
|
4616
4734
|
api.get("/diff", async (c) => {
|
|
4617
4735
|
const run3 = c.req.query("run") ?? "";
|
|
@@ -4675,7 +4793,7 @@ api.post("/config", async (c) => {
|
|
|
4675
4793
|
writeSetting(ws.path, String(key), value);
|
|
4676
4794
|
const w = watchOf(ws.id);
|
|
4677
4795
|
try {
|
|
4678
|
-
w.accepted = JSON.parse(
|
|
4796
|
+
w.accepted = JSON.parse(fs8.readFileSync(path8.join(ws.path, ".pi", "loops.json"), "utf8"));
|
|
4679
4797
|
} catch {
|
|
4680
4798
|
}
|
|
4681
4799
|
return c.json({ ok: true });
|
|
@@ -4722,7 +4840,7 @@ api.get("/meta", (c) => c.json({
|
|
|
4722
4840
|
}));
|
|
4723
4841
|
app.route("/api", api);
|
|
4724
4842
|
app.route("/api/v1", api);
|
|
4725
|
-
var DIST =
|
|
4843
|
+
var DIST = path8.join(APP_ROOT, "dist");
|
|
4726
4844
|
app.get("*", async (c) => {
|
|
4727
4845
|
const url = c.req.path;
|
|
4728
4846
|
if (DEV) {
|
|
@@ -4731,14 +4849,14 @@ app.get("*", async (c) => {
|
|
|
4731
4849
|
return new Response(res.body, { status: res.status, headers: { "content-type": res.headers.get("content-type") ?? "text/html" } });
|
|
4732
4850
|
}
|
|
4733
4851
|
const abs = inside(DIST, url === "/" ? "index.html" : url.replace(/^\//, ""));
|
|
4734
|
-
if (abs &&
|
|
4735
|
-
const ext =
|
|
4852
|
+
if (abs && fs8.existsSync(abs) && fs8.statSync(abs).isFile()) {
|
|
4853
|
+
const ext = path8.extname(abs);
|
|
4736
4854
|
const type = ext === ".js" ? "text/javascript" : ext === ".css" ? "text/css" : ext === ".woff2" ? "font/woff2" : ext === ".svg" ? "image/svg+xml" : ext === ".png" ? "image/png" : "text/html";
|
|
4737
|
-
return new Response(
|
|
4855
|
+
return new Response(fs8.readFileSync(abs), { headers: { "content-type": type } });
|
|
4738
4856
|
}
|
|
4739
|
-
const index =
|
|
4740
|
-
if (!
|
|
4741
|
-
return new Response(
|
|
4857
|
+
const index = path8.join(DIST, "index.html");
|
|
4858
|
+
if (!fs8.existsSync(index)) return c.text("run `npm run build` first", 500);
|
|
4859
|
+
return new Response(fs8.readFileSync(index), { headers: { "content-type": "text/html" } });
|
|
4742
4860
|
});
|
|
4743
4861
|
var server = http.createServer(getRequestListener(app.fetch));
|
|
4744
4862
|
setInterval(() => {
|
|
@@ -4748,7 +4866,7 @@ function openBrowser(url) {
|
|
|
4748
4866
|
if (process.argv.includes("--no-open") || process.env.LOOPS_UI_OPEN === "0") return;
|
|
4749
4867
|
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
4750
4868
|
try {
|
|
4751
|
-
const p =
|
|
4869
|
+
const p = spawn6(cmd, args, { stdio: "ignore", detached: true });
|
|
4752
4870
|
p.on("error", () => {
|
|
4753
4871
|
});
|
|
4754
4872
|
p.unref();
|