ghostrail 0.3.0 → 0.5.0
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/README.md +20 -1
- package/dist/cli/commands.d.ts +18 -3
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +189 -7
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/help.d.ts.map +1 -1
- package/dist/commands/help.js +10 -0
- package/dist/commands/help.js.map +1 -1
- package/dist/factory/build.d.ts +2 -7
- package/dist/factory/build.d.ts.map +1 -1
- package/dist/factory/build.js +2 -16
- package/dist/factory/build.js.map +1 -1
- package/dist/init/template.d.ts +40 -0
- package/dist/init/template.d.ts.map +1 -0
- package/dist/init/template.js +89 -0
- package/dist/init/template.js.map +1 -0
- package/dist/secrets/editor.d.ts +16 -0
- package/dist/secrets/editor.d.ts.map +1 -0
- package/dist/secrets/editor.js +35 -0
- package/dist/secrets/editor.js.map +1 -0
- package/dist/secrets/env-file.d.ts +0 -13
- package/dist/secrets/env-file.d.ts.map +1 -1
- package/dist/secrets/env-file.js +6 -2
- package/dist/secrets/env-file.js.map +1 -1
- package/dist/secrets/index.d.ts +3 -0
- package/dist/secrets/index.d.ts.map +1 -1
- package/dist/secrets/index.js +3 -0
- package/dist/secrets/index.js.map +1 -1
- package/dist/secrets/known.d.ts +19 -0
- package/dist/secrets/known.d.ts.map +1 -0
- package/dist/secrets/known.js +23 -0
- package/dist/secrets/known.js.map +1 -0
- package/dist/secrets/setup.d.ts +44 -0
- package/dist/secrets/setup.d.ts.map +1 -0
- package/dist/secrets/setup.js +124 -0
- package/dist/secrets/setup.js.map +1 -0
- package/package.json +1 -1
- package/skill/ghostrail/SKILL.md +29 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/factory/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI9C,OAAO,EAAE,MAAM,EAAiB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGnD,sDAAsD;AACtD,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;AAEpC
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/factory/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** The provenance manifest `init` writes at the repo root. Committed with the repo. */
|
|
2
|
+
export declare const MANIFEST_FILE = "ghostrail.template.json";
|
|
3
|
+
/**
|
|
4
|
+
* What `init` wrote, and from which template and version. `files` maps a
|
|
5
|
+
* repo-relative path to the sha256 of the content as scaffolded, which is the
|
|
6
|
+
* baseline a later `--diff` compares against to tell a local edit apart from an
|
|
7
|
+
* upstream template change.
|
|
8
|
+
*/
|
|
9
|
+
export interface TemplateManifest {
|
|
10
|
+
template: string;
|
|
11
|
+
version: string;
|
|
12
|
+
files: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* How a scaffolded file stands relative to the shipped template. See
|
|
16
|
+
* `specs/template-provenance.spec.md` for the decision table.
|
|
17
|
+
*/
|
|
18
|
+
export type FileStatus = "missing" | "up-to-date" | "unknown-baseline" | "upstream-changed" | "locally-modified" | "conflict";
|
|
19
|
+
/** sha256 of `content` as lowercase hex. No normalization: bytes are bytes. */
|
|
20
|
+
export declare function hashContent(content: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Classify one file from three content hashes. Total and mutually exclusive:
|
|
23
|
+
* every triple yields exactly one status. Never throws, never touches the disk.
|
|
24
|
+
*
|
|
25
|
+
* The baseline is what makes this more than a diff. Without it, "you edited
|
|
26
|
+
* this" and "the template moved" are indistinguishable, and reporting the wrong
|
|
27
|
+
* one would either hide an upstream fix or invite clobbering a customization.
|
|
28
|
+
*/
|
|
29
|
+
export declare function classify(current: string | undefined, template: string, baseline: string | undefined): FileStatus;
|
|
30
|
+
/**
|
|
31
|
+
* The manifest to persist after an init run: existing baselines preserved, the
|
|
32
|
+
* files this run wrote layered on top, retargeted to the current template and
|
|
33
|
+
* version. Pure; neither argument is mutated.
|
|
34
|
+
*/
|
|
35
|
+
export declare function mergeManifest(existing: TemplateManifest | undefined, template: string, version: string, written: Record<string, string>): TemplateManifest;
|
|
36
|
+
/** Read the manifest, or undefined if it is absent, unreadable, or malformed. */
|
|
37
|
+
export declare function readManifest(repoPath: string): Promise<TemplateManifest | undefined>;
|
|
38
|
+
/** Write the manifest as pretty JSON with a trailing newline. */
|
|
39
|
+
export declare function writeManifest(repoPath: string, manifest: TemplateManifest): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/init/template.ts"],"names":[],"mappings":"AAIA,uFAAuF;AACvF,eAAO,MAAM,aAAa,4BAA4B,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,YAAY,GACZ,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,GAClB,UAAU,CAAC;AAEf,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,UAAU,CAOZ;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,gBAAgB,CAMlB;AAyBD,iFAAiF;AACjF,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAY1F;AAED,iEAAiE;AACjE,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/F"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
/** The provenance manifest `init` writes at the repo root. Committed with the repo. */
|
|
5
|
+
export const MANIFEST_FILE = "ghostrail.template.json";
|
|
6
|
+
/** sha256 of `content` as lowercase hex. No normalization: bytes are bytes. */
|
|
7
|
+
export function hashContent(content) {
|
|
8
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Classify one file from three content hashes. Total and mutually exclusive:
|
|
12
|
+
* every triple yields exactly one status. Never throws, never touches the disk.
|
|
13
|
+
*
|
|
14
|
+
* The baseline is what makes this more than a diff. Without it, "you edited
|
|
15
|
+
* this" and "the template moved" are indistinguishable, and reporting the wrong
|
|
16
|
+
* one would either hide an upstream fix or invite clobbering a customization.
|
|
17
|
+
*/
|
|
18
|
+
export function classify(current, template, baseline) {
|
|
19
|
+
if (current === undefined)
|
|
20
|
+
return "missing";
|
|
21
|
+
if (current === template)
|
|
22
|
+
return "up-to-date";
|
|
23
|
+
if (baseline === undefined)
|
|
24
|
+
return "unknown-baseline";
|
|
25
|
+
if (baseline === current)
|
|
26
|
+
return "upstream-changed";
|
|
27
|
+
if (baseline === template)
|
|
28
|
+
return "locally-modified";
|
|
29
|
+
return "conflict";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The manifest to persist after an init run: existing baselines preserved, the
|
|
33
|
+
* files this run wrote layered on top, retargeted to the current template and
|
|
34
|
+
* version. Pure; neither argument is mutated.
|
|
35
|
+
*/
|
|
36
|
+
export function mergeManifest(existing, template, version, written) {
|
|
37
|
+
return {
|
|
38
|
+
template,
|
|
39
|
+
version,
|
|
40
|
+
files: { ...(existing?.files ?? {}), ...written },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** True if `value` is a plain object (not null, not an array). */
|
|
44
|
+
function isRecord(value) {
|
|
45
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Validate a parsed manifest. Anything malformed is rejected outright rather
|
|
49
|
+
* than partially trusted: a bad manifest must degrade to "baseline unknown",
|
|
50
|
+
* never to a false claim that a file is unmodified.
|
|
51
|
+
*/
|
|
52
|
+
function parseManifest(raw) {
|
|
53
|
+
if (!isRecord(raw))
|
|
54
|
+
return undefined;
|
|
55
|
+
const { template, version, files } = raw;
|
|
56
|
+
if (typeof template !== "string" || typeof version !== "string")
|
|
57
|
+
return undefined;
|
|
58
|
+
if (!isRecord(files))
|
|
59
|
+
return undefined;
|
|
60
|
+
const entries = {};
|
|
61
|
+
for (const [key, value] of Object.entries(files)) {
|
|
62
|
+
if (typeof value !== "string")
|
|
63
|
+
return undefined;
|
|
64
|
+
entries[key] = value;
|
|
65
|
+
}
|
|
66
|
+
return { template, version, files: entries };
|
|
67
|
+
}
|
|
68
|
+
/** Read the manifest, or undefined if it is absent, unreadable, or malformed. */
|
|
69
|
+
export async function readManifest(repoPath) {
|
|
70
|
+
let raw;
|
|
71
|
+
try {
|
|
72
|
+
raw = await readFile(join(repoPath, MANIFEST_FILE), "utf8");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
return parseManifest(JSON.parse(raw));
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Write the manifest as pretty JSON with a trailing newline. */
|
|
85
|
+
export async function writeManifest(repoPath, manifest) {
|
|
86
|
+
const body = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
87
|
+
await writeFile(join(repoPath, MANIFEST_FILE), body, "utf8");
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/init/template.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor command to launch, as an argv array, from `$VISUAL` then
|
|
3
|
+
* `$EDITOR`. The value is split on whitespace so `code -w` works. A variable
|
|
4
|
+
* that is unset, empty, or only whitespace is skipped rather than chosen, so an
|
|
5
|
+
* empty `VISUAL` still falls through to `EDITOR`. Undefined when neither yields
|
|
6
|
+
* a command: the caller prints the path instead of guessing at an editor.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveEditor(env: Record<string, string | undefined>): string[] | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Run the editor on `path`, inheriting the terminal so a full-screen editor
|
|
11
|
+
* works, and resolve its exit code. Never rejects: a spawn failure resolves
|
|
12
|
+
* non-zero, because the file is already written by the time this runs and a
|
|
13
|
+
* failing editor is not a failing setup.
|
|
14
|
+
*/
|
|
15
|
+
export declare function launchEditor(command: readonly string[], path: string): Promise<number>;
|
|
16
|
+
//# sourceMappingURL=editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/secrets/editor.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,EAAE,GAAG,SAAS,CAM3F;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQtF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
/** The env vars that name an editor, in preference order. */
|
|
3
|
+
const EDITOR_VARS = ["VISUAL", "EDITOR"];
|
|
4
|
+
/**
|
|
5
|
+
* The editor command to launch, as an argv array, from `$VISUAL` then
|
|
6
|
+
* `$EDITOR`. The value is split on whitespace so `code -w` works. A variable
|
|
7
|
+
* that is unset, empty, or only whitespace is skipped rather than chosen, so an
|
|
8
|
+
* empty `VISUAL` still falls through to `EDITOR`. Undefined when neither yields
|
|
9
|
+
* a command: the caller prints the path instead of guessing at an editor.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveEditor(env) {
|
|
12
|
+
for (const name of EDITOR_VARS) {
|
|
13
|
+
const parts = (env[name] ?? "").split(/\s+/).filter((part) => part.length > 0);
|
|
14
|
+
if (parts.length > 0)
|
|
15
|
+
return parts;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Run the editor on `path`, inheriting the terminal so a full-screen editor
|
|
21
|
+
* works, and resolve its exit code. Never rejects: a spawn failure resolves
|
|
22
|
+
* non-zero, because the file is already written by the time this runs and a
|
|
23
|
+
* failing editor is not a failing setup.
|
|
24
|
+
*/
|
|
25
|
+
export function launchEditor(command, path) {
|
|
26
|
+
const [file, ...args] = command;
|
|
27
|
+
if (file === undefined)
|
|
28
|
+
return Promise.resolve(1);
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const child = spawn(file, [...args, path], { stdio: "inherit" });
|
|
31
|
+
child.on("error", () => resolve(1));
|
|
32
|
+
child.on("close", (code) => resolve(code ?? 1));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/secrets/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,6DAA6D;AAC7D,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,GAAuC;IACnE,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC/E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IACrC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,OAA0B,EAAE,IAAY;IACnE,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC;IAChC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QACjE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parse a dotenv-style file into key/value pairs.
|
|
3
|
-
*
|
|
4
|
-
* - One `KEY=VALUE` per line; an optional leading `export ` is stripped.
|
|
5
|
-
* - Blank lines and lines whose first non-space character is `#` are ignored.
|
|
6
|
-
* - The key is everything before the first `=`, trimmed. A line with no `=`, or
|
|
7
|
-
* an empty key, is skipped.
|
|
8
|
-
* - The value is everything after the first `=`, trimmed; if it is wholly wrapped
|
|
9
|
-
* in matching single or double quotes, the quotes are removed (and the inner
|
|
10
|
-
* text is not further trimmed). `=` characters in the value are preserved.
|
|
11
|
-
*
|
|
12
|
-
* Pure: no I/O, never throws for any string.
|
|
13
|
-
*/
|
|
14
1
|
export declare function parseEnvFile(content: string): Record<string, string>;
|
|
15
2
|
/** True if a variable name denotes a secret (by known name or suffix). */
|
|
16
3
|
export declare function isSecretKey(key: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-file.d.ts","sourceRoot":"","sources":["../../src/secrets/env-file.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"env-file.d.ts","sourceRoot":"","sources":["../../src/secrets/env-file.ts"],"names":[],"mappings":"AAkBA,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBpE;AAwBD,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,EAAE,CAUhF"}
|
package/dist/secrets/env-file.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Parse a dotenv-style file into key/value pairs.
|
|
3
3
|
*
|
|
4
|
-
* - One `KEY=VALUE` per line;
|
|
4
|
+
* - One `KEY=VALUE` per line; a leading `export` and the whitespace after it are
|
|
5
|
+
* stripped. Any whitespace separates them, as in a shell, so `export\tKEY=v`
|
|
6
|
+
* yields the key `KEY` rather than one literally named `export\tKEY`.
|
|
5
7
|
* - Blank lines and lines whose first non-space character is `#` are ignored.
|
|
6
8
|
* - The key is everything before the first `=`, trimmed. A line with no `=`, or
|
|
7
9
|
* an empty key, is skipped.
|
|
@@ -11,13 +13,15 @@
|
|
|
11
13
|
*
|
|
12
14
|
* Pure: no I/O, never throws for any string.
|
|
13
15
|
*/
|
|
16
|
+
/** A leading shell `export` and whatever whitespace separates it from the key. */
|
|
17
|
+
const EXPORT_PREFIX = /^export\s+/;
|
|
14
18
|
export function parseEnvFile(content) {
|
|
15
19
|
const result = {};
|
|
16
20
|
for (const rawLine of content.split("\n")) {
|
|
17
21
|
const line = rawLine.trim();
|
|
18
22
|
if (line.length === 0 || line.startsWith("#"))
|
|
19
23
|
continue;
|
|
20
|
-
const withoutExport = line.
|
|
24
|
+
const withoutExport = line.replace(EXPORT_PREFIX, "");
|
|
21
25
|
const eq = withoutExport.indexOf("=");
|
|
22
26
|
if (eq === -1)
|
|
23
27
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-file.js","sourceRoot":"","sources":["../../src/secrets/env-file.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"env-file.js","sourceRoot":"","sources":["../../src/secrets/env-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,kFAAkF;AAClF,MAAM,aAAa,GAAG,YAAY,CAAC;AAEnC,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAExD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAS;QAExB,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAE/B,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,OAAO,CAAC,KAAa;IAC5B,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACvD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,uFAAuF;AACvF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,gBAAgB;IAChB,mBAAmB;IACnB,UAAU;IACV,cAAc;IACd,WAAW;CACZ,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,mDAAmD,CAAC;AAE/E,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAuC;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACrF,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/secrets/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
export { launchEditor, resolveEditor } from "./editor.js";
|
|
1
2
|
export { isSecretKey, parseEnvFile, secretValuesOf } from "./env-file.js";
|
|
3
|
+
export { type CredentialKey, DEFAULT_FORWARD_ENV } from "./known.js";
|
|
4
|
+
export { appendPlaceholders, KNOWN_SECRETS, mentionedKeys, missingSecrets, readSecretsFile, renderFile, SECRETS_DIR_MODE, SECRETS_FILE_MODE, type SecretSpec, writeSecretsFile, } from "./setup.js";
|
|
2
5
|
export { applyOverrides, GLOBAL_SECRETS_FILE, type LoadedEnv, loadEnvOverrides, REPO_SECRETS_FILE, } from "./load.js";
|
|
3
6
|
export { REDACTION, redactSecrets } from "./redact.js";
|
|
4
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/secrets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,KAAK,SAAS,EACd,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/secrets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,KAAK,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,UAAU,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,KAAK,SAAS,EACd,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/secrets/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
export { launchEditor, resolveEditor } from "./editor.js";
|
|
1
2
|
export { isSecretKey, parseEnvFile, secretValuesOf } from "./env-file.js";
|
|
3
|
+
export { DEFAULT_FORWARD_ENV } from "./known.js";
|
|
4
|
+
export { appendPlaceholders, KNOWN_SECRETS, mentionedKeys, missingSecrets, readSecretsFile, renderFile, SECRETS_DIR_MODE, SECRETS_FILE_MODE, writeSecretsFile, } from "./setup.js";
|
|
2
5
|
export { applyOverrides, GLOBAL_SECRETS_FILE, loadEnvOverrides, REPO_SECRETS_FILE, } from "./load.js";
|
|
3
6
|
export { REDACTION, redactSecrets } from "./redact.js";
|
|
4
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/secrets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EACL,cAAc,EACd,mBAAmB,EAEnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/secrets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAsB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EAEjB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,cAAc,EACd,mBAAmB,EAEnB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host env names forwarded into every container `docker exec` by default, so the
|
|
3
|
+
* agent and gate have the credentials they need. Only names are configured;
|
|
4
|
+
* values come from the process environment (after the secrets loader has applied
|
|
5
|
+
* them), and unset names are simply skipped. The container inherits nothing else.
|
|
6
|
+
*
|
|
7
|
+
* This is also the canonical list of credentials ghostrail reads, so
|
|
8
|
+
* `secrets-setup` scaffolds a placeholder for exactly these names. It lives in a
|
|
9
|
+
* leaf module so both consumers can share it without dragging in the factory's
|
|
10
|
+
* dependency graph.
|
|
11
|
+
*
|
|
12
|
+
* Agent auth covers both rails: ANTHROPIC_API_KEY bills metered API and wins if
|
|
13
|
+
* both are set; CLAUDE_CODE_OAUTH_TOKEN (from `claude setup-token`) bills the
|
|
14
|
+
* Pro/Max subscription when the API key is unset.
|
|
15
|
+
*/
|
|
16
|
+
export declare const DEFAULT_FORWARD_ENV: readonly ["ANTHROPIC_API_KEY", "CLAUDE_CODE_OAUTH_TOKEN", "GH_TOKEN", "GITHUB_TOKEN", "LINEAR_API_KEY"];
|
|
17
|
+
/** A credential name ghostrail reads. */
|
|
18
|
+
export type CredentialKey = (typeof DEFAULT_FORWARD_ENV)[number];
|
|
19
|
+
//# sourceMappingURL=known.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"known.d.ts","sourceRoot":"","sources":["../../src/secrets/known.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,yGAMtB,CAAC;AAEX,yCAAyC;AACzC,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host env names forwarded into every container `docker exec` by default, so the
|
|
3
|
+
* agent and gate have the credentials they need. Only names are configured;
|
|
4
|
+
* values come from the process environment (after the secrets loader has applied
|
|
5
|
+
* them), and unset names are simply skipped. The container inherits nothing else.
|
|
6
|
+
*
|
|
7
|
+
* This is also the canonical list of credentials ghostrail reads, so
|
|
8
|
+
* `secrets-setup` scaffolds a placeholder for exactly these names. It lives in a
|
|
9
|
+
* leaf module so both consumers can share it without dragging in the factory's
|
|
10
|
+
* dependency graph.
|
|
11
|
+
*
|
|
12
|
+
* Agent auth covers both rails: ANTHROPIC_API_KEY bills metered API and wins if
|
|
13
|
+
* both are set; CLAUDE_CODE_OAUTH_TOKEN (from `claude setup-token`) bills the
|
|
14
|
+
* Pro/Max subscription when the API key is unset.
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_FORWARD_ENV = [
|
|
17
|
+
"ANTHROPIC_API_KEY",
|
|
18
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
19
|
+
"GH_TOKEN",
|
|
20
|
+
"GITHUB_TOKEN",
|
|
21
|
+
"LINEAR_API_KEY",
|
|
22
|
+
];
|
|
23
|
+
//# sourceMappingURL=known.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"known.js","sourceRoot":"","sources":["../../src/secrets/known.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,mBAAmB;IACnB,yBAAyB;IACzB,UAAU;IACV,cAAc;IACd,gBAAgB;CACR,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** One credential the secrets file can carry. */
|
|
2
|
+
export interface SecretSpec {
|
|
3
|
+
/** The variable name. */
|
|
4
|
+
key: string;
|
|
5
|
+
/** One line: what it is and where to get it. */
|
|
6
|
+
description: string;
|
|
7
|
+
}
|
|
8
|
+
/** The secrets file holds credentials, so it is created owner-only. */
|
|
9
|
+
export declare const SECRETS_FILE_MODE = 384;
|
|
10
|
+
/** The global config dir, created owner-only for the same reason. */
|
|
11
|
+
export declare const SECRETS_DIR_MODE = 448;
|
|
12
|
+
/** Every credential ghostrail reads, in forwarding order. */
|
|
13
|
+
export declare const KNOWN_SECRETS: readonly SecretSpec[];
|
|
14
|
+
/**
|
|
15
|
+
* The variable names the content refers to, whether as a live assignment or as a
|
|
16
|
+
* commented placeholder. Both count, so topping a file up never re-adds a
|
|
17
|
+
* placeholder the file already carries.
|
|
18
|
+
*
|
|
19
|
+
* Only names are returned. No value is read, returned, or surfaced.
|
|
20
|
+
*/
|
|
21
|
+
export declare function mentionedKeys(content: string): Set<string>;
|
|
22
|
+
/**
|
|
23
|
+
* The known credentials the content does not mention, in `KNOWN_SECRETS` order.
|
|
24
|
+
* `undefined` content (no file yet) means every one of them is missing. Names
|
|
25
|
+
* the file carries that ghostrail does not read are ignored.
|
|
26
|
+
*/
|
|
27
|
+
export declare function missingSecrets(content: string | undefined): SecretSpec[];
|
|
28
|
+
/**
|
|
29
|
+
* The full content of a fresh secrets file: a header, then a commented
|
|
30
|
+
* placeholder per spec. Every line is a comment, so creating the file never
|
|
31
|
+
* changes what ghostrail resolves.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderFile(specs: readonly SecretSpec[]): string;
|
|
34
|
+
/**
|
|
35
|
+
* Append placeholder blocks for `specs` to existing content, preserving every
|
|
36
|
+
* byte of it up to its trailing newlines. Only the run of trailing newlines can
|
|
37
|
+
* change, which keeps repeated runs from stacking blank lines.
|
|
38
|
+
*/
|
|
39
|
+
export declare function appendPlaceholders(existing: string, specs: readonly SecretSpec[]): string;
|
|
40
|
+
/** The secrets file's content, or undefined when it does not exist. */
|
|
41
|
+
export declare function readSecretsFile(path: string): Promise<string | undefined>;
|
|
42
|
+
/** Write the secrets file owner-only, creating its directory owner-only too. */
|
|
43
|
+
export declare function writeSecretsFile(path: string, content: string): Promise<void>;
|
|
44
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/secrets/setup.ts"],"names":[],"mappings":"AAKA,iDAAiD;AACjD,MAAM,WAAW,UAAU;IACzB,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,uEAAuE;AACvE,eAAO,MAAM,iBAAiB,MAAQ,CAAC;AACvC,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,MAAQ,CAAC;AAkBtC,6DAA6D;AAC7D,eAAO,MAAM,aAAa,EAAE,SAAS,UAAU,EAG5C,CAAC;AASJ;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAU1D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,EAAE,CAIxE;AAyBD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,CAI/D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,CAOzF;AAED,uEAAuE;AACvE,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAM/E;AAED,gFAAgF;AAChF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGnF"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { parseEnvFile } from "./env-file.js";
|
|
4
|
+
import { DEFAULT_FORWARD_ENV } from "./known.js";
|
|
5
|
+
/** The secrets file holds credentials, so it is created owner-only. */
|
|
6
|
+
export const SECRETS_FILE_MODE = 0o600;
|
|
7
|
+
/** The global config dir, created owner-only for the same reason. */
|
|
8
|
+
export const SECRETS_DIR_MODE = 0o700;
|
|
9
|
+
/**
|
|
10
|
+
* What each credential is for. Keyed by {@link CredentialKey}, so adding a name
|
|
11
|
+
* to `DEFAULT_FORWARD_ENV` without describing it here is a type error rather
|
|
12
|
+
* than a key silently missing from the scaffolded file.
|
|
13
|
+
*/
|
|
14
|
+
const DESCRIPTIONS = {
|
|
15
|
+
ANTHROPIC_API_KEY: "Anthropic API key. Bills metered API usage, and wins over CLAUDE_CODE_OAUTH_TOKEN when both are set.",
|
|
16
|
+
CLAUDE_CODE_OAUTH_TOKEN: "From `claude setup-token`. Bills a Claude Pro/Max subscription; leave ANTHROPIC_API_KEY unset to use it.",
|
|
17
|
+
GH_TOKEN: "GitHub token with repo + pull-request write. Used to push factory branches and open PRs.",
|
|
18
|
+
GITHUB_TOKEN: "Alternative name for the GitHub token, read when GH_TOKEN is unset.",
|
|
19
|
+
LINEAR_API_KEY: "Linear personal API key (Settings > Security & Access). Required for every run.",
|
|
20
|
+
};
|
|
21
|
+
/** Every credential ghostrail reads, in forwarding order. */
|
|
22
|
+
export const KNOWN_SECRETS = DEFAULT_FORWARD_ENV.map((key) => ({
|
|
23
|
+
key,
|
|
24
|
+
description: DESCRIPTIONS[key],
|
|
25
|
+
}));
|
|
26
|
+
/**
|
|
27
|
+
* A commented placeholder line: a comment whose first content is `KEY=`, with an
|
|
28
|
+
* optional `export`. Requiring the `=` is what separates a placeholder from
|
|
29
|
+
* prose that merely names a variable.
|
|
30
|
+
*/
|
|
31
|
+
const COMMENTED_ASSIGNMENT = /^#+\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/;
|
|
32
|
+
/**
|
|
33
|
+
* The variable names the content refers to, whether as a live assignment or as a
|
|
34
|
+
* commented placeholder. Both count, so topping a file up never re-adds a
|
|
35
|
+
* placeholder the file already carries.
|
|
36
|
+
*
|
|
37
|
+
* Only names are returned. No value is read, returned, or surfaced.
|
|
38
|
+
*/
|
|
39
|
+
export function mentionedKeys(content) {
|
|
40
|
+
const keys = new Set(Object.keys(parseEnvFile(content)));
|
|
41
|
+
for (const rawLine of content.split("\n")) {
|
|
42
|
+
const line = rawLine.trim();
|
|
43
|
+
if (!line.startsWith("#"))
|
|
44
|
+
continue;
|
|
45
|
+
const match = COMMENTED_ASSIGNMENT.exec(line);
|
|
46
|
+
const key = match?.[1];
|
|
47
|
+
if (key !== undefined)
|
|
48
|
+
keys.add(key);
|
|
49
|
+
}
|
|
50
|
+
return keys;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The known credentials the content does not mention, in `KNOWN_SECRETS` order.
|
|
54
|
+
* `undefined` content (no file yet) means every one of them is missing. Names
|
|
55
|
+
* the file carries that ghostrail does not read are ignored.
|
|
56
|
+
*/
|
|
57
|
+
export function missingSecrets(content) {
|
|
58
|
+
if (content === undefined)
|
|
59
|
+
return [...KNOWN_SECRETS];
|
|
60
|
+
const mentioned = mentionedKeys(content);
|
|
61
|
+
return KNOWN_SECRETS.filter((spec) => !mentioned.has(spec.key));
|
|
62
|
+
}
|
|
63
|
+
const HEADER = [
|
|
64
|
+
"# ghostrail credentials.",
|
|
65
|
+
"#",
|
|
66
|
+
"# ghostrail reads this file on every run, watch, respond, and triage. A value",
|
|
67
|
+
"# set here overrides the same variable in your environment (precedence: a",
|
|
68
|
+
"# repo's .ghostrail.env, then this file, then the ambient environment).",
|
|
69
|
+
"#",
|
|
70
|
+
"# Uncomment a line and fill in the value. Leave the rest commented: an",
|
|
71
|
+
"# uncommented empty value would blank out a working credential from your",
|
|
72
|
+
"# environment. This file is created mode 600 and belongs to you alone.",
|
|
73
|
+
];
|
|
74
|
+
/**
|
|
75
|
+
* The description + commented placeholder block for one credential. A
|
|
76
|
+
* description carrying newlines becomes one comment line per line, so no input
|
|
77
|
+
* can produce an uncommented line and quietly turn the file into a live
|
|
78
|
+
* assignment.
|
|
79
|
+
*/
|
|
80
|
+
function block(spec) {
|
|
81
|
+
const description = spec.description.split("\n").map((line) => `# ${line}`);
|
|
82
|
+
return ["", ...description, `# ${spec.key}=`];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The full content of a fresh secrets file: a header, then a commented
|
|
86
|
+
* placeholder per spec. Every line is a comment, so creating the file never
|
|
87
|
+
* changes what ghostrail resolves.
|
|
88
|
+
*/
|
|
89
|
+
export function renderFile(specs) {
|
|
90
|
+
const lines = [...HEADER];
|
|
91
|
+
for (const spec of specs)
|
|
92
|
+
lines.push(...block(spec));
|
|
93
|
+
return `${lines.join("\n")}\n`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Append placeholder blocks for `specs` to existing content, preserving every
|
|
97
|
+
* byte of it up to its trailing newlines. Only the run of trailing newlines can
|
|
98
|
+
* change, which keeps repeated runs from stacking blank lines.
|
|
99
|
+
*/
|
|
100
|
+
export function appendPlaceholders(existing, specs) {
|
|
101
|
+
if (specs.length === 0)
|
|
102
|
+
return existing;
|
|
103
|
+
const base = existing.replace(/\n+$/, "");
|
|
104
|
+
const lines = ["# Added by ghostrail secrets-setup."];
|
|
105
|
+
for (const spec of specs)
|
|
106
|
+
lines.push(...block(spec));
|
|
107
|
+
const body = `${lines.join("\n")}\n`;
|
|
108
|
+
return base.trim().length === 0 ? body : `${base}\n\n${body}`;
|
|
109
|
+
}
|
|
110
|
+
/** The secrets file's content, or undefined when it does not exist. */
|
|
111
|
+
export async function readSecretsFile(path) {
|
|
112
|
+
try {
|
|
113
|
+
return await readFile(path, "utf8");
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Write the secrets file owner-only, creating its directory owner-only too. */
|
|
120
|
+
export async function writeSecretsFile(path, content) {
|
|
121
|
+
await mkdir(dirname(path), { recursive: true, mode: SECRETS_DIR_MODE });
|
|
122
|
+
await writeFile(path, content, { encoding: "utf8", mode: SECRETS_FILE_MODE });
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/secrets/setup.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skill/ghostrail/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ user-invocable: true
|
|
|
5
5
|
argument-hint: "[customize | init | doctor | status | run | respond | triage]"
|
|
6
6
|
license: Apache-2.0
|
|
7
7
|
metadata:
|
|
8
|
-
version: 0.
|
|
8
|
+
version: 0.2.0
|
|
9
9
|
allowed-tools:
|
|
10
10
|
- Read
|
|
11
11
|
- Write
|
|
@@ -85,6 +85,28 @@ Scaffold the factory into this repo, then hand off to **customize**.
|
|
|
85
85
|
3. Tell the user the two things the scaffold can't know (repo-specific prompt
|
|
86
86
|
detail and the tracker filters), and offer to run **customize** now.
|
|
87
87
|
|
|
88
|
+
### Already initialized? Check for template drift
|
|
89
|
+
|
|
90
|
+
If the repo already has `ghostrail.toml` and `prompts/`, do not re-run a bare
|
|
91
|
+
`init` and report "0 files written" as if nothing were wrong. Run:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
<GR> init <template> --diff
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
It is read-only. It reports each scaffolded file as up to date, locally
|
|
98
|
+
customized, safe to take, a conflict, or missing, using the baseline recorded in
|
|
99
|
+
`ghostrail.template.json`. Summarize the rows that are not up to date, show the
|
|
100
|
+
`diff` command it printed, and offer to merge template changes in by hand.
|
|
101
|
+
|
|
102
|
+
Never suggest `--force` as the way to take a template update: it overwrites every
|
|
103
|
+
file, destroying the gate commands, tracker filters, and prompt edits that
|
|
104
|
+
**customize** wrote. Merging by hand is the correct path.
|
|
105
|
+
|
|
106
|
+
If it reports no recorded baseline, the repo predates provenance. Re-running a
|
|
107
|
+
bare `<GR> init` once adopts a baseline for every file that still matches the
|
|
108
|
+
template exactly, which is safe and improves later diffs.
|
|
109
|
+
|
|
88
110
|
---
|
|
89
111
|
|
|
90
112
|
## doctor
|
|
@@ -101,7 +123,12 @@ Diagnose a setup without changing anything. Check and report each, with the fix:
|
|
|
101
123
|
- **Credentials** — report which of `LINEAR_API_KEY`, `GH_TOKEN`/`GITHUB_TOKEN`,
|
|
102
124
|
and `ANTHROPIC_API_KEY` **or** `CLAUDE_CODE_OAUTH_TOKEN` are set. Report only
|
|
103
125
|
set/unset, **never print a value**. Note that `ANTHROPIC_API_KEY` wins over the
|
|
104
|
-
OAuth token, so it must be unset to bill a Pro/Max subscription.
|
|
126
|
+
OAuth token, so it must be unset to bill a Pro/Max subscription. Remember they
|
|
127
|
+
can come from a file as well as the environment: `~/.config/ghostrail/secrets.env`
|
|
128
|
+
and a repo's `.ghostrail.env`, which beat an exported variable of the same name.
|
|
129
|
+
When something is missing, the fix to suggest is `ghostrail secrets-setup --write`,
|
|
130
|
+
which scaffolds that file and opens it in the user's editor. Never offer to fill
|
|
131
|
+
in a value yourself.
|
|
105
132
|
- **GitHub** — `gh auth status` succeeds.
|
|
106
133
|
- **Container backend**, if `[backend].kind = "container"` — the configured
|
|
107
134
|
`image` exists locally (`docker images`). If not, it must be built.
|