bloby-bot 0.46.2 → 0.47.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.
package/worker/index.ts CHANGED
@@ -16,6 +16,9 @@ import {
16
16
  import { startClaudeOAuth, exchangeClaudeCode, getClaudeAuthStatus, readClaudeAccessToken } from './claude-auth.js';
17
17
  import { checkAvailability, registerHandle, claimReservedHandle, releaseHandle, updateTunnelUrl, startHeartbeat, stopHeartbeat } from '../shared/relay.js';
18
18
  import { ensureFileDirs } from '../supervisor/file-saver.js';
19
+ import { readPiAuth, writePiAuth, clearPiAuth, getPiAuthStatus } from '../supervisor/harnesses/pi/auth-storage.js';
20
+ import { runPiTestCompletion } from '../supervisor/harnesses/pi/test-completion.js';
21
+ import { PI_SUB_PROVIDERS, getPiSubProvider } from '../supervisor/harnesses/pi/sub-providers.js';
19
22
 
20
23
  // ── Password hashing (scrypt) ──
21
24
 
@@ -133,12 +136,12 @@ app.post('/api/conversations/:id/messages', (req, res) => {
133
136
  });
134
137
  app.get('/api/conversations/:id/messages/recent', (req, res) => {
135
138
  const limit = parseInt(req.query.limit as string) || 20;
136
- const msgs = getRecentMessages(req.params.id, Math.min(limit, 100));
139
+ const msgs = getRecentMessages(req.params.id, Math.min(limit, 1000));
137
140
  res.json(msgs);
138
141
  });
139
142
  app.get('/api/conversations/:id/messages', (req, res) => {
140
143
  const before = req.query.before as string;
141
- const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
144
+ const limit = Math.min(parseInt(req.query.limit as string) || 20, 1000);
142
145
  if (before) {
143
146
  res.json(getMessagesBefore(req.params.id, before, limit));
144
147
  } else {
@@ -228,6 +231,89 @@ app.get('/api/auth/claude/status', async (_req, res) => {
228
231
  res.json(await getClaudeAuthStatus());
229
232
  });
230
233
 
234
+ // ── Pi (Bloby third harness) auth routes ──
235
+
236
+ app.get('/api/auth/pi/providers', (_req, res) => {
237
+ res.json({
238
+ providers: PI_SUB_PROVIDERS.map((p) => ({
239
+ id: p.id,
240
+ name: p.name,
241
+ subtitle: p.subtitle,
242
+ flavor: p.flavor,
243
+ baseUrl: p.baseUrl,
244
+ needsBaseUrl: !!p.needsBaseUrl,
245
+ needsApiKey: p.needsApiKey !== false,
246
+ apiKeyUrl: p.apiKeyUrl,
247
+ models: p.models,
248
+ defaultModel: p.defaultModel,
249
+ })),
250
+ });
251
+ });
252
+
253
+ app.get('/api/auth/pi/status', (_req, res) => {
254
+ res.json(getPiAuthStatus());
255
+ });
256
+
257
+ app.post('/api/auth/pi/test', async (req, res) => {
258
+ const { subProvider, apiKey, baseUrl, modelId } = req.body || {};
259
+ if (!subProvider || typeof subProvider !== 'string') {
260
+ res.json({ ok: false, error: 'Missing subProvider' });
261
+ return;
262
+ }
263
+ const provider = getPiSubProvider(subProvider);
264
+ if (!provider) {
265
+ res.json({ ok: false, error: `Unknown sub-provider: ${subProvider}` });
266
+ return;
267
+ }
268
+ const prompt = 'Reply with the single word OK so we can confirm this LLM endpoint is reachable.';
269
+ const result = await runPiTestCompletion({ subProvider, apiKey, baseUrl, modelId, prompt });
270
+ res.json(result);
271
+ });
272
+
273
+ app.post('/api/auth/pi/save', (req, res) => {
274
+ const { subProvider, apiKey, baseUrl, modelId } = req.body || {};
275
+ if (!subProvider || typeof subProvider !== 'string') {
276
+ res.json({ ok: false, error: 'Missing subProvider' });
277
+ return;
278
+ }
279
+ const provider = getPiSubProvider(subProvider);
280
+ if (!provider) {
281
+ res.json({ ok: false, error: `Unknown sub-provider: ${subProvider}` });
282
+ return;
283
+ }
284
+ const saved = writePiAuth({
285
+ subProvider,
286
+ apiKey: typeof apiKey === 'string' ? apiKey : undefined,
287
+ baseUrl: typeof baseUrl === 'string' && baseUrl.trim() ? baseUrl.trim() : provider.baseUrl,
288
+ modelId: typeof modelId === 'string' && modelId.trim() ? modelId.trim() : provider.defaultModel,
289
+ });
290
+ res.json({ ok: true, status: { configured: true, subProvider: saved.subProvider, modelId: saved.modelId, baseUrl: saved.baseUrl } });
291
+ });
292
+
293
+ app.delete('/api/auth/pi', (_req, res) => {
294
+ clearPiAuth();
295
+ res.json({ ok: true });
296
+ });
297
+
298
+ app.post('/api/auth/pi/completion', async (req, res) => {
299
+ const auth = readPiAuth();
300
+ if (!auth) {
301
+ res.json({ ok: false, error: 'Bloby provider is not configured yet' });
302
+ return;
303
+ }
304
+ const prompt = (req.body?.prompt && typeof req.body.prompt === 'string')
305
+ ? req.body.prompt
306
+ : 'In one short sentence, introduce yourself as the model behind this endpoint and confirm the connection is working.';
307
+ const result = await runPiTestCompletion({
308
+ subProvider: auth.subProvider,
309
+ apiKey: auth.apiKey,
310
+ baseUrl: auth.baseUrl,
311
+ modelId: auth.modelId,
312
+ prompt,
313
+ });
314
+ res.json(result);
315
+ });
316
+
231
317
  // ── Handle registration ──
232
318
 
233
319
  app.get('/api/handle/check/:username', async (req, res) => {