@proagentstore/cli 0.4.41 → 0.4.42
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.
|
@@ -1,15 +1,49 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* clone`. The coding engine stopped using tmux when it moved to the structured stream-json
|
|
9
|
-
* interface, so anyone cleaning up that module found its two most load-bearing functions
|
|
10
|
-
* inside it (#247). Split out so the tmux module is only tmux, and only the terminal-operator
|
|
11
|
-
* agents depend on it.
|
|
5
|
+
* Inspect a workdir: does it exist, does it hold anything, is it a checkout. Never throws —
|
|
6
|
+
* every failure is a `false`, because this function exists to REPORT a broken path and one that
|
|
7
|
+
* threw would be reported as a broken runner instead.
|
|
12
8
|
*/
|
|
9
|
+
export function checkWorkdir(dir) {
|
|
10
|
+
const absent = { checked: true, path: dir, exists: false, isDirectory: false, entryCount: 0, insideWorkTree: false, gitChecked: true };
|
|
11
|
+
let isDirectory = false;
|
|
12
|
+
try {
|
|
13
|
+
isDirectory = statSync(dir).isDirectory();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return { ...absent };
|
|
17
|
+
}
|
|
18
|
+
if (!isDirectory)
|
|
19
|
+
return { ...absent, exists: true };
|
|
20
|
+
let entryCount = 0;
|
|
21
|
+
try {
|
|
22
|
+
entryCount = readdirSync(dir).length;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Unreadable (permissions) — reported as empty rather than as a crash; the cloud's
|
|
26
|
+
// message for "empty" tells the owner to look at the path either way.
|
|
27
|
+
}
|
|
28
|
+
let insideWorkTree = false;
|
|
29
|
+
let gitChecked = true;
|
|
30
|
+
try {
|
|
31
|
+
const out = execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
32
|
+
cwd: dir,
|
|
33
|
+
encoding: "utf-8",
|
|
34
|
+
timeout: 10_000,
|
|
35
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
36
|
+
});
|
|
37
|
+
insideWorkTree = out.trim() === "true";
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
// Exit 128 ("not a git repository") is an ANSWER. ENOENT is git missing from PATH, which
|
|
41
|
+
// is not — and reporting it as "not a checkout" would condemn every repo on that machine.
|
|
42
|
+
if (e?.code === "ENOENT")
|
|
43
|
+
gitChecked = false;
|
|
44
|
+
}
|
|
45
|
+
return { checked: true, path: dir, exists: true, isDirectory, entryCount, insideWorkTree, gitChecked };
|
|
46
|
+
}
|
|
13
47
|
/**
|
|
14
48
|
* A safe, collision-resistant label derived from an arbitrary string.
|
|
15
49
|
*
|
|
@@ -2,7 +2,7 @@ import { homedir } from "node:os";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { defaultStatePath, HeadlessSession } from "./headless.js";
|
|
4
4
|
import { InspectError, readGitRemoteOrigin, readRepoFile, repoTree, runRepoGit } from "./inspect.js";
|
|
5
|
-
import { ensureRepo, sanitizeSessionName } from "./repo.js";
|
|
5
|
+
import { checkWorkdir, ensureRepo, sanitizeSessionName } from "./repo.js";
|
|
6
6
|
/** Hard cap on a pane returned to the brain/console (matches the worker MAX_PANE_CHARS). */
|
|
7
7
|
const MAX_PANE = 64 * 1024;
|
|
8
8
|
export class CodingRuntime {
|
|
@@ -58,6 +58,16 @@ export class CodingRuntime {
|
|
|
58
58
|
gitRemote(input) {
|
|
59
59
|
return { remote: readGitRemoteOrigin(this.resolveWorkDir(input)) };
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Is the configured workdir usable AT ALL — present, non-empty, a checkout (#405)?
|
|
63
|
+
*
|
|
64
|
+
* The only endpoint here that answers a question about the PATH rather than about its
|
|
65
|
+
* contents, and the one the cloud needs before it may call a repo `ready`. Reports the
|
|
66
|
+
* resolved path so a message can name the thing the owner actually typed, `~` and all.
|
|
67
|
+
*/
|
|
68
|
+
checkRepo(input) {
|
|
69
|
+
return checkWorkdir(this.resolveWorkDir(input));
|
|
70
|
+
}
|
|
61
71
|
static taskTypes() {
|
|
62
72
|
return ["coding.session"];
|
|
63
73
|
}
|
|
@@ -232,6 +232,18 @@ async function route(runner, req, res) {
|
|
|
232
232
|
return json(res, 400, { error: e instanceof Error ? e.message : String(e) });
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
|
+
// Does the configured workdir exist / hold files / belong to a checkout (#405)? An older
|
|
236
|
+
// runner 404s this; the cloud reads the missing `checked:true` as "unverified" and leaves
|
|
237
|
+
// the repo's status alone rather than condemning it.
|
|
238
|
+
if (req.method === "POST" && path === "/coding/repo-check") {
|
|
239
|
+
const b = await readJson(req);
|
|
240
|
+
try {
|
|
241
|
+
return json(res, 200, runner.coding.checkRepo(b));
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
return json(res, 400, { error: e instanceof Error ? e.message : String(e) });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
235
247
|
if (req.method === "POST" && path === "/coding/tree") {
|
|
236
248
|
const b = await readJson(req);
|
|
237
249
|
try {
|