@sequenceholdings/studio-cli 0.1.18 → 0.1.21

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 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 specs, and platform git repos. Runs from
5
- any repo against the platform over HTTP — no monorepo checkout required.
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`, `pipeline init`,
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
- (M2M wins when the secret is set, otherwise the cached user token is used):
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,101 @@ 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>]` | Compile and validate locally, without API access |
149
+ | `seq-studio agents plan [--dir <dir>]` | Offline compile/hash plan |
150
+ | `seq-studio agents plan [--dir <dir>] -e <env> [--target <APP_ENV>]` | Diff creates, updates, and unchanged definitions against an environment |
151
+ | `seq-studio agents apply [--dir <dir>] -e <env> [--target <APP_ENV>] [--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
+ An optional `deploy-manifest.json` targets definitions by deployment identity:
156
+
157
+ ```json
158
+ {
159
+ "schemaVersion": 1,
160
+ "definitions": [
161
+ {
162
+ "id": "680000000000000000000001",
163
+ "path": "support/agent.ts",
164
+ "environments": ["local", "staging", "production"]
165
+ }
166
+ ]
167
+ }
168
+ ```
169
+
170
+ Standalone workflow:
171
+
172
+ ```bash
173
+ pnpm dlx @sequenceholdings/studio-cli agents init support-agent
174
+ cd support-agent
175
+ pnpm install
176
+ pnpm exec seq-studio agents validate
177
+ pnpm exec seq-studio agents plan
178
+ # Authenticate only when ready to inspect or apply an environment:
179
+ pnpm exec seq-studio login
180
+ pnpm exec seq-studio agents plan -e <env>
181
+ pnpm exec seq-studio agents apply -e <env>
182
+ ```
183
+
184
+ Use `--repo agents/<name> [--ref <ref>]` or `--git-url <url>` instead of
185
+ `--dir` to materialize reviewed source from the platform git service.
186
+
187
+ ## ORM commands
188
+
189
+ `seq-studio orm` authors and deploys governed ORM v2 namespaces: TypeScript
190
+ table definitions and policies plus named GraphQL documents compiled into
191
+ persisted operations.
192
+
193
+ | Command | What it does |
194
+ |---------|--------------|
195
+ | `seq-studio orm init <dir>` | Scaffold one v2 namespace package (`sequence.config.ts`, `schema/*.ts`, `graphql/**`, and codegen config). |
196
+ | `seq-studio orm generate [dir]` | Generate `schema.graphql`, `operations.manifest.json`, `typePolicies.gen.ts`, and consumer codegen when `codegen.ts` is present. |
197
+ | `seq-studio orm validate [dir]` | Parse and compile the namespace, then verify its committed migration chain is current. |
198
+ | `seq-studio orm plan [dir] -e <env>` | Compare the compiled namespace with registry state without applying database changes. |
199
+ | `seq-studio orm diff [dir] [--check]` | Author the next committed migration, or verify the migration/snapshot chain offline for CI. |
200
+ | `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. |
201
+ | `seq-studio orm migrate-from-yaml <dir>` | Convert a legacy YAML namespace to TypeScript authoring while preserving its committed migrations. |
202
+
203
+ The everyday loop is:
204
+
205
+ ```bash
206
+ seq-studio orm init lending
207
+ cd lending
208
+ pnpm install
209
+
210
+ # Edit sequence.config.ts, schema/*.ts, and graphql/**/*.ts.
211
+ seq-studio orm generate .
212
+ seq-studio orm plan . -e local
213
+ seq-studio orm apply . -e local
214
+ seq-studio orm validate .
215
+ ```
216
+
217
+ `apply --dry-run` rehearses a migration against a disposable branch copy of
218
+ the target environment's data. `apply --no-create` is the CI guard that refuses
219
+ to provision a missing namespace. Destructive DDL requires explicit
220
+ `--allow-destructive` consent.
221
+
222
+ > **ORM v2 branch status:** the scaffold currently depends on
223
+ > `@sequenceholdings/orm@^2.0.0`, which is not published yet; the registry's
224
+ > latest release is the v1 `0.1.1` package. A clean standalone scaffold cannot
225
+ > complete `pnpm install` until the v2 ORM dependency chain is released. Do not
226
+ > use monorepo workspace links as evidence that the external workflow is ready.
227
+ > Sequence contributors should see
228
+ > [`docs/platform/orm-v2.md`](../../../docs/platform/orm-v2.md) for the branch
229
+ > architecture, internal verification workflow, demo, and remaining rollout
230
+ > gates.
231
+
131
232
  ## Process commands
132
233
 
133
234
  | Command | What it does |
@@ -193,6 +294,47 @@ cd my-processes
193
294
  seq-studio process lint # finds both processes
194
295
  ```
