@pi-harness/pi-harness 0.1.85 → 0.1.86
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/apps/web/dist/assets/{index--BtqJS5O.css → index-CYoyyJnL.css} +1 -1
- package/apps/web/dist/assets/index-DQB7fCT-.js +29 -0
- package/apps/web/dist/index.html +2 -2
- package/apps/web/package.json +1 -1
- package/apps/web/src/style.css +3 -0
- package/node_modules/@pi-harness/api-gateway/dist/index.d.ts.map +1 -1
- package/node_modules/@pi-harness/api-gateway/dist/index.js +142 -1
- package/node_modules/@pi-harness/api-gateway/dist/index.js.map +1 -1
- package/node_modules/@pi-harness/api-gateway/package.json +1 -1
- package/node_modules/@pi-harness/cli/package.json +1 -1
- package/node_modules/@pi-harness/host-webserver/package.json +1 -1
- package/node_modules/@pi-harness/web-app/package.json +1 -1
- package/package.json +6 -6
- package/packages/api-gateway/dist/index.d.ts.map +1 -1
- package/packages/api-gateway/dist/index.js +142 -1
- package/packages/api-gateway/dist/index.js.map +1 -1
- package/packages/api-gateway/package.json +1 -1
- package/packages/api-gateway/src/index.ts +136 -1
- package/packages/api-gateway/test/index.test.ts +95 -0
- package/packages/bundle-web-app/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/host-webserver/package.json +1 -1
- package/apps/web/dist/assets/index-Qupnc3-4.js +0 -29
package/apps/web/dist/index.html
CHANGED
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
<link rel="apple-touch-icon" href="/icons/app/apple-touch-icon-180.png" sizes="180x180" />
|
|
16
16
|
<link rel="manifest" href="/manifest.webmanifest" />
|
|
17
17
|
<title>Pi Harness / Control Room</title>
|
|
18
|
-
<script type="module" crossorigin src="/assets/index-
|
|
18
|
+
<script type="module" crossorigin src="/assets/index-DQB7fCT-.js"></script>
|
|
19
19
|
<link rel="modulepreload" crossorigin href="/assets/react-vendor-BxoMtjVy.js">
|
|
20
20
|
<link rel="modulepreload" crossorigin href="/assets/markdown-vendor-DUdMWn0M.js">
|
|
21
|
-
<link rel="stylesheet" crossorigin href="/assets/index
|
|
21
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CYoyyJnL.css">
|
|
22
22
|
</head>
|
|
23
23
|
<body>
|
|
24
24
|
<main id="root"></main>
|
package/apps/web/package.json
CHANGED
package/apps/web/src/style.css
CHANGED
|
@@ -1443,6 +1443,9 @@ button:disabled {
|
|
|
1443
1443
|
.global-search-results {
|
|
1444
1444
|
@apply [min-height:0] [overflow:auto] [padding:8px_10px_12px];
|
|
1445
1445
|
}
|
|
1446
|
+
.global-search-notice {
|
|
1447
|
+
@apply [flex:none] [padding:9px_18px] [border-top:1px_solid_var(--color-line)] [background:var(--color-warn-soft)] [color:var(--color-warn)] [font-size:11px];
|
|
1448
|
+
}
|
|
1446
1449
|
.global-search-group {
|
|
1447
1450
|
@apply [padding:0_0_8px];
|
|
1448
1451
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;;;;mBAy8BlC,OAAO;;AAHxB,wBAq/CE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { existsSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { mkdir, mkdtemp, readFile, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { mkdir, mkdtemp, opendir, readFile, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
@@ -23,6 +23,12 @@ const PROCESS_FAILURE_TEXT_LIMIT = 400;
|
|
|
23
23
|
const PROCESS_TIMEOUT_MS = 120_000;
|
|
24
24
|
// Mutating commands run user hooks (pre-commit, lint-staged, test suites) and may stage large trees; killing them part-way leaves index.lock and a half-finished operation behind, so they get a far more generous bound than read-only queries.
|
|
25
25
|
const GIT_MUTATION_TIMEOUT_MS = 120_000;
|
|
26
|
+
const MAX_WORKSPACE_FILES = 5_000;
|
|
27
|
+
const MAX_WORKSPACE_FILE_CANDIDATES = 20_000;
|
|
28
|
+
const MAX_WORKSPACE_DIRECTORIES = 2_000;
|
|
29
|
+
const MAX_WORKSPACE_DEPTH = 16;
|
|
30
|
+
const WORKSPACE_FILE_CACHE_MS = 1_000;
|
|
31
|
+
const IGNORED_WORKSPACE_DIRECTORIES = new Set([".git", "node_modules", ".pi", "dist", "build"]);
|
|
26
32
|
class PayloadTooLargeError extends Error {
|
|
27
33
|
constructor() {
|
|
28
34
|
super("Request body is too large");
|
|
@@ -673,6 +679,106 @@ function gitCommand(cwd, args, timeoutMs = GIT_TIMEOUT_MS) {
|
|
|
673
679
|
});
|
|
674
680
|
});
|
|
675
681
|
}
|
|
682
|
+
function ignoredWorkspaceDirectory(name) {
|
|
683
|
+
return IGNORED_WORKSPACE_DIRECTORIES.has(process.platform === "win32" || process.platform === "darwin" ? name.toLowerCase() : name);
|
|
684
|
+
}
|
|
685
|
+
async function existingWorkspaceFiles(root, candidates, truncated) {
|
|
686
|
+
const canonicalRoot = await realpath(resolve(root));
|
|
687
|
+
const paths = [];
|
|
688
|
+
for (let start = 0; start < candidates.length && paths.length < MAX_WORKSPACE_FILES; start += 64) {
|
|
689
|
+
const batch = candidates.slice(start, start + 64);
|
|
690
|
+
const existing = await Promise.all(batch.map(async (path) => {
|
|
691
|
+
const target = resolve(canonicalRoot, path);
|
|
692
|
+
if (escapesRoot(relative(canonicalRoot, target)))
|
|
693
|
+
return undefined;
|
|
694
|
+
try {
|
|
695
|
+
const canonical = await realpath(target);
|
|
696
|
+
if (escapesRoot(relative(canonicalRoot, canonical)) || !(await stat(canonical)).isFile())
|
|
697
|
+
return undefined;
|
|
698
|
+
return path;
|
|
699
|
+
}
|
|
700
|
+
catch {
|
|
701
|
+
return undefined;
|
|
702
|
+
}
|
|
703
|
+
}));
|
|
704
|
+
for (const path of existing) {
|
|
705
|
+
if (path !== undefined)
|
|
706
|
+
paths.push(path);
|
|
707
|
+
if (paths.length >= MAX_WORKSPACE_FILES)
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return { paths: paths.sort(), truncated: truncated || paths.length >= MAX_WORKSPACE_FILES };
|
|
712
|
+
}
|
|
713
|
+
async function gitWorkspaceFiles(root) {
|
|
714
|
+
const result = await gitCommand(root, ["ls-files", "--cached", "--others", "--exclude-standard", "--deduplicate", "-z"]);
|
|
715
|
+
if (result.code !== 0 && result.terminated !== "output-limit") {
|
|
716
|
+
if (/not a git repository/iu.test(result.stderr))
|
|
717
|
+
return undefined;
|
|
718
|
+
throw new Error(result.terminated ? gitTerminationMessage("ls-files", result.terminated) : result.stderr.trim() || "Unable to list workspace files");
|
|
719
|
+
}
|
|
720
|
+
const lastBoundary = result.stdout.lastIndexOf("\0");
|
|
721
|
+
const completeOutput = result.terminated === "output-limit" ? result.stdout.slice(0, lastBoundary + 1) : result.stdout;
|
|
722
|
+
const allCandidates = [...new Set(completeOutput.split("\0").filter((path) => path !== "" && !path.includes("\uFFFD")))];
|
|
723
|
+
const candidates = allCandidates.slice(0, MAX_WORKSPACE_FILE_CANDIDATES);
|
|
724
|
+
return existingWorkspaceFiles(root, candidates, result.terminated === "output-limit" || allCandidates.length > candidates.length);
|
|
725
|
+
}
|
|
726
|
+
async function walkedWorkspaceFiles(root) {
|
|
727
|
+
const canonicalRoot = await realpath(resolve(root));
|
|
728
|
+
const pending = [{ directory: canonicalRoot, depth: 0 }];
|
|
729
|
+
const paths = [];
|
|
730
|
+
let directories = 0;
|
|
731
|
+
let scanned = 0;
|
|
732
|
+
let truncated = false;
|
|
733
|
+
while (pending.length > 0 && paths.length < MAX_WORKSPACE_FILES && directories < MAX_WORKSPACE_DIRECTORIES && scanned < MAX_WORKSPACE_FILE_CANDIDATES) {
|
|
734
|
+
const current = pending.shift();
|
|
735
|
+
if (current === undefined)
|
|
736
|
+
break;
|
|
737
|
+
directories += 1;
|
|
738
|
+
try {
|
|
739
|
+
const handle = await opendir(current.directory);
|
|
740
|
+
for await (const entry of handle) {
|
|
741
|
+
scanned += 1;
|
|
742
|
+
if (scanned > MAX_WORKSPACE_FILE_CANDIDATES) {
|
|
743
|
+
truncated = true;
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
const name = entry.name;
|
|
747
|
+
if (name.includes("\uFFFD")) {
|
|
748
|
+
truncated = true;
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
const target = join(current.directory, name);
|
|
752
|
+
if (entry.isSymbolicLink())
|
|
753
|
+
continue;
|
|
754
|
+
if (entry.isFile()) {
|
|
755
|
+
paths.push(relative(canonicalRoot, target).split(sep).join("/"));
|
|
756
|
+
if (paths.length >= MAX_WORKSPACE_FILES) {
|
|
757
|
+
truncated = true;
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
else if (entry.isDirectory() && !ignoredWorkspaceDirectory(name)) {
|
|
762
|
+
if (current.depth >= MAX_WORKSPACE_DEPTH)
|
|
763
|
+
truncated = true;
|
|
764
|
+
else
|
|
765
|
+
pending.push({ directory: target, depth: current.depth + 1 });
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
catch (error) {
|
|
770
|
+
if (current.directory === canonicalRoot)
|
|
771
|
+
throw error;
|
|
772
|
+
truncated = true;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (pending.length > 0 || directories >= MAX_WORKSPACE_DIRECTORIES || scanned >= MAX_WORKSPACE_FILE_CANDIDATES)
|
|
776
|
+
truncated = true;
|
|
777
|
+
return { paths: paths.sort(), truncated };
|
|
778
|
+
}
|
|
779
|
+
async function workspaceFiles(root) {
|
|
780
|
+
return (await gitWorkspaceFiles(root)) ?? walkedWorkspaceFiles(root);
|
|
781
|
+
}
|
|
676
782
|
function probeEveryApiCliAuth() {
|
|
677
783
|
const executable = process.env.EVERYAPI_CLI_PATH?.trim() || "everyapi";
|
|
678
784
|
return new Promise((resolveStatus) => {
|
|
@@ -774,6 +880,25 @@ export default {
|
|
|
774
880
|
};
|
|
775
881
|
const disposePluginPanels = registerPluginPanels(context, services);
|
|
776
882
|
let busy = false;
|
|
883
|
+
let workspaceFileCache;
|
|
884
|
+
let workspaceFileRequest;
|
|
885
|
+
const readWorkspaceFiles = (cwd) => {
|
|
886
|
+
if (workspaceFileCache?.cwd === cwd && workspaceFileCache.expiresAt > Date.now())
|
|
887
|
+
return Promise.resolve(workspaceFileCache.catalogue);
|
|
888
|
+
if (workspaceFileRequest?.cwd === cwd)
|
|
889
|
+
return workspaceFileRequest.promise;
|
|
890
|
+
const promise = workspaceFiles(cwd)
|
|
891
|
+
.then((catalogue) => {
|
|
892
|
+
workspaceFileCache = { cwd, expiresAt: Date.now() + WORKSPACE_FILE_CACHE_MS, catalogue };
|
|
893
|
+
return catalogue;
|
|
894
|
+
})
|
|
895
|
+
.finally(() => {
|
|
896
|
+
if (workspaceFileRequest?.promise === promise)
|
|
897
|
+
workspaceFileRequest = undefined;
|
|
898
|
+
});
|
|
899
|
+
workspaceFileRequest = { cwd, promise };
|
|
900
|
+
return promise;
|
|
901
|
+
};
|
|
777
902
|
const events = [];
|
|
778
903
|
const eventClients = new Set();
|
|
779
904
|
const initialRunAt = Date.now();
|
|
@@ -1537,6 +1662,21 @@ export default {
|
|
|
1537
1662
|
sendJson(response, 200, { items, ...(status.truncated ? { truncated: true } : {}) });
|
|
1538
1663
|
},
|
|
1539
1664
|
});
|
|
1665
|
+
const disposeWorkspaceFiles = services.webServer.register({
|
|
1666
|
+
path: "/api/workspace/files",
|
|
1667
|
+
async handler(_request, response) {
|
|
1668
|
+
try {
|
|
1669
|
+
const catalogue = await readWorkspaceFiles(activeCwd(services));
|
|
1670
|
+
sendJson(response, 200, {
|
|
1671
|
+
items: catalogue.paths.map((path) => ({ path, status: "", label: "workspace" })),
|
|
1672
|
+
truncated: catalogue.truncated,
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
catch (error) {
|
|
1676
|
+
sendJson(response, 500, { error: errorText(error) });
|
|
1677
|
+
}
|
|
1678
|
+
},
|
|
1679
|
+
});
|
|
1540
1680
|
const disposeFileDiff = services.webServer.register({
|
|
1541
1681
|
path: "/api/files/diff",
|
|
1542
1682
|
async handler(request, response) {
|
|
@@ -2270,6 +2410,7 @@ export default {
|
|
|
2270
2410
|
disposeWorkspaces();
|
|
2271
2411
|
disposePickDirectory();
|
|
2272
2412
|
disposeFiles();
|
|
2413
|
+
disposeWorkspaceFiles();
|
|
2273
2414
|
disposeFileDiff();
|
|
2274
2415
|
disposeFileCommit();
|
|
2275
2416
|
disposeFileRevert();
|