@awebai/oats 0.41.1 → 0.42.1
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/bin/oats.mjs +10 -21
- package/docs/capabilities.md +73 -2
- package/docs/capability-manifest.schema.json +38 -0
- package/docs/desktop-cli-api.md +161 -13
- package/docs/desktop.md +21 -7
- package/docs/execution-targets.md +202 -45
- package/docs/implementation.md +3 -1
- package/docs/release-notes/v0.42.0.md +398 -0
- package/docs/release-notes/v0.42.1.md +110 -0
- package/docs/servers.md +4 -1
- package/docs/souls-and-instances.md +441 -8
- package/injects/instance-boundary.md +4 -3
- package/injects/work-checkout.md +25 -3
- package/injects/work-worktree.md +30 -7
- package/lib/capability-contract.mjs +56 -0
- package/lib/core.mjs +1384 -271
- package/lib/instance-git.mjs +22 -0
- package/lib/instance-lifecycle.mjs +28 -2
- package/lib/launch-preference.mjs +9 -0
- package/lib/login-environment.mjs +212 -0
- package/lib/retire-output.mjs +63 -0
- package/lib/servers.mjs +5 -2
- package/lib/tree-copy.mjs +4 -2
- package/package.json +1 -1
|
@@ -86,9 +86,65 @@ export function manifestContractProblems(m) {
|
|
|
86
86
|
else if (isObject(value) && value.required === true && event !== "spawn") bad(`${at}/required`, `hook "${event}" cannot be required — only the spawn hook is enforced (retire, launch and soul-scaffold run outside a spawn transaction)`);
|
|
87
87
|
if (hookScriptEscapes(command)) bad(isObject(value) ? `${at}/command` : at, `hook "${event}" script ${JSON.stringify(command.trim().split(/\s+/)[0])} escapes the capability directory; a hook script is a path inside it`);
|
|
88
88
|
}
|
|
89
|
+
|
|
90
|
+
if (m.retirement !== undefined) {
|
|
91
|
+
if (!isObject(m.retirement) || !isObject(m.retirement.disposable)) bad("/retirement", "manifest retirement must contain a disposable map");
|
|
92
|
+
else {
|
|
93
|
+
const unknown = Object.keys(m.retirement).filter((key) => key !== "disposable");
|
|
94
|
+
const scopes = Object.keys(m.retirement.disposable).filter((key) => !["home", "work"].includes(key));
|
|
95
|
+
if (unknown.length || scopes.length) bad("/retirement", `manifest retirement has unsupported keys: ${[...unknown, ...scopes].join(", ")}`);
|
|
96
|
+
for (const scope of ["home", "work"]) {
|
|
97
|
+
const roots = m.retirement.disposable[scope];
|
|
98
|
+
if (roots === undefined) continue;
|
|
99
|
+
const at = `/retirement/disposable/${scope}`;
|
|
100
|
+
if (!Array.isArray(roots) || roots.some((root) => typeof root !== "string")) { bad(at, `manifest retirement.disposable.${scope} must be an array of relative roots`); continue; }
|
|
101
|
+
if (scope !== "home") continue;
|
|
102
|
+
roots.forEach((root, i) => {
|
|
103
|
+
const problem = disposableHomeRootProblem(root);
|
|
104
|
+
if (problem === "shape") bad(`${at}/${i}`, `manifest retirement.disposable.home entry ${JSON.stringify(root)} must name one hidden top-level home entry (".name", or ".prefix-*")`);
|
|
105
|
+
else if (problem === "kernel-owned") bad(`${at}/${i}`, `manifest retirement.disposable.home entry ${JSON.stringify(root)} covers a kernel-owned home entry`);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
89
110
|
return problems;
|
|
90
111
|
}
|
|
91
112
|
|
|
113
|
+
/** `retirement.disposable.home`: provider-owned top-level entries of an instance
|
|
114
|
+
* home that retirement neither fingerprints nor copies to recovery. An entry is
|
|
115
|
+
* one hidden top-level name (`.aw`) or a prefix (`.aweb-identity-*`: every
|
|
116
|
+
* top-level name starting with the text before `*`). No separator, no
|
|
117
|
+
* traversal, no other glob, and nothing that covers a name the kernel owns in
|
|
118
|
+
* a home. The published schema (docs/capability-manifest.schema.json) states
|
|
119
|
+
* the same grammar. */
|
|
120
|
+
const DISPOSABLE_HOME_EXACT_RE = /^\.[A-Za-z0-9_][A-Za-z0-9._-]*$/;
|
|
121
|
+
const DISPOSABLE_HOME_PREFIX_RE = /^\.[A-Za-z0-9_][A-Za-z0-9._-]*-\*$/;
|
|
122
|
+
/** The top-level entries the KERNEL writes in an instance home, as a
|
|
123
|
+
* declaration may not cover them: these exact names, and every exact name
|
|
124
|
+
* starting with one of these prefixes (the events log; the stop, restart and
|
|
125
|
+
* start receipts and locks; the rollback marker; AGENTS.md backups; the
|
|
126
|
+
* attachments directory). This is the one list: a new kernel-written top-level
|
|
127
|
+
* home entry MUST be added here and to the `not` patterns of
|
|
128
|
+
* `retirement.disposable.home` in docs/capability-manifest.schema.json. A
|
|
129
|
+
* prefix declaration can reach none of them except through `.oats-`, which is
|
|
130
|
+
* refused whole; `.oats-<provider>` as an exact name stays declarable. */
|
|
131
|
+
const KERNEL_HOME_NAMES = new Set([".oats", ".agents", ".claude"]);
|
|
132
|
+
const KERNEL_HOME_NAME_PREFIXES = [".oats-events", ".oats-stop", ".oats-restart", ".oats-rollback", ".oats-agents-md", ".oats-start", ".oats-attachments"];
|
|
133
|
+
|
|
134
|
+
/** Why `root` is not a declarable home entry: "shape" (not one hidden top-level
|
|
135
|
+
* name or prefix), "kernel-owned", or undefined when it is sound. */
|
|
136
|
+
export function disposableHomeRootProblem(root) {
|
|
137
|
+
if (typeof root !== "string") return "shape";
|
|
138
|
+
if (DISPOSABLE_HOME_PREFIX_RE.test(root)) return root.startsWith(".oats-") ? "kernel-owned" : undefined;
|
|
139
|
+
if (!DISPOSABLE_HOME_EXACT_RE.test(root)) return "shape";
|
|
140
|
+
return KERNEL_HOME_NAMES.has(root) || KERNEL_HOME_NAME_PREFIXES.some((prefix) => root.startsWith(prefix)) ? "kernel-owned" : undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Whether the declared `root` (already sound) covers the top-level home entry `name`. */
|
|
144
|
+
export function disposableHomeRootMatches(root, name) {
|
|
145
|
+
return root.endsWith("*") ? name.startsWith(root.slice(0, -1)) : name === root;
|
|
146
|
+
}
|
|
147
|
+
|
|
92
148
|
/** The setting keys a manifest declares (`settings.<key>`), sorted: names only, never their
|
|
93
149
|
* descriptions or defaults. The spawn preview and inspect expose them so a client can gate
|
|
94
150
|
* a choice on a declared key (e.g. a messaging provider's `join`). */
|