@aglit-ai/cli 0.1.1 → 0.1.2
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 +24 -4
- package/bin/aglit.cjs +128 -69
- package/package.json +2 -1
- package/src/ui.cjs +81 -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
|
package/bin/aglit.cjs
CHANGED
|
@@ -4,24 +4,30 @@ 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 { createUi, teamsFromRoles } = require('../src/ui.cjs');
|
|
9
9
|
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
10
10
|
const help = `Aglit Computer Use
|
|
11
11
|
|
|
12
|
-
aglit
|
|
13
|
-
aglit init
|
|
12
|
+
aglit Interactive setup
|
|
13
|
+
aglit init Install and register this computer
|
|
14
|
+
[--name "Computer name"] [--provider codex|claude|auto]
|
|
15
|
+
[--account EMAIL] [--headed | --headless] [--team TEAM_ID] [--invite EMAIL]
|
|
16
|
+
aglit install Install the signed Desktop runtime
|
|
17
|
+
aglit start [--headed | --headless]
|
|
14
18
|
aglit status [--json]
|
|
15
19
|
aglit enable | disable
|
|
16
20
|
aglit teams
|
|
21
|
+
aglit pipelines [--open]
|
|
17
22
|
aglit providers
|
|
18
23
|
aglit invite EMAIL --team TEAM_ID
|
|
19
24
|
aglit uninstall
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
Aglit runs in the background by default. --headed opens the Desktop window.
|
|
27
|
+
Use --json with install, init, start or status for non-interactive JSON output.
|
|
28
|
+
Uninstall revokes access and purges only this installation. Mini Computer,
|
|
29
|
+
VMs, shared browser profiles and original provider logins remain separate.
|
|
30
|
+
Team selection does not grant access to this personal computer.
|
|
25
31
|
`;
|
|
26
32
|
|
|
27
33
|
async function finishUninstall(receipt) {
|
|
@@ -31,108 +37,161 @@ async function finishUninstall(receipt) {
|
|
|
31
37
|
try { process.kill(receipt.pid, 0); } catch (error) {
|
|
32
38
|
if (error.code !== 'ESRCH') throw error;
|
|
33
39
|
installation.purge();
|
|
34
|
-
|
|
40
|
+
return { uninstalled: true, purged: true };
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
throw Error('Runtime has not exited; local files preserved. Retry aglit uninstall after it quits.');
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
async function main() {
|
|
41
|
-
const { values, positionals } = parseArgs({ options: {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
const { values, positionals } = parseArgs({ options: {
|
|
48
|
+
name: { type: 'string' }, provider: { type: 'string' }, account: { type: 'string' },
|
|
49
|
+
team: { type: 'string' }, invite: { type: 'string' }, headed: { type: 'boolean' },
|
|
50
|
+
headless: { type: 'boolean' }, open: { type: 'boolean' }, json: { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
|
|
51
|
+
}, allowPositionals: true, strict: true });
|
|
52
|
+
const interactive = Boolean(process.stdin.isTTY && process.stderr.isTTY && !values.json && !process.env.CI);
|
|
53
|
+
if (values.help || !positionals.length && !interactive) { process.stdout.write(help); return; }
|
|
54
|
+
const [command = 'init', email] = positionals;
|
|
55
|
+
const options = {
|
|
56
|
+
install: ['json'], init: ['name', 'provider', 'account', 'team', 'invite', 'headed', 'headless', 'json'],
|
|
57
|
+
start: ['headed', 'headless', 'json'], status: ['json'], enable: [], disable: [], teams: ['json'], pipelines: ['open', 'json'], providers: ['json'], invite: ['team'], uninstall: [],
|
|
58
|
+
};
|
|
45
59
|
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.');
|
|
60
|
+
if (values.headed && values.headless) throw Error('Choose either --headed or --headless');
|
|
46
61
|
if (values.team && !/^[A-Za-z0-9._:-]{1,128}$/.test(values.team)) throw Error('Invalid team ID');
|
|
47
62
|
if (values.provider && !['auto', 'codex', 'claude'].includes(values.provider)) throw Error('Provider must be auto, codex or claude');
|
|
63
|
+
const validEmail = value => value.length <= 254 && /^[^\s@*]+@[^\s@*]+\.[^\s@*]+$/.test(value);
|
|
48
64
|
const recipient = command === 'invite' ? email : values.invite;
|
|
49
|
-
if (recipient && (recipient
|
|
65
|
+
if (recipient && !validEmail(recipient)) throw Error('An individual invitation email address is required');
|
|
66
|
+
if (values.account !== undefined && !validEmail(values.account)) throw Error('An individual account email address is required');
|
|
50
67
|
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
|
-
|
|
68
|
+
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');
|
|
69
|
+
const ui = await createUi(interactive);
|
|
70
|
+
const installed = () => fs.existsSync(path.join(installation.root, 'installation.json'));
|
|
71
|
+
if (command === 'install') {
|
|
72
|
+
const receipt = await ui.step('Download and verify Desktop', () => installation.install());
|
|
73
|
+
if (interactive) ui.info('Desktop installed. Run aglit init to register this computer.');
|
|
74
|
+
else ui.result(receipt);
|
|
75
|
+
ui.finish('Ready for setup'); return;
|
|
56
76
|
}
|
|
57
|
-
if (command === '
|
|
77
|
+
if (command === 'status' && !installed()) {
|
|
78
|
+
ui.result({ product: 'Aglit Computer Use', installed: false, running: false, enabled: false });
|
|
79
|
+
ui.finish('Run aglit to set up this computer'); return;
|
|
80
|
+
}
|
|
81
|
+
if (command === 'uninstall' && !installed()) {
|
|
58
82
|
if (fs.existsSync(installation.root)) installation.purgeIncomplete();
|
|
59
|
-
|
|
83
|
+
ui.result({ uninstalled: true, purged: true }); ui.finish('Uninstalled'); return;
|
|
60
84
|
}
|
|
61
|
-
if (command === 'uninstall' || command === 'status') {
|
|
85
|
+
if ((command === 'uninstall' || command === 'status') && installed()) {
|
|
62
86
|
const pending = installation.readUninstall();
|
|
63
87
|
if (pending) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
88
|
+
ui.result(command === 'uninstall' ? await finishUninstall(pending) : { installed: true, uninstalling: true, enabled: false });
|
|
89
|
+
ui.finish('Uninstall cleanup'); return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
let name = values.name;
|
|
93
|
+
let mode = values.headed ? 'headed' : 'headless';
|
|
94
|
+
if (command === 'init') {
|
|
95
|
+
if (!name && interactive && !installed()) name = await ui.name(os.hostname());
|
|
96
|
+
if (interactive && values.headed === undefined && values.headless === undefined) mode = await ui.mode();
|
|
97
|
+
if (!installed()) {
|
|
98
|
+
if (!interactive && !name) throw Error('Provide --name for unattended setup');
|
|
99
|
+
await ui.step('Download and verify Desktop', () => installation.install());
|
|
67
100
|
}
|
|
68
101
|
}
|
|
102
|
+
if (!installed()) throw Error('Run aglit init to install and register this computer first.');
|
|
69
103
|
installation.readInstallation();
|
|
70
104
|
const configPath = path.join(installation.profile, 'ipc/sdk-ipc.json');
|
|
71
105
|
const control = (action, fields = {}) => require('../src/control.cjs').control(configPath, action, fields);
|
|
72
106
|
let state;
|
|
73
107
|
try { state = await control('status'); } catch (error) {
|
|
74
|
-
if (command === 'status') {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
108
|
+
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; }
|
|
109
|
+
state = await ui.step('Start Desktop in the background', async () => {
|
|
110
|
+
installation.start();
|
|
111
|
+
const deadline = Date.now() + 60000;
|
|
112
|
+
while (Date.now() < deadline) {
|
|
113
|
+
await delay(1000);
|
|
114
|
+
try { return await control('status'); } catch { /* Wait for this installation's authenticated native socket. */ }
|
|
115
|
+
}
|
|
116
|
+
throw Error('Computer Use did not become ready. See runtime.log in its installation directory.');
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (command === 'status') { ui.result(state); ui.finish('Computer status'); return; }
|
|
120
|
+
if (command === 'teams' || command === 'pipelines') {
|
|
121
|
+
const result = await ui.step(command === 'teams' ? 'Load your teams' : 'Load authorized pipeline devices', () => control(command));
|
|
122
|
+
if (interactive) ui[command](result); else ui.result(result);
|
|
123
|
+
if (command === 'pipelines' && values.open) await control('open-pipelines');
|
|
124
|
+
ui.finish(command === 'teams' ? 'Your teams' : 'Your pipelines'); return;
|
|
125
|
+
}
|
|
126
|
+
if (command === 'start' || command === 'init') {
|
|
127
|
+
const window = await control('window', { mode });
|
|
128
|
+
state = { ...state, ...window };
|
|
129
|
+
if (command === 'start') { ui.result(state); ui.finish(mode === 'headed' ? 'Desktop window opened' : 'Running in the background'); return; }
|
|
82
130
|
}
|
|
83
|
-
if (command === 'status') { print(state); return; }
|
|
84
131
|
if (command === 'init') {
|
|
85
132
|
if (!state.email) {
|
|
86
|
-
await
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
133
|
+
state = await ui.step('Complete sign-in in your browser', async () => {
|
|
134
|
+
await control('login');
|
|
135
|
+
const deadline = Date.now() + 5 * 60000;
|
|
136
|
+
while (Date.now() < deadline) {
|
|
137
|
+
await delay(1500); const current = await control('status'); if (current.email) return current;
|
|
138
|
+
}
|
|
139
|
+
throw Error('Sign-in timed out. Run aglit init to resume.');
|
|
140
|
+
});
|
|
93
141
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
142
|
+
if (values.account && state.email?.toLowerCase() !== values.account.toLowerCase())
|
|
143
|
+
throw Error(`Signed in as ${state.email}; expected ${values.account}. Switch the browser sign-in account and rerun aglit init.`);
|
|
144
|
+
if (interactive && !values.account && !await ui.confirmAccount(state.email))
|
|
145
|
+
throw Object.assign(Error('Setup cancelled before enabling remote access.'), { cancelled: true });
|
|
146
|
+
ui.info(`Signed in as ${state.email}`);
|
|
147
|
+
let teamId = values.team;
|
|
148
|
+
if (interactive) {
|
|
149
|
+
const discovery = await ui.step('Load your teams and pipelines', () => Promise.allSettled([control('teams'), control('pipelines')]));
|
|
150
|
+
let teams = [];
|
|
151
|
+
if (discovery[0].status === 'fulfilled') { ui.teams(discovery[0].value); teams = teamsFromRoles(discovery[0].value); }
|
|
152
|
+
else ui.info('Teams are unavailable. You can retry with aglit teams.');
|
|
153
|
+
if (discovery[1].status === 'fulfilled') ui.pipelines(discovery[1].value);
|
|
154
|
+
else ui.info('Pipeline devices are unavailable. You can retry with aglit pipelines.');
|
|
155
|
+
if (!teamId && teams.length) teamId = await ui.team(teams);
|
|
98
156
|
}
|
|
157
|
+
name ||= state.name;
|
|
158
|
+
if (!name && interactive) name = await ui.name(os.hostname());
|
|
99
159
|
if (!name) throw Error('Provide --name for unattended setup');
|
|
100
|
-
const detected = await control('providers');
|
|
160
|
+
const detected = await ui.step('Find signed-in AI providers', () => control('providers'));
|
|
101
161
|
const available = detected.providers.filter(provider => provider.authenticated).map(provider => provider.provider);
|
|
102
162
|
let provider = values.provider && values.provider !== 'auto' ? values.provider :
|
|
103
163
|
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);
|
|
164
|
+
if (!provider && available.length && interactive) provider = await ui.provider(available, detected.preferred);
|
|
165
|
+
if (!provider || !available.includes(provider)) throw Error('Sign in to Codex or Claude on this computer, then rerun aglit init --provider codex|claude.');
|
|
166
|
+
await ui.step('Save computer settings', () => control('configure', { name, provider, ...(teamId ? { teamId } : {}) }));
|
|
167
|
+
const permissions = await ui.step('Check screen recording and input permissions', () => control('permissions'));
|
|
168
|
+
if (!interactive) ui.result(permissions);
|
|
112
169
|
if (permissions.permissions?.supported && (permissions.permissions.screenRecording !== 'granted' || permissions.permissions.accessibility !== 'granted'))
|
|
113
170
|
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
|
-
return;
|
|
171
|
+
state = await ui.step('Connect this computer to your account', async () => {
|
|
172
|
+
await control('enable');
|
|
173
|
+
const deadline = Date.now() + 30000;
|
|
174
|
+
do {
|
|
175
|
+
const current = await control('status');
|
|
176
|
+
if (current.connected) return current;
|
|
177
|
+
await delay(1000);
|
|
178
|
+
} while (Date.now() < deadline);
|
|
179
|
+
throw Error('Anywhere Access is enabled but its connection is not ready. Check aglit status before retrying.');
|
|
180
|
+
});
|
|
181
|
+
if (values.invite) ui.result(await control('invite', { teamId: values.team, email: values.invite }));
|
|
182
|
+
ui.result(state); ui.finish('Computer ready · https://console.aglit.ai/'); return;
|
|
126
183
|
}
|
|
127
|
-
if (command === 'invite') {
|
|
184
|
+
if (command === 'invite') { ui.result(await control('invite', { teamId: values.team, email })); ui.finish('Invitation sent'); return; }
|
|
128
185
|
if (command === 'uninstall') {
|
|
129
186
|
const result = await control('uninstall');
|
|
130
187
|
if (!result?.revoked || !result?.quitting || !Number.isSafeInteger(result.pid) || result.pid <= 0) throw Error('Revocation was not confirmed; installation preserved');
|
|
131
|
-
await finishUninstall(result);
|
|
188
|
+
ui.result(await ui.step('Remove this installation', () => finishUninstall(result)));
|
|
189
|
+
ui.finish('Uninstalled'); return;
|
|
132
190
|
}
|
|
133
|
-
|
|
191
|
+
ui.result(await control(command)); ui.finish('Done');
|
|
134
192
|
}
|
|
135
193
|
|
|
136
194
|
main().then(() => process.exit(process.exitCode || 0), error => {
|
|
137
|
-
process.stderr.write(`aglit: ${error.message}\n`);
|
|
195
|
+
if (!error.cancelled) process.stderr.write(`aglit: ${error.message}\n`);
|
|
196
|
+
process.exit(error.cancelled ? 130 : 1);
|
|
138
197
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglit-ai/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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": {
|
package/src/ui.cjs
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const clean = value => String(value ?? '').replace(/[\x00-\x1f\x7f-\x9f]/g, '');
|
|
4
|
+
function teamsFromRoles(value) {
|
|
5
|
+
const teams = new Map();
|
|
6
|
+
for (const company of value?.companies || []) {
|
|
7
|
+
for (const team of [...(company.teams || []), ...(company.organizations || []).flatMap(organization => organization.teams || [])]) {
|
|
8
|
+
if (typeof team.teamId === 'string') teams.set(team.teamId, { id: team.teamId, roles: team.roles || [] });
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
for (const role of value?.roles || []) {
|
|
12
|
+
const id = role.teamId || (role.scopeType === 'team' ? role.scopeId : null);
|
|
13
|
+
if (typeof id === 'string' && !teams.has(id)) teams.set(id, { id, roles: [role.roleLabel || role.role].filter(Boolean) });
|
|
14
|
+
}
|
|
15
|
+
return [...teams.values()];
|
|
16
|
+
}
|
|
17
|
+
async function createUi(interactive) {
|
|
18
|
+
if (!interactive) return {
|
|
19
|
+
interactive: false,
|
|
20
|
+
step: (_label, action) => action(),
|
|
21
|
+
result: value => process.stdout.write(JSON.stringify(value, null, 2) + '\n'),
|
|
22
|
+
info: message => process.stderr.write(message + '\n'),
|
|
23
|
+
finish() {},
|
|
24
|
+
};
|
|
25
|
+
const p = await import('@clack/prompts');
|
|
26
|
+
p.intro('Aglit Computer Use');
|
|
27
|
+
async function answer(promise) {
|
|
28
|
+
const value = await promise;
|
|
29
|
+
if (p.isCancel(value)) {
|
|
30
|
+
p.cancel('Setup cancelled.');
|
|
31
|
+
throw Object.assign(Error('Setup cancelled'), { cancelled: true });
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
interactive: true,
|
|
37
|
+
name: initialValue => answer(p.text({ message: 'Name this computer', placeholder: initialValue, defaultValue: initialValue,
|
|
38
|
+
validate: value => value && (!value.trim() || value.length > 100 || /[\x00-\x1f\x7f-\x9f]/.test(value)) ? 'Use 1–100 printable characters.' : undefined })),
|
|
39
|
+
mode: () => answer(p.select({ message: 'How should Aglit run?', initialValue: 'headless', options: [
|
|
40
|
+
{ value: 'headless', label: 'Headless', hint: 'default · background' },
|
|
41
|
+
{ value: 'headed', label: 'Open Desktop window', hint: 'headed' },
|
|
42
|
+
] })),
|
|
43
|
+
provider: (providers, preferred) => answer(p.select({ message: 'Choose your AI provider', initialValue: preferred,
|
|
44
|
+
options: providers.map(value => ({ value, label: value === 'codex' ? 'Codex' : 'Claude' })) })),
|
|
45
|
+
team: teams => answer(p.select({ message: 'Team for invitations', initialValue: '', options: [
|
|
46
|
+
{ value: '', label: 'Personal computer', hint: 'access stays with your account' },
|
|
47
|
+
...teams.map(team => ({ value: team.id, label: clean(team.id), hint: team.roles.map(clean).join(', ') })),
|
|
48
|
+
] })),
|
|
49
|
+
teams(value) {
|
|
50
|
+
const teams = teamsFromRoles(value);
|
|
51
|
+
p.note(teams.length ? teams.map(team => `${clean(team.id)} ${team.roles.map(clean).join(', ')}`).join('\n') : 'No team memberships.', 'Your teams');
|
|
52
|
+
return teams;
|
|
53
|
+
},
|
|
54
|
+
pipelines(value) {
|
|
55
|
+
p.note((value.devices.length ? value.devices.map(device => `${clean(device.name)}${device.platform ? ' · ' + clean(device.platform) : ''}`).join('\n') + '\n\n' : 'No authorized pipeline devices.\n\n') +
|
|
56
|
+
'https://pipelines.aglit.ai/\nOpen the encrypted viewer with aglit pipelines --open.', 'Pipeline devices');
|
|
57
|
+
},
|
|
58
|
+
confirmAccount: email => answer(p.confirm({ message: `Register with ${clean(email)}?`, initialValue: true })),
|
|
59
|
+
async step(label, action) {
|
|
60
|
+
const spinner = p.spinner(); spinner.start(label);
|
|
61
|
+
try { const value = await action(); spinner.stop(label); return value; }
|
|
62
|
+
catch (error) { spinner.stop('Setup paused'); throw error; }
|
|
63
|
+
},
|
|
64
|
+
result(value) {
|
|
65
|
+
if (value.product === 'Aglit Computer Use') {
|
|
66
|
+
p.note([
|
|
67
|
+
value.name && `Computer ${clean(value.name)}`,
|
|
68
|
+
value.email && `Account ${clean(value.email)}`,
|
|
69
|
+
`Runtime ${value.running ? 'running' : value.installed ? 'stopped' : 'not installed'}`,
|
|
70
|
+
value.mode && `Window ${value.windowVisible ? 'open' : 'hidden'}`,
|
|
71
|
+
`Access ${value.connected ? 'connected' : value.enabled ? 'connecting' : 'disabled'}`,
|
|
72
|
+
value.provider && `Provider ${clean(value.provider)}`,
|
|
73
|
+
].filter(Boolean).join('\n'), 'This computer');
|
|
74
|
+
} else p.log.info(JSON.stringify(value, null, 2));
|
|
75
|
+
},
|
|
76
|
+
info: message => p.log.info(clean(message)),
|
|
77
|
+
finish: message => p.outro(message),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { createUi, teamsFromRoles };
|