u-foo 2.5.1 → 2.5.2
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/package.json +1 -1
- package/src/agents/internal/internalRunner.js +57 -78
- package/src/agents/launch/notifier.js +35 -111
- package/src/agents/launch/ptyRunner.js +28 -48
- package/src/app/cli/busCoreCommands.js +19 -1
- package/src/app/cli/run.js +1 -53
- package/src/code/agent.js +41 -140
- package/src/coordination/bus/deliveryQueue.js +308 -0
- package/src/coordination/bus/index.js +3 -27
- package/src/coordination/bus/message.js +35 -15
- package/src/coordination/bus/queue.js +23 -7
- package/src/runtime/daemon/deliveryScheduler.js +205 -0
- package/src/runtime/daemon/index.js +27 -38
- package/src/runtime/daemon/reportControlBus.js +20 -43
- package/SKILLS/brandkit/SKILL.md +0 -798
- package/SKILLS/brutalist-skill/SKILL.md +0 -92
- package/SKILLS/gpt-tasteskill/SKILL.md +0 -74
- package/SKILLS/image-to-code-skill/SKILL.md +0 -1228
- package/SKILLS/imagegen-frontend-mobile/SKILL.md +0 -1465
- package/SKILLS/imagegen-frontend-web/SKILL.md +0 -987
- package/SKILLS/llms.txt +0 -13
- package/SKILLS/minimalist-skill/SKILL.md +0 -85
- package/SKILLS/output-skill/SKILL.md +0 -49
- package/SKILLS/redesign-skill/SKILL.md +0 -178
- package/SKILLS/soft-skill/SKILL.md +0 -98
- package/SKILLS/stitch-skill/DESIGN.md +0 -121
- package/SKILLS/stitch-skill/SKILL.md +0 -184
- package/SKILLS/taste-skill/SKILL.md +0 -1206
- package/SKILLS/taste-skill-v1/SKILL.md +0 -226
- package/src/coordination/bus/daemon.js +0 -535
package/package.json
CHANGED
|
@@ -16,6 +16,7 @@ const { redactToolCallPayload, redactSecrets } = require("../../runtime/privacy/
|
|
|
16
16
|
const { buildCachedMemoryPrefix } = require("../../coordination/memory");
|
|
17
17
|
const { normalizePublisher, shouldForwardStreamToPublisher } = require("../launch/publisherRouting");
|
|
18
18
|
const { appendAgentRegistryDiagnostic } = require("../../coordination/state/agentRegistryDiagnostics");
|
|
19
|
+
const { DeliveryQueue } = require("../../coordination/bus/deliveryQueue");
|
|
19
20
|
const {
|
|
20
21
|
buildDefaultStartupBootstrapPrompt,
|
|
21
22
|
isValueForCodexOption,
|
|
@@ -216,37 +217,15 @@ function shouldStreamReplyToPublisher(projectRoot, publisher, evt = {}) {
|
|
|
216
217
|
return shouldForwardStreamToPublisher(projectRoot, publisher);
|
|
217
218
|
}
|
|
218
219
|
|
|
219
|
-
function
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
content = fs.readFileSync(processingFile, "utf8");
|
|
227
|
-
readOk = true;
|
|
228
|
-
} catch {
|
|
229
|
-
try {
|
|
230
|
-
if (fs.existsSync(processingFile)) {
|
|
231
|
-
fs.renameSync(processingFile, queueFile);
|
|
232
|
-
}
|
|
233
|
-
} catch {
|
|
234
|
-
// ignore rollback errors
|
|
235
|
-
}
|
|
236
|
-
return [];
|
|
237
|
-
} finally {
|
|
238
|
-
if (readOk) {
|
|
239
|
-
try {
|
|
240
|
-
if (fs.existsSync(processingFile)) {
|
|
241
|
-
fs.rmSync(processingFile, { force: true });
|
|
242
|
-
}
|
|
243
|
-
} catch {
|
|
244
|
-
// ignore cleanup errors
|
|
245
|
-
}
|
|
246
|
-
}
|
|
220
|
+
function claimQueuedEvents(queueFile) {
|
|
221
|
+
const queue = new DeliveryQueue(queueFile);
|
|
222
|
+
const claims = [];
|
|
223
|
+
while (true) {
|
|
224
|
+
const claim = queue.claimNext();
|
|
225
|
+
if (!claim) break;
|
|
226
|
+
claims.push({ ...claim, queue });
|
|
247
227
|
}
|
|
248
|
-
|
|
249
|
-
return content.split(/\r?\n/).filter(Boolean);
|
|
228
|
+
return claims;
|
|
250
229
|
}
|
|
251
230
|
|
|
252
231
|
function parseAgentViewRawInput(message) {
|
|
@@ -835,67 +814,67 @@ async function runInternalRunner({ projectRoot, agentType = "codex", extraArgs =
|
|
|
835
814
|
if (!processing) {
|
|
836
815
|
processing = true;
|
|
837
816
|
try {
|
|
838
|
-
const
|
|
839
|
-
if (
|
|
840
|
-
|
|
841
|
-
for (const line of lines) {
|
|
842
|
-
try {
|
|
843
|
-
events.push(JSON.parse(line));
|
|
844
|
-
} catch {
|
|
845
|
-
// ignore malformed line
|
|
846
|
-
}
|
|
847
|
-
}
|
|
817
|
+
const claims = claimQueuedEvents(queueFile);
|
|
818
|
+
if (claims.length > 0) {
|
|
819
|
+
let handledAny = false;
|
|
848
820
|
|
|
849
|
-
const
|
|
850
|
-
|
|
821
|
+
for (const claim of claims) {
|
|
822
|
+
const evt = claim.event;
|
|
823
|
+
const runnableEvents = [];
|
|
851
824
|
const rawInput = parseAgentViewRawInput(evt && evt.data ? evt.data.message : "");
|
|
852
825
|
if (rawInput === null) {
|
|
853
826
|
runnableEvents.push(evt);
|
|
854
|
-
|
|
827
|
+
} else {
|
|
828
|
+
const session = getInteractiveSession(evt.publisher || "unknown");
|
|
829
|
+
const submissions = session.handleRaw(rawInput);
|
|
830
|
+
for (const message of submissions) {
|
|
831
|
+
runnableEvents.push({
|
|
832
|
+
...evt,
|
|
833
|
+
__agentViewRaw: true,
|
|
834
|
+
data: {
|
|
835
|
+
...(evt.data || {}),
|
|
836
|
+
message,
|
|
837
|
+
},
|
|
838
|
+
});
|
|
839
|
+
}
|
|
855
840
|
}
|
|
856
841
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
for (const message of submissions) {
|
|
860
|
-
runnableEvents.push({
|
|
861
|
-
...evt,
|
|
862
|
-
__agentViewRaw: true,
|
|
863
|
-
data: {
|
|
864
|
-
...(evt.data || {}),
|
|
865
|
-
message,
|
|
866
|
-
},
|
|
867
|
-
});
|
|
842
|
+
if (runnableEvents.length > 0) {
|
|
843
|
+
activityTracker.notifyTurnStart("thinking");
|
|
868
844
|
}
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
if (runnableEvents.length > 0) {
|
|
872
|
-
activityTracker.notifyTurnStart("thinking");
|
|
873
|
-
}
|
|
874
845
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
846
|
+
try {
|
|
847
|
+
for (const runnableEvent of runnableEvents) {
|
|
848
|
+
// eslint-disable-next-line no-await-in-loop
|
|
849
|
+
await handleEvent(
|
|
850
|
+
projectRoot,
|
|
851
|
+
parsedAgentType,
|
|
852
|
+
provider,
|
|
853
|
+
model,
|
|
854
|
+
subscriber,
|
|
855
|
+
nickname,
|
|
856
|
+
runnableEvent,
|
|
857
|
+
busSender,
|
|
858
|
+
bootstrap.extraArgs,
|
|
859
|
+
threadRuntime,
|
|
860
|
+
bootstrap.promptText,
|
|
861
|
+
activityTracker
|
|
862
|
+
);
|
|
863
|
+
if (runnableEvent.__agentViewRaw) {
|
|
864
|
+
getInteractiveSession(runnableEvent.publisher || "unknown").writeResponsePrompt();
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
claim.queue.completeClaim(claim);
|
|
868
|
+
if (runnableEvents.length > 0) handledAny = true;
|
|
869
|
+
} catch (err) {
|
|
870
|
+
claim.queue.restoreClaim(claim);
|
|
871
|
+
throw err;
|
|
893
872
|
}
|
|
894
873
|
}
|
|
895
874
|
// 处理消息后更新心跳
|
|
896
875
|
updateHeartbeat();
|
|
897
876
|
lastHeartbeat = now;
|
|
898
|
-
if (
|
|
877
|
+
if (handledAny) {
|
|
899
878
|
activityTracker.markIdle();
|
|
900
879
|
}
|
|
901
880
|
await busSender.flush();
|
|
@@ -9,8 +9,8 @@ const { shakeTerminalByTty } = require("../../coordination/bus/shake");
|
|
|
9
9
|
const { isITerm2 } = require("../../runtime/terminal/detect");
|
|
10
10
|
const iterm2 = require("../../runtime/terminal/iterm2");
|
|
11
11
|
const { createActivityStatePublisher } = require("../activity/activityStatePublisher");
|
|
12
|
-
const { INJECTION_MODES, getInjectionModeFromEvent } = require("../../coordination/bus/messageMeta");
|
|
13
12
|
const { buildPromptInjectionText } = require("../../coordination/bus/promptEnvelope");
|
|
13
|
+
const { DeliveryQueue } = require("../../coordination/bus/deliveryQueue");
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Agent 消息通知监听器
|
|
@@ -41,6 +41,7 @@ class AgentNotifier {
|
|
|
41
41
|
safeSub,
|
|
42
42
|
"pending.jsonl"
|
|
43
43
|
);
|
|
44
|
+
this.deliveryQueue = new DeliveryQueue(this.queueFile);
|
|
44
45
|
this.agentsFile = paths.agentsFile;
|
|
45
46
|
|
|
46
47
|
// 初始化 injector
|
|
@@ -187,45 +188,6 @@ class AgentNotifier {
|
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
drainPending() {
|
|
191
|
-
if (!fs.existsSync(this.queueFile)) return [];
|
|
192
|
-
const processingFile = `${this.queueFile}.processing.${process.pid}.${Date.now()}`;
|
|
193
|
-
let content = "";
|
|
194
|
-
let readOk = false;
|
|
195
|
-
try {
|
|
196
|
-
fs.renameSync(this.queueFile, processingFile);
|
|
197
|
-
content = fs.readFileSync(processingFile, "utf8");
|
|
198
|
-
readOk = true;
|
|
199
|
-
} catch {
|
|
200
|
-
try {
|
|
201
|
-
if (fs.existsSync(processingFile)) {
|
|
202
|
-
fs.renameSync(processingFile, this.queueFile);
|
|
203
|
-
}
|
|
204
|
-
} catch {
|
|
205
|
-
// ignore rollback errors
|
|
206
|
-
}
|
|
207
|
-
return [];
|
|
208
|
-
} finally {
|
|
209
|
-
if (readOk) {
|
|
210
|
-
try {
|
|
211
|
-
if (fs.existsSync(processingFile)) {
|
|
212
|
-
fs.rmSync(processingFile, { force: true });
|
|
213
|
-
}
|
|
214
|
-
} catch {
|
|
215
|
-
// ignore cleanup errors
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
if (!content.trim()) return [];
|
|
220
|
-
return content.split(/\r?\n/).filter(Boolean).map((line) => {
|
|
221
|
-
try {
|
|
222
|
-
return JSON.parse(line);
|
|
223
|
-
} catch {
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
}).filter(Boolean);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
191
|
normalizePublisher(publisher) {
|
|
230
192
|
if (!publisher) return "";
|
|
231
193
|
if (typeof publisher === "string") return publisher;
|
|
@@ -272,57 +234,41 @@ class AgentNotifier {
|
|
|
272
234
|
return 0;
|
|
273
235
|
}
|
|
274
236
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const requeue = [];
|
|
278
|
-
let delivered = 0;
|
|
279
|
-
let consumedOne = false;
|
|
280
|
-
for (const evt of events) {
|
|
281
|
-
if (!evt || evt.event !== "message" || !evt.data || typeof evt.data.message !== "string") {
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
const injectionMode = getInjectionModeFromEvent(evt, INJECTION_MODES.IMMEDIATE);
|
|
285
|
-
const activityState = this.getCurrentActivityState();
|
|
286
|
-
if (injectionMode === INJECTION_MODES.QUEUED && this.isBusyState(activityState)) {
|
|
287
|
-
requeue.push(evt);
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
if (consumedOne) {
|
|
291
|
-
requeue.push(evt);
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
const message = buildPromptInjectionText(evt, this.subscriber, this.getAgentsMap());
|
|
295
|
-
try {
|
|
296
|
-
// Inject the prompt-facing text into the terminal/tmux agent
|
|
297
|
-
// (Bus is the source of truth; inject is the delivery adapter.)
|
|
298
|
-
// eslint-disable-next-line no-await-in-loop
|
|
299
|
-
await this.injector.inject(this.subscriber, message);
|
|
300
|
-
delivered += 1;
|
|
301
|
-
consumedOne = true;
|
|
302
|
-
this.injectFailCount = 0;
|
|
303
|
-
this.updateActivityState("working");
|
|
304
|
-
// eslint-disable-next-line no-await-in-loop
|
|
305
|
-
await this.emitDelivery(evt, "ok");
|
|
306
|
-
} catch (err) {
|
|
307
|
-
consumedOne = true;
|
|
308
|
-
this.injectFailCount += 1;
|
|
309
|
-
requeue.push(evt);
|
|
310
|
-
// eslint-disable-next-line no-await-in-loop
|
|
311
|
-
await this.emitDelivery(evt, "error", err.message || "inject failed");
|
|
312
|
-
}
|
|
237
|
+
if (this.isBusyState(this.getCurrentActivityState())) {
|
|
238
|
+
return 0;
|
|
313
239
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
240
|
+
|
|
241
|
+
const claim = this.deliveryQueue.claimNext();
|
|
242
|
+
if (!claim) return 0;
|
|
243
|
+
|
|
244
|
+
const evt = claim.event;
|
|
245
|
+
if (!evt || evt.event !== "message" || !evt.data || typeof evt.data.message !== "string") {
|
|
246
|
+
this.deliveryQueue.completeClaim(claim);
|
|
247
|
+
return 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const activityState = this.getCurrentActivityState();
|
|
251
|
+
if (this.isBusyState(activityState)) {
|
|
252
|
+
this.deliveryQueue.restoreClaim(claim);
|
|
253
|
+
return 0;
|
|
321
254
|
}
|
|
322
|
-
|
|
255
|
+
|
|
256
|
+
const message = buildPromptInjectionText(evt, this.subscriber, this.getAgentsMap());
|
|
257
|
+
try {
|
|
258
|
+
// Inject the prompt-facing text into the terminal/tmux agent.
|
|
259
|
+
await this.injector.inject(this.subscriber, message);
|
|
260
|
+
this.deliveryQueue.completeClaim(claim);
|
|
261
|
+
this.injectFailCount = 0;
|
|
262
|
+
this.updateActivityState("working");
|
|
263
|
+
await this.emitDelivery(evt, "ok");
|
|
323
264
|
this.lastWorkingAt = Date.now();
|
|
265
|
+
return 1;
|
|
266
|
+
} catch (err) {
|
|
267
|
+
this.injectFailCount += 1;
|
|
268
|
+
this.deliveryQueue.restoreClaim(claim);
|
|
269
|
+
await this.emitDelivery(evt, "error", err.message || "inject failed");
|
|
270
|
+
return 0;
|
|
324
271
|
}
|
|
325
|
-
return delivered;
|
|
326
272
|
}
|
|
327
273
|
|
|
328
274
|
/**
|
|
@@ -367,32 +313,10 @@ class AgentNotifier {
|
|
|
367
313
|
if (currentCount > this.lastCount) {
|
|
368
314
|
const newCount = currentCount - this.lastCount;
|
|
369
315
|
this.notify(newCount);
|
|
370
|
-
|
|
371
|
-
// 自动触发终端输入(非阻塞)
|
|
372
|
-
this.autoTriggerInput().catch(() => {
|
|
373
|
-
// 忽略触发失败
|
|
374
|
-
});
|
|
375
316
|
}
|
|
376
317
|
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
if (this.isUfooCodeSubscriber()) {
|
|
380
|
-
if (this.lastUbusWakeCount !== currentCount) {
|
|
381
|
-
try {
|
|
382
|
-
await this.autoTriggerInput();
|
|
383
|
-
this.lastUbusWakeCount = currentCount;
|
|
384
|
-
} catch {
|
|
385
|
-
// ignore delivery errors
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
} else {
|
|
389
|
-
try {
|
|
390
|
-
await this.deliverPending();
|
|
391
|
-
} catch {
|
|
392
|
-
// ignore delivery errors
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
318
|
+
// Delivery is owned by the project daemon scheduler. The notifier keeps
|
|
319
|
+
// terminal notifications, title badges, heartbeat, and activity fallback.
|
|
396
320
|
if (currentCount <= 0) {
|
|
397
321
|
this.lastUbusWakeCount = -1;
|
|
398
322
|
}
|
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
resolveDefaultManualBootstrap,
|
|
18
18
|
} = require("../prompts/defaultBootstrap");
|
|
19
19
|
const { buildPromptInjectionText } = require("../../coordination/bus/promptEnvelope");
|
|
20
|
+
const { DeliveryQueue } = require("../../coordination/bus/deliveryQueue");
|
|
20
21
|
|
|
21
22
|
function sleep(ms) {
|
|
22
23
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -40,37 +41,15 @@ function safeSubscriber(subscriber) {
|
|
|
40
41
|
return subscriber.replace(/:/g, "_");
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
content = fs.readFileSync(processingFile, "utf8");
|
|
51
|
-
readOk = true;
|
|
52
|
-
} catch {
|
|
53
|
-
try {
|
|
54
|
-
if (fs.existsSync(processingFile)) {
|
|
55
|
-
fs.renameSync(processingFile, queueFile);
|
|
56
|
-
}
|
|
57
|
-
} catch {
|
|
58
|
-
// ignore rollback errors
|
|
59
|
-
}
|
|
60
|
-
return [];
|
|
61
|
-
} finally {
|
|
62
|
-
if (readOk) {
|
|
63
|
-
try {
|
|
64
|
-
if (fs.existsSync(processingFile)) {
|
|
65
|
-
fs.rmSync(processingFile, { force: true });
|
|
66
|
-
}
|
|
67
|
-
} catch {
|
|
68
|
-
// ignore cleanup errors
|
|
69
|
-
}
|
|
70
|
-
}
|
|
44
|
+
function claimQueuedEvents(queueFile) {
|
|
45
|
+
const queue = new DeliveryQueue(queueFile);
|
|
46
|
+
const claims = [];
|
|
47
|
+
while (true) {
|
|
48
|
+
const claim = queue.claimNext();
|
|
49
|
+
if (!claim) break;
|
|
50
|
+
claims.push({ ...claim, queue });
|
|
71
51
|
}
|
|
72
|
-
|
|
73
|
-
return content.split(/\r?\n/).filter(Boolean);
|
|
52
|
+
return claims;
|
|
74
53
|
}
|
|
75
54
|
|
|
76
55
|
function stripAnsi(text) {
|
|
@@ -1044,28 +1023,29 @@ async function runPtyRunner({ projectRoot, agentType = "codex", extraArgs = [] }
|
|
|
1044
1023
|
lastHeartbeat = now;
|
|
1045
1024
|
}
|
|
1046
1025
|
|
|
1047
|
-
const
|
|
1048
|
-
if (
|
|
1026
|
+
const claims = claimQueuedEvents(queueFile);
|
|
1027
|
+
if (claims.length > 0) {
|
|
1049
1028
|
const agents = readAgentsMap(agentsFilePath);
|
|
1050
|
-
const
|
|
1051
|
-
|
|
1029
|
+
for (const claim of claims) {
|
|
1030
|
+
const evt = claim.event;
|
|
1052
1031
|
try {
|
|
1053
|
-
|
|
1032
|
+
const input = buildPtyInputFromEvent(evt, subscriber, agents);
|
|
1033
|
+
if (!input) {
|
|
1034
|
+
claim.queue.completeClaim(claim);
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
const { raw, text } = input;
|
|
1038
|
+
if (messageQueue.length >= maxQueue) {
|
|
1039
|
+
messageQueue.shift();
|
|
1040
|
+
}
|
|
1041
|
+
const publisher = typeof evt.publisher === "object" && evt.publisher
|
|
1042
|
+
? (evt.publisher.subscriber || evt.publisher.nickname || "unknown")
|
|
1043
|
+
: (evt.publisher || "unknown");
|
|
1044
|
+
messageQueue.push({ publisher, raw, text });
|
|
1045
|
+
claim.queue.completeClaim(claim);
|
|
1054
1046
|
} catch {
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
for (const evt of events) {
|
|
1059
|
-
const input = buildPtyInputFromEvent(evt, subscriber, agents);
|
|
1060
|
-
if (!input) continue;
|
|
1061
|
-
const { raw, text } = input;
|
|
1062
|
-
if (messageQueue.length >= maxQueue) {
|
|
1063
|
-
messageQueue.shift();
|
|
1047
|
+
claim.queue.restoreClaim(claim);
|
|
1064
1048
|
}
|
|
1065
|
-
const publisher = typeof evt.publisher === "object" && evt.publisher
|
|
1066
|
-
? (evt.publisher.subscriber || evt.publisher.nickname || "unknown")
|
|
1067
|
-
: (evt.publisher || "unknown");
|
|
1068
|
-
messageQueue.push({ publisher, raw, text });
|
|
1069
1049
|
}
|
|
1070
1050
|
}
|
|
1071
1051
|
processQueue();
|
|
@@ -39,6 +39,18 @@ function parseSendArgs(cmdArgs = []) {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function resolvePollSubscriber(cmdArgs = [], env = process.env) {
|
|
43
|
+
const positionals = cmdArgs.filter((arg) => typeof arg === "string" && !arg.startsWith("--"));
|
|
44
|
+
const subscriber = String(positionals[0] || env.UFOO_SUBSCRIBER_ID || "").trim();
|
|
45
|
+
if (!subscriber) {
|
|
46
|
+
throw new Error("poll requires [subscriber] or UFOO_SUBSCRIBER_ID");
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
subscriber,
|
|
50
|
+
autoAck: cmdArgs.includes("--ack") || cmdArgs.includes("--auto-ack"),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
42
54
|
async function runBusCoreCommand(eventBus, cmd, cmdArgs = []) {
|
|
43
55
|
switch (cmd) {
|
|
44
56
|
case "init":
|
|
@@ -76,6 +88,12 @@ async function runBusCoreCommand(eventBus, cmd, cmdArgs = []) {
|
|
|
76
88
|
case "check":
|
|
77
89
|
await eventBus.check(cmdArgs[0]);
|
|
78
90
|
return {};
|
|
91
|
+
case "poll":
|
|
92
|
+
{
|
|
93
|
+
const parsed = resolvePollSubscriber(cmdArgs);
|
|
94
|
+
await eventBus.check(parsed.subscriber, parsed.autoAck);
|
|
95
|
+
}
|
|
96
|
+
return {};
|
|
79
97
|
case "ack":
|
|
80
98
|
await eventBus.ack(cmdArgs[0]);
|
|
81
99
|
return {};
|
|
@@ -99,4 +117,4 @@ async function runBusCoreCommand(eventBus, cmd, cmdArgs = []) {
|
|
|
99
117
|
}
|
|
100
118
|
}
|
|
101
119
|
|
|
102
|
-
module.exports = { runBusCoreCommand };
|
|
120
|
+
module.exports = { runBusCoreCommand, resolvePollSubscriber };
|
package/src/app/cli/run.js
CHANGED
|
@@ -1524,32 +1524,6 @@ async function runCli(argv) {
|
|
|
1524
1524
|
process.exitCode = 1;
|
|
1525
1525
|
});
|
|
1526
1526
|
});
|
|
1527
|
-
bus
|
|
1528
|
-
.command("daemon")
|
|
1529
|
-
.description("Start/stop daemon that auto-injects /bus into terminals")
|
|
1530
|
-
.option("--interval <n>", "Poll interval in seconds", "2")
|
|
1531
|
-
.option("--daemon", "Run in background")
|
|
1532
|
-
.option("--stop", "Stop running daemon")
|
|
1533
|
-
.option("--status", "Check daemon status")
|
|
1534
|
-
.action((opts) => {
|
|
1535
|
-
const EventBus = require("../../coordination/bus");
|
|
1536
|
-
const eventBus = new EventBus(process.cwd());
|
|
1537
|
-
(async () => {
|
|
1538
|
-
try {
|
|
1539
|
-
const interval = parseInt(opts.interval, 10) * 1000 || 2000;
|
|
1540
|
-
if (opts.stop) {
|
|
1541
|
-
await eventBus.daemon("stop");
|
|
1542
|
-
} else if (opts.status) {
|
|
1543
|
-
await eventBus.daemon("status");
|
|
1544
|
-
} else {
|
|
1545
|
-
await eventBus.daemon("start", { background: opts.daemon, interval });
|
|
1546
|
-
}
|
|
1547
|
-
} catch (err) {
|
|
1548
|
-
console.error(err.message);
|
|
1549
|
-
process.exitCode = 1;
|
|
1550
|
-
}
|
|
1551
|
-
})();
|
|
1552
|
-
});
|
|
1553
1527
|
bus
|
|
1554
1528
|
.command("inject")
|
|
1555
1529
|
.description("Inject /bus into a Terminal.app tab by subscriber ID")
|
|
@@ -1739,6 +1713,7 @@ async function runCli(argv) {
|
|
|
1739
1713
|
console.log(" ufoo online send --nickname <name> --text <msg> [--channel <ch>] [--room <id>]");
|
|
1740
1714
|
console.log(" ufoo online inbox <nickname> [--clear] [--unread]");
|
|
1741
1715
|
console.log(" ufoo bus wake <target> [--reason <reason>] [--no-shake]");
|
|
1716
|
+
console.log(" ufoo bus poll [subscriber] [--ack]");
|
|
1742
1717
|
console.log(" ufoo bus <args...> (JS bus implementation)");
|
|
1743
1718
|
console.log(" ufoo ctx <subcmd> ... (doctor|lint|decisions|sync)");
|
|
1744
1719
|
console.log(" ufoo history <build|show|prompt> [limit]");
|
|
@@ -2377,33 +2352,6 @@ async function runCli(argv) {
|
|
|
2377
2352
|
});
|
|
2378
2353
|
return;
|
|
2379
2354
|
}
|
|
2380
|
-
if (sub === "daemon") {
|
|
2381
|
-
// 使用 JavaScript daemon
|
|
2382
|
-
const EventBus = require("../../coordination/bus");
|
|
2383
|
-
const eventBus = new EventBus(process.cwd());
|
|
2384
|
-
|
|
2385
|
-
(async () => {
|
|
2386
|
-
try {
|
|
2387
|
-
const hasStop = rest.includes("--stop");
|
|
2388
|
-
const hasStatus = rest.includes("--status");
|
|
2389
|
-
const hasDaemon = rest.includes("--daemon");
|
|
2390
|
-
const intervalIdx = rest.indexOf("--interval");
|
|
2391
|
-
const interval = intervalIdx !== -1 ? parseInt(rest[intervalIdx + 1], 10) * 1000 : 2000;
|
|
2392
|
-
|
|
2393
|
-
if (hasStop) {
|
|
2394
|
-
await eventBus.daemon("stop");
|
|
2395
|
-
} else if (hasStatus) {
|
|
2396
|
-
await eventBus.daemon("status");
|
|
2397
|
-
} else {
|
|
2398
|
-
await eventBus.daemon("start", { background: hasDaemon, interval });
|
|
2399
|
-
}
|
|
2400
|
-
} catch (err) {
|
|
2401
|
-
console.error(err.message);
|
|
2402
|
-
process.exitCode = 1;
|
|
2403
|
-
}
|
|
2404
|
-
})();
|
|
2405
|
-
return;
|
|
2406
|
-
}
|
|
2407
2355
|
if (sub === "inject") {
|
|
2408
2356
|
// 使用 JavaScript inject
|
|
2409
2357
|
const EventBus = require("../../coordination/bus");
|