@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.
Files changed (117) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +96 -0
  3. package/dist/agent-resolution.d.ts +41 -0
  4. package/dist/agent-resolution.d.ts.map +1 -0
  5. package/dist/agent-resolution.js +80 -0
  6. package/dist/api-client.d.ts +59 -0
  7. package/dist/api-client.d.ts.map +1 -0
  8. package/dist/api-client.js +145 -0
  9. package/dist/auth-keys.d.ts +10 -0
  10. package/dist/auth-keys.d.ts.map +1 -0
  11. package/dist/auth-keys.js +382 -0
  12. package/dist/auth-login.d.ts +11 -0
  13. package/dist/auth-login.d.ts.map +1 -0
  14. package/dist/auth-login.js +231 -0
  15. package/dist/auth-logout.d.ts +3 -0
  16. package/dist/auth-logout.d.ts.map +1 -0
  17. package/dist/auth-logout.js +32 -0
  18. package/dist/auth-store.d.ts +67 -0
  19. package/dist/auth-store.d.ts.map +1 -0
  20. package/dist/auth-store.js +150 -0
  21. package/dist/auth-whoami.d.ts +3 -0
  22. package/dist/auth-whoami.d.ts.map +1 -0
  23. package/dist/auth-whoami.js +52 -0
  24. package/dist/blueprint-command.d.ts +3 -0
  25. package/dist/blueprint-command.d.ts.map +1 -0
  26. package/dist/blueprint-command.js +203 -0
  27. package/dist/blueprint-create.d.ts +71 -0
  28. package/dist/blueprint-create.d.ts.map +1 -0
  29. package/dist/blueprint-create.js +377 -0
  30. package/dist/byok-resolver.d.ts +141 -0
  31. package/dist/byok-resolver.d.ts.map +1 -0
  32. package/dist/byok-resolver.js +89 -0
  33. package/dist/cli.d.ts +3 -0
  34. package/dist/cli.d.ts.map +1 -0
  35. package/dist/cli.js +1066 -0
  36. package/dist/dev-command.d.ts +169 -0
  37. package/dist/dev-command.d.ts.map +1 -0
  38. package/dist/dev-command.js +222 -0
  39. package/dist/gadget-command.d.ts +3 -0
  40. package/dist/gadget-command.d.ts.map +1 -0
  41. package/dist/gadget-command.js +181 -0
  42. package/dist/gadget-create.d.ts +106 -0
  43. package/dist/gadget-create.d.ts.map +1 -0
  44. package/dist/gadget-create.js +492 -0
  45. package/dist/generation-probe.d.ts +135 -0
  46. package/dist/generation-probe.d.ts.map +1 -0
  47. package/dist/generation-probe.js +132 -0
  48. package/dist/internal/artifact-install.d.ts +150 -0
  49. package/dist/internal/artifact-install.d.ts.map +1 -0
  50. package/dist/internal/artifact-install.js +1019 -0
  51. package/dist/internal/artifact-publish.d.ts +204 -0
  52. package/dist/internal/artifact-publish.d.ts.map +1 -0
  53. package/dist/internal/artifact-publish.js +1141 -0
  54. package/dist/internal/artifact-search.d.ts +155 -0
  55. package/dist/internal/artifact-search.d.ts.map +1 -0
  56. package/dist/internal/artifact-search.js +539 -0
  57. package/dist/internal/artifact-uninstall.d.ts +30 -0
  58. package/dist/internal/artifact-uninstall.d.ts.map +1 -0
  59. package/dist/internal/artifact-uninstall.js +205 -0
  60. package/dist/internal/auth-cache.d.ts +40 -0
  61. package/dist/internal/auth-cache.d.ts.map +1 -0
  62. package/dist/internal/auth-cache.js +133 -0
  63. package/dist/internal/auth-strategy.d.ts +89 -0
  64. package/dist/internal/auth-strategy.d.ts.map +1 -0
  65. package/dist/internal/auth-strategy.js +132 -0
  66. package/dist/internal/ggui-json.d.ts +53 -0
  67. package/dist/internal/ggui-json.d.ts.map +1 -0
  68. package/dist/internal/ggui-json.js +92 -0
  69. package/dist/internal/key-store.d.ts +43 -0
  70. package/dist/internal/key-store.d.ts.map +1 -0
  71. package/dist/internal/key-store.js +128 -0
  72. package/dist/internal/oidc-token.d.ts +52 -0
  73. package/dist/internal/oidc-token.d.ts.map +1 -0
  74. package/dist/internal/oidc-token.js +351 -0
  75. package/dist/internal/register-author-key.d.ts +44 -0
  76. package/dist/internal/register-author-key.d.ts.map +1 -0
  77. package/dist/internal/register-author-key.js +212 -0
  78. package/dist/local-keys.d.ts +47 -0
  79. package/dist/local-keys.d.ts.map +1 -0
  80. package/dist/local-keys.js +115 -0
  81. package/dist/mcp-backend.d.ts +356 -0
  82. package/dist/mcp-backend.d.ts.map +1 -0
  83. package/dist/mcp-backend.js +668 -0
  84. package/dist/paths.d.ts +77 -0
  85. package/dist/paths.d.ts.map +1 -0
  86. package/dist/paths.js +119 -0
  87. package/dist/persistent-secrets.d.ts +28 -0
  88. package/dist/persistent-secrets.d.ts.map +1 -0
  89. package/dist/persistent-secrets.js +94 -0
  90. package/dist/persistent-short-code-index.d.ts +14 -0
  91. package/dist/persistent-short-code-index.d.ts.map +1 -0
  92. package/dist/persistent-short-code-index.js +39 -0
  93. package/dist/persistent-stores.d.ts +12 -0
  94. package/dist/persistent-stores.d.ts.map +1 -0
  95. package/dist/persistent-stores.js +42 -0
  96. package/dist/serve-command.d.ts +500 -0
  97. package/dist/serve-command.d.ts.map +1 -0
  98. package/dist/serve-command.js +616 -0
  99. package/dist/serve-fallback.d.ts +64 -0
  100. package/dist/serve-fallback.d.ts.map +1 -0
  101. package/dist/serve-fallback.js +122 -0
  102. package/dist/spawn-cloudflared.d.ts +61 -0
  103. package/dist/spawn-cloudflared.d.ts.map +1 -0
  104. package/dist/spawn-cloudflared.js +167 -0
  105. package/dist/theme-command.d.ts +4 -0
  106. package/dist/theme-command.d.ts.map +1 -0
  107. package/dist/theme-command.js +154 -0
  108. package/dist/theme-file-uploader.d.ts +8 -0
  109. package/dist/theme-file-uploader.d.ts.map +1 -0
  110. package/dist/theme-file-uploader.js +58 -0
  111. package/dist/theme-writer.d.ts +8 -0
  112. package/dist/theme-writer.d.ts.map +1 -0
  113. package/dist/theme-writer.js +103 -0
  114. package/dist/tunnel-provider.d.ts +273 -0
  115. package/dist/tunnel-provider.d.ts.map +1 -0
  116. package/dist/tunnel-provider.js +142 -0
  117. package/package.json +81 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Return the OSS CLI config directory.
3
+ *
4
+ * Resolution order:
5
+ * 1. `GGUI_CONFIG_DIR` env (if set + non-empty) — tests + clean-room.
6
+ * 2. `~/.ggui` (`os.homedir() + DIRNAME`) — operator default.
7
+ */
8
+ export declare function getConfigDir(): string;
9
+ /**
10
+ * Path to the BYOK credentials file. Written + read by the
11
+ * `PlaintextFileProviderKeyStore` from `@ggui-ai/mcp-server-core/plaintext`
12
+ * at the global app-scope (single-user personal-mode default).
13
+ *
14
+ * On-disk shape is `PlaintextFileProviderKeyStore`'s v1 document. Mode
15
+ * 0o600 is enforced by the store on every write.
16
+ */
17
+ export declare function getCredentialsFile(): string;
18
+ /**
19
+ * Path to the CLI auth-session file. Written by `ggui login` after a
20
+ * successful device-flow exchange against `api.ggui.ai`; read by every
21
+ * authenticated command (`whoami`, `keys *`).
22
+ *
23
+ * On-disk shape is `AuthSessionDocument` from `./lib/auth-store.ts`.
24
+ * Mode 0o600 is enforced by the store on every write — the access +
25
+ * refresh tokens are bearer secrets equivalent to a short-lived
26
+ * password.
27
+ */
28
+ export declare function getAuthFile(): string;
29
+ /**
30
+ * Path to the local-embedding model cache. Resolution order matches
31
+ * the OSS full-generation port plan (§2.B + §7.2):
32
+ *
33
+ * 1. `GGUI_EMBEDDING_CACHE_DIR` env — operator override (CI, custom
34
+ * mounts, shared NFS).
35
+ * 2. `~/.ggui/models` — first-run default. Owned by ggui to avoid
36
+ * polluting the operator's `~/.cache/huggingface` cache.
37
+ *
38
+ * Returned even when the directory does not exist on disk — the
39
+ * embedding bootstrap creates it on first warmup.
40
+ */
41
+ export declare function getEmbeddingCacheDir(): string;
42
+ /**
43
+ * Path to the content-addressable componentCode cache. Used by
44
+ * `FileSystemCodeStore` when wired as the OSS dev default.
45
+ *
46
+ * Resolution order:
47
+ * 1. `GGUI_CODE_CACHE_DIR` env — operator override.
48
+ * 2. `~/.ggui/code-cache` — first-run default.
49
+ *
50
+ * The store creates the directory lazily on first put; the path is
51
+ * safe to return even when nothing has been written yet. Operators
52
+ * who want to reset the cache can `rm -rf` this directory at any
53
+ * time — every entry is content-addressable + immutable, so a
54
+ * re-fetch repopulates from upstream code on next push.
55
+ */
56
+ export declare function getCodeCacheDir(): string;
57
+ /**
58
+ * Directory backing the `--persistent` bundle (sessions.sqlite,
59
+ * short-codes.sqlite, bootstrap-secret.hex, render-signer-secret.hex,
60
+ * keys.json …). Survives `ggui serve` restarts so claude.ai chat-history
61
+ * revisits can rehydrate the iframe instead of seeing four cascading
62
+ * failures (HMAC + shortCode + session + pairing).
63
+ *
64
+ * Resolution order:
65
+ * 1. `GGUI_PERSISTENT_DIR` env — operator override (tests, clean-room,
66
+ * shared mount).
67
+ * 2. `<projectRoot>/.ggui/persistent` — when the CLI resolved a
68
+ * ggui.json. Per-project isolation; different projects get
69
+ * separate sessions even on the same machine.
70
+ * 3. `~/.ggui/persistent` — fallback when there's no manifest
71
+ * (`ggui serve` from a bare directory).
72
+ *
73
+ * Returned even when the directory doesn't exist on disk — the
74
+ * persistence wiring creates parents on first write.
75
+ */
76
+ export declare function getPersistentDir(projectRoot?: string | null): string;
77
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAkBA;;;;;;GAMG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAIrC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAI7C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAIxC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAOpE"}
package/dist/paths.js ADDED
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Centralized path helpers for the open `@ggui-ai/cli` (`ggui`).
3
+ *
4
+ * The OSS dirstub lives at `~/.ggui/`. All CLI-managed state
5
+ * (credentials, embedding cache, code cache, persistent bundle) is
6
+ * rooted under this single directory.
7
+ *
8
+ * Override the dir via `GGUI_CONFIG_DIR` env so tests + clean-room
9
+ * harnesses can scope the store to a temp dir without touching the
10
+ * operator's HOME. The override is read on every call (no module-level
11
+ * cache) so process-level env mutations between calls take effect —
12
+ * matters for vitest suites that mutate `process.env` per test.
13
+ */
14
+ import { homedir } from 'node:os';
15
+ import { join } from 'node:path';
16
+ const DIRNAME = '.ggui';
17
+ /**
18
+ * Return the OSS CLI config directory.
19
+ *
20
+ * Resolution order:
21
+ * 1. `GGUI_CONFIG_DIR` env (if set + non-empty) — tests + clean-room.
22
+ * 2. `~/.ggui` (`os.homedir() + DIRNAME`) — operator default.
23
+ */
24
+ export function getConfigDir() {
25
+ const override = process.env['GGUI_CONFIG_DIR'];
26
+ if (override && override.length > 0)
27
+ return override;
28
+ return join(homedir(), DIRNAME);
29
+ }
30
+ /**
31
+ * Path to the BYOK credentials file. Written + read by the
32
+ * `PlaintextFileProviderKeyStore` from `@ggui-ai/mcp-server-core/plaintext`
33
+ * at the global app-scope (single-user personal-mode default).
34
+ *
35
+ * On-disk shape is `PlaintextFileProviderKeyStore`'s v1 document. Mode
36
+ * 0o600 is enforced by the store on every write.
37
+ */
38
+ export function getCredentialsFile() {
39
+ return join(getConfigDir(), 'credentials.json');
40
+ }
41
+ /**
42
+ * Path to the CLI auth-session file. Written by `ggui login` after a
43
+ * successful device-flow exchange against `api.ggui.ai`; read by every
44
+ * authenticated command (`whoami`, `keys *`).
45
+ *
46
+ * On-disk shape is `AuthSessionDocument` from `./lib/auth-store.ts`.
47
+ * Mode 0o600 is enforced by the store on every write — the access +
48
+ * refresh tokens are bearer secrets equivalent to a short-lived
49
+ * password.
50
+ */
51
+ export function getAuthFile() {
52
+ return join(getConfigDir(), 'auth.json');
53
+ }
54
+ /**
55
+ * Path to the local-embedding model cache. Resolution order matches
56
+ * the OSS full-generation port plan (§2.B + §7.2):
57
+ *
58
+ * 1. `GGUI_EMBEDDING_CACHE_DIR` env — operator override (CI, custom
59
+ * mounts, shared NFS).
60
+ * 2. `~/.ggui/models` — first-run default. Owned by ggui to avoid
61
+ * polluting the operator's `~/.cache/huggingface` cache.
62
+ *
63
+ * Returned even when the directory does not exist on disk — the
64
+ * embedding bootstrap creates it on first warmup.
65
+ */
66
+ export function getEmbeddingCacheDir() {
67
+ const override = process.env['GGUI_EMBEDDING_CACHE_DIR'];
68
+ if (override && override.length > 0)
69
+ return override;
70
+ return join(getConfigDir(), 'models');
71
+ }
72
+ /**
73
+ * Path to the content-addressable componentCode cache. Used by
74
+ * `FileSystemCodeStore` when wired as the OSS dev default.
75
+ *
76
+ * Resolution order:
77
+ * 1. `GGUI_CODE_CACHE_DIR` env — operator override.
78
+ * 2. `~/.ggui/code-cache` — first-run default.
79
+ *
80
+ * The store creates the directory lazily on first put; the path is
81
+ * safe to return even when nothing has been written yet. Operators
82
+ * who want to reset the cache can `rm -rf` this directory at any
83
+ * time — every entry is content-addressable + immutable, so a
84
+ * re-fetch repopulates from upstream code on next push.
85
+ */
86
+ export function getCodeCacheDir() {
87
+ const override = process.env['GGUI_CODE_CACHE_DIR'];
88
+ if (override && override.length > 0)
89
+ return override;
90
+ return join(getConfigDir(), 'code-cache');
91
+ }
92
+ /**
93
+ * Directory backing the `--persistent` bundle (sessions.sqlite,
94
+ * short-codes.sqlite, bootstrap-secret.hex, render-signer-secret.hex,
95
+ * keys.json …). Survives `ggui serve` restarts so claude.ai chat-history
96
+ * revisits can rehydrate the iframe instead of seeing four cascading
97
+ * failures (HMAC + shortCode + session + pairing).
98
+ *
99
+ * Resolution order:
100
+ * 1. `GGUI_PERSISTENT_DIR` env — operator override (tests, clean-room,
101
+ * shared mount).
102
+ * 2. `<projectRoot>/.ggui/persistent` — when the CLI resolved a
103
+ * ggui.json. Per-project isolation; different projects get
104
+ * separate sessions even on the same machine.
105
+ * 3. `~/.ggui/persistent` — fallback when there's no manifest
106
+ * (`ggui serve` from a bare directory).
107
+ *
108
+ * Returned even when the directory doesn't exist on disk — the
109
+ * persistence wiring creates parents on first write.
110
+ */
111
+ export function getPersistentDir(projectRoot) {
112
+ const override = process.env['GGUI_PERSISTENT_DIR'];
113
+ if (override && override.length > 0)
114
+ return override;
115
+ if (projectRoot && projectRoot.length > 0) {
116
+ return join(projectRoot, DIRNAME, 'persistent');
117
+ }
118
+ return join(getConfigDir(), 'persistent');
119
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Read a hex-encoded HMAC secret from `filename`. If the file doesn't
3
+ * exist (ENOENT), mint a fresh 32-byte secret, write it with mode 0600,
4
+ * and return the hex string.
5
+ *
6
+ * Concurrency: the write is atomic — a fresh secret is written to a
7
+ * sibling temp file with `O_CREAT | O_WRONLY` and then `rename(2)`'d
8
+ * into place. POSIX guarantees `rename` is atomic on the same
9
+ * filesystem, so two `ggui serve` processes racing on the same
10
+ * persistent dir can't tear the file at a byte boundary; the loser's
11
+ * rename overwrites the winner's. Both processes then re-read the
12
+ * same surviving value on next boot — last-writer-wins on the secret
13
+ * itself, never partial-write corruption. (Two processes simultaneously
14
+ * minting fresh secrets is already a degenerate case for a per-user
15
+ * persistent dir.)
16
+ *
17
+ * Throws on:
18
+ * - read errors other than ENOENT (permission denied, EISDIR, etc.)
19
+ * - write errors (mkdir / writeFile / rename)
20
+ * - on-disk content that isn't a 64-char hex string (corrupt secret)
21
+ *
22
+ * The throw-on-corrupt branch is deliberate: silently re-minting would
23
+ * invalidate every outstanding token without telling the operator.
24
+ * Better to fail loud so the operator can diagnose (manual edit?) and
25
+ * decide whether to delete and accept the cost.
26
+ */
27
+ export declare function readOrMintHexSecret(filename: string): string;
28
+ //# sourceMappingURL=persistent-secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistent-secrets.d.ts","sourceRoot":"","sources":["../src/persistent-secrets.ts"],"names":[],"mappings":"AAiCA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA4B5D"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Read-or-mint helpers for HMAC secrets that MUST survive a `ggui serve`
3
+ * restart.
4
+ *
5
+ * Background: `createGguiServer` mints two HMAC secrets at boot when the
6
+ * caller doesn't pass them — `bootstrapSecret` (signs MCP Apps `_meta.
7
+ * ggui.bootstrap.token` so iframes can subscribe to the live-channel WS)
8
+ * and the render-URL signer secret (signs `/r/<code>?sig=&exp=`). Both
9
+ * are process-local `randomBytes(32)` by default — fine for a single
10
+ * process lifetime, fatal for "revisit a claude.ai chat history after a
11
+ * restart": the cached bootstrap token + cached render URL fail HMAC
12
+ * verify against the freshly-minted secret.
13
+ *
14
+ * Persisting the secrets to a 0600-mode file makes those tokens valid
15
+ * across restarts, which is the precondition for any rehydrate path
16
+ * working at all. The file format is intentionally tiny: 64 chars of
17
+ * lowercase hex (32 bytes), no trailing newline, no JSON wrapper. Easy
18
+ * to inspect, easy to rotate (delete the file, restart, fresh secret).
19
+ *
20
+ * Security posture: these secrets are a session-token signing key, NOT
21
+ * a long-lived credential like an OAuth client secret. Losing them
22
+ * means an attacker can mint MCP Apps bootstrap tokens for arbitrary
23
+ * `(sessionId, appId)` pairs — same blast radius as before, just
24
+ * surviving restart. Mode 0600 on the file matches `~/.ggui/credentials
25
+ * .json` (BYOK keys) which has the same threat model.
26
+ *
27
+ * Caller composes the path; this module is pure I/O over a given
28
+ * filename so tests can scope to a tmpdir.
29
+ */
30
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
31
+ import { randomBytes } from 'node:crypto';
32
+ import { dirname, join } from 'node:path';
33
+ /**
34
+ * Read a hex-encoded HMAC secret from `filename`. If the file doesn't
35
+ * exist (ENOENT), mint a fresh 32-byte secret, write it with mode 0600,
36
+ * and return the hex string.
37
+ *
38
+ * Concurrency: the write is atomic — a fresh secret is written to a
39
+ * sibling temp file with `O_CREAT | O_WRONLY` and then `rename(2)`'d
40
+ * into place. POSIX guarantees `rename` is atomic on the same
41
+ * filesystem, so two `ggui serve` processes racing on the same
42
+ * persistent dir can't tear the file at a byte boundary; the loser's
43
+ * rename overwrites the winner's. Both processes then re-read the
44
+ * same surviving value on next boot — last-writer-wins on the secret
45
+ * itself, never partial-write corruption. (Two processes simultaneously
46
+ * minting fresh secrets is already a degenerate case for a per-user
47
+ * persistent dir.)
48
+ *
49
+ * Throws on:
50
+ * - read errors other than ENOENT (permission denied, EISDIR, etc.)
51
+ * - write errors (mkdir / writeFile / rename)
52
+ * - on-disk content that isn't a 64-char hex string (corrupt secret)
53
+ *
54
+ * The throw-on-corrupt branch is deliberate: silently re-minting would
55
+ * invalidate every outstanding token without telling the operator.
56
+ * Better to fail loud so the operator can diagnose (manual edit?) and
57
+ * decide whether to delete and accept the cost.
58
+ */
59
+ export function readOrMintHexSecret(filename) {
60
+ let existing = null;
61
+ try {
62
+ existing = readFileSync(filename, 'utf8').trim();
63
+ }
64
+ catch (err) {
65
+ if (!isEnoent(err))
66
+ throw err;
67
+ }
68
+ if (existing !== null) {
69
+ if (!HEX_64.test(existing)) {
70
+ throw new Error(`[ggui-cli] persistent secret at ${filename} is corrupt — expected ` +
71
+ `64 lowercase hex chars (32 bytes), got ${existing.length} chars. ` +
72
+ `Delete the file to re-mint a fresh secret (this will invalidate ` +
73
+ `every outstanding token).`);
74
+ }
75
+ return existing;
76
+ }
77
+ const minted = randomBytes(32).toString('hex');
78
+ const parent = dirname(filename);
79
+ mkdirSync(parent, { recursive: true });
80
+ // Atomic write: pid-suffixed temp file in the same dir, then rename
81
+ // into place. Same-filesystem rename is atomic on POSIX, so a racing
82
+ // boot from another process can't observe a half-written secret.
83
+ const tmp = join(parent, `${filename.split('/').pop()}.tmp-${process.pid}`);
84
+ writeFileSync(tmp, minted, { mode: 0o600, encoding: 'utf8' });
85
+ renameSync(tmp, filename);
86
+ return minted;
87
+ }
88
+ const HEX_64 = /^[0-9a-f]{64}$/;
89
+ function isEnoent(err) {
90
+ return (typeof err === 'object'
91
+ && err !== null
92
+ && 'code' in err
93
+ && err.code === 'ENOENT');
94
+ }
@@ -0,0 +1,14 @@
1
+ import type { ShortCodeIndex } from '@ggui-ai/mcp-server-core';
2
+ /**
3
+ * Construct the persistent ShortCodeIndex backed by `<persistentDir>/
4
+ * short-codes.sqlite`. Creates the parent dir if missing (the persistent
5
+ * dir always exists by the time this is called — the secrets helper
6
+ * already mkdir'd it — but mkdir is idempotent and cheap).
7
+ *
8
+ * Throws if `better-sqlite3` can't be loaded (e.g. native binding
9
+ * missing for the current platform). The CLI surfaces this as a fatal
10
+ * boot error so the operator can either install the binding or pass
11
+ * `--ephemeral`.
12
+ */
13
+ export declare function createPersistentShortCodeIndex(persistentDir: string): Promise<ShortCodeIndex>;
14
+ //# sourceMappingURL=persistent-short-code-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistent-short-code-index.d.ts","sourceRoot":"","sources":["../src/persistent-short-code-index.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;;;;;;GAUG;AACH,wBAAsB,8BAA8B,CAClD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,cAAc,CAAC,CAQzB"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Resolve a persistent `ShortCodeIndex` for `ggui serve`.
3
+ *
4
+ * The OSS default for `shortCodeIndex` is `InMemoryShortCodeIndex` —
5
+ * the mapping lives in process memory and a restart drops every
6
+ * binding, so `/r/<code>` + `/api/bootstrap/<code>` 404 on the cached
7
+ * envelopes claude.ai stored before the restart.
8
+ *
9
+ * When the CLI is running in persistent mode (no `--ephemeral`), this
10
+ * helper constructs a `SqliteShortCodeIndex` against
11
+ * `<persistentDir>/short-codes.sqlite` so the same lookups survive
12
+ * restart. better-sqlite3 is dynamic-imported (same pattern as
13
+ * `@ggui-ai/mcp-server::resolveStorageFromConfig`) so consumers that
14
+ * stay ephemeral don't pay the N-API binding load on boot.
15
+ *
16
+ * Caller composes the path; this module is pure construction over a
17
+ * given dir + better-sqlite3 dynamic-import, so tests can scope to a
18
+ * tmpdir.
19
+ */
20
+ import { mkdirSync } from 'node:fs';
21
+ import { join } from 'node:path';
22
+ /**
23
+ * Construct the persistent ShortCodeIndex backed by `<persistentDir>/
24
+ * short-codes.sqlite`. Creates the parent dir if missing (the persistent
25
+ * dir always exists by the time this is called — the secrets helper
26
+ * already mkdir'd it — but mkdir is idempotent and cheap).
27
+ *
28
+ * Throws if `better-sqlite3` can't be loaded (e.g. native binding
29
+ * missing for the current platform). The CLI surfaces this as a fatal
30
+ * boot error so the operator can either install the binding or pass
31
+ * `--ephemeral`.
32
+ */
33
+ export async function createPersistentShortCodeIndex(persistentDir) {
34
+ mkdirSync(persistentDir, { recursive: true });
35
+ const { SqliteShortCodeIndex } = await import('@ggui-ai/mcp-server-core/sqlite');
36
+ return new SqliteShortCodeIndex({
37
+ filename: join(persistentDir, 'short-codes.sqlite'),
38
+ });
39
+ }
@@ -0,0 +1,12 @@
1
+ import type { SessionStore, VectorStore } from '@ggui-ai/mcp-server-core';
2
+ export declare function createPersistentSessionStore(persistentDir: string): Promise<SessionStore>;
3
+ export declare function createPersistentVectorStore(persistentDir: string): Promise<VectorStore>;
4
+ /**
5
+ * Compute the auto-default `keysFile` path. Returns `undefined` when
6
+ * the caller wants ephemeral pairing (the operator already passed
7
+ * `--keys-file` explicitly, or `--ephemeral` is on). The CLI uses
8
+ * this to honour explicit operator paths while still providing a
9
+ * survives-restart default in the common case.
10
+ */
11
+ export declare function defaultKeysFile(persistentDir: string): string;
12
+ //# sourceMappingURL=persistent-stores.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistent-stores.d.ts","sourceRoot":"","sources":["../src/persistent-stores.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAElC,wBAAsB,4BAA4B,CAChD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,YAAY,CAAC,CAQvB;AAED,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,CAAC,CAQtB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE7D"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Default-on persistent stores under `<persistentDir>/`. Used when the
3
+ * operator hasn't declared `storage.sessions` / `storage.vectors` in
4
+ * `ggui.json` AND hasn't passed `--ephemeral`. Closes the SessionStore
5
+ * leg of the rehydrate problem: without this, a restart drops every
6
+ * session row even though the HMAC secret + ShortCodeIndex now persist.
7
+ *
8
+ * Override precedence (caller enforces):
9
+ * 1. Explicit `ggui.json#storage.sessions` / `.vectors` (user wins)
10
+ * 2. These defaults (operator-friendly)
11
+ * 3. In-memory (`createGguiServer`'s internal fallback) (last resort)
12
+ *
13
+ * `better-sqlite3` is dynamic-imported (same pattern as the storage
14
+ * resolver in `@ggui-ai/mcp-server`) so `--ephemeral` runs don't pay
15
+ * the N-API binding load.
16
+ */
17
+ import { mkdirSync } from 'node:fs';
18
+ import { join } from 'node:path';
19
+ export async function createPersistentSessionStore(persistentDir) {
20
+ mkdirSync(persistentDir, { recursive: true });
21
+ const { SqliteSessionStore } = await import('@ggui-ai/mcp-server-core/sqlite');
22
+ return new SqliteSessionStore({
23
+ filename: join(persistentDir, 'sessions.sqlite'),
24
+ });
25
+ }
26
+ export async function createPersistentVectorStore(persistentDir) {
27
+ mkdirSync(persistentDir, { recursive: true });
28
+ const { SqliteVectorStore } = await import('@ggui-ai/mcp-server-core/sqlite');
29
+ return new SqliteVectorStore({
30
+ filename: join(persistentDir, 'vectors.sqlite'),
31
+ });
32
+ }
33
+ /**
34
+ * Compute the auto-default `keysFile` path. Returns `undefined` when
35
+ * the caller wants ephemeral pairing (the operator already passed
36
+ * `--keys-file` explicitly, or `--ephemeral` is on). The CLI uses
37
+ * this to honour explicit operator paths while still providing a
38
+ * survives-restart default in the common case.
39
+ */
40
+ export function defaultKeysFile(persistentDir) {
41
+ return join(persistentDir, 'keys.json');
42
+ }