@open-mercato/ai-assistant 0.6.1-develop.3291.1.6fad645fd0 → 0.6.1
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +30 -4
- package/dist/frontend/components/AiChatButton.js +3 -2
- package/dist/frontend/components/AiChatButton.js.map +2 -2
- package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-LOOP-001-006.spec.js +364 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-AGENT-LOOP-001-006.spec.js.map +7 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js +7 -7
- package/dist/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.js.map +2 -2
- package/dist/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.js +182 -0
- package/dist/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/loop-override/route.js +316 -0
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/loop-override/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js +8 -7
- package/dist/modules/ai_assistant/api/ai/agents/[agentId]/models/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/ai/chat/route.js +43 -20
- package/dist/modules/ai_assistant/api/ai/chat/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/settings/route.js +4 -3
- package/dist/modules/ai_assistant/api/settings/route.js.map +2 -2
- package/dist/modules/ai_assistant/api/usage/daily/route.js +111 -0
- package/dist/modules/ai_assistant/api/usage/daily/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/usage/sessions/[sessionId]/route.js +108 -0
- package/dist/modules/ai_assistant/api/usage/sessions/[sessionId]/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/usage/sessions/route.js +153 -0
- package/dist/modules/ai_assistant/api/usage/sessions/route.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js +335 -38
- package/dist/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js +2 -7
- package/dist/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js +44 -35
- package/dist/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.js.map +2 -2
- package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.js +282 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.js +25 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/cli.js +12 -0
- package/dist/modules/ai_assistant/cli.js.map +2 -2
- package/dist/modules/ai_assistant/components/AiAssistantSettingsPageClient.js.map +1 -1
- package/dist/modules/ai_assistant/data/entities.js +177 -1
- package/dist/modules/ai_assistant/data/entities.js.map +2 -2
- package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js +104 -2
- package/dist/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.js.map +2 -2
- package/dist/modules/ai_assistant/data/repositories/AiTokenUsageRepository.js +168 -0
- package/dist/modules/ai_assistant/data/repositories/AiTokenUsageRepository.js.map +7 -0
- package/dist/modules/ai_assistant/events.js +8 -0
- package/dist/modules/ai_assistant/events.js.map +2 -2
- package/dist/modules/ai_assistant/i18n/de.json +74 -1
- package/dist/modules/ai_assistant/i18n/en.json +74 -1
- package/dist/modules/ai_assistant/i18n/es.json +75 -2
- package/dist/modules/ai_assistant/i18n/pl.json +74 -1
- package/dist/modules/ai_assistant/lib/agent-policy.js.map +2 -2
- package/dist/modules/ai_assistant/lib/agent-runtime.js +588 -23
- package/dist/modules/ai_assistant/lib/agent-runtime.js.map +3 -3
- package/dist/modules/ai_assistant/lib/agent-tools.js +6 -1
- package/dist/modules/ai_assistant/lib/agent-tools.js.map +2 -2
- package/dist/modules/ai_assistant/lib/ai-agent-definition.js.map +2 -2
- package/dist/modules/ai_assistant/lib/model-factory.js +63 -22
- package/dist/modules/ai_assistant/lib/model-factory.js.map +2 -2
- package/dist/modules/ai_assistant/lib/token-usage-recorder.js +78 -0
- package/dist/modules/ai_assistant/lib/token-usage-recorder.js.map +7 -0
- package/dist/modules/ai_assistant/lib/usage-serialization.js +33 -0
- package/dist/modules/ai_assistant/lib/usage-serialization.js.map +7 -0
- package/dist/modules/ai_assistant/migrations/Migration20260508160000_ai_agent_loop_overrides.js +25 -0
- package/dist/modules/ai_assistant/migrations/Migration20260508160000_ai_agent_loop_overrides.js.map +7 -0
- package/dist/modules/ai_assistant/migrations/Migration20260508170000_ai_token_usage.js +88 -0
- package/dist/modules/ai_assistant/migrations/Migration20260508170000_ai_token_usage.js.map +7 -0
- package/dist/modules/ai_assistant/setup.js +34 -0
- package/dist/modules/ai_assistant/setup.js.map +2 -2
- package/dist/modules/ai_assistant/workers/ai-token-usage-prune.js +114 -0
- package/dist/modules/ai_assistant/workers/ai-token-usage-prune.js.map +7 -0
- package/generated/entities/ai_agent_runtime_override/index.ts +7 -0
- package/generated/entities/ai_token_usage_daily/index.ts +16 -0
- package/generated/entities/ai_token_usage_event/index.ts +19 -0
- package/generated/entities.ids.generated.ts +2 -0
- package/generated/entity-fields-registry.ts +47 -1
- package/package.json +15 -7
- package/src/frontend/components/AiChatButton.tsx +3 -2
- package/src/modules/ai_assistant/__integration__/TC-AI-AGENT-LOOP-001-006.spec.ts +521 -0
- package/src/modules/ai_assistant/__integration__/TC-AI-RUNTIME-OVERRIDES-006-model-picker.spec.ts +8 -8
- package/src/modules/ai_assistant/__integration__/TC-AI-TOKEN-USAGE-001-005.spec.ts +231 -0
- package/src/modules/ai_assistant/__tests__/events.test.ts +4 -3
- package/src/modules/ai_assistant/__tests__/settings-page-logic.test.ts +5 -5
- package/src/modules/ai_assistant/__tests__/token-usage-recorder.test.ts +109 -0
- package/src/modules/ai_assistant/api/ai/agents/[agentId]/loop-override/route.ts +388 -0
- package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/__tests__/route.test.ts +5 -0
- package/src/modules/ai_assistant/api/ai/agents/[agentId]/models/route.ts +8 -7
- package/src/modules/ai_assistant/api/ai/chat/__tests__/route.test.ts +102 -5
- package/src/modules/ai_assistant/api/ai/chat/route.ts +55 -18
- package/src/modules/ai_assistant/api/settings/route.ts +5 -3
- package/src/modules/ai_assistant/api/usage/daily/__tests__/route.test.ts +159 -0
- package/src/modules/ai_assistant/api/usage/daily/route.ts +126 -0
- package/src/modules/ai_assistant/api/usage/sessions/[sessionId]/__tests__/route.test.ts +143 -0
- package/src/modules/ai_assistant/api/usage/sessions/[sessionId]/route.ts +130 -0
- package/src/modules/ai_assistant/api/usage/sessions/__tests__/route.test.ts +123 -0
- package/src/modules/ai_assistant/api/usage/sessions/route.ts +184 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/agents/AiAgentSettingsPageClient.tsx +372 -16
- package/src/modules/ai_assistant/backend/config/ai-assistant/allowlist/AiTenantAllowlistPageClient.tsx +1 -4
- package/src/modules/ai_assistant/backend/config/ai-assistant/playground/AiPlaygroundPageClient.tsx +26 -9
- package/src/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.tsx +469 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.ts +23 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/usage/page.tsx +12 -0
- package/src/modules/ai_assistant/cli.ts +18 -0
- package/src/modules/ai_assistant/components/AiAssistantSettingsPageClient.tsx +1 -1
- package/src/modules/ai_assistant/data/entities.ts +237 -0
- package/src/modules/ai_assistant/data/repositories/AiAgentRuntimeOverrideRepository.ts +135 -3
- package/src/modules/ai_assistant/data/repositories/AiTokenUsageRepository.ts +213 -0
- package/src/modules/ai_assistant/data/repositories/__tests__/AiAgentRuntimeOverrideRepository.test.ts +223 -0
- package/src/modules/ai_assistant/data/repositories/__tests__/AiTokenUsageRepository.test.ts +58 -0
- package/src/modules/ai_assistant/events.ts +8 -0
- package/src/modules/ai_assistant/i18n/de.json +74 -1
- package/src/modules/ai_assistant/i18n/en.json +74 -1
- package/src/modules/ai_assistant/i18n/es.json +75 -2
- package/src/modules/ai_assistant/i18n/pl.json +74 -1
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase0.test.ts +439 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase1.test.ts +243 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase2.test.ts +388 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime-loop-phase3.test.ts +359 -0
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime-phase4a.test.ts +2 -2
- package/src/modules/ai_assistant/lib/__tests__/agent-runtime.test.ts +2 -1
- package/src/modules/ai_assistant/lib/__tests__/max-steps-budget.integration.test.ts +12 -13
- package/src/modules/ai_assistant/lib/__tests__/model-factory.test.ts +77 -14
- package/src/modules/ai_assistant/lib/agent-policy.ts +9 -0
- package/src/modules/ai_assistant/lib/agent-runtime.ts +1148 -43
- package/src/modules/ai_assistant/lib/agent-tools.ts +5 -1
- package/src/modules/ai_assistant/lib/ai-agent-definition.ts +289 -2
- package/src/modules/ai_assistant/lib/model-factory.ts +128 -43
- package/src/modules/ai_assistant/lib/token-usage-recorder.ts +122 -0
- package/src/modules/ai_assistant/lib/usage-serialization.ts +29 -0
- package/src/modules/ai_assistant/migrations/.snapshot-open-mercato.json +791 -0
- package/src/modules/ai_assistant/migrations/Migration20260508160000_ai_agent_loop_overrides.ts +25 -0
- package/src/modules/ai_assistant/migrations/Migration20260508170000_ai_token_usage.ts +89 -0
- package/src/modules/ai_assistant/setup.ts +49 -0
- package/src/modules/ai_assistant/workers/__tests__/ai-token-usage-prune.test.ts +144 -0
- package/src/modules/ai_assistant/workers/ai-token-usage-prune.ts +188 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { BarChart2, ChevronRight, Loader2 } from "lucide-react";
|
|
6
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
7
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
8
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
9
|
+
import { Label } from "@open-mercato/ui/primitives/label";
|
|
10
|
+
import {
|
|
11
|
+
Dialog,
|
|
12
|
+
DialogContent,
|
|
13
|
+
DialogHeader,
|
|
14
|
+
DialogTitle
|
|
15
|
+
} from "@open-mercato/ui/primitives/dialog";
|
|
16
|
+
import { apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
17
|
+
function todayIso() {
|
|
18
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
19
|
+
}
|
|
20
|
+
function daysAgoIso(days) {
|
|
21
|
+
const date = /* @__PURE__ */ new Date();
|
|
22
|
+
date.setDate(date.getDate() - days);
|
|
23
|
+
return date.toISOString().slice(0, 10);
|
|
24
|
+
}
|
|
25
|
+
async function fetchDailyRollup(from, to) {
|
|
26
|
+
const params = new URLSearchParams({ from, to });
|
|
27
|
+
const { result, status } = await apiCallOrThrow(
|
|
28
|
+
`/api/ai_assistant/usage/daily?${params}`,
|
|
29
|
+
{ method: "GET", credentials: "include" },
|
|
30
|
+
{ errorMessage: "Failed to load daily token usage" }
|
|
31
|
+
);
|
|
32
|
+
if (!result) throw new Error(`Failed to load daily usage (${status})`);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
async function fetchSessions(from, to, offset) {
|
|
36
|
+
const params = new URLSearchParams({ from, to, limit: "50", offset: String(offset) });
|
|
37
|
+
const { result, status } = await apiCallOrThrow(
|
|
38
|
+
`/api/ai_assistant/usage/sessions?${params}`,
|
|
39
|
+
{ method: "GET", credentials: "include" },
|
|
40
|
+
{ errorMessage: "Failed to load session list" }
|
|
41
|
+
);
|
|
42
|
+
if (!result) throw new Error(`Failed to load sessions (${status})`);
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
async function fetchSessionDetail(sessionId) {
|
|
46
|
+
const { result, status } = await apiCallOrThrow(
|
|
47
|
+
`/api/ai_assistant/usage/sessions/${encodeURIComponent(sessionId)}`,
|
|
48
|
+
{ method: "GET", credentials: "include" },
|
|
49
|
+
{ errorMessage: "Failed to load session detail" }
|
|
50
|
+
);
|
|
51
|
+
if (!result) throw new Error(`Failed to load session detail (${status})`);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
function sumBigintRows(rows, field) {
|
|
55
|
+
return rows.reduce((acc, row) => acc + parseInt(String(row[field] ?? "0"), 10), 0);
|
|
56
|
+
}
|
|
57
|
+
function formatNumber(value) {
|
|
58
|
+
return value.toLocaleString();
|
|
59
|
+
}
|
|
60
|
+
function formatDate(iso) {
|
|
61
|
+
return new Date(iso).toLocaleString();
|
|
62
|
+
}
|
|
63
|
+
function shortId(id) {
|
|
64
|
+
return id.slice(0, 8);
|
|
65
|
+
}
|
|
66
|
+
function AiUsageStatsPageClient() {
|
|
67
|
+
const t = useT();
|
|
68
|
+
const defaultFrom = daysAgoIso(30);
|
|
69
|
+
const defaultTo = todayIso();
|
|
70
|
+
const [from, setFrom] = React.useState(defaultFrom);
|
|
71
|
+
const [to, setTo] = React.useState(defaultTo);
|
|
72
|
+
const [appliedFrom, setAppliedFrom] = React.useState(defaultFrom);
|
|
73
|
+
const [appliedTo, setAppliedTo] = React.useState(defaultTo);
|
|
74
|
+
const [sessionsOffset, setSessionsOffset] = React.useState(0);
|
|
75
|
+
const [selectedSessionId, setSelectedSessionId] = React.useState(null);
|
|
76
|
+
const dailyQuery = useQuery({
|
|
77
|
+
queryKey: ["ai-usage-daily", appliedFrom, appliedTo],
|
|
78
|
+
queryFn: () => fetchDailyRollup(appliedFrom, appliedTo)
|
|
79
|
+
});
|
|
80
|
+
const sessionsQuery = useQuery({
|
|
81
|
+
queryKey: ["ai-usage-sessions", appliedFrom, appliedTo, sessionsOffset],
|
|
82
|
+
queryFn: () => fetchSessions(appliedFrom, appliedTo, sessionsOffset)
|
|
83
|
+
});
|
|
84
|
+
const sessionDetailQuery = useQuery({
|
|
85
|
+
queryKey: ["ai-usage-session-detail", selectedSessionId],
|
|
86
|
+
queryFn: () => fetchSessionDetail(selectedSessionId),
|
|
87
|
+
enabled: selectedSessionId !== null
|
|
88
|
+
});
|
|
89
|
+
function applyFilter() {
|
|
90
|
+
setSessionsOffset(0);
|
|
91
|
+
setAppliedFrom(from);
|
|
92
|
+
setAppliedTo(to);
|
|
93
|
+
}
|
|
94
|
+
const dailyRows = dailyQuery.data?.rows ?? [];
|
|
95
|
+
const totalInputTokens = sumBigintRows(dailyRows, "inputTokens");
|
|
96
|
+
const totalOutputTokens = sumBigintRows(dailyRows, "outputTokens");
|
|
97
|
+
const totalSteps = sumBigintRows(dailyRows, "stepCount");
|
|
98
|
+
const totalSessions = sumBigintRows(dailyRows, "sessionCount");
|
|
99
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6 max-w-5xl", children: [
|
|
100
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
101
|
+
/* @__PURE__ */ jsx(BarChart2, { className: "text-muted-foreground", size: 20 }),
|
|
102
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: t("ai_assistant.usage.title", "Token Usage Statistics") })
|
|
103
|
+
] }),
|
|
104
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-4 flex-wrap", children: [
|
|
105
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
106
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "usage-from", children: t("ai_assistant.usage.from", "From") }),
|
|
107
|
+
/* @__PURE__ */ jsx(
|
|
108
|
+
Input,
|
|
109
|
+
{
|
|
110
|
+
id: "usage-from",
|
|
111
|
+
type: "date",
|
|
112
|
+
value: from,
|
|
113
|
+
onChange: (e) => setFrom(e.target.value),
|
|
114
|
+
className: "w-40"
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
] }),
|
|
118
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
119
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "usage-to", children: t("ai_assistant.usage.to", "To") }),
|
|
120
|
+
/* @__PURE__ */ jsx(
|
|
121
|
+
Input,
|
|
122
|
+
{
|
|
123
|
+
id: "usage-to",
|
|
124
|
+
type: "date",
|
|
125
|
+
value: to,
|
|
126
|
+
onChange: (e) => setTo(e.target.value),
|
|
127
|
+
className: "w-40"
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
] }),
|
|
131
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: applyFilter, children: t("ai_assistant.usage.apply", "Apply") })
|
|
132
|
+
] }),
|
|
133
|
+
dailyQuery.isLoading && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground text-sm", children: [
|
|
134
|
+
/* @__PURE__ */ jsx(Loader2, { size: 14, className: "animate-spin" }),
|
|
135
|
+
t("ai_assistant.usage.loading", "Loading usage data...")
|
|
136
|
+
] }),
|
|
137
|
+
dailyQuery.isError && /* @__PURE__ */ jsx("p", { className: "text-status-error-text text-sm", children: t("ai_assistant.usage.error", "Failed to load usage data.") }),
|
|
138
|
+
dailyQuery.isSuccess && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-4", children: [
|
|
139
|
+
{ label: t("ai_assistant.usage.inputTokens", "Input tokens"), value: formatNumber(totalInputTokens) },
|
|
140
|
+
{ label: t("ai_assistant.usage.outputTokens", "Output tokens"), value: formatNumber(totalOutputTokens) },
|
|
141
|
+
{ label: t("ai_assistant.usage.steps", "Steps"), value: formatNumber(totalSteps) },
|
|
142
|
+
{ label: t("ai_assistant.usage.sessions", "Sessions"), value: formatNumber(totalSessions) }
|
|
143
|
+
].map((tile) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border p-4 space-y-1", children: [
|
|
144
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: tile.label }),
|
|
145
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-xl", children: tile.value })
|
|
146
|
+
] }, tile.label)) }),
|
|
147
|
+
dailyQuery.isSuccess && dailyRows.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
148
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-muted-foreground", children: t("ai_assistant.usage.dailyBreakdown", "Daily breakdown") }),
|
|
149
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full text-sm", children: [
|
|
150
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted/40", children: [
|
|
151
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-muted-foreground", children: t("ai_assistant.usage.col.day", "Day") }),
|
|
152
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-muted-foreground", children: t("ai_assistant.usage.col.agent", "Agent") }),
|
|
153
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium text-muted-foreground", children: t("ai_assistant.usage.col.inputTokens", "Input") }),
|
|
154
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium text-muted-foreground", children: t("ai_assistant.usage.col.outputTokens", "Output") }),
|
|
155
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium text-muted-foreground", children: t("ai_assistant.usage.col.sessions", "Sessions") })
|
|
156
|
+
] }) }),
|
|
157
|
+
/* @__PURE__ */ jsx("tbody", { children: dailyRows.map((row) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-border last:border-b-0", children: [
|
|
158
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 tabular-nums", children: row.day }),
|
|
159
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 font-mono text-xs", children: row.agentId }),
|
|
160
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: formatNumber(parseInt(row.inputTokens, 10)) }),
|
|
161
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: formatNumber(parseInt(row.outputTokens, 10)) }),
|
|
162
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: row.sessionCount })
|
|
163
|
+
] }, row.id)) })
|
|
164
|
+
] }) })
|
|
165
|
+
] }),
|
|
166
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
167
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-muted-foreground", children: t("ai_assistant.usage.sessionsList", "Sessions") }),
|
|
168
|
+
sessionsQuery.isLoading && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground text-sm", children: [
|
|
169
|
+
/* @__PURE__ */ jsx(Loader2, { size: 14, className: "animate-spin" }),
|
|
170
|
+
t("ai_assistant.usage.loadingSessions", "Loading sessions...")
|
|
171
|
+
] }),
|
|
172
|
+
sessionsQuery.isError && /* @__PURE__ */ jsx("p", { className: "text-status-error-text text-sm", children: t("ai_assistant.usage.errorSessions", "Failed to load sessions.") }),
|
|
173
|
+
sessionsQuery.isSuccess && (sessionsQuery.data?.sessions ?? []).length === 0 && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: t("ai_assistant.usage.noSessions", "No sessions found for the selected period.") }),
|
|
174
|
+
sessionsQuery.isSuccess && (sessionsQuery.data?.sessions ?? []).length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
175
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full text-sm", children: [
|
|
176
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted/40", children: [
|
|
177
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-muted-foreground", children: t("ai_assistant.usage.col.session", "Session") }),
|
|
178
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-muted-foreground", children: t("ai_assistant.usage.col.agent", "Agent") }),
|
|
179
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-muted-foreground", children: t("ai_assistant.usage.col.startedAt", "Started") }),
|
|
180
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium text-muted-foreground", children: t("ai_assistant.usage.col.inputTokens", "Input") }),
|
|
181
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium text-muted-foreground", children: t("ai_assistant.usage.col.outputTokens", "Output") }),
|
|
182
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium text-muted-foreground", children: t("ai_assistant.usage.col.steps", "Steps") }),
|
|
183
|
+
/* @__PURE__ */ jsx("th", { className: "w-8" })
|
|
184
|
+
] }) }),
|
|
185
|
+
/* @__PURE__ */ jsx("tbody", { children: (sessionsQuery.data?.sessions ?? []).map((session) => /* @__PURE__ */ jsxs(
|
|
186
|
+
"tr",
|
|
187
|
+
{
|
|
188
|
+
className: "border-b border-border last:border-b-0 cursor-pointer hover:bg-muted/30 transition-colors",
|
|
189
|
+
onClick: () => setSelectedSessionId(session.sessionId),
|
|
190
|
+
children: [
|
|
191
|
+
/* @__PURE__ */ jsxs("td", { className: "px-3 py-2 font-mono text-xs", children: [
|
|
192
|
+
shortId(session.sessionId),
|
|
193
|
+
"\u2026"
|
|
194
|
+
] }),
|
|
195
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 font-mono text-xs", children: session.agentId }),
|
|
196
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-muted-foreground text-xs", children: formatDate(session.startedAt) }),
|
|
197
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: formatNumber(session.inputTokens) }),
|
|
198
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: formatNumber(session.outputTokens) }),
|
|
199
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: session.stepCount }),
|
|
200
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-muted-foreground", children: /* @__PURE__ */ jsx(ChevronRight, { size: 14 }) })
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
session.sessionId
|
|
204
|
+
)) })
|
|
205
|
+
] }) }),
|
|
206
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
207
|
+
/* @__PURE__ */ jsx(
|
|
208
|
+
Button,
|
|
209
|
+
{
|
|
210
|
+
variant: "secondary",
|
|
211
|
+
size: "sm",
|
|
212
|
+
disabled: sessionsOffset === 0,
|
|
213
|
+
onClick: () => setSessionsOffset(Math.max(0, sessionsOffset - 50)),
|
|
214
|
+
children: t("ai_assistant.usage.prev", "Previous")
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground text-sm", children: [
|
|
218
|
+
sessionsOffset + 1,
|
|
219
|
+
"\u2013",
|
|
220
|
+
sessionsOffset + (sessionsQuery.data?.sessions.length ?? 0),
|
|
221
|
+
sessionsQuery.data?.total !== void 0 ? ` / ${sessionsQuery.data.total}` : ""
|
|
222
|
+
] }),
|
|
223
|
+
/* @__PURE__ */ jsx(
|
|
224
|
+
Button,
|
|
225
|
+
{
|
|
226
|
+
variant: "secondary",
|
|
227
|
+
size: "sm",
|
|
228
|
+
disabled: (sessionsQuery.data?.sessions.length ?? 0) < 50 || sessionsOffset + 50 >= (sessionsQuery.data?.total ?? 0),
|
|
229
|
+
onClick: () => setSessionsOffset(sessionsOffset + 50),
|
|
230
|
+
children: t("ai_assistant.usage.next", "Next")
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
] })
|
|
234
|
+
] })
|
|
235
|
+
] }),
|
|
236
|
+
/* @__PURE__ */ jsx(
|
|
237
|
+
Dialog,
|
|
238
|
+
{
|
|
239
|
+
open: selectedSessionId !== null,
|
|
240
|
+
onOpenChange: (open) => {
|
|
241
|
+
if (!open) setSelectedSessionId(null);
|
|
242
|
+
},
|
|
243
|
+
children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-3xl max-h-[80vh] overflow-y-auto", children: [
|
|
244
|
+
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsxs(DialogTitle, { children: [
|
|
245
|
+
t("ai_assistant.usage.sessionDetail", "Session detail"),
|
|
246
|
+
selectedSessionId && /* @__PURE__ */ jsxs("span", { className: "ml-2 font-mono text-sm text-muted-foreground", children: [
|
|
247
|
+
shortId(selectedSessionId),
|
|
248
|
+
"\u2026"
|
|
249
|
+
] })
|
|
250
|
+
] }) }),
|
|
251
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4", children: [
|
|
252
|
+
sessionDetailQuery.isLoading && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground text-sm", children: [
|
|
253
|
+
/* @__PURE__ */ jsx(Loader2, { size: 14, className: "animate-spin" }),
|
|
254
|
+
t("ai_assistant.usage.loadingDetail", "Loading session events...")
|
|
255
|
+
] }),
|
|
256
|
+
sessionDetailQuery.isError && /* @__PURE__ */ jsx("p", { className: "text-status-error-text text-sm", children: t("ai_assistant.usage.errorDetail", "Failed to load session events.") }),
|
|
257
|
+
sessionDetailQuery.isSuccess && /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full text-sm", children: [
|
|
258
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted/40", children: [
|
|
259
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-muted-foreground", children: t("ai_assistant.usage.col.step", "Step") }),
|
|
260
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-muted-foreground", children: t("ai_assistant.usage.col.model", "Model") }),
|
|
261
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium text-muted-foreground", children: t("ai_assistant.usage.col.inputTokens", "Input") }),
|
|
262
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium text-muted-foreground", children: t("ai_assistant.usage.col.outputTokens", "Output") }),
|
|
263
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-muted-foreground", children: t("ai_assistant.usage.col.finishReason", "Finish") })
|
|
264
|
+
] }) }),
|
|
265
|
+
/* @__PURE__ */ jsx("tbody", { children: (sessionDetailQuery.data?.events ?? []).map((event) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-border last:border-b-0", children: [
|
|
266
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 tabular-nums", children: event.stepIndex }),
|
|
267
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 font-mono text-xs", children: event.modelId }),
|
|
268
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: formatNumber(event.inputTokens) }),
|
|
269
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: formatNumber(event.outputTokens) }),
|
|
270
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-muted-foreground text-xs", children: event.finishReason ?? "\u2014" })
|
|
271
|
+
] }, event.id)) })
|
|
272
|
+
] }) })
|
|
273
|
+
] })
|
|
274
|
+
] })
|
|
275
|
+
}
|
|
276
|
+
)
|
|
277
|
+
] });
|
|
278
|
+
}
|
|
279
|
+
export {
|
|
280
|
+
AiUsageStatsPageClient
|
|
281
|
+
};
|
|
282
|
+
//# sourceMappingURL=AiUsageStatsPageClient.js.map
|
package/dist/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/ai_assistant/backend/config/ai-assistant/usage/AiUsageStatsPageClient.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { BarChart2, ChevronRight, Loader2 } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\n\ntype DailyRow = {\n id: string\n tenantId: string\n organizationId: string | null\n day: string\n agentId: string\n modelId: string\n providerId: string\n inputTokens: string\n outputTokens: string\n cachedInputTokens: string\n reasoningTokens: string\n stepCount: string\n turnCount: string\n sessionCount: string\n createdAt: string\n updatedAt: string\n}\n\ntype DailyResponse = {\n rows: DailyRow[]\n total: number\n}\n\ntype SessionSummary = {\n sessionId: string\n agentId: string\n moduleId: string\n userId: string\n startedAt: string\n lastEventAt: string\n stepCount: number\n turnCount: number\n inputTokens: number\n outputTokens: number\n cachedInputTokens: number\n reasoningTokens: number\n}\n\ntype SessionsResponse = {\n sessions: SessionSummary[]\n total: number\n limit: number\n offset: number\n}\n\ntype StepEvent = {\n id: string\n tenantId: string\n organizationId: string | null\n userId: string\n agentId: string\n moduleId: string\n sessionId: string\n turnId: string\n stepIndex: number\n providerId: string\n modelId: string\n inputTokens: number\n outputTokens: number\n cachedInputTokens: number | null\n reasoningTokens: number | null\n finishReason: string | null\n loopAbortReason: string | null\n createdAt: string\n updatedAt: string\n}\n\ntype SessionDetailResponse = {\n events: StepEvent[]\n total: number\n sessionId: string\n}\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\nfunction daysAgoIso(days: number): string {\n const date = new Date()\n date.setDate(date.getDate() - days)\n return date.toISOString().slice(0, 10)\n}\n\nasync function fetchDailyRollup(from: string, to: string): Promise<DailyResponse> {\n const params = new URLSearchParams({ from, to })\n const { result, status } = await apiCallOrThrow<DailyResponse>(\n `/api/ai_assistant/usage/daily?${params}`,\n { method: 'GET', credentials: 'include' },\n { errorMessage: 'Failed to load daily token usage' },\n )\n if (!result) throw new Error(`Failed to load daily usage (${status})`)\n return result\n}\n\nasync function fetchSessions(from: string, to: string, offset: number): Promise<SessionsResponse> {\n const params = new URLSearchParams({ from, to, limit: '50', offset: String(offset) })\n const { result, status } = await apiCallOrThrow<SessionsResponse>(\n `/api/ai_assistant/usage/sessions?${params}`,\n { method: 'GET', credentials: 'include' },\n { errorMessage: 'Failed to load session list' },\n )\n if (!result) throw new Error(`Failed to load sessions (${status})`)\n return result\n}\n\nasync function fetchSessionDetail(sessionId: string): Promise<SessionDetailResponse> {\n const { result, status } = await apiCallOrThrow<SessionDetailResponse>(\n `/api/ai_assistant/usage/sessions/${encodeURIComponent(sessionId)}`,\n { method: 'GET', credentials: 'include' },\n { errorMessage: 'Failed to load session detail' },\n )\n if (!result) throw new Error(`Failed to load session detail (${status})`)\n return result\n}\n\nfunction sumBigintRows(rows: DailyRow[], field: keyof DailyRow): number {\n return rows.reduce((acc, row) => acc + parseInt(String(row[field] ?? '0'), 10), 0)\n}\n\nfunction formatNumber(value: number): string {\n return value.toLocaleString()\n}\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString()\n}\n\nfunction shortId(id: string): string {\n return id.slice(0, 8)\n}\n\nexport function AiUsageStatsPageClient() {\n const t = useT()\n\n const defaultFrom = daysAgoIso(30)\n const defaultTo = todayIso()\n\n const [from, setFrom] = React.useState(defaultFrom)\n const [to, setTo] = React.useState(defaultTo)\n const [appliedFrom, setAppliedFrom] = React.useState(defaultFrom)\n const [appliedTo, setAppliedTo] = React.useState(defaultTo)\n const [sessionsOffset, setSessionsOffset] = React.useState(0)\n const [selectedSessionId, setSelectedSessionId] = React.useState<string | null>(null)\n\n const dailyQuery = useQuery({\n queryKey: ['ai-usage-daily', appliedFrom, appliedTo],\n queryFn: () => fetchDailyRollup(appliedFrom, appliedTo),\n })\n\n const sessionsQuery = useQuery({\n queryKey: ['ai-usage-sessions', appliedFrom, appliedTo, sessionsOffset],\n queryFn: () => fetchSessions(appliedFrom, appliedTo, sessionsOffset),\n })\n\n const sessionDetailQuery = useQuery({\n queryKey: ['ai-usage-session-detail', selectedSessionId],\n queryFn: () => fetchSessionDetail(selectedSessionId!),\n enabled: selectedSessionId !== null,\n })\n\n function applyFilter() {\n setSessionsOffset(0)\n setAppliedFrom(from)\n setAppliedTo(to)\n }\n\n const dailyRows = dailyQuery.data?.rows ?? []\n const totalInputTokens = sumBigintRows(dailyRows, 'inputTokens')\n const totalOutputTokens = sumBigintRows(dailyRows, 'outputTokens')\n const totalSteps = sumBigintRows(dailyRows, 'stepCount')\n const totalSessions = sumBigintRows(dailyRows, 'sessionCount')\n\n return (\n <div className=\"space-y-6 max-w-5xl\">\n <div className=\"flex items-center gap-2\">\n <BarChart2 className=\"text-muted-foreground\" size={20} />\n <h2 className=\"text-lg font-semibold\">\n {t('ai_assistant.usage.title', 'Token Usage Statistics')}\n </h2>\n </div>\n\n {/* Date range filter */}\n <div className=\"flex items-end gap-4 flex-wrap\">\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor=\"usage-from\">\n {t('ai_assistant.usage.from', 'From')}\n </Label>\n <Input\n id=\"usage-from\"\n type=\"date\"\n value={from}\n onChange={(e) => setFrom(e.target.value)}\n className=\"w-40\"\n />\n </div>\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor=\"usage-to\">\n {t('ai_assistant.usage.to', 'To')}\n </Label>\n <Input\n id=\"usage-to\"\n type=\"date\"\n value={to}\n onChange={(e) => setTo(e.target.value)}\n className=\"w-40\"\n />\n </div>\n <Button variant=\"secondary\" onClick={applyFilter}>\n {t('ai_assistant.usage.apply', 'Apply')}\n </Button>\n </div>\n\n {/* Summary tiles */}\n {dailyQuery.isLoading && (\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <Loader2 size={14} className=\"animate-spin\" />\n {t('ai_assistant.usage.loading', 'Loading usage data...')}\n </div>\n )}\n {dailyQuery.isError && (\n <p className=\"text-status-error-text text-sm\">\n {t('ai_assistant.usage.error', 'Failed to load usage data.')}\n </p>\n )}\n {dailyQuery.isSuccess && (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n {[\n { label: t('ai_assistant.usage.inputTokens', 'Input tokens'), value: formatNumber(totalInputTokens) },\n { label: t('ai_assistant.usage.outputTokens', 'Output tokens'), value: formatNumber(totalOutputTokens) },\n { label: t('ai_assistant.usage.steps', 'Steps'), value: formatNumber(totalSteps) },\n { label: t('ai_assistant.usage.sessions', 'Sessions'), value: formatNumber(totalSessions) },\n ].map((tile) => (\n <div key={tile.label} className=\"rounded-lg border border-border p-4 space-y-1\">\n <p className=\"text-muted-foreground text-xs\">{tile.label}</p>\n <p className=\"font-semibold text-xl\">{tile.value}</p>\n </div>\n ))}\n </div>\n )}\n\n {/* Daily breakdown table */}\n {dailyQuery.isSuccess && dailyRows.length > 0 && (\n <div className=\"space-y-2\">\n <h3 className=\"text-sm font-medium text-muted-foreground\">\n {t('ai_assistant.usage.dailyBreakdown', 'Daily breakdown')}\n </h3>\n <div className=\"overflow-x-auto rounded-lg border border-border\">\n <table className=\"min-w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border bg-muted/40\">\n <th className=\"px-3 py-2 text-left font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.day', 'Day')}\n </th>\n <th className=\"px-3 py-2 text-left font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.agent', 'Agent')}\n </th>\n <th className=\"px-3 py-2 text-right font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.inputTokens', 'Input')}\n </th>\n <th className=\"px-3 py-2 text-right font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.outputTokens', 'Output')}\n </th>\n <th className=\"px-3 py-2 text-right font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.sessions', 'Sessions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {dailyRows.map((row) => (\n <tr key={row.id} className=\"border-b border-border last:border-b-0\">\n <td className=\"px-3 py-2 tabular-nums\">{row.day}</td>\n <td className=\"px-3 py-2 font-mono text-xs\">{row.agentId}</td>\n <td className=\"px-3 py-2 text-right tabular-nums\">{formatNumber(parseInt(row.inputTokens, 10))}</td>\n <td className=\"px-3 py-2 text-right tabular-nums\">{formatNumber(parseInt(row.outputTokens, 10))}</td>\n <td className=\"px-3 py-2 text-right tabular-nums\">{row.sessionCount}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n {/* Sessions list */}\n <div className=\"space-y-2\">\n <h3 className=\"text-sm font-medium text-muted-foreground\">\n {t('ai_assistant.usage.sessionsList', 'Sessions')}\n </h3>\n {sessionsQuery.isLoading && (\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <Loader2 size={14} className=\"animate-spin\" />\n {t('ai_assistant.usage.loadingSessions', 'Loading sessions...')}\n </div>\n )}\n {sessionsQuery.isError && (\n <p className=\"text-status-error-text text-sm\">\n {t('ai_assistant.usage.errorSessions', 'Failed to load sessions.')}\n </p>\n )}\n {sessionsQuery.isSuccess && (sessionsQuery.data?.sessions ?? []).length === 0 && (\n <p className=\"text-muted-foreground text-sm\">\n {t('ai_assistant.usage.noSessions', 'No sessions found for the selected period.')}\n </p>\n )}\n {sessionsQuery.isSuccess && (sessionsQuery.data?.sessions ?? []).length > 0 && (\n <>\n <div className=\"overflow-x-auto rounded-lg border border-border\">\n <table className=\"min-w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border bg-muted/40\">\n <th className=\"px-3 py-2 text-left font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.session', 'Session')}\n </th>\n <th className=\"px-3 py-2 text-left font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.agent', 'Agent')}\n </th>\n <th className=\"px-3 py-2 text-left font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.startedAt', 'Started')}\n </th>\n <th className=\"px-3 py-2 text-right font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.inputTokens', 'Input')}\n </th>\n <th className=\"px-3 py-2 text-right font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.outputTokens', 'Output')}\n </th>\n <th className=\"px-3 py-2 text-right font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.steps', 'Steps')}\n </th>\n <th className=\"w-8\" />\n </tr>\n </thead>\n <tbody>\n {(sessionsQuery.data?.sessions ?? []).map((session) => (\n <tr\n key={session.sessionId}\n className=\"border-b border-border last:border-b-0 cursor-pointer hover:bg-muted/30 transition-colors\"\n onClick={() => setSelectedSessionId(session.sessionId)}\n >\n <td className=\"px-3 py-2 font-mono text-xs\">{shortId(session.sessionId)}\u2026</td>\n <td className=\"px-3 py-2 font-mono text-xs\">{session.agentId}</td>\n <td className=\"px-3 py-2 text-muted-foreground text-xs\">{formatDate(session.startedAt)}</td>\n <td className=\"px-3 py-2 text-right tabular-nums\">{formatNumber(session.inputTokens)}</td>\n <td className=\"px-3 py-2 text-right tabular-nums\">{formatNumber(session.outputTokens)}</td>\n <td className=\"px-3 py-2 text-right tabular-nums\">{session.stepCount}</td>\n <td className=\"px-3 py-2 text-muted-foreground\">\n <ChevronRight size={14} />\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={sessionsOffset === 0}\n onClick={() => setSessionsOffset(Math.max(0, sessionsOffset - 50))}\n >\n {t('ai_assistant.usage.prev', 'Previous')}\n </Button>\n <span className=\"text-muted-foreground text-sm\">\n {sessionsOffset + 1}\u2013{sessionsOffset + (sessionsQuery.data?.sessions.length ?? 0)}\n {sessionsQuery.data?.total !== undefined ? ` / ${sessionsQuery.data.total}` : ''}\n </span>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={\n (sessionsQuery.data?.sessions.length ?? 0) < 50 ||\n sessionsOffset + 50 >= (sessionsQuery.data?.total ?? 0)\n }\n onClick={() => setSessionsOffset(sessionsOffset + 50)}\n >\n {t('ai_assistant.usage.next', 'Next')}\n </Button>\n </div>\n </>\n )}\n </div>\n\n {/* Session drill-down dialog */}\n <Dialog\n open={selectedSessionId !== null}\n onOpenChange={(open) => { if (!open) setSelectedSessionId(null) }}\n >\n <DialogContent className=\"max-w-3xl max-h-[80vh] overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>\n {t('ai_assistant.usage.sessionDetail', 'Session detail')}\n {selectedSessionId && (\n <span className=\"ml-2 font-mono text-sm text-muted-foreground\">\n {shortId(selectedSessionId)}\u2026\n </span>\n )}\n </DialogTitle>\n </DialogHeader>\n <div className=\"mt-4\">\n {sessionDetailQuery.isLoading && (\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <Loader2 size={14} className=\"animate-spin\" />\n {t('ai_assistant.usage.loadingDetail', 'Loading session events...')}\n </div>\n )}\n {sessionDetailQuery.isError && (\n <p className=\"text-status-error-text text-sm\">\n {t('ai_assistant.usage.errorDetail', 'Failed to load session events.')}\n </p>\n )}\n {sessionDetailQuery.isSuccess && (\n <div className=\"overflow-x-auto rounded-lg border border-border\">\n <table className=\"min-w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border bg-muted/40\">\n <th className=\"px-3 py-2 text-left font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.step', 'Step')}\n </th>\n <th className=\"px-3 py-2 text-left font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.model', 'Model')}\n </th>\n <th className=\"px-3 py-2 text-right font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.inputTokens', 'Input')}\n </th>\n <th className=\"px-3 py-2 text-right font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.outputTokens', 'Output')}\n </th>\n <th className=\"px-3 py-2 text-left font-medium text-muted-foreground\">\n {t('ai_assistant.usage.col.finishReason', 'Finish')}\n </th>\n </tr>\n </thead>\n <tbody>\n {(sessionDetailQuery.data?.events ?? []).map((event) => (\n <tr key={event.id} className=\"border-b border-border last:border-b-0\">\n <td className=\"px-3 py-2 tabular-nums\">{event.stepIndex}</td>\n <td className=\"px-3 py-2 font-mono text-xs\">{event.modelId}</td>\n <td className=\"px-3 py-2 text-right tabular-nums\">{formatNumber(event.inputTokens)}</td>\n <td className=\"px-3 py-2 text-right tabular-nums\">{formatNumber(event.outputTokens)}</td>\n <td className=\"px-3 py-2 text-muted-foreground text-xs\">{event.finishReason ?? '\u2014'}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n </DialogContent>\n </Dialog>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
3
|
+
import { AiUsageStatsPageClient } from "./AiUsageStatsPageClient.js";
|
|
4
|
+
async function AiUsageStatsPage() {
|
|
5
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(AiUsageStatsPageClient, {}) }) });
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
AiUsageStatsPage as default
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/ai_assistant/backend/config/ai-assistant/usage/page.tsx"],
|
|
4
|
+
"sourcesContent": ["import { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { AiUsageStatsPageClient } from './AiUsageStatsPageClient'\n\nexport default async function AiUsageStatsPage() {\n return (\n <Page>\n <PageBody>\n <AiUsageStatsPageClient />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "AAOQ;AAPR,SAAS,MAAM,gBAAgB;AAC/B,SAAS,8BAA8B;AAEvC,eAAO,mBAA0C;AAC/C,SACE,oBAAC,QACC,8BAAC,YACC,8BAAC,0BAAuB,GAC1B,GACF;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
const usageIcon = React.createElement(
|
|
3
|
+
"svg",
|
|
4
|
+
{ width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" },
|
|
5
|
+
React.createElement("path", { d: "M3 3v18h18" }),
|
|
6
|
+
React.createElement("path", { d: "M7 16l4-4 4 4 4-4" })
|
|
7
|
+
);
|
|
8
|
+
const metadata = {
|
|
9
|
+
requireAuth: true,
|
|
10
|
+
requireFeatures: ["ai_assistant.settings.manage"],
|
|
11
|
+
pageTitle: "AI Usage",
|
|
12
|
+
pageTitleKey: "ai_assistant.usage.navTitle",
|
|
13
|
+
pageGroup: "Module Configs",
|
|
14
|
+
pageGroupKey: "settings.sections.moduleConfigs",
|
|
15
|
+
pageOrder: 431,
|
|
16
|
+
icon: usageIcon,
|
|
17
|
+
pageContext: "settings",
|
|
18
|
+
breadcrumb: [
|
|
19
|
+
{ label: "AI Usage", labelKey: "ai_assistant.usage.navTitle" }
|
|
20
|
+
]
|
|
21
|
+
};
|
|
22
|
+
export {
|
|
23
|
+
metadata
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=page.meta.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../src/modules/ai_assistant/backend/config/ai-assistant/usage/page.meta.ts"],
|
|
4
|
+
"sourcesContent": ["import React from 'react'\n\nconst usageIcon = React.createElement(\n 'svg',\n { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },\n React.createElement('path', { d: 'M3 3v18h18' }),\n React.createElement('path', { d: 'M7 16l4-4 4 4 4-4' }),\n)\n\nexport const metadata = {\n requireAuth: true,\n requireFeatures: ['ai_assistant.settings.manage'],\n pageTitle: 'AI Usage',\n pageTitleKey: 'ai_assistant.usage.navTitle',\n pageGroup: 'Module Configs',\n pageGroupKey: 'settings.sections.moduleConfigs',\n pageOrder: 431,\n icon: usageIcon,\n pageContext: 'settings' as const,\n breadcrumb: [\n { label: 'AI Usage', labelKey: 'ai_assistant.usage.navTitle' },\n ],\n} as const\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,WAAW;AAElB,MAAM,YAAY,MAAM;AAAA,EACtB;AAAA,EACA,EAAE,OAAO,IAAI,QAAQ,IAAI,SAAS,aAAa,MAAM,QAAQ,QAAQ,gBAAgB,aAAa,GAAG,eAAe,SAAS,gBAAgB,QAAQ;AAAA,EACrJ,MAAM,cAAc,QAAQ,EAAE,GAAG,aAAa,CAAC;AAAA,EAC/C,MAAM,cAAc,QAAQ,EAAE,GAAG,oBAAoB,CAAC;AACxD;AAEO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,8BAA8B;AAAA,EAChD,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,MAAM;AAAA,EACN,aAAa;AAAA,EACb,YAAY;AAAA,IACV,EAAE,OAAO,YAAY,UAAU,8BAA8B;AAAA,EAC/D;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -269,6 +269,17 @@ const testTools = {
|
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
};
|
|
272
|
+
const runTokenUsagePrune = {
|
|
273
|
+
command: "run-token-usage-prune",
|
|
274
|
+
async run() {
|
|
275
|
+
await ensureBootstrap();
|
|
276
|
+
const container = await createRequestContainer();
|
|
277
|
+
const { runTokenUsagePrune: runPrune } = await import("./workers/ai-token-usage-prune.js");
|
|
278
|
+
const em = container.resolve("em");
|
|
279
|
+
const summary = await runPrune({ em });
|
|
280
|
+
console.log("[ai-token-usage-prune] Prune complete:", summary);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
272
283
|
var cli_default = [
|
|
273
284
|
mcpServe,
|
|
274
285
|
mcpServeHttp,
|
|
@@ -276,6 +287,7 @@ var cli_default = [
|
|
|
276
287
|
listTools,
|
|
277
288
|
entityGraph,
|
|
278
289
|
runPendingActionCleanup,
|
|
290
|
+
runTokenUsagePrune,
|
|
279
291
|
testTools
|
|
280
292
|
];
|
|
281
293
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/ai_assistant/cli.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\n/**\n * Ensure app bootstrap is called before creating DI container.\n * Uses the shared generated-bootstrap loader so the command works both from\n * the monorepo app and from standalone apps installed through npm packages.\n */\nasync function ensureBootstrap(): Promise<void> {\n // First check if DI is already available\n try {\n const { getDiRegistrars } = await import('@open-mercato/shared/lib/di/container')\n getDiRegistrars()\n return // DI already available\n } catch {\n // DI not available, need to bootstrap\n }\n\n try {\n const { bootstrapFromAppRoot } = await import('@open-mercato/shared/lib/bootstrap/dynamicLoader')\n await bootstrapFromAppRoot()\n } catch (error) {\n console.error('[MCP] Bootstrap failed:', error instanceof Error ? error.message : error)\n // Continue - some contexts may not have bootstrap available\n }\n}\n\nfunction parseArgs(rest: string[]): Record<string, string | boolean> {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i]\n if (!arg?.startsWith('--')) continue\n\n const [key, value] = arg.replace(/^--/, '').split('=')\n if (value !== undefined) {\n args[key] = value\n } else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) {\n args[key] = rest[i + 1]!\n i++\n } else {\n args[key] = true\n }\n }\n return args\n}\n\nconst mcpServe: ModuleCli = {\n command: 'mcp:serve',\n async run(rest) {\n const args = parseArgs(rest)\n const apiKey = String(args['api-key'] ?? args.apiKey ?? '') || null\n const tenantId = String(args.tenant ?? args.tenantId ?? '') || null\n const organizationId = String(args.org ?? args.organizationId ?? '') || null\n const userId = String(args.user ?? args.userId ?? '') || null\n const debug = args.debug === true || args.debug === 'true'\n\n // Either API key or tenant is required\n if (!apiKey && !tenantId) {\n console.error('Usage: mercato ai_assistant mcp:serve [options]')\n console.error('')\n console.error('Authentication (choose one):')\n console.error(' --api-key <secret> API key secret for authentication (recommended)')\n console.error(' --tenant <id> Tenant ID (for manual context)')\n console.error('')\n console.error('Options (with --tenant):')\n console.error(' --org <id> Organization ID (optional)')\n console.error(' --user <id> User ID for ACL (optional, uses superadmin if not set)')\n console.error('')\n console.error('Common options:')\n console.error(' --debug Enable debug logging')\n console.error('')\n console.error('Examples:')\n console.error(' mercato ai_assistant mcp:serve --api-key omk_xxxx.yyyy...')\n console.error(' mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000')\n return\n }\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpServer } = await import('./lib/mcp-server')\n\n if (apiKey) {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n apiKeySecret: apiKey,\n })\n } else {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n context: {\n tenantId,\n organizationId,\n userId,\n },\n })\n }\n },\n}\n\nconst MCP_DEFAULT_PORT = 3001\n\nconst mcpServeHttp: ModuleCli = {\n command: 'mcp:serve-http',\n async run(rest) {\n const args = parseArgs(rest)\n const portArg = parseInt(String(args.port ?? ''), 10)\n const port = !portArg || isNaN(portArg) ? MCP_DEFAULT_PORT : portArg\n const debug = args.debug === true || args.debug === 'true'\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpHttpServer } = await import('./lib/http-server')\n\n await runMcpHttpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n port,\n })\n },\n}\n\nconst mcpDev: ModuleCli = {\n command: 'mcp:dev',\n async run() {\n await ensureBootstrap()\n const { runMcpDevServer } = await import('./lib/mcp-dev-server')\n await runMcpDevServer()\n },\n}\n\nconst listTools: ModuleCli = {\n command: 'mcp:list-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const verbose = args.verbose === true || args.verbose === 'true'\n\n // Ensure bootstrap runs so modules are registered for API discovery\n await ensureBootstrap()\n\n const { loadAllModuleTools } = await import('./lib/tool-loader')\n await loadAllModuleTools()\n\n const { getToolRegistry } = await import('./lib/tool-registry')\n const registry = getToolRegistry()\n const toolNames = registry.listToolNames()\n\n if (toolNames.length === 0) {\n console.log('\\nNo MCP tools registered.')\n console.log('Tools can be registered by modules using registerMcpTool().\\n')\n return\n }\n\n console.log(`\\nRegistered MCP Tools (${toolNames.length}):\\n`)\n\n // Group tools by module\n const byModule = new Map<string, string[]>()\n for (const name of toolNames) {\n const [module] = name.split('.')\n const list = byModule.get(module) ?? []\n list.push(name)\n byModule.set(module, list)\n }\n\n // Sort modules alphabetically\n const sortedModules = Array.from(byModule.keys()).sort()\n\n for (const module of sortedModules) {\n const tools = byModule.get(module)!\n console.log(`${module} (${tools.length} tools):`)\n\n for (const name of tools.sort()) {\n const tool = registry.getTool(name)\n if (!tool) continue\n\n if (verbose) {\n console.log(` ${name}`)\n console.log(` ${tool.description}`)\n if (tool.requiredFeatures?.length) {\n console.log(` Requires: ${tool.requiredFeatures.join(', ')}`)\n }\n } else {\n console.log(` - ${name}`)\n }\n }\n console.log('')\n }\n },\n}\n\nconst entityGraph: ModuleCli = {\n command: 'entity-graph',\n async run(rest) {\n const args = parseArgs(rest)\n const format = String(args.format ?? 'triples') as 'json' | 'triples'\n const entity = args.entity ? String(args.entity) : undefined\n const module = args.module ? String(args.module) : undefined\n\n await ensureBootstrap()\n\n const { getOrm } = await import('@open-mercato/shared/lib/db/mikro')\n const { extractEntityGraph, formatGraphAsTriples, filterGraphByEntity, filterGraphByModule } = await import(\n './lib/entity-graph'\n )\n\n console.log('[Entity Graph] Extracting from MikroORM metadata...')\n\n const orm = await getOrm()\n const graph = await extractEntityGraph(orm)\n\n // Apply filters\n let edges = graph.edges\n\n if (entity) {\n edges = filterGraphByEntity(graph, entity)\n console.log(`[Entity Graph] Filtered by entity: ${entity}`)\n }\n\n if (module) {\n const filteredGraph = { ...graph, edges }\n edges = filterGraphByModule(filteredGraph, module)\n console.log(`[Entity Graph] Filtered by module: ${module}`)\n }\n\n const filteredGraph = { ...graph, edges }\n\n if (format === 'json') {\n console.log(JSON.stringify(filteredGraph, null, 2))\n } else {\n const triples = formatGraphAsTriples(filteredGraph)\n console.log('')\n for (const triple of triples) {\n console.log(triple)\n }\n }\n\n console.log(`\\n[Entity Graph] ${graph.nodes.length} entities, ${edges.length} relationships`)\n },\n}\n\nconst runPendingActionCleanup: ModuleCli = {\n command: 'run-pending-action-cleanup',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runPendingActionCleanup: runCleanup } = await import(\n './workers/ai-pending-action-cleanup'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runCleanup({ em })\n\n console.log('[ai-pending-action-cleanup] Sweep complete:', summary)\n },\n}\n\nconst testTools: ModuleCli = {\n command: 'test-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const json = args.json === true || args.json === 'true'\n const moduleFilter =\n typeof args.module === 'string' && args.module.length > 0 ? args.module : null\n const includeMutations = args['no-mutations'] !== true && args['no-mutations'] !== 'true'\n const tenantId =\n typeof args.tenant === 'string' && args.tenant.length > 0 ? args.tenant : null\n const organizationId =\n typeof args.org === 'string' && args.org.length > 0 ? args.org : null\n\n await ensureBootstrap()\n const { runToolTests } = await import('./lib/tool-test-runner')\n const report = await runToolTests({\n tenantId,\n organizationId,\n moduleFilter,\n includeMutations,\n })\n\n if (json) {\n // Wrap in markers so a Playwright spec (or any caller) can extract the\n // JSON payload without being thrown off by bootstrap log lines emitted\n // to stdout by other modules during DI container creation.\n console.log('---TOOL_TEST_REPORT_BEGIN---')\n console.log(JSON.stringify(report))\n console.log('---TOOL_TEST_REPORT_END---')\n } else {\n console.log('')\n console.log(\n `AI tool test report \u2014 tenant=${report.tenantId ?? '<none>'} org=${\n report.organizationId ?? '<none>'\n }`,\n )\n console.log(\n `total=${report.total} pass=${report.passed} fail=${report.failed} skip=${report.skipped}`,\n )\n const byModule = new Map<string, typeof report.records>()\n for (const record of report.records) {\n const list = byModule.get(record.module) ?? []\n list.push(record)\n byModule.set(record.module, list)\n }\n const sortedModules = Array.from(byModule.keys()).sort()\n for (const moduleId of sortedModules) {\n const list = byModule.get(moduleId)!\n console.log('')\n console.log(`${moduleId} (${list.length}):`)\n for (const record of list) {\n const marker =\n record.status === 'pass' ? '\u2713' : record.status === 'fail' ? '\u2717' : '\u00B7'\n const reason = record.reason ? ` \u2014 ${record.reason}` : ''\n const mutation = record.isMutation ? ' [mutation]' : ''\n console.log(\n ` ${marker} ${record.tool}${mutation} (${record.durationMs}ms)${reason}`,\n )\n }\n }\n console.log('')\n }\n\n if (report.failed > 0) {\n process.exitCode = 1\n }\n },\n}\n\nexport default [\n mcpServe,\n mcpServeHttp,\n mcpDev,\n listTools,\n entityGraph,\n runPendingActionCleanup,\n testTools,\n]\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\n/**\n * Ensure app bootstrap is called before creating DI container.\n * Uses the shared generated-bootstrap loader so the command works both from\n * the monorepo app and from standalone apps installed through npm packages.\n */\nasync function ensureBootstrap(): Promise<void> {\n // First check if DI is already available\n try {\n const { getDiRegistrars } = await import('@open-mercato/shared/lib/di/container')\n getDiRegistrars()\n return // DI already available\n } catch {\n // DI not available, need to bootstrap\n }\n\n try {\n const { bootstrapFromAppRoot } = await import('@open-mercato/shared/lib/bootstrap/dynamicLoader')\n await bootstrapFromAppRoot()\n } catch (error) {\n console.error('[MCP] Bootstrap failed:', error instanceof Error ? error.message : error)\n // Continue - some contexts may not have bootstrap available\n }\n}\n\nfunction parseArgs(rest: string[]): Record<string, string | boolean> {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i]\n if (!arg?.startsWith('--')) continue\n\n const [key, value] = arg.replace(/^--/, '').split('=')\n if (value !== undefined) {\n args[key] = value\n } else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) {\n args[key] = rest[i + 1]!\n i++\n } else {\n args[key] = true\n }\n }\n return args\n}\n\nconst mcpServe: ModuleCli = {\n command: 'mcp:serve',\n async run(rest) {\n const args = parseArgs(rest)\n const apiKey = String(args['api-key'] ?? args.apiKey ?? '') || null\n const tenantId = String(args.tenant ?? args.tenantId ?? '') || null\n const organizationId = String(args.org ?? args.organizationId ?? '') || null\n const userId = String(args.user ?? args.userId ?? '') || null\n const debug = args.debug === true || args.debug === 'true'\n\n // Either API key or tenant is required\n if (!apiKey && !tenantId) {\n console.error('Usage: mercato ai_assistant mcp:serve [options]')\n console.error('')\n console.error('Authentication (choose one):')\n console.error(' --api-key <secret> API key secret for authentication (recommended)')\n console.error(' --tenant <id> Tenant ID (for manual context)')\n console.error('')\n console.error('Options (with --tenant):')\n console.error(' --org <id> Organization ID (optional)')\n console.error(' --user <id> User ID for ACL (optional, uses superadmin if not set)')\n console.error('')\n console.error('Common options:')\n console.error(' --debug Enable debug logging')\n console.error('')\n console.error('Examples:')\n console.error(' mercato ai_assistant mcp:serve --api-key omk_xxxx.yyyy...')\n console.error(' mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000')\n return\n }\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpServer } = await import('./lib/mcp-server')\n\n if (apiKey) {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n apiKeySecret: apiKey,\n })\n } else {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n context: {\n tenantId,\n organizationId,\n userId,\n },\n })\n }\n },\n}\n\nconst MCP_DEFAULT_PORT = 3001\n\nconst mcpServeHttp: ModuleCli = {\n command: 'mcp:serve-http',\n async run(rest) {\n const args = parseArgs(rest)\n const portArg = parseInt(String(args.port ?? ''), 10)\n const port = !portArg || isNaN(portArg) ? MCP_DEFAULT_PORT : portArg\n const debug = args.debug === true || args.debug === 'true'\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpHttpServer } = await import('./lib/http-server')\n\n await runMcpHttpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n port,\n })\n },\n}\n\nconst mcpDev: ModuleCli = {\n command: 'mcp:dev',\n async run() {\n await ensureBootstrap()\n const { runMcpDevServer } = await import('./lib/mcp-dev-server')\n await runMcpDevServer()\n },\n}\n\nconst listTools: ModuleCli = {\n command: 'mcp:list-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const verbose = args.verbose === true || args.verbose === 'true'\n\n // Ensure bootstrap runs so modules are registered for API discovery\n await ensureBootstrap()\n\n const { loadAllModuleTools } = await import('./lib/tool-loader')\n await loadAllModuleTools()\n\n const { getToolRegistry } = await import('./lib/tool-registry')\n const registry = getToolRegistry()\n const toolNames = registry.listToolNames()\n\n if (toolNames.length === 0) {\n console.log('\\nNo MCP tools registered.')\n console.log('Tools can be registered by modules using registerMcpTool().\\n')\n return\n }\n\n console.log(`\\nRegistered MCP Tools (${toolNames.length}):\\n`)\n\n // Group tools by module\n const byModule = new Map<string, string[]>()\n for (const name of toolNames) {\n const [module] = name.split('.')\n const list = byModule.get(module) ?? []\n list.push(name)\n byModule.set(module, list)\n }\n\n // Sort modules alphabetically\n const sortedModules = Array.from(byModule.keys()).sort()\n\n for (const module of sortedModules) {\n const tools = byModule.get(module)!\n console.log(`${module} (${tools.length} tools):`)\n\n for (const name of tools.sort()) {\n const tool = registry.getTool(name)\n if (!tool) continue\n\n if (verbose) {\n console.log(` ${name}`)\n console.log(` ${tool.description}`)\n if (tool.requiredFeatures?.length) {\n console.log(` Requires: ${tool.requiredFeatures.join(', ')}`)\n }\n } else {\n console.log(` - ${name}`)\n }\n }\n console.log('')\n }\n },\n}\n\nconst entityGraph: ModuleCli = {\n command: 'entity-graph',\n async run(rest) {\n const args = parseArgs(rest)\n const format = String(args.format ?? 'triples') as 'json' | 'triples'\n const entity = args.entity ? String(args.entity) : undefined\n const module = args.module ? String(args.module) : undefined\n\n await ensureBootstrap()\n\n const { getOrm } = await import('@open-mercato/shared/lib/db/mikro')\n const { extractEntityGraph, formatGraphAsTriples, filterGraphByEntity, filterGraphByModule } = await import(\n './lib/entity-graph'\n )\n\n console.log('[Entity Graph] Extracting from MikroORM metadata...')\n\n const orm = await getOrm()\n const graph = await extractEntityGraph(orm)\n\n // Apply filters\n let edges = graph.edges\n\n if (entity) {\n edges = filterGraphByEntity(graph, entity)\n console.log(`[Entity Graph] Filtered by entity: ${entity}`)\n }\n\n if (module) {\n const filteredGraph = { ...graph, edges }\n edges = filterGraphByModule(filteredGraph, module)\n console.log(`[Entity Graph] Filtered by module: ${module}`)\n }\n\n const filteredGraph = { ...graph, edges }\n\n if (format === 'json') {\n console.log(JSON.stringify(filteredGraph, null, 2))\n } else {\n const triples = formatGraphAsTriples(filteredGraph)\n console.log('')\n for (const triple of triples) {\n console.log(triple)\n }\n }\n\n console.log(`\\n[Entity Graph] ${graph.nodes.length} entities, ${edges.length} relationships`)\n },\n}\n\nconst runPendingActionCleanup: ModuleCli = {\n command: 'run-pending-action-cleanup',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runPendingActionCleanup: runCleanup } = await import(\n './workers/ai-pending-action-cleanup'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runCleanup({ em })\n\n console.log('[ai-pending-action-cleanup] Sweep complete:', summary)\n },\n}\n\nconst testTools: ModuleCli = {\n command: 'test-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const json = args.json === true || args.json === 'true'\n const moduleFilter =\n typeof args.module === 'string' && args.module.length > 0 ? args.module : null\n const includeMutations = args['no-mutations'] !== true && args['no-mutations'] !== 'true'\n const tenantId =\n typeof args.tenant === 'string' && args.tenant.length > 0 ? args.tenant : null\n const organizationId =\n typeof args.org === 'string' && args.org.length > 0 ? args.org : null\n\n await ensureBootstrap()\n const { runToolTests } = await import('./lib/tool-test-runner')\n const report = await runToolTests({\n tenantId,\n organizationId,\n moduleFilter,\n includeMutations,\n })\n\n if (json) {\n // Wrap in markers so a Playwright spec (or any caller) can extract the\n // JSON payload without being thrown off by bootstrap log lines emitted\n // to stdout by other modules during DI container creation.\n console.log('---TOOL_TEST_REPORT_BEGIN---')\n console.log(JSON.stringify(report))\n console.log('---TOOL_TEST_REPORT_END---')\n } else {\n console.log('')\n console.log(\n `AI tool test report \u2014 tenant=${report.tenantId ?? '<none>'} org=${\n report.organizationId ?? '<none>'\n }`,\n )\n console.log(\n `total=${report.total} pass=${report.passed} fail=${report.failed} skip=${report.skipped}`,\n )\n const byModule = new Map<string, typeof report.records>()\n for (const record of report.records) {\n const list = byModule.get(record.module) ?? []\n list.push(record)\n byModule.set(record.module, list)\n }\n const sortedModules = Array.from(byModule.keys()).sort()\n for (const moduleId of sortedModules) {\n const list = byModule.get(moduleId)!\n console.log('')\n console.log(`${moduleId} (${list.length}):`)\n for (const record of list) {\n const marker =\n record.status === 'pass' ? '\u2713' : record.status === 'fail' ? '\u2717' : '\u00B7'\n const reason = record.reason ? ` \u2014 ${record.reason}` : ''\n const mutation = record.isMutation ? ' [mutation]' : ''\n console.log(\n ` ${marker} ${record.tool}${mutation} (${record.durationMs}ms)${reason}`,\n )\n }\n }\n console.log('')\n }\n\n if (report.failed > 0) {\n process.exitCode = 1\n }\n },\n}\n\nconst runTokenUsagePrune: ModuleCli = {\n command: 'run-token-usage-prune',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runTokenUsagePrune: runPrune } = await import(\n './workers/ai-token-usage-prune'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runPrune({ em })\n\n console.log('[ai-token-usage-prune] Prune complete:', summary)\n },\n}\n\nexport default [\n mcpServe,\n mcpServeHttp,\n mcpDev,\n listTools,\n entityGraph,\n runPendingActionCleanup,\n runTokenUsagePrune,\n testTools,\n]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["filteredGraph"]
|
|
7
7
|
}
|