feat(cloud-connection,cli): install-local POST reports storageDir, os package install quotes it - #7288
Conversation
…I quotes it (#6721) The install POST's `data` block now carries `storageDir: this.storageDir` — the same resolved ledger directory (`LocalManifestSource.dir`) the GET listing sibling already served. `os package install` runs on a different machine and never touches the runtime's disk, so before this the CLI could only describe the cache location by literal, and that literal was the plugin's default — wrong for every host that configures `storageDir`. The CLI now quotes the reported value, and prints NO directory sentence when the response lacks the field (an older host). No literal fallback: a `??` there would reinstate the defect PD #12 forbids and #5996 deleted. Fixes #6721 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0158ZQo7LiHSxGWpYKuPq1wu
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 2 package(s): 19 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also reference the affected code. These are read-only:
|
Fixes #6721
What changed
Two endpoints of
MarketplaceInstallLocalPlugindisagreed about one fact, and thedisagreement was load-bearing for
os package install.Producer —
packages/cloud-connection/src/marketplace-install-local-plugin.ts.The install POST's
datablock now carriesstorageDir: this.storageDir, read fromthe same field the GET listing (
handleList) already served.this.storageDiristhis.ledger.dir, resolved once in the ctor byLocalManifestSource— the host'sconfigured
storageDirwhen it set one,DEFAULT_INSTALLED_PACKAGES_DIRwhen it didnot. The change is purely additive: an already-in-hand value put into a response, no
existing key touched, no migration, nothing obliged to read it.
Consumer —
packages/cli/src/commands/package/install.ts. The post-install hintquotes the reported directory instead of a literal:
When the response has no
storageDir— a runtime older than the producer half — theCLI prints no directory sentence. There is no fallback. The long
#6643commentblock explaining why the literal had to stay is deleted along with the literal.
Why one PR across two packages
The 2026-08-09 seat grade on #6721 ruled this shape, and the reason is the rule itself
rather than convenience:
DEFAULT_INSTALLED_PACKAGES_DIR的取值复述成字面量(#5996 三件套之外的残留) #6643 measured that no consumer-side answer exists.A constant resolved in the CLI describes the machine typing the command, not the
remote host that stored the manifest; it is in any case only the plugin's default,
wrong the moment a host configures
storageDir; and a static import of@objectstack/cloud-connectionwould make a pure-HTTP command fail at module loadwherever that package is absent or unbuilt. Every CLI reference to that package is a
guarded dynamic load for exactly this reason. So the consumer half cannot be correct
until the producer half lands — splitting the PR would leave it dangling on a
one-line change.
install-localfinds the plugin andpackages/cli/src/commands/package/install.ts; the other hits(
packages/runtime/src/domains/packages.ts,packages/client/src/index.ts,packages/objectql/src/plugin.ts) are prose references to the manifest shape, notreaders of this response. Nothing else observes the new field.
does not know about it, so the producer half carries no compatibility risk of its own.
The binding delivery criterion from that seat grade, quoted verbatim:
That is why the absent case prints nothing rather than degrading gracefully into the
old string: a
?? '.objectstack/installed-packages/'would read as a kindness to olderhosts and would reinstate precisely the defect Prime Directive #12 forbids and #5996
deleted — a consumer inventing a value the producer declined to state. An empty or
non-string value is treated as the same "the host did not state it" case, not as a
reason to print a blank path.
Changeset level
.changeset/install-local-post-storage-dir.mdbumps@objectstack/cloud-connectionminor and
@objectstack/clipatch. The producer half adds a new field to apublic HTTP response — additive capability, so minor. The consumer half changes only
what an existing command prints, and changes it from a wrong path to a right one (or
to silence) — a presentation fix, so patch.
Tests
New, both halves:
packages/cloud-connection/src/marketplace-install-local-storage-dir.test.ts— POSTdata.storageDirequals the resolved ledger dir for a host with a non-defaultstorageDir(the configuration the old literal misreported, and the only one thatseparates "quotes the resolved value" from "restates the default"); equals the ctor
default when the host configured nothing (run under a
chdirinto a temp dir, sincethat path genuinely writes relative to
process.cwd()); and equals the GET listing'svalue string-for-string, which is the divergence this closes.
packages/cli/test/package-install-storage-dir.test.ts— the sentence quotes theresponse value verbatim; the sentence is absent when the response omits
storageDir, and absent for empty / whitespace / null, while the install itself isstill reported.
Reverse verification
Predicted direction before running, and both matched.
storageDirfrom the POST response ⇒ all 3 producer cases red(
expected undefined to be '/tmp/mil-storage-dir-…'). Restored.|| '.objectstack/installed-packages/') ⇒ the 4absence cases red (
expected … not to contain 'cached') while the happy path staysgreen. That is the delivery criterion's teeth: a happy-path-only test would have
stayed green through this edit. Restored.
Local gates
Dependency closures were built first (
--filter '@objectstack/cloud-connection^...',--filter '@objectstack/cli^...'— the^...suffix, i.e. upstream dependencies, whichis the direction that matters here since neither change narrows an exported type).
check:type-check-debtwas run after the full build closure and raises no ledger entry;the two new test files add no TEST_DEBT.
Generated by Claude Code