@sequenceholdings/studio-cli 0.1.18 → 0.1.22
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 +232 -11
- package/dist/agents/apply-chunks.d.ts +13 -0
- package/dist/agents/apply-chunks.js +43 -0
- package/dist/agents/commands.d.ts +10 -0
- package/dist/agents/commands.js +238 -0
- package/dist/agents/scaffold.d.ts +2 -0
- package/dist/agents/scaffold.js +77 -0
- package/dist/agents/source.d.ts +20 -0
- package/dist/agents/source.js +144 -0
- package/dist/atlas-client.js +29 -0
- package/dist/auth.d.ts +19 -17
- package/dist/auth.js +102 -33
- package/dist/functions/commands.d.ts +2 -10
- package/dist/functions/commands.js +18 -25
- package/dist/functions/manifest.d.ts +2 -0
- package/dist/functions/manifest.js +39 -9
- package/dist/functions/source-selection.d.ts +24 -0
- package/dist/functions/source-selection.js +67 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +6 -0
- package/dist/orm/delegate.js +11 -6
- package/dist/pipeline/commands.js +21 -0
- package/dist/pipeline/lifecycle.d.ts +2 -0
- package/dist/pipeline/lifecycle.js +107 -0
- package/dist/process/build.js +2 -1
- package/dist/process/lint.js +8 -0
- package/dist/repos/commands.d.ts +53 -1
- package/dist/repos/commands.js +258 -1
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
# `@sequenceholdings/studio-cli` — `seq-studio`
|
|
2
2
|
|
|
3
|
-
Standalone CLI for the Sequence platform: Lattice processes, Artifact Studio
|
|
4
|
-
apps, Managed Functions, Managed Secrets, Data Pipelines stage
|
|
5
|
-
any repo against the platform over
|
|
3
|
+
Standalone CLI for the Sequence platform: typed agents, Lattice processes, Artifact Studio
|
|
4
|
+
apps, Managed Functions, Managed Secrets, ORM namespaces, Data Pipelines stage
|
|
5
|
+
specs, and platform git repos. Runs from any repo against the platform over
|
|
6
|
+
HTTP — no monorepo checkout required.
|
|
6
7
|
|
|
7
8
|
```
|
|
8
9
|
seq-studio process lint
|
|
9
10
|
seq-studio process plan -e <env>
|
|
10
11
|
seq-studio process apply -e <env>
|
|
12
|
+
seq-studio agents validate
|
|
13
|
+
seq-studio agents plan -e <env>
|
|
11
14
|
seq-studio artifact deploy -e <env>
|
|
15
|
+
seq-studio orm init lending
|
|
12
16
|
seq-studio pipeline validate
|
|
13
17
|
seq-studio envs list
|
|
14
18
|
seq-studio doctor
|
|
@@ -16,8 +20,8 @@ seq-studio doctor
|
|
|
16
20
|
|
|
17
21
|
> **Note:** all network commands require a Sequence platform account with the
|
|
18
22
|
> appropriate permissions. Without one, only the offline commands
|
|
19
|
-
> (`init`, `lint`, `simulate`, `build`, `bundle inspect`, `
|
|
20
|
-
> `pipeline validate`) work.
|
|
23
|
+
> (`init`, `lint`, `simulate`, `build`, `bundle inspect`, `agents init`,
|
|
24
|
+
> `agents validate`, offline `agents plan`, `pipeline init`, `pipeline validate`) work.
|
|
21
25
|
> Sequence-internal contributors: see `INTERNAL.md` in the monorepo for
|
|
22
26
|
> rollout SOPs, preview environments, and publishing docs.
|
|
23
27
|
|
|
@@ -64,12 +68,14 @@ Use `seq-studio logout` to remove the shared Sequence session, or
|
|
|
64
68
|
### Headless auth (CI) — M2M
|
|
65
69
|
|
|
66
70
|
When there's no interactive login (CI, automation), set the service-account
|
|
67
|
-
secret and `seq-studio` mints a token via the Auth0 client-credentials grant
|
|
68
|
-
|
|
71
|
+
secret and `seq-studio` mints a token via the Auth0 client-credentials grant.
|
|
72
|
+
A valid cached user session wins over an ambient M2M secret (common under
|
|
73
|
+
Doppler `atlas/dev`); force the service account with `SEQAPI_AUTH_MODE=m2m`:
|
|
69
74
|
|
|
70
75
|
```bash
|
|
71
76
|
export AUTH0_M2M_CLIENT_SECRET=... # built-in Sequence environments
|
|
72
77
|
export AUTH0_M2M_CLIENT_SECRET_BSM_STAGING=... # registered bsm-staging realm
|
|
78
|
+
export SEQAPI_AUTH_MODE=m2m # optional: ignore a leftover user session
|
|
73
79
|
seq-studio artifact deploy -e <env>
|
|
74
80
|
```
|
|
75
81
|
|
|
@@ -128,6 +134,107 @@ Pass `--env <name>` (or `-e <name>`) on commands that talk to the platform.
|
|
|
128
134
|
the artifact folder's `.artifact-studio/config.json` `defaultEnv` (set by
|
|
129
135
|
`artifact link` / `artifact env use`).
|
|
130
136
|
|
|
137
|
+
## Agent commands
|
|
138
|
+
|
|
139
|
+
Typed agent repositories export one or more `defineAgent(...)` values from files
|
|
140
|
+
named `agent.ts`. Definitions are compiled in a credential-scrubbed child process,
|
|
141
|
+
validated with the published `@sequenceholdings/agent-spec` contract, normalized,
|
|
142
|
+
and hashed before deployment. Apply creates or updates only the definitions in the
|
|
143
|
+
bundle; it never implicitly deletes agents.
|
|
144
|
+
|
|
145
|
+
| Command | What it does |
|
|
146
|
+
|---------|--------------|
|
|
147
|
+
| `seq-studio agents init <dir>` | Scaffold a standalone typed agent repository |
|
|
148
|
+
| `seq-studio agents validate [--dir <dir>] [--target <APP_ENV>] [--only <id1,id2>]` | Compile and validate locally, without API access |
|
|
149
|
+
| `seq-studio agents plan [--dir <dir>] [--only <id1,id2>]` | Offline compile/hash plan |
|
|
150
|
+
| `seq-studio agents plan [--dir <dir>] -e <env> [--target <APP_ENV>] [--only <id1,id2>]` | Diff creates, updates, and unchanged definitions against an environment |
|
|
151
|
+
| `seq-studio agents apply [--dir <dir>] -e <env> [--target <APP_ENV>] [--only <id1,id2>] [--yes]` | Apply creates and updates after showing the plan |
|
|
152
|
+
| `seq-studio agents list -e <env>` | List visible runtime agents |
|
|
153
|
+
| `seq-studio agents show <id> -e <env>` | Show one runtime agent |
|
|
154
|
+
|
|
155
|
+
For `validate`, `plan`, and `apply`, `--only` accepts a comma-separated list of
|
|
156
|
+
agent IDs after deployment-environment selection; every requested ID must be
|
|
157
|
+
selected.
|
|
158
|
+
|
|
159
|
+
An optional `deploy-manifest.json` targets definitions by deployment identity:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"schemaVersion": 1,
|
|
164
|
+
"definitions": [
|
|
165
|
+
{
|
|
166
|
+
"id": "680000000000000000000001",
|
|
167
|
+
"path": "support/agent.ts",
|
|
168
|
+
"environments": ["local", "staging", "production"]
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Standalone workflow:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
pnpm dlx @sequenceholdings/studio-cli agents init support-agent
|
|
178
|
+
cd support-agent
|
|
179
|
+
pnpm install
|
|
180
|
+
pnpm exec seq-studio agents validate
|
|
181
|
+
pnpm exec seq-studio agents plan
|
|
182
|
+
# Authenticate only when ready to inspect or apply an environment:
|
|
183
|
+
pnpm exec seq-studio login
|
|
184
|
+
pnpm exec seq-studio agents plan -e <env>
|
|
185
|
+
pnpm exec seq-studio agents apply -e <env>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Use `--repo agents/<name> [--ref <ref>]` or `--git-url <url>` instead of
|
|
189
|
+
`--dir` to materialize reviewed source from the platform git service.
|
|
190
|
+
|
|
191
|
+
## ORM commands
|
|
192
|
+
|
|
193
|
+
`seq-studio orm` authors and deploys governed ORM v2 namespaces: TypeScript
|
|
194
|
+
table definitions, Drizzle-authored read-only views, and policies plus named
|
|
195
|
+
GraphQL documents compiled into persisted operations. Import Drizzle query
|
|
196
|
+
helpers from `@sequenceholdings/orm/drizzle`; managed view builders are
|
|
197
|
+
compiled to canonical SQL before registration.
|
|
198
|
+
|
|
199
|
+
| Command | What it does |
|
|
200
|
+
|---------|--------------|
|
|
201
|
+
| `seq-studio orm init <dir>` | Scaffold one v2 namespace package (`sequence.config.ts`, `schema/*.ts`, `graphql/**`, and codegen config). |
|
|
202
|
+
| `seq-studio orm generate [dir]` | Generate `schema.graphql`, `operations.manifest.json`, `typePolicies.gen.ts`, and consumer codegen when `codegen.ts` is present. |
|
|
203
|
+
| `seq-studio orm validate [dir]` | Parse and compile the namespace, then verify its committed migration chain is current. |
|
|
204
|
+
| `seq-studio orm plan [dir] -e <env>` | Compare the compiled namespace with registry state without applying database changes. |
|
|
205
|
+
| `seq-studio orm diff [dir] [--check]` | Author the next committed migration, or verify the migration/snapshot chain offline for CI. |
|
|
206
|
+
| `seq-studio orm apply [dir] -e <env>` | Author a migration if needed, register/apply the namespace, activate its operation set, publish roles/capabilities, and refresh generated outputs. |
|
|
207
|
+
| `seq-studio orm migrate-from-yaml <dir>` | Convert a legacy YAML namespace to TypeScript authoring while preserving its committed migrations. |
|
|
208
|
+
|
|
209
|
+
The everyday loop is:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
seq-studio orm init lending
|
|
213
|
+
cd lending
|
|
214
|
+
pnpm install
|
|
215
|
+
|
|
216
|
+
# Edit sequence.config.ts, schema/*.ts, and graphql/**/*.ts.
|
|
217
|
+
seq-studio orm generate .
|
|
218
|
+
seq-studio orm plan . -e local
|
|
219
|
+
seq-studio orm apply . -e local
|
|
220
|
+
seq-studio orm validate .
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`apply --dry-run` rehearses a migration against a disposable branch copy of
|
|
224
|
+
the target environment's data. `apply --no-create` is the CI guard that refuses
|
|
225
|
+
to provision a missing namespace. Destructive DDL requires explicit
|
|
226
|
+
`--allow-destructive` consent.
|
|
227
|
+
|
|
228
|
+
> **ORM v2 branch status:** the scaffold currently depends on
|
|
229
|
+
> `@sequenceholdings/orm@^2.0.0`, which is not published yet; the registry's
|
|
230
|
+
> latest release is the v1 `0.1.1` package. A clean standalone scaffold cannot
|
|
231
|
+
> complete `pnpm install` until the v2 ORM dependency chain is released. Do not
|
|
232
|
+
> use monorepo workspace links as evidence that the external workflow is ready.
|
|
233
|
+
> Sequence contributors should see
|
|
234
|
+
> [`docs/platform/orm-v2.md`](../../../docs/platform/orm-v2.md) for the branch
|
|
235
|
+
> architecture, internal verification workflow, demo, and remaining rollout
|
|
236
|
+
> gates.
|
|
237
|
+
|
|
131
238
|
## Process commands
|
|
132
239
|
|
|
133
240
|
| Command | What it does |
|
|
@@ -193,6 +300,81 @@ cd my-processes
|
|
|
193
300
|
seq-studio process lint # finds both processes
|
|
194
301
|
```
|
|
195
302
|
|
|
303
|
+
## Managed Function commands
|
|
304
|
+
|
|
305
|
+
| Command | What it does |
|
|
306
|
+
|---------|--------------|
|
|
307
|
+
| `seq-studio functions init <dir>` | Scaffold one standalone TypeScript managed function |
|
|
308
|
+
| `seq-studio functions build [--dir <dir>]` | Validate a local manifest, bundle, lockfile, and size |
|
|
309
|
+
| `seq-studio functions deploy --dir <dir> -e <env>` | Preview secrets and deploy a local function |
|
|
310
|
+
| `seq-studio functions build --repo managed-functions/<name> [--path <dir>] -e <env>` | Materialize and validate a function from the platform Git Service |
|
|
311
|
+
| `seq-studio functions deploy --repo managed-functions/<name> [--path <dir>] -e <env>` | Materialize and deploy a function from the platform Git Service |
|
|
312
|
+
|
|
313
|
+
A normal function repo keeps `managed-function.yml` at its root and omits
|
|
314
|
+
`--path`. A repo may also contain related, independently deployed functions:
|
|
315
|
+
|
|
316
|
+
```text
|
|
317
|
+
functions/
|
|
318
|
+
get-loan/
|
|
319
|
+
managed-function.yml
|
|
320
|
+
package.json
|
|
321
|
+
pnpm-lock.yaml
|
|
322
|
+
index.ts
|
|
323
|
+
update-loan/
|
|
324
|
+
managed-function.yml
|
|
325
|
+
package.json
|
|
326
|
+
pnpm-lock.yaml
|
|
327
|
+
index.ts
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Select exactly one function directory for each build or deploy:
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
seq-studio functions build --repo managed-functions/encompass \
|
|
334
|
+
--path functions/get-loan -e staging
|
|
335
|
+
seq-studio functions deploy --repo managed-functions/encompass \
|
|
336
|
+
--path functions/get-loan -e staging
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Each selected directory is a self-contained function package. Functions in the
|
|
340
|
+
same repo share Git review and commit provenance, but keep separate manifests,
|
|
341
|
+
versions, runtime resources, secrets, and permissions. `--path` accepts only a
|
|
342
|
+
canonical relative directory inside a remote repo; use `--dir` for local source.
|
|
343
|
+
|
|
344
|
+
### Scaling
|
|
345
|
+
|
|
346
|
+
Managed functions scale to zero by default. Set a bounded warm pool in
|
|
347
|
+
`managed-function.yml` when first-request latency matters:
|
|
348
|
+
|
|
349
|
+
```yaml
|
|
350
|
+
limits:
|
|
351
|
+
min_instances: 1
|
|
352
|
+
max_instances: 3
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`min_instances` defaults to `0`, cannot exceed `max_instances`, and incurs
|
|
356
|
+
Cloud Run idle-instance charges while warm.
|
|
357
|
+
|
|
358
|
+
### ORM data access
|
|
359
|
+
|
|
360
|
+
A function declares its ORM Data API reach in `capabilities.data`, grouped by
|
|
361
|
+
namespace: `tables` it may read, v1 `actions` and ORM v2 persisted `operations`
|
|
362
|
+
it may invoke, and whether raw read `query` is allowed. At invoke time the
|
|
363
|
+
platform mints a short-lived data token scoped to exactly these refs — an
|
|
364
|
+
operation is scoped as `<namespace>/ops/<OperationName>` (the GraphQL operation
|
|
365
|
+
name from the namespace's `graphql/` documents, case-sensitive), and anything
|
|
366
|
+
undeclared is denied by the Data API. A function that reaches any namespace
|
|
367
|
+
must also attach a top-level `service_account`:
|
|
368
|
+
|
|
369
|
+
```yaml
|
|
370
|
+
service_account: lucky-svc
|
|
371
|
+
capabilities:
|
|
372
|
+
data:
|
|
373
|
+
lucky:
|
|
374
|
+
tables: [lucky_draws]
|
|
375
|
+
operations: [RollLuckyNumber]
|
|
376
|
+
```
|
|
377
|
+
|
|
196
378
|
## Artifact commands
|
|
197
379
|
|
|
198
380
|
`seq-studio artifact <sub>` is the entry point for Artifact Studio. It runs
|
|
@@ -205,11 +387,11 @@ routed through `~/.config/lattice/config.toml` and the shared seqapi token.
|
|
|
205
387
|
| `seq-studio artifact link [dir] -e <env> [--project <id>]` | `link [dir] --env <env>` |
|
|
206
388
|
| `seq-studio artifact build [dir]` | `build [dir]` |
|
|
207
389
|
| `seq-studio artifact plan [dir] -e <env>` | `plan [dir] --env <env>` |
|
|
208
|
-
| `seq-studio artifact deploy [dir] -e <env> [--skip-unchanged] [--no-create] [--project <id>]` | `deploy [dir] --env <env>` — `--skip-unchanged` no-ops (before building) when the remote active deployment's `sourceHash`
|
|
390
|
+
| `seq-studio artifact deploy [dir] -e <env> [--skip-unchanged] [--no-create] [--project <id>]` | `deploy [dir] --env <env>` — `--skip-unchanged` no-ops (before building) when the remote active deployment's `sourceHash` **and** CLI/atlas-ui peer versions already match; `--no-create` errors instead of creating a missing project; `--project <id>` targets a project directly when duplicate slugs make the lookup ambiguous (slug must still match the manifest) |
|
|
209
391
|
| `seq-studio artifact dev [dir] -e <env>` | `dev [dir] --env <env>` |
|
|
210
392
|
| `seq-studio artifact pull <project-id> -e <env> [--out <dir>]` | `pull <project-id> --env <env> [--out <dir>]` |
|
|
211
393
|
| `seq-studio artifact list -e <env>` | `list --env <env>` — projects visible on the environment (slug, id, active version, visibility) |
|
|
212
|
-
| `seq-studio artifact show <slug-or-id> -e <env>` | `show <slug-or-id> --env <env>` — one project's detail incl. active deployment
|
|
394
|
+
| `seq-studio artifact show <slug-or-id> -e <env>` | `show <slug-or-id> --env <env>` — one project's detail incl. active deployment, git provenance, and (for newer deployments) the building `artifact-studio` / force-aliased `atlas-ui` versions |
|
|
213
395
|
| `seq-studio artifact promote <deployment-id> -e <env>` | `promote <deployment-id> --env <env>` |
|
|
214
396
|
| `seq-studio artifact rollback <deployment-id> -e <env>` | `rollback <deployment-id> --env <env>` |
|
|
215
397
|
|
|
@@ -219,6 +401,18 @@ routed through `~/.config/lattice/config.toml` and the shared seqapi token.
|
|
|
219
401
|
directory's `.artifact-studio/config.json` — run them from the linked artifact
|
|
220
402
|
folder (or run `seq-studio artifact link` first).
|
|
221
403
|
|
|
404
|
+
**Active (official) deploys require a clean `main` checkout with a resolved
|
|
405
|
+
git commit** for both Git Service (`--repo`) and local sources. Feature
|
|
406
|
+
branches, dirty trees, non-git sources, and detached checkouts that cannot be
|
|
407
|
+
attributed to `main` are preview-only
|
|
408
|
+
(`seq-studio artifact dev` / preview channel); promotion applies the same
|
|
409
|
+
provenance check.
|
|
410
|
+
|
|
411
|
+
**CLI atlas-ui stamp (DES-254):** builds force-alias `@sequenceholdings/atlas-ui`
|
|
412
|
+
to the CLI's copy — not the artifact's declared semver. Deployments record
|
|
413
|
+
`cliVersion` + `atlasUiVersion`. Contract:
|
|
414
|
+
`@sequenceholdings/artifact-studio` → `VERSION-PIN.md`.
|
|
415
|
+
|
|
222
416
|
### Source for `build` / `plan` / `deploy`
|
|
223
417
|
|
|
224
418
|
These three commands read their source tree from one of three places — the rest
|
|
@@ -230,8 +424,10 @@ of the pipeline (build → upload) is identical regardless of origin:
|
|
|
230
424
|
| `--repo <ns>/<name>` | a repo in the platform Git Service, e.g. `--repo artifacts/accounts` |
|
|
231
425
|
| `--git-url <url>` | a public, credential-free HTTPS git URL (cloned to a temp dir) |
|
|
232
426
|
|
|
233
|
-
`--ref <branch\|tag\|commit>` selects the revision
|
|
234
|
-
|
|
427
|
+
`--ref <branch\|tag\|commit>` selects the revision for `build` and `plan`
|
|
428
|
+
(default: the repo's default branch). Active `deploy` requires `main`; other
|
|
429
|
+
refs are preview-only. A remote source is materialized to a temp dir, built,
|
|
430
|
+
and torn down;
|
|
235
431
|
the target project is resolved from the source's `artifact.bundle.yml`
|
|
236
432
|
`project_id`. The deployment records the resolved commit as its `git_commit`
|
|
237
433
|
provenance. `--repo` needs `-e <env>` + a platform login. M2M/CI builds accept
|
|
@@ -311,11 +507,34 @@ JSON API — the same repos `--repo <ns>/<name>` sources build from.
|
|
|
311
507
|
| `seq-studio repos clone <ns>/<name> \| --url <clone-url> \| --id <uuid> -e <env> [--ref <r>] [--out <dir>] [--force]` | smart-HTTP `git clone` when `ATLAS_GIT_PAT` is set (`--url`/`--id` need no seqapi); otherwise JSON materialize + PAT hint |
|
|
312
508
|
| `seq-studio repos pull <ns>/<name> -e <env> [--ref <r>] [--out <dir>] [--force]` | always materialize via JSON API (no `.git` dir); refuses a non-empty destination unless `--force` |
|
|
313
509
|
| `seq-studio repos delete <ns>/<name> -e <env> [--yes]` | delete a repo — interactive confirm unless `--yes` |
|
|
510
|
+
| `seq-studio repos ci show <ns>/<name> -e <env> [--ref <r>]` | preview CI checks discovered from the ref |
|
|
511
|
+
| `seq-studio repos ci require <ns>/<name> --check <name> -e <env>` | reserved for requiring a named CI check; currently refuses to write until the sandboxed runner is live |
|
|
512
|
+
| `seq-studio repos ci import <ns>/<name> -e <env> [--ref <r>]` | reserved for requiring every discovered check; currently refuses to write until the sandboxed runner is live |
|
|
314
513
|
|
|
315
514
|
`show` prints the smart-HTTP clone URL (`…/repos/<id>/git`). Basic auth:
|
|
316
515
|
any username, PAT as password. Prefer `repos clone` over hand-rolling the
|
|
317
516
|
tree API.
|
|
318
517
|
|
|
518
|
+
PR CI discovers `ci/check` from the first available `lint`, `typecheck`, or
|
|
519
|
+
`check` script and `ci/test` from `test`. A `.seq/ci.json` takes precedence;
|
|
520
|
+
its `checks` array can declare script/argv checks or be empty to opt out.
|
|
521
|
+
|
|
522
|
+
For example:
|
|
523
|
+
|
|
524
|
+
{ "checks": [
|
|
525
|
+
{ "phase": "check", "script": "lint" },
|
|
526
|
+
{ "phase": "test", "command": ["pnpm", "test"] }
|
|
527
|
+
] }
|
|
528
|
+
|
|
529
|
+
Each check needs `phase` (`check` or `test`) and exactly one of `script` or
|
|
530
|
+
`command`; `name` is optional and otherwise defaults to `ci/<phase>`
|
|
531
|
+
(`-2`, etc. for additional checks in that phase).
|
|
532
|
+
Discovery alone never blocks a merge. Until the sandboxed executor is live,
|
|
533
|
+
`repos ci show` is preview-only, Settings controls are disabled, and
|
|
534
|
+
`require`/`import` refuse to write (discovery currently posts `neutral`
|
|
535
|
+
check-runs). Once the executor is live, repo owners can opt in by requiring
|
|
536
|
+
check names in Settings or with `repos ci require`/`repos ci import`.
|
|
537
|
+
|
|
319
538
|
## Pipeline commands
|
|
320
539
|
|
|
321
540
|
`seq-studio pipeline <sub>` authors and validates Data Pipelines **stage
|
|
@@ -330,6 +549,8 @@ install it alongside the CLI to use this family.
|
|
|
330
549
|
| `seq-studio pipeline validate [dir] [--assets <file\|url>] [--json]` | Run the full offline spec gate: envelope + body validation, `schema_ref` resolution, and repo-level graph validation (reference resolution, single-writer, cycles, column subsets, serving projection checks). Exit 0/1 |
|
|
331
550
|
| `seq-studio pipeline plan --repo pipelines/<slug> --ref <sha\|branch> -e <env> [--json]` | Plan a Pipeline deploy (materialize → SDK/`validateSpecGraph` → compile → live-diff → provision findings). Does **not** run Databricks `bundle validate` (that is a Trigger deploy-path hard gate). Exit 1 on destructive findings (CI-safe). `--json` emits the stable plan envelope |
|
|
332
551
|
| `seq-studio pipeline deploy --repo pipelines/<slug> --ref <sha> -e <env> [--approved-by <sub>] [--no-wait]` | Plan then enqueue deploy; Trigger runs `bundle validate` then `bundle deploy` against reviewed bytes. Polls to terminal unless `--no-wait`. Production/banksouth require a pinned 40-hex SHA (client + server) |
|
|
552
|
+
| `seq-studio pipeline adopt --stage <slug> --ref <sha\|branch> -e <env> --native-id <id> --approved-by <you> [--resource-key <key>] [--kind job\|dlt_pipeline] [--old-source-removal-pr <url>] [--repo pipelines/<slug>]` | Bind a live Databricks job/pipeline into the stage without recreation (`bundle deployment bind` on Trigger). Always requires `--approved-by` naming the caller. When the key is still in the monorepo DAB, pass `--old-source-removal-pr` and follow the returned cutover checklist: unbind the old bundle state without deleting the remote, then remove its DAB declaration and add the target-specific adopted-resource entry in the same PR before redeploying. |
|
|
553
|
+
| `seq-studio pipeline unbind --stage <slug> --ref <sha\|branch> -e <env> --approved-by <you> [--resource-key <key>] [--repo pipelines/<slug>]` | Release an adopted binding on Trigger; the remote object stays live (never deleted) |
|
|
333
554
|
| `seq-studio pipeline run-now --stage <slug> -e <env> [--repo pipelines/<slug>] [--json]` | Run the stage's active job or DLT pipeline immediately and print its Databricks run URL |
|
|
334
555
|
| `seq-studio pipeline promote --stage <slug> --version <v> -e <env> [--repo pipelines/<slug>] [--approved-by <you>] [--no-wait]` | Promote a validated version to another environment. Prod/banksouth require `--approved-by` naming the authenticated caller (approvals are self-recorded); `--repo` disambiguates a slug that exists in multiple Pipelines |
|
|
335
556
|
| `seq-studio pipeline rollback --stage <slug> -e <env> [--repo pipelines/<slug>] [--approved-by <you>] [--no-wait]` | Redeploy the previously retired deployment's version. Prod/banksouth require `--approved-by` — approvals are explicit even for rollbacks |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type AgentDefinition } from '@sequenceholdings/agent-spec';
|
|
2
|
+
import { type CommandContext } from '../functions/commands.js';
|
|
3
|
+
export interface ApplySummary {
|
|
4
|
+
created: number;
|
|
5
|
+
updated: number;
|
|
6
|
+
unchanged: number;
|
|
7
|
+
}
|
|
8
|
+
/** Each chunk is hashed over its own definitions — the apply route validates the body it received. */
|
|
9
|
+
export declare function applyInChunks({ context, definitions, source, }: {
|
|
10
|
+
context: CommandContext;
|
|
11
|
+
definitions: AgentDefinition[];
|
|
12
|
+
source: string;
|
|
13
|
+
}): Promise<ApplySummary>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { hashAgentBundle, } from '@sequenceholdings/agent-spec';
|
|
2
|
+
import { postJson } from '../atlas-client.js';
|
|
3
|
+
import { clientOptions } from '../functions/commands.js';
|
|
4
|
+
const LOG = '[seq-studio]';
|
|
5
|
+
// Chunked to stay inside the HTTP client's timeout; each agent commits independently anyway.
|
|
6
|
+
const APPLY_CHUNK_SIZE = 20;
|
|
7
|
+
/** Each chunk is hashed over its own definitions — the apply route validates the body it received. */
|
|
8
|
+
export async function applyInChunks({ context, definitions, source, }) {
|
|
9
|
+
const chunks = [];
|
|
10
|
+
for (let i = 0; i < definitions.length; i += APPLY_CHUNK_SIZE) {
|
|
11
|
+
chunks.push(definitions.slice(i, i + APPLY_CHUNK_SIZE));
|
|
12
|
+
}
|
|
13
|
+
const total = { created: 0, updated: 0, unchanged: 0 };
|
|
14
|
+
let applied = 0;
|
|
15
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
16
|
+
try {
|
|
17
|
+
const result = await postJson({
|
|
18
|
+
...clientOptions(context),
|
|
19
|
+
path: '/api/agents/deploy/apply',
|
|
20
|
+
body: {
|
|
21
|
+
definitions: chunk,
|
|
22
|
+
bundleHash: hashAgentBundle({ definitions: chunk }),
|
|
23
|
+
source,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
total.created += result.summary.created;
|
|
27
|
+
total.updated += result.summary.updated;
|
|
28
|
+
total.unchanged += result.summary.unchanged;
|
|
29
|
+
applied += chunk.length;
|
|
30
|
+
if (chunks.length > 1) {
|
|
31
|
+
console.log(`${LOG} batch ${index + 1}/${chunks.length}: ${applied} of ${definitions.length} agents`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
// Name what already landed. Without it a mid-run failure reads as a total
|
|
36
|
+
// failure, and the operator cannot tell which agents are already on the
|
|
37
|
+
// new definition.
|
|
38
|
+
console.error(`${LOG} batch ${index + 1}/${chunks.length} failed after ${applied} of ${definitions.length} agents applied — re-run to resume`);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return total;
|
|
43
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ParsedArgs } from '../process/commands.js';
|
|
2
|
+
import { agentsInitCommand } from './scaffold.js';
|
|
3
|
+
export { agentsInitCommand };
|
|
4
|
+
export declare function agentsValidateCommand(args: ParsedArgs): Promise<number>;
|
|
5
|
+
export declare function agentsPlanCommand(args: ParsedArgs): Promise<number>;
|
|
6
|
+
export declare function agentsApplyCommand(args: ParsedArgs): Promise<number>;
|
|
7
|
+
export declare function agentsListCommand(args: ParsedArgs): Promise<number>;
|
|
8
|
+
export declare function agentsShowCommand(args: ParsedArgs): Promise<number>;
|
|
9
|
+
export declare const AGENTS_USAGE = "usage:\n seq-studio agents init <dir> scaffold a typed agent\n seq-studio agents validate [--dir d] [--target app] [--only ids] offline compile + validation\n seq-studio agents plan [--dir d] [-e <env>] [--only ids] offline bundle plan or live diff\n seq-studio agents apply [--dir d] -e <env> [--only ids] [--yes] apply creates/updates; never deletes\n seq-studio agents list -e <env> list visible agents\n seq-studio agents show <id> -e <env> show one agent\n\n Source: local --dir (default .), --repo agents/<name>, or --git-url <url>.\n Use --ref for remote sources. --target selects the deployment APP_ENV when it\n differs from the CLI environment alias (notably OpCo registrations). --only\n accepts a comma-separated list of agent IDs after environment selection.\n";
|
|
10
|
+
export declare function runAgentsCommand(sub: string | undefined, args: ParsedArgs): Promise<number>;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { getJson, postJson } from '../atlas-client.js';
|
|
2
|
+
import { readConfig } from '../config.js';
|
|
3
|
+
import { applyInChunks } from './apply-chunks.js';
|
|
4
|
+
import { confirmYes } from '../prompt.js';
|
|
5
|
+
import { buildContext, clientOptions, flagBool, printError, requestedEnvironment, } from '../functions/commands.js';
|
|
6
|
+
import { compileAgentSource as compileSource, materializeAgentSource as materialize, } from './source.js';
|
|
7
|
+
import { agentsInitCommand } from './scaffold.js';
|
|
8
|
+
export { agentsInitCommand };
|
|
9
|
+
const LOG = '[seq-studio]';
|
|
10
|
+
function requestedAgentIds(args) {
|
|
11
|
+
const only = args.flags.only;
|
|
12
|
+
if (only === undefined)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (typeof only !== 'string') {
|
|
15
|
+
throw new Error('--only must be a comma-separated list of agent IDs');
|
|
16
|
+
}
|
|
17
|
+
const ids = only
|
|
18
|
+
.split(',')
|
|
19
|
+
.map((id) => id.trim())
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
if (ids.length === 0) {
|
|
22
|
+
throw new Error('--only must include at least one agent ID');
|
|
23
|
+
}
|
|
24
|
+
return [...new Set(ids)];
|
|
25
|
+
}
|
|
26
|
+
export async function agentsValidateCommand(args) {
|
|
27
|
+
const { source } = await materialize({ args, requireEnvironment: false });
|
|
28
|
+
try {
|
|
29
|
+
const target = typeof args.flags.target === 'string' ? args.flags.target : undefined;
|
|
30
|
+
const bundle = await compileSource({
|
|
31
|
+
directory: source.dir,
|
|
32
|
+
targetEnvironment: target,
|
|
33
|
+
deployEnvironments: await deployEnvironmentNames(),
|
|
34
|
+
onlyIds: requestedAgentIds(args),
|
|
35
|
+
});
|
|
36
|
+
if (bundle.definitions.length === 0) {
|
|
37
|
+
console.error(`${LOG} no named agent.ts definitions found`);
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
console.log(`${LOG} valid: ${bundle.definitions.length} definition${bundle.definitions.length === 1 ? '' : 's'}, bundle ${bundle.hash}`);
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
await source.cleanup();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function deploymentBundle({ args, source, context, }) {
|
|
48
|
+
const target = (typeof args.flags.target === 'string' ? args.flags.target : undefined) ??
|
|
49
|
+
context.env.name;
|
|
50
|
+
return compileSource({
|
|
51
|
+
directory: source.dir,
|
|
52
|
+
targetEnvironment: target,
|
|
53
|
+
deployEnvironments: await deployEnvironmentNames(),
|
|
54
|
+
onlyIds: requestedAgentIds(args),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Names the CLI can actually deploy to. A target outside both this set and the
|
|
59
|
+
* manifest's own vocabulary is a typo, not a selection.
|
|
60
|
+
*/
|
|
61
|
+
async function deployEnvironmentNames() {
|
|
62
|
+
try {
|
|
63
|
+
return Object.keys((await readConfig()).envs);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Never let an unreadable config block a deploy — the guard is a safety
|
|
67
|
+
// net, and the manifest's own vocabulary still constrains the target.
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function printPlan(plan) {
|
|
72
|
+
for (const entry of plan.entries) {
|
|
73
|
+
const marker = entry.action === 'create' ? '+' : entry.action === 'update' ? '~' : '=';
|
|
74
|
+
// Name the fields an update would rewrite, so an operator can tell an
|
|
75
|
+
// intended edit from unexpected drift before confirming an apply.
|
|
76
|
+
const fields = entry.changedFields?.length
|
|
77
|
+
? `: ${entry.changedFields.join(', ')}`
|
|
78
|
+
: '';
|
|
79
|
+
console.log(` ${marker} ${entry.name} (${entry.id}) ${entry.action}${fields}`);
|
|
80
|
+
}
|
|
81
|
+
console.log(`${LOG} plan: ${plan.summary.creates} create, ${plan.summary.updates} update, ${plan.summary.unchanged} unchanged`);
|
|
82
|
+
}
|
|
83
|
+
export async function agentsPlanCommand(args) {
|
|
84
|
+
const environment = requestedEnvironment(args);
|
|
85
|
+
if (!environment) {
|
|
86
|
+
const { source } = await materialize({ args, requireEnvironment: false });
|
|
87
|
+
try {
|
|
88
|
+
// Honor --target the same way validate does. Without this, offline plan
|
|
89
|
+
// silently compiles the unfiltered manifest set even when the caller
|
|
90
|
+
// asked for a specific environment's overrides.
|
|
91
|
+
const target = typeof args.flags.target === 'string' ? args.flags.target : undefined;
|
|
92
|
+
const bundle = await compileSource({
|
|
93
|
+
directory: source.dir,
|
|
94
|
+
targetEnvironment: target,
|
|
95
|
+
deployEnvironments: await deployEnvironmentNames(),
|
|
96
|
+
onlyIds: requestedAgentIds(args),
|
|
97
|
+
});
|
|
98
|
+
console.log(`${LOG} offline plan: ${bundle.definitions.length} valid definition${bundle.definitions.length === 1 ? '' : 's'}, bundle ${bundle.hash}`);
|
|
99
|
+
console.log(`${LOG} pass -e <env> for create/update/unchanged live diff`);
|
|
100
|
+
return bundle.definitions.length > 0 ? 0 : 1;
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
await source.cleanup();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const { source, context } = await materialize({
|
|
107
|
+
args,
|
|
108
|
+
requireEnvironment: true,
|
|
109
|
+
});
|
|
110
|
+
if (!context)
|
|
111
|
+
throw new Error('Missing deployment context');
|
|
112
|
+
try {
|
|
113
|
+
const bundle = await deploymentBundle({ args, source, context });
|
|
114
|
+
const plan = await postJson({
|
|
115
|
+
...clientOptions(context),
|
|
116
|
+
path: '/api/agents/deploy/plan',
|
|
117
|
+
body: {
|
|
118
|
+
definitions: bundle.definitions,
|
|
119
|
+
bundleHash: bundle.hash,
|
|
120
|
+
source: source.provenance.gitCommit ?? 'local',
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
printPlan(plan);
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
await source.cleanup();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export async function agentsApplyCommand(args) {
|
|
131
|
+
const { source, context } = await materialize({
|
|
132
|
+
args,
|
|
133
|
+
requireEnvironment: true,
|
|
134
|
+
});
|
|
135
|
+
if (!context)
|
|
136
|
+
throw new Error('Missing deployment context');
|
|
137
|
+
try {
|
|
138
|
+
const bundle = await deploymentBundle({ args, source, context });
|
|
139
|
+
const plan = await postJson({
|
|
140
|
+
...clientOptions(context),
|
|
141
|
+
path: '/api/agents/deploy/plan',
|
|
142
|
+
body: {
|
|
143
|
+
definitions: bundle.definitions,
|
|
144
|
+
bundleHash: bundle.hash,
|
|
145
|
+
source: source.provenance.gitCommit ?? 'local',
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
printPlan(plan);
|
|
149
|
+
const preview = [
|
|
150
|
+
`${LOG} apply ${bundle.definitions.length} agent definitions to ${context.env.name}`,
|
|
151
|
+
`${LOG} bundle ${bundle.hash}`,
|
|
152
|
+
`${LOG} no agents will be deleted`,
|
|
153
|
+
];
|
|
154
|
+
if (!(await confirmYes({ preview, confirmed: flagBool(args.flags, 'yes') }))) {
|
|
155
|
+
return 1;
|
|
156
|
+
}
|
|
157
|
+
const summary = await applyInChunks({
|
|
158
|
+
context,
|
|
159
|
+
definitions: bundle.definitions,
|
|
160
|
+
source: source.provenance.gitCommit ?? 'local',
|
|
161
|
+
});
|
|
162
|
+
console.log(`${LOG} applied: ${summary.created} created, ${summary.updated} updated, ${summary.unchanged} unchanged`);
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
await source.cleanup();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
export async function agentsListCommand(args) {
|
|
170
|
+
const context = await buildContext(args);
|
|
171
|
+
const agents = await getJson({
|
|
172
|
+
...clientOptions(context),
|
|
173
|
+
path: '/api/agents/agents?view=list',
|
|
174
|
+
});
|
|
175
|
+
for (const agent of agents) {
|
|
176
|
+
console.log(`${agent.id} ${agent.name} managed=${agent.managedBy ?? 'user'}`);
|
|
177
|
+
}
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
export async function agentsShowCommand(args) {
|
|
181
|
+
const id = args.positional[0];
|
|
182
|
+
if (!id) {
|
|
183
|
+
console.error('usage: seq-studio agents show <id> -e <env>');
|
|
184
|
+
return 1;
|
|
185
|
+
}
|
|
186
|
+
const context = await buildContext(args);
|
|
187
|
+
const agent = await getJson({
|
|
188
|
+
...clientOptions(context),
|
|
189
|
+
path: `/api/agents/agents/${encodeURIComponent(id)}`,
|
|
190
|
+
});
|
|
191
|
+
console.log(JSON.stringify(agent, null, 2));
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
export const AGENTS_USAGE = `usage:
|
|
195
|
+
seq-studio agents init <dir> scaffold a typed agent
|
|
196
|
+
seq-studio agents validate [--dir d] [--target app] [--only ids] offline compile + validation
|
|
197
|
+
seq-studio agents plan [--dir d] [-e <env>] [--only ids] offline bundle plan or live diff
|
|
198
|
+
seq-studio agents apply [--dir d] -e <env> [--only ids] [--yes] apply creates/updates; never deletes
|
|
199
|
+
seq-studio agents list -e <env> list visible agents
|
|
200
|
+
seq-studio agents show <id> -e <env> show one agent
|
|
201
|
+
|
|
202
|
+
Source: local --dir (default .), --repo agents/<name>, or --git-url <url>.
|
|
203
|
+
Use --ref for remote sources. --target selects the deployment APP_ENV when it
|
|
204
|
+
differs from the CLI environment alias (notably OpCo registrations). --only
|
|
205
|
+
accepts a comma-separated list of agent IDs after environment selection.
|
|
206
|
+
`;
|
|
207
|
+
export async function runAgentsCommand(sub, args) {
|
|
208
|
+
try {
|
|
209
|
+
switch (sub) {
|
|
210
|
+
case 'init':
|
|
211
|
+
return await agentsInitCommand(args);
|
|
212
|
+
case 'validate':
|
|
213
|
+
return await agentsValidateCommand(args);
|
|
214
|
+
case 'plan':
|
|
215
|
+
return await agentsPlanCommand(args);
|
|
216
|
+
case 'apply':
|
|
217
|
+
return await agentsApplyCommand(args);
|
|
218
|
+
case 'list':
|
|
219
|
+
return await agentsListCommand(args);
|
|
220
|
+
case 'show':
|
|
221
|
+
return await agentsShowCommand(args);
|
|
222
|
+
case 'help':
|
|
223
|
+
case '--help':
|
|
224
|
+
case '-h':
|
|
225
|
+
case undefined:
|
|
226
|
+
console.log(AGENTS_USAGE);
|
|
227
|
+
return sub ? 0 : 1;
|
|
228
|
+
default:
|
|
229
|
+
console.error(`unknown agents command: ${sub}`);
|
|
230
|
+
console.error(AGENTS_USAGE);
|
|
231
|
+
return 1;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
printError(error);
|
|
236
|
+
return 1;
|
|
237
|
+
}
|
|
238
|
+
}
|