@aglit-ai/cli 0.1.1 → 0.1.4

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
@@ -7,15 +7,23 @@ After the CLI and a compatible signed desktop release have been published:
7
7
 
8
8
  ```sh
9
9
  pnpm add -g @aglit-ai/cli
10
- aglit install
11
- aglit init
10
+ aglit
11
+ aglit start --headed
12
+ aglit start --headless
12
13
  aglit status
13
14
  aglit disable
14
15
  aglit enable
15
16
  aglit uninstall
16
17
  ```
17
18
 
18
- `init` opens Aglit's Google login in the browser, asks for a computer name,
19
+ Run `aglit` in an interactive terminal to open the guided setup. `aglit init`
20
+ also runs this flow and installs the signed runtime if it is missing. Setup asks
21
+ for a computer name and whether to keep the app in the background or open its
22
+ Desktop window. **Headless is the default**, including login startup. Headed
23
+ mode is an explicit choice; it shows the same app and profile. `aglit start
24
+ --headless` hides its window without disabling its connection.
25
+
26
+ Setup opens Aglit's Google login in the browser,
19
27
  detects existing Codex/Claude logins, and uses a configured authenticated provider
20
28
  or the sole available provider. Multiple usable providers prompt for a choice.
21
29
  Provider login detection is not an inference acceptance check. Original provider
@@ -25,11 +33,23 @@ accounts remain owned by the user; uninstall does not delete their original logi
25
33
  aglit init --name "Office Mac" --provider codex
26
34
  aglit providers
27
35
  aglit teams
36
+ aglit pipelines
37
+ aglit pipelines --open
28
38
  aglit init --name "Office Mac" --provider auto --team TEAM_ID
29
39
  aglit invite USER_EMAIL --team TEAM_ID
30
40
  ```
31
41
 
32
- For unattended setup, provide `--name` and an explicit `--provider` when detection
42
+ The terminal shows the current account's team memberships and authorized
43
+ pipeline devices. `aglit pipelines --open` opens the existing encrypted pipeline
44
+ viewer for pipeline contents and controls. A device listed in the catalog is not
45
+ proof that its pipelines are connected. Team selection remains invitation
46
+ context; it does not share the newly registered computer.
47
+
48
+ For unattended setup, use `aglit init --json --name "Office Mac" --provider codex
49
+ --account YOUR_EMAIL --headless`. `--account` checks the actual signed-in
50
+ account before configuration or remote access is enabled; it never selects or
51
+ impersonates an account. JSON mode and redirected input skip interactive prompts.
52
+ Provide an explicit `--provider` when detection
33
53
  cannot select a single provider. Initial account login and OS consent still need
34
54
  the user. On macOS, Screen Recording and Accessibility must be granted before
35
55
  `init` enables access. The native tray offers status, enable/disable, Console and
@@ -67,3 +87,19 @@ identity. It never adopts a managed guest's key or changes its registration.
67
87
 
68
88
  See [architecture and acceptance](../../docs/computer-use.md). The source CLI can
69
89
  be inspected without installation using `node packages/cli/bin/aglit.cjs --help`.
90
+
91
+ ## Updates
92
+
93
+ One CLI installation includes the correct signed Desktop runtime for this
94
+ computer. The runtime assets are bundled internally through npm; no separate
95
+ runtime package installation is needed.
96
+ Downloads verify the npm package integrities, every runtime part, the complete
97
+ Desktop archive and the native publisher before launch. GitHub delta downloads
98
+ are not used by this installation.
99
+
100
+ Automatic update checks run while Desktop is active. They preserve the computer's
101
+ account, device identity and profile, and wait for an idle runtime before a native
102
+ restart. Use `aglit update --check` to inspect available updates, `aglit update`
103
+ to apply one, and `aglit updates off` or `aglit updates on` to change automatic
104
+ checks. New installations follow `latest`; experimental installations can save
105
+ an explicit channel with `aglit init --channel embedded`.
package/bin/aglit.cjs CHANGED
@@ -4,24 +4,34 @@ const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
  const os = require('node:os');
6
6
  const { parseArgs } = require('node:util');
7
- const { createInterface } = require('node:readline/promises');
8
7
  const installation = require('../src/installation.cjs');
8
+ const updates = require('../src/updates.cjs');
9
+ const { createUi, teamsFromRoles } = require('../src/ui.cjs');
9
10
  const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
10
11
  const help = `Aglit Computer Use
