Repository navigation
Commit 615bb97
fix(cli): os build carries a package-relative OpenAPI connector spec into the artifact (#28533)
Fixes #28505
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:`os build` 现在把 `provider: 'openapi'` 连接器以包内相对路径引用的 OpenAPI
文档读出来,直接内联写进 `dist/objectstack.json`;路径无效时构建失败。
- 为什么改:装进别的运行时(`os package install` 或市场安装)只带过去那份
JSON,路径会按宿主自己的目录解析,找不到文件,连接器静默不出现;Stripe 应用装上即用不了。
- 风险与回滚:产物里 `providerConfig.spec` 由路径变成对象,体积随文档增大(Stripe 由约 4.7 KB 到
59.8 KB);读产物的云端流水线需接受内联形态(spec 本就允许)。回滚即还原本 PR,源码不需要改。
- 先看:`packages/cli/src/utils/bundle-connector-spec-files.ts`(只识别 openapi
的 `spec` 字符串且非 http(s) 的那一种);`packages/cli/src/commands/compile.ts` 新增的
4a-bis
段;`packages/qa/dogfood/test/install-local-connector-spec-file.dogfood.test.ts`。
- 两分钟验证:`pnpm --filter @objectstack/connector-app-stripe build`,预期输出一行
`Bundling connector spec files... 1 document (46.0 KB)`,且
`dist/objectstack.json` 里 `connectors[0].providerConfig.spec.openapi` 为
`3.0.0`。
- 需要你做的:无,席位落地。
## What changes
- **`os build` carries a package-relative OpenAPI spec into the
artifact.** New `packages/cli/src/utils/bundle-connector-spec-files.ts`,
wired in `compile.ts` right after the seed-file bundling, split the same
way (`collect` = the gate, `place` = the assembly). For every connector
entry (top level, `manifest`, and each `packages[i].manifest` body) with
`provider: 'openapi'` whose `providerConfig.spec` is a non-empty string
that is not an http(s) URL, the file is read relative to the directory
holding `objectstack.config.ts`, confined to it by the runtime loader's
own rule, parsed as JSON, and written in place of the path. Every other
provider, key and form is written as before.
- **The build refuses a reference it cannot carry**: absolute, climbing
out of the root, naming no file, or naming a file that is not a JSON
object. Located at the entry (`connectors[0].providerConfig.spec`), in
the text face and in `--json`.
- **Docs**: one paragraph in `content/docs/automation/connectors.mdx`
(the `provider: 'openapi'` section) says the build carries the file.
- **Changeset**: `@objectstack/cli` patch.
**What this changes for readers of the artifact** (the cloud ingestion
pipeline among them): an artifact built by this CLI carries
`providerConfig.spec` as the inline document object, never the path, and
is larger by the document's size. The inline form is one the spec schema
(`providerConfig: z.record(z.string(), z.unknown())`) and the `openapi`
provider already accept; no `packages/spec` change.
### The measurement (before any code), at `23d1303d`
Scratch app, `provider: 'openapi'`, `providerConfig.spec:
'./openapi/fixture.openapi.json'`, `auth: bearer` with the credential
set:
1. `os build` wrote `dist/objectstack.json` (0.7 KB) with
`providerConfig: { spec: './openapi/fixture.openapi.json' }`, no
document.
2. Installed into the showcase (a different root) through `POST
/api/v1/marketplace/install-local`, body exactly as
`packages/cli/src/commands/package/install.ts` builds it for an artifact
file: `200`, `hotLoaded: true`, `credentialRefs[0].status: set`.
3. The reconcile then logged, and `GET /api/v1/automation/connectors`
did not list the instance at all (not even degraded):
```text
ERROR [Automation] failed to materialize connector instance 'qa28505_fixture' via provider 'openapi' (ADR-0097). {"error":"connector-openapi provider: connector 'qa28505_fixture' failed to read providerConfig.spec './openapi/fixture.openapi.json': package file ref './openapi/fixture.openapi.json' could not be read (resolved to '/home/user/objectstack-issue-28505/examples/app-showcase/openapi/fixture.openapi.json'): ENOENT ..."}
```
The install door caches the artifact JSON alone
(`STORAGE_DIR/SAFE_ID.json`, `marketplace-install-local-plugin.ts`
header), and `service-automation` hands every instance
`createPackageFileLoader(this.options.packageRoot)`, the host's root. An
installed package has no directory on disk, so "resolve against the
installed package's own root" (route b) has nothing to resolve against
without the artifact carrying the file anyway; route (a) needs no
contract change, so it is the route taken. ADR-0097 (§ Deliberate scope
boundaries) describes the file form as resolved against "the declaring
stack/package root (`packageRoot`, CLI default: the
`objectstack.config.ts` directory)" and does not anticipate an installed
package having a root of its own.
After the fix, the real Stripe app (`packages/apps/connector-stripe`,
now on `main`) builds to 59.8 KB with the 46.0 KB document inline, and
installed into the showcase the same way it materializes `ready` with
its eight actions.
## Tests
- `packages/cli/src/utils/bundle-connector-spec-files.test.ts` (unit
tier): carried with its other keys kept; manifest and `packages[]`
bodies; inline document, http(s) URL, another provider and a descriptor
left as written; five refusals, each located and naming the reference,
nothing changed.
-
`packages/qa/dogfood/test/install-local-connector-spec-file.dogfood.test.ts`:
spawns the BUILT `os build` on a one-connector app, asserts the artifact
carries the document, installs it into the showcase through
install-local and asserts `GET /automation/connectors` lists the
instance `ready` with the document's operation. Declares a measured
`test-budget: 20` (class: spawns the real producer once per single-cause
fixture).
<details><summary>Red before, green after, one ablation (dogfood file,
reads the CLI's dist)</summary>
- Red, CLI dist built from `main`: `Tests 2 failed (2)` — `expected
'./openapi/fixture.openapi.json' to deeply equal { openapi: '3.0.3',
…(3) }`; the listing held only the showcase's five connectors.
- Green after `pnpm --filter @objectstack/cli build`: `Tests 2 passed
(2)`.
- Ablation, committed head `9b81f435`: `node
scripts/ablation-replace.mjs --file packages/cli/src/commands/compile.ts
--anchor 'placeConnectorSpecFiles(connectorSpecs);' --replacement 'void
placeConnectorSpecFiles; /* ABLATED_SPEC_PLACE */' -- …` → `anchor 1 ->
0, blob daba252 -> 0994c37b6d13`; rebuild; `ablation-dist-preflight
@objectstack/cli ABLATED_SPEC_PLACE` → `marker present in 1 built file`;
`Tests 2 failed (2)`; restored `blob == HEAD (daba252) and git diff
HEAD is empty`. Restore leg: rebuild; preflight `--absent` → `marker
absent from all 772 built files`, `working tree clean against HEAD`;
`Tests 2 passed (2)`.
</details>
<details><summary>Final head f6ca481 (after merging
origin/main)</summary>
- `pnpm --filter @objectstack/cli exec vitest run --project unit
--maxWorkers=2 src/utils/bundle-connector-spec-files.test.ts
src/utils/bundle-seed-files.test.ts` → `Tests 16 passed (16)`.
- `pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2
test/install-local-connector-spec-file.dogfood.test.ts` → `Tests 2
passed (2)`.
- `pnpm --filter @objectstack/connector-app-stripe test` → `Tests 16
passed (16)`.
- `pnpm --filter @objectstack/cli typecheck` and `pnpm --filter
@objectstack/dogfood typecheck` → exit 0 (`tsc --listFiles`: the dogfood
program holds the new test; the CLI's `tsconfig.json` program covers
`src/**` tests).
- CLI `integration` tier: declared to CI (no integration-tier file or
spawn entry touched).
</details>
## Acceptance notes
- `os validate` does not run this gate; only `os build` does (seed files
run in both). A missing spec file is therefore refused at build, not at
validate.
- `os dev`'s watcher watches `objectstack.config.ts` and `src/`; a spec
file outside `src/` (the Stripe app's `./openapi/`) edited during `os
dev` reaches the running artifact at the next recompile, not on its own.
- A non-OpenAPI-3 document carried inline is refused at materialization
naming "(the inline document)" rather than the file path.
- The runtime half is untouched: `loadPackageFile` /
`resolvePackagePath` still resolve an installed package's file ref
against the host root. Measured: an installed package naming
`./src/system/connectors/status-openapi.json` materialized `ready` with
the showcase host's own document. Reported for a separate card, not
fixed here.
- ADR-0097's scope-boundary paragraph does not mention build-time
carrying; an amendment note is the maintainer's call (governed surface,
not edited here).
`skills/objectstack-automation/references/integration-recipes.md` recipe
1 stays correct ("ships in the package").
## Verification
<details><summary>Derived gate families, local, head f6ca481</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` → 90 commands; all 90 run, exit 0 each (two
first exited 3, PREREQUISITE NOT MET for unbuilt packages outside this
diff — `check:skill-examples` needed `@objectstack/client-react` dist,
`check:dual-build-cjs-loads` seven package dists; built, re-run: `278
prose examples type-check across 3 surface(s)` and `113 published
require entry point(s) across 70 package(s) load`). `--ran`
reconciliation: `90 derived famil(ies) accounted for — 90 run, 0
NOT-MEASURED`. Repo-wide scans (`pnpm lint`) and `test:repo` are CI's.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 8a12a17 commit 615bb97
10 files changed
Lines changed: 628 additions & 1 deletion
File tree
- .changeset
- content/docs/automation
- packages
- cli
- src
- commands
- utils
- test
- qa/dogfood/test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
131 | 131 | | |
132 | 132 | | |
133 | 133 | | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
134 | 138 | | |
135 | 139 | | |
136 | 140 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
1122 | 1123 | | |
1123 | 1124 | | |
1124 | 1125 | | |
| 1126 | + | |
| 1127 | + | |
| 1128 | + | |
| 1129 | + | |
| 1130 | + | |
| 1131 | + | |
| 1132 | + | |
| 1133 | + | |
| 1134 | + | |
| 1135 | + | |
| 1136 | + | |
| 1137 | + | |
| 1138 | + | |
| 1139 | + | |
| 1140 | + | |
| 1141 | + | |
| 1142 | + | |
| 1143 | + | |
| 1144 | + | |
| 1145 | + | |
| 1146 | + | |
| 1147 | + | |
| 1148 | + | |
| 1149 | + | |
1125 | 1150 | | |
1126 | 1151 | | |
1127 | 1152 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
| |||
804 | 805 | | |
805 | 806 | | |
806 | 807 | | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
807 | 831 | | |
808 | 832 | | |
809 | 833 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
0 commit comments