@flame0510/project-aether 1.1.15 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +2 -1
  2. package/app/agents/ModelSection.tsx +313 -0
  3. package/app/agents/PageClient.tsx +83 -4
  4. package/app/agents/create/page.tsx +8 -21
  5. package/app/api/agents/[id]/model/route.ts +113 -0
  6. package/app/api/agents/[id]/recreate/route.ts +10 -34
  7. package/app/api/agents/[id]/route.ts +10 -29
  8. package/app/api/agents/create/route.ts +59 -57
  9. package/app/api/agents/models-summary/route.ts +163 -0
  10. package/app/api/assistant/route.ts +36 -15
  11. package/app/api/credentials/[id]/sync/route.ts +3 -3
  12. package/app/api/credentials/detect/route.ts +126 -176
  13. package/app/api/credentials/route.ts +3 -0
  14. package/app/api/gateway/agent/route.ts +23 -6
  15. package/app/api/gateway/provider/keys.ts +13 -1
  16. package/app/api/gateway/provider/route.ts +43 -12
  17. package/app/api/gateway/sync.ts +248 -72
  18. package/app/api/models/route.ts +28 -34
  19. package/app/api/provider/auth.ts +65 -0
  20. package/app/api/provider/upstream.ts +9 -2
  21. package/app/api/provider/v1/chat/completions/route.ts +22 -16
  22. package/app/api/provider/v1/models/route.ts +26 -133
  23. package/app/components/PulseChat.tsx +25 -39
  24. package/app/components/Skeleton.tsx +132 -0
  25. package/app/components/ui/RemoveButton.tsx +46 -0
  26. package/app/components/ui/Select.tsx +3 -2
  27. package/app/components/ui/index.ts +1 -0
  28. package/app/credentials/PageClient.tsx +461 -140
  29. package/app/credentials/loading.tsx +19 -5
  30. package/app/gateway/PageClient.tsx +257 -673
  31. package/app/globals.css +8 -0
  32. package/app/lib/models-context.tsx +43 -7
  33. package/app/wizard/useWizard.ts +6 -1
  34. package/bin/rev4a.js +73 -9
  35. package/docs/ARCHITECTURE.md +92 -33
  36. package/docs/FRONTEND-ARCHITECTURE.md +36 -6
  37. package/docs/REV4A.md +62 -30
  38. package/docs/dev/API-REFERENCE.md +490 -227
  39. package/docs/dev/DATABASE.md +8 -3
  40. package/docs/dev/GATEWAY.md +236 -92
  41. package/docs/dev/PROVIDERS.md +44 -44
  42. package/docs/rag/DATA-FRESHNESS.md +57 -28
  43. package/docs/rag/GLOSSARY.md +20 -18
  44. package/docs/rag/REV4A-OVERVIEW.md +28 -32
  45. package/docs/rag/WHAT-I-CAN-ANSWER.md +10 -12
  46. package/instrumentation.ts +9 -1
  47. package/lib/agent-readiness.ts +110 -0
  48. package/lib/channelManager.ts +64 -22
  49. package/lib/container-file.ts +27 -0
  50. package/lib/credentials/delivery.ts +212 -119
  51. package/lib/credentials/detect.ts +229 -97
  52. package/lib/credentials/providers.ts +38 -7
  53. package/lib/credentials/vault.ts +78 -13
  54. package/lib/docker-exec.ts +50 -14
  55. package/lib/model-catalogue.ts +140 -27
  56. package/lib/rev4a-paths.ts +0 -21
  57. package/model-pricing.json +118 -110
  58. package/models.config.json +27 -12
  59. package/package.json +1 -1
  60. package/app/api/gateway/route.ts +0 -191
