@solazah/solazah-cli 0.2.11 → 0.2.12

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/CHANGELOG.md CHANGED
@@ -2,19 +2,28 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
- ### [0.2.11](https://cnb.cool/seayona/solazah/solazah-cli/compare/v0.2.10...v0.2.11) (2026-04-21)
6
-
7
- ### [0.2.10](https://cnb.cool/seayona/solazah/solazah-cli/compare/v0.2.4...v0.2.10) (2026-04-21)
5
+ ### [0.2.12](https://cnb.cool/seayona/solazah/solazah-cli/compare/v0.2.9...v0.2.12) (2026-04-24)
8
6
 
9
7
 
10
8
  ### Features
11
9
 
12
- * **cli:** 添加插件发布和账户管理功能 ([a696b70](https://cnb.cool/seayona/solazah/solazah-cli/commit/a696b70ef9627f75e63ddb3cc59ca553385055eb))
13
- * **cli:** 添加插件发布和账户管理功能 ([a9388c4](https://cnb.cool/seayona/solazah/solazah-cli/commit/a9388c4b250621ee6c20dffde4c607484822e0be))
14
- * **create-plugin:** 添加目标目录选择功能 ([6119cd3](https://cnb.cool/seayona/solazah/solazah-cli/commit/6119cd3741a0c8480d9e36f28189078cc71c9ce8))
15
- * **plugin:** 添加插件简单名称提取功能 ([75ea562](https://cnb.cool/seayona/solazah/solazah-cli/commit/75ea562d4603292b473d9140f5c5e63ee146b6f1))
16
- * **template:** 优化模版 ([fd45529](https://cnb.cool/seayona/solazah/solazah-cli/commit/fd455296259352930e476ae690f25435f9908ef8))
10
+ * 发布到npm ([c97f210](https://cnb.cool/seayona/solazah/solazah-cli/commit/c97f2107c417ef7b1bb5e0b38b46698af80489a5))
11
+ * **plugin:** 添加版本管理和发布命令 ([654dfc0](https://cnb.cool/seayona/solazah/solazah-cli/commit/654dfc07bc97f08f04f8c36868d17e31dbcbce73))
12
+ * **plugin:** include icon.png in files to copy for release ([550e966](https://cnb.cool/seayona/solazah/solazah-cli/commit/550e96635f65f2cd3a053ced41b9b03f8c0cfa34))
17
13
 
14
+ ### [0.2.11](https://cnb.cool/seayona/solazah/solazah-cli/compare/v0.2.10...v0.2.11) (2026-04-21)
15
+
16
+ ### [0.2.10](https://cnb.cool/seayona/solazah/solazah-cli/compare/v0.2.4...v0.2.10) (2026-04-21)
17
+
18
+
19
+ ### Features
20
+
21
+ * **cli:** 添加插件发布和账户管理功能 ([a696b70](https://cnb.cool/seayona/solazah/solazah-cli/commit/a696b70ef9627f75e63ddb3cc59ca553385055eb))
22
+ * **cli:** 添加插件发布和账户管理功能 ([a9388c4](https://cnb.cool/seayona/solazah/solazah-cli/commit/a9388c4b250621ee6c20dffde4c607484822e0be))
23
+ * **create-plugin:** 添加目标目录选择功能 ([6119cd3](https://cnb.cool/seayona/solazah/solazah-cli/commit/6119cd3741a0c8480d9e36f28189078cc71c9ce8))
24
+ * **plugin:** 添加插件简单名称提取功能 ([75ea562](https://cnb.cool/seayona/solazah/solazah-cli/commit/75ea562d4603292b473d9140f5c5e63ee146b6f1))
25
+ * **template:** 优化模版 ([fd45529](https://cnb.cool/seayona/solazah/solazah-cli/commit/fd455296259352930e476ae690f25435f9908ef8))
26
+
18
27
  ### [0.2.9](https://cnb.cool/seayona/solazah/solazah-cli/compare/v0.2.8...v0.2.9) (2026-04-20)
19
28
 
20
29
 
@@ -1 +1 @@
1
- {"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../../src/commands/plugin/package.ts"],"names":[],"mappings":"AASA,UAAU,oBAAoB;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAwHD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgEvF"}
1
+ {"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../../src/commands/plugin/package.ts"],"names":[],"mappings":"AASA,UAAU,oBAAoB;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAsID,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgEvF"}
@@ -0,0 +1,6 @@
1
+ interface ReleaseOptions {
2
+ dir?: string;
3
+ }
4
+ export declare function releasePluginCommand(options: ReleaseOptions): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=release.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release.d.ts","sourceRoot":"","sources":["../../../src/commands/plugin/release.ts"],"names":[],"mappings":"AAOA,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBjF"}
@@ -0,0 +1,7 @@
1
+ interface VersionOptions {
2
+ dir?: string;
3
+ releaseAs?: string;
4
+ }
5
+ export declare function versionPluginCommand(options: VersionOptions): Promise<void>;
6
+ export {};
7
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/commands/plugin/version.ts"],"names":[],"mappings":"AAyBA,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyFD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA+FjF"}
package/index.js CHANGED
@@ -10,6 +10,9 @@ import { promisify } from "util";
10
10
  import validatePackageName from "validate-npm-package-name";
11
11
  import { fileURLToPath } from "url";
12
12
  import os from "os";
13
+ import { createRequire } from "module";
14
+ import semver from "semver";
15
+ import conventionalChangelog from "conventional-changelog-core";
13
16
  function validatePluginName(name) {
14
17
  const errors = [];
15
18
  if (!name) {
@@ -110,7 +113,7 @@ async function replaceInDirectory(dirPath, replacements, extensions = [".ts", ".
110
113
  }
111
114
  }
112
115
  }
113
- const execAsync$1 = promisify(exec);
116
+ const execAsync$2 = promisify(exec);
114
117
  async function createPluginCommand(options) {
115
118
  console.log(chalk.cyan.bold("\n🚀 Solazah Plugin Creator\n"));
116
119
  try {
@@ -272,7 +275,7 @@ async function createPluginCommand(options) {
272
275
  if (!options.skipInstall) {
273
276
  spinner.start("Installing dependencies...");
274
277
  try {
275
- await execAsync$1("npm install", { cwd: targetDir });
278
+ await execAsync$2("npm install", { cwd: targetDir });
276
279
  spinner.succeed("Dependencies installed");
277
280
  } catch (error) {
278
281
  spinner.fail("Failed to install dependencies");
@@ -295,11 +298,11 @@ async function createPluginCommand(options) {
295
298
  process.exit(1);
296
299
  }
297
300
  }
298
- const execAsync = promisify(exec);
301
+ const execAsync$1 = promisify(exec);
299
302
  function resolveFilesToCopy(projectDir, manifest) {
300
303
  const entries = [];
301
304
  entries.push("manifest.json");
302
- for (const doc of ["CHANGELOG.md", "README.md"]) {
305
+ for (const doc of ["CHANGELOG.md", "README.md", "icon.png"]) {
303
306
  if (fs.existsSync(path.join(projectDir, doc))) {
304
307
  entries.push(doc);
305
308
  }
@@ -371,8 +374,17 @@ function processPackageJson(projectDir, releaseDir) {
371
374
  }
372
375
  function cleanDistDir(projectDir) {
373
376
  const distDir = path.join(projectDir, "dist");
374
- if (fs.existsSync(distDir)) {
375
- fs.rmSync(distDir, { recursive: true, force: true });
377
+ if (!fs.existsSync(distDir)) return;
378
+ const maxRetries = 5;
379
+ const retryDelayMs = 200;
380
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
381
+ try {
382
+ fs.rmSync(distDir, { recursive: true, force: true });
383
+ return;
384
+ } catch (err) {
385
+ if (attempt === maxRetries) throw err;
386
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, retryDelayMs);
387
+ }
376
388
  }
377
389
  }
378
390
  async function packagePluginCommand(options) {
@@ -394,7 +406,7 @@ async function packagePluginCommand(options) {
394
406
  `));
395
407
  const buildSpinner = ora("Building plugin...").start();
396
408
  try {
397
- await execAsync("npm run build", { cwd: projectDir });
409
+ await execAsync$1("npm run build", { cwd: projectDir });
398
410
  buildSpinner.succeed("Build completed");
399
411
  } catch (error) {
400
412
  buildSpinner.fail("Build failed");
@@ -417,7 +429,7 @@ async function packagePluginCommand(options) {
417
429
  }
418
430
  const tgzSpinner = ora("Creating .tgz archive...").start();
419
431
  try {
420
- const { stdout } = await execAsync("npm pack ./release --pack-destination ./release", { cwd: projectDir });
432
+ const { stdout } = await execAsync$1("npm pack ./release --pack-destination ./release", { cwd: projectDir });
421
433
  const tgzFile = stdout.trim();
422
434
  tgzSpinner.succeed(`Archive created: ${chalk.cyan("release/" + tgzFile)}`);
423
435
  } catch (error) {
@@ -757,6 +769,168 @@ async function publishPluginCommand(options) {
757
769
  process.exit(1);
758
770
  }
759
771
  }
772
+ const require$1 = createRequire(import.meta.url);
773
+ const conventionalRecommendedBump = require$1("conventional-recommended-bump");
774
+ const execAsync = promisify(exec);
775
+ const bumpAsync = (options) => new Promise(
776
+ (resolve, reject) => conventionalRecommendedBump(options, (err, result) => {
777
+ if (err) reject(err);
778
+ else resolve(result);
779
+ })
780
+ );
781
+ function loadVersionrc(projectDir) {
782
+ for (const name of [".versionrc", ".versionrc.json"]) {
783
+ const p = path.join(projectDir, name);
784
+ if (fs.existsSync(p)) {
785
+ try {
786
+ return fs.readJsonSync(p);
787
+ } catch {
788
+ }
789
+ }
790
+ }
791
+ return {};
792
+ }
793
+ function buildChangelogConfig(versionrc) {
794
+ const types = versionrc.types;
795
+ if (!types) return {};
796
+ const parserOpts = {
797
+ noteKeywords: ["BREAKING CHANGE", "BREAKING-CHANGE"]
798
+ };
799
+ const writerOpts = {
800
+ transform: (commit) => {
801
+ const match = types.find((t) => t.type === commit.type);
802
+ if (!match) return false;
803
+ if (match.hidden) return false;
804
+ commit.type = match.section ?? commit.type;
805
+ return commit;
806
+ },
807
+ groupBy: "type",
808
+ commitGroupsSort: "title",
809
+ commitsSort: ["scope", "subject"]
810
+ };
811
+ return { parserOpts, writerOpts };
812
+ }
813
+ async function generateChangelogEntry(projectDir, newVersion, versionrc) {
814
+ return new Promise((resolve, reject) => {
815
+ const { parserOpts, writerOpts } = buildChangelogConfig(versionrc);
816
+ versionrc.header ?? "# CHANGELOG\n\n";
817
+ const stream = conventionalChangelog(
818
+ { preset: "angular", parserOpts, writerOpts },
819
+ { version: newVersion },
820
+ void 0,
821
+ void 0,
822
+ void 0
823
+ );
824
+ let content = "";
825
+ stream.on("data", (chunk) => {
826
+ content += chunk.toString();
827
+ });
828
+ stream.on("end", () => resolve(content));
829
+ stream.on("error", reject);
830
+ stream.cwd = projectDir;
831
+ });
832
+ }
833
+ function bumpVersionInFile(filePath, newVersion) {
834
+ if (!fs.existsSync(filePath)) return;
835
+ const content = fs.readFileSync(filePath, "utf-8");
836
+ const updated = content.replace(
837
+ /^(\s*"version"\s*:\s*)"[^"]*"/m,
838
+ `$1"${newVersion}"`
839
+ );
840
+ fs.writeFileSync(filePath, updated, "utf-8");
841
+ }
842
+ function prependChangelog(projectDir, entry, header) {
843
+ const changelogPath = path.join(projectDir, "CHANGELOG.md");
844
+ const existing = fs.existsSync(changelogPath) ? fs.readFileSync(changelogPath, "utf-8") : "";
845
+ const body = existing.startsWith(header.trim()) ? existing.slice(header.trim().length).trimStart() : existing;
846
+ fs.writeFileSync(changelogPath, header.trim() + "\n\n" + entry + (body ? "\n" + body : ""), "utf-8");
847
+ }
848
+ async function versionPluginCommand(options) {
849
+ const projectDir = path.resolve(options.dir || ".");
850
+ const packageJsonPath = path.join(projectDir, "package.json");
851
+ const manifestPath = path.join(projectDir, "manifest.json");
852
+ if (!await fs.pathExists(packageJsonPath)) {
853
+ console.error(chalk.red("❌ No package.json found."));
854
+ process.exit(1);
855
+ }
856
+ const pkg = await fs.readJson(packageJsonPath);
857
+ const currentVersion = pkg.version ?? "0.0.0";
858
+ let releaseType = options.releaseAs;
859
+ if (!releaseType) {
860
+ const spinner = ora("Analyzing commits...").start();
861
+ try {
862
+ const prevCwd = process.cwd();
863
+ process.chdir(projectDir);
864
+ const result = await bumpAsync({ preset: "angular" });
865
+ process.chdir(prevCwd);
866
+ releaseType = result.releaseType;
867
+ spinner.succeed(`Release type: ${chalk.cyan(releaseType)} (${result.releaseType})`);
868
+ } catch (err) {
869
+ spinner.fail(`Failed to analyze commits: ${err.message}`);
870
+ process.exit(1);
871
+ }
872
+ }
873
+ const newVersion = semver.inc(currentVersion, releaseType);
874
+ if (!newVersion) {
875
+ console.error(chalk.red(`❌ Invalid version: ${currentVersion} + ${releaseType}`));
876
+ process.exit(1);
877
+ }
878
+ const versionrc = loadVersionrc(projectDir);
879
+ const header = versionrc.header ?? "# CHANGELOG\n\n";
880
+ console.log(chalk.gray(`
881
+ ${currentVersion} → ${chalk.green(newVersion)}
882
+ `));
883
+ const changelogSpinner = ora("Generating changelog...").start();
884
+ let changelogEntry = "";
885
+ try {
886
+ const prevCwd = process.cwd();
887
+ process.chdir(projectDir);
888
+ changelogEntry = await generateChangelogEntry(projectDir, newVersion, versionrc);
889
+ process.chdir(prevCwd);
890
+ changelogSpinner.succeed("Changelog generated");
891
+ } catch (err) {
892
+ changelogSpinner.warn(`Changelog generation skipped: ${err.message}`);
893
+ }
894
+ const bumpSpinner = ora("Bumping version files...").start();
895
+ bumpVersionInFile(packageJsonPath, newVersion);
896
+ bumpVersionInFile(path.join(projectDir, "package-lock.json"), newVersion);
897
+ bumpVersionInFile(manifestPath, newVersion);
898
+ bumpSpinner.succeed("Version files updated");
899
+ if (changelogEntry.trim()) {
900
+ prependChangelog(projectDir, changelogEntry.trim(), header);
901
+ ora("").succeed("CHANGELOG.md updated");
902
+ }
903
+ const gitSpinner = ora("Creating git commit and tag...").start();
904
+ try {
905
+ const filesToStage = ["package.json", "manifest.json", "CHANGELOG.md", "package-lock.json"].filter((f) => fs.existsSync(path.join(projectDir, f)));
906
+ await execAsync(`git add ${filesToStage.join(" ")}`, { cwd: projectDir });
907
+ await execAsync(`git commit -m "chore(release): ${newVersion}"`, { cwd: projectDir });
908
+ try {
909
+ await execAsync(`git tag -a v${newVersion} -m "chore(release): ${newVersion}"`, { cwd: projectDir });
910
+ } catch (tagErr) {
911
+ if (!tagErr.message?.includes("already exists")) throw tagErr;
912
+ gitSpinner.warn(`Tag v${newVersion} already exists, skipping`);
913
+ return;
914
+ }
915
+ gitSpinner.succeed(`Tagged release v${newVersion}`);
916
+ } catch (err) {
917
+ gitSpinner.fail(`Git operations failed: ${err.stderr || err.message}`);
918
+ process.exit(1);
919
+ }
920
+ console.log(chalk.green.bold(`
921
+ ✅ Version bumped to ${newVersion}
922
+ `));
923
+ }
924
+ async function releasePluginCommand(options) {
925
+ const projectDir = path.resolve(options.dir || ".");
926
+ if (!await fs.pathExists(path.join(projectDir, "package.json"))) {
927
+ console.error(chalk.red("❌ No package.json found in the target directory."));
928
+ process.exit(1);
929
+ }
930
+ await versionPluginCommand({ dir: projectDir });
931
+ await packagePluginCommand({ dir: projectDir });
932
+ await publishPluginCommand({ dir: projectDir });
933
+ }
760
934
  function openBrowser(url) {
761
935
  try {
762
936
  const platform = process.platform;
@@ -943,6 +1117,8 @@ const plugin = program.command("plugin").description("Manage Solazah plugins");
943
1117
  plugin.command("create").description("Create a new Solazah plugin").option("-n, --name <name>", "Plugin name (e.g., my-plugin or @scope/plugin-name)").option("-d, --dir <directory>", "Target directory", ".").option("-t, --template <template>", "Template to use (e.g., react)", "react").option("--skip-install", "Skip npm install").action(createPluginCommand);
944
1118
  plugin.command("package").description("Build and package a Solazah plugin for distribution").option("-d, --dir <directory>", "Plugin project directory", ".").action(packagePluginCommand);
945
1119
  plugin.command("publish").description("Publish a plugin package to Solazah marketplace").option("-f, --file <file>", "Path to the .tgz plugin package file").option("-d, --dir <directory>", "Plugin project directory", ".").action(publishPluginCommand);
1120
+ plugin.command("version").description("Bump plugin version, update CHANGELOG and create git tag").option("-d, --dir <directory>", "Plugin project directory", ".").option("-r, --release-as <type>", "Specify release type: major, minor, patch").action(versionPluginCommand);
1121
+ plugin.command("release").description("Bump version, package and publish a plugin in one step").option("-d, --dir <directory>", "Plugin project directory", ".").action(releasePluginCommand);
946
1122
  const account = program.command("account").description("Manage Solazah account authentication");
947
1123
  const withIssuer = (cmd) => cmd.option("--issuer <issuer>", "OAuth2 issuer URL");
948
1124
  withIssuer(account.command("login")).description("Login via OAuth2 device code flow").action(loginCommand);
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/utils/validate.ts","../src/utils/file.ts","../src/commands/plugin/create.ts","../src/commands/plugin/package.ts","../src/utils/auth-storage.ts","../src/utils/oauth-device.ts","../src/commands/plugin/publish.ts","../src/utils/open-browser.ts","../src/commands/account/shared.ts","../src/commands/account/login.ts","../src/commands/account/logout.ts","../src/commands/account/userinfo.ts","../src/index.ts"],"sourcesContent":["import validatePackageName from 'validate-npm-package-name';\r\n\r\nexport interface ValidationResult {\r\n valid: boolean;\r\n errors: string[];\r\n}\r\n\r\n/**\r\n * 验证插件名称\r\n * 接受任何合法的 npm 包名\r\n */\r\nexport function validatePluginName(name: string): ValidationResult {\r\n const errors: string[] = [];\r\n\r\n // 基本格式检查\r\n if (!name) {\r\n errors.push('Plugin name is required');\r\n return { valid: false, errors };\r\n }\r\n\r\n // 使用 npm 包名验证\r\n const validation = validatePackageName(name);\r\n if (!validation.validForNewPackages) {\r\n if (validation.errors) {\r\n errors.push(...validation.errors);\r\n }\r\n if (validation.warnings) {\r\n errors.push(...validation.warnings);\r\n }\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n errors,\r\n };\r\n}\r\n\r\n/**\r\n * 从包名提取插件名称\r\n * @example extractPluginName('@solazah/solazah-plugin-example') => 'solazah-plugin-example'\r\n */\r\nexport function extractPluginName(packageName: string): string {\r\n // 移除作用域前缀(如 @solazah/)\r\n return packageName.replace(/^@[^/]+\\//, '');\r\n}\r\n\r\nexport function extractPluginSimpleName(packageName: string){\r\n // 移除作用域前缀(如 @solazah/)\r\n let name = packageName.replace(/^@[^/]+\\//, '');\r\n\r\n // 移除常见的插件前缀\r\n name = name.replace(/^plugin-/, '').replace(/^solazah-plugin-/, '');\r\n\r\n return name;\r\n}\r\n\r\n/**\r\n * 生成显示名称\r\n * @example generateDisplayName('example') => 'Example'\r\n */\r\nexport function generateDisplayName(name: string): string {\r\n return name\r\n .split('-')\r\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\r\n .join(' ');\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\nexport interface TemplateInfo {\r\n name: string;\r\n displayName: string;\r\n description: string;\r\n version: string;\r\n features?: string[];\r\n}\r\n\r\n/**\r\n * 检查目录是否为空\r\n */\r\nexport async function isDirectoryEmpty(dirPath: string): Promise<boolean> {\r\n try {\r\n const files = await fs.readdir(dirPath);\r\n return files.length === 0;\r\n } catch {\r\n return true; // 目录不存在,视为空\r\n }\r\n}\r\n\r\n/**\r\n * 确保目录存在\r\n */\r\nexport async function ensureDirectory(dirPath: string): Promise<void> {\r\n await fs.ensureDir(dirPath);\r\n}\r\n\r\n/**\r\n * 获取模板根目录路径\r\n */\r\nexport function getTemplatesRootDir(): string {\r\n // __dirname 在编译后会是 dist/,templates 在项目根目录\r\n // 所以需要向上一级: dist/ -> project_root/\r\n return path.resolve(__dirname, '../templates');\r\n}\r\n\r\n/**\r\n * 获取指定模板的目录路径\r\n */\r\nexport function getTemplateDir(templateName: string): string {\r\n return path.join(getTemplatesRootDir(), templateName);\r\n}\r\n\r\n/**\r\n * 获取所有可用模板\r\n */\r\nexport async function getAvailableTemplates(): Promise<TemplateInfo[]> {\r\n const templatesRoot = getTemplatesRootDir();\r\n const templates: TemplateInfo[] = [];\r\n\r\n try {\r\n const entries = await fs.readdir(templatesRoot, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n if (entry.isDirectory()) {\r\n const templateJsonPath = path.join(templatesRoot, entry.name, 'template.json');\r\n\r\n if (await fs.pathExists(templateJsonPath)) {\r\n const templateInfo = await fs.readJson(templateJsonPath);\r\n templates.push(templateInfo);\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.error('Failed to read templates:', error);\r\n }\r\n\r\n return templates;\r\n}\r\n\r\n/**\r\n * 复制模板文件\r\n */\r\nexport async function copyTemplate(templateDir: string, targetDir: string): Promise<void> {\r\n await fs.copy(templateDir, targetDir, {\r\n filter: (src) => {\r\n // 排除 node_modules 和其他不需要的文件\r\n const basename = path.basename(src);\r\n return !['node_modules', '.git', 'dist', 'target', 'release'].includes(basename);\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * 替换文件内容中的占位符\r\n * 支持 {{PLACEHOLDER}} 格式\r\n */\r\nexport async function replacePlaceholders(\r\n filePath: string,\r\n replacements: Record<string, string>\r\n): Promise<void> {\r\n let content = await fs.readFile(filePath, 'utf-8');\r\n\r\n for (const [key, value] of Object.entries(replacements)) {\r\n // 支持 {{KEY}} 和直接字符串替换\r\n const placeholder = `{{${key}}}`;\r\n content = content.replaceAll(placeholder, value);\r\n // 兼容旧的直接替换方式\r\n content = content.replaceAll(key, value);\r\n }\r\n\r\n await fs.writeFile(filePath, content, 'utf-8');\r\n}\r\n\r\n/**\r\n * 批量替换目录中所有文件的占位符\r\n */\r\nexport async function replaceInDirectory(\r\n dirPath: string,\r\n replacements: Record<string, string>,\r\n extensions: string[] = ['.ts', '.tsx', '.js', '.jsx', '.json', '.html', '.md']\r\n): Promise<void> {\r\n const files = await fs.readdir(dirPath, { withFileTypes: true });\r\n\r\n for (const file of files) {\r\n const filePath = path.join(dirPath, file.name);\r\n\r\n if (file.isDirectory()) {\r\n // 递归处理子目录\r\n await replaceInDirectory(filePath, replacements, extensions);\r\n } else if (file.isFile()) {\r\n // 检查文件扩展名\r\n const ext = path.extname(file.name);\r\n if (extensions.includes(ext) || file.name === 'manifest.json') {\r\n await replacePlaceholders(filePath, replacements);\r\n }\r\n }\r\n }\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport inquirer from 'inquirer';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport { exec } from 'child_process';\r\nimport { promisify } from 'util';\r\nimport {\r\n validatePluginName,\r\n extractPluginName,\r\n generateDisplayName, extractPluginSimpleName,\r\n} from '../../utils/validate.js';\r\nimport {\r\n isDirectoryEmpty,\r\n ensureDirectory,\r\n getTemplateDir,\r\n getAvailableTemplates,\r\n copyTemplate,\r\n replaceInDirectory,\r\n} from '../../utils/file.js';\r\n\r\nconst execAsync = promisify(exec);\r\n\r\ninterface CreatePluginOptions {\r\n name?: string;\r\n dir?: string;\r\n template?: string;\r\n skipInstall?: boolean;\r\n}\r\n\r\nexport async function createPluginCommand(options: CreatePluginOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n🚀 Solazah Plugin Creator\\n'));\r\n\r\n try {\r\n // 1. 获取可用模板\r\n const availableTemplates = await getAvailableTemplates();\r\n\r\n if (availableTemplates.length === 0) {\r\n console.error(chalk.red('❌ No templates found'));\r\n process.exit(1);\r\n }\r\n\r\n // 2. 选择模板\r\n let selectedTemplate = options.template || 'react';\r\n\r\n if (!options.template && availableTemplates.length > 1) {\r\n const { template } = await inquirer.prompt([\r\n {\r\n type: 'list',\r\n name: 'template',\r\n message: 'Select a template:',\r\n choices: availableTemplates.map((t) => ({\r\n name: `${t.displayName} - ${t.description}`,\r\n value: t.name,\r\n })),\r\n default: 'react',\r\n },\r\n ]);\r\n selectedTemplate = template;\r\n }\r\n\r\n const templateInfo = availableTemplates.find((t) => t.name === selectedTemplate);\r\n if (!templateInfo) {\r\n console.error(chalk.red(`❌ Template \"${selectedTemplate}\" not found`));\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.gray(`Using template: ${templateInfo.displayName}\\n`));\r\n\r\n // 3. 获取或询问插件名称\r\n let pluginName = options.name;\r\n\r\n if (!pluginName) {\r\n const answers = await inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'name',\r\n message: 'Plugin name (e.g., my-plugin or @scope/plugin-name):',\r\n validate: (input: string) => {\r\n const result = validatePluginName(input);\r\n return result.valid || result.errors.join(', ');\r\n },\r\n },\r\n ]);\r\n pluginName = answers.name;\r\n }\r\n\r\n // 验证插件名称\r\n const validation = validatePluginName(pluginName!);\r\n if (!validation.valid) {\r\n console.error(chalk.red('❌ Invalid plugin name:'));\r\n validation.errors.forEach((error) => console.error(chalk.red(` - ${error}`)));\r\n process.exit(1);\r\n }\r\n\r\n // 2. 提取信息\r\n const packageName = extractPluginName(pluginName!)\r\n const simpleName = extractPluginSimpleName(pluginName!);\r\n const defaultDisplayName = generateDisplayName(simpleName);\r\n\r\n // 3. 确定目标目录\r\n const defaultTargetDir = options.dir ? path.resolve(options.dir, packageName) : path.resolve('.');\r\n const { targetDir: confirmedTargetDir } = await inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'targetDir',\r\n message: 'Target directory:',\r\n default: defaultTargetDir,\r\n },\r\n ]);\r\n const targetDir = path.resolve(confirmedTargetDir);\r\n\r\n // 4. 询问额外信息\r\n const answers = await inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'displayName',\r\n message: 'Display name:',\r\n default: defaultDisplayName,\r\n },\r\n {\r\n type: 'input',\r\n name: 'description',\r\n message: 'Description:',\r\n default: `A Solazah plugin - ${defaultDisplayName}`,\r\n },\r\n {\r\n type: 'input',\r\n name: 'author',\r\n message: 'Author:',\r\n default: '',\r\n },\r\n {\r\n type: 'number',\r\n name: 'port',\r\n message: 'Development server port:',\r\n default: 5200,\r\n },\r\n ]);\r\n\r\n // 检查目录是否存在且非空\r\n const dirExists = await fs.pathExists(targetDir);\r\n if (dirExists) {\r\n const isEmpty = await isDirectoryEmpty(targetDir);\r\n if (!isEmpty) {\r\n const { overwrite } = await inquirer.prompt([\r\n {\r\n type: 'confirm',\r\n name: 'overwrite',\r\n message: `Directory ${chalk.cyan(targetDir)} is not empty. Overwrite?`,\r\n default: false,\r\n },\r\n ]);\r\n\r\n if (!overwrite) {\r\n console.log(chalk.yellow('❌ Cancelled'));\r\n process.exit(0);\r\n }\r\n\r\n await fs.remove(targetDir);\r\n }\r\n }\r\n\r\n // 5. 创建目录\r\n await ensureDirectory(targetDir);\r\n\r\n // 6. 复制模板\r\n const spinner = ora('Copying template files...').start();\r\n const templateDir = getTemplateDir(selectedTemplate);\r\n\r\n if (!(await fs.pathExists(templateDir))) {\r\n spinner.fail(chalk.red(`Template directory not found: ${templateDir}`));\r\n process.exit(1);\r\n }\r\n\r\n await copyTemplate(templateDir, targetDir);\r\n spinner.succeed('Template files copied');\r\n\r\n // 7. 更新文件内容\r\n spinner.start('Updating files...');\r\n\r\n // 定义占位符替换映射\r\n const replacements: Record<string, string> = {\r\n PLUGIN_NAME: pluginName!,\r\n PLUGIN_DISPLAY_NAME: answers.displayName,\r\n PLUGIN_DESCRIPTION: answers.description,\r\n PLUGIN_AUTHOR: answers.author || '',\r\n PLUGIN_VERSION: '0.0.1',\r\n DEV_PORT: answers.port.toString(),\r\n PLUGIN_SIMPLE_NAME: simpleName,\r\n PLUGIN_CLASS_NAME: `solazah-${simpleName}`,\r\n };\r\n\r\n // 批量替换目录中的占位符\r\n await replaceInDirectory(targetDir, replacements);\r\n\r\n // 额外处理特定文件(JSON 文件需要特殊处理)\r\n const packageJsonPath = path.join(targetDir, 'package.json');\r\n const packageJson = await fs.readJson(packageJsonPath);\r\n packageJson.name = pluginName;\r\n packageJson.version = '0.0.1';\r\n packageJson.description = answers.description;\r\n if (answers.author) {\r\n packageJson.author = answers.author;\r\n }\r\n packageJson.scripts.dev = `vite --port ${answers.port}`;\r\n await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });\r\n\r\n const manifestPath = path.join(targetDir, 'manifest.json');\r\n const manifest = await fs.readJson(manifestPath);\r\n manifest.name = pluginName;\r\n manifest.displayName = answers.displayName;\r\n manifest.commands = [answers.displayName];\r\n manifest.development.main = `http://localhost:${answers.port}`;\r\n await fs.writeJson(manifestPath, manifest, { spaces: 2 });\r\n\r\n // 删除不需要的文件\r\n const filesToRemove = [\r\n 'template.json',\r\n ];\r\n for (const file of filesToRemove) {\r\n const filePath = path.join(targetDir, file);\r\n if (await fs.pathExists(filePath)) {\r\n await fs.remove(filePath);\r\n }\r\n }\r\n\r\n spinner.succeed('Files updated');\r\n\r\n // 8. 安装依赖\r\n if (!options.skipInstall) {\r\n spinner.start('Installing dependencies...');\r\n try {\r\n await execAsync('npm install', { cwd: targetDir });\r\n spinner.succeed('Dependencies installed');\r\n } catch (error) {\r\n spinner.fail('Failed to install dependencies');\r\n console.log(chalk.yellow('You can install them manually by running: npm install'));\r\n }\r\n }\r\n\r\n // 9. 完成\r\n console.log(chalk.green.bold('\\n✅ Plugin created successfully!\\n'));\r\n console.log(chalk.cyan('Next steps:'));\r\n const relativePath = path.relative(process.cwd(), targetDir);\r\n if (relativePath && relativePath !== '.') {\r\n console.log(chalk.gray(` cd ${relativePath}`));\r\n }\r\n if (options.skipInstall) {\r\n console.log(chalk.gray(' npm install'));\r\n }\r\n console.log(chalk.gray(' npm run dev'));\r\n console.log();\r\n } catch (error) {\r\n console.error(chalk.red('❌ Error:'), error);\r\n process.exit(1);\r\n }\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport { exec } from 'child_process';\r\nimport { promisify } from 'util';\r\n\r\nconst execAsync = promisify(exec);\r\n\r\ninterface PackagePluginOptions {\r\n dir?: string;\r\n}\r\n\r\ntype FileCopyEntry = string | { from: string; to: string };\r\n\r\n/**\r\n * 根据 manifest.json 推断需要复制到 release/ 的文件列表\r\n */\r\nfunction resolveFilesToCopy(projectDir: string, manifest: Record<string, unknown>): FileCopyEntry[] {\r\n const entries: FileCopyEntry[] = [];\r\n\r\n // manifest.json 始终需要\r\n entries.push('manifest.json');\r\n\r\n // 可选文档文件\r\n for (const doc of ['CHANGELOG.md', 'README.md']) {\r\n if (fs.existsSync(path.join(projectDir, doc))) {\r\n entries.push(doc);\r\n }\r\n }\r\n\r\n // 根据 manifest.main 推断构建产物目标目录\r\n // e.g. \"renderer/index.html\" → dist -> renderer\r\n // e.g. \"index.html\" → dist -> .\r\n const main = manifest.main as string | undefined;\r\n if (main) {\r\n const mainDir = path.dirname(main);\r\n entries.push({ from: 'dist', to: mainDir === '.' ? '.' : mainDir });\r\n } else {\r\n entries.push({ from: 'dist', to: '.' });\r\n }\r\n\r\n // preload 目录\r\n const preload = manifest.preload as string | undefined;\r\n if (preload) {\r\n const preloadDir = path.dirname(preload); // e.g. \"preload/preload.mjs\" -> \"preload\"\r\n const srcPreload = path.join(projectDir, 'src', 'preload');\r\n if (fs.existsSync(srcPreload)) {\r\n entries.push({ from: 'src/preload', to: preloadDir });\r\n }\r\n }\r\n\r\n // skills 目录\r\n const srcSkills = path.join(projectDir, 'src', 'skills');\r\n if (fs.existsSync(srcSkills)) {\r\n entries.push({ from: 'src/skills', to: 'skills' });\r\n }\r\n\r\n // screenshots 目录\r\n const srcScreenshots = path.join(projectDir, 'src', 'screenshots');\r\n if (fs.existsSync(srcScreenshots)) {\r\n entries.push({ from: 'src/screenshots', to: 'screenshots' });\r\n }\r\n\r\n return entries;\r\n}\r\n\r\n/**\r\n * 清理并初始化 release 目录\r\n */\r\nfunction cleanReleaseDir(releaseDir: string): void {\r\n if (fs.existsSync(releaseDir)) {\r\n fs.rmSync(releaseDir, { recursive: true, force: true });\r\n }\r\n fs.mkdirSync(releaseDir, { recursive: true });\r\n}\r\n\r\n/**\r\n * 按照文件列表复制到 release 目录\r\n */\r\nfunction copyFiles(projectDir: string, releaseDir: string, files: FileCopyEntry[]): void {\r\n for (const file of files) {\r\n if (typeof file === 'string') {\r\n const src = path.resolve(projectDir, file);\r\n const dest = path.resolve(releaseDir, file);\r\n fs.cpSync(src, dest, { recursive: true });\r\n } else {\r\n const src = path.resolve(projectDir, file.from);\r\n const dest = path.resolve(releaseDir, file.to);\r\n fs.cpSync(src, dest, { recursive: true });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 精简 package.json,只保留发布需要的字段\r\n */\r\nfunction processPackageJson(projectDir: string, releaseDir: string): void {\r\n const raw = fs.readFileSync(path.resolve(projectDir, 'package.json'), 'utf-8');\r\n const pkg = JSON.parse(raw);\r\n\r\n const cleaned: Record<string, unknown> = {\r\n name: pkg.name,\r\n main: pkg.main,\r\n type: pkg.type,\r\n version: pkg.version,\r\n description: pkg.description,\r\n author: pkg.author,\r\n license: pkg.license,\r\n homepage: pkg.homepage,\r\n repository: pkg.repository,\r\n keywords: pkg.keywords,\r\n peerDependencies: pkg.peerDependencies,\r\n };\r\n\r\n fs.writeFileSync(\r\n path.resolve(releaseDir, 'package.json'),\r\n JSON.stringify(cleaned, null, 2),\r\n );\r\n}\r\n\r\n/**\r\n * 清理构建产物临时目录 (dist/)\r\n */\r\nfunction cleanDistDir(projectDir: string): void {\r\n const distDir = path.join(projectDir, 'dist');\r\n if (fs.existsSync(distDir)) {\r\n fs.rmSync(distDir, { recursive: true, force: true });\r\n }\r\n}\r\n\r\nexport async function packagePluginCommand(options: PackagePluginOptions): Promise<void> {\r\n const projectDir = path.resolve(options.dir || '.');\r\n\r\n console.log(chalk.cyan.bold('\\n📦 Solazah Plugin Packager\\n'));\r\n\r\n // 1. 验证项目目录\r\n const packageJsonPath = path.join(projectDir, 'package.json');\r\n if (!(await fs.pathExists(packageJsonPath))) {\r\n console.error(chalk.red('❌ No package.json found in the target directory.'));\r\n process.exit(1);\r\n }\r\n\r\n const manifestPath = path.join(projectDir, 'manifest.json');\r\n if (!(await fs.pathExists(manifestPath))) {\r\n console.error(chalk.red('❌ No manifest.json found. Is this a Solazah plugin project?'));\r\n process.exit(1);\r\n }\r\n\r\n const packageData = await fs.readJson(packageJsonPath);\r\n const manifest = await fs.readJson(manifestPath);\r\n console.log(chalk.gray(`Plugin: ${packageData.name || 'unknown'} v${packageData.version || '0.0.0'}\\n`));\r\n\r\n // 2. 构建项目\r\n const buildSpinner = ora('Building plugin...').start();\r\n try {\r\n await execAsync('npm run build', { cwd: projectDir });\r\n buildSpinner.succeed('Build completed');\r\n } catch (error: any) {\r\n buildSpinner.fail('Build failed');\r\n console.error(chalk.red(error.stderr || error.message));\r\n process.exit(1);\r\n }\r\n\r\n // 3. 打包到 release/\r\n const packageSpinner = ora('Packaging plugin...').start();\r\n try {\r\n const releaseDir = path.join(projectDir, 'release');\r\n const filesToCopy = resolveFilesToCopy(projectDir, manifest);\r\n\r\n cleanReleaseDir(releaseDir);\r\n copyFiles(projectDir, releaseDir, filesToCopy);\r\n processPackageJson(projectDir, releaseDir);\r\n cleanDistDir(projectDir);\r\n\r\n packageSpinner.succeed('Package created in release/');\r\n } catch (error: any) {\r\n packageSpinner.fail('Packaging failed');\r\n console.error(chalk.red(error.message));\r\n process.exit(1);\r\n }\r\n\r\n // 4. 生成 .tgz 文件到 release/\r\n const tgzSpinner = ora('Creating .tgz archive...').start();\r\n try {\r\n const { stdout } = await execAsync('npm pack ./release --pack-destination ./release', { cwd: projectDir });\r\n const tgzFile = stdout.trim();\r\n tgzSpinner.succeed(`Archive created: ${chalk.cyan('release/' + tgzFile)}`);\r\n } catch (error: any) {\r\n tgzSpinner.fail('Failed to create .tgz archive');\r\n console.error(chalk.red(error.stderr || error.message));\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.green.bold('\\n✅ Plugin packaged successfully!\\n'));\r\n}\r\n","import path from 'path';\r\nimport os from 'os';\r\nimport fs from 'fs-extra';\r\n\r\nexport interface StoredTokens {\r\n access_token: string;\r\n refresh_token?: string;\r\n id_token?: string;\r\n token_type: string;\r\n scope?: string;\r\n expires_at?: number; // epoch seconds\r\n issuer?: string;\r\n}\r\n\r\nexport interface TokenResponse {\r\n access_token: string;\r\n refresh_token?: string;\r\n id_token?: string;\r\n token_type: string;\r\n expires_in?: number;\r\n scope?: string;\r\n}\r\n\r\nconst AUTH_DIR = path.join(os.homedir(), '.solazah');\r\nconst AUTH_FILE = path.join(AUTH_DIR, 'auth.json');\r\nconst EXPIRY_SKEW_SECONDS = 30;\r\n\r\nexport async function saveTokens(tokens: StoredTokens): Promise<void> {\r\n await fs.ensureDir(AUTH_DIR);\r\n await fs.writeJson(AUTH_FILE, tokens, { spaces: 2 });\r\n try {\r\n await fs.chmod(AUTH_FILE, 0o600);\r\n } catch {\r\n // ignore chmod errors on Windows\r\n }\r\n}\r\n\r\nexport async function loadTokens(): Promise<StoredTokens | null> {\r\n try {\r\n return (await fs.readJson(AUTH_FILE)) as StoredTokens;\r\n } catch (err) {\r\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return null;\r\n }\r\n throw err;\r\n }\r\n}\r\n\r\nexport async function clearTokens(): Promise<void> {\r\n try {\r\n await fs.remove(AUTH_FILE);\r\n } catch {\r\n // ignore\r\n }\r\n}\r\n\r\nexport function isExpired(tokens: StoredTokens): boolean {\r\n if (!tokens.expires_at) return false;\r\n return Math.floor(Date.now() / 1000) >= tokens.expires_at - EXPIRY_SKEW_SECONDS;\r\n}\r\n\r\nexport function toStoredTokens(tokens: TokenResponse, issuer: string): StoredTokens {\r\n const now = Math.floor(Date.now() / 1000);\r\n return {\r\n access_token: tokens.access_token,\r\n refresh_token: tokens.refresh_token,\r\n id_token: tokens.id_token,\r\n token_type: tokens.token_type,\r\n scope: tokens.scope,\r\n expires_at: tokens.expires_in ? now + tokens.expires_in : undefined,\r\n issuer,\r\n };\r\n}\r\n\r\n/**\r\n * 合并刷新后的令牌:若服务器未返回新的 refresh_token 则保留原值。\r\n */\r\nexport function mergeRefreshedTokens(\r\n previous: StoredTokens,\r\n refreshed: TokenResponse,\r\n issuer: string,\r\n): StoredTokens {\r\n const next = toStoredTokens(refreshed, issuer);\r\n if (!next.refresh_token) {\r\n next.refresh_token = previous.refresh_token;\r\n }\r\n return next;\r\n}\r\n","import { TokenResponse } from './auth-storage.js';\r\n\r\nexport const DEFAULT_ISSUER = 'https://solazah.seayona.com/account';\r\nexport const DEFAULT_CLIENT_ID = 'solazah-cli';\r\nexport const DEFAULT_CLIENT_SECRET = 'solazah-cli';\r\nexport const DEFAULT_SCOPE = 'openid profile offline_access';\r\nexport const DEFAULT_TOKEN_TYPE = 'Bearer';\r\n\r\n/**\r\n * 授权服务器默认把 verification_uri 指向后端的表单端点\r\n * (/account/oauth2/device_verification),但我们的前端是 SPA,\r\n * 直接让用户访问后端表单会看到空白页。这里统一重写为 SPA 的\r\n * /#/device hash 路由,由前端引导用户登录并提交 user_code。\r\n */\r\nexport const DEFAULT_VERIFICATION_URI = 'https://solazah.seayona.com/account/#/device';\r\n\r\nexport const GRANT_TYPE = {\r\n DEVICE_CODE: 'urn:ietf:params:oauth:grant-type:device_code',\r\n REFRESH_TOKEN: 'refresh_token',\r\n} as const;\r\n\r\nexport const TOKEN_HINT = {\r\n ACCESS: 'access_token',\r\n REFRESH: 'refresh_token',\r\n} as const;\r\nexport type TokenTypeHint = typeof TOKEN_HINT[keyof typeof TOKEN_HINT];\r\n\r\nexport const DEVICE_ERROR = {\r\n AUTH_PENDING: 'authorization_pending',\r\n SLOW_DOWN: 'slow_down',\r\n ACCESS_DENIED: 'access_denied',\r\n EXPIRED_TOKEN: 'expired_token',\r\n} as const;\r\n\r\nexport interface OidcDiscovery {\r\n issuer: string;\r\n device_authorization_endpoint?: string;\r\n token_endpoint: string;\r\n userinfo_endpoint?: string;\r\n end_session_endpoint?: string;\r\n revocation_endpoint?: string;\r\n}\r\n\r\nexport interface DeviceAuthorizationResponse {\r\n device_code: string;\r\n user_code: string;\r\n verification_uri: string;\r\n verification_uri_complete?: string;\r\n expires_in: number;\r\n interval?: number;\r\n}\r\n\r\nexport interface UserInfo {\r\n sub: string;\r\n name?: string;\r\n username?: string;\r\n preferred_username?: string;\r\n email?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\nexport async function discover(issuer: string = DEFAULT_ISSUER): Promise<OidcDiscovery> {\r\n const url = `${issuer.replace(/\\/+$/, '')}/.well-known/openid-configuration`;\r\n const res = await fetch(url);\r\n if (!res.ok) {\r\n throw new Error(`Failed to load OIDC discovery (${res.status}): ${url}`);\r\n }\r\n return (await res.json()) as OidcDiscovery;\r\n}\r\n\r\n/**\r\n * OAuth 端点 POST:application/x-www-form-urlencoded;\r\n * 使用 client_secret_post 方式认证,client_id 和 client_secret 放在表单中。\r\n */\r\nasync function postForm(\r\n url: string,\r\n params: Record<string, string>,\r\n): Promise<{ ok: boolean; status: number; data: Record<string, unknown> }> {\r\n const body = new URLSearchParams(params);\r\n const res = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n Accept: 'application/json',\r\n },\r\n body: body.toString(),\r\n redirect: 'manual',\r\n });\r\n\r\n // 服务器返回 3xx 重定向通常意味着客户端认证失败或端点配置错误\r\n if (res.status >= 300 && res.status < 400) {\r\n const location = res.headers.get('location') ?? '(unknown)';\r\n throw new Error(\r\n `服务器返回了重定向 (${res.status}) → ${location},` +\r\n `请确认客户端 client_id 已在授权服务器注册为公共客户端`,\r\n );\r\n }\r\n\r\n const text = await res.text();\r\n let data: Record<string, unknown>;\r\n try {\r\n data = JSON.parse(text) as Record<string, unknown>;\r\n } catch {\r\n throw new Error(\r\n `服务器返回了非 JSON 响应 (${res.status}): ${text.slice(0, 200)}`,\r\n );\r\n }\r\n return { ok: res.ok, status: res.status, data };\r\n}\r\n\r\n/** 安全提取 string */\r\nfunction str(v: unknown): string | undefined {\r\n return typeof v === 'string' ? v : undefined;\r\n}\r\n/** 安全提取 number */\r\nfunction num(v: unknown): number | undefined {\r\n return typeof v === 'number' ? v : undefined;\r\n}\r\n\r\nexport async function requestDeviceCode(options: {\r\n discovery: OidcDiscovery;\r\n clientId?: string;\r\n scope?: string;\r\n}): Promise<DeviceAuthorizationResponse> {\r\n const { discovery } = options;\r\n if (!discovery.device_authorization_endpoint) {\r\n throw new Error('授权服务器未提供 device_authorization_endpoint');\r\n }\r\n const { ok, status, data } = await postForm(discovery.device_authorization_endpoint, {\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n scope: options.scope ?? DEFAULT_SCOPE,\r\n });\r\n if (!ok) {\r\n throw new Error(`设备授权请求失败 (${status}): ${JSON.stringify(data)}`);\r\n }\r\n\r\n // 兼容 snake_case(RFC 8628 标准)和 camelCase 两种响应格式\r\n const deviceCode = str(data.device_code) ?? str(data.deviceCode);\r\n const userCode = str(data.user_code) ?? str(data.userCode);\r\n const expiresIn = num(data.expires_in) ?? num(data.expiresIn);\r\n const interval = num(data.interval);\r\n\r\n if (!deviceCode || !userCode || expiresIn == null) {\r\n throw new Error(\r\n `设备授权响应缺少必要字段(device_code/user_code/expires_in),` +\r\n `实际响应:${JSON.stringify(data)}`,\r\n );\r\n }\r\n\r\n // 授权服务器默认返回后端表单端点作为 verification_uri,\r\n // 这里覆写为 SPA 地址,保证用户打开后能看到前端页面。\r\n const verificationUri = DEFAULT_VERIFICATION_URI;\r\n const verificationUriComplete = `${verificationUri}?user_code=${encodeURIComponent(userCode)}`;\r\n\r\n return {\r\n device_code: deviceCode,\r\n user_code: userCode,\r\n verification_uri: verificationUri,\r\n verification_uri_complete: verificationUriComplete,\r\n expires_in: expiresIn,\r\n interval,\r\n };\r\n}\r\n\r\nexport type PollStatus = 'success' | 'pending' | 'slow_down' | 'denied' | 'expired' | 'error';\r\n\r\nexport interface PollResult {\r\n status: PollStatus;\r\n tokens?: TokenResponse;\r\n error?: string;\r\n errorDescription?: string;\r\n}\r\n\r\nexport async function pollToken(options: {\r\n discovery: OidcDiscovery;\r\n deviceCode: string;\r\n clientId?: string;\r\n}): Promise<PollResult> {\r\n const { ok, data } = await postForm(options.discovery.token_endpoint, {\r\n grant_type: GRANT_TYPE.DEVICE_CODE,\r\n device_code: options.deviceCode,\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n });\r\n\r\n if (ok && typeof data.access_token === 'string') {\r\n return { status: 'success', tokens: tokenResponseFrom(data) };\r\n }\r\n\r\n const err = typeof data.error === 'string' ? data.error : 'error';\r\n const errorDescription =\r\n typeof data.error_description === 'string' ? data.error_description : undefined;\r\n const status: PollStatus =\r\n err === DEVICE_ERROR.AUTH_PENDING ? 'pending'\r\n : err === DEVICE_ERROR.SLOW_DOWN ? 'slow_down'\r\n : err === DEVICE_ERROR.ACCESS_DENIED ? 'denied'\r\n : err === DEVICE_ERROR.EXPIRED_TOKEN ? 'expired'\r\n : 'error';\r\n return { status, error: err, errorDescription };\r\n}\r\n\r\nexport async function fetchUserInfo(options: {\r\n discovery: OidcDiscovery;\r\n accessToken: string;\r\n}): Promise<UserInfo> {\r\n if (!options.discovery.userinfo_endpoint) {\r\n throw new Error('授权服务器未提供 userinfo_endpoint');\r\n }\r\n const res = await fetch(options.discovery.userinfo_endpoint, {\r\n headers: {\r\n Authorization: `Bearer ${options.accessToken}`,\r\n Accept: 'application/json',\r\n },\r\n });\r\n if (!res.ok) {\r\n const text = await res.text();\r\n throw new Error(`获取用户信息失败 (${res.status}): ${text}`);\r\n }\r\n return (await res.json()) as UserInfo;\r\n}\r\n\r\nexport async function refreshTokens(options: {\r\n discovery: OidcDiscovery;\r\n refreshToken: string;\r\n clientId?: string;\r\n}): Promise<TokenResponse> {\r\n const { ok, status, data } = await postForm(options.discovery.token_endpoint, {\r\n grant_type: GRANT_TYPE.REFRESH_TOKEN,\r\n refresh_token: options.refreshToken,\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n });\r\n if (!ok) {\r\n throw new Error(`刷新令牌失败 (${status}): ${JSON.stringify(data)}`);\r\n }\r\n return tokenResponseFrom(data);\r\n}\r\n\r\n/**\r\n * 撤销指定类型的令牌;best-effort,出错不抛出。\r\n */\r\nexport async function revokeToken(options: {\r\n discovery: OidcDiscovery;\r\n token: string;\r\n tokenTypeHint?: TokenTypeHint;\r\n clientId?: string;\r\n}): Promise<void> {\r\n if (!options.discovery.revocation_endpoint) {\r\n return;\r\n }\r\n const params: Record<string, string> = {\r\n token: options.token,\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n };\r\n if (options.tokenTypeHint) {\r\n params.token_type_hint = options.tokenTypeHint;\r\n }\r\n await postForm(options.discovery.revocation_endpoint, params).catch(() => {\r\n // best-effort\r\n });\r\n}\r\n\r\n/**\r\n * 并发撤销 access_token 与 refresh_token。\r\n */\r\nexport async function revokeAll(options: {\r\n discovery: OidcDiscovery;\r\n accessToken?: string;\r\n refreshToken?: string;\r\n clientId?: string;\r\n}): Promise<void> {\r\n if (!options.discovery.revocation_endpoint) return;\r\n const jobs: Promise<void>[] = [];\r\n if (options.refreshToken) {\r\n jobs.push(revokeToken({\r\n discovery: options.discovery,\r\n token: options.refreshToken,\r\n tokenTypeHint: TOKEN_HINT.REFRESH,\r\n clientId: options.clientId,\r\n }));\r\n }\r\n if (options.accessToken) {\r\n jobs.push(revokeToken({\r\n discovery: options.discovery,\r\n token: options.accessToken,\r\n tokenTypeHint: TOKEN_HINT.ACCESS,\r\n clientId: options.clientId,\r\n }));\r\n }\r\n await Promise.all(jobs);\r\n}\r\n\r\nfunction tokenResponseFrom(data: Record<string, unknown>): TokenResponse {\r\n return {\r\n access_token: String(data.access_token ?? ''),\r\n refresh_token: typeof data.refresh_token === 'string' ? data.refresh_token : undefined,\r\n id_token: typeof data.id_token === 'string' ? data.id_token : undefined,\r\n token_type: typeof data.token_type === 'string' ? data.token_type : DEFAULT_TOKEN_TYPE,\r\n expires_in: typeof data.expires_in === 'number' ? data.expires_in : undefined,\r\n scope: typeof data.scope === 'string' ? data.scope : undefined,\r\n };\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport {\r\n loadTokens,\r\n isExpired,\r\n mergeRefreshedTokens,\r\n saveTokens,\r\n StoredTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport { discover, refreshTokens } from '../../utils/oauth-device.js';\r\n\r\nconst MARKETPLACE_BASE_URL = 'https://solazah.seayona.com/marketplace';\r\n\r\ninterface PublishOptions {\r\n file?: string;\r\n dir?: string;\r\n}\r\n\r\nasync function ensureAuth(): Promise<StoredTokens> {\r\n const tokens = await loadTokens();\r\n if (!tokens) {\r\n console.log(chalk.red('当前未登录,请先执行:solazah-cli account login'));\r\n process.exit(1);\r\n }\r\n\r\n if (isExpired(tokens) && tokens.refresh_token) {\r\n const spinner = ora('令牌已过期,正在刷新...').start();\r\n try {\r\n const discovery = await discover(tokens.issuer);\r\n const refreshed = await refreshTokens({ discovery, refreshToken: tokens.refresh_token });\r\n const updated = mergeRefreshedTokens(tokens, refreshed, discovery.issuer);\r\n await saveTokens(updated);\r\n spinner.succeed('令牌刷新成功');\r\n return updated;\r\n } catch {\r\n spinner.fail('令牌刷新失败,请重新登录:solazah-cli account login');\r\n process.exit(1);\r\n }\r\n }\r\n\r\n return tokens;\r\n}\r\n\r\n/**\r\n * 根据 manifest.json 的 name 和 version 推算 release/ 中的 .tgz 文件路径\r\n * npm pack 生成的文件名规则:@scope/pkg-name -> scope-pkg-name-version.tgz\r\n */\r\nfunction findTgzByManifest(projectDir: string): string {\r\n const manifestPath = path.join(projectDir, 'manifest.json');\r\n if (!fs.existsSync(manifestPath)) {\r\n console.log(chalk.red('❌ 未找到 manifest.json,无法确定插件包文件名'));\r\n process.exit(1);\r\n }\r\n\r\n const manifest = fs.readJsonSync(manifestPath);\r\n const name: string = manifest.name;\r\n const version: string = manifest.version;\r\n\r\n if (!name || !version) {\r\n console.log(chalk.red('❌ manifest.json 中缺少 name 或 version 字段'));\r\n process.exit(1);\r\n }\r\n\r\n // @scope/pkg-name -> scope-pkg-name, pkg-name -> pkg-name\r\n const tgzName = name.replace(/^@/, '').replace(/\\//, '-');\r\n const tgzFile = `${tgzName}-${version}.tgz`;\r\n\r\n return path.join(projectDir, 'release', tgzFile);\r\n}\r\n\r\nexport async function publishPluginCommand(options: PublishOptions): Promise<void> {\r\n let filePath: string;\r\n\r\n if (options.file) {\r\n filePath = path.resolve(options.file);\r\n } else {\r\n const projectDir = path.resolve(options.dir || '.');\r\n filePath = findTgzByManifest(projectDir);\r\n }\r\n\r\n // 检查文件是否存在\r\n if (!await fs.pathExists(filePath)) {\r\n console.log(chalk.red(`文件不存在:${filePath}`));\r\n process.exit(1);\r\n }\r\n\r\n // 检查文件扩展名\r\n if (!filePath.endsWith('.tgz')) {\r\n console.log(chalk.red('仅支持 .tgz 格式的插件包'));\r\n process.exit(1);\r\n }\r\n\r\n // 检查登录状态 & 刷新令牌\r\n const tokens = await ensureAuth();\r\n\r\n const fileName = path.basename(filePath);\r\n const spinner = ora(`正在发布插件包:${fileName}`).start();\r\n\r\n try {\r\n const fileBuffer = await fs.readFile(filePath);\r\n const blob = new Blob([fileBuffer], { type: 'application/gzip' });\r\n\r\n const formData = new FormData();\r\n formData.append('package', blob, fileName);\r\n\r\n const res = await fetch(`${MARKETPLACE_BASE_URL}/api/v1/publish-requests`, {\r\n method: 'POST',\r\n headers: {\r\n Authorization: `Bearer ${tokens.access_token}`,\r\n },\r\n body: formData,\r\n });\r\n\r\n const data = await res.json().catch(() => ({}));\r\n\r\n if (!res.ok) {\r\n const code = (data as Record<string, unknown>).code ?? '';\r\n const message = (data as Record<string, unknown>).message ?? res.statusText;\r\n if (code === 'DUPLICATE_SUBMISSION') {\r\n spinner.fail('发布失败:该版本已提交过发布申请,请勿重复提交');\r\n } else {\r\n spinner.fail(`发布失败 (${res.status}): ${message}`);\r\n }\r\n process.exit(1);\r\n }\r\n\r\n const result = data as Record<string, unknown>;\r\n spinner.succeed('插件发布申请已提交');\r\n console.log('');\r\n console.log(chalk.green(' 申请详情:'));\r\n console.log(` 申请ID: ${result.pluginPublishRequestId}`);\r\n console.log(` 插件名称: ${result.pluginName}`);\r\n console.log(` 状态: ${result.status}`);\r\n console.log('');\r\n console.log(chalk.gray(' 发布申请已提交,等待管理员审核。'));\r\n } catch (err) {\r\n spinner.fail(`发布失败:${(err as Error).message}`);\r\n process.exit(1);\r\n }\r\n}\r\n","import { spawn } from 'child_process';\r\n\r\n/**\r\n * 跨平台打开浏览器,best-effort:失败时静默返回 false,由调用方提示用户手动打开。\r\n */\r\nexport function openBrowser(url: string): boolean {\r\n try {\r\n const platform = process.platform;\r\n let command: string;\r\n let args: string[];\r\n\r\n if (platform === 'win32') {\r\n command = 'cmd';\r\n args = ['/c', 'start', '\"\"', url.replace(/&/g, '^&')];\r\n } else if (platform === 'darwin') {\r\n command = 'open';\r\n args = [url];\r\n } else {\r\n command = 'xdg-open';\r\n args = [url];\r\n }\r\n\r\n const child = spawn(command, args, {\r\n detached: true,\r\n stdio: 'ignore',\r\n shell: false,\r\n });\r\n child.on('error', () => {\r\n // ignore; caller already printed the URL\r\n });\r\n child.unref();\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n","import {\r\n isExpired,\r\n mergeRefreshedTokens,\r\n saveTokens,\r\n StoredTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport {\r\n DEFAULT_ISSUER,\r\n discover,\r\n OidcDiscovery,\r\n refreshTokens,\r\n} from '../../utils/oauth-device.js';\r\n\r\nexport interface CommonOptions {\r\n issuer?: string;\r\n}\r\n\r\nexport const delay = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\r\n\r\nexport function loadDiscovery(issuer?: string): Promise<OidcDiscovery> {\r\n return discover(issuer || DEFAULT_ISSUER);\r\n}\r\n\r\nexport async function ensureValidTokens(\r\n discovery: OidcDiscovery,\r\n tokens: StoredTokens,\r\n): Promise<StoredTokens> {\r\n if (!isExpired(tokens)) {\r\n return tokens;\r\n }\r\n if (!tokens.refresh_token) {\r\n throw new Error('访问令牌已过期且无 refresh_token,请重新登录');\r\n }\r\n const refreshed = await refreshTokens({\r\n discovery,\r\n refreshToken: tokens.refresh_token,\r\n });\r\n const stored = mergeRefreshedTokens(tokens, refreshed, discovery.issuer);\r\n await saveTokens(stored);\r\n return stored;\r\n}\r\n","import chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport {\r\n saveTokens,\r\n toStoredTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport {\r\n fetchUserInfo,\r\n pollToken,\r\n requestDeviceCode,\r\n} from '../../utils/oauth-device.js';\r\nimport { openBrowser } from '../../utils/open-browser.js';\r\nimport { CommonOptions, delay, loadDiscovery } from './shared.js';\r\n\r\nexport async function loginCommand(options: CommonOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\nSolazah 账户登录\\n'));\r\n\r\n const spinner = ora('加载授权服务器元数据...').start();\r\n let discovery;\r\n try {\r\n discovery = await loadDiscovery(options.issuer);\r\n spinner.succeed('已加载授权服务器元数据');\r\n } catch (err) {\r\n spinner.fail('加载授权服务器元数据失败');\r\n console.error(chalk.red((err as Error).message));\r\n process.exit(1);\r\n }\r\n\r\n spinner.start('申请设备授权码...');\r\n let device;\r\n try {\r\n device = await requestDeviceCode({ discovery });\r\n spinner.succeed('已获取设备授权码');\r\n } catch (err) {\r\n spinner.fail('申请设备授权码失败');\r\n console.error(chalk.red((err as Error).message));\r\n process.exit(1);\r\n }\r\n\r\n const verificationUri = device.verification_uri_complete || device.verification_uri;\r\n\r\n console.log();\r\n console.log(chalk.bold('请在浏览器中完成授权:'));\r\n if (device.verification_uri_complete) {\r\n console.log(` 一键链接:${chalk.cyan(device.verification_uri_complete)}`);\r\n }\r\n console.log(` 用户码 :${chalk.yellow.bold(device.user_code)}`);\r\n console.log(` 有效期 :${device.expires_in} 秒`);\r\n console.log();\r\n\r\n const opened = openBrowser(verificationUri);\r\n console.log(chalk.gray(\r\n opened\r\n ? '已尝试为你打开浏览器,若未自动打开请手动访问上面的一键链接。'\r\n : '请手动在浏览器中打开上面的一键链接。',\r\n ));\r\n console.log();\r\n\r\n // RFC 8628 建议的最小轮询间隔为 5 秒\r\n let interval = Math.max(device.interval ?? 5, 5);\r\n const deadline = Date.now() + device.expires_in * 1000;\r\n\r\n const poll = ora('等待用户授权...').start();\r\n while (Date.now() < deadline) {\r\n await delay(interval * 1000);\r\n let result;\r\n try {\r\n result = await pollToken({ discovery, deviceCode: device.device_code });\r\n } catch (err) {\r\n // 网络抖动等瞬时异常:继续轮询直到截止时间\r\n poll.text = `轮询出错,将重试:${(err as Error).message}`;\r\n continue;\r\n }\r\n\r\n if (result.status === 'success' && result.tokens) {\r\n const stored = toStoredTokens(result.tokens, discovery.issuer);\r\n await saveTokens(stored);\r\n poll.succeed('授权成功');\r\n\r\n if (discovery.userinfo_endpoint) {\r\n try {\r\n const info = await fetchUserInfo({\r\n discovery,\r\n accessToken: stored.access_token,\r\n });\r\n console.log();\r\n console.log(chalk.green('已登录:'));\r\n console.log(` sub :${info.sub}`);\r\n const username = info.username ?? info.preferred_username;\r\n if (username) console.log(` username:${username}`);\r\n if (info.name) console.log(` name :${info.name}`);\r\n if (info.email) console.log(` email :${info.email}`);\r\n } catch {\r\n // 打印用户信息失败不影响登录结果\r\n }\r\n }\r\n console.log();\r\n return;\r\n }\r\n\r\n if (result.status === 'pending') continue;\r\n\r\n if (result.status === 'slow_down') {\r\n interval += 5;\r\n poll.text = `服务器要求降低轮询频率(${interval}s)...`;\r\n continue;\r\n }\r\n\r\n if (result.status === 'denied') {\r\n poll.fail('用户拒绝了授权');\r\n process.exit(1);\r\n }\r\n\r\n if (result.status === 'expired') {\r\n poll.fail('设备码已过期,请重新执行登录命令');\r\n process.exit(1);\r\n }\r\n\r\n // 其他错误视为瞬时错误,继续轮询直到截止时间\r\n poll.text = `授权失败(将重试):${result.error}${result.errorDescription ? ' - ' + result.errorDescription : ''}`;\r\n }\r\n poll.fail('等待授权超时,请重新执行登录命令');\r\n process.exit(1);\r\n}\r\n","import chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport {\r\n clearTokens,\r\n loadTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport { revokeAll } from '../../utils/oauth-device.js';\r\nimport { CommonOptions, loadDiscovery } from './shared.js';\r\n\r\nexport async function logoutCommand(options: CommonOptions): Promise<void> {\r\n const tokens = await loadTokens();\r\n if (!tokens) {\r\n console.log(chalk.yellow('当前未登录。'));\r\n return;\r\n }\r\n\r\n const spinner = ora('正在登出...').start();\r\n try {\r\n const discovery = await loadDiscovery(tokens.issuer || options.issuer).catch(() => null);\r\n if (discovery) {\r\n await revokeAll({\r\n discovery,\r\n accessToken: tokens.access_token,\r\n refreshToken: tokens.refresh_token,\r\n });\r\n }\r\n await clearTokens();\r\n spinner.succeed('已登出');\r\n } catch (err) {\r\n await clearTokens();\r\n spinner.warn('本地凭证已清除,但撤销令牌时发生错误');\r\n console.error(chalk.gray((err as Error).message));\r\n }\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadTokens } from '../../utils/auth-storage.js';\r\nimport { fetchUserInfo } from '../../utils/oauth-device.js';\r\nimport { CommonOptions, ensureValidTokens, loadDiscovery } from './shared.js';\r\n\r\nexport async function userinfoCommand(options: CommonOptions): Promise<void> {\r\n const tokens = await loadTokens();\r\n if (!tokens) {\r\n console.log(chalk.yellow('当前未登录,请先执行:solazah-cli account login'));\r\n process.exit(1);\r\n }\r\n\r\n try {\r\n const discovery = await loadDiscovery(tokens.issuer || options.issuer);\r\n const valid = await ensureValidTokens(discovery, tokens);\r\n const info = await fetchUserInfo({\r\n discovery,\r\n accessToken: valid.access_token,\r\n });\r\n console.log(JSON.stringify(info, null, 2));\r\n } catch (err) {\r\n console.error(chalk.red('获取用户信息失败:'), (err as Error).message);\r\n process.exit(1);\r\n }\r\n}\r\n","#!/usr/bin/env node\r\n\r\nimport { Command } from 'commander';\r\nimport { createPluginCommand } from './commands/plugin/create.js';\r\nimport { packagePluginCommand } from './commands/plugin/package.js';\r\nimport { publishPluginCommand } from './commands/plugin/publish.js';\r\nimport { loginCommand } from './commands/account/login.js';\r\nimport { logoutCommand } from './commands/account/logout.js';\r\nimport { userinfoCommand } from './commands/account/userinfo.js';\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('solazah-cli')\r\n .description('Solazah CLI - Command line tool for Solazah plugin development')\r\n .version(\"0.0.1\");\r\n\r\n// Plugin commands\r\nconst plugin = program\r\n .command('plugin')\r\n .description('Manage Solazah plugins');\r\n\r\nplugin\r\n .command('create')\r\n .description('Create a new Solazah plugin')\r\n .option('-n, --name <name>', 'Plugin name (e.g., my-plugin or @scope/plugin-name)')\r\n .option('-d, --dir <directory>', 'Target directory', '.')\r\n .option('-t, --template <template>', 'Template to use (e.g., react)', 'react')\r\n .option('--skip-install', 'Skip npm install')\r\n .action(createPluginCommand);\r\n\r\nplugin\r\n .command('package')\r\n .description('Build and package a Solazah plugin for distribution')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .action(packagePluginCommand);\r\n\r\nplugin\r\n .command('publish')\r\n .description('Publish a plugin package to Solazah marketplace')\r\n .option('-f, --file <file>', 'Path to the .tgz plugin package file')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .action(publishPluginCommand);\r\n\r\n// Account commands (OAuth2 Device Authorization Grant)\r\nconst account = program\r\n .command('account')\r\n .description('Manage Solazah account authentication');\r\n\r\nconst withIssuer = (cmd: Command) =>\r\n cmd.option('--issuer <issuer>', 'OAuth2 issuer URL');\r\n\r\nwithIssuer(account.command('login'))\r\n .description('Login via OAuth2 device code flow')\r\n .action(loginCommand);\r\n\r\nwithIssuer(account.command('logout'))\r\n .description('Logout and revoke stored tokens')\r\n .action(logoutCommand);\r\n\r\nwithIssuer(account.command('userinfo'))\r\n .description('Show current user info')\r\n .action(userinfoCommand);\r\n\r\nprogram.parseAsync().catch((err) => {\r\n console.error(err);\r\n process.exit(1);\r\n});\r\n"],"names":["__filename","__dirname","execAsync","answers"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/utils/validate.ts","../src/utils/file.ts","../src/commands/plugin/create.ts","../src/commands/plugin/package.ts","../src/utils/auth-storage.ts","../src/utils/oauth-device.ts","../src/commands/plugin/publish.ts","../src/commands/plugin/version.ts","../src/commands/plugin/release.ts","../src/utils/open-browser.ts","../src/commands/account/shared.ts","../src/commands/account/login.ts","../src/commands/account/logout.ts","../src/commands/account/userinfo.ts","../src/index.ts"],"sourcesContent":["import validatePackageName from 'validate-npm-package-name';\r\n\r\nexport interface ValidationResult {\r\n valid: boolean;\r\n errors: string[];\r\n}\r\n\r\n/**\r\n * 验证插件名称\r\n * 接受任何合法的 npm 包名\r\n */\r\nexport function validatePluginName(name: string): ValidationResult {\r\n const errors: string[] = [];\r\n\r\n // 基本格式检查\r\n if (!name) {\r\n errors.push('Plugin name is required');\r\n return { valid: false, errors };\r\n }\r\n\r\n // 使用 npm 包名验证\r\n const validation = validatePackageName(name);\r\n if (!validation.validForNewPackages) {\r\n if (validation.errors) {\r\n errors.push(...validation.errors);\r\n }\r\n if (validation.warnings) {\r\n errors.push(...validation.warnings);\r\n }\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n errors,\r\n };\r\n}\r\n\r\n/**\r\n * 从包名提取插件名称\r\n * @example extractPluginName('@solazah/solazah-plugin-example') => 'solazah-plugin-example'\r\n */\r\nexport function extractPluginName(packageName: string): string {\r\n // 移除作用域前缀(如 @solazah/)\r\n return packageName.replace(/^@[^/]+\\//, '');\r\n}\r\n\r\nexport function extractPluginSimpleName(packageName: string){\r\n // 移除作用域前缀(如 @solazah/)\r\n let name = packageName.replace(/^@[^/]+\\//, '');\r\n\r\n // 移除常见的插件前缀\r\n name = name.replace(/^plugin-/, '').replace(/^solazah-plugin-/, '');\r\n\r\n return name;\r\n}\r\n\r\n/**\r\n * 生成显示名称\r\n * @example generateDisplayName('example') => 'Example'\r\n */\r\nexport function generateDisplayName(name: string): string {\r\n return name\r\n .split('-')\r\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\r\n .join(' ');\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\nexport interface TemplateInfo {\r\n name: string;\r\n displayName: string;\r\n description: string;\r\n version: string;\r\n features?: string[];\r\n}\r\n\r\n/**\r\n * 检查目录是否为空\r\n */\r\nexport async function isDirectoryEmpty(dirPath: string): Promise<boolean> {\r\n try {\r\n const files = await fs.readdir(dirPath);\r\n return files.length === 0;\r\n } catch {\r\n return true; // 目录不存在,视为空\r\n }\r\n}\r\n\r\n/**\r\n * 确保目录存在\r\n */\r\nexport async function ensureDirectory(dirPath: string): Promise<void> {\r\n await fs.ensureDir(dirPath);\r\n}\r\n\r\n/**\r\n * 获取模板根目录路径\r\n */\r\nexport function getTemplatesRootDir(): string {\r\n // __dirname 在编译后会是 dist/,templates 在项目根目录\r\n // 所以需要向上一级: dist/ -> project_root/\r\n return path.resolve(__dirname, '../templates');\r\n}\r\n\r\n/**\r\n * 获取指定模板的目录路径\r\n */\r\nexport function getTemplateDir(templateName: string): string {\r\n return path.join(getTemplatesRootDir(), templateName);\r\n}\r\n\r\n/**\r\n * 获取所有可用模板\r\n */\r\nexport async function getAvailableTemplates(): Promise<TemplateInfo[]> {\r\n const templatesRoot = getTemplatesRootDir();\r\n const templates: TemplateInfo[] = [];\r\n\r\n try {\r\n const entries = await fs.readdir(templatesRoot, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n if (entry.isDirectory()) {\r\n const templateJsonPath = path.join(templatesRoot, entry.name, 'template.json');\r\n\r\n if (await fs.pathExists(templateJsonPath)) {\r\n const templateInfo = await fs.readJson(templateJsonPath);\r\n templates.push(templateInfo);\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n console.error('Failed to read templates:', error);\r\n }\r\n\r\n return templates;\r\n}\r\n\r\n/**\r\n * 复制模板文件\r\n */\r\nexport async function copyTemplate(templateDir: string, targetDir: string): Promise<void> {\r\n await fs.copy(templateDir, targetDir, {\r\n filter: (src) => {\r\n // 排除 node_modules 和其他不需要的文件\r\n const basename = path.basename(src);\r\n return !['node_modules', '.git', 'dist', 'target', 'release'].includes(basename);\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * 替换文件内容中的占位符\r\n * 支持 {{PLACEHOLDER}} 格式\r\n */\r\nexport async function replacePlaceholders(\r\n filePath: string,\r\n replacements: Record<string, string>\r\n): Promise<void> {\r\n let content = await fs.readFile(filePath, 'utf-8');\r\n\r\n for (const [key, value] of Object.entries(replacements)) {\r\n // 支持 {{KEY}} 和直接字符串替换\r\n const placeholder = `{{${key}}}`;\r\n content = content.replaceAll(placeholder, value);\r\n // 兼容旧的直接替换方式\r\n content = content.replaceAll(key, value);\r\n }\r\n\r\n await fs.writeFile(filePath, content, 'utf-8');\r\n}\r\n\r\n/**\r\n * 批量替换目录中所有文件的占位符\r\n */\r\nexport async function replaceInDirectory(\r\n dirPath: string,\r\n replacements: Record<string, string>,\r\n extensions: string[] = ['.ts', '.tsx', '.js', '.jsx', '.json', '.html', '.md']\r\n): Promise<void> {\r\n const files = await fs.readdir(dirPath, { withFileTypes: true });\r\n\r\n for (const file of files) {\r\n const filePath = path.join(dirPath, file.name);\r\n\r\n if (file.isDirectory()) {\r\n // 递归处理子目录\r\n await replaceInDirectory(filePath, replacements, extensions);\r\n } else if (file.isFile()) {\r\n // 检查文件扩展名\r\n const ext = path.extname(file.name);\r\n if (extensions.includes(ext) || file.name === 'manifest.json') {\r\n await replacePlaceholders(filePath, replacements);\r\n }\r\n }\r\n }\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport inquirer from 'inquirer';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport { exec } from 'child_process';\r\nimport { promisify } from 'util';\r\nimport {\r\n validatePluginName,\r\n extractPluginName,\r\n generateDisplayName, extractPluginSimpleName,\r\n} from '../../utils/validate.js';\r\nimport {\r\n isDirectoryEmpty,\r\n ensureDirectory,\r\n getTemplateDir,\r\n getAvailableTemplates,\r\n copyTemplate,\r\n replaceInDirectory,\r\n} from '../../utils/file.js';\r\n\r\nconst execAsync = promisify(exec);\r\n\r\ninterface CreatePluginOptions {\r\n name?: string;\r\n dir?: string;\r\n template?: string;\r\n skipInstall?: boolean;\r\n}\r\n\r\nexport async function createPluginCommand(options: CreatePluginOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\n🚀 Solazah Plugin Creator\\n'));\r\n\r\n try {\r\n // 1. 获取可用模板\r\n const availableTemplates = await getAvailableTemplates();\r\n\r\n if (availableTemplates.length === 0) {\r\n console.error(chalk.red('❌ No templates found'));\r\n process.exit(1);\r\n }\r\n\r\n // 2. 选择模板\r\n let selectedTemplate = options.template || 'react';\r\n\r\n if (!options.template && availableTemplates.length > 1) {\r\n const { template } = await inquirer.prompt([\r\n {\r\n type: 'list',\r\n name: 'template',\r\n message: 'Select a template:',\r\n choices: availableTemplates.map((t) => ({\r\n name: `${t.displayName} - ${t.description}`,\r\n value: t.name,\r\n })),\r\n default: 'react',\r\n },\r\n ]);\r\n selectedTemplate = template;\r\n }\r\n\r\n const templateInfo = availableTemplates.find((t) => t.name === selectedTemplate);\r\n if (!templateInfo) {\r\n console.error(chalk.red(`❌ Template \"${selectedTemplate}\" not found`));\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.gray(`Using template: ${templateInfo.displayName}\\n`));\r\n\r\n // 3. 获取或询问插件名称\r\n let pluginName = options.name;\r\n\r\n if (!pluginName) {\r\n const answers = await inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'name',\r\n message: 'Plugin name (e.g., my-plugin or @scope/plugin-name):',\r\n validate: (input: string) => {\r\n const result = validatePluginName(input);\r\n return result.valid || result.errors.join(', ');\r\n },\r\n },\r\n ]);\r\n pluginName = answers.name;\r\n }\r\n\r\n // 验证插件名称\r\n const validation = validatePluginName(pluginName!);\r\n if (!validation.valid) {\r\n console.error(chalk.red('❌ Invalid plugin name:'));\r\n validation.errors.forEach((error) => console.error(chalk.red(` - ${error}`)));\r\n process.exit(1);\r\n }\r\n\r\n // 2. 提取信息\r\n const packageName = extractPluginName(pluginName!)\r\n const simpleName = extractPluginSimpleName(pluginName!);\r\n const defaultDisplayName = generateDisplayName(simpleName);\r\n\r\n // 3. 确定目标目录\r\n const defaultTargetDir = options.dir ? path.resolve(options.dir, packageName) : path.resolve('.');\r\n const { targetDir: confirmedTargetDir } = await inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'targetDir',\r\n message: 'Target directory:',\r\n default: defaultTargetDir,\r\n },\r\n ]);\r\n const targetDir = path.resolve(confirmedTargetDir);\r\n\r\n // 4. 询问额外信息\r\n const answers = await inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'displayName',\r\n message: 'Display name:',\r\n default: defaultDisplayName,\r\n },\r\n {\r\n type: 'input',\r\n name: 'description',\r\n message: 'Description:',\r\n default: `A Solazah plugin - ${defaultDisplayName}`,\r\n },\r\n {\r\n type: 'input',\r\n name: 'author',\r\n message: 'Author:',\r\n default: '',\r\n },\r\n {\r\n type: 'number',\r\n name: 'port',\r\n message: 'Development server port:',\r\n default: 5200,\r\n },\r\n ]);\r\n\r\n // 检查目录是否存在且非空\r\n const dirExists = await fs.pathExists(targetDir);\r\n if (dirExists) {\r\n const isEmpty = await isDirectoryEmpty(targetDir);\r\n if (!isEmpty) {\r\n const { overwrite } = await inquirer.prompt([\r\n {\r\n type: 'confirm',\r\n name: 'overwrite',\r\n message: `Directory ${chalk.cyan(targetDir)} is not empty. Overwrite?`,\r\n default: false,\r\n },\r\n ]);\r\n\r\n if (!overwrite) {\r\n console.log(chalk.yellow('❌ Cancelled'));\r\n process.exit(0);\r\n }\r\n\r\n await fs.remove(targetDir);\r\n }\r\n }\r\n\r\n // 5. 创建目录\r\n await ensureDirectory(targetDir);\r\n\r\n // 6. 复制模板\r\n const spinner = ora('Copying template files...').start();\r\n const templateDir = getTemplateDir(selectedTemplate);\r\n\r\n if (!(await fs.pathExists(templateDir))) {\r\n spinner.fail(chalk.red(`Template directory not found: ${templateDir}`));\r\n process.exit(1);\r\n }\r\n\r\n await copyTemplate(templateDir, targetDir);\r\n spinner.succeed('Template files copied');\r\n\r\n // 7. 更新文件内容\r\n spinner.start('Updating files...');\r\n\r\n // 定义占位符替换映射\r\n const replacements: Record<string, string> = {\r\n PLUGIN_NAME: pluginName!,\r\n PLUGIN_DISPLAY_NAME: answers.displayName,\r\n PLUGIN_DESCRIPTION: answers.description,\r\n PLUGIN_AUTHOR: answers.author || '',\r\n PLUGIN_VERSION: '0.0.1',\r\n DEV_PORT: answers.port.toString(),\r\n PLUGIN_SIMPLE_NAME: simpleName,\r\n PLUGIN_CLASS_NAME: `solazah-${simpleName}`,\r\n };\r\n\r\n // 批量替换目录中的占位符\r\n await replaceInDirectory(targetDir, replacements);\r\n\r\n // 额外处理特定文件(JSON 文件需要特殊处理)\r\n const packageJsonPath = path.join(targetDir, 'package.json');\r\n const packageJson = await fs.readJson(packageJsonPath);\r\n packageJson.name = pluginName;\r\n packageJson.version = '0.0.1';\r\n packageJson.description = answers.description;\r\n if (answers.author) {\r\n packageJson.author = answers.author;\r\n }\r\n packageJson.scripts.dev = `vite --port ${answers.port}`;\r\n await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });\r\n\r\n const manifestPath = path.join(targetDir, 'manifest.json');\r\n const manifest = await fs.readJson(manifestPath);\r\n manifest.name = pluginName;\r\n manifest.displayName = answers.displayName;\r\n manifest.commands = [answers.displayName];\r\n manifest.development.main = `http://localhost:${answers.port}`;\r\n await fs.writeJson(manifestPath, manifest, { spaces: 2 });\r\n\r\n // 删除不需要的文件\r\n const filesToRemove = [\r\n 'template.json',\r\n ];\r\n for (const file of filesToRemove) {\r\n const filePath = path.join(targetDir, file);\r\n if (await fs.pathExists(filePath)) {\r\n await fs.remove(filePath);\r\n }\r\n }\r\n\r\n spinner.succeed('Files updated');\r\n\r\n // 8. 安装依赖\r\n if (!options.skipInstall) {\r\n spinner.start('Installing dependencies...');\r\n try {\r\n await execAsync('npm install', { cwd: targetDir });\r\n spinner.succeed('Dependencies installed');\r\n } catch (error) {\r\n spinner.fail('Failed to install dependencies');\r\n console.log(chalk.yellow('You can install them manually by running: npm install'));\r\n }\r\n }\r\n\r\n // 9. 完成\r\n console.log(chalk.green.bold('\\n✅ Plugin created successfully!\\n'));\r\n console.log(chalk.cyan('Next steps:'));\r\n const relativePath = path.relative(process.cwd(), targetDir);\r\n if (relativePath && relativePath !== '.') {\r\n console.log(chalk.gray(` cd ${relativePath}`));\r\n }\r\n if (options.skipInstall) {\r\n console.log(chalk.gray(' npm install'));\r\n }\r\n console.log(chalk.gray(' npm run dev'));\r\n console.log();\r\n } catch (error) {\r\n console.error(chalk.red('❌ Error:'), error);\r\n process.exit(1);\r\n }\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport { exec } from 'child_process';\r\nimport { promisify } from 'util';\r\n\r\nconst execAsync = promisify(exec);\r\n\r\ninterface PackagePluginOptions {\r\n dir?: string;\r\n}\r\n\r\ntype FileCopyEntry = string | { from: string; to: string };\r\n\r\n/**\r\n * 根据 manifest.json 推断需要复制到 release/ 的文件列表\r\n */\r\nfunction resolveFilesToCopy(projectDir: string, manifest: Record<string, unknown>): FileCopyEntry[] {\r\n const entries: FileCopyEntry[] = [];\r\n\r\n // manifest.json 始终需要\r\n entries.push('manifest.json');\r\n\r\n // 可选文档文件\r\n for (const doc of ['CHANGELOG.md', 'README.md','icon.png']) {\r\n if (fs.existsSync(path.join(projectDir, doc))) {\r\n entries.push(doc);\r\n }\r\n }\r\n\r\n // 根据 manifest.main 推断构建产物目标目录\r\n // e.g. \"renderer/index.html\" → dist -> renderer\r\n // e.g. \"index.html\" → dist -> .\r\n const main = manifest.main as string | undefined;\r\n if (main) {\r\n const mainDir = path.dirname(main);\r\n entries.push({ from: 'dist', to: mainDir === '.' ? '.' : mainDir });\r\n } else {\r\n entries.push({ from: 'dist', to: '.' });\r\n }\r\n\r\n // preload 目录\r\n const preload = manifest.preload as string | undefined;\r\n if (preload) {\r\n const preloadDir = path.dirname(preload); // e.g. \"preload/preload.mjs\" -> \"preload\"\r\n const srcPreload = path.join(projectDir, 'src', 'preload');\r\n if (fs.existsSync(srcPreload)) {\r\n entries.push({ from: 'src/preload', to: preloadDir });\r\n }\r\n }\r\n\r\n // skills 目录\r\n const srcSkills = path.join(projectDir, 'src', 'skills');\r\n if (fs.existsSync(srcSkills)) {\r\n entries.push({ from: 'src/skills', to: 'skills' });\r\n }\r\n\r\n // screenshots 目录\r\n const srcScreenshots = path.join(projectDir, 'src', 'screenshots');\r\n if (fs.existsSync(srcScreenshots)) {\r\n entries.push({ from: 'src/screenshots', to: 'screenshots' });\r\n }\r\n\r\n return entries;\r\n}\r\n\r\n/**\r\n * 清理并初始化 release 目录\r\n */\r\nfunction cleanReleaseDir(releaseDir: string): void {\r\n if (fs.existsSync(releaseDir)) {\r\n fs.rmSync(releaseDir, { recursive: true, force: true });\r\n }\r\n fs.mkdirSync(releaseDir, { recursive: true });\r\n}\r\n\r\n/**\r\n * 按照文件列表复制到 release 目录\r\n */\r\nfunction copyFiles(projectDir: string, releaseDir: string, files: FileCopyEntry[]): void {\r\n for (const file of files) {\r\n if (typeof file === 'string') {\r\n const src = path.resolve(projectDir, file);\r\n const dest = path.resolve(releaseDir, file);\r\n fs.cpSync(src, dest, { recursive: true });\r\n } else {\r\n const src = path.resolve(projectDir, file.from);\r\n const dest = path.resolve(releaseDir, file.to);\r\n fs.cpSync(src, dest, { recursive: true });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 精简 package.json,只保留发布需要的字段\r\n */\r\nfunction processPackageJson(projectDir: string, releaseDir: string): void {\r\n const raw = fs.readFileSync(path.resolve(projectDir, 'package.json'), 'utf-8');\r\n const pkg = JSON.parse(raw);\r\n\r\n const cleaned: Record<string, unknown> = {\r\n name: pkg.name,\r\n main: pkg.main,\r\n type: pkg.type,\r\n version: pkg.version,\r\n description: pkg.description,\r\n author: pkg.author,\r\n license: pkg.license,\r\n homepage: pkg.homepage,\r\n repository: pkg.repository,\r\n keywords: pkg.keywords,\r\n peerDependencies: pkg.peerDependencies,\r\n };\r\n\r\n fs.writeFileSync(\r\n path.resolve(releaseDir, 'package.json'),\r\n JSON.stringify(cleaned, null, 2),\r\n );\r\n}\r\n\r\n/**\r\n * 清理构建产物临时目录 (dist/)\r\n * Windows 上 cpSync 之后文件句柄可能尚未完全释放,rmSync 会报 ENOTEMPTY。\r\n * 加重试逻辑,等待系统释放句柄后再删除。\r\n */\r\nfunction cleanDistDir(projectDir: string): void {\r\n const distDir = path.join(projectDir, 'dist');\r\n if (!fs.existsSync(distDir)) return;\r\n\r\n const maxRetries = 5;\r\n const retryDelayMs = 200;\r\n\r\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n try {\r\n fs.rmSync(distDir, { recursive: true, force: true });\r\n return;\r\n } catch (err: any) {\r\n if (attempt === maxRetries) throw err;\r\n // 同步等待后重试(仅用于磁盘 I/O 竞态,不阻塞事件循环中的业务逻辑)\r\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, retryDelayMs);\r\n }\r\n }\r\n}\r\n\r\nexport async function packagePluginCommand(options: PackagePluginOptions): Promise<void> {\r\n const projectDir = path.resolve(options.dir || '.');\r\n\r\n console.log(chalk.cyan.bold('\\n📦 Solazah Plugin Packager\\n'));\r\n\r\n // 1. 验证项目目录\r\n const packageJsonPath = path.join(projectDir, 'package.json');\r\n if (!(await fs.pathExists(packageJsonPath))) {\r\n console.error(chalk.red('❌ No package.json found in the target directory.'));\r\n process.exit(1);\r\n }\r\n\r\n const manifestPath = path.join(projectDir, 'manifest.json');\r\n if (!(await fs.pathExists(manifestPath))) {\r\n console.error(chalk.red('❌ No manifest.json found. Is this a Solazah plugin project?'));\r\n process.exit(1);\r\n }\r\n\r\n const packageData = await fs.readJson(packageJsonPath);\r\n const manifest = await fs.readJson(manifestPath);\r\n console.log(chalk.gray(`Plugin: ${packageData.name || 'unknown'} v${packageData.version || '0.0.0'}\\n`));\r\n\r\n // 2. 构建项目\r\n const buildSpinner = ora('Building plugin...').start();\r\n try {\r\n await execAsync('npm run build', { cwd: projectDir });\r\n buildSpinner.succeed('Build completed');\r\n } catch (error: any) {\r\n buildSpinner.fail('Build failed');\r\n console.error(chalk.red(error.stderr || error.message));\r\n process.exit(1);\r\n }\r\n\r\n // 3. 打包到 release/\r\n const packageSpinner = ora('Packaging plugin...').start();\r\n try {\r\n const releaseDir = path.join(projectDir, 'release');\r\n const filesToCopy = resolveFilesToCopy(projectDir, manifest);\r\n\r\n cleanReleaseDir(releaseDir);\r\n copyFiles(projectDir, releaseDir, filesToCopy);\r\n processPackageJson(projectDir, releaseDir);\r\n cleanDistDir(projectDir);\r\n\r\n packageSpinner.succeed('Package created in release/');\r\n } catch (error: any) {\r\n packageSpinner.fail('Packaging failed');\r\n console.error(chalk.red(error.message));\r\n process.exit(1);\r\n }\r\n\r\n // 4. 生成 .tgz 文件到 release/\r\n const tgzSpinner = ora('Creating .tgz archive...').start();\r\n try {\r\n const { stdout } = await execAsync('npm pack ./release --pack-destination ./release', { cwd: projectDir });\r\n const tgzFile = stdout.trim();\r\n tgzSpinner.succeed(`Archive created: ${chalk.cyan('release/' + tgzFile)}`);\r\n } catch (error: any) {\r\n tgzSpinner.fail('Failed to create .tgz archive');\r\n console.error(chalk.red(error.stderr || error.message));\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.green.bold('\\n✅ Plugin packaged successfully!\\n'));\r\n}\r\n","import path from 'path';\r\nimport os from 'os';\r\nimport fs from 'fs-extra';\r\n\r\nexport interface StoredTokens {\r\n access_token: string;\r\n refresh_token?: string;\r\n id_token?: string;\r\n token_type: string;\r\n scope?: string;\r\n expires_at?: number; // epoch seconds\r\n issuer?: string;\r\n}\r\n\r\nexport interface TokenResponse {\r\n access_token: string;\r\n refresh_token?: string;\r\n id_token?: string;\r\n token_type: string;\r\n expires_in?: number;\r\n scope?: string;\r\n}\r\n\r\nconst AUTH_DIR = path.join(os.homedir(), '.solazah');\r\nconst AUTH_FILE = path.join(AUTH_DIR, 'auth.json');\r\nconst EXPIRY_SKEW_SECONDS = 30;\r\n\r\nexport async function saveTokens(tokens: StoredTokens): Promise<void> {\r\n await fs.ensureDir(AUTH_DIR);\r\n await fs.writeJson(AUTH_FILE, tokens, { spaces: 2 });\r\n try {\r\n await fs.chmod(AUTH_FILE, 0o600);\r\n } catch {\r\n // ignore chmod errors on Windows\r\n }\r\n}\r\n\r\nexport async function loadTokens(): Promise<StoredTokens | null> {\r\n try {\r\n return (await fs.readJson(AUTH_FILE)) as StoredTokens;\r\n } catch (err) {\r\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return null;\r\n }\r\n throw err;\r\n }\r\n}\r\n\r\nexport async function clearTokens(): Promise<void> {\r\n try {\r\n await fs.remove(AUTH_FILE);\r\n } catch {\r\n // ignore\r\n }\r\n}\r\n\r\nexport function isExpired(tokens: StoredTokens): boolean {\r\n if (!tokens.expires_at) return false;\r\n return Math.floor(Date.now() / 1000) >= tokens.expires_at - EXPIRY_SKEW_SECONDS;\r\n}\r\n\r\nexport function toStoredTokens(tokens: TokenResponse, issuer: string): StoredTokens {\r\n const now = Math.floor(Date.now() / 1000);\r\n return {\r\n access_token: tokens.access_token,\r\n refresh_token: tokens.refresh_token,\r\n id_token: tokens.id_token,\r\n token_type: tokens.token_type,\r\n scope: tokens.scope,\r\n expires_at: tokens.expires_in ? now + tokens.expires_in : undefined,\r\n issuer,\r\n };\r\n}\r\n\r\n/**\r\n * 合并刷新后的令牌:若服务器未返回新的 refresh_token 则保留原值。\r\n */\r\nexport function mergeRefreshedTokens(\r\n previous: StoredTokens,\r\n refreshed: TokenResponse,\r\n issuer: string,\r\n): StoredTokens {\r\n const next = toStoredTokens(refreshed, issuer);\r\n if (!next.refresh_token) {\r\n next.refresh_token = previous.refresh_token;\r\n }\r\n return next;\r\n}\r\n","import { TokenResponse } from './auth-storage.js';\r\n\r\nexport const DEFAULT_ISSUER = 'https://solazah.seayona.com/account';\r\nexport const DEFAULT_CLIENT_ID = 'solazah-cli';\r\nexport const DEFAULT_CLIENT_SECRET = 'solazah-cli';\r\nexport const DEFAULT_SCOPE = 'openid profile offline_access';\r\nexport const DEFAULT_TOKEN_TYPE = 'Bearer';\r\n\r\n/**\r\n * 授权服务器默认把 verification_uri 指向后端的表单端点\r\n * (/account/oauth2/device_verification),但我们的前端是 SPA,\r\n * 直接让用户访问后端表单会看到空白页。这里统一重写为 SPA 的\r\n * /#/device hash 路由,由前端引导用户登录并提交 user_code。\r\n */\r\nexport const DEFAULT_VERIFICATION_URI = 'https://solazah.seayona.com/account/#/device';\r\n\r\nexport const GRANT_TYPE = {\r\n DEVICE_CODE: 'urn:ietf:params:oauth:grant-type:device_code',\r\n REFRESH_TOKEN: 'refresh_token',\r\n} as const;\r\n\r\nexport const TOKEN_HINT = {\r\n ACCESS: 'access_token',\r\n REFRESH: 'refresh_token',\r\n} as const;\r\nexport type TokenTypeHint = typeof TOKEN_HINT[keyof typeof TOKEN_HINT];\r\n\r\nexport const DEVICE_ERROR = {\r\n AUTH_PENDING: 'authorization_pending',\r\n SLOW_DOWN: 'slow_down',\r\n ACCESS_DENIED: 'access_denied',\r\n EXPIRED_TOKEN: 'expired_token',\r\n} as const;\r\n\r\nexport interface OidcDiscovery {\r\n issuer: string;\r\n device_authorization_endpoint?: string;\r\n token_endpoint: string;\r\n userinfo_endpoint?: string;\r\n end_session_endpoint?: string;\r\n revocation_endpoint?: string;\r\n}\r\n\r\nexport interface DeviceAuthorizationResponse {\r\n device_code: string;\r\n user_code: string;\r\n verification_uri: string;\r\n verification_uri_complete?: string;\r\n expires_in: number;\r\n interval?: number;\r\n}\r\n\r\nexport interface UserInfo {\r\n sub: string;\r\n name?: string;\r\n username?: string;\r\n preferred_username?: string;\r\n email?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\nexport async function discover(issuer: string = DEFAULT_ISSUER): Promise<OidcDiscovery> {\r\n const url = `${issuer.replace(/\\/+$/, '')}/.well-known/openid-configuration`;\r\n const res = await fetch(url);\r\n if (!res.ok) {\r\n throw new Error(`Failed to load OIDC discovery (${res.status}): ${url}`);\r\n }\r\n return (await res.json()) as OidcDiscovery;\r\n}\r\n\r\n/**\r\n * OAuth 端点 POST:application/x-www-form-urlencoded;\r\n * 使用 client_secret_post 方式认证,client_id 和 client_secret 放在表单中。\r\n */\r\nasync function postForm(\r\n url: string,\r\n params: Record<string, string>,\r\n): Promise<{ ok: boolean; status: number; data: Record<string, unknown> }> {\r\n const body = new URLSearchParams(params);\r\n const res = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n Accept: 'application/json',\r\n },\r\n body: body.toString(),\r\n redirect: 'manual',\r\n });\r\n\r\n // 服务器返回 3xx 重定向通常意味着客户端认证失败或端点配置错误\r\n if (res.status >= 300 && res.status < 400) {\r\n const location = res.headers.get('location') ?? '(unknown)';\r\n throw new Error(\r\n `服务器返回了重定向 (${res.status}) → ${location},` +\r\n `请确认客户端 client_id 已在授权服务器注册为公共客户端`,\r\n );\r\n }\r\n\r\n const text = await res.text();\r\n let data: Record<string, unknown>;\r\n try {\r\n data = JSON.parse(text) as Record<string, unknown>;\r\n } catch {\r\n throw new Error(\r\n `服务器返回了非 JSON 响应 (${res.status}): ${text.slice(0, 200)}`,\r\n );\r\n }\r\n return { ok: res.ok, status: res.status, data };\r\n}\r\n\r\n/** 安全提取 string */\r\nfunction str(v: unknown): string | undefined {\r\n return typeof v === 'string' ? v : undefined;\r\n}\r\n/** 安全提取 number */\r\nfunction num(v: unknown): number | undefined {\r\n return typeof v === 'number' ? v : undefined;\r\n}\r\n\r\nexport async function requestDeviceCode(options: {\r\n discovery: OidcDiscovery;\r\n clientId?: string;\r\n scope?: string;\r\n}): Promise<DeviceAuthorizationResponse> {\r\n const { discovery } = options;\r\n if (!discovery.device_authorization_endpoint) {\r\n throw new Error('授权服务器未提供 device_authorization_endpoint');\r\n }\r\n const { ok, status, data } = await postForm(discovery.device_authorization_endpoint, {\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n scope: options.scope ?? DEFAULT_SCOPE,\r\n });\r\n if (!ok) {\r\n throw new Error(`设备授权请求失败 (${status}): ${JSON.stringify(data)}`);\r\n }\r\n\r\n // 兼容 snake_case(RFC 8628 标准)和 camelCase 两种响应格式\r\n const deviceCode = str(data.device_code) ?? str(data.deviceCode);\r\n const userCode = str(data.user_code) ?? str(data.userCode);\r\n const expiresIn = num(data.expires_in) ?? num(data.expiresIn);\r\n const interval = num(data.interval);\r\n\r\n if (!deviceCode || !userCode || expiresIn == null) {\r\n throw new Error(\r\n `设备授权响应缺少必要字段(device_code/user_code/expires_in),` +\r\n `实际响应:${JSON.stringify(data)}`,\r\n );\r\n }\r\n\r\n // 授权服务器默认返回后端表单端点作为 verification_uri,\r\n // 这里覆写为 SPA 地址,保证用户打开后能看到前端页面。\r\n const verificationUri = DEFAULT_VERIFICATION_URI;\r\n const verificationUriComplete = `${verificationUri}?user_code=${encodeURIComponent(userCode)}`;\r\n\r\n return {\r\n device_code: deviceCode,\r\n user_code: userCode,\r\n verification_uri: verificationUri,\r\n verification_uri_complete: verificationUriComplete,\r\n expires_in: expiresIn,\r\n interval,\r\n };\r\n}\r\n\r\nexport type PollStatus = 'success' | 'pending' | 'slow_down' | 'denied' | 'expired' | 'error';\r\n\r\nexport interface PollResult {\r\n status: PollStatus;\r\n tokens?: TokenResponse;\r\n error?: string;\r\n errorDescription?: string;\r\n}\r\n\r\nexport async function pollToken(options: {\r\n discovery: OidcDiscovery;\r\n deviceCode: string;\r\n clientId?: string;\r\n}): Promise<PollResult> {\r\n const { ok, data } = await postForm(options.discovery.token_endpoint, {\r\n grant_type: GRANT_TYPE.DEVICE_CODE,\r\n device_code: options.deviceCode,\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n });\r\n\r\n if (ok && typeof data.access_token === 'string') {\r\n return { status: 'success', tokens: tokenResponseFrom(data) };\r\n }\r\n\r\n const err = typeof data.error === 'string' ? data.error : 'error';\r\n const errorDescription =\r\n typeof data.error_description === 'string' ? data.error_description : undefined;\r\n const status: PollStatus =\r\n err === DEVICE_ERROR.AUTH_PENDING ? 'pending'\r\n : err === DEVICE_ERROR.SLOW_DOWN ? 'slow_down'\r\n : err === DEVICE_ERROR.ACCESS_DENIED ? 'denied'\r\n : err === DEVICE_ERROR.EXPIRED_TOKEN ? 'expired'\r\n : 'error';\r\n return { status, error: err, errorDescription };\r\n}\r\n\r\nexport async function fetchUserInfo(options: {\r\n discovery: OidcDiscovery;\r\n accessToken: string;\r\n}): Promise<UserInfo> {\r\n if (!options.discovery.userinfo_endpoint) {\r\n throw new Error('授权服务器未提供 userinfo_endpoint');\r\n }\r\n const res = await fetch(options.discovery.userinfo_endpoint, {\r\n headers: {\r\n Authorization: `Bearer ${options.accessToken}`,\r\n Accept: 'application/json',\r\n },\r\n });\r\n if (!res.ok) {\r\n const text = await res.text();\r\n throw new Error(`获取用户信息失败 (${res.status}): ${text}`);\r\n }\r\n return (await res.json()) as UserInfo;\r\n}\r\n\r\nexport async function refreshTokens(options: {\r\n discovery: OidcDiscovery;\r\n refreshToken: string;\r\n clientId?: string;\r\n}): Promise<TokenResponse> {\r\n const { ok, status, data } = await postForm(options.discovery.token_endpoint, {\r\n grant_type: GRANT_TYPE.REFRESH_TOKEN,\r\n refresh_token: options.refreshToken,\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n });\r\n if (!ok) {\r\n throw new Error(`刷新令牌失败 (${status}): ${JSON.stringify(data)}`);\r\n }\r\n return tokenResponseFrom(data);\r\n}\r\n\r\n/**\r\n * 撤销指定类型的令牌;best-effort,出错不抛出。\r\n */\r\nexport async function revokeToken(options: {\r\n discovery: OidcDiscovery;\r\n token: string;\r\n tokenTypeHint?: TokenTypeHint;\r\n clientId?: string;\r\n}): Promise<void> {\r\n if (!options.discovery.revocation_endpoint) {\r\n return;\r\n }\r\n const params: Record<string, string> = {\r\n token: options.token,\r\n client_id: options.clientId ?? DEFAULT_CLIENT_ID,\r\n client_secret: DEFAULT_CLIENT_SECRET,\r\n };\r\n if (options.tokenTypeHint) {\r\n params.token_type_hint = options.tokenTypeHint;\r\n }\r\n await postForm(options.discovery.revocation_endpoint, params).catch(() => {\r\n // best-effort\r\n });\r\n}\r\n\r\n/**\r\n * 并发撤销 access_token 与 refresh_token。\r\n */\r\nexport async function revokeAll(options: {\r\n discovery: OidcDiscovery;\r\n accessToken?: string;\r\n refreshToken?: string;\r\n clientId?: string;\r\n}): Promise<void> {\r\n if (!options.discovery.revocation_endpoint) return;\r\n const jobs: Promise<void>[] = [];\r\n if (options.refreshToken) {\r\n jobs.push(revokeToken({\r\n discovery: options.discovery,\r\n token: options.refreshToken,\r\n tokenTypeHint: TOKEN_HINT.REFRESH,\r\n clientId: options.clientId,\r\n }));\r\n }\r\n if (options.accessToken) {\r\n jobs.push(revokeToken({\r\n discovery: options.discovery,\r\n token: options.accessToken,\r\n tokenTypeHint: TOKEN_HINT.ACCESS,\r\n clientId: options.clientId,\r\n }));\r\n }\r\n await Promise.all(jobs);\r\n}\r\n\r\nfunction tokenResponseFrom(data: Record<string, unknown>): TokenResponse {\r\n return {\r\n access_token: String(data.access_token ?? ''),\r\n refresh_token: typeof data.refresh_token === 'string' ? data.refresh_token : undefined,\r\n id_token: typeof data.id_token === 'string' ? data.id_token : undefined,\r\n token_type: typeof data.token_type === 'string' ? data.token_type : DEFAULT_TOKEN_TYPE,\r\n expires_in: typeof data.expires_in === 'number' ? data.expires_in : undefined,\r\n scope: typeof data.scope === 'string' ? data.scope : undefined,\r\n };\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport {\r\n loadTokens,\r\n isExpired,\r\n mergeRefreshedTokens,\r\n saveTokens,\r\n StoredTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport { discover, refreshTokens } from '../../utils/oauth-device.js';\r\n\r\nconst MARKETPLACE_BASE_URL = 'https://solazah.seayona.com/marketplace';\r\n\r\ninterface PublishOptions {\r\n file?: string;\r\n dir?: string;\r\n}\r\n\r\nasync function ensureAuth(): Promise<StoredTokens> {\r\n const tokens = await loadTokens();\r\n if (!tokens) {\r\n console.log(chalk.red('当前未登录,请先执行:solazah-cli account login'));\r\n process.exit(1);\r\n }\r\n\r\n if (isExpired(tokens) && tokens.refresh_token) {\r\n const spinner = ora('令牌已过期,正在刷新...').start();\r\n try {\r\n const discovery = await discover(tokens.issuer);\r\n const refreshed = await refreshTokens({ discovery, refreshToken: tokens.refresh_token });\r\n const updated = mergeRefreshedTokens(tokens, refreshed, discovery.issuer);\r\n await saveTokens(updated);\r\n spinner.succeed('令牌刷新成功');\r\n return updated;\r\n } catch {\r\n spinner.fail('令牌刷新失败,请重新登录:solazah-cli account login');\r\n process.exit(1);\r\n }\r\n }\r\n\r\n return tokens;\r\n}\r\n\r\n/**\r\n * 根据 manifest.json 的 name 和 version 推算 release/ 中的 .tgz 文件路径\r\n * npm pack 生成的文件名规则:@scope/pkg-name -> scope-pkg-name-version.tgz\r\n */\r\nfunction findTgzByManifest(projectDir: string): string {\r\n const manifestPath = path.join(projectDir, 'manifest.json');\r\n if (!fs.existsSync(manifestPath)) {\r\n console.log(chalk.red('❌ 未找到 manifest.json,无法确定插件包文件名'));\r\n process.exit(1);\r\n }\r\n\r\n const manifest = fs.readJsonSync(manifestPath);\r\n const name: string = manifest.name;\r\n const version: string = manifest.version;\r\n\r\n if (!name || !version) {\r\n console.log(chalk.red('❌ manifest.json 中缺少 name 或 version 字段'));\r\n process.exit(1);\r\n }\r\n\r\n // @scope/pkg-name -> scope-pkg-name, pkg-name -> pkg-name\r\n const tgzName = name.replace(/^@/, '').replace(/\\//, '-');\r\n const tgzFile = `${tgzName}-${version}.tgz`;\r\n\r\n return path.join(projectDir, 'release', tgzFile);\r\n}\r\n\r\nexport async function publishPluginCommand(options: PublishOptions): Promise<void> {\r\n let filePath: string;\r\n\r\n if (options.file) {\r\n filePath = path.resolve(options.file);\r\n } else {\r\n const projectDir = path.resolve(options.dir || '.');\r\n filePath = findTgzByManifest(projectDir);\r\n }\r\n\r\n // 检查文件是否存在\r\n if (!await fs.pathExists(filePath)) {\r\n console.log(chalk.red(`文件不存在:${filePath}`));\r\n process.exit(1);\r\n }\r\n\r\n // 检查文件扩展名\r\n if (!filePath.endsWith('.tgz')) {\r\n console.log(chalk.red('仅支持 .tgz 格式的插件包'));\r\n process.exit(1);\r\n }\r\n\r\n // 检查登录状态 & 刷新令牌\r\n const tokens = await ensureAuth();\r\n\r\n const fileName = path.basename(filePath);\r\n const spinner = ora(`正在发布插件包:${fileName}`).start();\r\n\r\n try {\r\n const fileBuffer = await fs.readFile(filePath);\r\n const blob = new Blob([fileBuffer], { type: 'application/gzip' });\r\n\r\n const formData = new FormData();\r\n formData.append('package', blob, fileName);\r\n\r\n const res = await fetch(`${MARKETPLACE_BASE_URL}/api/v1/publish-requests`, {\r\n method: 'POST',\r\n headers: {\r\n Authorization: `Bearer ${tokens.access_token}`,\r\n },\r\n body: formData,\r\n });\r\n\r\n const data = await res.json().catch(() => ({}));\r\n\r\n if (!res.ok) {\r\n const code = (data as Record<string, unknown>).code ?? '';\r\n const message = (data as Record<string, unknown>).message ?? res.statusText;\r\n if (code === 'DUPLICATE_SUBMISSION') {\r\n spinner.fail('发布失败:该版本已提交过发布申请,请勿重复提交');\r\n } else {\r\n spinner.fail(`发布失败 (${res.status}): ${message}`);\r\n }\r\n process.exit(1);\r\n }\r\n\r\n const result = data as Record<string, unknown>;\r\n spinner.succeed('插件发布申请已提交');\r\n console.log('');\r\n console.log(chalk.green(' 申请详情:'));\r\n console.log(` 申请ID: ${result.pluginPublishRequestId}`);\r\n console.log(` 插件名称: ${result.pluginName}`);\r\n console.log(` 状态: ${result.status}`);\r\n console.log('');\r\n console.log(chalk.gray(' 发布申请已提交,等待管理员审核。'));\r\n } catch (err) {\r\n spinner.fail(`发布失败:${(err as Error).message}`);\r\n process.exit(1);\r\n }\r\n}\r\n","import path from 'path';\r\nimport { createRequire } from 'module';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport semver from 'semver';\r\nimport { promisify } from 'util';\r\nimport { exec } from 'child_process';\r\nimport conventionalChangelog from 'conventional-changelog-core';\r\n\r\nconst require = createRequire(import.meta.url);\r\nconst conventionalRecommendedBump = require('conventional-recommended-bump') as (\r\n options: Record<string, unknown>,\r\n callback: (err: Error | null, result: { releaseType: string }) => void,\r\n) => void;\r\n\r\nconst execAsync = promisify(exec);\r\nconst bumpAsync = (options: Record<string, unknown>): Promise<{ releaseType: string }> =>\r\n new Promise((resolve, reject) =>\r\n conventionalRecommendedBump(options, (err, result) => {\r\n if (err) reject(err);\r\n else resolve(result);\r\n }),\r\n );\r\n\r\ninterface VersionOptions {\r\n dir?: string;\r\n releaseAs?: string;\r\n}\r\n\r\n// 读取 .versionrc(如果存在),提取自定义 commit type → section 映射\r\nfunction loadVersionrc(projectDir: string): Record<string, unknown> {\r\n for (const name of ['.versionrc', '.versionrc.json']) {\r\n const p = path.join(projectDir, name);\r\n if (fs.existsSync(p)) {\r\n try {\r\n return fs.readJsonSync(p);\r\n } catch {\r\n // ignore malformed versionrc\r\n }\r\n }\r\n }\r\n return {};\r\n}\r\n\r\n// 把 .versionrc 中的 types 数组转成 conventional-changelog-core 需要的 parserOpts / writerOpts\r\nfunction buildChangelogConfig(versionrc: Record<string, unknown>) {\r\n const types = versionrc.types as Array<{ type: string; section?: string; hidden?: boolean }> | undefined;\r\n if (!types) return {};\r\n\r\n const parserOpts = {\r\n noteKeywords: ['BREAKING CHANGE', 'BREAKING-CHANGE'],\r\n };\r\n\r\n const writerOpts = {\r\n transform: (commit: Record<string, unknown>) => {\r\n const match = types.find((t) => t.type === commit.type);\r\n if (!match) return false;\r\n if (match.hidden) return false;\r\n commit.type = match.section ?? commit.type;\r\n return commit;\r\n },\r\n groupBy: 'type',\r\n commitGroupsSort: 'title',\r\n commitsSort: ['scope', 'subject'],\r\n };\r\n\r\n return { parserOpts, writerOpts };\r\n}\r\n\r\n// 生成本次发布的 changelog 片段\r\nasync function generateChangelogEntry(projectDir: string, newVersion: string, versionrc: Record<string, unknown>): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n const { parserOpts, writerOpts } = buildChangelogConfig(versionrc);\r\n const header = (versionrc.header as string | undefined) ?? '# CHANGELOG\\n\\n';\r\n\r\n const stream = conventionalChangelog(\r\n { preset: 'angular', parserOpts, writerOpts } as Parameters<typeof conventionalChangelog>[0],\r\n { version: newVersion } as Parameters<typeof conventionalChangelog>[1],\r\n undefined,\r\n undefined,\r\n undefined,\r\n );\r\n\r\n let content = '';\r\n stream.on('data', (chunk: Buffer | string) => { content += chunk.toString(); });\r\n stream.on('end', () => resolve(content));\r\n stream.on('error', reject);\r\n\r\n // cwd 必须在插件项目目录,conventional-changelog 默认从当前目录读 git log\r\n (stream as any).cwd = projectDir;\r\n });\r\n}\r\n\r\n// 更新文件中的 version 字段(package.json / manifest.json)\r\nfunction bumpVersionInFile(filePath: string, newVersion: string): void {\r\n if (!fs.existsSync(filePath)) return;\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n // 只替换顶层 \"version\" 字段,避免误改嵌套结构\r\n const updated = content.replace(\r\n /^(\\s*\"version\"\\s*:\\s*)\"[^\"]*\"/m,\r\n `$1\"${newVersion}\"`,\r\n );\r\n fs.writeFileSync(filePath, updated, 'utf-8');\r\n}\r\n\r\n// 追加 changelog 片段到 CHANGELOG.md(在现有内容之前)\r\nfunction prependChangelog(projectDir: string, entry: string, header: string): void {\r\n const changelogPath = path.join(projectDir, 'CHANGELOG.md');\r\n const existing = fs.existsSync(changelogPath) ? fs.readFileSync(changelogPath, 'utf-8') : '';\r\n // 去掉已有的 header 行,再重新写\r\n const body = existing.startsWith(header.trim())\r\n ? existing.slice(header.trim().length).trimStart()\r\n : existing;\r\n fs.writeFileSync(changelogPath, header.trim() + '\\n\\n' + entry + (body ? '\\n' + body : ''), 'utf-8');\r\n}\r\n\r\nexport async function versionPluginCommand(options: VersionOptions): Promise<void> {\r\n const projectDir = path.resolve(options.dir || '.');\r\n\r\n const packageJsonPath = path.join(projectDir, 'package.json');\r\n const manifestPath = path.join(projectDir, 'manifest.json');\r\n\r\n if (!(await fs.pathExists(packageJsonPath))) {\r\n console.error(chalk.red('❌ No package.json found.'));\r\n process.exit(1);\r\n }\r\n\r\n const pkg = await fs.readJson(packageJsonPath);\r\n const currentVersion: string = pkg.version ?? '0.0.0';\r\n\r\n // 1. 确定 release type\r\n let releaseType = options.releaseAs;\r\n if (!releaseType) {\r\n const spinner = ora('Analyzing commits...').start();\r\n try {\r\n // conventional-recommended-bump 从 process.cwd() 读 git,需要切换目录\r\n const prevCwd = process.cwd();\r\n process.chdir(projectDir);\r\n const result = await bumpAsync({ preset: 'angular' });\r\n process.chdir(prevCwd);\r\n releaseType = result.releaseType;\r\n spinner.succeed(`Release type: ${chalk.cyan(releaseType)} (${result.releaseType})`);\r\n } catch (err: any) {\r\n spinner.fail(`Failed to analyze commits: ${err.message}`);\r\n process.exit(1);\r\n }\r\n }\r\n\r\n // 2. 计算新版本号\r\n const newVersion = semver.inc(currentVersion, releaseType as semver.ReleaseType);\r\n if (!newVersion) {\r\n console.error(chalk.red(`❌ Invalid version: ${currentVersion} + ${releaseType}`));\r\n process.exit(1);\r\n }\r\n\r\n const versionrc = loadVersionrc(projectDir);\r\n const header = (versionrc.header as string | undefined) ?? '# CHANGELOG\\n\\n';\r\n\r\n console.log(chalk.gray(`\\n ${currentVersion} → ${chalk.green(newVersion)}\\n`));\r\n\r\n // 3. 生成 changelog 片段\r\n const changelogSpinner = ora('Generating changelog...').start();\r\n let changelogEntry = '';\r\n try {\r\n const prevCwd = process.cwd();\r\n process.chdir(projectDir);\r\n changelogEntry = await generateChangelogEntry(projectDir, newVersion, versionrc);\r\n process.chdir(prevCwd);\r\n changelogSpinner.succeed('Changelog generated');\r\n } catch (err: any) {\r\n changelogSpinner.warn(`Changelog generation skipped: ${err.message}`);\r\n }\r\n\r\n // 4. 更新版本号\r\n const bumpSpinner = ora('Bumping version files...').start();\r\n bumpVersionInFile(packageJsonPath, newVersion);\r\n bumpVersionInFile(path.join(projectDir, 'package-lock.json'), newVersion);\r\n bumpVersionInFile(manifestPath, newVersion);\r\n bumpSpinner.succeed('Version files updated');\r\n\r\n // 5. 写入 CHANGELOG.md\r\n if (changelogEntry.trim()) {\r\n prependChangelog(projectDir, changelogEntry.trim(), header);\r\n ora('').succeed('CHANGELOG.md updated');\r\n }\r\n\r\n // 6. git commit + tag\r\n const gitSpinner = ora('Creating git commit and tag...').start();\r\n try {\r\n const filesToStage = ['package.json', 'manifest.json', 'CHANGELOG.md', 'package-lock.json']\r\n .filter((f) => fs.existsSync(path.join(projectDir, f)));\r\n\r\n await execAsync(`git add ${filesToStage.join(' ')}`, { cwd: projectDir });\r\n await execAsync(`git commit -m \"chore(release): ${newVersion}\"`, { cwd: projectDir });\r\n\r\n // 尝试打 tag,已存在则跳过\r\n try {\r\n await execAsync(`git tag -a v${newVersion} -m \"chore(release): ${newVersion}\"`, { cwd: projectDir });\r\n } catch (tagErr: any) {\r\n if (!tagErr.message?.includes('already exists')) throw tagErr;\r\n gitSpinner.warn(`Tag v${newVersion} already exists, skipping`);\r\n return;\r\n }\r\n\r\n gitSpinner.succeed(`Tagged release v${newVersion}`);\r\n } catch (err: any) {\r\n gitSpinner.fail(`Git operations failed: ${err.stderr || err.message}`);\r\n process.exit(1);\r\n }\r\n\r\n console.log(chalk.green.bold(`\\n✅ Version bumped to ${newVersion}\\n`));\r\n}\r\n","import path from 'path';\r\nimport fs from 'fs-extra';\r\nimport chalk from 'chalk';\r\nimport { versionPluginCommand } from './version.js';\r\nimport { packagePluginCommand } from './package.js';\r\nimport { publishPluginCommand } from './publish.js';\r\n\r\ninterface ReleaseOptions {\r\n dir?: string;\r\n}\r\n\r\nexport async function releasePluginCommand(options: ReleaseOptions): Promise<void> {\r\n const projectDir = path.resolve(options.dir || '.');\r\n\r\n if (!(await fs.pathExists(path.join(projectDir, 'package.json')))) {\r\n console.error(chalk.red('❌ No package.json found in the target directory.'));\r\n process.exit(1);\r\n }\r\n\r\n // 1. 版本升级\r\n await versionPluginCommand({ dir: projectDir });\r\n\r\n // 2. 打包\r\n await packagePluginCommand({ dir: projectDir });\r\n\r\n // 3. 发布\r\n await publishPluginCommand({ dir: projectDir });\r\n}\r\n","import { spawn } from 'child_process';\r\n\r\n/**\r\n * 跨平台打开浏览器,best-effort:失败时静默返回 false,由调用方提示用户手动打开。\r\n */\r\nexport function openBrowser(url: string): boolean {\r\n try {\r\n const platform = process.platform;\r\n let command: string;\r\n let args: string[];\r\n\r\n if (platform === 'win32') {\r\n command = 'cmd';\r\n args = ['/c', 'start', '\"\"', url.replace(/&/g, '^&')];\r\n } else if (platform === 'darwin') {\r\n command = 'open';\r\n args = [url];\r\n } else {\r\n command = 'xdg-open';\r\n args = [url];\r\n }\r\n\r\n const child = spawn(command, args, {\r\n detached: true,\r\n stdio: 'ignore',\r\n shell: false,\r\n });\r\n child.on('error', () => {\r\n // ignore; caller already printed the URL\r\n });\r\n child.unref();\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n","import {\r\n isExpired,\r\n mergeRefreshedTokens,\r\n saveTokens,\r\n StoredTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport {\r\n DEFAULT_ISSUER,\r\n discover,\r\n OidcDiscovery,\r\n refreshTokens,\r\n} from '../../utils/oauth-device.js';\r\n\r\nexport interface CommonOptions {\r\n issuer?: string;\r\n}\r\n\r\nexport const delay = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\r\n\r\nexport function loadDiscovery(issuer?: string): Promise<OidcDiscovery> {\r\n return discover(issuer || DEFAULT_ISSUER);\r\n}\r\n\r\nexport async function ensureValidTokens(\r\n discovery: OidcDiscovery,\r\n tokens: StoredTokens,\r\n): Promise<StoredTokens> {\r\n if (!isExpired(tokens)) {\r\n return tokens;\r\n }\r\n if (!tokens.refresh_token) {\r\n throw new Error('访问令牌已过期且无 refresh_token,请重新登录');\r\n }\r\n const refreshed = await refreshTokens({\r\n discovery,\r\n refreshToken: tokens.refresh_token,\r\n });\r\n const stored = mergeRefreshedTokens(tokens, refreshed, discovery.issuer);\r\n await saveTokens(stored);\r\n return stored;\r\n}\r\n","import chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport {\r\n saveTokens,\r\n toStoredTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport {\r\n fetchUserInfo,\r\n pollToken,\r\n requestDeviceCode,\r\n} from '../../utils/oauth-device.js';\r\nimport { openBrowser } from '../../utils/open-browser.js';\r\nimport { CommonOptions, delay, loadDiscovery } from './shared.js';\r\n\r\nexport async function loginCommand(options: CommonOptions): Promise<void> {\r\n console.log(chalk.cyan.bold('\\nSolazah 账户登录\\n'));\r\n\r\n const spinner = ora('加载授权服务器元数据...').start();\r\n let discovery;\r\n try {\r\n discovery = await loadDiscovery(options.issuer);\r\n spinner.succeed('已加载授权服务器元数据');\r\n } catch (err) {\r\n spinner.fail('加载授权服务器元数据失败');\r\n console.error(chalk.red((err as Error).message));\r\n process.exit(1);\r\n }\r\n\r\n spinner.start('申请设备授权码...');\r\n let device;\r\n try {\r\n device = await requestDeviceCode({ discovery });\r\n spinner.succeed('已获取设备授权码');\r\n } catch (err) {\r\n spinner.fail('申请设备授权码失败');\r\n console.error(chalk.red((err as Error).message));\r\n process.exit(1);\r\n }\r\n\r\n const verificationUri = device.verification_uri_complete || device.verification_uri;\r\n\r\n console.log();\r\n console.log(chalk.bold('请在浏览器中完成授权:'));\r\n if (device.verification_uri_complete) {\r\n console.log(` 一键链接:${chalk.cyan(device.verification_uri_complete)}`);\r\n }\r\n console.log(` 用户码 :${chalk.yellow.bold(device.user_code)}`);\r\n console.log(` 有效期 :${device.expires_in} 秒`);\r\n console.log();\r\n\r\n const opened = openBrowser(verificationUri);\r\n console.log(chalk.gray(\r\n opened\r\n ? '已尝试为你打开浏览器,若未自动打开请手动访问上面的一键链接。'\r\n : '请手动在浏览器中打开上面的一键链接。',\r\n ));\r\n console.log();\r\n\r\n // RFC 8628 建议的最小轮询间隔为 5 秒\r\n let interval = Math.max(device.interval ?? 5, 5);\r\n const deadline = Date.now() + device.expires_in * 1000;\r\n\r\n const poll = ora('等待用户授权...').start();\r\n while (Date.now() < deadline) {\r\n await delay(interval * 1000);\r\n let result;\r\n try {\r\n result = await pollToken({ discovery, deviceCode: device.device_code });\r\n } catch (err) {\r\n // 网络抖动等瞬时异常:继续轮询直到截止时间\r\n poll.text = `轮询出错,将重试:${(err as Error).message}`;\r\n continue;\r\n }\r\n\r\n if (result.status === 'success' && result.tokens) {\r\n const stored = toStoredTokens(result.tokens, discovery.issuer);\r\n await saveTokens(stored);\r\n poll.succeed('授权成功');\r\n\r\n if (discovery.userinfo_endpoint) {\r\n try {\r\n const info = await fetchUserInfo({\r\n discovery,\r\n accessToken: stored.access_token,\r\n });\r\n console.log();\r\n console.log(chalk.green('已登录:'));\r\n console.log(` sub :${info.sub}`);\r\n const username = info.username ?? info.preferred_username;\r\n if (username) console.log(` username:${username}`);\r\n if (info.name) console.log(` name :${info.name}`);\r\n if (info.email) console.log(` email :${info.email}`);\r\n } catch {\r\n // 打印用户信息失败不影响登录结果\r\n }\r\n }\r\n console.log();\r\n return;\r\n }\r\n\r\n if (result.status === 'pending') continue;\r\n\r\n if (result.status === 'slow_down') {\r\n interval += 5;\r\n poll.text = `服务器要求降低轮询频率(${interval}s)...`;\r\n continue;\r\n }\r\n\r\n if (result.status === 'denied') {\r\n poll.fail('用户拒绝了授权');\r\n process.exit(1);\r\n }\r\n\r\n if (result.status === 'expired') {\r\n poll.fail('设备码已过期,请重新执行登录命令');\r\n process.exit(1);\r\n }\r\n\r\n // 其他错误视为瞬时错误,继续轮询直到截止时间\r\n poll.text = `授权失败(将重试):${result.error}${result.errorDescription ? ' - ' + result.errorDescription : ''}`;\r\n }\r\n poll.fail('等待授权超时,请重新执行登录命令');\r\n process.exit(1);\r\n}\r\n","import chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport {\r\n clearTokens,\r\n loadTokens,\r\n} from '../../utils/auth-storage.js';\r\nimport { revokeAll } from '../../utils/oauth-device.js';\r\nimport { CommonOptions, loadDiscovery } from './shared.js';\r\n\r\nexport async function logoutCommand(options: CommonOptions): Promise<void> {\r\n const tokens = await loadTokens();\r\n if (!tokens) {\r\n console.log(chalk.yellow('当前未登录。'));\r\n return;\r\n }\r\n\r\n const spinner = ora('正在登出...').start();\r\n try {\r\n const discovery = await loadDiscovery(tokens.issuer || options.issuer).catch(() => null);\r\n if (discovery) {\r\n await revokeAll({\r\n discovery,\r\n accessToken: tokens.access_token,\r\n refreshToken: tokens.refresh_token,\r\n });\r\n }\r\n await clearTokens();\r\n spinner.succeed('已登出');\r\n } catch (err) {\r\n await clearTokens();\r\n spinner.warn('本地凭证已清除,但撤销令牌时发生错误');\r\n console.error(chalk.gray((err as Error).message));\r\n }\r\n}\r\n","import chalk from 'chalk';\r\nimport { loadTokens } from '../../utils/auth-storage.js';\r\nimport { fetchUserInfo } from '../../utils/oauth-device.js';\r\nimport { CommonOptions, ensureValidTokens, loadDiscovery } from './shared.js';\r\n\r\nexport async function userinfoCommand(options: CommonOptions): Promise<void> {\r\n const tokens = await loadTokens();\r\n if (!tokens) {\r\n console.log(chalk.yellow('当前未登录,请先执行:solazah-cli account login'));\r\n process.exit(1);\r\n }\r\n\r\n try {\r\n const discovery = await loadDiscovery(tokens.issuer || options.issuer);\r\n const valid = await ensureValidTokens(discovery, tokens);\r\n const info = await fetchUserInfo({\r\n discovery,\r\n accessToken: valid.access_token,\r\n });\r\n console.log(JSON.stringify(info, null, 2));\r\n } catch (err) {\r\n console.error(chalk.red('获取用户信息失败:'), (err as Error).message);\r\n process.exit(1);\r\n }\r\n}\r\n","#!/usr/bin/env node\r\n\r\nimport { Command } from 'commander';\r\nimport { createPluginCommand } from './commands/plugin/create.js';\r\nimport { packagePluginCommand } from './commands/plugin/package.js';\r\nimport { publishPluginCommand } from './commands/plugin/publish.js';\r\nimport { versionPluginCommand } from './commands/plugin/version.js';\r\nimport { releasePluginCommand } from './commands/plugin/release.js';\r\nimport { loginCommand } from './commands/account/login.js';\r\nimport { logoutCommand } from './commands/account/logout.js';\r\nimport { userinfoCommand } from './commands/account/userinfo.js';\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('solazah-cli')\r\n .description('Solazah CLI - Command line tool for Solazah plugin development')\r\n .version(\"0.0.1\");\r\n\r\n// Plugin commands\r\nconst plugin = program\r\n .command('plugin')\r\n .description('Manage Solazah plugins');\r\n\r\nplugin\r\n .command('create')\r\n .description('Create a new Solazah plugin')\r\n .option('-n, --name <name>', 'Plugin name (e.g., my-plugin or @scope/plugin-name)')\r\n .option('-d, --dir <directory>', 'Target directory', '.')\r\n .option('-t, --template <template>', 'Template to use (e.g., react)', 'react')\r\n .option('--skip-install', 'Skip npm install')\r\n .action(createPluginCommand);\r\n\r\nplugin\r\n .command('package')\r\n .description('Build and package a Solazah plugin for distribution')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .action(packagePluginCommand);\r\n\r\nplugin\r\n .command('publish')\r\n .description('Publish a plugin package to Solazah marketplace')\r\n .option('-f, --file <file>', 'Path to the .tgz plugin package file')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .action(publishPluginCommand);\r\n\r\nplugin\r\n .command('version')\r\n .description('Bump plugin version, update CHANGELOG and create git tag')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .option('-r, --release-as <type>', 'Specify release type: major, minor, patch')\r\n .action(versionPluginCommand);\r\n\r\nplugin\r\n .command('release')\r\n .description('Bump version, package and publish a plugin in one step')\r\n .option('-d, --dir <directory>', 'Plugin project directory', '.')\r\n .action(releasePluginCommand);\r\n\r\n// Account commands (OAuth2 Device Authorization Grant)\r\nconst account = program\r\n .command('account')\r\n .description('Manage Solazah account authentication');\r\n\r\nconst withIssuer = (cmd: Command) =>\r\n cmd.option('--issuer <issuer>', 'OAuth2 issuer URL');\r\n\r\nwithIssuer(account.command('login'))\r\n .description('Login via OAuth2 device code flow')\r\n .action(loginCommand);\r\n\r\nwithIssuer(account.command('logout'))\r\n .description('Logout and revoke stored tokens')\r\n .action(logoutCommand);\r\n\r\nwithIssuer(account.command('userinfo'))\r\n .description('Show current user info')\r\n .action(userinfoCommand);\r\n\r\nprogram.parseAsync().catch((err) => {\r\n console.error(err);\r\n process.exit(1);\r\n});\r\n"],"names":["__filename","__dirname","execAsync","answers","require"],"mappings":"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"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "solazah-cli": "./index.js"
5
5
  },
6
6
  "type": "module",
7
- "version": "0.2.11",
7
+ "version": "0.2.12",
8
8
  "description": "Solazah CLI - Command line tool for Solazah plugin development",
9
9
  "author": "",
10
10
  "license": "MIT",