Skip to content

Commit ed0e71d

Browse files
fix(cli)!: os plugin publish parses the compiled manifest through ManifestSchema before any upload (#27910)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9D4WPVh2L9JrUnP8Q91s8oA4)_ Fixes #27810 Clause-②: no (narrowing) Metadata-protocol: narrowed Skills-docs: none — no skill under `skills/**` and no page under `content/docs` teaches what `os plugin publish` checks in the compiled manifest (`skills/objectstack-platform/references/operations.md` and `packages/cli/README.md` list the command as "upload / publish a signed .osplugin", which stays true); the QA ledger row that did name the old refusal is re-pointed here. ## 维护者速读 - 改了什么:`os plugin publish` 现在用安装与启动已在用的同一个函数(`readCompiledPluginManifest`),按 `ManifestSchema` 校验 `.osplugin` 里的编译清单,校验不过就在发任何网络请求之前拒绝;CLI 内部的 `readOspluginManifest` 也改走这个函数。 - 为什么改:以前发布只做 `JSON.parse`,旧版本打出的包(比如带已退役的 `packaging: 'manifest-deps'`)能照样传到市场,要到用户安装时才被拒;现在在发布者自己的终端就被拒,并给出 schema 的处方。 - 风险与回滚:这是收窄:清单不合 schema 的包从此发不出去(缺 `name`/`type`、多余键、畸形 `integrity` 等);合法的包照常发布,上传的 `plugin_manifest` 仍是包里原样的清单(不带解析时补上的默认值)。回滚即回退本 PR。 - 先看:`packages/cli/src/commands/plugin/publish.ts` 第 2 步(校验与原样上传两段)→ `packages/cli/src/utils/osplugin.ts` 的 `readOspluginManifest` → `packages/cli/test/plugin-publish.test.ts` 新增的拒绝与原样上传钉子。 - 两分钟验证:`pnpm --filter @objectstack/cli exec vitest run test/plugin-publish.test.ts -t manifest-deps`,预期两条通过:发布被拒、零次请求、输出含 `` packaging: `packaging: 'manifest-deps'` was removed `` 与 `Delete the key`。 - 需要你做的:无,席位落地(另需一次契约复审 PASS)。 ## What changes - **`os plugin publish`, step 2.** The compiled manifest is read with `readCompiledPluginManifest` from `@objectstack/runtime` (the helper PR #27791 gave `os plugin install` and the boot loader), right after the archive is read and before the integrity preflight, the signature read, the auth lookup and both POSTs. - `unreadable` keeps the existing `Cannot read manifest from artifact: …` refusal. - `invalid` prints `The artifact's objectstack.plugin.json is not a valid plugin manifest, so nothing was published:`, one `• path: message` line per schema issue (for `manifest-deps`, the retirement prescription), and the rebuild / re-sign remedy, then exits 1. - The hand-rolled malformed-`integrity` check is gone: the map's shape is a schema issue. `Artifact manifest is missing id or version.` stays, now reachable only from an empty `--manifest-id`. - Every field the command reads (`id`, `version`, `name`, `runtime`, `description`, `integrity`) comes from the parsed manifest. - **The uploaded `plugin_manifest` is the artifact's own manifest, unchanged. Flag for the reviewer.** `ManifestSchema` stamps two defaults at parse (`scope: 'project'`, `defaultDatasource: 'default'`), so uploading the parsed value would make the declared manifest differ from the one inside the signed bytes on every valid publish. After the helper answers `ok`, publish decodes the same entry once more for the payload only. That decode reads no rule: the verdict and every read field are the helper's. The pin `uploads the manifest the artifact carries, as it carries it` reds if `plugin_manifest` becomes the parsed value (measured, below). The helper's module is untouched. - **`readOspluginManifest`** (internal to the CLI, not in any `exports` entry; its only caller is `plugin-publish.test.ts`, so no inspect/diagnose path depends on a lenient read) parses through the same helper, throws the helper's message, and answers the parsed manifest. It is now `async` and imports `@objectstack/runtime` on call: every `os plugin` command loads `osplugin.ts`, and the runtime's root entry measured ~1.2 s to import against ~0.3 s for `@objectstack/core`, which `os plugin build` and `os plugin sign` would otherwise pay for a read they never make. `publish.ts` imports the helper statically, as `install.ts` does. - **Spec guard.** `manifest-packaging-retirement.test.ts` gains one `EXCLUDED` entry, `packages/cli/test/plugin-publish.test.ts` ("the publish door's own pin authors it to assert `os plugin publish` refuses it before any upload"), the same shape #27686 used (claim amended by the seat first). - **QA ledger.** `docs/qa/platform-checklist/areas/cli.json`, item `cli.plugin-manifest-build-contract`: the compiled-manifest clause and its verify step now name the schema refusal instead of `Artifact manifest is missing id or version.`, the `publish.ts#version` source row is re-worded, revision 6 → 7 with a history entry (claim amended by the seat first). - **Changeset.** `@objectstack/cli` minor, `fix(cli)!`, BREAKING banner, `adr-0087: not-required (no-migration-prescription)`, both declaration lines. ## Tests `packages/cli/test/plugin-publish.test.ts`, five new pins and two updated: - `readOspluginManifest` throws the `manifest-deps` prescription (`` packaging: `packaging: 'manifest-deps'` was removed ``, `Delete the key`). - `readOspluginManifest` throws a drifted manifest's issues by name (`name: `, `integrity: `). - publish refuses a `manifest-deps` artifact with zero `fetch` calls, the prescription on a `•` line, and the `os plugin build` remedy. - publish refuses a drifted manifest (no `name`, `integrity` an array) with zero `fetch` calls, naming both issues. - a valid artifact's upload carries `plugin_manifest` deep-equal to the artifact's manifest, with no `scope` / `defaultDatasource`. - the existing round-trip and absent-manifest pins, now awaiting the async read. Ablation, on disk: restoring `publish.ts` and `osplugin.ts` from `main` turns the four refusal pins red (`Tests 4 failed | 8 passed`); setting `plugin_manifest: manifest` (the parsed value) turns the verbatim pin red (`1 failed | 11 passed`). Both restored; the file then passes 12/12. ## Acceptance notes - Refusal before anything leaves the machine: the manifest read sits before the signature file read, the cloud-config read and both POSTs; the pins count `fetch` calls (0). - One parse, no second copy: no manifest rule is written in `publish.ts` or `osplugin.ts`; both ask `readCompiledPluginManifest`. - Not changed: the integrity preflight, the signature, auth and visibility paths, and the payload of a valid publish. ## Verification Head `bd6d77e2`. Gate readings (87 derived families, `pnpm lint`, typecheck, the CLI unit layer, the dogfood battery) are in the `os-dev-report` comment on #27810, read from their logs; `dispatch-gates --ran` reconciled 87 derived / 87 run / 0 NOT-MEASURED, every exit 0. --- _Generated by [Claude Code](https://claude.ai/code/session_016WV4cyHFdayr8QnKE8nszT)_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016WV4cyHFdayr8QnKE8nszT Co-authored-by: Claude <noreply@anthropic.com>
1 parent ca54eac commit ed0e71d

6 files changed

Lines changed: 152 additions & 38 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/cli': minor
3+
---
4+
5+
fix(cli)!: `os plugin publish` parses the artifact's compiled manifest through `ManifestSchema` before anything is uploaded, so a stale `.osplugin` (e.g. `packaging: 'manifest-deps'`) is refused at the publisher's terminal with the schema's prescription instead of failing later at its consumer's install
6+
7+
Clause-②: no (narrowing)
8+
Metadata-protocol: narrowed
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema is added, removed, renamed or re-shaped: `ManifestSchema` is consumed as it stands, and what changes is that a third door, `os plugin publish`, holds the compiled manifest inside an artifact to it, as `os plugin build` holds the source manifest and `os plugin install` and the boot-time loader hold the compiled one. The refusal is the schema's own message (for `manifest-deps`, the retirement prescription), and the fix is to the source manifest and a rebuild, so there is nothing new for a metadata migration to rewrite and no tombstone. -->
11+
12+
**BREAKING** accept-set narrowing at `os plugin publish` (`@objectstack/cli`).
13+
14+
- **Before.** `os plugin publish` `JSON.parse`d the artifact's `objectstack.plugin.json` and checked only that `id` and `version` were present and that `integrity` was an object. An artifact built before a schema change uploaded: one carrying the retired `packaging: 'manifest-deps'` reached the registry and was refused only at its consumer's `os plugin install`. Any other compiled-manifest drift (a missing `name` or `type`, an unknown key) uploaded the same way.
15+
- **Now.** The publish parses the compiled manifest with `readCompiledPluginManifest` (`@objectstack/runtime`), the one helper `os plugin install` and the boot-time loader already use, before any request leaves the machine.
16+
- A manifest the schema refuses stops the publish with `The artifact's objectstack.plugin.json is not a valid plugin manifest, so nothing was published:` and one line per schema issue. For `manifest-deps` that line is the retirement prescription. Neither the package row nor the version is posted.
17+
- The hand-rolled malformed-`integrity` refusal is gone: a malformed map is a schema issue now. `Artifact manifest is missing id or version.` is now reached only by an empty `--manifest-id`.
18+
- A valid artifact publishes unchanged: the uploaded `plugin_manifest` is still the manifest the artifact carries, byte for byte, without the defaults the parse stamps (`scope`, `defaultDatasource`).
19+
- `readOspluginManifest` (internal to the CLI) parses through the same helper and is now async.
20+
- **Who sees it.** Anyone publishing an `.osplugin` whose compiled manifest `ManifestSchema` refuses: one built by an `os plugin build` from before a schema change, or one assembled by hand.
21+
22+
**The remedy.** Fix the plugin's source `objectstack.plugin.json` as the refusal says (for `manifest-deps`: delete the `packaging` key; `bundled` is the default and the only strategy), rebuild the artifact with `os plugin build`, re-sign it with `os plugin sign`, and publish it again. An artifact `os plugin build` produced from a valid manifest publishes unchanged.

‎docs/qa/platform-checklist/areas/cli.json‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,7 +1716,7 @@
17161716
"title": "os plugin build enforces the manifest at the packaging boundary: an invalid manifest is refused with located errors, `bundled` is the one packaging strategy (the retired `manifest-deps` is refused at the manifest, a native addon is refused by name), and the compiled manifest the artifact carries is readable back",
17171717
"since": "v15",
17181718
"status": "active",
1719-
"revision": 6,
1719+
"revision": 7,
17201720
"priority": "P2",
17211721
"surface": "cli",
17221722
"personas": [
@@ -1772,9 +1772,9 @@
17721772
"evidence": "the three refusals with their exit codes and the listings taken after them"
17731773
},
17741774
{
1775-
"clause": "the artifact carries a COMPILED manifest — the authored manifest plus `main: \"dist/index.mjs\"` and an `integrity` map of archive-relative path → sha256 SRI digest, with the manifest itself and SIGNATURE excluded from the map — and os plugin publish reads that manifest back out of the artifact BEFORE any network call, refusing 'Artifact manifest is missing id or version.' when it cannot",
1775+
"clause": "the artifact carries a COMPILED manifest — the authored manifest plus `main: \"dist/index.mjs\"` and an `integrity` map of archive-relative path → sha256 SRI digest, with the manifest itself and SIGNATURE excluded from the map — and os plugin publish reads that manifest back out of the artifact BEFORE any network call and parses it through ManifestSchema (readCompiledPluginManifest, the parse os plugin install and the boot loader share), refusing one the schema refuses with 'The artifact's objectstack.plugin.json is not a valid plugin manifest, so nothing was published:' and one line per schema issue (a missing version reads `version: …`; a pre-retirement `manifest-deps` artifact reads the retirement prescription) when it cannot",
17761776
"oracle": "build",
1777-
"verify": "read the objectstack.plugin.json entry out of each archive and diff it against the authored file; then run publish against a good artifact (expect it to reach the auth refusal, proving the manifest was read) and against one whose manifest lacks a version (expect the artifact-manifest refusal, before any auth). ⚠️ The publish leg is scored on WHICH refusal arrives, not on success",
1777+
"verify": "read the objectstack.plugin.json entry out of each archive and diff it against the authored file; then run publish against a good artifact (expect it to reach the auth refusal, proving the manifest was read) and against one whose manifest lacks a version (expect the schema refusal naming `version`, before any auth). ⚠️ The publish leg is scored on WHICH refusal arrives, not on success",
17781778
"evidence": "the compiled manifest of each artifact, and the two publish outputs distinguished by their refusal"
17791779
},
17801780
{
@@ -1798,7 +1798,7 @@
17981798
"source": [
17991799
"packages/cli/src/commands/plugin/build.ts#formatZodErrors — manifest read + named refusal; ManifestSchema.safeParse + formatZodErrors; the explicit id/version guard; packaging default and the Loaded line; entry resolution; the bundled-only esbuild call (only `@objectstack/*` external); findNativeAddons and the native-addon refusal ahead of the integrity map; computeIntegrity + the compiled manifest; the artifact write and its printed block",
18001800
"packages/cli/src/utils/osplugin.ts#MANIFEST_FILENAME — MANIFEST_FILENAME = 'objectstack.plugin.json'; OSPLUGIN_EXT; computeIntegrity (manifest and SIGNATURE excluded, keys sorted); the ustar writer with mtime pinned to 0; readOspluginManifest",
1801-
"packages/cli/src/commands/plugin/publish.ts#version — artifact discovery; readOspluginManifest before any network call; the id/version refusal; the unsigned-artifact notice; the auth refusal",
1801+
"packages/cli/src/commands/plugin/publish.ts#version — artifact discovery; the compiled manifest parsed through readCompiledPluginManifest (ManifestSchema) before any network call and the schema refusal; the empty `--manifest-id` refusal; the unsigned-artifact notice; the auth refusal",
18021802
"packages/cli/src/commands/plugin/install.ts#PluginInstall — the install-time re-verification the integrity negative points at (verifyIntegrity over the unpacked entries, `integrity_mismatch`; a compiled manifest ManifestSchema refuses, a malformed integrity map included, is `manifest_invalid` one step earlier through readCompiledPluginManifest; nothing installed) and the install-time tier warning; not scored here",
18031803
"packages/core/src/security/plugin-artifact-integrity.ts#verifyIntegrity — the one integrity verifier, shared by the publish preflight and the install-time check; not scored here",
18041804
"packages/runtime/src/installed-plugin-loader.ts#dispatchPluginRuntime — the boot-time tier dispatch point the trust-tier negative points at (`node` only; `sandbox` / `worker` refused by name; an undeclared tier refused); not scored here",
@@ -1844,6 +1844,12 @@
18441844
"date": "2026-10-30",
18451845
"change": "one source row re-pointed, no clause changed: os plugin install no longer has an `integrity_malformed` refusal. The compiled manifest is parsed through ManifestSchema first (readCompiledPluginManifest, the parse install and the boot loader share), so a malformed integrity map is refused as `manifest_invalid` before the integrity step; `integrity_mismatch` is unchanged. Re-verified against install.ts and compiled-plugin-manifest.ts at this PR's head, no qa-run record",
18461846
"ref": "#27686"
1847+
},
1848+
{
1849+
"revision": 7,
1850+
"date": "2026-10-30",
1851+
"change": "the compiled-manifest clause's publish half (clause and verify) and the publish.ts source row re-pointed: os plugin publish no longer hand-parses the compiled manifest. It parses it through ManifestSchema with readCompiledPluginManifest (the helper os plugin install and the boot loader share), so a manifest that lacks a version is refused as 'not a valid plugin manifest' with the schema's issue line (`version: …`), not 'Artifact manifest is missing id or version.' — that sentence is now reachable only from an empty `--manifest-id`. The uploaded plugin_manifest stays the artifact's own manifest, unchanged",
1852+
"ref": "#27810"
18471853
}
18481854
]
18491855
},

