fix(windows): node wrapper updateAoMetadata supports V2 .json metadata format
The Windows Node.js gh/git wrappers in NODE_UPDATE_AO_METADATA only tried the bare session path (e.g. ao-154), but V2 storage uses ao-154.json files. This caused silent metadata update failures on Windows — PR URLs written by agents via `gh pr create` were never recorded in session metadata. Fix mirrors bash ao-metadata-helper.sh: try .json first (V2), fall back to bare name (V1/legacy). Also adds JSON.parse/stringify handling for V2 JSON format instead of the key=value line-splitting that only worked for V1. Bump WRAPPER_VERSION 0.6.0 → 0.7.0 to force reinstall on existing setups.
This commit is contained in:
parent
720cbfcb26
commit
6d9c38ae33
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -97,7 +97,7 @@ describe("setupPathWrapperWorkspace (Unix)", () => {
|
|||
|
||||
it("skips wrapper rewrite when version matches", async () => {
|
||||
mockReadFile
|
||||
.mockResolvedValueOnce("0.6.0") // version marker matches
|
||||
.mockResolvedValueOnce("0.7.0") // version marker matches
|
||||
.mockRejectedValueOnce(new Error("ENOENT")); // AGENTS.md doesn't exist
|
||||
|
||||
await setupPathWrapperWorkspace("/workspace");
|
||||
|
|
@ -269,6 +269,27 @@ describe("buildNodeWrapper", () => {
|
|||
expect(script).toContain("fs.existsSync");
|
||||
expect(script).toContain("findRealGh()");
|
||||
});
|
||||
|
||||
it("updateAoMetadata in node wrappers handles V2 .json metadata format", () => {
|
||||
const ghScript = buildNodeWrapper("gh", "");
|
||||
// Must try the .json extension first (V2 storage format)
|
||||
expect(ghScript).toContain('aoSession + ".json"');
|
||||
// Must fall back to bare name (V1 legacy format)
|
||||
expect(ghScript).toContain("path.join(resolvedDir, aoSession)");
|
||||
// Must handle JSON.parse/stringify for V2 format
|
||||
expect(ghScript).toContain("JSON.parse");
|
||||
expect(ghScript).toContain("JSON.stringify");
|
||||
// Must handle key=value lines for V1 format
|
||||
expect(ghScript).toContain('split("\\n")');
|
||||
});
|
||||
|
||||
it("updateAoMetadata is shared between gh and git wrappers", () => {
|
||||
const ghScript = buildNodeWrapper("gh", "");
|
||||
const gitScript = buildNodeWrapper("git", "");
|
||||
// Both wrappers should contain the V2 JSON format support
|
||||
expect(ghScript).toContain('aoSession + ".json"');
|
||||
expect(gitScript).toContain('aoSession + ".json"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("AO_METADATA_HELPER", () => {
|
||||
|
|
@ -400,7 +421,7 @@ describe("GH_WRAPPER", () => {
|
|||
});
|
||||
|
||||
it("uses current wrapper version in trace logging", () => {
|
||||
expect(GH_WRAPPER).toContain("0.6.0");
|
||||
expect(GH_WRAPPER).toContain("0.7.0");
|
||||
});
|
||||
|
||||
it("logs cache outcomes (hit/miss-stored/miss-negative/miss-error) to trace", () => {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ function getAoBinDir(): string {
|
|||
}
|
||||
|
||||
/** Current version of wrapper scripts — bump when scripts change */
|
||||
const WRAPPER_VERSION = "0.6.0";
|
||||
const WRAPPER_VERSION = "0.7.0";
|
||||
|
||||
// =============================================================================
|
||||
// PATH Builder
|
||||
|
|
@ -666,7 +666,11 @@ function updateAoMetadata(key, value) {
|
|||
const allowed = [path.join(home, ".ao"), path.join(home, ".agent-orchestrator"), os.tmpdir()];
|
||||
if (!allowed.some(a => resolvedDir === a || resolvedDir.startsWith(a + sep))) return;
|
||||
|
||||
const metadataFile = path.join(resolvedDir, aoSession);
|
||||
// Try V2 (.json) first, then fall back to V1 (bare) — mirrors bash ao-metadata-helper.sh
|
||||
let metadataFile = path.join(resolvedDir, aoSession + ".json");
|
||||
if (!fs.existsSync(metadataFile)) {
|
||||
metadataFile = path.join(resolvedDir, aoSession);
|
||||
}
|
||||
if (!fs.existsSync(metadataFile)) return;
|
||||
|
||||
// Strip newlines from value
|
||||
|
|
@ -675,18 +679,26 @@ function updateAoMetadata(key, value) {
|
|||
let content;
|
||||
try { content = fs.readFileSync(metadataFile, "utf8"); } catch { return; }
|
||||
|
||||
const lines = content.split("\\n");
|
||||
const keyPrefix = key + "=";
|
||||
const idx = lines.findIndex(l => l.startsWith(keyPrefix));
|
||||
if (idx >= 0) {
|
||||
lines[idx] = key + "=" + cleanValue;
|
||||
} else {
|
||||
lines.push(key + "=" + cleanValue);
|
||||
}
|
||||
|
||||
const tmpFile = metadataFile + ".tmp." + process.pid;
|
||||
try {
|
||||
fs.writeFileSync(tmpFile, lines.join("\\n"), "utf8");
|
||||
if (metadataFile.endsWith(".json")) {
|
||||
// V2 JSON format
|
||||
let d;
|
||||
try { d = JSON.parse(content); } catch { return; }
|
||||
d[key] = cleanValue;
|
||||
fs.writeFileSync(tmpFile, JSON.stringify(d, null, 2), "utf8");
|
||||
} else {
|
||||
// V1 key=value format
|
||||
const lines = content.split("\\n");
|
||||
const keyPrefix = key + "=";
|
||||
const idx = lines.findIndex(l => l.startsWith(keyPrefix));
|
||||
if (idx >= 0) {
|
||||
lines[idx] = key + "=" + cleanValue;
|
||||
} else {
|
||||
lines.push(key + "=" + cleanValue);
|
||||
}
|
||||
fs.writeFileSync(tmpFile, lines.join("\\n"), "utf8");
|
||||
}
|
||||
fs.renameSync(tmpFile, metadataFile);
|
||||
} catch {
|
||||
try { fs.unlinkSync(tmpFile); } catch {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue