@cartanova/qgrid-cli 2.3.5 → 2.3.7

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 (92) hide show
  1. package/bundle/dist/application/qgrid/oauth.js +15 -12
  2. package/bundle/dist/application/qgrid/qgrid-run-lifecycle.js +2 -1
  3. package/bundle/dist/application/qgrid/qgrid.dispatcher.js +2 -1
  4. package/bundle/dist/application/qgrid/qgrid.frame.js +40 -20
  5. package/bundle/dist/application/qgrid/qgrid.types.js +8 -2
  6. package/bundle/dist/application/qgrid/token-subscriber.js +16 -8
  7. package/bundle/dist/application/qgrid/token-trigger-setup.js +3 -2
  8. package/bundle/dist/application/request-log/request-log.model.js +12 -7
  9. package/bundle/dist/application/sonamu.generated.js +11 -3
  10. package/bundle/dist/application/sonamu.generated.sso.js +7 -2
  11. package/bundle/dist/application/token/token.model.js +10 -2
  12. package/bundle/dist/application/token/token.types.js +5 -3
  13. package/bundle/dist/i18n/sd.generated.js +4 -1
  14. package/bundle/dist/migrations/20260630223950_alter_tokens_add1_alter1.js +16 -0
  15. package/bundle/dist/migrations/20260701105327_alter_request_logs_add1_alter1.js +16 -0
  16. package/bundle/dist/migrations/20260701105328_alter_request_log_steps_add1_alter1.js +16 -0
  17. package/bundle/dist/sonamu.config.js +13 -6
  18. package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +99 -12
  19. package/bundle/dist/utils/providers/anthropic/anthropic-quota.js +31 -0
  20. package/bundle/dist/utils/providers/anthropic/claude-session.js +8 -3
  21. package/bundle/dist/utils/providers/common/ttft.js +25 -0
  22. package/bundle/dist/utils/providers/openai/codex-worker.js +35 -7
  23. package/bundle/dist/utils/providers/openai/openai-dispatcher.js +237 -39
  24. package/bundle/dist/utils/providers/openai/openai-quota.js +30 -0
  25. package/bundle/src/application/qgrid/oauth.ts +17 -7
  26. package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +96 -0
  27. package/bundle/src/application/qgrid/qgrid-run-lifecycle.ts +1 -0
  28. package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +16 -0
  29. package/bundle/src/application/qgrid/qgrid.dispatcher.ts +1 -0
  30. package/bundle/src/application/qgrid/qgrid.frame.test.ts +172 -0
  31. package/bundle/src/application/qgrid/qgrid.frame.ts +45 -20
  32. package/bundle/src/application/qgrid/qgrid.types.ts +6 -0
  33. package/bundle/src/application/qgrid/token-subscriber.test.ts +89 -0
  34. package/bundle/src/application/qgrid/token-subscriber.ts +40 -11
  35. package/bundle/src/application/qgrid/token-trigger-setup.ts +2 -1
  36. package/bundle/src/application/qgrid/tool-emulation.test.ts +7 -3
  37. package/bundle/src/application/request-log/request-log.entity.json +3 -0
  38. package/bundle/src/application/request-log/request-log.model.test.ts +92 -0
  39. package/bundle/src/application/request-log/request-log.model.ts +20 -6
  40. package/bundle/src/application/request-log-step/request-log-step.entity.json +3 -0
  41. package/bundle/src/application/sonamu.generated.http +7 -2
  42. package/bundle/src/application/sonamu.generated.sso.ts +5 -0
  43. package/bundle/src/application/sonamu.generated.ts +11 -1
  44. package/bundle/src/application/token/token.entity.json +8 -2
  45. package/bundle/src/application/token/token.model.test.ts +69 -0
  46. package/bundle/src/application/token/token.model.ts +8 -1
  47. package/bundle/src/application/token/token.types.test.ts +34 -0
  48. package/bundle/src/application/token/token.types.ts +5 -0
  49. package/bundle/src/i18n/sd.generated.ts +3 -0
  50. package/bundle/src/migrations/20260630223950_alter_tokens_add1_alter1.ts +15 -0
  51. package/bundle/src/migrations/20260701105327_alter_request_logs_add1_alter1.ts +15 -0
  52. package/bundle/src/migrations/20260701105328_alter_request_log_steps_add1_alter1.ts +15 -0
  53. package/bundle/src/sonamu.config.ts +8 -1
  54. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.test.ts +183 -15
  55. package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +142 -12
  56. package/bundle/src/utils/providers/anthropic/anthropic-quota.test.ts +89 -0
  57. package/bundle/src/utils/providers/anthropic/anthropic-quota.ts +27 -0
  58. package/bundle/src/utils/providers/anthropic/claude-session.ts +7 -2
  59. package/bundle/src/utils/providers/common/provider-dispatcher.ts +1 -0
  60. package/bundle/src/utils/providers/common/ttft.test.ts +37 -0
  61. package/bundle/src/utils/providers/common/ttft.ts +26 -0
  62. package/bundle/src/utils/providers/openai/codex-worker.test.ts +148 -0
  63. package/bundle/src/utils/providers/openai/codex-worker.ts +50 -8
  64. package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +378 -6
  65. package/bundle/src/utils/providers/openai/openai-dispatcher.ts +332 -37
  66. package/bundle/src/utils/providers/openai/openai-quota.test.ts +85 -0
  67. package/bundle/src/utils/providers/openai/openai-quota.ts +46 -0
  68. package/bundle/web-dist/client/assets/index-5WdFEqZT.css +1 -0
  69. package/bundle/web-dist/client/assets/{index-DeThumA3.js → index-jnUY7nJ-.js} +3 -3
  70. package/bundle/web-dist/client/assets/logs-B0pWpDrg.js +1 -0
  71. package/bundle/web-dist/client/assets/routes-7XhVuG7o.js +5 -0
  72. package/bundle/web-dist/client/assets/{sd.generated-7jzAQL2_.js → sd.generated-DXhjjL2f.js} +1 -1
  73. package/bundle/web-dist/client/assets/{services.generated-Bx1Eh7ot.js → services.generated-B7u0U1wC.js} +1 -1
  74. package/bundle/web-dist/client/assets/{show-C6OofoAL.js → show-CcjZ9nr7.js} +3 -3
  75. package/bundle/web-dist/client/assets/tokens-DlwAm0v4.js +1 -0
  76. package/bundle/web-dist/client/index.html +3 -3
  77. package/bundle/web-dist/server/assets/{lazyRouteComponent-BEaI4mOq.js → lazyRouteComponent-B0LkbiYf.js} +1 -2
  78. package/bundle/web-dist/server/assets/{logs-CHM0fhfO.js → logs-dLx6gMXT.js} +17 -5
  79. package/bundle/web-dist/server/assets/{routes-C9xFMJ_r.js → routes-DD3Hb9xO.js} +201 -13
  80. package/bundle/web-dist/server/assets/{sd.generated-BeUHLWKH.js → sd.generated-DdfpD_iN.js} +131 -1
  81. package/bundle/web-dist/server/assets/{services.generated-C39hdSv0.js → services.generated-D3ZCvIkt.js} +5 -4
  82. package/bundle/web-dist/server/assets/{show-BZWqH09U.js → show-BQjeyv6A.js} +76 -25
  83. package/bundle/web-dist/server/assets/{tokens-IPwv3pNB.js → tokens-CSOsBpP7.js} +4 -3
  84. package/bundle/web-dist/server/entry-server.generated.js +6 -7
  85. package/dist/cli.js +67 -15
  86. package/package.json +1 -1
  87. package/bundle/web-dist/client/assets/index-B5DSo5uH.css +0 -1
  88. package/bundle/web-dist/client/assets/logs-CtmOVH58.js +0 -1
  89. package/bundle/web-dist/client/assets/routes-Cfoe9J0I.js +0 -5
  90. package/bundle/web-dist/client/assets/tokens-BAZq8sTo.js +0 -1
  91. package/bundle/web-dist/server/assets/shim-B9ZrFt3Z.js +0 -129
  92. /package/bundle/web-dist/server/assets/{cost-DzjfVtUr.js → cost-CSH_t17I.js} +0 -0
