@open-everywhere/provider-discord 0.1.0
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/LICENSE +201 -0
- package/NOTICE +21 -0
- package/README.md +47 -0
- package/agent/access/discord-grant-click.ts +237 -0
- package/agent/approvals/discord-admin.ts +227 -0
- package/agent/approvals/discord-card.ts +248 -0
- package/agent/approvals/discord-flow.ts +469 -0
- package/agent/approvals/discord-scope-label.ts +27 -0
- package/agent/channels/discord-approval-card.ts +373 -0
- package/agent/channels/discord-backscroll.ts +593 -0
- package/agent/channels/discord-bot-name.ts +113 -0
- package/agent/channels/discord-channel-info.ts +83 -0
- package/agent/channels/discord-directory.ts +179 -0
- package/agent/channels/discord-dispatch-target.ts +103 -0
- package/agent/channels/discord-events.ts +175 -0
- package/agent/channels/discord-front.ts +355 -0
- package/agent/channels/discord-gateway.ts +3256 -0
- package/agent/channels/discord-identity.ts +269 -0
- package/agent/channels/discord-interaction-defer.ts +123 -0
- package/agent/channels/discord-message-ops.ts +127 -0
- package/agent/channels/discord-origin-codec.ts +105 -0
- package/agent/channels/discord-post-arm.ts +230 -0
- package/agent/channels/discord-post.ts +463 -0
- package/agent/channels/discord-read.ts +140 -0
- package/agent/channels/discord-rest.ts +85 -0
- package/agent/channels/discord.ts +244 -0
- package/agent/commands/discord-command-sync.ts +285 -0
- package/agent/commands/discord-commands.ts +291 -0
- package/agent/contributions.ts +31 -0
- package/agent/docs-generated.ts +15 -0
- package/agent/identity/discord-link-consent.ts +124 -0
- package/agent/ops.ts +14 -0
- package/agent/provider-events.ts +86 -0
- package/agent/register.ts +147 -0
- package/agent/surface-parity.ts +95 -0
- package/dist/agent/access/discord-grant-click.d.ts +25 -0
- package/dist/agent/access/discord-grant-click.d.ts.map +1 -0
- package/dist/agent/access/discord-grant-click.js +210 -0
- package/dist/agent/access/discord-grant-click.js.map +1 -0
- package/dist/agent/approvals/discord-admin.d.ts +63 -0
- package/dist/agent/approvals/discord-admin.d.ts.map +1 -0
- package/dist/agent/approvals/discord-admin.js +219 -0
- package/dist/agent/approvals/discord-admin.js.map +1 -0
- package/dist/agent/approvals/discord-card.d.ts +102 -0
- package/dist/agent/approvals/discord-card.d.ts.map +1 -0
- package/dist/agent/approvals/discord-card.js +217 -0
- package/dist/agent/approvals/discord-card.js.map +1 -0
- package/dist/agent/approvals/discord-flow.d.ts +109 -0
- package/dist/agent/approvals/discord-flow.d.ts.map +1 -0
- package/dist/agent/approvals/discord-flow.js +375 -0
- package/dist/agent/approvals/discord-flow.js.map +1 -0
- package/dist/agent/approvals/discord-scope-label.d.ts +3 -0
- package/dist/agent/approvals/discord-scope-label.d.ts.map +1 -0
- package/dist/agent/approvals/discord-scope-label.js +27 -0
- package/dist/agent/approvals/discord-scope-label.js.map +1 -0
- package/dist/agent/channels/discord-approval-card.d.ts +81 -0
- package/dist/agent/channels/discord-approval-card.d.ts.map +1 -0
- package/dist/agent/channels/discord-approval-card.js +303 -0
- package/dist/agent/channels/discord-approval-card.js.map +1 -0
- package/dist/agent/channels/discord-backscroll.d.ts +209 -0
- package/dist/agent/channels/discord-backscroll.d.ts.map +1 -0
- package/dist/agent/channels/discord-backscroll.js +505 -0
- package/dist/agent/channels/discord-backscroll.js.map +1 -0
- package/dist/agent/channels/discord-bot-name.d.ts +19 -0
- package/dist/agent/channels/discord-bot-name.d.ts.map +1 -0
- package/dist/agent/channels/discord-bot-name.js +98 -0
- package/dist/agent/channels/discord-bot-name.js.map +1 -0
- package/dist/agent/channels/discord-channel-info.d.ts +22 -0
- package/dist/agent/channels/discord-channel-info.d.ts.map +1 -0
- package/dist/agent/channels/discord-channel-info.js +65 -0
- package/dist/agent/channels/discord-channel-info.js.map +1 -0
- package/dist/agent/channels/discord-directory.d.ts +11 -0
- package/dist/agent/channels/discord-directory.d.ts.map +1 -0
- package/dist/agent/channels/discord-directory.js +170 -0
- package/dist/agent/channels/discord-directory.js.map +1 -0
- package/dist/agent/channels/discord-dispatch-target.d.ts +15 -0
- package/dist/agent/channels/discord-dispatch-target.d.ts.map +1 -0
- package/dist/agent/channels/discord-dispatch-target.js +99 -0
- package/dist/agent/channels/discord-dispatch-target.js.map +1 -0
- package/dist/agent/channels/discord-events.d.ts +58 -0
- package/dist/agent/channels/discord-events.d.ts.map +1 -0
- package/dist/agent/channels/discord-events.js +150 -0
- package/dist/agent/channels/discord-events.js.map +1 -0
- package/dist/agent/channels/discord-front.d.ts +115 -0
- package/dist/agent/channels/discord-front.d.ts.map +1 -0
- package/dist/agent/channels/discord-front.js +279 -0
- package/dist/agent/channels/discord-front.js.map +1 -0
- package/dist/agent/channels/discord-gateway.d.ts +783 -0
- package/dist/agent/channels/discord-gateway.d.ts.map +1 -0
- package/dist/agent/channels/discord-gateway.js +2591 -0
- package/dist/agent/channels/discord-gateway.js.map +1 -0
- package/dist/agent/channels/discord-identity.d.ts +110 -0
- package/dist/agent/channels/discord-identity.d.ts.map +1 -0
- package/dist/agent/channels/discord-identity.js +201 -0
- package/dist/agent/channels/discord-identity.js.map +1 -0
- package/dist/agent/channels/discord-interaction-defer.d.ts +33 -0
- package/dist/agent/channels/discord-interaction-defer.d.ts.map +1 -0
- package/dist/agent/channels/discord-interaction-defer.js +102 -0
- package/dist/agent/channels/discord-interaction-defer.js.map +1 -0
- package/dist/agent/channels/discord-message-ops.d.ts +3 -0
- package/dist/agent/channels/discord-message-ops.d.ts.map +1 -0
- package/dist/agent/channels/discord-message-ops.js +123 -0
- package/dist/agent/channels/discord-message-ops.js.map +1 -0
- package/dist/agent/channels/discord-origin-codec.d.ts +7 -0
- package/dist/agent/channels/discord-origin-codec.d.ts.map +1 -0
- package/dist/agent/channels/discord-origin-codec.js +98 -0
- package/dist/agent/channels/discord-origin-codec.js.map +1 -0
- package/dist/agent/channels/discord-post-arm.d.ts +3 -0
- package/dist/agent/channels/discord-post-arm.d.ts.map +1 -0
- package/dist/agent/channels/discord-post-arm.js +195 -0
- package/dist/agent/channels/discord-post-arm.js.map +1 -0
- package/dist/agent/channels/discord-post.d.ts +120 -0
- package/dist/agent/channels/discord-post.d.ts.map +1 -0
- package/dist/agent/channels/discord-post.js +426 -0
- package/dist/agent/channels/discord-post.js.map +1 -0
- package/dist/agent/channels/discord-read.d.ts +40 -0
- package/dist/agent/channels/discord-read.d.ts.map +1 -0
- package/dist/agent/channels/discord-read.js +128 -0
- package/dist/agent/channels/discord-read.js.map +1 -0
- package/dist/agent/channels/discord-rest.d.ts +33 -0
- package/dist/agent/channels/discord-rest.d.ts.map +1 -0
- package/dist/agent/channels/discord-rest.js +75 -0
- package/dist/agent/channels/discord-rest.js.map +1 -0
- package/dist/agent/channels/discord.d.ts +3 -0
- package/dist/agent/channels/discord.d.ts.map +1 -0
- package/dist/agent/channels/discord.js +230 -0
- package/dist/agent/channels/discord.js.map +1 -0
- package/dist/agent/commands/discord-command-sync.d.ts +66 -0
- package/dist/agent/commands/discord-command-sync.d.ts.map +1 -0
- package/dist/agent/commands/discord-command-sync.js +224 -0
- package/dist/agent/commands/discord-command-sync.js.map +1 -0
- package/dist/agent/commands/discord-commands.d.ts +45 -0
- package/dist/agent/commands/discord-commands.d.ts.map +1 -0
- package/dist/agent/commands/discord-commands.js +266 -0
- package/dist/agent/commands/discord-commands.js.map +1 -0
- package/dist/agent/contributions.d.ts +17 -0
- package/dist/agent/contributions.d.ts.map +1 -0
- package/dist/agent/contributions.js +31 -0
- package/dist/agent/contributions.js.map +1 -0
- package/dist/agent/docs-generated.d.ts +3 -0
- package/dist/agent/docs-generated.d.ts.map +1 -0
- package/dist/agent/docs-generated.js +13 -0
- package/dist/agent/docs-generated.js.map +1 -0
- package/dist/agent/identity/discord-link-consent.d.ts +13 -0
- package/dist/agent/identity/discord-link-consent.d.ts.map +1 -0
- package/dist/agent/identity/discord-link-consent.js +111 -0
- package/dist/agent/identity/discord-link-consent.js.map +1 -0
- package/dist/agent/ops.d.ts +2 -0
- package/dist/agent/ops.d.ts.map +1 -0
- package/dist/agent/ops.js +13 -0
- package/dist/agent/ops.js.map +1 -0
- package/dist/agent/provider-events.d.ts +3 -0
- package/dist/agent/provider-events.d.ts.map +1 -0
- package/dist/agent/provider-events.js +78 -0
- package/dist/agent/provider-events.js.map +1 -0
- package/dist/agent/register.d.ts +5 -0
- package/dist/agent/register.d.ts.map +1 -0
- package/dist/agent/register.js +125 -0
- package/dist/agent/register.js.map +1 -0
- package/dist/agent/surface-parity.d.ts +3 -0
- package/dist/agent/surface-parity.d.ts.map +1 -0
- package/dist/agent/surface-parity.js +90 -0
- package/dist/agent/surface-parity.js.map +1 -0
- package/package.json +185 -0
- package/tsconfig.build.json +22 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// THE DISCORD HALF of the surface-command reconcile plane (OPE-1687) — the deployment registers its
|
|
2
|
+
// own "/oe" command instead of waiting for an operator to run a CLI.
|
|
3
|
+
//
|
|
4
|
+
// WHAT IT DOES, ONCE PER PROCESS START: read the commands Discord holds for this application at each
|
|
5
|
+
// declared guild, project both sides onto the fields this deployment actually declares, and write
|
|
6
|
+
// ONLY when they differ. No difference ⇒ no write, so a warm deployment's steady-state cost is one
|
|
7
|
+
// vendor read per cold start.
|
|
8
|
+
//
|
|
9
|
+
// 🔴 THE WRITE IS ADDITIVE — `POST …/commands`, NEVER the bulk `PUT`. Discord's bulk PUT is a full
|
|
10
|
+
// OVERWRITE of every command at that scope, which is the right contract for the operator CLI
|
|
11
|
+
// (`scripts/register-discord-commands.ts`, run deliberately, documented as destructive) and the wrong
|
|
12
|
+
// one for an unattended boot. `docs/deploy/surfaces/discord.md` tells operators to register their own
|
|
13
|
+
// `/ask` command by hand at this same scope; a boot-time PUT of our one-command array would delete it
|
|
14
|
+
// silently on the next cold start. The per-command POST creates-or-updates by NAME and leaves every
|
|
15
|
+
// other command at the scope untouched, so this plane can only ever add or correct "/oe".
|
|
16
|
+
//
|
|
17
|
+
// 🔴 THE DIFF IS A PROJECTION, NOT A DEEP EQUAL. Discord's GET returns fields nobody declared — `id`,
|
|
18
|
+
// `application_id`, `version`, `guild_id`, `default_member_permissions`, `nsfw`, localisation maps —
|
|
19
|
+
// and omits `required` where it is false. A deep equal against the declared body would therefore
|
|
20
|
+
// differ EVERY time and turn "write only on a difference" into a write on every cold start. Both
|
|
21
|
+
// sides go through the same `projectCommand`, which keeps exactly the fields
|
|
22
|
+
// `buildOeDiscordCommandDefinition` sets and fills `required` with its Discord default.
|
|
23
|
+
//
|
|
24
|
+
// NEVER THROWS, AND NEVER BLOCKS BOOT FOR LONG. Every failure — no credential, a refused request, a
|
|
25
|
+
// timeout, malformed JSON — answers with an outcome the boot plane logs. A bot token never reaches an
|
|
26
|
+
// outcome's `detail`, and neither does a response body: a 401 says "401", not what Discord wrote back.
|
|
27
|
+
// The latency bound is two constants below, and both are load-bearing rather than tuning: no 429
|
|
28
|
+
// backoff at all, and one budget for the whole reconcile whatever the guild count.
|
|
29
|
+
//
|
|
30
|
+
// 🔴 ANY PROCESS THAT STARTS WITH BOTH CREDENTIALS WRITES TO THE DECLARED GUILD. The credential pair
|
|
31
|
+
// is the only gate, so a local `eve dev`/`eve start` run with the deployment's own `DISCORD_BOT_TOKEN`
|
|
32
|
+
// registers THAT CHECKOUT's command shape into the declared guild — see the warning in
|
|
33
|
+
// `agent/commands/discord-commands.ts`.
|
|
34
|
+
import { discordFetch } from "../channels/discord-rest.js";
|
|
35
|
+
import { buildOeDiscordCommandDefinition } from "./discord-commands.js";
|
|
36
|
+
/**
|
|
37
|
+
* 🔴 NO 429 BACKOFF AT ALL — the reconcile sits on the FIRST REQUEST's latency path.
|
|
38
|
+
*
|
|
39
|
+
* `withRetryAfter`'s defaults allow up to four attempts, a 30 s single wait and a 60 s total
|
|
40
|
+
* (`packages/core/agent/channels/rate-limit-retry.ts`), and its own header says a latency-critical
|
|
41
|
+
* caller must pass a tighter policy. A boot on a lambda is the strictest such caller there is: the
|
|
42
|
+
* cold start it rides is usually a Discord INTERACTION, which must be acknowledged inside 3 seconds.
|
|
43
|
+
* A 429 answered with `retry_after: 60` would otherwise sleep 30 s inside the boot's own top-level
|
|
44
|
+
* await, blow that ack, and possibly time the invocation out. So a rate-limited Discord is a plain
|
|
45
|
+
* failure here — the next cold start reads again, and the command list is stale meanwhile, which is
|
|
46
|
+
* the state it was already in.
|
|
47
|
+
*
|
|
48
|
+
* The same reasoning is why `discord-gateway.ts` gives its two ephemeral affordances `AFFORDANCE_RETRY`.
|
|
49
|
+
*/
|
|
50
|
+
const BOOT_RETRY = { maxAttempts: 1 };
|
|
51
|
+
/** How long ONE Discord call may take. `AbortSignal.timeout` bounds a call, never a backoff sleep —
|
|
52
|
+
* that is what {@link BOOT_RETRY} removes. */
|
|
53
|
+
const CALL_TIMEOUT_MS = 1_500;
|
|
54
|
+
/**
|
|
55
|
+
* The ceiling on the WHOLE reconcile, across every declared guild and both of each guild's calls.
|
|
56
|
+
* Without it the worst case multiplies by declared guild, and "bounded" would depend on how many
|
|
57
|
+
* rooms an operator listed. A guild the budget cannot reach reports `failed` and is read again on the
|
|
58
|
+
* next cold start.
|
|
59
|
+
*/
|
|
60
|
+
const RECONCILE_BUDGET_MS = 2_000;
|
|
61
|
+
const API_BASE = "https://discord.com/api/v10";
|
|
62
|
+
function isObject(v) {
|
|
63
|
+
return typeof v === "object" && v !== null;
|
|
64
|
+
}
|
|
65
|
+
function str(v) {
|
|
66
|
+
return typeof v === "string" ? v : "";
|
|
67
|
+
}
|
|
68
|
+
function numOrUndefined(v) {
|
|
69
|
+
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
70
|
+
}
|
|
71
|
+
function projectOptions(raw) {
|
|
72
|
+
if (!Array.isArray(raw))
|
|
73
|
+
return [];
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const entry of raw) {
|
|
76
|
+
if (!isObject(entry))
|
|
77
|
+
continue;
|
|
78
|
+
const min = numOrUndefined(entry["min_value"]);
|
|
79
|
+
const max = numOrUndefined(entry["max_value"]);
|
|
80
|
+
out.push({
|
|
81
|
+
name: str(entry["name"]),
|
|
82
|
+
description: str(entry["description"]),
|
|
83
|
+
type: numOrUndefined(entry["type"]) ?? 0,
|
|
84
|
+
// Discord OMITS `required` when it is false, so the default is part of the projection. Without
|
|
85
|
+
// it every optional option reads as a difference and the reconcile writes on every cold start.
|
|
86
|
+
required: entry["required"] === true,
|
|
87
|
+
...(min === undefined ? {} : { min_value: min }),
|
|
88
|
+
...(max === undefined ? {} : { max_value: max }),
|
|
89
|
+
options: projectOptions(entry["options"]),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Project one ApplicationCommand — declared or vendor-held — onto the fields this deployment sets.
|
|
96
|
+
* Returns undefined for anything that is not an object with a name, which is what a malformed or
|
|
97
|
+
* unexpected list entry looks like.
|
|
98
|
+
*
|
|
99
|
+
* OPTION ORDER IS PART OF THE PROJECTION. Discord renders a command's options in the order they were
|
|
100
|
+
* registered, so a reordered list is a real user-visible difference and must be written. Discord
|
|
101
|
+
* returns them in the registered order, so a steady state never reorders on its own.
|
|
102
|
+
*/
|
|
103
|
+
export function projectCommand(raw) {
|
|
104
|
+
if (!isObject(raw))
|
|
105
|
+
return undefined;
|
|
106
|
+
const name = str(raw["name"]);
|
|
107
|
+
if (name.length === 0)
|
|
108
|
+
return undefined;
|
|
109
|
+
return {
|
|
110
|
+
name,
|
|
111
|
+
// Discord's own default for `type` is 1 (CHAT_INPUT); the declaration sets it explicitly.
|
|
112
|
+
type: numOrUndefined(raw["type"]) ?? 1,
|
|
113
|
+
description: str(raw["description"]),
|
|
114
|
+
options: projectOptions(raw["options"]),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** A stable string for one projected command, so two projections compare as text. */
|
|
118
|
+
function canonical(command) {
|
|
119
|
+
const opt = (o) => [o.name, o.description, o.type, o.required, o.min_value ?? null, o.max_value ?? null, o.options.map(opt)];
|
|
120
|
+
return JSON.stringify([command.name, command.type, command.description, command.options.map(opt)]);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Compare the command this deployment DECLARES against the list Discord HOLDS at one scope.
|
|
124
|
+
*
|
|
125
|
+
* `held` is the raw array Discord's GET returned. Only the entry whose name matches the declared
|
|
126
|
+
* command is considered: every other command at that scope belongs to someone else and this plane
|
|
127
|
+
* neither reads nor writes it.
|
|
128
|
+
*/
|
|
129
|
+
export function discordCommandDrift(declared, held) {
|
|
130
|
+
const want = projectCommand(declared);
|
|
131
|
+
if (want === undefined) {
|
|
132
|
+
// A declaration this file cannot project is a code defect, not a vendor state. Refusing to write
|
|
133
|
+
// is the safe half: a bad body would replace a working command with a broken one.
|
|
134
|
+
return { changed: false, reason: "the declared command has no name — nothing was compared and nothing will be written" };
|
|
135
|
+
}
|
|
136
|
+
const list = Array.isArray(held) ? held : [];
|
|
137
|
+
const mine = list.map((entry) => projectCommand(entry)).find((entry) => entry !== undefined && entry.name === want.name);
|
|
138
|
+
if (mine === undefined) {
|
|
139
|
+
return { changed: true, reason: `"/${want.name}" is not registered at this scope` };
|
|
140
|
+
}
|
|
141
|
+
if (canonical(mine) !== canonical(want)) {
|
|
142
|
+
return { changed: true, reason: `"/${want.name}" is registered with a different shape` };
|
|
143
|
+
}
|
|
144
|
+
return { changed: false, reason: `"/${want.name}" already matches what this deployment declares` };
|
|
145
|
+
}
|
|
146
|
+
/** Read + compare + (only on a difference) write, for ONE guild. Never throws. `msLeft` is what is
|
|
147
|
+
* left of the whole reconcile's budget, so no single guild can spend more than the rest of them. */
|
|
148
|
+
async function reconcileOneGuild(guildId, applicationId, botToken, deps, msLeft) {
|
|
149
|
+
const declared = buildOeDiscordCommandDefinition();
|
|
150
|
+
const url = `${API_BASE}/applications/${applicationId}/guilds/${guildId}/commands`;
|
|
151
|
+
const headers = { authorization: `Bot ${botToken}`, "content-type": "application/json" };
|
|
152
|
+
const cap = deps.timeoutMs ?? CALL_TIMEOUT_MS;
|
|
153
|
+
const fetchDeps = { ...(deps.fetch ? { fetch: deps.fetch } : {}), retry: BOOT_RETRY };
|
|
154
|
+
/** This call's own ceiling: the per-call cap, or whatever is left of the reconcile's budget. */
|
|
155
|
+
const nextTimeout = () => Math.min(cap, msLeft());
|
|
156
|
+
if (nextTimeout() <= 0) {
|
|
157
|
+
return { status: "failed", detail: `guild ${guildId}: the boot budget was already spent on an earlier guild — read again on the next start` };
|
|
158
|
+
}
|
|
159
|
+
let held;
|
|
160
|
+
try {
|
|
161
|
+
const res = await discordFetch("GET /applications/{id}/guilds/{id}/commands", url, { method: "GET", headers, signal: AbortSignal.timeout(nextTimeout()) }, fetchDeps);
|
|
162
|
+
if (!res.ok)
|
|
163
|
+
return { status: "failed", detail: `guild ${guildId}: the command list read returned ${res.status}` };
|
|
164
|
+
held = await res.json();
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
return { status: "failed", detail: `guild ${guildId}: the command list read failed (${err instanceof Error ? err.name : "error"})` };
|
|
168
|
+
}
|
|
169
|
+
const drift = discordCommandDrift(declared, held);
|
|
170
|
+
if (!drift.changed)
|
|
171
|
+
return { status: "unchanged", detail: `guild ${guildId}: ${drift.reason}` };
|
|
172
|
+
if (nextTimeout() <= 0) {
|
|
173
|
+
return { status: "failed", detail: `guild ${guildId}: ${drift.reason}, and the boot budget was spent before the write — retried on the next start` };
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
// CREATE-OR-UPDATE BY NAME. Discord treats a POST naming an existing command as an update of
|
|
177
|
+
// that command, and touches nothing else at the scope — see the header on why the bulk PUT is
|
|
178
|
+
// wrong here.
|
|
179
|
+
const res = await discordFetch("POST /applications/{id}/guilds/{id}/commands", url, { method: "POST", headers, body: JSON.stringify(declared), signal: AbortSignal.timeout(nextTimeout()) }, fetchDeps);
|
|
180
|
+
if (!res.ok)
|
|
181
|
+
return { status: "failed", detail: `guild ${guildId}: ${drift.reason}, and the write returned ${res.status}` };
|
|
182
|
+
return { status: "updated", detail: `guild ${guildId}: ${drift.reason} — registered` };
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
return { status: "failed", detail: `guild ${guildId}: ${drift.reason}, and the write failed (${err instanceof Error ? err.name : "error"})` };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* THE RECONCILER the discord bundle registers with core's command plane.
|
|
190
|
+
*
|
|
191
|
+
* Runs at boot, so it is gated on BOTH deployment credentials being present: `DISCORD_APPLICATION_ID`
|
|
192
|
+
* says which app this is and `DISCORD_BOT_TOKEN` authenticates the call. Either missing ⇒ `skipped`,
|
|
193
|
+
* which is the correct reading of a deployment that carries the bundle but no Discord app — and it is
|
|
194
|
+
* also what keeps every offline test tier network-free, since no test publishes an application id.
|
|
195
|
+
*
|
|
196
|
+
* 🔴 THESE TWO STAY ENV. They are the credential pair this deployment speaks as, which is exactly the
|
|
197
|
+
* `secret`/`identity` half of the config-as-code line. The GUILD ids do not: they are tracked literals
|
|
198
|
+
* in `agent/commands/discord-commands.ts` and arrive here as the declared scope.
|
|
199
|
+
*/
|
|
200
|
+
export async function reconcileDiscordCommands(scope, deps = {}) {
|
|
201
|
+
const applicationId = deps.applicationId ?? process.env.DISCORD_APPLICATION_ID;
|
|
202
|
+
const botToken = deps.botToken ?? process.env.DISCORD_BOT_TOKEN;
|
|
203
|
+
if (!applicationId || !botToken) {
|
|
204
|
+
return {
|
|
205
|
+
status: "skipped",
|
|
206
|
+
detail: "DISCORD_APPLICATION_ID and DISCORD_BOT_TOKEN must both be set for this deployment to register its own commands.",
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const deadline = Date.now() + (deps.budgetMs ?? RECONCILE_BUDGET_MS);
|
|
210
|
+
const msLeft = () => deadline - Date.now();
|
|
211
|
+
const results = [];
|
|
212
|
+
for (const guildId of scope.scopes) {
|
|
213
|
+
results.push(await reconcileOneGuild(guildId, applicationId, botToken, deps, msLeft));
|
|
214
|
+
}
|
|
215
|
+
const detail = results.map((r) => r.detail).join("; ");
|
|
216
|
+
// A failure on ANY guild is the outcome, even when another guild succeeded: the boot log must not
|
|
217
|
+
// read green while one room is still missing the command.
|
|
218
|
+
if (results.some((r) => r.status === "failed"))
|
|
219
|
+
return { status: "failed", detail };
|
|
220
|
+
if (results.some((r) => r.status === "updated"))
|
|
221
|
+
return { status: "updated", detail };
|
|
222
|
+
return { status: "unchanged", detail };
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=discord-command-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discord-command-sync.js","sourceRoot":"","sources":["../../../agent/commands/discord-command-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type AttachSessionForCancel, type OeCommand } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
import { type OpenEverywhereDiscordAuth } from "../channels/discord-identity.js";
|
|
3
|
+
import type { DiscordInteractionResponse } from "../approvals/discord-flow.js";
|
|
4
|
+
/**
|
|
5
|
+
* Walk Discord's nested SUB_COMMAND_GROUP/SUB_COMMAND option tree for "/oe" into an
|
|
6
|
+
* `OeCommand`. Discord's schema REQUIRES picking a concrete subcommand — you cannot invoke a
|
|
7
|
+
* bare "/oe budget" the way Slack's free-text grammar allows ("budget" alone means
|
|
8
|
+
* budget-status there); that's a genuine, documented platform difference (see
|
|
9
|
+
* docs/deploy/surfaces/discord.md), not a bug. Defensive: untrusted wire JSON, every field
|
|
10
|
+
* probed, NEVER throws.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseDiscordOeCommandOptions(rawOptions: unknown): OeCommand;
|
|
13
|
+
interface ParsedOeInvocation {
|
|
14
|
+
readonly auth: OpenEverywhereDiscordAuth;
|
|
15
|
+
readonly command: OeCommand;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Parse a raw (already Ed25519-verified) Discord interaction body into the auth + command a
|
|
19
|
+
* "/oe" invocation carries — or an `{ error }` for a recognizably-ours-but-unusable body
|
|
20
|
+
* (no resolvable user, or a bot author), or `null` for anything that isn't type-2 name-"oe"
|
|
21
|
+
* at all (the front route's classifier already checked this; re-checking here is the same
|
|
22
|
+
* defensive belt-and-braces parseDiscordComponentClick applies to approval clicks). NEVER
|
|
23
|
+
* throws.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseOeCommandInteraction(raw: unknown): ParsedOeInvocation | {
|
|
26
|
+
readonly error: string;
|
|
27
|
+
} | null;
|
|
28
|
+
/**
|
|
29
|
+
* Handle one verified Discord interaction the front route classified as "ours" (an "/oe"
|
|
30
|
+
* APPLICATION_COMMAND). Returns null for anything that doesn't actually parse as one (the
|
|
31
|
+
* caller then forwards to Eve — defensive fallback, mirrors handleDiscordApprovalInteraction);
|
|
32
|
+
* otherwise ALWAYS returns an ephemeral interaction response — NEVER throws, because Discord's
|
|
33
|
+
* interactions endpoint must always answer within 3s. Zero-inference: this never calls the
|
|
34
|
+
* model, and never consults spend caps (see core.ts's header — that's the entire point).
|
|
35
|
+
*/
|
|
36
|
+
export declare function handleDiscordOeCommandInteraction(raw: unknown, attachSession?: AttachSessionForCancel): Promise<DiscordInteractionResponse | null>;
|
|
37
|
+
/**
|
|
38
|
+
* The Discord ApplicationCommand body for "/oe" — pure, no network, so the registration
|
|
39
|
+
* script (scripts/register-discord-commands.ts) and its test share exactly one definition.
|
|
40
|
+
* Shape: a "budget" SUB_COMMAND_GROUP (status/raise/clear) plus top-level "status"/"help"
|
|
41
|
+
* SUB_COMMANDs — see the file header for why "budget" alone isn't invokable on Discord.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildOeDiscordCommandDefinition(): Record<string, unknown>;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=discord-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discord-commands.d.ts","sourceRoot":"","sources":["../../../agent/commands/discord-commands.ts"],"names":[],"mappings":"AA2BA,OAAO,EAML,KAAK,sBAAsB,EAE3B,KAAK,SAAS,EACf,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAoB,KAAK,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AACnG,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AA2D/E;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,OAAO,GAAG,SAAS,CA2C3E;AAED,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAsB9G;AAED;;;;;;;GAOG;AACH,wBAAsB,iCAAiC,CACrD,GAAG,EAAE,OAAO,EACZ,aAAa,CAAC,EAAE,sBAAsB,GACrC,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,CAqB5C;AAWD;;;;;GAKG;AACH,wBAAgB,+BAA+B,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsDzE"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
// The Discord I/O half of the "/oe" zero-inference slash command (backlog item 23) — the
|
|
2
|
+
// sibling of approvals/discord-flow.ts's split (pure render/parse in one file, the
|
|
3
|
+
// interactions-endpoint handler here). Two entry points:
|
|
4
|
+
//
|
|
5
|
+
// buildOeDiscordCommandDefinition() — the pure ApplicationCommand JSON body for "/oe"
|
|
6
|
+
// (SUB_COMMAND_GROUP "budget" + top-level "status"/
|
|
7
|
+
// "stop"/"rate"/"help"). Shared by the registration script
|
|
8
|
+
// AND its test, so there is exactly one source of truth
|
|
9
|
+
// for the shape Discord expects.
|
|
10
|
+
// handleDiscordOeCommandInteraction() — called by the Discord front route
|
|
11
|
+
// (channels/discord-front.ts) for every verified
|
|
12
|
+
// interaction body its classifier marked "oe-command".
|
|
13
|
+
// Returns null for anything that isn't actually a
|
|
14
|
+
// parseable "/oe" invocation (defensive belt-and-braces
|
|
15
|
+
// — the front's classifier already checked type===2 &&
|
|
16
|
+
// data.name==="oe"), so the caller falls back to
|
|
17
|
+
// forwarding rather than silently swallowing the click.
|
|
18
|
+
// NEVER throws — Discord's interactions endpoint must
|
|
19
|
+
// always answer within 3s, so every failure mode here
|
|
20
|
+
// is fail-soft (an ephemeral note).
|
|
21
|
+
//
|
|
22
|
+
// WHY THIS NEVER REACHES discordChannel's onCommand: docs/deploy/surfaces/discord.md notes
|
|
23
|
+
// that hook "does not filter on a specific command name — it accepts ANY registered
|
|
24
|
+
// application command" and dispatches a full agent turn. "/oe" is deliberately intercepted
|
|
25
|
+
// at the FRONT (before onCommand ever sees it) so it costs zero inference — see
|
|
26
|
+
// channels/discord-front.ts's classifyDiscordInteraction.
|
|
27
|
+
import { bindFrom, MODEL_AB_RATING_MAX, MODEL_AB_RATING_MIN, OE_COMMAND_NAME, runOeCommand, } from "@open-everywhere/core/agent/adapter-api.js";
|
|
28
|
+
import { buildDiscordAuth } from "../channels/discord-identity.js";
|
|
29
|
+
// Discord interaction-response wire constants, defined locally (same "leaf-over-import" call
|
|
30
|
+
// approvals/discord-flow.ts and approvals/discord-card.ts make for their own copies): importing
|
|
31
|
+
// eve's channel module here just for two stable public-API integers isn't worth the coupling.
|
|
32
|
+
const RESPONSE_CHANNEL_MESSAGE = 4; // CHANNEL_MESSAGE_WITH_SOURCE — a fresh reply to the invocation
|
|
33
|
+
const EPHEMERAL_FLAG = 64; // only the invoking user sees the reply
|
|
34
|
+
function ephemeral(content) {
|
|
35
|
+
return { type: RESPONSE_CHANNEL_MESSAGE, data: { content, flags: EPHEMERAL_FLAG } };
|
|
36
|
+
}
|
|
37
|
+
// --- defensive wire-JSON probing (untrusted body, post-signature-check) ---------------------
|
|
38
|
+
// Mirrors approvals/discord-flow.ts's parseDiscordComponentClick posture exactly: this is
|
|
39
|
+
// attacker-shaped JSON reaching a public URL (Discord relays whatever a client sends), so
|
|
40
|
+
// every field is probed, nothing assumed present or well-typed.
|
|
41
|
+
function isObject(v) {
|
|
42
|
+
return typeof v === "object" && v !== null;
|
|
43
|
+
}
|
|
44
|
+
function str(v) {
|
|
45
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
46
|
+
}
|
|
47
|
+
function num(v) {
|
|
48
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
49
|
+
return v;
|
|
50
|
+
if (typeof v === "string" && v.trim().length > 0) {
|
|
51
|
+
const n = Number(v);
|
|
52
|
+
return Number.isFinite(n) ? n : undefined;
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
/** Read Discord's raw numeric channel `type` off the interaction payload — duplicated in
|
|
57
|
+
* miniature from channels/discord.ts's private channelTypeFromRaw (not exported there; this
|
|
58
|
+
* file only needs the one field and importing the whole module for it isn't worth the
|
|
59
|
+
* coupling — the same leaf-duplication call the header note makes). */
|
|
60
|
+
function channelTypeFromRaw(raw) {
|
|
61
|
+
const ch = raw["channel"];
|
|
62
|
+
if (ch && typeof ch === "object") {
|
|
63
|
+
const t = ch["type"];
|
|
64
|
+
if (typeof t === "number")
|
|
65
|
+
return String(t);
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
function optArray(options) {
|
|
70
|
+
return Array.isArray(options) ? options : [];
|
|
71
|
+
}
|
|
72
|
+
function findOpt(options, name) {
|
|
73
|
+
return options.find((o) => isObject(o) && o.name === name);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Walk Discord's nested SUB_COMMAND_GROUP/SUB_COMMAND option tree for "/oe" into an
|
|
77
|
+
* `OeCommand`. Discord's schema REQUIRES picking a concrete subcommand — you cannot invoke a
|
|
78
|
+
* bare "/oe budget" the way Slack's free-text grammar allows ("budget" alone means
|
|
79
|
+
* budget-status there); that's a genuine, documented platform difference (see
|
|
80
|
+
* docs/deploy/surfaces/discord.md), not a bug. Defensive: untrusted wire JSON, every field
|
|
81
|
+
* probed, NEVER throws.
|
|
82
|
+
*/
|
|
83
|
+
export function parseDiscordOeCommandOptions(rawOptions) {
|
|
84
|
+
const top = optArray(rawOptions);
|
|
85
|
+
if (top.length === 0)
|
|
86
|
+
return { kind: "help" }; // shouldn't happen (Discord enforces a subcommand) — safe default
|
|
87
|
+
const first = top[0];
|
|
88
|
+
const name = str(first.name);
|
|
89
|
+
if (name === "status")
|
|
90
|
+
return { kind: "status" };
|
|
91
|
+
if (name === "stop")
|
|
92
|
+
return { kind: "stop" };
|
|
93
|
+
if (name === "help")
|
|
94
|
+
return { kind: "help" };
|
|
95
|
+
if (name === "rate") {
|
|
96
|
+
// OPE-34. Discord types the option INTEGER with min/max, so a well-formed client cannot send an
|
|
97
|
+
// out-of-range value — but this is untrusted wire JSON, so the range is re-checked here and the
|
|
98
|
+
// message is the one a person reads. The BOUNDS COME FROM CORE, in all three places this file
|
|
99
|
+
// states them: a scale that lived here as a literal would silently start refusing scores the
|
|
100
|
+
// Slack parser accepts the day core's scale changed.
|
|
101
|
+
const scoreOpt = findOpt(optArray(first.options), "score");
|
|
102
|
+
const score = num(scoreOpt?.value);
|
|
103
|
+
if (score === undefined || !Number.isInteger(score) || score < MODEL_AB_RATING_MIN || score > MODEL_AB_RATING_MAX) {
|
|
104
|
+
return {
|
|
105
|
+
kind: "error",
|
|
106
|
+
message: `"/oe rate" needs a whole "score" from ${MODEL_AB_RATING_MIN} to ${MODEL_AB_RATING_MAX} (got ${JSON.stringify(scoreOpt?.value)}).`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return { kind: "rate", score };
|
|
110
|
+
}
|
|
111
|
+
if (name === "budget") {
|
|
112
|
+
const sub = optArray(first.options)[0];
|
|
113
|
+
const subName = sub ? str(sub.name) : undefined;
|
|
114
|
+
if (!subName || subName === "status")
|
|
115
|
+
return { kind: "budget-status" };
|
|
116
|
+
if (subName === "clear")
|
|
117
|
+
return { kind: "budget-clear" };
|
|
118
|
+
if (subName === "raise") {
|
|
119
|
+
const usdOpt = findOpt(optArray(sub?.options), "usd");
|
|
120
|
+
const usd = num(usdOpt?.value);
|
|
121
|
+
if (usd === undefined || usd <= 0) {
|
|
122
|
+
return {
|
|
123
|
+
kind: "error",
|
|
124
|
+
message: `"/oe budget raise" needs a positive "usd" amount (got ${JSON.stringify(usdOpt?.value)}).`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return { kind: "budget-raise", usd };
|
|
128
|
+
}
|
|
129
|
+
return { kind: "error", message: `Unknown "budget" subcommand "${subName}".` };
|
|
130
|
+
}
|
|
131
|
+
return { kind: "error", message: `Unknown "/oe" subcommand "${name ?? "?"}" — try "/oe help".` };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Parse a raw (already Ed25519-verified) Discord interaction body into the auth + command a
|
|
135
|
+
* "/oe" invocation carries — or an `{ error }` for a recognizably-ours-but-unusable body
|
|
136
|
+
* (no resolvable user, or a bot author), or `null` for anything that isn't type-2 name-"oe"
|
|
137
|
+
* at all (the front route's classifier already checked this; re-checking here is the same
|
|
138
|
+
* defensive belt-and-braces parseDiscordComponentClick applies to approval clicks). NEVER
|
|
139
|
+
* throws.
|
|
140
|
+
*/
|
|
141
|
+
export function parseOeCommandInteraction(raw) {
|
|
142
|
+
if (!isObject(raw) || raw["type"] !== 2)
|
|
143
|
+
return null;
|
|
144
|
+
const data = isObject(raw["data"]) ? raw["data"] : undefined;
|
|
145
|
+
if (!data || data["name"] !== OE_COMMAND_NAME)
|
|
146
|
+
return null;
|
|
147
|
+
const member = isObject(raw["member"]) ? raw["member"] : undefined;
|
|
148
|
+
const memberUser = member && isObject(member["user"]) ? member["user"] : undefined;
|
|
149
|
+
const user = isObject(raw["user"]) ? raw["user"] : undefined;
|
|
150
|
+
const userId = (memberUser ? str(memberUser["id"]) : undefined) ?? (user ? str(user["id"]) : undefined);
|
|
151
|
+
const isBot = memberUser?.["bot"] === true || user?.["bot"] === true;
|
|
152
|
+
if (!userId || isBot) {
|
|
153
|
+
return { error: "Couldn't identify who invoked this — commands can't be run by a bot." };
|
|
154
|
+
}
|
|
155
|
+
const channelId = str(raw["channel_id"]);
|
|
156
|
+
if (!channelId) {
|
|
157
|
+
return { error: "Couldn't resolve this channel's id — try again from a real channel or DM." };
|
|
158
|
+
}
|
|
159
|
+
const guildId = str(raw["guild_id"]);
|
|
160
|
+
const channelType = channelTypeFromRaw(raw);
|
|
161
|
+
const auth = buildDiscordAuth({ userId, guildId, channelId, channelType });
|
|
162
|
+
const command = parseDiscordOeCommandOptions(data["options"]);
|
|
163
|
+
return { auth, command };
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Handle one verified Discord interaction the front route classified as "ours" (an "/oe"
|
|
167
|
+
* APPLICATION_COMMAND). Returns null for anything that doesn't actually parse as one (the
|
|
168
|
+
* caller then forwards to Eve — defensive fallback, mirrors handleDiscordApprovalInteraction);
|
|
169
|
+
* otherwise ALWAYS returns an ephemeral interaction response — NEVER throws, because Discord's
|
|
170
|
+
* interactions endpoint must always answer within 3s. Zero-inference: this never calls the
|
|
171
|
+
* model, and never consults spend caps (see core.ts's header — that's the entire point).
|
|
172
|
+
*/
|
|
173
|
+
export async function handleDiscordOeCommandInteraction(raw, attachSession) {
|
|
174
|
+
const parsed = parseOeCommandInteraction(raw);
|
|
175
|
+
if (parsed === null)
|
|
176
|
+
return null; // not actually an "/oe" invocation — forward to Eve
|
|
177
|
+
if ("error" in parsed)
|
|
178
|
+
return ephemeral(parsed.error);
|
|
179
|
+
try {
|
|
180
|
+
const ctx = { channel: { kind: "discord" }, session: { auth: { current: parsed.auth } } };
|
|
181
|
+
const binding = bindFrom(ctx);
|
|
182
|
+
// `attachSession` is eve's I/O-free cancel door, threaded down from the route args purely for
|
|
183
|
+
// "/oe stop" (OPE-418). Every other branch ignores it, and its ABSENCE is handled with a
|
|
184
|
+
// user-facing sentence rather than a silent no-op — see core.ts's runStop.
|
|
185
|
+
const text = await runOeCommand(parsed.command, binding, attachSession ? { attachSession } : {});
|
|
186
|
+
return ephemeral(text);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
// runOeCommand's budget-raise/clear branches can throw a user-facing validation error
|
|
190
|
+
// (e.g. "propose from a DM needs an explicit scope") — surface it verbatim; anything else
|
|
191
|
+
// (a genuinely unexpected store/network failure) gets a generic fail-soft note. Either way,
|
|
192
|
+
// never throw out of this function.
|
|
193
|
+
console.error(`[open-everywhere:discord-commands] "/oe" command failed softly: ${String(err)}`);
|
|
194
|
+
return ephemeral(`Couldn't complete that: ${err instanceof Error ? err.message : String(err)}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// --- registration: the pure ApplicationCommand JSON body ------------------------------------
|
|
198
|
+
// Discord option type integers (stable public API constants, defined locally — see the header
|
|
199
|
+
// note on why this file doesn't import eve's channel module for two integers).
|
|
200
|
+
const SUB_COMMAND = 1;
|
|
201
|
+
const SUB_COMMAND_GROUP = 2;
|
|
202
|
+
const OPTION_INTEGER = 4;
|
|
203
|
+
const OPTION_NUMBER = 10;
|
|
204
|
+
const CHAT_INPUT = 1;
|
|
205
|
+
/**
|
|
206
|
+
* The Discord ApplicationCommand body for "/oe" — pure, no network, so the registration
|
|
207
|
+
* script (scripts/register-discord-commands.ts) and its test share exactly one definition.
|
|
208
|
+
* Shape: a "budget" SUB_COMMAND_GROUP (status/raise/clear) plus top-level "status"/"help"
|
|
209
|
+
* SUB_COMMANDs — see the file header for why "budget" alone isn't invokable on Discord.
|
|
210
|
+
*/
|
|
211
|
+
export function buildOeDiscordCommandDefinition() {
|
|
212
|
+
return {
|
|
213
|
+
name: OE_COMMAND_NAME,
|
|
214
|
+
type: CHAT_INPUT,
|
|
215
|
+
description: "Open Everywhere control-plane commands (status, budget) — no model turn.",
|
|
216
|
+
options: [
|
|
217
|
+
{
|
|
218
|
+
name: "budget",
|
|
219
|
+
description: "Spend-cap status and proposals for this channel/server.",
|
|
220
|
+
type: SUB_COMMAND_GROUP,
|
|
221
|
+
options: [
|
|
222
|
+
{ name: "status", description: "Show effective caps and current-month spend.", type: SUB_COMMAND },
|
|
223
|
+
{
|
|
224
|
+
name: "raise",
|
|
225
|
+
description: "Propose a new monthly USD cap for this channel (posts an approval card).",
|
|
226
|
+
type: SUB_COMMAND,
|
|
227
|
+
options: [
|
|
228
|
+
{
|
|
229
|
+
name: "usd",
|
|
230
|
+
description: "Proposed monthly USD budget.",
|
|
231
|
+
type: OPTION_NUMBER,
|
|
232
|
+
required: true,
|
|
233
|
+
min_value: 0.01,
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
},
|
|
237
|
+
{ name: "clear", description: "Propose clearing this channel's spend-limit override.", type: SUB_COMMAND },
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
{ name: "status", description: "Deploy sanity: app version, model config, memory provider.", type: SUB_COMMAND },
|
|
241
|
+
// OPE-418: the free, pre-model stop. A top-level SUB_COMMAND (not a group) because it takes
|
|
242
|
+
// no arguments — the channel it was invoked in IS the argument.
|
|
243
|
+
{ name: "stop", description: "Stand down the turn running in this channel.", type: SUB_COMMAND },
|
|
244
|
+
// OPE-34: the human mark on the model A/B test. A top-level SUB_COMMAND with one required
|
|
245
|
+
// INTEGER, bounded by Discord's own min/max so the client refuses an out-of-range score
|
|
246
|
+
// before it ever reaches the interaction endpoint.
|
|
247
|
+
{
|
|
248
|
+
name: "rate",
|
|
249
|
+
description: `Rate this channel's last answer (${MODEL_AB_RATING_MIN}-${MODEL_AB_RATING_MAX}) to mark the model A/B test.`,
|
|
250
|
+
type: SUB_COMMAND,
|
|
251
|
+
options: [
|
|
252
|
+
{
|
|
253
|
+
name: "score",
|
|
254
|
+
description: `${MODEL_AB_RATING_MIN} (worst) to ${MODEL_AB_RATING_MAX} (best).`,
|
|
255
|
+
type: OPTION_INTEGER,
|
|
256
|
+
required: true,
|
|
257
|
+
min_value: MODEL_AB_RATING_MIN,
|
|
258
|
+
max_value: MODEL_AB_RATING_MAX,
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
{ name: "help", description: "List the /oe commands.", type: SUB_COMMAND },
|
|
263
|
+
],
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=discord-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discord-commands.js","sourceRoot":"","sources":["../../../agent/commands/discord-commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const CONTRIBUTIONS: {
|
|
2
|
+
readonly schemaVersion: 1;
|
|
3
|
+
readonly package: "@open-everywhere/provider-discord";
|
|
4
|
+
readonly register: "agent/register.js#registerDiscordProvider";
|
|
5
|
+
readonly contributions: readonly [{
|
|
6
|
+
readonly kind: "channel";
|
|
7
|
+
readonly name: "discord";
|
|
8
|
+
readonly module: "agent/channels/discord.js";
|
|
9
|
+
readonly summary: "The Discord surface. Wires Discord's HTTP Interactions (slash commands) → an OpenEverywhere session, and — the point of the \"Discord-binding\" slice — populates the session auth context with the REAL…";
|
|
10
|
+
}, {
|
|
11
|
+
readonly kind: "channel";
|
|
12
|
+
readonly name: "discord-gateway";
|
|
13
|
+
readonly module: "agent/channels/discord-gateway.js";
|
|
14
|
+
readonly summary: "The Discord GATEWAY ingestion surface — Milestone 2 of the Discord Gateway feature.";
|
|
15
|
+
}];
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=contributions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../../agent/contributions.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,aAAa;aACxB,aAAa,EAAE,CAAC;aAChB,OAAO,EAAE,mCAAmC;aAC5C,QAAQ,EAAE,2CAA2C;aACrD,aAAa;iBACT,IAAI,EAAE,SAAS;iBAAE,IAAI,EAAE,SAAS;iBAAE,MAAM,EAAE,2BAA2B;iBAAE,OAAO,EAAE,2MAA2M;;iBAC3R,IAAI,EAAE,SAAS;iBAAE,IAAI,EAAE,iBAAiB;iBAAE,MAAM,EAAE,mCAAmC;iBAAE,OAAO,EAAE,qFAAqF;;CAEjL,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// THE CONTRIBUTION MANIFEST for `@open-everywhere/provider-discord` (OPE-375) — the curated
|
|
2
|
+
// catalogue of everything this package OFFERS an app's eve slot tree. `oe build` reads it;
|
|
3
|
+
// the app's `roster.config.ts` decides about it, and `roster.lock.json` records the review.
|
|
4
|
+
//
|
|
5
|
+
// 🔴 CURATED, NOT SCANNED, AND THAT IS MEASURED RATHER THAN STYLISTIC. A directory scan cannot
|
|
6
|
+
// enumerate contributions: core ships 47 files under `agent/channels/` of which 9 are channels, and
|
|
7
|
+
// 13 under `agent/connections/` of which 1 is a connection. Shape predicates do not rescue it either
|
|
8
|
+
// — only 8 of the 9 mounted core channels contain `defineChannel(`. A hand-kept list is the only
|
|
9
|
+
// honest catalogue, so keeping it current is a real authoring duty (`npm run lint:contributions`
|
|
10
|
+
// scan-compares the two kinds where a predicate IS sound — tools and hooks — and says plainly that
|
|
11
|
+
// the other four are unguarded).
|
|
12
|
+
//
|
|
13
|
+
// 🔴 THIS FILE IMPORTS NOTHING, DELIBERATELY. A provider bundle may reach core only through the
|
|
14
|
+
// `@open-everywhere/core/agent/adapter-api.js` barrel (the boundary lock, packages/core/scripts/
|
|
15
|
+
// boundary-test.ts), so a manifest that imported its own types would be a boundary red — and
|
|
16
|
+
// widening the barrel to admit a build-time type would be the wrong fix. Every manifest is therefore
|
|
17
|
+
// import-free DATA whose shape `roster-plan.ts` validates at build time, exactly as it must for a
|
|
18
|
+
// third-party package it has never seen. The types are in
|
|
19
|
+
// `@open-everywhere/core/agent/scaffold/roster-config.js` for READERS.
|
|
20
|
+
//
|
|
21
|
+
// ERASABLE SYNTAX ONLY (no `enum`, no `namespace`) — `oe build` loads this with a bare import.
|
|
22
|
+
export const CONTRIBUTIONS = {
|
|
23
|
+
schemaVersion: 1,
|
|
24
|
+
package: "@open-everywhere/provider-discord",
|
|
25
|
+
register: "agent/register.js#registerDiscordProvider",
|
|
26
|
+
contributions: [
|
|
27
|
+
{ kind: "channel", name: "discord", module: "agent/channels/discord.js", summary: "The Discord surface. Wires Discord's HTTP Interactions (slash commands) → an OpenEverywhere session, and — the point of the \"Discord-binding\" slice — populates the session auth context with the REAL…" },
|
|
28
|
+
{ kind: "channel", name: "discord-gateway", module: "agent/channels/discord-gateway.js", summary: "The Discord GATEWAY ingestion surface — Milestone 2 of the Discord Gateway feature." },
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=contributions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contributions.js","sourceRoot":"","sources":["../../agent/contributions.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,2FAA2F;AAC3F,4FAA4F;AAC5F,EAAE;AACF,+FAA+F;AAC/F,oGAAoG;AACpG,qGAAqG;AACrG,iGAAiG;AACjG,iGAAiG;AACjG,mGAAmG;AACnG,iCAAiC;AACjC,EAAE;AACF,gGAAgG;AAChG,iGAAiG;AACjG,6FAA6F;AAC7F,qGAAqG;AACrG,kGAAkG;AAClG,0DAA0D;AAC1D,uEAAuE;AACvE,EAAE;AACF,+FAA+F;AAE/F,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,aAAa,EAAE,CAAC;IAChB,OAAO,EAAE,mCAAmC;IAC5C,QAAQ,EAAE,2CAA2C;IACrD,aAAa,EAAE;QACb,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,2BAA2B,EAAE,OAAO,EAAE,2MAA2M,EAAE;QAC/R,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,mCAAmC,EAAE,OAAO,EAAE,qFAAqF,EAAE;KAC1L;CACO,CAAC"}
|