195
296
 
297
+ ## Managed Function commands
298
+
299
+ | Command | What it does |
300
+ |---------|--------------|
301
+ | `seq-studio functions init <dir>` | Scaffold one standalone TypeScript managed function |
302
+ | `seq-studio functions build [--dir <dir>]` | Validate a local manifest, bundle, lockfile, and size |
303
+ | `seq-studio functions deploy --dir <dir> -e <env>` | Preview secrets and deploy a local function |
304
+ | `seq-studio functions build --repo managed-functions/<name> [--path <dir>] -e <env>` | Materialize and validate a function from the platform Git Service |
305
+ | `seq-studio functions deploy --repo managed-functions/<name> [--path <dir>] -e <env>` | Materialize and deploy a function from the platform Git Service |
306
+
307
+ A normal function repo keeps `managed-function.yml` at its root and omits
308
+ `--path`. A repo may also contain related, independently deployed functions:
309
+
310
+ ```text
311
+ functions/
312
+ get-loan/
313
+ managed-function.yml
314
+ package.json
315
+ pnpm-lock.yaml
316
+ index.ts
317
+ update-loan/
318
+ managed-function.yml
319
+ package.json
320
+ pnpm-lock.yaml
321
+ index.ts
322
+ ```
323
+
324
+ Select exactly one function directory for each build or deploy:
325
+
326
+ ```bash
327
+ seq-studio functions build --repo managed-functions/encompass \
328
+ --path functions/get-loan -e staging
329
+ seq-studio functions deploy --repo managed-functions/encompass \
330
+ --path functions/get-loan -e staging
331
+ ```
332
+
333
+ Each selected directory is a self-contained function package. Functions in the
334
+ same repo share Git review and commit provenance, but keep separate manifests,
335
+ versions, runtime resources, secrets, and permissions. `--path` accepts only a
336
+ canonical relative directory inside a remote repo; use `--dir` for local source.
337
+
196
338
  ## Artifact commands
197
339
 
198
340
  `seq-studio artifact <sub>` is the entry point for Artifact Studio. It runs
@@ -205,11 +347,11 @@ routed through `~/.config/lattice/config.toml` and the shared seqapi token.
205
347
  | `seq-studio artifact link [dir] -e <env> [--project <id>]` | `link [dir] --env <env>` |
206
348
  | `seq-studio artifact build [dir]` | `build [dir]` |
207
349
  | `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` already matches the local source; `--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) |
350
+ | `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
351
  | `seq-studio artifact dev [dir] -e <env>` | `dev [dir] --env <env>` |
210
352
  | `seq-studio artifact pull <project-id> -e <env> [--out <dir>]` | `pull <project-id> --env <env> [--out <dir>]` |
211
353
  | `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 + git provenance |
354
+ | `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
355
  | `seq-studio artifact promote <deployment-id> -e <env>` | `promote <deployment-id> --env <env>` |
214
356
  | `seq-studio artifact rollback <deployment-id> -e <env>` | `rollback <deployment-id> --env <env>` |
215
357
 
@@ -219,6 +361,18 @@ routed through `~/.config/lattice/config.toml` and the shared seqapi token.
219
361
  directory's `.artifact-studio/config.json` — run them from the linked artifact
