@cotal-ai/cli 0.13.2 → 0.14.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/cotal-skills/.claude-plugin/plugin.json +6 -0
- package/cotal-skills/skills/team-topology/SKILL.md +96 -0
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +6 -2
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/clean.d.ts.map +1 -1
- package/dist/commands/clean.js +41 -11
- package/dist/commands/clean.js.map +1 -1
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +17 -6
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/down-manifest.js +2 -2
- package/dist/commands/down-manifest.js.map +1 -1
- package/dist/commands/down.d.ts +9 -0
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +113 -46
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/mint.d.ts.map +1 -1
- package/dist/commands/mint.js +5 -4
- package/dist/commands/mint.js.map +1 -1
- package/dist/commands/setup.d.ts +4 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +227 -31
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/spawn.d.ts.map +1 -1
- package/dist/commands/spawn.js +3 -3
- package/dist/commands/spawn.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +93 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +16 -12
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +9 -3
- package/dist/commands/update.js.map +1 -1
- package/dist/lib/agent-skills.d.ts +61 -0
- package/dist/lib/agent-skills.d.ts.map +1 -0
- package/dist/lib/agent-skills.js +240 -0
- package/dist/lib/agent-skills.js.map +1 -0
- package/dist/lib/auth-proc.d.ts +19 -1
- package/dist/lib/auth-proc.d.ts.map +1 -1
- package/dist/lib/auth-proc.js +102 -27
- package/dist/lib/auth-proc.js.map +1 -1
- package/dist/lib/control.js +2 -2
- package/dist/lib/control.js.map +1 -1
- package/dist/lib/delivery-proc.js +3 -3
- package/dist/lib/delivery-proc.js.map +1 -1
- package/dist/lib/maintenance-files.d.ts.map +1 -1
- package/dist/lib/maintenance-files.js +25 -6
- package/dist/lib/maintenance-files.js.map +1 -1
- package/dist/lib/pid.d.ts +23 -0
- package/dist/lib/pid.d.ts.map +1 -0
- package/dist/lib/pid.js +39 -0
- package/dist/lib/pid.js.map +1 -0
- package/dist/lib/restore.js +2 -2
- package/dist/lib/restore.js.map +1 -1
- package/dist/lib/status.d.ts +1 -2
- package/dist/lib/status.d.ts.map +1 -1
- package/dist/lib/status.js +34 -8
- package/dist/lib/status.js.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { homeCotalDir } from "@cotal-ai/workspace";
|
|
6
|
+
/**
|
|
7
|
+
* Cross-vendor Agent Skills distribution.
|
|
8
|
+
*
|
|
9
|
+
* Cotal authors a small set of Agent Skills (SKILL.md, the agentskills.io open format). One canonical
|
|
10
|
+
* copy of each ships inside this CLI package (see package.json `files`) and feeds three delivery
|
|
11
|
+
* channels from that single source:
|
|
12
|
+
* 1. Claude Code, bundled in the `cotal-skills` plugin, installed from the mesh marketplace at user
|
|
13
|
+
* scope (real remote update via a release-derived plugin version; see setup.ts).
|
|
14
|
+
* 2. Every other harness (Codex, Cursor, OpenCode, Gemini CLI, Windsurf/Devin) reads the cross-vendor
|
|
15
|
+
* `~/.agents/skills/` directory convention. No remote index reaches them today, so `cotal setup`
|
|
16
|
+
* reconciles the files here and `cotal status` reports skew. This module owns that reconcile.
|
|
17
|
+
* 3. The website Agent Skills discovery index, generated from the same canonical files at build.
|
|
18
|
+
*
|
|
19
|
+
* File-level ownership (the safety model): Cotal owns exactly ONE file per skill it ships,
|
|
20
|
+
* `~/.agents/skills/<name>/SKILL.md`, tracked by digest in a validated manifest under `~/.cotal`. It
|
|
21
|
+
* only ever writes/removes that file: it never recursively deletes a skill directory (a retired skill's
|
|
22
|
+
* dir is removed only if it is left empty), never touches any other file a user or third party put
|
|
23
|
+
* there, refuses to follow a symlink anywhere in the managed path, and writes via a stage-and-rename so
|
|
24
|
+
* it replaces the directory entry instead of writing through a hard-linked inode. That keeps a
|
|
25
|
+
* destructive reconcile from becoming a data-loss or arbitrary-write primitive.
|
|
26
|
+
*/
|
|
27
|
+
/** The canonical `SKILL.md` source dir, shipped in this CLI package. Resolved the same way in a dev
|
|
28
|
+
* clone (built dist/lib) and an installed binary: two levels up from dist/lib is the package root. */
|
|
29
|
+
export function canonicalSkillsDir() {
|
|
30
|
+
return join(import.meta.dirname, "..", "..", "cotal-skills", "skills");
|
|
31
|
+
}
|
|
32
|
+
/** The cross-vendor skills directory the other harnesses read: `~/.agents/skills`. Anchored on the OS
|
|
33
|
+
* home dir (not `~/.cotal`), because that is the real path those tools scan. */
|
|
34
|
+
export function agentSkillsHome() {
|
|
35
|
+
return join(homedir(), ".agents", "skills");
|
|
36
|
+
}
|
|
37
|
+
// The Agent Skills name grammar: lowercase alphanumerics in hyphen-separated segments, no leading,
|
|
38
|
+
// trailing, or doubled hyphen (so no path separators and no `..`). Kept exactly as strict as the
|
|
39
|
+
// harnesses that consume the dir, so a name that passes local validation can never be one they reject.
|
|
40
|
+
const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
41
|
+
const MAX_SKILL_NAME = 64;
|
|
42
|
+
const DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
43
|
+
function validSkillName(name) {
|
|
44
|
+
return name.length <= MAX_SKILL_NAME && SKILL_NAME.test(name);
|
|
45
|
+
}
|
|
46
|
+
function digest(buf) {
|
|
47
|
+
return `sha256:${createHash("sha256").update(buf).digest("hex")}`;
|
|
48
|
+
}
|
|
49
|
+
/** Write bytes to a file Cotal owns by staging a fresh file in the SAME directory and renaming it over
|
|
50
|
+
* the destination. rename() replaces the directory entry, so it never writes THROUGH an existing inode:
|
|
51
|
+
* a hard-linked `SKILL.md` (a regular file, so it passes the symlink guard) is superseded rather than
|
|
52
|
+
* truncated, and any file outside the tree it was linked to is left intact. The `wx` flag creates the
|
|
53
|
+
* temp exclusively (O_EXCL refuses to follow a pre-planted symlink at the temp path). */
|
|
54
|
+
function writeOwnedFile(destFile, bytes) {
|
|
55
|
+
const tmp = `${destFile}.tmp.${process.pid}`;
|
|
56
|
+
rmSync(tmp, { force: true }); // clear a stale temp from a crashed run (removes the entry, not any link target)
|
|
57
|
+
writeFileSync(tmp, bytes, { flag: "wx" });
|
|
58
|
+
renameSync(tmp, destFile);
|
|
59
|
+
}
|
|
60
|
+
/** Back up a user's current DIVERGENT `SKILL.md` to a fresh sibling that does not already exist, created
|
|
61
|
+
* exclusively (`wx` = O_CREAT|O_EXCL, so it neither follows a symlink nor overwrites a file Cotal does
|
|
62
|
+
* not own). Tries `.bak`, then `.bak.1`, `.bak.2`, ...: a pre-existing or third-party backup is never
|
|
63
|
+
* destroyed, and every divergent overwrite keeps its OWN recoverable copy rather than clobbering an
|
|
64
|
+
* earlier one. */
|
|
65
|
+
function backupDivergent(destFile, cur) {
|
|
66
|
+
for (let i = 0; i < 1000; i++) {
|
|
67
|
+
const bak = i === 0 ? `${destFile}.bak` : `${destFile}.bak.${i}`;
|
|
68
|
+
try {
|
|
69
|
+
writeFileSync(bak, cur, { flag: "wx" });
|
|
70
|
+
return bak; // the exact path we wrote, so callers can point the user at their recoverable copy
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
if (e.code === "EEXIST")
|
|
74
|
+
continue; // that slot is taken (a prior/foreign backup): try the next
|
|
75
|
+
throw e;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`could not create a backup for ${destFile}: too many existing backups`);
|
|
79
|
+
}
|
|
80
|
+
/** Where the ownership manifest lives (`~/.cotal/agent-skills.json`): the record of which skill names
|
|
81
|
+
* Cotal owns in `~/.agents/skills` and the digest it last wrote for each. */
|
|
82
|
+
function manifestPath() {
|
|
83
|
+
return join(homeCotalDir(), "agent-skills.json");
|
|
84
|
+
}
|
|
85
|
+
/** Read the ownership manifest. Absent bootstraps to empty; a present-but-malformed manifest (bad JSON,
|
|
86
|
+
* wrong shape, an illegal skill name, or a non-digest value) FAILS LOUD rather than silently resetting.
|
|
87
|
+
* This ledger authorizes deletion, so its integrity is the safety boundary: a corrupt or tampered file
|
|
88
|
+
* must never be trusted to name what we may remove, nor silently forget what we still own. */
|
|
89
|
+
function readManifest() {
|
|
90
|
+
const path = manifestPath();
|
|
91
|
+
if (!existsSync(path))
|
|
92
|
+
return { skills: {} };
|
|
93
|
+
let parsed;
|
|
94
|
+
try {
|
|
95
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw new Error(`Corrupt Cotal skills manifest at ${path} (invalid JSON). Fix or delete it, then re-run cotal setup.`);
|
|
99
|
+
}
|
|
100
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
101
|
+
throw new Error(`Corrupt Cotal skills manifest at ${path} (unexpected shape). Fix or delete it, then re-run cotal setup.`);
|
|
102
|
+
const skills = parsed.skills;
|
|
103
|
+
// Must be a plain object, never an array: array indices ("0", "1") pass a slug check, so an array
|
|
104
|
+
// would let a numeric "name" be treated as owned (and deleted), and a string property assigned to an
|
|
105
|
+
// array is dropped by JSON.stringify, so ownership would silently fail to persist. Reject it outright.
|
|
106
|
+
if (typeof skills !== "object" || skills === null || Array.isArray(skills))
|
|
107
|
+
throw new Error(`Corrupt Cotal skills manifest at ${path} (skills is not an object). Fix or delete it, then re-run cotal setup.`);
|
|
108
|
+
for (const [name, dig] of Object.entries(skills)) {
|
|
109
|
+
if (!validSkillName(name))
|
|
110
|
+
throw new Error(`Corrupt Cotal skills manifest at ${path}: illegal skill name ${JSON.stringify(name)}.`);
|
|
111
|
+
if (typeof dig !== "string" || !DIGEST.test(dig))
|
|
112
|
+
throw new Error(`Corrupt Cotal skills manifest at ${path}: bad digest for ${JSON.stringify(name)}.`);
|
|
113
|
+
}
|
|
114
|
+
return { skills: skills };
|
|
115
|
+
}
|
|
116
|
+
/** Write the manifest atomically (temp + rename) so a crash can't leave a half-written ledger. */
|
|
117
|
+
function writeManifest(m) {
|
|
118
|
+
const path = manifestPath();
|
|
119
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
120
|
+
writeOwnedFile(path, Buffer.from(JSON.stringify(m, null, 2) + "\n")); // stage-and-rename: its temp is unlinked + `wx`-created, so it can't be a symlink/hard-link clobber vector
|
|
121
|
+
}
|
|
122
|
+
/** The Cotal-authored skill names shipped in this binary, one dir with a `SKILL.md` each. Fails LOUD
|
|
123
|
+
* (repo rule: no silent fallbacks) if the bundle is absent, empty, a child dir lacks its SKILL.md, or a
|
|
124
|
+
* child name is not a valid slug, so a truncated/corrupt install surfaces instead of shipping zero (or
|
|
125
|
+
* a malformed) skill. */
|
|
126
|
+
export function canonicalSkillNames() {
|
|
127
|
+
const dir = canonicalSkillsDir();
|
|
128
|
+
if (!existsSync(dir))
|
|
129
|
+
throw new Error(`Cotal skills bundle missing at ${dir}. The cotal-ai install looks corrupt; reinstall it.`);
|
|
130
|
+
const names = [];
|
|
131
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
132
|
+
if (!e.isDirectory())
|
|
133
|
+
continue;
|
|
134
|
+
if (!validSkillName(e.name))
|
|
135
|
+
throw new Error(`Cotal skill dir "${e.name}" has an illegal name. Corrupt skills bundle.`);
|
|
136
|
+
if (!existsSync(join(dir, e.name, "SKILL.md")))
|
|
137
|
+
throw new Error(`Cotal skill "${e.name}" is missing SKILL.md at ${join(dir, e.name)}. Corrupt skills bundle.`);
|
|
138
|
+
names.push(e.name);
|
|
139
|
+
}
|
|
140
|
+
if (!names.length)
|
|
141
|
+
throw new Error(`No Cotal skills found in ${dir}. Corrupt skills bundle.`);
|
|
142
|
+
return names.sort();
|
|
143
|
+
}
|
|
144
|
+
/** Refuse to touch a skill whose `~/.agents`, `~/.agents/skills`, `<name>` dir, or `<name>/SKILL.md` is
|
|
145
|
+
* a symlink: Cotal only writes/removes real files under `~/.agents/skills`, so a redirected component
|
|
146
|
+
* can never make a read, write, or delete land on a file outside it. */
|
|
147
|
+
function assertNoSymlink(name) {
|
|
148
|
+
for (const p of [join(homedir(), ".agents"), agentSkillsHome(), join(agentSkillsHome(), name), join(agentSkillsHome(), name, "SKILL.md")]) {
|
|
149
|
+
let st;
|
|
150
|
+
try {
|
|
151
|
+
st = lstatSync(p);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
continue; // does not exist yet: nothing to follow
|
|
155
|
+
}
|
|
156
|
+
if (st.isSymbolicLink())
|
|
157
|
+
throw new Error(`Refusing to manage skills: ${p} is a symlink. Cotal only writes real files under ~/.agents/skills.`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** Reconcile Cotal's authored skills into `~/.agents/skills`, at the file level:
|
|
161
|
+
* - install/refresh each canonical skill's `SKILL.md` (and only that file, never other files in the
|
|
162
|
+
* dir); if the destination is a user's or third party's copy (not what we last wrote), copy the
|
|
163
|
+
* current content into a fresh `SKILL.md.bak` slot (created exclusively, never overwriting an
|
|
164
|
+
* existing/foreign backup) before overwriting, so every divergent edit stays recoverable;
|
|
165
|
+
* - remove a skill Cotal previously owned that is no longer canonical (retired), but only its
|
|
166
|
+
* `SKILL.md`, and only when that file is still exactly what we wrote; then drop the dir if it is now
|
|
167
|
+
* empty. A user's file in the dir, or a copy they have changed, is never removed.
|
|
168
|
+
* Idempotent; fails loud on a corrupt bundle or manifest. */
|
|
169
|
+
export function installAgentSkills() {
|
|
170
|
+
const src = canonicalSkillsDir();
|
|
171
|
+
const names = canonicalSkillNames();
|
|
172
|
+
const home = agentSkillsHome();
|
|
173
|
+
const manifest = readManifest();
|
|
174
|
+
const backedUp = [];
|
|
175
|
+
const removed = [];
|
|
176
|
+
for (const name of names) {
|
|
177
|
+
assertNoSymlink(name);
|
|
178
|
+
const dir = join(home, name);
|
|
179
|
+
const destFile = join(dir, "SKILL.md");
|
|
180
|
+
const canonical = readFileSync(join(src, name, "SKILL.md"));
|
|
181
|
+
if (existsSync(destFile)) {
|
|
182
|
+
const cur = readFileSync(destFile);
|
|
183
|
+
const ours = manifest.skills[name] === digest(cur);
|
|
184
|
+
if (!ours && digest(cur) !== digest(canonical)) {
|
|
185
|
+
const path = backupDivergent(destFile, cur); // create a FRESH backup slot (never overwrite a pre-existing/foreign .bak); every divergent edit stays recoverable
|
|
186
|
+
backedUp.push({ name, path });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
mkdirSync(dir, { recursive: true });
|
|
190
|
+
writeOwnedFile(destFile, canonical); // write ONLY our file, via rename (never truncates a hard-linked inode); never delete or replace anything else in the dir
|
|
191
|
+
manifest.skills[name] = digest(canonical);
|
|
192
|
+
}
|
|
193
|
+
for (const name of Object.keys(manifest.skills)) {
|
|
194
|
+
if (names.includes(name))
|
|
195
|
+
continue; // still shipped
|
|
196
|
+
assertNoSymlink(name);
|
|
197
|
+
const dir = join(home, name);
|
|
198
|
+
const destFile = join(dir, "SKILL.md");
|
|
199
|
+
if (existsSync(destFile) && digest(readFileSync(destFile)) === manifest.skills[name]) {
|
|
200
|
+
rmSync(destFile); // remove ONLY our file (not the dir, not a user's files)
|
|
201
|
+
try {
|
|
202
|
+
if (existsSync(dir) && readdirSync(dir).length === 0)
|
|
203
|
+
rmdirSync(dir); // drop the dir only if now empty (rmdir refuses a non-empty dir)
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
/* not empty or already gone: leave it */
|
|
207
|
+
}
|
|
208
|
+
removed.push(name);
|
|
209
|
+
}
|
|
210
|
+
// a retired skill the user has since edited, or whose dir holds their files, is left in place
|
|
211
|
+
delete manifest.skills[name];
|
|
212
|
+
}
|
|
213
|
+
writeManifest(manifest);
|
|
214
|
+
return { installed: names, backedUp, removed };
|
|
215
|
+
}
|
|
216
|
+
/** Compare the managed `~/.agents/skills` tree against canonical so `cotal status` can surface drift:
|
|
217
|
+
* `current` (identical), `stale` (present but differs), `missing` (not dropped), or `retired` (a skill
|
|
218
|
+
* Cotal still owns on disk but no longer ships, awaiting removal on the next `cotal setup`). Throws on
|
|
219
|
+
* a corrupt bundle or manifest; the caller renders that as an integrity error. */
|
|
220
|
+
export function agentSkillsSkew() {
|
|
221
|
+
const src = canonicalSkillsDir();
|
|
222
|
+
const names = canonicalSkillNames();
|
|
223
|
+
const home = agentSkillsHome();
|
|
224
|
+
const out = names.map((name) => {
|
|
225
|
+
const installed = join(home, name, "SKILL.md");
|
|
226
|
+
if (!existsSync(installed))
|
|
227
|
+
return { name, state: "missing" };
|
|
228
|
+
const canonical = readFileSync(join(src, name, "SKILL.md"));
|
|
229
|
+
return { name, state: readFileSync(installed).equals(canonical) ? "current" : "stale" };
|
|
230
|
+
});
|
|
231
|
+
const manifest = readManifest();
|
|
232
|
+
for (const name of Object.keys(manifest.skills)) {
|
|
233
|
+
if (names.includes(name))
|
|
234
|
+
continue;
|
|
235
|
+
if (existsSync(join(home, name, "SKILL.md")))
|
|
236
|
+
out.push({ name, state: "retired" });
|
|
237
|
+
}
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=agent-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-skills.js","sourceRoot":"","sources":["../../src/lib/agent-skills.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/auth-proc.d.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { type AuthPrepared } from "@cotal-ai/core";
|
|
2
2
|
export { resolveAuthProvider } from "@cotal-ai/core";
|
|
3
|
+
/** Reclaim a DEAD pre-hex pidfile before a fresh start claims the canonical (hex) slot. A crash on
|
|
4
|
+
* a pre-upgrade build leaves `auth-service.<encoded>.pid`; the new start claims
|
|
5
|
+
* `auth-service.<hex>.pid` and, without this, BOTH would then exist - which `readPidPath` correctly
|
|
6
|
+
* refuses as ambiguous, wedging every later status/down.
|
|
7
|
+
*
|
|
8
|
+
* It reclaims ONLY what the canonical claim ({@link claimAuthPidSlot}) would: an empty slot (a
|
|
9
|
+
* pre-protocol crash) or a positive finite PID proven DEAD. It NEVER deletes an unattributable
|
|
10
|
+
* record - garbled/non-numeric/non-positive content, or a still-LIVE legacy holder - because that
|
|
11
|
+
* is exactly the live pre-hex signer this whole path exists not to orphan (a torn or tampered
|
|
12
|
+
* pidfile of a running service reads as garbled). Those cases THROW so the start aborts loud rather
|
|
13
|
+
* than delete the record and launch a competing service. Byte-exact match, no case-fold. Exported
|
|
14
|
+
* for the boundary smoke, which must exercise the real reclaim, not a stand-in. */
|
|
15
|
+
export declare function reclaimDeadLegacyPid(space: string): void;
|
|
3
16
|
/** True if the auth service we started for THIS space is still running (pid file + liveness). */
|
|
4
17
|
export declare function authServiceUp(space: string): boolean;
|
|
5
18
|
/** EXCLUSIVE pidfile claim (#29 HIGH 3 belt): the slot is published ATOMICALLY and
|
|
@@ -35,6 +48,11 @@ export declare function ensureAuthService(opts: {
|
|
|
35
48
|
prepared: AuthPrepared;
|
|
36
49
|
}): Promise<Record<string, unknown>>;
|
|
37
50
|
/** Stop THIS space's auth service if we started one. Scoped by the space-carrying pid file — never
|
|
38
|
-
* a root-global kill.
|
|
51
|
+
* a root-global kill. Honors the same attribution contract as {@link claimAuthPidSlot}: it only
|
|
52
|
+
* removes a record it can ACT on. An empty slot is a husk (nothing to signal, safe to clear); a
|
|
53
|
+
* positive PID is signalled then removed; unattributable content (garbled, or a non-positive value
|
|
54
|
+
* that would make `process.kill` signal a whole process GROUP) is NEVER removed and fails loud -
|
|
55
|
+
* silently dropping it would orphan a live signer behind a torn/tampered pidfile while reporting a
|
|
56
|
+
* clean stop. */
|
|
39
57
|
export declare function stopAuthService(space: string): void;
|
|
40
58
|
//# sourceMappingURL=auth-proc.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-proc.d.ts","sourceRoot":"","sources":["../../src/lib/auth-proc.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth-proc.d.ts","sourceRoot":"","sources":["../../src/lib/auth-proc.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA6BnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;;;;;;;;;oFAWoF;AACpF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CA4BxD;AAED,iGAAiG;AACjG,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKpD;AAED;;;;;;;;;;;;;;;6DAe6D;AAC7D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAmChG;AAiCD;;;;yDAIyD;AACzD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,YAAY,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAOnC;AAED;;;;;;kBAMkB;AAClB,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAyBnD"}
|
package/dist/lib/auth-proc.js
CHANGED
|
@@ -8,31 +8,86 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
10
|
import { randomBytes } from "node:crypto";
|
|
11
|
-
import { closeSync, existsSync, ftruncateSync, linkSync, openSync, readFileSync, rmSync, writeSync } from "node:fs";
|
|
11
|
+
import { closeSync, existsSync, ftruncateSync, linkSync, openSync, readdirSync, readFileSync, rmSync, writeSync } from "node:fs";
|
|
12
|
+
import { basename, dirname } from "node:path";
|
|
12
13
|
import {} from "@cotal-ai/core";
|
|
14
|
+
import { spaceKey } from "@cotal-ai/workspace";
|
|
15
|
+
import { parsePid, probeLiveness } from "./pid.js";
|
|
13
16
|
import { selfArgv } from "./self-exec.js";
|
|
14
17
|
import { cotalPath } from "./paths.js";
|
|
15
|
-
const PID_PATH = (space) => cotalPath(`auth-service.${
|
|
16
|
-
const LOG_PATH = (space) => cotalPath(`auth-service.${
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const PID_PATH = (space) => cotalPath(`auth-service.${spaceKey(space)}.pid`);
|
|
19
|
+
const LOG_PATH = (space) => cotalPath(`auth-service.${spaceKey(space)}.log`);
|
|
20
|
+
/** The pidfile to READ for a space's auth service: the canonical hex name, or the pre-hex
|
|
21
|
+
* `auth-service.<encoded>.pid` a build before the re-key wrote. `up`'s restart and its stop read
|
|
22
|
+
* this so an upgrade never orphans the live callout SIGNER of a pre-upgrade auth-service. Byte-
|
|
23
|
+
* exact (a bare `existsSync` case-folds a sibling space's file on macOS/Windows); both present is
|
|
24
|
+
* ambiguous and fails loud. Starts always WRITE the canonical PID_PATH. */
|
|
25
|
+
function readPidPath(space) {
|
|
26
|
+
const canonical = PID_PATH(space);
|
|
27
|
+
const legacy = cotalPath(`auth-service.${encodeURIComponent(space)}.pid`);
|
|
28
|
+
if (legacy === canonical)
|
|
29
|
+
return canonical;
|
|
30
|
+
const exact = (p) => { try {
|
|
31
|
+
return readdirSync(dirname(p)).includes(basename(p));
|
|
21
32
|
}
|
|
22
33
|
catch {
|
|
23
34
|
return false;
|
|
24
|
-
}
|
|
35
|
+
} };
|
|
36
|
+
const c = exact(canonical), l = exact(legacy);
|
|
37
|
+
if (c && l)
|
|
38
|
+
throw new Error(`both ${canonical} and the pre-hex ${legacy} exist for space "${space}" - ambiguous auth-service record; remove the stale one`);
|
|
39
|
+
return l && !c ? legacy : canonical;
|
|
25
40
|
}
|
|
41
|
+
// parsePid + probeLiveness (the pid-attribution contract) live in ./pid.js, shared with `down` so
|
|
42
|
+
// there is exactly ONE parser + ONE tri-state probe across the pidfile subsystem.
|
|
26
43
|
// Provider resolution now lives in core (the manager needs the identical resolution); re-exported
|
|
27
44
|
// so existing CLI imports keep working.
|
|
28
45
|
export { resolveAuthProvider } from "@cotal-ai/core";
|
|
46
|
+
/** Reclaim a DEAD pre-hex pidfile before a fresh start claims the canonical (hex) slot. A crash on
|
|
47
|
+
* a pre-upgrade build leaves `auth-service.<encoded>.pid`; the new start claims
|
|
48
|
+
* `auth-service.<hex>.pid` and, without this, BOTH would then exist - which `readPidPath` correctly
|
|
49
|
+
* refuses as ambiguous, wedging every later status/down.
|
|
50
|
+
*
|
|
51
|
+
* It reclaims ONLY what the canonical claim ({@link claimAuthPidSlot}) would: an empty slot (a
|
|
52
|
+
* pre-protocol crash) or a positive finite PID proven DEAD. It NEVER deletes an unattributable
|
|
53
|
+
* record - garbled/non-numeric/non-positive content, or a still-LIVE legacy holder - because that
|
|
54
|
+
* is exactly the live pre-hex signer this whole path exists not to orphan (a torn or tampered
|
|
55
|
+
* pidfile of a running service reads as garbled). Those cases THROW so the start aborts loud rather
|
|
56
|
+
* than delete the record and launch a competing service. Byte-exact match, no case-fold. Exported
|
|
57
|
+
* for the boundary smoke, which must exercise the real reclaim, not a stand-in. */
|
|
58
|
+
export function reclaimDeadLegacyPid(space) {
|
|
59
|
+
const legacy = cotalPath(`auth-service.${encodeURIComponent(space)}.pid`);
|
|
60
|
+
if (legacy === PID_PATH(space))
|
|
61
|
+
return;
|
|
62
|
+
try {
|
|
63
|
+
if (!readdirSync(dirname(legacy)).includes(basename(legacy)))
|
|
64
|
+
return; // byte-exact absent
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return; // parent dir gone → nothing to reclaim
|
|
68
|
+
}
|
|
69
|
+
const trimmed = readFileSync(legacy, "utf8").trim();
|
|
70
|
+
if (trimmed === "") {
|
|
71
|
+
rmSync(legacy, { force: true }); // empty = pre-protocol husk, safe to reclaim (as the canonical claim does)
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const pid = parsePid(trimmed);
|
|
75
|
+
if (pid === undefined)
|
|
76
|
+
throw new Error(`pre-hex auth-service pidfile ${legacy} holds unattributable content ${JSON.stringify(trimmed)} - refusing to reclaim it or start a competing service; inspect or remove it manually`);
|
|
77
|
+
const state = probeLiveness(pid);
|
|
78
|
+
if (state === "alive")
|
|
79
|
+
throw new Error(`a pre-hex auth-service is already running (pid ${pid}) at ${legacy} - refusing to start a second; run \`cotal down\` to stop it, then retry`);
|
|
80
|
+
if (state === "unknown")
|
|
81
|
+
throw new Error(`cannot determine whether pid ${pid} in ${legacy} is alive - refusing to reclaim an unattributable record or start a competing service; inspect or remove it manually`);
|
|
82
|
+
rmSync(legacy, { force: true }); // ESRCH-proven dead - reclaim it
|
|
83
|
+
}
|
|
29
84
|
/** True if the auth service we started for THIS space is still running (pid file + liveness). */
|
|
30
85
|
export function authServiceUp(space) {
|
|
31
|
-
const p =
|
|
86
|
+
const p = readPidPath(space);
|
|
32
87
|
if (!existsSync(p))
|
|
33
88
|
return false;
|
|
34
|
-
const pid =
|
|
35
|
-
return
|
|
89
|
+
const pid = parsePid(readFileSync(p, "utf8")); // strict positive safe integer, or undefined
|
|
90
|
+
return pid !== undefined && probeLiveness(pid) === "alive"; // up only if provably present; dead/unknown → not up
|
|
36
91
|
}
|
|
37
92
|
/** EXCLUSIVE pidfile claim (#29 HIGH 3 belt): the slot is published ATOMICALLY and
|
|
38
93
|
* PRE-POPULATED, so no reader can ever observe a legitimately-empty claim and no create/write
|
|
@@ -77,17 +132,21 @@ export function claimAuthPidSlot(space) {
|
|
|
77
132
|
continue; // vanished between publish-refusal and read - retry the claim once
|
|
78
133
|
}
|
|
79
134
|
const trimmed = raw.trim();
|
|
80
|
-
|
|
81
|
-
|
|
135
|
+
// Reclaim ONLY an empty pre-protocol slot or a pid ESRCH-proven dead. A live/EPERM holder is
|
|
136
|
+
// adopted; an unattributable record (garbled/fractional/unsignalable, or a probe that is not a
|
|
137
|
+
// clean ESRCH) is never stolen - yield.
|
|
138
|
+
const pid = parsePid(trimmed);
|
|
139
|
+
const state = pid === undefined ? undefined : probeLiveness(pid);
|
|
140
|
+
if (state === "alive")
|
|
82
141
|
return { livePid: pid };
|
|
83
|
-
if ((trimmed === "" ||
|
|
142
|
+
if ((trimmed === "" || state === "dead") && attempt === 0) {
|
|
84
143
|
try {
|
|
85
144
|
rmSync(target);
|
|
86
145
|
}
|
|
87
146
|
catch { /* a sibling removed it first */ }
|
|
88
|
-
continue; //
|
|
147
|
+
continue; // empty pre-protocol slot, or ESRCH-proven-dead holder: reclaim ONCE
|
|
89
148
|
}
|
|
90
|
-
return undefined; //
|
|
149
|
+
return undefined; // unattributable/unknown content (or still contested after the retry): never steal
|
|
91
150
|
}
|
|
92
151
|
}
|
|
93
152
|
return undefined;
|
|
@@ -96,6 +155,8 @@ export function claimAuthPidSlot(space) {
|
|
|
96
155
|
* The pid slot is claimed exclusively FIRST ({@link claimAuthPidSlot}); a held or contested slot
|
|
97
156
|
* yields to the existing daemon (the caller's ready() poll adjudicates liveness). */
|
|
98
157
|
function startAuthServiceDetached(space, server, command) {
|
|
158
|
+
reclaimDeadLegacyPid(space); // a pre-hex crash leaves a dead legacy pidfile; clear it or the
|
|
159
|
+
// canonical claim below produces the both-present wedge readPidPath refuses
|
|
99
160
|
const slot = claimAuthPidSlot(space);
|
|
100
161
|
if (slot === undefined)
|
|
101
162
|
return 0;
|
|
@@ -139,20 +200,34 @@ export async function ensureAuthService(opts) {
|
|
|
139
200
|
}
|
|
140
201
|
}
|
|
141
202
|
/** Stop THIS space's auth service if we started one. Scoped by the space-carrying pid file — never
|
|
142
|
-
* a root-global kill.
|
|
203
|
+
* a root-global kill. Honors the same attribution contract as {@link claimAuthPidSlot}: it only
|
|
204
|
+
* removes a record it can ACT on. An empty slot is a husk (nothing to signal, safe to clear); a
|
|
205
|
+
* positive PID is signalled then removed; unattributable content (garbled, or a non-positive value
|
|
206
|
+
* that would make `process.kill` signal a whole process GROUP) is NEVER removed and fails loud -
|
|
207
|
+
* silently dropping it would orphan a live signer behind a torn/tampered pidfile while reporting a
|
|
208
|
+
* clean stop. */
|
|
143
209
|
export function stopAuthService(space) {
|
|
144
|
-
const p =
|
|
210
|
+
const p = readPidPath(space); // find a pre-hex pidfile too, or an upgrade leaks the signer
|
|
145
211
|
if (!existsSync(p))
|
|
146
212
|
return;
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
213
|
+
const trimmed = readFileSync(p, "utf8").trim();
|
|
214
|
+
if (trimmed === "") {
|
|
215
|
+
rmSync(p, { force: true }); // empty husk: no process to signal
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const pid = parsePid(trimmed);
|
|
219
|
+
if (pid === undefined)
|
|
220
|
+
throw new Error(`auth-service pidfile ${p} holds unattributable content ${JSON.stringify(trimmed)} - refusing to remove a record for a process it cannot identify or signal; inspect or remove it manually`);
|
|
221
|
+
try {
|
|
222
|
+
process.kill(pid, "SIGTERM");
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
// ESRCH = already gone, so removing its record is safe. EPERM (exists, another user's) or any
|
|
226
|
+
// other errno means we could NOT stop it - removing the record would orphan a live signer while
|
|
227
|
+
// reporting a clean stop, so preserve it and fail loud.
|
|
228
|
+
if (e.code !== "ESRCH")
|
|
229
|
+
throw new Error(`could not stop auth-service (pid ${pid}) at ${p} (${e.code ?? "unknown error"}) - refusing to remove a record for a process it could not signal; stop it manually`);
|
|
155
230
|
}
|
|
156
|
-
rmSync(p);
|
|
231
|
+
rmSync(p, { force: true });
|
|
157
232
|
}
|
|
158
233
|
//# sourceMappingURL=auth-proc.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-proc.js","sourceRoot":"","sources":["../../src/lib/auth-proc.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-proc.js","sourceRoot":"","sources":["../../src/lib/auth-proc.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjI,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAqB,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,SAAS,CAAC,gBAAgB,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACrF,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,SAAS,CAAC,gBAAgB,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAErF;;;;4EAI4E;AAC5E,SAAS,WAAW,CAAC,KAAa;IAChC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,SAAS,CAAC,gBAAgB,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1E,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,GAAG,IAAI,CAAC;QAAC,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC,CAAC,CAAC,CAAC;IACvH,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,SAAS,oBAAoB,MAAM,qBAAqB,KAAK,yDAAyD,CAAC,CAAC;IAC5J,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACtC,CAAC;AAED,kGAAkG;AAClG,kFAAkF;AAElF,kGAAkG;AAClG,wCAAwC;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;;;;;;;;;oFAWoF;AACpF,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,MAAM,GAAG,SAAS,CAAC,gBAAgB,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1E,IAAI,MAAM,KAAK,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO;IACvC,IAAI,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAAE,OAAO,CAAC,oBAAoB;IAC5F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,uCAAuC;IACjD,CAAC;IACD,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,2EAA2E;QAC5G,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,SAAS;QACnB,MAAM,IAAI,KAAK,CACb,gCAAgC,MAAM,iCAAiC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,uFAAuF,CACtL,CAAC;IACJ,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,OAAO;QACnB,MAAM,IAAI,KAAK,CACb,kDAAkD,GAAG,QAAQ,MAAM,0EAA0E,CAC9I,CAAC;IACJ,IAAI,KAAK,KAAK,SAAS;QACrB,MAAM,IAAI,KAAK,CACb,gCAAgC,GAAG,OAAO,MAAM,sHAAsH,CACvK,CAAC;IACJ,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,iCAAiC;AACpE,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,GAAG,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,6CAA6C;IAC5F,OAAO,GAAG,KAAK,SAAS,IAAI,aAAa,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,CAAC,qDAAqD;AACnH,CAAC;AAED;;;;;;;;;;;;;;;6DAe6D;AAC7D,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,GAAG,MAAM,UAAU,OAAO,CAAC,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAChF,IAAI,EAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC1B,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,6DAA6D;YACrF,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,+DAA+D;YAC7E,OAAO,EAAE,EAAE,EAAE,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,EAAE,KAAK,SAAS;gBAAE,SAAS,CAAC,EAAE,CAAC,CAAC;YACpC,IAAI,CAAC;gBAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,oCAAoC,CAAC,CAAC;YACpE,IAAI,GAAW,CAAC;YAChB,IAAI,CAAC;gBACH,GAAG,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACrC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,CAAC,mEAAmE;YAC/E,CAAC;YACD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;YAC3B,6FAA6F;YAC7F,+FAA+F;YAC/F,wCAAwC;YACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC9B,MAAM,KAAK,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;YACjE,IAAI,KAAK,KAAK,OAAO;gBAAE,OAAO,EAAE,OAAO,EAAE,GAAI,EAAE,CAAC;YAChD,IAAI,CAAC,OAAO,KAAK,EAAE,IAAI,KAAK,KAAK,MAAM,CAAC,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;gBAC1D,IAAI,CAAC;oBAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,gCAAgC,CAAC,CAAC;gBAClE,SAAS,CAAC,qEAAqE;YACjF,CAAC;YACD,OAAO,SAAS,CAAC,CAAC,mFAAmF;QACvG,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;sFAEsF;AACtF,SAAS,wBAAwB,CAAC,KAAa,EAAE,MAAc,EAAE,OAAe;IAC9E,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,gEAAgE;IAChE,4EAA4E;IACzG,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACjC,IAAI,SAAS,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,QAAQ,EAAE,CAAC;QACnC,gGAAgG;QAChG,qGAAqG;QACrG,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE;YAClF,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC;YACzB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,yBAAyB,EAAE,GAAG,EAAE;SACxD,CAAC,CAAC;QACH,SAAS,CAAC,EAAE,CAAC,CAAC;QACd,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,2FAA2F;QAC3F,gFAAgF;QAChF,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACzC,OAAO,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACxB,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;AACH,CAAC;AAED;;;;yDAIyD;AACzD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAKvC;IACC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,wBAAwB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACjH,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACnE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACjG,CAAC;AACH,CAAC;AAED;;;;;;kBAMkB;AAClB,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,6DAA6D;IAC3F,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO;IAC3B,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/C,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,MAAM,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,mCAAmC;QAC/D,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,SAAS;QACnB,MAAM,IAAI,KAAK,CACb,wBAAwB,CAAC,iCAAiC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,0GAA0G,CAC5L,CAAC;IACJ,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,8FAA8F;QAC9F,gGAAgG;QAChG,wDAAwD;QACxD,IAAK,CAA2B,CAAC,IAAI,KAAK,OAAO;YAC/C,MAAM,IAAI,KAAK,CACb,oCAAoC,GAAG,QAAQ,CAAC,KAAM,CAA2B,CAAC,IAAI,IAAI,eAAe,qFAAqF,CAC/L,CAAC;IACN,CAAC;IACD,MAAM,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7B,CAAC"}
|
package/dist/lib/control.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CotalEndpoint, DEFAULT_SPACE, CONTROL_PRIVILEGED, CONTROL_ADMIN, } from "@cotal-ai/core";
|
|
2
|
-
import { authDir, endpointAuth, findCotalRoot, loadSpaceAuth } from "@cotal-ai/workspace";
|
|
2
|
+
import { authDir, endpointAuth, findCotalRoot, loadSpaceAuth, soleSpaceOf } from "@cotal-ai/workspace";
|
|
3
3
|
import { c, staleStoreHint } from "../ui.js";
|
|
4
4
|
import { connectOrExit } from "./connect.js";
|
|
5
5
|
/** Client-side request window for the manager's readiness-waiting launch ops (`start`, and the
|
|
@@ -21,7 +21,7 @@ export const START_TIMEOUT_MS = 40_000;
|
|
|
21
21
|
*/
|
|
22
22
|
export async function resolveControlTarget(flags, profile) {
|
|
23
23
|
const withSpace = flags.creds
|
|
24
|
-
? { ...flags, space: flags.space ??
|
|
24
|
+
? { ...flags, space: flags.space ?? soleSpaceOf(authDir(findCotalRoot())) ?? DEFAULT_SPACE }
|
|
25
25
|
: flags;
|
|
26
26
|
// USER MODE rides through: the control call connects with the operator's bearer (actor `cli`) and
|
|
27
27
|
// publishes on its OWN ctl principal subject — the broker grants that publish only when the cli
|
package/dist/lib/control.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.js","sourceRoot":"","sources":["../../src/lib/control.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,GAId,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"control.js","sourceRoot":"","sources":["../../src/lib/control.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { existsSync, openSync, closeSync, writeFileSync, readFileSync, rmSync } from "node:fs";
|
|
3
3
|
import { DEFAULT_SERVER, mintCreds, newIdentity, waitForDeliveryLease, } from "@cotal-ai/core";
|
|
4
|
-
import { DELIVERY_CREDS_KEY, authDir, findCotalRoot,
|
|
4
|
+
import { DELIVERY_CREDS_KEY, authDir, findCotalRoot, getSoleSpaceAuth, listSpaceAccounts, workspaceSecretStore } from "@cotal-ai/workspace";
|
|
5
5
|
import { selfArgv } from "./self-exec.js";
|
|
6
6
|
import { resolveSpace } from "./status.js";
|
|
7
7
|
import { cotalPath } from "./paths.js";
|
|
@@ -30,7 +30,7 @@ export function deliveryUp() {
|
|
|
30
30
|
/** True when this folder runs an authed mesh — the only mode with a delivery daemon (Plane-3 needs the
|
|
31
31
|
* trusted reader; open dev mode is live-only). */
|
|
32
32
|
function hasAuth() {
|
|
33
|
-
return
|
|
33
|
+
return listSpaceAccounts(authDir(findCotalRoot())).length > 0;
|
|
34
34
|
}
|
|
35
35
|
/** True if an OLD (pre-delivery-daemon) Plane-3-hosting manager is live: a `manager.pid` that is alive
|
|
36
36
|
* but carries no delivery-aware marker. Such a manager still calls `startPlane3` and would double-bind
|
|
@@ -85,7 +85,7 @@ export async function ensureDelivery(o = {}) {
|
|
|
85
85
|
// written to disk). The daemon process reads the file and never holds the signer (a container mounts it
|
|
86
86
|
// read-only). A reuse (daemon already up) mints a throwaway probe cred — the running daemon keeps its
|
|
87
87
|
// own creds file.
|
|
88
|
-
const auth =
|
|
88
|
+
const auth = (await getSoleSpaceAuth(credsStore(), authDir(findCotalRoot())));
|
|
89
89
|
const id = newIdentity();
|
|
90
90
|
const creds = await mintCreds(auth, id, "delivery");
|
|
91
91
|
const space = o.space ?? resolveSpace(process.cwd());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery-proc.js","sourceRoot":"","sources":["../../src/lib/delivery-proc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC/F,OAAO,EACL,cAAc,EACd,SAAS,EACT,WAAW,EACX,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"delivery-proc.js","sourceRoot":"","sources":["../../src/lib/delivery-proc.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maintenance-files.d.ts","sourceRoot":"","sources":["../../src/lib/maintenance-files.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"maintenance-files.d.ts","sourceRoot":"","sources":["../../src/lib/maintenance-files.ts"],"names":[],"mappings":"AAOA,OAAO,EAAoG,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEjK,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CACzB;AAgDD,gGAAgG;AAChG,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAuBhI"}
|