@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.
Files changed (48) hide show
  1. package/README.md +30 -5
  2. package/bin/0dai.js +289 -60
  3. package/lib/commands/audit.js +13 -0
  4. package/lib/commands/auth.js +341 -98
  5. package/lib/commands/boneyard.js +44 -0
  6. package/lib/commands/ci.js +329 -0
  7. package/lib/commands/compliance.js +20 -0
  8. package/lib/commands/doctor.js +20 -1
  9. package/lib/commands/experience.js +5 -1
  10. package/lib/commands/feedback.js +92 -5
  11. package/lib/commands/gh.js +506 -0
  12. package/lib/commands/graph.js +78 -10
  13. package/lib/commands/heatmap.js +17 -0
  14. package/lib/commands/import_claude_code_agents.js +367 -0
  15. package/lib/commands/init.js +440 -28
  16. package/lib/commands/loop.js +108 -0
  17. package/lib/commands/mcp.js +410 -0
  18. package/lib/commands/models.js +27 -3
  19. package/lib/commands/paste.js +114 -0
  20. package/lib/commands/play.js +173 -0
  21. package/lib/commands/provider.js +69 -0
  22. package/lib/commands/quota.js +76 -0
  23. package/lib/commands/receipt.js +53 -0
  24. package/lib/commands/report.js +29 -2
  25. package/lib/commands/run.js +44 -4
  26. package/lib/commands/runner.js +527 -0
  27. package/lib/commands/session.js +1 -7
  28. package/lib/commands/standup.js +40 -0
  29. package/lib/commands/status.js +26 -1
  30. package/lib/commands/swarm.js +97 -4
  31. package/lib/commands/tui.js +81 -13
  32. package/lib/commands/usage.js +87 -0
  33. package/lib/commands/vault.js +246 -0
  34. package/lib/onboarding.js +9 -3
  35. package/lib/shared.js +29 -14
  36. package/lib/tui/index.mjs +571 -187
  37. package/lib/utils/auth.js +1 -0
  38. package/lib/utils/canonical-counts.js +54 -0
  39. package/lib/utils/diff-preview.js +192 -0
  40. package/lib/utils/identity.js +76 -18
  41. package/lib/utils/mcp-auth.js +607 -0
  42. package/lib/utils/plan.js +37 -2
  43. package/lib/vault/cipher.js +125 -0
  44. package/lib/vault/identity.js +122 -0
  45. package/lib/vault/index.js +184 -0
  46. package/lib/vault/storage.js +84 -0
  47. package/lib/wizard.js +19 -12
  48. 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 useMemo5(create2, deps) {
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 = useMemo5;
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 React14 = require_react();
7868
+ var React11 = require_react();
7869
7869
  var Scheduler = require_scheduler();
7870
- var ReactSharedInternals = React14.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
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, flag) {
12476
- return (parentContext & flag) !== 0;
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, tail2, lastContentRow, tailMode) {
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: tail2,
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 = tail2;
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 tail2;
16712
+ var tail;
16713
16713
  if (lastContentRow === null) {
16714
- tail2 = workInProgress2.child;
16714
+ tail = workInProgress2.child;
16715
16715
  workInProgress2.child = null;
16716
16716
  } else {
16717
- tail2 = lastContentRow.sibling;
16717
+ tail = lastContentRow.sibling;
16718
16718
  lastContentRow.sibling = null;
16719
16719
  }
16720
16720
  initSuspenseListRenderState(
16721
16721
  workInProgress2,
16722
16722
  false,
16723
16723
  // isBackwards
16724
- tail2,
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, path6, index2) {
22748
- var key = path6[index2];
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 === path6.length) {
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], path6, index2 + 1);
22758
+ updated[key] = copyWithDeleteImpl(obj[key], path8, index2 + 1);
22759
22759
  return updated;
22760
22760
  };
