@hasna/testers 0.0.34 → 0.0.35

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/dist/cli/index.js CHANGED
@@ -2100,6 +2100,56 @@ var require_commander = __commonJS((exports) => {
2100
2100
  });
2101
2101
 
2102
2102
  // src/types/index.ts
2103
+ function isRecord(value) {
2104
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2105
+ }
2106
+ function stringValue(value) {
2107
+ return typeof value === "string" && value.trim() ? value : undefined;
2108
+ }
2109
+ function numberValue(value) {
2110
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
2111
+ }
2112
+ function stringMap(value) {
2113
+ if (!isRecord(value))
2114
+ return;
2115
+ const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
2116
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined;
2117
+ }
2118
+ function cleanupValue(value) {
2119
+ if (value === "delete" || value === "stop" || value === "keep")
2120
+ return value;
2121
+ return;
2122
+ }
2123
+ function workflowExecutionFromValue(value) {
2124
+ const input = isRecord(value) ? value : {};
2125
+ const rawTarget = stringValue(input["target"]) ?? "local";
2126
+ if (rawTarget === "local") {
2127
+ const timeoutMs2 = numberValue(input["timeoutMs"]);
2128
+ return timeoutMs2 === undefined ? { target: "local" } : { target: "local", timeoutMs: timeoutMs2 };
2129
+ }
2130
+ if (rawTarget !== "sandbox" && rawTarget !== "connector:e2b") {
2131
+ throw new Error(`Unsupported workflow execution target: ${rawTarget}`);
2132
+ }
2133
+ const provider = rawTarget === "connector:e2b" ? "e2b" : stringValue(input["provider"]) ?? stringValue(input["connector"]);
2134
+ const sandboxImage = stringValue(input["sandboxImage"]) ?? stringValue(input["sandboxTemplate"]);
2135
+ const sandboxRemoteDir = stringValue(input["sandboxRemoteDir"]);
2136
+ const sandboxCleanup = cleanupValue(input["sandboxCleanup"]);
2137
+ const setupCommand = stringValue(input["setupCommand"]);
2138
+ const packageSpec = stringValue(input["packageSpec"]);
2139
+ const timeoutMs = numberValue(input["timeoutMs"]);
2140
+ const env = stringMap(input["env"]);
2141
+ return {
2142
+ target: "sandbox",
2143
+ ...provider ? { provider } : {},
2144
+ ...sandboxImage ? { sandboxImage } : {},
2145
+ ...sandboxRemoteDir ? { sandboxRemoteDir } : {},
2146
+ ...sandboxCleanup ? { sandboxCleanup } : {},
2147
+ ...setupCommand ? { setupCommand } : {},
2148
+ ...packageSpec ? { packageSpec } : {},
2149
+ ...timeoutMs !== undefined ? { timeoutMs } : {},
2150
+ ...env ? { env } : {}
2151
+ };
2152
+ }
2103
2153
  function workflowFromRow(row) {
2104
2154
  return {
2105
2155
  id: row.id,
@@ -2109,7 +2159,7 @@ function workflowFromRow(row) {
2109
2159
  scenarioFilter: JSON.parse(row.scenario_filter || "{}"),
2110
2160
  personaIds: JSON.parse(row.persona_ids || "[]"),
2111
2161
  goal: row.goal ? JSON.parse(row.goal) : null,
2112
- execution: JSON.parse(row.execution || '{"target":"local"}'),
2162
+ execution: workflowExecutionFromValue(JSON.parse(row.execution || '{"target":"local"}')),
2113
2163
  settings: JSON.parse(row.settings || "{}"),
2114
2164
  enabled: row.enabled === 1,
2115
2165
  createdAt: row.created_at,
@@ -14074,6 +14124,7 @@ __export(exports_ai_client, {
14074
14124
  createClientForModel: () => createClientForModel,
14075
14125
  createClient: () => createClient,
14076
14126
  callOpenAICompatible: () => callOpenAICompatible,
14127
+ buildScenarioUserMessage: () => buildScenarioUserMessage,
14077
14128
  BROWSER_TOOLS: () => BROWSER_TOOLS
14078
14129
  });
14079
14130
  import Anthropic2 from "@anthropic-ai/sdk";
@@ -14486,7 +14537,6 @@ async function executeTool(page, screenshotter, toolName, toolInput, context) {
14486
14537
  const assertionType = toolInput.assertion_type;
14487
14538
  const selector = toolInput.selector;
14488
14539
  const expected = toolInput.expected;
14489
- const sessionId = context.sessionId ?? "default";
14490
14540
  switch (assertionType) {
14491
14541
  case "element_exists": {
14492
14542
  if (!selector)
@@ -14551,7 +14601,6 @@ async function executeTool(page, screenshotter, toolName, toolInput, context) {
14551
14601
  case "browser_intercept": {
14552
14602
  const action = toolInput.action;
14553
14603
  const pattern = toolInput.pattern;
14554
- const interceptAction = toolInput.intercept_action;
14555
14604
  const statusCode = toolInput.status_code;
14556
14605
  const body = toolInput.body;
14557
14606
  const sessionId = context.sessionId ?? "default";
@@ -14628,7 +14677,28 @@ ${JSON.stringify(har, null, 2)}` };
14628
14677
  }
14629
14678
  case "browser_a11y": {
14630
14679
  const level = toolInput.level ?? "AA";
14631
- const snapshot = await page.accessibility.snapshot();
14680
+ const snapshot = await page.evaluate(() => {
14681
+ function readRole(el) {
14682
+ return el.getAttribute("role") ?? el.tagName.toLowerCase();
14683
+ }
14684
+ function readName(el) {
14685
+ const labelledBy = el.getAttribute("aria-labelledby");
14686
+ if (labelledBy) {
14687
+ const labelledText = labelledBy.split(/\s+/).map((id) => document.getElementById(id)?.textContent?.trim()).filter(Boolean).join(" ");
14688
+ if (labelledText)
14689
+ return labelledText;
14690
+ }
14691
+ return el.getAttribute("aria-label") ?? el.getAttribute("alt") ?? el.textContent?.trim() ?? "";
14692
+ }
14693
+ function walk(el) {
14694
+ return {
14695
+ role: readRole(el),
14696
+ name: readName(el),
14697
+ children: Array.from(el.children).map((child) => walk(child))
14698
+ };
14699
+ }
14700
+ return document.body ? walk(document.body) : null;
14701
+ });
14632
14702
  if (!snapshot)
14633
14703
  return { result: "Error: could not capture accessibility tree" };
14634
14704
  const issues = [];
@@ -14670,6 +14740,38 @@ ${filtered.join(`
14670
14740
  return { result: `Error executing ${toolName}: ${message}` };
14671
14741
  }
14672
14742
  }
14743
+ function resolveStartUrl(baseUrl, targetPath) {
14744
+ try {
14745
+ return new URL(targetPath, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
14746
+ } catch {
14747
+ return `${baseUrl.replace(/\/+$/, "")}/${targetPath.replace(/^\/+/, "")}`;
14748
+ }
14749
+ }
14750
+ function buildScenarioUserMessage(scenario, baseUrl) {
14751
+ const userParts = [
14752
+ `**Scenario:** ${scenario.name}`,
14753
+ `**Description:** ${scenario.description}`
14754
+ ];
14755
+ if (baseUrl) {
14756
+ const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
14757
+ userParts.push(`**Base URL:** ${normalizedBaseUrl}`);
14758
+ if (scenario.targetPath) {
14759
+ userParts.push(`**Start URL:** ${resolveStartUrl(normalizedBaseUrl, scenario.targetPath)}`);
14760
+ }
14761
+ userParts.push("**Navigation Boundary:** Treat the Base URL as the application under test. Resolve relative paths and in-app navigation against this origin. Do not navigate to another host unless a step explicitly includes an absolute external URL.");
14762
+ }
14763
+ if (scenario.targetPath) {
14764
+ userParts.push(`**Target Path:** ${scenario.targetPath}`);
14765
+ }
14766
+ if (scenario.steps.length > 0) {
14767
+ userParts.push("**Steps:**");
14768
+ for (let i = 0;i < scenario.steps.length; i++) {
14769
+ userParts.push(`${i + 1}. ${scenario.steps[i]}`);
14770
+ }
14771
+ }
14772
+ return userParts.join(`
14773
+ `);
14774
+ }
14673
14775
  async function runAgentLoop(options) {
14674
14776
  const {
14675
14777
  client,
@@ -14679,6 +14781,7 @@ async function runAgentLoop(options) {
14679
14781
  model,
14680
14782
  runId,
14681
14783
  sessionId,
14784
+ baseUrl,
14682
14785
  maxTurns = 30,
14683
14786
  onStep,
14684
14787
  persona,
@@ -14726,21 +14829,7 @@ Instructions: ${persona.instructions}` : "",
14726
14829
  "- Verify both positive and negative states"
14727
14830
  ].join(`
14728
14831
  `) + personaSection;
14729
- const userParts = [
14730
- `**Scenario:** ${scenario.name}`,
14731
- `**Description:** ${scenario.description}`
14732
- ];
14733
- if (scenario.targetPath) {
14734
- userParts.push(`**Target Path:** ${scenario.targetPath}`);
14735
- }
14736
- if (scenario.steps.length > 0) {
14737
- userParts.push("**Steps:**");
14738
- for (let i = 0;i < scenario.steps.length; i++) {
14739
- userParts.push(`${i + 1}. ${scenario.steps[i]}`);
14740
- }
14741
- }
14742
- const userMessage = userParts.join(`
14743
- `);
14832
+ const userMessage = buildScenarioUserMessage(scenario, baseUrl);
14744
14833
  const screenshots = [];
14745
14834
  let tokensUsed = 0;
14746
14835
  let stepNumber = 0;
@@ -14803,7 +14892,7 @@ Instructions: ${persona.instructions}` : "",
14803
14892
  if (onStep) {
14804
14893
  onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
14805
14894
  }
14806
- const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber, sessionId, a11y });
14895
+ const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber, sessionId: sessionId ?? runId, a11y });
14807
14896
  if (onStep) {
14808
14897
  onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
14809
14898
  }
@@ -17494,6 +17583,381 @@ var init_failure_pipeline = __esm(() => {
17494
17583
  init_todos_connector();
17495
17584
  });
17496
17585
 
17586
+ // src/lib/a11y-audit.ts
17587
+ async function runA11yAudit(page, options = {}) {
17588
+ const { level = "AA", rules, exclude = [] } = options;
17589
+ await page.addScriptTag({ url: "https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.9.1/axe.min.js" });
17590
+ const config = {
17591
+ runOnly: {
17592
+ type: level === "AAA" ? "standard" : "tag",
17593
+ values: level === "AAA" ? undefined : [level, "best-practice"]
17594
+ }
17595
+ };
17596
+ if (rules && rules.length > 0) {
17597
+ config.rules = Object.fromEntries(rules.map((r) => [r, { enabled: true }]));
17598
+ }
17599
+ if (exclude.length > 0) {
17600
+ config.exclude = exclude;
17601
+ }
17602
+ const result = await page.evaluate(async (auditConfig) => {
17603
+ const axeResult = await window.axe.run(auditConfig);
17604
+ return axeResult;
17605
+ }, config);
17606
+ const violations = (result.violations ?? []).map((v) => ({
17607
+ id: v.id,
17608
+ impact: v.impact,
17609
+ description: v.description,
17610
+ help: v.help,
17611
+ helpUrl: v.helpUrl,
17612
+ nodes: (v.nodes ?? []).map((n) => ({
17613
+ html: n.html,
17614
+ target: n.target,
17615
+ failureSummary: n.failureSummary
17616
+ }))
17617
+ }));
17618
+ const passes = (result.passes ?? []).map((p) => ({
17619
+ id: p.id,
17620
+ description: p.description
17621
+ }));
17622
+ const incomplete = (result.incomplete ?? []).map((i) => ({
17623
+ id: i.id,
17624
+ description: i.description,
17625
+ impact: i.impact
17626
+ }));
17627
+ const criticalCount = violations.filter((v) => v.impact === "critical").length;
17628
+ const seriousCount = violations.filter((v) => v.impact === "serious").length;
17629
+ const moderateCount = violations.filter((v) => v.impact === "moderate").length;
17630
+ const minorCount = violations.filter((v) => v.impact === "minor").length;
17631
+ return {
17632
+ violations,
17633
+ passes,
17634
+ incomplete,
17635
+ url: page.url(),
17636
+ timestamp: new Date().toISOString(),
17637
+ totalViolations: violations.length,
17638
+ criticalCount,
17639
+ seriousCount,
17640
+ moderateCount,
17641
+ minorCount
17642
+ };
17643
+ }
17644
+
17645
+ // src/lib/assertions.ts
17646
+ async function evaluateAssertions(page, assertions, context = {}) {
17647
+ const results = [];
17648
+ for (const assertion of assertions) {
17649
+ try {
17650
+ const result = await evaluateOne(page, assertion, context);
17651
+ results.push(result);
17652
+ } catch (err) {
17653
+ results.push({
17654
+ assertion,
17655
+ passed: false,
17656
+ actual: "",
17657
+ error: err instanceof Error ? err.message : String(err)
17658
+ });
17659
+ }
17660
+ }
17661
+ return results;
17662
+ }
17663
+ async function evaluateOne(page, assertion, context) {
17664
+ switch (assertion.type) {
17665
+ case "visible": {
17666
+ const visible = await page.locator(assertion.selector).isVisible();
17667
+ return {
17668
+ assertion,
17669
+ passed: visible,
17670
+ actual: String(visible)
17671
+ };
17672
+ }
17673
+ case "not_visible": {
17674
+ const visible = await page.locator(assertion.selector).isVisible();
17675
+ return {
17676
+ assertion,
17677
+ passed: !visible,
17678
+ actual: String(visible)
17679
+ };
17680
+ }
17681
+ case "text_contains": {
17682
+ const text = await page.locator(assertion.selector).textContent() ?? "";
17683
+ const expected = String(assertion.expected ?? "");
17684
+ return {
17685
+ assertion,
17686
+ passed: text.includes(expected),
17687
+ actual: text
17688
+ };
17689
+ }
17690
+ case "text_equals": {
17691
+ const text = await page.locator(assertion.selector).textContent() ?? "";
17692
+ const expected = String(assertion.expected ?? "");
17693
+ return {
17694
+ assertion,
17695
+ passed: text.trim() === expected.trim(),
17696
+ actual: text
17697
+ };
17698
+ }
17699
+ case "element_count": {
17700
+ const count = await page.locator(assertion.selector).count();
17701
+ const expected = Number(assertion.expected ?? 0);
17702
+ return {
17703
+ assertion,
17704
+ passed: count === expected,
17705
+ actual: String(count)
17706
+ };
17707
+ }
17708
+ case "no_console_errors": {
17709
+ if (context.consoleErrors !== undefined) {
17710
+ const errors = context.consoleErrors.filter(Boolean);
17711
+ return {
17712
+ assertion,
17713
+ passed: errors.length === 0,
17714
+ actual: errors.length === 0 ? "No console errors captured" : errors.slice(0, 3).join(" | ")
17715
+ };
17716
+ }
17717
+ const errorElements = await page.locator('[role="alert"], .error, .error-message, [data-testid="error"]').count();
17718
+ return {
17719
+ assertion,
17720
+ passed: errorElements === 0,
17721
+ actual: `${errorElements} error element(s) found`
17722
+ };
17723
+ }
17724
+ case "no_a11y_violations": {
17725
+ try {
17726
+ const auditResult = await runA11yAudit(page);
17727
+ const hasIssues = auditResult.violations.length > 0;
17728
+ return {
17729
+ assertion,
17730
+ passed: !hasIssues,
17731
+ actual: hasIssues ? `${auditResult.totalViolations} violation(s): ${auditResult.violations.map((v) => v.id).join(", ")}` : "No accessibility violations found"
17732
+ };
17733
+ } catch (err) {
17734
+ return {
17735
+ assertion,
17736
+ passed: false,
17737
+ actual: "",
17738
+ error: err instanceof Error ? err.message : String(err)
17739
+ };
17740
+ }
17741
+ }
17742
+ case "url_contains": {
17743
+ const url = page.url();
17744
+ const expected = String(assertion.expected ?? "");
17745
+ return {
17746
+ assertion,
17747
+ passed: url.includes(expected),
17748
+ actual: url
17749
+ };
17750
+ }
17751
+ case "title_contains": {
17752
+ const title = await page.title();
17753
+ const expected = String(assertion.expected ?? "");
17754
+ return {
17755
+ assertion,
17756
+ passed: title.includes(expected),
17757
+ actual: title
17758
+ };
17759
+ }
17760
+ case "cookie_exists": {
17761
+ const cookieName = assertion.expected;
17762
+ const cookies = await page.context().cookies();
17763
+ const found = cookies.some((c) => c.name === cookieName);
17764
+ return {
17765
+ assertion,
17766
+ passed: found,
17767
+ actual: found ? `Cookie "${cookieName}" exists` : `Cookie "${cookieName}" not found`
17768
+ };
17769
+ }
17770
+ case "cookie_not_exists": {
17771
+ const cookieName = assertion.expected;
17772
+ const cookies = await page.context().cookies();
17773
+ const found = cookies.some((c) => c.name === cookieName);
17774
+ return {
17775
+ assertion,
17776
+ passed: !found,
17777
+ actual: found ? `Cookie "${cookieName}" found (unexpected)` : `Cookie "${cookieName}" does not exist`
17778
+ };
17779
+ }
17780
+ case "cookie_value": {
17781
+ const [cookieName, expectedValue] = assertion.expected.split("=", 2);
17782
+ const cookies = await page.context().cookies();
17783
+ const cookie = cookies.find((c) => c.name === cookieName);
17784
+ const actualValue = cookie?.value ?? "";
17785
+ return {
17786
+ assertion,
17787
+ passed: actualValue === expectedValue,
17788
+ actual: cookie ? `${cookieName}=${actualValue}` : `Cookie "${cookieName}" not found`
17789
+ };
17790
+ }
17791
+ case "local_storage_exists": {
17792
+ const key = assertion.expected;
17793
+ const value = await page.evaluate((k) => localStorage.getItem(k), key);
17794
+ return {
17795
+ assertion,
17796
+ passed: value !== null,
17797
+ actual: value !== null ? `Key "${key}" exists with value "${value}"` : `Key "${key}" not found in localStorage`
17798
+ };
17799
+ }
17800
+ case "local_storage_not_exists": {
17801
+ const key = assertion.expected;
17802
+ const value = await page.evaluate((k) => localStorage.getItem(k), key);
17803
+ return {
17804
+ assertion,
17805
+ passed: value === null,
17806
+ actual: value !== null ? `Key "${key}" exists (unexpected)` : `Key "${key}" does not exist in localStorage`
17807
+ };
17808
+ }
17809
+ case "local_storage_value": {
17810
+ const [lsKey, expectedValue] = assertion.expected.split("=", 2);
17811
+ const value = await page.evaluate((k) => localStorage.getItem(k), lsKey ?? "");
17812
+ return {
17813
+ assertion,
17814
+ passed: value === expectedValue,
17815
+ actual: value !== null ? `${lsKey}=${value}` : `Key "${lsKey}" not found in localStorage`
17816
+ };
17817
+ }
17818
+ case "session_storage_value": {
17819
+ const [ssKey, expectedValue] = assertion.expected.split("=", 2);
17820
+ const value = await page.evaluate((k) => sessionStorage.getItem(k), ssKey ?? "");
17821
+ return {
17822
+ assertion,
17823
+ passed: value === expectedValue,
17824
+ actual: value !== null ? `${ssKey}=${value}` : `Key "${ssKey}" not found in sessionStorage`
17825
+ };
17826
+ }
17827
+ case "session_storage_not_exists": {
17828
+ const key = assertion.expected;
17829
+ const value = await page.evaluate((k) => sessionStorage.getItem(k), key);
17830
+ return {
17831
+ assertion,
17832
+ passed: value === null,
17833
+ actual: value !== null ? `Key "${key}" exists (unexpected)` : `Key "${key}" does not exist in sessionStorage`
17834
+ };
17835
+ }
17836
+ default: {
17837
+ return {
17838
+ assertion,
17839
+ passed: false,
17840
+ actual: "",
17841
+ error: `Unknown assertion type: ${assertion.type}`
17842
+ };
17843
+ }
17844
+ }
17845
+ }
17846
+ function parseAssertionString(str) {
17847
+ const trimmed = str.trim();
17848
+ if (trimmed === "no-console-errors") {
17849
+ return { type: "no_console_errors", description: "No console errors" };
17850
+ }
17851
+ if (trimmed.startsWith("url:contains:")) {
17852
+ const expected = trimmed.slice("url:contains:".length);
17853
+ return { type: "url_contains", expected, description: `URL contains "${expected}"` };
17854
+ }
17855
+ if (trimmed.startsWith("title:contains:")) {
17856
+ const expected = trimmed.slice("title:contains:".length);
17857
+ return { type: "title_contains", expected, description: `Title contains "${expected}"` };
17858
+ }
17859
+ if (trimmed.startsWith("count:")) {
17860
+ const rest = trimmed.slice("count:".length);
17861
+ const eqIdx = rest.indexOf(" eq:");
17862
+ if (eqIdx === -1) {
17863
+ throw new Error(`Invalid count assertion format: ${str}. Expected "count:<selector> eq:<number>"`);
17864
+ }
17865
+ const selector = rest.slice(0, eqIdx);
17866
+ const expected = parseInt(rest.slice(eqIdx + " eq:".length), 10);
17867
+ return { type: "element_count", selector, expected, description: `${selector} count equals ${expected}` };
17868
+ }
17869
+ if (trimmed.startsWith("text:")) {
17870
+ const rest = trimmed.slice("text:".length);
17871
+ const containsIdx = rest.indexOf(" contains:");
17872
+ const equalsIdx = rest.indexOf(" equals:");
17873
+ if (containsIdx !== -1) {
17874
+ const selector = rest.slice(0, containsIdx);
17875
+ const expected = rest.slice(containsIdx + " contains:".length);
17876
+ return { type: "text_contains", selector, expected, description: `${selector} text contains "${expected}"` };
17877
+ }
17878
+ if (equalsIdx !== -1) {
17879
+ const selector = rest.slice(0, equalsIdx);
17880
+ const expected = rest.slice(equalsIdx + " equals:".length);
17881
+ return { type: "text_equals", selector, expected, description: `${selector} text equals "${expected}"` };
17882
+ }
17883
+ throw new Error(`Invalid text assertion format: ${str}. Expected "text:<selector> contains:<text>" or "text:<selector> equals:<text>"`);
17884
+ }
17885
+ if (trimmed.startsWith("selector:")) {
17886
+ const rest = trimmed.slice("selector:".length);
17887
+ const lastSpace = rest.lastIndexOf(" ");
17888
+ if (lastSpace === -1) {
17889
+ throw new Error(`Invalid selector assertion format: ${str}. Expected "selector:<selector> visible" or "selector:<selector> not-visible"`);
17890
+ }
17891
+ const selector = rest.slice(0, lastSpace);
17892
+ const action = rest.slice(lastSpace + 1);
17893
+ if (action === "visible") {
17894
+ return { type: "visible", selector, description: `${selector} is visible` };
17895
+ }
17896
+ if (action === "not-visible") {
17897
+ return { type: "not_visible", selector, description: `${selector} is not visible` };
17898
+ }
17899
+ throw new Error(`Unknown selector action: "${action}". Expected "visible" or "not-visible"`);
17900
+ }
17901
+ if (trimmed.startsWith("cookie:exists:")) {
17902
+ const name = trimmed.slice("cookie:exists:".length);
17903
+ return { type: "cookie_exists", expected: name, description: `Cookie "${name}" exists` };
17904
+ }
17905
+ if (trimmed.startsWith("cookie:not-exists:")) {
17906
+ const name = trimmed.slice("cookie:not-exists:".length);
17907
+ return { type: "cookie_not_exists", expected: name, description: `Cookie "${name}" does not exist` };
17908
+ }
17909
+ if (trimmed.startsWith("cookie:value:")) {
17910
+ const valueStr = trimmed.slice("cookie:value:".length);
17911
+ return { type: "cookie_value", expected: valueStr, description: `Cookie value is "${valueStr}"` };
17912
+ }
17913
+ if (trimmed.startsWith("local:exists:")) {
17914
+ const key = trimmed.slice("local:exists:".length);
17915
+ return { type: "local_storage_exists", expected: key, description: `LocalStorage key "${key}" exists` };
17916
+ }
17917
+ if (trimmed.startsWith("local:not-exists:")) {
17918
+ const key = trimmed.slice("local:not-exists:".length);
17919
+ return { type: "local_storage_not_exists", expected: key, description: `LocalStorage key "${key}" does not exist` };
17920
+ }
17921
+ if (trimmed.startsWith("local:value:")) {
17922
+ const valueStr = trimmed.slice("local:value:".length);
17923
+ return { type: "local_storage_value", expected: valueStr, description: `LocalStorage value is "${valueStr}"` };
17924
+ }
17925
+ if (trimmed.startsWith("session:value:")) {
17926
+ const valueStr = trimmed.slice("session:value:".length);
17927
+ return { type: "session_storage_value", expected: valueStr, description: `SessionStorage value is "${valueStr}"` };
17928
+ }
17929
+ if (trimmed.startsWith("session:not-exists:")) {
17930
+ const key = trimmed.slice("session:not-exists:".length);
17931
+ return { type: "session_storage_not_exists", expected: key, description: `SessionStorage key "${key}" does not exist` };
17932
+ }
17933
+ throw new Error(`Cannot parse assertion: "${str}". See --help for assertion formats.`);
17934
+ }
17935
+ function allAssertionsPassed(results) {
17936
+ return results.every((r) => r.passed);
17937
+ }
17938
+ function formatAssertionResults(results) {
17939
+ if (results.length === 0)
17940
+ return "No assertions.";
17941
+ const lines = [];
17942
+ for (const r of results) {
17943
+ const icon = r.passed ? "PASS" : "FAIL";
17944
+ const desc = r.assertion.description || `${r.assertion.type}${r.assertion.selector ? ` ${r.assertion.selector}` : ""}`;
17945
+ let line = ` [${icon}] ${desc}`;
17946
+ if (!r.passed) {
17947
+ line += ` (actual: ${r.actual})`;
17948
+ if (r.error)
17949
+ line += ` \u2014 ${r.error}`;
17950
+ }
17951
+ lines.push(line);
17952
+ }
17953
+ const passed = results.filter((r) => r.passed).length;
17954
+ lines.push(`
17955
+ ${passed}/${results.length} assertions passed.`);
17956
+ return lines.join(`
17957
+ `);
17958
+ }
17959
+ var init_assertions = () => {};
17960
+
17497
17961
  // src/db/flows.ts
17498
17962
  var exports_flows = {};
17499
17963
  __export(exports_flows, {
@@ -17652,7 +18116,9 @@ __export(exports_runner, {
17652
18116
  runSingleScenario: () => runSingleScenario,
17653
18117
  runByFilter: () => runByFilter,
17654
18118
  runBatch: () => runBatch,
17655
- onRunEvent: () => onRunEvent
18119
+ resolveScenariosForRun: () => resolveScenariosForRun,
18120
+ onRunEvent: () => onRunEvent,
18121
+ applyStructuredAssertionsToResult: () => applyStructuredAssertionsToResult
17656
18122
  });
17657
18123
  import { mkdirSync as mkdirSync8 } from "fs";
17658
18124
  import { join as join13 } from "path";
@@ -17664,6 +18130,54 @@ function emit(event) {
17664
18130
  if (eventHandler)
17665
18131
  eventHandler(event);
17666
18132
  }
18133
+ function assertionDescription(result) {
18134
+ return result.assertion.description || `${result.assertion.type}${result.assertion.selector ? ` ${result.assertion.selector}` : ""}`;
18135
+ }
18136
+ function summarizeAssertionResult(result) {
18137
+ const description = assertionDescription(result);
18138
+ if (result.passed)
18139
+ return description;
18140
+ const suffix = result.error ? `; ${result.error}` : "";
18141
+ return `${description} (actual: ${result.actual}${suffix})`;
18142
+ }
18143
+ async function applyStructuredAssertionsToResult(input) {
18144
+ const assertions = input.scenario.assertions ?? [];
18145
+ if (assertions.length === 0) {
18146
+ return {
18147
+ status: input.status,
18148
+ reasoning: input.reasoning,
18149
+ assertionsPassed: [],
18150
+ assertionsFailed: [],
18151
+ assertionResults: []
18152
+ };
18153
+ }
18154
+ const results = await evaluateAssertions(input.page, assertions, {
18155
+ consoleErrors: input.consoleErrors
18156
+ });
18157
+ const assertionsPassed = results.filter((r) => r.passed).map(summarizeAssertionResult);
18158
+ const assertionsFailed = results.filter((r) => !r.passed).map(summarizeAssertionResult);
18159
+ const assertionResults = results.map((result) => ({
18160
+ type: result.assertion.type,
18161
+ description: assertionDescription(result),
18162
+ passed: result.passed,
18163
+ actual: result.actual,
18164
+ ...result.error ? { error: result.error } : {}
18165
+ }));
18166
+ const assertionsOk = allAssertionsPassed(results);
18167
+ const status = assertionsOk || input.status !== "passed" ? input.status : "failed";
18168
+ const assertionHeading = assertionsOk ? "Structured assertions passed:" : "Structured assertions failed:";
18169
+ const reasoningParts = [input.reasoning, `${assertionHeading}
18170
+ ${formatAssertionResults(results)}`].map((part) => part.trim()).filter(Boolean);
18171
+ return {
18172
+ status,
18173
+ reasoning: reasoningParts.join(`
18174
+
18175
+ `),
18176
+ assertionsPassed,
18177
+ assertionsFailed,
18178
+ assertionResults
18179
+ };
18180
+ }
17667
18181
  function withTimeout(promise, ms, label) {
17668
18182
  return new Promise((resolve, reject) => {
17669
18183
  const warningAt = Math.floor(ms * 0.8);
@@ -17834,6 +18348,7 @@ async function runSingleScenario(scenario, runId, options) {
17834
18348
  model,
17835
18349
  runId,
17836
18350
  sessionId: result.id,
18351
+ baseUrl: options.url,
17837
18352
  maxTurns: effectiveOptions.minimal ? 10 : 30,
17838
18353
  a11y: effectiveOptions.a11y,
17839
18354
  persona: persona ? {
@@ -17916,27 +18431,46 @@ async function runSingleScenario(scenario, runId, options) {
17916
18431
  closeSession(result.id);
17917
18432
  const lightpandaNote = options.engine === "lightpanda" ? " (Running with Lightpanda \u2014 no screenshots)" : options.engine === "bun" ? " (Running with Bun.WebView \u2014 native, ~11x faster)" : "";
17918
18433
  const networkMeta = networkErrors.length > 0 ? { networkErrors: networkErrors.slice(0, 20) } : {};
17919
- let updatedResult = updateResult(result.id, {
18434
+ const baseReasoning = agentResult.reasoning ? agentResult.reasoning + lightpandaNote : lightpandaNote || "";
18435
+ const assertionOutcome = await applyStructuredAssertionsToResult({
18436
+ page,
18437
+ scenario,
18438
+ consoleErrors,
17920
18439
  status: agentResult.status,
17921
- reasoning: agentResult.reasoning ? agentResult.reasoning + lightpandaNote : lightpandaNote || undefined,
18440
+ reasoning: baseReasoning
18441
+ });
18442
+ const structuredAssertionMeta = assertionOutcome.assertionResults.length > 0 ? {
18443
+ structuredAssertions: {
18444
+ passed: assertionOutcome.assertionsPassed,
18445
+ failed: assertionOutcome.assertionsFailed,
18446
+ results: assertionOutcome.assertionResults
18447
+ }
18448
+ } : {};
18449
+ let updatedResult = updateResult(result.id, {
18450
+ status: assertionOutcome.status,
18451
+ reasoning: assertionOutcome.reasoning || undefined,
17922
18452
  stepsCompleted: agentResult.stepsCompleted,
17923
18453
  durationMs: Date.now() - new Date(result.createdAt).getTime(),
17924
18454
  tokensUsed: agentResult.tokensUsed,
17925
18455
  costCents: estimateCost(model, agentResult.tokensUsed),
17926
- metadata: { consoleLogs, ...networkErrors.length > 0 ? networkMeta : {} }
18456
+ metadata: {
18457
+ consoleLogs,
18458
+ ...networkErrors.length > 0 ? networkMeta : {},
18459
+ ...structuredAssertionMeta
18460
+ }
17927
18461
  });
17928
- if (agentResult.status === "failed" || agentResult.status === "error") {
17929
- const failureAnalysis = analyzeFailure(null, agentResult.reasoning ?? null);
18462
+ if (assertionOutcome.status === "failed" || assertionOutcome.status === "error") {
18463
+ const failureAnalysis = analyzeFailure(null, assertionOutcome.reasoning ?? null);
17930
18464
  if (failureAnalysis) {
17931
18465
  updatedResult = updateResult(result.id, { failureAnalysis });
17932
18466
  }
17933
18467
  }
17934
- if (agentResult.status === "passed") {
18468
+ if (assertionOutcome.status === "passed") {
17935
18469
  try {
17936
18470
  updateScenarioPassedCache(scenario.id, options.url);
17937
18471
  } catch {}
17938
18472
  }
17939
- const eventType = agentResult.status === "passed" ? "scenario:pass" : "scenario:fail";
18473
+ const eventType = assertionOutcome.status === "passed" ? "scenario:pass" : "scenario:fail";
17940
18474
  emit({ type: eventType, scenarioId: scenario.id, scenarioName: scenario.name, resultId: result.id, runId });
17941
18475
  return updatedResult;
17942
18476
  } catch (error) {
@@ -17961,7 +18495,8 @@ async function runSingleScenario(scenario, runId, options) {
17961
18495
  } finally {
17962
18496
  if (harPath) {
17963
18497
  try {
17964
- updateResult(result.id, { metadata: { harPath } });
18498
+ const existing = getResult(result.id);
18499
+ updateResult(result.id, { metadata: { ...existing?.metadata ?? {}, harPath } });
17965
18500
  } catch {}
17966
18501
  }
17967
18502
  if (browser) {
@@ -18133,22 +18668,31 @@ async function runBatch(scenarios, options) {
18133
18668
  }
18134
18669
  return { run: finalRun, results };
18135
18670
  }
18136
- async function runByFilter(options) {
18137
- let scenarios;
18671
+ function findScenarioInList(scenarios, id) {
18672
+ return scenarios.find((scenario) => scenario.id === id || scenario.shortId === id || scenario.id.startsWith(id)) ?? null;
18673
+ }
18674
+ function resolveScenariosForRun(options) {
18138
18675
  if (options.scenarioIds && options.scenarioIds.length > 0) {
18139
- const all = listScenarios({ projectId: options.projectId });
18140
- scenarios = all.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
18141
- if (scenarios.length === 0 && options.projectId) {
18142
- const global2 = listScenarios({});
18143
- scenarios = global2.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
18676
+ const scoped = listScenarios({ projectId: options.projectId });
18677
+ const resolved = [];
18678
+ const seen = new Set;
18679
+ for (const id of options.scenarioIds) {
18680
+ const scenario = findScenarioInList(scoped, id) ?? getScenario(id);
18681
+ if (scenario && !seen.has(scenario.id)) {
18682
+ resolved.push(scenario);
18683
+ seen.add(scenario.id);
18684
+ }
18144
18685
  }
18145
- } else {
18146
- scenarios = listScenarios({
18147
- projectId: options.projectId,
18148
- tags: options.tags,
18149
- priority: options.priority
18150
- });
18686
+ return resolved;
18151
18687
  }
18688
+ return listScenarios({
18689
+ projectId: options.projectId,
18690
+ tags: options.tags,
18691
+ priority: options.priority
18692
+ });
18693
+ }
18694
+ async function runByFilter(options) {
18695
+ const scenarios = resolveScenariosForRun(options);
18152
18696
  if (scenarios.length === 0) {
18153
18697
  const config = loadConfig();
18154
18698
  const model = resolveModel(options.model ?? config.defaultModel);
@@ -18161,17 +18705,7 @@ async function runByFilter(options) {
18161
18705
  function startRunAsync(options) {
18162
18706
  const config = loadConfig();
18163
18707
  const model = resolveModel(options.model ?? config.defaultModel);
18164
- let scenarios;
18165
- if (options.scenarioIds && options.scenarioIds.length > 0) {
18166
- const all = listScenarios({ projectId: options.projectId });
18167
- scenarios = all.filter((s) => options.scenarioIds.includes(s.id) || options.scenarioIds.includes(s.shortId));
18168
- } else {
18169
- scenarios = listScenarios({
18170
- projectId: options.projectId,
18171
- tags: options.tags,
18172
- priority: options.priority
18173
- });
18174
- }
18708
+ const scenarios = resolveScenariosForRun(options);
18175
18709
  if (!options.skipBudgetCheck) {
18176
18710
  const cap = options.maxCostCents ?? config.defaultMaxCostCents;
18177
18711
  if (cap !== undefined && cap > 0 && scenarios.length > 0) {
@@ -18275,6 +18809,7 @@ var init_runner = __esm(() => {
18275
18809
  init_session_tracker();
18276
18810
  init_webhooks();
18277
18811
  init_failure_pipeline();
18812
+ init_assertions();
18278
18813
  });
18279
18814
 
18280
18815
  // src/lib/reporter.ts
@@ -19778,7 +20313,6 @@ var require_range = __commonJS((exports, module) => {
19778
20313
  return this.range;
19779
20314
  }
19780
20315
  parseRange(range) {
19781
- range = range.replace(BUILDSTRIPRE, "");
19782
20316
  const memoOpts = (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | (this.options.loose && FLAG_LOOSE);
19783
20317
  const memoKey = memoOpts + ":" + range;
19784
20318
  const cached = cache.get(memoKey);
@@ -19860,14 +20394,12 @@ var require_range = __commonJS((exports, module) => {
19860
20394
  var SemVer = require_semver();
19861
20395
  var {
19862
20396
  safeRe: re,
19863
- src,
19864
20397
  t,
19865
20398
  comparatorTrimReplace,
19866
20399
  tildeTrimReplace,
19867
20400
  caretTrimReplace
19868
20401
  } = require_re();
19869
20402
  var { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require_constants();
19870
- var BUILDSTRIPRE = new RegExp(src[t.BUILD], "g");
19871
20403
  var isNullSet = (c) => c.value === "<0.0.0-0";
19872
20404
  var isAny = (c) => c.value === "";
19873
20405
  var isSatisfiable = (comparators, options) => {
@@ -25417,18 +25949,7 @@ function normalizeFilter(input) {
25417
25949
  };
25418
25950
  }
25419
25951
  function normalizeExecution(input) {
25420
- const target = input?.target ?? "local";
25421
- if (target === "connector:e2b") {
25422
- return {
25423
- target,
25424
- connector: input?.connector ?? "e2b",
25425
- operation: input?.operation ?? "run",
25426
- sandboxTemplate: input?.sandboxTemplate,
25427
- timeoutMs: input?.timeoutMs,
25428
- env: input?.env
25429
- };
25430
- }
25431
- return { ...DEFAULT_EXECUTION, timeoutMs: input?.timeoutMs };
25952
+ return input ? workflowExecutionFromValue(input) : DEFAULT_EXECUTION;
25432
25953
  }
25433
25954
  function createTestingWorkflow(input) {
25434
25955
  const db2 = getDatabase();
@@ -26023,54 +26544,52 @@ import { promises as fsPromises } from "fs";
26023
26544
  import { createHash as createHash2, createPrivateKey, createPublicKey, sign } from "crypto";
26024
26545
  import { promises as fs2 } from "fs";
26025
26546
  import { homedir as homedir22 } from "os";
26026
- import { dirname as dirname32, join as join62 } from "path";
26547
+ import { dirname as dirname32, join as join52 } from "path";
26027
26548
  import { exec } from "child_process";
26028
26549
  import { promisify } from "util";
26029
- import { readFileSync as readFileSync42 } from "fs";
26550
+ import { readFileSync as readFileSync32 } from "fs";
26030
26551
  import { webcrypto as crypto2 } from "crypto";
26031
- import { existsSync as existsSync5, writeFileSync as writeFileSync42, readFileSync as readFileSync32, mkdirSync as mkdirSync32 } from "fs";
26032
- import { join as join52 } from "path";
26552
+ import { existsSync as existsSync42, writeFileSync as writeFileSync32, readFileSync as readFileSync22, mkdirSync as mkdirSync32 } from "fs";
26553
+ import { join as join42 } from "path";
26033
26554
  import { Database as Database4 } from "bun:sqlite";
26034
26555
  import { existsSync as existsSync16, mkdirSync as mkdirSync13 } from "fs";
26035
- import { dirname as dirname4, join as join18, resolve as resolve3 } from "path";
26036
- import { existsSync as existsSync22, writeFileSync as writeFileSync6 } from "fs";
26556
+ import { dirname as dirname4, join as join19, resolve as resolve2 } from "path";
26557
+ import { existsSync as existsSync22, writeFileSync as writeFileSync7 } from "fs";
26037
26558
  import { join as join22 } from "path";
26038
- import { execFileSync } from "child_process";
26039
- import { writeFileSync as writeFileSync22, existsSync as existsSync32, readFileSync as readFileSync8 } from "fs";
26040
- import { join as join32 } from "path";
26041
- import { hostname } from "os";
26042
- import { existsSync as existsSync42, readFileSync as readFileSync22, writeFileSync as writeFileSync32, mkdirSync as mkdirSync22 } from "fs";
26559
+ import { execSync as execSync3, execFileSync } from "child_process";
26560
+ import { existsSync as existsSync32, readFileSync as readFileSync7, writeFileSync as writeFileSync22, mkdirSync as mkdirSync22 } from "fs";
26043
26561
  import { homedir as homedir11 } from "os";
26044
- import { join as join42, dirname as dirname22 } from "path";
26562
+ import { join as join32, dirname as dirname22 } from "path";
26563
+ import { hostname } from "os";
26045
26564
  import { Buffer as Buffer2 } from "buffer";
26046
26565
  import * as zlib from "zlib";
26047
26566
  import { Readable } from "stream";
26048
26567
  import { Writable } from "stream";
26049
26568
  import { createHash as createHash22 } from "crypto";
26050
- import { mkdirSync as mkdirSync4, statSync as statSync2, writeFileSync as writeFileSync52 } from "fs";
26051
- import { dirname as dirname42, join as join72, relative as relative3 } from "path";
26569
+ import { mkdirSync as mkdirSync4, statSync as statSync2, writeFileSync as writeFileSync42 } from "fs";
26570
+ import { dirname as dirname42, join as join62, relative as relative3 } from "path";
26052
26571
  import { readdir, readFile as readFile2 } from "fs/promises";
26053
- import { existsSync as existsSync72, readdirSync as readdirSync5, readFileSync as readFileSync52, statSync as statSync22 } from "fs";
26054
- import { basename as basename2, join as join82, resolve as resolve22 } from "path";
26572
+ import { existsSync as existsSync62, readdirSync as readdirSync5, readFileSync as readFileSync42, statSync as statSync22 } from "fs";
26573
+ import { basename as basename2, join as join72, resolve as resolve22 } from "path";
26055
26574
  import { execFileSync as execFileSync2 } from "child_process";
26056
- import { existsSync as existsSync82, readFileSync as readFileSync62, writeFileSync as writeFileSync62, mkdirSync as mkdirSync52 } from "fs";
26057
- import { dirname as dirname5, join as join92 } from "path";
26575
+ import { existsSync as existsSync72, readFileSync as readFileSync52, writeFileSync as writeFileSync52, mkdirSync as mkdirSync52 } from "fs";
26576
+ import { dirname as dirname5, join as join82 } from "path";
26058
26577
  import { execFileSync as execFileSync4 } from "child_process";
26059
- import { existsSync as existsSync112 } from "fs";
26060
- import { join as join122 } from "path";
26061
- import { execFileSync as execFileSync3 } from "child_process";
26062
- import { existsSync as existsSync92 } from "fs";
26063
- import { homedir as homedir32, hostname as hostname2, platform as platform2, type } from "os";
26064
- import { join as join102 } from "path";
26065
26578
  import { existsSync as existsSync102 } from "fs";
26066
- import { execSync as execSync3 } from "child_process";
26067
26579
  import { join as join112 } from "path";
26580
+ import { execFileSync as execFileSync3 } from "child_process";
26581
+ import { existsSync as existsSync82 } from "fs";
26582
+ import { homedir as homedir32, hostname as hostname2, platform as platform2, type } from "os";
26583
+ import { join as join92 } from "path";
26584
+ import { existsSync as existsSync92 } from "fs";
26068
26585
  import { execSync as execSync22 } from "child_process";
26069
- import { readFileSync as readFileSync72 } from "fs";
26586
+ import { join as join102 } from "path";
26587
+ import { execSync as execSync32 } from "child_process";
26588
+ import { readFileSync as readFileSync62 } from "fs";
26070
26589
  import { mkdirSync as mkdirSync62 } from "fs";
26071
- import { dirname as dirname6, join as join132 } from "path";
26590
+ import { dirname as dirname6, join as join122 } from "path";
26072
26591
  import { fileURLToPath } from "url";
26073
- import { existsSync as existsSync122 } from "fs";
26592
+ import { existsSync as existsSync112 } from "fs";
26074
26593
  function __accessProp3(key) {
26075
26594
  return this[key];
26076
26595
  }
@@ -26209,12 +26728,12 @@ function getDbPath2() {
26209
26728
  return process.env["PROJECTS_DB_PATH"];
26210
26729
  }
26211
26730
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
26212
- return join18(home, ".hasna", "projects", "projects.db");
26731
+ return join19(home, ".hasna", "projects", "projects.db");
26213
26732
  }
26214
26733
  function ensureDir2(filePath) {
26215
26734
  if (filePath === ":memory:")
26216
26735
  return;
26217
- const dir = dirname4(resolve3(filePath));
26736
+ const dir = dirname4(resolve2(filePath));
26218
26737
  if (!existsSync16(dir)) {
26219
26738
  mkdirSync13(dir, { recursive: true });
26220
26739
  }
@@ -26247,25 +26766,14 @@ function uuid2() {
26247
26766
  function isGitRepo(path) {
26248
26767
  return existsSync22(join22(path, ".git"));
26249
26768
  }
26250
- function getCurrentBranch(path) {
26251
- return execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
26252
- cwd: path,
26253
- stdio: "pipe",
26254
- encoding: "utf-8",
26255
- env: process.env
26256
- }).trim();
26257
- }
26258
- function bootstrapPathsToStage(path) {
26259
- return BOOTSTRAP_PATHS.filter((entry) => existsSync22(join22(path, entry)));
26260
- }
26261
26769
  function gitInit(project) {
26262
26770
  const { path, name, id, slug } = project;
26263
26771
  if (isGitRepo(path))
26264
26772
  return;
26265
- execFileSync("git", ["init", "-b", "main"], { cwd: path, stdio: "pipe", env: process.env });
26773
+ execSync3("git init", { cwd: path, stdio: "pipe" });
26266
26774
  const gitignorePath = join22(path, ".gitignore");
26267
26775
  if (!existsSync22(gitignorePath)) {
26268
- writeFileSync6(gitignorePath, GITIGNORE_TEMPLATE, "utf-8");
26776
+ writeFileSync7(gitignorePath, GITIGNORE_TEMPLATE, "utf-8");
26269
26777
  }
26270
26778
  const projectJson = {
26271
26779
  id,
@@ -26274,18 +26782,26 @@ function gitInit(project) {
26274
26782
  created_at: project.created_at,
26275
26783
  integrations: project.integrations ?? {}
26276
26784
  };
26277
- writeFileSync6(join22(path, ".project.json"), JSON.stringify(projectJson, null, 2) + `
26785
+ writeFileSync7(join22(path, ".project.json"), JSON.stringify(projectJson, null, 2) + `
26278
26786
  `, "utf-8");
26279
- const staged = bootstrapPathsToStage(path);
26280
- if (staged.length === 0)
26281
- return;
26282
- execFileSync("git", ["add", ...staged], { cwd: path, stdio: "pipe", env: process.env });
26283
- execFileSync("git", ["commit", "-m", `chore: init project ${name}`], {
26787
+ execSync3("git add .gitignore .project.json", { cwd: path, stdio: "pipe" });
26788
+ execSync3(`git commit -m "chore: init project ${name}"`, {
26284
26789
  cwd: path,
26285
26790
  stdio: "pipe",
26286
26791
  env: { ...process.env, GIT_AUTHOR_NAME: "open-projects", GIT_COMMITTER_NAME: "open-projects" }
26287
26792
  });
26288
26793
  }
26794
+ function getConfig() {
26795
+ if (existsSync32(CONFIG_PATH3)) {
26796
+ try {
26797
+ const user = JSON.parse(readFileSync7(CONFIG_PATH3, "utf-8"));
26798
+ return { ...DEFAULTS, ...user };
26799
+ } catch {
26800
+ return { ...DEFAULTS };
26801
+ }
26802
+ }
26803
+ return { ...DEFAULTS };
26804
+ }
26289
26805
  function rowToWorkdir(row) {
26290
26806
  return {
26291
26807
  ...row,
@@ -26329,176 +26845,6 @@ function removeWorkdir(projectId, path, db2) {
26329
26845
  const d = db2 || getDatabase2();
26330
26846
  d.run("DELETE FROM project_workdirs WHERE project_id = ? AND path = ?", [projectId, path]);
26331
26847
  }
26332
- function markWorkdirGenerated(projectId, path, db2) {
26333
- const d = db2 || getDatabase2();
26334
- d.run("UPDATE project_workdirs SET claude_md_generated = 1, agents_md_generated = 1 WHERE project_id = ? AND path = ?", [projectId, path]);
26335
- }
26336
- function buildWorkdirList(workdirs, currentPath) {
26337
- return workdirs.map((w) => {
26338
- const isCurrent = w.path === currentPath;
26339
- const primary = w.is_primary ? " (primary)" : "";
26340
- const current = isCurrent ? " \u2190 you are here" : "";
26341
- return `- \`${w.path}\` [${w.label}]${primary}${current} *(machine: ${w.machine_id})*`;
26342
- }).join(`
26343
- `);
26344
- }
26345
- function claudeMdContent(project, workdir, allWorkdirs) {
26346
- const otherDirs = allWorkdirs.filter((w) => w.path !== workdir.path);
26347
- const otherSection = otherDirs.length ? `
26348
- ## Other Working Directories
26349
- ${buildWorkdirList(otherDirs, workdir.path)}
26350
- ` : "";
26351
- const integrations = Object.keys(project.integrations).length ? `
26352
- ## Integrations
26353
- ${Object.entries(project.integrations).filter(([, v]) => v).map(([k, v]) => `- **${k}:** \`${v}\``).join(`
26354
- `)}
26355
- ` : "";
26356
- const tags = project.tags.length ? `
26357
- **Tags:** ${project.tags.join(", ")}` : "";
26358
- return `# Project: ${project.name}
26359
-
26360
- > ${project.description ?? `Working directory for project **${project.name}**`}
26361
- ${tags}
26362
-
26363
- ## Working Directory
26364
-
26365
- You are working in the **${workdir.label}** directory for this project:
26366
-
26367
- \`\`\`
26368
- ${workdir.path}
26369
- \`\`\`
26370
-
26371
- **Write all code to this directory.** Do not write to other project directories unless explicitly instructed.
26372
-
26373
- ## Project Metadata
26374
-
26375
- | Field | Value |
26376
- |-------|-------|
26377
- | ID | \`${project.id}\` |
26378
- | Slug | \`${project.slug}\` |
26379
- | Label | \`${workdir.label}\` |
26380
- | Machine | \`${workdir.machine_id}\` |
26381
- | Status | ${project.status} |
26382
- ${project.git_remote ? `| Git Remote | ${project.git_remote} |` : ""}
26383
- ${project.s3_bucket ? `| S3 Bucket | \`${project.s3_bucket}\` |` : ""}
26384
- ${integrations}${otherSection}
26385
- ## All Working Directories
26386
-
26387
- ${buildWorkdirList(allWorkdirs, workdir.path)}
26388
-
26389
- ## Instructions for AI Agents
26390
-
26391
- 1. **Write code in \`${workdir.path}\`** \u2014 this is your working directory for this session.
26392
- 2. Use \`projects sync ${project.slug}\` to push changes to S3.
26393
- 3. Use \`projects git ${project.slug} <args>\` to run git commands.
26394
- 4. If you need to switch to a different workdir, call \`projects_open\` with the project ID.
26395
-
26396
- ---
26397
- *Generated by open-projects. Regenerate: \`projects workdir generate ${project.slug}\`*
26398
- `.trimStart();
26399
- }
26400
- function agentsMdContent(project, workdir, allWorkdirs) {
26401
- const otherDirs = allWorkdirs.filter((w) => w.path !== workdir.path);
26402
- return `---
26403
- project_id: ${project.id}
26404
- project_slug: ${project.slug}
26405
- project_name: ${project.name}
26406
- working_directory: ${workdir.path}
26407
- label: ${workdir.label}
26408
- machine_id: ${workdir.machine_id}
26409
- is_primary: ${workdir.is_primary}
26410
- ---
26411
-
26412
- # ${project.name} \u2014 Agent Instructions
26413
-
26414
- ## Scope
26415
-
26416
- This agent session is scoped to the **${workdir.label}** working directory:
26417
-
26418
- \`\`\`
26419
- ${workdir.path}
26420
- \`\`\`
26421
-
26422
- **Only write files inside this directory** unless the user explicitly asks you to work elsewhere.
26423
- ${otherDirs.length ? `
26424
- ## Other workdirs for this project
26425
-
26426
- ${otherDirs.map((w) => `- \`${w.path}\` [${w.label}] on ${w.machine_id}`).join(`
26427
- `)}
26428
- ` : ""}
26429
- ## Quick Reference
26430
-
26431
- \`\`\`bash
26432
- # Open this project
26433
- cd ${workdir.path}
26434
-
26435
- # Sync to S3
26436
- projects sync ${project.slug}
26437
-
26438
- # Git operations
26439
- projects git ${project.slug} status
26440
- projects git ${project.slug} log --oneline -10
26441
- \`\`\`
26442
- ${Object.keys(project.integrations).length ? `
26443
- ## Service IDs
26444
-
26445
- ${Object.entries(project.integrations).filter(([, v]) => v).map(([k, v]) => `- **${k}:** \`${v}\``).join(`
26446
- `)}
26447
- ` : ""}
26448
- ---
26449
- *Generated by open-projects ${new Date().toISOString().slice(0, 10)}*
26450
- `.trimStart();
26451
- }
26452
- function generateForWorkdir(project, workdir, allWorkdirs, options = {}) {
26453
- const claudeMd = claudeMdContent(project, workdir, allWorkdirs);
26454
- const agentsMd = agentsMdContent(project, workdir, allWorkdirs);
26455
- const claudePath = join32(workdir.path, "CLAUDE.md");
26456
- const agentsPath = join32(workdir.path, "AGENTS.md");
26457
- let written = false;
26458
- if (!options.dryRun && existsSync32(workdir.path)) {
26459
- if (existsSync32(claudePath) && !options.force) {
26460
- const existing = readFileSync8(claudePath, "utf-8");
26461
- if (existing.includes("Generated by open-projects")) {
26462
- writeFileSync22(claudePath, claudeMd, "utf-8");
26463
- } else {
26464
- const separator = `
26465
-
26466
- ---
26467
-
26468
- <!-- open-projects -->
26469
- `;
26470
- const marker = "<!-- open-projects -->";
26471
- if (existing.includes(marker)) {
26472
- const before = existing.slice(0, existing.indexOf(marker) - separator.length + 1);
26473
- writeFileSync22(claudePath, before + separator + claudeMd, "utf-8");
26474
- } else {
26475
- writeFileSync22(claudePath, existing.trimEnd() + separator + claudeMd, "utf-8");
26476
- }
26477
- }
26478
- } else {
26479
- writeFileSync22(claudePath, claudeMd, "utf-8");
26480
- }
26481
- writeFileSync22(agentsPath, agentsMd, "utf-8");
26482
- markWorkdirGenerated(project.id, workdir.path, options.db);
26483
- written = true;
26484
- }
26485
- return { path: workdir.path, claude_md: claudeMd, agents_md: agentsMd, written };
26486
- }
26487
- function generateAllWorkdirs(project, options = {}) {
26488
- const workdirs = listWorkdirs(project.id, options.db);
26489
- return workdirs.map((w) => generateForWorkdir(project, w, workdirs, options));
26490
- }
26491
- function getConfig() {
26492
- if (existsSync42(CONFIG_PATH3)) {
26493
- try {
26494
- const user = JSON.parse(readFileSync22(CONFIG_PATH3, "utf-8"));
26495
- return { ...DEFAULTS, ...user };
26496
- } catch {
26497
- return { ...DEFAULTS };
26498
- }
26499
- }
26500
- return { ...DEFAULTS };
26501
- }
26502
26848
  function generateProjectId() {
26503
26849
  return `prj_${nanoid()}`;
26504
26850
  }
@@ -26506,10 +26852,12 @@ function slugify2(name) {
26506
26852
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
26507
26853
  }
26508
26854
  function scaffoldProject(path) {
26855
+ if (existsSync42(path))
26856
+ return;
26509
26857
  mkdirSync32(path, { recursive: true });
26510
26858
  const config = getConfig();
26511
26859
  for (const dir of config.scaffold_dirs || ["data", "scripts", "assets", "docs"]) {
26512
- mkdirSync32(join52(path, dir), { recursive: true });
26860
+ mkdirSync32(join42(path, dir), { recursive: true });
26513
26861
  }
26514
26862
  }
26515
26863
  function ensureUniqueSlug(base, db2, excludeId) {
@@ -26560,9 +26908,6 @@ function createProject2(input, db2) {
26560
26908
  try {
26561
26909
  addWorkdir({ project_id: id, path: input.path, label: "main", is_primary: true }, d);
26562
26910
  } catch {}
26563
- try {
26564
- generateAllWorkdirs(project, { db: d });
26565
- } catch {}
26566
26911
  const shouldGitInit = input.git_init !== false;
26567
26912
  if (shouldGitInit) {
26568
26913
  try {
@@ -26668,10 +27013,10 @@ function setIntegrations(id, integrations, db2) {
26668
27013
  id
26669
27014
  ]);
26670
27015
  try {
26671
- const jsonPath = join52(project.path, ".project.json");
26672
- if (existsSync5(jsonPath)) {
26673
- const existing = JSON.parse(readFileSync32(jsonPath, "utf-8"));
26674
- writeFileSync42(jsonPath, JSON.stringify({ ...existing, integrations: merged }, null, 2) + `
27016
+ const jsonPath = join42(project.path, ".project.json");
27017
+ if (existsSync42(jsonPath)) {
27018
+ const existing = JSON.parse(readFileSync22(jsonPath, "utf-8"));
27019
+ writeFileSync32(jsonPath, JSON.stringify({ ...existing, integrations: merged }, null, 2) + `
26675
27020
  `, "utf-8");
26676
27021
  }
26677
27022
  } catch {}
@@ -27745,7 +28090,7 @@ async function collectLocalFiles(rootPath) {
27745
28090
  async function walk(dir) {
27746
28091
  const entries = await readdir(dir, { withFileTypes: true });
27747
28092
  for (const entry of entries) {
27748
- const fullPath = join72(dir, entry.name);
28093
+ const fullPath = join62(dir, entry.name);
27749
28094
  if (entry.isDirectory()) {
27750
28095
  await walk(fullPath);
27751
28096
  } else if (entry.isFile()) {
@@ -27817,7 +28162,7 @@ async function syncProject(project, options = {}) {
27817
28162
  continue;
27818
28163
  }
27819
28164
  try {
27820
- const data = await readFile2(join72(project.path, relPath));
28165
+ const data = await readFile2(join62(project.path, relPath));
27821
28166
  await client.send(new PutObjectCommand({
27822
28167
  Bucket: bucket,
27823
28168
  Key: s3Key,
@@ -27855,9 +28200,9 @@ async function syncProject(project, options = {}) {
27855
28200
  chunks.push(chunk);
27856
28201
  }
27857
28202
  const data = Buffer.concat(chunks);
27858
- const localPath = join72(project.path, relPath);
28203
+ const localPath = join62(project.path, relPath);
27859
28204
  mkdirSync4(dirname42(localPath), { recursive: true });
27860
- writeFileSync52(localPath, data);
28205
+ writeFileSync42(localPath, data);
27861
28206
  log(`pull: ${relPath} (${data.length}B)`);
27862
28207
  result.pulled++;
27863
28208
  result.bytes += data.length;
@@ -27878,18 +28223,18 @@ async function syncProject(project, options = {}) {
27878
28223
  return result;
27879
28224
  }
27880
28225
  function inferProjectName(projectPath) {
27881
- const pkgPath = join82(projectPath, "package.json");
27882
- if (existsSync72(pkgPath)) {
28226
+ const pkgPath = join72(projectPath, "package.json");
28227
+ if (existsSync62(pkgPath)) {
27883
28228
  try {
27884
- const pkg = JSON.parse(readFileSync52(pkgPath, "utf-8"));
28229
+ const pkg = JSON.parse(readFileSync42(pkgPath, "utf-8"));
27885
28230
  if (pkg.name)
27886
28231
  return pkg.name.replace(/^@[^/]+\//, "");
27887
28232
  } catch {}
27888
28233
  }
27889
- const projPath = join82(projectPath, ".project.json");
27890
- if (existsSync72(projPath)) {
28234
+ const projPath = join72(projectPath, ".project.json");
28235
+ if (existsSync62(projPath)) {
27891
28236
  try {
27892
- const p2 = JSON.parse(readFileSync52(projPath, "utf-8"));
28237
+ const p2 = JSON.parse(readFileSync42(projPath, "utf-8"));
27893
28238
  if (p2.name)
27894
28239
  return p2.name;
27895
28240
  } catch {}
@@ -27897,11 +28242,11 @@ function inferProjectName(projectPath) {
27897
28242
  return basename2(projectPath);
27898
28243
  }
27899
28244
  function inferGitRemote(projectPath) {
27900
- const gitConfigPath = join82(projectPath, ".git", "config");
27901
- if (!existsSync72(gitConfigPath))
28245
+ const gitConfigPath = join72(projectPath, ".git", "config");
28246
+ if (!existsSync62(gitConfigPath))
27902
28247
  return;
27903
28248
  try {
27904
- const config = readFileSync52(gitConfigPath, "utf-8");
28249
+ const config = readFileSync42(gitConfigPath, "utf-8");
27905
28250
  const match = config.match(/\[remote "origin"\][\s\S]*?url\s*=\s*(.+)/);
27906
28251
  return match?.[1]?.trim();
27907
28252
  } catch {
@@ -27911,7 +28256,7 @@ function inferGitRemote(projectPath) {
27911
28256
  async function importProject(projectPath, options = {}) {
27912
28257
  const absPath = resolve22(projectPath);
27913
28258
  const log = options.onProgress ?? (() => {});
27914
- if (!existsSync72(absPath)) {
28259
+ if (!existsSync62(absPath)) {
27915
28260
  return { error: `Path does not exist: ${absPath}` };
27916
28261
  }
27917
28262
  const stat = statSync22(absPath);
@@ -27946,7 +28291,7 @@ async function importBulk(dirPath, options = {}) {
27946
28291
  const absDir = resolve22(dirPath);
27947
28292
  const result = { imported: [], skipped: [], errors: [] };
27948
28293
  const log = options.onProgress ?? (() => {});
27949
- if (!existsSync72(absDir)) {
28294
+ if (!existsSync62(absDir)) {
27950
28295
  result.errors.push({ path: absDir, error: "Directory does not exist" });
27951
28296
  return result;
27952
28297
  }
@@ -27954,7 +28299,7 @@ async function importBulk(dirPath, options = {}) {
27954
28299
  const dirs = entries.filter((e2) => e2.isDirectory() && !e2.name.startsWith("."));
27955
28300
  log(`Found ${dirs.length} subdirectories in ${absDir}`);
27956
28301
  for (const entry of dirs) {
27957
- const subPath = join82(absDir, entry.name);
28302
+ const subPath = join72(absDir, entry.name);
27958
28303
  const res = await importProject(subPath, options);
27959
28304
  if (res.project) {
27960
28305
  result.imported.push(res.project);
@@ -27989,8 +28334,7 @@ function publishProject(name, path, options = {}) {
27989
28334
  } else {
27990
28335
  execFileSync2("git", ["remote", "add", "origin", remote], { cwd: path, stdio: "pipe", env: process.env });
27991
28336
  }
27992
- const branch = getCurrentBranch(path);
27993
- execFileSync2("git", ["push", "-u", "origin", branch, "--quiet"], { cwd: path, stdio: "pipe", env: process.env });
28337
+ execFileSync2("git", ["push", "-u", "origin", "main", "--quiet"], { cwd: path, stdio: "pipe", env: process.env });
27994
28338
  pushed = true;
27995
28339
  } catch {}
27996
28340
  }
@@ -28016,14 +28360,14 @@ function getGitHubUrl(path) {
28016
28360
  }
28017
28361
  }
28018
28362
  function getScheduleConfig() {
28019
- if (!existsSync82(CONFIG_PATH22)) {
28363
+ if (!existsSync72(CONFIG_PATH22)) {
28020
28364
  return { enabled: false, interval: "daily", direction: "both" };
28021
28365
  }
28022
- return JSON.parse(readFileSync62(CONFIG_PATH22, "utf-8"));
28366
+ return JSON.parse(readFileSync52(CONFIG_PATH22, "utf-8"));
28023
28367
  }
28024
28368
  function saveScheduleConfig(config) {
28025
28369
  mkdirSync52(dirname5(CONFIG_PATH22), { recursive: true });
28026
- writeFileSync62(CONFIG_PATH22, JSON.stringify(config, null, 2) + `
28370
+ writeFileSync52(CONFIG_PATH22, JSON.stringify(config, null, 2) + `
28027
28371
  `, "utf-8");
28028
28372
  }
28029
28373
  async function syncAll(direction = "both", onProgress) {
@@ -28053,7 +28397,7 @@ async function syncAll(direction = "both", onProgress) {
28053
28397
  function getMachineProfile() {
28054
28398
  const host = (process.env["HOSTNAME"] || hostname2()).split(".")[0] || hostname2();
28055
28399
  const currentPlatform = platform2();
28056
- const workspaceRoot = currentPlatform === "darwin" ? join102(homedir32(), "Workspace") : join102(homedir32(), "workspace");
28400
+ const workspaceRoot = currentPlatform === "darwin" ? join92(homedir32(), "Workspace") : join92(homedir32(), "workspace");
28057
28401
  return {
28058
28402
  hostname: host,
28059
28403
  platform: currentPlatform,
@@ -28080,7 +28424,7 @@ function commandAvailability(command, versionArgs = ["--version"]) {
28080
28424
  return { command, available: true, path: commandPath, version };
28081
28425
  }
28082
28426
  function pathExists(path) {
28083
- return existsSync92(path);
28427
+ return existsSync82(path);
28084
28428
  }
28085
28429
  function classifyMachine(host, currentPlatform) {
28086
28430
  if (host === "apple01")
@@ -28094,10 +28438,10 @@ function classifyMachine(host, currentPlatform) {
28094
28438
  return "unknown";
28095
28439
  }
28096
28440
  function gitStatus(path) {
28097
- if (!existsSync102(join112(path, ".git")))
28441
+ if (!existsSync92(join102(path, ".git")))
28098
28442
  return "not a repo";
28099
28443
  try {
28100
- const out = execSync3("git status --porcelain", { cwd: path, stdio: "pipe", encoding: "utf-8" }).trim();
28444
+ const out = execSync22("git status --porcelain", { cwd: path, stdio: "pipe", encoding: "utf-8" }).trim();
28101
28445
  if (!out)
28102
28446
  return "clean";
28103
28447
  const n2 = out.split(`
@@ -28108,17 +28452,17 @@ function gitStatus(path) {
28108
28452
  }
28109
28453
  }
28110
28454
  function dirSize(path) {
28111
- if (!existsSync102(path))
28455
+ if (!existsSync92(path))
28112
28456
  return 0;
28113
28457
  try {
28114
- const out = execSync3(`du -sb -- "${path}" 2>/dev/null || du -sk -- "${path}" 2>/dev/null`, { stdio: "pipe", encoding: "utf-8" }).trim();
28458
+ const out = execSync22(`du -sb -- "${path}" 2>/dev/null || du -sk -- "${path}" 2>/dev/null`, { stdio: "pipe", encoding: "utf-8" }).trim();
28115
28459
  return parseInt(out.split("\t")[0] ?? "0", 10);
28116
28460
  } catch {
28117
28461
  return 0;
28118
28462
  }
28119
28463
  }
28120
28464
  function getProjectStatus(project) {
28121
- const pathExists2 = existsSync102(project.path);
28465
+ const pathExists2 = existsSync92(project.path);
28122
28466
  const workdirs = listWorkdirs(project.id);
28123
28467
  const lastSync = getDatabase2().query("SELECT completed_at FROM sync_log WHERE project_id = ? AND status = 'completed' ORDER BY completed_at DESC LIMIT 1").get(project.id)?.completed_at ?? null;
28124
28468
  return {
@@ -28131,21 +28475,18 @@ function getProjectStatus(project) {
28131
28475
  };
28132
28476
  }
28133
28477
  function run(cmd) {
28134
- return execSync22(cmd, { encoding: "utf-8", stdio: "pipe" }).trim();
28478
+ return execSync32(cmd, { encoding: "utf-8", stdio: "pipe" }).trim();
28135
28479
  }
28136
28480
  function getTmuxSessionName(project) {
28137
- return project.slug || project.name;
28481
+ const raw = project.slug || project.name;
28482
+ if (project.path?.includes("opensourcedev")) {
28483
+ const normalized = raw.replace(/^proj-/, "");
28484
+ return normalized.startsWith("open-") ? normalized : `open-${normalized}`;
28485
+ }
28486
+ return raw;
28138
28487
  }
28139
28488
  function listSessions2() {
28140
- let output = "";
28141
- try {
28142
- output = run("tmux list-sessions -F '#{session_name}:#{session_group}:#{session_windows}:#{session_attached}'");
28143
- } catch (err) {
28144
- const message = err instanceof Error ? err.message : String(err);
28145
- if (message.includes("no server running"))
28146
- return [];
28147
- throw err;
28148
- }
28489
+ const output = run("tmux list-sessions -F '#{session_name}:#{session_group}:#{session_windows}:#{session_attached}'");
28149
28490
  return output.split(`
28150
28491
  `).filter(Boolean).map((line) => {
28151
28492
  const [name, group, windows, attached] = line.split(":");
@@ -28253,7 +28594,7 @@ function getProjectLocations(project) {
28253
28594
  const currentMachine = getMachineId();
28254
28595
  return listWorkdirs(project.id).map((workdir) => ({
28255
28596
  ...workdir,
28256
- exists: existsSync112(workdir.path),
28597
+ exists: existsSync102(workdir.path),
28257
28598
  currentMachine: workdir.machine_id === currentMachine,
28258
28599
  recommended: workdir.is_primary || workdir.path === project.path
28259
28600
  }));
@@ -28290,7 +28631,7 @@ function buildProjectContext(project) {
28290
28631
  };
28291
28632
  }
28292
28633
  function getGitContext(path) {
28293
- if (!existsSync112(join122(path, ".git"))) {
28634
+ if (!existsSync102(join112(path, ".git"))) {
28294
28635
  return { isRepo: false, branch: null, dirtyCount: null, remote: null };
28295
28636
  }
28296
28637
  return {
@@ -28333,7 +28674,7 @@ function setupMachineReport(options = {}) {
28333
28674
  const dryRun = options.dryRun !== false;
28334
28675
  const checks = [];
28335
28676
  const dbDir = dirname6(getDbPath2());
28336
- const cloudConfig = join132(process.env["HOME"] || "~", ".hasna", "cloud", "config.json");
28677
+ const cloudConfig = join122(process.env["HOME"] || "~", ".hasna", "cloud", "config.json");
28337
28678
  checks.push(pathCheck("PROJECTS_DATA_DIR", "projects data dir", dbDir, options));
28338
28679
  checks.push(pathCheck("WORKSPACE_ROOT", "workspace root", machine.workspaceRoot, options));
28339
28680
  checks.push(commandCheck("bun", ["--version"], "Bun runtime"));
@@ -28394,8 +28735,8 @@ function commandCheck(command, versionArgs, label, missingStatus = "error") {
28394
28735
  }
28395
28736
  function packageVersion() {
28396
28737
  try {
28397
- const pkgPath = join132(dirname6(fileURLToPath(import.meta.url)), "..", "..", "package.json");
28398
- return JSON.parse(readFileSync72(pkgPath, "utf-8")).version || "0.0.0";
28738
+ const pkgPath = join122(dirname6(fileURLToPath(import.meta.url)), "..", "..", "package.json");
28739
+ return JSON.parse(readFileSync62(pkgPath, "utf-8")).version || "0.0.0";
28399
28740
  } catch {
28400
28741
  return "0.0.0";
28401
28742
  }
@@ -28409,7 +28750,7 @@ function findStaleIssues(project) {
28409
28750
  const issues = [];
28410
28751
  const currentMachine = getMachineId();
28411
28752
  for (const p2 of projects) {
28412
- if (p2.status === "active" && !existsSync122(p2.path)) {
28753
+ if (p2.status === "active" && !existsSync112(p2.path)) {
28413
28754
  issues.push({
28414
28755
  code: "PROJECT_PATH_MISSING",
28415
28756
  severity: "error",
@@ -28420,7 +28761,7 @@ function findStaleIssues(project) {
28420
28761
  });
28421
28762
  }
28422
28763
  for (const workdir of listWorkdirs(p2.id)) {
28423
- if (workdir.machine_id === currentMachine && !existsSync122(workdir.path)) {
28764
+ if (workdir.machine_id === currentMachine && !existsSync112(workdir.path)) {
28424
28765
  issues.push({
28425
28766
  code: "WORKDIR_PATH_MISSING",
28426
28767
  severity: "warn",
@@ -29362,7 +29703,7 @@ Reference: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.ht
29362
29703
  }
29363
29704
  const credentials = await fromWebToken({
29364
29705
  ...init,
29365
- webIdentityToken: import_shared_ini_file_loader10.externalDataInterceptor?.getTokenRecord?.()[webIdentityTokenFile] ?? readFileSync42(webIdentityTokenFile, { encoding: "ascii" }),
29706
+ webIdentityToken: import_shared_ini_file_loader10.externalDataInterceptor?.getTokenRecord?.()[webIdentityTokenFile] ?? readFileSync32(webIdentityTokenFile, { encoding: "ascii" }),
29366
29707
  roleArn,
29367
29708
  roleSessionName
29368
29709
  })(awsIdentityProperties);
@@ -29449,7 +29790,7 @@ coverage/
29449
29790
  # Cache
29450
29791
  .cache/
29451
29792
  .turbo/
29452
- `, BOOTSTRAP_PATHS, CONFIG_PATH3, DEFAULTS, nanoid, import_protocol_http, addExpectContinueMiddlewareOptions, getAddExpectContinuePlugin = (options) => ({
29793
+ `, CONFIG_PATH3, DEFAULTS, nanoid, import_protocol_http, addExpectContinueMiddlewareOptions, getAddExpectContinuePlugin = (options) => ({
29453
29794
  applyToStack: (clientStack) => {
29454
29795
  clientStack.add(addExpectContinueMiddleware(options), addExpectContinueMiddlewareOptions);
29455
29796
  }
@@ -42528,11 +42869,11 @@ More information can be found at: https://a.co/c895JFp`);
42528
42869
  var numberSelector = (obj, key, type2) => {
42529
42870
  if (!(key in obj))
42530
42871
  return;
42531
- const numberValue = parseInt(obj[key], 10);
42532
- if (Number.isNaN(numberValue)) {
42872
+ const numberValue2 = parseInt(obj[key], 10);
42873
+ if (Number.isNaN(numberValue2)) {
42533
42874
  throw new TypeError(`Cannot load ${type2} '${key}'. Expected number, got '${obj[key]}'.`);
42534
42875
  }
42535
- return numberValue;
42876
+ return numberValue2;
42536
42877
  };
42537
42878
  exports.SelectorType = undefined;
42538
42879
  (function(SelectorType2) {
@@ -47260,7 +47601,7 @@ More information can be found at: https://a.co/c895JFp`);
47260
47601
  };
47261
47602
  });
47262
47603
  require_dist_cjs41 = __commonJS3((exports) => {
47263
- var __dirname2 = "/Users/hasna/Workspace/hasna/opensource/open-projects/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
47604
+ var __dirname2 = "/home/hasna/workspace/hasna/opensource/open-projects/node_modules/@aws-sdk/util-user-agent-node/dist-cjs";
47264
47605
  var node_os = __require3("os");
47265
47606
  var node_process = __require3("process");
47266
47607
  var utilConfigProvider = require_dist_cjs28();
@@ -50930,10 +51271,10 @@ More information can be found at: https://a.co/c895JFp`);
50930
51271
  await fs2.writeFile(tokenFilePath, JSON.stringify(token, null, 2), "utf8");
50931
51272
  }
50932
51273
  getTokenFilePath() {
50933
- const directory = process.env.AWS_LOGIN_CACHE_DIRECTORY ?? join62(homedir22(), ".aws", "login", "cache");
51274
+ const directory = process.env.AWS_LOGIN_CACHE_DIRECTORY ?? join52(homedir22(), ".aws", "login", "cache");
50934
51275
  const loginSessionBytes = Buffer.from(this.loginSession, "utf8");
50935
51276
  const loginSessionSha256 = createHash2("sha256").update(loginSessionBytes).digest("hex");
50936
- return join62(directory, `${loginSessionSha256}.json`);
51277
+ return join52(directory, `${loginSessionSha256}.json`);
50937
51278
  }
50938
51279
  derToRawSignature(derSignature) {
50939
51280
  let offset = 2;
@@ -51234,18 +51575,7 @@ More information can be found at: https://a.co/c895JFp`);
51234
51575
  INSERT OR IGNORE INTO _migrations (id) VALUES (4);
51235
51576
  `
51236
51577
  ];
51237
- BOOTSTRAP_PATHS = [
51238
- ".gitignore",
51239
- ".project.json",
51240
- "CLAUDE.md",
51241
- "AGENTS.md",
51242
- "README.md",
51243
- "docs",
51244
- "data",
51245
- "scripts",
51246
- "assets"
51247
- ];
51248
- CONFIG_PATH3 = join42(homedir11(), ".hasna", "projects", "config.json");
51578
+ CONFIG_PATH3 = join32(homedir11(), ".hasna", "projects", "config.json");
51249
51579
  DEFAULTS = {
51250
51580
  default_path: process.cwd(),
51251
51581
  default_github_org: "hasnaxyz",
@@ -57031,7 +57361,7 @@ More information can be found at: https://a.co/c895JFp`);
57031
57361
  }).s("AmazonS3", "PutObject", {}).n("S3Client", "PutObjectCommand").sc(PutObject$).build() {
57032
57362
  };
57033
57363
  MAX_FILE_SIZE = 100 * 1024 * 1024;
57034
- CONFIG_PATH22 = join92(process.env["HOME"] || "~", ".hasna", "projects", "scheduler.json");
57364
+ CONFIG_PATH22 = join82(process.env["HOME"] || "~", ".hasna", "projects", "scheduler.json");
57035
57365
  SPARK_MACHINES = new Set(["spark01", "spark02"]);
57036
57366
  });
57037
57367
 
@@ -57289,7 +57619,7 @@ __export(exports_openapi_import, {
57289
57619
  importFromOpenAPI: () => importFromOpenAPI,
57290
57620
  importApiChecksFromOpenAPI: () => importApiChecksFromOpenAPI
57291
57621
  });
57292
- import { readFileSync as readFileSync9 } from "fs";
57622
+ import { readFileSync as readFileSync8 } from "fs";
57293
57623
  function parseSpec(content) {
57294
57624
  try {
57295
57625
  return JSON.parse(content);
@@ -57318,7 +57648,7 @@ function parseOpenAPISpec(filePathOrUrl) {
57318
57648
  if (filePathOrUrl.startsWith("http")) {
57319
57649
  throw new Error("URL fetching not supported yet. Download the spec file first.");
57320
57650
  }
57321
- content = readFileSync9(filePathOrUrl, "utf-8");
57651
+ content = readFileSync8(filePathOrUrl, "utf-8");
57322
57652
  const spec = parseSpec(content);
57323
57653
  const isOpenAPI3 = !!spec.openapi;
57324
57654
  const isSwagger2 = !!spec.swagger;
@@ -57381,7 +57711,7 @@ function parseOpenAPISpecAsChecks(filePathOrUrl) {
57381
57711
  if (filePathOrUrl.startsWith("http")) {
57382
57712
  throw new Error("URL fetching not supported yet. Download the spec file first.");
57383
57713
  }
57384
- content = readFileSync9(filePathOrUrl, "utf-8");
57714
+ content = readFileSync8(filePathOrUrl, "utf-8");
57385
57715
  const spec = parseSpec(content);
57386
57716
  if (!spec.openapi && !spec.swagger) {
57387
57717
  throw new Error("Not a valid OpenAPI 3.x or Swagger 2.0 spec");
@@ -57721,7 +58051,7 @@ async function recordSession(url, options) {
57721
58051
  await Promise.race([
57722
58052
  page.waitForEvent("close").catch(() => {}),
57723
58053
  context.waitForEvent("close").catch(() => {}),
57724
- new Promise((resolve4) => setTimeout(resolve4, timeout))
58054
+ new Promise((resolve3) => setTimeout(resolve3, timeout))
57725
58055
  ]);
57726
58056
  clearInterval(pollInterval);
57727
58057
  try {
@@ -74582,34 +74912,27 @@ var init_v3 = __esm(() => {
74582
74912
 
74583
74913
  // node_modules/@ai-sdk/provider-utils/node_modules/eventsource-parser/dist/index.js
74584
74914
  function noop(_arg) {}
74585
- function createParser(config2) {
74586
- if (typeof config2 == "function")
74587
- throw new TypeError("`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?");
74588
- const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config2, pendingFragments = [];
74589
- let pendingFragmentsLength = 0, isFirstChunk = true, id, data = "", dataLines = 0, eventType, terminated = false;
74915
+ function createParser(callbacks) {
74916
+ if (typeof callbacks == "function")
74917
+ throw new TypeError("`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?");
74918
+ const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks, pendingFragments = [];
74919
+ let isFirstChunk = true, id, data = "", dataLines = 0, eventType;
74590
74920
  function feed(chunk) {
74591
- if (terminated)
74592
- throw new Error("Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.");
74593
74921
  if (isFirstChunk && (isFirstChunk = false, chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191 && (chunk = chunk.slice(3))), pendingFragments.length === 0) {
74594
74922
  const trailing2 = processLines(chunk);
74595
- trailing2 !== "" && (pendingFragments.push(trailing2), pendingFragmentsLength = trailing2.length), checkBufferSize();
74923
+ trailing2 !== "" && pendingFragments.push(trailing2);
74596
74924
  return;
74597
74925
  }
74598
74926
  if (chunk.indexOf(`
74599
74927
  `) === -1 && chunk.indexOf("\r") === -1) {
74600
- pendingFragments.push(chunk), pendingFragmentsLength += chunk.length, checkBufferSize();
74928
+ pendingFragments.push(chunk);
74601
74929
  return;
74602
74930
  }
74603
74931
  pendingFragments.push(chunk);
74604
74932
  const input = pendingFragments.join("");
74605
- pendingFragments.length = 0, pendingFragmentsLength = 0;
74933
+ pendingFragments.length = 0;
74606
74934
  const trailing = processLines(input);
74607
- trailing !== "" && (pendingFragments.push(trailing), pendingFragmentsLength = trailing.length), checkBufferSize();
74608
- }
74609
- function checkBufferSize() {
74610
- maxBufferSize !== undefined && (pendingFragmentsLength + data.length <= maxBufferSize || (terminated = true, pendingFragments.length = 0, pendingFragmentsLength = 0, id = undefined, data = "", dataLines = 0, eventType = undefined, onError(new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {
74611
- type: "max-buffer-size-exceeded"
74612
- }))));
74935
+ trailing !== "" && pendingFragments.push(trailing);
74613
74936
  }
74614
74937
  function processLines(chunk) {
74615
74938
  let searchIndex = 0;
@@ -74721,7 +75044,7 @@ ${value}`, dataLines++;
74721
75044
  const incompleteLine = pendingFragments.join("");
74722
75045
  parseLine(incompleteLine, 0, incompleteLine.length);
74723
75046
  }
74724
- isFirstChunk = true, id = undefined, data = "", dataLines = 0, eventType = undefined, pendingFragments.length = 0, pendingFragmentsLength = 0, terminated = false;
75047
+ isFirstChunk = true, id = undefined, data = "", dataLines = 0, eventType = undefined, pendingFragments.length = 0;
74725
75048
  }
74726
75049
  return { feed, reset };
74727
75050
  }
@@ -74745,7 +75068,7 @@ var EventSourceParserStream;
74745
75068
  var init_stream = __esm(() => {
74746
75069
  init_dist4();
74747
75070
  EventSourceParserStream = class EventSourceParserStream extends TransformStream {
74748
- constructor({ onError, onRetry, onComment, maxBufferSize } = {}) {
75071
+ constructor({ onError, onRetry, onComment } = {}) {
74749
75072
  let parser;
74750
75073
  super({
74751
75074
  start(controller) {
@@ -74754,11 +75077,10 @@ var init_stream = __esm(() => {
74754
75077
  controller.enqueue(event);
74755
75078
  },
74756
75079
  onError(error40) {
74757
- typeof onError == "function" && onError(error40), (onError === "terminate" || error40.type === "max-buffer-size-exceeded") && controller.error(error40);
75080
+ onError === "terminate" ? controller.error(error40) : typeof onError == "function" && onError(error40);
74758
75081
  },
74759
75082
  onRetry,
74760
- onComment,
74761
- maxBufferSize
75083
+ onComment
74762
75084
  });
74763
75085
  },
74764
75086
  transform(chunk) {
@@ -76100,7 +76422,7 @@ function createProviderToolFactoryWithOutputSchema({
76100
76422
  supportsDeferredResults
76101
76423
  });
76102
76424
  }
76103
- async function resolve4(value) {
76425
+ async function resolve3(value) {
76104
76426
  if (typeof value === "function") {
76105
76427
  value = value();
76106
76428
  }
@@ -76197,7 +76519,7 @@ var DelayedPromise = class {
76197
76519
  });
76198
76520
  }
76199
76521
  return () => `${prefix}${separator}${generator()}`;
76200
- }, generateId, FETCH_FAILED_ERROR_MESSAGES, BUN_ERROR_CODES, VERSION = "4.0.27", getOriginalFetch = () => globalThis.fetch, getFromApi = async ({
76522
+ }, generateId, FETCH_FAILED_ERROR_MESSAGES, BUN_ERROR_CODES, VERSION = "4.0.26", getOriginalFetch = () => globalThis.fetch, getFromApi = async ({
76201
76523
  url: url2,
76202
76524
  headers = {},
76203
76525
  successfulResponseHandler,
@@ -76863,19 +77185,19 @@ var require_token_io = __commonJS((exports, module) => {
76863
77185
  getUserDataDir: () => getUserDataDir
76864
77186
  });
76865
77187
  module.exports = __toCommonJS2(token_io_exports);
76866
- var import_path27 = __toESM4(__require("path"));
76867
- var import_fs31 = __toESM4(__require("fs"));
77188
+ var import_path26 = __toESM4(__require("path"));
77189
+ var import_fs30 = __toESM4(__require("fs"));
76868
77190
  var import_os13 = __toESM4(__require("os"));
76869
77191
  var import_token_error = require_token_error();
76870
77192
  function findRootDir() {
76871
77193
  try {
76872
77194
  let dir = process.cwd();
76873
- while (dir !== import_path27.default.dirname(dir)) {
76874
- const pkgPath = import_path27.default.join(dir, ".vercel");
76875
- if (import_fs31.default.existsSync(pkgPath)) {
77195
+ while (dir !== import_path26.default.dirname(dir)) {
77196
+ const pkgPath = import_path26.default.join(dir, ".vercel");
77197
+ if (import_fs30.default.existsSync(pkgPath)) {
76876
77198
  return dir;
76877
77199
  }
76878
- dir = import_path27.default.dirname(dir);
77200
+ dir = import_path26.default.dirname(dir);
76879
77201
  }
76880
77202
  } catch (e2) {
76881
77203
  throw new import_token_error.VercelOidcTokenError("Token refresh only supported in node server environments");
@@ -76888,9 +77210,9 @@ var require_token_io = __commonJS((exports, module) => {
76888
77210
  }
76889
77211
  switch (import_os13.default.platform()) {
76890
77212
  case "darwin":
76891
- return import_path27.default.join(import_os13.default.homedir(), "Library/Application Support");
77213
+ return import_path26.default.join(import_os13.default.homedir(), "Library/Application Support");
76892
77214
  case "linux":
76893
- return import_path27.default.join(import_os13.default.homedir(), ".local/share");
77215
+ return import_path26.default.join(import_os13.default.homedir(), ".local/share");
76894
77216
  case "win32":
76895
77217
  if (process.env.LOCALAPPDATA) {
76896
77218
  return process.env.LOCALAPPDATA;
@@ -77787,7 +78109,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
77787
78109
  try {
77788
78110
  const { value } = await getFromApi({
77789
78111
  url: `${this.config.baseURL}/config`,
77790
- headers: await resolve4(this.config.headers()),
78112
+ headers: await resolve3(this.config.headers()),
77791
78113
  successfulResponseHandler: createJsonResponseHandler(gatewayAvailableModelsResponseSchema),
77792
78114
  failedResponseHandler: createJsonErrorResponseHandler({
77793
78115
  errorSchema: exports_external2.any(),
@@ -77805,7 +78127,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
77805
78127
  const baseUrl = new URL(this.config.baseURL);
77806
78128
  const { value } = await getFromApi({
77807
78129
  url: `${baseUrl.origin}/v1/credits`,
77808
- headers: await resolve4(this.config.headers()),
78130
+ headers: await resolve3(this.config.headers()),
77809
78131
  successfulResponseHandler: createJsonResponseHandler(gatewayCreditsResponseSchema),
77810
78132
  failedResponseHandler: createJsonErrorResponseHandler({
77811
78133
  errorSchema: exports_external2.any(),
@@ -77851,7 +78173,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
77851
78173
  }
77852
78174
  const { value } = await getFromApi({
77853
78175
  url: `${baseUrl.origin}/v1/report?${searchParams.toString()}`,
77854
- headers: await resolve4(this.config.headers()),
78176
+ headers: await resolve3(this.config.headers()),
77855
78177
  successfulResponseHandler: createJsonResponseHandler(gatewaySpendReportResponseSchema),
77856
78178
  failedResponseHandler: createJsonErrorResponseHandler({
77857
78179
  errorSchema: exports_external2.any(),
@@ -77873,7 +78195,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
77873
78195
  const baseUrl = new URL(this.config.baseURL);
77874
78196
  const { value } = await getFromApi({
77875
78197
  url: `${baseUrl.origin}/v1/generation?id=${encodeURIComponent(params.id)}`,
77876
- headers: await resolve4(this.config.headers()),
78198
+ headers: await resolve3(this.config.headers()),
77877
78199
  successfulResponseHandler: createJsonResponseHandler(gatewayGenerationInfoResponseSchema),
77878
78200
  failedResponseHandler: createJsonErrorResponseHandler({
77879
78201
  errorSchema: exports_external2.any(),
@@ -77906,7 +78228,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
77906
78228
  async doGenerate(options) {
77907
78229
  const { args, warnings } = await this.getArgs(options);
77908
78230
  const { abortSignal } = options;
77909
- const resolvedHeaders = await resolve4(this.config.headers());
78231
+ const resolvedHeaders = await resolve3(this.config.headers());
77910
78232
  try {
77911
78233
  const {
77912
78234
  responseHeaders,
@@ -77914,7 +78236,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
77914
78236
  rawValue: rawResponse
77915
78237
  } = await postJsonToApi({
77916
78238
  url: this.getUrl(),
77917
- headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await resolve4(this.config.o11yHeaders)),
78239
+ headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await resolve3(this.config.o11yHeaders)),
77918
78240
  body: args,
77919
78241
  successfulResponseHandler: createJsonResponseHandler(exports_external2.any()),
77920
78242
  failedResponseHandler: createJsonErrorResponseHandler({
@@ -77937,11 +78259,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
77937
78259
  async doStream(options) {
77938
78260
  const { args, warnings } = await this.getArgs(options);
77939
78261
  const { abortSignal } = options;
77940
- const resolvedHeaders = await resolve4(this.config.headers());
78262
+ const resolvedHeaders = await resolve3(this.config.headers());
77941
78263
  try {
77942
78264
  const { value: response, responseHeaders } = await postJsonToApi({
77943
78265
  url: this.getUrl(),
77944
- headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await resolve4(this.config.o11yHeaders)),
78266
+ headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await resolve3(this.config.o11yHeaders)),
77945
78267
  body: args,
77946
78268
  successfulResponseHandler: createEventSourceResponseHandler(exports_external2.any()),
77947
78269
  failedResponseHandler: createJsonErrorResponseHandler({
@@ -78026,7 +78348,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
78026
78348
  providerOptions
78027
78349
  }) {
78028
78350
  var _a92;
78029
- const resolvedHeaders = await resolve4(this.config.headers());
78351
+ const resolvedHeaders = await resolve3(this.config.headers());
78030
78352
  try {
78031
78353
  const {
78032
78354
  responseHeaders,
@@ -78034,7 +78356,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
78034
78356
  rawValue
78035
78357
  } = await postJsonToApi({
78036
78358
  url: this.getUrl(),
78037
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
78359
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders)),
78038
78360
  body: {
78039
78361
  values,
78040
78362
  ...providerOptions ? { providerOptions } : {}
@@ -78090,7 +78412,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
78090
78412
  abortSignal
78091
78413
  }) {
78092
78414
  var _a92, _b92, _c2, _d2;
78093
- const resolvedHeaders = await resolve4(this.config.headers());
78415
+ const resolvedHeaders = await resolve3(this.config.headers());
78094
78416
  try {
78095
78417
  const {
78096
78418
  responseHeaders,
@@ -78098,7 +78420,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
78098
78420
  rawValue
78099
78421
  } = await postJsonToApi({
78100
78422
  url: this.getUrl(),
78101
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
78423
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders)),
78102
78424
  body: {
78103
78425
  prompt,
78104
78426
  n: n2,
@@ -78173,11 +78495,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
78173
78495
  abortSignal
78174
78496
  }) {
78175
78497
  var _a92;
78176
- const resolvedHeaders = await resolve4(this.config.headers());
78498
+ const resolvedHeaders = await resolve3(this.config.headers());
78177
78499
  try {
78178
78500
  const { responseHeaders, value: responseBody } = await postJsonToApi({
78179
78501
  url: this.getUrl(),
78180
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders), { accept: "text/event-stream" }),
78502
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders), { accept: "text/event-stream" }),
78181
78503
  body: {
78182
78504
  prompt,
78183
78505
  n: n2,
@@ -78300,7 +78622,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
78300
78622
  abortSignal,
78301
78623
  providerOptions
78302
78624
  }) {
78303
- const resolvedHeaders = await resolve4(this.config.headers());
78625
+ const resolvedHeaders = await resolve3(this.config.headers());
78304
78626
  try {
78305
78627
  const {
78306
78628
  responseHeaders,
@@ -78308,7 +78630,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
78308
78630
  rawValue
78309
78631
  } = await postJsonToApi({
78310
78632
  url: this.getUrl(),
78311
- headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
78633
+ headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve3(this.config.o11yHeaders)),
78312
78634
  body: {
78313
78635
  documents,
78314
78636
  query,
@@ -78342,7 +78664,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
78342
78664
  "ai-model-id": this.modelId
78343
78665
  };
78344
78666
  }
78345
- }, gatewayRerankingResponseSchema, parallelSearchInputSchema, parallelSearchOutputSchema, parallelSearchToolFactory, parallelSearch = (config2 = {}) => parallelSearchToolFactory(config2), perplexitySearchInputSchema, perplexitySearchOutputSchema, perplexitySearchToolFactory, perplexitySearch = (config2 = {}) => perplexitySearchToolFactory(config2), gatewayTools, VERSION2 = "3.0.120", AI_GATEWAY_PROTOCOL_VERSION = "0.0.1", gateway;
78667
+ }, gatewayRerankingResponseSchema, parallelSearchInputSchema, parallelSearchOutputSchema, parallelSearchToolFactory, parallelSearch = (config2 = {}) => parallelSearchToolFactory(config2), perplexitySearchInputSchema, perplexitySearchOutputSchema, perplexitySearchToolFactory, perplexitySearch = (config2 = {}) => perplexitySearchToolFactory(config2), gatewayTools, VERSION2 = "3.0.110", AI_GATEWAY_PROTOCOL_VERSION = "0.0.1", gateway;
78346
78668
  var init_dist6 = __esm(() => {
78347
78669
  init_dist5();
78348
78670
  init_dist3();
@@ -78381,15 +78703,13 @@ var init_dist6 = __esm(() => {
78381
78703
  message,
78382
78704
  statusCode = 500,
78383
78705
  cause,
78384
- generationId,
78385
- isRetryable = statusCode != null && (statusCode === 408 || statusCode === 409 || statusCode === 429 || statusCode >= 500)
78706
+ generationId
78386
78707
  }) {
78387
78708
  super(generationId ? `${message} [${generationId}]` : message);
78388
78709
  this[_a17] = true;
78389
78710
  this.statusCode = statusCode;
78390
78711
  this.cause = cause;
78391
78712
  this.generationId = generationId;
78392
- this.isRetryable = isRetryable;
78393
78713
  }
78394
78714
  static isInstance(error40) {
78395
78715
  return _GatewayError.hasMarker(error40);
@@ -78912,11 +79232,62 @@ Run 'npx vercel link' to link your project, then 'vc env pull' to fetch the toke
78912
79232
  gateway = createGatewayProvider();
78913
79233
  });
78914
79234
 
79235
+ // node_modules/@opentelemetry/api/build/src/platform/node/globalThis.js
79236
+ var require_globalThis = __commonJS((exports) => {
79237
+ Object.defineProperty(exports, "__esModule", { value: true });
79238
+ exports._globalThis = undefined;
79239
+ exports._globalThis = typeof globalThis === "object" ? globalThis : global;
79240
+ });
79241
+
79242
+ // node_modules/@opentelemetry/api/build/src/platform/node/index.js
79243
+ var require_node = __commonJS((exports) => {
79244
+ var __createBinding2 = exports && exports.__createBinding || (Object.create ? function(o2, m2, k2, k22) {
79245
+ if (k22 === undefined)
79246
+ k22 = k2;
79247
+ Object.defineProperty(o2, k22, { enumerable: true, get: function() {
79248
+ return m2[k2];
79249
+ } });
79250
+ } : function(o2, m2, k2, k22) {
79251
+ if (k22 === undefined)
79252
+ k22 = k2;
79253
+ o2[k22] = m2[k2];
79254
+ });
79255
+ var __exportStar2 = exports && exports.__exportStar || function(m2, exports2) {
79256
+ for (var p2 in m2)
79257
+ if (p2 !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p2))
79258
+ __createBinding2(exports2, m2, p2);
79259
+ };
79260
+ Object.defineProperty(exports, "__esModule", { value: true });
79261
+ __exportStar2(require_globalThis(), exports);
79262
+ });
79263
+
79264
+ // node_modules/@opentelemetry/api/build/src/platform/index.js
79265
+ var require_platform = __commonJS((exports) => {
79266
+ var __createBinding2 = exports && exports.__createBinding || (Object.create ? function(o2, m2, k2, k22) {
79267
+ if (k22 === undefined)
79268
+ k22 = k2;
79269
+ Object.defineProperty(o2, k22, { enumerable: true, get: function() {
79270
+ return m2[k2];
79271
+ } });
79272
+ } : function(o2, m2, k2, k22) {
79273
+ if (k22 === undefined)
79274
+ k22 = k2;
79275
+ o2[k22] = m2[k2];
79276
+ });
79277
+ var __exportStar2 = exports && exports.__exportStar || function(m2, exports2) {
79278
+ for (var p2 in m2)
79279
+ if (p2 !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p2))
79280
+ __createBinding2(exports2, m2, p2);
79281
+ };
79282
+ Object.defineProperty(exports, "__esModule", { value: true });
79283
+ __exportStar2(require_node(), exports);
79284
+ });
79285
+
78915
79286
  // node_modules/@opentelemetry/api/build/src/version.js
78916
79287
  var require_version = __commonJS((exports) => {
78917
79288
  Object.defineProperty(exports, "__esModule", { value: true });
78918
79289
  exports.VERSION = undefined;
78919
- exports.VERSION = "1.9.1";
79290
+ exports.VERSION = "1.9.0";
78920
79291
  });
78921
79292
 
78922
79293
  // node_modules/@opentelemetry/api/build/src/internal/semver.js
@@ -78994,11 +79365,12 @@ var require_semver2 = __commonJS((exports) => {
78994
79365
  var require_global_utils = __commonJS((exports) => {
78995
79366
  Object.defineProperty(exports, "__esModule", { value: true });
78996
79367
  exports.unregisterGlobal = exports.getGlobal = exports.registerGlobal = undefined;
79368
+ var platform_1 = require_platform();
78997
79369
  var version_1 = require_version();
78998
79370
  var semver_1 = require_semver2();
78999
79371
  var major = version_1.VERSION.split(".")[0];
79000
79372
  var GLOBAL_OPENTELEMETRY_API_KEY = Symbol.for(`opentelemetry.js.api.${major}`);
79001
- var _global = typeof globalThis === "object" ? globalThis : typeof self === "object" ? self : typeof window === "object" ? window : typeof global === "object" ? global : {};
79373
+ var _global = platform_1._globalThis;
79002
79374
  function registerGlobal(type2, instance, diag, allowOverride = false) {
79003
79375
  var _a16;
79004
79376
  const api2 = _global[GLOBAL_OPENTELEMETRY_API_KEY] = (_a16 = _global[GLOBAL_OPENTELEMETRY_API_KEY]) !== null && _a16 !== undefined ? _a16 : {
@@ -79070,7 +79442,8 @@ var require_ComponentLogger = __commonJS((exports) => {
79070
79442
  if (!logger) {
79071
79443
  return;
79072
79444
  }
79073
- return logger[funcName](namespace, ...args);
79445
+ args.unshift(namespace);
79446
+ return logger[funcName](...args);
79074
79447
  }
79075
79448
  });
79076
79449
 
@@ -79131,12 +79504,6 @@ var require_diag = __commonJS((exports) => {
79131
79504
  var API_NAME = "diag";
79132
79505
 
79133
79506
  class DiagAPI {
79134
- static instance() {
79135
- if (!this._instance) {
79136
- this._instance = new DiagAPI;
79137
- }
79138
- return this._instance;
79139
- }
79140
79507
  constructor() {
79141
79508
  function _logProxy(funcName) {
79142
79509
  return function(...args) {
@@ -79181,6 +79548,12 @@ var require_diag = __commonJS((exports) => {
79181
79548
  self2.warn = _logProxy("warn");
79182
79549
  self2.error = _logProxy("error");
79183
79550
  }
79551
+ static instance() {
79552
+ if (!this._instance) {
79553
+ this._instance = new DiagAPI;
79554
+ }
79555
+ return this._instance;
79556
+ }
79184
79557
  }
79185
79558
  exports.DiagAPI = DiagAPI;
79186
79559
  });
@@ -79202,7 +79575,7 @@ var require_baggage_impl = __commonJS((exports) => {
79202
79575
  return Object.assign({}, entry);
79203
79576
  }
79204
79577
  getAllEntries() {
79205
- return Array.from(this._entries.entries());
79578
+ return Array.from(this._entries.entries()).map(([k2, v2]) => [k2, v2]);
79206
79579
  }
79207
79580
  setEntry(key, entry) {
79208
79581
  const newBaggage = new BaggageImpl(this._entries);
@@ -79294,7 +79667,7 @@ var require_context = __commonJS((exports) => {
79294
79667
  // node_modules/@opentelemetry/api/build/src/diag/consoleLogger.js
79295
79668
  var require_consoleLogger = __commonJS((exports) => {
79296
79669
  Object.defineProperty(exports, "__esModule", { value: true });
79297
- exports.DiagConsoleLogger = exports._originalConsoleMethods = undefined;
79670
+ exports.DiagConsoleLogger = undefined;
79298
79671
  var consoleMap = [
79299
79672
  { n: "error", c: "error" },
79300
79673
  { n: "warn", c: "warn" },
@@ -79302,39 +79675,19 @@ var require_consoleLogger = __commonJS((exports) => {
79302
79675
  { n: "debug", c: "debug" },
79303
79676
  { n: "verbose", c: "trace" }
79304
79677
  ];
79305
- exports._originalConsoleMethods = {};
79306
- if (typeof console !== "undefined") {
79307
- const keys = [
79308
- "error",
79309
- "warn",
79310
- "info",
79311
- "debug",
79312
- "trace",
79313
- "log"
79314
- ];
79315
- for (const key of keys) {
79316
- if (typeof console[key] === "function") {
79317
- exports._originalConsoleMethods[key] = console[key];
79318
- }
79319
- }
79320
- }
79321
79678
 
79322
79679
  class DiagConsoleLogger {
79323
79680
  constructor() {
79324
79681
  function _consoleFunc(funcName) {
79325
79682
  return function(...args) {
79326
- let theFunc = exports._originalConsoleMethods[funcName];
79327
- if (typeof theFunc !== "function") {
79328
- theFunc = exports._originalConsoleMethods["log"];
79329
- }
79330
- if (typeof theFunc !== "function" && console) {
79331
- theFunc = console[funcName];
79683
+ if (console) {
79684
+ let theFunc = console[funcName];
79332
79685
  if (typeof theFunc !== "function") {
79333
79686
  theFunc = console.log;
79334
79687
  }
79335
- }
79336
- if (typeof theFunc === "function") {
79337
- return theFunc.apply(console, args);
79688
+ if (typeof theFunc === "function") {
79689
+ return theFunc.apply(console, args);
79690
+ }
79338
79691
  }
79339
79692
  };
79340
79693
  }
@@ -79572,8 +79925,8 @@ var require_NonRecordingSpan = __commonJS((exports) => {
79572
79925
  var invalid_span_constants_1 = require_invalid_span_constants();
79573
79926
 
79574
79927
  class NonRecordingSpan {
79575
- constructor(spanContext = invalid_span_constants_1.INVALID_SPAN_CONTEXT) {
79576
- this._spanContext = spanContext;
79928
+ constructor(_spanContext = invalid_span_constants_1.INVALID_SPAN_CONTEXT) {
79929
+ this._spanContext = _spanContext;
79577
79930
  }
79578
79931
  spanContext() {
79579
79932
  return this._spanContext;
@@ -79649,126 +80002,14 @@ var require_spancontext_utils = __commonJS((exports) => {
79649
80002
  exports.wrapSpanContext = exports.isSpanContextValid = exports.isValidSpanId = exports.isValidTraceId = undefined;
79650
80003
  var invalid_span_constants_1 = require_invalid_span_constants();
79651
80004
  var NonRecordingSpan_1 = require_NonRecordingSpan();
79652
- var isHex = new Uint8Array([
79653
- 0,
79654
- 0,
79655
- 0,
79656
- 0,
79657
- 0,
79658
- 0,
79659
- 0,
79660
- 0,
79661
- 0,
79662
- 0,
79663
- 0,
79664
- 0,
79665
- 0,
79666
- 0,
79667
- 0,
79668
- 0,
79669
- 0,
79670
- 0,
79671
- 0,
79672
- 0,
79673
- 0,
79674
- 0,
79675
- 0,
79676
- 0,
79677
- 0,
79678
- 0,
79679
- 0,
79680
- 0,
79681
- 0,
79682
- 0,
79683
- 0,
79684
- 0,
79685
- 0,
79686
- 0,
79687
- 0,
79688
- 0,
79689
- 0,
79690
- 0,
79691
- 0,
79692
- 0,
79693
- 0,
79694
- 0,
79695
- 0,
79696
- 0,
79697
- 0,
79698
- 0,
79699
- 0,
79700
- 0,
79701
- 1,
79702
- 1,
79703
- 1,
79704
- 1,
79705
- 1,
79706
- 1,
79707
- 1,
79708
- 1,
79709
- 1,
79710
- 1,
79711
- 0,
79712
- 0,
79713
- 0,
79714
- 0,
79715
- 0,
79716
- 0,
79717
- 0,
79718
- 1,
79719
- 1,
79720
- 1,
79721
- 1,
79722
- 1,
79723
- 1,
79724
- 0,
79725
- 0,
79726
- 0,
79727
- 0,
79728
- 0,
79729
- 0,
79730
- 0,
79731
- 0,
79732
- 0,
79733
- 0,
79734
- 0,
79735
- 0,
79736
- 0,
79737
- 0,
79738
- 0,
79739
- 0,
79740
- 0,
79741
- 0,
79742
- 0,
79743
- 0,
79744
- 0,
79745
- 0,
79746
- 0,
79747
- 0,
79748
- 0,
79749
- 0,
79750
- 1,
79751
- 1,
79752
- 1,
79753
- 1,
79754
- 1,
79755
- 1
79756
- ]);
79757
- function isValidHex(id, length) {
79758
- if (typeof id !== "string" || id.length !== length)
79759
- return false;
79760
- let r2 = 0;
79761
- for (let i2 = 0;i2 < id.length; i2 += 4) {
79762
- r2 += (isHex[id.charCodeAt(i2)] | 0) + (isHex[id.charCodeAt(i2 + 1)] | 0) + (isHex[id.charCodeAt(i2 + 2)] | 0) + (isHex[id.charCodeAt(i2 + 3)] | 0);
79763
- }
79764
- return r2 === length;
79765
- }
80005
+ var VALID_TRACEID_REGEX = /^([0-9a-f]{32})$/i;
80006
+ var VALID_SPANID_REGEX = /^[0-9a-f]{16}$/i;
79766
80007
  function isValidTraceId(traceId) {
79767
- return isValidHex(traceId, 32) && traceId !== invalid_span_constants_1.INVALID_TRACEID;
80008
+ return VALID_TRACEID_REGEX.test(traceId) && traceId !== invalid_span_constants_1.INVALID_TRACEID;
79768
80009
  }
79769
80010
  exports.isValidTraceId = isValidTraceId;
79770
80011
  function isValidSpanId(spanId) {
79771
- return isValidHex(spanId, 16) && spanId !== invalid_span_constants_1.INVALID_SPANID;
80012
+ return VALID_SPANID_REGEX.test(spanId) && spanId !== invalid_span_constants_1.INVALID_SPANID;
79772
80013
  }
79773
80014
  exports.isValidSpanId = isValidSpanId;
79774
80015
  function isSpanContextValid(spanContext) {
@@ -79828,7 +80069,7 @@ var require_NoopTracer = __commonJS((exports) => {
79828
80069
  }
79829
80070
  exports.NoopTracer = NoopTracer;
79830
80071
  function isSpanContext(spanContext) {
79831
- return spanContext !== null && typeof spanContext === "object" && "spanId" in spanContext && typeof spanContext["spanId"] === "string" && "traceId" in spanContext && typeof spanContext["traceId"] === "string" && "traceFlags" in spanContext && typeof spanContext["traceFlags"] === "number";
80072
+ return typeof spanContext === "object" && typeof spanContext["spanId"] === "string" && typeof spanContext["traceId"] === "string" && typeof spanContext["traceFlags"] === "number";
79832
80073
  }
79833
80074
  });
79834
80075
 
@@ -79840,8 +80081,8 @@ var require_ProxyTracer = __commonJS((exports) => {
79840
80081
  var NOOP_TRACER = new NoopTracer_1.NoopTracer;
79841
80082
 
79842
80083
  class ProxyTracer {
79843
- constructor(provider, name15, version2, options) {
79844
- this._provider = provider;
80084
+ constructor(_provider, name15, version2, options) {
80085
+ this._provider = _provider;
79845
80086
  this.name = name15;
79846
80087
  this.version = version2;
79847
80088
  this.options = options;
@@ -80001,7 +80242,7 @@ var require_tracestate_impl = __commonJS((exports) => {
80001
80242
  return this._internalState.get(key);
80002
80243
  }
80003
80244
  serialize() {
80004
- return Array.from(this._internalState.keys()).reduceRight((agg, key) => {
80245
+ return this._keys().reduce((agg, key) => {
80005
80246
  agg.push(key + LIST_MEMBER_KEY_VALUE_SPLITTER + this.get(key));
80006
80247
  return agg;
80007
80248
  }, []).join(LIST_MEMBERS_SEPARATOR);
@@ -80009,7 +80250,7 @@ var require_tracestate_impl = __commonJS((exports) => {
80009
80250
  _parse(rawTraceState) {
80010
80251
  if (rawTraceState.length > MAX_TRACE_STATE_LEN)
80011
80252
  return;
80012
- this._internalState = rawTraceState.split(LIST_MEMBERS_SEPARATOR).reduceRight((agg, part) => {
80253
+ this._internalState = rawTraceState.split(LIST_MEMBERS_SEPARATOR).reverse().reduce((agg, part) => {
80013
80254
  const listMember = part.trim();
80014
80255
  const i2 = listMember.indexOf(LIST_MEMBER_KEY_VALUE_SPLITTER);
80015
80256
  if (i2 !== -1) {
@@ -81109,10 +81350,7 @@ function convertToLanguageModelMessage({
81109
81350
  type: "tool-result",
81110
81351
  toolCallId: part.toolCallId,
81111
81352
  toolName: part.toolName,
81112
- output: mapToolResultOutput({
81113
- output: part.output,
81114
- downloadedAssets
81115
- }),
81353
+ output: mapToolResultOutput(part.output),
81116
81354
  providerOptions
81117
81355
  };
81118
81356
  }
@@ -81131,10 +81369,7 @@ function convertToLanguageModelMessage({
81131
81369
  type: "tool-result",
81132
81370
  toolCallId: part.toolCallId,
81133
81371
  toolName: part.toolName,
81134
- output: mapToolResultOutput({
81135
- output: part.output,
81136
- downloadedAssets
81137
- }),
81372
+ output: mapToolResultOutput(part.output),
81138
81373
  providerOptions: part.providerOptions
81139
81374
  };
81140
81375
  }
@@ -81158,44 +81393,15 @@ function convertToLanguageModelMessage({
81158
81393
  }
81159
81394
  }
81160
81395
  async function downloadAssets(messages, download2, supportedUrls) {
81161
- var _a21;
81162
- const downloadableFiles = [];
81163
- for (const message of messages) {
81164
- if (message.role === "user" && Array.isArray(message.content)) {
81165
- for (const part of message.content) {
81166
- if (part.type === "image" || part.type === "file") {
81167
- downloadableFiles.push({
81168
- data: part.type === "image" ? part.image : part.data,
81169
- mediaType: (_a21 = part.mediaType) != null ? _a21 : part.type === "image" ? "image/*" : undefined
81170
- });
81171
- }
81172
- }
81173
- }
81174
- if (message.role === "tool" || message.role === "assistant") {
81175
- if (!Array.isArray(message.content)) {
81176
- continue;
81177
- }
81178
- for (const part of message.content) {
81179
- if (part.type !== "tool-result") {
81180
- continue;
81181
- }
81182
- if (part.output.type !== "content") {
81183
- continue;
81184
- }
81185
- for (const contentPart of part.output.value) {
81186
- if (contentPart.type === "image-url" || contentPart.type === "file-url") {
81187
- downloadableFiles.push({
81188
- data: new URL(contentPart.url),
81189
- mediaType: contentPart.type === "image-url" ? "image/*" : undefined
81190
- });
81191
- }
81192
- }
81193
- }
81396
+ const plannedDownloads = messages.filter((message) => message.role === "user").map((message) => message.content).filter((content) => Array.isArray(content)).flat().filter((part) => part.type === "image" || part.type === "file").map((part) => {
81397
+ var _a21;
81398
+ const mediaType = (_a21 = part.mediaType) != null ? _a21 : part.type === "image" ? "image/*" : undefined;
81399
+ let data = part.type === "image" ? part.image : part.data;
81400
+ if (typeof data === "string") {
81401
+ try {
81402
+ data = new URL(data);
81403
+ } catch (ignored) {}
81194
81404
  }
81195
- }
81196
- const plannedDownloads = downloadableFiles.map((part) => {
81197
- const mediaType = part.mediaType;
81198
- const { data } = convertToLanguageModelV3DataContent(part.data);
81199
81405
  return { mediaType, data };
81200
81406
  }).filter((part) => part.data instanceof URL).map((part) => ({
81201
81407
  url: part.data,
@@ -81269,41 +81475,13 @@ function convertPartToLanguageModelPart(part, downloadedAssets) {
81269
81475
  }
81270
81476
  }
81271
81477
  }
81272
- function mapToolResultOutput({
81273
- output,
81274
- downloadedAssets
81275
- }) {
81478
+ function mapToolResultOutput(output) {
81276
81479
  if (output.type !== "content") {
81277
81480
  return output;
81278
81481
  }
81279
81482
  return {
81280
81483
  type: "content",
81281
81484
  value: output.value.map((item) => {
81282
- var _a21, _b16;
81283
- if (item.type === "image-url") {
81284
- const downloadedFile = downloadedAssets[new URL(item.url).toString()];
81285
- if (downloadedFile) {
81286
- return {
81287
- type: "image-data",
81288
- data: convertDataContentToBase64String(downloadedFile.data),
81289
- mediaType: (_a21 = downloadedFile.mediaType) != null ? _a21 : "image/*",
81290
- providerOptions: item.providerOptions
81291
- };
81292
- }
81293
- return item;
81294
- }
81295
- if (item.type === "file-url") {
81296
- const downloadedFile = downloadedAssets[new URL(item.url).toString()];
81297
- if (downloadedFile) {
81298
- return {
81299
- type: "file-data",
81300
- data: convertDataContentToBase64String(downloadedFile.data),
81301
- mediaType: (_b16 = downloadedFile.mediaType) != null ? _b16 : "application/octet-stream",
81302
- providerOptions: item.providerOptions
81303
- };
81304
- }
81305
- return item;
81306
- }
81307
81485
  if (item.type !== "media") {
81308
81486
  return item;
81309
81487
  }
@@ -81856,7 +82034,7 @@ function getRetryDelayInMs({
81856
82034
  error: error40,
81857
82035
  exponentialBackoffDelay
81858
82036
  }) {
81859
- const headers = APICallError.isInstance(error40) ? error40.responseHeaders : APICallError.isInstance(error40.cause) ? error40.cause.responseHeaders : undefined;
82037
+ const headers = error40.responseHeaders;
81860
82038
  if (!headers)
81861
82039
  return exponentialBackoffDelay;
81862
82040
  let ms;
@@ -81906,7 +82084,7 @@ async function _retryWithExponentialBackoff(f2, {
81906
82084
  errors: newErrors
81907
82085
  });
81908
82086
  }
81909
- if (error40 instanceof Error && (APICallError.isInstance(error40) && error40.isRetryable === true || GatewayError.isInstance(error40) && error40.isRetryable === true) && tryNumber <= maxRetries) {
82087
+ if (error40 instanceof Error && APICallError.isInstance(error40) && error40.isRetryable === true && tryNumber <= maxRetries) {
81910
82088
  await delay(getRetryDelayInMs({
81911
82089
  error: error40,
81912
82090
  exponentialBackoffDelay: delayInMs
@@ -82124,8 +82302,7 @@ async function executeToolCall({
82124
82302
  input,
82125
82303
  error: error40,
82126
82304
  dynamic: tool2.type === "dynamic",
82127
- ...toolCall.providerMetadata != null ? { providerMetadata: toolCall.providerMetadata } : {},
82128
- ...toolCall.toolMetadata != null ? { toolMetadata: toolCall.toolMetadata } : {}
82305
+ ...toolCall.providerMetadata != null ? { providerMetadata: toolCall.providerMetadata } : {}
82129
82306
  };
82130
82307
  }
82131
82308
  const durationMs = now3() - startTime;
@@ -82155,8 +82332,7 @@ async function executeToolCall({
82155
82332
  input,
82156
82333
  output,
82157
82334
  dynamic: tool2.type === "dynamic",
82158
- ...toolCall.providerMetadata != null ? { providerMetadata: toolCall.providerMetadata } : {},
82159
- ...toolCall.toolMetadata != null ? { toolMetadata: toolCall.toolMetadata } : {}
82335
+ ...toolCall.providerMetadata != null ? { providerMetadata: toolCall.providerMetadata } : {}
82160
82336
  };
82161
82337
  }
82162
82338
  });
@@ -82521,6 +82697,15 @@ async function parsePartialJson(jsonText) {
82521
82697
  }
82522
82698
  return { value: undefined, state: "failed-parse" };
82523
82699
  }
82700
+ function mergeToolProviderMetadata(toolMetadata, callMetadata) {
82701
+ if (toolMetadata == null) {
82702
+ return callMetadata;
82703
+ }
82704
+ if (callMetadata == null) {
82705
+ return toolMetadata;
82706
+ }
82707
+ return { ...toolMetadata, ...callMetadata };
82708
+ }
82524
82709
  async function parseToolCall({
82525
82710
  toolCall,
82526
82711
  tools,
@@ -82528,6 +82713,7 @@ async function parseToolCall({
82528
82713
  system,
82529
82714
  messages
82530
82715
  }) {
82716
+ var _a21, _b16;
82531
82717
  try {
82532
82718
  if (tools == null) {
82533
82719
  if (toolCall.providerExecuted && toolCall.dynamic) {
@@ -82568,7 +82754,6 @@ async function parseToolCall({
82568
82754
  } catch (error40) {
82569
82755
  const parsedInput = await safeParseJSON({ text: toolCall.input });
82570
82756
  const input = parsedInput.success ? parsedInput.value : toolCall.input;
82571
- const tool2 = tools == null ? undefined : tools[toolCall.toolName];
82572
82757
  return {
82573
82758
  type: "tool-call",
82574
82759
  toolCallId: toolCall.toolCallId,
@@ -82577,10 +82762,9 @@ async function parseToolCall({
82577
82762
  dynamic: true,
82578
82763
  invalid: true,
82579
82764
  error: error40,
82580
- title: tool2 == null ? undefined : tool2.title,
82765
+ title: (_a21 = tools == null ? undefined : tools[toolCall.toolName]) == null ? undefined : _a21.title,
82581
82766
  providerExecuted: toolCall.providerExecuted,
82582
- providerMetadata: toolCall.providerMetadata,
82583
- ...(tool2 == null ? undefined : tool2.metadata) != null ? { toolMetadata: tool2.metadata } : {}
82767
+ providerMetadata: mergeToolProviderMetadata((_b16 = tools == null ? undefined : tools[toolCall.toolName]) == null ? undefined : _b16.providerMetadata, toolCall.providerMetadata)
82584
82768
  };
82585
82769
  }
82586
82770
  }
@@ -82627,14 +82811,14 @@ async function doParseToolCall({
82627
82811
  cause: parseResult.error
82628
82812
  });
82629
82813
  }
82814
+ const mergedProviderMetadata = mergeToolProviderMetadata(tool2.providerMetadata, toolCall.providerMetadata);
82630
82815
  return tool2.type === "dynamic" ? {
82631
82816
  type: "tool-call",
82632
82817
  toolCallId: toolCall.toolCallId,
82633
82818
  toolName: toolCall.toolName,
82634
82819
  input: parseResult.value,
82635
82820
  providerExecuted: toolCall.providerExecuted,
82636
- providerMetadata: toolCall.providerMetadata,
82637
- ...tool2.metadata != null ? { toolMetadata: tool2.metadata } : {},
82821
+ providerMetadata: mergedProviderMetadata,
82638
82822
  dynamic: true,
82639
82823
  title: tool2.title
82640
82824
  } : {
@@ -82643,8 +82827,7 @@ async function doParseToolCall({
82643
82827
  toolName,
82644
82828
  input: parseResult.value,
82645
82829
  providerExecuted: toolCall.providerExecuted,
82646
- providerMetadata: toolCall.providerMetadata,
82647
- ...tool2.metadata != null ? { toolMetadata: tool2.metadata } : {},
82830
+ providerMetadata: mergedProviderMetadata,
82648
82831
  title: tool2.title
82649
82832
  };
82650
82833
  }
@@ -83476,8 +83659,7 @@ function asContent({
83476
83659
  error: part.result,
83477
83660
  providerExecuted: true,
83478
83661
  dynamic: part.dynamic,
83479
- ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
83480
- ...(tool2 == null ? undefined : tool2.metadata) != null ? { toolMetadata: tool2.metadata } : {}
83662
+ ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
83481
83663
  });
83482
83664
  } else {
83483
83665
  contentParts.push({
@@ -83488,8 +83670,7 @@ function asContent({
83488
83670
  output: part.result,
83489
83671
  providerExecuted: true,
83490
83672
  dynamic: part.dynamic,
83491
- ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
83492
- ...(tool2 == null ? undefined : tool2.metadata) != null ? { toolMetadata: tool2.metadata } : {}
83673
+ ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
83493
83674
  });
83494
83675
  }
83495
83676
  break;
@@ -83503,8 +83684,7 @@ function asContent({
83503
83684
  error: part.result,
83504
83685
  providerExecuted: true,
83505
83686
  dynamic: toolCall.dynamic,
83506
- ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
83507
- ...toolCall.toolMetadata != null ? { toolMetadata: toolCall.toolMetadata } : {}
83687
+ ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
83508
83688
  });
83509
83689
  } else {
83510
83690
  contentParts.push({
@@ -83515,8 +83695,7 @@ function asContent({
83515
83695
  output: part.result,
83516
83696
  providerExecuted: true,
83517
83697
  dynamic: toolCall.dynamic,
83518
- ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
83519
- ...toolCall.toolMetadata != null ? { toolMetadata: toolCall.toolMetadata } : {}
83698
+ ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
83520
83699
  });
83521
83700
  }
83522
83701
  break;
@@ -83730,9 +83909,6 @@ function processUIMessageStream({
83730
83909
  if (options.title !== undefined) {
83731
83910
  anyPart.title = options.title;
83732
83911
  }
83733
- if (options.toolMetadata !== undefined) {
83734
- anyPart.toolMetadata = options.toolMetadata;
83735
- }
83736
83912
  anyPart.providerExecuted = (_a222 = anyOptions.providerExecuted) != null ? _a222 : part.providerExecuted;
83737
83913
  const providerMetadata = anyOptions.providerMetadata;
83738
83914
  if (providerMetadata != null) {
@@ -83749,7 +83925,6 @@ function processUIMessageStream({
83749
83925
  toolCallId: options.toolCallId,
83750
83926
  state: options.state,
83751
83927
  title: options.title,
83752
- ...options.toolMetadata !== undefined ? { toolMetadata: options.toolMetadata } : {},
83753
83928
  input: anyOptions.input,
83754
83929
  output: anyOptions.output,
83755
83930
  rawInput: anyOptions.rawInput,
@@ -83777,9 +83952,6 @@ function processUIMessageStream({
83777
83952
  if (options.title !== undefined) {
83778
83953
  anyPart.title = options.title;
83779
83954
  }
83780
- if (options.toolMetadata !== undefined) {
83781
- anyPart.toolMetadata = options.toolMetadata;
83782
- }
83783
83955
  anyPart.providerExecuted = (_b23 = anyOptions.providerExecuted) != null ? _b23 : part.providerExecuted;
83784
83956
  const providerMetadata = anyOptions.providerMetadata;
83785
83957
  if (providerMetadata != null) {
@@ -83802,7 +83974,6 @@ function processUIMessageStream({
83802
83974
  preliminary: anyOptions.preliminary,
83803
83975
  providerExecuted: anyOptions.providerExecuted,
83804
83976
  title: options.title,
83805
- ...options.toolMetadata !== undefined ? { toolMetadata: options.toolMetadata } : {},
83806
83977
  ...anyOptions.providerMetadata != null && (options.state === "output-available" || options.state === "output-error") ? { resultProviderMetadata: anyOptions.providerMetadata } : {},
83807
83978
  ...anyOptions.providerMetadata != null && !(options.state === "output-available" || options.state === "output-error") ? { callProviderMetadata: anyOptions.providerMetadata } : {}
83808
83979
  });
@@ -83947,8 +84118,7 @@ function processUIMessageStream({
83947
84118
  toolName: chunk.toolName,
83948
84119
  index: toolInvocations.length,
83949
84120
  dynamic: chunk.dynamic,
83950
- title: chunk.title,
83951
- toolMetadata: chunk.toolMetadata
84121
+ title: chunk.title
83952
84122
  };
83953
84123
  if (chunk.dynamic) {
83954
84124
  updateDynamicToolPart({
@@ -83958,7 +84128,6 @@ function processUIMessageStream({
83958
84128
  input: undefined,
83959
84129
  providerExecuted: chunk.providerExecuted,
83960
84130
  title: chunk.title,
83961
- toolMetadata: chunk.toolMetadata,
83962
84131
  providerMetadata: chunk.providerMetadata
83963
84132
  });
83964
84133
  } else {
@@ -83969,7 +84138,6 @@ function processUIMessageStream({
83969
84138
  input: undefined,
83970
84139
  providerExecuted: chunk.providerExecuted,
83971
84140
  title: chunk.title,
83972
- toolMetadata: chunk.toolMetadata,
83973
84141
  providerMetadata: chunk.providerMetadata
83974
84142
  });
83975
84143
  }
@@ -83993,8 +84161,7 @@ function processUIMessageStream({
83993
84161
  toolName: partialToolCall.toolName,
83994
84162
  state: "input-streaming",
83995
84163
  input: partialArgs,
83996
- title: partialToolCall.title,
83997
- toolMetadata: partialToolCall.toolMetadata
84164
+ title: partialToolCall.title
83998
84165
  });
83999
84166
  } else {
84000
84167
  updateToolPart({
@@ -84002,8 +84169,7 @@ function processUIMessageStream({
84002
84169
  toolName: partialToolCall.toolName,
84003
84170
  state: "input-streaming",
84004
84171
  input: partialArgs,
84005
- title: partialToolCall.title,
84006
- toolMetadata: partialToolCall.toolMetadata
84172
+ title: partialToolCall.title
84007
84173
  });
84008
84174
  }
84009
84175
  write();
@@ -84018,8 +84184,7 @@ function processUIMessageStream({
84018
84184
  input: chunk.input,
84019
84185
  providerExecuted: chunk.providerExecuted,
84020
84186
  providerMetadata: chunk.providerMetadata,
84021
- title: chunk.title,
84022
- toolMetadata: chunk.toolMetadata
84187
+ title: chunk.title
84023
84188
  });
84024
84189
  } else {
84025
84190
  updateToolPart({
@@ -84029,8 +84194,7 @@ function processUIMessageStream({
84029
84194
  input: chunk.input,
84030
84195
  providerExecuted: chunk.providerExecuted,
84031
84196
  providerMetadata: chunk.providerMetadata,
84032
- title: chunk.title,
84033
- toolMetadata: chunk.toolMetadata
84197
+ title: chunk.title
84034
84198
  });
84035
84199
  }
84036
84200
  write();
@@ -84052,8 +84216,7 @@ function processUIMessageStream({
84052
84216
  input: chunk.input,
84053
84217
  errorText: chunk.errorText,
84054
84218
  providerExecuted: chunk.providerExecuted,
84055
- providerMetadata: chunk.providerMetadata,
84056
- toolMetadata: chunk.toolMetadata
84219
+ providerMetadata: chunk.providerMetadata
84057
84220
  });
84058
84221
  } else {
84059
84222
  updateToolPart({
@@ -84064,8 +84227,7 @@ function processUIMessageStream({
84064
84227
  rawInput: chunk.input,
84065
84228
  errorText: chunk.errorText,
84066
84229
  providerExecuted: chunk.providerExecuted,
84067
- providerMetadata: chunk.providerMetadata,
84068
- toolMetadata: chunk.toolMetadata
84230
+ providerMetadata: chunk.providerMetadata
84069
84231
  });
84070
84232
  }
84071
84233
  write();
@@ -84096,8 +84258,7 @@ function processUIMessageStream({
84096
84258
  preliminary: chunk.preliminary,
84097
84259
  providerExecuted: chunk.providerExecuted,
84098
84260
  providerMetadata: chunk.providerMetadata,
84099
- title: toolInvocation.title,
84100
- toolMetadata: toolInvocation.toolMetadata
84261
+ title: toolInvocation.title
84101
84262
  });
84102
84263
  } else {
84103
84264
  updateToolPart({
@@ -84109,8 +84270,7 @@ function processUIMessageStream({
84109
84270
  providerExecuted: chunk.providerExecuted,
84110
84271
  preliminary: chunk.preliminary,
84111
84272
  providerMetadata: chunk.providerMetadata,
84112
- title: toolInvocation.title,
84113
- toolMetadata: toolInvocation.toolMetadata
84273
+ title: toolInvocation.title
84114
84274
  });
84115
84275
  }
84116
84276
  write();
@@ -84127,8 +84287,7 @@ function processUIMessageStream({
84127
84287
  errorText: chunk.errorText,
84128
84288
  providerExecuted: chunk.providerExecuted,
84129
84289
  providerMetadata: chunk.providerMetadata,
84130
- title: toolInvocation.title,
84131
- toolMetadata: toolInvocation.toolMetadata
84290
+ title: toolInvocation.title
84132
84291
  });
84133
84292
  } else {
84134
84293
  updateToolPart({
@@ -84140,8 +84299,7 @@ function processUIMessageStream({
84140
84299
  errorText: chunk.errorText,
84141
84300
  providerExecuted: chunk.providerExecuted,
84142
84301
  providerMetadata: chunk.providerMetadata,
84143
- title: toolInvocation.title,
84144
- toolMetadata: toolInvocation.toolMetadata
84302
+ title: toolInvocation.title
84145
84303
  });
84146
84304
  }
84147
84305
  write();
@@ -84599,8 +84757,7 @@ function runToolsTransformation({
84599
84757
  input: toolCall.input,
84600
84758
  error: getErrorMessage2(toolCall.error),
84601
84759
  dynamic: true,
84602
- title: toolCall.title,
84603
- ...toolCall.toolMetadata != null ? { toolMetadata: toolCall.toolMetadata } : {}
84760
+ title: toolCall.title
84604
84761
  });
84605
84762
  break;
84606
84763
  }
@@ -84668,7 +84825,6 @@ function runToolsTransformation({
84668
84825
  }
84669
84826
  case "tool-result": {
84670
84827
  const toolName = chunk.toolName;
84671
- const toolCall = toolCallsByToolCallId.get(chunk.toolCallId);
84672
84828
  if (chunk.isError) {
84673
84829
  toolResultsStreamController.enqueue({
84674
84830
  type: "tool-error",
@@ -84678,8 +84834,7 @@ function runToolsTransformation({
84678
84834
  providerExecuted: true,
84679
84835
  error: chunk.result,
84680
84836
  dynamic: chunk.dynamic,
84681
- ...chunk.providerMetadata != null ? { providerMetadata: chunk.providerMetadata } : {},
84682
- ...(toolCall == null ? undefined : toolCall.toolMetadata) != null ? { toolMetadata: toolCall.toolMetadata } : {}
84837
+ ...chunk.providerMetadata != null ? { providerMetadata: chunk.providerMetadata } : {}
84683
84838
  });
84684
84839
  } else {
84685
84840
  controller.enqueue({
@@ -84690,8 +84845,7 @@ function runToolsTransformation({
84690
84845
  output: chunk.result,
84691
84846
  providerExecuted: true,
84692
84847
  dynamic: chunk.dynamic,
84693
- ...chunk.providerMetadata != null ? { providerMetadata: chunk.providerMetadata } : {},
84694
- ...(toolCall == null ? undefined : toolCall.toolMetadata) != null ? { toolMetadata: toolCall.toolMetadata } : {}
84848
+ ...chunk.providerMetadata != null ? { providerMetadata: chunk.providerMetadata } : {}
84695
84849
  });
84696
84850
  }
84697
84851
  break;
@@ -85474,7 +85628,7 @@ async function embed({
85474
85628
  }),
85475
85629
  tracer,
85476
85630
  fn: async (doEmbedSpan) => {
85477
- var _a21, _b16;
85631
+ var _a21;
85478
85632
  const modelResponse = await model.doEmbed({
85479
85633
  values: [value],
85480
85634
  abortSignal,
@@ -85495,7 +85649,7 @@ async function embed({
85495
85649
  return {
85496
85650
  embedding: embedding2,
85497
85651
  usage: usage2,
85498
- warnings: (_b16 = modelResponse.warnings) != null ? _b16 : [],
85652
+ warnings: modelResponse.warnings,
85499
85653
  providerMetadata: modelResponse.providerMetadata,
85500
85654
  response: modelResponse.response
85501
85655
  };
@@ -85591,7 +85745,7 @@ async function embedMany({
85591
85745
  }),
85592
85746
  tracer,
85593
85747
  fn: async (doEmbedSpan) => {
85594
- var _a222, _b16;
85748
+ var _a222;
85595
85749
  const modelResponse = await model.doEmbed({
85596
85750
  values,
85597
85751
  abortSignal,
@@ -85612,7 +85766,7 @@ async function embedMany({
85612
85766
  return {
85613
85767
  embeddings: embeddings3,
85614
85768
  usage: usage2,
85615
- warnings: (_b16 = modelResponse.warnings) != null ? _b16 : [],
85769
+ warnings: modelResponse.warnings,
85616
85770
  providerMetadata: modelResponse.providerMetadata,
85617
85771
  response: modelResponse.response
85618
85772
  };
@@ -85669,7 +85823,7 @@ async function embedMany({
85669
85823
  }),
85670
85824
  tracer,
85671
85825
  fn: async (doEmbedSpan) => {
85672
- var _a222, _b16;
85826
+ var _a222;
85673
85827
  const modelResponse = await model.doEmbed({
85674
85828
  values: chunk,
85675
85829
  abortSignal,
@@ -85690,7 +85844,7 @@ async function embedMany({
85690
85844
  return {
85691
85845
  embeddings: embeddings2,
85692
85846
  usage,
85693
- warnings: (_b16 = modelResponse.warnings) != null ? _b16 : [],
85847
+ warnings: modelResponse.warnings,
85694
85848
  providerMetadata: modelResponse.providerMetadata,
85695
85849
  response: modelResponse.response
85696
85850
  };
@@ -87804,7 +87958,7 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
87804
87958
  const bytes = typeof data === "string" ? convertBase64ToUint8Array(data) : data;
87805
87959
  const id3Size = (bytes[6] & 127) << 21 | (bytes[7] & 127) << 14 | (bytes[8] & 127) << 7 | bytes[9] & 127;
87806
87960
  return bytes.slice(id3Size + 10);
87807
- }, VERSION3 = "6.0.191", download = async ({
87961
+ }, VERSION3 = "6.0.175", download = async ({
87808
87962
  url: url2,
87809
87963
  maxBytes,
87810
87964
  abortSignal
@@ -87894,7 +88048,7 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
87894
88048
  const schema = asSchema(inputSchema);
87895
88049
  return {
87896
88050
  name: "object",
87897
- responseFormat: resolve4(schema.jsonSchema).then((jsonSchema2) => ({
88051
+ responseFormat: resolve3(schema.jsonSchema).then((jsonSchema2) => ({
87898
88052
  type: "json",
87899
88053
  schema: jsonSchema2,
87900
88054
  ...name21 != null && { name: name21 },
@@ -87955,7 +88109,7 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
87955
88109
  const elementSchema = asSchema(inputElementSchema);
87956
88110
  return {
87957
88111
  name: "array",
87958
- responseFormat: resolve4(elementSchema.jsonSchema).then((jsonSchema2) => {
88112
+ responseFormat: resolve3(elementSchema.jsonSchema).then((jsonSchema2) => {
87959
88113
  const { $schema, ...itemSchema } = jsonSchema2;
87960
88114
  return {
87961
88115
  type: "json",
@@ -88312,7 +88466,7 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
88312
88466
  }
88313
88467
  return this._output;
88314
88468
  }
88315
- }, JsonToSseTransformStream, UI_MESSAGE_STREAM_HEADERS, toolMetadataSchema, uiMessageChunkSchema, isToolOrDynamicToolUIPart, getToolOrDynamicToolName, originalGenerateId2, DefaultStreamTextResult = class {
88469
+ }, JsonToSseTransformStream, UI_MESSAGE_STREAM_HEADERS, uiMessageChunkSchema, isToolOrDynamicToolUIPart, getToolOrDynamicToolName, originalGenerateId2, DefaultStreamTextResult = class {
88316
88470
  constructor({
88317
88471
  model,
88318
88472
  telemetry,
@@ -89560,7 +89714,6 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
89560
89714
  toolName: part.toolName,
89561
89715
  ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
89562
89716
  ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
89563
- ...part.toolMetadata != null ? { toolMetadata: part.toolMetadata } : {},
89564
89717
  ...dynamic != null ? { dynamic } : {},
89565
89718
  ...part.title != null ? { title: part.title } : {}
89566
89719
  });
@@ -89584,7 +89737,6 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
89584
89737
  input: part.input,
89585
89738
  ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
89586
89739
  ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
89587
- ...part.toolMetadata != null ? { toolMetadata: part.toolMetadata } : {},
89588
89740
  ...dynamic != null ? { dynamic } : {},
89589
89741
  errorText: onError(part.error),
89590
89742
  ...part.title != null ? { title: part.title } : {}
@@ -89597,7 +89749,6 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
89597
89749
  input: part.input,
89598
89750
  ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
89599
89751
  ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
89600
- ...part.toolMetadata != null ? { toolMetadata: part.toolMetadata } : {},
89601
89752
  ...dynamic != null ? { dynamic } : {},
89602
89753
  ...part.title != null ? { title: part.title } : {}
89603
89754
  });
@@ -89620,7 +89771,6 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
89620
89771
  output: part.output,
89621
89772
  ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
89622
89773
  ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
89623
- ...part.toolMetadata != null ? { toolMetadata: part.toolMetadata } : {},
89624
89774
  ...part.preliminary != null ? { preliminary: part.preliminary } : {},
89625
89775
  ...dynamic != null ? { dynamic } : {}
89626
89776
  });
@@ -89634,7 +89784,6 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
89634
89784
  errorText: part.providerExecuted ? typeof part.error === "string" ? part.error : JSON.stringify(part.error) : onError(part.error),
89635
89785
  ...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
89636
89786
  ...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
89637
- ...part.toolMetadata != null ? { toolMetadata: part.toolMetadata } : {},
89638
89787
  ...dynamic != null ? { dynamic } : {}
89639
89788
  });
89640
89789
  break;
@@ -89808,21 +89957,10 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
89808
89957
  ...options
89809
89958
  };
89810
89959
  const preparedCallArgs = (_d2 = await ((_c2 = (_b16 = this.settings).prepareCall) == null ? undefined : _c2.call(_b16, baseCallArgs))) != null ? _d2 : baseCallArgs;
89811
- const {
89812
- instructions,
89813
- allowSystemInMessages,
89814
- messages,
89815
- prompt,
89816
- ...callArgs
89817
- } = preparedCallArgs;
89960
+ const { instructions, messages, prompt, ...callArgs } = preparedCallArgs;
89818
89961
  return {
89819
89962
  ...callArgs,
89820
- ...{
89821
- system: instructions,
89822
- allowSystemInMessages,
89823
- messages,
89824
- prompt
89825
- }
89963
+ ...{ system: instructions, messages, prompt }
89826
89964
  };
89827
89965
  }
89828
89966
  mergeOnStepFinishCallbacks(methodCallback) {
@@ -89863,7 +90001,7 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
89863
90001
  onStepFinish: this.mergeOnStepFinishCallbacks(onStepFinish)
89864
90002
  });
89865
90003
  }
89866
- }, toolMetadataSchema2, uiMessagesSchema, DefaultEmbedResult = class {
90004
+ }, uiMessagesSchema, DefaultEmbedResult = class {
89867
90005
  constructor(options) {
89868
90006
  this.value = options.value;
89869
90007
  this.embedding = options.embedding;
@@ -90868,9 +91006,9 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
90868
91006
  ...options
90869
91007
  }) {
90870
91008
  var _a21, _b16, _c2, _d2, _e2;
90871
- const resolvedBody = await resolve4(this.body);
90872
- const resolvedHeaders = await resolve4(this.headers);
90873
- const resolvedCredentials = await resolve4(this.credentials);
91009
+ const resolvedBody = await resolve3(this.body);
91010
+ const resolvedHeaders = await resolve3(this.headers);
91011
+ const resolvedCredentials = await resolve3(this.credentials);
90874
91012
  const baseHeaders = {
90875
91013
  ...normalizeHeaders(resolvedHeaders),
90876
91014
  ...normalizeHeaders(options.headers)
@@ -90918,9 +91056,9 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
90918
91056
  }
90919
91057
  async reconnectToStream(options) {
90920
91058
  var _a21, _b16, _c2, _d2, _e2;
90921
- const resolvedBody = await resolve4(this.body);
90922
- const resolvedHeaders = await resolve4(this.headers);
90923
- const resolvedCredentials = await resolve4(this.credentials);
91059
+ const resolvedBody = await resolve3(this.body);
91060
+ const resolvedHeaders = await resolve3(this.headers);
91061
+ const resolvedCredentials = await resolve3(this.credentials);
90924
91062
  const baseHeaders = {
90925
91063
  ...normalizeHeaders(resolvedHeaders),
90926
91064
  ...normalizeHeaders(options.headers)
@@ -91371,7 +91509,6 @@ var init_dist7 = __esm(() => {
91371
91509
  init_dist6();
91372
91510
  init_dist3();
91373
91511
  init_dist3();
91374
- init_dist6();
91375
91512
  init_dist5();
91376
91513
  init_dist5();
91377
91514
  init_dist5();
@@ -92231,7 +92368,6 @@ var init_dist7 = __esm(() => {
92231
92368
  "x-vercel-ai-ui-message-stream": "v1",
92232
92369
  "x-accel-buffering": "no"
92233
92370
  };
92234
- toolMetadataSchema = exports_external2.record(exports_external2.string(), jsonValueSchema.optional());
92235
92371
  uiMessageChunkSchema = lazySchema(() => zodSchema(exports_external2.union([
92236
92372
  exports_external2.strictObject({
92237
92373
  type: exports_external2.literal("text-start"),
@@ -92259,7 +92395,6 @@ var init_dist7 = __esm(() => {
92259
92395
  toolName: exports_external2.string(),
92260
92396
  providerExecuted: exports_external2.boolean().optional(),
92261
92397
  providerMetadata: providerMetadataSchema.optional(),
92262
- toolMetadata: toolMetadataSchema.optional(),
92263
92398
  dynamic: exports_external2.boolean().optional(),
92264
92399
  title: exports_external2.string().optional()
92265
92400
  }),
@@ -92275,7 +92410,6 @@ var init_dist7 = __esm(() => {
92275
92410
  input: exports_external2.unknown(),
92276
92411
  providerExecuted: exports_external2.boolean().optional(),
92277
92412
  providerMetadata: providerMetadataSchema.optional(),
92278
- toolMetadata: toolMetadataSchema.optional(),
92279
92413
  dynamic: exports_external2.boolean().optional(),
92280
92414
  title: exports_external2.string().optional()
92281
92415
  }),
@@ -92286,7 +92420,6 @@ var init_dist7 = __esm(() => {
92286
92420
  input: exports_external2.unknown(),
92287
92421
  providerExecuted: exports_external2.boolean().optional(),
92288
92422
  providerMetadata: providerMetadataSchema.optional(),
92289
- toolMetadata: toolMetadataSchema.optional(),
92290
92423
  dynamic: exports_external2.boolean().optional(),
92291
92424
  errorText: exports_external2.string(),
92292
92425
  title: exports_external2.string().optional()
@@ -92302,7 +92435,6 @@ var init_dist7 = __esm(() => {
92302
92435
  output: exports_external2.unknown(),
92303
92436
  providerExecuted: exports_external2.boolean().optional(),
92304
92437
  providerMetadata: providerMetadataSchema.optional(),
92305
- toolMetadata: toolMetadataSchema.optional(),
92306
92438
  dynamic: exports_external2.boolean().optional(),
92307
92439
  preliminary: exports_external2.boolean().optional()
92308
92440
  }),
@@ -92312,7 +92444,6 @@ var init_dist7 = __esm(() => {
92312
92444
  errorText: exports_external2.string(),
92313
92445
  providerExecuted: exports_external2.boolean().optional(),
92314
92446
  providerMetadata: providerMetadataSchema.optional(),
92315
- toolMetadata: toolMetadataSchema.optional(),
92316
92447
  dynamic: exports_external2.boolean().optional()
92317
92448
  }),
92318
92449
  exports_external2.strictObject({
@@ -92400,7 +92531,6 @@ var init_dist7 = __esm(() => {
92400
92531
  prefix: "aitxt",
92401
92532
  size: 24
92402
92533
  });
92403
- toolMetadataSchema2 = exports_external2.record(exports_external2.string(), jsonValueSchema.optional());
92404
92534
  uiMessagesSchema = lazySchema(() => zodSchema(exports_external2.array(exports_external2.object({
92405
92535
  id: exports_external2.string(),
92406
92536
  role: exports_external2.enum(["system", "user", "assistant"]),
@@ -92452,7 +92582,6 @@ var init_dist7 = __esm(() => {
92452
92582
  type: exports_external2.literal("dynamic-tool"),
92453
92583
  toolName: exports_external2.string(),
92454
92584
  toolCallId: exports_external2.string(),
92455
- toolMetadata: toolMetadataSchema2.optional(),
92456
92585
  state: exports_external2.literal("input-streaming"),
92457
92586
  input: exports_external2.unknown().optional(),
92458
92587
  providerExecuted: exports_external2.boolean().optional(),
@@ -92465,7 +92594,6 @@ var init_dist7 = __esm(() => {
92465
92594
  type: exports_external2.literal("dynamic-tool"),
92466
92595
  toolName: exports_external2.string(),
92467
92596
  toolCallId: exports_external2.string(),
92468
- toolMetadata: toolMetadataSchema2.optional(),
92469
92597
  state: exports_external2.literal("input-available"),
92470
92598
  input: exports_external2.unknown(),
92471
92599
  providerExecuted: exports_external2.boolean().optional(),
@@ -92478,7 +92606,6 @@ var init_dist7 = __esm(() => {
92478
92606
  type: exports_external2.literal("dynamic-tool"),
92479
92607
  toolName: exports_external2.string(),
92480
92608
  toolCallId: exports_external2.string(),
92481
- toolMetadata: toolMetadataSchema2.optional(),
92482
92609
  state: exports_external2.literal("approval-requested"),
92483
92610
  input: exports_external2.unknown(),
92484
92611
  providerExecuted: exports_external2.boolean().optional(),
@@ -92495,7 +92622,6 @@ var init_dist7 = __esm(() => {
92495
92622
  type: exports_external2.literal("dynamic-tool"),
92496
92623
  toolName: exports_external2.string(),
92497
92624
  toolCallId: exports_external2.string(),
92498
- toolMetadata: toolMetadataSchema2.optional(),
92499
92625
  state: exports_external2.literal("approval-responded"),
92500
92626
  input: exports_external2.unknown(),
92501
92627
  providerExecuted: exports_external2.boolean().optional(),
@@ -92512,7 +92638,6 @@ var init_dist7 = __esm(() => {
92512
92638
  type: exports_external2.literal("dynamic-tool"),
92513
92639
  toolName: exports_external2.string(),
92514
92640
  toolCallId: exports_external2.string(),
92515
- toolMetadata: toolMetadataSchema2.optional(),
92516
92641
  state: exports_external2.literal("output-available"),
92517
92642
  input: exports_external2.unknown(),
92518
92643
  providerExecuted: exports_external2.boolean().optional(),
@@ -92531,9 +92656,8 @@ var init_dist7 = __esm(() => {
92531
92656
  type: exports_external2.literal("dynamic-tool"),
92532
92657
  toolName: exports_external2.string(),
92533
92658
  toolCallId: exports_external2.string(),
92534
- toolMetadata: toolMetadataSchema2.optional(),
92535
92659
  state: exports_external2.literal("output-error"),
92536
- input: exports_external2.unknown().optional(),
92660
+ input: exports_external2.unknown(),
92537
92661
  rawInput: exports_external2.unknown().optional(),
92538
92662
  providerExecuted: exports_external2.boolean().optional(),
92539
92663
  output: exports_external2.never().optional(),
@@ -92550,7 +92674,6 @@ var init_dist7 = __esm(() => {
92550
92674
  type: exports_external2.literal("dynamic-tool"),
92551
92675
  toolName: exports_external2.string(),
92552
92676
  toolCallId: exports_external2.string(),
92553
- toolMetadata: toolMetadataSchema2.optional(),
92554
92677
  state: exports_external2.literal("output-denied"),
92555
92678
  input: exports_external2.unknown(),
92556
92679
  providerExecuted: exports_external2.boolean().optional(),
@@ -92566,7 +92689,6 @@ var init_dist7 = __esm(() => {
92566
92689
  exports_external2.object({
92567
92690
  type: exports_external2.string().startsWith("tool-"),
92568
92691
  toolCallId: exports_external2.string(),
92569
- toolMetadata: toolMetadataSchema2.optional(),
92570
92692
  state: exports_external2.literal("input-streaming"),
92571
92693
  providerExecuted: exports_external2.boolean().optional(),
92572
92694
  callProviderMetadata: providerMetadataSchema.optional(),
@@ -92578,7 +92700,6 @@ var init_dist7 = __esm(() => {
92578
92700
  exports_external2.object({
92579
92701
  type: exports_external2.string().startsWith("tool-"),
92580
92702
  toolCallId: exports_external2.string(),
92581
- toolMetadata: toolMetadataSchema2.optional(),
92582
92703
  state: exports_external2.literal("input-available"),
92583
92704
  providerExecuted: exports_external2.boolean().optional(),
92584
92705
  input: exports_external2.unknown(),
@@ -92590,7 +92711,6 @@ var init_dist7 = __esm(() => {
92590
92711
  exports_external2.object({
92591
92712
  type: exports_external2.string().startsWith("tool-"),
92592
92713
  toolCallId: exports_external2.string(),
92593
- toolMetadata: toolMetadataSchema2.optional(),
92594
92714
  state: exports_external2.literal("approval-requested"),
92595
92715
  input: exports_external2.unknown(),
92596
92716
  providerExecuted: exports_external2.boolean().optional(),
@@ -92606,7 +92726,6 @@ var init_dist7 = __esm(() => {
92606
92726
  exports_external2.object({
92607
92727
  type: exports_external2.string().startsWith("tool-"),
92608
92728
  toolCallId: exports_external2.string(),
92609
- toolMetadata: toolMetadataSchema2.optional(),
92610
92729
  state: exports_external2.literal("approval-responded"),
92611
92730
  input: exports_external2.unknown(),
92612
92731
  providerExecuted: exports_external2.boolean().optional(),
@@ -92622,7 +92741,6 @@ var init_dist7 = __esm(() => {
92622
92741
  exports_external2.object({
92623
92742
  type: exports_external2.string().startsWith("tool-"),
92624
92743
  toolCallId: exports_external2.string(),
92625
- toolMetadata: toolMetadataSchema2.optional(),
92626
92744
  state: exports_external2.literal("output-available"),
92627
92745
  providerExecuted: exports_external2.boolean().optional(),
92628
92746
  input: exports_external2.unknown(),
@@ -92640,10 +92758,9 @@ var init_dist7 = __esm(() => {
92640
92758
  exports_external2.object({
92641
92759
  type: exports_external2.string().startsWith("tool-"),
92642
92760
  toolCallId: exports_external2.string(),
92643
- toolMetadata: toolMetadataSchema2.optional(),
92644
92761
  state: exports_external2.literal("output-error"),
92645
92762
  providerExecuted: exports_external2.boolean().optional(),
92646
- input: exports_external2.unknown().optional(),
92763
+ input: exports_external2.unknown(),
92647
92764
  rawInput: exports_external2.unknown().optional(),
92648
92765
  output: exports_external2.never().optional(),
92649
92766
  errorText: exports_external2.string(),
@@ -92658,7 +92775,6 @@ var init_dist7 = __esm(() => {
92658
92775
  exports_external2.object({
92659
92776
  type: exports_external2.string().startsWith("tool-"),
92660
92777
  toolCallId: exports_external2.string(),
92661
- toolMetadata: toolMetadataSchema2.optional(),
92662
92778
  state: exports_external2.literal("output-denied"),
92663
92779
  providerExecuted: exports_external2.boolean().optional(),
92664
92780
  input: exports_external2.unknown(),
@@ -93163,7 +93279,7 @@ __export(exports_session_converter, {
93163
93279
  convertSessionToScenario: () => convertSessionToScenario,
93164
93280
  convertSessionFile: () => convertSessionFile
93165
93281
  });
93166
- import { readFileSync as readFileSync10 } from "fs";
93282
+ import { readFileSync as readFileSync9 } from "fs";
93167
93283
  import { extname } from "path";
93168
93284
  function parseRrwebSession(events) {
93169
93285
  const result = [];
@@ -93349,7 +93465,7 @@ ${condensed}`;
93349
93465
  };
93350
93466
  }
93351
93467
  async function convertSessionFile(filePath, format, options) {
93352
- const raw = readFileSync10(filePath, "utf-8");
93468
+ const raw = readFileSync9(filePath, "utf-8");
93353
93469
  let parsed;
93354
93470
  try {
93355
93471
  parsed = JSON.parse(raw);
@@ -93383,7 +93499,7 @@ function detectSessionFormat(filePath) {
93383
93499
  if (ext === ".har")
93384
93500
  return "har";
93385
93501
  try {
93386
- const content = readFileSync10(filePath, "utf-8").trim();
93502
+ const content = readFileSync9(filePath, "utf-8").trim();
93387
93503
  const parsed = JSON.parse(content);
93388
93504
  if (Array.isArray(parsed) && parsed[0]?.type !== undefined && typeof parsed[0]?.timestamp === "number") {
93389
93505
  return "rrweb";
@@ -93827,7 +93943,7 @@ import chalk6 from "chalk";
93827
93943
  // package.json
93828
93944
  var package_default = {
93829
93945
  name: "@hasna/testers",
93830
- version: "0.0.33",
93946
+ version: "0.0.35",
93831
93947
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
93832
93948
  type: "module",
93833
93949
  main: "dist/index.js",
@@ -93851,10 +93967,10 @@ var package_default = {
93851
93967
  ],
93852
93968
  scripts: {
93853
93969
  build: "bun run build:dashboard && bun run build:cli && bun run build:mcp && bun run build:server && bun run build:lib && bun run build:types",
93854
- "build:cli": "bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external @anthropic-ai/sdk --external playwright --external @hasna/browser",
93855
- "build:mcp": "bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external @anthropic-ai/sdk --external playwright --external @hasna/browser",
93856
- "build:server": "bun build src/server/index.ts --outdir dist/server --target bun --external @anthropic-ai/sdk --external playwright --external @hasna/browser",
93857
- "build:lib": "bun build src/index.ts --outdir dist --target bun --external playwright --external @anthropic-ai/sdk --external @modelcontextprotocol/sdk --external @hasna/browser",
93970
+ "build:cli": "bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external @anthropic-ai/sdk --external playwright --external @hasna/browser --external @hasna/sandboxes",
93971
+ "build:mcp": "bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external @anthropic-ai/sdk --external playwright --external @hasna/browser --external @hasna/sandboxes",
93972
+ "build:server": "bun build src/server/index.ts --outdir dist/server --target bun --external @anthropic-ai/sdk --external playwright --external @hasna/browser --external @hasna/sandboxes",
93973
+ "build:lib": "bun build src/index.ts --outdir dist --target bun --external playwright --external @anthropic-ai/sdk --external @modelcontextprotocol/sdk --external @hasna/browser --external @hasna/sandboxes",
93858
93974
  "build:types": "NODE_OPTIONS='--max-old-space-size=8192' tsc --emitDeclarationOnly --outDir dist --skipLibCheck || true",
93859
93975
  "build:dashboard": "cd dashboard && bun run build",
93860
93976
  "build:ext": "cd extension && bun run build",
@@ -93868,10 +93984,11 @@ var package_default = {
93868
93984
  },
93869
93985
  dependencies: {
93870
93986
  "@anthropic-ai/sdk": "^0.52.0",
93871
- "@hasna/browser": "^0.4.5",
93987
+ "@hasna/browser": "^0.4.12",
93872
93988
  "@hasna/cloud": "^0.1.24",
93873
93989
  "@hasna/contacts": "^0.6.8",
93874
93990
  "@hasna/projects": "^0.1.42",
93991
+ "@hasna/sandboxes": "^0.1.27",
93875
93992
  "@modelcontextprotocol/sdk": "^1.12.1",
93876
93993
  ai: "^6.0.175",
93877
93994
  chalk: "^5.4.1",
@@ -93924,9 +94041,9 @@ init_todos_connector();
93924
94041
  init_browser();
93925
94042
  import { render, Box, Text, useInput, useApp } from "ink";
93926
94043
  import React, { useState } from "react";
93927
- import { readFileSync as readFileSync11, readdirSync as readdirSync6, writeFileSync as writeFileSync7 } from "fs";
94044
+ import { readFileSync as readFileSync10, readdirSync as readdirSync6, writeFileSync as writeFileSync8 } from "fs";
93928
94045
  import { createInterface } from "readline";
93929
- import { join as join19, resolve as resolve5 } from "path";
94046
+ import { join as join20, resolve as resolve4 } from "path";
93930
94047
 
93931
94048
  // src/lib/init.ts
93932
94049
  init_paths();
@@ -95655,9 +95772,13 @@ init_flows();
95655
95772
  init_workflows();
95656
95773
 
95657
95774
  // src/lib/workflow-runner.ts
95775
+ init_database();
95658
95776
  init_workflows();
95659
95777
  init_personas();
95660
95778
  init_runner();
95779
+ import { mkdtempSync, rmSync, writeFileSync as writeFileSync4 } from "fs";
95780
+ import { tmpdir } from "os";
95781
+ import { join as join16 } from "path";
95661
95782
  function buildWorkflowRunPlan(workflow, options) {
95662
95783
  const runOptions = {
95663
95784
  url: options.url,
@@ -95674,10 +95795,10 @@ function buildWorkflowRunPlan(workflow, options) {
95674
95795
  return {
95675
95796
  workflow,
95676
95797
  runOptions,
95677
- connectorCommand: workflow.execution.target === "connector:e2b" ? buildConnectorCommand(workflow.execution, runOptions) : null
95798
+ sandbox: workflow.execution.target === "sandbox" ? buildSandboxPlan(workflow, workflow.execution, runOptions) : null
95678
95799
  };
95679
95800
  }
95680
- async function runTestingWorkflow(workflowId, options) {
95801
+ async function runTestingWorkflow(workflowId, options, dependencies = {}) {
95681
95802
  const workflow = getTestingWorkflow(workflowId);
95682
95803
  if (!workflow)
95683
95804
  throw new Error(`Testing workflow not found: ${workflowId}`);
@@ -95687,13 +95808,25 @@ async function runTestingWorkflow(workflowId, options) {
95687
95808
  const plan = buildWorkflowRunPlan(workflow, options);
95688
95809
  if (options.dryRun)
95689
95810
  return { run: null, results: [], plan };
95690
- if (workflow.execution.target === "connector:e2b") {
95691
- const connectorResult = await runViaConnector(plan);
95692
- return { run: null, results: [], plan, connectorResult };
95811
+ if (workflow.execution.target === "sandbox") {
95812
+ const sandboxResult = await runViaSandbox(plan, dependencies);
95813
+ return { run: null, results: [], plan, sandboxResult };
95693
95814
  }
95694
- const { run, results } = await runByFilter(plan.runOptions);
95815
+ const runLocal = dependencies.runByFilter ?? runByFilter;
95816
+ const { run, results } = await runLocal(plan.runOptions);
95695
95817
  return { run, results, plan };
95696
95818
  }
95819
+ function createWorkflowDatabaseBundle(workflow, plan) {
95820
+ if (!plan.sandbox)
95821
+ throw new Error(`Workflow is not configured for sandbox execution: ${workflow.name}`);
95822
+ const localDir = mkdtempSync(join16(tmpdir(), `testers-workflow-${workflow.id.slice(0, 8)}-`));
95823
+ writeFileSync4(join16(localDir, "testers.db"), getDatabase().serialize());
95824
+ return {
95825
+ localDir,
95826
+ remoteDir: plan.sandbox.stateRemoteDir,
95827
+ cleanup: () => rmSync(localDir, { recursive: true, force: true })
95828
+ };
95829
+ }
95697
95830
  function validatePersonaIds(workflow) {
95698
95831
  for (const personaId of workflow.personaIds) {
95699
95832
  if (!getPersona(personaId)) {
@@ -95701,46 +95834,109 @@ function validatePersonaIds(workflow) {
95701
95834
  }
95702
95835
  }
95703
95836
  }
95704
- function buildConnectorCommand(execution, runOptions) {
95705
- const connector = execution.connector ?? "e2b";
95706
- const operation = execution.operation ?? "run";
95707
- const payload = JSON.stringify({
95708
- operation,
95709
- template: execution.sandboxTemplate,
95837
+ function buildSandboxPlan(workflow, execution, runOptions) {
95838
+ const remoteDir = execution.sandboxRemoteDir ?? `/tmp/testers-workflow-${workflow.id.slice(0, 8)}`;
95839
+ const stateRemoteDir = `${remoteDir.replace(/\/+$/, "")}/.testers-state`;
95840
+ return {
95841
+ provider: execution.provider,
95842
+ image: execution.sandboxImage,
95843
+ name: `testers-${workflow.id.slice(0, 8)}`,
95844
+ remoteDir,
95845
+ stateRemoteDir,
95846
+ cleanup: execution.sandboxCleanup ?? "delete",
95710
95847
  timeoutMs: execution.timeoutMs,
95711
- env: execution.env ?? {},
95712
- command: [
95713
- "bunx",
95714
- "@hasna/testers",
95715
- "run",
95716
- runOptions.url,
95717
- ...runOptions.scenarioIds?.length ? ["--scenario", runOptions.scenarioIds.join(",")] : [],
95718
- ...runOptions.tags?.length ? runOptions.tags.flatMap((tag) => ["--tag", tag]) : [],
95719
- ...runOptions.priority ? ["--priority", runOptions.priority] : [],
95720
- ...runOptions.projectId ? ["--project", runOptions.projectId] : [],
95721
- ...runOptions.model ? ["--model", runOptions.model] : [],
95722
- "--json"
95723
- ]
95724
- });
95725
- return ["connectors", "run", connector, operation, payload];
95848
+ env: execution.env,
95849
+ command: buildSandboxCommand({
95850
+ runOptions,
95851
+ remoteDir,
95852
+ dbPath: `${stateRemoteDir}/testers.db`,
95853
+ setupCommand: execution.setupCommand,
95854
+ packageSpec: execution.packageSpec ?? "@hasna/testers"
95855
+ })
95856
+ };
95726
95857
  }
95727
- async function runViaConnector(plan) {
95728
- if (!plan.connectorCommand)
95729
- throw new Error("Workflow does not have a connector command");
95730
- const proc = Bun.spawn(plan.connectorCommand, {
95731
- stdout: "pipe",
95732
- stderr: "pipe",
95733
- env: process.env
95734
- });
95735
- const [stdout, stderr, exitCode] = await Promise.all([
95736
- new Response(proc.stdout).text(),
95737
- new Response(proc.stderr).text(),
95738
- proc.exited
95739
- ]);
95740
- if (exitCode !== 0) {
95741
- throw new Error(`Connector execution failed (${exitCode}): ${stderr || stdout}`);
95858
+ function buildSandboxCommand(input) {
95859
+ const args = [
95860
+ "bunx",
95861
+ input.packageSpec,
95862
+ "run",
95863
+ input.runOptions.url,
95864
+ ...input.runOptions.scenarioIds?.length ? ["--scenario", input.runOptions.scenarioIds.join(",")] : [],
95865
+ ...input.runOptions.tags?.length ? input.runOptions.tags.flatMap((tag) => ["--tag", tag]) : [],
95866
+ ...input.runOptions.priority ? ["--priority", input.runOptions.priority] : [],
95867
+ ...input.runOptions.projectId ? ["--project", input.runOptions.projectId] : [],
95868
+ ...input.runOptions.model ? ["--model", input.runOptions.model] : [],
95869
+ ...input.runOptions.headed ? ["--headed"] : [],
95870
+ ...input.runOptions.parallel ? ["--parallel", String(input.runOptions.parallel)] : [],
95871
+ ...input.runOptions.timeout ? ["--timeout", String(input.runOptions.timeout)] : [],
95872
+ ...input.runOptions.personaIds?.length ? ["--persona", input.runOptions.personaIds.join(",")] : [],
95873
+ "--no-auto-generate",
95874
+ "--json"
95875
+ ];
95876
+ return [
95877
+ "set -euo pipefail",
95878
+ `mkdir -p ${shellQuote(input.remoteDir)}`,
95879
+ `cd ${shellQuote(input.remoteDir)}`,
95880
+ input.setupCommand,
95881
+ `HASNA_TESTERS_DB_PATH=${shellQuote(input.dbPath)} ${args.map(shellQuote).join(" ")}`
95882
+ ].filter(Boolean).join(`
95883
+ `);
95884
+ }
95885
+ async function runViaSandbox(plan, dependencies) {
95886
+ if (!plan.sandbox)
95887
+ throw new Error("Workflow does not have a sandbox plan");
95888
+ const sandboxes = await resolveSandboxesRuntime(dependencies);
95889
+ const createBundle = dependencies.createDatabaseBundle ?? createWorkflowDatabaseBundle;
95890
+ const bundle = createBundle(plan.workflow, plan);
95891
+ try {
95892
+ const raw = await sandboxes.runCommandInSandbox({
95893
+ command: plan.sandbox.command,
95894
+ provider: plan.sandbox.provider,
95895
+ name: plan.sandbox.name,
95896
+ image: plan.sandbox.image,
95897
+ sandboxTimeout: plan.sandbox.timeoutMs,
95898
+ commandTimeoutMs: plan.sandbox.timeoutMs,
95899
+ projectId: plan.workflow.projectId ?? undefined,
95900
+ config: {
95901
+ source: "testers",
95902
+ workflowId: plan.workflow.id,
95903
+ workflowName: plan.workflow.name
95904
+ },
95905
+ sandboxEnvVars: plan.sandbox.env,
95906
+ cleanup: plan.sandbox.cleanup,
95907
+ upload: {
95908
+ localDir: bundle.localDir,
95909
+ remoteDir: bundle.remoteDir
95910
+ }
95911
+ });
95912
+ const exitCode = raw.result.exit_code ?? raw.result.exitCode ?? 0;
95913
+ const stdout = raw.result.stdout ?? "";
95914
+ const stderr = raw.result.stderr ?? "";
95915
+ if (exitCode !== 0) {
95916
+ throw new Error(`Sandbox workflow execution failed (${exitCode}): ${stderr || stdout}`);
95917
+ }
95918
+ return {
95919
+ sandboxId: raw.sandbox.id,
95920
+ sessionId: raw.session.id,
95921
+ exitCode,
95922
+ stdout,
95923
+ stderr,
95924
+ cleanup: raw.cleanup
95925
+ };
95926
+ } finally {
95927
+ bundle.cleanup?.();
95742
95928
  }
95743
- return stdout.trim();
95929
+ }
95930
+ async function resolveSandboxesRuntime(dependencies) {
95931
+ if (dependencies.sandboxes)
95932
+ return dependencies.sandboxes;
95933
+ if (dependencies.createSandboxesSDK)
95934
+ return dependencies.createSandboxesSDK();
95935
+ const mod = await import("@hasna/sandboxes");
95936
+ return mod.createSandboxesSDK();
95937
+ }
95938
+ function shellQuote(value) {
95939
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
95744
95940
  }
95745
95941
 
95746
95942
  // src/db/environments.ts
@@ -95813,111 +96009,19 @@ function getDefaultEnvironment() {
95813
96009
 
95814
96010
  // src/cli/index.tsx
95815
96011
  init_ci();
95816
-
95817
- // src/lib/assertions.ts
95818
- function parseAssertionString(str) {
95819
- const trimmed = str.trim();
95820
- if (trimmed === "no-console-errors") {
95821
- return { type: "no_console_errors", description: "No console errors" };
95822
- }
95823
- if (trimmed.startsWith("url:contains:")) {
95824
- const expected = trimmed.slice("url:contains:".length);
95825
- return { type: "url_contains", expected, description: `URL contains "${expected}"` };
95826
- }
95827
- if (trimmed.startsWith("title:contains:")) {
95828
- const expected = trimmed.slice("title:contains:".length);
95829
- return { type: "title_contains", expected, description: `Title contains "${expected}"` };
95830
- }
95831
- if (trimmed.startsWith("count:")) {
95832
- const rest = trimmed.slice("count:".length);
95833
- const eqIdx = rest.indexOf(" eq:");
95834
- if (eqIdx === -1) {
95835
- throw new Error(`Invalid count assertion format: ${str}. Expected "count:<selector> eq:<number>"`);
95836
- }
95837
- const selector = rest.slice(0, eqIdx);
95838
- const expected = parseInt(rest.slice(eqIdx + " eq:".length), 10);
95839
- return { type: "element_count", selector, expected, description: `${selector} count equals ${expected}` };
95840
- }
95841
- if (trimmed.startsWith("text:")) {
95842
- const rest = trimmed.slice("text:".length);
95843
- const containsIdx = rest.indexOf(" contains:");
95844
- const equalsIdx = rest.indexOf(" equals:");
95845
- if (containsIdx !== -1) {
95846
- const selector = rest.slice(0, containsIdx);
95847
- const expected = rest.slice(containsIdx + " contains:".length);
95848
- return { type: "text_contains", selector, expected, description: `${selector} text contains "${expected}"` };
95849
- }
95850
- if (equalsIdx !== -1) {
95851
- const selector = rest.slice(0, equalsIdx);
95852
- const expected = rest.slice(equalsIdx + " equals:".length);
95853
- return { type: "text_equals", selector, expected, description: `${selector} text equals "${expected}"` };
95854
- }
95855
- throw new Error(`Invalid text assertion format: ${str}. Expected "text:<selector> contains:<text>" or "text:<selector> equals:<text>"`);
95856
- }
95857
- if (trimmed.startsWith("selector:")) {
95858
- const rest = trimmed.slice("selector:".length);
95859
- const lastSpace = rest.lastIndexOf(" ");
95860
- if (lastSpace === -1) {
95861
- throw new Error(`Invalid selector assertion format: ${str}. Expected "selector:<selector> visible" or "selector:<selector> not-visible"`);
95862
- }
95863
- const selector = rest.slice(0, lastSpace);
95864
- const action = rest.slice(lastSpace + 1);
95865
- if (action === "visible") {
95866
- return { type: "visible", selector, description: `${selector} is visible` };
95867
- }
95868
- if (action === "not-visible") {
95869
- return { type: "not_visible", selector, description: `${selector} is not visible` };
95870
- }
95871
- throw new Error(`Unknown selector action: "${action}". Expected "visible" or "not-visible"`);
95872
- }
95873
- if (trimmed.startsWith("cookie:exists:")) {
95874
- const name = trimmed.slice("cookie:exists:".length);
95875
- return { type: "cookie_exists", expected: name, description: `Cookie "${name}" exists` };
95876
- }
95877
- if (trimmed.startsWith("cookie:not-exists:")) {
95878
- const name = trimmed.slice("cookie:not-exists:".length);
95879
- return { type: "cookie_not_exists", expected: name, description: `Cookie "${name}" does not exist` };
95880
- }
95881
- if (trimmed.startsWith("cookie:value:")) {
95882
- const valueStr = trimmed.slice("cookie:value:".length);
95883
- return { type: "cookie_value", expected: valueStr, description: `Cookie value is "${valueStr}"` };
95884
- }
95885
- if (trimmed.startsWith("local:exists:")) {
95886
- const key = trimmed.slice("local:exists:".length);
95887
- return { type: "local_storage_exists", expected: key, description: `LocalStorage key "${key}" exists` };
95888
- }
95889
- if (trimmed.startsWith("local:not-exists:")) {
95890
- const key = trimmed.slice("local:not-exists:".length);
95891
- return { type: "local_storage_not_exists", expected: key, description: `LocalStorage key "${key}" does not exist` };
95892
- }
95893
- if (trimmed.startsWith("local:value:")) {
95894
- const valueStr = trimmed.slice("local:value:".length);
95895
- return { type: "local_storage_value", expected: valueStr, description: `LocalStorage value is "${valueStr}"` };
95896
- }
95897
- if (trimmed.startsWith("session:value:")) {
95898
- const valueStr = trimmed.slice("session:value:".length);
95899
- return { type: "session_storage_value", expected: valueStr, description: `SessionStorage value is "${valueStr}"` };
95900
- }
95901
- if (trimmed.startsWith("session:not-exists:")) {
95902
- const key = trimmed.slice("session:not-exists:".length);
95903
- return { type: "session_storage_not_exists", expected: key, description: `SessionStorage key "${key}" does not exist` };
95904
- }
95905
- throw new Error(`Cannot parse assertion: "${str}". See --help for assertion formats.`);
95906
- }
95907
-
95908
- // src/cli/index.tsx
96012
+ init_assertions();
95909
96013
  init_paths();
95910
96014
  init_sessions();
95911
96015
  import { existsSync as existsSync17, mkdirSync as mkdirSync14 } from "fs";
95912
96016
 
95913
96017
  // src/lib/repo-discovery.ts
95914
96018
  init_paths();
95915
- import { existsSync as existsSync14, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync4, mkdirSync as mkdirSync11, unlinkSync } from "fs";
96019
+ import { existsSync as existsSync14, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync5, mkdirSync as mkdirSync11, unlinkSync } from "fs";
95916
96020
  import { createHash } from "crypto";
95917
- import { join as join16, resolve, relative as relative2 } from "path";
96021
+ import { join as join17, resolve, relative as relative2 } from "path";
95918
96022
  function getCacheDir() {
95919
96023
  const testersDir = getTestersDir();
95920
- const cacheDir = join16(testersDir, "repo-index");
96024
+ const cacheDir = join17(testersDir, "repo-index");
95921
96025
  if (!existsSync14(cacheDir)) {
95922
96026
  mkdirSync11(cacheDir, { recursive: true });
95923
96027
  }
@@ -95927,11 +96031,11 @@ function pathHash(repoPath) {
95927
96031
  return createHash("sha256").update(repoPath).digest("hex").slice(0, 16);
95928
96032
  }
95929
96033
  function getCachePath(repoPath) {
95930
- return join16(getCacheDir(), `${pathHash(repoPath)}.json`);
96034
+ return join17(getCacheDir(), `${pathHash(repoPath)}.json`);
95931
96035
  }
95932
96036
  function isCacheStale(cached, repoPath) {
95933
96037
  for (const spec of cached.specs) {
95934
- const fullPath = join16(repoPath, spec.file);
96038
+ const fullPath = join17(repoPath, spec.file);
95935
96039
  if (!existsSync14(fullPath))
95936
96040
  return true;
95937
96041
  try {
@@ -95943,11 +96047,11 @@ function isCacheStale(cached, repoPath) {
95943
96047
  }
95944
96048
  }
95945
96049
  if (cached.configPath) {
95946
- const configFullPath = join16(repoPath, cached.configPath);
96050
+ const configFullPath = join17(repoPath, cached.configPath);
95947
96051
  if (!existsSync14(configFullPath))
95948
96052
  return true;
95949
96053
  try {
95950
- const stat = statSync(configFullPath);
96054
+ statSync(configFullPath);
95951
96055
  const age = Date.now() - new Date(cached.snapshotAt).getTime();
95952
96056
  if (age > 3600000)
95953
96057
  return true;
@@ -95970,14 +96074,14 @@ function loadCache(repoPath) {
95970
96074
  }
95971
96075
  function saveCache(snapshot) {
95972
96076
  const cachePath = getCachePath(snapshot.repoPath);
95973
- writeFileSync4(cachePath, JSON.stringify(snapshot, null, 2), "utf-8");
96077
+ writeFileSync5(cachePath, JSON.stringify(snapshot, null, 2), "utf-8");
95974
96078
  }
95975
96079
  function detectPackageManager(repoPath) {
95976
96080
  const result = {
95977
- npm: existsSync14(join16(repoPath, "package-lock.json")),
95978
- yarn: existsSync14(join16(repoPath, "yarn.lock")),
95979
- pnpm: existsSync14(join16(repoPath, "pnpm-lock.yaml")),
95980
- bun: existsSync14(join16(repoPath, "bun.lockb")) || existsSync14(join16(repoPath, "bun.lock")),
96081
+ npm: existsSync14(join17(repoPath, "package-lock.json")),
96082
+ yarn: existsSync14(join17(repoPath, "yarn.lock")),
96083
+ pnpm: existsSync14(join17(repoPath, "pnpm-lock.yaml")),
96084
+ bun: existsSync14(join17(repoPath, "bun.lockb")) || existsSync14(join17(repoPath, "bun.lock")),
95981
96085
  preferred: "npm"
95982
96086
  };
95983
96087
  if (result.bun)
@@ -95991,7 +96095,7 @@ function detectPackageManager(repoPath) {
95991
96095
  return result;
95992
96096
  }
95993
96097
  function detectDevScripts(repoPath) {
95994
- const pkgPath = join16(repoPath, "package.json");
96098
+ const pkgPath = join17(repoPath, "package.json");
95995
96099
  if (!existsSync14(pkgPath)) {
95996
96100
  return { dev: null, test: null, seed: null, build: null };
95997
96101
  }
@@ -96018,7 +96122,7 @@ function findPlaywrightConfig(repoPath) {
96018
96122
  "playwright-ct.config.js"
96019
96123
  ];
96020
96124
  for (const name of candidates) {
96021
- if (existsSync14(join16(repoPath, name)))
96125
+ if (existsSync14(join17(repoPath, name)))
96022
96126
  return name;
96023
96127
  }
96024
96128
  return null;
@@ -96027,7 +96131,7 @@ function extractTestGlobPatterns(configPath, repoPath) {
96027
96131
  if (!configPath) {
96028
96132
  return ["**/*.spec.ts", "**/*.spec.js", "**/*.test.ts", "**/*.test.js", "**/e2e/**/*.ts", "**/e2e/**/*.js", "**/tests/**/*.ts", "**/tests/**/*.js"];
96029
96133
  }
96030
- const fullPath = join16(repoPath, configPath);
96134
+ const fullPath = join17(repoPath, configPath);
96031
96135
  let content;
96032
96136
  try {
96033
96137
  content = readFileSync6(fullPath, "utf-8");
@@ -96038,8 +96142,9 @@ function extractTestGlobPatterns(configPath, repoPath) {
96038
96142
  const testDirMatch = content.match(/testDir\s*[:=]\s*['"`]([^'"`]+)['"`]/);
96039
96143
  const testDir = testDirMatch?.[1];
96040
96144
  const testMatchArray = content.match(/testMatch\s*[:=]\s*\[([^\]]+)\]/);
96041
- if (testMatchArray) {
96042
- const items = testMatchArray[1].match(/['"`]([^'"`]+)['"`]/g);
96145
+ const testMatchBody = testMatchArray?.[1];
96146
+ if (testMatchBody) {
96147
+ const items = testMatchBody.match(/['"`]([^'"`]+)['"`]/g);
96043
96148
  if (items) {
96044
96149
  for (const item of items) {
96045
96150
  patterns.push(item.replace(/['"`]/g, ""));
@@ -96047,8 +96152,9 @@ function extractTestGlobPatterns(configPath, repoPath) {
96047
96152
  }
96048
96153
  }
96049
96154
  const testMatchSingle = content.match(/testMatch\s*[:=]\s*['"`]([^'"`]+)['"`]/);
96050
- if (testMatchSingle) {
96051
- patterns.push(testMatchSingle[1]);
96155
+ const singleTestMatch = testMatchSingle?.[1];
96156
+ if (singleTestMatch) {
96157
+ patterns.push(singleTestMatch);
96052
96158
  }
96053
96159
  if (testDir && patterns.length === 0) {
96054
96160
  patterns.push(`${testDir}/**/*.spec.ts`, `${testDir}/**/*.test.ts`, `${testDir}/**/*.spec.js`, `${testDir}/**/*.test.js`);
@@ -96074,7 +96180,7 @@ function findSpecFiles(repoPath, globPatterns) {
96074
96180
  for (const pattern of globPatterns) {
96075
96181
  const dirsToSearch = ["", ".", "tests", "e2e", "test", "__tests__", "specs", "src"];
96076
96182
  for (const dir of dirsToSearch) {
96077
- const searchDir = dir ? join16(repoPath, dir) : repoPath;
96183
+ const searchDir = dir ? join17(repoPath, dir) : repoPath;
96078
96184
  if (!existsSync14(searchDir))
96079
96185
  continue;
96080
96186
  try {
@@ -96108,7 +96214,7 @@ function walkDir(dir) {
96108
96214
  try {
96109
96215
  const entries = readdirSync3(dir, { withFileTypes: true });
96110
96216
  for (const entry of entries) {
96111
- const fullPath = join16(dir, entry.name);
96217
+ const fullPath = join17(dir, entry.name);
96112
96218
  if (entry.isDirectory()) {
96113
96219
  if (entry.name === "node_modules" || entry.name === ".git")
96114
96220
  continue;
@@ -96126,7 +96232,7 @@ function matchesGlob(filePath, pattern) {
96126
96232
  return new RegExp(regex).test(filePath);
96127
96233
  }
96128
96234
  function detectSuggestedUrl(repoPath) {
96129
- const pkgPath = join16(repoPath, "package.json");
96235
+ const pkgPath = join17(repoPath, "package.json");
96130
96236
  if (!existsSync14(pkgPath))
96131
96237
  return null;
96132
96238
  try {
@@ -96146,10 +96252,10 @@ function detectSuggestedUrl(repoPath) {
96146
96252
  return null;
96147
96253
  }
96148
96254
  function checkPlaywrightBrowserInstalled(repoPath) {
96149
- const cacheDir = join16(repoPath, "node_modules", ".cache", "ms-playwright");
96255
+ const cacheDir = join17(repoPath, "node_modules", ".cache", "ms-playwright");
96150
96256
  if (existsSync14(cacheDir))
96151
96257
  return true;
96152
- const globalCache = join16(repoPath, ".cache", "ms-playwright");
96258
+ const globalCache = join17(repoPath, ".cache", "ms-playwright");
96153
96259
  if (existsSync14(globalCache))
96154
96260
  return true;
96155
96261
  return false;
@@ -96166,7 +96272,7 @@ function getInstallCommand(pm) {
96166
96272
  return "bun install";
96167
96273
  }
96168
96274
  }
96169
- function getPlaywrightInstallCommand(pm) {
96275
+ function getPlaywrightInstallCommand(_pm) {
96170
96276
  return "npx playwright install";
96171
96277
  }
96172
96278
  function discoverRepo(opts) {
@@ -96181,7 +96287,7 @@ function discoverRepo(opts) {
96181
96287
  let configRaw = null;
96182
96288
  if (configPath) {
96183
96289
  try {
96184
- configRaw = readFileSync6(join16(repoPath, configPath), "utf-8");
96290
+ configRaw = readFileSync6(join17(repoPath, configPath), "utf-8");
96185
96291
  } catch {
96186
96292
  configRaw = null;
96187
96293
  }
@@ -96190,7 +96296,7 @@ function discoverRepo(opts) {
96190
96296
  const specs = findSpecFiles(repoPath, globPatterns);
96191
96297
  const packageManager = detectPackageManager(repoPath);
96192
96298
  const devScripts = detectDevScripts(repoPath);
96193
- const playwrightInstalled = existsSync14(join16(repoPath, "node_modules", "playwright")) || existsSync14(join16(repoPath, "node_modules", "@playwright", "test"));
96299
+ const playwrightInstalled = existsSync14(join17(repoPath, "node_modules", "playwright")) || existsSync14(join17(repoPath, "node_modules", "@playwright", "test"));
96194
96300
  const browsersInstalled = checkPlaywrightBrowserInstalled(repoPath);
96195
96301
  const configExists = configPath !== null;
96196
96302
  const specsFound = specs.length > 0;
@@ -96259,7 +96365,7 @@ function clearDiscoveryCache(repoPath) {
96259
96365
  } else {
96260
96366
  for (const file of readdirSync3(cacheDir)) {
96261
96367
  if (file.endsWith(".json")) {
96262
- unlinkSync(join16(cacheDir, file));
96368
+ unlinkSync(join17(cacheDir, file));
96263
96369
  }
96264
96370
  }
96265
96371
  }
@@ -96283,10 +96389,10 @@ init_runs();
96283
96389
  init_database();
96284
96390
  init_paths();
96285
96391
  import { execSync as execSync2 } from "child_process";
96286
- import { existsSync as existsSync15, mkdirSync as mkdirSync12, writeFileSync as writeFileSync5 } from "fs";
96287
- import { join as join17 } from "path";
96392
+ import { existsSync as existsSync15, mkdirSync as mkdirSync12, writeFileSync as writeFileSync6 } from "fs";
96393
+ import { join as join18 } from "path";
96288
96394
  function resolvePlaywrightCmd(repoPath) {
96289
- const localPw = join17(repoPath, "node_modules", ".bin", "playwright");
96395
+ const localPw = join18(repoPath, "node_modules", ".bin", "playwright");
96290
96396
  if (existsSync15(localPw)) {
96291
96397
  return [localPw, "test"];
96292
96398
  }
@@ -96305,7 +96411,7 @@ function buildPlaywrightArgs(specFiles, extraArgs = []) {
96305
96411
  }
96306
96412
  function runPlaywright(repoPath, workingDir, specFiles, extraArgs, timeoutMs) {
96307
96413
  const cmd = resolvePlaywrightCmd(repoPath);
96308
- const args = buildPlaywrightArgs(specFiles, extraArgs, workingDir);
96414
+ const args = buildPlaywrightArgs(specFiles, extraArgs);
96309
96415
  const startTime = Date.now();
96310
96416
  try {
96311
96417
  const result = execSync2(`${cmd.join(" ")} ${args.join(" ")}`, {
@@ -96333,7 +96439,7 @@ function runPlaywright(repoPath, workingDir, specFiles, extraArgs, timeoutMs) {
96333
96439
  };
96334
96440
  }
96335
96441
  }
96336
- function parsePlaywrightJsonOutput(stdout, stderr) {
96442
+ function parsePlaywrightJsonOutput(stdout, _stderr) {
96337
96443
  const testResults = [];
96338
96444
  try {
96339
96445
  const obj = JSON.parse(stdout);
@@ -96438,19 +96544,21 @@ async function runRepoTests(opts) {
96438
96544
  const workingDir = opts.snapshot.workingDir;
96439
96545
  const repoPath = snapshot.repoPath;
96440
96546
  const url = opts.url ?? snapshot.suggestedUrl ?? "http://localhost:3000";
96441
- const run = createRun({
96547
+ const initialRun = createRun({
96442
96548
  projectId: opts.projectId,
96443
96549
  url,
96444
96550
  model: opts.model ?? "repo-native",
96445
96551
  headed: false,
96446
- parallel: 1,
96447
- metadata: {
96552
+ parallel: 1
96553
+ });
96554
+ const run = updateRun(initialRun.id, {
96555
+ metadata: JSON.stringify({
96448
96556
  runType: "repo-native",
96449
96557
  repoPath,
96450
96558
  configPath: snapshot.configPath,
96451
96559
  cacheKey: snapshot.cacheKey,
96452
96560
  label: opts.label
96453
- }
96561
+ })
96454
96562
  });
96455
96563
  const specResults = [];
96456
96564
  const startTime = Date.now();
@@ -96480,10 +96588,10 @@ async function runRepoTests(opts) {
96480
96588
  }
96481
96589
  const resultRecord = { id: resultId };
96482
96590
  if (result.stdout || result.stderr) {
96483
- const reportersDir = join17(getTestersDir(), "repo-run-output");
96591
+ const reportersDir = join18(getTestersDir(), "repo-run-output");
96484
96592
  mkdirSync12(reportersDir, { recursive: true });
96485
- const outputFile = join17(reportersDir, `${resultRecord.id}.log`);
96486
- writeFileSync5(outputFile, `=== stdout ===
96593
+ const outputFile = join18(reportersDir, `${resultRecord.id}.log`);
96594
+ writeFileSync6(outputFile, `=== stdout ===
96487
96595
  ${result.stdout}
96488
96596
 
96489
96597
  === stderr ===
@@ -96590,6 +96698,10 @@ function processSyncEnv() {
96590
96698
  // src/cli/index.tsx
96591
96699
  import { jsxDEV } from "react/jsx-dev-runtime";
96592
96700
  var PRIORITIES = ["low", "medium", "high", "critical"];
96701
+ function splitCsvOption(value) {
96702
+ const items = value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
96703
+ return items.length > 0 ? items : undefined;
96704
+ }
96593
96705
  function AddForm({ onComplete }) {
96594
96706
  const { exit } = useApp();
96595
96707
  const [state, setState] = useState({
@@ -96863,25 +96975,30 @@ program2.command("prod-debug <target>").description("Create a safe production de
96863
96975
  }, config2.prodDebug);
96864
96976
  const output = opts.json ? JSON.stringify(plan, null, 2) : formatProdDebugPlan(plan);
96865
96977
  if (opts.output) {
96866
- writeFileSync7(resolve5(opts.output), output + `
96978
+ writeFileSync8(resolve4(opts.output), output + `
96867
96979
  `);
96868
96980
  } else {
96869
96981
  log(output);
96870
96982
  }
96871
96983
  });
96872
96984
  var CONFIG_DIR5 = getTestersDir();
96873
- var CONFIG_PATH4 = join19(CONFIG_DIR5, "config.json");
96985
+ var CONFIG_PATH4 = join20(CONFIG_DIR5, "config.json");
96874
96986
  function getActiveProject() {
96875
96987
  try {
96876
96988
  if (existsSync17(CONFIG_PATH4)) {
96877
- const raw = JSON.parse(readFileSync11(CONFIG_PATH4, "utf-8"));
96989
+ const raw = JSON.parse(readFileSync10(CONFIG_PATH4, "utf-8"));
96878
96990
  return raw.activeProject ?? undefined;
96879
96991
  }
96880
96992
  } catch {}
96881
96993
  return;
96882
96994
  }
96883
96995
  function resolveProject2(optProject) {
96884
- return optProject ?? getActiveProject();
96996
+ if (optProject)
96997
+ return optProject;
96998
+ const activeProject = getActiveProject();
96999
+ if (!activeProject)
97000
+ return;
97001
+ return getProject(activeProject) ? activeProject : undefined;
96885
97002
  }
96886
97003
  program2.command("add [name]").alias("create").description("Create a new test scenario (interactive if no name/flags given)").option("-d, --description <text>", "Scenario description", "").option("-s, --steps <step>", "Test step (repeatable)", (val, acc) => {
96887
97004
  acc.push(val);
@@ -97066,7 +97183,7 @@ program2.command("delete <id>").description("Delete a scenario").option("-y, --y
97066
97183
  }
97067
97184
  if (!opts.yes) {
97068
97185
  process.stdout.write(chalk6.yellow(`Delete scenario ${scenario.shortId} "${scenario.name}"? [y/N] `));
97069
- const answer = await new Promise((resolve6) => {
97186
+ const answer = await new Promise((resolve5) => {
97070
97187
  let buf = "";
97071
97188
  process.stdin.setRawMode?.(true);
97072
97189
  process.stdin.resume();
@@ -97076,7 +97193,7 @@ program2.command("delete <id>").description("Delete a scenario").option("-y, --y
97076
97193
  process.stdin.pause();
97077
97194
  process.stdout.write(`
97078
97195
  `);
97079
- resolve6(buf);
97196
+ resolve5(buf);
97080
97197
  });
97081
97198
  });
97082
97199
  if (answer !== "y" && answer !== "yes") {
@@ -97105,7 +97222,7 @@ program2.command("remove <id>").alias("uninstall").description("Remove a scenari
97105
97222
  }
97106
97223
  if (!opts.yes) {
97107
97224
  process.stdout.write(chalk6.yellow(`Remove scenario ${scenario.shortId} "${scenario.name}"? [y/N] `));
97108
- const answer = await new Promise((resolve6) => {
97225
+ const answer = await new Promise((resolve5) => {
97109
97226
  let buf = "";
97110
97227
  process.stdin.setRawMode?.(true);
97111
97228
  process.stdin.resume();
@@ -97115,7 +97232,7 @@ program2.command("remove <id>").alias("uninstall").description("Remove a scenari
97115
97232
  process.stdin.pause();
97116
97233
  process.stdout.write(`
97117
97234
  `);
97118
- resolve6(buf);
97235
+ resolve5(buf);
97119
97236
  });
97120
97237
  });
97121
97238
  if (answer !== "y" && answer !== "yes") {
@@ -97161,6 +97278,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
97161
97278
  logError(chalk6.red("No URL provided. Pass a URL argument, use --env <name>, or set a default environment with 'testers env use <name>'."));
97162
97279
  process.exit(2);
97163
97280
  }
97281
+ const scenarioIds = splitCsvOption(opts.scenario);
97164
97282
  if (!opts.dryRun) {
97165
97283
  const hasAnthropic = Boolean(process.env["ANTHROPIC_API_KEY"]);
97166
97284
  const hasOpenAI = Boolean(process.env["OPENAI_API_KEY"]);
@@ -97231,7 +97349,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
97231
97349
  tags: opts.tag.length > 0 ? opts.tag : undefined,
97232
97350
  projectId
97233
97351
  }).filter((s2) => {
97234
- if (opts.scenario && s2.id !== opts.scenario && s2.shortId !== opts.scenario)
97352
+ if (scenarioIds && !scenarioIds.includes(s2.id) && !scenarioIds.includes(s2.shortId))
97235
97353
  return false;
97236
97354
  if (opts.priority && s2.priority !== opts.priority)
97237
97355
  return false;
@@ -97280,7 +97398,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
97280
97398
  const { runId, scenarioCount } = startRunAsync({
97281
97399
  url: url2,
97282
97400
  tags: opts.tag.length > 0 ? opts.tag : undefined,
97283
- scenarioIds: opts.scenario ? [opts.scenario] : undefined,
97401
+ scenarioIds,
97284
97402
  priority: opts.priority,
97285
97403
  model: opts.model,
97286
97404
  headed: opts.headed,
@@ -97314,7 +97432,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
97314
97432
  `);
97315
97433
  }
97316
97434
  };
97317
- await new Promise((resolve6) => {
97435
+ await new Promise((resolve5) => {
97318
97436
  const poll = setInterval(() => {
97319
97437
  const run3 = getRun(runId);
97320
97438
  if (!run3)
@@ -97322,7 +97440,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
97322
97440
  renderTable();
97323
97441
  if (DONE_STATUSES.has(run3.status)) {
97324
97442
  clearInterval(poll);
97325
- resolve6();
97443
+ resolve5();
97326
97444
  }
97327
97445
  }, POLL_INTERVAL);
97328
97446
  });
@@ -97415,7 +97533,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
97415
97533
  if (opts.json || opts.output) {
97416
97534
  const jsonOutput = formatJSON(run3, results2);
97417
97535
  if (opts.output) {
97418
- writeFileSync7(opts.output, jsonOutput, "utf-8");
97536
+ writeFileSync8(opts.output, jsonOutput, "utf-8");
97419
97537
  log(chalk6.green(`Results written to ${opts.output}`));
97420
97538
  }
97421
97539
  if (opts.json) {
@@ -97438,7 +97556,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
97438
97556
  }
97439
97557
  process.exit(getExitCode(run3));
97440
97558
  }
97441
- const noFilters = !opts.scenario && opts.tag.length === 0 && !opts.priority;
97559
+ const noFilters = !scenarioIds && opts.tag.length === 0 && !opts.priority;
97442
97560
  if (noFilters && !opts.json && !opts.output) {
97443
97561
  const allScenarios = listScenarios({ projectId });
97444
97562
  log(chalk6.bold(` Running all ${allScenarios.length} scenarios...`));
@@ -97502,7 +97620,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
97502
97620
  const { run: run2, results } = await runByFilter({
97503
97621
  url: url2,
97504
97622
  tags: opts.tag.length > 0 ? opts.tag : undefined,
97505
- scenarioIds: diffScenarioIds ?? (opts.scenario ? [opts.scenario] : undefined),
97623
+ scenarioIds: diffScenarioIds ?? scenarioIds,
97506
97624
  priority: opts.priority,
97507
97625
  model: opts.model,
97508
97626
  headed: opts.headed,
@@ -97524,7 +97642,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
97524
97642
  if (opts.json || opts.output) {
97525
97643
  const jsonOutput = formatJSON(run2, results);
97526
97644
  if (opts.output) {
97527
- writeFileSync7(opts.output, jsonOutput, "utf-8");
97645
+ writeFileSync8(opts.output, jsonOutput, "utf-8");
97528
97646
  log(chalk6.green(`Results written to ${opts.output}`));
97529
97647
  }
97530
97648
  if (opts.json) {
@@ -97714,7 +97832,7 @@ program2.command("screenshots <id>").description("List screenshots for a run or
97714
97832
  });
97715
97833
  program2.command("import <dir>").description("Import markdown test files as scenarios").action((dir) => {
97716
97834
  try {
97717
- const absDir = resolve5(dir);
97835
+ const absDir = resolve4(dir);
97718
97836
  const files = readdirSync6(absDir).filter((f2) => f2.endsWith(".md"));
97719
97837
  if (files.length === 0) {
97720
97838
  log(chalk6.dim("No .md files found in directory."));
@@ -97722,7 +97840,7 @@ program2.command("import <dir>").description("Import markdown test files as scen
97722
97840
  }
97723
97841
  let imported = 0;
97724
97842
  for (const file2 of files) {
97725
- const content = readFileSync11(join19(absDir, file2), "utf-8");
97843
+ const content = readFileSync10(join20(absDir, file2), "utf-8");
97726
97844
  const lines = content.split(`
97727
97845
  `);
97728
97846
  let name21 = file2.replace(/\.md$/, "");
@@ -97777,8 +97895,8 @@ program2.command("export [format]").description("Export scenarios as JSON (defau
97777
97895
  if (fmt === "json") {
97778
97896
  const outputPath = opts.output ?? "testers-export.json";
97779
97897
  const data = JSON.stringify(scenarios, null, 2);
97780
- writeFileSync7(outputPath, data, "utf-8");
97781
- log(chalk6.green(`Exported ${scenarios.length} scenario(s) to ${resolve5(outputPath)}`));
97898
+ writeFileSync8(outputPath, data, "utf-8");
97899
+ log(chalk6.green(`Exported ${scenarios.length} scenario(s) to ${resolve4(outputPath)}`));
97782
97900
  return;
97783
97901
  }
97784
97902
  const outputDir = opts.output ?? ".";
@@ -97810,13 +97928,13 @@ program2.command("export [format]").description("Export scenarios as JSON (defau
97810
97928
  lines.push("");
97811
97929
  }
97812
97930
  const safeFilename = s2.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
97813
- const filePath = join19(outputDir, `${s2.shortId}-${safeFilename}.md`);
97814
- writeFileSync7(filePath, lines.join(`
97931
+ const filePath = join20(outputDir, `${s2.shortId}-${safeFilename}.md`);
97932
+ writeFileSync8(filePath, lines.join(`
97815
97933
  `), "utf-8");
97816
97934
  log(chalk6.dim(` ${s2.shortId}: ${s2.name} \u2192 ${filePath}`));
97817
97935
  }
97818
97936
  log(chalk6.green(`
97819
- Exported ${scenarios.length} scenario(s) as markdown to ${resolve5(outputDir)}`));
97937
+ Exported ${scenarios.length} scenario(s) as markdown to ${resolve4(outputDir)}`));
97820
97938
  } catch (error40) {
97821
97939
  logError(chalk6.red(`Error: ${error40 instanceof Error ? error40.message : String(error40)}`));
97822
97940
  process.exit(1);
@@ -97835,7 +97953,7 @@ program2.command("status").description("Show database and auth status").action((
97835
97953
  try {
97836
97954
  const config2 = loadConfig();
97837
97955
  const hasApiKey = !!config2.anthropicApiKey || !!process.env["ANTHROPIC_API_KEY"];
97838
- const dbPath = join19(getTestersDir(), "testers.db");
97956
+ const dbPath = join20(getTestersDir(), "testers.db");
97839
97957
  log("");
97840
97958
  log(chalk6.bold(" Open Testers Status"));
97841
97959
  log("");
@@ -97989,11 +98107,11 @@ projectCmd.command("use <name>").description("Set active project (find or create
97989
98107
  let config2 = {};
97990
98108
  if (existsSync17(CONFIG_PATH4)) {
97991
98109
  try {
97992
- config2 = JSON.parse(readFileSync11(CONFIG_PATH4, "utf-8"));
98110
+ config2 = JSON.parse(readFileSync10(CONFIG_PATH4, "utf-8"));
97993
98111
  } catch {}
97994
98112
  }
97995
98113
  config2.activeProject = project.id;
97996
- writeFileSync7(CONFIG_PATH4, JSON.stringify(config2, null, 2), "utf-8");
98114
+ writeFileSync8(CONFIG_PATH4, JSON.stringify(config2, null, 2), "utf-8");
97997
98115
  if (opts.json) {
97998
98116
  log(JSON.stringify({ activeProject: project.id, project }, null, 2));
97999
98117
  return;
@@ -98007,7 +98125,7 @@ projectCmd.command("use <name>").description("Set active project (find or create
98007
98125
  var repoCmd = program2.command("repo").description("Discover and run repo-native Playwright tests");
98008
98126
  repoCmd.command("discover [path]").alias("scan").description("Discover Playwright tests in a repo").option("--refresh", "Force a fresh scan, ignoring cache", false).option("--json", "Output as JSON", false).option("--base-url <url>", "Override the suggested base URL").action((path, opts) => {
98009
98127
  try {
98010
- const repoPath = resolve5(path ?? process.cwd());
98128
+ const repoPath = resolve4(path ?? process.cwd());
98011
98129
  const snapshot = discoverRepo({
98012
98130
  repoPath,
98013
98131
  refresh: opts.refresh,
@@ -98073,7 +98191,7 @@ repoCmd.command("discover [path]").alias("scan").description("Discover Playwrigh
98073
98191
  });
98074
98192
  repoCmd.command("prepare [path]").alias("prep").description("Install dependencies and browsers for repo tests").option("--all", "Run all prep steps (install, browsers, build, seed)", false).option("--install", "Install dependencies", false).option("--browsers", "Install Playwright browsers", false).option("--build", "Build the app", false).option("--seed", "Seed the database", false).option("--refresh", "Force fresh discovery scan", false).option("--json", "Output as JSON", false).action((path, opts) => {
98075
98193
  try {
98076
- const repoPath = resolve5(path ?? process.cwd());
98194
+ const repoPath = resolve4(path ?? process.cwd());
98077
98195
  const snapshot = discoverRepo({ repoPath, refresh: opts.refresh });
98078
98196
  const steps = [];
98079
98197
  if (opts.all) {
@@ -98151,7 +98269,7 @@ repoCmd.command("run [path]").description("Run discovered Playwright tests nativ
98151
98269
  return acc;
98152
98270
  }, []).option("--timeout <ms>", "Timeout per spec file", "300000").option("--url <url>", "Dev server URL").option("--project <id>", "Project ID for result storage").option("--label <text>", "Run label").option("--json", "Output as JSON", false).action(async (path, opts) => {
98153
98271
  try {
98154
- const repoPath = resolve5(path ?? process.cwd());
98272
+ const repoPath = resolve4(path ?? process.cwd());
98155
98273
  const snapshot = discoverRepo({
98156
98274
  repoPath,
98157
98275
  refresh: opts.refresh,
@@ -98217,7 +98335,7 @@ repoCmd.command("run [path]").description("Run discovered Playwright tests nativ
98217
98335
  repoCmd.command("cache [path]").description("Manage discovery cache").option("--clear", "Clear discovery cache", false).option("--status", "Show cache status", false).action((path, opts) => {
98218
98336
  try {
98219
98337
  if (opts.clear) {
98220
- const repoPath2 = path ? resolve5(path) : undefined;
98338
+ const repoPath2 = path ? resolve4(path) : undefined;
98221
98339
  clearDiscoveryCache(repoPath2);
98222
98340
  if (repoPath2) {
98223
98341
  log(chalk6.green("Discovery cache cleared for this repo."));
@@ -98227,7 +98345,7 @@ repoCmd.command("cache [path]").description("Manage discovery cache").option("--
98227
98345
  return;
98228
98346
  }
98229
98347
  if (opts.status) {
98230
- const repoPath2 = resolve5(path ?? process.cwd());
98348
+ const repoPath2 = resolve4(path ?? process.cwd());
98231
98349
  const info2 = getDiscoveryCacheInfo(repoPath2);
98232
98350
  if (!info2) {
98233
98351
  log(chalk6.dim("No discovery cache for this repo."));
@@ -98241,7 +98359,7 @@ repoCmd.command("cache [path]").description("Manage discovery cache").option("--
98241
98359
  log("");
98242
98360
  return;
98243
98361
  }
98244
- const repoPath = resolve5(path ?? process.cwd());
98362
+ const repoPath = resolve4(path ?? process.cwd());
98245
98363
  const info = getDiscoveryCacheInfo(repoPath);
98246
98364
  if (!info) {
98247
98365
  log(chalk6.dim("No discovery cache. Run 'testers repo discover' to create one."));
@@ -98330,8 +98448,8 @@ sessionCmd.command("show <id>").description("Show details of a recorded session"
98330
98448
  });
98331
98449
  sessionCmd.command("import <file>").description("Import a session JSON file exported from the Chrome extension").action(async (file2) => {
98332
98450
  try {
98333
- const { readFileSync: readFileSync12 } = await import("fs");
98334
- const raw = readFileSync12(file2, "utf-8");
98451
+ const { readFileSync: readFileSync11 } = await import("fs");
98452
+ const raw = readFileSync11(file2, "utf-8");
98335
98453
  const data = JSON.parse(raw);
98336
98454
  const items = Array.isArray(data) ? data : [data];
98337
98455
  const { createSession: createSession2 } = await Promise.resolve().then(() => (init_sessions(), exports_sessions));
@@ -98571,7 +98689,7 @@ program2.command("daemon").description("Start the scheduler daemon").option("--i
98571
98689
  } catch (err) {
98572
98690
  logError(chalk6.red(`Daemon error: ${err instanceof Error ? err.message : String(err)}`));
98573
98691
  }
98574
- await new Promise((resolve6) => setTimeout(resolve6, intervalMs));
98692
+ await new Promise((resolve5) => setTimeout(resolve5, intervalMs));
98575
98693
  }
98576
98694
  };
98577
98695
  process.on("SIGINT", () => {
@@ -98600,12 +98718,12 @@ program2.command("ci [provider]").description("Print or write a CI workflow (def
98600
98718
  }
98601
98719
  const workflow = generateGitHubActionsWorkflow();
98602
98720
  if (opts.output) {
98603
- const outPath = resolve5(opts.output);
98721
+ const outPath = resolve4(opts.output);
98604
98722
  const outDir = outPath.replace(/\/[^/]*$/, "");
98605
98723
  if (outDir && !existsSync17(outDir)) {
98606
98724
  mkdirSync14(outDir, { recursive: true });
98607
98725
  }
98608
- writeFileSync7(outPath, workflow, "utf-8");
98726
+ writeFileSync8(outPath, workflow, "utf-8");
98609
98727
  log(chalk6.green(`Workflow written to ${outPath}`));
98610
98728
  return;
98611
98729
  }
@@ -98636,12 +98754,12 @@ program2.command("init").description("Initialize a new testing project").option(
98636
98754
  log(` ${chalk6.dim(s2.shortId)} ${s2.name} ${chalk6.dim(`[${s2.tags.join(", ")}]`)}`);
98637
98755
  }
98638
98756
  if (opts.ci === "github") {
98639
- const workflowDir = join19(process.cwd(), ".github", "workflows");
98757
+ const workflowDir = join20(process.cwd(), ".github", "workflows");
98640
98758
  if (!existsSync17(workflowDir)) {
98641
98759
  mkdirSync14(workflowDir, { recursive: true });
98642
98760
  }
98643
- const workflowPath = join19(workflowDir, "testers.yml");
98644
- writeFileSync7(workflowPath, generateGitHubActionsWorkflow(), "utf-8");
98761
+ const workflowPath = join20(workflowDir, "testers.yml");
98762
+ writeFileSync8(workflowPath, generateGitHubActionsWorkflow(), "utf-8");
98645
98763
  log(` CI: ${chalk6.green("GitHub Actions workflow written to .github/workflows/testers.yml")}`);
98646
98764
  } else if (opts.ci) {
98647
98765
  log(chalk6.yellow(` Unknown CI provider: ${opts.ci}. Supported: github`));
@@ -98650,7 +98768,7 @@ program2.command("init").description("Initialize a new testing project").option(
98650
98768
  if (opts.yes)
98651
98769
  return;
98652
98770
  const rl2 = createInterface({ input: process.stdin, output: process.stdout });
98653
- const ask = (q2) => new Promise((resolve6) => rl2.question(q2, resolve6));
98771
+ const ask = (q2) => new Promise((resolve5) => rl2.question(q2, resolve5));
98654
98772
  try {
98655
98773
  const envAnswer = await ask(" Would you like to configure environments? [y/N] ");
98656
98774
  if (envAnswer.trim().toLowerCase() === "y") {
@@ -98835,8 +98953,8 @@ program2.command("report [run-id]").description("Generate HTML test report or co
98835
98953
  format
98836
98954
  });
98837
98955
  if (opts.output && opts.output !== "report.html") {
98838
- writeFileSync7(opts.output, content, "utf-8");
98839
- const absPath2 = resolve5(opts.output);
98956
+ writeFileSync8(opts.output, content, "utf-8");
98957
+ const absPath2 = resolve4(opts.output);
98840
98958
  log(chalk6.green(`Compliance report written to ${absPath2}`));
98841
98959
  } else {
98842
98960
  log(content);
@@ -98849,8 +98967,8 @@ program2.command("report [run-id]").description("Generate HTML test report or co
98849
98967
  } else {
98850
98968
  html = generateHtmlReport(runId);
98851
98969
  }
98852
- writeFileSync7(opts.output, html, "utf-8");
98853
- const absPath = resolve5(opts.output);
98970
+ writeFileSync8(opts.output, html, "utf-8");
98971
+ const absPath = resolve4(opts.output);
98854
98972
  log(chalk6.green(`Report generated: ${absPath}`));
98855
98973
  if (opts.open) {
98856
98974
  const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
@@ -99659,7 +99777,7 @@ program2.command("doctor").description("Check system setup and configuration").a
99659
99777
  log(chalk6.red("\u2717") + " ANTHROPIC_API_KEY is not set (required for AI-powered tests)");
99660
99778
  allPassed = false;
99661
99779
  }
99662
- const dbPath = join19(getTestersDir(), "testers.db");
99780
+ const dbPath = join20(getTestersDir(), "testers.db");
99663
99781
  try {
99664
99782
  const { Database: Database5 } = await import("bun:sqlite");
99665
99783
  const db2 = new Database5(dbPath, { create: true });
@@ -99711,7 +99829,7 @@ program2.command("serve").description("Start the Open Testers web dashboard").op
99711
99829
  try {
99712
99830
  const port = parseInt(opts.port, 10);
99713
99831
  const url2 = `http://localhost:${port}`;
99714
- const serverBin = join19(resolve5(process.execPath, ".."), "..", "dist", "server", "index.js");
99832
+ const serverBin = join20(resolve4(process.execPath, ".."), "..", "dist", "server", "index.js");
99715
99833
  const { join: pathJoin, resolve: pathResolve, dirname: dirname7 } = await import("path");
99716
99834
  const { fileURLToPath: fileURLToPath2 } = await import("url");
99717
99835
  const serverPath = pathJoin(dirname7(fileURLToPath2(import.meta.url)), "..", "server", "index.js");
@@ -100127,7 +100245,7 @@ workflowCmd.command("create <name>").description("Save a reusable testing workfl
100127
100245
  }, []).option("--priority <level>", "Scenario priority").option("--persona <ids>", "Comma-separated persona IDs").option("--goal <prompt>", "Goal prompt for the agentic testing loop").option("--success <criteria>", "Success criteria (repeatable)", (val, acc) => {
100128
100246
  acc.push(val);
100129
100247
  return acc;
100130
- }, []).option("--max-iterations <n>", "Goal-loop iteration cap", "10").option("--target <target>", "Execution target: local or connector:e2b", "local").option("--e2b-template <name>", "E2B sandbox template name").option("--connector-operation <name>", "Connector operation for E2B", "run").option("--timeout <ms>", "Workflow timeout").option("--json", "Output as JSON", false).action((name21, opts) => {
100248
+ }, []).option("--max-iterations <n>", "Goal-loop iteration cap", "10").option("--target <target>", "Execution target: local or sandbox", "local").option("--sandbox-provider <provider>", "Sandbox provider: e2b, daytona, or modal").option("--sandbox-image <image>", "Sandbox image/template").option("--sandbox-remote-dir <path>", "Remote working directory for sandbox runs").option("--sandbox-cleanup <mode>", "Sandbox cleanup mode: delete, stop, or keep", "delete").option("--sandbox-setup-command <command>", "Shell command to run before testers in the sandbox").option("--sandbox-package <spec>", "Package spec to execute in the sandbox", "@hasna/testers").option("--e2b-template <name>", "Legacy alias for --sandbox-image").option("--timeout <ms>", "Workflow timeout").option("--json", "Output as JSON", false).action((name21, opts) => {
100131
100249
  try {
100132
100250
  const workflow = createTestingWorkflow({
100133
100251
  name: name21,
@@ -100146,9 +100264,12 @@ workflowCmd.command("create <name>").description("Save a reusable testing workfl
100146
100264
  } : null,
100147
100265
  execution: {
100148
100266
  target: opts.target,
100149
- connector: opts.target === "connector:e2b" ? "e2b" : undefined,
100150
- operation: opts.connectorOperation,
100151
- sandboxTemplate: opts.e2bTemplate,
100267
+ provider: opts.sandboxProvider ?? (opts.target === "connector:e2b" ? "e2b" : undefined),
100268
+ sandboxImage: opts.sandboxImage ?? opts.e2bTemplate,
100269
+ sandboxRemoteDir: opts.sandboxRemoteDir,
100270
+ sandboxCleanup: opts.sandboxCleanup,
100271
+ setupCommand: opts.sandboxSetupCommand,
100272
+ packageSpec: opts.sandboxPackage,
100152
100273
  timeoutMs: opts.timeout ? parseInt(opts.timeout, 10) : undefined
100153
100274
  }
100154
100275
  });
@@ -100180,7 +100301,7 @@ workflowCmd.command("list").description("List saved testing workflows").option("
100180
100301
  log(chalk6.bold(" Testing Workflows"));
100181
100302
  log("");
100182
100303
  for (const workflow of workflows) {
100183
- const target = workflow.execution.target === "connector:e2b" ? chalk6.cyan("e2b") : chalk6.green("local");
100304
+ const target = workflow.execution.target === "sandbox" ? chalk6.cyan(`sandbox${workflow.execution.provider ? `:${workflow.execution.provider}` : ""}`) : chalk6.green("local");
100184
100305
  log(` ${chalk6.dim(workflow.id.slice(0, 8))} ${workflow.name} ${target} ${chalk6.dim(workflow.personaIds.length ? `${workflow.personaIds.length} personas` : "no personas")}`);
100185
100306
  }
100186
100307
  log("");
@@ -100392,7 +100513,7 @@ personaCmd.command("delete <id>").description("Delete a persona").option("-y, --
100392
100513
  }
100393
100514
  if (!opts.yes) {
100394
100515
  process.stdout.write(chalk6.yellow(`Delete persona ${persona.shortId} "${persona.name}"? [y/N] `));
100395
- const answer = await new Promise((resolve6) => {
100516
+ const answer = await new Promise((resolve5) => {
100396
100517
  let buf = "";
100397
100518
  process.stdin.setRawMode?.(true);
100398
100519
  process.stdin.resume();
@@ -100402,7 +100523,7 @@ personaCmd.command("delete <id>").description("Delete a persona").option("-y, --
100402
100523
  process.stdin.pause();
100403
100524
  process.stdout.write(`
100404
100525
  `);
100405
- resolve6(buf);
100526
+ resolve5(buf);
100406
100527
  });
100407
100528
  });
100408
100529
  if (answer !== "y" && answer !== "yes") {
@@ -100563,7 +100684,7 @@ evalCmd.command("rag <url>").description("Run RAG quality evaluation \u2014 fait
100563
100684
  let ragTestCases = [];
100564
100685
  if (opts.docs) {
100565
100686
  try {
100566
- const raw = readFileSync11(opts.docs, "utf-8");
100687
+ const raw = readFileSync10(opts.docs, "utf-8");
100567
100688
  ragTestCases = JSON.parse(raw);
100568
100689
  } catch {
100569
100690
  logError(chalk6.red(`Failed to read docs file: ${opts.docs}`));
@@ -100653,9 +100774,9 @@ Created golden answer check ${chalk6.bold(golden2.shortId)}`));
100653
100774
  }
100654
100775
  const ask = (prompt) => {
100655
100776
  const rl2 = createInterface({ input: process.stdin, output: process.stdout });
100656
- return new Promise((resolve6) => rl2.question(prompt, (ans) => {
100777
+ return new Promise((resolve5) => rl2.question(prompt, (ans) => {
100657
100778
  rl2.close();
100658
- resolve6(ans.trim());
100779
+ resolve5(ans.trim());
100659
100780
  }));
100660
100781
  };
100661
100782
  const question = await ask("Question (what this endpoint should answer): ");
@@ -100816,9 +100937,9 @@ program2.command("run-many <url>").description("Run scenarios \xD7 personas matr
100816
100937
  });
100817
100938
  program2.command("run-script <file>").description("Run a hybrid test script (.ts) that exports an array of HybridScenario objects").option("--url <url>", "Base URL to run against").option("--json", "Output as JSON", false).action(async (file2, opts) => {
100818
100939
  try {
100819
- const { resolve: resolve6 } = await import("path");
100940
+ const { resolve: resolve5 } = await import("path");
100820
100941
  const { runHybridScenario: runHybridScenario2 } = await Promise.resolve().then(() => (init_hybrid_runner(), exports_hybrid_runner));
100821
- const scriptPath = resolve6(process.cwd(), file2);
100942
+ const scriptPath = resolve5(process.cwd(), file2);
100822
100943
  const mod = await import(scriptPath);
100823
100944
  const scenarios = mod.scenarios ?? mod.default ?? [];
100824
100945
  if (!Array.isArray(scenarios) || scenarios.length === 0) {