borgmcp 1.0.6 → 1.0.8
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 +5 -3
- package/dist/assimilate-cmd.js +39 -511
- package/dist/assimilate-deps.js +3 -177
- package/dist/assimilate-welcome.js +2 -24
- package/dist/auth-env.js +1 -107
- package/dist/auth.js +23 -612
- package/dist/claude.js +11 -281
- package/dist/cli-help.js +29 -50
- package/dist/cli-platform.js +4 -94
- package/dist/codex-app-server.js +4 -228
- package/dist/codex-app-wake.js +2 -122
- package/dist/codex-launch.js +1 -81
- package/dist/codex-remote.js +1 -250
- package/dist/config-utils.js +3 -385
- package/dist/config.js +1 -190
- package/dist/console-prefix.js +1 -86
- package/dist/cube-name.js +1 -65
- package/dist/cubes.js +4 -269
- package/dist/debug.js +1 -71
- package/dist/device-auth.js +1 -167
- package/dist/direct-log.js +1 -11
- package/dist/health-beat.js +1 -168
- package/dist/inbox-monitor.js +1 -129
- package/dist/index.js +26 -1378
- package/dist/lifecycle-log-guard.js +2 -93
- package/dist/list-roles-render.js +6 -39
- package/dist/log-audit.js +3 -186
- package/dist/log-stream.js +9 -848
- package/dist/name-validator.js +1 -22
- package/dist/parse-assimilate-args.js +1 -82
- package/dist/postinstall.js +8 -22
- package/dist/regen-format.js +11 -337
- package/dist/regen.js +5 -83
- package/dist/remote-client.d.ts +4 -7
- package/dist/remote-client.js +1 -695
- package/dist/role-resolver.js +1 -36
- package/dist/role-section.js +8 -208
- package/dist/roster-render.js +3 -96
- package/dist/setup.js +41 -251
- package/dist/shell-escape.js +1 -22
- package/dist/spawn.js +10 -29
- package/dist/stale-version-check.js +1 -102
- package/dist/stream-owner.js +2 -202
- package/dist/stream-status.js +3 -211
- package/dist/subscription-retry.js +1 -23
- package/dist/sync-roles-render.js +3 -118
- package/dist/sync.js +22 -286
- package/dist/templates.js +120 -626
- package/dist/terminal-title.js +1 -68
- package/dist/token-crypto.js +1 -91
- package/dist/token-store.js +1 -222
- package/dist/types.d.ts +0 -5
- package/dist/types.js +0 -5
- package/dist/version.js +2 -78
- package/dist/worktree-lifecycle.js +2 -173
- package/package.json +12 -2
- package/dist/assimilate-cmd.d.ts.map +0 -1
- package/dist/assimilate-cmd.js.map +0 -1
- package/dist/assimilate-deps.d.ts.map +0 -1
- package/dist/assimilate-deps.js.map +0 -1
- package/dist/assimilate-welcome.d.ts.map +0 -1
- package/dist/assimilate-welcome.js.map +0 -1
- package/dist/auth-env.d.ts.map +0 -1
- package/dist/auth-env.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/claude.d.ts.map +0 -1
- package/dist/claude.js.map +0 -1
- package/dist/cli-help.d.ts.map +0 -1
- package/dist/cli-help.js.map +0 -1
- package/dist/cli-platform.d.ts.map +0 -1
- package/dist/cli-platform.js.map +0 -1
- package/dist/codex-app-server.d.ts.map +0 -1
- package/dist/codex-app-server.js.map +0 -1
- package/dist/codex-app-wake.d.ts.map +0 -1
- package/dist/codex-app-wake.js.map +0 -1
- package/dist/codex-launch.d.ts.map +0 -1
- package/dist/codex-launch.js.map +0 -1
- package/dist/codex-remote.d.ts.map +0 -1
- package/dist/codex-remote.js.map +0 -1
- package/dist/config-utils.d.ts.map +0 -1
- package/dist/config-utils.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/console-prefix.d.ts.map +0 -1
- package/dist/console-prefix.js.map +0 -1
- package/dist/cube-name.d.ts.map +0 -1
- package/dist/cube-name.js.map +0 -1
- package/dist/cubes.d.ts.map +0 -1
- package/dist/cubes.js.map +0 -1
- package/dist/debug.d.ts.map +0 -1
- package/dist/debug.js.map +0 -1
- package/dist/device-auth.d.ts.map +0 -1
- package/dist/device-auth.js.map +0 -1
- package/dist/direct-log.d.ts.map +0 -1
- package/dist/direct-log.js.map +0 -1
- package/dist/health-beat.d.ts.map +0 -1
- package/dist/health-beat.js.map +0 -1
- package/dist/inbox-monitor.d.ts.map +0 -1
- package/dist/inbox-monitor.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lifecycle-log-guard.d.ts.map +0 -1
- package/dist/lifecycle-log-guard.js.map +0 -1
- package/dist/list-roles-render.d.ts.map +0 -1
- package/dist/list-roles-render.js.map +0 -1
- package/dist/log-audit.d.ts.map +0 -1
- package/dist/log-audit.js.map +0 -1
- package/dist/log-stream.d.ts.map +0 -1
- package/dist/log-stream.js.map +0 -1
- package/dist/name-validator.d.ts.map +0 -1
- package/dist/name-validator.js.map +0 -1
- package/dist/parse-assimilate-args.d.ts.map +0 -1
- package/dist/parse-assimilate-args.js.map +0 -1
- package/dist/postinstall.d.ts.map +0 -1
- package/dist/postinstall.js.map +0 -1
- package/dist/regen-format.d.ts.map +0 -1
- package/dist/regen-format.js.map +0 -1
- package/dist/regen.d.ts.map +0 -1
- package/dist/regen.js.map +0 -1
- package/dist/remote-client.d.ts.map +0 -1
- package/dist/remote-client.js.map +0 -1
- package/dist/role-resolver.d.ts.map +0 -1
- package/dist/role-resolver.js.map +0 -1
- package/dist/role-section.d.ts.map +0 -1
- package/dist/role-section.js.map +0 -1
- package/dist/roster-render.d.ts.map +0 -1
- package/dist/roster-render.js.map +0 -1
- package/dist/setup.d.ts.map +0 -1
- package/dist/setup.js.map +0 -1
- package/dist/shell-escape.d.ts.map +0 -1
- package/dist/shell-escape.js.map +0 -1
- package/dist/spawn.d.ts.map +0 -1
- package/dist/spawn.js.map +0 -1
- package/dist/stale-version-check.d.ts.map +0 -1
- package/dist/stale-version-check.js.map +0 -1
- package/dist/stream-owner.d.ts.map +0 -1
- package/dist/stream-owner.js.map +0 -1
- package/dist/stream-status.d.ts.map +0 -1
- package/dist/stream-status.js.map +0 -1
- package/dist/subscription-retry.d.ts.map +0 -1
- package/dist/subscription-retry.js.map +0 -1
- package/dist/sync-roles-render.d.ts.map +0 -1
- package/dist/sync-roles-render.js.map +0 -1
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js.map +0 -1
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js.map +0 -1
- package/dist/terminal-title.d.ts.map +0 -1
- package/dist/terminal-title.js.map +0 -1
- package/dist/token-crypto.d.ts.map +0 -1
- package/dist/token-crypto.js.map +0 -1
- package/dist/token-store.d.ts.map +0 -1
- package/dist/token-store.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/dist/worktree-lifecycle.d.ts.map +0 -1
- package/dist/worktree-lifecycle.js.map +0 -1
package/dist/role-resolver.js
CHANGED
|
@@ -1,36 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Normalize a role-name argument or stored name into a slug used for
|
|
3
|
-
* both worktree-path construction and role lookup. Single source of
|
|
4
|
-
* truth so the path and the matched role always agree.
|
|
5
|
-
*
|
|
6
|
-
* Path-safe: strips characters outside `[a-z0-9-]` after the
|
|
7
|
-
* underscore/whitespace collapse. The no-arg path bypasses
|
|
8
|
-
* validateName (the matched role's name comes from the DB, which
|
|
9
|
-
* has no charset constraint on role names — CreateRoleSchema is
|
|
10
|
-
* `min(1).max(64)` only), so the safety has to live here.
|
|
11
|
-
*/
|
|
12
|
-
export function roleSlug(raw) {
|
|
13
|
-
return raw.toLowerCase().replace(/[_\s]+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Case- and separator-insensitive lookup against the cube's roles.
|
|
17
|
-
*/
|
|
18
|
-
export function matchRoleByName(roles, query) {
|
|
19
|
-
const slug = roleSlug(query);
|
|
20
|
-
return roles.find((r) => roleSlug(r.name) === slug);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Default-role picker for the no-arg case:
|
|
24
|
-
* - First drone in cube + a human-seat role exists → that role
|
|
25
|
-
* - Otherwise → the is_default role
|
|
26
|
-
* - Neither available → undefined (caller errors out)
|
|
27
|
-
*/
|
|
28
|
-
export function pickDefaultRole(roles, opts) {
|
|
29
|
-
if (opts.isFirstDrone) {
|
|
30
|
-
const humanSeat = roles.find((r) => r.is_human_seat);
|
|
31
|
-
if (humanSeat)
|
|
32
|
-
return humanSeat;
|
|
33
|
-
}
|
|
34
|
-
return roles.find((r) => r.is_default);
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=role-resolver.js.map
|
|
1
|
+
function a(e){return e.toLowerCase().replace(/[_\s]+/g,"-").replace(/[^a-z0-9-]/g,"")}function i(e,t){const n=a(t);return e.find(r=>a(r.name)===n)}function o(e,t){if(t.isFirstDrone){const n=e.find(r=>r.is_human_seat);if(n)return n}return e.find(n=>n.is_default)}export{i as matchRoleByName,o as pickDefaultRole,a as roleSlug};
|
package/dist/role-section.js
CHANGED
|
@@ -1,208 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
// The worker imports this via `../client/src/role-section` (mirrors the
|
|
11
|
-
// existing `workers/mcp-server.ts → ../client/src/templates` precedent). KEEP
|
|
12
|
-
// THIS MODULE SELF-CONTAINED (no runtime imports) so the worker bundle
|
|
13
|
-
// resolves it cleanly; the client tsconfig `rootDir: ./src` also requires the
|
|
14
|
-
// shared canonical to live under `client/src/`.
|
|
15
|
-
/**
|
|
16
|
-
* Test whether a single line is a plain-label section heading.
|
|
17
|
-
*
|
|
18
|
-
* Rules (deliberately narrow to avoid false positives against prose
|
|
19
|
-
* that merely contains a colon):
|
|
20
|
-
* - no leading whitespace (label lines start at column 0);
|
|
21
|
-
* - the line ends with exactly one `:`;
|
|
22
|
-
* - the text before the colon is non-empty, contains no other `:`,
|
|
23
|
-
* and is "short" (≤ 60 chars) — long colon-terminated prose lines
|
|
24
|
-
* are not labels;
|
|
25
|
-
* - the label is not a markdown emphasis run (does not start with
|
|
26
|
-
* `*`, `-`, `#`, `>` or backtick) — those are list/markdown lines,
|
|
27
|
-
* not plain labels (the woven `**Dense communication discipline:**`
|
|
28
|
-
* constant headings must NOT be treated as section boundaries).
|
|
29
|
-
*/
|
|
30
|
-
export function isLabelLine(line) {
|
|
31
|
-
// Leading whitespace disqualifies (label lines are flush-left).
|
|
32
|
-
if (/^\s/.test(line))
|
|
33
|
-
return false;
|
|
34
|
-
if (!line.endsWith(':'))
|
|
35
|
-
return false;
|
|
36
|
-
const label = line.slice(0, -1);
|
|
37
|
-
if (label.length === 0)
|
|
38
|
-
return false;
|
|
39
|
-
if (label.length > 60)
|
|
40
|
-
return false;
|
|
41
|
-
// Exactly one colon total (the trailing one).
|
|
42
|
-
if (label.includes(':'))
|
|
43
|
-
return false;
|
|
44
|
-
// Markdown/list/quote/code lead-ins are not plain labels.
|
|
45
|
-
if (/^[*\-#>`]/.test(label))
|
|
46
|
-
return false;
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Parse role `detailed_description` text into ordered sections.
|
|
51
|
-
*
|
|
52
|
-
* The result always round-trips byte-identical through
|
|
53
|
-
* `serializeSections`. An empty string yields a single empty preamble
|
|
54
|
-
* section so callers always have a stable shape to address.
|
|
55
|
-
*/
|
|
56
|
-
export function parseRoleSections(text) {
|
|
57
|
-
const sections = [];
|
|
58
|
-
const lines = text.split('\n');
|
|
59
|
-
// Reconstruct each line WITH its terminating newline so that the
|
|
60
|
-
// concatenation is lossless. `split('\n')` drops the separators; we
|
|
61
|
-
// re-append '\n' to every line except the last (which had no trailing
|
|
62
|
-
// newline in the source unless the source ended with one — in which
|
|
63
|
-
// case the final split element is '' and contributes nothing).
|
|
64
|
-
const lineWithSep = (idx) => idx < lines.length - 1 ? lines[idx] + '\n' : lines[idx];
|
|
65
|
-
let currentHeading = null;
|
|
66
|
-
let currentKind = 'preamble';
|
|
67
|
-
let currentBody = '';
|
|
68
|
-
let started = false;
|
|
69
|
-
const flush = () => {
|
|
70
|
-
sections.push({ heading: currentHeading, kind: currentKind, body: currentBody });
|
|
71
|
-
};
|
|
72
|
-
for (let i = 0; i < lines.length; i++) {
|
|
73
|
-
const raw = lines[i];
|
|
74
|
-
if (isLabelLine(raw)) {
|
|
75
|
-
// Close the in-progress section (preamble or previous label) and
|
|
76
|
-
// open a new label section headed by this line.
|
|
77
|
-
flush();
|
|
78
|
-
currentHeading = raw.slice(0, -1).trim();
|
|
79
|
-
currentKind = 'label';
|
|
80
|
-
currentBody = lineWithSep(i);
|
|
81
|
-
started = true;
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
currentBody += lineWithSep(i);
|
|
85
|
-
started = true;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// Always emit the final in-progress section. For empty input this is a
|
|
89
|
-
// single empty preamble (started === false, currentBody === '').
|
|
90
|
-
if (started || sections.length === 0) {
|
|
91
|
-
flush();
|
|
92
|
-
}
|
|
93
|
-
return sections;
|
|
94
|
-
}
|
|
95
|
-
/** Concatenate sections back into the original field text. */
|
|
96
|
-
export function serializeSections(sections) {
|
|
97
|
-
return sections.map((s) => s.body).join('');
|
|
98
|
-
}
|
|
99
|
-
function normalizeHeading(value) {
|
|
100
|
-
return value.trim().toLowerCase();
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Build the body for a freshly inserted/replaced label section from a
|
|
104
|
-
* heading + caller-supplied body text.
|
|
105
|
-
*
|
|
106
|
-
* The heading line is rendered as `<heading>:` on its own line. The
|
|
107
|
-
* caller's `body` is the text BELOW the heading. We guarantee the
|
|
108
|
-
* section body ends with a single trailing newline so adjacent sections
|
|
109
|
-
* stay separated when re-serialized (matching how real role text
|
|
110
|
-
* separates label sections with blank lines is the caller's choice via
|
|
111
|
-
* their body text; we only ensure the structural newline).
|
|
112
|
-
*/
|
|
113
|
-
function renderLabelSection(heading, body) {
|
|
114
|
-
const headingLine = `${heading.trim()}:\n`;
|
|
115
|
-
if (body === '')
|
|
116
|
-
return headingLine;
|
|
117
|
-
// Ensure the body ends with a newline so the next section's heading
|
|
118
|
-
// starts on its own line.
|
|
119
|
-
const normalizedBody = body.endsWith('\n') ? body : body + '\n';
|
|
120
|
-
return headingLine + normalizedBody;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Guarantee the section at `idx` ends in a newline before a new section
|
|
124
|
-
* heading is glued on after it.
|
|
125
|
-
*
|
|
126
|
-
* Real role `detailed_description` text (every template role) ends WITHOUT
|
|
127
|
-
* a trailing newline — it closes on the woven `${...DISCIPLINE}` constant.
|
|
128
|
-
* Appending a `<heading>:` line directly onto such text would fuse the
|
|
129
|
-
* heading onto the prior section's last line, so the heading is no longer
|
|
130
|
-
* a flush-left label and the inserted section is LOST on re-parse. Adding
|
|
131
|
-
* the structural newline here keeps the inserted heading on its own line.
|
|
132
|
-
*
|
|
133
|
-
* No-ops when the preceding body is empty (an empty preamble needs no
|
|
134
|
-
* separator) or already ends in '\n' (preserves the already-correct case
|
|
135
|
-
* byte-for-byte).
|
|
136
|
-
*/
|
|
137
|
-
function ensureTrailingNewline(sections, idx) {
|
|
138
|
-
if (idx < 0 || idx >= sections.length)
|
|
139
|
-
return;
|
|
140
|
-
const prev = sections[idx];
|
|
141
|
-
if (prev.body !== '' && !prev.body.endsWith('\n')) {
|
|
142
|
-
sections[idx] = { ...prev, body: prev.body + '\n' };
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Apply a single section-level patch to role text, returning the new
|
|
147
|
-
* full field text. Every other byte is preserved.
|
|
148
|
-
*
|
|
149
|
-
* - replace: overwrite the matched section's body with a re-rendered
|
|
150
|
-
* `<heading>:\n<body>` block. The heading match is case-insensitive
|
|
151
|
-
* on the label; the re-rendered heading uses the supplied casing.
|
|
152
|
-
* - insert: add a new label section. Placed after the section named by
|
|
153
|
-
* `after` (case-insensitive), or appended at the end when `after` is
|
|
154
|
-
* omitted/null. Rejects if a section with the same heading already
|
|
155
|
-
* exists (use replace instead).
|
|
156
|
-
* - delete: drop the matched section entirely.
|
|
157
|
-
*
|
|
158
|
-
* Throws when the target section is missing (replace/delete) or already
|
|
159
|
-
* present (insert), or when `after` does not resolve.
|
|
160
|
-
*/
|
|
161
|
-
export function patchRoleSectionText(text, op) {
|
|
162
|
-
const sections = parseRoleSections(text);
|
|
163
|
-
const targetKey = normalizeHeading(op.heading);
|
|
164
|
-
if (op.action === 'replace') {
|
|
165
|
-
const idx = sections.findIndex((s) => s.kind === 'label' && s.heading != null && normalizeHeading(s.heading) === targetKey);
|
|
166
|
-
if (idx === -1) {
|
|
167
|
-
throw new Error(`Role section "${op.heading}" not found. Use action="insert" to add it.`);
|
|
168
|
-
}
|
|
169
|
-
sections[idx] = {
|
|
170
|
-
heading: op.heading.trim(),
|
|
171
|
-
kind: 'label',
|
|
172
|
-
body: renderLabelSection(op.heading, op.body),
|
|
173
|
-
};
|
|
174
|
-
return serializeSections(sections);
|
|
175
|
-
}
|
|
176
|
-
if (op.action === 'delete') {
|
|
177
|
-
const idx = sections.findIndex((s) => s.kind === 'label' && s.heading != null && normalizeHeading(s.heading) === targetKey);
|
|
178
|
-
if (idx === -1) {
|
|
179
|
-
throw new Error(`Role section "${op.heading}" not found.`);
|
|
180
|
-
}
|
|
181
|
-
sections.splice(idx, 1);
|
|
182
|
-
return serializeSections(sections);
|
|
183
|
-
}
|
|
184
|
-
// insert
|
|
185
|
-
const exists = sections.some((s) => s.kind === 'label' && s.heading != null && normalizeHeading(s.heading) === targetKey);
|
|
186
|
-
if (exists) {
|
|
187
|
-
throw new Error(`Role section "${op.heading}" already exists. Use action="replace" to overwrite it.`);
|
|
188
|
-
}
|
|
189
|
-
const newSection = {
|
|
190
|
-
heading: op.heading.trim(),
|
|
191
|
-
kind: 'label',
|
|
192
|
-
body: renderLabelSection(op.heading, op.body),
|
|
193
|
-
};
|
|
194
|
-
if (op.after == null) {
|
|
195
|
-
ensureTrailingNewline(sections, sections.length - 1);
|
|
196
|
-
sections.push(newSection);
|
|
197
|
-
return serializeSections(sections);
|
|
198
|
-
}
|
|
199
|
-
const afterKey = normalizeHeading(op.after);
|
|
200
|
-
const afterIdx = sections.findIndex((s) => s.kind === 'label' && s.heading != null && normalizeHeading(s.heading) === afterKey);
|
|
201
|
-
if (afterIdx === -1) {
|
|
202
|
-
throw new Error(`Cannot insert after section "${op.after}" — it does not exist.`);
|
|
203
|
-
}
|
|
204
|
-
ensureTrailingNewline(sections, afterIdx);
|
|
205
|
-
sections.splice(afterIdx + 1, 0, newSection);
|
|
206
|
-
return serializeSections(sections);
|
|
207
|
-
}
|
|
208
|
-
//# sourceMappingURL=role-section.js.map
|
|
1
|
+
function b(t){if(/^\s/.test(t)||!t.endsWith(":"))return!1;const e=t.slice(0,-1);return!(e.length===0||e.length>60||e.includes(":")||/^[*\-#>`]/.test(e))}function w(t){const e=[],n=t.split(`
|
|
2
|
+
`),l=i=>i<n.length-1?n[i]+`
|
|
3
|
+
`:n[i];let f=null,s="preamble",d="",a=!1;const r=()=>{e.push({heading:f,kind:s,body:d})};for(let i=0;i<n.length;i++){const u=n[i];b(u)?(r(),f=u.slice(0,-1).trim(),s="label",d=l(i),a=!0):(d+=l(i),a=!0)}return(a||e.length===0)&&r(),e}function c(t){return t.map(e=>e.body).join("")}function o(t){return t.trim().toLowerCase()}function h(t,e){const n=`${t.trim()}:
|
|
4
|
+
`;if(e==="")return n;const l=e.endsWith(`
|
|
5
|
+
`)?e:e+`
|
|
6
|
+
`;return n+l}function g(t,e){if(e<0||e>=t.length)return;const n=t[e];n.body!==""&&!n.body.endsWith(`
|
|
7
|
+
`)&&(t[e]={...n,body:n.body+`
|
|
8
|
+
`})}function y(t,e){const n=w(t),l=o(e.heading);if(e.action==="replace"){const r=n.findIndex(i=>i.kind==="label"&&i.heading!=null&&o(i.heading)===l);if(r===-1)throw new Error(`Role section "${e.heading}" not found. Use action="insert" to add it.`);return n[r]={heading:e.heading.trim(),kind:"label",body:h(e.heading,e.body)},c(n)}if(e.action==="delete"){const r=n.findIndex(i=>i.kind==="label"&&i.heading!=null&&o(i.heading)===l);if(r===-1)throw new Error(`Role section "${e.heading}" not found.`);return n.splice(r,1),c(n)}if(n.some(r=>r.kind==="label"&&r.heading!=null&&o(r.heading)===l))throw new Error(`Role section "${e.heading}" already exists. Use action="replace" to overwrite it.`);const s={heading:e.heading.trim(),kind:"label",body:h(e.heading,e.body)};if(e.after==null)return g(n,n.length-1),n.push(s),c(n);const d=o(e.after),a=n.findIndex(r=>r.kind==="label"&&r.heading!=null&&o(r.heading)===d);if(a===-1)throw new Error(`Cannot insert after section "${e.after}" \u2014 it does not exist.`);return g(n,a),n.splice(a+1,0,s),c(n)}export{b as isLabelLine,w as parseRoleSections,y as patchRoleSectionText,c as serializeSections};
|
package/dist/roster-render.js
CHANGED
|
@@ -1,96 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Split out from `index.ts` so the T2.1 sender-side liveness probe
|
|
5
|
-
* (the `awake`/`stale-since-X` column rendering when a `since` arg is
|
|
6
|
-
* passed) can be unit-tested without spinning up the MCP server. Same
|
|
7
|
-
* pure-function + injected-`humanAgo` pattern as `stream-status.ts`.
|
|
8
|
-
*
|
|
9
|
-
* Two modes:
|
|
10
|
-
* - No `since` provided: classic roster — one line per drone with
|
|
11
|
-
* label, role, and `last seen` relative time.
|
|
12
|
-
* - `since` provided: each line additionally carries an `awake`
|
|
13
|
-
* marker if the drone's `last_seen` is after the resolved
|
|
14
|
-
* timestamp, otherwise a `stale-since-<relative>` marker derived
|
|
15
|
-
* from the resolved timestamp the server echoed back.
|
|
16
|
-
*/
|
|
17
|
-
/**
|
|
18
|
-
* gh#503 — render-layer display labels for the wake-path wire enum.
|
|
19
|
-
*
|
|
20
|
-
* The gh#406 SLI MEASURES a streak of unanswered directed challenges; it
|
|
21
|
-
* cannot confirm a genuinely broken wake path ("deaf") — a seat that is
|
|
22
|
-
* alive but idle-and-text-only looks identical past the SLA. So the
|
|
23
|
-
* roster shows the measured-confidence label, not the unconfirmed verdict:
|
|
24
|
-
* degraded (1 miss) → `unverified` (signal seen, not yet corroborated)
|
|
25
|
-
* deaf (2+ miss) → `unresponsive` (no response past the SLA)
|
|
26
|
-
*
|
|
27
|
-
* The wire enum VALUES (`live`/`degraded`/`deaf`), computed server-side by
|
|
28
|
-
* `wakePathFromStreak`, are intentionally UNCHANGED — this is a display-only
|
|
29
|
-
* relabel within gh#503's copy-only scope (renaming the enum would be a
|
|
30
|
-
* wire + schema change). An operator who wants a confirmed read cross-checks
|
|
31
|
-
* `borg:stream-status` on the affected seat. Unknown wire values fall back
|
|
32
|
-
* to the raw value so a newer server can't break an older client's roster.
|
|
33
|
-
*/
|
|
34
|
-
const WAKE_PATH_DISPLAY = {
|
|
35
|
-
degraded: 'unverified',
|
|
36
|
-
deaf: 'unresponsive',
|
|
37
|
-
};
|
|
38
|
-
export function formatRoleAgentLabel(roleName, agentKind) {
|
|
39
|
-
return agentKind ? `${roleName}, ${agentKind}` : roleName;
|
|
40
|
-
}
|
|
41
|
-
export function renderRoster(inputs) {
|
|
42
|
-
const { cubeName, drones, roles, resolvedSince, humanAgo } = inputs;
|
|
43
|
-
const roleById = new Map();
|
|
44
|
-
for (const r of roles)
|
|
45
|
-
roleById.set(r.id, r);
|
|
46
|
-
const lines = [];
|
|
47
|
-
lines.push(`# Drones in cube: ${cubeName}`);
|
|
48
|
-
lines.push('');
|
|
49
|
-
if (resolvedSince) {
|
|
50
|
-
// Surface the liveness-probe context so the reader knows what the
|
|
51
|
-
// `awake`/`stale-since-X` column is measured against.
|
|
52
|
-
lines.push(`_Liveness probe since ${resolvedSince} (${humanAgo(resolvedSince)}). \`awake\` = drone posted to the cube log after that point._`);
|
|
53
|
-
lines.push('');
|
|
54
|
-
}
|
|
55
|
-
if (!drones.length) {
|
|
56
|
-
lines.push('_(no drones connected)_');
|
|
57
|
-
return lines.join('\n');
|
|
58
|
-
}
|
|
59
|
-
for (const d of drones) {
|
|
60
|
-
const role = roleById.get(d.role_id);
|
|
61
|
-
const roleName = role?.name ?? 'unknown';
|
|
62
|
-
const roleLabel = formatRoleAgentLabel(roleName, d.agent_kind);
|
|
63
|
-
const lastSeen = humanAgo(d.last_seen);
|
|
64
|
-
const wakePathMarker = d.wake_path && d.wake_path !== 'live'
|
|
65
|
-
? ` · \`wake-path:${WAKE_PATH_DISPLAY[d.wake_path] ?? d.wake_path}\``
|
|
66
|
-
: '';
|
|
67
|
-
const wakePathClassMarker = d.wake_path_alert_class && d.wake_path_alert_class !== 'independent'
|
|
68
|
-
? ` · \`wake-path-class:${d.wake_path_alert_class}\``
|
|
69
|
-
: '';
|
|
70
|
-
const regenCountMarker = typeof d.regen_count === 'number' ? ` · \`regen-count:${d.regen_count}\`` : '';
|
|
71
|
-
if (resolvedSince) {
|
|
72
|
-
// T2.1 awake/stale column. `seen_since === true` → drone called a
|
|
73
|
-
// tool after the resolved timestamp; treat as awake. False or
|
|
74
|
-
// missing → stale. Missing should not happen when the server
|
|
75
|
-
// echoed a since, but defending against a shape mismatch is
|
|
76
|
-
// cheap and matches the renderer's no-server-assumptions
|
|
77
|
-
// discipline established in stream-status.ts.
|
|
78
|
-
//
|
|
79
|
-
// Per drone-4's polish NIT on the original T2.1 ship: the marker
|
|
80
|
-
// is just `stale` (not `stale-since-<relative>`) because the
|
|
81
|
-
// header already anchors the reference point and per-row "since
|
|
82
|
-
// when" would be identical across all stale rows in a single
|
|
83
|
-
// probe call — pure redundancy. The per-row `last seen X ago`
|
|
84
|
-
// field carries the diagnostic detail for "how stale is this
|
|
85
|
-
// particular drone."
|
|
86
|
-
const isAwake = d.seen_since === true;
|
|
87
|
-
const marker = isAwake ? '`awake`' : '`stale`';
|
|
88
|
-
lines.push(`- **${d.label}** (${roleLabel}) — last seen ${lastSeen} · ${marker}${regenCountMarker}${wakePathMarker}${wakePathClassMarker}`);
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
lines.push(`- **${d.label}** (${roleLabel}) — last seen ${lastSeen}${regenCountMarker}${wakePathMarker}${wakePathClassMarker}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return lines.join('\n');
|
|
95
|
-
}
|
|
96
|
-
//# sourceMappingURL=roster-render.js.map
|
|
1
|
+
const d={degraded:"unverified",deaf:"unresponsive"};function w(t,s){return s?`${t}, ${s}`:t}function b(t){const{cubeName:s,drones:o,roles:h,resolvedSince:a,humanAgo:r}=t,c=new Map;for(const e of h)c.set(e.id,e);const n=[];if(n.push(`# Drones in cube: ${s}`),n.push(""),a&&(n.push(`_Liveness probe since ${a} (${r(a)}). \`awake\` = drone posted to the cube log after that point._`),n.push("")),!o.length)return n.push("_(no drones connected)_"),n.join(`
|
|
2
|
+
`);for(const e of o){const k=c.get(e.role_id)?.name??"unknown",l=w(k,e.agent_kind),p=r(e.last_seen),u=e.wake_path&&e.wake_path!=="live"?` \xB7 \`wake-path:${d[e.wake_path]??e.wake_path}\``:"",_=e.wake_path_alert_class&&e.wake_path_alert_class!=="independent"?` \xB7 \`wake-path-class:${e.wake_path_alert_class}\``:"",i=typeof e.regen_count=="number"?` \xB7 \`regen-count:${e.regen_count}\``:"";if(a){const $=e.seen_since===!0?"`awake`":"`stale`";n.push(`- **${e.label}** (${l}) \u2014 last seen ${p} \xB7 ${$}${i}${u}${_}`)}else n.push(`- **${e.label}** (${l}) \u2014 last seen ${p}${i}${u}${_}`)}return n.join(`
|
|
3
|
+
`)}export{w as formatRoleAgentLabel,b as renderRoster};
|