anentrypoint-design 0.0.193 → 0.0.194

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anentrypoint-design",
3
- "version": "0.0.193",
3
+ "version": "0.0.194",
4
4
  "description": "247420 design system SDK — webjsx + modified ripple-ui, single-file ESM bundle for reproducible use of the AnEntrypoint design.",
5
5
  "type": "module",
6
6
  "main": "./dist/247420.js",
@@ -46,7 +46,27 @@ export function makeChatPage(ctx) {
46
46
  return async function chat(h0) {
47
47
  const root = ctx.root;
48
48
  const skills = [...h0.pi.skills.values()];
49
- const providers = await fetch('/api/providers').then(r => r.json()).catch(() => []);
49
+ let providers = await fetch('/api/providers').then(r => r.json()).catch(() => []);
50
+ if (!Array.isArray(providers)) providers = [];
51
+ // Static deploy (no freddie-server, so /api/providers 404s): probe the
52
+ // acptoapi gateway directly and, when it answers, surface it as a real
53
+ // configured provider with its model list. Without this the dashboard
54
+ // tells the user to "run a gateway" even though one is live and chat works.
55
+ if (!providers.some(p => p.configured)) {
56
+ try {
57
+ const cfg = (window.__debug?.instances?.i1?.host?.fs?.readJson?.('/etc/freddie/freddie.json', null)) || {};
58
+ const baseUrl = (cfg?.providers?.openai?.baseUrl || 'http://localhost:4800').replace(/\/+$/, '');
59
+ const ac = new AbortController();
60
+ const t = setTimeout(() => ac.abort(), 4000);
61
+ const r = await fetch(baseUrl + '/v1/models', { signal: ac.signal }).catch(() => null);
62
+ clearTimeout(t);
63
+ if (r && r.ok) {
64
+ const j = await r.json().catch(() => null);
65
+ const models = Array.isArray(j?.data) ? j.data.map(m => m.id) : [];
66
+ providers = [{ id: 'acptoapi', name: 'acptoapi gateway (' + baseUrl + ')', configured: true, models: ['auto', ...models] }, ...providers];
67
+ }
68
+ } catch {}
69
+ }
50
70
  const configuredProviders = providers.filter(p => p.configured);
51
71
 
52
72
  const chatState = window.__fd_chatState = window.__fd_chatState || {
@@ -81,15 +101,98 @@ export function makeChatPage(ctx) {
81
101
  if (!trimmed) return;
82
102
  chatState.messages.push({ role: 'user', content: trimmed });
83
103
  chatState.busy = true;
104
+ chatState.progress = 'agent thinking…';
84
105
  chatState.abort = new AbortController();
85
106
  saveRecentPath(chatState.cwd);
86
107
  syncMessages();
87
108
  renderPage();
88
109
  try {
89
110
  const body = { prompt: trimmed, cwd: chatState.cwd || undefined, skill: chatState.skill || undefined, provider: chatState.provider || undefined, model: chatState.model || undefined, sessionId: chatState.sessionId || undefined };
90
- const resp = await fetch('/api/chat', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body), signal: chatState.abort.signal });
91
- const text = await resp.text();
92
- const events = parseSseEvents(text);
111
+ let resp;
112
+ try {
113
+ resp = await fetch('/api/chat', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body), signal: chatState.abort.signal });
114
+ } catch (fetchErr) {
115
+ resp = null;
116
+ }
117
+ let text = '';
118
+ let events = [];
119
+ if (resp && resp.ok) {
120
+ text = await resp.text();
121
+ events = parseSseEvents(text);
122
+ } else if (typeof window !== 'undefined' && typeof window.__thebirdRunAgent === 'function') {
123
+ // Static deploy with an in-page agent runtime (e.g. thebird): drive
124
+ // the REAL multi-step agent loop (host tools + acptoapi gateway +
125
+ // tool execution) instead of a single bare completion, so the model
126
+ // emits tool_calls, the loop executes them, feeds results back, and
127
+ // iterates. We synthesize the same {event:'message'} stream the
128
+ // server path produces so the render loop below is unchanged. The
129
+ // window global is the opt-in: hosts without it keep single-shot.
130
+ try {
131
+ let stepN = 0;
132
+ const onUpdate = (snap) => {
133
+ try {
134
+ const msgs = (snap && snap.context && snap.context.messages) || [];
135
+ const toolMsgs = msgs.filter(m => m.role === 'tool');
136
+ const lastAssist = [...msgs].reverse().find(m => m.role === 'assistant' && Array.isArray(m.tool_calls) && m.tool_calls.length);
137
+ const running = lastAssist && lastAssist.tool_calls[0] && (lastAssist.tool_calls[0].function?.name || lastAssist.tool_calls[0].name);
138
+ stepN = toolMsgs.length;
139
+ chatState.progress = running
140
+ ? ('agent: ' + running + ' (step ' + (stepN + 1) + ')…')
141
+ : ('agent thinking' + (stepN ? ' (step ' + stepN + ')' : '') + '…');
142
+ renderPage();
143
+ } catch {}
144
+ };
145
+ const out = await window.__thebirdRunAgent({ prompt: trimmed, onUpdate });
146
+ const turnMsgs = (out && Array.isArray(out.messages)) ? out.messages : [];
147
+ for (const m of turnMsgs) {
148
+ if (m.role === 'assistant' && Array.isArray(m.tool_calls) && m.tool_calls.length) {
149
+ const parts = [];
150
+ if (m.content) parts.push({ type: 'text', text: String(m.content) });
151
+ for (const tc of m.tool_calls) {
152
+ const rawArgs = tc.function?.arguments ?? tc.arguments;
153
+ let input = {};
154
+ if (rawArgs && typeof rawArgs === 'object') input = rawArgs;
155
+ else if (typeof rawArgs === 'string') { try { input = JSON.parse(rawArgs || '{}'); } catch { input = {}; } }
156
+ parts.push({ type: 'tool_use', name: tc.function?.name || tc.name, input });
157
+ }
158
+ events.push({ event: 'message', data: { role: 'assistant', content: parts } });
159
+ } else if (m.role === 'tool') {
160
+ events.push({ event: 'message', data: { role: 'tool', content: [{ content: String(m.content ?? '') }] } });
161
+ }
162
+ }
163
+ const finalText = (out && out.result) || (out && out.error ? 'error: ' + out.error : '');
164
+ if (finalText) events.push({ event: 'message', data: { role: 'assistant', content: [{ type: 'text', text: String(finalText) }] } });
165
+ if (!events.length) events.push({ event: 'message', data: { role: 'assistant', content: [{ type: 'text', text: '' }] } });
166
+ } catch (e) {
167
+ events = [{ event: 'error', data: { error: e?.message || String(e) } }];
168
+ }
169
+ } else {
170
+ // Static deploy without an in-page agent runtime: single direct
171
+ // acptoapi /v1/chat/completions call (no tool loop — one shot).
172
+ const cfg = (window.__debug?.instances?.i1?.host?.fs?.readJson?.('/etc/freddie/freddie.json', null)) || {};
173
+ const baseUrl = cfg?.providers?.openai?.baseUrl || 'http://localhost:4800';
174
+ try {
175
+ const url = baseUrl.replace(/\/+$/, '') + '/v1/chat/completions';
176
+ const reqBody = { model: chatState.model || cfg?.providers?.openai?.model || 'auto', messages: [{ role: 'user', content: trimmed }] };
177
+ const r2 = await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(reqBody), signal: chatState.abort.signal });
178
+ if (!r2.ok) {
179
+ const errText = await r2.text().catch(() => '');
180
+ events = [{ event: 'error', data: { error: 'acptoapi ' + r2.status + ': ' + errText.slice(0, 200) } }];
181
+ } else {
182
+ const j = await r2.json();
183
+ const content = j?.choices?.[0]?.message?.content || '';
184
+ const tool_calls = j?.choices?.[0]?.message?.tool_calls;
185
+ const parts = [];
186
+ if (content) parts.push({ type: 'text', text: content });
187
+ if (Array.isArray(tool_calls)) {
188
+ for (const tc of tool_calls) parts.push({ type: 'tool_use', name: tc.function?.name, input: (() => { try { return JSON.parse(tc.function?.arguments || '{}'); } catch { return {}; } })() });
189
+ }
190
+ events = [{ event: 'message', data: { role: 'assistant', content: parts.length ? parts : [{ type: 'text', text: '' }] } }];
191
+ }
192
+ } catch (e) {
193
+ events = [{ event: 'error', data: { error: e?.message || String(e) } }];
194
+ }
195
+ }
93
196
  let assistantContent = '';
94
197
  for (const { event, data } of events) {
95
198
  if (event === 'start' && data.sessionId) chatState.sessionId = data.sessionId;
@@ -140,6 +243,7 @@ export function makeChatPage(ctx) {
140
243
  }
141
244
  chatState.abort = null;
142
245
  chatState.busy = false;
246
+ chatState.progress = '';
143
247
  syncMessages();
144
248
  renderPage();
145
249
  };
@@ -152,7 +256,9 @@ export function makeChatPage(ctx) {
152
256
  const host = getChatHost();
153
257
  if (host) {
154
258
  host.busy = chatState.busy;
155
- host.placeholder = chatState.busy ? 'agent working…' : 'describe what you want to do in the working directory…';
259
+ host.placeholder = chatState.busy
260
+ ? (chatState.progress || 'agent working…')
261
+ : 'describe what you want to do in the working directory…';
156
262
  }
157
263
  // Refresh disabled state on header buttons.
158
264
  const newBtn = root.querySelector('.fd-chat-new');