220
362
  folder (or run `seq-studio artifact link` first).
221
363
 
364
+ **Active (official) deploys require a clean `main` checkout with a resolved
365
+ git commit** for both Git Service (`--repo`) and local sources. Feature
366
+ branches, dirty trees, non-git sources, and detached checkouts that cannot be
367
+ attributed to `main` are preview-only
368
+ (`seq-studio artifact dev` / preview channel); promotion applies the same
369
+ provenance check.
370
+
371
+ **CLI atlas-ui stamp (DES-254):** builds force-alias `@sequenceholdings/atlas-ui`
372
+ to the CLI's copy — not the artifact's declared semver. Deployments record
373
+ `cliVersion` + `atlasUiVersion`. Contract:
374
+ `@sequenceholdings/artifact-studio` → `VERSION-PIN.md`.
375
+
222
376
  ### Source for `build` / `plan` / `deploy`
223
377
 
224
378
  These three commands read their source tree from one of three places — the rest
@@ -230,8 +384,10 @@ of the pipeline (build → upload) is identical regardless of origin:
230
384
  | `--repo <ns>/<name>` | a repo in the platform Git Service, e.g. `--repo artifacts/accounts` |
231
385
  | `--git-url <url>` | a public, credential-free HTTPS git URL (cloned to a temp dir) |
232
386
 
