{{ extension.name }}
diff --git a/.changeset/interaction-quality.md b/.changeset/interaction-quality.md
deleted file mode 100644
index 37df82c7..00000000
--- a/.changeset/interaction-quality.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-'@inkcre/client-web': patch
-'@inkcre/core': patch
----
-
-按任务精简 Settings、Peer 与 Extension 界面,统一页面和浏览器标题,补齐场景化加载、刷新失败保留、保存与取消的动作反馈。修正重复检索、任务不存在及日志读取失败的可观察结果,保留失败后的恢复入口。
diff --git a/.changeset/schema-forms-tabs.md b/.changeset/schema-forms-tabs.md
deleted file mode 100644
index 01d5bf10..00000000
--- a/.changeset/schema-forms-tabs.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-'@inkcre/client-web': patch
-'@inkcre/core': patch
-'@inkcre/ext-dev-utils': patch
-'@inkcre/ext-mail': patch
-'@inkcre/ext-memos': patch
-'@inkcre/ext-twitter': patch
----
-
-Source 与 Peer 配置使用可视化表单,保留完整 JSON 编辑模式;Peers 页面可确认删除离线 Peer 并清理其扩展启用记录。Extension 浏览与版本选择交由 Registry,Web 接收精确版本并确认安装;Web 与 Module Federation 扩展统一升级正式 UI 2.2.0,Peer AI 凭据在 schema 中标记为密码输入。
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9baa557f..96f62b15 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -41,7 +41,7 @@ jobs:
run: go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 .github/workflows/*.yml
core-release:
- name: Resolve stable core release
+ name: Resolve admitted Core release
runs-on: ubuntu-latest
outputs:
contract_revision: ${{ steps.release.outputs.contract_revision }}
@@ -64,7 +64,10 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- - name: Resolve stable once to an immutable release
+ - name: Verify canonical candidate admission boundaries
+ run: node --test scripts/test-core-release.mjs
+
+ - name: Resolve admitted Core once to an immutable release
id: release
run: node scripts/resolve-core-release.mjs
@@ -121,7 +124,7 @@ jobs:
--core-package packages/core/package.json
--artifact-directory extensions/twitter/dist/client-web
- - name: Verify publication recovery over HTTP
+ - name: Verify independent MF publication
run: node --test scripts/test-extension-publication.mjs scripts/test-core-sdk-publication.mjs
peer-database:
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 9746ff32..fe3f1823 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -1,7 +1,7 @@
# Repository Architecture
This monorepo implements three logical client-side units across shared packages, applications, and
-independently versioned Extension producers.
+independently built Extension producers.
```text
Vue web application -----------+
@@ -32,8 +32,9 @@ locations, not Unit boundaries.
inbound → non-delegating local implementation.
3. Content: `Block.getHydratedContent()` → exact Resolver → solved value or safe browser handle →
registered renderer.
-4. Native Extension: durable enabled intent → exact Release and Host-range preflight → native
- manifest → initialize/activate; disable reverses lifecycle before removing Peer intent.
+4. Native Extension: exact Peer selection and enabled intent → exact Release and Host-range preflight →
+ native manifest → initialize/activate; disable first records false intent, then releases native
+ runtime resources.
## Realization Surfaces
@@ -41,7 +42,7 @@ locations, not Unit boundaries.
configuration/authentication, and Extension Host.
- `apps/client-web/` - Vue SPA, graph/list surfaces, settings, and static Vite artifact.
- `apps/client-webext/` - WXT browser surfaces and content scripts.
-- `extensions/` - independently versioned native Module Federation producers.
+- `extensions/` - independently built native Module Federation producers.
- `packages/ext-dev-utils/` - producer development support consumed by applications.
The package graph is ESM-first. Workspace Vite and WXT applications consume `@inkcre/core` source
diff --git a/apps/client-web/CHANGELOG.md b/apps/client-web/CHANGELOG.md
index 915d55eb..f4b85846 100644
--- a/apps/client-web/CHANGELOG.md
+++ b/apps/client-web/CHANGELOG.md
@@ -1,5 +1,20 @@
# @inkcre/client-web
+## 0.1.3
+
+### Patch Changes
+
+- 7307dda: 按任务精简 Settings、Peer 与 Extension 界面,统一页面和浏览器标题,补齐场景化加载、刷新失败保留、保存与取消的动作反馈。修正重复检索、任务不存在及日志读取失败的可观察结果,保留失败后的恢复入口。
+- 插件配置与业务状态在部署内共享;精确分发版本和启用意图分别保存在每个 Peer。Client Web 在同一插件入口管理各 Peer 的安装、版本和运行状态,并采用 Extension management v2。
+
+ 原生 SDK 不再提供全局插件版本与 enabled 数组。新版 MF 使用 0.4 SDK 的参与记录接口,历史 MF 需按其 SDK 范围运行或明确选择新版;不会自动改写旧版本选择。Python 与 MF 分别从自己的仓库发布,插件负责共享数据契约兼容。
+
+- 7307dda: Source 与 Peer 配置使用可视化表单,保留完整 JSON 编辑模式;Peers 页面可确认删除离线 Peer 并清理其扩展启用记录。Extension 浏览与版本选择交由 Registry,Web 接收精确版本并确认安装;Web 与 Module Federation 扩展统一升级正式 UI 2.2.0,Peer AI 凭据在 schema 中标记为密码输入。
+- Updated dependencies [7307dda]
+- Updated dependencies
+- Updated dependencies [7307dda]
+ - @inkcre/core@0.4.0
+
## 0.1.2
### Patch Changes
diff --git a/apps/client-web/package.json b/apps/client-web/package.json
index 576b9cbc..f72ce257 100644
--- a/apps/client-web/package.json
+++ b/apps/client-web/package.json
@@ -1,6 +1,6 @@
{
"name": "@inkcre/client-web",
- "version": "0.1.2",
+ "version": "0.1.3",
"private": true,
"type": "module",
"scripts": {
@@ -12,7 +12,7 @@
},
"dependencies": {
"@inkcre/core": "workspace:*",
- "@inkcre/extension-runtime-client-web": "github:InKCre/ext-reg#57dcd80289285620a1fd27a0a48027e56c4ce7b1&path:/runtimes/client-web",
+ "@inkcre/extension-runtime-client-web": "https://github.com/InKCre/ext-reg/releases/download/runtime-client-web-v0.4.0/inkcre-extension-runtime-client-web-0.4.0.tgz",
"@inkcre/ui-web": "2.2.1",
"@module-federation/runtime": "^0.21.4",
"@supabase/postgrest-js": "^2.84.0",
diff --git a/apps/client-web/src/components/extension/extensionCard/extensionCard.md b/apps/client-web/src/components/extension/extensionCard/extensionCard.md
index 7ef01361..fdb7e70d 100644
--- a/apps/client-web/src/components/extension/extensionCard/extensionCard.md
+++ b/apps/client-web/src/components/extension/extensionCard/extensionCard.md
@@ -1,28 +1,21 @@
# ExtensionCard
-A component that displays an installed Extension and opens focused Peer selection for enablement changes.
+Displays one deployment Extension and its independent native participation on each Peer.
-## Props
+The `extension` prop supplies shared identity and configuration; `participations` supplies exact
+Peer/distribution/version selections and enabled intent. `peers`, `livePeerIds` and `currentPeerId`
+identify operation targets and determine local runtime, remote management or offline intent routing.
+`peerSelectionDisabled` prevents mutation while target discovery is unavailable.
-- `extension` (InstalledExtension, required): the canonical installed row
-- `peers` (Peer[], required): available Peers, including this browser
-- `livePeerIds` (ReadonlySet {{ extension.name }}
- {{ t('extension.enabledPeerCount', { count: extension.enabled.length }) }}
-
+ {{
+ pin
+ ? `${distributionLabel(pin.distribution)} · ${pin.version}`
+ : t('extension.notInstalled')
+ }}
+ {{ statusText(peer.id) }}
+ {{ t('extension.unavailableOnPeer') }}
+
+ {{ errors[peer.id] || status?.error }}
+
- {{ t('extension.uninstallDisabled') }}
- {{ t('extension.uninstallConfirm') }} {{ operationError }} {{ t('extension.documentationMissing') }}
- {{ t('extension.desiredStateExplanation') }}
+
+ {{ t('extension.distribution') }}: {{ distributionLabel(distributionModel) }}
+
+ {{
+ t('extension.noPublishedDistribution', {
+ distribution: distributionLabel(distributionModel),
+ })
+ }}
- {{ error }}
+
+ {{ t('extension.publishedDistributions') }}:
+ {{ knownPublishedFormats.map(distributionLabel).join(', ') }}
+ {{ errors[selectedPeer.id] }}
+ {{ operationError }} {{ operationError }}{{ t('extension.usedOnPeers') }}
+
- {{ t('extension.installedVersion', { version: installedMatch.version }) }}
+
+ {{ t('extension.noSupportedPeer') }}
-+
{{ t('extension.connectBeforeInstall') }}
{{ installError }}
{{ t('extension.installedUnavailable') }}
diff --git a/apps/client-webext/CHANGELOG.md b/apps/client-webext/CHANGELOG.md index 3ba8127d..f552ef4e 100644 --- a/apps/client-webext/CHANGELOG.md +++ b/apps/client-webext/CHANGELOG.md @@ -1,5 +1,14 @@ # @inkcre/client-webext +## 0.1.3 + +### Patch Changes + +- Updated dependencies [7307dda] +- Updated dependencies +- Updated dependencies [7307dda] + - @inkcre/core@0.4.0 + ## 0.1.2 ### Patch Changes diff --git a/apps/client-webext/package.json b/apps/client-webext/package.json index fa11129e..6ce54332 100644 --- a/apps/client-webext/package.json +++ b/apps/client-webext/package.json @@ -1,6 +1,6 @@ { "name": "@inkcre/client-webext", - "version": "0.1.2", + "version": "0.1.3", "private": true, "description": "InKCre's Web extension", "type": "module", diff --git a/contracts/core-release.json b/contracts/core-release.json index 1ec5c79e..34ced64f 100644 --- a/contracts/core-release.json +++ b/contracts/core-release.json @@ -5,5 +5,10 @@ "runtime_images": { "pgvector": "pgvector/pgvector:pg17@sha256:d2ef61f42ef767baa5a1475393303cc235bcd92febd9d7014eddb48b41f3bad0", "postgrest": "postgrest/postgrest:v14.15@sha256:2f8e7b656f09db697a8875177694b417b35cb76c21370de07fc54e711e902326" + }, + "canonical_candidate": { + "image": "ghcr.io/inkcre/core-py@sha256:c31a852f60b2aed5e4ad3df1352b135884a44fb15d156f08354baf8f468e8071", + "source_revision": "c5a8429307442f446adb8816c6cc5c9fa1eb2a58", + "contract_revision": "extension-peer-participation-v2" } } diff --git a/docs/30-unit-tdd/info-base.md b/docs/30-unit-tdd/info-base.md index c7d54dcc..457ed89a 100644 --- a/docs/30-unit-tdd/info-base.md +++ b/docs/30-unit-tdd/info-base.md @@ -51,7 +51,7 @@ or solved meaning. ## Exact Resolver Contracts `Resolver.getClass(id)` requires an exact registered class. Registration is idempotent only for -the same class and rejects collisions; there is no default decoder. Core bootstrap independently +the same class and rejects collisions; there is no default decoder. `Resolver.unregister(id, exactClass)` removes only that class’s registration and returns false when no matching owner remains. Stale module cleanup cannot remove another module’s class. Core bootstrap independently registers the versioned text, HTML, image, audio, video, PDF, EPUB, ZIP, and file contracts. Extensions register their own namespaced, versioned contracts rather than aliases for retired IDs. @@ -87,7 +87,7 @@ exists while open/render/download remains supported. the registered component, and disposes its Resolver on unmount. Graph and editor surfaces use this path and never fall back to the persisted pointer string. -`ResolverCache` reuses instances by Block identity plus `updated_at` snapshot. Replacement, +`ResolverCache` first requires a currently registered class, then reuses instances by Block identity, `updated_at` snapshot, and that exact class identity. A disabled registration cannot return a cached instance; a replacement class disposes and replaces the old instance on the next read. Replacement, targeted invalidation, and clearing dispose old instances. Resolver-local hydration, relation, and solved-content caches still obey `refresh`; instance reuse does not change authority. diff --git a/docs/30-unit-tdd/native-extension-runtime.md b/docs/30-unit-tdd/native-extension-runtime.md index 886e819e..7b8df1f9 100644 --- a/docs/30-unit-tdd/native-extension-runtime.md +++ b/docs/30-unit-tdd/native-extension-runtime.md @@ -3,7 +3,7 @@ ## Boundary The native Extension Runtime spans the browser Web Extension Host, its SDK-owned durable models, and -independently versioned Module Federation producers. It owns compatibility preflight, executable +independently built Module Federation producers. It owns compatibility preflight, executable loading, runtime lifecycle, and reconciliation with durable enabled intent. Release publication, Registry delivery, and deployment workflow remain outside this Unit TDD; cross-unit Extension and Registry contracts remain in [`../_shared/`](../_shared/). @@ -12,15 +12,15 @@ Resolver/content producer semantics belong to [Info-Base](info-base.md). Registr artifact delivery belong to [First-party Extension Delivery](../40-deployment/native-extension-delivery.md). -There is one native format: an Extension Release may associate a Module Federation distribution. -The Host consumes that association directly. `@inkcre/extension-runtime-client-web`, maintained in +The browser supports the Module Federation native distribution. Registry v2 resolves an exact +`(name, module_federation, version)` release; other Peers independently select their native distributions. `@inkcre/extension-runtime-client-web`, maintained in ext-reg, owns the Web Host and Registry consumers. `@inkcre/core` owns the shared models, Peer transport, and browser configuration. There is no generic target matcher or cross-format artifact manifest. ## Producer and Host Contract -Each producer is an independently versioned package. Its default export preserves the optional +Each producer is an independent build package; its package version owns its MF release. Its default export preserves the optional native lifecycle methods `initialize`, `activate`, `deactivate`, and `dispose`; host-consumed capabilities come from its package entry rather than playground bootstrap code. Extension code imports `@inkcre/core` directly. @@ -50,36 +50,33 @@ passes directly to the current native Module Federation implementation. ## Durable State and Peer Intent -The Runtime uses `ExtensionModel` from the shared `@inkcre/core` instance for installed state, -configuration, and per-Peer enabled intent. There is no application-owned `ExtensionStatePort` or -second PostgREST adapter. The application chooses the targeted Peers for each enable or disable -action: current-runtime operations use its local Host, online remote Hosts receive management -commands, and offline enablement changes update desired state through the SDK model. The browser -uses `PeerManager.listLive()` for this routing decision, matching the Peers page rather than -comparing a lease timestamp with the browser clock. Selecting -several Peers performs independent per-Peer operations, preserves successful changes when another -Peer fails, then reads the canonical installed row before offering another decision. It does not -invent a batch transaction or retry an ambiguous remote outcome. - -`listAdvertisedExtensionManagementPeers` and `manageExtensionOnPeer` in the Runtime own the shared -management capability contract. The latter delegates once to the exact Peer through the SDK's -`PeerManager`, validates the installed-row response, and preserves unknown outcomes. Consumers do -not retry mutations or choose a replacement Peer automatically. Response bodies are not included -in management errors because configuration validation may echo credentials. - -Installed state records exact name and version plus the set of enabled Peer IDs. Registry Web owns -catalog search and version browsing; its link returns only a name and exact version to the -client-web Extensions page. Opening the link reads the published Release from this deployment's -configured Registry and shows a confirmation, but does not write installed state. Confirmation -uses the deployment-scoped Web Runtime install operation without selecting a Peer or requiring a -particular Host distribution. A Python-only Release can therefore be installed from the Web -interface. Installation does not enable either Host, and the shared version still affects every -Peer. Reopening an installed version does not silently change it; version changes remain an -explicit management operation. - -Version change and uninstall are refused while any Peer remains enabled or a local runtime is -running. Startup reads canonical installed state and starts only entries enabled for the current -Peer. Shutdown stops volatile runtimes without changing durable enabled intent. +The shared `ExtensionModel` owns one deployment identity, display name and configuration. Business +state remains deployment-scoped and is never cleared by native version changes. The +`ExtensionParticipationModel` owns each `(name, peer_id)` selection: exact distribution/version, +enabled intent and configuration schema from that exact selection. No global package version or +shared enabled array remains writable. + +The Extension card displays a single plugin with a row for each Peer. Installation and version +changes explicitly target that row. The current browser uses its local Host; an online remote Peer +uses `core.extension.management.v2`; offline operations update an existing exact participation. +A new remote installation requires a real supported-distribution observation. Peer names and +application versions do not imply format support. Missing MF releases remain unavailable choices; +a Python-only plugin does not get a failing browser enable action. + +Management responses contain supported distributions and actual loaded versions, running state, +restart requirements and errors. Database enabled intent is not proof that a sleeping Peer is running. +The consumer sends each mutation once to its exact target and preserves unknown outcomes. It does +not retry mutations on another Peer. Configuration forms identify the selected Peer/distribution/ +version that provided their schema; shared configuration has no global last-started schema. + +Registry installation links carry `name`, `distribution` and exact `version`. Opening a link only +reads the release and presents the target Peer choice; confirmation persists that exact target's +selection without enabling it. Startup reads only current-Peer enabled selections and never follows +latest. Legacy records with unresolved distribution retain their exact version until the upgraded +Host resolves its own native format. A missing release requires an explicit replacement selection. + +Removing a participation requires it to be disabled and does not remove the plugin's shared identity, +configuration, authorization state, Sources or info-base content. Another Peer's selection is unchanged. ## Documentation and Memos Connection @@ -97,7 +94,15 @@ runtime assumption. The contribution obtains the complete server URL from `memos it does not read Peer configuration or construct a Memos route. Address-read failures preserve the saved token and enabled state. Opening, selecting, and refreshing only read state. Unknown mutation outcomes require refreshing before another attempt. The PAT is masked by default; a normal help -link opens the exact MF release's connection guide. +link opens the current browser participation's exact MF release connection guide. + +The Twitter setup contribution selects an authorization Peer for OAuth requests and callbacks. It +can open from the browser's running MF distribution before the selected Peer has installed Twitter; +an explicit enable action selects an available Python release when no pin exists, then enables that +Peer. Existing exact selections are not replaced by that action. Source, Cron and collection Jobs +remain shared deployment records; choosing the authorization entry does not bind their future +execution to that Peer. The interface does not call this a collection-Peer binding or attribute its +schedule timezone to the selected OAuth entry. ## Lifecycle and Compensation @@ -107,26 +112,43 @@ The normal lifecycle is: load --> initialize --> activate --> deactivate --> dispose ``` +Mail and Twitter define their resolver classes at module import, register them during `initialize`, and unregister only their exact classes during `dispose`. A partially failed initialization is cleaned up by the Host’s existing compensation path without removing another module’s registrations. Module import therefore does not leave capabilities from a failed or stopped runtime behind. Memos has no resolver registrations. + The Host records a runtime as running only after initialization and activation finish. If either start phase fails, it compensates through the available reverse lifecycle and preserves both the original and cleanup failures when cleanup also fails. Stop deactivates only an active module and then disposes it; disposal releases the loaded runtime. -Enable starts the exact runtime before adding the current Peer to durable enabled intent. If -persistence fails, the Host stops and removes that runtime. If the persisted version changed during -enable, the Host rolls back the just-added Peer intent and requires a retry against the new exact -Release. +An explicit enable or disable command saves its exact participation intent before scheduling native +lifecycle work. Initial restore consumes enabled intent without rewriting it. The Host serializes +local lifecycle work per Extension and rechecks the selected coordinate and enabled intent before +and after executable loading, initialization and activation. A superseded candidate is disposed +without entering the running projection. Local serialization does not make direct writes by other +admitted clients atomic; fresh persistent observations remain necessary. + +An operation can persist intent and still fail during activation or shutdown. The Extension card +therefore reads both participation and actual runtime after success or failure and retains the +operation error. If selection returns an unknown outcome, only an independent read of the original +connection’s same name, Peer, distribution and version can complete the picker. This read never +replays a mutation or enables the Peer. Configuration changes, unmount and a newer picker invalidate +old callbacks; observing a pin does not establish loaded version or running state. -Disable reverses the order: it stops the runtime before removing durable enabled intent. If state -persistence fails, it restarts the previous exact installed runtime to restore correspondence. -Combined failures retain both the persistence and compensation errors; they must not be flattened -into apparent success. Startup isolates failures per enabled extension, retains each runtime error -for inspection, and reports an aggregate after attempting the remaining entries. +Version catalogs are read only after an explicit version choice. A native format with no published +release is distinguished from an unknown or failed read. The card shows an unavailable target only +when all formats supported by that Peer are known to lack published releases. A Python-only +catalog can explain why the browser has no compatible choice without guessing from application +names. An explicit card refresh discards those catalog observations and allows another read. + +The Client Web shell opens database views before native Extension restore finishes. A cold deep link can therefore encounter an unregistered Extension resolver. The content popup retries that specific lookup failure after the Host completes restore, while preserving other read failures. Extension cards wait for the same restore before observing current-Peer runtime status; a persisted enabled intent alone does not establish that the module is running. + +A restore attempt isolates module failures. Completing the attempt, including an aggregate failure, allows registered resolvers to serve content and exposes each module’s actual status; an unknown resolver after that attempt remains a visible error. ## Invariants -- Canonical installed/enabled state is accessed through the shared SDK's `ExtensionModel`. +- Shared identity/configuration and per-Peer native selection have separate SDK models. - Compatibility is proven before native executable fetch. - The exact Release and native manifest association remain stable through one start attempt. - Durable enabled intent and volatile runtime are reconciled with explicit compensation. - Producer identity and lifecycle are native Module Federation contracts, not deployment scripts. + +Settings 在切换数据库地址、数据库签名密钥或 Peer ID 之前排空旧 Host,连接成功后创建绑定新 Peer 的 Host。同一连接只调整租约选项时保留 Host;旧连接的恢复和管理回调不能改变新 Host 的状态。 diff --git a/docs/40-deployment/native-extension-delivery.md b/docs/40-deployment/native-extension-delivery.md index 32c96e38..bf1ee79c 100644 --- a/docs/40-deployment/native-extension-delivery.md +++ b/docs/40-deployment/native-extension-delivery.md @@ -24,7 +24,7 @@ Extension packages. On a protected `main` push, snapshots and SHA-256 digests as the immutable `first-party-extension-candidate` Actions artifact. No Registry writes occur until that artifact is retained and checked. - Producers are discovered from package metadata. A published Release with an existing Module - Federation association is a historical no-op at candidate selection. Missing associations become + Federation association is a historical no-op at candidate selection. Missing MF native coordinates become fixed candidates; the publish job downloads this run's exact artifact, prepares them with their saved provenance, uploads, publishes, and verifies every candidate from the public Registry. It never consumes pull-request CI artifacts or rebuilds a publication candidate. @@ -39,11 +39,47 @@ replace this gate with an empty-check merge or a broader credential. Client checks, Pages production, and Pages preview are separate application lifecycles. They cannot publish first-party Extensions. Local development and local verification must never publish either. -Python and Module Federation producers for one Extension must target the same exact Release when -both Hosts are needed. A published Python-only Release may receive its missing Module Federation -association through the normal publish workflow; existing associations are not overwritten. Select -the Changesets increment that reaches that Release rather than downgrading the shared installation -or manually editing generated package versions. +Python and Module Federation are independent native releases. Each producer owns +`(extension name, distribution, version)` and publishes only that distribution through Registry v2. +The Web workflow does not read Core source or a Core version plan. Python publication neither +selects nor waits for MF, and MF publication neither selects nor waits for Python. Host SDK ranges +remain native loading requirements; they do not describe cross-distribution business compatibility. + +A plugin owns compatibility of its shared configuration, state, Source/Job parameters and stored +information. Changesets record MF changes even when no Python change is necessary. Published native +coordinates and saved candidate bytes are immutable; repairs require a new native version. + +## Native SDK 0.4 cutover + +The Peer-participation protocol removes the SDK's global Extension version and enabled array. +New first-party MF releases therefore require `@inkcre/core >=0.4.0 <0.5.0`; historical MF releases +keep their existing declared range and are not silently repinned or declared compatible. Shared +configuration and business data remain preserved when the user chooses a supported native release. + +This breaking cutover prepares the SDK and its in-repository consumers in the same reviewed change. +The existing `pnpm release:version` command consumes the already-admitted UI intents and the native +participation intent once, generating SDK 0.4 and independent MF patch releases. The resulting +Version outputs are committed; no duplicate pending intent remains for the next reconciliation. +This is the one-time delivery boundary for this cutover. Ordinary feature work continues to submit +Changesets and use the automated Version PR described above. + +Client Web consumes the official Runtime SDK 0.4 tarball and Toolkit 0.5 wheel through exact GitHub +Release asset URLs and lock-file integrity. Consumers install prebuilt packages without checking +out Registry SDK source or running its SDK preparation. The Runtime SDK release publisher still +qualifies its build with the pinned `packages/core` package source and its own frozen tooling; it +does not install a producer workspace or combine Python and MF source. That bounded build-source +transition does not establish complete SDK artifact independence. This repository's Core SDK 0.4 +remains workspace-owned until its own protected-main release. Dependency publication, candidate +qualification and production Extension publication remain separate delivery boundaries. + +Required database checks normally resolve the protected Core `stable` artifact. During this cutover, +`contracts/core-release.json` can admit a `canonical_candidate` with an existing canonical Core +image digest, its protected-main source SHA and exact database contract revision. The consumer +checks the image-owned manifest against both expected values and rejects tags, other registries, +source mismatches and contract mismatches. This reference is populated only after the existing Core +canonical artifact lane publishes it; a local or preview image is qualification evidence and cannot +be entered as that canonical candidate. Omitting the entry continues to use stable. This admission +does not move stable, deploy Core or migrate a production database. ## Registry Authority and Secret Boundary @@ -55,7 +91,7 @@ the endpoint through repository variable `INKCRE_EXTENSION_REGISTRY_URL`. The workflow records `source_repository`, `source_revision`, and a release-workflow `build_id` in the Module Federation distribution before upload. These Web-distribution provenance facts remain -independent of any Python distribution attached to the same Registry Release. +independent of Python release provenance. Hosted documentation is discovered separately for the exact Release. Browser code follows the absolute entry or snapshot URL returned by Registry and never derives a content hostname from the diff --git a/docs/40-deployment/web-delivery.md b/docs/40-deployment/web-delivery.md index 1ee3c6f4..54cbfcdc 100644 --- a/docs/40-deployment/web-delivery.md +++ b/docs/40-deployment/web-delivery.md @@ -50,6 +50,27 @@ on `main` with `source_sha` equal to its full current SHA. Each dispatch permits while the variable remains true; it does not clear the pause or select a newer source silently. Restore automatic delivery only after the cutover has been verified. +### Breaking database protocol upgrades + +Pages delivery changes the HTML and assets served to new visits; it does not replace JavaScript +already running in an open tab. Client Web 0.1.2 and Core SDK 0.3 use the global Extension +`version`, `enabled` and `config_schema` columns. The Peer-participation migration removes those +columns, so an old tab can fail Extension reads and writes even while unrelated information remains +readable. Reloading before the matching App is deployed still loads an incompatible client. + +The deployment owner coordinates a maintenance window across the affected Core/PostgREST deployment +and Client Web. Pause browser writes and close old connected tabs before the database cutover. Keep +the browser's connection settings and Peer ID. Resume use only after the deployment's +`extension-peer-participation-v2` database and management endpoint are ready and Client Web 0.1.3 +has been deployed, then fully reload each connected tab. A startup error's Retry button performs +that full reload; it cannot repair an incompatible database or update a tab while it remains open. +The Core deployment documentation owns backup, migration and service recovery commands. + +This maintenance sequence does not install, upgrade or enable Extensions. The new Host restores +existing enabled intent at the saved exact native version; it never substitutes latest. Historical +MF releases retain their original SDK range and may require the user to select a supported release +explicitly. Preserve shared configuration, state and stored information throughout the cutover. + ## Core SDK assets [`.github/workflows/core-sdk-release.yml`](../../.github/workflows/core-sdk-release.yml) independently diff --git a/docs/_shared b/docs/_shared index 7916312c..394f1349 160000 --- a/docs/_shared +++ b/docs/_shared @@ -1 +1 @@ -Subproject commit 7916312c12cfaf2ee396d7dc1afffee67d4f0721 +Subproject commit 394f1349f1893cc8bbbbadd27d59ac37bf35c909 diff --git a/extensions/AGENTS.md b/extensions/AGENTS.md index 6481722b..d03bdbd6 100644 --- a/extensions/AGENTS.md +++ b/extensions/AGENTS.md @@ -8,15 +8,17 @@ Host/producer runtime contracts belong to ## Local Tripwires -- Keep each directory an independently versioned package. Its directory name is the local ID; +- Keep each directory an independent build package. Its directory name is the local ID; `package.json#inkcre.name`, version, nickname, and `module_federation` association are Registry identity and compatibility inputs, not playground metadata. - Keep production remotes relocatable: `vite.config.ts` must retain `base: './'`, emit the native `mf-manifest.json`, and write `dist/client-web`. - Export host-consumed capabilities from `src/index.ts`; keep `src/main.ts` limited to the local playground. Do not make the Host depend on playground bootstrap behavior. -- Changes to a native producer must preserve the asset-closure validation and select only affected - independently releasable packages in `pnpm changeset`. +- Changes to a native producer must preserve asset-closure validation. Record its native release intent + in a Changeset for that package; its committed package version owns the MF release coordinate. +- Build and publish only this repository’s MF distributions. Python producers use their own repositories + and versions; do not read their source or version to choose an MF release. ## Required Check diff --git a/extensions/mail/CHANGELOG.md b/extensions/mail/CHANGELOG.md index a9b47e93..5699da62 100644 --- a/extensions/mail/CHANGELOG.md +++ b/extensions/mail/CHANGELOG.md @@ -1,5 +1,19 @@ # @inkcre/ext-mail +## 0.3.2 + +### Patch Changes + +- 插件配置与业务状态在部署内共享;精确分发版本和启用意图分别保存在每个 Peer。Client Web 在同一插件入口管理各 Peer 的安装、版本和运行状态,并采用 Extension management v2。 + + 原生 SDK 不再提供全局插件版本与 enabled 数组。新版 MF 使用 0.4 SDK 的参与记录接口,历史 MF 需按其 SDK 范围运行或明确选择新版;不会自动改写旧版本选择。Python 与 MF 分别从自己的仓库发布,插件负责共享数据契约兼容。 + +- 7307dda: Source 与 Peer 配置使用可视化表单,保留完整 JSON 编辑模式;Peers 页面可确认删除离线 Peer 并清理其扩展启用记录。Extension 浏览与版本选择交由 Registry,Web 接收精确版本并确认安装;Web 与 Module Federation 扩展统一升级正式 UI 2.2.0,Peer AI 凭据在 schema 中标记为密码输入。 +- Updated dependencies [7307dda] +- Updated dependencies +- Updated dependencies [7307dda] + - @inkcre/core@0.4.0 + ## 0.3.1 ### Patch Changes diff --git a/extensions/mail/package.json b/extensions/mail/package.json index cb676552..997220f4 100644 --- a/extensions/mail/package.json +++ b/extensions/mail/package.json @@ -1,6 +1,6 @@ { "name": "@inkcre/ext-mail", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "scripts": { @@ -38,7 +38,7 @@ "nickname": "Mail", "module_federation": { "host_sdk": "@inkcre/core", - "host_sdk_version": ">=0.2.0 <0.4.0" + "host_sdk_version": ">=0.4.0 <0.5.0" } }, "intent": { diff --git a/extensions/mail/src/index.ts b/extensions/mail/src/index.ts index 7940cd2e..5efea160 100644 --- a/extensions/mail/src/index.ts +++ b/extensions/mail/src/index.ts @@ -1,12 +1,30 @@ -import type { ExtensionModule } from '@inkcre/core' +import { Resolver, type ExtensionModule } from '@inkcre/core' +import { + EmailResolver, + MailboxResolver, + EmailAddressResolver, + MailFlagResolver, + MailMimePartResolver, +} from './resolver' -import './resolver' +const resolvers = [ + EmailResolver, + MailboxResolver, + EmailAddressResolver, + MailFlagResolver, + MailMimePartResolver, +] const Extension: ExtensionModule = { - async initialize() {}, + async initialize() { + for (const resolver of resolvers) Resolver.register(resolver.type, resolver) + }, async activate() {}, async deactivate() {}, - async dispose() {}, + async dispose() { + // Host compensation also calls dispose after a partially failed initialize. + for (const resolver of resolvers) Resolver.unregister(resolver.type, resolver) + }, } export default Extension diff --git a/extensions/mail/src/resolver.ts b/extensions/mail/src/resolver.ts index b4093156..40074ce5 100644 --- a/extensions/mail/src/resolver.ts +++ b/extensions/mail/src/resolver.ts @@ -73,10 +73,6 @@ export class EmailResolver extends Resolver{{ error }}
- This Core Peer handles authorization and bookmark collection. -
+No available Core Peer has the Twitter extension installed.
+No authorization Peer is available.
Time uses the collection Peer's timezone.
Bookmarks will be collected on this schedule.
diff --git a/extensions/twitter/src/index.ts b/extensions/twitter/src/index.ts
index 7bb8264b..e8d1fcb7 100644
--- a/extensions/twitter/src/index.ts
+++ b/extensions/twitter/src/index.ts
@@ -10,8 +10,8 @@
* This is the main export loaded by the host application.
*/
-import { type ExtensionModule } from '@inkcre/core'
-import './resolver'
+import { Resolver, type ExtensionModule } from '@inkcre/core'
+import { TweetResolver } from './resolver'
import TwitterSetupWizard from './components/twitterSetupWizard/twitterSetupWizard.vue'
const Extension: ExtensionModule = {
@@ -19,7 +19,9 @@ const Extension: ExtensionModule = {
component: TwitterSetupWizard,
},
- async initialize() {},
+ async initialize() {
+ Resolver.register(TweetResolver.type, TweetResolver)
+ },
async activate() {
console.log('[Twitter Extension] Activated')
@@ -31,7 +33,7 @@ const Extension: ExtensionModule = {
async dispose() {
console.log('[Twitter Extension] Disposed')
- // Cleanup resources if needed
+ Resolver.unregister(TweetResolver.type, TweetResolver)
},
}
diff --git a/extensions/twitter/src/resolver.ts b/extensions/twitter/src/resolver.ts
index ee2bae25..c77cbdfc 100644
--- a/extensions/twitter/src/resolver.ts
+++ b/extensions/twitter/src/resolver.ts
@@ -27,10 +27,6 @@ export class TweetResolver extends Resolver