@hasna/testers 0.0.46 → 0.0.47

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
@@ -27262,7 +27262,7 @@ async function runViaSandbox(plan, dependencies) {
27262
27262
  workflowId: plan.workflow.id,
27263
27263
  workflowName: plan.workflow.name
27264
27264
  },
27265
- sandboxEnvVars: plan.sandbox.env,
27265
+ sandboxEnvVars: resolveSandboxEnv(plan.sandbox.env),
27266
27266
  cleanup: plan.sandbox.cleanup,
27267
27267
  upload: {
27268
27268
  localDir: bundle.localDir,
@@ -27288,6 +27288,19 @@ async function runViaSandbox(plan, dependencies) {
27288
27288
  bundle.cleanup?.();
27289
27289
  }
27290
27290
  }
27291
+ function resolveSandboxEnv(env) {
27292
+ if (!env || Object.keys(env).length === 0)
27293
+ return;
27294
+ const resolved = {};
27295
+ for (const [key, value] of Object.entries(env)) {
27296
+ const resolvedValue = resolveCredential(value);
27297
+ if (resolvedValue === null) {
27298
+ throw new Error(`Missing sandbox env value for ${key}`);
27299
+ }
27300
+ resolved[key] = resolvedValue;
27301
+ }
27302
+ return resolved;
27303
+ }
27291
27304
  async function resolveSandboxesRuntime(dependencies) {
27292
27305
  if (dependencies.sandboxes)
27293
27306
  return dependencies.sandboxes;
@@ -27305,6 +27318,7 @@ var init_workflow_runner = __esm(() => {
27305
27318
  init_workflows();
27306
27319
  init_personas();
27307
27320
  init_runner();
27321
+ init_secrets_resolver();
27308
27322
  APP_SOURCE_EXCLUDES = [
27309
27323
  "node_modules",
27310
27324
  ".git",
@@ -94465,7 +94479,7 @@ import chalk6 from "chalk";
94465
94479
  // package.json
94466
94480
  var package_default = {
94467
94481
  name: "@hasna/testers",
94468
- version: "0.0.46",
94482
+ version: "0.0.47",
94469
94483
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
94470
94484
  type: "module",
94471
94485
  main: "dist/index.js",
@@ -97365,6 +97379,29 @@ function validateStoredAssertion(value) {
97365
97379
  }
97366
97380
  return describeStoredAssertion(value);
97367
97381
  }
97382
+ function envCredentialRef(value) {
97383
+ const trimmed = value?.trim();
97384
+ if (!trimmed)
97385
+ return;
97386
+ return trimmed.startsWith("$") ? trimmed : `$${trimmed}`;
97387
+ }
97388
+ function parseSandboxEnv(values) {
97389
+ if (!values?.length)
97390
+ return;
97391
+ const env = {};
97392
+ for (const value of values) {
97393
+ const trimmed = value.trim();
97394
+ if (!trimmed)
97395
+ continue;
97396
+ const separator = trimmed.indexOf("=");
97397
+ const key = separator >= 0 ? trimmed.slice(0, separator).trim() : trimmed;
97398
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
97399
+ throw new Error(`Invalid sandbox env var name: ${key || value}`);
97400
+ }
97401
+ env[key] = separator >= 0 ? trimmed.slice(separator + 1) : `$${key}`;
97402
+ }
97403
+ return Object.keys(env).length > 0 ? env : undefined;
97404
+ }
97368
97405
  function AddForm({ onComplete }) {
97369
97406
  const { exit } = useApp();
97370
97407
  const [state, setState] = useState({
@@ -97669,12 +97706,12 @@ program2.command("add [name]").alias("create").description("Create a new test sc
97669
97706
  }, []).option("-t, --tag <tag>", "Tag (repeatable)", (val, acc) => {
97670
97707
  acc.push(val);
97671
97708
  return acc;
97672
- }, []).option("-p, --priority <level>", "Priority level", "medium").option("-m, --model <model>", "AI model to use").option("--path <path>", "Target path on the URL").option("--auth", "Requires authentication", false).option("--timeout <ms>", "Timeout in milliseconds").option("--project <id>", "Project ID").option("--template <name>", "Seed scenarios from a template (auth, crud, forms, nav, a11y)").option("--assert <assertion>", "Structured assertion (repeatable). Formats: selector:<sel> visible, text:<sel> contains:<text>, no-console-errors, url:contains:<path>, title:contains:<text>, count:<sel> eq:<n>", (val, acc) => {
97709
+ }, []).option("-p, --priority <level>", "Priority level", "medium").option("-m, --model <model>", "AI model to use").option("--path <path>", "Target path on the URL").option("--auth", "Requires authentication", false).option("--auth-preset <name>", "Attach email/password/loginPath from a named auth preset").option("--timeout <ms>", "Timeout in milliseconds").option("--project <id>", "Project ID").option("--template <name>", "Seed scenarios from a template (auth, crud, forms, nav, a11y)").option("--assert <assertion>", "Structured assertion (repeatable). Formats: selector:<sel> visible, text:<sel> contains:<text>, no-console-errors, url:contains:<path>, title:contains:<text>, count:<sel> eq:<n>", (val, acc) => {
97673
97710
  acc.push(val);
97674
97711
  return acc;
97675
97712
  }, []).action(async (name21, opts) => {
97676
97713
  try {
97677
- const hasFlags = opts.description || opts.steps?.length || opts.tag?.length || opts.model || opts.path || opts.auth || opts.timeout || opts.template || opts.assert?.length;
97714
+ const hasFlags = opts.description || opts.steps?.length || opts.tag?.length || opts.model || opts.path || opts.auth || opts.authPreset || opts.timeout || opts.template || opts.assert?.length;
97678
97715
  if (!name21 && !hasFlags) {
97679
97716
  const projectId2 = resolveProject2(opts.project);
97680
97717
  await runInteractiveAdd(projectId2);
@@ -97699,6 +97736,11 @@ program2.command("add [name]").alias("create").description("Create a new test sc
97699
97736
  }
97700
97737
  const assertions = opts.assert.map(parseAssertionString);
97701
97738
  const projectId = resolveProject2(opts.project);
97739
+ const authPreset = opts.authPreset ? getAuthPreset(opts.authPreset) : null;
97740
+ if (opts.authPreset && !authPreset) {
97741
+ logError(chalk6.red(`Auth preset not found: ${opts.authPreset}`));
97742
+ process.exit(1);
97743
+ }
97702
97744
  const scenario = createScenario({
97703
97745
  name: name21,
97704
97746
  description: opts.description || name21,
@@ -97707,7 +97749,12 @@ program2.command("add [name]").alias("create").description("Create a new test sc
97707
97749
  priority: opts.priority,
97708
97750
  model: opts.model,
97709
97751
  targetPath: opts.path,
97710
- requiresAuth: opts.auth,
97752
+ requiresAuth: opts.auth || Boolean(authPreset),
97753
+ authConfig: authPreset ? {
97754
+ email: authPreset.email,
97755
+ password: authPreset.password,
97756
+ loginPath: authPreset.loginPath
97757
+ } : undefined,
97711
97758
  timeoutMs: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
97712
97759
  assertions: assertions.length > 0 ? assertions : undefined,
97713
97760
  projectId
@@ -99701,12 +99748,22 @@ program2.command("report [run-id]").description("Generate HTML test report or co
99701
99748
  }
99702
99749
  });
99703
99750
  var authCmd = program2.command("auth").description("Manage auth presets");
99704
- authCmd.command("add <name>").description("Create an auth preset").requiredOption("--email <email>", "Login email").requiredOption("--password <password>", "Login password").option("--login-path <path>", "Login page path", "/login").action((name21, opts) => {
99751
+ authCmd.command("add <name>").description("Create an auth preset").option("--email <email>", "Login email or credential reference").option("--password <password>", "Login password or credential reference").option("--email-env <name>", "Environment variable name for the login email").option("--password-env <name>", "Environment variable name for the login password").option("--login-path <path>", "Login page path", "/login").action((name21, opts) => {
99705
99752
  try {
99753
+ const email3 = opts.email ?? envCredentialRef(opts.emailEnv);
99754
+ const password = opts.password ?? envCredentialRef(opts.passwordEnv);
99755
+ if (!email3) {
99756
+ logError(chalk6.red("Error: provide --email or --email-env"));
99757
+ process.exit(1);
99758
+ }
99759
+ if (!password) {
99760
+ logError(chalk6.red("Error: provide --password or --password-env"));
99761
+ process.exit(1);
99762
+ }
99706
99763
  const preset = createAuthPreset({
99707
99764
  name: name21,
99708
- email: opts.email,
99709
- password: opts.password,
99765
+ email: email3,
99766
+ password,
99710
99767
  loginPath: opts.loginPath
99711
99768
  });
99712
99769
  log(chalk6.green(`Created auth preset ${chalk6.bold(preset.name)} (${preset.email})`));
@@ -100968,7 +101025,10 @@ workflowCmd.command("create <name>").description("Save a reusable testing workfl
100968
101025
  }, []).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) => {
100969
101026
  acc.push(val);
100970
101027
  return acc;
100971
- }, []).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-sync <strategy>", "Sandbox upload sync strategy: rsync or archive", "rsync").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("--sandbox-app-source <path>", "Local app source directory to upload into the sandbox").option("--sandbox-app-remote-dir <path>", "Remote app directory inside the sandbox (default: <sandbox-remote-dir>/app)").option("--sandbox-app-start-command <command>", "Shell command to start the app before testers runs").option("--sandbox-app-url <url>", "URL testers should target inside the sandbox after the app starts").option("--sandbox-app-wait-url <url>", "URL to poll before starting testers (defaults to --sandbox-app-url)").option("--sandbox-app-wait-timeout <ms>", "App readiness wait timeout in milliseconds").option("--e2b-template <name>", "Legacy alias for --sandbox-image").option("--timeout <ms>", "Workflow timeout").option("--json", "Output as JSON", false).action((name21, opts) => {
101028
+ }, []).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-sync <strategy>", "Sandbox upload sync strategy: rsync or archive", "rsync").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("--sandbox-env <assignment>", "Sandbox env var; KEY forwards host KEY, KEY=value stores value (repeatable)", (val, acc) => {
101029
+ acc.push(val);
101030
+ return acc;
101031
+ }, []).option("--sandbox-app-source <path>", "Local app source directory to upload into the sandbox").option("--sandbox-app-remote-dir <path>", "Remote app directory inside the sandbox (default: <sandbox-remote-dir>/app)").option("--sandbox-app-start-command <command>", "Shell command to start the app before testers runs").option("--sandbox-app-url <url>", "URL testers should target inside the sandbox after the app starts").option("--sandbox-app-wait-url <url>", "URL to poll before starting testers (defaults to --sandbox-app-url)").option("--sandbox-app-wait-timeout <ms>", "App readiness wait timeout in milliseconds").option("--e2b-template <name>", "Legacy alias for --sandbox-image").option("--timeout <ms>", "Workflow timeout").option("--json", "Output as JSON", false).action((name21, opts) => {
100972
101032
  try {
100973
101033
  const workflow = createTestingWorkflow({
100974
101034
  name: name21,
@@ -100994,6 +101054,7 @@ workflowCmd.command("create <name>").description("Save a reusable testing workfl
100994
101054
  sandboxSyncStrategy: opts.sandboxSync,
100995
101055
  setupCommand: opts.sandboxSetupCommand,
100996
101056
  packageSpec: opts.sandboxPackage,
101057
+ env: parseSandboxEnv(opts.sandboxEnv),
100997
101058
  appSourceDir: opts.sandboxAppSource,
100998
101059
  appRemoteDir: opts.sandboxAppRemoteDir,
100999
101060
  appStartCommand: opts.sandboxAppStartCommand,
package/dist/index.js CHANGED
@@ -17445,7 +17445,7 @@ async function runViaSandbox(plan, dependencies) {
17445
17445
  workflowId: plan.workflow.id,
17446
17446
  workflowName: plan.workflow.name
17447
17447
  },
17448
- sandboxEnvVars: plan.sandbox.env,
17448
+ sandboxEnvVars: resolveSandboxEnv(plan.sandbox.env),
17449
17449
  cleanup: plan.sandbox.cleanup,
17450
17450
  upload: {
17451
17451
  localDir: bundle.localDir,
@@ -17471,6 +17471,19 @@ async function runViaSandbox(plan, dependencies) {
17471
17471
  bundle.cleanup?.();
17472
17472
  }
17473
17473
  }
17474
+ function resolveSandboxEnv(env2) {
17475
+ if (!env2 || Object.keys(env2).length === 0)
17476
+ return;
17477
+ const resolved = {};
17478
+ for (const [key, value] of Object.entries(env2)) {
17479
+ const resolvedValue = resolveCredential(value);
17480
+ if (resolvedValue === null) {
17481
+ throw new Error(`Missing sandbox env value for ${key}`);
17482
+ }
17483
+ resolved[key] = resolvedValue;
17484
+ }
17485
+ return resolved;
17486
+ }
17474
17487
  async function resolveSandboxesRuntime(dependencies) {
17475
17488
  if (dependencies.sandboxes)
17476
17489
  return dependencies.sandboxes;
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-runner.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-runner.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EACV,MAAM,EACN,GAAG,EACH,eAAe,EAEf,sBAAsB,EACtB,2BAA2B,EAC5B,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,sBAAsB,CAAC;IAChC,YAAY,EAAE,2BAA2B,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,UAAU,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACxF,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,oBAAoB;IAC5B,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,MAAM,EAAE;QACN,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,mBAAmB,CAAC,KAAK,EAAE;QACzB,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACxC,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,EAAE;YACN,QAAQ,EAAE,MAAM,CAAC;YACjB,SAAS,EAAE,MAAM,CAAC;YAClB,YAAY,CAAC,EAAE,2BAA2B,CAAC;SAC5C,CAAC;QACF,OAAO,CAAC,EAAE,sBAAsB,CAAC;QACjC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;KACnC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,OAAO,WAAW,CAAC;IACjC,SAAS,CAAC,EAAE,wBAAwB,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACxF,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,KAAK,sBAAsB,CAAC;CACrG;AAaD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,kBAAkB,GAAG,eAAe,CAqB5G;AAED,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,kBAAkB,EAC3B,YAAY,GAAE,0BAA+B,GAC5C,OAAO,CAAC;IACT,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,eAAe,CAAC;IACtB,aAAa,CAAC,EAAE,8BAA8B,CAAC;CAChD,CAAC,CAiBD;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,eAAe,GACpB,sBAAsB,CAiBxB"}
1
+ {"version":3,"file":"workflow-runner.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-runner.ts"],"names":[],"mappings":"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"}
package/dist/mcp/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "@hasna/testers",
55
- version: "0.0.46",
55
+ version: "0.0.47",
56
56
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
57
57
  type: "module",
58
58
  main: "dist/index.js",
@@ -23723,7 +23723,7 @@ async function runViaSandbox(plan, dependencies) {
23723
23723
  workflowId: plan.workflow.id,
23724
23724
  workflowName: plan.workflow.name
23725
23725
  },
23726
- sandboxEnvVars: plan.sandbox.env,
23726
+ sandboxEnvVars: resolveSandboxEnv(plan.sandbox.env),
23727
23727
  cleanup: plan.sandbox.cleanup,
23728
23728
  upload: {
23729
23729
  localDir: bundle.localDir,
@@ -23749,6 +23749,19 @@ async function runViaSandbox(plan, dependencies) {
23749
23749
  bundle.cleanup?.();
23750
23750
  }
23751
23751
  }
23752
+ function resolveSandboxEnv(env2) {
23753
+ if (!env2 || Object.keys(env2).length === 0)
23754
+ return;
23755
+ const resolved = {};
23756
+ for (const [key, value] of Object.entries(env2)) {
23757
+ const resolvedValue = resolveCredential(value);
23758
+ if (resolvedValue === null) {
23759
+ throw new Error(`Missing sandbox env value for ${key}`);
23760
+ }
23761
+ resolved[key] = resolvedValue;
23762
+ }
23763
+ return resolved;
23764
+ }
23752
23765
  async function resolveSandboxesRuntime(dependencies) {
23753
23766
  if (dependencies.sandboxes)
23754
23767
  return dependencies.sandboxes;
@@ -23766,6 +23779,7 @@ var init_workflow_runner = __esm(() => {
23766
23779
  init_workflows();
23767
23780
  init_personas();
23768
23781
  init_runner();
23782
+ init_secrets_resolver();
23769
23783
  APP_SOURCE_EXCLUDES = [
23770
23784
  "node_modules",
23771
23785
  ".git",
@@ -46937,7 +46937,7 @@ import { join as join14 } from "path";
46937
46937
  // package.json
46938
46938
  var package_default = {
46939
46939
  name: "@hasna/testers",
46940
- version: "0.0.46",
46940
+ version: "0.0.47",
46941
46941
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
46942
46942
  type: "module",
46943
46943
  main: "dist/index.js",
@@ -51561,7 +51561,7 @@ async function runViaSandbox(plan, dependencies) {
51561
51561
  workflowId: plan.workflow.id,
51562
51562
  workflowName: plan.workflow.name
51563
51563
  },
51564
- sandboxEnvVars: plan.sandbox.env,
51564
+ sandboxEnvVars: resolveSandboxEnv(plan.sandbox.env),
51565
51565
  cleanup: plan.sandbox.cleanup,
51566
51566
  upload: {
51567
51567
  localDir: bundle.localDir,
@@ -51587,6 +51587,19 @@ async function runViaSandbox(plan, dependencies) {
51587
51587
  bundle.cleanup?.();
51588
51588
  }
51589
51589
  }
51590
+ function resolveSandboxEnv(env) {
51591
+ if (!env || Object.keys(env).length === 0)
51592
+ return;
51593
+ const resolved = {};
51594
+ for (const [key, value] of Object.entries(env)) {
51595
+ const resolvedValue = resolveCredential(value);
51596
+ if (resolvedValue === null) {
51597
+ throw new Error(`Missing sandbox env value for ${key}`);
51598
+ }
51599
+ resolved[key] = resolvedValue;
51600
+ }
51601
+ return resolved;
51602
+ }
51590
51603
  async function resolveSandboxesRuntime(dependencies) {
51591
51604
  if (dependencies.sandboxes)
51592
51605
  return dependencies.sandboxes;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/testers",
3
- "version": "0.0.46",
3
+ "version": "0.0.47",
4
4
  "description": "AI-powered QA testing CLI — spawns cheap AI agents to test web apps with headless browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",