@sandagent/runner-cli 0.9.9-beta.0 → 0.9.9-beta.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.
Files changed (2) hide show
  1. package/dist/bundle.mjs +176 -166
  2. package/package.json +4 -4
package/dist/bundle.mjs CHANGED
@@ -1398,19 +1398,20 @@ function createPiRunner(options = {}) {
1398
1398
  }
1399
1399
  const { provider, modelName } = parseModelSpec(modelSpec.trim());
1400
1400
  const cwd = options.cwd || process.cwd();
1401
+ const apiKeyEnvKey = `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY`;
1402
+ const inlineApiKey = typeof options.env?.[apiKeyEnvKey] === "string" && options.env[apiKeyEnvKey].length > 0 ? options.env[apiKeyEnvKey] : void 0;
1401
1403
  const modelRegistry = new ModelRegistry(AuthStorage.create());
1402
1404
  const defaultModel = getModel(provider, modelName);
1403
1405
  let model = defaultModel ?? modelRegistry.find(provider, modelName);
1404
1406
  if (model == null) {
1405
1407
  const baseUrlEnvKey = `${provider.toUpperCase().replace(/-/g, "_")}_BASE_URL`;
1406
- const apiKeyEnvKey = `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY`;
1407
1408
  const baseUrl = getEnvValue(options.env, baseUrlEnvKey) ?? getEnvValue(options.env, "OPENAI_BASE_URL");
1408
1409
  if (!baseUrl) {
1409
1410
  throw new Error(`Pi runner: model "${modelSpec}" not found in built-in catalog. Set ${baseUrlEnvKey} (or OPENAI_BASE_URL) to auto-register it.`);
1410
1411
  }
1411
1412
  modelRegistry.registerProvider(provider, {
1412
1413
  baseUrl,
1413
- apiKey: apiKeyEnvKey,
1414
+ apiKey: inlineApiKey ?? apiKeyEnvKey,
1414
1415
  api: "openai-completions",
1415
1416
  models: [
1416
1417
  {
@@ -1433,213 +1434,222 @@ function createPiRunner(options = {}) {
1433
1434
  applyModelOverrides(model, provider, options.env);
1434
1435
  return {
1435
1436
  async *run(userInput) {
1436
- const resume = options.sessionId?.trim();
1437
- const sessionManager = await (async () => {
1438
- if (resume !== void 0 && resume !== "") {
1439
- if (resume.includes("/")) {
1440
- return SessionManager.open(resume);
1437
+ if (inlineApiKey !== void 0) {
1438
+ modelRegistry.authStorage.setRuntimeApiKey(provider, inlineApiKey);
1439
+ }
1440
+ try {
1441
+ const resume = options.sessionId?.trim();
1442
+ const sessionManager = await (async () => {
1443
+ if (resume !== void 0 && resume !== "") {
1444
+ if (resume.includes("/")) {
1445
+ return SessionManager.open(resume);
1446
+ }
1447
+ const sessions = await SessionManager.list(cwd);
1448
+ const found = sessions.find((s) => s.id === resume);
1449
+ return found ? SessionManager.open(found.path) : SessionManager.create(cwd);
1441
1450
  }
1442
- const sessions = await SessionManager.list(cwd);
1443
- const found = sessions.find((s) => s.id === resume);
1444
- return found ? SessionManager.open(found.path) : SessionManager.create(cwd);
1451
+ return SessionManager.create(cwd);
1452
+ })();
1453
+ const resourceLoader = options.skillPaths ? new SandagentResourceLoader({ cwd, skillPaths: options.skillPaths }) : void 0;
1454
+ if (options.skillPaths && options.skillPaths.length > 0) {
1455
+ console.error(`${LOG_PREFIX2} runner: cwd=${cwd} skillPaths=${JSON.stringify(options.skillPaths)}`);
1445
1456
  }
1446
- return SessionManager.create(cwd);
1447
- })();
1448
- const resourceLoader = options.skillPaths ? new SandagentResourceLoader({ cwd, skillPaths: options.skillPaths }) : void 0;
1449
- if (options.skillPaths && options.skillPaths.length > 0) {
1450
- console.error(`${LOG_PREFIX2} runner: cwd=${cwd} skillPaths=${JSON.stringify(options.skillPaths)}`);
1451
- }
1452
- if (resourceLoader) {
1453
- await resourceLoader.reload();
1454
- }
1455
- const { session } = await createAgentSession({
1456
- cwd,
1457
- model,
1458
- sessionManager,
1459
- modelRegistry,
1460
- resourceLoader
1461
- });
1462
- if (options.systemPrompt != null && options.systemPrompt !== "") {
1463
- const existing = session.agent.state.systemPrompt ?? "";
1464
- session.agent.setSystemPrompt(existing ? `${existing}
1457
+ if (resourceLoader) {
1458
+ await resourceLoader.reload();
1459
+ }
1460
+ const { session } = await createAgentSession({
1461
+ cwd,
1462
+ model,
1463
+ sessionManager,
1464
+ modelRegistry,
1465
+ resourceLoader
1466
+ });
1467
+ if (options.systemPrompt != null && options.systemPrompt !== "") {
1468
+ const existing = session.agent.state.systemPrompt ?? "";
1469
+ session.agent.setSystemPrompt(existing ? `${existing}
1465
1470
 
1466
1471
  ---
1467
1472
 
1468
1473
  ${options.systemPrompt}` : options.systemPrompt);
1469
- }
1470
- const eventQueue = [];
1471
- let isComplete = false;
1472
- let aborted = false;
1473
- let wakeConsumer = null;
1474
- const notify = () => {
1475
- wakeConsumer?.();
1476
- wakeConsumer = null;
1477
- };
1478
- const unsubscribe = session.subscribe((e) => {
1479
- eventQueue.push(e);
1480
- if (e.type === "agent_end") {
1481
- isComplete = true;
1482
1474
  }
1483
- notify();
1484
- });
1485
- const abortSignal = options.abortController?.signal;
1486
- const abortHandler = () => {
1487
- aborted = true;
1488
- isComplete = true;
1489
- void session.abort();
1490
- notify();
1491
- };
1492
- if (abortSignal) {
1493
- abortSignal.addEventListener("abort", abortHandler);
1494
- if (abortSignal.aborted) {
1495
- abortHandler();
1475
+ const eventQueue = [];
1476
+ let isComplete = false;
1477
+ let aborted = false;
1478
+ let wakeConsumer = null;
1479
+ const notify = () => {
1480
+ wakeConsumer?.();
1481
+ wakeConsumer = null;
1482
+ };
1483
+ const unsubscribe = session.subscribe((e) => {
1484
+ eventQueue.push(e);
1485
+ if (e.type === "agent_end") {
1486
+ isComplete = true;
1487
+ }
1488
+ notify();
1489
+ });
1490
+ const abortSignal = options.abortController?.signal;
1491
+ const abortHandler = () => {
1492
+ aborted = true;
1493
+ isComplete = true;
1494
+ void session.abort();
1495
+ notify();
1496
+ };
1497
+ if (abortSignal) {
1498
+ abortSignal.addEventListener("abort", abortHandler);
1499
+ if (abortSignal.aborted) {
1500
+ abortHandler();
1501
+ }
1496
1502
  }
1497
- }
1498
- try {
1499
- traceRawMessage(cwd, null, true);
1500
- let promptText = userInput;
1501
- let images;
1502
1503
  try {
1503
- if (userInput.startsWith("[") && userInput.endsWith("]")) {
1504
- const parsed = JSON.parse(userInput);
1505
- if (Array.isArray(parsed)) {
1506
- promptText = parsed.filter((p) => p.type === "text").map((p) => p.text).join("\n");
1507
- const imageParts = parsed.filter((p) => p.type === "image");
1508
- if (imageParts.length > 0) {
1509
- images = imageParts.map((p) => ({
1510
- type: "image",
1511
- data: p.data,
1512
- mimeType: p.mimeType
1513
- }));
1504
+ traceRawMessage(cwd, null, true);
1505
+ let promptText = userInput;
1506
+ let images;
1507
+ try {
1508
+ if (userInput.startsWith("[") && userInput.endsWith("]")) {
1509
+ const parsed = JSON.parse(userInput);
1510
+ if (Array.isArray(parsed)) {
1511
+ promptText = parsed.filter((p) => p.type === "text").map((p) => p.text).join("\n");
1512
+ const imageParts = parsed.filter((p) => p.type === "image");
1513
+ if (imageParts.length > 0) {
1514
+ images = imageParts.map((p) => ({
1515
+ type: "image",
1516
+ data: p.data,
1517
+ mimeType: p.mimeType
1518
+ }));
1519
+ }
1514
1520
  }
1515
1521
  }
1522
+ } catch (_e) {
1516
1523
  }
1517
- } catch (_e) {
1518
- }
1519
- const promptPromise = session.prompt(promptText, images ? { images } : void 0);
1520
- const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`;
1521
- const textId = `text_${Date.now()}_${Math.random().toString(36).slice(2)}`;
1522
- let hasStarted = false;
1523
- let hasTextStarted = false;
1524
- let hasFinished = false;
1525
- const ensureStartEvent = async function* () {
1526
- if (!hasStarted) {
1527
- yield `data: ${JSON.stringify({ type: "start", messageId })}
1524
+ const promptPromise = session.prompt(promptText, images ? { images } : void 0);
1525
+ const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`;
1526
+ const textId = `text_${Date.now()}_${Math.random().toString(36).slice(2)}`;
1527
+ let hasStarted = false;
1528
+ let hasTextStarted = false;
1529
+ let hasFinished = false;
1530
+ const ensureStartEvent = async function* () {
1531
+ if (!hasStarted) {
1532
+ yield `data: ${JSON.stringify({ type: "start", messageId })}
1528
1533
 
1529
1534
  `;
1530
- yield `data: ${JSON.stringify({
1531
- type: "message-metadata",
1532
- messageMetadata: { sessionId: session.sessionId }
1533
- })}
1535
+ yield `data: ${JSON.stringify({
1536
+ type: "message-metadata",
1537
+ messageMetadata: { sessionId: session.sessionId }
1538
+ })}
1534
1539
 
1535
1540
  `;
1536
- hasStarted = true;
1537
- }
1538
- };
1539
- const finishSuccess = async function* (usage) {
1540
- if (hasTextStarted) {
1541
- yield `data: ${JSON.stringify({ type: "text-end", id: textId })}
1541
+ hasStarted = true;
1542
+ }
1543
+ };
1544
+ const finishSuccess = async function* (usage) {
1545
+ if (hasTextStarted) {
1546
+ yield `data: ${JSON.stringify({ type: "text-end", id: textId })}
1542
1547
 
1543
1548
  `;
1544
- }
1545
- const finishPayload = { type: "finish", finishReason: "stop" };
1546
- if (usage != null) {
1547
- finishPayload.messageMetadata = {
1548
- usage: usageToMessageMetadata(usage)
1549
- };
1550
- }
1551
- yield `data: ${JSON.stringify(finishPayload)}
1549
+ }
1550
+ const finishPayload = { type: "finish", finishReason: "stop" };
1551
+ if (usage != null) {
1552
+ finishPayload.messageMetadata = {
1553
+ usage: usageToMessageMetadata(usage)
1554
+ };
1555
+ }
1556
+ yield `data: ${JSON.stringify(finishPayload)}
1552
1557
 
1553
1558
  `;
1554
- yield "data: [DONE]\n\n";
1555
- hasFinished = true;
1556
- };
1557
- const finishError = async function* (errorText) {
1558
- for (const chunk of emitStreamError(errorText)) {
1559
- yield chunk;
1560
- }
1561
- hasFinished = true;
1562
- };
1563
- while (!isComplete || eventQueue.length > 0) {
1564
- while (eventQueue.length > 0) {
1565
- const event = eventQueue.shift();
1566
- traceRawMessage(cwd, event);
1567
- yield* ensureStartEvent();
1568
- if (event.type === "message_update") {
1569
- if (event.assistantMessageEvent.type === "text_delta") {
1570
- const delta = event.assistantMessageEvent.delta;
1571
- if (delta) {
1572
- if (!hasTextStarted) {
1573
- yield `data: ${JSON.stringify({ type: "text-start", id: textId })}
1559
+ yield "data: [DONE]\n\n";
1560
+ hasFinished = true;
1561
+ };
1562
+ const finishError = async function* (errorText) {
1563
+ for (const chunk of emitStreamError(errorText)) {
1564
+ yield chunk;
1565
+ }
1566
+ hasFinished = true;
1567
+ };
1568
+ while (!isComplete || eventQueue.length > 0) {
1569
+ while (eventQueue.length > 0) {
1570
+ const event = eventQueue.shift();
1571
+ traceRawMessage(cwd, event);
1572
+ yield* ensureStartEvent();
1573
+ if (event.type === "message_update") {
1574
+ if (event.assistantMessageEvent.type === "text_delta") {
1575
+ const delta = event.assistantMessageEvent.delta;
1576
+ if (delta) {
1577
+ if (!hasTextStarted) {
1578
+ yield `data: ${JSON.stringify({ type: "text-start", id: textId })}
1574
1579
 
1575
1580
  `;
1576
- hasTextStarted = true;
1577
- }
1578
- yield `data: ${JSON.stringify({ type: "text-delta", id: textId, delta })}
1581
+ hasTextStarted = true;
1582
+ }
1583
+ yield `data: ${JSON.stringify({ type: "text-delta", id: textId, delta })}
1579
1584
 
1580
1585
  `;
1586
+ }
1581
1587
  }
1582
- }
1583
- } else if (event.type === "tool_execution_start") {
1584
- yield `data: ${JSON.stringify({ type: "tool-input-start", toolCallId: event.toolCallId, toolName: event.toolName, dynamic: true, providerExecuted: true })}
1588
+ } else if (event.type === "tool_execution_start") {
1589
+ yield `data: ${JSON.stringify({ type: "tool-input-start", toolCallId: event.toolCallId, toolName: event.toolName, dynamic: true, providerExecuted: true })}
1585
1590
 
1586
1591
  `;
1587
- yield `data: ${JSON.stringify({ type: "tool-input-available", toolCallId: event.toolCallId, toolName: event.toolName, input: event.args, dynamic: true, providerExecuted: true })}
1592
+ yield `data: ${JSON.stringify({ type: "tool-input-available", toolCallId: event.toolCallId, toolName: event.toolName, input: event.args, dynamic: true, providerExecuted: true })}
1588
1593
 
1589
1594
  `;
1590
- } else if (event.type === "tool_execution_end") {
1591
- const output = extractToolResultText(event.result);
1592
- yield `data: ${JSON.stringify({ type: "tool-output-available", toolCallId: event.toolCallId, output, isError: event.isError, dynamic: true, providerExecuted: true })}
1595
+ } else if (event.type === "tool_execution_end") {
1596
+ const output = extractToolResultText(event.result);
1597
+ yield `data: ${JSON.stringify({ type: "tool-output-available", toolCallId: event.toolCallId, output, isError: event.isError, dynamic: true, providerExecuted: true })}
1593
1598
 
1594
1599
  `;
1595
- } else if (event.type === "agent_end") {
1596
- if (aborted) {
1597
- yield* finishError("Run aborted by signal.");
1598
- } else {
1599
- const errorMsg = getErrorFromAgentEndMessages(event.messages);
1600
- if (errorMsg) {
1601
- yield* finishError(errorMsg);
1600
+ } else if (event.type === "agent_end") {
1601
+ if (aborted) {
1602
+ yield* finishError("Run aborted by signal.");
1602
1603
  } else {
1603
- const usage = getUsageFromAgentEndMessages(event.messages);
1604
- yield* finishSuccess(usage);
1604
+ const errorMsg = getErrorFromAgentEndMessages(event.messages);
1605
+ if (errorMsg) {
1606
+ yield* finishError(errorMsg);
1607
+ } else {
1608
+ const usage = getUsageFromAgentEndMessages(event.messages);
1609
+ yield* finishSuccess(usage);
1610
+ }
1605
1611
  }
1606
1612
  }
1607
1613
  }
1614
+ if (aborted && !hasFinished) {
1615
+ yield* ensureStartEvent();
1616
+ yield* finishError("Run aborted by signal.");
1617
+ break;
1618
+ }
1619
+ if (!isComplete && eventQueue.length === 0) {
1620
+ await new Promise((resolve4) => {
1621
+ wakeConsumer = resolve4;
1622
+ });
1623
+ }
1608
1624
  }
1609
- if (aborted && !hasFinished) {
1610
- yield* ensureStartEvent();
1611
- yield* finishError("Run aborted by signal.");
1612
- break;
1625
+ if (hasFinished) {
1626
+ return;
1613
1627
  }
1614
- if (!isComplete && eventQueue.length === 0) {
1615
- await new Promise((resolve4) => {
1616
- wakeConsumer = resolve4;
1617
- });
1628
+ try {
1629
+ await promptPromise;
1630
+ } catch (error) {
1631
+ if (!hasFinished) {
1632
+ yield* ensureStartEvent();
1633
+ const message = error instanceof Error ? error.message : "Pi agent run failed.";
1634
+ yield* finishError(message);
1635
+ }
1636
+ return;
1618
1637
  }
1619
- }
1620
- if (hasFinished) {
1621
- return;
1622
- }
1623
- try {
1624
- await promptPromise;
1625
- } catch (error) {
1626
- if (!hasFinished) {
1638
+ if (!hasFinished && session.agent.state.error) {
1627
1639
  yield* ensureStartEvent();
1628
- const message = error instanceof Error ? error.message : "Pi agent run failed.";
1629
- yield* finishError(message);
1640
+ yield* finishError(session.agent.state.error);
1630
1641
  }
1631
- return;
1632
- }
1633
- if (!hasFinished && session.agent.state.error) {
1634
- yield* ensureStartEvent();
1635
- yield* finishError(session.agent.state.error);
1642
+ } finally {
1643
+ if (abortSignal) {
1644
+ abortSignal.removeEventListener("abort", abortHandler);
1645
+ }
1646
+ unsubscribe();
1647
+ session.dispose();
1636
1648
  }
1637
1649
  } finally {
1638
- if (abortSignal) {
1639
- abortSignal.removeEventListener("abort", abortHandler);
1650
+ if (inlineApiKey !== void 0) {
1651
+ modelRegistry.authStorage.removeRuntimeApiKey(provider);
1640
1652
  }
1641
- unsubscribe();
1642
- session.dispose();
1643
1653
  }
1644
1654
  }
1645
1655
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sandagent/runner-cli",
3
- "version": "0.9.9-beta.0",
3
+ "version": "0.9.9-beta.2",
4
4
  "description": "SandAgent Runner CLI - Like gemini-cli or claude-code, runs in your local terminal with AI SDK UI streaming",
5
5
  "type": "module",
6
6
  "bin": {
@@ -53,12 +53,12 @@
53
53
  "esbuild": "^0.27.2",
54
54
  "typescript": "^5.3.0",
55
55
  "vitest": "^1.6.1",
56
+ "@sandagent/runner-claude": "0.6.2",
57
+ "@sandagent/runner-codex": "0.6.2",
56
58
  "@sandagent/runner-core": "0.1.0",
57
59
  "@sandagent/runner-gemini": "0.6.2",
58
- "@sandagent/runner-opencode": "0.6.2",
59
- "@sandagent/runner-claude": "0.6.2",
60
60
  "@sandagent/runner-pi": "0.6.3",
61
- "@sandagent/runner-codex": "0.6.2"
61
+ "@sandagent/runner-opencode": "0.6.2"
62
62
  },
63
63
  "scripts": {
64
64
  "build": "tsc && pnpm bundle",