@clawos-dev/clawd 0.2.440 → 0.2.441
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/cli.cjs
CHANGED
|
@@ -7089,10 +7089,9 @@ var init_persona_schemas = __esm({
|
|
|
7089
7089
|
writableRoots: external_exports.array(external_exports.string()).optional(),
|
|
7090
7090
|
// 禁读 glob(相对 workspace root 或绝对),如 ".secrets/**"、"**/*.env"。
|
|
7091
7091
|
denyRead: external_exports.array(external_exports.string()).optional(),
|
|
7092
|
-
// true → 网络全放(*);false
|
|
7093
|
-
//
|
|
7094
|
-
//
|
|
7095
|
-
// 「卡片显示开、实际断网」的幽灵态。
|
|
7092
|
+
// true → 网络全放(*);false / 缺省 → 断网(fail-closed)。**「新建默认联网」不靠这里的缺省**,
|
|
7093
|
+
// 靠 persona 创建时就把 CODEX_SANDBOX_DEFAULT 物化进文件(见 PersonaManager.create)——
|
|
7094
|
+
// 缺省只在文件损坏这种异常路径上出现,那时断网是更安全的一侧。
|
|
7096
7095
|
network: external_exports.boolean().optional()
|
|
7097
7096
|
}).strict();
|
|
7098
7097
|
PersonaSandboxSettingsSchema = external_exports.object({
|
|
@@ -47089,7 +47088,7 @@ var SessionManager = class {
|
|
|
47089
47088
|
subSessionMeta.codexSandbox = {
|
|
47090
47089
|
writableRoots: [subSessionMeta.userWorkDir, ...(codexCfg?.writableRoots ?? []).map(expand)],
|
|
47091
47090
|
denyRead: codexCfg?.denyRead ?? [],
|
|
47092
|
-
network: codexCfg?.network ??
|
|
47091
|
+
network: codexCfg?.network ?? false
|
|
47093
47092
|
};
|
|
47094
47093
|
}
|
|
47095
47094
|
if (subSessionMeta?.personaMode === "owner" && file.projectPath) {
|
|
@@ -49466,408 +49465,20 @@ var PersonaRegistry = class {
|
|
|
49466
49465
|
}
|
|
49467
49466
|
};
|
|
49468
49467
|
|
|
49469
|
-
// src/skills/scanner.ts
|
|
49470
|
-
var import_node_fs9 = __toESM(require("fs"), 1);
|
|
49471
|
-
var import_node_os6 = __toESM(require("os"), 1);
|
|
49472
|
-
var import_node_path9 = __toESM(require("path"), 1);
|
|
49473
|
-
|
|
49474
|
-
// src/skills/frontmatter.ts
|
|
49475
|
-
var STRIP_QUOTES = /^["']|["']$/g;
|
|
49476
|
-
function strip(s) {
|
|
49477
|
-
return s.trim().replace(STRIP_QUOTES, "");
|
|
49478
|
-
}
|
|
49479
|
-
function parseFrontmatter(content, keys) {
|
|
49480
|
-
const out = {};
|
|
49481
|
-
if (!content.startsWith("---")) return out;
|
|
49482
|
-
const end = content.indexOf("---", 3);
|
|
49483
|
-
if (end === -1) return out;
|
|
49484
|
-
const lines = content.slice(3, end).split("\n");
|
|
49485
|
-
for (let i = 0; i < lines.length; i++) {
|
|
49486
|
-
const trimmed = lines[i].trim();
|
|
49487
|
-
for (const key of keys) {
|
|
49488
|
-
const prefix = `${key}:`;
|
|
49489
|
-
if (!trimmed.startsWith(prefix)) continue;
|
|
49490
|
-
const rest = trimmed.slice(prefix.length).trim();
|
|
49491
|
-
if (rest === "|" || rest === ">" || rest === "|-" || rest === ">-") {
|
|
49492
|
-
const parts = [];
|
|
49493
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
49494
|
-
const next = lines[j];
|
|
49495
|
-
if (next.length === 0) {
|
|
49496
|
-
parts.push("");
|
|
49497
|
-
continue;
|
|
49498
|
-
}
|
|
49499
|
-
if (!/^\s/.test(next)) break;
|
|
49500
|
-
parts.push(next.replace(/^\s+/, ""));
|
|
49501
|
-
}
|
|
49502
|
-
const value = parts.filter(Boolean).join(" ").trim();
|
|
49503
|
-
if (value) out[key] = value;
|
|
49504
|
-
} else {
|
|
49505
|
-
const value = strip(rest);
|
|
49506
|
-
if (value) out[key] = value;
|
|
49507
|
-
}
|
|
49508
|
-
break;
|
|
49509
|
-
}
|
|
49510
|
-
}
|
|
49511
|
-
return out;
|
|
49512
|
-
}
|
|
49513
|
-
|
|
49514
|
-
// src/skills/builtin-cc-resources.ts
|
|
49515
|
-
var BUILTIN_SKILLS = [
|
|
49516
|
-
{ name: "update-config", source: "builtin", description: "Update Claude Code configuration." },
|
|
49517
|
-
{ name: "keybindings-help", source: "builtin", description: "Show available keybindings." },
|
|
49518
|
-
{ name: "verify", source: "builtin", description: "Verify the implementation." },
|
|
49519
|
-
{ name: "debug", source: "builtin", description: "Debug current issue." },
|
|
49520
|
-
{ name: "lorem-ipsum", source: "builtin", description: "Generate lorem ipsum placeholder text." },
|
|
49521
|
-
{ name: "skillify", source: "builtin", description: "Convert a process into a reusable skill." },
|
|
49522
|
-
{ name: "remember", source: "builtin", description: "Remember context for later use." },
|
|
49523
|
-
{ name: "simplify", source: "builtin", description: "Simplify the code or message." },
|
|
49524
|
-
{ name: "batch", source: "builtin", description: "Run a batch of similar operations." },
|
|
49525
|
-
{ name: "stuck", source: "builtin", description: "Help unstick a stalled task." },
|
|
49526
|
-
{ name: "loop", source: "builtin", description: "Loop a task until a condition is met." },
|
|
49527
|
-
{ name: "cron-list", source: "builtin", description: "List scheduled cron tasks." },
|
|
49528
|
-
{ name: "cron-delete", source: "builtin", description: "Delete a scheduled cron task." },
|
|
49529
|
-
{ name: "dream", source: "builtin", description: "Brainstorm freely without constraints." },
|
|
49530
|
-
{ name: "hunter", source: "builtin", description: "Hunt down a bug or root cause." },
|
|
49531
|
-
{ name: "schedule", source: "builtin", description: "Schedule a task to run later." },
|
|
49532
|
-
{ name: "claude-api", source: "builtin", description: "Use the Claude API directly." },
|
|
49533
|
-
{ name: "claude-in-chrome", source: "builtin", description: "Drive Claude inside Chrome via the Chrome extension." }
|
|
49534
|
-
];
|
|
49535
|
-
var BUILTIN_AGENTS = [
|
|
49536
|
-
{
|
|
49537
|
-
name: "general-purpose",
|
|
49538
|
-
source: "builtin",
|
|
49539
|
-
description: "General-purpose agent for researching complex questions and executing multi-step tasks.",
|
|
49540
|
-
whenToUse: "When you are searching for a keyword or file and are not confident you will find the right match in the first few tries."
|
|
49541
|
-
},
|
|
49542
|
-
{
|
|
49543
|
-
name: "statusline-setup",
|
|
49544
|
-
source: "builtin",
|
|
49545
|
-
description: "Configure the user's Claude Code status line setting.",
|
|
49546
|
-
whenToUse: "When the user is setting up or changing their status line configuration."
|
|
49547
|
-
},
|
|
49548
|
-
{
|
|
49549
|
-
name: "Explore",
|
|
49550
|
-
source: "builtin",
|
|
49551
|
-
description: "Explore the codebase to gather context.",
|
|
49552
|
-
whenToUse: "Before making changes that require understanding the surrounding code."
|
|
49553
|
-
},
|
|
49554
|
-
{
|
|
49555
|
-
name: "Plan",
|
|
49556
|
-
source: "builtin",
|
|
49557
|
-
description: "Produce an implementation plan before writing code.",
|
|
49558
|
-
whenToUse: "When the task is non-trivial and would benefit from an explicit plan."
|
|
49559
|
-
},
|
|
49560
|
-
{
|
|
49561
|
-
name: "claude-code-guide",
|
|
49562
|
-
source: "builtin",
|
|
49563
|
-
description: "Guide the user through Claude Code features.",
|
|
49564
|
-
whenToUse: "When the user needs help understanding what Claude Code can do."
|
|
49565
|
-
},
|
|
49566
|
-
{
|
|
49567
|
-
name: "verification",
|
|
49568
|
-
source: "builtin",
|
|
49569
|
-
description: "Verify that a change works as intended.",
|
|
49570
|
-
whenToUse: "After implementing a change, before declaring it done."
|
|
49571
|
-
}
|
|
49572
|
-
];
|
|
49573
|
-
|
|
49574
|
-
// src/skills/scanner.ts
|
|
49575
|
-
function parseDescription(content) {
|
|
49576
|
-
const fields = parseFrontmatter(content, ["description"]);
|
|
49577
|
-
return { description: fields.description ?? "" };
|
|
49578
|
-
}
|
|
49579
|
-
function isDirLikeSync(p2) {
|
|
49580
|
-
try {
|
|
49581
|
-
return import_node_fs9.default.statSync(p2).isDirectory();
|
|
49582
|
-
} catch {
|
|
49583
|
-
return false;
|
|
49584
|
-
}
|
|
49585
|
-
}
|
|
49586
|
-
function scanSkillDir(dir, source, seen, out, pluginName) {
|
|
49587
|
-
let entries;
|
|
49588
|
-
try {
|
|
49589
|
-
entries = import_node_fs9.default.readdirSync(dir, { withFileTypes: true });
|
|
49590
|
-
} catch {
|
|
49591
|
-
return;
|
|
49592
|
-
}
|
|
49593
|
-
for (const ent of entries) {
|
|
49594
|
-
const entryPath = import_node_path9.default.join(dir, ent.name);
|
|
49595
|
-
if (!ent.isDirectory() && !(ent.isSymbolicLink() && isDirLikeSync(entryPath))) continue;
|
|
49596
|
-
let content;
|
|
49597
|
-
try {
|
|
49598
|
-
content = import_node_fs9.default.readFileSync(import_node_path9.default.join(entryPath, "SKILL.md"), "utf8");
|
|
49599
|
-
} catch {
|
|
49600
|
-
try {
|
|
49601
|
-
content = import_node_fs9.default.readFileSync(import_node_path9.default.join(entryPath, "skill.md"), "utf8");
|
|
49602
|
-
} catch {
|
|
49603
|
-
continue;
|
|
49604
|
-
}
|
|
49605
|
-
}
|
|
49606
|
-
const { description } = parseDescription(content);
|
|
49607
|
-
const baseName = ent.name;
|
|
49608
|
-
const name = pluginName ? `${pluginName}:${baseName}` : baseName;
|
|
49609
|
-
if (seen.has(name)) continue;
|
|
49610
|
-
seen.add(name);
|
|
49611
|
-
const info = { name, source, path: entryPath, description: description || void 0 };
|
|
49612
|
-
if (pluginName) info.plugin = pluginName;
|
|
49613
|
-
out.push(info);
|
|
49614
|
-
}
|
|
49615
|
-
}
|
|
49616
|
-
function listSkillsForDir(dir, source) {
|
|
49617
|
-
const seen = /* @__PURE__ */ new Set();
|
|
49618
|
-
const out = [];
|
|
49619
|
-
scanSkillDir(dir, source, seen, out);
|
|
49620
|
-
return out;
|
|
49621
|
-
}
|
|
49622
|
-
function scanCommandDir(dir, source, seen, out, pluginName) {
|
|
49623
|
-
let entries;
|
|
49624
|
-
try {
|
|
49625
|
-
entries = import_node_fs9.default.readdirSync(dir, { withFileTypes: true });
|
|
49626
|
-
} catch {
|
|
49627
|
-
return;
|
|
49628
|
-
}
|
|
49629
|
-
for (const ent of entries) {
|
|
49630
|
-
const entryPath = import_node_path9.default.join(dir, ent.name);
|
|
49631
|
-
if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync(entryPath)) {
|
|
49632
|
-
const ns = ent.name;
|
|
49633
|
-
let subEntries;
|
|
49634
|
-
try {
|
|
49635
|
-
subEntries = import_node_fs9.default.readdirSync(entryPath, { withFileTypes: true });
|
|
49636
|
-
} catch {
|
|
49637
|
-
continue;
|
|
49638
|
-
}
|
|
49639
|
-
for (const se of subEntries) {
|
|
49640
|
-
if (!se.name.endsWith(".md")) continue;
|
|
49641
|
-
const sePath = import_node_path9.default.join(entryPath, se.name);
|
|
49642
|
-
let content;
|
|
49643
|
-
try {
|
|
49644
|
-
content = import_node_fs9.default.readFileSync(sePath, "utf8");
|
|
49645
|
-
} catch {
|
|
49646
|
-
continue;
|
|
49647
|
-
}
|
|
49648
|
-
const cmd = se.name.replace(/\.md$/, "");
|
|
49649
|
-
const { description } = parseDescription(content);
|
|
49650
|
-
const qualified = `${ns}:${cmd}`;
|
|
49651
|
-
const name = pluginName ? `${pluginName}:${qualified}` : qualified;
|
|
49652
|
-
if (seen.has(name)) continue;
|
|
49653
|
-
seen.add(name);
|
|
49654
|
-
const info = { name, source, path: sePath, description: description || void 0 };
|
|
49655
|
-
if (pluginName) info.plugin = pluginName;
|
|
49656
|
-
out.push(info);
|
|
49657
|
-
}
|
|
49658
|
-
} else if (ent.name.endsWith(".md")) {
|
|
49659
|
-
let content;
|
|
49660
|
-
try {
|
|
49661
|
-
content = import_node_fs9.default.readFileSync(entryPath, "utf8");
|
|
49662
|
-
} catch {
|
|
49663
|
-
continue;
|
|
49664
|
-
}
|
|
49665
|
-
const cmd = ent.name.replace(/\.md$/, "");
|
|
49666
|
-
const { description } = parseDescription(content);
|
|
49667
|
-
const name = pluginName ? `${pluginName}:${cmd}` : cmd;
|
|
49668
|
-
if (seen.has(name)) continue;
|
|
49669
|
-
seen.add(name);
|
|
49670
|
-
const info = { name, source, path: entryPath, description: description || void 0 };
|
|
49671
|
-
if (pluginName) info.plugin = pluginName;
|
|
49672
|
-
out.push(info);
|
|
49673
|
-
}
|
|
49674
|
-
}
|
|
49675
|
-
}
|
|
49676
|
-
function readInstalledPlugins(home) {
|
|
49677
|
-
const file = import_node_path9.default.join(home, ".claude", "plugins", "installed_plugins.json");
|
|
49678
|
-
let raw;
|
|
49679
|
-
try {
|
|
49680
|
-
raw = import_node_fs9.default.readFileSync(file, "utf8");
|
|
49681
|
-
} catch {
|
|
49682
|
-
return [];
|
|
49683
|
-
}
|
|
49684
|
-
let parsed;
|
|
49685
|
-
try {
|
|
49686
|
-
parsed = JSON.parse(raw);
|
|
49687
|
-
} catch {
|
|
49688
|
-
return [];
|
|
49689
|
-
}
|
|
49690
|
-
const out = [];
|
|
49691
|
-
for (const [key, entries] of Object.entries(parsed.plugins ?? {})) {
|
|
49692
|
-
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
49693
|
-
const entry = entries[0];
|
|
49694
|
-
if (!entry?.installPath) continue;
|
|
49695
|
-
const pluginName = key.includes("@") ? key.slice(0, key.indexOf("@")) : key;
|
|
49696
|
-
if (!pluginName) continue;
|
|
49697
|
-
out.push({ name: pluginName, root: entry.installPath });
|
|
49698
|
-
}
|
|
49699
|
-
return out;
|
|
49700
|
-
}
|
|
49701
|
-
var SkillsScanner = class {
|
|
49702
|
-
home;
|
|
49703
|
-
extraPluginRoots;
|
|
49704
|
-
constructor(opts = {}) {
|
|
49705
|
-
this.home = opts.home ?? import_node_os6.default.homedir();
|
|
49706
|
-
this.extraPluginRoots = opts.extraPluginRoots ?? [];
|
|
49707
|
-
}
|
|
49708
|
-
/**
|
|
49709
|
-
* 对齐 cc-direct(openclaw-plugin-clawos/src/claude-code/skills-scanner.ts):
|
|
49710
|
-
* 1. global: ~/.claude/skills + ~/.claude/commands
|
|
49711
|
-
* 2. project: <cwd>/.claude/skills + <cwd>/.claude/commands
|
|
49712
|
-
* 3. plugin: installed_plugins.json 里每个 plugin 的 skills + commands
|
|
49713
|
-
* 顺序 global → project → plugin,同名去重(先到先得)
|
|
49714
|
-
*/
|
|
49715
|
-
list(args) {
|
|
49716
|
-
const seen = /* @__PURE__ */ new Set();
|
|
49717
|
-
const builtinBlock = [];
|
|
49718
|
-
for (const b2 of BUILTIN_SKILLS) {
|
|
49719
|
-
if (seen.has(b2.name)) continue;
|
|
49720
|
-
seen.add(b2.name);
|
|
49721
|
-
builtinBlock.push({
|
|
49722
|
-
name: b2.name,
|
|
49723
|
-
source: "builtin",
|
|
49724
|
-
description: b2.description
|
|
49725
|
-
});
|
|
49726
|
-
}
|
|
49727
|
-
const fsBlock = [];
|
|
49728
|
-
scanSkillDir(import_node_path9.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
|
|
49729
|
-
scanCommandDir(import_node_path9.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
|
|
49730
|
-
scanSkillDir(import_node_path9.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
|
|
49731
|
-
scanCommandDir(import_node_path9.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
|
|
49732
|
-
const plugins = [...readInstalledPlugins(this.home), ...this.extraPluginRoots];
|
|
49733
|
-
for (const { name, root } of plugins) {
|
|
49734
|
-
scanSkillDir(import_node_path9.default.join(root, "skills"), "plugin", seen, fsBlock, name);
|
|
49735
|
-
scanCommandDir(import_node_path9.default.join(root, "commands"), "plugin", seen, fsBlock, name);
|
|
49736
|
-
}
|
|
49737
|
-
fsBlock.sort((a, b2) => a.name < b2.name ? -1 : a.name > b2.name ? 1 : 0);
|
|
49738
|
-
return [...builtinBlock, ...fsBlock];
|
|
49739
|
-
}
|
|
49740
|
-
};
|
|
49741
|
-
|
|
49742
|
-
// src/persona/manager.ts
|
|
49743
|
-
var PersonaManager = class {
|
|
49744
|
-
constructor(deps) {
|
|
49745
|
-
this.deps = deps;
|
|
49746
|
-
}
|
|
49747
|
-
deps;
|
|
49748
|
-
/**
|
|
49749
|
-
* slug 是 persona 的身份,label 只是显示名——两者刻意分开:id 落进文件路径 /
|
|
49750
|
-
* 分享链接且创建后不可改,label 可中文、可随时改。此前 id 从 label 推导,
|
|
49751
|
-
* 非 ascii 被剥空后一律 fallback 成 `persona-persona`。
|
|
49752
|
-
*
|
|
49753
|
-
* 撞名直接抛错而非改名:id 由调用方显式指定,静默换成带后缀的 id 等于把
|
|
49754
|
-
* 用户的选择偷偷改掉。字符集校验在 PersonaCreateArgsSchema(路径穿越边界)。
|
|
49755
|
-
*/
|
|
49756
|
-
create(args) {
|
|
49757
|
-
const personaId2 = `persona-${args.slug}`;
|
|
49758
|
-
if (this.deps.store.has(personaId2)) {
|
|
49759
|
-
throw new Error(`personaId already exists: ${personaId2}`);
|
|
49760
|
-
}
|
|
49761
|
-
const now = Date.now();
|
|
49762
|
-
const persona = {
|
|
49763
|
-
personaId: personaId2,
|
|
49764
|
-
label: args.label,
|
|
49765
|
-
model: args.model,
|
|
49766
|
-
// effort 兜底 high:persona 一律持有具体档位(spec 2026-08-12),UI 会带界面上高亮的
|
|
49767
|
-
// 那一档,但 persona:create 也可能来自 butler 的 persona-authoring skill 这类不带该
|
|
49768
|
-
// 字段的调用方——不兜的话要等下次 daemon 启动 backfill 才补上,中间建的 session 就漏了。
|
|
49769
|
-
// 取值理由与 backfillPersonaSessionDefaults 一致(见那里的注释)。
|
|
49770
|
-
effort: args.effort ?? "high",
|
|
49771
|
-
tool: args.tool,
|
|
49772
|
-
public: args.public ?? false,
|
|
49773
|
-
iconKey: args.iconKey,
|
|
49774
|
-
createdAt: now,
|
|
49775
|
-
updatedAt: now
|
|
49776
|
-
};
|
|
49777
|
-
this.deps.store.write(persona, args.personality);
|
|
49778
|
-
this.deps.registry.set(persona);
|
|
49779
|
-
return persona;
|
|
49780
|
-
}
|
|
49781
|
-
update(personaId2, patch, texts) {
|
|
49782
|
-
const existing = this.deps.registry.get(personaId2);
|
|
49783
|
-
if (!existing) throw new Error(`persona not found: ${personaId2}`);
|
|
49784
|
-
const { iconKey: iconKeyPatch, ...restPatch } = patch;
|
|
49785
|
-
const updated = {
|
|
49786
|
-
...existing,
|
|
49787
|
-
...restPatch,
|
|
49788
|
-
updatedAt: Date.now()
|
|
49789
|
-
};
|
|
49790
|
-
const toolChanged = patch.tool !== void 0 && (patch.tool ?? "claude") !== (existing.tool ?? "claude");
|
|
49791
|
-
if (toolChanged) {
|
|
49792
|
-
if (patch.model === void 0) delete updated.model;
|
|
49793
|
-
if (patch.effort === void 0) delete updated.effort;
|
|
49794
|
-
}
|
|
49795
|
-
if (iconKeyPatch === null) {
|
|
49796
|
-
delete updated.iconKey;
|
|
49797
|
-
} else if (iconKeyPatch !== void 0) {
|
|
49798
|
-
updated.iconKey = iconKeyPatch;
|
|
49799
|
-
}
|
|
49800
|
-
if (texts?.personality !== void 0) {
|
|
49801
|
-
this.deps.store.writePersonality(personaId2, texts.personality);
|
|
49802
|
-
}
|
|
49803
|
-
if (texts?.personalityLocal !== void 0) {
|
|
49804
|
-
this.deps.store.writePersonalityLocal(personaId2, texts.personalityLocal);
|
|
49805
|
-
}
|
|
49806
|
-
this.deps.store.writeMeta(updated);
|
|
49807
|
-
this.deps.registry.set(updated);
|
|
49808
|
-
return updated;
|
|
49809
|
-
}
|
|
49810
|
-
/** 读取 CLAUDE.md 内容;persona 目录或文件不存在时返回 null。透传给 handler 拼响应。 */
|
|
49811
|
-
readPersonality(personaId2) {
|
|
49812
|
-
return this.deps.store.readPersonality(personaId2);
|
|
49813
|
-
}
|
|
49814
|
-
/** 读人格补充层(`.clawd/personality.local.md`);没写过返回 null。供 persona:get 拼响应。 */
|
|
49815
|
-
readPersonalityLocal(personaId2) {
|
|
49816
|
-
return this.deps.store.readPersonalityLocal(personaId2);
|
|
49817
|
-
}
|
|
49818
|
-
/** 扫 persona 私有 skills 目录;目录不存在返回空数组。供 persona:get handler 拼响应。 */
|
|
49819
|
-
listSkills(personaId2) {
|
|
49820
|
-
return listSkillsForDir(this.deps.store.skillsDir(personaId2), "project");
|
|
49821
|
-
}
|
|
49822
|
-
/**
|
|
49823
|
-
* 读取 persona `.claude/settings.json` 中已启用的插件列表(value === true 的 key)。
|
|
49824
|
-
* 文件不存在 / 解析失败 / 字段缺失 → 空数组。供 persona:get handler 拼响应。
|
|
49825
|
-
*/
|
|
49826
|
-
listEnabledPlugins(personaId2) {
|
|
49827
|
-
return this.deps.store.readEnabledPlugins(personaId2);
|
|
49828
|
-
}
|
|
49829
|
-
/** 读 sandbox-settings.json;不存在 / 损坏返回 null。供 persona:get handler 拼响应。 */
|
|
49830
|
-
/** 读 codex guest 沙箱配置(`.clawd/codex-sandbox.json`);缺失 / 坏 → null。透传给 handler 拼响应。 */
|
|
49831
|
-
readCodexSandboxSettings(personaId2) {
|
|
49832
|
-
return this.deps.store.readCodexSandboxSettings(personaId2);
|
|
49833
|
-
}
|
|
49834
|
-
/**
|
|
49835
|
-
* 局部合并写 codex guest 沙箱配置,返回合并后的全量。
|
|
49836
|
-
*
|
|
49837
|
-
* **必须是合并不是覆盖**:UI 的「访客联网」开关只发 `{network}`,整体覆盖会静默抹掉 owner
|
|
49838
|
-
* 手配的 writableRoots / denyRead(丢的是安全边界,且用户不会立刻发现)。
|
|
49839
|
-
* 不走 PersonaManager.update 是因为它管的是 PersonaFile meta + 人格两层文本,
|
|
49840
|
-
* 而这份配置是另一个文件、另一套 schema。
|
|
49841
|
-
*/
|
|
49842
|
-
mergeCodexSandbox(personaId2, patch) {
|
|
49843
|
-
if (!this.deps.registry.get(personaId2)) throw new Error(`persona not found: ${personaId2}`);
|
|
49844
|
-
const merged = { ...this.deps.store.readCodexSandboxSettings(personaId2) ?? {}, ...patch };
|
|
49845
|
-
this.deps.store.writeCodexSandboxSettings(personaId2, merged);
|
|
49846
|
-
return merged;
|
|
49847
|
-
}
|
|
49848
|
-
readSandboxSettings(personaId2) {
|
|
49849
|
-
return this.deps.store.readSandboxSettings(personaId2);
|
|
49850
|
-
}
|
|
49851
|
-
/**
|
|
49852
|
-
* 删除 persona。
|
|
49853
|
-
* PersonaStore.remove(personaId) 级联删整个 <personaRoot>/<personaId>/ 目录
|
|
49854
|
-
* (含 .clawd/sessions/owner/ 和 .clawd/sessions/guests/<capId>/),rmSync
|
|
49855
|
-
* recursive force 一锅清。
|
|
49856
|
-
*/
|
|
49857
|
-
delete(personaId2) {
|
|
49858
|
-
this.deps.store.remove(personaId2);
|
|
49859
|
-
this.deps.registry.remove(personaId2);
|
|
49860
|
-
}
|
|
49861
|
-
};
|
|
49862
|
-
|
|
49863
49468
|
// src/persona/seed.ts
|
|
49864
|
-
var
|
|
49865
|
-
var
|
|
49469
|
+
var fs12 = __toESM(require("fs"), 1);
|
|
49470
|
+
var path15 = __toESM(require("path"), 1);
|
|
49866
49471
|
var import_node_url = require("url");
|
|
49867
49472
|
var import_meta = {};
|
|
49868
49473
|
var DEFAULT_BYPASS_PROFILE = {
|
|
49869
49474
|
permissions: { defaultMode: "bypassPermissions" }
|
|
49870
49475
|
};
|
|
49476
|
+
var PKG_CACHE_ROOTS = ["~/Library/pnpm", "~/.local/share/pnpm", "~/.local/state/pnpm", "~/.npm", "~/.pnpm-store"];
|
|
49477
|
+
var CODEX_SANDBOX_DEFAULT = {
|
|
49478
|
+
writableRoots: [...PKG_CACHE_ROOTS],
|
|
49479
|
+
denyRead: [".secrets/**"],
|
|
49480
|
+
network: true
|
|
49481
|
+
};
|
|
49871
49482
|
var DEFAULT_PERSONAS = [
|
|
49872
49483
|
{
|
|
49873
49484
|
personaId: "persona-researcher",
|
|
@@ -49952,7 +49563,7 @@ var DEFAULT_PERSONAS = [
|
|
|
49952
49563
|
}
|
|
49953
49564
|
},
|
|
49954
49565
|
codexSandbox: {
|
|
49955
|
-
writableRoots: [
|
|
49566
|
+
writableRoots: [...PKG_CACHE_ROOTS],
|
|
49956
49567
|
denyRead: [".secrets/**"],
|
|
49957
49568
|
network: true
|
|
49958
49569
|
}
|
|
@@ -49986,7 +49597,7 @@ var DEFAULT_PERSONAS = [
|
|
|
49986
49597
|
}
|
|
49987
49598
|
},
|
|
49988
49599
|
codexSandbox: {
|
|
49989
|
-
writableRoots: [
|
|
49600
|
+
writableRoots: [...PKG_CACHE_ROOTS],
|
|
49990
49601
|
denyRead: [".secrets/**"],
|
|
49991
49602
|
network: true
|
|
49992
49603
|
}
|
|
@@ -50028,7 +49639,7 @@ var DEFAULT_PERSONAS = [
|
|
|
50028
49639
|
}
|
|
50029
49640
|
},
|
|
50030
49641
|
codexSandbox: {
|
|
50031
|
-
writableRoots: [
|
|
49642
|
+
writableRoots: [...PKG_CACHE_ROOTS],
|
|
50032
49643
|
denyRead: [".secrets/**"],
|
|
50033
49644
|
network: true
|
|
50034
49645
|
}
|
|
@@ -50053,24 +49664,24 @@ function bundleSiblingFromArgv(argv1, sibling) {
|
|
|
50053
49664
|
if (!argv1) return null;
|
|
50054
49665
|
let real = argv1;
|
|
50055
49666
|
try {
|
|
50056
|
-
real =
|
|
49667
|
+
real = fs12.realpathSync(argv1);
|
|
50057
49668
|
} catch {
|
|
50058
49669
|
}
|
|
50059
|
-
return
|
|
49670
|
+
return path15.resolve(path15.dirname(real), sibling);
|
|
50060
49671
|
}
|
|
50061
49672
|
function findDefaultsRoot(logger) {
|
|
50062
49673
|
const candidates = [];
|
|
50063
49674
|
try {
|
|
50064
|
-
const here =
|
|
50065
|
-
candidates.push(
|
|
50066
|
-
candidates.push(
|
|
49675
|
+
const here = path15.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
49676
|
+
candidates.push(path15.resolve(here, "defaults"));
|
|
49677
|
+
candidates.push(path15.resolve(here, "persona-defaults"));
|
|
50067
49678
|
} catch {
|
|
50068
49679
|
}
|
|
50069
49680
|
const fromArgv = bundleSiblingFromArgv(process.argv[1], "persona-defaults");
|
|
50070
49681
|
if (fromArgv) candidates.push(fromArgv);
|
|
50071
49682
|
for (const c of candidates) {
|
|
50072
49683
|
try {
|
|
50073
|
-
if (
|
|
49684
|
+
if (fs12.statSync(c).isDirectory()) {
|
|
50074
49685
|
logger?.info("persona.defaults-root.resolved", { root: c });
|
|
50075
49686
|
return c;
|
|
50076
49687
|
}
|
|
@@ -50087,8 +49698,8 @@ function seedDefaultPersonas(args) {
|
|
|
50087
49698
|
args.logger.info("persona.seed.skip", { personaId: entry.personaId, reason: "exists" });
|
|
50088
49699
|
continue;
|
|
50089
49700
|
}
|
|
50090
|
-
const bundleDir =
|
|
50091
|
-
if (!
|
|
49701
|
+
const bundleDir = path15.join(args.defaultsRoot, entry.personaId);
|
|
49702
|
+
if (!fs12.existsSync(bundleDir)) {
|
|
50092
49703
|
args.logger.warn("persona.seed.skip", {
|
|
50093
49704
|
personaId: entry.personaId,
|
|
50094
49705
|
reason: "bundle-missing",
|
|
@@ -50096,8 +49707,8 @@ function seedDefaultPersonas(args) {
|
|
|
50096
49707
|
});
|
|
50097
49708
|
continue;
|
|
50098
49709
|
}
|
|
50099
|
-
const claudeMdPath =
|
|
50100
|
-
if (!
|
|
49710
|
+
const claudeMdPath = path15.join(bundleDir, "CLAUDE.md");
|
|
49711
|
+
if (!fs12.existsSync(claudeMdPath)) {
|
|
50101
49712
|
args.logger.warn("persona.seed.skip", {
|
|
50102
49713
|
personaId: entry.personaId,
|
|
50103
49714
|
reason: "no-CLAUDE.md",
|
|
@@ -50105,7 +49716,7 @@ function seedDefaultPersonas(args) {
|
|
|
50105
49716
|
});
|
|
50106
49717
|
continue;
|
|
50107
49718
|
}
|
|
50108
|
-
const personality =
|
|
49719
|
+
const personality = fs12.readFileSync(claudeMdPath, "utf8");
|
|
50109
49720
|
const now = Date.now();
|
|
50110
49721
|
const persona = {
|
|
50111
49722
|
personaId: entry.personaId,
|
|
@@ -50132,51 +49743,51 @@ function seedDefaultPersonas(args) {
|
|
|
50132
49743
|
}
|
|
50133
49744
|
}
|
|
50134
49745
|
function skipNodeModulesUnder(srcRoot) {
|
|
50135
|
-
return (src) => !
|
|
49746
|
+
return (src) => !path15.relative(srcRoot, src).split(path15.sep).includes("node_modules");
|
|
50136
49747
|
}
|
|
50137
49748
|
function copyBundleExtras(srcDir, dstDir) {
|
|
50138
|
-
for (const entry of
|
|
49749
|
+
for (const entry of fs12.readdirSync(srcDir, { withFileTypes: true })) {
|
|
50139
49750
|
if (entry.name === "CLAUDE.md" || entry.name === ".clawd") continue;
|
|
50140
|
-
const srcPath =
|
|
50141
|
-
const dstPath =
|
|
49751
|
+
const srcPath = path15.join(srcDir, entry.name);
|
|
49752
|
+
const dstPath = path15.join(dstDir, entry.name);
|
|
50142
49753
|
if (entry.isDirectory()) {
|
|
50143
|
-
|
|
49754
|
+
fs12.cpSync(srcPath, dstPath, { recursive: true, dereference: true, filter: skipNodeModulesUnder(srcPath) });
|
|
50144
49755
|
} else if (entry.isFile()) {
|
|
50145
|
-
|
|
49756
|
+
fs12.copyFileSync(srcPath, dstPath);
|
|
50146
49757
|
}
|
|
50147
49758
|
}
|
|
50148
49759
|
}
|
|
50149
49760
|
var DAEMON_MANAGED_PATHS = ["extension-kit", "CLAUDE.md", ".mcp.json", ".claude/skills", ".claude/agents"];
|
|
50150
49761
|
function replaceManagedPath(src, dst) {
|
|
50151
|
-
if (!
|
|
50152
|
-
|
|
50153
|
-
|
|
50154
|
-
|
|
49762
|
+
if (!fs12.statSync(src).isDirectory()) {
|
|
49763
|
+
fs12.mkdirSync(path15.dirname(dst), { recursive: true });
|
|
49764
|
+
fs12.rmSync(dst, { recursive: true, force: true });
|
|
49765
|
+
fs12.cpSync(src, dst, { dereference: true });
|
|
50155
49766
|
return;
|
|
50156
49767
|
}
|
|
50157
|
-
|
|
50158
|
-
for (const e of
|
|
49768
|
+
fs12.mkdirSync(dst, { recursive: true });
|
|
49769
|
+
for (const e of fs12.readdirSync(src, { withFileTypes: true })) {
|
|
50159
49770
|
if (e.name === "node_modules") continue;
|
|
50160
|
-
const child =
|
|
50161
|
-
|
|
50162
|
-
|
|
49771
|
+
const child = path15.join(dst, e.name);
|
|
49772
|
+
fs12.rmSync(child, { recursive: true, force: true });
|
|
49773
|
+
fs12.cpSync(path15.join(src, e.name), child, { recursive: true, dereference: true });
|
|
50163
49774
|
}
|
|
50164
49775
|
}
|
|
50165
49776
|
function refreshDaemonManagedDirs(args) {
|
|
50166
49777
|
const entries = args.entries ?? DEFAULT_PERSONAS;
|
|
50167
49778
|
for (const entry of entries) {
|
|
50168
|
-
const bundleDir =
|
|
50169
|
-
if (!
|
|
49779
|
+
const bundleDir = path15.join(args.defaultsRoot, entry.personaId);
|
|
49780
|
+
if (!fs12.existsSync(bundleDir)) continue;
|
|
50170
49781
|
const personaDir = args.store.personaDirPath(entry.personaId);
|
|
50171
|
-
if (!
|
|
49782
|
+
if (!fs12.existsSync(personaDir)) continue;
|
|
50172
49783
|
for (const relPath of DAEMON_MANAGED_PATHS) {
|
|
50173
|
-
const srcPath =
|
|
50174
|
-
if (!
|
|
50175
|
-
const dstPath =
|
|
49784
|
+
const srcPath = path15.join(bundleDir, relPath);
|
|
49785
|
+
if (!fs12.existsSync(srcPath)) continue;
|
|
49786
|
+
const dstPath = path15.join(personaDir, relPath);
|
|
50176
49787
|
try {
|
|
50177
49788
|
replaceManagedPath(srcPath, dstPath);
|
|
50178
49789
|
if (relPath === "CLAUDE.md") {
|
|
50179
|
-
replaceManagedPath(srcPath,
|
|
49790
|
+
replaceManagedPath(srcPath, path15.join(personaDir, "AGENTS.md"));
|
|
50180
49791
|
}
|
|
50181
49792
|
args.logger.info("persona.refresh.synced", {
|
|
50182
49793
|
personaId: entry.personaId,
|
|
@@ -50200,12 +49811,12 @@ function migratePersonalityOverlay(args) {
|
|
|
50200
49811
|
for (const entry of entries) {
|
|
50201
49812
|
const personaId2 = entry.personaId;
|
|
50202
49813
|
try {
|
|
50203
|
-
if (
|
|
50204
|
-
const bundleClaudeMd =
|
|
50205
|
-
if (!
|
|
49814
|
+
if (fs12.existsSync(args.store.personalityLocalPath(personaId2))) continue;
|
|
49815
|
+
const bundleClaudeMd = path15.join(args.defaultsRoot, personaId2, "CLAUDE.md");
|
|
49816
|
+
if (!fs12.existsSync(bundleClaudeMd)) continue;
|
|
50206
49817
|
const current = args.store.readPersonality(personaId2);
|
|
50207
49818
|
if (current === null) continue;
|
|
50208
|
-
const bundle =
|
|
49819
|
+
const bundle = fs12.readFileSync(bundleClaudeMd, "utf8");
|
|
50209
49820
|
if (current === bundle) continue;
|
|
50210
49821
|
if (!current.startsWith(bundle)) continue;
|
|
50211
49822
|
const tail = current.slice(bundle.length).trim();
|
|
@@ -50246,8 +49857,6 @@ function migrateAgentsMirror(args) {
|
|
|
50246
49857
|
}
|
|
50247
49858
|
if (mirrored > 0) args.logger.info("persona.agents-mirror.done", { mirrored });
|
|
50248
49859
|
}
|
|
50249
|
-
var PKG_CACHE_ROOTS = ["~/Library/pnpm", "~/.local/share/pnpm", "~/.local/state/pnpm", "~/.npm", "~/.pnpm-store"];
|
|
50250
|
-
var CODEX_SANDBOX_DEFAULT = { writableRoots: [...PKG_CACHE_ROOTS], denyRead: [], network: true };
|
|
50251
49860
|
function migrateCodexSandbox(args) {
|
|
50252
49861
|
const entries = args.entries ?? DEFAULT_PERSONAS;
|
|
50253
49862
|
let n = 0;
|
|
@@ -50306,7 +49915,7 @@ function migrateSandboxAllowRead(args) {
|
|
|
50306
49915
|
for (const personaId2 of args.store.list()) {
|
|
50307
49916
|
try {
|
|
50308
49917
|
const readPath = tildePath(args.store.personaDirPath(personaId2));
|
|
50309
|
-
if (!
|
|
49918
|
+
if (!fs12.existsSync(args.store.sandboxSettingsPath(personaId2))) {
|
|
50310
49919
|
args.store.writeSandboxSettings(personaId2, buildGuestSettingsV1());
|
|
50311
49920
|
n++;
|
|
50312
49921
|
continue;
|
|
@@ -50349,13 +49958,13 @@ var PERSONA_BUILDER_TOMBSTONE = `${PERSONA_BUILDER_TOMBSTONE_MARKER}
|
|
|
50349
49958
|
function containsAnyFile(dir) {
|
|
50350
49959
|
let entries;
|
|
50351
49960
|
try {
|
|
50352
|
-
entries =
|
|
49961
|
+
entries = fs12.readdirSync(dir, { withFileTypes: true });
|
|
50353
49962
|
} catch {
|
|
50354
49963
|
return false;
|
|
50355
49964
|
}
|
|
50356
49965
|
for (const e of entries) {
|
|
50357
49966
|
if (e.isDirectory()) {
|
|
50358
|
-
if (containsAnyFile(
|
|
49967
|
+
if (containsAnyFile(path15.join(dir, e.name))) return true;
|
|
50359
49968
|
} else {
|
|
50360
49969
|
return true;
|
|
50361
49970
|
}
|
|
@@ -50364,9 +49973,9 @@ function containsAnyFile(dir) {
|
|
|
50364
49973
|
}
|
|
50365
49974
|
function retirePersonaBuilder(args) {
|
|
50366
49975
|
const dir = args.store.personaDirPath(PERSONA_BUILDER_ID);
|
|
50367
|
-
if (!
|
|
49976
|
+
if (!fs12.existsSync(dir)) return;
|
|
50368
49977
|
try {
|
|
50369
|
-
const used = containsAnyFile(
|
|
49978
|
+
const used = containsAnyFile(path15.join(dir, ".clawd", "sessions")) || containsAnyFile(path15.join(dir, ".claude", "skills")) || containsAnyFile(path15.join(dir, "projects"));
|
|
50370
49979
|
if (!used) {
|
|
50371
49980
|
args.store.remove(PERSONA_BUILDER_ID);
|
|
50372
49981
|
args.logger.info("persona.retire-builder.removed", { personaId: PERSONA_BUILDER_ID });
|
|
@@ -50374,10 +49983,10 @@ function retirePersonaBuilder(args) {
|
|
|
50374
49983
|
}
|
|
50375
49984
|
let wrote = false;
|
|
50376
49985
|
for (const name of ["CLAUDE.md", "AGENTS.md"]) {
|
|
50377
|
-
const p2 =
|
|
50378
|
-
const cur =
|
|
49986
|
+
const p2 = path15.join(dir, name);
|
|
49987
|
+
const cur = fs12.existsSync(p2) ? fs12.readFileSync(p2, "utf8") : null;
|
|
50379
49988
|
if (cur?.startsWith(PERSONA_BUILDER_TOMBSTONE_MARKER)) continue;
|
|
50380
|
-
|
|
49989
|
+
fs12.writeFileSync(p2, PERSONA_BUILDER_TOMBSTONE);
|
|
50381
49990
|
wrote = true;
|
|
50382
49991
|
}
|
|
50383
49992
|
if (wrote) args.logger.info("persona.retire-builder.tombstoned", { personaId: PERSONA_BUILDER_ID });
|
|
@@ -50391,15 +50000,15 @@ function retirePersonaBuilder(args) {
|
|
|
50391
50000
|
function findDeployKitRoot(logger) {
|
|
50392
50001
|
const candidates = [];
|
|
50393
50002
|
try {
|
|
50394
|
-
const here =
|
|
50395
|
-
candidates.push(
|
|
50003
|
+
const here = path15.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
50004
|
+
candidates.push(path15.resolve(here, "..", "deploy-kit"));
|
|
50396
50005
|
} catch {
|
|
50397
50006
|
}
|
|
50398
50007
|
const fromArgv = bundleSiblingFromArgv(process.argv[1], "deploy-kit");
|
|
50399
50008
|
if (fromArgv) candidates.push(fromArgv);
|
|
50400
50009
|
for (const c of candidates) {
|
|
50401
50010
|
try {
|
|
50402
|
-
if (
|
|
50011
|
+
if (fs12.statSync(c).isDirectory()) {
|
|
50403
50012
|
logger?.info("persona.deploy-kit-root.resolved", { root: c });
|
|
50404
50013
|
return c;
|
|
50405
50014
|
}
|
|
@@ -50410,8 +50019,8 @@ function findDeployKitRoot(logger) {
|
|
|
50410
50019
|
return null;
|
|
50411
50020
|
}
|
|
50412
50021
|
function seedDeployKit(args) {
|
|
50413
|
-
const dst =
|
|
50414
|
-
|
|
50022
|
+
const dst = path15.join(args.dataDir, "deploy-kit");
|
|
50023
|
+
fs12.cpSync(args.deployKitBundleRoot, dst, {
|
|
50415
50024
|
recursive: true,
|
|
50416
50025
|
dereference: true,
|
|
50417
50026
|
force: false,
|
|
@@ -50420,28 +50029,434 @@ function seedDeployKit(args) {
|
|
|
50420
50029
|
args.logger.info("deploy-kit.seed.done", { dst });
|
|
50421
50030
|
}
|
|
50422
50031
|
function refreshDeployKit(args) {
|
|
50423
|
-
const dst =
|
|
50424
|
-
if (!
|
|
50032
|
+
const dst = path15.join(args.dataDir, "deploy-kit");
|
|
50033
|
+
if (!fs12.existsSync(dst)) {
|
|
50425
50034
|
seedDeployKit(args);
|
|
50426
50035
|
return;
|
|
50427
50036
|
}
|
|
50428
50037
|
for (const sub of ["scripts", "contract"]) {
|
|
50429
|
-
const s =
|
|
50430
|
-
if (
|
|
50431
|
-
|
|
50038
|
+
const s = path15.join(args.deployKitBundleRoot, sub);
|
|
50039
|
+
if (fs12.existsSync(s)) {
|
|
50040
|
+
fs12.cpSync(s, path15.join(dst, sub), { recursive: true, force: true, dereference: true });
|
|
50432
50041
|
}
|
|
50433
50042
|
}
|
|
50434
|
-
const secretsSrc =
|
|
50435
|
-
if (
|
|
50436
|
-
|
|
50437
|
-
for (const f of
|
|
50043
|
+
const secretsSrc = path15.join(args.deployKitBundleRoot, ".secrets");
|
|
50044
|
+
if (fs12.existsSync(secretsSrc)) {
|
|
50045
|
+
fs12.mkdirSync(path15.join(dst, ".secrets"), { recursive: true });
|
|
50046
|
+
for (const f of fs12.readdirSync(secretsSrc)) {
|
|
50438
50047
|
if (f.endsWith(".local")) continue;
|
|
50439
|
-
|
|
50048
|
+
fs12.copyFileSync(path15.join(secretsSrc, f), path15.join(dst, ".secrets", f));
|
|
50440
50049
|
}
|
|
50441
50050
|
}
|
|
50442
50051
|
args.logger.info("deploy-kit.refresh.done", { dst });
|
|
50443
50052
|
}
|
|
50444
50053
|
|
|
50054
|
+
// src/skills/scanner.ts
|
|
50055
|
+
var import_node_fs9 = __toESM(require("fs"), 1);
|
|
50056
|
+
var import_node_os6 = __toESM(require("os"), 1);
|
|
50057
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
50058
|
+
|
|
50059
|
+
// src/skills/frontmatter.ts
|
|
50060
|
+
var STRIP_QUOTES = /^["']|["']$/g;
|
|
50061
|
+
function strip(s) {
|
|
50062
|
+
return s.trim().replace(STRIP_QUOTES, "");
|
|
50063
|
+
}
|
|
50064
|
+
function parseFrontmatter(content, keys) {
|
|
50065
|
+
const out = {};
|
|
50066
|
+
if (!content.startsWith("---")) return out;
|
|
50067
|
+
const end = content.indexOf("---", 3);
|
|
50068
|
+
if (end === -1) return out;
|
|
50069
|
+
const lines = content.slice(3, end).split("\n");
|
|
50070
|
+
for (let i = 0; i < lines.length; i++) {
|
|
50071
|
+
const trimmed = lines[i].trim();
|
|
50072
|
+
for (const key of keys) {
|
|
50073
|
+
const prefix = `${key}:`;
|
|
50074
|
+
if (!trimmed.startsWith(prefix)) continue;
|
|
50075
|
+
const rest = trimmed.slice(prefix.length).trim();
|
|
50076
|
+
if (rest === "|" || rest === ">" || rest === "|-" || rest === ">-") {
|
|
50077
|
+
const parts = [];
|
|
50078
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
50079
|
+
const next = lines[j];
|
|
50080
|
+
if (next.length === 0) {
|
|
50081
|
+
parts.push("");
|
|
50082
|
+
continue;
|
|
50083
|
+
}
|
|
50084
|
+
if (!/^\s/.test(next)) break;
|
|
50085
|
+
parts.push(next.replace(/^\s+/, ""));
|
|
50086
|
+
}
|
|
50087
|
+
const value = parts.filter(Boolean).join(" ").trim();
|
|
50088
|
+
if (value) out[key] = value;
|
|
50089
|
+
} else {
|
|
50090
|
+
const value = strip(rest);
|
|
50091
|
+
if (value) out[key] = value;
|
|
50092
|
+
}
|
|
50093
|
+
break;
|
|
50094
|
+
}
|
|
50095
|
+
}
|
|
50096
|
+
return out;
|
|
50097
|
+
}
|
|
50098
|
+
|
|
50099
|
+
// src/skills/builtin-cc-resources.ts
|
|
50100
|
+
var BUILTIN_SKILLS = [
|
|
50101
|
+
{ name: "update-config", source: "builtin", description: "Update Claude Code configuration." },
|
|
50102
|
+
{ name: "keybindings-help", source: "builtin", description: "Show available keybindings." },
|
|
50103
|
+
{ name: "verify", source: "builtin", description: "Verify the implementation." },
|
|
50104
|
+
{ name: "debug", source: "builtin", description: "Debug current issue." },
|
|
50105
|
+
{ name: "lorem-ipsum", source: "builtin", description: "Generate lorem ipsum placeholder text." },
|
|
50106
|
+
{ name: "skillify", source: "builtin", description: "Convert a process into a reusable skill." },
|
|
50107
|
+
{ name: "remember", source: "builtin", description: "Remember context for later use." },
|
|
50108
|
+
{ name: "simplify", source: "builtin", description: "Simplify the code or message." },
|
|
50109
|
+
{ name: "batch", source: "builtin", description: "Run a batch of similar operations." },
|
|
50110
|
+
{ name: "stuck", source: "builtin", description: "Help unstick a stalled task." },
|
|
50111
|
+
{ name: "loop", source: "builtin", description: "Loop a task until a condition is met." },
|
|
50112
|
+
{ name: "cron-list", source: "builtin", description: "List scheduled cron tasks." },
|
|
50113
|
+
{ name: "cron-delete", source: "builtin", description: "Delete a scheduled cron task." },
|
|
50114
|
+
{ name: "dream", source: "builtin", description: "Brainstorm freely without constraints." },
|
|
50115
|
+
{ name: "hunter", source: "builtin", description: "Hunt down a bug or root cause." },
|
|
50116
|
+
{ name: "schedule", source: "builtin", description: "Schedule a task to run later." },
|
|
50117
|
+
{ name: "claude-api", source: "builtin", description: "Use the Claude API directly." },
|
|
50118
|
+
{ name: "claude-in-chrome", source: "builtin", description: "Drive Claude inside Chrome via the Chrome extension." }
|
|
50119
|
+
];
|
|
50120
|
+
var BUILTIN_AGENTS = [
|
|
50121
|
+
{
|
|
50122
|
+
name: "general-purpose",
|
|
50123
|
+
source: "builtin",
|
|
50124
|
+
description: "General-purpose agent for researching complex questions and executing multi-step tasks.",
|
|
50125
|
+
whenToUse: "When you are searching for a keyword or file and are not confident you will find the right match in the first few tries."
|
|
50126
|
+
},
|
|
50127
|
+
{
|
|
50128
|
+
name: "statusline-setup",
|
|
50129
|
+
source: "builtin",
|
|
50130
|
+
description: "Configure the user's Claude Code status line setting.",
|
|
50131
|
+
whenToUse: "When the user is setting up or changing their status line configuration."
|
|
50132
|
+
},
|
|
50133
|
+
{
|
|
50134
|
+
name: "Explore",
|
|
50135
|
+
source: "builtin",
|
|
50136
|
+
description: "Explore the codebase to gather context.",
|
|
50137
|
+
whenToUse: "Before making changes that require understanding the surrounding code."
|
|
50138
|
+
},
|
|
50139
|
+
{
|
|
50140
|
+
name: "Plan",
|
|
50141
|
+
source: "builtin",
|
|
50142
|
+
description: "Produce an implementation plan before writing code.",
|
|
50143
|
+
whenToUse: "When the task is non-trivial and would benefit from an explicit plan."
|
|
50144
|
+
},
|
|
50145
|
+
{
|
|
50146
|
+
name: "claude-code-guide",
|
|
50147
|
+
source: "builtin",
|
|
50148
|
+
description: "Guide the user through Claude Code features.",
|
|
50149
|
+
whenToUse: "When the user needs help understanding what Claude Code can do."
|
|
50150
|
+
},
|
|
50151
|
+
{
|
|
50152
|
+
name: "verification",
|
|
50153
|
+
source: "builtin",
|
|
50154
|
+
description: "Verify that a change works as intended.",
|
|
50155
|
+
whenToUse: "After implementing a change, before declaring it done."
|
|
50156
|
+
}
|
|
50157
|
+
];
|
|
50158
|
+
|
|
50159
|
+
// src/skills/scanner.ts
|
|
50160
|
+
function parseDescription(content) {
|
|
50161
|
+
const fields = parseFrontmatter(content, ["description"]);
|
|
50162
|
+
return { description: fields.description ?? "" };
|
|
50163
|
+
}
|
|
50164
|
+
function isDirLikeSync(p2) {
|
|
50165
|
+
try {
|
|
50166
|
+
return import_node_fs9.default.statSync(p2).isDirectory();
|
|
50167
|
+
} catch {
|
|
50168
|
+
return false;
|
|
50169
|
+
}
|
|
50170
|
+
}
|
|
50171
|
+
function scanSkillDir(dir, source, seen, out, pluginName) {
|
|
50172
|
+
let entries;
|
|
50173
|
+
try {
|
|
50174
|
+
entries = import_node_fs9.default.readdirSync(dir, { withFileTypes: true });
|
|
50175
|
+
} catch {
|
|
50176
|
+
return;
|
|
50177
|
+
}
|
|
50178
|
+
for (const ent of entries) {
|
|
50179
|
+
const entryPath = import_node_path9.default.join(dir, ent.name);
|
|
50180
|
+
if (!ent.isDirectory() && !(ent.isSymbolicLink() && isDirLikeSync(entryPath))) continue;
|
|
50181
|
+
let content;
|
|
50182
|
+
try {
|
|
50183
|
+
content = import_node_fs9.default.readFileSync(import_node_path9.default.join(entryPath, "SKILL.md"), "utf8");
|
|
50184
|
+
} catch {
|
|
50185
|
+
try {
|
|
50186
|
+
content = import_node_fs9.default.readFileSync(import_node_path9.default.join(entryPath, "skill.md"), "utf8");
|
|
50187
|
+
} catch {
|
|
50188
|
+
continue;
|
|
50189
|
+
}
|
|
50190
|
+
}
|
|
50191
|
+
const { description } = parseDescription(content);
|
|
50192
|
+
const baseName = ent.name;
|
|
50193
|
+
const name = pluginName ? `${pluginName}:${baseName}` : baseName;
|
|
50194
|
+
if (seen.has(name)) continue;
|
|
50195
|
+
seen.add(name);
|
|
50196
|
+
const info = { name, source, path: entryPath, description: description || void 0 };
|
|
50197
|
+
if (pluginName) info.plugin = pluginName;
|
|
50198
|
+
out.push(info);
|
|
50199
|
+
}
|
|
50200
|
+
}
|
|
50201
|
+
function listSkillsForDir(dir, source) {
|
|
50202
|
+
const seen = /* @__PURE__ */ new Set();
|
|
50203
|
+
const out = [];
|
|
50204
|
+
scanSkillDir(dir, source, seen, out);
|
|
50205
|
+
return out;
|
|
50206
|
+
}
|
|
50207
|
+
function scanCommandDir(dir, source, seen, out, pluginName) {
|
|
50208
|
+
let entries;
|
|
50209
|
+
try {
|
|
50210
|
+
entries = import_node_fs9.default.readdirSync(dir, { withFileTypes: true });
|
|
50211
|
+
} catch {
|
|
50212
|
+
return;
|
|
50213
|
+
}
|
|
50214
|
+
for (const ent of entries) {
|
|
50215
|
+
const entryPath = import_node_path9.default.join(dir, ent.name);
|
|
50216
|
+
if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync(entryPath)) {
|
|
50217
|
+
const ns = ent.name;
|
|
50218
|
+
let subEntries;
|
|
50219
|
+
try {
|
|
50220
|
+
subEntries = import_node_fs9.default.readdirSync(entryPath, { withFileTypes: true });
|
|
50221
|
+
} catch {
|
|
50222
|
+
continue;
|
|
50223
|
+
}
|
|
50224
|
+
for (const se of subEntries) {
|
|
50225
|
+
if (!se.name.endsWith(".md")) continue;
|
|
50226
|
+
const sePath = import_node_path9.default.join(entryPath, se.name);
|
|
50227
|
+
let content;
|
|
50228
|
+
try {
|
|
50229
|
+
content = import_node_fs9.default.readFileSync(sePath, "utf8");
|
|
50230
|
+
} catch {
|
|
50231
|
+
continue;
|
|
50232
|
+
}
|
|
50233
|
+
const cmd = se.name.replace(/\.md$/, "");
|
|
50234
|
+
const { description } = parseDescription(content);
|
|
50235
|
+
const qualified = `${ns}:${cmd}`;
|
|
50236
|
+
const name = pluginName ? `${pluginName}:${qualified}` : qualified;
|
|
50237
|
+
if (seen.has(name)) continue;
|
|
50238
|
+
seen.add(name);
|
|
50239
|
+
const info = { name, source, path: sePath, description: description || void 0 };
|
|
50240
|
+
if (pluginName) info.plugin = pluginName;
|
|
50241
|
+
out.push(info);
|
|
50242
|
+
}
|
|
50243
|
+
} else if (ent.name.endsWith(".md")) {
|
|
50244
|
+
let content;
|
|
50245
|
+
try {
|
|
50246
|
+
content = import_node_fs9.default.readFileSync(entryPath, "utf8");
|
|
50247
|
+
} catch {
|
|
50248
|
+
continue;
|
|
50249
|
+
}
|
|
50250
|
+
const cmd = ent.name.replace(/\.md$/, "");
|
|
50251
|
+
const { description } = parseDescription(content);
|
|
50252
|
+
const name = pluginName ? `${pluginName}:${cmd}` : cmd;
|
|
50253
|
+
if (seen.has(name)) continue;
|
|
50254
|
+
seen.add(name);
|
|
50255
|
+
const info = { name, source, path: entryPath, description: description || void 0 };
|
|
50256
|
+
if (pluginName) info.plugin = pluginName;
|
|
50257
|
+
out.push(info);
|
|
50258
|
+
}
|
|
50259
|
+
}
|
|
50260
|
+
}
|
|
50261
|
+
function readInstalledPlugins(home) {
|
|
50262
|
+
const file = import_node_path9.default.join(home, ".claude", "plugins", "installed_plugins.json");
|
|
50263
|
+
let raw;
|
|
50264
|
+
try {
|
|
50265
|
+
raw = import_node_fs9.default.readFileSync(file, "utf8");
|
|
50266
|
+
} catch {
|
|
50267
|
+
return [];
|
|
50268
|
+
}
|
|
50269
|
+
let parsed;
|
|
50270
|
+
try {
|
|
50271
|
+
parsed = JSON.parse(raw);
|
|
50272
|
+
} catch {
|
|
50273
|
+
return [];
|
|
50274
|
+
}
|
|
50275
|
+
const out = [];
|
|
50276
|
+
for (const [key, entries] of Object.entries(parsed.plugins ?? {})) {
|
|
50277
|
+
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
50278
|
+
const entry = entries[0];
|
|
50279
|
+
if (!entry?.installPath) continue;
|
|
50280
|
+
const pluginName = key.includes("@") ? key.slice(0, key.indexOf("@")) : key;
|
|
50281
|
+
if (!pluginName) continue;
|
|
50282
|
+
out.push({ name: pluginName, root: entry.installPath });
|
|
50283
|
+
}
|
|
50284
|
+
return out;
|
|
50285
|
+
}
|
|
50286
|
+
var SkillsScanner = class {
|
|
50287
|
+
home;
|
|
50288
|
+
extraPluginRoots;
|
|
50289
|
+
constructor(opts = {}) {
|
|
50290
|
+
this.home = opts.home ?? import_node_os6.default.homedir();
|
|
50291
|
+
this.extraPluginRoots = opts.extraPluginRoots ?? [];
|
|
50292
|
+
}
|
|
50293
|
+
/**
|
|
50294
|
+
* 对齐 cc-direct(openclaw-plugin-clawos/src/claude-code/skills-scanner.ts):
|
|
50295
|
+
* 1. global: ~/.claude/skills + ~/.claude/commands
|
|
50296
|
+
* 2. project: <cwd>/.claude/skills + <cwd>/.claude/commands
|
|
50297
|
+
* 3. plugin: installed_plugins.json 里每个 plugin 的 skills + commands
|
|
50298
|
+
* 顺序 global → project → plugin,同名去重(先到先得)
|
|
50299
|
+
*/
|
|
50300
|
+
list(args) {
|
|
50301
|
+
const seen = /* @__PURE__ */ new Set();
|
|
50302
|
+
const builtinBlock = [];
|
|
50303
|
+
for (const b2 of BUILTIN_SKILLS) {
|
|
50304
|
+
if (seen.has(b2.name)) continue;
|
|
50305
|
+
seen.add(b2.name);
|
|
50306
|
+
builtinBlock.push({
|
|
50307
|
+
name: b2.name,
|
|
50308
|
+
source: "builtin",
|
|
50309
|
+
description: b2.description
|
|
50310
|
+
});
|
|
50311
|
+
}
|
|
50312
|
+
const fsBlock = [];
|
|
50313
|
+
scanSkillDir(import_node_path9.default.join(this.home, ".claude", "skills"), "global", seen, fsBlock);
|
|
50314
|
+
scanCommandDir(import_node_path9.default.join(this.home, ".claude", "commands"), "global", seen, fsBlock);
|
|
50315
|
+
scanSkillDir(import_node_path9.default.join(args.cwd, ".claude", "skills"), "project", seen, fsBlock);
|
|
50316
|
+
scanCommandDir(import_node_path9.default.join(args.cwd, ".claude", "commands"), "project", seen, fsBlock);
|
|
50317
|
+
const plugins = [...readInstalledPlugins(this.home), ...this.extraPluginRoots];
|
|
50318
|
+
for (const { name, root } of plugins) {
|
|
50319
|
+
scanSkillDir(import_node_path9.default.join(root, "skills"), "plugin", seen, fsBlock, name);
|
|
50320
|
+
scanCommandDir(import_node_path9.default.join(root, "commands"), "plugin", seen, fsBlock, name);
|
|
50321
|
+
}
|
|
50322
|
+
fsBlock.sort((a, b2) => a.name < b2.name ? -1 : a.name > b2.name ? 1 : 0);
|
|
50323
|
+
return [...builtinBlock, ...fsBlock];
|
|
50324
|
+
}
|
|
50325
|
+
};
|
|
50326
|
+
|
|
50327
|
+
// src/persona/manager.ts
|
|
50328
|
+
var PersonaManager = class {
|
|
50329
|
+
constructor(deps) {
|
|
50330
|
+
this.deps = deps;
|
|
50331
|
+
}
|
|
50332
|
+
deps;
|
|
50333
|
+
/**
|
|
50334
|
+
* slug 是 persona 的身份,label 只是显示名——两者刻意分开:id 落进文件路径 /
|
|
50335
|
+
* 分享链接且创建后不可改,label 可中文、可随时改。此前 id 从 label 推导,
|
|
50336
|
+
* 非 ascii 被剥空后一律 fallback 成 `persona-persona`。
|
|
50337
|
+
*
|
|
50338
|
+
* 撞名直接抛错而非改名:id 由调用方显式指定,静默换成带后缀的 id 等于把
|
|
50339
|
+
* 用户的选择偷偷改掉。字符集校验在 PersonaCreateArgsSchema(路径穿越边界)。
|
|
50340
|
+
*/
|
|
50341
|
+
create(args) {
|
|
50342
|
+
const personaId2 = `persona-${args.slug}`;
|
|
50343
|
+
if (this.deps.store.has(personaId2)) {
|
|
50344
|
+
throw new Error(`personaId already exists: ${personaId2}`);
|
|
50345
|
+
}
|
|
50346
|
+
const now = Date.now();
|
|
50347
|
+
const persona = {
|
|
50348
|
+
personaId: personaId2,
|
|
50349
|
+
label: args.label,
|
|
50350
|
+
model: args.model,
|
|
50351
|
+
// effort 兜底 high:persona 一律持有具体档位(spec 2026-08-12),UI 会带界面上高亮的
|
|
50352
|
+
// 那一档,但 persona:create 也可能来自 butler 的 persona-authoring skill 这类不带该
|
|
50353
|
+
// 字段的调用方——不兜的话要等下次 daemon 启动 backfill 才补上,中间建的 session 就漏了。
|
|
50354
|
+
// 取值理由与 backfillPersonaSessionDefaults 一致(见那里的注释)。
|
|
50355
|
+
effort: args.effort ?? "high",
|
|
50356
|
+
tool: args.tool,
|
|
50357
|
+
public: args.public ?? false,
|
|
50358
|
+
iconKey: args.iconKey,
|
|
50359
|
+
createdAt: now,
|
|
50360
|
+
updatedAt: now
|
|
50361
|
+
};
|
|
50362
|
+
this.deps.store.write(persona, args.personality);
|
|
50363
|
+
this.deps.store.writeCodexSandboxSettings(personaId2, CODEX_SANDBOX_DEFAULT);
|
|
50364
|
+
this.deps.registry.set(persona);
|
|
50365
|
+
return persona;
|
|
50366
|
+
}
|
|
50367
|
+
update(personaId2, patch, texts) {
|
|
50368
|
+
const existing = this.deps.registry.get(personaId2);
|
|
50369
|
+
if (!existing) throw new Error(`persona not found: ${personaId2}`);
|
|
50370
|
+
const { iconKey: iconKeyPatch, ...restPatch } = patch;
|
|
50371
|
+
const updated = {
|
|
50372
|
+
...existing,
|
|
50373
|
+
...restPatch,
|
|
50374
|
+
updatedAt: Date.now()
|
|
50375
|
+
};
|
|
50376
|
+
const toolChanged = patch.tool !== void 0 && (patch.tool ?? "claude") !== (existing.tool ?? "claude");
|
|
50377
|
+
if (toolChanged) {
|
|
50378
|
+
if (patch.model === void 0) delete updated.model;
|
|
50379
|
+
if (patch.effort === void 0) delete updated.effort;
|
|
50380
|
+
}
|
|
50381
|
+
if (iconKeyPatch === null) {
|
|
50382
|
+
delete updated.iconKey;
|
|
50383
|
+
} else if (iconKeyPatch !== void 0) {
|
|
50384
|
+
updated.iconKey = iconKeyPatch;
|
|
50385
|
+
}
|
|
50386
|
+
if (texts?.personality !== void 0) {
|
|
50387
|
+
this.deps.store.writePersonality(personaId2, texts.personality);
|
|
50388
|
+
}
|
|
50389
|
+
if (texts?.personalityLocal !== void 0) {
|
|
50390
|
+
this.deps.store.writePersonalityLocal(personaId2, texts.personalityLocal);
|
|
50391
|
+
}
|
|
50392
|
+
this.deps.store.writeMeta(updated);
|
|
50393
|
+
this.deps.registry.set(updated);
|
|
50394
|
+
return updated;
|
|
50395
|
+
}
|
|
50396
|
+
/** 读取 CLAUDE.md 内容;persona 目录或文件不存在时返回 null。透传给 handler 拼响应。 */
|
|
50397
|
+
readPersonality(personaId2) {
|
|
50398
|
+
return this.deps.store.readPersonality(personaId2);
|
|
50399
|
+
}
|
|
50400
|
+
/** 读人格补充层(`.clawd/personality.local.md`);没写过返回 null。供 persona:get 拼响应。 */
|
|
50401
|
+
readPersonalityLocal(personaId2) {
|
|
50402
|
+
return this.deps.store.readPersonalityLocal(personaId2);
|
|
50403
|
+
}
|
|
50404
|
+
/** 扫 persona 私有 skills 目录;目录不存在返回空数组。供 persona:get handler 拼响应。 */
|
|
50405
|
+
listSkills(personaId2) {
|
|
50406
|
+
return listSkillsForDir(this.deps.store.skillsDir(personaId2), "project");
|
|
50407
|
+
}
|
|
50408
|
+
/**
|
|
50409
|
+
* 读取 persona `.claude/settings.json` 中已启用的插件列表(value === true 的 key)。
|
|
50410
|
+
* 文件不存在 / 解析失败 / 字段缺失 → 空数组。供 persona:get handler 拼响应。
|
|
50411
|
+
*/
|
|
50412
|
+
listEnabledPlugins(personaId2) {
|
|
50413
|
+
return this.deps.store.readEnabledPlugins(personaId2);
|
|
50414
|
+
}
|
|
50415
|
+
/** 读 codex guest 沙箱配置(`.clawd/codex-sandbox.json`);缺失 / 坏 → null。透传给 handler 拼响应。 */
|
|
50416
|
+
readCodexSandboxSettings(personaId2) {
|
|
50417
|
+
return this.deps.store.readCodexSandboxSettings(personaId2);
|
|
50418
|
+
}
|
|
50419
|
+
/**
|
|
50420
|
+
* 局部合并写 codex guest 沙箱配置,返回合并后的全量。
|
|
50421
|
+
*
|
|
50422
|
+
* **必须是合并不是覆盖**:UI 的「访客联网」开关只发 `{network}`,整体覆盖会静默抹掉 owner
|
|
50423
|
+
* 手配的 writableRoots / denyRead(丢的是安全边界,且用户不会立刻发现)。
|
|
50424
|
+
* 不走 PersonaManager.update 是因为它管的是 PersonaFile meta + 人格两层文本,
|
|
50425
|
+
* 而这份配置是另一个文件、另一套 schema。
|
|
50426
|
+
*
|
|
50427
|
+
* **读不到时 base 用 CODEX_SANDBOX_DEFAULT 而不是 `{}`**:`{}` 会让这次「合并」实际写出一份
|
|
50428
|
+
* 只有 patch 字段的残缺配置(例如只有 `{network:true}`,没有包管理器缓存目录 → `npm install`
|
|
50429
|
+
* EPERM),而且从此 migrateCodexSandbox 见文件已存在就跳过,UI 上再也补不回来。
|
|
50430
|
+
* 正常路径下文件在 create 时就已物化,这条兜底只在文件被删 / 被改坏时触发。
|
|
50431
|
+
*
|
|
50432
|
+
* ⚠️ 已知限制:`CodexSandboxSettingsSchema` 是 `.strict()` 的,owner 手编时多写一个键
|
|
50433
|
+
* (注释键 / 历史字段)会让整份文件读成 null,于是这次合并把它替换成默认值 ⊕ patch。
|
|
50434
|
+
* 已有测试锁定该 strict 语义(store.spec「schema 校验失败 → 返回 null」),本次不动它——
|
|
50435
|
+
* 至少残缺配置不会再被写出去。要放宽得单独评估。
|
|
50436
|
+
*/
|
|
50437
|
+
mergeCodexSandbox(personaId2, patch) {
|
|
50438
|
+
if (!this.deps.registry.get(personaId2)) throw new Error(`persona not found: ${personaId2}`);
|
|
50439
|
+
const base = this.deps.store.readCodexSandboxSettings(personaId2) ?? CODEX_SANDBOX_DEFAULT;
|
|
50440
|
+
const merged = { ...base, ...patch };
|
|
50441
|
+
this.deps.store.writeCodexSandboxSettings(personaId2, merged);
|
|
50442
|
+
return merged;
|
|
50443
|
+
}
|
|
50444
|
+
/** 读 sandbox-settings.json;不存在 / 损坏返回 null。供 persona:get handler 拼响应。 */
|
|
50445
|
+
readSandboxSettings(personaId2) {
|
|
50446
|
+
return this.deps.store.readSandboxSettings(personaId2);
|
|
50447
|
+
}
|
|
50448
|
+
/**
|
|
50449
|
+
* 删除 persona。
|
|
50450
|
+
* PersonaStore.remove(personaId) 级联删整个 <personaRoot>/<personaId>/ 目录
|
|
50451
|
+
* (含 .clawd/sessions/owner/ 和 .clawd/sessions/guests/<capId>/),rmSync
|
|
50452
|
+
* recursive force 一锅清。
|
|
50453
|
+
*/
|
|
50454
|
+
delete(personaId2) {
|
|
50455
|
+
this.deps.store.remove(personaId2);
|
|
50456
|
+
this.deps.registry.remove(personaId2);
|
|
50457
|
+
}
|
|
50458
|
+
};
|
|
50459
|
+
|
|
50445
50460
|
// src/share-md-viewer/load.ts
|
|
50446
50461
|
var import_node_fs11 = __toESM(require("fs"), 1);
|
|
50447
50462
|
var import_node_path10 = __toESM(require("path"), 1);
|
|
@@ -63003,7 +63018,7 @@ function computeMethodAccess(args) {
|
|
|
63003
63018
|
}
|
|
63004
63019
|
|
|
63005
63020
|
// src/version.ts
|
|
63006
|
-
var version = "0.2.
|
|
63021
|
+
var version = "0.2.441".length > 0 ? "0.2.441" : "dev";
|
|
63007
63022
|
|
|
63008
63023
|
// src/cli-probe/probe.ts
|
|
63009
63024
|
var fs66 = __toESM(require("fs"), 1);
|
|
@@ -63187,16 +63202,8 @@ function buildPersonaHandlers(deps) {
|
|
|
63187
63202
|
const args = parseArgs2(PersonaUpdateArgsSchema, frame, "persona:update");
|
|
63188
63203
|
const { personality, personalityLocal, codexSandbox, ...metaPatch } = args.patch;
|
|
63189
63204
|
const persona = personaManager.update(args.personaId, metaPatch, { personality, personalityLocal });
|
|
63190
|
-
|
|
63191
|
-
return {
|
|
63192
|
-
response: {
|
|
63193
|
-
type: "persona:info",
|
|
63194
|
-
...persona,
|
|
63195
|
-
// 只在本次确实动过时回带,让调用方拿到合并后的真值而不用再 get 一次;
|
|
63196
|
-
// 没动就不带(与 personality 等分层字段同口径:persona:update 的响应是 meta 视图)。
|
|
63197
|
-
...mergedCodexSandbox ? { codexSandbox: mergedCodexSandbox } : {}
|
|
63198
|
-
}
|
|
63199
|
-
};
|
|
63205
|
+
if (codexSandbox) personaManager.mergeCodexSandbox(args.personaId, codexSandbox);
|
|
63206
|
+
return { response: { type: "persona:info", ...persona } };
|
|
63200
63207
|
};
|
|
63201
63208
|
const del = async (frame) => {
|
|
63202
63209
|
const args = parseArgs2(PersonaIdArgsSchema, frame, "persona:delete");
|