@atbash/cli 0.5.15-dev.13 → 0.5.15-dev.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -1
- package/dist/commands/connect.d.ts +35 -1
- package/dist/commands/connect.js +175 -21
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/setup.d.ts +153 -21
- package/dist/commands/setup.js +595 -54
- package/dist/commands/setup.js.map +1 -1
- package/dist/shared/openclaw-runtime.d.ts +221 -0
- package/dist/shared/openclaw-runtime.js +453 -0
- package/dist/shared/openclaw-runtime.js.map +1 -0
- package/package.json +2 -1
package/dist/commands/setup.d.ts
CHANGED
|
@@ -78,12 +78,72 @@ export type Step = {
|
|
|
78
78
|
before: string | null;
|
|
79
79
|
after: string;
|
|
80
80
|
secret?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Id of an `exec` step this write DEPENDS ON. The write is skipped entirely
|
|
83
|
+
* when that step did not succeed.
|
|
84
|
+
*
|
|
85
|
+
* ⚠️ This exists because the order used to be the other way round, on a
|
|
86
|
+
* rationale that reads convincingly and is wrong: "writes first, then
|
|
87
|
+
* commands, so a failed install still leaves a correct config behind for a
|
|
88
|
+
* manual retry". A plugin config with no plugin is not a head start — it is
|
|
89
|
+
* a DANGLING REFERENCE, and OpenClaw treats it as one:
|
|
90
|
+
*
|
|
91
|
+
* plugins.entries.atbash-openclaw: plugin not found: atbash-openclaw
|
|
92
|
+
* (stale config entry ignored; remove it from plugins config)
|
|
93
|
+
* → Run "openclaw doctor --fix" to remove stale plugin ids
|
|
94
|
+
*
|
|
95
|
+
* So the leftover config is not merely useless: OpenClaw's own repair
|
|
96
|
+
* command offers to DELETE it, and an operator who accepts silently
|
|
97
|
+
* un-governs the agent. On 2026.2.x it was worse still — an entry naming an
|
|
98
|
+
* absent plugin contributed to the config being refused, which took down
|
|
99
|
+
* the very `plugins install` that would have created it.
|
|
100
|
+
*
|
|
101
|
+
* Not writing is strictly better than writing-then-rolling-back: there is
|
|
102
|
+
* no window in which a broken state exists on disk at all.
|
|
103
|
+
*/
|
|
104
|
+
requires?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Apply this write by handing the change to the runtime instead of writing
|
|
107
|
+
* the file ourselves.
|
|
108
|
+
*
|
|
109
|
+
* `payload` is piped to `command args` on stdin. Used for
|
|
110
|
+
* `openclaw config patch --stdin`, which merges recursively and validates
|
|
111
|
+
* in ONE write — so OpenClaw owns its own file format, key names, schema,
|
|
112
|
+
* comments, permissions and migrations. Every bug in this thread came from
|
|
113
|
+
* us asserting that shape from outside.
|
|
114
|
+
*
|
|
115
|
+
* It also closes a hazard we cannot otherwise handle: OpenClaw rewrites
|
|
116
|
+
* `openclaw.json` on its own (a machine in the field carried three
|
|
117
|
+
* `openclaw.json.clobbered.<timestamp>` files plus an
|
|
118
|
+
* `openclaw.json.last-good`), so anything we hand-merge can be discarded
|
|
119
|
+
* without warning. A patch describes INTENT and survives that.
|
|
120
|
+
*
|
|
121
|
+
* `before`/`after` are still computed so `--dry-run` shows a real diff —
|
|
122
|
+
* the preview stays ours, only the application moves.
|
|
123
|
+
*/
|
|
124
|
+
applyVia?: {
|
|
125
|
+
command: string;
|
|
126
|
+
args: string[];
|
|
127
|
+
payload: string;
|
|
128
|
+
/**
|
|
129
|
+
* Extra environment for the child.
|
|
130
|
+
*
|
|
131
|
+
* ⚠️ REQUIRED IN PRACTICE, not a nicety. `openclaw config patch` edits
|
|
132
|
+
* whatever config OpenClaw considers active, which is NOT necessarily
|
|
133
|
+
* `step.file` — so without pinning `OPENCLAW_CONFIG_PATH` to the file this
|
|
134
|
+
* step names, a `--home <fixture>` run would patch the operator's REAL
|
|
135
|
+
* `~/.openclaw/openclaw.json`. A step must only ever change the file it
|
|
136
|
+
* declares; that is what makes the plan an honest preview.
|
|
137
|
+
*/
|
|
138
|
+
env?: Record<string, string>;
|
|
139
|
+
};
|
|
81
140
|
} | {
|
|
82
141
|
kind: "exec";
|
|
83
142
|
label: string;
|
|
84
143
|
command: string;
|
|
85
144
|
args: string[];
|
|
86
145
|
optionalWhy?: string;
|
|
146
|
+
id?: string;
|
|
87
147
|
} | {
|
|
88
148
|
kind: "manual";
|
|
89
149
|
label: string;
|
|
@@ -126,28 +186,31 @@ export declare function isJsonc(text: string): boolean;
|
|
|
126
186
|
* `<your-username>` placeholder that people paste verbatim, producing a path that
|
|
127
187
|
* does not exist and a plugin that never loads.
|
|
128
188
|
*/
|
|
129
|
-
export declare function mergeOpenclawConfig(config: Record<string, unknown>, home: string, orgName?: string, opts?: {
|
|
130
|
-
installsModern?: boolean;
|
|
131
|
-
}): Record<string, unknown>;
|
|
132
189
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* ⚠️ That command cannot overwrite. If the extension directory is there it
|
|
136
|
-
* aborts —
|
|
190
|
+
* WHICH entry this run governs, and whether a legacy one is being stood down.
|
|
137
191
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
192
|
+
* Extracted so the hand-merge and the `openclaw config patch` payload cannot
|
|
193
|
+
* disagree. Two implementations of this decision is exactly how a build ended up
|
|
194
|
+
* writing the modern plugin's `orgName` onto the LEGACY entry — whose schema is
|
|
195
|
+
* closed and has no such field — so OpenClaw rejected the whole file and the run
|
|
196
|
+
* broke its own install step.
|
|
140
197
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* meant to be re-runnable; a step that can only ever succeed once is not.
|
|
145
|
-
*
|
|
146
|
-
* There is no `plugins uninstall` in OpenClaw 2026.2.1 — the subcommands are
|
|
147
|
-
* list/info/enable/disable/install/update/doctor — so the way to refresh an
|
|
148
|
-
* existing install is `plugins update <id>`, which re-resolves the recorded spec
|
|
149
|
-
* and reports "up to date" when there is nothing to do.
|
|
198
|
+
* An entry is not the only way the legacy plugin is present: `plugins.installs`
|
|
199
|
+
* records it independently, and an installed, allowed plugin loads with its
|
|
200
|
+
* defaults whether or not anyone wrote an entry for it.
|
|
150
201
|
*/
|
|
202
|
+
export declare function openclawEntryDecision(config: Record<string, unknown>, opts?: {
|
|
203
|
+
installsModern?: boolean;
|
|
204
|
+
}): {
|
|
205
|
+
entryKey: string;
|
|
206
|
+
useLegacy: boolean;
|
|
207
|
+
legacy: boolean;
|
|
208
|
+
standDownLegacy: boolean;
|
|
209
|
+
};
|
|
210
|
+
export declare function mergeOpenclawConfig(config: Record<string, unknown>, home: string, orgName?: string, opts?: {
|
|
211
|
+
installsModern?: boolean;
|
|
212
|
+
hooksSupported?: boolean;
|
|
213
|
+
}): Record<string, unknown>;
|
|
151
214
|
export declare function openclawInstallState(home: string, spec: string): {
|
|
152
215
|
installed: boolean;
|
|
153
216
|
sameSpec: boolean;
|
|
@@ -341,6 +404,31 @@ export declare function mergeHermesEnv(existing: string | null): string;
|
|
|
341
404
|
*/
|
|
342
405
|
export declare function hadInlineKey(config: Record<string, unknown>, serversKey?: "mcpServers" | "servers"): boolean;
|
|
343
406
|
export declare function mergeMcpServer(config: Record<string, unknown>, serversKey?: "mcpServers" | "servers"): Record<string, unknown>;
|
|
407
|
+
/**
|
|
408
|
+
* The change we want, expressed as a PATCH rather than a whole file.
|
|
409
|
+
*
|
|
410
|
+
* `openclaw config patch` merges objects recursively and validates in one write,
|
|
411
|
+
* so this is intent — "this entry should exist and look like this" — instead of
|
|
412
|
+
* "here are the complete new bytes of your config". That difference is what makes
|
|
413
|
+
* it survive the things that kept defeating us from outside: OpenClaw's own
|
|
414
|
+
* format changes, key renames, comment preservation, file permissions, its
|
|
415
|
+
* migrations, and the fact that it rewrites `openclaw.json` on its own (a machine
|
|
416
|
+
* in the field carried three `openclaw.json.clobbered.<timestamp>` files).
|
|
417
|
+
*
|
|
418
|
+
* ⚠️ DO NOT call `config patch --dry-run` from buildPlan to preview this. It is
|
|
419
|
+
* NOT side-effect free: running it triggers OpenClaw's state migrations, which
|
|
420
|
+
* wrote `~/.openclaw/update-check.json.migrated` and moved config-health state
|
|
421
|
+
* into SQLite on the machine this was developed on. `atbash setup --dry-run`
|
|
422
|
+
* promises that nothing is written, and that promise has to hold for OpenClaw's
|
|
423
|
+
* housekeeping too. The preview stays a merge we compute ourselves.
|
|
424
|
+
*/
|
|
425
|
+
export declare function openclawPatchPayload(args: {
|
|
426
|
+
entryKey: string;
|
|
427
|
+
orgName?: string;
|
|
428
|
+
keyPath: string;
|
|
429
|
+
/** Switch the legacy entry off in the same patch, when one is live. */
|
|
430
|
+
standDownLegacy: boolean;
|
|
431
|
+
}): string;
|
|
344
432
|
/**
|
|
345
433
|
* Work out everything that needs doing on this machine, without doing any of it.
|
|
346
434
|
*
|
|
@@ -361,6 +449,19 @@ export declare function buildPlan(args: {
|
|
|
361
449
|
noInstall: boolean;
|
|
362
450
|
/** Restrict to these runtime ids; empty means "everything detected". */
|
|
363
451
|
only: string[];
|
|
452
|
+
/**
|
|
453
|
+
* What the OpenClaw on this machine can do, probed by detectOpenclaw.
|
|
454
|
+
*
|
|
455
|
+
* Optional so tests and callers that only want the file-level merge need not
|
|
456
|
+
* shell out. Absent means "assume nothing extra" — the hand-merge path, which
|
|
457
|
+
* works on every version — rather than assuming the newest capabilities.
|
|
458
|
+
*/
|
|
459
|
+
openclaw?: {
|
|
460
|
+
caps: {
|
|
461
|
+
patch: boolean;
|
|
462
|
+
};
|
|
463
|
+
hooksSupported?: boolean;
|
|
464
|
+
};
|
|
364
465
|
}): Plan;
|
|
365
466
|
/**
|
|
366
467
|
* A minimal line diff, so the preview shows what CHANGES rather than dumping a
|
|
@@ -387,9 +488,40 @@ export interface ApplyResult {
|
|
|
387
488
|
backups: string[];
|
|
388
489
|
ran: string[];
|
|
389
490
|
failures: string[];
|
|
491
|
+
/** Writes deliberately NOT made because a step they depend on failed. */
|
|
492
|
+
skipped: string[];
|
|
493
|
+
/** Files restored from backup because the result did not validate. */
|
|
494
|
+
rolledBack: string[];
|
|
390
495
|
}
|
|
391
|
-
/**
|
|
392
|
-
*
|
|
393
|
-
|
|
496
|
+
/**
|
|
497
|
+
* Execute the plan, in the order that cannot leave a half-wired machine.
|
|
498
|
+
*
|
|
499
|
+
* 1. Independent writes — the key file above all. Safe on their own, useful
|
|
500
|
+
* even if everything after fails, and required by the plugin at load.
|
|
501
|
+
* 2. Commands — the plugin install, so the plugin id EXISTS on disk.
|
|
502
|
+
* 3. Dependent writes — the plugin config, now that it refers to something
|
|
503
|
+
* real. Skipped outright if its install failed (see Step.requires).
|
|
504
|
+
* 4. Verify, and roll back anything we broke (see verify).
|
|
505
|
+
*
|
|
506
|
+
* ⚠️ THE ORDER IS THE FIX, and it is the reverse of what this function used to
|
|
507
|
+
* do. "Writes first, then commands, so a failed install still leaves a correct
|
|
508
|
+
* config behind" sounds prudent and produces the single worst outcome available:
|
|
509
|
+
* a config entry for a plugin that is not installed, which OpenClaw reports as a
|
|
510
|
+
* stale reference and offers to delete via `doctor --fix` — silently un-governing
|
|
511
|
+
* the agent — and which on 2026.2.x helped make the config unloadable, killing
|
|
512
|
+
* the install that would have fixed it.
|
|
513
|
+
*/
|
|
514
|
+
export declare function applyPlan(plan: Plan, opts?: {
|
|
515
|
+
/**
|
|
516
|
+
* Ask the runtime to confirm the result after writing, and restore the
|
|
517
|
+
* backup if the runtime says WE broke it. Omitted (or a build without
|
|
518
|
+
* `validate`) means no verification — which must read as "unverified", never
|
|
519
|
+
* as "verified fine".
|
|
520
|
+
*/
|
|
521
|
+
verify?: (file: string) => {
|
|
522
|
+
valid: boolean;
|
|
523
|
+
problems: string;
|
|
524
|
+
} | undefined;
|
|
525
|
+
}): ApplyResult;
|
|
394
526
|
export declare function registerSetupCommand(program: Command): void;
|
|
395
527
|
export {};
|