@meistrari/tela-cli 0.6.0 → 0.7.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.
Files changed (59) hide show
  1. package/README.md +56 -106
  2. package/dist/commands/auth.d.ts +2 -0
  3. package/dist/commands/context.d.ts +2 -0
  4. package/dist/commands/doctor-auth.d.ts +2 -0
  5. package/dist/commands/env.d.ts +2 -0
  6. package/dist/credentials.d.ts +2 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +7027 -0
  9. package/dist/output.d.ts +13 -0
  10. package/dist/program.d.ts +2 -0
  11. package/package.json +17 -24
  12. package/scripts/install-skills.cjs +127 -0
  13. package/skills/tela-cli/SKILL.md +20 -0
  14. package/skills/tela-cli/references/agent.md +111 -0
  15. package/cli.ts +0 -25
  16. package/postinstall.sh +0 -19
  17. package/src/agent-api.ts +0 -300
  18. package/src/assets.d.ts +0 -4
  19. package/src/auth.ts +0 -432
  20. package/src/claude.ts +0 -101
  21. package/src/commands/doctor.ts +0 -35
  22. package/src/commands/feedback.ts +0 -701
  23. package/src/commands/metadata.ts +0 -242
  24. package/src/commands/push.ts +0 -208
  25. package/src/commands/run.ts +0 -1699
  26. package/src/commands/status.ts +0 -109
  27. package/src/commands/sync.ts +0 -245
  28. package/src/commit-message.ts +0 -69
  29. package/src/config.ts +0 -799
  30. package/src/diagnostics.ts +0 -813
  31. package/src/errors.ts +0 -122
  32. package/src/forgejo.ts +0 -233
  33. package/src/git.ts +0 -457
  34. package/src/ink/assets/160092__jorickhoofd__dog-bark-1.mp3 +0 -0
  35. package/src/ink/bark.tsx +0 -240
  36. package/src/ink/components.tsx +0 -177
  37. package/src/ink/runtime.tsx +0 -89
  38. package/src/ink/screens/feedback-screen.tsx +0 -394
  39. package/src/ink/screens/push-screen.tsx +0 -231
  40. package/src/ink/screens/run-screen.tsx +0 -291
  41. package/src/ink/screens/setup/breadcrumb.tsx +0 -23
  42. package/src/ink/screens/setup/setup-screen.tsx +0 -611
  43. package/src/ink/screens/setup/step-frame.tsx +0 -30
  44. package/src/ink/screens/setup/steps/done-step.tsx +0 -28
  45. package/src/ink/screens/setup/steps/environment-step.tsx +0 -22
  46. package/src/ink/screens/setup/steps/organization-step.tsx +0 -34
  47. package/src/ink/screens/setup/steps/review-step.tsx +0 -77
  48. package/src/ink/screens/setup/steps/running-step.tsx +0 -36
  49. package/src/ink/screens/setup/steps/ssh-key-step.tsx +0 -47
  50. package/src/ink/screens/setup/steps/text-field-step.tsx +0 -17
  51. package/src/ink/screens/setup/steps.ts +0 -156
  52. package/src/ink/screens/setup/types.ts +0 -65
  53. package/src/ink/screens/sync-screen.tsx +0 -282
  54. package/src/process.ts +0 -251
  55. package/src/program.ts +0 -2105
  56. package/src/runtime.ts +0 -147
  57. package/src/types.ts +0 -165
  58. package/src/ui.ts +0 -295
  59. package/src/version.ts +0 -3
