mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix: migrate tlon installs to tloncorp package
Switch new Tlon installs to @tloncorp/openclaw, add an on-load config migration for existing plugins.installs.tlon npm records, and document the rollout contract for the package rename. Preserve the tlon plugin/channel id and clear stale npm resolution metadata when rewriting old install specs so future plugin updates resolve cleanly. Regeneration-Prompt: | Migrate the Tlon OpenClaw plugin package from @openclaw/tlon to @tloncorp/openclaw without breaking existing installs. Keep the plugin id and channel id as tlon, switch onboarding and docs for new installs to the new npm package, and add an automatic config migration for existing plugins.installs.tlon npm records that still reference the old package. When rewriting stored install specs, clear old npm resolution metadata such as integrity and resolvedSpec so the next plugin update does not compare the new package against the old package artifact. Also capture the migration contract in repo docs so the companion tloncorp package can preserve version continuity and manifest invariants.
This commit is contained in:
@@ -21,7 +21,7 @@ Tlon ships as a plugin and is not bundled with the core install.
|
||||
Install via CLI (npm registry):
|
||||
|
||||
```bash
|
||||
openclaw plugins install @openclaw/tlon
|
||||
openclaw plugins install @tloncorp/openclaw
|
||||
```
|
||||
|
||||
Local checkout (when running from a git repo):
|
||||
@@ -32,6 +32,9 @@ openclaw plugins install ./extensions/tlon
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
Existing installs from `@openclaw/tlon` are migrated to the new package name on config load so
|
||||
future `openclaw plugins update` runs resolve from `@tloncorp/openclaw`.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Install the Tlon plugin.
|
||||
|
||||
73
docs/refactor/tlon-package-migration.md
Normal file
73
docs/refactor/tlon-package-migration.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: "Tlon Package Migration"
|
||||
summary: "Plan for migrating the Tlon plugin package from @openclaw/tlon to @tloncorp/openclaw"
|
||||
read_when:
|
||||
- Updating Tlon onboarding/install metadata
|
||||
- Migrating existing Tlon plugin installs
|
||||
---
|
||||
|
||||
# Tlon Package Migration
|
||||
|
||||
This document captures the OpenClaw-side plan for migrating the Tlon plugin package from
|
||||
`@openclaw/tlon` to `@tloncorp/openclaw` without breaking existing installs.
|
||||
|
||||
## Goals
|
||||
|
||||
- Keep the plugin id and channel id as `tlon`.
|
||||
- Switch new onboarding and docs to install `@tloncorp/openclaw`.
|
||||
- Auto-migrate existing `plugins.installs.tlon` records that still point at `@openclaw/tlon`.
|
||||
- Preserve version continuity on the Tlon side so pinned or version-qualified specs can move cleanly.
|
||||
- Keep local checkout installs (`./extensions/tlon`) working as before.
|
||||
|
||||
## Required invariants
|
||||
|
||||
The published `@tloncorp/openclaw` package must remain a drop-in replacement for the Tlon plugin.
|
||||
|
||||
- `package.json.name` should be `@tloncorp/openclaw`.
|
||||
- `package.json.version` should continue the existing `2026.x.y` line instead of resetting to `0.x`.
|
||||
- `openclaw.channel.id` must stay `tlon`.
|
||||
- `openclaw.install.npmSpec` must be `@tloncorp/openclaw`.
|
||||
- `openclaw.extensions` must point at the real published entrypoint.
|
||||
- `openclaw.plugin.json.id` must stay `tlon`.
|
||||
- `openclaw.plugin.json.channels` must contain `tlon`.
|
||||
|
||||
## OpenClaw migration behavior
|
||||
|
||||
On config load, OpenClaw should detect old Tlon npm install specs and rewrite them in place.
|
||||
|
||||
- Rewrite `plugins.installs.tlon.spec` from `@openclaw/tlon` to `@tloncorp/openclaw`.
|
||||
- Rewrite version-qualified specs the same way, preserving the suffix after the package name.
|
||||
- Apply this to any `plugins.installs.tlon` record that still references the old package name.
|
||||
- Clear stale npm resolution metadata so the next `openclaw plugins update tlon` resolves against the
|
||||
new package without comparing the old package integrity hash to the new package artifact.
|
||||
|
||||
Fields to clear when the spec is rewritten:
|
||||
|
||||
- `resolvedName`
|
||||
- `resolvedVersion`
|
||||
- `resolvedSpec`
|
||||
- `integrity`
|
||||
- `shasum`
|
||||
- `resolvedAt`
|
||||
|
||||
Fields to preserve:
|
||||
|
||||
- `source`
|
||||
- `installPath`
|
||||
- `sourcePath`
|
||||
- `version`
|
||||
- `installedAt`
|
||||
|
||||
## Rollout
|
||||
|
||||
1. Publish `@tloncorp/openclaw` with the correct manifest and version continuity.
|
||||
2. Update OpenClaw onboarding metadata and docs to point new installs at `@tloncorp/openclaw`.
|
||||
3. Ship the config migration so existing installs start updating from the new package name.
|
||||
4. Keep `@openclaw/tlon` available during the transition window.
|
||||
5. Deprecate `@openclaw/tlon` after OpenClaw releases with the migration have had time to land.
|
||||
|
||||
## External follow-up in tloncorp/openclaw-tlon
|
||||
|
||||
- Publish the next `2026.x.y` version under `@tloncorp/openclaw`.
|
||||
- Update the README to say the plugin is installed separately, not “included with OpenClaw”.
|
||||
- Verify the published tarball contains the correct `openclaw.plugin.json`.
|
||||
@@ -923,7 +923,7 @@ it’s present in your workspace/managed skills locations.
|
||||
Recommended packaging:
|
||||
|
||||
- Main package: `openclaw` (this repo)
|
||||
- Plugins: separate npm packages under `@openclaw/*` (example: `@openclaw/voice-call`)
|
||||
- Plugins: separate npm packages, usually under `@openclaw/*` (examples: `@openclaw/voice-call`, `@tloncorp/openclaw`)
|
||||
|
||||
Publishing contract:
|
||||
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
|
||||
Tlon/Urbit channel plugin for OpenClaw. Supports DMs, group mentions, and thread replies.
|
||||
|
||||
Install from npm: `openclaw plugins install @tloncorp/openclaw`
|
||||
|
||||
Docs: https://docs.openclaw.ai/channels/tlon
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"quickstartAllowFrom": true
|
||||
},
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/tlon",
|
||||
"npmSpec": "@tloncorp/openclaw",
|
||||
"localPath": "extensions/tlon",
|
||||
"defaultChoice": "npm"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,62 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { migrateLegacyConfig } from "./legacy-migrate.js";
|
||||
import { WHISPER_BASE_AUDIO_MODEL } from "./legacy-migrate.test-helpers.js";
|
||||
import { findLegacyConfigIssues } from "./legacy.js";
|
||||
|
||||
describe("legacy migrate tlon plugin install spec", () => {
|
||||
it("flags legacy @openclaw/tlon install specs for auto-migration", () => {
|
||||
const issues = findLegacyConfigIssues({
|
||||
plugins: {
|
||||
installs: {
|
||||
tlon: {
|
||||
source: "npm",
|
||||
spec: "@openclaw/tlon",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(issues).toContainEqual({
|
||||
path: "plugins.installs.tlon.spec",
|
||||
message:
|
||||
"plugins.installs.tlon.spec moved from @openclaw/tlon to @tloncorp/openclaw (auto-migrated on load).",
|
||||
});
|
||||
});
|
||||
|
||||
it("rewrites old tlon install specs and clears stale npm metadata", () => {
|
||||
const res = migrateLegacyConfig({
|
||||
plugins: {
|
||||
installs: {
|
||||
tlon: {
|
||||
source: "npm",
|
||||
spec: "@openclaw/tlon@2026.2.21",
|
||||
resolvedName: "@openclaw/tlon",
|
||||
resolvedVersion: "2026.2.21",
|
||||
resolvedSpec: "@openclaw/tlon@2026.2.21",
|
||||
integrity: "sha512-old",
|
||||
shasum: "old",
|
||||
resolvedAt: "2026-03-01T00:00:00.000Z",
|
||||
installPath: "/tmp/tlon",
|
||||
version: "2026.2.21",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.changes).toContain(
|
||||
"Moved plugins.installs.tlon.spec → @tloncorp/openclaw@2026.2.21 and cleared stale npm resolution metadata.",
|
||||
);
|
||||
expect(res.config?.plugins?.installs?.tlon?.spec).toBe("@tloncorp/openclaw@2026.2.21");
|
||||
expect(res.config?.plugins?.installs?.tlon?.version).toBe("2026.2.21");
|
||||
expect(res.config?.plugins?.installs?.tlon?.installPath).toBe("/tmp/tlon");
|
||||
expect(res.config?.plugins?.installs?.tlon?.resolvedName).toBeUndefined();
|
||||
expect(res.config?.plugins?.installs?.tlon?.resolvedVersion).toBeUndefined();
|
||||
expect(res.config?.plugins?.installs?.tlon?.resolvedSpec).toBeUndefined();
|
||||
expect(res.config?.plugins?.installs?.tlon?.integrity).toBeUndefined();
|
||||
expect(res.config?.plugins?.installs?.tlon?.shasum).toBeUndefined();
|
||||
expect(res.config?.plugins?.installs?.tlon?.resolvedAt).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("legacy migrate audio transcription", () => {
|
||||
it("moves routing.transcribeAudio into tools.media.audio.models", () => {
|
||||
|
||||
@@ -35,6 +35,16 @@ const AGENT_HEARTBEAT_KEYS = new Set([
|
||||
|
||||
const CHANNEL_HEARTBEAT_KEYS = new Set(["showOk", "showAlerts", "useIndicator"]);
|
||||
|
||||
function migrateLegacyTlonInstallSpec(spec: string): string | null {
|
||||
if (spec === "@openclaw/tlon") {
|
||||
return "@tloncorp/openclaw";
|
||||
}
|
||||
if (spec.startsWith("@openclaw/tlon@")) {
|
||||
return `@tloncorp/openclaw${spec.slice("@openclaw/tlon".length)}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function splitLegacyHeartbeat(legacyHeartbeat: Record<string, unknown>): {
|
||||
agentHeartbeat: Record<string, unknown> | null;
|
||||
channelHeartbeat: Record<string, unknown> | null;
|
||||
@@ -97,6 +107,35 @@ function mergeLegacyIntoDefaults(params: {
|
||||
// tools.alsoAllow legacy migration intentionally omitted (field not shipped in prod).
|
||||
|
||||
export const LEGACY_CONFIG_MIGRATIONS_PART_3: LegacyConfigMigration[] = [
|
||||
{
|
||||
id: "plugins.installs.tlon.spec->tloncorp",
|
||||
describe: "Move Tlon plugin install records to @tloncorp/openclaw",
|
||||
apply: (raw, changes) => {
|
||||
const plugins = getRecord(raw.plugins);
|
||||
const installs = getRecord(plugins?.installs);
|
||||
const tlon = getRecord(installs?.tlon);
|
||||
const spec = typeof tlon?.spec === "string" ? tlon.spec : null;
|
||||
if (!tlon || !spec) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextSpec = migrateLegacyTlonInstallSpec(spec);
|
||||
if (!nextSpec) {
|
||||
return;
|
||||
}
|
||||
|
||||
tlon.spec = nextSpec;
|
||||
delete tlon.resolvedName;
|
||||
delete tlon.resolvedVersion;
|
||||
delete tlon.resolvedSpec;
|
||||
delete tlon.integrity;
|
||||
delete tlon.shasum;
|
||||
delete tlon.resolvedAt;
|
||||
changes.push(
|
||||
`Moved plugins.installs.tlon.spec → ${nextSpec} and cleared stale npm resolution metadata.`,
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
// v2026.2.26 added a startup guard requiring gateway.controlUi.allowedOrigins (or the
|
||||
// host-header fallback flag) for any non-loopback bind. The onboarding wizard was updated
|
||||
|
||||
@@ -46,6 +46,13 @@ function isLegacyGatewayBindHostAlias(value: unknown): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function isLegacyTlonInstallSpec(value: unknown): boolean {
|
||||
if (typeof value !== "string") {
|
||||
return false;
|
||||
}
|
||||
return value === "@openclaw/tlon" || value.startsWith("@openclaw/tlon@");
|
||||
}
|
||||
|
||||
export const LEGACY_CONFIG_RULES: LegacyConfigRule[] = [
|
||||
{
|
||||
path: ["whatsapp"],
|
||||
@@ -209,4 +216,10 @@ export const LEGACY_CONFIG_RULES: LegacyConfigRule[] = [
|
||||
message:
|
||||
"top-level heartbeat is not a valid config path; use agents.defaults.heartbeat (cadence/target/model settings) or channels.defaults.heartbeat (showOk/showAlerts/useIndicator).",
|
||||
},
|
||||
{
|
||||
path: ["plugins", "installs", "tlon", "spec"],
|
||||
message:
|
||||
"plugins.installs.tlon.spec moved from @openclaw/tlon to @tloncorp/openclaw (auto-migrated on load).",
|
||||
match: (value) => isLegacyTlonInstallSpec(value),
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user