@ggui-ai/cli 0.1.0-rc.1
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/LICENSE +201 -0
- package/README.md +96 -0
- package/dist/agent-resolution.d.ts +41 -0
- package/dist/agent-resolution.d.ts.map +1 -0
- package/dist/agent-resolution.js +80 -0
- package/dist/api-client.d.ts +59 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +145 -0
- package/dist/auth-keys.d.ts +10 -0
- package/dist/auth-keys.d.ts.map +1 -0
- package/dist/auth-keys.js +382 -0
- package/dist/auth-login.d.ts +11 -0
- package/dist/auth-login.d.ts.map +1 -0
- package/dist/auth-login.js +231 -0
- package/dist/auth-logout.d.ts +3 -0
- package/dist/auth-logout.d.ts.map +1 -0
- package/dist/auth-logout.js +32 -0
- package/dist/auth-store.d.ts +67 -0
- package/dist/auth-store.d.ts.map +1 -0
- package/dist/auth-store.js +150 -0
- package/dist/auth-whoami.d.ts +3 -0
- package/dist/auth-whoami.d.ts.map +1 -0
- package/dist/auth-whoami.js +52 -0
- package/dist/blueprint-command.d.ts +3 -0
- package/dist/blueprint-command.d.ts.map +1 -0
- package/dist/blueprint-command.js +203 -0
- package/dist/blueprint-create.d.ts +71 -0
- package/dist/blueprint-create.d.ts.map +1 -0
- package/dist/blueprint-create.js +377 -0
- package/dist/byok-resolver.d.ts +141 -0
- package/dist/byok-resolver.d.ts.map +1 -0
- package/dist/byok-resolver.js +89 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1066 -0
- package/dist/dev-command.d.ts +169 -0
- package/dist/dev-command.d.ts.map +1 -0
- package/dist/dev-command.js +222 -0
- package/dist/gadget-command.d.ts +3 -0
- package/dist/gadget-command.d.ts.map +1 -0
- package/dist/gadget-command.js +181 -0
- package/dist/gadget-create.d.ts +106 -0
- package/dist/gadget-create.d.ts.map +1 -0
- package/dist/gadget-create.js +492 -0
- package/dist/generation-probe.d.ts +135 -0
- package/dist/generation-probe.d.ts.map +1 -0
- package/dist/generation-probe.js +132 -0
- package/dist/internal/artifact-install.d.ts +150 -0
- package/dist/internal/artifact-install.d.ts.map +1 -0
- package/dist/internal/artifact-install.js +1019 -0
- package/dist/internal/artifact-publish.d.ts +204 -0
- package/dist/internal/artifact-publish.d.ts.map +1 -0
- package/dist/internal/artifact-publish.js +1141 -0
- package/dist/internal/artifact-search.d.ts +155 -0
- package/dist/internal/artifact-search.d.ts.map +1 -0
- package/dist/internal/artifact-search.js +539 -0
- package/dist/internal/artifact-uninstall.d.ts +30 -0
- package/dist/internal/artifact-uninstall.d.ts.map +1 -0
- package/dist/internal/artifact-uninstall.js +205 -0
- package/dist/internal/auth-cache.d.ts +40 -0
- package/dist/internal/auth-cache.d.ts.map +1 -0
- package/dist/internal/auth-cache.js +133 -0
- package/dist/internal/auth-strategy.d.ts +89 -0
- package/dist/internal/auth-strategy.d.ts.map +1 -0
- package/dist/internal/auth-strategy.js +132 -0
- package/dist/internal/ggui-json.d.ts +53 -0
- package/dist/internal/ggui-json.d.ts.map +1 -0
- package/dist/internal/ggui-json.js +92 -0
- package/dist/internal/key-store.d.ts +43 -0
- package/dist/internal/key-store.d.ts.map +1 -0
- package/dist/internal/key-store.js +128 -0
- package/dist/internal/oidc-token.d.ts +52 -0
- package/dist/internal/oidc-token.d.ts.map +1 -0
- package/dist/internal/oidc-token.js +351 -0
- package/dist/internal/register-author-key.d.ts +44 -0
- package/dist/internal/register-author-key.d.ts.map +1 -0
- package/dist/internal/register-author-key.js +212 -0
- package/dist/local-keys.d.ts +47 -0
- package/dist/local-keys.d.ts.map +1 -0
- package/dist/local-keys.js +115 -0
- package/dist/mcp-backend.d.ts +356 -0
- package/dist/mcp-backend.d.ts.map +1 -0
- package/dist/mcp-backend.js +668 -0
- package/dist/paths.d.ts +77 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +119 -0
- package/dist/persistent-secrets.d.ts +28 -0
- package/dist/persistent-secrets.d.ts.map +1 -0
- package/dist/persistent-secrets.js +94 -0
- package/dist/persistent-short-code-index.d.ts +14 -0
- package/dist/persistent-short-code-index.d.ts.map +1 -0
- package/dist/persistent-short-code-index.js +39 -0
- package/dist/persistent-stores.d.ts +12 -0
- package/dist/persistent-stores.d.ts.map +1 -0
- package/dist/persistent-stores.js +42 -0
- package/dist/serve-command.d.ts +500 -0
- package/dist/serve-command.d.ts.map +1 -0
- package/dist/serve-command.js +616 -0
- package/dist/serve-fallback.d.ts +64 -0
- package/dist/serve-fallback.d.ts.map +1 -0
- package/dist/serve-fallback.js +122 -0
- package/dist/spawn-cloudflared.d.ts +61 -0
- package/dist/spawn-cloudflared.d.ts.map +1 -0
- package/dist/spawn-cloudflared.js +167 -0
- package/dist/theme-command.d.ts +4 -0
- package/dist/theme-command.d.ts.map +1 -0
- package/dist/theme-command.js +154 -0
- package/dist/theme-file-uploader.d.ts +8 -0
- package/dist/theme-file-uploader.d.ts.map +1 -0
- package/dist/theme-file-uploader.js +58 -0
- package/dist/theme-writer.d.ts +8 -0
- package/dist/theme-writer.d.ts.map +1 -0
- package/dist/theme-writer.js +103 -0
- package/dist/tunnel-provider.d.ts +273 -0
- package/dist/tunnel-provider.d.ts.map +1 -0
- package/dist/tunnel-provider.js +142 -0
- package/package.json +81 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Canonical filename — duplicated from `@ggui-ai/project-config` so
|
|
2
|
+
* this helper stays free of that dependency. The constant lives in one
|
|
3
|
+
* place per package; updates ride along on a project-config bump. */
|
|
4
|
+
export declare const GGUI_JSON_FILENAME = "ggui.json";
|
|
5
|
+
/** How many parent directories to walk before giving up. Mirrors the
|
|
6
|
+
* convention used by `gadget-search.ts#findGguiJson` + the upstream
|
|
7
|
+
* `@ggui-ai/project-config/node` helper. */
|
|
8
|
+
export declare const FIND_MAX_DEPTH = 8;
|
|
9
|
+
/**
|
|
10
|
+
* Walk up from `startDir` looking for the first `ggui.json` ancestor.
|
|
11
|
+
* Returns the absolute path or `null` if none found within
|
|
12
|
+
* {@link FIND_MAX_DEPTH} parent directories.
|
|
13
|
+
*
|
|
14
|
+
* Mirrors the algorithm in `@ggui-ai/project-config/node`'s
|
|
15
|
+
* `findGguiJson`. Duplicated here (rather than imported) so the
|
|
16
|
+
* publish/install/search subcommands don't pull the full project-config
|
|
17
|
+
* dependency tree — the boundary justification matches the existing
|
|
18
|
+
* `gadget-search.ts` rationale.
|
|
19
|
+
*/
|
|
20
|
+
export declare function findGguiJson(startDir: string): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Object shape of a successfully-decoded ggui.json. Always a plain
|
|
23
|
+
* JSON object at the root — arrays / scalars / null are rejected by
|
|
24
|
+
* the schema-side parser at boot, but this helper is structural so we
|
|
25
|
+
* narrow to "object-or-die" explicitly.
|
|
26
|
+
*/
|
|
27
|
+
export type GguiJsonObject = {
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Read + structurally validate a ggui.json file from a known path.
|
|
32
|
+
* Returns the decoded JSON object on success or a descriptive error
|
|
33
|
+
* string. The shape is left as `GguiJsonObject` (string-keyed unknown)
|
|
34
|
+
* because callers walk the tree by path; running the full
|
|
35
|
+
* `@ggui-ai/project-config` parser here would couple gadget-install
|
|
36
|
+
* to the entire manifest's schema posture.
|
|
37
|
+
*/
|
|
38
|
+
export declare function readGguiJson(path: string): {
|
|
39
|
+
value: GguiJsonObject;
|
|
40
|
+
} | {
|
|
41
|
+
error: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Write a decoded ggui.json back to disk. Preserves a 2-space indent
|
|
45
|
+
* with a trailing newline — matches the formatting `prettier` would
|
|
46
|
+
* apply, so the gadget-install diff stays minimal in operator repos
|
|
47
|
+
* that run prettier on commit. We do NOT round-trip JSON5 / comments
|
|
48
|
+
* (ggui.json is plain JSON per `@ggui-ai/project-config`); any
|
|
49
|
+
* pre-existing comments would have failed the original `JSON.parse`
|
|
50
|
+
* already.
|
|
51
|
+
*/
|
|
52
|
+
export declare function writeGguiJson(path: string, value: GguiJsonObject): void;
|
|
53
|
+
//# sourceMappingURL=ggui-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ggui-json.d.ts","sourceRoot":"","sources":["../../src/internal/ggui-json.ts"],"names":[],"mappings":"AAqBA;;qEAEqE;AACrE,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C;;4CAE4C;AAC5C,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU5D;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAyB/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAEvE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ggui.json read/write helpers shared by gadget subcommands.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the publish and install subcommands so neither has to
|
|
5
|
+
* duplicate the path-walk + JSON IO scaffolding. The parser is
|
|
6
|
+
* deliberately structural (no schema parse): install touches
|
|
7
|
+
* `app.gadgets` only, so a malformed `theme` block elsewhere in the
|
|
8
|
+
* file shouldn't prevent an install from rewriting the array.
|
|
9
|
+
*
|
|
10
|
+
* The shape returned is the raw decoded JSON. Callers narrow the
|
|
11
|
+
* sub-objects they touch (e.g. `app.gadgets`) and run the
|
|
12
|
+
* full schema parser themselves if they want round-trip validation —
|
|
13
|
+
* keeps this helper minimal + zero-dep against `@ggui-ai/project-config`.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
16
|
+
import { dirname, join, resolve } from 'node:path';
|
|
17
|
+
/** Canonical filename — duplicated from `@ggui-ai/project-config` so
|
|
18
|
+
* this helper stays free of that dependency. The constant lives in one
|
|
19
|
+
* place per package; updates ride along on a project-config bump. */
|
|
20
|
+
export const GGUI_JSON_FILENAME = 'ggui.json';
|
|
21
|
+
/** How many parent directories to walk before giving up. Mirrors the
|
|
22
|
+
* convention used by `gadget-search.ts#findGguiJson` + the upstream
|
|
23
|
+
* `@ggui-ai/project-config/node` helper. */
|
|
24
|
+
export const FIND_MAX_DEPTH = 8;
|
|
25
|
+
/**
|
|
26
|
+
* Walk up from `startDir` looking for the first `ggui.json` ancestor.
|
|
27
|
+
* Returns the absolute path or `null` if none found within
|
|
28
|
+
* {@link FIND_MAX_DEPTH} parent directories.
|
|
29
|
+
*
|
|
30
|
+
* Mirrors the algorithm in `@ggui-ai/project-config/node`'s
|
|
31
|
+
* `findGguiJson`. Duplicated here (rather than imported) so the
|
|
32
|
+
* publish/install/search subcommands don't pull the full project-config
|
|
33
|
+
* dependency tree — the boundary justification matches the existing
|
|
34
|
+
* `gadget-search.ts` rationale.
|
|
35
|
+
*/
|
|
36
|
+
export function findGguiJson(startDir) {
|
|
37
|
+
let dir = resolve(startDir);
|
|
38
|
+
for (let i = 0; i <= FIND_MAX_DEPTH; i++) {
|
|
39
|
+
const candidate = join(dir, GGUI_JSON_FILENAME);
|
|
40
|
+
if (existsSync(candidate))
|
|
41
|
+
return candidate;
|
|
42
|
+
const parent = dirname(dir);
|
|
43
|
+
if (parent === dir)
|
|
44
|
+
return null;
|
|
45
|
+
dir = parent;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Read + structurally validate a ggui.json file from a known path.
|
|
51
|
+
* Returns the decoded JSON object on success or a descriptive error
|
|
52
|
+
* string. The shape is left as `GguiJsonObject` (string-keyed unknown)
|
|
53
|
+
* because callers walk the tree by path; running the full
|
|
54
|
+
* `@ggui-ai/project-config` parser here would couple gadget-install
|
|
55
|
+
* to the entire manifest's schema posture.
|
|
56
|
+
*/
|
|
57
|
+
export function readGguiJson(path) {
|
|
58
|
+
let raw;
|
|
59
|
+
try {
|
|
60
|
+
raw = readFileSync(path, 'utf-8');
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
return {
|
|
64
|
+
error: `failed to read ${path}: ${err instanceof Error ? err.message : String(err)}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
let decoded;
|
|
68
|
+
try {
|
|
69
|
+
decoded = JSON.parse(raw);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
return {
|
|
73
|
+
error: `${path} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (decoded === null || typeof decoded !== 'object' || Array.isArray(decoded)) {
|
|
77
|
+
return { error: `${path} is not a JSON object` };
|
|
78
|
+
}
|
|
79
|
+
return { value: decoded };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Write a decoded ggui.json back to disk. Preserves a 2-space indent
|
|
83
|
+
* with a trailing newline — matches the formatting `prettier` would
|
|
84
|
+
* apply, so the gadget-install diff stays minimal in operator repos
|
|
85
|
+
* that run prettier on commit. We do NOT round-trip JSON5 / comments
|
|
86
|
+
* (ggui.json is plain JSON per `@ggui-ai/project-config`); any
|
|
87
|
+
* pre-existing comments would have failed the original `JSON.parse`
|
|
88
|
+
* already.
|
|
89
|
+
*/
|
|
90
|
+
export function writeGguiJson(path, value) {
|
|
91
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
|
|
92
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the default private-key path for an npm scope.
|
|
3
|
+
*
|
|
4
|
+
* - `@mapbox/foo-gadget` → `~/.ggui/keys/@mapbox/private.key`
|
|
5
|
+
* - `@you/widgets` → `~/.ggui/keys/@you/private.key`
|
|
6
|
+
*
|
|
7
|
+
* The `artifactId` argument is the full `<scope>/<name>`; this helper
|
|
8
|
+
* strips the name and uses the scope.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getPrivateKeyPath(scope: string): string;
|
|
11
|
+
export declare function getPublicKeyPath(scope: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Extract the npm scope from a `<scope>/<name>` artifactId. Throws on
|
|
14
|
+
* malformed input — the parser layer should have caught it first.
|
|
15
|
+
*/
|
|
16
|
+
export declare function scopeOf(artifactId: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Read a raw 32-byte private key from disk. Returns null when the
|
|
19
|
+
* file does not exist (the caller decides whether to generate vs.
|
|
20
|
+
* error). Throws when the file exists but is the wrong length —
|
|
21
|
+
* silent truncation would produce a broken signature.
|
|
22
|
+
*/
|
|
23
|
+
export declare function readPrivateKey(path: string): Uint8Array | null;
|
|
24
|
+
/**
|
|
25
|
+
* Write a 32-byte private key to disk with mode `0o600` + an
|
|
26
|
+
* accompanying public-key file alongside. Atomic via write-temp +
|
|
27
|
+
* rename so a crash mid-write doesn't strand a partial blob.
|
|
28
|
+
*/
|
|
29
|
+
export declare function writePrivateKey(scope: string, privateKey: Uint8Array, publicKey: Uint8Array): void;
|
|
30
|
+
/**
|
|
31
|
+
* Returns true when both halves exist on disk for `scope`. Used by
|
|
32
|
+
* the publish flow to decide between "load existing" vs. "generate
|
|
33
|
+
* new + register".
|
|
34
|
+
*/
|
|
35
|
+
export declare function hasKeypair(scope: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* stat helper — exported for tests that want to assert mode bits
|
|
38
|
+
* survived the round-trip.
|
|
39
|
+
*/
|
|
40
|
+
export declare function statPrivateKey(scope: string): {
|
|
41
|
+
mode: number;
|
|
42
|
+
} | null;
|
|
43
|
+
//# sourceMappingURL=key-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-store.d.ts","sourceRoot":"","sources":["../../src/internal/key-store.ts"],"names":[],"mappings":"AA6BA;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQlD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAS9D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,UAAU,GACpB,IAAI,CAyBN;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKrE"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Ed25519 signing-key store for `ggui gadget publish`.
|
|
3
|
+
*
|
|
4
|
+
* Layout: `~/.ggui/keys/<scope>/private.key` (raw 32-byte seed) +
|
|
5
|
+
* `public.key` next to it (raw 32-byte public). One directory per
|
|
6
|
+
* npm scope so an author publishing multiple gadgets under the same
|
|
7
|
+
* scope reuses one key; switching scopes generates a new keypair on
|
|
8
|
+
* next first-publish.
|
|
9
|
+
*
|
|
10
|
+
* **Why raw bytes.** PEM/JWK wrappers add format-decode failure
|
|
11
|
+
* modes. The signer in `gadget-signing.ts` consumes raw seeds; the
|
|
12
|
+
* round-trip is "read 32 bytes → sign". Operators copying keys
|
|
13
|
+
* between machines `cat`-with-mode-0600 the file.
|
|
14
|
+
*
|
|
15
|
+
* **Mode 0o600.** Same discipline as `~/.ggui/auth.json` — the
|
|
16
|
+
* private key is a secret equivalent to a long-lived password.
|
|
17
|
+
*/
|
|
18
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync, } from 'node:fs';
|
|
19
|
+
import { dirname, join } from 'node:path';
|
|
20
|
+
import { getConfigDir } from '../paths.js';
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the default private-key path for an npm scope.
|
|
23
|
+
*
|
|
24
|
+
* - `@mapbox/foo-gadget` → `~/.ggui/keys/@mapbox/private.key`
|
|
25
|
+
* - `@you/widgets` → `~/.ggui/keys/@you/private.key`
|
|
26
|
+
*
|
|
27
|
+
* The `artifactId` argument is the full `<scope>/<name>`; this helper
|
|
28
|
+
* strips the name and uses the scope.
|
|
29
|
+
*/
|
|
30
|
+
export function getPrivateKeyPath(scope) {
|
|
31
|
+
const dir = join(getConfigDir(), 'keys', scope);
|
|
32
|
+
return join(dir, 'private.key');
|
|
33
|
+
}
|
|
34
|
+
export function getPublicKeyPath(scope) {
|
|
35
|
+
const dir = join(getConfigDir(), 'keys', scope);
|
|
36
|
+
return join(dir, 'public.key');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract the npm scope from a `<scope>/<name>` artifactId. Throws on
|
|
40
|
+
* malformed input — the parser layer should have caught it first.
|
|
41
|
+
*/
|
|
42
|
+
export function scopeOf(artifactId) {
|
|
43
|
+
const slash = artifactId.indexOf('/');
|
|
44
|
+
if (slash === -1 || !artifactId.startsWith('@')) {
|
|
45
|
+
throw new Error(`key-store: artifactId must be \`<scope>/<name>\` (got ${artifactId})`);
|
|
46
|
+
}
|
|
47
|
+
return artifactId.slice(0, slash);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Read a raw 32-byte private key from disk. Returns null when the
|
|
51
|
+
* file does not exist (the caller decides whether to generate vs.
|
|
52
|
+
* error). Throws when the file exists but is the wrong length —
|
|
53
|
+
* silent truncation would produce a broken signature.
|
|
54
|
+
*/
|
|
55
|
+
export function readPrivateKey(path) {
|
|
56
|
+
if (!existsSync(path))
|
|
57
|
+
return null;
|
|
58
|
+
const buf = readFileSync(path);
|
|
59
|
+
if (buf.length !== 32) {
|
|
60
|
+
throw new Error(`key-store: ${path} is not a 32-byte Ed25519 seed (got ${buf.length} bytes)`);
|
|
61
|
+
}
|
|
62
|
+
return new Uint8Array(buf);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Write a 32-byte private key to disk with mode `0o600` + an
|
|
66
|
+
* accompanying public-key file alongside. Atomic via write-temp +
|
|
67
|
+
* rename so a crash mid-write doesn't strand a partial blob.
|
|
68
|
+
*/
|
|
69
|
+
export function writePrivateKey(scope, privateKey, publicKey) {
|
|
70
|
+
if (privateKey.length !== 32) {
|
|
71
|
+
throw new Error(`key-store: private key must be 32 bytes (got ${privateKey.length})`);
|
|
72
|
+
}
|
|
73
|
+
if (publicKey.length !== 32) {
|
|
74
|
+
throw new Error(`key-store: public key must be 32 bytes (got ${publicKey.length})`);
|
|
75
|
+
}
|
|
76
|
+
const privPath = getPrivateKeyPath(scope);
|
|
77
|
+
const pubPath = getPublicKeyPath(scope);
|
|
78
|
+
const dir = dirname(privPath);
|
|
79
|
+
if (!existsSync(dir)) {
|
|
80
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
try {
|
|
84
|
+
chmodSync(dir, 0o700);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// best-effort
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
writeAtomic(privPath, Buffer.from(privateKey), 0o600);
|
|
91
|
+
writeAtomic(pubPath, Buffer.from(publicKey), 0o644);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Returns true when both halves exist on disk for `scope`. Used by
|
|
95
|
+
* the publish flow to decide between "load existing" vs. "generate
|
|
96
|
+
* new + register".
|
|
97
|
+
*/
|
|
98
|
+
export function hasKeypair(scope) {
|
|
99
|
+
return existsSync(getPrivateKeyPath(scope));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* stat helper — exported for tests that want to assert mode bits
|
|
103
|
+
* survived the round-trip.
|
|
104
|
+
*/
|
|
105
|
+
export function statPrivateKey(scope) {
|
|
106
|
+
const path = getPrivateKeyPath(scope);
|
|
107
|
+
if (!existsSync(path))
|
|
108
|
+
return null;
|
|
109
|
+
const s = statSync(path);
|
|
110
|
+
return { mode: s.mode };
|
|
111
|
+
}
|
|
112
|
+
function writeAtomic(target, bytes, mode) {
|
|
113
|
+
const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
|
|
114
|
+
writeFileSync(tmp, bytes, { mode });
|
|
115
|
+
try {
|
|
116
|
+
chmodSync(tmp, mode);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// best-effort
|
|
120
|
+
}
|
|
121
|
+
renameSync(tmp, target);
|
|
122
|
+
try {
|
|
123
|
+
chmodSync(target, mode);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// best-effort
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface OidcResolveOptions {
|
|
2
|
+
/** `--identity-token <jwt>` flag value (cosign convention). */
|
|
3
|
+
readonly identityTokenFlag?: string;
|
|
4
|
+
/** Environment snapshot — keeps the resolver pure for tests. */
|
|
5
|
+
readonly env: NodeJS.ProcessEnv;
|
|
6
|
+
/** Is stdout attached to a TTY? Gates the interactive browser flow. */
|
|
7
|
+
readonly isTty: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Override the issuer for the interactive flow. Default:
|
|
10
|
+
* `https://oauth2.sigstore.dev/auth`.
|
|
11
|
+
*/
|
|
12
|
+
readonly issuer?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Test seam: when `false`, skip the interactive flow entirely + throw
|
|
15
|
+
* `no_token_available` instead. Production callers leave it `true`
|
|
16
|
+
* (or omit it).
|
|
17
|
+
*/
|
|
18
|
+
readonly interactive?: boolean;
|
|
19
|
+
/** Test seam: inject `fetch`. Production = `globalThis.fetch`. */
|
|
20
|
+
readonly fetch?: typeof fetch;
|
|
21
|
+
/** Test seam: write status lines. Production = `process.stdout.write`. */
|
|
22
|
+
readonly stdout?: (line: string) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Test seam: ask the operator for a value (used by the non-TTY
|
|
25
|
+
* fallback inside the interactive branch). Production = stdin prompt.
|
|
26
|
+
*/
|
|
27
|
+
readonly prompt?: (label: string) => Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Test seam: open the issuer URL in the operator's browser. Production
|
|
30
|
+
* prints the URL + relies on the user clicking it. The `open` package
|
|
31
|
+
* isn't a workspace dep yet, so the default impl just prints — this
|
|
32
|
+
* seam lets tests + future wiring swap it.
|
|
33
|
+
*/
|
|
34
|
+
readonly openBrowser?: (url: string) => Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export interface OidcResolveResult {
|
|
37
|
+
/** The resolved OIDC token (JWT). */
|
|
38
|
+
readonly token: string;
|
|
39
|
+
/** Where the token came from. */
|
|
40
|
+
readonly source: 'flag' | 'env' | 'github-actions' | 'interactive';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Sentinel error thrown when no OIDC source produces a usable token.
|
|
44
|
+
* Carries a `code` discriminant so callers can map back into a
|
|
45
|
+
* structured `PublishError`.
|
|
46
|
+
*/
|
|
47
|
+
export declare class OidcResolutionError extends Error {
|
|
48
|
+
readonly code: 'no_token_available' | 'github_actions_fetch_failed' | 'interactive_failed';
|
|
49
|
+
constructor(code: 'no_token_available' | 'github_actions_fetch_failed' | 'interactive_failed', message: string);
|
|
50
|
+
}
|
|
51
|
+
export declare function resolveOidcToken(opts: OidcResolveOptions): Promise<OidcResolveResult>;
|
|
52
|
+
//# sourceMappingURL=oidc-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-token.d.ts","sourceRoot":"","sources":["../../src/internal/oidc-token.ts"],"names":[],"mappings":"AAsCA,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,gEAAgE;IAChE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,gBAAgB,GAAG,aAAa,CAAC;CACpE;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EACT,oBAAoB,GACpB,6BAA6B,GAC7B,oBAAoB,CAAC;gBAEvB,IAAI,EACA,oBAAoB,GACpB,6BAA6B,GAC7B,oBAAoB,EACxB,OAAO,EAAE,MAAM;CAMlB;AAWD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CAgD5B"}
|