‎packages/cli/src/commands/plugin/publish.ts‎

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77
* Flow:
88
* 1. Read the `.osplugin` bytes + the detached `.sig` (publisher signature).
99
* 2. Extract the compiled `objectstack.plugin.json` from inside the
10-
* artifact (id / version / name / runtime / permissions / integrity),
11-
* then preflight the artifact files against the manifest's declared
12-
* per-file `integrity` digests (ADR-0025 §3.2) — refuse on any
10+
* artifact and parse it through `ManifestSchema`
11+
* (`readCompiledPluginManifest`, the read `os plugin install` and the
12+
* boot-time loader make too): a manifest the schema refuses (one built
13+
* before a value was retired, such as `packaging: 'manifest-deps'`) is
14+
* refused here with the schema's own words, before anything leaves the
15+
* machine. Then preflight the artifact files against the manifest's
16+
* declared per-file `integrity` digests (ADR-0025 §3.2) — refuse on any
1317
* mismatch / missing / extra file; an absent map skips the check
1418
* (the field is optional).
1519
* 3. POST /cloud/packages — ensure the sys_package row exists
@@ -39,6 +43,7 @@ import {
3943
type ArchiveFile,
4044
} from '../../utils/osplugin.js';
4145
import { verifyIntegrity, formatIntegrityViolation } from '@objectstack/core';
46+
import { readCompiledPluginManifest } from '@objectstack/runtime';
4247

