@stackbone/cli 0.3.5 → 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 CHANGED
@@ -7,6 +7,155 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-09-10
11
+
12
+ ### Added
13
+
14
+ - **A workflow declares what it sends outward, and `stackbone dev` types it.**
15
+ `export const declaredOutputs = [{ name: 'report', kind: 'file' }, { name:
16
+ 'fileIssue', kind: 'action' }]` is a sibling export next to
17
+ `inputSchema`/`outputSchema`, read out of the workflow module or its
18
+ `.contract.ts`. The emulator writes `.stackbone/outputs.d.ts` from it, which
19
+ closes the name sets the SDK's `stackbone.outputs.write` and
20
+ `stackbone.action` accept — naming an action where a file output belongs
21
+ becomes a compile error instead of a run-time `unbound`. The list is read by
22
+ PARSING, never by evaluating: an operator binds a name to a destination before
23
+ any run exists, so a name that only appears once the workflow is running is a
24
+ name nobody can point a destination at, and the parse refuses it where it is
25
+ written, with a line. `kind` is required in source and defaulted on both wires
26
+ (PLA-1092, PLA-1251).
27
+
28
+ - **`.stackbone/connect.d.ts` types the connections the box has registered, not
29
+ just the connectors declared as files.** For seven of the eight built-ins the
30
+ difference is cosmetic, because their operations are a published API. For
31
+ Supabase it is the whole problem: a connection's operations ARE the customer's
32
+ schema, one per table, so they exist in no file and used to be retyped by hand
33
+ off the picker with nothing checking either half. The codegen now also reads
34
+ the box's own management surface. The box introspects and the CLI consumes,
35
+ which is forced rather than chosen — the credential is custodied in the broker
36
+ and Supabase refuses its metadata endpoint to an unauthenticated read. An id
37
+ declared in `connect/*.json` wins on collision, so the existing path stays
38
+ byte-identical.
39
+
40
+ - **`stackbone init` offers the organization's existing workspaces.** It used to
41
+ resolve the workspace by (org, slug) and create on a 404, so a creator who had
42
+ made a workspace in the browser and then ran `init` in a folder of that name
43
+ silently got a second, empty one. When nothing else names the workspace it now
44
+ lists the organization's agents and offers to pick one. The non-interactive
45
+ contract does not move: `--name`, a positional directory, `--json`, `--yes`
46
+ and a missing terminal all skip the listing and the prompt (PLA-1204).
47
+
48
+ ### Changed
49
+
50
+ - **BREAKING: `stackbone link` registers a box against one INSTALLATION, named
51
+ by `--installation <id>`, and never creates one.** The deployment registry
52
+ used to hold one row per agent template, while a box answers for exactly one
53
+ installation id and refuses every other with a 401 — so with two installations
54
+ of the same template at most one could be reached. The row now belongs to the
55
+ installation (`POST /api/v1/deployments` requires `installationId`), and
56
+ `link` has to be told which one: `--installation <id>` is one of the linked
57
+ agent's `cloud` rows in `stackbone agents list`. It is validated before the
58
+ box is touched — an id of another agent, an id this organization does not
59
+ hold, or a local-dev id (served by its `stackbone dev` tunnel, never by a
60
+ registered deployment) is refused with no teach and no registration. Without
61
+ the flag, `link` refuses before any network call and names the discovery
62
+ command, except on a `--force` re-run in a directory whose
63
+ `.stackbone/project.json` remembers the id the last link wrote (new field
64
+ `deploymentInstallationId`, named that because it sits next to
65
+ `localDevInstallationId` and the two are different rows; older files still
66
+ load, including one written under the interim `installationId` spelling —
67
+ that key is simply dropped, so the next `link --force` in that directory asks
68
+ for `--installation` once and rewrites the file. Nothing has to migrate it).
69
+ `link` no longer creates a cloud
70
+ installation when the agent has none — install the agent first. When the
71
+ target installation already answers somewhere, `link` prints the address it
72
+ is about to replace before proceeding. The JSON envelope keeps
73
+ `installation {id, organization_id, kind}` and drops `deployment_reachable`,
74
+ which could only ever say `reachable: true` now that a local-dev target is
75
+ refused up front (PLA-853).
76
+
77
+ ### Removed
78
+
79
+ - **BREAKING: `stackbone runs retry` is gone.** `POST /api/runs/:id/retry`
80
+ copied the failed run's input, inserted a fresh `running` row and published
81
+ `run.created` so Studio repainted — and stopped there. Nothing ever advanced
82
+ that row, because nothing dispatched the payload to the agent. Three surfaces
83
+ sold it as real anyway, this verb among them, gated behind `--yes` as if it
84
+ were destructive. The route now answers 404 and a test pins it there, so the
85
+ placebo cannot come back without a dispatch behind it. Nothing is lost: the
86
+ action never did anything (PLA-949).
87
+
88
+ ### Fixed
89
+
90
+ - **`stackbone eval` counts `not_evaluable` as an ending, and a swallowed
91
+ refusal no longer grades as a quality regression.** The CLI holds a twin of
92
+ Studio's ending list and only Studio's had been widened, so the same run got
93
+ one verdict in CI and another in the browser. The connector registry is now
94
+ asked before the row's status is read, on all three terminal paths and on the
95
+ timeout, rather than on `failed` alone — a workflow that catches its refused
96
+ connector call and returns anyway ends `done`, and the criteria would
97
+ otherwise grade an answer produced without the call ever happening.
98
+
99
+ - **`stackbone package` ends `mc`'s flag parsing before the minted MinIO
100
+ password.** A generated password beginning with a dash was read as a flag.
101
+
102
+ - **The CLI sends the bearer of the control plane it is actually calling.** The
103
+ session was picked by `credentials.activeUrl`, a single global pointer, while
104
+ the URL was resolved per invocation from `STACKBONE_API_URL`, the project's
105
+ `.stackbone/project.json` and the global config. On a machine signed in to
106
+ both production and a local stack the two disagreed constantly, so `whoami`,
107
+ `metadata` and `dev` sent one environment's token to another and got a 401
108
+ whose only suggestion — `stackbone login` — could not fix it, because the
109
+ operator was already logged in. Any CLI command run from a second workspace
110
+ rewrote the pointer and broke a session in progress. Sessions are now read
111
+ from `sessions[apiUrl]`, the active organization is keyed the same way,
112
+ `logout` revokes at the URL it prints in `control_plane_url` instead of
113
+ wherever the pointer aimed, and a missing session names the URL:
114
+ `No session for http://localhost:3790` (PLA-797).
115
+
116
+ ## [0.4.0-alpha.0] - 2026-08-31
117
+
118
+ ### Added
119
+
120
+ - **`stackbone package --target byoc | self-host` picks which folder the command
121
+ writes.** It used to write one artifact, the two-service self-host stack.
122
+ `byoc` is the default and the historic folder — the agent alone, registered
123
+ from app.stackbone.ai — while `self-host` keeps the control plane and Studio
124
+ beside it. The exclusion is the point, not a convenience: a box that finds
125
+ `STUDIO_STANDALONE_TOKEN` on its boot env authenticates the operator by static
126
+ bearer and turns the browser identity gate off, so a BYOC folder carrying that
127
+ variable would be a box anyone holding one string could drive, with every
128
+ container still healthy. The BYOC inputs have no field to write it into
129
+ (PLA-1056).
130
+
131
+ - **The `self-host` target generates both application services.** The shipped
132
+ Compose started the control plane, Postgres, Redis and MinIO and no agent at
133
+ all, so nothing served the box-owned routes Studio needs. It now brings up the
134
+ control plane with the Studio SPA and the workspace image beside it, on
135
+ separate configurable host ports, sharing one database, one `HMAC_SECRET` and
136
+ one operator token, with health checks and real startup ordering. The control
137
+ plane addresses the box as `http://agent:8080`, its Compose service name
138
+ (PLA-1069).
139
+
140
+ ### Changed
141
+
142
+ - **The offline archives are named apart.** `agent-image.tar` carries one image
143
+ and never pulls a control plane; `stackbone-images.tar` carries both. A script
144
+ that hardcoded the single old name has to pick the one its target writes.
145
+
146
+ ### Fixed
147
+
148
+ - **A rejected payload prints the fields it names, not just the sentence.**
149
+ `stackbone secrets set` printed `Request body failed schema validation.` and
150
+ nothing else, because the per-field detail rides under `details.issues` and
151
+ the reader took only `message`. It now prints the server's sentence followed
152
+ by one ` - name: Required` line per offending field, three at most and then a
153
+ ` (+<n> more)` line. A refusal carrying no issues prints its sentence
154
+ byte-for-byte, and a malformed or non-JSON body still prints `HTTP <status>`.
155
+ A local `ZodError` renders through the same bound, so the CLI's own rejections
156
+ and the box's read alike. SSE and multipart inherit it through the same
157
+ function (PLA-1044).
158
+
10
159
  ## [0.3.5] - 2026-08-27
11
160
 
12
161
  ### Added