@spencer-kit/coder-studio 0.1.3
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 -0
- package/bin/coder-studio.mjs +10 -0
- package/lib/cli.mjs +1190 -0
- package/lib/completion.mjs +562 -0
- package/lib/config.mjs +59 -0
- package/lib/http.mjs +89 -0
- package/lib/platform.mjs +50 -0
- package/lib/process-utils.mjs +76 -0
- package/lib/runtime-controller.mjs +350 -0
- package/lib/state.mjs +75 -0
- package/lib/user-config.mjs +521 -0
- package/package.json +26 -0
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { listConfigKeys } from './user-config.mjs';
|
|
6
|
+
export const SUPPORTED_COMPLETION_SHELLS = ['bash', 'zsh', 'fish'];
|
|
7
|
+
const TOP_LEVEL_COMMANDS = [
|
|
8
|
+
'help',
|
|
9
|
+
'start',
|
|
10
|
+
'stop',
|
|
11
|
+
'restart',
|
|
12
|
+
'status',
|
|
13
|
+
'logs',
|
|
14
|
+
'open',
|
|
15
|
+
'doctor',
|
|
16
|
+
'config',
|
|
17
|
+
'auth',
|
|
18
|
+
'completion',
|
|
19
|
+
];
|
|
20
|
+
const HELP_TOPICS = TOP_LEVEL_COMMANDS.filter((command) => command !== 'help');
|
|
21
|
+
const CONFIG_SUBCOMMANDS = ['path', 'show', 'get', 'set', 'unset', 'validate', 'root', 'password', 'auth'];
|
|
22
|
+
const CONFIG_ROOT_SUBCOMMANDS = ['show', 'set', 'clear'];
|
|
23
|
+
const CONFIG_PASSWORD_SUBCOMMANDS = ['status', 'set', 'clear'];
|
|
24
|
+
const CONFIG_AUTH_SUBCOMMANDS = ['public-mode', 'session-idle', 'session-max'];
|
|
25
|
+
const AUTH_SUBCOMMANDS = ['status', 'ip'];
|
|
26
|
+
const AUTH_IP_SUBCOMMANDS = ['list', 'unblock'];
|
|
27
|
+
const CONFIG_KEYS = listConfigKeys();
|
|
28
|
+
const TOP_LEVEL_FLAGS = ['--help', '-h', '--version', '-v'];
|
|
29
|
+
const START_FLAGS = ['--host', '--port', '--foreground', '--json', '--help', '-h'];
|
|
30
|
+
const STOP_FLAGS = ['--json', '--help', '-h'];
|
|
31
|
+
const RESTART_FLAGS = ['--json', '--help', '-h'];
|
|
32
|
+
const STATUS_FLAGS = ['--host', '--port', '--json', '--help', '-h'];
|
|
33
|
+
const LOGS_FLAGS = ['--follow', '-f', '--lines', '-n', '--help', '-h'];
|
|
34
|
+
const OPEN_FLAGS = ['--host', '--port', '--json', '--help', '-h'];
|
|
35
|
+
const DOCTOR_FLAGS = ['--host', '--port', '--json', '--help', '-h'];
|
|
36
|
+
const CONFIG_FLAGS = ['--json', '--help', '-h'];
|
|
37
|
+
const AUTH_FLAGS = ['--json', '--help', '-h'];
|
|
38
|
+
const COMPLETION_FLAGS = ['--help', '-h'];
|
|
39
|
+
const COMPLETION_COMMANDS = ['install', 'uninstall', ...SUPPORTED_COMPLETION_SHELLS];
|
|
40
|
+
const COMPLETION_INSTALL_FLAGS = ['--json', '--force', '--help', '-h'];
|
|
41
|
+
const COMPLETION_UNINSTALL_FLAGS = ['--json', '--help', '-h'];
|
|
42
|
+
const CONFIG_PASSWORD_SET_FLAGS = ['--stdin', '--help', '-h'];
|
|
43
|
+
const AUTH_IP_UNBLOCK_FLAGS = ['--all', '--json', '--help', '-h'];
|
|
44
|
+
const MANAGED_BLOCK_START = '# >>> coder-studio completion >>>';
|
|
45
|
+
const MANAGED_BLOCK_END = '# <<< coder-studio completion <<<';
|
|
46
|
+
function words(items) {
|
|
47
|
+
return items.join(' ');
|
|
48
|
+
}
|
|
49
|
+
function lines(items) {
|
|
50
|
+
return `${items.join('\n')}\n`;
|
|
51
|
+
}
|
|
52
|
+
function generateBashScript() {
|
|
53
|
+
return lines([
|
|
54
|
+
'# bash completion for coder-studio',
|
|
55
|
+
'__coder_studio_complete() {',
|
|
56
|
+
' local cur prev command subcommand nested',
|
|
57
|
+
' COMPREPLY=()',
|
|
58
|
+
' cur="${COMP_WORDS[COMP_CWORD]}"',
|
|
59
|
+
' prev="${COMP_WORDS[COMP_CWORD-1]}"',
|
|
60
|
+
' command="${COMP_WORDS[1]}"',
|
|
61
|
+
' subcommand="${COMP_WORDS[2]}"',
|
|
62
|
+
' nested="${COMP_WORDS[3]}"',
|
|
63
|
+
'',
|
|
64
|
+
' case "$prev" in',
|
|
65
|
+
' --host|--port|--lines|-n)',
|
|
66
|
+
' return 0',
|
|
67
|
+
' ;;',
|
|
68
|
+
' esac',
|
|
69
|
+
'',
|
|
70
|
+
' if [[ $COMP_CWORD -eq 1 ]]; then',
|
|
71
|
+
' if [[ "$cur" == -* ]]; then',
|
|
72
|
+
` COMPREPLY=( $(compgen -W "${words(TOP_LEVEL_FLAGS)}" -- "$cur") )`,
|
|
73
|
+
' else',
|
|
74
|
+
` COMPREPLY=( $(compgen -W "${words(TOP_LEVEL_COMMANDS)}" -- "$cur") )`,
|
|
75
|
+
' fi',
|
|
76
|
+
' return 0',
|
|
77
|
+
' fi',
|
|
78
|
+
'',
|
|
79
|
+
' case "$command" in',
|
|
80
|
+
' help)',
|
|
81
|
+
` COMPREPLY=( $(compgen -W "${words(HELP_TOPICS.concat(COMPLETION_FLAGS))}" -- "$cur") )`,
|
|
82
|
+
' return 0',
|
|
83
|
+
' ;;',
|
|
84
|
+
' start)',
|
|
85
|
+
` COMPREPLY=( $(compgen -W "${words(START_FLAGS)}" -- "$cur") )`,
|
|
86
|
+
' return 0',
|
|
87
|
+
' ;;',
|
|
88
|
+
' stop)',
|
|
89
|
+
` COMPREPLY=( $(compgen -W "${words(STOP_FLAGS)}" -- "$cur") )`,
|
|
90
|
+
' return 0',
|
|
91
|
+
' ;;',
|
|
92
|
+
' restart)',
|
|
93
|
+
` COMPREPLY=( $(compgen -W "${words(RESTART_FLAGS)}" -- "$cur") )`,
|
|
94
|
+
' return 0',
|
|
95
|
+
' ;;',
|
|
96
|
+
' status)',
|
|
97
|
+
` COMPREPLY=( $(compgen -W "${words(STATUS_FLAGS)}" -- "$cur") )`,
|
|
98
|
+
' return 0',
|
|
99
|
+
' ;;',
|
|
100
|
+
' logs)',
|
|
101
|
+
` COMPREPLY=( $(compgen -W "${words(LOGS_FLAGS)}" -- "$cur") )`,
|
|
102
|
+
' return 0',
|
|
103
|
+
' ;;',
|
|
104
|
+
' open)',
|
|
105
|
+
` COMPREPLY=( $(compgen -W "${words(OPEN_FLAGS)}" -- "$cur") )`,
|
|
106
|
+
' return 0',
|
|
107
|
+
' ;;',
|
|
108
|
+
' doctor)',
|
|
109
|
+
` COMPREPLY=( $(compgen -W "${words(DOCTOR_FLAGS)}" -- "$cur") )`,
|
|
110
|
+
' return 0',
|
|
111
|
+
' ;;',
|
|
112
|
+
' completion)',
|
|
113
|
+
' if [[ $COMP_CWORD -eq 2 ]]; then',
|
|
114
|
+
` COMPREPLY=( $(compgen -W "${words(COMPLETION_COMMANDS.concat(COMPLETION_FLAGS))}" -- "$cur") )`,
|
|
115
|
+
' return 0',
|
|
116
|
+
' fi',
|
|
117
|
+
' if [[ "$subcommand" == "install" ]]; then',
|
|
118
|
+
' if [[ $COMP_CWORD -eq 3 ]]; then',
|
|
119
|
+
` COMPREPLY=( $(compgen -W "${words(SUPPORTED_COMPLETION_SHELLS)}" -- "$cur") )`,
|
|
120
|
+
' else',
|
|
121
|
+
` COMPREPLY=( $(compgen -W "${words(COMPLETION_INSTALL_FLAGS)}" -- "$cur") )`,
|
|
122
|
+
' fi',
|
|
123
|
+
' return 0',
|
|
124
|
+
' fi',
|
|
125
|
+
' if [[ "$subcommand" == "uninstall" ]]; then',
|
|
126
|
+
' if [[ $COMP_CWORD -eq 3 ]]; then',
|
|
127
|
+
` COMPREPLY=( $(compgen -W "${words(SUPPORTED_COMPLETION_SHELLS)}" -- "$cur") )`,
|
|
128
|
+
' else',
|
|
129
|
+
` COMPREPLY=( $(compgen -W "${words(COMPLETION_UNINSTALL_FLAGS)}" -- "$cur") )`,
|
|
130
|
+
' fi',
|
|
131
|
+
' return 0',
|
|
132
|
+
' fi',
|
|
133
|
+
` COMPREPLY=( $(compgen -W "${words(SUPPORTED_COMPLETION_SHELLS.concat(COMPLETION_FLAGS))}" -- "$cur") )`,
|
|
134
|
+
' return 0',
|
|
135
|
+
' ;;',
|
|
136
|
+
' config)',
|
|
137
|
+
' if [[ $COMP_CWORD -eq 2 ]]; then',
|
|
138
|
+
` COMPREPLY=( $(compgen -W "${words(CONFIG_SUBCOMMANDS.concat(CONFIG_FLAGS))}" -- "$cur") )`,
|
|
139
|
+
' return 0',
|
|
140
|
+
' fi',
|
|
141
|
+
' case "$subcommand" in',
|
|
142
|
+
' get|set|unset)',
|
|
143
|
+
' if [[ $COMP_CWORD -eq 3 ]]; then',
|
|
144
|
+
` COMPREPLY=( $(compgen -W "${words(CONFIG_KEYS)}" -- "$cur") )`,
|
|
145
|
+
' return 0',
|
|
146
|
+
' fi',
|
|
147
|
+
' ;;',
|
|
148
|
+
' show|validate|path)',
|
|
149
|
+
` COMPREPLY=( $(compgen -W "${words(CONFIG_FLAGS)}" -- "$cur") )`,
|
|
150
|
+
' return 0',
|
|
151
|
+
' ;;',
|
|
152
|
+
' root)',
|
|
153
|
+
' if [[ $COMP_CWORD -eq 3 ]]; then',
|
|
154
|
+
` COMPREPLY=( $(compgen -W "${words(CONFIG_ROOT_SUBCOMMANDS.concat(COMPLETION_FLAGS))}" -- "$cur") )`,
|
|
155
|
+
' return 0',
|
|
156
|
+
' fi',
|
|
157
|
+
' ;;',
|
|
158
|
+
' password)',
|
|
159
|
+
' if [[ $COMP_CWORD -eq 3 ]]; then',
|
|
160
|
+
` COMPREPLY=( $(compgen -W "${words(CONFIG_PASSWORD_SUBCOMMANDS.concat(COMPLETION_FLAGS))}" -- "$cur") )`,
|
|
161
|
+
' return 0',
|
|
162
|
+
' fi',
|
|
163
|
+
' if [[ "$nested" == "set" ]]; then',
|
|
164
|
+
` COMPREPLY=( $(compgen -W "${words(CONFIG_PASSWORD_SET_FLAGS)}" -- "$cur") )`,
|
|
165
|
+
' return 0',
|
|
166
|
+
' fi',
|
|
167
|
+
' ;;',
|
|
168
|
+
' auth)',
|
|
169
|
+
' if [[ $COMP_CWORD -eq 3 ]]; then',
|
|
170
|
+
` COMPREPLY=( $(compgen -W "${words(CONFIG_AUTH_SUBCOMMANDS.concat(COMPLETION_FLAGS))}" -- "$cur") )`,
|
|
171
|
+
' return 0',
|
|
172
|
+
' fi',
|
|
173
|
+
' if [[ "$nested" == "public-mode" && $COMP_CWORD -eq 4 ]]; then',
|
|
174
|
+
' COMPREPLY=( $(compgen -W "on off" -- "$cur") )',
|
|
175
|
+
' return 0',
|
|
176
|
+
' fi',
|
|
177
|
+
' ;;',
|
|
178
|
+
' esac',
|
|
179
|
+
' ;;',
|
|
180
|
+
' auth)',
|
|
181
|
+
' if [[ $COMP_CWORD -eq 2 ]]; then',
|
|
182
|
+
` COMPREPLY=( $(compgen -W "${words(AUTH_SUBCOMMANDS.concat(AUTH_FLAGS))}" -- "$cur") )`,
|
|
183
|
+
' return 0',
|
|
184
|
+
' fi',
|
|
185
|
+
' case "$subcommand" in',
|
|
186
|
+
' status)',
|
|
187
|
+
` COMPREPLY=( $(compgen -W "${words(AUTH_FLAGS)}" -- "$cur") )`,
|
|
188
|
+
' return 0',
|
|
189
|
+
' ;;',
|
|
190
|
+
' ip)',
|
|
191
|
+
' if [[ $COMP_CWORD -eq 3 ]]; then',
|
|
192
|
+
` COMPREPLY=( $(compgen -W "${words(AUTH_IP_SUBCOMMANDS.concat(COMPLETION_FLAGS))}" -- "$cur") )`,
|
|
193
|
+
' return 0',
|
|
194
|
+
' fi',
|
|
195
|
+
' if [[ "$nested" == "list" ]]; then',
|
|
196
|
+
` COMPREPLY=( $(compgen -W "${words(AUTH_FLAGS)}" -- "$cur") )`,
|
|
197
|
+
' return 0',
|
|
198
|
+
' fi',
|
|
199
|
+
' if [[ "$nested" == "unblock" ]]; then',
|
|
200
|
+
` COMPREPLY=( $(compgen -W "${words(AUTH_IP_UNBLOCK_FLAGS)}" -- "$cur") )`,
|
|
201
|
+
' return 0',
|
|
202
|
+
' fi',
|
|
203
|
+
' ;;',
|
|
204
|
+
' esac',
|
|
205
|
+
' ;;',
|
|
206
|
+
' esac',
|
|
207
|
+
'',
|
|
208
|
+
` COMPREPLY=( $(compgen -W "${words(TOP_LEVEL_FLAGS)}" -- "$cur") )`,
|
|
209
|
+
'}',
|
|
210
|
+
'complete -F __coder_studio_complete coder-studio',
|
|
211
|
+
]);
|
|
212
|
+
}
|
|
213
|
+
function generateZshScript() {
|
|
214
|
+
return lines([
|
|
215
|
+
'#compdef coder-studio',
|
|
216
|
+
'',
|
|
217
|
+
'_coder_studio_complete() {',
|
|
218
|
+
' local -a suggestions',
|
|
219
|
+
' local command subcommand nested prev',
|
|
220
|
+
' command="${words[2]}"',
|
|
221
|
+
' subcommand="${words[3]}"',
|
|
222
|
+
' nested="${words[4]}"',
|
|
223
|
+
' prev="${words[CURRENT-1]}"',
|
|
224
|
+
'',
|
|
225
|
+
' case "$prev" in',
|
|
226
|
+
' --host|--port|--lines|-n)',
|
|
227
|
+
' return 0',
|
|
228
|
+
' ;;',
|
|
229
|
+
' esac',
|
|
230
|
+
'',
|
|
231
|
+
' if (( CURRENT == 2 )); then',
|
|
232
|
+
` suggestions=(${words(TOP_LEVEL_COMMANDS)} ${words(TOP_LEVEL_FLAGS)})`,
|
|
233
|
+
' compadd -- "${suggestions[@]}"',
|
|
234
|
+
' return 0',
|
|
235
|
+
' fi',
|
|
236
|
+
'',
|
|
237
|
+
' case "$command" in',
|
|
238
|
+
' help)',
|
|
239
|
+
` suggestions=(${words(HELP_TOPICS)} ${words(COMPLETION_FLAGS)})`,
|
|
240
|
+
' ;;',
|
|
241
|
+
' start)',
|
|
242
|
+
` suggestions=(${words(START_FLAGS)})`,
|
|
243
|
+
' ;;',
|
|
244
|
+
' stop)',
|
|
245
|
+
` suggestions=(${words(STOP_FLAGS)})`,
|
|
246
|
+
' ;;',
|
|
247
|
+
' restart)',
|
|
248
|
+
` suggestions=(${words(RESTART_FLAGS)})`,
|
|
249
|
+
' ;;',
|
|
250
|
+
' status)',
|
|
251
|
+
` suggestions=(${words(STATUS_FLAGS)})`,
|
|
252
|
+
' ;;',
|
|
253
|
+
' logs)',
|
|
254
|
+
` suggestions=(${words(LOGS_FLAGS)})`,
|
|
255
|
+
' ;;',
|
|
256
|
+
' open)',
|
|
257
|
+
` suggestions=(${words(OPEN_FLAGS)})`,
|
|
258
|
+
' ;;',
|
|
259
|
+
' doctor)',
|
|
260
|
+
` suggestions=(${words(DOCTOR_FLAGS)})`,
|
|
261
|
+
' ;;',
|
|
262
|
+
' completion)',
|
|
263
|
+
' if (( CURRENT == 3 )); then',
|
|
264
|
+
` suggestions=(${words(COMPLETION_COMMANDS)} ${words(COMPLETION_FLAGS)})`,
|
|
265
|
+
' elif [[ "$subcommand" == "install" ]]; then',
|
|
266
|
+
' if (( CURRENT == 4 )); then',
|
|
267
|
+
` suggestions=(${words(SUPPORTED_COMPLETION_SHELLS)})`,
|
|
268
|
+
' else',
|
|
269
|
+
` suggestions=(${words(COMPLETION_INSTALL_FLAGS)})`,
|
|
270
|
+
' fi',
|
|
271
|
+
' elif [[ "$subcommand" == "uninstall" ]]; then',
|
|
272
|
+
' if (( CURRENT == 4 )); then',
|
|
273
|
+
` suggestions=(${words(SUPPORTED_COMPLETION_SHELLS)})`,
|
|
274
|
+
' else',
|
|
275
|
+
` suggestions=(${words(COMPLETION_UNINSTALL_FLAGS)})`,
|
|
276
|
+
' fi',
|
|
277
|
+
' else',
|
|
278
|
+
` suggestions=(${words(SUPPORTED_COMPLETION_SHELLS)} ${words(COMPLETION_FLAGS)})`,
|
|
279
|
+
' fi',
|
|
280
|
+
' ;;',
|
|
281
|
+
' config)',
|
|
282
|
+
' if (( CURRENT == 3 )); then',
|
|
283
|
+
` suggestions=(${words(CONFIG_SUBCOMMANDS)} ${words(CONFIG_FLAGS)})`,
|
|
284
|
+
' else',
|
|
285
|
+
' case "$subcommand" in',
|
|
286
|
+
' get|set|unset)',
|
|
287
|
+
' if (( CURRENT == 4 )); then',
|
|
288
|
+
` suggestions=(${words(CONFIG_KEYS)})`,
|
|
289
|
+
' else',
|
|
290
|
+
' suggestions=()',
|
|
291
|
+
' fi',
|
|
292
|
+
' ;;',
|
|
293
|
+
' show|validate|path)',
|
|
294
|
+
` suggestions=(${words(CONFIG_FLAGS)})`,
|
|
295
|
+
' ;;',
|
|
296
|
+
' root)',
|
|
297
|
+
' if (( CURRENT == 4 )); then',
|
|
298
|
+
` suggestions=(${words(CONFIG_ROOT_SUBCOMMANDS)} ${words(COMPLETION_FLAGS)})`,
|
|
299
|
+
' else',
|
|
300
|
+
' suggestions=()',
|
|
301
|
+
' fi',
|
|
302
|
+
' ;;',
|
|
303
|
+
' password)',
|
|
304
|
+
' if (( CURRENT == 4 )); then',
|
|
305
|
+
` suggestions=(${words(CONFIG_PASSWORD_SUBCOMMANDS)} ${words(COMPLETION_FLAGS)})`,
|
|
306
|
+
' elif [[ "$nested" == "set" ]]; then',
|
|
307
|
+
` suggestions=(${words(CONFIG_PASSWORD_SET_FLAGS)})`,
|
|
308
|
+
' else',
|
|
309
|
+
' suggestions=()',
|
|
310
|
+
' fi',
|
|
311
|
+
' ;;',
|
|
312
|
+
' auth)',
|
|
313
|
+
' if (( CURRENT == 4 )); then',
|
|
314
|
+
` suggestions=(${words(CONFIG_AUTH_SUBCOMMANDS)} ${words(COMPLETION_FLAGS)})`,
|
|
315
|
+
' elif [[ "$nested" == "public-mode" ]] && (( CURRENT == 5 )); then',
|
|
316
|
+
' suggestions=(on off)',
|
|
317
|
+
' else',
|
|
318
|
+
' suggestions=()',
|
|
319
|
+
' fi',
|
|
320
|
+
' ;;',
|
|
321
|
+
' *)',
|
|
322
|
+
' suggestions=()',
|
|
323
|
+
' ;;',
|
|
324
|
+
' esac',
|
|
325
|
+
' fi',
|
|
326
|
+
' ;;',
|
|
327
|
+
' auth)',
|
|
328
|
+
' if (( CURRENT == 3 )); then',
|
|
329
|
+
` suggestions=(${words(AUTH_SUBCOMMANDS)} ${words(AUTH_FLAGS)})`,
|
|
330
|
+
' else',
|
|
331
|
+
' case "$subcommand" in',
|
|
332
|
+
' status)',
|
|
333
|
+
` suggestions=(${words(AUTH_FLAGS)})`,
|
|
334
|
+
' ;;',
|
|
335
|
+
' ip)',
|
|
336
|
+
' if (( CURRENT == 4 )); then',
|
|
337
|
+
` suggestions=(${words(AUTH_IP_SUBCOMMANDS)} ${words(COMPLETION_FLAGS)})`,
|
|
338
|
+
' elif [[ "$nested" == "list" ]]; then',
|
|
339
|
+
` suggestions=(${words(AUTH_FLAGS)})`,
|
|
340
|
+
' elif [[ "$nested" == "unblock" ]]; then',
|
|
341
|
+
` suggestions=(${words(AUTH_IP_UNBLOCK_FLAGS)})`,
|
|
342
|
+
' else',
|
|
343
|
+
' suggestions=()',
|
|
344
|
+
' fi',
|
|
345
|
+
' ;;',
|
|
346
|
+
' *)',
|
|
347
|
+
' suggestions=()',
|
|
348
|
+
' ;;',
|
|
349
|
+
' esac',
|
|
350
|
+
' fi',
|
|
351
|
+
' ;;',
|
|
352
|
+
' *)',
|
|
353
|
+
` suggestions=(${words(TOP_LEVEL_FLAGS)})`,
|
|
354
|
+
' ;;',
|
|
355
|
+
' esac',
|
|
356
|
+
'',
|
|
357
|
+
' (( ${#suggestions[@]} )) && compadd -- "${suggestions[@]}"',
|
|
358
|
+
'}',
|
|
359
|
+
'',
|
|
360
|
+
'if ! typeset -f compdef >/dev/null 2>&1; then',
|
|
361
|
+
' autoload -Uz compinit',
|
|
362
|
+
' compinit >/dev/null 2>&1',
|
|
363
|
+
'fi',
|
|
364
|
+
'',
|
|
365
|
+
'compdef _coder_studio_complete coder-studio',
|
|
366
|
+
]);
|
|
367
|
+
}
|
|
368
|
+
function generateFishScript() {
|
|
369
|
+
return lines([
|
|
370
|
+
'# fish completion for coder-studio',
|
|
371
|
+
'',
|
|
372
|
+
'complete -c coder-studio -n "not __fish_seen_subcommand_from help start stop restart status logs open doctor config auth completion" -a "help start stop restart status logs open doctor config auth completion"',
|
|
373
|
+
'complete -c coder-studio -n "not __fish_seen_subcommand_from help start stop restart status logs open doctor config auth completion" -l help -s h',
|
|
374
|
+
'complete -c coder-studio -n "not __fish_seen_subcommand_from help start stop restart status logs open doctor config auth completion" -l version -s v',
|
|
375
|
+
'',
|
|
376
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from help" -a "start stop restart status logs open doctor config auth completion"',
|
|
377
|
+
'',
|
|
378
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from start" -l host -r',
|
|
379
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from start" -l port -r',
|
|
380
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from start" -l foreground',
|
|
381
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from start" -l json',
|
|
382
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from start" -l help -s h',
|
|
383
|
+
'',
|
|
384
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from stop" -l json',
|
|
385
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from stop" -l help -s h',
|
|
386
|
+
'',
|
|
387
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from restart" -l json',
|
|
388
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from restart" -l help -s h',
|
|
389
|
+
'',
|
|
390
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from status" -l host -r',
|
|
391
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from status" -l port -r',
|
|
392
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from status" -l json',
|
|
393
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from status" -l help -s h',
|
|
394
|
+
'',
|
|
395
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from logs" -l follow -s f',
|
|
396
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from logs" -l lines -s n -r',
|
|
397
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from logs" -l help -s h',
|
|
398
|
+
'',
|
|
399
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from open" -l host -r',
|
|
400
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from open" -l port -r',
|
|
401
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from open" -l json',
|
|
402
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from open" -l help -s h',
|
|
403
|
+
'',
|
|
404
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from doctor" -l host -r',
|
|
405
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from doctor" -l port -r',
|
|
406
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from doctor" -l json',
|
|
407
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from doctor" -l help -s h',
|
|
408
|
+
'',
|
|
409
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and not __fish_seen_subcommand_from install uninstall bash zsh fish" -a "install uninstall bash zsh fish"',
|
|
410
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from completion" -l help -s h',
|
|
411
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from bash zsh fish" -a "bash zsh fish"',
|
|
412
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from install" -l force',
|
|
413
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from install" -l json',
|
|
414
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from uninstall; and not __fish_seen_subcommand_from bash zsh fish" -a "bash zsh fish"',
|
|
415
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from completion; and __fish_seen_subcommand_from uninstall" -l json',
|
|
416
|
+
'',
|
|
417
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from path show get set unset validate root password auth" -a "path show get set unset validate root password auth"',
|
|
418
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from config" -l json',
|
|
419
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from config" -l help -s h',
|
|
420
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from get set unset" -a "server.host server.port root.path auth.publicMode auth.password auth.sessionIdleMinutes auth.sessionMaxHours system.openCommand logs.tailLines"',
|
|
421
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from root; and not __fish_seen_subcommand_from show set clear" -a "show set clear"',
|
|
422
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from password; and not __fish_seen_subcommand_from status set clear" -a "status set clear"',
|
|
423
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from password; and __fish_seen_subcommand_from set" -l stdin',
|
|
424
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from auth; and not __fish_seen_subcommand_from public-mode session-idle session-max" -a "public-mode session-idle session-max"',
|
|
425
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from config; and __fish_seen_subcommand_from public-mode" -a "on off"',
|
|
426
|
+
'',
|
|
427
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from auth; and not __fish_seen_subcommand_from status ip" -a "status ip"',
|
|
428
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from auth" -l json',
|
|
429
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from auth" -l help -s h',
|
|
430
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from auth; and __fish_seen_subcommand_from ip; and not __fish_seen_subcommand_from list unblock" -a "list unblock"',
|
|
431
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from auth; and __fish_seen_subcommand_from ip; and __fish_seen_subcommand_from unblock" -l all',
|
|
432
|
+
'complete -c coder-studio -n "__fish_seen_subcommand_from auth; and __fish_seen_subcommand_from ip; and __fish_seen_subcommand_from unblock" -l json',
|
|
433
|
+
]);
|
|
434
|
+
}
|
|
435
|
+
function resolveInstallPlan(shell, env = process.env) {
|
|
436
|
+
const home = os.homedir();
|
|
437
|
+
const sharedCompletionDir = path.join(home, '.coder-studio', 'completions');
|
|
438
|
+
const xdgConfigHome = env.XDG_CONFIG_HOME || path.join(home, '.config');
|
|
439
|
+
switch (shell) {
|
|
440
|
+
case 'bash':
|
|
441
|
+
return {
|
|
442
|
+
shell,
|
|
443
|
+
scriptPath: path.join(sharedCompletionDir, 'coder-studio.bash'),
|
|
444
|
+
profilePath: path.join(home, '.bashrc'),
|
|
445
|
+
sourceLine: '[ -f "$HOME/.coder-studio/completions/coder-studio.bash" ] && source "$HOME/.coder-studio/completions/coder-studio.bash"',
|
|
446
|
+
activationCommand: 'source ~/.bashrc',
|
|
447
|
+
};
|
|
448
|
+
case 'zsh':
|
|
449
|
+
return {
|
|
450
|
+
shell,
|
|
451
|
+
scriptPath: path.join(sharedCompletionDir, 'coder-studio.zsh'),
|
|
452
|
+
profilePath: path.join(home, '.zshrc'),
|
|
453
|
+
sourceLine: '[ -f "$HOME/.coder-studio/completions/coder-studio.zsh" ] && source "$HOME/.coder-studio/completions/coder-studio.zsh"',
|
|
454
|
+
activationCommand: 'source ~/.zshrc',
|
|
455
|
+
};
|
|
456
|
+
case 'fish':
|
|
457
|
+
return {
|
|
458
|
+
shell,
|
|
459
|
+
scriptPath: path.join(xdgConfigHome, 'fish', 'completions', 'coder-studio.fish'),
|
|
460
|
+
profilePath: null,
|
|
461
|
+
sourceLine: null,
|
|
462
|
+
activationCommand: 'exec fish',
|
|
463
|
+
};
|
|
464
|
+
default:
|
|
465
|
+
throw new Error(`unsupported completion shell: ${shell}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function buildManagedBlock(sourceLine) {
|
|
469
|
+
return `${MANAGED_BLOCK_START}\n${sourceLine}\n${MANAGED_BLOCK_END}`;
|
|
470
|
+
}
|
|
471
|
+
function escapeRegex(value) {
|
|
472
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
473
|
+
}
|
|
474
|
+
function upsertManagedBlock(currentText, block) {
|
|
475
|
+
const pattern = new RegExp(`${escapeRegex(MANAGED_BLOCK_START)}[\\s\\S]*?${escapeRegex(MANAGED_BLOCK_END)}`, 'm');
|
|
476
|
+
if (pattern.test(currentText)) {
|
|
477
|
+
return currentText.replace(pattern, block);
|
|
478
|
+
}
|
|
479
|
+
const normalized = currentText.trimEnd();
|
|
480
|
+
return normalized ? `${normalized}\n\n${block}\n` : `${block}\n`;
|
|
481
|
+
}
|
|
482
|
+
function removeManagedBlock(currentText) {
|
|
483
|
+
const pattern = new RegExp(`\\n*${escapeRegex(MANAGED_BLOCK_START)}\\n[\\s\\S]*?\\n${escapeRegex(MANAGED_BLOCK_END)}\\n*`, 'm');
|
|
484
|
+
if (!pattern.test(currentText)) {
|
|
485
|
+
return currentText;
|
|
486
|
+
}
|
|
487
|
+
const next = currentText.replace(pattern, '\n').replace(/\n{3,}/g, '\n\n').trimEnd();
|
|
488
|
+
return next ? `${next}\n` : '';
|
|
489
|
+
}
|
|
490
|
+
async function readOptionalText(filePath) {
|
|
491
|
+
try {
|
|
492
|
+
return await fs.readFile(filePath, 'utf8');
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
if (error && typeof error === 'object' && error.code === 'ENOENT') {
|
|
496
|
+
return '';
|
|
497
|
+
}
|
|
498
|
+
throw error;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
export async function installCompletionScript(shell, { env = process.env, force = false } = {}) {
|
|
502
|
+
const plan = resolveInstallPlan(shell, env);
|
|
503
|
+
const script = generateCompletionScript(shell);
|
|
504
|
+
await fs.mkdir(path.dirname(plan.scriptPath), { recursive: true });
|
|
505
|
+
const currentScript = await readOptionalText(plan.scriptPath);
|
|
506
|
+
const scriptUpdated = force || currentScript !== script;
|
|
507
|
+
if (scriptUpdated) {
|
|
508
|
+
await fs.writeFile(plan.scriptPath, script, 'utf8');
|
|
509
|
+
}
|
|
510
|
+
let profileUpdated = false;
|
|
511
|
+
if (plan.profilePath && plan.sourceLine) {
|
|
512
|
+
const currentProfile = await readOptionalText(plan.profilePath);
|
|
513
|
+
const nextProfile = upsertManagedBlock(currentProfile, buildManagedBlock(plan.sourceLine));
|
|
514
|
+
profileUpdated = force || nextProfile !== currentProfile;
|
|
515
|
+
if (profileUpdated) {
|
|
516
|
+
await fs.writeFile(plan.profilePath, nextProfile, 'utf8');
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
shell: plan.shell,
|
|
521
|
+
scriptPath: plan.scriptPath,
|
|
522
|
+
scriptUpdated,
|
|
523
|
+
profilePath: plan.profilePath,
|
|
524
|
+
profileUpdated,
|
|
525
|
+
activationCommand: plan.activationCommand,
|
|
526
|
+
forced: Boolean(force),
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
export async function uninstallCompletionScript(shell, { env = process.env } = {}) {
|
|
530
|
+
const plan = resolveInstallPlan(shell, env);
|
|
531
|
+
const currentScript = await readOptionalText(plan.scriptPath);
|
|
532
|
+
const scriptRemoved = currentScript.length > 0;
|
|
533
|
+
await fs.rm(plan.scriptPath, { force: true });
|
|
534
|
+
let profileUpdated = false;
|
|
535
|
+
if (plan.profilePath) {
|
|
536
|
+
const currentProfile = await readOptionalText(plan.profilePath);
|
|
537
|
+
const nextProfile = removeManagedBlock(currentProfile);
|
|
538
|
+
profileUpdated = nextProfile !== currentProfile;
|
|
539
|
+
if (profileUpdated) {
|
|
540
|
+
await fs.writeFile(plan.profilePath, nextProfile, 'utf8');
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return {
|
|
544
|
+
shell: plan.shell,
|
|
545
|
+
scriptPath: plan.scriptPath,
|
|
546
|
+
scriptRemoved,
|
|
547
|
+
profilePath: plan.profilePath,
|
|
548
|
+
profileUpdated,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
export function generateCompletionScript(shell) {
|
|
552
|
+
switch (shell) {
|
|
553
|
+
case 'bash':
|
|
554
|
+
return generateBashScript();
|
|
555
|
+
case 'zsh':
|
|
556
|
+
return generateZshScript();
|
|
557
|
+
case 'fish':
|
|
558
|
+
return generateFishScript();
|
|
559
|
+
default:
|
|
560
|
+
throw new Error(`unsupported completion shell: ${shell}`);
|
|
561
|
+
}
|
|
562
|
+
}
|
package/lib/config.mjs
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export const DEFAULT_HOST = '127.0.0.1';
|
|
5
|
+
export const DEFAULT_PORT = 41033;
|
|
6
|
+
export const DEFAULT_LOG_TAIL_LINES = 80;
|
|
7
|
+
export const DEFAULT_SESSION_IDLE_MINUTES = 15;
|
|
8
|
+
export const DEFAULT_SESSION_MAX_HOURS = 12;
|
|
9
|
+
export const STATE_DIR_NAME = 'coder-studio';
|
|
10
|
+
export function resolveStateDir(env = process.env, platform = process.platform) {
|
|
11
|
+
if (env.CODER_STUDIO_HOME) {
|
|
12
|
+
return path.resolve(env.CODER_STUDIO_HOME);
|
|
13
|
+
}
|
|
14
|
+
const home = os.homedir();
|
|
15
|
+
if (platform === 'darwin') {
|
|
16
|
+
return path.join(home, 'Library', 'Application Support', STATE_DIR_NAME);
|
|
17
|
+
}
|
|
18
|
+
if (platform === 'win32') {
|
|
19
|
+
const localAppData = env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
|
|
20
|
+
return path.join(localAppData, STATE_DIR_NAME);
|
|
21
|
+
}
|
|
22
|
+
const xdgStateHome = env.XDG_STATE_HOME || path.join(home, '.local', 'state');
|
|
23
|
+
return path.join(xdgStateHome, STATE_DIR_NAME);
|
|
24
|
+
}
|
|
25
|
+
export function resolveDataDir(stateDir, env = process.env) {
|
|
26
|
+
if (env.CODER_STUDIO_DATA_DIR) {
|
|
27
|
+
return path.resolve(env.CODER_STUDIO_DATA_DIR);
|
|
28
|
+
}
|
|
29
|
+
return path.join(stateDir, 'data');
|
|
30
|
+
}
|
|
31
|
+
export function resolveConfigPath(stateDir) {
|
|
32
|
+
return path.join(stateDir, 'config.json');
|
|
33
|
+
}
|
|
34
|
+
export function resolveAuthPath(dataDir) {
|
|
35
|
+
return path.join(dataDir, 'auth.json');
|
|
36
|
+
}
|
|
37
|
+
export function resolveLogPath(stateDir) {
|
|
38
|
+
return path.join(stateDir, 'coder-studio.log');
|
|
39
|
+
}
|
|
40
|
+
export function resolvePidPath(stateDir) {
|
|
41
|
+
return path.join(stateDir, 'coder-studio.pid');
|
|
42
|
+
}
|
|
43
|
+
export function resolveRuntimePath(stateDir) {
|
|
44
|
+
return path.join(stateDir, 'runtime.json');
|
|
45
|
+
}
|
|
46
|
+
function formatHostForUrl(host) {
|
|
47
|
+
if (!host)
|
|
48
|
+
return DEFAULT_HOST;
|
|
49
|
+
if (host.startsWith('[') && host.endsWith(']')) {
|
|
50
|
+
return host;
|
|
51
|
+
}
|
|
52
|
+
return host.includes(':') ? `[${host}]` : host;
|
|
53
|
+
}
|
|
54
|
+
export function buildEndpoint(host = DEFAULT_HOST, port = DEFAULT_PORT) {
|
|
55
|
+
return `http://${formatHostForUrl(host)}:${port}`;
|
|
56
|
+
}
|
|
57
|
+
export function defaultRootPath() {
|
|
58
|
+
return path.join(os.homedir(), 'coder-studio-workspaces');
|
|
59
|
+
}
|