@@ -1,191 +0,0 @@
1
- import { type NextRequest } from "next/server";
2
- /**
3
- * Gateway Status API
4
- *
5
- * Reports the per-agent model settings the Gateway page needs to render its
6
- * agent cards and pre-fill the "edit default model" form. Only containers with
7
- * an AGENT_ID label are considered — no hardcoded container names.
8
- *
9
- * GET /api/gateway
10
- *
11
- * This used to shell out to the OpenClaw CLI inside every container, twice:
12
- * `openclaw models status --json` (~5.7s) and `openclaw models list --json`
13
- * (~4.4s), serially, with execSync — so ~10s per agent with the event loop
14
- * frozen throughout, and roughly 70s on a seven-agent host.
15
- *
16
- * Both were avoidable. `models list` produced a model catalogue and alias map
17
- * that no line of the UI ever read: the page takes its model list from
18
- * /api/gateway/provider, which reads local files. And `models status` returned,
19
- * for the two fields actually used, exactly what sits in the agent's own
20
- * openclaw.json — verified field by field against a live agent. So the CLI was
21
- * being booted for five seconds to read a file that `cat` returns in 260ms.
22
- *
23
- * The response shape is unchanged so the page keeps working untouched; the
24
- * fields that came only from the discarded CLI output are now reported empty
25
- * rather than computed at great cost and thrown away.
26
- */
27
- import { NextResponse } from 'next/server';
28
- import { execFile } from 'child_process';
29
- import { promisify } from 'util';
30
- import { requireAuthJWT } from '@/lib/rev4a-auth';
31
- import { dockerExecNoFail, mapWithConcurrency } from '@/lib/docker-exec';
32
-
33
- const execFileAsync = promisify(execFile);
34
-
35
- export const dynamic = 'force-dynamic';
36
-
37
- /** Agents queried at once. Each is a `docker exec … cat`, cheap but still a process. */
38
- const AGENT_QUERY_CONCURRENCY = 8;
39
-
40
- const AGENT_CONFIG_PATH = '/root/.openclaw/openclaw.json';
41
-
42
- interface GatewayProvider {
43
- provider: string;
44
- kind: string;
45
- detail: string;
46
- profiles: number;
47
- labels: string[];
48
- }
49
-
50
- interface GatewayAgentStatus {
51
- agentId: string;
52
- containerName: string;
53
- state: string;
54
- defaultModel: string;
55
- fallbacks: string[];
56
- allowed: string[];
57
- aliases: Record<string, string>;
58
- providers: GatewayProvider[];
59
- error?: string;
60
- }
61
-
62
- interface AgentRef {
63
- agentId: string;
64
- containerName: string;
65
- state: string;
66
- }
67
-
68
- /**
69
- * Containers carrying an AGENT_ID label, with the label read straight out of
70
- * `docker ps` rather than a follow-up `docker inspect` per container.
71
- */
72
- async function getAgents(): Promise<AgentRef[]> {
73
- try {
74
- const { stdout } = await execFileAsync('docker', [
75
- 'ps', '--filter', 'label=AGENT_ID',
76
- '--format', '{{.Label "AGENT_ID"}}\t{{.Names}}\t{{.State}}',
77
- ], { encoding: 'utf-8', timeout: 5000 });
78
-
79
- return stdout.trim().split('\n')
80
- .map((line) => {
81
- const [agentId, containerName, state] = line.split('\t');
82
- if (!containerName?.trim()) return null;
83
- return {
84
- agentId: agentId?.trim() || containerName.trim(),
85
- containerName: containerName.trim(),
86
- state: state?.trim() || 'unknown',
87
- };
88
- })
89
- .filter(Boolean) as AgentRef[];
90
- } catch {
91
- return [];
92
- }
93
- }
94
-
95
- /**
96
- * Read an agent's model settings out of its openclaw.json.
97
- *
98
- * `agents.defaults.model` is the same value `openclaw models status` reports as
99
- * `defaultModel`/`fallbacks`. The CLI additionally resolves an `imageModel`
100
- * default when the config sets none, but the base image defines no such default
101
- * (verified: `imageModel: null`), and agent creation always writes an explicit
102
- * model — so there is no case where the file and the CLI would disagree.
103
- */
104
- async function readAgentStatus(agent: AgentRef): Promise<GatewayAgentStatus> {
105
- const raw = await dockerExecNoFail(
106
- agent.containerName,
107
- ['cat', AGENT_CONFIG_PATH],
108
- { timeoutMs: 8000 },
109
- );
110
-
111
- const base: GatewayAgentStatus = {
112
- agentId: agent.agentId,
113
- containerName: agent.containerName,
114
- state: agent.state,
115
- defaultModel: '',
116
- fallbacks: [],
117
- allowed: [],
118
- aliases: {},
119
- providers: [],
120
- };
121
-
122
- if (!raw) return { ...base, error: 'config unreadable' };
123
-
124
- try {
125
- const config = JSON.parse(raw) as Record<string, any>;
126
- const model = config?.agents?.defaults?.model ?? {};
127
- return {
128
- ...base,
129
- defaultModel: typeof model.primary === 'string' ? model.primary : '',
130
- fallbacks: Array.isArray(model.fallbacks) ? model.fallbacks.map(String) : [],
131
- };
132
- } catch {
133
- return { ...base, error: 'config unparseable' };
134
- }
135
- }
136
-
137
- export async function GET(request: NextRequest): Promise<NextResponse> {
138
- const denied = await requireAuthJWT(request);
139
- if (denied) return denied;
140
- try {
141
- const timestamp = Date.now();
142
-
143
- const refs = await getAgents();
144
- const agents = await mapWithConcurrency(refs, AGENT_QUERY_CONCURRENCY, readAgentStatus);
145
-
146
- // First agent is the "primary" for core model settings
147
- const primaryAgent = agents[0] ?? null;
148
-
149
- return NextResponse.json({
150
- timestamp,
151
- gateway: 'Rev4a Model Gateway',
152
- status: 'online',
153
- // The model catalogue lives in /api/gateway/provider, which reads the
154
- // bundled config plus user overrides. Recomputing it per agent here cost
155
- // 4.4s each and was never read.
156
- models: {
157
- total: 0,
158
- available: 0,
159
- byProvider: [],
160
- list: [],
161
- },
162
- agents: {
163
- total: agents.length,
164
- list: agents.map((a) => ({
165
- agentId: a.agentId,
166
- containerName: a.containerName,
167
- state: a.state,
168
- defaultModel: a.defaultModel,
169
- fallbacks: a.fallbacks,
170
- aliases: a.aliases,
171
- providers: a.providers,
172
- })),
173
- },
174
- aliases: {},
175
- coreModel: primaryAgent
176
- ? {
177
- defaultModel: primaryAgent.defaultModel,
178
- fallbacks: primaryAgent.fallbacks,
179
- }
180
- : null,
181
- // Provider credential state comes from /api/gateway/provider (`configured`
182
- // per provider), which reads provider-keys.json directly.
183
- apiKeys: {
184
- configured: [],
185
- all: [],
186
- },
187
- });
188
- } catch (e: unknown) {
189
- return NextResponse.json({ error: (e as Error).message }, { status: 500 });
190
- }
191
- }