@meistrari/tela-cli 0.7.0 → 0.7.5
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/dist/output.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Writable } from 'node:stream';
|
|
2
|
+
export type CliIo = {
|
|
3
|
+
stdout: Pick<Writable, 'write'>;
|
|
4
|
+
stderr: Pick<Writable, 'write'>;
|
|
5
|
+
};
|
|
6
|
+
export type GlobalOptions = {
|
|
7
|
+
json: boolean;
|
|
8
|
+
verbose?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function writeJson(io: CliIo, value: unknown): void;
|
|
11
|
+
export declare function writeLine(io: CliIo, line?: string): void;
|
|
12
|
+
export declare function writeError(io: CliIo, message: string): void;
|
|
13
|
+
export declare function formatBool(value: boolean): string;
|
package/package.json
CHANGED
|
@@ -1,34 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meistrari/tela-cli",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.7.5",
|
|
4
|
+
"description": "Tela command line tools for local state and agent workflows.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/meistrari/tela-dock.git",
|
|
9
|
+
"directory": "apps/tela-cli"
|
|
10
|
+
},
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"types": "dist/index.d.ts",
|
|
5
13
|
"bin": {
|
|
6
|
-
"tela": "
|
|
14
|
+
"tela": "dist/index.js"
|
|
7
15
|
},
|
|
8
16
|
"files": [
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
17
|
+
"dist",
|
|
18
|
+
"scripts/install-skills.cjs",
|
|
19
|
+
"skills"
|
|
12
20
|
],
|
|
13
21
|
"scripts": {
|
|
14
|
-
"
|
|
15
|
-
"postinstall": "bash ./postinstall.sh",
|
|
16
|
-
"test": "bun test",
|
|
17
|
-
"typecheck": "bunx tsc --noEmit"
|
|
18
|
-
},
|
|
19
|
-
"dependencies": {
|
|
20
|
-
"@meistrari/auth-cli": "^1.0.1",
|
|
21
|
-
"better-auth": "^1.6.3",
|
|
22
|
-
"commander": "13.1.0",
|
|
23
|
-
"ink": "6.2.3",
|
|
24
|
-
"react": "19"
|
|
25
|
-
},
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"@types/react": "19",
|
|
28
|
-
"bun-types": "1.3.4",
|
|
29
|
-
"ink-testing-library": "4.0.0"
|
|
22
|
+
"postinstall": "node scripts/install-skills.cjs --quiet"
|
|
30
23
|
},
|
|
31
|
-
"
|
|
32
|
-
"
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
33
26
|
}
|
|
34
27
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const os = require('node:os')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
|
|
7
|
+
const skillName = 'tela-cli'
|
|
8
|
+
const metadataFile = '.tela-cli-skill.json'
|
|
9
|
+
const managedSource = '@meistrari/tela-cli'
|
|
10
|
+
const sourceDir = path.resolve(__dirname, '..', 'skills', skillName)
|
|
11
|
+
const quiet = process.argv.includes('--quiet')
|
|
12
|
+
const json = process.argv.includes('--json')
|
|
13
|
+
|
|
14
|
+
function homeDir() {
|
|
15
|
+
return process.env.HOME || os.homedir()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function targetRoots() {
|
|
19
|
+
return [
|
|
20
|
+
path.join(homeDir(), '.agents', 'skills'),
|
|
21
|
+
path.join(homeDir(), '.claude', 'skills'),
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function readJson(filePath) {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
28
|
+
} catch {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isManagedInstall(targetDir) {
|
|
34
|
+
const metadata = readJson(path.join(targetDir, metadataFile))
|
|
35
|
+
return metadata?.managedBy === managedSource && metadata?.skillName === skillName
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function copySkill(targetDir) {
|
|
39
|
+
const parentDir = path.dirname(targetDir)
|
|
40
|
+
fs.mkdirSync(parentDir, { recursive: true })
|
|
41
|
+
|
|
42
|
+
const tempDir = fs.mkdtempSync(path.join(parentDir, `.${skillName}-tmp-`))
|
|
43
|
+
const backupDir = path.join(parentDir, `.${skillName}-backup-${process.pid}-${Date.now()}`)
|
|
44
|
+
let movedExisting = false
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
fs.cpSync(sourceDir, tempDir, { recursive: true })
|
|
48
|
+
fs.writeFileSync(path.join(tempDir, metadataFile), `${JSON.stringify({
|
|
49
|
+
managedBy: managedSource,
|
|
50
|
+
skillName,
|
|
51
|
+
installedAt: new Date().toISOString(),
|
|
52
|
+
sourceDir,
|
|
53
|
+
}, null, 2)}\n`)
|
|
54
|
+
|
|
55
|
+
if (fs.existsSync(targetDir)) {
|
|
56
|
+
fs.renameSync(targetDir, backupDir)
|
|
57
|
+
movedExisting = true
|
|
58
|
+
}
|
|
59
|
+
fs.renameSync(tempDir, targetDir)
|
|
60
|
+
fs.rmSync(backupDir, { recursive: true, force: true })
|
|
61
|
+
} catch (error) {
|
|
62
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
63
|
+
if (movedExisting && !fs.existsSync(targetDir) && fs.existsSync(backupDir)) {
|
|
64
|
+
fs.renameSync(backupDir, targetDir)
|
|
65
|
+
}
|
|
66
|
+
throw error
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function install() {
|
|
71
|
+
if (process.env.TELA_CLI_SKIP_SKILL_INSTALL === '1') {
|
|
72
|
+
return {
|
|
73
|
+
ok: true,
|
|
74
|
+
skipped: true,
|
|
75
|
+
reason: 'TELA_CLI_SKIP_SKILL_INSTALL=1',
|
|
76
|
+
targets: [],
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!fs.existsSync(path.join(sourceDir, 'SKILL.md'))) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
error: `Bundled skill source is missing: ${sourceDir}`,
|
|
84
|
+
targets: [],
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const targets = targetRoots().map((root) => {
|
|
89
|
+
const targetDir = path.join(root, skillName)
|
|
90
|
+
const exists = fs.existsSync(targetDir)
|
|
91
|
+
|
|
92
|
+
if (exists && !isManagedInstall(targetDir)) {
|
|
93
|
+
return {
|
|
94
|
+
targetDir,
|
|
95
|
+
status: 'skipped',
|
|
96
|
+
reason: 'existing unmanaged skill folder',
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
copySkill(targetDir)
|
|
101
|
+
return {
|
|
102
|
+
targetDir,
|
|
103
|
+
status: exists ? 'updated' : 'installed',
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
ok: true,
|
|
109
|
+
skillName,
|
|
110
|
+
targets,
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const result = install()
|
|
115
|
+
|
|
116
|
+
if (json) {
|
|
117
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)
|
|
118
|
+
} else if (!quiet) {
|
|
119
|
+
for (const target of result.targets || []) {
|
|
120
|
+
process.stdout.write(`${target.status}: ${target.targetDir}${target.reason ? ` (${target.reason})` : ''}\n`)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!result.ok) {
|
|
125
|
+
if (!quiet && result.error) process.stderr.write(`${result.error}\n`)
|
|
126
|
+
process.exitCode = 1
|
|
127
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tela-cli
|
|
3
|
+
description: Use when a coding agent needs to operate Tela Agent repositories through the local `tela` CLI with non-interactive, flag-driven commands and JSON output.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tela CLI
|
|
7
|
+
|
|
8
|
+
Use the local `tela` binary for Tela Agent repository workflows. Prefer explicit
|
|
9
|
+
flags and `--json` so commands are safe in non-interactive agent sessions.
|
|
10
|
+
|
|
11
|
+
## Subpaths
|
|
12
|
+
|
|
13
|
+
- Agent workflows: read `references/agent.md` before running `tela agent ...`.
|
|
14
|
+
|
|
15
|
+
## Core Rules
|
|
16
|
+
|
|
17
|
+
- Use `tela agent ...` commands, not legacy top-level `sync`, `run`, `push`, or `feedback`.
|
|
18
|
+
- Prefer `--json` for machine-readable output and errors.
|
|
19
|
+
- Do not rely on prompts in coding-agent sessions; pass every required value by flag.
|
|
20
|
+
- Do not print token material. Use `tela auth token --json` for metadata and `--raw` only when a shell command explicitly needs the token.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Tela Agent CLI
|
|
2
|
+
|
|
3
|
+
Use these commands from a repository checkout unless the command says otherwise.
|
|
4
|
+
Add `--json` whenever you need structured output.
|
|
5
|
+
|
|
6
|
+
## Setup
|
|
7
|
+
|
|
8
|
+
Inspect valid setup choices:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
tela agent setup --list-options --context production/foundation --json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Validate setup without provisioning or writing config:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
tela agent setup \
|
|
18
|
+
--context production/foundation \
|
|
19
|
+
--organization acme \
|
|
20
|
+
--clone-root "$HOME/tela-agents" \
|
|
21
|
+
--ssh-public-key "$HOME/.ssh/id_ed25519.pub" \
|
|
22
|
+
--ssh-private-key "$HOME/.ssh/id_ed25519" \
|
|
23
|
+
--dry-run \
|
|
24
|
+
--json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Complete setup by removing `--dry-run`. This provisions the Forgejo user and
|
|
28
|
+
saves Agent setup metadata in the shared Tela local config.
|
|
29
|
+
|
|
30
|
+
## Sync
|
|
31
|
+
|
|
32
|
+
Preview repository sync:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
tela agent sync agent-api --dry-run --json
|
|
36
|
+
tela agent sync --current --dry-run --json
|
|
37
|
+
tela agent sync --all --dry-run --json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Run without `--dry-run` only after checking the plan. `--current` must be run
|
|
41
|
+
inside a git checkout. `--all` operates on the configured clone root.
|
|
42
|
+
|
|
43
|
+
## Run
|
|
44
|
+
|
|
45
|
+
Run one-shot, non-interactively:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
tela agent run \
|
|
49
|
+
--mode one-shot \
|
|
50
|
+
--input caderno_teses=./brief.docx \
|
|
51
|
+
--input-conflict fail \
|
|
52
|
+
--json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Input assignment format is `variable=path`, where `variable` is a first-level
|
|
56
|
+
folder under `input/`. Conflict modes:
|
|
57
|
+
|
|
58
|
+
- `fail`: default; exits before running Claude if the destination exists.
|
|
59
|
+
- `overwrite`: replaces the existing staged input.
|
|
60
|
+
- `rename`: uses the CLI suggested destination name.
|
|
61
|
+
|
|
62
|
+
Avoid `--mode chat` in coding-agent sessions because chat mode requires an
|
|
63
|
+
interactive terminal and cannot be combined with `--json`.
|
|
64
|
+
|
|
65
|
+
## Push
|
|
66
|
+
|
|
67
|
+
Preview commit and push:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
tela agent push --message "feat: update agent behavior" --dry-run --json
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Push after checking the dry run:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
tela agent push --message "feat: update agent behavior" --json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use `--message` for agent sessions. `--claude` asks the local Claude CLI to
|
|
80
|
+
write a message, and `--manual` relies on fallback behavior.
|
|
81
|
+
|
|
82
|
+
## Feedback
|
|
83
|
+
|
|
84
|
+
List feedback for the current commit:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
tela agent feedback --list --json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Apply all actionable feedback:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
tela agent feedback --apply all --json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Apply selected actionable feedback ids without prompts:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
tela agent feedback --apply select --feedback-id 1 --feedback-id 3 --json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Only use ids where the `feedbacks[].actionable` value from `--list --json` is
|
|
103
|
+
`true`.
|
|
104
|
+
|
|
105
|
+
## Guardrails
|
|
106
|
+
|
|
107
|
+
- Run `tela doctor auth --json` before agent workflows if credentials look stale.
|
|
108
|
+
- Use `tela context current --json` and `tela context list --json` to inspect the active context.
|
|
109
|
+
- Use `tela context use <context-id> --json` only when you intentionally want to mutate the active context.
|
|
110
|
+
- Treat non-JSON output as human display text; parse JSON only.
|
|
111
|
+
- Stop on `ok: false` or a non-zero exit code and inspect `error.code`, `error.summary`, and `error.fixes`.
|
package/cli.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import { renderUserFacingError, serializeError, toUserFacingError } from './src/errors.ts'
|
|
4
|
-
import { main } from './src/program.ts'
|
|
5
|
-
|
|
6
|
-
function hasFlag(argv: string[], flag: string): boolean {
|
|
7
|
-
return argv.includes(flag)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
try {
|
|
11
|
-
await main(process.argv)
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
if (hasFlag(process.argv, '--json')) {
|
|
15
|
-
process.stderr.write(`${JSON.stringify(serializeError(error), null, 2)}\n`)
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
process.stderr.write(`${renderUserFacingError(error, {
|
|
19
|
-
decorate: Boolean(process.stderr.isTTY),
|
|
20
|
-
verbose: hasFlag(process.argv, '--verbose'),
|
|
21
|
-
})}\n`)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
process.exitCode = toUserFacingError(error).exitCode
|
|
25
|
-
}
|
package/postinstall.sh
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
ZSHRC="$HOME/.zshrc"
|
|
5
|
-
PKG_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
-
ALIAS_LINE="alias tela='bun run $PKG_DIR/cli.ts'"
|
|
7
|
-
MARKER="# tela-cli"
|
|
8
|
-
|
|
9
|
-
if [ ! -f "$ZSHRC" ]; then
|
|
10
|
-
touch "$ZSHRC"
|
|
11
|
-
fi
|
|
12
|
-
|
|
13
|
-
# Remove any previous tela-cli entry
|
|
14
|
-
sed -i '' "/$MARKER/d" "$ZSHRC" 2>/dev/null || sed -i "/$MARKER/d" "$ZSHRC" 2>/dev/null || true
|
|
15
|
-
|
|
16
|
-
echo "$ALIAS_LINE $MARKER" >> "$ZSHRC"
|
|
17
|
-
|
|
18
|
-
echo "tela command added to $ZSHRC"
|
|
19
|
-
echo "Run 'source ~/.zshrc' or open a new terminal to use 'tela'"
|
package/src/agent-api.ts
DELETED
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
import { buildAgentApiHeaders, type FetchLike } from './auth.ts'
|
|
2
|
-
import type {
|
|
3
|
-
AgentRepoSummary,
|
|
4
|
-
AvailableRepositorySummary,
|
|
5
|
-
AgentFeedbackItem,
|
|
6
|
-
AgentFeedbackListResponse,
|
|
7
|
-
ForgejoUserCreateResponse,
|
|
8
|
-
ForgejoUserSshKeyInput,
|
|
9
|
-
OrganizationListResponse,
|
|
10
|
-
OrganizationSummary,
|
|
11
|
-
RepositoryListItemResponse,
|
|
12
|
-
RepositoryListResponse,
|
|
13
|
-
RepositoryLookupResponse,
|
|
14
|
-
TelaCliConfig,
|
|
15
|
-
TelaCliForgejoOrganization,
|
|
16
|
-
} from './types.ts'
|
|
17
|
-
|
|
18
|
-
const DEFAULT_TIMEOUT_MS = 10_000
|
|
19
|
-
|
|
20
|
-
function joinUrl(baseUrl: string, path: string): string {
|
|
21
|
-
return new URL(path, baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`).toString()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function parseJsonBody(responseText: string): unknown {
|
|
25
|
-
try {
|
|
26
|
-
return responseText ? JSON.parse(responseText) as unknown : null
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return null
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function isFeedbackItem(value: unknown): value is AgentFeedbackItem {
|
|
34
|
-
if (!value || typeof value !== 'object') {
|
|
35
|
-
return false
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const candidate = value as Partial<AgentFeedbackItem>
|
|
39
|
-
return (candidate.status === 'success' || candidate.status === 'failed')
|
|
40
|
-
&& typeof candidate.reason === 'string'
|
|
41
|
-
&& typeof candidate.suggestion === 'string'
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function isForgejoOrganization(value: unknown): value is TelaCliForgejoOrganization {
|
|
45
|
-
if (!value || typeof value !== 'object') {
|
|
46
|
-
return false
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const candidate = value as Partial<TelaCliForgejoOrganization>
|
|
50
|
-
return typeof candidate.id === 'string' && typeof candidate.name === 'string'
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function isOrganizationSummary(value: unknown): value is OrganizationSummary {
|
|
54
|
-
if (!value || typeof value !== 'object') {
|
|
55
|
-
return false
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const candidate = value as Partial<OrganizationSummary>
|
|
59
|
-
return typeof candidate.id === 'string'
|
|
60
|
-
&& typeof candidate.workspaceId === 'string'
|
|
61
|
-
&& typeof candidate.name === 'string'
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function isAgentRepoSummary(value: unknown): value is AgentRepoSummary {
|
|
65
|
-
if (!value || typeof value !== 'object') {
|
|
66
|
-
return false
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const candidate = value as Partial<AgentRepoSummary>
|
|
70
|
-
return typeof candidate.id === 'string'
|
|
71
|
-
&& typeof candidate.workspaceId === 'string'
|
|
72
|
-
&& typeof candidate.organizationId === 'string'
|
|
73
|
-
&& typeof candidate.repository === 'string'
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function isRepositoryListItemResponse(value: unknown): value is RepositoryListItemResponse {
|
|
77
|
-
if (!value || typeof value !== 'object') {
|
|
78
|
-
return false
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const candidate = value as Partial<RepositoryListItemResponse>
|
|
82
|
-
return isOrganizationSummary(candidate.organization)
|
|
83
|
-
&& isAgentRepoSummary(candidate.agentRepo)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export class AgentApiError extends Error {
|
|
87
|
-
constructor(
|
|
88
|
-
message: string,
|
|
89
|
-
public readonly status: number,
|
|
90
|
-
public readonly responseBody: string,
|
|
91
|
-
) {
|
|
92
|
-
super(message)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function fetchRepositoryLookup(
|
|
97
|
-
repository: string,
|
|
98
|
-
config: TelaCliConfig,
|
|
99
|
-
fetchFn: FetchLike = fetch,
|
|
100
|
-
): Promise<Required<Pick<RepositoryLookupResponse, 'organization' | 'agentRepo' | 'forgejoRepository'>> & { success: true }> {
|
|
101
|
-
const headers = await buildAgentApiHeaders(config, { fetchFn })
|
|
102
|
-
const response = await fetchFn(
|
|
103
|
-
joinUrl(config.agentApiUrl, `/v3/agents/repositories/${encodeURIComponent(repository)}`),
|
|
104
|
-
{
|
|
105
|
-
method: 'GET',
|
|
106
|
-
headers,
|
|
107
|
-
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
|
|
108
|
-
},
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
const responseText = await response.text()
|
|
112
|
-
const payload = parseJsonBody(responseText) as RepositoryLookupResponse | null
|
|
113
|
-
|
|
114
|
-
if (!response.ok) {
|
|
115
|
-
throw new AgentApiError(
|
|
116
|
-
payload?.error ?? `Agent API lookup failed with status ${response.status}`,
|
|
117
|
-
response.status,
|
|
118
|
-
responseText,
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (!payload?.success || !payload.organization || !payload.agentRepo || !payload.forgejoRepository) {
|
|
123
|
-
throw new AgentApiError('Agent API lookup returned an invalid payload', response.status, responseText)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
success: true,
|
|
128
|
-
organization: payload.organization,
|
|
129
|
-
agentRepo: payload.agentRepo,
|
|
130
|
-
forgejoRepository: payload.forgejoRepository,
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export async function fetchCommitFeedback(
|
|
135
|
-
organizationName: string,
|
|
136
|
-
repository: string,
|
|
137
|
-
commitHash: string,
|
|
138
|
-
config: TelaCliConfig,
|
|
139
|
-
fetchFn: FetchLike = fetch,
|
|
140
|
-
): Promise<AgentFeedbackListResponse> {
|
|
141
|
-
const headers = await buildAgentApiHeaders(config, { fetchFn })
|
|
142
|
-
const response = await fetchFn(
|
|
143
|
-
joinUrl(
|
|
144
|
-
config.agentApiUrl,
|
|
145
|
-
`/v3/agents/${encodeURIComponent(organizationName)}/${encodeURIComponent(repository)}/feedback?commitHash=${encodeURIComponent(commitHash)}`,
|
|
146
|
-
),
|
|
147
|
-
{
|
|
148
|
-
method: 'GET',
|
|
149
|
-
headers,
|
|
150
|
-
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
|
|
151
|
-
},
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
const responseText = await response.text()
|
|
155
|
-
const payload = parseJsonBody(responseText) as Partial<AgentFeedbackListResponse> & { error?: string } | null
|
|
156
|
-
|
|
157
|
-
if (!response.ok) {
|
|
158
|
-
throw new AgentApiError(
|
|
159
|
-
payload?.error ?? `Agent API feedback lookup failed with status ${response.status}`,
|
|
160
|
-
response.status,
|
|
161
|
-
responseText,
|
|
162
|
-
)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (!payload
|
|
166
|
-
|| typeof payload.commitHash !== 'string'
|
|
167
|
-
|| payload.commitHash !== commitHash
|
|
168
|
-
|| !Array.isArray(payload.feedbacks)
|
|
169
|
-
|| !payload.feedbacks.every(isFeedbackItem)) {
|
|
170
|
-
throw new AgentApiError('Agent API feedback lookup returned an invalid payload', response.status, responseText)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
commitHash: payload.commitHash,
|
|
175
|
-
feedbacks: payload.feedbacks,
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export async function listOrganizations(
|
|
180
|
-
config: TelaCliConfig,
|
|
181
|
-
fetchFn: FetchLike = fetch,
|
|
182
|
-
): Promise<TelaCliForgejoOrganization[]> {
|
|
183
|
-
const headers = await buildAgentApiHeaders(config, { fetchFn })
|
|
184
|
-
const response = await fetchFn(
|
|
185
|
-
joinUrl(config.agentApiUrl, '/v3/organizations'),
|
|
186
|
-
{
|
|
187
|
-
method: 'GET',
|
|
188
|
-
headers,
|
|
189
|
-
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
|
|
190
|
-
},
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
const responseText = await response.text()
|
|
194
|
-
const payload = parseJsonBody(responseText) as OrganizationListResponse | null
|
|
195
|
-
|
|
196
|
-
if (!response.ok) {
|
|
197
|
-
throw new AgentApiError(
|
|
198
|
-
payload?.error ?? `Agent API organization lookup failed with status ${response.status}`,
|
|
199
|
-
response.status,
|
|
200
|
-
responseText,
|
|
201
|
-
)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (!payload
|
|
205
|
-
|| payload.success !== true
|
|
206
|
-
|| !Array.isArray(payload.organizations)
|
|
207
|
-
|| !payload.organizations.every(isForgejoOrganization)) {
|
|
208
|
-
throw new AgentApiError('Agent API organization lookup returned an invalid payload', response.status, responseText)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return payload.organizations
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export async function listAvailableRepositories(
|
|
215
|
-
config: TelaCliConfig,
|
|
216
|
-
fetchFn: FetchLike = fetch,
|
|
217
|
-
): Promise<AvailableRepositorySummary[]> {
|
|
218
|
-
const headers = await buildAgentApiHeaders(config, { fetchFn })
|
|
219
|
-
const response = await fetchFn(
|
|
220
|
-
joinUrl(config.agentApiUrl, '/v3/agents/repositories'),
|
|
221
|
-
{
|
|
222
|
-
method: 'GET',
|
|
223
|
-
headers,
|
|
224
|
-
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
|
|
225
|
-
},
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
const responseText = await response.text()
|
|
229
|
-
const payload = parseJsonBody(responseText) as RepositoryListResponse | null
|
|
230
|
-
|
|
231
|
-
if (!response.ok) {
|
|
232
|
-
throw new AgentApiError(
|
|
233
|
-
payload?.error ?? `Agent API repository listing failed with status ${response.status}`,
|
|
234
|
-
response.status,
|
|
235
|
-
responseText,
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (!payload
|
|
240
|
-
|| payload.success !== true
|
|
241
|
-
|| !Array.isArray(payload.repositories)
|
|
242
|
-
|| !payload.repositories.every(isRepositoryListItemResponse)) {
|
|
243
|
-
throw new AgentApiError('Agent API repository listing returned an invalid payload', response.status, responseText)
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return payload.repositories.map(repository => ({
|
|
247
|
-
...repository,
|
|
248
|
-
syncStatus: 'available' as const,
|
|
249
|
-
}))
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export async function provisionForgejoUser(
|
|
253
|
-
input: {
|
|
254
|
-
organizationName: string
|
|
255
|
-
sshKey: ForgejoUserSshKeyInput
|
|
256
|
-
},
|
|
257
|
-
config: TelaCliConfig,
|
|
258
|
-
fetchFn: FetchLike = fetch,
|
|
259
|
-
): Promise<ForgejoUserCreateResponse> {
|
|
260
|
-
const headers = await buildAgentApiHeaders(config, { fetchFn })
|
|
261
|
-
const response = await fetchFn(
|
|
262
|
-
joinUrl(config.agentApiUrl, '/v3/forgejo/users'),
|
|
263
|
-
{
|
|
264
|
-
method: 'POST',
|
|
265
|
-
headers: {
|
|
266
|
-
...headers,
|
|
267
|
-
'Content-Type': 'application/json',
|
|
268
|
-
},
|
|
269
|
-
body: JSON.stringify({
|
|
270
|
-
organizationName: input.organizationName,
|
|
271
|
-
sshKey: input.sshKey,
|
|
272
|
-
}),
|
|
273
|
-
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
|
|
274
|
-
},
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
const responseText = await response.text()
|
|
278
|
-
const payload = parseJsonBody(responseText) as ForgejoUserCreateResponse | null
|
|
279
|
-
|
|
280
|
-
if (!response.ok) {
|
|
281
|
-
throw new AgentApiError(
|
|
282
|
-
payload?.error ?? `Agent API Forgejo user provisioning failed with status ${response.status}`,
|
|
283
|
-
response.status,
|
|
284
|
-
responseText,
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (!payload
|
|
289
|
-
|| payload.success !== true
|
|
290
|
-
|| typeof payload.username !== 'string'
|
|
291
|
-
|| typeof payload.organizationName !== 'string'
|
|
292
|
-
|| typeof payload.teamName !== 'string'
|
|
293
|
-
|| typeof payload.userCreated !== 'boolean'
|
|
294
|
-
|| typeof payload.teamCreated !== 'boolean'
|
|
295
|
-
|| typeof payload.sshKeyCreated !== 'boolean') {
|
|
296
|
-
throw new AgentApiError('Agent API Forgejo user provisioning returned an invalid payload', response.status, responseText)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return payload
|
|
300
|
-
}
|
package/src/assets.d.ts
DELETED