@zucker-framework/cli 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-O6YSETKJ.mjs +6 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.js +2 -2
- package/dist/index.mjs +828 -0
- package/dist/release-catalog.json +59 -59
- package/dist/tooling.d.mts +53 -0
- package/dist/tooling.d.ts +53 -0
- package/dist/tooling.js +554 -0
- package/dist/tooling.mjs +516 -0
- package/package.json +28 -7
- package/tooling/agent-instructions.d.mts +25 -0
- package/tooling/agent-instructions.mjs +145 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,828 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
__name
|
|
4
|
+
} from "./chunk-O6YSETKJ.mjs";
|
|
5
|
+
|
|
6
|
+
// src/bin.ts
|
|
7
|
+
import "dotenv/config";
|
|
8
|
+
import { Command as Command6 } from "commander";
|
|
9
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
10
|
+
import { resolve as resolve5 } from "path";
|
|
11
|
+
|
|
12
|
+
// src/commands/doctor.ts
|
|
13
|
+
import { Command } from "commander";
|
|
14
|
+
import { existsSync, readFileSync } from "fs";
|
|
15
|
+
import { resolve } from "path";
|
|
16
|
+
async function runDiagnostics() {
|
|
17
|
+
const results = [];
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
const pkgPath = resolve(cwd, "package.json");
|
|
20
|
+
if (!existsSync(pkgPath)) {
|
|
21
|
+
results.push({
|
|
22
|
+
name: "package.json",
|
|
23
|
+
status: "error",
|
|
24
|
+
message: "package.json not found. Are you in a Zucker project directory?"
|
|
25
|
+
});
|
|
26
|
+
return results;
|
|
27
|
+
}
|
|
28
|
+
results.push({
|
|
29
|
+
name: "package.json",
|
|
30
|
+
status: "ok",
|
|
31
|
+
message: "Found"
|
|
32
|
+
});
|
|
33
|
+
const nodeVersion = process.versions.node;
|
|
34
|
+
const major = parseInt(nodeVersion.split(".")[0], 10);
|
|
35
|
+
if (major < 20) {
|
|
36
|
+
results.push({
|
|
37
|
+
name: "Node.js",
|
|
38
|
+
status: "error",
|
|
39
|
+
message: `v${nodeVersion} detected. Zucker requires Node.js >= 20.`
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
results.push({
|
|
43
|
+
name: "Node.js",
|
|
44
|
+
status: "ok",
|
|
45
|
+
message: `v${nodeVersion}`
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
let pkg = {};
|
|
49
|
+
try {
|
|
50
|
+
pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
51
|
+
} catch {
|
|
52
|
+
results.push({
|
|
53
|
+
name: "package.json",
|
|
54
|
+
status: "error",
|
|
55
|
+
message: "Failed to parse package.json"
|
|
56
|
+
});
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
const deps = {
|
|
60
|
+
...pkg.dependencies,
|
|
61
|
+
...pkg.devDependencies
|
|
62
|
+
};
|
|
63
|
+
const requiredPkgs = [
|
|
64
|
+
"@zucker-framework/core",
|
|
65
|
+
"@zucker-framework/crud",
|
|
66
|
+
"@zucker-framework/common"
|
|
67
|
+
];
|
|
68
|
+
for (const name of requiredPkgs) {
|
|
69
|
+
if (deps[name]) {
|
|
70
|
+
results.push({
|
|
71
|
+
name,
|
|
72
|
+
status: "ok",
|
|
73
|
+
message: deps[name]
|
|
74
|
+
});
|
|
75
|
+
} else {
|
|
76
|
+
results.push({
|
|
77
|
+
name,
|
|
78
|
+
status: "warn",
|
|
79
|
+
message: "Not installed"
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const envChecks = [
|
|
84
|
+
{
|
|
85
|
+
name: "JWT_SECRET",
|
|
86
|
+
envVar: "JWT_SECRET",
|
|
87
|
+
required: true
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "DATABASE_URL",
|
|
91
|
+
envVar: "DATABASE_URL",
|
|
92
|
+
required: false
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
for (const check of envChecks) {
|
|
96
|
+
const value = process.env[check.envVar];
|
|
97
|
+
if (value) {
|
|
98
|
+
results.push({
|
|
99
|
+
name: check.name,
|
|
100
|
+
status: "ok",
|
|
101
|
+
message: `Set (${value.substring(0, 8)}...)`
|
|
102
|
+
});
|
|
103
|
+
} else if (check.required) {
|
|
104
|
+
results.push({
|
|
105
|
+
name: check.name,
|
|
106
|
+
status: "warn",
|
|
107
|
+
message: "Not set. Required for production."
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
results.push({
|
|
111
|
+
name: check.name,
|
|
112
|
+
status: "ok",
|
|
113
|
+
message: "Not set (optional \u2014 will use InMemoryAdapter)"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const envLocalPath = resolve(cwd, ".env.local");
|
|
118
|
+
if (existsSync(envLocalPath)) {
|
|
119
|
+
results.push({
|
|
120
|
+
name: ".env.local",
|
|
121
|
+
status: "ok",
|
|
122
|
+
message: "Found"
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
results.push({
|
|
126
|
+
name: ".env.local",
|
|
127
|
+
status: "warn",
|
|
128
|
+
message: "Not found. Consider creating one for local development."
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const tsconfigPath = resolve(cwd, "tsconfig.json");
|
|
132
|
+
if (existsSync(tsconfigPath)) {
|
|
133
|
+
results.push({
|
|
134
|
+
name: "tsconfig.json",
|
|
135
|
+
status: "ok",
|
|
136
|
+
message: "Found"
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
results.push({
|
|
140
|
+
name: "tsconfig.json",
|
|
141
|
+
status: "error",
|
|
142
|
+
message: "Not found"
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return results;
|
|
146
|
+
}
|
|
147
|
+
__name(runDiagnostics, "runDiagnostics");
|
|
148
|
+
function formatStatus(status) {
|
|
149
|
+
switch (status) {
|
|
150
|
+
case "ok":
|
|
151
|
+
return "\x1B[32m[OK]\x1B[0m";
|
|
152
|
+
case "warn":
|
|
153
|
+
return "\x1B[33m[WARN]\x1B[0m";
|
|
154
|
+
case "error":
|
|
155
|
+
return "\x1B[31m[ERROR]\x1B[0m";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
__name(formatStatus, "formatStatus");
|
|
159
|
+
var doctorCommand = new Command("doctor").description("Check project health and configuration").action(async () => {
|
|
160
|
+
console.log("\n Zucker Doctor\n");
|
|
161
|
+
console.log(" Running diagnostics...\n");
|
|
162
|
+
const results = await runDiagnostics();
|
|
163
|
+
const maxNameLen = Math.max(...results.map((r) => r.name.length));
|
|
164
|
+
for (const result of results) {
|
|
165
|
+
const padding = " ".repeat(maxNameLen - result.name.length);
|
|
166
|
+
console.log(` ${formatStatus(result.status)} ${result.name}${padding} ${result.message}`);
|
|
167
|
+
}
|
|
168
|
+
const errors = results.filter((r) => r.status === "error");
|
|
169
|
+
const warnings = results.filter((r) => r.status === "warn");
|
|
170
|
+
console.log("");
|
|
171
|
+
if (errors.length > 0) {
|
|
172
|
+
console.log(` \x1B[31m${errors.length} error(s)\x1B[0m found. Please fix them before proceeding.`);
|
|
173
|
+
} else if (warnings.length > 0) {
|
|
174
|
+
console.log(` \x1B[33m${warnings.length} warning(s)\x1B[0m found. Review them for production readiness.`);
|
|
175
|
+
} else {
|
|
176
|
+
console.log(" \x1B[32mAll checks passed!\x1B[0m");
|
|
177
|
+
}
|
|
178
|
+
console.log("");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// src/commands/init.ts
|
|
182
|
+
import { Command as Command2 } from "commander";
|
|
183
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync, readFileSync as readFileSync2, cpSync } from "fs";
|
|
184
|
+
import { resolve as resolve2 } from "path";
|
|
185
|
+
import { randomBytes } from "crypto";
|
|
186
|
+
function readReleaseCatalog() {
|
|
187
|
+
const path = resolve2(__dirname, "release-catalog.json");
|
|
188
|
+
if (!existsSync2(path)) throw new Error("CLI release catalog is missing; build or reinstall the CLI package.");
|
|
189
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
190
|
+
}
|
|
191
|
+
__name(readReleaseCatalog, "readReleaseCatalog");
|
|
192
|
+
var initCommand = new Command2("init").description("Initialize a new Zucker project").argument("[name]", "Project name", "zucker-app").option("--template <template>", "Template to use (server, admin, web)", "server").action(async (name, options) => {
|
|
193
|
+
if (![
|
|
194
|
+
"server",
|
|
195
|
+
"admin",
|
|
196
|
+
"web"
|
|
197
|
+
].includes(options.template)) {
|
|
198
|
+
throw new Error(`Unsupported template: ${options.template}`);
|
|
199
|
+
}
|
|
200
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(name)) throw new Error("Project name must use lowercase letters, digits, dots, underscores or hyphens.");
|
|
201
|
+
const catalog = readReleaseCatalog();
|
|
202
|
+
const targetDir = resolve2(process.cwd(), name);
|
|
203
|
+
console.log(`
|
|
204
|
+
Initializing Zucker project: ${name}`);
|
|
205
|
+
console.log(` Template: ${options.template}`);
|
|
206
|
+
console.log(` Directory: ${targetDir}
|
|
207
|
+
`);
|
|
208
|
+
if (existsSync2(targetDir)) {
|
|
209
|
+
console.log(` \x1B[31mError:\x1B[0m Directory "${name}" already exists.`);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
const monorepoTemplate = resolve2(__dirname, "..", "..", "..", "templates", options.template);
|
|
213
|
+
const templateDir2 = existsSync2(monorepoTemplate) ? monorepoTemplate : null;
|
|
214
|
+
if (!templateDir2) {
|
|
215
|
+
if (options.template !== "server") {
|
|
216
|
+
throw new Error(`The ${options.template} template requires a framework checkout. The installed CLI includes the standalone server scaffold.`);
|
|
217
|
+
}
|
|
218
|
+
console.log(` \x1B[33mNote:\x1B[0m Template "${options.template}" not found locally.`);
|
|
219
|
+
console.log(" Generating minimal project scaffold instead.\n");
|
|
220
|
+
mkdirSync(resolve2(targetDir, "src"), {
|
|
221
|
+
recursive: true
|
|
222
|
+
});
|
|
223
|
+
const pkg = {
|
|
224
|
+
name,
|
|
225
|
+
version: "0.1.0",
|
|
226
|
+
private: true,
|
|
227
|
+
type: "commonjs",
|
|
228
|
+
engines: {
|
|
229
|
+
node: ">=22.12.0"
|
|
230
|
+
},
|
|
231
|
+
scripts: {
|
|
232
|
+
dev: "nest start --watch",
|
|
233
|
+
build: "nest build",
|
|
234
|
+
start: "node dist/main.js"
|
|
235
|
+
},
|
|
236
|
+
dependencies: catalog.server.dependencies,
|
|
237
|
+
devDependencies: catalog.server.devDependencies
|
|
238
|
+
};
|
|
239
|
+
writeFileSync(resolve2(targetDir, "package.json"), JSON.stringify(pkg, null, 2) + "\n");
|
|
240
|
+
const tsconfig = {
|
|
241
|
+
compilerOptions: {
|
|
242
|
+
module: "NodeNext",
|
|
243
|
+
moduleResolution: "NodeNext",
|
|
244
|
+
target: "ES2022",
|
|
245
|
+
strict: true,
|
|
246
|
+
esModuleInterop: true,
|
|
247
|
+
skipLibCheck: true,
|
|
248
|
+
experimentalDecorators: true,
|
|
249
|
+
emitDecoratorMetadata: true,
|
|
250
|
+
sourceMap: false,
|
|
251
|
+
declarationMap: false,
|
|
252
|
+
outDir: "dist",
|
|
253
|
+
rootDir: "src"
|
|
254
|
+
},
|
|
255
|
+
include: [
|
|
256
|
+
"src"
|
|
257
|
+
]
|
|
258
|
+
};
|
|
259
|
+
writeFileSync(resolve2(targetDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n");
|
|
260
|
+
writeFileSync(resolve2(targetDir, "nest-cli.json"), JSON.stringify({
|
|
261
|
+
sourceRoot: "src",
|
|
262
|
+
compilerOptions: {
|
|
263
|
+
deleteOutDir: true
|
|
264
|
+
}
|
|
265
|
+
}, null, 2) + "\n");
|
|
266
|
+
writeFileSync(resolve2(targetDir, ".gitignore"), "node_modules/\ndist/\n.env\n.env.local\n");
|
|
267
|
+
writeFileSync(resolve2(targetDir, ".env"), `JWT_SECRET=${randomBytes(32).toString("hex")}
|
|
268
|
+
`, {
|
|
269
|
+
mode: 384
|
|
270
|
+
});
|
|
271
|
+
writeFileSync(resolve2(targetDir, ".env.example"), [
|
|
272
|
+
"# JWT secret \u2014 required for production",
|
|
273
|
+
"# JWT_SECRET=your-secure-secret-here",
|
|
274
|
+
"",
|
|
275
|
+
"# Database URL \u2014 leave unset to use InMemoryAdapter",
|
|
276
|
+
"# DATABASE_URL=postgresql://user:pass@localhost:5432/zucker",
|
|
277
|
+
""
|
|
278
|
+
].join("\n"));
|
|
279
|
+
writeFileSync(resolve2(targetDir, "src", "main.ts"), [
|
|
280
|
+
"import 'dotenv/config';",
|
|
281
|
+
"import { NestFactory } from '@nestjs/core';",
|
|
282
|
+
"import { ValidationPipe, Logger } from '@nestjs/common';",
|
|
283
|
+
"import { HttpExceptionFilter } from '@zucker-framework/common';",
|
|
284
|
+
"import { AppModule } from './app.module';",
|
|
285
|
+
"",
|
|
286
|
+
"async function bootstrap() {",
|
|
287
|
+
" const startTime = performance.now();",
|
|
288
|
+
" const app = await NestFactory.create(AppModule);",
|
|
289
|
+
"",
|
|
290
|
+
" app.setGlobalPrefix('api');",
|
|
291
|
+
" app.useGlobalFilters(new HttpExceptionFilter());",
|
|
292
|
+
" app.useGlobalPipes(new ValidationPipe({ transform: true }));",
|
|
293
|
+
"",
|
|
294
|
+
" const port = process.env.PORT || 4200;",
|
|
295
|
+
" await app.listen(port);",
|
|
296
|
+
"",
|
|
297
|
+
" const elapsed = (performance.now() - startTime).toFixed(0);",
|
|
298
|
+
" Logger.log(`Server ready on http://localhost:${port} (${elapsed}ms)`, 'Bootstrap');",
|
|
299
|
+
"}",
|
|
300
|
+
"",
|
|
301
|
+
"bootstrap();",
|
|
302
|
+
""
|
|
303
|
+
].join("\n"));
|
|
304
|
+
writeFileSync(resolve2(targetDir, "src", "app.module.ts"), [
|
|
305
|
+
"import { Module } from '@nestjs/common';",
|
|
306
|
+
"import { EventEmitterModule } from '@nestjs/event-emitter';",
|
|
307
|
+
"import { ZuckerEventModule } from '@zucker-framework/event';",
|
|
308
|
+
"import { ZuckerCommonModule } from '@zucker-framework/common';",
|
|
309
|
+
"import { ZuckerCrudModule, InMemoryAdapter } from '@zucker-framework/crud';",
|
|
310
|
+
"import { ZuckerAuthModule } from '@zucker-framework/auth';",
|
|
311
|
+
"",
|
|
312
|
+
"@Module({",
|
|
313
|
+
" imports: [",
|
|
314
|
+
" EventEmitterModule.forRoot(),",
|
|
315
|
+
" ZuckerEventModule.forRoot(),",
|
|
316
|
+
" ZuckerCommonModule,",
|
|
317
|
+
" ZuckerCrudModule.forRoot({ adapterClass: InMemoryAdapter }),",
|
|
318
|
+
" ZuckerAuthModule.forRoot({ jwtSecret: process.env.JWT_SECRET }),",
|
|
319
|
+
" ],",
|
|
320
|
+
"})",
|
|
321
|
+
"export class AppModule {}",
|
|
322
|
+
""
|
|
323
|
+
].join("\n"));
|
|
324
|
+
console.log(" \x1B[32mProject scaffolded successfully!\x1B[0m\n");
|
|
325
|
+
console.log(" Next steps:");
|
|
326
|
+
console.log(` cd ${name}`);
|
|
327
|
+
console.log(" pnpm install");
|
|
328
|
+
console.log(" pnpm dev\n");
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
console.log(` Copying template from ${templateDir2}...
|
|
332
|
+
`);
|
|
333
|
+
cpSync(templateDir2, targetDir, {
|
|
334
|
+
recursive: true,
|
|
335
|
+
filter: /* @__PURE__ */ __name((src) => !src.split(/[\\/]/).some((part) => [
|
|
336
|
+
"node_modules",
|
|
337
|
+
"dist",
|
|
338
|
+
".turbo",
|
|
339
|
+
".next",
|
|
340
|
+
".cache",
|
|
341
|
+
"generated"
|
|
342
|
+
].includes(part) || part.startsWith(".env") && part !== ".env.example" || part.endsWith(".tsbuildinfo")), "filter")
|
|
343
|
+
});
|
|
344
|
+
const copiedManifestPath = resolve2(targetDir, "package.json");
|
|
345
|
+
const copiedManifest = JSON.parse(readFileSync2(copiedManifestPath, "utf8"));
|
|
346
|
+
copiedManifest.name = name;
|
|
347
|
+
for (const field of [
|
|
348
|
+
"dependencies",
|
|
349
|
+
"devDependencies",
|
|
350
|
+
"peerDependencies",
|
|
351
|
+
"optionalDependencies"
|
|
352
|
+
]) {
|
|
353
|
+
const entries = copiedManifest[field];
|
|
354
|
+
for (const [dependency, version] of Object.entries(entries ?? {})) {
|
|
355
|
+
if (!version.startsWith("workspace:")) continue;
|
|
356
|
+
const releasedVersion = catalog.versions[dependency];
|
|
357
|
+
if (!releasedVersion) throw new Error(`No release version found for ${dependency}`);
|
|
358
|
+
entries[dependency] = releasedVersion;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
writeFileSync(copiedManifestPath, JSON.stringify(copiedManifest, null, 2) + "\n");
|
|
362
|
+
console.log(" \x1B[32mProject created successfully!\x1B[0m\n");
|
|
363
|
+
console.log(" Next steps:");
|
|
364
|
+
console.log(` cd ${name}`);
|
|
365
|
+
console.log(" pnpm install");
|
|
366
|
+
console.log(" pnpm dev\n");
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// src/commands/add.ts
|
|
370
|
+
import { Command as Command3 } from "commander";
|
|
371
|
+
import { existsSync as existsSync3 } from "fs";
|
|
372
|
+
import { resolve as resolve3 } from "path";
|
|
373
|
+
import { execSync } from "child_process";
|
|
374
|
+
var MODULE_REGISTRY = {
|
|
375
|
+
auth: {
|
|
376
|
+
pkg: "@zucker-framework/auth",
|
|
377
|
+
peerDeps: [
|
|
378
|
+
"@nestjs/passport",
|
|
379
|
+
"@nestjs/jwt"
|
|
380
|
+
],
|
|
381
|
+
moduleImport: "import { ZuckerAuthModule } from '@zucker-framework/auth';",
|
|
382
|
+
moduleSetup: "ZuckerAuthModule.forRoot({ jwtSecret: process.env.JWT_SECRET })"
|
|
383
|
+
},
|
|
384
|
+
logging: {
|
|
385
|
+
pkg: "@zucker-framework/logging",
|
|
386
|
+
moduleImport: "import { ZuckerLoggingModule } from '@zucker-framework/logging';",
|
|
387
|
+
moduleSetup: "ZuckerLoggingModule.forRoot()"
|
|
388
|
+
},
|
|
389
|
+
config: {
|
|
390
|
+
pkg: "@zucker-framework/config",
|
|
391
|
+
moduleImport: "import { ZuckerConfigModule } from '@zucker-framework/config';",
|
|
392
|
+
moduleSetup: "ZuckerConfigModule.forRoot()"
|
|
393
|
+
},
|
|
394
|
+
dictionary: {
|
|
395
|
+
pkg: "@zucker-framework/dictionary",
|
|
396
|
+
moduleImport: "import { ZuckerDictionaryModule } from '@zucker-framework/dictionary';",
|
|
397
|
+
moduleSetup: "ZuckerDictionaryModule.forRoot()"
|
|
398
|
+
},
|
|
399
|
+
cache: {
|
|
400
|
+
pkg: "@zucker-framework/cache",
|
|
401
|
+
moduleImport: "import { ZuckerCacheModule } from '@zucker-framework/cache';",
|
|
402
|
+
moduleSetup: "ZuckerCacheModule.forRoot({ provider: 'memory' })"
|
|
403
|
+
},
|
|
404
|
+
storage: {
|
|
405
|
+
pkg: "@zucker-framework/storage",
|
|
406
|
+
moduleImport: "import { ZuckerStorageModule } from '@zucker-framework/storage';",
|
|
407
|
+
moduleSetup: "ZuckerStorageModule.forRoot({ provider: 'local', basePath: './uploads' })"
|
|
408
|
+
},
|
|
409
|
+
notification: {
|
|
410
|
+
pkg: "@zucker-framework/notification",
|
|
411
|
+
moduleImport: "import { ZuckerNotificationModule } from '@zucker-framework/notification';",
|
|
412
|
+
moduleSetup: "ZuckerNotificationModule.forRoot({ providers: [] })"
|
|
413
|
+
},
|
|
414
|
+
credit: {
|
|
415
|
+
pkg: "@zucker-framework/credit",
|
|
416
|
+
moduleImport: "import { ZuckerCreditModule } from '@zucker-framework/credit';",
|
|
417
|
+
moduleSetup: "ZuckerCreditModule.forRoot()"
|
|
418
|
+
},
|
|
419
|
+
ai: {
|
|
420
|
+
pkg: "@zucker-framework/ai",
|
|
421
|
+
moduleImport: "import { ZuckerAIModule } from '@zucker-framework/ai';",
|
|
422
|
+
moduleSetup: "ZuckerAIModule.forRoot()"
|
|
423
|
+
},
|
|
424
|
+
tenant: {
|
|
425
|
+
pkg: "@zucker-framework/tenant",
|
|
426
|
+
moduleImport: "import { ZuckerTenantModule } from '@zucker-framework/tenant';",
|
|
427
|
+
moduleSetup: "ZuckerTenantModule.forRoot()"
|
|
428
|
+
},
|
|
429
|
+
form: {
|
|
430
|
+
pkg: "@zucker-framework/form",
|
|
431
|
+
moduleImport: "import { ZuckerFormModule } from '@zucker-framework/form';",
|
|
432
|
+
moduleSetup: "ZuckerFormModule.forRoot()"
|
|
433
|
+
},
|
|
434
|
+
monitoring: {
|
|
435
|
+
pkg: "@zucker-framework/monitoring",
|
|
436
|
+
moduleImport: "import { ZuckerMonitoringModule } from '@zucker-framework/monitoring';",
|
|
437
|
+
moduleSetup: "ZuckerMonitoringModule"
|
|
438
|
+
},
|
|
439
|
+
"rate-limiter": {
|
|
440
|
+
pkg: "@zucker-framework/rate-limiter",
|
|
441
|
+
moduleImport: "import { ZuckerRateLimiterModule } from '@zucker-framework/rate-limiter';",
|
|
442
|
+
moduleSetup: `ZuckerRateLimiterModule.forRoot({
|
|
443
|
+
limiters: {
|
|
444
|
+
login: { points: 5, duration: 900 },
|
|
445
|
+
'api-default': { points: 100, duration: 60 },
|
|
446
|
+
},
|
|
447
|
+
})`
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
function detectPackageManager() {
|
|
451
|
+
const cwd = process.cwd();
|
|
452
|
+
if (existsSync3(resolve3(cwd, "pnpm-lock.yaml")) || existsSync3(resolve3(cwd, "pnpm-workspace.yaml"))) {
|
|
453
|
+
return "pnpm";
|
|
454
|
+
}
|
|
455
|
+
if (existsSync3(resolve3(cwd, "yarn.lock"))) return "yarn";
|
|
456
|
+
return "npm";
|
|
457
|
+
}
|
|
458
|
+
__name(detectPackageManager, "detectPackageManager");
|
|
459
|
+
var addCommand = new Command3("add").description("Add a Zucker module to the current project").argument("<module>", `Module name (${Object.keys(MODULE_REGISTRY).join(", ")})`).option("--dry-run", "Show what would be done without making changes", false).action(async (moduleName, options) => {
|
|
460
|
+
const entry = MODULE_REGISTRY[moduleName];
|
|
461
|
+
if (!entry) {
|
|
462
|
+
console.log(`
|
|
463
|
+
\x1B[31mError:\x1B[0m Unknown module "${moduleName}".`);
|
|
464
|
+
console.log(` Available modules: ${Object.keys(MODULE_REGISTRY).join(", ")}
|
|
465
|
+
`);
|
|
466
|
+
process.exit(1);
|
|
467
|
+
}
|
|
468
|
+
console.log(`
|
|
469
|
+
Adding @zucker-framework/${moduleName} to your project...
|
|
470
|
+
`);
|
|
471
|
+
const pm = detectPackageManager();
|
|
472
|
+
const pkgs = [
|
|
473
|
+
entry.pkg,
|
|
474
|
+
...entry.peerDeps ?? []
|
|
475
|
+
];
|
|
476
|
+
const installCmd = pm === "pnpm" ? `pnpm add ${pkgs.join(" ")}` : pm === "yarn" ? `yarn add ${pkgs.join(" ")}` : `npm install ${pkgs.join(" ")}`;
|
|
477
|
+
if (options.dryRun) {
|
|
478
|
+
console.log(` [dry-run] Would run: ${installCmd}`);
|
|
479
|
+
} else {
|
|
480
|
+
console.log(` Running: ${installCmd}`);
|
|
481
|
+
try {
|
|
482
|
+
execSync(installCmd, {
|
|
483
|
+
stdio: "inherit",
|
|
484
|
+
cwd: process.cwd()
|
|
485
|
+
});
|
|
486
|
+
} catch {
|
|
487
|
+
console.log(`
|
|
488
|
+
\x1B[33mWarning:\x1B[0m Package install failed. You may need to install manually.
|
|
489
|
+
`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
console.log("\n Add the following to your AppModule:\n");
|
|
493
|
+
console.log(` \x1B[36m// Import:\x1B[0m`);
|
|
494
|
+
console.log(` ${entry.moduleImport}
|
|
495
|
+
`);
|
|
496
|
+
console.log(` \x1B[36m// In @Module imports array:\x1B[0m`);
|
|
497
|
+
console.log(` ${entry.moduleSetup},
|
|
498
|
+
`);
|
|
499
|
+
console.log(" \x1B[32mDone!\x1B[0m\n");
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
// src/commands/ext.ts
|
|
503
|
+
import { Command as Command4 } from "commander";
|
|
504
|
+
import { cpSync as cpSync2, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
505
|
+
import { join, resolve as resolve4 } from "path";
|
|
506
|
+
var monorepoRoot = resolve4(__dirname, "..", "..", "..");
|
|
507
|
+
var kitDir = join(monorepoRoot, "packages", "ext-kit");
|
|
508
|
+
var templateDir = join(monorepoRoot, "templates", "extension");
|
|
509
|
+
var VENDORED = [
|
|
510
|
+
{
|
|
511
|
+
from: join(kitDir, "dist", "ext-kit.js"),
|
|
512
|
+
to: "lib/ext-kit.js"
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
from: join(kitDir, "dist", "ext-kit-popup.js"),
|
|
516
|
+
to: "lib/ext-kit-popup.js"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
from: join(kitDir, "assets", "offscreen.html"),
|
|
520
|
+
to: "offscreen.html"
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
from: join(kitDir, "assets", "offscreen.js"),
|
|
524
|
+
to: "offscreen.js"
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
from: join(kitDir, "assets", "package.mjs"),
|
|
528
|
+
to: "scripts/package.mjs"
|
|
529
|
+
}
|
|
530
|
+
];
|
|
531
|
+
function syncVendored(extDir) {
|
|
532
|
+
const updated = [];
|
|
533
|
+
for (const file of VENDORED) {
|
|
534
|
+
if (!existsSync4(file.from)) {
|
|
535
|
+
throw new Error(`Kit artifact missing: ${file.from} \u2014 run \`pnpm --filter @zucker-framework/ext-kit build\` first.`);
|
|
536
|
+
}
|
|
537
|
+
const dest = join(extDir, file.to);
|
|
538
|
+
mkdirSync2(join(dest, ".."), {
|
|
539
|
+
recursive: true
|
|
540
|
+
});
|
|
541
|
+
const next = readFileSync3(file.from);
|
|
542
|
+
const prev = existsSync4(dest) ? readFileSync3(dest) : null;
|
|
543
|
+
if (!prev || !prev.equals(next)) {
|
|
544
|
+
writeFileSync2(dest, next);
|
|
545
|
+
updated.push(file.to);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return updated;
|
|
549
|
+
}
|
|
550
|
+
__name(syncVendored, "syncVendored");
|
|
551
|
+
function isExtensionDir(dir) {
|
|
552
|
+
return existsSync4(join(dir, "manifest.json")) && existsSync4(join(dir, "lib", "ext-kit.js"));
|
|
553
|
+
}
|
|
554
|
+
__name(isExtensionDir, "isExtensionDir");
|
|
555
|
+
var createCommand = new Command4("create").description("Scaffold a new browser extension on top of @zucker-framework/ext-kit").argument("<slug>", 'Product slug, lowercase (e.g. "sampleext") \u2014 storage keys & server product id').option("--name <name>", 'Store display name (e.g. "Sample Extension")').option("--brand <brand>", 'Brand word (e.g. "Sample") \u2014 folder prefix, wordmark').option("--desc <desc>", "Store description").option("--accent <color>", "Accent color", "#7c8cff").option("--accent-deep <color>", "Deep accent color", "#5a6cf0").option("--badge <color>", "Badge background color", "#4353d9").option("--dir <dir>", "Target directory (default: <slug>-extension)").action((slug, options) => {
|
|
556
|
+
if (!/^[a-z][a-z0-9]*$/.test(slug)) {
|
|
557
|
+
console.log(' \x1B[31mError:\x1B[0m slug must be lowercase alphanumeric, e.g. "sampleext".');
|
|
558
|
+
process.exit(1);
|
|
559
|
+
}
|
|
560
|
+
const brand = options.brand || slug.charAt(0).toUpperCase() + slug.slice(1);
|
|
561
|
+
const name = options.name || `${brand} Extension`;
|
|
562
|
+
const desc = options.desc || `${brand} browser extension.`;
|
|
563
|
+
const targetDir = resolve4(process.cwd(), options.dir || `${slug}-extension`);
|
|
564
|
+
if (existsSync4(targetDir)) {
|
|
565
|
+
console.log(` \x1B[31mError:\x1B[0m Directory already exists: ${targetDir}`);
|
|
566
|
+
process.exit(1);
|
|
567
|
+
}
|
|
568
|
+
if (!existsSync4(templateDir)) {
|
|
569
|
+
console.log(` \x1B[31mError:\x1B[0m Template not found: ${templateDir}`);
|
|
570
|
+
process.exit(1);
|
|
571
|
+
}
|
|
572
|
+
cpSync2(templateDir, targetDir, {
|
|
573
|
+
recursive: true
|
|
574
|
+
});
|
|
575
|
+
const repls = [
|
|
576
|
+
[
|
|
577
|
+
"__EXT_NAME__",
|
|
578
|
+
name
|
|
579
|
+
],
|
|
580
|
+
[
|
|
581
|
+
"__EXT_DESC__",
|
|
582
|
+
desc
|
|
583
|
+
],
|
|
584
|
+
[
|
|
585
|
+
"__EXT_BRAND__",
|
|
586
|
+
brand
|
|
587
|
+
],
|
|
588
|
+
[
|
|
589
|
+
"__EXT_PREFIX__",
|
|
590
|
+
slug.toUpperCase()
|
|
591
|
+
],
|
|
592
|
+
[
|
|
593
|
+
"__EXT_SLUG__",
|
|
594
|
+
slug
|
|
595
|
+
],
|
|
596
|
+
[
|
|
597
|
+
"__EXT_ACCENT_DEEP__",
|
|
598
|
+
options.accentDeep
|
|
599
|
+
],
|
|
600
|
+
[
|
|
601
|
+
"__EXT_ACCENT__",
|
|
602
|
+
options.accent
|
|
603
|
+
],
|
|
604
|
+
[
|
|
605
|
+
"__EXT_BADGE_COLOR__",
|
|
606
|
+
options.badge
|
|
607
|
+
]
|
|
608
|
+
];
|
|
609
|
+
const walk = /* @__PURE__ */ __name((dir) => {
|
|
610
|
+
for (const entry of readdirSync(dir)) {
|
|
611
|
+
const p = join(dir, entry);
|
|
612
|
+
if (statSync(p).isDirectory()) {
|
|
613
|
+
walk(p);
|
|
614
|
+
} else {
|
|
615
|
+
let content = readFileSync3(p, "utf8");
|
|
616
|
+
for (const [a, b] of repls) {
|
|
617
|
+
content = content.split(a).join(b);
|
|
618
|
+
}
|
|
619
|
+
writeFileSync2(p, content);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}, "walk");
|
|
623
|
+
walk(targetDir);
|
|
624
|
+
const updated = syncVendored(targetDir);
|
|
625
|
+
console.log(`
|
|
626
|
+
Created ${targetDir}`);
|
|
627
|
+
console.log(` Vendored: ${updated.join(", ")}`);
|
|
628
|
+
console.log("\n Next steps:");
|
|
629
|
+
console.log(" 1. Implement capture() in background.js (default: full-page screenshot).");
|
|
630
|
+
console.log(" 2. Adjust popup settings fields in popup/popup.html + popup.js.");
|
|
631
|
+
console.log(" 3. Add icons/ and the manifest icons field.");
|
|
632
|
+
console.log(` 4. Register product "${slug}" on the licensing server + create a Stripe Payment Link.
|
|
633
|
+
`);
|
|
634
|
+
});
|
|
635
|
+
var syncCommand = new Command4("sync").description("Refresh vendored ext-kit artifacts (lib/ext-kit*.js, offscreen, package script) in extensions").argument("[dirs...]", "Extension directories (default: scan cwd + direct children)").action((dirs) => {
|
|
636
|
+
let targets = (dirs || []).map((d) => resolve4(process.cwd(), d));
|
|
637
|
+
if (!targets.length) {
|
|
638
|
+
const cwd = process.cwd();
|
|
639
|
+
targets = isExtensionDir(cwd) ? [
|
|
640
|
+
cwd
|
|
641
|
+
] : readdirSync(cwd).map((entry) => join(cwd, entry)).filter((p) => statSync(p).isDirectory() && isExtensionDir(p));
|
|
642
|
+
}
|
|
643
|
+
if (!targets.length) {
|
|
644
|
+
console.log(" No extension directories found (need manifest.json + lib/ext-kit.js).");
|
|
645
|
+
process.exit(1);
|
|
646
|
+
}
|
|
647
|
+
for (const dir of targets) {
|
|
648
|
+
if (!isExtensionDir(dir)) {
|
|
649
|
+
console.log(` Skipped (not an ext-kit extension): ${dir}`);
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
const updated = syncVendored(dir);
|
|
653
|
+
console.log(` ${dir}: ${updated.length ? `updated ${updated.join(", ")}` : "already up to date"}`);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
var SERVER_FILES = [
|
|
657
|
+
"core.ts",
|
|
658
|
+
"stripe.ts",
|
|
659
|
+
"types.ts"
|
|
660
|
+
];
|
|
661
|
+
var SERVER_BANNER = "// AUTO-GENERATED from zucker-framework packages/ext-server \u2014 DO NOT EDIT.\n// \u4FEE\u6539\u8BF7\u6539\u6846\u67B6\u6E90\u7801\u540E\u8FD0\u884C `zucker ext sync-server <dir>` \u91CD\u65B0\u5206\u53D1\u3002\n";
|
|
662
|
+
var syncServerCommand = new Command4("sync-server").description("Vendor @zucker-framework/ext-server sources (core/stripe/types) into a NestJS app directory").argument("<dir>", "Target directory (e.g. apps/server/src/extension/core)").action((dir) => {
|
|
663
|
+
const target = resolve4(process.cwd(), dir);
|
|
664
|
+
mkdirSync2(target, {
|
|
665
|
+
recursive: true
|
|
666
|
+
});
|
|
667
|
+
const updated = [];
|
|
668
|
+
for (const name of SERVER_FILES) {
|
|
669
|
+
const src = join(monorepoRoot, "packages", "ext-server", "src", name);
|
|
670
|
+
if (!existsSync4(src)) {
|
|
671
|
+
console.log(` \x1B[31mError:\x1B[0m Missing ${src}`);
|
|
672
|
+
process.exit(1);
|
|
673
|
+
}
|
|
674
|
+
const content = SERVER_BANNER + readFileSync3(src, "utf8");
|
|
675
|
+
const dest = join(target, name);
|
|
676
|
+
if (!existsSync4(dest) || readFileSync3(dest, "utf8") !== content) {
|
|
677
|
+
writeFileSync2(dest, content);
|
|
678
|
+
updated.push(name);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
console.log(` ${target}: ${updated.length ? `updated ${updated.join(", ")}` : "already up to date"}`);
|
|
682
|
+
console.log(" Reminder: prisma \u7247\u6BB5\u89C1 packages/ext-server/assets/extension-product.prisma\uFF08\u624B\u52A8\u5E76\u5165\u6D88\u8D39\u65B9 schema\uFF09");
|
|
683
|
+
});
|
|
684
|
+
var extCommand = new Command4("ext").description("Browser extension scaffolding & vendored kit management").addCommand(createCommand).addCommand(syncCommand).addCommand(syncServerCommand);
|
|
685
|
+
|
|
686
|
+
// src/commands/ai.ts
|
|
687
|
+
import { Command as Command5 } from "commander";
|
|
688
|
+
import { Pool } from "pg";
|
|
689
|
+
import { AIAccessProviderRegistry, PostgresCredentialProfileStore } from "@zucker-framework/ai-access";
|
|
690
|
+
import { createOpenAICodexAccessProvider } from "@zucker-framework/ai-access/openai-codex";
|
|
691
|
+
function createAICommand(dependencies = {}) {
|
|
692
|
+
const registry = dependencies.registry ?? createDefaultRegistry();
|
|
693
|
+
const createStore = dependencies.createStore ?? createPostgresStore;
|
|
694
|
+
const env = dependencies.env ?? process.env;
|
|
695
|
+
const write = dependencies.write ?? ((message) => process.stdout.write(`${message}
|
|
696
|
+
`));
|
|
697
|
+
const line = /* @__PURE__ */ __name((message = "") => write(message), "line");
|
|
698
|
+
const withStore = /* @__PURE__ */ __name(async (operation) => {
|
|
699
|
+
const resource = await createStore(requiredDatabaseUrl(env));
|
|
700
|
+
try {
|
|
701
|
+
await resource.store.ensureSchema?.();
|
|
702
|
+
return await operation(resource.store);
|
|
703
|
+
} finally {
|
|
704
|
+
await resource.close();
|
|
705
|
+
}
|
|
706
|
+
}, "withStore");
|
|
707
|
+
const providersCommand = new Command5("providers").description("\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 AI Provider").action(() => {
|
|
708
|
+
for (const provider of registry.list()) {
|
|
709
|
+
const auth = provider.authMethods.map((method) => method.id).join(", ") || "\u65E0";
|
|
710
|
+
const sources = provider.credentialSources.map((source) => source.id).join(", ") || "\u65E0";
|
|
711
|
+
line(`${provider.id} ${provider.name}`);
|
|
712
|
+
line(` \u8BA4\u8BC1\u65B9\u5F0F\uFF1A${auth}`);
|
|
713
|
+
line(` \u51ED\u636E\u6765\u6E90\uFF1A${sources}`);
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
const storeCommand = new Command5("store").description("\u7BA1\u7406 AI Profile \u5B58\u50A8").addCommand(new Command5("init").description("\u521D\u59CB\u5316 PostgreSQL Profile \u8868").action(async () => {
|
|
717
|
+
await withStore(async () => void 0);
|
|
718
|
+
line("AI Profile \u5B58\u50A8\u5DF2\u521D\u59CB\u5316\u3002");
|
|
719
|
+
}));
|
|
720
|
+
const loginCommand = new Command5("login").description("\u767B\u5F55 AI Provider \u5E76\u4FDD\u5B58 Profile").argument("<provider>", "Provider ID").option("--profile <name>", "Profile \u540D\u79F0", "default").option("--method <method>", "\u8BA4\u8BC1\u65B9\u5F0F ID").action(async (providerId, options) => {
|
|
721
|
+
const provider = registry.get(providerId);
|
|
722
|
+
const method = options.method ? provider.authMethods.find((candidate) => candidate.id === options.method) : provider.authMethods[0];
|
|
723
|
+
if (!method) throw new Error(`Provider ${providerId} \u4E0D\u652F\u6301\u6307\u5B9A\u7684\u8BA4\u8BC1\u65B9\u5F0F\u3002`);
|
|
724
|
+
await withStore(async (store) => {
|
|
725
|
+
await method.login({
|
|
726
|
+
store,
|
|
727
|
+
profileName: options.profile,
|
|
728
|
+
onVerification: /* @__PURE__ */ __name((verification) => {
|
|
729
|
+
line(`\u8BF7\u6253\u5F00\uFF1A${verification.verificationUrl}`);
|
|
730
|
+
line(`\u5E76\u8F93\u5165\u8BBE\u5907\u7801\uFF1A${verification.userCode}`);
|
|
731
|
+
}, "onVerification")
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
line(`Provider ${providerId} \u7684 Profile ${options.profile} \u5DF2\u767B\u5F55\u3002`);
|
|
735
|
+
});
|
|
736
|
+
const statusCommand = new Command5("status").description("\u67E5\u770B\u5DF2\u4FDD\u5B58\u7684 AI Profile\uFF0C\u4E0D\u663E\u793A\u51ED\u636E\u5185\u5BB9").argument("[provider]", "Provider ID").action(async (providerId) => {
|
|
737
|
+
if (providerId) registry.get(providerId);
|
|
738
|
+
const profiles = await withStore((store) => store.list(providerId));
|
|
739
|
+
if (profiles.length === 0) {
|
|
740
|
+
line("\u6CA1\u6709\u5DF2\u4FDD\u5B58\u7684 AI Profile\u3002");
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
for (const profile of profiles) line(formatProfileStatus(profile));
|
|
744
|
+
});
|
|
745
|
+
const importCommand = new Command5("import").description("\u4ECE\u6587\u4EF6\u5BFC\u5165 Provider Profile").argument("<provider>", "Provider ID").requiredOption("--from-file <path>", "\u51ED\u636E\u6587\u4EF6\u8DEF\u5F84").option("--profile <name>", "Profile \u540D\u79F0", "default").action(async (providerId, options) => {
|
|
746
|
+
const provider = registry.get(providerId);
|
|
747
|
+
const source = provider.credentialSources.find((candidate) => candidate.kind === "file");
|
|
748
|
+
if (!source) throw new Error(`Provider ${providerId} \u4E0D\u652F\u6301\u4ECE\u6587\u4EF6\u5BFC\u5165 Profile\u3002`);
|
|
749
|
+
const profiles = await source.discover({
|
|
750
|
+
profileName: options.profile,
|
|
751
|
+
location: options.fromFile
|
|
752
|
+
});
|
|
753
|
+
await withStore(async (store) => {
|
|
754
|
+
for (const profile of profiles) {
|
|
755
|
+
if (profile.providerId !== providerId) {
|
|
756
|
+
throw new Error(`\u51ED\u636E\u6E90\u8FD4\u56DE\u4E86\u9519\u8BEF\u7684 Provider\uFF1A${profile.providerId}`);
|
|
757
|
+
}
|
|
758
|
+
await store.save(profile);
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
line(`\u5DF2\u5BFC\u5165 ${profiles.length} \u4E2A ${providerId} Profile\u3002`);
|
|
762
|
+
});
|
|
763
|
+
const profileCommand = new Command5("profile").description("\u7BA1\u7406 AI Profile").addCommand(importCommand);
|
|
764
|
+
const smokeCommand = new Command5("smoke").description("\u4F7F\u7528\u6307\u5B9A Profile \u53D1\u8D77\u4E00\u6B21\u6700\u5C0F\u771F\u5B9E\u8BF7\u6C42").argument("<provider>", "Provider ID").requiredOption("--model <model>", "\u6A21\u578B\u540D\u79F0").option("--profile <name>", "Profile \u540D\u79F0", "default").option("--input <text>", "\u6D4B\u8BD5\u8F93\u5165", "\u8BF7\u53EA\u56DE\u590D\uFF1A\u8FDE\u63A5\u6210\u529F").action(async (providerId, options) => {
|
|
765
|
+
const provider = registry.get(providerId);
|
|
766
|
+
const result = await withStore((store) => provider.runtime.complete({
|
|
767
|
+
model: options.model,
|
|
768
|
+
input: options.input,
|
|
769
|
+
reasoningEffort: "low",
|
|
770
|
+
textVerbosity: "low"
|
|
771
|
+
}, {
|
|
772
|
+
store,
|
|
773
|
+
profileName: options.profile
|
|
774
|
+
}));
|
|
775
|
+
line(result.content.trim());
|
|
776
|
+
line(`Token\uFF1A\u8F93\u5165 ${result.tokenUsage.input}\uFF0C\u8F93\u51FA ${result.tokenUsage.output}\uFF0C\u5408\u8BA1 ${result.tokenUsage.total}`);
|
|
777
|
+
});
|
|
778
|
+
return new Command5("ai").description("\u7BA1\u7406 AI Provider\u3001\u8BA4\u8BC1 Profile \u4E0E\u8C03\u7528\u9A8C\u8BC1").addCommand(providersCommand).addCommand(storeCommand).addCommand(loginCommand).addCommand(statusCommand).addCommand(profileCommand).addCommand(smokeCommand);
|
|
779
|
+
}
|
|
780
|
+
__name(createAICommand, "createAICommand");
|
|
781
|
+
function createDefaultRegistry() {
|
|
782
|
+
const registry = new AIAccessProviderRegistry();
|
|
783
|
+
registry.register(createOpenAICodexAccessProvider());
|
|
784
|
+
return registry;
|
|
785
|
+
}
|
|
786
|
+
__name(createDefaultRegistry, "createDefaultRegistry");
|
|
787
|
+
async function createPostgresStore(databaseUrl) {
|
|
788
|
+
const pool = new Pool({
|
|
789
|
+
connectionString: databaseUrl
|
|
790
|
+
});
|
|
791
|
+
return {
|
|
792
|
+
store: new PostgresCredentialProfileStore({
|
|
793
|
+
pool
|
|
794
|
+
}),
|
|
795
|
+
close: /* @__PURE__ */ __name(() => pool.end(), "close")
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
__name(createPostgresStore, "createPostgresStore");
|
|
799
|
+
function requiredDatabaseUrl(env) {
|
|
800
|
+
const databaseUrl = env.AI_ACCESS_DATABASE_URL?.trim() || env.DATABASE_URL?.trim();
|
|
801
|
+
if (!databaseUrl) {
|
|
802
|
+
throw new Error("\u8BF7\u8BBE\u7F6E AI_ACCESS_DATABASE_URL \u6216 DATABASE_URL \u540E\u518D\u4F7F\u7528 AI Profile\u3002");
|
|
803
|
+
}
|
|
804
|
+
return databaseUrl;
|
|
805
|
+
}
|
|
806
|
+
__name(requiredDatabaseUrl, "requiredDatabaseUrl");
|
|
807
|
+
function formatProfileStatus(profile) {
|
|
808
|
+
const expiresAt = profile.expiresAt;
|
|
809
|
+
const status = expiresAt && expiresAt <= Date.now() ? "\u5DF2\u8FC7\u671F\uFF0C\u53EF\u5C1D\u8BD5\u81EA\u52A8\u5237\u65B0" : "\u53EF\u7528";
|
|
810
|
+
const expiry = expiresAt ? new Date(expiresAt).toISOString() : "\u957F\u671F\u6709\u6548";
|
|
811
|
+
return `${profile.providerId}/${profile.profileName} ${status} \u8BA4\u8BC1=${profile.authMethod} \u5230\u671F=${expiry}`;
|
|
812
|
+
}
|
|
813
|
+
__name(formatProfileStatus, "formatProfileStatus");
|
|
814
|
+
|
|
815
|
+
// src/bin.ts
|
|
816
|
+
var program = new Command6();
|
|
817
|
+
program.name("zucker").description("CLI tool for Zucker Framework").version(JSON.parse(readFileSync4(resolve5(__dirname, "../package.json"), "utf8")).version);
|
|
818
|
+
program.addCommand(initCommand);
|
|
819
|
+
program.addCommand(doctorCommand);
|
|
820
|
+
program.addCommand(addCommand);
|
|
821
|
+
program.addCommand(extCommand);
|
|
822
|
+
program.addCommand(createAICommand());
|
|
823
|
+
program.parseAsync().catch((error) => {
|
|
824
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
825
|
+
process.stderr.write(`\u9519\u8BEF\uFF1A${message}
|
|
826
|
+
`);
|
|
827
|
+
process.exitCode = 1;
|
|
828
|
+
});
|