@aiaiai-pt/frankctl 0.4.0
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/CHANGELOG.md +12 -0
- package/README.md +73 -0
- package/dist/index.js +347 -0
- package/package.json +65 -0
- package/skills/frank-cli/SKILL.md +323 -0
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aiaiai-pt/frankctl",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Terminal-first client for the Frank Low-Code Pipeline platform",
|
|
5
|
+
"homepage": "https://frank-docs-4k3.pages.dev",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/westeuropeco/frank-low-code.git",
|
|
9
|
+
"directory": "frank-cli"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/westeuropeco/frank-low-code/issues"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "aiaiai-pt",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"frank",
|
|
18
|
+
"frankctl",
|
|
19
|
+
"cli",
|
|
20
|
+
"elt",
|
|
21
|
+
"iceberg",
|
|
22
|
+
"data-pipeline"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"bin": {
|
|
26
|
+
"frankctl": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"CHANGELOG.md",
|
|
35
|
+
"skills/frank-cli/SKILL.md"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "node scripts/build.mjs",
|
|
42
|
+
"dev": "node --import tsx src/index.ts",
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"test:watch": "vitest",
|
|
45
|
+
"test:e2e": "FRANK_E2E=1 vitest run --config vitest-e2e.config.ts",
|
|
46
|
+
"coverage": "vitest run --coverage",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"prepack": "node scripts/build.mjs && mkdir -p skills/frank-cli && cp ../skills/frank-cli/SKILL.md skills/frank-cli/SKILL.md"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"chalk": "^5.4.1",
|
|
52
|
+
"commander": "^12.1.0",
|
|
53
|
+
"ora": "^8.2.0",
|
|
54
|
+
"yaml": "^2.8.0",
|
|
55
|
+
"zod": "^4.3.6"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/node": "^22.0.0",
|
|
59
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
60
|
+
"esbuild": "^0.24.0",
|
|
61
|
+
"tsx": "^4.19.0",
|
|
62
|
+
"typescript": "~5.7.0",
|
|
63
|
+
"vitest": "^2.1.0"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frank-cli
|
|
3
|
+
description: Use the frankctl CLI to interact with the Frank API — auth, config, pipelines, sources, transforms, runs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# frankctl (Frank CLI) — Phase 1
|
|
7
|
+
|
|
8
|
+
## 1. Overview
|
|
9
|
+
|
|
10
|
+
`frankctl` is a TypeScript CLI that gives humans and agents scriptable access to the Frank API: authentication against Keycloak, config/profile management, and the core data-flow surface — pipelines, sources, transforms, and runs. Version: `0.2.0`.
|
|
11
|
+
|
|
12
|
+
## 2. When to use
|
|
13
|
+
|
|
14
|
+
- Before activating a pipeline: `frankctl pipelines validate <id>` to run it end-to-end in sandbox mode, stream step badges to stderr, emit a final JSON summary on stdout.
|
|
15
|
+
- Scripting against Frank from bash/CI: `$(frankctl auth token)` injects a fresh access token into any `curl` call.
|
|
16
|
+
- Agents composing pipelines via Martha can self-check their work without going through the UI.
|
|
17
|
+
|
|
18
|
+
## 3. Installation & binary location
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd /home/ork/harnasso/frank/frank-cli
|
|
22
|
+
npm install
|
|
23
|
+
npm run build
|
|
24
|
+
npm link # installs `frankctl` on PATH
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The built entry point is `dist/index.js` (ESM, esbuild-bundled, `#!/usr/bin/env node` shebang). Node ≥18 required.
|
|
28
|
+
|
|
29
|
+
## 4. First-run setup
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
frankctl status # shows defaults, unauthenticated
|
|
33
|
+
frankctl auth login # browser PKCE flow (default)
|
|
34
|
+
frankctl auth status # confirms subject + tenant
|
|
35
|
+
frankctl pipelines list
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Credentials are written to `~/.frankctl/credentials.json` (0o600).
|
|
39
|
+
|
|
40
|
+
## 5. Global options
|
|
41
|
+
|
|
42
|
+
Available on every subcommand:
|
|
43
|
+
|
|
44
|
+
| Flag | Purpose |
|
|
45
|
+
|------|---------|
|
|
46
|
+
| `-p, --profile <name>` | Select config profile (default: `default`) |
|
|
47
|
+
| `--json` | Emit machine-readable JSON to stdout |
|
|
48
|
+
| `-v, --verbose` | Verbose logging (stderr) |
|
|
49
|
+
| `-q, --quiet` | Suppress non-error output |
|
|
50
|
+
| `--no-color` | Disable ANSI colors |
|
|
51
|
+
| `--api-url <url>` | Override API base URL for this invocation |
|
|
52
|
+
|
|
53
|
+
## 6. Environment variables
|
|
54
|
+
|
|
55
|
+
| Var | Effect |
|
|
56
|
+
|-----|--------|
|
|
57
|
+
| `FRANKCTL_API_URL` | Override `apiUrl` |
|
|
58
|
+
| `FRANKCTL_KEYCLOAK_URL` | Override `keycloakUrl` |
|
|
59
|
+
| `FRANKCTL_KEYCLOAK_REALM` | Override `realm` |
|
|
60
|
+
| `FRANKCTL_CLIENT_ID` | Override `clientId` |
|
|
61
|
+
| `FRANKCTL_CLIENT_SECRET` | Service-account secret |
|
|
62
|
+
| `FRANKCTL_PROFILE` | Active profile |
|
|
63
|
+
| `FRANKCTL_TOKEN` | Bypass token store; use this raw JWT |
|
|
64
|
+
| `FRANK_DEV_MODE=true` + `FRANKCTL_TENANT_ID` | Forward `X-Tenant-ID` header |
|
|
65
|
+
|
|
66
|
+
## 7. Profiles & `~/.frankctl/config.yaml`
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
defaultProfile: default
|
|
70
|
+
profiles:
|
|
71
|
+
default:
|
|
72
|
+
apiUrl: http://localhost:8000
|
|
73
|
+
keycloakUrl: http://localhost:8180
|
|
74
|
+
realm: frank
|
|
75
|
+
clientId: frank-low-code
|
|
76
|
+
prod:
|
|
77
|
+
apiUrl: https://frank.prod.example
|
|
78
|
+
keycloakUrl: https://kc.prod.example
|
|
79
|
+
realm: frank
|
|
80
|
+
clientId: frank-low-code
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Precedence (top wins): CLI flag → env → YAML → built-in default.
|
|
84
|
+
|
|
85
|
+
## 8. Auth flows
|
|
86
|
+
|
|
87
|
+
- **PKCE (default)** — browser-based, best for humans. Requires a free port in `[5173, 4173, 5174]` (matches the `frank-low-code` Keycloak client's registered redirect URIs).
|
|
88
|
+
- **Headless (ROPC)** — `frankctl auth login --headless --username ... --password ...`. Uses `frank-low-code` public client.
|
|
89
|
+
- **Service account** — `frankctl auth login --service-account --client-id frank-api --client-secret $S`. Uses `client_credentials` grant.
|
|
90
|
+
|
|
91
|
+
## 9. Token handling
|
|
92
|
+
|
|
93
|
+
- Tokens refresh automatically 30s before expiry.
|
|
94
|
+
- Service-account tokens re-acquire via `client_credentials` when `FRANKCTL_CLIENT_SECRET` is set.
|
|
95
|
+
- Agents should use `TOKEN=$(frankctl auth token)` rather than reading the credentials file.
|
|
96
|
+
|
|
97
|
+
## 10. `frankctl status`
|
|
98
|
+
|
|
99
|
+
Prints version, active profile, URLs, and authentication state. `--json` mirrors all fields.
|
|
100
|
+
|
|
101
|
+
## 11. `frankctl auth`
|
|
102
|
+
|
|
103
|
+
- `login [--headless | --service-account] [--username --password --client-id --client-secret --no-browser]`
|
|
104
|
+
- `logout` — deletes credentials for active profile.
|
|
105
|
+
- `status` — exits 3 if not logged in.
|
|
106
|
+
- `token` — prints a valid access token to stdout.
|
|
107
|
+
|
|
108
|
+
## 12. `frankctl config`
|
|
109
|
+
|
|
110
|
+
- `config set <key> <value>` — dotted key (e.g. `profiles.default.apiUrl`).
|
|
111
|
+
- `config get <key>` — returns `{value, source}` where source ∈ `flag | env | config | default`.
|
|
112
|
+
- `config list` — merged effective profile.
|
|
113
|
+
|
|
114
|
+
## 13. `frankctl pipelines list`
|
|
115
|
+
|
|
116
|
+
`GET /api/v1/pipelines` with optional `--status`, `--search`, `--page`, `--page-size`. Table output by default; `--json` returns the full `PipelineListResponse`.
|
|
117
|
+
|
|
118
|
+
## 14. `frankctl pipelines get <id>`
|
|
119
|
+
|
|
120
|
+
`GET /api/v1/pipelines/{id}`. Optional `--include-version` to include the active version payload.
|
|
121
|
+
|
|
122
|
+
## 15. `frankctl pipelines validate <id>`
|
|
123
|
+
|
|
124
|
+
`POST /api/v1/pipelines/{id}/sandbox` then polls `/sandbox/{wf}/status` every 2500 ms (`--poll-interval` to override). Step badges stream to **stderr**; the final status JSON is written to **stdout**.
|
|
125
|
+
|
|
126
|
+
Flags: `--sample-limit <n=1000> --timeout <sec=600> --no-wait`.
|
|
127
|
+
|
|
128
|
+
Exit codes: `completed` → 0, `partial_failure | failed` → 5.
|
|
129
|
+
|
|
130
|
+
## 16. JSON output contracts
|
|
131
|
+
|
|
132
|
+
- `--json` on `list`/`get`/`status`/`auth status`/`config *` emits a single JSON object/array on stdout.
|
|
133
|
+
- `validate` always emits the final sandbox status as JSON on stdout, regardless of `--json`.
|
|
134
|
+
|
|
135
|
+
## 17. Exit codes
|
|
136
|
+
|
|
137
|
+
| Code | Meaning |
|
|
138
|
+
|------|---------|
|
|
139
|
+
| 0 | Success |
|
|
140
|
+
| 1 | Generic error |
|
|
141
|
+
| 2 | Usage error |
|
|
142
|
+
| 3 | Authentication error |
|
|
143
|
+
| 4 | API error (non-2xx) |
|
|
144
|
+
| 5 | Validation failed |
|
|
145
|
+
|
|
146
|
+
## 18. Errors & troubleshooting
|
|
147
|
+
|
|
148
|
+
- FastAPI returns `{detail: str}` or `{detail: [...]}` — both shapes are normalized.
|
|
149
|
+
- `401` triggers a single token refresh + retry before surfacing.
|
|
150
|
+
- Port binding failure on PKCE prints the candidate list — use `--headless` in containers.
|
|
151
|
+
|
|
152
|
+
## 19. Dev mode
|
|
153
|
+
|
|
154
|
+
Setting `FRANK_DEV_MODE=true` + `FRANKCTL_TENANT_ID=<uuid>` forwards `X-Tenant-ID` to bypass JWT-based tenant resolution (dev only).
|
|
155
|
+
|
|
156
|
+
## 20. Relationship to `frank-pattern` (Python)
|
|
157
|
+
|
|
158
|
+
The existing Python `frank` CLI in `utils/frank_cli/` handles pattern authoring (`init|validate|test|build`) and is being renamed to `frank-pattern` in a follow-up PR on the `frank-shared-utils` submodule. The TS `frankctl` and Python `frank-pattern` are orthogonal: different domains, different binaries.
|
|
159
|
+
|
|
160
|
+
## 20b. Data-flow subtrees (WEC-133)
|
|
161
|
+
|
|
162
|
+
### `sources`
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
frankctl sources list [--type <pattern-id>] [--status <s>] [--search <q>]
|
|
166
|
+
frankctl sources get <id> [--no-streams]
|
|
167
|
+
frankctl sources create -f source.yaml [--name ...] [--type ...]
|
|
168
|
+
frankctl sources update <id> -f patch.yaml
|
|
169
|
+
frankctl sources delete <id> --yes
|
|
170
|
+
frankctl sources discover <id> [--no-wait] [--timeout <s>]
|
|
171
|
+
frankctl sources sync <id> [--streams a,b] [--force-full-refresh] [--no-wait]
|
|
172
|
+
frankctl sources logs <id> <run-id> [-f] [--level INFO|ERROR]
|
|
173
|
+
frankctl sources history <id> [--limit N] [--offset N]
|
|
174
|
+
|
|
175
|
+
frankctl sources streams list <source-id> [--enabled-only]
|
|
176
|
+
frankctl sources streams set <source-id> -f streams.yaml
|
|
177
|
+
frankctl sources streams refresh-schema <source-id> --from-discovery
|
|
178
|
+
frankctl sources streams refresh-schema <source-id> -f discovery.json
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`-f` accepts YAML, JSON, or `-` (stdin). `sources create` requires `{name, pattern_id, source_config}`. `sources streams set` body can be a bare array or `{streams: [...]}`.
|
|
182
|
+
|
|
183
|
+
### `transforms`
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
frankctl transforms list [--status <s>] [--pipeline-id <id>]
|
|
187
|
+
frankctl transforms get <id>
|
|
188
|
+
frankctl transforms runs <id> [--status] [--limit N]
|
|
189
|
+
frankctl transforms logs <id> <run-id> [-f] [--level]
|
|
190
|
+
frankctl transforms trigger <id> # UI "Run Now" path
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### `runs`
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
frankctl runs get <workflow-id>
|
|
197
|
+
frankctl runs wait <workflow-id> [--timeout <s>] [--poll-interval <ms>]
|
|
198
|
+
frankctl runs cancel <id> --yes # UUID cascades through transform-runs → sync-runs; non-UUID hits temporal
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 20c. AI subtree (WEC-134)
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
frankctl ai compose-pipeline -f spec.yaml [--no-wait] [--timeout <s>] [--poll-interval <ms>]
|
|
205
|
+
frankctl ai generate-transform -f spec.yaml
|
|
206
|
+
frankctl ai review-sql -f spec.yaml
|
|
207
|
+
frankctl ai fix-ci-failure -f spec.yaml
|
|
208
|
+
frankctl ai suggest target-schema -f spec.yaml
|
|
209
|
+
frankctl ai suggest field-mappings -f spec.yaml
|
|
210
|
+
frankctl ai suggest pattern-params -f spec.yaml
|
|
211
|
+
frankctl ai publish-transform -f spec.yaml
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
All AI commands emit JSON. Every response includes `available: bool`; when `false`, the CLI exits 4 with `AI service unavailable (Martha offline).`.
|
|
215
|
+
|
|
216
|
+
**compose-pipeline quirks** (observed against the dev stack):
|
|
217
|
+
- With incomplete/unknown `source_tables`, the backend returns `{available:true, execution_id:null, status:"error"}` synchronously. The CLI detects `execution_id==null && !processing` and exits 5 immediately (no poll).
|
|
218
|
+
- With valid inputs, `execution_id` is returned and the CLI polls `/api/v1/ai/compose-pipeline/{id}/status` until `{completed, failed, error, cancelled}` or `--timeout` elapses. Exit 0 on `completed`, 5 on other terminal, 4 on timeout.
|
|
219
|
+
|
|
220
|
+
## 20d. Patterns subtree (WEC-134)
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
frankctl patterns list [--category <c>] [--search <q>]
|
|
224
|
+
frankctl patterns get <id>
|
|
225
|
+
frankctl patterns validate-params <pattern-id> -f params.yaml
|
|
226
|
+
frankctl patterns register -f webhook-payload.json # CI only
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- `validate-params` POSTs `{params: <file>}` to `/api/v1/transform-patterns/{id}/validate`; file contents are a bare params object. Exits 5 if `valid: false`.
|
|
230
|
+
- `patterns register` HMAC-signs the payload with `X-Hub-Signature-256: sha256=<hex>` using `PATTERN_WEBHOOK_SECRET`. **This command is CI-only** — pattern publish workflows in `frank-transforms` invoke it after pushing a digest-pinned image to GHCR. If `PATTERN_WEBHOOK_SECRET` is unset the CLI exits **6** with a message pointing at the env var; there is no override flag because unsigned payloads are rejected by the backend with `403` regardless.
|
|
231
|
+
|
|
232
|
+
## 20e. Schedules subtree (WEC-134, source-scoped)
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
frankctl schedules list
|
|
236
|
+
frankctl schedules get <source-id>
|
|
237
|
+
frankctl schedules set <source-id> -f sched.yaml # schedule_value required
|
|
238
|
+
frankctl schedules pause|resume|trigger <source-id>
|
|
239
|
+
frankctl schedules delete <source-id> --yes
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Backend expects `schedule_value` even for `schedule_type: manual` (it 422s on omission), so the zod schema requires it. Example:
|
|
243
|
+
|
|
244
|
+
```yaml
|
|
245
|
+
schedule_type: cron
|
|
246
|
+
schedule_value: "0 */6 * * *"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**Transform schedules are a follow-up.** The backend surface exists (`/api/v1/transforms/{id}/schedule[/pause|resume|trigger]`) but we did not add a parallel `frankctl transform-schedules` tree in WEC-134 to keep scope tight; tracked for a future phase.
|
|
250
|
+
|
|
251
|
+
## 20f. Datasets subtree (WEC-134)
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
frankctl datasets list [--layer bronze|silver|gold] [--namespace <ns>] [--page N] [--page-size N]
|
|
255
|
+
frankctl datasets preview <layer.namespace.table> [--limit 20]
|
|
256
|
+
frankctl datasets snapshots <layer.namespace.table>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
- Dataset IDs are the 3-part `layer.namespace.table` dotted form the backend uses directly in the path (FastAPI `{dataset_id:path}` converter accepts dots verbatim).
|
|
260
|
+
- `preview` renders the payload as a table of column headers + row values with cells capped at 60 chars; `--json` returns `{dataset_id, columns, rows, row_count}` unmodified.
|
|
261
|
+
|
|
262
|
+
## 20a. Running E2E tests
|
|
263
|
+
|
|
264
|
+
E2E tests exercise the compiled CLI against a live Frank stack. They are gated on `FRANK_E2E=1` so `npm test` stays hermetic.
|
|
265
|
+
|
|
266
|
+
Preconditions:
|
|
267
|
+
- Frank stack running (`docker-compose up -d` from repo root; `curl localhost:8002/health` → 200).
|
|
268
|
+
- Test user provisioned (`testuser` / `testuser123` in the `frank` realm).
|
|
269
|
+
- Dev tenant id: `550e8400-e29b-41d4-a716-446655440001`.
|
|
270
|
+
|
|
271
|
+
Recipe:
|
|
272
|
+
```bash
|
|
273
|
+
cd frank-cli
|
|
274
|
+
export FRANKCTL_API_URL=http://localhost:8002
|
|
275
|
+
export FRANK_DEV_MODE=true
|
|
276
|
+
export FRANKCTL_TENANT_ID=550e8400-e29b-41d4-a716-446655440001
|
|
277
|
+
./dist/index.js auth login --headless --username testuser --password testuser123
|
|
278
|
+
export FRANKCTL_TOKEN=$(./dist/index.js auth token)
|
|
279
|
+
npm run build
|
|
280
|
+
npm run test:e2e
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Expect ≥24 tests green in ~90s on a healthy stack (new AI file is skipped unless Martha is available). Run a single file:
|
|
284
|
+
```bash
|
|
285
|
+
FRANK_E2E=1 npx vitest run --config vitest-e2e.config.ts tests/e2e/sources.e2e.test.ts
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Fixtures live in `tests/e2e/fixtures/` (bash scripts). Each test wraps its seed in try/finally so a failed assertion never leaks state. Config is `vitest-e2e.config.ts` (serial, 120s timeouts).
|
|
289
|
+
|
|
290
|
+
Notes on dev-stack quirks the suite tolerates:
|
|
291
|
+
- `sources discover --wait` against real postgres can exceed 45s — test accepts exit 0 OR clean timeout (exit 4/5).
|
|
292
|
+
- `sources sync` kicks off a Temporal workflow but `sync_run` rows may not materialize synchronously in dev — test asserts sync acceptance + well-formed history response, not a populated row.
|
|
293
|
+
- `transforms trigger` returns 500 when Dagster code location is unreachable — test accepts 2xx JSON OR exit 4/5 with a stderr message.
|
|
294
|
+
|
|
295
|
+
## 21. Known limitations
|
|
296
|
+
|
|
297
|
+
- Prod tenant resolution requires `FRANK_DEV_MODE=true` + `FRANKCTL_TENANT_ID` (backend expects `X-Tenant-ID` today — tracked as follow-up WEC-135).
|
|
298
|
+
- `runs cancel` against a raw temporal workflow-id currently 501s (backend gap).
|
|
299
|
+
- No `pipelines create/update`.
|
|
300
|
+
- No `transform-schedules` tree (follow-up). No transform-run cancel.
|
|
301
|
+
- PKCE redirect ports are fixed by Keycloak realm JSON.
|
|
302
|
+
|
|
303
|
+
## 22. Roadmap
|
|
304
|
+
|
|
305
|
+
Phase 3+: JWT-claim tenant resolution (removes `FRANK_DEV_MODE` requirement), transform-schedules subtree, shell completion bundle, `pipelines create/update`.
|
|
306
|
+
|
|
307
|
+
## 23. Exit code reference
|
|
308
|
+
|
|
309
|
+
| Code | Meaning |
|
|
310
|
+
|------|---------|
|
|
311
|
+
| 0 | Success |
|
|
312
|
+
| 1 | Generic error |
|
|
313
|
+
| 2 | Usage error |
|
|
314
|
+
| 3 | Authentication |
|
|
315
|
+
| 4 | API (incl. Martha unavailable) |
|
|
316
|
+
| 5 | Validation (sandbox/compose-pipeline terminal failure, validate-params invalid) |
|
|
317
|
+
| 6 | Config (e.g. `PATTERN_WEBHOOK_SECRET` unset for `patterns register`) |
|
|
318
|
+
|
|
319
|
+
## 24. Changelog
|
|
320
|
+
|
|
321
|
+
- `0.3.0` — WEC-134: ai (compose-pipeline + generate/review/fix-ci/suggest/publish), patterns (list/get/validate-params/register with HMAC), schedules (source-scoped list/get/set/delete/pause/resume/trigger), datasets (list/preview/snapshots). Adds ExitCode 6 (Config).
|
|
322
|
+
- `0.2.0` — WEC-133: sources CRUD + discover/sync/streams/logs/history, transforms list/get/runs/logs/trigger, runs get/wait/cancel.
|
|
323
|
+
- `0.1.0` — WEC-132: status, auth (PKCE/ROPC/client-credentials), config, pipelines list/get/validate.
|