@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
- * Repo/workdir helpers for the coding engine.
6
- *
7
- * These lived in `coding/tmux.ts` and have nothing to do with tmux — `ensureRepo` runs `git
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proagentstore/cli",
3
- "version": "0.4.41",
3
+ "version": "0.4.42",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",