shadok-ai 0.17.41 → 0.17.43
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/dist/migrate-groups.d.ts +52 -8
- package/dist/migrate-groups.js +115 -9
- package/dist/migrate-groups.js.map +1 -1
- package/dist/server.js +62 -30
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/migrate-groups.d.ts
CHANGED
|
@@ -99,15 +99,59 @@ export declare function commonsOsUser(input: {
|
|
|
99
99
|
* path is unreadable and the guard dies `check-failed` in silence. The migration
|
|
100
100
|
* relocates the `.claude` tree into the new HOME, so the reference must follow.
|
|
101
101
|
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
102
|
+
* The `<oldHome>/.claude` base is ALWAYS rewritten — the tree the migration moved —
|
|
103
|
+
* plus any extra RELATIVE prefix the caller passes (the `~/.shadok-ai/<dir>` script
|
|
104
|
+
* dirs a guard also names; see `shadokScriptDirs`). Every OTHER `/root/...` mention
|
|
105
|
+
* is left intact, so a reference to a dir that was NOT relocated keeps pointing at
|
|
106
|
+
* the original. Identity when the home did not move (mono-tenant, or old === new) or
|
|
107
|
+
* when either base is blank (never blank out a path). A `$HOME`-relative check is
|
|
108
|
+
* already portable and contains no absolute base to match, so it passes through
|
|
109
|
+
* untouched. Pure, so the boot self-heal that walks the cron file stays a thin
|
|
110
|
+
* caller — and the exact substitution is locked by test, the twin of
|
|
111
|
+
* `rewriteSkillsBase`.
|
|
109
112
|
*/
|
|
110
|
-
export declare function rewriteCronCheckHome(check: string, oldHome: string, newHome: string): string;
|
|
113
|
+
export declare function rewriteCronCheckHome(check: string, oldHome: string, newHome: string, extraRelPrefixes?: readonly string[]): string;
|
|
114
|
+
/**
|
|
115
|
+
* Shadok's OWN directories under `~/.shadok-ai` — NEVER carried into the commons
|
|
116
|
+
* HOME by the stranded-script heal, even if a cron check names one. The credential
|
|
117
|
+
* stores (`ssh`, `users`, `secrets`) are the load-bearing entries: copying them
|
|
118
|
+
* into `/home/main` would hand every commons agent the SSH private key, the
|
|
119
|
+
* per-session signing keys and the account password hashes. The rest are internal
|
|
120
|
+
* per-instance state a guard has no business reading. A denylist (not an allowlist)
|
|
121
|
+
* on purpose: an operator/agent names its script dir whatever it likes (`scripts`,
|
|
122
|
+
* `checks`, `marketing`, …), and an allowlist would silently fail to heal a
|
|
123
|
+
* custom-named one — reintroducing the exact silent breakage this closes.
|
|
124
|
+
*/
|
|
125
|
+
export declare const SHADOK_INTERNAL_DIRS: readonly string[];
|
|
126
|
+
/**
|
|
127
|
+
* The `~/.claude/<dir>` subtrees NEVER carried by the heal. `.credentials` is a
|
|
128
|
+
* FILE, not a dir, so it can't match anyway — the real entries are the big
|
|
129
|
+
* already-migrated trees (`migChownTreeToCommons` copies `projects`/`todos`/
|
|
130
|
+
* `shell-snapshots`/`file-history`) and the caches (`statsig`/`plugins`/`ide`):
|
|
131
|
+
* re-copying them would be wasteful, and they are not scripts a guard runs.
|
|
132
|
+
* `skills` and `scripts` (and any custom dir) are deliberately NOT here — those
|
|
133
|
+
* are where an agent keeps the monitoring scripts it authored.
|
|
134
|
+
*/
|
|
135
|
+
export declare const CLAUDE_INTERNAL_DIRS: readonly string[];
|
|
136
|
+
/**
|
|
137
|
+
* The home-rooted `<top>/<sub>` script directories a cron `check` names, that the
|
|
138
|
+
* HOME move off `/root` stranded. A dropped `main` guard cannot traverse `/root`
|
|
139
|
+
* (0700), so ANY `/root/<top>/<sub>/…` path it names — a script or its sibling
|
|
140
|
+
* state file — is unreachable and the guard dies `check-failed` in silence. #419
|
|
141
|
+
* relocated only `.claude/skills`; this generalises the discovery so `.claude/scripts`
|
|
142
|
+
* (seen on the biosense cockpit), every `.shadok-ai/<dir>` (the dogfood instance),
|
|
143
|
+
* and any future script dir are all carried across — the trap is not one directory,
|
|
144
|
+
* it is "any `/root` path a check names".
|
|
145
|
+
*
|
|
146
|
+
* Matches an absolute `oldHome` reference AND a `$HOME`/`${HOME}`/`~`-relative one
|
|
147
|
+
* (a portable check still needs its dir to EXIST in the new HOME). Only the 2-level
|
|
148
|
+
* `<top>/<sub>` unit is relocated, with `<top>` restricted to the two script homes
|
|
149
|
+
* and `<sub>` refused when it is shadok's own state/credentials or a big
|
|
150
|
+
* already-migrated `.claude` tree (`RELOCATABLE_TOPS`). Returns distinct RELATIVE
|
|
151
|
+
* prefixes (`".claude/scripts"`, `".shadok-ai/checks"`, …), ready to feed
|
|
152
|
+
* `rewriteCronCheckHome` and `path.join`. Pure.
|
|
153
|
+
*/
|
|
154
|
+
export declare function strandedCheckDirs(check: string, oldHome: string): string[];
|
|
111
155
|
/**
|
|
112
156
|
* The running agents to respawn, as session ids — deduped, order preserved so
|
|
113
157
|
* the caller cycles them ONE AT A TIME (the OAuth refresh-token race,
|
package/dist/migrate-groups.js
CHANGED
|
@@ -100,18 +100,124 @@ export function commonsOsUser(input) {
|
|
|
100
100
|
* path is unreadable and the guard dies `check-failed` in silence. The migration
|
|
101
101
|
* relocates the `.claude` tree into the new HOME, so the reference must follow.
|
|
102
102
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
103
|
+
* The `<oldHome>/.claude` base is ALWAYS rewritten — the tree the migration moved —
|
|
104
|
+
* plus any extra RELATIVE prefix the caller passes (the `~/.shadok-ai/<dir>` script
|
|
105
|
+
* dirs a guard also names; see `shadokScriptDirs`). Every OTHER `/root/...` mention
|
|
106
|
+
* is left intact, so a reference to a dir that was NOT relocated keeps pointing at
|
|
107
|
+
* the original. Identity when the home did not move (mono-tenant, or old === new) or
|
|
108
|
+
* when either base is blank (never blank out a path). A `$HOME`-relative check is
|
|
109
|
+
* already portable and contains no absolute base to match, so it passes through
|
|
110
|
+
* untouched. Pure, so the boot self-heal that walks the cron file stays a thin
|
|
111
|
+
* caller — and the exact substitution is locked by test, the twin of
|
|
112
|
+
* `rewriteSkillsBase`.
|
|
110
113
|
*/
|
|
111
|
-
export function rewriteCronCheckHome(check, oldHome, newHome) {
|
|
114
|
+
export function rewriteCronCheckHome(check, oldHome, newHome, extraRelPrefixes = []) {
|
|
112
115
|
if (!check || !oldHome || !newHome || oldHome === newHome)
|
|
113
116
|
return check;
|
|
114
|
-
|
|
117
|
+
let out = check;
|
|
118
|
+
for (const rel of [".claude", ...extraRelPrefixes]) {
|
|
119
|
+
out = out.split(oldHome + "/" + rel).join(newHome + "/" + rel);
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
/** Escape a string for literal use inside a `RegExp`. */
|
|
124
|
+
function escapeRegExp(s) {
|
|
125
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Shadok's OWN directories under `~/.shadok-ai` — NEVER carried into the commons
|
|
129
|
+
* HOME by the stranded-script heal, even if a cron check names one. The credential
|
|
130
|
+
* stores (`ssh`, `users`, `secrets`) are the load-bearing entries: copying them
|
|
131
|
+
* into `/home/main` would hand every commons agent the SSH private key, the
|
|
132
|
+
* per-session signing keys and the account password hashes. The rest are internal
|
|
133
|
+
* per-instance state a guard has no business reading. A denylist (not an allowlist)
|
|
134
|
+
* on purpose: an operator/agent names its script dir whatever it likes (`scripts`,
|
|
135
|
+
* `checks`, `marketing`, …), and an allowlist would silently fail to heal a
|
|
136
|
+
* custom-named one — reintroducing the exact silent breakage this closes.
|
|
137
|
+
*/
|
|
138
|
+
export const SHADOK_INTERNAL_DIRS = [
|
|
139
|
+
"app",
|
|
140
|
+
"browsers",
|
|
141
|
+
"channels",
|
|
142
|
+
"config",
|
|
143
|
+
"crons",
|
|
144
|
+
"files",
|
|
145
|
+
"keys",
|
|
146
|
+
"ledger",
|
|
147
|
+
"locks",
|
|
148
|
+
"media",
|
|
149
|
+
"peers",
|
|
150
|
+
"pilotctl",
|
|
151
|
+
"profiles",
|
|
152
|
+
"run",
|
|
153
|
+
"secrets",
|
|
154
|
+
"self",
|
|
155
|
+
"ssh",
|
|
156
|
+
"tail",
|
|
157
|
+
"tokens",
|
|
158
|
+
"users",
|
|
159
|
+
"whisper",
|
|
160
|
+
"worktrees",
|
|
161
|
+
"wt",
|
|
162
|
+
];
|
|
163
|
+
/**
|
|
164
|
+
* The `~/.claude/<dir>` subtrees NEVER carried by the heal. `.credentials` is a
|
|
165
|
+
* FILE, not a dir, so it can't match anyway — the real entries are the big
|
|
166
|
+
* already-migrated trees (`migChownTreeToCommons` copies `projects`/`todos`/
|
|
167
|
+
* `shell-snapshots`/`file-history`) and the caches (`statsig`/`plugins`/`ide`):
|
|
168
|
+
* re-copying them would be wasteful, and they are not scripts a guard runs.
|
|
169
|
+
* `skills` and `scripts` (and any custom dir) are deliberately NOT here — those
|
|
170
|
+
* are where an agent keeps the monitoring scripts it authored.
|
|
171
|
+
*/
|
|
172
|
+
export const CLAUDE_INTERNAL_DIRS = [
|
|
173
|
+
"projects",
|
|
174
|
+
"todos",
|
|
175
|
+
"shell-snapshots",
|
|
176
|
+
"file-history",
|
|
177
|
+
"statsig",
|
|
178
|
+
"plugins",
|
|
179
|
+
"ide",
|
|
180
|
+
];
|
|
181
|
+
/** The two home subtrees under which an agent/operator legitimately drops a
|
|
182
|
+
* script a cron guard runs. An ALLOWLIST of TOPS (safe — scripts never live
|
|
183
|
+
* anywhere else under the HOME) combined with a per-top DENYLIST of SUBS (so a
|
|
184
|
+
* custom-named script dir still heals). */
|
|
185
|
+
const RELOCATABLE_TOPS = {
|
|
186
|
+
".claude": CLAUDE_INTERNAL_DIRS,
|
|
187
|
+
".shadok-ai": SHADOK_INTERNAL_DIRS,
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* The home-rooted `<top>/<sub>` script directories a cron `check` names, that the
|
|
191
|
+
* HOME move off `/root` stranded. A dropped `main` guard cannot traverse `/root`
|
|
192
|
+
* (0700), so ANY `/root/<top>/<sub>/…` path it names — a script or its sibling
|
|
193
|
+
* state file — is unreachable and the guard dies `check-failed` in silence. #419
|
|
194
|
+
* relocated only `.claude/skills`; this generalises the discovery so `.claude/scripts`
|
|
195
|
+
* (seen on the biosense cockpit), every `.shadok-ai/<dir>` (the dogfood instance),
|
|
196
|
+
* and any future script dir are all carried across — the trap is not one directory,
|
|
197
|
+
* it is "any `/root` path a check names".
|
|
198
|
+
*
|
|
199
|
+
* Matches an absolute `oldHome` reference AND a `$HOME`/`${HOME}`/`~`-relative one
|
|
200
|
+
* (a portable check still needs its dir to EXIST in the new HOME). Only the 2-level
|
|
201
|
+
* `<top>/<sub>` unit is relocated, with `<top>` restricted to the two script homes
|
|
202
|
+
* and `<sub>` refused when it is shadok's own state/credentials or a big
|
|
203
|
+
* already-migrated `.claude` tree (`RELOCATABLE_TOPS`). Returns distinct RELATIVE
|
|
204
|
+
* prefixes (`".claude/scripts"`, `".shadok-ai/checks"`, …), ready to feed
|
|
205
|
+
* `rewriteCronCheckHome` and `path.join`. Pure.
|
|
206
|
+
*/
|
|
207
|
+
export function strandedCheckDirs(check, oldHome) {
|
|
208
|
+
if (!check || !oldHome)
|
|
209
|
+
return [];
|
|
210
|
+
const homeAlt = [escapeRegExp(oldHome), "\\$HOME", "\\$\\{HOME\\}", "~"].join("|");
|
|
211
|
+
const tops = Object.keys(RELOCATABLE_TOPS).map((t) => escapeRegExp(t)).join("|");
|
|
212
|
+
const re = new RegExp("(?:" + homeAlt + ")/(" + tops + ")/([^/\\s'\"]+)/", "g");
|
|
213
|
+
const out = new Set();
|
|
214
|
+
for (let m = re.exec(check); m; m = re.exec(check)) {
|
|
215
|
+
const [, top, sub] = m;
|
|
216
|
+
if (RELOCATABLE_TOPS[top]?.includes(sub))
|
|
217
|
+
continue;
|
|
218
|
+
out.add(top + "/" + sub);
|
|
219
|
+
}
|
|
220
|
+
return [...out];
|
|
115
221
|
}
|
|
116
222
|
/**
|
|
117
223
|
* The running agents to respawn, as session ids — deduped, order preserved so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate-groups.js","sourceRoot":"","sources":["../src/migrate-groups.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;kDACkD;AAClD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC;AAEnC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,sBAAsB,CAAC;AAuB5D;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,KAK7B;IACC,IAAI,KAAK,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;QAAE,OAAO,IAAI,CAAC;IAE/C,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,0BAA0B,GAAa,EAAE,CAAC;IAChD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,SAAS;QAChC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACZ,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,0EAA0E;IAC1E,0EAA0E;IAC1E,MAAM,iBAAiB,GAAG,KAAK,CAAC,cAAc;SAC3C,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,YAAY,CAAC;SAClE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEpB,OAAO;QACL,iBAAiB,EAAE,IAAI;QACvB,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,IAAI;QACjB,0BAA0B;QAC1B,mBAAmB,EAAE,IAAI;QACzB,iBAAiB;KAClB,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,KAAkD;IAC9E,OAAO,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/E,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"migrate-groups.js","sourceRoot":"","sources":["../src/migrate-groups.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -34,7 +34,7 @@ import { readGroundAt } from "./ground.js";
|
|
|
34
34
|
import { homeGreetingBrief } from "./greeting.js";
|
|
35
35
|
import { ensureSshIdentity } from "./ssh.js";
|
|
36
36
|
import { accountsEnabled, deploymentMode, probeCanProvision } from "./capability.js";
|
|
37
|
-
import { migrationPlan, runMigration, respawnPlan, commonsOsUser, COMMONS_USER, MIGRATION_MARKER_FILE, rewriteCronCheckHome, } from "./migrate-groups.js";
|
|
37
|
+
import { migrationPlan, runMigration, respawnPlan, commonsOsUser, COMMONS_USER, MIGRATION_MARKER_FILE, rewriteCronCheckHome, strandedCheckDirs, } from "./migrate-groups.js";
|
|
38
38
|
import { relayOAuthCallback } from "./oauth-relay.js";
|
|
39
39
|
import { openBrowser } from "./open-browser.js";
|
|
40
40
|
import { parseSkillMeta, prepromptParts, prepromptFingerprint, stalenessReasons, LEGACY_FINGERPRINT, } from "./preprompt.js";
|
|
@@ -3013,20 +3013,27 @@ async function ensureGroupsProvisioned() {
|
|
|
3013
3013
|
preseedGroupHomes();
|
|
3014
3014
|
// And heal the commons agent's own helper scripts + cron guards, which the HOME
|
|
3015
3015
|
// move off /root left stranded (see below).
|
|
3016
|
-
await
|
|
3016
|
+
await healCommonsStrandedScripts();
|
|
3017
3017
|
}
|
|
3018
3018
|
/**
|
|
3019
|
-
* Boot self-heal for the commons agent's OWN
|
|
3020
|
-
*
|
|
3019
|
+
* Boot self-heal for the commons agent's OWN helper scripts and cron guards after
|
|
3020
|
+
* its HOME moved off `/root` to `/home/main`. A cron guard runs DROPPED as the
|
|
3021
|
+
* commons `main` user, which cannot traverse `/root` (0700) — so ANY absolute
|
|
3022
|
+
* `/root/...` path it names (script or sibling state file) is unreadable and the
|
|
3023
|
+
* guard dies `check-failed` in silence (measured on a live instance: monitoring
|
|
3024
|
+
* crons stuck, "son script est sous /root"). New agents are fine (their HOME is
|
|
3025
|
+
* already `/home/main`); this is the one-way carry for what predates the move.
|
|
3021
3026
|
*
|
|
3022
|
-
*
|
|
3023
|
-
*
|
|
3024
|
-
*
|
|
3025
|
-
*
|
|
3026
|
-
*
|
|
3027
|
-
*
|
|
3028
|
-
*
|
|
3029
|
-
* `/
|
|
3027
|
+
* What is relocated here and then repointed:
|
|
3028
|
+
* 1. `~/.claude/skills` — ALWAYS, the subtree `migChownTreeToCommons` skipped,
|
|
3029
|
+
* where an agent keeps the custom skills it authored even when no guard names
|
|
3030
|
+
* them (#419).
|
|
3031
|
+
* 2. Every home-rooted `<top>/<sub>` script dir a cron check NAMES — `.claude/scripts`
|
|
3032
|
+
* (seen on the biosense cockpit), any `.shadok-ai/<dir>` (the dogfood instance),
|
|
3033
|
+
* and any future one — discovered by `strandedCheckDirs`. The trap is not one
|
|
3034
|
+
* directory, it is "any `/root` path a check names". Its denylist refuses shadok's
|
|
3035
|
+
* own state/credential stores and the big already-migrated `.claude` trees, so a
|
|
3036
|
+
* key/secret dir is NEVER copied into `/home/main`.
|
|
3030
3037
|
*
|
|
3031
3038
|
* Runs on EVERY capable boot, independent of the migration marker (the twin of
|
|
3032
3039
|
* the reprovision reconciler above), so an ALREADY-migrated instance self-heals on
|
|
@@ -3035,38 +3042,62 @@ async function ensureGroupsProvisioned() {
|
|
|
3035
3042
|
* (`cp -rn`, non-clobber) and the checks already rewritten (identity). No-op unless
|
|
3036
3043
|
* the HOME actually moved (mono-tenant / pre-migration: old === new).
|
|
3037
3044
|
*/
|
|
3038
|
-
async function
|
|
3045
|
+
async function healCommonsStrandedScripts() {
|
|
3039
3046
|
if (DEPLOYMENT_MODE !== "multi-tenant")
|
|
3040
3047
|
return;
|
|
3041
3048
|
const oldHome = os.homedir();
|
|
3042
3049
|
const newHome = homeForUser(COMMONS_USER, GROUP_HOME_BASE);
|
|
3043
3050
|
if (!newHome || newHome === oldHome)
|
|
3044
3051
|
return; // not migrated off /root → nothing moved
|
|
3045
|
-
|
|
3046
|
-
//
|
|
3047
|
-
//
|
|
3048
|
-
//
|
|
3052
|
+
const owner = `${COMMONS_USER}:${COMMONS_USER}`;
|
|
3053
|
+
// Carry one relative tree <oldHome>/<rel> → <newHome>/<rel>, non-clobber, owned
|
|
3054
|
+
// by `main`. The parent is `install -d`'d too so `~/.shadok-ai` exists in the new
|
|
3055
|
+
// HOME. No-op when the source is absent.
|
|
3056
|
+
const copyTree = async (rel) => {
|
|
3057
|
+
const src = path.join(oldHome, rel);
|
|
3058
|
+
if (!fs.existsSync(src))
|
|
3059
|
+
return;
|
|
3060
|
+
const dst = path.join(newHome, rel);
|
|
3061
|
+
await runPrivileged("install", ["-d", "-o", COMMONS_USER, "-g", COMMONS_USER, path.dirname(dst)]);
|
|
3062
|
+
await runPrivileged("install", ["-d", "-o", COMMONS_USER, "-g", COMMONS_USER, dst]);
|
|
3063
|
+
await runPrivileged("cp", ["-rn", `${src}/.`, `${dst}/`], { timeoutMs: TREE_OP_TIMEOUT_MS });
|
|
3064
|
+
await runPrivileged("chown", ["-R", owner, dst], { timeoutMs: TREE_OP_TIMEOUT_MS });
|
|
3065
|
+
};
|
|
3066
|
+
// Load once; the same list feeds the dir discovery AND the rewrite below.
|
|
3067
|
+
let crons = [];
|
|
3049
3068
|
try {
|
|
3050
|
-
|
|
3051
|
-
if (fs.existsSync(srcSkills)) {
|
|
3052
|
-
const owner = `${COMMONS_USER}:${COMMONS_USER}`;
|
|
3053
|
-
const dstSkills = path.join(newHome, ".claude", "skills");
|
|
3054
|
-
await runPrivileged("install", ["-d", "-o", COMMONS_USER, "-g", COMMONS_USER, dstSkills]);
|
|
3055
|
-
await runPrivileged("cp", ["-rn", `${srcSkills}/.`, `${dstSkills}/`], { timeoutMs: TREE_OP_TIMEOUT_MS });
|
|
3056
|
-
await runPrivileged("chown", ["-R", owner, dstSkills], { timeoutMs: TREE_OP_TIMEOUT_MS });
|
|
3057
|
-
}
|
|
3069
|
+
crons = loadCrons();
|
|
3058
3070
|
}
|
|
3059
3071
|
catch (e) {
|
|
3060
|
-
console.error(`heal:
|
|
3072
|
+
console.error(`heal: reading crons failed (best-effort): ${e?.message ?? e}`);
|
|
3073
|
+
}
|
|
3074
|
+
// The home-rooted <top>/<sub> script dirs the guards name (.claude/scripts,
|
|
3075
|
+
// .shadok-ai/<dir>, …), deduped — never a credential/state dir, strandedCheckDirs
|
|
3076
|
+
// denylists those.
|
|
3077
|
+
const scriptDirs = [
|
|
3078
|
+
...new Set(crons.flatMap((c) => (c.check ? strandedCheckDirs(c.check, oldHome) : []))),
|
|
3079
|
+
];
|
|
3080
|
+
// 1) Relocate ~/.claude/skills unconditionally (the subtree the migration skipped,
|
|
3081
|
+
// where an agent's custom skills live even when no guard names them), plus every
|
|
3082
|
+
// discovered script dir. Deduped; each copy is independent and best-effort.
|
|
3083
|
+
for (const rel of [...new Set([path.join(".claude", "skills"), ...scriptDirs])]) {
|
|
3084
|
+
try {
|
|
3085
|
+
await copyTree(rel);
|
|
3086
|
+
}
|
|
3087
|
+
catch (e) {
|
|
3088
|
+
console.error(`heal: relocating commons ~/${rel} failed (best-effort): ${e?.message ?? e}`);
|
|
3089
|
+
}
|
|
3061
3090
|
}
|
|
3062
|
-
// 2) Repoint this instance's cron guards
|
|
3091
|
+
// 2) Repoint this instance's cron guards: the .claude base (covers any .claude/*
|
|
3092
|
+
// path) AND every relocated .shadok-ai/<dir>. A /root path NOT relocated (a
|
|
3093
|
+
// denylisted or unknown dir) stays intact — surfaced as a check-failed, not
|
|
3094
|
+
// silently repointed to a place nothing was copied.
|
|
3063
3095
|
try {
|
|
3064
|
-
const crons = loadCrons();
|
|
3065
3096
|
let changed = false;
|
|
3066
3097
|
for (const c of crons) {
|
|
3067
3098
|
if (!c.check)
|
|
3068
3099
|
continue;
|
|
3069
|
-
const next = rewriteCronCheckHome(c.check, oldHome, newHome);
|
|
3100
|
+
const next = rewriteCronCheckHome(c.check, oldHome, newHome, scriptDirs);
|
|
3070
3101
|
if (next !== c.check) {
|
|
3071
3102
|
c.check = next;
|
|
3072
3103
|
changed = true;
|
|
@@ -3074,7 +3105,8 @@ async function healCommonsClaudeScripts() {
|
|
|
3074
3105
|
}
|
|
3075
3106
|
if (changed) {
|
|
3076
3107
|
saveCrons(crons);
|
|
3077
|
-
console.log(`heal: repointed cron guard path(s) from ${oldHome}
|
|
3108
|
+
console.log(`heal: repointed cron guard path(s) from ${oldHome} → ${newHome} ` +
|
|
3109
|
+
`(.claude${scriptDirs.length ? ", " + scriptDirs.join(", ") : ""})`);
|
|
3078
3110
|
}
|
|
3079
3111
|
}
|
|
3080
3112
|
catch (e) {
|