@astroanywhere/cli 0.2.0 → 0.2.2

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.
@@ -168,6 +168,10 @@ var AstroClient = class {
168
168
  async listFileChanges(executionId) {
169
169
  return this.get("/api/data/file-changes", { executionId });
170
170
  }
171
+ // ── Usage / Cost ────────────────────────────────────────────────────
172
+ async getUsageHistory(weeks = 1) {
173
+ return this.get("/api/data/usage/history", { weeks: String(weeks) });
174
+ }
171
175
  // ── Activity ────────────────────────────────────────────────────────
172
176
  async listActivities(params) {
173
177
  return this.get("/api/data/activities", params);
@@ -286,15 +290,79 @@ var AstroClient = class {
286
290
  }
287
291
  return res;
288
292
  }
289
- };
290
- async function streamDispatchToStdout(response) {
291
- if (!response.body) {
292
- console.log("Task dispatched (no stream)");
293
- return;
293
+ // ── Chat (SSE streaming) ──────────────────────────────────────────
294
+ async projectChat(payload) {
295
+ const url = new URL("/api/agent/project-chat", this.baseUrl);
296
+ const res = await fetch(url.toString(), {
297
+ method: "POST",
298
+ headers: this.headers,
299
+ body: JSON.stringify(payload)
300
+ });
301
+ if (!res.ok) {
302
+ const text = await res.text();
303
+ throw new Error(`Project chat failed (${res.status}): ${text}`);
304
+ }
305
+ return res;
306
+ }
307
+ async taskChat(payload) {
308
+ const url = new URL("/api/agent/task-chat", this.baseUrl);
309
+ const res = await fetch(url.toString(), {
310
+ method: "POST",
311
+ headers: this.headers,
312
+ body: JSON.stringify(payload)
313
+ });
314
+ if (!res.ok) {
315
+ const text = await res.text();
316
+ throw new Error(`Task chat failed (${res.status}): ${text}`);
317
+ }
318
+ return res;
294
319
  }
320
+ // ── Approval ───────────────────────────────────────────────────────
321
+ async sendApproval(payload) {
322
+ return this.post("/api/dispatch/approval", payload);
323
+ }
324
+ // ── Summarize ──────────────────────────────────────────────────────
325
+ async summarize(payload) {
326
+ return this.post("/api/agent/summarize", payload);
327
+ }
328
+ // ── Slurm Dispatch ────────────────────────────────────────────────
329
+ async dispatchSlurmTask(payload) {
330
+ const url = new URL("/api/relay/slurm/dispatch", this.baseUrl);
331
+ const res = await fetch(url.toString(), {
332
+ method: "POST",
333
+ headers: this.headers,
334
+ body: JSON.stringify(payload)
335
+ });
336
+ if (!res.ok) {
337
+ const text = await res.text();
338
+ throw new Error(`Slurm dispatch failed (${res.status}): ${text}`);
339
+ }
340
+ return res;
341
+ }
342
+ };
343
+ async function readSSEStream(response, handler) {
344
+ if (!response.body) return;
295
345
  const reader = response.body.getReader();
296
346
  const decoder = new TextDecoder();
297
347
  let buffer = "";
348
+ let currentEvent = "";
349
+ let dataLines = [];
350
+ function flushEvent() {
351
+ if (!currentEvent) return null;
352
+ const data = dataLines.join("\n");
353
+ const event = currentEvent;
354
+ currentEvent = "";
355
+ dataLines = [];
356
+ if (event === "text") {
357
+ return { type: "text", content: data, text: data };
358
+ }
359
+ try {
360
+ const parsed = JSON.parse(data);
361
+ return { type: event, ...parsed };
362
+ } catch {
363
+ return { type: event, data };
364
+ }
365
+ }
298
366
  while (true) {
299
367
  const { done, value } = await reader.read();
300
368
  if (done) break;
@@ -302,34 +370,176 @@ async function streamDispatchToStdout(response) {
302
370
  const lines = buffer.split("\n");
303
371
  buffer = lines.pop() ?? "";
304
372
  for (const line of lines) {
305
- if (line.startsWith("data: ")) {
306
- try {
307
- const event = JSON.parse(line.slice(6));
308
- switch (event.type) {
309
- case "text":
310
- process.stdout.write(event.content ?? "");
311
- break;
312
- case "tool_use":
313
- console.log(`
314
- [tool] ${event.name}`);
315
- break;
316
- case "result":
317
- console.log(`
318
- --- Result: ${event.status} ---`);
319
- if (event.summary) console.log(event.summary);
320
- break;
321
- case "error":
322
- console.error(`
323
- Error: ${event.message}`);
324
- break;
325
- case "done":
326
- break;
327
- }
328
- } catch {
329
- }
373
+ if (line.startsWith("event:")) {
374
+ const pending2 = flushEvent();
375
+ if (pending2) await handler(pending2);
376
+ currentEvent = line.slice(6).trim();
377
+ } else if (line.startsWith("data:")) {
378
+ const value2 = line.slice(5);
379
+ dataLines.push(value2.startsWith(" ") ? value2.slice(1) : value2);
380
+ } else if (line === "") {
381
+ const pending2 = flushEvent();
382
+ if (pending2) await handler(pending2);
330
383
  }
331
384
  }
332
385
  }
386
+ const pending = flushEvent();
387
+ if (pending) await handler(pending);
388
+ }
389
+ async function streamDispatchToStdout(response, opts) {
390
+ const result = {};
391
+ if (!response.body) {
392
+ console.log("Task dispatched (no stream)");
393
+ return result;
394
+ }
395
+ await readSSEStream(response, async (event) => {
396
+ const type = event.type;
397
+ if (opts?.json) {
398
+ console.log(JSON.stringify(event));
399
+ return;
400
+ }
401
+ switch (type) {
402
+ case "text":
403
+ process.stdout.write(event.content ?? "");
404
+ break;
405
+ case "tool_use":
406
+ process.stderr.write(`
407
+ [tool] ${event.toolName ?? event.name}
408
+ `);
409
+ break;
410
+ case "tool_result":
411
+ break;
412
+ case "session_init":
413
+ result.sessionId = event.sessionId;
414
+ break;
415
+ case "result":
416
+ console.log(`
417
+ --- Result: ${event.status} ---`);
418
+ if (event.summary) console.log(event.summary);
419
+ if (event.durationMs || event.inputTokens || event.outputTokens || event.totalCost) {
420
+ result.metrics = {
421
+ durationMs: event.durationMs,
422
+ inputTokens: event.inputTokens,
423
+ outputTokens: event.outputTokens,
424
+ totalCost: event.totalCost,
425
+ model: event.model
426
+ };
427
+ }
428
+ break;
429
+ case "plan_result":
430
+ console.log("\n--- Plan Generated ---");
431
+ console.log(JSON.stringify(event.plan ?? event, null, 2));
432
+ break;
433
+ case "approval_request":
434
+ if (opts?.onApprovalRequest) {
435
+ const approval = await opts.onApprovalRequest({
436
+ requestId: event.requestId,
437
+ question: event.question,
438
+ options: event.options,
439
+ machineId: event.machineId,
440
+ taskId: event.taskId
441
+ });
442
+ void approval;
443
+ } else {
444
+ process.stderr.write(`
445
+ [approval] ${event.question}
446
+ `);
447
+ process.stderr.write(` Options: ${event.options.join(", ")}
448
+ `);
449
+ }
450
+ break;
451
+ case "error":
452
+ console.error(`
453
+ Error: ${event.error ?? event.message}`);
454
+ break;
455
+ case "done":
456
+ case "heartbeat":
457
+ case "aborted":
458
+ break;
459
+ }
460
+ });
461
+ return result;
462
+ }
463
+ async function streamChatToStdout(response, opts) {
464
+ const result = {};
465
+ let assistantText = "";
466
+ if (!response.body) {
467
+ console.log("No stream received");
468
+ return result;
469
+ }
470
+ await readSSEStream(response, async (event) => {
471
+ const type = event.type;
472
+ if (opts?.json) {
473
+ console.log(JSON.stringify(event));
474
+ return;
475
+ }
476
+ switch (type) {
477
+ case "text":
478
+ const content = event.content ?? "";
479
+ process.stdout.write(content);
480
+ assistantText += content;
481
+ break;
482
+ case "tool_use":
483
+ process.stderr.write(`
484
+ [tool] ${event.toolName ?? event.name}
485
+ `);
486
+ break;
487
+ case "tool_result":
488
+ break;
489
+ case "session_init":
490
+ result.sessionId = event.sessionId;
491
+ break;
492
+ case "file_change":
493
+ process.stderr.write(`[file] ${event.action} ${event.path}
494
+ `);
495
+ break;
496
+ case "compaction":
497
+ process.stderr.write(`[compaction] History compacted: ${event.originalCount} \u2192 ${event.compactedCount} messages
498
+ `);
499
+ break;
500
+ case "plan_result":
501
+ console.log("\n--- Plan Generated ---");
502
+ console.log(JSON.stringify(event.plan ?? event, null, 2));
503
+ break;
504
+ case "approval_request":
505
+ if (opts?.onApprovalRequest) {
506
+ const approval = await opts.onApprovalRequest({
507
+ requestId: event.requestId,
508
+ question: event.question,
509
+ options: event.options,
510
+ machineId: event.machineId,
511
+ taskId: event.taskId
512
+ });
513
+ void approval;
514
+ } else {
515
+ process.stderr.write(`
516
+ [approval] ${event.question}
517
+ `);
518
+ process.stderr.write(` Options: ${event.options.join(", ")}
519
+ `);
520
+ }
521
+ break;
522
+ case "done":
523
+ if (event.durationMs || event.inputTokens || event.outputTokens || event.totalCost) {
524
+ result.metrics = {
525
+ durationMs: event.durationMs,
526
+ inputTokens: event.inputTokens,
527
+ outputTokens: event.outputTokens,
528
+ totalCost: event.totalCost,
529
+ model: event.model
530
+ };
531
+ }
532
+ break;
533
+ case "error":
534
+ console.error(`
535
+ Error: ${event.message}`);
536
+ break;
537
+ case "heartbeat":
538
+ break;
539
+ }
540
+ });
541
+ result.assistantText = assistantText;
542
+ return result;
333
543
  }
334
544
  var _client = null;
335
545
  var _clientUrl;
@@ -349,6 +559,8 @@ export {
349
559
  resetConfig,
350
560
  getServerUrl,
351
561
  AstroClient,
562
+ readSSEStream,
352
563
  streamDispatchToStdout,
564
+ streamChatToStdout,
353
565
  getClient
354
566
  };
package/dist/client.js CHANGED
@@ -2,10 +2,14 @@
2
2
  import {
3
3
  AstroClient,
4
4
  getClient,
5
+ readSSEStream,
6
+ streamChatToStdout,
5
7
  streamDispatchToStdout
6
- } from "./chunk-7H7WD7QX.js";
8
+ } from "./chunk-MJRAJPBU.js";
7
9
  export {
8
10
  AstroClient,
9
11
  getClient,
12
+ readSSEStream,
13
+ streamChatToStdout,
10
14
  streamDispatchToStdout
11
15
  };