@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 +40 -4
- package/bin/aglit.cjs +171 -72
- package/package.json +2 -1
- package/src/installation.cjs +120 -81
- package/src/launcher.cjs +30 -0
- package/src/ui.cjs +81 -0
- package/src/update-worker.cjs +37 -0
- package/src/updates.cjs +530 -0
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
|
|
11
|
-
aglit
|
|
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
|
-
`
|
|
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
|
-
|
|
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
|
|
13
|
-
aglit init
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
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
|
|
53
|
-
|
|
54
|
-
if (command === '
|
|
55
|
-
|
|
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 === '
|
|
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
|
-
|
|
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
|
|
65
|
-
|
|
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') {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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 &&
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (!
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (values.invite)
|
|
125
|
-
|
|
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') {
|
|
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
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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`);
|
|
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.
|
|
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": {
|