@gaiaworks/gaia-cli 0.0.6 → 0.0.8

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/README.md CHANGED
@@ -27,4 +27,44 @@ npx @gaiaworks/gaia-cli@latest install --mode internal
27
27
 
28
28
  未指定 `--mode` 时默认写入 `external`。安装脚本会执行 `gaia setup --mode <external|internal>`,用于初始化本地 `install_mode` 和 Agent Skills。
29
29
 
30
- 安装时会根据 npm 包版本,从 OSS 的同版本目录下载当前平台对应的 `gaia-cli` 二进制。
30
+ 安装时会根据 npm 包版本,从 OSS 的同版本目录下载当前平台对应的 `gaia-cli` 二进制。
31
+
32
+ 升级到 npm `latest` 并保留当前安装模式:
33
+
34
+ ```bash
35
+ gaia update
36
+ ```
37
+
38
+ 仅检查或安装指定版本:
39
+
40
+ ```bash
41
+ gaia update --check
42
+ gaia update --version 0.0.7
43
+ ```
44
+
45
+ 更新命令拒绝降级。首个包含该命令的版本仍需通过 `npx ... install` 手工升级一次。
46
+
47
+ 查看脱敏后的当前有效配置,并诊断配置、端点、登录凭据及 Internal 远程授权服务:
48
+
49
+ ```bash
50
+ gaia config show
51
+ gaia doctor
52
+ gaia doctor --json
53
+ ```
54
+
55
+ `config show` 不输出 Token 或 Client Secret 明文。未登录属于警告;配置非法、必填端点缺失或远程授权服务健康检查失败时,`doctor` 返回非零退出码。
56
+
57
+ 卸载全局命令和 Gaia 管理的 Agent Skills,默认保留本地配置、凭据和目标信息:
58
+
59
+ ```bash
60
+ gaia uninstall
61
+ ```
62
+
63
+ 需要同时删除全部本地 Gaia 数据时使用:
64
+
65
+ ```bash
66
+ gaia uninstall --purge
67
+ gaia uninstall --purge --yes
68
+ ```
69
+
70
+ `--purge` 删除 `GAIA_CONFIG_DIR` 指向的目录;未设置该环境变量时删除 `~/.gaia`。默认会要求确认,自动化场景可显式添加 `--yes`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaiaworks/gaia-cli",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "JavaScript launcher for gaia-cli.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
package/scripts/run.js CHANGED
@@ -10,12 +10,26 @@ const { binaryName } = require('./install');
10
10
  const PACKAGE_ROOT = path.resolve(__dirname, '..');
11
11
  const BINARY_PATH = path.join(PACKAGE_ROOT, 'bin', binaryName());
12
12
 
13
- function runInstallWizard(args) {
13
+ function runInstallWizard(args) {
14
14
  const result = spawnSync(process.execPath, [path.join(__dirname, 'install-wizard.js'), ...args], {
15
15
  stdio: 'inherit',
16
16
  });
17
17
  process.exit(typeof result.status === 'number' ? result.status : 1);
18
- }
18
+ }
19
+
20
+ function runUpdater(args) {
21
+ const result = spawnSync(process.execPath, [path.join(__dirname, 'update.js'), ...args], {
22
+ stdio: 'inherit',
23
+ });
24
+ process.exit(typeof result.status === 'number' ? result.status : 1);
25
+ }
26
+
27
+ function runUninstaller(args) {
28
+ const result = spawnSync(process.execPath, [path.join(__dirname, 'uninstall.js'), ...args], {
29
+ stdio: 'inherit',
30
+ });
31
+ process.exit(typeof result.status === 'number' ? result.status : 1);
32
+ }
19
33
 