233
- `--ref <branch\|tag\|commit>` selects the revision (default: the repo's default
234
- branch). A remote source is materialized to a temp dir, built, and torn down;
387
+ `--ref <branch\|tag\|commit>` selects the revision for `build` and `plan`
388
+ (default: the repo's default branch). Active `deploy` requires `main`; other
389
+ refs are preview-only. A remote source is materialized to a temp dir, built,
390
+ and torn down;
235
391
  the target project is resolved from the source's `artifact.bundle.yml`
236
392
  `project_id`. The deployment records the resolved commit as its `git_commit`
237
393
  provenance. `--repo` needs `-e <env>` + a platform login. M2M/CI builds accept
@@ -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] offline compile + validation\n seq-studio agents plan [--dir d] [-e <env>] offline bundle plan or live diff\n seq-studio agents apply [--dir d] -e <env> [--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).\n";
10
+ export declare function runAgentsCommand(sub: string | undefined, args: ParsedArgs): Promise<number>;
@@ -0,0 +1,218 @@
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
+ export async function agentsValidateCommand(args) {
11
+ const { source } = await materialize({ args, requireEnvironment: false });
12
+ try {
13
+ const target = typeof args.flags.target === 'string' ? args.flags.target : undefined;
14
+ const bundle = await compileSource({
15
+ directory: source.dir,
16
+ targetEnvironment: target,
17
+ deployEnvironments: await deployEnvironmentNames(),
18
+ });
19
+ if (bundle.definitions.length === 0) {
20
+ console.error(`${LOG} no named agent.ts definitions found`);
21
+ return 1;
22
+ }
23
+ console.log(`${LOG} valid: ${bundle.definitions.length} definition${bundle.definitions.length === 1 ? '' : 's'}, bundle ${bundle.hash}`);
24
+ return 0;
25
+ }
26
+ finally {
27
+ await source.cleanup();
28
+ }
29
+ }
30
+ async function deploymentBundle({ args, source, context, }) {
31
+ const target = (typeof args.flags.target === 'string' ? args.flags.target : undefined) ??
32
+ context.env.name;
33
+ return compileSource({
34
+ directory: source.dir,
35
+ targetEnvironment: target,
36
+ deployEnvironments: await deployEnvironmentNames(),
37
+ });
38
+ }
39
+ /**
40
+ * Names the CLI can actually deploy to. A target outside both this set and the
41
+ * manifest's own vocabulary is a typo, not a selection.
42
+ */
43
+ async function deployEnvironmentNames() {
44
+ try {
45
+ return Object.keys((await readConfig()).envs);
46
+ }
47
+ catch {
48
+ // Never let an unreadable config block a deploy — the guard is a safety
49
+ // net, and the manifest's own vocabulary still constrains the target.
50
+ return [];
51
+ }
52
+ }
53
+ function printPlan(plan) {
54
+ for (const entry of plan.entries) {
55
+ const marker = entry.action === 'create' ? '+' : entry.action === 'update' ? '~' : '=';
56
+ // Name the fields an update would rewrite, so an operator can tell an
57
+ // intended edit from unexpected drift before confirming an apply.
58
+ const fields = entry.changedFields?.length
59
+ ? `: ${entry.changedFields.join(', ')}`
60
+ : '';
61
+ console.log(` ${marker} ${entry.name} (${entry.id}) ${entry.action}${fields}`);
62
+ }
63
+ console.log(`${LOG} plan: ${plan.summary.creates} create, ${plan.summary.updates} update, ${plan.summary.unchanged} unchanged`);
64
+ }
65
+ export async function agentsPlanCommand(args) {
66
+ const environment = requestedEnvironment(args);
67
+ if (!environment) {
68
+ const { source } = await materialize({ args, requireEnvironment: false });
69
+ try {
70
+ // Honor --target the same way validate does. Without this, offline plan
71
+ // silently compiles the unfiltered manifest set even when the caller
72
+ // asked for a specific environment's overrides.
73
+ const target = typeof args.flags.target === 'string' ? args.flags.target : undefined;
74
+ const bundle = await compileSource({
75
+ directory: source.dir,
76
+ targetEnvironment: target,
77
+ deployEnvironments: await deployEnvironmentNames(),
78
+ });
79
+ console.log(`${LOG} offline plan: ${bundle.definitions.length} valid definition${bundle.definitions.length === 1 ? '' : 's'}, bundle ${bundle.hash}`);
80
+ console.log(`${LOG} pass -e <env> for create/update/unchanged live diff`);
81
+ return bundle.definitions.length > 0 ? 0 : 1;
82
+ }
83
+ finally {
84
+ await source.cleanup();
85
+ }
86
+ }
87
+ const { source, context } = await materialize({
88
+ args,
89
+ requireEnvironment: true,
90
+ });
91
+ if (!context)
92
+ throw new Error('Missing deployment context');
93
+ try {
94
+ const bundle = await deploymentBundle({ args, source, context });
95
+ const plan = await postJson({
96
+ ...clientOptions(context),
97
+ path: '/api/agents/deploy/plan',
98
+ body: {
99
+ definitions: bundle.definitions,
100
+ bundleHash: bundle.hash,
101
+ source: source.provenance.gitCommit ?? 'local',
102
+ },
103
+ });
104
+ printPlan(plan);
105
+ return 0;
106
+ }
107
+ finally {
108
+ await source.cleanup();
109
+ }
110
+ }
111
+ export async function agentsApplyCommand(args) {
112
+ const { source, context } = await materialize({
113
+ args,
114
+ requireEnvironment: true,
115
+ });
116
+ if (!context)
117
+ throw new Error('Missing deployment context');
118
+ try {
119
+ const bundle = await deploymentBundle({ args, source, context });
120
+ const plan = await postJson({
121
+ ...clientOptions(context),
122
+ path: '/api/agents/deploy/plan',
123
+ body: {
124
+ definitions: bundle.definitions,
125
+ bundleHash: bundle.hash,
126
+ source: source.provenance.gitCommit ?? 'local',
127
+ },
128
+ });
129
+ printPlan(plan);
130
+ const preview = [
131
+ `${LOG} apply ${bundle.definitions.length} agent definitions to ${context.env.name}`,
132
+ `${LOG} bundle ${bundle.hash}`,
133
+ `${LOG} no agents will be deleted`,
134
+ ];
135
+ if (!(await confirmYes({ preview, confirmed: flagBool(args.flags, 'yes') }))) {
136
+ return 1;
137
+ }
138
+ const summary = await applyInChunks({
139
+ context,
140
+ definitions: bundle.definitions,
141
+ source: source.provenance.gitCommit ?? 'local',
142
+ });
143
+ console.log(`${LOG} applied: ${summary.created} created, ${summary.updated} updated, ${summary.unchanged} unchanged`);
144
+ return 0;
145
+ }
146
+ finally {
147
+ await source.cleanup();
148
+ }
149
+ }
150
+ export async function agentsListCommand(args) {
151
+ const context = await buildContext(args);
152
+ const agents = await getJson({
153
+ ...clientOptions(context),
154
+ path: '/api/agents/agents?view=list',
155
+ });
156
+ for (const agent of agents) {
157
+ console.log(`${agent.id} ${agent.name} managed=${agent.managedBy ?? 'user'}`);
158
+ }
159
+ return 0;
160
+ }
161
+ export async function agentsShowCommand(args) {
162
+ const id = args.positional[0];
163
+ if (!id) {
164
+ console.error('usage: seq-studio agents show <id> -e <env>');
165
+ return 1;
166
+ }
167
+ const context = await buildContext(args);
168
+ const agent = await getJson({
169
+ ...clientOptions(context),
170
+ path: `/api/agents/agents/${encodeURIComponent(id)}`,
171
+ });
172
+ console.log(JSON.stringify(agent, null, 2));
173
+ return 0;
174
+ }
175
+ export const AGENTS_USAGE = `usage:
176
+ seq-studio agents init <dir> scaffold a typed agent
177
+ seq-studio agents validate [--dir d] [--target app] offline compile + validation
178
+ seq-studio agents plan [--dir d] [-e <env>] offline bundle plan or live diff
179
+ seq-studio agents apply [--dir d] -e <env> [--yes] apply creates/updates; never deletes
180
+ seq-studio agents list -e <env> list visible agents
181
+ seq-studio agents show <id> -e <env> show one agent
182
+
183
+ Source: local --dir (default .), --repo agents/<name>, or --git-url <url>.
184
+ Use --ref for remote sources. --target selects the deployment APP_ENV when it
185
+ differs from the CLI environment alias (notably OpCo registrations).
186
+ `;
187
+ export async function runAgentsCommand(sub, args) {
188
+ try {
189
+ switch (sub) {
190
+ case 'init':
191
+ return await agentsInitCommand(args);
192
+ case 'validate':
193
+ return await agentsValidateCommand(args);
194
+ case 'plan':
195
+ return await agentsPlanCommand(args);
196
+ case 'apply':
197
+ return await agentsApplyCommand(args);
198
+ case 'list':
199
+ return await agentsListCommand(args);
200
+ case 'show':
201
+ return await agentsShowCommand(args);
202
+ case 'help':
203
+ case '--help':
204
+ case '-h':
205
+ case undefined:
206
+ console.log(AGENTS_USAGE);
207
+ return sub ? 0 : 1;
208
+ default:
209
+ console.error(`unknown agents command: ${sub}`);
210
+ console.error(AGENTS_USAGE);
211
+ return 1;
212
+ }
213
+ }
214
+ catch (error) {
215
+ printError(error);
216
+ return 1;
217
+ }
218
+ }
@@ -0,0 +1,2 @@
1
+ import type { ParsedArgs } from '../process/commands.js';
2
+ export declare function agentsInitCommand(args: ParsedArgs): Promise<number>;
@@ -0,0 +1,77 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { existsSync } from 'node:fs';
3
+ import { mkdir, writeFile } from 'node:fs/promises';
4
+ import { basename, join, resolve } from 'node:path';
5
+ import { currentVersion } from '../update-check.js';
6
+ const LOG = '[seq-studio]';
7
+ const scaffoldSource = ({ id, name, }) => `import { defineAgent } from '@sequenceholdings/agent-spec'
8
+
9
+ export const agent = defineAgent({
10
+ id: '${id}',
11
+ name: '${name}',
12
+ model: {
13
+ provider: 'anthropic',
14
+ name: 'claude-sonnet-4-6',
15
+ },
16
+ systemPrompt: 'Describe the agent role and operating instructions.',
17
+ })
18
+ `;
19
+ export async function agentsInitCommand(args) {
20
+ const target = args.positional[0];
21
+ if (!target) {
22
+ console.error('usage: seq-studio agents init <dir>');
23
+ return 1;
24
+ }
25
+ const directory = resolve(target);
26
+ const file = join(directory, 'agent.ts');
27
+ if (existsSync(file)) {
28
+ console.error(`${LOG} ${file} already exists`);
29
+ return 1;
30
+ }
31
+ const name = basename(directory)
32
+ .split(/[-_]+/)
33
+ .filter(Boolean)
34
+ .map((part) => `${part[0]?.toUpperCase() ?? ''}${part.slice(1)}`)
35
+ .join(' ');
36
+ await mkdir(directory, { recursive: true });
37
+ await writeFile(file, scaffoldSource({ id: randomBytes(12).toString('hex'), name }));
38
+ await writeFile(join(directory, 'package.json'), `${JSON.stringify({
39
+ name: basename(directory),
40
+ version: '0.0.1',
41
+ private: true,
42
+ type: 'module',
43
+ scripts: { validate: 'seq-studio agents validate' },
44
+ dependencies: { '@sequenceholdings/agent-spec': '^0.1.0' },
45
+ devDependencies: {
46
+ '@sequenceholdings/studio-cli': `^${currentVersion()}`,
47
+ },
48
+ }, null, 2)}\n`);
49
+ await writeFile(join(directory, 'tsconfig.json'), `${JSON.stringify({
50
+ compilerOptions: {
51
+ target: 'ES2022',
52
+ module: 'NodeNext',
53
+ moduleResolution: 'NodeNext',
54
+ strict: true,
55
+ noEmit: true,
56
+ },
57
+ include: ['agent.ts'],
58
+ }, null, 2)}\n`);
59
+ await writeFile(join(directory, 'pnpm-workspace.yaml'), `packages:
60
+ - '.'
61
+
62
+ minimumReleaseAge: 10080
63
+ minimumReleaseAgeExclude:
64
+ - '@sequenceholdings/agent-spec'
65
+ - '@sequenceholdings/atlas-ui'
66
+ - '@sequenceholdings/lattice-form-renderer'
67
+ - '@sequenceholdings/artifact-studio'
68
+ - '@sequenceholdings/lattice'
69
+ - '@sequenceholdings/studio-cli'
70
+ strictDepBuilds: true
71
+ allowBuilds:
72
+ esbuild: true
73
+ `);
74
+ console.log(`${LOG} scaffolded typed agent in ${directory}`);
75
+ console.log('Next: pnpm install && seq-studio agents validate');
76
+ return 0;
77
+ }
@@ -0,0 +1,18 @@
1
+ import { type CompiledAgentBundle } from '@sequenceholdings/agent-spec/compiler';
2
+ import { type ResolvedSource, type SourceSpec } from '@sequenceholdings/artifact-studio/source-resolver';
3
+ import type { ParsedArgs } from '../process/commands.js';
4
+ import { type CommandContext } from '../functions/commands.js';
5
+ export declare function materializeAgentSource({ args, requireEnvironment, }: {
6
+ args: ParsedArgs;
7
+ requireEnvironment: boolean;
8
+ }): Promise<{
9
+ spec: SourceSpec;
10
+ source: ResolvedSource;
11
+ context: CommandContext | null;
12
+ }>;
13
+ export declare function compileAgentSource({ directory, targetEnvironment, deployEnvironments, }: {
14
+ directory: string;
15
+ targetEnvironment?: string;
16
+ /** Registered deployment environments, so a real env absent from the manifest is not read as a typo. */
17
+ deployEnvironments?: readonly string[];
18
+ }): Promise<CompiledAgentBundle>;