@@ -1,8 +1,8 @@
1
- import { K as __commonJSMin, Y as __toESM, g as require_react, h as require_jsx_runtime, q as __exportAll } from "./sd.generated-BeUHLWKH.js";
2
- import { n as RequestLogService, r as RequestLogStepService } from "./services.generated-C39hdSv0.js";
3
- import { a as Link } from "./lazyRouteComponent-BEaI4mOq.js";
1
+ import { J as __exportAll, X as __toESM, _ as require_react, g as require_jsx_runtime, q as __commonJSMin } from "./sd.generated-DdfpD_iN.js";
2
+ import { n as RequestLogService, r as RequestLogStepService } from "./services.generated-D3ZCvIkt.js";
3
+ import { a as Link } from "./lazyRouteComponent-B0LkbiYf.js";
4
4
  import { t as Route } from "../entry-server.generated.js";
5
- import { n as formatMicroUsd, t as cacheHitRate } from "./cost-DzjfVtUr.js";
5
+ import { n as formatMicroUsd, t as cacheHitRate } from "./cost-CSH_t17I.js";
6
6
  import minpath from "node:path";
7
7
  import minproc from "node:process";
8
8
  import { fileURLToPath as urlToPath } from "node:url";
@@ -25088,11 +25088,15 @@ function RequestShowPage() {
25088
25088
  function formatNum(n) {
25089
25089
  return n.toLocaleString();
25090
25090
  }
25091
+ var TOKENS_PER_SEC_PROVIDERS = /* @__PURE__ */ new Set();
25091
25092
  function tryParseJson(text) {
25092
25093
  try {
25093
- return JSON.parse(text);
25094
+ return {
25095
+ ok: true,
25096
+ value: JSON.parse(text)
25097
+ };
25094
25098
  } catch {
25095
- return null;
25099
+ return { ok: false };
25096
25100
  }
25097
25101
  }
25098
25102
  function safeParseJson(text) {
@@ -25106,7 +25110,7 @@ function safeParseJson(text) {
25106
25110
  function CopyButton({ text }) {
25107
25111
  const [copied, setCopied] = (0, import_react.useState)(false);
25108
25112
  (0, import_react.useEffect)(() => {
25109
- if (!copied) return;
25113
+ if (!copied) return void 0;
25110
25114
  const id = setTimeout(() => setCopied(false), 1500);
25111
25115
  return () => clearTimeout(id);
25112
25116
  }, [copied]);
@@ -25124,7 +25128,7 @@ function CopyButton({ text }) {
25124
25128
  function FormattedContent({ text, markdown }) {
25125
25129
  const parsed = tryParseJson(text);
25126
25130
  const [mode, setMode] = (0, import_react.useState)("pretty");
25127
- if (parsed === null) {
25131
+ if (!parsed.ok) {
25128
25132
  if (markdown) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
25129
25133
  className: "prose prose-sm prose-qgrid max-w-none",
25130
25134
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Markdown, {
@@ -25151,14 +25155,14 @@ function FormattedContent({ text, markdown }) {
25151
25155
  children: "Pretty"
25152
25156
  })]
25153
25157
  }), mode === "pretty" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JsonView, {
25154
- value: parsed,
25158
+ value: parsed.value,
25155
25159
  style: lightTheme,
25156
25160
  displayDataTypes: false,
25157
25161
  enableClipboard: true,
25158
25162
  collapsed: false
25159
25163
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
25160
25164
  className: "text-sm text-sand-800 whitespace-pre-wrap wrap-break-word font-mono leading-relaxed",
25161
- children: JSON.stringify(parsed, null, 2)
25165
+ children: JSON.stringify(parsed.value, null, 2)
25162
25166
  })] });
25163
25167
  }
