@wenathlan/getry 2.0.152 → 2.0.155

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wenathlan/getry",
3
- "version": "2.0.152",
3
+ "version": "2.0.155",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/wenathlan/devthink"
@@ -20,18 +20,25 @@
20
20
  "engines": {
21
21
  "bare": "^1.18.0",
22
22
  "bun": "^1.4.2",
23
+ "cargo": ">=1.85.0",
23
24
  "chrome": "^16.0.912",
24
25
  "cnpm": "^9.4.0",
25
26
  "corepack": "^0.35.0",
26
27
  "deno": "^2.9.5",
28
+ "dotnet": ">=8.0.0",
27
29
  "ecmascript": "^5.0.0",
28
30
  "firefox": ">=0.8.0",
31
+ "go": ">=1.24.0",
29
32
  "iojs": "^1.0.0",
33
+ "java": ">=17",
30
34
  "node": ">=26.13.0 <27",
31
35
  "npm": "^12.1.0",
32
36
  "pnpm": "^12.6.0",
37
+ "python": ">=3.11.0",
38
+ "rust": ">=1.85.0",
33
39
  "teleport": ">=0.2.0",
34
40
  "typescript": "^5.8.0",
41
+ "v": "^0.5.2",
35
42
  "vscode": "^1.118.0",
36
43
  "yarn": "^4.17.1"
37
44
  },
@@ -40,9 +47,165 @@
40
47
  "./thinking": {
41
48
  "types": "./thinking.ts",
42
49
  "default": "./thinking.ts"
50
+ },
51
+ "./engine": {
52
+ "types": "./engine.ts",
53
+ "import": "./engine.ts",
54
+ "default": "./engine.ts"
55
+ },
56
+ "./llm": {
57
+ "types": "./llm.ts",
58
+ "import": "./llm.ts",
59
+ "default": "./llm.ts"
60
+ },
61
+ "./streaming": {
62
+ "types": "./streaming.ts",
63
+ "import": "./streaming.ts",
64
+ "default": "./streaming.ts"
65
+ },
66
+ "./models": {
67
+ "types": "./models.ts",
68
+ "import": "./models.ts",
69
+ "default": "./models.ts"
70
+ },
71
+ "./request": {
72
+ "types": "./request.ts",
73
+ "import": "./request.ts",
74
+ "default": "./request.ts"
75
+ },
76
+ "./providers": {
77
+ "types": "./providers.ts",
78
+ "import": "./providers.ts",
79
+ "default": "./providers.ts"
80
+ },
81
+ "./antigravity": {
82
+ "types": "./antigravity.ts",
83
+ "import": "./antigravity.ts",
84
+ "default": "./antigravity.ts"
85
+ },
86
+ "./oauth": {
87
+ "types": "./oauth.ts",
88
+ "import": "./oauth.ts",
89
+ "default": "./oauth.ts"
90
+ },
91
+ "./accounts": {
92
+ "types": "./accounts.ts",
93
+ "import": "./accounts.ts",
94
+ "default": "./accounts.ts"
95
+ },
96
+ "./auth": {
97
+ "types": "./auth.ts",
98
+ "import": "./auth.ts",
99
+ "default": "./auth.ts"
100
+ },
101
+ "./server": {
102
+ "types": "./server.ts",
103
+ "import": "./server.ts",
104
+ "default": "./server.ts"
105
+ },
106
+ "./cli": {
107
+ "types": "./cli.ts",
108
+ "import": "./cli.ts",
109
+ "default": "./cli.ts"
110
+ },
111
+ "./providercli": {
112
+ "types": "./providercli.ts",
113
+ "import": "./providercli.ts",
114
+ "default": "./providercli.ts"
115
+ },
116
+ "./project": {
117
+ "types": "./project.ts",
118
+ "import": "./project.ts",
119
+ "default": "./project.ts"
120
+ },
121
+ "./quota": {
122
+ "types": "./quota.ts",
123
+ "import": "./quota.ts",
124
+ "default": "./quota.ts"
125
+ },
126
+ "./recovery": {
127
+ "types": "./recovery.ts",
128
+ "import": "./recovery.ts",
129
+ "default": "./recovery.ts"
130
+ },
131
+ "./fingerprint": {
132
+ "types": "./fingerprint.ts",
133
+ "import": "./fingerprint.ts",
134
+ "default": "./fingerprint.ts"
135
+ },
136
+ "./search": {
137
+ "types": "./search.ts",
138
+ "import": "./search.ts",
139
+ "default": "./search.ts"
140
+ },
141
+ "./utils": {
142
+ "types": "./utils.ts",
143
+ "import": "./utils.ts",
144
+ "default": "./utils.ts"
145
+ },
146
+ "./versionregistry": {
147
+ "types": "./versionregistry.ts",
148
+ "import": "./versionregistry.ts",
149
+ "default": "./versionregistry.ts"
150
+ },
151
+ "./intelligence": {
152
+ "types": "./intelligence.ts",
153
+ "import": "./intelligence.ts",
154
+ "default": "./intelligence.ts"
155
+ },
156
+ "./json": {
157
+ "types": "./json.ts",
158
+ "import": "./json.ts",
159
+ "default": "./json.ts"
160
+ },
161
+ "./jsonpolicy": {
162
+ "types": "./jsonpolicy.ts",
163
+ "import": "./jsonpolicy.ts",
164
+ "default": "./jsonpolicy.ts"
165
+ },
166
+ "./jsonschema": {
167
+ "types": "./jsonschema.ts",
168
+ "import": "./jsonschema.ts",
169
+ "default": "./jsonschema.ts"
170
+ },
171
+ "./gatewayconfig": {
172
+ "types": "./gatewayconfig.ts",
173
+ "import": "./gatewayconfig.ts",
174
+ "default": "./gatewayconfig.ts"
175
+ },
176
+ "./gatewayviewconfig": {
177
+ "types": "./gatewayviewconfig.ts",
178
+ "import": "./gatewayviewconfig.ts",
179
+ "default": "./gatewayviewconfig.ts"
180
+ },
181
+ "./gatewayprobe": {
182
+ "types": "./gatewayprobe.ts",
183
+ "import": "./gatewayprobe.ts",
184
+ "default": "./gatewayprobe.ts"
185
+ },
186
+ "./gatewaysmoke": {
187
+ "types": "./gatewaysmoke.ts",
188
+ "import": "./gatewaysmoke.ts",
189
+ "default": "./gatewaysmoke.ts"
190
+ },
191
+ "./nodeshim": {
192
+ "types": "./nodeshim.ts",
193
+ "import": "./nodeshim.ts",
194
+ "default": "./nodeshim.ts"
195
+ },
196
+ "./types": {
197
+ "types": "./types.ts",
198
+ "import": "./types.ts",
199
+ "default": "./types.ts"
200
+ },
201
+ "./constants": {
202
+ "types": "./constants.ts",
203
+ "import": "./constants.ts",
204
+ "default": "./constants.ts"
43
205
  }
