clisbot 0.1.20 → 0.1.21
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 +5 -2
- package/dist/main.js +264 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,8 +64,11 @@ If you want to try first without persisting the token yet, just remove `--persis
|
|
|
64
64
|
Current auth note:
|
|
65
65
|
|
|
66
66
|
- DMs currently start in pairing mode by default.
|
|
67
|
-
-
|
|
68
|
-
- Today, if you want an owner or app admin, grant that principal explicitly with
|
|
67
|
+
- If no app owner is configured yet, the first DM user during the first `ownerClaimWindowMinutes` becomes app `owner` automatically and does not need pairing approval.
|
|
68
|
+
- Today, if you want an owner or app admin, grant that principal explicitly with the platform prefix plus the channel-native user id, for example `telegram:1276408333` or `slack:U123ABC456`.
|
|
69
|
+
- Example commands:
|
|
70
|
+
- `clisbot auth add-user app --role owner --user telegram:1276408333`
|
|
71
|
+
- `clisbot auth add-user app --role admin --user slack:U123ABC456`
|
|
69
72
|
- `clisbot auth --help` now covers role scopes, permission sets, and add/remove flows for users and permissions.
|
|
70
73
|
- App-level auth and owner-claim semantics in [Authorization And Roles](docs/user-guide/auth-and-roles.md) describe both the current runtime reality and the remaining target-model gaps.
|
|
71
74
|
|
package/dist/main.js
CHANGED
|
@@ -54487,6 +54487,9 @@ function renderPairingSetupHelpLines(prefix = "", options = {}) {
|
|
|
54487
54487
|
lines.push(`${prefix} - Approve the returned Slack code with: \`clisbot pairing approve slack <code>\``);
|
|
54488
54488
|
}
|
|
54489
54489
|
lines.push(`${prefix} - Configured app owner/admin principals bypass pairing in DMs.`);
|
|
54490
|
+
if (options.ownerConfigured === false) {
|
|
54491
|
+
lines.push(`${prefix} - If no owner is configured yet, the first DM user during the first ${options.ownerClaimWindowMinutes ?? 30} minutes becomes app owner automatically.`);
|
|
54492
|
+
}
|
|
54490
54493
|
return lines;
|
|
54491
54494
|
}
|
|
54492
54495
|
function renderTmuxDebugHelpLines(prefix = "") {
|
|
@@ -66523,6 +66526,7 @@ async function monitorTmuxRun(params) {
|
|
|
66523
66526
|
promptSubmitDelayMs: params.promptSubmitDelayMs,
|
|
66524
66527
|
timingContext: params.timingContext
|
|
66525
66528
|
});
|
|
66529
|
+
await params.onPromptSubmitted?.();
|
|
66526
66530
|
logLatencyDebug("tmux-submit-complete", params.timingContext, {
|
|
66527
66531
|
sessionName: params.sessionName,
|
|
66528
66532
|
promptSubmitDelayMs: params.promptSubmitDelayMs,
|
|
@@ -66697,7 +66701,8 @@ class ActiveRunManager {
|
|
|
66697
66701
|
observers: new Map,
|
|
66698
66702
|
observerFailures: new Map,
|
|
66699
66703
|
initialResult,
|
|
66700
|
-
latestUpdate: update
|
|
66704
|
+
latestUpdate: update,
|
|
66705
|
+
steeringReady: true
|
|
66701
66706
|
});
|
|
66702
66707
|
this.startRunMonitor(resolved.sessionKey, {
|
|
66703
66708
|
prompt: undefined,
|
|
@@ -66742,7 +66747,8 @@ class ActiveRunManager {
|
|
|
66742
66747
|
fullSnapshot: "",
|
|
66743
66748
|
initialSnapshot: "",
|
|
66744
66749
|
note: "Starting runner session..."
|
|
66745
|
-
})
|
|
66750
|
+
}),
|
|
66751
|
+
steeringReady: false
|
|
66746
66752
|
});
|
|
66747
66753
|
try {
|
|
66748
66754
|
const { resolved, initialSnapshot } = await this.runnerSessions.preparePromptSession(target, {
|
|
@@ -66836,6 +66842,9 @@ class ActiveRunManager {
|
|
|
66836
66842
|
hasActiveRun(target) {
|
|
66837
66843
|
return this.activeRuns.has(target.sessionKey);
|
|
66838
66844
|
}
|
|
66845
|
+
canSteerActiveRun(target) {
|
|
66846
|
+
return this.activeRuns.get(target.sessionKey)?.steeringReady ?? false;
|
|
66847
|
+
}
|
|
66839
66848
|
async stop() {
|
|
66840
66849
|
this.stopping = true;
|
|
66841
66850
|
const activeRuns = [...this.activeRuns.values()];
|
|
@@ -66940,6 +66949,9 @@ class ActiveRunManager {
|
|
|
66940
66949
|
}
|
|
66941
66950
|
(async () => {
|
|
66942
66951
|
try {
|
|
66952
|
+
if (!params.prompt) {
|
|
66953
|
+
run.steeringReady = true;
|
|
66954
|
+
}
|
|
66943
66955
|
await monitorTmuxRun({
|
|
66944
66956
|
tmux: this.tmux,
|
|
66945
66957
|
sessionName: run.resolved.sessionName,
|
|
@@ -66954,6 +66966,13 @@ class ActiveRunManager {
|
|
|
66954
66966
|
initialSnapshot: params.initialSnapshot,
|
|
66955
66967
|
detachedAlready: params.detachedAlready,
|
|
66956
66968
|
timingContext: params.timingContext,
|
|
66969
|
+
onPromptSubmitted: async () => {
|
|
66970
|
+
const currentRun = this.activeRuns.get(sessionKey);
|
|
66971
|
+
if (!currentRun) {
|
|
66972
|
+
return;
|
|
66973
|
+
}
|
|
66974
|
+
currentRun.steeringReady = true;
|
|
66975
|
+
},
|
|
66957
66976
|
onRunning: async (update) => {
|
|
66958
66977
|
const currentRun = this.activeRuns.get(sessionKey);
|
|
66959
66978
|
if (!currentRun) {
|
|
@@ -67136,6 +67155,9 @@ class AgentService {
|
|
|
67136
67155
|
hasActiveRun(target) {
|
|
67137
67156
|
return this.activeRuns.hasActiveRun(target);
|
|
67138
67157
|
}
|
|
67158
|
+
canSteerActiveRun(target) {
|
|
67159
|
+
return this.activeRuns.canSteerActiveRun(target);
|
|
67160
|
+
}
|
|
67139
67161
|
async submitSessionInput(target, text) {
|
|
67140
67162
|
return this.runnerSessions.submitSessionInput(target, text);
|
|
67141
67163
|
}
|
|
@@ -68249,6 +68271,28 @@ function renderTranscriptDisabledMessage() {
|
|
|
68249
68271
|
].join(`
|
|
68250
68272
|
`);
|
|
68251
68273
|
}
|
|
68274
|
+
function renderStartupSteeringUnavailableMessage() {
|
|
68275
|
+
return [
|
|
68276
|
+
"The active run is still starting and cannot accept steering input yet.",
|
|
68277
|
+
"Send a normal follow-up message to keep it ordered behind the first prompt, or wait until startup finishes before using `/steer`."
|
|
68278
|
+
].join(`
|
|
68279
|
+
`);
|
|
68280
|
+
}
|
|
68281
|
+
function renderPrincipalFormat(identity) {
|
|
68282
|
+
if (identity.platform === "slack") {
|
|
68283
|
+
return "slack:<nativeUserId>";
|
|
68284
|
+
}
|
|
68285
|
+
return "telegram:<nativeUserId>";
|
|
68286
|
+
}
|
|
68287
|
+
function renderPrincipalExample(identity) {
|
|
68288
|
+
if (identity.senderId) {
|
|
68289
|
+
return `${identity.platform}:${identity.senderId}`;
|
|
68290
|
+
}
|
|
68291
|
+
if (identity.platform === "slack") {
|
|
68292
|
+
return "slack:U123ABC456";
|
|
68293
|
+
}
|
|
68294
|
+
return "telegram:1276408333";
|
|
68295
|
+
}
|
|
68252
68296
|
function renderWhoAmIMessage(params) {
|
|
68253
68297
|
const lines = [
|
|
68254
68298
|
"Who am I",
|
|
@@ -68273,7 +68317,7 @@ function renderWhoAmIMessage(params) {
|
|
|
68273
68317
|
if (params.identity.topicId) {
|
|
68274
68318
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
68275
68319
|
}
|
|
68276
|
-
lines.push(`principal: \`${params.auth.principal ?? "(none)"}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayBypassPairing: \`${params.auth.mayBypassPairing}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `verbose: \`${params.route.verbose}\``);
|
|
68320
|
+
lines.push(`principal: \`${params.auth.principal ?? "(none)"}\``, `principalFormat: \`${renderPrincipalFormat(params.identity)}\``, `principalExample: \`${renderPrincipalExample(params.identity)}\``, `appRole: \`${params.auth.appRole}\``, `agentRole: \`${params.auth.agentRole}\``, `mayBypassPairing: \`${params.auth.mayBypassPairing}\``, `mayManageProtectedResources: \`${params.auth.mayManageProtectedResources}\``, `canUseShell: \`${params.auth.canUseShell}\``, `verbose: \`${params.route.verbose}\``);
|
|
68277
68321
|
return lines.join(`
|
|
68278
68322
|
`);
|
|
68279
68323
|
}
|
|
@@ -68301,7 +68345,7 @@ function renderRouteStatusMessage(params) {
|
|
|
68301
68345
|
if (params.identity.topicId) {
|
|
68302
68346
|
lines.push(`topicId: \`${params.identity.topicId}\``);
|
|
68303
68347
|
}
|
|
68304
|
-
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}\``, `
|
|
68348
|
+
lines.push(`principal: \`${params.auth.principal ?? "(none)"}\``, `principalFormat: \`${renderPrincipalFormat(params.identity)}\``, `principalExample: \`${renderPrincipalExample(params.identity)}\``, `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}\``, `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}\``);
|
|
68305
68349
|
if (params.runtimeState.startedAt) {
|
|
68306
68350
|
lines.push(`run.startedAt: \`${new Date(params.runtimeState.startedAt).toISOString()}\``);
|
|
68307
68351
|
}
|
|
@@ -68963,6 +69007,7 @@ async function processChannelInteraction(params) {
|
|
|
68963
69007
|
const explicitQueueMessage = slashCommand?.type === "queue" ? slashCommand.text.trim() : undefined;
|
|
68964
69008
|
const explicitSteerMessage = slashCommand?.type === "steer" ? slashCommand.text.trim() : undefined;
|
|
68965
69009
|
const sessionBusy = await (params.agentService.isAwaitingFollowUpRouting?.(params.sessionTarget) ?? params.agentService.isSessionBusy?.(params.sessionTarget) ?? false);
|
|
69010
|
+
const canSteerActiveRun = params.agentService.canSteerActiveRun?.(params.sessionTarget) ?? !sessionBusy;
|
|
68966
69011
|
const queueByMode = !explicitQueueMessage && params.route.additionalMessageMode === "queue" && sessionBusy;
|
|
68967
69012
|
const forceQueuedDelivery = typeof explicitQueueMessage === "string" || queueByMode;
|
|
68968
69013
|
const delayedPromptText = explicitQueueMessage ? params.agentPromptBuilder ? params.agentPromptBuilder(explicitQueueMessage) : explicitQueueMessage : params.agentPromptText ?? params.text;
|
|
@@ -69384,6 +69429,11 @@ ${escapeCodeFence(shellResult.output)}
|
|
|
69384
69429
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
69385
69430
|
return interactionResult;
|
|
69386
69431
|
}
|
|
69432
|
+
if (!canSteerActiveRun) {
|
|
69433
|
+
await params.postText(renderStartupSteeringUnavailableMessage());
|
|
69434
|
+
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
69435
|
+
return interactionResult;
|
|
69436
|
+
}
|
|
69387
69437
|
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(explicitSteerMessage, params.protectedControlMutationRule));
|
|
69388
69438
|
await params.postText("Steered.");
|
|
69389
69439
|
await params.agentService.recordConversationReply(params.sessionTarget);
|
|
@@ -69392,7 +69442,7 @@ ${escapeCodeFence(shellResult.output)}
|
|
|
69392
69442
|
};
|
|
69393
69443
|
}
|
|
69394
69444
|
if (!forceQueuedDelivery && params.route.additionalMessageMode === "steer") {
|
|
69395
|
-
if (sessionBusy) {
|
|
69445
|
+
if (sessionBusy && canSteerActiveRun) {
|
|
69396
69446
|
await params.agentService.submitSessionInput(params.sessionTarget, buildSteeringMessage(params.text, params.protectedControlMutationRule));
|
|
69397
69447
|
return {
|
|
69398
69448
|
processingIndicatorLifecycle: "active-run"
|
|
@@ -69485,7 +69535,7 @@ function mergeRoleRecord2(defaults, overrides) {
|
|
|
69485
69535
|
}
|
|
69486
69536
|
return merged;
|
|
69487
69537
|
}
|
|
69488
|
-
function
|
|
69538
|
+
function normalizeAuthPrincipal(principal) {
|
|
69489
69539
|
const trimmed = principal.trim();
|
|
69490
69540
|
if (!trimmed) {
|
|
69491
69541
|
return "";
|
|
@@ -69503,17 +69553,17 @@ function normalizePrincipal(principal) {
|
|
|
69503
69553
|
return `${platform}:${userId.trim()}`;
|
|
69504
69554
|
}
|
|
69505
69555
|
function normalizeRoleUsers(users) {
|
|
69506
|
-
return (users ?? []).map(
|
|
69556
|
+
return (users ?? []).map(normalizeAuthPrincipal).filter(Boolean);
|
|
69507
69557
|
}
|
|
69508
|
-
function
|
|
69558
|
+
function resolveAuthPrincipal(identity) {
|
|
69509
69559
|
const senderId = identity.senderId?.trim();
|
|
69510
69560
|
if (!senderId) {
|
|
69511
69561
|
return;
|
|
69512
69562
|
}
|
|
69513
69563
|
if (identity.platform === "slack") {
|
|
69514
|
-
return
|
|
69564
|
+
return normalizeAuthPrincipal(`slack:${senderId}`);
|
|
69515
69565
|
}
|
|
69516
|
-
return
|
|
69566
|
+
return normalizeAuthPrincipal(`telegram:${senderId}`);
|
|
69517
69567
|
}
|
|
69518
69568
|
function findExplicitRole(roles, principal) {
|
|
69519
69569
|
if (!principal || !roles) {
|
|
@@ -69545,7 +69595,7 @@ function hasAppPermission(config, appRole, permission) {
|
|
|
69545
69595
|
return getAllowedPermissions(config.app.auth.roles, appRole).has(permission);
|
|
69546
69596
|
}
|
|
69547
69597
|
function resolveChannelAuth(params) {
|
|
69548
|
-
const principal =
|
|
69598
|
+
const principal = resolveAuthPrincipal(params.identity);
|
|
69549
69599
|
const appAuth = params.config.app.auth;
|
|
69550
69600
|
const explicitAppRole = findExplicitRole(appAuth.roles, principal);
|
|
69551
69601
|
const appRole = explicitAppRole ?? appAuth.defaultRole;
|
|
@@ -69565,6 +69615,128 @@ function resolveChannelAuth(params) {
|
|
|
69565
69615
|
};
|
|
69566
69616
|
}
|
|
69567
69617
|
|
|
69618
|
+
// src/auth/owner-claim.ts
|
|
69619
|
+
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
69620
|
+
var OWNER_CLAIM_RUNTIME_STARTED_AT_MS = Date.now();
|
|
69621
|
+
var CONFIG_LOCK_OPTIONS = {
|
|
69622
|
+
retries: {
|
|
69623
|
+
retries: 10,
|
|
69624
|
+
factor: 2,
|
|
69625
|
+
minTimeout: 50,
|
|
69626
|
+
maxTimeout: 2000,
|
|
69627
|
+
randomize: true
|
|
69628
|
+
},
|
|
69629
|
+
stale: 30000
|
|
69630
|
+
};
|
|
69631
|
+
var ownerClaimRuntimeState = {
|
|
69632
|
+
initialized: false,
|
|
69633
|
+
armed: false,
|
|
69634
|
+
closed: false,
|
|
69635
|
+
openedAtMs: OWNER_CLAIM_RUNTIME_STARTED_AT_MS,
|
|
69636
|
+
windowMs: 0
|
|
69637
|
+
};
|
|
69638
|
+
function getOwnerUsers(config) {
|
|
69639
|
+
return config.app.auth.roles.owner?.users ?? [];
|
|
69640
|
+
}
|
|
69641
|
+
function hasConfiguredOwner(config) {
|
|
69642
|
+
return getOwnerUsers(config).some((entry) => entry.trim().length > 0);
|
|
69643
|
+
}
|
|
69644
|
+
function syncRuntimeStateWithConfig(config) {
|
|
69645
|
+
if (hasConfiguredOwner(config)) {
|
|
69646
|
+
ownerClaimRuntimeState.closed = true;
|
|
69647
|
+
}
|
|
69648
|
+
}
|
|
69649
|
+
function primeOwnerClaimRuntime(config, nowMs = OWNER_CLAIM_RUNTIME_STARTED_AT_MS) {
|
|
69650
|
+
if (!ownerClaimRuntimeState.initialized) {
|
|
69651
|
+
ownerClaimRuntimeState.initialized = true;
|
|
69652
|
+
ownerClaimRuntimeState.armed = !hasConfiguredOwner(config);
|
|
69653
|
+
ownerClaimRuntimeState.closed = !ownerClaimRuntimeState.armed;
|
|
69654
|
+
ownerClaimRuntimeState.openedAtMs = nowMs;
|
|
69655
|
+
ownerClaimRuntimeState.windowMs = Math.max(0, config.app.auth.ownerClaimWindowMinutes * 60000);
|
|
69656
|
+
}
|
|
69657
|
+
syncRuntimeStateWithConfig(config);
|
|
69658
|
+
return { ...ownerClaimRuntimeState };
|
|
69659
|
+
}
|
|
69660
|
+
function isOwnerClaimOpen(config, nowMs = Date.now()) {
|
|
69661
|
+
primeOwnerClaimRuntime(config);
|
|
69662
|
+
syncRuntimeStateWithConfig(config);
|
|
69663
|
+
if (ownerClaimRuntimeState.closed || !ownerClaimRuntimeState.armed) {
|
|
69664
|
+
return false;
|
|
69665
|
+
}
|
|
69666
|
+
return nowMs - ownerClaimRuntimeState.openedAtMs <= ownerClaimRuntimeState.windowMs;
|
|
69667
|
+
}
|
|
69668
|
+
function syncOwnerUsers(target, source) {
|
|
69669
|
+
target.app.auth.roles.owner = {
|
|
69670
|
+
...target.app.auth.roles.owner,
|
|
69671
|
+
allow: [...source.app.auth.roles.owner?.allow ?? target.app.auth.roles.owner?.allow ?? []],
|
|
69672
|
+
users: [...source.app.auth.roles.owner?.users ?? []]
|
|
69673
|
+
};
|
|
69674
|
+
}
|
|
69675
|
+
async function withConfigLock(configPath, fn) {
|
|
69676
|
+
const expandedPath = await ensureEditableConfigFile(configPath);
|
|
69677
|
+
let release;
|
|
69678
|
+
try {
|
|
69679
|
+
release = await import_proper_lockfile2.default.lock(expandedPath, CONFIG_LOCK_OPTIONS);
|
|
69680
|
+
return await fn(expandedPath);
|
|
69681
|
+
} finally {
|
|
69682
|
+
if (release) {
|
|
69683
|
+
try {
|
|
69684
|
+
await release();
|
|
69685
|
+
} catch {}
|
|
69686
|
+
}
|
|
69687
|
+
}
|
|
69688
|
+
}
|
|
69689
|
+
async function claimFirstOwnerFromDirectMessage(params) {
|
|
69690
|
+
const nowMs = params.nowMs ?? Date.now();
|
|
69691
|
+
primeOwnerClaimRuntime(params.config);
|
|
69692
|
+
if (params.identity.conversationKind !== "dm") {
|
|
69693
|
+
return { claimed: false, principal: undefined };
|
|
69694
|
+
}
|
|
69695
|
+
const principal = resolveAuthPrincipal(params.identity);
|
|
69696
|
+
if (!principal || !isOwnerClaimOpen(params.config, nowMs)) {
|
|
69697
|
+
return { claimed: false, principal };
|
|
69698
|
+
}
|
|
69699
|
+
const result = await withConfigLock(params.configPath, async (expandedPath) => {
|
|
69700
|
+
const { config: freshConfig } = await readEditableConfig(expandedPath);
|
|
69701
|
+
primeOwnerClaimRuntime(freshConfig);
|
|
69702
|
+
syncRuntimeStateWithConfig(freshConfig);
|
|
69703
|
+
if (!isOwnerClaimOpen(freshConfig, nowMs)) {
|
|
69704
|
+
syncOwnerUsers(params.config, freshConfig);
|
|
69705
|
+
return { claimed: false, principal, configPath: expandedPath };
|
|
69706
|
+
}
|
|
69707
|
+
const currentOwners = getOwnerUsers(freshConfig).map((entry) => entry.trim()).filter(Boolean);
|
|
69708
|
+
if (currentOwners.includes(principal)) {
|
|
69709
|
+
syncOwnerUsers(params.config, freshConfig);
|
|
69710
|
+
ownerClaimRuntimeState.closed = true;
|
|
69711
|
+
return { claimed: false, principal, configPath: expandedPath };
|
|
69712
|
+
}
|
|
69713
|
+
freshConfig.app.auth.roles.owner.users = [...currentOwners, principal];
|
|
69714
|
+
await writeEditableConfig(expandedPath, freshConfig);
|
|
69715
|
+
syncOwnerUsers(params.config, freshConfig);
|
|
69716
|
+
ownerClaimRuntimeState.closed = true;
|
|
69717
|
+
console.log(`clisbot auto-claimed first owner ${principal}`);
|
|
69718
|
+
return { claimed: true, principal, configPath: expandedPath };
|
|
69719
|
+
});
|
|
69720
|
+
return result;
|
|
69721
|
+
}
|
|
69722
|
+
function renderFirstOwnerClaimMessage(params) {
|
|
69723
|
+
return [
|
|
69724
|
+
"First owner claim complete.",
|
|
69725
|
+
"",
|
|
69726
|
+
`principal: \`${params.principal}\``,
|
|
69727
|
+
"role: `owner`",
|
|
69728
|
+
`reason: no owner was configured, and this was the first direct message received during the first ${params.ownerClaimWindowMinutes} minutes after runtime start`,
|
|
69729
|
+
"pairing: not required for you anymore because app owners bypass DM pairing",
|
|
69730
|
+
"",
|
|
69731
|
+
"You can now:",
|
|
69732
|
+
"- chat without pairing approval",
|
|
69733
|
+
"- use full app-level control",
|
|
69734
|
+
"- manage auth, channels, and agent settings",
|
|
69735
|
+
"- use admin-level actions across all agents and routed surfaces"
|
|
69736
|
+
].join(`
|
|
69737
|
+
`);
|
|
69738
|
+
}
|
|
69739
|
+
|
|
69568
69740
|
// src/channels/slack/session-routing.ts
|
|
69569
69741
|
function resolveSlackConversationTarget(params) {
|
|
69570
69742
|
const sessionConfig = params.loadedConfig.raw.session;
|
|
@@ -70773,21 +70945,48 @@ class SlackSocketService {
|
|
|
70773
70945
|
if (params.conversationKind === "dm") {
|
|
70774
70946
|
const directUserId = typeof event.user === "string" ? event.user.trim() : "";
|
|
70775
70947
|
const dmConfig = this.loadedConfig.raw.channels.slack.directMessages;
|
|
70776
|
-
const
|
|
70777
|
-
|
|
70778
|
-
|
|
70779
|
-
|
|
70780
|
-
|
|
70781
|
-
|
|
70782
|
-
senderId: directUserId || undefined,
|
|
70783
|
-
channelId
|
|
70784
|
-
}
|
|
70785
|
-
});
|
|
70948
|
+
const dmIdentity = {
|
|
70949
|
+
platform: "slack",
|
|
70950
|
+
conversationKind: params.conversationKind,
|
|
70951
|
+
senderId: directUserId || undefined,
|
|
70952
|
+
channelId
|
|
70953
|
+
};
|
|
70786
70954
|
if (!directUserId || dmConfig.policy === "disabled") {
|
|
70787
70955
|
debugSlackEvent("drop-dm-disabled", { eventId, directUserId });
|
|
70788
70956
|
await this.processedEventsStore.markCompleted(eventId);
|
|
70789
70957
|
return;
|
|
70790
70958
|
}
|
|
70959
|
+
let ownerClaimed = false;
|
|
70960
|
+
let ownerPrincipal;
|
|
70961
|
+
try {
|
|
70962
|
+
const claimResult = await claimFirstOwnerFromDirectMessage({
|
|
70963
|
+
config: this.loadedConfig.raw,
|
|
70964
|
+
configPath: this.loadedConfig.configPath,
|
|
70965
|
+
identity: dmIdentity
|
|
70966
|
+
});
|
|
70967
|
+
ownerClaimed = claimResult.claimed;
|
|
70968
|
+
ownerPrincipal = claimResult.principal;
|
|
70969
|
+
} catch (error) {
|
|
70970
|
+
console.error("slack first-owner claim failed", error);
|
|
70971
|
+
}
|
|
70972
|
+
if (ownerClaimed && ownerPrincipal) {
|
|
70973
|
+
try {
|
|
70974
|
+
await postSlackText(this.app.client, {
|
|
70975
|
+
channel: channelId,
|
|
70976
|
+
text: renderFirstOwnerClaimMessage({
|
|
70977
|
+
principal: ownerPrincipal,
|
|
70978
|
+
ownerClaimWindowMinutes: this.loadedConfig.raw.app.auth.ownerClaimWindowMinutes
|
|
70979
|
+
})
|
|
70980
|
+
});
|
|
70981
|
+
} catch (error) {
|
|
70982
|
+
console.error("slack first-owner claim reply failed", error);
|
|
70983
|
+
}
|
|
70984
|
+
}
|
|
70985
|
+
const auth2 = resolveChannelAuth({
|
|
70986
|
+
config: this.loadedConfig.raw,
|
|
70987
|
+
agentId: params.route.agentId,
|
|
70988
|
+
identity: dmIdentity
|
|
70989
|
+
});
|
|
70791
70990
|
if (dmConfig.policy !== "open" && !auth2.mayBypassPairing) {
|
|
70792
70991
|
const storedAllowFrom = await readChannelAllowFromStore("slack");
|
|
70793
70992
|
const allowed = isSlackSenderAllowed({
|
|
@@ -72298,20 +72497,47 @@ class TelegramPollingService {
|
|
|
72298
72497
|
const directMessages = this.loadedConfig.raw.channels.telegram.directMessages;
|
|
72299
72498
|
const senderId = message.from?.id != null ? String(message.from.id) : "";
|
|
72300
72499
|
const senderUsername = message.from?.username;
|
|
72301
|
-
const
|
|
72302
|
-
|
|
72303
|
-
|
|
72304
|
-
|
|
72305
|
-
|
|
72306
|
-
|
|
72307
|
-
senderId: senderId || undefined,
|
|
72308
|
-
chatId: String(message.chat.id)
|
|
72309
|
-
}
|
|
72310
|
-
});
|
|
72500
|
+
const dmIdentity = {
|
|
72501
|
+
platform: "telegram",
|
|
72502
|
+
conversationKind: routeInfo.conversationKind,
|
|
72503
|
+
senderId: senderId || undefined,
|
|
72504
|
+
chatId: String(message.chat.id)
|
|
72505
|
+
};
|
|
72311
72506
|
if (!senderId || directMessages.policy === "disabled") {
|
|
72312
72507
|
await this.processedEventsStore.markCompleted(eventId);
|
|
72313
72508
|
return;
|
|
72314
72509
|
}
|
|
72510
|
+
let ownerClaimed = false;
|
|
72511
|
+
let ownerPrincipal;
|
|
72512
|
+
try {
|
|
72513
|
+
const claimResult = await claimFirstOwnerFromDirectMessage({
|
|
72514
|
+
config: this.loadedConfig.raw,
|
|
72515
|
+
configPath: this.loadedConfig.configPath,
|
|
72516
|
+
identity: dmIdentity
|
|
72517
|
+
});
|
|
72518
|
+
ownerClaimed = claimResult.claimed;
|
|
72519
|
+
ownerPrincipal = claimResult.principal;
|
|
72520
|
+
} catch (error) {
|
|
72521
|
+
console.error("telegram first-owner claim failed", error);
|
|
72522
|
+
}
|
|
72523
|
+
if (ownerClaimed && ownerPrincipal) {
|
|
72524
|
+
try {
|
|
72525
|
+
await callTelegramApi(this.accountConfig.botToken, "sendMessage", {
|
|
72526
|
+
chat_id: message.chat.id,
|
|
72527
|
+
text: renderFirstOwnerClaimMessage({
|
|
72528
|
+
principal: ownerPrincipal,
|
|
72529
|
+
ownerClaimWindowMinutes: this.loadedConfig.raw.app.auth.ownerClaimWindowMinutes
|
|
72530
|
+
})
|
|
72531
|
+
});
|
|
72532
|
+
} catch (error) {
|
|
72533
|
+
console.error("telegram first-owner claim reply failed", error);
|
|
72534
|
+
}
|
|
72535
|
+
}
|
|
72536
|
+
const auth = resolveChannelAuth({
|
|
72537
|
+
config: this.loadedConfig.raw,
|
|
72538
|
+
agentId: routeInfo.route.agentId,
|
|
72539
|
+
identity: dmIdentity
|
|
72540
|
+
});
|
|
72315
72541
|
if (directMessages.policy !== "open" && !auth.mayBypassPairing) {
|
|
72316
72542
|
const storedAllowFrom = await readChannelAllowFromStore("telegram");
|
|
72317
72543
|
const allowed = isTelegramSenderAllowed({
|
|
@@ -73335,6 +73561,7 @@ class RuntimeSupervisor {
|
|
|
73335
73561
|
}
|
|
73336
73562
|
async createRuntime(loadedConfig) {
|
|
73337
73563
|
const runtimeId = this.nextRuntimeId++;
|
|
73564
|
+
primeOwnerClaimRuntime(loadedConfig.raw);
|
|
73338
73565
|
const agentService = this.dependencies.createAgentService(loadedConfig);
|
|
73339
73566
|
const processedEventsStore = this.dependencies.createProcessedEventsStore(loadedConfig.processedEventsPath);
|
|
73340
73567
|
const activityStore = this.dependencies.createActivityStore();
|
|
@@ -73795,7 +74022,8 @@ function appendAuthOnboardingLines(lines, summary, prefix = "") {
|
|
|
73795
74022
|
lines.push(`${prefix}Auth onboarding:`);
|
|
73796
74023
|
if (!hasConfiguredPrivilegedPrincipal(summary)) {
|
|
73797
74024
|
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`);
|
|
73798
|
-
lines.push(`${prefix} -
|
|
74025
|
+
lines.push(`${prefix} - if no owner exists yet, the first DM user during the first ${summary.ownerSummary.ownerClaimWindowMinutes} minutes becomes app owner automatically`);
|
|
74026
|
+
lines.push(`${prefix} - after the first owner exists, add more principals with: \`clisbot auth add-user app --role <owner|admin> --user <principal>\``);
|
|
73799
74027
|
} else {
|
|
73800
74028
|
lines.push(`${prefix} - inspect current app roles with: \`clisbot auth show app\``);
|
|
73801
74029
|
}
|
|
@@ -73916,6 +74144,8 @@ function renderStartSummary(summary) {
|
|
|
73916
74144
|
lines.push(...renderPairingSetupHelpLines(" ", {
|
|
73917
74145
|
slackEnabled: summary.channelSummaries.some((channel) => channel.channel === "slack" && channel.enabled),
|
|
73918
74146
|
telegramEnabled: summary.channelSummaries.some((channel) => channel.channel === "telegram" && channel.enabled),
|
|
74147
|
+
ownerConfigured: hasConfiguredPrivilegedPrincipal(summary),
|
|
74148
|
+
ownerClaimWindowMinutes: summary.ownerSummary.ownerClaimWindowMinutes,
|
|
73919
74149
|
slackDirectMessagesPolicy: summary.channelSummaries.find((channel) => channel.channel === "slack")?.directMessagesPolicy,
|
|
73920
74150
|
telegramDirectMessagesPolicy: summary.channelSummaries.find((channel) => channel.channel === "telegram")?.directMessagesPolicy,
|
|
73921
74151
|
conditionalOnly: true
|
|
@@ -73938,6 +74168,8 @@ function renderStartSummary(summary) {
|
|
|
73938
74168
|
lines.push(...renderPairingSetupHelpLines("", {
|
|
73939
74169
|
slackEnabled: summary.channelSummaries.some((channel) => channel.channel === "slack" && channel.enabled),
|
|
73940
74170
|
telegramEnabled: summary.channelSummaries.some((channel) => channel.channel === "telegram" && channel.enabled),
|
|
74171
|
+
ownerConfigured: hasConfiguredPrivilegedPrincipal(summary),
|
|
74172
|
+
ownerClaimWindowMinutes: summary.ownerSummary.ownerClaimWindowMinutes,
|
|
73941
74173
|
slackDirectMessagesPolicy: summary.channelSummaries.find((channel) => channel.channel === "slack")?.directMessagesPolicy,
|
|
73942
74174
|
telegramDirectMessagesPolicy: summary.channelSummaries.find((channel) => channel.channel === "telegram")?.directMessagesPolicy,
|
|
73943
74175
|
conditionalOnly: true
|