clisbot 0.1.16 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -20
- package/config/clisbot.json.template +10 -2
- package/dist/main.js +1136 -161
- package/package.json +1 -1
- package/templates/slack/default/app-manifest.json +2 -3
package/dist/main.js
CHANGED
|
@@ -54471,14 +54471,22 @@ function renderPairingSetupHelpLines(prefix = "", options = {}) {
|
|
|
54471
54471
|
return lines;
|
|
54472
54472
|
}
|
|
54473
54473
|
lines.push(`${prefix}Pairing notes:`);
|
|
54474
|
+
if (shouldRenderSlack && shouldRenderTelegram) {
|
|
54475
|
+
lines.push(`${prefix} - Send a direct message (DM) to the Telegram or Slack bot. Send \`/start\` or \`hi\` to receive a pairing code.`);
|
|
54476
|
+
}
|
|
54474
54477
|
if (shouldRenderTelegram) {
|
|
54475
|
-
|
|
54478
|
+
if (!shouldRenderSlack) {
|
|
54479
|
+
lines.push(`${prefix} - Send a direct message (DM) to the Telegram bot. Send \`/start\` or \`hi\` to receive a pairing code.`);
|
|
54480
|
+
}
|
|
54476
54481
|
lines.push(`${prefix} - Approve the returned Telegram code with: \`clisbot pairing approve telegram <code>\``);
|
|
54477
54482
|
}
|
|
54478
54483
|
if (shouldRenderSlack) {
|
|
54479
|
-
|
|
54484
|
+
if (!shouldRenderTelegram) {
|
|
54485
|
+
lines.push(`${prefix} - Send a direct message (DM) to the Slack bot. Say \`hi\` to receive a pairing code.`);
|
|
54486
|
+
}
|
|
54480
54487
|
lines.push(`${prefix} - Approve the returned Slack code with: \`clisbot pairing approve slack <code>\``);
|
|
54481
54488
|
}
|
|
54489
|
+
lines.push(`${prefix} - Configured app owner/admin principals bypass pairing in DMs.`);
|
|
54482
54490
|
return lines;
|
|
54483
54491
|
}
|
|
54484
54492
|
function renderTmuxDebugHelpLines(prefix = "") {
|
|
@@ -54585,6 +54593,12 @@ function parseCliArgs(argv) {
|
|
|
54585
54593
|
args: args.slice(1)
|
|
54586
54594
|
};
|
|
54587
54595
|
}
|
|
54596
|
+
if (command === "auth") {
|
|
54597
|
+
return {
|
|
54598
|
+
name: "auth",
|
|
54599
|
+
args: args.slice(1)
|
|
54600
|
+
};
|
|
54601
|
+
}
|
|
54588
54602
|
if (command === "pairing") {
|
|
54589
54603
|
return {
|
|
54590
54604
|
name: "pairing",
|
|
@@ -54607,6 +54621,10 @@ function renderCliHelp() {
|
|
|
54607
54621
|
return [
|
|
54608
54622
|
`clisbot v${getClisbotVersion()}`,
|
|
54609
54623
|
"",
|
|
54624
|
+
"Platform support:",
|
|
54625
|
+
" Linux/macOS Supported.",
|
|
54626
|
+
" Windows Native Windows is not supported yet. Use WSL2.",
|
|
54627
|
+
"",
|
|
54610
54628
|
"Fastest start:",
|
|
54611
54629
|
" 1. Choose the channels you want to bootstrap explicitly.",
|
|
54612
54630
|
" 2. Run one of these commands:",
|
|
@@ -54639,6 +54657,7 @@ function renderCliHelp() {
|
|
|
54639
54657
|
" clisbot loops <subcommand>",
|
|
54640
54658
|
" clisbot message <subcommand>",
|
|
54641
54659
|
" clisbot agents <subcommand>",
|
|
54660
|
+
" clisbot auth <subcommand>",
|
|
54642
54661
|
" clisbot pairing <subcommand>",
|
|
54643
54662
|
" clisbot init [--cli <codex|claude|gemini>] [--bot-type <personal|team>] [--persist]",
|
|
54644
54663
|
" [--slack-account <id> --slack-app-token <ENV_NAME|${ENV_NAME}|literal> --slack-bot-token <ENV_NAME|${ENV_NAME}|literal>]...",
|
|
@@ -54675,6 +54694,7 @@ function renderCliHelp() {
|
|
|
54675
54694
|
" cancel --all",
|
|
54676
54695
|
" message Run provider message actions such as send, react, read, edit, delete, and pins.",
|
|
54677
54696
|
" agents Manage configured agents and top-level bindings.",
|
|
54697
|
+
" auth Manage app and agent auth roles, principals, and permissions in config. See `clisbot auth --help`.",
|
|
54678
54698
|
" pairing Run the pairing control CLI.",
|
|
54679
54699
|
` init Seed ${configPath} and optionally create the first agent without starting clisbot.`,
|
|
54680
54700
|
" --version, -v Show the installed clisbot version.",
|
|
@@ -59856,6 +59876,10 @@ var authRoleSchema = exports_external.object({
|
|
|
59856
59876
|
allow: exports_external.array(exports_external.string().min(1)).default([]),
|
|
59857
59877
|
users: exports_external.array(exports_external.string().min(1)).default([])
|
|
59858
59878
|
});
|
|
59879
|
+
var authRoleOverrideSchema = exports_external.object({
|
|
59880
|
+
allow: exports_external.array(exports_external.string().min(1)).optional(),
|
|
59881
|
+
users: exports_external.array(exports_external.string().min(1)).optional()
|
|
59882
|
+
});
|
|
59859
59883
|
var appAuthSchema = exports_external.object({
|
|
59860
59884
|
ownerClaimWindowMinutes: exports_external.number().int().positive().default(30),
|
|
59861
59885
|
defaultRole: exports_external.string().min(1).default("member"),
|
|
@@ -59887,11 +59911,15 @@ var agentAuthSchema = exports_external.object({
|
|
|
59887
59911
|
}
|
|
59888
59912
|
})
|
|
59889
59913
|
});
|
|
59914
|
+
var agentAuthOverrideSchema = exports_external.object({
|
|
59915
|
+
defaultRole: exports_external.string().min(1).optional(),
|
|
59916
|
+
roles: exports_external.record(exports_external.string(), authRoleOverrideSchema).default({})
|
|
59917
|
+
});
|
|
59890
59918
|
var agentOverrideSchema = exports_external.object({
|
|
59891
59919
|
workspace: exports_external.string().optional(),
|
|
59892
59920
|
responseMode: exports_external.enum(["capture-pane", "message-tool"]).optional(),
|
|
59893
59921
|
additionalMessageMode: exports_external.enum(["queue", "steer"]).optional(),
|
|
59894
|
-
auth:
|
|
59922
|
+
auth: agentAuthOverrideSchema.optional(),
|
|
59895
59923
|
runner: runnerOverrideSchema.optional(),
|
|
59896
59924
|
stream: streamSchema.partial().optional(),
|
|
59897
59925
|
session: sessionSchema.partial().optional()
|
|
@@ -59986,9 +60014,18 @@ var channelAgentPromptSchema = exports_external.object({
|
|
|
59986
60014
|
var channelResponseModeSchema = exports_external.enum(["capture-pane", "message-tool"]);
|
|
59987
60015
|
var channelAdditionalMessageModeSchema = exports_external.enum(["queue", "steer"]);
|
|
59988
60016
|
var channelVerboseSchema = exports_external.enum(["off", "minimal"]);
|
|
60017
|
+
var surfaceNotificationModeSchema = exports_external.enum(["none", "brief", "full"]);
|
|
59989
60018
|
var timezoneSchema = exports_external.string().refine(isValidLoopTimezone, {
|
|
59990
60019
|
message: "Expected a valid IANA timezone such as Asia/Ho_Chi_Minh"
|
|
59991
60020
|
});
|
|
60021
|
+
var surfaceNotificationsSchema = exports_external.object({
|
|
60022
|
+
queueStart: surfaceNotificationModeSchema.default("brief"),
|
|
60023
|
+
loopStart: surfaceNotificationModeSchema.default("brief")
|
|
60024
|
+
});
|
|
60025
|
+
var surfaceNotificationsOverrideSchema = exports_external.object({
|
|
60026
|
+
queueStart: surfaceNotificationModeSchema.optional(),
|
|
60027
|
+
loopStart: surfaceNotificationModeSchema.optional()
|
|
60028
|
+
});
|
|
59992
60029
|
var slackRouteSchema = exports_external.object({
|
|
59993
60030
|
requireMention: exports_external.boolean().default(true),
|
|
59994
60031
|
allowBots: exports_external.boolean().default(false),
|
|
@@ -59999,6 +60036,7 @@ var slackRouteSchema = exports_external.object({
|
|
|
59999
60036
|
response: slackResponseSchema.optional(),
|
|
60000
60037
|
responseMode: channelResponseModeSchema.optional(),
|
|
60001
60038
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60039
|
+
surfaceNotifications: surfaceNotificationsOverrideSchema.optional(),
|
|
60002
60040
|
verbose: channelVerboseSchema.optional(),
|
|
60003
60041
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
60004
60042
|
timezone: timezoneSchema.optional()
|
|
@@ -60013,6 +60051,7 @@ var telegramTopicRouteSchema = exports_external.object({
|
|
|
60013
60051
|
response: slackResponseSchema.optional(),
|
|
60014
60052
|
responseMode: channelResponseModeSchema.optional(),
|
|
60015
60053
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60054
|
+
surfaceNotifications: surfaceNotificationsOverrideSchema.optional(),
|
|
60016
60055
|
verbose: channelVerboseSchema.optional(),
|
|
60017
60056
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
60018
60057
|
timezone: timezoneSchema.optional()
|
|
@@ -60027,6 +60066,7 @@ var telegramGroupRouteSchema = exports_external.object({
|
|
|
60027
60066
|
response: slackResponseSchema.optional(),
|
|
60028
60067
|
responseMode: channelResponseModeSchema.optional(),
|
|
60029
60068
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60069
|
+
surfaceNotifications: surfaceNotificationsOverrideSchema.optional(),
|
|
60030
60070
|
verbose: channelVerboseSchema.optional(),
|
|
60031
60071
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
60032
60072
|
timezone: timezoneSchema.optional(),
|
|
@@ -60045,6 +60085,7 @@ var telegramDirectMessagesSchema = exports_external.object({
|
|
|
60045
60085
|
response: slackResponseSchema.optional(),
|
|
60046
60086
|
responseMode: channelResponseModeSchema.optional(),
|
|
60047
60087
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60088
|
+
surfaceNotifications: surfaceNotificationsOverrideSchema.optional(),
|
|
60048
60089
|
verbose: channelVerboseSchema.optional(),
|
|
60049
60090
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
60050
60091
|
timezone: timezoneSchema.optional()
|
|
@@ -60078,10 +60119,11 @@ var telegramSchema = exports_external.object({
|
|
|
60078
60119
|
slash: ["::", "\\"],
|
|
60079
60120
|
bash: ["!"]
|
|
60080
60121
|
}),
|
|
60081
|
-
streaming: slackStreamingSchema.default("
|
|
60122
|
+
streaming: slackStreamingSchema.default("off"),
|
|
60082
60123
|
response: slackResponseSchema.default("final"),
|
|
60083
60124
|
responseMode: channelResponseModeSchema.default("message-tool"),
|
|
60084
60125
|
additionalMessageMode: channelAdditionalMessageModeSchema.default("steer"),
|
|
60126
|
+
surfaceNotifications: surfaceNotificationsSchema.optional(),
|
|
60085
60127
|
verbose: channelVerboseSchema.default("minimal"),
|
|
60086
60128
|
followUp: slackFollowUpSchema.default({
|
|
60087
60129
|
mode: "auto",
|
|
@@ -60113,6 +60155,7 @@ var directMessagesSchema = exports_external.object({
|
|
|
60113
60155
|
response: slackResponseSchema.optional(),
|
|
60114
60156
|
responseMode: channelResponseModeSchema.optional(),
|
|
60115
60157
|
additionalMessageMode: channelAdditionalMessageModeSchema.optional(),
|
|
60158
|
+
surfaceNotifications: surfaceNotificationsOverrideSchema.optional(),
|
|
60116
60159
|
verbose: channelVerboseSchema.optional(),
|
|
60117
60160
|
followUp: slackFollowUpOverrideSchema.optional(),
|
|
60118
60161
|
timezone: timezoneSchema.optional()
|
|
@@ -60153,10 +60196,11 @@ var slackSchema = exports_external.object({
|
|
|
60153
60196
|
slash: ["::", "\\"],
|
|
60154
60197
|
bash: ["!"]
|
|
60155
60198
|
}),
|
|
60156
|
-
streaming: slackStreamingSchema.default("
|
|
60199
|
+
streaming: slackStreamingSchema.default("off"),
|
|
60157
60200
|
response: slackResponseSchema.default("final"),
|
|
60158
60201
|
responseMode: channelResponseModeSchema.default("message-tool"),
|
|
60159
60202
|
additionalMessageMode: channelAdditionalMessageModeSchema.default("steer"),
|
|
60203
|
+
surfaceNotifications: surfaceNotificationsSchema.optional(),
|
|
60160
60204
|
verbose: channelVerboseSchema.default("minimal"),
|
|
60161
60205
|
followUp: slackFollowUpSchema.default({
|
|
60162
60206
|
mode: "auto",
|
|
@@ -60304,10 +60348,14 @@ var clisbotConfigSchema = exports_external.object({
|
|
|
60304
60348
|
slash: ["::", "\\"],
|
|
60305
60349
|
bash: ["!"]
|
|
60306
60350
|
},
|
|
60307
|
-
streaming: "
|
|
60351
|
+
streaming: "off",
|
|
60308
60352
|
response: "final",
|
|
60309
60353
|
responseMode: "message-tool",
|
|
60310
60354
|
additionalMessageMode: "steer",
|
|
60355
|
+
surfaceNotifications: {
|
|
60356
|
+
queueStart: "brief",
|
|
60357
|
+
loopStart: "brief"
|
|
60358
|
+
},
|
|
60311
60359
|
verbose: "minimal",
|
|
60312
60360
|
followUp: {
|
|
60313
60361
|
mode: "auto",
|
|
@@ -60621,10 +60669,14 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60621
60669
|
slash: ["::", "\\"],
|
|
60622
60670
|
bash: ["!"]
|
|
60623
60671
|
},
|
|
60624
|
-
streaming: "
|
|
60672
|
+
streaming: "off",
|
|
60625
60673
|
response: "final",
|
|
60626
60674
|
responseMode: "message-tool",
|
|
60627
60675
|
additionalMessageMode: "steer",
|
|
60676
|
+
surfaceNotifications: {
|
|
60677
|
+
queueStart: "brief",
|
|
60678
|
+
loopStart: "brief"
|
|
60679
|
+
},
|
|
60628
60680
|
verbose: "minimal",
|
|
60629
60681
|
followUp: {
|
|
60630
60682
|
mode: "auto",
|
|
@@ -60662,10 +60714,14 @@ function renderDefaultConfigTemplate(options = {}) {
|
|
|
60662
60714
|
slash: ["::", "\\"],
|
|
60663
60715
|
bash: ["!"]
|
|
60664
60716
|
},
|
|
60665
|
-
streaming: "
|
|
60717
|
+
streaming: "off",
|
|
60666
60718
|
response: "final",
|
|
60667
60719
|
responseMode: "message-tool",
|
|
60668
60720
|
additionalMessageMode: "steer",
|
|
60721
|
+
surfaceNotifications: {
|
|
60722
|
+
queueStart: "brief",
|
|
60723
|
+
loopStart: "brief"
|
|
60724
|
+
},
|
|
60669
60725
|
verbose: "minimal",
|
|
60670
60726
|
followUp: {
|
|
60671
60727
|
mode: "auto",
|
|
@@ -61834,6 +61890,10 @@ function getExecutableNames(command) {
|
|
|
61834
61890
|
// src/runners/tmux/client.ts
|
|
61835
61891
|
var MAIN_WINDOW_NAME = "main";
|
|
61836
61892
|
var TMUX_NOT_FOUND_CODE = "ENOENT";
|
|
61893
|
+
var TMUX_SERVER_DEFAULTS = [
|
|
61894
|
+
["exit-empty", "off"],
|
|
61895
|
+
["destroy-unattached", "off"]
|
|
61896
|
+
];
|
|
61837
61897
|
|
|
61838
61898
|
class TmuxClient {
|
|
61839
61899
|
socketPath;
|
|
@@ -61893,6 +61953,14 @@ class TmuxClient {
|
|
|
61893
61953
|
${result.stdout}`.trim();
|
|
61894
61954
|
return !output.includes("no server running");
|
|
61895
61955
|
}
|
|
61956
|
+
async ensureServerDefaults() {
|
|
61957
|
+
if (!await this.isServerRunning()) {
|
|
61958
|
+
return;
|
|
61959
|
+
}
|
|
61960
|
+
for (const [name, value] of TMUX_SERVER_DEFAULTS) {
|
|
61961
|
+
await this.execOrThrow(["set-option", "-g", name, value]);
|
|
61962
|
+
}
|
|
61963
|
+
}
|
|
61896
61964
|
async newSession(params) {
|
|
61897
61965
|
await this.execOrThrow([
|
|
61898
61966
|
"new-session",
|
|
@@ -61905,6 +61973,7 @@ ${result.stdout}`.trim();
|
|
|
61905
61973
|
params.cwd,
|
|
61906
61974
|
params.command
|
|
61907
61975
|
]);
|
|
61976
|
+
await this.ensureServerDefaults();
|
|
61908
61977
|
await this.freezeWindowName(`${params.sessionName}:${MAIN_WINDOW_NAME}`);
|
|
61909
61978
|
}
|
|
61910
61979
|
async newWindow(params) {
|
|
@@ -62692,40 +62761,329 @@ async function runAccountsCli(args, deps = {}) {
|
|
|
62692
62761
|
throw new Error(renderAccountsHelp());
|
|
62693
62762
|
}
|
|
62694
62763
|
|
|
62695
|
-
// src/
|
|
62696
|
-
function
|
|
62697
|
-
|
|
62698
|
-
|
|
62699
|
-
|
|
62700
|
-
|
|
62764
|
+
// src/control/auth-cli.ts
|
|
62765
|
+
function getEditableConfigPath3() {
|
|
62766
|
+
return process.env.CLISBOT_CONFIG_PATH;
|
|
62767
|
+
}
|
|
62768
|
+
function parseRepeatedOption2(args, name) {
|
|
62769
|
+
const values = [];
|
|
62770
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
62771
|
+
if (args[index] !== name) {
|
|
62772
|
+
continue;
|
|
62773
|
+
}
|
|
62774
|
+
const value = args[index + 1]?.trim();
|
|
62775
|
+
if (!value) {
|
|
62776
|
+
throw new Error(`Missing value for ${name}`);
|
|
62777
|
+
}
|
|
62778
|
+
values.push(value);
|
|
62779
|
+
}
|
|
62780
|
+
return values;
|
|
62781
|
+
}
|
|
62782
|
+
function parseSingleOption2(args, name) {
|
|
62783
|
+
const values = parseRepeatedOption2(args, name);
|
|
62784
|
+
if (values.length === 0) {
|
|
62785
|
+
return;
|
|
62786
|
+
}
|
|
62787
|
+
return values[values.length - 1];
|
|
62788
|
+
}
|
|
62789
|
+
function hasFlag3(args, name) {
|
|
62790
|
+
return args.includes(name);
|
|
62791
|
+
}
|
|
62792
|
+
function parseScope(raw, args) {
|
|
62793
|
+
if (raw === "app") {
|
|
62794
|
+
return { kind: "app" };
|
|
62795
|
+
}
|
|
62796
|
+
if (raw === "agent-defaults") {
|
|
62797
|
+
return { kind: "agent-defaults" };
|
|
62798
|
+
}
|
|
62799
|
+
if (raw === "agent") {
|
|
62800
|
+
const agentId = parseSingleOption2(args, "--agent");
|
|
62801
|
+
if (!agentId) {
|
|
62802
|
+
throw new Error("Missing value for --agent");
|
|
62803
|
+
}
|
|
62804
|
+
return { kind: "agent", agentId };
|
|
62805
|
+
}
|
|
62806
|
+
throw new Error("Scope required: app | agent-defaults | agent");
|
|
62807
|
+
}
|
|
62808
|
+
function renderAuthCliHelp() {
|
|
62809
|
+
return [
|
|
62810
|
+
"clisbot auth",
|
|
62701
62811
|
"",
|
|
62702
|
-
"
|
|
62812
|
+
"Manage auth roles, principals, and permissions in config.",
|
|
62703
62813
|
"",
|
|
62704
|
-
"
|
|
62705
|
-
|
|
62814
|
+
"Usage:",
|
|
62815
|
+
" clisbot auth list [--json]",
|
|
62816
|
+
" clisbot auth show <app|agent-defaults|agent> [--agent <id>] [--json]",
|
|
62817
|
+
" clisbot auth add-user <app|agent-defaults|agent> --role <role> --user <principal> [--agent <id>]",
|
|
62818
|
+
" clisbot auth remove-user <app|agent-defaults|agent> --role <role> --user <principal> [--agent <id>]",
|
|
62819
|
+
" clisbot auth add-permission <app|agent-defaults|agent> --role <role> --permission <permission> [--agent <id>]",
|
|
62820
|
+
" clisbot auth remove-permission <app|agent-defaults|agent> --role <role> --permission <permission> [--agent <id>]",
|
|
62706
62821
|
"",
|
|
62707
|
-
"
|
|
62708
|
-
|
|
62709
|
-
|
|
62710
|
-
|
|
62711
|
-
|
|
62822
|
+
"Scopes:",
|
|
62823
|
+
" app edit app.auth",
|
|
62824
|
+
" agent-defaults edit agents.defaults.auth",
|
|
62825
|
+
" agent edit one agents.list[].auth override; requires --agent <id>",
|
|
62826
|
+
"",
|
|
62827
|
+
"Permission sets:",
|
|
62828
|
+
` app ${APP_ADMIN_PERMISSIONS.join(", ")}`,
|
|
62829
|
+
` agent ${DEFAULT_AGENT_ADMIN_PERMISSIONS.join(", ")}`,
|
|
62830
|
+
"",
|
|
62831
|
+
"Notes:",
|
|
62832
|
+
" add-user/remove-user mutate roles.<role>.users",
|
|
62833
|
+
" add-permission/remove-permission mutate roles.<role>.allow",
|
|
62834
|
+
" agent role edits clone the inherited agent-defaults role into the target agent override on first write",
|
|
62835
|
+
"",
|
|
62836
|
+
"Examples:",
|
|
62837
|
+
" clisbot auth add-user app --role owner --user telegram:1276408333",
|
|
62838
|
+
" clisbot auth remove-user app --role admin --user slack:U123",
|
|
62839
|
+
" clisbot auth add-user agent --agent default --role admin --user slack:U123",
|
|
62840
|
+
" clisbot auth add-permission agent-defaults --role member --permission shellExecute",
|
|
62841
|
+
" clisbot auth remove-permission agent --agent default --role member --permission shellExecute",
|
|
62842
|
+
" clisbot auth show agent-defaults",
|
|
62843
|
+
" clisbot auth list --json"
|
|
62844
|
+
].join(`
|
|
62845
|
+
`);
|
|
62846
|
+
}
|
|
62847
|
+
function cloneRoleDefinition(value) {
|
|
62848
|
+
return {
|
|
62849
|
+
allow: [...value?.allow ?? []],
|
|
62850
|
+
users: [...value?.users ?? []]
|
|
62851
|
+
};
|
|
62852
|
+
}
|
|
62853
|
+
function mergeRoleDefinitions(inherited, override) {
|
|
62854
|
+
return {
|
|
62855
|
+
allow: [...override?.allow ?? inherited?.allow ?? []],
|
|
62856
|
+
users: [...override?.users ?? inherited?.users ?? []]
|
|
62857
|
+
};
|
|
62858
|
+
}
|
|
62859
|
+
function mergeRoleRecord(defaults, overrides) {
|
|
62860
|
+
const merged = {};
|
|
62861
|
+
const roleNames = new Set([
|
|
62862
|
+
...Object.keys(defaults ?? {}),
|
|
62863
|
+
...Object.keys(overrides ?? {})
|
|
62864
|
+
]);
|
|
62865
|
+
for (const roleName of roleNames) {
|
|
62866
|
+
merged[roleName] = mergeRoleDefinitions(defaults?.[roleName], overrides?.[roleName]);
|
|
62712
62867
|
}
|
|
62713
|
-
|
|
62714
|
-
|
|
62715
|
-
|
|
62716
|
-
|
|
62868
|
+
return merged;
|
|
62869
|
+
}
|
|
62870
|
+
function normalizeUnique(values) {
|
|
62871
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort();
|
|
62872
|
+
}
|
|
62873
|
+
function ensureAgentEntry(config, agentId) {
|
|
62874
|
+
const entry = config.agents.list.find((item) => item.id === agentId);
|
|
62875
|
+
if (!entry) {
|
|
62876
|
+
throw new Error(`Unknown agent: ${agentId}`);
|
|
62717
62877
|
}
|
|
62718
|
-
return
|
|
62719
|
-
|
|
62878
|
+
return entry;
|
|
62879
|
+
}
|
|
62880
|
+
function getAuthLabel(scope) {
|
|
62881
|
+
if (scope.kind === "app") {
|
|
62882
|
+
return "app.auth";
|
|
62883
|
+
}
|
|
62884
|
+
if (scope.kind === "agent-defaults") {
|
|
62885
|
+
return "agents.defaults.auth";
|
|
62886
|
+
}
|
|
62887
|
+
return `agents.list[${scope.agentId}].auth`;
|
|
62888
|
+
}
|
|
62889
|
+
function ensureEditableRoleDefinition(role) {
|
|
62890
|
+
role.allow = role.allow ?? [];
|
|
62891
|
+
role.users = role.users ?? [];
|
|
62892
|
+
return role;
|
|
62893
|
+
}
|
|
62894
|
+
function resolveAppRole(config, roleName) {
|
|
62895
|
+
const role = config.app.auth.roles[roleName];
|
|
62896
|
+
if (!role) {
|
|
62897
|
+
throw new Error(`Unknown app role: ${roleName}`);
|
|
62898
|
+
}
|
|
62899
|
+
return ensureEditableRoleDefinition(role);
|
|
62900
|
+
}
|
|
62901
|
+
function resolveAgentDefaultsRole(config, roleName) {
|
|
62902
|
+
const role = config.agents.defaults.auth.roles[roleName];
|
|
62903
|
+
if (!role) {
|
|
62904
|
+
throw new Error(`Unknown agent-defaults role: ${roleName}`);
|
|
62905
|
+
}
|
|
62906
|
+
return ensureEditableRoleDefinition(role);
|
|
62907
|
+
}
|
|
62908
|
+
function ensureAgentOverrideAuth(entry, config) {
|
|
62909
|
+
if (!entry.auth) {
|
|
62910
|
+
entry.auth = {
|
|
62911
|
+
defaultRole: config.agents.defaults.auth.defaultRole,
|
|
62912
|
+
roles: {}
|
|
62913
|
+
};
|
|
62914
|
+
}
|
|
62915
|
+
return entry.auth;
|
|
62916
|
+
}
|
|
62917
|
+
function resolveAgentRoleForEdit(config, agentId, roleName) {
|
|
62918
|
+
const entry = ensureAgentEntry(config, agentId);
|
|
62919
|
+
const explicitRole = entry.auth?.roles?.[roleName];
|
|
62920
|
+
if (explicitRole) {
|
|
62921
|
+
return ensureEditableRoleDefinition(explicitRole);
|
|
62922
|
+
}
|
|
62923
|
+
const inheritedRole = config.agents.defaults.auth.roles[roleName];
|
|
62924
|
+
if (!inheritedRole) {
|
|
62925
|
+
throw new Error(`Unknown agent role: ${roleName}`);
|
|
62926
|
+
}
|
|
62927
|
+
const auth = ensureAgentOverrideAuth(entry, config);
|
|
62928
|
+
auth.roles[roleName] = cloneRoleDefinition(inheritedRole);
|
|
62929
|
+
return ensureEditableRoleDefinition(auth.roles[roleName]);
|
|
62930
|
+
}
|
|
62931
|
+
function resolveRoleForEdit(config, scope, roleName) {
|
|
62932
|
+
if (scope.kind === "app") {
|
|
62933
|
+
return resolveAppRole(config, roleName);
|
|
62934
|
+
}
|
|
62935
|
+
if (scope.kind === "agent-defaults") {
|
|
62936
|
+
return resolveAgentDefaultsRole(config, roleName);
|
|
62937
|
+
}
|
|
62938
|
+
return resolveAgentRoleForEdit(config, scope.agentId, roleName);
|
|
62939
|
+
}
|
|
62940
|
+
function validatePermission(scope, permission) {
|
|
62941
|
+
const trimmed = permission.trim();
|
|
62942
|
+
if (!trimmed) {
|
|
62943
|
+
throw new Error("Missing value for --permission");
|
|
62944
|
+
}
|
|
62945
|
+
const allowedPermissions = new Set(scope.kind === "app" ? APP_ADMIN_PERMISSIONS : DEFAULT_AGENT_ADMIN_PERMISSIONS);
|
|
62946
|
+
if (!allowedPermissions.has(trimmed)) {
|
|
62947
|
+
const sorted = [...allowedPermissions].sort().join(", ");
|
|
62948
|
+
throw new Error(`Unknown permission for ${scope.kind}: ${trimmed}. Allowed: ${sorted}`);
|
|
62949
|
+
}
|
|
62950
|
+
return trimmed;
|
|
62951
|
+
}
|
|
62952
|
+
function buildShowPayload(config, scope) {
|
|
62953
|
+
if (scope.kind === "app") {
|
|
62954
|
+
return config.app.auth;
|
|
62955
|
+
}
|
|
62956
|
+
if (scope.kind === "agent-defaults") {
|
|
62957
|
+
return config.agents.defaults.auth;
|
|
62958
|
+
}
|
|
62959
|
+
const entry = ensureAgentEntry(config, scope.agentId);
|
|
62960
|
+
return {
|
|
62961
|
+
defaultRole: entry.auth?.defaultRole ?? config.agents.defaults.auth.defaultRole,
|
|
62962
|
+
roles: mergeRoleRecord(config.agents.defaults.auth.roles, entry.auth?.roles)
|
|
62963
|
+
};
|
|
62964
|
+
}
|
|
62965
|
+
async function listAuth(args) {
|
|
62966
|
+
const { config } = await readEditableConfig(getEditableConfigPath3());
|
|
62967
|
+
const payload = {
|
|
62968
|
+
app: config.app.auth,
|
|
62969
|
+
agentDefaults: config.agents.defaults.auth,
|
|
62970
|
+
agents: config.agents.list.map((entry) => ({
|
|
62971
|
+
agentId: entry.id,
|
|
62972
|
+
auth: buildShowPayload(config, { kind: "agent", agentId: entry.id })
|
|
62973
|
+
}))
|
|
62974
|
+
};
|
|
62975
|
+
if (hasFlag3(args, "--json")) {
|
|
62976
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
62977
|
+
return;
|
|
62978
|
+
}
|
|
62979
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
62980
|
+
}
|
|
62981
|
+
async function showAuth(args) {
|
|
62982
|
+
const scope = parseScope(args[0], args.slice(1));
|
|
62983
|
+
const { config } = await readEditableConfig(getEditableConfigPath3());
|
|
62984
|
+
const payload = buildShowPayload(config, scope);
|
|
62985
|
+
if (hasFlag3(args, "--json")) {
|
|
62986
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
62987
|
+
return;
|
|
62988
|
+
}
|
|
62989
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
62990
|
+
}
|
|
62991
|
+
async function mutateUsers(mode, args) {
|
|
62992
|
+
const scope = parseScope(args[0], args.slice(1));
|
|
62993
|
+
const roleName = parseSingleOption2(args, "--role");
|
|
62994
|
+
const principal = parseSingleOption2(args, "--user")?.trim();
|
|
62995
|
+
if (!roleName) {
|
|
62996
|
+
throw new Error("Missing value for --role");
|
|
62997
|
+
}
|
|
62998
|
+
if (!principal) {
|
|
62999
|
+
throw new Error("Missing value for --user");
|
|
63000
|
+
}
|
|
63001
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath3());
|
|
63002
|
+
const role = resolveRoleForEdit(config, scope, roleName);
|
|
63003
|
+
role.users = normalizeUnique(mode === "add" ? [...role.users, principal] : role.users.filter((value) => value !== principal));
|
|
63004
|
+
await writeEditableConfig(configPath, config);
|
|
63005
|
+
console.log(`${mode === "add" ? "Added" : "Removed"} user ${principal} ${mode === "add" ? "to" : "from"} ${getAuthLabel(scope)} role ${roleName}.`);
|
|
63006
|
+
}
|
|
63007
|
+
async function mutatePermissions(mode, args) {
|
|
63008
|
+
const scope = parseScope(args[0], args.slice(1));
|
|
63009
|
+
const roleName = parseSingleOption2(args, "--role");
|
|
63010
|
+
const permission = validatePermission(scope, parseSingleOption2(args, "--permission") ?? "");
|
|
63011
|
+
if (!roleName) {
|
|
63012
|
+
throw new Error("Missing value for --role");
|
|
63013
|
+
}
|
|
63014
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath3());
|
|
63015
|
+
const role = resolveRoleForEdit(config, scope, roleName);
|
|
63016
|
+
role.allow = normalizeUnique(mode === "add" ? [...role.allow, permission] : role.allow.filter((value) => value !== permission));
|
|
63017
|
+
await writeEditableConfig(configPath, config);
|
|
63018
|
+
console.log(`${mode === "add" ? "Added" : "Removed"} permission ${permission} ${mode === "add" ? "to" : "from"} ${getAuthLabel(scope)} role ${roleName}.`);
|
|
63019
|
+
}
|
|
63020
|
+
async function runAuthCli(args) {
|
|
63021
|
+
const [command, ...rest] = args;
|
|
63022
|
+
if (!command || command === "--help" || command === "-h" || command === "help") {
|
|
63023
|
+
console.log(renderAuthCliHelp());
|
|
63024
|
+
return;
|
|
63025
|
+
}
|
|
63026
|
+
if (command === "list") {
|
|
63027
|
+
await listAuth(rest);
|
|
63028
|
+
return;
|
|
63029
|
+
}
|
|
63030
|
+
if (command === "show") {
|
|
63031
|
+
await showAuth(rest);
|
|
63032
|
+
return;
|
|
63033
|
+
}
|
|
63034
|
+
if (command === "add-user") {
|
|
63035
|
+
await mutateUsers("add", rest);
|
|
63036
|
+
return;
|
|
63037
|
+
}
|
|
63038
|
+
if (command === "remove-user") {
|
|
63039
|
+
await mutateUsers("remove", rest);
|
|
63040
|
+
return;
|
|
63041
|
+
}
|
|
63042
|
+
if (command === "add-permission") {
|
|
63043
|
+
await mutatePermissions("add", rest);
|
|
63044
|
+
return;
|
|
63045
|
+
}
|
|
63046
|
+
if (command === "remove-permission") {
|
|
63047
|
+
await mutatePermissions("remove", rest);
|
|
63048
|
+
return;
|
|
63049
|
+
}
|
|
63050
|
+
throw new Error(renderAuthCliHelp());
|
|
62720
63051
|
}
|
|
62721
63052
|
|
|
62722
63053
|
// src/channels/mode-config-shared.ts
|
|
63054
|
+
function createTelegramGroupRoute() {
|
|
63055
|
+
return {
|
|
63056
|
+
requireMention: true,
|
|
63057
|
+
allowBots: false,
|
|
63058
|
+
topics: {}
|
|
63059
|
+
};
|
|
63060
|
+
}
|
|
63061
|
+
function ensureTelegramGroupRoute(config, chatId) {
|
|
63062
|
+
const existing = config.channels.telegram.groups[chatId];
|
|
63063
|
+
if (existing) {
|
|
63064
|
+
return existing;
|
|
63065
|
+
}
|
|
63066
|
+
const created = createTelegramGroupRoute();
|
|
63067
|
+
config.channels.telegram.groups[chatId] = created;
|
|
63068
|
+
return created;
|
|
63069
|
+
}
|
|
63070
|
+
function ensureTelegramTopicRoute(config, chatId, topicId) {
|
|
63071
|
+
const group = ensureTelegramGroupRoute(config, chatId);
|
|
63072
|
+
const existing = group.topics[topicId];
|
|
63073
|
+
if (existing) {
|
|
63074
|
+
return existing;
|
|
63075
|
+
}
|
|
63076
|
+
const created = {};
|
|
63077
|
+
group.topics[topicId] = created;
|
|
63078
|
+
return created;
|
|
63079
|
+
}
|
|
62723
63080
|
function getModeValue(source, field) {
|
|
62724
63081
|
return source[field];
|
|
62725
63082
|
}
|
|
62726
63083
|
function setModeValue(source, field, value) {
|
|
62727
63084
|
source[field] = value;
|
|
62728
63085
|
}
|
|
63086
|
+
var EMPTY_MODE_SOURCE = {};
|
|
62729
63087
|
function resolveSlackConfigTarget(config, field, params) {
|
|
62730
63088
|
if (!params.target) {
|
|
62731
63089
|
return {
|
|
@@ -62807,22 +63165,25 @@ function resolveTelegramConfigTarget(config, field, params) {
|
|
|
62807
63165
|
};
|
|
62808
63166
|
}
|
|
62809
63167
|
const group = config.channels.telegram.groups[chatId];
|
|
62810
|
-
if (!group) {
|
|
62811
|
-
throw new Error(renderTelegramRouteChoiceMessage({ chatId }));
|
|
62812
|
-
}
|
|
62813
63168
|
if (topicId) {
|
|
62814
|
-
const topic = group
|
|
62815
|
-
if (!topic) {
|
|
62816
|
-
throw new Error(renderTelegramRouteChoiceMessage({ chatId, topicId }));
|
|
62817
|
-
}
|
|
63169
|
+
const topic = group?.topics?.[topicId];
|
|
62818
63170
|
return {
|
|
62819
|
-
get: () => getModeValue(topic, field) ?? getModeValue(group, field) ?? getModeValue(config.channels.telegram, field),
|
|
63171
|
+
get: () => getModeValue(topic ?? EMPTY_MODE_SOURCE, field) ?? getModeValue(group ?? EMPTY_MODE_SOURCE, field) ?? getModeValue(config.channels.telegram, field),
|
|
62820
63172
|
set: (value) => {
|
|
62821
|
-
setModeValue(
|
|
63173
|
+
setModeValue(ensureTelegramTopicRoute(config, chatId, topicId), field, value);
|
|
62822
63174
|
},
|
|
62823
63175
|
label: `telegram topic ${chatId}/${topicId}`
|
|
62824
63176
|
};
|
|
62825
63177
|
}
|
|
63178
|
+
if (!group) {
|
|
63179
|
+
return {
|
|
63180
|
+
get: () => getModeValue(config.channels.telegram, field),
|
|
63181
|
+
set: (value) => {
|
|
63182
|
+
setModeValue(ensureTelegramGroupRoute(config, chatId), field, value);
|
|
63183
|
+
},
|
|
63184
|
+
label: `telegram group ${chatId}`
|
|
63185
|
+
};
|
|
63186
|
+
}
|
|
62826
63187
|
return {
|
|
62827
63188
|
get: () => getModeValue(group, field) ?? getModeValue(config.channels.telegram, field),
|
|
62828
63189
|
set: (value) => {
|
|
@@ -62863,11 +63224,11 @@ function renderFieldLabel(field) {
|
|
|
62863
63224
|
}
|
|
62864
63225
|
|
|
62865
63226
|
// src/channels/additional-message-mode-config.ts
|
|
62866
|
-
function
|
|
63227
|
+
function getEditableConfigPath4() {
|
|
62867
63228
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
62868
63229
|
}
|
|
62869
63230
|
async function getConversationAdditionalMessageMode(params) {
|
|
62870
|
-
const { config } = await readEditableConfig(
|
|
63231
|
+
const { config } = await readEditableConfig(getEditableConfigPath4());
|
|
62871
63232
|
const target = resolveConfiguredSurfaceModeTarget(config, "additionalMessageMode", buildConfiguredTargetFromIdentity(params.identity));
|
|
62872
63233
|
return {
|
|
62873
63234
|
label: target.label,
|
|
@@ -62875,7 +63236,7 @@ async function getConversationAdditionalMessageMode(params) {
|
|
|
62875
63236
|
};
|
|
62876
63237
|
}
|
|
62877
63238
|
async function setConversationAdditionalMessageMode(params) {
|
|
62878
|
-
const { config, configPath } = await readEditableConfig(
|
|
63239
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath4());
|
|
62879
63240
|
const target = resolveConfiguredSurfaceModeTarget(config, "additionalMessageMode", buildConfiguredTargetFromIdentity(params.identity));
|
|
62880
63241
|
target.set(params.additionalMessageMode);
|
|
62881
63242
|
await writeEditableConfig(configPath, config);
|
|
@@ -62886,7 +63247,7 @@ async function setConversationAdditionalMessageMode(params) {
|
|
|
62886
63247
|
};
|
|
62887
63248
|
}
|
|
62888
63249
|
async function getConfiguredAdditionalMessageMode(params) {
|
|
62889
|
-
const { config, configPath } = await readEditableConfig(
|
|
63250
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath4());
|
|
62890
63251
|
const target = resolveConfiguredSurfaceModeTarget(config, "additionalMessageMode", params);
|
|
62891
63252
|
return {
|
|
62892
63253
|
configPath,
|
|
@@ -62895,7 +63256,7 @@ async function getConfiguredAdditionalMessageMode(params) {
|
|
|
62895
63256
|
};
|
|
62896
63257
|
}
|
|
62897
63258
|
async function setConfiguredAdditionalMessageMode(params) {
|
|
62898
|
-
const { config, configPath } = await readEditableConfig(
|
|
63259
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath4());
|
|
62899
63260
|
const target = resolveConfiguredSurfaceModeTarget(config, "additionalMessageMode", params);
|
|
62900
63261
|
target.set(params.additionalMessageMode);
|
|
62901
63262
|
await writeEditableConfig(configPath, config);
|
|
@@ -62907,11 +63268,11 @@ async function setConfiguredAdditionalMessageMode(params) {
|
|
|
62907
63268
|
}
|
|
62908
63269
|
|
|
62909
63270
|
// src/channels/response-mode-config.ts
|
|
62910
|
-
function
|
|
63271
|
+
function getEditableConfigPath5() {
|
|
62911
63272
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
62912
63273
|
}
|
|
62913
63274
|
async function getConversationResponseMode(params) {
|
|
62914
|
-
const { config } = await readEditableConfig(
|
|
63275
|
+
const { config } = await readEditableConfig(getEditableConfigPath5());
|
|
62915
63276
|
const target = resolveConfiguredSurfaceModeTarget(config, "responseMode", buildConfiguredTargetFromIdentity(params.identity));
|
|
62916
63277
|
return {
|
|
62917
63278
|
label: target.label,
|
|
@@ -62919,7 +63280,7 @@ async function getConversationResponseMode(params) {
|
|
|
62919
63280
|
};
|
|
62920
63281
|
}
|
|
62921
63282
|
async function setConversationResponseMode(params) {
|
|
62922
|
-
const { config, configPath } = await readEditableConfig(
|
|
63283
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
62923
63284
|
const target = resolveConfiguredSurfaceModeTarget(config, "responseMode", buildConfiguredTargetFromIdentity(params.identity));
|
|
62924
63285
|
target.set(params.responseMode);
|
|
62925
63286
|
await writeEditableConfig(configPath, config);
|
|
@@ -62930,7 +63291,7 @@ async function setConversationResponseMode(params) {
|
|
|
62930
63291
|
};
|
|
62931
63292
|
}
|
|
62932
63293
|
async function getConfiguredResponseMode(params) {
|
|
62933
|
-
const { config, configPath } = await readEditableConfig(
|
|
63294
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
62934
63295
|
const target = resolveConfiguredSurfaceModeTarget(config, "responseMode", params);
|
|
62935
63296
|
return {
|
|
62936
63297
|
configPath,
|
|
@@ -62939,7 +63300,7 @@ async function getConfiguredResponseMode(params) {
|
|
|
62939
63300
|
};
|
|
62940
63301
|
}
|
|
62941
63302
|
async function setConfiguredResponseMode(params) {
|
|
62942
|
-
const { config, configPath } = await readEditableConfig(
|
|
63303
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath5());
|
|
62943
63304
|
const target = resolveConfiguredSurfaceModeTarget(config, "responseMode", params);
|
|
62944
63305
|
target.set(params.responseMode);
|
|
62945
63306
|
await writeEditableConfig(configPath, config);
|
|
@@ -62965,7 +63326,7 @@ async function runChannelPrivilegeCli(_args) {
|
|
|
62965
63326
|
|
|
62966
63327
|
// src/control/channels-cli.ts
|
|
62967
63328
|
var AUTH_USER_GUIDE_DOC_PATH = "docs/user-guide/auth-and-roles.md";
|
|
62968
|
-
function
|
|
63329
|
+
function getEditableConfigPath6() {
|
|
62969
63330
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
62970
63331
|
}
|
|
62971
63332
|
function renderChannelsHelp() {
|
|
@@ -63114,7 +63475,7 @@ function getAgentId(args) {
|
|
|
63114
63475
|
return parseOptionValue2(args, "--agent") ?? "default";
|
|
63115
63476
|
}
|
|
63116
63477
|
async function setChannelEnabled(action, channel) {
|
|
63117
|
-
const { config, configPath } = await readEditableConfig(
|
|
63478
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath6());
|
|
63118
63479
|
const enabled = action === "enable";
|
|
63119
63480
|
const current = config.channels[channel].enabled;
|
|
63120
63481
|
if (current === enabled) {
|
|
@@ -63134,7 +63495,7 @@ async function addTelegramGroup(args) {
|
|
|
63134
63495
|
if (!chatId) {
|
|
63135
63496
|
throw new Error("Usage: clisbot channels add telegram-group <chatId> [--topic <topicId>] [--agent <id>] [--require-mention true|false]");
|
|
63136
63497
|
}
|
|
63137
|
-
const { config, configPath } = await readEditableConfig(
|
|
63498
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath6());
|
|
63138
63499
|
const topicId = parseOptionValue2(args, "--topic");
|
|
63139
63500
|
const agentId = getAgentId(args);
|
|
63140
63501
|
const requireMention = parseBooleanOption(args, "--require-mention", true);
|
|
@@ -63187,7 +63548,7 @@ async function removeTelegramGroup(args) {
|
|
|
63187
63548
|
if (!chatId) {
|
|
63188
63549
|
throw new Error("Usage: clisbot channels remove telegram-group <chatId> [--topic <topicId>]");
|
|
63189
63550
|
}
|
|
63190
|
-
const { config, configPath } = await readEditableConfig(
|
|
63551
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath6());
|
|
63191
63552
|
const topicId = parseOptionValue2(args, "--topic");
|
|
63192
63553
|
const groupRoute = config.channels.telegram.groups[chatId];
|
|
63193
63554
|
if (!groupRoute) {
|
|
@@ -63217,7 +63578,7 @@ async function addSlackRoute(kind, args) {
|
|
|
63217
63578
|
if (!routeId) {
|
|
63218
63579
|
throw new Error(`Usage: clisbot channels add slack-${kind} <${kind}Id> [--agent <id>] [--require-mention true|false]`);
|
|
63219
63580
|
}
|
|
63220
|
-
const { config, configPath } = await readEditableConfig(
|
|
63581
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath6());
|
|
63221
63582
|
const agentId = getAgentId(args);
|
|
63222
63583
|
const requireMention = parseBooleanOption(args, "--require-mention", false);
|
|
63223
63584
|
const target = kind === "channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
@@ -63241,7 +63602,7 @@ async function removeSlackRoute(kind, args) {
|
|
|
63241
63602
|
if (!routeId) {
|
|
63242
63603
|
throw new Error(`Usage: clisbot channels remove slack-${kind} <${kind}Id>`);
|
|
63243
63604
|
}
|
|
63244
|
-
const { config, configPath } = await readEditableConfig(
|
|
63605
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath6());
|
|
63245
63606
|
const target = kind === "channel" ? config.channels.slack.channels : config.channels.slack.groups;
|
|
63246
63607
|
if (!target[routeId]) {
|
|
63247
63608
|
console.log(`slack ${kind} route ${routeId} is not configured`);
|
|
@@ -63254,7 +63615,7 @@ async function removeSlackRoute(kind, args) {
|
|
|
63254
63615
|
console.log(`config: ${configPath}`);
|
|
63255
63616
|
}
|
|
63256
63617
|
async function setToken(target, value) {
|
|
63257
|
-
const { config, configPath } = await readEditableConfig(
|
|
63618
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath6());
|
|
63258
63619
|
if (target === "slack-app") {
|
|
63259
63620
|
config.channels.slack.appToken = value;
|
|
63260
63621
|
const defaultAccountId = config.channels.slack.defaultAccount || "default";
|
|
@@ -63286,7 +63647,7 @@ async function setToken(target, value) {
|
|
|
63286
63647
|
console.log(`config: ${configPath}`);
|
|
63287
63648
|
}
|
|
63288
63649
|
async function clearToken(target) {
|
|
63289
|
-
const { config, configPath } = await readEditableConfig(
|
|
63650
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath6());
|
|
63290
63651
|
if (target === "slack-app") {
|
|
63291
63652
|
config.channels.slack.appToken = "";
|
|
63292
63653
|
const defaultAccountId = config.channels.slack.defaultAccount || "default";
|
|
@@ -64025,7 +64386,7 @@ class SessionStore {
|
|
|
64025
64386
|
}
|
|
64026
64387
|
|
|
64027
64388
|
// src/control/loops-cli.ts
|
|
64028
|
-
function
|
|
64389
|
+
function getEditableConfigPath7() {
|
|
64029
64390
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
64030
64391
|
}
|
|
64031
64392
|
function renderLoopsHelp() {
|
|
@@ -64083,7 +64444,7 @@ function getSessionState(sessionStorePath) {
|
|
|
64083
64444
|
return new AgentSessionState(new SessionStore(sessionStorePath));
|
|
64084
64445
|
}
|
|
64085
64446
|
async function loadLoopControlState() {
|
|
64086
|
-
const configPath = await ensureEditableConfigFile(
|
|
64447
|
+
const configPath = await ensureEditableConfigFile(getEditableConfigPath7());
|
|
64087
64448
|
const loadedConfig = await loadConfigWithoutEnvResolution(configPath);
|
|
64088
64449
|
const sessionStorePath = resolveSessionStorePath(loadedConfig);
|
|
64089
64450
|
return {
|
|
@@ -64168,7 +64529,7 @@ ${params.text}
|
|
|
64168
64529
|
}
|
|
64169
64530
|
function renderAgentPromptInstruction(params) {
|
|
64170
64531
|
const messageToolMode = (params.responseMode ?? "message-tool") === "message-tool";
|
|
64171
|
-
const progressAllowed = messageToolMode && (params.streaming ?? "
|
|
64532
|
+
const progressAllowed = messageToolMode && (params.streaming ?? "off") === "off";
|
|
64172
64533
|
const lines = [
|
|
64173
64534
|
`[${renderPromptTimestamp()}] ${renderIdentitySummary(params.identity)}`,
|
|
64174
64535
|
"",
|
|
@@ -64259,13 +64620,16 @@ function buildReplyCommand(params) {
|
|
|
64259
64620
|
const lines = [`${params.command} message send \\`];
|
|
64260
64621
|
if (params.identity.platform === "slack") {
|
|
64261
64622
|
lines.push(" --channel slack \\");
|
|
64623
|
+
if (params.identity.accountId) {
|
|
64624
|
+
lines.push(` --account ${params.identity.accountId} \\`);
|
|
64625
|
+
}
|
|
64262
64626
|
lines.push(` --target channel:${params.identity.channelId ?? ""} \\`);
|
|
64263
64627
|
if (params.identity.threadTs) {
|
|
64264
64628
|
lines.push(` --thread-id ${params.identity.threadTs} \\`);
|
|
64265
64629
|
}
|
|
64266
64630
|
lines.push(" --final \\");
|
|
64267
64631
|
lines.push(' --message "$(cat <<\\__CLISBOT_MESSAGE__');
|
|
64268
|
-
lines.push("<
|
|
64632
|
+
lines.push("<user-facing reply>");
|
|
64269
64633
|
lines.push("__CLISBOT_MESSAGE__");
|
|
64270
64634
|
lines.push(')" \\');
|
|
64271
64635
|
lines.push(" [--media /absolute/path/to/file]");
|
|
@@ -64273,13 +64637,16 @@ function buildReplyCommand(params) {
|
|
|
64273
64637
|
`);
|
|
64274
64638
|
}
|
|
64275
64639
|
lines.push(" --channel telegram \\");
|
|
64640
|
+
if (params.identity.accountId) {
|
|
64641
|
+
lines.push(` --account ${params.identity.accountId} \\`);
|
|
64642
|
+
}
|
|
64276
64643
|
lines.push(` --target ${params.identity.chatId ?? ""} \\`);
|
|
64277
64644
|
if (params.identity.topicId) {
|
|
64278
64645
|
lines.push(` --thread-id ${params.identity.topicId} \\`);
|
|
64279
64646
|
}
|
|
64280
64647
|
lines.push(" --final \\");
|
|
64281
64648
|
lines.push(' --message "$(cat <<\\__CLISBOT_MESSAGE__');
|
|
64282
|
-
lines.push("<
|
|
64649
|
+
lines.push("<user-facing reply>");
|
|
64283
64650
|
lines.push("__CLISBOT_MESSAGE__");
|
|
64284
64651
|
lines.push(')" \\');
|
|
64285
64652
|
lines.push(" [--media /absolute/path/to/file]");
|
|
@@ -64287,6 +64654,57 @@ function buildReplyCommand(params) {
|
|
|
64287
64654
|
`);
|
|
64288
64655
|
}
|
|
64289
64656
|
|
|
64657
|
+
// src/channels/surface-notifications.ts
|
|
64658
|
+
function sanitizeInlineCode(text) {
|
|
64659
|
+
return text.replaceAll("`", "'");
|
|
64660
|
+
}
|
|
64661
|
+
function summarizeSurfaceNotificationText(text, maxLength = 60) {
|
|
64662
|
+
const singleLine = sanitizeInlineCode(text.replace(/\s+/g, " ").trim());
|
|
64663
|
+
if (!singleLine) {
|
|
64664
|
+
return "(empty)";
|
|
64665
|
+
}
|
|
64666
|
+
if (singleLine.length <= maxLength) {
|
|
64667
|
+
return singleLine;
|
|
64668
|
+
}
|
|
64669
|
+
return `${singleLine.slice(0, maxLength - 3)}...`;
|
|
64670
|
+
}
|
|
64671
|
+
function renderQueueStartNotification(params) {
|
|
64672
|
+
if (params.mode === "none") {
|
|
64673
|
+
return;
|
|
64674
|
+
}
|
|
64675
|
+
const summary = summarizeSurfaceNotificationText(params.promptSummary);
|
|
64676
|
+
if (params.mode === "full") {
|
|
64677
|
+
return `Queued message is now running for agent \`${params.agentId}\`: \`${summary}\`.`;
|
|
64678
|
+
}
|
|
64679
|
+
return `Queued message is now running: \`${summary}\`.`;
|
|
64680
|
+
}
|
|
64681
|
+
function renderLoopScheduleSegment(params) {
|
|
64682
|
+
if (params.kind === "calendar") {
|
|
64683
|
+
return formatCalendarLoopSchedule({
|
|
64684
|
+
cadence: params.cadence,
|
|
64685
|
+
dayOfWeek: params.dayOfWeek,
|
|
64686
|
+
localTime: params.localTime
|
|
64687
|
+
});
|
|
64688
|
+
}
|
|
64689
|
+
return `every ${formatLoopIntervalShort(params.intervalMs)}`;
|
|
64690
|
+
}
|
|
64691
|
+
function renderLoopStartNotification(params) {
|
|
64692
|
+
if (params.mode === "none") {
|
|
64693
|
+
return;
|
|
64694
|
+
}
|
|
64695
|
+
const summary = summarizeSurfaceNotificationText(params.promptSummary);
|
|
64696
|
+
const segments = [
|
|
64697
|
+
`\`${summary}\``,
|
|
64698
|
+
renderLoopScheduleSegment(params),
|
|
64699
|
+
`next run \`${new Date(params.nextRunAt).toISOString()}\``,
|
|
64700
|
+
`remaining \`${params.remainingRuns}/${params.maxRuns}\``
|
|
64701
|
+
];
|
|
64702
|
+
if (params.mode === "full") {
|
|
64703
|
+
return `Loop \`${params.loopId}\` is now running for agent \`${params.agentId}\`: ${segments.join(" · ")}.`;
|
|
64704
|
+
}
|
|
64705
|
+
return `Loop \`${params.loopId}\` is now running: ${segments.join(" · ")}.`;
|
|
64706
|
+
}
|
|
64707
|
+
|
|
64290
64708
|
// src/agents/session-key.ts
|
|
64291
64709
|
var DEFAULT_MAIN_KEY = "main";
|
|
64292
64710
|
var DEFAULT_ACCOUNT_ID = "default";
|
|
@@ -64931,11 +65349,50 @@ function extractScrolledAppend(previous, current) {
|
|
|
64931
65349
|
}
|
|
64932
65350
|
return "";
|
|
64933
65351
|
}
|
|
65352
|
+
function deriveRunningInteractionText(previousSnapshot, currentSnapshot) {
|
|
65353
|
+
const previous = cleanInteractionSnapshot(previousSnapshot);
|
|
65354
|
+
const current = cleanInteractionSnapshot(currentSnapshot);
|
|
65355
|
+
if (!current || current === previous) {
|
|
65356
|
+
return "";
|
|
65357
|
+
}
|
|
65358
|
+
if (!previous) {
|
|
65359
|
+
return current;
|
|
65360
|
+
}
|
|
65361
|
+
return extractScrolledAppend(previous, current);
|
|
65362
|
+
}
|
|
64934
65363
|
function deriveInteractionText(initialSnapshot, currentSnapshot) {
|
|
64935
65364
|
const previous = cleanInteractionSnapshot(initialSnapshot);
|
|
64936
65365
|
const current = cleanInteractionSnapshot(currentSnapshot);
|
|
64937
65366
|
return extractScrolledAppend(previous, current) || diffText(previous, current);
|
|
64938
65367
|
}
|
|
65368
|
+
function appendInteractionText(currentBody, nextDelta) {
|
|
65369
|
+
const trimmedCurrent = currentBody.trim();
|
|
65370
|
+
const trimmedDelta = nextDelta.trim();
|
|
65371
|
+
if (!trimmedDelta) {
|
|
65372
|
+
return trimmedCurrent;
|
|
65373
|
+
}
|
|
65374
|
+
if (!trimmedCurrent) {
|
|
65375
|
+
return trimmedDelta;
|
|
65376
|
+
}
|
|
65377
|
+
const currentLines = trimmedCurrent.split(`
|
|
65378
|
+
`);
|
|
65379
|
+
const deltaLines = trimmedDelta.split(`
|
|
65380
|
+
`);
|
|
65381
|
+
const maxOverlap = Math.min(currentLines.length, deltaLines.length, 8);
|
|
65382
|
+
let overlap = 0;
|
|
65383
|
+
for (let size = maxOverlap;size > 0; size -= 1) {
|
|
65384
|
+
const currentSuffix = currentLines.slice(currentLines.length - size).join(`
|
|
65385
|
+
`);
|
|
65386
|
+
const deltaPrefix = deltaLines.slice(0, size).join(`
|
|
65387
|
+
`);
|
|
65388
|
+
if (currentSuffix === deltaPrefix) {
|
|
65389
|
+
overlap = size;
|
|
65390
|
+
break;
|
|
65391
|
+
}
|
|
65392
|
+
}
|
|
65393
|
+
return [...currentLines, ...deltaLines.slice(overlap)].join(`
|
|
65394
|
+
`).trim();
|
|
65395
|
+
}
|
|
64939
65396
|
function truncateTail(raw, maxChars) {
|
|
64940
65397
|
if (raw.length <= maxChars) {
|
|
64941
65398
|
return raw;
|
|
@@ -65713,6 +66170,9 @@ class RunnerSessionService {
|
|
|
65713
66170
|
await ensureRunnerExitRecordDir(this.loadedConfig.stateDir, resolved.sessionName);
|
|
65714
66171
|
const existing = await this.sessionState.getEntry(resolved.sessionKey);
|
|
65715
66172
|
const serverRunning = await this.tmux.isServerRunning();
|
|
66173
|
+
if (serverRunning) {
|
|
66174
|
+
await this.tmux.ensureServerDefaults();
|
|
66175
|
+
}
|
|
65716
66176
|
if (serverRunning && await this.tmux.hasSession(resolved.sessionName)) {
|
|
65717
66177
|
logLatencyDebug("ensure-session-ready-existing-session", timingContext, {
|
|
65718
66178
|
hasStoredSessionId: Boolean(existing?.sessionId)
|
|
@@ -66006,7 +66466,7 @@ async function monitorTmuxRun(params) {
|
|
|
66006
66466
|
let previousSnapshot = params.initialSnapshot;
|
|
66007
66467
|
let lastChangeAt = Date.now();
|
|
66008
66468
|
let sawChange = false;
|
|
66009
|
-
let
|
|
66469
|
+
let cumulativeInteractionSnapshot = "";
|
|
66010
66470
|
let detachedNotified = params.detachedAlready;
|
|
66011
66471
|
let firstMeaningfulDeltaLogged = false;
|
|
66012
66472
|
if (params.prompt) {
|
|
@@ -66032,12 +66492,14 @@ async function monitorTmuxRun(params) {
|
|
|
66032
66492
|
const snapshot = normalizePaneText(await params.tmux.capturePane(params.sessionName, params.captureLines));
|
|
66033
66493
|
const now = Date.now();
|
|
66034
66494
|
if (snapshot !== previousSnapshot) {
|
|
66495
|
+
const priorSnapshot = previousSnapshot;
|
|
66035
66496
|
lastChangeAt = now;
|
|
66036
66497
|
previousSnapshot = snapshot;
|
|
66037
|
-
const
|
|
66038
|
-
|
|
66498
|
+
const interactionDelta = deriveRunningInteractionText(priorSnapshot, snapshot);
|
|
66499
|
+
const nextInteractionSnapshot = appendInteractionText(cumulativeInteractionSnapshot, interactionDelta);
|
|
66500
|
+
if (nextInteractionSnapshot && nextInteractionSnapshot !== cumulativeInteractionSnapshot) {
|
|
66039
66501
|
sawChange = true;
|
|
66040
|
-
|
|
66502
|
+
cumulativeInteractionSnapshot = nextInteractionSnapshot;
|
|
66041
66503
|
if (!firstMeaningfulDeltaLogged) {
|
|
66042
66504
|
firstMeaningfulDeltaLogged = true;
|
|
66043
66505
|
logLatencyDebug("tmux-first-meaningful-delta", params.timingContext, {
|
|
@@ -66046,7 +66508,7 @@ async function monitorTmuxRun(params) {
|
|
|
66046
66508
|
});
|
|
66047
66509
|
}
|
|
66048
66510
|
await params.onRunning({
|
|
66049
|
-
snapshot:
|
|
66511
|
+
snapshot: cumulativeInteractionSnapshot,
|
|
66050
66512
|
fullSnapshot: snapshot,
|
|
66051
66513
|
initialSnapshot: params.initialSnapshot
|
|
66052
66514
|
});
|
|
@@ -66055,14 +66517,14 @@ async function monitorTmuxRun(params) {
|
|
|
66055
66517
|
if (!detachedNotified && now - params.startedAt >= params.maxRuntimeMs) {
|
|
66056
66518
|
detachedNotified = true;
|
|
66057
66519
|
await params.onDetached({
|
|
66058
|
-
snapshot: deriveInteractionText(params.initialSnapshot, previousSnapshot),
|
|
66520
|
+
snapshot: cumulativeInteractionSnapshot || deriveInteractionText(params.initialSnapshot, previousSnapshot),
|
|
66059
66521
|
fullSnapshot: previousSnapshot,
|
|
66060
66522
|
initialSnapshot: params.initialSnapshot
|
|
66061
66523
|
});
|
|
66062
66524
|
}
|
|
66063
66525
|
if (sawChange && now - lastChangeAt >= params.idleTimeoutMs) {
|
|
66064
66526
|
await params.onCompleted({
|
|
66065
|
-
snapshot: deriveInteractionText(params.initialSnapshot, previousSnapshot),
|
|
66527
|
+
snapshot: cumulativeInteractionSnapshot || deriveInteractionText(params.initialSnapshot, previousSnapshot),
|
|
66066
66528
|
fullSnapshot: previousSnapshot,
|
|
66067
66529
|
initialSnapshot: params.initialSnapshot
|
|
66068
66530
|
});
|
|
@@ -66070,7 +66532,7 @@ async function monitorTmuxRun(params) {
|
|
|
66070
66532
|
}
|
|
66071
66533
|
if (!sawChange && now - params.startedAt >= params.noOutputTimeoutMs) {
|
|
66072
66534
|
await params.onTimeout({
|
|
66073
|
-
snapshot: deriveInteractionText(params.initialSnapshot, previousSnapshot),
|
|
66535
|
+
snapshot: cumulativeInteractionSnapshot || deriveInteractionText(params.initialSnapshot, previousSnapshot),
|
|
66074
66536
|
fullSnapshot: previousSnapshot,
|
|
66075
66537
|
initialSnapshot: params.initialSnapshot
|
|
66076
66538
|
});
|
|
@@ -66526,6 +66988,7 @@ class AgentService {
|
|
|
66526
66988
|
cleanupTimer;
|
|
66527
66989
|
loopTimers = new Set;
|
|
66528
66990
|
intervalLoops = new Map;
|
|
66991
|
+
surfaceNotificationHandlers = new Map;
|
|
66529
66992
|
constructor(loadedConfig, deps = {}) {
|
|
66530
66993
|
this.loadedConfig = loadedConfig;
|
|
66531
66994
|
this.tmuxClient = deps.tmux ?? new TmuxClient(this.loadedConfig.raw.tmux.socketPath);
|
|
@@ -66554,7 +67017,9 @@ class AgentService {
|
|
|
66554
67017
|
}
|
|
66555
67018
|
await this.runnerSessions.runSessionCleanup();
|
|
66556
67019
|
this.cleanupTimer = setInterval(() => {
|
|
66557
|
-
this.runnerSessions.runSessionCleanup()
|
|
67020
|
+
this.runnerSessions.runSessionCleanup().catch((error) => {
|
|
67021
|
+
console.error("session cleanup failed", error);
|
|
67022
|
+
});
|
|
66558
67023
|
}, cleanup.intervalMinutes * 60000);
|
|
66559
67024
|
}
|
|
66560
67025
|
async stop() {
|
|
@@ -66578,6 +67043,12 @@ class AgentService {
|
|
|
66578
67043
|
async cleanupStaleSessions() {
|
|
66579
67044
|
await this.runnerSessions.runSessionCleanup();
|
|
66580
67045
|
}
|
|
67046
|
+
registerSurfaceNotificationHandler(params) {
|
|
67047
|
+
this.surfaceNotificationHandlers.set(this.getSurfaceNotificationHandlerKey(params.platform, params.accountId), params.handler);
|
|
67048
|
+
}
|
|
67049
|
+
unregisterSurfaceNotificationHandler(params) {
|
|
67050
|
+
this.surfaceNotificationHandlers.delete(this.getSurfaceNotificationHandlerKey(params.platform, params.accountId));
|
|
67051
|
+
}
|
|
66581
67052
|
resolveTarget(target) {
|
|
66582
67053
|
return resolveAgentTarget(this.loadedConfig, target);
|
|
66583
67054
|
}
|
|
@@ -66686,7 +67157,9 @@ class AgentService {
|
|
|
66686
67157
|
target: params.target,
|
|
66687
67158
|
loop
|
|
66688
67159
|
});
|
|
66689
|
-
await this.runIntervalLoopIteration(loop.id
|
|
67160
|
+
await this.runIntervalLoopIteration(loop.id, {
|
|
67161
|
+
notifyStart: false
|
|
67162
|
+
});
|
|
66690
67163
|
return this.getIntervalLoop(loop.id);
|
|
66691
67164
|
}
|
|
66692
67165
|
async createCalendarLoop(params) {
|
|
@@ -66848,7 +67321,7 @@ class AgentService {
|
|
|
66848
67321
|
}
|
|
66849
67322
|
this.intervalLoops.delete(loopId);
|
|
66850
67323
|
}
|
|
66851
|
-
async runIntervalLoopIteration(loopId) {
|
|
67324
|
+
async runIntervalLoopIteration(loopId, options = {}) {
|
|
66852
67325
|
const managed = this.intervalLoops.get(loopId);
|
|
66853
67326
|
if (!managed) {
|
|
66854
67327
|
return;
|
|
@@ -66884,6 +67357,9 @@ class AgentService {
|
|
|
66884
67357
|
this.dropManagedIntervalLoop(loopId);
|
|
66885
67358
|
return;
|
|
66886
67359
|
}
|
|
67360
|
+
if (options.notifyStart !== false) {
|
|
67361
|
+
await this.notifyManagedLoopStart(managed.target, nextLoopState);
|
|
67362
|
+
}
|
|
66887
67363
|
const promptText = this.buildManagedLoopPrompt(managed.target.agentId, nextLoopState);
|
|
66888
67364
|
const { result } = this.enqueuePrompt(managed.target, promptText, {
|
|
66889
67365
|
observerId: `loop:${loopId}:${attemptedRuns}`,
|
|
@@ -66903,12 +67379,107 @@ class AgentService {
|
|
|
66903
67379
|
}
|
|
66904
67380
|
this.scheduleIntervalLoopTimer(loopId, Math.max(0, nextLoopState.nextRunAt - now));
|
|
66905
67381
|
}
|
|
66906
|
-
|
|
66907
|
-
|
|
66908
|
-
|
|
67382
|
+
getSurfaceNotificationHandlerKey(platform, accountId) {
|
|
67383
|
+
return `${platform}:${accountId?.trim() || "default"}`;
|
|
67384
|
+
}
|
|
67385
|
+
async notifySurface(request) {
|
|
67386
|
+
const handler = this.surfaceNotificationHandlers.get(this.getSurfaceNotificationHandlerKey(request.binding.platform, request.binding.accountId));
|
|
67387
|
+
if (!handler) {
|
|
66909
67388
|
return;
|
|
66910
67389
|
}
|
|
66911
|
-
|
|
67390
|
+
await handler(request);
|
|
67391
|
+
}
|
|
67392
|
+
resolveLoopSurfaceNotifications(identity) {
|
|
67393
|
+
if (identity.platform === "slack") {
|
|
67394
|
+
const channelConfig2 = this.loadedConfig.raw.channels.slack;
|
|
67395
|
+
let resolved2 = {
|
|
67396
|
+
queueStart: channelConfig2.surfaceNotifications?.queueStart ?? "brief",
|
|
67397
|
+
loopStart: channelConfig2.surfaceNotifications?.loopStart ?? "brief"
|
|
67398
|
+
};
|
|
67399
|
+
if (identity.conversationKind === "dm") {
|
|
67400
|
+
return {
|
|
67401
|
+
...resolved2,
|
|
67402
|
+
...channelConfig2.directMessages.surfaceNotifications ?? {}
|
|
67403
|
+
};
|
|
67404
|
+
}
|
|
67405
|
+
const routeCollection = identity.conversationKind === "group" ? channelConfig2.groups : channelConfig2.channels;
|
|
67406
|
+
const route = identity.channelId ? routeCollection[identity.channelId] ?? routeCollection["*"] : undefined;
|
|
67407
|
+
return {
|
|
67408
|
+
...resolved2,
|
|
67409
|
+
...route?.surfaceNotifications ?? {}
|
|
67410
|
+
};
|
|
67411
|
+
}
|
|
67412
|
+
const channelConfig = this.loadedConfig.raw.channels.telegram;
|
|
67413
|
+
let resolved = {
|
|
67414
|
+
queueStart: channelConfig.surfaceNotifications?.queueStart ?? "brief",
|
|
67415
|
+
loopStart: channelConfig.surfaceNotifications?.loopStart ?? "brief"
|
|
67416
|
+
};
|
|
67417
|
+
if (identity.conversationKind === "dm") {
|
|
67418
|
+
return {
|
|
67419
|
+
...resolved,
|
|
67420
|
+
...channelConfig.directMessages.surfaceNotifications ?? {}
|
|
67421
|
+
};
|
|
67422
|
+
}
|
|
67423
|
+
const groupRoute = identity.chatId ? channelConfig.groups[identity.chatId] ?? channelConfig.groups["*"] : undefined;
|
|
67424
|
+
resolved = {
|
|
67425
|
+
...resolved,
|
|
67426
|
+
...groupRoute?.surfaceNotifications ?? {}
|
|
67427
|
+
};
|
|
67428
|
+
if (identity.conversationKind === "topic" && identity.topicId) {
|
|
67429
|
+
return {
|
|
67430
|
+
...resolved,
|
|
67431
|
+
...groupRoute?.topics?.[identity.topicId]?.surfaceNotifications ?? {}
|
|
67432
|
+
};
|
|
67433
|
+
}
|
|
67434
|
+
return resolved;
|
|
67435
|
+
}
|
|
67436
|
+
async notifyManagedLoopStart(target, loop) {
|
|
67437
|
+
if (!loop.surfaceBinding) {
|
|
67438
|
+
return;
|
|
67439
|
+
}
|
|
67440
|
+
const identity = this.buildLoopChannelIdentity(loop.surfaceBinding);
|
|
67441
|
+
const notifications = this.resolveLoopSurfaceNotifications(identity);
|
|
67442
|
+
const text = loop.kind === "calendar" ? renderLoopStartNotification({
|
|
67443
|
+
mode: notifications.loopStart,
|
|
67444
|
+
agentId: target.agentId,
|
|
67445
|
+
loopId: loop.id,
|
|
67446
|
+
promptSummary: loop.promptSummary,
|
|
67447
|
+
cadence: loop.cadence,
|
|
67448
|
+
dayOfWeek: loop.dayOfWeek,
|
|
67449
|
+
localTime: loop.localTime,
|
|
67450
|
+
timezone: loop.timezone,
|
|
67451
|
+
nextRunAt: loop.nextRunAt,
|
|
67452
|
+
remainingRuns: Math.max(0, loop.maxRuns - loop.attemptedRuns),
|
|
67453
|
+
maxRuns: loop.maxRuns,
|
|
67454
|
+
kind: "calendar"
|
|
67455
|
+
}) : renderLoopStartNotification({
|
|
67456
|
+
mode: notifications.loopStart,
|
|
67457
|
+
agentId: target.agentId,
|
|
67458
|
+
loopId: loop.id,
|
|
67459
|
+
promptSummary: loop.promptSummary,
|
|
67460
|
+
intervalMs: loop.intervalMs,
|
|
67461
|
+
nextRunAt: loop.nextRunAt,
|
|
67462
|
+
remainingRuns: Math.max(0, loop.maxRuns - loop.attemptedRuns),
|
|
67463
|
+
maxRuns: loop.maxRuns
|
|
67464
|
+
});
|
|
67465
|
+
if (!text) {
|
|
67466
|
+
return;
|
|
67467
|
+
}
|
|
67468
|
+
try {
|
|
67469
|
+
await this.notifySurface({
|
|
67470
|
+
binding: loop.surfaceBinding,
|
|
67471
|
+
text
|
|
67472
|
+
});
|
|
67473
|
+
} catch (error) {
|
|
67474
|
+
console.error("loop start notification failed", error);
|
|
67475
|
+
}
|
|
67476
|
+
}
|
|
67477
|
+
scheduleIntervalLoopTimer(loopId, delayMs) {
|
|
67478
|
+
const managed = this.intervalLoops.get(loopId);
|
|
67479
|
+
if (!managed) {
|
|
67480
|
+
return;
|
|
67481
|
+
}
|
|
67482
|
+
if (managed.timer) {
|
|
66912
67483
|
clearTimeout(managed.timer);
|
|
66913
67484
|
this.loopTimers.delete(managed.timer);
|
|
66914
67485
|
}
|
|
@@ -66919,7 +67490,9 @@ class AgentService {
|
|
|
66919
67490
|
return;
|
|
66920
67491
|
}
|
|
66921
67492
|
current.timer = undefined;
|
|
66922
|
-
this.runIntervalLoopIteration(loopId
|
|
67493
|
+
this.runIntervalLoopIteration(loopId, {
|
|
67494
|
+
notifyStart: true
|
|
67495
|
+
}).catch((error) => {
|
|
66923
67496
|
if (this.shouldSuppressLoopShutdownError(error)) {
|
|
66924
67497
|
return;
|
|
66925
67498
|
}
|
|
@@ -66977,6 +67550,7 @@ class AgentService {
|
|
|
66977
67550
|
buildLoopChannelIdentity(binding) {
|
|
66978
67551
|
return {
|
|
66979
67552
|
platform: binding.platform,
|
|
67553
|
+
accountId: binding.accountId,
|
|
66980
67554
|
conversationKind: binding.conversationKind,
|
|
66981
67555
|
channelId: binding.channelId,
|
|
66982
67556
|
chatId: binding.chatId,
|
|
@@ -67386,18 +67960,6 @@ function parseAgentCommand(text, options = {}) {
|
|
|
67386
67960
|
source: "slash"
|
|
67387
67961
|
};
|
|
67388
67962
|
}
|
|
67389
|
-
if (lowered === "queue-list" || lowered === "queuelist") {
|
|
67390
|
-
return {
|
|
67391
|
-
type: "control",
|
|
67392
|
-
name: "queue-list"
|
|
67393
|
-
};
|
|
67394
|
-
}
|
|
67395
|
-
if (lowered === "queue-clear" || lowered === "queueclear") {
|
|
67396
|
-
return {
|
|
67397
|
-
type: "control",
|
|
67398
|
-
name: "queue-clear"
|
|
67399
|
-
};
|
|
67400
|
-
}
|
|
67401
67963
|
if (lowered === "loop") {
|
|
67402
67964
|
const loopText = withoutSlash.slice(command.length).trim();
|
|
67403
67965
|
const loweredLoopText = loopText.toLowerCase();
|
|
@@ -67445,9 +68007,37 @@ function parseAgentCommand(text, options = {}) {
|
|
|
67445
68007
|
};
|
|
67446
68008
|
}
|
|
67447
68009
|
if (lowered === "queue" || lowered === "q") {
|
|
68010
|
+
const queueText = withoutSlash.slice(command.length).trim();
|
|
68011
|
+
const normalizedQueueText = queueText.toLowerCase();
|
|
68012
|
+
if (lowered === "queue") {
|
|
68013
|
+
if (normalizedQueueText === "list") {
|
|
68014
|
+
return {
|
|
68015
|
+
type: "control",
|
|
68016
|
+
name: "queue-list"
|
|
68017
|
+
};
|
|
68018
|
+
}
|
|
68019
|
+
if (normalizedQueueText === "clear") {
|
|
68020
|
+
return {
|
|
68021
|
+
type: "control",
|
|
68022
|
+
name: "queue-clear"
|
|
68023
|
+
};
|
|
68024
|
+
}
|
|
68025
|
+
}
|
|
67448
68026
|
return {
|
|
67449
68027
|
type: "queue",
|
|
67450
|
-
text:
|
|
68028
|
+
text: queueText
|
|
68029
|
+
};
|
|
68030
|
+
}
|
|
68031
|
+
if (lowered === "queue-list" || lowered === "queuelist") {
|
|
68032
|
+
return {
|
|
68033
|
+
type: "control",
|
|
68034
|
+
name: "queue-list"
|
|
68035
|
+
};
|
|
68036
|
+
}
|
|
68037
|
+
if (lowered === "queue-clear" || lowered === "queueclear") {
|
|
68038
|
+
return {
|
|
68039
|
+
type: "control",
|
|
68040
|
+
name: "queue-clear"
|
|
67451
68041
|
};
|
|
67452
68042
|
}
|
|
67453
68043
|
if (lowered === "steer" || lowered === "s") {
|
|
@@ -67495,11 +68085,7 @@ function renderAgentControlSlashHelp() {
|
|
|
67495
68085
|
"- `/followup mention-only`: require explicit mention for each later turn",
|
|
67496
68086
|
"- `/followup pause`: stop passive follow-up until the next explicit mention",
|
|
67497
68087
|
"- `/followup resume`: clear the runtime override and restore config defaults",
|
|
67498
|
-
"- `/streaming status`: show
|
|
67499
|
-
"- `/streaming on`: enable streaming for this surface using the current `all` preview behavior",
|
|
67500
|
-
"- `/streaming off`: disable surface streaming previews for this surface",
|
|
67501
|
-
"- `/streaming latest`: prefer the latest preview shape for this surface",
|
|
67502
|
-
"- `/streaming all`: retain the full current preview shape for this surface",
|
|
68088
|
+
"- `/streaming status|on|off|latest|all`: show or change streaming mode for this surface",
|
|
67503
68089
|
"- `/responsemode status`: show the configured response mode for this surface",
|
|
67504
68090
|
"- `/responsemode capture-pane`: settle replies from captured pane output for this surface",
|
|
67505
68091
|
"- `/responsemode message-tool`: expect the agent to reply through `clisbot message send` for this surface",
|
|
@@ -67508,8 +68094,8 @@ function renderAgentControlSlashHelp() {
|
|
|
67508
68094
|
"- `/additionalmessagemode queue`: queue later user messages behind the active run for this surface",
|
|
67509
68095
|
"- `/queue <message>` or `\\q <message>`: enqueue a later message behind the active run and let clisbot deliver it in order",
|
|
67510
68096
|
"- `/steer <message>` or `\\s <message>`: inject a steering message into the active run immediately",
|
|
67511
|
-
"- `/queue
|
|
67512
|
-
"- `/queue
|
|
68097
|
+
"- `/queue list`: show queued messages that have not started yet",
|
|
68098
|
+
"- `/queue clear`: clear queued messages that have not started yet",
|
|
67513
68099
|
...renderLoopHelpLines(),
|
|
67514
68100
|
"- `/bash` followed by a shell command: requires `shellExecute` on the resolved agent role",
|
|
67515
68101
|
"- shortcut prefixes such as `!` run bash only when the resolved agent role allows `shellExecute`",
|
|
@@ -67580,11 +68166,11 @@ function formatChannelFollowUpStatus(params) {
|
|
|
67580
68166
|
}
|
|
67581
68167
|
|
|
67582
68168
|
// src/channels/streaming-config.ts
|
|
67583
|
-
function
|
|
68169
|
+
function getEditableConfigPath8() {
|
|
67584
68170
|
return process.env.CLISBOT_CONFIG_PATH;
|
|
67585
68171
|
}
|
|
67586
68172
|
async function getConversationStreaming(params) {
|
|
67587
|
-
const { config } = await readEditableConfig(
|
|
68173
|
+
const { config } = await readEditableConfig(getEditableConfigPath8());
|
|
67588
68174
|
const target = resolveConfiguredSurfaceModeTarget(config, "streaming", buildConfiguredTargetFromIdentity(params.identity));
|
|
67589
68175
|
return {
|
|
67590
68176
|
label: target.label,
|
|
@@ -67592,7 +68178,7 @@ async function getConversationStreaming(params) {
|
|
|
67592
68178
|
};
|
|
67593
68179
|
}
|
|
67594
68180
|
async function setConversationStreaming(params) {
|
|
67595
|
-
const { config, configPath } = await readEditableConfig(
|
|
68181
|
+
const { config, configPath } = await readEditableConfig(getEditableConfigPath8());
|
|
67596
68182
|
const target = resolveConfiguredSurfaceModeTarget(config, "streaming", buildConfiguredTargetFromIdentity(params.identity));
|
|
67597
68183
|
target.set(params.streaming);
|
|
67598
68184
|
await writeEditableConfig(configPath, config);
|
|
@@ -67673,7 +68259,7 @@ function renderRouteStatusMessage(params) {
|
|
|
67673
68259
|
if (params.identity.topicId) {
|
|
67674
68260
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
67675
68261
|
}
|
|
67676
|
-
lines.push(`streaming: \`${params.route.streaming}\``, `response: \`${params.route.response}\``, `responseMode: \`${params.route.responseMode}\``, `additionalMessageMode: \`${params.route.additionalMessageMode}\``, `verbose: \`${params.route.verbose}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `timezone: \`${params.route.timezone ?? "(inherit host/app)"}\``, `followUp.mode: \`${params.followUpState.overrideMode ?? params.route.followUp.mode}\``, `followUp.windowMinutes: \`${formatFollowUpTtlMinutes(params.route.followUp.participationTtlMs)}\``, `run.state: \`${params.runtimeState.state}\``);
|
|
68262
|
+
lines.push(`streaming: \`${params.route.streaming}\``, `response: \`${params.route.response}\``, `responseMode: \`${params.route.responseMode}\``, `additionalMessageMode: \`${params.route.additionalMessageMode}\``, `surfaceNotifications.queueStart: \`${params.route.surfaceNotifications.queueStart}\``, `surfaceNotifications.loopStart: \`${params.route.surfaceNotifications.loopStart}\``, `verbose: \`${params.route.verbose}\``, `principal: \`${params.auth.principal ?? "(none)"}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `timezone: \`${params.route.timezone ?? "(inherit host/app)"}\``, `followUp.mode: \`${params.followUpState.overrideMode ?? params.route.followUp.mode}\``, `followUp.windowMinutes: \`${formatFollowUpTtlMinutes(params.route.followUp.participationTtlMs)}\``, `run.state: \`${params.runtimeState.state}\``);
|
|
67677
68263
|
if (params.runtimeState.startedAt) {
|
|
67678
68264
|
lines.push(`run.startedAt: \`${new Date(params.runtimeState.startedAt).toISOString()}\``);
|
|
67679
68265
|
}
|
|
@@ -67687,7 +68273,7 @@ function renderRouteStatusMessage(params) {
|
|
|
67687
68273
|
lines.push(`- \`${loop.id}\` ${renderLoopStatusSchedule(loop)} remaining \`${loop.remainingRuns}\` nextRunAt \`${new Date(loop.nextRunAt).toISOString()}\``);
|
|
67688
68274
|
}
|
|
67689
68275
|
}
|
|
67690
|
-
lines.push("", "Useful commands:", "- `/help`", "- `/whoami`", "- `/status`", "- `/attach`, `/detach`, `/watch every 30s`", "- `/followup status`", "- `/streaming status`", "- `/responsemode status`", "- `/additionalmessagemode status`", "- `/loop status`, `/loop cancel`, `/loop cancel <id>`", "- `/queue <message>`, `/steer <message>`", "- `/queue
|
|
68276
|
+
lines.push("", "Useful commands:", "- `/help`", "- `/whoami`", "- `/status`", "- `/attach`, `/detach`, `/watch every 30s`", "- `/followup status`", "- `/streaming status|on|off|latest|all`", "- `/responsemode status`", "- `/additionalmessagemode status`", "- `/loop status`, `/loop cancel`, `/loop cancel <id>`", "- `/queue <message>`, `/steer <message>`", "- `/queue list`, `/queue clear`", params.route.verbose === "off" ? "- `/transcript` disabled on this route (`verbose: off`)" : "- `/transcript` enabled on this route (`verbose: minimal`)", "- `/bash` requires `shellExecute`");
|
|
67691
68277
|
return lines.join(`
|
|
67692
68278
|
`);
|
|
67693
68279
|
}
|
|
@@ -67910,6 +68496,7 @@ async function resolveLoopPromptText(params) {
|
|
|
67910
68496
|
function buildLoopSurfaceBinding(identity) {
|
|
67911
68497
|
return {
|
|
67912
68498
|
platform: identity.platform,
|
|
68499
|
+
accountId: identity.accountId,
|
|
67913
68500
|
conversationKind: identity.conversationKind,
|
|
67914
68501
|
channelId: identity.channelId,
|
|
67915
68502
|
chatId: identity.chatId,
|
|
@@ -67925,7 +68512,9 @@ async function executePromptDelivery(params) {
|
|
|
67925
68512
|
let finalReplyRecorded = false;
|
|
67926
68513
|
let loggedFirstRunningUpdate = false;
|
|
67927
68514
|
let activePreviewStartedAt;
|
|
67928
|
-
let
|
|
68515
|
+
let messageToolPreviewHandedOff = false;
|
|
68516
|
+
let queueStartPending = false;
|
|
68517
|
+
let deferredQueueStartPreview = false;
|
|
67929
68518
|
const paneManagedDelivery = params.route.responseMode === "capture-pane" || params.forceQueuedDelivery === true;
|
|
67930
68519
|
const messageToolPreview = params.route.responseMode === "message-tool" && params.forceQueuedDelivery !== true && params.route.streaming !== "off";
|
|
67931
68520
|
const previewEnabled = params.route.streaming !== "off" && (paneManagedDelivery || messageToolPreview);
|
|
@@ -67961,11 +68550,12 @@ async function executePromptDelivery(params) {
|
|
|
67961
68550
|
renderedState = undefined;
|
|
67962
68551
|
activePreviewStartedAt = undefined;
|
|
67963
68552
|
}
|
|
67964
|
-
function
|
|
67965
|
-
|
|
67966
|
-
|
|
67967
|
-
|
|
67968
|
-
|
|
68553
|
+
async function handoffMessageToolPreview() {
|
|
68554
|
+
if (messageToolPreviewHandedOff) {
|
|
68555
|
+
return;
|
|
68556
|
+
}
|
|
68557
|
+
messageToolPreviewHandedOff = true;
|
|
68558
|
+
await clearResponseText();
|
|
67969
68559
|
}
|
|
67970
68560
|
async function getMessageToolRuntimeSignals() {
|
|
67971
68561
|
if (params.route.responseMode !== "message-tool" || params.forceQueuedDelivery === true) {
|
|
@@ -67999,6 +68589,68 @@ async function executePromptDelivery(params) {
|
|
|
67999
68589
|
agentId: params.route.agentId,
|
|
68000
68590
|
sessionKey: params.sessionTarget.sessionKey
|
|
68001
68591
|
});
|
|
68592
|
+
async function maybeRenderQueueStartNotification() {
|
|
68593
|
+
if (!queueStartPending) {
|
|
68594
|
+
return false;
|
|
68595
|
+
}
|
|
68596
|
+
const text = renderQueueStartNotification({
|
|
68597
|
+
mode: params.queueStartMode ?? "none",
|
|
68598
|
+
agentId: params.route.agentId,
|
|
68599
|
+
promptSummary: params.notificationPromptSummary ?? summarizeSurfaceNotificationText(params.promptText)
|
|
68600
|
+
});
|
|
68601
|
+
if (!text) {
|
|
68602
|
+
queueStartPending = false;
|
|
68603
|
+
return false;
|
|
68604
|
+
}
|
|
68605
|
+
if (previewEnabled && responseChunks.length === 0) {
|
|
68606
|
+
deferredQueueStartPreview = true;
|
|
68607
|
+
return true;
|
|
68608
|
+
}
|
|
68609
|
+
queueStartPending = false;
|
|
68610
|
+
if (responseChunks.length > 0) {
|
|
68611
|
+
const postedNew = await renderResponseText(text);
|
|
68612
|
+
if (postedNew) {
|
|
68613
|
+
await recordVisibleReply("reply", "channel");
|
|
68614
|
+
activePreviewStartedAt = Date.now();
|
|
68615
|
+
}
|
|
68616
|
+
renderedState = {
|
|
68617
|
+
text,
|
|
68618
|
+
body: ""
|
|
68619
|
+
};
|
|
68620
|
+
return true;
|
|
68621
|
+
}
|
|
68622
|
+
const posted = await params.postText(text);
|
|
68623
|
+
if (posted.length > 0) {
|
|
68624
|
+
await recordVisibleReply("reply", "channel");
|
|
68625
|
+
}
|
|
68626
|
+
return posted.length > 0;
|
|
68627
|
+
}
|
|
68628
|
+
function buildInitialPlaceholderText(positionAhead) {
|
|
68629
|
+
if (deferredQueueStartPreview && queueStartPending) {
|
|
68630
|
+
deferredQueueStartPreview = false;
|
|
68631
|
+
queueStartPending = false;
|
|
68632
|
+
return renderQueueStartNotification({
|
|
68633
|
+
mode: params.queueStartMode ?? "none",
|
|
68634
|
+
agentId: params.route.agentId,
|
|
68635
|
+
promptSummary: params.notificationPromptSummary ?? summarizeSurfaceNotificationText(params.promptText)
|
|
68636
|
+
}) ?? renderPlatformInteraction({
|
|
68637
|
+
platform: params.identity.platform,
|
|
68638
|
+
status: positionAhead > 0 ? "queued" : "running",
|
|
68639
|
+
content: "",
|
|
68640
|
+
queuePosition: positionAhead,
|
|
68641
|
+
maxChars: Number.POSITIVE_INFINITY,
|
|
68642
|
+
note: positionAhead > 0 ? "Waiting for the agent queue to clear." : "Working..."
|
|
68643
|
+
});
|
|
68644
|
+
}
|
|
68645
|
+
return renderPlatformInteraction({
|
|
68646
|
+
platform: params.identity.platform,
|
|
68647
|
+
status: positionAhead > 0 ? "queued" : "running",
|
|
68648
|
+
content: "",
|
|
68649
|
+
queuePosition: positionAhead,
|
|
68650
|
+
maxChars: Number.POSITIVE_INFINITY,
|
|
68651
|
+
note: positionAhead > 0 ? "Waiting for the agent queue to clear." : "Working..."
|
|
68652
|
+
});
|
|
68653
|
+
}
|
|
68002
68654
|
try {
|
|
68003
68655
|
const { positionAhead, result } = params.agentService.enqueuePrompt(params.sessionTarget, params.promptText, {
|
|
68004
68656
|
observerId: params.observerId,
|
|
@@ -68007,9 +68659,6 @@ async function executePromptDelivery(params) {
|
|
|
68007
68659
|
if (!paneManagedDelivery && !messageToolPreview) {
|
|
68008
68660
|
return;
|
|
68009
68661
|
}
|
|
68010
|
-
if (params.route.streaming === "off" && update.status === "running") {
|
|
68011
|
-
return;
|
|
68012
|
-
}
|
|
68013
68662
|
if (update.status === "running" && !loggedFirstRunningUpdate) {
|
|
68014
68663
|
loggedFirstRunningUpdate = true;
|
|
68015
68664
|
logLatencyDebug("channel-first-running-update", params.timingContext, {
|
|
@@ -68018,14 +68667,20 @@ async function executePromptDelivery(params) {
|
|
|
68018
68667
|
});
|
|
68019
68668
|
}
|
|
68020
68669
|
await (renderChain = renderChain.then(async () => {
|
|
68021
|
-
|
|
68022
|
-
if (
|
|
68023
|
-
|
|
68024
|
-
|
|
68670
|
+
let renderedQueueStart = false;
|
|
68671
|
+
if (update.status === "running") {
|
|
68672
|
+
renderedQueueStart = await maybeRenderQueueStartNotification();
|
|
68673
|
+
}
|
|
68674
|
+
if (params.route.streaming === "off" && update.status === "running") {
|
|
68675
|
+
return;
|
|
68676
|
+
}
|
|
68677
|
+
if (renderedQueueStart) {
|
|
68025
68678
|
return;
|
|
68026
68679
|
}
|
|
68027
|
-
|
|
68028
|
-
|
|
68680
|
+
const signals = await getMessageToolRuntimeSignals();
|
|
68681
|
+
if (messageToolPreview && typeof activePreviewStartedAt === "number" && (typeof signals.messageToolFinalReplyAt === "number" && signals.messageToolFinalReplyAt >= activePreviewStartedAt || typeof signals.lastMessageToolReplyAt === "number" && signals.lastMessageToolReplyAt >= activePreviewStartedAt)) {
|
|
68682
|
+
await handoffMessageToolPreview();
|
|
68683
|
+
return;
|
|
68029
68684
|
}
|
|
68030
68685
|
const nextState2 = buildRenderedMessageState({
|
|
68031
68686
|
platform: params.identity.platform,
|
|
@@ -68041,9 +68696,6 @@ async function executePromptDelivery(params) {
|
|
|
68041
68696
|
if (renderedState?.text === nextState2.text) {
|
|
68042
68697
|
return;
|
|
68043
68698
|
}
|
|
68044
|
-
if (!renderedState && lastFrozenPreviewText === nextState2.text) {
|
|
68045
|
-
return;
|
|
68046
|
-
}
|
|
68047
68699
|
const postedNew2 = await renderResponseText(nextState2.text);
|
|
68048
68700
|
if (postedNew2) {
|
|
68049
68701
|
await recordVisibleReply("reply", "channel");
|
|
@@ -68053,15 +68705,9 @@ async function executePromptDelivery(params) {
|
|
|
68053
68705
|
}));
|
|
68054
68706
|
}
|
|
68055
68707
|
});
|
|
68708
|
+
queueStartPending = positionAhead > 0 && (params.queueStartMode ?? "none") !== "none";
|
|
68056
68709
|
if (previewEnabled) {
|
|
68057
|
-
const placeholderText =
|
|
68058
|
-
platform: params.identity.platform,
|
|
68059
|
-
status: positionAhead > 0 ? "queued" : "running",
|
|
68060
|
-
content: "",
|
|
68061
|
-
queuePosition: positionAhead,
|
|
68062
|
-
maxChars: Number.POSITIVE_INFINITY,
|
|
68063
|
-
note: positionAhead > 0 ? "Waiting for the agent queue to clear." : "Working..."
|
|
68064
|
-
});
|
|
68710
|
+
const placeholderText = buildInitialPlaceholderText(positionAhead);
|
|
68065
68711
|
const postedNew2 = await renderResponseText(placeholderText);
|
|
68066
68712
|
if (postedNew2) {
|
|
68067
68713
|
await recordVisibleReply("reply", "channel");
|
|
@@ -68091,6 +68737,10 @@ async function executePromptDelivery(params) {
|
|
|
68091
68737
|
}
|
|
68092
68738
|
const finalResult = await result;
|
|
68093
68739
|
await renderChain;
|
|
68740
|
+
await maybeRenderQueueStartNotification();
|
|
68741
|
+
if (!paneManagedDelivery && messageToolPreviewHandedOff) {
|
|
68742
|
+
return;
|
|
68743
|
+
}
|
|
68094
68744
|
const nextState = buildRenderedMessageState({
|
|
68095
68745
|
platform: params.identity.platform,
|
|
68096
68746
|
status: finalResult.status,
|
|
@@ -68136,6 +68786,21 @@ async function executePromptDelivery(params) {
|
|
|
68136
68786
|
if (finalResult.status === "completed") {
|
|
68137
68787
|
return;
|
|
68138
68788
|
}
|
|
68789
|
+
if (messageToolPreview && responseChunks.length > 0) {
|
|
68790
|
+
const postedNew2 = await renderResponseText(renderPlatformInteraction({
|
|
68791
|
+
platform: params.identity.platform,
|
|
68792
|
+
status: finalResult.status,
|
|
68793
|
+
content: "",
|
|
68794
|
+
maxChars: Number.POSITIVE_INFINITY,
|
|
68795
|
+
note: finalResult.note,
|
|
68796
|
+
allowTranscriptInspection: allowTranscriptInspectionForRoute(params.route),
|
|
68797
|
+
responsePolicy: params.route.response
|
|
68798
|
+
}));
|
|
68799
|
+
if (postedNew2) {
|
|
68800
|
+
await recordVisibleReply("reply", "channel");
|
|
68801
|
+
}
|
|
68802
|
+
return;
|
|
68803
|
+
}
|
|
68139
68804
|
if (params.route.streaming === "off" || responseChunks.length === 0) {
|
|
68140
68805
|
const postedNew2 = await renderResponseText(renderPlatformInteraction({
|
|
68141
68806
|
platform: params.identity.platform,
|
|
@@ -68557,6 +69222,8 @@ ${renderLoopUsage()}`);
|
|
|
68557
69222
|
route: params.route,
|
|
68558
69223
|
maxChars: params.maxChars,
|
|
68559
69224
|
promptText: buildLoopPromptText(resolvedLoopPrompt.text),
|
|
69225
|
+
queueStartMode: params.route.surfaceNotifications.queueStart,
|
|
69226
|
+
notificationPromptSummary: summarizeLoopPrompt(resolvedLoopPrompt.text, resolvedLoopPrompt.maintenancePrompt),
|
|
68560
69227
|
postText: params.postText,
|
|
68561
69228
|
reconcileText: params.reconcileText,
|
|
68562
69229
|
observerId: `${observerId}:loop:${index + 1}`
|
|
@@ -68697,6 +69364,8 @@ ${escapeCodeFence(shellResult.output)}
|
|
|
68697
69364
|
route: params.route,
|
|
68698
69365
|
maxChars: params.maxChars,
|
|
68699
69366
|
promptText: delayedPromptText,
|
|
69367
|
+
queueStartMode: params.route.surfaceNotifications.queueStart,
|
|
69368
|
+
notificationPromptSummary: explicitQueueMessage ?? summarizeSurfaceNotificationText(params.text),
|
|
68700
69369
|
postText: params.postText,
|
|
68701
69370
|
reconcileText: params.reconcileText,
|
|
68702
69371
|
observerId,
|
|
@@ -68757,6 +69426,23 @@ function isTelegramSenderAllowed(params) {
|
|
|
68757
69426
|
}
|
|
68758
69427
|
|
|
68759
69428
|
// src/auth/resolve.ts
|
|
69429
|
+
function mergeRoleDefinitions2(inherited, override) {
|
|
69430
|
+
return {
|
|
69431
|
+
allow: override?.allow ?? inherited?.allow ?? [],
|
|
69432
|
+
users: override?.users ?? inherited?.users ?? []
|
|
69433
|
+
};
|
|
69434
|
+
}
|
|
69435
|
+
function mergeRoleRecord2(defaults, overrides) {
|
|
69436
|
+
const merged = {};
|
|
69437
|
+
const roleNames = new Set([
|
|
69438
|
+
...Object.keys(defaults ?? {}),
|
|
69439
|
+
...Object.keys(overrides ?? {})
|
|
69440
|
+
]);
|
|
69441
|
+
for (const roleName of roleNames) {
|
|
69442
|
+
merged[roleName] = mergeRoleDefinitions2(defaults?.[roleName], overrides?.[roleName]);
|
|
69443
|
+
}
|
|
69444
|
+
return merged;
|
|
69445
|
+
}
|
|
68760
69446
|
function normalizePrincipal(principal) {
|
|
68761
69447
|
const trimmed = principal.trim();
|
|
68762
69448
|
if (!trimmed) {
|
|
@@ -68804,10 +69490,7 @@ function getAgentAuth(config, agentId) {
|
|
|
68804
69490
|
const override = entry?.auth;
|
|
68805
69491
|
return {
|
|
68806
69492
|
defaultRole: override?.defaultRole ?? defaults.defaultRole,
|
|
68807
|
-
roles:
|
|
68808
|
-
...defaults.roles,
|
|
68809
|
-
...override?.roles ?? {}
|
|
68810
|
-
}
|
|
69493
|
+
roles: mergeRoleRecord2(defaults.roles, override?.roles)
|
|
68811
69494
|
};
|
|
68812
69495
|
}
|
|
68813
69496
|
function getAllowedPermissions(roles, role) {
|
|
@@ -68930,6 +69613,10 @@ function buildSharedChannelRoute(params) {
|
|
|
68930
69613
|
response: params.route?.response ?? params.channelConfig.response,
|
|
68931
69614
|
responseMode: params.route?.responseMode ?? agentEntry?.responseMode ?? params.channelConfig.responseMode,
|
|
68932
69615
|
additionalMessageMode: params.route?.additionalMessageMode ?? agentEntry?.additionalMessageMode ?? params.channelConfig.additionalMessageMode,
|
|
69616
|
+
surfaceNotifications: {
|
|
69617
|
+
queueStart: params.route?.surfaceNotifications?.queueStart ?? params.channelConfig.surfaceNotifications?.queueStart ?? "brief",
|
|
69618
|
+
loopStart: params.route?.surfaceNotifications?.loopStart ?? params.channelConfig.surfaceNotifications?.loopStart ?? "brief"
|
|
69619
|
+
},
|
|
68933
69620
|
verbose: params.route?.verbose ?? params.channelConfig.verbose,
|
|
68934
69621
|
followUp: {
|
|
68935
69622
|
mode: params.route?.followUp?.mode ?? params.channelConfig.followUp.mode,
|
|
@@ -69906,6 +70593,20 @@ class SlackSocketService {
|
|
|
69906
70593
|
appToken: this.accountConfig.appToken,
|
|
69907
70594
|
socketMode: true
|
|
69908
70595
|
});
|
|
70596
|
+
this.agentService.registerSurfaceNotificationHandler({
|
|
70597
|
+
platform: "slack",
|
|
70598
|
+
accountId: this.accountId,
|
|
70599
|
+
handler: async ({ binding, text }) => {
|
|
70600
|
+
if (!binding.channelId) {
|
|
70601
|
+
return;
|
|
70602
|
+
}
|
|
70603
|
+
await postSlackText(this.app.client, {
|
|
70604
|
+
channel: binding.channelId,
|
|
70605
|
+
threadTs: binding.threadTs,
|
|
70606
|
+
text
|
|
70607
|
+
});
|
|
70608
|
+
}
|
|
70609
|
+
});
|
|
69909
70610
|
this.registerEvents();
|
|
69910
70611
|
}
|
|
69911
70612
|
getSlackMaxChars(agentId) {
|
|
@@ -70159,6 +70860,7 @@ class SlackSocketService {
|
|
|
70159
70860
|
const cliTool = getAgentEntry(this.loadedConfig, params.route.agentId)?.cliTool;
|
|
70160
70861
|
const identity = {
|
|
70161
70862
|
platform: "slack",
|
|
70863
|
+
accountId: this.accountId,
|
|
70162
70864
|
conversationKind: params.conversationKind,
|
|
70163
70865
|
senderId: typeof event.user === "string" ? event.user.trim().toUpperCase() : undefined,
|
|
70164
70866
|
channelId,
|
|
@@ -70342,6 +71044,10 @@ class SlackSocketService {
|
|
|
70342
71044
|
await this.startPromise;
|
|
70343
71045
|
}
|
|
70344
71046
|
async stop() {
|
|
71047
|
+
this.agentService.unregisterSurfaceNotificationHandler({
|
|
71048
|
+
platform: "slack",
|
|
71049
|
+
accountId: this.accountId
|
|
71050
|
+
});
|
|
70345
71051
|
await this.app.stop();
|
|
70346
71052
|
}
|
|
70347
71053
|
getBotUserLabel() {
|
|
@@ -71196,6 +71902,33 @@ async function resolveTelegramAttachmentPaths(params) {
|
|
|
71196
71902
|
return attachmentPaths;
|
|
71197
71903
|
}
|
|
71198
71904
|
|
|
71905
|
+
// src/channels/telegram/route-guidance.ts
|
|
71906
|
+
function renderTelegramRouteChoiceMessage(params) {
|
|
71907
|
+
const chatId = String(params.chatId);
|
|
71908
|
+
const topicId = params.topicId != null ? String(params.topicId) : undefined;
|
|
71909
|
+
const lines = [
|
|
71910
|
+
topicId != null ? "clisbot: this Telegram topic is not configured yet." : "clisbot: this Telegram group is not configured yet.",
|
|
71911
|
+
"",
|
|
71912
|
+
"Ask the bot owner to choose one of these:",
|
|
71913
|
+
"",
|
|
71914
|
+
"Add the whole group with the default agent:",
|
|
71915
|
+
`\`clisbot channels add telegram-group ${chatId}\``,
|
|
71916
|
+
"",
|
|
71917
|
+
"Add the whole group with a specific agent:",
|
|
71918
|
+
`\`clisbot channels add telegram-group ${chatId} --agent <id>\``
|
|
71919
|
+
];
|
|
71920
|
+
if (topicId != null) {
|
|
71921
|
+
lines.push("", "Add only this topic with a specific agent:", `\`clisbot channels add telegram-group ${chatId} --topic ${topicId} --agent <id>\``);
|
|
71922
|
+
}
|
|
71923
|
+
if (params.includeConfigPath) {
|
|
71924
|
+
lines.push("", topicId != null ? `Config path: \`channels.telegram.groups."${chatId}".topics."${topicId}"\`` : `Config path: \`channels.telegram.groups."${chatId}"\``);
|
|
71925
|
+
} else {
|
|
71926
|
+
lines.push("", "After that, routed commands such as `/status`, `/stop`, `/nudge`, `/followup`, and `/bash` will work here.");
|
|
71927
|
+
}
|
|
71928
|
+
return lines.join(`
|
|
71929
|
+
`);
|
|
71930
|
+
}
|
|
71931
|
+
|
|
71199
71932
|
// src/channels/telegram/typing.ts
|
|
71200
71933
|
var TELEGRAM_TYPING_HEARTBEAT_MS = 4500;
|
|
71201
71934
|
function logTelegramTypingError(onError, error) {
|
|
@@ -71258,8 +71991,10 @@ var TELEGRAM_FULL_COMMANDS = [
|
|
|
71258
71991
|
{ command: "followup", description: "Show or change follow-up mode" },
|
|
71259
71992
|
{ command: "streaming", description: "Show or change streaming mode" },
|
|
71260
71993
|
{ command: "responsemode", description: "Show or change response mode" },
|
|
71994
|
+
{ command: "additionalmessagemode", description: "Show or change later-message mode" },
|
|
71261
71995
|
{ command: "queue", description: "Queue a later message behind the active run" },
|
|
71262
71996
|
{ command: "steer", description: "Steer the active run immediately" },
|
|
71997
|
+
{ command: "loop", description: "Show or manage loops for this route" },
|
|
71263
71998
|
{ command: "bash", description: "Run bash in the agent workspace" }
|
|
71264
71999
|
];
|
|
71265
72000
|
var TELEGRAM_STARTUP_CONFLICT_MAX_WAIT_MS = 6000;
|
|
@@ -71337,6 +72072,7 @@ class TelegramPollingService {
|
|
|
71337
72072
|
activityStore;
|
|
71338
72073
|
accountId;
|
|
71339
72074
|
accountConfig;
|
|
72075
|
+
reportLifecycle;
|
|
71340
72076
|
botUserId = 0;
|
|
71341
72077
|
botUsername = "";
|
|
71342
72078
|
running = false;
|
|
@@ -71345,13 +72081,35 @@ class TelegramPollingService {
|
|
|
71345
72081
|
activePollController;
|
|
71346
72082
|
inFlightUpdates = new Set;
|
|
71347
72083
|
processingIndicators = new ConversationProcessingIndicatorCoordinator;
|
|
71348
|
-
constructor(loadedConfig, agentService, processedEventsStore, activityStore, accountId = "default", accountConfig) {
|
|
72084
|
+
constructor(loadedConfig, agentService, processedEventsStore, activityStore, accountId = "default", accountConfig, reportLifecycle) {
|
|
71349
72085
|
this.loadedConfig = loadedConfig;
|
|
71350
72086
|
this.agentService = agentService;
|
|
71351
72087
|
this.processedEventsStore = processedEventsStore;
|
|
71352
72088
|
this.activityStore = activityStore;
|
|
71353
72089
|
this.accountId = accountId;
|
|
71354
72090
|
this.accountConfig = accountConfig;
|
|
72091
|
+
this.reportLifecycle = reportLifecycle;
|
|
72092
|
+
this.agentService.registerSurfaceNotificationHandler({
|
|
72093
|
+
platform: "telegram",
|
|
72094
|
+
accountId: this.accountId,
|
|
72095
|
+
handler: async ({ binding, text }) => {
|
|
72096
|
+
if (!binding.chatId) {
|
|
72097
|
+
return;
|
|
72098
|
+
}
|
|
72099
|
+
const chatId = Number(binding.chatId);
|
|
72100
|
+
if (!Number.isFinite(chatId)) {
|
|
72101
|
+
return;
|
|
72102
|
+
}
|
|
72103
|
+
const topicId = binding.topicId ? Number(binding.topicId) : undefined;
|
|
72104
|
+
await postTelegramText({
|
|
72105
|
+
token: this.accountConfig.botToken,
|
|
72106
|
+
chatId,
|
|
72107
|
+
text,
|
|
72108
|
+
topicId: Number.isFinite(topicId) ? topicId : undefined,
|
|
72109
|
+
omitThreadId: shouldOmitTelegramThreadId(Number.isFinite(topicId) ? topicId : undefined)
|
|
72110
|
+
});
|
|
72111
|
+
}
|
|
72112
|
+
});
|
|
71355
72113
|
}
|
|
71356
72114
|
async start() {
|
|
71357
72115
|
const me = await callTelegramApi(this.accountConfig.botToken, "getMe", {});
|
|
@@ -71368,6 +72126,10 @@ class TelegramPollingService {
|
|
|
71368
72126
|
this.activePollController?.abort();
|
|
71369
72127
|
await this.loopPromise;
|
|
71370
72128
|
await Promise.allSettled([...this.inFlightUpdates]);
|
|
72129
|
+
this.agentService.unregisterSurfaceNotificationHandler({
|
|
72130
|
+
platform: "telegram",
|
|
72131
|
+
accountId: this.accountId
|
|
72132
|
+
});
|
|
71371
72133
|
}
|
|
71372
72134
|
getBotLabel() {
|
|
71373
72135
|
return this.botUsername ? `@${this.botUsername}` : `${this.botUserId || "unknown"}`;
|
|
@@ -71414,6 +72176,15 @@ class TelegramPollingService {
|
|
|
71414
72176
|
}
|
|
71415
72177
|
if (isTelegramPollingConflict(error)) {
|
|
71416
72178
|
this.running = false;
|
|
72179
|
+
await this.reportLifecycle?.({
|
|
72180
|
+
connection: "failed",
|
|
72181
|
+
summary: "Telegram polling stopped because another instance is already using this bot token.",
|
|
72182
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
72183
|
+
actions: [
|
|
72184
|
+
"stop the other Telegram poller that is using the same bot token",
|
|
72185
|
+
"run `clisbot start` again after the token is no longer in use elsewhere"
|
|
72186
|
+
]
|
|
72187
|
+
});
|
|
71417
72188
|
console.error("telegram polling stopped: another bot instance is already calling getUpdates for this token");
|
|
71418
72189
|
return;
|
|
71419
72190
|
}
|
|
@@ -71581,6 +72352,7 @@ class TelegramPollingService {
|
|
|
71581
72352
|
const senderName = [message.from?.first_name, message.from?.last_name].filter((value) => typeof value === "string" && value.trim().length > 0).join(" ").trim();
|
|
71582
72353
|
const identity = {
|
|
71583
72354
|
platform: "telegram",
|
|
72355
|
+
accountId: this.accountId,
|
|
71584
72356
|
conversationKind: routeInfo.conversationKind,
|
|
71585
72357
|
senderId: message.from?.id != null ? String(message.from.id).trim() : undefined,
|
|
71586
72358
|
senderName: senderName || message.from?.username?.trim() || undefined,
|
|
@@ -71993,7 +72765,7 @@ var telegramChannelPlugin = {
|
|
|
71993
72765
|
accountId,
|
|
71994
72766
|
config
|
|
71995
72767
|
})),
|
|
71996
|
-
createRuntimeService: (context, account) => new TelegramPollingService(context.loadedConfig, context.agentService, context.processedEventsStore, context.activityStore, account.accountId, account.config),
|
|
72768
|
+
createRuntimeService: (context, account) => new TelegramPollingService(context.loadedConfig, context.agentService, context.processedEventsStore, context.activityStore, account.accountId, account.config, context.reportLifecycle),
|
|
71997
72769
|
renderHealthSummary: (state) => {
|
|
71998
72770
|
switch (state) {
|
|
71999
72771
|
case "starting":
|
|
@@ -72087,7 +72859,7 @@ var defaultMessageCliDependencies = {
|
|
|
72087
72859
|
await agentService.recordConversationReply(target, kind, source);
|
|
72088
72860
|
}
|
|
72089
72861
|
};
|
|
72090
|
-
function
|
|
72862
|
+
function parseRepeatedOption3(args, name) {
|
|
72091
72863
|
const values = [];
|
|
72092
72864
|
for (let index = 0;index < args.length; index += 1) {
|
|
72093
72865
|
if (args[index] !== name) {
|
|
@@ -72102,7 +72874,7 @@ function parseRepeatedOption2(args, name) {
|
|
|
72102
72874
|
return values;
|
|
72103
72875
|
}
|
|
72104
72876
|
function parseOptionValue4(args, name) {
|
|
72105
|
-
const values =
|
|
72877
|
+
const values = parseRepeatedOption3(args, name);
|
|
72106
72878
|
return values.length > 0 ? values.at(-1) : undefined;
|
|
72107
72879
|
}
|
|
72108
72880
|
function parseIntegerOption(args, name) {
|
|
@@ -72116,7 +72888,7 @@ function parseIntegerOption(args, name) {
|
|
|
72116
72888
|
}
|
|
72117
72889
|
return parsed;
|
|
72118
72890
|
}
|
|
72119
|
-
function
|
|
72891
|
+
function hasFlag4(args, name) {
|
|
72120
72892
|
return args.includes(name);
|
|
72121
72893
|
}
|
|
72122
72894
|
function resolveReplyKind(command) {
|
|
@@ -72148,18 +72920,18 @@ function parseMessageCommand(args) {
|
|
|
72148
72920
|
media: parseOptionValue4(rest, "--media"),
|
|
72149
72921
|
messageId: parseOptionValue4(rest, "--message-id"),
|
|
72150
72922
|
emoji: parseOptionValue4(rest, "--emoji"),
|
|
72151
|
-
remove:
|
|
72923
|
+
remove: hasFlag4(rest, "--remove"),
|
|
72152
72924
|
threadId: parseOptionValue4(rest, "--thread-id"),
|
|
72153
72925
|
replyTo: parseOptionValue4(rest, "--reply-to"),
|
|
72154
72926
|
limit: parseIntegerOption(rest, "--limit"),
|
|
72155
72927
|
query: parseOptionValue4(rest, "--query"),
|
|
72156
72928
|
pollQuestion: parseOptionValue4(rest, "--poll-question"),
|
|
72157
|
-
pollOptions:
|
|
72158
|
-
forceDocument:
|
|
72159
|
-
silent:
|
|
72160
|
-
progress:
|
|
72161
|
-
final:
|
|
72162
|
-
json:
|
|
72929
|
+
pollOptions: parseRepeatedOption3(rest, "--poll-option"),
|
|
72930
|
+
forceDocument: hasFlag4(rest, "--force-document"),
|
|
72931
|
+
silent: hasFlag4(rest, "--silent"),
|
|
72932
|
+
progress: hasFlag4(rest, "--progress"),
|
|
72933
|
+
final: hasFlag4(rest, "--final"),
|
|
72934
|
+
json: hasFlag4(rest, "--json")
|
|
72163
72935
|
};
|
|
72164
72936
|
}
|
|
72165
72937
|
function renderMessageHelp() {
|
|
@@ -72382,6 +73154,7 @@ class RuntimeSupervisor {
|
|
|
72382
73154
|
reloadInFlight = false;
|
|
72383
73155
|
reloadRequested = false;
|
|
72384
73156
|
configWatchDebounceMs = 250;
|
|
73157
|
+
nextRuntimeId = 1;
|
|
72385
73158
|
dependencies;
|
|
72386
73159
|
constructor(configPath, dependencies) {
|
|
72387
73160
|
this.configPath = configPath;
|
|
@@ -72398,15 +73171,50 @@ class RuntimeSupervisor {
|
|
|
72398
73171
|
async start() {
|
|
72399
73172
|
await this.reload("initial");
|
|
72400
73173
|
}
|
|
72401
|
-
async stop() {
|
|
73174
|
+
async stop(options = {}) {
|
|
72402
73175
|
this.clearReloadTimer();
|
|
72403
73176
|
this.stopWatchingConfig();
|
|
72404
73177
|
await this.stopActiveRuntime();
|
|
73178
|
+
if (options.markChannelsStopped !== false) {
|
|
73179
|
+
for (const plugin of this.dependencies.listChannelPlugins()) {
|
|
73180
|
+
await this.dependencies.runtimeHealthStore.setChannel({
|
|
73181
|
+
channel: plugin.id,
|
|
73182
|
+
connection: "stopped",
|
|
73183
|
+
summary: plugin.renderHealthSummary("stopped")
|
|
73184
|
+
});
|
|
73185
|
+
}
|
|
73186
|
+
}
|
|
73187
|
+
}
|
|
73188
|
+
async markFatalFailure(error) {
|
|
73189
|
+
const activeRuntime = this.activeRuntime;
|
|
73190
|
+
if (!activeRuntime) {
|
|
73191
|
+
return;
|
|
73192
|
+
}
|
|
73193
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
73194
|
+
const instancesByChannel = new Map;
|
|
73195
|
+
for (const entry of activeRuntime.channelServices) {
|
|
73196
|
+
const identity = entry.service.getRuntimeIdentity?.();
|
|
73197
|
+
if (!identity) {
|
|
73198
|
+
continue;
|
|
73199
|
+
}
|
|
73200
|
+
const existing = instancesByChannel.get(entry.channel) ?? [];
|
|
73201
|
+
existing.push(identity);
|
|
73202
|
+
instancesByChannel.set(entry.channel, existing);
|
|
73203
|
+
}
|
|
72405
73204
|
for (const plugin of this.dependencies.listChannelPlugins()) {
|
|
73205
|
+
if (!plugin.isEnabled(activeRuntime.loadedConfig)) {
|
|
73206
|
+
continue;
|
|
73207
|
+
}
|
|
72406
73208
|
await this.dependencies.runtimeHealthStore.setChannel({
|
|
72407
73209
|
channel: plugin.id,
|
|
72408
|
-
connection: "
|
|
72409
|
-
summary:
|
|
73210
|
+
connection: "failed",
|
|
73211
|
+
summary: "Runtime crashed due to a fatal error.",
|
|
73212
|
+
detail,
|
|
73213
|
+
actions: [
|
|
73214
|
+
"run `clisbot logs` and inspect the fatal error",
|
|
73215
|
+
"fix the underlying runtime fault, then restart with `clisbot start`"
|
|
73216
|
+
],
|
|
73217
|
+
instances: instancesByChannel.get(plugin.id) ?? []
|
|
72410
73218
|
});
|
|
72411
73219
|
}
|
|
72412
73220
|
}
|
|
@@ -72484,6 +73292,7 @@ class RuntimeSupervisor {
|
|
|
72484
73292
|
}
|
|
72485
73293
|
}
|
|
72486
73294
|
async createRuntime(loadedConfig) {
|
|
73295
|
+
const runtimeId = this.nextRuntimeId++;
|
|
72487
73296
|
const agentService = this.dependencies.createAgentService(loadedConfig);
|
|
72488
73297
|
const processedEventsStore = this.dependencies.createProcessedEventsStore(loadedConfig.processedEventsPath);
|
|
72489
73298
|
const activityStore = this.dependencies.createActivityStore();
|
|
@@ -72505,7 +73314,14 @@ class RuntimeSupervisor {
|
|
|
72505
73314
|
loadedConfig,
|
|
72506
73315
|
agentService,
|
|
72507
73316
|
processedEventsStore,
|
|
72508
|
-
activityStore
|
|
73317
|
+
activityStore,
|
|
73318
|
+
reportLifecycle: (event) => this.reportChannelLifecycle({
|
|
73319
|
+
runtimeId,
|
|
73320
|
+
plugin,
|
|
73321
|
+
channelServices,
|
|
73322
|
+
accountId: account.accountId,
|
|
73323
|
+
event
|
|
73324
|
+
})
|
|
72509
73325
|
}, account)
|
|
72510
73326
|
});
|
|
72511
73327
|
}
|
|
@@ -72536,6 +73352,8 @@ class RuntimeSupervisor {
|
|
|
72536
73352
|
});
|
|
72537
73353
|
}
|
|
72538
73354
|
return {
|
|
73355
|
+
id: runtimeId,
|
|
73356
|
+
loadedConfig,
|
|
72539
73357
|
agentService,
|
|
72540
73358
|
channelServices
|
|
72541
73359
|
};
|
|
@@ -72575,6 +73393,38 @@ class RuntimeSupervisor {
|
|
|
72575
73393
|
});
|
|
72576
73394
|
}
|
|
72577
73395
|
}
|
|
73396
|
+
getChannelInstances(channelServices, channel) {
|
|
73397
|
+
return channelServices.filter((entry) => entry.channel === channel).map((entry) => entry.service.getRuntimeIdentity?.()).filter((identity) => identity != null);
|
|
73398
|
+
}
|
|
73399
|
+
async reportChannelLifecycle(params) {
|
|
73400
|
+
if (this.activeRuntime?.id !== params.runtimeId) {
|
|
73401
|
+
return;
|
|
73402
|
+
}
|
|
73403
|
+
const instances = this.getChannelInstances(params.channelServices, params.plugin.id);
|
|
73404
|
+
if (params.event.connection === "active") {
|
|
73405
|
+
await this.dependencies.runtimeHealthStore.setChannel({
|
|
73406
|
+
channel: params.plugin.id,
|
|
73407
|
+
connection: "active",
|
|
73408
|
+
summary: params.event.summary ?? params.plugin.renderActiveHealthSummary(Math.max(1, instances.length)),
|
|
73409
|
+
detail: params.event.detail,
|
|
73410
|
+
actions: params.event.actions,
|
|
73411
|
+
instances
|
|
73412
|
+
});
|
|
73413
|
+
return;
|
|
73414
|
+
}
|
|
73415
|
+
const detailPrefix = `account=${params.accountId}`;
|
|
73416
|
+
await this.dependencies.runtimeHealthStore.setChannel({
|
|
73417
|
+
channel: params.plugin.id,
|
|
73418
|
+
connection: "failed",
|
|
73419
|
+
summary: params.event.summary ?? `${params.plugin.id} channel failed after startup.`,
|
|
73420
|
+
detail: params.event.detail ? `${detailPrefix}; ${params.event.detail}` : detailPrefix,
|
|
73421
|
+
actions: params.event.actions ?? [
|
|
73422
|
+
"run `clisbot logs` and inspect the latest channel error",
|
|
73423
|
+
"restart `clisbot` after fixing the channel-level issue"
|
|
73424
|
+
],
|
|
73425
|
+
instances
|
|
73426
|
+
});
|
|
73427
|
+
}
|
|
72578
73428
|
async reconcileConfigWatcher(loadedConfig) {
|
|
72579
73429
|
const configReload = loadedConfig.raw.control.configReload;
|
|
72580
73430
|
this.configWatchDebounceMs = configReload.watchDebounceMs;
|
|
@@ -72765,6 +73615,11 @@ async function getRuntimeOperatorSummary(params) {
|
|
|
72765
73615
|
];
|
|
72766
73616
|
return {
|
|
72767
73617
|
loadedConfig,
|
|
73618
|
+
ownerSummary: {
|
|
73619
|
+
ownerPrincipals: loadedConfig.raw.app.auth.roles.owner?.users ?? [],
|
|
73620
|
+
adminPrincipals: loadedConfig.raw.app.auth.roles.admin?.users ?? [],
|
|
73621
|
+
ownerClaimWindowMinutes: loadedConfig.raw.app.auth.ownerClaimWindowMinutes
|
|
73622
|
+
},
|
|
72768
73623
|
agentSummaries,
|
|
72769
73624
|
channelSummaries,
|
|
72770
73625
|
activeRuns: await agentService.listActiveSessionRuntimes(),
|
|
@@ -72846,11 +73701,75 @@ function renderAgentSummaryLines(summary) {
|
|
|
72846
73701
|
})
|
|
72847
73702
|
];
|
|
72848
73703
|
}
|
|
73704
|
+
function renderOwnerSummaryLines(summary) {
|
|
73705
|
+
const ownerPrincipals = summary.ownerSummary.ownerPrincipals;
|
|
73706
|
+
const adminPrincipals = summary.ownerSummary.adminPrincipals;
|
|
73707
|
+
const configuredOwners = ownerPrincipals.length > 0 ? ownerPrincipals.join(",") : "none";
|
|
73708
|
+
const claimWindow = `${summary.ownerSummary.ownerClaimWindowMinutes}m`;
|
|
73709
|
+
const lines = [
|
|
73710
|
+
"Owner:",
|
|
73711
|
+
` - configured=${ownerPrincipals.length > 0 ? "yes" : "no"} principals=${configuredOwners} claimWindow=${claimWindow}`,
|
|
73712
|
+
" - access=full app control, DM pairing bypass, implicit admin across all agents/channels"
|
|
73713
|
+
];
|
|
73714
|
+
if (adminPrincipals.length > 0) {
|
|
73715
|
+
lines.push(` - appAdmins=${adminPrincipals.join(",")}`);
|
|
73716
|
+
}
|
|
73717
|
+
return lines;
|
|
73718
|
+
}
|
|
73719
|
+
function hasConfiguredPrivilegedPrincipal(summary) {
|
|
73720
|
+
return summary.ownerSummary.ownerPrincipals.length > 0 || summary.ownerSummary.adminPrincipals.length > 0;
|
|
73721
|
+
}
|
|
73722
|
+
function renderPrivilegedChatHint(summary, action) {
|
|
73723
|
+
if (hasConfiguredPrivilegedPrincipal(summary)) {
|
|
73724
|
+
return `chat with the bot from that owner/admin account to ${action}`;
|
|
73725
|
+
}
|
|
73726
|
+
return `after you configure an owner/admin principal, use that account to ${action}`;
|
|
73727
|
+
}
|
|
73728
|
+
function appendChannelNextStepLines(lines, summary, prefix = "") {
|
|
73729
|
+
const slackEnabled = summary.channelSummaries.some((channel) => channel.channel === "slack" && channel.enabled);
|
|
73730
|
+
const telegramEnabled = summary.channelSummaries.some((channel) => channel.channel === "telegram" && channel.enabled);
|
|
73731
|
+
const hasEnabledChannel = slackEnabled || telegramEnabled;
|
|
73732
|
+
if (!hasEnabledChannel) {
|
|
73733
|
+
lines.push(`${prefix}- run \`clisbot channels enable <slack|telegram>\` for the first channel you want to expose`);
|
|
73734
|
+
return;
|
|
73735
|
+
}
|
|
73736
|
+
if (telegramEnabled && slackEnabled) {
|
|
73737
|
+
lines.push(`${prefix}- DM the Telegram or Slack bot first to confirm it responds normally`);
|
|
73738
|
+
} else if (telegramEnabled) {
|
|
73739
|
+
lines.push(`${prefix}- DM the Telegram bot first to confirm it responds normally`);
|
|
73740
|
+
} else {
|
|
73741
|
+
lines.push(`${prefix}- DM the Slack bot first to confirm it responds normally`);
|
|
73742
|
+
}
|
|
73743
|
+
lines.push(`${prefix}- after DM works, add the bot to the target Slack channel or Telegram group/topic`);
|
|
73744
|
+
lines.push(`${prefix}- route that surface with \`clisbot channels add slack-channel <channelId> --agent <id>\` or \`clisbot channels add telegram-group <chatId> --agent <id>\``);
|
|
73745
|
+
if (telegramEnabled) {
|
|
73746
|
+
lines.push(`${prefix}- Telegram: send \`/start\` in the target DM, group, or topic to get onboarding or pairing guidance`);
|
|
73747
|
+
}
|
|
73748
|
+
if (slackEnabled) {
|
|
73749
|
+
lines.push(`${prefix}- Slack: mention \`@<botname> \\start\` in the target channel to verify mention flow`);
|
|
73750
|
+
}
|
|
73751
|
+
}
|
|
73752
|
+
function appendAuthOnboardingLines(lines, summary, prefix = "") {
|
|
73753
|
+
lines.push(`${prefix}Auth onboarding:`);
|
|
73754
|
+
if (!hasConfiguredPrivilegedPrincipal(summary)) {
|
|
73755
|
+
lines.push(`${prefix} - get the principal from a surface the bot can already see; Telegram groups or topics can use \`/whoami\` before routing, while DMs with pairing must pair first`);
|
|
73756
|
+
lines.push(`${prefix} - set the first owner with: \`clisbot auth add-user app --role owner --user <principal>\``);
|
|
73757
|
+
} else {
|
|
73758
|
+
lines.push(`${prefix} - inspect current app roles with: \`clisbot auth show app\``);
|
|
73759
|
+
}
|
|
73760
|
+
lines.push(`${prefix} - inspect default agent roles with: \`clisbot auth show agent-defaults\``);
|
|
73761
|
+
lines.push(`${prefix} - add or remove principals with: \`clisbot auth add-user ...\` and \`clisbot auth remove-user ...\``);
|
|
73762
|
+
lines.push(`${prefix} - tune role permissions with: \`clisbot auth add-permission ...\` and \`clisbot auth remove-permission ...\``);
|
|
73763
|
+
lines.push(`${prefix} - run \`clisbot auth --help\` or read docs/user-guide/auth-and-roles.md for scopes and permission names`);
|
|
73764
|
+
}
|
|
72849
73765
|
function renderChannelSummaryLines(summary) {
|
|
72850
73766
|
return [
|
|
72851
73767
|
"",
|
|
72852
73768
|
"Channels:",
|
|
72853
73769
|
...summary.channelSummaries.map((channel) => {
|
|
73770
|
+
if (!channel.enabled) {
|
|
73771
|
+
return ` - ${channel.channel} enabled=no`;
|
|
73772
|
+
}
|
|
72854
73773
|
const last = channel.lastActivityAt ? ` last=${formatTime(channel.lastActivityAt)} via ${channel.lastActivityAgentId ?? "unknown"}` : " last=never";
|
|
72855
73774
|
const dm = ` dm=${channel.directMessagesEnabled ? channel.directMessagesPolicy : "disabled"}`;
|
|
72856
73775
|
const group = channel.groupPolicy ? ` groups=${channel.groupPolicy}` : "";
|
|
@@ -72906,8 +73825,9 @@ function renderRuntimeDiagnosticsSummary(summary) {
|
|
|
72906
73825
|
`).trim();
|
|
72907
73826
|
}
|
|
72908
73827
|
function renderStartSummary(summary) {
|
|
72909
|
-
const configPath = collapseHomePath(getDefaultConfigPath());
|
|
72910
73828
|
const lines = [
|
|
73829
|
+
...renderOwnerSummaryLines(summary),
|
|
73830
|
+
"",
|
|
72911
73831
|
...renderAgentSummaryLines(summary),
|
|
72912
73832
|
...renderChannelSummaryLines(summary)
|
|
72913
73833
|
];
|
|
@@ -72945,7 +73865,10 @@ function renderStartSummary(summary) {
|
|
|
72945
73865
|
lines.push("");
|
|
72946
73866
|
lines.push(" Next steps after bootstrap:");
|
|
72947
73867
|
lines.push(" - chat with the bot or open the workspace, then follow BOOTSTRAP.md");
|
|
72948
|
-
lines
|
|
73868
|
+
appendChannelNextStepLines(lines, summary, " ");
|
|
73869
|
+
lines.push(` - ${renderPrivilegedChatHint(summary, "verify DM access and adjust in-chat settings")}`);
|
|
73870
|
+
lines.push("");
|
|
73871
|
+
appendAuthOnboardingLines(lines, summary, " ");
|
|
72949
73872
|
lines.push(" - run `clisbot status` to recheck runtime and bootstrap state");
|
|
72950
73873
|
lines.push(" - run `clisbot logs` if the bot does not answer as expected");
|
|
72951
73874
|
lines.push(...renderPairingSetupHelpLines(" ", {
|
|
@@ -72963,9 +73886,11 @@ function renderStartSummary(summary) {
|
|
|
72963
73886
|
}
|
|
72964
73887
|
lines.push("");
|
|
72965
73888
|
lines.push("Next steps:");
|
|
72966
|
-
lines
|
|
72967
|
-
lines.push(" - verify
|
|
72968
|
-
lines.push(
|
|
73889
|
+
appendChannelNextStepLines(lines, summary, " ");
|
|
73890
|
+
lines.push(" - verify routes and defaultAgentId values match the agent you want to expose");
|
|
73891
|
+
lines.push(` - ${renderPrivilegedChatHint(summary, "adjust in-chat surface settings")}`);
|
|
73892
|
+
lines.push("");
|
|
73893
|
+
appendAuthOnboardingLines(lines, summary);
|
|
72969
73894
|
lines.push(" - run `clisbot status` to inspect agents, channels, and tmux session state");
|
|
72970
73895
|
lines.push(" - run `clisbot logs` if anything looks wrong");
|
|
72971
73896
|
lines.push(...renderPairingSetupHelpLines("", {
|
|
@@ -72992,20 +73917,24 @@ function appendChannelSetupNotes(lines, summary, prefix = "") {
|
|
|
72992
73917
|
if (channel.channel === "telegram") {
|
|
72993
73918
|
lines.push(`${prefix} - telegram: no explicit group or topic routes are configured yet`);
|
|
72994
73919
|
lines.push(`${prefix} dms: ${channel.directMessagesEnabled ? `enabled (${channel.directMessagesPolicy})` : "disabled"}`);
|
|
72995
|
-
lines.push(`${prefix}
|
|
72996
|
-
lines.push(`${prefix}
|
|
72997
|
-
lines.push(`${prefix}
|
|
73920
|
+
lines.push(`${prefix} add group: \`clisbot channels add telegram-group <chatId> --agent <id>\``);
|
|
73921
|
+
lines.push(`${prefix} add topic: \`clisbot channels add telegram-group <chatId> --topic <topicId> --agent <id>\``);
|
|
73922
|
+
lines.push(`${prefix} adjust later: ${renderPrivilegedChatHint(summary, "run in-chat commands here")}`);
|
|
72998
73923
|
continue;
|
|
72999
73924
|
}
|
|
73000
73925
|
lines.push(`${prefix} - slack: no explicit channel or group routes are configured yet`);
|
|
73001
73926
|
lines.push(`${prefix} dms: ${channel.directMessagesEnabled ? `enabled (${channel.directMessagesPolicy})` : "disabled"}`);
|
|
73002
73927
|
lines.push(`${prefix} groups: ${channel.groupPolicy ?? "n/a"}`);
|
|
73003
|
-
lines.push(`${prefix}
|
|
73928
|
+
lines.push(`${prefix} add channel: \`clisbot channels add slack-channel <channelId> --agent <id>\``);
|
|
73929
|
+
lines.push(`${prefix} add group: \`clisbot channels add slack-group <groupId> --agent <id>\``);
|
|
73930
|
+
lines.push(`${prefix} adjust later: ${renderPrivilegedChatHint(summary, "run in-chat commands here")}`);
|
|
73004
73931
|
}
|
|
73005
73932
|
}
|
|
73006
73933
|
function renderStatusSummary(summary) {
|
|
73007
73934
|
const lines = [
|
|
73008
73935
|
`stats agents=${summary.configuredAgents} bootstrapped=${summary.bootstrappedAgents} pendingBootstrap=${summary.bootstrapPendingAgents} tmuxSessions=${summary.runningTmuxSessions}`,
|
|
73936
|
+
...renderOwnerSummaryLines(summary),
|
|
73937
|
+
"",
|
|
73009
73938
|
...renderAgentSummaryLines(summary),
|
|
73010
73939
|
...renderChannelSummaryLines(summary),
|
|
73011
73940
|
...renderChannelDiagnosticLines(summary),
|
|
@@ -73027,6 +73956,15 @@ function printCommandOutcomeBanner(outcome) {
|
|
|
73027
73956
|
function printCommandOutcomeFooter(outcome) {
|
|
73028
73957
|
printCommandOutcomeBanner(outcome);
|
|
73029
73958
|
}
|
|
73959
|
+
function assertSupportedPlatform(command) {
|
|
73960
|
+
if (process.platform !== "win32") {
|
|
73961
|
+
return;
|
|
73962
|
+
}
|
|
73963
|
+
if (command.name === "help" || command.name === "version") {
|
|
73964
|
+
return;
|
|
73965
|
+
}
|
|
73966
|
+
throw new Error("Native Windows is not supported yet. Run clisbot from WSL2 or use Linux/macOS instead.");
|
|
73967
|
+
}
|
|
73030
73968
|
function getPrimaryWorkspacePath(summary) {
|
|
73031
73969
|
const preferredAgentId = summary.channelSummaries.find((channel) => channel.enabled)?.defaultAgentId ?? "default";
|
|
73032
73970
|
return summary.agentSummaries.find((agent) => agent.id === preferredAgentId)?.workspacePath ?? summary.agentSummaries[0]?.workspacePath;
|
|
@@ -73176,25 +74114,57 @@ async function serveForeground() {
|
|
|
73176
74114
|
const runtimeCredentialsPath = getDefaultRuntimeCredentialsPath();
|
|
73177
74115
|
const runtimeSupervisor = new RuntimeSupervisor(configPath);
|
|
73178
74116
|
let shuttingDown = false;
|
|
73179
|
-
|
|
74117
|
+
let fatalHandling = false;
|
|
74118
|
+
const shutdown = async (exitCode = 0, options = {}) => {
|
|
73180
74119
|
if (shuttingDown) {
|
|
73181
74120
|
return;
|
|
73182
74121
|
}
|
|
73183
74122
|
shuttingDown = true;
|
|
73184
74123
|
try {
|
|
73185
|
-
await runtimeSupervisor.stop(
|
|
74124
|
+
await runtimeSupervisor.stop({
|
|
74125
|
+
markChannelsStopped: options.markChannelsStopped
|
|
74126
|
+
});
|
|
73186
74127
|
} finally {
|
|
73187
74128
|
removeRuntimeCredentials(runtimeCredentialsPath);
|
|
73188
74129
|
removeRuntimePid(pidPath);
|
|
73189
74130
|
process.exit(exitCode);
|
|
73190
74131
|
}
|
|
73191
74132
|
};
|
|
74133
|
+
const handleFatal = async (source, error) => {
|
|
74134
|
+
if (fatalHandling || shuttingDown) {
|
|
74135
|
+
return;
|
|
74136
|
+
}
|
|
74137
|
+
fatalHandling = true;
|
|
74138
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
74139
|
+
const fatalError = new Error(`fatal ${source}: ${detail}`);
|
|
74140
|
+
console.error(`clisbot fatal ${source}`, error);
|
|
74141
|
+
const forceExitTimer = setTimeout(() => {
|
|
74142
|
+
process.exit(1);
|
|
74143
|
+
}, 5000);
|
|
74144
|
+
forceExitTimer.unref?.();
|
|
74145
|
+
try {
|
|
74146
|
+
await runtimeSupervisor.markFatalFailure(fatalError);
|
|
74147
|
+
} catch (markError) {
|
|
74148
|
+
console.error("failed to record fatal runtime health", markError);
|
|
74149
|
+
}
|
|
74150
|
+
try {
|
|
74151
|
+
await shutdown(1, { markChannelsStopped: false });
|
|
74152
|
+
} finally {
|
|
74153
|
+
clearTimeout(forceExitTimer);
|
|
74154
|
+
}
|
|
74155
|
+
};
|
|
73192
74156
|
process.once("SIGINT", () => {
|
|
73193
74157
|
shutdown(0);
|
|
73194
74158
|
});
|
|
73195
74159
|
process.once("SIGTERM", () => {
|
|
73196
74160
|
shutdown(0);
|
|
73197
74161
|
});
|
|
74162
|
+
process.once("uncaughtException", (error) => {
|
|
74163
|
+
handleFatal("uncaughtException", error);
|
|
74164
|
+
});
|
|
74165
|
+
process.once("unhandledRejection", (error) => {
|
|
74166
|
+
handleFatal("unhandledRejection", error);
|
|
74167
|
+
});
|
|
73198
74168
|
try {
|
|
73199
74169
|
await runtimeSupervisor.start();
|
|
73200
74170
|
await writeRuntimePid(pidPath, process.pid);
|
|
@@ -73271,7 +74241,7 @@ async function start(args = []) {
|
|
|
73271
74241
|
console.log(`clisbot is already running with pid: ${result.pid}`);
|
|
73272
74242
|
const workspacePath = getPrimaryWorkspacePath(summary);
|
|
73273
74243
|
if (workspacePath) {
|
|
73274
|
-
console.log(`workspace: ${workspacePath}
|
|
74244
|
+
console.log(`workspace: ${workspacePath}`);
|
|
73275
74245
|
}
|
|
73276
74246
|
console.log(`config: ${result.configPath}`);
|
|
73277
74247
|
console.log(`log: ${result.logPath}`);
|
|
@@ -73303,7 +74273,7 @@ async function start(args = []) {
|
|
|
73303
74273
|
console.log(`clisbot started with pid: ${result.pid}`);
|
|
73304
74274
|
const workspacePath = getPrimaryWorkspacePath(summary);
|
|
73305
74275
|
if (workspacePath) {
|
|
73306
|
-
console.log(`workspace: ${workspacePath}
|
|
74276
|
+
console.log(`workspace: ${workspacePath}`);
|
|
73307
74277
|
}
|
|
73308
74278
|
console.log(`config: ${result.configPath}`);
|
|
73309
74279
|
console.log(`log: ${result.logPath}`);
|
|
@@ -73392,7 +74362,7 @@ async function status() {
|
|
|
73392
74362
|
});
|
|
73393
74363
|
const workspacePath = getPrimaryWorkspacePath(summary);
|
|
73394
74364
|
if (workspacePath) {
|
|
73395
|
-
console.log(`workspace: ${workspacePath}
|
|
74365
|
+
console.log(`workspace: ${workspacePath}`);
|
|
73396
74366
|
}
|
|
73397
74367
|
console.log(`config: ${runtimeStatus.configPath}`);
|
|
73398
74368
|
console.log(`pid file: ${runtimeStatus.pidPath}`);
|
|
@@ -73435,6 +74405,7 @@ async function logs(lines) {
|
|
|
73435
74405
|
} catch {}
|
|
73436
74406
|
}
|
|
73437
74407
|
async function main(command = parseCliArgs(process.argv)) {
|
|
74408
|
+
assertSupportedPlatform(command);
|
|
73438
74409
|
if (command.name === "help") {
|
|
73439
74410
|
console.log(renderCliHelp());
|
|
73440
74411
|
return;
|
|
@@ -73491,6 +74462,10 @@ async function main(command = parseCliArgs(process.argv)) {
|
|
|
73491
74462
|
await runAgentsCli(command.args);
|
|
73492
74463
|
return;
|
|
73493
74464
|
}
|
|
74465
|
+
if (command.name === "auth") {
|
|
74466
|
+
await runAuthCli(command.args);
|
|
74467
|
+
return;
|
|
74468
|
+
}
|
|
73494
74469
|
if (command.name === "pairing") {
|
|
73495
74470
|
await runPairingCli(command.args);
|
|
73496
74471
|
return;
|