@caddis/cli 0.4.77 → 0.4.79
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/claude-deepseek.js +29 -1
- package/dist/claude-glm.js +29 -1
- package/dist/claude-oss.js +29 -1
- package/dist/cli.js +238 -146
- package/dist/ui/assets/Guide-D6PTiZmH.js +9 -0
- package/dist/ui/assets/Guide-zcVFGUxR.css +1 -0
- package/dist/ui/assets/favicon-DtHQyAC1.svg +8 -0
- package/dist/ui/assets/favicon-dot-DHAVYP15.svg +9 -0
- package/dist/ui/assets/index-D7fsc0PF.js +221 -0
- package/dist/ui/assets/index-DWiypj6N.css +1 -0
- package/dist/ui/assets/mark-small-CwCIyjyC.svg +8 -0
- package/dist/ui/assets/wordmark-CD0s2ofS.svg +99 -0
- package/dist/ui/fonts/OFL-Atkinson.txt +93 -0
- package/dist/ui/fonts/OFL-MapleMono.txt +93 -0
- package/dist/ui/fonts/atkinson-400.849ae6f0.woff2 +0 -0
- package/dist/ui/fonts/atkinson-700.3a9cc18a.woff2 +0 -0
- package/dist/ui/fonts/maple-mono-400.071799fd.woff2 +0 -0
- package/dist/ui/fonts/maple-mono-700.1e17b237.woff2 +0 -0
- package/dist/ui/index.html +14 -0
- package/package.json +13 -3
package/dist/cli.js
CHANGED
|
@@ -1209,7 +1209,7 @@ var require_command = __commonJS({
|
|
|
1209
1209
|
"use strict";
|
|
1210
1210
|
var EventEmitter = __require("events").EventEmitter;
|
|
1211
1211
|
var childProcess = __require("child_process");
|
|
1212
|
-
var
|
|
1212
|
+
var path20 = __require("path");
|
|
1213
1213
|
var fs = __require("fs");
|
|
1214
1214
|
var process3 = __require("process");
|
|
1215
1215
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
@@ -2222,9 +2222,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2222
2222
|
let launchWithNode = false;
|
|
2223
2223
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2224
2224
|
function findFile(baseDir, baseName) {
|
|
2225
|
-
const localBin =
|
|
2225
|
+
const localBin = path20.resolve(baseDir, baseName);
|
|
2226
2226
|
if (fs.existsSync(localBin)) return localBin;
|
|
2227
|
-
if (sourceExt.includes(
|
|
2227
|
+
if (sourceExt.includes(path20.extname(baseName))) return void 0;
|
|
2228
2228
|
const foundExt = sourceExt.find(
|
|
2229
2229
|
(ext) => fs.existsSync(`${localBin}${ext}`)
|
|
2230
2230
|
);
|
|
@@ -2242,17 +2242,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2242
2242
|
} catch {
|
|
2243
2243
|
resolvedScriptPath = this._scriptPath;
|
|
2244
2244
|
}
|
|
2245
|
-
executableDir =
|
|
2246
|
-
|
|
2245
|
+
executableDir = path20.resolve(
|
|
2246
|
+
path20.dirname(resolvedScriptPath),
|
|
2247
2247
|
executableDir
|
|
2248
2248
|
);
|
|
2249
2249
|
}
|
|
2250
2250
|
if (executableDir) {
|
|
2251
2251
|
let localFile = findFile(executableDir, executableFile);
|
|
2252
2252
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2253
|
-
const legacyName =
|
|
2253
|
+
const legacyName = path20.basename(
|
|
2254
2254
|
this._scriptPath,
|
|
2255
|
-
|
|
2255
|
+
path20.extname(this._scriptPath)
|
|
2256
2256
|
);
|
|
2257
2257
|
if (legacyName !== this._name) {
|
|
2258
2258
|
localFile = findFile(
|
|
@@ -2263,7 +2263,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2263
2263
|
}
|
|
2264
2264
|
executableFile = localFile || executableFile;
|
|
2265
2265
|
}
|
|
2266
|
-
launchWithNode = sourceExt.includes(
|
|
2266
|
+
launchWithNode = sourceExt.includes(path20.extname(executableFile));
|
|
2267
2267
|
let proc;
|
|
2268
2268
|
if (process3.platform !== "win32") {
|
|
2269
2269
|
if (launchWithNode) {
|
|
@@ -3178,7 +3178,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3178
3178
|
* @return {Command}
|
|
3179
3179
|
*/
|
|
3180
3180
|
nameFromFilename(filename) {
|
|
3181
|
-
this._name =
|
|
3181
|
+
this._name = path20.basename(filename, path20.extname(filename));
|
|
3182
3182
|
return this;
|
|
3183
3183
|
}
|
|
3184
3184
|
/**
|
|
@@ -3192,9 +3192,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3192
3192
|
* @param {string} [path]
|
|
3193
3193
|
* @return {(string|null|Command)}
|
|
3194
3194
|
*/
|
|
3195
|
-
executableDir(
|
|
3196
|
-
if (
|
|
3197
|
-
this._executableDir =
|
|
3195
|
+
executableDir(path21) {
|
|
3196
|
+
if (path21 === void 0) return this._executableDir;
|
|
3197
|
+
this._executableDir = path21;
|
|
3198
3198
|
return this;
|
|
3199
3199
|
}
|
|
3200
3200
|
/**
|
|
@@ -3927,12 +3927,28 @@ var init_theme = __esm({
|
|
|
3927
3927
|
}
|
|
3928
3928
|
});
|
|
3929
3929
|
|
|
3930
|
+
// ui/src/brand/palette.ts
|
|
3931
|
+
var PALETTE;
|
|
3932
|
+
var init_palette = __esm({
|
|
3933
|
+
"ui/src/brand/palette.ts"() {
|
|
3934
|
+
"use strict";
|
|
3935
|
+
PALETTE = {
|
|
3936
|
+
blue: "#0D42FC",
|
|
3937
|
+
cyan: "#00CAD5",
|
|
3938
|
+
green: "#91D202",
|
|
3939
|
+
amber: "#FDAD01",
|
|
3940
|
+
orange: "#FC4E23",
|
|
3941
|
+
violet: "#8730FB"
|
|
3942
|
+
};
|
|
3943
|
+
}
|
|
3944
|
+
});
|
|
3945
|
+
|
|
3930
3946
|
// src/util/banner.ts
|
|
3931
3947
|
function renderBanner(options = {}) {
|
|
3932
3948
|
process.stdout.write("\n");
|
|
3933
3949
|
for (let i = 0; i < 7; i++) {
|
|
3934
|
-
const m2 = import_picocolors2.default.bold(
|
|
3935
|
-
const w2 = i < 2 ? import_picocolors2.default.bold(
|
|
3950
|
+
const m2 = import_picocolors2.default.bold(color.mark(MARK[i]));
|
|
3951
|
+
const w2 = i < 2 ? import_picocolors2.default.bold(color.top(WORDMARK[i])) : i < 4 ? import_picocolors2.default.bold(color.mid(WORDMARK[i])) : import_picocolors2.default.bold(color.bottom(WORDMARK[i]));
|
|
3936
3952
|
process.stdout.write(` ${m2} ${w2}
|
|
3937
3953
|
`);
|
|
3938
3954
|
}
|
|
@@ -3941,19 +3957,36 @@ function renderBanner(options = {}) {
|
|
|
3941
3957
|
`);
|
|
3942
3958
|
if (options.cliVersion || options.poolVersion) {
|
|
3943
3959
|
const parts = [];
|
|
3944
|
-
if (options.cliVersion) parts.push(`cli ${
|
|
3945
|
-
if (options.poolVersion) parts.push(`pool ${
|
|
3960
|
+
if (options.cliVersion) parts.push(`cli ${color.cli(options.cliVersion)}`);
|
|
3961
|
+
if (options.poolVersion) parts.push(`pool ${color.pool(import_picocolors2.default.bold(options.poolVersion))}`);
|
|
3946
3962
|
process.stdout.write(` ${import_picocolors2.default.dim(parts.join(" \xB7 "))}
|
|
3947
3963
|
`);
|
|
3948
3964
|
}
|
|
3949
3965
|
process.stdout.write("\n");
|
|
3950
3966
|
}
|
|
3951
|
-
var import_picocolors2, MARK, WORDMARK;
|
|
3967
|
+
var import_picocolors2, BANNER_COLORS, color, MARK, WORDMARK;
|
|
3952
3968
|
var init_banner = __esm({
|
|
3953
3969
|
"src/util/banner.ts"() {
|
|
3954
3970
|
"use strict";
|
|
3955
3971
|
import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
3956
3972
|
init_theme();
|
|
3973
|
+
init_palette();
|
|
3974
|
+
BANNER_COLORS = {
|
|
3975
|
+
mark: PALETTE.green,
|
|
3976
|
+
wordmarkTop: PALETTE.cyan,
|
|
3977
|
+
wordmarkMid: PALETTE.green,
|
|
3978
|
+
wordmarkBottom: PALETTE.violet,
|
|
3979
|
+
cliVersion: PALETTE.cyan,
|
|
3980
|
+
poolVersion: PALETTE.violet
|
|
3981
|
+
};
|
|
3982
|
+
color = {
|
|
3983
|
+
mark: hex(BANNER_COLORS.mark),
|
|
3984
|
+
top: hex(BANNER_COLORS.wordmarkTop),
|
|
3985
|
+
mid: hex(BANNER_COLORS.wordmarkMid),
|
|
3986
|
+
bottom: hex(BANNER_COLORS.wordmarkBottom),
|
|
3987
|
+
cli: hex(BANNER_COLORS.cliVersion),
|
|
3988
|
+
pool: hex(BANNER_COLORS.poolVersion)
|
|
3989
|
+
};
|
|
3957
3990
|
MARK = [
|
|
3958
3991
|
" \u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2584 ",
|
|
3959
3992
|
" \u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584 ",
|
|
@@ -4001,7 +4034,7 @@ function errorLine(text) {
|
|
|
4001
4034
|
process.stderr.write(`${theme.coral("error")} ${text}
|
|
4002
4035
|
`);
|
|
4003
4036
|
}
|
|
4004
|
-
var import_picocolors3, OK, WARN, FAIL, SKIP, MARKS,
|
|
4037
|
+
var import_picocolors3, OK, WARN, FAIL, SKIP, MARKS, color2;
|
|
4005
4038
|
var init_log = __esm({
|
|
4006
4039
|
"src/util/log.ts"() {
|
|
4007
4040
|
"use strict";
|
|
@@ -4020,7 +4053,7 @@ var init_log = __esm({
|
|
|
4020
4053
|
skip: import_picocolors3.default.dim("\xB7"),
|
|
4021
4054
|
info: theme.sky("\u2022")
|
|
4022
4055
|
};
|
|
4023
|
-
|
|
4056
|
+
color2 = Object.assign(import_picocolors3.default, {
|
|
4024
4057
|
mint: theme.mint,
|
|
4025
4058
|
violet: theme.violet,
|
|
4026
4059
|
magenta: theme.magenta,
|
|
@@ -4641,14 +4674,14 @@ async function driveAgents(entries, options) {
|
|
|
4641
4674
|
}
|
|
4642
4675
|
if (result.ok) {
|
|
4643
4676
|
outcome.changed += 1;
|
|
4644
|
-
spinner?.stop(`${label} \u2014 ${
|
|
4677
|
+
spinner?.stop(`${label} \u2014 ${color2.green("done")}`, 0);
|
|
4645
4678
|
if (!spinner) item("ok", `${label} \u2014 done`);
|
|
4646
4679
|
for (const step of result.steps) detail(step.command);
|
|
4647
4680
|
if (result.message) detail(result.message);
|
|
4648
4681
|
continue;
|
|
4649
4682
|
}
|
|
4650
4683
|
outcome.failed += 1;
|
|
4651
|
-
spinner?.stop(`${label} \u2014 ${
|
|
4684
|
+
spinner?.stop(`${label} \u2014 ${color2.red("failed")}`, 1);
|
|
4652
4685
|
if (!spinner) item("fail", `${label} \u2014 failed`);
|
|
4653
4686
|
if (result.message) detail(result.message);
|
|
4654
4687
|
for (const step of result.steps) {
|
|
@@ -4664,18 +4697,18 @@ async function driveAgents(entries, options) {
|
|
|
4664
4697
|
function reportOutcome(outcome, dryRun) {
|
|
4665
4698
|
line("");
|
|
4666
4699
|
if (dryRun) {
|
|
4667
|
-
line(` ${
|
|
4700
|
+
line(` ${color2.cyan("dry run")} \u2014 nothing was changed. Re-run without --dry-run to apply.`);
|
|
4668
4701
|
return 0;
|
|
4669
4702
|
}
|
|
4670
4703
|
if (outcome.failed > 0) {
|
|
4671
|
-
line(` ${
|
|
4704
|
+
line(` ${color2.red(`${outcome.failed} agent(s) failed`)}, ${outcome.changed} updated. Run ${color2.cyan("caddis doctor")} for detail.`);
|
|
4672
4705
|
return 1;
|
|
4673
4706
|
}
|
|
4674
4707
|
if (outcome.changed === 0) {
|
|
4675
|
-
line(` ${
|
|
4708
|
+
line(` ${color2.green("Nothing to do")} \u2014 everything already current.`);
|
|
4676
4709
|
return 0;
|
|
4677
4710
|
}
|
|
4678
|
-
line(` ${
|
|
4711
|
+
line(` ${color2.green(`${outcome.changed} agent(s) updated.`)} Run ${color2.cyan("caddis status")} to confirm.`);
|
|
4679
4712
|
return 0;
|
|
4680
4713
|
}
|
|
4681
4714
|
var init_drive = __esm({
|
|
@@ -4793,35 +4826,35 @@ async function init(options) {
|
|
|
4793
4826
|
const report = await gather(options.adapters);
|
|
4794
4827
|
const interactive = !options.yes && !options.dryRun && process.stdout.isTTY === true;
|
|
4795
4828
|
renderBanner({ cliVersion: report.cliVersion, poolVersion: report.poolVersion });
|
|
4796
|
-
Ie(`${
|
|
4829
|
+
Ie(`${color2.bold("caddis installer")} ${color2.dim(`\xB7 pool ${report.poolVersion}`)}`);
|
|
4797
4830
|
heading("Detected Agents");
|
|
4798
4831
|
for (const entry of report.agents) {
|
|
4799
4832
|
if (!entry.detection.present) {
|
|
4800
|
-
item("skip", `${entry.adapter.name} \u2014 ${
|
|
4833
|
+
item("skip", `${entry.adapter.name} \u2014 ${color2.dim("not found")}`);
|
|
4801
4834
|
continue;
|
|
4802
4835
|
}
|
|
4803
4836
|
if (!entry.adapter.supported) {
|
|
4804
|
-
item("info", `${entry.adapter.name} \u2014 ${
|
|
4837
|
+
item("info", `${entry.adapter.name} \u2014 ${color2.dim("detected, not yet supported (v0.2)")}`);
|
|
4805
4838
|
continue;
|
|
4806
4839
|
}
|
|
4807
4840
|
const current = entry.drift === "current";
|
|
4808
4841
|
item(
|
|
4809
4842
|
current ? "ok" : "warn",
|
|
4810
|
-
`${entry.adapter.name} \u2014 ${current ? `caddis ${entry.status.version} ${
|
|
4843
|
+
`${entry.adapter.name} \u2014 ${current ? `caddis ${entry.status.version} ${color2.dim("(current)")}` : entry.status.installed ? `caddis ${entry.status.version ?? "?"} \u2192 ${color2.bold(report.poolVersion)}` : color2.amber("caddis not installed")}`
|
|
4811
4844
|
);
|
|
4812
4845
|
}
|
|
4813
4846
|
const targets = actionable(report, { includeCurrent: true });
|
|
4814
4847
|
if (targets.length === 0) {
|
|
4815
4848
|
Se(
|
|
4816
|
-
`${
|
|
4849
|
+
`${color2.yellow("No supported agent found.")} caddis v0.1 drives Claude Code and agy \u2014 install one, then re-run.`
|
|
4817
4850
|
);
|
|
4818
4851
|
return 0;
|
|
4819
4852
|
}
|
|
4820
4853
|
line("");
|
|
4821
4854
|
heading("Planned Installations");
|
|
4822
4855
|
for (const entry of targets) {
|
|
4823
|
-
line(` ${
|
|
4824
|
-
line(` ${
|
|
4856
|
+
line(` ${color2.mint("\u2022")} ${color2.bold(entry.adapter.name)}`);
|
|
4857
|
+
line(` ${color2.dim("\u2192")} ${entry.adapter.summary}`);
|
|
4825
4858
|
}
|
|
4826
4859
|
line("");
|
|
4827
4860
|
if (interactive) {
|
|
@@ -4843,11 +4876,11 @@ async function init(options) {
|
|
|
4843
4876
|
const code = reportOutcome(outcome, options.dryRun);
|
|
4844
4877
|
if (code === 0 && !options.dryRun) {
|
|
4845
4878
|
line("");
|
|
4846
|
-
line(` ${
|
|
4847
|
-
line(` ${
|
|
4879
|
+
line(` ${color2.mint("\u2713")} ${color2.bold("Caddis dev harness is ready.")} Open any configured agent above to start.`);
|
|
4880
|
+
line(` ${color2.dim("Run")} ${color2.sky("caddis doctor")} ${color2.dim("at any time to verify agent health and updates.")}`);
|
|
4848
4881
|
line("");
|
|
4849
4882
|
}
|
|
4850
|
-
Se(code === 0 ?
|
|
4883
|
+
Se(code === 0 ? color2.green("Setup complete.") : color2.red("Finished with errors."));
|
|
4851
4884
|
return code;
|
|
4852
4885
|
}
|
|
4853
4886
|
var init_init = __esm({
|
|
@@ -4874,10 +4907,10 @@ function keysFilePath(env) {
|
|
|
4874
4907
|
const expanded = expandHome(configured);
|
|
4875
4908
|
return isAbsolute(expanded) ? expanded : expanded;
|
|
4876
4909
|
}
|
|
4877
|
-
function parseKeysFile(
|
|
4910
|
+
function parseKeysFile(path20) {
|
|
4878
4911
|
let text;
|
|
4879
4912
|
try {
|
|
4880
|
-
text = readFileSync3(
|
|
4913
|
+
text = readFileSync3(path20, "utf8");
|
|
4881
4914
|
} catch {
|
|
4882
4915
|
return {};
|
|
4883
4916
|
}
|
|
@@ -4989,18 +5022,18 @@ function upsertKeyLines(existing, updates) {
|
|
|
4989
5022
|
if (!text.endsWith("\n")) text += "\n";
|
|
4990
5023
|
return text;
|
|
4991
5024
|
}
|
|
4992
|
-
function restrictPermissions(
|
|
5025
|
+
function restrictPermissions(path20) {
|
|
4993
5026
|
if (process.platform === "win32") return null;
|
|
4994
5027
|
try {
|
|
4995
|
-
chmodSync(
|
|
5028
|
+
chmodSync(path20, 384);
|
|
4996
5029
|
return null;
|
|
4997
5030
|
} catch (error) {
|
|
4998
5031
|
return error instanceof Error ? error.message : String(error);
|
|
4999
5032
|
}
|
|
5000
5033
|
}
|
|
5001
5034
|
async function keys(options = {}) {
|
|
5002
|
-
const
|
|
5003
|
-
const fileKeys = parseKeysFile(
|
|
5035
|
+
const path20 = keysFilePath(process.env);
|
|
5036
|
+
const fileKeys = parseKeysFile(path20);
|
|
5004
5037
|
const names = Object.keys(PROVIDERS).sort();
|
|
5005
5038
|
const current = names.map((name) => {
|
|
5006
5039
|
const preset = PROVIDERS[name];
|
|
@@ -5016,14 +5049,14 @@ async function keys(options = {}) {
|
|
|
5016
5049
|
if (options.check) {
|
|
5017
5050
|
const configured = current.filter((entry) => entry.value);
|
|
5018
5051
|
if (!configured.length) {
|
|
5019
|
-
process.stdout.write(`no provider keys configured (looked in $ENV and ${
|
|
5052
|
+
process.stdout.write(`no provider keys configured (looked in $ENV and ${path20})
|
|
5020
5053
|
`);
|
|
5021
5054
|
return 1;
|
|
5022
5055
|
}
|
|
5023
5056
|
let bad = 0;
|
|
5024
5057
|
for (const entry of configured) {
|
|
5025
5058
|
const probe = await probeKey(entry.preset.baseUrl, entry.preset.model, entry.value);
|
|
5026
|
-
const label = probe.ok ?
|
|
5059
|
+
const label = probe.ok ? color2.green("ok") : probe.rejected ? color2.red("REJECTED") : color2.yellow("unverified");
|
|
5027
5060
|
process.stdout.write(` ${entry.name.padEnd(12)} ${label} ${probe.detail}
|
|
5028
5061
|
`);
|
|
5029
5062
|
if (probe.rejected) bad += 1;
|
|
@@ -5031,11 +5064,11 @@ async function keys(options = {}) {
|
|
|
5031
5064
|
return bad ? 1 : 0;
|
|
5032
5065
|
}
|
|
5033
5066
|
Ie("caddis keys");
|
|
5034
|
-
process.stdout.write(` keys file: ${
|
|
5067
|
+
process.stdout.write(` keys file: ${path20}
|
|
5035
5068
|
|
|
5036
5069
|
`);
|
|
5037
5070
|
for (const entry of current) {
|
|
5038
|
-
const state = entry.source ? `${
|
|
5071
|
+
const state = entry.source ? `${color2.green("present")} (${entry.source}) ${color2.dim(maskKey(entry.value))}` : color2.dim("missing");
|
|
5039
5072
|
process.stdout.write(` ${entry.name.padEnd(12)} ${state}
|
|
5040
5073
|
`);
|
|
5041
5074
|
}
|
|
@@ -5044,7 +5077,7 @@ async function keys(options = {}) {
|
|
|
5044
5077
|
for (const entry of current) {
|
|
5045
5078
|
if (entry.source === "env") {
|
|
5046
5079
|
process.stdout.write(
|
|
5047
|
-
|
|
5080
|
+
color2.dim(` ${entry.name}: set via $${entry.preset.keyEnv}; the environment wins, skipping
|
|
5048
5081
|
`)
|
|
5049
5082
|
);
|
|
5050
5083
|
continue;
|
|
@@ -5073,15 +5106,15 @@ async function keys(options = {}) {
|
|
|
5073
5106
|
if (!value) continue;
|
|
5074
5107
|
const probe = await probeKey(entry.preset.baseUrl, entry.preset.model, value);
|
|
5075
5108
|
if (probe.rejected) {
|
|
5076
|
-
process.stdout.write(
|
|
5109
|
+
process.stdout.write(color2.red(` ${entry.name}: ${probe.detail} \u2014 not saved
|
|
5077
5110
|
`));
|
|
5078
5111
|
continue;
|
|
5079
5112
|
}
|
|
5080
5113
|
if (!probe.ok) {
|
|
5081
|
-
process.stdout.write(
|
|
5114
|
+
process.stdout.write(color2.yellow(` ${entry.name}: ${probe.detail} \u2014 saving anyway
|
|
5082
5115
|
`));
|
|
5083
5116
|
} else {
|
|
5084
|
-
process.stdout.write(
|
|
5117
|
+
process.stdout.write(color2.green(` ${entry.name}: validated
|
|
5085
5118
|
`));
|
|
5086
5119
|
}
|
|
5087
5120
|
collected[entry.preset.keyEnv] = value;
|
|
@@ -5091,22 +5124,22 @@ async function keys(options = {}) {
|
|
|
5091
5124
|
return 0;
|
|
5092
5125
|
}
|
|
5093
5126
|
if (options.dryRun) {
|
|
5094
|
-
Se(`dry run \u2014 would write ${Object.keys(collected).join(", ")} to ${
|
|
5127
|
+
Se(`dry run \u2014 would write ${Object.keys(collected).join(", ")} to ${path20}`);
|
|
5095
5128
|
return 0;
|
|
5096
5129
|
}
|
|
5097
5130
|
let existing = "";
|
|
5098
5131
|
try {
|
|
5099
|
-
existing = readFileSync4(
|
|
5132
|
+
existing = readFileSync4(path20, "utf8");
|
|
5100
5133
|
} catch {
|
|
5101
5134
|
existing = "";
|
|
5102
5135
|
}
|
|
5103
|
-
mkdirSync2(dirname(
|
|
5104
|
-
writeFileSync(
|
|
5105
|
-
const permWarning = restrictPermissions(
|
|
5136
|
+
mkdirSync2(dirname(path20), { recursive: true });
|
|
5137
|
+
writeFileSync(path20, upsertKeyLines(existing, collected), { encoding: "utf8" });
|
|
5138
|
+
const permWarning = restrictPermissions(path20);
|
|
5106
5139
|
process.stdout.write(`
|
|
5107
|
-
wrote ${Object.keys(collected).join(", ")} to ${
|
|
5140
|
+
wrote ${Object.keys(collected).join(", ")} to ${path20}
|
|
5108
5141
|
`);
|
|
5109
|
-
if (permWarning) process.stdout.write(
|
|
5142
|
+
if (permWarning) process.stdout.write(color2.yellow(` could not set 0600 on the keys file: ${permWarning}
|
|
5110
5143
|
`));
|
|
5111
5144
|
Se("lanes ready \u2014 try `claude-glm --print-config`");
|
|
5112
5145
|
return 0;
|
|
@@ -5127,7 +5160,7 @@ __export(update_exports, {
|
|
|
5127
5160
|
});
|
|
5128
5161
|
async function update(options) {
|
|
5129
5162
|
const report = await gather(options.adapters);
|
|
5130
|
-
heading(`caddis update \u2192 pool ${
|
|
5163
|
+
heading(`caddis update \u2192 pool ${color2.bold(report.poolVersion)}`);
|
|
5131
5164
|
for (const entry of report.agents) {
|
|
5132
5165
|
if (!entry.detection.present) {
|
|
5133
5166
|
item("skip", `${entry.adapter.name} \u2014 not installed`);
|
|
@@ -5144,14 +5177,14 @@ async function update(options) {
|
|
|
5144
5177
|
const ahead = report.agents.filter((e2) => e2.drift === "ahead");
|
|
5145
5178
|
line("");
|
|
5146
5179
|
if (supportedPresent.length === 0) {
|
|
5147
|
-
line(` ${
|
|
5180
|
+
line(` ${color2.yellow("No supported agent found.")} caddis v0.1 drives Claude Code and agy.`);
|
|
5148
5181
|
} else if (ahead.length > 0) {
|
|
5149
5182
|
line(
|
|
5150
|
-
` ${
|
|
5183
|
+
` ${color2.green("Nothing to do.")} ${ahead.length} agent(s) are NEWER than this package's bundled pool (${report.poolVersion}) \u2014 driving them would be a DOWNGRADE, so they were left alone.`
|
|
5151
5184
|
);
|
|
5152
|
-
line(` Update the CLI itself (${
|
|
5185
|
+
line(` Update the CLI itself (${color2.cyan("npm i -g @caddis/cli")}), or ${color2.cyan("--force")} to roll back deliberately.`);
|
|
5153
5186
|
} else {
|
|
5154
|
-
line(` ${
|
|
5187
|
+
line(` ${color2.green("Everything is already current.")} Use --force to re-drive anyway.`);
|
|
5155
5188
|
}
|
|
5156
5189
|
return 0;
|
|
5157
5190
|
}
|
|
@@ -5284,7 +5317,7 @@ function renderEnvironment(report, problems) {
|
|
|
5284
5317
|
}
|
|
5285
5318
|
item("info", `platform ${process2.platform} ${process2.arch} (${os4.release()})`);
|
|
5286
5319
|
if (report.cliUpdate) {
|
|
5287
|
-
item("warn", `@caddis/cli ${report.cliVersion} ${
|
|
5320
|
+
item("warn", `@caddis/cli ${report.cliVersion} ${color2.dim("\u2192")} ${color2.bold(report.cliUpdate.latest)} available`);
|
|
5288
5321
|
detail('so EVERY "current" below may be wrong \u2014 it only means "matches the pool this install carries"');
|
|
5289
5322
|
problems.push({
|
|
5290
5323
|
kind: "problem",
|
|
@@ -5300,7 +5333,7 @@ function renderEnvironment(report, problems) {
|
|
|
5300
5333
|
detail(`no readable bundles/ in ${packageInfo().root}`);
|
|
5301
5334
|
problems.push({ kind: "problem", text: "this package has no shipped bundles", fix: "reinstall: npm i -g @caddis/cli" });
|
|
5302
5335
|
} else {
|
|
5303
|
-
item("ok", `shipped pool ${
|
|
5336
|
+
item("ok", `shipped pool ${color2.bold(report.poolVersion)}`);
|
|
5304
5337
|
for (const [name, version] of Object.entries(manifest.bundles)) {
|
|
5305
5338
|
detail(`bundle ${name} @ ${version}`);
|
|
5306
5339
|
}
|
|
@@ -5315,7 +5348,7 @@ function renderAgents(report, problems) {
|
|
|
5315
5348
|
const entry = report.agents[i];
|
|
5316
5349
|
const { adapter, detection, status: status5 } = entry;
|
|
5317
5350
|
if (i > 0) line("");
|
|
5318
|
-
item(markFor(entry.drift), `${
|
|
5351
|
+
item(markFor(entry.drift), `${color2.bold(adapter.name)} \u2014 ${describe(entry, report.poolVersion)}`);
|
|
5319
5352
|
if (detection.path) detail(detection.path);
|
|
5320
5353
|
if (detection.agentVersion) detail(`agent version ${detection.agentVersion}`);
|
|
5321
5354
|
if (status5.source) detail(`read from ${status5.source}`);
|
|
@@ -5325,12 +5358,12 @@ function renderAgents(report, problems) {
|
|
|
5325
5358
|
const problem = problemFor(entry, report.poolVersion);
|
|
5326
5359
|
if (problem) {
|
|
5327
5360
|
problems.push(problem);
|
|
5328
|
-
if (problem.fix) hint(
|
|
5361
|
+
if (problem.fix) hint(color2.bold(problem.fix));
|
|
5329
5362
|
}
|
|
5330
5363
|
const extrasProblem = extrasProblemFor(entry, report.extrasVersion);
|
|
5331
5364
|
if (extrasProblem) {
|
|
5332
5365
|
problems.push(extrasProblem);
|
|
5333
|
-
if (extrasProblem.fix) hint(
|
|
5366
|
+
if (extrasProblem.fix) hint(color2.bold(extrasProblem.fix));
|
|
5334
5367
|
}
|
|
5335
5368
|
}
|
|
5336
5369
|
}
|
|
@@ -5352,11 +5385,11 @@ function renderSessions(older, problems) {
|
|
|
5352
5385
|
const started = Number.isNaN(date.getTime()) ? "unknown" : `${date.toISOString().replace("T", " ").slice(0, 16)} UTC`;
|
|
5353
5386
|
item(
|
|
5354
5387
|
"warn",
|
|
5355
|
-
`${session.harness} session ${
|
|
5388
|
+
`${session.harness} session ${color2.bold(session.sessionId)} (pid ${session.pid}, started ${started}) runs caddis ${color2.yellow(session.caddisVersion ?? "?")}, installed ${color2.bold(session.installed)}`
|
|
5356
5389
|
);
|
|
5357
5390
|
}
|
|
5358
5391
|
const fix = "restart these sessions (or run /reload-plugins in each) to load the installed caddis";
|
|
5359
|
-
hint(
|
|
5392
|
+
hint(color2.bold(fix));
|
|
5360
5393
|
problems.push({
|
|
5361
5394
|
kind: "problem",
|
|
5362
5395
|
text: `${older.length} live session${older.length === 1 ? "" : "s"} still run${older.length === 1 ? "s" : ""} an older caddis`,
|
|
@@ -5428,19 +5461,19 @@ function renderSummary(report, findings) {
|
|
|
5428
5461
|
const notes = findings.filter((f) => f.kind === "note");
|
|
5429
5462
|
if (problems.length === 0) {
|
|
5430
5463
|
line(`
|
|
5431
|
-
${
|
|
5464
|
+
${color2.mint("Everything caddis manages is current.")}`);
|
|
5432
5465
|
} else {
|
|
5433
5466
|
line(`
|
|
5434
|
-
${
|
|
5467
|
+
${color2.amber(color2.bold(`${problems.length} thing${problems.length === 1 ? "" : "s"} to fix:`))}`);
|
|
5435
5468
|
for (const problem of problems) {
|
|
5436
|
-
line(` ${
|
|
5437
|
-
if (problem.fix) line(` ${
|
|
5469
|
+
line(` ${color2.dim("\u2022")} ${problem.text}`);
|
|
5470
|
+
if (problem.fix) line(` ${color2.sky(problem.fix)}`);
|
|
5438
5471
|
}
|
|
5439
5472
|
}
|
|
5440
5473
|
if (notes.length > 0) {
|
|
5441
5474
|
line(`
|
|
5442
|
-
${
|
|
5443
|
-
for (const note of notes) line(` ${
|
|
5475
|
+
${color2.dim("For your information:")}`);
|
|
5476
|
+
for (const note of notes) line(` ${color2.dim(`\u2022 ${note.text}`)}`);
|
|
5444
5477
|
}
|
|
5445
5478
|
line("");
|
|
5446
5479
|
}
|
|
@@ -5462,19 +5495,19 @@ function markFor(drift) {
|
|
|
5462
5495
|
function describe(entry, poolVersion) {
|
|
5463
5496
|
switch (entry.drift) {
|
|
5464
5497
|
case "current":
|
|
5465
|
-
return `caddis ${entry.status.version} ${
|
|
5498
|
+
return `caddis ${entry.status.version} ${color2.dim("(current)")}`;
|
|
5466
5499
|
case "ahead":
|
|
5467
|
-
return `caddis ${
|
|
5500
|
+
return `caddis ${color2.green(entry.status.version ?? "?")} ${color2.dim("\u2014 newer than this CLI bundles")} ${color2.dim(`(${poolVersion}); left alone, update @caddis/cli to catch up`)}`;
|
|
5468
5501
|
case "stale":
|
|
5469
|
-
return `caddis ${
|
|
5502
|
+
return `caddis ${color2.yellow(entry.status.version ?? "?")} ${color2.dim("\u2192")} ${color2.bold(poolVersion)} available`;
|
|
5470
5503
|
case "unknown":
|
|
5471
5504
|
return "caddis installed, version not reported";
|
|
5472
5505
|
case "missing":
|
|
5473
|
-
return
|
|
5506
|
+
return color2.yellow("caddis not installed");
|
|
5474
5507
|
case "unsupported":
|
|
5475
|
-
return
|
|
5508
|
+
return color2.dim("detected \u2014 not yet supported (v0.2)");
|
|
5476
5509
|
case "absent":
|
|
5477
|
-
return
|
|
5510
|
+
return color2.dim("not installed on this machine");
|
|
5478
5511
|
}
|
|
5479
5512
|
}
|
|
5480
5513
|
function describeExtras(entry, extrasVersion) {
|
|
@@ -5635,7 +5668,7 @@ async function status4(options) {
|
|
|
5635
5668
|
// "stays quiet when everything is current" test — correctly: a qualifier phrased like a warning
|
|
5636
5669
|
// fires on every healthy run, and a warning that always fires is the one nobody reads. doctor
|
|
5637
5670
|
// carries the strong version, because doctor is where the network check actually happens.
|
|
5638
|
-
` ${
|
|
5671
|
+
` ${color2.bold("caddis")} cli ${color2.bold(report.cliVersion)} \xB7 pool ${color2.bold(report.poolVersion)} ${color2.dim('(what "current" is measured against)')}` + (report.extrasVersion ? ` \xB7 extras ${color2.bold(report.extrasVersion)}` : "")
|
|
5639
5672
|
);
|
|
5640
5673
|
line("");
|
|
5641
5674
|
line(renderTable(["AGENT", "DETECTED", "CADDIS", "EXTRAS", "STATE"], report.agents.map(toRow)));
|
|
@@ -5645,12 +5678,12 @@ async function status4(options) {
|
|
|
5645
5678
|
);
|
|
5646
5679
|
const ahead = report.agents.filter((entry) => entry.drift === "ahead");
|
|
5647
5680
|
if (stale.length > 0) {
|
|
5648
|
-
line(` ${
|
|
5681
|
+
line(` ${color2.yellow(`${stale.length} agent(s) behind`)} \u2014 run ${color2.cyan("caddis update")} (or ${color2.cyan("caddis doctor")} for detail)`);
|
|
5649
5682
|
line("");
|
|
5650
5683
|
}
|
|
5651
5684
|
if (ahead.length > 0) {
|
|
5652
5685
|
line(
|
|
5653
|
-
` ${ahead.length} agent(s) are NEWER than this CLI's bundled pool (${report.poolVersion}) \u2014 left alone. Update the CLI itself (${
|
|
5686
|
+
` ${ahead.length} agent(s) are NEWER than this CLI's bundled pool (${report.poolVersion}) \u2014 left alone. Update the CLI itself (${color2.cyan("npm i -g @caddis/cli")}) to catch up.`
|
|
5654
5687
|
);
|
|
5655
5688
|
line("");
|
|
5656
5689
|
}
|
|
@@ -5660,30 +5693,30 @@ function toRow(entry) {
|
|
|
5660
5693
|
const managed = entry.drift !== "unsupported" && entry.drift !== "absent";
|
|
5661
5694
|
return [
|
|
5662
5695
|
entry.adapter.name,
|
|
5663
|
-
entry.detection.present ?
|
|
5664
|
-
managed && entry.status.version ||
|
|
5696
|
+
entry.detection.present ? color2.green("yes") : color2.dim("no"),
|
|
5697
|
+
managed && entry.status.version || color2.dim("\u2014"),
|
|
5665
5698
|
// Extras gets its own column rather than being folded into CADDIS: it
|
|
5666
5699
|
// versions independently, so showing one number for both would be a lie.
|
|
5667
|
-
managed && entry.status.extras?.version ||
|
|
5700
|
+
managed && entry.status.extras?.version || color2.dim("\u2014"),
|
|
5668
5701
|
stateLabel(entry.drift, entry.status.disabled ?? false)
|
|
5669
5702
|
];
|
|
5670
5703
|
}
|
|
5671
5704
|
function stateLabel(drift, disabled) {
|
|
5672
5705
|
switch (drift) {
|
|
5673
5706
|
case "current":
|
|
5674
|
-
return disabled ?
|
|
5707
|
+
return disabled ? color2.yellow("current, disabled") : color2.green("current");
|
|
5675
5708
|
case "stale":
|
|
5676
|
-
return
|
|
5709
|
+
return color2.yellow("update available");
|
|
5677
5710
|
case "ahead":
|
|
5678
|
-
return
|
|
5711
|
+
return color2.green("newer than CLI pool");
|
|
5679
5712
|
case "unknown":
|
|
5680
|
-
return
|
|
5713
|
+
return color2.yellow("version unknown");
|
|
5681
5714
|
case "missing":
|
|
5682
|
-
return
|
|
5715
|
+
return color2.yellow("not installed");
|
|
5683
5716
|
case "unsupported":
|
|
5684
|
-
return
|
|
5717
|
+
return color2.dim("v0.2");
|
|
5685
5718
|
case "absent":
|
|
5686
|
-
return
|
|
5719
|
+
return color2.dim("\u2014");
|
|
5687
5720
|
}
|
|
5688
5721
|
}
|
|
5689
5722
|
function toJson2(report) {
|
|
@@ -6371,6 +6404,28 @@ var init_auth = __esm({
|
|
|
6371
6404
|
|
|
6372
6405
|
// src/hub/server.ts
|
|
6373
6406
|
import http from "http";
|
|
6407
|
+
import { existsSync as existsSync7 } from "fs";
|
|
6408
|
+
import { readFile, stat } from "fs/promises";
|
|
6409
|
+
import path13 from "path";
|
|
6410
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6411
|
+
function defaultUiDir() {
|
|
6412
|
+
const here = path13.dirname(fileURLToPath2(import.meta.url));
|
|
6413
|
+
const beside = path13.join(here, "ui");
|
|
6414
|
+
return existsSync7(beside) ? beside : path13.resolve(here, "..", "..", "dist", "ui");
|
|
6415
|
+
}
|
|
6416
|
+
function resolveStatic(root, urlPath) {
|
|
6417
|
+
let decoded;
|
|
6418
|
+
try {
|
|
6419
|
+
decoded = decodeURIComponent(urlPath);
|
|
6420
|
+
} catch {
|
|
6421
|
+
return null;
|
|
6422
|
+
}
|
|
6423
|
+
if (decoded.includes(String.fromCharCode(0)) || decoded.includes(String.fromCharCode(92))) return null;
|
|
6424
|
+
if (decoded.split("/").includes("..")) return null;
|
|
6425
|
+
const base = path13.resolve(root);
|
|
6426
|
+
const file = path13.resolve(base, "." + decoded);
|
|
6427
|
+
return file.startsWith(base + path13.sep) ? file : null;
|
|
6428
|
+
}
|
|
6374
6429
|
function send(res, status5, body, extra = {}) {
|
|
6375
6430
|
const text = JSON.stringify(body);
|
|
6376
6431
|
res.writeHead(status5, {
|
|
@@ -6412,6 +6467,33 @@ function tooLarge(req, res) {
|
|
|
6412
6467
|
res.on("finish", () => req.socket.destroy());
|
|
6413
6468
|
fail(res, 413, "body_too_large");
|
|
6414
6469
|
}
|
|
6470
|
+
async function serveStatic(res, uiDir, urlPath) {
|
|
6471
|
+
const rel = urlPath === "/" ? "/index.html" : urlPath;
|
|
6472
|
+
const file = resolveStatic(uiDir, rel);
|
|
6473
|
+
if (!file) return fail(res, 404, "not_found");
|
|
6474
|
+
let body;
|
|
6475
|
+
try {
|
|
6476
|
+
if (!(await stat(file)).isFile()) return fail(res, 404, "not_found");
|
|
6477
|
+
body = await readFile(file);
|
|
6478
|
+
} catch {
|
|
6479
|
+
if (urlPath === "/") {
|
|
6480
|
+
res.writeHead(503, {
|
|
6481
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
6482
|
+
"Content-Length": Buffer.byteLength(NOT_BUILT_HTML),
|
|
6483
|
+
"Cache-Control": "no-store"
|
|
6484
|
+
});
|
|
6485
|
+
res.end(NOT_BUILT_HTML);
|
|
6486
|
+
return;
|
|
6487
|
+
}
|
|
6488
|
+
return fail(res, 404, "not_found");
|
|
6489
|
+
}
|
|
6490
|
+
res.writeHead(200, {
|
|
6491
|
+
"Content-Type": MIME[path13.extname(file).toLowerCase()] ?? "application/octet-stream",
|
|
6492
|
+
"Content-Length": body.length,
|
|
6493
|
+
"Cache-Control": urlPath === "/" ? "no-store" : "public, max-age=31536000, immutable"
|
|
6494
|
+
});
|
|
6495
|
+
res.end(body);
|
|
6496
|
+
}
|
|
6415
6497
|
function keyOk(req, key) {
|
|
6416
6498
|
const v2 = req.headers[KEY_HEADER];
|
|
6417
6499
|
return typeof v2 === "string" && safeEqual(v2, key);
|
|
@@ -6424,6 +6506,7 @@ async function startServer(opts) {
|
|
|
6424
6506
|
}
|
|
6425
6507
|
const auth = new AuthStore(opts.now ?? Date.now);
|
|
6426
6508
|
const { key, versions } = opts;
|
|
6509
|
+
const uiDir = opts.uiDir ?? defaultUiDir();
|
|
6427
6510
|
let port = opts.port;
|
|
6428
6511
|
const sessionOf = (req) => auth.getSession(parseCookies(req.headers.cookie)[SESSION_COOKIE]);
|
|
6429
6512
|
const tabSessionOf = (req) => {
|
|
@@ -6441,23 +6524,23 @@ async function startServer(opts) {
|
|
|
6441
6524
|
fail(res, 400, "bad_url");
|
|
6442
6525
|
return;
|
|
6443
6526
|
}
|
|
6444
|
-
const
|
|
6527
|
+
const path20 = url.pathname;
|
|
6445
6528
|
const method = req.method ?? "GET";
|
|
6446
|
-
if (
|
|
6529
|
+
if (path20.startsWith("/api/") || path20 === "/login") res.setHeader("Cache-Control", "no-store");
|
|
6447
6530
|
if (!hostAllowed(req.headers.host, port)) {
|
|
6448
|
-
if (
|
|
6531
|
+
if (path20 === "/login") auth.discardCode(url.searchParams.get("code") ?? "");
|
|
6449
6532
|
fail(res, 403, "bad_host");
|
|
6450
6533
|
return;
|
|
6451
6534
|
}
|
|
6452
|
-
if (
|
|
6535
|
+
if (path20 === "/api/hub/whoami" && method === "GET") {
|
|
6453
6536
|
if (!opts.instance) return fail(res, 404, "not_found");
|
|
6454
6537
|
return send(res, 200, { instance: opts.instance });
|
|
6455
6538
|
}
|
|
6456
|
-
if (
|
|
6539
|
+
if (path20 === "/api/health" && method === "GET") {
|
|
6457
6540
|
if (!keyOk(req, key)) return fail(res, 401, "unauthorized");
|
|
6458
6541
|
return send(res, 200, { schema: 1, ok: true, versions });
|
|
6459
6542
|
}
|
|
6460
|
-
if (
|
|
6543
|
+
if (path20 === "/api/hub/code" && method === "POST") {
|
|
6461
6544
|
if (!keyOk(req, key)) return fail(res, 401, "unauthorized");
|
|
6462
6545
|
try {
|
|
6463
6546
|
await readBody(req);
|
|
@@ -6468,7 +6551,7 @@ async function startServer(opts) {
|
|
|
6468
6551
|
const code = auth.mintCode();
|
|
6469
6552
|
return send(res, 200, { schema: 1, code, expires_at: auth.codeExpiresAt() });
|
|
6470
6553
|
}
|
|
6471
|
-
if (
|
|
6554
|
+
if (path20 === "/api/hub/stop" && method === "POST") {
|
|
6472
6555
|
if (!keyOk(req, key)) return fail(res, 401, "unauthorized");
|
|
6473
6556
|
try {
|
|
6474
6557
|
await readBody(req);
|
|
@@ -6492,7 +6575,7 @@ async function startServer(opts) {
|
|
|
6492
6575
|
if (e2 instanceof BodyTooLarge) return tooLarge(req, res);
|
|
6493
6576
|
throw e2;
|
|
6494
6577
|
}
|
|
6495
|
-
if (
|
|
6578
|
+
if (path20 === "/api/logout" && method === "POST") {
|
|
6496
6579
|
auth.deleteSession(session.id);
|
|
6497
6580
|
return send(res, 200, { schema: 1, ok: true }, {
|
|
6498
6581
|
"Set-Cookie": `${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`
|
|
@@ -6500,15 +6583,11 @@ async function startServer(opts) {
|
|
|
6500
6583
|
}
|
|
6501
6584
|
return fail(res, 404, "not_found");
|
|
6502
6585
|
}
|
|
6503
|
-
if (
|
|
6504
|
-
res
|
|
6505
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
6506
|
-
"Content-Length": Buffer.byteLength(PLACEHOLDER_HTML)
|
|
6507
|
-
});
|
|
6508
|
-
res.end(PLACEHOLDER_HTML);
|
|
6586
|
+
if (path20 === "/" || path20.startsWith("/assets/") || path20.startsWith("/fonts/")) {
|
|
6587
|
+
await serveStatic(res, uiDir, path20);
|
|
6509
6588
|
return;
|
|
6510
6589
|
}
|
|
6511
|
-
if (
|
|
6590
|
+
if (path20 === "/login") {
|
|
6512
6591
|
const id = auth.redeemCode(url.searchParams.get("code") ?? "");
|
|
6513
6592
|
const session = id ? auth.getSession(id) : null;
|
|
6514
6593
|
if (!id || !session) return fail(res, 401, "bad_code");
|
|
@@ -6520,12 +6599,12 @@ async function startServer(opts) {
|
|
|
6520
6599
|
res.end();
|
|
6521
6600
|
return;
|
|
6522
6601
|
}
|
|
6523
|
-
if (
|
|
6602
|
+
if (path20 === "/api/session") {
|
|
6524
6603
|
const session = tabSessionOf(req);
|
|
6525
6604
|
if (!session) return fail(res, 401, "unauthorized");
|
|
6526
6605
|
return send(res, 200, { schema: 1, csrf_token: session.csrf, versions });
|
|
6527
6606
|
}
|
|
6528
|
-
if (
|
|
6607
|
+
if (path20 === "/api/state") {
|
|
6529
6608
|
if (!tabSessionOf(req)) return fail(res, 401, "unauthorized");
|
|
6530
6609
|
let state;
|
|
6531
6610
|
try {
|
|
@@ -6564,7 +6643,7 @@ async function startServer(opts) {
|
|
|
6564
6643
|
})
|
|
6565
6644
|
};
|
|
6566
6645
|
}
|
|
6567
|
-
var SESSION_COOKIE, KEY_HEADER, CSRF_HEADER, TAB_HEADER, MAX_BODY_BYTES, MIN_KEY_BYTES, CSP,
|
|
6646
|
+
var SESSION_COOKIE, KEY_HEADER, CSRF_HEADER, TAB_HEADER, MAX_BODY_BYTES, MIN_KEY_BYTES, CSP, NOT_BUILT_HTML, MIME, BodyTooLarge, fail;
|
|
6568
6647
|
var init_server = __esm({
|
|
6569
6648
|
"src/hub/server.ts"() {
|
|
6570
6649
|
"use strict";
|
|
@@ -6576,7 +6655,20 @@ var init_server = __esm({
|
|
|
6576
6655
|
MAX_BODY_BYTES = 16 * 1024;
|
|
6577
6656
|
MIN_KEY_BYTES = 32;
|
|
6578
6657
|
CSP = "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'";
|
|
6579
|
-
|
|
6658
|
+
NOT_BUILT_HTML = '<!doctype html><html lang="en"><head><meta charset="utf-8"><title>caddis hub</title></head><body>UI not built: run npm run build</body></html>';
|
|
6659
|
+
MIME = {
|
|
6660
|
+
".html": "text/html; charset=utf-8",
|
|
6661
|
+
".js": "text/javascript; charset=utf-8",
|
|
6662
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
6663
|
+
".css": "text/css; charset=utf-8",
|
|
6664
|
+
".json": "application/json; charset=utf-8",
|
|
6665
|
+
".svg": "image/svg+xml",
|
|
6666
|
+
".png": "image/png",
|
|
6667
|
+
".ico": "image/x-icon",
|
|
6668
|
+
".woff2": "font/woff2",
|
|
6669
|
+
".txt": "text/plain; charset=utf-8",
|
|
6670
|
+
".map": "application/json; charset=utf-8"
|
|
6671
|
+
};
|
|
6580
6672
|
BodyTooLarge = class extends Error {
|
|
6581
6673
|
};
|
|
6582
6674
|
fail = (res, status5, error) => send(res, status5, { schema: 1, error });
|
|
@@ -6710,8 +6802,8 @@ var init_spawn = __esm({
|
|
|
6710
6802
|
});
|
|
6711
6803
|
|
|
6712
6804
|
// src/hub/sources/agents.ts
|
|
6713
|
-
import { existsSync as
|
|
6714
|
-
import
|
|
6805
|
+
import { existsSync as existsSync8, readFileSync as readFileSync10, statSync } from "fs";
|
|
6806
|
+
import path14 from "path";
|
|
6715
6807
|
function classifyWaiting(waitingFor) {
|
|
6716
6808
|
const w2 = (waitingFor ?? "").toLowerCase();
|
|
6717
6809
|
if (w2 === "permission prompt" || w2 === "input needed" || w2.includes("question")) return "owner";
|
|
@@ -6719,15 +6811,15 @@ function classifyWaiting(waitingFor) {
|
|
|
6719
6811
|
}
|
|
6720
6812
|
function repoPathFor(cwd) {
|
|
6721
6813
|
if (!cwd) return null;
|
|
6722
|
-
let dir =
|
|
6814
|
+
let dir = path14.resolve(cwd);
|
|
6723
6815
|
for (; ; ) {
|
|
6724
|
-
const dotGit =
|
|
6725
|
-
if (
|
|
6816
|
+
const dotGit = path14.join(dir, ".git");
|
|
6817
|
+
if (existsSync8(dotGit)) {
|
|
6726
6818
|
if (isDir(dotGit)) return normalizeRepoPath(dir);
|
|
6727
6819
|
if (isFile(dotGit)) return commonRoot(dir, dotGit);
|
|
6728
6820
|
return null;
|
|
6729
6821
|
}
|
|
6730
|
-
const parent =
|
|
6822
|
+
const parent = path14.dirname(dir);
|
|
6731
6823
|
if (parent === dir) return null;
|
|
6732
6824
|
dir = parent;
|
|
6733
6825
|
}
|
|
@@ -6736,16 +6828,16 @@ function commonRoot(dir, dotGitFile) {
|
|
|
6736
6828
|
try {
|
|
6737
6829
|
const m2 = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync10(dotGitFile, "utf8"));
|
|
6738
6830
|
if (!m2) return null;
|
|
6739
|
-
const gitdir =
|
|
6831
|
+
const gitdir = path14.resolve(dir, m2[1] ?? "");
|
|
6740
6832
|
let common;
|
|
6741
6833
|
try {
|
|
6742
|
-
const rel = readFileSync10(
|
|
6834
|
+
const rel = readFileSync10(path14.join(gitdir, "commondir"), "utf8").trim();
|
|
6743
6835
|
if (!rel) return normalizeRepoPath(dir);
|
|
6744
|
-
common =
|
|
6836
|
+
common = path14.resolve(gitdir, rel);
|
|
6745
6837
|
} catch {
|
|
6746
6838
|
return normalizeRepoPath(dir);
|
|
6747
6839
|
}
|
|
6748
|
-
return normalizeRepoPath(
|
|
6840
|
+
return normalizeRepoPath(path14.dirname(common));
|
|
6749
6841
|
} catch {
|
|
6750
6842
|
return null;
|
|
6751
6843
|
}
|
|
@@ -6883,17 +6975,17 @@ var init_agents = __esm({
|
|
|
6883
6975
|
});
|
|
6884
6976
|
|
|
6885
6977
|
// src/hub/sources/collector.ts
|
|
6886
|
-
import { existsSync as
|
|
6978
|
+
import { existsSync as existsSync9 } from "fs";
|
|
6887
6979
|
import os5 from "os";
|
|
6888
|
-
import
|
|
6980
|
+
import path15 from "path";
|
|
6889
6981
|
function defaultScriptPath() {
|
|
6890
6982
|
const name = "caddis_hub_collect.py";
|
|
6891
6983
|
const bundle = bundlePath("antigravity-plugin", { requireManifest: false });
|
|
6892
6984
|
if (bundle) {
|
|
6893
|
-
const shipped =
|
|
6894
|
-
if (
|
|
6985
|
+
const shipped = path15.join(bundle, "scripts", name);
|
|
6986
|
+
if (existsSync9(shipped)) return shipped;
|
|
6895
6987
|
}
|
|
6896
|
-
return
|
|
6988
|
+
return path15.join(packageInfo().root, "..", "scripts", name);
|
|
6897
6989
|
}
|
|
6898
6990
|
async function resolvePython() {
|
|
6899
6991
|
const py = await find("python");
|
|
@@ -6972,7 +7064,7 @@ function createCollectorSource(opts) {
|
|
|
6972
7064
|
pre = found.pre;
|
|
6973
7065
|
}
|
|
6974
7066
|
const script = opts.scriptPath ?? defaultScriptPath();
|
|
6975
|
-
const args = repos.length ? [...pre, script, "--repos", ...repos, "--json"] : [...pre, script, "--repos",
|
|
7067
|
+
const args = repos.length ? [...pre, script, "--repos", ...repos, "--json"] : [...pre, script, "--repos", path15.join(os5.tmpdir(), "caddis-hub-no-repos"), "--only", "spend,guide", "--json"];
|
|
6976
7068
|
const result = await spawn2(cmd, args, { timeoutMs: RUN_TIMEOUT_MS2 });
|
|
6977
7069
|
if (result.timedOut) return fail2(`collector timed out after ${RUN_TIMEOUT_MS2 / 1e3} s`);
|
|
6978
7070
|
if (!result.ok) {
|
|
@@ -7027,7 +7119,7 @@ var init_collector = __esm({
|
|
|
7027
7119
|
|
|
7028
7120
|
// src/hub/sources/coordinator.ts
|
|
7029
7121
|
import { readdirSync as readdirSync3, readFileSync as readFileSync11 } from "fs";
|
|
7030
|
-
import
|
|
7122
|
+
import path16 from "path";
|
|
7031
7123
|
function stub(repoId, schema) {
|
|
7032
7124
|
return {
|
|
7033
7125
|
repo_id: repoId,
|
|
@@ -7084,7 +7176,7 @@ function createCoordinatorSource(opts = {}) {
|
|
|
7084
7176
|
const errors = /* @__PURE__ */ new Map();
|
|
7085
7177
|
return {
|
|
7086
7178
|
refresh() {
|
|
7087
|
-
const root =
|
|
7179
|
+
const root = path16.join(hubHome(opts.home), "repos");
|
|
7088
7180
|
let ids = [];
|
|
7089
7181
|
try {
|
|
7090
7182
|
ids = readdirSync3(root, { withFileTypes: true }).filter((d3) => d3.isDirectory()).map((d3) => d3.name);
|
|
@@ -7096,7 +7188,7 @@ function createCoordinatorSource(opts = {}) {
|
|
|
7096
7188
|
for (const id of ids) {
|
|
7097
7189
|
let text;
|
|
7098
7190
|
try {
|
|
7099
|
-
text = readFileSync11(
|
|
7191
|
+
text = readFileSync11(path16.join(root, id, "coordinator.json"), "utf8");
|
|
7100
7192
|
} catch (e2) {
|
|
7101
7193
|
if (e2.code === "ENOENT") {
|
|
7102
7194
|
lastGood2.delete(id);
|
|
@@ -7149,7 +7241,7 @@ var init_coordinator = __esm({
|
|
|
7149
7241
|
|
|
7150
7242
|
// src/hub/sources/github.ts
|
|
7151
7243
|
import { readFileSync as readFileSync12 } from "fs";
|
|
7152
|
-
import
|
|
7244
|
+
import path17 from "path";
|
|
7153
7245
|
function parseGithubRemote(url) {
|
|
7154
7246
|
const m2 = /^(?:https?:\/\/(?:[^@/]+@)?|ssh:\/\/git@|git@)github\.com[/:]([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i.exec(url.trim());
|
|
7155
7247
|
return m2 ? `${m2[1]}/${m2[2]}` : null;
|
|
@@ -7159,7 +7251,7 @@ function readOriginUrl(repoDir) {
|
|
|
7159
7251
|
if (!root) return null;
|
|
7160
7252
|
let text;
|
|
7161
7253
|
try {
|
|
7162
|
-
text = readFileSync12(
|
|
7254
|
+
text = readFileSync12(path17.join(root, ".git", "config"), "utf8");
|
|
7163
7255
|
} catch {
|
|
7164
7256
|
return null;
|
|
7165
7257
|
}
|
|
@@ -7177,7 +7269,7 @@ function readOriginUrl(repoDir) {
|
|
|
7177
7269
|
}
|
|
7178
7270
|
function readRequiredChecks(repoDir) {
|
|
7179
7271
|
try {
|
|
7180
|
-
const text = readFileSync12(
|
|
7272
|
+
const text = readFileSync12(path17.join(repoDir, ".caddis", "coordinator.md"), "utf8");
|
|
7181
7273
|
const m2 = /```json[^\S\r\n]*\r?\n([\s\S]*?)\r?\n[^\S\r\n]*```/.exec(text);
|
|
7182
7274
|
if (!m2) return null;
|
|
7183
7275
|
const parsed = JSON.parse(m2[1] ?? "");
|
|
@@ -7524,18 +7616,18 @@ var init_github = __esm({
|
|
|
7524
7616
|
// src/hub/state.ts
|
|
7525
7617
|
import { createHash as createHash2 } from "crypto";
|
|
7526
7618
|
import { readFileSync as readFileSync13 } from "fs";
|
|
7527
|
-
import
|
|
7619
|
+
import path18 from "path";
|
|
7528
7620
|
function repoIdFor(nameWithOwner) {
|
|
7529
7621
|
return nameWithOwner.trim().toLowerCase().replace(/\//g, "__");
|
|
7530
7622
|
}
|
|
7531
7623
|
function localRepoId(repoPath) {
|
|
7532
7624
|
const norm = process.platform === "win32" ? repoPath.toLowerCase().replace(/\//g, "\\") : repoPath;
|
|
7533
7625
|
const digest = createHash2("sha256").update(norm, "utf8").digest("hex").slice(0, 6);
|
|
7534
|
-
return `local__${
|
|
7626
|
+
return `local__${path18.basename(repoPath)}__${digest}`;
|
|
7535
7627
|
}
|
|
7536
7628
|
function readPluginVersion(home) {
|
|
7537
7629
|
try {
|
|
7538
|
-
const file =
|
|
7630
|
+
const file = path18.join(home, ".claude", "plugins", "installed_plugins.json");
|
|
7539
7631
|
const parsed = JSON.parse(readFileSync13(file, "utf8"));
|
|
7540
7632
|
for (const [key, entries] of Object.entries(parsed.plugins ?? {})) {
|
|
7541
7633
|
if (!key.startsWith("caddis@") || !Array.isArray(entries)) continue;
|
|
@@ -7662,7 +7754,7 @@ function createHub(opts = {}) {
|
|
|
7662
7754
|
const repoId = g2?.repo ? repoIdFor(g2.repo) : g2?.reason === "no github remote" ? localRepoId(p2) : void 0;
|
|
7663
7755
|
const out = {
|
|
7664
7756
|
path: p2,
|
|
7665
|
-
name:
|
|
7757
|
+
name: path18.basename(p2),
|
|
7666
7758
|
pinned: pinned.has(k3),
|
|
7667
7759
|
roadmaps: c?.roadmaps ?? null,
|
|
7668
7760
|
plans: c?.plans ?? null,
|
|
@@ -7768,7 +7860,7 @@ __export(hub_exports, {
|
|
|
7768
7860
|
startHub: () => startHub
|
|
7769
7861
|
});
|
|
7770
7862
|
import http2 from "http";
|
|
7771
|
-
import
|
|
7863
|
+
import path19 from "path";
|
|
7772
7864
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync14 } from "fs";
|
|
7773
7865
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
7774
7866
|
function defaultVersions2() {
|
|
@@ -7854,7 +7946,7 @@ async function isNodeProcess(pid) {
|
|
|
7854
7946
|
if (!r2.ok) return false;
|
|
7855
7947
|
const name = (r2.stdout.split(/\r?\n/)[0] ?? "").replace(/^"([^"]*)".*$/, "$1").trim().toLowerCase();
|
|
7856
7948
|
if (!name) return false;
|
|
7857
|
-
const runtime =
|
|
7949
|
+
const runtime = path19.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
7858
7950
|
return name.includes("node") || name.includes(runtime);
|
|
7859
7951
|
}
|
|
7860
7952
|
async function defaultOpener(url) {
|
|
@@ -7984,7 +8076,7 @@ async function hubServe(deps = {}) {
|
|
|
7984
8076
|
process.once("exit", onExit);
|
|
7985
8077
|
line(`caddis hub serving on ${hubUrl(hub2.port)} (pid ${process.pid}); stop it with: caddis hub stop`);
|
|
7986
8078
|
const pkgRoot = packageInfo().root;
|
|
7987
|
-
const newEntry =
|
|
8079
|
+
const newEntry = path19.join(pkgRoot, "dist", "cli.js");
|
|
7988
8080
|
const runningVersion = (deps.versions ?? defaultVersions2()).cli ?? packageInfo().version;
|
|
7989
8081
|
const restarter = createRestarter({
|
|
7990
8082
|
currentVersion: runningVersion,
|
|
@@ -8324,14 +8416,14 @@ function* pluginBlocks(stdout) {
|
|
|
8324
8416
|
if (current) yield current;
|
|
8325
8417
|
}
|
|
8326
8418
|
async function detect() {
|
|
8327
|
-
const
|
|
8328
|
-
if (!
|
|
8419
|
+
const path20 = await findBin(BIN);
|
|
8420
|
+
if (!path20) {
|
|
8329
8421
|
return { present: false, note: "no `claude` binary on PATH" };
|
|
8330
8422
|
}
|
|
8331
8423
|
const version = await run(BIN, ["--version"], { timeout: 2e4 });
|
|
8332
8424
|
return {
|
|
8333
8425
|
present: true,
|
|
8334
|
-
path:
|
|
8426
|
+
path: path20,
|
|
8335
8427
|
agentVersion: version.ok ? version.stdout.trim().split(/\s+/)[0] || void 0 : void 0,
|
|
8336
8428
|
note: version.ok ? void 0 : "binary found but `claude --version` failed"
|
|
8337
8429
|
};
|
|
@@ -9184,7 +9276,7 @@ hub.command("autostart <action>").description("start the hub at logon: enable, d
|
|
|
9184
9276
|
program2.addHelpText(
|
|
9185
9277
|
"after",
|
|
9186
9278
|
`
|
|
9187
|
-
${
|
|
9279
|
+
${color2.bold("Examples")}
|
|
9188
9280
|
$ npx @caddis/cli init detect your agents and install caddis into each
|
|
9189
9281
|
$ caddis doctor what is installed where, and what to do about it
|
|
9190
9282
|
$ caddis update --dry-run preview the exact vendor commands
|
|
@@ -9192,7 +9284,7 @@ ${color.bold("Examples")}
|
|
|
9192
9284
|
$ caddis init --extras add the optional caddis-extras skill library
|
|
9193
9285
|
$ caddis doctor --strict exit non-zero on drift (CI)
|
|
9194
9286
|
|
|
9195
|
-
${
|
|
9287
|
+
${color2.bold("Supported agents")}
|
|
9196
9288
|
claude Claude Code via its plugin marketplace
|
|
9197
9289
|
agy Antigravity via the caddis bundle shipped in this package
|
|
9198
9290
|
(--extras adds caddis-extras; already-installed
|