@hasna/instructions 0.4.11 → 0.4.12
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/dist/cli/index.js +76 -8
- package/dist/index.js +66 -4
- package/dist/lib/diff-redaction.test.d.ts +2 -0
- package/dist/lib/diff-redaction.test.d.ts.map +1 -0
- package/dist/lib/redact.d.ts +9 -0
- package/dist/lib/redact.d.ts.map +1 -1
- package/dist/lib/sync.d.ts +10 -0
- package/dist/lib/sync.d.ts.map +1 -1
- package/dist/mcp/index.js +69 -6
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -7550,6 +7550,9 @@ function scanSecrets(content, format) {
|
|
|
7550
7550
|
const r = redactContent(content, format);
|
|
7551
7551
|
return r.redacted;
|
|
7552
7552
|
}
|
|
7553
|
+
function isSecretVarName(name) {
|
|
7554
|
+
return SECRET_KEY_PATTERN.test(name);
|
|
7555
|
+
}
|
|
7553
7556
|
var SECRET_KEY_PATTERN, VALUE_PATTERNS, MIN_SECRET_VALUE_LEN = 8;
|
|
7554
7557
|
var init_redact = __esm(() => {
|
|
7555
7558
|
SECRET_KEY_PATTERN = /^(.*_?API_?KEY|.*_?TOKEN|.*_?SECRET|.*_?PASSWORD|.*_?PASSWD|.*_?CREDENTIAL|.*_?AUTH(?:_TOKEN|_KEY|ORIZATION)?|.*_?PRIVATE_?KEY|.*_?ACCESS_?KEY|.*_?CLIENT_?SECRET|.*_?SIGNING_?KEY|.*_?ENCRYPTION_?KEY|.*_AUTH_TOKEN)$/i;
|
|
@@ -12289,30 +12292,86 @@ async function syncToDisk(opts = {}) {
|
|
|
12289
12292
|
}
|
|
12290
12293
|
return result;
|
|
12291
12294
|
}
|
|
12292
|
-
function
|
|
12295
|
+
function redactionPlaceholders(storedLine) {
|
|
12296
|
+
const found = [...storedLine.match(CERTAIN_PLACEHOLDER_RE) ?? []];
|
|
12297
|
+
const assigned = storedLine.match(ASSIGNED_PLACEHOLDER_RE);
|
|
12298
|
+
if (assigned) {
|
|
12299
|
+
const name = assigned[3] ?? assigned[4] ?? assigned[5];
|
|
12300
|
+
if (name && isSecretVarName(name))
|
|
12301
|
+
found.push(assigned[2]);
|
|
12302
|
+
}
|
|
12303
|
+
return found;
|
|
12304
|
+
}
|
|
12305
|
+
function redactFormatForTarget(targetPath, storedFormat) {
|
|
12306
|
+
const p = targetPath.toLowerCase();
|
|
12307
|
+
if (/(^|\/)\.(zshrc|zprofile|bashrc|bash_profile|profile|zshenv|env)$/.test(p) || p.endsWith(".env"))
|
|
12308
|
+
return "shell";
|
|
12309
|
+
if (/(^|\/)\.(npmrc|yarnrc|curlrc|netrc)$/.test(p))
|
|
12310
|
+
return "ini";
|
|
12311
|
+
return storedFormat;
|
|
12312
|
+
}
|
|
12313
|
+
function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
|
|
12314
|
+
return redactionPlaceholders(storedLine).some((p) => !diskLine.includes(p));
|
|
12315
|
+
}
|
|
12316
|
+
function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
12293
12317
|
const path = expandPath(targetPath);
|
|
12294
12318
|
if (!existsSync9(path))
|
|
12295
12319
|
return `(file not found on disk: ${path})`;
|
|
12296
12320
|
const diskContent = readFileSync6(path, "utf-8");
|
|
12297
12321
|
if (diskContent === expectedContent)
|
|
12298
12322
|
return "(no diff \u2014 identical)";
|
|
12323
|
+
const format = redactFormatForTarget(targetPath, storedFormat);
|
|
12299
12324
|
const stored = expectedContent.split(`
|
|
12300
12325
|
`);
|
|
12301
12326
|
const disk = diskContent.split(`
|
|
12302
12327
|
`);
|
|
12328
|
+
const { content: redactedDiskContent, redacted } = showSecrets ? { content: diskContent, redacted: [] } : redactContent(diskContent, format);
|
|
12329
|
+
const redactedDisk = redactedDiskContent.split(`
|
|
12330
|
+
`);
|
|
12331
|
+
const reasonByLine = new Map;
|
|
12332
|
+
for (const r of redacted)
|
|
12333
|
+
if (!reasonByLine.has(r.line))
|
|
12334
|
+
reasonByLine.set(r.line, r.reason);
|
|
12303
12335
|
const lines = [`--- stored (DB)`, `+++ disk (${path})`];
|
|
12304
12336
|
const maxLen = Math.max(stored.length, disk.length);
|
|
12305
12337
|
for (let i = 0;i < maxLen; i++) {
|
|
12338
|
+
const lineNo = i + 1;
|
|
12306
12339
|
const s = stored[i];
|
|
12307
12340
|
const dk = disk[i];
|
|
12308
12341
|
if (s === dk) {
|
|
12309
12342
|
if (s !== undefined)
|
|
12310
12343
|
lines.push(` ${s}`);
|
|
12311
|
-
|
|
12344
|
+
continue;
|
|
12345
|
+
}
|
|
12346
|
+
if (showSecrets || dk === undefined) {
|
|
12312
12347
|
if (s !== undefined)
|
|
12313
12348
|
lines.push(`-${s}`);
|
|
12314
12349
|
if (dk !== undefined)
|
|
12315
12350
|
lines.push(`+${dk}`);
|
|
12351
|
+
continue;
|
|
12352
|
+
}
|
|
12353
|
+
const structural = s !== undefined && storedPlaceholderIsLiteralOnDisk(s, dk);
|
|
12354
|
+
const patterned = reasonByLine.has(lineNo);
|
|
12355
|
+
if (!structural && !patterned) {
|
|
12356
|
+
if (s !== undefined)
|
|
12357
|
+
lines.push(`-${s}`);
|
|
12358
|
+
lines.push(`+${dk}`);
|
|
12359
|
+
continue;
|
|
12360
|
+
}
|
|
12361
|
+
if (structural) {
|
|
12362
|
+
const placeholder = redactionPlaceholders(s).find((p) => !dk.includes(p)) ?? "a placeholder";
|
|
12363
|
+
lines.push(`~line ${lineNo} differs: stored \`${s}\`, disk holds a literal value for ${placeholder} \u2014 redacted (use --show-secrets on a TTY to reveal)`);
|
|
12364
|
+
continue;
|
|
12365
|
+
}
|
|
12366
|
+
const safe = redactedDisk[i] ?? "";
|
|
12367
|
+
const reason = reasonByLine.get(lineNo);
|
|
12368
|
+
if (s !== undefined && safe === s) {
|
|
12369
|
+
lines.push(`~line ${lineNo} differs: stored \`${s}\`, disk holds a literal value (${reason}) \u2014 redacted (use --show-secrets on a TTY to reveal)`);
|
|
12370
|
+
} else {
|
|
12371
|
+
if (s !== undefined)
|
|
12372
|
+
lines.push(`-${s}`);
|
|
12373
|
+
lines.push(`+${safe}`);
|
|
12374
|
+
lines.push(`~line ${lineNo}: disk value redacted (${reason}) \u2014 use --show-secrets on a TTY to reveal`);
|
|
12316
12375
|
}
|
|
12317
12376
|
}
|
|
12318
12377
|
return lines.join(`
|
|
@@ -12327,15 +12386,16 @@ async function diffConfig(config, opts = {}) {
|
|
|
12327
12386
|
if (isGeneratedOutputTarget2(config, outputOwnerIdsByTarget(contextConfigs))) {
|
|
12328
12387
|
return "(generated output \u2014 managed by fan-out)";
|
|
12329
12388
|
}
|
|
12389
|
+
const showSecrets = opts.showSecrets ?? false;
|
|
12330
12390
|
if (config.target_path) {
|
|
12331
|
-
const diff = buildDiff(config.content, config.target_path);
|
|
12391
|
+
const diff = buildDiff(config.content, config.target_path, config.format, showSecrets);
|
|
12332
12392
|
if (!diff.includes("no diff"))
|
|
12333
12393
|
diffs.push(diff);
|
|
12334
12394
|
}
|
|
12335
12395
|
if (config.outputs.length > 0) {
|
|
12336
12396
|
for (const output of config.outputs) {
|
|
12337
12397
|
const expected = applyTransform(config, output, { configs: contextConfigs });
|
|
12338
|
-
const diff = buildDiff(expected, output.target_path);
|
|
12398
|
+
const diff = buildDiff(expected, output.target_path, detectFormat(output.target_path), showSecrets);
|
|
12339
12399
|
if (!diff.includes("no diff"))
|
|
12340
12400
|
diffs.push(diff);
|
|
12341
12401
|
}
|
|
@@ -12406,7 +12466,7 @@ function detectFormat(filePath) {
|
|
|
12406
12466
|
return "ini";
|
|
12407
12467
|
return "text";
|
|
12408
12468
|
}
|
|
12409
|
-
var CLAUDE_PROMPT_OUTPUTS, KNOWN_CONFIGS, PROJECT_CONFIG_FILES;
|
|
12469
|
+
var CLAUDE_PROMPT_OUTPUTS, KNOWN_CONFIGS, PROJECT_CONFIG_FILES, CERTAIN_PLACEHOLDER_RE, ASSIGNED_PLACEHOLDER_RE;
|
|
12410
12470
|
var init_sync = __esm(() => {
|
|
12411
12471
|
init_config_store();
|
|
12412
12472
|
init_apply();
|
|
@@ -12470,6 +12530,8 @@ var init_sync = __esm(() => {
|
|
|
12470
12530
|
{ file: ".agents/mcp_config.json", category: "mcp", agent: "antigravity", format: "json" },
|
|
12471
12531
|
{ file: ".qwen/settings.json", category: "agent", agent: "qwen", format: "json" }
|
|
12472
12532
|
];
|
|
12533
|
+
CERTAIN_PLACEHOLDER_RE = /\{\{[A-Za-z_][A-Za-z0-9_]*\}\}/g;
|
|
12534
|
+
ASSIGNED_PLACEHOLDER_RE = /[=:]\s*(["']?)(\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Z][A-Z0-9_]*)|%([A-Za-z_][A-Za-z0-9_]*)%)\1\s*,?\s*$/;
|
|
12473
12535
|
});
|
|
12474
12536
|
|
|
12475
12537
|
// src/lib/package-manager-guard.ts
|
|
@@ -15430,12 +15492,18 @@ program.command("apply <id>").description("Apply a config to its target_path and
|
|
|
15430
15492
|
process.exit(1);
|
|
15431
15493
|
}
|
|
15432
15494
|
});
|
|
15433
|
-
program.command("diff [id]").description("Show diff between stored config and disk (omit id for --all)").option("--all", "diff every known config against disk").action(async (id, opts) => {
|
|
15495
|
+
program.command("diff [id]").description("Show diff between stored config and disk (omit id for --all). Credential values on disk are redacted.").option("--all", "diff every known config against disk").option("--show-secrets", "render credential values from disk verbatim (interactive TTY only)").action(async (id, opts) => {
|
|
15434
15496
|
try {
|
|
15497
|
+
if (opts.showSecrets && !process.stdout.isTTY) {
|
|
15498
|
+
console.error(chalk.red("--show-secrets refuses to run on a non-TTY: the output would be captured verbatim into a log or transcript."));
|
|
15499
|
+
console.error(chalk.dim("Run it in an interactive terminal, or use the redacted default."));
|
|
15500
|
+
process.exit(1);
|
|
15501
|
+
}
|
|
15502
|
+
const showSecrets = Boolean(opts.showSecrets);
|
|
15435
15503
|
const store = resolveConfigStore();
|
|
15436
15504
|
if (id) {
|
|
15437
15505
|
const config = await store.getConfig(id);
|
|
15438
|
-
console.log(await diffConfig(config, { store }));
|
|
15506
|
+
console.log(await diffConfig(config, { store, showSecrets }));
|
|
15439
15507
|
return;
|
|
15440
15508
|
}
|
|
15441
15509
|
const configs = await store.listConfigs({ kind: "file" });
|
|
@@ -15443,7 +15511,7 @@ program.command("diff [id]").description("Show diff between stored config and di
|
|
|
15443
15511
|
for (const c of configs) {
|
|
15444
15512
|
if (!c.target_path)
|
|
15445
15513
|
continue;
|
|
15446
|
-
const diff = await diffConfig(c, { store });
|
|
15514
|
+
const diff = await diffConfig(c, { store, showSecrets });
|
|
15447
15515
|
if (diff.includes("no diff") || diff.includes("not found"))
|
|
15448
15516
|
continue;
|
|
15449
15517
|
drifted++;
|
package/dist/index.js
CHANGED
|
@@ -5515,6 +5515,9 @@ function scanSecrets(content, format) {
|
|
|
5515
5515
|
function hasSecrets(content, format) {
|
|
5516
5516
|
return scanSecrets(content, format).length > 0;
|
|
5517
5517
|
}
|
|
5518
|
+
function isSecretVarName(name) {
|
|
5519
|
+
return SECRET_KEY_PATTERN.test(name);
|
|
5520
|
+
}
|
|
5518
5521
|
|
|
5519
5522
|
// src/lib/session-render-contract.ts
|
|
5520
5523
|
var CODEWITH_NATIVE_IMPORTS_ENV = "HASNA_CONFIGS_CODEWITH_NATIVE_IMPORTS";
|
|
@@ -11680,30 +11683,88 @@ async function syncToDisk(opts = {}) {
|
|
|
11680
11683
|
}
|
|
11681
11684
|
return result;
|
|
11682
11685
|
}
|
|
11683
|
-
|
|
11686
|
+
var CERTAIN_PLACEHOLDER_RE = /\{\{[A-Za-z_][A-Za-z0-9_]*\}\}/g;
|
|
11687
|
+
var ASSIGNED_PLACEHOLDER_RE = /[=:]\s*(["']?)(\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Z][A-Z0-9_]*)|%([A-Za-z_][A-Za-z0-9_]*)%)\1\s*,?\s*$/;
|
|
11688
|
+
function redactionPlaceholders(storedLine) {
|
|
11689
|
+
const found = [...storedLine.match(CERTAIN_PLACEHOLDER_RE) ?? []];
|
|
11690
|
+
const assigned = storedLine.match(ASSIGNED_PLACEHOLDER_RE);
|
|
11691
|
+
if (assigned) {
|
|
11692
|
+
const name = assigned[3] ?? assigned[4] ?? assigned[5];
|
|
11693
|
+
if (name && isSecretVarName(name))
|
|
11694
|
+
found.push(assigned[2]);
|
|
11695
|
+
}
|
|
11696
|
+
return found;
|
|
11697
|
+
}
|
|
11698
|
+
function redactFormatForTarget(targetPath, storedFormat) {
|
|
11699
|
+
const p = targetPath.toLowerCase();
|
|
11700
|
+
if (/(^|\/)\.(zshrc|zprofile|bashrc|bash_profile|profile|zshenv|env)$/.test(p) || p.endsWith(".env"))
|
|
11701
|
+
return "shell";
|
|
11702
|
+
if (/(^|\/)\.(npmrc|yarnrc|curlrc|netrc)$/.test(p))
|
|
11703
|
+
return "ini";
|
|
11704
|
+
return storedFormat;
|
|
11705
|
+
}
|
|
11706
|
+
function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
|
|
11707
|
+
return redactionPlaceholders(storedLine).some((p) => !diskLine.includes(p));
|
|
11708
|
+
}
|
|
11709
|
+
function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
11684
11710
|
const path = expandPath(targetPath);
|
|
11685
11711
|
if (!existsSync11(path))
|
|
11686
11712
|
return `(file not found on disk: ${path})`;
|
|
11687
11713
|
const diskContent = readFileSync9(path, "utf-8");
|
|
11688
11714
|
if (diskContent === expectedContent)
|
|
11689
11715
|
return "(no diff \u2014 identical)";
|
|
11716
|
+
const format = redactFormatForTarget(targetPath, storedFormat);
|
|
11690
11717
|
const stored = expectedContent.split(`
|
|
11691
11718
|
`);
|
|
11692
11719
|
const disk = diskContent.split(`
|
|
11693
11720
|
`);
|
|
11721
|
+
const { content: redactedDiskContent, redacted } = showSecrets ? { content: diskContent, redacted: [] } : redactContent(diskContent, format);
|
|
11722
|
+
const redactedDisk = redactedDiskContent.split(`
|
|
11723
|
+
`);
|
|
11724
|
+
const reasonByLine = new Map;
|
|
11725
|
+
for (const r of redacted)
|
|
11726
|
+
if (!reasonByLine.has(r.line))
|
|
11727
|
+
reasonByLine.set(r.line, r.reason);
|
|
11694
11728
|
const lines = [`--- stored (DB)`, `+++ disk (${path})`];
|
|
11695
11729
|
const maxLen = Math.max(stored.length, disk.length);
|
|
11696
11730
|
for (let i = 0;i < maxLen; i++) {
|
|
11731
|
+
const lineNo = i + 1;
|
|
11697
11732
|
const s = stored[i];
|
|
11698
11733
|
const dk = disk[i];
|
|
11699
11734
|
if (s === dk) {
|
|
11700
11735
|
if (s !== undefined)
|
|
11701
11736
|
lines.push(` ${s}`);
|
|
11702
|
-
|
|
11737
|
+
continue;
|
|
11738
|
+
}
|
|
11739
|
+
if (showSecrets || dk === undefined) {
|
|
11703
11740
|
if (s !== undefined)
|
|
11704
11741
|
lines.push(`-${s}`);
|
|
11705
11742
|
if (dk !== undefined)
|
|
11706
11743
|
lines.push(`+${dk}`);
|
|
11744
|
+
continue;
|
|
11745
|
+
}
|
|
11746
|
+
const structural = s !== undefined && storedPlaceholderIsLiteralOnDisk(s, dk);
|
|
11747
|
+
const patterned = reasonByLine.has(lineNo);
|
|
11748
|
+
if (!structural && !patterned) {
|
|
11749
|
+
if (s !== undefined)
|
|
11750
|
+
lines.push(`-${s}`);
|
|
11751
|
+
lines.push(`+${dk}`);
|
|
11752
|
+
continue;
|
|
11753
|
+
}
|
|
11754
|
+
if (structural) {
|
|
11755
|
+
const placeholder = redactionPlaceholders(s).find((p) => !dk.includes(p)) ?? "a placeholder";
|
|
11756
|
+
lines.push(`~line ${lineNo} differs: stored \`${s}\`, disk holds a literal value for ${placeholder} \u2014 redacted (use --show-secrets on a TTY to reveal)`);
|
|
11757
|
+
continue;
|
|
11758
|
+
}
|
|
11759
|
+
const safe = redactedDisk[i] ?? "";
|
|
11760
|
+
const reason = reasonByLine.get(lineNo);
|
|
11761
|
+
if (s !== undefined && safe === s) {
|
|
11762
|
+
lines.push(`~line ${lineNo} differs: stored \`${s}\`, disk holds a literal value (${reason}) \u2014 redacted (use --show-secrets on a TTY to reveal)`);
|
|
11763
|
+
} else {
|
|
11764
|
+
if (s !== undefined)
|
|
11765
|
+
lines.push(`-${s}`);
|
|
11766
|
+
lines.push(`+${safe}`);
|
|
11767
|
+
lines.push(`~line ${lineNo}: disk value redacted (${reason}) \u2014 use --show-secrets on a TTY to reveal`);
|
|
11707
11768
|
}
|
|
11708
11769
|
}
|
|
11709
11770
|
return lines.join(`
|
|
@@ -11718,15 +11779,16 @@ async function diffConfig(config, opts = {}) {
|
|
|
11718
11779
|
if (isGeneratedOutputTarget2(config, outputOwnerIdsByTarget(contextConfigs))) {
|
|
11719
11780
|
return "(generated output \u2014 managed by fan-out)";
|
|
11720
11781
|
}
|
|
11782
|
+
const showSecrets = opts.showSecrets ?? false;
|
|
11721
11783
|
if (config.target_path) {
|
|
11722
|
-
const diff = buildDiff(config.content, config.target_path);
|
|
11784
|
+
const diff = buildDiff(config.content, config.target_path, config.format, showSecrets);
|
|
11723
11785
|
if (!diff.includes("no diff"))
|
|
11724
11786
|
diffs.push(diff);
|
|
11725
11787
|
}
|
|
11726
11788
|
if (config.outputs.length > 0) {
|
|
11727
11789
|
for (const output of config.outputs) {
|
|
11728
11790
|
const expected = applyTransform(config, output, { configs: contextConfigs });
|
|
11729
|
-
const diff = buildDiff(expected, output.target_path);
|
|
11791
|
+
const diff = buildDiff(expected, output.target_path, detectFormat(output.target_path), showSecrets);
|
|
11730
11792
|
if (!diff.includes("no diff"))
|
|
11731
11793
|
diffs.push(diff);
|
|
11732
11794
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-redaction.test.d.ts","sourceRoot":"","sources":["../../src/lib/diff-redaction.test.ts"],"names":[],"mappings":""}
|
package/dist/lib/redact.d.ts
CHANGED
|
@@ -26,4 +26,13 @@ export declare function redactContent(content: string, format: RedactFormat): Re
|
|
|
26
26
|
export declare function scanSecrets(content: string, format: RedactFormat): RedactedVar[];
|
|
27
27
|
/** Returns true if content contains any detectable secrets. */
|
|
28
28
|
export declare function hasSecrets(content: string, format: RedactFormat): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Is this variable NAME secret-shaped? (`NPM_TOKEN` yes, `PATH`/`HOME` no.)
|
|
31
|
+
*
|
|
32
|
+
* Exported so callers reasoning about placeholder names — notably the diff
|
|
33
|
+
* renderer, which must tell a redaction artefact from an ordinary shell
|
|
34
|
+
* variable — share this module's single definition of "secret-shaped" instead
|
|
35
|
+
* of duplicating the pattern and drifting from it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isSecretVarName(name: string): boolean;
|
|
29
38
|
//# sourceMappingURL=redact.d.ts.map
|
package/dist/lib/redact.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/lib/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAgND,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5F,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,YAAY,CAQjF;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,WAAW,EAAE,CAGhF;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAEzE"}
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/lib/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAgND,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5F,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,YAAY,CAQjF;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,WAAW,EAAE,CAGhF;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAEzE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD"}
|
package/dist/lib/sync.d.ts
CHANGED
|
@@ -43,6 +43,16 @@ export interface SyncToDiskOptions {
|
|
|
43
43
|
export declare function syncToDisk(opts?: SyncToDiskOptions): Promise<SyncResult>;
|
|
44
44
|
export interface DiffConfigOptions {
|
|
45
45
|
store?: ConfigStore;
|
|
46
|
+
/**
|
|
47
|
+
* Render the raw disk content, INCLUDING any credential values it holds.
|
|
48
|
+
*
|
|
49
|
+
* Off by default and deliberately hard to reach: the CLI refuses it on a
|
|
50
|
+
* non-TTY, mirroring `secrets get --show`. Everything a tool prints is
|
|
51
|
+
* persisted verbatim into the session transcript, and agent output is also
|
|
52
|
+
* served as run evidence — so an unredacted diff writes a live credential
|
|
53
|
+
* into a durable, fetchable artefact.
|
|
54
|
+
*/
|
|
55
|
+
showSecrets?: boolean;
|
|
46
56
|
}
|
|
47
57
|
export declare function diffConfig(config: Config, opts?: DiffConfigOptions): Promise<string>;
|
|
48
58
|
export declare function detectCategory(filePath: string): ConfigCategory;
|
package/dist/lib/sync.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/lib/sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrH,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAW/E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,eAAO,MAAM,qBAAqB,EAAE,YAAY,EAQ/C,CAAC;AAwDF,eAAO,MAAM,aAAa,EAAE,WAAW,EAwDtC,CAAC;AAIF,eAAO,MAAM,oBAAoB;;cAC2B,cAAc;WAAsB,WAAW;YAAwB,YAAY;GAc9I,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAgE/E;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAqGhF;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmClF;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/lib/sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrH,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAW/E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,eAAO,MAAM,qBAAqB,EAAE,YAAY,EAQ/C,CAAC;AAwDF,eAAO,MAAM,aAAa,EAAE,WAAW,EAwDtC,CAAC;AAIF,eAAO,MAAM,oBAAoB;;cAC2B,cAAc;WAAsB,WAAW;YAAwB,YAAY;GAc9I,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAgE/E;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAqGhF;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmClF;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA8JD,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CA+B9F;AAGD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAU/D;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAgBzD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAQ3D;AAGD,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -5170,6 +5170,7 @@ var exports_redact = {};
|
|
|
5170
5170
|
__export(exports_redact, {
|
|
5171
5171
|
scanSecrets: () => scanSecrets,
|
|
5172
5172
|
redactContent: () => redactContent,
|
|
5173
|
+
isSecretVarName: () => isSecretVarName,
|
|
5173
5174
|
hasSecrets: () => hasSecrets
|
|
5174
5175
|
});
|
|
5175
5176
|
function redactShell(content) {
|
|
@@ -5343,6 +5344,9 @@ function scanSecrets(content, format) {
|
|
|
5343
5344
|
function hasSecrets(content, format) {
|
|
5344
5345
|
return scanSecrets(content, format).length > 0;
|
|
5345
5346
|
}
|
|
5347
|
+
function isSecretVarName(name) {
|
|
5348
|
+
return SECRET_KEY_PATTERN.test(name);
|
|
5349
|
+
}
|
|
5346
5350
|
var SECRET_KEY_PATTERN, VALUE_PATTERNS, MIN_SECRET_VALUE_LEN = 8;
|
|
5347
5351
|
var init_redact = __esm(() => {
|
|
5348
5352
|
SECRET_KEY_PATTERN = /^(.*_?API_?KEY|.*_?TOKEN|.*_?SECRET|.*_?PASSWORD|.*_?PASSWD|.*_?CREDENTIAL|.*_?AUTH(?:_TOKEN|_KEY|ORIZATION)?|.*_?PRIVATE_?KEY|.*_?ACCESS_?KEY|.*_?CLIENT_?SECRET|.*_?SIGNING_?KEY|.*_?ENCRYPTION_?KEY|.*_AUTH_TOKEN)$/i;
|
|
@@ -6515,30 +6519,86 @@ async function syncToDisk(opts = {}) {
|
|
|
6515
6519
|
}
|
|
6516
6520
|
return result;
|
|
6517
6521
|
}
|
|
6518
|
-
function
|
|
6522
|
+
function redactionPlaceholders(storedLine) {
|
|
6523
|
+
const found = [...storedLine.match(CERTAIN_PLACEHOLDER_RE) ?? []];
|
|
6524
|
+
const assigned = storedLine.match(ASSIGNED_PLACEHOLDER_RE);
|
|
6525
|
+
if (assigned) {
|
|
6526
|
+
const name = assigned[3] ?? assigned[4] ?? assigned[5];
|
|
6527
|
+
if (name && isSecretVarName(name))
|
|
6528
|
+
found.push(assigned[2]);
|
|
6529
|
+
}
|
|
6530
|
+
return found;
|
|
6531
|
+
}
|
|
6532
|
+
function redactFormatForTarget(targetPath, storedFormat) {
|
|
6533
|
+
const p = targetPath.toLowerCase();
|
|
6534
|
+
if (/(^|\/)\.(zshrc|zprofile|bashrc|bash_profile|profile|zshenv|env)$/.test(p) || p.endsWith(".env"))
|
|
6535
|
+
return "shell";
|
|
6536
|
+
if (/(^|\/)\.(npmrc|yarnrc|curlrc|netrc)$/.test(p))
|
|
6537
|
+
return "ini";
|
|
6538
|
+
return storedFormat;
|
|
6539
|
+
}
|
|
6540
|
+
function storedPlaceholderIsLiteralOnDisk(storedLine, diskLine) {
|
|
6541
|
+
return redactionPlaceholders(storedLine).some((p) => !diskLine.includes(p));
|
|
6542
|
+
}
|
|
6543
|
+
function buildDiff(expectedContent, targetPath, storedFormat, showSecrets) {
|
|
6519
6544
|
const path = expandPath(targetPath);
|
|
6520
6545
|
if (!existsSync5(path))
|
|
6521
6546
|
return `(file not found on disk: ${path})`;
|
|
6522
6547
|
const diskContent = readFileSync3(path, "utf-8");
|
|
6523
6548
|
if (diskContent === expectedContent)
|
|
6524
6549
|
return "(no diff \u2014 identical)";
|
|
6550
|
+
const format = redactFormatForTarget(targetPath, storedFormat);
|
|
6525
6551
|
const stored = expectedContent.split(`
|
|
6526
6552
|
`);
|
|
6527
6553
|
const disk = diskContent.split(`
|
|
6528
6554
|
`);
|
|
6555
|
+
const { content: redactedDiskContent, redacted } = showSecrets ? { content: diskContent, redacted: [] } : redactContent(diskContent, format);
|
|
6556
|
+
const redactedDisk = redactedDiskContent.split(`
|
|
6557
|
+
`);
|
|
6558
|
+
const reasonByLine = new Map;
|
|
6559
|
+
for (const r of redacted)
|
|
6560
|
+
if (!reasonByLine.has(r.line))
|
|
6561
|
+
reasonByLine.set(r.line, r.reason);
|
|
6529
6562
|
const lines = [`--- stored (DB)`, `+++ disk (${path})`];
|
|
6530
6563
|
const maxLen = Math.max(stored.length, disk.length);
|
|
6531
6564
|
for (let i = 0;i < maxLen; i++) {
|
|
6565
|
+
const lineNo = i + 1;
|
|
6532
6566
|
const s = stored[i];
|
|
6533
6567
|
const dk = disk[i];
|
|
6534
6568
|
if (s === dk) {
|
|
6535
6569
|
if (s !== undefined)
|
|
6536
6570
|
lines.push(` ${s}`);
|
|
6537
|
-
|
|
6571
|
+
continue;
|
|
6572
|
+
}
|
|
6573
|
+
if (showSecrets || dk === undefined) {
|
|
6538
6574
|
if (s !== undefined)
|
|
6539
6575
|
lines.push(`-${s}`);
|
|
6540
6576
|
if (dk !== undefined)
|
|
6541
6577
|
lines.push(`+${dk}`);
|
|
6578
|
+
continue;
|
|
6579
|
+
}
|
|
6580
|
+
const structural = s !== undefined && storedPlaceholderIsLiteralOnDisk(s, dk);
|
|
6581
|
+
const patterned = reasonByLine.has(lineNo);
|
|
6582
|
+
if (!structural && !patterned) {
|
|
6583
|
+
if (s !== undefined)
|
|
6584
|
+
lines.push(`-${s}`);
|
|
6585
|
+
lines.push(`+${dk}`);
|
|
6586
|
+
continue;
|
|
6587
|
+
}
|
|
6588
|
+
if (structural) {
|
|
6589
|
+
const placeholder = redactionPlaceholders(s).find((p) => !dk.includes(p)) ?? "a placeholder";
|
|
6590
|
+
lines.push(`~line ${lineNo} differs: stored \`${s}\`, disk holds a literal value for ${placeholder} \u2014 redacted (use --show-secrets on a TTY to reveal)`);
|
|
6591
|
+
continue;
|
|
6592
|
+
}
|
|
6593
|
+
const safe = redactedDisk[i] ?? "";
|
|
6594
|
+
const reason = reasonByLine.get(lineNo);
|
|
6595
|
+
if (s !== undefined && safe === s) {
|
|
6596
|
+
lines.push(`~line ${lineNo} differs: stored \`${s}\`, disk holds a literal value (${reason}) \u2014 redacted (use --show-secrets on a TTY to reveal)`);
|
|
6597
|
+
} else {
|
|
6598
|
+
if (s !== undefined)
|
|
6599
|
+
lines.push(`-${s}`);
|
|
6600
|
+
lines.push(`+${safe}`);
|
|
6601
|
+
lines.push(`~line ${lineNo}: disk value redacted (${reason}) \u2014 use --show-secrets on a TTY to reveal`);
|
|
6542
6602
|
}
|
|
6543
6603
|
}
|
|
6544
6604
|
return lines.join(`
|
|
@@ -6553,15 +6613,16 @@ async function diffConfig(config, opts = {}) {
|
|
|
6553
6613
|
if (isGeneratedOutputTarget2(config, outputOwnerIdsByTarget(contextConfigs))) {
|
|
6554
6614
|
return "(generated output \u2014 managed by fan-out)";
|
|
6555
6615
|
}
|
|
6616
|
+
const showSecrets = opts.showSecrets ?? false;
|
|
6556
6617
|
if (config.target_path) {
|
|
6557
|
-
const diff = buildDiff(config.content, config.target_path);
|
|
6618
|
+
const diff = buildDiff(config.content, config.target_path, config.format, showSecrets);
|
|
6558
6619
|
if (!diff.includes("no diff"))
|
|
6559
6620
|
diffs.push(diff);
|
|
6560
6621
|
}
|
|
6561
6622
|
if (config.outputs.length > 0) {
|
|
6562
6623
|
for (const output of config.outputs) {
|
|
6563
6624
|
const expected = applyTransform(config, output, { configs: contextConfigs });
|
|
6564
|
-
const diff = buildDiff(expected, output.target_path);
|
|
6625
|
+
const diff = buildDiff(expected, output.target_path, detectFormat(output.target_path), showSecrets);
|
|
6565
6626
|
if (!diff.includes("no diff"))
|
|
6566
6627
|
diffs.push(diff);
|
|
6567
6628
|
}
|
|
@@ -6632,7 +6693,7 @@ function detectFormat(filePath) {
|
|
|
6632
6693
|
return "ini";
|
|
6633
6694
|
return "text";
|
|
6634
6695
|
}
|
|
6635
|
-
var CLAUDE_PROMPT_OUTPUTS, KNOWN_CONFIGS, PROJECT_CONFIG_FILES;
|
|
6696
|
+
var CLAUDE_PROMPT_OUTPUTS, KNOWN_CONFIGS, PROJECT_CONFIG_FILES, CERTAIN_PLACEHOLDER_RE, ASSIGNED_PLACEHOLDER_RE;
|
|
6636
6697
|
var init_sync = __esm(() => {
|
|
6637
6698
|
init_config_store();
|
|
6638
6699
|
init_apply();
|
|
@@ -6696,6 +6757,8 @@ var init_sync = __esm(() => {
|
|
|
6696
6757
|
{ file: ".agents/mcp_config.json", category: "mcp", agent: "antigravity", format: "json" },
|
|
6697
6758
|
{ file: ".qwen/settings.json", category: "agent", agent: "qwen", format: "json" }
|
|
6698
6759
|
];
|
|
6760
|
+
CERTAIN_PLACEHOLDER_RE = /\{\{[A-Za-z_][A-Za-z0-9_]*\}\}/g;
|
|
6761
|
+
ASSIGNED_PLACEHOLDER_RE = /[=:]\s*(["']?)(\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Z][A-Z0-9_]*)|%([A-Za-z_][A-Za-z0-9_]*)%)\1\s*,?\s*$/;
|
|
6699
6762
|
});
|
|
6700
6763
|
|
|
6701
6764
|
// src/lib/sync-dir.ts
|
|
@@ -6792,7 +6855,7 @@ var init_sync_dir = __esm(() => {
|
|
|
6792
6855
|
var require_package = __commonJS((exports, module) => {
|
|
6793
6856
|
module.exports = {
|
|
6794
6857
|
name: "@hasna/instructions",
|
|
6795
|
-
version: "0.4.
|
|
6858
|
+
version: "0.4.12",
|
|
6796
6859
|
description: "AI coding agent instruction & configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
|
|
6797
6860
|
type: "module",
|
|
6798
6861
|
main: "dist/index.js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/instructions",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.12",
|
|
4
4
|
"description": "AI coding agent instruction & configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|