borgmcp 3.17.0 → 4.0.1
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 +33 -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 +33 -14
- package/dist/regen-format.js.map +1 -1
- package/dist/remote-client.d.ts +10 -5
- package/dist/remote-client.d.ts.map +1 -1
- package/dist/remote-client.js +20 -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 +40 -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 +1 -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 +38 -12
- package/src/log-audit-core.ts +4 -2
- package/src/regen-format.ts +35 -14
- package/src/remote-client.ts +48 -54
- package/src/server-handshake.ts +1 -1
- package/src/tool-manifest.ts +41 -25
- package/src/tool-scope.ts +1 -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:
|
|
@@ -385,26 +402,29 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
385
402
|
{
|
|
386
403
|
name: 'borg_log',
|
|
387
404
|
description:
|
|
388
|
-
'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.',
|
|
389
406
|
inputSchema: {
|
|
390
407
|
type: 'object',
|
|
391
408
|
properties: {
|
|
392
409
|
message: { type: 'string', description: 'The log message (max 10KB).' },
|
|
393
410
|
to: {
|
|
394
|
-
|
|
395
|
-
|
|
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
|
+
],
|
|
396
421
|
description:
|
|
397
|
-
'
|
|
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.',
|
|
398
423
|
},
|
|
399
424
|
class: {
|
|
400
425
|
type: 'string',
|
|
401
426
|
description:
|
|
402
|
-
'Optional declared message class
|
|
403
|
-
},
|
|
404
|
-
visibility: {
|
|
405
|
-
type: 'string',
|
|
406
|
-
enum: ['broadcast', 'direct'],
|
|
407
|
-
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.',
|
|
408
428
|
},
|
|
409
429
|
documents: {
|
|
410
430
|
type: 'array',
|
|
@@ -415,7 +435,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
415
435
|
description: 'Optional full opaque ids of 1-100 same-cube documents to cite atomically. Unknown, duplicate, or foreign ids are refused.',
|
|
416
436
|
},
|
|
417
437
|
},
|
|
418
|
-
required: ['message'],
|
|
438
|
+
required: ['message', 'to'],
|
|
419
439
|
},
|
|
420
440
|
},
|
|
421
441
|
{
|
|
@@ -456,14 +476,12 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
456
476
|
cube_directive: { type: 'string', description: 'New cube directive markdown (optional).' },
|
|
457
477
|
message_taxonomy: {
|
|
458
478
|
type: 'array',
|
|
459
|
-
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.',
|
|
460
480
|
items: {
|
|
461
481
|
type: 'object',
|
|
462
482
|
properties: {
|
|
463
483
|
class: { type: 'string', description: 'Unique class name.' },
|
|
464
|
-
prefixes: { type: 'array', items: { type: 'string' }, description: 'Message prefixes
|
|
465
|
-
routing: { type: 'string', enum: ['broadcast', 'directed'], description: 'Routing mode.' },
|
|
466
|
-
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.' },
|
|
467
485
|
lifecycle: { type: 'string', enum: ['dispatch', 'completion'], description: 'Optional lifecycle marker for stuck-dispatch detection.' },
|
|
468
486
|
},
|
|
469
487
|
},
|
|
@@ -475,7 +493,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
475
493
|
{
|
|
476
494
|
name: 'borg_patch-taxonomy-class',
|
|
477
495
|
description:
|
|
478
|
-
"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.",
|
|
479
497
|
inputSchema: {
|
|
480
498
|
type: 'object',
|
|
481
499
|
properties: {
|
|
@@ -483,15 +501,13 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
483
501
|
action: { type: 'string', enum: ['add', 'replace', 'remove'], description: 'add / replace / remove a single class.' },
|
|
484
502
|
class_def: {
|
|
485
503
|
type: 'object',
|
|
486
|
-
description: 'The class definition (for add/replace). Shape: { class, prefixes?,
|
|
504
|
+
description: 'The class definition (for add/replace). Shape: { class, prefixes?, lifecycle? }.',
|
|
487
505
|
properties: {
|
|
488
506
|
class: { type: 'string', description: 'Unique class name.' },
|
|
489
|
-
prefixes: { type: 'array', items: { type: 'string' }, description: 'Message prefixes
|
|
490
|
-
routing: { type: 'string', enum: ['broadcast', 'directed'], description: 'Routing mode.' },
|
|
491
|
-
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.' },
|
|
492
508
|
lifecycle: { type: 'string', enum: ['dispatch', 'completion'], description: 'Optional lifecycle marker for stuck-dispatch detection.' },
|
|
493
509
|
},
|
|
494
|
-
required: ['class'
|
|
510
|
+
required: ['class'],
|
|
495
511
|
},
|
|
496
512
|
class: { type: 'string', description: 'For remove only: the name of the class to drop (case-insensitive).' },
|
|
497
513
|
},
|
|
@@ -524,7 +540,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
524
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.' },
|
|
525
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.' },
|
|
526
542
|
can_broadcast: { type: 'boolean', description: 'If true, drones in this role may post broadcast log entries when strict broadcast gating is enabled.' },
|
|
527
|
-
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.' },
|
|
528
544
|
},
|
|
529
545
|
required: ['cube_id', 'name', 'short_description', 'detailed_description'],
|
|
530
546
|
},
|
|
@@ -543,7 +559,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
543
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.' },
|
|
544
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).' },
|
|
545
561
|
can_broadcast: { type: 'boolean', description: 'Set true/false to allow or deny broadcast log entries when strict broadcast gating is enabled.' },
|
|
546
|
-
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.' },
|
|
547
563
|
},
|
|
548
564
|
required: ['role_id'],
|
|
549
565
|
},
|
|
@@ -566,7 +582,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
566
582
|
},
|
|
567
583
|
{
|
|
568
584
|
name: 'borg_delete-role',
|
|
569
|
-
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.',
|
|
570
586
|
inputSchema: {
|
|
571
587
|
type: 'object',
|
|
572
588
|
properties: {
|
package/src/tool-scope.ts
CHANGED
|
@@ -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
|
-
}
|