@cabane/companion 0.6.157 → 0.6.159
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/README.md +2 -2
- package/dist/cli.js +281 -214
- package/dist/runtime.js +256 -202
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2896,6 +2896,208 @@ var init_options = __esm({
|
|
|
2896
2896
|
}
|
|
2897
2897
|
});
|
|
2898
2898
|
|
|
2899
|
+
// packages/agent-runtime/src/claude-code/failure-evidence.ts
|
|
2900
|
+
function createFailureEvidence() {
|
|
2901
|
+
const signals = { sawRejectedLimit: false };
|
|
2902
|
+
return {
|
|
2903
|
+
observe(msg) {
|
|
2904
|
+
if (msg.type === "assistant" || msg.type === "auth_status") {
|
|
2905
|
+
const error = msg.error;
|
|
2906
|
+
if (typeof error === "string" && error.length > 0) {
|
|
2907
|
+
if (msg.type === "assistant") signals.lastAssistantError = error;
|
|
2908
|
+
else signals.authError = error;
|
|
2909
|
+
}
|
|
2910
|
+
} else if (msg.type === "rate_limit_event") {
|
|
2911
|
+
const info = msg.rate_limit_info;
|
|
2912
|
+
if (info?.status === "rejected") {
|
|
2913
|
+
signals.sawRejectedLimit = true;
|
|
2914
|
+
signals.rateLimitResetIso = resetsAtToIso(info.resetsAt) ?? signals.rateLimitResetIso;
|
|
2915
|
+
if (typeof info.rateLimitType === "string") signals.rateLimitType = info.rateLimitType;
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
},
|
|
2919
|
+
preInitReason(msg) {
|
|
2920
|
+
const failure = msg ? classifyPreInitFrame(msg, signals) : classifyFailure(signals, { text: "" });
|
|
2921
|
+
return failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
2922
|
+
},
|
|
2923
|
+
failedResultReason(msg) {
|
|
2924
|
+
const failure = classifyResultFailure(msg, signals);
|
|
2925
|
+
const resultText = msg.result ?? "";
|
|
2926
|
+
const isError = msg.is_error === true;
|
|
2927
|
+
return failure ? encodeFailureReason(failure) : isError ? `error:${resultText.slice(0, 200) || "unknown"}` : `result_error:${msg.subtype}`;
|
|
2928
|
+
},
|
|
2929
|
+
thrownFailure(message2, initReceived) {
|
|
2930
|
+
return initReceived ? classifyErrorText(message2) : classifyFailure(signals, { text: message2 });
|
|
2931
|
+
}
|
|
2932
|
+
};
|
|
2933
|
+
}
|
|
2934
|
+
function classifyFailure(s, frame) {
|
|
2935
|
+
const rejectedIsCap = s.sawRejectedLimit && (s.rateLimitResetIso !== void 0 || isSubscriptionWindow(s.rateLimitType));
|
|
2936
|
+
if (rejectedIsCap || frame.terminalReason === "blocking_limit")
|
|
2937
|
+
return {
|
|
2938
|
+
kind: "usage_capped",
|
|
2939
|
+
...s.rateLimitResetIso ? { resetsAt: s.rateLimitResetIso } : {}
|
|
2940
|
+
};
|
|
2941
|
+
return classifyAssistantError(s.lastAssistantError) ?? classifyErrorText([s.authError, frame.text].filter(Boolean).join(" "));
|
|
2942
|
+
}
|
|
2943
|
+
function classifyResultFailure(msg, s) {
|
|
2944
|
+
const resultText = msg.result ?? "";
|
|
2945
|
+
const terminalReason = msg.terminal_reason;
|
|
2946
|
+
const errors = msg.errors;
|
|
2947
|
+
const text = [resultText, ...Array.isArray(errors) ? errors.map(String) : []].join(" ");
|
|
2948
|
+
return classifyFailure(s, { terminalReason, text });
|
|
2949
|
+
}
|
|
2950
|
+
function classifyPreInitFrame(msg, s) {
|
|
2951
|
+
if (msg.type === "result") {
|
|
2952
|
+
const isError = msg.is_error === true;
|
|
2953
|
+
if (msg.subtype === "success" && !isError) return classifyFailure(s, { text: "" });
|
|
2954
|
+
return classifyResultFailure(msg, s);
|
|
2955
|
+
}
|
|
2956
|
+
if (msg.type !== "assistant") return classifyFailure(s, { text: "" });
|
|
2957
|
+
const text = contentBlocks(msg).flatMap((b) => b.type === "text" && typeof b.text === "string" ? [b.text] : []).join(" ");
|
|
2958
|
+
return classifyFailure(s, { text });
|
|
2959
|
+
}
|
|
2960
|
+
function preInitToolActivity(msg) {
|
|
2961
|
+
const kind = msg.type === "assistant" ? "tool_use" : msg.type === "user" ? "tool_result" : null;
|
|
2962
|
+
return kind !== null && contentBlocks(msg).some((b) => b.type === kind);
|
|
2963
|
+
}
|
|
2964
|
+
function contentBlocks(msg) {
|
|
2965
|
+
const content = msg.message?.content;
|
|
2966
|
+
return Array.isArray(content) ? content : [];
|
|
2967
|
+
}
|
|
2968
|
+
function isSubscriptionWindow(value) {
|
|
2969
|
+
return value === "five_hour" || value === "seven_day" || value === "seven_day_opus" || value === "seven_day_sonnet" || value === "overage";
|
|
2970
|
+
}
|
|
2971
|
+
var init_failure_evidence = __esm({
|
|
2972
|
+
"packages/agent-runtime/src/claude-code/failure-evidence.ts"() {
|
|
2973
|
+
"use strict";
|
|
2974
|
+
init_failure();
|
|
2975
|
+
}
|
|
2976
|
+
});
|
|
2977
|
+
|
|
2978
|
+
// packages/agent-runtime/src/claude-code/context-usage.ts
|
|
2979
|
+
function createContextUsage() {
|
|
2980
|
+
let resolvedModel;
|
|
2981
|
+
let lastRequestContextTokens;
|
|
2982
|
+
const compactions = [];
|
|
2983
|
+
return {
|
|
2984
|
+
get resolvedModel() {
|
|
2985
|
+
return resolvedModel;
|
|
2986
|
+
},
|
|
2987
|
+
recordInit(msg) {
|
|
2988
|
+
const model = msg.model;
|
|
2989
|
+
if (typeof model === "string" && model.length > 0) resolvedModel = model;
|
|
2990
|
+
},
|
|
2991
|
+
recordCompaction(msg) {
|
|
2992
|
+
const compaction = readCompaction(msg);
|
|
2993
|
+
if (compaction) compactions.push(compaction);
|
|
2994
|
+
},
|
|
2995
|
+
recordRequest(msg) {
|
|
2996
|
+
const tokens = readRequestContextTokens(msg);
|
|
2997
|
+
if (tokens !== void 0) lastRequestContextTokens = tokens;
|
|
2998
|
+
},
|
|
2999
|
+
async resultUsage(msg, readContextUsage) {
|
|
3000
|
+
const usage = readSdkUsage(msg.usage);
|
|
3001
|
+
if (usage) {
|
|
3002
|
+
if (lastRequestContextTokens !== void 0) usage.contextTokens = lastRequestContextTokens;
|
|
3003
|
+
const window = readContextWindow(msg, resolvedModel);
|
|
3004
|
+
if (window !== void 0) usage.contextWindow = window;
|
|
3005
|
+
if (compactions.length > 0) usage.contextCompactions = compactions;
|
|
3006
|
+
const reading = await readHarnessContext(readContextUsage);
|
|
3007
|
+
if (reading) Object.assign(usage, reading);
|
|
3008
|
+
}
|
|
3009
|
+
return usage;
|
|
3010
|
+
}
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
function readSdkUsage(usage) {
|
|
3014
|
+
if (!usage || typeof usage !== "object") return void 0;
|
|
3015
|
+
const fields = usage;
|
|
3016
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
3017
|
+
const cacheReadTokens = num(fields.cache_read_input_tokens);
|
|
3018
|
+
const cacheCreationTokens = num(fields.cache_creation_input_tokens);
|
|
3019
|
+
const inputTokens = num(fields.input_tokens) + cacheCreationTokens + cacheReadTokens;
|
|
3020
|
+
const outputTokens = num(fields.output_tokens);
|
|
3021
|
+
return { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens };
|
|
3022
|
+
}
|
|
3023
|
+
function readRequestContextTokens(msg) {
|
|
3024
|
+
return readSdkUsage(msg.message?.usage)?.inputTokens;
|
|
3025
|
+
}
|
|
3026
|
+
async function readHarnessContext(read) {
|
|
3027
|
+
if (!read) return void 0;
|
|
3028
|
+
let timer;
|
|
3029
|
+
const timeout = new Promise((resolve2) => {
|
|
3030
|
+
timer = setTimeout(() => resolve2(void 0), CONTEXT_READING_TIMEOUT_MS);
|
|
3031
|
+
});
|
|
3032
|
+
try {
|
|
3033
|
+
return parseContextUsage(await Promise.race([read(), timeout]));
|
|
3034
|
+
} catch {
|
|
3035
|
+
return void 0;
|
|
3036
|
+
} finally {
|
|
3037
|
+
clearTimeout(timer);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
function parseContextUsage(response) {
|
|
3041
|
+
const r = response;
|
|
3042
|
+
if (!r || typeof r !== "object") return void 0;
|
|
3043
|
+
const total = readContextCount(r.totalTokens);
|
|
3044
|
+
const window = readContextCount(r.maxTokens);
|
|
3045
|
+
if (total === void 0 || window === void 0 || window === 0) return void 0;
|
|
3046
|
+
const categories = [];
|
|
3047
|
+
for (const c of Array.isArray(r.categories) ? r.categories : []) {
|
|
3048
|
+
const row3 = c;
|
|
3049
|
+
const tokens = readContextCount(row3?.tokens);
|
|
3050
|
+
if (typeof row3?.name === "string" && tokens !== void 0 && typeof row3.kind === "string" && CATEGORY_KINDS.has(row3.kind)) {
|
|
3051
|
+
categories.push({ name: row3.name, tokens, kind: row3.kind });
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
const buffer = categories.filter((c) => c.kind === "buffer").reduce((sum, c) => sum + c.tokens, 0);
|
|
3055
|
+
return {
|
|
3056
|
+
contextTokens: total,
|
|
3057
|
+
contextWindow: window,
|
|
3058
|
+
...buffer > 0 && buffer < window ? { contextCompactAt: window - buffer } : {},
|
|
3059
|
+
...categories.length > 0 ? { contextCategories: categories } : {}
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3062
|
+
function readCompaction(msg) {
|
|
3063
|
+
const meta = msg.compact_metadata;
|
|
3064
|
+
const preTokens = readContextCount(meta?.pre_tokens);
|
|
3065
|
+
if (!meta || preTokens === void 0) return void 0;
|
|
3066
|
+
if (meta.trigger !== "manual" && meta.trigger !== "auto") return void 0;
|
|
3067
|
+
const postTokens = readContextCount(meta.post_tokens);
|
|
3068
|
+
return {
|
|
3069
|
+
trigger: meta.trigger,
|
|
3070
|
+
preTokens,
|
|
3071
|
+
...postTokens !== void 0 ? { postTokens } : {}
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
function readContextWindow(msg, resolvedModel) {
|
|
3075
|
+
const modelUsage = msg.modelUsage;
|
|
3076
|
+
if (!modelUsage || typeof modelUsage !== "object") return void 0;
|
|
3077
|
+
const pos = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
|
|
3078
|
+
if (resolvedModel) {
|
|
3079
|
+
const direct = pos(modelUsage[resolvedModel]?.contextWindow);
|
|
3080
|
+
if (direct !== void 0) return direct;
|
|
3081
|
+
}
|
|
3082
|
+
let max;
|
|
3083
|
+
for (const entry of Object.values(modelUsage)) {
|
|
3084
|
+
const w = pos(entry?.contextWindow);
|
|
3085
|
+
if (w !== void 0 && (max === void 0 || w > max)) max = w;
|
|
3086
|
+
}
|
|
3087
|
+
return max;
|
|
3088
|
+
}
|
|
3089
|
+
function readContextCount(v) {
|
|
3090
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0;
|
|
3091
|
+
}
|
|
3092
|
+
var CONTEXT_READING_TIMEOUT_MS, CATEGORY_KINDS;
|
|
3093
|
+
var init_context_usage = __esm({
|
|
3094
|
+
"packages/agent-runtime/src/claude-code/context-usage.ts"() {
|
|
3095
|
+
"use strict";
|
|
3096
|
+
CONTEXT_READING_TIMEOUT_MS = 2e3;
|
|
3097
|
+
CATEGORY_KINDS = /* @__PURE__ */ new Set(["used", "free", "buffer", "deferred"]);
|
|
3098
|
+
}
|
|
3099
|
+
});
|
|
3100
|
+
|
|
2899
3101
|
// packages/agent-runtime/src/claude-code/loop.ts
|
|
2900
3102
|
async function* decodeSdkStream(iter, ctx2) {
|
|
2901
3103
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -2911,17 +3113,15 @@ async function* decodeSdkStream(iter, ctx2) {
|
|
|
2911
3113
|
let resultReason;
|
|
2912
3114
|
let sawResult = false;
|
|
2913
3115
|
let usage;
|
|
2914
|
-
const
|
|
2915
|
-
|
|
2916
|
-
let resolvedModel;
|
|
2917
|
-
const signals = { sawRejectedLimit: false };
|
|
3116
|
+
const contextUsage = createContextUsage();
|
|
3117
|
+
const failureEvidence = createFailureEvidence();
|
|
2918
3118
|
let preInitNotice = null;
|
|
2919
3119
|
try {
|
|
2920
3120
|
for await (const msg of iter) {
|
|
2921
3121
|
if (ctx2.signal.aborted) return;
|
|
3122
|
+
failureEvidence.observe(msg);
|
|
2922
3123
|
if (msg.type === "system" && msg.subtype === "init") {
|
|
2923
|
-
|
|
2924
|
-
if (typeof initModel === "string" && initModel.length > 0) resolvedModel = initModel;
|
|
3124
|
+
contextUsage.recordInit(msg);
|
|
2925
3125
|
mcpInventory = readMcpInventory(msg);
|
|
2926
3126
|
workspaceProven = provesWorkspaceTools(msg);
|
|
2927
3127
|
if (!workspaceProven) {
|
|
@@ -2938,63 +3138,35 @@ async function* decodeSdkStream(iter, ctx2) {
|
|
|
2938
3138
|
};
|
|
2939
3139
|
}
|
|
2940
3140
|
} else if (msg.type === "system" && msg.subtype === "compact_boundary") {
|
|
2941
|
-
|
|
2942
|
-
if (compaction) compactions.push(compaction);
|
|
3141
|
+
contextUsage.recordCompaction(msg);
|
|
2943
3142
|
} else if (!workspaceProven && (msg.type === "assistant" || msg.type === "user" || msg.type === "result")) {
|
|
2944
3143
|
if (preInitToolActivity(msg) && ctx2.startEvidence) ctx2.startEvidence.toolActivity = true;
|
|
2945
|
-
|
|
2946
|
-
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
3144
|
+
resultReason = failureEvidence.preInitReason(msg);
|
|
2947
3145
|
if (msg.type === "assistant" && isHarnessAuthored(msg))
|
|
2948
3146
|
preInitNotice = harnessNoticeText(msg);
|
|
2949
3147
|
break;
|
|
2950
3148
|
} else if (msg.type === "assistant") {
|
|
2951
|
-
const assistantErr = msg.error;
|
|
2952
|
-
if (typeof assistantErr === "string" && assistantErr.length > 0) {
|
|
2953
|
-
signals.lastAssistantError = assistantErr;
|
|
2954
|
-
}
|
|
2955
3149
|
if (isHarnessAuthored(msg)) {
|
|
2956
3150
|
await emitHarnessMessage(msg, emit, pending, buffer);
|
|
2957
3151
|
yield* drain(out);
|
|
2958
3152
|
continue;
|
|
2959
3153
|
}
|
|
2960
|
-
|
|
2961
|
-
if (reqContext !== void 0) lastRequestContextTokens = reqContext;
|
|
3154
|
+
contextUsage.recordRequest(msg);
|
|
2962
3155
|
await processAssistantMessage(msg, emit, pending, buffer);
|
|
2963
3156
|
yield* drain(out);
|
|
2964
3157
|
} else if (msg.type === "user") {
|
|
2965
3158
|
await emitToolResults(msg, emit, pending);
|
|
2966
3159
|
yield* drain(out);
|
|
2967
|
-
} else if (msg.type === "rate_limit_event") {
|
|
2968
|
-
const info = msg.rate_limit_info;
|
|
2969
|
-
if (info?.status === "rejected") {
|
|
2970
|
-
signals.sawRejectedLimit = true;
|
|
2971
|
-
signals.rateLimitResetIso = resetsAtToIso(info.resetsAt) ?? signals.rateLimitResetIso;
|
|
2972
|
-
if (typeof info.rateLimitType === "string") signals.rateLimitType = info.rateLimitType;
|
|
2973
|
-
}
|
|
2974
|
-
} else if (msg.type === "auth_status") {
|
|
2975
|
-
const err = msg.error;
|
|
2976
|
-
if (typeof err === "string" && err.length > 0) signals.authError = err;
|
|
2977
3160
|
} else if (msg.type === "result") {
|
|
2978
3161
|
const origin = msg.origin;
|
|
2979
3162
|
if (origin?.kind === "task-notification") continue;
|
|
2980
3163
|
sawResult = true;
|
|
2981
|
-
usage =
|
|
2982
|
-
if (usage) {
|
|
2983
|
-
if (lastRequestContextTokens !== void 0)
|
|
2984
|
-
usage.contextTokens = lastRequestContextTokens;
|
|
2985
|
-
const window = readContextWindow(msg, resolvedModel);
|
|
2986
|
-
if (window !== void 0) usage.contextWindow = window;
|
|
2987
|
-
if (compactions.length > 0) usage.contextCompactions = compactions;
|
|
2988
|
-
const reading = await readHarnessContext(ctx2.readContextUsage);
|
|
2989
|
-
if (reading) Object.assign(usage, reading);
|
|
2990
|
-
}
|
|
3164
|
+
usage = await contextUsage.resultUsage(msg, ctx2.readContextUsage);
|
|
2991
3165
|
const isError = msg.is_error === true;
|
|
2992
3166
|
if (msg.subtype === "success" && !isError) {
|
|
2993
3167
|
ok = true;
|
|
2994
3168
|
} else {
|
|
2995
|
-
|
|
2996
|
-
const failure = classifyResultFailure(msg, signals);
|
|
2997
|
-
resultReason = failure ? encodeFailureReason(failure) : isError ? `error:${resultText.slice(0, 200) || "unknown"}` : `result_error:${msg.subtype}`;
|
|
3169
|
+
resultReason = failureEvidence.failedResultReason(msg);
|
|
2998
3170
|
ok = false;
|
|
2999
3171
|
}
|
|
3000
3172
|
break;
|
|
@@ -3003,7 +3175,7 @@ async function* decodeSdkStream(iter, ctx2) {
|
|
|
3003
3175
|
} catch (err) {
|
|
3004
3176
|
if (ctx2.signal.aborted) throw err;
|
|
3005
3177
|
const message2 = err instanceof Error ? err.message : String(err);
|
|
3006
|
-
const failure =
|
|
3178
|
+
const failure = failureEvidence.thrownFailure(message2, mcpInventory.initReceived);
|
|
3007
3179
|
if (!failure && (mcpInventory.initReceived || ctx2.resumedSessionId === null)) throw err;
|
|
3008
3180
|
ok = false;
|
|
3009
3181
|
if (failure) {
|
|
@@ -3018,8 +3190,7 @@ async function* decodeSdkStream(iter, ctx2) {
|
|
|
3018
3190
|
if (!workspaceProven) {
|
|
3019
3191
|
ok = false;
|
|
3020
3192
|
if (resultReason === void 0) {
|
|
3021
|
-
|
|
3022
|
-
resultReason = failure ? encodeFailureReason(failure) : "session_start_failed";
|
|
3193
|
+
resultReason = failureEvidence.preInitReason();
|
|
3023
3194
|
}
|
|
3024
3195
|
if (preInitNotice) yield { type: "runtime_notice", body: preInitNotice };
|
|
3025
3196
|
}
|
|
@@ -3037,7 +3208,7 @@ async function* decodeSdkStream(iter, ctx2) {
|
|
|
3037
3208
|
ok,
|
|
3038
3209
|
...resultReason ? { reason: resultReason } : {},
|
|
3039
3210
|
...usage ? { usage } : {},
|
|
3040
|
-
...resolvedModel ? { resolvedModel } : {},
|
|
3211
|
+
...contextUsage.resolvedModel ? { resolvedModel: contextUsage.resolvedModel } : {},
|
|
3041
3212
|
mcpInventory
|
|
3042
3213
|
};
|
|
3043
3214
|
}
|
|
@@ -3056,38 +3227,6 @@ function provesWorkspaceTools(msg) {
|
|
|
3056
3227
|
const tools = msg.tools;
|
|
3057
3228
|
return Array.isArray(tools) && tools.includes("mcp__cabane__query");
|
|
3058
3229
|
}
|
|
3059
|
-
function classifyFailure(s, frame) {
|
|
3060
|
-
const rejectedIsCap = s.sawRejectedLimit && (s.rateLimitResetIso !== void 0 || isSubscriptionWindow(s.rateLimitType));
|
|
3061
|
-
if (rejectedIsCap || frame.terminalReason === "blocking_limit")
|
|
3062
|
-
return {
|
|
3063
|
-
kind: "usage_capped",
|
|
3064
|
-
...s.rateLimitResetIso ? { resetsAt: s.rateLimitResetIso } : {}
|
|
3065
|
-
};
|
|
3066
|
-
return classifyAssistantError(s.lastAssistantError) ?? classifyErrorText([s.authError, frame.text].filter(Boolean).join(" "));
|
|
3067
|
-
}
|
|
3068
|
-
function classifyResultFailure(msg, s) {
|
|
3069
|
-
const resultText = msg.result ?? "";
|
|
3070
|
-
const terminalReason = msg.terminal_reason;
|
|
3071
|
-
const errors = msg.errors;
|
|
3072
|
-
const text = [resultText, ...Array.isArray(errors) ? errors.map(String) : []].join(" ");
|
|
3073
|
-
return classifyFailure(s, { terminalReason, text });
|
|
3074
|
-
}
|
|
3075
|
-
function classifyPreInitFrame(msg, s) {
|
|
3076
|
-
if (msg.type === "result") {
|
|
3077
|
-
const isError = msg.is_error === true;
|
|
3078
|
-
if (msg.subtype === "success" && !isError) return classifyFailure(s, { text: "" });
|
|
3079
|
-
return classifyResultFailure(msg, s);
|
|
3080
|
-
}
|
|
3081
|
-
if (msg.type !== "assistant") return classifyFailure(s, { text: "" });
|
|
3082
|
-
const error = msg.error;
|
|
3083
|
-
const signals = typeof error === "string" && error.length > 0 ? { ...s, lastAssistantError: error } : s;
|
|
3084
|
-
const text = contentBlocks(msg).flatMap((b) => b.type === "text" && typeof b.text === "string" ? [b.text] : []).join(" ");
|
|
3085
|
-
return classifyFailure(signals, { text });
|
|
3086
|
-
}
|
|
3087
|
-
function preInitToolActivity(msg) {
|
|
3088
|
-
const kind = msg.type === "assistant" ? "tool_use" : msg.type === "user" ? "tool_result" : null;
|
|
3089
|
-
return kind !== null && contentBlocks(msg).some((b) => b.type === kind);
|
|
3090
|
-
}
|
|
3091
3230
|
function isHarnessAuthored(msg) {
|
|
3092
3231
|
const error = msg.error;
|
|
3093
3232
|
if (typeof error === "string" && error.length > 0) return true;
|
|
@@ -3111,107 +3250,19 @@ async function emitHarnessMessage(msg, emit, pending, buffer) {
|
|
|
3111
3250
|
);
|
|
3112
3251
|
}
|
|
3113
3252
|
}
|
|
3114
|
-
function contentBlocks(msg) {
|
|
3115
|
-
const content = msg.message?.content;
|
|
3116
|
-
return Array.isArray(content) ? content : [];
|
|
3117
|
-
}
|
|
3118
|
-
function isSubscriptionWindow(value) {
|
|
3119
|
-
return value === "five_hour" || value === "seven_day" || value === "seven_day_opus" || value === "seven_day_sonnet" || value === "overage";
|
|
3120
|
-
}
|
|
3121
3253
|
function* drain(out) {
|
|
3122
3254
|
while (out.length > 0) yield out.shift();
|
|
3123
3255
|
}
|
|
3124
|
-
|
|
3125
|
-
const usage = msg.usage;
|
|
3126
|
-
if (!usage || typeof usage !== "object") return void 0;
|
|
3127
|
-
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
3128
|
-
const cacheReadTokens = num(usage.cache_read_input_tokens);
|
|
3129
|
-
const cacheCreationTokens = num(usage.cache_creation_input_tokens);
|
|
3130
|
-
const inputTokens = num(usage.input_tokens) + cacheCreationTokens + cacheReadTokens;
|
|
3131
|
-
const outputTokens = num(usage.output_tokens);
|
|
3132
|
-
return { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens };
|
|
3133
|
-
}
|
|
3134
|
-
function readRequestContextTokens(msg) {
|
|
3135
|
-
const usage = msg.message?.usage;
|
|
3136
|
-
if (!usage || typeof usage !== "object") return void 0;
|
|
3137
|
-
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
3138
|
-
return num(usage.input_tokens) + num(usage.cache_read_input_tokens) + num(usage.cache_creation_input_tokens);
|
|
3139
|
-
}
|
|
3140
|
-
async function readHarnessContext(read) {
|
|
3141
|
-
if (!read) return void 0;
|
|
3142
|
-
let timer;
|
|
3143
|
-
const timeout = new Promise((resolve2) => {
|
|
3144
|
-
timer = setTimeout(() => resolve2(void 0), CONTEXT_READING_TIMEOUT_MS);
|
|
3145
|
-
});
|
|
3146
|
-
try {
|
|
3147
|
-
return parseContextUsage(await Promise.race([read(), timeout]));
|
|
3148
|
-
} catch {
|
|
3149
|
-
return void 0;
|
|
3150
|
-
} finally {
|
|
3151
|
-
clearTimeout(timer);
|
|
3152
|
-
}
|
|
3153
|
-
}
|
|
3154
|
-
function parseContextUsage(response) {
|
|
3155
|
-
const r = response;
|
|
3156
|
-
if (!r || typeof r !== "object") return void 0;
|
|
3157
|
-
const count = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0;
|
|
3158
|
-
const total = count(r.totalTokens);
|
|
3159
|
-
const window = count(r.maxTokens);
|
|
3160
|
-
if (total === void 0 || window === void 0 || window === 0) return void 0;
|
|
3161
|
-
const categories = [];
|
|
3162
|
-
for (const c of Array.isArray(r.categories) ? r.categories : []) {
|
|
3163
|
-
const row3 = c;
|
|
3164
|
-
const tokens = count(row3?.tokens);
|
|
3165
|
-
if (typeof row3?.name === "string" && tokens !== void 0 && typeof row3.kind === "string" && CATEGORY_KINDS.has(row3.kind)) {
|
|
3166
|
-
categories.push({ name: row3.name, tokens, kind: row3.kind });
|
|
3167
|
-
}
|
|
3168
|
-
}
|
|
3169
|
-
const buffer = categories.filter((c) => c.kind === "buffer").reduce((sum, c) => sum + c.tokens, 0);
|
|
3170
|
-
return {
|
|
3171
|
-
contextTokens: total,
|
|
3172
|
-
contextWindow: window,
|
|
3173
|
-
...buffer > 0 && buffer < window ? { contextCompactAt: window - buffer } : {},
|
|
3174
|
-
...categories.length > 0 ? { contextCategories: categories } : {}
|
|
3175
|
-
};
|
|
3176
|
-
}
|
|
3177
|
-
function readCompaction(msg) {
|
|
3178
|
-
const meta = msg.compact_metadata;
|
|
3179
|
-
const count = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? Math.round(v) : void 0;
|
|
3180
|
-
const preTokens = count(meta?.pre_tokens);
|
|
3181
|
-
if (!meta || preTokens === void 0) return void 0;
|
|
3182
|
-
if (meta.trigger !== "manual" && meta.trigger !== "auto") return void 0;
|
|
3183
|
-
const postTokens = count(meta.post_tokens);
|
|
3184
|
-
return {
|
|
3185
|
-
trigger: meta.trigger,
|
|
3186
|
-
preTokens,
|
|
3187
|
-
...postTokens !== void 0 ? { postTokens } : {}
|
|
3188
|
-
};
|
|
3189
|
-
}
|
|
3190
|
-
function readContextWindow(msg, resolvedModel) {
|
|
3191
|
-
const modelUsage = msg.modelUsage;
|
|
3192
|
-
if (!modelUsage || typeof modelUsage !== "object") return void 0;
|
|
3193
|
-
const pos = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
|
|
3194
|
-
if (resolvedModel) {
|
|
3195
|
-
const direct = pos(modelUsage[resolvedModel]?.contextWindow);
|
|
3196
|
-
if (direct !== void 0) return direct;
|
|
3197
|
-
}
|
|
3198
|
-
let max;
|
|
3199
|
-
for (const entry of Object.values(modelUsage)) {
|
|
3200
|
-
const w = pos(entry?.contextWindow);
|
|
3201
|
-
if (w !== void 0 && (max === void 0 || w > max)) max = w;
|
|
3202
|
-
}
|
|
3203
|
-
return max;
|
|
3204
|
-
}
|
|
3205
|
-
var HARNESS_PLACEHOLDERS, CONTEXT_READING_TIMEOUT_MS, CATEGORY_KINDS;
|
|
3256
|
+
var HARNESS_PLACEHOLDERS;
|
|
3206
3257
|
var init_loop = __esm({
|
|
3207
3258
|
"packages/agent-runtime/src/claude-code/loop.ts"() {
|
|
3208
3259
|
"use strict";
|
|
3209
3260
|
init_transcript();
|
|
3210
3261
|
init_failure();
|
|
3211
3262
|
init_session();
|
|
3263
|
+
init_failure_evidence();
|
|
3264
|
+
init_context_usage();
|
|
3212
3265
|
HARNESS_PLACEHOLDERS = /* @__PURE__ */ new Set(["No response requested.", "(no content)"]);
|
|
3213
|
-
CONTEXT_READING_TIMEOUT_MS = 2e3;
|
|
3214
|
-
CATEGORY_KINDS = /* @__PURE__ */ new Set(["used", "free", "buffer", "deferred"]);
|
|
3215
3266
|
}
|
|
3216
3267
|
});
|
|
3217
3268
|
|
|
@@ -15382,6 +15433,8 @@ var SseSubscriber = class {
|
|
|
15382
15433
|
opts;
|
|
15383
15434
|
aborted = false;
|
|
15384
15435
|
controller = null;
|
|
15436
|
+
// Ends the reconnect backoff early, so a stop never waits out a sleep of up to 30 s.
|
|
15437
|
+
wake = null;
|
|
15385
15438
|
lastEventId;
|
|
15386
15439
|
// Set after `start()`; resolved when the subscriber loop exits (either
|
|
15387
15440
|
// because `stop()` was called or because an auth failure tore it down).
|
|
@@ -15392,12 +15445,7 @@ var SseSubscriber = class {
|
|
|
15392
15445
|
stop() {
|
|
15393
15446
|
this.aborted = true;
|
|
15394
15447
|
this.controller?.abort();
|
|
15395
|
-
|
|
15396
|
-
// Connect again after a `stop()`, once its loop has exited.
|
|
15397
|
-
async resume() {
|
|
15398
|
-
await this.finished;
|
|
15399
|
-
this.aborted = false;
|
|
15400
|
-
this.start();
|
|
15448
|
+
this.wake?.();
|
|
15401
15449
|
}
|
|
15402
15450
|
setCursor(eventId) {
|
|
15403
15451
|
this.lastEventId = eventId;
|
|
@@ -15410,7 +15458,7 @@ var SseSubscriber = class {
|
|
|
15410
15458
|
backoff = 500;
|
|
15411
15459
|
if (!this.aborted) {
|
|
15412
15460
|
this.opts.log.warn("Lost the connection to Cabane; reconnecting");
|
|
15413
|
-
await
|
|
15461
|
+
await this.backoff(backoff);
|
|
15414
15462
|
}
|
|
15415
15463
|
} catch (err) {
|
|
15416
15464
|
if (this.aborted) return;
|
|
@@ -15430,11 +15478,21 @@ var SseSubscriber = class {
|
|
|
15430
15478
|
},
|
|
15431
15479
|
"Lost the connection to Cabane; reconnecting"
|
|
15432
15480
|
);
|
|
15433
|
-
await
|
|
15481
|
+
await this.backoff(backoff);
|
|
15434
15482
|
backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
|
|
15435
15483
|
}
|
|
15436
15484
|
}
|
|
15437
15485
|
}
|
|
15486
|
+
backoff(ms) {
|
|
15487
|
+
return new Promise((resolve2) => {
|
|
15488
|
+
const timer = setTimeout(done, ms);
|
|
15489
|
+
this.wake = done;
|
|
15490
|
+
function done() {
|
|
15491
|
+
clearTimeout(timer);
|
|
15492
|
+
resolve2();
|
|
15493
|
+
}
|
|
15494
|
+
});
|
|
15495
|
+
}
|
|
15438
15496
|
async connect() {
|
|
15439
15497
|
const base = this.opts.baseUrl.endsWith("/") ? this.opts.baseUrl.slice(0, -1) : this.opts.baseUrl;
|
|
15440
15498
|
const url = `${base}${this.opts.path}`;
|
|
@@ -15481,9 +15539,6 @@ var SseSubscriber = class {
|
|
|
15481
15539
|
}
|
|
15482
15540
|
}
|
|
15483
15541
|
};
|
|
15484
|
-
function sleep4(ms) {
|
|
15485
|
-
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
15486
|
-
}
|
|
15487
15542
|
|
|
15488
15543
|
// src/supervisor.ts
|
|
15489
15544
|
init_transcript_writer();
|
|
@@ -15666,7 +15721,7 @@ function updateMode(env = process.env) {
|
|
|
15666
15721
|
return env.CABANE_COMPANION_SUPERVISED === "1" ? "supervised" : "attached";
|
|
15667
15722
|
}
|
|
15668
15723
|
var STAGE_RETRY_MS = 60 * 6e4;
|
|
15669
|
-
var
|
|
15724
|
+
var SWAP_WAIT_MS = 10 * 6e4;
|
|
15670
15725
|
var SOURCE_BUILD_VERSION = "0.0.0";
|
|
15671
15726
|
function versionToStage(input) {
|
|
15672
15727
|
const { own, state, now, force } = input;
|
|
@@ -15816,6 +15871,8 @@ var CompanionUpdater = class {
|
|
|
15816
15871
|
deps;
|
|
15817
15872
|
staging = null;
|
|
15818
15873
|
ready = null;
|
|
15874
|
+
// When `ready` verified; `SWAP_WAIT_MS` counts from here.
|
|
15875
|
+
readyAt = 0;
|
|
15819
15876
|
swapping = false;
|
|
15820
15877
|
watchdog = null;
|
|
15821
15878
|
now;
|
|
@@ -15914,7 +15971,7 @@ var CompanionUpdater = class {
|
|
|
15914
15971
|
return { ok: true, message: `Cabane ${this.deps.own} is up to date.` };
|
|
15915
15972
|
if (state.pending?.to === latest) return { ok: true, message: this.waitingMessage(latest) };
|
|
15916
15973
|
if (this.ready === latest) {
|
|
15917
|
-
void this.trySwap();
|
|
15974
|
+
void this.trySwap(true);
|
|
15918
15975
|
return { ok: true, message: this.waitingMessage(latest) };
|
|
15919
15976
|
}
|
|
15920
15977
|
this.maybeStage(true);
|
|
@@ -15930,13 +15987,14 @@ var CompanionUpdater = class {
|
|
|
15930
15987
|
message: failed?.kind === "failed" && failed.to === latest ? `Couldn't update to ${latest}: ${failed.reason ?? "see cabane logs"}.` : `Couldn't update to ${latest}. cabane logs says why.`
|
|
15931
15988
|
};
|
|
15932
15989
|
}
|
|
15933
|
-
void this.trySwap();
|
|
15990
|
+
void this.trySwap(true);
|
|
15934
15991
|
return { ok: true, message: this.waitingMessage(latest) };
|
|
15935
15992
|
}
|
|
15993
|
+
// Only the person's `cabane update` asks, and that swaps at once.
|
|
15936
15994
|
waitingMessage(version) {
|
|
15937
15995
|
if (this.deps.mode === "attached")
|
|
15938
15996
|
return `Cabane ${version} is installed. It runs from the next cabane start.`;
|
|
15939
|
-
return this.deps.isIdle() ? `Updating to ${version} now.` : `Updating to ${version}
|
|
15997
|
+
return this.deps.isIdle() ? `Updating to ${version} now.` : `Updating to ${version} now. The turns running now carry on after the restart.`;
|
|
15940
15998
|
}
|
|
15941
15999
|
maybeStage(force) {
|
|
15942
16000
|
if (this.staging) return;
|
|
@@ -15954,6 +16012,7 @@ var CompanionUpdater = class {
|
|
|
15954
16012
|
const outcome = await stage(version, this.deps.harnesses());
|
|
15955
16013
|
if (outcome.ok) {
|
|
15956
16014
|
this.ready = version;
|
|
16015
|
+
this.readyAt = this.now();
|
|
15957
16016
|
updateUpdateState((s) => {
|
|
15958
16017
|
const { failed: _failed, ...rest } = s;
|
|
15959
16018
|
return rest;
|
|
@@ -15992,8 +16051,10 @@ var CompanionUpdater = class {
|
|
|
15992
16051
|
});
|
|
15993
16052
|
void this.staging.then(() => this.trySwap());
|
|
15994
16053
|
}
|
|
15995
|
-
// Swap into the ready version
|
|
15996
|
-
|
|
16054
|
+
// Swap into the ready version: when nothing is running, when it has waited `SWAP_WAIT_MS` for
|
|
16055
|
+
// that, when this version is below the server's minimum, or `now` (the person asked). Called on
|
|
16056
|
+
// each heartbeat until it does.
|
|
16057
|
+
async trySwap(now = false) {
|
|
15997
16058
|
const to = this.ready;
|
|
15998
16059
|
if (!to || this.swapping) return;
|
|
15999
16060
|
if (readUpdateState().pending) return;
|
|
@@ -16002,18 +16063,9 @@ var CompanionUpdater = class {
|
|
|
16002
16063
|
this.deps.log.info({ version: to }, `Cabane ${to} is installed; it runs from the next start`);
|
|
16003
16064
|
return;
|
|
16004
16065
|
}
|
|
16005
|
-
if (!this.deps.isIdle()) return;
|
|
16066
|
+
if (!now && !this.deps.isIdle() && !this.waitedLongEnough() && !this.belowMinimum()) return;
|
|
16006
16067
|
this.swapping = true;
|
|
16007
16068
|
try {
|
|
16008
|
-
await this.deps.pauseStream();
|
|
16009
|
-
const sleep5 = this.deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
16010
|
-
const deadline = this.now() + SWAP_DRAIN_MS;
|
|
16011
|
-
await sleep5(250);
|
|
16012
|
-
while (!this.deps.isIdle() && this.now() < deadline) await sleep5(250);
|
|
16013
|
-
if (!this.deps.isIdle()) {
|
|
16014
|
-
this.deps.resumeStream();
|
|
16015
|
-
return;
|
|
16016
|
-
}
|
|
16017
16069
|
this.select(to);
|
|
16018
16070
|
this.deps.log.info({ from: this.deps.own, to }, `Restarting into Cabane ${to}`);
|
|
16019
16071
|
await this.deps.restart();
|
|
@@ -16021,6 +16073,14 @@ var CompanionUpdater = class {
|
|
|
16021
16073
|
this.swapping = false;
|
|
16022
16074
|
}
|
|
16023
16075
|
}
|
|
16076
|
+
waitedLongEnough() {
|
|
16077
|
+
return this.now() - this.readyAt >= SWAP_WAIT_MS;
|
|
16078
|
+
}
|
|
16079
|
+
// Cabane refuses this version's turns: waiting would only turn them away.
|
|
16080
|
+
belowMinimum() {
|
|
16081
|
+
const minimum = readUpdateState().server?.minimum;
|
|
16082
|
+
return !!minimum && compareVersions(this.deps.own, minimum) < 0;
|
|
16083
|
+
}
|
|
16024
16084
|
// Record the swap and point `current` at `to`, leaving the guard that counts its starts.
|
|
16025
16085
|
select(to) {
|
|
16026
16086
|
const fromRoot = currentRoot() ?? ownPackageRoot();
|
|
@@ -16045,6 +16105,7 @@ init_version2();
|
|
|
16045
16105
|
init_src();
|
|
16046
16106
|
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
16047
16107
|
var ARCHIVE_PRUNE_MS = 24 * 60 * 6e4;
|
|
16108
|
+
var FORCE_EXIT_MS = 4e3;
|
|
16048
16109
|
var SHUTDOWN_HEARTBEAT_WAIT_MS = 1e3;
|
|
16049
16110
|
function positiveIntEnv(name) {
|
|
16050
16111
|
const raw = process.env[name];
|
|
@@ -16265,14 +16326,7 @@ var CompanionSupervisor = class {
|
|
|
16265
16326
|
autoUpdate: () => this.config.update?.auto === true,
|
|
16266
16327
|
harnesses: () => this.connectedBundledHarnesses(),
|
|
16267
16328
|
isIdle: () => this.idle(),
|
|
16268
|
-
|
|
16269
|
-
const sub = this.deviceSub;
|
|
16270
|
-
if (!sub) return;
|
|
16271
|
-
sub.stop();
|
|
16272
|
-
await sub.finished;
|
|
16273
|
-
},
|
|
16274
|
-
resumeStream: () => void this.deviceSub?.resume(),
|
|
16275
|
-
restart: () => this.restartForUpdate(),
|
|
16329
|
+
restart: () => this.restartBounded(),
|
|
16276
16330
|
log: this.log
|
|
16277
16331
|
});
|
|
16278
16332
|
this.updater.start();
|
|
@@ -17394,8 +17448,22 @@ var CompanionSupervisor = class {
|
|
|
17394
17448
|
);
|
|
17395
17449
|
}
|
|
17396
17450
|
}
|
|
17451
|
+
// The update's restart, held to the same `FORCE_EXIT_MS` a signal's stop gets. Exit 0: the
|
|
17452
|
+
// swap is recorded and `current` names the new version, so the service starts it either way.
|
|
17453
|
+
async restartBounded() {
|
|
17454
|
+
const force = setTimeout(() => {
|
|
17455
|
+
this.log.warn({ ms: FORCE_EXIT_MS }, "Restarting for the update: shutdown took too long");
|
|
17456
|
+
this.exitFn(0);
|
|
17457
|
+
}, FORCE_EXIT_MS);
|
|
17458
|
+
force.unref?.();
|
|
17459
|
+
try {
|
|
17460
|
+
await this.restartForUpdate();
|
|
17461
|
+
} finally {
|
|
17462
|
+
clearTimeout(force);
|
|
17463
|
+
}
|
|
17464
|
+
}
|
|
17397
17465
|
// Nothing running and nothing about to: no dispatch claimed, no turn chained, no recovery
|
|
17398
|
-
// in progress. What a self-update swap waits for
|
|
17466
|
+
// in progress. What a self-update swap waits for, up to `SWAP_WAIT_MS`.
|
|
17399
17467
|
idle() {
|
|
17400
17468
|
if (this.claims.size > 0 || this.recovering) return false;
|
|
17401
17469
|
for (const wr of this.workspaces.values()) if (wr.chains.size > 0) return false;
|
|
@@ -17417,7 +17485,7 @@ var CompanionSupervisor = class {
|
|
|
17417
17485
|
if (auto) this.kickHeartbeat();
|
|
17418
17486
|
return {
|
|
17419
17487
|
ok: true,
|
|
17420
|
-
message: auto ? "Cabane will keep itself up to date. Updates install between turns and roll back if they fail." : "Cabane won't update itself. Run cabane update to update."
|
|
17488
|
+
message: auto ? "Cabane will keep itself up to date. Updates install between turns when they can, and roll back if they fail." : "Cabane won't update itself. Run cabane update to update."
|
|
17421
17489
|
};
|
|
17422
17490
|
}
|
|
17423
17491
|
if (!this.updater) return { ok: false, message: "This computer isn't paired yet." };
|
|
@@ -18127,7 +18195,7 @@ async function startDaemon(opts = {}, deps = {}) {
|
|
|
18127
18195
|
const readState = deps.readState ?? readLiveRuntimeState;
|
|
18128
18196
|
const verify = deps.verify ?? ((s) => verifyRuntime(s));
|
|
18129
18197
|
const spawnDetached = deps.spawnDetached ?? defaultSpawnDetached;
|
|
18130
|
-
const
|
|
18198
|
+
const sleep4 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
18131
18199
|
const now = deps.now ?? (() => Date.now());
|
|
18132
18200
|
const quiet = opts.report === "failures";
|
|
18133
18201
|
const cfg = requireStartConfig({
|
|
@@ -18175,7 +18243,7 @@ async function startDaemon(opts = {}, deps = {}) {
|
|
|
18175
18243
|
const deadline = now() + STARTUP_TIMEOUT_MS;
|
|
18176
18244
|
let seen = readState();
|
|
18177
18245
|
while (!ready(seen) && now() < deadline) {
|
|
18178
|
-
await
|
|
18246
|
+
await sleep4(POLL_INTERVAL_MS);
|
|
18179
18247
|
seen = readState();
|
|
18180
18248
|
}
|
|
18181
18249
|
return ready(seen) ? seen : null;
|
|
@@ -18248,7 +18316,7 @@ async function stop(deps = {}) {
|
|
|
18248
18316
|
const readState = deps.readState ?? readLiveRuntimeState;
|
|
18249
18317
|
const verify = deps.verify ?? ((s) => verifyRuntime(s));
|
|
18250
18318
|
const kill = deps.kill ?? ((pid2, signal) => process.kill(pid2, signal));
|
|
18251
|
-
const
|
|
18319
|
+
const sleep4 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
18252
18320
|
const now = deps.now ?? (() => Date.now());
|
|
18253
18321
|
const state = readState();
|
|
18254
18322
|
if (!state) {
|
|
@@ -18276,7 +18344,7 @@ async function stop(deps = {}) {
|
|
|
18276
18344
|
process.stdout.write("companion: stopped.\n");
|
|
18277
18345
|
return;
|
|
18278
18346
|
}
|
|
18279
|
-
await
|
|
18347
|
+
await sleep4(POLL_INTERVAL_MS2);
|
|
18280
18348
|
}
|
|
18281
18349
|
process.stdout.write(
|
|
18282
18350
|
`companion: didn't exit within ${TERM_GRACE_MS / 1e3}s, sending SIGKILL.
|
|
@@ -18302,7 +18370,7 @@ function isAlive(kill, pid) {
|
|
|
18302
18370
|
var STARTUP_WAIT_MS = 2e4;
|
|
18303
18371
|
async function installAndStartService(deps = {}) {
|
|
18304
18372
|
const readState = deps.readState ?? readLiveRuntimeState;
|
|
18305
|
-
const
|
|
18373
|
+
const sleep4 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
18306
18374
|
const now = deps.now ?? Date.now;
|
|
18307
18375
|
const before = await serviceState(deps);
|
|
18308
18376
|
const running = readState();
|
|
@@ -18318,7 +18386,7 @@ async function installAndStartService(deps = {}) {
|
|
|
18318
18386
|
while (now() < deadline) {
|
|
18319
18387
|
const state = readState();
|
|
18320
18388
|
if (state?.socket) return true;
|
|
18321
|
-
await
|
|
18389
|
+
await sleep4(250);
|
|
18322
18390
|
}
|
|
18323
18391
|
write(
|
|
18324
18392
|
`${INDENT}The service is installed but Cabane hasn't come up yet. ${cabane("service status")} and ${cabane("logs")} say why.`
|
|
@@ -18332,7 +18400,7 @@ async function serviceInstall(deps = {}) {
|
|
|
18332
18400
|
}
|
|
18333
18401
|
write(
|
|
18334
18402
|
INDENT + tick(
|
|
18335
|
-
"Cabane runs as a service. It starts at boot, restarts if it stops, and updates itself between turns."
|
|
18403
|
+
"Cabane runs as a service. It starts at boot, restarts if it stops, and updates itself, usually between turns."
|
|
18336
18404
|
)
|
|
18337
18405
|
);
|
|
18338
18406
|
write(`${INDENT} Status: ${cabane("service status")} Logs: ${cabane("logs")}`);
|
|
@@ -18395,7 +18463,7 @@ function updateRows(state, auto, opts = {}) {
|
|
|
18395
18463
|
rows.push(
|
|
18396
18464
|
row(
|
|
18397
18465
|
"Auto-update",
|
|
18398
|
-
!opts.service ? `off \u2014 runs with the service: ${cabane("service install")}` : auto === true ? "on \u2014 updates install between turns and roll back if they fail" : `off \u2014 ${cabane("update --auto on")} turns it on`
|
|
18466
|
+
!opts.service ? `off \u2014 runs with the service: ${cabane("service install")}` : auto === true ? "on \u2014 updates install between turns when they can, and roll back if they fail" : `off \u2014 ${cabane("update --auto on")} turns it on`
|
|
18399
18467
|
)
|
|
18400
18468
|
);
|
|
18401
18469
|
const last = state.lastResult;
|
|
@@ -18426,7 +18494,6 @@ function setRunsAsService(on) {
|
|
|
18426
18494
|
}
|
|
18427
18495
|
|
|
18428
18496
|
// src/commands/start.ts
|
|
18429
|
-
var FORCE_EXIT_MS = 4e3;
|
|
18430
18497
|
var MAX_CODES = 3;
|
|
18431
18498
|
async function start(opts = {}) {
|
|
18432
18499
|
setLogOverrides(opts);
|
|
@@ -18892,7 +18959,7 @@ async function askHowToRun(deps = {}) {
|
|
|
18892
18959
|
return { asService: false };
|
|
18893
18960
|
blank();
|
|
18894
18961
|
const asService = await ask(
|
|
18895
|
-
"Keep Cabane running and up to date on this computer? It starts at boot, restarts if it stops, and updates itself between turns."
|
|
18962
|
+
"Keep Cabane running and up to date on this computer? It starts at boot, restarts if it stops, and updates itself, usually between turns."
|
|
18896
18963
|
);
|
|
18897
18964
|
if (!asService) saveConfig({ ...cfg, serviceDeclined: true });
|
|
18898
18965
|
blank();
|
|
@@ -19316,7 +19383,7 @@ async function update(opts = {}) {
|
|
|
19316
19383
|
saveConfig({ ...cfg2, update: { auto } });
|
|
19317
19384
|
write(
|
|
19318
19385
|
INDENT + tick(
|
|
19319
|
-
auto ? "Cabane will keep itself up to date. Updates install between turns and roll back if they fail." : "Cabane won't update itself."
|
|
19386
|
+
auto ? "Cabane will keep itself up to date. Updates install between turns when they can, and roll back if they fail." : "Cabane won't update itself."
|
|
19320
19387
|
)
|
|
19321
19388
|
);
|
|
19322
19389
|
return;
|