25164
25168
  function Section({ title, defaultOpen = true, children }) {
@@ -25239,15 +25243,24 @@ function HeaderBar({ data }) {
25239
25243
  });
25240
25244
  }
25241
25245
  function MetricsPanel({ data, toolCallCount }) {
25246
+ const tokensPerSecEnabled = isTokensPerSecEnabled(data);
25242
25247
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25243
25248
  className: "panel overflow-hidden px-5 py-3 space-y-3",
25244
25249
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25245
- className: "grid grid-cols-4 gap-x-6",
25250
+ className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-x-6 gap-y-3",
25246
25251
  children: [
25247
25252
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25248
25253
  label: "Duration",
25249
25254
  value: `${(data.duration_ms / 1e3).toFixed(1)}s`
25250
25255
  }),
25256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25257
+ label: "TTFT",
25258
+ value: formatDurationMs(data.ttft_ms)
25259
+ }),
25260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25261
+ label: "Tokens/sec",
25262
+ value: formatTokensPerSec(data.output_tokens, data.duration_ms, data.ttft_ms, tokensPerSecEnabled)
25263
+ }),
25251
25264
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25252
25265
  label: "Cost",
25253
25266
  value: data.cost_usd !== null ? formatMicroUsd(data.cost_usd) : "—"
@@ -25255,14 +25268,10 @@ function MetricsPanel({ data, toolCallCount }) {
25255
25268
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25256
25269
  label: "Tool Calls",
25257
25270
  value: `${toolCallCount}회`
25258
- }),
25259
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25260
- label: "Cache Hit",
25261
- value: cacheHitRate(data)
25262
25271
  })