4348
interface PostResult { ok: boolean; status: number; body: any; error?: string }
4449

@@ -103,21 +108,42 @@ export default class PluginPublish extends Command {
103108
const checksum = sha256Hex(bytes);
104109
const base64 = Buffer.from(bytes).toString('base64');
105110

106-
// 2. Extract the compiled manifest from inside the artifact. ────────
107-
let manifest: Record<string, any>;
111+
// 2. Extract the compiled manifest from inside the artifact, through
112+
// `ManifestSchema` (the read `os plugin install` and the boot-time
113+
// loader make too): an artifact built before a schema change is
114+
// refused here, with the schema's own words, not at its consumer's
115+
// install. ⛔ No manifest rule is restated in this command. ────────
108116
let archiveFiles: ArchiveFile[];
109117
try {
110118
archiveFiles = readTarGz(bytes);
111-
const entry = archiveFiles.find((f) => f.path === MANIFEST_FILENAME);
112-
if (!entry) throw new Error(`${MANIFEST_FILENAME} not found in artifact`);
113-
manifest = JSON.parse(Buffer.from(entry.data).toString('utf8')) as Record<string, any>;
114119
} catch (err: any) {
115120
printError(`Cannot read manifest from artifact: ${err?.message ?? err}`);
116121
this.exit(1);
117122
return;
118123
}
119-
const id = String(flags['manifest-id'] ?? manifest.id ?? '').trim();
120-
const version = String(manifest.version ?? '').trim();
124+
const read = readCompiledPluginManifest(archiveFiles);
125+
if (!read.ok) {
126+
if (read.reason === 'unreadable') {
127+
printError(`Cannot read manifest from artifact: ${read.message}`);
128+
} else {
129+
printError(`The artifact's ${MANIFEST_FILENAME} is not a valid plugin manifest, so nothing was published:`);
130+
for (const issue of read.issues) console.log(` • ${issue}`);
131+
console.log('\n Fix the source manifest as above and rebuild the artifact with `os plugin build` (then re-sign it with `os plugin sign`), and publish the fresh artifact.');
132+
}
133+
this.exit(1);
134+
return;
135+
}
136+
const manifest = read.manifest;
137+
// What is uploaded is the manifest the artifact carries, as it carries
138+
// it: the parse above stamps the schema's defaults (`scope`,
139+
// `defaultDatasource`), which would make the declared manifest differ
140+
// from the signed bytes' own. The entry is there and is a JSON object:
141+
// the read above answered `ok`.
142+
const declaredManifest = JSON.parse(
143+
Buffer.from(archiveFiles.find((f) => f.path === MANIFEST_FILENAME)!.data).toString('utf8'),
144+
) as Record<string, unknown>;
145+
const id = String(flags['manifest-id'] ?? manifest.id).trim();
146+
const version = manifest.version;
121147
const displayName = String(flags['display-name'] ?? manifest.name ?? id).trim();
122148
if (!id || !version) { printError('Artifact manifest is missing id or version.'); this.exit(1); return; }
123149
printStep(`${id}@${version} (${(bytes.byteLength / 1024).toFixed(1)} KB, runtime: ${manifest.runtime ?? 'unset'})`);
@@ -126,21 +152,13 @@ export default class PluginPublish extends Command {
126152
// bytes against the manifest's own declared per-file digests before
127153
// upload. Absent map = permissive by contract (the field is
128154
// `.optional()`; artifacts built before integrity computation stay
129-
// publishable). The installing side re-verifies at unpack
130-
// (`os plugin install`, ADR-0025 §3.5 M1); this preflight is the
131-
// publisher's own check, and the cloud control plane runs none.
132-
const declaredIntegrity = manifest.integrity;
133-
if (
134-
declaredIntegrity !== undefined && declaredIntegrity !== null
135-
&& (typeof declaredIntegrity !== 'object' || Array.isArray(declaredIntegrity))
136-
) {
137-
printError('Artifact manifest has a malformed `integrity` map (expected an object of path → digest). Rebuild with `os plugin build`.');
138-
this.exit(1);
139-
return;
140-
}
155+
// publishable). The map's shape is `ManifestSchema`'s, checked above.
156+
// The installing side re-verifies at unpack (`os plugin install`,
157+
// ADR-0025 §3.5 M1); this preflight is the publisher's own check, and
158+
// the cloud control plane runs none.
141159
const integrityCheck = verifyIntegrity(
142160
archiveFiles,
143-
declaredIntegrity as Record<string, string> | undefined,
161+
manifest.integrity,
144162
{ exempt: [MANIFEST_FILENAME, SIGNATURE_FILENAME] },
145163
);
146164
if (!integrityCheck.ok) {
@@ -203,7 +221,7 @@ export default class PluginPublish extends Command {
203221
version,
204222
artifact_kind: 'plugin',
205223
osplugin: base64,
206-
plugin_manifest: manifest,
224+
plugin_manifest: declaredManifest,
207225
artifact_checksum: checksum,
208226
is_pre_release: flags['pre-release'] || /-(alpha|beta|rc|dev|preview|staging|pr)/i.test(version),
209227
};

‎packages/cli/src/utils/osplugin.ts‎

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import { createHash } from 'node:crypto';
3939
import { gzipSync } from 'node:zlib';
4040
import { readTarGz, type ArchiveFile } from '@objectstack/core';
41+
import type { ObjectStackManifestParsed } from '@objectstack/spec/kernel';
4142

4243
export { readTar, readTarGz, sha256Hex, MAX_UNPACKED_ARTIFACT_BYTES, type ArchiveFile } from '@objectstack/core';
4344

@@ -135,11 +136,21 @@ export function createTarGz(files: ArchiveFile[]): Buffer {
135136
}
136137

137138
/**
138-
* Extract + parse the compiled `objectstack.plugin.json` from an `.osplugin`
139-
* blob. Throws if the manifest is absent or not valid JSON.
139+
* Extract the compiled `objectstack.plugin.json` from an `.osplugin` blob and
140+
* parse it through `ManifestSchema` — `readCompiledPluginManifest`, the one
141+
* read `os plugin install` and the boot-time loader make too. Throws with the
142+
* helper's message when the manifest is absent, not a JSON object, or refused
143+
* by the schema (a retired value such as `packaging: 'manifest-deps'` carries
144+
* its prescription in that message).
145+
*
146+
* The helper is imported on call, not at the top: every `os plugin` command
147+
* loads this module, and `@objectstack/runtime`'s root entry would add about
148+
* a second to `os plugin build` and `os plugin sign`, which never read a
149+
* compiled manifest back.
140150
*/
141-
export function readOspluginManifest(blob: Uint8Array): Record<string, unknown> {
142-
const entry = readTarGz(blob).find((f) => f.path === MANIFEST_FILENAME);
143-
if (!entry) throw new Error(`${MANIFEST_FILENAME} not found in artifact`);
144-
return JSON.parse(Buffer.from(entry.data).toString('utf8')) as Record<string, unknown>;
151+
export async function readOspluginManifest(blob: Uint8Array): Promise<ObjectStackManifestParsed> {
152+
const { readCompiledPluginManifest } = await import('@objectstack/runtime');
153+
const read = readCompiledPluginManifest(readTarGz(blob));
154+
if (!read.ok) throw new Error(read.message);
155+
return read.manifest;
145156
}

0 commit comments

Comments
 (0)