switchroom 0.15.45 → 0.16.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-scheduler/index.js +56 -15
- package/dist/auth-broker/index.js +383 -97
- package/dist/cli/autoaccept-poll.js +4842 -35
- package/dist/cli/drive-write-pretool.mjs +7 -4
- package/dist/cli/notion-write-pretool.mjs +35 -4
- package/dist/cli/self-improve-apply-guard-pretool.mjs +626 -0
- package/dist/cli/self-improve-stop.mjs +428 -0
- package/dist/cli/switchroom.js +2894 -841
- package/dist/host-control/main.js +2685 -207
- package/dist/vault/approvals/kernel-server.js +7453 -7413
- package/dist/vault/broker/server.js +11428 -11388
- package/examples/minimal.yaml +1 -0
- package/examples/switchroom.yaml +1 -0
- package/package.json +3 -3
- package/profiles/_base/start.sh.hbs +97 -1
- package/profiles/_shared/execution-discipline.md.hbs +18 -0
- package/profiles/default/CLAUDE.md.hbs +0 -19
- package/telegram-plugin/.claude-plugin/plugin.json +2 -2
- package/telegram-plugin/answer-stream-flag.ts +12 -49
- package/telegram-plugin/answer-stream.ts +5 -150
- package/telegram-plugin/auth-snapshot-format.ts +280 -48
- package/telegram-plugin/auto-fallback-fleet.ts +44 -1
- package/telegram-plugin/context-exhaustion.ts +12 -0
- package/telegram-plugin/demo-mask.ts +154 -0
- package/telegram-plugin/dist/bridge/bridge.js +55 -12
- package/telegram-plugin/dist/gateway/gateway.js +2938 -977
- package/telegram-plugin/dist/server.js +55 -12
- package/telegram-plugin/docs/waiting-ux-spec.md +2 -2
- package/telegram-plugin/draft-stream.ts +47 -410
- package/telegram-plugin/final-answer-detect.ts +17 -12
- package/telegram-plugin/fleet-fallback-resume.ts +131 -0
- package/telegram-plugin/format.ts +56 -19
- package/telegram-plugin/gateway/auth-add-flow.ts +332 -127
- package/telegram-plugin/gateway/auth-broker-client.ts +2 -2
- package/telegram-plugin/gateway/auth-command.ts +70 -14
- package/telegram-plugin/gateway/clean-shutdown-marker.ts +44 -0
- package/telegram-plugin/gateway/config-approval-handler.test.ts +91 -4
- package/telegram-plugin/gateway/config-approval-handler.ts +94 -13
- package/telegram-plugin/gateway/current-turn-map.ts +188 -0
- package/telegram-plugin/gateway/disconnect-flush.ts +3 -1
- package/telegram-plugin/gateway/effort-command.ts +8 -3
- package/telegram-plugin/gateway/emission-authority.ts +369 -0
- package/telegram-plugin/gateway/feed-open-gate.ts +292 -0
- package/telegram-plugin/gateway/gateway.ts +1857 -292
- package/telegram-plugin/gateway/inject-handler.test.ts +2 -1
- package/telegram-plugin/gateway/model-command.ts +115 -4
- package/telegram-plugin/gateway/ms365-write-approval.test.ts +4 -4
- package/telegram-plugin/gateway/represent-guard.ts +72 -0
- package/telegram-plugin/gateway/status-surface-log.test.ts +5 -4
- package/telegram-plugin/gateway/status-surface-log.ts +14 -3
- package/telegram-plugin/history.ts +33 -11
- package/telegram-plugin/hooks/repo-context-pretool.mjs +26 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +5 -0
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +8 -0
- package/telegram-plugin/hooks/tool-label-pretool.mjs +39 -15
- package/telegram-plugin/issues-card.ts +4 -0
- package/telegram-plugin/model-unavailable.ts +124 -0
- package/telegram-plugin/narrative-dedup.ts +69 -0
- package/telegram-plugin/over-ping-safety-net.ts +70 -4
- package/telegram-plugin/package.json +3 -3
- package/telegram-plugin/pending-work-progress.ts +12 -0
- package/telegram-plugin/permission-rule.ts +32 -5
- package/telegram-plugin/permission-title.ts +152 -9
- package/telegram-plugin/quota-check.ts +13 -0
- package/telegram-plugin/quota-watch.ts +135 -7
- package/telegram-plugin/registry/turns-schema.test.ts +24 -0
- package/telegram-plugin/registry/turns-schema.ts +9 -0
- package/telegram-plugin/runtime-metrics.ts +13 -0
- package/telegram-plugin/session-tail.ts +96 -11
- package/telegram-plugin/silence-poke.ts +170 -24
- package/telegram-plugin/slot-banner-driver.ts +3 -0
- package/telegram-plugin/status-no-truncate.ts +44 -0
- package/telegram-plugin/status-reactions.ts +20 -3
- package/telegram-plugin/stream-controller.ts +4 -23
- package/telegram-plugin/stream-reply-handler.ts +6 -24
- package/telegram-plugin/streaming-metrics.ts +91 -0
- package/telegram-plugin/subagent-watcher.ts +212 -66
- package/telegram-plugin/tests/activity-ever-opened-sticky.test.ts +47 -0
- package/telegram-plugin/tests/answer-stream-dedup.test.ts +9 -26
- package/telegram-plugin/tests/answer-stream-flag.test.ts +25 -58
- package/telegram-plugin/tests/answer-stream-silent-markers.test.ts +41 -51
- package/telegram-plugin/tests/answer-stream.test.ts +2 -411
- package/telegram-plugin/tests/auth-add-flow.test.ts +488 -253
- package/telegram-plugin/tests/auth-command-format2.test.ts +71 -1
- package/telegram-plugin/tests/auth-snapshot-format.test.ts +376 -6
- package/telegram-plugin/tests/auto-fallback-fleet.test.ts +120 -0
- package/telegram-plugin/tests/cross-turn-card-gate.test.ts +424 -0
- package/telegram-plugin/tests/demo-mask.test.ts +127 -0
- package/telegram-plugin/tests/draft-stream.test.ts +0 -827
- package/telegram-plugin/tests/emission-authority-card-drain-gate.test.ts +236 -0
- package/telegram-plugin/tests/emission-authority-facade.test.ts +488 -0
- package/telegram-plugin/tests/emission-authority-open-gate.test.ts +179 -0
- package/telegram-plugin/tests/emission-authority-ping-gate.test.ts +395 -0
- package/telegram-plugin/tests/emission-determinism-wiring.test.ts +177 -0
- package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +146 -0
- package/telegram-plugin/tests/feed-open-gate.test.ts +259 -0
- package/telegram-plugin/tests/feed-survival.test.ts +526 -0
- package/telegram-plugin/tests/fleet-fallback-resume.test.ts +197 -0
- package/telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts +117 -0
- package/telegram-plugin/tests/gateway-no-reply-single-emit.test.ts +4 -11
- package/telegram-plugin/tests/history.test.ts +60 -0
- package/telegram-plugin/tests/model-command.test.ts +134 -0
- package/telegram-plugin/tests/model-unavailable.test.ts +118 -0
- package/telegram-plugin/tests/narrative-dedup.test.ts +118 -0
- package/telegram-plugin/tests/orphaned-reply-rearm.test.ts +285 -0
- package/telegram-plugin/tests/over-ping-final-answer-decoupling.test.ts +194 -0
- package/telegram-plugin/tests/over-ping-safety-net.test.ts +2 -2
- package/telegram-plugin/tests/per-topic-current-turn.test.ts +373 -0
- package/telegram-plugin/tests/permission-card-origin-kill-switch.test.ts +42 -0
- package/telegram-plugin/tests/permission-rule.test.ts +17 -0
- package/telegram-plugin/tests/permission-title.test.ts +206 -17
- package/telegram-plugin/tests/quota-watch.test.ts +252 -9
- package/telegram-plugin/tests/reply-terminal-reaction.test.ts +6 -1
- package/telegram-plugin/tests/repo-context-pretool.test.ts +62 -0
- package/telegram-plugin/tests/represent-guard.test.ts +162 -0
- package/telegram-plugin/tests/session-tail.test.ts +147 -3
- package/telegram-plugin/tests/silence-liveness-wiring.test.ts +18 -0
- package/telegram-plugin/tests/status-card-budget-parity.test.ts +72 -0
- package/telegram-plugin/tests/status-surface-log.test.ts +146 -0
- package/telegram-plugin/tests/subagent-watcher-clip-narrative.test.ts +58 -0
- package/telegram-plugin/tests/subagent-watcher-parent-turn-key.test.ts +102 -0
- package/telegram-plugin/tests/subagent-watcher-workflow-visibility.test.ts +225 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +147 -0
- package/telegram-plugin/tests/telegram-activity-visibility-integration.test.ts +597 -0
- package/telegram-plugin/tests/telegram-format.test.ts +101 -6
- package/telegram-plugin/tests/tool-activity-summary.test.ts +550 -15
- package/telegram-plugin/tests/tool-label-pretool.test.ts +73 -0
- package/telegram-plugin/tests/tool-label-sidecar.test.ts +44 -0
- package/telegram-plugin/tests/tool-labels.test.ts +67 -0
- package/telegram-plugin/tests/turn-liveness-floor.test.ts +196 -0
- package/telegram-plugin/tests/turn-liveness-invariant.test.ts +340 -0
- package/telegram-plugin/tests/welcome-text.test.ts +32 -3
- package/telegram-plugin/tests/worker-activity-feed.test.ts +470 -22
- package/telegram-plugin/tool-activity-summary.ts +375 -58
- package/telegram-plugin/turn-liveness-floor.ts +240 -0
- package/telegram-plugin/uat/assertions.ts +115 -0
- package/telegram-plugin/uat/driver.ts +68 -0
- package/telegram-plugin/uat/scenarios/bg-sub-agent-dispatch-dm.test.ts +119 -133
- package/telegram-plugin/uat/scenarios/jtbd-answer-pings.test.ts +94 -0
- package/telegram-plugin/uat/scenarios/jtbd-cross-turn-card-dm.test.ts +109 -0
- package/telegram-plugin/uat/scenarios/jtbd-foreground-feed-thinkgap-dm.test.ts +478 -0
- package/telegram-plugin/uat/scenarios/jtbd-foreground-feed-visibility-dm.test.ts +396 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-feed-open-dm.test.ts +202 -0
- package/telegram-plugin/uat/scenarios/jtbd-reply-is-last-dm.test.ts +202 -0
- package/telegram-plugin/uat/scenarios/reactions-dm.test.ts +93 -87
- package/telegram-plugin/welcome-text.ts +13 -1
- package/telegram-plugin/worker-activity-feed.ts +157 -82
- package/telegram-plugin/draft-transport.ts +0 -122
- package/telegram-plugin/tests/draft-retirement-wiring.test.ts +0 -82
- package/telegram-plugin/tests/draft-transport.test.ts +0 -211
|
@@ -9664,10 +9664,2044 @@ var init_secretlint_source = __esm(() => {
|
|
|
9664
9664
|
init_suppressor();
|
|
9665
9665
|
});
|
|
9666
9666
|
|
|
9667
|
+
// node_modules/.bun/commander@13.1.0/node_modules/commander/lib/error.js
|
|
9668
|
+
var require_error = __commonJS((exports2) => {
|
|
9669
|
+
class CommanderError extends Error {
|
|
9670
|
+
constructor(exitCode, code, message) {
|
|
9671
|
+
super(message);
|
|
9672
|
+
Error.captureStackTrace(this, this.constructor);
|
|
9673
|
+
this.name = this.constructor.name;
|
|
9674
|
+
this.code = code;
|
|
9675
|
+
this.exitCode = exitCode;
|
|
9676
|
+
this.nestedError = undefined;
|
|
9677
|
+
}
|
|
9678
|
+
}
|
|
9679
|
+
|
|
9680
|
+
class InvalidArgumentError extends CommanderError {
|
|
9681
|
+
constructor(message) {
|
|
9682
|
+
super(1, "commander.invalidArgument", message);
|
|
9683
|
+
Error.captureStackTrace(this, this.constructor);
|
|
9684
|
+
this.name = this.constructor.name;
|
|
9685
|
+
}
|
|
9686
|
+
}
|
|
9687
|
+
exports2.CommanderError = CommanderError;
|
|
9688
|
+
exports2.InvalidArgumentError = InvalidArgumentError;
|
|
9689
|
+
});
|
|
9690
|
+
|
|
9691
|
+
// node_modules/.bun/commander@13.1.0/node_modules/commander/lib/argument.js
|
|
9692
|
+
var require_argument = __commonJS((exports2) => {
|
|
9693
|
+
var { InvalidArgumentError } = require_error();
|
|
9694
|
+
|
|
9695
|
+
class Argument {
|
|
9696
|
+
constructor(name, description) {
|
|
9697
|
+
this.description = description || "";
|
|
9698
|
+
this.variadic = false;
|
|
9699
|
+
this.parseArg = undefined;
|
|
9700
|
+
this.defaultValue = undefined;
|
|
9701
|
+
this.defaultValueDescription = undefined;
|
|
9702
|
+
this.argChoices = undefined;
|
|
9703
|
+
switch (name[0]) {
|
|
9704
|
+
case "<":
|
|
9705
|
+
this.required = true;
|
|
9706
|
+
this._name = name.slice(1, -1);
|
|
9707
|
+
break;
|
|
9708
|
+
case "[":
|
|
9709
|
+
this.required = false;
|
|
9710
|
+
this._name = name.slice(1, -1);
|
|
9711
|
+
break;
|
|
9712
|
+
default:
|
|
9713
|
+
this.required = true;
|
|
9714
|
+
this._name = name;
|
|
9715
|
+
break;
|
|
9716
|
+
}
|
|
9717
|
+
if (this._name.length > 3 && this._name.slice(-3) === "...") {
|
|
9718
|
+
this.variadic = true;
|
|
9719
|
+
this._name = this._name.slice(0, -3);
|
|
9720
|
+
}
|
|
9721
|
+
}
|
|
9722
|
+
name() {
|
|
9723
|
+
return this._name;
|
|
9724
|
+
}
|
|
9725
|
+
_concatValue(value, previous) {
|
|
9726
|
+
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
9727
|
+
return [value];
|
|
9728
|
+
}
|
|
9729
|
+
return previous.concat(value);
|
|
9730
|
+
}
|
|
9731
|
+
default(value, description) {
|
|
9732
|
+
this.defaultValue = value;
|
|
9733
|
+
this.defaultValueDescription = description;
|
|
9734
|
+
return this;
|
|
9735
|
+
}
|
|
9736
|
+
argParser(fn) {
|
|
9737
|
+
this.parseArg = fn;
|
|
9738
|
+
return this;
|
|
9739
|
+
}
|
|
9740
|
+
choices(values) {
|
|
9741
|
+
this.argChoices = values.slice();
|
|
9742
|
+
this.parseArg = (arg, previous) => {
|
|
9743
|
+
if (!this.argChoices.includes(arg)) {
|
|
9744
|
+
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
|
|
9745
|
+
}
|
|
9746
|
+
if (this.variadic) {
|
|
9747
|
+
return this._concatValue(arg, previous);
|
|
9748
|
+
}
|
|
9749
|
+
return arg;
|
|
9750
|
+
};
|
|
9751
|
+
return this;
|
|
9752
|
+
}
|
|
9753
|
+
argRequired() {
|
|
9754
|
+
this.required = true;
|
|
9755
|
+
return this;
|
|
9756
|
+
}
|
|
9757
|
+
argOptional() {
|
|
9758
|
+
this.required = false;
|
|
9759
|
+
return this;
|
|
9760
|
+
}
|
|
9761
|
+
}
|
|
9762
|
+
function humanReadableArgName(arg) {
|
|
9763
|
+
const nameOutput = arg.name() + (arg.variadic === true ? "..." : "");
|
|
9764
|
+
return arg.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]";
|
|
9765
|
+
}
|
|
9766
|
+
exports2.Argument = Argument;
|
|
9767
|
+
exports2.humanReadableArgName = humanReadableArgName;
|
|
9768
|
+
});
|
|
9769
|
+
|
|
9770
|
+
// node_modules/.bun/commander@13.1.0/node_modules/commander/lib/help.js
|
|
9771
|
+
var require_help = __commonJS((exports2) => {
|
|
9772
|
+
var { humanReadableArgName } = require_argument();
|
|
9773
|
+
|
|
9774
|
+
class Help {
|
|
9775
|
+
constructor() {
|
|
9776
|
+
this.helpWidth = undefined;
|
|
9777
|
+
this.minWidthToWrap = 40;
|
|
9778
|
+
this.sortSubcommands = false;
|
|
9779
|
+
this.sortOptions = false;
|
|
9780
|
+
this.showGlobalOptions = false;
|
|
9781
|
+
}
|
|
9782
|
+
prepareContext(contextOptions) {
|
|
9783
|
+
this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
|
|
9784
|
+
}
|
|
9785
|
+
visibleCommands(cmd) {
|
|
9786
|
+
const visibleCommands = cmd.commands.filter((cmd2) => !cmd2._hidden);
|
|
9787
|
+
const helpCommand = cmd._getHelpCommand();
|
|
9788
|
+
if (helpCommand && !helpCommand._hidden) {
|
|
9789
|
+
visibleCommands.push(helpCommand);
|
|
9790
|
+
}
|
|
9791
|
+
if (this.sortSubcommands) {
|
|
9792
|
+
visibleCommands.sort((a, b) => {
|
|
9793
|
+
return a.name().localeCompare(b.name());
|
|
9794
|
+
});
|
|
9795
|
+
}
|
|
9796
|
+
return visibleCommands;
|
|
9797
|
+
}
|
|
9798
|
+
compareOptions(a, b) {
|
|
9799
|
+
const getSortKey = (option) => {
|
|
9800
|
+
return option.short ? option.short.replace(/^-/, "") : option.long.replace(/^--/, "");
|
|
9801
|
+
};
|
|
9802
|
+
return getSortKey(a).localeCompare(getSortKey(b));
|
|
9803
|
+
}
|
|
9804
|
+
visibleOptions(cmd) {
|
|
9805
|
+
const visibleOptions = cmd.options.filter((option) => !option.hidden);
|
|
9806
|
+
const helpOption = cmd._getHelpOption();
|
|
9807
|
+
if (helpOption && !helpOption.hidden) {
|
|
9808
|
+
const removeShort = helpOption.short && cmd._findOption(helpOption.short);
|
|
9809
|
+
const removeLong = helpOption.long && cmd._findOption(helpOption.long);
|
|
9810
|
+
if (!removeShort && !removeLong) {
|
|
9811
|
+
visibleOptions.push(helpOption);
|
|
9812
|
+
} else if (helpOption.long && !removeLong) {
|
|
9813
|
+
visibleOptions.push(cmd.createOption(helpOption.long, helpOption.description));
|
|
9814
|
+
} else if (helpOption.short && !removeShort) {
|
|
9815
|
+
visibleOptions.push(cmd.createOption(helpOption.short, helpOption.description));
|
|
9816
|
+
}
|
|
9817
|
+
}
|
|
9818
|
+
if (this.sortOptions) {
|
|
9819
|
+
visibleOptions.sort(this.compareOptions);
|
|
9820
|
+
}
|
|
9821
|
+
return visibleOptions;
|
|
9822
|
+
}
|
|
9823
|
+
visibleGlobalOptions(cmd) {
|
|
9824
|
+
if (!this.showGlobalOptions)
|
|
9825
|
+
return [];
|
|
9826
|
+
const globalOptions = [];
|
|
9827
|
+
for (let ancestorCmd = cmd.parent;ancestorCmd; ancestorCmd = ancestorCmd.parent) {
|
|
9828
|
+
const visibleOptions = ancestorCmd.options.filter((option) => !option.hidden);
|
|
9829
|
+
globalOptions.push(...visibleOptions);
|
|
9830
|
+
}
|
|
9831
|
+
if (this.sortOptions) {
|
|
9832
|
+
globalOptions.sort(this.compareOptions);
|
|
9833
|
+
}
|
|
9834
|
+
return globalOptions;
|
|
9835
|
+
}
|
|
9836
|
+
visibleArguments(cmd) {
|
|
9837
|
+
if (cmd._argsDescription) {
|
|
9838
|
+
cmd.registeredArguments.forEach((argument) => {
|
|
9839
|
+
argument.description = argument.description || cmd._argsDescription[argument.name()] || "";
|
|
9840
|
+
});
|
|
9841
|
+
}
|
|
9842
|
+
if (cmd.registeredArguments.find((argument) => argument.description)) {
|
|
9843
|
+
return cmd.registeredArguments;
|
|
9844
|
+
}
|
|
9845
|
+
return [];
|
|
9846
|
+
}
|
|
9847
|
+
subcommandTerm(cmd) {
|
|
9848
|
+
const args = cmd.registeredArguments.map((arg) => humanReadableArgName(arg)).join(" ");
|
|
9849
|
+
return cmd._name + (cmd._aliases[0] ? "|" + cmd._aliases[0] : "") + (cmd.options.length ? " [options]" : "") + (args ? " " + args : "");
|
|
9850
|
+
}
|
|
9851
|
+
optionTerm(option) {
|
|
9852
|
+
return option.flags;
|
|
9853
|
+
}
|
|
9854
|
+
argumentTerm(argument) {
|
|
9855
|
+
return argument.name();
|
|
9856
|
+
}
|
|
9857
|
+
longestSubcommandTermLength(cmd, helper) {
|
|
9858
|
+
return helper.visibleCommands(cmd).reduce((max, command) => {
|
|
9859
|
+
return Math.max(max, this.displayWidth(helper.styleSubcommandTerm(helper.subcommandTerm(command))));
|
|
9860
|
+
}, 0);
|
|
9861
|
+
}
|
|
9862
|
+
longestOptionTermLength(cmd, helper) {
|
|
9863
|
+
return helper.visibleOptions(cmd).reduce((max, option) => {
|
|
9864
|
+
return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
|
|
9865
|
+
}, 0);
|
|
9866
|
+
}
|
|
9867
|
+
longestGlobalOptionTermLength(cmd, helper) {
|
|
9868
|
+
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
|
|
9869
|
+
return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
|
|
9870
|
+
}, 0);
|
|
9871
|
+
}
|
|
9872
|
+
longestArgumentTermLength(cmd, helper) {
|
|
9873
|
+
return helper.visibleArguments(cmd).reduce((max, argument) => {
|
|
9874
|
+
return Math.max(max, this.displayWidth(helper.styleArgumentTerm(helper.argumentTerm(argument))));
|
|
9875
|
+
}, 0);
|
|
9876
|
+
}
|
|
9877
|
+
commandUsage(cmd) {
|
|
9878
|
+
let cmdName = cmd._name;
|
|
9879
|
+
if (cmd._aliases[0]) {
|
|
9880
|
+
cmdName = cmdName + "|" + cmd._aliases[0];
|
|
9881
|
+
}
|
|
9882
|
+
let ancestorCmdNames = "";
|
|
9883
|
+
for (let ancestorCmd = cmd.parent;ancestorCmd; ancestorCmd = ancestorCmd.parent) {
|
|
9884
|
+
ancestorCmdNames = ancestorCmd.name() + " " + ancestorCmdNames;
|
|
9885
|
+
}
|
|
9886
|
+
return ancestorCmdNames + cmdName + " " + cmd.usage();
|
|
9887
|
+
}
|
|
9888
|
+
commandDescription(cmd) {
|
|
9889
|
+
return cmd.description();
|
|
9890
|
+
}
|
|
9891
|
+
subcommandDescription(cmd) {
|
|
9892
|
+
return cmd.summary() || cmd.description();
|
|
9893
|
+
}
|
|
9894
|
+
optionDescription(option) {
|
|
9895
|
+
const extraInfo = [];
|
|
9896
|
+
if (option.argChoices) {
|
|
9897
|
+
extraInfo.push(`choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(", ")}`);
|
|
9898
|
+
}
|
|
9899
|
+
if (option.defaultValue !== undefined) {
|
|
9900
|
+
const showDefault = option.required || option.optional || option.isBoolean() && typeof option.defaultValue === "boolean";
|
|
9901
|
+
if (showDefault) {
|
|
9902
|
+
extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
|
|
9903
|
+
}
|
|
9904
|
+
}
|
|
9905
|
+
if (option.presetArg !== undefined && option.optional) {
|
|
9906
|
+
extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
|
|
9907
|
+
}
|
|
9908
|
+
if (option.envVar !== undefined) {
|
|
9909
|
+
extraInfo.push(`env: ${option.envVar}`);
|
|
9910
|
+
}
|
|
9911
|
+
if (extraInfo.length > 0) {
|
|
9912
|
+
return `${option.description} (${extraInfo.join(", ")})`;
|
|
9913
|
+
}
|
|
9914
|
+
return option.description;
|
|
9915
|
+
}
|
|
9916
|
+
argumentDescription(argument) {
|
|
9917
|
+
const extraInfo = [];
|
|
9918
|
+
if (argument.argChoices) {
|
|
9919
|
+
extraInfo.push(`choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(", ")}`);
|
|
9920
|
+
}
|
|
9921
|
+
if (argument.defaultValue !== undefined) {
|
|
9922
|
+
extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`);
|
|
9923
|
+
}
|
|
9924
|
+
if (extraInfo.length > 0) {
|
|
9925
|
+
const extraDescription = `(${extraInfo.join(", ")})`;
|
|
9926
|
+
if (argument.description) {
|
|
9927
|
+
return `${argument.description} ${extraDescription}`;
|
|
9928
|
+
}
|
|
9929
|
+
return extraDescription;
|
|
9930
|
+
}
|
|
9931
|
+
return argument.description;
|
|
9932
|
+
}
|
|
9933
|
+
formatHelp(cmd, helper) {
|
|
9934
|
+
const termWidth = helper.padWidth(cmd, helper);
|
|
9935
|
+
const helpWidth = helper.helpWidth ?? 80;
|
|
9936
|
+
function callFormatItem(term, description) {
|
|
9937
|
+
return helper.formatItem(term, termWidth, description, helper);
|
|
9938
|
+
}
|
|
9939
|
+
let output = [
|
|
9940
|
+
`${helper.styleTitle("Usage:")} ${helper.styleUsage(helper.commandUsage(cmd))}`,
|
|
9941
|
+
""
|
|
9942
|
+
];
|
|
9943
|
+
const commandDescription = helper.commandDescription(cmd);
|
|
9944
|
+
if (commandDescription.length > 0) {
|
|
9945
|
+
output = output.concat([
|
|
9946
|
+
helper.boxWrap(helper.styleCommandDescription(commandDescription), helpWidth),
|
|
9947
|
+
""
|
|
9948
|
+
]);
|
|
9949
|
+
}
|
|
9950
|
+
const argumentList = helper.visibleArguments(cmd).map((argument) => {
|
|
9951
|
+
return callFormatItem(helper.styleArgumentTerm(helper.argumentTerm(argument)), helper.styleArgumentDescription(helper.argumentDescription(argument)));
|
|
9952
|
+
});
|
|
9953
|
+
if (argumentList.length > 0) {
|
|
9954
|
+
output = output.concat([
|
|
9955
|
+
helper.styleTitle("Arguments:"),
|
|
9956
|
+
...argumentList,
|
|
9957
|
+
""
|
|
9958
|
+
]);
|
|
9959
|
+
}
|
|
9960
|
+
const optionList = helper.visibleOptions(cmd).map((option) => {
|
|
9961
|
+
return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
|
|
9962
|
+
});
|
|
9963
|
+
if (optionList.length > 0) {
|
|
9964
|
+
output = output.concat([
|
|
9965
|
+
helper.styleTitle("Options:"),
|
|
9966
|
+
...optionList,
|
|
9967
|
+
""
|
|
9968
|
+
]);
|
|
9969
|
+
}
|
|
9970
|
+
if (helper.showGlobalOptions) {
|
|
9971
|
+
const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
|
|
9972
|
+
return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
|
|
9973
|
+
});
|
|
9974
|
+
if (globalOptionList.length > 0) {
|
|
9975
|
+
output = output.concat([
|
|
9976
|
+
helper.styleTitle("Global Options:"),
|
|
9977
|
+
...globalOptionList,
|
|
9978
|
+
""
|
|
9979
|
+
]);
|
|
9980
|
+
}
|
|
9981
|
+
}
|
|
9982
|
+
const commandList = helper.visibleCommands(cmd).map((cmd2) => {
|
|
9983
|
+
return callFormatItem(helper.styleSubcommandTerm(helper.subcommandTerm(cmd2)), helper.styleSubcommandDescription(helper.subcommandDescription(cmd2)));
|
|
9984
|
+
});
|
|
9985
|
+
if (commandList.length > 0) {
|
|
9986
|
+
output = output.concat([
|
|
9987
|
+
helper.styleTitle("Commands:"),
|
|
9988
|
+
...commandList,
|
|
9989
|
+
""
|
|
9990
|
+
]);
|
|
9991
|
+
}
|
|
9992
|
+
return output.join(`
|
|
9993
|
+
`);
|
|
9994
|
+
}
|
|
9995
|
+
displayWidth(str) {
|
|
9996
|
+
return stripColor(str).length;
|
|
9997
|
+
}
|
|
9998
|
+
styleTitle(str) {
|
|
9999
|
+
return str;
|
|
10000
|
+
}
|
|
10001
|
+
styleUsage(str) {
|
|
10002
|
+
return str.split(" ").map((word) => {
|
|
10003
|
+
if (word === "[options]")
|
|
10004
|
+
return this.styleOptionText(word);
|
|
10005
|
+
if (word === "[command]")
|
|
10006
|
+
return this.styleSubcommandText(word);
|
|
10007
|
+
if (word[0] === "[" || word[0] === "<")
|
|
10008
|
+
return this.styleArgumentText(word);
|
|
10009
|
+
return this.styleCommandText(word);
|
|
10010
|
+
}).join(" ");
|
|
10011
|
+
}
|
|
10012
|
+
styleCommandDescription(str) {
|
|
10013
|
+
return this.styleDescriptionText(str);
|
|
10014
|
+
}
|
|
10015
|
+
styleOptionDescription(str) {
|
|
10016
|
+
return this.styleDescriptionText(str);
|
|
10017
|
+
}
|
|
10018
|
+
styleSubcommandDescription(str) {
|
|
10019
|
+
return this.styleDescriptionText(str);
|
|
10020
|
+
}
|
|
10021
|
+
styleArgumentDescription(str) {
|
|
10022
|
+
return this.styleDescriptionText(str);
|
|
10023
|
+
}
|
|
10024
|
+
styleDescriptionText(str) {
|
|
10025
|
+
return str;
|
|
10026
|
+
}
|
|
10027
|
+
styleOptionTerm(str) {
|
|
10028
|
+
return this.styleOptionText(str);
|
|
10029
|
+
}
|
|
10030
|
+
styleSubcommandTerm(str) {
|
|
10031
|
+
return str.split(" ").map((word) => {
|
|
10032
|
+
if (word === "[options]")
|
|
10033
|
+
return this.styleOptionText(word);
|
|
10034
|
+
if (word[0] === "[" || word[0] === "<")
|
|
10035
|
+
return this.styleArgumentText(word);
|
|
10036
|
+
return this.styleSubcommandText(word);
|
|
10037
|
+
}).join(" ");
|
|
10038
|
+
}
|
|
10039
|
+
styleArgumentTerm(str) {
|
|
10040
|
+
return this.styleArgumentText(str);
|
|
10041
|
+
}
|
|
10042
|
+
styleOptionText(str) {
|
|
10043
|
+
return str;
|
|
10044
|
+
}
|
|
10045
|
+
styleArgumentText(str) {
|
|
10046
|
+
return str;
|
|
10047
|
+
}
|
|
10048
|
+
styleSubcommandText(str) {
|
|
10049
|
+
return str;
|
|
10050
|
+
}
|
|
10051
|
+
styleCommandText(str) {
|
|
10052
|
+
return str;
|
|
10053
|
+
}
|
|
10054
|
+
padWidth(cmd, helper) {
|
|
10055
|
+
return Math.max(helper.longestOptionTermLength(cmd, helper), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper));
|
|
10056
|
+
}
|
|
10057
|
+
preformatted(str) {
|
|
10058
|
+
return /\n[^\S\r\n]/.test(str);
|
|
10059
|
+
}
|
|
10060
|
+
formatItem(term, termWidth, description, helper) {
|
|
10061
|
+
const itemIndent = 2;
|
|
10062
|
+
const itemIndentStr = " ".repeat(itemIndent);
|
|
10063
|
+
if (!description)
|
|
10064
|
+
return itemIndentStr + term;
|
|
10065
|
+
const paddedTerm = term.padEnd(termWidth + term.length - helper.displayWidth(term));
|
|
10066
|
+
const spacerWidth = 2;
|
|
10067
|
+
const helpWidth = this.helpWidth ?? 80;
|
|
10068
|
+
const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
|
|
10069
|
+
let formattedDescription;
|
|
10070
|
+
if (remainingWidth < this.minWidthToWrap || helper.preformatted(description)) {
|
|
10071
|
+
formattedDescription = description;
|
|
10072
|
+
} else {
|
|
10073
|
+
const wrappedDescription = helper.boxWrap(description, remainingWidth);
|
|
10074
|
+
formattedDescription = wrappedDescription.replace(/\n/g, `
|
|
10075
|
+
` + " ".repeat(termWidth + spacerWidth));
|
|
10076
|
+
}
|
|
10077
|
+
return itemIndentStr + paddedTerm + " ".repeat(spacerWidth) + formattedDescription.replace(/\n/g, `
|
|
10078
|
+
${itemIndentStr}`);
|
|
10079
|
+
}
|
|
10080
|
+
boxWrap(str, width) {
|
|
10081
|
+
if (width < this.minWidthToWrap)
|
|
10082
|
+
return str;
|
|
10083
|
+
const rawLines = str.split(/\r\n|\n/);
|
|
10084
|
+
const chunkPattern = /[\s]*[^\s]+/g;
|
|
10085
|
+
const wrappedLines = [];
|
|
10086
|
+
rawLines.forEach((line) => {
|
|
10087
|
+
const chunks = line.match(chunkPattern);
|
|
10088
|
+
if (chunks === null) {
|
|
10089
|
+
wrappedLines.push("");
|
|
10090
|
+
return;
|
|
10091
|
+
}
|
|
10092
|
+
let sumChunks = [chunks.shift()];
|
|
10093
|
+
let sumWidth = this.displayWidth(sumChunks[0]);
|
|
10094
|
+
chunks.forEach((chunk2) => {
|
|
10095
|
+
const visibleWidth = this.displayWidth(chunk2);
|
|
10096
|
+
if (sumWidth + visibleWidth <= width) {
|
|
10097
|
+
sumChunks.push(chunk2);
|
|
10098
|
+
sumWidth += visibleWidth;
|
|
10099
|
+
return;
|
|
10100
|
+
}
|
|
10101
|
+
wrappedLines.push(sumChunks.join(""));
|
|
10102
|
+
const nextChunk = chunk2.trimStart();
|
|
10103
|
+
sumChunks = [nextChunk];
|
|
10104
|
+
sumWidth = this.displayWidth(nextChunk);
|
|
10105
|
+
});
|
|
10106
|
+
wrappedLines.push(sumChunks.join(""));
|
|
10107
|
+
});
|
|
10108
|
+
return wrappedLines.join(`
|
|
10109
|
+
`);
|
|
10110
|
+
}
|
|
10111
|
+
}
|
|
10112
|
+
function stripColor(str) {
|
|
10113
|
+
const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
|
|
10114
|
+
return str.replace(sgrPattern, "");
|
|
10115
|
+
}
|
|
10116
|
+
exports2.Help = Help;
|
|
10117
|
+
exports2.stripColor = stripColor;
|
|
10118
|
+
});
|
|
10119
|
+
|
|
10120
|
+
// node_modules/.bun/commander@13.1.0/node_modules/commander/lib/option.js
|
|
10121
|
+
var require_option = __commonJS((exports2) => {
|
|
10122
|
+
var { InvalidArgumentError } = require_error();
|
|
10123
|
+
|
|
10124
|
+
class Option {
|
|
10125
|
+
constructor(flags, description) {
|
|
10126
|
+
this.flags = flags;
|
|
10127
|
+
this.description = description || "";
|
|
10128
|
+
this.required = flags.includes("<");
|
|
10129
|
+
this.optional = flags.includes("[");
|
|
10130
|
+
this.variadic = /\w\.\.\.[>\]]$/.test(flags);
|
|
10131
|
+
this.mandatory = false;
|
|
10132
|
+
const optionFlags = splitOptionFlags(flags);
|
|
10133
|
+
this.short = optionFlags.shortFlag;
|
|
10134
|
+
this.long = optionFlags.longFlag;
|
|
10135
|
+
this.negate = false;
|
|
10136
|
+
if (this.long) {
|
|
10137
|
+
this.negate = this.long.startsWith("--no-");
|
|
10138
|
+
}
|
|
10139
|
+
this.defaultValue = undefined;
|
|
10140
|
+
this.defaultValueDescription = undefined;
|
|
10141
|
+
this.presetArg = undefined;
|
|
10142
|
+
this.envVar = undefined;
|
|
10143
|
+
this.parseArg = undefined;
|
|
10144
|
+
this.hidden = false;
|
|
10145
|
+
this.argChoices = undefined;
|
|
10146
|
+
this.conflictsWith = [];
|
|
10147
|
+
this.implied = undefined;
|
|
10148
|
+
}
|
|
10149
|
+
default(value, description) {
|
|
10150
|
+
this.defaultValue = value;
|
|
10151
|
+
this.defaultValueDescription = description;
|
|
10152
|
+
return this;
|
|
10153
|
+
}
|
|
10154
|
+
preset(arg) {
|
|
10155
|
+
this.presetArg = arg;
|
|
10156
|
+
return this;
|
|
10157
|
+
}
|
|
10158
|
+
conflicts(names) {
|
|
10159
|
+
this.conflictsWith = this.conflictsWith.concat(names);
|
|
10160
|
+
return this;
|
|
10161
|
+
}
|
|
10162
|
+
implies(impliedOptionValues) {
|
|
10163
|
+
let newImplied = impliedOptionValues;
|
|
10164
|
+
if (typeof impliedOptionValues === "string") {
|
|
10165
|
+
newImplied = { [impliedOptionValues]: true };
|
|
10166
|
+
}
|
|
10167
|
+
this.implied = Object.assign(this.implied || {}, newImplied);
|
|
10168
|
+
return this;
|
|
10169
|
+
}
|
|
10170
|
+
env(name) {
|
|
10171
|
+
this.envVar = name;
|
|
10172
|
+
return this;
|
|
10173
|
+
}
|
|
10174
|
+
argParser(fn) {
|
|
10175
|
+
this.parseArg = fn;
|
|
10176
|
+
return this;
|
|
10177
|
+
}
|
|
10178
|
+
makeOptionMandatory(mandatory = true) {
|
|
10179
|
+
this.mandatory = !!mandatory;
|
|
10180
|
+
return this;
|
|
10181
|
+
}
|
|
10182
|
+
hideHelp(hide = true) {
|
|
10183
|
+
this.hidden = !!hide;
|
|
10184
|
+
return this;
|
|
10185
|
+
}
|
|
10186
|
+
_concatValue(value, previous) {
|
|
10187
|
+
if (previous === this.defaultValue || !Array.isArray(previous)) {
|
|
10188
|
+
return [value];
|
|
10189
|
+
}
|
|
10190
|
+
return previous.concat(value);
|
|
10191
|
+
}
|
|
10192
|
+
choices(values) {
|
|
10193
|
+
this.argChoices = values.slice();
|
|
10194
|
+
this.parseArg = (arg, previous) => {
|
|
10195
|
+
if (!this.argChoices.includes(arg)) {
|
|
10196
|
+
throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
|
|
10197
|
+
}
|
|
10198
|
+
if (this.variadic) {
|
|
10199
|
+
return this._concatValue(arg, previous);
|
|
10200
|
+
}
|
|
10201
|
+
return arg;
|
|
10202
|
+
};
|
|
10203
|
+
return this;
|
|
10204
|
+
}
|
|
10205
|
+
name() {
|
|
10206
|
+
if (this.long) {
|
|
10207
|
+
return this.long.replace(/^--/, "");
|
|
10208
|
+
}
|
|
10209
|
+
return this.short.replace(/^-/, "");
|
|
10210
|
+
}
|
|
10211
|
+
attributeName() {
|
|
10212
|
+
if (this.negate) {
|
|
10213
|
+
return camelcase(this.name().replace(/^no-/, ""));
|
|
10214
|
+
}
|
|
10215
|
+
return camelcase(this.name());
|
|
10216
|
+
}
|
|
10217
|
+
is(arg) {
|
|
10218
|
+
return this.short === arg || this.long === arg;
|
|
10219
|
+
}
|
|
10220
|
+
isBoolean() {
|
|
10221
|
+
return !this.required && !this.optional && !this.negate;
|
|
10222
|
+
}
|
|
10223
|
+
}
|
|
10224
|
+
|
|
10225
|
+
class DualOptions {
|
|
10226
|
+
constructor(options) {
|
|
10227
|
+
this.positiveOptions = new Map;
|
|
10228
|
+
this.negativeOptions = new Map;
|
|
10229
|
+
this.dualOptions = new Set;
|
|
10230
|
+
options.forEach((option) => {
|
|
10231
|
+
if (option.negate) {
|
|
10232
|
+
this.negativeOptions.set(option.attributeName(), option);
|
|
10233
|
+
} else {
|
|
10234
|
+
this.positiveOptions.set(option.attributeName(), option);
|
|
10235
|
+
}
|
|
10236
|
+
});
|
|
10237
|
+
this.negativeOptions.forEach((value, key) => {
|
|
10238
|
+
if (this.positiveOptions.has(key)) {
|
|
10239
|
+
this.dualOptions.add(key);
|
|
10240
|
+
}
|
|
10241
|
+
});
|
|
10242
|
+
}
|
|
10243
|
+
valueFromOption(value, option) {
|
|
10244
|
+
const optionKey = option.attributeName();
|
|
10245
|
+
if (!this.dualOptions.has(optionKey))
|
|
10246
|
+
return true;
|
|
10247
|
+
const preset = this.negativeOptions.get(optionKey).presetArg;
|
|
10248
|
+
const negativeValue = preset !== undefined ? preset : false;
|
|
10249
|
+
return option.negate === (negativeValue === value);
|
|
10250
|
+
}
|
|
10251
|
+
}
|
|
10252
|
+
function camelcase(str) {
|
|
10253
|
+
return str.split("-").reduce((str2, word) => {
|
|
10254
|
+
return str2 + word[0].toUpperCase() + word.slice(1);
|
|
10255
|
+
});
|
|
10256
|
+
}
|
|
10257
|
+
function splitOptionFlags(flags) {
|
|
10258
|
+
let shortFlag;
|
|
10259
|
+
let longFlag;
|
|
10260
|
+
const shortFlagExp = /^-[^-]$/;
|
|
10261
|
+
const longFlagExp = /^--[^-]/;
|
|
10262
|
+
const flagParts = flags.split(/[ |,]+/).concat("guard");
|
|
10263
|
+
if (shortFlagExp.test(flagParts[0]))
|
|
10264
|
+
shortFlag = flagParts.shift();
|
|
10265
|
+
if (longFlagExp.test(flagParts[0]))
|
|
10266
|
+
longFlag = flagParts.shift();
|
|
10267
|
+
if (!shortFlag && shortFlagExp.test(flagParts[0]))
|
|
10268
|
+
shortFlag = flagParts.shift();
|
|
10269
|
+
if (!shortFlag && longFlagExp.test(flagParts[0])) {
|
|
10270
|
+
shortFlag = longFlag;
|
|
10271
|
+
longFlag = flagParts.shift();
|
|
10272
|
+
}
|
|
10273
|
+
if (flagParts[0].startsWith("-")) {
|
|
10274
|
+
const unsupportedFlag = flagParts[0];
|
|
10275
|
+
const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`;
|
|
10276
|
+
if (/^-[^-][^-]/.test(unsupportedFlag))
|
|
10277
|
+
throw new Error(`${baseError}
|
|
10278
|
+
- a short flag is a single dash and a single character
|
|
10279
|
+
- either use a single dash and a single character (for a short flag)
|
|
10280
|
+
- or use a double dash for a long option (and can have two, like '--ws, --workspace')`);
|
|
10281
|
+
if (shortFlagExp.test(unsupportedFlag))
|
|
10282
|
+
throw new Error(`${baseError}
|
|
10283
|
+
- too many short flags`);
|
|
10284
|
+
if (longFlagExp.test(unsupportedFlag))
|
|
10285
|
+
throw new Error(`${baseError}
|
|
10286
|
+
- too many long flags`);
|
|
10287
|
+
throw new Error(`${baseError}
|
|
10288
|
+
- unrecognised flag format`);
|
|
10289
|
+
}
|
|
10290
|
+
if (shortFlag === undefined && longFlag === undefined)
|
|
10291
|
+
throw new Error(`option creation failed due to no flags found in '${flags}'.`);
|
|
10292
|
+
return { shortFlag, longFlag };
|
|
10293
|
+
}
|
|
10294
|
+
exports2.Option = Option;
|
|
10295
|
+
exports2.DualOptions = DualOptions;
|
|
10296
|
+
});
|
|
10297
|
+
|
|
10298
|
+
// node_modules/.bun/commander@13.1.0/node_modules/commander/lib/suggestSimilar.js
|
|
10299
|
+
var require_suggestSimilar = __commonJS((exports2) => {
|
|
10300
|
+
var maxDistance = 3;
|
|
10301
|
+
function editDistance(a, b) {
|
|
10302
|
+
if (Math.abs(a.length - b.length) > maxDistance)
|
|
10303
|
+
return Math.max(a.length, b.length);
|
|
10304
|
+
const d = [];
|
|
10305
|
+
for (let i = 0;i <= a.length; i++) {
|
|
10306
|
+
d[i] = [i];
|
|
10307
|
+
}
|
|
10308
|
+
for (let j = 0;j <= b.length; j++) {
|
|
10309
|
+
d[0][j] = j;
|
|
10310
|
+
}
|
|
10311
|
+
for (let j = 1;j <= b.length; j++) {
|
|
10312
|
+
for (let i = 1;i <= a.length; i++) {
|
|
10313
|
+
let cost = 1;
|
|
10314
|
+
if (a[i - 1] === b[j - 1]) {
|
|
10315
|
+
cost = 0;
|
|
10316
|
+
} else {
|
|
10317
|
+
cost = 1;
|
|
10318
|
+
}
|
|
10319
|
+
d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
|
|
10320
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
10321
|
+
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
|
|
10322
|
+
}
|
|
10323
|
+
}
|
|
10324
|
+
}
|
|
10325
|
+
return d[a.length][b.length];
|
|
10326
|
+
}
|
|
10327
|
+
function suggestSimilar(word, candidates) {
|
|
10328
|
+
if (!candidates || candidates.length === 0)
|
|
10329
|
+
return "";
|
|
10330
|
+
candidates = Array.from(new Set(candidates));
|
|
10331
|
+
const searchingOptions = word.startsWith("--");
|
|
10332
|
+
if (searchingOptions) {
|
|
10333
|
+
word = word.slice(2);
|
|
10334
|
+
candidates = candidates.map((candidate) => candidate.slice(2));
|
|
10335
|
+
}
|
|
10336
|
+
let similar = [];
|
|
10337
|
+
let bestDistance = maxDistance;
|
|
10338
|
+
const minSimilarity = 0.4;
|
|
10339
|
+
candidates.forEach((candidate) => {
|
|
10340
|
+
if (candidate.length <= 1)
|
|
10341
|
+
return;
|
|
10342
|
+
const distance = editDistance(word, candidate);
|
|
10343
|
+
const length = Math.max(word.length, candidate.length);
|
|
10344
|
+
const similarity = (length - distance) / length;
|
|
10345
|
+
if (similarity > minSimilarity) {
|
|
10346
|
+
if (distance < bestDistance) {
|
|
10347
|
+
bestDistance = distance;
|
|
10348
|
+
similar = [candidate];
|
|
10349
|
+
} else if (distance === bestDistance) {
|
|
10350
|
+
similar.push(candidate);
|
|
10351
|
+
}
|
|
10352
|
+
}
|
|
10353
|
+
});
|
|
10354
|
+
similar.sort((a, b) => a.localeCompare(b));
|
|
10355
|
+
if (searchingOptions) {
|
|
10356
|
+
similar = similar.map((candidate) => `--${candidate}`);
|
|
10357
|
+
}
|
|
10358
|
+
if (similar.length > 1) {
|
|
10359
|
+
return `
|
|
10360
|
+
(Did you mean one of ${similar.join(", ")}?)`;
|
|
10361
|
+
}
|
|
10362
|
+
if (similar.length === 1) {
|
|
10363
|
+
return `
|
|
10364
|
+
(Did you mean ${similar[0]}?)`;
|
|
10365
|
+
}
|
|
10366
|
+
return "";
|
|
10367
|
+
}
|
|
10368
|
+
exports2.suggestSimilar = suggestSimilar;
|
|
10369
|
+
});
|
|
10370
|
+
|
|
10371
|
+
// node_modules/.bun/commander@13.1.0/node_modules/commander/lib/command.js
|
|
10372
|
+
var require_command = __commonJS((exports2) => {
|
|
10373
|
+
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
10374
|
+
var childProcess = __require("node:child_process");
|
|
10375
|
+
var path2 = __require("node:path");
|
|
10376
|
+
var fs2 = __require("node:fs");
|
|
10377
|
+
var process2 = __require("node:process");
|
|
10378
|
+
var { Argument, humanReadableArgName } = require_argument();
|
|
10379
|
+
var { CommanderError } = require_error();
|
|
10380
|
+
var { Help, stripColor } = require_help();
|
|
10381
|
+
var { Option, DualOptions } = require_option();
|
|
10382
|
+
var { suggestSimilar } = require_suggestSimilar();
|
|
10383
|
+
|
|
10384
|
+
class Command extends EventEmitter2 {
|
|
10385
|
+
constructor(name) {
|
|
10386
|
+
super();
|
|
10387
|
+
this.commands = [];
|
|
10388
|
+
this.options = [];
|
|
10389
|
+
this.parent = null;
|
|
10390
|
+
this._allowUnknownOption = false;
|
|
10391
|
+
this._allowExcessArguments = false;
|
|
10392
|
+
this.registeredArguments = [];
|
|
10393
|
+
this._args = this.registeredArguments;
|
|
10394
|
+
this.args = [];
|
|
10395
|
+
this.rawArgs = [];
|
|
10396
|
+
this.processedArgs = [];
|
|
10397
|
+
this._scriptPath = null;
|
|
10398
|
+
this._name = name || "";
|
|
10399
|
+
this._optionValues = {};
|
|
10400
|
+
this._optionValueSources = {};
|
|
10401
|
+
this._storeOptionsAsProperties = false;
|
|
10402
|
+
this._actionHandler = null;
|
|
10403
|
+
this._executableHandler = false;
|
|
10404
|
+
this._executableFile = null;
|
|
10405
|
+
this._executableDir = null;
|
|
10406
|
+
this._defaultCommandName = null;
|
|
10407
|
+
this._exitCallback = null;
|
|
10408
|
+
this._aliases = [];
|
|
10409
|
+
this._combineFlagAndOptionalValue = true;
|
|
10410
|
+
this._description = "";
|
|
10411
|
+
this._summary = "";
|
|
10412
|
+
this._argsDescription = undefined;
|
|
10413
|
+
this._enablePositionalOptions = false;
|
|
10414
|
+
this._passThroughOptions = false;
|
|
10415
|
+
this._lifeCycleHooks = {};
|
|
10416
|
+
this._showHelpAfterError = false;
|
|
10417
|
+
this._showSuggestionAfterError = true;
|
|
10418
|
+
this._savedState = null;
|
|
10419
|
+
this._outputConfiguration = {
|
|
10420
|
+
writeOut: (str) => process2.stdout.write(str),
|
|
10421
|
+
writeErr: (str) => process2.stderr.write(str),
|
|
10422
|
+
outputError: (str, write) => write(str),
|
|
10423
|
+
getOutHelpWidth: () => process2.stdout.isTTY ? process2.stdout.columns : undefined,
|
|
10424
|
+
getErrHelpWidth: () => process2.stderr.isTTY ? process2.stderr.columns : undefined,
|
|
10425
|
+
getOutHasColors: () => useColor() ?? (process2.stdout.isTTY && process2.stdout.hasColors?.()),
|
|
10426
|
+
getErrHasColors: () => useColor() ?? (process2.stderr.isTTY && process2.stderr.hasColors?.()),
|
|
10427
|
+
stripColor: (str) => stripColor(str)
|
|
10428
|
+
};
|
|
10429
|
+
this._hidden = false;
|
|
10430
|
+
this._helpOption = undefined;
|
|
10431
|
+
this._addImplicitHelpCommand = undefined;
|
|
10432
|
+
this._helpCommand = undefined;
|
|
10433
|
+
this._helpConfiguration = {};
|
|
10434
|
+
}
|
|
10435
|
+
copyInheritedSettings(sourceCommand) {
|
|
10436
|
+
this._outputConfiguration = sourceCommand._outputConfiguration;
|
|
10437
|
+
this._helpOption = sourceCommand._helpOption;
|
|
10438
|
+
this._helpCommand = sourceCommand._helpCommand;
|
|
10439
|
+
this._helpConfiguration = sourceCommand._helpConfiguration;
|
|
10440
|
+
this._exitCallback = sourceCommand._exitCallback;
|
|
10441
|
+
this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties;
|
|
10442
|
+
this._combineFlagAndOptionalValue = sourceCommand._combineFlagAndOptionalValue;
|
|
10443
|
+
this._allowExcessArguments = sourceCommand._allowExcessArguments;
|
|
10444
|
+
this._enablePositionalOptions = sourceCommand._enablePositionalOptions;
|
|
10445
|
+
this._showHelpAfterError = sourceCommand._showHelpAfterError;
|
|
10446
|
+
this._showSuggestionAfterError = sourceCommand._showSuggestionAfterError;
|
|
10447
|
+
return this;
|
|
10448
|
+
}
|
|
10449
|
+
_getCommandAndAncestors() {
|
|
10450
|
+
const result = [];
|
|
10451
|
+
for (let command = this;command; command = command.parent) {
|
|
10452
|
+
result.push(command);
|
|
10453
|
+
}
|
|
10454
|
+
return result;
|
|
10455
|
+
}
|
|
10456
|
+
command(nameAndArgs, actionOptsOrExecDesc, execOpts) {
|
|
10457
|
+
let desc = actionOptsOrExecDesc;
|
|
10458
|
+
let opts = execOpts;
|
|
10459
|
+
if (typeof desc === "object" && desc !== null) {
|
|
10460
|
+
opts = desc;
|
|
10461
|
+
desc = null;
|
|
10462
|
+
}
|
|
10463
|
+
opts = opts || {};
|
|
10464
|
+
const [, name, args] = nameAndArgs.match(/([^ ]+) *(.*)/);
|
|
10465
|
+
const cmd = this.createCommand(name);
|
|
10466
|
+
if (desc) {
|
|
10467
|
+
cmd.description(desc);
|
|
10468
|
+
cmd._executableHandler = true;
|
|
10469
|
+
}
|
|
10470
|
+
if (opts.isDefault)
|
|
10471
|
+
this._defaultCommandName = cmd._name;
|
|
10472
|
+
cmd._hidden = !!(opts.noHelp || opts.hidden);
|
|
10473
|
+
cmd._executableFile = opts.executableFile || null;
|
|
10474
|
+
if (args)
|
|
10475
|
+
cmd.arguments(args);
|
|
10476
|
+
this._registerCommand(cmd);
|
|
10477
|
+
cmd.parent = this;
|
|
10478
|
+
cmd.copyInheritedSettings(this);
|
|
10479
|
+
if (desc)
|
|
10480
|
+
return this;
|
|
10481
|
+
return cmd;
|
|
10482
|
+
}
|
|
10483
|
+
createCommand(name) {
|
|
10484
|
+
return new Command(name);
|
|
10485
|
+
}
|
|
10486
|
+
createHelp() {
|
|
10487
|
+
return Object.assign(new Help, this.configureHelp());
|
|
10488
|
+
}
|
|
10489
|
+
configureHelp(configuration) {
|
|
10490
|
+
if (configuration === undefined)
|
|
10491
|
+
return this._helpConfiguration;
|
|
10492
|
+
this._helpConfiguration = configuration;
|
|
10493
|
+
return this;
|
|
10494
|
+
}
|
|
10495
|
+
configureOutput(configuration) {
|
|
10496
|
+
if (configuration === undefined)
|
|
10497
|
+
return this._outputConfiguration;
|
|
10498
|
+
Object.assign(this._outputConfiguration, configuration);
|
|
10499
|
+
return this;
|
|
10500
|
+
}
|
|
10501
|
+
showHelpAfterError(displayHelp = true) {
|
|
10502
|
+
if (typeof displayHelp !== "string")
|
|
10503
|
+
displayHelp = !!displayHelp;
|
|
10504
|
+
this._showHelpAfterError = displayHelp;
|
|
10505
|
+
return this;
|
|
10506
|
+
}
|
|
10507
|
+
showSuggestionAfterError(displaySuggestion = true) {
|
|
10508
|
+
this._showSuggestionAfterError = !!displaySuggestion;
|
|
10509
|
+
return this;
|
|
10510
|
+
}
|
|
10511
|
+
addCommand(cmd, opts) {
|
|
10512
|
+
if (!cmd._name) {
|
|
10513
|
+
throw new Error(`Command passed to .addCommand() must have a name
|
|
10514
|
+
- specify the name in Command constructor or using .name()`);
|
|
10515
|
+
}
|
|
10516
|
+
opts = opts || {};
|
|
10517
|
+
if (opts.isDefault)
|
|
10518
|
+
this._defaultCommandName = cmd._name;
|
|
10519
|
+
if (opts.noHelp || opts.hidden)
|
|
10520
|
+
cmd._hidden = true;
|
|
10521
|
+
this._registerCommand(cmd);
|
|
10522
|
+
cmd.parent = this;
|
|
10523
|
+
cmd._checkForBrokenPassThrough();
|
|
10524
|
+
return this;
|
|
10525
|
+
}
|
|
10526
|
+
createArgument(name, description) {
|
|
10527
|
+
return new Argument(name, description);
|
|
10528
|
+
}
|
|
10529
|
+
argument(name, description, fn, defaultValue) {
|
|
10530
|
+
const argument = this.createArgument(name, description);
|
|
10531
|
+
if (typeof fn === "function") {
|
|
10532
|
+
argument.default(defaultValue).argParser(fn);
|
|
10533
|
+
} else {
|
|
10534
|
+
argument.default(fn);
|
|
10535
|
+
}
|
|
10536
|
+
this.addArgument(argument);
|
|
10537
|
+
return this;
|
|
10538
|
+
}
|
|
10539
|
+
arguments(names) {
|
|
10540
|
+
names.trim().split(/ +/).forEach((detail) => {
|
|
10541
|
+
this.argument(detail);
|
|
10542
|
+
});
|
|
10543
|
+
return this;
|
|
10544
|
+
}
|
|
10545
|
+
addArgument(argument) {
|
|
10546
|
+
const previousArgument = this.registeredArguments.slice(-1)[0];
|
|
10547
|
+
if (previousArgument && previousArgument.variadic) {
|
|
10548
|
+
throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
|
|
10549
|
+
}
|
|
10550
|
+
if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
|
|
10551
|
+
throw new Error(`a default value for a required argument is never used: '${argument.name()}'`);
|
|
10552
|
+
}
|
|
10553
|
+
this.registeredArguments.push(argument);
|
|
10554
|
+
return this;
|
|
10555
|
+
}
|
|
10556
|
+
helpCommand(enableOrNameAndArgs, description) {
|
|
10557
|
+
if (typeof enableOrNameAndArgs === "boolean") {
|
|
10558
|
+
this._addImplicitHelpCommand = enableOrNameAndArgs;
|
|
10559
|
+
return this;
|
|
10560
|
+
}
|
|
10561
|
+
enableOrNameAndArgs = enableOrNameAndArgs ?? "help [command]";
|
|
10562
|
+
const [, helpName, helpArgs] = enableOrNameAndArgs.match(/([^ ]+) *(.*)/);
|
|
10563
|
+
const helpDescription = description ?? "display help for command";
|
|
10564
|
+
const helpCommand = this.createCommand(helpName);
|
|
10565
|
+
helpCommand.helpOption(false);
|
|
10566
|
+
if (helpArgs)
|
|
10567
|
+
helpCommand.arguments(helpArgs);
|
|
10568
|
+
if (helpDescription)
|
|
10569
|
+
helpCommand.description(helpDescription);
|
|
10570
|
+
this._addImplicitHelpCommand = true;
|
|
10571
|
+
this._helpCommand = helpCommand;
|
|
10572
|
+
return this;
|
|
10573
|
+
}
|
|
10574
|
+
addHelpCommand(helpCommand, deprecatedDescription) {
|
|
10575
|
+
if (typeof helpCommand !== "object") {
|
|
10576
|
+
this.helpCommand(helpCommand, deprecatedDescription);
|
|
10577
|
+
return this;
|
|
10578
|
+
}
|
|
10579
|
+
this._addImplicitHelpCommand = true;
|
|
10580
|
+
this._helpCommand = helpCommand;
|
|
10581
|
+
return this;
|
|
10582
|
+
}
|
|
10583
|
+
_getHelpCommand() {
|
|
10584
|
+
const hasImplicitHelpCommand = this._addImplicitHelpCommand ?? (this.commands.length && !this._actionHandler && !this._findCommand("help"));
|
|
10585
|
+
if (hasImplicitHelpCommand) {
|
|
10586
|
+
if (this._helpCommand === undefined) {
|
|
10587
|
+
this.helpCommand(undefined, undefined);
|
|
10588
|
+
}
|
|
10589
|
+
return this._helpCommand;
|
|
10590
|
+
}
|
|
10591
|
+
return null;
|
|
10592
|
+
}
|
|
10593
|
+
hook(event, listener) {
|
|
10594
|
+
const allowedValues = ["preSubcommand", "preAction", "postAction"];
|
|
10595
|
+
if (!allowedValues.includes(event)) {
|
|
10596
|
+
throw new Error(`Unexpected value for event passed to hook : '${event}'.
|
|
10597
|
+
Expecting one of '${allowedValues.join("', '")}'`);
|
|
10598
|
+
}
|
|
10599
|
+
if (this._lifeCycleHooks[event]) {
|
|
10600
|
+
this._lifeCycleHooks[event].push(listener);
|
|
10601
|
+
} else {
|
|
10602
|
+
this._lifeCycleHooks[event] = [listener];
|
|
10603
|
+
}
|
|
10604
|
+
return this;
|
|
10605
|
+
}
|
|
10606
|
+
exitOverride(fn) {
|
|
10607
|
+
if (fn) {
|
|
10608
|
+
this._exitCallback = fn;
|
|
10609
|
+
} else {
|
|
10610
|
+
this._exitCallback = (err2) => {
|
|
10611
|
+
if (err2.code !== "commander.executeSubCommandAsync") {
|
|
10612
|
+
throw err2;
|
|
10613
|
+
} else {}
|
|
10614
|
+
};
|
|
10615
|
+
}
|
|
10616
|
+
return this;
|
|
10617
|
+
}
|
|
10618
|
+
_exit(exitCode, code, message) {
|
|
10619
|
+
if (this._exitCallback) {
|
|
10620
|
+
this._exitCallback(new CommanderError(exitCode, code, message));
|
|
10621
|
+
}
|
|
10622
|
+
process2.exit(exitCode);
|
|
10623
|
+
}
|
|
10624
|
+
action(fn) {
|
|
10625
|
+
const listener = (args) => {
|
|
10626
|
+
const expectedArgsCount = this.registeredArguments.length;
|
|
10627
|
+
const actionArgs = args.slice(0, expectedArgsCount);
|
|
10628
|
+
if (this._storeOptionsAsProperties) {
|
|
10629
|
+
actionArgs[expectedArgsCount] = this;
|
|
10630
|
+
} else {
|
|
10631
|
+
actionArgs[expectedArgsCount] = this.opts();
|
|
10632
|
+
}
|
|
10633
|
+
actionArgs.push(this);
|
|
10634
|
+
return fn.apply(this, actionArgs);
|
|
10635
|
+
};
|
|
10636
|
+
this._actionHandler = listener;
|
|
10637
|
+
return this;
|
|
10638
|
+
}
|
|
10639
|
+
createOption(flags, description) {
|
|
10640
|
+
return new Option(flags, description);
|
|
10641
|
+
}
|
|
10642
|
+
_callParseArg(target, value, previous, invalidArgumentMessage) {
|
|
10643
|
+
try {
|
|
10644
|
+
return target.parseArg(value, previous);
|
|
10645
|
+
} catch (err2) {
|
|
10646
|
+
if (err2.code === "commander.invalidArgument") {
|
|
10647
|
+
const message = `${invalidArgumentMessage} ${err2.message}`;
|
|
10648
|
+
this.error(message, { exitCode: err2.exitCode, code: err2.code });
|
|
10649
|
+
}
|
|
10650
|
+
throw err2;
|
|
10651
|
+
}
|
|
10652
|
+
}
|
|
10653
|
+
_registerOption(option) {
|
|
10654
|
+
const matchingOption = option.short && this._findOption(option.short) || option.long && this._findOption(option.long);
|
|
10655
|
+
if (matchingOption) {
|
|
10656
|
+
const matchingFlag = option.long && this._findOption(option.long) ? option.long : option.short;
|
|
10657
|
+
throw new Error(`Cannot add option '${option.flags}'${this._name && ` to command '${this._name}'`} due to conflicting flag '${matchingFlag}'
|
|
10658
|
+
- already used by option '${matchingOption.flags}'`);
|
|
10659
|
+
}
|
|
10660
|
+
this.options.push(option);
|
|
10661
|
+
}
|
|
10662
|
+
_registerCommand(command) {
|
|
10663
|
+
const knownBy = (cmd) => {
|
|
10664
|
+
return [cmd.name()].concat(cmd.aliases());
|
|
10665
|
+
};
|
|
10666
|
+
const alreadyUsed = knownBy(command).find((name) => this._findCommand(name));
|
|
10667
|
+
if (alreadyUsed) {
|
|
10668
|
+
const existingCmd = knownBy(this._findCommand(alreadyUsed)).join("|");
|
|
10669
|
+
const newCmd = knownBy(command).join("|");
|
|
10670
|
+
throw new Error(`cannot add command '${newCmd}' as already have command '${existingCmd}'`);
|
|
10671
|
+
}
|
|
10672
|
+
this.commands.push(command);
|
|
10673
|
+
}
|
|
10674
|
+
addOption(option) {
|
|
10675
|
+
this._registerOption(option);
|
|
10676
|
+
const oname = option.name();
|
|
10677
|
+
const name = option.attributeName();
|
|
10678
|
+
if (option.negate) {
|
|
10679
|
+
const positiveLongFlag = option.long.replace(/^--no-/, "--");
|
|
10680
|
+
if (!this._findOption(positiveLongFlag)) {
|
|
10681
|
+
this.setOptionValueWithSource(name, option.defaultValue === undefined ? true : option.defaultValue, "default");
|
|
10682
|
+
}
|
|
10683
|
+
} else if (option.defaultValue !== undefined) {
|
|
10684
|
+
this.setOptionValueWithSource(name, option.defaultValue, "default");
|
|
10685
|
+
}
|
|
10686
|
+
const handleOptionValue = (val, invalidValueMessage, valueSource) => {
|
|
10687
|
+
if (val == null && option.presetArg !== undefined) {
|
|
10688
|
+
val = option.presetArg;
|
|
10689
|
+
}
|
|
10690
|
+
const oldValue = this.getOptionValue(name);
|
|
10691
|
+
if (val !== null && option.parseArg) {
|
|
10692
|
+
val = this._callParseArg(option, val, oldValue, invalidValueMessage);
|
|
10693
|
+
} else if (val !== null && option.variadic) {
|
|
10694
|
+
val = option._concatValue(val, oldValue);
|
|
10695
|
+
}
|
|
10696
|
+
if (val == null) {
|
|
10697
|
+
if (option.negate) {
|
|
10698
|
+
val = false;
|
|
10699
|
+
} else if (option.isBoolean() || option.optional) {
|
|
10700
|
+
val = true;
|
|
10701
|
+
} else {
|
|
10702
|
+
val = "";
|
|
10703
|
+
}
|
|
10704
|
+
}
|
|
10705
|
+
this.setOptionValueWithSource(name, val, valueSource);
|
|
10706
|
+
};
|
|
10707
|
+
this.on("option:" + oname, (val) => {
|
|
10708
|
+
const invalidValueMessage = `error: option '${option.flags}' argument '${val}' is invalid.`;
|
|
10709
|
+
handleOptionValue(val, invalidValueMessage, "cli");
|
|
10710
|
+
});
|
|
10711
|
+
if (option.envVar) {
|
|
10712
|
+
this.on("optionEnv:" + oname, (val) => {
|
|
10713
|
+
const invalidValueMessage = `error: option '${option.flags}' value '${val}' from env '${option.envVar}' is invalid.`;
|
|
10714
|
+
handleOptionValue(val, invalidValueMessage, "env");
|
|
10715
|
+
});
|
|
10716
|
+
}
|
|
10717
|
+
return this;
|
|
10718
|
+
}
|
|
10719
|
+
_optionEx(config, flags, description, fn, defaultValue) {
|
|
10720
|
+
if (typeof flags === "object" && flags instanceof Option) {
|
|
10721
|
+
throw new Error("To add an Option object use addOption() instead of option() or requiredOption()");
|
|
10722
|
+
}
|
|
10723
|
+
const option = this.createOption(flags, description);
|
|
10724
|
+
option.makeOptionMandatory(!!config.mandatory);
|
|
10725
|
+
if (typeof fn === "function") {
|
|
10726
|
+
option.default(defaultValue).argParser(fn);
|
|
10727
|
+
} else if (fn instanceof RegExp) {
|
|
10728
|
+
const regex = fn;
|
|
10729
|
+
fn = (val, def) => {
|
|
10730
|
+
const m = regex.exec(val);
|
|
10731
|
+
return m ? m[0] : def;
|
|
10732
|
+
};
|
|
10733
|
+
option.default(defaultValue).argParser(fn);
|
|
10734
|
+
} else {
|
|
10735
|
+
option.default(fn);
|
|
10736
|
+
}
|
|
10737
|
+
return this.addOption(option);
|
|
10738
|
+
}
|
|
10739
|
+
option(flags, description, parseArg, defaultValue) {
|
|
10740
|
+
return this._optionEx({}, flags, description, parseArg, defaultValue);
|
|
10741
|
+
}
|
|
10742
|
+
requiredOption(flags, description, parseArg, defaultValue) {
|
|
10743
|
+
return this._optionEx({ mandatory: true }, flags, description, parseArg, defaultValue);
|
|
10744
|
+
}
|
|
10745
|
+
combineFlagAndOptionalValue(combine = true) {
|
|
10746
|
+
this._combineFlagAndOptionalValue = !!combine;
|
|
10747
|
+
return this;
|
|
10748
|
+
}
|
|
10749
|
+
allowUnknownOption(allowUnknown = true) {
|
|
10750
|
+
this._allowUnknownOption = !!allowUnknown;
|
|
10751
|
+
return this;
|
|
10752
|
+
}
|
|
10753
|
+
allowExcessArguments(allowExcess = true) {
|
|
10754
|
+
this._allowExcessArguments = !!allowExcess;
|
|
10755
|
+
return this;
|
|
10756
|
+
}
|
|
10757
|
+
enablePositionalOptions(positional = true) {
|
|
10758
|
+
this._enablePositionalOptions = !!positional;
|
|
10759
|
+
return this;
|
|
10760
|
+
}
|
|
10761
|
+
passThroughOptions(passThrough2 = true) {
|
|
10762
|
+
this._passThroughOptions = !!passThrough2;
|
|
10763
|
+
this._checkForBrokenPassThrough();
|
|
10764
|
+
return this;
|
|
10765
|
+
}
|
|
10766
|
+
_checkForBrokenPassThrough() {
|
|
10767
|
+
if (this.parent && this._passThroughOptions && !this.parent._enablePositionalOptions) {
|
|
10768
|
+
throw new Error(`passThroughOptions cannot be used for '${this._name}' without turning on enablePositionalOptions for parent command(s)`);
|
|
10769
|
+
}
|
|
10770
|
+
}
|
|
10771
|
+
storeOptionsAsProperties(storeAsProperties = true) {
|
|
10772
|
+
if (this.options.length) {
|
|
10773
|
+
throw new Error("call .storeOptionsAsProperties() before adding options");
|
|
10774
|
+
}
|
|
10775
|
+
if (Object.keys(this._optionValues).length) {
|
|
10776
|
+
throw new Error("call .storeOptionsAsProperties() before setting option values");
|
|
10777
|
+
}
|
|
10778
|
+
this._storeOptionsAsProperties = !!storeAsProperties;
|
|
10779
|
+
return this;
|
|
10780
|
+
}
|
|
10781
|
+
getOptionValue(key) {
|
|
10782
|
+
if (this._storeOptionsAsProperties) {
|
|
10783
|
+
return this[key];
|
|
10784
|
+
}
|
|
10785
|
+
return this._optionValues[key];
|
|
10786
|
+
}
|
|
10787
|
+
setOptionValue(key, value) {
|
|
10788
|
+
return this.setOptionValueWithSource(key, value, undefined);
|
|
10789
|
+
}
|
|
10790
|
+
setOptionValueWithSource(key, value, source) {
|
|
10791
|
+
if (this._storeOptionsAsProperties) {
|
|
10792
|
+
this[key] = value;
|
|
10793
|
+
} else {
|
|
10794
|
+
this._optionValues[key] = value;
|
|
10795
|
+
}
|
|
10796
|
+
this._optionValueSources[key] = source;
|
|
10797
|
+
return this;
|
|
10798
|
+
}
|
|
10799
|
+
getOptionValueSource(key) {
|
|
10800
|
+
return this._optionValueSources[key];
|
|
10801
|
+
}
|
|
10802
|
+
getOptionValueSourceWithGlobals(key) {
|
|
10803
|
+
let source;
|
|
10804
|
+
this._getCommandAndAncestors().forEach((cmd) => {
|
|
10805
|
+
if (cmd.getOptionValueSource(key) !== undefined) {
|
|
10806
|
+
source = cmd.getOptionValueSource(key);
|
|
10807
|
+
}
|
|
10808
|
+
});
|
|
10809
|
+
return source;
|
|
10810
|
+
}
|
|
10811
|
+
_prepareUserArgs(argv, parseOptions) {
|
|
10812
|
+
if (argv !== undefined && !Array.isArray(argv)) {
|
|
10813
|
+
throw new Error("first parameter to parse must be array or undefined");
|
|
10814
|
+
}
|
|
10815
|
+
parseOptions = parseOptions || {};
|
|
10816
|
+
if (argv === undefined && parseOptions.from === undefined) {
|
|
10817
|
+
if (process2.versions?.electron) {
|
|
10818
|
+
parseOptions.from = "electron";
|
|
10819
|
+
}
|
|
10820
|
+
const execArgv = process2.execArgv ?? [];
|
|
10821
|
+
if (execArgv.includes("-e") || execArgv.includes("--eval") || execArgv.includes("-p") || execArgv.includes("--print")) {
|
|
10822
|
+
parseOptions.from = "eval";
|
|
10823
|
+
}
|
|
10824
|
+
}
|
|
10825
|
+
if (argv === undefined) {
|
|
10826
|
+
argv = process2.argv;
|
|
10827
|
+
}
|
|
10828
|
+
this.rawArgs = argv.slice();
|
|
10829
|
+
let userArgs;
|
|
10830
|
+
switch (parseOptions.from) {
|
|
10831
|
+
case undefined:
|
|
10832
|
+
case "node":
|
|
10833
|
+
this._scriptPath = argv[1];
|
|
10834
|
+
userArgs = argv.slice(2);
|
|
10835
|
+
break;
|
|
10836
|
+
case "electron":
|
|
10837
|
+
if (process2.defaultApp) {
|
|
10838
|
+
this._scriptPath = argv[1];
|
|
10839
|
+
userArgs = argv.slice(2);
|
|
10840
|
+
} else {
|
|
10841
|
+
userArgs = argv.slice(1);
|
|
10842
|
+
}
|
|
10843
|
+
break;
|
|
10844
|
+
case "user":
|
|
10845
|
+
userArgs = argv.slice(0);
|
|
10846
|
+
break;
|
|
10847
|
+
case "eval":
|
|
10848
|
+
userArgs = argv.slice(1);
|
|
10849
|
+
break;
|
|
10850
|
+
default:
|
|
10851
|
+
throw new Error(`unexpected parse option { from: '${parseOptions.from}' }`);
|
|
10852
|
+
}
|
|
10853
|
+
if (!this._name && this._scriptPath)
|
|
10854
|
+
this.nameFromFilename(this._scriptPath);
|
|
10855
|
+
this._name = this._name || "program";
|
|
10856
|
+
return userArgs;
|
|
10857
|
+
}
|
|
10858
|
+
parse(argv, parseOptions) {
|
|
10859
|
+
this._prepareForParse();
|
|
10860
|
+
const userArgs = this._prepareUserArgs(argv, parseOptions);
|
|
10861
|
+
this._parseCommand([], userArgs);
|
|
10862
|
+
return this;
|
|
10863
|
+
}
|
|
10864
|
+
async parseAsync(argv, parseOptions) {
|
|
10865
|
+
this._prepareForParse();
|
|
10866
|
+
const userArgs = this._prepareUserArgs(argv, parseOptions);
|
|
10867
|
+
await this._parseCommand([], userArgs);
|
|
10868
|
+
return this;
|
|
10869
|
+
}
|
|
10870
|
+
_prepareForParse() {
|
|
10871
|
+
if (this._savedState === null) {
|
|
10872
|
+
this.saveStateBeforeParse();
|
|
10873
|
+
} else {
|
|
10874
|
+
this.restoreStateBeforeParse();
|
|
10875
|
+
}
|
|
10876
|
+
}
|
|
10877
|
+
saveStateBeforeParse() {
|
|
10878
|
+
this._savedState = {
|
|
10879
|
+
_name: this._name,
|
|
10880
|
+
_optionValues: { ...this._optionValues },
|
|
10881
|
+
_optionValueSources: { ...this._optionValueSources }
|
|
10882
|
+
};
|
|
10883
|
+
}
|
|
10884
|
+
restoreStateBeforeParse() {
|
|
10885
|
+
if (this._storeOptionsAsProperties)
|
|
10886
|
+
throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
|
|
10887
|
+
- either make a new Command for each call to parse, or stop storing options as properties`);
|
|
10888
|
+
this._name = this._savedState._name;
|
|
10889
|
+
this._scriptPath = null;
|
|
10890
|
+
this.rawArgs = [];
|
|
10891
|
+
this._optionValues = { ...this._savedState._optionValues };
|
|
10892
|
+
this._optionValueSources = { ...this._savedState._optionValueSources };
|
|
10893
|
+
this.args = [];
|
|
10894
|
+
this.processedArgs = [];
|
|
10895
|
+
}
|
|
10896
|
+
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
10897
|
+
if (fs2.existsSync(executableFile))
|
|
10898
|
+
return;
|
|
10899
|
+
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
10900
|
+
const executableMissing = `'${executableFile}' does not exist
|
|
10901
|
+
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
10902
|
+
- if the default executable name is not suitable, use the executableFile option to supply a custom name or path
|
|
10903
|
+
- ${executableDirMessage}`;
|
|
10904
|
+
throw new Error(executableMissing);
|
|
10905
|
+
}
|
|
10906
|
+
_executeSubCommand(subcommand, args) {
|
|
10907
|
+
args = args.slice();
|
|
10908
|
+
let launchWithNode = false;
|
|
10909
|
+
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
10910
|
+
function findFile(baseDir, baseName) {
|
|
10911
|
+
const localBin = path2.resolve(baseDir, baseName);
|
|
10912
|
+
if (fs2.existsSync(localBin))
|
|
10913
|
+
return localBin;
|
|
10914
|
+
if (sourceExt.includes(path2.extname(baseName)))
|
|
10915
|
+
return;
|
|
10916
|
+
const foundExt = sourceExt.find((ext) => fs2.existsSync(`${localBin}${ext}`));
|
|
10917
|
+
if (foundExt)
|
|
10918
|
+
return `${localBin}${foundExt}`;
|
|
10919
|
+
return;
|
|
10920
|
+
}
|
|
10921
|
+
this._checkForMissingMandatoryOptions();
|
|
10922
|
+
this._checkForConflictingOptions();
|
|
10923
|
+
let executableFile = subcommand._executableFile || `${this._name}-${subcommand._name}`;
|
|
10924
|
+
let executableDir = this._executableDir || "";
|
|
10925
|
+
if (this._scriptPath) {
|
|
10926
|
+
let resolvedScriptPath;
|
|
10927
|
+
try {
|
|
10928
|
+
resolvedScriptPath = fs2.realpathSync(this._scriptPath);
|
|
10929
|
+
} catch {
|
|
10930
|
+
resolvedScriptPath = this._scriptPath;
|
|
10931
|
+
}
|
|
10932
|
+
executableDir = path2.resolve(path2.dirname(resolvedScriptPath), executableDir);
|
|
10933
|
+
}
|
|
10934
|
+
if (executableDir) {
|
|
10935
|
+
let localFile = findFile(executableDir, executableFile);
|
|
10936
|
+
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
10937
|
+
const legacyName = path2.basename(this._scriptPath, path2.extname(this._scriptPath));
|
|
10938
|
+
if (legacyName !== this._name) {
|
|
10939
|
+
localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
|
|
10940
|
+
}
|
|
10941
|
+
}
|
|
10942
|
+
executableFile = localFile || executableFile;
|
|
10943
|
+
}
|
|
10944
|
+
launchWithNode = sourceExt.includes(path2.extname(executableFile));
|
|
10945
|
+
let proc;
|
|
10946
|
+
if (process2.platform !== "win32") {
|
|
10947
|
+
if (launchWithNode) {
|
|
10948
|
+
args.unshift(executableFile);
|
|
10949
|
+
args = incrementNodeInspectorPort(process2.execArgv).concat(args);
|
|
10950
|
+
proc = childProcess.spawn(process2.argv[0], args, { stdio: "inherit" });
|
|
10951
|
+
} else {
|
|
10952
|
+
proc = childProcess.spawn(executableFile, args, { stdio: "inherit" });
|
|
10953
|
+
}
|
|
10954
|
+
} else {
|
|
10955
|
+
this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
|
|
10956
|
+
args.unshift(executableFile);
|
|
10957
|
+
args = incrementNodeInspectorPort(process2.execArgv).concat(args);
|
|
10958
|
+
proc = childProcess.spawn(process2.execPath, args, { stdio: "inherit" });
|
|
10959
|
+
}
|
|
10960
|
+
if (!proc.killed) {
|
|
10961
|
+
const signals = ["SIGUSR1", "SIGUSR2", "SIGTERM", "SIGINT", "SIGHUP"];
|
|
10962
|
+
signals.forEach((signal) => {
|
|
10963
|
+
process2.on(signal, () => {
|
|
10964
|
+
if (proc.killed === false && proc.exitCode === null) {
|
|
10965
|
+
proc.kill(signal);
|
|
10966
|
+
}
|
|
10967
|
+
});
|
|
10968
|
+
});
|
|
10969
|
+
}
|
|
10970
|
+
const exitCallback = this._exitCallback;
|
|
10971
|
+
proc.on("close", (code) => {
|
|
10972
|
+
code = code ?? 1;
|
|
10973
|
+
if (!exitCallback) {
|
|
10974
|
+
process2.exit(code);
|
|
10975
|
+
} else {
|
|
10976
|
+
exitCallback(new CommanderError(code, "commander.executeSubCommandAsync", "(close)"));
|
|
10977
|
+
}
|
|
10978
|
+
});
|
|
10979
|
+
proc.on("error", (err2) => {
|
|
10980
|
+
if (err2.code === "ENOENT") {
|
|
10981
|
+
this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
|
|
10982
|
+
} else if (err2.code === "EACCES") {
|
|
10983
|
+
throw new Error(`'${executableFile}' not executable`);
|
|
10984
|
+
}
|
|
10985
|
+
if (!exitCallback) {
|
|
10986
|
+
process2.exit(1);
|
|
10987
|
+
} else {
|
|
10988
|
+
const wrappedError = new CommanderError(1, "commander.executeSubCommandAsync", "(error)");
|
|
10989
|
+
wrappedError.nestedError = err2;
|
|
10990
|
+
exitCallback(wrappedError);
|
|
10991
|
+
}
|
|
10992
|
+
});
|
|
10993
|
+
this.runningCommand = proc;
|
|
10994
|
+
}
|
|
10995
|
+
_dispatchSubcommand(commandName, operands, unknown) {
|
|
10996
|
+
const subCommand = this._findCommand(commandName);
|
|
10997
|
+
if (!subCommand)
|
|
10998
|
+
this.help({ error: true });
|
|
10999
|
+
subCommand._prepareForParse();
|
|
11000
|
+
let promiseChain;
|
|
11001
|
+
promiseChain = this._chainOrCallSubCommandHook(promiseChain, subCommand, "preSubcommand");
|
|
11002
|
+
promiseChain = this._chainOrCall(promiseChain, () => {
|
|
11003
|
+
if (subCommand._executableHandler) {
|
|
11004
|
+
this._executeSubCommand(subCommand, operands.concat(unknown));
|
|
11005
|
+
} else {
|
|
11006
|
+
return subCommand._parseCommand(operands, unknown);
|
|
11007
|
+
}
|
|
11008
|
+
});
|
|
11009
|
+
return promiseChain;
|
|
11010
|
+
}
|
|
11011
|
+
_dispatchHelpCommand(subcommandName) {
|
|
11012
|
+
if (!subcommandName) {
|
|
11013
|
+
this.help();
|
|
11014
|
+
}
|
|
11015
|
+
const subCommand = this._findCommand(subcommandName);
|
|
11016
|
+
if (subCommand && !subCommand._executableHandler) {
|
|
11017
|
+
subCommand.help();
|
|
11018
|
+
}
|
|
11019
|
+
return this._dispatchSubcommand(subcommandName, [], [this._getHelpOption()?.long ?? this._getHelpOption()?.short ?? "--help"]);
|
|
11020
|
+
}
|
|
11021
|
+
_checkNumberOfArguments() {
|
|
11022
|
+
this.registeredArguments.forEach((arg, i) => {
|
|
11023
|
+
if (arg.required && this.args[i] == null) {
|
|
11024
|
+
this.missingArgument(arg.name());
|
|
11025
|
+
}
|
|
11026
|
+
});
|
|
11027
|
+
if (this.registeredArguments.length > 0 && this.registeredArguments[this.registeredArguments.length - 1].variadic) {
|
|
11028
|
+
return;
|
|
11029
|
+
}
|
|
11030
|
+
if (this.args.length > this.registeredArguments.length) {
|
|
11031
|
+
this._excessArguments(this.args);
|
|
11032
|
+
}
|
|
11033
|
+
}
|
|
11034
|
+
_processArguments() {
|
|
11035
|
+
const myParseArg = (argument, value, previous) => {
|
|
11036
|
+
let parsedValue = value;
|
|
11037
|
+
if (value !== null && argument.parseArg) {
|
|
11038
|
+
const invalidValueMessage = `error: command-argument value '${value}' is invalid for argument '${argument.name()}'.`;
|
|
11039
|
+
parsedValue = this._callParseArg(argument, value, previous, invalidValueMessage);
|
|
11040
|
+
}
|
|
11041
|
+
return parsedValue;
|
|
11042
|
+
};
|
|
11043
|
+
this._checkNumberOfArguments();
|
|
11044
|
+
const processedArgs = [];
|
|
11045
|
+
this.registeredArguments.forEach((declaredArg, index) => {
|
|
11046
|
+
let value = declaredArg.defaultValue;
|
|
11047
|
+
if (declaredArg.variadic) {
|
|
11048
|
+
if (index < this.args.length) {
|
|
11049
|
+
value = this.args.slice(index);
|
|
11050
|
+
if (declaredArg.parseArg) {
|
|
11051
|
+
value = value.reduce((processed, v) => {
|
|
11052
|
+
return myParseArg(declaredArg, v, processed);
|
|
11053
|
+
}, declaredArg.defaultValue);
|
|
11054
|
+
}
|
|
11055
|
+
} else if (value === undefined) {
|
|
11056
|
+
value = [];
|
|
11057
|
+
}
|
|
11058
|
+
} else if (index < this.args.length) {
|
|
11059
|
+
value = this.args[index];
|
|
11060
|
+
if (declaredArg.parseArg) {
|
|
11061
|
+
value = myParseArg(declaredArg, value, declaredArg.defaultValue);
|
|
11062
|
+
}
|
|
11063
|
+
}
|
|
11064
|
+
processedArgs[index] = value;
|
|
11065
|
+
});
|
|
11066
|
+
this.processedArgs = processedArgs;
|
|
11067
|
+
}
|
|
11068
|
+
_chainOrCall(promise, fn) {
|
|
11069
|
+
if (promise && promise.then && typeof promise.then === "function") {
|
|
11070
|
+
return promise.then(() => fn());
|
|
11071
|
+
}
|
|
11072
|
+
return fn();
|
|
11073
|
+
}
|
|
11074
|
+
_chainOrCallHooks(promise, event) {
|
|
11075
|
+
let result = promise;
|
|
11076
|
+
const hooks = [];
|
|
11077
|
+
this._getCommandAndAncestors().reverse().filter((cmd) => cmd._lifeCycleHooks[event] !== undefined).forEach((hookedCommand) => {
|
|
11078
|
+
hookedCommand._lifeCycleHooks[event].forEach((callback) => {
|
|
11079
|
+
hooks.push({ hookedCommand, callback });
|
|
11080
|
+
});
|
|
11081
|
+
});
|
|
11082
|
+
if (event === "postAction") {
|
|
11083
|
+
hooks.reverse();
|
|
11084
|
+
}
|
|
11085
|
+
hooks.forEach((hookDetail) => {
|
|
11086
|
+
result = this._chainOrCall(result, () => {
|
|
11087
|
+
return hookDetail.callback(hookDetail.hookedCommand, this);
|
|
11088
|
+
});
|
|
11089
|
+
});
|
|
11090
|
+
return result;
|
|
11091
|
+
}
|
|
11092
|
+
_chainOrCallSubCommandHook(promise, subCommand, event) {
|
|
11093
|
+
let result = promise;
|
|
11094
|
+
if (this._lifeCycleHooks[event] !== undefined) {
|
|
11095
|
+
this._lifeCycleHooks[event].forEach((hook) => {
|
|
11096
|
+
result = this._chainOrCall(result, () => {
|
|
11097
|
+
return hook(this, subCommand);
|
|
11098
|
+
});
|
|
11099
|
+
});
|
|
11100
|
+
}
|
|
11101
|
+
return result;
|
|
11102
|
+
}
|
|
11103
|
+
_parseCommand(operands, unknown) {
|
|
11104
|
+
const parsed = this.parseOptions(unknown);
|
|
11105
|
+
this._parseOptionsEnv();
|
|
11106
|
+
this._parseOptionsImplied();
|
|
11107
|
+
operands = operands.concat(parsed.operands);
|
|
11108
|
+
unknown = parsed.unknown;
|
|
11109
|
+
this.args = operands.concat(unknown);
|
|
11110
|
+
if (operands && this._findCommand(operands[0])) {
|
|
11111
|
+
return this._dispatchSubcommand(operands[0], operands.slice(1), unknown);
|
|
11112
|
+
}
|
|
11113
|
+
if (this._getHelpCommand() && operands[0] === this._getHelpCommand().name()) {
|
|
11114
|
+
return this._dispatchHelpCommand(operands[1]);
|
|
11115
|
+
}
|
|
11116
|
+
if (this._defaultCommandName) {
|
|
11117
|
+
this._outputHelpIfRequested(unknown);
|
|
11118
|
+
return this._dispatchSubcommand(this._defaultCommandName, operands, unknown);
|
|
11119
|
+
}
|
|
11120
|
+
if (this.commands.length && this.args.length === 0 && !this._actionHandler && !this._defaultCommandName) {
|
|
11121
|
+
this.help({ error: true });
|
|
11122
|
+
}
|
|
11123
|
+
this._outputHelpIfRequested(parsed.unknown);
|
|
11124
|
+
this._checkForMissingMandatoryOptions();
|
|
11125
|
+
this._checkForConflictingOptions();
|
|
11126
|
+
const checkForUnknownOptions = () => {
|
|
11127
|
+
if (parsed.unknown.length > 0) {
|
|
11128
|
+
this.unknownOption(parsed.unknown[0]);
|
|
11129
|
+
}
|
|
11130
|
+
};
|
|
11131
|
+
const commandEvent = `command:${this.name()}`;
|
|
11132
|
+
if (this._actionHandler) {
|
|
11133
|
+
checkForUnknownOptions();
|
|
11134
|
+
this._processArguments();
|
|
11135
|
+
let promiseChain;
|
|
11136
|
+
promiseChain = this._chainOrCallHooks(promiseChain, "preAction");
|
|
11137
|
+
promiseChain = this._chainOrCall(promiseChain, () => this._actionHandler(this.processedArgs));
|
|
11138
|
+
if (this.parent) {
|
|
11139
|
+
promiseChain = this._chainOrCall(promiseChain, () => {
|
|
11140
|
+
this.parent.emit(commandEvent, operands, unknown);
|
|
11141
|
+
});
|
|
11142
|
+
}
|
|
11143
|
+
promiseChain = this._chainOrCallHooks(promiseChain, "postAction");
|
|
11144
|
+
return promiseChain;
|
|
11145
|
+
}
|
|
11146
|
+
if (this.parent && this.parent.listenerCount(commandEvent)) {
|
|
11147
|
+
checkForUnknownOptions();
|
|
11148
|
+
this._processArguments();
|
|
11149
|
+
this.parent.emit(commandEvent, operands, unknown);
|
|
11150
|
+
} else if (operands.length) {
|
|
11151
|
+
if (this._findCommand("*")) {
|
|
11152
|
+
return this._dispatchSubcommand("*", operands, unknown);
|
|
11153
|
+
}
|
|
11154
|
+
if (this.listenerCount("command:*")) {
|
|
11155
|
+
this.emit("command:*", operands, unknown);
|
|
11156
|
+
} else if (this.commands.length) {
|
|
11157
|
+
this.unknownCommand();
|
|
11158
|
+
} else {
|
|
11159
|
+
checkForUnknownOptions();
|
|
11160
|
+
this._processArguments();
|
|
11161
|
+
}
|
|
11162
|
+
} else if (this.commands.length) {
|
|
11163
|
+
checkForUnknownOptions();
|
|
11164
|
+
this.help({ error: true });
|
|
11165
|
+
} else {
|
|
11166
|
+
checkForUnknownOptions();
|
|
11167
|
+
this._processArguments();
|
|
11168
|
+
}
|
|
11169
|
+
}
|
|
11170
|
+
_findCommand(name) {
|
|
11171
|
+
if (!name)
|
|
11172
|
+
return;
|
|
11173
|
+
return this.commands.find((cmd) => cmd._name === name || cmd._aliases.includes(name));
|
|
11174
|
+
}
|
|
11175
|
+
_findOption(arg) {
|
|
11176
|
+
return this.options.find((option) => option.is(arg));
|
|
11177
|
+
}
|
|
11178
|
+
_checkForMissingMandatoryOptions() {
|
|
11179
|
+
this._getCommandAndAncestors().forEach((cmd) => {
|
|
11180
|
+
cmd.options.forEach((anOption) => {
|
|
11181
|
+
if (anOption.mandatory && cmd.getOptionValue(anOption.attributeName()) === undefined) {
|
|
11182
|
+
cmd.missingMandatoryOptionValue(anOption);
|
|
11183
|
+
}
|
|
11184
|
+
});
|
|
11185
|
+
});
|
|
11186
|
+
}
|
|
11187
|
+
_checkForConflictingLocalOptions() {
|
|
11188
|
+
const definedNonDefaultOptions = this.options.filter((option) => {
|
|
11189
|
+
const optionKey = option.attributeName();
|
|
11190
|
+
if (this.getOptionValue(optionKey) === undefined) {
|
|
11191
|
+
return false;
|
|
11192
|
+
}
|
|
11193
|
+
return this.getOptionValueSource(optionKey) !== "default";
|
|
11194
|
+
});
|
|
11195
|
+
const optionsWithConflicting = definedNonDefaultOptions.filter((option) => option.conflictsWith.length > 0);
|
|
11196
|
+
optionsWithConflicting.forEach((option) => {
|
|
11197
|
+
const conflictingAndDefined = definedNonDefaultOptions.find((defined) => option.conflictsWith.includes(defined.attributeName()));
|
|
11198
|
+
if (conflictingAndDefined) {
|
|
11199
|
+
this._conflictingOption(option, conflictingAndDefined);
|
|
11200
|
+
}
|
|
11201
|
+
});
|
|
11202
|
+
}
|
|
11203
|
+
_checkForConflictingOptions() {
|
|
11204
|
+
this._getCommandAndAncestors().forEach((cmd) => {
|
|
11205
|
+
cmd._checkForConflictingLocalOptions();
|
|
11206
|
+
});
|
|
11207
|
+
}
|
|
11208
|
+
parseOptions(argv) {
|
|
11209
|
+
const operands = [];
|
|
11210
|
+
const unknown = [];
|
|
11211
|
+
let dest = operands;
|
|
11212
|
+
const args = argv.slice();
|
|
11213
|
+
function maybeOption(arg) {
|
|
11214
|
+
return arg.length > 1 && arg[0] === "-";
|
|
11215
|
+
}
|
|
11216
|
+
let activeVariadicOption = null;
|
|
11217
|
+
while (args.length) {
|
|
11218
|
+
const arg = args.shift();
|
|
11219
|
+
if (arg === "--") {
|
|
11220
|
+
if (dest === unknown)
|
|
11221
|
+
dest.push(arg);
|
|
11222
|
+
dest.push(...args);
|
|
11223
|
+
break;
|
|
11224
|
+
}
|
|
11225
|
+
if (activeVariadicOption && !maybeOption(arg)) {
|
|
11226
|
+
this.emit(`option:${activeVariadicOption.name()}`, arg);
|
|
11227
|
+
continue;
|
|
11228
|
+
}
|
|
11229
|
+
activeVariadicOption = null;
|
|
11230
|
+
if (maybeOption(arg)) {
|
|
11231
|
+
const option = this._findOption(arg);
|
|
11232
|
+
if (option) {
|
|
11233
|
+
if (option.required) {
|
|
11234
|
+
const value = args.shift();
|
|
11235
|
+
if (value === undefined)
|
|
11236
|
+
this.optionMissingArgument(option);
|
|
11237
|
+
this.emit(`option:${option.name()}`, value);
|
|
11238
|
+
} else if (option.optional) {
|
|
11239
|
+
let value = null;
|
|
11240
|
+
if (args.length > 0 && !maybeOption(args[0])) {
|
|
11241
|
+
value = args.shift();
|
|
11242
|
+
}
|
|
11243
|
+
this.emit(`option:${option.name()}`, value);
|
|
11244
|
+
} else {
|
|
11245
|
+
this.emit(`option:${option.name()}`);
|
|
11246
|
+
}
|
|
11247
|
+
activeVariadicOption = option.variadic ? option : null;
|
|
11248
|
+
continue;
|
|
11249
|
+
}
|
|
11250
|
+
}
|
|
11251
|
+
if (arg.length > 2 && arg[0] === "-" && arg[1] !== "-") {
|
|
11252
|
+
const option = this._findOption(`-${arg[1]}`);
|
|
11253
|
+
if (option) {
|
|
11254
|
+
if (option.required || option.optional && this._combineFlagAndOptionalValue) {
|
|
11255
|
+
this.emit(`option:${option.name()}`, arg.slice(2));
|
|
11256
|
+
} else {
|
|
11257
|
+
this.emit(`option:${option.name()}`);
|
|
11258
|
+
args.unshift(`-${arg.slice(2)}`);
|
|
11259
|
+
}
|
|
11260
|
+
continue;
|
|
11261
|
+
}
|
|
11262
|
+
}
|
|
11263
|
+
if (/^--[^=]+=/.test(arg)) {
|
|
11264
|
+
const index = arg.indexOf("=");
|
|
11265
|
+
const option = this._findOption(arg.slice(0, index));
|
|
11266
|
+
if (option && (option.required || option.optional)) {
|
|
11267
|
+
this.emit(`option:${option.name()}`, arg.slice(index + 1));
|
|
11268
|
+
continue;
|
|
11269
|
+
}
|
|
11270
|
+
}
|
|
11271
|
+
if (maybeOption(arg)) {
|
|
11272
|
+
dest = unknown;
|
|
11273
|
+
}
|
|
11274
|
+
if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
|
|
11275
|
+
if (this._findCommand(arg)) {
|
|
11276
|
+
operands.push(arg);
|
|
11277
|
+
if (args.length > 0)
|
|
11278
|
+
unknown.push(...args);
|
|
11279
|
+
break;
|
|
11280
|
+
} else if (this._getHelpCommand() && arg === this._getHelpCommand().name()) {
|
|
11281
|
+
operands.push(arg);
|
|
11282
|
+
if (args.length > 0)
|
|
11283
|
+
operands.push(...args);
|
|
11284
|
+
break;
|
|
11285
|
+
} else if (this._defaultCommandName) {
|
|
11286
|
+
unknown.push(arg);
|
|
11287
|
+
if (args.length > 0)
|
|
11288
|
+
unknown.push(...args);
|
|
11289
|
+
break;
|
|
11290
|
+
}
|
|
11291
|
+
}
|
|
11292
|
+
if (this._passThroughOptions) {
|
|
11293
|
+
dest.push(arg);
|
|
11294
|
+
if (args.length > 0)
|
|
11295
|
+
dest.push(...args);
|
|
11296
|
+
break;
|
|
11297
|
+
}
|
|
11298
|
+
dest.push(arg);
|
|
11299
|
+
}
|
|
11300
|
+
return { operands, unknown };
|
|
11301
|
+
}
|
|
11302
|
+
opts() {
|
|
11303
|
+
if (this._storeOptionsAsProperties) {
|
|
11304
|
+
const result = {};
|
|
11305
|
+
const len = this.options.length;
|
|
11306
|
+
for (let i = 0;i < len; i++) {
|
|
11307
|
+
const key = this.options[i].attributeName();
|
|
11308
|
+
result[key] = key === this._versionOptionName ? this._version : this[key];
|
|
11309
|
+
}
|
|
11310
|
+
return result;
|
|
11311
|
+
}
|
|
11312
|
+
return this._optionValues;
|
|
11313
|
+
}
|
|
11314
|
+
optsWithGlobals() {
|
|
11315
|
+
return this._getCommandAndAncestors().reduce((combinedOptions, cmd) => Object.assign(combinedOptions, cmd.opts()), {});
|
|
11316
|
+
}
|
|
11317
|
+
error(message, errorOptions) {
|
|
11318
|
+
this._outputConfiguration.outputError(`${message}
|
|
11319
|
+
`, this._outputConfiguration.writeErr);
|
|
11320
|
+
if (typeof this._showHelpAfterError === "string") {
|
|
11321
|
+
this._outputConfiguration.writeErr(`${this._showHelpAfterError}
|
|
11322
|
+
`);
|
|
11323
|
+
} else if (this._showHelpAfterError) {
|
|
11324
|
+
this._outputConfiguration.writeErr(`
|
|
11325
|
+
`);
|
|
11326
|
+
this.outputHelp({ error: true });
|
|
11327
|
+
}
|
|
11328
|
+
const config = errorOptions || {};
|
|
11329
|
+
const exitCode = config.exitCode || 1;
|
|
11330
|
+
const code = config.code || "commander.error";
|
|
11331
|
+
this._exit(exitCode, code, message);
|
|
11332
|
+
}
|
|
11333
|
+
_parseOptionsEnv() {
|
|
11334
|
+
this.options.forEach((option) => {
|
|
11335
|
+
if (option.envVar && option.envVar in process2.env) {
|
|
11336
|
+
const optionKey = option.attributeName();
|
|
11337
|
+
if (this.getOptionValue(optionKey) === undefined || ["default", "config", "env"].includes(this.getOptionValueSource(optionKey))) {
|
|
11338
|
+
if (option.required || option.optional) {
|
|
11339
|
+
this.emit(`optionEnv:${option.name()}`, process2.env[option.envVar]);
|
|
11340
|
+
} else {
|
|
11341
|
+
this.emit(`optionEnv:${option.name()}`);
|
|
11342
|
+
}
|
|
11343
|
+
}
|
|
11344
|
+
}
|
|
11345
|
+
});
|
|
11346
|
+
}
|
|
11347
|
+
_parseOptionsImplied() {
|
|
11348
|
+
const dualHelper = new DualOptions(this.options);
|
|
11349
|
+
const hasCustomOptionValue = (optionKey) => {
|
|
11350
|
+
return this.getOptionValue(optionKey) !== undefined && !["default", "implied"].includes(this.getOptionValueSource(optionKey));
|
|
11351
|
+
};
|
|
11352
|
+
this.options.filter((option) => option.implied !== undefined && hasCustomOptionValue(option.attributeName()) && dualHelper.valueFromOption(this.getOptionValue(option.attributeName()), option)).forEach((option) => {
|
|
11353
|
+
Object.keys(option.implied).filter((impliedKey) => !hasCustomOptionValue(impliedKey)).forEach((impliedKey) => {
|
|
11354
|
+
this.setOptionValueWithSource(impliedKey, option.implied[impliedKey], "implied");
|
|
11355
|
+
});
|
|
11356
|
+
});
|
|
11357
|
+
}
|
|
11358
|
+
missingArgument(name) {
|
|
11359
|
+
const message = `error: missing required argument '${name}'`;
|
|
11360
|
+
this.error(message, { code: "commander.missingArgument" });
|
|
11361
|
+
}
|
|
11362
|
+
optionMissingArgument(option) {
|
|
11363
|
+
const message = `error: option '${option.flags}' argument missing`;
|
|
11364
|
+
this.error(message, { code: "commander.optionMissingArgument" });
|
|
11365
|
+
}
|
|
11366
|
+
missingMandatoryOptionValue(option) {
|
|
11367
|
+
const message = `error: required option '${option.flags}' not specified`;
|
|
11368
|
+
this.error(message, { code: "commander.missingMandatoryOptionValue" });
|
|
11369
|
+
}
|
|
11370
|
+
_conflictingOption(option, conflictingOption) {
|
|
11371
|
+
const findBestOptionFromValue = (option2) => {
|
|
11372
|
+
const optionKey = option2.attributeName();
|
|
11373
|
+
const optionValue = this.getOptionValue(optionKey);
|
|
11374
|
+
const negativeOption = this.options.find((target) => target.negate && optionKey === target.attributeName());
|
|
11375
|
+
const positiveOption = this.options.find((target) => !target.negate && optionKey === target.attributeName());
|
|
11376
|
+
if (negativeOption && (negativeOption.presetArg === undefined && optionValue === false || negativeOption.presetArg !== undefined && optionValue === negativeOption.presetArg)) {
|
|
11377
|
+
return negativeOption;
|
|
11378
|
+
}
|
|
11379
|
+
return positiveOption || option2;
|
|
11380
|
+
};
|
|
11381
|
+
const getErrorMessage = (option2) => {
|
|
11382
|
+
const bestOption = findBestOptionFromValue(option2);
|
|
11383
|
+
const optionKey = bestOption.attributeName();
|
|
11384
|
+
const source = this.getOptionValueSource(optionKey);
|
|
11385
|
+
if (source === "env") {
|
|
11386
|
+
return `environment variable '${bestOption.envVar}'`;
|
|
11387
|
+
}
|
|
11388
|
+
return `option '${bestOption.flags}'`;
|
|
11389
|
+
};
|
|
11390
|
+
const message = `error: ${getErrorMessage(option)} cannot be used with ${getErrorMessage(conflictingOption)}`;
|
|
11391
|
+
this.error(message, { code: "commander.conflictingOption" });
|
|
11392
|
+
}
|
|
11393
|
+
unknownOption(flag) {
|
|
11394
|
+
if (this._allowUnknownOption)
|
|
11395
|
+
return;
|
|
11396
|
+
let suggestion = "";
|
|
11397
|
+
if (flag.startsWith("--") && this._showSuggestionAfterError) {
|
|
11398
|
+
let candidateFlags = [];
|
|
11399
|
+
let command = this;
|
|
11400
|
+
do {
|
|
11401
|
+
const moreFlags = command.createHelp().visibleOptions(command).filter((option) => option.long).map((option) => option.long);
|
|
11402
|
+
candidateFlags = candidateFlags.concat(moreFlags);
|
|
11403
|
+
command = command.parent;
|
|
11404
|
+
} while (command && !command._enablePositionalOptions);
|
|
11405
|
+
suggestion = suggestSimilar(flag, candidateFlags);
|
|
11406
|
+
}
|
|
11407
|
+
const message = `error: unknown option '${flag}'${suggestion}`;
|
|
11408
|
+
this.error(message, { code: "commander.unknownOption" });
|
|
11409
|
+
}
|
|
11410
|
+
_excessArguments(receivedArgs) {
|
|
11411
|
+
if (this._allowExcessArguments)
|
|
11412
|
+
return;
|
|
11413
|
+
const expected = this.registeredArguments.length;
|
|
11414
|
+
const s = expected === 1 ? "" : "s";
|
|
11415
|
+
const forSubcommand = this.parent ? ` for '${this.name()}'` : "";
|
|
11416
|
+
const message = `error: too many arguments${forSubcommand}. Expected ${expected} argument${s} but got ${receivedArgs.length}.`;
|
|
11417
|
+
this.error(message, { code: "commander.excessArguments" });
|
|
11418
|
+
}
|
|
11419
|
+
unknownCommand() {
|
|
11420
|
+
const unknownName = this.args[0];
|
|
11421
|
+
let suggestion = "";
|
|
11422
|
+
if (this._showSuggestionAfterError) {
|
|
11423
|
+
const candidateNames = [];
|
|
11424
|
+
this.createHelp().visibleCommands(this).forEach((command) => {
|
|
11425
|
+
candidateNames.push(command.name());
|
|
11426
|
+
if (command.alias())
|
|
11427
|
+
candidateNames.push(command.alias());
|
|
11428
|
+
});
|
|
11429
|
+
suggestion = suggestSimilar(unknownName, candidateNames);
|
|
11430
|
+
}
|
|
11431
|
+
const message = `error: unknown command '${unknownName}'${suggestion}`;
|
|
11432
|
+
this.error(message, { code: "commander.unknownCommand" });
|
|
11433
|
+
}
|
|
11434
|
+
version(str, flags, description) {
|
|
11435
|
+
if (str === undefined)
|
|
11436
|
+
return this._version;
|
|
11437
|
+
this._version = str;
|
|
11438
|
+
flags = flags || "-V, --version";
|
|
11439
|
+
description = description || "output the version number";
|
|
11440
|
+
const versionOption = this.createOption(flags, description);
|
|
11441
|
+
this._versionOptionName = versionOption.attributeName();
|
|
11442
|
+
this._registerOption(versionOption);
|
|
11443
|
+
this.on("option:" + versionOption.name(), () => {
|
|
11444
|
+
this._outputConfiguration.writeOut(`${str}
|
|
11445
|
+
`);
|
|
11446
|
+
this._exit(0, "commander.version", str);
|
|
11447
|
+
});
|
|
11448
|
+
return this;
|
|
11449
|
+
}
|
|
11450
|
+
description(str, argsDescription) {
|
|
11451
|
+
if (str === undefined && argsDescription === undefined)
|
|
11452
|
+
return this._description;
|
|
11453
|
+
this._description = str;
|
|
11454
|
+
if (argsDescription) {
|
|
11455
|
+
this._argsDescription = argsDescription;
|
|
11456
|
+
}
|
|
11457
|
+
return this;
|
|
11458
|
+
}
|
|
11459
|
+
summary(str) {
|
|
11460
|
+
if (str === undefined)
|
|
11461
|
+
return this._summary;
|
|
11462
|
+
this._summary = str;
|
|
11463
|
+
return this;
|
|
11464
|
+
}
|
|
11465
|
+
alias(alias) {
|
|
11466
|
+
if (alias === undefined)
|
|
11467
|
+
return this._aliases[0];
|
|
11468
|
+
let command = this;
|
|
11469
|
+
if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) {
|
|
11470
|
+
command = this.commands[this.commands.length - 1];
|
|
11471
|
+
}
|
|
11472
|
+
if (alias === command._name)
|
|
11473
|
+
throw new Error("Command alias can't be the same as its name");
|
|
11474
|
+
const matchingCommand = this.parent?._findCommand(alias);
|
|
11475
|
+
if (matchingCommand) {
|
|
11476
|
+
const existingCmd = [matchingCommand.name()].concat(matchingCommand.aliases()).join("|");
|
|
11477
|
+
throw new Error(`cannot add alias '${alias}' to command '${this.name()}' as already have command '${existingCmd}'`);
|
|
11478
|
+
}
|
|
11479
|
+
command._aliases.push(alias);
|
|
11480
|
+
return this;
|
|
11481
|
+
}
|
|
11482
|
+
aliases(aliases) {
|
|
11483
|
+
if (aliases === undefined)
|
|
11484
|
+
return this._aliases;
|
|
11485
|
+
aliases.forEach((alias) => this.alias(alias));
|
|
11486
|
+
return this;
|
|
11487
|
+
}
|
|
11488
|
+
usage(str) {
|
|
11489
|
+
if (str === undefined) {
|
|
11490
|
+
if (this._usage)
|
|
11491
|
+
return this._usage;
|
|
11492
|
+
const args = this.registeredArguments.map((arg) => {
|
|
11493
|
+
return humanReadableArgName(arg);
|
|
11494
|
+
});
|
|
11495
|
+
return [].concat(this.options.length || this._helpOption !== null ? "[options]" : [], this.commands.length ? "[command]" : [], this.registeredArguments.length ? args : []).join(" ");
|
|
11496
|
+
}
|
|
11497
|
+
this._usage = str;
|
|
11498
|
+
return this;
|
|
11499
|
+
}
|
|
11500
|
+
name(str) {
|
|
11501
|
+
if (str === undefined)
|
|
11502
|
+
return this._name;
|
|
11503
|
+
this._name = str;
|
|
11504
|
+
return this;
|
|
11505
|
+
}
|
|
11506
|
+
nameFromFilename(filename) {
|
|
11507
|
+
this._name = path2.basename(filename, path2.extname(filename));
|
|
11508
|
+
return this;
|
|
11509
|
+
}
|
|
11510
|
+
executableDir(path3) {
|
|
11511
|
+
if (path3 === undefined)
|
|
11512
|
+
return this._executableDir;
|
|
11513
|
+
this._executableDir = path3;
|
|
11514
|
+
return this;
|
|
11515
|
+
}
|
|
11516
|
+
helpInformation(contextOptions) {
|
|
11517
|
+
const helper = this.createHelp();
|
|
11518
|
+
const context = this._getOutputContext(contextOptions);
|
|
11519
|
+
helper.prepareContext({
|
|
11520
|
+
error: context.error,
|
|
11521
|
+
helpWidth: context.helpWidth,
|
|
11522
|
+
outputHasColors: context.hasColors
|
|
11523
|
+
});
|
|
11524
|
+
const text = helper.formatHelp(this, helper);
|
|
11525
|
+
if (context.hasColors)
|
|
11526
|
+
return text;
|
|
11527
|
+
return this._outputConfiguration.stripColor(text);
|
|
11528
|
+
}
|
|
11529
|
+
_getOutputContext(contextOptions) {
|
|
11530
|
+
contextOptions = contextOptions || {};
|
|
11531
|
+
const error = !!contextOptions.error;
|
|
11532
|
+
let baseWrite;
|
|
11533
|
+
let hasColors;
|
|
11534
|
+
let helpWidth;
|
|
11535
|
+
if (error) {
|
|
11536
|
+
baseWrite = (str) => this._outputConfiguration.writeErr(str);
|
|
11537
|
+
hasColors = this._outputConfiguration.getErrHasColors();
|
|
11538
|
+
helpWidth = this._outputConfiguration.getErrHelpWidth();
|
|
11539
|
+
} else {
|
|
11540
|
+
baseWrite = (str) => this._outputConfiguration.writeOut(str);
|
|
11541
|
+
hasColors = this._outputConfiguration.getOutHasColors();
|
|
11542
|
+
helpWidth = this._outputConfiguration.getOutHelpWidth();
|
|
11543
|
+
}
|
|
11544
|
+
const write = (str) => {
|
|
11545
|
+
if (!hasColors)
|
|
11546
|
+
str = this._outputConfiguration.stripColor(str);
|
|
11547
|
+
return baseWrite(str);
|
|
11548
|
+
};
|
|
11549
|
+
return { error, write, hasColors, helpWidth };
|
|
11550
|
+
}
|
|
11551
|
+
outputHelp(contextOptions) {
|
|
11552
|
+
let deprecatedCallback;
|
|
11553
|
+
if (typeof contextOptions === "function") {
|
|
11554
|
+
deprecatedCallback = contextOptions;
|
|
11555
|
+
contextOptions = undefined;
|
|
11556
|
+
}
|
|
11557
|
+
const outputContext = this._getOutputContext(contextOptions);
|
|
11558
|
+
const eventContext = {
|
|
11559
|
+
error: outputContext.error,
|
|
11560
|
+
write: outputContext.write,
|
|
11561
|
+
command: this
|
|
11562
|
+
};
|
|
11563
|
+
this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", eventContext));
|
|
11564
|
+
this.emit("beforeHelp", eventContext);
|
|
11565
|
+
let helpInformation = this.helpInformation({ error: outputContext.error });
|
|
11566
|
+
if (deprecatedCallback) {
|
|
11567
|
+
helpInformation = deprecatedCallback(helpInformation);
|
|
11568
|
+
if (typeof helpInformation !== "string" && !Buffer.isBuffer(helpInformation)) {
|
|
11569
|
+
throw new Error("outputHelp callback must return a string or a Buffer");
|
|
11570
|
+
}
|
|
11571
|
+
}
|
|
11572
|
+
outputContext.write(helpInformation);
|
|
11573
|
+
if (this._getHelpOption()?.long) {
|
|
11574
|
+
this.emit(this._getHelpOption().long);
|
|
11575
|
+
}
|
|
11576
|
+
this.emit("afterHelp", eventContext);
|
|
11577
|
+
this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp", eventContext));
|
|
11578
|
+
}
|
|
11579
|
+
helpOption(flags, description) {
|
|
11580
|
+
if (typeof flags === "boolean") {
|
|
11581
|
+
if (flags) {
|
|
11582
|
+
this._helpOption = this._helpOption ?? undefined;
|
|
11583
|
+
} else {
|
|
11584
|
+
this._helpOption = null;
|
|
11585
|
+
}
|
|
11586
|
+
return this;
|
|
11587
|
+
}
|
|
11588
|
+
flags = flags ?? "-h, --help";
|
|
11589
|
+
description = description ?? "display help for command";
|
|
11590
|
+
this._helpOption = this.createOption(flags, description);
|
|
11591
|
+
return this;
|
|
11592
|
+
}
|
|
11593
|
+
_getHelpOption() {
|
|
11594
|
+
if (this._helpOption === undefined) {
|
|
11595
|
+
this.helpOption(undefined, undefined);
|
|
11596
|
+
}
|
|
11597
|
+
return this._helpOption;
|
|
11598
|
+
}
|
|
11599
|
+
addHelpOption(option) {
|
|
11600
|
+
this._helpOption = option;
|
|
11601
|
+
return this;
|
|
11602
|
+
}
|
|
11603
|
+
help(contextOptions) {
|
|
11604
|
+
this.outputHelp(contextOptions);
|
|
11605
|
+
let exitCode = Number(process2.exitCode ?? 0);
|
|
11606
|
+
if (exitCode === 0 && contextOptions && typeof contextOptions !== "function" && contextOptions.error) {
|
|
11607
|
+
exitCode = 1;
|
|
11608
|
+
}
|
|
11609
|
+
this._exit(exitCode, "commander.help", "(outputHelp)");
|
|
11610
|
+
}
|
|
11611
|
+
addHelpText(position, text) {
|
|
11612
|
+
const allowedValues = ["beforeAll", "before", "after", "afterAll"];
|
|
11613
|
+
if (!allowedValues.includes(position)) {
|
|
11614
|
+
throw new Error(`Unexpected value for position to addHelpText.
|
|
11615
|
+
Expecting one of '${allowedValues.join("', '")}'`);
|
|
11616
|
+
}
|
|
11617
|
+
const helpEvent = `${position}Help`;
|
|
11618
|
+
this.on(helpEvent, (context) => {
|
|
11619
|
+
let helpStr;
|
|
11620
|
+
if (typeof text === "function") {
|
|
11621
|
+
helpStr = text({ error: context.error, command: context.command });
|
|
11622
|
+
} else {
|
|
11623
|
+
helpStr = text;
|
|
11624
|
+
}
|
|
11625
|
+
if (helpStr) {
|
|
11626
|
+
context.write(`${helpStr}
|
|
11627
|
+
`);
|
|
11628
|
+
}
|
|
11629
|
+
});
|
|
11630
|
+
return this;
|
|
11631
|
+
}
|
|
11632
|
+
_outputHelpIfRequested(args) {
|
|
11633
|
+
const helpOption = this._getHelpOption();
|
|
11634
|
+
const helpRequested = helpOption && args.find((arg) => helpOption.is(arg));
|
|
11635
|
+
if (helpRequested) {
|
|
11636
|
+
this.outputHelp();
|
|
11637
|
+
this._exit(0, "commander.helpDisplayed", "(outputHelp)");
|
|
11638
|
+
}
|
|
11639
|
+
}
|
|
11640
|
+
}
|
|
11641
|
+
function incrementNodeInspectorPort(args) {
|
|
11642
|
+
return args.map((arg) => {
|
|
11643
|
+
if (!arg.startsWith("--inspect")) {
|
|
11644
|
+
return arg;
|
|
11645
|
+
}
|
|
11646
|
+
let debugOption;
|
|
11647
|
+
let debugHost = "127.0.0.1";
|
|
11648
|
+
let debugPort = "9229";
|
|
11649
|
+
let match;
|
|
11650
|
+
if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) {
|
|
11651
|
+
debugOption = match[1];
|
|
11652
|
+
} else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) {
|
|
11653
|
+
debugOption = match[1];
|
|
11654
|
+
if (/^\d+$/.test(match[3])) {
|
|
11655
|
+
debugPort = match[3];
|
|
11656
|
+
} else {
|
|
11657
|
+
debugHost = match[3];
|
|
11658
|
+
}
|
|
11659
|
+
} else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) {
|
|
11660
|
+
debugOption = match[1];
|
|
11661
|
+
debugHost = match[3];
|
|
11662
|
+
debugPort = match[4];
|
|
11663
|
+
}
|
|
11664
|
+
if (debugOption && debugPort !== "0") {
|
|
11665
|
+
return `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`;
|
|
11666
|
+
}
|
|
11667
|
+
return arg;
|
|
11668
|
+
});
|
|
11669
|
+
}
|
|
11670
|
+
function useColor() {
|
|
11671
|
+
if (process2.env.NO_COLOR || process2.env.FORCE_COLOR === "0" || process2.env.FORCE_COLOR === "false")
|
|
11672
|
+
return false;
|
|
11673
|
+
if (process2.env.FORCE_COLOR || process2.env.CLICOLOR_FORCE !== undefined)
|
|
11674
|
+
return true;
|
|
11675
|
+
return;
|
|
11676
|
+
}
|
|
11677
|
+
exports2.Command = Command;
|
|
11678
|
+
exports2.useColor = useColor;
|
|
11679
|
+
});
|
|
11680
|
+
|
|
11681
|
+
// node_modules/.bun/commander@13.1.0/node_modules/commander/index.js
|
|
11682
|
+
var require_commander = __commonJS((exports2) => {
|
|
11683
|
+
var { Argument } = require_argument();
|
|
11684
|
+
var { Command } = require_command();
|
|
11685
|
+
var { CommanderError, InvalidArgumentError } = require_error();
|
|
11686
|
+
var { Help } = require_help();
|
|
11687
|
+
var { Option } = require_option();
|
|
11688
|
+
exports2.program = new Command;
|
|
11689
|
+
exports2.createCommand = (name) => new Command(name);
|
|
11690
|
+
exports2.createOption = (flags, description) => new Option(flags, description);
|
|
11691
|
+
exports2.createArgument = (name, description) => new Argument(name, description);
|
|
11692
|
+
exports2.Command = Command;
|
|
11693
|
+
exports2.Option = Option;
|
|
11694
|
+
exports2.Argument = Argument;
|
|
11695
|
+
exports2.Help = Help;
|
|
11696
|
+
exports2.CommanderError = CommanderError;
|
|
11697
|
+
exports2.InvalidArgumentError = InvalidArgumentError;
|
|
11698
|
+
exports2.InvalidOptionArgumentError = InvalidArgumentError;
|
|
11699
|
+
});
|
|
11700
|
+
|
|
9667
11701
|
// src/host-control/main.ts
|
|
9668
|
-
import { homedir as
|
|
9669
|
-
import { existsSync as
|
|
9670
|
-
import { join as
|
|
11702
|
+
import { homedir as homedir4 } from "node:os";
|
|
11703
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
11704
|
+
import { join as join6, resolve as resolve7 } from "node:path";
|
|
9671
11705
|
|
|
9672
11706
|
// src/config/loader.ts
|
|
9673
11707
|
import { readFileSync as readFileSync2, existsSync as existsSync3 } from "node:fs";
|
|
@@ -13885,7 +15919,7 @@ var TelegramChannelSchema = exports_external.object({
|
|
|
13885
15919
|
format: exports_external.enum(["html", "markdownv2", "text"]).optional().describe("Default reply format passed to the plugin"),
|
|
13886
15920
|
rate_limit_ms: exports_external.number().optional().describe("Minimum delay between outgoing messages in ms"),
|
|
13887
15921
|
stream_mode: exports_external.enum(["pty", "checklist"]).optional().describe("How live progress is streamed to Telegram during a turn. " + "'pty' (default) surfaces text snapshots of Claude Code's TUI — " + "compatible but can flicker as Ink re-renders. 'checklist' drives " + "a structured progress card from session-tail events — stable " + "order, per-tool status emojis, fires only on semantic transitions."),
|
|
13888
|
-
stream_throttle_ms: exports_external.number().int().nonnegative().optional().describe("Throttle window in ms between successive stream edits
|
|
15922
|
+
stream_throttle_ms: exports_external.number().int().nonnegative().optional().describe("Throttle window in ms between successive in-place stream edits " + "during a turn. Lower = more responsive stream, higher = fewer API " + "calls. Floored at 250 by draft-stream itself. Default 400 ms for DMs " + "and 1000 ms for groups/forums (respects Telegram's ~1 edit/sec/message " + "practical ceiling). Override per-agent if a particular agent needs " + "snappier or quieter streaming."),
|
|
13889
15923
|
clear_status_on_completion: exports_external.boolean().optional().describe("When true, the live activity/status feed (the in-place 'what it's " + "doing' message — Reading X, Searching the web for Y, …) is DELETED " + "when the turn's final answer lands, so only the reply remains. " + "Default false: the status message is left in the chat as a record " + "(its last step marked done) — no post-then-delete. Per-agent " + "override; cascades defaults → profile → agent (per-key)."),
|
|
13890
15924
|
hotReloadStable: exports_external.boolean().optional().describe("If true, the stable workspace prefix (AGENTS.md, SOUL.md, USER.md, " + "IDENTITY.md, TOOLS.md) is re-injected on every turn via " + "the UserPromptSubmit hook instead of baked into --append-system-prompt " + "at session start. Lets workspace edits propagate without a restart. " + "Costs ~5-10% per-turn latency/spend since the stable prefix is no " + "longer prompt-cached."),
|
|
13891
15925
|
inject_on_change: exports_external.boolean().optional().describe("Context-efficiency gate for per-turn hook injection (default true). " + "When true (the default), the turn-pacing directive and dynamic " + "workspace content are only re-emitted when their content changes or " + "the session_id changes — suppressing redundant injection that " + "otherwise triples compaction frequency. Set to false to revert to " + "the legacy always-emit behaviour (every turn injects the full " + "content regardless of whether it changed)."),
|
|
@@ -13973,6 +16007,14 @@ var GoogleWorkspaceConfigSchema = exports_external.object({
|
|
|
13973
16007
|
approvers: exports_external.array(ApproverIdSchema).min(1).describe("Array of numeric Telegram user IDs authorized to approve drive onboarding. " + "At least one must be specified."),
|
|
13974
16008
|
tier: GoogleWorkspaceTierSchema.optional().describe("RFC G Phase 1: which upstream MCP tier to expose. " + "core (default) = ~16 tools (Drive+Docs+Sheets+Calendar). " + "extended = ~40 tools (+Slides, Forms, Tasks, Chat). " + "complete = ~60+ tools (+Gmail; not recommended yet — see RFC G §5).")
|
|
13975
16009
|
}).optional();
|
|
16010
|
+
var LiteLLMConfigSchema = exports_external.object({
|
|
16011
|
+
enabled: exports_external.boolean().optional().describe("Opt-in toggle. When true, `switchroom apply` provisions a per-agent " + "LiteLLM virtual key and injects routing env into the container. " + "Default OFF."),
|
|
16012
|
+
base_url: exports_external.string().optional().describe("LiteLLM proxy base URL the agent's claude CLI routes through, e.g. " + "'http://127.0.0.1:4010'. Agents use network_mode:host, so loopback " + "reaches a host-bound proxy. Exported as ANTHROPIC_BASE_URL."),
|
|
16013
|
+
admin_key: exports_external.string().optional().describe("LiteLLM master/admin key used at apply time to provision the team + " + "virtual key. Supports a vault reference (e.g. " + "'vault:litellm/master-key') — resolution happens at apply time via " + "the vault-broker. Never injected into the agent container."),
|
|
16014
|
+
team: exports_external.string().optional().describe("LiteLLM team alias the per-agent key is created under. Defaults to " + "'switchroom' (applied in code, not as a schema default)."),
|
|
16015
|
+
small_fast_model: exports_external.string().optional().describe("Model id exported as ANTHROPIC_SMALL_FAST_MODEL for the claude CLI's " + "background/fast lane, e.g. 'claude-haiku-4-5-20251001'."),
|
|
16016
|
+
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins).")
|
|
16017
|
+
}).optional().describe("LiteLLM routing config — opt-in per-agent virtual-key auto-provisioning " + "+ routing env. Default OFF. See LiteLLMConfigSchema doc for the full flow.");
|
|
13976
16018
|
var MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
13977
16019
|
microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL — omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
13978
16020
|
microsoft_client_secret: exports_external.string().min(1).optional().describe("Microsoft OAuth client secret. Optional — public-client apps " + "(Mobile + Desktop platform with 'Allow public client flows' " + "enabled) work without a secret; confidential clients pass " + "one. Either literal or vault reference e.g. " + "'vault:microsoft-oauth-client-secret'."),
|
|
@@ -14069,6 +16111,7 @@ var profileFields = {
|
|
|
14069
16111
|
mcp_servers: exports_external.record(exports_external.string(), exports_external.unknown()).optional(),
|
|
14070
16112
|
hooks: AgentHooksSchema,
|
|
14071
16113
|
env: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
16114
|
+
litellm: LiteLLMConfigSchema,
|
|
14072
16115
|
system_prompt_append: exports_external.string().optional(),
|
|
14073
16116
|
skills: exports_external.array(exports_external.string()).optional(),
|
|
14074
16117
|
bundled_skills: exports_external.record(exports_external.string(), exports_external.boolean()).optional().describe("Opt-out map for switchroom's bundled-default skills " + "(e.g. skill-creator, mcp-builder, webapp-testing, pdf, docx, " + "xlsx, pptx, switchroom-cli, switchroom-status, switchroom-health). " + "Set a key to `false` to suppress that default for this agent. " + "Cascades from defaults.bundled_skills."),
|
|
@@ -14141,6 +16184,7 @@ var AgentSchema = exports_external.object({
|
|
|
14141
16184
|
mcp_servers: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("Additional MCP server configurations"),
|
|
14142
16185
|
hooks: AgentHooksSchema.describe("Claude Code lifecycle hooks (SessionStart, UserPromptSubmit, Stop, etc). " + "Written to settings.json.hooks in Claude Code's native shape."),
|
|
14143
16186
|
env: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Environment variables exported in start.sh before claude runs"),
|
|
16187
|
+
litellm: LiteLLMConfigSchema.describe("Per-agent LiteLLM routing override. Presence with `enabled: true` opts " + "this agent IN to per-agent virtual-key auto-provisioning + routing env " + "(falls back to the top-level `litellm:` block for base_url/admin_key/" + "team/small_fast_model). Deep-merges one level over defaults/profile; " + "`tags` merge per-key, agent wins. Default OFF."),
|
|
14144
16188
|
system_prompt_append: exports_external.string().optional().describe("Text passed via claude's --append-system-prompt flag. " + "Appended to the default or CLAUDE.md-derived system prompt."),
|
|
14145
16189
|
skills: exports_external.array(exports_external.string()).optional().describe("Names of skills from switchroom.skills_dir to symlink into this " + "agent's skills/ directory. Unioned with defaults.skills."),
|
|
14146
16190
|
bundled_skills: exports_external.record(exports_external.string(), exports_external.boolean()).optional().describe("Per-agent override of switchroom's bundled-default skills " + "(skill-creator, mcp-builder, webapp-testing, pdf, docx, xlsx, " + "pptx, switchroom-cli/status/health). Set a key to `false` to " + "opt out for this agent. Per-agent value wins over defaults.bundled_skills."),
|
|
@@ -14266,7 +16310,7 @@ var WebServiceConfigSchema = exports_external.object({
|
|
|
14266
16310
|
});
|
|
14267
16311
|
var HostdConfigSchema = exports_external.object({
|
|
14268
16312
|
config_edit_enabled: exports_external.boolean().default(false).describe("Opt-in toggle for the `config_propose_edit` hostd verb (RFC " + "admin-agent-config-edit §3). Default false — the verb returns " + "`E_CONFIG_EDIT_DISABLED` until the operator explicitly flips " + "this to true. When true, admin agents can propose unified-diff " + "patches against " + "`/state/config/switchroom.yaml`, gated by an operator approval " + "card in the primary chat. Same trust posture as `update_apply` " + "and `agent_restart`: the human-in-the-loop tap is the security " + "boundary, not the agent's judgement."),
|
|
14269
|
-
config_edit_rate_per_hour: exports_external.number().int().min(1).max(20).default(3).describe("Per-requesting-agent rate cap for `config_propose_edit` cards " + "(RFC admin-agent-config-edit §5). Default 3 cards/hour; min 1, " + "max 20.
|
|
16313
|
+
config_edit_rate_per_hour: exports_external.number().int().min(1).max(20).default(3).describe("Per-requesting-agent rate cap for `config_propose_edit` cards " + "(RFC admin-agent-config-edit §5). Default 3 cards/hour; min 1, " + "max 20. ENFORCED server-side: a caller exceeding this in a sliding " + "1-hour window is rejected with `E_RATE_LIMITED` (carrying a " + "`retry_after` fix) instead of posting another operator approval " + "card — so a looping agent is throttled rather than spamming the chat.")
|
|
14270
16314
|
});
|
|
14271
16315
|
var CronEgressSchema = exports_external.object({
|
|
14272
16316
|
allowed_hosts: exports_external.array(exports_external.string().min(1)).default([]).describe("Hosts a poll may reach (exact, https-only). loopback/private/IP-literal are always rejected."),
|
|
@@ -14299,11 +16343,14 @@ var SwitchroomConfigSchema = exports_external.object({
|
|
|
14299
16343
|
message: "Consumer name must be a path-safe slug (letters, digits, underscore, hyphen)"
|
|
14300
16344
|
}).describe("Socket-path identity; binds at /run/switchroom/auth-broker/<name>/sock"),
|
|
14301
16345
|
account: exports_external.string().min(1).describe("Pinned account label for this consumer. `get-credentials` returns " + "this account's credentials; `mark-exhausted` from this consumer " + "only affects this account."),
|
|
14302
|
-
uid: exports_external.number().int().nonnegative().optional().describe("Optional UID to chown the consumer socket to (defaults to 0 = root, " + "suitable for sibling containers running as root).")
|
|
14303
|
-
|
|
16346
|
+
uid: exports_external.number().int().nonnegative().optional().describe("Optional UID to chown the consumer socket to (defaults to 0 = root, " + "suitable for sibling containers running as root)."),
|
|
16347
|
+
mirror_dir: exports_external.string().optional().describe("Optional host-side directory path. When set, the broker actively " + "writes the consumer's effective-account `.credentials.json` mirror " + "here — in addition to serving creds on demand via `get-credentials`. " + "Use this to eliminate the pull-latency gap: without a mirror the " + "consumer only gets failover creds at its next scheduled re-fetch " + "(up to 30 min). With a mirror the broker pushes failover creds " + "immediately when it detects exhaustion (consumer-quota-sensor tick, " + "or a mark-exhausted RPC on the pinned account). The directory must " + "be accessible to the broker container (bind-mounted from the host) " + "and to the consumer container; the broker writes " + "`<mirror_dir>/.credentials.json` atomically. Chown is attempted to " + "`uid` (default 0) — swallowed when CAP_CHOWN is absent.")
|
|
16348
|
+
})).optional().describe("Non-agent peers that hold a broker socket (RFC H §4.8). Each gets " + "its own `/run/switchroom/auth-broker/<name>/sock` chowned to its UID. " + "Consumers cannot be admins; a consumer name that collides with an " + "agent (whether that agent has `admin: true` or not) is a config " + "error caught at schema validation."),
|
|
16349
|
+
allow_overage_accounts: exports_external.array(exports_external.string().min(1)).optional().describe("Opt-in list of account labels (bare strings matching `auth.active` / " + "`auth.fallback_order` entries) that may be served PAST the weekly " + "utilization wall when Anthropic overage billing is available for the " + "account (`overageStatus === 'allowed'`). Overage is REAL MONEY — " + "default is empty (no account gets this). An account in this list is " + "only kept eligible when its fresh quota snapshot reports " + "`overageStatus: 'allowed'` AND `overageDisabledReason` is NOT " + "'out_of_credits' (i.e. the overage credit has not been exhausted). " + "As soon as `overageDisabledReason` becomes 'out_of_credits', the " + "account is blocked immediately regardless of this flag. Overage lifts " + "ONLY the utilization wall — it cannot lift an active exhaustion mark " + "written by a real 429 (`mark-exhausted`).")
|
|
14304
16350
|
}).optional().describe("Switchroom-auth-broker configuration (RFC H). Fleet-wide active account, " + "fallback order, admin-agent ACL, and ephemeral-consumer surface. " + "Required from the v0.8+ schema onwards; pre-v0.8 fleets are migrated " + "in-place by `switchroom apply` (see src/auth/migrate-schema.ts)."),
|
|
14305
16351
|
drive: GoogleWorkspaceConfigSchema.describe("RFC D legacy key — use `google_workspace:` instead. Optional Google " + "Workspace onboarding configuration. When set, supplies Google OAuth " + "client credentials, the approver allowlist for `switchroom drive " + "connect`, and the optional tier knob. Env vars " + "(SWITCHROOM_GOOGLE_CLIENT_ID, SWITCHROOM_GOOGLE_CLIENT_SECRET, " + "SWITCHROOM_APPROVER_USER_ID) take precedence over this block when " + "set, preserving back-compat with the env-only flow shipped in #766."),
|
|
14306
16352
|
google_workspace: GoogleWorkspaceConfigSchema.describe("RFC G canonical key. Top-level Google Workspace configuration — " + "OAuth client credentials, approver allowlist, and tier knob (`core` " + "| `extended` | `complete`, default `core`). Mutually exclusive with " + "`drive:` at the top level (loader fails fast if both are set)."),
|
|
16353
|
+
litellm: LiteLLMConfigSchema.describe("Top-level LiteLLM routing infra — global base_url, admin_key (the " + "LiteLLM master key, supports a `vault:` ref), team alias, and " + "small_fast_model shared by every agent that opts in. Set `enabled: " + "true` here to default the whole fleet on (each agent can still set " + "`litellm.enabled: false` to opt out). Default OFF."),
|
|
14307
16354
|
microsoft_workspace: MicrosoftWorkspaceConfigSchema.describe("RFC #1873 (Microsoft 365 integration). Top-level Microsoft Workspace " + "configuration — OAuth client credentials (Entra app), authority " + "endpoint (defaults to /common for personal MSA + work), and the " + "org_mode opt-in for Teams/SharePoint surfaces. Block is optional; " + "when omitted the broker does not register the Microsoft provider."),
|
|
14308
16355
|
notion_workspace: NotionWorkspaceConfigSchema.describe("RFC reference/rfcs/notion-integration.md. Top-level Notion integration " + "config — vault key for the integration token, friendly-name → " + "database UUID map, optional MCP-package version pin, and optional " + "global rate-limit override (default 3 rps, Notion's documented " + "public-API limit). Block is optional; when omitted no agent gets a " + "Notion MCP entry regardless of per-agent config."),
|
|
14309
16356
|
quota: QuotaConfigSchema.optional().describe("Optional weekly/monthly USD spend budgets rendered in the session " + "greeting. Usage is read from ccusage at runtime; no network calls."),
|
|
@@ -14733,6 +16780,24 @@ function mergeAgentConfig(defaultsIn, agentIn) {
|
|
|
14733
16780
|
...merged.env ?? {}
|
|
14734
16781
|
};
|
|
14735
16782
|
}
|
|
16783
|
+
if (defaults.litellm || merged.litellm) {
|
|
16784
|
+
const base = defaults.litellm ?? {};
|
|
16785
|
+
const override = merged.litellm ?? {};
|
|
16786
|
+
const combined = { ...base };
|
|
16787
|
+
for (const [k, v] of Object.entries(override)) {
|
|
16788
|
+
if (v === undefined)
|
|
16789
|
+
continue;
|
|
16790
|
+
if (k === "tags" && base.tags && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
16791
|
+
combined.tags = {
|
|
16792
|
+
...base.tags,
|
|
16793
|
+
...v
|
|
16794
|
+
};
|
|
16795
|
+
} else {
|
|
16796
|
+
combined[k] = v;
|
|
16797
|
+
}
|
|
16798
|
+
}
|
|
16799
|
+
merged.litellm = combined;
|
|
16800
|
+
}
|
|
14736
16801
|
if (defaults.subagents || merged.subagents) {
|
|
14737
16802
|
const dSub = defaults.subagents ?? {};
|
|
14738
16803
|
const mSub = merged.subagents ?? {};
|
|
@@ -15082,6 +17147,14 @@ function resolveAgentsDir(config) {
|
|
|
15082
17147
|
// src/agents/compose.ts
|
|
15083
17148
|
import { createHash } from "node:crypto";
|
|
15084
17149
|
|
|
17150
|
+
// src/config/timezone.ts
|
|
17151
|
+
var CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
17152
|
+
"UTC",
|
|
17153
|
+
"Etc/UTC",
|
|
17154
|
+
"Etc/Universal",
|
|
17155
|
+
"Universal"
|
|
17156
|
+
]);
|
|
17157
|
+
|
|
15085
17158
|
// src/vault/broker/peercred.ts
|
|
15086
17159
|
var RESERVED_AGENT_NAMES = new Set(["operator", "hostd"]);
|
|
15087
17160
|
function isReservedAgentName(name) {
|
|
@@ -15114,18 +17187,18 @@ import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
|
|
|
15114
17187
|
import { mkdir, chmod, chown, unlink, appendFile } from "node:fs/promises";
|
|
15115
17188
|
import {
|
|
15116
17189
|
readdirSync as readdirSync2,
|
|
15117
|
-
existsSync as
|
|
15118
|
-
readFileSync as
|
|
17190
|
+
existsSync as existsSync8,
|
|
17191
|
+
readFileSync as readFileSync6,
|
|
15119
17192
|
writeFileSync as writeFileSync3,
|
|
15120
|
-
renameSync,
|
|
17193
|
+
renameSync as renameSync2,
|
|
15121
17194
|
mkdirSync as mkdirSync2,
|
|
15122
|
-
openSync as
|
|
17195
|
+
openSync as openSync3,
|
|
15123
17196
|
ftruncateSync,
|
|
15124
|
-
writeSync,
|
|
15125
|
-
fsyncSync,
|
|
15126
|
-
closeSync as
|
|
17197
|
+
writeSync as writeSync2,
|
|
17198
|
+
fsyncSync as fsyncSync2,
|
|
17199
|
+
closeSync as closeSync3
|
|
15127
17200
|
} from "node:fs";
|
|
15128
|
-
import { join as
|
|
17201
|
+
import { join as join5, dirname as dirname6, resolve as resolve6 } from "node:path";
|
|
15129
17202
|
import { createHash as createHash5, randomUUID as randomUUID2, randomBytes } from "node:crypto";
|
|
15130
17203
|
|
|
15131
17204
|
// src/host-control/protocol.ts
|
|
@@ -15178,6 +17251,16 @@ var ApplyRequestSchema = exports_external.object({
|
|
|
15178
17251
|
op: exports_external.literal("apply"),
|
|
15179
17252
|
args: exports_external.object({}).optional()
|
|
15180
17253
|
});
|
|
17254
|
+
var RolloutRequestSchema = exports_external.object({
|
|
17255
|
+
...RequestEnvelope,
|
|
17256
|
+
op: exports_external.literal("rollout"),
|
|
17257
|
+
args: exports_external.object({
|
|
17258
|
+
pin: exports_external.string().regex(/^v\d+\.\d+\.\d+$/),
|
|
17259
|
+
agents: exports_external.array(AgentNameSchema).min(1).optional(),
|
|
17260
|
+
skip_web: exports_external.boolean().optional(),
|
|
17261
|
+
allow_downgrade: exports_external.boolean().optional()
|
|
17262
|
+
}).required({ pin: true })
|
|
17263
|
+
});
|
|
15181
17264
|
var AgentStartRequestSchema = exports_external.object({
|
|
15182
17265
|
...RequestEnvelope,
|
|
15183
17266
|
op: exports_external.literal("agent_start"),
|
|
@@ -15251,6 +17334,7 @@ var RequestSchema = exports_external.discriminatedUnion("op", [
|
|
|
15251
17334
|
UpdateCheckRequestSchema,
|
|
15252
17335
|
UpdateApplyRequestSchema,
|
|
15253
17336
|
ApplyRequestSchema,
|
|
17337
|
+
RolloutRequestSchema,
|
|
15254
17338
|
AgentStartRequestSchema,
|
|
15255
17339
|
AgentStopRequestSchema,
|
|
15256
17340
|
AgentLogsRequestSchema,
|
|
@@ -20442,63 +22526,309 @@ function redactedMarker(ruleId) {
|
|
|
20442
22526
|
if (!trimmed || trimmed === "key_value" || trimmed === "kv_entropy") {
|
|
20443
22527
|
return REDACTED_MARKER;
|
|
20444
22528
|
}
|
|
20445
|
-
return `[REDACTED:${trimmed}]`;
|
|
20446
|
-
}
|
|
20447
|
-
// src/cli/install-detect.ts
|
|
20448
|
-
import * as fs from "node:fs";
|
|
20449
|
-
import * as path from "node:path";
|
|
20450
|
-
import * as os from "node:os";
|
|
20451
|
-
var BIN_PATH = "/usr/local/bin/switchroom";
|
|
20452
|
-
function sourceArtifactPath() {
|
|
20453
|
-
return path.join(os.homedir(), "code", "switchroom", "dist", "cli", "switchroom.js");
|
|
20454
|
-
}
|
|
20455
|
-
function sourceDistPrefix() {
|
|
20456
|
-
return path.join(os.homedir(), "code", "switchroom", "dist") + path.sep;
|
|
22529
|
+
return `[REDACTED:${trimmed}]`;
|
|
22530
|
+
}
|
|
22531
|
+
// src/cli/install-detect.ts
|
|
22532
|
+
import * as fs from "node:fs";
|
|
22533
|
+
import * as path from "node:path";
|
|
22534
|
+
import * as os from "node:os";
|
|
22535
|
+
var BIN_PATH = "/usr/local/bin/switchroom";
|
|
22536
|
+
function sourceArtifactPath() {
|
|
22537
|
+
return path.join(os.homedir(), "code", "switchroom", "dist", "cli", "switchroom.js");
|
|
22538
|
+
}
|
|
22539
|
+
function sourceDistPrefix() {
|
|
22540
|
+
return path.join(os.homedir(), "code", "switchroom", "dist") + path.sep;
|
|
22541
|
+
}
|
|
22542
|
+
function detectInstallType() {
|
|
22543
|
+
try {
|
|
22544
|
+
const repoArtifact = sourceArtifactPath();
|
|
22545
|
+
const distPrefix = sourceDistPrefix();
|
|
22546
|
+
const binExists = fs.existsSync(BIN_PATH);
|
|
22547
|
+
const repoExists = fs.existsSync(repoArtifact);
|
|
22548
|
+
if (binExists) {
|
|
22549
|
+
const lst = fs.lstatSync(BIN_PATH);
|
|
22550
|
+
if (lst.isSymbolicLink()) {
|
|
22551
|
+
const target = fs.readlinkSync(BIN_PATH);
|
|
22552
|
+
const resolved = path.isAbsolute(target) ? target : path.resolve(path.dirname(BIN_PATH), target);
|
|
22553
|
+
if (resolved.startsWith(distPrefix)) {
|
|
22554
|
+
return {
|
|
22555
|
+
install_type: "source",
|
|
22556
|
+
source_paths: { bin: BIN_PATH, repo: repoExists ? repoArtifact : undefined }
|
|
22557
|
+
};
|
|
22558
|
+
}
|
|
22559
|
+
return {
|
|
22560
|
+
install_type: "binary",
|
|
22561
|
+
source_paths: { bin: BIN_PATH }
|
|
22562
|
+
};
|
|
22563
|
+
}
|
|
22564
|
+
return {
|
|
22565
|
+
install_type: "binary",
|
|
22566
|
+
source_paths: { bin: BIN_PATH }
|
|
22567
|
+
};
|
|
22568
|
+
}
|
|
22569
|
+
if (repoExists) {
|
|
22570
|
+
return {
|
|
22571
|
+
install_type: "source-unlinked",
|
|
22572
|
+
source_paths: { repo: repoArtifact }
|
|
22573
|
+
};
|
|
22574
|
+
}
|
|
22575
|
+
return { install_type: "docker", source_paths: {} };
|
|
22576
|
+
} catch {
|
|
22577
|
+
return { install_type: "unknown", source_paths: {} };
|
|
22578
|
+
}
|
|
22579
|
+
}
|
|
22580
|
+
|
|
22581
|
+
// src/util/atomic.ts
|
|
22582
|
+
import { closeSync as closeSync2, constants, fsyncSync, openSync as openSync2, renameSync, rmSync, writeSync } from "node:fs";
|
|
22583
|
+
var TMP_OPEN_FLAGS = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0);
|
|
22584
|
+
|
|
22585
|
+
// src/cli/resolve-version.ts
|
|
22586
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
|
|
22587
|
+
import { dirname as dirname4, join as join2 } from "node:path";
|
|
22588
|
+
|
|
22589
|
+
// src/build-info.ts
|
|
22590
|
+
var VERSION = "0.16.5";
|
|
22591
|
+
|
|
22592
|
+
// src/cli/resolve-version.ts
|
|
22593
|
+
function readPackageVersion() {
|
|
22594
|
+
let dir = import.meta.dirname;
|
|
22595
|
+
for (let i = 0;i < 12 && dir && dir !== "/"; i++) {
|
|
22596
|
+
const p = join2(dir, "package.json");
|
|
22597
|
+
if (existsSync6(p)) {
|
|
22598
|
+
try {
|
|
22599
|
+
const pkg = JSON.parse(readFileSync4(p, "utf-8"));
|
|
22600
|
+
if (pkg?.name === "switchroom" && typeof pkg.version === "string") {
|
|
22601
|
+
return pkg.version;
|
|
22602
|
+
}
|
|
22603
|
+
} catch {}
|
|
22604
|
+
}
|
|
22605
|
+
dir = dirname4(dir);
|
|
22606
|
+
}
|
|
22607
|
+
return null;
|
|
22608
|
+
}
|
|
22609
|
+
var SWITCHROOM_VERSION = (() => {
|
|
22610
|
+
const pkgVersion = readPackageVersion();
|
|
22611
|
+
if (pkgVersion !== null && pkgVersion === VERSION) {
|
|
22612
|
+
return VERSION;
|
|
22613
|
+
}
|
|
22614
|
+
return VERSION ?? pkgVersion ?? "0.0.0";
|
|
22615
|
+
})();
|
|
22616
|
+
|
|
22617
|
+
// src/host-control/audit-reader.ts
|
|
22618
|
+
function parseAuditLine(line) {
|
|
22619
|
+
const trimmed = line.trim();
|
|
22620
|
+
if (trimmed.length === 0)
|
|
22621
|
+
return null;
|
|
22622
|
+
let obj;
|
|
22623
|
+
try {
|
|
22624
|
+
obj = JSON.parse(trimmed);
|
|
22625
|
+
} catch {
|
|
22626
|
+
return null;
|
|
22627
|
+
}
|
|
22628
|
+
if (typeof obj !== "object" || obj === null)
|
|
22629
|
+
return null;
|
|
22630
|
+
const o = obj;
|
|
22631
|
+
if (typeof o.ts !== "string" || typeof o.op !== "string")
|
|
22632
|
+
return null;
|
|
22633
|
+
if (typeof o.request_id !== "string" || typeof o.result !== "string")
|
|
22634
|
+
return null;
|
|
22635
|
+
if (typeof o.duration_ms !== "number")
|
|
22636
|
+
return null;
|
|
22637
|
+
const callerRaw = o.caller;
|
|
22638
|
+
let caller;
|
|
22639
|
+
if (callerRaw && callerRaw.kind === "agent" && typeof callerRaw.name === "string") {
|
|
22640
|
+
caller = { kind: "agent", name: callerRaw.name };
|
|
22641
|
+
} else if (callerRaw && callerRaw.kind === "operator") {
|
|
22642
|
+
caller = { kind: "operator" };
|
|
22643
|
+
} else {
|
|
22644
|
+
return null;
|
|
22645
|
+
}
|
|
22646
|
+
const exit_code = o.exit_code === null || typeof o.exit_code === "number" ? o.exit_code : null;
|
|
22647
|
+
const entry = {
|
|
22648
|
+
ts: o.ts,
|
|
22649
|
+
op: o.op,
|
|
22650
|
+
caller,
|
|
22651
|
+
request_id: o.request_id,
|
|
22652
|
+
result: o.result,
|
|
22653
|
+
exit_code,
|
|
22654
|
+
duration_ms: o.duration_ms
|
|
22655
|
+
};
|
|
22656
|
+
if (typeof o.error === "string")
|
|
22657
|
+
entry.error = o.error;
|
|
22658
|
+
if (typeof o.phase === "string")
|
|
22659
|
+
entry.phase = o.phase;
|
|
22660
|
+
if (typeof o.stdout_tail === "string")
|
|
22661
|
+
entry.stdout_tail = o.stdout_tail;
|
|
22662
|
+
if (typeof o.stderr_tail === "string")
|
|
22663
|
+
entry.stderr_tail = o.stderr_tail;
|
|
22664
|
+
if (typeof o.channel === "string")
|
|
22665
|
+
entry.channel = o.channel;
|
|
22666
|
+
if (typeof o.pin === "string")
|
|
22667
|
+
entry.pin = o.pin;
|
|
22668
|
+
if (o.resolved_sha && typeof o.resolved_sha === "object" && !Array.isArray(o.resolved_sha)) {
|
|
22669
|
+
const rs = {};
|
|
22670
|
+
for (const [k, v] of Object.entries(o.resolved_sha)) {
|
|
22671
|
+
if (typeof v === "string")
|
|
22672
|
+
rs[k] = v;
|
|
22673
|
+
}
|
|
22674
|
+
if (Object.keys(rs).length > 0)
|
|
22675
|
+
entry.resolved_sha = rs;
|
|
22676
|
+
}
|
|
22677
|
+
if (o.install_context && typeof o.install_context === "object" && !Array.isArray(o.install_context)) {
|
|
22678
|
+
const ic = o.install_context;
|
|
22679
|
+
if (typeof ic.install_type === "string" && typeof ic.detected_at === "string") {
|
|
22680
|
+
entry.install_context = {
|
|
22681
|
+
install_type: ic.install_type,
|
|
22682
|
+
detected_at: ic.detected_at
|
|
22683
|
+
};
|
|
22684
|
+
}
|
|
22685
|
+
}
|
|
22686
|
+
if (Array.isArray(o.rolled) && o.rolled.every((x) => typeof x === "string")) {
|
|
22687
|
+
entry.rolled = o.rolled;
|
|
22688
|
+
}
|
|
22689
|
+
if (typeof o.failed_step === "string")
|
|
22690
|
+
entry.failed_step = o.failed_step;
|
|
22691
|
+
if (typeof o.failed_agent === "string")
|
|
22692
|
+
entry.failed_agent = o.failed_agent;
|
|
22693
|
+
if (typeof o.prior_pin === "string")
|
|
22694
|
+
entry.prior_pin = o.prior_pin;
|
|
22695
|
+
return entry;
|
|
22696
|
+
}
|
|
22697
|
+
|
|
22698
|
+
// src/cli/rollout.ts
|
|
22699
|
+
function isVersionAssertable(target) {
|
|
22700
|
+
return /^v?\d+\.\d+\.\d+$/.test(target.trim());
|
|
22701
|
+
}
|
|
22702
|
+
var ROLLOUT_RESULT_SENTINEL = "SWITCHROOM_ROLLOUT_RESULT:";
|
|
22703
|
+
function parseRolloutResultLine(stdout) {
|
|
22704
|
+
const lines = stdout.split(`
|
|
22705
|
+
`);
|
|
22706
|
+
for (let i = lines.length - 1;i >= 0; i--) {
|
|
22707
|
+
const line = lines[i].trim();
|
|
22708
|
+
if (line.startsWith(ROLLOUT_RESULT_SENTINEL)) {
|
|
22709
|
+
try {
|
|
22710
|
+
return JSON.parse(line.slice(ROLLOUT_RESULT_SENTINEL.length));
|
|
22711
|
+
} catch {
|
|
22712
|
+
return null;
|
|
22713
|
+
}
|
|
22714
|
+
}
|
|
22715
|
+
}
|
|
22716
|
+
return null;
|
|
22717
|
+
}
|
|
22718
|
+
|
|
22719
|
+
// node_modules/.bun/commander@13.1.0/node_modules/commander/esm.mjs
|
|
22720
|
+
var import__3 = __toESM(require_commander(), 1);
|
|
22721
|
+
|
|
22722
|
+
// src/cli/update.ts
|
|
22723
|
+
import { join as join3, dirname as dirname5, resolve as resolve5 } from "node:path";
|
|
22724
|
+
import { homedir as homedir3 } from "node:os";
|
|
22725
|
+
|
|
22726
|
+
// src/agents/lifecycle.ts
|
|
22727
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
22728
|
+
|
|
22729
|
+
// src/agents/tmux.ts
|
|
22730
|
+
var MAX_BYTES = 10 * 1024 * 1024;
|
|
22731
|
+
|
|
22732
|
+
// src/agents/lifecycle.ts
|
|
22733
|
+
function containerName(name) {
|
|
22734
|
+
return `switchroom-${name}`;
|
|
22735
|
+
}
|
|
22736
|
+
function getAllAgentStatuses(config) {
|
|
22737
|
+
const agentNames = Object.keys(config.agents);
|
|
22738
|
+
const statuses = {};
|
|
22739
|
+
if (agentNames.length === 0)
|
|
22740
|
+
return statuses;
|
|
22741
|
+
const cnByAgent = new Map;
|
|
22742
|
+
const agentByCn = new Map;
|
|
22743
|
+
for (const a of agentNames) {
|
|
22744
|
+
const cn = containerName(a);
|
|
22745
|
+
cnByAgent.set(a, cn);
|
|
22746
|
+
agentByCn.set(cn, a);
|
|
22747
|
+
statuses[a] = { active: "inactive", uptime: null, memory: null, pid: null };
|
|
22748
|
+
}
|
|
22749
|
+
const insp = spawnSync("docker", [
|
|
22750
|
+
"inspect",
|
|
22751
|
+
"--format",
|
|
22752
|
+
"{{.Name}}|{{.State.Status}}|{{.State.StartedAt}}|{{.State.Pid}}",
|
|
22753
|
+
...cnByAgent.values()
|
|
22754
|
+
], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 15000 });
|
|
22755
|
+
for (const line of (insp.stdout ?? "").split(`
|
|
22756
|
+
`)) {
|
|
22757
|
+
const t = line.trim();
|
|
22758
|
+
if (!t)
|
|
22759
|
+
continue;
|
|
22760
|
+
const [rawName, status, startedAt, pidStr] = t.split("|");
|
|
22761
|
+
const cn = (rawName ?? "").replace(/^\//, "");
|
|
22762
|
+
const agent = agentByCn.get(cn);
|
|
22763
|
+
if (!agent)
|
|
22764
|
+
continue;
|
|
22765
|
+
const pidNum = parseInt(pidStr ?? "", 10);
|
|
22766
|
+
statuses[agent] = {
|
|
22767
|
+
active: status === "running" ? "active" : status || "inactive",
|
|
22768
|
+
uptime: startedAt && startedAt !== "0001-01-01T00:00:00Z" ? startedAt : null,
|
|
22769
|
+
memory: null,
|
|
22770
|
+
pid: Number.isFinite(pidNum) && pidNum > 0 ? pidNum : null
|
|
22771
|
+
};
|
|
22772
|
+
}
|
|
22773
|
+
const stats = spawnSync("docker", ["stats", "--no-stream", "--format", "{{.Name}}|{{.MemUsage}}"], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 15000 });
|
|
22774
|
+
if (stats.status === 0) {
|
|
22775
|
+
for (const line of (stats.stdout ?? "").split(`
|
|
22776
|
+
`)) {
|
|
22777
|
+
const t = line.trim();
|
|
22778
|
+
if (!t)
|
|
22779
|
+
continue;
|
|
22780
|
+
const [cn, memUsage] = t.split("|");
|
|
22781
|
+
const agent = cn ? agentByCn.get(cn) : undefined;
|
|
22782
|
+
if (!agent || statuses[agent].active !== "active")
|
|
22783
|
+
continue;
|
|
22784
|
+
const first = (memUsage ?? "").split("/")[0]?.trim();
|
|
22785
|
+
if (!first)
|
|
22786
|
+
continue;
|
|
22787
|
+
const m = first.match(/([\d.]+)\s*([KMG]i?B)/i);
|
|
22788
|
+
if (m) {
|
|
22789
|
+
const val = parseFloat(m[1]);
|
|
22790
|
+
const unit = m[2].toUpperCase();
|
|
22791
|
+
let mb = val;
|
|
22792
|
+
if (unit.startsWith("K"))
|
|
22793
|
+
mb = val / 1024;
|
|
22794
|
+
else if (unit.startsWith("G"))
|
|
22795
|
+
mb = val * 1024;
|
|
22796
|
+
statuses[agent].memory = `${Math.round(mb)}MB`;
|
|
22797
|
+
} else {
|
|
22798
|
+
statuses[agent].memory = first;
|
|
22799
|
+
}
|
|
22800
|
+
}
|
|
22801
|
+
}
|
|
22802
|
+
return statuses;
|
|
20457
22803
|
}
|
|
20458
|
-
|
|
22804
|
+
|
|
22805
|
+
// src/cli/update.ts
|
|
22806
|
+
var DEFAULT_COMPOSE_PATH = join3(homedir3(), ".switchroom", "compose", "docker-compose.yml");
|
|
22807
|
+
var UPDATE_RESULT_SENTINEL = "SWITCHROOM_UPDATE_RESULT:";
|
|
22808
|
+
function parseUpdateResultLine(stdout) {
|
|
22809
|
+
const idx = stdout.lastIndexOf(UPDATE_RESULT_SENTINEL);
|
|
22810
|
+
if (idx === -1)
|
|
22811
|
+
return null;
|
|
22812
|
+
const raw = stdout.slice(idx + UPDATE_RESULT_SENTINEL.length).split(`
|
|
22813
|
+
`)[0];
|
|
22814
|
+
if (!raw)
|
|
22815
|
+
return null;
|
|
20459
22816
|
try {
|
|
20460
|
-
const
|
|
20461
|
-
|
|
20462
|
-
|
|
20463
|
-
const repoExists = fs.existsSync(repoArtifact);
|
|
20464
|
-
if (binExists) {
|
|
20465
|
-
const lst = fs.lstatSync(BIN_PATH);
|
|
20466
|
-
if (lst.isSymbolicLink()) {
|
|
20467
|
-
const target = fs.readlinkSync(BIN_PATH);
|
|
20468
|
-
const resolved = path.isAbsolute(target) ? target : path.resolve(path.dirname(BIN_PATH), target);
|
|
20469
|
-
if (resolved.startsWith(distPrefix)) {
|
|
20470
|
-
return {
|
|
20471
|
-
install_type: "source",
|
|
20472
|
-
source_paths: { bin: BIN_PATH, repo: repoExists ? repoArtifact : undefined }
|
|
20473
|
-
};
|
|
20474
|
-
}
|
|
20475
|
-
return {
|
|
20476
|
-
install_type: "binary",
|
|
20477
|
-
source_paths: { bin: BIN_PATH }
|
|
20478
|
-
};
|
|
20479
|
-
}
|
|
20480
|
-
return {
|
|
20481
|
-
install_type: "binary",
|
|
20482
|
-
source_paths: { bin: BIN_PATH }
|
|
20483
|
-
};
|
|
20484
|
-
}
|
|
20485
|
-
if (repoExists) {
|
|
20486
|
-
return {
|
|
20487
|
-
install_type: "source-unlinked",
|
|
20488
|
-
source_paths: { repo: repoArtifact }
|
|
20489
|
-
};
|
|
22817
|
+
const parsed = JSON.parse(raw);
|
|
22818
|
+
if (parsed !== null && typeof parsed === "object" && "ok" in parsed && "deferred" in parsed && typeof parsed.ok === "boolean" && Array.isArray(parsed.deferred)) {
|
|
22819
|
+
return parsed;
|
|
20490
22820
|
}
|
|
20491
|
-
return
|
|
22821
|
+
return null;
|
|
20492
22822
|
} catch {
|
|
20493
|
-
return
|
|
22823
|
+
return null;
|
|
20494
22824
|
}
|
|
20495
22825
|
}
|
|
20496
22826
|
|
|
20497
22827
|
// src/host-control/config-edit-validator.ts
|
|
20498
|
-
import { mkdtempSync, writeFileSync as writeFileSync2, rmSync, existsSync as
|
|
22828
|
+
import { mkdtempSync, writeFileSync as writeFileSync2, rmSync as rmSync2, existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
|
|
20499
22829
|
import { tmpdir } from "node:os";
|
|
20500
|
-
import { join as
|
|
20501
|
-
import { spawnSync } from "node:child_process";
|
|
22830
|
+
import { join as join4, isAbsolute as isAbsolute2, normalize } from "node:path";
|
|
22831
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
20502
22832
|
import { isDeepStrictEqual } from "node:util";
|
|
20503
22833
|
var MAX_PATCH_BYTES = 1024 * 1024;
|
|
20504
22834
|
var UNLOCK_CARD_YAML_ALLOWLIST = new Set([
|
|
@@ -20583,20 +22913,20 @@ function validateShape(unifiedDiff, targetPath) {
|
|
|
20583
22913
|
return null;
|
|
20584
22914
|
}
|
|
20585
22915
|
function applyPatch(unifiedDiff, configPath, gitBin) {
|
|
20586
|
-
if (!
|
|
22916
|
+
if (!existsSync7(configPath)) {
|
|
20587
22917
|
return {
|
|
20588
22918
|
ok: false,
|
|
20589
22919
|
code: "E_PATCH_APPLY_FAILED",
|
|
20590
22920
|
detail: `target config not found at ${configPath}`
|
|
20591
22921
|
};
|
|
20592
22922
|
}
|
|
20593
|
-
const liveContent =
|
|
20594
|
-
const scratchDir = mkdtempSync(
|
|
22923
|
+
const liveContent = readFileSync5(configPath, "utf8");
|
|
22924
|
+
const scratchDir = mkdtempSync(join4(tmpdir(), "config-propose-edit-"));
|
|
20595
22925
|
try {
|
|
20596
22926
|
const basename2 = configPath.split("/").pop() ?? "switchroom.yaml";
|
|
20597
|
-
const scratchFile =
|
|
22927
|
+
const scratchFile = join4(scratchDir, basename2);
|
|
20598
22928
|
writeFileSync2(scratchFile, liveContent);
|
|
20599
|
-
const patchFile =
|
|
22929
|
+
const patchFile = join4(scratchDir, "proposal.patch");
|
|
20600
22930
|
writeFileSync2(patchFile, unifiedDiff);
|
|
20601
22931
|
const bin = gitBin ?? "git";
|
|
20602
22932
|
const baseArgs = [
|
|
@@ -20605,10 +22935,10 @@ function applyPatch(unifiedDiff, configPath, gitBin) {
|
|
|
20605
22935
|
"--recount",
|
|
20606
22936
|
"--unidiff-zero"
|
|
20607
22937
|
];
|
|
20608
|
-
const checkP1 =
|
|
22938
|
+
const checkP1 = spawnSync2(bin, [...baseArgs.slice(0, 1), "--check", ...baseArgs.slice(1), "-p1", patchFile], { cwd: scratchDir, encoding: "utf8", timeout: 1e4 });
|
|
20609
22939
|
let pStrip = "-p1";
|
|
20610
22940
|
if (checkP1.status !== 0) {
|
|
20611
|
-
const checkP0 =
|
|
22941
|
+
const checkP0 = spawnSync2(bin, [...baseArgs.slice(0, 1), "--check", ...baseArgs.slice(1), "-p0", patchFile], { cwd: scratchDir, encoding: "utf8", timeout: 1e4 });
|
|
20612
22942
|
if (checkP0.status !== 0) {
|
|
20613
22943
|
const stderr = (checkP1.stderr || "") + (checkP0.stderr || "");
|
|
20614
22944
|
return {
|
|
@@ -20619,7 +22949,7 @@ function applyPatch(unifiedDiff, configPath, gitBin) {
|
|
|
20619
22949
|
}
|
|
20620
22950
|
pStrip = "-p0";
|
|
20621
22951
|
}
|
|
20622
|
-
const real =
|
|
22952
|
+
const real = spawnSync2(bin, [...baseArgs, pStrip, patchFile], { cwd: scratchDir, encoding: "utf8", timeout: 1e4 });
|
|
20623
22953
|
if (real.status !== 0) {
|
|
20624
22954
|
return {
|
|
20625
22955
|
ok: false,
|
|
@@ -20627,10 +22957,10 @@ function applyPatch(unifiedDiff, configPath, gitBin) {
|
|
|
20627
22957
|
detail: `git apply failed: ${(real.stderr || "").trim().slice(0, 500)}`
|
|
20628
22958
|
};
|
|
20629
22959
|
}
|
|
20630
|
-
const after =
|
|
22960
|
+
const after = readFileSync5(scratchFile, "utf8");
|
|
20631
22961
|
return { ok: true, after };
|
|
20632
22962
|
} finally {
|
|
20633
|
-
|
|
22963
|
+
rmSync2(scratchDir, { recursive: true, force: true });
|
|
20634
22964
|
}
|
|
20635
22965
|
}
|
|
20636
22966
|
function failsafeParse(source) {
|
|
@@ -20825,7 +23155,7 @@ function validateConfigEdit(opts) {
|
|
|
20825
23155
|
return schemaErr;
|
|
20826
23156
|
let beforeData = {};
|
|
20827
23157
|
try {
|
|
20828
|
-
const beforeRaw =
|
|
23158
|
+
const beforeRaw = existsSync7(opts.configPath) ? readFileSync5(opts.configPath, "utf8") : "";
|
|
20829
23159
|
const beforeDoc = $parseDocument(beforeRaw, { merge: false, strict: false });
|
|
20830
23160
|
beforeData = beforeDoc.toJS();
|
|
20831
23161
|
} catch {
|
|
@@ -20978,85 +23308,6 @@ function classifyBlastRadius(beforeYaml, afterYaml) {
|
|
|
20978
23308
|
};
|
|
20979
23309
|
}
|
|
20980
23310
|
|
|
20981
|
-
// src/agents/lifecycle.ts
|
|
20982
|
-
import { execFileSync, spawn, spawnSync as spawnSync2 } from "node:child_process";
|
|
20983
|
-
|
|
20984
|
-
// src/agents/tmux.ts
|
|
20985
|
-
var MAX_BYTES = 10 * 1024 * 1024;
|
|
20986
|
-
|
|
20987
|
-
// src/agents/lifecycle.ts
|
|
20988
|
-
function containerName(name) {
|
|
20989
|
-
return `switchroom-${name}`;
|
|
20990
|
-
}
|
|
20991
|
-
function getAllAgentStatuses(config) {
|
|
20992
|
-
const agentNames = Object.keys(config.agents);
|
|
20993
|
-
const statuses = {};
|
|
20994
|
-
if (agentNames.length === 0)
|
|
20995
|
-
return statuses;
|
|
20996
|
-
const cnByAgent = new Map;
|
|
20997
|
-
const agentByCn = new Map;
|
|
20998
|
-
for (const a of agentNames) {
|
|
20999
|
-
const cn = containerName(a);
|
|
21000
|
-
cnByAgent.set(a, cn);
|
|
21001
|
-
agentByCn.set(cn, a);
|
|
21002
|
-
statuses[a] = { active: "inactive", uptime: null, memory: null, pid: null };
|
|
21003
|
-
}
|
|
21004
|
-
const insp = spawnSync2("docker", [
|
|
21005
|
-
"inspect",
|
|
21006
|
-
"--format",
|
|
21007
|
-
"{{.Name}}|{{.State.Status}}|{{.State.StartedAt}}|{{.State.Pid}}",
|
|
21008
|
-
...cnByAgent.values()
|
|
21009
|
-
], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 15000 });
|
|
21010
|
-
for (const line of (insp.stdout ?? "").split(`
|
|
21011
|
-
`)) {
|
|
21012
|
-
const t = line.trim();
|
|
21013
|
-
if (!t)
|
|
21014
|
-
continue;
|
|
21015
|
-
const [rawName, status, startedAt, pidStr] = t.split("|");
|
|
21016
|
-
const cn = (rawName ?? "").replace(/^\//, "");
|
|
21017
|
-
const agent = agentByCn.get(cn);
|
|
21018
|
-
if (!agent)
|
|
21019
|
-
continue;
|
|
21020
|
-
const pidNum = parseInt(pidStr ?? "", 10);
|
|
21021
|
-
statuses[agent] = {
|
|
21022
|
-
active: status === "running" ? "active" : status || "inactive",
|
|
21023
|
-
uptime: startedAt && startedAt !== "0001-01-01T00:00:00Z" ? startedAt : null,
|
|
21024
|
-
memory: null,
|
|
21025
|
-
pid: Number.isFinite(pidNum) && pidNum > 0 ? pidNum : null
|
|
21026
|
-
};
|
|
21027
|
-
}
|
|
21028
|
-
const stats = spawnSync2("docker", ["stats", "--no-stream", "--format", "{{.Name}}|{{.MemUsage}}"], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 15000 });
|
|
21029
|
-
if (stats.status === 0) {
|
|
21030
|
-
for (const line of (stats.stdout ?? "").split(`
|
|
21031
|
-
`)) {
|
|
21032
|
-
const t = line.trim();
|
|
21033
|
-
if (!t)
|
|
21034
|
-
continue;
|
|
21035
|
-
const [cn, memUsage] = t.split("|");
|
|
21036
|
-
const agent = cn ? agentByCn.get(cn) : undefined;
|
|
21037
|
-
if (!agent || statuses[agent].active !== "active")
|
|
21038
|
-
continue;
|
|
21039
|
-
const first = (memUsage ?? "").split("/")[0]?.trim();
|
|
21040
|
-
if (!first)
|
|
21041
|
-
continue;
|
|
21042
|
-
const m = first.match(/([\d.]+)\s*([KMG]i?B)/i);
|
|
21043
|
-
if (m) {
|
|
21044
|
-
const val = parseFloat(m[1]);
|
|
21045
|
-
const unit = m[2].toUpperCase();
|
|
21046
|
-
let mb = val;
|
|
21047
|
-
if (unit.startsWith("K"))
|
|
21048
|
-
mb = val / 1024;
|
|
21049
|
-
else if (unit.startsWith("G"))
|
|
21050
|
-
mb = val * 1024;
|
|
21051
|
-
statuses[agent].memory = `${Math.round(mb)}MB`;
|
|
21052
|
-
} else {
|
|
21053
|
-
statuses[agent].memory = first;
|
|
21054
|
-
}
|
|
21055
|
-
}
|
|
21056
|
-
}
|
|
21057
|
-
return statuses;
|
|
21058
|
-
}
|
|
21059
|
-
|
|
21060
23311
|
// src/scheduler/dispatch.ts
|
|
21061
23312
|
import { createHash as createHash4 } from "node:crypto";
|
|
21062
23313
|
function collectScheduleEntries(config) {
|
|
@@ -21139,11 +23390,11 @@ function resolveDigests(imageRefs) {
|
|
|
21139
23390
|
return out;
|
|
21140
23391
|
}
|
|
21141
23392
|
function readCachedInstallType(bindRoot) {
|
|
21142
|
-
const cacheDir =
|
|
21143
|
-
const cachePath =
|
|
21144
|
-
if (
|
|
23393
|
+
const cacheDir = join5(bindRoot, ".switchroom");
|
|
23394
|
+
const cachePath = join5(cacheDir, "install-type.json");
|
|
23395
|
+
if (existsSync8(cachePath)) {
|
|
21145
23396
|
try {
|
|
21146
|
-
const raw =
|
|
23397
|
+
const raw = readFileSync6(cachePath, "utf-8");
|
|
21147
23398
|
const parsed = JSON.parse(raw);
|
|
21148
23399
|
if (parsed && typeof parsed.install_type === "string" && typeof parsed.detected_at === "string") {
|
|
21149
23400
|
return parsed;
|
|
@@ -21163,7 +23414,7 @@ function readCachedInstallType(bindRoot) {
|
|
|
21163
23414
|
mkdirSync2(cacheDir, { recursive: true });
|
|
21164
23415
|
const tmp = `${cachePath}.tmp`;
|
|
21165
23416
|
writeFileSync3(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
|
|
21166
|
-
|
|
23417
|
+
renameSync2(tmp, cachePath);
|
|
21167
23418
|
} catch {}
|
|
21168
23419
|
return payload;
|
|
21169
23420
|
}
|
|
@@ -21181,18 +23432,18 @@ function formatConfigApprovalDenyError(approval, approvalId) {
|
|
|
21181
23432
|
}
|
|
21182
23433
|
function writeFileInPlacePreservingInode(targetPath, content) {
|
|
21183
23434
|
const buf = Buffer.from(content, "utf-8");
|
|
21184
|
-
const fd =
|
|
23435
|
+
const fd = openSync3(targetPath, "r+");
|
|
21185
23436
|
try {
|
|
21186
23437
|
ftruncateSync(fd, 0);
|
|
21187
23438
|
let off = 0;
|
|
21188
23439
|
while (off < buf.length) {
|
|
21189
|
-
off +=
|
|
23440
|
+
off += writeSync2(fd, buf, off, buf.length - off, off);
|
|
21190
23441
|
}
|
|
21191
|
-
|
|
23442
|
+
fsyncSync2(fd);
|
|
21192
23443
|
} finally {
|
|
21193
|
-
|
|
23444
|
+
closeSync3(fd);
|
|
21194
23445
|
}
|
|
21195
|
-
const readBack =
|
|
23446
|
+
const readBack = readFileSync6(targetPath);
|
|
21196
23447
|
if (readBack.length !== buf.length) {
|
|
21197
23448
|
throw new Error(`in-place write short: wrote ${buf.length} bytes but read back ${readBack.length}`);
|
|
21198
23449
|
}
|
|
@@ -21200,6 +23451,7 @@ function writeFileInPlacePreservingInode(targetPath, content) {
|
|
|
21200
23451
|
var STATUS_RETENTION_MS = 10 * 60 * 1000;
|
|
21201
23452
|
var STATUS_MAX_ENTRIES = 256;
|
|
21202
23453
|
var TAIL_BYTES = 4096;
|
|
23454
|
+
var ORPHAN_RECONCILE_AGE_MS = 15 * 60 * 1000;
|
|
21203
23455
|
|
|
21204
23456
|
class HostdServer {
|
|
21205
23457
|
opts;
|
|
@@ -21213,7 +23465,7 @@ class HostdServer {
|
|
|
21213
23465
|
this.opts = opts;
|
|
21214
23466
|
}
|
|
21215
23467
|
async start() {
|
|
21216
|
-
const hostdDir =
|
|
23468
|
+
const hostdDir = join5(this.opts.homeDir, ".switchroom", "hostd");
|
|
21217
23469
|
await mkdir(hostdDir, { recursive: true });
|
|
21218
23470
|
await chmod(hostdDir, 493).catch(() => {
|
|
21219
23471
|
return;
|
|
@@ -21224,13 +23476,13 @@ class HostdServer {
|
|
|
21224
23476
|
}
|
|
21225
23477
|
try {
|
|
21226
23478
|
for (const name of agentNames) {
|
|
21227
|
-
const dir =
|
|
21228
|
-
const sockPath =
|
|
23479
|
+
const dir = join5(hostdDir, name);
|
|
23480
|
+
const sockPath = join5(dir, "sock");
|
|
21229
23481
|
await mkdir(dir, { recursive: true });
|
|
21230
23482
|
await chmod(dir, 493).catch(() => {
|
|
21231
23483
|
return;
|
|
21232
23484
|
});
|
|
21233
|
-
if (
|
|
23485
|
+
if (existsSync8(sockPath))
|
|
21234
23486
|
await unlink(sockPath).catch(() => {
|
|
21235
23487
|
return;
|
|
21236
23488
|
});
|
|
@@ -21239,8 +23491,8 @@ class HostdServer {
|
|
|
21239
23491
|
process.stderr.write(`hostd: server error on ${sockPath}: ${err2.message}
|
|
21240
23492
|
`);
|
|
21241
23493
|
});
|
|
21242
|
-
await new Promise((
|
|
21243
|
-
server.listen(sockPath, () =>
|
|
23494
|
+
await new Promise((resolve7, reject) => {
|
|
23495
|
+
server.listen(sockPath, () => resolve7());
|
|
21244
23496
|
server.once("error", reject);
|
|
21245
23497
|
});
|
|
21246
23498
|
await chmod(sockPath, 432).catch(() => {
|
|
@@ -21261,13 +23513,13 @@ class HostdServer {
|
|
|
21261
23513
|
process.stderr.write(`hostd: SWITCHROOM_HOSTD_OPERATOR_UID='${opUidStr}' is not a positive integer; skipping operator listener
|
|
21262
23514
|
`);
|
|
21263
23515
|
} else {
|
|
21264
|
-
const dir =
|
|
21265
|
-
const sockPath =
|
|
23516
|
+
const dir = join5(hostdDir, "operator");
|
|
23517
|
+
const sockPath = join5(dir, "sock");
|
|
21266
23518
|
await mkdir(dir, { recursive: true });
|
|
21267
23519
|
await chmod(dir, 493).catch(() => {
|
|
21268
23520
|
return;
|
|
21269
23521
|
});
|
|
21270
|
-
if (
|
|
23522
|
+
if (existsSync8(sockPath))
|
|
21271
23523
|
await unlink(sockPath).catch(() => {
|
|
21272
23524
|
return;
|
|
21273
23525
|
});
|
|
@@ -21276,8 +23528,8 @@ class HostdServer {
|
|
|
21276
23528
|
process.stderr.write(`hostd: server error on ${sockPath}: ${err2.message}
|
|
21277
23529
|
`);
|
|
21278
23530
|
});
|
|
21279
|
-
await new Promise((
|
|
21280
|
-
server.listen(sockPath, () =>
|
|
23531
|
+
await new Promise((resolve7, reject) => {
|
|
23532
|
+
server.listen(sockPath, () => resolve7());
|
|
21281
23533
|
server.once("error", reject);
|
|
21282
23534
|
});
|
|
21283
23535
|
await chmod(sockPath, 384).catch(() => {
|
|
@@ -21296,11 +23548,72 @@ class HostdServer {
|
|
|
21296
23548
|
await this.stop();
|
|
21297
23549
|
throw err2;
|
|
21298
23550
|
}
|
|
23551
|
+
await this.reconcileOrphanedFleetMutations().catch((e) => {
|
|
23552
|
+
process.stderr.write(`hostd: orphan-reconcile sweep failed (non-fatal): ${e.message}
|
|
23553
|
+
`);
|
|
23554
|
+
});
|
|
23555
|
+
}
|
|
23556
|
+
async reconcileOrphanedFleetMutations() {
|
|
23557
|
+
const path2 = this.auditLogPath();
|
|
23558
|
+
if (!existsSync8(path2))
|
|
23559
|
+
return;
|
|
23560
|
+
let raw;
|
|
23561
|
+
try {
|
|
23562
|
+
raw = readFileSync6(path2, "utf-8");
|
|
23563
|
+
} catch {
|
|
23564
|
+
return;
|
|
23565
|
+
}
|
|
23566
|
+
const FLEET_OPS = new Set(["update_apply", "apply", "rollout"]);
|
|
23567
|
+
const startedRows = new Map;
|
|
23568
|
+
for (const line of raw.split(`
|
|
23569
|
+
`)) {
|
|
23570
|
+
const e = parseAuditLine(line);
|
|
23571
|
+
if (!e)
|
|
23572
|
+
continue;
|
|
23573
|
+
if (e.phase === "orphan_reconciled") {
|
|
23574
|
+
startedRows.delete(e.request_id);
|
|
23575
|
+
continue;
|
|
23576
|
+
}
|
|
23577
|
+
if (!FLEET_OPS.has(e.op))
|
|
23578
|
+
continue;
|
|
23579
|
+
if (e.phase === "terminal") {
|
|
23580
|
+
startedRows.delete(e.request_id);
|
|
23581
|
+
continue;
|
|
23582
|
+
}
|
|
23583
|
+
if (e.result === "started" && e.phase === undefined) {
|
|
23584
|
+
const tsMs = Date.parse(e.ts);
|
|
23585
|
+
startedRows.set(e.request_id, {
|
|
23586
|
+
op: e.op,
|
|
23587
|
+
ts: Number.isFinite(tsMs) ? tsMs : Date.now(),
|
|
23588
|
+
caller_name: e.caller.kind === "agent" ? e.caller.name : undefined
|
|
23589
|
+
});
|
|
23590
|
+
}
|
|
23591
|
+
}
|
|
23592
|
+
const now = Date.now();
|
|
23593
|
+
for (const [request_id, info] of startedRows) {
|
|
23594
|
+
if (now - info.ts < ORPHAN_RECONCILE_AGE_MS)
|
|
23595
|
+
continue;
|
|
23596
|
+
const synthOp = info.op === "rollout" ? "rollout_orphaned" : "update_failed";
|
|
23597
|
+
process.stderr.write(`hostd: ORPHANED ${info.op} (request_id=${request_id}, ` + `started ${Math.floor((now - info.ts) / 60000)}m ago, no terminal ` + `row) — emitting ${synthOp}. The fleet may be half-rolled; verify ` + `host-side.
|
|
23598
|
+
`);
|
|
23599
|
+
await this.appendAuditRow({
|
|
23600
|
+
ts: new Date().toISOString(),
|
|
23601
|
+
op: synthOp,
|
|
23602
|
+
phase: "orphan_reconciled",
|
|
23603
|
+
request_id,
|
|
23604
|
+
original_op: info.op,
|
|
23605
|
+
caller: info.caller_name ? { kind: "agent", name: info.caller_name } : { kind: "operator" },
|
|
23606
|
+
result: "error",
|
|
23607
|
+
exit_code: null,
|
|
23608
|
+
duration_ms: now - info.ts,
|
|
23609
|
+
error: `${info.op} left a perpetual 'started' status with no terminal row ` + `(hostd likely SIGKILLed mid-mutation — brick scenario #1). ` + `Reconciled to a failure on hostd boot. The fleet may be ` + `half-rolled; verify versions host-side and re-run if needed.`
|
|
23610
|
+
});
|
|
23611
|
+
}
|
|
21299
23612
|
}
|
|
21300
23613
|
async stop() {
|
|
21301
23614
|
const paths = [...this.servers.keys()];
|
|
21302
23615
|
for (const [, server] of this.servers) {
|
|
21303
|
-
await new Promise((
|
|
23616
|
+
await new Promise((resolve7) => server.close(() => resolve7()));
|
|
21304
23617
|
}
|
|
21305
23618
|
this.servers.clear();
|
|
21306
23619
|
for (const s of paths) {
|
|
@@ -21406,6 +23719,9 @@ class HostdServer {
|
|
|
21406
23719
|
case "apply":
|
|
21407
23720
|
resp = this.handleApply(req, caller, started);
|
|
21408
23721
|
break;
|
|
23722
|
+
case "rollout":
|
|
23723
|
+
resp = this.handleRollout(req, caller, started);
|
|
23724
|
+
break;
|
|
21409
23725
|
case "agent_start":
|
|
21410
23726
|
resp = await this.handleAgentStart(req, started);
|
|
21411
23727
|
break;
|
|
@@ -21468,6 +23784,7 @@ class HostdServer {
|
|
|
21468
23784
|
return null;
|
|
21469
23785
|
case "update_apply":
|
|
21470
23786
|
case "apply":
|
|
23787
|
+
case "rollout":
|
|
21471
23788
|
return callerAdmin ? null : `${req.op} requires admin: true on caller "${caller.name}"`;
|
|
21472
23789
|
case "agent_start":
|
|
21473
23790
|
case "agent_stop":
|
|
@@ -21567,12 +23884,12 @@ class HostdServer {
|
|
|
21567
23884
|
};
|
|
21568
23885
|
}
|
|
21569
23886
|
missingApplyAssets() {
|
|
21570
|
-
const root = this.opts.applyAssetsRoot ??
|
|
23887
|
+
const root = this.opts.applyAssetsRoot ?? resolve6(import.meta.dirname, "../..");
|
|
21571
23888
|
return [
|
|
21572
|
-
|
|
21573
|
-
|
|
21574
|
-
|
|
21575
|
-
].filter((p) => !
|
|
23889
|
+
join5(root, "profiles"),
|
|
23890
|
+
join5(root, "profiles", "default"),
|
|
23891
|
+
join5(root, "vendor", "hindsight-memory")
|
|
23892
|
+
].filter((p) => !existsSync8(p));
|
|
21576
23893
|
}
|
|
21577
23894
|
applyAssetPreflight(request_id, started) {
|
|
21578
23895
|
const missing = this.missingApplyAssets();
|
|
@@ -21629,7 +23946,7 @@ class HostdServer {
|
|
|
21629
23946
|
request_id: req.request_id,
|
|
21630
23947
|
started_at: started
|
|
21631
23948
|
};
|
|
21632
|
-
this.spawnFleetMutation(req.op, args, entry);
|
|
23949
|
+
this.spawnFleetMutation(req.op, args, entry, { SWITCHROOM_HOSTD_CONTEXT: "1" });
|
|
21633
23950
|
return {
|
|
21634
23951
|
v: 1,
|
|
21635
23952
|
request_id: req.request_id,
|
|
@@ -21672,6 +23989,62 @@ class HostdServer {
|
|
|
21672
23989
|
duration_ms: Date.now() - started
|
|
21673
23990
|
};
|
|
21674
23991
|
}
|
|
23992
|
+
handleRollout(req, caller, started) {
|
|
23993
|
+
const denied = this.checkFleetMutationLock(req.op, req.request_id, started);
|
|
23994
|
+
if (denied)
|
|
23995
|
+
return denied;
|
|
23996
|
+
const assetDenied = this.applyAssetPreflight(req.request_id, started);
|
|
23997
|
+
if (assetDenied)
|
|
23998
|
+
return assetDenied;
|
|
23999
|
+
const args = ["rollout", "--pin", req.args.pin];
|
|
24000
|
+
if (req.args.agents && req.args.agents.length > 0) {
|
|
24001
|
+
args.push("--agents", req.args.agents.join(","));
|
|
24002
|
+
}
|
|
24003
|
+
if (req.args.skip_web)
|
|
24004
|
+
args.push("--skip-web");
|
|
24005
|
+
if (req.args.allow_downgrade)
|
|
24006
|
+
args.push("--allow-downgrade");
|
|
24007
|
+
const installCtx = readCachedInstallType(this.opts.bindRoot ?? this.opts.homeDir);
|
|
24008
|
+
let priorPin;
|
|
24009
|
+
try {
|
|
24010
|
+
const cfg = loadConfig(this.opts.configPath);
|
|
24011
|
+
const cfgPin = cfg.release?.pin;
|
|
24012
|
+
if (cfgPin && isVersionAssertable(cfgPin)) {
|
|
24013
|
+
priorPin = cfgPin.startsWith("v") ? cfgPin : `v${cfgPin}`;
|
|
24014
|
+
}
|
|
24015
|
+
} catch {}
|
|
24016
|
+
const entry = {
|
|
24017
|
+
request_id: req.request_id,
|
|
24018
|
+
caller,
|
|
24019
|
+
op: req.op,
|
|
24020
|
+
result: "started",
|
|
24021
|
+
exit_code: null,
|
|
24022
|
+
started_at: started,
|
|
24023
|
+
finished_at: null,
|
|
24024
|
+
stdout_tail: "",
|
|
24025
|
+
stderr_tail: "",
|
|
24026
|
+
pin: req.args.pin,
|
|
24027
|
+
install_context: {
|
|
24028
|
+
install_type: installCtx.install_type,
|
|
24029
|
+
detected_at: installCtx.detected_at
|
|
24030
|
+
},
|
|
24031
|
+
...priorPin ? { prior_pin: priorPin } : {}
|
|
24032
|
+
};
|
|
24033
|
+
this.recordStatus(entry);
|
|
24034
|
+
this.fleetMutationInFlight = {
|
|
24035
|
+
op: "rollout",
|
|
24036
|
+
request_id: req.request_id,
|
|
24037
|
+
started_at: started
|
|
24038
|
+
};
|
|
24039
|
+
this.spawnRollout(args, entry);
|
|
24040
|
+
return {
|
|
24041
|
+
v: 1,
|
|
24042
|
+
request_id: req.request_id,
|
|
24043
|
+
result: "started",
|
|
24044
|
+
exit_code: null,
|
|
24045
|
+
duration_ms: Date.now() - started
|
|
24046
|
+
};
|
|
24047
|
+
}
|
|
21675
24048
|
async handleAgentStart(req, started) {
|
|
21676
24049
|
const res = await this.runSwitchroom(["agent", "start", req.args.name]);
|
|
21677
24050
|
return {
|
|
@@ -21753,7 +24126,7 @@ class HostdServer {
|
|
|
21753
24126
|
if (caller.kind === "agent" && this.opts.config.agents[caller.name]?.admin !== true) {
|
|
21754
24127
|
let beforeContent;
|
|
21755
24128
|
try {
|
|
21756
|
-
beforeContent =
|
|
24129
|
+
beforeContent = readFileSync6(configPath, "utf-8");
|
|
21757
24130
|
} catch {
|
|
21758
24131
|
beforeContent = "";
|
|
21759
24132
|
}
|
|
@@ -21775,6 +24148,34 @@ class HostdServer {
|
|
|
21775
24148
|
`);
|
|
21776
24149
|
return await pending;
|
|
21777
24150
|
}
|
|
24151
|
+
const rate = this.checkConfigEditRate(callerName, Date.now());
|
|
24152
|
+
if (!rate.ok) {
|
|
24153
|
+
const retryAtIso = new Date(rate.retryAtMs).toISOString();
|
|
24154
|
+
process.stderr.write(`hostd: config_propose_edit — RATE-LIMITED ${callerName} ` + `(>${rate.limit}/hour); next slot ${retryAtIso}
|
|
24155
|
+
`);
|
|
24156
|
+
this.appendAuditRow({
|
|
24157
|
+
ts: new Date().toISOString(),
|
|
24158
|
+
op: "config_propose_edit",
|
|
24159
|
+
phase: "rate_limited",
|
|
24160
|
+
request_id: req.request_id,
|
|
24161
|
+
caller: caller.kind === "agent" ? { kind: "agent", name: caller.name } : { kind: "operator" },
|
|
24162
|
+
result: "denied",
|
|
24163
|
+
exit_code: null,
|
|
24164
|
+
duration_ms: Date.now() - started,
|
|
24165
|
+
error: `E_RATE_LIMITED: >${rate.limit} config_propose_edit cards/hour`
|
|
24166
|
+
});
|
|
24167
|
+
return err("E_RATE_LIMITED", `config_propose_edit rate limit exceeded (max ${rate.limit}/hour for this agent)`).why(`next slot opens at ${retryAtIso}`).fixRetryAfter(retryAtIso).op("config_propose_edit").caller(caller.kind === "agent" ? "agent" : "operator").agentName(caller.kind === "agent" ? caller.name : undefined).asDenied().build(req.request_id, Date.now() - started);
|
|
24168
|
+
}
|
|
24169
|
+
this.appendAuditRow({
|
|
24170
|
+
ts: new Date().toISOString(),
|
|
24171
|
+
op: "config_propose_edit",
|
|
24172
|
+
phase: "requested",
|
|
24173
|
+
request_id: req.request_id,
|
|
24174
|
+
caller: caller.kind === "agent" ? { kind: "agent", name: caller.name } : { kind: "operator" },
|
|
24175
|
+
result: "started",
|
|
24176
|
+
exit_code: null,
|
|
24177
|
+
duration_ms: Date.now() - started
|
|
24178
|
+
});
|
|
21778
24179
|
const run = this.runConfigProposeApprovalAndApply(req, caller, callerName, configPath, verdict.postApplyContent, started);
|
|
21779
24180
|
this.inflightConfigProposals.set(dedupeKey, run);
|
|
21780
24181
|
try {
|
|
@@ -21784,6 +24185,22 @@ class HostdServer {
|
|
|
21784
24185
|
}
|
|
21785
24186
|
}
|
|
21786
24187
|
inflightConfigProposals = new Map;
|
|
24188
|
+
configEditPostTimes = new Map;
|
|
24189
|
+
static DEFAULT_CONFIG_EDIT_RATE_PER_HOUR = 3;
|
|
24190
|
+
checkConfigEditRate(callerName, now) {
|
|
24191
|
+
const limit = this.opts.config.hostd?.config_edit_rate_per_hour ?? HostdServer.DEFAULT_CONFIG_EDIT_RATE_PER_HOUR;
|
|
24192
|
+
const windowMs = 60 * 60 * 1000;
|
|
24193
|
+
const cutoff = now - windowMs;
|
|
24194
|
+
const prior = (this.configEditPostTimes.get(callerName) ?? []).filter((t) => t > cutoff);
|
|
24195
|
+
if (prior.length >= limit) {
|
|
24196
|
+
const retryAtMs = prior[0] + windowMs;
|
|
24197
|
+
this.configEditPostTimes.set(callerName, prior);
|
|
24198
|
+
return { ok: false, limit, retryAtMs };
|
|
24199
|
+
}
|
|
24200
|
+
prior.push(now);
|
|
24201
|
+
this.configEditPostTimes.set(callerName, prior);
|
|
24202
|
+
return { ok: true };
|
|
24203
|
+
}
|
|
21787
24204
|
async runConfigProposeApprovalAndApply(req, caller, callerName, configPath, postApply, started) {
|
|
21788
24205
|
const approvalId = (this.opts.generateApprovalId ?? defaultApprovalId)();
|
|
21789
24206
|
const approval = await this.opts.approvalGateway.requestApproval({
|
|
@@ -21813,7 +24230,7 @@ class HostdServer {
|
|
|
21813
24230
|
try {
|
|
21814
24231
|
let snapshot;
|
|
21815
24232
|
try {
|
|
21816
|
-
snapshot =
|
|
24233
|
+
snapshot = readFileSync6(configPath, "utf-8");
|
|
21817
24234
|
} catch (e) {
|
|
21818
24235
|
await approval.finalize({
|
|
21819
24236
|
outcome: "reconcile_failed_rolled_back",
|
|
@@ -22007,7 +24424,7 @@ class HostdServer {
|
|
|
22007
24424
|
const agentsDir = resolveAgentsDir(cfg);
|
|
22008
24425
|
const recentByAgent = {};
|
|
22009
24426
|
for (const agent of new Set(entries.map((e) => e.agent))) {
|
|
22010
|
-
const rows = readRecentFires(
|
|
24427
|
+
const rows = readRecentFires(resolve6(agentsDir, agent, "scheduler.jsonl"));
|
|
22011
24428
|
if (rows.length > 0)
|
|
22012
24429
|
recentByAgent[agent] = rows;
|
|
22013
24430
|
}
|
|
@@ -22032,7 +24449,7 @@ class HostdServer {
|
|
|
22032
24449
|
}
|
|
22033
24450
|
}
|
|
22034
24451
|
runDocker(args) {
|
|
22035
|
-
return new Promise((
|
|
24452
|
+
return new Promise((resolve7, reject) => {
|
|
22036
24453
|
const bin = this.opts.dockerBin ?? "docker";
|
|
22037
24454
|
const child = spawn2(bin, args, {
|
|
22038
24455
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -22047,15 +24464,15 @@ class HostdServer {
|
|
|
22047
24464
|
stderr += d.toString("utf8");
|
|
22048
24465
|
});
|
|
22049
24466
|
child.on("error", (err2) => reject(err2));
|
|
22050
|
-
child.on("close", (code) =>
|
|
24467
|
+
child.on("close", (code) => resolve7({ exit_code: code ?? -1, stdout, stderr }));
|
|
22051
24468
|
});
|
|
22052
24469
|
}
|
|
22053
24470
|
imageRefsForDigestCapture() {
|
|
22054
24471
|
if (this.opts.imageRefsForDigests)
|
|
22055
24472
|
return this.opts.imageRefsForDigests();
|
|
22056
24473
|
try {
|
|
22057
|
-
const composePath =
|
|
22058
|
-
if (!
|
|
24474
|
+
const composePath = join5(this.opts.bindRoot ?? this.opts.homeDir, ".switchroom", "compose", "docker-compose.yml");
|
|
24475
|
+
if (!existsSync8(composePath))
|
|
22059
24476
|
return [];
|
|
22060
24477
|
const r = spawnSync3("docker", [
|
|
22061
24478
|
"compose",
|
|
@@ -22081,18 +24498,59 @@ class HostdServer {
|
|
|
22081
24498
|
const ageMs = Date.now() - inFlight.started_at;
|
|
22082
24499
|
return deniedResponse(request_id, `${op}: fleet-mutation lock held by ${inFlight.op} ` + `(request_id "${inFlight.request_id}", running ${Math.floor(ageMs / 1000)}s). ` + `Wait for it to complete (poll get_status with target_request_id="${inFlight.request_id}") ` + `before issuing another fleet mutation.`, Date.now() - started);
|
|
22083
24500
|
}
|
|
22084
|
-
spawnFleetMutation(op, args, entry) {
|
|
22085
|
-
this.runSwitchroom(args).then((res) => {
|
|
24501
|
+
spawnFleetMutation(op, args, entry, extraEnv) {
|
|
24502
|
+
this.runSwitchroom(args, extraEnv).then((res) => {
|
|
22086
24503
|
entry.result = res.exit_code === 0 ? "completed" : "error";
|
|
22087
24504
|
entry.exit_code = res.exit_code;
|
|
22088
24505
|
entry.finished_at = Date.now();
|
|
22089
24506
|
entry.stdout_tail = tail(res.stdout);
|
|
22090
24507
|
entry.stderr_tail = tail(res.stderr);
|
|
24508
|
+
if (op === "update_apply") {
|
|
24509
|
+
const parsed = parseUpdateResultLine(res.stdout);
|
|
24510
|
+
if (parsed && parsed.deferred.length > 0) {
|
|
24511
|
+
entry.deferred = parsed.deferred;
|
|
24512
|
+
}
|
|
24513
|
+
}
|
|
24514
|
+
}).catch((err2) => {
|
|
24515
|
+
entry.result = "error";
|
|
24516
|
+
entry.exit_code = null;
|
|
24517
|
+
entry.finished_at = Date.now();
|
|
24518
|
+
entry.error = err2.message;
|
|
24519
|
+
}).finally(() => {
|
|
24520
|
+
this.writeTerminalAudit(entry);
|
|
24521
|
+
if (this.fleetMutationInFlight && this.fleetMutationInFlight.request_id === entry.request_id) {
|
|
24522
|
+
this.fleetMutationInFlight = null;
|
|
24523
|
+
}
|
|
24524
|
+
});
|
|
24525
|
+
}
|
|
24526
|
+
spawnRollout(args, entry) {
|
|
24527
|
+
this.runSwitchroom(args, { SWITCHROOM_HOSTD_CONTEXT: "1" }).then((res) => {
|
|
24528
|
+
entry.exit_code = res.exit_code;
|
|
24529
|
+
entry.finished_at = Date.now();
|
|
24530
|
+
entry.stdout_tail = tail(res.stdout);
|
|
24531
|
+
entry.stderr_tail = tail(res.stderr);
|
|
24532
|
+
const parsed = parseRolloutResultLine(res.stdout);
|
|
24533
|
+
if (parsed) {
|
|
24534
|
+
entry.rolled = parsed.rolled;
|
|
24535
|
+
if (parsed.failedStep)
|
|
24536
|
+
entry.failed_step = parsed.failedStep;
|
|
24537
|
+
if (parsed.failedAgent)
|
|
24538
|
+
entry.failed_agent = parsed.failedAgent;
|
|
24539
|
+
if (parsed.got !== undefined)
|
|
24540
|
+
entry.got = parsed.got;
|
|
24541
|
+
entry.result = parsed.ok ? "completed" : "error";
|
|
24542
|
+
} else {
|
|
24543
|
+
entry.result = res.exit_code === 0 ? "completed" : "error";
|
|
24544
|
+
}
|
|
24545
|
+
if (entry.result !== "completed") {
|
|
24546
|
+
delete entry.prior_pin;
|
|
24547
|
+
}
|
|
22091
24548
|
}).catch((err2) => {
|
|
22092
24549
|
entry.result = "error";
|
|
22093
24550
|
entry.exit_code = null;
|
|
22094
24551
|
entry.finished_at = Date.now();
|
|
22095
24552
|
entry.error = err2.message;
|
|
24553
|
+
delete entry.prior_pin;
|
|
22096
24554
|
}).finally(() => {
|
|
22097
24555
|
this.writeTerminalAudit(entry);
|
|
22098
24556
|
if (this.fleetMutationInFlight && this.fleetMutationInFlight.request_id === entry.request_id) {
|
|
@@ -22110,6 +24568,19 @@ class HostdServer {
|
|
|
22110
24568
|
return this.statusEntryToResponse(req.request_id, entry);
|
|
22111
24569
|
}
|
|
22112
24570
|
statusEntryToResponse(request_id, entry) {
|
|
24571
|
+
const rolloutPayload = entry.op === "rollout" && (entry.rolled !== undefined || entry.failed_step !== undefined || entry.failed_agent !== undefined) ? JSON.stringify({
|
|
24572
|
+
rolled: entry.rolled ?? [],
|
|
24573
|
+
...entry.failed_step ? { failedStep: entry.failed_step } : {},
|
|
24574
|
+
...entry.failed_agent ? { failedAgent: entry.failed_agent } : {},
|
|
24575
|
+
...entry.got !== undefined ? { got: entry.got } : {},
|
|
24576
|
+
...entry.pin ? { pin: entry.pin } : {}
|
|
24577
|
+
}) : undefined;
|
|
24578
|
+
const updatePayload = entry.op === "update_apply" && (entry.deferred !== undefined || entry.channel !== undefined || entry.pin !== undefined) ? {
|
|
24579
|
+
...entry.deferred !== undefined ? { deferred: entry.deferred } : {},
|
|
24580
|
+
...entry.pin !== undefined ? { pin: entry.pin } : {},
|
|
24581
|
+
...entry.channel !== undefined ? { channel: entry.channel } : {}
|
|
24582
|
+
} : null;
|
|
24583
|
+
const payload = rolloutPayload ?? (updatePayload ? JSON.stringify(updatePayload) : undefined);
|
|
22113
24584
|
return {
|
|
22114
24585
|
v: 1,
|
|
22115
24586
|
request_id,
|
|
@@ -22118,6 +24589,7 @@ class HostdServer {
|
|
|
22118
24589
|
duration_ms: (entry.finished_at ?? Date.now()) - entry.started_at,
|
|
22119
24590
|
stdout_tail: entry.stdout_tail || undefined,
|
|
22120
24591
|
stderr_tail: entry.stderr_tail || undefined,
|
|
24592
|
+
...payload ? { payload } : {},
|
|
22121
24593
|
error: entry.error
|
|
22122
24594
|
};
|
|
22123
24595
|
}
|
|
@@ -22141,12 +24613,12 @@ class HostdServer {
|
|
|
22141
24613
|
}
|
|
22142
24614
|
}
|
|
22143
24615
|
auditLogPath() {
|
|
22144
|
-
return this.opts.auditLogPath ??
|
|
24616
|
+
return this.opts.auditLogPath ?? join5(this.opts.homeDir, ".switchroom", "host-control-audit.log");
|
|
22145
24617
|
}
|
|
22146
24618
|
appendAuditRow(row) {
|
|
22147
24619
|
const path2 = this.auditLogPath();
|
|
22148
24620
|
this.auditAppendChain = this.auditAppendChain.then(async () => {
|
|
22149
|
-
await mkdir(
|
|
24621
|
+
await mkdir(dirname6(path2), { recursive: true }).catch(() => {
|
|
22150
24622
|
return;
|
|
22151
24623
|
});
|
|
22152
24624
|
if (this.auditChainState === undefined) {
|
|
@@ -22194,15 +24666,21 @@ class HostdServer {
|
|
|
22194
24666
|
...entry.channel ? { channel: entry.channel } : {},
|
|
22195
24667
|
...entry.pin ? { pin: entry.pin } : {},
|
|
22196
24668
|
...entry.resolved_sha ? { resolved_sha: entry.resolved_sha } : {},
|
|
22197
|
-
...entry.install_context ? { install_context: entry.install_context } : {}
|
|
24669
|
+
...entry.install_context ? { install_context: entry.install_context } : {},
|
|
24670
|
+
...entry.rolled ? { rolled: entry.rolled } : {},
|
|
24671
|
+
...entry.failed_step ? { failed_step: entry.failed_step } : {},
|
|
24672
|
+
...entry.failed_agent ? { failed_agent: entry.failed_agent } : {},
|
|
24673
|
+
...entry.got !== undefined ? { got: entry.got } : {},
|
|
24674
|
+
...entry.deferred ? { deferred: entry.deferred } : {},
|
|
24675
|
+
...entry.prior_pin ? { prior_pin: entry.prior_pin } : {}
|
|
22198
24676
|
});
|
|
22199
24677
|
}
|
|
22200
|
-
runSwitchroom(args) {
|
|
22201
|
-
return new Promise((
|
|
24678
|
+
runSwitchroom(args, extraEnv) {
|
|
24679
|
+
return new Promise((resolve7, reject) => {
|
|
22202
24680
|
const bin = this.opts.switchroomBin ?? "switchroom";
|
|
22203
24681
|
const child = spawn2(bin, args, {
|
|
22204
24682
|
stdio: ["ignore", "pipe", "pipe"],
|
|
22205
|
-
env: { ...process.env }
|
|
24683
|
+
env: { ...process.env, ...extraEnv ?? {} }
|
|
22206
24684
|
});
|
|
22207
24685
|
let stdout = "";
|
|
22208
24686
|
let stderr = "";
|
|
@@ -22213,7 +24691,7 @@ class HostdServer {
|
|
|
22213
24691
|
stderr += d.toString("utf8");
|
|
22214
24692
|
});
|
|
22215
24693
|
child.on("error", (err2) => reject(err2));
|
|
22216
|
-
child.on("close", (code) =>
|
|
24694
|
+
child.on("close", (code) => resolve7({ exit_code: code ?? -1, stdout, stderr }));
|
|
22217
24695
|
});
|
|
22218
24696
|
}
|
|
22219
24697
|
}
|
|
@@ -22341,7 +24819,7 @@ class SocketApprovalGateway {
|
|
|
22341
24819
|
finalize: async () => {}
|
|
22342
24820
|
};
|
|
22343
24821
|
}
|
|
22344
|
-
return await new Promise((
|
|
24822
|
+
return await new Promise((resolve7) => {
|
|
22345
24823
|
const client2 = connect({ path: sockPath });
|
|
22346
24824
|
let buffer = "";
|
|
22347
24825
|
let resolved = false;
|
|
@@ -22380,7 +24858,7 @@ class SocketApprovalGateway {
|
|
|
22380
24858
|
return;
|
|
22381
24859
|
resolved = true;
|
|
22382
24860
|
log(`request_config_approval write failed (requestId=${req.requestId}): ${err2.message}`);
|
|
22383
|
-
|
|
24861
|
+
resolve7({
|
|
22384
24862
|
verdict: "deny",
|
|
22385
24863
|
reason: `request_config_approval write failed: ${err2.message}`,
|
|
22386
24864
|
denySource: "dispatch_failure",
|
|
@@ -22409,7 +24887,7 @@ class SocketApprovalGateway {
|
|
|
22409
24887
|
const verdict = obj.verdict;
|
|
22410
24888
|
const reasonField = typeof obj.reason === "string" ? obj.reason : undefined;
|
|
22411
24889
|
const denySource = verdict === "deny" ? obj.denySource === "dispatch_failure" ? "dispatch_failure" : "operator" : undefined;
|
|
22412
|
-
|
|
24890
|
+
resolve7({
|
|
22413
24891
|
verdict,
|
|
22414
24892
|
...reasonField !== undefined ? { reason: reasonField } : {},
|
|
22415
24893
|
...denySource !== undefined ? { denySource } : {},
|
|
@@ -22423,7 +24901,7 @@ class SocketApprovalGateway {
|
|
|
22423
24901
|
return;
|
|
22424
24902
|
resolved = true;
|
|
22425
24903
|
log(`gateway socket error (requestId=${req.requestId}): ${err2.message}`);
|
|
22426
|
-
|
|
24904
|
+
resolve7({
|
|
22427
24905
|
verdict: "deny",
|
|
22428
24906
|
reason: `gateway socket error: ${err2.message}`,
|
|
22429
24907
|
denySource: "dispatch_failure",
|
|
@@ -22434,7 +24912,7 @@ class SocketApprovalGateway {
|
|
|
22434
24912
|
if (resolved)
|
|
22435
24913
|
return;
|
|
22436
24914
|
resolved = true;
|
|
22437
|
-
|
|
24915
|
+
resolve7({
|
|
22438
24916
|
verdict: "deny",
|
|
22439
24917
|
reason: "gateway socket closed before verdict",
|
|
22440
24918
|
denySource: "dispatch_failure",
|
|
@@ -22709,17 +25187,17 @@ async function main() {
|
|
|
22709
25187
|
`);
|
|
22710
25188
|
process.exit(2);
|
|
22711
25189
|
}
|
|
22712
|
-
const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ??
|
|
25190
|
+
const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? join6(homedir4(), ".switchroom", "agents");
|
|
22713
25191
|
const approvalGateway = new SocketApprovalGateway({
|
|
22714
25192
|
resolveGatewaySocket: (agentName) => {
|
|
22715
|
-
const sock =
|
|
22716
|
-
return
|
|
25193
|
+
const sock = resolve7(agentsDir, agentName, "telegram", "gateway.sock");
|
|
25194
|
+
return existsSync9(sock) ? sock : null;
|
|
22717
25195
|
},
|
|
22718
25196
|
log: (m) => process.stderr.write(`hostd: approval-gateway — ${m}
|
|
22719
25197
|
`)
|
|
22720
25198
|
});
|
|
22721
25199
|
const server = new HostdServer({
|
|
22722
|
-
homeDir:
|
|
25200
|
+
homeDir: homedir4(),
|
|
22723
25201
|
agentUids,
|
|
22724
25202
|
config: {
|
|
22725
25203
|
agents: Object.fromEntries(Object.entries(config.agents).map(([n, a]) => [
|
|
@@ -22741,7 +25219,7 @@ async function main() {
|
|
|
22741
25219
|
let releaseWatcher = null;
|
|
22742
25220
|
const autoRel = config.host_control?.auto_release_check;
|
|
22743
25221
|
if (autoRel?.enabled === true) {
|
|
22744
|
-
const eventsLog =
|
|
25222
|
+
const eventsLog = join6(homedir4(), ".switchroom", "release-watcher-events.jsonl");
|
|
22745
25223
|
releaseWatcher = new ReleaseWatcher({
|
|
22746
25224
|
intervalMs: autoRel.interval_minutes * 60000,
|
|
22747
25225
|
checkFn: makeReleaseCheck({
|