ai-whisper 0.10.0 → 0.11.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/bin/broker-daemon.js +44 -6
- package/dist/bin/companion-agent.js +44 -6
- package/dist/bin/relay-monitor.js +44 -6
- package/dist/bin/whisper.js +555 -58
- package/package.json +5 -5
|
@@ -633,6 +633,40 @@ function countActiveWorkflowsForCollab(db, collabId2) {
|
|
|
633
633
|
const row = db.prepare("SELECT COUNT(*) AS n FROM workflows WHERE collab_id = ? AND status IN ('running', 'paused')").get(collabId2);
|
|
634
634
|
return row.n;
|
|
635
635
|
}
|
|
636
|
+
var COUNTED_STATUSES = ["done", "halted"];
|
|
637
|
+
function getHandsOffStats(db) {
|
|
638
|
+
const placeholders = COUNTED_STATUSES.map(() => "?").join(",");
|
|
639
|
+
const rows = db.prepare(`SELECT status, created_at, updated_at
|
|
640
|
+
FROM workflows
|
|
641
|
+
WHERE status IN (${placeholders})`).all(...COUNTED_STATUSES);
|
|
642
|
+
const stats = {
|
|
643
|
+
totalMs: 0,
|
|
644
|
+
count: 0,
|
|
645
|
+
byStatus: {
|
|
646
|
+
done: { count: 0, totalMs: 0 },
|
|
647
|
+
halted: { count: 0, totalMs: 0 }
|
|
648
|
+
},
|
|
649
|
+
earliestKickoffAt: null,
|
|
650
|
+
skipped: 0
|
|
651
|
+
};
|
|
652
|
+
for (const row of rows) {
|
|
653
|
+
const start = Date.parse(row.created_at);
|
|
654
|
+
const end = Date.parse(row.updated_at);
|
|
655
|
+
if (Number.isNaN(start) || Number.isNaN(end)) {
|
|
656
|
+
stats.skipped += 1;
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
const elapsed = Math.max(0, end - start);
|
|
660
|
+
stats.totalMs += elapsed;
|
|
661
|
+
stats.count += 1;
|
|
662
|
+
stats.byStatus[row.status].count += 1;
|
|
663
|
+
stats.byStatus[row.status].totalMs += elapsed;
|
|
664
|
+
if (stats.earliestKickoffAt === null || row.created_at < stats.earliestKickoffAt) {
|
|
665
|
+
stats.earliestKickoffAt = row.created_at;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return stats;
|
|
669
|
+
}
|
|
636
670
|
|
|
637
671
|
// ../broker/dist/runtime/workspace-snapshot.js
|
|
638
672
|
import { execFileSync } from "node:child_process";
|
|
@@ -4352,6 +4386,9 @@ function createControlService(db, events) {
|
|
|
4352
4386
|
...now !== void 0 ? { now } : {}
|
|
4353
4387
|
});
|
|
4354
4388
|
},
|
|
4389
|
+
getHandsOffStats() {
|
|
4390
|
+
return getHandsOffStats(db);
|
|
4391
|
+
},
|
|
4355
4392
|
listRunCostRows(collabId2, workflowId) {
|
|
4356
4393
|
return listRunCostRows(db, { collabId: collabId2, workflowId });
|
|
4357
4394
|
},
|
|
@@ -5310,19 +5347,20 @@ async function sweepStaleBrokerDaemons(input) {
|
|
|
5310
5347
|
}
|
|
5311
5348
|
return { deleted };
|
|
5312
5349
|
}
|
|
5313
|
-
function
|
|
5350
|
+
function isPidAlive(pid) {
|
|
5314
5351
|
try {
|
|
5315
5352
|
process.kill(pid, 0);
|
|
5316
|
-
return
|
|
5353
|
+
return true;
|
|
5317
5354
|
} catch (err) {
|
|
5318
5355
|
const code = err.code;
|
|
5319
|
-
if (code === "ESRCH")
|
|
5320
|
-
return Promise.resolve({ alive: false, startTime: null });
|
|
5321
5356
|
if (code === "EPERM")
|
|
5322
|
-
return
|
|
5323
|
-
return
|
|
5357
|
+
return true;
|
|
5358
|
+
return false;
|
|
5324
5359
|
}
|
|
5325
5360
|
}
|
|
5361
|
+
function defaultIsAlive(pid) {
|
|
5362
|
+
return Promise.resolve({ alive: isPidAlive(pid), startTime: null });
|
|
5363
|
+
}
|
|
5326
5364
|
|
|
5327
5365
|
// ../broker/dist/runtime/daemon-heartbeat.js
|
|
5328
5366
|
function createDaemonHeartbeat(input) {
|
|
@@ -666,6 +666,40 @@ function countActiveWorkflowsForCollab(db, collabId) {
|
|
|
666
666
|
const row = db.prepare("SELECT COUNT(*) AS n FROM workflows WHERE collab_id = ? AND status IN ('running', 'paused')").get(collabId);
|
|
667
667
|
return row.n;
|
|
668
668
|
}
|
|
669
|
+
var COUNTED_STATUSES = ["done", "halted"];
|
|
670
|
+
function getHandsOffStats(db) {
|
|
671
|
+
const placeholders = COUNTED_STATUSES.map(() => "?").join(",");
|
|
672
|
+
const rows = db.prepare(`SELECT status, created_at, updated_at
|
|
673
|
+
FROM workflows
|
|
674
|
+
WHERE status IN (${placeholders})`).all(...COUNTED_STATUSES);
|
|
675
|
+
const stats = {
|
|
676
|
+
totalMs: 0,
|
|
677
|
+
count: 0,
|
|
678
|
+
byStatus: {
|
|
679
|
+
done: { count: 0, totalMs: 0 },
|
|
680
|
+
halted: { count: 0, totalMs: 0 }
|
|
681
|
+
},
|
|
682
|
+
earliestKickoffAt: null,
|
|
683
|
+
skipped: 0
|
|
684
|
+
};
|
|
685
|
+
for (const row of rows) {
|
|
686
|
+
const start = Date.parse(row.created_at);
|
|
687
|
+
const end = Date.parse(row.updated_at);
|
|
688
|
+
if (Number.isNaN(start) || Number.isNaN(end)) {
|
|
689
|
+
stats.skipped += 1;
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
const elapsed = Math.max(0, end - start);
|
|
693
|
+
stats.totalMs += elapsed;
|
|
694
|
+
stats.count += 1;
|
|
695
|
+
stats.byStatus[row.status].count += 1;
|
|
696
|
+
stats.byStatus[row.status].totalMs += elapsed;
|
|
697
|
+
if (stats.earliestKickoffAt === null || row.created_at < stats.earliestKickoffAt) {
|
|
698
|
+
stats.earliestKickoffAt = row.created_at;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return stats;
|
|
702
|
+
}
|
|
669
703
|
|
|
670
704
|
// ../broker/dist/runtime/workspace-snapshot.js
|
|
671
705
|
import { execFileSync } from "node:child_process";
|
|
@@ -4372,6 +4406,9 @@ function createControlService(db, events) {
|
|
|
4372
4406
|
...now !== void 0 ? { now } : {}
|
|
4373
4407
|
});
|
|
4374
4408
|
},
|
|
4409
|
+
getHandsOffStats() {
|
|
4410
|
+
return getHandsOffStats(db);
|
|
4411
|
+
},
|
|
4375
4412
|
listRunCostRows(collabId, workflowId) {
|
|
4376
4413
|
return listRunCostRows(db, { collabId, workflowId });
|
|
4377
4414
|
},
|
|
@@ -5320,19 +5357,20 @@ async function sweepStaleBrokerDaemons(input) {
|
|
|
5320
5357
|
}
|
|
5321
5358
|
return { deleted };
|
|
5322
5359
|
}
|
|
5323
|
-
function
|
|
5360
|
+
function isPidAlive(pid) {
|
|
5324
5361
|
try {
|
|
5325
5362
|
process.kill(pid, 0);
|
|
5326
|
-
return
|
|
5363
|
+
return true;
|
|
5327
5364
|
} catch (err) {
|
|
5328
5365
|
const code = err.code;
|
|
5329
|
-
if (code === "ESRCH")
|
|
5330
|
-
return Promise.resolve({ alive: false, startTime: null });
|
|
5331
5366
|
if (code === "EPERM")
|
|
5332
|
-
return
|
|
5333
|
-
return
|
|
5367
|
+
return true;
|
|
5368
|
+
return false;
|
|
5334
5369
|
}
|
|
5335
5370
|
}
|
|
5371
|
+
function defaultIsAlive(pid) {
|
|
5372
|
+
return Promise.resolve({ alive: isPidAlive(pid), startTime: null });
|
|
5373
|
+
}
|
|
5336
5374
|
|
|
5337
5375
|
// ../broker/dist/runtime/daemon-heartbeat.js
|
|
5338
5376
|
function createDaemonHeartbeat(input) {
|
|
@@ -636,6 +636,40 @@ function countActiveWorkflowsForCollab(db, collabId2) {
|
|
|
636
636
|
const row = db.prepare("SELECT COUNT(*) AS n FROM workflows WHERE collab_id = ? AND status IN ('running', 'paused')").get(collabId2);
|
|
637
637
|
return row.n;
|
|
638
638
|
}
|
|
639
|
+
var COUNTED_STATUSES = ["done", "halted"];
|
|
640
|
+
function getHandsOffStats(db) {
|
|
641
|
+
const placeholders = COUNTED_STATUSES.map(() => "?").join(",");
|
|
642
|
+
const rows = db.prepare(`SELECT status, created_at, updated_at
|
|
643
|
+
FROM workflows
|
|
644
|
+
WHERE status IN (${placeholders})`).all(...COUNTED_STATUSES);
|
|
645
|
+
const stats = {
|
|
646
|
+
totalMs: 0,
|
|
647
|
+
count: 0,
|
|
648
|
+
byStatus: {
|
|
649
|
+
done: { count: 0, totalMs: 0 },
|
|
650
|
+
halted: { count: 0, totalMs: 0 }
|
|
651
|
+
},
|
|
652
|
+
earliestKickoffAt: null,
|
|
653
|
+
skipped: 0
|
|
654
|
+
};
|
|
655
|
+
for (const row of rows) {
|
|
656
|
+
const start = Date.parse(row.created_at);
|
|
657
|
+
const end = Date.parse(row.updated_at);
|
|
658
|
+
if (Number.isNaN(start) || Number.isNaN(end)) {
|
|
659
|
+
stats.skipped += 1;
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
const elapsed = Math.max(0, end - start);
|
|
663
|
+
stats.totalMs += elapsed;
|
|
664
|
+
stats.count += 1;
|
|
665
|
+
stats.byStatus[row.status].count += 1;
|
|
666
|
+
stats.byStatus[row.status].totalMs += elapsed;
|
|
667
|
+
if (stats.earliestKickoffAt === null || row.created_at < stats.earliestKickoffAt) {
|
|
668
|
+
stats.earliestKickoffAt = row.created_at;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return stats;
|
|
672
|
+
}
|
|
639
673
|
|
|
640
674
|
// ../broker/dist/runtime/workspace-snapshot.js
|
|
641
675
|
import { execFileSync } from "node:child_process";
|
|
@@ -4342,6 +4376,9 @@ function createControlService(db, events) {
|
|
|
4342
4376
|
...now !== void 0 ? { now } : {}
|
|
4343
4377
|
});
|
|
4344
4378
|
},
|
|
4379
|
+
getHandsOffStats() {
|
|
4380
|
+
return getHandsOffStats(db);
|
|
4381
|
+
},
|
|
4345
4382
|
listRunCostRows(collabId2, workflowId) {
|
|
4346
4383
|
return listRunCostRows(db, { collabId: collabId2, workflowId });
|
|
4347
4384
|
},
|
|
@@ -5294,19 +5331,20 @@ async function sweepStaleBrokerDaemons(input) {
|
|
|
5294
5331
|
}
|
|
5295
5332
|
return { deleted };
|
|
5296
5333
|
}
|
|
5297
|
-
function
|
|
5334
|
+
function isPidAlive(pid) {
|
|
5298
5335
|
try {
|
|
5299
5336
|
process.kill(pid, 0);
|
|
5300
|
-
return
|
|
5337
|
+
return true;
|
|
5301
5338
|
} catch (err) {
|
|
5302
5339
|
const code = err.code;
|
|
5303
|
-
if (code === "ESRCH")
|
|
5304
|
-
return Promise.resolve({ alive: false, startTime: null });
|
|
5305
5340
|
if (code === "EPERM")
|
|
5306
|
-
return
|
|
5307
|
-
return
|
|
5341
|
+
return true;
|
|
5342
|
+
return false;
|
|
5308
5343
|
}
|
|
5309
5344
|
}
|
|
5345
|
+
function defaultIsAlive(pid) {
|
|
5346
|
+
return Promise.resolve({ alive: isPidAlive(pid), startTime: null });
|
|
5347
|
+
}
|
|
5310
5348
|
|
|
5311
5349
|
// ../broker/dist/runtime/daemon-heartbeat.js
|
|
5312
5350
|
function createDaemonHeartbeat(input) {
|
package/dist/bin/whisper.js
CHANGED
|
@@ -821,6 +821,46 @@ function insertCollab(db, collab) {
|
|
|
821
821
|
orchestrator_max_rounds
|
|
822
822
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(collab.collabId, collab.workspaceRoot, collab.displayName, collab.status, collab.createdAt, collab.updatedAt, collab.orchestratorEnabled ? 1 : 0, collab.orchestratorMaxRounds);
|
|
823
823
|
}
|
|
824
|
+
function listAllCollabs(db) {
|
|
825
|
+
const rows = db.prepare(`SELECT collab_id, workspace_root, workspace_id, status, tmux_session
|
|
826
|
+
FROM collab
|
|
827
|
+
ORDER BY created_at`).all();
|
|
828
|
+
return rows.map((r) => ({
|
|
829
|
+
collabId: r.collab_id,
|
|
830
|
+
workspaceRoot: r.workspace_root,
|
|
831
|
+
workspaceId: r.workspace_id,
|
|
832
|
+
status: r.status,
|
|
833
|
+
tmuxSession: r.tmux_session
|
|
834
|
+
}));
|
|
835
|
+
}
|
|
836
|
+
function tableHasCollabIdColumn(db, table) {
|
|
837
|
+
const cols = db.prepare(`PRAGMA table_info("${table}")`).all();
|
|
838
|
+
return cols.some((c) => c.name === "collab_id");
|
|
839
|
+
}
|
|
840
|
+
function listCollabIdTables(db) {
|
|
841
|
+
const names = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name").all().map((r) => r.name);
|
|
842
|
+
return names.filter((name) => name !== "collab" && tableHasCollabIdColumn(db, name));
|
|
843
|
+
}
|
|
844
|
+
function countCollabRows(db, collabId) {
|
|
845
|
+
let total = 0;
|
|
846
|
+
for (const table of listCollabIdTables(db)) {
|
|
847
|
+
total += db.prepare(`SELECT COUNT(*) AS n FROM "${table}" WHERE collab_id = ?`).get(collabId).n;
|
|
848
|
+
}
|
|
849
|
+
total += db.prepare("SELECT COUNT(*) AS n FROM workflow_phases WHERE workflow_id IN (SELECT workflow_id FROM workflows WHERE collab_id = ?)").get(collabId).n;
|
|
850
|
+
total += db.prepare("SELECT COUNT(*) AS n FROM work_item_cancellation WHERE work_item_id IN (SELECT work_item_id FROM work_item WHERE collab_id = ?)").get(collabId).n;
|
|
851
|
+
total += db.prepare("SELECT COUNT(*) AS n FROM clipboard_capture_lease WHERE holder_collab_id = ?").get(collabId).n;
|
|
852
|
+
total += db.prepare("SELECT COUNT(*) AS n FROM collab WHERE collab_id = ?").get(collabId).n;
|
|
853
|
+
return total;
|
|
854
|
+
}
|
|
855
|
+
function deleteCollabCascade(db, collabId) {
|
|
856
|
+
db.prepare("DELETE FROM workflow_phases WHERE workflow_id IN (SELECT workflow_id FROM workflows WHERE collab_id = ?)").run(collabId);
|
|
857
|
+
db.prepare("DELETE FROM work_item_cancellation WHERE work_item_id IN (SELECT work_item_id FROM work_item WHERE collab_id = ?)").run(collabId);
|
|
858
|
+
for (const table of listCollabIdTables(db)) {
|
|
859
|
+
db.prepare(`DELETE FROM "${table}" WHERE collab_id = ?`).run(collabId);
|
|
860
|
+
}
|
|
861
|
+
db.prepare("DELETE FROM clipboard_capture_lease WHERE holder_collab_id = ?").run(collabId);
|
|
862
|
+
db.prepare("DELETE FROM collab WHERE collab_id = ?").run(collabId);
|
|
863
|
+
}
|
|
824
864
|
function getCollab(db, collabId) {
|
|
825
865
|
const row = db.prepare(`SELECT collab_id, workspace_root, display_name, status, created_at, updated_at,
|
|
826
866
|
orchestrator_enabled, orchestrator_max_rounds
|
|
@@ -912,9 +952,52 @@ function countActiveWorkflowsForCollab(db, collabId) {
|
|
|
912
952
|
const row = db.prepare("SELECT COUNT(*) AS n FROM workflows WHERE collab_id = ? AND status IN ('running', 'paused')").get(collabId);
|
|
913
953
|
return row.n;
|
|
914
954
|
}
|
|
955
|
+
function findNonTerminalWorkflow(db, collabId) {
|
|
956
|
+
const row = db.prepare(`SELECT workflow_id, status
|
|
957
|
+
FROM workflows
|
|
958
|
+
WHERE collab_id = ? AND status NOT IN ('done', 'canceled')
|
|
959
|
+
ORDER BY created_at DESC
|
|
960
|
+
LIMIT 1`).get(collabId);
|
|
961
|
+
return row ? { workflowId: row.workflow_id, status: row.status } : null;
|
|
962
|
+
}
|
|
963
|
+
function getHandsOffStats(db) {
|
|
964
|
+
const placeholders = COUNTED_STATUSES.map(() => "?").join(",");
|
|
965
|
+
const rows = db.prepare(`SELECT status, created_at, updated_at
|
|
966
|
+
FROM workflows
|
|
967
|
+
WHERE status IN (${placeholders})`).all(...COUNTED_STATUSES);
|
|
968
|
+
const stats = {
|
|
969
|
+
totalMs: 0,
|
|
970
|
+
count: 0,
|
|
971
|
+
byStatus: {
|
|
972
|
+
done: { count: 0, totalMs: 0 },
|
|
973
|
+
halted: { count: 0, totalMs: 0 }
|
|
974
|
+
},
|
|
975
|
+
earliestKickoffAt: null,
|
|
976
|
+
skipped: 0
|
|
977
|
+
};
|
|
978
|
+
for (const row of rows) {
|
|
979
|
+
const start = Date.parse(row.created_at);
|
|
980
|
+
const end = Date.parse(row.updated_at);
|
|
981
|
+
if (Number.isNaN(start) || Number.isNaN(end)) {
|
|
982
|
+
stats.skipped += 1;
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
const elapsed = Math.max(0, end - start);
|
|
986
|
+
stats.totalMs += elapsed;
|
|
987
|
+
stats.count += 1;
|
|
988
|
+
stats.byStatus[row.status].count += 1;
|
|
989
|
+
stats.byStatus[row.status].totalMs += elapsed;
|
|
990
|
+
if (stats.earliestKickoffAt === null || row.created_at < stats.earliestKickoffAt) {
|
|
991
|
+
stats.earliestKickoffAt = row.created_at;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
return stats;
|
|
995
|
+
}
|
|
996
|
+
var COUNTED_STATUSES;
|
|
915
997
|
var init_workflow_repository = __esm({
|
|
916
998
|
"../broker/dist/storage/repositories/workflow-repository.js"() {
|
|
917
999
|
"use strict";
|
|
1000
|
+
COUNTED_STATUSES = ["done", "halted"];
|
|
918
1001
|
}
|
|
919
1002
|
});
|
|
920
1003
|
|
|
@@ -4773,6 +4856,9 @@ function createControlService(db, events) {
|
|
|
4773
4856
|
...now !== void 0 ? { now } : {}
|
|
4774
4857
|
});
|
|
4775
4858
|
},
|
|
4859
|
+
getHandsOffStats() {
|
|
4860
|
+
return getHandsOffStats(db);
|
|
4861
|
+
},
|
|
4776
4862
|
listRunCostRows(collabId, workflowId) {
|
|
4777
4863
|
return listRunCostRows(db, { collabId, workflowId });
|
|
4778
4864
|
},
|
|
@@ -5836,19 +5922,20 @@ async function sweepStaleBrokerDaemons(input) {
|
|
|
5836
5922
|
}
|
|
5837
5923
|
return { deleted };
|
|
5838
5924
|
}
|
|
5839
|
-
function
|
|
5925
|
+
function isPidAlive(pid) {
|
|
5840
5926
|
try {
|
|
5841
5927
|
process.kill(pid, 0);
|
|
5842
|
-
return
|
|
5928
|
+
return true;
|
|
5843
5929
|
} catch (err) {
|
|
5844
5930
|
const code = err.code;
|
|
5845
|
-
if (code === "ESRCH")
|
|
5846
|
-
return Promise.resolve({ alive: false, startTime: null });
|
|
5847
5931
|
if (code === "EPERM")
|
|
5848
|
-
return
|
|
5849
|
-
return
|
|
5932
|
+
return true;
|
|
5933
|
+
return false;
|
|
5850
5934
|
}
|
|
5851
5935
|
}
|
|
5936
|
+
function defaultIsAlive(pid) {
|
|
5937
|
+
return Promise.resolve({ alive: isPidAlive(pid), startTime: null });
|
|
5938
|
+
}
|
|
5852
5939
|
var init_broker_daemon_sweep = __esm({
|
|
5853
5940
|
"../broker/dist/runtime/broker-daemon-sweep.js"() {
|
|
5854
5941
|
"use strict";
|
|
@@ -6490,6 +6577,8 @@ var init_dist2 = __esm({
|
|
|
6490
6577
|
init_session_attachment_repository();
|
|
6491
6578
|
init_broker_daemon_repository();
|
|
6492
6579
|
init_recovery_state_repository();
|
|
6580
|
+
init_collab_repository();
|
|
6581
|
+
init_workflow_repository();
|
|
6493
6582
|
}
|
|
6494
6583
|
});
|
|
6495
6584
|
|
|
@@ -6582,6 +6671,15 @@ function fmtDur(ms) {
|
|
|
6582
6671
|
const m = Math.floor(s / 60);
|
|
6583
6672
|
return m > 0 ? `${m}m${String(s % 60).padStart(2, "0")}s` : `${s}s`;
|
|
6584
6673
|
}
|
|
6674
|
+
function fmtDurCoarse(ms) {
|
|
6675
|
+
const totalMin = Math.floor(Math.max(0, ms) / 6e4);
|
|
6676
|
+
const d = Math.floor(totalMin / 1440);
|
|
6677
|
+
const h = Math.floor(totalMin % 1440 / 60);
|
|
6678
|
+
const m = totalMin % 60;
|
|
6679
|
+
if (d > 0) return `${d}d ${h}h`;
|
|
6680
|
+
if (h > 0) return `${h}h ${m}m`;
|
|
6681
|
+
return `${m}m`;
|
|
6682
|
+
}
|
|
6585
6683
|
function isOkOutcome(outcome) {
|
|
6586
6684
|
if (!outcome) return false;
|
|
6587
6685
|
return !/escalat|halt|fail|cancel/i.test(outcome);
|
|
@@ -7088,10 +7186,13 @@ function Wall(props) {
|
|
|
7088
7186
|
const paneWidth = Math.floor(props.cols / colsCount);
|
|
7089
7187
|
let globalIdx = 0;
|
|
7090
7188
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: props.cols, children: [
|
|
7091
|
-
props.counts ? /* @__PURE__ */
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7189
|
+
props.counts ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
7190
|
+
SUMMARY_SEGMENTS.map((seg, i) => {
|
|
7191
|
+
const n = props.counts[seg.key];
|
|
7192
|
+
return /* @__PURE__ */ jsx3(Text2, { color: n > 0 ? seg.color : THEME.muted, children: (i > 0 ? " " : "") + `${seg.glyph} ${n} ${seg.label}` }, seg.key);
|
|
7193
|
+
}),
|
|
7194
|
+
/* @__PURE__ */ jsx3(Text2, { color: THEME.muted, children: ` \u2502 hands-off saved ${fmtDurCoarse(state.handsOff.totalMs)} (or ${Math.floor(state.handsOff.totalMs / 36e5)}h) (${state.handsOff.count} wf runs)` })
|
|
7195
|
+
] }) : null,
|
|
7095
7196
|
state.sections.map((sec) => {
|
|
7096
7197
|
const rows = [];
|
|
7097
7198
|
for (let i = 0; i < sec.panes.length; i += colsCount) {
|
|
@@ -7693,7 +7794,8 @@ function buildWallState(input) {
|
|
|
7693
7794
|
page: alloc.page,
|
|
7694
7795
|
pageCount: alloc.pageCount,
|
|
7695
7796
|
totalRuns: alloc.totalRuns,
|
|
7696
|
-
selected
|
|
7797
|
+
selected,
|
|
7798
|
+
handsOff: input.handsOff ?? { totalMs: 0, count: 0 }
|
|
7697
7799
|
};
|
|
7698
7800
|
}
|
|
7699
7801
|
var MIN_PANE_COLS2, CARD_HEIGHT, HEADER_ROWS, GROUP_ORDER, GROUP_LABEL;
|
|
@@ -8035,6 +8137,7 @@ function createDashboardRuntime(input) {
|
|
|
8035
8137
|
mountAlive: mountAliveOf(sess.agentType)
|
|
8036
8138
|
}));
|
|
8037
8139
|
}
|
|
8140
|
+
const handsOff = c.getHandsOffStats();
|
|
8038
8141
|
const wallState = buildWallState({
|
|
8039
8142
|
summaries,
|
|
8040
8143
|
now: isoNow,
|
|
@@ -8043,7 +8146,8 @@ function createDashboardRuntime(input) {
|
|
|
8043
8146
|
rows,
|
|
8044
8147
|
page: wallPage,
|
|
8045
8148
|
selected: wallSelected,
|
|
8046
|
-
snapshots
|
|
8149
|
+
snapshots,
|
|
8150
|
+
handsOff: { totalMs: handsOff.totalMs, count: handsOff.count }
|
|
8047
8151
|
});
|
|
8048
8152
|
wallPage = wallState.page;
|
|
8049
8153
|
wallSelected = wallState.selected;
|
|
@@ -8238,7 +8342,8 @@ function loadDotEnv() {
|
|
|
8238
8342
|
}
|
|
8239
8343
|
|
|
8240
8344
|
// src/create-cli.ts
|
|
8241
|
-
|
|
8345
|
+
init_dist2();
|
|
8346
|
+
import { execSync as execSync5 } from "node:child_process";
|
|
8242
8347
|
import { Command, Option } from "commander";
|
|
8243
8348
|
|
|
8244
8349
|
// src/runtime/wait-for-broker-ready.ts
|
|
@@ -13934,8 +14039,8 @@ function compareSemver(a, b) {
|
|
|
13934
14039
|
var EZIO_PROVENANCE = {
|
|
13935
14040
|
ezioCliVersion: "0.3.0",
|
|
13936
14041
|
ezioGitSha: "ffd95dc",
|
|
13937
|
-
builtAt: "2026-
|
|
13938
|
-
whisperVersion: "0.
|
|
14042
|
+
builtAt: "2026-07-01T04:07:32.417Z",
|
|
14043
|
+
whisperVersion: "0.11.0"
|
|
13939
14044
|
};
|
|
13940
14045
|
|
|
13941
14046
|
// src/ezio-provenance-types.ts
|
|
@@ -17862,6 +17967,250 @@ function runCollabStop(input) {
|
|
|
17862
17967
|
}
|
|
17863
17968
|
}
|
|
17864
17969
|
|
|
17970
|
+
// src/commands/collab/purge.ts
|
|
17971
|
+
init_dist2();
|
|
17972
|
+
init_dist2();
|
|
17973
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
17974
|
+
import { createInterface } from "node:readline/promises";
|
|
17975
|
+
function isCollabLive(db, collabId, isAlive) {
|
|
17976
|
+
const daemon = getBrokerDaemonByCollab(db, collabId);
|
|
17977
|
+
if (daemon?.pid != null && isAlive(daemon.pid)) return true;
|
|
17978
|
+
return listSessionAttachmentsByCollab(db, collabId).some(
|
|
17979
|
+
(a) => a.pid != null && isAlive(a.pid)
|
|
17980
|
+
);
|
|
17981
|
+
}
|
|
17982
|
+
function classifyCollab(db, collab, isAlive) {
|
|
17983
|
+
const base = {
|
|
17984
|
+
collabId: collab.collabId,
|
|
17985
|
+
workspaceRoot: collab.workspaceRoot,
|
|
17986
|
+
workspaceId: collab.workspaceId,
|
|
17987
|
+
status: collab.status,
|
|
17988
|
+
tmuxSession: collab.tmuxSession,
|
|
17989
|
+
rowCount: countCollabRows(db, collab.collabId)
|
|
17990
|
+
};
|
|
17991
|
+
const daemon = getBrokerDaemonByCollab(db, collab.collabId);
|
|
17992
|
+
const daemonAlive = daemon?.pid != null && isAlive(daemon.pid);
|
|
17993
|
+
const mountAlive = listSessionAttachmentsByCollab(db, collab.collabId).some(
|
|
17994
|
+
(a) => a.pid != null && isAlive(a.pid)
|
|
17995
|
+
);
|
|
17996
|
+
if (daemonAlive || mountAlive) {
|
|
17997
|
+
return {
|
|
17998
|
+
...base,
|
|
17999
|
+
bucket: "live",
|
|
18000
|
+
reason: daemonAlive ? "live-daemon" : "live-mount"
|
|
18001
|
+
};
|
|
18002
|
+
}
|
|
18003
|
+
const wf = findNonTerminalWorkflow(db, collab.collabId);
|
|
18004
|
+
if (wf) {
|
|
18005
|
+
return {
|
|
18006
|
+
...base,
|
|
18007
|
+
bucket: "protected",
|
|
18008
|
+
reason: `workflow ${wf.status}`,
|
|
18009
|
+
workflowId: wf.workflowId,
|
|
18010
|
+
workflowStatus: wf.status
|
|
18011
|
+
};
|
|
18012
|
+
}
|
|
18013
|
+
const reason = collab.status === "stopped" ? "stopped" : daemon ? "dead-daemon" : "dead-mounts";
|
|
18014
|
+
return { ...base, bucket: "stale", reason };
|
|
18015
|
+
}
|
|
18016
|
+
function classifyAllCollabs(db, collabs, isAlive) {
|
|
18017
|
+
return collabs.map((c) => classifyCollab(db, c, isAlive));
|
|
18018
|
+
}
|
|
18019
|
+
function defaultConfirm(promptText) {
|
|
18020
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
18021
|
+
return rl.question(`${promptText} [y/N] `).then((answer) => /^y(es)?$/i.test(answer.trim())).finally(() => rl.close());
|
|
18022
|
+
}
|
|
18023
|
+
function defaultKillTmuxSession(session) {
|
|
18024
|
+
try {
|
|
18025
|
+
const escaped = session.replace(/'/g, "'\\''");
|
|
18026
|
+
execSync3(`tmux kill-session -t '${escaped}'`, { stdio: "ignore" });
|
|
18027
|
+
} catch {
|
|
18028
|
+
}
|
|
18029
|
+
}
|
|
18030
|
+
function buildJsonPayload(classifications, purged, skippedWentLive, skippedError, protectedSkipped, aborted) {
|
|
18031
|
+
return {
|
|
18032
|
+
classifications,
|
|
18033
|
+
purged,
|
|
18034
|
+
skipped: { wentLive: skippedWentLive, error: skippedError },
|
|
18035
|
+
protected: protectedSkipped,
|
|
18036
|
+
aborted
|
|
18037
|
+
};
|
|
18038
|
+
}
|
|
18039
|
+
function renderTable(classifications, log) {
|
|
18040
|
+
if (classifications.length === 0) {
|
|
18041
|
+
log("No collabs found.");
|
|
18042
|
+
return;
|
|
18043
|
+
}
|
|
18044
|
+
log(
|
|
18045
|
+
"COLLAB BUCKET REASON ROWS WORKSPACE"
|
|
18046
|
+
);
|
|
18047
|
+
for (const c of classifications) {
|
|
18048
|
+
const wf = c.workflowId ? ` [${c.workflowId} ${c.workflowStatus}]` : "";
|
|
18049
|
+
log(
|
|
18050
|
+
`${c.collabId.padEnd(31)} ${c.bucket.padEnd(10)} ${c.reason.padEnd(16)} ${String(c.rowCount).padStart(4)} ${c.workspaceRoot}${wf}`
|
|
18051
|
+
);
|
|
18052
|
+
}
|
|
18053
|
+
}
|
|
18054
|
+
async function runCollabPurge(opts) {
|
|
18055
|
+
if (opts.collabId && opts.workspace) {
|
|
18056
|
+
throw new Error("--collab and --workspace are mutually exclusive");
|
|
18057
|
+
}
|
|
18058
|
+
const isAlive = opts.isAlive ?? isPidAlive;
|
|
18059
|
+
const log = opts.log ?? ((line) => console.log(line));
|
|
18060
|
+
const isTTY = opts.isTTY ?? Boolean(process.stdin.isTTY);
|
|
18061
|
+
const confirm = opts.confirm ?? defaultConfirm;
|
|
18062
|
+
const killTmux = opts.killTmuxSession ?? defaultKillTmuxSession;
|
|
18063
|
+
const empty = (exitCode, classifications = [], protectedSkipped = [], aborted = false) => ({
|
|
18064
|
+
classifications,
|
|
18065
|
+
purged: [],
|
|
18066
|
+
skippedWentLive: [],
|
|
18067
|
+
skippedError: [],
|
|
18068
|
+
protectedSkipped,
|
|
18069
|
+
aborted,
|
|
18070
|
+
exitCode
|
|
18071
|
+
});
|
|
18072
|
+
let workspaceFilter = null;
|
|
18073
|
+
if (opts.workspace) {
|
|
18074
|
+
try {
|
|
18075
|
+
workspaceFilter = workspaceIdFromPath(opts.workspace);
|
|
18076
|
+
} catch {
|
|
18077
|
+
log(`Cannot resolve --workspace path: ${opts.workspace}`);
|
|
18078
|
+
return empty(1);
|
|
18079
|
+
}
|
|
18080
|
+
}
|
|
18081
|
+
const db = openDatabase(getSharedSqlitePath());
|
|
18082
|
+
applyMigrations(db);
|
|
18083
|
+
try {
|
|
18084
|
+
let collabs = listAllCollabs(db);
|
|
18085
|
+
if (opts.collabId)
|
|
18086
|
+
collabs = collabs.filter((c) => c.collabId === opts.collabId);
|
|
18087
|
+
if (workspaceFilter)
|
|
18088
|
+
collabs = collabs.filter((c) => c.workspaceId === workspaceFilter);
|
|
18089
|
+
const classifications = classifyAllCollabs(db, collabs, isAlive);
|
|
18090
|
+
const protectedSkipped = classifications.filter((c) => c.bucket === "protected").map((c) => c.collabId);
|
|
18091
|
+
const candidates = classifications.filter(
|
|
18092
|
+
(c) => c.bucket === "stale" || opts.force === true && c.bucket === "protected"
|
|
18093
|
+
);
|
|
18094
|
+
if (!opts.json) {
|
|
18095
|
+
renderTable(classifications, log);
|
|
18096
|
+
}
|
|
18097
|
+
if (candidates.length === 0) {
|
|
18098
|
+
if (opts.json) {
|
|
18099
|
+
log(
|
|
18100
|
+
JSON.stringify(
|
|
18101
|
+
buildJsonPayload(
|
|
18102
|
+
classifications,
|
|
18103
|
+
[],
|
|
18104
|
+
[],
|
|
18105
|
+
[],
|
|
18106
|
+
protectedSkipped,
|
|
18107
|
+
false
|
|
18108
|
+
),
|
|
18109
|
+
null,
|
|
18110
|
+
2
|
|
18111
|
+
)
|
|
18112
|
+
);
|
|
18113
|
+
} else {
|
|
18114
|
+
log("Nothing to purge.");
|
|
18115
|
+
}
|
|
18116
|
+
return empty(0, classifications, protectedSkipped);
|
|
18117
|
+
}
|
|
18118
|
+
const previewOnly = opts.dryRun === true || opts.json === true && opts.yes !== true;
|
|
18119
|
+
if (previewOnly) {
|
|
18120
|
+
if (opts.json) {
|
|
18121
|
+
log(
|
|
18122
|
+
JSON.stringify(
|
|
18123
|
+
buildJsonPayload(
|
|
18124
|
+
classifications,
|
|
18125
|
+
[],
|
|
18126
|
+
[],
|
|
18127
|
+
[],
|
|
18128
|
+
protectedSkipped,
|
|
18129
|
+
false
|
|
18130
|
+
),
|
|
18131
|
+
null,
|
|
18132
|
+
2
|
|
18133
|
+
)
|
|
18134
|
+
);
|
|
18135
|
+
} else {
|
|
18136
|
+
log(
|
|
18137
|
+
`Dry run \u2014 ${candidates.length} collab(s) would be purged. Nothing deleted.`
|
|
18138
|
+
);
|
|
18139
|
+
}
|
|
18140
|
+
return empty(0, classifications, protectedSkipped);
|
|
18141
|
+
}
|
|
18142
|
+
if (opts.yes !== true) {
|
|
18143
|
+
if (!isTTY) {
|
|
18144
|
+
log(
|
|
18145
|
+
`Refusing to delete ${candidates.length} collab(s) without confirmation. Re-run with --yes.`
|
|
18146
|
+
);
|
|
18147
|
+
return empty(1, classifications, protectedSkipped, true);
|
|
18148
|
+
}
|
|
18149
|
+
const ok = await confirm(`Delete these ${candidates.length} collab(s)?`);
|
|
18150
|
+
if (!ok) {
|
|
18151
|
+
log("Aborted. Nothing deleted.");
|
|
18152
|
+
return empty(0, classifications, protectedSkipped, true);
|
|
18153
|
+
}
|
|
18154
|
+
}
|
|
18155
|
+
const purged = [];
|
|
18156
|
+
const skippedWentLive = [];
|
|
18157
|
+
const skippedError = [];
|
|
18158
|
+
const cascade = opts.deleteCascade ?? deleteCollabCascade;
|
|
18159
|
+
const purgeOne = db.transaction((collabId) => {
|
|
18160
|
+
if (isCollabLive(db, collabId, isAlive)) return false;
|
|
18161
|
+
cascade(db, collabId);
|
|
18162
|
+
return true;
|
|
18163
|
+
});
|
|
18164
|
+
for (const cand of candidates) {
|
|
18165
|
+
try {
|
|
18166
|
+
const deleted = purgeOne.immediate(cand.collabId);
|
|
18167
|
+
if (deleted) {
|
|
18168
|
+
purged.push(cand.collabId);
|
|
18169
|
+
if (cand.tmuxSession) killTmux(cand.tmuxSession);
|
|
18170
|
+
} else {
|
|
18171
|
+
skippedWentLive.push(cand.collabId);
|
|
18172
|
+
}
|
|
18173
|
+
} catch (err) {
|
|
18174
|
+
skippedError.push({
|
|
18175
|
+
collabId: cand.collabId,
|
|
18176
|
+
error: err.message
|
|
18177
|
+
});
|
|
18178
|
+
}
|
|
18179
|
+
}
|
|
18180
|
+
if (opts.json) {
|
|
18181
|
+
log(
|
|
18182
|
+
JSON.stringify(
|
|
18183
|
+
buildJsonPayload(
|
|
18184
|
+
classifications,
|
|
18185
|
+
purged,
|
|
18186
|
+
skippedWentLive,
|
|
18187
|
+
skippedError,
|
|
18188
|
+
protectedSkipped,
|
|
18189
|
+
false
|
|
18190
|
+
),
|
|
18191
|
+
null,
|
|
18192
|
+
2
|
|
18193
|
+
)
|
|
18194
|
+
);
|
|
18195
|
+
} else {
|
|
18196
|
+
log(
|
|
18197
|
+
`Purged ${purged.length}, skipped (went live) ${skippedWentLive.length}, errors ${skippedError.length}, protected ${protectedSkipped.length}.`
|
|
18198
|
+
);
|
|
18199
|
+
}
|
|
18200
|
+
return {
|
|
18201
|
+
classifications,
|
|
18202
|
+
purged,
|
|
18203
|
+
skippedWentLive,
|
|
18204
|
+
skippedError,
|
|
18205
|
+
protectedSkipped,
|
|
18206
|
+
aborted: false,
|
|
18207
|
+
exitCode: skippedError.length > 0 ? 1 : 0
|
|
18208
|
+
};
|
|
18209
|
+
} finally {
|
|
18210
|
+
db.close();
|
|
18211
|
+
}
|
|
18212
|
+
}
|
|
18213
|
+
|
|
17865
18214
|
// src/commands/collab/tell.ts
|
|
17866
18215
|
init_dist2();
|
|
17867
18216
|
init_dist();
|
|
@@ -18049,7 +18398,7 @@ async function runCollabTell(input) {
|
|
|
18049
18398
|
}
|
|
18050
18399
|
|
|
18051
18400
|
// src/runtime/launcher.ts
|
|
18052
|
-
import { execSync as
|
|
18401
|
+
import { execSync as execSync4, spawn as nodeSpawn2 } from "node:child_process";
|
|
18053
18402
|
import { dirname as dirname8, resolve as resolve4 } from "node:path";
|
|
18054
18403
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
18055
18404
|
var __dirname = dirname8(fileURLToPath5(import.meta.url));
|
|
@@ -18060,7 +18409,7 @@ function shellQuote(value) {
|
|
|
18060
18409
|
}
|
|
18061
18410
|
function detectTmux() {
|
|
18062
18411
|
try {
|
|
18063
|
-
|
|
18412
|
+
execSync4("which tmux", { stdio: "ignore" });
|
|
18064
18413
|
return true;
|
|
18065
18414
|
} catch {
|
|
18066
18415
|
return false;
|
|
@@ -18103,7 +18452,7 @@ function defaultSpawn(command) {
|
|
|
18103
18452
|
return child.pid;
|
|
18104
18453
|
}
|
|
18105
18454
|
function defaultExec(command) {
|
|
18106
|
-
|
|
18455
|
+
execSync4(command, { stdio: "inherit" });
|
|
18107
18456
|
}
|
|
18108
18457
|
function wrapForTerminalWindow(agentCommand, label) {
|
|
18109
18458
|
if (process.platform === "darwin") {
|
|
@@ -18323,6 +18672,53 @@ async function runWorkflowTypes() {
|
|
|
18323
18672
|
return listWorkflowTypes();
|
|
18324
18673
|
}
|
|
18325
18674
|
|
|
18675
|
+
// src/commands/workflow/stats.ts
|
|
18676
|
+
init_relay_view_state();
|
|
18677
|
+
function runWorkflowStats(deps) {
|
|
18678
|
+
const stats = deps.broker.control.getHandsOffStats();
|
|
18679
|
+
const out = deps.stdout ?? process.stdout;
|
|
18680
|
+
if (deps.json) {
|
|
18681
|
+
const payload = {
|
|
18682
|
+
totalMs: stats.totalMs,
|
|
18683
|
+
totalHuman: fmtDurCoarse(stats.totalMs),
|
|
18684
|
+
count: stats.count,
|
|
18685
|
+
since: stats.earliestKickoffAt,
|
|
18686
|
+
byStatus: {
|
|
18687
|
+
done: {
|
|
18688
|
+
count: stats.byStatus.done.count,
|
|
18689
|
+
totalMs: stats.byStatus.done.totalMs,
|
|
18690
|
+
human: fmtDurCoarse(stats.byStatus.done.totalMs)
|
|
18691
|
+
},
|
|
18692
|
+
halted: {
|
|
18693
|
+
count: stats.byStatus.halted.count,
|
|
18694
|
+
totalMs: stats.byStatus.halted.totalMs,
|
|
18695
|
+
human: fmtDurCoarse(stats.byStatus.halted.totalMs)
|
|
18696
|
+
}
|
|
18697
|
+
},
|
|
18698
|
+
skipped: stats.skipped
|
|
18699
|
+
};
|
|
18700
|
+
out.write(`${JSON.stringify(payload, null, 2)}
|
|
18701
|
+
`);
|
|
18702
|
+
return;
|
|
18703
|
+
}
|
|
18704
|
+
if (stats.count === 0) {
|
|
18705
|
+
out.write("Hands-off time saved: 0m (no completed workflows yet)\n");
|
|
18706
|
+
return;
|
|
18707
|
+
}
|
|
18708
|
+
const since = (stats.earliestKickoffAt ?? "").slice(0, 10);
|
|
18709
|
+
out.write(
|
|
18710
|
+
`Hands-off time saved: ${fmtDurCoarse(stats.totalMs)} (${stats.count} workflows, since ${since})
|
|
18711
|
+
`
|
|
18712
|
+
);
|
|
18713
|
+
for (const status of ["done", "halted"]) {
|
|
18714
|
+
const b = stats.byStatus[status];
|
|
18715
|
+
out.write(
|
|
18716
|
+
` ${status.padEnd(9)}${b.count} runs \xB7 ${fmtDurCoarse(b.totalMs)}
|
|
18717
|
+
`
|
|
18718
|
+
);
|
|
18719
|
+
}
|
|
18720
|
+
}
|
|
18721
|
+
|
|
18326
18722
|
// src/commands/skill/install.ts
|
|
18327
18723
|
import { cp, mkdir, readdir, stat } from "node:fs/promises";
|
|
18328
18724
|
import path3 from "node:path";
|
|
@@ -18548,9 +18944,7 @@ function createCli() {
|
|
|
18548
18944
|
launch
|
|
18549
18945
|
});
|
|
18550
18946
|
}
|
|
18551
|
-
console.log(
|
|
18552
|
-
`Collab started: ${r.collabId} (launch: ${launchMode})`
|
|
18553
|
-
);
|
|
18947
|
+
console.log(`Collab started: ${r.collabId} (launch: ${launchMode})`);
|
|
18554
18948
|
if (launchMode === "none") {
|
|
18555
18949
|
console.log("Collab started (no-launch mode).");
|
|
18556
18950
|
}
|
|
@@ -18563,7 +18957,10 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18563
18957
|
);
|
|
18564
18958
|
}
|
|
18565
18959
|
});
|
|
18566
|
-
collab.command("status").description("Show current collaboration status").option(
|
|
18960
|
+
collab.command("status").description("Show current collaboration status").option(
|
|
18961
|
+
"--collab <id>",
|
|
18962
|
+
"Inspect a specific collab id (defaults to the active collab for cwd)"
|
|
18963
|
+
).option("--json", "Emit machine-readable JSON instead of text").action((opts) => {
|
|
18567
18964
|
const output = runCollabStatus({
|
|
18568
18965
|
cwd: process.cwd(),
|
|
18569
18966
|
...opts.collab ? { collabIdOverride: opts.collab } : {},
|
|
@@ -18571,7 +18968,13 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18571
18968
|
});
|
|
18572
18969
|
console.log(output);
|
|
18573
18970
|
});
|
|
18574
|
-
collab.command("tell").description("Send an instruction to an agent").requiredOption(
|
|
18971
|
+
collab.command("tell").description("Send an instruction to an agent").requiredOption(
|
|
18972
|
+
"--target <agent>",
|
|
18973
|
+
"Target agent: codex, claude, ezio, or agy"
|
|
18974
|
+
).option(
|
|
18975
|
+
"--collab <id>",
|
|
18976
|
+
"Send to a specific collab id (defaults to the active collab for cwd)"
|
|
18977
|
+
).option("--action <action>", "Explicit requested action").option(
|
|
18575
18978
|
"--artifact <path>",
|
|
18576
18979
|
"Artifact file path. Repeat for multiple files.",
|
|
18577
18980
|
collectArtifact,
|
|
@@ -18600,7 +19003,10 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18600
19003
|
console.log("No work items to process.");
|
|
18601
19004
|
}
|
|
18602
19005
|
});
|
|
18603
|
-
collab.command("recover").description("Recover the current workspace collab after broker loss").option(
|
|
19006
|
+
collab.command("recover").description("Recover the current workspace collab after broker loss").option(
|
|
19007
|
+
"--collab <id>",
|
|
19008
|
+
"Recover a specific collab id (defaults to the active collab for cwd)"
|
|
19009
|
+
).option(
|
|
18604
19010
|
"--port <port>",
|
|
18605
19011
|
"Explicit port to bind for the recovered daemon",
|
|
18606
19012
|
(v) => Number.parseInt(v, 10)
|
|
@@ -18624,18 +19030,30 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18624
19030
|
`Collab recovered: ${result.collabId} (pid ${result.pid}, port ${result.port})`
|
|
18625
19031
|
);
|
|
18626
19032
|
});
|
|
18627
|
-
collab.command("reconnect").description(
|
|
18628
|
-
|
|
18629
|
-
|
|
18630
|
-
|
|
18631
|
-
|
|
18632
|
-
|
|
18633
|
-
|
|
18634
|
-
|
|
18635
|
-
|
|
19033
|
+
collab.command("reconnect").description(
|
|
19034
|
+
"Reconnect a remembered role after broker recovery (mount mode)"
|
|
19035
|
+
).argument("<agent>", "Target agent: codex, claude, ezio, or agy").option("--workspace <path>", "Workspace root", process.cwd()).option(
|
|
19036
|
+
"--collab <id>",
|
|
19037
|
+
"Target a specific collab id (defaults to the active collab for cwd)"
|
|
19038
|
+
).action(
|
|
19039
|
+
async (target, opts) => {
|
|
19040
|
+
await runCollabReconnect({
|
|
19041
|
+
workspaceRoot: opts.workspace,
|
|
19042
|
+
...opts.collab ? { collabIdOverride: opts.collab } : {},
|
|
19043
|
+
target,
|
|
19044
|
+
now: (/* @__PURE__ */ new Date()).toISOString()
|
|
19045
|
+
});
|
|
19046
|
+
}
|
|
19047
|
+
);
|
|
19048
|
+
collab.command("mount").description(
|
|
19049
|
+
"Mount the current terminal as the managed session surface for a role"
|
|
19050
|
+
).argument("<agent>", "Target agent: codex, claude, ezio, or agy").argument(
|
|
18636
19051
|
"[passthroughArgs...]",
|
|
18637
19052
|
"Args forwarded after `--` to the agent binary spawn (e.g. `mount codex -- --full-auto`)"
|
|
18638
|
-
).option("--workspace <path>", "Workspace root", process.cwd()).option(
|
|
19053
|
+
).option("--workspace <path>", "Workspace root", process.cwd()).option(
|
|
19054
|
+
"--collab <id>",
|
|
19055
|
+
"Target a specific collab id (defaults to the active collab for cwd)"
|
|
19056
|
+
).option(
|
|
18639
19057
|
"--turn-events <providers>",
|
|
18640
19058
|
"Scope the push turn-completion event path to the given providers (comma-separated providers with turn-end hooks: claude,codex,agy), or disable it with `off`/`none` to revert to pure clipboard capture. Overrides AI_WHISPER_TURN_EVENTS. Default: on (claude,codex,agy)."
|
|
18641
19059
|
).action(
|
|
@@ -18650,7 +19068,10 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18650
19068
|
});
|
|
18651
19069
|
}
|
|
18652
19070
|
);
|
|
18653
|
-
collab.command("inspect").description("Inspect the active collab thread").option(
|
|
19071
|
+
collab.command("inspect").description("Inspect the active collab thread").option(
|
|
19072
|
+
"--collab <id>",
|
|
19073
|
+
"Inspect a specific collab id (defaults to the active collab for cwd)"
|
|
19074
|
+
).option("--watch", "Continuously redraw the active-thread operator view").option(
|
|
18654
19075
|
"--captures [chainId]",
|
|
18655
19076
|
"Show recent capture-diagnostics rows. Pass a chain id to filter, or 'all' for the full history."
|
|
18656
19077
|
).option(
|
|
@@ -18673,13 +19094,20 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18673
19094
|
}
|
|
18674
19095
|
}
|
|
18675
19096
|
);
|
|
18676
|
-
collab.command("relay-monitor").description(
|
|
19097
|
+
collab.command("relay-monitor").description(
|
|
19098
|
+
"Run the relay monitor in the current terminal (renders the relay conversation stream)"
|
|
19099
|
+
).option(
|
|
19100
|
+
"--collab <id>",
|
|
19101
|
+
"Monitor a specific collab id (defaults to the active collab for cwd)"
|
|
19102
|
+
).action(async (opts) => {
|
|
18677
19103
|
await runCollabRelayMonitor({
|
|
18678
19104
|
cwd: process.cwd(),
|
|
18679
19105
|
...opts.collab ? { collabIdOverride: opts.collab } : {}
|
|
18680
19106
|
});
|
|
18681
19107
|
});
|
|
18682
|
-
collab.command("dashboard").description(
|
|
19108
|
+
collab.command("dashboard").description(
|
|
19109
|
+
"Full-screen dashboard: live wall of recently-active runs + per-run inspector"
|
|
19110
|
+
).option(
|
|
18683
19111
|
"--window <duration>",
|
|
18684
19112
|
"Eligible-collab activity window. Accepts ms or Ns/Nm/Nh/Nd (e.g. 45s, 30m, 2h, 1d), or 'all' for no limit. Default: 30m."
|
|
18685
19113
|
).option(
|
|
@@ -18716,7 +19144,7 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18716
19144
|
},
|
|
18717
19145
|
execCommand: (cmd) => {
|
|
18718
19146
|
try {
|
|
18719
|
-
|
|
19147
|
+
execSync5(cmd, { stdio: "ignore" });
|
|
18720
19148
|
} catch {
|
|
18721
19149
|
}
|
|
18722
19150
|
}
|
|
@@ -18736,10 +19164,43 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18736
19164
|
throw err;
|
|
18737
19165
|
}
|
|
18738
19166
|
});
|
|
19167
|
+
collab.command("purge").description("Remove stale collabs (no live process) across all workspaces").option("--dry-run", "Classify and print only; never prompt or delete").option("-y, --yes", "Skip the confirmation prompt and delete").option(
|
|
19168
|
+
"--force",
|
|
19169
|
+
"Also purge collabs with a non-terminal (resumable) workflow"
|
|
19170
|
+
).option("--collab <id>", "Limit the sweep to a single collab id").option("--workspace <path>", "Limit the sweep to a single workspace root").option("--json", "Emit machine-readable JSON instead of the table").action(
|
|
19171
|
+
async (opts) => {
|
|
19172
|
+
if (opts.collab && opts.workspace) {
|
|
19173
|
+
console.error("--collab and --workspace are mutually exclusive.");
|
|
19174
|
+
process.exitCode = 1;
|
|
19175
|
+
return;
|
|
19176
|
+
}
|
|
19177
|
+
const result = await runCollabPurge({
|
|
19178
|
+
cwd: process.cwd(),
|
|
19179
|
+
...opts.dryRun ? { dryRun: opts.dryRun } : {},
|
|
19180
|
+
...opts.yes ? { yes: opts.yes } : {},
|
|
19181
|
+
...opts.force ? { force: opts.force } : {},
|
|
19182
|
+
...opts.json ? { json: opts.json } : {},
|
|
19183
|
+
...opts.collab ? { collabId: opts.collab } : {},
|
|
19184
|
+
...opts.workspace ? { workspace: opts.workspace } : {}
|
|
19185
|
+
});
|
|
19186
|
+
process.exitCode = result.exitCode;
|
|
19187
|
+
}
|
|
19188
|
+
);
|
|
18739
19189
|
const workflow = cli.command("workflow").description("Manage AI agent workflows");
|
|
18740
|
-
workflow.command("start").description("Start a new workflow").requiredOption(
|
|
19190
|
+
workflow.command("start").description("Start a new workflow").requiredOption(
|
|
19191
|
+
"--type <type>",
|
|
19192
|
+
"Workflow type (e.g. spec-driven-development)"
|
|
19193
|
+
).requiredOption("--spec <path>", "Spec file path").option(
|
|
19194
|
+
"--implementer <agent>",
|
|
19195
|
+
"Implementer agent: claude, codex, ezio, or agy (defaults to the workflow type's defaultImplementer)"
|
|
19196
|
+
).option(
|
|
19197
|
+
"--reviewer <agent>",
|
|
19198
|
+
"Reviewer agent: claude, codex, ezio, or agy (defaults to the workflow type's defaultReviewer)"
|
|
19199
|
+
).option("--name <name>", "Optional workflow display name").option("--workspace <path>", "Workspace root", process.cwd()).action(
|
|
18741
19200
|
async (opts) => {
|
|
18742
|
-
const { broker, collabId } = await connectToWorkspaceBroker({
|
|
19201
|
+
const { broker, collabId } = await connectToWorkspaceBroker({
|
|
19202
|
+
cwd: opts.workspace
|
|
19203
|
+
});
|
|
18743
19204
|
try {
|
|
18744
19205
|
const result = await runWorkflowStart({
|
|
18745
19206
|
broker,
|
|
@@ -18760,7 +19221,9 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18760
19221
|
}
|
|
18761
19222
|
);
|
|
18762
19223
|
workflow.command("list").description("List workflows for the active collab").option("--workspace <path>", "Workspace root", process.cwd()).action(async (opts) => {
|
|
18763
|
-
const { broker, collabId } = await connectToWorkspaceBroker({
|
|
19224
|
+
const { broker, collabId } = await connectToWorkspaceBroker({
|
|
19225
|
+
cwd: opts.workspace
|
|
19226
|
+
});
|
|
18764
19227
|
try {
|
|
18765
19228
|
const list = runWorkflowList({ broker, collabId });
|
|
18766
19229
|
if (list.length === 0) {
|
|
@@ -18775,7 +19238,9 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18775
19238
|
}
|
|
18776
19239
|
});
|
|
18777
19240
|
workflow.command("inspect").description("Inspect a workflow and its phase runs").argument("<workflowId>", "Workflow ID").option("--workspace <path>", "Workspace root", process.cwd()).action(async (workflowId, opts) => {
|
|
18778
|
-
const { broker } = await connectToWorkspaceBroker({
|
|
19241
|
+
const { broker } = await connectToWorkspaceBroker({
|
|
19242
|
+
cwd: opts.workspace
|
|
19243
|
+
});
|
|
18779
19244
|
try {
|
|
18780
19245
|
const result = await runWorkflowInspect({ broker, workflowId });
|
|
18781
19246
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -18784,17 +19249,28 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18784
19249
|
}
|
|
18785
19250
|
});
|
|
18786
19251
|
workflow.command("pause").description("Pause a running workflow").argument("<workflowId>", "Workflow ID").option("--workspace <path>", "Workspace root", process.cwd()).action(async (workflowId, opts) => {
|
|
18787
|
-
const { broker } = await connectToWorkspaceBroker({
|
|
19252
|
+
const { broker } = await connectToWorkspaceBroker({
|
|
19253
|
+
cwd: opts.workspace
|
|
19254
|
+
});
|
|
18788
19255
|
try {
|
|
18789
|
-
await runWorkflowPause({
|
|
19256
|
+
await runWorkflowPause({
|
|
19257
|
+
broker,
|
|
19258
|
+
workflowId,
|
|
19259
|
+
now: (/* @__PURE__ */ new Date()).toISOString()
|
|
19260
|
+
});
|
|
18790
19261
|
console.log(`Workflow paused: ${workflowId}`);
|
|
18791
19262
|
} finally {
|
|
18792
19263
|
await broker.stop();
|
|
18793
19264
|
}
|
|
18794
19265
|
});
|
|
18795
|
-
workflow.command("resume").description("Resume a paused or halted workflow").argument("<workflowId>", "Workflow ID").option("--workspace <path>", "Workspace root", process.cwd()).option(
|
|
19266
|
+
workflow.command("resume").description("Resume a paused or halted workflow").argument("<workflowId>", "Workflow ID").option("--workspace <path>", "Workspace root", process.cwd()).option(
|
|
19267
|
+
"--message <note>",
|
|
19268
|
+
"Operator note delivered to the agents on resume"
|
|
19269
|
+
).action(
|
|
18796
19270
|
async (workflowId, opts) => {
|
|
18797
|
-
const { broker } = await connectToWorkspaceBroker({
|
|
19271
|
+
const { broker } = await connectToWorkspaceBroker({
|
|
19272
|
+
cwd: opts.workspace
|
|
19273
|
+
});
|
|
18798
19274
|
try {
|
|
18799
19275
|
await runWorkflowResume({
|
|
18800
19276
|
broker,
|
|
@@ -18809,9 +19285,15 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18809
19285
|
}
|
|
18810
19286
|
);
|
|
18811
19287
|
workflow.command("cancel").description("Cancel a running or halted workflow").argument("<workflowId>", "Workflow ID").option("--workspace <path>", "Workspace root", process.cwd()).action(async (workflowId, opts) => {
|
|
18812
|
-
const { broker } = await connectToWorkspaceBroker({
|
|
19288
|
+
const { broker } = await connectToWorkspaceBroker({
|
|
19289
|
+
cwd: opts.workspace
|
|
19290
|
+
});
|
|
18813
19291
|
try {
|
|
18814
|
-
await runWorkflowCancel({
|
|
19292
|
+
await runWorkflowCancel({
|
|
19293
|
+
broker,
|
|
19294
|
+
workflowId,
|
|
19295
|
+
now: (/* @__PURE__ */ new Date()).toISOString()
|
|
19296
|
+
});
|
|
18815
19297
|
console.log(`Workflow canceled: ${workflowId}`);
|
|
18816
19298
|
} finally {
|
|
18817
19299
|
await broker.stop();
|
|
@@ -18823,22 +19305,37 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
18823
19305
|
console.log(t);
|
|
18824
19306
|
}
|
|
18825
19307
|
});
|
|
19308
|
+
workflow.command("stats").description(
|
|
19309
|
+
"Show accumulated hands-off time saved across all workflows (global, all-time)"
|
|
19310
|
+
).option("--json", "Output raw JSON instead of the human summary").action(async (opts) => {
|
|
19311
|
+
const sqlitePath = getSharedSqlitePath();
|
|
19312
|
+
const broker = createBrokerRuntime({
|
|
19313
|
+
sqlitePath,
|
|
19314
|
+
runWorkflowDriver: false,
|
|
19315
|
+
runDiagnosticsSweep: false,
|
|
19316
|
+
runDaemonHeartbeat: false,
|
|
19317
|
+
runBrokerDaemonSweep: false
|
|
19318
|
+
});
|
|
19319
|
+
try {
|
|
19320
|
+
runWorkflowStats({ broker, json: opts.json ?? false });
|
|
19321
|
+
} finally {
|
|
19322
|
+
await broker.stop();
|
|
19323
|
+
}
|
|
19324
|
+
});
|
|
18826
19325
|
const skill = cli.command("skill").description("Manage bundled agent skills");
|
|
18827
19326
|
skill.command("install").description(
|
|
18828
19327
|
"Install the bundled ai-whisper skills into your agent skill directories"
|
|
18829
19328
|
).addOption(
|
|
18830
19329
|
new Option("--target <target>", "Agent install target").choices(["claude", "codex", "ezio", "agy", "all"]).default("all")
|
|
18831
|
-
).option("--force", "Overwrite existing skill destinations").action(
|
|
18832
|
-
|
|
18833
|
-
|
|
18834
|
-
|
|
18835
|
-
|
|
18836
|
-
|
|
18837
|
-
|
|
18838
|
-
console.log(`Installed: ${p}`);
|
|
18839
|
-
}
|
|
19330
|
+
).option("--force", "Overwrite existing skill destinations").action(async (opts) => {
|
|
19331
|
+
const result = await runSkillInstall({
|
|
19332
|
+
target: opts.target,
|
|
19333
|
+
...opts.force ? { force: true } : {}
|
|
19334
|
+
});
|
|
19335
|
+
for (const p of result.installedAt) {
|
|
19336
|
+
console.log(`Installed: ${p}`);
|
|
18840
19337
|
}
|
|
18841
|
-
);
|
|
19338
|
+
});
|
|
18842
19339
|
cli.command("env").description(
|
|
18843
19340
|
"Print machine-readable engine/integration facts for external supervisors"
|
|
18844
19341
|
).option(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-whisper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Terminal-first relay for paired AI coding agents (Claude + Codex), driven by structured workflows.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -52,13 +52,13 @@
|
|
|
52
52
|
"@types/better-sqlite3": "^7.6.12",
|
|
53
53
|
"@types/react": "^19.2.14",
|
|
54
54
|
"ink-testing-library": "^4.0.0",
|
|
55
|
+
"@ai-whisper/adapter-ai-ezio": "0.0.0",
|
|
56
|
+
"@ai-whisper/adapter-claude": "0.0.0",
|
|
55
57
|
"@ai-whisper/adapter-antigravity": "0.0.0",
|
|
56
58
|
"@ai-whisper/adapter-codex": "0.0.0",
|
|
57
|
-
"@ai-whisper/adapter-claude": "0.0.0",
|
|
58
|
-
"@ai-whisper/adapter-ai-ezio": "0.0.0",
|
|
59
59
|
"@ai-whisper/broker": "0.0.0",
|
|
60
|
-
"@ai-whisper/
|
|
61
|
-
"@ai-whisper/
|
|
60
|
+
"@ai-whisper/shared": "0.0.0",
|
|
61
|
+
"@ai-whisper/companion-core": "0.0.0"
|
|
62
62
|
},
|
|
63
63
|
"files": [
|
|
64
64
|
"dist",
|