run402 4.70.2 → 4.70.3
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/gitvault-surface.json +1 -1
- package/lib/path-lookup.mjs +29 -1
- package/lib/path-lookup.test.mjs +28 -2
- package/lib/repos.mjs +14 -0
- package/package.json +1 -1
package/gitvault-surface.json
CHANGED
package/lib/path-lookup.mjs
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* subprocess — a plain directory scan mirrors what the OS loader itself
|
|
7
7
|
* does to resolve an unqualified command name.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import {accessSync, constants, readFileSync } from "node:fs";
|
|
10
10
|
import { delimiter, join } from "node:path";
|
|
11
11
|
|
|
12
12
|
/** True iff `name` resolves to an executable file somewhere on `PATH`. Windows `PATHEXT` is out of scope — this CLI targets POSIX (`engines.node` + the hardened-git doc comments assume it) — so no `.exe`/`.cmd` suffix search. */
|
|
@@ -22,3 +22,31 @@ export function isExecutableOnPath(name, env = process.env) {
|
|
|
22
22
|
}
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* After a restore into `dir`, the ONE thing that still stands between the
|
|
28
|
+
* caller and its first `git push` is git finding the remote helper the
|
|
29
|
+
* checkout's origin scheme names (`git-remote-kygit` for `kygit::`,
|
|
30
|
+
* `git-remote-run402` for `run402::`). A resume or join run through
|
|
31
|
+
* `npx` has the helper only inside the npx cache, so git cannot see it.
|
|
32
|
+
* Returns a `next_actions` entry naming the fix, or null when the helper
|
|
33
|
+
* is on PATH or the checkout's remote cannot be read (never throws).
|
|
34
|
+
*/
|
|
35
|
+
export function remoteHelperNextAction(dir, env = process.env) {
|
|
36
|
+
let url = null;
|
|
37
|
+
try {
|
|
38
|
+
const cfg = readFileSync(join(dir, ".git", "config"), "utf8");
|
|
39
|
+
const m = cfg.match(/\[remote "origin"\][^[]*?\n\s*url\s*=\s*(\S+)/);
|
|
40
|
+
url = m ? m[1] : null;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (!url) return null;
|
|
45
|
+
const helper = url.startsWith("kygit::") ? "git-remote-kygit" : url.startsWith("run402::") ? "git-remote-run402" : null;
|
|
46
|
+
if (!helper || isExecutableOnPath(helper, env)) return null;
|
|
47
|
+
return {
|
|
48
|
+
type: "install_remote_helper",
|
|
49
|
+
command: "npm i -g @kychee/kygit run402",
|
|
50
|
+
why: `git push needs ${helper} on PATH and this session does not have it there (an npx run keeps it inside the npx cache); install both packages once and every git command in this checkout works`,
|
|
51
|
+
};
|
|
52
|
+
}
|
package/lib/path-lookup.test.mjs
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { describe, it } from "node:test";
|
|
8
8
|
import assert from "node:assert/strict";
|
|
9
|
-
import { mkdtempSync, chmodSync, writeFileSync, rmSync } from "node:fs";
|
|
9
|
+
import { mkdtempSync, mkdirSync, chmodSync, writeFileSync, rmSync } from "node:fs";
|
|
10
10
|
import { tmpdir } from "node:os";
|
|
11
11
|
import { join } from "node:path";
|
|
12
|
-
import { isExecutableOnPath } from "./path-lookup.mjs";
|
|
12
|
+
import { isExecutableOnPath, remoteHelperNextAction } from "./path-lookup.mjs";
|
|
13
13
|
|
|
14
14
|
describe("isExecutableOnPath", () => {
|
|
15
15
|
it("finds an executable file on PATH", () => {
|
|
@@ -64,3 +64,29 @@ describe("isExecutableOnPath", () => {
|
|
|
64
64
|
assert.equal(isExecutableOnPath("git-remote-kygit", { PATH: "" }), false);
|
|
65
65
|
});
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
describe("remoteHelperNextAction", () => {
|
|
69
|
+
it("names the missing helper for the checkout's remote scheme, and nothing when it is on PATH or the checkout is unreadable", () => {
|
|
70
|
+
const dir = mkdtempSync(join(tmpdir(), "run402-remote-helper-"));
|
|
71
|
+
try {
|
|
72
|
+
mkdirSync(join(dir, ".git"));
|
|
73
|
+
writeFileSync(join(dir, ".git", "config"), `[core]\n\trepositoryformatversion = 0\n[remote "origin"]\n\turl = kygit::org/name\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n`);
|
|
74
|
+
const emptyPath = { PATH: dir };
|
|
75
|
+
const missing = remoteHelperNextAction(dir, emptyPath);
|
|
76
|
+
assert.equal(missing?.type, "install_remote_helper");
|
|
77
|
+
assert.match(missing.why, /git-remote-kygit/);
|
|
78
|
+
assert.equal(missing.command, "npm i -g @kychee/kygit run402");
|
|
79
|
+
// present on PATH → nothing to say
|
|
80
|
+
const bin = join(dir, "bin"); mkdirSync(bin);
|
|
81
|
+
writeFileSync(join(bin, "git-remote-kygit"), "#!/bin/sh\n"); chmodSync(join(bin, "git-remote-kygit"), 0o755);
|
|
82
|
+
assert.equal(remoteHelperNextAction(dir, { PATH: bin }), null);
|
|
83
|
+
// run402:: scheme names the other helper
|
|
84
|
+
writeFileSync(join(dir, ".git", "config"), `[remote "origin"]\n\turl = run402::org/name\n`);
|
|
85
|
+
assert.match(remoteHelperNextAction(dir, emptyPath).why, /git-remote-run402/);
|
|
86
|
+
// no checkout → null, never a throw
|
|
87
|
+
assert.equal(remoteHelperNextAction(join(dir, "nope"), emptyPath), null);
|
|
88
|
+
} finally {
|
|
89
|
+
rmSync(dir, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
package/lib/repos.mjs
CHANGED
|
@@ -1918,6 +1918,13 @@ async function resume(args) {
|
|
|
1918
1918
|
try {
|
|
1919
1919
|
const result = await sdk.gitvault.resume({ key, ...(to != null ? { to } : {}), onLine: (line) => console.error(line) });
|
|
1920
1920
|
if (coldStart.next_action) result.next_actions = [...(result.next_actions ?? []), coldStart.next_action];
|
|
1921
|
+
// The restored checkout's first git command needs the remote helper on
|
|
1922
|
+
// PATH; an npx-run resume/join has it only in the npx cache. Name it.
|
|
1923
|
+
try {
|
|
1924
|
+
const { remoteHelperNextAction } = await import("./path-lookup.mjs");
|
|
1925
|
+
const helperAction = result.restored?.dir ? remoteHelperNextAction(result.restored.dir) : null;
|
|
1926
|
+
if (helperAction) result.next_actions = [...(result.next_actions ?? []), helperAction];
|
|
1927
|
+
} catch { /* never fails a completed claim */ }
|
|
1921
1928
|
if (a.includes("--json")) {
|
|
1922
1929
|
printJson(sdk, { ...result, cold_start: coldStart });
|
|
1923
1930
|
} else {
|
|
@@ -2111,6 +2118,13 @@ async function joinInvite(args) {
|
|
|
2111
2118
|
} catch {
|
|
2112
2119
|
// never fails a completed join
|
|
2113
2120
|
}
|
|
2121
|
+
// The restored checkout's first git command needs the remote helper on
|
|
2122
|
+
// PATH; an npx-run resume/join has it only in the npx cache. Name it.
|
|
2123
|
+
try {
|
|
2124
|
+
const { remoteHelperNextAction } = await import("./path-lookup.mjs");
|
|
2125
|
+
const helperAction = result.restored?.dir ? remoteHelperNextAction(result.restored.dir) : null;
|
|
2126
|
+
if (helperAction) result.next_actions = [...(result.next_actions ?? []), helperAction];
|
|
2127
|
+
} catch { /* never fails a completed claim */ }
|
|
2114
2128
|
if (a.includes("--json")) {
|
|
2115
2129
|
printJson(sdk, { ...result, cold_start: coldStart });
|
|
2116
2130
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "run402",
|
|
3
|
-
"version": "4.70.
|
|
3
|
+
"version": "4.70.3",
|
|
4
4
|
"description": "CLI for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|