20
34
  function ensureBinary() {
21
35
  if (fs.existsSync(BINARY_PATH)) return;
@@ -40,8 +54,14 @@ function runBinary(args) {
40
54
  }
41
55
 
42
56
  const args = process.argv.slice(2);
43
- if (args[0] === 'install') {
44
- runInstallWizard(args.slice(1));
45
- }
46
-
47
- runBinary(args);
57
+ if (args[0] === 'install') {
58
+ runInstallWizard(args.slice(1));
59
+ }
60
+ if (args[0] === 'update') {
61
+ runUpdater(args.slice(1));
62
+ }
63
+ if (args[0] === 'uninstall') {
64
+ runUninstaller(args.slice(1));
65
+ }
66
+
67
+ runBinary(args);
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const os = require('node:os');
6
+ const path = require('node:path');
7
+ const { spawnSync } = require('node:child_process');
8
+ const readline = require('node:readline/promises');
9
+
10
+ function defaultExecute(command, args) {
11
+ return spawnSync(command, args, {
12
+ stdio: ['ignore', 'pipe', 'pipe'],
13
+ encoding: 'utf8',
14
+ });
15
+ }
16
+
17
+ function npmInvocation(args, platform, env) {
18
+ if (platform === 'win32') {
19
+ const npmCli = env.npm_execpath || path.join(
20
+ path.dirname(process.execPath),
21
+ 'node_modules',
22
+ 'npm',
23
+ 'bin',
24
+ 'npm-cli.js'
25
+ );
26
+ return { command: process.execPath, args: [npmCli, ...args] };
27
+ }
28
+ return { command: 'npm', args };
29
+ }
30
+
31
+ function executeChecked(execute, command, args, action) {
32
+ const result = execute(command, args);
33
+ if (result.error || result.status !== 0) {
34
+ const detail = result.error
35
+ ? result.error.message
36
+ : `${result.stdout || ''}${result.stderr || ''}`.trim();
37
+ throw new Error(`${action} failed${detail ? `: ${detail}` : ''}`);
38
+ }
39
+ }
40
+
41
+ async function defaultConfirm(message) {
42
+ const prompt = readline.createInterface({ input: process.stdin, output: process.stderr });
43
+ try {
44
+ const answer = await prompt.question(`${message} [y/N] `);
45
+ return /^(y|yes)$/i.test(answer.trim());
46
+ } finally {
47
+ prompt.close();
48
+ }
49
+ }
50
+
51
+ async function uninstallCli(args, options = {}) {
52
+ const purge = args.includes('--purge');
53
+ const yes = args.includes('--yes');
54
+ const invalidArgs = args.filter((arg) => arg !== '--purge' && arg !== '--yes');
55
+ if (invalidArgs.length > 0 || yes && !purge || new Set(args).size !== args.length) {
56
+ throw new Error(`unsupported uninstall arguments: ${args.join(' ')}`);
57
+ }
58
+
59
+ const env = options.env || process.env;
60
+ const platform = options.platform || process.platform;
61
+ const packageInfo = options.packageInfo || JSON.parse(
62
+ fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')
63
+ );
64
+ const execute = options.execute || defaultExecute;
65
+ const write = options.write || ((message) => console.error(message));
66
+ const confirm = options.confirm || defaultConfirm;
67
+ const runScript = options.runScript || path.join(__dirname, 'run.js');
68
+ const configDir = path.resolve(env.GAIA_CONFIG_DIR || path.join(os.homedir(), '.gaia'));
69
+
70
+ if (purge) {
71
+ const root = path.parse(configDir).root;
72
+ const protectedPaths = [root, path.resolve(os.homedir()), path.resolve(process.cwd())];
73
+ const normalizedConfigDir = platform === 'win32' ? configDir.toLowerCase() : configDir;
74
+ if (protectedPaths.some((protectedPath) => (
75
+ platform === 'win32' ? protectedPath.toLowerCase() : protectedPath
76
+ ) === normalizedConfigDir)) {
77
+ throw new Error(`refusing to purge unsafe Gaia configuration directory: ${configDir}`);
78
+ }
79
+ if (!yes && !await confirm(`Remove all Gaia CLI configuration and credentials at ${configDir}?`)) {
80
+ write('Uninstall cancelled.');
81
+ return { purged: false, cancelled: true, configDir };
82
+ }
83
+ }
84
+
85
+ write('Removing Gaia-managed Agent Skills...');
86
+ executeChecked(
87
+ execute,
88
+ process.execPath,
89
+ [runScript, 'skills', 'remove'],
90
+ 'remove Gaia-managed Agent Skills'
91
+ );
92
+
93
+ write(`Uninstalling ${packageInfo.name}...`);
94
+ const uninstall = npmInvocation(['uninstall', '-g', packageInfo.name], platform, env);
95
+ executeChecked(execute, uninstall.command, uninstall.args, 'uninstall Gaia CLI package');
96
+
97
+ if (purge) {
98
+ fs.rmSync(configDir, { recursive: true, force: true });
99
+ write(`Gaia CLI uninstalled. Configuration removed from ${configDir}`);
100
+ return { purged: true, configDir };
101
+ }
102
+
103
+ write(`Gaia CLI uninstalled. Configuration retained at ${configDir}`);
104
+
105
+ return { purged: false, configDir };
106
+ }
107
+
108
+ if (require.main === module) {
109
+ uninstallCli(process.argv.slice(2)).catch((error) => {
110
+ console.error(`[gaia-cli] Uninstall failed: ${error.message}`);
111
+ process.exitCode = 1;
112
+ });
113
+ }
114
+
115
+ module.exports = { uninstallCli };
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const os = require('node:os');
6
+ const path = require('node:path');
7
+ const { spawnSync } = require('node:child_process');
8
+
9
+ const registry = 'https://registry.npmjs.org';
10
+
11
+ function parseVersion(version) {
12
+ const match = String(version).match(
13
+ /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/
14
+ );
15
+ if (!match) throw new Error(`invalid Gaia CLI version: ${version}`);
16
+ return {
17
+ core: match.slice(1, 4).map(Number),
18
+ prerelease: match[4] ? match[4].split('.') : [],
19
+ };
20
+ }
21
+
22
+ function compareVersions(left, right) {
23
+ const a = parseVersion(left);
24
+ const b = parseVersion(right);
25
+ for (let index = 0; index < a.core.length; index += 1) {
26
+ if (a.core[index] !== b.core[index]) return a.core[index] - b.core[index];
27
+ }
28
+ if (a.prerelease.length === 0) return b.prerelease.length === 0 ? 0 : 1;
29
+ if (b.prerelease.length === 0) return -1;
30
+ const length = Math.max(a.prerelease.length, b.prerelease.length);
31
+ for (let index = 0; index < length; index += 1) {
32
+ const aPart = a.prerelease[index];
33
+ const bPart = b.prerelease[index];
34
+ if (aPart === undefined) return -1;
35
+ if (bPart === undefined) return 1;
36
+ if (aPart === bPart) continue;
37
+ const aNumber = /^\d+$/.test(aPart);
38
+ const bNumber = /^\d+$/.test(bPart);
39
+ if (aNumber && bNumber) return Number(aPart) - Number(bPart);
40
+ if (aNumber) return -1;
41
+ if (bNumber) return 1;
42
+ return aPart.localeCompare(bPart);
43
+ }
44
+ return 0;
45
+ }
46
+
47
+ function defaultExecute(command, args) {
48
+ return spawnSync(command, args, {
49
+ stdio: ['ignore', 'pipe', 'pipe'],
50
+ encoding: 'utf8',
51
+ });
52
+ }
53
+
54
+ function npmInvocation(args, platform, env) {
55
+ if (platform === 'win32') {
56
+ const npmCli = env.npm_execpath || path.join(
57
+ path.dirname(process.execPath),
58
+ 'node_modules',
59
+ 'npm',
60
+ 'bin',
61
+ 'npm-cli.js'
62
+ );
63
+ return { command: process.execPath, args: [npmCli, ...args] };
64
+ }
65
+ return { command: 'npm', args };
66
+ }
67
+
68
+ function executeChecked(execute, command, args, action) {
69
+ const result = execute(command, args);
70
+ if (result.error || result.status !== 0) {
71
+ const detail = result.error
72
+ ? result.error.message
73
+ : `${result.stdout || ''}${result.stderr || ''}`.trim();
74
+ throw new Error(`${action} failed${detail ? `: ${detail}` : ''}`);
75
+ }
76
+ return result;
77
+ }
78
+
79
+ function readInstallMode(env) {
80
+ const configDir = env.GAIA_CONFIG_DIR || path.join(os.homedir(), '.gaia');
81
+ const configPath = path.join(configDir, 'config.json');
82
+ let config;
83
+ try {
84
+ config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
85
+ } catch (error) {
86
+ throw new Error(`cannot read Gaia CLI configuration at ${configPath}: ${error.message}`);
87
+ }
88
+ if (config.install_mode !== 'internal' && config.install_mode !== 'external') {
89
+ throw new Error(`invalid install_mode in ${configPath}`);
90
+ }
91
+ return config.install_mode;
92
+ }
93
+
94
+ function updateCli(args, options = {}) {
95
+ const checkOnly = args.length === 1 && args[0] === '--check';
96
+ const requestedVersion = args.length === 2 && args[0] === '--version' ? args[1] : '';
97
+ if (args.length > 0 && !checkOnly && !requestedVersion) {
98
+ throw new Error(`unsupported update arguments: ${args.join(' ')}`);
99
+ }
100
+ if (requestedVersion) {
101
+ try {
102
+ parseVersion(requestedVersion);
103
+ } catch {
104
+ throw new Error(`explicit version must be a semantic version: ${requestedVersion}`);
105
+ }
106
+ }
107
+
108
+ const env = options.env || process.env;
109
+ const platform = options.platform || process.platform;
110
+ const packageInfo = options.packageInfo || JSON.parse(
111
+ fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')
112
+ );
113
+ const execute = options.execute || defaultExecute;
114
+ const write = options.write || ((message) => console.error(message));
115
+ const mode = readInstallMode(env);
116
+
117
+ const requestedSpec = requestedVersion || 'latest';
118
+ const view = npmInvocation([
119
+ 'view', `${packageInfo.name}@${requestedSpec}`, 'version', '--registry', registry,
120
+ ], platform, env);
121
+ const viewResult = executeChecked(execute, view.command, view.args, 'check latest version');
122
+ const targetVersion = viewResult.stdout.trim();
123
+ if (!targetVersion) throw new Error('npm returned an empty latest version');
124
+ if (compareVersions(targetVersion, packageInfo.version) < 0) {
125
+ throw new Error(`refusing to downgrade Gaia CLI from v${packageInfo.version} to v${targetVersion}`);
126
+ }
127
+
128
+ if (checkOnly) {
129
+ const updateAvailable = targetVersion !== packageInfo.version;
130
+ write(updateAvailable
131
+ ? `Update available: v${packageInfo.version} -> v${targetVersion}`
132
+ : `Gaia CLI is already up to date: v${packageInfo.version}`);
133
+ return {
134
+ currentVersion: packageInfo.version,
135
+ targetVersion,
136
+ updated: false,
137
+ updateAvailable,
138
+ };
139
+ }
140
+
141
+ if (targetVersion === packageInfo.version) {
142
+ write(`Gaia CLI is already up to date: v${packageInfo.version}`);
143
+ return { currentVersion: packageInfo.version, targetVersion, updated: false };
144
+ }
145
+
146
+ write(`Updating Gaia CLI from v${packageInfo.version} to v${targetVersion}...`);
147
+ const prefix = npmInvocation(['prefix', '-g'], platform, env);
148
+ const prefixResult = executeChecked(execute, prefix.command, prefix.args, 'resolve npm global prefix');
149
+ const globalPrefix = prefixResult.stdout.trim();
150
+ if (!globalPrefix) throw new Error('npm returned an empty global prefix');
151
+
152
+ const install = npmInvocation([
153
+ 'install', '-g', `${packageInfo.name}@${targetVersion}`, '--registry', registry,
154
+ ], platform, env);
155
+ executeChecked(execute, install.command, install.args, 'install Gaia CLI update');
156
+
157
+ let setupCommand;
158
+ let setupArgs;
159
+ if (platform === 'win32') {
160
+ setupCommand = process.execPath;
161
+ setupArgs = [
162
+ path.win32.join(globalPrefix, 'node_modules', ...packageInfo.name.split('/'), 'scripts', 'run.js'),
163
+ 'setup',
164
+ '--mode',
165
+ mode,
166
+ ];
167
+ } else {
168
+ setupCommand = path.posix.join(globalPrefix, 'bin', 'gaia');
169
+ setupArgs = ['setup', '--mode', mode];
170
+ }
171
+ executeChecked(execute, setupCommand, setupArgs, 'restore Gaia CLI mode');
172
+
173
+ write(`Update completed: v${targetVersion}`);
174
+ write(`Install mode retained: ${mode}`);
175
+ return { currentVersion: packageInfo.version, targetVersion, updated: true };
176
+ }
177
+
178
+ if (require.main === module) {
179
+ try {
180
+ updateCli(process.argv.slice(2));
181
+ } catch (error) {
182
+ console.error(`[gaia-cli] Update failed: ${error.message}`);
183
+ process.exit(1);
184
+ }
185
+ }
186
+
187
+ module.exports = { updateCli };