44
206
  },
45
207
  "scripts": {
208
+ "db:generate": "prisma generate --schema schema.prisma",
46
209
  "dev": "vite --host",
47
210
  "build": "vite build",
48
211
  "start": "vite preview --host",
@@ -55,21 +218,31 @@
55
218
  "dependencies": {
56
219
  "@fluentui/react-icons": "^2.0.345",
57
220
  "@fontsource-variable/bricolage-grotesque": "^5.3.0",
221
+ "@fontsource-variable/geist-mono": "^5.3.0",
222
+ "@fontsource-variable/inter": "^5.3.0",
58
223
  "@fontsource-variable/jetbrains-mono": "^5.3.0",
59
224
  "@fontsource-variable/outfit": "^5.3.0",
60
225
  "@hono/node-server": "^2.1.5",
61
226
  "@libsql/client": "^0.18.0",
62
227
  "@prisma/adapter-libsql": "^7.10.1",
63
228
  "@prisma/client": "^7.10.1",
64
- "@wenathlan/saddle": "^2.1.6",
65
- "@wenathlan/vault": "^2.0.151",
66
- "hono": "^4.13.14",
229
+ "@wenathlan/cadria": "^2.0.154",
230
+ "@wenathlan/debonair": "^2.0.154",
231
+ "@wenathlan/devthink": "^2.0.154",
232
+ "@wenathlan/getry": "^2.0.154",
233
+ "@wenathlan/saddle": "^2.0.154",
234
+ "@wenathlan/vault": "^2.0.154",
235
+ "better-sqlite3": "^13.0.3",
236
+ "drizzle-orm": "^0.45.4",
237
+ "gsap": "^3.15.0",
238
+ "hono": "^4.13.16",
67
239
  "ink": "^7.1.1",
68
- "lucide-react": "^1.66.0",
240
+ "lucide-react": "^1.67.0",
69
241
  "motion": "^12.43.0",
70
242
  "react": "^19.3.1",
71
243
  "react-devtools-core": "^8.0.0",
72
244
  "react-dom": "^19.3.1",
245
+ "three": "^0.187.0",
73
246
  "wouter": "^3.13.0",
74
247
  "z-ai-web-dev-sdk": "^0.0.18"
75
248
  },
@@ -110,9 +283,9 @@
110
283
  "@radix-ui/react-toggle": "^1.2.1",
111
284
  "@radix-ui/react-toggle-group": "^1.2.1",
112
285
  "@radix-ui/react-tooltip": "^1.4.1",
113
- "@tailwindcss/postcss": "^4.3.3",
286
+ "@tailwindcss/postcss": "^4.3.4",
114
287
  "@tailwindcss/typography": "^0.5.20",
115
- "@tailwindcss/vite": "^4.3.3",
288
+ "@tailwindcss/vite": "^4.3.4",
116
289
  "@tsconfig/node26": "^26.0.1",
117
290
  "@types/better-sqlite3": "^9.6.0",
118
291
  "@types/chrome": "^0.3.7",
@@ -121,7 +294,8 @@
121
294
  "@types/node": "^26.6.6",
122
295
  "@types/react": "^19.3.0",
123
296
  "@types/react-dom": "^19.3.0",
124
- "@vitejs/plugin-react": "^6.1.2",
297
+ "@types/three": "^0.187.0",
298
+ "@vitejs/plugin-react": "^6.2.1",
125
299
  "add": "^2.0.9",
126
300
  "autoprefixer": "^10.6.2",
127
301
  "axios": "^1.21.0",
@@ -139,10 +313,10 @@
139
313
  "express": "^5.3.0",
140
314
  "fast-check": "^4.10.2",
141
315
  "framer-motion": "^13.5.1",
142
- "hono": "^4.13.14",
316
+ "hono": "^4.13.16",
143
317
  "input-otp": "^1.5.0",
144
318
  "jsdom": "26.1.0",
145
- "lucide-react": "^1.66.0",
319
+ "lucide-react": "^1.67.0",
146
320
  "mysql2": "^3.24.6",
147
321
  "nanoid": "^6.0.2",
148
322
  "next-themes": "^0.4.6",
@@ -157,12 +331,12 @@
157
331
  "react-day-picker": "^10.0.2",
158
332
  "react-dom": "^19.3.1",
159
333
  "react-hook-form": "^7.91.0",
160
- "react-resizable-panels": "^4.15.3",
334
+ "react-resizable-panels": "^4.18.0",
161
335
  "recharts": "^3.10.1",
162
336
  "sonner": "^2.0.8",
163
337
  "streamdown": "^2.7.1",
164
338
  "tailwind-merge": "^3.7.0",
165
- "tailwindcss": "^4.3.3",
339
+ "tailwindcss": "^4.3.4",
166
340
  "tailwindcss-animate": "^1.0.7",
167
341
  "tsup": "^8.5.1",
168
342
  "tsx": "^4.23.15",
@@ -171,7 +345,9 @@
171
345
  "vaul": "^1.1.2",
172
346
  "vite": "^8.3.5",
173
347
  "vite-plugin-manus-runtime": "^0.0.59",
174
- "vitest": "^5.0.5",
348
+ "vitest": "^5.1.0",
349
+ "webpack": "^5.112.2",
350
+ "webpack-cli": "^6.0.1",
175
351
  "wouter": "^3.13.0",
176
352
  "yaml": "^2.9.1",
177
353
  "zod": "^4.6.7"
@@ -0,0 +1,300 @@
1
+ /**
2
+ * cli — the scaffolding cli under real subprocess execution (the getry copy
3
+ * of the devthink lane): every command runs as a child process exactly like
4
+ * a consumer types it: init with piped answers, list show validate models
5
+ * keys export help. the runner spawns the owner face directly (tsx cli.ts)
6
+ * — the devthink entry keeps answering the same family through its compat
7
+ * shim, and that lane stays covered by the devthink battery.
8
+ */
9
+
10
+ import { execFile } from "node:child_process";
11
+ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { promisify } from "node:util";
15
+ import { afterAll, describe, expect, it } from "vitest";
16
+
17
+ const exec = promisify(execFile);
18
+
19
+ /** the tests village sits inside the getry app folder, so the cli runner
20
+ * and its tsx binary resolve through the app root one hop up (the owner
21
+ * face answers from its own root the same way). */
22
+ const tsxbin = join(import.meta.dirname, "..", "node_modules", ".bin", "tsx");
23
+ const clits = join(import.meta.dirname, "..", "cli.ts");
24
+ /** the tsx subprocess resolves the library specifiers through the explicit
25
+ * application tsconfig (the runner cwd sits in a scaffold workdir, and the
26
+ * tsconfig discovery that walks from the cwd would never find the app
27
+ * paths — the flag pins the resolution to the owner tree). */
28
+ const tsconfigpath = join(import.meta.dirname, "..", "tsconfig.json");
29
+
30
+ /** run the cli in a workdir with optional piped stdin */
31
+ async function runcli(
32
+ workdir: string,
33
+ args: string[],
34
+ input = "",
35
+ ): Promise<{ stdout: string; stderr: string; code: number }> {
36
+ try {
37
+ const { stdout, stderr } = await exec(tsxbin, ["--tsconfig", tsconfigpath, clits, ...args], {
38
+ cwd: workdir,
39
+ env: { ...process.env, DEVTHINK_DATABASE_URL: `file:${join(workdir, "cli.db")}` },
40
+ maxBuffer: 10 * 1024 * 1024,
41
+ });
42
+ if (input) {
43
+ // promisified execFile does not feed stdin — handled by the caller via runcliwithinput
44
+ }
45
+ return { stdout, stderr, code: 0 };
46
+ } catch (err) {
47
+ const e = err as { stdout?: string; stderr?: string; code?: number };
48
+ return { stdout: e.stdout ?? "", stderr: e.stderr ?? "", code: e.code ?? 1 };
49
+ }
50
+ }
51
+
52
+ /** run the cli feeding piped stdin answers (the automation consumer flow) */
53
+ async function runcliwithinput(
54
+ workdir: string,
55
+ args: string[],
56
+ input: string,
57
+ ): Promise<{ stdout: string; stderr: string; code: number }> {
58
+ return new Promise((resolve) => {
59
+ const child = execFile(tsxbin, ["--tsconfig", tsconfigpath, clits, ...args], {
60
+ cwd: workdir,
61
+ env: { ...process.env, DEVTHINK_DATABASE_URL: `file:${join(workdir, "cli.db")}` },
62
+ maxBuffer: 10 * 1024 * 1024,
63
+ });
64
+ child.stdin?.write(input);
65
+ child.stdin?.end();
66
+ let stdout = "";
67
+ let stderr = "";
68
+ child.stdout?.on("data", (d: Buffer) => {
69
+ stdout += d.toString();
70
+ });
71
+ child.stderr?.on("data", (d: Buffer) => {
72
+ stderr += d.toString();
73
+ });
74
+ child.on("close", (code) => resolve({ stdout, stderr, code: code ?? 0 }));
75
+ });
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // workspace — one temp dir hosts the whole consumer journey
80
+ // ---------------------------------------------------------------------------
81
+
82
+ const workdir = mkdtempSync(join(tmpdir(), "gateway-cli-"));
83
+
84
+ afterAll(() => {
85
+ rmSync(workdir, { recursive: true, force: true });
86
+ });
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // help and unknown commands
90
+ // ---------------------------------------------------------------------------
91
+
92
+ describe("cli help", () => {
93
+ it("prints the usage banner with every command", async () => {
94
+ const { stdout, code } = await runcli(workdir, ["help"]);
95
+ expect(code).toBe(0);
96
+ expect(stdout).toContain("@wenathlan/devthink gateway");
97
+ expect(stdout).toContain("Usage: devthink gateway <command>");
98
+ for (const cmd of ["init", "add", "list", "show", "validate", "keys", "models", "serve", "export"]) {
99
+ expect(stdout).toContain(cmd);
100
+ }
101
+ });
102
+
103
+ it("bare invocation prints help", async () => {
104
+ const { stdout, code } = await runcli(workdir, []);
105
+ expect(code).toBe(0);
106
+ expect(stdout).toContain("Usage: devthink gateway <command>");
107
+ });
108
+
109
+ it("unknown commands fail with the usage", async () => {
110
+ const { stdout, code } = await runcli(workdir, ["definitely-not-a-command"]);
111
+ expect(code).toBe(1);
112
+ expect(stdout).toContain("unknown command");
113
+ });
114
+ });
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // init — the piped automation consumer flow
118
+ // ---------------------------------------------------------------------------
119
+
120
+ describe("cli init", () => {
121
+ it("scaffolds the full project from piped default answers", async () => {
122
+ // 30 blank lines cover every question — each falls back to its default
123
+ const { stdout, code } = await runcliwithinput(workdir, ["init"], "\n".repeat(30));
124
+ expect(code).toBe(0);
125
+ expect(stdout).toContain("init");
126
+ expect(existsSync(join(workdir, "config.mjs"))).toBe(true);
127
+ expect(existsSync(join(workdir, "schema.prisma"))).toBe(true);
128
+ expect(existsSync(join(workdir, "prismaconfig.ts"))).toBe(true);
129
+ expect(existsSync(join(workdir, ".env.example"))).toBe(true);
130
+ expect(stdout).toContain("config.mjs");
131
+ expect(stdout).toContain("schema.prisma");
132
+ expect(stdout).toContain("prismaconfig.ts");
133
+ });
134
+
135
+ it("the scaffold is .mjs — loadable by plain node in a typeless package", () => {
136
+ const cfg = readFileSync(join(workdir, "config.mjs"), "utf8");
137
+ expect(cfg).toContain("export const config");
138
+ });
139
+
140
+ it("the generated config carries the version and meta model", () => {
141
+ const cfg = readFileSync(join(workdir, "config.mjs"), "utf8");
142
+ expect(cfg).toContain("v1");
143
+ expect(cfg).toContain("devthink");
144
+ expect(cfg).toContain("custom");
145
+ expect(cfg).toContain("rotation");
146
+ expect(cfg).toContain("bearer");
147
+ });
148
+
149
+ it("the generated schema defines the chat message and key models", () => {
150
+ const schema = readFileSync(join(workdir, "schema.prisma"), "utf8");
151
+ expect(schema).toContain("model ChatMessage");
152
+ expect(schema).toContain("model ApiKey");
153
+ expect(schema).toContain("model NvidiaKey");
154
+ expect(schema).toContain("datasource db");
155
+ });
156
+
157
+ it("the prisma config resolves the schema and datasource url", () => {
158
+ const prismaconfig = readFileSync(join(workdir, "prismaconfig.ts"), "utf8");
159
+ expect(prismaconfig).toContain("schema.prisma");
160
+ expect(prismaconfig).toContain("DEVTHINK_DATABASE_URL");
161
+ });
162
+
163
+ it("the env example lists the derived env var", () => {
164
+ const env = readFileSync(join(workdir, ".env.example"), "utf8");
165
+ expect(env).toContain("CUSTOM_API_KEY=");
166
+ });
167
+
168
+ it("init refuses to overwrite without a yes answer", async () => {
169
+ const before = readFileSync(join(workdir, "config.mjs"), "utf8");
170
+ const { stdout, code } = await runcliwithinput(workdir, ["init"], "n\n");
171
+ expect(code).toBe(0);
172
+ expect(stdout).toContain("keeping existing config");
173
+ expect(readFileSync(join(workdir, "config.mjs"), "utf8")).toBe(before);
174
+ });
175
+ });
176
+
177
+ // ---------------------------------------------------------------------------
178
+ // list show validate models — reading the scaffolded config
179
+ // ---------------------------------------------------------------------------
180
+
181
+ describe("cli list show validate models", () => {
182
+ it("list shows the version summary", async () => {
183
+ const { stdout, code } = await runcli(workdir, ["list"]);
184
+ expect(code).toBe(0);
185
+ expect(stdout).toContain("1 versions");
186
+ expect(stdout).toContain("v1");
187
+ expect(stdout).toContain("custom");
188
+ });
189
+
190
+ it("show prints one version detail", async () => {
191
+ const { stdout, code } = await runcli(workdir, ["show", "v1"]);
192
+ expect(code).toBe(0);
193
+ expect(stdout).toContain("providername");
194
+ expect(stdout).toContain("upstreams");
195
+ });
196
+
197
+ it("show of an unknown version fails", async () => {
198
+ const { code } = await runcli(workdir, ["show", "nope"]);
199
+ expect(code).toBe(1);
200
+ });
201
+
202
+ it("validate reports the scaffolded config valid", async () => {
203
+ const { stdout, code } = await runcli(workdir, ["validate"]);
204
+ expect(code).toBe(0);
205
+ expect(stdout).toContain("config is valid");
206
+ });
207
+
208
+ it("models lists the catalog and rotation pool", async () => {
209
+ const { stdout, code } = await runcli(workdir, ["models", "v1"]);
210
+ expect(code).toBe(0);
211
+ expect(stdout).toContain("models for v1");
212
+ expect(stdout).toContain("default-model");
213
+ expect(stdout).toContain("rotation pool");
214
+ });
215
+ });
216
+
217
+ // ---------------------------------------------------------------------------
218
+ // keys — env instructions and the db seed script
219
+ // ---------------------------------------------------------------------------
220
+
221
+ describe("cli keys", () => {
222
+ it("without a db model it documents the env var", async () => {
223
+ const { stdout, code } = await runcli(workdir, ["keys", "v1"]);
224
+ expect(code).toBe(0);
225
+ expect(stdout).toContain("CUSTOM_API_KEY");
226
+ expect(stdout).toContain("comma separated");
227
+ });
228
+
229
+ it("keys of an unknown version fails", async () => {
230
+ const { code } = await runcli(workdir, ["keys", "nope"]);
231
+ expect(code).toBe(1);
232
+ });
233
+ });
234
+
235
+ // ---------------------------------------------------------------------------
236
+ // export — the deployable scaffold
237
+ // ---------------------------------------------------------------------------
238
+
239
+ describe("cli export", () => {
240
+ it("exports the config and schema for deploy", async () => {
241
+ const { stdout, code } = await runcli(workdir, ["export", "--dir", "export"]);
242
+ expect(code).toBe(0);
243
+ expect(stdout).toContain("exported config.mjs");
244
+ expect(stdout).toContain("exported schema.prisma");
245
+ expect(existsSync(join(workdir, "export", "config.mjs"))).toBe(true);
246
+ expect(existsSync(join(workdir, "export", "schema.prisma"))).toBe(true);
247
+ });
248
+ });
249
+
250
+ // ---------------------------------------------------------------------------
251
+ // db-backed keys — a second workspace with db key storage enabled
252
+ // ---------------------------------------------------------------------------
253
+
254
+ describe("cli init with db keys", () => {
255
+ const dbworkdir = mkdtempSync(join(tmpdir(), "gateway-cli-db-"));
256
+
257
+ it("scaffolds with database key storage and generates the seed script", async () => {
258
+ const answers = [
259
+ "1", // versions
260
+ "", // id → v1
261
+ "", // provider → custom
262
+ "", // baseurl → default
263
+ "", // auth → bearer
264
+ "", // required → default true
265
+ "", // envvar → CUSTOM_API_KEY
266
+ "y", // store keys in database
267
+ "apiKey", // db model
268
+ "1", // one model
269
+ "", // model id default
270
+ "", // context
271
+ "", // maxoutput
272
+ "n", // no rotation (single model)
273
+ "y", // retry
274
+ "", // max retries
275
+ "", // backoff base
276
+ "", // backoff cap
277
+ "n", // no shared context
278
+ "", // meta id → devthink
279
+ "", // gateway name
280
+ "", // description
281
+ ].join("\n");
282
+ const { code } = await runcliwithinput(dbworkdir, ["init"], `${answers}\n`);
283
+ expect(code).toBe(0);
284
+
285
+ const { stdout, code: keyscode } = await runcliwithinput(
286
+ dbworkdir,
287
+ ["keys", "v1"],
288
+ "2\nkey-aaaaaaaaaa\nkey-bbbbbbbbbb\n",
289
+ );
290
+ expect(keyscode).toBe(0);
291
+ expect(stdout).toContain("register keys");
292
+ const seed = join(dbworkdir, "tests", "seedkeys.mjs");
293
+ expect(existsSync(seed)).toBe(true);
294
+ const content = readFileSync(seed, "utf8");
295
+ expect(content).toContain("key-aaaaaaaaaa");
296
+ expect(content).toContain("key-bbbbbbbbbb");
297
+ expect(content).toContain("apiKey");
298
+ rmSync(dbworkdir, { recursive: true, force: true });
299
+ });
300
+ });