@bububuger/spanory 0.1.20 → 0.1.22
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/index.js +321 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4542,6 +4542,14 @@ ${notifyLine}
|
|
|
4542
4542
|
function escapeTomlBasicString(value) {
|
|
4543
4543
|
return String(value).replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
4544
4544
|
}
|
|
4545
|
+
function isSpanoryCodexNotifyConfigured(configText) {
|
|
4546
|
+
return /notify\s*=\s*\[[^\]]*spanory-codex-notify\.sh[^\]]*\]/m.test(String(configText ?? ""));
|
|
4547
|
+
}
|
|
4548
|
+
function stripSpanoryCodexNotifyConfig(configText) {
|
|
4549
|
+
const next = String(configText ?? "").replace(/^notify\s*=\s*\[[^\]]*spanory-codex-notify\.sh[^\]]*\]\s*$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
4550
|
+
return next ? `${next}
|
|
4551
|
+
` : "";
|
|
4552
|
+
}
|
|
4545
4553
|
function codexNotifyScriptContent({ spanoryBin, codexHome, exportDir, logFile }) {
|
|
4546
4554
|
return `#!/usr/bin/env bash
|
|
4547
4555
|
set -euo pipefail
|
|
@@ -4629,6 +4637,183 @@ async function applyCodexSetup({ homeRoot, spanoryBin, dryRun }) {
|
|
|
4629
4637
|
configBackup
|
|
4630
4638
|
};
|
|
4631
4639
|
}
|
|
4640
|
+
async function applyCodexWatchSetup({ homeRoot, dryRun }) {
|
|
4641
|
+
const codexHome = path8.join(homeRoot, ".codex");
|
|
4642
|
+
const scriptPath = path8.join(codexHome, "bin", "spanory-codex-notify.sh");
|
|
4643
|
+
const configPath = path8.join(codexHome, "config.toml");
|
|
4644
|
+
let currentConfig = "";
|
|
4645
|
+
try {
|
|
4646
|
+
currentConfig = await readFile8(configPath, "utf-8");
|
|
4647
|
+
} catch (error) {
|
|
4648
|
+
if (error?.code !== "ENOENT")
|
|
4649
|
+
throw error;
|
|
4650
|
+
}
|
|
4651
|
+
const nextConfig = stripSpanoryCodexNotifyConfig(currentConfig);
|
|
4652
|
+
const configChanged = currentConfig !== nextConfig;
|
|
4653
|
+
let scriptPresent = false;
|
|
4654
|
+
try {
|
|
4655
|
+
await stat4(scriptPath);
|
|
4656
|
+
scriptPresent = true;
|
|
4657
|
+
} catch {
|
|
4658
|
+
}
|
|
4659
|
+
const changed = configChanged || scriptPresent;
|
|
4660
|
+
let configBackup = null;
|
|
4661
|
+
if (changed && !dryRun) {
|
|
4662
|
+
if (configChanged) {
|
|
4663
|
+
await mkdir4(path8.dirname(configPath), { recursive: true });
|
|
4664
|
+
configBackup = await backupIfExists(configPath);
|
|
4665
|
+
await writeFile4(configPath, nextConfig, "utf-8");
|
|
4666
|
+
}
|
|
4667
|
+
if (scriptPresent) {
|
|
4668
|
+
await rm(scriptPath, { force: true });
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
return {
|
|
4672
|
+
runtime: "codex",
|
|
4673
|
+
ok: true,
|
|
4674
|
+
changed,
|
|
4675
|
+
dryRun,
|
|
4676
|
+
mode: "watch",
|
|
4677
|
+
configPath,
|
|
4678
|
+
scriptPath,
|
|
4679
|
+
configBackup,
|
|
4680
|
+
notifyConfigured: isSpanoryCodexNotifyConfigured(nextConfig),
|
|
4681
|
+
scriptPresent: false
|
|
4682
|
+
};
|
|
4683
|
+
}
|
|
4684
|
+
function removeClaudeHooks(settings) {
|
|
4685
|
+
if (!settings.hooks || typeof settings.hooks !== "object")
|
|
4686
|
+
return;
|
|
4687
|
+
for (const eventName of Object.keys(settings.hooks)) {
|
|
4688
|
+
const groups = settings.hooks[eventName];
|
|
4689
|
+
if (!Array.isArray(groups))
|
|
4690
|
+
continue;
|
|
4691
|
+
for (const group of groups) {
|
|
4692
|
+
if (!group || !Array.isArray(group.hooks))
|
|
4693
|
+
continue;
|
|
4694
|
+
group.hooks = group.hooks.filter((hook) => !(hook && typeof hook === "object" && isSpanoryHookCommand(hook.command)));
|
|
4695
|
+
}
|
|
4696
|
+
settings.hooks[eventName] = groups.filter((group) => !group || !Array.isArray(group.hooks) || group.hooks.length > 0);
|
|
4697
|
+
if (settings.hooks[eventName].length === 0)
|
|
4698
|
+
delete settings.hooks[eventName];
|
|
4699
|
+
}
|
|
4700
|
+
if (Object.keys(settings.hooks).length === 0)
|
|
4701
|
+
delete settings.hooks;
|
|
4702
|
+
}
|
|
4703
|
+
async function teardownClaudeSetup({ homeRoot, dryRun }) {
|
|
4704
|
+
const settingsPath = path8.join(homeRoot, ".claude", "settings.json");
|
|
4705
|
+
let settings = {};
|
|
4706
|
+
try {
|
|
4707
|
+
settings = JSON.parse(await readFile8(settingsPath, "utf-8"));
|
|
4708
|
+
} catch (error) {
|
|
4709
|
+
if (error?.code !== "ENOENT")
|
|
4710
|
+
throw new Error(`failed to parse Claude settings: ${error.message ?? String(error)}`);
|
|
4711
|
+
return { runtime: "claude-code", ok: true, changed: false, dryRun, settingsPath, backup: null };
|
|
4712
|
+
}
|
|
4713
|
+
const before = JSON.stringify(settings);
|
|
4714
|
+
removeClaudeHooks(settings);
|
|
4715
|
+
const after = JSON.stringify(settings);
|
|
4716
|
+
const changed = before !== after;
|
|
4717
|
+
let backup = null;
|
|
4718
|
+
if (changed && !dryRun) {
|
|
4719
|
+
backup = await backupIfExists(settingsPath);
|
|
4720
|
+
await writeFile4(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
4721
|
+
}
|
|
4722
|
+
return { runtime: "claude-code", ok: true, changed, dryRun, settingsPath, backup };
|
|
4723
|
+
}
|
|
4724
|
+
async function teardownCodexSetup({ homeRoot, dryRun }) {
|
|
4725
|
+
const codexHome = path8.join(homeRoot, ".codex");
|
|
4726
|
+
const configPath = path8.join(codexHome, "config.toml");
|
|
4727
|
+
const scriptPath = path8.join(codexHome, "bin", "spanory-codex-notify.sh");
|
|
4728
|
+
let currentConfig = "";
|
|
4729
|
+
try {
|
|
4730
|
+
currentConfig = await readFile8(configPath, "utf-8");
|
|
4731
|
+
} catch (error) {
|
|
4732
|
+
if (error?.code !== "ENOENT")
|
|
4733
|
+
throw error;
|
|
4734
|
+
}
|
|
4735
|
+
const nextConfig = stripSpanoryCodexNotifyConfig(currentConfig);
|
|
4736
|
+
const configChanged = currentConfig !== nextConfig;
|
|
4737
|
+
let scriptPresent = false;
|
|
4738
|
+
try {
|
|
4739
|
+
await stat4(scriptPath);
|
|
4740
|
+
scriptPresent = true;
|
|
4741
|
+
} catch {
|
|
4742
|
+
}
|
|
4743
|
+
const changed = configChanged || scriptPresent;
|
|
4744
|
+
let configBackup = null;
|
|
4745
|
+
if (changed && !dryRun) {
|
|
4746
|
+
if (configChanged) {
|
|
4747
|
+
configBackup = await backupIfExists(configPath);
|
|
4748
|
+
await writeFile4(configPath, nextConfig, "utf-8");
|
|
4749
|
+
}
|
|
4750
|
+
if (scriptPresent)
|
|
4751
|
+
await rm(scriptPath, { force: true });
|
|
4752
|
+
}
|
|
4753
|
+
return { runtime: "codex", ok: true, changed, dryRun, configPath, scriptPath, configBackup, scriptRemoved: scriptPresent };
|
|
4754
|
+
}
|
|
4755
|
+
async function teardownOpenclawSetup({ homeRoot, openclawRuntimeHome, dryRun }) {
|
|
4756
|
+
if (!commandExists("openclaw")) {
|
|
4757
|
+
return { runtime: "openclaw", ok: true, skipped: true, detail: "openclaw command not found in PATH" };
|
|
4758
|
+
}
|
|
4759
|
+
const runtimeHome = openclawRuntimeHomeForSetup(homeRoot, openclawRuntimeHome);
|
|
4760
|
+
if (dryRun)
|
|
4761
|
+
return { runtime: "openclaw", ok: true, changed: true, dryRun };
|
|
4762
|
+
const result = runSystemCommand("openclaw", ["plugins", "uninstall", OPENCLAW_SPANORY_PLUGIN_ID], {
|
|
4763
|
+
env: { ...process.env, ...runtimeHome ? { OPENCLAW_STATE_DIR: resolveRuntimeHome3("openclaw", runtimeHome) } : {} }
|
|
4764
|
+
});
|
|
4765
|
+
if (result.code !== 0)
|
|
4766
|
+
throw new Error(result.stderr || result.error || "openclaw plugins uninstall failed");
|
|
4767
|
+
return { runtime: "openclaw", ok: true, changed: true, dryRun };
|
|
4768
|
+
}
|
|
4769
|
+
async function teardownOpencodeSetup({ homeRoot, opencodeRuntimeHome, dryRun }) {
|
|
4770
|
+
const runtimeHome = opencodeRuntimeHomeForSetup(homeRoot, opencodeRuntimeHome);
|
|
4771
|
+
const loaderFile = opencodePluginLoaderPath(runtimeHome);
|
|
4772
|
+
let present = false;
|
|
4773
|
+
try {
|
|
4774
|
+
await stat4(loaderFile);
|
|
4775
|
+
present = true;
|
|
4776
|
+
} catch {
|
|
4777
|
+
}
|
|
4778
|
+
if (present && !dryRun)
|
|
4779
|
+
await rm(loaderFile, { force: true });
|
|
4780
|
+
return { runtime: "opencode", ok: true, changed: present, dryRun, loaderFile };
|
|
4781
|
+
}
|
|
4782
|
+
async function runSetupTeardown(options) {
|
|
4783
|
+
const homeRoot = setupHomeRoot(options.home);
|
|
4784
|
+
const selected = parseSetupRuntimes(options.runtimes);
|
|
4785
|
+
const dryRun = Boolean(options.dryRun);
|
|
4786
|
+
const results = [];
|
|
4787
|
+
if (selected.includes("claude-code")) {
|
|
4788
|
+
try {
|
|
4789
|
+
results.push(await teardownClaudeSetup({ homeRoot, dryRun }));
|
|
4790
|
+
} catch (error) {
|
|
4791
|
+
results.push({ runtime: "claude-code", ok: false, error: String(error?.message ?? error) });
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
if (selected.includes("codex")) {
|
|
4795
|
+
try {
|
|
4796
|
+
results.push(await teardownCodexSetup({ homeRoot, dryRun }));
|
|
4797
|
+
} catch (error) {
|
|
4798
|
+
results.push({ runtime: "codex", ok: false, error: String(error?.message ?? error) });
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
if (selected.includes("openclaw")) {
|
|
4802
|
+
try {
|
|
4803
|
+
results.push(await teardownOpenclawSetup({ homeRoot, openclawRuntimeHome: options.openclawRuntimeHome, dryRun }));
|
|
4804
|
+
} catch (error) {
|
|
4805
|
+
results.push({ runtime: "openclaw", ok: false, error: String(error?.message ?? error) });
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4808
|
+
if (selected.includes("opencode")) {
|
|
4809
|
+
try {
|
|
4810
|
+
results.push(await teardownOpencodeSetup({ homeRoot, opencodeRuntimeHome: options.opencodeRuntimeHome, dryRun }));
|
|
4811
|
+
} catch (error) {
|
|
4812
|
+
results.push({ runtime: "opencode", ok: false, error: String(error?.message ?? error) });
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
return { ok: results.every((r) => r.ok), results };
|
|
4816
|
+
}
|
|
4632
4817
|
function commandExists(command) {
|
|
4633
4818
|
const result = runSystemCommand("which", [command], { env: process.env });
|
|
4634
4819
|
return result.code === 0;
|
|
@@ -4671,7 +4856,102 @@ function openclawRuntimeHomeForSetup(homeRoot, explicitRuntimeHome) {
|
|
|
4671
4856
|
function opencodeRuntimeHomeForSetup(homeRoot, explicitRuntimeHome) {
|
|
4672
4857
|
return explicitRuntimeHome || path8.join(homeRoot, ".config", "opencode");
|
|
4673
4858
|
}
|
|
4674
|
-
function
|
|
4859
|
+
function resolveOpenclawConfigPath(runtimeHome) {
|
|
4860
|
+
return path8.join(runtimeHome, "openclaw.json");
|
|
4861
|
+
}
|
|
4862
|
+
function canonicalPath(input) {
|
|
4863
|
+
const resolved = path8.resolve(String(input ?? ""));
|
|
4864
|
+
try {
|
|
4865
|
+
return realpathSync(resolved);
|
|
4866
|
+
} catch {
|
|
4867
|
+
return resolved;
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
function readOpenclawPluginNameFromDir(dirPath) {
|
|
4871
|
+
const pkgPath = path8.join(dirPath, "package.json");
|
|
4872
|
+
if (!existsSync(pkgPath))
|
|
4873
|
+
return null;
|
|
4874
|
+
try {
|
|
4875
|
+
const parsed = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
4876
|
+
const name = String(parsed?.name ?? "").trim();
|
|
4877
|
+
return name || null;
|
|
4878
|
+
} catch {
|
|
4879
|
+
return null;
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4882
|
+
function isSpanoryOpenclawPluginPath(candidatePath) {
|
|
4883
|
+
const text = String(candidatePath ?? "").trim();
|
|
4884
|
+
if (!text)
|
|
4885
|
+
return false;
|
|
4886
|
+
const normalized = text.replaceAll("\\", "/").toLowerCase();
|
|
4887
|
+
if (normalized.includes("spanory") && normalized.includes("openclaw-plugin")) {
|
|
4888
|
+
return true;
|
|
4889
|
+
}
|
|
4890
|
+
const canonical = canonicalPath(text);
|
|
4891
|
+
const pluginName = readOpenclawPluginNameFromDir(canonical);
|
|
4892
|
+
return pluginName === "@bububuger/spanory-openclaw-plugin" || pluginName === "@alipay/spanory-openclaw-plugin";
|
|
4893
|
+
}
|
|
4894
|
+
async function normalizeOpenclawPluginLoadPaths(runtimeHome, pluginDir, dryRun) {
|
|
4895
|
+
const configPath = resolveOpenclawConfigPath(runtimeHome);
|
|
4896
|
+
let configRaw = "";
|
|
4897
|
+
try {
|
|
4898
|
+
configRaw = await readFile8(configPath, "utf-8");
|
|
4899
|
+
} catch (error) {
|
|
4900
|
+
if (error?.code === "ENOENT")
|
|
4901
|
+
return { changed: false, configPath, backup: null };
|
|
4902
|
+
throw error;
|
|
4903
|
+
}
|
|
4904
|
+
let parsed;
|
|
4905
|
+
try {
|
|
4906
|
+
parsed = JSON.parse(configRaw);
|
|
4907
|
+
} catch (error) {
|
|
4908
|
+
throw new Error(`failed to parse openclaw config ${configPath}: ${error?.message ?? String(error)}`);
|
|
4909
|
+
}
|
|
4910
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4911
|
+
parsed = {};
|
|
4912
|
+
}
|
|
4913
|
+
if (!parsed.plugins || typeof parsed.plugins !== "object" || Array.isArray(parsed.plugins)) {
|
|
4914
|
+
parsed.plugins = {};
|
|
4915
|
+
}
|
|
4916
|
+
if (!parsed.plugins.load || typeof parsed.plugins.load !== "object" || Array.isArray(parsed.plugins.load)) {
|
|
4917
|
+
parsed.plugins.load = {};
|
|
4918
|
+
}
|
|
4919
|
+
const target = path8.resolve(pluginDir);
|
|
4920
|
+
const targetCanonical = canonicalPath(target);
|
|
4921
|
+
const currentPaths = Array.isArray(parsed.plugins.load.paths) ? parsed.plugins.load.paths : [];
|
|
4922
|
+
const nonSpanoryPaths = [];
|
|
4923
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4924
|
+
for (const item of currentPaths) {
|
|
4925
|
+
if (typeof item !== "string")
|
|
4926
|
+
continue;
|
|
4927
|
+
const text = item.trim();
|
|
4928
|
+
if (!text)
|
|
4929
|
+
continue;
|
|
4930
|
+
if (isSpanoryOpenclawPluginPath(text))
|
|
4931
|
+
continue;
|
|
4932
|
+
const key = canonicalPath(text);
|
|
4933
|
+
if (seen.has(key))
|
|
4934
|
+
continue;
|
|
4935
|
+
seen.add(key);
|
|
4936
|
+
nonSpanoryPaths.push(text);
|
|
4937
|
+
}
|
|
4938
|
+
if (!seen.has(targetCanonical)) {
|
|
4939
|
+
nonSpanoryPaths.push(target);
|
|
4940
|
+
}
|
|
4941
|
+
parsed.plugins.load.paths = nonSpanoryPaths;
|
|
4942
|
+
const nextRaw = `${JSON.stringify(parsed, null, 2)}
|
|
4943
|
+
`;
|
|
4944
|
+
if (nextRaw === configRaw) {
|
|
4945
|
+
return { changed: false, configPath, backup: null };
|
|
4946
|
+
}
|
|
4947
|
+
let backup = null;
|
|
4948
|
+
if (!dryRun) {
|
|
4949
|
+
backup = await backupIfExists(configPath);
|
|
4950
|
+
await writeFile4(configPath, nextRaw, "utf-8");
|
|
4951
|
+
}
|
|
4952
|
+
return { changed: true, configPath, backup };
|
|
4953
|
+
}
|
|
4954
|
+
async function installOpenclawPlugin(runtimeHome, dryRun) {
|
|
4675
4955
|
const pluginDir = path8.resolve(resolveOpenclawPluginDir());
|
|
4676
4956
|
const installResult = runSystemCommand("openclaw", ["plugins", "install", "-l", pluginDir], {
|
|
4677
4957
|
env: {
|
|
@@ -4691,9 +4971,12 @@ function installOpenclawPlugin(runtimeHome) {
|
|
|
4691
4971
|
if (enableResult.code !== 0) {
|
|
4692
4972
|
throw new Error(enableResult.stderr || enableResult.error || "openclaw plugins enable failed");
|
|
4693
4973
|
}
|
|
4974
|
+
const pathNormalizeResult = await normalizeOpenclawPluginLoadPaths(runtimeHome, pluginDir, dryRun);
|
|
4694
4975
|
return {
|
|
4976
|
+
pluginDir,
|
|
4695
4977
|
installStdout: installResult.stdout.trim(),
|
|
4696
|
-
enableStdout: enableResult.stdout.trim()
|
|
4978
|
+
enableStdout: enableResult.stdout.trim(),
|
|
4979
|
+
pathNormalizeResult
|
|
4697
4980
|
};
|
|
4698
4981
|
}
|
|
4699
4982
|
async function installOpencodePlugin(runtimeHome, pluginDirOverride) {
|
|
@@ -4749,7 +5032,7 @@ async function runSetupDetect(options) {
|
|
|
4749
5032
|
let codexNotifyConfigured = false;
|
|
4750
5033
|
try {
|
|
4751
5034
|
const config = await readFile8(codexConfigPath, "utf-8");
|
|
4752
|
-
codexNotifyConfigured =
|
|
5035
|
+
codexNotifyConfigured = isSpanoryCodexNotifyConfigured(config);
|
|
4753
5036
|
} catch {
|
|
4754
5037
|
}
|
|
4755
5038
|
let codexScriptPresent = true;
|
|
@@ -4761,10 +5044,11 @@ async function runSetupDetect(options) {
|
|
|
4761
5044
|
report2.runtimes.push({
|
|
4762
5045
|
runtime: "codex",
|
|
4763
5046
|
available: commandExists("codex"),
|
|
4764
|
-
configured: codexNotifyConfigured
|
|
5047
|
+
configured: !codexNotifyConfigured,
|
|
4765
5048
|
details: {
|
|
4766
5049
|
configPath: codexConfigPath,
|
|
4767
5050
|
scriptPath: codexScriptPath,
|
|
5051
|
+
mode: "watch",
|
|
4768
5052
|
notifyConfigured: codexNotifyConfigured,
|
|
4769
5053
|
scriptPresent: codexScriptPresent
|
|
4770
5054
|
}
|
|
@@ -4822,7 +5106,7 @@ async function runSetupDoctor(options) {
|
|
|
4822
5106
|
let notifyConfigured = false;
|
|
4823
5107
|
try {
|
|
4824
5108
|
const config = await readFile8(configPath, "utf-8");
|
|
4825
|
-
notifyConfigured =
|
|
5109
|
+
notifyConfigured = isSpanoryCodexNotifyConfigured(config);
|
|
4826
5110
|
} catch {
|
|
4827
5111
|
}
|
|
4828
5112
|
let scriptPresent = false;
|
|
@@ -4832,15 +5116,15 @@ async function runSetupDoctor(options) {
|
|
|
4832
5116
|
} catch {
|
|
4833
5117
|
}
|
|
4834
5118
|
checks.push({
|
|
4835
|
-
id: "
|
|
5119
|
+
id: "codex_watch_mode",
|
|
4836
5120
|
runtime: "codex",
|
|
4837
|
-
ok: notifyConfigured,
|
|
5121
|
+
ok: !notifyConfigured,
|
|
4838
5122
|
detail: configPath
|
|
4839
5123
|
});
|
|
4840
5124
|
checks.push({
|
|
4841
|
-
id: "
|
|
5125
|
+
id: "codex_notify_script_absent",
|
|
4842
5126
|
runtime: "codex",
|
|
4843
|
-
ok: scriptPresent,
|
|
5127
|
+
ok: !scriptPresent,
|
|
4844
5128
|
detail: scriptPath
|
|
4845
5129
|
});
|
|
4846
5130
|
}
|
|
@@ -4883,7 +5167,7 @@ async function runSetupApply(options) {
|
|
|
4883
5167
|
const selected = parseSetupRuntimes(options.runtimes);
|
|
4884
5168
|
const spanoryBin = options.spanoryBin ?? "spanory";
|
|
4885
5169
|
const dryRun = Boolean(options.dryRun);
|
|
4886
|
-
const codexMode = options.codexMode ?? "
|
|
5170
|
+
const codexMode = options.codexMode ?? process.env.SPANORY_CODEX_MODE ?? "watch";
|
|
4887
5171
|
const results = [];
|
|
4888
5172
|
if (selected.includes("claude-code")) {
|
|
4889
5173
|
try {
|
|
@@ -4898,14 +5182,7 @@ async function runSetupApply(options) {
|
|
|
4898
5182
|
}
|
|
4899
5183
|
}
|
|
4900
5184
|
if (selected.includes("codex")) {
|
|
4901
|
-
if (codexMode
|
|
4902
|
-
results.push({
|
|
4903
|
-
runtime: "codex",
|
|
4904
|
-
ok: true,
|
|
4905
|
-
skipped: true,
|
|
4906
|
-
detail: `codex mode "${codexMode}" skips notify setup`
|
|
4907
|
-
});
|
|
4908
|
-
} else {
|
|
5185
|
+
if (codexMode === "notify") {
|
|
4909
5186
|
try {
|
|
4910
5187
|
const result = await applyCodexSetup({ homeRoot, spanoryBin, dryRun });
|
|
4911
5188
|
results.push(result);
|
|
@@ -4916,6 +5193,24 @@ async function runSetupApply(options) {
|
|
|
4916
5193
|
error: String(error?.message ?? error)
|
|
4917
5194
|
});
|
|
4918
5195
|
}
|
|
5196
|
+
} else if (codexMode === "watch") {
|
|
5197
|
+
try {
|
|
5198
|
+
const result = await applyCodexWatchSetup({ homeRoot, dryRun });
|
|
5199
|
+
results.push(result);
|
|
5200
|
+
} catch (error) {
|
|
5201
|
+
results.push({
|
|
5202
|
+
runtime: "codex",
|
|
5203
|
+
ok: false,
|
|
5204
|
+
error: String(error?.message ?? error)
|
|
5205
|
+
});
|
|
5206
|
+
}
|
|
5207
|
+
} else {
|
|
5208
|
+
results.push({
|
|
5209
|
+
runtime: "codex",
|
|
5210
|
+
ok: true,
|
|
5211
|
+
skipped: true,
|
|
5212
|
+
detail: `codex mode "${codexMode}" has no setup action`
|
|
5213
|
+
});
|
|
4919
5214
|
}
|
|
4920
5215
|
}
|
|
4921
5216
|
if (selected.includes("openclaw")) {
|
|
@@ -4930,7 +5225,7 @@ async function runSetupApply(options) {
|
|
|
4930
5225
|
const runtimeHome = openclawRuntimeHomeForSetup(homeRoot, options.openclawRuntimeHome);
|
|
4931
5226
|
try {
|
|
4932
5227
|
if (!dryRun)
|
|
4933
|
-
installOpenclawPlugin(runtimeHome);
|
|
5228
|
+
await installOpenclawPlugin(runtimeHome, dryRun);
|
|
4934
5229
|
const doctor = await runOpenclawPluginDoctor(runtimeHome);
|
|
4935
5230
|
results.push({
|
|
4936
5231
|
runtime: "openclaw",
|
|
@@ -5286,7 +5581,7 @@ setup.command("detect").description("Detect local runtime availability and setup
|
|
|
5286
5581
|
const report2 = await runSetupDetect(options);
|
|
5287
5582
|
console.log(JSON.stringify(report2, null, 2));
|
|
5288
5583
|
});
|
|
5289
|
-
setup.command("apply").description("Apply idempotent local setup for selected runtimes").option("--runtimes <csv>", `Comma-separated runtimes (default: ${DEFAULT_SETUP_RUNTIMES.join(",")})`, DEFAULT_SETUP_RUNTIMES.join(",")).option("--home <path>", "Home directory root override (default: $HOME)").option("--spanory-bin <path>", "Spanory binary/command to write into runtime configs", "spanory").option("--
|
|
5584
|
+
setup.command("apply").description("Apply idempotent local setup for selected runtimes").option("--runtimes <csv>", `Comma-separated runtimes (default: ${DEFAULT_SETUP_RUNTIMES.join(",")})`, DEFAULT_SETUP_RUNTIMES.join(",")).option("--home <path>", "Home directory root override (default: $HOME)").option("--spanory-bin <path>", "Spanory binary/command to write into runtime configs", "spanory").option("--openclaw-runtime-home <path>", "Override OpenClaw runtime home (default: ~/.openclaw)").option("--opencode-runtime-home <path>", "Override OpenCode runtime home (default: ~/.config/opencode)").option("--dry-run", "Only print planned changes without writing files", false).action(async (options) => {
|
|
5290
5585
|
const report2 = await runSetupApply(options);
|
|
5291
5586
|
console.log(JSON.stringify(report2, null, 2));
|
|
5292
5587
|
if (!report2.ok)
|
|
@@ -5298,6 +5593,12 @@ setup.command("doctor").description("Run setup diagnostics for selected runtimes
|
|
|
5298
5593
|
if (!report2.ok)
|
|
5299
5594
|
process.exitCode = 2;
|
|
5300
5595
|
});
|
|
5596
|
+
setup.command("teardown").description("Remove all Spanory integration from local runtimes").option("--runtimes <csv>", `Comma-separated runtimes (default: ${DEFAULT_SETUP_RUNTIMES.join(",")})`, DEFAULT_SETUP_RUNTIMES.join(",")).option("--home <path>", "Home directory root override (default: $HOME)").option("--openclaw-runtime-home <path>", "Override OpenClaw runtime home (default: ~/.openclaw)").option("--opencode-runtime-home <path>", "Override OpenCode runtime home (default: ~/.config/opencode)").option("--dry-run", "Only print planned changes without writing files", false).action(async (options) => {
|
|
5597
|
+
const report2 = await runSetupTeardown(options);
|
|
5598
|
+
console.log(JSON.stringify(report2, null, 2));
|
|
5599
|
+
if (!report2.ok)
|
|
5600
|
+
process.exitCode = 2;
|
|
5601
|
+
});
|
|
5301
5602
|
program.command("upgrade").description("Upgrade spanory CLI from npm registry").option("--dry-run", "Print upgrade command without executing", false).option("--manager <name>", "Package manager override: npm|tnpm").option("--scope <scope>", "Install scope override: global|local").action(async (options) => {
|
|
5302
5603
|
const manager = options.manager === "tnpm" ? "tnpm" : detectUpgradePackageManager();
|
|
5303
5604
|
const scope = options.scope === "local" ? "local" : options.scope === "global" ? "global" : detectUpgradeScope();
|