diff --git a/packages/cli/package.json b/packages/cli/package.json index 25cd4c825..d7b76af09 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openrouter/spawn", - "version": "1.0.35", + "version": "1.0.36", "type": "module", "bin": { "spawn": "cli.js" diff --git a/packages/cli/src/__tests__/export.test.ts b/packages/cli/src/__tests__/export.test.ts index 6d99e1a26..6a4510655 100644 --- a/packages/cli/src/__tests__/export.test.ts +++ b/packages/cli/src/__tests__/export.test.ts @@ -217,6 +217,18 @@ describe("buildExportScript", () => { expect(s).not.toContain("Possible secrets detected in staged files; aborting"); }); + it("uses '#' as the sed delimiter — '|' would clash with SECRET_REGEX alternation", () => { + // Regression: the sed substitution previously used '|' as its delimiter + // ("s|${SECRET_REGEX}|${REDACT}|g"). Because SECRET_REGEX itself contains + // '|' (it's a |-separated alternation of provider patterns), bash + // expansion produced a string sed parsed with the wrong number of fields, + // failing with "unknown option to `s'". '#' is absent from both the regex + // and the placeholder, so the substitution is unambiguous. + const s = buildExportScript(opts); + expect(s).toContain('sed -i -E "s#${SECRET_REGEX}#${REDACT_PLACEHOLDER}#g"'); + expect(s).not.toContain('sed -i -E "s|${SECRET_REGEX}|${REDACT_PLACEHOLDER}|g"'); + }); + it("pauses before commit with needs_confirmation when ALLOW_REDACT=0 (first pass)", () => { const s = buildExportScript({ ...opts, diff --git a/packages/cli/src/commands/export.ts b/packages/cli/src/commands/export.ts index 60e73ccc7..76ef6f55c 100644 --- a/packages/cli/src/commands/export.ts +++ b/packages/cli/src/commands/export.ts @@ -345,7 +345,11 @@ export function buildExportScript(opts: { ' printf "%s\\n" "$SECRET_HITS" >&2', " while IFS= read -r f; do", ' [ -z "$f" ] && continue', - ' sed -i -E "s|${SECRET_REGEX}|${REDACT_PLACEHOLDER}|g" "$f"', + " # Delimiter is '#' — SECRET_REGEX contains '|' (alternation), so '|'", + " # as the sed delimiter would close the pattern at the first alternative", + " # (\"unknown option to s\"). '#' appears in neither the regex nor the", + " # placeholder, so the substitution is unambiguous.", + ' sed -i -E "s#${SECRET_REGEX}#${REDACT_PLACEHOLDER}#g" "$f"', ' done <<< "$SECRET_HITS"', " # Re-stage so the redacted blobs replace the originals in the index.", " git add -A",