veodl 1.8.3 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +796 -761
- package/docs/AGENT_GUIDE.md +82 -66
- package/package.json +1 -1
- package/src/cli.js +613 -492
- package/src/config-diagnostics.js +145 -144
- package/src/config-editor.js +285 -281
- package/src/config-template.js +224 -171
- package/src/config.js +217 -213
- package/src/downloader.js +601 -600
- package/src/interactive.js +115 -82
- package/src/runs.js +343 -333
- package/src/source-discovery.js +251 -0
- package/src/state.js +27 -27
- package/src/uninstall.js +222 -221
package/src/uninstall.js
CHANGED
|
@@ -1,221 +1,222 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
|
-
import { rm } from 'node:fs/promises';
|
|
4
|
-
import { commandOutput } from './output.js';
|
|
5
|
-
import { cacheBase
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
child
|
|
47
|
-
child.once('
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
let
|
|
54
|
-
let
|
|
55
|
-
let
|
|
56
|
-
let
|
|
57
|
-
let
|
|
58
|
-
let
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
else if (token === '--
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
else if (token === '--keep-
|
|
76
|
-
else if (token === '--keep-
|
|
77
|
-
else if (token === '--
|
|
78
|
-
else if (token === '--
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
let
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (!
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
stdout.write(`
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
stdout.write(
|
|
218
|
-
stdout.write(
|
|
219
|
-
stdout.write(
|
|
220
|
-
|
|
221
|
-
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { rm } from 'node:fs/promises';
|
|
4
|
+
import { commandOutput } from './output.js';
|
|
5
|
+
import { cacheBase } from './paths.js';
|
|
6
|
+
import { configFile as resolveConfigFile } from './config.js';
|
|
7
|
+
import { cleanText, readableError } from './utils.js';
|
|
8
|
+
import { removeAlias, validateAliasName, listAliases } from './alias.js';
|
|
9
|
+
|
|
10
|
+
const PACKAGE = 'veodl';
|
|
11
|
+
|
|
12
|
+
export const UNINSTALL_HELP = `veo uninstall - remove veo completely or one command alias
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
veo uninstall [-p|--prefix <name>] [--json]
|
|
16
|
+
Remove one alias (e.g. veo uninstall -p veodl)
|
|
17
|
+
veo uninstall [--yes] [--keep-cache] [--keep-config] [--keep-aliases] [--json]
|
|
18
|
+
Remove everything: aliases, package, cache, config
|
|
19
|
+
veo uninstall --help Show this help
|
|
20
|
+
|
|
21
|
+
With -p/--prefix, only that alias is deleted; veo itself stays installed.
|
|
22
|
+
Without -p, veo prints what would be removed. Add --yes to delete everything:
|
|
23
|
+
custom wrappers created with veo alias add, the ${PACKAGE} package itself
|
|
24
|
+
(commands veo, veodl) with npm, veo's cache directory (downloads, jobs,
|
|
25
|
+
backend tools, history, statistics) and the config file. Use --keep-cache,
|
|
26
|
+
--keep-config or --keep-aliases to preserve one part. The config file is
|
|
27
|
+
deleted by default; its path is shown before and after.
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
export function npmUninstallCommand({ platform = process.platform } = {}) {
|
|
31
|
+
const args = ['uninstall', '-g', PACKAGE];
|
|
32
|
+
if (platform === 'win32') {
|
|
33
|
+
return {
|
|
34
|
+
command: 'cmd.exe',
|
|
35
|
+
args: ['/d', '/s', '/c', `npm ${args.join(' ')}`],
|
|
36
|
+
shell: false,
|
|
37
|
+
windowsVerbatimArguments: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { command: 'npm', args, shell: false };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function runNpmUninstall({ spawnImpl = spawn, platform = process.platform, signal, timeoutMs = 600_000 } = {}) {
|
|
44
|
+
const { command, args, ...spawnOptions } = npmUninstallCommand({ platform });
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const child = spawnImpl(command, args, { ...spawnOptions, stdio: 'inherit', windowsHide: true, signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs) });
|
|
47
|
+
child.once('error', reject);
|
|
48
|
+
child.once('close', resolve);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseUninstallArgs(args) {
|
|
53
|
+
let prefix;
|
|
54
|
+
let yes = false;
|
|
55
|
+
let keepCache = false;
|
|
56
|
+
let keepConfig = false;
|
|
57
|
+
let keepAliases = false;
|
|
58
|
+
let json = false;
|
|
59
|
+
let binDir;
|
|
60
|
+
const rest = [];
|
|
61
|
+
for (let index = 0; index < args.length; index++) {
|
|
62
|
+
const token = args[index];
|
|
63
|
+
if (token === '-p' || token === '--prefix') {
|
|
64
|
+
prefix = args[++index];
|
|
65
|
+
if (!prefix) throw new Error('--prefix requires an alias name. Use: veo uninstall -p veodl');
|
|
66
|
+
} else if (token.startsWith('-p=')) {
|
|
67
|
+
prefix = token.slice(3) || undefined;
|
|
68
|
+
if (!prefix) throw new Error('--prefix requires an alias name. Use: veo uninstall -p veodl');
|
|
69
|
+
} else if (token.startsWith('--prefix=')) {
|
|
70
|
+
prefix = token.slice(9) || undefined;
|
|
71
|
+
if (!prefix) throw new Error('--prefix requires an alias name. Use: veo uninstall -p veodl');
|
|
72
|
+
} else if (token === '--yes') yes = true;
|
|
73
|
+
else if (token === '--purge') {
|
|
74
|
+
// Deprecated: full cleanup is now the default. Kept so older commands keep working.
|
|
75
|
+
} else if (token === '--keep-cache') keepCache = true;
|
|
76
|
+
else if (token === '--keep-config') keepConfig = true;
|
|
77
|
+
else if (token === '--keep-aliases') keepAliases = true;
|
|
78
|
+
else if (token === '--json') json = true;
|
|
79
|
+
else if (token === '--bin-dir') {
|
|
80
|
+
binDir = args[++index];
|
|
81
|
+
if (!binDir) throw new Error('--bin-dir requires a directory.');
|
|
82
|
+
} else if (token.startsWith('--bin-dir=')) {
|
|
83
|
+
binDir = token.slice(10) || undefined;
|
|
84
|
+
if (!binDir) throw new Error('--bin-dir requires a directory.');
|
|
85
|
+
} else rest.push(token);
|
|
86
|
+
}
|
|
87
|
+
return { prefix, yes, keepCache, keepConfig, keepAliases, json, binDir, rest };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function uninstallMain(args = [], {
|
|
91
|
+
stdout = process.stdout,
|
|
92
|
+
stderr = process.stderr,
|
|
93
|
+
platform = process.platform,
|
|
94
|
+
env = process.env,
|
|
95
|
+
spawnImpl = spawn,
|
|
96
|
+
rmImpl = rm,
|
|
97
|
+
cacheRoot,
|
|
98
|
+
configFile,
|
|
99
|
+
listImpl = listAliases,
|
|
100
|
+
removeImpl = removeAlias,
|
|
101
|
+
} = {}) {
|
|
102
|
+
[args, stdout, stderr] = commandOutput(args, stdout, stderr);
|
|
103
|
+
const { prefix, yes, keepCache, keepConfig, keepAliases, json, binDir, rest } = parseUninstallArgs(args);
|
|
104
|
+
if (rest.includes('-h') || rest.includes('--help')) {
|
|
105
|
+
stdout.write(UNINSTALL_HELP);
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
if (rest.length) throw new Error(`Unknown option for veo uninstall: ${rest.join(' ')}. Use: veo uninstall --help`);
|
|
109
|
+
|
|
110
|
+
if (prefix) {
|
|
111
|
+
if (keepCache || keepConfig || keepAliases) throw new Error('--keep-* cannot be combined with --prefix. Remove one alias or uninstall veo itself.');
|
|
112
|
+
const valid = validateAliasName(prefix);
|
|
113
|
+
const removed = await removeImpl(valid, { binDir, platform, env });
|
|
114
|
+
if (json) {
|
|
115
|
+
stdout.write(`${JSON.stringify({ status: 'removed', ...removed })}\n`);
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
stdout.write(`Alias removed: ${removed.name} (${removed.removed.length} file(s) in ${removed.binDir})\n`);
|
|
119
|
+
if (removed.builtin) stdout.write('Note: veo update recreates the shipped veodl name.\n');
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const cache = cacheRoot || cacheBase({ platform, env });
|
|
124
|
+
const config = configFile || resolveConfigFile({ platform, env });
|
|
125
|
+
const manual = `npm uninstall -g ${PACKAGE}`;
|
|
126
|
+
|
|
127
|
+
// Custom wrappers first so the plan names them; npm removes veo/veodl itself.
|
|
128
|
+
let customAliases = [];
|
|
129
|
+
let aliasDir = binDir ? path.resolve(binDir) : null;
|
|
130
|
+
try {
|
|
131
|
+
const listed = await listImpl({ binDir: binDir || undefined, platform, env });
|
|
132
|
+
aliasDir = listed.binDir;
|
|
133
|
+
customAliases = listed.aliases.filter(entry => entry.present && entry.managed && !entry.builtin).map(entry => entry.name);
|
|
134
|
+
} catch {
|
|
135
|
+
// No bin directory (e.g. running from source without a global install):
|
|
136
|
+
// npm uninstall still works, alias cleanup is reported as skipped.
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!yes) {
|
|
140
|
+
if (json) {
|
|
141
|
+
stdout.write(`${JSON.stringify({ status: 'planned', package: PACKAGE, manual, aliases: customAliases, binDir: aliasDir, cache, config })}\n`);
|
|
142
|
+
return 0;
|
|
143
|
+
}
|
|
144
|
+
stdout.write(`This removes everything: the ${PACKAGE} package with npm (commands veo, veodl).\n`);
|
|
145
|
+
if (!keepAliases) {
|
|
146
|
+
stdout.write(customAliases.length
|
|
147
|
+
? `Custom wrappers to delete: ${customAliases.join(', ')} (${aliasDir})\n`
|
|
148
|
+
: `Custom wrappers: none found${aliasDir ? ` (${aliasDir})` : ''}.\n`);
|
|
149
|
+
}
|
|
150
|
+
if (!keepCache) stdout.write(`Cache to delete: ${cache}\n`);
|
|
151
|
+
if (!keepConfig) stdout.write(`Config to delete: ${config}\n`);
|
|
152
|
+
if (keepCache || keepConfig || keepAliases) {
|
|
153
|
+
const kept = [
|
|
154
|
+
keepAliases && 'aliases',
|
|
155
|
+
keepCache && 'cache',
|
|
156
|
+
keepConfig && 'config',
|
|
157
|
+
].filter(Boolean).join(', ');
|
|
158
|
+
stdout.write(`Kept: ${kept}.\n`);
|
|
159
|
+
}
|
|
160
|
+
const keepFlags = `${keepAliases ? ' --keep-aliases' : ''}${keepCache ? ' --keep-cache' : ''}${keepConfig ? ' --keep-config' : ''}`;
|
|
161
|
+
stdout.write(`Run with --yes to proceed: veo uninstall --yes${keepFlags}\n`);
|
|
162
|
+
stdout.write(`Manual command: ${manual}\n`);
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let code;
|
|
167
|
+
try {
|
|
168
|
+
stdout.write(`Uninstalling ${PACKAGE} with npm…\n`);
|
|
169
|
+
code = await runNpmUninstall({ spawnImpl, platform, signal: AbortSignal.timeout(600_000) });
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const reason = error.code === 'ENOENT' ? 'npm was not found. Install Node.js/npm, or uninstall manually.' : `Could not run npm: ${readableError(error)}`;
|
|
172
|
+
stderr.write(`veo: ${reason}\nManual command: ${manual}\n`);
|
|
173
|
+
return 1;
|
|
174
|
+
}
|
|
175
|
+
if (code !== 0) {
|
|
176
|
+
stderr.write(`veo: npm exited with code ${code}. Uninstall manually with: ${manual}\n`);
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const removedAliases = [];
|
|
181
|
+
if (!keepAliases) {
|
|
182
|
+
for (const name of customAliases) {
|
|
183
|
+
try {
|
|
184
|
+
await removeImpl(name, { binDir: aliasDir, platform, env });
|
|
185
|
+
removedAliases.push(name);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
stderr.write(`veo: removed the package but could not delete alias ${name}: ${readableError(error)}\n`);
|
|
188
|
+
return 1;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
let cacheDeleted = false;
|
|
193
|
+
if (!keepCache) {
|
|
194
|
+
try {
|
|
195
|
+
await rmImpl(cache, { recursive: true, force: true });
|
|
196
|
+
cacheDeleted = true;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
stderr.write(`veo: removed the package but could not delete the cache ${cache}: ${readableError(error)}\n`);
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
let configDeleted = false;
|
|
203
|
+
if (!keepConfig) {
|
|
204
|
+
try {
|
|
205
|
+
await rmImpl(config, { force: true });
|
|
206
|
+
configDeleted = true;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
stderr.write(`veo: removed the package but could not delete the config ${config}: ${readableError(error)}\n`);
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (json) {
|
|
213
|
+
stdout.write(`${JSON.stringify({ status: 'uninstalled', package: PACKAGE, aliases: removedAliases, binDir: aliasDir, cacheDeleted, cache, configDeleted, config })}\n`);
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
stdout.write(`veo uninstalled (${PACKAGE}).`);
|
|
217
|
+
if (!keepAliases) stdout.write(removedAliases.length ? ` Aliases deleted: ${removedAliases.join(', ')}.` : ' No custom wrappers found.');
|
|
218
|
+
stdout.write(cacheDeleted ? ` Cache deleted: ${cache}.` : ` Cache kept: ${cache}.`);
|
|
219
|
+
stdout.write(configDeleted ? ` Config deleted: ${config}.` : ` Config kept: ${config}.`);
|
|
220
|
+
stdout.write('\n');
|
|
221
|
+
return 0;
|
|
222
|
+
}
|