@macintacos/caret 1.0.2 → 1.1.0
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/.claude-plugin/plugin.json +1 -1
- package/README.md +16 -9
- package/bin/caret +1 -1
- package/commands/doctor.md +139 -0
- package/commands/plan.md +25 -0
- package/dist/cli.js +1845 -821
- package/opencode/caret.plugin.ts +6 -5
- package/opencode/commands/doctor.md +120 -0
- package/opencode/commands/plan.md +24 -0
- package/package.json +1 -1
- package/ui/dist/assets/{angular-html-CNIx69MF.js → angular-html-BECXXPyr.js} +1 -1
- package/ui/dist/assets/{angular-ts-B6ob15kD.js → angular-ts-CJKAKY9F.js} +1 -1
- package/ui/dist/assets/{apl-BzQg5wKE.js → apl-CCcfDDqZ.js} +1 -1
- package/ui/dist/assets/{astro-DQ6PiTON.js → astro-DKCnAnVR.js} +1 -1
- package/ui/dist/assets/{blade-DdGMUzbN.js → blade-DQ_JdT2N.js} +1 -1
- package/ui/dist/assets/{c-DhrAH2di.js → c-D3TwszVj.js} +1 -1
- package/ui/dist/assets/{chapel-BIJH6n90.js → chapel-C-2sPcCX.js} +1 -1
- package/ui/dist/assets/{cobol-CR-dFJ1T.js → cobol-BPmo_4f_.js} +1 -1
- package/ui/dist/assets/{coffee-DWT27RO5.js → coffee-DMTsNDVt.js} +1 -1
- package/ui/dist/assets/{cpp-CuONTEvg.js → cpp-BJr6_Y8R.js} +1 -1
- package/ui/dist/assets/{crystal-Bvc0l-OM.js → crystal-pIWESUkD.js} +1 -1
- package/ui/dist/assets/{css-BYejQHVV.js → css-DRd9Q4Li.js} +1 -1
- package/ui/dist/assets/{dist-COogZ9jg.js → dist-BFQBMwmz.js} +1 -1
- package/ui/dist/assets/{dist-BgZhLp84.js → dist-BGfsV7n2.js} +1 -1
- package/ui/dist/assets/{dist-Cuerw0_4.js → dist-BJ7SLVNC.js} +1 -1
- package/ui/dist/assets/{dist-D-lJ6tIg.js → dist-BNr0c85A.js} +1 -1
- package/ui/dist/assets/{dist-C-FIXuZc.js → dist-C6t67BjE.js} +1 -1
- package/ui/dist/assets/{dist-Bn_ExXCc.js → dist-CD25BkNS.js} +1 -1
- package/ui/dist/assets/{dist-BbAsmjOe.js → dist-CU5aMlNo.js} +1 -1
- package/ui/dist/assets/{dist-CCPLuvXB.js → dist-DoJTqy3z.js} +1 -1
- package/ui/dist/assets/{dist-RKZBj-sl.js → dist-RKKeH1zs.js} +1 -1
- package/ui/dist/assets/{dist-DV7t-p4N.js → dist-aKlRxpD_.js} +1 -1
- package/ui/dist/assets/{edge-B_-8Eolr.js → edge-DeNk4F2G.js} +1 -1
- package/ui/dist/assets/{elixir-CAhwbI74.js → elixir-DHc-DOO8.js} +1 -1
- package/ui/dist/assets/{elm-yg-VSwEl.js → elm-CJRqEcvz.js} +1 -1
- package/ui/dist/assets/{erb-CIMOpWGO.js → erb-BEWGTHDA.js} +1 -1
- package/ui/dist/assets/{git-rebase-DI22snkc.js → git-rebase-DzlcWhg9.js} +1 -1
- package/ui/dist/assets/{glimmer-js-CZHZNAs-.js → glimmer-js-B-aWCRIZ.js} +1 -1
- package/ui/dist/assets/{glimmer-ts-C2rq-JGq.js → glimmer-ts-DcjmPyQn.js} +1 -1
- package/ui/dist/assets/{glsl-ByXN6hIN.js → glsl-C9FOT-Uv.js} +1 -1
- package/ui/dist/assets/{graphql-DiiJwLqR.js → graphql-BgKSoRp0.js} +1 -1
- package/ui/dist/assets/{hack-hQSSm1D1.js → hack-BOedpUUE.js} +1 -1
- package/ui/dist/assets/{haml-cf9hcOgw.js → haml-Cpngw1vd.js} +1 -1
- package/ui/dist/assets/{handlebars-B3tg892p.js → handlebars-CpW1IyBg.js} +1 -1
- package/ui/dist/assets/{html-CkIr2qlC.js → html-CV142D8s.js} +1 -1
- package/ui/dist/assets/{html-derivative-U1EOyk5J.js → html-derivative-Df7h1Nuw.js} +1 -1
- package/ui/dist/assets/{http-ek0-hDT8.js → http-Cneddv9N.js} +1 -1
- package/ui/dist/assets/{hurl-DbN52Bpk.js → hurl-D6TS55N6.js} +1 -1
- package/ui/dist/assets/{index-uBaTjG1U.js → index-Ci9yCNtV.js} +248 -214
- package/ui/dist/assets/index-t6i4na1R.css +1 -0
- package/ui/dist/assets/{java-DXEu0FGs.js → java-BicD3zQH.js} +1 -1
- package/ui/dist/assets/{javascript-Bi95ibpN.js → javascript-l6vKHWBV.js} +1 -1
- package/ui/dist/assets/{jinja-BcKnkqjY.js → jinja-BJF2iot2.js} +1 -1
- package/ui/dist/assets/{jison-CFaHQ730.js → jison-DiXPFJAT.js} +1 -1
- package/ui/dist/assets/{json--ulVurkL.js → json-BDRuI4UV.js} +1 -1
- package/ui/dist/assets/{jsx-ZonywBBD.js → jsx-BvfdkIOy.js} +1 -1
- package/ui/dist/assets/{julia-Bnt3bGqT.js → julia-bXAhreI5.js} +1 -1
- package/ui/dist/assets/{just-CBpil8Tz.js → just-BOxCfXDg.js} +1 -1
- package/ui/dist/assets/{latex-D-qwKUqq.js → latex-B9OiTBLF.js} +1 -1
- package/ui/dist/assets/{liquid-Bu8cI_b1.js → liquid-DuLKDeWE.js} +1 -1
- package/ui/dist/assets/{lua-DO_5Mymc.js → lua-Dbh9FQm_.js} +1 -1
- package/ui/dist/assets/{marko-DJfs6dcc.js → marko--tjEBgDM.js} +1 -1
- package/ui/dist/assets/{mdc-CnkU1WJT.js → mdc-ByNJpl3m.js} +1 -1
- package/ui/dist/assets/{nginx-B5LxXEA3.js → nginx-D4wsmFK9.js} +1 -1
- package/ui/dist/assets/{nim-DLTorTuD.js → nim-Cy8RxisP.js} +1 -1
- package/ui/dist/assets/{org-DnNvsXCU.js → org-CAm7o61d.js} +1 -1
- package/ui/dist/assets/{perl-Q5Q2rA31.js → perl-BAvaFGKe.js} +1 -1
- package/ui/dist/assets/{php-S65hjGBt.js → php-DpAf3w7u.js} +1 -1
- package/ui/dist/assets/{pug-k-MB8PEo.js → pug-Dm7otyJU.js} +1 -1
- package/ui/dist/assets/{qml-Bf9cLRPv.js → qml-D4rWQQKC.js} +1 -1
- package/ui/dist/assets/{r-Cr3hDQkZ.js → r-DMKUX8qf.js} +1 -1
- package/ui/dist/assets/{razor-BO5-wpmp.js → razor-BIUJkuf_.js} +1 -1
- package/ui/dist/assets/{regexp-ffQ9gsih.js → regexp-D8W_pVHj.js} +1 -1
- package/ui/dist/assets/{rst-B4AQC5xj.js → rst-aFr0DP5H.js} +1 -1
- package/ui/dist/assets/{ruby-Dr8kfvLX.js → ruby-BzH6kUKr.js} +1 -1
- package/ui/dist/assets/{sas-Kuo2pLeL.js → sas-CDk9DyCV.js} +1 -1
- package/ui/dist/assets/{scss-Ci63BQ8y.js → scss-DABbvU5W.js} +1 -1
- package/ui/dist/assets/{shellscript-DBh4gnVx.js → shellscript-B0ObP3K-.js} +1 -1
- package/ui/dist/assets/{shellsession-L5KFvHI3.js → shellsession-2sv3Jq0j.js} +1 -1
- package/ui/dist/assets/{soy-D6F5ilyd.js → soy-irSRV4oq.js} +1 -1
- package/ui/dist/assets/{sql-xwRblxjL.js → sql-DJCHoKvP.js} +1 -1
- package/ui/dist/assets/{stata-l8sBIGPT.js → stata-a8YnzbIc.js} +1 -1
- package/ui/dist/assets/{surrealql-DL2sOSzB.js → surrealql-DdXPOg8s.js} +1 -1
- package/ui/dist/assets/{svelte-Dp5d2Ja3.js → svelte-ClAKUEZR.js} +1 -1
- package/ui/dist/assets/{templ-B3LfgeSM.js → templ-NUCvvUBA.js} +1 -1
- package/ui/dist/assets/{tex-CfLQjgEV.js → tex-DwZeSAfC.js} +1 -1
- package/ui/dist/assets/{ts-tags-BCfi6Eh_.js → ts-tags-CFJEHYY_.js} +1 -1
- package/ui/dist/assets/{tsx-BXhT7ita.js → tsx-B934yX2T.js} +1 -1
- package/ui/dist/assets/{twig-t6tdMpRg.js → twig-NFkkOj3w.js} +1 -1
- package/ui/dist/assets/{typescript-CFIlDlxn.js → typescript-7bmMC_xU.js} +1 -1
- package/ui/dist/assets/{typst-CVQ9ief2.js → typst-BQEjp9tn.js} +1 -1
- package/ui/dist/assets/{vue-D92U7PL6.js → vue-JqD9ieOF.js} +1 -1
- package/ui/dist/assets/{vue-html-B_-4d7cG.js → vue-html-CT4XQRX1.js} +1 -1
- package/ui/dist/assets/{vue-vine-LaG8znAe.js → vue-vine-DIR27Dmn.js} +1 -1
- package/ui/dist/assets/{xml-DgAE0jfV.js → xml-DhsMbw-1.js} +1 -1
- package/ui/dist/assets/{xsl-DkD8aIzS.js → xsl-BvqsVUg9.js} +1 -1
- package/ui/dist/assets/{yaml-CEG92fN8.js → yaml-C8eWjK_Q.js} +1 -1
- package/ui/dist/index.html +2 -2
- package/commands/debug.md +0 -138
- package/commands/discovery.md +0 -35
- package/opencode/commands/debug.md +0 -59
- package/opencode/commands/discovery.md +0 -31
- package/ui/dist/assets/index-CAl4rX7_.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -419,6 +419,7 @@ var init_claude = __esm(() => {
|
|
|
419
419
|
claudeAdapter = {
|
|
420
420
|
id: "claude",
|
|
421
421
|
approveVariants: APPROVE_VARIANTS,
|
|
422
|
+
restartHint: "Then run /reload-plugins or restart Claude Code.",
|
|
422
423
|
parseHookInput(stdin) {
|
|
423
424
|
const hook = parseHookStdin(stdin);
|
|
424
425
|
return {
|
|
@@ -2983,212 +2984,6 @@ function isNewer(latest, current) {
|
|
|
2983
2984
|
return a2 > b2;
|
|
2984
2985
|
}
|
|
2985
2986
|
|
|
2986
|
-
// src/adapters/opencode/upgrade.ts
|
|
2987
|
-
import { existsSync as existsSync2, readFileSync as readFileSync3, rmSync } from "fs";
|
|
2988
|
-
import { join as join6 } from "path";
|
|
2989
|
-
function upgradeVerdict(input) {
|
|
2990
|
-
const { entry, cached, published } = input;
|
|
2991
|
-
if (published === null) {
|
|
2992
|
-
return { kind: "unknown", reason: "could not reach npm for the published caret version" };
|
|
2993
|
-
}
|
|
2994
|
-
if (parseVersionTriple(published) === null) {
|
|
2995
|
-
return { kind: "unknown", reason: `npm reported an unreadable version (${published})` };
|
|
2996
|
-
}
|
|
2997
|
-
if (entry === null)
|
|
2998
|
-
return { kind: "fresh" };
|
|
2999
|
-
const pinned = splitPluginSpecifier(entry).version;
|
|
3000
|
-
if (pinned !== null && parseVersionTriple(pinned) !== null) {
|
|
3001
|
-
return isNewer(published, pinned) ? { kind: "stale-pin", entry, pinned, published } : { kind: "current", version: pinned };
|
|
3002
|
-
}
|
|
3003
|
-
if (cached === null)
|
|
3004
|
-
return { kind: "fresh" };
|
|
3005
|
-
if (parseVersionTriple(cached) === null) {
|
|
3006
|
-
return { kind: "unknown", reason: `OpenCode cached an unreadable version (${cached})` };
|
|
3007
|
-
}
|
|
3008
|
-
return isNewer(published, cached) ? { kind: "stale-cache", cached, published } : { kind: "current", version: cached };
|
|
3009
|
-
}
|
|
3010
|
-
function readCachedCaretVersion(dirs = existingOpencodeCachePackageDirs()) {
|
|
3011
|
-
for (const d of dirs) {
|
|
3012
|
-
try {
|
|
3013
|
-
const deps = JSON.parse(readFileSync3(join6(d, "package.json"), "utf-8")).dependencies;
|
|
3014
|
-
const v = deps?.[CARET_PACKAGE];
|
|
3015
|
-
if (typeof v === "string" && v.length > 0)
|
|
3016
|
-
return v;
|
|
3017
|
-
} catch {}
|
|
3018
|
-
}
|
|
3019
|
-
return null;
|
|
3020
|
-
}
|
|
3021
|
-
function clearCachedCaret(dirs = existingOpencodeCachePackageDirs()) {
|
|
3022
|
-
const cleared = [];
|
|
3023
|
-
for (const d of dirs) {
|
|
3024
|
-
if (!existsSync2(d))
|
|
3025
|
-
continue;
|
|
3026
|
-
rmSync(d, { recursive: true, force: true });
|
|
3027
|
-
cleared.push(d);
|
|
3028
|
-
}
|
|
3029
|
-
return cleared;
|
|
3030
|
-
}
|
|
3031
|
-
var init_upgrade = __esm(() => {
|
|
3032
|
-
init_config_plugin();
|
|
3033
|
-
init_paths2();
|
|
3034
|
-
});
|
|
3035
|
-
|
|
3036
|
-
// src/adapters/opencode/install.ts
|
|
3037
|
-
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
3038
|
-
import { join as join7 } from "path";
|
|
3039
|
-
function readOpencodeInstallState() {
|
|
3040
|
-
const dir = opencodeConfigDir();
|
|
3041
|
-
if (!existsSync3(dir)) {
|
|
3042
|
-
return { pluginVersion: "unknown", pluginEnabled: "unknown", hookInUserSettings: "unknown" };
|
|
3043
|
-
}
|
|
3044
|
-
const version = readCachedCaretVersion(existingOpencodeCachePackageDirs()) ?? "unknown";
|
|
3045
|
-
return {
|
|
3046
|
-
pluginVersion: version,
|
|
3047
|
-
pluginEnabled: version !== "unknown",
|
|
3048
|
-
hookInUserSettings: readCaretInPluginArray(dir)
|
|
3049
|
-
};
|
|
3050
|
-
}
|
|
3051
|
-
function readCaretInPluginArray(dir) {
|
|
3052
|
-
let sawConfig = false;
|
|
3053
|
-
for (const name of CONFIG_FILENAMES) {
|
|
3054
|
-
const path = join7(dir, name);
|
|
3055
|
-
if (!existsSync3(path))
|
|
3056
|
-
continue;
|
|
3057
|
-
let cfg;
|
|
3058
|
-
try {
|
|
3059
|
-
cfg = parse3(readFileSync4(path, "utf-8"));
|
|
3060
|
-
} catch {
|
|
3061
|
-
continue;
|
|
3062
|
-
}
|
|
3063
|
-
if (cfg === undefined || cfg === null)
|
|
3064
|
-
continue;
|
|
3065
|
-
sawConfig = true;
|
|
3066
|
-
const arr = cfg.plugin;
|
|
3067
|
-
if (Array.isArray(arr) && arr.some((e) => typeof e === "string" && e.includes("caret"))) {
|
|
3068
|
-
return true;
|
|
3069
|
-
}
|
|
3070
|
-
}
|
|
3071
|
-
return sawConfig ? false : "unknown";
|
|
3072
|
-
}
|
|
3073
|
-
var init_install3 = __esm(() => {
|
|
3074
|
-
init_main();
|
|
3075
|
-
init_paths2();
|
|
3076
|
-
init_upgrade();
|
|
3077
|
-
});
|
|
3078
|
-
|
|
3079
|
-
// src/adapters/opencode/skills.ts
|
|
3080
|
-
import { readdir as readdir2 } from "fs/promises";
|
|
3081
|
-
import { sep as sep2 } from "path";
|
|
3082
|
-
async function commandNamesUnder(dir) {
|
|
3083
|
-
let entries;
|
|
3084
|
-
try {
|
|
3085
|
-
entries = await readdir2(dir, { recursive: true });
|
|
3086
|
-
} catch {
|
|
3087
|
-
return [];
|
|
3088
|
-
}
|
|
3089
|
-
return entries.filter((rel) => rel.endsWith(".md")).map((rel) => rel.slice(0, -".md".length).split(sep2).join("/"));
|
|
3090
|
-
}
|
|
3091
|
-
async function readOpencodeCommands() {
|
|
3092
|
-
const perDir = await Promise.all(commandDirs(opencodeConfigDir()).map(commandNamesUnder));
|
|
3093
|
-
const names = new Set(perDir.flat());
|
|
3094
|
-
return [...names].sort().map((name) => ({ name, origin: "command" }));
|
|
3095
|
-
}
|
|
3096
|
-
async function readOpencodeCommandDescription(name) {
|
|
3097
|
-
const relative = `${name}.md`;
|
|
3098
|
-
for (const dir of commandDirs(opencodeConfigDir())) {
|
|
3099
|
-
const abs = await containedFile(dir, relative);
|
|
3100
|
-
if (abs !== null)
|
|
3101
|
-
return descriptionOf(abs);
|
|
3102
|
-
}
|
|
3103
|
-
return null;
|
|
3104
|
-
}
|
|
3105
|
-
var init_skills2 = __esm(() => {
|
|
3106
|
-
init_paths2();
|
|
3107
|
-
init_skill_doc();
|
|
3108
|
-
});
|
|
3109
|
-
|
|
3110
|
-
// src/adapters/opencode/index.ts
|
|
3111
|
-
var opencodeAdapter;
|
|
3112
|
-
var init_opencode = __esm(() => {
|
|
3113
|
-
init_approve3();
|
|
3114
|
-
init_feedback3();
|
|
3115
|
-
init_install3();
|
|
3116
|
-
init_skills2();
|
|
3117
|
-
opencodeAdapter = {
|
|
3118
|
-
id: "opencode",
|
|
3119
|
-
approveVariants: APPROVE_VARIANTS3,
|
|
3120
|
-
parseHookInput(stdin) {
|
|
3121
|
-
const hook = parseHookStdin(stdin);
|
|
3122
|
-
return {
|
|
3123
|
-
sessionId: hook.session_id,
|
|
3124
|
-
cwd: hook.cwd,
|
|
3125
|
-
plan: hook.tool_input?.plan,
|
|
3126
|
-
planFilePath: hook.tool_input?.planFilePath
|
|
3127
|
-
};
|
|
3128
|
-
},
|
|
3129
|
-
emitDecision(decision) {
|
|
3130
|
-
return JSON.stringify(toWireDecision(decision));
|
|
3131
|
-
},
|
|
3132
|
-
fatalDenyLine,
|
|
3133
|
-
readInstallState() {
|
|
3134
|
-
return readOpencodeInstallState();
|
|
3135
|
-
},
|
|
3136
|
-
listSkills() {
|
|
3137
|
-
return readOpencodeCommands();
|
|
3138
|
-
},
|
|
3139
|
-
readSkillDescription(_cwd, skill) {
|
|
3140
|
-
return readOpencodeCommandDescription(skill.name);
|
|
3141
|
-
}
|
|
3142
|
-
};
|
|
3143
|
-
});
|
|
3144
|
-
|
|
3145
|
-
// src/adapters/index.ts
|
|
3146
|
-
function agentIds() {
|
|
3147
|
-
return Object.keys(REGISTRY);
|
|
3148
|
-
}
|
|
3149
|
-
function selectAdapter(id) {
|
|
3150
|
-
const raw = (id ?? process.env.CARET_AGENT ?? "").trim();
|
|
3151
|
-
const key = raw === "" ? DEFAULT_AGENT : raw;
|
|
3152
|
-
const adapter = REGISTRY[key];
|
|
3153
|
-
if (!adapter) {
|
|
3154
|
-
throw new Error(`unknown agent adapter: "${key}" (known: ${agentIds().join(", ")})`);
|
|
3155
|
-
}
|
|
3156
|
-
return adapter;
|
|
3157
|
-
}
|
|
3158
|
-
function fatalDeny(reason) {
|
|
3159
|
-
let adapter;
|
|
3160
|
-
try {
|
|
3161
|
-
adapter = selectAdapter();
|
|
3162
|
-
} catch {}
|
|
3163
|
-
if (adapter) {
|
|
3164
|
-
try {
|
|
3165
|
-
return adapter.emitDecision({ behavior: "deny", feedback: reason, decidedAt: Date.now() });
|
|
3166
|
-
} catch {}
|
|
3167
|
-
try {
|
|
3168
|
-
return adapter.fatalDenyLine(reason);
|
|
3169
|
-
} catch {}
|
|
3170
|
-
}
|
|
3171
|
-
return permissionRequestDenyLine(reason);
|
|
3172
|
-
}
|
|
3173
|
-
var DEFAULT_AGENT = "claude", CLAUDE_MCP_AGENT = "claude-mcp", REGISTRY;
|
|
3174
|
-
var init_adapters = __esm(() => {
|
|
3175
|
-
init_claude();
|
|
3176
|
-
init_codex();
|
|
3177
|
-
init_opencode();
|
|
3178
|
-
REGISTRY = {
|
|
3179
|
-
claude: claudeAdapter,
|
|
3180
|
-
codex: codexAdapter,
|
|
3181
|
-
opencode: opencodeAdapter,
|
|
3182
|
-
[CLAUDE_MCP_AGENT]: {
|
|
3183
|
-
...claudeAdapter,
|
|
3184
|
-
id: CLAUDE_MCP_AGENT,
|
|
3185
|
-
parseHookInput: opencodeAdapter.parseHookInput,
|
|
3186
|
-
emitDecision: opencodeAdapter.emitDecision,
|
|
3187
|
-
fatalDenyLine: opencodeAdapter.fatalDenyLine
|
|
3188
|
-
}
|
|
3189
|
-
};
|
|
3190
|
-
});
|
|
3191
|
-
|
|
3192
2987
|
// node_modules/.bun/zod@4.5.4/node_modules/zod/v4/core/util.js
|
|
3193
2988
|
function getEnumValues(entries) {
|
|
3194
2989
|
const numericValues = Object.values(entries).filter((v) => typeof v === "number");
|
|
@@ -4105,7 +3900,7 @@ var cuid, cuid2, ulid, xid, ksuid, nanoid, duration, guid, uuid = (version) => {
|
|
|
4105
3900
|
}, email, _emoji = `^[\\p{Extended_Pictographic}\\p{Emoji_Component}]+$`, ipv4, ipv6, cidrv4, cidrv6, base64, base64url, httpProtocol, e164, dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`, date, string = (params) => {
|
|
4106
3901
|
const regex = params ? `[\\s\\S]{${params?.minimum ?? 0},${params?.maximum ?? ""}}` : `[\\s\\S]*`;
|
|
4107
3902
|
return new RegExp(`^${regex}$`);
|
|
4108
|
-
}, integer, number, boolean, _null, lowercase, uppercase;
|
|
3903
|
+
}, bigint, integer, number, boolean, _null, lowercase, uppercase;
|
|
4109
3904
|
var init_regexes = __esm(() => {
|
|
4110
3905
|
cuid = /^[cC][0-9a-z]{6,}$/;
|
|
4111
3906
|
cuid2 = /^[0-9a-z]+$/;
|
|
@@ -4125,6 +3920,7 @@ var init_regexes = __esm(() => {
|
|
|
4125
3920
|
httpProtocol = /^https?$/;
|
|
4126
3921
|
e164 = /^\+[1-9]\d{6,14}$/;
|
|
4127
3922
|
date = /* @__PURE__ */ anchor(dateSource);
|
|
3923
|
+
bigint = /^-?\d+n?$/;
|
|
4128
3924
|
integer = /^-?\d+$/;
|
|
4129
3925
|
number = /^-?\d+(?:\.\d+)?$/;
|
|
4130
3926
|
boolean = /^(?:true|false)$/i;
|
|
@@ -4955,7 +4751,7 @@ function handleRefineResult(result, payload, input, inst) {
|
|
|
4955
4751
|
payload.issues.push(issue(_iss));
|
|
4956
4752
|
}
|
|
4957
4753
|
}
|
|
4958
|
-
var $ZodType, toStandardResult = (r) => r.success ? { value: r.data } : { issues: r.error?.issues }, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, URL_BAD_FORMAT = 1, URL_UNPARSEABLE = 2, asciiTabOrNewline, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, ipv6Alphabet, $ZodIPv6, $ZodCIDRv4, $ZodCIDRv6, $ZodBase64, $ZodBase64URL, $ZodE164, $ZodJWT, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodNull, $ZodUnknown, $ZodNever, $ZodArray, NO_SYMBOL_KEYS, propShapes, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodDiscriminatedUnion, $ZodIntersection, $ZodRecord, $ZodEnum, $ZodLiteral, $ZodTransform, $ZodOptional, $ZodExactOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodCatch, $ZodPipe, $ZodPreprocess, $ZodReadonly, $ZodCustom;
|
|
4754
|
+
var $ZodType, toStandardResult = (r) => r.success ? { value: r.data } : { issues: r.error?.issues }, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, URL_BAD_FORMAT = 1, URL_UNPARSEABLE = 2, asciiTabOrNewline, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, ipv6Alphabet, $ZodIPv6, $ZodCIDRv4, $ZodCIDRv6, $ZodBase64, $ZodBase64URL, $ZodE164, $ZodJWT, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodBigInt, $ZodNull, $ZodUnknown, $ZodNever, $ZodDate, $ZodArray, NO_SYMBOL_KEYS, propShapes, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodDiscriminatedUnion, $ZodIntersection, $ZodRecord, $ZodEnum, $ZodLiteral, $ZodTransform, $ZodOptional, $ZodExactOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodCatch, $ZodPipe, $ZodPreprocess, $ZodReadonly, $ZodCustom;
|
|
4959
4755
|
var init_schemas = __esm(() => {
|
|
4960
4756
|
init_checks();
|
|
4961
4757
|
init_core();
|
|
@@ -5376,6 +5172,25 @@ var init_schemas = __esm(() => {
|
|
|
5376
5172
|
return payload;
|
|
5377
5173
|
};
|
|
5378
5174
|
});
|
|
5175
|
+
$ZodBigInt = /* @__PURE__ */ $constructor("$ZodBigInt", (inst, def) => {
|
|
5176
|
+
$ZodType.init(inst, def);
|
|
5177
|
+
inst._zod.pattern = bigint;
|
|
5178
|
+
inst._zod.parse = (payload, _ctx) => {
|
|
5179
|
+
if (def.coerce)
|
|
5180
|
+
try {
|
|
5181
|
+
payload.value = BigInt(payload.value);
|
|
5182
|
+
} catch (_) {}
|
|
5183
|
+
if (typeof payload.value === "bigint")
|
|
5184
|
+
return payload;
|
|
5185
|
+
payload.issues.push({
|
|
5186
|
+
expected: "bigint",
|
|
5187
|
+
code: "invalid_type",
|
|
5188
|
+
input: payload.value,
|
|
5189
|
+
inst
|
|
5190
|
+
});
|
|
5191
|
+
return payload;
|
|
5192
|
+
};
|
|
5193
|
+
});
|
|
5379
5194
|
$ZodNull = /* @__PURE__ */ $constructor("$ZodNull", (inst, def) => {
|
|
5380
5195
|
$ZodType.init(inst, def);
|
|
5381
5196
|
inst._zod.pattern = _null;
|
|
@@ -5409,6 +5224,29 @@ var init_schemas = __esm(() => {
|
|
|
5409
5224
|
return payload;
|
|
5410
5225
|
};
|
|
5411
5226
|
});
|
|
5227
|
+
$ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
5228
|
+
$ZodType.init(inst, def);
|
|
5229
|
+
inst._zod.parse = (payload, _ctx) => {
|
|
5230
|
+
if (def.coerce) {
|
|
5231
|
+
try {
|
|
5232
|
+
payload.value = new Date(payload.value);
|
|
5233
|
+
} catch (_err) {}
|
|
5234
|
+
}
|
|
5235
|
+
const input = payload.value;
|
|
5236
|
+
const isDate = input instanceof Date;
|
|
5237
|
+
const isValidDate = isDate && !Number.isNaN(input.getTime());
|
|
5238
|
+
if (isValidDate)
|
|
5239
|
+
return payload;
|
|
5240
|
+
payload.issues.push({
|
|
5241
|
+
expected: "date",
|
|
5242
|
+
code: "invalid_type",
|
|
5243
|
+
input,
|
|
5244
|
+
...isDate ? { received: "Invalid Date" } : {},
|
|
5245
|
+
inst
|
|
5246
|
+
});
|
|
5247
|
+
return payload;
|
|
5248
|
+
};
|
|
5249
|
+
});
|
|
5412
5250
|
$ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
|
|
5413
5251
|
$ZodType.init(inst, def);
|
|
5414
5252
|
const memo = globalConfig.memoizer;
|
|
@@ -6620,6 +6458,13 @@ function _string(Class, params) {
|
|
|
6620
6458
|
...normalizeParams(params)
|
|
6621
6459
|
});
|
|
6622
6460
|
}
|
|
6461
|
+
function _coercedString(Class, params) {
|
|
6462
|
+
return new Class({
|
|
6463
|
+
type: "string",
|
|
6464
|
+
coerce: true,
|
|
6465
|
+
...normalizeParams(params)
|
|
6466
|
+
});
|
|
6467
|
+
}
|
|
6623
6468
|
function _email(Class, params) {
|
|
6624
6469
|
return new Class({
|
|
6625
6470
|
type: "string",
|
|
@@ -6864,6 +6709,14 @@ function _number(Class, params) {
|
|
|
6864
6709
|
...normalizeParams(params)
|
|
6865
6710
|
});
|
|
6866
6711
|
}
|
|
6712
|
+
function _coercedNumber(Class, params) {
|
|
6713
|
+
return new Class({
|
|
6714
|
+
type: "number",
|
|
6715
|
+
coerce: true,
|
|
6716
|
+
checks: [],
|
|
6717
|
+
...normalizeParams(params)
|
|
6718
|
+
});
|
|
6719
|
+
}
|
|
6867
6720
|
function _int(Class, params) {
|
|
6868
6721
|
return new Class({
|
|
6869
6722
|
type: "number",
|
|
@@ -6879,6 +6732,20 @@ function _boolean(Class, params) {
|
|
|
6879
6732
|
...normalizeParams(params)
|
|
6880
6733
|
});
|
|
6881
6734
|
}
|
|
6735
|
+
function _coercedBoolean(Class, params) {
|
|
6736
|
+
return new Class({
|
|
6737
|
+
type: "boolean",
|
|
6738
|
+
coerce: true,
|
|
6739
|
+
...normalizeParams(params)
|
|
6740
|
+
});
|
|
6741
|
+
}
|
|
6742
|
+
function _coercedBigint(Class, params) {
|
|
6743
|
+
return new Class({
|
|
6744
|
+
type: "bigint",
|
|
6745
|
+
coerce: true,
|
|
6746
|
+
...normalizeParams(params)
|
|
6747
|
+
});
|
|
6748
|
+
}
|
|
6882
6749
|
function _null2(Class, params) {
|
|
6883
6750
|
return new Class({
|
|
6884
6751
|
type: "null",
|
|
@@ -6896,6 +6763,13 @@ function _never(Class, params) {
|
|
|
6896
6763
|
...normalizeParams(params)
|
|
6897
6764
|
});
|
|
6898
6765
|
}
|
|
6766
|
+
function _coercedDate(Class, params) {
|
|
6767
|
+
return new Class({
|
|
6768
|
+
type: "date",
|
|
6769
|
+
coerce: true,
|
|
6770
|
+
...normalizeParams(params)
|
|
6771
|
+
});
|
|
6772
|
+
}
|
|
6899
6773
|
function _lt(value, params) {
|
|
6900
6774
|
return new $ZodCheckLessThan({
|
|
6901
6775
|
check: "less_than",
|
|
@@ -8517,7 +8391,7 @@ function preprocess(fn, schema) {
|
|
|
8517
8391
|
out: schema
|
|
8518
8392
|
});
|
|
8519
8393
|
}
|
|
8520
|
-
var ZodType, _ZodString, ZodString, ZodStringFormat, ZodISODateTime, ZodISODate, ZodISOTime, ZodISODuration, ZodEmail, ZodGUID, ZodUUID, ZodURL, ZodEmoji, ZodNanoID, ZodCUID, ZodCUID2, ZodULID, ZodXID, ZodKSUID, ZodIPv4, ZodIPv6, ZodCIDRv4, ZodCIDRv6, ZodBase64, ZodBase64URL, ZodE164, ZodJWT, ZodNumber, ZodNumberFormat, ZodBoolean, ZodNull, ZodUnknown, ZodNever, ZodArray, ZodObject, ZodUnion, ZodDiscriminatedUnion, ZodIntersection, ZodRecord, ZodEnum, ZodLiteral, ZodTransform, ZodOptional, ZodExactOptional, ZodNullable, ZodDefault, ZodPrefault, ZodNonOptional, ZodCatch, ZodPipe, ZodPreprocess, ZodReadonly, ZodCustom;
|
|
8394
|
+
var ZodType, _ZodString, ZodString, ZodStringFormat, ZodISODateTime, ZodISODate, ZodISOTime, ZodISODuration, ZodEmail, ZodGUID, ZodUUID, ZodURL, ZodEmoji, ZodNanoID, ZodCUID, ZodCUID2, ZodULID, ZodXID, ZodKSUID, ZodIPv4, ZodIPv6, ZodCIDRv4, ZodCIDRv6, ZodBase64, ZodBase64URL, ZodE164, ZodJWT, ZodNumber, ZodNumberFormat, ZodBoolean, ZodBigInt, ZodNull, ZodUnknown, ZodNever, ZodDate, ZodArray, ZodObject, ZodUnion, ZodDiscriminatedUnion, ZodIntersection, ZodRecord, ZodEnum, ZodLiteral, ZodTransform, ZodOptional, ZodExactOptional, ZodNullable, ZodDefault, ZodPrefault, ZodNonOptional, ZodCatch, ZodPipe, ZodPreprocess, ZodReadonly, ZodCustom;
|
|
8521
8395
|
var init_schemas2 = __esm(() => {
|
|
8522
8396
|
init_core2();
|
|
8523
8397
|
init_core2();
|
|
@@ -8991,6 +8865,49 @@ var init_schemas2 = __esm(() => {
|
|
|
8991
8865
|
ZodType.init(inst, def);
|
|
8992
8866
|
inst._zod.processJSONSchema = (ctx, json, params) => booleanProcessor(inst, ctx, json, params);
|
|
8993
8867
|
});
|
|
8868
|
+
ZodBigInt = /* @__PURE__ */ $constructor("ZodBigInt", (inst, def) => {
|
|
8869
|
+
$ZodBigInt.init(inst, def);
|
|
8870
|
+
ZodType.init(inst, def);
|
|
8871
|
+
inst._zod.processJSONSchema = (ctx, json, params) => bigintProcessor(inst, ctx, json, params);
|
|
8872
|
+
const bag = inst._zod.bag;
|
|
8873
|
+
inst.minValue = bag.minimum ?? null;
|
|
8874
|
+
inst.maxValue = bag.maximum ?? null;
|
|
8875
|
+
inst.format = bag.format ?? null;
|
|
8876
|
+
}, {
|
|
8877
|
+
gte(value, params) {
|
|
8878
|
+
return this.check(_gte(value, params));
|
|
8879
|
+
},
|
|
8880
|
+
min(value, params) {
|
|
8881
|
+
return this.check(_gte(value, params));
|
|
8882
|
+
},
|
|
8883
|
+
gt(value, params) {
|
|
8884
|
+
return this.check(_gt(value, params));
|
|
8885
|
+
},
|
|
8886
|
+
lt(value, params) {
|
|
8887
|
+
return this.check(_lt(value, params));
|
|
8888
|
+
},
|
|
8889
|
+
lte(value, params) {
|
|
8890
|
+
return this.check(_lte(value, params));
|
|
8891
|
+
},
|
|
8892
|
+
max(value, params) {
|
|
8893
|
+
return this.check(_lte(value, params));
|
|
8894
|
+
},
|
|
8895
|
+
positive(params) {
|
|
8896
|
+
return this.check(_gt(BigInt(0), params));
|
|
8897
|
+
},
|
|
8898
|
+
negative(params) {
|
|
8899
|
+
return this.check(_lt(BigInt(0), params));
|
|
8900
|
+
},
|
|
8901
|
+
nonpositive(params) {
|
|
8902
|
+
return this.check(_lte(BigInt(0), params));
|
|
8903
|
+
},
|
|
8904
|
+
nonnegative(params) {
|
|
8905
|
+
return this.check(_gte(BigInt(0), params));
|
|
8906
|
+
},
|
|
8907
|
+
multipleOf(value, params) {
|
|
8908
|
+
return this.check(_multipleOf(value, params));
|
|
8909
|
+
}
|
|
8910
|
+
});
|
|
8994
8911
|
ZodNull = /* @__PURE__ */ $constructor("ZodNull", (inst, def) => {
|
|
8995
8912
|
$ZodNull.init(inst, def);
|
|
8996
8913
|
ZodType.init(inst, def);
|
|
@@ -9006,6 +8923,16 @@ var init_schemas2 = __esm(() => {
|
|
|
9006
8923
|
ZodType.init(inst, def);
|
|
9007
8924
|
inst._zod.processJSONSchema = (ctx, json, params) => neverProcessor(inst, ctx, json, params);
|
|
9008
8925
|
});
|
|
8926
|
+
ZodDate = /* @__PURE__ */ $constructor("ZodDate", (inst, def) => {
|
|
8927
|
+
$ZodDate.init(inst, def);
|
|
8928
|
+
ZodType.init(inst, def);
|
|
8929
|
+
inst._zod.processJSONSchema = (ctx, json, params) => dateProcessor(inst, ctx, json, params);
|
|
8930
|
+
inst.min = (value, params) => inst.check(_gte(value, params));
|
|
8931
|
+
inst.max = (value, params) => inst.check(_lte(value, params));
|
|
8932
|
+
const c = inst._zod.bag;
|
|
8933
|
+
inst.minDate = c.minimum ? new Date(c.minimum) : null;
|
|
8934
|
+
inst.maxDate = c.maximum ? new Date(c.maximum) : null;
|
|
8935
|
+
});
|
|
9009
8936
|
ZodArray = /* @__PURE__ */ $constructor("ZodArray", (inst, def) => {
|
|
9010
8937
|
_ensureDefaultMemoizer();
|
|
9011
8938
|
$ZodArray.init(inst, def);
|
|
@@ -9272,7 +9199,33 @@ var init_iso = __esm(() => {
|
|
|
9272
9199
|
});
|
|
9273
9200
|
|
|
9274
9201
|
// node_modules/.bun/zod@4.5.4/node_modules/zod/v4/classic/coerce.js
|
|
9275
|
-
var
|
|
9202
|
+
var exports_coerce = {};
|
|
9203
|
+
__export(exports_coerce, {
|
|
9204
|
+
bigint: () => bigint2,
|
|
9205
|
+
boolean: () => boolean3,
|
|
9206
|
+
date: () => date2,
|
|
9207
|
+
number: () => number3,
|
|
9208
|
+
string: () => string3
|
|
9209
|
+
});
|
|
9210
|
+
function string3(params) {
|
|
9211
|
+
return _coercedString(ZodString, params);
|
|
9212
|
+
}
|
|
9213
|
+
function number3(params) {
|
|
9214
|
+
return _coercedNumber(ZodNumber, params);
|
|
9215
|
+
}
|
|
9216
|
+
function boolean3(params) {
|
|
9217
|
+
return _coercedBoolean(ZodBoolean, params);
|
|
9218
|
+
}
|
|
9219
|
+
function bigint2(params) {
|
|
9220
|
+
return _coercedBigint(ZodBigInt, params);
|
|
9221
|
+
}
|
|
9222
|
+
function date2(params) {
|
|
9223
|
+
return _coercedDate(ZodDate, params);
|
|
9224
|
+
}
|
|
9225
|
+
var init_coerce = __esm(() => {
|
|
9226
|
+
init_core2();
|
|
9227
|
+
init_schemas2();
|
|
9228
|
+
});
|
|
9276
9229
|
|
|
9277
9230
|
// node_modules/.bun/zod@4.5.4/node_modules/zod/v4/classic/external.js
|
|
9278
9231
|
var init_external = __esm(() => {
|
|
@@ -9293,6 +9246,429 @@ var init_zod = __esm(() => {
|
|
|
9293
9246
|
init_external();
|
|
9294
9247
|
});
|
|
9295
9248
|
|
|
9249
|
+
// package.json
|
|
9250
|
+
var package_default;
|
|
9251
|
+
var init_package = __esm(() => {
|
|
9252
|
+
package_default = {
|
|
9253
|
+
name: "@macintacos/caret",
|
|
9254
|
+
version: "1.1.0",
|
|
9255
|
+
license: "MIT",
|
|
9256
|
+
type: "module",
|
|
9257
|
+
description: "A Claude Code plugin that replaces the terminal plan-approval prompt with a local web UI for inline review, annotation, and approval.",
|
|
9258
|
+
main: "./opencode/index.ts",
|
|
9259
|
+
exports: {
|
|
9260
|
+
".": "./opencode/index.ts"
|
|
9261
|
+
},
|
|
9262
|
+
bin: {
|
|
9263
|
+
caret: "./bin/caret"
|
|
9264
|
+
},
|
|
9265
|
+
homepage: "https://github.com/macintacos/caret",
|
|
9266
|
+
repository: {
|
|
9267
|
+
type: "git",
|
|
9268
|
+
url: "git+https://github.com/macintacos/caret.git"
|
|
9269
|
+
},
|
|
9270
|
+
publishConfig: {
|
|
9271
|
+
access: "public"
|
|
9272
|
+
},
|
|
9273
|
+
files: [
|
|
9274
|
+
"dist/",
|
|
9275
|
+
"ui/dist/",
|
|
9276
|
+
"bin/caret",
|
|
9277
|
+
"bin/caret-launcher",
|
|
9278
|
+
"hooks/",
|
|
9279
|
+
"commands/",
|
|
9280
|
+
"opencode/",
|
|
9281
|
+
"templates/",
|
|
9282
|
+
".claude-plugin/plugin.json"
|
|
9283
|
+
],
|
|
9284
|
+
scripts: {
|
|
9285
|
+
test: "bun test --conditions browser --parallel --timeout 35000",
|
|
9286
|
+
lint: "mise run lint",
|
|
9287
|
+
format: "mise run format",
|
|
9288
|
+
build: "mise run build"
|
|
9289
|
+
},
|
|
9290
|
+
devDependencies: {
|
|
9291
|
+
"@clack/prompts": "^1.7.0",
|
|
9292
|
+
"@codemirror/autocomplete": "^6.20.3",
|
|
9293
|
+
"@codemirror/commands": "^6.10.3",
|
|
9294
|
+
"@codemirror/lang-markdown": "^6.5.0",
|
|
9295
|
+
"@codemirror/language": "^6.12.3",
|
|
9296
|
+
"@codemirror/language-data": "^6.5.2",
|
|
9297
|
+
"@codemirror/state": "^6.6.0",
|
|
9298
|
+
"@codemirror/view": "^6.43.0",
|
|
9299
|
+
"@commander-js/extra-typings": "^15.0.0",
|
|
9300
|
+
"@happy-dom/global-registrator": "^20.11.2",
|
|
9301
|
+
"@internationalized/date": "^3.12.2",
|
|
9302
|
+
"@laststance/tailwind-suggest-canonical-classes": "^0.1.1",
|
|
9303
|
+
"@lezer/highlight": "^1.2.3",
|
|
9304
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
9305
|
+
"@pierre/diffs": "1.3.6",
|
|
9306
|
+
"@pierre/trees": "1.0.0-beta.6",
|
|
9307
|
+
"@playwright/test": "^1.62.1",
|
|
9308
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
9309
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
9310
|
+
"@types/bun": "^1.4.0",
|
|
9311
|
+
"@types/semver": "^7.7.1",
|
|
9312
|
+
"@typescript/native": "npm:typescript@^7.0.2",
|
|
9313
|
+
"bits-ui": "^2.18.1",
|
|
9314
|
+
clsx: "^2.1.1",
|
|
9315
|
+
commander: "^15.0.0",
|
|
9316
|
+
cuelume: "^0.2.2",
|
|
9317
|
+
"happy-dom": "^20.11.2",
|
|
9318
|
+
"jsonc-parser": "^3.3.1",
|
|
9319
|
+
listr2: "^11.0.0",
|
|
9320
|
+
marked: "^18.0.4",
|
|
9321
|
+
pino: "^10.3.1",
|
|
9322
|
+
"pino-pretty": "^13.1.3",
|
|
9323
|
+
semver: "^7.8.1",
|
|
9324
|
+
shiki: "^4.4.3",
|
|
9325
|
+
"smol-toml": "^1.8.0",
|
|
9326
|
+
svelte: "^5.56.9",
|
|
9327
|
+
"svelte-check": "^4.7.5",
|
|
9328
|
+
"tailwind-merge": "^3.6.0",
|
|
9329
|
+
"tailwind-variants": "^3.2.2",
|
|
9330
|
+
tailwindcss: "^4.3.3",
|
|
9331
|
+
"tw-animate-css": "^1.4.0",
|
|
9332
|
+
typescript: "^6.0.3",
|
|
9333
|
+
vite: "^8.2.1",
|
|
9334
|
+
xss: "^1.0.15",
|
|
9335
|
+
zod: "^4.4.3"
|
|
9336
|
+
},
|
|
9337
|
+
dependencies: {
|
|
9338
|
+
"@opencode-ai/plugin": "^1.18.17"
|
|
9339
|
+
},
|
|
9340
|
+
pinned: {
|
|
9341
|
+
"//": "bun.lock is committed, so it is what pins every dependency exactly; a range above is the upgrade policy, not the resolved version. A bare version therefore means `bun update` must never move this one on its own, and these entries are the whole set. Everything else takes a range deliberately \u2014 zod and the TOML parser reading the config.toml trust boundary included: the lockfile already holds them, so a range costs no reproducibility and keeps them visible to an upgrade sweep. test/structure/exact-pin.test.ts fails on an exact pin with no reason, and on a reason that outlived its pin.",
|
|
9342
|
+
"@pierre/diffs": "Version-coupled to shiki through the resolve.alias block in ui/vite.config.ts, where an alias that stops matching grows the bundle by ~600 KB. That growth is no longer silent \u2014 the caret-bundle-budget plugin fails `vite build` against the recorded budget in ui/bundle-budget.ts \u2014 but the gate reports the size, not the cause, so the pair still moves as one reviewed bump, never on a sweep.",
|
|
9343
|
+
"@pierre/trees": "No stable release exists \u2014 the `latest` and `beta` dist-tags both point at the same prerelease. Exact rather than caret because a prerelease under a caret range is still a caret range: `^1.0.0-beta.6` expands to `>=1.0.0-beta.6 <2.0.0-0`, so a sweep would take a stable 1.0.0 the day it lands, and beta-to-stable is the jump that wants a human."
|
|
9344
|
+
},
|
|
9345
|
+
held: {
|
|
9346
|
+
"//": "A dependency whose range deliberately stops below the current major, with the evidence for that and the condition that lifts it. Distinct from `pinned` above, which is about exact versions: an entry here names a range that is doing its job by excluding something. Unlike a pin, a held range is self-describing to `bun outdated` \u2014 it shows up as a major behind \u2014 so this block answers the question it raises rather than surfacing a decision nothing else would show.",
|
|
9347
|
+
typescript: "NOT a hold on adoption \u2014 TypeScript 7 IS what type-checks this tree (EXC-1089). It arrives as the `@typescript/native` alias above; hk.pkl runs the backend program through node_modules/@typescript/native/bin/tsc and the UI program through `svelte-check --tsgo`, and both were proved live against injected type errors rather than trusted because they exited 0. This range stops at ^6 because svelte-check requires BOTH majors installed: 4.7.6 hard-throws from bin/ts-version-check.js once `typescript` is major >= 7, and the recipe it throws is the arrangement used here verbatim \u2014 `typescript@~6 @typescript/native@npm:typescript@7`, plus --tsgo. Its README repeats it and dist/src/index.js resolves `@typescript/native` before `@typescript/native-preview`, so this is upstream's supported setup rather than a workaround. A second reason keeps 6.x honest even so: the test/structure/ suites that import `typescript` use its compiler API as a PARSING LIBRARY, and 7.x exports lib/version.cjs as `.` with the API behind ./unstable/* subpaths that offer no standalone parse (unstable/ast's createSourceFile is a factory over Statement[]; the only SourceFile producer is Program.getSourceFile). With 6.x installed those suites need no port, and `tsc` 7 type-checks them clean \u2014 the 50 errors EXC-1083 measured were never caret's code, only two files typed against a package whose `.` export had moved. What lifts this: a svelte-check release whose peerDependencies.typescript admits ^7 \u2014 but that clears only the first reason. Collapsing to a single `typescript@7` means dropping the alias, moving this range to ^7, AND porting those suites off the compiler API in the same pass, or they break on `ts.ScriptTarget` being undefined. test/structure/typescript-arrangement.test.ts reds on exactly that widening, and also pins the tsc path, the --tsgo flag, and this entry's own existence, because losing any of them silently reverts the adoption. Full evidence: EXC-1089, and the trial in EXC-1083."
|
|
9348
|
+
}
|
|
9349
|
+
};
|
|
9350
|
+
});
|
|
9351
|
+
|
|
9352
|
+
// src/lib/upstream.ts
|
|
9353
|
+
async function readJson(url, fetchImpl, headers) {
|
|
9354
|
+
try {
|
|
9355
|
+
const res = await fetchImpl(url, {
|
|
9356
|
+
signal: AbortSignal.timeout(UPSTREAM_TIMEOUT_MS),
|
|
9357
|
+
...headers ? { headers } : {}
|
|
9358
|
+
});
|
|
9359
|
+
return res.ok ? await res.json() : null;
|
|
9360
|
+
} catch {
|
|
9361
|
+
return null;
|
|
9362
|
+
}
|
|
9363
|
+
}
|
|
9364
|
+
async function publishedCaretVersion(fetchImpl = fetch) {
|
|
9365
|
+
const body = await readJson(NPM_LATEST_URL, fetchImpl);
|
|
9366
|
+
const v = body?.version;
|
|
9367
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
9368
|
+
}
|
|
9369
|
+
async function latestReleaseTag(fetchImpl = fetch) {
|
|
9370
|
+
const body = await readJson(LATEST_RELEASE_URL, fetchImpl, GITHUB_HEADERS);
|
|
9371
|
+
const tag = body?.tag_name;
|
|
9372
|
+
if (typeof tag !== "string" || tag.length === 0)
|
|
9373
|
+
return null;
|
|
9374
|
+
const stripped = tag.replace(/^v/, "");
|
|
9375
|
+
return stripped.length > 0 ? stripped : null;
|
|
9376
|
+
}
|
|
9377
|
+
async function commitsAheadOfTrunk(commit, fetchImpl = fetch) {
|
|
9378
|
+
const url = `${COMPARE_URL}/${encodeURIComponent(commit)}...trunk`;
|
|
9379
|
+
const body = await readJson(url, fetchImpl, GITHUB_HEADERS);
|
|
9380
|
+
const ahead = body?.ahead_by;
|
|
9381
|
+
return typeof ahead === "number" && Number.isFinite(ahead) ? ahead : null;
|
|
9382
|
+
}
|
|
9383
|
+
async function readParsed(url, schema, fetchImpl) {
|
|
9384
|
+
const parsed = schema.safeParse(await readJson(url, fetchImpl, GITHUB_HEADERS));
|
|
9385
|
+
return parsed.success ? parsed.data : null;
|
|
9386
|
+
}
|
|
9387
|
+
function listReleases(fetchImpl = fetch) {
|
|
9388
|
+
return readParsed(RELEASES_URL, array(ReleaseSchema), fetchImpl);
|
|
9389
|
+
}
|
|
9390
|
+
function compareToTrunk(commit, fetchImpl = fetch) {
|
|
9391
|
+
return readParsed(`${COMPARE_URL}/${encodeURIComponent(commit)}...trunk`, CompareSchema, fetchImpl);
|
|
9392
|
+
}
|
|
9393
|
+
var NPM_LATEST_URL, LATEST_RELEASE_URL = "https://api.github.com/repos/macintacos/caret/releases/latest", COMPARE_URL = "https://api.github.com/repos/macintacos/caret/compare", RELEASES_URL = "https://api.github.com/repos/macintacos/caret/releases?per_page=100", UPSTREAM_TIMEOUT_MS = 3000, GITHUB_HEADERS, ReleaseSchema, CommitSchema, CompareSchema;
|
|
9394
|
+
var init_upstream = __esm(() => {
|
|
9395
|
+
init_zod();
|
|
9396
|
+
init_package();
|
|
9397
|
+
NPM_LATEST_URL = `https://registry.npmjs.org/${package_default.name}/latest`;
|
|
9398
|
+
GITHUB_HEADERS = { "user-agent": "caret" };
|
|
9399
|
+
ReleaseSchema = object({
|
|
9400
|
+
tag_name: string2(),
|
|
9401
|
+
body: string2().nullable(),
|
|
9402
|
+
draft: boolean2(),
|
|
9403
|
+
prerelease: boolean2()
|
|
9404
|
+
});
|
|
9405
|
+
CommitSchema = object({
|
|
9406
|
+
sha: string2(),
|
|
9407
|
+
commit: object({ message: string2() })
|
|
9408
|
+
});
|
|
9409
|
+
CompareSchema = object({
|
|
9410
|
+
total_commits: number2(),
|
|
9411
|
+
commits: array(CommitSchema)
|
|
9412
|
+
});
|
|
9413
|
+
});
|
|
9414
|
+
|
|
9415
|
+
// src/adapters/opencode/upgrade.ts
|
|
9416
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, rmSync } from "fs";
|
|
9417
|
+
import { join as join6 } from "path";
|
|
9418
|
+
function upgradeVerdict(input) {
|
|
9419
|
+
const { entry, cached, published } = input;
|
|
9420
|
+
if (published === null) {
|
|
9421
|
+
return { kind: "unknown", reason: "could not reach npm for the published caret version" };
|
|
9422
|
+
}
|
|
9423
|
+
if (parseVersionTriple(published) === null) {
|
|
9424
|
+
return { kind: "unknown", reason: `npm reported an unreadable version (${published})` };
|
|
9425
|
+
}
|
|
9426
|
+
if (entry === null)
|
|
9427
|
+
return { kind: "fresh" };
|
|
9428
|
+
const pinned = splitPluginSpecifier(entry).version;
|
|
9429
|
+
if (pinned !== null && parseVersionTriple(pinned) !== null) {
|
|
9430
|
+
return isNewer(published, pinned) ? { kind: "stale-pin", entry, pinned, published } : { kind: "current", version: pinned };
|
|
9431
|
+
}
|
|
9432
|
+
if (cached === null)
|
|
9433
|
+
return { kind: "fresh" };
|
|
9434
|
+
if (parseVersionTriple(cached) === null) {
|
|
9435
|
+
return { kind: "unknown", reason: `OpenCode cached an unreadable version (${cached})` };
|
|
9436
|
+
}
|
|
9437
|
+
return isNewer(published, cached) ? { kind: "stale-cache", cached, published } : { kind: "current", version: cached };
|
|
9438
|
+
}
|
|
9439
|
+
function upgradeVerdictLine(verdict) {
|
|
9440
|
+
switch (verdict.kind) {
|
|
9441
|
+
case "fresh":
|
|
9442
|
+
return "OpenCode will resolve caret on its next start";
|
|
9443
|
+
case "current":
|
|
9444
|
+
return `OpenCode's caret is ${verdict.version} \u2014 already current`;
|
|
9445
|
+
case "stale-cache":
|
|
9446
|
+
return `OpenCode's cached caret is ${verdict.cached}; ${verdict.published} is published`;
|
|
9447
|
+
case "stale-pin":
|
|
9448
|
+
return `Your config pins ${verdict.entry}; ${verdict.published} is published`;
|
|
9449
|
+
case "unknown":
|
|
9450
|
+
return "Could not check which caret OpenCode would load";
|
|
9451
|
+
}
|
|
9452
|
+
}
|
|
9453
|
+
function upgradeCheck(verdict) {
|
|
9454
|
+
const base = {
|
|
9455
|
+
id: "opencode-caret-version",
|
|
9456
|
+
title: "OpenCode's caret",
|
|
9457
|
+
detail: upgradeVerdictLine(verdict)
|
|
9458
|
+
};
|
|
9459
|
+
switch (verdict.kind) {
|
|
9460
|
+
case "unknown":
|
|
9461
|
+
return { ...base, status: "unknown", reason: verdict.reason };
|
|
9462
|
+
case "stale-cache":
|
|
9463
|
+
return {
|
|
9464
|
+
...base,
|
|
9465
|
+
status: "fail",
|
|
9466
|
+
remedy: "run `caret install --refresh` to clear the cached copy"
|
|
9467
|
+
};
|
|
9468
|
+
case "stale-pin":
|
|
9469
|
+
return { ...base, status: "fail", remedy: "run `caret install --refresh` to bump the pin" };
|
|
9470
|
+
case "fresh":
|
|
9471
|
+
case "current":
|
|
9472
|
+
return { ...base, status: "pass" };
|
|
9473
|
+
}
|
|
9474
|
+
}
|
|
9475
|
+
async function readUpgradeVerdict(deps) {
|
|
9476
|
+
return upgradeVerdict({
|
|
9477
|
+
entry: findPluginEntry(readConfigText(deps.configFile), CARET_PACKAGE),
|
|
9478
|
+
cached: readCachedCaretVersion((deps.cacheDirs ?? existingOpencodeCachePackageDirs)()),
|
|
9479
|
+
published: await (deps.published ?? publishedCaretVersion)()
|
|
9480
|
+
});
|
|
9481
|
+
}
|
|
9482
|
+
function hasCaretPluginEntry(configFile) {
|
|
9483
|
+
return findPluginEntry(readConfigText(configFile), CARET_PACKAGE) !== null;
|
|
9484
|
+
}
|
|
9485
|
+
function readConfigText(path) {
|
|
9486
|
+
return existsSync2(path) ? readFileSync3(path, "utf-8") : null;
|
|
9487
|
+
}
|
|
9488
|
+
function readCachedCaretVersion(dirs = existingOpencodeCachePackageDirs()) {
|
|
9489
|
+
for (const d of dirs) {
|
|
9490
|
+
try {
|
|
9491
|
+
const deps = JSON.parse(readFileSync3(join6(d, "package.json"), "utf-8")).dependencies;
|
|
9492
|
+
const v = deps?.[CARET_PACKAGE];
|
|
9493
|
+
if (typeof v === "string" && v.length > 0)
|
|
9494
|
+
return v;
|
|
9495
|
+
} catch {}
|
|
9496
|
+
}
|
|
9497
|
+
return null;
|
|
9498
|
+
}
|
|
9499
|
+
function clearCachedCaret(dirs = existingOpencodeCachePackageDirs()) {
|
|
9500
|
+
const cleared = [];
|
|
9501
|
+
for (const d of dirs) {
|
|
9502
|
+
if (!existsSync2(d))
|
|
9503
|
+
continue;
|
|
9504
|
+
rmSync(d, { recursive: true, force: true });
|
|
9505
|
+
cleared.push(d);
|
|
9506
|
+
}
|
|
9507
|
+
return cleared;
|
|
9508
|
+
}
|
|
9509
|
+
var init_upgrade = __esm(() => {
|
|
9510
|
+
init_config_plugin();
|
|
9511
|
+
init_paths2();
|
|
9512
|
+
init_upstream();
|
|
9513
|
+
});
|
|
9514
|
+
|
|
9515
|
+
// src/adapters/opencode/install.ts
|
|
9516
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
9517
|
+
import { join as join7 } from "path";
|
|
9518
|
+
function readOpencodeInstallState() {
|
|
9519
|
+
const dir = opencodeConfigDir();
|
|
9520
|
+
if (!existsSync3(dir)) {
|
|
9521
|
+
return { pluginVersion: "unknown", pluginEnabled: "unknown", hookInUserSettings: "unknown" };
|
|
9522
|
+
}
|
|
9523
|
+
const version = readCachedCaretVersion(existingOpencodeCachePackageDirs()) ?? "unknown";
|
|
9524
|
+
return {
|
|
9525
|
+
pluginVersion: version,
|
|
9526
|
+
pluginEnabled: version !== "unknown",
|
|
9527
|
+
hookInUserSettings: readCaretInPluginArray(dir)
|
|
9528
|
+
};
|
|
9529
|
+
}
|
|
9530
|
+
function readCaretInPluginArray(dir) {
|
|
9531
|
+
let sawConfig = false;
|
|
9532
|
+
for (const name of CONFIG_FILENAMES) {
|
|
9533
|
+
const path = join7(dir, name);
|
|
9534
|
+
if (!existsSync3(path))
|
|
9535
|
+
continue;
|
|
9536
|
+
let cfg;
|
|
9537
|
+
try {
|
|
9538
|
+
cfg = parse3(readFileSync4(path, "utf-8"));
|
|
9539
|
+
} catch {
|
|
9540
|
+
continue;
|
|
9541
|
+
}
|
|
9542
|
+
if (cfg === undefined || cfg === null)
|
|
9543
|
+
continue;
|
|
9544
|
+
sawConfig = true;
|
|
9545
|
+
const arr = cfg.plugin;
|
|
9546
|
+
if (Array.isArray(arr) && arr.some((e) => typeof e === "string" && e.includes("caret"))) {
|
|
9547
|
+
return true;
|
|
9548
|
+
}
|
|
9549
|
+
}
|
|
9550
|
+
return sawConfig ? false : "unknown";
|
|
9551
|
+
}
|
|
9552
|
+
var init_install3 = __esm(() => {
|
|
9553
|
+
init_main();
|
|
9554
|
+
init_paths2();
|
|
9555
|
+
init_upgrade();
|
|
9556
|
+
});
|
|
9557
|
+
|
|
9558
|
+
// src/adapters/opencode/skills.ts
|
|
9559
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
9560
|
+
import { sep as sep2 } from "path";
|
|
9561
|
+
async function commandNamesUnder(dir) {
|
|
9562
|
+
let entries;
|
|
9563
|
+
try {
|
|
9564
|
+
entries = await readdir2(dir, { recursive: true });
|
|
9565
|
+
} catch {
|
|
9566
|
+
return [];
|
|
9567
|
+
}
|
|
9568
|
+
return entries.filter((rel) => rel.endsWith(".md")).map((rel) => rel.slice(0, -".md".length).split(sep2).join("/"));
|
|
9569
|
+
}
|
|
9570
|
+
async function readOpencodeCommands() {
|
|
9571
|
+
const perDir = await Promise.all(commandDirs(opencodeConfigDir()).map(commandNamesUnder));
|
|
9572
|
+
const names = new Set(perDir.flat());
|
|
9573
|
+
return [...names].sort().map((name) => ({ name, origin: "command" }));
|
|
9574
|
+
}
|
|
9575
|
+
async function readOpencodeCommandDescription(name) {
|
|
9576
|
+
const relative = `${name}.md`;
|
|
9577
|
+
for (const dir of commandDirs(opencodeConfigDir())) {
|
|
9578
|
+
const abs = await containedFile(dir, relative);
|
|
9579
|
+
if (abs !== null)
|
|
9580
|
+
return descriptionOf(abs);
|
|
9581
|
+
}
|
|
9582
|
+
return null;
|
|
9583
|
+
}
|
|
9584
|
+
var init_skills2 = __esm(() => {
|
|
9585
|
+
init_paths2();
|
|
9586
|
+
init_skill_doc();
|
|
9587
|
+
});
|
|
9588
|
+
|
|
9589
|
+
// src/adapters/opencode/index.ts
|
|
9590
|
+
var opencodeAdapter;
|
|
9591
|
+
var init_opencode = __esm(() => {
|
|
9592
|
+
init_approve3();
|
|
9593
|
+
init_feedback3();
|
|
9594
|
+
init_install3();
|
|
9595
|
+
init_skills2();
|
|
9596
|
+
opencodeAdapter = {
|
|
9597
|
+
id: "opencode",
|
|
9598
|
+
approveVariants: APPROVE_VARIANTS3,
|
|
9599
|
+
restartHint: "Then restart OpenCode.",
|
|
9600
|
+
parseHookInput(stdin) {
|
|
9601
|
+
const hook = parseHookStdin(stdin);
|
|
9602
|
+
return {
|
|
9603
|
+
sessionId: hook.session_id,
|
|
9604
|
+
cwd: hook.cwd,
|
|
9605
|
+
plan: hook.tool_input?.plan,
|
|
9606
|
+
planFilePath: hook.tool_input?.planFilePath
|
|
9607
|
+
};
|
|
9608
|
+
},
|
|
9609
|
+
emitDecision(decision) {
|
|
9610
|
+
return JSON.stringify(toWireDecision(decision));
|
|
9611
|
+
},
|
|
9612
|
+
fatalDenyLine,
|
|
9613
|
+
readInstallState() {
|
|
9614
|
+
return readOpencodeInstallState();
|
|
9615
|
+
},
|
|
9616
|
+
listSkills() {
|
|
9617
|
+
return readOpencodeCommands();
|
|
9618
|
+
},
|
|
9619
|
+
readSkillDescription(_cwd, skill) {
|
|
9620
|
+
return readOpencodeCommandDescription(skill.name);
|
|
9621
|
+
}
|
|
9622
|
+
};
|
|
9623
|
+
});
|
|
9624
|
+
|
|
9625
|
+
// src/adapters/index.ts
|
|
9626
|
+
function agentIds() {
|
|
9627
|
+
return Object.keys(REGISTRY);
|
|
9628
|
+
}
|
|
9629
|
+
function selectAdapter(id) {
|
|
9630
|
+
const raw = (id ?? process.env.CARET_AGENT ?? "").trim();
|
|
9631
|
+
const key = raw === "" ? DEFAULT_AGENT : raw;
|
|
9632
|
+
const adapter = REGISTRY[key];
|
|
9633
|
+
if (!adapter) {
|
|
9634
|
+
throw new Error(`unknown agent adapter: "${key}" (known: ${agentIds().join(", ")})`);
|
|
9635
|
+
}
|
|
9636
|
+
return adapter;
|
|
9637
|
+
}
|
|
9638
|
+
function fatalDeny(reason) {
|
|
9639
|
+
let adapter;
|
|
9640
|
+
try {
|
|
9641
|
+
adapter = selectAdapter();
|
|
9642
|
+
} catch {}
|
|
9643
|
+
if (adapter) {
|
|
9644
|
+
try {
|
|
9645
|
+
return adapter.emitDecision({ behavior: "deny", feedback: reason, decidedAt: Date.now() });
|
|
9646
|
+
} catch {}
|
|
9647
|
+
try {
|
|
9648
|
+
return adapter.fatalDenyLine(reason);
|
|
9649
|
+
} catch {}
|
|
9650
|
+
}
|
|
9651
|
+
return permissionRequestDenyLine(reason);
|
|
9652
|
+
}
|
|
9653
|
+
var DEFAULT_AGENT = "claude", CLAUDE_MCP_AGENT = "claude-mcp", REGISTRY;
|
|
9654
|
+
var init_adapters = __esm(() => {
|
|
9655
|
+
init_claude();
|
|
9656
|
+
init_codex();
|
|
9657
|
+
init_opencode();
|
|
9658
|
+
REGISTRY = {
|
|
9659
|
+
claude: claudeAdapter,
|
|
9660
|
+
codex: codexAdapter,
|
|
9661
|
+
opencode: opencodeAdapter,
|
|
9662
|
+
[CLAUDE_MCP_AGENT]: {
|
|
9663
|
+
...claudeAdapter,
|
|
9664
|
+
id: CLAUDE_MCP_AGENT,
|
|
9665
|
+
parseHookInput: opencodeAdapter.parseHookInput,
|
|
9666
|
+
emitDecision: opencodeAdapter.emitDecision,
|
|
9667
|
+
fatalDenyLine: opencodeAdapter.fatalDenyLine
|
|
9668
|
+
}
|
|
9669
|
+
};
|
|
9670
|
+
});
|
|
9671
|
+
|
|
9296
9672
|
// src/config/constants.ts
|
|
9297
9673
|
function hasKnownFileExtension(path) {
|
|
9298
9674
|
const base = path.split("/").pop() ?? "";
|
|
@@ -9353,6 +9729,7 @@ var init_constants = __esm(() => {
|
|
|
9353
9729
|
"php",
|
|
9354
9730
|
"swift",
|
|
9355
9731
|
"dart",
|
|
9732
|
+
"lua",
|
|
9356
9733
|
"txt",
|
|
9357
9734
|
"lock",
|
|
9358
9735
|
"cfg",
|
|
@@ -9428,6 +9805,9 @@ function daemonStderrLogFile() {
|
|
|
9428
9805
|
function daemonLock() {
|
|
9429
9806
|
return `${stateDir()}/daemon.lock`;
|
|
9430
9807
|
}
|
|
9808
|
+
function daemonBootMarker() {
|
|
9809
|
+
return `${stateDir()}/daemon.boot`;
|
|
9810
|
+
}
|
|
9431
9811
|
function ensureStateDir(target = stateDir()) {
|
|
9432
9812
|
mkdirSync(target, { recursive: true, mode: 448 });
|
|
9433
9813
|
chmodSync(target, 448);
|
|
@@ -9513,109 +9893,6 @@ async function loadUiAssets() {
|
|
|
9513
9893
|
}
|
|
9514
9894
|
var init_assets = () => {};
|
|
9515
9895
|
|
|
9516
|
-
// package.json
|
|
9517
|
-
var package_default;
|
|
9518
|
-
var init_package = __esm(() => {
|
|
9519
|
-
package_default = {
|
|
9520
|
-
name: "@macintacos/caret",
|
|
9521
|
-
version: "1.0.2",
|
|
9522
|
-
license: "MIT",
|
|
9523
|
-
type: "module",
|
|
9524
|
-
description: "A Claude Code plugin that replaces the terminal plan-approval prompt with a local web UI for inline review, annotation, and approval.",
|
|
9525
|
-
main: "./opencode/index.ts",
|
|
9526
|
-
exports: {
|
|
9527
|
-
".": "./opencode/index.ts"
|
|
9528
|
-
},
|
|
9529
|
-
bin: {
|
|
9530
|
-
caret: "./bin/caret"
|
|
9531
|
-
},
|
|
9532
|
-
homepage: "https://github.com/macintacos/caret",
|
|
9533
|
-
repository: {
|
|
9534
|
-
type: "git",
|
|
9535
|
-
url: "git+https://github.com/macintacos/caret.git"
|
|
9536
|
-
},
|
|
9537
|
-
publishConfig: {
|
|
9538
|
-
access: "public"
|
|
9539
|
-
},
|
|
9540
|
-
files: [
|
|
9541
|
-
"dist/",
|
|
9542
|
-
"ui/dist/",
|
|
9543
|
-
"bin/caret",
|
|
9544
|
-
"bin/caret-launcher",
|
|
9545
|
-
"hooks/",
|
|
9546
|
-
"commands/",
|
|
9547
|
-
"opencode/",
|
|
9548
|
-
"templates/",
|
|
9549
|
-
".claude-plugin/plugin.json"
|
|
9550
|
-
],
|
|
9551
|
-
scripts: {
|
|
9552
|
-
test: "bun test --conditions browser --parallel --timeout 35000",
|
|
9553
|
-
lint: "mise run lint",
|
|
9554
|
-
format: "mise run format",
|
|
9555
|
-
build: "mise run build"
|
|
9556
|
-
},
|
|
9557
|
-
devDependencies: {
|
|
9558
|
-
"@clack/prompts": "^1.7.0",
|
|
9559
|
-
"@codemirror/autocomplete": "^6.20.3",
|
|
9560
|
-
"@codemirror/commands": "^6.10.3",
|
|
9561
|
-
"@codemirror/lang-markdown": "^6.5.0",
|
|
9562
|
-
"@codemirror/language": "^6.12.3",
|
|
9563
|
-
"@codemirror/language-data": "^6.5.2",
|
|
9564
|
-
"@codemirror/state": "^6.6.0",
|
|
9565
|
-
"@codemirror/view": "^6.43.0",
|
|
9566
|
-
"@commander-js/extra-typings": "^15.0.0",
|
|
9567
|
-
"@happy-dom/global-registrator": "^20.11.2",
|
|
9568
|
-
"@internationalized/date": "^3.12.2",
|
|
9569
|
-
"@laststance/tailwind-suggest-canonical-classes": "^0.1.1",
|
|
9570
|
-
"@lezer/highlight": "^1.2.3",
|
|
9571
|
-
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
9572
|
-
"@pierre/diffs": "1.3.6",
|
|
9573
|
-
"@pierre/trees": "1.0.0-beta.6",
|
|
9574
|
-
"@playwright/test": "^1.62.1",
|
|
9575
|
-
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
9576
|
-
"@tailwindcss/vite": "^4.3.3",
|
|
9577
|
-
"@types/bun": "^1.4.0",
|
|
9578
|
-
"@types/semver": "^7.7.1",
|
|
9579
|
-
"@typescript/native": "npm:typescript@^7.0.2",
|
|
9580
|
-
"bits-ui": "^2.18.1",
|
|
9581
|
-
clsx: "^2.1.1",
|
|
9582
|
-
commander: "^15.0.0",
|
|
9583
|
-
cuelume: "^0.2.2",
|
|
9584
|
-
"happy-dom": "^20.11.2",
|
|
9585
|
-
"jsonc-parser": "^3.3.1",
|
|
9586
|
-
listr2: "^11.0.0",
|
|
9587
|
-
marked: "^18.0.4",
|
|
9588
|
-
pino: "^10.3.1",
|
|
9589
|
-
"pino-pretty": "^13.1.3",
|
|
9590
|
-
semver: "^7.8.1",
|
|
9591
|
-
shiki: "^4.4.3",
|
|
9592
|
-
"smol-toml": "^1.8.0",
|
|
9593
|
-
svelte: "^5.56.9",
|
|
9594
|
-
"svelte-check": "^4.7.5",
|
|
9595
|
-
"tailwind-merge": "^3.6.0",
|
|
9596
|
-
"tailwind-variants": "^3.2.2",
|
|
9597
|
-
tailwindcss: "^4.3.3",
|
|
9598
|
-
"tw-animate-css": "^1.4.0",
|
|
9599
|
-
typescript: "^6.0.3",
|
|
9600
|
-
vite: "^8.2.1",
|
|
9601
|
-
xss: "^1.0.15",
|
|
9602
|
-
zod: "^4.4.3"
|
|
9603
|
-
},
|
|
9604
|
-
dependencies: {
|
|
9605
|
-
"@opencode-ai/plugin": "^1.18.17"
|
|
9606
|
-
},
|
|
9607
|
-
pinned: {
|
|
9608
|
-
"//": "bun.lock is committed, so it is what pins every dependency exactly; a range above is the upgrade policy, not the resolved version. A bare version therefore means `bun update` must never move this one on its own, and these entries are the whole set. Everything else takes a range deliberately \u2014 zod and the TOML parser reading the config.toml trust boundary included: the lockfile already holds them, so a range costs no reproducibility and keeps them visible to an upgrade sweep. test/structure/exact-pin.test.ts fails on an exact pin with no reason, and on a reason that outlived its pin.",
|
|
9609
|
-
"@pierre/diffs": "Version-coupled to shiki through the resolve.alias block in ui/vite.config.ts, where an alias that stops matching grows the bundle by ~600 KB. That growth is no longer silent \u2014 the caret-bundle-budget plugin fails `vite build` against the recorded budget in ui/bundle-budget.ts \u2014 but the gate reports the size, not the cause, so the pair still moves as one reviewed bump, never on a sweep.",
|
|
9610
|
-
"@pierre/trees": "No stable release exists \u2014 the `latest` and `beta` dist-tags both point at the same prerelease. Exact rather than caret because a prerelease under a caret range is still a caret range: `^1.0.0-beta.6` expands to `>=1.0.0-beta.6 <2.0.0-0`, so a sweep would take a stable 1.0.0 the day it lands, and beta-to-stable is the jump that wants a human."
|
|
9611
|
-
},
|
|
9612
|
-
held: {
|
|
9613
|
-
"//": "A dependency whose range deliberately stops below the current major, with the evidence for that and the condition that lifts it. Distinct from `pinned` above, which is about exact versions: an entry here names a range that is doing its job by excluding something. Unlike a pin, a held range is self-describing to `bun outdated` \u2014 it shows up as a major behind \u2014 so this block answers the question it raises rather than surfacing a decision nothing else would show.",
|
|
9614
|
-
typescript: "NOT a hold on adoption \u2014 TypeScript 7 IS what type-checks this tree (EXC-1089). It arrives as the `@typescript/native` alias above; hk.pkl runs the backend program through node_modules/@typescript/native/bin/tsc and the UI program through `svelte-check --tsgo`, and both were proved live against injected type errors rather than trusted because they exited 0. This range stops at ^6 because svelte-check requires BOTH majors installed: 4.7.6 hard-throws from bin/ts-version-check.js once `typescript` is major >= 7, and the recipe it throws is the arrangement used here verbatim \u2014 `typescript@~6 @typescript/native@npm:typescript@7`, plus --tsgo. Its README repeats it and dist/src/index.js resolves `@typescript/native` before `@typescript/native-preview`, so this is upstream's supported setup rather than a workaround. A second reason keeps 6.x honest even so: the test/structure/ suites that import `typescript` use its compiler API as a PARSING LIBRARY, and 7.x exports lib/version.cjs as `.` with the API behind ./unstable/* subpaths that offer no standalone parse (unstable/ast's createSourceFile is a factory over Statement[]; the only SourceFile producer is Program.getSourceFile). With 6.x installed those suites need no port, and `tsc` 7 type-checks them clean \u2014 the 50 errors EXC-1083 measured were never caret's code, only two files typed against a package whose `.` export had moved. What lifts this: a svelte-check release whose peerDependencies.typescript admits ^7 \u2014 but that clears only the first reason. Collapsing to a single `typescript@7` means dropping the alias, moving this range to ^7, AND porting those suites off the compiler API in the same pass, or they break on `ts.ScriptTarget` being undefined. test/structure/typescript-arrangement.test.ts reds on exactly that widening, and also pins the tsc path, the --tsgo flag, and this entry's own existence, because losing any of them silently reverts the adoption. Full evidence: EXC-1089, and the trial in EXC-1083."
|
|
9615
|
-
}
|
|
9616
|
-
};
|
|
9617
|
-
});
|
|
9618
|
-
|
|
9619
9896
|
// src/lib/build-id.ts
|
|
9620
9897
|
import { createHash } from "crypto";
|
|
9621
9898
|
function buildKind(argv1 = process.argv[1]) {
|
|
@@ -11641,7 +11918,7 @@ var require_indexes = __commonJS(function(exports, module) {
|
|
|
11641
11918
|
|
|
11642
11919
|
// node_modules/.bun/thread-stream@4.2.0/node_modules/thread-stream/index.js
|
|
11643
11920
|
var require_thread_stream = __commonJS(function(exports, module) {
|
|
11644
|
-
var __dirname = "/Users/juliantorres/GitLocal/Play/caret/
|
|
11921
|
+
var __dirname = "/Users/juliantorres/GitLocal/Play/caret/node_modules/.bun/thread-stream@4.2.0/node_modules/thread-stream";
|
|
11645
11922
|
var { version } = require_package();
|
|
11646
11923
|
var { EventEmitter } = __require("events");
|
|
11647
11924
|
var { Worker } = __require("worker_threads");
|
|
@@ -12157,7 +12434,7 @@ var require_thread_stream = __commonJS(function(exports, module) {
|
|
|
12157
12434
|
|
|
12158
12435
|
// node_modules/.bun/pino@10.3.1/node_modules/pino/lib/transport.js
|
|
12159
12436
|
var require_transport = __commonJS(function(exports, module) {
|
|
12160
|
-
var __dirname = "/Users/juliantorres/GitLocal/Play/caret/
|
|
12437
|
+
var __dirname = "/Users/juliantorres/GitLocal/Play/caret/node_modules/.bun/pino@10.3.1/node_modules/pino/lib";
|
|
12161
12438
|
var { createRequire } = __require("module");
|
|
12162
12439
|
var { existsSync } = __require("fs");
|
|
12163
12440
|
var getCallers = require_caller();
|
|
@@ -14294,9 +14571,10 @@ var init_node = __esm(() => {
|
|
|
14294
14571
|
});
|
|
14295
14572
|
|
|
14296
14573
|
// src/lib/log.ts
|
|
14297
|
-
function wrap(logger,
|
|
14574
|
+
function wrap(logger, opts) {
|
|
14575
|
+
const { liveLevel, liveRedact, source, rotate, bound } = opts;
|
|
14298
14576
|
function fields(extra, step, redact) {
|
|
14299
|
-
const out = scrubValue({ ...extra
|
|
14577
|
+
const out = scrubValue(Object.assign({}, ...bound, extra), redact);
|
|
14300
14578
|
out.step = step;
|
|
14301
14579
|
if (out.source == null) {
|
|
14302
14580
|
out.source = source;
|
|
@@ -14322,17 +14600,19 @@ function wrap(logger, liveLevel, liveRedact, source, rotate) {
|
|
|
14322
14600
|
debug: (step, msg, extra) => emit("debug", step, msg, extra),
|
|
14323
14601
|
info: (step, msg, extra) => emit("info", step, msg, extra),
|
|
14324
14602
|
warn: (step, msg, extra) => emit("warn", step, msg, extra),
|
|
14325
|
-
error(step, err, extra) {
|
|
14603
|
+
error(step, code, err, extra) {
|
|
14326
14604
|
try {
|
|
14327
14605
|
rotate?.();
|
|
14328
14606
|
const r = liveRedact();
|
|
14329
|
-
const
|
|
14607
|
+
const record = fields(extra, step, r);
|
|
14608
|
+
record.code = code;
|
|
14330
14609
|
if (err instanceof Error)
|
|
14331
|
-
|
|
14610
|
+
record.err = scrubValue(import_pino.default.stdSerializers.errWithCause(err), r);
|
|
14332
14611
|
const msg = errorMessage(err);
|
|
14333
|
-
logger.error(
|
|
14612
|
+
logger.error(record, r ? scrubString(msg) : msg);
|
|
14334
14613
|
} catch {}
|
|
14335
|
-
}
|
|
14614
|
+
},
|
|
14615
|
+
child: (ctx) => wrap(logger, { ...opts, bound: [...bound, ctx] })
|
|
14336
14616
|
};
|
|
14337
14617
|
}
|
|
14338
14618
|
function createHookLogger(level, redact, limits) {
|
|
@@ -14341,7 +14621,14 @@ function createHookLogger(level, redact, limits) {
|
|
|
14341
14621
|
ensureLogsDir();
|
|
14342
14622
|
const dest = import_pino.default.destination({ dest: path, sync: true, mode: 384 });
|
|
14343
14623
|
const rotate = () => rotateIfOversized(path, limits.maxSize(), limits.keep());
|
|
14344
|
-
|
|
14624
|
+
const log = wrap(import_pino.default(pinoOpts, dest), {
|
|
14625
|
+
liveLevel: level,
|
|
14626
|
+
liveRedact: redact,
|
|
14627
|
+
source: "hook",
|
|
14628
|
+
rotate,
|
|
14629
|
+
bound: []
|
|
14630
|
+
});
|
|
14631
|
+
return { log, dest, path };
|
|
14345
14632
|
} catch {
|
|
14346
14633
|
return { log: noopLogger, dest: null, path };
|
|
14347
14634
|
}
|
|
@@ -14349,14 +14636,14 @@ function createHookLogger(level, redact, limits) {
|
|
|
14349
14636
|
function hook() {
|
|
14350
14637
|
const path = logFile();
|
|
14351
14638
|
if (hookState.instance && hookState.instance.path === path) {
|
|
14352
|
-
return hookState.instance.log;
|
|
14639
|
+
return hookState.instance.log.child(hookState.context);
|
|
14353
14640
|
}
|
|
14354
14641
|
try {
|
|
14355
14642
|
hookState.instance?.dest?.destroy();
|
|
14356
14643
|
} catch {}
|
|
14357
14644
|
const next = createHookLogger(() => hookState.level, () => hookState.redact, { maxSize: () => hookState.maxSize, keep: () => hookState.keep });
|
|
14358
14645
|
hookState.instance = next.dest ? next : null;
|
|
14359
|
-
return next.log;
|
|
14646
|
+
return next.log.child(hookState.context);
|
|
14360
14647
|
}
|
|
14361
14648
|
function setLogLevel(level) {
|
|
14362
14649
|
hookState.level = level;
|
|
@@ -14368,6 +14655,9 @@ function setLogRotation(maxSize, keep) {
|
|
|
14368
14655
|
hookState.maxSize = maxSize;
|
|
14369
14656
|
hookState.keep = keep;
|
|
14370
14657
|
}
|
|
14658
|
+
function setLogContext(ctx) {
|
|
14659
|
+
hookState.context = { ...ctx };
|
|
14660
|
+
}
|
|
14371
14661
|
function logDebug(step, msg, extra) {
|
|
14372
14662
|
hook().debug(step, msg, extra);
|
|
14373
14663
|
}
|
|
@@ -14377,8 +14667,8 @@ function logInfo(step, msg, extra) {
|
|
|
14377
14667
|
function logWarn(step, msg, extra) {
|
|
14378
14668
|
hook().warn(step, msg, extra);
|
|
14379
14669
|
}
|
|
14380
|
-
function logError(step, err,
|
|
14381
|
-
hook().error(step, err,
|
|
14670
|
+
function logError(step, code, err, extra) {
|
|
14671
|
+
hook().error(step, code, err, extra);
|
|
14382
14672
|
}
|
|
14383
14673
|
function createDaemonLogger(level, dest = daemonLogFile(), redact = () => false, limits) {
|
|
14384
14674
|
try {
|
|
@@ -14390,7 +14680,13 @@ function createDaemonLogger(level, dest = daemonLogFile(), redact = () => false,
|
|
|
14390
14680
|
});
|
|
14391
14681
|
const logger = import_pino.default({ ...pinoOpts, base: { pid: process.pid } }, target);
|
|
14392
14682
|
const rotate = limits && typeof dest === "string" ? () => rotateIfOversized(dest, limits.maxSize(), limits.keep()) : undefined;
|
|
14393
|
-
return wrap(logger,
|
|
14683
|
+
return wrap(logger, {
|
|
14684
|
+
liveLevel: level,
|
|
14685
|
+
liveRedact: redact,
|
|
14686
|
+
source: "daemon",
|
|
14687
|
+
rotate,
|
|
14688
|
+
bound: []
|
|
14689
|
+
});
|
|
14394
14690
|
} catch {
|
|
14395
14691
|
return noopLogger;
|
|
14396
14692
|
}
|
|
@@ -14413,13 +14709,15 @@ var init_log = __esm(() => {
|
|
|
14413
14709
|
debug: () => {},
|
|
14414
14710
|
info: () => {},
|
|
14415
14711
|
warn: () => {},
|
|
14416
|
-
error: () => {}
|
|
14712
|
+
error: () => {},
|
|
14713
|
+
child: () => noopLogger
|
|
14417
14714
|
};
|
|
14418
14715
|
hookState = {
|
|
14419
14716
|
level: "info",
|
|
14420
14717
|
redact: false,
|
|
14421
14718
|
maxSize: DEFAULT_LOG_MAX_SIZE,
|
|
14422
14719
|
keep: DEFAULT_LOG_KEEP,
|
|
14720
|
+
context: {},
|
|
14423
14721
|
instance: null
|
|
14424
14722
|
};
|
|
14425
14723
|
});
|
|
@@ -14438,7 +14736,7 @@ function freeze(s) {
|
|
|
14438
14736
|
}
|
|
14439
14737
|
function logValidationFailure(err) {
|
|
14440
14738
|
const safe = err.issues.map((i) => `${i.path.length ? i.path.join(".") : "(root)"}: ${i.code}`).join("; ");
|
|
14441
|
-
logError("settings", new Error(`config.toml ignored \u2014 invalid: ${safe}`));
|
|
14739
|
+
logError("settings", "config-invalid", new Error(`config.toml ignored \u2014 invalid: ${safe}`));
|
|
14442
14740
|
}
|
|
14443
14741
|
function parseAndValidate(text, isCompiled) {
|
|
14444
14742
|
let raw;
|
|
@@ -14675,18 +14973,24 @@ async function postReview(baseUrl, input) {
|
|
|
14675
14973
|
throw new Error(`POST /api/reviews failed: ${res.status}`);
|
|
14676
14974
|
return await res.json();
|
|
14677
14975
|
}
|
|
14678
|
-
|
|
14679
|
-
|
|
14976
|
+
function versionSearch(version) {
|
|
14977
|
+
return version === undefined ? "" : `?version=${version}`;
|
|
14978
|
+
}
|
|
14979
|
+
async function expireReview(baseUrl, id, version) {
|
|
14980
|
+
const res = await fetch(`${baseUrl}/api/reviews/${id}/expire${versionSearch(version)}`, {
|
|
14680
14981
|
method: "POST",
|
|
14681
14982
|
signal: AbortSignal.timeout(1000)
|
|
14682
14983
|
});
|
|
14683
|
-
if (!res.ok && res.status !== 404)
|
|
14984
|
+
if (!res.ok && res.status !== 404 && res.status !== 409) {
|
|
14684
14985
|
throw new Error(`POST /expire failed: ${res.status}`);
|
|
14986
|
+
}
|
|
14685
14987
|
}
|
|
14686
|
-
async function longPoll(baseUrl, id) {
|
|
14687
|
-
const res = await fetch(`${baseUrl}/api/reviews/${id}/decision`);
|
|
14988
|
+
async function longPoll(baseUrl, id, version) {
|
|
14989
|
+
const res = await fetch(`${baseUrl}/api/reviews/${id}/decision${versionSearch(version)}`);
|
|
14688
14990
|
if (res.status === 204)
|
|
14689
14991
|
return null;
|
|
14992
|
+
if (res.status === 409)
|
|
14993
|
+
return "superseded";
|
|
14690
14994
|
if (!res.ok)
|
|
14691
14995
|
throw new Error(`decision long-poll failed: ${res.status}`);
|
|
14692
14996
|
return await res.json();
|
|
@@ -14714,9 +15018,12 @@ import {
|
|
|
14714
15018
|
chmodSync as chmodSync2,
|
|
14715
15019
|
constants,
|
|
14716
15020
|
existsSync as existsSync6,
|
|
15021
|
+
linkSync,
|
|
14717
15022
|
openSync as openSync2,
|
|
14718
15023
|
readFileSync as readFileSync8,
|
|
14719
|
-
|
|
15024
|
+
renameSync as renameSync2,
|
|
15025
|
+
unlinkSync as unlinkSync2,
|
|
15026
|
+
writeFileSync as writeFileSync3
|
|
14720
15027
|
} from "fs";
|
|
14721
15028
|
import { join as join9, normalize } from "path";
|
|
14722
15029
|
function isAddrInUse(e) {
|
|
@@ -14738,7 +15045,9 @@ async function ensureDaemon(deps, mode = "takeover") {
|
|
|
14738
15045
|
let windowSpent = mode === "successor" ? await awaitDrained(deps, windowEnd) : false;
|
|
14739
15046
|
let supervised;
|
|
14740
15047
|
let spawnedPid;
|
|
15048
|
+
let waitingOn;
|
|
14741
15049
|
for (let attempt = 0;attempt < timing.maxAttempts && timing.now() < deadline; attempt++) {
|
|
15050
|
+
waitingOn = undefined;
|
|
14742
15051
|
windowSpent ||= timing.now() >= windowEnd;
|
|
14743
15052
|
const h = await deps.health(deps.baseUrl);
|
|
14744
15053
|
if (h && h.service === "caret") {
|
|
@@ -14783,17 +15092,27 @@ async function ensureDaemon(deps, mode = "takeover") {
|
|
|
14783
15092
|
deps.removeLock();
|
|
14784
15093
|
logDebug("spawn", "orphan daemon lock removed");
|
|
14785
15094
|
}
|
|
14786
|
-
|
|
15095
|
+
const claim = claimBoot(deps);
|
|
15096
|
+
if (claim.claimed && spawnedPid !== undefined && deps.isAlive(spawnedPid)) {
|
|
15097
|
+
deps.bootMarker.clear();
|
|
15098
|
+
} else if (claim.claimed) {
|
|
14787
15099
|
try {
|
|
14788
15100
|
spawnedPid = deps.spawn();
|
|
15101
|
+
deps.bootMarker.assign(spawnedPid);
|
|
14789
15102
|
logDebug("spawn", "daemon spawned", { pid: spawnedPid });
|
|
14790
15103
|
} catch (e) {
|
|
15104
|
+
deps.bootMarker.clear();
|
|
14791
15105
|
if (!isAddrInUse(e))
|
|
14792
15106
|
throw e;
|
|
14793
15107
|
}
|
|
15108
|
+
} else if (claim.bootPid !== spawnedPid) {
|
|
15109
|
+
waitingOn = claim.bootPid;
|
|
14794
15110
|
}
|
|
14795
15111
|
await timing.backoff(attempt);
|
|
14796
15112
|
}
|
|
15113
|
+
if (waitingOn !== undefined) {
|
|
15114
|
+
logDebug("spawn", "gave up waiting on a booting daemon", { bootPid: waitingOn });
|
|
15115
|
+
}
|
|
14797
15116
|
const final = await deps.health(deps.baseUrl);
|
|
14798
15117
|
if (final && final.service === "caret") {
|
|
14799
15118
|
if (isForeignWorld(final, deps.currentStateDir))
|
|
@@ -14802,6 +15121,22 @@ async function ensureDaemon(deps, mode = "takeover") {
|
|
|
14802
15121
|
}
|
|
14803
15122
|
throw new Error("caret daemon did not become healthy in time");
|
|
14804
15123
|
}
|
|
15124
|
+
function claimBoot(deps) {
|
|
15125
|
+
const { bootMarker, timing } = deps;
|
|
15126
|
+
if (bootMarker.claim({ pid: process.pid, claimedAt: timing.wallNow() })) {
|
|
15127
|
+
return { claimed: true };
|
|
15128
|
+
}
|
|
15129
|
+
const held = bootMarker.read();
|
|
15130
|
+
if (!held)
|
|
15131
|
+
return { claimed: false };
|
|
15132
|
+
const age = timing.wallNow() - held.claimedAt;
|
|
15133
|
+
if (deps.isAlive(held.pid) && age >= 0 && age < BOOT_MARKER_TTL_MS) {
|
|
15134
|
+
return { claimed: false, bootPid: held.pid };
|
|
15135
|
+
}
|
|
15136
|
+
bootMarker.clear();
|
|
15137
|
+
logDebug("spawn", "stale boot marker removed", { bootPid: held.pid });
|
|
15138
|
+
return { claimed: false };
|
|
15139
|
+
}
|
|
14805
15140
|
async function awaitDrained(deps, until) {
|
|
14806
15141
|
const h = await deps.health(deps.baseUrl);
|
|
14807
15142
|
if (h?.service !== "caret")
|
|
@@ -14868,6 +15203,51 @@ function readDaemonLock() {
|
|
|
14868
15203
|
return lock;
|
|
14869
15204
|
return null;
|
|
14870
15205
|
}
|
|
15206
|
+
function readBootMarker() {
|
|
15207
|
+
const m = readJsonFileSync(daemonBootMarker());
|
|
15208
|
+
if (m && typeof m.pid === "number" && typeof m.claimedAt === "number")
|
|
15209
|
+
return m;
|
|
15210
|
+
return null;
|
|
15211
|
+
}
|
|
15212
|
+
function fileBootMarker() {
|
|
15213
|
+
const write = (marker, place) => {
|
|
15214
|
+
const path = daemonBootMarker();
|
|
15215
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
15216
|
+
writeFileSync3(tmp, JSON.stringify(marker), { mode: 384 });
|
|
15217
|
+
try {
|
|
15218
|
+
place(tmp, path);
|
|
15219
|
+
} finally {
|
|
15220
|
+
rmQuiet(tmp);
|
|
15221
|
+
}
|
|
15222
|
+
};
|
|
15223
|
+
return {
|
|
15224
|
+
claim: (marker) => {
|
|
15225
|
+
try {
|
|
15226
|
+
ensureStateDir();
|
|
15227
|
+
write(marker, linkSync);
|
|
15228
|
+
return true;
|
|
15229
|
+
} catch (e) {
|
|
15230
|
+
return e.code !== "EEXIST";
|
|
15231
|
+
}
|
|
15232
|
+
},
|
|
15233
|
+
assign: (pid) => {
|
|
15234
|
+
try {
|
|
15235
|
+
write({ pid, claimedAt: readBootMarker()?.claimedAt ?? 0 }, renameSync2);
|
|
15236
|
+
} catch {}
|
|
15237
|
+
},
|
|
15238
|
+
read: readBootMarker,
|
|
15239
|
+
clear: () => rmQuiet(daemonBootMarker())
|
|
15240
|
+
};
|
|
15241
|
+
}
|
|
15242
|
+
function removeOwnBootMarker() {
|
|
15243
|
+
if (readBootMarker()?.pid === process.pid)
|
|
15244
|
+
rmQuiet(daemonBootMarker());
|
|
15245
|
+
}
|
|
15246
|
+
function rmQuiet(path) {
|
|
15247
|
+
try {
|
|
15248
|
+
unlinkSync2(path);
|
|
15249
|
+
} catch {}
|
|
15250
|
+
}
|
|
14871
15251
|
function isPidAlive(pid) {
|
|
14872
15252
|
try {
|
|
14873
15253
|
process.kill(pid, 0);
|
|
@@ -14877,9 +15257,7 @@ function isPidAlive(pid) {
|
|
|
14877
15257
|
}
|
|
14878
15258
|
}
|
|
14879
15259
|
function removeDaemonLock() {
|
|
14880
|
-
|
|
14881
|
-
unlinkSync2(daemonLock());
|
|
14882
|
-
} catch {}
|
|
15260
|
+
rmQuiet(daemonLock());
|
|
14883
15261
|
}
|
|
14884
15262
|
function removeOwnDaemonLock() {
|
|
14885
15263
|
if (readDaemonLock()?.pid === process.pid)
|
|
@@ -14972,16 +15350,18 @@ async function prodEnsureDeps(s, service, reserveMs) {
|
|
|
14972
15350
|
retire: (baseUrl, lock) => retireDaemon(baseUrl, lock, world),
|
|
14973
15351
|
removeLock: removeDaemonLock,
|
|
14974
15352
|
spawn: () => spawnDaemon(s),
|
|
15353
|
+
bootMarker: fileBootMarker(),
|
|
14975
15354
|
timing: {
|
|
14976
15355
|
backoff,
|
|
14977
15356
|
maxAttempts: PROD_MAX_ATTEMPTS,
|
|
14978
15357
|
now: () => performance.now(),
|
|
15358
|
+
wallNow: Date.now,
|
|
14979
15359
|
windowMs: SUPERVISOR_WINDOW_MS,
|
|
14980
15360
|
reserveMs
|
|
14981
15361
|
}
|
|
14982
15362
|
};
|
|
14983
15363
|
}
|
|
14984
|
-
var FOREIGN_WORLD_ERROR = "port serves a different caret world (state dir mismatch) \u2014 set CARET_PORT to a free port", DAEMON_CWD = "/", PROD_MAX_ATTEMPTS = 12, SUPERVISOR_WINDOW_MS;
|
|
15364
|
+
var BOOT_MARKER_TTL_MS = 30000, FOREIGN_WORLD_ERROR = "port serves a different caret world (state dir mismatch) \u2014 set CARET_PORT to a free port", DAEMON_CWD = "/", PROD_MAX_ATTEMPTS = 12, SUPERVISOR_WINDOW_MS;
|
|
14985
15365
|
var init_lifecycle = __esm(() => {
|
|
14986
15366
|
init_paths3();
|
|
14987
15367
|
init_settings();
|
|
@@ -16342,7 +16722,7 @@ __export(exports_dist, {
|
|
|
16342
16722
|
box: () => box,
|
|
16343
16723
|
cancel: () => cancel,
|
|
16344
16724
|
confirm: () => confirm,
|
|
16345
|
-
date: () =>
|
|
16725
|
+
date: () => date3,
|
|
16346
16726
|
formatInstructionFooter: () => formatInstructionFooter,
|
|
16347
16727
|
group: () => group,
|
|
16348
16728
|
groupMultiselect: () => groupMultiselect,
|
|
@@ -16375,8 +16755,8 @@ __export(exports_dist, {
|
|
|
16375
16755
|
});
|
|
16376
16756
|
import { styleText as styleText2, stripVTControlCharacters } from "util";
|
|
16377
16757
|
import process$1 from "process";
|
|
16378
|
-
import { existsSync as
|
|
16379
|
-
import { dirname as
|
|
16758
|
+
import { existsSync as existsSync10, lstatSync, readdirSync as readdirSync4 } from "fs";
|
|
16759
|
+
import { dirname as dirname8, join as join15 } from "path";
|
|
16380
16760
|
function isUnicodeSupported() {
|
|
16381
16761
|
if (process$1.platform !== "win32") {
|
|
16382
16762
|
return process$1.env.TERM !== "linux";
|
|
@@ -16725,7 +17105,7 @@ ${g}
|
|
|
16725
17105
|
}
|
|
16726
17106
|
}
|
|
16727
17107
|
}).prompt();
|
|
16728
|
-
},
|
|
17108
|
+
}, date3 = (e) => {
|
|
16729
17109
|
const r = e.validate;
|
|
16730
17110
|
return new U2({
|
|
16731
17111
|
...e,
|
|
@@ -17087,10 +17467,10 @@ ${n}
|
|
|
17087
17467
|
return [];
|
|
17088
17468
|
try {
|
|
17089
17469
|
let i;
|
|
17090
|
-
|
|
17470
|
+
existsSync10(t) ? lstatSync(t).isDirectory() && (!e.directory || t.endsWith("/")) ? i = t : i = dirname8(t) : i = dirname8(t);
|
|
17091
17471
|
const c = t.length > 1 && t.endsWith("/") ? t.slice(0, -1) : t;
|
|
17092
|
-
return
|
|
17093
|
-
const n =
|
|
17472
|
+
return readdirSync4(i).map((r) => {
|
|
17473
|
+
const n = join15(i, r), m = lstatSync(n);
|
|
17094
17474
|
return {
|
|
17095
17475
|
name: r,
|
|
17096
17476
|
path: n,
|
|
@@ -18675,7 +19055,7 @@ var handleResult = (ctx, result) => {
|
|
|
18675
19055
|
}
|
|
18676
19056
|
};
|
|
18677
19057
|
}
|
|
18678
|
-
}, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, jwtRegex, durationRegex, emailRegex, _emojiRegex = `^[\\p{Extended_Pictographic}\\p{Emoji_Component}]+$`, emojiRegex, ipv4Regex, ipv4CidrRegex, ipv6Regex, ipv6CidrRegex, base64Regex, base64urlRegex, dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`, dateRegex, ZodString2, ZodNumber2,
|
|
19058
|
+
}, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, jwtRegex, durationRegex, emailRegex, _emojiRegex = `^[\\p{Extended_Pictographic}\\p{Emoji_Component}]+$`, emojiRegex, ipv4Regex, ipv4CidrRegex, ipv6Regex, ipv6CidrRegex, base64Regex, base64urlRegex, dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`, dateRegex, ZodString2, ZodNumber2, ZodBigInt2, ZodBoolean2, ZodDate2, ZodSymbol, ZodUndefined, ZodNull2, ZodAny, ZodUnknown2, ZodNever2, ZodVoid, ZodArray2, ZodObject2, ZodUnion2, getDiscriminator = (type) => {
|
|
18679
19059
|
if (type instanceof ZodLazy) {
|
|
18680
19060
|
return getDiscriminator(type.schema);
|
|
18681
19061
|
} else if (type instanceof ZodEffects) {
|
|
@@ -19502,7 +19882,7 @@ var init_types = __esm(() => {
|
|
|
19502
19882
|
...processCreateParams(params)
|
|
19503
19883
|
});
|
|
19504
19884
|
};
|
|
19505
|
-
|
|
19885
|
+
ZodBigInt2 = class ZodBigInt2 extends ZodType2 {
|
|
19506
19886
|
constructor() {
|
|
19507
19887
|
super(...arguments);
|
|
19508
19888
|
this.min = this.gte;
|
|
@@ -19587,7 +19967,7 @@ var init_types = __esm(() => {
|
|
|
19587
19967
|
return this.setLimit("max", value, false, errorUtil.toString(message));
|
|
19588
19968
|
}
|
|
19589
19969
|
setLimit(kind, value, inclusive, message) {
|
|
19590
|
-
return new
|
|
19970
|
+
return new ZodBigInt2({
|
|
19591
19971
|
...this._def,
|
|
19592
19972
|
checks: [
|
|
19593
19973
|
...this._def.checks,
|
|
@@ -19601,7 +19981,7 @@ var init_types = __esm(() => {
|
|
|
19601
19981
|
});
|
|
19602
19982
|
}
|
|
19603
19983
|
_addCheck(check) {
|
|
19604
|
-
return new
|
|
19984
|
+
return new ZodBigInt2({
|
|
19605
19985
|
...this._def,
|
|
19606
19986
|
checks: [...this._def.checks, check]
|
|
19607
19987
|
});
|
|
@@ -19666,8 +20046,8 @@ var init_types = __esm(() => {
|
|
|
19666
20046
|
return max;
|
|
19667
20047
|
}
|
|
19668
20048
|
};
|
|
19669
|
-
|
|
19670
|
-
return new
|
|
20049
|
+
ZodBigInt2.create = (params) => {
|
|
20050
|
+
return new ZodBigInt2({
|
|
19671
20051
|
checks: [],
|
|
19672
20052
|
typeName: ZodFirstPartyTypeKind2.ZodBigInt,
|
|
19673
20053
|
coerce: params?.coerce ?? false,
|
|
@@ -19699,7 +20079,7 @@ var init_types = __esm(() => {
|
|
|
19699
20079
|
...processCreateParams(params)
|
|
19700
20080
|
});
|
|
19701
20081
|
};
|
|
19702
|
-
|
|
20082
|
+
ZodDate2 = class ZodDate2 extends ZodType2 {
|
|
19703
20083
|
_parse(input) {
|
|
19704
20084
|
if (this._def.coerce) {
|
|
19705
20085
|
input.data = new Date(input.data);
|
|
@@ -19760,7 +20140,7 @@ var init_types = __esm(() => {
|
|
|
19760
20140
|
};
|
|
19761
20141
|
}
|
|
19762
20142
|
_addCheck(check) {
|
|
19763
|
-
return new
|
|
20143
|
+
return new ZodDate2({
|
|
19764
20144
|
...this._def,
|
|
19765
20145
|
checks: [...this._def.checks, check]
|
|
19766
20146
|
});
|
|
@@ -19800,8 +20180,8 @@ var init_types = __esm(() => {
|
|
|
19800
20180
|
return max != null ? new Date(max) : null;
|
|
19801
20181
|
}
|
|
19802
20182
|
};
|
|
19803
|
-
|
|
19804
|
-
return new
|
|
20183
|
+
ZodDate2.create = (params) => {
|
|
20184
|
+
return new ZodDate2({
|
|
19805
20185
|
checks: [],
|
|
19806
20186
|
coerce: params?.coerce || false,
|
|
19807
20187
|
typeName: ZodFirstPartyTypeKind2.ZodDate,
|
|
@@ -21467,9 +21847,9 @@ var init_types = __esm(() => {
|
|
|
21467
21847
|
stringType = ZodString2.create;
|
|
21468
21848
|
numberType = ZodNumber2.create;
|
|
21469
21849
|
nanType = ZodNaN.create;
|
|
21470
|
-
bigIntType =
|
|
21850
|
+
bigIntType = ZodBigInt2.create;
|
|
21471
21851
|
booleanType = ZodBoolean2.create;
|
|
21472
|
-
dateType =
|
|
21852
|
+
dateType = ZodDate2.create;
|
|
21473
21853
|
symbolType = ZodSymbol.create;
|
|
21474
21854
|
undefinedType = ZodUndefined.create;
|
|
21475
21855
|
nullType = ZodNull2.create;
|
|
@@ -33494,6 +33874,89 @@ function optedOut(text) {
|
|
|
33494
33874
|
// src/commands/daemon.ts
|
|
33495
33875
|
init_settings();
|
|
33496
33876
|
|
|
33877
|
+
// src/daemon/update-changes.ts
|
|
33878
|
+
var COMMIT_CAP = 50;
|
|
33879
|
+
var RELEASE_TAG = /^v?\d+\.\d+\.\d+$/;
|
|
33880
|
+
var FULL_SHA = /^[0-9a-f]{40}$/;
|
|
33881
|
+
var COMPARABLE_COMMIT = /^[0-9a-f]{7,40}$/;
|
|
33882
|
+
function selectReleases(releases, running, target) {
|
|
33883
|
+
return releases.filter((r) => !r.draft && !r.prerelease && RELEASE_TAG.test(r.tag_name)).map((r) => ({ version: r.tag_name.replace(/^v/, ""), body: r.body ?? "" })).filter((r) => isNewer(r.version, running) && !isNewer(r.version, target)).sort((a, b) => isNewer(a.version, b.version) ? -1 : isNewer(b.version, a.version) ? 1 : 0);
|
|
33884
|
+
}
|
|
33885
|
+
function selectCommits(newestFirst, total, cap) {
|
|
33886
|
+
const commits = newestFirst.filter((c) => FULL_SHA.test(c.sha)).slice(0, cap).map((c) => {
|
|
33887
|
+
const subject = c.commit.message.split(`
|
|
33888
|
+
`, 1)[0] ?? "";
|
|
33889
|
+
return { sha: c.sha, subject, pr: prNumber(subject) };
|
|
33890
|
+
});
|
|
33891
|
+
return { kind: "commits", commits, more: Math.max(0, total - commits.length) };
|
|
33892
|
+
}
|
|
33893
|
+
function prNumber(subject) {
|
|
33894
|
+
const match = /\(#(\d+)\)$/.exec(subject);
|
|
33895
|
+
return match ? Number(match[1]) : null;
|
|
33896
|
+
}
|
|
33897
|
+
function createUpdateChanges(deps) {
|
|
33898
|
+
let slot = null;
|
|
33899
|
+
return () => {
|
|
33900
|
+
const status = deps.status();
|
|
33901
|
+
if (slot?.status === status)
|
|
33902
|
+
return slot.promise;
|
|
33903
|
+
const promise = changesFor(deps, status).catch(() => failed(deps.log)).then((changes) => {
|
|
33904
|
+
if (changes === null && slot?.promise === promise)
|
|
33905
|
+
slot = null;
|
|
33906
|
+
return changes;
|
|
33907
|
+
});
|
|
33908
|
+
slot = { status, promise };
|
|
33909
|
+
return promise;
|
|
33910
|
+
};
|
|
33911
|
+
}
|
|
33912
|
+
async function changesFor(deps, status) {
|
|
33913
|
+
if (deps.install === "bundle" && status.kind === "behind-release") {
|
|
33914
|
+
const releases = await deps.releases();
|
|
33915
|
+
if (!releases)
|
|
33916
|
+
return failed(deps.log);
|
|
33917
|
+
return { kind: "releases", releases: selectReleases(releases, deps.version, status.available) };
|
|
33918
|
+
}
|
|
33919
|
+
const behind = status.kind === "behind-release" || status.kind === "behind-commit";
|
|
33920
|
+
if (deps.install !== "binary" || !behind || !COMPARABLE_COMMIT.test(deps.commit))
|
|
33921
|
+
return null;
|
|
33922
|
+
const comparison = await deps.compare(deps.commit);
|
|
33923
|
+
if (!comparison)
|
|
33924
|
+
return failed(deps.log);
|
|
33925
|
+
return selectCommits([...comparison.commits].reverse(), comparison.total_commits, COMMIT_CAP);
|
|
33926
|
+
}
|
|
33927
|
+
function failed(log) {
|
|
33928
|
+
log.warn("update", "update changes read failed");
|
|
33929
|
+
return null;
|
|
33930
|
+
}
|
|
33931
|
+
|
|
33932
|
+
// src/daemon/dev-update.ts
|
|
33933
|
+
var RELEASES_BACK = 3;
|
|
33934
|
+
var COMMITS_BACK = 60;
|
|
33935
|
+
function devUpdateIdentity(raw, built, git) {
|
|
33936
|
+
if (built.install !== "dev")
|
|
33937
|
+
return null;
|
|
33938
|
+
if (raw === "release") {
|
|
33939
|
+
const tags = git(["tag", "--list", "v*", "--sort=-v:refname"])?.split(`
|
|
33940
|
+
`) ?? [];
|
|
33941
|
+
const tag = tags.filter((t) => RELEASE_TAG.test(t))[RELEASES_BACK];
|
|
33942
|
+
return tag ? { install: "bundle", version: tag.replace(/^v/, ""), commit: built.commit } : null;
|
|
33943
|
+
}
|
|
33944
|
+
if (raw === "commit") {
|
|
33945
|
+
const commit = git(["rev-parse", "--verify", "--quiet", `origin/trunk~${COMMITS_BACK}`]);
|
|
33946
|
+
return commit ? { install: "binary", version: built.version, commit } : null;
|
|
33947
|
+
}
|
|
33948
|
+
return null;
|
|
33949
|
+
}
|
|
33950
|
+
function sourceGit(args) {
|
|
33951
|
+
try {
|
|
33952
|
+
const r = Bun.spawnSync(["git", "-C", import.meta.dir, ...args]);
|
|
33953
|
+
const out = r.exitCode === 0 ? r.stdout.toString().trim() : "";
|
|
33954
|
+
return out || null;
|
|
33955
|
+
} catch {
|
|
33956
|
+
return null;
|
|
33957
|
+
}
|
|
33958
|
+
}
|
|
33959
|
+
|
|
33497
33960
|
// src/daemon/diagnostics.ts
|
|
33498
33961
|
init_settings();
|
|
33499
33962
|
init_core3();
|
|
@@ -33533,7 +33996,7 @@ function buildDiagnostics(deps) {
|
|
|
33533
33996
|
init_lifecycle();
|
|
33534
33997
|
|
|
33535
33998
|
// src/daemon/server.ts
|
|
33536
|
-
import { renameSync as
|
|
33999
|
+
import { renameSync as renameSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
33537
34000
|
import { dirname as dirname6 } from "path";
|
|
33538
34001
|
init_constants();
|
|
33539
34002
|
init_paths3();
|
|
@@ -33686,8 +34149,10 @@ var BehaviorSchema = _enum(["allow", "deny"]);
|
|
|
33686
34149
|
var ResolveBodySchema = object({
|
|
33687
34150
|
behavior: BehaviorSchema.catch("allow"),
|
|
33688
34151
|
feedback: string2().optional(),
|
|
33689
|
-
acceptMode: string2().optional().catch(undefined)
|
|
34152
|
+
acceptMode: string2().optional().catch(undefined),
|
|
34153
|
+
version: number2().int().positive().optional().catch(undefined)
|
|
33690
34154
|
}).catch({ behavior: "allow" });
|
|
34155
|
+
var VersionQuerySchema = exports_coerce.number().int().positive().optional().catch(undefined);
|
|
33691
34156
|
var ConfigPatchSchema = object({ updates: object({ check: boolean2() }).strict().optional() }).strict();
|
|
33692
34157
|
var LineAnnotationSchema = object({
|
|
33693
34158
|
id: string2(),
|
|
@@ -33835,7 +34300,8 @@ var EXT_LANGUAGE = {
|
|
|
33835
34300
|
".gql": "graphql",
|
|
33836
34301
|
".php": "php",
|
|
33837
34302
|
".swift": "swift",
|
|
33838
|
-
".dart": "dart"
|
|
34303
|
+
".dart": "dart",
|
|
34304
|
+
".lua": "lua"
|
|
33839
34305
|
};
|
|
33840
34306
|
function languageForPath(path) {
|
|
33841
34307
|
return EXT_LANGUAGE[extname(path).toLowerCase()] ?? "text";
|
|
@@ -35529,23 +35995,37 @@ var un = x.lex;
|
|
|
35529
35995
|
init_log();
|
|
35530
35996
|
|
|
35531
35997
|
// src/plan/canonical-file.ts
|
|
35532
|
-
import { appendFileSync, existsSync as existsSync7, statSync as statSync3, writeFileSync as
|
|
35998
|
+
import { appendFileSync, existsSync as existsSync7, readFileSync as readFileSync10, statSync as statSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
35999
|
+
function isPlanFile(path) {
|
|
36000
|
+
return path.endsWith(".md") && existsSync7(path) && statSync3(path).isFile();
|
|
36001
|
+
}
|
|
36002
|
+
function readPlanFile(path) {
|
|
36003
|
+
try {
|
|
36004
|
+
return isPlanFile(path) ? readFileSync10(path, "utf8") : undefined;
|
|
36005
|
+
} catch {
|
|
36006
|
+
return;
|
|
36007
|
+
}
|
|
36008
|
+
}
|
|
35533
36009
|
function guardedPlanFileWrite(planFilePath, log, failMsg, write) {
|
|
35534
36010
|
try {
|
|
35535
|
-
if (!planFilePath
|
|
35536
|
-
return;
|
|
35537
|
-
if (!existsSync7(planFilePath) || !statSync3(planFilePath).isFile())
|
|
36011
|
+
if (!isPlanFile(planFilePath))
|
|
35538
36012
|
return;
|
|
35539
|
-
write(planFilePath);
|
|
36013
|
+
return write(planFilePath);
|
|
35540
36014
|
} catch (err) {
|
|
35541
36015
|
const code = err?.code;
|
|
35542
|
-
log.warn("review", failMsg, code ? { code } : {});
|
|
36016
|
+
log.warn("review", failMsg, code ? { fsCode: code } : {});
|
|
36017
|
+
return;
|
|
35543
36018
|
}
|
|
35544
36019
|
}
|
|
35545
|
-
function writeCanonicalPlanFile(
|
|
35546
|
-
|
|
35547
|
-
|
|
35548
|
-
|
|
36020
|
+
function writeCanonicalPlanFile(input, canonical, log) {
|
|
36021
|
+
return guardedPlanFileWrite(input.planFilePath, log, "plan file canonicalize failed", (p) => {
|
|
36022
|
+
if (readFileSync10(p, "utf8") !== (input.plan ?? "")) {
|
|
36023
|
+
log.info("review", "plan file changed; rewrite skipped");
|
|
36024
|
+
return "changed";
|
|
36025
|
+
}
|
|
36026
|
+
writeFileSync4(p, canonical);
|
|
36027
|
+
return "written";
|
|
36028
|
+
}) ?? "skipped";
|
|
35549
36029
|
}
|
|
35550
36030
|
function appendReviewerNotesToPlanFile(planFilePath, notes, log) {
|
|
35551
36031
|
const section = reviewerNotesSection(notes);
|
|
@@ -35560,7 +36040,7 @@ init_log();
|
|
|
35560
36040
|
// src/plan/rumdl.ts
|
|
35561
36041
|
init_paths3();
|
|
35562
36042
|
import { createHash as createHash2 } from "crypto";
|
|
35563
|
-
import { chmodSync as chmodSync3, existsSync as existsSync8, mkdirSync as mkdirSync2, renameSync as
|
|
36043
|
+
import { chmodSync as chmodSync3, existsSync as existsSync8, mkdirSync as mkdirSync2, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
35564
36044
|
import { mkdtemp, rm as rm3 } from "fs/promises";
|
|
35565
36045
|
import { tmpdir } from "os";
|
|
35566
36046
|
import { dirname as dirname5, join as join13 } from "path";
|
|
@@ -35620,7 +36100,7 @@ async function downloadRumdl(binPath, asset, fetchImpl = fetch) {
|
|
|
35620
36100
|
const tmp = `${binPath}.${process.pid}.tmp`;
|
|
35621
36101
|
await Bun.write(tmp, Bun.file(extracted));
|
|
35622
36102
|
chmodSync3(tmp, 493);
|
|
35623
|
-
|
|
36103
|
+
renameSync3(tmp, binPath);
|
|
35624
36104
|
return binPath;
|
|
35625
36105
|
} finally {
|
|
35626
36106
|
await rm3(work, { recursive: true, force: true });
|
|
@@ -35645,7 +36125,7 @@ var acquisition = { cooldownUntil: 0 };
|
|
|
35645
36125
|
async function ensureRumdl(install = installPinnedRumdl, now = Date.now, state = acquisition) {
|
|
35646
36126
|
ensureStateDir(rumdlDir());
|
|
35647
36127
|
const config = rumdlConfig();
|
|
35648
|
-
|
|
36128
|
+
writeFileSync5(config, RUMDL_CONFIG);
|
|
35649
36129
|
const override = process.env.CARET_RUMDL_BIN?.trim();
|
|
35650
36130
|
if (override && await installedVersion(override) === RUMDL_VERSION) {
|
|
35651
36131
|
return { bin: override, config, installed: false };
|
|
@@ -35727,21 +36207,25 @@ function deriveTitle(plan) {
|
|
|
35727
36207
|
}
|
|
35728
36208
|
async function routeIncomingPlan(input, store, log = noopLogger) {
|
|
35729
36209
|
const sessionId = input.sessionId ?? `anon-${Date.now()}`;
|
|
35730
|
-
const
|
|
35731
|
-
|
|
36210
|
+
const sessionLog = log.child({ sessionId });
|
|
36211
|
+
const plan = await formatPlanMarkdown(input.plan ?? "", sessionLog);
|
|
36212
|
+
const { planFilePath } = input;
|
|
36213
|
+
const planFile = planFilePath ? {
|
|
36214
|
+
planFileCurrent: writeCanonicalPlanFile({ ...input, planFilePath }, plan, sessionLog) !== "changed"
|
|
36215
|
+
} : {};
|
|
35732
36216
|
const now = Date.now();
|
|
36217
|
+
const [latest, ...older] = store.bySession(sessionId);
|
|
35733
36218
|
const expired = [];
|
|
35734
|
-
for (const
|
|
35735
|
-
await store.expire(
|
|
35736
|
-
expired.push(
|
|
35737
|
-
log.info("review", `review superseded: ${shortId(
|
|
35738
|
-
reviewId:
|
|
36219
|
+
for (const orphan of older.filter((r) => r.status === "pending")) {
|
|
36220
|
+
await store.expire(orphan.id);
|
|
36221
|
+
expired.push(orphan.id);
|
|
36222
|
+
log.info("review", `review superseded: ${shortId(orphan.id)}`, {
|
|
36223
|
+
reviewId: orphan.id,
|
|
35739
36224
|
sessionId,
|
|
35740
36225
|
action: "supersede"
|
|
35741
36226
|
});
|
|
35742
36227
|
}
|
|
35743
|
-
|
|
35744
|
-
if (latest && latest.status === "rejected") {
|
|
36228
|
+
if (latest && (latest.status === "rejected" || latest.status === "pending")) {
|
|
35745
36229
|
const version = latest.versions.length + 1;
|
|
35746
36230
|
await store.update(latest.id, (r) => {
|
|
35747
36231
|
r.versions.push({ version, plan, annotations: [], createdAt: now });
|
|
@@ -35760,7 +36244,8 @@ async function routeIncomingPlan(input, store, log = noopLogger) {
|
|
|
35760
36244
|
id: latest.id,
|
|
35761
36245
|
action: "append",
|
|
35762
36246
|
version,
|
|
35763
|
-
expired
|
|
36247
|
+
expired,
|
|
36248
|
+
...planFile
|
|
35764
36249
|
};
|
|
35765
36250
|
}
|
|
35766
36251
|
const id = newReviewId();
|
|
@@ -35782,7 +36267,7 @@ async function routeIncomingPlan(input, store, log = noopLogger) {
|
|
|
35782
36267
|
action: "new",
|
|
35783
36268
|
version: 1
|
|
35784
36269
|
});
|
|
35785
|
-
return { id, action: "new", version: 1, expired };
|
|
36270
|
+
return { id, action: "new", version: 1, expired, ...planFile };
|
|
35786
36271
|
}
|
|
35787
36272
|
|
|
35788
36273
|
// src/service/manager.ts
|
|
@@ -35824,7 +36309,16 @@ var MAX_BODY_BYTES = 64 * 1024;
|
|
|
35824
36309
|
var MAX_EVENTS = 100;
|
|
35825
36310
|
var MAX_MSG_LEN = 256;
|
|
35826
36311
|
var STEP_RE = /^[a-z][a-z0-9-]{0,31}$/;
|
|
35827
|
-
var RESERVED_KEYS = new Set([
|
|
36312
|
+
var RESERVED_KEYS = new Set([
|
|
36313
|
+
"level",
|
|
36314
|
+
"time",
|
|
36315
|
+
"msg",
|
|
36316
|
+
"step",
|
|
36317
|
+
"pid",
|
|
36318
|
+
"err",
|
|
36319
|
+
"caller",
|
|
36320
|
+
"code"
|
|
36321
|
+
]);
|
|
35828
36322
|
var CONTROL_RE = /[\u0000-\u0008\u000A-\u001F\u007F-\u009F]/g;
|
|
35829
36323
|
function sanitizeString(s) {
|
|
35830
36324
|
return s.replace(CONTROL_RE, "");
|
|
@@ -35898,6 +36392,8 @@ function resolveOptions(opts) {
|
|
|
35898
36392
|
readSkillDescription: opts.readSkillDescription,
|
|
35899
36393
|
diagnostics: opts.diagnostics,
|
|
35900
36394
|
updateReport: opts.updateReport,
|
|
36395
|
+
updateChanges: opts.updateChanges,
|
|
36396
|
+
restartHint: opts.restartHint,
|
|
35901
36397
|
onUpdatesEnabled: opts.onUpdatesEnabled,
|
|
35902
36398
|
onDecisionAwaited: opts.onDecisionAwaited,
|
|
35903
36399
|
markPaneRead: opts.markPaneRead ?? ((pane) => markPaneRead(pane, { log: opts.log })),
|
|
@@ -35916,6 +36412,19 @@ function ifNoneMatchHit(header, etag) {
|
|
|
35916
36412
|
return false;
|
|
35917
36413
|
return header.split(",").some((t) => t.trim().replace(/^W\//, "") === etag);
|
|
35918
36414
|
}
|
|
36415
|
+
function isStaleVersion(review, version) {
|
|
36416
|
+
return review !== undefined && version !== undefined && currentVersion(review).version !== version;
|
|
36417
|
+
}
|
|
36418
|
+
function expiredDeny() {
|
|
36419
|
+
return {
|
|
36420
|
+
behavior: "deny",
|
|
36421
|
+
feedback: "caret: this review expired before a decision; denying so no unreviewed plan ships.",
|
|
36422
|
+
decidedAt: Date.now()
|
|
36423
|
+
};
|
|
36424
|
+
}
|
|
36425
|
+
function versionQuery(req) {
|
|
36426
|
+
return VersionQuerySchema.parse(new URL(req.url).searchParams.get("version") ?? undefined);
|
|
36427
|
+
}
|
|
35919
36428
|
function createServer(opts) {
|
|
35920
36429
|
const cfg = resolveOptions(opts);
|
|
35921
36430
|
const { store, idle, heartbeat, resident, assets, onShutdown, routePlan, configPath, log } = cfg;
|
|
@@ -35955,6 +36464,9 @@ function createServer(opts) {
|
|
|
35955
36464
|
function notFound() {
|
|
35956
36465
|
return new Response("not found", { status: 404 });
|
|
35957
36466
|
}
|
|
36467
|
+
function versionConflict() {
|
|
36468
|
+
return new Response("superseded by a newer version", { status: 409 });
|
|
36469
|
+
}
|
|
35958
36470
|
function tooLarge() {
|
|
35959
36471
|
return new Response("too large", { status: 413 });
|
|
35960
36472
|
}
|
|
@@ -35970,7 +36482,8 @@ function createServer(opts) {
|
|
|
35970
36482
|
supervised: isSupervised(),
|
|
35971
36483
|
...process.env.CARET_FRESH === "1" ? { fresh: true } : {},
|
|
35972
36484
|
...approveVariants ? { approveVariants: [...approveVariants] } : {},
|
|
35973
|
-
...source ? { source } : {}
|
|
36485
|
+
...source ? { source } : {},
|
|
36486
|
+
...cfg.restartHint ? { restartHint: cfg.restartHint } : {}
|
|
35974
36487
|
};
|
|
35975
36488
|
return Response.json(body);
|
|
35976
36489
|
}
|
|
@@ -35984,6 +36497,12 @@ function createServer(opts) {
|
|
|
35984
36497
|
return notFound();
|
|
35985
36498
|
return Response.json(await cfg.updateReport());
|
|
35986
36499
|
}
|
|
36500
|
+
async function handleUpdateChanges() {
|
|
36501
|
+
if (!cfg.updateChanges)
|
|
36502
|
+
return notFound();
|
|
36503
|
+
const changes = await cfg.updateChanges();
|
|
36504
|
+
return changes ? Response.json(changes) : new Response("upstream unavailable", { status: 502 });
|
|
36505
|
+
}
|
|
35987
36506
|
function handleRetire() {
|
|
35988
36507
|
log.info("retire", "retire requested");
|
|
35989
36508
|
liveness.drain();
|
|
@@ -36023,19 +36542,21 @@ function createServer(opts) {
|
|
|
36023
36542
|
const cacheControl = path.startsWith("/assets/") ? ASSET_CACHE_CONTROL : INDEX_CACHE_CONTROL;
|
|
36024
36543
|
return assetResponse(req, file, cacheControl);
|
|
36025
36544
|
}
|
|
36026
|
-
async function handleCreateReview(req) {
|
|
36545
|
+
async function handleCreateReview(req, routed) {
|
|
36027
36546
|
if (liveness.isDraining()) {
|
|
36028
36547
|
log.warn("drain", "review refused: draining");
|
|
36029
36548
|
return new Response("draining", { status: 503 });
|
|
36030
36549
|
}
|
|
36031
36550
|
const body = await parseBody(req, PlanInputSchema);
|
|
36032
|
-
|
|
36033
|
-
|
|
36551
|
+
if (body.sessionId)
|
|
36552
|
+
routed.log = log.child({ sessionId: body.sessionId });
|
|
36553
|
+
const routing = await routePlan(body, store, routed.log);
|
|
36554
|
+
for (const staleId of routing.expired)
|
|
36034
36555
|
clearDecision(staleId);
|
|
36035
|
-
if (
|
|
36036
|
-
clearDecision(
|
|
36556
|
+
if (routing.action === "append")
|
|
36557
|
+
clearDecision(routing.id);
|
|
36037
36558
|
const hasLiveClient = isClientLive(lastReviewsPollAt, Date.now(), LIVE_CLIENT_WINDOW_MS);
|
|
36038
|
-
return Response.json({ ...
|
|
36559
|
+
return Response.json({ ...routing, hasLiveClient });
|
|
36039
36560
|
}
|
|
36040
36561
|
async function handleLogs(req) {
|
|
36041
36562
|
const reject = (status) => {
|
|
@@ -36057,8 +36578,12 @@ function createServer(opts) {
|
|
|
36057
36578
|
const result = parseUiLogBatch(parsed);
|
|
36058
36579
|
if ("status" in result)
|
|
36059
36580
|
return reject(result.status);
|
|
36060
|
-
for (const ev of result.events)
|
|
36061
|
-
|
|
36581
|
+
for (const ev of result.events) {
|
|
36582
|
+
if (ev.level === "error")
|
|
36583
|
+
log.error(ev.step, "ui-error", ev.msg, ev.extra);
|
|
36584
|
+
else
|
|
36585
|
+
log[ev.level](ev.step, ev.msg, ev.extra);
|
|
36586
|
+
}
|
|
36062
36587
|
return new Response(null, { status: 204 });
|
|
36063
36588
|
}
|
|
36064
36589
|
function handleListReviews() {
|
|
@@ -36095,11 +36620,12 @@ function createServer(opts) {
|
|
|
36095
36620
|
cfg.onUpdatesEnabled?.();
|
|
36096
36621
|
return Response.json({ ok: true });
|
|
36097
36622
|
}
|
|
36098
|
-
function handleGetReview(id) {
|
|
36623
|
+
function handleGetReview({ id }) {
|
|
36099
36624
|
const r = store.get(id);
|
|
36100
36625
|
return r ? Response.json(toClientReview(r)) : notFound();
|
|
36101
36626
|
}
|
|
36102
|
-
async function handleFileRefs(req,
|
|
36627
|
+
async function handleFileRefs(req, route) {
|
|
36628
|
+
const { id } = route;
|
|
36103
36629
|
const r = store.get(id);
|
|
36104
36630
|
if (!r)
|
|
36105
36631
|
return notFound();
|
|
@@ -36121,14 +36647,13 @@ function createServer(opts) {
|
|
|
36121
36647
|
})));
|
|
36122
36648
|
}
|
|
36123
36649
|
const resolved = Object.fromEntries(entries.filter((e) => e !== null));
|
|
36124
|
-
log.debug("request", `file-refs resolved: ${Object.keys(resolved).length}/${unique.length}`, {
|
|
36125
|
-
reviewId: id,
|
|
36650
|
+
route.log.debug("request", `file-refs resolved: ${Object.keys(resolved).length}/${unique.length}`, {
|
|
36126
36651
|
requested: paths.length,
|
|
36127
36652
|
directories: Object.values(resolved).filter((k) => k === "directory").length
|
|
36128
36653
|
});
|
|
36129
36654
|
return Response.json({ resolved });
|
|
36130
36655
|
}
|
|
36131
|
-
async function handleFileExcerpt(req, id) {
|
|
36656
|
+
async function handleFileExcerpt(req, { id }) {
|
|
36132
36657
|
const r = store.get(id);
|
|
36133
36658
|
if (!r)
|
|
36134
36659
|
return notFound();
|
|
@@ -36147,7 +36672,8 @@ function createServer(opts) {
|
|
|
36147
36672
|
return Response.json(excerpt);
|
|
36148
36673
|
return await isFileTooLargeToPreview(r.cwd, path) ? tooLarge() : notFound();
|
|
36149
36674
|
}
|
|
36150
|
-
async function handleDirListing(req,
|
|
36675
|
+
async function handleDirListing(req, route) {
|
|
36676
|
+
const { id } = route;
|
|
36151
36677
|
const r = store.get(id);
|
|
36152
36678
|
if (!r)
|
|
36153
36679
|
return notFound();
|
|
@@ -36155,24 +36681,25 @@ function createServer(opts) {
|
|
|
36155
36681
|
const listing = await listDirectory(r.cwd, params.get("root") ?? "", params.get("path") ?? "");
|
|
36156
36682
|
if (listing === null)
|
|
36157
36683
|
return notFound();
|
|
36158
|
-
log.debug("request", "dir listed", {
|
|
36159
|
-
reviewId: id,
|
|
36684
|
+
route.log.debug("request", "dir listed", {
|
|
36160
36685
|
total: listing.total,
|
|
36161
36686
|
returned: listing.entries.length
|
|
36162
36687
|
});
|
|
36163
36688
|
return Response.json(listing);
|
|
36164
36689
|
}
|
|
36165
|
-
async function handleSkills(
|
|
36690
|
+
async function handleSkills(route) {
|
|
36691
|
+
const { id } = route;
|
|
36166
36692
|
if (!cfg.listSkills)
|
|
36167
36693
|
return notFound();
|
|
36168
36694
|
const r = store.get(id);
|
|
36169
36695
|
if (!r)
|
|
36170
36696
|
return notFound();
|
|
36171
36697
|
const skills = await cfg.listSkills(r.cwd);
|
|
36172
|
-
log.debug("request", "skills listed", {
|
|
36698
|
+
route.log.debug("request", "skills listed", { count: skills.length });
|
|
36173
36699
|
return Response.json(skills);
|
|
36174
36700
|
}
|
|
36175
|
-
async function handleSkillDescription(req,
|
|
36701
|
+
async function handleSkillDescription(req, route) {
|
|
36702
|
+
const { id } = route;
|
|
36176
36703
|
if (!cfg.readSkillDescription)
|
|
36177
36704
|
return notFound();
|
|
36178
36705
|
const r = store.get(id);
|
|
@@ -36183,10 +36710,11 @@ function createServer(opts) {
|
|
|
36183
36710
|
name: params.get("name") ?? "",
|
|
36184
36711
|
origin: params.get("origin") ?? ""
|
|
36185
36712
|
});
|
|
36186
|
-
log.debug("request", "skill description read", {
|
|
36713
|
+
route.log.debug("request", "skill description read", { found: description !== null });
|
|
36187
36714
|
return Response.json({ description });
|
|
36188
36715
|
}
|
|
36189
|
-
async function handleFileSearch(req,
|
|
36716
|
+
async function handleFileSearch(req, route) {
|
|
36717
|
+
const { id } = route;
|
|
36190
36718
|
const r = store.get(id);
|
|
36191
36719
|
if (!r)
|
|
36192
36720
|
return notFound();
|
|
@@ -36194,26 +36722,30 @@ function createServer(opts) {
|
|
|
36194
36722
|
const result = await searchFiles(r.cwd, query);
|
|
36195
36723
|
if (result === null)
|
|
36196
36724
|
return notFound();
|
|
36197
|
-
log.debug("request", "file search answered", {
|
|
36198
|
-
reviewId: id,
|
|
36725
|
+
route.log.debug("request", "file search answered", {
|
|
36199
36726
|
returned: result.paths.length,
|
|
36200
36727
|
stoppedAt: result.stoppedAt
|
|
36201
36728
|
});
|
|
36202
36729
|
return Response.json(result);
|
|
36203
36730
|
}
|
|
36204
|
-
async function handleDecision(
|
|
36731
|
+
async function handleDecision(req, route) {
|
|
36732
|
+
const { id } = route;
|
|
36205
36733
|
const inMem = store.get(id);
|
|
36734
|
+
const disk = inMem ? undefined : await store.persisted(id);
|
|
36735
|
+
if (isStaleVersion(inMem ?? disk, versionQuery(req)))
|
|
36736
|
+
return versionConflict();
|
|
36206
36737
|
if (inMem?.decision) {
|
|
36207
36738
|
clearDecision(id);
|
|
36208
36739
|
return Response.json(inMem.decision);
|
|
36209
36740
|
}
|
|
36210
|
-
if (
|
|
36211
|
-
|
|
36212
|
-
|
|
36213
|
-
|
|
36214
|
-
|
|
36215
|
-
|
|
36216
|
-
}
|
|
36741
|
+
if (disk?.decision) {
|
|
36742
|
+
route.log.debug("decision", `decision served from disk: ${shortId(id)}`);
|
|
36743
|
+
clearDecision(id);
|
|
36744
|
+
return Response.json(disk.decision);
|
|
36745
|
+
}
|
|
36746
|
+
if (disk?.status === "expired") {
|
|
36747
|
+
route.log.debug("decision", `expired review denied: ${shortId(id)}`);
|
|
36748
|
+
return Response.json(expiredDeny());
|
|
36217
36749
|
}
|
|
36218
36750
|
const decision = await raceDecision(id, heartbeat);
|
|
36219
36751
|
if (!decision)
|
|
@@ -36221,31 +36753,38 @@ function createServer(opts) {
|
|
|
36221
36753
|
clearDecision(id);
|
|
36222
36754
|
return Response.json(decision);
|
|
36223
36755
|
}
|
|
36224
|
-
async function handleDraft(req,
|
|
36756
|
+
async function handleDraft(req, route) {
|
|
36757
|
+
const { id } = route;
|
|
36225
36758
|
const raw = await req.json().catch(() => ({}));
|
|
36226
36759
|
const invalid = malformedLineAnchor(raw);
|
|
36227
36760
|
if (invalid)
|
|
36228
36761
|
return Response.json({ error: invalid }, { status: 400 });
|
|
36229
36762
|
const body = DraftBodySchema.parse(raw);
|
|
36230
36763
|
const updated = await store.update(id, (r) => {
|
|
36231
|
-
|
|
36232
|
-
|
|
36764
|
+
const cur = currentVersion(r);
|
|
36765
|
+
const isStale = body.version != null && body.version !== cur.version;
|
|
36766
|
+
if (body.annotations != null && !isStale) {
|
|
36767
|
+
cur.annotations = body.annotations;
|
|
36233
36768
|
}
|
|
36234
36769
|
if (body.generalCommentDraft != null) {
|
|
36235
36770
|
r.generalCommentDraft = body.generalCommentDraft;
|
|
36236
36771
|
}
|
|
36237
|
-
|
|
36238
|
-
if (body.composerScratches != null && (body.version == null || body.version === cur.version)) {
|
|
36772
|
+
if (body.composerScratches != null && !isStale) {
|
|
36239
36773
|
cur.composerScratches = body.composerScratches;
|
|
36240
36774
|
}
|
|
36241
36775
|
});
|
|
36242
36776
|
if (updated)
|
|
36243
|
-
log.debug("draft", `draft saved: ${shortId(id)}
|
|
36777
|
+
route.log.debug("draft", `draft saved: ${shortId(id)}`);
|
|
36244
36778
|
return updated ? Response.json({ ok: true }) : notFound();
|
|
36245
36779
|
}
|
|
36246
|
-
async function handleResolve(req,
|
|
36780
|
+
async function handleResolve(req, route) {
|
|
36781
|
+
const { id } = route;
|
|
36247
36782
|
const body = await parseBody(req, ResolveBodySchema);
|
|
36248
36783
|
const existing = store.get(id);
|
|
36784
|
+
if (isStaleVersion(existing, body.version)) {
|
|
36785
|
+
route.log.info("resolve", "resolve refused: stale version", { version: body.version });
|
|
36786
|
+
return versionConflict();
|
|
36787
|
+
}
|
|
36249
36788
|
if (existing?.status !== "pending")
|
|
36250
36789
|
return notFound();
|
|
36251
36790
|
const decision = {
|
|
@@ -36270,26 +36809,28 @@ function createServer(opts) {
|
|
|
36270
36809
|
if (existing.cmux)
|
|
36271
36810
|
cfg.markPaneRead(existing.cmux);
|
|
36272
36811
|
setTimeout(() => resolveDecision(id, decision), 0);
|
|
36273
|
-
log.info("resolve", `review ${shortId(id)} resolved: ${decision.behavior}`, {
|
|
36274
|
-
reviewId: id,
|
|
36812
|
+
route.log.info("resolve", `review ${shortId(id)} resolved: ${decision.behavior}`, {
|
|
36275
36813
|
sessionId: existing.sessionId,
|
|
36276
36814
|
acceptMode: decision.acceptMode
|
|
36277
36815
|
});
|
|
36278
36816
|
return Response.json({ ok: true });
|
|
36279
36817
|
}
|
|
36280
|
-
async function handleExpire(
|
|
36281
|
-
|
|
36818
|
+
async function handleExpire(req, route) {
|
|
36819
|
+
const { id } = route;
|
|
36282
36820
|
const existing = store.get(id);
|
|
36821
|
+
if (isStaleVersion(existing ?? await store.persisted(id), versionQuery(req))) {
|
|
36822
|
+
return versionConflict();
|
|
36823
|
+
}
|
|
36824
|
+
clearDecision(id);
|
|
36283
36825
|
if (existing?.status !== "pending")
|
|
36284
36826
|
return notFound();
|
|
36285
36827
|
await store.expire(id);
|
|
36286
|
-
log.info("review", `review expired: ${shortId(id)}`, {
|
|
36287
|
-
reviewId: id,
|
|
36828
|
+
route.log.info("review", `review expired: ${shortId(id)}`, {
|
|
36288
36829
|
sessionId: existing.sessionId
|
|
36289
36830
|
});
|
|
36290
36831
|
return Response.json({ ok: true });
|
|
36291
36832
|
}
|
|
36292
|
-
function handleSeen(id) {
|
|
36833
|
+
function handleSeen({ id }) {
|
|
36293
36834
|
const existing = store.get(id);
|
|
36294
36835
|
if (!existing)
|
|
36295
36836
|
return notFound();
|
|
@@ -36297,7 +36838,8 @@ function createServer(opts) {
|
|
|
36297
36838
|
cfg.markPaneRead(existing.cmux);
|
|
36298
36839
|
return new Response(null, { status: 204 });
|
|
36299
36840
|
}
|
|
36300
|
-
async function dispatch(req,
|
|
36841
|
+
async function dispatch(req, routed) {
|
|
36842
|
+
const { method, path, route } = routed;
|
|
36301
36843
|
const readsUi = method === "GET" || method === "HEAD";
|
|
36302
36844
|
if (method === "GET" && path === "/api/health")
|
|
36303
36845
|
return handleHealth();
|
|
@@ -36305,12 +36847,14 @@ function createServer(opts) {
|
|
|
36305
36847
|
return handleDiagnostics();
|
|
36306
36848
|
if (method === "GET" && path === "/api/update")
|
|
36307
36849
|
return handleUpdate();
|
|
36850
|
+
if (method === "GET" && path === "/api/update/changes")
|
|
36851
|
+
return handleUpdateChanges();
|
|
36308
36852
|
if (method === "POST" && path === "/api/retire")
|
|
36309
36853
|
return handleRetire();
|
|
36310
36854
|
if (readsUi && (path === "/" || path === INDEX_PATH))
|
|
36311
36855
|
return handleIndex(req);
|
|
36312
36856
|
if (method === "POST" && path === "/api/reviews")
|
|
36313
|
-
return handleCreateReview(req);
|
|
36857
|
+
return handleCreateReview(req, routed);
|
|
36314
36858
|
if (method === "POST" && path === "/api/logs")
|
|
36315
36859
|
return handleLogs(req);
|
|
36316
36860
|
if (method === "POST" && path === "/api/ui/gone")
|
|
@@ -36319,34 +36863,33 @@ function createServer(opts) {
|
|
|
36319
36863
|
return handleListReviews();
|
|
36320
36864
|
if (method === "POST" && path === "/api/config")
|
|
36321
36865
|
return handleSetConfig(req);
|
|
36322
|
-
const route = matchIdRoute(path);
|
|
36323
36866
|
if (route) {
|
|
36324
|
-
const {
|
|
36867
|
+
const { sub } = route;
|
|
36325
36868
|
if (method === "GET" && !sub)
|
|
36326
|
-
return handleGetReview(
|
|
36869
|
+
return handleGetReview(route);
|
|
36327
36870
|
if (method === "GET" && sub === "/file")
|
|
36328
|
-
return handleFileExcerpt(req,
|
|
36871
|
+
return handleFileExcerpt(req, route);
|
|
36329
36872
|
if (method === "GET" && sub === "/dir")
|
|
36330
|
-
return handleDirListing(req,
|
|
36873
|
+
return handleDirListing(req, route);
|
|
36331
36874
|
if (method === "GET" && sub === "/skills")
|
|
36332
|
-
return handleSkills(
|
|
36875
|
+
return handleSkills(route);
|
|
36333
36876
|
if (method === "GET" && sub === "/skill-description") {
|
|
36334
|
-
return handleSkillDescription(req,
|
|
36877
|
+
return handleSkillDescription(req, route);
|
|
36335
36878
|
}
|
|
36336
36879
|
if (method === "POST" && sub === "/file-refs")
|
|
36337
|
-
return handleFileRefs(req,
|
|
36880
|
+
return handleFileRefs(req, route);
|
|
36338
36881
|
if (method === "POST" && sub === "/file-search")
|
|
36339
|
-
return handleFileSearch(req,
|
|
36882
|
+
return handleFileSearch(req, route);
|
|
36340
36883
|
if (method === "GET" && sub === "/decision")
|
|
36341
|
-
return handleDecision(
|
|
36884
|
+
return handleDecision(req, route);
|
|
36342
36885
|
if (method === "PUT" && sub === "/draft")
|
|
36343
|
-
return handleDraft(req,
|
|
36886
|
+
return handleDraft(req, route);
|
|
36344
36887
|
if (method === "POST" && sub === "/resolve")
|
|
36345
|
-
return handleResolve(req,
|
|
36888
|
+
return handleResolve(req, route);
|
|
36346
36889
|
if (method === "POST" && sub === "/expire")
|
|
36347
|
-
return handleExpire(
|
|
36890
|
+
return handleExpire(req, route);
|
|
36348
36891
|
if (method === "POST" && sub === "/seen")
|
|
36349
|
-
return handleSeen(
|
|
36892
|
+
return handleSeen(route);
|
|
36350
36893
|
}
|
|
36351
36894
|
if (readsUi) {
|
|
36352
36895
|
const asset = handleAsset(req, path);
|
|
@@ -36357,6 +36900,7 @@ function createServer(opts) {
|
|
|
36357
36900
|
}
|
|
36358
36901
|
async function handle(req, self) {
|
|
36359
36902
|
const end = liveness.begin(req.method);
|
|
36903
|
+
let routed;
|
|
36360
36904
|
try {
|
|
36361
36905
|
const port = self.port ?? -1;
|
|
36362
36906
|
if (isForeignHost(req, port)) {
|
|
@@ -36368,10 +36912,13 @@ function createServer(opts) {
|
|
|
36368
36912
|
if (!isSafeMethod(method) && isCrossOrigin(req, port)) {
|
|
36369
36913
|
return new Response("cross-origin request blocked", { status: 403 });
|
|
36370
36914
|
}
|
|
36371
|
-
|
|
36915
|
+
const idRoute = matchIdRoute(path);
|
|
36916
|
+
const route = idRoute && { ...idRoute, log: log.child({ reviewId: idRoute.id }) };
|
|
36917
|
+
routed = { method, path, route, log: route?.log ?? log };
|
|
36918
|
+
return await dispatch(req, routed);
|
|
36372
36919
|
} catch (err) {
|
|
36373
36920
|
try {
|
|
36374
|
-
log.error("request", err);
|
|
36921
|
+
(routed?.log ?? log).error("request", "request-failed", err);
|
|
36375
36922
|
} catch {}
|
|
36376
36923
|
return new Response("internal error", { status: 500 });
|
|
36377
36924
|
} finally {
|
|
@@ -36405,8 +36952,8 @@ function createServer(opts) {
|
|
|
36405
36952
|
instanceId
|
|
36406
36953
|
};
|
|
36407
36954
|
const tmp = `${lockPath}.tmp.${process.pid}`;
|
|
36408
|
-
|
|
36409
|
-
|
|
36955
|
+
writeFileSync6(tmp, JSON.stringify(lock));
|
|
36956
|
+
renameSync4(tmp, lockPath);
|
|
36410
36957
|
} catch {}
|
|
36411
36958
|
}
|
|
36412
36959
|
function removeLock() {
|
|
@@ -36432,7 +36979,7 @@ function createServer(opts) {
|
|
|
36432
36979
|
// src/daemon/update-check.ts
|
|
36433
36980
|
init_paths3();
|
|
36434
36981
|
init_json_file();
|
|
36435
|
-
import { writeFileSync as
|
|
36982
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
36436
36983
|
import { dirname as dirname7 } from "path";
|
|
36437
36984
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
36438
36985
|
var REFRESH_COMMAND = "bunx --no-cache @macintacos/caret@latest install --refresh";
|
|
@@ -36502,7 +37049,7 @@ function fileUpdateCache(path) {
|
|
|
36502
37049
|
write: (entry) => {
|
|
36503
37050
|
try {
|
|
36504
37051
|
ensureStateDir(dirname7(path));
|
|
36505
|
-
|
|
37052
|
+
writeFileSync7(path, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
36506
37053
|
} catch {}
|
|
36507
37054
|
}
|
|
36508
37055
|
};
|
|
@@ -36564,44 +37111,7 @@ function startUpkeep({ tasks, log, everyMs, schedule }) {
|
|
|
36564
37111
|
// src/commands/daemon.ts
|
|
36565
37112
|
init_build_id();
|
|
36566
37113
|
init_log();
|
|
36567
|
-
|
|
36568
|
-
// src/lib/upstream.ts
|
|
36569
|
-
init_package();
|
|
36570
|
-
var NPM_LATEST_URL = `https://registry.npmjs.org/${package_default.name}/latest`;
|
|
36571
|
-
var LATEST_RELEASE_URL = "https://api.github.com/repos/macintacos/caret/releases/latest";
|
|
36572
|
-
var COMPARE_URL = "https://api.github.com/repos/macintacos/caret/compare";
|
|
36573
|
-
var UPSTREAM_TIMEOUT_MS = 3000;
|
|
36574
|
-
var GITHUB_HEADERS = { "user-agent": "caret" };
|
|
36575
|
-
async function readJson(url, fetchImpl, headers) {
|
|
36576
|
-
try {
|
|
36577
|
-
const res = await fetchImpl(url, {
|
|
36578
|
-
signal: AbortSignal.timeout(UPSTREAM_TIMEOUT_MS),
|
|
36579
|
-
...headers ? { headers } : {}
|
|
36580
|
-
});
|
|
36581
|
-
return res.ok ? await res.json() : null;
|
|
36582
|
-
} catch {
|
|
36583
|
-
return null;
|
|
36584
|
-
}
|
|
36585
|
-
}
|
|
36586
|
-
async function publishedCaretVersion(fetchImpl = fetch) {
|
|
36587
|
-
const body = await readJson(NPM_LATEST_URL, fetchImpl);
|
|
36588
|
-
const v = body?.version;
|
|
36589
|
-
return typeof v === "string" && v.length > 0 ? v : null;
|
|
36590
|
-
}
|
|
36591
|
-
async function latestReleaseTag(fetchImpl = fetch) {
|
|
36592
|
-
const body = await readJson(LATEST_RELEASE_URL, fetchImpl, GITHUB_HEADERS);
|
|
36593
|
-
const tag = body?.tag_name;
|
|
36594
|
-
if (typeof tag !== "string" || tag.length === 0)
|
|
36595
|
-
return null;
|
|
36596
|
-
const stripped = tag.replace(/^v/, "");
|
|
36597
|
-
return stripped.length > 0 ? stripped : null;
|
|
36598
|
-
}
|
|
36599
|
-
async function commitsAheadOfTrunk(commit, fetchImpl = fetch) {
|
|
36600
|
-
const url = `${COMPARE_URL}/${encodeURIComponent(commit)}...trunk`;
|
|
36601
|
-
const body = await readJson(url, fetchImpl, GITHUB_HEADERS);
|
|
36602
|
-
const ahead = body?.ahead_by;
|
|
36603
|
-
return typeof ahead === "number" && Number.isFinite(ahead) ? ahead : null;
|
|
36604
|
-
}
|
|
37114
|
+
init_upstream();
|
|
36605
37115
|
|
|
36606
37116
|
// src/review/store.ts
|
|
36607
37117
|
init_paths3();
|
|
@@ -36733,11 +37243,16 @@ async function runDaemon(opts) {
|
|
|
36733
37243
|
});
|
|
36734
37244
|
warnInvalidEnvVars((msg) => log.warn("env", msg));
|
|
36735
37245
|
const adapter = selectAdapter();
|
|
36736
|
-
const
|
|
36737
|
-
const
|
|
36738
|
-
const
|
|
37246
|
+
const built = { install: buildKind(), version: VERSION, commit: currentCommit() };
|
|
37247
|
+
const devPose = process.env.CARET_DEV_UPDATE;
|
|
37248
|
+
const posed = devUpdateIdentity(devPose, built, sourceGit);
|
|
37249
|
+
if (devPose && built.install === "dev") {
|
|
37250
|
+
log.info("update", posed ? `CARET_DEV_UPDATE=${devPose}: posing as ${posed.install} ${posed.version} at ${posed.commit.slice(0, 7)}` : `CARET_DEV_UPDATE=${devPose}: no older build to pose as`);
|
|
37251
|
+
}
|
|
37252
|
+
const { install, version, commit } = posed ?? built;
|
|
36739
37253
|
const updateCache = fileUpdateCache(updateCheckFile());
|
|
36740
37254
|
let updateStatus = readCachedStatus(updateCache, version, commit);
|
|
37255
|
+
const servedReport = () => updateReportFor({ install, version, commit }, updateStatus, svc.current().updates.check);
|
|
36741
37256
|
function refreshUpdate() {
|
|
36742
37257
|
runUpdateCheck({
|
|
36743
37258
|
kind: install,
|
|
@@ -36753,7 +37268,7 @@ async function runDaemon(opts) {
|
|
|
36753
37268
|
}).then((status) => {
|
|
36754
37269
|
if (status)
|
|
36755
37270
|
updateStatus = status;
|
|
36756
|
-
}, (err) => log.error("update", err));
|
|
37271
|
+
}, (err) => log.error("update", "update-check-failed", err));
|
|
36757
37272
|
}
|
|
36758
37273
|
const store = createStore(reviewsDir(), log);
|
|
36759
37274
|
await store.rehydrate();
|
|
@@ -36778,10 +37293,14 @@ async function runDaemon(opts) {
|
|
|
36778
37293
|
shutdown(0);
|
|
36779
37294
|
});
|
|
36780
37295
|
process.once("exit", removeOwnDaemonLock);
|
|
36781
|
-
|
|
37296
|
+
process.once("exit", removeOwnBootMarker);
|
|
37297
|
+
function exitTerminal(reason, code, err) {
|
|
36782
37298
|
process.stderr.write(`caret: ${reason}; exiting.
|
|
36783
37299
|
`);
|
|
36784
|
-
log.error("fatal", err, {
|
|
37300
|
+
log.error("fatal", code, err, {
|
|
37301
|
+
reason,
|
|
37302
|
+
exitStatus: SERVICE_TERMINAL_EXIT_STATUS
|
|
37303
|
+
});
|
|
36785
37304
|
process.exit(SERVICE_TERMINAL_EXIT_STATUS);
|
|
36786
37305
|
}
|
|
36787
37306
|
const buildId = await currentBuildId();
|
|
@@ -36798,7 +37317,7 @@ async function runDaemon(opts) {
|
|
|
36798
37317
|
lockPath: daemonLock(),
|
|
36799
37318
|
buildId,
|
|
36800
37319
|
assetDigest,
|
|
36801
|
-
commit,
|
|
37320
|
+
commit: built.commit,
|
|
36802
37321
|
stateDir: stateDir(),
|
|
36803
37322
|
instanceId: randomUUID().slice(0, 8),
|
|
36804
37323
|
approveVariants: adapter.approveVariants,
|
|
@@ -36812,8 +37331,18 @@ async function runDaemon(opts) {
|
|
|
36812
37331
|
settings: () => settings().current(),
|
|
36813
37332
|
configPath: cfg
|
|
36814
37333
|
})),
|
|
36815
|
-
updateReport:
|
|
37334
|
+
updateReport: servedReport,
|
|
36816
37335
|
onUpdatesEnabled: refreshUpdate,
|
|
37336
|
+
updateChanges: createUpdateChanges({
|
|
37337
|
+
install,
|
|
37338
|
+
version,
|
|
37339
|
+
commit,
|
|
37340
|
+
status: () => servedReport().status,
|
|
37341
|
+
releases: listReleases,
|
|
37342
|
+
compare: compareToTrunk,
|
|
37343
|
+
log
|
|
37344
|
+
}),
|
|
37345
|
+
restartHint: resident ? undefined : adapter.restartHint,
|
|
36817
37346
|
log
|
|
36818
37347
|
});
|
|
36819
37348
|
} catch (e) {
|
|
@@ -36822,10 +37351,11 @@ async function runDaemon(opts) {
|
|
|
36822
37351
|
`);
|
|
36823
37352
|
process.exit(0);
|
|
36824
37353
|
}
|
|
36825
|
-
exitTerminal("cannot bind the daemon port", e);
|
|
37354
|
+
exitTerminal("cannot bind the daemon port", "daemon-bind-failed", e);
|
|
36826
37355
|
}
|
|
37356
|
+
removeOwnBootMarker();
|
|
36827
37357
|
const onFatal = (label) => (err) => {
|
|
36828
|
-
log.error(label, err);
|
|
37358
|
+
log.error(label, "daemon-crashed", err);
|
|
36829
37359
|
shutdown(1);
|
|
36830
37360
|
};
|
|
36831
37361
|
process.once("uncaughtException", onFatal("uncaughtException"));
|
|
@@ -36844,21 +37374,141 @@ async function runDaemon(opts) {
|
|
|
36844
37374
|
return server.port;
|
|
36845
37375
|
}
|
|
36846
37376
|
|
|
36847
|
-
// src/commands/
|
|
37377
|
+
// src/commands/doctor.ts
|
|
36848
37378
|
init_adapters();
|
|
37379
|
+
init_paths2();
|
|
37380
|
+
init_upgrade();
|
|
37381
|
+
import { existsSync as existsSync11 } from "fs";
|
|
37382
|
+
import { release } from "os";
|
|
37383
|
+
|
|
37384
|
+
// src/commands/install/ui.ts
|
|
37385
|
+
function isTerminal() {
|
|
37386
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
37387
|
+
}
|
|
37388
|
+
var silentUI = {
|
|
37389
|
+
intro: () => {},
|
|
37390
|
+
step: async (_label, work) => work(() => {}),
|
|
37391
|
+
info: () => {},
|
|
37392
|
+
warn: () => {},
|
|
37393
|
+
error: () => {},
|
|
37394
|
+
note: () => {},
|
|
37395
|
+
cancel: () => {},
|
|
37396
|
+
outro: () => {}
|
|
37397
|
+
};
|
|
37398
|
+
function plainUI(write) {
|
|
37399
|
+
const line = (s) => write(`caret: ${s}
|
|
37400
|
+
`);
|
|
37401
|
+
return {
|
|
37402
|
+
intro: (action) => line(action),
|
|
37403
|
+
async step(label, work, done) {
|
|
37404
|
+
try {
|
|
37405
|
+
const value = await work(() => {});
|
|
37406
|
+
line(done ? done(value) : label);
|
|
37407
|
+
return value;
|
|
37408
|
+
} catch (e) {
|
|
37409
|
+
line(`${label} \u2014 failed`);
|
|
37410
|
+
throw e;
|
|
37411
|
+
}
|
|
37412
|
+
},
|
|
37413
|
+
info: line,
|
|
37414
|
+
warn: line,
|
|
37415
|
+
error: line,
|
|
37416
|
+
note: (body, title) => line(`${title}
|
|
37417
|
+
${body.split(`
|
|
37418
|
+
`).map((l) => ` ${l}`).join(`
|
|
37419
|
+
`)}`),
|
|
37420
|
+
cancel: line,
|
|
37421
|
+
outro: line
|
|
37422
|
+
};
|
|
37423
|
+
}
|
|
37424
|
+
async function createInstallUI(opts = {}) {
|
|
37425
|
+
const write = opts.write ?? ((s) => void process.stdout.write(s));
|
|
37426
|
+
const interactive = opts.interactive ?? (process.stdout.isTTY === true && !process.env.NO_COLOR && process.env.CI !== "true");
|
|
37427
|
+
if (!interactive)
|
|
37428
|
+
return plainUI(write);
|
|
37429
|
+
const clack = await Promise.resolve().then(() => (init_dist5(), {}));
|
|
37430
|
+
return {
|
|
37431
|
+
intro: (action) => intro(`caret \xB7 ${action}`),
|
|
37432
|
+
async step(label, work, done) {
|
|
37433
|
+
const spin = spinner();
|
|
37434
|
+
spin.start(label);
|
|
37435
|
+
try {
|
|
37436
|
+
const value = await work((m) => spin.message(m));
|
|
37437
|
+
spin.stop(done ? done(value) : label);
|
|
37438
|
+
return value;
|
|
37439
|
+
} catch (e) {
|
|
37440
|
+
spin.error(label);
|
|
37441
|
+
throw e;
|
|
37442
|
+
}
|
|
37443
|
+
},
|
|
37444
|
+
info: (m) => log.info(m),
|
|
37445
|
+
warn: (m) => log.warn(m),
|
|
37446
|
+
error: (m) => log.error(m),
|
|
37447
|
+
note: (body, title) => note(body, title),
|
|
37448
|
+
cancel: (m) => cancel(m),
|
|
37449
|
+
outro: (m) => outro(m)
|
|
37450
|
+
};
|
|
37451
|
+
}
|
|
37452
|
+
|
|
37453
|
+
// src/commands/doctor.ts
|
|
36849
37454
|
init_paths3();
|
|
36850
37455
|
init_settings();
|
|
36851
37456
|
init_lifecycle();
|
|
36852
|
-
import { existsSync as existsSync10 } from "fs";
|
|
36853
|
-
import { release } from "os";
|
|
36854
37457
|
|
|
36855
|
-
// src/
|
|
37458
|
+
// src/doctor/bundle.ts
|
|
37459
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync11 } from "fs";
|
|
37460
|
+
import { basename as basename3, join as join16 } from "path";
|
|
37461
|
+
function stamp2(now) {
|
|
37462
|
+
return now.toISOString().replace(/[-:.]/g, "");
|
|
37463
|
+
}
|
|
37464
|
+
function bundlePath(stateDir, now) {
|
|
37465
|
+
return join16(stateDir, `doctor-${stamp2(now)}.zip`);
|
|
37466
|
+
}
|
|
37467
|
+
function member(path, name) {
|
|
37468
|
+
try {
|
|
37469
|
+
return { name, data: readFileSync11(path) };
|
|
37470
|
+
} catch {
|
|
37471
|
+
return null;
|
|
37472
|
+
}
|
|
37473
|
+
}
|
|
37474
|
+
var MAX_REVIEW_MEMBERS = 5000;
|
|
37475
|
+
function entriesFor(deps) {
|
|
37476
|
+
const logs = deps.logPaths.map((p) => member(p, `logs/${basename3(p)}`));
|
|
37477
|
+
let reviews;
|
|
37478
|
+
try {
|
|
37479
|
+
reviews = readdirSync5(deps.reviewsDir).filter((f) => f.endsWith(".json"));
|
|
37480
|
+
} catch {
|
|
37481
|
+
reviews = [];
|
|
37482
|
+
}
|
|
37483
|
+
const records = reviews.slice(0, MAX_REVIEW_MEMBERS).map((f) => member(join16(deps.reviewsDir, f), `reviews/${f}`));
|
|
37484
|
+
return [...logs, ...records].filter((e) => e !== null);
|
|
37485
|
+
}
|
|
37486
|
+
async function runBundle(opts, deps) {
|
|
37487
|
+
if (!opts.yes) {
|
|
37488
|
+
if (!deps.isInteractive()) {
|
|
37489
|
+
return {
|
|
37490
|
+
kind: "refused",
|
|
37491
|
+
message: "a diagnostics bundle holds unredacted logs and full plan bodies, so it is never written unasked \u2014 re-run with --yes to confirm"
|
|
37492
|
+
};
|
|
37493
|
+
}
|
|
37494
|
+
if (await deps.confirm() !== true)
|
|
37495
|
+
return { kind: "declined" };
|
|
37496
|
+
}
|
|
37497
|
+
const now = deps.now();
|
|
37498
|
+
const path = bundlePath(deps.stateDir, now);
|
|
37499
|
+
deps.write(path, entriesFor(deps), now);
|
|
37500
|
+
return { kind: "written", path };
|
|
37501
|
+
}
|
|
37502
|
+
|
|
37503
|
+
// src/doctor/report.ts
|
|
36856
37504
|
init_paths3();
|
|
36857
37505
|
init_json_file();
|
|
36858
37506
|
init_log();
|
|
36859
|
-
|
|
36860
|
-
import {
|
|
37507
|
+
init_node();
|
|
37508
|
+
import { readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
37509
|
+
import { basename as basename4, join as join17 } from "path";
|
|
36861
37510
|
var CARET_BIN = "caret-native";
|
|
37511
|
+
var ERROR_RECORD_FIELDS = ["time", "source", "step", "code", "reviewId", "sessionId"];
|
|
36862
37512
|
async function safe(build) {
|
|
36863
37513
|
try {
|
|
36864
37514
|
return await build();
|
|
@@ -36874,19 +37524,31 @@ async function collectReport(deps) {
|
|
|
36874
37524
|
} catch (e) {
|
|
36875
37525
|
healthError = { error: errorMessage(e) };
|
|
36876
37526
|
}
|
|
36877
|
-
const [
|
|
37527
|
+
const [
|
|
37528
|
+
system,
|
|
37529
|
+
install,
|
|
37530
|
+
settings,
|
|
37531
|
+
daemon,
|
|
37532
|
+
lockAndPort,
|
|
37533
|
+
processes,
|
|
37534
|
+
reviews,
|
|
37535
|
+
installState,
|
|
37536
|
+
logs,
|
|
37537
|
+
failures
|
|
37538
|
+
] = await Promise.all([
|
|
36878
37539
|
safe(() => deps.system()),
|
|
36879
37540
|
safe(() => deps.install()),
|
|
36880
37541
|
safe(() => buildSettings(deps)),
|
|
36881
|
-
healthError ?? safe(() => buildDaemon(health)),
|
|
37542
|
+
healthError ?? safe(() => buildDaemon(deps, health)),
|
|
36882
37543
|
healthError ?? safe(() => buildLockAndPort(deps, health)),
|
|
36883
37544
|
safe(() => buildProcesses(deps)),
|
|
36884
37545
|
safe(() => tallyReviews(deps.listReviewFiles())),
|
|
36885
37546
|
safe(() => deps.readAgentInstallState()),
|
|
36886
|
-
safe(() => buildLogs(deps))
|
|
37547
|
+
safe(() => buildLogs(deps)),
|
|
37548
|
+
safe(() => buildFailures(deps))
|
|
36887
37549
|
]);
|
|
36888
37550
|
return {
|
|
36889
|
-
schema: "caret-
|
|
37551
|
+
schema: "caret-doctor/1",
|
|
36890
37552
|
version: deps.version,
|
|
36891
37553
|
generatedAt: deps.now().toISOString(),
|
|
36892
37554
|
system,
|
|
@@ -36897,7 +37559,8 @@ async function collectReport(deps) {
|
|
|
36897
37559
|
processes,
|
|
36898
37560
|
reviews,
|
|
36899
37561
|
installState,
|
|
36900
|
-
logs
|
|
37562
|
+
logs,
|
|
37563
|
+
failures
|
|
36901
37564
|
};
|
|
36902
37565
|
}
|
|
36903
37566
|
function buildSettings(deps) {
|
|
@@ -36918,11 +37581,13 @@ function buildSettings(deps) {
|
|
|
36918
37581
|
effectiveHeartbeatMs: e.heartbeatMs
|
|
36919
37582
|
};
|
|
36920
37583
|
}
|
|
36921
|
-
function buildDaemon(health) {
|
|
37584
|
+
function buildDaemon(deps, health) {
|
|
37585
|
+
const serviceInstalled = deps.serviceInstalled();
|
|
36922
37586
|
if (!health)
|
|
36923
|
-
return { reachable: false };
|
|
37587
|
+
return { reachable: false, serviceInstalled };
|
|
36924
37588
|
return {
|
|
36925
37589
|
reachable: true,
|
|
37590
|
+
serviceInstalled,
|
|
36926
37591
|
service: health.service,
|
|
36927
37592
|
daemonVersion: health.version,
|
|
36928
37593
|
build: health.build,
|
|
@@ -36931,11 +37596,17 @@ function buildDaemon(health) {
|
|
|
36931
37596
|
}
|
|
36932
37597
|
function buildLockAndPort(deps, health) {
|
|
36933
37598
|
const portServesCaret = health?.service === "caret";
|
|
37599
|
+
const marker = deps.readBootMarker();
|
|
37600
|
+
const bootFields = marker && {
|
|
37601
|
+
bootMarkerPid: marker.pid,
|
|
37602
|
+
bootMarkerAgeMs: deps.now().getTime() - marker.claimedAt
|
|
37603
|
+
};
|
|
36934
37604
|
const lock = deps.readLock();
|
|
36935
37605
|
if (!lock)
|
|
36936
|
-
return { lockExists: false, portServesCaret };
|
|
37606
|
+
return { lockExists: false, portServesCaret, ...bootFields };
|
|
36937
37607
|
return {
|
|
36938
37608
|
lockExists: true,
|
|
37609
|
+
lockPath: daemonLock(),
|
|
36939
37610
|
lockPid: lock.pid,
|
|
36940
37611
|
lockPort: lock.port,
|
|
36941
37612
|
lockBuild: lock.build,
|
|
@@ -36943,7 +37614,8 @@ function buildLockAndPort(deps, health) {
|
|
|
36943
37614
|
lockStartedAt: lock.startedAt,
|
|
36944
37615
|
pidAlive: deps.isPidAlive(lock.pid),
|
|
36945
37616
|
portServesCaret,
|
|
36946
|
-
portMismatch: lock.port !== deps.effective().port
|
|
37617
|
+
portMismatch: lock.port !== deps.effective().port,
|
|
37618
|
+
...bootFields
|
|
36947
37619
|
};
|
|
36948
37620
|
}
|
|
36949
37621
|
function buildProcesses(deps) {
|
|
@@ -36962,6 +37634,21 @@ async function buildLogs(deps) {
|
|
|
36962
37634
|
]);
|
|
36963
37635
|
return { caret, daemon, daemonStderr };
|
|
36964
37636
|
}
|
|
37637
|
+
async function buildFailures(deps) {
|
|
37638
|
+
const [caret, daemon] = await Promise.all([
|
|
37639
|
+
deps.logErrorRecords(deps.logPaths.caret),
|
|
37640
|
+
deps.logErrorRecords(deps.logPaths.daemon)
|
|
37641
|
+
]);
|
|
37642
|
+
return groupFailures([...caret, ...daemon], deps.now().getTime());
|
|
37643
|
+
}
|
|
37644
|
+
var ERROR_WINDOW_HOURS = 24;
|
|
37645
|
+
var ERROR_WINDOW_MS = ERROR_WINDOW_HOURS * 60 * 60 * 1000;
|
|
37646
|
+
function inErrorWindow(time, generatedAt) {
|
|
37647
|
+
if (time === undefined)
|
|
37648
|
+
return true;
|
|
37649
|
+
const age = generatedAt - Date.parse(time);
|
|
37650
|
+
return Number.isNaN(age) || age < ERROR_WINDOW_MS;
|
|
37651
|
+
}
|
|
36965
37652
|
function tallyReviews(records) {
|
|
36966
37653
|
const counts = { pending: 0, approved: 0, rejected: 0, expired: 0, other: 0 };
|
|
36967
37654
|
const pendingIds = [];
|
|
@@ -36987,11 +37674,47 @@ function tallyReviews(records) {
|
|
|
36987
37674
|
}
|
|
36988
37675
|
return { ...counts, total: records.length, pendingIds };
|
|
36989
37676
|
}
|
|
36990
|
-
var
|
|
36991
|
-
function
|
|
37677
|
+
var FAILURES_CAP = 50;
|
|
37678
|
+
function groupFailures(records, generatedAt) {
|
|
37679
|
+
const recent = records.filter((r) => inErrorWindow(r.time, generatedAt)).sort((a, b) => compareTimes(a.time ?? "", b.time ?? ""));
|
|
37680
|
+
const newest = recent.slice(-FAILURES_CAP);
|
|
37681
|
+
const groups = new Map;
|
|
37682
|
+
const ungrouped = [];
|
|
37683
|
+
for (const { reviewId, sessionId, ...record } of newest) {
|
|
37684
|
+
const groupId = reviewId !== undefined ? { reviewId } : sessionId !== undefined ? { sessionId } : null;
|
|
37685
|
+
if (groupId === null) {
|
|
37686
|
+
ungrouped.push(record);
|
|
37687
|
+
continue;
|
|
37688
|
+
}
|
|
37689
|
+
const key = JSON.stringify(groupId);
|
|
37690
|
+
const group = groups.get(key) ?? { ...groupId, records: [] };
|
|
37691
|
+
group.records.push(record);
|
|
37692
|
+
groups.set(key, group);
|
|
37693
|
+
}
|
|
37694
|
+
return {
|
|
37695
|
+
windowHours: ERROR_WINDOW_HOURS,
|
|
37696
|
+
total: recent.length,
|
|
37697
|
+
omitted: recent.length - newest.length,
|
|
37698
|
+
groups: [...groups.values()],
|
|
37699
|
+
ungrouped
|
|
37700
|
+
};
|
|
37701
|
+
}
|
|
37702
|
+
function compareTimes(a, b) {
|
|
37703
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
37704
|
+
}
|
|
37705
|
+
var HEADER_KEYS = new Set(["schema", "version", "generatedAt", "checks"]);
|
|
37706
|
+
var STATUS_GLYPH = { pass: "\u2713", fail: "\u2717", unknown: "?" };
|
|
37707
|
+
var STATUS_COLOR = { pass: "\x1B[32m", fail: "\x1B[31m", unknown: "\x1B[33m" };
|
|
37708
|
+
var COLOR_RESET = "\x1B[0m";
|
|
37709
|
+
function renderStdout(doc, format, color = false) {
|
|
37710
|
+
const redacted = scrubValue(doc, true);
|
|
37711
|
+
return format === "json" ? JSON.stringify(redacted, null, 2) : renderDocument(redacted, color);
|
|
37712
|
+
}
|
|
37713
|
+
function renderDocument(doc, color = false) {
|
|
36992
37714
|
const lines = [];
|
|
36993
|
-
lines.push(`caret
|
|
36994
|
-
|
|
37715
|
+
lines.push(`caret doctor (${doc.schema}) version ${doc.version} at ${doc.generatedAt}`);
|
|
37716
|
+
lines.push("", "checks:", ...renderChecks(doc.checks, color));
|
|
37717
|
+
const sections = Object.entries(doc).filter(([key]) => !HEADER_KEYS.has(key));
|
|
36995
37718
|
for (const [title, value] of sections) {
|
|
36996
37719
|
lines.push("");
|
|
36997
37720
|
lines.push(`${title}:`);
|
|
@@ -37000,6 +37723,19 @@ function renderReport(report) {
|
|
|
37000
37723
|
return lines.join(`
|
|
37001
37724
|
`);
|
|
37002
37725
|
}
|
|
37726
|
+
function renderChecks(checks, color) {
|
|
37727
|
+
const out = [];
|
|
37728
|
+
for (const c of checks) {
|
|
37729
|
+
const glyph = STATUS_GLYPH[c.status];
|
|
37730
|
+
const marker = color ? `${STATUS_COLOR[c.status]}${glyph}${COLOR_RESET}` : glyph;
|
|
37731
|
+
out.push(` ${marker} ${c.id}${c.detail ? ` \u2014 ${c.detail}` : ""}`);
|
|
37732
|
+
if (c.status === "fail")
|
|
37733
|
+
out.push(` remedy: ${c.remedy}`);
|
|
37734
|
+
if (c.status === "unknown")
|
|
37735
|
+
out.push(` reason: ${c.reason}`);
|
|
37736
|
+
}
|
|
37737
|
+
return out;
|
|
37738
|
+
}
|
|
37003
37739
|
function isSectionError(v) {
|
|
37004
37740
|
return typeof v === "object" && v !== null && "error" in v && typeof v.error === "string";
|
|
37005
37741
|
}
|
|
@@ -37038,7 +37774,7 @@ function parsePsLines(text) {
|
|
|
37038
37774
|
const [, pidStr, comm] = m ?? [];
|
|
37039
37775
|
if (pidStr === undefined || comm === undefined)
|
|
37040
37776
|
continue;
|
|
37041
|
-
const name =
|
|
37777
|
+
const name = basename4(comm.trim());
|
|
37042
37778
|
if (name === CARET_BIN)
|
|
37043
37779
|
out.push({ pid: Number(pidStr), name });
|
|
37044
37780
|
}
|
|
@@ -37058,7 +37794,7 @@ function listReviewFiles() {
|
|
|
37058
37794
|
const dir = reviewsDir();
|
|
37059
37795
|
let files;
|
|
37060
37796
|
try {
|
|
37061
|
-
files =
|
|
37797
|
+
files = readdirSync6(dir);
|
|
37062
37798
|
} catch {
|
|
37063
37799
|
return [];
|
|
37064
37800
|
}
|
|
@@ -37068,7 +37804,7 @@ function listReviewFiles() {
|
|
|
37068
37804
|
continue;
|
|
37069
37805
|
if (out.length >= 5000)
|
|
37070
37806
|
break;
|
|
37071
|
-
const raw = readJsonFileSync(
|
|
37807
|
+
const raw = readJsonFileSync(join17(dir, file));
|
|
37072
37808
|
if (raw && typeof raw.id === "string" && typeof raw.status === "string") {
|
|
37073
37809
|
out.push({ id: raw.id, status: raw.status });
|
|
37074
37810
|
}
|
|
@@ -37084,44 +37820,321 @@ async function logStats(path) {
|
|
|
37084
37820
|
return { path, exists: false, size: 0, errors: 0, warns: 0 };
|
|
37085
37821
|
}
|
|
37086
37822
|
try {
|
|
37087
|
-
const
|
|
37088
|
-
const
|
|
37089
|
-
|
|
37090
|
-
return { path, exists: true, size, errors, warns };
|
|
37823
|
+
const { text, partial } = await readTail(path, size);
|
|
37824
|
+
const { errors, warns, lastErrorAt } = countLogLevels(text, partial);
|
|
37825
|
+
return { path, exists: true, size, errors, warns, lastErrorAt };
|
|
37091
37826
|
} catch {
|
|
37092
37827
|
return { path, exists: true, size, errors: 0, warns: 0 };
|
|
37093
37828
|
}
|
|
37094
37829
|
}
|
|
37095
|
-
function
|
|
37096
|
-
|
|
37097
|
-
|
|
37098
|
-
|
|
37099
|
-
|
|
37100
|
-
|
|
37830
|
+
async function readTail(path, size) {
|
|
37831
|
+
const start = Math.max(0, size - TAIL_BYTES);
|
|
37832
|
+
return { text: await Bun.file(path).slice(start).text(), partial: start > 0 };
|
|
37833
|
+
}
|
|
37834
|
+
async function logErrorRecords(path) {
|
|
37835
|
+
let size;
|
|
37836
|
+
try {
|
|
37837
|
+
size = statSync4(path).size;
|
|
37838
|
+
} catch {
|
|
37839
|
+
return [];
|
|
37840
|
+
}
|
|
37841
|
+
const { text, partial } = await readTail(path, size);
|
|
37842
|
+
return parseErrorRecords(text, partial);
|
|
37843
|
+
}
|
|
37844
|
+
function parseErrorRecords(tailText, dropFirstLine) {
|
|
37845
|
+
const out = [];
|
|
37846
|
+
for (const record of tailRecords(tailText, dropFirstLine)) {
|
|
37847
|
+
if (typeof record.level !== "number" || record.level < 50)
|
|
37848
|
+
continue;
|
|
37849
|
+
const triageFields = {};
|
|
37850
|
+
for (const field of ERROR_RECORD_FIELDS) {
|
|
37851
|
+
const value = record[field];
|
|
37852
|
+
if (typeof value === "string")
|
|
37853
|
+
triageFields[field] = value;
|
|
37854
|
+
}
|
|
37855
|
+
out.push(triageFields);
|
|
37856
|
+
}
|
|
37857
|
+
return out;
|
|
37858
|
+
}
|
|
37859
|
+
function* tailRecords(tailText, dropFirstLine) {
|
|
37860
|
+
for (const [i, line] of tailText.split(`
|
|
37861
|
+
`).entries()) {
|
|
37101
37862
|
if (i === 0 && dropFirstLine)
|
|
37102
37863
|
continue;
|
|
37103
37864
|
if (!line.startsWith("{"))
|
|
37104
37865
|
continue;
|
|
37105
|
-
let
|
|
37866
|
+
let record;
|
|
37106
37867
|
try {
|
|
37107
|
-
|
|
37868
|
+
record = JSON.parse(line);
|
|
37108
37869
|
} catch {
|
|
37109
37870
|
continue;
|
|
37110
37871
|
}
|
|
37111
|
-
if (typeof
|
|
37872
|
+
if (typeof record === "object" && record !== null)
|
|
37873
|
+
yield record;
|
|
37874
|
+
}
|
|
37875
|
+
}
|
|
37876
|
+
function countLogLevels(tailText, dropFirstLine) {
|
|
37877
|
+
let errors = 0;
|
|
37878
|
+
let warns = 0;
|
|
37879
|
+
let lastErrorAt;
|
|
37880
|
+
for (const record of tailRecords(tailText, dropFirstLine)) {
|
|
37881
|
+
if (typeof record.level !== "number")
|
|
37112
37882
|
continue;
|
|
37113
|
-
if (level >= 50)
|
|
37883
|
+
if (record.level >= 50) {
|
|
37114
37884
|
errors++;
|
|
37115
|
-
|
|
37885
|
+
lastErrorAt = typeof record.time === "string" ? record.time : undefined;
|
|
37886
|
+
} else if (record.level === 40)
|
|
37116
37887
|
warns++;
|
|
37117
37888
|
}
|
|
37118
|
-
return { errors, warns };
|
|
37889
|
+
return { errors, warns, lastErrorAt };
|
|
37890
|
+
}
|
|
37891
|
+
|
|
37892
|
+
// src/doctor/checks.ts
|
|
37893
|
+
function present(value) {
|
|
37894
|
+
return isSectionError(value) ? undefined : value;
|
|
37895
|
+
}
|
|
37896
|
+
function undecided(id, title, section) {
|
|
37897
|
+
return {
|
|
37898
|
+
id,
|
|
37899
|
+
title,
|
|
37900
|
+
status: "unknown",
|
|
37901
|
+
detail: "",
|
|
37902
|
+
reason: `the ${section} section could not be collected`
|
|
37903
|
+
};
|
|
37904
|
+
}
|
|
37905
|
+
function daemonReachable(report) {
|
|
37906
|
+
const daemon = present(report.daemon);
|
|
37907
|
+
if (daemon === undefined)
|
|
37908
|
+
return undecided("daemon-reachable", "Daemon", "daemon");
|
|
37909
|
+
if (daemon.serviceInstalled && !daemon.reachable) {
|
|
37910
|
+
return {
|
|
37911
|
+
id: "daemon-reachable",
|
|
37912
|
+
title: "Daemon",
|
|
37913
|
+
status: "fail",
|
|
37914
|
+
detail: "a caret service is recorded for this install, but /api/health did not answer",
|
|
37915
|
+
remedy: "run `caret install --refresh` \u2014 or check the service is not disabled \u2014 then read logs/daemon-stderr.log"
|
|
37916
|
+
};
|
|
37917
|
+
}
|
|
37918
|
+
if (daemon.reachable && daemon.service !== "caret") {
|
|
37919
|
+
return {
|
|
37920
|
+
id: "daemon-reachable",
|
|
37921
|
+
title: "Daemon",
|
|
37922
|
+
status: "fail",
|
|
37923
|
+
detail: `the effective port is answering as ${daemon.service ?? "an unnamed service"}, not caret`,
|
|
37924
|
+
remedy: "stop whatever is bound to caret's effective port, or set `[daemon] port` to a free one"
|
|
37925
|
+
};
|
|
37926
|
+
}
|
|
37927
|
+
return {
|
|
37928
|
+
id: "daemon-reachable",
|
|
37929
|
+
title: "Daemon",
|
|
37930
|
+
status: "pass",
|
|
37931
|
+
detail: daemon.reachable ? "answering on the effective port" : "not running"
|
|
37932
|
+
};
|
|
37933
|
+
}
|
|
37934
|
+
function daemonLock2(report) {
|
|
37935
|
+
const lock = present(report.lockAndPort);
|
|
37936
|
+
if (lock === undefined)
|
|
37937
|
+
return undecided("daemon-lock", "Daemon lock", "lockAndPort");
|
|
37938
|
+
const faults = [];
|
|
37939
|
+
const remedies = [];
|
|
37940
|
+
if (lock.pidAlive === false) {
|
|
37941
|
+
faults.push(`its pid ${lock.lockPid} is not alive`);
|
|
37942
|
+
remedies.push(`delete the stale lock at ${lock.lockPath}`);
|
|
37943
|
+
}
|
|
37944
|
+
if (lock.portMismatch === true) {
|
|
37945
|
+
faults.push(`its port ${lock.lockPort} is not the effective one`);
|
|
37946
|
+
remedies.push("restart caret so it binds the configured port");
|
|
37947
|
+
}
|
|
37948
|
+
if (faults.length === 0) {
|
|
37949
|
+
return {
|
|
37950
|
+
id: "daemon-lock",
|
|
37951
|
+
title: "Daemon lock",
|
|
37952
|
+
status: "pass",
|
|
37953
|
+
detail: lock.lockExists ? "live, on the effective port" : "no lock file"
|
|
37954
|
+
};
|
|
37955
|
+
}
|
|
37956
|
+
return {
|
|
37957
|
+
id: "daemon-lock",
|
|
37958
|
+
title: "Daemon lock",
|
|
37959
|
+
status: "fail",
|
|
37960
|
+
detail: faults.join("; "),
|
|
37961
|
+
remedy: remedies.join("; ")
|
|
37962
|
+
};
|
|
37963
|
+
}
|
|
37964
|
+
function agentInstall(report) {
|
|
37965
|
+
const probe = present(report.installState);
|
|
37966
|
+
if (probe === undefined)
|
|
37967
|
+
return undecided("agent-install", "Agent install", "installState");
|
|
37968
|
+
if (probe.pluginEnabled === false) {
|
|
37969
|
+
return {
|
|
37970
|
+
id: "agent-install",
|
|
37971
|
+
title: "Agent install",
|
|
37972
|
+
status: "fail",
|
|
37973
|
+
detail: `${probe.agent} does not have caret enabled`,
|
|
37974
|
+
remedy: "run `caret install`"
|
|
37975
|
+
};
|
|
37976
|
+
}
|
|
37977
|
+
return {
|
|
37978
|
+
id: "agent-install",
|
|
37979
|
+
title: "Agent install",
|
|
37980
|
+
status: "pass",
|
|
37981
|
+
detail: probe.pluginEnabled === true ? `caret ${probe.pluginVersion} is enabled for ${probe.agent}` : `${probe.agent}'s install state could not be read`
|
|
37982
|
+
};
|
|
37983
|
+
}
|
|
37984
|
+
function describeErrors(log) {
|
|
37985
|
+
const when = log.lastErrorAt ? `, last at ${log.lastErrorAt}` : "";
|
|
37986
|
+
return `${log.errors} error record(s) in ${log.path}${when}`;
|
|
37987
|
+
}
|
|
37988
|
+
function logErrors(report) {
|
|
37989
|
+
const logs = present(report.logs);
|
|
37990
|
+
if (logs === undefined)
|
|
37991
|
+
return undecided("log-errors", "Logs", "logs");
|
|
37992
|
+
const noisy = Object.values(logs).filter((l) => l.errors > 0);
|
|
37993
|
+
if (noisy.length === 0) {
|
|
37994
|
+
return {
|
|
37995
|
+
id: "log-errors",
|
|
37996
|
+
title: "Logs",
|
|
37997
|
+
status: "pass",
|
|
37998
|
+
detail: "no NDJSON error records"
|
|
37999
|
+
};
|
|
38000
|
+
}
|
|
38001
|
+
const generatedAt = Date.parse(report.generatedAt);
|
|
38002
|
+
const erring = noisy.filter((l) => inErrorWindow(l.lastErrorAt, generatedAt));
|
|
38003
|
+
if (erring.length === 0) {
|
|
38004
|
+
return {
|
|
38005
|
+
id: "log-errors",
|
|
38006
|
+
title: "Logs",
|
|
38007
|
+
status: "pass",
|
|
38008
|
+
detail: `none in the last ${ERROR_WINDOW_HOURS}h; ${noisy.map(describeErrors).join("; ")}`
|
|
38009
|
+
};
|
|
38010
|
+
}
|
|
38011
|
+
return {
|
|
38012
|
+
id: "log-errors",
|
|
38013
|
+
title: "Logs",
|
|
38014
|
+
status: "fail",
|
|
38015
|
+
detail: erring.map(describeErrors).join("; "),
|
|
38016
|
+
remedy: `read ${erring.map((l) => l.path).join(" and ")}, then /systematic-debugging`
|
|
38017
|
+
};
|
|
38018
|
+
}
|
|
38019
|
+
function runChecks(report, adapterChecks = []) {
|
|
38020
|
+
return [
|
|
38021
|
+
daemonReachable(report),
|
|
38022
|
+
daemonLock2(report),
|
|
38023
|
+
agentInstall(report),
|
|
38024
|
+
logErrors(report),
|
|
38025
|
+
...adapterChecks
|
|
38026
|
+
];
|
|
38027
|
+
}
|
|
38028
|
+
|
|
38029
|
+
// src/doctor/zip.ts
|
|
38030
|
+
import { closeSync as closeSync2, openSync as openSync3, rmSync as rmSync2, writeSync } from "fs";
|
|
38031
|
+
import { crc32 } from "zlib";
|
|
38032
|
+
var LOCAL_SIG = 67324752;
|
|
38033
|
+
var CENTRAL_SIG = 33639248;
|
|
38034
|
+
var EOCD_SIG = 101010256;
|
|
38035
|
+
var ZIP_VERSION = 20;
|
|
38036
|
+
var DEFLATE = 8;
|
|
38037
|
+
var UNIX_HOST = 3;
|
|
38038
|
+
var ENTRY_MODE = 384;
|
|
38039
|
+
function dosStamp(now) {
|
|
38040
|
+
return {
|
|
38041
|
+
time: now.getUTCHours() << 11 | now.getUTCMinutes() << 5 | now.getUTCSeconds() >> 1,
|
|
38042
|
+
date: now.getUTCFullYear() - 1980 << 9 | now.getUTCMonth() + 1 << 5 | now.getUTCDate()
|
|
38043
|
+
};
|
|
38044
|
+
}
|
|
38045
|
+
function localHeader(r, stamp) {
|
|
38046
|
+
const head = new DataView(new ArrayBuffer(30));
|
|
38047
|
+
head.setUint32(0, LOCAL_SIG, true);
|
|
38048
|
+
head.setUint16(4, ZIP_VERSION, true);
|
|
38049
|
+
head.setUint16(6, 0, true);
|
|
38050
|
+
head.setUint16(8, DEFLATE, true);
|
|
38051
|
+
head.setUint16(10, stamp.time, true);
|
|
38052
|
+
head.setUint16(12, stamp.date, true);
|
|
38053
|
+
head.setUint32(14, r.crc, true);
|
|
38054
|
+
head.setUint32(18, r.compressedSize, true);
|
|
38055
|
+
head.setUint32(22, r.size, true);
|
|
38056
|
+
head.setUint16(26, r.name.length, true);
|
|
38057
|
+
head.setUint16(28, 0, true);
|
|
38058
|
+
return new Uint8Array(head.buffer);
|
|
38059
|
+
}
|
|
38060
|
+
function centralHeader(r, stamp) {
|
|
38061
|
+
const head = new DataView(new ArrayBuffer(46));
|
|
38062
|
+
head.setUint32(0, CENTRAL_SIG, true);
|
|
38063
|
+
head.setUint16(4, UNIX_HOST << 8 | ZIP_VERSION, true);
|
|
38064
|
+
head.setUint16(6, ZIP_VERSION, true);
|
|
38065
|
+
head.setUint16(8, 0, true);
|
|
38066
|
+
head.setUint16(10, DEFLATE, true);
|
|
38067
|
+
head.setUint16(12, stamp.time, true);
|
|
38068
|
+
head.setUint16(14, stamp.date, true);
|
|
38069
|
+
head.setUint32(16, r.crc, true);
|
|
38070
|
+
head.setUint32(20, r.compressedSize, true);
|
|
38071
|
+
head.setUint32(24, r.size, true);
|
|
38072
|
+
head.setUint16(28, r.name.length, true);
|
|
38073
|
+
head.setUint16(30, 0, true);
|
|
38074
|
+
head.setUint16(32, 0, true);
|
|
38075
|
+
head.setUint16(34, 0, true);
|
|
38076
|
+
head.setUint16(36, 0, true);
|
|
38077
|
+
head.setUint32(38, ENTRY_MODE << 16, true);
|
|
38078
|
+
head.setUint32(42, r.offset, true);
|
|
38079
|
+
return new Uint8Array(head.buffer);
|
|
38080
|
+
}
|
|
38081
|
+
function eocd(count, size, offset) {
|
|
38082
|
+
const head = new DataView(new ArrayBuffer(22));
|
|
38083
|
+
head.setUint32(0, EOCD_SIG, true);
|
|
38084
|
+
head.setUint16(4, 0, true);
|
|
38085
|
+
head.setUint16(6, 0, true);
|
|
38086
|
+
head.setUint16(8, count, true);
|
|
38087
|
+
head.setUint16(10, count, true);
|
|
38088
|
+
head.setUint32(12, size, true);
|
|
38089
|
+
head.setUint32(16, offset, true);
|
|
38090
|
+
head.setUint16(20, 0, true);
|
|
38091
|
+
return new Uint8Array(head.buffer);
|
|
38092
|
+
}
|
|
38093
|
+
function writeZip(path, entries, now) {
|
|
38094
|
+
const stamp = dosStamp(now);
|
|
38095
|
+
const encoder = new TextEncoder;
|
|
38096
|
+
const fd = openSync3(path, "wx", 384);
|
|
38097
|
+
try {
|
|
38098
|
+
const records = [];
|
|
38099
|
+
let offset = 0;
|
|
38100
|
+
const put = (bytes) => {
|
|
38101
|
+
const written = writeSync(fd, bytes);
|
|
38102
|
+
if (written !== bytes.length) {
|
|
38103
|
+
throw new Error(`zip write truncated: ${written} of ${bytes.length} bytes`);
|
|
38104
|
+
}
|
|
38105
|
+
offset += bytes.length;
|
|
38106
|
+
};
|
|
38107
|
+
for (const entry of entries) {
|
|
38108
|
+
const body = Bun.deflateSync(entry.data, { windowBits: -15 });
|
|
38109
|
+
const record = {
|
|
38110
|
+
name: encoder.encode(entry.name),
|
|
38111
|
+
compressedSize: body.length,
|
|
38112
|
+
crc: crc32(entry.data),
|
|
38113
|
+
size: entry.data.length,
|
|
38114
|
+
offset
|
|
38115
|
+
};
|
|
38116
|
+
records.push(record);
|
|
38117
|
+
put(localHeader(record, stamp));
|
|
38118
|
+
put(record.name);
|
|
38119
|
+
put(body);
|
|
38120
|
+
}
|
|
38121
|
+
const directoryStart = offset;
|
|
38122
|
+
for (const record of records) {
|
|
38123
|
+
put(centralHeader(record, stamp));
|
|
38124
|
+
put(record.name);
|
|
38125
|
+
}
|
|
38126
|
+
put(eocd(records.length, offset - directoryStart, directoryStart));
|
|
38127
|
+
} catch (e) {
|
|
38128
|
+
rmSync2(path, { force: true });
|
|
38129
|
+
throw e;
|
|
38130
|
+
} finally {
|
|
38131
|
+
closeSync2(fd);
|
|
38132
|
+
}
|
|
37119
38133
|
}
|
|
37120
38134
|
|
|
37121
|
-
// src/commands/
|
|
38135
|
+
// src/commands/doctor.ts
|
|
37122
38136
|
init_build_id();
|
|
37123
|
-
|
|
37124
|
-
function prodDiscoveryDeps(s) {
|
|
38137
|
+
function prodDoctorDeps(s) {
|
|
37125
38138
|
return {
|
|
37126
38139
|
now: () => new Date,
|
|
37127
38140
|
version: VERSION,
|
|
@@ -37133,7 +38146,7 @@ function prodDiscoveryDeps(s) {
|
|
|
37133
38146
|
}),
|
|
37134
38147
|
settings: () => s,
|
|
37135
38148
|
configPath: configFile(),
|
|
37136
|
-
configExists: () =>
|
|
38149
|
+
configExists: () => existsSync11(configFile()),
|
|
37137
38150
|
effective: () => ({
|
|
37138
38151
|
port: getPort(s),
|
|
37139
38152
|
idleMs: idleMs(s),
|
|
@@ -37142,12 +38155,18 @@ function prodDiscoveryDeps(s) {
|
|
|
37142
38155
|
}),
|
|
37143
38156
|
baseUrl: `http://localhost:${getPort(s)}`,
|
|
37144
38157
|
health: httpHealth,
|
|
38158
|
+
serviceInstalled: () => existsSync11(launcherServiceFile()),
|
|
37145
38159
|
readLock: readDaemonLock,
|
|
38160
|
+
readBootMarker,
|
|
37146
38161
|
isPidAlive,
|
|
37147
38162
|
listProcesses,
|
|
37148
38163
|
listReviewFiles,
|
|
37149
|
-
readAgentInstallState: () =>
|
|
38164
|
+
readAgentInstallState: () => {
|
|
38165
|
+
const adapter = selectAdapter();
|
|
38166
|
+
return { agent: adapter.id, ...adapter.readInstallState() };
|
|
38167
|
+
},
|
|
37150
38168
|
logStats,
|
|
38169
|
+
logErrorRecords,
|
|
37151
38170
|
logPaths: {
|
|
37152
38171
|
caret: logFile(),
|
|
37153
38172
|
daemon: daemonLogFile(),
|
|
@@ -37155,19 +38174,66 @@ function prodDiscoveryDeps(s) {
|
|
|
37155
38174
|
}
|
|
37156
38175
|
};
|
|
37157
38176
|
}
|
|
37158
|
-
async function
|
|
38177
|
+
async function readAdapterChecks() {
|
|
37159
38178
|
try {
|
|
38179
|
+
const config = resolveConfigFile(opencodeConfigDir());
|
|
38180
|
+
if (!hasCaretPluginEntry(config))
|
|
38181
|
+
return [];
|
|
38182
|
+
return [upgradeCheck(await readUpgradeVerdict({ configFile: config }))];
|
|
38183
|
+
} catch (e) {
|
|
38184
|
+
return [upgradeCheck({ kind: "unknown", reason: errorMessage(e) })];
|
|
38185
|
+
}
|
|
38186
|
+
}
|
|
38187
|
+
function prodBundleDeps() {
|
|
38188
|
+
return {
|
|
38189
|
+
stateDir: stateDir(),
|
|
38190
|
+
logPaths: [logFile(), daemonLogFile(), daemonStderrLogFile()],
|
|
38191
|
+
reviewsDir: reviewsDir(),
|
|
38192
|
+
now: () => new Date,
|
|
38193
|
+
isInteractive: isTerminal,
|
|
38194
|
+
confirm: confirmBundle,
|
|
38195
|
+
write: (path, entries, now) => {
|
|
38196
|
+
ensureStateDir();
|
|
38197
|
+
writeZip(path, entries, now);
|
|
38198
|
+
}
|
|
38199
|
+
};
|
|
38200
|
+
}
|
|
38201
|
+
async function confirmBundle() {
|
|
38202
|
+
await Promise.resolve().then(() => init_dist5());
|
|
38203
|
+
const answer = await confirm({
|
|
38204
|
+
message: "The bundle holds your logs and plan bodies, unredacted. Write it?"
|
|
38205
|
+
});
|
|
38206
|
+
return isCancel(answer) ? null : answer === true;
|
|
38207
|
+
}
|
|
38208
|
+
async function runDoctorSubcommand(opts) {
|
|
38209
|
+
try {
|
|
38210
|
+
const bundled = opts.bundle ? await runBundle({ yes: opts.yes }, prodBundleDeps()) : undefined;
|
|
38211
|
+
if (bundled?.kind === "refused") {
|
|
38212
|
+
process.stderr.write(`caret doctor: ${bundled.message}
|
|
38213
|
+
`);
|
|
38214
|
+
process.exit(2);
|
|
38215
|
+
}
|
|
38216
|
+
if (bundled?.kind === "written") {
|
|
38217
|
+
const note = opts.json ? process.stderr : process.stdout;
|
|
38218
|
+
note.write(`caret doctor: wrote ${bundled.path}
|
|
38219
|
+
`);
|
|
38220
|
+
note.write(`caret doctor: it is unredacted \u2014 move it over a channel you trust
|
|
38221
|
+
`);
|
|
38222
|
+
}
|
|
37160
38223
|
const s = loadSettings();
|
|
37161
|
-
const report = await
|
|
37162
|
-
|
|
37163
|
-
|
|
37164
|
-
|
|
38224
|
+
const [report, adapterChecks] = await Promise.all([
|
|
38225
|
+
collectReport(prodDoctorDeps(s)),
|
|
38226
|
+
readAdapterChecks()
|
|
38227
|
+
]);
|
|
38228
|
+
const doc = { ...report, checks: runChecks(report, adapterChecks) };
|
|
38229
|
+
const color = process.stdout.isTTY === true && !process.env.NO_COLOR && process.env.CI !== "true";
|
|
38230
|
+
process.stdout.write(`${renderStdout(doc, opts.json ? "json" : "text", color)}
|
|
37165
38231
|
`);
|
|
37166
|
-
process.exit(0);
|
|
38232
|
+
process.exit(doc.checks.some((c) => c.status === "fail") ? 1 : 0);
|
|
37167
38233
|
} catch (e) {
|
|
37168
|
-
process.stderr.write(`caret
|
|
38234
|
+
process.stderr.write(`caret doctor: ${errorMessage(e)}
|
|
37169
38235
|
`);
|
|
37170
|
-
process.exit(
|
|
38236
|
+
process.exit(2);
|
|
37171
38237
|
}
|
|
37172
38238
|
}
|
|
37173
38239
|
|
|
@@ -37175,38 +38241,38 @@ async function runDiscoverySubcommand(opts) {
|
|
|
37175
38241
|
import {
|
|
37176
38242
|
accessSync as accessSync2,
|
|
37177
38243
|
constants as constants2,
|
|
37178
|
-
existsSync as
|
|
38244
|
+
existsSync as existsSync13,
|
|
37179
38245
|
mkdirSync as mkdirSync3,
|
|
37180
|
-
rmSync as
|
|
38246
|
+
rmSync as rmSync3,
|
|
37181
38247
|
statSync as statSync5,
|
|
37182
38248
|
symlinkSync,
|
|
37183
|
-
writeFileSync as
|
|
38249
|
+
writeFileSync as writeFileSync8
|
|
37184
38250
|
} from "fs";
|
|
37185
|
-
import { join as
|
|
38251
|
+
import { join as join19, resolve as resolve2 } from "path";
|
|
37186
38252
|
|
|
37187
38253
|
// src/adapters/opencode/packaging.ts
|
|
37188
|
-
import { existsSync as
|
|
37189
|
-
import { dirname as
|
|
38254
|
+
import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync12 } from "fs";
|
|
38255
|
+
import { dirname as dirname9, join as join18 } from "path";
|
|
37190
38256
|
function resolveCaretRoot() {
|
|
37191
38257
|
const candidates = [];
|
|
37192
38258
|
const script = process.argv[1];
|
|
37193
38259
|
if (script)
|
|
37194
|
-
candidates.push(
|
|
37195
|
-
candidates.push(
|
|
38260
|
+
candidates.push(join18(dirname9(script), ".."));
|
|
38261
|
+
candidates.push(join18(dirname9(process.execPath), ".."));
|
|
37196
38262
|
for (const root of candidates) {
|
|
37197
|
-
if (
|
|
38263
|
+
if (existsSync12(join18(root, "opencode", "caret.plugin.ts")))
|
|
37198
38264
|
return root;
|
|
37199
38265
|
}
|
|
37200
38266
|
throw new Error(`caret OpenCode packaging not found (no opencode/caret.plugin.ts under: ${candidates.join(", ") || "(none)"}). The OpenCode plugin source must ship with caret.`);
|
|
37201
38267
|
}
|
|
37202
38268
|
var DEMO_TEMPLATE = "templates/demo.md";
|
|
37203
38269
|
function loadOpencodePackaging(root = resolveCaretRoot()) {
|
|
37204
|
-
const binPath =
|
|
37205
|
-
const demoTemplate =
|
|
37206
|
-
const commandsDir =
|
|
38270
|
+
const binPath = join18(root, "bin", "caret");
|
|
38271
|
+
const demoTemplate = readFileSync12(join18(root, DEMO_TEMPLATE), "utf-8");
|
|
38272
|
+
const commandsDir = join18(root, "opencode", "commands");
|
|
37207
38273
|
let commands = [];
|
|
37208
38274
|
try {
|
|
37209
|
-
commands =
|
|
38275
|
+
commands = readdirSync7(commandsDir).filter((f) => f.endsWith(".md")).sort().map((f) => ({ name: f, contents: readFileSync12(join18(commandsDir, f), "utf-8") }));
|
|
37210
38276
|
} catch {
|
|
37211
38277
|
commands = [];
|
|
37212
38278
|
}
|
|
@@ -37216,17 +38282,17 @@ function loadOpencodePackaging(root = resolveCaretRoot()) {
|
|
|
37216
38282
|
// src/commands/install/local.ts
|
|
37217
38283
|
init_paths3();
|
|
37218
38284
|
function devMarketplaceDir() {
|
|
37219
|
-
return
|
|
38285
|
+
return join19(stateDir(), "dev-marketplace");
|
|
37220
38286
|
}
|
|
37221
38287
|
function resolveLocalCheckout(opts = {}, deps = {}) {
|
|
37222
38288
|
const repoDir = resolve2(caretRoot(deps));
|
|
37223
|
-
if (!
|
|
38289
|
+
if (!existsSync13(join19(repoDir, ".claude-plugin", "marketplace.json"))) {
|
|
37224
38290
|
throw new Error(`--from-local must run from a caret checkout (no .claude-plugin/marketplace.json at ${repoDir}). A published caret installs with a plain \`caret install\`.`);
|
|
37225
38291
|
}
|
|
37226
38292
|
if (opts.requireArtifacts !== false) {
|
|
37227
38293
|
const missing = [
|
|
37228
|
-
isExecutable(
|
|
37229
|
-
isDirectory(
|
|
38294
|
+
isExecutable(join19(repoDir, "bin", "caret-native")) ? null : "bin/caret-native",
|
|
38295
|
+
isDirectory(join19(repoDir, "bin", "ui")) ? null : "bin/ui"
|
|
37230
38296
|
].filter((m) => m !== null);
|
|
37231
38297
|
if (missing.length > 0) {
|
|
37232
38298
|
throw new Error(`--from-local reuses the build artifacts bin/caret-native + bin/ui, and ${repoDir} is missing ${missing.join(" + ")} \u2014 run \`mise run build\` first.`);
|
|
@@ -37244,10 +38310,10 @@ function caretRoot(deps) {
|
|
|
37244
38310
|
}
|
|
37245
38311
|
}
|
|
37246
38312
|
function writeDevMarketplace(repoDir, outDir) {
|
|
37247
|
-
|
|
37248
|
-
mkdirSync3(
|
|
37249
|
-
symlinkSync(resolve2(repoDir),
|
|
37250
|
-
|
|
38313
|
+
rmSync3(outDir, { recursive: true, force: true });
|
|
38314
|
+
mkdirSync3(join19(outDir, ".claude-plugin"), { recursive: true });
|
|
38315
|
+
symlinkSync(resolve2(repoDir), join19(outDir, "caret"));
|
|
38316
|
+
writeFileSync8(join19(outDir, ".claude-plugin", "marketplace.json"), `${JSON.stringify({
|
|
37251
38317
|
name: "caret",
|
|
37252
38318
|
description: "Local caret dev build (generated by caret install --from-local).",
|
|
37253
38319
|
owner: { name: "macintacos" },
|
|
@@ -37258,7 +38324,7 @@ function writeDevMarketplace(repoDir, outDir) {
|
|
|
37258
38324
|
async function prewarmLocalBuild(repoDir, deps = {}) {
|
|
37259
38325
|
const spawn = deps.spawn ?? runCaptured;
|
|
37260
38326
|
try {
|
|
37261
|
-
await spawn([
|
|
38327
|
+
await spawn([join19(repoDir, "bin", "caret-native"), "prewarm"]);
|
|
37262
38328
|
} catch {}
|
|
37263
38329
|
}
|
|
37264
38330
|
function isExecutable(path) {
|
|
@@ -37294,75 +38360,6 @@ async function runCaptured(cmd) {
|
|
|
37294
38360
|
return await proc.exited;
|
|
37295
38361
|
}
|
|
37296
38362
|
|
|
37297
|
-
// src/commands/install/ui.ts
|
|
37298
|
-
function isTerminal() {
|
|
37299
|
-
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
37300
|
-
}
|
|
37301
|
-
var silentUI = {
|
|
37302
|
-
intro: () => {},
|
|
37303
|
-
step: async (_label, work) => work(() => {}),
|
|
37304
|
-
info: () => {},
|
|
37305
|
-
warn: () => {},
|
|
37306
|
-
error: () => {},
|
|
37307
|
-
note: () => {},
|
|
37308
|
-
cancel: () => {},
|
|
37309
|
-
outro: () => {}
|
|
37310
|
-
};
|
|
37311
|
-
function plainUI(write) {
|
|
37312
|
-
const line = (s) => write(`caret: ${s}
|
|
37313
|
-
`);
|
|
37314
|
-
return {
|
|
37315
|
-
intro: (action) => line(action),
|
|
37316
|
-
async step(label, work, done) {
|
|
37317
|
-
try {
|
|
37318
|
-
const value = await work(() => {});
|
|
37319
|
-
line(done ? done(value) : label);
|
|
37320
|
-
return value;
|
|
37321
|
-
} catch (e) {
|
|
37322
|
-
line(`${label} \u2014 failed`);
|
|
37323
|
-
throw e;
|
|
37324
|
-
}
|
|
37325
|
-
},
|
|
37326
|
-
info: line,
|
|
37327
|
-
warn: line,
|
|
37328
|
-
error: line,
|
|
37329
|
-
note: (body, title) => line(`${title}
|
|
37330
|
-
${body.split(`
|
|
37331
|
-
`).map((l) => ` ${l}`).join(`
|
|
37332
|
-
`)}`),
|
|
37333
|
-
cancel: line,
|
|
37334
|
-
outro: line
|
|
37335
|
-
};
|
|
37336
|
-
}
|
|
37337
|
-
async function createInstallUI(opts = {}) {
|
|
37338
|
-
const write = opts.write ?? ((s) => void process.stdout.write(s));
|
|
37339
|
-
const interactive = opts.interactive ?? (process.stdout.isTTY === true && !process.env.NO_COLOR && process.env.CI !== "true");
|
|
37340
|
-
if (!interactive)
|
|
37341
|
-
return plainUI(write);
|
|
37342
|
-
const clack = await Promise.resolve().then(() => (init_dist5(), {}));
|
|
37343
|
-
return {
|
|
37344
|
-
intro: (action) => intro(`caret \xB7 ${action}`),
|
|
37345
|
-
async step(label, work, done) {
|
|
37346
|
-
const spin = spinner();
|
|
37347
|
-
spin.start(label);
|
|
37348
|
-
try {
|
|
37349
|
-
const value = await work((m) => spin.message(m));
|
|
37350
|
-
spin.stop(done ? done(value) : label);
|
|
37351
|
-
return value;
|
|
37352
|
-
} catch (e) {
|
|
37353
|
-
spin.error(label);
|
|
37354
|
-
throw e;
|
|
37355
|
-
}
|
|
37356
|
-
},
|
|
37357
|
-
info: (m) => log.info(m),
|
|
37358
|
-
warn: (m) => log.warn(m),
|
|
37359
|
-
error: (m) => log.error(m),
|
|
37360
|
-
note: (body, title) => note(body, title),
|
|
37361
|
-
cancel: (m) => cancel(m),
|
|
37362
|
-
outro: (m) => outro(m)
|
|
37363
|
-
};
|
|
37364
|
-
}
|
|
37365
|
-
|
|
37366
38363
|
// src/commands/install/claude.ts
|
|
37367
38364
|
var MARKETPLACE_SOURCE = "macintacos/caret";
|
|
37368
38365
|
var MARKETPLACE_NAME = "caret";
|
|
@@ -37537,11 +38534,11 @@ async function runInstallClaudeTarget(opts, deps = {}) {
|
|
|
37537
38534
|
|
|
37538
38535
|
// src/commands/install/opencode.ts
|
|
37539
38536
|
init_config_plugin();
|
|
37540
|
-
import { existsSync as existsSync16,
|
|
37541
|
-
import { basename as
|
|
38537
|
+
import { existsSync as existsSync16, writeFileSync as writeFileSync10 } from "fs";
|
|
38538
|
+
import { basename as basename5, join as join20 } from "path";
|
|
37542
38539
|
|
|
37543
38540
|
// src/adapters/opencode/deploy.ts
|
|
37544
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync4, rmSync as
|
|
38541
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync4, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
37545
38542
|
import { dirname as dirname10 } from "path";
|
|
37546
38543
|
function renderPlugin(source, opts) {
|
|
37547
38544
|
return source.replaceAll("__CARET_VERSION__", () => opts.version).replaceAll("__CARET_BIN__", () => opts.binPath).replaceAll("__CARET_DEMO_TEMPLATE__", () => opts.demoTemplate);
|
|
@@ -37551,7 +38548,7 @@ function deployFiles(files, opts) {
|
|
|
37551
38548
|
for (const f of files) {
|
|
37552
38549
|
if (!opts.dryRun) {
|
|
37553
38550
|
mkdirSync4(dirname10(f.path), { recursive: true });
|
|
37554
|
-
|
|
38551
|
+
writeFileSync9(f.path, f.contents);
|
|
37555
38552
|
}
|
|
37556
38553
|
paths.push(f.path);
|
|
37557
38554
|
}
|
|
@@ -37563,7 +38560,7 @@ function removeFiles(targets, opts) {
|
|
|
37563
38560
|
if (!existsSync14(p))
|
|
37564
38561
|
continue;
|
|
37565
38562
|
if (!opts.dryRun)
|
|
37566
|
-
|
|
38563
|
+
rmSync4(p, { force: true });
|
|
37567
38564
|
paths.push(p);
|
|
37568
38565
|
}
|
|
37569
38566
|
return { paths, dryRun: opts.dryRun };
|
|
@@ -37573,6 +38570,9 @@ function removeFiles(targets, opts) {
|
|
|
37573
38570
|
init_paths2();
|
|
37574
38571
|
init_upgrade();
|
|
37575
38572
|
|
|
38573
|
+
// src/commands/install/prompt.ts
|
|
38574
|
+
init_upgrade();
|
|
38575
|
+
|
|
37576
38576
|
// src/commands/install/targets.ts
|
|
37577
38577
|
init_paths2();
|
|
37578
38578
|
import { existsSync as existsSync15 } from "fs";
|
|
@@ -37631,20 +38631,6 @@ async function promptForServiceChoice() {
|
|
|
37631
38631
|
});
|
|
37632
38632
|
return isCancel(choice) ? null : choice;
|
|
37633
38633
|
}
|
|
37634
|
-
function upgradeVerdictLine(verdict) {
|
|
37635
|
-
switch (verdict.kind) {
|
|
37636
|
-
case "fresh":
|
|
37637
|
-
return "OpenCode will resolve caret on its next start";
|
|
37638
|
-
case "current":
|
|
37639
|
-
return `OpenCode's caret is ${verdict.version} \u2014 already current`;
|
|
37640
|
-
case "stale-cache":
|
|
37641
|
-
return `OpenCode's cached caret is ${verdict.cached}; ${verdict.published} is published`;
|
|
37642
|
-
case "stale-pin":
|
|
37643
|
-
return `Your config pins ${verdict.entry}; ${verdict.published} is published`;
|
|
37644
|
-
case "unknown":
|
|
37645
|
-
return "Could not check which caret OpenCode would load";
|
|
37646
|
-
}
|
|
37647
|
-
}
|
|
37648
38634
|
function upgradePromptMessage(verdict) {
|
|
37649
38635
|
const remedy = verdict.kind === "stale-pin" ? "Bump the pin?" : "Clear the cached copy so OpenCode re-resolves?";
|
|
37650
38636
|
return `${upgradeVerdictLine(verdict)}. ${remedy}`;
|
|
@@ -37658,7 +38644,7 @@ async function promptUpgrade(verdict, clack) {
|
|
|
37658
38644
|
// src/commands/install/opencode.ts
|
|
37659
38645
|
init_build_id();
|
|
37660
38646
|
function isCaretCheckout(dir) {
|
|
37661
|
-
return existsSync16(
|
|
38647
|
+
return existsSync16(join20(dir, "opencode", "caret.plugin.ts"));
|
|
37662
38648
|
}
|
|
37663
38649
|
function caretEntries(text, isCheckout) {
|
|
37664
38650
|
return pluginEntries(text).filter((entry) => {
|
|
@@ -37684,12 +38670,12 @@ async function runInstallOpencodeTarget(opts, deps = {}) {
|
|
|
37684
38670
|
const ui = deps.ui ?? silentUI;
|
|
37685
38671
|
const isCheckout = deps.isCheckout ?? isCaretCheckout;
|
|
37686
38672
|
const configFile = resolveConfigFile(dir);
|
|
37687
|
-
const commandPaths = pkg.commands.map((c) =>
|
|
38673
|
+
const commandPaths = pkg.commands.map((c) => join20(commandDir(dir), namespacedCommandFilename(c.name)));
|
|
37688
38674
|
const legacy = existingLegacyInstallFiles(dir);
|
|
37689
38675
|
const specifier = opts.local ? localPluginSpecifier(opts.local.repoDir) : CARET_PACKAGE;
|
|
37690
38676
|
if (opts.dryRun) {
|
|
37691
38677
|
const verb = opts.uninstall ? "remove" : "write";
|
|
37692
|
-
const found = checks3(opts) ? ["", previewLine(await
|
|
38678
|
+
const found = checks3(opts) ? ["", previewLine(await readVerdict(configFile, deps))] : [];
|
|
37693
38679
|
const entry = opts.uninstall ? [] : ["", `plugin entry: ${specifier}`];
|
|
37694
38680
|
const sweep = legacy.length === 0 ? [] : ["", "pre-array-install files to remove:", ...legacy];
|
|
37695
38681
|
ui.note([configFile, ...commandPaths, ...entry, ...sweep, ...found].join(`
|
|
@@ -37697,17 +38683,17 @@ async function runInstallOpencodeTarget(opts, deps = {}) {
|
|
|
37697
38683
|
return;
|
|
37698
38684
|
}
|
|
37699
38685
|
if (opts.uninstall) {
|
|
37700
|
-
await ui.step("Removing caret from OpenCode's plugin array", async () => editConfig(configFile, (text) => text === null ? null : caretEntries(text, isCheckout).reduce((acc, entry) => removePluginFromConfigText(acc, entry), text)), (changed) => changed.length > 0 ? `Removed caret from ${
|
|
38686
|
+
await ui.step("Removing caret from OpenCode's plugin array", async () => editConfig(configFile, (text) => text === null ? null : caretEntries(text, isCheckout).reduce((acc, entry) => removePluginFromConfigText(acc, entry), text)), (changed) => changed.length > 0 ? `Removed caret from ${basename5(configFile)}` : `caret was not in ${basename5(configFile)}`);
|
|
37701
38687
|
await ui.step("Removing the /caret:* command files", async () => removeFiles(commandPaths, { dryRun: false }), (removed) => `Removed ${removed.paths.length} command file(s) from ${dir}`);
|
|
37702
38688
|
await sweepLegacy(legacy, dir, ui);
|
|
37703
38689
|
return;
|
|
37704
38690
|
}
|
|
37705
|
-
await ui.step(`Adding ${specifier} to OpenCode's plugin array`, async () => editConfig(configFile, (text) => setCaretPluginEntry(text, specifier, isCheckout)), (changed) => changed.length > 0 ? `Added ${specifier} to ${
|
|
38691
|
+
await ui.step(`Adding ${specifier} to OpenCode's plugin array`, async () => editConfig(configFile, (text) => setCaretPluginEntry(text, specifier, isCheckout)), (changed) => changed.length > 0 ? `Added ${specifier} to ${basename5(configFile)}` : `${specifier} was already in ${basename5(configFile)}`);
|
|
37706
38692
|
if (checks3(opts))
|
|
37707
38693
|
await upgradeStep(configFile, opts, deps, ui);
|
|
37708
38694
|
await sweepLegacy(legacy, dir, ui);
|
|
37709
38695
|
const files = pkg.commands.map((c) => ({
|
|
37710
|
-
path:
|
|
38696
|
+
path: join20(commandDir(dir), namespacedCommandFilename(c.name)),
|
|
37711
38697
|
contents: renderPlugin(c.contents, {
|
|
37712
38698
|
version: VERSION,
|
|
37713
38699
|
binPath: pkg.binPath,
|
|
@@ -37728,15 +38714,11 @@ function previewLine(verdict) {
|
|
|
37728
38714
|
const line = upgradeVerdictLine(verdict);
|
|
37729
38715
|
return verdict.kind === "unknown" ? `${line} (${verdict.reason})` : line;
|
|
37730
38716
|
}
|
|
37731
|
-
async function
|
|
37732
|
-
return
|
|
37733
|
-
entry: findPluginEntry(readConfigText(configFile), CARET_PACKAGE),
|
|
37734
|
-
cached: readCachedCaretVersion((deps.cacheDirs ?? existingOpencodeCachePackageDirs)()),
|
|
37735
|
-
published: await (deps.published ?? publishedCaretVersion)()
|
|
37736
|
-
});
|
|
38717
|
+
async function readVerdict(configFile, deps) {
|
|
38718
|
+
return readUpgradeVerdict({ configFile, cacheDirs: deps.cacheDirs, published: deps.published });
|
|
37737
38719
|
}
|
|
37738
38720
|
async function upgradeStep(configFile, opts, deps, ui) {
|
|
37739
|
-
const verdict = await ui.step("Checking OpenCode's caret version", () =>
|
|
38721
|
+
const verdict = await ui.step("Checking OpenCode's caret version", () => readVerdict(configFile, deps), upgradeVerdictLine);
|
|
37740
38722
|
if (verdict.kind === "unknown") {
|
|
37741
38723
|
ui.warn(`Could not check OpenCode's caret version (${verdict.reason}) \u2014 nothing changed.`);
|
|
37742
38724
|
return;
|
|
@@ -37757,36 +38739,33 @@ async function upgradeStep(configFile, opts, deps, ui) {
|
|
|
37757
38739
|
await ui.step("Clearing OpenCode's cached caret", async () => (deps.clearCache ?? clearCachedCaret)(dirs), (cleared) => `Cleared ${cleared.length} cached ${cleared.length === 1 ? "copy" : "copies"} \u2014 OpenCode re-resolves on next start`);
|
|
37758
38740
|
return;
|
|
37759
38741
|
}
|
|
37760
|
-
await ui.step(`Bumping ${CARET_PACKAGE} to ${verdict.published}`, async () => editConfig(configFile, (text) => text === null ? null : setPluginVersionInConfigText(text, CARET_PACKAGE, verdict.published)), (changed) => changed.length > 0 ? `Bumped the pin to ${CARET_PACKAGE}@${verdict.published}` : `Left ${
|
|
37761
|
-
}
|
|
37762
|
-
function readConfigText(path) {
|
|
37763
|
-
return existsSync16(path) ? readFileSync11(path, "utf-8") : null;
|
|
38742
|
+
await ui.step(`Bumping ${CARET_PACKAGE} to ${verdict.published}`, async () => editConfig(configFile, (text) => text === null ? null : setPluginVersionInConfigText(text, CARET_PACKAGE, verdict.published)), (changed) => changed.length > 0 ? `Bumped the pin to ${CARET_PACKAGE}@${verdict.published}` : `Left ${basename5(configFile)} unchanged`);
|
|
37764
38743
|
}
|
|
37765
38744
|
function editConfig(path, transform) {
|
|
37766
38745
|
const existing = readConfigText(path);
|
|
37767
38746
|
const next = transform(existing);
|
|
37768
38747
|
if (next === null || next === existing)
|
|
37769
38748
|
return [];
|
|
37770
|
-
|
|
38749
|
+
writeFileSync10(path, next);
|
|
37771
38750
|
return [path];
|
|
37772
38751
|
}
|
|
37773
38752
|
|
|
37774
38753
|
// src/commands/install/launcher.ts
|
|
37775
|
-
import { chmodSync as chmodSync4, copyFileSync, existsSync as existsSync17, renameSync as
|
|
37776
|
-
import { dirname as dirname11, join as
|
|
38754
|
+
import { chmodSync as chmodSync4, copyFileSync, existsSync as existsSync17, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync11 } from "fs";
|
|
38755
|
+
import { dirname as dirname11, join as join21 } from "path";
|
|
37777
38756
|
init_paths3();
|
|
37778
38757
|
init_build_id();
|
|
37779
38758
|
function installLauncher(deps = {}) {
|
|
37780
|
-
const source = (deps.source ?? (() =>
|
|
38759
|
+
const source = (deps.source ?? (() => join21(resolveCaretRoot(), "bin", "caret-launcher")))();
|
|
37781
38760
|
ensureStateDir(dirname11(launcherPath()));
|
|
37782
38761
|
const tmp = `${launcherPath()}.${process.pid}.tmp`;
|
|
37783
38762
|
copyFileSync(source, tmp);
|
|
37784
38763
|
chmodSync4(tmp, 493);
|
|
37785
|
-
|
|
38764
|
+
renameSync5(tmp, launcherPath());
|
|
37786
38765
|
const bunPath = deps.bunPath ?? (buildKind() === "binary" ? undefined : process.execPath);
|
|
37787
38766
|
const unpinned = deps.pinnedRoot === undefined && existsSync17(launcherPinnedRootFile());
|
|
37788
38767
|
if (unpinned)
|
|
37789
|
-
|
|
38768
|
+
rmSync5(launcherPinnedRootFile(), { force: true });
|
|
37790
38769
|
if (bunPath === undefined && deps.serviceLabel === undefined && deps.pinnedRoot === undefined) {
|
|
37791
38770
|
return { unpinned };
|
|
37792
38771
|
}
|
|
@@ -37800,12 +38779,12 @@ function installLauncher(deps = {}) {
|
|
|
37800
38779
|
return { unpinned };
|
|
37801
38780
|
}
|
|
37802
38781
|
function writeRecord(path, value) {
|
|
37803
|
-
|
|
38782
|
+
writeFileSync11(path, `${value}
|
|
37804
38783
|
`, { mode: 384 });
|
|
37805
38784
|
}
|
|
37806
38785
|
function uninstallLauncher() {
|
|
37807
|
-
|
|
37808
|
-
|
|
38786
|
+
rmSync5(dirname11(launcherPath()), { recursive: true, force: true });
|
|
38787
|
+
rmSync5(launcherRecordDir(), { recursive: true, force: true });
|
|
37809
38788
|
}
|
|
37810
38789
|
|
|
37811
38790
|
// src/commands/install/service.ts
|
|
@@ -38051,9 +39030,9 @@ function servicePlatform(platform = process.platform) {
|
|
|
38051
39030
|
|
|
38052
39031
|
// src/service/launchd-manager.ts
|
|
38053
39032
|
init_log();
|
|
38054
|
-
import { mkdirSync as mkdirSync5, rmSync as
|
|
39033
|
+
import { mkdirSync as mkdirSync5, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "fs";
|
|
38055
39034
|
import { homedir as homedir6 } from "os";
|
|
38056
|
-
import { join as
|
|
39035
|
+
import { join as join22 } from "path";
|
|
38057
39036
|
|
|
38058
39037
|
// src/service/launchd.ts
|
|
38059
39038
|
var EXIT_TIMEOUT_SEC = 20;
|
|
@@ -38114,10 +39093,10 @@ function commandError(tool, verb, result) {
|
|
|
38114
39093
|
}
|
|
38115
39094
|
|
|
38116
39095
|
// src/service/unit-file.ts
|
|
38117
|
-
import { readFileSync as
|
|
39096
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
38118
39097
|
function unitUnchanged(path, text) {
|
|
38119
39098
|
try {
|
|
38120
|
-
return
|
|
39099
|
+
return readFileSync13(path, "utf-8") === text;
|
|
38121
39100
|
} catch {
|
|
38122
39101
|
return false;
|
|
38123
39102
|
}
|
|
@@ -38129,14 +39108,14 @@ var RUNNING = [/^\s*state\s*=\s*running\b/m, /^\s*pid\s*=\s*\d+/m];
|
|
|
38129
39108
|
var DISABLED2 = new RegExp(`"${LAUNCHD_LABEL.replace(/\./g, "\\.")}"\\s*=>\\s*(?:true|disabled)\\b`);
|
|
38130
39109
|
var launchctlError = (action, result) => commandError("launchctl", action, result);
|
|
38131
39110
|
function createLaunchdManager(deps = {}) {
|
|
38132
|
-
const launchAgentsDir = deps.launchAgentsDir ??
|
|
39111
|
+
const launchAgentsDir = deps.launchAgentsDir ?? join22(homedir6(), "Library", "LaunchAgents");
|
|
38133
39112
|
const launchctl = deps.launchctl ?? spawnLaunchctl;
|
|
38134
39113
|
const uid = deps.uid ?? process.getuid?.();
|
|
38135
39114
|
if (uid === undefined)
|
|
38136
39115
|
throw new Error("caret service: launchd needs a POSIX uid");
|
|
38137
39116
|
const domain = `gui/${uid}`;
|
|
38138
39117
|
const target = `${domain}/${LAUNCHD_LABEL}`;
|
|
38139
|
-
const plistPath =
|
|
39118
|
+
const plistPath = join22(launchAgentsDir, `${LAUNCHD_LABEL}.plist`);
|
|
38140
39119
|
async function readStatus() {
|
|
38141
39120
|
const [printed, printDisabled] = await Promise.all([
|
|
38142
39121
|
launchctl(["print", target]),
|
|
@@ -38165,7 +39144,7 @@ function createLaunchdManager(deps = {}) {
|
|
|
38165
39144
|
}
|
|
38166
39145
|
}
|
|
38167
39146
|
mkdirSync5(launchAgentsDir, { recursive: true });
|
|
38168
|
-
|
|
39147
|
+
writeFileSync12(plistPath, text);
|
|
38169
39148
|
await launchctl(["bootout", target]);
|
|
38170
39149
|
const bootstrapped = await launchctl(["bootstrap", domain, plistPath]);
|
|
38171
39150
|
if (bootstrapped.code !== 0)
|
|
@@ -38173,7 +39152,7 @@ function createLaunchdManager(deps = {}) {
|
|
|
38173
39152
|
},
|
|
38174
39153
|
async uninstall() {
|
|
38175
39154
|
await launchctl(["bootout", target]);
|
|
38176
|
-
|
|
39155
|
+
rmSync6(plistPath, { force: true });
|
|
38177
39156
|
},
|
|
38178
39157
|
status: readStatus,
|
|
38179
39158
|
async restart() {
|
|
@@ -38186,9 +39165,9 @@ function createLaunchdManager(deps = {}) {
|
|
|
38186
39165
|
|
|
38187
39166
|
// src/service/systemd-manager.ts
|
|
38188
39167
|
init_log();
|
|
38189
|
-
import { mkdirSync as mkdirSync6, rmSync as
|
|
39168
|
+
import { mkdirSync as mkdirSync6, rmSync as rmSync7, writeFileSync as writeFileSync13 } from "fs";
|
|
38190
39169
|
import { homedir as homedir7 } from "os";
|
|
38191
|
-
import { join as
|
|
39170
|
+
import { join as join23 } from "path";
|
|
38192
39171
|
|
|
38193
39172
|
// src/service/systemd.ts
|
|
38194
39173
|
var START_LIMIT_INTERVAL_SEC = 60;
|
|
@@ -38230,10 +39209,10 @@ WantedBy=default.target
|
|
|
38230
39209
|
|
|
38231
39210
|
// src/service/systemd-manager.ts
|
|
38232
39211
|
function createSystemdManager(deps = {}) {
|
|
38233
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
38234
|
-
const unitDir = deps.unitDir ??
|
|
39212
|
+
const configHome = process.env.XDG_CONFIG_HOME || join23(homedir7(), ".config");
|
|
39213
|
+
const unitDir = deps.unitDir ?? join23(configHome, "systemd", "user");
|
|
38235
39214
|
const run = deps.run ?? runCommand;
|
|
38236
|
-
const unitPath =
|
|
39215
|
+
const unitPath = join23(unitDir, SYSTEMD_UNIT);
|
|
38237
39216
|
const systemctl = (...args) => run(["systemctl", "--user", ...args]);
|
|
38238
39217
|
async function probeSystemd() {
|
|
38239
39218
|
const shown = await systemctl("show-environment");
|
|
@@ -38292,7 +39271,7 @@ function createSystemdManager(deps = {}) {
|
|
|
38292
39271
|
}
|
|
38293
39272
|
mkdirSync6(unitDir, { recursive: true });
|
|
38294
39273
|
try {
|
|
38295
|
-
|
|
39274
|
+
writeFileSync13(unitPath, text);
|
|
38296
39275
|
const reloaded = await systemctl("daemon-reload");
|
|
38297
39276
|
if (reloaded.code !== 0)
|
|
38298
39277
|
throw commandError("systemctl", "daemon-reload", reloaded);
|
|
@@ -38304,14 +39283,14 @@ function createSystemdManager(deps = {}) {
|
|
|
38304
39283
|
if (restarted.code !== 0)
|
|
38305
39284
|
throw commandError("systemctl", "restart", restarted);
|
|
38306
39285
|
} catch (err) {
|
|
38307
|
-
|
|
39286
|
+
rmSync7(unitPath, { force: true });
|
|
38308
39287
|
throw err;
|
|
38309
39288
|
}
|
|
38310
39289
|
await enableLinger();
|
|
38311
39290
|
},
|
|
38312
39291
|
async uninstall() {
|
|
38313
39292
|
await systemctl("disable", "--now", SYSTEMD_UNIT);
|
|
38314
|
-
|
|
39293
|
+
rmSync7(unitPath, { force: true });
|
|
38315
39294
|
await systemctl("daemon-reload");
|
|
38316
39295
|
},
|
|
38317
39296
|
status: readStatus,
|
|
@@ -38369,79 +39348,6 @@ init_boot();
|
|
|
38369
39348
|
init_settings();
|
|
38370
39349
|
init_log();
|
|
38371
39350
|
|
|
38372
|
-
// src/review/reconcile.ts
|
|
38373
|
-
init_log();
|
|
38374
|
-
async function runReconcile(stdin, deps) {
|
|
38375
|
-
const ctx = {};
|
|
38376
|
-
try {
|
|
38377
|
-
const input = deps.parseHookInput(stdin);
|
|
38378
|
-
ctx.sessionId = input.sessionId;
|
|
38379
|
-
if (!input.sessionId)
|
|
38380
|
-
return;
|
|
38381
|
-
const match = (await deps.listReviews()).find((r) => r.sessionId === input.sessionId);
|
|
38382
|
-
if (!match) {
|
|
38383
|
-
logDebug("reconcile", "no pending review for session; nothing to reconcile", { ...ctx });
|
|
38384
|
-
return;
|
|
38385
|
-
}
|
|
38386
|
-
ctx.reviewId = match.id;
|
|
38387
|
-
await deps.resolveReview(match.id);
|
|
38388
|
-
logInfo("reconcile", "terminal approval reconciled", { ...ctx });
|
|
38389
|
-
} catch (err) {
|
|
38390
|
-
logDebug("reconcile", "reconcile skipped", { ...ctx, err: String(err) });
|
|
38391
|
-
}
|
|
38392
|
-
}
|
|
38393
|
-
|
|
38394
|
-
// src/commands/reconcile.ts
|
|
38395
|
-
function prodReconcileDeps(baseUrl, adapter) {
|
|
38396
|
-
return {
|
|
38397
|
-
parseHookInput: (stdin) => adapter.parseHookInput(stdin),
|
|
38398
|
-
listReviews: () => listReviews(baseUrl),
|
|
38399
|
-
resolveReview: (id) => resolveReview(baseUrl, id, { behavior: "allow" })
|
|
38400
|
-
};
|
|
38401
|
-
}
|
|
38402
|
-
async function runReconcileSubcommand() {
|
|
38403
|
-
try {
|
|
38404
|
-
const loaded = loadSettings();
|
|
38405
|
-
bootHookLogging(loaded);
|
|
38406
|
-
const adapter = selectAdapter();
|
|
38407
|
-
const baseUrl = `http://localhost:${getPort(loaded)}`;
|
|
38408
|
-
const stdin = await Bun.stdin.text();
|
|
38409
|
-
await runReconcile(stdin, prodReconcileDeps(baseUrl, adapter));
|
|
38410
|
-
} catch (err) {
|
|
38411
|
-
logDebug("reconcile", "reconcile subcommand skipped", { err: String(err) });
|
|
38412
|
-
}
|
|
38413
|
-
process.exit(0);
|
|
38414
|
-
}
|
|
38415
|
-
|
|
38416
|
-
// src/commands/redact.ts
|
|
38417
|
-
init_node();
|
|
38418
|
-
function runRedactSubcommand() {
|
|
38419
|
-
try {
|
|
38420
|
-
const written = redactLogFiles();
|
|
38421
|
-
if (written.length === 0) {
|
|
38422
|
-
process.stdout.write(`caret redact: no logs found to redact.
|
|
38423
|
-
`);
|
|
38424
|
-
} else {
|
|
38425
|
-
for (const path of written)
|
|
38426
|
-
process.stdout.write(`${path}
|
|
38427
|
-
`);
|
|
38428
|
-
}
|
|
38429
|
-
process.exit(0);
|
|
38430
|
-
} catch (e) {
|
|
38431
|
-
process.stderr.write(`caret redact: ${e}
|
|
38432
|
-
`);
|
|
38433
|
-
process.exit(1);
|
|
38434
|
-
}
|
|
38435
|
-
}
|
|
38436
|
-
|
|
38437
|
-
// src/commands/review.ts
|
|
38438
|
-
init_adapters();
|
|
38439
|
-
init_boot();
|
|
38440
|
-
init_paths3();
|
|
38441
|
-
init_settings();
|
|
38442
|
-
init_lifecycle();
|
|
38443
|
-
init_log();
|
|
38444
|
-
|
|
38445
39351
|
// src/review/orchestrate.ts
|
|
38446
39352
|
init_constants();
|
|
38447
39353
|
init_paths3();
|
|
@@ -38466,9 +39372,27 @@ function hasUntaggedCodeBlock(plan) {
|
|
|
38466
39372
|
function denyDecision(reason) {
|
|
38467
39373
|
return { behavior: "deny", feedback: reason, decidedAt: Date.now() };
|
|
38468
39374
|
}
|
|
39375
|
+
function parseHook(parse, stdin) {
|
|
39376
|
+
try {
|
|
39377
|
+
return { input: parse(stdin) };
|
|
39378
|
+
} catch (error) {
|
|
39379
|
+
return { error };
|
|
39380
|
+
}
|
|
39381
|
+
}
|
|
38469
39382
|
|
|
38470
39383
|
class TimeoutError extends Error {
|
|
38471
39384
|
}
|
|
39385
|
+
var REVIEW_FAILURE_CODES = {
|
|
39386
|
+
parse: "hook-input-invalid",
|
|
39387
|
+
validatePlan: "unexpected",
|
|
39388
|
+
ensureDaemon: "daemon-unreachable",
|
|
39389
|
+
postReview: "review-create-failed",
|
|
39390
|
+
reconnect: "daemon-unreachable",
|
|
39391
|
+
longPoll: "unexpected"
|
|
39392
|
+
};
|
|
39393
|
+
function reviewFailureCode(step, err) {
|
|
39394
|
+
return err instanceof TimeoutError ? "review-timeout" : REVIEW_FAILURE_CODES[step];
|
|
39395
|
+
}
|
|
38472
39396
|
function withTimeout(p, ms, message) {
|
|
38473
39397
|
return new Promise((resolve, reject) => {
|
|
38474
39398
|
const t = setTimeout(() => reject(new TimeoutError(message)), ms);
|
|
@@ -38485,25 +39409,29 @@ async function expireAbandoned(expire, handle) {
|
|
|
38485
39409
|
if (!handle)
|
|
38486
39410
|
return;
|
|
38487
39411
|
try {
|
|
38488
|
-
await expire(handle.baseUrl, handle.id);
|
|
39412
|
+
await expire(handle.baseUrl, handle.id, handle.version);
|
|
38489
39413
|
} catch {}
|
|
38490
39414
|
}
|
|
38491
|
-
async function runReview(
|
|
39415
|
+
async function runReview(parsed, deps) {
|
|
38492
39416
|
let step = "parse";
|
|
38493
|
-
|
|
39417
|
+
setLogContext({});
|
|
38494
39418
|
let baseUrl;
|
|
39419
|
+
let version;
|
|
39420
|
+
let reviewId;
|
|
38495
39421
|
try {
|
|
38496
|
-
|
|
38497
|
-
|
|
38498
|
-
|
|
38499
|
-
|
|
39422
|
+
if ("error" in parsed)
|
|
39423
|
+
throw parsed.error;
|
|
39424
|
+
const input = parsed.input;
|
|
39425
|
+
const ids = { sessionId: input.sessionId, cwd: input.cwd };
|
|
39426
|
+
setLogContext(ids);
|
|
39427
|
+
logInfo("review", "review requested");
|
|
38500
39428
|
step = "validatePlan";
|
|
38501
39429
|
if (!input.plan?.trim()) {
|
|
38502
|
-
logInfo(step, "plan rejected: plan is empty"
|
|
39430
|
+
logInfo(step, "plan rejected: plan is empty");
|
|
38503
39431
|
return denyDecision(PLAN_EMPTY_DENY_MESSAGE);
|
|
38504
39432
|
}
|
|
38505
39433
|
if (hasUntaggedCodeBlock(input.plan)) {
|
|
38506
|
-
logInfo(step, "plan rejected: code block missing language marker"
|
|
39434
|
+
logInfo(step, "plan rejected: code block missing language marker");
|
|
38507
39435
|
return denyDecision(PLAN_FORMAT_DENY_MESSAGE);
|
|
38508
39436
|
}
|
|
38509
39437
|
step = "ensureDaemon";
|
|
@@ -38512,7 +39440,7 @@ async function runReview(stdin, deps) {
|
|
|
38512
39440
|
const payload = { ...input, cmux: deps.readPane?.() };
|
|
38513
39441
|
let created = await deps.postReview(baseUrl, payload);
|
|
38514
39442
|
if (!created) {
|
|
38515
|
-
logInfo("review", "review refused: daemon draining"
|
|
39443
|
+
logInfo("review", "review refused: daemon draining");
|
|
38516
39444
|
step = "reconnect";
|
|
38517
39445
|
baseUrl = await deps.ensureDaemon("successor");
|
|
38518
39446
|
step = "postReview";
|
|
@@ -38520,10 +39448,12 @@ async function runReview(stdin, deps) {
|
|
|
38520
39448
|
if (!created)
|
|
38521
39449
|
throw new Error("daemon draining; review not created");
|
|
38522
39450
|
}
|
|
38523
|
-
const { id, hasLiveClient } = created;
|
|
38524
|
-
|
|
38525
|
-
|
|
38526
|
-
|
|
39451
|
+
const { id, hasLiveClient, planFileCurrent } = created;
|
|
39452
|
+
version = created.version;
|
|
39453
|
+
reviewId = id;
|
|
39454
|
+
setLogContext({ ...ids, reviewId });
|
|
39455
|
+
deps.onPosted?.({ baseUrl, id, version, planFileCurrent });
|
|
39456
|
+
logDebug("review", `review created: ${shortId(id)}`);
|
|
38527
39457
|
const open = new URL(baseUrl);
|
|
38528
39458
|
open.hostname = VANITY_HOST;
|
|
38529
39459
|
const url = `${open.origin}/?review=${id}`;
|
|
@@ -38538,7 +39468,12 @@ async function runReview(stdin, deps) {
|
|
|
38538
39468
|
if (remaining <= 0)
|
|
38539
39469
|
throw new TimeoutError("review timed out");
|
|
38540
39470
|
try {
|
|
38541
|
-
|
|
39471
|
+
const polled = await withTimeout(deps.longPoll(baseUrl, id, version), remaining, "review timed out");
|
|
39472
|
+
if (polled === "superseded") {
|
|
39473
|
+
logInfo("review", `review yielded to newer version: ${shortId(id)}`, { version });
|
|
39474
|
+
return denyDecision("caret: superseded by a newer revision of this plan.");
|
|
39475
|
+
}
|
|
39476
|
+
decision = polled ?? undefined;
|
|
38542
39477
|
} catch (err) {
|
|
38543
39478
|
if (err instanceof TimeoutError)
|
|
38544
39479
|
throw err;
|
|
@@ -38548,19 +39483,19 @@ async function runReview(stdin, deps) {
|
|
|
38548
39483
|
}
|
|
38549
39484
|
}
|
|
38550
39485
|
if (decision.behavior === "deny") {
|
|
38551
|
-
logInfo("decision", "plan rejected", {
|
|
39486
|
+
logInfo("decision", "plan rejected", { feedbackChars: decision.feedback?.length });
|
|
38552
39487
|
} else {
|
|
38553
|
-
logInfo("decision", "plan approved", {
|
|
39488
|
+
logInfo("decision", "plan approved", { acceptMode: decision.acceptMode });
|
|
38554
39489
|
}
|
|
38555
39490
|
return decision;
|
|
38556
39491
|
} catch (err) {
|
|
38557
|
-
logError(step, err,
|
|
38558
|
-
if (
|
|
39492
|
+
logError(step, reviewFailureCode(step, err), err);
|
|
39493
|
+
if (reviewId && baseUrl) {
|
|
38559
39494
|
try {
|
|
38560
|
-
await deps.expire(baseUrl,
|
|
38561
|
-
logDebug("review", `review expire requested: ${shortId(
|
|
39495
|
+
await deps.expire(baseUrl, reviewId, version);
|
|
39496
|
+
logDebug("review", `review expire requested: ${shortId(reviewId)}`);
|
|
38562
39497
|
} catch {
|
|
38563
|
-
logDebug("review", "review expire failed;
|
|
39498
|
+
logDebug("review", "review expire failed; next plan reclaims it");
|
|
38564
39499
|
}
|
|
38565
39500
|
}
|
|
38566
39501
|
const msg = errorMessage(err);
|
|
@@ -38568,7 +39503,79 @@ async function runReview(stdin, deps) {
|
|
|
38568
39503
|
}
|
|
38569
39504
|
}
|
|
38570
39505
|
|
|
39506
|
+
// src/review/reconcile.ts
|
|
39507
|
+
init_log();
|
|
39508
|
+
async function runReconcile(parsed, deps) {
|
|
39509
|
+
const ctx = {};
|
|
39510
|
+
try {
|
|
39511
|
+
if ("error" in parsed)
|
|
39512
|
+
throw parsed.error;
|
|
39513
|
+
const input = parsed.input;
|
|
39514
|
+
ctx.sessionId = input.sessionId;
|
|
39515
|
+
if (!input.sessionId)
|
|
39516
|
+
return;
|
|
39517
|
+
const match = (await deps.listReviews()).find((r) => r.sessionId === input.sessionId);
|
|
39518
|
+
if (!match) {
|
|
39519
|
+
logDebug("reconcile", "no pending review for session; nothing to reconcile", { ...ctx });
|
|
39520
|
+
return;
|
|
39521
|
+
}
|
|
39522
|
+
ctx.reviewId = match.id;
|
|
39523
|
+
await deps.resolveReview(match.id);
|
|
39524
|
+
logInfo("reconcile", "terminal approval reconciled", { ...ctx });
|
|
39525
|
+
} catch (err) {
|
|
39526
|
+
logDebug("reconcile", "reconcile skipped", { ...ctx, err: String(err) });
|
|
39527
|
+
}
|
|
39528
|
+
}
|
|
39529
|
+
|
|
39530
|
+
// src/commands/reconcile.ts
|
|
39531
|
+
function prodReconcileDeps(baseUrl) {
|
|
39532
|
+
return {
|
|
39533
|
+
listReviews: () => listReviews(baseUrl),
|
|
39534
|
+
resolveReview: (id) => resolveReview(baseUrl, id, { behavior: "allow" })
|
|
39535
|
+
};
|
|
39536
|
+
}
|
|
39537
|
+
async function runReconcileSubcommand() {
|
|
39538
|
+
try {
|
|
39539
|
+
const loaded = loadSettings();
|
|
39540
|
+
bootHookLogging(loaded);
|
|
39541
|
+
const adapter = selectAdapter();
|
|
39542
|
+
const baseUrl = `http://localhost:${getPort(loaded)}`;
|
|
39543
|
+
const stdin = await Bun.stdin.text();
|
|
39544
|
+
await runReconcile(parseHook(adapter.parseHookInput, stdin), prodReconcileDeps(baseUrl));
|
|
39545
|
+
} catch (err) {
|
|
39546
|
+
logDebug("reconcile", "reconcile subcommand skipped", { err: String(err) });
|
|
39547
|
+
}
|
|
39548
|
+
process.exit(0);
|
|
39549
|
+
}
|
|
39550
|
+
|
|
39551
|
+
// src/commands/redact.ts
|
|
39552
|
+
init_node();
|
|
39553
|
+
function runRedactSubcommand() {
|
|
39554
|
+
try {
|
|
39555
|
+
const written = redactLogFiles();
|
|
39556
|
+
if (written.length === 0) {
|
|
39557
|
+
process.stdout.write(`caret redact: no logs found to redact.
|
|
39558
|
+
`);
|
|
39559
|
+
} else {
|
|
39560
|
+
for (const path of written)
|
|
39561
|
+
process.stdout.write(`${path}
|
|
39562
|
+
`);
|
|
39563
|
+
}
|
|
39564
|
+
process.exit(0);
|
|
39565
|
+
} catch (e) {
|
|
39566
|
+
process.stderr.write(`caret redact: ${e}
|
|
39567
|
+
`);
|
|
39568
|
+
process.exit(1);
|
|
39569
|
+
}
|
|
39570
|
+
}
|
|
39571
|
+
|
|
38571
39572
|
// src/commands/review.ts
|
|
39573
|
+
init_adapters();
|
|
39574
|
+
init_boot();
|
|
39575
|
+
init_paths3();
|
|
39576
|
+
init_settings();
|
|
39577
|
+
init_lifecycle();
|
|
39578
|
+
init_log();
|
|
38572
39579
|
function browserOpenCmd(platform, url) {
|
|
38573
39580
|
return platform === "darwin" ? ["open", url] : platform === "win32" ? ["cmd", "/c", "start", "", url] : ["xdg-open", url];
|
|
38574
39581
|
}
|
|
@@ -38576,6 +39583,13 @@ function reviewUrlLine(url) {
|
|
|
38576
39583
|
return `caret: review this plan at ${url}
|
|
38577
39584
|
`;
|
|
38578
39585
|
}
|
|
39586
|
+
function notesAppendTarget(decision, planFilePath, posted) {
|
|
39587
|
+
if (decision.behavior !== "allow" || !decision.feedback || !planFilePath)
|
|
39588
|
+
return;
|
|
39589
|
+
if (posted?.planFileCurrent === false)
|
|
39590
|
+
return "skip-moved-on";
|
|
39591
|
+
return { path: planFilePath, notes: decision.feedback };
|
|
39592
|
+
}
|
|
38579
39593
|
function openBrowser(url) {
|
|
38580
39594
|
try {
|
|
38581
39595
|
Bun.spawn(browserOpenCmd(process.platform, url), {
|
|
@@ -38584,10 +39598,9 @@ function openBrowser(url) {
|
|
|
38584
39598
|
} catch {}
|
|
38585
39599
|
}
|
|
38586
39600
|
var REVIEW_RESERVE_MS = SUPERVISOR_WINDOW_MS;
|
|
38587
|
-
function prodReviewDeps(
|
|
39601
|
+
function prodReviewDeps(settings) {
|
|
38588
39602
|
return {
|
|
38589
|
-
|
|
38590
|
-
ensureDaemon: async (mode) => ensureDaemon(await prodEnsureDeps(s, () => prodService().manager, REVIEW_RESERVE_MS), mode),
|
|
39603
|
+
ensureDaemon: async (mode) => ensureDaemon(await prodEnsureDeps(settings, () => prodService().manager, REVIEW_RESERVE_MS), mode),
|
|
38591
39604
|
postReview,
|
|
38592
39605
|
longPoll,
|
|
38593
39606
|
openBrowser,
|
|
@@ -38595,10 +39608,18 @@ function prodReviewDeps(s, adapter) {
|
|
|
38595
39608
|
process.stderr.write(reviewUrlLine(url));
|
|
38596
39609
|
},
|
|
38597
39610
|
readPane: readCmuxPane,
|
|
38598
|
-
timeoutMs: reviewTimeoutMs(
|
|
39611
|
+
timeoutMs: reviewTimeoutMs(settings),
|
|
38599
39612
|
expire: expireReview
|
|
38600
39613
|
};
|
|
38601
39614
|
}
|
|
39615
|
+
async function reviewHookInput(parsed, deps, readPlan) {
|
|
39616
|
+
if ("error" in parsed)
|
|
39617
|
+
return { decision: await runReview(parsed, deps) };
|
|
39618
|
+
const payload = parsed.input;
|
|
39619
|
+
const fromFile = payload.planFilePath ? readPlan(payload.planFilePath) : undefined;
|
|
39620
|
+
const input = fromFile?.trim() ? { ...payload, plan: fromFile } : payload;
|
|
39621
|
+
return { decision: await runReview({ input }, deps), input };
|
|
39622
|
+
}
|
|
38602
39623
|
async function runReviewSubcommand() {
|
|
38603
39624
|
const loaded = loadSettings();
|
|
38604
39625
|
bootHookLogging(loaded);
|
|
@@ -38615,7 +39636,7 @@ async function runReviewSubcommand() {
|
|
|
38615
39636
|
};
|
|
38616
39637
|
const denyAndExit = async (reason) => {
|
|
38617
39638
|
if (!responded)
|
|
38618
|
-
logError("signal", new Error(reason));
|
|
39639
|
+
logError("signal", "hook-interrupted", new Error(reason));
|
|
38619
39640
|
respond({ behavior: "deny", feedback: `${reason} See ${logFile()}.`, decidedAt: Date.now() });
|
|
38620
39641
|
await expireAbandoned(expireReview, posted);
|
|
38621
39642
|
process.exit(0);
|
|
@@ -38623,18 +39644,17 @@ async function runReviewSubcommand() {
|
|
|
38623
39644
|
process.once("SIGINT", () => void denyAndExit("caret: interrupted (SIGINT) \u2014 denying to fail safe."));
|
|
38624
39645
|
process.once("SIGTERM", () => void denyAndExit("caret: terminated (SIGTERM) \u2014 denying to fail safe."));
|
|
38625
39646
|
const stdin = await Bun.stdin.text();
|
|
38626
|
-
|
|
38627
|
-
|
|
38628
|
-
|
|
38629
|
-
|
|
38630
|
-
}
|
|
38631
|
-
|
|
38632
|
-
|
|
38633
|
-
|
|
38634
|
-
|
|
38635
|
-
|
|
38636
|
-
|
|
38637
|
-
appendReviewerNotesToPlanFile(hookInput.planFilePath, out.feedback, { warn: logWarn });
|
|
39647
|
+
const deps = prodReviewDeps(loaded);
|
|
39648
|
+
deps.onPosted = (handle) => {
|
|
39649
|
+
posted = handle;
|
|
39650
|
+
};
|
|
39651
|
+
const { decision: out, input } = await reviewHookInput(parseHook(adapter.parseHookInput, stdin), deps, readPlanFile);
|
|
39652
|
+
hookInput = input;
|
|
39653
|
+
const target = notesAppendTarget(out, hookInput?.planFilePath, posted);
|
|
39654
|
+
if (target === "skip-moved-on") {
|
|
39655
|
+
logInfo("review", "plan file changed; notes append skipped");
|
|
39656
|
+
} else if (target) {
|
|
39657
|
+
appendReviewerNotesToPlanFile(target.path, target.notes, { warn: logWarn });
|
|
38638
39658
|
}
|
|
38639
39659
|
respond(out);
|
|
38640
39660
|
process.exit(0);
|
|
@@ -40763,7 +41783,11 @@ function buildProgram() {
|
|
|
40763
41783
|
program.command("reconcile").description("reconcile a terminal plan approval into the daemon (ExitPlanMode PostToolUse hook)").action(() => runReconcileSubcommand());
|
|
40764
41784
|
program.command("steer").description("steer the model to title its plan (EnterPlanMode PostToolUse / UserPromptSubmit hook)").action(() => runSteerSubcommand());
|
|
40765
41785
|
program.command("redact").description("write redacted copies of the logs").action(() => runRedactSubcommand());
|
|
40766
|
-
program.command("
|
|
41786
|
+
program.command("doctor").description("diagnose this caret install").option("--json", "emit the machine-readable JSON document").option("--bundle", "also write a zip of the raw logs and review records, to share").option("--yes", "write the bundle without asking \u2014 it holds unredacted content").action((opts) => runDoctorSubcommand({
|
|
41787
|
+
json: opts.json ?? false,
|
|
41788
|
+
bundle: opts.bundle ?? false,
|
|
41789
|
+
yes: opts.yes ?? false
|
|
41790
|
+
}));
|
|
40767
41791
|
program.command("install").description("install caret into a coding agent, plus the rumdl plan formatter, and choose whether the review UI runs as a service").option("--uninstall", "remove caret from every agent on this machine, and its service, instead of installing").option("--dry-run", "print what would change without writing").option("--refresh", "take the published caret in every target without asking \u2014 clears OpenCode's cached copy, or bumps a pinned entry (Claude Code always takes an update) \u2014 and cycles caret's service onto it").option("--from-local", "dev loop: install the built caret checkout this binary runs from, then hand it the daemon").action(async (opts) => {
|
|
40768
41792
|
const outcome = await runInstallSubcommand({
|
|
40769
41793
|
uninstall: opts.uninstall ?? false,
|
|
@@ -40777,7 +41801,7 @@ function buildProgram() {
|
|
|
40777
41801
|
}
|
|
40778
41802
|
if (import.meta.main) {
|
|
40779
41803
|
runProgram(buildProgram(), (err) => {
|
|
40780
|
-
logError("fatal", err);
|
|
41804
|
+
logError("fatal", "unexpected", err);
|
|
40781
41805
|
const reason = `caret: fatal ${err} \u2014 denying to fail safe. See ${logFile()}.`;
|
|
40782
41806
|
process.stdout.write(`${fatalDeny(reason)}
|
|
40783
41807
|
`);
|