borgmcp 1.0.5 → 1.0.7
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/dist/assimilate-cmd.js +39 -497
- 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 -329
- package/dist/regen.js +5 -83
- 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 +36 -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 -563
- package/dist/terminal-title.js +1 -68
- package/dist/token-crypto.js +1 -91
- package/dist/token-store.js +1 -222
- package/dist/types.js +0 -5
- package/dist/version.js +2 -78
- package/dist/worktree-lifecycle.js +2 -173
- package/package.json +11 -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};
|
package/dist/setup.js
CHANGED
|
@@ -1,252 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let codexCliPath = null;
|
|
39
|
-
try {
|
|
40
|
-
claudeCliPath = which.sync('claude');
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
// Optional: Borg can also run with Codex.
|
|
44
|
-
}
|
|
45
|
-
try {
|
|
46
|
-
codexCliPath = which.sync('codex');
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
// Optional: Borg can also run with Claude Code.
|
|
50
|
-
}
|
|
51
|
-
if (claudeCliPath)
|
|
52
|
-
console.log(chalk.gray(`Found Claude CLI: ${claudeCliPath}`));
|
|
53
|
-
if (codexCliPath)
|
|
54
|
-
console.log(chalk.gray(`Found Codex CLI: ${codexCliPath}`));
|
|
55
|
-
if (claudeCliPath || codexCliPath)
|
|
56
|
-
console.log('');
|
|
57
|
-
if (!claudeCliPath && !codexCliPath) {
|
|
58
|
-
console.error(chalk.red('◼ No supported agent CLI found\n'));
|
|
59
|
-
console.error(chalk.yellow('Please install Claude Code or Codex first:'));
|
|
60
|
-
console.error(chalk.gray(' Claude Code: https://claude.ai/download'));
|
|
61
|
-
console.error(chalk.gray(' Codex: https://developers.openai.com/codex\n'));
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
// Step 1: Configure every detected agent CLI. Idempotent; re-running
|
|
65
|
-
// setup is the normal path for OAuth refresh and CLI self-healing.
|
|
66
|
-
console.log(chalk.blue('◼ Agent CLI Integration'));
|
|
67
|
-
if (claudeCliPath) {
|
|
68
|
-
try {
|
|
69
|
-
if (!isMcpServerConfigured())
|
|
70
|
-
addMcpServer();
|
|
71
|
-
addSessionStartHook();
|
|
72
|
-
addUserPromptSubmitHook();
|
|
73
|
-
console.log(chalk.green('◼ borg configured for Claude Code'));
|
|
74
|
-
}
|
|
75
|
-
catch (error) {
|
|
76
|
-
console.error(chalk.red(`\n◼ Failed to configure Claude Code: ${error.message}\n`));
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (codexCliPath) {
|
|
81
|
-
try {
|
|
82
|
-
if (!isCodexMcpServerConfigured())
|
|
83
|
-
addCodexMcpServer();
|
|
84
|
-
addCodexSessionStartHook();
|
|
85
|
-
addCodexUserPromptSubmitHook();
|
|
86
|
-
console.log(chalk.green('◼ borg configured for Codex'));
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
console.error(chalk.red(`\n◼ Failed to configure Codex: ${error.message}\n`));
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
console.log('');
|
|
94
|
-
// Step 2: Authentication
|
|
95
|
-
console.log(chalk.blue('◼ Google Authentication'));
|
|
96
|
-
const authed = await isAuthenticated();
|
|
97
|
-
if (!authed) {
|
|
98
|
-
try {
|
|
99
|
-
await authenticateWithGoogle(noBrowser ? { noBrowser: true } : undefined);
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
console.error(chalk.red(`\n◼ Authentication failed: ${error.message}\n`));
|
|
103
|
-
// gh#557 NOTE-2: device-flow errors (access_denied / expired_token) and
|
|
104
|
-
// any other auth failure exit here — give the remote user a recovery
|
|
105
|
-
// path instead of a bare exit.
|
|
106
|
-
console.error(chalk.yellow('Re-run `borg setup` to try again.\n'));
|
|
107
|
-
process.exit(1);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
console.log(chalk.green('◼ Already authenticated\n'));
|
|
112
|
-
}
|
|
113
|
-
// Step 3: Subscription
|
|
114
|
-
console.log(chalk.blue('◼ Subscription Check'));
|
|
115
|
-
let status;
|
|
116
|
-
try {
|
|
117
|
-
status = await checkSubscriptionStatus();
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
console.error(chalk.red(`\n◼ Failed to check subscription: ${error.message}\n`));
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
// gh#521: a user who just subscribed via web hits propagation lag — retry a
|
|
124
|
-
// few times (non-alarmingly) before declaring no subscription, instead of
|
|
125
|
-
// flashing a scary "not found" immediately after payment.
|
|
126
|
-
status = await retrySubscriptionCheck(status, {
|
|
127
|
-
check: checkSubscriptionStatus,
|
|
128
|
-
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
129
|
-
onRetry: (attempt, total) => console.log(chalk.gray(`◼ Checking subscription... (attempt ${attempt}/${total})`)),
|
|
130
|
-
});
|
|
131
|
-
if (!status.hasAccess) {
|
|
132
|
-
// gh#687: a fresh user has no subscription BY DESIGN — the Free tier is the
|
|
133
|
-
// permanent entry point (no trial). Lead with that as a WIN, not a
|
|
134
|
-
// "not found" failure, and present upgrading as an OFFER (Continue on Free
|
|
135
|
-
// is the default). The gh#521 just-subscribed propagation-lag retry already
|
|
136
|
-
// ran above; the "I already subscribed — re-check" choice covers the tail.
|
|
137
|
-
console.log(chalk.green("◼ You're on the Free tier — permanent, no card needed: 1 cube + 3 drones + 100 req/hr."));
|
|
138
|
-
console.log(chalk.gray('◼ Start using borgmcp right now. Upgrade any time for unlimited cubes + drones ($1/cube/month).\n'));
|
|
139
|
-
const { subscribeMethod } = await prompts({
|
|
140
|
-
type: 'select',
|
|
141
|
-
name: 'subscribeMethod',
|
|
142
|
-
message: "You're ready on the Free tier. Want to do more?",
|
|
143
|
-
choices: [
|
|
144
|
-
{
|
|
145
|
-
title: '◼ Continue on the Free tier (recommended)',
|
|
146
|
-
value: 'skip',
|
|
147
|
-
description: 'Start now — 1 cube, 3 drones, 100 req/hr. No payment required.'
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
title: '◼ Upgrade to Cube tier — $1/cube/month',
|
|
151
|
-
value: 'web',
|
|
152
|
-
description: 'Unlimited cubes + drones + 1000 req/hr. Opens the subscribe page in your browser.'
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
title: '◼ Quick Stripe checkout',
|
|
156
|
-
value: 'stripe',
|
|
157
|
-
description: 'Fast upgrade checkout in the browser'
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
title: '◼ I already subscribed — re-check',
|
|
161
|
-
value: 'recheck',
|
|
162
|
-
description: 'Re-check now — a just-completed subscription can take a moment to activate'
|
|
163
|
-
}
|
|
164
|
-
]
|
|
165
|
-
});
|
|
166
|
-
switch (subscribeMethod) {
|
|
167
|
-
case 'web':
|
|
168
|
-
console.log(chalk.blue('\n◼ Opening: https://borgmcp.ai/subscribe'));
|
|
169
|
-
try {
|
|
170
|
-
await open('https://borgmcp.ai/subscribe');
|
|
171
|
-
console.log(chalk.gray('◼ Waiting for subscription (checking every 5s for 2 min)...\n'));
|
|
172
|
-
await pollForSubscription();
|
|
173
|
-
}
|
|
174
|
-
catch (error) {
|
|
175
|
-
console.error(chalk.yellow(`\n◼ ${error.message}`));
|
|
176
|
-
}
|
|
177
|
-
break;
|
|
178
|
-
case 'stripe':
|
|
179
|
-
try {
|
|
180
|
-
const checkoutUrl = await createSubscription();
|
|
181
|
-
console.log(chalk.blue(`\n◼ Opening Stripe: ${checkoutUrl}`));
|
|
182
|
-
await open(checkoutUrl);
|
|
183
|
-
console.log(chalk.gray('◼ Waiting for subscription...\n'));
|
|
184
|
-
await pollForSubscription();
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
console.error(chalk.red(`\n◼ Failed to create checkout: ${error.message}\n`));
|
|
188
|
-
}
|
|
189
|
-
break;
|
|
190
|
-
case 'recheck':
|
|
191
|
-
try {
|
|
192
|
-
const recheckStatus = await checkSubscriptionStatus();
|
|
193
|
-
if (recheckStatus.hasAccess) {
|
|
194
|
-
console.log(chalk.green('\n◼ Subscription found!\n'));
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
console.log(chalk.yellow('\n◼ No subscription found\n'));
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
console.error(chalk.red(`\n◼ Failed to recheck: ${error.message}\n`));
|
|
202
|
-
}
|
|
203
|
-
break;
|
|
204
|
-
case 'skip':
|
|
205
|
-
console.log(chalk.green("\n◼ You're all set on the Free tier: 1 cube, 3 drones, 100 req/hr.\n"));
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
console.log(chalk.green('◼ Active subscription found'));
|
|
211
|
-
if (status.expiresAt) {
|
|
212
|
-
const expiresAt = new Date(status.expiresAt);
|
|
213
|
-
console.log(chalk.gray(` Expires: ${expiresAt.toLocaleDateString()}\n`));
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
console.log('');
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
// Success message
|
|
220
|
-
console.log(chalk.green.bold('Setup complete!\n'));
|
|
221
|
-
console.log(chalk.yellow('🔄 Restart Claude Code/Codex (or open a new session) for the changes to take effect.\n'));
|
|
222
|
-
console.log(chalk.gray('◼ Next steps:'));
|
|
223
|
-
console.log(chalk.gray('1. Run "borg" to start Claude Code or Codex with your cube'));
|
|
224
|
-
console.log(chalk.gray('2. Manage cubes and subscription at https://borgmcp.ai/dashboard\n'));
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Poll for subscription activation
|
|
228
|
-
* Checks every 5 seconds for 2 minutes (24 attempts)
|
|
229
|
-
*/
|
|
230
|
-
async function pollForSubscription() {
|
|
231
|
-
const maxAttempts = 24;
|
|
232
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
233
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
234
|
-
try {
|
|
235
|
-
const status = await checkSubscriptionStatus();
|
|
236
|
-
if (status.hasAccess) {
|
|
237
|
-
console.log(chalk.green('◼ Subscription activated!\n'));
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
catch (error) {
|
|
242
|
-
// Continue polling even on errors
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
throw new Error('Timeout - Run setup again after subscribing');
|
|
246
|
-
}
|
|
247
|
-
// Run wizard
|
|
248
|
-
main().catch((error) => {
|
|
249
|
-
console.error(chalk.red(`\n◼ Setup failed: ${error.message}\n`));
|
|
250
|
-
process.exit(1);
|
|
251
|
-
});
|
|
252
|
-
//# sourceMappingURL=setup.js.map
|
|
2
|
+
import d from"prompts";import e from"chalk";import a from"open";import l from"which";import{authenticateWithGoogle as g}from"./auth.js";import{checkSubscriptionStatus as i,createSubscription as p}from"./remote-client.js";import{retrySubscriptionCheck as h}from"./subscription-retry.js";import{addMcpServer as b,addSessionStartHook as m,addUserPromptSubmitHook as f,addCodexMcpServer as y,addCodexSessionStartHook as w,addCodexUserPromptSubmitHook as C,isMcpServerConfigured as k,isCodexMcpServerConfigured as S}from"./config-utils.js";import{isAuthenticated as x}from"./config.js";import{handleVersionFlag as v}from"./version.js";import{initDebugFromArgv as $}from"./debug.js";async function A(){$(process.argv),v(),console.log(e.blue.bold(`
|
|
3
|
+
\u25FC Borg MCP Setup Wizard \u25FC`));const c=process.argv.includes("--no-browser")||process.argv.includes("--device");let s=null,t=null;try{s=l.sync("claude")}catch{}try{t=l.sync("codex")}catch{}if(s&&console.log(e.gray(`Found Claude CLI: ${s}`)),t&&console.log(e.gray(`Found Codex CLI: ${t}`)),(s||t)&&console.log(""),!s&&!t&&(console.error(e.red(`\u25FC No supported agent CLI found
|
|
4
|
+
`)),console.error(e.yellow("Please install Claude Code or Codex first:")),console.error(e.gray(" Claude Code: https://claude.ai/download")),console.error(e.gray(` Codex: https://developers.openai.com/codex
|
|
5
|
+
`)),process.exit(1)),console.log(e.blue("\u25FC Agent CLI Integration")),s)try{k()||b(),m(),f(),console.log(e.green("\u25FC borg configured for Claude Code"))}catch(o){console.error(e.red(`
|
|
6
|
+
\u25FC Failed to configure Claude Code: ${o.message}
|
|
7
|
+
`)),process.exit(1)}if(t)try{S()||y(),w(),C(),console.log(e.green("\u25FC borg configured for Codex"))}catch(o){console.error(e.red(`
|
|
8
|
+
\u25FC Failed to configure Codex: ${o.message}
|
|
9
|
+
`)),process.exit(1)}if(console.log(""),console.log(e.blue("\u25FC Google Authentication")),await x())console.log(e.green(`\u25FC Already authenticated
|
|
10
|
+
`));else try{await g(c?{noBrowser:!0}:void 0)}catch(o){console.error(e.red(`
|
|
11
|
+
\u25FC Authentication failed: ${o.message}
|
|
12
|
+
`)),console.error(e.yellow("Re-run `borg setup` to try again.\n")),process.exit(1)}console.log(e.blue("\u25FC Subscription Check"));let n;try{n=await i()}catch(o){console.error(e.red(`
|
|
13
|
+
\u25FC Failed to check subscription: ${o.message}
|
|
14
|
+
`)),process.exit(1)}if(n=await h(n,{check:i,sleep:o=>new Promise(r=>setTimeout(r,o)),onRetry:(o,r)=>console.log(e.gray(`\u25FC Checking subscription... (attempt ${o}/${r})`))}),n.hasAccess)if(console.log(e.green("\u25FC Active subscription found")),n.expiresAt){const o=new Date(n.expiresAt);console.log(e.gray(` Expires: ${o.toLocaleDateString()}
|
|
15
|
+
`))}else console.log("");else{console.log(e.green("\u25FC You're on the Free tier \u2014 permanent, no card needed: 1 cube + 3 drones + 100 req/hr.")),console.log(e.gray(`\u25FC Start using borgmcp right now. Upgrade any time for unlimited cubes + drones ($1/cube/month).
|
|
16
|
+
`));const{subscribeMethod:o}=await d({type:"select",name:"subscribeMethod",message:"You're ready on the Free tier. Want to do more?",choices:[{title:"\u25FC Continue on the Free tier (recommended)",value:"skip",description:"Start now \u2014 1 cube, 3 drones, 100 req/hr. No payment required."},{title:"\u25FC Upgrade to Cube tier \u2014 $1/cube/month",value:"web",description:"Unlimited cubes + drones + 1000 req/hr. Opens the subscribe page in your browser."},{title:"\u25FC Quick Stripe checkout",value:"stripe",description:"Fast upgrade checkout in the browser"},{title:"\u25FC I already subscribed \u2014 re-check",value:"recheck",description:"Re-check now \u2014 a just-completed subscription can take a moment to activate"}]});switch(o){case"web":console.log(e.blue(`
|
|
17
|
+
\u25FC Opening: https://borgmcp.ai/subscribe`));try{await a("https://borgmcp.ai/subscribe"),console.log(e.gray(`\u25FC Waiting for subscription (checking every 5s for 2 min)...
|
|
18
|
+
`)),await u()}catch(r){console.error(e.yellow(`
|
|
19
|
+
\u25FC ${r.message}`))}break;case"stripe":try{const r=await p();console.log(e.blue(`
|
|
20
|
+
\u25FC Opening Stripe: ${r}`)),await a(r),console.log(e.gray(`\u25FC Waiting for subscription...
|
|
21
|
+
`)),await u()}catch(r){console.error(e.red(`
|
|
22
|
+
\u25FC Failed to create checkout: ${r.message}
|
|
23
|
+
`))}break;case"recheck":try{(await i()).hasAccess?console.log(e.green(`
|
|
24
|
+
\u25FC Subscription found!
|
|
25
|
+
`)):console.log(e.yellow(`
|
|
26
|
+
\u25FC No subscription found
|
|
27
|
+
`))}catch(r){console.error(e.red(`
|
|
28
|
+
\u25FC Failed to recheck: ${r.message}
|
|
29
|
+
`))}break;case"skip":console.log(e.green(`
|
|
30
|
+
\u25FC You're all set on the Free tier: 1 cube, 3 drones, 100 req/hr.
|
|
31
|
+
`));break}}console.log(e.green.bold(`Setup complete!
|
|
32
|
+
`)),console.log(e.yellow(`\u{1F504} Restart Claude Code/Codex (or open a new session) for the changes to take effect.
|
|
33
|
+
`)),console.log(e.gray("\u25FC Next steps:")),console.log(e.gray('1. Run "borg" to start Claude Code or Codex with your cube')),console.log(e.gray(`2. Manage cubes and subscription at https://borgmcp.ai/dashboard
|
|
34
|
+
`))}async function u(){for(let s=0;s<24;s++){await new Promise(t=>setTimeout(t,5e3));try{if((await i()).hasAccess){console.log(e.green(`\u25FC Subscription activated!
|
|
35
|
+
`));return}}catch{}}throw new Error("Timeout - Run setup again after subscribing")}A().catch(c=>{console.error(e.red(`
|
|
36
|
+
\u25FC Setup failed: ${c.message}
|
|
37
|
+
`)),process.exit(1)});
|