@@ -1,611 +0,0 @@
1
- import React, { useEffect, useMemo, useRef, useState } from 'react'
2
- import { useInput } from 'ink'
3
- import { ENVIRONMENT_PRESET_OPTIONS, resolveEnvironmentConfigForPreset } from '../../../config.ts'
4
- import {
5
- inferSshPrivateKeyPath,
6
- loadSshPublicKey,
7
- validateSshPrivateKeyPath,
8
- validateSshPublicKeyPath,
9
- } from '../../../forgejo.ts'
10
- import type { TelaCliAuthState, TelaCliForgejoOrganization } from '../../../types.ts'
11
- import { StepFrame } from './step-frame.tsx'
12
- import type { BreadcrumbEntry } from './breadcrumb.tsx'
13
- import {
14
- getVisibleEditSteps,
15
- summarizeStep,
16
- type SetupStepDescriptor,
17
- type SetupStepId,
18
- } from './steps.ts'
19
- import { DoneStep } from './steps/done-step.tsx'
20
- import { EnvironmentStep } from './steps/environment-step.tsx'
21
- import { OrganizationStep } from './steps/organization-step.tsx'
22
- import { ReviewStep } from './steps/review-step.tsx'
23
- import { RunningStep } from './steps/running-step.tsx'
24
- import { SshKeyStep, buildSshKeyOptions } from './steps/ssh-key-step.tsx'
25
- import { TextFieldStep } from './steps/text-field-step.tsx'
26
- import {
27
- CUSTOM_SSH_KEY_OPTION,
28
- type SetupFormValues,
29
- type SetupScreenProps,
30
- type SetupScreenResult,
31
- } from './types.ts'
32
-
33
- export type { SetupScreenProps, SetupScreenResult } from './types.ts'
34
-
35
- type SetupPhase =
36
- | 'editing'
37
- | 'review'
38
- | 'authenticating'
39
- | 'selectingOrganization'
40
- | 'provisioning'
41
- | 'done'
42
-
43
- type TextStepId = Exclude<SetupStepId, 'environment' | 'sshKey'>
44
-
45
- function pushProgressLines(lines: string[], message: string): string[] {
46
- const nextLines = message
47
- .split('\n')
48
- .map(line => line.trim())
49
- .filter(Boolean)
50
-
51
- if (nextLines.length === 0) {
52
- return lines
53
- }
54
-
55
- return [...lines, ...nextLines].slice(-12)
56
- }
57
-
58
- export function SetupScreen(props: SetupScreenProps): React.ReactElement {
59
- const [phase, setPhase] = useState<SetupPhase>('editing')
60
- const [stepIndex, setStepIndex] = useState(0)
61
- const [values, setValues] = useState<SetupFormValues>({
62
- environment: props.defaults.environment,
63
- agentApiUrl: props.defaults.agentApiUrl,
64
- authApiUrl: props.defaults.authApiUrl,
65
- authDashboardUrl: props.defaults.authDashboardUrl,
66
- telaApiUrl: props.defaults.telaApiUrl,
67
- requesterApplicationId: props.defaults.requesterApplicationId,
68
- targetApplicationId: props.defaults.targetApplicationId,
69
- cloneRoot: props.defaults.cloneRoot,
70
- sshKeySelection: props.sshPublicKeys[0]?.path ?? CUSTOM_SSH_KEY_OPTION,
71
- sshKeyPath: '',
72
- sshPrivateKeyPath: props.sshPublicKeys[0]
73
- ? inferSshPrivateKeyPath(props.sshPublicKeys[0].path)
74
- : '',
75
- })
76
- const [errors, setErrors] = useState<Partial<Record<SetupStepId, string>>>({})
77
- const [createCloneRoot, setCreateCloneRoot] = useState(true)
78
- const [progressLines, setProgressLines] = useState<string[]>([])
79
- const [verificationUrl, setVerificationUrl] = useState<string | null>(null)
80
- const [userCode, setUserCode] = useState<string | null>(null)
81
- const [runningStage, setRunningStage] = useState<'auth' | 'provision'>('auth')
82
- const [authenticated, setAuthenticated] = useState<TelaCliAuthState | null>(null)
83
- const [organizationOptions, setOrganizationOptions] = useState<TelaCliForgejoOrganization[]>([])
84
- const [organizationIndex, setOrganizationIndex] = useState(0)
85
- const [preferredOrganizationId, setPreferredOrganizationId] = useState<string | undefined>(undefined)
86
- const [selectedOrganization, setSelectedOrganization] = useState<TelaCliForgejoOrganization | null>(null)
87
- const [result, setResult] = useState<SetupScreenResult | null>(null)
88
- const authStartedRef = useRef(false)
89
- const provisionStartedRef = useRef(false)
90
- const previousSuggestedPrivateKeyPathRef = useRef(values.sshPrivateKeyPath)
91
-
92
- const visibleSteps = useMemo(() => getVisibleEditSteps(values), [values])
93
- const safeStepIndex = Math.min(stepIndex, Math.max(visibleSteps.length - 1, 0))
94
- const currentStep: SetupStepDescriptor | undefined = visibleSteps[safeStepIndex]
95
-
96
- const resolvedConfig = useMemo(() => {
97
- try {
98
- return resolveEnvironmentConfigForPreset(values.environment, values)
99
- }
100
- catch {
101
- return {
102
- environment: values.environment,
103
- agentApiUrl: values.agentApiUrl,
104
- authApiUrl: values.authApiUrl,
105
- authDashboardUrl: values.authDashboardUrl,
106
- telaApiUrl: values.telaApiUrl,
107
- requesterApplicationId: values.requesterApplicationId,
108
- targetApplicationId: values.targetApplicationId,
109
- }
110
- }
111
- }, [values])
112
-
113
- const sshKeyOptions = useMemo(() => buildSshKeyOptions(props.sshPublicKeys), [props.sshPublicKeys])
114
- const selectedSshKey = useMemo(() => {
115
- if (values.sshKeySelection === CUSTOM_SSH_KEY_OPTION) {
116
- try {
117
- return values.sshKeyPath.trim() ? loadSshPublicKey(values.sshKeyPath) : undefined
118
- }
119
- catch {
120
- return undefined
121
- }
122
- }
123
-
124
- return props.sshPublicKeys.find(key => key.path === values.sshKeySelection)
125
- }, [props.sshPublicKeys, values.sshKeyPath, values.sshKeySelection])
126
- const suggestedPrivateKeyPath = useMemo(
127
- () => selectedSshKey ? inferSshPrivateKeyPath(selectedSshKey.path) : '',
128
- [selectedSshKey],
129
- )
130
- const cloneRootState = useMemo(() => props.inspectCloneRoot(values.cloneRoot), [props, values.cloneRoot])
131
-
132
- const breadcrumb = useMemo<BreadcrumbEntry[]>(() => {
133
- const entries: BreadcrumbEntry[] = []
134
- const answered = visibleSteps.slice(0, safeStepIndex)
135
- for (const step of answered) {
136
- entries.push({
137
- label: step.label,
138
- value: summarizeStep(step, values, selectedSshKey?.title),
139
- })
140
- }
141
-
142
- return entries
143
- }, [safeStepIndex, selectedSshKey, values, visibleSteps])
144
-
145
- const reviewResolved = Boolean(authenticated && selectedOrganization)
146
-
147
- function resetResolvedSetup(): void {
148
- setAuthenticated(null)
149
- setOrganizationOptions([])
150
- setOrganizationIndex(0)
151
- setPreferredOrganizationId(undefined)
152
- setSelectedOrganization(null)
153
- setProgressLines([])
154
- setVerificationUrl(null)
155
- setUserCode(null)
156
- setRunningStage('auth')
157
- }
158
-
159
- useEffect(() => {
160
- if (phase !== 'authenticating') {
161
- authStartedRef.current = false
162
- }
163
-
164
- if (phase !== 'provisioning') {
165
- provisionStartedRef.current = false
166
- }
167
- }, [phase])
168
-
169
- useEffect(() => {
170
- if (phase !== 'authenticating' || authStartedRef.current) {
171
- return
172
- }
173
-
174
- authStartedRef.current = true
175
- resetResolvedSetup()
176
- setRunningStage('auth')
177
- setProgressLines(['Starting Tela sign-in...'])
178
-
179
- void props.authenticate({
180
- ...resolvedConfig,
181
- cloneRoot: values.cloneRoot,
182
- }, (event) => {
183
- if (event.type === 'user_code') {
184
- setVerificationUrl(event.verificationUrl ?? null)
185
- setUserCode(event.userCode ?? null)
186
- }
187
- setProgressLines(current => pushProgressLines(current, event.message))
188
- })
189
- .then(async (auth) => {
190
- setAuthenticated(auth)
191
- setVerificationUrl(null)
192
- setUserCode(null)
193
- setProgressLines(current => pushProgressLines(
194
- current,
195
- `Signed in as ${auth.user.email} in ${auth.workspace.title}.`,
196
- ))
197
- setProgressLines(current => pushProgressLines(current, 'Loading workspace organizations...'))
198
-
199
- const organizationResolution = await props.resolveOrganizations({
200
- ...resolvedConfig,
201
- cloneRoot: values.cloneRoot,
202
- auth,
203
- })
204
-
205
- setOrganizationOptions(organizationResolution.organizations)
206
- setPreferredOrganizationId(organizationResolution.preferredOrganization?.id)
207
-
208
- if (organizationResolution.organizations.length === 1) {
209
- const organization = organizationResolution.organizations[0]!
210
- setSelectedOrganization(organization)
211
- setProgressLines(current => pushProgressLines(
212
- current,
213
- `Using workspace organization ${organization.name}.`,
214
- ))
215
- setPhase('review')
216
- return
217
- }
218
-
219
- const preferredIndex = organizationResolution.organizations.findIndex(
220
- organization => organization.id === organizationResolution.preferredOrganization?.id,
221
- )
222
- setOrganizationIndex(Math.max(preferredIndex, 0))
223
- setPhase('selectingOrganization')
224
- })
225
- .catch((error) => {
226
- props.controller.reject(error)
227
- })
228
- }, [phase, props, resolvedConfig, values.cloneRoot])
229
-
230
- useEffect(() => {
231
- if (phase !== 'provisioning' || provisionStartedRef.current) {
232
- return
233
- }
234
-
235
- provisionStartedRef.current = true
236
- setRunningStage('provision')
237
- setVerificationUrl(null)
238
- setUserCode(null)
239
-
240
- if (!selectedSshKey) {
241
- props.controller.reject(new Error('SSH public key is required.'))
242
- return
243
- }
244
-
245
- if (!authenticated || !selectedOrganization) {
246
- props.controller.reject(new Error('Authenticated organization selection is required.'))
247
- return
248
- }
249
-
250
- setProgressLines(current => pushProgressLines(
251
- current,
252
- `Provisioning Forgejo access for ${selectedOrganization.name}.`,
253
- ))
254
-
255
- void props.execute({
256
- ...resolvedConfig,
257
- cloneRoot: values.cloneRoot,
258
- createCloneRoot,
259
- sshKey: selectedSshKey,
260
- sshPrivateKeyPath: values.sshPrivateKeyPath,
261
- auth: authenticated,
262
- organization: selectedOrganization,
263
- }, (event) => {
264
- setProgressLines(current => pushProgressLines(current, event.message))
265
- })
266
- .then((nextResult) => {
267
- setResult(nextResult)
268
- setPhase('done')
269
- })
270
- .catch((error) => {
271
- props.controller.reject(error)
272
- })
273
- }, [
274
- authenticated,
275
- createCloneRoot,
276
- phase,
277
- props,
278
- resolvedConfig,
279
- selectedOrganization,
280
- selectedSshKey,
281
- values.sshPrivateKeyPath,
282
- values.cloneRoot,
283
- ])
284
-
285
- useEffect(() => {
286
- if (phase === 'done' && result) {
287
- props.controller.resolve(result, 80)
288
- }
289
- }, [phase, props, result])
290
-
291
- useEffect(() => {
292
- setValues((current) => {
293
- const previousSuggested = previousSuggestedPrivateKeyPathRef.current
294
- previousSuggestedPrivateKeyPathRef.current = suggestedPrivateKeyPath
295
-
296
- if (
297
- current.sshPrivateKeyPath
298
- && current.sshPrivateKeyPath !== previousSuggested
299
- ) {
300
- return current
301
- }
302
-
303
- if (current.sshPrivateKeyPath === suggestedPrivateKeyPath) {
304
- return current
305
- }
306
-
307
- return {
308
- ...current,
309
- sshPrivateKeyPath: suggestedPrivateKeyPath,
310
- }
311
- })
312
- }, [suggestedPrivateKeyPath])
313
-
314
- useInput((input, key) => {
315
- if (phase === 'authenticating' || phase === 'provisioning' || phase === 'done') {
316
- return
317
- }
318
-
319
- if (phase === 'selectingOrganization') {
320
- if (key.escape || key.leftArrow) {
321
- resetResolvedSetup()
322
- setPhase('review')
323
- return
324
- }
325
-
326
- if (key.upArrow || key.downArrow) {
327
- const direction = key.upArrow ? -1 : 1
328
- setOrganizationIndex(current => {
329
- if (organizationOptions.length === 0) {
330
- return current
331
- }
332
-
333
- return (organizationOptions.length + current + direction) % organizationOptions.length
334
- })
335
- return
336
- }
337
-
338
- if (key.return) {
339
- const organization = organizationOptions[organizationIndex]
340
- if (!organization) {
341
- props.controller.reject(new Error('Organization selection is required.'))
342
- return
343
- }
344
-
345
- setSelectedOrganization(organization)
346
- setProgressLines(current => pushProgressLines(
347
- current,
348
- `Selected workspace organization ${organization.name}.`,
349
- ))
350
- setPhase('review')
351
- }
352
-
353
- return
354
- }
355
-
356
- if (phase === 'review') {
357
- if (key.escape || key.leftArrow) {
358
- if (reviewResolved) {
359
- if (organizationOptions.length > 1) {
360
- setSelectedOrganization(null)
361
- setPhase('selectingOrganization')
362
- return
363
- }
364
-
365
- resetResolvedSetup()
366
- return
367
- }
368
-
369
- setPhase('editing')
370
- setStepIndex(Math.max(visibleSteps.length - 1, 0))
371
- return
372
- }
373
-
374
- if (!cloneRootState.exists && (input === ' ' || key.rightArrow)) {
375
- setCreateCloneRoot(current => !current)
376
- return
377
- }
378
-
379
- if (key.return) {
380
- setPhase(reviewResolved ? 'provisioning' : 'authenticating')
381
- }
382
-
383
- return
384
- }
385
-
386
- if (!currentStep) {
387
- return
388
- }
389
-
390
- if (key.escape || key.leftArrow) {
391
- if (safeStepIndex === 0) {
392
- props.controller.reject(new Error('Setup cancelled.'))
393
- return
394
- }
395
-
396
- setErrors(current => ({ ...current, [currentStep.id]: undefined }))
397
- setStepIndex(current => Math.max(current - 1, 0))
398
- return
399
- }
400
-
401
- if (currentStep.kind === 'select-environment' && (key.upArrow || key.downArrow)) {
402
- const direction = key.upArrow ? -1 : 1
403
- setValues((current) => {
404
- const currentIdx = ENVIRONMENT_PRESET_OPTIONS.findIndex(option => option.value === current.environment)
405
- const nextIdx = (ENVIRONMENT_PRESET_OPTIONS.length + currentIdx + direction) % ENVIRONMENT_PRESET_OPTIONS.length
406
- return {
407
- ...current,
408
- environment: ENVIRONMENT_PRESET_OPTIONS[nextIdx]?.value ?? current.environment,
409
- }
410
- })
411
- return
412
- }
413
-
414
- if (currentStep.kind === 'select-ssh-key' && (key.upArrow || key.downArrow)) {
415
- const direction = key.upArrow ? -1 : 1
416
- setValues((current) => {
417
- const currentIdx = sshKeyOptions.findIndex(option => option.key === current.sshKeySelection)
418
- const nextIdx = (sshKeyOptions.length + currentIdx + direction) % sshKeyOptions.length
419
- return {
420
- ...current,
421
- sshKeySelection: sshKeyOptions[nextIdx]?.key ?? current.sshKeySelection,
422
- }
423
- })
424
- return
425
- }
426
-
427
- if (currentStep.kind === 'text' && (key.backspace || key.delete)) {
428
- const fieldKey = currentStep.id as TextStepId
429
- setValues(current => ({
430
- ...current,
431
- [fieldKey]: current[fieldKey].slice(0, -1),
432
- }))
433
- setErrors(current => ({ ...current, [currentStep.id]: undefined }))
434
- return
435
- }
436
-
437
- if (key.return) {
438
- const validationError = validateStep(currentStep, values, props, selectedSshKey)
439
- if (validationError) {
440
- setErrors(current => ({ ...current, [currentStep.id]: validationError }))
441
- return
442
- }
443
-
444
- const nextIndex = safeStepIndex + 1
445
- const nextVisible = getVisibleEditSteps(values)
446
- if (nextIndex >= nextVisible.length) {
447
- setPhase('review')
448
- return
449
- }
450
-
451
- setStepIndex(nextIndex)
452
- return
453
- }
454
-
455
- if (currentStep.kind === 'text' && input) {
456
- const fieldKey = currentStep.id as TextStepId
457
- setValues(current => ({
458
- ...current,
459
- [fieldKey]: `${current[fieldKey]}${input}`,
460
- }))
461
- setErrors(current => ({ ...current, [currentStep.id]: undefined }))
462
- }
463
- })
464
-
465
- if (phase === 'authenticating' || phase === 'provisioning') {
466
- return (
467
- <RunningStep
468
- stage={runningStage}
469
- organizationName={selectedOrganization?.name}
470
- verificationUrl={verificationUrl}
471
- userCode={userCode}
472
- progressLines={progressLines}
473
- />
474
- )
475
- }
476
-
477
- if (phase === 'done' && result) {
478
- return <DoneStep result={result} />
479
- }
480
-
481
- if (phase === 'selectingOrganization' && authenticated) {
482
- return (
483
- <OrganizationStep
484
- auth={authenticated}
485
- organizations={organizationOptions}
486
- currentIndex={organizationIndex}
487
- preferredOrganizationId={preferredOrganizationId}
488
- />
489
- )
490
- }
491
-
492
- if (phase === 'review') {
493
- return (
494
- <ReviewStep
495
- values={values}
496
- resolvedConfig={resolvedConfig}
497
- auth={authenticated}
498
- selectedOrganization={selectedOrganization}
499
- selectedSshKey={selectedSshKey}
500
- cloneRootState={cloneRootState}
501
- createCloneRoot={createCloneRoot}
502
- />
503
- )
504
- }
505
-
506
- if (!currentStep) {
507
- return (
508
- <StepFrame
509
- stepNumber={1}
510
- totalSteps={1}
511
- stepLabel="Setup"
512
- question="Preparing setup…"
513
- breadcrumb={[]}
514
- footer="Esc cancel"
515
- >
516
- <></>
517
- </StepFrame>
518
- )
519
- }
520
-
521
- const footer = buildFooter(currentStep, safeStepIndex === 0)
522
- const error = errors[currentStep.id]
523
-
524
- return (
525
- <StepFrame
526
- stepNumber={safeStepIndex + 1}
527
- totalSteps={visibleSteps.length}
528
- stepLabel={currentStep.label}
529
- question={currentStep.question}
530
- breadcrumb={breadcrumb}
531
- footer={footer}
532
- >
533
- {renderStepBody(currentStep, values, error, sshKeyOptions, props.sshPublicKeys.length > 0)}
534
- </StepFrame>
535
- )
536
- }
537
-
538
- function renderStepBody(
539
- step: SetupStepDescriptor,
540
- values: SetupFormValues,
541
- error: string | undefined,
542
- sshKeyOptions: ReturnType<typeof buildSshKeyOptions>,
543
- hasDiscoveredKeys: boolean,
544
- ): React.ReactElement {
545
- if (step.kind === 'select-environment') {
546
- return <EnvironmentStep values={values} />
547
- }
548
-
549
- if (step.kind === 'select-ssh-key') {
550
- return (
551
- <SshKeyStep
552
- options={sshKeyOptions}
553
- selection={values.sshKeySelection}
554
- hasDiscoveredKeys={hasDiscoveredKeys}
555
- />
556
- )
557
- }
558
-
559
- const fieldKey = step.id as TextStepId
560
- return (
561
- <TextFieldStep
562
- value={values[fieldKey]}
563
- placeholder={step.placeholder}
564
- error={error}
565
- />
566
- )
567
- }
568
-
569
- function buildFooter(step: SetupStepDescriptor, isFirstStep: boolean): string {
570
- const back = isFirstStep ? 'Esc cancel' : 'Esc back'
571
- switch (step.kind) {
572
- case 'select-environment':
573
- return `↑/↓ choose · Enter continue · ${back}`
574
- case 'select-ssh-key':
575
- return `↑/↓ choose · Enter continue · ${back}`
576
- case 'text':
577
- return `Type to edit · Enter continue · ${back}`
578
- }
579
- }
580
-
581
- function validateStep(
582
- step: SetupStepDescriptor,
583
- values: SetupFormValues,
584
- props: SetupScreenProps,
585
- selectedSshKey: ReturnType<typeof loadSshPublicKey> | undefined,
586
- ): string | undefined {
587
- switch (step.id) {
588
- case 'environment':
589
- return undefined
590
- case 'agentApiUrl':
591
- return props.validateAgentApiUrl(values.agentApiUrl)
592
- case 'authApiUrl':
593
- return props.validateAuthApiUrl(values.authApiUrl)
594
- case 'authDashboardUrl':
595
- return props.validateAuthDashboardUrl(values.authDashboardUrl)
596
- case 'telaApiUrl':
597
- return props.validateTelaApiUrl(values.telaApiUrl)
598
- case 'requesterApplicationId':
599
- return props.validateApplicationId(values.requesterApplicationId, 'Requester application ID')
600
- case 'targetApplicationId':
601
- return props.validateApplicationId(values.targetApplicationId, 'Target application ID')
602
- case 'cloneRoot':
603
- return props.validateCloneRoot(values.cloneRoot)
604
- case 'sshKey':
605
- return selectedSshKey ? undefined : 'SSH public key is required'
606
- case 'sshKeyPath':
607
- return validateSshPublicKeyPath(values.sshKeyPath)
608
- case 'sshPrivateKeyPath':
609
- return validateSshPrivateKeyPath(values.sshPrivateKeyPath)
610
- }
611
- }
@@ -1,30 +0,0 @@
1
- import React, { type ReactNode } from 'react'
2
- import { AppFrame, Section } from '../../components.tsx'
3
- import { Breadcrumb, type BreadcrumbEntry } from './breadcrumb.tsx'
4
-
5
- export interface StepFrameProps {
6
- stepNumber: number
7
- totalSteps: number
8
- stepLabel: string
9
- question: string
10
- breadcrumb: BreadcrumbEntry[]
11
- footer?: string
12
- status?: string
13
- children: ReactNode
14
- }
15
-
16
- export function StepFrame(props: StepFrameProps): React.ReactElement {
17
- return (
18
- <AppFrame
19
- title="Setup"
20
- subtitle={`Step ${props.stepNumber} of ${props.totalSteps} — ${props.stepLabel}`}
21
- footer={props.footer}
22
- status={props.status}
23
- >
24
- <Breadcrumb entries={props.breadcrumb} />
25
- <Section title={props.question}>
26
- {props.children}
27
- </Section>
28
- </AppFrame>
29
- )
30
- }
@@ -1,28 +0,0 @@
1
- import React from 'react'
2
- import { Text } from 'ink'
3
- import { AppFrame, Section } from '../../../components.tsx'
4
- import type { SetupScreenResult } from '../types.ts'
5
-
6
- export function DoneStep(props: { result: SetupScreenResult }): React.ReactElement {
7
- return (
8
- <AppFrame
9
- title="Setup"
10
- subtitle="Setup complete."
11
- >
12
- <Section title="Authenticated">
13
- <Text>User: {props.result.config.auth?.user.email ?? '<missing>'}</Text>
14
- <Text>Workspace: {props.result.config.auth?.workspace.title ?? '<missing>'}</Text>
15
- <Text>Organization: {props.result.config.forgejoOrganization?.name ?? '<missing>'}</Text>
16
- <Text>Clone root: {props.result.config.cloneRoot}</Text>
17
- <Text>Forgejo user: {props.result.config.forgejoUserRegistered ? 'registered' : 'pending'}</Text>
18
- </Section>
19
- <Section title="Checks">
20
- {props.result.checks.map(check => (
21
- <Text key={check.label}>
22
- {check.label}: {check.status} — {check.detail}
23
- </Text>
24
- ))}
25
- </Section>
26
- </AppFrame>
27
- )
28
- }
@@ -1,22 +0,0 @@
1
- import React from 'react'
2
- import { ENVIRONMENT_PRESET_OPTIONS } from '../../../../config.ts'
3
- import { SelectList } from '../../../components.tsx'
4
- import type { SetupFormValues } from '../types.ts'
5
-
6
- export function EnvironmentStep(props: { values: SetupFormValues }): React.ReactElement {
7
- const currentIndex = Math.max(
8
- ENVIRONMENT_PRESET_OPTIONS.findIndex(option => option.value === props.values.environment),
9
- 0,
10
- )
11
-
12
- return (
13
- <SelectList
14
- items={ENVIRONMENT_PRESET_OPTIONS.map(option => ({
15
- key: option.value,
16
- label: option.label,
17
- hint: option.hint,
18
- }))}
19
- currentIndex={currentIndex}
20
- />
21
- )
22
- }