25263
25272
  ]
25264
25273
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25265
- className: "border-t border-sand-100/60 pt-3 grid grid-cols-4 gap-x-6",
25274
+ className: "border-t border-sand-100/60 pt-3 grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-x-6 gap-y-3",
25266
25275
  children: [
25267
25276
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25268
25277
  label: "Input",
@@ -25279,15 +25288,41 @@ function MetricsPanel({ data, toolCallCount }) {
25279
25288
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25280
25289
  label: "Cache Write",
25281
25290
  value: formatNum(data.cache_creation_tokens)
25291
+ }),
25292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Metric, {
25293
+ label: "Cache Hit",
25294
+ value: cacheHitRate(data)
25282
25295
  })
25283
25296
  ]
25284
25297
  })]
25285
25298
  });
25286
25299
  }
25287
25300
  function formatDurationMs(ms) {
25288
- if (ms == null) return "—";
25301
+ if (ms === null || ms === void 0) return "—";
25289
25302
  return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${ms}ms`;
25290
25303
  }
25304
+ function detectTokenRateProvider(source) {
25305
+ const modelName = source.model_name?.trim().toLowerCase();
25306
+ if (modelName) {
25307
+ const canonical = modelName.includes("/") ? modelName.split("/").pop() : modelName;
25308
+ if (canonical.startsWith("claude-")) return "anthropic";
25309
+ if (/^(gpt-|codex-|o\d)/.test(canonical)) return "openai";
25310
+ }
25311
+ const tokenName = source.token_name?.trim().toLowerCase();
25312
+ if (tokenName?.startsWith("anthropic/")) return "anthropic";
25313
+ if (tokenName?.startsWith("openai/")) return "openai";
25314
+ return null;
25315
+ }
25316
+ function isTokensPerSecEnabled(source) {
25317
+ const provider = detectTokenRateProvider(source);
25318
+ return provider !== null && TOKENS_PER_SEC_PROVIDERS.has(provider);
25319
+ }
25320
+ function formatTokensPerSec(outputTokens, durationMs, ttftMs, enabled) {
25321
+ if (!enabled || outputTokens === null || outputTokens === void 0 || durationMs === null || durationMs === void 0 || ttftMs === null || ttftMs === void 0) return "—";
25322
+ const generationMs = durationMs - ttftMs;
25323
+ if (outputTokens <= 0 || generationMs <= 0) return "—";
25324
+ return `${Math.round(outputTokens / (generationMs / 1e3)).toLocaleString()} tok/s`;
25325
+ }
25291
25326
  function CompactMetric({ label, value }) {
25292
25327
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
25293
25328
  className: "text-[10px] uppercase tracking-wider text-sand-400 font-medium",
@@ -25387,9 +25422,9 @@ function ReasoningBlock({ stepId }) {
25387
25422
  })]
25388
25423
  });
25389
25424
  }
25390
- function StepTreeItem({ entry }) {
25425
+ function StepTreeItem({ entry, tokensPerSecEnabled }) {
25391
25426
  const generate = entry.generate;
25392
- const hasReasoning = generate != null && generate.reasoningTokens != null && generate.reasoningTokens > 0;
25427
+ const hasReasoning = generate !== null && generate.reasoningTokens !== null && generate.reasoningTokens !== void 0 && generate.reasoningTokens > 0;
25393
25428
  const errorCount = entry.toolCalls.filter((toolCall) => toolCall.error).length;
25394
25429
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", {
25395
25430
  open: true,
@@ -25428,12 +25463,20 @@ function StepTreeItem({ entry }) {
25428
25463
  children: [generate && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25429
25464
  className: "rounded-md border border-sand-100 bg-sand-50/70 p-3",
25430
25465
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
25431
- className: "grid grid-cols-3 md:grid-cols-6 gap-3",
25466
+ className: "grid grid-cols-2 md:grid-cols-4 xl:grid-cols-8 gap-3",
25432
25467
  children: [
25433
25468
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
25434
25469
  label: "Duration",
25435
25470
  value: formatDurationMs(generate.durationMs)
25436
25471
  }),
25472
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
25473
+ label: "TTFT",
25474
+ value: formatDurationMs(generate.ttftMs)
25475
+ }),
25476
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
25477
+ label: "Tokens/sec",
25478
+ value: formatTokensPerSec(generate.outputTokens, generate.durationMs, generate.ttftMs, tokensPerSecEnabled)
25479
+ }),
25437
25480
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
25438
25481
  label: "Input",
25439
25482
  value: formatNum(generate.inputTokens ?? 0)
@@ -25452,7 +25495,7 @@ function StepTreeItem({ entry }) {
25452
25495
  }),
25453
25496
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CompactMetric, {
25454
25497
  label: "Reasoning",
25455
- value: generate.reasoningTokens != null ? formatNum(generate.reasoningTokens) : "—"
25498
+ value: generate.reasoningTokens !== null && generate.reasoningTokens !== void 0 ? formatNum(generate.reasoningTokens) : "—"
25456
25499
  })
25457
25500
  ]
25458
25501
  }), hasReasoning && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReasoningBlock, { stepId: generate.id })]
@@ -25463,14 +25506,17 @@ function StepTreeItem({ entry }) {
25463
25506
  })]
25464
25507
  });
25465
25508
  }
25466
- function StepTreeSection({ steps }) {
25509
+ function StepTreeSection({ steps, tokensPerSecEnabled }) {
25467
25510
  const toolCallCount = steps.reduce((sum, step) => sum + step.toolCalls.length, 0);
25468
25511
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Section, {
25469
25512
  title: `Steps (${steps.length} steps · ${toolCallCount} tool calls)`,
25470
25513
  defaultOpen: true,
25471
25514
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
25472
25515
  className: "space-y-1",
25473
- children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StepTreeItem, { entry: step }, step.stepIndex))
25516
+ children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StepTreeItem, {
25517
+ entry: step,
25518
+ tokensPerSecEnabled
25519
+ }, step.stepIndex))
25474
25520
  })
25475
25521
  });
25476
25522
  }
@@ -25498,6 +25544,7 @@ function buildStepTree(steps) {
25498
25544
  cacheReadTokens: step.cache_read_tokens,
25499
25545
  cacheCreationTokens: step.cache_creation_tokens,
25500
25546
  durationMs: step.duration_ms,
25547
+ ttftMs: step.ttft_ms,
25501
25548
  finishReason: step.finish_reason,
25502
25549
  reasoningTokens: step.reasoning_tokens
25503
25550
  };
@@ -25549,7 +25596,7 @@ function HistorySection({ history }) {
25549
25596
  className: "text-[10px] uppercase tracking-wider text-sand-500 font-medium mb-1",
25550
25597
  children: historyItemLabel(item)
25551
25598
  }),
25552
- item.content != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
25599
+ item.content !== null && item.content !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
25553
25600
  className: "text-[12px] text-sand-700 whitespace-pre-wrap wrap-break-word font-mono leading-relaxed max-h-40 overflow-auto",
25554
25601
  children: extractText(item.content)
25555
25602
  }),
@@ -25596,6 +25643,7 @@ function RequestDetail({ id }) {
25596
25643
  const stepTree = buildStepTree(stepsData?.rows ?? []);
25597
25644
  const toolCalls = stepTree.flatMap((step) => step.toolCalls);
25598
25645
  const hasSteps = stepTree.length > 0;
25646
+ const tokensPerSecEnabled = isTokensPerSecEnabled(data);
25599
25647
  const history = data.history ?? null;
25600
25648
  const hasHistory = history !== null && history.length > 0;
25601
25649
  const promptSections = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -25638,7 +25686,10 @@ function RequestDetail({ id }) {
25638
25686
  children: promptSections
25639
25687
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
25640
25688
  className: "flex-1 min-w-0 space-y-4",
25641
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StepTreeSection, { steps: stepTree })
25689
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StepTreeSection, {
25690
+ steps: stepTree,
25691
+ tokensPerSecEnabled
25692
+ })
25642
25693
  })]
25643
25694
  }) : promptSections,
25644
25695
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Section, {
@@ -1,5 +1,5 @@
1
- import { Y as __toESM, g as require_react, h as require_jsx_runtime, m as useQueryClient } from "./sd.generated-BeUHLWKH.js";
2
- import { i as TokenService, t as QgridService } from "./services.generated-C39hdSv0.js";
1
+ import { X as __toESM, _ as require_react, g as require_jsx_runtime, h as useQueryClient } from "./sd.generated-DdfpD_iN.js";
2
+ import { i as TokenService, t as QgridService } from "./services.generated-D3ZCvIkt.js";
3
3
  import { i as lucideKeyRound, r as Q } from "../entry-server.generated.js";
4
4
  //#region ~icons/lucide/plus.jsx
5
5
  var import_react = /* @__PURE__ */ __toESM(require_react());
@@ -237,7 +237,8 @@ function TokenTable({ data, isLoading }) {
237
237
  if (!editTarget) return;
238
238
  await updateMutation.mutateAsync({
239
239
  id: editTarget.id,
240
- name: editName.trim()
240
+ name: editName.trim(),
241
+ quotaThreshold: editTarget.quota_threshold
241
242
  });
242
243
  await invalidate();
243
244
  setEditTarget(null);
@@ -1,6 +1,5 @@
1
- import { A as hashQueryKeyByOptions, F as partialMatchKey, G as Subscribable, J as __require, K as __commonJSMin, M as matchMutation, N as matchQuery, O as functionalUpdate, P as noop$1, R as resolveStaleTime, S as Query, T as notifyManager, V as skipToken, W as focusManager, Y as __toESM, _ as Mutation, c as number, f as require_react_dom, g as require_react, h as require_jsx_runtime, k as hashKey, l as object, n as SUPPORTED_LOCALES, p as QueryClientProvider, r as setLocale, t as SD, u as string, w as onlineManager, x as infiniteQueryBehavior } from "./assets/sd.generated-BeUHLWKH.js";
2
- import { A as decodePath, C as findRouteMatch, D as createLRUCache, E as processRouteTree, F as last, I as replaceEqualDeep, M as findLast, N as functionalUpdate$1, O as invariant, P as isPromise, S as findFlatMatch, T as processRouteMasks, _ as interpolatePath, a as Link$1, b as trimPath, c as usePrevious$1, d as useRouter, f as getRouterContext, g as cleanPath, h as isNotFound, i as createRootRouteWithContext, j as deepEqual$2, k as createControlledPromise, l as matchContext, m as rootRouteId, n as createFileRoute, p as warning, r as createLazyFileRoute, s as useLayoutEffect$3, t as lazyRouteComponent, u as useRouterState, v as joinPaths, w as findSingleMatch, x as trimPathRight, y as resolvePath } from "./assets/lazyRouteComponent-BEaI4mOq.js";
3
- import { t as require_shim } from "./assets/shim-B9ZrFt3Z.js";
1
+ import { A as hashKey, C as Query, E as notifyManager, F as noop$1, G as focusManager, H as skipToken, I as partialMatchKey, K as Subscribable, N as matchMutation, P as matchQuery, S as infiniteQueryBehavior, T as onlineManager, X as __toESM, Y as __require, _ as require_react, c as number, f as require_react_dom, g as require_jsx_runtime, j as hashQueryKeyByOptions, k as functionalUpdate, l as object, m as QueryClientProvider, n as SUPPORTED_LOCALES, p as require_shim, q as __commonJSMin, r as setLocale, t as SD, u as string, v as Mutation, z as resolveStaleTime } from "./assets/sd.generated-DdfpD_iN.js";
2
+ import { A as decodePath, C as findRouteMatch, D as createLRUCache, E as processRouteTree, F as last, I as replaceEqualDeep, M as findLast, N as functionalUpdate$1, O as invariant, P as isPromise, S as findFlatMatch, T as processRouteMasks, _ as interpolatePath, a as Link$1, b as trimPath, c as usePrevious$1, d as useRouter, f as getRouterContext, g as cleanPath, h as isNotFound, i as createRootRouteWithContext, j as deepEqual$2, k as createControlledPromise, l as matchContext, m as rootRouteId, n as createFileRoute, p as warning, r as createLazyFileRoute, s as useLayoutEffect$3, t as lazyRouteComponent, u as useRouterState, v as joinPaths, w as findSingleMatch, x as trimPathRight, y as resolvePath } from "./assets/lazyRouteComponent-B0LkbiYf.js";
4
3
  //#region ../../node_modules/.pnpm/@tanstack+query-core@5.95.2/node_modules/@tanstack/query-core/build/modern/hydration.js
5
4
  function defaultTransformerFn(data) {
6
5
  return data;
@@ -44559,11 +44558,11 @@ function RootComponent() {
44559
44558
  }
44560
44559
  //#endregion
44561
44560
  //#region src/routes/tokens.tsx
44562
- var $$splitComponentImporter$3 = () => import("./assets/tokens-IPwv3pNB.js");
44561
+ var $$splitComponentImporter$3 = () => import("./assets/tokens-CSOsBpP7.js");
44563
44562
  var Route$3 = createFileRoute("/tokens")({ component: lazyRouteComponent($$splitComponentImporter$3, "component") });
44564
44563
  //#endregion
44565
44564
  //#region src/routes/logs.tsx
44566
- var $$splitComponentImporter$2 = () => import("./assets/logs-CHM0fhfO.js");
44565
+ var $$splitComponentImporter$2 = () => import("./assets/logs-dLx6gMXT.js");
44567
44566
  var logsSearchSchema = object({
44568
44567
  token: string().optional(),
44569
44568
  project: string().optional(),
@@ -44575,11 +44574,11 @@ var Route$2 = createFileRoute("/logs")({
44575
44574
  });
44576
44575
  //#endregion
44577
44576
  //#region src/routes/index.tsx
44578
- var $$splitComponentImporter$1 = () => import("./assets/routes-C9xFMJ_r.js");
44577
+ var $$splitComponentImporter$1 = () => import("./assets/routes-DD3Hb9xO.js");
44579
44578
  var Route$1 = createFileRoute("/")({ component: lazyRouteComponent($$splitComponentImporter$1, "component") });
44580
44579
  //#endregion
44581
44580
  //#region src/routes/requests/show.tsx
44582
- var $$splitComponentImporter = () => import("./assets/show-BZWqH09U.js");
44581
+ var $$splitComponentImporter = () => import("./assets/show-BQjeyv6A.js");
44583
44582
  var showSearchSchema = object({ id: number() });
44584
44583
  var Route = createFileRoute("/requests/show")({
44585
44584
  validateSearch: showSearchSchema,
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { execSync, spawnSync } from "node:child_process";
2
+ import { execFileSync, execSync, spawnSync } from "node:child_process";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -7,12 +7,71 @@ import { Command } from "commander";
7
7
  //#region src/cli.ts
8
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
9
  const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
10
- function hasCommand(command) {
10
+ const RUNTIME_CLI_DEPENDENCIES = [{
11
+ command: "codex",
12
+ packageName: "@openai/codex",
13
+ label: "Codex CLI",
14
+ missingReason: "OpenAI tokens require codex app-server."
15
+ }, {
16
+ command: "claude",
17
+ packageName: "@anthropic-ai/claude-code",
18
+ label: "Claude Code",
19
+ missingReason: "Anthropic tokens require Claude Code."
20
+ }];
21
+ function commandVersion(command) {
11
22
  try {
12
- execSync(`${command} --version`, { stdio: "ignore" });
13
- return true;
23
+ return execFileSync(command, ["--version"], {
24
+ encoding: "utf-8",
25
+ stdio: "pipe"
26
+ }).trim();
14
27
  } catch {
15
- return false;
28
+ return null;
29
+ }
30
+ }
31
+ function packageLatestVersion(packageName) {
32
+ return execFileSync("npm", [
33
+ "view",
34
+ packageName,
35
+ "version"
36
+ ], {
37
+ encoding: "utf-8",
38
+ stdio: "pipe"
39
+ }).trim();
40
+ }
41
+ function parseVersion(output) {
42
+ return output?.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0] ?? null;
43
+ }
44
+ function warnMissingRuntimeCliDependency(dep) {
45
+ console.warn(`Warning: ${dep.command} CLI not found. ${dep.missingReason}`);
46
+ console.warn(`Install: npm i -g ${dep.packageName}`);
47
+ }
48
+ function installRuntimeCliDependency(dep) {
49
+ execFileSync("npm", [
50
+ "i",
51
+ "-g",
52
+ `${dep.packageName}@latest`
53
+ ], { stdio: "inherit" });
54
+ }
55
+ function ensureLatestRuntimeCliDependencies() {
56
+ for (const dep of RUNTIME_CLI_DEPENDENCIES) {
57
+ let installedOutput = commandVersion(dep.command);
58
+ try {
59
+ const installed = parseVersion(installedOutput);
60
+ if (!installed) {
61
+ console.log(`Updating ${dep.label}: installing latest`);
62
+ installRuntimeCliDependency(dep);
63
+ installedOutput = commandVersion(dep.command);
64
+ } else {
65
+ const latest = packageLatestVersion(dep.packageName);
66
+ if (installed === latest) continue;
67
+ console.log(`Updating ${dep.label}: ${installed} → ${latest}`);
68
+ installRuntimeCliDependency(dep);
69
+ installedOutput = commandVersion(dep.command);
70
+ }
71
+ } catch (e) {
72
+ console.warn(`Warning: failed to update ${dep.label}: ${e.message}`);
73
+ }
74
+ if (!installedOutput) warnMissingRuntimeCliDependency(dep);
16
75
  }
17
76
  }
18
77
  function normalizePort(port) {
@@ -25,7 +84,7 @@ function normalizePort(port) {
25
84
  return value;
26
85
  }
27
86
  const program = new Command();
28
- program.name("qgrid").version(pkg.version).description("Qgrid — LLM subscription token proxy server").option("--db <url>", "PostgreSQL connection URL (e.g. postgres://user:pw@host:port/dbname)").option("-p, --port <port>", "server port (default: 44900)").option("--skip-update", "skip auto-update check").action(async (opts) => {
87
+ program.name("qgrid").version(pkg.version).description("Qgrid — LLM subscription token proxy server").option("--db <url>", "PostgreSQL connection URL (e.g. postgres://user:pw@host:port/dbname)").option("-p, --port <port>", "server port (default: 44900)").option("--skip-update", "skip qgrid self-update check").action(async (opts) => {
29
88
  const serverPort = normalizePort(opts.port ?? "44900");
30
89
  try {
31
90
  const pid = execSync(`lsof -ti :${serverPort}`, { encoding: "utf-8" }).trim();
@@ -36,7 +95,7 @@ program.name("qgrid").version(pkg.version).description("Qgrid — LLM subscripti
36
95
  }
37
96
  } catch {}
38
97
  if (!opts.skipUpdate) {
39
- const latest = execSync("npm view @cartanova/qgrid-cli version", { encoding: "utf-8" }).trim();
98
+ const latest = packageLatestVersion("@cartanova/qgrid-cli");
40
99
  if (latest !== pkg.version) {
41
100
  const installCmd = process.env.npm_config_user_agent?.includes("pnpm") || execSync("which qgrid", { encoding: "utf-8" }).includes("pnpm") ? "pnpm add -g @cartanova/qgrid-cli@latest" : "npm i -g @cartanova/qgrid-cli@latest";
42
101
  console.log(`Updating qgrid-cli: ${pkg.version} → ${latest}`);
@@ -46,6 +105,7 @@ program.name("qgrid").version(pkg.version).description("Qgrid — LLM subscripti
46
105
  process.exit(restarted.status ?? 0);
47
106
  }
48
107
  }
108
+ ensureLatestRuntimeCliDependencies();
49
109
  if (opts.db) {
50
110
  const m = opts.db.match(/^postgres(?:ql)?:\/\/([^:]+):(.+)@([^:]+):(\d+)\/(.+)$/);
51
111
  if (!m) {
@@ -60,14 +120,6 @@ program.name("qgrid").version(pkg.version).description("Qgrid — LLM subscripti
60
120
  process.env.QGRID_DB_NAME = dbName;
61
121
  }
62
122
  process.env.PORT = serverPort;
63
- if (!hasCommand("codex")) {
64
- console.warn("Warning: codex CLI not found. OpenAI tokens require codex app-server.");
65
- console.warn("Install: npm i -g @openai/codex");
66
- }
67
- if (!hasCommand("claude")) {
68
- console.warn("Warning: claude CLI not found. Anthropic tokens require Claude Code.");
69
- console.warn("Install: npm i -g @anthropic-ai/claude-code");
70
- }
71
123
  process.env.LR = "remote";
72
124
  const bundlePath = join(__dirname, "..", "bundle");
73
125
  const serverEntry = join(bundlePath, "dist", "index.js");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cartanova/qgrid-cli",
3
- "version": "2.3.5",
3
+ "version": "2.3.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cartanova-ai/qgrid"