borgmcp 3.16.1 → 4.0.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/README.md +10 -0
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +4 -1
- package/dist/cli-help.js.map +1 -1
- package/dist/direct-log.d.ts +2 -1
- package/dist/direct-log.d.ts.map +1 -1
- package/dist/direct-log.js +23 -9
- package/dist/direct-log.js.map +1 -1
- package/dist/docs-sections.js +3 -3
- package/dist/docs-sections.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -13
- package/dist/index.js.map +1 -1
- package/dist/log-audit-core.d.ts.map +1 -1
- package/dist/log-audit-core.js +4 -2
- package/dist/log-audit-core.js.map +1 -1
- package/dist/regen-format.d.ts +6 -2
- package/dist/regen-format.d.ts.map +1 -1
- package/dist/regen-format.js +37 -13
- package/dist/regen-format.js.map +1 -1
- package/dist/remote-client.d.ts +12 -5
- package/dist/remote-client.d.ts.map +1 -1
- package/dist/remote-client.js +32 -33
- package/dist/remote-client.js.map +1 -1
- package/dist/server-handshake.d.ts +1 -1
- package/dist/tool-manifest.d.ts.map +1 -1
- package/dist/tool-manifest.js +55 -25
- package/dist/tool-manifest.js.map +1 -1
- package/dist/tool-scope.d.ts +1 -1
- package/dist/tool-scope.d.ts.map +1 -1
- package/dist/tool-scope.js +2 -0
- package/dist/tool-scope.js.map +1 -1
- package/docs/DOCUMENTS.md +4 -2
- package/docs/RELEASING.md +4 -2
- package/package.json +2 -2
- package/src/cli-help.ts +4 -1
- package/src/direct-log.ts +25 -8
- package/src/docs-sections.ts +3 -3
- package/src/index.ts +50 -12
- package/src/log-audit-core.ts +4 -2
- package/src/regen-format.ts +39 -13
- package/src/remote-client.ts +74 -54
- package/src/server-handshake.ts +1 -1
- package/src/tool-manifest.ts +57 -25
- package/src/tool-scope.ts +2 -0
- package/dist/local-log-routing.d.ts +0 -19
- package/dist/local-log-routing.d.ts.map +0 -1
- package/dist/local-log-routing.js +0 -67
- package/dist/local-log-routing.js.map +0 -1
- package/src/local-log-routing.ts +0 -106
package/src/tool-manifest.ts
CHANGED
|
@@ -208,7 +208,8 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
208
208
|
'this reads oldest-unread-first from your server cursor and ' +
|
|
209
209
|
'advances the watermark so bursts are not skipped. Optional `since` is a strict-after ' +
|
|
210
210
|
'cursor for explicit bounded reads only; do not use it with the same timestamp as a ' +
|
|
211
|
-
'notification preview because it can skip the boundary entry.'
|
|
211
|
+
'notification preview because it can skip the boundary entry. Use `borg_read-entry` ' +
|
|
212
|
+
'to fetch one known entry without changing the unread cursor.',
|
|
212
213
|
inputSchema: {
|
|
213
214
|
type: 'object',
|
|
214
215
|
properties: {
|
|
@@ -229,6 +230,22 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
229
230
|
},
|
|
230
231
|
},
|
|
231
232
|
},
|
|
233
|
+
{
|
|
234
|
+
name: 'borg_read-entry',
|
|
235
|
+
description:
|
|
236
|
+
'Read one complete activity-log entry by its canonical UUID or unique 8-hex prefix without changing the unread cursor. Returns the same entry shape and structured routing recipients as borg_read-log. Use borg_read-log unread_only=true for routine wake drains. Direct routing controls delivery and wakes, not read confidentiality inside the cube.',
|
|
237
|
+
inputSchema: {
|
|
238
|
+
type: 'object',
|
|
239
|
+
properties: {
|
|
240
|
+
entry_id: {
|
|
241
|
+
type: 'string',
|
|
242
|
+
pattern: '^(?:[0-9A-Fa-f]{8}|[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[1-8][0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12})$',
|
|
243
|
+
description: 'Canonical activity-log entry UUID or unique 8-hex prefix.',
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
required: ['entry_id'],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
232
249
|
{
|
|
233
250
|
name: 'borg_ack',
|
|
234
251
|
description:
|
|
@@ -250,6 +267,22 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
250
267
|
},
|
|
251
268
|
},
|
|
252
269
|
},
|
|
270
|
+
{
|
|
271
|
+
name: 'borg_ack-status',
|
|
272
|
+
description:
|
|
273
|
+
'Read acknowledgement status for one activity-log entry without changing it. Returns per-recipient acknowledgements and advisory claims separately. Activity-log silence is not evidence that acknowledgement is missing; use this query instead. This read-only query does not acknowledge or claim the entry and preserves unread cursors.',
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: 'object',
|
|
276
|
+
required: ['entry_id'],
|
|
277
|
+
properties: {
|
|
278
|
+
entry_id: {
|
|
279
|
+
type: 'string',
|
|
280
|
+
format: 'uuid',
|
|
281
|
+
description: 'Full UUID of the activity-log entry to inspect.',
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
},
|
|
253
286
|
{
|
|
254
287
|
name: 'borg_decide',
|
|
255
288
|
description:
|
|
@@ -369,26 +402,29 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
369
402
|
{
|
|
370
403
|
name: 'borg_log',
|
|
371
404
|
description:
|
|
372
|
-
'Append a message to the cube\'s activity log
|
|
405
|
+
'Append a message to the cube\'s activity log with an explicit audience. Every call must pass `to: "broadcast"` for all drones or a non-empty selector array for direct delivery. Selectors accept an exact drone label, drone id, the stable 8-hex `id:` token shown in roster/read-log, role name, or role slug. Message text and taxonomy classes never choose the audience; optional `class` records classification/lifecycle metadata only. Cite durable cube documents through `documents`; citations carry current metadata but do not inline document content. Direct routing controls delivery and wakes, not read confidentiality inside the cube.',
|
|
373
406
|
inputSchema: {
|
|
374
407
|
type: 'object',
|
|
375
408
|
properties: {
|
|
376
409
|
message: { type: 'string', description: 'The log message (max 10KB).' },
|
|
377
410
|
to: {
|
|
378
|
-
|
|
379
|
-
|
|
411
|
+
oneOf: [
|
|
412
|
+
{ type: 'string', enum: ['broadcast'] },
|
|
413
|
+
{
|
|
414
|
+
type: 'array',
|
|
415
|
+
items: { type: 'string', minLength: 1, maxLength: 120 },
|
|
416
|
+
minItems: 1,
|
|
417
|
+
maxItems: 100,
|
|
418
|
+
uniqueItems: true,
|
|
419
|
+
},
|
|
420
|
+
],
|
|
380
421
|
description:
|
|
381
|
-
'
|
|
422
|
+
'Required explicit audience: "broadcast" for every drone, or a non-empty array of exact drone labels, drone ids, stable 8-hex `id:` tokens, role names, or role slugs.',
|
|
382
423
|
},
|
|
383
424
|
class: {
|
|
384
425
|
type: 'string',
|
|
385
426
|
description:
|
|
386
|
-
'Optional declared message class
|
|
387
|
-
},
|
|
388
|
-
visibility: {
|
|
389
|
-
type: 'string',
|
|
390
|
-
enum: ['broadcast', 'direct'],
|
|
391
|
-
description: 'Optional explicit visibility. Overrides class-based routing defaults.',
|
|
427
|
+
'Optional declared message class for classification/lifecycle metadata. It never changes the required `to` audience.',
|
|
392
428
|
},
|
|
393
429
|
documents: {
|
|
394
430
|
type: 'array',
|
|
@@ -399,7 +435,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
399
435
|
description: 'Optional full opaque ids of 1-100 same-cube documents to cite atomically. Unknown, duplicate, or foreign ids are refused.',
|
|
400
436
|
},
|
|
401
437
|
},
|
|
402
|
-
required: ['message'],
|
|
438
|
+
required: ['message', 'to'],
|
|
403
439
|
},
|
|
404
440
|
},
|
|
405
441
|
{
|
|
@@ -440,14 +476,12 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
440
476
|
cube_directive: { type: 'string', description: 'New cube directive markdown (optional).' },
|
|
441
477
|
message_taxonomy: {
|
|
442
478
|
type: 'array',
|
|
443
|
-
description: 'New
|
|
479
|
+
description: 'New classification/lifecycle taxonomy (optional). REPLACES the whole taxonomy; the server re-validates non-overlapping prefixes and unique class names. Pass [] to clear. To change ONE class without resending the whole array, use borg_patch-taxonomy-class instead. Optional lifecycle tags mark dispatch/completion classes for stuck-dispatch detection. Taxonomy never chooses log recipients.',
|
|
444
480
|
items: {
|
|
445
481
|
type: 'object',
|
|
446
482
|
properties: {
|
|
447
483
|
class: { type: 'string', description: 'Unique class name.' },
|
|
448
|
-
prefixes: { type: 'array', items: { type: 'string' }, description: 'Message prefixes
|
|
449
|
-
routing: { type: 'string', enum: ['broadcast', 'directed'], description: 'Routing mode.' },
|
|
450
|
-
default_to: { type: 'array', items: { type: 'string' }, description: 'Default recipients (role name/slug/label, or @human-seat) for a directed class.' },
|
|
484
|
+
prefixes: { type: 'array', items: { type: 'string' }, description: 'Message prefixes classified by this class.' },
|
|
451
485
|
lifecycle: { type: 'string', enum: ['dispatch', 'completion'], description: 'Optional lifecycle marker for stuck-dispatch detection.' },
|
|
452
486
|
},
|
|
453
487
|
},
|
|
@@ -459,7 +493,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
459
493
|
{
|
|
460
494
|
name: 'borg_patch-taxonomy-class',
|
|
461
495
|
description:
|
|
462
|
-
"Patch ONE
|
|
496
|
+
"Patch ONE classification/lifecycle class in a cube's message_taxonomy without resending the whole taxonomy (avoids clobbering). action=add|replace|remove (replace/remove match name case-insensitively). The full taxonomy is re-validated for non-overlapping prefixes and unique names. Optional lifecycle tags mark dispatch/completion classes for stuck-dispatch detection. Taxonomy never chooses log recipients.",
|
|
463
497
|
inputSchema: {
|
|
464
498
|
type: 'object',
|
|
465
499
|
properties: {
|
|
@@ -467,15 +501,13 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
467
501
|
action: { type: 'string', enum: ['add', 'replace', 'remove'], description: 'add / replace / remove a single class.' },
|
|
468
502
|
class_def: {
|
|
469
503
|
type: 'object',
|
|
470
|
-
description: 'The class definition (for add/replace). Shape: { class, prefixes?,
|
|
504
|
+
description: 'The class definition (for add/replace). Shape: { class, prefixes?, lifecycle? }.',
|
|
471
505
|
properties: {
|
|
472
506
|
class: { type: 'string', description: 'Unique class name.' },
|
|
473
|
-
prefixes: { type: 'array', items: { type: 'string' }, description: 'Message prefixes
|
|
474
|
-
routing: { type: 'string', enum: ['broadcast', 'directed'], description: 'Routing mode.' },
|
|
475
|
-
default_to: { type: 'array', items: { type: 'string' }, description: 'Default recipients (required for directed classes): role name/slug/label, or @human-seat.' },
|
|
507
|
+
prefixes: { type: 'array', items: { type: 'string' }, description: 'Message prefixes classified by this class.' },
|
|
476
508
|
lifecycle: { type: 'string', enum: ['dispatch', 'completion'], description: 'Optional lifecycle marker for stuck-dispatch detection.' },
|
|
477
509
|
},
|
|
478
|
-
required: ['class'
|
|
510
|
+
required: ['class'],
|
|
479
511
|
},
|
|
480
512
|
class: { type: 'string', description: 'For remove only: the name of the class to drop (case-insensitive).' },
|
|
481
513
|
},
|
|
@@ -508,7 +540,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
508
540
|
is_mandatory: { type: 'boolean', description: 'If true, role-less assimilation prioritizes this unoccupied role before ordinary worker roles. Platform-wide management roles are never auto-assigned; a mandatory human-operator role is selected first until occupied.' },
|
|
509
541
|
is_human_seat: { type: 'boolean', description: 'If true, this role represents the cube\'s human-occupied seat (where the human Queen sits directly). The class-hierarchy guard in reassign-drone allows promotion FROM a human-seat role TO the platform Queen role; promotion from non-human-seat roles is rejected.' },
|
|
510
542
|
can_broadcast: { type: 'boolean', description: 'If true, drones in this role may post broadcast log entries when strict broadcast gating is enabled.' },
|
|
511
|
-
receives_all_direct: { type: 'boolean', description: 'If true, drones in this role
|
|
543
|
+
receives_all_direct: { type: 'boolean', description: 'If true, drones in this role are included as observer/audit recipients for every direct route.' },
|
|
512
544
|
},
|
|
513
545
|
required: ['cube_id', 'name', 'short_description', 'detailed_description'],
|
|
514
546
|
},
|
|
@@ -527,7 +559,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
527
559
|
is_mandatory: { type: 'boolean', description: 'Set true/false to prioritize this unoccupied role during role-less assimilation. Platform-wide management roles are never auto-assigned.' },
|
|
528
560
|
is_human_seat: { type: 'boolean', description: 'Set true/false to mark/unmark this as the cube\'s human-occupied seat (the elevation source for the platform Queen role).' },
|
|
529
561
|
can_broadcast: { type: 'boolean', description: 'Set true/false to allow or deny broadcast log entries when strict broadcast gating is enabled.' },
|
|
530
|
-
receives_all_direct: { type: 'boolean', description: 'Set true/false to
|
|
562
|
+
receives_all_direct: { type: 'boolean', description: 'Set true/false to include or remove this role as observer/audit recipients for every direct route.' },
|
|
531
563
|
},
|
|
532
564
|
required: ['role_id'],
|
|
533
565
|
},
|
|
@@ -550,7 +582,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
550
582
|
},
|
|
551
583
|
{
|
|
552
584
|
name: 'borg_delete-role',
|
|
553
|
-
description: 'Delete a role using the selected local client\'s cube-management grant. Unknown or inaccessible roles refuse. Also refuses for the default, mandatory, or human-seat role
|
|
585
|
+
description: 'Delete a role using the selected local client\'s cube-management grant. Unknown or inaccessible roles refuse. Also refuses for the default, mandatory, or human-seat role, or a role assigned to an active drone. Reassign active drones with borg_reassign-drone or remove them with borg_evict-drone first. Evicted drones that held the deleted role are reassigned to the cube\'s default role; their activity-log attribution is unaffected.',
|
|
554
586
|
inputSchema: {
|
|
555
587
|
type: 'object',
|
|
556
588
|
properties: {
|
package/src/tool-scope.ts
CHANGED
|
@@ -20,10 +20,12 @@ export const UNIVERSAL_TOOLS = [
|
|
|
20
20
|
'borg_regen',
|
|
21
21
|
'borg_log',
|
|
22
22
|
'borg_read-log',
|
|
23
|
+
'borg_read-entry',
|
|
23
24
|
'borg_roster',
|
|
24
25
|
'borg_stream-status',
|
|
25
26
|
'borg_whoami',
|
|
26
27
|
'borg_ack',
|
|
28
|
+
'borg_ack-status',
|
|
27
29
|
'borg_version',
|
|
28
30
|
'borg_cube',
|
|
29
31
|
'borg_role',
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export interface LocalRoutingDrone {
|
|
2
|
-
id: string;
|
|
3
|
-
label?: string | null;
|
|
4
|
-
role_id?: string | null;
|
|
5
|
-
}
|
|
6
|
-
export interface LocalRoutingRole {
|
|
7
|
-
id: string;
|
|
8
|
-
name: string;
|
|
9
|
-
is_human_seat?: boolean;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Resolve the public `borg_log to:` addressing forms against the local
|
|
13
|
-
* server's authoritative cube roster. This intentionally mirrors the hosted
|
|
14
|
-
* route resolver: exact drone id/label first, then displayed short UUID, then
|
|
15
|
-
* role name/slug expansion. Every failure is closed before the log POST so a
|
|
16
|
-
* miss can never degrade into a broadcast.
|
|
17
|
-
*/
|
|
18
|
-
export declare function resolveLocalLogRecipients(recipients: readonly string[], drones: readonly LocalRoutingDrone[], roles: readonly LocalRoutingRole[]): string[];
|
|
19
|
-
//# sourceMappingURL=local-log-routing.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"local-log-routing.d.ts","sourceRoot":"","sources":["../src/local-log-routing.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,MAAM,EAAE,SAAS,iBAAiB,EAAE,EACpC,KAAK,EAAE,SAAS,gBAAgB,EAAE,GACjC,MAAM,EAAE,CAgFV"}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { roleSlug } from './role-resolver.js';
|
|
2
|
-
/**
|
|
3
|
-
* Resolve the public `borg_log to:` addressing forms against the local
|
|
4
|
-
* server's authoritative cube roster. This intentionally mirrors the hosted
|
|
5
|
-
* route resolver: exact drone id/label first, then displayed short UUID, then
|
|
6
|
-
* role name/slug expansion. Every failure is closed before the log POST so a
|
|
7
|
-
* miss can never degrade into a broadcast.
|
|
8
|
-
*/
|
|
9
|
-
export function resolveLocalLogRecipients(recipients, drones, roles) {
|
|
10
|
-
const ids = new Set();
|
|
11
|
-
for (const recipient of recipients) {
|
|
12
|
-
if (recipient === '@human-seat') {
|
|
13
|
-
const roleIds = new Set(roles.filter((role) => role.is_human_seat === true).map((role) => role.id));
|
|
14
|
-
const matches = drones.filter((drone) => drone.role_id !== null && drone.role_id !== undefined && roleIds.has(drone.role_id));
|
|
15
|
-
if (roleIds.size === 0 || matches.length === 0) {
|
|
16
|
-
throw new Error('Direct-message role recipient has no active drones: @human-seat. ' +
|
|
17
|
-
'Use borg_roster to find active recipients, or omit to broadcast if your role can broadcast.');
|
|
18
|
-
}
|
|
19
|
-
for (const drone of matches)
|
|
20
|
-
ids.add(drone.id);
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
const droneMatches = drones.filter((drone) => drone.id === recipient || drone.label === recipient);
|
|
24
|
-
const uniqueDroneIds = [...new Set(droneMatches.map((drone) => drone.id))];
|
|
25
|
-
if (uniqueDroneIds.length > 1) {
|
|
26
|
-
throw new Error(`Ambiguous direct-message recipient: ${recipient} matches multiple drones`);
|
|
27
|
-
}
|
|
28
|
-
if (uniqueDroneIds.length === 1) {
|
|
29
|
-
ids.add(uniqueDroneIds[0]);
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
const shortUuid = recipient.replace(/`/g, '').replace(/^id:/i, '').toLowerCase();
|
|
33
|
-
if (/^[0-9a-f]{8,}$/.test(shortUuid)) {
|
|
34
|
-
const prefixMatches = drones.filter((drone) => drone.id.toLowerCase().startsWith(shortUuid));
|
|
35
|
-
const uniquePrefixIds = [...new Set(prefixMatches.map((drone) => drone.id))];
|
|
36
|
-
if (uniquePrefixIds.length > 1) {
|
|
37
|
-
const listed = prefixMatches
|
|
38
|
-
.map((drone) => `${drone.id} (${drone.label ?? 'unlabeled'})`)
|
|
39
|
-
.join(', ');
|
|
40
|
-
throw new Error(`Ambiguous short-uuid recipient: ${recipient} matches multiple drones — ${listed}. ` +
|
|
41
|
-
'Address by the full drone id.');
|
|
42
|
-
}
|
|
43
|
-
if (uniquePrefixIds.length === 1) {
|
|
44
|
-
ids.add(uniquePrefixIds[0]);
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const roleMatches = roles.filter((role) => roleSlug(role.name) === roleSlug(recipient));
|
|
49
|
-
const uniqueRoleIds = [...new Set(roleMatches.map((role) => role.id))];
|
|
50
|
-
if (uniqueRoleIds.length > 1) {
|
|
51
|
-
throw new Error(`Ambiguous direct-message recipient: ${recipient} matches multiple roles`);
|
|
52
|
-
}
|
|
53
|
-
if (uniqueRoleIds.length === 0) {
|
|
54
|
-
throw new Error(`Unknown direct-message recipient: ${recipient}. ` +
|
|
55
|
-
'Use an exact drone label, drone id, role name, or role slug.');
|
|
56
|
-
}
|
|
57
|
-
const roleDrones = drones.filter((drone) => drone.role_id === uniqueRoleIds[0]);
|
|
58
|
-
if (roleDrones.length === 0) {
|
|
59
|
-
throw new Error(`Direct-message role recipient has no active drones: ${recipient}. ` +
|
|
60
|
-
'Use borg_roster to find active recipients, or omit to broadcast if your role can broadcast.');
|
|
61
|
-
}
|
|
62
|
-
for (const drone of roleDrones)
|
|
63
|
-
ids.add(drone.id);
|
|
64
|
-
}
|
|
65
|
-
return [...ids];
|
|
66
|
-
}
|
|
67
|
-
//# sourceMappingURL=local-log-routing.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"local-log-routing.js","sourceRoot":"","sources":["../src/local-log-routing.ts"],"names":[],"mappings":"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"}
|
package/src/local-log-routing.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { roleSlug } from './role-resolver.js';
|
|
2
|
-
|
|
3
|
-
export interface LocalRoutingDrone {
|
|
4
|
-
id: string;
|
|
5
|
-
label?: string | null;
|
|
6
|
-
role_id?: string | null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface LocalRoutingRole {
|
|
10
|
-
id: string;
|
|
11
|
-
name: string;
|
|
12
|
-
is_human_seat?: boolean;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Resolve the public `borg_log to:` addressing forms against the local
|
|
17
|
-
* server's authoritative cube roster. This intentionally mirrors the hosted
|
|
18
|
-
* route resolver: exact drone id/label first, then displayed short UUID, then
|
|
19
|
-
* role name/slug expansion. Every failure is closed before the log POST so a
|
|
20
|
-
* miss can never degrade into a broadcast.
|
|
21
|
-
*/
|
|
22
|
-
export function resolveLocalLogRecipients(
|
|
23
|
-
recipients: readonly string[],
|
|
24
|
-
drones: readonly LocalRoutingDrone[],
|
|
25
|
-
roles: readonly LocalRoutingRole[],
|
|
26
|
-
): string[] {
|
|
27
|
-
const ids = new Set<string>();
|
|
28
|
-
|
|
29
|
-
for (const recipient of recipients) {
|
|
30
|
-
if (recipient === '@human-seat') {
|
|
31
|
-
const roleIds = new Set(
|
|
32
|
-
roles.filter((role) => role.is_human_seat === true).map((role) => role.id),
|
|
33
|
-
);
|
|
34
|
-
const matches = drones.filter((drone) =>
|
|
35
|
-
drone.role_id !== null && drone.role_id !== undefined && roleIds.has(drone.role_id)
|
|
36
|
-
);
|
|
37
|
-
if (roleIds.size === 0 || matches.length === 0) {
|
|
38
|
-
throw new Error(
|
|
39
|
-
'Direct-message role recipient has no active drones: @human-seat. ' +
|
|
40
|
-
'Use borg_roster to find active recipients, or omit to broadcast if your role can broadcast.',
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
for (const drone of matches) ids.add(drone.id);
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const droneMatches = drones.filter(
|
|
48
|
-
(drone) => drone.id === recipient || drone.label === recipient,
|
|
49
|
-
);
|
|
50
|
-
const uniqueDroneIds = [...new Set(droneMatches.map((drone) => drone.id))];
|
|
51
|
-
if (uniqueDroneIds.length > 1) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
`Ambiguous direct-message recipient: ${recipient} matches multiple drones`,
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
if (uniqueDroneIds.length === 1) {
|
|
57
|
-
ids.add(uniqueDroneIds[0]!);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const shortUuid = recipient.replace(/`/g, '').replace(/^id:/i, '').toLowerCase();
|
|
62
|
-
if (/^[0-9a-f]{8,}$/.test(shortUuid)) {
|
|
63
|
-
const prefixMatches = drones.filter((drone) =>
|
|
64
|
-
drone.id.toLowerCase().startsWith(shortUuid)
|
|
65
|
-
);
|
|
66
|
-
const uniquePrefixIds = [...new Set(prefixMatches.map((drone) => drone.id))];
|
|
67
|
-
if (uniquePrefixIds.length > 1) {
|
|
68
|
-
const listed = prefixMatches
|
|
69
|
-
.map((drone) => `${drone.id} (${drone.label ?? 'unlabeled'})`)
|
|
70
|
-
.join(', ');
|
|
71
|
-
throw new Error(
|
|
72
|
-
`Ambiguous short-uuid recipient: ${recipient} matches multiple drones — ${listed}. ` +
|
|
73
|
-
'Address by the full drone id.',
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
if (uniquePrefixIds.length === 1) {
|
|
77
|
-
ids.add(uniquePrefixIds[0]!);
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const roleMatches = roles.filter((role) => roleSlug(role.name) === roleSlug(recipient));
|
|
83
|
-
const uniqueRoleIds = [...new Set(roleMatches.map((role) => role.id))];
|
|
84
|
-
if (uniqueRoleIds.length > 1) {
|
|
85
|
-
throw new Error(
|
|
86
|
-
`Ambiguous direct-message recipient: ${recipient} matches multiple roles`,
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
if (uniqueRoleIds.length === 0) {
|
|
90
|
-
throw new Error(
|
|
91
|
-
`Unknown direct-message recipient: ${recipient}. ` +
|
|
92
|
-
'Use an exact drone label, drone id, role name, or role slug.',
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
const roleDrones = drones.filter((drone) => drone.role_id === uniqueRoleIds[0]);
|
|
96
|
-
if (roleDrones.length === 0) {
|
|
97
|
-
throw new Error(
|
|
98
|
-
`Direct-message role recipient has no active drones: ${recipient}. ` +
|
|
99
|
-
'Use borg_roster to find active recipients, or omit to broadcast if your role can broadcast.',
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
for (const drone of roleDrones) ids.add(drone.id);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return [...ids];
|
|
106
|
-
}
|