ftown 0.25.2 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/command-rpc.d.ts +3 -0
- package/dist/command-rpc.js +41 -0
- package/dist/command-rpc.js.map +1 -1
- package/dist/coordination/board.d.ts +12 -0
- package/dist/coordination/board.js +240 -0
- package/dist/coordination/board.js.map +1 -0
- package/dist/coordination/card-store.d.ts +9 -0
- package/dist/coordination/card-store.js +188 -0
- package/dist/coordination/card-store.js.map +1 -0
- package/dist/coordination/contract.d.ts +236 -0
- package/dist/coordination/contract.js +63 -0
- package/dist/coordination/contract.js.map +1 -0
- package/dist/coordination/format.d.ts +5 -0
- package/dist/coordination/format.js +60 -0
- package/dist/coordination/format.js.map +1 -0
- package/dist/coordination/tree-events.d.ts +31 -0
- package/dist/coordination/tree-events.js +226 -0
- package/dist/coordination/tree-events.js.map +1 -0
- package/dist/coordination/wiring.d.ts +48 -0
- package/dist/coordination/wiring.js +143 -0
- package/dist/coordination/wiring.js.map +1 -0
- package/dist/coordination-cli.d.ts +34 -0
- package/dist/coordination-cli.js +460 -0
- package/dist/coordination-cli.js.map +1 -0
- package/dist/ftown-cli.js +12 -2
- package/dist/ftown-cli.js.map +1 -1
- package/dist/index.js +33 -7
- package/dist/index.js.map +1 -1
- package/dist/local-api-server.d.ts +6 -1
- package/dist/local-api-server.js +282 -5
- package/dist/local-api-server.js.map +1 -1
- package/dist/mail-delivery.d.ts +16 -2
- package/dist/mail-delivery.js +331 -77
- package/dist/mail-delivery.js.map +1 -1
- package/dist/mail-store.d.ts +7 -0
- package/dist/mail-store.js +71 -1
- package/dist/mail-store.js.map +1 -1
- package/dist/pull-request-events.d.ts +5 -0
- package/dist/pull-request-events.js +21 -1
- package/dist/pull-request-events.js.map +1 -1
- package/dist/quota/agy.d.ts +16 -0
- package/dist/quota/agy.js +287 -0
- package/dist/quota/agy.js.map +1 -0
- package/dist/quota/annotate.d.ts +5 -0
- package/dist/quota/annotate.js +56 -0
- package/dist/quota/annotate.js.map +1 -0
- package/dist/quota/claude.d.ts +27 -0
- package/dist/quota/claude.js +265 -0
- package/dist/quota/claude.js.map +1 -0
- package/dist/quota/codex.d.ts +25 -0
- package/dist/quota/codex.js +378 -0
- package/dist/quota/codex.js.map +1 -0
- package/dist/quota/format.d.ts +6 -0
- package/dist/quota/format.js +113 -0
- package/dist/quota/format.js.map +1 -0
- package/dist/quota/grok.d.ts +97 -0
- package/dist/quota/grok.js +563 -0
- package/dist/quota/grok.js.map +1 -0
- package/dist/quota/models-agy.d.ts +10 -0
- package/dist/quota/models-agy.js +144 -0
- package/dist/quota/models-agy.js.map +1 -0
- package/dist/quota/models-claude.d.ts +10 -0
- package/dist/quota/models-claude.js +227 -0
- package/dist/quota/models-claude.js.map +1 -0
- package/dist/quota/models-codex.d.ts +18 -0
- package/dist/quota/models-codex.js +305 -0
- package/dist/quota/models-codex.js.map +1 -0
- package/dist/quota/models-grok.d.ts +19 -0
- package/dist/quota/models-grok.js +272 -0
- package/dist/quota/models-grok.js.map +1 -0
- package/dist/quota/models-muse.d.ts +13 -0
- package/dist/quota/models-muse.js +200 -0
- package/dist/quota/models-muse.js.map +1 -0
- package/dist/quota/models-opencode.d.ts +51 -0
- package/dist/quota/models-opencode.js +400 -0
- package/dist/quota/models-opencode.js.map +1 -0
- package/dist/quota/muse.d.ts +38 -0
- package/dist/quota/muse.js +225 -0
- package/dist/quota/muse.js.map +1 -0
- package/dist/quota/opencode.d.ts +7 -0
- package/dist/quota/opencode.js +47 -0
- package/dist/quota/opencode.js.map +1 -0
- package/dist/quota/routing-config.d.ts +18 -0
- package/dist/quota/routing-config.js +438 -0
- package/dist/quota/routing-config.js.map +1 -0
- package/dist/quota/routing-service.d.ts +24 -0
- package/dist/quota/routing-service.js +118 -0
- package/dist/quota/routing-service.js.map +1 -0
- package/dist/quota/routing.d.ts +10 -0
- package/dist/quota/routing.js +180 -0
- package/dist/quota/routing.js.map +1 -0
- package/dist/quota/types.d.ts +304 -0
- package/dist/quota/types.js +45 -0
- package/dist/quota/types.js.map +1 -0
- package/dist/quota-cli.d.ts +80 -0
- package/dist/quota-cli.js +462 -0
- package/dist/quota-cli.js.map +1 -0
- package/dist/security/bridge/permissions.js +24 -2
- package/dist/security/bridge/permissions.js.map +1 -1
- package/dist/types.d.ts +7 -3
- package/dist/wire-types.d.ts +17 -1
- package/dist/wire-types.js +1 -1
- package/dist/wire-types.js.map +1 -1
- package/package.json +3 -2
- package/skills/ftown/SKILL.md +31 -5
- package/skills/ftown/references/orchestrator.md +17 -7
- package/skills/ftown/references/quota.md +177 -0
- package/skills/ftown/references/sessions.md +45 -3
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ftown status | board | events` — coordination phase-A CLI.
|
|
3
|
+
* Thin client over the local coordination API (see coordination/contract.ts).
|
|
4
|
+
*/
|
|
5
|
+
import { Command, CommanderError } from 'commander';
|
|
6
|
+
import { api as request, loadPointer } from './cli-client.js';
|
|
7
|
+
import { BOARD_WAIT_CONDITIONS, CARD_LIMITS, CARD_STATES, COORDINATION_ROUTES, } from './coordination/contract.js';
|
|
8
|
+
import { formatBoard, formatBoardRow, formatEvent } from './coordination/format.js';
|
|
9
|
+
export const WAIT_DEFAULT_TIMEOUT_SECONDS = 300;
|
|
10
|
+
export const WAIT_MAX_TIMEOUT_SECONDS = 300;
|
|
11
|
+
const EVENTS_WAIT_CHUNK_SECONDS = 300;
|
|
12
|
+
const STATUS_SET_USAGE = 'Usage: ftown status set [--state <s>] [--headline <t>] [--progress <d>/<t>|--no-progress] ' +
|
|
13
|
+
'[--on human|parent|external|session:<id>] [--text <t>] [--next <t>|--no-next] [--session-id <id>]';
|
|
14
|
+
/** Usage problem: message printed, exit code 2. */
|
|
15
|
+
export class UsageError extends Error {
|
|
16
|
+
usage;
|
|
17
|
+
constructor(message, usage) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.usage = usage;
|
|
20
|
+
this.name = 'UsageError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Thrown after output has been printed, to carry a non-zero exit code. */
|
|
24
|
+
class ExitError extends Error {
|
|
25
|
+
code;
|
|
26
|
+
constructor(code) {
|
|
27
|
+
super(`exit ${code}`);
|
|
28
|
+
this.code = code;
|
|
29
|
+
this.name = 'ExitError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function defaultApi(method, path, body) {
|
|
33
|
+
return request(loadPointer(), method, path, body);
|
|
34
|
+
}
|
|
35
|
+
function resolveDeps(partial) {
|
|
36
|
+
return {
|
|
37
|
+
api: partial?.api ?? defaultApi,
|
|
38
|
+
now: partial?.now ?? (() => Date.now()),
|
|
39
|
+
env: partial?.env ?? process.env,
|
|
40
|
+
log: partial?.log ?? ((text) => console.log(text)),
|
|
41
|
+
errorLog: partial?.errorLog ?? ((text) => console.error(text)),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function requireSessionId(explicit, env) {
|
|
45
|
+
const id = (explicit ?? env.FTOWN_SESSION_ID)?.trim();
|
|
46
|
+
if (!id || !/^[A-Za-z0-9_-]{1,200}$/.test(id)) {
|
|
47
|
+
throw new UsageError('Supply a valid --session-id or set FTOWN_SESSION_ID');
|
|
48
|
+
}
|
|
49
|
+
return id;
|
|
50
|
+
}
|
|
51
|
+
function cardPath(sessionId) {
|
|
52
|
+
return COORDINATION_ROUTES.card.replace(':id', encodeURIComponent(sessionId));
|
|
53
|
+
}
|
|
54
|
+
function boardPath(sessionId, query) {
|
|
55
|
+
const params = new URLSearchParams();
|
|
56
|
+
if (query.since !== undefined)
|
|
57
|
+
params.set('since', String(query.since));
|
|
58
|
+
if (query.ready)
|
|
59
|
+
params.set('ready', '1');
|
|
60
|
+
if (query.all)
|
|
61
|
+
params.set('all', '1');
|
|
62
|
+
const qs = params.toString();
|
|
63
|
+
const base = COORDINATION_ROUTES.board.replace(':id', encodeURIComponent(sessionId));
|
|
64
|
+
return qs ? `${base}?${qs}` : base;
|
|
65
|
+
}
|
|
66
|
+
function eventsPath(sessionId, query) {
|
|
67
|
+
const params = new URLSearchParams();
|
|
68
|
+
if (query.since !== undefined)
|
|
69
|
+
params.set('since', String(query.since));
|
|
70
|
+
if (query.limit !== undefined)
|
|
71
|
+
params.set('limit', String(query.limit));
|
|
72
|
+
if (query.waitSeconds !== undefined)
|
|
73
|
+
params.set('waitSeconds', String(query.waitSeconds));
|
|
74
|
+
const qs = params.toString();
|
|
75
|
+
const base = COORDINATION_ROUTES.events.replace(':id', encodeURIComponent(sessionId));
|
|
76
|
+
return qs ? `${base}?${qs}` : base;
|
|
77
|
+
}
|
|
78
|
+
// ───────────────────────── Flag parsing (pure) ─────────────────────────
|
|
79
|
+
export function parseProgress(value) {
|
|
80
|
+
const match = /^(\d+)\/(\d+)$/.exec(value);
|
|
81
|
+
if (!match) {
|
|
82
|
+
throw new UsageError(`--progress must be <done>/<total> (got "${value}")`);
|
|
83
|
+
}
|
|
84
|
+
const done = Number(match[1]);
|
|
85
|
+
const total = Number(match[2]);
|
|
86
|
+
if (total < 1 || total > CARD_LIMITS.progressTotal || done > total) {
|
|
87
|
+
throw new UsageError(`--progress must satisfy 0 <= done <= total and 1 <= total <= ${CARD_LIMITS.progressTotal} (got "${value}")`);
|
|
88
|
+
}
|
|
89
|
+
return { done, total };
|
|
90
|
+
}
|
|
91
|
+
function parseBlockedOn(on, text) {
|
|
92
|
+
if (on === 'human' || on === 'parent' || on === 'external') {
|
|
93
|
+
return { kind: on, text };
|
|
94
|
+
}
|
|
95
|
+
if (on.startsWith('session:')) {
|
|
96
|
+
const ref = on.slice('session:'.length);
|
|
97
|
+
if (!/^[A-Za-z0-9_-]{1,200}$/.test(ref)) {
|
|
98
|
+
throw new UsageError('--on session:<id> requires a valid session id');
|
|
99
|
+
}
|
|
100
|
+
return { kind: 'session', ref, text };
|
|
101
|
+
}
|
|
102
|
+
throw new UsageError('--on must be human, parent, external or session:<id>');
|
|
103
|
+
}
|
|
104
|
+
export function buildCardPatch(flags) {
|
|
105
|
+
const patch = {};
|
|
106
|
+
let touched = false;
|
|
107
|
+
if (flags.state !== undefined) {
|
|
108
|
+
if (!CARD_STATES.includes(flags.state)) {
|
|
109
|
+
throw new UsageError(`--state must be one of: ${CARD_STATES.join(', ')}`);
|
|
110
|
+
}
|
|
111
|
+
patch.state = flags.state;
|
|
112
|
+
touched = true;
|
|
113
|
+
}
|
|
114
|
+
if (flags.headline !== undefined) {
|
|
115
|
+
patch.headline = flags.headline;
|
|
116
|
+
touched = true;
|
|
117
|
+
}
|
|
118
|
+
if (typeof flags.progress === 'string') {
|
|
119
|
+
patch.progress = parseProgress(flags.progress);
|
|
120
|
+
touched = true;
|
|
121
|
+
}
|
|
122
|
+
else if (flags.progress === false) {
|
|
123
|
+
patch.progress = null;
|
|
124
|
+
touched = true;
|
|
125
|
+
}
|
|
126
|
+
if (flags.on !== undefined) {
|
|
127
|
+
if (flags.text === undefined)
|
|
128
|
+
throw new UsageError('--on requires --text');
|
|
129
|
+
patch.blockedOn = parseBlockedOn(flags.on, flags.text);
|
|
130
|
+
touched = true;
|
|
131
|
+
}
|
|
132
|
+
else if (flags.text !== undefined) {
|
|
133
|
+
throw new UsageError('--text requires --on');
|
|
134
|
+
}
|
|
135
|
+
if (typeof flags.next === 'string') {
|
|
136
|
+
patch.next = flags.next;
|
|
137
|
+
touched = true;
|
|
138
|
+
}
|
|
139
|
+
else if (flags.next === false) {
|
|
140
|
+
patch.next = null;
|
|
141
|
+
touched = true;
|
|
142
|
+
}
|
|
143
|
+
if (!touched) {
|
|
144
|
+
throw new UsageError('Nothing to set: supply at least one card field', STATUS_SET_USAGE);
|
|
145
|
+
}
|
|
146
|
+
return patch;
|
|
147
|
+
}
|
|
148
|
+
/** Pure wait-condition evaluation over a fetched board and the events that arrived. */
|
|
149
|
+
export function evaluateWaitCondition(condition, board, events) {
|
|
150
|
+
switch (condition) {
|
|
151
|
+
case 'any-change':
|
|
152
|
+
return events.length > 0;
|
|
153
|
+
case 'any-done':
|
|
154
|
+
return board.cards.some((card) => card.declared?.state === 'done');
|
|
155
|
+
case 'any-blocked':
|
|
156
|
+
return board.cards.some((card) => card.declared?.state === 'blocked');
|
|
157
|
+
case 'all-done':
|
|
158
|
+
return board.cards.length > 0 && board.cards.every((card) => card.declared?.state === 'done');
|
|
159
|
+
case 'any-ready':
|
|
160
|
+
return board.cards.some((card) => card.derived.pr?.state === 'passed' && card.declared?.state !== 'done');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function parseSince(value) {
|
|
164
|
+
if (value === undefined)
|
|
165
|
+
return undefined;
|
|
166
|
+
if (!/^\d{1,12}$/.test(value))
|
|
167
|
+
throw new UsageError('--since must be a non-negative integer');
|
|
168
|
+
return Number(value);
|
|
169
|
+
}
|
|
170
|
+
function parseLimit(value) {
|
|
171
|
+
if (value === undefined)
|
|
172
|
+
return undefined;
|
|
173
|
+
if (!/^\d+$/.test(value))
|
|
174
|
+
throw new UsageError('--limit must be a whole number between 1 and 500');
|
|
175
|
+
const limit = Number(value);
|
|
176
|
+
if (limit < 1 || limit > 500)
|
|
177
|
+
throw new UsageError('--limit must be between 1 and 500');
|
|
178
|
+
return limit;
|
|
179
|
+
}
|
|
180
|
+
function parseTimeoutSeconds(value) {
|
|
181
|
+
if (value === undefined)
|
|
182
|
+
return WAIT_DEFAULT_TIMEOUT_SECONDS;
|
|
183
|
+
if (!/^\d+$/.test(value)) {
|
|
184
|
+
throw new UsageError(`--timeout must be a whole number of seconds between 1 and ${WAIT_MAX_TIMEOUT_SECONDS}`);
|
|
185
|
+
}
|
|
186
|
+
const seconds = Number(value);
|
|
187
|
+
if (seconds < 1 || seconds > WAIT_MAX_TIMEOUT_SECONDS) {
|
|
188
|
+
throw new UsageError(`--timeout must be between 1 and ${WAIT_MAX_TIMEOUT_SECONDS} seconds`);
|
|
189
|
+
}
|
|
190
|
+
return seconds;
|
|
191
|
+
}
|
|
192
|
+
function parseWaitCondition(value) {
|
|
193
|
+
if (!BOARD_WAIT_CONDITIONS.includes(value)) {
|
|
194
|
+
throw new UsageError(`--until must be one of: ${BOARD_WAIT_CONDITIONS.join(', ')}`);
|
|
195
|
+
}
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
// ───────────────────────── Output helpers ─────────────────────────
|
|
199
|
+
function printCard(deps, card, json) {
|
|
200
|
+
if (json) {
|
|
201
|
+
deps.log(JSON.stringify(card, null, 2));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
deps.log(formatBoardRow(card, deps.now()));
|
|
205
|
+
if (card.declared?.next)
|
|
206
|
+
deps.log(`next: ${card.declared.next}`);
|
|
207
|
+
}
|
|
208
|
+
function printBoard(deps, board, json) {
|
|
209
|
+
if (json) {
|
|
210
|
+
deps.log(JSON.stringify(board, null, 2));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
deps.log(formatBoard(board, deps.now()));
|
|
214
|
+
deps.log(`cursor ${board.cursor}`);
|
|
215
|
+
}
|
|
216
|
+
function printEvents(deps, page, json) {
|
|
217
|
+
if (json) {
|
|
218
|
+
deps.log(JSON.stringify(page, null, 2));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
for (const event of page.events)
|
|
222
|
+
deps.log(formatEvent(event));
|
|
223
|
+
deps.log(`cursor ${page.cursor}`);
|
|
224
|
+
}
|
|
225
|
+
async function fetchBoard(deps, sessionId, query) {
|
|
226
|
+
return (await deps.api('GET', boardPath(sessionId, query)));
|
|
227
|
+
}
|
|
228
|
+
async function fetchEventsPage(deps, sessionId, query) {
|
|
229
|
+
return (await deps.api('GET', eventsPath(sessionId, query)));
|
|
230
|
+
}
|
|
231
|
+
async function getStatus(deps, opts) {
|
|
232
|
+
const sessionId = requireSessionId(opts.sessionId, deps.env);
|
|
233
|
+
const card = (await deps.api('GET', cardPath(sessionId)));
|
|
234
|
+
printCard(deps, card, opts.json === true);
|
|
235
|
+
}
|
|
236
|
+
async function setStatus(deps, opts) {
|
|
237
|
+
const sessionId = requireSessionId(opts.sessionId, deps.env);
|
|
238
|
+
const patch = buildCardPatch(opts);
|
|
239
|
+
const card = (await deps.api('PUT', cardPath(sessionId), patch));
|
|
240
|
+
printCard(deps, card, opts.json === true);
|
|
241
|
+
}
|
|
242
|
+
async function clearStatus(deps, opts) {
|
|
243
|
+
const sessionId = requireSessionId(opts.sessionId, deps.env);
|
|
244
|
+
const data = await deps.api('DELETE', cardPath(sessionId));
|
|
245
|
+
if (opts.json === true)
|
|
246
|
+
deps.log(JSON.stringify(data, null, 2));
|
|
247
|
+
else
|
|
248
|
+
deps.log(`card cleared for ${sessionId}`);
|
|
249
|
+
}
|
|
250
|
+
function statusProgram(deps) {
|
|
251
|
+
const program = new Command()
|
|
252
|
+
.name('ftown status')
|
|
253
|
+
.description("Show or update this session's status card");
|
|
254
|
+
program.exitOverride();
|
|
255
|
+
program.configureOutput({
|
|
256
|
+
writeOut: (text) => deps.log(text.trimEnd()),
|
|
257
|
+
writeErr: (text) => deps.errorLog(text.trimEnd()),
|
|
258
|
+
});
|
|
259
|
+
program
|
|
260
|
+
.option('--json', 'Machine-readable JSON output')
|
|
261
|
+
.option('--plain', 'Human-readable output (default)')
|
|
262
|
+
.option('--session-id <id>', 'Target session; defaults to FTOWN_SESSION_ID')
|
|
263
|
+
.action(async (opts) => {
|
|
264
|
+
await getStatus(deps, opts);
|
|
265
|
+
});
|
|
266
|
+
program
|
|
267
|
+
.command('set')
|
|
268
|
+
.description('Update the status card')
|
|
269
|
+
.option('--state <s>', `Card state: ${CARD_STATES.join(' | ')}`)
|
|
270
|
+
.option('--headline <t>', 'One-line headline')
|
|
271
|
+
.option('--progress <done/total>', 'Progress, e.g. 3/7')
|
|
272
|
+
.option('--no-progress', 'Clear progress')
|
|
273
|
+
.option('--on <kind>', 'Blocked on: human | parent | external | session:<id>')
|
|
274
|
+
.option('--text <t>', 'Blocked-on detail (required with --on)')
|
|
275
|
+
.option('--next <t>', 'Next step')
|
|
276
|
+
.option('--no-next', 'Clear next step')
|
|
277
|
+
.option('--json', 'Machine-readable JSON output')
|
|
278
|
+
.option('--plain', 'Human-readable output (default)')
|
|
279
|
+
.option('--session-id <id>', 'Target session; defaults to FTOWN_SESSION_ID')
|
|
280
|
+
.action(async (opts) => {
|
|
281
|
+
await setStatus(deps, opts);
|
|
282
|
+
});
|
|
283
|
+
program
|
|
284
|
+
.command('clear')
|
|
285
|
+
.description('Clear the status card')
|
|
286
|
+
.option('--json', 'Machine-readable JSON output')
|
|
287
|
+
.option('--plain', 'Human-readable output (default)')
|
|
288
|
+
.option('--session-id <id>', 'Target session; defaults to FTOWN_SESSION_ID')
|
|
289
|
+
.action(async (opts) => {
|
|
290
|
+
await clearStatus(deps, opts);
|
|
291
|
+
});
|
|
292
|
+
return program;
|
|
293
|
+
}
|
|
294
|
+
async function waitForBoard(deps, sessionId, query, condition, timeoutSeconds, json) {
|
|
295
|
+
// Conditions are evaluated only on events past the starting cursor, and
|
|
296
|
+
// always on an unfiltered board; the user-filtered board is what prints.
|
|
297
|
+
const start = await fetchBoard(deps, sessionId, {});
|
|
298
|
+
let cursor = start.cursor;
|
|
299
|
+
const timeoutMs = timeoutSeconds * 1000;
|
|
300
|
+
const deadline = deps.now() + timeoutMs;
|
|
301
|
+
let remainingMs = timeoutMs;
|
|
302
|
+
while (remainingMs > 0) {
|
|
303
|
+
const waitSeconds = Math.min(Math.ceil(remainingMs / 1000), EVENTS_WAIT_CHUNK_SECONDS);
|
|
304
|
+
const page = await fetchEventsPage(deps, sessionId, { since: cursor, waitSeconds });
|
|
305
|
+
cursor = page.cursor;
|
|
306
|
+
if (page.events.length > 0) {
|
|
307
|
+
const board = await fetchBoard(deps, sessionId, {});
|
|
308
|
+
if (evaluateWaitCondition(condition, board, page.events)) {
|
|
309
|
+
printBoard(deps, await fetchBoard(deps, sessionId, query), json);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
remainingMs = Math.min(remainingMs, deadline - deps.now());
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
remainingMs -= waitSeconds * 1000;
|
|
316
|
+
const wallRemaining = deadline - deps.now();
|
|
317
|
+
if (wallRemaining < remainingMs)
|
|
318
|
+
remainingMs = wallRemaining;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
printBoard(deps, await fetchBoard(deps, sessionId, query), json);
|
|
322
|
+
throw new ExitError(3);
|
|
323
|
+
}
|
|
324
|
+
async function runBoardCommand(deps, opts) {
|
|
325
|
+
const sessionId = requireSessionId(opts.sessionId, deps.env);
|
|
326
|
+
const since = parseSince(opts.since);
|
|
327
|
+
if (opts.wait && opts.until === undefined)
|
|
328
|
+
throw new UsageError('--wait requires --until <condition>');
|
|
329
|
+
if (!opts.wait && opts.until !== undefined)
|
|
330
|
+
throw new UsageError('--until requires --wait');
|
|
331
|
+
if (opts.timeout !== undefined && !opts.wait)
|
|
332
|
+
throw new UsageError('--timeout requires --wait');
|
|
333
|
+
const timeoutSeconds = parseTimeoutSeconds(opts.timeout);
|
|
334
|
+
const condition = opts.until === undefined ? undefined : parseWaitCondition(opts.until);
|
|
335
|
+
const query = { since, ready: opts.ready === true, all: opts.all === true };
|
|
336
|
+
const json = opts.json === true;
|
|
337
|
+
if (!opts.wait) {
|
|
338
|
+
printBoard(deps, await fetchBoard(deps, sessionId, query), json);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
await waitForBoard(deps, sessionId, query, condition, timeoutSeconds, json);
|
|
342
|
+
}
|
|
343
|
+
function boardProgram(deps) {
|
|
344
|
+
const program = new Command()
|
|
345
|
+
.name('ftown board')
|
|
346
|
+
.description("Child-session board for this session's tree");
|
|
347
|
+
program.exitOverride();
|
|
348
|
+
program.configureOutput({
|
|
349
|
+
writeOut: (text) => deps.log(text.trimEnd()),
|
|
350
|
+
writeErr: (text) => deps.errorLog(text.trimEnd()),
|
|
351
|
+
});
|
|
352
|
+
program
|
|
353
|
+
.option('--json', 'Machine-readable JSON output')
|
|
354
|
+
.option('--plain', 'Human-readable output (default)')
|
|
355
|
+
.option('--since <n>', 'Only children with tree events after this seq')
|
|
356
|
+
.option('--ready', 'Only merge-ready children')
|
|
357
|
+
.option('--all', 'Include archived/completed children')
|
|
358
|
+
.option('--wait', 'Wait until --until condition is met')
|
|
359
|
+
.option('--until <cond>', `Wait condition: ${BOARD_WAIT_CONDITIONS.join(' | ')}`)
|
|
360
|
+
.option('--timeout <s>', `With --wait: give up after this many seconds (1-${WAIT_MAX_TIMEOUT_SECONDS}, default ${WAIT_DEFAULT_TIMEOUT_SECONDS})`)
|
|
361
|
+
.option('--session-id <id>', 'Target session; defaults to FTOWN_SESSION_ID')
|
|
362
|
+
.action(async (opts) => {
|
|
363
|
+
await runBoardCommand(deps, opts);
|
|
364
|
+
});
|
|
365
|
+
return program;
|
|
366
|
+
}
|
|
367
|
+
async function waitForEvents(deps, sessionId, since, limit, timeoutSeconds, json) {
|
|
368
|
+
// Without --since, wait only for events from the current head (board cursor).
|
|
369
|
+
let cursor = since ?? (await fetchBoard(deps, sessionId, {})).cursor;
|
|
370
|
+
const timeoutMs = timeoutSeconds * 1000;
|
|
371
|
+
const deadline = deps.now() + timeoutMs;
|
|
372
|
+
let remainingMs = timeoutMs;
|
|
373
|
+
let lastPage = { events: [], cursor };
|
|
374
|
+
while (remainingMs > 0) {
|
|
375
|
+
const waitSeconds = Math.min(Math.ceil(remainingMs / 1000), EVENTS_WAIT_CHUNK_SECONDS);
|
|
376
|
+
const page = await fetchEventsPage(deps, sessionId, { since: cursor, limit, waitSeconds });
|
|
377
|
+
lastPage = page;
|
|
378
|
+
cursor = page.cursor;
|
|
379
|
+
if (page.events.length > 0) {
|
|
380
|
+
printEvents(deps, page, json);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
remainingMs -= waitSeconds * 1000;
|
|
384
|
+
const wallRemaining = deadline - deps.now();
|
|
385
|
+
if (wallRemaining < remainingMs)
|
|
386
|
+
remainingMs = wallRemaining;
|
|
387
|
+
}
|
|
388
|
+
printEvents(deps, lastPage, json);
|
|
389
|
+
throw new ExitError(3);
|
|
390
|
+
}
|
|
391
|
+
async function runEventsCommand(deps, opts) {
|
|
392
|
+
const sessionId = requireSessionId(opts.sessionId, deps.env);
|
|
393
|
+
const since = parseSince(opts.since);
|
|
394
|
+
const limit = parseLimit(opts.limit);
|
|
395
|
+
if (opts.timeout !== undefined && !opts.wait)
|
|
396
|
+
throw new UsageError('--timeout requires --wait');
|
|
397
|
+
const timeoutSeconds = parseTimeoutSeconds(opts.timeout);
|
|
398
|
+
const json = opts.json === true;
|
|
399
|
+
if (!opts.wait) {
|
|
400
|
+
printEvents(deps, await fetchEventsPage(deps, sessionId, { since, limit }), json);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
await waitForEvents(deps, sessionId, since, limit, timeoutSeconds, json);
|
|
404
|
+
}
|
|
405
|
+
function eventsProgram(deps) {
|
|
406
|
+
const program = new Command()
|
|
407
|
+
.name('ftown events')
|
|
408
|
+
.description("Tree event log for this session's tree");
|
|
409
|
+
program.exitOverride();
|
|
410
|
+
program.configureOutput({
|
|
411
|
+
writeOut: (text) => deps.log(text.trimEnd()),
|
|
412
|
+
writeErr: (text) => deps.errorLog(text.trimEnd()),
|
|
413
|
+
});
|
|
414
|
+
program
|
|
415
|
+
.option('--json', 'Machine-readable JSON output')
|
|
416
|
+
.option('--plain', 'Human-readable output (default)')
|
|
417
|
+
.option('--since <n>', 'Only events with seq greater than n')
|
|
418
|
+
.option('--limit <n>', 'Max events (1-500)')
|
|
419
|
+
.option('--wait', 'Wait for new events')
|
|
420
|
+
.option('--timeout <s>', `With --wait: give up after this many seconds (1-${WAIT_MAX_TIMEOUT_SECONDS}, default ${WAIT_DEFAULT_TIMEOUT_SECONDS})`)
|
|
421
|
+
.option('--session-id <id>', 'Target session; defaults to FTOWN_SESSION_ID')
|
|
422
|
+
.action(async (opts) => {
|
|
423
|
+
await runEventsCommand(deps, opts);
|
|
424
|
+
});
|
|
425
|
+
return program;
|
|
426
|
+
}
|
|
427
|
+
// ───────────────────────── entry points ─────────────────────────
|
|
428
|
+
async function runProgram(group, program, argv, deps) {
|
|
429
|
+
try {
|
|
430
|
+
await program.parseAsync(argv, { from: 'user' });
|
|
431
|
+
return 0;
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
if (error instanceof CommanderError)
|
|
435
|
+
return error.exitCode === 0 ? 0 : 2;
|
|
436
|
+
if (error instanceof ExitError)
|
|
437
|
+
return error.code;
|
|
438
|
+
if (error instanceof UsageError) {
|
|
439
|
+
deps.errorLog(`ftown ${group}: ${error.message}`);
|
|
440
|
+
if (error.usage)
|
|
441
|
+
deps.errorLog(error.usage);
|
|
442
|
+
return 2;
|
|
443
|
+
}
|
|
444
|
+
deps.errorLog(`ftown ${group}: ${error instanceof Error ? error.message : String(error)}`);
|
|
445
|
+
return 1;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
export async function runStatus(argv, deps) {
|
|
449
|
+
const resolved = resolveDeps(deps);
|
|
450
|
+
return runProgram('status', statusProgram(resolved), argv, resolved);
|
|
451
|
+
}
|
|
452
|
+
export async function runBoard(argv, deps) {
|
|
453
|
+
const resolved = resolveDeps(deps);
|
|
454
|
+
return runProgram('board', boardProgram(resolved), argv, resolved);
|
|
455
|
+
}
|
|
456
|
+
export async function runEvents(argv, deps) {
|
|
457
|
+
const resolved = resolveDeps(deps);
|
|
458
|
+
return runProgram('events', eventsProgram(resolved), argv, resolved);
|
|
459
|
+
}
|
|
460
|
+
//# sourceMappingURL=coordination-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordination-cli.js","sourceRoot":"","sources":["../src/coordination-cli.ts"],"names":[],"mappings":"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"}
|
package/dist/ftown-cli.js
CHANGED
|
@@ -5,11 +5,13 @@ import { runEnv } from './env-commands.js';
|
|
|
5
5
|
import { runSetup } from './setup.js';
|
|
6
6
|
import { runStart } from './start-commands.js';
|
|
7
7
|
import { runSecurity } from './security-cli.js';
|
|
8
|
+
import { runQuotaCli } from './quota-cli.js';
|
|
9
|
+
import { runBoard, runEvents, runStatus } from './coordination-cli.js';
|
|
8
10
|
import { executeCli, isCliEntry } from './cli-entry.js';
|
|
9
11
|
export { isCliEntry } from './cli-entry.js';
|
|
10
12
|
export function usage() {
|
|
11
13
|
return `Usage: ftown [bridge options]
|
|
12
|
-
ftown <setup|start|sessions|harness|env|security> [args...]
|
|
14
|
+
ftown <setup|start|sessions|harness|env|security|quota|status|board|events> [args...]
|
|
13
15
|
|
|
14
16
|
With no subcommand, start the bridge. Run setup explicitly before the first start.
|
|
15
17
|
|
|
@@ -19,7 +21,11 @@ Subcommands:
|
|
|
19
21
|
sessions List, create, read and drive ftown agent sessions
|
|
20
22
|
harness Inspect and drive the local ftown-bridge harness
|
|
21
23
|
security Pair browsers and inspect/revoke encrypted endpoints
|
|
22
|
-
env Manage per-provider machine tokens (~/.ftown/env.json)
|
|
24
|
+
env Manage per-provider machine tokens (~/.ftown/env.json)
|
|
25
|
+
quota Subscription quota left, routing advice and model lists per harness (--json for agents)
|
|
26
|
+
status Own status card: show, set or clear
|
|
27
|
+
board Child-session board for this session's tree
|
|
28
|
+
events Tree event log for this session's tree`;
|
|
23
29
|
}
|
|
24
30
|
export async function runFtown(argv, start = runStart) {
|
|
25
31
|
const [group, ...args] = argv;
|
|
@@ -34,6 +40,10 @@ export async function runFtown(argv, start = runStart) {
|
|
|
34
40
|
case 'harness': return runHarness(args);
|
|
35
41
|
case 'env': return runEnv(args);
|
|
36
42
|
case 'security': return runSecurity(args);
|
|
43
|
+
case 'quota': return runQuotaCli(args);
|
|
44
|
+
case 'status': return runStatus(args);
|
|
45
|
+
case 'board': return runBoard(args);
|
|
46
|
+
case 'events': return runEvents(args);
|
|
37
47
|
case 'setup': return runSetup(args);
|
|
38
48
|
case 'start': return start(args);
|
|
39
49
|
default:
|
package/dist/ftown-cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ftown-cli.js","sourceRoot":"","sources":["../src/ftown-cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,UAAU,KAAK;IACnB,OAAO
|
|
1
|
+
{"version":3,"file":"ftown-cli.js","sourceRoot":"","sources":["../src/ftown-cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,UAAU,KAAK;IACnB,OAAO;;;;;;;;;;;;;;;oDAe2C,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAc,EAAE,QAA6C,QAAQ;IAClG,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9B,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QAAC,OAAO,CAAC,CAAC;IAAC,CAAC;IACjF,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;IACxD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,UAAU,CAAC,CAAC,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;QAC1C,KAAK,SAAS,CAAC,CAAC,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QACxC,KAAK,KAAK,CAAC,CAAC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;QAChC,KAAK,UAAU,CAAC,CAAC,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;QAC1C,KAAK,OAAO,CAAC,CAAC,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;QACvC,KAAK,QAAQ,CAAC,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACtC,KAAK,OAAO,CAAC,CAAC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpC,KAAK,QAAQ,CAAC,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACtC,KAAK,OAAO,CAAC,CAAC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpC,KAAK,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC;YAAS,OAAO,CAAC,KAAK,CAAC,8BAA8B,KAAK,GAAG,CAAC,CAAC;YAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAAC,OAAO,CAAC,CAAC;IACnG,CAAC;AACH,CAAC;AAED,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,KAAK,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -29,9 +29,11 @@ import { loadOrCreateIdentity, readBoundBridgeIdentity } from './security/bridge
|
|
|
29
29
|
import { bindLedgerIdentity } from './security/bridge/ledger-identity.js';
|
|
30
30
|
import { BridgeSecurityService, applicationJson } from './security/bridge/security.js';
|
|
31
31
|
import { createCommandHandler } from './command-rpc.js';
|
|
32
|
+
import { createRealModelRoutingService } from './quota/routing-service.js';
|
|
32
33
|
import { PullRequestStatusService } from './pull-request-status.js';
|
|
33
34
|
import { createPrReviewService } from './pull-request-review.js';
|
|
34
35
|
import { PullRequestEventWatcher } from './pull-request-events.js';
|
|
36
|
+
import { createCoordination } from './coordination/wiring.js';
|
|
35
37
|
import { removeFtownSession } from './remove-ftown-session.js';
|
|
36
38
|
import { SessionResurrection, shouldResurrectStoredSession } from './session-resurrection.js';
|
|
37
39
|
import { createTerminalOutput } from './terminal-output.js';
|
|
@@ -746,18 +748,40 @@ export async function runBridge(argv, commandName = 'ftown-bridge') {
|
|
|
746
748
|
onSubagentIdle: (signal, isCurrent) => localApiServer.notifyIdleParent(signal, isCurrent),
|
|
747
749
|
});
|
|
748
750
|
localApiServer.setAttentionStore(attentionStore);
|
|
751
|
+
const mailStore = new MailStore((sessionId) => store.sessionDir(sessionId));
|
|
752
|
+
localApiServer.setMailStore(mailStore);
|
|
753
|
+
const pullRequestStatuses = new PullRequestStatusService();
|
|
754
|
+
const coordination = createCoordination({
|
|
755
|
+
store,
|
|
756
|
+
attention: attentionStore,
|
|
757
|
+
prStatus: async (url) => (await pullRequestStatuses.get([url])).statuses[0] ?? null,
|
|
758
|
+
mail: mailStore,
|
|
759
|
+
publishSession: (session) => { void centrifugo.publishSessionUpdate(userId, session); },
|
|
760
|
+
now: Date.now,
|
|
761
|
+
});
|
|
762
|
+
localApiServer.setCoordination(coordination);
|
|
749
763
|
const stopPushSessionObserver = centrifugo.onLocalPublication((channel, data) => {
|
|
750
764
|
if (channel !== `sessions:updates#${userId}` || !data || typeof data !== 'object')
|
|
751
765
|
return;
|
|
752
766
|
const update = data;
|
|
753
|
-
if (update.type === 'session_update' && update.session?.id
|
|
754
|
-
|
|
767
|
+
if (update.type === 'session_update' && update.session?.id) {
|
|
768
|
+
if (update.session.status === 'running') {
|
|
769
|
+
coordination.onSessionStart(update.session.id);
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
void attentionStore.cancelIdleNotification(update.session.id).catch(() => { });
|
|
773
|
+
}
|
|
755
774
|
}
|
|
756
775
|
});
|
|
757
|
-
const
|
|
758
|
-
|
|
776
|
+
const handleSessionExit = (sid) => {
|
|
777
|
+
unregisterSession(sid);
|
|
778
|
+
void coordination.onSessionExit(sid).catch((err) => {
|
|
779
|
+
console.warn(`[Bridge] Failed to emit exit event for ${sid}:`, err instanceof Error ? err.message : String(err));
|
|
780
|
+
});
|
|
781
|
+
};
|
|
759
782
|
const notifyScriptPath = canonicalNotifyPath();
|
|
760
783
|
const wireTerminalInput = (sessionId) => {
|
|
784
|
+
coordination.onSessionStart(sessionId);
|
|
761
785
|
centrifugo.subscribeToTerminalInput(userId, sessionId, (sid, data) => { runner.write(sid, data); }, (sid, cols, rows) => { handleClientResize(sid, cols, rows); }, (sid) => { publishScreenDump(sid); });
|
|
762
786
|
};
|
|
763
787
|
const sessionFactoryDeps = {
|
|
@@ -785,7 +809,7 @@ export async function runBridge(argv, commandName = 'ftown-bridge') {
|
|
|
785
809
|
lifecycle: {
|
|
786
810
|
publishSessionUpdate: (session) => centrifugo.publishSessionUpdate(userId, session),
|
|
787
811
|
publishHookEvent: (sid, event) => centrifugo.publishHookEvent(userId, sid, event),
|
|
788
|
-
unregisterSession:
|
|
812
|
+
unregisterSession: handleSessionExit,
|
|
789
813
|
collectUsage: (session) => collectSessionUsage(session),
|
|
790
814
|
},
|
|
791
815
|
});
|
|
@@ -823,18 +847,18 @@ export async function runBridge(argv, commandName = 'ftown-bridge') {
|
|
|
823
847
|
collectUsage: (session) => collectSessionUsage(session),
|
|
824
848
|
publishSyntheticStop: (sid, reason) => pump.publishSyntheticStop(sid, reason),
|
|
825
849
|
withSessionWrite: (sid, task) => pump.withSessionWrite(sid, task),
|
|
826
|
-
unregisterSession:
|
|
850
|
+
unregisterSession: handleSessionExit,
|
|
827
851
|
flushTerminalBuffer: (sid) => pump.flush(sid),
|
|
828
852
|
destroyTerminal: (sid) => terminalManager.destroy(sid),
|
|
829
853
|
});
|
|
830
854
|
localApiServer.setSessionController(sessionController);
|
|
831
|
-
const pullRequestStatuses = new PullRequestStatusService();
|
|
832
855
|
const pullRequestReview = createPrReviewService({ bridgeId, loadSession: id => sessionController.get(id), revalidate: principal => security.policy.revalidate(principal, 'read') });
|
|
833
856
|
const pullRequestEvents = new PullRequestEventWatcher({ bridgeId, store,
|
|
834
857
|
withSessionMutation: (id, task) => pump.withSessionWrite(id, task),
|
|
835
858
|
statusService: pullRequestStatuses, mailStore,
|
|
836
859
|
notifyCommitted: (session, sender) => localApiServer.notifyCommittedMail(session, sender, true),
|
|
837
860
|
});
|
|
861
|
+
pullRequestEvents.setTreeEventEmitter((session, kind, summary, ref) => coordination.emit(session, kind, summary, ref));
|
|
838
862
|
pullRequestEvents.start();
|
|
839
863
|
const updateHome = realpathSync(homedir());
|
|
840
864
|
const stagingRoot = join(updateHome, '.ftown', 'updates');
|
|
@@ -926,6 +950,7 @@ export async function runBridge(argv, commandName = 'ftown-bridge') {
|
|
|
926
950
|
}).finally(release);
|
|
927
951
|
});
|
|
928
952
|
let ready = false;
|
|
953
|
+
const modelRouting = createRealModelRoutingService(homedir());
|
|
929
954
|
const dispatchCommand = async (message) => {
|
|
930
955
|
if (!ready)
|
|
931
956
|
return { type: 'command_response', response: { requestId: message.envelope.messageId, success: false, error: 'bridge_offline' } };
|
|
@@ -940,6 +965,7 @@ export async function runBridge(argv, commandName = 'ftown-bridge') {
|
|
|
940
965
|
}
|
|
941
966
|
let result;
|
|
942
967
|
await createCommandHandler({ bridgeId, security, sessionController, attentionStore, updates, updateEffects, pullRequestStatuses, pullRequestReview,
|
|
968
|
+
modelRouting: () => modelRouting,
|
|
943
969
|
enqueueSessionMessage: (session, message) => localApiServer.enqueueSessionMessage(session, message),
|
|
944
970
|
acceptSessionMail: (session, body) => localApiServer.acceptSessionMail(session, body),
|
|
945
971
|
publishAttentionChanged: (sessionId) => { localApiServer.emit('event', { sessionId, eventName: 'AttentionSnapshot', data: {} }); },
|