@cyodaa/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cyodaa Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cli.js ADDED
@@ -0,0 +1,1023 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { Command as Command9 } from "commander";
5
+
6
+ // src/commands/init.ts
7
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
8
+ import { createInterface } from "readline/promises";
9
+ import { stdin, stdout } from "process";
10
+ import { join, resolve } from "path";
11
+ import chalk from "chalk";
12
+ import { Command } from "commander";
13
+
14
+ // src/commands/templates.ts
15
+ var ICON_ONLY_MANIFEST = `name: my-icon-plugin
16
+ version: 1.0.0
17
+ display_name: My Icon Plugin
18
+ description: \u50C5\u5716\u6A19\u63D2\u4EF6 \u2014 \u5728 Marketplace \u4E2D\u986F\u793A\u54C1\u724C\u5716\u6A19
19
+ category: utility
20
+ author:
21
+ name: Your Name
22
+ email: you@example.com
23
+ permissions: []
24
+ min_platform_version: "2.0"
25
+ icon: icon.svg
26
+ tags:
27
+ - branding
28
+ - icon
29
+ `;
30
+ var FRONTEND_ONLY_MANIFEST = `name: my-frontend-plugin
31
+ version: 1.0.0
32
+ display_name: My Frontend Plugin
33
+ description: \u524D\u7AEF\u63D2\u4EF6 \u2014 \u63D0\u4F9B\u81EA\u8A02 React \u7D44\u4EF6
34
+ category: visualization
35
+ author:
36
+ name: Your Name
37
+ email: you@example.com
38
+ permissions:
39
+ - read:data
40
+ min_platform_version: "2.0"
41
+ entry_points:
42
+ frontend: src/index.tsx
43
+ icon: icon.svg
44
+ tags:
45
+ - frontend
46
+ - dashboard
47
+ `;
48
+ var FRONTEND_INDEX_TSX = `/**
49
+ * Cyodaa \u524D\u7AEF\u63D2\u4EF6\u5165\u53E3
50
+ *
51
+ * \u6B64\u6A94\u6848\u662F\u63D2\u4EF6\u7684\u524D\u7AEF\u9032\u5165\u9EDE\u3002
52
+ * \u8ACB\u5728\u6B64\u5BE6\u4F5C\u60A8\u7684 React \u7D44\u4EF6\u3002
53
+ */
54
+
55
+ import React from 'react';
56
+
57
+ interface PluginProps {
58
+ /** \u5E73\u53F0\u63D0\u4F9B\u7684\u4E0A\u4E0B\u6587 */
59
+ context: {
60
+ tenantId: string;
61
+ userId: number;
62
+ locale: string;
63
+ };
64
+ }
65
+
66
+ /**
67
+ * \u63D2\u4EF6\u4E3B\u7D44\u4EF6
68
+ */
69
+ const MyPlugin: React.FC<PluginProps> = ({ context }) => {
70
+ return (
71
+ <div style={{ padding: '1rem' }}>
72
+ <h2>My Frontend Plugin</h2>
73
+ <p>\u6B61\u8FCE\u4F7F\u7528 Cyodaa \u63D2\u4EF6\u7CFB\u7D71\uFF01</p>
74
+ <p>Tenant: {context.tenantId}</p>
75
+ </div>
76
+ );
77
+ };
78
+
79
+ export default MyPlugin;
80
+ `;
81
+ var BACKEND_ONLY_MANIFEST = `name: my-backend-plugin
82
+ version: 1.0.0
83
+ display_name: My Backend Plugin
84
+ description: \u5F8C\u7AEF\u63D2\u4EF6 \u2014 \u63D0\u4F9B\u81EA\u8A02 API \u7AEF\u9EDE
85
+ category: integration
86
+ author:
87
+ name: Your Name
88
+ email: you@example.com
89
+ permissions:
90
+ - read:data
91
+ - write:data
92
+ min_platform_version: "2.0"
93
+ entry_points:
94
+ backend: src/main.py
95
+ icon: icon.svg
96
+ tags:
97
+ - backend
98
+ - api
99
+ `;
100
+ var BACKEND_MAIN_PY = `"""Cyodaa \u5F8C\u7AEF\u63D2\u4EF6\u5165\u53E3\u3002
101
+
102
+ \u6B64\u6A94\u6848\u662F\u63D2\u4EF6\u7684\u5F8C\u7AEF\u9032\u5165\u9EDE\u3002
103
+ \u8ACB\u5728\u6B64\u5B9A\u7FA9\u60A8\u7684 FastAPI \u8DEF\u7531\u3002
104
+ """
105
+
106
+ from __future__ import annotations
107
+
108
+ from fastapi import APIRouter
109
+
110
+ router = APIRouter(prefix="/plugin", tags=["my-backend-plugin"])
111
+
112
+
113
+ @router.get("/hello")
114
+ async def hello() -> dict[str, str]:
115
+ """\u63D2\u4EF6\u7BC4\u4F8B\u7AEF\u9EDE\u3002"""
116
+ return {"message": "\u6B61\u8FCE\u4F7F\u7528 Cyodaa \u63D2\u4EF6\u7CFB\u7D71\uFF01"}
117
+
118
+
119
+ @router.get("/health")
120
+ async def health() -> dict[str, str]:
121
+ """\u5065\u5EB7\u6AA2\u67E5\u3002"""
122
+ return {"status": "ok"}
123
+ `;
124
+ var FULL_STACK_MANIFEST = `name: my-fullstack-plugin
125
+ version: 1.0.0
126
+ display_name: My Full Stack Plugin
127
+ description: \u5168\u7AEF\u63D2\u4EF6 \u2014 \u5305\u542B\u524D\u7AEF\u7D44\u4EF6\u8207\u5F8C\u7AEF API
128
+ category: analytics
129
+ author:
130
+ name: Your Name
131
+ email: you@example.com
132
+ permissions:
133
+ - read:data
134
+ - write:data
135
+ - read:skills
136
+ min_platform_version: "2.0"
137
+ entry_points:
138
+ frontend: src/frontend/index.tsx
139
+ backend: src/backend/main.py
140
+ icon: icon.svg
141
+ tags:
142
+ - fullstack
143
+ - analytics
144
+ - dashboard
145
+ `;
146
+ var FULL_STACK_FRONTEND_TSX = `/**
147
+ * Cyodaa \u5168\u7AEF\u63D2\u4EF6 \u2014 \u524D\u7AEF\u5165\u53E3
148
+ *
149
+ * \u6B64\u6A94\u6848\u662F\u63D2\u4EF6\u7684\u524D\u7AEF\u9032\u5165\u9EDE\u3002
150
+ * \u53EF\u900F\u904E context.apiBase \u547C\u53EB\u63D2\u4EF6\u5F8C\u7AEF API\u3002
151
+ */
152
+
153
+ import React, { useEffect, useState } from 'react';
154
+
155
+ interface PluginProps {
156
+ /** \u5E73\u53F0\u63D0\u4F9B\u7684\u4E0A\u4E0B\u6587 */
157
+ context: {
158
+ tenantId: string;
159
+ userId: number;
160
+ locale: string;
161
+ /** \u63D2\u4EF6\u5F8C\u7AEF API \u57FA\u790E\u8DEF\u5F91 */
162
+ apiBase: string;
163
+ };
164
+ }
165
+
166
+ /**
167
+ * \u5168\u7AEF\u63D2\u4EF6\u4E3B\u7D44\u4EF6
168
+ */
169
+ const MyFullStackPlugin: React.FC<PluginProps> = ({ context }) => {
170
+ const [message, setMessage] = useState<string>('\u8F09\u5165\u4E2D...');
171
+
172
+ useEffect(() => {
173
+ fetch(\`\${context.apiBase}/plugin/hello\`)
174
+ .then((res) => res.json())
175
+ .then((data) => setMessage(data.message))
176
+ .catch(() => setMessage('\u7121\u6CD5\u9023\u7DDA\u81F3\u63D2\u4EF6\u5F8C\u7AEF'));
177
+ }, [context.apiBase]);
178
+
179
+ return (
180
+ <div style={{ padding: '1rem' }}>
181
+ <h2>My Full Stack Plugin</h2>
182
+ <p>{message}</p>
183
+ <p>Tenant: {context.tenantId}</p>
184
+ </div>
185
+ );
186
+ };
187
+
188
+ export default MyFullStackPlugin;
189
+ `;
190
+ var FULL_STACK_BACKEND_PY = `"""Cyodaa \u5168\u7AEF\u63D2\u4EF6 \u2014 \u5F8C\u7AEF\u5165\u53E3\u3002
191
+
192
+ \u6B64\u6A94\u6848\u662F\u5168\u7AEF\u63D2\u4EF6\u7684\u5F8C\u7AEF\u9032\u5165\u9EDE\u3002
193
+ \u524D\u7AEF\u53EF\u900F\u904E context.apiBase \u547C\u53EB\u9019\u4E9B\u7AEF\u9EDE\u3002
194
+ """
195
+
196
+ from __future__ import annotations
197
+
198
+ from fastapi import APIRouter
199
+
200
+ router = APIRouter(prefix="/plugin", tags=["my-fullstack-plugin"])
201
+
202
+
203
+ @router.get("/hello")
204
+ async def hello() -> dict[str, str]:
205
+ """\u63D2\u4EF6\u7BC4\u4F8B\u7AEF\u9EDE\u3002"""
206
+ return {"message": "\u4F86\u81EA\u5168\u7AEF\u63D2\u4EF6\u5F8C\u7AEF\u7684\u554F\u5019\uFF01"}
207
+
208
+
209
+ @router.get("/health")
210
+ async def health() -> dict[str, str]:
211
+ """\u5065\u5EB7\u6AA2\u67E5\u3002"""
212
+ return {"status": "ok"}
213
+
214
+
215
+ @router.get("/data")
216
+ async def get_data() -> dict[str, object]:
217
+ """\u7BC4\u4F8B\u8CC7\u6599\u7AEF\u9EDE\u3002
218
+
219
+ \u5728\u6B64\u63A5\u5165\u5E73\u53F0 Data Platform \u6216\u81EA\u5B9A\u8CC7\u6599\u6E90\u3002
220
+ """
221
+ return {
222
+ "items": [],
223
+ "total": 0,
224
+ "message": "\u8ACB\u66FF\u63DB\u6B64\u7AEF\u9EDE\u7684\u5BE6\u4F5C\u4EE5\u9023\u63A5\u771F\u5BE6\u8CC7\u6599\u3002",
225
+ }
226
+ `;
227
+ var TEMPLATES = {
228
+ icon_only: {
229
+ files: [{ path: "manifest.yaml", content: ICON_ONLY_MANIFEST }]
230
+ },
231
+ frontend_only: {
232
+ files: [
233
+ { path: "manifest.yaml", content: FRONTEND_ONLY_MANIFEST },
234
+ { path: "src/index.tsx", content: FRONTEND_INDEX_TSX }
235
+ ]
236
+ },
237
+ backend_only: {
238
+ files: [
239
+ { path: "manifest.yaml", content: BACKEND_ONLY_MANIFEST },
240
+ { path: "src/main.py", content: BACKEND_MAIN_PY }
241
+ ]
242
+ },
243
+ full_stack: {
244
+ files: [
245
+ { path: "manifest.yaml", content: FULL_STACK_MANIFEST },
246
+ { path: "src/frontend/index.tsx", content: FULL_STACK_FRONTEND_TSX },
247
+ { path: "src/backend/main.py", content: FULL_STACK_BACKEND_PY }
248
+ ]
249
+ }
250
+ };
251
+
252
+ // src/commands/init.ts
253
+ async function promptChoice(question, choices, defaultChoice) {
254
+ const rl = createInterface({ input: stdin, output: stdout });
255
+ try {
256
+ while (true) {
257
+ const answer = await rl.question(question);
258
+ const value = answer.trim() || defaultChoice;
259
+ if (choices.includes(value)) {
260
+ return value;
261
+ }
262
+ process.stdout.write(chalk.red(` \u8ACB\u8F38\u5165 ${choices.join(" / ")} \u5176\u4E2D\u4E4B\u4E00
263
+ `));
264
+ }
265
+ } finally {
266
+ rl.close();
267
+ }
268
+ }
269
+ async function promptText(question, defaultValue) {
270
+ const rl = createInterface({ input: stdin, output: stdout });
271
+ try {
272
+ const answer = await rl.question(question);
273
+ return answer.trim() || defaultValue;
274
+ } finally {
275
+ rl.close();
276
+ }
277
+ }
278
+ var TEMPLATE_MENU = [
279
+ { key: "1", id: "icon_only", desc: "\u50C5\u5716\u6A19\u63D2\u4EF6 \u2014 \u6700\u8F15\u91CF\uFF0C\u53EA\u9700\u63D0\u4F9B\u5716\u6A19\u8207 manifest" },
280
+ { key: "2", id: "frontend_only", desc: "\u524D\u7AEF\u63D2\u4EF6 \u2014 \u542B React \u7D44\u4EF6\uFF0C\u7121\u5F8C\u7AEF" },
281
+ { key: "3", id: "backend_only", desc: "\u5F8C\u7AEF\u63D2\u4EF6 \u2014 \u542B FastAPI \u7AEF\u9EDE\uFF0C\u7121\u524D\u7AEF" },
282
+ { key: "4", id: "full_stack", desc: "\u5168\u7AEF\u63D2\u4EF6 \u2014 \u524D\u5F8C\u7AEF\u5B8C\u6574\u67B6\u69CB" }
283
+ ];
284
+ function writeTemplateFiles(templateId, dest, projectName) {
285
+ const template = TEMPLATES[templateId];
286
+ for (const file of template.files) {
287
+ const filePath = join(dest, file.path);
288
+ const dir = join(filePath, "..");
289
+ mkdirSync(dir, { recursive: true });
290
+ let content = file.content;
291
+ if (file.path === "manifest.yaml") {
292
+ const displayName = projectName.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
293
+ content = content.replace(/^name: .+$/m, `name: ${projectName}`).replace(/^display_name: .+$/m, `display_name: ${displayName}`);
294
+ }
295
+ writeFileSync(filePath, content, "utf-8");
296
+ }
297
+ }
298
+ var initCommand = new Command("init").argument("[name]", "\u63D2\u4EF6\u540D\u7A31").description("\u521D\u59CB\u5316\u65B0\u7684 Cyodaa \u63D2\u4EF6\u5C08\u6848").action(async (name) => {
299
+ const projectName = name ?? await promptText(chalk.cyan("\u63D2\u4EF6\u540D\u7A31 (my-plugin): "), "my-plugin");
300
+ const dest = resolve(process.cwd(), projectName);
301
+ if (existsSync(dest)) {
302
+ process.stderr.write(chalk.red(`\u76EE\u9304 ${dest} \u5DF2\u5B58\u5728\uFF0C\u8ACB\u9078\u64C7\u5176\u4ED6\u540D\u7A31\u3002
303
+ `));
304
+ process.exit(1);
305
+ }
306
+ process.stdout.write("\n" + chalk.bold("\u8ACB\u9078\u64C7\u63D2\u4EF6\u6A21\u677F\uFF1A") + "\n\n");
307
+ for (const item of TEMPLATE_MENU) {
308
+ process.stdout.write(` ${chalk.cyan(item.key)} ${item.desc}
309
+ `);
310
+ }
311
+ process.stdout.write("\n");
312
+ const choice = await promptChoice(
313
+ "\u9078\u64C7\u6A21\u677F (1): ",
314
+ TEMPLATE_MENU.map((t) => t.key),
315
+ "1"
316
+ );
317
+ const selected = TEMPLATE_MENU.find((t) => t.key === choice);
318
+ mkdirSync(dest, { recursive: true });
319
+ writeTemplateFiles(selected.id, dest, projectName);
320
+ process.stdout.write("\n");
321
+ process.stdout.write(chalk.green(" \u63D2\u4EF6\u5C08\u6848\u5DF2\u5EFA\u7ACB\uFF01\n\n"));
322
+ process.stdout.write(` \u76EE\u9304\uFF1A${dest}
323
+ `);
324
+ process.stdout.write(` \u6A21\u677F\uFF1A${selected.desc}
325
+
326
+ `);
327
+ process.stdout.write(chalk.dim(" \u4E0B\u4E00\u6B65\uFF1A\n"));
328
+ process.stdout.write(chalk.dim(` cd ${projectName}
329
+ `));
330
+ process.stdout.write(chalk.dim(" cyodaa validate # \u9A57\u8B49 manifest\n"));
331
+ process.stdout.write(chalk.dim(" cyodaa dev # \u555F\u52D5\u958B\u767C\u4F3A\u670D\u5668\n\n"));
332
+ });
333
+
334
+ // src/commands/validate.ts
335
+ import { existsSync as existsSync2, readFileSync } from "fs";
336
+ import { join as join2, resolve as resolve2 } from "path";
337
+ import chalk2 from "chalk";
338
+ import { Command as Command2 } from "commander";
339
+ import yaml from "js-yaml";
340
+ var REQUIRED_FIELDS = [
341
+ { field: "name", type: "string", label: "\u63D2\u4EF6\u540D\u7A31\uFF08\u82F1\u6587\uFF0C\u5C0F\u5BEB + \u9023\u5B57\u7B26\uFF09" },
342
+ { field: "version", type: "string", label: "\u8A9E\u610F\u5316\u7248\u672C\u865F\uFF08\u5982 1.0.0\uFF09" },
343
+ { field: "display_name", type: "string", label: "\u986F\u793A\u540D\u7A31" },
344
+ { field: "description", type: "string", label: "\u63D2\u4EF6\u63CF\u8FF0" },
345
+ { field: "category", type: "string", label: "\u5206\u985E\uFF08analytics / integration / visualization / utility / ai\uFF09" }
346
+ ];
347
+ var VALID_CATEGORIES = /* @__PURE__ */ new Set([
348
+ "analytics",
349
+ "integration",
350
+ "visualization",
351
+ "utility",
352
+ "ai"
353
+ ]);
354
+ var VALID_PERMISSIONS = /* @__PURE__ */ new Set([
355
+ "read:data",
356
+ "write:data",
357
+ "read:skills",
358
+ "write:skills",
359
+ "read:users",
360
+ "execute:agents",
361
+ "admin"
362
+ ]);
363
+ function validateManifest(manifest, cwd) {
364
+ const issues = [];
365
+ for (const { field, type, label } of REQUIRED_FIELDS) {
366
+ const value = manifest[field];
367
+ if (value === void 0 || value === null) {
368
+ issues.push({ field, level: "error", message: `\u7F3A\u5C11\u5FC5\u586B\u6B04\u4F4D\uFF1A${label}` });
369
+ } else if (typeof value !== type) {
370
+ issues.push({
371
+ field,
372
+ level: "error",
373
+ message: `\u578B\u5225\u932F\u8AA4\uFF1A\u9810\u671F ${type}\uFF0C\u5BE6\u969B ${typeof value}`
374
+ });
375
+ }
376
+ }
377
+ const name = manifest.name;
378
+ if (typeof name === "string" && name) {
379
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
380
+ issues.push({
381
+ field: "name",
382
+ level: "error",
383
+ message: "\u540D\u7A31\u53EA\u80FD\u5305\u542B\u5C0F\u5BEB\u82F1\u6587\u3001\u6578\u5B57\u8207\u9023\u5B57\u7B26\uFF0C\u4E14\u5FC5\u9808\u4EE5\u5B57\u6BCD\u958B\u982D"
384
+ });
385
+ }
386
+ }
387
+ const version = manifest.version;
388
+ if (typeof version === "string" && version) {
389
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
390
+ issues.push({
391
+ field: "version",
392
+ level: "warning",
393
+ message: "\u5EFA\u8B70\u4F7F\u7528\u8A9E\u610F\u5316\u7248\u672C\u683C\u5F0F\uFF08X.Y.Z\uFF09"
394
+ });
395
+ }
396
+ }
397
+ const category = manifest.category;
398
+ if (typeof category === "string" && category && !VALID_CATEGORIES.has(category)) {
399
+ const valid = [...VALID_CATEGORIES].sort().join(", ");
400
+ issues.push({
401
+ field: "category",
402
+ level: "error",
403
+ message: `\u4E0D\u652F\u63F4\u7684\u5206\u985E '${category}'\uFF0C\u53EF\u9078\uFF1A${valid}`
404
+ });
405
+ }
406
+ const permissions = manifest.permissions;
407
+ if (Array.isArray(permissions)) {
408
+ for (const perm of permissions) {
409
+ if (!VALID_PERMISSIONS.has(String(perm))) {
410
+ const valid = [...VALID_PERMISSIONS].sort().join(", ");
411
+ issues.push({
412
+ field: "permissions",
413
+ level: "warning",
414
+ message: `\u672A\u77E5\u6B0A\u9650 '${perm}'\uFF0C\u53EF\u9078\uFF1A${valid}`
415
+ });
416
+ }
417
+ }
418
+ }
419
+ const author = manifest.author;
420
+ if (author !== void 0) {
421
+ if (typeof author !== "object" || author === null || Array.isArray(author)) {
422
+ issues.push({
423
+ field: "author",
424
+ level: "error",
425
+ message: "author \u5FC5\u9808\u662F\u7269\u4EF6\uFF08\u542B name / email\uFF09"
426
+ });
427
+ } else if (!("name" in author)) {
428
+ issues.push({
429
+ field: "author.name",
430
+ level: "warning",
431
+ message: "\u5EFA\u8B70\u586B\u5BEB\u4F5C\u8005\u540D\u7A31"
432
+ });
433
+ }
434
+ }
435
+ const entryPoints = manifest.entry_points;
436
+ if (typeof entryPoints === "object" && entryPoints !== null && !Array.isArray(entryPoints)) {
437
+ for (const [epKey, epPath] of Object.entries(entryPoints)) {
438
+ if (typeof epPath === "string" && !existsSync2(join2(cwd, epPath))) {
439
+ issues.push({
440
+ field: `entry_points.${epKey}`,
441
+ level: "warning",
442
+ message: `\u5165\u53E3\u6A94\u6848 '${epPath}' \u4E0D\u5B58\u5728`
443
+ });
444
+ }
445
+ }
446
+ }
447
+ return issues;
448
+ }
449
+ function printResults(issues) {
450
+ if (issues.length === 0) {
451
+ process.stdout.write("\n");
452
+ process.stdout.write(chalk2.green(" manifest.yaml \u9A57\u8B49\u901A\u904E\uFF01\u6240\u6709\u6B04\u4F4D\u7686\u5408\u6CD5\u3002\n\n"));
453
+ return;
454
+ }
455
+ process.stdout.write("\n");
456
+ process.stdout.write(chalk2.bold(" \u9A57\u8B49\u7D50\u679C\n"));
457
+ process.stdout.write(" " + "\u2500".repeat(60) + "\n");
458
+ let hasError = false;
459
+ for (const issue of issues) {
460
+ const isError = issue.level === "error";
461
+ if (isError) hasError = true;
462
+ const levelStr = isError ? chalk2.red("\u932F\u8AA4") : chalk2.yellow("\u8B66\u544A");
463
+ const fieldStr = chalk2.cyan(issue.field);
464
+ process.stdout.write(` ${levelStr} ${fieldStr} ${issue.message}
465
+ `);
466
+ }
467
+ process.stdout.write(" " + "\u2500".repeat(60) + "\n");
468
+ if (hasError) {
469
+ process.stdout.write(chalk2.red("\n \u5B58\u5728\u932F\u8AA4\uFF0C\u8ACB\u4FEE\u6B63\u5F8C\u91CD\u65B0\u9A57\u8B49\u3002\n\n"));
470
+ } else {
471
+ process.stdout.write(
472
+ chalk2.yellow("\n \u6709\u8B66\u544A\uFF0C\u4F46\u4E0D\u5F71\u97FF\u63D0\u4EA4\u3002\u5EFA\u8B70\u4FEE\u6B63\u4EE5\u7372\u5F97\u66F4\u597D\u7684\u5BE9\u6838\u7D50\u679C\u3002\n\n")
473
+ );
474
+ }
475
+ }
476
+ var validateCommand = new Command2("validate").description("\u9A57\u8B49\u7576\u524D\u76EE\u9304\u7684 manifest.yaml").option("-p, --path <path>", "\u63D2\u4EF6\u76EE\u9304\uFF08\u9810\u8A2D\u70BA\u7576\u524D\u76EE\u9304\uFF09", ".").action((opts) => {
477
+ const pluginDir = resolve2(opts.path);
478
+ const manifestPath = join2(pluginDir, "manifest.yaml");
479
+ if (!existsSync2(manifestPath)) {
480
+ process.stderr.write(chalk2.red("\u627E\u4E0D\u5230 manifest.yaml\uFF0C\u8ACB\u78BA\u8A8D\u60A8\u5728\u63D2\u4EF6\u76EE\u9304\u4E2D\u3002\n"));
481
+ process.stderr.write(chalk2.dim("\u63D0\u793A\uFF1A\u4F7F\u7528 `cyodaa init` \u5EFA\u7ACB\u65B0\u63D2\u4EF6\u5C08\u6848\u3002\n"));
482
+ process.exit(1);
483
+ }
484
+ let manifest;
485
+ try {
486
+ const raw = readFileSync(manifestPath, "utf-8");
487
+ manifest = yaml.load(raw);
488
+ } catch (error) {
489
+ process.stderr.write(chalk2.red(`manifest.yaml \u89E3\u6790\u5931\u6557\uFF1A${error}
490
+ `));
491
+ process.exit(1);
492
+ }
493
+ if (typeof manifest !== "object" || manifest === null || Array.isArray(manifest)) {
494
+ process.stderr.write(chalk2.red("manifest.yaml \u6839\u7BC0\u9EDE\u5FC5\u9808\u662F\u7269\u4EF6\u3002\n"));
495
+ process.exit(1);
496
+ }
497
+ const issues = validateManifest(manifest, pluginDir);
498
+ printResults(issues);
499
+ const hasError = issues.some((i) => i.level === "error");
500
+ if (hasError) {
501
+ process.exit(1);
502
+ }
503
+ });
504
+
505
+ // src/commands/build.ts
506
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
507
+ import { execSync } from "child_process";
508
+ import { join as join3, resolve as resolve3 } from "path";
509
+ import chalk3 from "chalk";
510
+ import { Command as Command3 } from "commander";
511
+ import yaml2 from "js-yaml";
512
+ var buildCommand = new Command3("build").description("\u5EFA\u69CB\u63D2\u4EF6\uFF08\u524D\u7AEF bundle / \u5F8C\u7AEF\u6253\u5305\uFF09").option("--path <path>", "\u63D2\u4EF6\u76EE\u9304", ".").action((opts) => {
513
+ const pluginDir = resolve3(opts.path);
514
+ const manifestPath = join3(pluginDir, "manifest.yaml");
515
+ if (!existsSync3(manifestPath)) {
516
+ process.stderr.write(chalk3.red("\u627E\u4E0D\u5230 manifest.yaml\uFF0C\u8ACB\u78BA\u8A8D\u60A8\u5728\u63D2\u4EF6\u76EE\u9304\u4E2D\u3002\n"));
517
+ process.exit(1);
518
+ }
519
+ const raw = readFileSync2(manifestPath, "utf-8");
520
+ const manifest = yaml2.load(raw);
521
+ const entryPoints = manifest.entry_points ?? {};
522
+ const hasFrontend = "frontend" in entryPoints;
523
+ const hasBackend = "backend" in entryPoints;
524
+ if (!hasFrontend && !hasBackend) {
525
+ process.stdout.write(chalk3.yellow("\u6B64\u63D2\u4EF6\u6C92\u6709\u5B9A\u7FA9 entry_points\uFF0C\u7121\u9700\u5EFA\u69CB\u3002\n"));
526
+ return;
527
+ }
528
+ const displayName = manifest.display_name ?? manifest.name ?? "(unknown)";
529
+ process.stdout.write(chalk3.bold(`
530
+ \u5EFA\u69CB\u63D2\u4EF6\uFF1A${displayName}
531
+
532
+ `));
533
+ if (hasFrontend) {
534
+ const packageJsonPath = join3(pluginDir, "package.json");
535
+ if (existsSync3(packageJsonPath)) {
536
+ process.stdout.write(` ${chalk3.cyan("\u524D\u7AEF")} \u57F7\u884C npm run build ...
537
+ `);
538
+ try {
539
+ execSync("npm run build", {
540
+ cwd: pluginDir,
541
+ stdio: "pipe",
542
+ timeout: 12e4
543
+ });
544
+ process.stdout.write(` ${chalk3.green("\u524D\u7AEF\u5EFA\u69CB\u6210\u529F")}
545
+ `);
546
+ } catch (error) {
547
+ const stderr = error?.stderr?.toString() ?? "";
548
+ process.stderr.write(` ${chalk3.red("\u524D\u7AEF\u5EFA\u69CB\u5931\u6557")}
549
+ `);
550
+ if (stderr) {
551
+ process.stderr.write(`${stderr}
552
+ `);
553
+ }
554
+ process.exit(1);
555
+ }
556
+ } else {
557
+ process.stdout.write(` ${chalk3.yellow("\u524D\u7AEF\uFF1A\u627E\u4E0D\u5230 package.json\uFF0C\u8DF3\u904E npm build\u3002")}
558
+ `);
559
+ process.stdout.write(
560
+ chalk3.dim(" \u63D0\u793A\uFF1A\u5982\u4F7F\u7528\u5176\u4ED6\u5EFA\u69CB\u5DE5\u5177\uFF0C\u8ACB\u624B\u52D5\u5EFA\u69CB\u5F8C\u57F7\u884C `cyodaa pack`\u3002\n")
561
+ );
562
+ }
563
+ }
564
+ if (hasBackend) {
565
+ const backendEntry = join3(pluginDir, entryPoints.backend);
566
+ if (existsSync3(backendEntry)) {
567
+ process.stdout.write(` ${chalk3.green("\u5F8C\u7AEF\u5165\u53E3\u6A94\u6848\u5B58\u5728")}
568
+ `);
569
+ } else {
570
+ process.stderr.write(
571
+ ` ${chalk3.red(`\u5F8C\u7AEF\u5165\u53E3 '${entryPoints.backend}' \u4E0D\u5B58\u5728`)}
572
+ `
573
+ );
574
+ process.exit(1);
575
+ }
576
+ }
577
+ process.stdout.write("\n");
578
+ process.stdout.write(chalk3.green(" \u5EFA\u69CB\u5B8C\u6210\uFF01\u4E0B\u4E00\u6B65\uFF1A`cyodaa pack` \u6253\u5305\u63D2\u4EF6\u3002\n\n"));
579
+ });
580
+
581
+ // src/commands/pack.ts
582
+ import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
583
+ import { join as join4, resolve as resolve4, relative } from "path";
584
+ import chalk4 from "chalk";
585
+ import { Command as Command4 } from "commander";
586
+ import yaml3 from "js-yaml";
587
+ import * as tar from "tar";
588
+ var EXCLUDE_DIRS = /* @__PURE__ */ new Set([
589
+ "node_modules",
590
+ "__pycache__",
591
+ ".git",
592
+ ".venv",
593
+ "dist"
594
+ ]);
595
+ var EXCLUDE_FILES = /* @__PURE__ */ new Set([
596
+ ".env",
597
+ ".DS_Store"
598
+ ]);
599
+ var EXCLUDE_EXTENSIONS = /* @__PURE__ */ new Set([
600
+ ".pyc",
601
+ ".pyo"
602
+ ]);
603
+ function shouldExclude(relativePath) {
604
+ const parts = relativePath.split("/");
605
+ for (const part of parts) {
606
+ if (EXCLUDE_DIRS.has(part) || EXCLUDE_FILES.has(part)) {
607
+ return true;
608
+ }
609
+ }
610
+ for (const ext of EXCLUDE_EXTENSIONS) {
611
+ if (relativePath.endsWith(ext)) {
612
+ return true;
613
+ }
614
+ }
615
+ return false;
616
+ }
617
+ function collectFiles(baseDir, currentDir) {
618
+ const results = [];
619
+ const entries = readdirSync(currentDir, { withFileTypes: true });
620
+ for (const entry of entries) {
621
+ const fullPath = join4(currentDir, entry.name);
622
+ const relPath = relative(baseDir, fullPath);
623
+ if (shouldExclude(relPath)) {
624
+ continue;
625
+ }
626
+ if (entry.isDirectory()) {
627
+ results.push(...collectFiles(baseDir, fullPath));
628
+ } else if (entry.isFile()) {
629
+ results.push(relPath);
630
+ }
631
+ }
632
+ return results;
633
+ }
634
+ function formatSize(bytes) {
635
+ const kb = bytes / 1024;
636
+ return kb < 1024 ? `${kb.toFixed(1)} KB` : `${(kb / 1024).toFixed(2)} MB`;
637
+ }
638
+ var packCommand = new Command4("pack").description("\u5C07\u63D2\u4EF6\u76EE\u9304\u6253\u5305\u70BA .tar.gz").option("--path <path>", "\u63D2\u4EF6\u76EE\u9304", ".").option("-o, --output <output>", "\u8F38\u51FA\u6A94\u6848\u8DEF\u5F91").action(async (opts) => {
639
+ const pluginDir = resolve4(opts.path);
640
+ const manifestPath = join4(pluginDir, "manifest.yaml");
641
+ if (!existsSync4(manifestPath)) {
642
+ process.stderr.write(chalk4.red("\u627E\u4E0D\u5230 manifest.yaml\uFF0C\u8ACB\u78BA\u8A8D\u60A8\u5728\u63D2\u4EF6\u76EE\u9304\u4E2D\u3002\n"));
643
+ process.exit(1);
644
+ }
645
+ const raw = readFileSync3(manifestPath, "utf-8");
646
+ const manifest = yaml3.load(raw);
647
+ const name = manifest.name ?? "plugin";
648
+ const version = manifest.version ?? "0.0.0";
649
+ const archiveName = `${name}-${version}.tar.gz`;
650
+ const archivePath = opts.output ? resolve4(opts.output) : join4(pluginDir, "..", archiveName);
651
+ const files = collectFiles(pluginDir, pluginDir);
652
+ if (files.length === 0) {
653
+ process.stderr.write(chalk4.red("\u6C92\u6709\u53EF\u6253\u5305\u7684\u6A94\u6848\u3002\n"));
654
+ process.exit(1);
655
+ }
656
+ await tar.create(
657
+ {
658
+ gzip: true,
659
+ file: archivePath,
660
+ cwd: pluginDir
661
+ },
662
+ [...files].sort()
663
+ );
664
+ const size = statSync(archivePath).size;
665
+ process.stdout.write("\n");
666
+ process.stdout.write(chalk4.green(" \u6253\u5305\u5B8C\u6210\uFF01\n\n"));
667
+ process.stdout.write(` \u6A94\u6848\uFF1A${archivePath}
668
+ `);
669
+ process.stdout.write(` \u5927\u5C0F\uFF1A${formatSize(size)}
670
+ `);
671
+ process.stdout.write(` \u5305\u542B\uFF1A${files.length} \u500B\u6A94\u6848
672
+
673
+ `);
674
+ process.stdout.write(chalk4.dim(" \u4E0B\u4E00\u6B65\uFF1A`cyodaa submit` \u63D0\u4EA4\u81F3\u5E02\u96C6\u5BE9\u6838\u3002\n\n"));
675
+ });
676
+
677
+ // src/commands/submit.ts
678
+ import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
679
+ import { join as join6, resolve as resolve5 } from "path";
680
+ import chalk5 from "chalk";
681
+ import ora from "ora";
682
+ import { Command as Command5 } from "commander";
683
+
684
+ // src/api-client.ts
685
+ import { readFileSync as readFileSync5 } from "fs";
686
+ import { basename } from "path";
687
+
688
+ // src/config.ts
689
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, chmodSync } from "fs";
690
+ import { homedir } from "os";
691
+ import { join as join5 } from "path";
692
+ var CONFIG_DIR = join5(homedir(), ".cyodaa");
693
+ var CONFIG_FILE = join5(CONFIG_DIR, "config.json");
694
+ var DEFAULT_CONFIG = {
695
+ api_url: "http://localhost:9850",
696
+ api_key: "",
697
+ developer_id: null
698
+ };
699
+ function loadConfig() {
700
+ if (!existsSync5(CONFIG_FILE)) {
701
+ return { ...DEFAULT_CONFIG };
702
+ }
703
+ try {
704
+ const raw = readFileSync4(CONFIG_FILE, "utf-8");
705
+ const stored = JSON.parse(raw);
706
+ return { ...DEFAULT_CONFIG, ...stored };
707
+ } catch {
708
+ return { ...DEFAULT_CONFIG };
709
+ }
710
+ }
711
+ function saveConfig(config) {
712
+ mkdirSync2(CONFIG_DIR, { recursive: true });
713
+ const content = JSON.stringify(config, null, 2) + "\n";
714
+ writeFileSync2(CONFIG_FILE, content, "utf-8");
715
+ chmodSync(CONFIG_FILE, 384);
716
+ }
717
+ function clearAuth() {
718
+ const config = {
719
+ ...loadConfig(),
720
+ api_key: "",
721
+ developer_id: null
722
+ };
723
+ saveConfig(config);
724
+ return config;
725
+ }
726
+
727
+ // src/api-client.ts
728
+ var ApiError = class extends Error {
729
+ statusCode;
730
+ detail;
731
+ constructor(statusCode, detail) {
732
+ super(`[${statusCode}] ${detail}`);
733
+ this.statusCode = statusCode;
734
+ this.detail = detail;
735
+ this.name = "ApiError";
736
+ }
737
+ };
738
+ var ApiClient = class _ApiClient {
739
+ baseUrl;
740
+ apiKey;
741
+ _developerId;
742
+ constructor(options) {
743
+ const config = loadConfig();
744
+ this.baseUrl = (options?.apiUrl ?? config.api_url ?? "").replace(/\/+$/, "");
745
+ this.apiKey = options?.apiKey ?? config.api_key ?? "";
746
+ this._developerId = config.developer_id ?? null;
747
+ }
748
+ // -- 內部 ---------------------------------------------------------------
749
+ headers() {
750
+ const h = { Accept: "application/json" };
751
+ if (this.apiKey) {
752
+ h["X-Developer-Key"] = this.apiKey;
753
+ }
754
+ return h;
755
+ }
756
+ url(path) {
757
+ return `${this.baseUrl}${path}`;
758
+ }
759
+ /** 統一處理回應:成功回傳 JSON,失敗拋出 ApiError。 */
760
+ static async handleResponse(resp) {
761
+ if (resp.ok) {
762
+ const contentType = resp.headers.get("content-type") ?? "";
763
+ if (contentType.startsWith("application/json")) {
764
+ return resp.json();
765
+ }
766
+ return resp.text();
767
+ }
768
+ let detail;
769
+ try {
770
+ const body = await resp.json();
771
+ detail = String(body.detail ?? resp.statusText);
772
+ } catch {
773
+ detail = resp.statusText;
774
+ }
775
+ throw new ApiError(resp.status, detail);
776
+ }
777
+ // -- 公開方法 -----------------------------------------------------------
778
+ /** GET 請求。 */
779
+ async get(path, params) {
780
+ let fullUrl = this.url(path);
781
+ if (params) {
782
+ const qs = new URLSearchParams(params).toString();
783
+ fullUrl = `${fullUrl}?${qs}`;
784
+ }
785
+ const resp = await fetch(fullUrl, {
786
+ method: "GET",
787
+ headers: this.headers(),
788
+ signal: AbortSignal.timeout(3e4)
789
+ });
790
+ return _ApiClient.handleResponse(resp);
791
+ }
792
+ /** POST 請求(JSON)。 */
793
+ async post(path, body) {
794
+ const resp = await fetch(this.url(path), {
795
+ method: "POST",
796
+ headers: { ...this.headers(), "Content-Type": "application/json" },
797
+ body: body ? JSON.stringify(body) : void 0,
798
+ signal: AbortSignal.timeout(3e4)
799
+ });
800
+ return _ApiClient.handleResponse(resp);
801
+ }
802
+ /** POST 上傳檔案(multipart)。 */
803
+ async postFile(path, filePath, field = "file") {
804
+ const fileBuffer = readFileSync5(filePath);
805
+ const fileName = basename(filePath);
806
+ const formData = new FormData();
807
+ formData.append(field, new Blob([fileBuffer], { type: "application/gzip" }), fileName);
808
+ const headers = this.headers();
809
+ delete headers["Content-Type"];
810
+ const resp = await fetch(this.url(path), {
811
+ method: "POST",
812
+ headers,
813
+ body: formData,
814
+ signal: AbortSignal.timeout(12e4)
815
+ });
816
+ return _ApiClient.handleResponse(resp);
817
+ }
818
+ // -- 屬性 ---------------------------------------------------------------
819
+ get developerId() {
820
+ return this._developerId;
821
+ }
822
+ get authenticated() {
823
+ return Boolean(this.apiKey);
824
+ }
825
+ };
826
+
827
+ // src/commands/submit.ts
828
+ function findArchive(pluginPath) {
829
+ const dir = resolve5(pluginPath, "..");
830
+ if (!existsSync6(dir)) return null;
831
+ const archives = readdirSync2(dir).filter((f) => f.endsWith(".tar.gz")).map((f) => ({
832
+ name: f,
833
+ path: join6(dir, f),
834
+ mtime: statSync2(join6(dir, f)).mtimeMs
835
+ })).sort((a, b) => b.mtime - a.mtime);
836
+ return archives.length > 0 ? archives[0].path : null;
837
+ }
838
+ function formatSize2(bytes) {
839
+ const kb = bytes / 1024;
840
+ return kb < 1024 ? `${kb.toFixed(1)} KB` : `${(kb / 1024).toFixed(2)} MB`;
841
+ }
842
+ var submitCommand = new Command5("submit").description("\u63D0\u4EA4\u63D2\u4EF6\u5305\u81F3 Marketplace\uFF0C\u89F8\u767C AI \u5BE9\u6838").option("-f, --file <file>", ".tar.gz \u6A94\u6848\u8DEF\u5F91").option("--path <path>", "\u63D2\u4EF6\u76EE\u9304\uFF08\u81EA\u52D5\u5C0B\u627E .tar.gz\uFF09", ".").action(async (opts) => {
843
+ const client = new ApiClient();
844
+ if (!client.authenticated) {
845
+ process.stderr.write(chalk5.red("\u5C1A\u672A\u767B\u5165\u3002\u8ACB\u5148\u57F7\u884C `cyodaa login`\u3002\n"));
846
+ process.exit(1);
847
+ }
848
+ let archivePath;
849
+ if (opts.file) {
850
+ archivePath = resolve5(opts.file);
851
+ if (!existsSync6(archivePath)) {
852
+ process.stderr.write(chalk5.red(`\u6A94\u6848\u4E0D\u5B58\u5728\uFF1A${archivePath}
853
+ `));
854
+ process.exit(1);
855
+ }
856
+ } else {
857
+ const found = findArchive(opts.path);
858
+ if (!found) {
859
+ process.stderr.write(
860
+ chalk5.red("\u627E\u4E0D\u5230 .tar.gz \u6A94\u6848\u3002\u8ACB\u5148\u57F7\u884C `cyodaa pack`\u3002\n")
861
+ );
862
+ process.exit(1);
863
+ }
864
+ archivePath = found;
865
+ }
866
+ const size = statSync2(archivePath).size;
867
+ const fileName = archivePath.split("/").pop() ?? archivePath;
868
+ process.stdout.write(
869
+ chalk5.bold(`
870
+ \u63D0\u4EA4\u6A94\u6848\uFF1A`) + `${fileName} (${formatSize2(size)})
871
+
872
+ `
873
+ );
874
+ const spinner = ora("\u4E0A\u50B3\u4E2D...").start();
875
+ try {
876
+ const result = await client.postFile(
877
+ "/v1/developers/plugins/submit",
878
+ archivePath,
879
+ "file"
880
+ );
881
+ spinner.succeed("\u4E0A\u50B3\u5B8C\u6210");
882
+ const pluginId = result.plugin_id ?? "N/A";
883
+ const reviewStatus = result.review_status ?? "pending";
884
+ process.stdout.write("\n");
885
+ process.stdout.write(chalk5.green(" \u63D0\u4EA4\u6210\u529F\uFF01AI \u5BE9\u6838\u5DF2\u555F\u52D5\u3002\n\n"));
886
+ process.stdout.write(` \u63D2\u4EF6 ID\uFF1A${pluginId}
887
+ `);
888
+ process.stdout.write(` \u5BE9\u6838\u72C0\u614B\uFF1A${reviewStatus}
889
+
890
+ `);
891
+ process.stdout.write(chalk5.dim(` \u67E5\u770B\u5BE9\u6838\u9032\u5EA6\uFF1A\`cyodaa status ${pluginId}\`
892
+
893
+ `));
894
+ } catch (error) {
895
+ spinner.fail("\u4E0A\u50B3\u5931\u6557");
896
+ if (error instanceof ApiError) {
897
+ process.stderr.write(chalk5.red(`\u63D0\u4EA4\u5931\u6557\uFF1A${error.detail}
898
+ `));
899
+ process.exit(1);
900
+ }
901
+ throw error;
902
+ }
903
+ });
904
+
905
+ // src/commands/login.ts
906
+ import { createInterface as createInterface2 } from "readline/promises";
907
+ import { stdin as stdin2, stdout as stdout2 } from "process";
908
+ import chalk6 from "chalk";
909
+ import { Command as Command6 } from "commander";
910
+ async function promptInput(question, hide = false) {
911
+ const rl = createInterface2({ input: stdin2, output: stdout2 });
912
+ try {
913
+ if (hide) {
914
+ const originalWrite = stdout2.write.bind(stdout2);
915
+ stdout2.write = (...args) => {
916
+ const text = typeof args[0] === "string" ? args[0] : "";
917
+ if (text.includes(question)) {
918
+ return originalWrite(...args);
919
+ }
920
+ return originalWrite("");
921
+ };
922
+ const answer = await rl.question(question);
923
+ stdout2.write = originalWrite;
924
+ stdout2.write("\n");
925
+ return answer;
926
+ }
927
+ return await rl.question(question);
928
+ } finally {
929
+ rl.close();
930
+ }
931
+ }
932
+ var loginCommand = new Command6("login").description("\u767B\u5165 Marketplace \u958B\u767C\u8005\u5E33\u865F").option("--email <email>", "\u958B\u767C\u8005 Email").option("--password <password>", "\u5BC6\u78BC").action(async (opts) => {
933
+ try {
934
+ const email = opts.email ?? await promptInput(chalk6.cyan("Email: "));
935
+ const password = opts.password ?? await promptInput(chalk6.cyan("\u5BC6\u78BC: "), true);
936
+ if (!email || !password) {
937
+ process.stderr.write(chalk6.red("Email \u8207\u5BC6\u78BC\u70BA\u5FC5\u586B\u3002\n"));
938
+ process.exit(1);
939
+ }
940
+ const client = new ApiClient();
941
+ const result = await client.post("/v1/developers/login", {
942
+ email,
943
+ password
944
+ });
945
+ const apiKey = result.api_key ?? "";
946
+ const developerId = result.developer_id ?? null;
947
+ const displayName = result.display_name ?? email;
948
+ const config = { ...loadConfig(), api_key: apiKey, developer_id: developerId };
949
+ saveConfig(config);
950
+ process.stdout.write("\n");
951
+ process.stdout.write(chalk6.green(` \u767B\u5165\u6210\u529F\uFF01\u6B61\u8FCE\u56DE\u4F86\uFF0C${displayName}
952
+ `));
953
+ process.stdout.write(` Developer ID: ${developerId}
954
+
955
+ `);
956
+ } catch (error) {
957
+ if (error instanceof ApiError) {
958
+ process.stderr.write(chalk6.red(`\u767B\u5165\u5931\u6557\uFF1A${error.detail}
959
+ `));
960
+ process.exit(1);
961
+ }
962
+ throw error;
963
+ }
964
+ });
965
+
966
+ // src/commands/logout.ts
967
+ import chalk7 from "chalk";
968
+ import { Command as Command7 } from "commander";
969
+ var logoutCommand = new Command7("logout").description("\u767B\u51FA\uFF08\u6E05\u9664\u672C\u6A5F API Key\uFF09").action(() => {
970
+ clearAuth();
971
+ process.stdout.write(chalk7.yellow("\u5DF2\u767B\u51FA\uFF0CAPI Key \u5DF2\u6E05\u9664\u3002\n"));
972
+ });
973
+
974
+ // src/commands/whoami.ts
975
+ import chalk8 from "chalk";
976
+ import { Command as Command8 } from "commander";
977
+ var whoamiCommand = new Command8("whoami").description("\u986F\u793A\u7576\u524D\u767B\u5165\u7684\u958B\u767C\u8005\u8CC7\u8A0A").action(async () => {
978
+ const client = new ApiClient();
979
+ if (!client.authenticated) {
980
+ process.stderr.write(chalk8.red("\u5C1A\u672A\u767B\u5165\u3002\u8ACB\u5148\u57F7\u884C `cyodaa login`\u3002\n"));
981
+ process.exit(1);
982
+ }
983
+ try {
984
+ const info = await client.get("/v1/developers/me");
985
+ process.stdout.write("\n");
986
+ process.stdout.write(chalk8.cyan(" \u958B\u767C\u8005\u8CC7\u8A0A\n"));
987
+ process.stdout.write(chalk8.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
988
+ process.stdout.write(` \u540D\u7A31\uFF1A${chalk8.bold(info.display_name ?? "N/A")}
989
+ `);
990
+ process.stdout.write(` Email\uFF1A${info.email ?? "N/A"}
991
+ `);
992
+ process.stdout.write(` Developer ID\uFF1A${info.id ?? "N/A"}
993
+ `);
994
+ process.stdout.write(` \u5DF2\u767C\u4F48\u63D2\u4EF6\u6578\uFF1A${info.plugin_count ?? 0}
995
+
996
+ `);
997
+ } catch (error) {
998
+ if (error instanceof ApiError) {
999
+ process.stderr.write(chalk8.red(`\u7121\u6CD5\u53D6\u5F97\u5E33\u865F\u8CC7\u8A0A\uFF1A${error.detail}
1000
+ `));
1001
+ process.exit(1);
1002
+ }
1003
+ throw error;
1004
+ }
1005
+ });
1006
+
1007
+ // src/cli.ts
1008
+ var program = new Command9();
1009
+ program.name("cyodaa").description("Cyodaa Plugin CLI \u2014 \u5EFA\u7ACB\u3001\u9A57\u8B49\u3001\u6253\u5305\u4E26\u767C\u4F48 Cyodaa \u63D2\u4EF6").version("0.1.0", "-v, --version", "\u986F\u793A\u7248\u672C\u865F");
1010
+ program.addCommand(initCommand);
1011
+ program.addCommand(validateCommand);
1012
+ program.addCommand(buildCommand);
1013
+ program.addCommand(packCommand);
1014
+ program.addCommand(submitCommand);
1015
+ program.addCommand(loginCommand);
1016
+ program.addCommand(logoutCommand);
1017
+ program.addCommand(whoamiCommand);
1018
+ program.parseAsync(process.argv).catch((error) => {
1019
+ process.stderr.write(`\u672A\u9810\u671F\u7684\u932F\u8AA4\uFF1A${error}
1020
+ `);
1021
+ process.exit(1);
1022
+ });
1023
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/init.ts","../src/commands/templates.ts","../src/commands/validate.ts","../src/commands/build.ts","../src/commands/pack.ts","../src/commands/submit.ts","../src/api-client.ts","../src/config.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/whoami.ts"],"sourcesContent":["/**\n * Cyodaa CLI 主入口\n *\n * 使用 Commander.js 註冊所有子指令。\n * shebang 由 tsup banner 自動加入。\n */\n\nimport { Command } from 'commander';\nimport { initCommand } from './commands/init.js';\nimport { validateCommand } from './commands/validate.js';\nimport { buildCommand } from './commands/build.js';\nimport { packCommand } from './commands/pack.js';\nimport { submitCommand } from './commands/submit.js';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { whoamiCommand } from './commands/whoami.js';\n\n// ---------------------------------------------------------------------------\n// 主程式\n// ---------------------------------------------------------------------------\n\nconst program = new Command();\n\nprogram\n .name('cyodaa')\n .description('Cyodaa Plugin CLI — 建立、驗證、打包並發佈 Cyodaa 插件')\n .version('0.1.0', '-v, --version', '顯示版本號');\n\n// 註冊子指令\nprogram.addCommand(initCommand);\nprogram.addCommand(validateCommand);\nprogram.addCommand(buildCommand);\nprogram.addCommand(packCommand);\nprogram.addCommand(submitCommand);\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(whoamiCommand);\n\n// 解析指令列引數\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n process.stderr.write(`未預期的錯誤:${error}\\n`);\n process.exit(1);\n});\n","/**\n * cyodaa init — 初始化插件專案。\n *\n * 模板內容以字串常量嵌入(Node.js 無 importlib.resources)。\n */\n\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { createInterface } from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\nimport { join, resolve } from 'node:path';\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport { TEMPLATES, type TemplateKey } from './templates.js';\n\n// ---------------------------------------------------------------------------\n// 互動提示\n// ---------------------------------------------------------------------------\n\nasync function promptChoice(\n question: string,\n choices: readonly string[],\n defaultChoice: string,\n): Promise<string> {\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const answer = await rl.question(question);\n const value = answer.trim() || defaultChoice;\n if (choices.includes(value)) {\n return value;\n }\n process.stdout.write(chalk.red(` 請輸入 ${choices.join(' / ')} 其中之一\\n`));\n }\n } finally {\n rl.close();\n }\n}\n\nasync function promptText(question: string, defaultValue: string): Promise<string> {\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n const answer = await rl.question(question);\n return answer.trim() || defaultValue;\n } finally {\n rl.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// 模板選單\n// ---------------------------------------------------------------------------\n\nconst TEMPLATE_MENU: ReadonlyArray<{\n readonly key: string;\n readonly id: TemplateKey;\n readonly desc: string;\n}> = [\n { key: '1', id: 'icon_only', desc: '僅圖標插件 — 最輕量,只需提供圖標與 manifest' },\n { key: '2', id: 'frontend_only', desc: '前端插件 — 含 React 組件,無後端' },\n { key: '3', id: 'backend_only', desc: '後端插件 — 含 FastAPI 端點,無前端' },\n { key: '4', id: 'full_stack', desc: '全端插件 — 前後端完整架構' },\n];\n\n// ---------------------------------------------------------------------------\n// 檔案寫入\n// ---------------------------------------------------------------------------\n\nfunction writeTemplateFiles(\n templateId: TemplateKey,\n dest: string,\n projectName: string,\n): void {\n const template = TEMPLATES[templateId];\n\n for (const file of template.files) {\n const filePath = join(dest, file.path);\n const dir = join(filePath, '..');\n mkdirSync(dir, { recursive: true });\n\n // 替換 manifest 中的名稱\n let content = file.content;\n if (file.path === 'manifest.yaml') {\n const displayName = projectName.replace(/-/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n content = content\n .replace(/^name: .+$/m, `name: ${projectName}`)\n .replace(/^display_name: .+$/m, `display_name: ${displayName}`);\n }\n\n writeFileSync(filePath, content, 'utf-8');\n }\n}\n\n// ---------------------------------------------------------------------------\n// 指令定義\n// ---------------------------------------------------------------------------\n\nexport const initCommand = new Command('init')\n .argument('[name]', '插件名稱')\n .description('初始化新的 Cyodaa 插件專案')\n .action(async (name?: string) => {\n // 1. 專案名稱\n const projectName =\n name ?? (await promptText(chalk.cyan('插件名稱 (my-plugin): '), 'my-plugin'));\n\n const dest = resolve(process.cwd(), projectName);\n if (existsSync(dest)) {\n process.stderr.write(chalk.red(`目錄 ${dest} 已存在,請選擇其他名稱。\\n`));\n process.exit(1);\n }\n\n // 2. 模板選擇\n process.stdout.write('\\n' + chalk.bold('請選擇插件模板:') + '\\n\\n');\n for (const item of TEMPLATE_MENU) {\n process.stdout.write(` ${chalk.cyan(item.key)} ${item.desc}\\n`);\n }\n process.stdout.write('\\n');\n\n const choice = await promptChoice(\n '選擇模板 (1): ',\n TEMPLATE_MENU.map((t) => t.key),\n '1',\n );\n const selected = TEMPLATE_MENU.find((t) => t.key === choice)!;\n\n // 3. 複製模板\n mkdirSync(dest, { recursive: true });\n writeTemplateFiles(selected.id, dest, projectName);\n\n // 4. 完成提示\n process.stdout.write('\\n');\n process.stdout.write(chalk.green(' 插件專案已建立!\\n\\n'));\n process.stdout.write(` 目錄:${dest}\\n`);\n process.stdout.write(` 模板:${selected.desc}\\n\\n`);\n process.stdout.write(chalk.dim(' 下一步:\\n'));\n process.stdout.write(chalk.dim(` cd ${projectName}\\n`));\n process.stdout.write(chalk.dim(' cyodaa validate # 驗證 manifest\\n'));\n process.stdout.write(chalk.dim(' cyodaa dev # 啟動開發伺服器\\n\\n'));\n });\n","/**\n * 內建模板定義 — 以字串常量嵌入。\n *\n * 四種模板:icon_only / frontend_only / backend_only / full_stack\n */\n\n// ---------------------------------------------------------------------------\n// 型別\n// ---------------------------------------------------------------------------\n\nexport interface TemplateFile {\n readonly path: string;\n readonly content: string;\n}\n\nexport interface Template {\n readonly files: readonly TemplateFile[];\n}\n\nexport type TemplateKey = 'icon_only' | 'frontend_only' | 'backend_only' | 'full_stack';\n\n// ---------------------------------------------------------------------------\n// 模板內容\n// ---------------------------------------------------------------------------\n\nconst ICON_ONLY_MANIFEST = `name: my-icon-plugin\nversion: 1.0.0\ndisplay_name: My Icon Plugin\ndescription: 僅圖標插件 — 在 Marketplace 中顯示品牌圖標\ncategory: utility\nauthor:\n name: Your Name\n email: you@example.com\npermissions: []\nmin_platform_version: \"2.0\"\nicon: icon.svg\ntags:\n - branding\n - icon\n`;\n\nconst FRONTEND_ONLY_MANIFEST = `name: my-frontend-plugin\nversion: 1.0.0\ndisplay_name: My Frontend Plugin\ndescription: 前端插件 — 提供自訂 React 組件\ncategory: visualization\nauthor:\n name: Your Name\n email: you@example.com\npermissions:\n - read:data\nmin_platform_version: \"2.0\"\nentry_points:\n frontend: src/index.tsx\nicon: icon.svg\ntags:\n - frontend\n - dashboard\n`;\n\nconst FRONTEND_INDEX_TSX = `/**\n * Cyodaa 前端插件入口\n *\n * 此檔案是插件的前端進入點。\n * 請在此實作您的 React 組件。\n */\n\nimport React from 'react';\n\ninterface PluginProps {\n /** 平台提供的上下文 */\n context: {\n tenantId: string;\n userId: number;\n locale: string;\n };\n}\n\n/**\n * 插件主組件\n */\nconst MyPlugin: React.FC<PluginProps> = ({ context }) => {\n return (\n <div style={{ padding: '1rem' }}>\n <h2>My Frontend Plugin</h2>\n <p>歡迎使用 Cyodaa 插件系統!</p>\n <p>Tenant: {context.tenantId}</p>\n </div>\n );\n};\n\nexport default MyPlugin;\n`;\n\nconst BACKEND_ONLY_MANIFEST = `name: my-backend-plugin\nversion: 1.0.0\ndisplay_name: My Backend Plugin\ndescription: 後端插件 — 提供自訂 API 端點\ncategory: integration\nauthor:\n name: Your Name\n email: you@example.com\npermissions:\n - read:data\n - write:data\nmin_platform_version: \"2.0\"\nentry_points:\n backend: src/main.py\nicon: icon.svg\ntags:\n - backend\n - api\n`;\n\nconst BACKEND_MAIN_PY = `\"\"\"Cyodaa 後端插件入口。\n\n此檔案是插件的後端進入點。\n請在此定義您的 FastAPI 路由。\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom fastapi import APIRouter\n\nrouter = APIRouter(prefix=\"/plugin\", tags=[\"my-backend-plugin\"])\n\n\n@router.get(\"/hello\")\nasync def hello() -> dict[str, str]:\n \"\"\"插件範例端點。\"\"\"\n return {\"message\": \"歡迎使用 Cyodaa 插件系統!\"}\n\n\n@router.get(\"/health\")\nasync def health() -> dict[str, str]:\n \"\"\"健康檢查。\"\"\"\n return {\"status\": \"ok\"}\n`;\n\nconst FULL_STACK_MANIFEST = `name: my-fullstack-plugin\nversion: 1.0.0\ndisplay_name: My Full Stack Plugin\ndescription: 全端插件 — 包含前端組件與後端 API\ncategory: analytics\nauthor:\n name: Your Name\n email: you@example.com\npermissions:\n - read:data\n - write:data\n - read:skills\nmin_platform_version: \"2.0\"\nentry_points:\n frontend: src/frontend/index.tsx\n backend: src/backend/main.py\nicon: icon.svg\ntags:\n - fullstack\n - analytics\n - dashboard\n`;\n\nconst FULL_STACK_FRONTEND_TSX = `/**\n * Cyodaa 全端插件 — 前端入口\n *\n * 此檔案是插件的前端進入點。\n * 可透過 context.apiBase 呼叫插件後端 API。\n */\n\nimport React, { useEffect, useState } from 'react';\n\ninterface PluginProps {\n /** 平台提供的上下文 */\n context: {\n tenantId: string;\n userId: number;\n locale: string;\n /** 插件後端 API 基礎路徑 */\n apiBase: string;\n };\n}\n\n/**\n * 全端插件主組件\n */\nconst MyFullStackPlugin: React.FC<PluginProps> = ({ context }) => {\n const [message, setMessage] = useState<string>('載入中...');\n\n useEffect(() => {\n fetch(\\`\\${context.apiBase}/plugin/hello\\`)\n .then((res) => res.json())\n .then((data) => setMessage(data.message))\n .catch(() => setMessage('無法連線至插件後端'));\n }, [context.apiBase]);\n\n return (\n <div style={{ padding: '1rem' }}>\n <h2>My Full Stack Plugin</h2>\n <p>{message}</p>\n <p>Tenant: {context.tenantId}</p>\n </div>\n );\n};\n\nexport default MyFullStackPlugin;\n`;\n\nconst FULL_STACK_BACKEND_PY = `\"\"\"Cyodaa 全端插件 — 後端入口。\n\n此檔案是全端插件的後端進入點。\n前端可透過 context.apiBase 呼叫這些端點。\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom fastapi import APIRouter\n\nrouter = APIRouter(prefix=\"/plugin\", tags=[\"my-fullstack-plugin\"])\n\n\n@router.get(\"/hello\")\nasync def hello() -> dict[str, str]:\n \"\"\"插件範例端點。\"\"\"\n return {\"message\": \"來自全端插件後端的問候!\"}\n\n\n@router.get(\"/health\")\nasync def health() -> dict[str, str]:\n \"\"\"健康檢查。\"\"\"\n return {\"status\": \"ok\"}\n\n\n@router.get(\"/data\")\nasync def get_data() -> dict[str, object]:\n \"\"\"範例資料端點。\n\n 在此接入平台 Data Platform 或自定資料源。\n \"\"\"\n return {\n \"items\": [],\n \"total\": 0,\n \"message\": \"請替換此端點的實作以連接真實資料。\",\n }\n`;\n\n// ---------------------------------------------------------------------------\n// 模板匯出\n// ---------------------------------------------------------------------------\n\nexport const TEMPLATES: Record<TemplateKey, Template> = {\n icon_only: {\n files: [{ path: 'manifest.yaml', content: ICON_ONLY_MANIFEST }],\n },\n\n frontend_only: {\n files: [\n { path: 'manifest.yaml', content: FRONTEND_ONLY_MANIFEST },\n { path: 'src/index.tsx', content: FRONTEND_INDEX_TSX },\n ],\n },\n\n backend_only: {\n files: [\n { path: 'manifest.yaml', content: BACKEND_ONLY_MANIFEST },\n { path: 'src/main.py', content: BACKEND_MAIN_PY },\n ],\n },\n\n full_stack: {\n files: [\n { path: 'manifest.yaml', content: FULL_STACK_MANIFEST },\n { path: 'src/frontend/index.tsx', content: FULL_STACK_FRONTEND_TSX },\n { path: 'src/backend/main.py', content: FULL_STACK_BACKEND_PY },\n ],\n },\n};\n","/**\n * cyodaa validate — 驗證 manifest.yaml 結構。\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport yaml from 'js-yaml';\n\n// ---------------------------------------------------------------------------\n// 驗證常量\n// ---------------------------------------------------------------------------\n\nconst REQUIRED_FIELDS: ReadonlyArray<{\n readonly field: string;\n readonly type: string;\n readonly label: string;\n}> = [\n { field: 'name', type: 'string', label: '插件名稱(英文,小寫 + 連字符)' },\n { field: 'version', type: 'string', label: '語意化版本號(如 1.0.0)' },\n { field: 'display_name', type: 'string', label: '顯示名稱' },\n { field: 'description', type: 'string', label: '插件描述' },\n { field: 'category', type: 'string', label: '分類(analytics / integration / visualization / utility / ai)' },\n];\n\nconst VALID_CATEGORIES = new Set([\n 'analytics', 'integration', 'visualization', 'utility', 'ai',\n]);\n\nconst VALID_PERMISSIONS = new Set([\n 'read:data', 'write:data', 'read:skills', 'write:skills',\n 'read:users', 'execute:agents', 'admin',\n]);\n\n// ---------------------------------------------------------------------------\n// 驗證結果型別\n// ---------------------------------------------------------------------------\n\ninterface ValidationIssue {\n readonly field: string;\n readonly level: 'error' | 'warning';\n readonly message: string;\n}\n\n// ---------------------------------------------------------------------------\n// 驗證邏輯\n// ---------------------------------------------------------------------------\n\nfunction validateManifest(manifest: Record<string, unknown>, cwd: string): readonly ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n // 必填欄位\n for (const { field, type, label } of REQUIRED_FIELDS) {\n const value = manifest[field];\n if (value === undefined || value === null) {\n issues.push({ field, level: 'error', message: `缺少必填欄位:${label}` });\n } else if (typeof value !== type) {\n issues.push({\n field,\n level: 'error',\n message: `型別錯誤:預期 ${type},實際 ${typeof value}`,\n });\n }\n }\n\n // name 格式(kebab-case)\n const name = manifest.name;\n if (typeof name === 'string' && name) {\n if (!/^[a-z][a-z0-9-]*$/.test(name)) {\n issues.push({\n field: 'name',\n level: 'error',\n message: '名稱只能包含小寫英文、數字與連字符,且必須以字母開頭',\n });\n }\n }\n\n // version 格式(semver)\n const version = manifest.version;\n if (typeof version === 'string' && version) {\n if (!/^\\d+\\.\\d+\\.\\d+$/.test(version)) {\n issues.push({\n field: 'version',\n level: 'warning',\n message: '建議使用語意化版本格式(X.Y.Z)',\n });\n }\n }\n\n // category 值\n const category = manifest.category;\n if (typeof category === 'string' && category && !VALID_CATEGORIES.has(category)) {\n const valid = [...VALID_CATEGORIES].sort().join(', ');\n issues.push({\n field: 'category',\n level: 'error',\n message: `不支援的分類 '${category}',可選:${valid}`,\n });\n }\n\n // permissions 值\n const permissions = manifest.permissions;\n if (Array.isArray(permissions)) {\n for (const perm of permissions) {\n if (!VALID_PERMISSIONS.has(String(perm))) {\n const valid = [...VALID_PERMISSIONS].sort().join(', ');\n issues.push({\n field: 'permissions',\n level: 'warning',\n message: `未知權限 '${perm}',可選:${valid}`,\n });\n }\n }\n }\n\n // author 結構\n const author = manifest.author;\n if (author !== undefined) {\n if (typeof author !== 'object' || author === null || Array.isArray(author)) {\n issues.push({\n field: 'author',\n level: 'error',\n message: 'author 必須是物件(含 name / email)',\n });\n } else if (!('name' in author)) {\n issues.push({\n field: 'author.name',\n level: 'warning',\n message: '建議填寫作者名稱',\n });\n }\n }\n\n // entry_points 存在性檢查\n const entryPoints = manifest.entry_points;\n if (typeof entryPoints === 'object' && entryPoints !== null && !Array.isArray(entryPoints)) {\n for (const [epKey, epPath] of Object.entries(entryPoints as Record<string, unknown>)) {\n if (typeof epPath === 'string' && !existsSync(join(cwd, epPath))) {\n issues.push({\n field: `entry_points.${epKey}`,\n level: 'warning',\n message: `入口檔案 '${epPath}' 不存在`,\n });\n }\n }\n }\n\n return issues;\n}\n\n// ---------------------------------------------------------------------------\n// 輸出格式化\n// ---------------------------------------------------------------------------\n\nfunction printResults(issues: readonly ValidationIssue[]): void {\n if (issues.length === 0) {\n process.stdout.write('\\n');\n process.stdout.write(chalk.green(' manifest.yaml 驗證通過!所有欄位皆合法。\\n\\n'));\n return;\n }\n\n process.stdout.write('\\n');\n process.stdout.write(chalk.bold(' 驗證結果\\n'));\n process.stdout.write(' ' + '─'.repeat(60) + '\\n');\n\n let hasError = false;\n\n for (const issue of issues) {\n const isError = issue.level === 'error';\n if (isError) hasError = true;\n\n const levelStr = isError\n ? chalk.red('錯誤')\n : chalk.yellow('警告');\n const fieldStr = chalk.cyan(issue.field);\n\n process.stdout.write(` ${levelStr} ${fieldStr} ${issue.message}\\n`);\n }\n\n process.stdout.write(' ' + '─'.repeat(60) + '\\n');\n\n if (hasError) {\n process.stdout.write(chalk.red('\\n 存在錯誤,請修正後重新驗證。\\n\\n'));\n } else {\n process.stdout.write(\n chalk.yellow('\\n 有警告,但不影響提交。建議修正以獲得更好的審核結果。\\n\\n'),\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// 指令定義\n// ---------------------------------------------------------------------------\n\nexport const validateCommand = new Command('validate')\n .description('驗證當前目錄的 manifest.yaml')\n .option('-p, --path <path>', '插件目錄(預設為當前目錄)', '.')\n .action((opts: { path: string }) => {\n const pluginDir = resolve(opts.path);\n const manifestPath = join(pluginDir, 'manifest.yaml');\n\n if (!existsSync(manifestPath)) {\n process.stderr.write(chalk.red('找不到 manifest.yaml,請確認您在插件目錄中。\\n'));\n process.stderr.write(chalk.dim('提示:使用 `cyodaa init` 建立新插件專案。\\n'));\n process.exit(1);\n }\n\n let manifest: Record<string, unknown>;\n try {\n const raw = readFileSync(manifestPath, 'utf-8');\n manifest = yaml.load(raw) as Record<string, unknown>;\n } catch (error) {\n process.stderr.write(chalk.red(`manifest.yaml 解析失敗:${error}\\n`));\n process.exit(1);\n }\n\n if (typeof manifest !== 'object' || manifest === null || Array.isArray(manifest)) {\n process.stderr.write(chalk.red('manifest.yaml 根節點必須是物件。\\n'));\n process.exit(1);\n }\n\n const issues = validateManifest(manifest, pluginDir);\n printResults(issues);\n\n const hasError = issues.some((i) => i.level === 'error');\n if (hasError) {\n process.exit(1);\n }\n });\n","/**\n * cyodaa build — 建構插件前端 bundle。\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { execSync } from 'node:child_process';\nimport { join, resolve } from 'node:path';\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport yaml from 'js-yaml';\n\n// ---------------------------------------------------------------------------\n// 型別\n// ---------------------------------------------------------------------------\n\ninterface Manifest {\n readonly name?: string;\n readonly display_name?: string;\n readonly entry_points?: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// 指令定義\n// ---------------------------------------------------------------------------\n\nexport const buildCommand = new Command('build')\n .description('建構插件(前端 bundle / 後端打包)')\n .option('--path <path>', '插件目錄', '.')\n .action((opts: { path: string }) => {\n const pluginDir = resolve(opts.path);\n const manifestPath = join(pluginDir, 'manifest.yaml');\n\n if (!existsSync(manifestPath)) {\n process.stderr.write(chalk.red('找不到 manifest.yaml,請確認您在插件目錄中。\\n'));\n process.exit(1);\n }\n\n const raw = readFileSync(manifestPath, 'utf-8');\n const manifest = yaml.load(raw) as Manifest;\n const entryPoints = manifest.entry_points ?? {};\n\n const hasFrontend = 'frontend' in entryPoints;\n const hasBackend = 'backend' in entryPoints;\n\n if (!hasFrontend && !hasBackend) {\n process.stdout.write(chalk.yellow('此插件沒有定義 entry_points,無需建構。\\n'));\n return;\n }\n\n const displayName = manifest.display_name ?? manifest.name ?? '(unknown)';\n process.stdout.write(chalk.bold(`\\n建構插件:${displayName}\\n\\n`));\n\n // 前端建構\n if (hasFrontend) {\n const packageJsonPath = join(pluginDir, 'package.json');\n\n if (existsSync(packageJsonPath)) {\n process.stdout.write(` ${chalk.cyan('前端')} 執行 npm run build ...\\n`);\n\n try {\n execSync('npm run build', {\n cwd: pluginDir,\n stdio: 'pipe',\n timeout: 120_000,\n });\n process.stdout.write(` ${chalk.green('前端建構成功')}\\n`);\n } catch (error) {\n const stderr = (error as { stderr?: Buffer })?.stderr?.toString() ?? '';\n process.stderr.write(` ${chalk.red('前端建構失敗')}\\n`);\n if (stderr) {\n process.stderr.write(`${stderr}\\n`);\n }\n process.exit(1);\n }\n } else {\n process.stdout.write(` ${chalk.yellow('前端:找不到 package.json,跳過 npm build。')}\\n`);\n process.stdout.write(\n chalk.dim(' 提示:如使用其他建構工具,請手動建構後執行 `cyodaa pack`。\\n'),\n );\n }\n }\n\n // 後端檢查\n if (hasBackend) {\n const backendEntry = join(pluginDir, entryPoints.backend!);\n if (existsSync(backendEntry)) {\n process.stdout.write(` ${chalk.green('後端入口檔案存在')}\\n`);\n } else {\n process.stderr.write(\n ` ${chalk.red(`後端入口 '${entryPoints.backend}' 不存在`)}\\n`,\n );\n process.exit(1);\n }\n }\n\n process.stdout.write('\\n');\n process.stdout.write(chalk.green(' 建構完成!下一步:`cyodaa pack` 打包插件。\\n\\n'));\n });\n","/**\n * cyodaa pack — 將插件打包為 .tar.gz。\n */\n\nimport { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';\nimport { join, resolve, relative } from 'node:path';\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport yaml from 'js-yaml';\nimport * as tar from 'tar';\n\n// ---------------------------------------------------------------------------\n// 排除清單\n// ---------------------------------------------------------------------------\n\nconst EXCLUDE_DIRS = new Set([\n 'node_modules',\n '__pycache__',\n '.git',\n '.venv',\n 'dist',\n]);\n\nconst EXCLUDE_FILES = new Set([\n '.env',\n '.DS_Store',\n]);\n\nconst EXCLUDE_EXTENSIONS = new Set([\n '.pyc',\n '.pyo',\n]);\n\nfunction shouldExclude(relativePath: string): boolean {\n const parts = relativePath.split('/');\n\n for (const part of parts) {\n if (EXCLUDE_DIRS.has(part) || EXCLUDE_FILES.has(part)) {\n return true;\n }\n }\n\n for (const ext of EXCLUDE_EXTENSIONS) {\n if (relativePath.endsWith(ext)) {\n return true;\n }\n }\n\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// 遞迴收集檔案\n// ---------------------------------------------------------------------------\n\nfunction collectFiles(baseDir: string, currentDir: string): readonly string[] {\n const results: string[] = [];\n const entries = readdirSync(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n const relPath = relative(baseDir, fullPath);\n\n if (shouldExclude(relPath)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n results.push(...collectFiles(baseDir, fullPath));\n } else if (entry.isFile()) {\n results.push(relPath);\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// 格式化檔案大小\n// ---------------------------------------------------------------------------\n\nfunction formatSize(bytes: number): string {\n const kb = bytes / 1024;\n return kb < 1024 ? `${kb.toFixed(1)} KB` : `${(kb / 1024).toFixed(2)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// 型別\n// ---------------------------------------------------------------------------\n\ninterface Manifest {\n readonly name?: string;\n readonly version?: string;\n}\n\n// ---------------------------------------------------------------------------\n// 指令定義\n// ---------------------------------------------------------------------------\n\nexport const packCommand = new Command('pack')\n .description('將插件目錄打包為 .tar.gz')\n .option('--path <path>', '插件目錄', '.')\n .option('-o, --output <output>', '輸出檔案路徑')\n .action(async (opts: { path: string; output?: string }) => {\n const pluginDir = resolve(opts.path);\n const manifestPath = join(pluginDir, 'manifest.yaml');\n\n if (!existsSync(manifestPath)) {\n process.stderr.write(chalk.red('找不到 manifest.yaml,請確認您在插件目錄中。\\n'));\n process.exit(1);\n }\n\n const raw = readFileSync(manifestPath, 'utf-8');\n const manifest = yaml.load(raw) as Manifest;\n const name = manifest.name ?? 'plugin';\n const version = manifest.version ?? '0.0.0';\n\n // 決定輸出路徑\n const archiveName = `${name}-${version}.tar.gz`;\n const archivePath = opts.output\n ? resolve(opts.output)\n : join(pluginDir, '..', archiveName);\n\n // 收集檔案\n const files = collectFiles(pluginDir, pluginDir);\n\n if (files.length === 0) {\n process.stderr.write(chalk.red('沒有可打包的檔案。\\n'));\n process.exit(1);\n }\n\n // 建立 tar.gz\n await tar.create(\n {\n gzip: true,\n file: archivePath,\n cwd: pluginDir,\n },\n [...files].sort(),\n );\n\n const size = statSync(archivePath).size;\n\n process.stdout.write('\\n');\n process.stdout.write(chalk.green(' 打包完成!\\n\\n'));\n process.stdout.write(` 檔案:${archivePath}\\n`);\n process.stdout.write(` 大小:${formatSize(size)}\\n`);\n process.stdout.write(` 包含:${files.length} 個檔案\\n\\n`);\n process.stdout.write(chalk.dim(' 下一步:`cyodaa submit` 提交至市集審核。\\n\\n'));\n });\n","/**\n * cyodaa submit — 提交插件至 Marketplace 審核。\n */\n\nimport { existsSync, readdirSync, statSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { Command } from 'commander';\nimport { ApiClient, ApiError } from '../api-client.js';\n\n// ---------------------------------------------------------------------------\n// 尋找 .tar.gz\n// ---------------------------------------------------------------------------\n\nfunction findArchive(pluginPath: string): string | null {\n const dir = resolve(pluginPath, '..');\n if (!existsSync(dir)) return null;\n\n const archives = readdirSync(dir)\n .filter((f) => f.endsWith('.tar.gz'))\n .map((f) => ({\n name: f,\n path: join(dir, f),\n mtime: statSync(join(dir, f)).mtimeMs,\n }))\n .sort((a, b) => b.mtime - a.mtime);\n\n return archives.length > 0 ? archives[0].path : null;\n}\n\n// ---------------------------------------------------------------------------\n// 格式化檔案大小\n// ---------------------------------------------------------------------------\n\nfunction formatSize(bytes: number): string {\n const kb = bytes / 1024;\n return kb < 1024 ? `${kb.toFixed(1)} KB` : `${(kb / 1024).toFixed(2)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// 型別\n// ---------------------------------------------------------------------------\n\ninterface SubmitResult {\n readonly plugin_id?: string;\n readonly review_status?: string;\n}\n\n// ---------------------------------------------------------------------------\n// 指令定義\n// ---------------------------------------------------------------------------\n\nexport const submitCommand = new Command('submit')\n .description('提交插件包至 Marketplace,觸發 AI 審核')\n .option('-f, --file <file>', '.tar.gz 檔案路徑')\n .option('--path <path>', '插件目錄(自動尋找 .tar.gz)', '.')\n .action(async (opts: { file?: string; path: string }) => {\n const client = new ApiClient();\n\n if (!client.authenticated) {\n process.stderr.write(chalk.red('尚未登入。請先執行 `cyodaa login`。\\n'));\n process.exit(1);\n }\n\n // 找到 archive\n let archivePath: string;\n\n if (opts.file) {\n archivePath = resolve(opts.file);\n if (!existsSync(archivePath)) {\n process.stderr.write(chalk.red(`檔案不存在:${archivePath}\\n`));\n process.exit(1);\n }\n } else {\n const found = findArchive(opts.path);\n if (!found) {\n process.stderr.write(\n chalk.red('找不到 .tar.gz 檔案。請先執行 `cyodaa pack`。\\n'),\n );\n process.exit(1);\n }\n archivePath = found;\n }\n\n const size = statSync(archivePath).size;\n const fileName = archivePath.split('/').pop() ?? archivePath;\n process.stdout.write(\n chalk.bold(`\\n提交檔案:`) + `${fileName} (${formatSize(size)})\\n\\n`,\n );\n\n // 上傳(使用 ora spinner)\n const spinner = ora('上傳中...').start();\n\n try {\n const result = (await client.postFile(\n '/v1/developers/plugins/submit',\n archivePath,\n 'file',\n )) as SubmitResult;\n\n spinner.succeed('上傳完成');\n\n const pluginId = result.plugin_id ?? 'N/A';\n const reviewStatus = result.review_status ?? 'pending';\n\n process.stdout.write('\\n');\n process.stdout.write(chalk.green(' 提交成功!AI 審核已啟動。\\n\\n'));\n process.stdout.write(` 插件 ID:${pluginId}\\n`);\n process.stdout.write(` 審核狀態:${reviewStatus}\\n\\n`);\n process.stdout.write(chalk.dim(` 查看審核進度:\\`cyodaa status ${pluginId}\\`\\n\\n`));\n } catch (error) {\n spinner.fail('上傳失敗');\n\n if (error instanceof ApiError) {\n process.stderr.write(chalk.red(`提交失敗:${error.detail}\\n`));\n process.exit(1);\n }\n throw error;\n }\n });\n","/**\n * HTTP 客戶端 — 與 Marketplace API 通訊。\n *\n * 使用 Node 18+ 原生 fetch,不需額外依賴。\n */\n\nimport { readFileSync } from 'node:fs';\nimport { basename } from 'node:path';\nimport { loadConfig } from './config.js';\n\n// ---------------------------------------------------------------------------\n// 例外\n// ---------------------------------------------------------------------------\n\n/** Marketplace API 回傳非 2xx 狀態碼。 */\nexport class ApiError extends Error {\n readonly statusCode: number;\n readonly detail: string;\n\n constructor(statusCode: number, detail: string) {\n super(`[${statusCode}] ${detail}`);\n this.statusCode = statusCode;\n this.detail = detail;\n this.name = 'ApiError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// 客戶端\n// ---------------------------------------------------------------------------\n\nexport class ApiClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly _developerId: number | null;\n\n constructor(options?: { apiUrl?: string; apiKey?: string }) {\n const config = loadConfig();\n this.baseUrl = (options?.apiUrl ?? config.api_url ?? '').replace(/\\/+$/, '');\n this.apiKey = options?.apiKey ?? config.api_key ?? '';\n this._developerId = config.developer_id ?? null;\n }\n\n // -- 內部 ---------------------------------------------------------------\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { Accept: 'application/json' };\n if (this.apiKey) {\n h['X-Developer-Key'] = this.apiKey;\n }\n return h;\n }\n\n private url(path: string): string {\n return `${this.baseUrl}${path}`;\n }\n\n /** 統一處理回應:成功回傳 JSON,失敗拋出 ApiError。 */\n private static async handleResponse(resp: Response): Promise<unknown> {\n if (resp.ok) {\n const contentType = resp.headers.get('content-type') ?? '';\n if (contentType.startsWith('application/json')) {\n return resp.json();\n }\n return resp.text();\n }\n\n // 嘗試從 JSON body 取得 detail\n let detail: string;\n try {\n const body = (await resp.json()) as Record<string, unknown>;\n detail = String(body.detail ?? resp.statusText);\n } catch {\n detail = resp.statusText;\n }\n throw new ApiError(resp.status, detail);\n }\n\n // -- 公開方法 -----------------------------------------------------------\n\n /** GET 請求。 */\n async get(path: string, params?: Record<string, string>): Promise<unknown> {\n let fullUrl = this.url(path);\n if (params) {\n const qs = new URLSearchParams(params).toString();\n fullUrl = `${fullUrl}?${qs}`;\n }\n\n const resp = await fetch(fullUrl, {\n method: 'GET',\n headers: this.headers(),\n signal: AbortSignal.timeout(30_000),\n });\n\n return ApiClient.handleResponse(resp);\n }\n\n /** POST 請求(JSON)。 */\n async post(path: string, body?: Record<string, unknown>): Promise<unknown> {\n const resp = await fetch(this.url(path), {\n method: 'POST',\n headers: { ...this.headers(), 'Content-Type': 'application/json' },\n body: body ? JSON.stringify(body) : undefined,\n signal: AbortSignal.timeout(30_000),\n });\n\n return ApiClient.handleResponse(resp);\n }\n\n /** POST 上傳檔案(multipart)。 */\n async postFile(path: string, filePath: string, field = 'file'): Promise<unknown> {\n const fileBuffer = readFileSync(filePath);\n const fileName = basename(filePath);\n\n const formData = new FormData();\n formData.append(field, new Blob([fileBuffer], { type: 'application/gzip' }), fileName);\n\n // 移除 Content-Type 讓 fetch 自動設定 multipart boundary\n const headers = this.headers();\n delete headers['Content-Type'];\n\n const resp = await fetch(this.url(path), {\n method: 'POST',\n headers,\n body: formData,\n signal: AbortSignal.timeout(120_000),\n });\n\n return ApiClient.handleResponse(resp);\n }\n\n // -- 屬性 ---------------------------------------------------------------\n\n get developerId(): number | null {\n return this._developerId;\n }\n\n get authenticated(): boolean {\n return Boolean(this.apiKey);\n }\n}\n","/**\n * 組態管理 — 讀寫 ~/.cyodaa/config.json\n *\n * 所有操作皆使用不可變模式:產生新物件,絕不修改原始物件。\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n// ---------------------------------------------------------------------------\n// 常量\n// ---------------------------------------------------------------------------\n\nexport const CONFIG_DIR = join(homedir(), '.cyodaa');\nexport const CONFIG_FILE = join(CONFIG_DIR, 'config.json');\n\n// ---------------------------------------------------------------------------\n// 型別\n// ---------------------------------------------------------------------------\n\nexport interface CyodaaConfig {\n readonly api_url: string;\n readonly api_key: string;\n readonly developer_id: number | null;\n readonly [key: string]: unknown;\n}\n\nconst DEFAULT_CONFIG: CyodaaConfig = {\n api_url: 'http://localhost:9850',\n api_key: '',\n developer_id: null,\n};\n\n// ---------------------------------------------------------------------------\n// 讀取\n// ---------------------------------------------------------------------------\n\n/** 讀取組態檔,不存在則回傳預設值(不可變合併)。 */\nexport function loadConfig(): CyodaaConfig {\n if (!existsSync(CONFIG_FILE)) {\n return { ...DEFAULT_CONFIG };\n }\n\n try {\n const raw = readFileSync(CONFIG_FILE, 'utf-8');\n const stored: Record<string, unknown> = JSON.parse(raw);\n // 以預設值為底,覆蓋已儲存欄位\n return { ...DEFAULT_CONFIG, ...stored } as CyodaaConfig;\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\n// ---------------------------------------------------------------------------\n// 寫入\n// ---------------------------------------------------------------------------\n\n/** 寫入組態檔(自動建立目錄,權限 0600)。 */\nexport function saveConfig(config: CyodaaConfig): void {\n mkdirSync(CONFIG_DIR, { recursive: true });\n const content = JSON.stringify(config, null, 2) + '\\n';\n writeFileSync(CONFIG_FILE, content, 'utf-8');\n // 設定檔案權限為 0600(僅擁有者可讀寫)\n chmodSync(CONFIG_FILE, 0o600);\n}\n\n// ---------------------------------------------------------------------------\n// 便利函式\n// ---------------------------------------------------------------------------\n\n/** 取得單一組態值。 */\nexport function getValue(key: string): unknown {\n return loadConfig()[key];\n}\n\n/** 設定單一組態值並回傳新組態(不可變)。 */\nexport function setValue(key: string, value: unknown): CyodaaConfig {\n const config: CyodaaConfig = { ...loadConfig(), [key]: value } as CyodaaConfig;\n saveConfig(config);\n return config;\n}\n\n/** 清除認證資訊並回傳新組態。 */\nexport function clearAuth(): CyodaaConfig {\n const config: CyodaaConfig = {\n ...loadConfig(),\n api_key: '',\n developer_id: null,\n };\n saveConfig(config);\n return config;\n}\n","/**\n * cyodaa login — 登入 Marketplace 開發者帳號。\n *\n * 使用 Node.js readline/promises 互動式提示。\n */\n\nimport { createInterface } from 'node:readline/promises';\nimport { stdin, stdout } from 'node:process';\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport { ApiClient, ApiError } from '../api-client.js';\nimport { loadConfig, saveConfig } from '../config.js';\n\n// ---------------------------------------------------------------------------\n// 互動提示\n// ---------------------------------------------------------------------------\n\nasync function promptInput(question: string, hide = false): Promise<string> {\n const rl = createInterface({ input: stdin, output: stdout });\n\n try {\n if (hide) {\n // 隱藏輸入(密碼模式)\n // 暫時攔截 stdout.write,僅顯示提示文字\n const originalWrite = stdout.write.bind(stdout);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (stdout as any).write = (...args: any[]) => {\n const text = typeof args[0] === 'string' ? args[0] : '';\n if (text.includes(question)) {\n return originalWrite(...(args as [string]));\n }\n return originalWrite('');\n };\n\n const answer = await rl.question(question);\n stdout.write = originalWrite;\n stdout.write('\\n');\n return answer;\n }\n\n return await rl.question(question);\n } finally {\n rl.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// 指令定義\n// ---------------------------------------------------------------------------\n\ninterface LoginResult {\n readonly api_key?: string;\n readonly developer_id?: number;\n readonly display_name?: string;\n}\n\nexport const loginCommand = new Command('login')\n .description('登入 Marketplace 開發者帳號')\n .option('--email <email>', '開發者 Email')\n .option('--password <password>', '密碼')\n .action(async (opts: { email?: string; password?: string }) => {\n try {\n const email = opts.email ?? (await promptInput(chalk.cyan('Email: ')));\n const password = opts.password ?? (await promptInput(chalk.cyan('密碼: '), true));\n\n if (!email || !password) {\n process.stderr.write(chalk.red('Email 與密碼為必填。\\n'));\n process.exit(1);\n }\n\n const client = new ApiClient();\n const result = (await client.post('/v1/developers/login', {\n email,\n password,\n })) as LoginResult;\n\n const apiKey = result.api_key ?? '';\n const developerId = result.developer_id ?? null;\n const displayName = result.display_name ?? email;\n\n // 不可變更新組態\n const config = { ...loadConfig(), api_key: apiKey, developer_id: developerId };\n saveConfig(config);\n\n process.stdout.write('\\n');\n process.stdout.write(chalk.green(` 登入成功!歡迎回來,${displayName}\\n`));\n process.stdout.write(` Developer ID: ${developerId}\\n\\n`);\n } catch (error) {\n if (error instanceof ApiError) {\n process.stderr.write(chalk.red(`登入失敗:${error.detail}\\n`));\n process.exit(1);\n }\n throw error;\n }\n });\n","/**\n * cyodaa logout — 登出(清除本機 API Key)。\n */\n\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport { clearAuth } from '../config.js';\n\nexport const logoutCommand = new Command('logout')\n .description('登出(清除本機 API Key)')\n .action(() => {\n clearAuth();\n process.stdout.write(chalk.yellow('已登出,API Key 已清除。\\n'));\n });\n","/**\n * cyodaa whoami — 顯示當前登入的開發者資訊。\n */\n\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport { ApiClient, ApiError } from '../api-client.js';\n\ninterface DeveloperInfo {\n readonly display_name?: string;\n readonly email?: string;\n readonly id?: number;\n readonly plugin_count?: number;\n}\n\nexport const whoamiCommand = new Command('whoami')\n .description('顯示當前登入的開發者資訊')\n .action(async () => {\n const client = new ApiClient();\n\n if (!client.authenticated) {\n process.stderr.write(chalk.red('尚未登入。請先執行 `cyodaa login`。\\n'));\n process.exit(1);\n }\n\n try {\n const info = (await client.get('/v1/developers/me')) as DeveloperInfo;\n\n process.stdout.write('\\n');\n process.stdout.write(chalk.cyan(' 開發者資訊\\n'));\n process.stdout.write(chalk.cyan(' ──────────────────\\n'));\n process.stdout.write(` 名稱:${chalk.bold(info.display_name ?? 'N/A')}\\n`);\n process.stdout.write(` Email:${info.email ?? 'N/A'}\\n`);\n process.stdout.write(` Developer ID:${info.id ?? 'N/A'}\\n`);\n process.stdout.write(` 已發佈插件數:${info.plugin_count ?? 0}\\n\\n`);\n } catch (error) {\n if (error instanceof ApiError) {\n process.stderr.write(chalk.red(`無法取得帳號資訊:${error.detail}\\n`));\n process.exit(1);\n }\n throw error;\n }\n });\n"],"mappings":"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","names":["Command","existsSync","join","resolve","chalk","Command","existsSync","readFileSync","join","resolve","chalk","Command","yaml","existsSync","readFileSync","join","resolve","chalk","Command","yaml","existsSync","readdirSync","statSync","join","resolve","chalk","Command","readFileSync","existsSync","mkdirSync","readFileSync","writeFileSync","join","readFileSync","resolve","existsSync","readdirSync","join","statSync","formatSize","Command","chalk","createInterface","stdin","stdout","chalk","Command","createInterface","stdin","stdout","Command","chalk","chalk","Command","Command","chalk","chalk","Command","Command","chalk","Command"]}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@cyodaa/cli",
3
+ "version": "0.1.0",
4
+ "description": "Cyodaa Plugin CLI - 建立、驗證、打包並發佈 Cyodaa 插件",
5
+ "type": "module",
6
+ "bin": {
7
+ "cyodaa": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "tsup --watch",
17
+ "typecheck": "tsc --noEmit",
18
+ "clean": "rm -rf dist"
19
+ },
20
+ "dependencies": {
21
+ "commander": "^12.0.0",
22
+ "js-yaml": "^4.1.0",
23
+ "tar": "^7.0.0",
24
+ "chalk": "^5.3.0",
25
+ "ora": "^8.0.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/js-yaml": "^4.0.0",
29
+ "@types/node": "^22.0.0",
30
+ "tsup": "^8.0.0",
31
+ "typescript": "^5.5.0"
32
+ },
33
+ "keywords": [
34
+ "cyodaa",
35
+ "plugin",
36
+ "cli",
37
+ "marketplace"
38
+ ],
39
+ "author": {
40
+ "name": "Cyodaa Team",
41
+ "email": "dev@cyodaa.com"
42
+ },
43
+ "license": "MIT",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/cyodaa/cli.git"
47
+ },
48
+ "homepage": "https://cyodaa.com/docs/cli"
49
+ }