@meistrari/tela-cli 0.7.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.
- package/README.md +56 -106
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/context.d.ts +2 -0
- package/dist/commands/doctor-auth.d.ts +2 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/credentials.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7027 -0
- package/dist/output.d.ts +13 -0
- package/dist/program.d.ts +2 -0
- package/package.json +17 -24
- package/scripts/install-skills.cjs +127 -0
- package/skills/tela-cli/SKILL.md +20 -0
- package/skills/tela-cli/references/agent.md +111 -0
- package/cli.ts +0 -25
- package/postinstall.sh +0 -19
- package/src/agent-api.ts +0 -300
- package/src/assets.d.ts +0 -4
- package/src/auth.ts +0 -432
- package/src/claude.ts +0 -101
- package/src/commands/doctor.ts +0 -35
- package/src/commands/feedback.ts +0 -701
- package/src/commands/metadata.ts +0 -242
- package/src/commands/push.ts +0 -208
- package/src/commands/run.ts +0 -1699
- package/src/commands/status.ts +0 -109
- package/src/commands/sync.ts +0 -245
- package/src/commit-message.ts +0 -69
- package/src/config.ts +0 -799
- package/src/diagnostics.ts +0 -813
- package/src/errors.ts +0 -122
- package/src/forgejo.ts +0 -233
- package/src/git.ts +0 -457
- package/src/ink/assets/160092__jorickhoofd__dog-bark-1.mp3 +0 -0
- package/src/ink/bark.tsx +0 -241
- package/src/ink/components.tsx +0 -178
- package/src/ink/runtime.tsx +0 -90
- package/src/ink/screens/feedback-screen.tsx +0 -395
- package/src/ink/screens/push-screen.tsx +0 -232
- package/src/ink/screens/run-screen.tsx +0 -292
- package/src/ink/screens/setup/breadcrumb.tsx +0 -24
- package/src/ink/screens/setup/setup-screen.tsx +0 -612
- package/src/ink/screens/setup/step-frame.tsx +0 -31
- package/src/ink/screens/setup/steps/done-step.tsx +0 -29
- package/src/ink/screens/setup/steps/environment-step.tsx +0 -23
- package/src/ink/screens/setup/steps/organization-step.tsx +0 -35
- package/src/ink/screens/setup/steps/review-step.tsx +0 -78
- package/src/ink/screens/setup/steps/running-step.tsx +0 -37
- package/src/ink/screens/setup/steps/ssh-key-step.tsx +0 -48
- package/src/ink/screens/setup/steps/text-field-step.tsx +0 -18
- package/src/ink/screens/setup/steps.ts +0 -156
- package/src/ink/screens/setup/types.ts +0 -65
- package/src/ink/screens/sync-screen.tsx +0 -283
- package/src/process.ts +0 -251
- package/src/program.ts +0 -2105
- package/src/runtime.ts +0 -147
- package/src/types.ts +0 -165
- package/src/ui.ts +0 -295
- package/src/version.ts +0 -3
package/src/errors.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { CommandError } from './process.ts'
|
|
2
|
-
import { errorCard, type RenderOptions } from './ui.ts'
|
|
3
|
-
|
|
4
|
-
export interface UserFacingErrorInput {
|
|
5
|
-
code: string
|
|
6
|
-
summary: string
|
|
7
|
-
details?: string
|
|
8
|
-
fixes?: string[]
|
|
9
|
-
evidence?: string[]
|
|
10
|
-
exitCode?: number
|
|
11
|
-
cause?: unknown
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class UserFacingError extends Error {
|
|
15
|
-
public readonly code: string
|
|
16
|
-
public readonly summary: string
|
|
17
|
-
public readonly details?: string
|
|
18
|
-
public readonly fixes: string[]
|
|
19
|
-
public readonly evidence: string[]
|
|
20
|
-
public readonly exitCode: number
|
|
21
|
-
public readonly cause?: unknown
|
|
22
|
-
|
|
23
|
-
constructor(input: UserFacingErrorInput) {
|
|
24
|
-
super(input.summary)
|
|
25
|
-
this.name = 'UserFacingError'
|
|
26
|
-
this.code = input.code
|
|
27
|
-
this.summary = input.summary
|
|
28
|
-
this.details = input.details
|
|
29
|
-
this.fixes = input.fixes ?? []
|
|
30
|
-
this.evidence = input.evidence ?? []
|
|
31
|
-
this.exitCode = input.exitCode ?? 1
|
|
32
|
-
this.cause = input.cause
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function createUserFacingError(input: UserFacingErrorInput): UserFacingError {
|
|
37
|
-
return new UserFacingError(input)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function isUserFacingError(error: unknown): error is UserFacingError {
|
|
41
|
-
return error instanceof UserFacingError
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function toUserFacingError(error: unknown): UserFacingError {
|
|
45
|
-
if (isUserFacingError(error)) {
|
|
46
|
-
return error
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (error instanceof CommandError) {
|
|
50
|
-
const stdout = error.result.stdout.trim()
|
|
51
|
-
const stderr = error.result.stderr.trim()
|
|
52
|
-
const evidence = [
|
|
53
|
-
`Command: ${error.result.command.join(' ')}`,
|
|
54
|
-
stdout ? `stdout: ${stdout}` : '',
|
|
55
|
-
stderr ? `stderr: ${stderr}` : '',
|
|
56
|
-
].filter(Boolean)
|
|
57
|
-
|
|
58
|
-
return new UserFacingError({
|
|
59
|
-
code: 'command_failed',
|
|
60
|
-
summary: 'A local command failed.',
|
|
61
|
-
details: error.message,
|
|
62
|
-
evidence,
|
|
63
|
-
exitCode: 1,
|
|
64
|
-
cause: error,
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (error instanceof Error) {
|
|
69
|
-
return new UserFacingError({
|
|
70
|
-
code: 'unexpected_error',
|
|
71
|
-
summary: error.message,
|
|
72
|
-
exitCode: 1,
|
|
73
|
-
cause: error,
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return new UserFacingError({
|
|
78
|
-
code: 'unexpected_error',
|
|
79
|
-
summary: String(error),
|
|
80
|
-
exitCode: 1,
|
|
81
|
-
cause: error,
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function renderUserFacingError(
|
|
86
|
-
error: unknown,
|
|
87
|
-
options: RenderOptions & { verbose?: boolean } = {},
|
|
88
|
-
): string {
|
|
89
|
-
const userError = toUserFacingError(error)
|
|
90
|
-
const evidence = options.verbose ? userError.evidence : []
|
|
91
|
-
|
|
92
|
-
return errorCard({
|
|
93
|
-
summary: userError.summary,
|
|
94
|
-
details: userError.details,
|
|
95
|
-
fixes: userError.fixes,
|
|
96
|
-
evidence,
|
|
97
|
-
}, options)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function serializeError(error: unknown): {
|
|
101
|
-
ok: false
|
|
102
|
-
error: {
|
|
103
|
-
code: string
|
|
104
|
-
summary: string
|
|
105
|
-
details?: string
|
|
106
|
-
fixes: string[]
|
|
107
|
-
evidence: string[]
|
|
108
|
-
}
|
|
109
|
-
} {
|
|
110
|
-
const userError = toUserFacingError(error)
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
ok: false,
|
|
114
|
-
error: {
|
|
115
|
-
code: userError.code,
|
|
116
|
-
summary: userError.summary,
|
|
117
|
-
details: userError.details,
|
|
118
|
-
fixes: userError.fixes,
|
|
119
|
-
evidence: userError.evidence,
|
|
120
|
-
},
|
|
121
|
-
}
|
|
122
|
-
}
|
package/src/forgejo.ts
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
|
|
2
|
-
import { homedir } from 'node:os'
|
|
3
|
-
import { basename, extname, join } from 'node:path'
|
|
4
|
-
import { provisionForgejoUser, type AgentApiError } from './agent-api.ts'
|
|
5
|
-
import type { FetchLike } from './auth.ts'
|
|
6
|
-
import { createUserFacingError } from './errors.ts'
|
|
7
|
-
import type {
|
|
8
|
-
ForgejoUserCreateResponse,
|
|
9
|
-
ForgejoUserSshKeyInput,
|
|
10
|
-
TelaCliConfig,
|
|
11
|
-
TelaCliGitSshKey,
|
|
12
|
-
TelaCliForgejoOrganization,
|
|
13
|
-
} from './types.ts'
|
|
14
|
-
|
|
15
|
-
export interface SelectedSshPublicKey extends ForgejoUserSshKeyInput {
|
|
16
|
-
path: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface EnsureForgejoUserAccessOptions {
|
|
20
|
-
config: TelaCliConfig
|
|
21
|
-
organization: TelaCliForgejoOrganization
|
|
22
|
-
sshKey: SelectedSshPublicKey
|
|
23
|
-
fetchFn?: FetchLike
|
|
24
|
-
onProgress?: (message: string, title?: string) => void
|
|
25
|
-
provisionFn?: typeof provisionForgejoUser
|
|
26
|
-
saveConfig: (config: TelaCliConfig) => void
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface ForgejoAccessResult {
|
|
30
|
-
config: TelaCliConfig
|
|
31
|
-
detail: string
|
|
32
|
-
response: ForgejoUserCreateResponse
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function getHomeDir(): string {
|
|
36
|
-
return process.env.HOME || homedir()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function expandHome(input: string): string {
|
|
40
|
-
if (input === '~') {
|
|
41
|
-
return getHomeDir()
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (input.startsWith('~/')) {
|
|
45
|
-
return join(getHomeDir(), input.slice(2))
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return input
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function getSshDirectory(): string {
|
|
52
|
-
return join(getHomeDir(), '.ssh')
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function sshKeyTitleFromPath(filePath: string): string {
|
|
56
|
-
const base = basename(filePath)
|
|
57
|
-
return extname(base) === '.pub'
|
|
58
|
-
? base.slice(0, -4)
|
|
59
|
-
: base
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function loadSshPublicKey(filePath: string): SelectedSshPublicKey {
|
|
63
|
-
const expandedPath = expandHome(filePath.trim())
|
|
64
|
-
if (!expandedPath) {
|
|
65
|
-
throw new Error('SSH public key path is required')
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!existsSync(expandedPath)) {
|
|
69
|
-
throw new Error(`SSH public key file does not exist: ${expandedPath}`)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const stats = statSync(expandedPath)
|
|
73
|
-
if (!stats.isFile()) {
|
|
74
|
-
throw new Error(`SSH public key path is not a file: ${expandedPath}`)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const key = readFileSync(expandedPath, 'utf-8').trim()
|
|
78
|
-
if (!key) {
|
|
79
|
-
throw new Error(`SSH public key file is empty: ${expandedPath}`)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
path: expandedPath,
|
|
84
|
-
title: sshKeyTitleFromPath(expandedPath),
|
|
85
|
-
key,
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function validateSshPublicKeyPath(value: string): string | undefined {
|
|
90
|
-
try {
|
|
91
|
-
loadSshPublicKey(value)
|
|
92
|
-
return undefined
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
return error instanceof Error ? error.message : 'SSH public key path is invalid'
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function inferSshPrivateKeyPath(publicKeyPath: string): string {
|
|
100
|
-
const expandedPath = expandHome(publicKeyPath.trim())
|
|
101
|
-
return expandedPath.endsWith('.pub')
|
|
102
|
-
? expandedPath.slice(0, -4)
|
|
103
|
-
: expandedPath
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function resolveSshPrivateKeyPath(filePath: string): string {
|
|
107
|
-
const expandedPath = expandHome(filePath.trim())
|
|
108
|
-
if (!expandedPath) {
|
|
109
|
-
throw new Error('SSH private key path is required')
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (!existsSync(expandedPath)) {
|
|
113
|
-
throw new Error(`SSH private key file does not exist: ${expandedPath}`)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const stats = statSync(expandedPath)
|
|
117
|
-
if (!stats.isFile()) {
|
|
118
|
-
throw new Error(`SSH private key path is not a file: ${expandedPath}`)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return expandedPath
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function validateSshPrivateKeyPath(value: string): string | undefined {
|
|
125
|
-
try {
|
|
126
|
-
resolveSshPrivateKeyPath(value)
|
|
127
|
-
return undefined
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
return error instanceof Error ? error.message : 'SSH private key path is invalid'
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export function createGitSshKey(sshKey: SelectedSshPublicKey, privateKeyPath: string): TelaCliGitSshKey {
|
|
135
|
-
return {
|
|
136
|
-
title: sshKey.title,
|
|
137
|
-
publicKeyPath: sshKey.path,
|
|
138
|
-
privateKeyPath: resolveSshPrivateKeyPath(privateKeyPath),
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export function listDiscoveredSshPublicKeys(): SelectedSshPublicKey[] {
|
|
143
|
-
const sshDirectory = getSshDirectory()
|
|
144
|
-
if (!existsSync(sshDirectory)) {
|
|
145
|
-
return []
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return readdirSync(sshDirectory, { withFileTypes: true })
|
|
149
|
-
.filter(entry => entry.isFile() && entry.name.endsWith('.pub'))
|
|
150
|
-
.map(entry => loadSshPublicKey(join(sshDirectory, entry.name)))
|
|
151
|
-
.sort((left, right) => left.title.localeCompare(right.title) || left.path.localeCompare(right.path))
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function buildProvisioningDetail(result: ForgejoUserCreateResponse, sshKey: SelectedSshPublicKey): string {
|
|
155
|
-
const userState = result.userCreated ? 'user created' : 'user already existed'
|
|
156
|
-
const keyState = result.sshKeyCreated ? 'SSH key added' : 'SSH key already present'
|
|
157
|
-
const teamState = result.teamCreated ? 'team created' : 'team ensured'
|
|
158
|
-
const username = result.username ?? '<unknown>'
|
|
159
|
-
const teamName = result.teamName ?? '<unknown>'
|
|
160
|
-
|
|
161
|
-
return `${username} in ${teamName}; ${userState}, ${teamState}, ${keyState} (${sshKey.title})`
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function toProvisioningError(error: unknown): never {
|
|
165
|
-
const agentApiError = error as AgentApiError
|
|
166
|
-
if (agentApiError && typeof agentApiError.status === 'number' && typeof agentApiError.responseBody === 'string') {
|
|
167
|
-
throw createUserFacingError({
|
|
168
|
-
code: 'forgejo_user_provision_failed',
|
|
169
|
-
summary: 'Forgejo user provisioning failed.',
|
|
170
|
-
details: agentApiError.message,
|
|
171
|
-
fixes: [
|
|
172
|
-
'Run `tela setup` again to refresh the Tela session and retry SSH-key provisioning.',
|
|
173
|
-
'Use `--verbose` if you need the full Agent API response details.',
|
|
174
|
-
],
|
|
175
|
-
evidence: [
|
|
176
|
-
`Agent API status: ${agentApiError.status}`,
|
|
177
|
-
agentApiError.responseBody ? `Response body: ${agentApiError.responseBody}` : '',
|
|
178
|
-
].filter(Boolean),
|
|
179
|
-
cause: error,
|
|
180
|
-
})
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
throw createUserFacingError({
|
|
184
|
-
code: 'forgejo_user_provision_failed',
|
|
185
|
-
summary: 'Forgejo user provisioning failed.',
|
|
186
|
-
details: error instanceof Error ? error.message : String(error),
|
|
187
|
-
fixes: [
|
|
188
|
-
'Run `tela setup` again to refresh the Tela session and retry SSH-key provisioning.',
|
|
189
|
-
],
|
|
190
|
-
cause: error,
|
|
191
|
-
})
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export async function ensureForgejoUserAccess(options: EnsureForgejoUserAccessOptions): Promise<ForgejoAccessResult> {
|
|
195
|
-
const provisionFn = options.provisionFn ?? provisionForgejoUser
|
|
196
|
-
const baseConfig: TelaCliConfig = {
|
|
197
|
-
...options.config,
|
|
198
|
-
forgejoOrganization: options.organization,
|
|
199
|
-
forgejoUserRegistered: false,
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
options.saveConfig(baseConfig)
|
|
203
|
-
options.onProgress?.(
|
|
204
|
-
`Provisioning Forgejo access for ${options.organization.name} with ${options.sshKey.title}.`,
|
|
205
|
-
'Forgejo',
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
let response: ForgejoUserCreateResponse
|
|
209
|
-
try {
|
|
210
|
-
response = await provisionFn({
|
|
211
|
-
organizationName: options.organization.name,
|
|
212
|
-
sshKey: options.sshKey,
|
|
213
|
-
}, baseConfig, options.fetchFn)
|
|
214
|
-
}
|
|
215
|
-
catch (error) {
|
|
216
|
-
toProvisioningError(error)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const finalConfig: TelaCliConfig = {
|
|
220
|
-
...baseConfig,
|
|
221
|
-
forgejoUserRegistered: true,
|
|
222
|
-
}
|
|
223
|
-
options.saveConfig(finalConfig)
|
|
224
|
-
|
|
225
|
-
const detail = buildProvisioningDetail(response, options.sshKey)
|
|
226
|
-
options.onProgress?.(`Forgejo access ready: ${detail}`, 'Forgejo')
|
|
227
|
-
|
|
228
|
-
return {
|
|
229
|
-
config: finalConfig,
|
|
230
|
-
detail,
|
|
231
|
-
response,
|
|
232
|
-
}
|
|
233
|
-
}
|