@computesdk/loops 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +5 -2
- package/README.md +6 -1
- package/bin/loops.mjs +15 -2
- package/dist/assets/{abnfDiagram-VCTEODGH-DPUvPEpH.js → abnfDiagram-VCTEODGH-CqEf5Tyi.js} +1 -1
- package/dist/assets/{arc-C71M1We_.js → arc-CrYrleHr.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-C3PLmrM3.js → architectureDiagram-5GKGNRK7-D5K4RlB6.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-DFshwmKE.js → blockDiagram-I7D4REHJ-Co-w8wX9.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-Dh29QjGI.js → c4Diagram-7LVT6UL2-DlcBV2hX.js} +1 -1
- package/dist/assets/channel-DoVi6ksL.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-qZ7USlsH.js → chunk-2Q5K7J3B-Dswi4LXs.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-YQpRymiv.js → chunk-5VM5RSS4-CtLKoCf3.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-D2O95m9O.js → chunk-F27PBJKO-Bbck4p8J.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-CftSjfdl.js → chunk-IMKFNOWR-CvTEJd26.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-_1wtK-EO.js → chunk-JWPE2WC7-AIHpJEyX.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-DJ2Xmp8X.js → chunk-POPQ4Y6H-C_B3I7hU.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-CAtSHcje.js → chunk-SVP7TREG-au1NQO2y.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-1BASqzB5.js → chunk-TICWLB2K-DOQXr0QZ.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-CWs6lP_i.js → chunk-XXDRQBXY-U36QglrB.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-Cyi65D9L.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-Cyi65D9L.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-9dCpEIjz.js → cose-bilkent-JH36ORCC-D7GIzP1z.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-EFt_R8HZ.js → cynefin-OW5HDTMX-CjokfIiy.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-BuManjb1.js → cynefinDiagram-5FMLGOSQ-1aEkMuPh.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-MovLAGVL.js → dagre-GXQ25YYZ-BiIp4Lpg.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-BiJw5LQ6.js → diagram-S7CK7UJ4-BZSI7zSx.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-DRsX_xa6.js → diagram-UQ7AKVKN-BRdBU2w8.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-B7m6F8Dm.js → diagram-VSXAHHWV-DQ-h_ycl.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-x4wioeVA.js → diagram-VX7I27RA-DmLQWSdL.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-0KYjyvJF.js → diagram-Z3DM3KII-m8FGF4_n.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-BZXmt3oU.js → ebnfDiagram-PWID7BFC-DtQ-Rw5F.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-CrfcYtD6.js → erDiagram-RLTQ6QDP-DIf-60L3.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-BqudKoEy.js → flowDiagram-HODETNUW-COvzkFo5.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-OBWjueb8.js → ganttDiagram-EL5Y4UJY-Dhz_v-Sb.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-B30e199R.js → gitGraphDiagram-WWUBYQGX-BwkibubM.js} +1 -1
- package/dist/assets/{highlighted-body-JLMTFACH-DapGkHdb.js → highlighted-body-JLMTFACH-CbkekRih.js} +1 -1
- package/dist/assets/{index-C_bKTXmh.js → index-DhZYU086.js} +123 -123
- package/dist/assets/{index-hKoeppfe.css → index-NzHlk7dF.css} +1 -1
- package/dist/assets/{infoDiagram-27XIBGKW-DPwVvB4K.js → infoDiagram-27XIBGKW-BltzH8hk.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-B0fvRdQL.js → ishikawaDiagram-5VMMS53U-CKXLbULh.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-CZBLF7ld.js → journeyDiagram-3NMN7TZE-Co-OVAQM.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-wTBJWh9T.js → kanban-definition-UXKFOSKX-QEXNvAHJ.js} +1 -1
- package/dist/assets/{layout-DYBs-gVA.js → layout-DlW7eXy-.js} +1 -1
- package/dist/assets/{linear-DTE3zVP_.js → linear-DmNmIGNl.js} +1 -1
- package/dist/assets/{mindmap-definition-YA3MSWOX-tV3HPpzU.js → mindmap-definition-YA3MSWOX-CfrGbvrI.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-B6Yznbb5.js → pegDiagram-XKGWAZYB-BRPtQX-b.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-B6QW8pJH.js → pieDiagram-E7YTZNPT-cyRAi108.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-DgHGTtj0.js → quadrantDiagram-AXDQQJYC-BBwMoijR.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-CgqVFEBK.js → railroadDiagram-O6MQD6OU-DicocbzE.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-FmXMY-6j.js → requirementDiagram-BXWQKSXE-DDiXCN4F.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-AVMEYVKy.js → sankeyDiagram-P5KCCOFB-DnDGP5-W.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-uOObReYi.js → sequenceDiagram-WJ2MYXX4-C1NFZjf0.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-BPOPqqxk.js → sizeCapture-INFHLROL-CEF6O58z.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-C1lXHNdu.js → stateDiagram-D77RDMKH-Ibszr2iY.js} +1 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-CCH6TZvE.js +1 -0
- package/dist/assets/{swimlanes-42K2YHIH-C52GOi05.js → swimlanes-42K2YHIH-DbvtObGJ.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-DyRa4rww.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-T0BhAPL-.js → timeline-definition-24CTP7MA-C6qVzSzu.js} +1 -1
- package/dist/assets/{vennDiagram-4TSXK5OY-DSGCn4iB.js → vennDiagram-4TSXK5OY-B3ZZFoCj.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-BaCYx_03.js → wardleyDiagram-VM6X3IG4-C9pFlzx9.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-CgPwXvFv.js → xychartDiagram-S5SC5T6Z-mUkoBUkH.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/server.mjs +280 -148
- 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
|
|
|
@@ -2614,7 +2614,11 @@ var PiRpc = class {
|
|
|
2614
2614
|
const done = (code) => {
|
|
2615
2615
|
if (this.exited) return;
|
|
2616
2616
|
this.exited = { code };
|
|
2617
|
-
for (const [, p] of this.pending)
|
|
2617
|
+
for (const [, p] of this.pending) {
|
|
2618
|
+
const noise = this.stderr.split("\n").map((l) => l.trim()).filter((l) => l && !/^warn(ing)?\b/i.test(l)).slice(-3).join(" \xB7 ").slice(-300);
|
|
2619
|
+
const how = code === 0 ? "pi exited before answering" : `pi exited (code ${code})`;
|
|
2620
|
+
p.reject(new Error(noise ? `${how}: ${noise}` : how));
|
|
2621
|
+
}
|
|
2618
2622
|
this.pending.clear();
|
|
2619
2623
|
for (const fn of this.exitListeners) {
|
|
2620
2624
|
try {
|
|
@@ -3449,8 +3453,8 @@ import path3 from "node:path";
|
|
|
3449
3453
|
import { spawn as spawn2 } from "node:child_process";
|
|
3450
3454
|
var lives = /* @__PURE__ */ new Map();
|
|
3451
3455
|
var IDLE_MS = 10 * 6e4;
|
|
3452
|
-
function serveCommand(cwd,
|
|
3453
|
-
for (const f of [path3.join(cwd, ".pi", "loops.json"), path3.join(
|
|
3456
|
+
function serveCommand(cwd, agentDir3 = process.env.PI_AGENT_DIR ?? path3.join(process.env.HOME ?? "", ".pi", "agent")) {
|
|
3457
|
+
for (const f of [path3.join(cwd, ".pi", "loops.json"), path3.join(agentDir3, "loops.json")]) {
|
|
3454
3458
|
try {
|
|
3455
3459
|
const j = JSON.parse(fs3.readFileSync(f, "utf8"));
|
|
3456
3460
|
if (typeof j?.serve === "string" && j.serve.trim()) return j.serve.trim();
|
|
@@ -4085,11 +4089,101 @@ async function doctor(cwd) {
|
|
|
4085
4089
|
return { checks, verdict: fatal ? "will-fail" : checks.every((c) => c.ok) ? "ready" : "incomplete" };
|
|
4086
4090
|
}
|
|
4087
4091
|
|
|
4088
|
-
//
|
|
4092
|
+
// ../pi-loops/lib/credentials.ts
|
|
4089
4093
|
import fs6 from "node:fs";
|
|
4094
|
+
import os3 from "node:os";
|
|
4090
4095
|
import path6 from "node:path";
|
|
4096
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
4097
|
+
var PROVIDERS = [
|
|
4098
|
+
{ id: "particle", label: "Particle AI" },
|
|
4099
|
+
{ id: "morph", label: "Morph" },
|
|
4100
|
+
{ id: "openrouter", label: "OpenRouter" },
|
|
4101
|
+
{ id: "anthropic", label: "Anthropic" },
|
|
4102
|
+
{ id: "openai", label: "OpenAI" },
|
|
4103
|
+
{ id: "deepseek", label: "DeepSeek" }
|
|
4104
|
+
];
|
|
4105
|
+
var agentDir2 = () => process.env.PI_AGENT_DIR ?? path6.join(os3.homedir(), ".pi", "agent");
|
|
4106
|
+
var authFile = (dir = agentDir2()) => path6.join(dir, "auth.json");
|
|
4107
|
+
var envVar = (provider) => `${provider.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_API_KEY`;
|
|
4108
|
+
function credentialStatus(dir = agentDir2()) {
|
|
4109
|
+
let auth = {};
|
|
4110
|
+
try {
|
|
4111
|
+
const raw2 = fs6.readFileSync(authFile(dir), "utf8");
|
|
4112
|
+
auth = JSON.parse(raw2) ?? {};
|
|
4113
|
+
} catch {
|
|
4114
|
+
}
|
|
4115
|
+
return PROVIDERS.map((p) => {
|
|
4116
|
+
const env = envVar(p.id);
|
|
4117
|
+
const entry = auth?.[p.id];
|
|
4118
|
+
const inAuth = !!(entry && typeof entry === "object" && (entry.key || entry.type === "oauth"));
|
|
4119
|
+
return { id: p.id, label: p.label, env, configured: inAuth || !!process.env[env], source: inAuth ? "auth" : process.env[env] ? "env" : "none" };
|
|
4120
|
+
});
|
|
4121
|
+
}
|
|
4122
|
+
var chain = Promise.resolve();
|
|
4123
|
+
function queued(fn) {
|
|
4124
|
+
const next = chain.then(fn, fn);
|
|
4125
|
+
chain = next.catch(() => {
|
|
4126
|
+
});
|
|
4127
|
+
return next;
|
|
4128
|
+
}
|
|
4129
|
+
async function writeApiKey(provider, key, dir = agentDir2()) {
|
|
4130
|
+
const trimmed = key.trim();
|
|
4131
|
+
if (!trimmed) throw new Error("a key is required");
|
|
4132
|
+
if (!PROVIDERS.some((p) => p.id === provider)) throw new Error(`unknown provider "${provider}"`);
|
|
4133
|
+
return queued(async () => {
|
|
4134
|
+
const file = authFile(dir);
|
|
4135
|
+
let auth = {};
|
|
4136
|
+
try {
|
|
4137
|
+
const raw2 = fs6.readFileSync(file, "utf8");
|
|
4138
|
+
const parsed = raw2.trim() ? JSON.parse(raw2) : {};
|
|
4139
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) auth = parsed;
|
|
4140
|
+
} catch {
|
|
4141
|
+
}
|
|
4142
|
+
auth[provider] = { ...auth[provider] && typeof auth[provider] === "object" ? auth[provider] : {}, type: "api_key", key: trimmed };
|
|
4143
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
4144
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
4145
|
+
fs6.writeFileSync(tmp, JSON.stringify(auth, null, 2) + "\n", { mode: 384 });
|
|
4146
|
+
fs6.renameSync(tmp, file);
|
|
4147
|
+
try {
|
|
4148
|
+
fs6.chmodSync(file, 384);
|
|
4149
|
+
} catch {
|
|
4150
|
+
}
|
|
4151
|
+
return file;
|
|
4152
|
+
});
|
|
4153
|
+
}
|
|
4154
|
+
async function verifyModel({ pi, model, cwd, timeoutMs = 6e4 }) {
|
|
4155
|
+
const args = ["--print", "--no-session", "--model", model, "Reply with the single word: ok"];
|
|
4156
|
+
return new Promise((resolve) => {
|
|
4157
|
+
let out = "";
|
|
4158
|
+
const child = spawn5(pi, args, { cwd, stdio: ["ignore", "pipe", "ignore"] });
|
|
4159
|
+
const timer = setTimeout(() => {
|
|
4160
|
+
try {
|
|
4161
|
+
child.kill("SIGKILL");
|
|
4162
|
+
} catch {
|
|
4163
|
+
}
|
|
4164
|
+
resolve({ ok: false, detail: `no answer in ${Math.round(timeoutMs / 1e3)}s` });
|
|
4165
|
+
}, timeoutMs);
|
|
4166
|
+
child.stdout.on("data", (b) => {
|
|
4167
|
+
out += String(b);
|
|
4168
|
+
});
|
|
4169
|
+
child.on("error", (e) => {
|
|
4170
|
+
clearTimeout(timer);
|
|
4171
|
+
resolve({ ok: false, detail: `could not run ${pi}: ${e.message}` });
|
|
4172
|
+
});
|
|
4173
|
+
child.on("close", (code) => {
|
|
4174
|
+
clearTimeout(timer);
|
|
4175
|
+
const said = out.split("\n").map((l) => l.trim()).filter(Boolean).slice(-2).join(" \xB7 ").slice(-160);
|
|
4176
|
+
if (code === 0 && /\bok\b/i.test(said)) return resolve({ ok: true, detail: `answered: ${said}` });
|
|
4177
|
+
resolve({ ok: false, detail: `exit ${code ?? "?"}${said ? ` \u2014 ${said}` : ""}`.trim() });
|
|
4178
|
+
});
|
|
4179
|
+
});
|
|
4180
|
+
}
|
|
4181
|
+
|
|
4182
|
+
// server/workspaces.ts
|
|
4183
|
+
import fs7 from "node:fs";
|
|
4184
|
+
import path7 from "node:path";
|
|
4091
4185
|
import { execFileSync } from "node:child_process";
|
|
4092
|
-
var slug = (p) =>
|
|
4186
|
+
var slug = (p) => path7.basename(p).replace(/[^a-z0-9._-]+/gi, "-").toLowerCase() || "repo";
|
|
4093
4187
|
var Workspaces = class {
|
|
4094
4188
|
/** Where workspaces.json and the per-repo session dirs live. */
|
|
4095
4189
|
root;
|
|
@@ -4099,15 +4193,15 @@ var Workspaces = class {
|
|
|
4099
4193
|
chatMap;
|
|
4100
4194
|
constructor({ root, cwd }) {
|
|
4101
4195
|
this.root = root;
|
|
4102
|
-
this.file =
|
|
4103
|
-
|
|
4196
|
+
this.file = path7.join(root, "workspaces.json");
|
|
4197
|
+
fs7.mkdirSync(root, { recursive: true });
|
|
4104
4198
|
let data = { workspaces: [], chats: {} };
|
|
4105
|
-
if (
|
|
4199
|
+
if (fs7.existsSync(this.file)) {
|
|
4106
4200
|
try {
|
|
4107
|
-
data = JSON.parse(
|
|
4201
|
+
data = JSON.parse(fs7.readFileSync(this.file, "utf8"));
|
|
4108
4202
|
} catch {
|
|
4109
4203
|
try {
|
|
4110
|
-
|
|
4204
|
+
fs7.renameSync(this.file, `${this.file}.corrupt-${Date.now()}`);
|
|
4111
4205
|
} catch {
|
|
4112
4206
|
}
|
|
4113
4207
|
console.error(`[workspaces] could not read ${this.file}; kept a copy and rebuilding from sessions`);
|
|
@@ -4124,8 +4218,8 @@ var Workspaces = class {
|
|
|
4124
4218
|
this.data.chats = Object.fromEntries(this.chatMap);
|
|
4125
4219
|
try {
|
|
4126
4220
|
const tmp = `${this.file}.tmp`;
|
|
4127
|
-
|
|
4128
|
-
|
|
4221
|
+
fs7.writeFileSync(tmp, JSON.stringify(this.data, null, 2) + "\n");
|
|
4222
|
+
fs7.renameSync(tmp, this.file);
|
|
4129
4223
|
} catch {
|
|
4130
4224
|
}
|
|
4131
4225
|
}
|
|
@@ -4140,18 +4234,18 @@ var Workspaces = class {
|
|
|
4140
4234
|
}
|
|
4141
4235
|
/** Add (or re-find) a repo. `persist: false` is used for the startup cwd. */
|
|
4142
4236
|
add(dir, { persist = true } = {}) {
|
|
4143
|
-
const abs =
|
|
4237
|
+
const abs = path7.resolve(dir);
|
|
4144
4238
|
const existing = [...this.byId.values()].find((w) => w.path === abs);
|
|
4145
4239
|
if (existing) return existing;
|
|
4146
|
-
if (!
|
|
4147
|
-
const st =
|
|
4240
|
+
if (!fs7.existsSync(abs) || !fs7.statSync(abs).isDirectory()) throw new Error(`${abs} is not a directory`);
|
|
4241
|
+
const st = fs7.statSync(abs);
|
|
4148
4242
|
if (!st.isDirectory()) throw new Error(`${abs} is not a directory`);
|
|
4149
4243
|
let id = slug(abs);
|
|
4150
4244
|
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
|
-
|
|
4245
|
+
const legacy = path7.join(abs, ".pi", "loops-ui", "sessions");
|
|
4246
|
+
const sessionDir = fs7.existsSync(legacy) ? legacy : path7.join(this.root, "workspaces", id, "sessions");
|
|
4247
|
+
const ws = { id, path: abs, sessionDir, title: path7.basename(abs) || abs };
|
|
4248
|
+
fs7.mkdirSync(ws.sessionDir, { recursive: true });
|
|
4155
4249
|
this.byId.set(id, ws);
|
|
4156
4250
|
if (persist) {
|
|
4157
4251
|
this.data.workspaces = [...this.data.workspaces.filter((w) => w.path !== abs), { id, path: abs }];
|
|
@@ -4175,25 +4269,25 @@ var Workspaces = class {
|
|
|
4175
4269
|
* workspace. This runs on startup, so a lost or stale `workspaces.json` is a cache miss, not lost history.
|
|
4176
4270
|
*/
|
|
4177
4271
|
recoverSessions() {
|
|
4178
|
-
const root =
|
|
4272
|
+
const root = path7.join(this.root, "workspaces");
|
|
4179
4273
|
let ids = [];
|
|
4180
4274
|
try {
|
|
4181
|
-
ids =
|
|
4275
|
+
ids = fs7.readdirSync(root);
|
|
4182
4276
|
} catch {
|
|
4183
4277
|
return;
|
|
4184
4278
|
}
|
|
4185
4279
|
for (const id of ids) {
|
|
4186
|
-
const dir =
|
|
4280
|
+
const dir = path7.join(root, id, "sessions");
|
|
4187
4281
|
let files = [];
|
|
4188
4282
|
try {
|
|
4189
|
-
files =
|
|
4283
|
+
files = fs7.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
|
|
4190
4284
|
} catch {
|
|
4191
4285
|
continue;
|
|
4192
4286
|
}
|
|
4193
4287
|
let cwd = null;
|
|
4194
4288
|
for (const f of files) {
|
|
4195
4289
|
try {
|
|
4196
|
-
const head = JSON.parse(
|
|
4290
|
+
const head = JSON.parse(fs7.readFileSync(path7.join(dir, f), "utf8").split("\n")[0]);
|
|
4197
4291
|
if (typeof head?.cwd === "string" && head.cwd) {
|
|
4198
4292
|
cwd = head.cwd;
|
|
4199
4293
|
break;
|
|
@@ -4248,8 +4342,8 @@ var Workspaces = class {
|
|
|
4248
4342
|
};
|
|
4249
4343
|
|
|
4250
4344
|
// server/index.ts
|
|
4251
|
-
var HERE =
|
|
4252
|
-
var APP_ROOT =
|
|
4345
|
+
var HERE = path8.dirname(fileURLToPath(import.meta.url));
|
|
4346
|
+
var APP_ROOT = path8.resolve(HERE, "..");
|
|
4253
4347
|
var arg = (name, fallback) => {
|
|
4254
4348
|
const i = process.argv.indexOf(`--${name}`);
|
|
4255
4349
|
return i >= 0 ? process.argv[i + 1] : process.env[`LOOPS_UI_${name.toUpperCase().replace(/-/g, "_")}`] ?? fallback;
|
|
@@ -4257,16 +4351,16 @@ var arg = (name, fallback) => {
|
|
|
4257
4351
|
var PORT = Number(arg("port", "8001"));
|
|
4258
4352
|
var HOST = arg("host", "127.0.0.1");
|
|
4259
4353
|
var LOOPBACK = ["127.0.0.1", "localhost", "::1"].includes(HOST);
|
|
4260
|
-
var CWD =
|
|
4354
|
+
var CWD = path8.resolve(arg("cwd", process.cwd()));
|
|
4261
4355
|
var DEV = process.argv.includes("--dev") || process.env.NODE_ENV === "development";
|
|
4262
4356
|
var VITE = arg("vite", "http://localhost:5173");
|
|
4263
4357
|
var PI = arg("pi", "pi");
|
|
4264
|
-
var DATA =
|
|
4358
|
+
var DATA = path8.resolve(arg("data", process.env.LOOPS_UI_DATA ?? path8.join(process.env.HOME ?? process.cwd(), ".pi", "loops-ui")));
|
|
4265
4359
|
var MODEL = arg("model", void 0) || null;
|
|
4266
|
-
var EXTENSIONS = (arg("extensions",
|
|
4360
|
+
var EXTENSIONS = (arg("extensions", path8.resolve(APP_ROOT, "../pi-loops/extensions/loop.ts")) ?? "").split(",").filter(Boolean);
|
|
4267
4361
|
function prerequisites() {
|
|
4268
4362
|
const out = [];
|
|
4269
|
-
const onPath = PI.includes("/") || /^[A-Za-z]:\\/.test(PI) ?
|
|
4363
|
+
const onPath = PI.includes("/") || /^[A-Za-z]:\\/.test(PI) ? fs8.existsSync(PI) : (() => {
|
|
4270
4364
|
try {
|
|
4271
4365
|
execFileSync2(process.platform === "win32" ? "where" : "which", [PI], { stdio: ["ignore", "pipe", "ignore"] });
|
|
4272
4366
|
return true;
|
|
@@ -4276,18 +4370,18 @@ function prerequisites() {
|
|
|
4276
4370
|
})();
|
|
4277
4371
|
if (!onPath) out.push({ name: "pi", detail: `\`${PI}\` is not on PATH`, fix: "install Pi, or pass --pi <path to the pi binary>" });
|
|
4278
4372
|
for (const e of EXTENSIONS) {
|
|
4279
|
-
if (!
|
|
4373
|
+
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
4374
|
}
|
|
4281
4375
|
return out;
|
|
4282
4376
|
}
|
|
4283
4377
|
var PROBLEMS = prerequisites();
|
|
4284
|
-
|
|
4378
|
+
fs8.mkdirSync(DATA, { recursive: true });
|
|
4285
4379
|
var TOKEN = (() => {
|
|
4286
4380
|
const explicit = arg("token", void 0);
|
|
4287
|
-
const file =
|
|
4381
|
+
const file = path8.join(DATA, "token");
|
|
4288
4382
|
const stored = (() => {
|
|
4289
4383
|
try {
|
|
4290
|
-
return
|
|
4384
|
+
return fs8.readFileSync(file, "utf8").trim() || null;
|
|
4291
4385
|
} catch {
|
|
4292
4386
|
return null;
|
|
4293
4387
|
}
|
|
@@ -4295,7 +4389,7 @@ var TOKEN = (() => {
|
|
|
4295
4389
|
if (explicit) {
|
|
4296
4390
|
if (!LOOPBACK) {
|
|
4297
4391
|
try {
|
|
4298
|
-
|
|
4392
|
+
fs8.writeFileSync(file, explicit + "\n", { mode: 384 });
|
|
4299
4393
|
} catch {
|
|
4300
4394
|
}
|
|
4301
4395
|
}
|
|
@@ -4304,7 +4398,7 @@ var TOKEN = (() => {
|
|
|
4304
4398
|
if (stored) return stored;
|
|
4305
4399
|
if (LOOPBACK) return null;
|
|
4306
4400
|
const made = randomBytes(24).toString("base64url");
|
|
4307
|
-
|
|
4401
|
+
fs8.writeFileSync(file, made + "\n", { mode: 384 });
|
|
4308
4402
|
return made;
|
|
4309
4403
|
})();
|
|
4310
4404
|
if (!LOOPBACK && !TOKEN) {
|
|
@@ -4324,7 +4418,7 @@ var manager = new ChatManager({
|
|
|
4324
4418
|
});
|
|
4325
4419
|
var wsOf = (chat) => chat ? workspaces.ofChat(chat) : workspaces.get();
|
|
4326
4420
|
var wsOfRun = (run3) => {
|
|
4327
|
-
const byIndex = workspaces.list().find((w) =>
|
|
4421
|
+
const byIndex = workspaces.list().find((w) => fs8.existsSync(path8.join(w.path, STATE_DIR, `${run3}.json`)));
|
|
4328
4422
|
if (byIndex) return byIndex;
|
|
4329
4423
|
const chat = manager.findRun(run3);
|
|
4330
4424
|
return chat ? workspaces.ofChat(chat.id) : workspaces.get();
|
|
@@ -4352,7 +4446,7 @@ var watchOf = (wsId) => {
|
|
|
4352
4446
|
if (!w) {
|
|
4353
4447
|
const read = () => {
|
|
4354
4448
|
try {
|
|
4355
|
-
return JSON.parse(
|
|
4449
|
+
return JSON.parse(fs8.readFileSync(path8.join(ws.path, ".pi", "loops.json"), "utf8"));
|
|
4356
4450
|
} catch {
|
|
4357
4451
|
return {};
|
|
4358
4452
|
}
|
|
@@ -4360,7 +4454,7 @@ var watchOf = (wsId) => {
|
|
|
4360
4454
|
w = { cwd: ws.path, accepted: read() };
|
|
4361
4455
|
watches.set(ws.id, w);
|
|
4362
4456
|
try {
|
|
4363
|
-
|
|
4457
|
+
fs8.watch(path8.join(ws.path, ".pi"), (_e, name) => {
|
|
4364
4458
|
if (name && name !== "loops.json") return;
|
|
4365
4459
|
clearTimeout(watchTimers.get(ws.id));
|
|
4366
4460
|
watchTimers.set(ws.id, setTimeout(() => checkConfig(ws.id), 120));
|
|
@@ -4382,10 +4476,10 @@ function notifyProposals() {
|
|
|
4382
4476
|
}
|
|
4383
4477
|
function checkConfig(wsId) {
|
|
4384
4478
|
const w = watchOf(wsId);
|
|
4385
|
-
const CONFIG =
|
|
4479
|
+
const CONFIG = path8.join(w.cwd, ".pi", "loops.json");
|
|
4386
4480
|
const raw2 = (() => {
|
|
4387
4481
|
try {
|
|
4388
|
-
return
|
|
4482
|
+
return fs8.readFileSync(CONFIG, "utf8");
|
|
4389
4483
|
} catch {
|
|
4390
4484
|
return "";
|
|
4391
4485
|
}
|
|
@@ -4406,16 +4500,29 @@ function checkConfig(wsId) {
|
|
|
4406
4500
|
notifyProposals();
|
|
4407
4501
|
}
|
|
4408
4502
|
var inside = (root, p) => {
|
|
4409
|
-
const abs =
|
|
4410
|
-
return abs === root || abs.startsWith(root +
|
|
4503
|
+
const abs = path8.resolve(root, p);
|
|
4504
|
+
return abs === root || abs.startsWith(root + path8.sep) ? abs : null;
|
|
4411
4505
|
};
|
|
4412
4506
|
var stateOf = (run3) => manager.findRun(run3)?.stateFor(run3) ?? readState(wsOfRun(run3).path, run3);
|
|
4413
4507
|
var app = new Hono3();
|
|
4508
|
+
var OWN_HOSTS = /* @__PURE__ */ new Set([`127.0.0.1:${PORT}`, `localhost:${PORT}`, `[::1]:${PORT}`]);
|
|
4509
|
+
var OWN_ORIGINS = new Set([...OWN_HOSTS].map((h) => `http://${h}`));
|
|
4414
4510
|
var ALLOWED_ORIGINS = (arg("allow-origin", "") ?? "").split(",").map((o) => o.trim()).filter(Boolean);
|
|
4415
|
-
var originAllowed = (origin) =>
|
|
4511
|
+
var originAllowed = (origin) => {
|
|
4512
|
+
if (ALLOWED_ORIGINS.includes("*")) return true;
|
|
4513
|
+
if (ALLOWED_ORIGINS.length) return ALLOWED_ORIGINS.includes(origin);
|
|
4514
|
+
return !LOOPBACK || OWN_ORIGINS.has(origin);
|
|
4515
|
+
};
|
|
4416
4516
|
app.use("*", async (c, next) => {
|
|
4517
|
+
const host = c.req.header("host") ?? "";
|
|
4518
|
+
if (LOOPBACK && !OWN_HOSTS.has(host)) {
|
|
4519
|
+
return c.text(`this server answers as ${[...OWN_HOSTS].join(", ")}, not "${host}"`, 403);
|
|
4520
|
+
}
|
|
4417
4521
|
const origin = c.req.header("origin");
|
|
4418
|
-
if (origin && originAllowed(origin)) {
|
|
4522
|
+
if (origin && !originAllowed(origin)) {
|
|
4523
|
+
return c.json({ error: "origin not allowed", origin }, 403);
|
|
4524
|
+
}
|
|
4525
|
+
if (origin) {
|
|
4419
4526
|
c.header("access-control-allow-origin", origin);
|
|
4420
4527
|
c.header("access-control-allow-headers", "authorization, content-type, last-event-id");
|
|
4421
4528
|
c.header("access-control-allow-methods", "GET, POST, OPTIONS");
|
|
@@ -4438,6 +4545,31 @@ app.use("/api/v1/*", async (c, next) => {
|
|
|
4438
4545
|
});
|
|
4439
4546
|
var api = new Hono3();
|
|
4440
4547
|
api.get("/health", (c) => c.json({ ok: PROBLEMS.length === 0, problems: PROBLEMS, cwd: CWD, data: DATA, pi: PI, extensions: EXTENSIONS, workspaces: workspaces.list() }));
|
|
4548
|
+
api.get("/credentials", (c) => c.json({ dir: agentDir2(), providers: credentialStatus() }));
|
|
4549
|
+
api.post("/credentials", async (c) => {
|
|
4550
|
+
const body = await c.req.json().catch(() => ({}));
|
|
4551
|
+
const provider = String(body?.provider ?? "");
|
|
4552
|
+
const key = String(body?.key ?? "").trim();
|
|
4553
|
+
if (!PROVIDERS.some((p) => p.id === provider)) return c.json({ error: `unknown provider "${provider}" \u2014 one of ${PROVIDERS.map((p) => p.id).join(", ")}` }, 400);
|
|
4554
|
+
if (!key) return c.json({ error: "a key is required" }, 400);
|
|
4555
|
+
try {
|
|
4556
|
+
const file = await writeApiKey(provider, key);
|
|
4557
|
+
let model = typeof body?.model === "string" && body.model.trim() ? body.model.trim() : "";
|
|
4558
|
+
if (!model) {
|
|
4559
|
+
for (const chat of manager.openChats()) {
|
|
4560
|
+
const found = (await chat.availableModels()).find((m) => m.provider === provider);
|
|
4561
|
+
if (found) {
|
|
4562
|
+
model = found.id;
|
|
4563
|
+
break;
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
const verified = model ? { model, ...await verifyModel({ pi: PI, model, cwd: CWD }) } : null;
|
|
4568
|
+
return c.json({ ok: true, provider, file, configured: credentialStatus().find((s) => s.id === provider)?.configured ?? true, verified });
|
|
4569
|
+
} catch (e) {
|
|
4570
|
+
return c.json({ error: String(e.message ?? e).slice(0, 200) }, 400);
|
|
4571
|
+
}
|
|
4572
|
+
});
|
|
4441
4573
|
api.get("/chats", (c) => c.json(chatsOf(c.req.query("ws"))));
|
|
4442
4574
|
api.get("/chats/:id/events", async (c) => {
|
|
4443
4575
|
const id = c.req.param("id");
|
|
@@ -4480,9 +4612,9 @@ api.post("/chat/delete", async (c) => {
|
|
|
4480
4612
|
const file = chat?.sessionFile ?? null;
|
|
4481
4613
|
await manager.close(String(id));
|
|
4482
4614
|
workspaces.unbindChat(String(id));
|
|
4483
|
-
if (file &&
|
|
4615
|
+
if (file && fs8.existsSync(file)) {
|
|
4484
4616
|
try {
|
|
4485
|
-
|
|
4617
|
+
fs8.rmSync(file);
|
|
4486
4618
|
} catch {
|
|
4487
4619
|
}
|
|
4488
4620
|
}
|
|
@@ -4492,9 +4624,9 @@ api.get("/workspaces", (c) => c.json(workspaces.list()));
|
|
|
4492
4624
|
api.post("/workspaces", async (c) => {
|
|
4493
4625
|
const body = await c.req.json().catch(() => ({}));
|
|
4494
4626
|
try {
|
|
4495
|
-
const dir =
|
|
4627
|
+
const dir = path8.resolve(String(body.path ?? "").replace(/^~(?=\/|$)/, process.env.HOME ?? "~"));
|
|
4496
4628
|
if (!dir || dir === "/") throw new Error("a path is required");
|
|
4497
|
-
if (!
|
|
4629
|
+
if (!fs8.existsSync(dir) || !fs8.statSync(dir).isDirectory()) throw new Error(`${dir} is not a directory`);
|
|
4498
4630
|
const ws = workspaces.add(dir);
|
|
4499
4631
|
watchOf(ws.id);
|
|
4500
4632
|
return c.json({ ok: true, workspace: ws, loopable: Workspaces.loopable(ws.path) });
|
|
@@ -4578,7 +4710,7 @@ api.get("/session", async (c) => {
|
|
|
4578
4710
|
if (!run3 || !name) return c.json([]);
|
|
4579
4711
|
const state = stateOf(run3);
|
|
4580
4712
|
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 || !
|
|
4713
|
+
if (!file || !fs8.existsSync(file)) return c.json([]);
|
|
4582
4714
|
const chat = await manager.open(c.req.query("chat") ?? manager.list()[0]?.id ?? "");
|
|
4583
4715
|
return c.json(chat.sessionItems(file));
|
|
4584
4716
|
});
|
|
@@ -4597,21 +4729,21 @@ api.get("/shot", (c) => {
|
|
|
4597
4729
|
const repo = wsOfRun(run3).path;
|
|
4598
4730
|
if (file.startsWith(STATE_DIR)) {
|
|
4599
4731
|
const abs2 = inside(repo, file);
|
|
4600
|
-
if (!abs2 || !
|
|
4601
|
-
return new Response(
|
|
4732
|
+
if (!abs2 || !fs8.existsSync(abs2)) return c.notFound();
|
|
4733
|
+
return new Response(fs8.readFileSync(abs2), { headers: { "content-type": abs2.endsWith(".jpg") || abs2.endsWith(".jpeg") ? "image/jpeg" : "image/png", "cache-control": "no-cache" } });
|
|
4602
4734
|
}
|
|
4603
4735
|
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
4736
|
if (!dir) return c.notFound();
|
|
4605
|
-
const abs = inside(repo,
|
|
4606
|
-
if (!abs || !
|
|
4607
|
-
return new Response(
|
|
4737
|
+
const abs = inside(repo, path8.join(dir, file));
|
|
4738
|
+
if (!abs || !fs8.existsSync(abs)) return c.notFound();
|
|
4739
|
+
return new Response(fs8.readFileSync(abs), { headers: { "content-type": abs.endsWith(".jpg") || abs.endsWith(".jpeg") ? "image/jpeg" : "image/png", "cache-control": "no-cache" } });
|
|
4608
4740
|
});
|
|
4609
4741
|
api.get("/sheet", (c) => {
|
|
4610
4742
|
const file = c.req.query("f") ?? "";
|
|
4611
|
-
const ws = workspaces.list().find((w) =>
|
|
4743
|
+
const ws = workspaces.list().find((w) => fs8.existsSync(path8.join(w.path, file))) ?? workspaces.get();
|
|
4612
4744
|
const abs = inside(ws.path, file);
|
|
4613
|
-
if (!abs || !abs.includes(STATE_DIR) || !
|
|
4614
|
-
return new Response(
|
|
4745
|
+
if (!abs || !abs.includes(STATE_DIR) || !fs8.existsSync(abs)) return c.notFound();
|
|
4746
|
+
return new Response(fs8.readFileSync(abs), { headers: { "content-type": "image/png", "cache-control": "no-cache" } });
|
|
4615
4747
|
});
|
|
4616
4748
|
api.get("/diff", async (c) => {
|
|
4617
4749
|
const run3 = c.req.query("run") ?? "";
|
|
@@ -4675,7 +4807,7 @@ api.post("/config", async (c) => {
|
|
|
4675
4807
|
writeSetting(ws.path, String(key), value);
|
|
4676
4808
|
const w = watchOf(ws.id);
|
|
4677
4809
|
try {
|
|
4678
|
-
w.accepted = JSON.parse(
|
|
4810
|
+
w.accepted = JSON.parse(fs8.readFileSync(path8.join(ws.path, ".pi", "loops.json"), "utf8"));
|
|
4679
4811
|
} catch {
|
|
4680
4812
|
}
|
|
4681
4813
|
return c.json({ ok: true });
|
|
@@ -4722,7 +4854,7 @@ api.get("/meta", (c) => c.json({
|
|
|
4722
4854
|
}));
|
|
4723
4855
|
app.route("/api", api);
|
|
4724
4856
|
app.route("/api/v1", api);
|
|
4725
|
-
var DIST =
|
|
4857
|
+
var DIST = path8.join(APP_ROOT, "dist");
|
|
4726
4858
|
app.get("*", async (c) => {
|
|
4727
4859
|
const url = c.req.path;
|
|
4728
4860
|
if (DEV) {
|
|
@@ -4731,14 +4863,14 @@ app.get("*", async (c) => {
|
|
|
4731
4863
|
return new Response(res.body, { status: res.status, headers: { "content-type": res.headers.get("content-type") ?? "text/html" } });
|
|
4732
4864
|
}
|
|
4733
4865
|
const abs = inside(DIST, url === "/" ? "index.html" : url.replace(/^\//, ""));
|
|
4734
|
-
if (abs &&
|
|
4735
|
-
const ext =
|
|
4866
|
+
if (abs && fs8.existsSync(abs) && fs8.statSync(abs).isFile()) {
|
|
4867
|
+
const ext = path8.extname(abs);
|
|
4736
4868
|
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(
|
|
4869
|
+
return new Response(fs8.readFileSync(abs), { headers: { "content-type": type } });
|
|
4738
4870
|
}
|
|
4739
|
-
const index =
|
|
4740
|
-
if (!
|
|
4741
|
-
return new Response(
|
|
4871
|
+
const index = path8.join(DIST, "index.html");
|
|
4872
|
+
if (!fs8.existsSync(index)) return c.text("run `npm run build` first", 500);
|
|
4873
|
+
return new Response(fs8.readFileSync(index), { headers: { "content-type": "text/html" } });
|
|
4742
4874
|
});
|
|
4743
4875
|
var server = http.createServer(getRequestListener(app.fetch));
|
|
4744
4876
|
setInterval(() => {
|
|
@@ -4748,7 +4880,7 @@ function openBrowser(url) {
|
|
|
4748
4880
|
if (process.argv.includes("--no-open") || process.env.LOOPS_UI_OPEN === "0") return;
|
|
4749
4881
|
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
4750
4882
|
try {
|
|
4751
|
-
const p =
|
|
4883
|
+
const p = spawn6(cmd, args, { stdio: "ignore", detached: true });
|
|
4752
4884
|
p.on("error", () => {
|
|
4753
4885
|
});
|
|
4754
4886
|
p.unref();
|