22761
- var copyWithDelete = function(obj, path6) {
22762
- return copyWithDeleteImpl(obj, path6, 0);
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, path6, index2, value) {
22801
- if (index2 >= path6.length) {
22800
+ var copyWithSetImpl = function(obj, path8, index2, value) {
22801
+ if (index2 >= path8.length) {
22802
22802
  return value;
22803
22803
  }
22804
- var key = path6[index2];
22804
+ var key = path8[index2];
22805
22805
  var updated = isArray(obj) ? obj.slice() : assign({}, obj);
22806
- updated[key] = copyWithSetImpl(obj[key], path6, index2 + 1, value);
22806
+ updated[key] = copyWithSetImpl(obj[key], path8, index2 + 1, value);
22807
22807
  return updated;
22808
22808
  };
22809
- var copyWithSet = function(obj, path6, value) {
22810
- return copyWithSetImpl(obj, path6, 0, value);
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, path6, value) {
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, path6, value);
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, path6) {
22833
+ overrideHookStateDeletePath = function(fiber, id, path8) {
22834
22834
  var hook = findHook(fiber, id);
22835
22835
  if (hook !== null) {
22836
- var newState = copyWithDelete(hook.memoizedState, path6);
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, path6, value) {
22860
- fiber.pendingProps = copyWithSet(fiber.memoizedProps, path6, value);
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, path6) {
22870
- fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path6);
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 https2 = __require("https");
25274
- var http2 = __require("http");
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: URL2 } = __require("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 URL2) {
25772
+ if (address instanceof URL3) {
25773
25773
  parsedUrl = address;
25774
25774
  } else {
25775
25775
  try {
25776
- parsedUrl = new URL2(address);
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 ? https2.request : http2.request;
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 URL2(location, address);
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 http2 = __require("http");
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 = http2.createServer((req, res) => {
26378
- const body = http2.STATUS_CODES[426];
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 || http2.STATUS_CODES[code];
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} ${http2.STATUS_CODES[code]}\r
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 React14 = require_react();
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 = React14.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
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 jsx14 = jsxWithValidationDynamic;
28115
- var jsxs13 = jsxWithValidationStatic;
28114
+ var jsx15 = jsxWithValidationDynamic;
28115
+ var jsxs14 = jsxWithValidationStatic;
28116
28116
  exports.Fragment = REACT_FRAGMENT_TYPE;
28117
- exports.jsx = jsx14;
28118
- exports.jsxs = jsxs13;
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(flag, argv = globalThis.Deno ? globalThis.Deno.args : process4.argv) {
31626
- const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
31627
- const position = argv.indexOf(prefix + flag);
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 = (path6) => {
32847
- return path6?.replace(`file://${cwd()}/`, "");
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 import_react26 = __toESM(require_react());
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/panes/budget.tsx
34033
- var import_react23 = __toESM(require_react());
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/panes/budget.tsx
34067
- var import_jsx_runtime9 = __toESM(require_jsx_runtime());
34068
- function Budget({ target }) {
34069
- const fetcher = (0, import_react23.useMemo)(
34070
- () => () => {
34071
- const p = path.join(target, "ai", "swarm", "budget.json");
34072
- if (!fs2.existsSync(p)) return {};
34073
- try {
34074
- return JSON.parse(fs2.readFileSync(p, "utf8"));
34075
- } catch {
34076
- return {};
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
- [target]
34170
+ intervalMs,
34171
+ EMPTY_SNAPSHOT
34080
34172
  );
34081
- const { data } = usePoll(fetcher, 1e4, {});
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/panes/agents.tsx
34118
- var import_react24 = __toESM(require_react());
34214
+ // lib/tui/src/hooks/use-agents.ts
34215
+ var import_react23 = __toESM(require_react());
34119
34216
  import { execFile } from "child_process";
34120
- var import_jsx_runtime10 = __toESM(require_jsx_runtime());
34121
- var AGENTS = [
34122
- { name: "claude", bin: "claude", accent: theme_default.accent.purple },
34123
- { name: "codex", bin: "codex", accent: theme_default.accent.amber },
34124
- { name: "gemini", bin: "gemini", accent: theme_default.accent.cyan },
34125
- { name: "aider", bin: "aider", accent: theme_default.accent.green },
34126
- { name: "opencode", bin: "opencode", accent: theme_default.accent.red },
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 probe(bin) {
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 Agents() {
34139
- const [rows, setRows] = (0, import_react24.useState)(
34140
- AGENTS.map((a) => ({ name: a.name, version: "\u2026", accent: a.accent }))
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, import_react24.useEffect)(() => {
34246
+ (0, import_react23.useEffect)(() => {
34143
34247
  let cancelled = false;
34144
34248
  (async () => {
34145
- const probed = await Promise.all(
34146
- AGENTS.map(async (a) => {
34147
- const version = await probe(a.bin);
34148
- return { name: a.name, version: version || "(not installed)", accent: a.accent };
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/panes/logs.tsx
34189
- var import_react25 = __toESM(require_react());
34190
- import fs3 from "fs";
34191
- import path2 from "path";
34192
- var import_jsx_runtime11 = __toESM(require_jsx_runtime());
34193
- function tail(target, lines = 12) {
34194
- const p = path2.join(target, "ai", "manifest", "audit.jsonl");
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 = fs3.readFileSync(p, "utf8");
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 Logs({ target }) {
34213
- const fetcher = (0, import_react25.useMemo)(() => () => tail(target, 14), [target]);
34214
- const { data } = usePoll(fetcher, 3e3, []);
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 fs4 from "fs";
34236
- import path3 from "path";
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 = path3.join(target, "ai", "swarm", bucket);
34239
- if (!fs4.existsSync(dir)) return [];
34445
+ const dir = path4.join(target, "ai", "swarm", bucket);
34446
+ if (!fs5.existsSync(dir)) return [];
34240
34447
  const out = [];
34241
- for (const name of fs4.readdirSync(dir)) {
34448
+ for (const name of fs5.readdirSync(dir)) {
34242
34449
  if (!name.endsWith(".json")) continue;
34243
34450
  try {
34244
- const raw = fs4.readFileSync(path3.join(dir, name), "utf8");
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 useSwarm(target, intervalMs = 3e3) {
34252
- return usePoll(() => {
34253
- const queue = readBucket(target, "queue");
34254
- const active = readBucket(target, "active");
34255
- const review = readBucket(target, "review");
34256
- const done = readBucket(target, "done");
34257
- const blocked = queue.filter((t) => t.status === "blocked").length;
34258
- const recent = [...active, ...review, ...done].sort((a, b) => String(b.created_at || "").localeCompare(String(a.created_at || ""))).slice(0, 5);
34259
- return {
34260
- counts: { queue: queue.length, active: active.length, review: review.length, done: done.length },
34261
- recent,
34262
- blocked
34263
- };
34264
- }, intervalMs, { counts: { queue: 0, active: 0, review: 0, done: 0 }, recent: [], blocked: 0 });
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 fs5 from "fs";
34269
- import path4 from "path";
34270
- function useSessions(target, intervalMs = 3e3) {
34271
- return usePoll(() => {
34272
- const activePath = path4.join(target, "ai", "sessions", "active.json");
34273
- const archiveDir = path4.join(target, "ai", "sessions", "archive");
34274
- let active = null;
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
- if (fs5.existsSync(activePath)) {
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
- let archive_count = 0;
34284
- if (fs5.existsSync(archiveDir)) {
34285
- try {
34286
- archive_count = fs5.readdirSync(archiveDir).filter((n) => n.endsWith(".json")).length;
34287
- } catch {
34288
- }
34289
- }
34290
- return { active, archive_count };
34291
- }, intervalMs, { active: null, archive_count: 0 });
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 fs6 from "fs";
34296
- import os3 from "os";
34297
- import path5 from "path";
34298
- import http from "http";
34299
- import https from "https";
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 readAuthToken() {
34559
+ function readAuthToken2() {
34303
34560
  try {
34304
- const authPath = path5.join(os3.homedir(), ".0dai", "auth.json");
34305
- if (!fs6.existsSync(authPath)) return null;
34306
- const parsed = JSON.parse(fs6.readFileSync(authPath, "utf8"));
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 URL(url);
34316
- const mod = u.protocol === "http:" ? http : https;
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 = readAuthToken();
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 import_jsx_runtime12 = __toESM(require_jsx_runtime());
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({ target, version, plan }) {
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, import_react26.useState)("overview");
34375
- const [refreshTick, setRefreshTick] = (0, import_react26.useState)(0);
34376
- const swarm = useSwarm(target, 3e3);
34377
- const sessions = useSessions(target, 3e3);
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, import_jsx_runtime12.jsx)(
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, import_jsx_runtime12.jsx)(Swarm, { swarm: swarm.data });
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, import_jsx_runtime12.jsx)(Sessions, { sessions: sessions.data });
34807
+ content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Sessions, { sessions: sessions.data });
34434
34808
  break;
34435
34809
  case "budget":
34436
- content = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Budget, { target });
34810
+ content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Budget, { target, remoteUrl, projectId });
34437
34811
  break;
34438
34812
  case "agents":
34439
- content = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Agents, {});
34813
+ content = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Agents, { remoteUrl, projectId });
34440
34814
  break;
34441
34815
  case "logs":
34442
- content = /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Logs, { target });
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, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", minHeight: 20, children: [
34447
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexGrow: 1, children: [
34448
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
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, import_jsx_runtime12.jsx)(
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, import_jsx_runtime12.jsx)(
34846
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
34473
34847
  Footer,
34474
34848
  {
34475
- hint: "[\u2191\u2193] nav \xB7 [1-6] jump \xB7 [r] refresh \xB7 [q] quit",
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, import_jsx_runtime12.jsx)(Footer, { hint: errorSummary }) : null
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 import_jsx_runtime13 = __toESM(require_jsx_runtime());
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, import_jsx_runtime13.jsx)(App2, { target: opts.target, version: opts.version, plan: opts.plan }),
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();