@agentcomms/events-daemon 0.18.0 → 0.18.2
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 +13 -4
- package/dist/{b3-contract-B6UBBK7F.mjs → b3-contract-BUm3TGtz.mjs} +1 -1
- package/dist/cli.mjs +111 -14
- package/dist/index.mjs +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -40,19 +40,28 @@ safe way to see what a rule would send before you point it at a webhook.
|
|
|
40
40
|
agent-events target add '{"targetId":"log","version":1,"kind":"dry-run","retentionMs":86400000}'
|
|
41
41
|
|
|
42
42
|
# 2. a rule: every received Gmail message with a subject, mapped to its subject only
|
|
43
|
-
agent-events sources list #
|
|
43
|
+
agent-events sources list # every connected account a rule can name: its source, id and name
|
|
44
44
|
agent-events rule create "$(cat rule.json)"
|
|
45
45
|
|
|
46
46
|
# 3. turn it on: each step prints an approval, which you approve by typing its challenge
|
|
47
|
-
agent-events rule enable mail-subjects 1
|
|
48
|
-
agent-events approve <approval-id>
|
|
49
47
|
agent-events enable-all # the standing approval that starts collection
|
|
50
48
|
agent-events approve <approval-id>
|
|
49
|
+
agent-events rule enable mail-subjects 1
|
|
50
|
+
agent-events approve <approval-id> # offers to test the target first; answering no is fine
|
|
51
51
|
|
|
52
|
-
# 4. read what matched
|
|
52
|
+
# 4. read what matched: the list gives each record's id, and show prints that record
|
|
53
53
|
agent-events dryrun list
|
|
54
|
+
agent-events dryrun show <record-id>
|
|
54
55
|
```
|
|
55
56
|
|
|
57
|
+
Switch collection on before approving the rule: a pending target can be tested only while collection is on. If a test
|
|
58
|
+
cannot run, the approval says why and still asks for its challenge.
|
|
59
|
+
|
|
60
|
+
An approval that `rule enable` or `enable-all` prints lasts 30 minutes. If it runs out, or is revoked, before you
|
|
61
|
+
approve it, nothing is turned on. On its next tick, or when it next starts, the service cancels the activation that
|
|
62
|
+
was waiting for it, and `status` stops counting it as pending. Run `rule enable` again for a new approval. An update
|
|
63
|
+
that lapsed this way no longer blocks the rule's next version.
|
|
64
|
+
|
|
56
65
|
`rule.json`:
|
|
57
66
|
|
|
58
67
|
```json
|
|
@@ -832,7 +832,7 @@ async function removeTarget(targetId, options = {}) {
|
|
|
832
832
|
//#endregion
|
|
833
833
|
//#region src/version.ts
|
|
834
834
|
/** The package version, read from package.json at build time. */
|
|
835
|
-
const VERSION = "0.18.
|
|
835
|
+
const VERSION = "0.18.2";
|
|
836
836
|
//#endregion
|
|
837
837
|
//#region src/mcp/server.ts
|
|
838
838
|
function reply(data) {
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as sourcesList, $t as assertControlSupported, A as normaliseWhatsAppSourceOptions, At as decideHeld, B as addTarget, Bt as CONTROL_PROTOCOL_VERSIONS, C as canonicalSseSubscriber, Ct as enableAll, D as normaliseResendSourceOptions, Dt as ACTIVATION_FAILURE, E as normaliseGmailSourceOptions, Et as doctor, F as canonicalJudgeBudget, Ft as catalogueList, G as addSubscriber, Gt as isControlRequest, H as targetsList, Ht as controlFailure, I as canonicalJudgeKindEnablement, It as catalogueShow, J as updateSubscriber, Jt as newInstanceRecord, K as removeSubscriber, Kt as negotiateVersion, L as judgeKindDefinition, Lt as approvalView, M as JUDGE_BUDGET_ID, Mt as dropDelivery, N as JUDGE_KIND_DEFINITIONS, Nt as heldList, O as normaliseSlackSourceOptions, Ot as isActivationFailureCode, P as canonicalJudge, Pt as retryDelivery, Q as sourceShow$1, Qt as writeInstance, R as createEventsMcpServer, Rt as EventControlClient, S as normaliseActivationDocument, St as updateJudge, T as classifySourceOptionChange, Tt as assertDoctorReportV2, U as updateTarget, Ut as encodeControlFrame, V as removeTarget$1, Vt as ControlFrameReader, W as targetTest, Wt as isControlHello, X as status, Xt as readInstance, Y as stop, Yt as processIsLive, Z as loadSqlite, Zt as removeInstance, _ as activationDocumentDigest, _t as judgeTest, an as ensureEventSocketDirectory, at as rulesList, b as canonicalTarget, bt as removeJudgeModel, cn as installErrorSink, ct as ruleTestRetained, d as B3_SECRET_TERMINAL_ORIGIN, dt as addJudge, en as assertSocketPathFits, et as createRule$1, f as isB3ControlOperation, ft as budgetShow, g as isOwnerControlWireOperation, gt as judgeModelStatus, h as OWNER_CONTROL_WIRES, ht as judgeKindsList, in as ensureEventPaths, it as ruleShow$1, j as APPROVED_TYPESAFE_MODELS, jt as deliveriesList, k as normaliseSourceOptions, kt as disableAll, lt as pause, m as renderB3ApprovalPreviewV1, mt as enableJudgeKind, nn as verifyControlSocket, nt as enableRule, on as ensureOwnerOnlyFile, ot as updateRule, p as isB3SecretOperation, pt as disableJudgeKind, q as subscribersList, qt as mayRecoverStaleInstance, rn as verifyPrivateSocketDirectory, rt as removeRule, sn as eventPaths, st as ruleTest, tn as controlEndpoint, tt as disableRule$1, ut as resume, v as canonicalFullRuleDocument, vt as judgesList, w as sseDeliveryTarget, wt as assertDoctorReportV1, x as disclosureBindingFor, xt as updateBudget, y as canonicalSubscriber, yt as removeJudge, z as VERSION, zt as probeControl } from "./b3-contract-
|
|
2
|
+
import { $ as sourcesList, $t as assertControlSupported, A as normaliseWhatsAppSourceOptions, At as decideHeld, B as addTarget, Bt as CONTROL_PROTOCOL_VERSIONS, C as canonicalSseSubscriber, Ct as enableAll, D as normaliseResendSourceOptions, Dt as ACTIVATION_FAILURE, E as normaliseGmailSourceOptions, Et as doctor, F as canonicalJudgeBudget, Ft as catalogueList, G as addSubscriber, Gt as isControlRequest, H as targetsList, Ht as controlFailure, I as canonicalJudgeKindEnablement, It as catalogueShow, J as updateSubscriber, Jt as newInstanceRecord, K as removeSubscriber, Kt as negotiateVersion, L as judgeKindDefinition, Lt as approvalView, M as JUDGE_BUDGET_ID, Mt as dropDelivery, N as JUDGE_KIND_DEFINITIONS, Nt as heldList, O as normaliseSlackSourceOptions, Ot as isActivationFailureCode, P as canonicalJudge, Pt as retryDelivery, Q as sourceShow$1, Qt as writeInstance, R as createEventsMcpServer, Rt as EventControlClient, S as normaliseActivationDocument, St as updateJudge, T as classifySourceOptionChange, Tt as assertDoctorReportV2, U as updateTarget, Ut as encodeControlFrame, V as removeTarget$1, Vt as ControlFrameReader, W as targetTest, Wt as isControlHello, X as status, Xt as readInstance, Y as stop, Yt as processIsLive, Z as loadSqlite, Zt as removeInstance, _ as activationDocumentDigest, _t as judgeTest, an as ensureEventSocketDirectory, at as rulesList, b as canonicalTarget, bt as removeJudgeModel, cn as installErrorSink, ct as ruleTestRetained, d as B3_SECRET_TERMINAL_ORIGIN, dt as addJudge, en as assertSocketPathFits, et as createRule$1, f as isB3ControlOperation, ft as budgetShow, g as isOwnerControlWireOperation, gt as judgeModelStatus, h as OWNER_CONTROL_WIRES, ht as judgeKindsList, in as ensureEventPaths, it as ruleShow$1, j as APPROVED_TYPESAFE_MODELS, jt as deliveriesList, k as normaliseSourceOptions, kt as disableAll, lt as pause, m as renderB3ApprovalPreviewV1, mt as enableJudgeKind, nn as verifyControlSocket, nt as enableRule, on as ensureOwnerOnlyFile, ot as updateRule, p as isB3SecretOperation, pt as disableJudgeKind, q as subscribersList, qt as mayRecoverStaleInstance, rn as verifyPrivateSocketDirectory, rt as removeRule, sn as eventPaths, st as ruleTest, tn as controlEndpoint, tt as disableRule$1, ut as resume, v as canonicalFullRuleDocument, vt as judgesList, w as sseDeliveryTarget, wt as assertDoctorReportV1, x as disclosureBindingFor, xt as updateBudget, y as canonicalSubscriber, yt as removeJudge, z as VERSION, zt as probeControl } from "./b3-contract-BUm3TGtz.mjs";
|
|
3
3
|
import { a as EventDomainError, d as isJudgeKind, f as validateLocalEndpoint, g as parseIpLiteral, h as assertApprovedAddressSet, i as webhookDeliveryTarget, l as JUDGE_KIND_TYPESAFE, m as assertAddressAllowed, n as canonicalWebhookTarget, o as JUDGE_KINDS, p as AddressPolicyError, r as secretWebhookUrlDescriptor, s as JUDGE_KIND_LAYA, t as canonicalSecretWebhookUrl, u as assertJudgeKindEnabled } from "./webhook-target-DyDTwWhV.mjs";
|
|
4
4
|
import { a as WriterQuiescer, c as openEventSecretStore, o as withEventWriterLease, r as RecordStorageError, s as migrateEventSecrets, t as EventRecordCipher } from "./records-DsUxYBwb.mjs";
|
|
5
5
|
import process$1 from "node:process";
|
|
@@ -9555,6 +9555,26 @@ async function assertLiveEventAccount(config, scope) {
|
|
|
9555
9555
|
source: scope.source
|
|
9556
9556
|
} });
|
|
9557
9557
|
}
|
|
9558
|
+
/**
|
|
9559
|
+
* Every connected account a rule may name, loaded afresh: Gmail's inboxes by their stable `ibx_…` id and the other
|
|
9560
|
+
* channels' accounts by their `acc_…` id, each beside the name a person knows it by. What `sources list` shows, so
|
|
9561
|
+
* a first rule's account id can be read from it before any rule is active (CUE-474).
|
|
9562
|
+
*/
|
|
9563
|
+
async function connectedEventAccounts(config) {
|
|
9564
|
+
const current = await config.load();
|
|
9565
|
+
const found = [];
|
|
9566
|
+
for (const [account, inbox] of Object.entries(current.inboxes)) if (inbox.provider === "gmail") found.push({
|
|
9567
|
+
source: "gmail",
|
|
9568
|
+
accountId: inbox.id,
|
|
9569
|
+
account
|
|
9570
|
+
});
|
|
9571
|
+
for (const [account, entry] of Object.entries(current.accounts)) if (entry.platform === "slack" || entry.platform === "resend" || entry.platform === "whatsapp") found.push({
|
|
9572
|
+
source: entry.platform,
|
|
9573
|
+
accountId: entry.id,
|
|
9574
|
+
account
|
|
9575
|
+
});
|
|
9576
|
+
return found.sort((left, right) => `${left.source}\u0000${left.account}`.localeCompare(`${right.source}\u0000${right.account}`));
|
|
9577
|
+
}
|
|
9558
9578
|
/** The current stable ids for one source, loaded afresh at the boundary. */
|
|
9559
9579
|
async function liveEventAccountIds(config, source) {
|
|
9560
9580
|
const current = await config.load();
|
|
@@ -11915,6 +11935,7 @@ var ActivationRuntime = class {
|
|
|
11915
11935
|
};
|
|
11916
11936
|
}
|
|
11917
11937
|
const oldId = `${input.ruleId}@${active.version}`;
|
|
11938
|
+
await this.#settleLapsedApprovals(oldId);
|
|
11918
11939
|
if (this.#store.database.prepare("SELECT 1 AS present FROM activation_intents WHERE replacement_of_version = ? AND status IN ('pending', 'pending-completion')").get(oldId) !== void 0) throw new CommsError("TRANSIENT", "this rule already has a replacement completing", { details: { reason: "REPLACEMENT_PENDING" } });
|
|
11919
11940
|
const live = await liveEventAccountIds(this.#config, parent.source.channel);
|
|
11920
11941
|
const points = this.#unionReplacementPoints(parent, document.rule).filter((point) => point.ruleVersion !== parent.version || live.has(point.accountId) && this.#holdsActivePoint(parent.ruleId, parent.version, {
|
|
@@ -12134,6 +12155,7 @@ var ActivationRuntime = class {
|
|
|
12134
12155
|
this.#cancel(intent.id, ACTIVATION_FAILURE.APPROVAL_ABSENT);
|
|
12135
12156
|
continue;
|
|
12136
12157
|
}
|
|
12158
|
+
if (this.#settleLapsedApproval(intent.id, intent.approval_id, record)) continue;
|
|
12137
12159
|
if (record.record.state === "approved") {
|
|
12138
12160
|
const binding = this.#settleOnDrift(intent);
|
|
12139
12161
|
if (binding === null) continue;
|
|
@@ -12168,7 +12190,8 @@ var ActivationRuntime = class {
|
|
|
12168
12190
|
}
|
|
12169
12191
|
/**
|
|
12170
12192
|
* A live owner retries only work that core has already marked used. Unlike startup recovery this never claims an
|
|
12171
|
-
* approved disclosure and it always carries the immutable usedAt back through the ordinary completion path.
|
|
12193
|
+
* approved disclosure and it always carries the immutable usedAt back through the ordinary completion path. After
|
|
12194
|
+
* that claimed work, it settles every unclaimed intent whose approval lapsed unused, as startup recovery does.
|
|
12172
12195
|
*/
|
|
12173
12196
|
async resumeClaimedCompletions() {
|
|
12174
12197
|
const rows = this.#store.database.prepare("SELECT id, kind, document, digest, effect, replacement_of_version, status, approval_id, claimed_at, completion_deadline FROM activation_intents WHERE status = 'pending-completion' ORDER BY id").all();
|
|
@@ -12197,6 +12220,36 @@ var ActivationRuntime = class {
|
|
|
12197
12220
|
throw error;
|
|
12198
12221
|
}
|
|
12199
12222
|
}
|
|
12223
|
+
await this.#settleLapsedApprovals();
|
|
12224
|
+
}
|
|
12225
|
+
/** Settles the unclaimed pending intents — of one replacement slot, or every one — whose approval lapsed unused. */
|
|
12226
|
+
async #settleLapsedApprovals(replacementOfVersion) {
|
|
12227
|
+
const rows = this.#store.database.prepare("SELECT id, approval_id, replacement_of_version FROM activation_intents WHERE status = 'pending' AND claimed_at IS NULL AND approval_id IS NOT NULL ORDER BY id").all();
|
|
12228
|
+
for (const row of rows) {
|
|
12229
|
+
if (replacementOfVersion !== void 0 && row.replacement_of_version !== replacementOfVersion) continue;
|
|
12230
|
+
this.#settleLapsedApproval(row.id, row.approval_id, await this.#approvals.get(row.approval_id));
|
|
12231
|
+
}
|
|
12232
|
+
}
|
|
12233
|
+
/**
|
|
12234
|
+
* CUE-491: a disclosure approval that core reads as expired or revoked can never be approved or claimed again, so
|
|
12235
|
+
* its unclaimed intent is cancelled (D2: expired, revoked and absent drop). Left pending, every pass would carry it
|
|
12236
|
+
* for good, and an update's would hold its rule's one replacement slot, refusing the next update as
|
|
12237
|
+
* REPLACEMENT_PENDING. Core's answer crossed an await: the write re-reads the intent under its transaction and
|
|
12238
|
+
* settles only the same unclaimed pending one. The failure code reuses APPROVAL_ABSENT — no usable approval
|
|
12239
|
+
* remains, as the claimed path already records for a non-used one — because the B3 control and doctor v1 schemas
|
|
12240
|
+
* that close the code set are pinned.
|
|
12241
|
+
*/
|
|
12242
|
+
#settleLapsedApproval(intentId, approvalId, record) {
|
|
12243
|
+
if (record?.form !== "v2" || record.record.kind !== "disclosure") return false;
|
|
12244
|
+
if (record.record.state !== "expired" && record.record.state !== "revoked") return false;
|
|
12245
|
+
let settled = false;
|
|
12246
|
+
this.#store.immediate(() => {
|
|
12247
|
+
const latest = this.#store.database.prepare("SELECT status, approval_id, claimed_at FROM activation_intents WHERE id = ?").get(intentId);
|
|
12248
|
+
if (latest?.status !== "pending" || latest.claimed_at !== null || latest.approval_id !== approvalId) return;
|
|
12249
|
+
this.#cancel(intentId, ACTIVATION_FAILURE.APPROVAL_ABSENT);
|
|
12250
|
+
settled = true;
|
|
12251
|
+
});
|
|
12252
|
+
return settled;
|
|
12200
12253
|
}
|
|
12201
12254
|
/**
|
|
12202
12255
|
* The live binding of a claimed intent, or null after settling it: a binding that no longer holds (its pointer or
|
|
@@ -14543,7 +14596,8 @@ var B3TargetTestControl = class {
|
|
|
14543
14596
|
SELECT 1 FROM object_revocations AS revoked
|
|
14544
14597
|
WHERE revoked.kind = 'target' AND revoked.object_id = target.target_id AND revoked.version = target.version
|
|
14545
14598
|
)`).get(targetId, targetVersion);
|
|
14546
|
-
if (row === void 0
|
|
14599
|
+
if (row === void 0) return refuse("the exact target version is not live");
|
|
14600
|
+
if (row.enabled !== 1) return refuse("collection is switched off: switch it on with enable-all first, or approve without the test");
|
|
14547
14601
|
if (selectorKind === "active") {
|
|
14548
14602
|
if (this.#store.database.prepare(`SELECT 1 AS present
|
|
14549
14603
|
FROM target_version_references AS reference
|
|
@@ -21069,7 +21123,7 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
|
|
|
21069
21123
|
control = await (options.startControlServer ?? startControlServer)({
|
|
21070
21124
|
endpoint: instance.endpoint,
|
|
21071
21125
|
token,
|
|
21072
|
-
handle: async (request) => handleControl(owner, lifecycle, database, activations, dryrun, sourceRegistry, writerQuiescer, request, ownerB3Service, doctorReport),
|
|
21126
|
+
handle: async (request) => handleControl(owner, lifecycle, database, activations, dryrun, sourceRegistry, writerQuiescer, request, ownerB3Service, doctorReport, () => connectedEventAccounts(core.config)),
|
|
21073
21127
|
verifyEndpoint: () => verifyPrivateSocketDirectory(paths)
|
|
21074
21128
|
});
|
|
21075
21129
|
if (options.startControlServer === void 0) {
|
|
@@ -21100,7 +21154,7 @@ async function runEventOwner(options = {}) {
|
|
|
21100
21154
|
await owner.stop();
|
|
21101
21155
|
}
|
|
21102
21156
|
}
|
|
21103
|
-
async function handleControl(owner, lifecycle, database, activations, dispatcher, sourceRegistry, writerQuiescer, request, b3OwnerService, doctorReport) {
|
|
21157
|
+
async function handleControl(owner, lifecycle, database, activations, dispatcher, sourceRegistry, writerQuiescer, request, b3OwnerService, doctorReport, connectedAccounts = async () => []) {
|
|
21104
21158
|
if (!isOwnerControlWireOperation(request.operation)) throw new CommsError("USAGE", "the local event control operation is not recognised");
|
|
21105
21159
|
if (isB3ControlOperation(request.operation)) {
|
|
21106
21160
|
if (!b3OwnerService) throw new CommsError("USAGE", "the local B3 control operation is not available");
|
|
@@ -21143,8 +21197,8 @@ async function handleControl(owner, lifecycle, database, activations, dispatcher
|
|
|
21143
21197
|
channel: definition.type.split(".")[0]
|
|
21144
21198
|
};
|
|
21145
21199
|
}
|
|
21146
|
-
case OWNER_CONTROL_WIRES.sourcesList: return sourceRows(database, sourceRegistry);
|
|
21147
|
-
case OWNER_CONTROL_WIRES.sourceShow: return sourceShow(database, sourceRegistry, requiredText(request.args.source, "source"));
|
|
21200
|
+
case OWNER_CONTROL_WIRES.sourcesList: return sourceRows(database, sourceRegistry, await connectedAccounts());
|
|
21201
|
+
case OWNER_CONTROL_WIRES.sourceShow: return sourceShow(database, sourceRegistry, requiredText(request.args.source, "source"), await connectedAccounts());
|
|
21148
21202
|
case OWNER_CONTROL_WIRES.rulesList: return ruleRows(database);
|
|
21149
21203
|
case OWNER_CONTROL_WIRES.ruleShow: return ruleShow(database, requiredText(request.args.ruleId, "rule id"));
|
|
21150
21204
|
case OWNER_CONTROL_WIRES.createRule:
|
|
@@ -21217,7 +21271,12 @@ function targetRows(database) {
|
|
|
21217
21271
|
function createTarget(database, document) {
|
|
21218
21272
|
return domain(() => new ImmutableVersions(database.database).createTarget(document));
|
|
21219
21273
|
}
|
|
21220
|
-
|
|
21274
|
+
/**
|
|
21275
|
+
* Each source's accounts: the scopes active rules read, and every connected account a rule may name, which has no
|
|
21276
|
+
* cursor scope until a rule reads it. Each carries the name it was connected under, so a person, or an agent writing
|
|
21277
|
+
* a first rule, can tell `ibx_…` from `ibx_…` (CUE-474).
|
|
21278
|
+
*/
|
|
21279
|
+
function sourceRows(database, sourceRegistry, connected = []) {
|
|
21221
21280
|
const rows = database.database.prepare("SELECT document FROM rule_versions WHERE state IN ('active', 'superseded')").all();
|
|
21222
21281
|
const scopes = /* @__PURE__ */ new Map();
|
|
21223
21282
|
for (const row of rows) {
|
|
@@ -21239,17 +21298,28 @@ function sourceRows(database, sourceRegistry) {
|
|
|
21239
21298
|
} catch {}
|
|
21240
21299
|
}
|
|
21241
21300
|
}
|
|
21242
|
-
const
|
|
21301
|
+
const nameOf = new Map(connected.map((entry) => [`${entry.source}\u0000${entry.accountId}`, entry.account]));
|
|
21302
|
+
const accounts = [...scopes.values()].map((scope) => ({
|
|
21303
|
+
...scope,
|
|
21304
|
+
account: nameOf.get(`${scope.source}\u0000${scope.accountId}`) ?? null
|
|
21305
|
+
}));
|
|
21306
|
+
for (const entry of connected) if (!accounts.some((row) => row.source === entry.source && row.accountId === entry.accountId)) accounts.push({
|
|
21307
|
+
source: entry.source,
|
|
21308
|
+
accountId: entry.accountId,
|
|
21309
|
+
account: entry.account,
|
|
21310
|
+
cursorScope: null
|
|
21311
|
+
});
|
|
21312
|
+
const ordered = accounts.sort((left, right) => `${left.source}\u0000${left.accountId}\u0000${left.cursorScope ?? ""}`.localeCompare(`${right.source}\u0000${right.accountId}\u0000${right.cursorScope ?? ""}`));
|
|
21243
21313
|
return sourceRegistry.sources().map((source) => ({
|
|
21244
21314
|
source,
|
|
21245
21315
|
accounts: ordered.filter((scope) => scope.source === source)
|
|
21246
21316
|
}));
|
|
21247
21317
|
}
|
|
21248
|
-
function sourceShow(database, sourceRegistry, source) {
|
|
21318
|
+
function sourceShow(database, sourceRegistry, source, connected = []) {
|
|
21249
21319
|
if (!sourceRegistry.sources().includes(source)) throw new CommsError("NOT_FOUND", "the requested source is not configured");
|
|
21250
21320
|
return {
|
|
21251
21321
|
source,
|
|
21252
|
-
accounts: sourceRows(database, sourceRegistry).find((row) => row.source === source).accounts
|
|
21322
|
+
accounts: sourceRows(database, sourceRegistry, connected).find((row) => row.source === source).accounts
|
|
21253
21323
|
};
|
|
21254
21324
|
}
|
|
21255
21325
|
async function acquireWithStaleRecovery(paths) {
|
|
@@ -21397,6 +21467,22 @@ function terminalSafeJson(value) {
|
|
|
21397
21467
|
function renderApprovalViewForTerminal(result) {
|
|
21398
21468
|
return escapeForDisplay(renderB3ApprovalPreviewV1(result).terminal);
|
|
21399
21469
|
}
|
|
21470
|
+
/**
|
|
21471
|
+
* An optional test the owner refused is reported and the approval goes on to its challenge (CUE-475): declining the
|
|
21472
|
+
* test was always allowed, so a test that could not run must not end the approval for the person.
|
|
21473
|
+
*/
|
|
21474
|
+
async function optionalTest(label, test) {
|
|
21475
|
+
try {
|
|
21476
|
+
return await test();
|
|
21477
|
+
} catch (error) {
|
|
21478
|
+
if (!isCommsError(error)) throw error;
|
|
21479
|
+
return {
|
|
21480
|
+
...label,
|
|
21481
|
+
test: "not run",
|
|
21482
|
+
reason: error.message
|
|
21483
|
+
};
|
|
21484
|
+
}
|
|
21485
|
+
}
|
|
21400
21486
|
/** Runs the only interactive terminal approval path; the owner, not this client, made every pending-target selector. */
|
|
21401
21487
|
async function runTerminalApprovalFlow(input) {
|
|
21402
21488
|
const start = await input.start(input.approvalId);
|
|
@@ -21404,13 +21490,14 @@ async function runTerminalApprovalFlow(input) {
|
|
|
21404
21490
|
const selector = start.pendingTargetSelectors[index];
|
|
21405
21491
|
if (selector === void 0) continue;
|
|
21406
21492
|
const answer = await input.terminal.question(`Test pending target ${index + 1} with the fixed synthetic payload before approving? [y/N] `);
|
|
21407
|
-
if (/^\s*(?:y|yes)\s*$/iu.test(answer)) input.print(await input.testPendingTarget(selector));
|
|
21493
|
+
if (/^\s*(?:y|yes)\s*$/iu.test(answer)) input.print(await optionalTest({ pendingTarget: index + 1 }, () => input.testPendingTarget(selector)));
|
|
21408
21494
|
}
|
|
21409
21495
|
for (let index = 0; index < start.pendingRuleJudgeTestSelectors.length; index += 1) {
|
|
21410
21496
|
const selector = start.pendingRuleJudgeTestSelectors[index];
|
|
21411
21497
|
if (selector === void 0) continue;
|
|
21412
21498
|
const answer = await input.terminal.question(`Test pending rule judge ${index + 1} with the fixed synthetic payload before approving? [y/N] `);
|
|
21413
|
-
|
|
21499
|
+
const testPendingRuleJudge = input.testPendingRuleJudge;
|
|
21500
|
+
if (/^\s*(?:y|yes)\s*$/iu.test(answer) && testPendingRuleJudge !== void 0) input.print(await optionalTest({ pendingRuleJudge: index + 1 }, () => testPendingRuleJudge(selector)));
|
|
21414
21501
|
}
|
|
21415
21502
|
const answer = await input.terminal.question(`Type this approval challenge: ${escapeForDisplay(start.challenge)}\n> `);
|
|
21416
21503
|
input.print(await input.approve(input.approvalId, answer));
|
|
@@ -21436,6 +21523,7 @@ async function run(argv, deps = {}) {
|
|
|
21436
21523
|
}
|
|
21437
21524
|
const env = deps.env ?? process.env;
|
|
21438
21525
|
const program = new Command();
|
|
21526
|
+
runEnvironment.set(program, env);
|
|
21439
21527
|
let ran = false;
|
|
21440
21528
|
program.name("agent-events").description("The local event-emission service: one foreground owner and local control clients.").option("--json", "write machine-readable output").option("--no-color", "never colour the output").option("--config-dir <dir>", "pin the configuration directory for this run").option("--state-dir <dir>", "pin the state directory for this run").option("--data-dir <dir>", "pin the data directory for this run").option("--secrets-dir <dir>", "pin the secrets directory for this run").option("--downloads-dir <dir>", "pin the downloads directory for this run").configureOutput({
|
|
21441
21529
|
writeOut: (text) => streams.stdout.write(escapeForDisplay(text)),
|
|
@@ -21868,10 +21956,19 @@ async function run(argv, deps = {}) {
|
|
|
21868
21956
|
return 1;
|
|
21869
21957
|
}
|
|
21870
21958
|
}
|
|
21959
|
+
/**
|
|
21960
|
+
* The state directory a control client looks in: the one pinned for this run, or the one the environment this CLI was
|
|
21961
|
+
* given resolves to. A caller that passes its own `env` to `run` reaches its own owner, never the one the process
|
|
21962
|
+
* environment names.
|
|
21963
|
+
*/
|
|
21871
21964
|
function controlOptions(program) {
|
|
21872
21965
|
const { stateDir } = program.opts();
|
|
21873
|
-
|
|
21966
|
+
if (stateDir !== void 0) return { stateDir };
|
|
21967
|
+
const env = runEnvironment.get(program);
|
|
21968
|
+
return env === void 0 ? {} : { stateDir: resolvePaths({ env }).stateDir };
|
|
21874
21969
|
}
|
|
21970
|
+
/** The environment each program was made with, read by `controlOptions` outside `run`'s closure. */
|
|
21971
|
+
const runEnvironment = /* @__PURE__ */ new WeakMap();
|
|
21875
21972
|
function parseVersion(value) {
|
|
21876
21973
|
const version = Number(value);
|
|
21877
21974
|
if (!Number.isSafeInteger(version) || version < 1) throw new CommsError("USAGE", "rule version must be a positive integer");
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { R as createEventsMcpServer, X as status, a as B3_CONTROL_V1_OPERATION_SPECS, c as B3_CONTROL_V1_SUCCESSOR_REQUIREMENT, d as B3_SECRET_TERMINAL_ORIGIN, i as B3_CONTROL_ERROR_CODES, l as B3_DESKTOP_CONTROL_OPERATIONS, m as renderB3ApprovalPreviewV1, n as B3_CAPABILITIES, o as B3_CONTROL_V1_RESULT_OPERATIONS, r as B3_CONTROL_CONTRACT, s as B3_CONTROL_V1_SCHEMA_VERSION, t as B3_APPROVAL_VIEW_VARIANTS, u as B3_JUDGE_UNAVAILABLE_VARIANTS } from "./b3-contract-
|
|
1
|
+
import { R as createEventsMcpServer, X as status, a as B3_CONTROL_V1_OPERATION_SPECS, c as B3_CONTROL_V1_SUCCESSOR_REQUIREMENT, d as B3_SECRET_TERMINAL_ORIGIN, i as B3_CONTROL_ERROR_CODES, l as B3_DESKTOP_CONTROL_OPERATIONS, m as renderB3ApprovalPreviewV1, n as B3_CAPABILITIES, o as B3_CONTROL_V1_RESULT_OPERATIONS, r as B3_CONTROL_CONTRACT, s as B3_CONTROL_V1_SCHEMA_VERSION, t as B3_APPROVAL_VIEW_VARIANTS, u as B3_JUDGE_UNAVAILABLE_VARIANTS } from "./b3-contract-BUm3TGtz.mjs";
|
|
2
2
|
export { B3_APPROVAL_VIEW_VARIANTS, B3_CAPABILITIES, B3_CONTROL_CONTRACT, B3_CONTROL_ERROR_CODES, B3_CONTROL_V1_OPERATION_SPECS, B3_CONTROL_V1_RESULT_OPERATIONS, B3_CONTROL_V1_SCHEMA_VERSION, B3_CONTROL_V1_SUCCESSOR_REQUIREMENT, B3_DESKTOP_CONTROL_OPERATIONS, B3_JUDGE_UNAVAILABLE_VARIANTS, B3_SECRET_TERMINAL_ORIGIN, createEventsMcpServer, renderB3ApprovalPreviewV1, status };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentcomms/events-daemon",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"description": "The local event service for agent-communications: rules you approve deliver Gmail, Slack, Resend and WhatsApp events to a signed webhook, a local live stream or a dry-run log.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@agentcomms/
|
|
38
|
-
"@agentcomms/
|
|
39
|
-
"@agentcomms/gmail": "0.18.
|
|
40
|
-
"@agentcomms/
|
|
41
|
-
"@agentcomms/
|
|
42
|
-
"@agentcomms/whatsapp": "0.18.
|
|
37
|
+
"@agentcomms/core": "0.18.2",
|
|
38
|
+
"@agentcomms/events": "0.18.2",
|
|
39
|
+
"@agentcomms/gmail": "0.18.2",
|
|
40
|
+
"@agentcomms/resend": "0.18.2",
|
|
41
|
+
"@agentcomms/slack": "0.18.2",
|
|
42
|
+
"@agentcomms/whatsapp": "0.18.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@modelcontextprotocol/client": "2.3.1",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"typecheck": "tsc -p tsconfig.json",
|
|
71
71
|
"test": "node --import ../../test/helpers/loopback-seal-preload.mjs --experimental-strip-types --disable-warning=ExperimentalWarning --test \"test/**/*.test.ts\""
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "e053c074e8f90d2b36d4d3b0dd127e073ccd0053"
|
|
74
74
|
}
|