@dmsdc-ai/aterm 0.1.13 → 0.1.15
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/bin/aterm.js +3 -119
- package/package.json +3 -5
- package/scripts/tui-installer.js +0 -311
package/bin/aterm.js
CHANGED
|
@@ -4,12 +4,8 @@ import path from 'node:path';
|
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import {
|
|
7
|
-
detectAiCliStatus,
|
|
8
7
|
ensureUserLayout,
|
|
9
|
-
getUserAtermConfigPath,
|
|
10
8
|
resolveAigentryConfig,
|
|
11
|
-
resolveInstallHomeDir,
|
|
12
|
-
updateConfigFile,
|
|
13
9
|
} from '../lib/aigentry.js';
|
|
14
10
|
|
|
15
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -22,125 +18,12 @@ const packageJson = JSON.parse(
|
|
|
22
18
|
fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'),
|
|
23
19
|
);
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
26
22
|
console.log(packageJson.version);
|
|
27
23
|
process.exit(0);
|
|
28
24
|
}
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
const configPath = getUserAtermConfigPath();
|
|
32
|
-
if (!fs.existsSync(configPath)) {
|
|
33
|
-
return {
|
|
34
|
-
configPath,
|
|
35
|
-
setupCompleted: false,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
41
|
-
return {
|
|
42
|
-
configPath,
|
|
43
|
-
setupCompleted: config.setupCompleted === true,
|
|
44
|
-
};
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.warn(`[aterm] invalid user config, rerunning setup: ${error.message}`);
|
|
47
|
-
return {
|
|
48
|
-
configPath,
|
|
49
|
-
setupCompleted: false,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function maybeRunFirstRunWizard() {
|
|
55
|
-
ensureUserLayout({ version: packageJson.version });
|
|
56
|
-
const setupState = readUserSetupState();
|
|
57
|
-
if (setupState.setupCompleted) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let wizardModule;
|
|
62
|
-
try {
|
|
63
|
-
wizardModule = await import('../scripts/tui-installer.js');
|
|
64
|
-
} catch (error) {
|
|
65
|
-
console.warn(`[aterm] setup wizard unavailable, launching with defaults (${error.message})`);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!wizardModule.shouldRunFirstRunWizard()) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const context = wizardModule.buildFirstRunWizardContext(packageJson.version, process.cwd());
|
|
74
|
-
const wizardResult = await wizardModule.runFirstRunWizard(context);
|
|
75
|
-
if (!wizardResult) {
|
|
76
|
-
process.exit(0);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
await wizardModule.completeFirstRunWizard(context, wizardResult);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function maybeRunConfigMigration() {
|
|
83
|
-
const configPath = getUserAtermConfigPath();
|
|
84
|
-
if (!fs.existsSync(configPath)) return;
|
|
85
|
-
|
|
86
|
-
let config;
|
|
87
|
-
try {
|
|
88
|
-
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
89
|
-
} catch { return; }
|
|
90
|
-
|
|
91
|
-
if (!config.setupCompleted) return;
|
|
92
|
-
|
|
93
|
-
const isTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
94
|
-
const cliStatus = detectAiCliStatus(resolveInstallHomeDir());
|
|
95
|
-
const patch = {};
|
|
96
|
-
const questions = [];
|
|
97
|
-
|
|
98
|
-
// Check each field individually — users from any version get prompted only for THEIR missing fields
|
|
99
|
-
if (!config.ai?.defaultCLI) {
|
|
100
|
-
questions.push({
|
|
101
|
-
type: 'select',
|
|
102
|
-
name: 'defaultCLI',
|
|
103
|
-
message: 'Default AI CLI for orchestrator workspace',
|
|
104
|
-
choices: [
|
|
105
|
-
{ title: 'claude', value: 'claude', disabled: !cliStatus.claude },
|
|
106
|
-
{ title: 'codex', value: 'codex', disabled: !cliStatus.codex },
|
|
107
|
-
{ title: 'gemini', value: 'gemini', disabled: !cliStatus.gemini },
|
|
108
|
-
{ title: 'none (plain zsh)', value: 'none' },
|
|
109
|
-
],
|
|
110
|
-
initial: cliStatus.claude ? 0 : cliStatus.codex ? 1 : cliStatus.gemini ? 2 : 3,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (questions.length === 0) return;
|
|
115
|
-
|
|
116
|
-
if (isTTY) {
|
|
117
|
-
try {
|
|
118
|
-
const prompts = (await import('prompts')).default;
|
|
119
|
-
const responses = await prompts(questions);
|
|
120
|
-
if (responses.defaultCLI) {
|
|
121
|
-
patch.ai = { ...config.ai, defaultCLI: responses.defaultCLI };
|
|
122
|
-
}
|
|
123
|
-
} catch { /* fall through to auto-detect */ }
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Auto-fill any fields not answered via prompt
|
|
127
|
-
if (!patch.ai?.defaultCLI && !config.ai?.defaultCLI) {
|
|
128
|
-
const auto = cliStatus.claude ? 'claude' : cliStatus.codex ? 'codex' : cliStatus.gemini ? 'gemini' : 'none';
|
|
129
|
-
patch.ai = { ...config.ai, ...patch.ai, defaultCLI: auto };
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (Object.keys(patch).length > 0) {
|
|
133
|
-
updateConfigFile(configPath, patch);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
138
|
-
printVersionAndExit();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
await maybeRunFirstRunWizard();
|
|
142
|
-
await maybeRunConfigMigration();
|
|
143
|
-
const resolvedConfig = resolveAigentryConfig({ cwd: process.cwd() });
|
|
26
|
+
ensureUserLayout({ version: packageJson.version });
|
|
144
27
|
|
|
145
28
|
if (!fs.existsSync(executable)) {
|
|
146
29
|
console.error('[aterm] native app bundle is not installed');
|
|
@@ -148,6 +31,7 @@ if (!fs.existsSync(executable)) {
|
|
|
148
31
|
process.exit(1);
|
|
149
32
|
}
|
|
150
33
|
|
|
34
|
+
const resolvedConfig = resolveAigentryConfig({ cwd: process.cwd() });
|
|
151
35
|
for (const warning of resolvedConfig.warnings) {
|
|
152
36
|
console.warn(warning);
|
|
153
37
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dmsdc-ai/aterm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Native aterm launcher package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./bin/aterm.js",
|
|
@@ -17,15 +17,13 @@
|
|
|
17
17
|
"package.json"
|
|
18
18
|
],
|
|
19
19
|
"optionalDependencies": {
|
|
20
|
-
"@dmsdc-ai/aterm-darwin-arm64": "0.1.
|
|
20
|
+
"@dmsdc-ai/aterm-darwin-arm64": "0.1.15"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@dmsdc-ai/aigentry-brain": "latest",
|
|
24
24
|
"@dmsdc-ai/aigentry-deliberation": "latest",
|
|
25
25
|
"@dmsdc-ai/aigentry-devkit": "latest",
|
|
26
|
-
"@dmsdc-ai/aigentry-telepty": "latest"
|
|
27
|
-
"blessed": "^0.1.81",
|
|
28
|
-
"prompts": "^2.4.2"
|
|
26
|
+
"@dmsdc-ai/aigentry-telepty": "latest"
|
|
29
27
|
},
|
|
30
28
|
"license": "UNLICENSED",
|
|
31
29
|
"publishConfig": {
|
package/scripts/tui-installer.js
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
|
-
import {
|
|
4
|
-
detectAiCliStatus,
|
|
5
|
-
resolveInstallHomeDir,
|
|
6
|
-
resolveProjectRoot,
|
|
7
|
-
updateConfigFile,
|
|
8
|
-
} from '../lib/aigentry.js';
|
|
9
|
-
|
|
10
|
-
const require = createRequire(import.meta.url);
|
|
11
|
-
|
|
12
|
-
function waitForKeypress(screen, keys = ['enter', 'space', 'escape']) {
|
|
13
|
-
return new Promise((resolve) => {
|
|
14
|
-
const handler = (_, key) => {
|
|
15
|
-
if (!key || !keys.includes(key.name)) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
screen.off('keypress', handler);
|
|
19
|
-
resolve();
|
|
20
|
-
};
|
|
21
|
-
screen.on('keypress', handler);
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async function showInfoScreen(blessed, title, lines, footer) {
|
|
26
|
-
const screen = blessed.screen({
|
|
27
|
-
smartCSR: true,
|
|
28
|
-
title: 'aterm installer',
|
|
29
|
-
fullUnicode: true,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
blessed.box({
|
|
33
|
-
parent: screen,
|
|
34
|
-
top: 'center',
|
|
35
|
-
left: 'center',
|
|
36
|
-
width: '78%',
|
|
37
|
-
height: Math.max(12, lines.length + 6),
|
|
38
|
-
border: { type: 'line' },
|
|
39
|
-
tags: true,
|
|
40
|
-
label: ` ${title} `,
|
|
41
|
-
style: {
|
|
42
|
-
border: { fg: 'cyan' },
|
|
43
|
-
fg: 'white',
|
|
44
|
-
},
|
|
45
|
-
content: `${lines.join('\n')}\n\n${footer}`,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
screen.render();
|
|
49
|
-
await waitForKeypress(screen);
|
|
50
|
-
screen.destroy();
|
|
51
|
-
// Reset terminal modes that blessed may leave set (DECCKM, keypad)
|
|
52
|
-
// so that subsequent prompts receive normal arrow key sequences.
|
|
53
|
-
process.stdout.write('\x1b[?1l\x1b>');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function buildWorkspaceChoices(context) {
|
|
57
|
-
const choices = [];
|
|
58
|
-
if (context.orchestratorAvailable) {
|
|
59
|
-
choices.push({
|
|
60
|
-
title: 'aigentry-orchestrator',
|
|
61
|
-
value: 'orchestrator',
|
|
62
|
-
description: context.orchestratorPath,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (context.projectRoot) {
|
|
67
|
-
choices.push({
|
|
68
|
-
title: 'current project',
|
|
69
|
-
value: 'project',
|
|
70
|
-
description: context.projectRoot,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
choices.push({
|
|
75
|
-
title: 'home directory',
|
|
76
|
-
value: 'home',
|
|
77
|
-
description: resolveInstallHomeDir(),
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
return choices;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function buildConfigPatch(context, answers) {
|
|
84
|
-
const workspacePathMap = {
|
|
85
|
-
orchestrator: context.orchestratorPath,
|
|
86
|
-
project: context.projectRoot,
|
|
87
|
-
home: resolveInstallHomeDir(),
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
shell: {
|
|
92
|
-
default: answers.defaultShell,
|
|
93
|
-
},
|
|
94
|
-
workspace: {
|
|
95
|
-
default: answers.defaultWorkspace,
|
|
96
|
-
path: workspacePathMap[answers.defaultWorkspace] ?? resolveInstallHomeDir(),
|
|
97
|
-
},
|
|
98
|
-
orchestrator: {
|
|
99
|
-
enabled: answers.defaultWorkspace === 'orchestrator',
|
|
100
|
-
path: context.orchestratorPath,
|
|
101
|
-
},
|
|
102
|
-
tailscale: {
|
|
103
|
-
connect_on_launch: answers.tailscaleConnect,
|
|
104
|
-
},
|
|
105
|
-
ai: {
|
|
106
|
-
defaultCLI: answers.defaultCLI,
|
|
107
|
-
detected_clis: context.cliStatus,
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export async function runFirstRunWizard(context) {
|
|
113
|
-
const blessed = require('blessed');
|
|
114
|
-
const prompts = (await import('prompts')).default;
|
|
115
|
-
|
|
116
|
-
await showInfoScreen(
|
|
117
|
-
blessed,
|
|
118
|
-
' aterm Installer ',
|
|
119
|
-
[
|
|
120
|
-
'{center}{bold}Aigentry Terminal Installer{/bold}{/center}',
|
|
121
|
-
'',
|
|
122
|
-
'This wizard finishes your aterm setup before the native app launches.',
|
|
123
|
-
'It will save your preferred shell, workspace, and Tailscale defaults.',
|
|
124
|
-
'',
|
|
125
|
-
`Package version: ${context.version}`,
|
|
126
|
-
],
|
|
127
|
-
'{gray-fg}Press Enter to continue{/}',
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const cliLines = [
|
|
131
|
-
'{bold}AI CLI detection{/bold}',
|
|
132
|
-
'',
|
|
133
|
-
`claude ${context.cliStatus.claude ? '✅' : '❌'}`,
|
|
134
|
-
`codex ${context.cliStatus.codex ? '✅' : '❌'}`,
|
|
135
|
-
`gemini ${context.cliStatus.gemini ? '✅' : '❌'}`,
|
|
136
|
-
];
|
|
137
|
-
|
|
138
|
-
await showInfoScreen(
|
|
139
|
-
blessed,
|
|
140
|
-
' CLI Status ',
|
|
141
|
-
cliLines,
|
|
142
|
-
'{gray-fg}Press Enter to continue{/}',
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
const workspaceChoices = buildWorkspaceChoices(context);
|
|
146
|
-
const defaultResponses = {
|
|
147
|
-
defaultShell: 'zsh',
|
|
148
|
-
defaultWorkspace: workspaceChoices[0]?.value ?? 'home',
|
|
149
|
-
defaultCLI: context.cliStatus.claude ? 'claude' : context.cliStatus.codex ? 'codex' : context.cliStatus.gemini ? 'gemini' : 'none',
|
|
150
|
-
tailscaleConnect: false,
|
|
151
|
-
};
|
|
152
|
-
let cancelled = false;
|
|
153
|
-
|
|
154
|
-
const responses = await prompts(
|
|
155
|
-
[
|
|
156
|
-
{
|
|
157
|
-
type: 'select',
|
|
158
|
-
name: 'defaultShell',
|
|
159
|
-
message: 'Default shell',
|
|
160
|
-
choices: [
|
|
161
|
-
{ title: 'zsh', value: 'zsh' },
|
|
162
|
-
{ title: 'bash', value: 'bash' },
|
|
163
|
-
{ title: 'fish', value: 'fish' },
|
|
164
|
-
],
|
|
165
|
-
initial: 0,
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
type: 'select',
|
|
169
|
-
name: 'defaultWorkspace',
|
|
170
|
-
message: 'Default workspace / orchestrator target',
|
|
171
|
-
choices: workspaceChoices,
|
|
172
|
-
initial: 0,
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
type: 'select',
|
|
176
|
-
name: 'defaultCLI',
|
|
177
|
-
message: 'Default AI CLI for orchestrator workspace',
|
|
178
|
-
choices: [
|
|
179
|
-
{ title: 'claude', value: 'claude', disabled: !context.cliStatus.claude },
|
|
180
|
-
{ title: 'codex', value: 'codex', disabled: !context.cliStatus.codex },
|
|
181
|
-
{ title: 'gemini', value: 'gemini', disabled: !context.cliStatus.gemini },
|
|
182
|
-
{ title: 'none (plain zsh)', value: 'none' },
|
|
183
|
-
],
|
|
184
|
-
initial: context.cliStatus.claude ? 0 : context.cliStatus.codex ? 1 : context.cliStatus.gemini ? 2 : 3,
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
type: 'toggle',
|
|
188
|
-
name: 'tailscaleConnect',
|
|
189
|
-
message: 'Enable Tailscale connect on launch?',
|
|
190
|
-
initial: false,
|
|
191
|
-
active: 'yes',
|
|
192
|
-
inactive: 'no',
|
|
193
|
-
},
|
|
194
|
-
],
|
|
195
|
-
{
|
|
196
|
-
onCancel: () => {
|
|
197
|
-
cancelled = true;
|
|
198
|
-
return false;
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
if (cancelled) {
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const mergedAnswers = {
|
|
208
|
-
...defaultResponses,
|
|
209
|
-
...responses,
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
configPatch: buildConfigPatch(context, mergedAnswers),
|
|
214
|
-
summary: mergedAnswers,
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export function shouldRunFirstRunWizard() {
|
|
219
|
-
if (process.env.CI) {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
if (process.env.npm_config_yes === 'true') {
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
export function buildFirstRunWizardContext(version, cwd = process.cwd()) {
|
|
229
|
-
const installHome = resolveInstallHomeDir();
|
|
230
|
-
const projectRoot = resolveProjectRoot(cwd);
|
|
231
|
-
const orchestratorPath = path.join(installHome, 'projects', 'aigentry-orchestrator');
|
|
232
|
-
const cliStatus = detectAiCliStatus(installHome);
|
|
233
|
-
|
|
234
|
-
return {
|
|
235
|
-
version,
|
|
236
|
-
projectRoot,
|
|
237
|
-
cliStatus,
|
|
238
|
-
orchestratorPath,
|
|
239
|
-
orchestratorAvailable: require('node:fs').existsSync(orchestratorPath),
|
|
240
|
-
userConfigPath: path.join(installHome, '.aigentry', 'config', 'aterm.json'),
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export async function showProgressScreen(runSteps) {
|
|
245
|
-
const blessed = require('blessed');
|
|
246
|
-
const screen = blessed.screen({
|
|
247
|
-
smartCSR: true,
|
|
248
|
-
title: 'aterm installer',
|
|
249
|
-
fullUnicode: true,
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
const box = blessed.log({
|
|
253
|
-
parent: screen,
|
|
254
|
-
top: 'center',
|
|
255
|
-
left: 'center',
|
|
256
|
-
width: '84%',
|
|
257
|
-
height: '70%',
|
|
258
|
-
border: { type: 'line' },
|
|
259
|
-
tags: true,
|
|
260
|
-
label: ' Installation Progress ',
|
|
261
|
-
style: {
|
|
262
|
-
border: { fg: 'green' },
|
|
263
|
-
fg: 'white',
|
|
264
|
-
},
|
|
265
|
-
scrollable: true,
|
|
266
|
-
alwaysScroll: true,
|
|
267
|
-
scrollbar: { ch: ' ', inverse: true },
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
const report = (line) => {
|
|
271
|
-
box.add(line);
|
|
272
|
-
screen.render();
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
screen.render();
|
|
276
|
-
await runSteps(report);
|
|
277
|
-
await new Promise(resolve => setTimeout(resolve, 250));
|
|
278
|
-
screen.destroy();
|
|
279
|
-
process.stdout.write('\x1b[?1l\x1b>');
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
export async function showDoneScreen(summaryLines) {
|
|
283
|
-
const blessed = require('blessed');
|
|
284
|
-
await showInfoScreen(
|
|
285
|
-
blessed,
|
|
286
|
-
' Install Complete ',
|
|
287
|
-
summaryLines,
|
|
288
|
-
'{green-fg}Run: aterm{/}',
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export async function completeFirstRunWizard(context, wizardResult) {
|
|
293
|
-
const blessed = require('blessed');
|
|
294
|
-
await showProgressScreen(async (progress) => {
|
|
295
|
-
progress('{bold}Saving setup{/bold}');
|
|
296
|
-
updateConfigFile(context.userConfigPath, {
|
|
297
|
-
...wizardResult.configPatch,
|
|
298
|
-
setupCompleted: true,
|
|
299
|
-
});
|
|
300
|
-
progress(`{green-fg}ok{/} ${context.userConfigPath}`);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
await showDoneScreen([
|
|
304
|
-
'aterm setup is complete.',
|
|
305
|
-
'',
|
|
306
|
-
`Shell: ${wizardResult.summary.defaultShell}`,
|
|
307
|
-
`AI CLI: ${wizardResult.summary.defaultCLI}`,
|
|
308
|
-
`Workspace: ${wizardResult.summary.defaultWorkspace}`,
|
|
309
|
-
`Tailscale connect: ${wizardResult.summary.tailscaleConnect ? 'yes' : 'no'}`,
|
|
310
|
-
]);
|
|
311
|
-
}
|