11
12
 
12
- aglit install
13
- aglit init [--name "Computer name"] [--provider codex|claude|auto] [--team TEAM_ID] [--invite EMAIL]
13
+ aglit Interactive setup
14
+ aglit init Install and register this computer
15
+ [--name "Computer name"] [--provider codex|claude|auto]
16
+ [--account EMAIL] [--headed | --headless] [--channel latest|canary|embedded]
17
+ [--team TEAM_ID] [--invite EMAIL]
18
+ aglit install Install the signed Desktop runtime
19
+ aglit start [--headed | --headless]
14
20
  aglit status [--json]
21
+ aglit update [--check] [--channel latest|canary|embedded] [--json]
22
+ aglit updates on|off|status [--json]
15
23
  aglit enable | disable
16
24
  aglit teams
25
+ aglit pipelines [--open]
17
26
  aglit providers
18
27
  aglit invite EMAIL --team TEAM_ID
19
28
  aglit uninstall
20
29
 
21
- Uninstall revokes remote access and purges this installation, including login,
22
- settings, owned bundled plugins/helpers and local data. Mini Computer and VMs
23
- are separate. Team invitations require backend authorization; pending invites
24
- grant no access. A team selector never grants access to a personal computer.
30
+ Aglit runs in the background by default. --headed opens the Desktop window.
31
+ Use --json with install, init, start or status for non-interactive JSON output.
32
+ Uninstall revokes access and purges only this installation. Mini Computer,
33
+ VMs, shared browser profiles and original provider logins remain separate.
34
+ Team selection does not grant access to this personal computer.
25
35
  `;
26
36
 
27
37
  async function finishUninstall(receipt) {
@@ -31,108 +41,197 @@ async function finishUninstall(receipt) {
31
41
  try { process.kill(receipt.pid, 0); } catch (error) {
32
42
  if (error.code !== 'ESRCH') throw error;
33
43
  installation.purge();
34
- process.stdout.write(JSON.stringify({ uninstalled: true, purged: true }) + '\n'); return;
44
+ return { uninstalled: true, purged: true };
35
45
  }
36
46
  }
37
47
  throw Error('Runtime has not exited; local files preserved. Retry aglit uninstall after it quits.');
38
48
  }
39
49
 
40
50
  async function main() {
41
- const { values, positionals } = parseArgs({ options: { name: { type: 'string' }, provider: { type: 'string' }, team: { type: 'string' }, invite: { type: 'string' }, json: { type: 'boolean' }, help: { type: 'boolean', short: 'h' } }, allowPositionals: true, strict: true });
42
- if (values.help || !positionals.length) { process.stdout.write(help); return; }
43
- const [command, email] = positionals;
44
- const options = { install: [], init: ['name', 'provider', 'team', 'invite'], status: ['json'], enable: [], disable: [], teams: [], providers: [], invite: ['team'], uninstall: [] };
45
- if (!Object.hasOwn(options, command) || Object.keys(values).some(key => !options[command].includes(key)) || positionals.length > (command === 'invite' ? 2 : 1)) throw Error('Invalid command or options. Run aglit --help.');
51
+ const delegated = updates.delegate(__filename); if (delegated !== null) { process.exitCode = delegated; return; }
52
+ const { values, positionals } = parseArgs({ options: {
53
+ name: { type: 'string' }, provider: { type: 'string' }, account: { type: 'string' },
54
+ team: { type: 'string' }, invite: { type: 'string' }, headed: { type: 'boolean' },
55
+ headless: { type: 'boolean' }, check: { type: 'boolean' }, channel: { type: 'string' }, open: { type: 'boolean' }, json: { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
56
+ }, allowPositionals: true, strict: true });
57
+ const interactive = Boolean(process.stdin.isTTY && process.stderr.isTTY && !values.json && !process.env.CI);
58
+ if (values.help || !positionals.length && !interactive) { process.stdout.write(help); return; }
59
+ const [command = 'init', email] = positionals;
60
+ const options = {
61
+ install: ['json', 'channel'], init: ['name', 'provider', 'account', 'team', 'invite', 'headed', 'headless', 'json', 'channel'],
62
+ start: ['headed', 'headless', 'json', 'channel'], status: ['json'], update: ['check', 'channel', 'json'], updates: ['json'], enable: [], disable: [], teams: ['json'], pipelines: ['open', 'json'], providers: ['json'], invite: ['team'], uninstall: [],
63
+ };
64
+ if (!Object.hasOwn(options, command) || Object.keys(values).some(key => !options[command].includes(key)) || positionals.length > (['invite', 'updates'].includes(command) ? 2 : 1)) throw Error('Invalid command or options. Run aglit --help.');
65
+ if (command === 'updates' && !['on', 'off', 'status'].includes(email)) throw Error('Use aglit updates on|off|status');
66
+ if (values.channel !== undefined && !['latest', 'canary', 'embedded'].includes(values.channel)) throw Error('Update channel must be latest, canary or embedded');
67
+ if (values.headed && values.headless) throw Error('Choose either --headed or --headless');
46
68
  if (values.team && !/^[A-Za-z0-9._:-]{1,128}$/.test(values.team)) throw Error('Invalid team ID');
47
69
  if (values.provider && !['auto', 'codex', 'claude'].includes(values.provider)) throw Error('Provider must be auto, codex or claude');
70
+ const validEmail = value => value.length <= 254 && /^[^\s@*]+@[^\s@*]+\.[^\s@*]+$/.test(value);
48
71
  const recipient = command === 'invite' ? email : values.invite;
49
- if (recipient && (recipient.length > 254 || !/^[^\s@*]+@[^\s@*]+\.[^\s@*]+$/.test(recipient))) throw Error('An individual invitation email address is required');
72
+ if (recipient && !validEmail(recipient)) throw Error('An individual invitation email address is required');
73
+ if (values.account !== undefined && !validEmail(values.account)) throw Error('An individual account email address is required');
50
74
  if ((command === 'invite' || values.invite) && (!recipient || !values.team)) throw Error('Invitations require an email address and --team TEAM_ID');
51
- if (values.name !== undefined && (!values.name.trim() || values.name.length > 100 || /[\x00-\x1f\x7f]/.test(values.name))) throw Error('Computer name must contain 1–100 printable characters');
52
- const print = value => process.stdout.write(JSON.stringify(value, null, 2) + '\n');
53
- if (command === 'install') { print(await installation.install()); return; }
54
- if (command === 'status' && !fs.existsSync(path.join(installation.root, 'installation.json'))) {
55
- print({ product: 'Aglit Computer Use', installed: false, running: false, enabled: false }); return;
75
+ if (values.name !== undefined && (!values.name.trim() || values.name.length > 100 || /[\x00-\x1f\x7f-\x9f]/.test(values.name))) throw Error('Computer name must contain 1–100 printable characters');
76
+ const ui = await createUi(interactive);
77
+ const installed = () => fs.existsSync(path.join(installation.root, 'installation.json'));
78
+ if (command === 'update') {
79
+ ui.result(await ui.step(values.check ? 'Check npm updates' : 'Update Aglit from npm', () => updates.update({check: Boolean(values.check), channel: values.channel})));
80
+ ui.finish('Update check complete'); return;
56
81
  }
57
- if (command === 'uninstall' && !fs.existsSync(path.join(installation.root, 'installation.json'))) {
82
+ if (command === 'updates') {
83
+ if (!installed()) throw Error('Run aglit init before configuring automatic updates');
84
+ ui.result(email === 'status' ? await updates.status() : await updates.automatic(email === 'on'));
85
+ ui.finish('Automatic updates'); return;
86
+ }
87
+ if (command === 'install') {
88
+ const receipt = await ui.step('Download and verify Desktop', () => installation.install(values.channel));
89
+ updates.initialize(values.channel);
90
+ if (interactive) ui.info('Desktop installed. Run aglit init to register this computer.');
91
+ else ui.result(receipt);
92
+ ui.finish('Ready for setup'); return;
93
+ }
94
+ if (command === 'status' && !installed()) {
95
+ ui.result({ product: 'Aglit Computer Use', installed: false, running: false, enabled: false });
96
+ ui.finish('Run aglit to set up this computer'); return;
97
+ }
98
+ if (command === 'uninstall' && !installed()) {
58
99
  if (fs.existsSync(installation.root)) installation.purgeIncomplete();
59
- print({ uninstalled: true, purged: true }); return;
100
+ ui.result({ uninstalled: true, purged: true }); ui.finish('Uninstalled'); return;
60
101
  }
61
- if (command === 'uninstall' || command === 'status') {
102
+ if ((command === 'uninstall' || command === 'status') && installed()) {
62
103
  const pending = installation.readUninstall();
63
104
  if (pending) {
64
- if (command === 'uninstall') await finishUninstall(pending);
65
- else print({ installed: true, uninstalling: true, enabled: false });
66
- return;
105
+ if (command === 'uninstall') await updates.stopWorker();
106
+ ui.result(command === 'uninstall' ? await finishUninstall(pending) : { installed: true, uninstalling: true, enabled: false });
107
+ ui.finish('Uninstall cleanup'); return;
108
+ }
109
+ }
110
+ let name = values.name;
111
+ let mode = values.headed ? 'headed' : 'headless';
112
+ if (command === 'init') {
113
+ if (!name && interactive && !installed()) name = await ui.name(os.hostname());
114
+ if (interactive && values.headed === undefined && values.headless === undefined) mode = await ui.mode();
115
+ if (!installed()) {
116
+ if (!interactive && !name) throw Error('Provide --name for unattended setup');
117
+ await ui.step('Download and verify Desktop', () => installation.install(values.channel));
67
118
  }
68
119
  }
120
+ if (!installed()) throw Error('Run aglit init to install and register this computer first.');
69
121
  installation.readInstallation();
122
+ if (['start', 'init'].includes(command)) {
123
+ const recovered = await updates.recoverActivation();
124
+ if (recovered.updating) { ui.result(recovered); ui.finish('Update in progress'); return; }
125
+ }
126
+ if (['start', 'init'].includes(command)) updates.initialize(values.channel);
70
127
  const configPath = path.join(installation.profile, 'ipc/sdk-ipc.json');
71
128
  const control = (action, fields = {}) => require('../src/control.cjs').control(configPath, action, fields);
72
129
  let state;
73
130
  try { state = await control('status'); } catch (error) {
74
- if (command === 'status') { print({ installed: true, running: false, enabled: null, error: error.message }); process.exitCode = 1; return; }
75
- installation.start();
76
- const deadline = Date.now() + 60000;
77
- while (Date.now() < deadline) {
78
- await delay(1000);
79
- try { state = await control('status'); break; } catch { /* Wait for this installed runtime's native socket. */ }
80
- }
81
- if (!state) throw Error('Computer Use did not become ready. See runtime.log in its installation directory.');
131
+ if (command === 'status') { ui.result({ product: 'Aglit Computer Use', installed: true, running: false, enabled: null, error: error.message }); ui.finish('Runtime stopped'); process.exitCode = 1; return; }
132
+ state = await ui.step('Start Desktop in the background', async () => {
133
+ // A stopped legacy runtime can make its first npm migration here without
134
+ // terminating active work or relying on handoff commands it predates.
135
+ if (updates.state().automatic && updates.runtimeStopped(installation.readInstallation())) {
136
+ try { await updates.update(); } catch {
137
+ if (fs.existsSync(path.join(installation.root, 'update-transaction.json')) || fs.existsSync(path.join(installation.root, 'update-activation.json'))) {
138
+ const recovered = await updates.recoverActivation(); if (recovered.updating) throw Error('An update is still active; retry aglit start after it finishes');
139
+ }
140
+ ui.info('Automatic update deferred; starting the installed Desktop.');
141
+ }
142
+ try { const current = await control('status'); if (current.running) return current; } catch { /* No update started Desktop. */ }
143
+ }
144
+ installation.start();
145
+ const deadline = Date.now() + 60000;
146
+ while (Date.now() < deadline) {
147
+ await delay(1000);
148
+ try { return await control('status'); } catch { /* Wait for this installation's authenticated native socket. */ }
149
+ }
150
+ throw Error('Computer Use did not become ready. See runtime.log in its installation directory.');
151
+ });
152
+ }
153
+ if (state.running && ['status', 'start', 'init'].includes(command)) updates.rememberStatus(state);
154
+ if (['start', 'init'].includes(command) && state.running) updates.ensureWorker(state.pid);
155
+ if (command === 'status') { ui.result(state); ui.finish('Computer status'); return; }
156
+ if (command === 'teams' || command === 'pipelines') {
157
+ const result = await ui.step(command === 'teams' ? 'Load your teams' : 'Load authorized pipeline devices', () => control(command));
158
+ if (interactive) ui[command](result); else ui.result(result);
159
+ if (command === 'pipelines' && values.open) await control('open-pipelines');
160
+ ui.finish(command === 'teams' ? 'Your teams' : 'Your pipelines'); return;
161
+ }
162
+ if (command === 'start' || command === 'init') {
163
+ const window = await control('window', { mode });
164
+ state = { ...state, ...window };
165
+ if (command === 'start') { ui.result(state); ui.finish(mode === 'headed' ? 'Desktop window opened' : 'Running in the background'); return; }
82
166
  }
83
- if (command === 'status') { print(state); return; }
84
167
  if (command === 'init') {
85
168
  if (!state.email) {
86
- await control('login');
87
- process.stderr.write('Complete sign-in in your browser.\n');
88
- const deadline = Date.now() + 5 * 60000;
89
- while (Date.now() < deadline) {
90
- await delay(1500); state = await control('status'); if (state.email) break;
91
- }
92
- if (!state.email) throw Error('Sign-in timed out. Run aglit init to resume.');
169
+ state = await ui.step('Complete sign-in in your browser', async () => {
170
+ await control('login');
171
+ const deadline = Date.now() + 5 * 60000;
172
+ while (Date.now() < deadline) {
173
+ await delay(1500); const current = await control('status'); if (current.email) return current;
174
+ }
175
+ throw Error('Sign-in timed out. Run aglit init to resume.');
176
+ });
93
177
  }
94
- let name = values.name || state.name;
95
- if (!name && process.stdin.isTTY) {
96
- const prompt = createInterface({ input: process.stdin, output: process.stderr });
97
- try { name = (await prompt.question(`Computer name [${os.hostname()}]: `)).trim() || os.hostname(); } finally { prompt.close(); }
178
+ if (values.account && state.email?.toLowerCase() !== values.account.toLowerCase())
179
+ throw Error(`Signed in as ${state.email}; expected ${values.account}. Switch the browser sign-in account and rerun aglit init.`);
180
+ if (interactive && !values.account && !await ui.confirmAccount(state.email))
181
+ throw Object.assign(Error('Setup cancelled before enabling remote access.'), { cancelled: true });
182
+ ui.info(`Signed in as ${state.email}`);
183
+ let teamId = values.team;
184
+ if (interactive) {
185
+ const discovery = await ui.step('Load your teams and pipelines', () => Promise.allSettled([control('teams'), control('pipelines')]));
186
+ let teams = [];
187
+ if (discovery[0].status === 'fulfilled') { ui.teams(discovery[0].value); teams = teamsFromRoles(discovery[0].value); }
188
+ else ui.info('Teams are unavailable. You can retry with aglit teams.');
189
+ if (discovery[1].status === 'fulfilled') ui.pipelines(discovery[1].value);
190
+ else ui.info('Pipeline devices are unavailable. You can retry with aglit pipelines.');
191
+ if (!teamId && teams.length) teamId = await ui.team(teams);
98
192
  }
193
+ name ||= state.name;
194
+ if (!name && interactive) name = await ui.name(os.hostname());
99
195
  if (!name) throw Error('Provide --name for unattended setup');
100
- const detected = await control('providers');
196
+ const detected = await ui.step('Find signed-in AI providers', () => control('providers'));
101
197
  const available = detected.providers.filter(provider => provider.authenticated).map(provider => provider.provider);
102
198
  let provider = values.provider && values.provider !== 'auto' ? values.provider :
103
199
  available.includes(detected.preferred) ? detected.preferred : available.length === 1 ? available[0] : null;
104
- if (!provider && available.length && process.stdin.isTTY) {
105
- const prompt = createInterface({ input: process.stdin, output: process.stderr });
106
- try { provider = (await prompt.question(`Provider (${available.join(', ')}): `)).trim(); } finally { prompt.close(); }
107
- }
108
- if (!provider || !available.includes(provider)) throw Error('No provider selected with a usable login. Sign in to Codex or Claude on this computer, then rerun aglit init --provider codex|claude.');
109
- await control('configure', { name, provider, ...(values.team ? { teamId: values.team } : {}) });
110
- const permissions = await control('permissions');
111
- print(permissions);
200
+ if (!provider && available.length && interactive) provider = await ui.provider(available, detected.preferred);
201
+ if (!provider || !available.includes(provider)) throw Error('Sign in to Codex or Claude on this computer, then rerun aglit init --provider codex|claude.');
202
+ await ui.step('Save computer settings', () => control('configure', { name, provider, ...(teamId ? { teamId } : {}) }));
203
+ const permissions = await ui.step('Check screen recording and input permissions', () => control('permissions'));
204
+ if (!interactive) ui.result(permissions);
112
205
  if (permissions.permissions?.supported && (permissions.permissions.screenRecording !== 'granted' || permissions.permissions.accessibility !== 'granted'))
113
206
  throw Error('Grant Screen Recording and Accessibility in System Settings, then rerun aglit init. Anywhere Access has not been enabled.');
114
- // An enable receipt is configuration, not proof that capture/input work.
115
- await control('enable');
116
- const connectionDeadline = Date.now() + 30000;
117
- do {
118
- state = await control('status');
119
- if (state.connected) break;
120
- await delay(1000);
121
- } while (Date.now() < connectionDeadline);
122
- print(state);
123
- if (!state.connected) throw Error('Anywhere Access is enabled but its connection is not ready. Check aglit status before retrying.');
124
- if (values.invite) print(await control('invite', { teamId: values.team, email: values.invite }));
125
- return;
207
+ state = await ui.step('Connect this computer to your account', async () => {
208
+ await control('enable');
209
+ const deadline = Date.now() + 30000;
210
+ do {
211
+ const current = await control('status');
212
+ if (current.connected) return current;
213
+ await delay(1000);
214
+ } while (Date.now() < deadline);
215
+ throw Error('Anywhere Access is enabled but its connection is not ready. Check aglit status before retrying.');
216
+ });
217
+ if (values.invite) ui.result(await control('invite', { teamId: values.team, email: values.invite }));
218
+ updates.rememberStatus(state);
219
+ ui.result(state); ui.finish('Computer ready · https://console.aglit.ai/'); return;
126
220
  }
127
- if (command === 'invite') { print(await control('invite', { teamId: values.team, email })); return; }
221
+ if (command === 'invite') { ui.result(await control('invite', { teamId: values.team, email })); ui.finish('Invitation sent'); return; }
128
222
  if (command === 'uninstall') {
129
- const result = await control('uninstall');
130
- if (!result?.revoked || !result?.quitting || !Number.isSafeInteger(result.pid) || result.pid <= 0) throw Error('Revocation was not confirmed; installation preserved');
131
- await finishUninstall(result); return;
223
+ const unlock = await updates.prepareUninstall();
224
+ try {
225
+ const result = await control('uninstall');
226
+ if (!result?.revoked || !result?.quitting || !Number.isSafeInteger(result.pid) || result.pid <= 0) throw Error('Revocation was not confirmed; installation preserved');
227
+ ui.result(await ui.step('Remove this installation', () => finishUninstall(result)));
228
+ ui.finish('Uninstalled'); return;
229
+ } finally { unlock(); }
132
230
  }
133
- print(await control(command));
231
+ ui.result(await control(command)); ui.finish('Done');
134
232
  }
135
233
 
136
234
  main().then(() => process.exit(process.exitCode || 0), error => {
137
- process.stderr.write(`aglit: ${error.message}\n`); process.exit(1);
235
+ if (!error.cancelled) process.stderr.write(`aglit: ${error.message}\n`);
236
+ process.exit(error.cancelled ? 130 : 1);
138
237
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglit-ai/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "Install and manage Aglit Computer Use",
5
5
  "license": "UNLICENSED",
6
6
  "type": "commonjs",
@@ -16,6 +16,7 @@
16
16
  "node": ">=22"
17
17
  },
18
18
  "dependencies": {
19
+ "@clack/prompts": "1.8.1",
19
20
  "yauzl": "3.4.0"
20
21
  },
21
22
  "publishConfig": {