@integrity-labs/agt-cli 0.15.1 → 0.15.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agt.js +3 -3
- package/dist/{chunk-7QE6SJQB.js → chunk-IQI7XAQ6.js} +1 -11
- package/dist/chunk-IQI7XAQ6.js.map +1 -0
- package/dist/lib/manager-worker.js +2 -57
- package/dist/lib/manager-worker.js.map +1 -1
- package/mcp/index.js +55 -14
- package/mcp/slack-channel.js +16 -15
- package/package.json +1 -1
- package/dist/chunk-7QE6SJQB.js.map +0 -1
package/mcp/index.js
CHANGED
|
@@ -20985,6 +20985,36 @@ var StdioServerTransport = class {
|
|
|
20985
20985
|
}
|
|
20986
20986
|
};
|
|
20987
20987
|
|
|
20988
|
+
// src/token-refresh-selection.ts
|
|
20989
|
+
var REFRESH_WINDOW_MS = 10 * 6e4;
|
|
20990
|
+
function selectIntegrationsToRefresh(input) {
|
|
20991
|
+
const now = input.now ?? Date.now();
|
|
20992
|
+
const windowMs = input.windowMs ?? REFRESH_WINDOW_MS;
|
|
20993
|
+
const oauth = input.integrations.filter((i) => i.auth_type === "oauth2");
|
|
20994
|
+
if (input.integration_id) {
|
|
20995
|
+
const target = oauth.find((i) => i.id === input.integration_id);
|
|
20996
|
+
if (!target) return [];
|
|
20997
|
+
return [{ kind: "force_refresh", integration: target }];
|
|
20998
|
+
}
|
|
20999
|
+
return oauth.map((integration) => {
|
|
21000
|
+
if (!integration.credentials.access_token) {
|
|
21001
|
+
return { kind: "force_refresh", integration };
|
|
21002
|
+
}
|
|
21003
|
+
const expiresAtRaw = integration.credentials.token_expires_at;
|
|
21004
|
+
if (!expiresAtRaw) {
|
|
21005
|
+
return { kind: "force_refresh", integration };
|
|
21006
|
+
}
|
|
21007
|
+
const expiresAt = Date.parse(expiresAtRaw);
|
|
21008
|
+
if (Number.isNaN(expiresAt)) {
|
|
21009
|
+
return { kind: "force_refresh", integration };
|
|
21010
|
+
}
|
|
21011
|
+
if (expiresAt - now < windowMs) {
|
|
21012
|
+
return { kind: "force_refresh", integration };
|
|
21013
|
+
}
|
|
21014
|
+
return { kind: "return_cached", integration };
|
|
21015
|
+
});
|
|
21016
|
+
}
|
|
21017
|
+
|
|
20988
21018
|
// src/index.ts
|
|
20989
21019
|
import { spawn } from "child_process";
|
|
20990
21020
|
var DeliveryTargetSchema = external_exports.union([
|
|
@@ -21371,40 +21401,48 @@ server.tool(
|
|
|
21371
21401
|
);
|
|
21372
21402
|
server.tool(
|
|
21373
21403
|
"token.refresh",
|
|
21374
|
-
"
|
|
21404
|
+
"Get a working OAuth access token for an integration. Returns the current cached token when it is still valid (the server-side cron rotates tokens every ~15 minutes) and forces a refresh only when the cached token is expiring or missing. Pass `integration_id` to force-refresh a specific integration after a 401/TokenExpired from the provider.",
|
|
21375
21405
|
{
|
|
21376
|
-
integration_id: external_exports.string().optional().describe("Specific integration UUID to refresh. If omitted,
|
|
21406
|
+
integration_id: external_exports.string().optional().describe("Specific integration UUID to force-refresh (use after a 401 from the provider). If omitted, returns current tokens for all OAuth integrations, refreshing any that are within ~10 minutes of expiry.")
|
|
21377
21407
|
},
|
|
21378
21408
|
async (params) => {
|
|
21379
21409
|
const integrations = await apiPost("/host/agent-integrations", {
|
|
21380
21410
|
agent_id: AGT_AGENT_ID
|
|
21381
21411
|
});
|
|
21382
|
-
const
|
|
21383
|
-
|
|
21384
|
-
|
|
21385
|
-
if (params.integration_id) return i.id === params.integration_id;
|
|
21386
|
-
if (!i.credentials.token_expires_at) return true;
|
|
21387
|
-
return new Date(i.credentials.token_expires_at).getTime() - Date.now() < TEN_MINUTES_MS;
|
|
21412
|
+
const decisions = selectIntegrationsToRefresh({
|
|
21413
|
+
integrations: integrations.integrations,
|
|
21414
|
+
integration_id: params.integration_id
|
|
21388
21415
|
});
|
|
21389
|
-
if (
|
|
21416
|
+
if (decisions.length === 0) {
|
|
21390
21417
|
return {
|
|
21391
21418
|
content: [
|
|
21392
21419
|
{
|
|
21393
21420
|
type: "text",
|
|
21394
|
-
text: params.integration_id ? `No OAuth integration found with ID ${params.integration_id}.` : "No
|
|
21421
|
+
text: params.integration_id ? `No OAuth integration found with ID ${params.integration_id}.` : "No OAuth integrations found for this agent."
|
|
21395
21422
|
}
|
|
21396
21423
|
]
|
|
21397
21424
|
};
|
|
21398
21425
|
}
|
|
21399
21426
|
const results = [];
|
|
21400
|
-
for (const
|
|
21427
|
+
for (const decision of decisions) {
|
|
21428
|
+
const { integration } = decision;
|
|
21429
|
+
if (decision.kind === "return_cached") {
|
|
21430
|
+
results.push({
|
|
21431
|
+
definition_id: integration.definition_id,
|
|
21432
|
+
access_token: integration.credentials.access_token,
|
|
21433
|
+
expires_at: integration.credentials.token_expires_at,
|
|
21434
|
+
source: "cached"
|
|
21435
|
+
});
|
|
21436
|
+
continue;
|
|
21437
|
+
}
|
|
21401
21438
|
try {
|
|
21402
21439
|
const refreshed = await apiPost(`/integrations/oauth/${integration.id}/refresh`, {});
|
|
21403
21440
|
if (refreshed.ok && refreshed.access_token) {
|
|
21404
21441
|
results.push({
|
|
21405
21442
|
definition_id: integration.definition_id,
|
|
21406
21443
|
access_token: refreshed.access_token,
|
|
21407
|
-
expires_at: refreshed.expires_at
|
|
21444
|
+
expires_at: refreshed.expires_at,
|
|
21445
|
+
source: "refreshed"
|
|
21408
21446
|
});
|
|
21409
21447
|
} else {
|
|
21410
21448
|
results.push({ definition_id: integration.definition_id, error: true });
|
|
@@ -21414,17 +21452,20 @@ server.tool(
|
|
|
21414
21452
|
}
|
|
21415
21453
|
}
|
|
21416
21454
|
const summary = results.map(
|
|
21417
|
-
(r) => r.access_token ? `${r.definition_id}:
|
|
21455
|
+
(r) => r.access_token ? `${r.definition_id}: ${r.source} (expires ${r.expires_at ?? "unknown"})` : `${r.definition_id}: refresh failed`
|
|
21418
21456
|
);
|
|
21419
21457
|
return {
|
|
21420
21458
|
content: [
|
|
21421
21459
|
{
|
|
21422
21460
|
type: "text",
|
|
21423
21461
|
text: JSON.stringify({
|
|
21462
|
+
// Key is `refreshed` for backwards compatibility — entries can now
|
|
21463
|
+
// be either cached or freshly-rotated, distinguished by `source`.
|
|
21424
21464
|
refreshed: results.filter((r) => r.access_token).map((r) => ({
|
|
21425
21465
|
definition_id: r.definition_id,
|
|
21426
21466
|
access_token: r.access_token,
|
|
21427
|
-
expires_at: r.expires_at
|
|
21467
|
+
expires_at: r.expires_at,
|
|
21468
|
+
source: r.source
|
|
21428
21469
|
})),
|
|
21429
21470
|
failed: results.filter((r) => r.error).map((r) => r.definition_id),
|
|
21430
21471
|
summary: summary.join("; ")
|
package/mcp/slack-channel.js
CHANGED
|
@@ -14229,6 +14229,21 @@ if (!BOT_TOKEN || !APP_TOKEN) {
|
|
|
14229
14229
|
);
|
|
14230
14230
|
process.exit(1);
|
|
14231
14231
|
}
|
|
14232
|
+
async function getBotUserId() {
|
|
14233
|
+
try {
|
|
14234
|
+
const res = await fetch("https://slack.com/api/auth.test", {
|
|
14235
|
+
headers: { Authorization: `Bearer ${BOT_TOKEN}` }
|
|
14236
|
+
});
|
|
14237
|
+
const data = await res.json();
|
|
14238
|
+
return data.ok ? data.user_id ?? null : null;
|
|
14239
|
+
} catch {
|
|
14240
|
+
return null;
|
|
14241
|
+
}
|
|
14242
|
+
}
|
|
14243
|
+
var botUserId = await getBotUserId();
|
|
14244
|
+
process.stderr.write(`slack-channel: Bot user ID: ${botUserId}
|
|
14245
|
+
`);
|
|
14246
|
+
var selfIdentityInstruction = botUserId ? `Your own Slack user_id is ${botUserId}. Treat <@${botUserId}> mentions as directed at you, even inside auto_followed threads.` : "";
|
|
14232
14247
|
var mcp = new Server(
|
|
14233
14248
|
{ name: "slack", version: "0.1.0" },
|
|
14234
14249
|
{
|
|
@@ -14241,6 +14256,7 @@ var mcp = new Server(
|
|
|
14241
14256
|
// and the agent silently loses attachment-handling guidance.
|
|
14242
14257
|
instructions: [
|
|
14243
14258
|
'Messages from Slack arrive as <channel source="slack" user="<slack-id>" user_name="<display-name>" channel="..." thread_ts="...">.',
|
|
14259
|
+
...selfIdentityInstruction ? [selfIdentityInstruction] : [],
|
|
14244
14260
|
"Inbound attachments: the <channel> tag's `files` attribute is a JSON-serialised array \u2014 JSON.parse it before iterating. If a file entry has a `path`, the image is ALREADY DOWNLOADED locally \u2014 Read that path directly, do NOT call slack.download_attachment. That tool is only for entries with `file_id` but NO `path` (PDF, docx, csv, etc.): pass file_id + channel verbatim (never paraphrase), then Read the returned path. Single-image messages also get a top-level `image_path` convenience attribute; multi-image messages omit it. Never tell the user about internal file-handling failures that don't affect the answer.",
|
|
14245
14261
|
"Reply via slack.reply passing channel and thread_ts from the tag. Always include thread_ts on threaded replies so the response lands in the same thread.",
|
|
14246
14262
|
"Address users by user_name, never by raw user ID. In multi-participant threads the CURRENT speaker is the one on the latest <channel> tag \u2014 don't conflate with earlier participants.",
|
|
@@ -14768,18 +14784,6 @@ async function buildInboundFileMeta(rawFiles, codeName, channel) {
|
|
|
14768
14784
|
return out;
|
|
14769
14785
|
}
|
|
14770
14786
|
await mcp.connect(new StdioServerTransport());
|
|
14771
|
-
var botUserId = null;
|
|
14772
|
-
async function getBotUserId() {
|
|
14773
|
-
try {
|
|
14774
|
-
const res = await fetch("https://slack.com/api/auth.test", {
|
|
14775
|
-
headers: { Authorization: `Bearer ${BOT_TOKEN}` }
|
|
14776
|
-
});
|
|
14777
|
-
const data = await res.json();
|
|
14778
|
-
return data.ok ? data.user_id ?? null : null;
|
|
14779
|
-
} catch {
|
|
14780
|
-
return null;
|
|
14781
|
-
}
|
|
14782
|
-
}
|
|
14783
14787
|
var userNameCache = /* @__PURE__ */ new Map();
|
|
14784
14788
|
async function resolveUserName(userId) {
|
|
14785
14789
|
if (!userId || userId === "unknown") return userId ?? "unknown";
|
|
@@ -14983,7 +14987,4 @@ if (THREAD_STORE_PATH) {
|
|
|
14983
14987
|
"slack-channel: AGT_AGENT_CODE_NAME not set \u2014 running without thread-follow persistence\n"
|
|
14984
14988
|
);
|
|
14985
14989
|
}
|
|
14986
|
-
botUserId = await getBotUserId();
|
|
14987
|
-
process.stderr.write(`slack-channel: Bot user ID: ${botUserId}
|
|
14988
|
-
`);
|
|
14989
14990
|
connectSocketModeSafely();
|