@0dai-dev/cli 4.2.0 → 4.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -5
- package/bin/0dai.js +289 -60
- package/lib/commands/audit.js +13 -0
- package/lib/commands/auth.js +341 -98
- package/lib/commands/boneyard.js +44 -0
- package/lib/commands/ci.js +329 -0
- package/lib/commands/compliance.js +20 -0
- package/lib/commands/doctor.js +20 -1
- package/lib/commands/experience.js +5 -1
- package/lib/commands/feedback.js +92 -5
- package/lib/commands/gh.js +506 -0
- package/lib/commands/graph.js +78 -10
- package/lib/commands/heatmap.js +17 -0
- package/lib/commands/import_claude_code_agents.js +367 -0
- package/lib/commands/init.js +440 -28
- package/lib/commands/loop.js +108 -0
- package/lib/commands/mcp.js +410 -0
- package/lib/commands/models.js +27 -3
- package/lib/commands/paste.js +114 -0
- package/lib/commands/play.js +173 -0
- package/lib/commands/provider.js +69 -0
- package/lib/commands/quota.js +76 -0
- package/lib/commands/receipt.js +53 -0
- package/lib/commands/report.js +29 -2
- package/lib/commands/run.js +44 -4
- package/lib/commands/runner.js +527 -0
- package/lib/commands/session.js +1 -7
- package/lib/commands/standup.js +40 -0
- package/lib/commands/status.js +26 -1
- package/lib/commands/swarm.js +97 -4
- package/lib/commands/tui.js +81 -13
- package/lib/commands/usage.js +87 -0
- package/lib/commands/vault.js +246 -0
- package/lib/onboarding.js +9 -3
- package/lib/shared.js +29 -14
- package/lib/tui/index.mjs +571 -187
- package/lib/utils/auth.js +1 -0
- package/lib/utils/canonical-counts.js +54 -0
- package/lib/utils/diff-preview.js +192 -0
- package/lib/utils/identity.js +76 -18
- package/lib/utils/mcp-auth.js +607 -0
- package/lib/utils/plan.js +37 -2
- package/lib/vault/cipher.js +125 -0
- package/lib/vault/identity.js +122 -0
- package/lib/vault/index.js +184 -0
- package/lib/vault/storage.js +84 -0
- package/lib/wizard.js +19 -12
- package/package.json +2 -2
package/lib/tui/index.mjs
CHANGED
|
@@ -1398,7 +1398,7 @@ var require_react_development = __commonJS({
|
|
|
1398
1398
|
var dispatcher = resolveDispatcher();
|
|
1399
1399
|
return dispatcher.useCallback(callback, deps);
|
|
1400
1400
|
}
|
|
1401
|
-
function
|
|
1401
|
+
function useMemo3(create2, deps) {
|
|
1402
1402
|
var dispatcher = resolveDispatcher();
|
|
1403
1403
|
return dispatcher.useMemo(create2, deps);
|
|
1404
1404
|
}
|
|
@@ -2170,7 +2170,7 @@ var require_react_development = __commonJS({
|
|
|
2170
2170
|
exports.useImperativeHandle = useImperativeHandle;
|
|
2171
2171
|
exports.useInsertionEffect = useInsertionEffect;
|
|
2172
2172
|
exports.useLayoutEffect = useLayoutEffect2;
|
|
2173
|
-
exports.useMemo =
|
|
2173
|
+
exports.useMemo = useMemo3;
|
|
2174
2174
|
exports.useReducer = useReducer;
|
|
2175
2175
|
exports.useRef = useRef2;
|
|
2176
2176
|
exports.useState = useState5;
|
|
@@ -7865,9 +7865,9 @@ var require_react_reconciler_development = __commonJS({
|
|
|
7865
7865
|
module.exports = function $$$reconciler($$$hostConfig) {
|
|
7866
7866
|
var exports2 = {};
|
|
7867
7867
|
"use strict";
|
|
7868
|
-
var
|
|
7868
|
+
var React11 = require_react();
|
|
7869
7869
|
var Scheduler = require_scheduler();
|
|
7870
|
-
var ReactSharedInternals =
|
|
7870
|
+
var ReactSharedInternals = React11.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
7871
7871
|
var suppressWarning = false;
|
|
7872
7872
|
function setSuppressWarning(newSuppressWarning) {
|
|
7873
7873
|
{
|
|
@@ -12472,8 +12472,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
12472
12472
|
var InvisibleParentSuspenseContext = 1;
|
|
12473
12473
|
var ForceSuspenseFallback = 2;
|
|
12474
12474
|
var suspenseStackCursor = createCursor(DefaultSuspenseContext);
|
|
12475
|
-
function hasSuspenseContext(parentContext,
|
|
12476
|
-
return (parentContext &
|
|
12475
|
+
function hasSuspenseContext(parentContext, flag2) {
|
|
12476
|
+
return (parentContext & flag2) !== 0;
|
|
12477
12477
|
}
|
|
12478
12478
|
function setDefaultShallowSuspenseContext(parentContext) {
|
|
12479
12479
|
return parentContext & SubtreeSuspenseContextMask;
|
|
@@ -16661,7 +16661,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
16661
16661
|
}
|
|
16662
16662
|
}
|
|
16663
16663
|
}
|
|
16664
|
-
function initSuspenseListRenderState(workInProgress2, isBackwards,
|
|
16664
|
+
function initSuspenseListRenderState(workInProgress2, isBackwards, tail, lastContentRow, tailMode) {
|
|
16665
16665
|
var renderState = workInProgress2.memoizedState;
|
|
16666
16666
|
if (renderState === null) {
|
|
16667
16667
|
workInProgress2.memoizedState = {
|
|
@@ -16669,7 +16669,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
16669
16669
|
rendering: null,
|
|
16670
16670
|
renderingStartTime: 0,
|
|
16671
16671
|
last: lastContentRow,
|
|
16672
|
-
tail
|
|
16672
|
+
tail,
|
|
16673
16673
|
tailMode
|
|
16674
16674
|
};
|
|
16675
16675
|
} else {
|
|
@@ -16677,7 +16677,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
16677
16677
|
renderState.rendering = null;
|
|
16678
16678
|
renderState.renderingStartTime = 0;
|
|
16679
16679
|
renderState.last = lastContentRow;
|
|
16680
|
-
renderState.tail =
|
|
16680
|
+
renderState.tail = tail;
|
|
16681
16681
|
renderState.tailMode = tailMode;
|
|
16682
16682
|
}
|
|
16683
16683
|
}
|
|
@@ -16709,19 +16709,19 @@ var require_react_reconciler_development = __commonJS({
|
|
|
16709
16709
|
switch (revealOrder) {
|
|
16710
16710
|
case "forwards": {
|
|
16711
16711
|
var lastContentRow = findLastContentRow(workInProgress2.child);
|
|
16712
|
-
var
|
|
16712
|
+
var tail;
|
|
16713
16713
|
if (lastContentRow === null) {
|
|
16714
|
-
|
|
16714
|
+
tail = workInProgress2.child;
|
|
16715
16715
|
workInProgress2.child = null;
|
|
16716
16716
|
} else {
|
|
16717
|
-
|
|
16717
|
+
tail = lastContentRow.sibling;
|
|
16718
16718
|
lastContentRow.sibling = null;
|
|
16719
16719
|
}
|
|
16720
16720
|
initSuspenseListRenderState(
|
|
16721
16721
|
workInProgress2,
|
|
16722
16722
|
false,
|
|
16723
16723
|
// isBackwards
|
|
16724
|
-
|
|
16724
|
+
tail,
|
|
16725
16725
|
lastContentRow,
|
|
16726
16726
|
tailMode
|
|
16727
16727
|
);
|
|
@@ -22744,10 +22744,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
22744
22744
|
var setErrorHandler = null;
|
|
22745
22745
|
var setSuspenseHandler = null;
|
|
22746
22746
|
{
|
|
22747
|
-
var copyWithDeleteImpl = function(obj,
|
|
22748
|
-
var key =
|
|
22747
|
+
var copyWithDeleteImpl = function(obj, path8, index2) {
|
|
22748
|
+
var key = path8[index2];
|
|
22749
22749
|
var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
|
22750
|
-
if (index2 + 1 ===
|
|
22750
|
+
if (index2 + 1 === path8.length) {
|
|
22751
22751
|
if (isArray(updated)) {
|
|
22752
22752
|
updated.splice(key, 1);
|
|
22753
22753
|
} else {
|
|
@@ -22755,11 +22755,11 @@ var require_react_reconciler_development = __commonJS({
|
|
|
22755
22755
|
}
|
|
22756
22756
|
return updated;
|
|
22757
22757
|
}
|
|
22758
|
-
updated[key] = copyWithDeleteImpl(obj[key],
|
|
22758
|
+
updated[key] = copyWithDeleteImpl(obj[key], path8, index2 + 1);
|
|
22759
22759
|
return updated;
|
|
22760
22760
|
};
|
|
22761
|
-
var copyWithDelete = function(obj,
|
|
22762
|
-
return copyWithDeleteImpl(obj,
|
|
22761
|
+
var copyWithDelete = function(obj, path8) {
|
|
22762
|
+
return copyWithDeleteImpl(obj, path8, 0);
|
|
22763
22763
|
};
|
|
22764
22764
|
var copyWithRenameImpl = function(obj, oldPath, newPath, index2) {
|
|
22765
22765
|
var oldKey = oldPath[index2];
|
|
@@ -22797,17 +22797,17 @@ var require_react_reconciler_development = __commonJS({
|
|
|
22797
22797
|
}
|
|
22798
22798
|
return copyWithRenameImpl(obj, oldPath, newPath, 0);
|
|
22799
22799
|
};
|
|
22800
|
-
var copyWithSetImpl = function(obj,
|
|
22801
|
-
if (index2 >=
|
|
22800
|
+
var copyWithSetImpl = function(obj, path8, index2, value) {
|
|
22801
|
+
if (index2 >= path8.length) {
|
|
22802
22802
|
return value;
|
|
22803
22803
|
}
|
|
22804
|
-
var key =
|
|
22804
|
+
var key = path8[index2];
|
|
22805
22805
|
var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
|
22806
|
-
updated[key] = copyWithSetImpl(obj[key],
|
|
22806
|
+
updated[key] = copyWithSetImpl(obj[key], path8, index2 + 1, value);
|
|
22807
22807
|
return updated;
|
|
22808
22808
|
};
|
|
22809
|
-
var copyWithSet = function(obj,
|
|
22810
|
-
return copyWithSetImpl(obj,
|
|
22809
|
+
var copyWithSet = function(obj, path8, value) {
|
|
22810
|
+
return copyWithSetImpl(obj, path8, 0, value);
|
|
22811
22811
|
};
|
|
22812
22812
|
var findHook = function(fiber, id) {
|
|
22813
22813
|
var currentHook2 = fiber.memoizedState;
|
|
@@ -22817,10 +22817,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
22817
22817
|
}
|
|
22818
22818
|
return currentHook2;
|
|
22819
22819
|
};
|
|
22820
|
-
overrideHookState = function(fiber, id,
|
|
22820
|
+
overrideHookState = function(fiber, id, path8, value) {
|
|
22821
22821
|
var hook = findHook(fiber, id);
|
|
22822
22822
|
if (hook !== null) {
|
|
22823
|
-
var newState = copyWithSet(hook.memoizedState,
|
|
22823
|
+
var newState = copyWithSet(hook.memoizedState, path8, value);
|
|
22824
22824
|
hook.memoizedState = newState;
|
|
22825
22825
|
hook.baseState = newState;
|
|
22826
22826
|
fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
|
@@ -22830,10 +22830,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
22830
22830
|
}
|
|
22831
22831
|
}
|
|
22832
22832
|
};
|
|
22833
|
-
overrideHookStateDeletePath = function(fiber, id,
|
|
22833
|
+
overrideHookStateDeletePath = function(fiber, id, path8) {
|
|
22834
22834
|
var hook = findHook(fiber, id);
|
|
22835
22835
|
if (hook !== null) {
|
|
22836
|
-
var newState = copyWithDelete(hook.memoizedState,
|
|
22836
|
+
var newState = copyWithDelete(hook.memoizedState, path8);
|
|
22837
22837
|
hook.memoizedState = newState;
|
|
22838
22838
|
hook.baseState = newState;
|
|
22839
22839
|
fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
|
@@ -22856,8 +22856,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
22856
22856
|
}
|
|
22857
22857
|
}
|
|
22858
22858
|
};
|
|
22859
|
-
overrideProps = function(fiber,
|
|
22860
|
-
fiber.pendingProps = copyWithSet(fiber.memoizedProps,
|
|
22859
|
+
overrideProps = function(fiber, path8, value) {
|
|
22860
|
+
fiber.pendingProps = copyWithSet(fiber.memoizedProps, path8, value);
|
|
22861
22861
|
if (fiber.alternate) {
|
|
22862
22862
|
fiber.alternate.pendingProps = fiber.pendingProps;
|
|
22863
22863
|
}
|
|
@@ -22866,8 +22866,8 @@ var require_react_reconciler_development = __commonJS({
|
|
|
22866
22866
|
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|
22867
22867
|
}
|
|
22868
22868
|
};
|
|
22869
|
-
overridePropsDeletePath = function(fiber,
|
|
22870
|
-
fiber.pendingProps = copyWithDelete(fiber.memoizedProps,
|
|
22869
|
+
overridePropsDeletePath = function(fiber, path8) {
|
|
22870
|
+
fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path8);
|
|
22871
22871
|
if (fiber.alternate) {
|
|
22872
22872
|
fiber.alternate.pendingProps = fiber.pendingProps;
|
|
22873
22873
|
}
|
|
@@ -25270,13 +25270,13 @@ var require_websocket = __commonJS({
|
|
|
25270
25270
|
"node_modules/ws/lib/websocket.js"(exports, module) {
|
|
25271
25271
|
"use strict";
|
|
25272
25272
|
var EventEmitter3 = __require("events");
|
|
25273
|
-
var
|
|
25274
|
-
var
|
|
25273
|
+
var https3 = __require("https");
|
|
25274
|
+
var http3 = __require("http");
|
|
25275
25275
|
var net = __require("net");
|
|
25276
25276
|
var tls = __require("tls");
|
|
25277
25277
|
var { randomBytes, createHash } = __require("crypto");
|
|
25278
25278
|
var { Duplex, Readable } = __require("stream");
|
|
25279
|
-
var { URL:
|
|
25279
|
+
var { URL: URL3 } = __require("url");
|
|
25280
25280
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
25281
25281
|
var Receiver2 = require_receiver();
|
|
25282
25282
|
var Sender2 = require_sender();
|
|
@@ -25769,11 +25769,11 @@ var require_websocket = __commonJS({
|
|
|
25769
25769
|
);
|
|
25770
25770
|
}
|
|
25771
25771
|
let parsedUrl;
|
|
25772
|
-
if (address instanceof
|
|
25772
|
+
if (address instanceof URL3) {
|
|
25773
25773
|
parsedUrl = address;
|
|
25774
25774
|
} else {
|
|
25775
25775
|
try {
|
|
25776
|
-
parsedUrl = new
|
|
25776
|
+
parsedUrl = new URL3(address);
|
|
25777
25777
|
} catch {
|
|
25778
25778
|
throw new SyntaxError(`Invalid URL: ${address}`);
|
|
25779
25779
|
}
|
|
@@ -25805,7 +25805,7 @@ var require_websocket = __commonJS({
|
|
|
25805
25805
|
}
|
|
25806
25806
|
const defaultPort = isSecure ? 443 : 80;
|
|
25807
25807
|
const key = randomBytes(16).toString("base64");
|
|
25808
|
-
const request = isSecure ?
|
|
25808
|
+
const request = isSecure ? https3.request : http3.request;
|
|
25809
25809
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
25810
25810
|
let perMessageDeflate;
|
|
25811
25811
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -25910,7 +25910,7 @@ var require_websocket = __commonJS({
|
|
|
25910
25910
|
req.abort();
|
|
25911
25911
|
let addr;
|
|
25912
25912
|
try {
|
|
25913
|
-
addr = new
|
|
25913
|
+
addr = new URL3(location, address);
|
|
25914
25914
|
} catch (e) {
|
|
25915
25915
|
const err = new SyntaxError(`Invalid URL: ${location}`);
|
|
25916
25916
|
emitErrorAndClose(websocket, err);
|
|
@@ -26299,7 +26299,7 @@ var require_websocket_server = __commonJS({
|
|
|
26299
26299
|
"node_modules/ws/lib/websocket-server.js"(exports, module) {
|
|
26300
26300
|
"use strict";
|
|
26301
26301
|
var EventEmitter3 = __require("events");
|
|
26302
|
-
var
|
|
26302
|
+
var http3 = __require("http");
|
|
26303
26303
|
var { Duplex } = __require("stream");
|
|
26304
26304
|
var { createHash } = __require("crypto");
|
|
26305
26305
|
var extension2 = require_extension();
|
|
@@ -26374,8 +26374,8 @@ var require_websocket_server = __commonJS({
|
|
|
26374
26374
|
);
|
|
26375
26375
|
}
|
|
26376
26376
|
if (options.port != null) {
|
|
26377
|
-
this._server =
|
|
26378
|
-
const body =
|
|
26377
|
+
this._server = http3.createServer((req, res) => {
|
|
26378
|
+
const body = http3.STATUS_CODES[426];
|
|
26379
26379
|
res.writeHead(426, {
|
|
26380
26380
|
"Content-Length": body.length,
|
|
26381
26381
|
"Content-Type": "text/plain"
|
|
@@ -26662,7 +26662,7 @@ var require_websocket_server = __commonJS({
|
|
|
26662
26662
|
this.destroy();
|
|
26663
26663
|
}
|
|
26664
26664
|
function abortHandshake(socket, code, message, headers) {
|
|
26665
|
-
message = message ||
|
|
26665
|
+
message = message || http3.STATUS_CODES[code];
|
|
26666
26666
|
headers = {
|
|
26667
26667
|
Connection: "close",
|
|
26668
26668
|
"Content-Type": "text/html",
|
|
@@ -26671,7 +26671,7 @@ var require_websocket_server = __commonJS({
|
|
|
26671
26671
|
};
|
|
26672
26672
|
socket.once("finish", socket.destroy);
|
|
26673
26673
|
socket.end(
|
|
26674
|
-
`HTTP/1.1 ${code} ${
|
|
26674
|
+
`HTTP/1.1 ${code} ${http3.STATUS_CODES[code]}\r
|
|
26675
26675
|
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
26676
26676
|
);
|
|
26677
26677
|
}
|
|
@@ -27235,7 +27235,7 @@ var require_react_jsx_runtime_development = __commonJS({
|
|
|
27235
27235
|
if (process.env.NODE_ENV !== "production") {
|
|
27236
27236
|
(function() {
|
|
27237
27237
|
"use strict";
|
|
27238
|
-
var
|
|
27238
|
+
var React11 = require_react();
|
|
27239
27239
|
var REACT_ELEMENT_TYPE = Symbol.for("react.element");
|
|
27240
27240
|
var REACT_PORTAL_TYPE = Symbol.for("react.portal");
|
|
27241
27241
|
var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
|
|
@@ -27261,7 +27261,7 @@ var require_react_jsx_runtime_development = __commonJS({
|
|
|
27261
27261
|
}
|
|
27262
27262
|
return null;
|
|
27263
27263
|
}
|
|
27264
|
-
var ReactSharedInternals =
|
|
27264
|
+
var ReactSharedInternals = React11.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
27265
27265
|
function error(format) {
|
|
27266
27266
|
{
|
|
27267
27267
|
{
|
|
@@ -28111,11 +28111,11 @@ var require_react_jsx_runtime_development = __commonJS({
|
|
|
28111
28111
|
return jsxWithValidation(type, props, key, false);
|
|
28112
28112
|
}
|
|
28113
28113
|
}
|
|
28114
|
-
var
|
|
28115
|
-
var
|
|
28114
|
+
var jsx15 = jsxWithValidationDynamic;
|
|
28115
|
+
var jsxs14 = jsxWithValidationStatic;
|
|
28116
28116
|
exports.Fragment = REACT_FRAGMENT_TYPE;
|
|
28117
|
-
exports.jsx =
|
|
28118
|
-
exports.jsxs =
|
|
28117
|
+
exports.jsx = jsx15;
|
|
28118
|
+
exports.jsxs = jsxs14;
|
|
28119
28119
|
})();
|
|
28120
28120
|
}
|
|
28121
28121
|
}
|
|
@@ -31622,9 +31622,9 @@ var ansi_styles_default2 = ansiStyles2;
|
|
|
31622
31622
|
import process4 from "node:process";
|
|
31623
31623
|
import os2 from "node:os";
|
|
31624
31624
|
import tty from "node:tty";
|
|
31625
|
-
function hasFlag(
|
|
31626
|
-
const prefix =
|
|
31627
|
-
const position = argv.indexOf(prefix +
|
|
31625
|
+
function hasFlag(flag2, argv = globalThis.Deno ? globalThis.Deno.args : process4.argv) {
|
|
31626
|
+
const prefix = flag2.startsWith("-") ? "" : flag2.length === 1 ? "-" : "--";
|
|
31627
|
+
const position = argv.indexOf(prefix + flag2);
|
|
31628
31628
|
const terminatorPosition = argv.indexOf("--");
|
|
31629
31629
|
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
|
|
31630
31630
|
}
|
|
@@ -32843,8 +32843,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
32843
32843
|
}
|
|
32844
32844
|
|
|
32845
32845
|
// node_modules/ink/build/components/ErrorOverview.js
|
|
32846
|
-
var cleanupPath = (
|
|
32847
|
-
return
|
|
32846
|
+
var cleanupPath = (path8) => {
|
|
32847
|
+
return path8?.replace(`file://${cwd()}/`, "");
|
|
32848
32848
|
};
|
|
32849
32849
|
var stackUtils = new import_stack_utils.default({
|
|
32850
32850
|
cwd: cwd(),
|
|
@@ -33768,7 +33768,7 @@ var import_react20 = __toESM(require_react(), 1);
|
|
|
33768
33768
|
var import_react21 = __toESM(require_react(), 1);
|
|
33769
33769
|
|
|
33770
33770
|
// lib/tui/src/app.tsx
|
|
33771
|
-
var
|
|
33771
|
+
var import_react24 = __toESM(require_react());
|
|
33772
33772
|
|
|
33773
33773
|
// lib/tui/src/theme.ts
|
|
33774
33774
|
var bg = {
|
|
@@ -34029,10 +34029,86 @@ function Sessions({ sessions }) {
|
|
|
34029
34029
|
] });
|
|
34030
34030
|
}
|
|
34031
34031
|
|
|
34032
|
-
// lib/tui/src/
|
|
34033
|
-
|
|
34032
|
+
// lib/tui/src/hooks/use-budget.ts
|
|
34033
|
+
import fs3 from "fs";
|
|
34034
|
+
import path2 from "path";
|
|
34035
|
+
|
|
34036
|
+
// lib/tui/src/hooks/use-http.ts
|
|
34034
34037
|
import fs2 from "fs";
|
|
34038
|
+
import http from "http";
|
|
34039
|
+
import https from "https";
|
|
34040
|
+
import os3 from "os";
|
|
34035
34041
|
import path from "path";
|
|
34042
|
+
import { URL } from "url";
|
|
34043
|
+
var DEFAULT_TIMEOUT_MS = 4e3;
|
|
34044
|
+
var DEFAULT_REMOTE_BASE = process.env.ODAI_TUI_REMOTE_URL || "https://api.0dai.dev";
|
|
34045
|
+
function readAuthToken(homeDir = os3.homedir()) {
|
|
34046
|
+
try {
|
|
34047
|
+
const authPath = path.join(homeDir, ".0dai", "auth.json");
|
|
34048
|
+
if (!fs2.existsSync(authPath)) return null;
|
|
34049
|
+
const parsed = JSON.parse(fs2.readFileSync(authPath, "utf8"));
|
|
34050
|
+
const token = String(parsed?.access_token || "");
|
|
34051
|
+
return token || null;
|
|
34052
|
+
} catch {
|
|
34053
|
+
return null;
|
|
34054
|
+
}
|
|
34055
|
+
}
|
|
34056
|
+
function fetchState(endpoint, options = {}) {
|
|
34057
|
+
const {
|
|
34058
|
+
baseUrl = DEFAULT_REMOTE_BASE,
|
|
34059
|
+
token = readAuthToken(),
|
|
34060
|
+
query = "",
|
|
34061
|
+
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
34062
|
+
httpModule = http,
|
|
34063
|
+
httpsModule = https
|
|
34064
|
+
} = options;
|
|
34065
|
+
const headers = {
|
|
34066
|
+
Accept: "application/json"
|
|
34067
|
+
};
|
|
34068
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
34069
|
+
let url;
|
|
34070
|
+
try {
|
|
34071
|
+
url = new URL(endpoint, baseUrl);
|
|
34072
|
+
if (query) url.search = query.startsWith("?") ? query.slice(1) : query;
|
|
34073
|
+
} catch {
|
|
34074
|
+
return Promise.resolve(null);
|
|
34075
|
+
}
|
|
34076
|
+
return new Promise((resolve) => {
|
|
34077
|
+
try {
|
|
34078
|
+
const mod = url.protocol === "http:" ? httpModule : httpsModule;
|
|
34079
|
+
const req = mod.request(
|
|
34080
|
+
{
|
|
34081
|
+
hostname: url.hostname,
|
|
34082
|
+
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
34083
|
+
path: url.pathname + url.search,
|
|
34084
|
+
method: "GET",
|
|
34085
|
+
headers,
|
|
34086
|
+
timeout: timeoutMs
|
|
34087
|
+
},
|
|
34088
|
+
(res) => {
|
|
34089
|
+
let body = "";
|
|
34090
|
+
res.on("data", (chunk) => body += chunk);
|
|
34091
|
+
res.on("end", () => {
|
|
34092
|
+
if ((res.statusCode || 500) >= 400) return resolve(null);
|
|
34093
|
+
try {
|
|
34094
|
+
resolve(JSON.parse(body));
|
|
34095
|
+
} catch {
|
|
34096
|
+
resolve(null);
|
|
34097
|
+
}
|
|
34098
|
+
});
|
|
34099
|
+
}
|
|
34100
|
+
);
|
|
34101
|
+
req.on("error", () => resolve(null));
|
|
34102
|
+
req.on("timeout", () => {
|
|
34103
|
+
req.destroy();
|
|
34104
|
+
resolve(null);
|
|
34105
|
+
});
|
|
34106
|
+
req.end();
|
|
34107
|
+
} catch {
|
|
34108
|
+
resolve(null);
|
|
34109
|
+
}
|
|
34110
|
+
});
|
|
34111
|
+
}
|
|
34036
34112
|
|
|
34037
34113
|
// lib/tui/src/hooks/use-poll.ts
|
|
34038
34114
|
var import_react22 = __toESM(require_react());
|
|
@@ -34063,22 +34139,43 @@ function usePoll(fetcher, intervalMs = 3e3, initial) {
|
|
|
34063
34139
|
return { data, refresh: () => void tick(), error };
|
|
34064
34140
|
}
|
|
34065
34141
|
|
|
34066
|
-
// lib/tui/src/
|
|
34067
|
-
var
|
|
34068
|
-
function
|
|
34069
|
-
const
|
|
34070
|
-
|
|
34071
|
-
|
|
34072
|
-
|
|
34073
|
-
|
|
34074
|
-
|
|
34075
|
-
|
|
34076
|
-
|
|
34077
|
-
|
|
34142
|
+
// lib/tui/src/hooks/use-budget.ts
|
|
34143
|
+
var EMPTY_SNAPSHOT = {};
|
|
34144
|
+
function readLocalBudget(target) {
|
|
34145
|
+
const p = path2.join(target, "ai", "swarm", "budget.json");
|
|
34146
|
+
if (!fs3.existsSync(p)) return {};
|
|
34147
|
+
try {
|
|
34148
|
+
return JSON.parse(fs3.readFileSync(p, "utf8"));
|
|
34149
|
+
} catch {
|
|
34150
|
+
return {};
|
|
34151
|
+
}
|
|
34152
|
+
}
|
|
34153
|
+
async function fetchRemoteBudget(remoteUrl, projectId) {
|
|
34154
|
+
const result = await fetchState("/v1/state/budget", {
|
|
34155
|
+
baseUrl: remoteUrl,
|
|
34156
|
+
query: `project=${encodeURIComponent(projectId)}`
|
|
34157
|
+
});
|
|
34158
|
+
return result?.snapshot ?? {};
|
|
34159
|
+
}
|
|
34160
|
+
function useBudget(target, intervalMs = 1e4, options = {}) {
|
|
34161
|
+
const { remoteUrl, projectId } = options;
|
|
34162
|
+
return usePoll(
|
|
34163
|
+
async () => {
|
|
34164
|
+
if (remoteUrl) {
|
|
34165
|
+
if (!projectId) return EMPTY_SNAPSHOT;
|
|
34166
|
+
return fetchRemoteBudget(remoteUrl, projectId);
|
|
34167
|
+
}
|
|
34168
|
+
return readLocalBudget(target);
|
|
34078
34169
|
},
|
|
34079
|
-
|
|
34170
|
+
intervalMs,
|
|
34171
|
+
EMPTY_SNAPSHOT
|
|
34080
34172
|
);
|
|
34081
|
-
|
|
34173
|
+
}
|
|
34174
|
+
|
|
34175
|
+
// lib/tui/src/panes/budget.tsx
|
|
34176
|
+
var import_jsx_runtime9 = __toESM(require_jsx_runtime());
|
|
34177
|
+
function Budget({ target, remoteUrl, projectId }) {
|
|
34178
|
+
const { data } = useBudget(target, 1e4, { remoteUrl, projectId });
|
|
34082
34179
|
const snap = data ?? {};
|
|
34083
34180
|
const daily = snap.daily_spent ?? 0;
|
|
34084
34181
|
const dailyLimit = snap.daily_limit ?? 0;
|
|
@@ -34114,46 +34211,81 @@ function Budget({ target }) {
|
|
|
34114
34211
|
] });
|
|
34115
34212
|
}
|
|
34116
34213
|
|
|
34117
|
-
// lib/tui/src/
|
|
34118
|
-
var
|
|
34214
|
+
// lib/tui/src/hooks/use-agents.ts
|
|
34215
|
+
var import_react23 = __toESM(require_react());
|
|
34119
34216
|
import { execFile } from "child_process";
|
|
34120
|
-
var
|
|
34121
|
-
|
|
34122
|
-
|
|
34123
|
-
|
|
34124
|
-
|
|
34125
|
-
|
|
34126
|
-
|
|
34127
|
-
{ name: "qoder", bin: "qoder", accent: theme_default.text.secondary }
|
|
34217
|
+
var DEFAULT_AGENT_BINS = [
|
|
34218
|
+
"claude",
|
|
34219
|
+
"codex",
|
|
34220
|
+
"gemini",
|
|
34221
|
+
"aider",
|
|
34222
|
+
"opencode",
|
|
34223
|
+
"qoder"
|
|
34128
34224
|
];
|
|
34129
|
-
function
|
|
34225
|
+
function probeLocal(bin) {
|
|
34130
34226
|
return new Promise((resolve) => {
|
|
34131
34227
|
execFile(bin, ["--version"], { timeout: 3e3 }, (err, stdout) => {
|
|
34132
|
-
if (err) return resolve("");
|
|
34228
|
+
if (err) return resolve("(not installed)");
|
|
34133
34229
|
const first = String(stdout || "").trim().split(/\r?\n/)[0] || "";
|
|
34134
|
-
resolve(first);
|
|
34230
|
+
resolve(first || "(not installed)");
|
|
34135
34231
|
});
|
|
34136
34232
|
});
|
|
34137
34233
|
}
|
|
34138
|
-
function
|
|
34139
|
-
const
|
|
34140
|
-
|
|
34234
|
+
async function fetchRemoteAgents(remoteUrl, projectId) {
|
|
34235
|
+
const result = await fetchState("/v1/state/agents", {
|
|
34236
|
+
baseUrl: remoteUrl,
|
|
34237
|
+
query: `project=${encodeURIComponent(projectId)}`
|
|
34238
|
+
});
|
|
34239
|
+
return result?.rows ?? [];
|
|
34240
|
+
}
|
|
34241
|
+
function useAgents(options = {}) {
|
|
34242
|
+
const { remoteUrl, projectId, bins = DEFAULT_AGENT_BINS } = options;
|
|
34243
|
+
const [rows, setRows] = (0, import_react23.useState)(
|
|
34244
|
+
bins.map((b) => ({ name: b, version: "\u2026" }))
|
|
34141
34245
|
);
|
|
34142
|
-
(0,
|
|
34246
|
+
(0, import_react23.useEffect)(() => {
|
|
34143
34247
|
let cancelled = false;
|
|
34144
34248
|
(async () => {
|
|
34145
|
-
|
|
34146
|
-
|
|
34147
|
-
|
|
34148
|
-
|
|
34149
|
-
}
|
|
34150
|
-
|
|
34249
|
+
let probed;
|
|
34250
|
+
if (remoteUrl) {
|
|
34251
|
+
if (!projectId) {
|
|
34252
|
+
probed = bins.map((b) => ({ name: b, version: "(not installed)" }));
|
|
34253
|
+
} else {
|
|
34254
|
+
probed = await fetchRemoteAgents(remoteUrl, projectId);
|
|
34255
|
+
if (probed.length === 0) {
|
|
34256
|
+
probed = bins.map((b) => ({ name: b, version: "(not installed)" }));
|
|
34257
|
+
}
|
|
34258
|
+
}
|
|
34259
|
+
} else {
|
|
34260
|
+
probed = await Promise.all(
|
|
34261
|
+
bins.map(async (b) => ({ name: b, version: await probeLocal(b) }))
|
|
34262
|
+
);
|
|
34263
|
+
}
|
|
34151
34264
|
if (!cancelled) setRows(probed);
|
|
34152
34265
|
})();
|
|
34153
34266
|
return () => {
|
|
34154
34267
|
cancelled = true;
|
|
34155
34268
|
};
|
|
34156
|
-
}, []);
|
|
34269
|
+
}, [remoteUrl, projectId, bins]);
|
|
34270
|
+
return { rows };
|
|
34271
|
+
}
|
|
34272
|
+
|
|
34273
|
+
// lib/tui/src/panes/agents.tsx
|
|
34274
|
+
var import_jsx_runtime10 = __toESM(require_jsx_runtime());
|
|
34275
|
+
var ACCENTS = {
|
|
34276
|
+
claude: theme_default.accent.purple,
|
|
34277
|
+
codex: theme_default.accent.amber,
|
|
34278
|
+
gemini: theme_default.accent.cyan,
|
|
34279
|
+
aider: theme_default.accent.green,
|
|
34280
|
+
opencode: theme_default.accent.red,
|
|
34281
|
+
qoder: theme_default.text.secondary
|
|
34282
|
+
};
|
|
34283
|
+
function Agents({ remoteUrl, projectId } = {}) {
|
|
34284
|
+
const { rows: probed } = useAgents({ remoteUrl, projectId });
|
|
34285
|
+
const rows = probed.map((r) => ({
|
|
34286
|
+
...r,
|
|
34287
|
+
accent: ACCENTS[r.name] ?? theme_default.text.secondary
|
|
34288
|
+
}));
|
|
34157
34289
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Pane, { title: "Agents", subtitle: "detected CLIs on PATH", children: [
|
|
34158
34290
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
34159
34291
|
Table,
|
|
@@ -34185,16 +34317,15 @@ function Agents() {
|
|
|
34185
34317
|
] });
|
|
34186
34318
|
}
|
|
34187
34319
|
|
|
34188
|
-
// lib/tui/src/
|
|
34189
|
-
|
|
34190
|
-
import
|
|
34191
|
-
|
|
34192
|
-
|
|
34193
|
-
|
|
34194
|
-
|
|
34195
|
-
if (!fs3.existsSync(p)) return [];
|
|
34320
|
+
// lib/tui/src/hooks/use-logs.ts
|
|
34321
|
+
import fs4 from "fs";
|
|
34322
|
+
import path3 from "path";
|
|
34323
|
+
var EMPTY = [];
|
|
34324
|
+
function tailLocal(target, lines) {
|
|
34325
|
+
const p = path3.join(target, "ai", "manifest", "audit.jsonl");
|
|
34326
|
+
if (!fs4.existsSync(p)) return [];
|
|
34196
34327
|
try {
|
|
34197
|
-
const raw =
|
|
34328
|
+
const raw = fs4.readFileSync(p, "utf8");
|
|
34198
34329
|
const all = raw.split(/\r?\n/).filter((l) => l.trim());
|
|
34199
34330
|
const slice = all.slice(-lines);
|
|
34200
34331
|
const out = [];
|
|
@@ -34209,9 +34340,32 @@ function tail(target, lines = 12) {
|
|
|
34209
34340
|
return [];
|
|
34210
34341
|
}
|
|
34211
34342
|
}
|
|
34212
|
-
function
|
|
34213
|
-
const
|
|
34214
|
-
|
|
34343
|
+
async function fetchRemoteLogs(remoteUrl, projectId, tail) {
|
|
34344
|
+
const result = await fetchState("/v1/state/logs", {
|
|
34345
|
+
baseUrl: remoteUrl,
|
|
34346
|
+
query: `project=${encodeURIComponent(projectId)}&tail=${tail}`
|
|
34347
|
+
});
|
|
34348
|
+
return result?.entries ?? [];
|
|
34349
|
+
}
|
|
34350
|
+
function useLogs(target, intervalMs = 3e3, options = {}) {
|
|
34351
|
+
const { remoteUrl, projectId, tail = 14 } = options;
|
|
34352
|
+
return usePoll(
|
|
34353
|
+
async () => {
|
|
34354
|
+
if (remoteUrl) {
|
|
34355
|
+
if (!projectId) return EMPTY;
|
|
34356
|
+
return fetchRemoteLogs(remoteUrl, projectId, tail);
|
|
34357
|
+
}
|
|
34358
|
+
return tailLocal(target, tail);
|
|
34359
|
+
},
|
|
34360
|
+
intervalMs,
|
|
34361
|
+
EMPTY
|
|
34362
|
+
);
|
|
34363
|
+
}
|
|
34364
|
+
|
|
34365
|
+
// lib/tui/src/panes/logs.tsx
|
|
34366
|
+
var import_jsx_runtime11 = __toESM(require_jsx_runtime());
|
|
34367
|
+
function Logs({ target, remoteUrl, projectId }) {
|
|
34368
|
+
const { data } = useLogs(target, 3e3, { tail: 14, remoteUrl, projectId });
|
|
34215
34369
|
const entries = data ?? [];
|
|
34216
34370
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Pane, { title: "Logs", subtitle: "ai/manifest/audit.jsonl (last 14)", children: entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: theme_default.text.muted, children: "(no audit events yet)" }) : entries.map((e, idx) => {
|
|
34217
34371
|
const time = (e.timestamp || "").slice(11, 19) || "--:--:--";
|
|
@@ -34231,79 +34385,182 @@ function Logs({ target }) {
|
|
|
34231
34385
|
}) });
|
|
34232
34386
|
}
|
|
34233
34387
|
|
|
34388
|
+
// lib/tui/src/panes/mq.tsx
|
|
34389
|
+
var import_jsx_runtime12 = __toESM(require_jsx_runtime());
|
|
34390
|
+
function flag({ label, on }) {
|
|
34391
|
+
const status2 = on ? "on" : "off";
|
|
34392
|
+
const color = on ? theme_default.accent.green : theme_default.text.muted;
|
|
34393
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { children: [
|
|
34394
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { width: 36, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: theme_default.text.secondary, children: label }) }),
|
|
34395
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color, children: status2 })
|
|
34396
|
+
] });
|
|
34397
|
+
}
|
|
34398
|
+
function statRow({ label, value, accent: accent2 }) {
|
|
34399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { children: [
|
|
34400
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { width: 36, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: theme_default.text.secondary, children: label }) }),
|
|
34401
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: accent2, children: value })
|
|
34402
|
+
] });
|
|
34403
|
+
}
|
|
34404
|
+
function Mq({ mq }) {
|
|
34405
|
+
const cfg = mq?.config;
|
|
34406
|
+
const counts = mq?.counts;
|
|
34407
|
+
const merges = mq?.recent_merges ?? [];
|
|
34408
|
+
const modeColor = cfg?.mode === "owned-live" ? theme_default.accent.green : theme_default.accent.amber;
|
|
34409
|
+
const modeLabel = cfg?.mode || "unknown";
|
|
34410
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Pane, { title: "Merge Queue", subtitle: "ai/manifest/merge-queue.json + git log origin/main", children: [
|
|
34411
|
+
statRow({ label: "ai-mq mode", value: modeLabel, accent: modeColor }),
|
|
34412
|
+
flag({ label: "update-branch-before-merge", on: cfg?.update_branch_before_merge }),
|
|
34413
|
+
flag({ label: "trust-prior-ci-on-update-branch", on: cfg?.trust_prior_ci_on_update_branch }),
|
|
34414
|
+
flag({ label: "runner-capacity-gate", on: cfg?.runner_capacity_gate_enabled }),
|
|
34415
|
+
counts ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [
|
|
34416
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: theme_default.text.muted, children: "MCP tool counts" }),
|
|
34417
|
+
statRow({ label: "mcp_tools_total", value: counts.mcp_tools_total, accent: theme_default.accent.cyan }),
|
|
34418
|
+
statRow({ label: "free_mcp_tools_total", value: counts.free_mcp_tools_total, accent: theme_default.accent.cyan }),
|
|
34419
|
+
statRow({ label: "pro_mcp_tools_total", value: counts.pro_mcp_tools_total, accent: theme_default.accent.cyan })
|
|
34420
|
+
] }) : null,
|
|
34421
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: theme_default.text.muted, children: "Recent merges (origin/main, last 10)" }) }),
|
|
34422
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
34423
|
+
Table,
|
|
34424
|
+
{
|
|
34425
|
+
columns: [
|
|
34426
|
+
{ key: "sha", label: "sha", width: 9 },
|
|
34427
|
+
{ key: "subject", label: "subject", width: 60 }
|
|
34428
|
+
],
|
|
34429
|
+
rows: merges,
|
|
34430
|
+
emptyMessage: "(no merges visible \u2014 run: git fetch origin)"
|
|
34431
|
+
}
|
|
34432
|
+
)
|
|
34433
|
+
] });
|
|
34434
|
+
}
|
|
34435
|
+
|
|
34234
34436
|
// lib/tui/src/hooks/use-swarm.ts
|
|
34235
|
-
import
|
|
34236
|
-
import
|
|
34437
|
+
import fs5 from "fs";
|
|
34438
|
+
import path4 from "path";
|
|
34439
|
+
var EMPTY_STATE = {
|
|
34440
|
+
counts: { queue: 0, active: 0, review: 0, done: 0 },
|
|
34441
|
+
recent: [],
|
|
34442
|
+
blocked: 0
|
|
34443
|
+
};
|
|
34237
34444
|
function readBucket(target, bucket) {
|
|
34238
|
-
const dir =
|
|
34239
|
-
if (!
|
|
34445
|
+
const dir = path4.join(target, "ai", "swarm", bucket);
|
|
34446
|
+
if (!fs5.existsSync(dir)) return [];
|
|
34240
34447
|
const out = [];
|
|
34241
|
-
for (const name of
|
|
34448
|
+
for (const name of fs5.readdirSync(dir)) {
|
|
34242
34449
|
if (!name.endsWith(".json")) continue;
|
|
34243
34450
|
try {
|
|
34244
|
-
const raw =
|
|
34451
|
+
const raw = fs5.readFileSync(path4.join(dir, name), "utf8");
|
|
34245
34452
|
out.push(JSON.parse(raw));
|
|
34246
34453
|
} catch {
|
|
34247
34454
|
}
|
|
34248
34455
|
}
|
|
34249
34456
|
return out;
|
|
34250
34457
|
}
|
|
34251
|
-
function
|
|
34252
|
-
|
|
34253
|
-
|
|
34254
|
-
|
|
34255
|
-
|
|
34256
|
-
|
|
34257
|
-
|
|
34258
|
-
|
|
34259
|
-
|
|
34260
|
-
|
|
34261
|
-
|
|
34262
|
-
|
|
34263
|
-
|
|
34264
|
-
|
|
34458
|
+
function readLocalSwarmState(target) {
|
|
34459
|
+
const queue = readBucket(target, "queue");
|
|
34460
|
+
const active = readBucket(target, "active");
|
|
34461
|
+
const review = readBucket(target, "review");
|
|
34462
|
+
const done = readBucket(target, "done");
|
|
34463
|
+
const blocked = queue.filter((t) => t.status === "blocked").length;
|
|
34464
|
+
const recent = [...active, ...review, ...done].sort(
|
|
34465
|
+
(a, b) => String(b.created_at || "").localeCompare(String(a.created_at || ""))
|
|
34466
|
+
).slice(0, 5);
|
|
34467
|
+
return {
|
|
34468
|
+
counts: {
|
|
34469
|
+
queue: queue.length,
|
|
34470
|
+
active: active.length,
|
|
34471
|
+
review: review.length,
|
|
34472
|
+
done: done.length
|
|
34473
|
+
},
|
|
34474
|
+
recent,
|
|
34475
|
+
blocked
|
|
34476
|
+
};
|
|
34477
|
+
}
|
|
34478
|
+
async function fetchRemoteSwarmState(remoteUrl, projectId) {
|
|
34479
|
+
const result = await fetchState(
|
|
34480
|
+
"/v1/state/swarm",
|
|
34481
|
+
{ baseUrl: remoteUrl, query: `project=${encodeURIComponent(projectId)}` }
|
|
34482
|
+
);
|
|
34483
|
+
if (!result) return EMPTY_STATE;
|
|
34484
|
+
const { schema_version: _ignored, ...state } = result;
|
|
34485
|
+
return state;
|
|
34486
|
+
}
|
|
34487
|
+
function useSwarm(target, intervalMs = 3e3, options = {}) {
|
|
34488
|
+
const { remoteUrl, projectId } = options;
|
|
34489
|
+
return usePoll(
|
|
34490
|
+
async () => {
|
|
34491
|
+
if (remoteUrl) {
|
|
34492
|
+
if (!projectId) return EMPTY_STATE;
|
|
34493
|
+
return fetchRemoteSwarmState(remoteUrl, projectId);
|
|
34494
|
+
}
|
|
34495
|
+
return readLocalSwarmState(target);
|
|
34496
|
+
},
|
|
34497
|
+
intervalMs,
|
|
34498
|
+
EMPTY_STATE
|
|
34499
|
+
);
|
|
34265
34500
|
}
|
|
34266
34501
|
|
|
34267
34502
|
// lib/tui/src/hooks/use-sessions.ts
|
|
34268
|
-
import
|
|
34269
|
-
import
|
|
34270
|
-
|
|
34271
|
-
|
|
34272
|
-
|
|
34273
|
-
|
|
34274
|
-
|
|
34503
|
+
import fs6 from "fs";
|
|
34504
|
+
import path5 from "path";
|
|
34505
|
+
var EMPTY_STATE2 = { active: null, archive_count: 0 };
|
|
34506
|
+
function readLocalSessionsState(target) {
|
|
34507
|
+
const activePath = path5.join(target, "ai", "sessions", "active.json");
|
|
34508
|
+
const archiveDir = path5.join(target, "ai", "sessions", "archive");
|
|
34509
|
+
let active = null;
|
|
34510
|
+
try {
|
|
34511
|
+
if (fs6.existsSync(activePath)) {
|
|
34512
|
+
const raw = fs6.readFileSync(activePath, "utf8");
|
|
34513
|
+
const parsed = JSON.parse(raw);
|
|
34514
|
+
if (parsed && parsed.id) active = parsed;
|
|
34515
|
+
}
|
|
34516
|
+
} catch {
|
|
34517
|
+
}
|
|
34518
|
+
let archive_count = 0;
|
|
34519
|
+
if (fs6.existsSync(archiveDir)) {
|
|
34275
34520
|
try {
|
|
34276
|
-
|
|
34277
|
-
const raw = fs5.readFileSync(activePath, "utf8");
|
|
34278
|
-
const parsed = JSON.parse(raw);
|
|
34279
|
-
if (parsed && parsed.id) active = parsed;
|
|
34280
|
-
}
|
|
34521
|
+
archive_count = fs6.readdirSync(archiveDir).filter((n) => n.endsWith(".json")).length;
|
|
34281
34522
|
} catch {
|
|
34282
34523
|
}
|
|
34283
|
-
|
|
34284
|
-
|
|
34285
|
-
|
|
34286
|
-
|
|
34287
|
-
|
|
34288
|
-
|
|
34289
|
-
}
|
|
34290
|
-
|
|
34291
|
-
|
|
34524
|
+
}
|
|
34525
|
+
return { active, archive_count };
|
|
34526
|
+
}
|
|
34527
|
+
async function fetchRemoteSessionsState(remoteUrl, projectId) {
|
|
34528
|
+
const result = await fetchState(
|
|
34529
|
+
"/v1/state/sessions",
|
|
34530
|
+
{ baseUrl: remoteUrl, query: `project=${encodeURIComponent(projectId)}` }
|
|
34531
|
+
);
|
|
34532
|
+
if (!result) return EMPTY_STATE2;
|
|
34533
|
+
const { schema_version: _ignored, ...state } = result;
|
|
34534
|
+
return state;
|
|
34535
|
+
}
|
|
34536
|
+
function useSessions(target, intervalMs = 3e3, options = {}) {
|
|
34537
|
+
const { remoteUrl, projectId } = options;
|
|
34538
|
+
return usePoll(
|
|
34539
|
+
async () => {
|
|
34540
|
+
if (remoteUrl) {
|
|
34541
|
+
if (!projectId) return EMPTY_STATE2;
|
|
34542
|
+
return fetchRemoteSessionsState(remoteUrl, projectId);
|
|
34543
|
+
}
|
|
34544
|
+
return readLocalSessionsState(target);
|
|
34545
|
+
},
|
|
34546
|
+
intervalMs,
|
|
34547
|
+
EMPTY_STATE2
|
|
34548
|
+
);
|
|
34292
34549
|
}
|
|
34293
34550
|
|
|
34294
34551
|
// lib/tui/src/hooks/use-activation.ts
|
|
34295
|
-
import
|
|
34296
|
-
import
|
|
34297
|
-
import
|
|
34298
|
-
import
|
|
34299
|
-
import
|
|
34300
|
-
import { URL } from "url";
|
|
34552
|
+
import fs7 from "fs";
|
|
34553
|
+
import os4 from "os";
|
|
34554
|
+
import path6 from "path";
|
|
34555
|
+
import http2 from "http";
|
|
34556
|
+
import https2 from "https";
|
|
34557
|
+
import { URL as URL2 } from "url";
|
|
34301
34558
|
var API_URL = process.env.ODAI_API_URL || "https://api.0dai.dev";
|
|
34302
|
-
function
|
|
34559
|
+
function readAuthToken2() {
|
|
34303
34560
|
try {
|
|
34304
|
-
const authPath =
|
|
34305
|
-
if (!
|
|
34306
|
-
const parsed = JSON.parse(
|
|
34561
|
+
const authPath = path6.join(os4.homedir(), ".0dai", "auth.json");
|
|
34562
|
+
if (!fs7.existsSync(authPath)) return null;
|
|
34563
|
+
const parsed = JSON.parse(fs7.readFileSync(authPath, "utf8"));
|
|
34307
34564
|
return String(parsed.access_token || "") || null;
|
|
34308
34565
|
} catch {
|
|
34309
34566
|
return null;
|
|
@@ -34312,8 +34569,8 @@ function readAuthToken() {
|
|
|
34312
34569
|
function getJSON(url, headers) {
|
|
34313
34570
|
return new Promise((resolve) => {
|
|
34314
34571
|
try {
|
|
34315
|
-
const u = new
|
|
34316
|
-
const mod = u.protocol === "http:" ?
|
|
34572
|
+
const u = new URL2(url);
|
|
34573
|
+
const mod = u.protocol === "http:" ? http2 : https2;
|
|
34317
34574
|
const req = mod.request(
|
|
34318
34575
|
{
|
|
34319
34576
|
hostname: u.hostname,
|
|
@@ -34349,7 +34606,7 @@ function getJSON(url, headers) {
|
|
|
34349
34606
|
}
|
|
34350
34607
|
function useActivation(intervalMs = 3e4) {
|
|
34351
34608
|
return usePoll(async () => {
|
|
34352
|
-
const token =
|
|
34609
|
+
const token = readAuthToken2();
|
|
34353
34610
|
if (!token) return null;
|
|
34354
34611
|
return getJSON(`${API_URL}/v1/stats/activation`, {
|
|
34355
34612
|
Authorization: `Bearer ${token}`,
|
|
@@ -34358,24 +34615,136 @@ function useActivation(intervalMs = 3e4) {
|
|
|
34358
34615
|
}, intervalMs, null);
|
|
34359
34616
|
}
|
|
34360
34617
|
|
|
34618
|
+
// lib/tui/src/hooks/use-mq.ts
|
|
34619
|
+
import fs8 from "fs";
|
|
34620
|
+
import path7 from "path";
|
|
34621
|
+
import { execFile as execFile2 } from "child_process";
|
|
34622
|
+
function readJSON(p) {
|
|
34623
|
+
try {
|
|
34624
|
+
return JSON.parse(fs8.readFileSync(p, "utf8"));
|
|
34625
|
+
} catch {
|
|
34626
|
+
return null;
|
|
34627
|
+
}
|
|
34628
|
+
}
|
|
34629
|
+
function readMergeQueueConfig(target) {
|
|
34630
|
+
const candidates = [
|
|
34631
|
+
path7.join(target, "ai", "manifest", "merge-queue.json"),
|
|
34632
|
+
path7.join(target, "ai", "meta", "manifest", "merge-queue.json")
|
|
34633
|
+
];
|
|
34634
|
+
for (const p of candidates) {
|
|
34635
|
+
const data = readJSON(p);
|
|
34636
|
+
if (!data) continue;
|
|
34637
|
+
const rollout = data.rollout || {};
|
|
34638
|
+
const gate = data.runner_capacity_gate || {};
|
|
34639
|
+
return {
|
|
34640
|
+
mode: String(rollout.mode || "unknown"),
|
|
34641
|
+
update_branch_before_merge: rollout.update_branch_before_merge === true,
|
|
34642
|
+
trust_prior_ci_on_update_branch: rollout.trust_prior_ci_on_update_branch === true,
|
|
34643
|
+
runner_capacity_gate_enabled: gate.enabled === true
|
|
34644
|
+
};
|
|
34645
|
+
}
|
|
34646
|
+
return null;
|
|
34647
|
+
}
|
|
34648
|
+
function readCounts(target) {
|
|
34649
|
+
const candidates = [
|
|
34650
|
+
path7.join(target, "ai", "manifest", "canonical-counts.json"),
|
|
34651
|
+
path7.join(target, "ai", "meta", "manifest", "canonical-counts.json")
|
|
34652
|
+
];
|
|
34653
|
+
for (const p of candidates) {
|
|
34654
|
+
const data = readJSON(p);
|
|
34655
|
+
if (!data) continue;
|
|
34656
|
+
return {
|
|
34657
|
+
mcp_tools_total: Number(data.mcp_tools_total || 0),
|
|
34658
|
+
free_mcp_tools_total: Number(data.free_mcp_tools_total || 0),
|
|
34659
|
+
pro_mcp_tools_total: Number(data.pro_mcp_tools_total || 0)
|
|
34660
|
+
};
|
|
34661
|
+
}
|
|
34662
|
+
return null;
|
|
34663
|
+
}
|
|
34664
|
+
function recentMerges(target) {
|
|
34665
|
+
return new Promise((resolve) => {
|
|
34666
|
+
execFile2(
|
|
34667
|
+
"git",
|
|
34668
|
+
[
|
|
34669
|
+
"-C",
|
|
34670
|
+
target,
|
|
34671
|
+
"log",
|
|
34672
|
+
"origin/main",
|
|
34673
|
+
"--oneline",
|
|
34674
|
+
"-10",
|
|
34675
|
+
"--no-decorate"
|
|
34676
|
+
],
|
|
34677
|
+
{ timeout: 3e3 },
|
|
34678
|
+
(err, stdout) => {
|
|
34679
|
+
if (err) return resolve([]);
|
|
34680
|
+
const lines = String(stdout || "").trim().split(/\r?\n/).filter(Boolean);
|
|
34681
|
+
const out = [];
|
|
34682
|
+
for (const line of lines) {
|
|
34683
|
+
const space = line.indexOf(" ");
|
|
34684
|
+
if (space <= 0) continue;
|
|
34685
|
+
out.push({
|
|
34686
|
+
sha: line.slice(0, space).trim(),
|
|
34687
|
+
subject: line.slice(space + 1).trim()
|
|
34688
|
+
});
|
|
34689
|
+
}
|
|
34690
|
+
resolve(out);
|
|
34691
|
+
}
|
|
34692
|
+
);
|
|
34693
|
+
});
|
|
34694
|
+
}
|
|
34695
|
+
function useMq(target, intervalMs = 5e3) {
|
|
34696
|
+
return usePoll(
|
|
34697
|
+
async () => {
|
|
34698
|
+
const [config, counts, merges] = [
|
|
34699
|
+
readMergeQueueConfig(target),
|
|
34700
|
+
readCounts(target),
|
|
34701
|
+
await recentMerges(target)
|
|
34702
|
+
];
|
|
34703
|
+
return {
|
|
34704
|
+
config,
|
|
34705
|
+
counts,
|
|
34706
|
+
recent_merges: merges,
|
|
34707
|
+
last_fetched_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
34708
|
+
};
|
|
34709
|
+
},
|
|
34710
|
+
intervalMs,
|
|
34711
|
+
{
|
|
34712
|
+
config: null,
|
|
34713
|
+
counts: null,
|
|
34714
|
+
recent_merges: [],
|
|
34715
|
+
last_fetched_at: ""
|
|
34716
|
+
}
|
|
34717
|
+
);
|
|
34718
|
+
}
|
|
34719
|
+
|
|
34361
34720
|
// lib/tui/src/app.tsx
|
|
34362
|
-
var
|
|
34363
|
-
var PANES = ["overview", "swarm", "sessions", "budget", "agents", "logs"];
|
|
34721
|
+
var import_jsx_runtime13 = __toESM(require_jsx_runtime());
|
|
34722
|
+
var PANES = ["overview", "swarm", "mq", "sessions", "budget", "agents", "logs"];
|
|
34364
34723
|
var LABELS = {
|
|
34365
34724
|
overview: "Overview",
|
|
34366
34725
|
swarm: "Swarm",
|
|
34726
|
+
mq: "Merge Queue",
|
|
34367
34727
|
sessions: "Sessions",
|
|
34368
34728
|
budget: "Budget",
|
|
34369
34729
|
agents: "Agents",
|
|
34370
34730
|
logs: "Logs"
|
|
34371
34731
|
};
|
|
34372
|
-
function App2({
|
|
34732
|
+
function App2({
|
|
34733
|
+
target,
|
|
34734
|
+
version,
|
|
34735
|
+
plan,
|
|
34736
|
+
writeMode = false,
|
|
34737
|
+
remoteUrl,
|
|
34738
|
+
projectId
|
|
34739
|
+
}) {
|
|
34373
34740
|
const { exit } = use_app_default();
|
|
34374
|
-
const [activeKey, setActiveKey] = (0,
|
|
34375
|
-
const [refreshTick, setRefreshTick] = (0,
|
|
34376
|
-
const
|
|
34377
|
-
const
|
|
34741
|
+
const [activeKey, setActiveKey] = (0, import_react24.useState)("overview");
|
|
34742
|
+
const [refreshTick, setRefreshTick] = (0, import_react24.useState)(0);
|
|
34743
|
+
const remoteOpts = { remoteUrl, projectId };
|
|
34744
|
+
const swarm = useSwarm(target, 3e3, remoteOpts);
|
|
34745
|
+
const sessions = useSessions(target, 3e3, remoteOpts);
|
|
34378
34746
|
const activation = useActivation(3e4);
|
|
34747
|
+
const mq = useMq(target, 5e3);
|
|
34379
34748
|
use_input_default((input, key) => {
|
|
34380
34749
|
if (input === "q" || key.escape || key.ctrl && input === "c") {
|
|
34381
34750
|
exit();
|
|
@@ -34395,6 +34764,7 @@ function App2({ target, version, plan }) {
|
|
|
34395
34764
|
swarm.refresh();
|
|
34396
34765
|
sessions.refresh();
|
|
34397
34766
|
activation.refresh();
|
|
34767
|
+
mq.refresh();
|
|
34398
34768
|
setRefreshTick((n) => n + 1);
|
|
34399
34769
|
return;
|
|
34400
34770
|
}
|
|
@@ -34410,14 +34780,15 @@ function App2({ target, version, plan }) {
|
|
|
34410
34780
|
const total = queue + active + review;
|
|
34411
34781
|
hint = total ? total : void 0;
|
|
34412
34782
|
}
|
|
34783
|
+
if (key === "mq" && mq.data?.config?.mode === "owned-live") hint = "live";
|
|
34413
34784
|
if (key === "sessions" && sessions.data?.active) hint = "\u25CF";
|
|
34414
34785
|
return { key, label: LABELS[key], hint };
|
|
34415
34786
|
});
|
|
34416
|
-
const errorSummary = swarm.error || sessions.error || activation.error ? "some data sources unavailable \u2014 press r to retry" : void 0;
|
|
34787
|
+
const errorSummary = swarm.error || sessions.error || activation.error || mq.error ? "some data sources unavailable \u2014 press r to retry" : void 0;
|
|
34417
34788
|
let content;
|
|
34418
34789
|
switch (activeKey) {
|
|
34419
34790
|
case "overview":
|
|
34420
|
-
content = /* @__PURE__ */ (0,
|
|
34791
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
34421
34792
|
Overview,
|
|
34422
34793
|
{
|
|
34423
34794
|
swarm: swarm.data,
|
|
@@ -34427,25 +34798,28 @@ function App2({ target, version, plan }) {
|
|
|
34427
34798
|
);
|
|
34428
34799
|
break;
|
|
34429
34800
|
case "swarm":
|
|
34430
|
-
content = /* @__PURE__ */ (0,
|
|
34801
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Swarm, { swarm: swarm.data });
|
|
34802
|
+
break;
|
|
34803
|
+
case "mq":
|
|
34804
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Mq, { mq: mq.data });
|
|
34431
34805
|
break;
|
|
34432
34806
|
case "sessions":
|
|
34433
|
-
content = /* @__PURE__ */ (0,
|
|
34807
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Sessions, { sessions: sessions.data });
|
|
34434
34808
|
break;
|
|
34435
34809
|
case "budget":
|
|
34436
|
-
content = /* @__PURE__ */ (0,
|
|
34810
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Budget, { target, remoteUrl, projectId });
|
|
34437
34811
|
break;
|
|
34438
34812
|
case "agents":
|
|
34439
|
-
content = /* @__PURE__ */ (0,
|
|
34813
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Agents, { remoteUrl, projectId });
|
|
34440
34814
|
break;
|
|
34441
34815
|
case "logs":
|
|
34442
|
-
content = /* @__PURE__ */ (0,
|
|
34816
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Logs, { target, remoteUrl, projectId });
|
|
34443
34817
|
break;
|
|
34444
34818
|
}
|
|
34445
34819
|
const heartbeat = activation.data?.first_heartbeat_at ? `heartbeat ${activation.data.first_heartbeat_at.slice(11, 19)}` : "heartbeat \u2014";
|
|
34446
|
-
return /* @__PURE__ */ (0,
|
|
34447
|
-
/* @__PURE__ */ (0,
|
|
34448
|
-
/* @__PURE__ */ (0,
|
|
34820
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", minHeight: 20, children: [
|
|
34821
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexGrow: 1, children: [
|
|
34822
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
34449
34823
|
Sidebar,
|
|
34450
34824
|
{
|
|
34451
34825
|
items: navItems,
|
|
@@ -34454,7 +34828,7 @@ function App2({ target, version, plan }) {
|
|
|
34454
34828
|
version
|
|
34455
34829
|
}
|
|
34456
34830
|
),
|
|
34457
|
-
/* @__PURE__ */ (0,
|
|
34831
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
34458
34832
|
Box_default,
|
|
34459
34833
|
{
|
|
34460
34834
|
borderStyle: "single",
|
|
@@ -34469,26 +34843,36 @@ function App2({ target, version, plan }) {
|
|
|
34469
34843
|
}
|
|
34470
34844
|
)
|
|
34471
34845
|
] }),
|
|
34472
|
-
/* @__PURE__ */ (0,
|
|
34846
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
34473
34847
|
Footer,
|
|
34474
34848
|
{
|
|
34475
|
-
hint: "[\u2191\u2193] nav \xB7 [1-
|
|
34476
|
-
status: `${heartbeat} \xB7 ticks: ${refreshTick} \xB7 target: ${target}`
|
|
34849
|
+
hint: writeMode ? "[\u2191\u2193] nav \xB7 [1-7] jump \xB7 [r] refresh \xB7 [w] write-action \xB7 [q] quit" : "[\u2191\u2193] nav \xB7 [1-7] jump \xB7 [r] refresh \xB7 [q] quit \xB7 (read-only \u2014 set ODAI_TUI_WRITE=1 for writes)",
|
|
34850
|
+
status: `${heartbeat} \xB7 ticks: ${refreshTick} \xB7 target: ${target}${writeMode ? " \xB7 WRITE" : " \xB7 RO"}`
|
|
34477
34851
|
}
|
|
34478
34852
|
),
|
|
34479
|
-
errorSummary ? /* @__PURE__ */ (0,
|
|
34853
|
+
errorSummary ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Footer, { hint: errorSummary }) : null
|
|
34480
34854
|
] });
|
|
34481
34855
|
}
|
|
34482
34856
|
|
|
34483
34857
|
// lib/tui/src/index.tsx
|
|
34484
|
-
var
|
|
34858
|
+
var import_jsx_runtime14 = __toESM(require_jsx_runtime());
|
|
34485
34859
|
async function run(opts) {
|
|
34486
34860
|
if (!process.stdout.isTTY) {
|
|
34487
34861
|
process.stderr.write("0dai tui requires a TTY. Try: 0dai status\n");
|
|
34488
34862
|
process.exit(2);
|
|
34489
34863
|
}
|
|
34490
34864
|
const { waitUntilExit } = render_default(
|
|
34491
|
-
/* @__PURE__ */ (0,
|
|
34865
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
34866
|
+
App2,
|
|
34867
|
+
{
|
|
34868
|
+
target: opts.target,
|
|
34869
|
+
version: opts.version,
|
|
34870
|
+
plan: opts.plan,
|
|
34871
|
+
writeMode: !!opts.writeMode,
|
|
34872
|
+
remoteUrl: opts.remoteUrl,
|
|
34873
|
+
projectId: opts.projectId
|
|
34874
|
+
}
|
|
34875
|
+
),
|
|
34492
34876
|
{ exitOnCtrlC: false }
|
|
34493
34877
|
);
|
|
34494
34878
|
await waitUntilExit();
|