testdriverai 7.8.0-test.32 → 7.8.0-test.39

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.
@@ -49,7 +49,7 @@ function generateTestResultsTable(testCases, testRunUrl) {
49
49
 
50
50
  // Filter out skipped tests
51
51
  const nonSkippedTests = testCases.filter(test => test.status !== 'skipped');
52
-
52
+
53
53
  if (nonSkippedTests.length === 0) {
54
54
  return '_No test cases to display (all tests were skipped)_';
55
55
  }
@@ -62,12 +62,12 @@ function generateTestResultsTable(testCases, testRunUrl) {
62
62
  const name = test.testName || 'Unknown';
63
63
  const file = test.testFile || 'unknown';
64
64
  const duration = formatDuration(test.duration || 0);
65
-
65
+
66
66
  // Use test run context URL instead of direct replay URL
67
67
  let replay = '-';
68
68
  if (test.replayUrl) {
69
69
  const linkUrl = (test.id && testRunUrl) ? `${testRunUrl}/${test.id}` : test.replayUrl;
70
-
70
+
71
71
  // Extract replay ID and generate GIF URL
72
72
  const replayId = extractReplayId(test.replayUrl);
73
73
  if (replayId) {
@@ -97,28 +97,28 @@ function generateTestResultsTable(testCases, testRunUrl) {
97
97
  */
98
98
  function generateExceptionsSection(testCases, testRunUrl) {
99
99
  const failedTests = testCases.filter(t => t.status === 'failed' && t.errorMessage);
100
-
100
+
101
101
  if (failedTests.length === 0) {
102
102
  return '';
103
103
  }
104
104
 
105
105
  let section = '\n## 🔴 Failures\n\n';
106
-
106
+
107
107
  for (const test of failedTests) {
108
108
  section += `### ${test.testName}\n\n`;
109
109
  section += `**File:** \`${test.testFile}\`\n\n`;
110
-
110
+
111
111
  // Use test run context URL instead of direct replay URL
112
112
  if (test.id && testRunUrl) {
113
113
  section += `**📹 [Watch Replay](${testRunUrl}/${test.id})**\n\n`;
114
114
  } else if (test.replayUrl) {
115
115
  section += `**📹 [Watch Replay](${test.replayUrl})**\n\n`;
116
116
  }
117
-
117
+
118
118
  section += '```\n';
119
119
  section += test.errorMessage || 'Unknown error';
120
120
  section += '\n```\n\n';
121
-
121
+
122
122
  if (test.errorStack) {
123
123
  section += '<details>\n';
124
124
  section += '<summary>Stack Trace</summary>\n\n';
@@ -140,22 +140,22 @@ function generateExceptionsSection(testCases, testRunUrl) {
140
140
  */
141
141
  function generateReplaySection(testCases, testRunUrl) {
142
142
  const testsWithReplays = testCases.filter(t => t.replayUrl);
143
-
143
+
144
144
  if (testsWithReplays.length === 0) {
145
145
  return '';
146
146
  }
147
147
 
148
148
  let section = '\n## 🎥 Dashcam Replays\n\n';
149
-
149
+
150
150
  for (const test of testsWithReplays) {
151
151
  section += `### ${test.testName}\n\n`;
152
-
152
+
153
153
  // Determine the link URL - prefer test run context
154
154
  let linkUrl = test.replayUrl;
155
155
  if (test.id && testRunUrl) {
156
156
  linkUrl = `${testRunUrl}/${test.id}`;
157
157
  }
158
-
158
+
159
159
  // Extract replay ID from URL for GIF embed
160
160
  const replayId = extractReplayId(test.replayUrl);
161
161
  if (replayId) {
@@ -177,7 +177,7 @@ function generateReplaySection(testCases, testRunUrl) {
177
177
  */
178
178
  function extractReplayId(url) {
179
179
  if (!url) return null;
180
-
180
+
181
181
  // Match pattern: /replay/{id} or /replay/{id}?params
182
182
  const match = url.match(/\/replay\/([^?/#]+)/);
183
183
  return match ? match[1] : null;
@@ -190,7 +190,7 @@ function extractReplayId(url) {
190
190
  */
191
191
  function extractShareKey(url) {
192
192
  if (!url) return null;
193
-
193
+
194
194
  // Match pattern: ?share=KEY or &share=KEY
195
195
  const match = url.match(/[?&]share=([^&#]+)/);
196
196
  return match ? match[1] : null;
@@ -204,32 +204,50 @@ function extractShareKey(url) {
204
204
  */
205
205
  function getReplayGifUrl(replayUrl, replayId) {
206
206
  // Determine the API base URL based on the replay URL
207
+ // Replay URLs use console domains; GIF endpoints live on the corresponding API domain
207
208
  let apiBaseUrl;
208
-
209
+
209
210
  if (replayUrl.includes('app.dashcam.io')) {
210
211
  // Production dashcam uses Heroku API
211
212
  apiBaseUrl = 'https://testdriverai-v6-c96fc597be11.herokuapp.com';
212
- } else if (replayUrl.includes('console.testdriver.ai')) {
213
- // TestDriver console
214
- apiBaseUrl = 'https://api.testdriver.ai';
215
213
  } else if (replayUrl.includes('localhost')) {
216
214
  // Local development
217
215
  apiBaseUrl = 'http://localhost:1337';
218
216
  } else {
219
- // Default: try to extract base URL from replay URL
220
- const urlObj = new URL(replayUrl);
221
- apiBaseUrl = `${urlObj.protocol}//${urlObj.host}`;
217
+ // Map console URLs API URLs for all environments
218
+ // console-test.testdriver.ai api-test.testdriver.ai
219
+ // console-canary.testdriver.ai → api-canary.testdriver.ai
220
+ // console.testdriver.ai → api.testdriver.ai
221
+ const consoleEnvMatch = replayUrl.match(/https:\/\/console-(test|canary)\.testdriver\.ai/);
222
+ if (consoleEnvMatch) {
223
+ apiBaseUrl = `https://api-${consoleEnvMatch[1]}.testdriver.ai`;
224
+ } else if (replayUrl.includes('console.testdriver.ai')) {
225
+ apiBaseUrl = 'https://api.testdriver.ai';
226
+ }
227
+ // Fly.io: map web app → API app
228
+ // pr-123-web.fly.dev → pr-123-api.fly.dev
229
+ // td-test-web.fly.dev → td-test-api.fly.dev
230
+ else {
231
+ const flyWebMatch = replayUrl.match(/https:\/\/([\w-]+)-web\.fly\.dev/);
232
+ if (flyWebMatch) {
233
+ apiBaseUrl = `https://${flyWebMatch[1]}-api.fly.dev`;
234
+ } else {
235
+ // Fallback: extract base URL from replay URL as-is
236
+ const urlObj = new URL(replayUrl);
237
+ apiBaseUrl = `${urlObj.protocol}//${urlObj.host}`;
238
+ }
239
+ }
222
240
  }
223
-
241
+
224
242
  // Extract share key if present
225
243
  const shareKey = extractShareKey(replayUrl);
226
-
244
+
227
245
  // Build GIF URL with shareKey parameter
228
246
  let gifUrl = `${apiBaseUrl}/replay/${replayId}/gif`;
229
247
  if (shareKey) {
230
248
  gifUrl += `?shareKey=${shareKey}`;
231
249
  }
232
-
250
+
233
251
  return gifUrl;
234
252
  }
235
253
 
@@ -257,9 +275,9 @@ export function generateGitHubComment(testRunData, testCases = []) {
257
275
  // Header with overall status
258
276
  const statusEmoji = getStatusEmoji(status);
259
277
  const statusColor = status === 'passed' ? '🟢' : status === 'failed' ? '🔴' : '🟡';
260
-
278
+
261
279
  let comment = `# ${statusColor} TestDriver Test Results\n\n`;
262
-
280
+
263
281
  // Compact summary line
264
282
  comment += `**Status:** ${statusEmoji} ${status.toUpperCase()}`;
265
283
  comment += ` • **Duration:** ${formatDuration(duration)}`;
@@ -270,23 +288,23 @@ export function generateGitHubComment(testRunData, testCases = []) {
270
288
  comment += `, ${skippedTests} skipped`;
271
289
  }
272
290
  comment += `\n\n`;
273
-
291
+
274
292
  // Exceptions section (only if there are failures) - show first
275
293
  comment += generateExceptionsSection(testCases, testRunUrl);
276
-
294
+
277
295
  // Test results table (now includes embedded GIFs)
278
296
  comment += '## 📝 Test Results\n\n';
279
297
  comment += generateTestResultsTable(testCases, testRunUrl);
280
-
298
+
281
299
  // Link to full test run (below table)
282
300
  if (testRunUrl) {
283
301
  comment += `\n[📋 View Full Test Run](${testRunUrl})\n`;
284
302
  }
285
-
303
+
286
304
  // Footer
287
305
  comment += '\n---\n';
288
306
  comment += `<sub>Generated by [TestDriver](https://testdriver.ai) • Run ID: \`${runId}\`</sub>\n`;
289
-
307
+
290
308
  return comment;
291
309
  }
292
310
 
@@ -303,15 +321,15 @@ export function generateGitHubComment(testRunData, testCases = []) {
303
321
  */
304
322
  export async function postGitHubComment(options) {
305
323
  const { token, owner, repo, prNumber, commitSha, body } = options;
306
-
324
+
307
325
  if (!token) {
308
326
  throw new Error('GitHub token is required');
309
327
  }
310
-
328
+
311
329
  if (!owner || !repo) {
312
330
  throw new Error('Repository owner and name are required');
313
331
  }
314
-
332
+
315
333
  if (!prNumber && !commitSha) {
316
334
  throw new Error('Either prNumber or commitSha must be provided');
317
335
  }
@@ -351,7 +369,7 @@ export async function postGitHubComment(options) {
351
369
  */
352
370
  export async function updateGitHubComment(options) {
353
371
  const { token, owner, repo, commentId, body } = options;
354
-
372
+
355
373
  if (!token || !owner || !repo || !commentId) {
356
374
  throw new Error('Token, owner, repo, and commentId are required');
357
375
  }
@@ -364,7 +382,7 @@ export async function updateGitHubComment(options) {
364
382
  comment_id: commentId,
365
383
  body,
366
384
  });
367
-
385
+
368
386
  return response.data;
369
387
  }
370
388
 
@@ -379,7 +397,7 @@ export async function updateGitHubComment(options) {
379
397
  */
380
398
  export async function findExistingComment(options) {
381
399
  const { token, owner, repo, prNumber } = options;
382
-
400
+
383
401
  if (!token || !owner || !repo || !prNumber) {
384
402
  return null;
385
403
  }
@@ -410,11 +428,11 @@ export async function findExistingComment(options) {
410
428
  */
411
429
  export async function postOrUpdateTestResults(testRunData, testCases, githubOptions) {
412
430
  const commentBody = generateGitHubComment(testRunData, testCases);
413
-
431
+
414
432
  // Try to find and delete existing comment to keep it at the end
415
433
  if (githubOptions.prNumber) {
416
434
  const existingComment = await findExistingComment(githubOptions);
417
-
435
+
418
436
  if (existingComment) {
419
437
  // Delete the old comment
420
438
  const octokit = new Octokit({ auth: githubOptions.token });
@@ -425,7 +443,7 @@ export async function postOrUpdateTestResults(testRunData, testCases, githubOpti
425
443
  });
426
444
  }
427
445
  }
428
-
446
+
429
447
  // Always create a new comment (will be at the end of the thread)
430
448
  return await postGitHubComment({
431
449
  ...githubOptions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "7.8.0-test.32",
3
+ "version": "7.8.0-test.39",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "sdk.js",
6
6
  "types": "sdk.d.ts",
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Usage: ./install-dev-runner.sh <instance-id>
5
+ INSTANCE_ID="${1:?Usage: $0 <instance-id>}"
6
+ AWS_REGION="${AWS_REGION:-us-east-2}"
7
+
8
+ RUNNER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../runner" && pwd)"
9
+
10
+ echo "Packing local runner..."
11
+ TMPDIR=$(mktemp -d)
12
+ pushd "$RUNNER_DIR" > /dev/null
13
+ npm pack --pack-destination "$TMPDIR" > /dev/null 2>&1
14
+ TARBALL=$(ls "$TMPDIR"/*.tgz)
15
+ popd > /dev/null
16
+ echo "Tarball: $TARBALL"
17
+
18
+ echo "Uploading to S3..."
19
+ S3_KEY="runner-dev/$(date +%s)-$(openssl rand -hex 4)/runner.tgz"
20
+ aws s3 cp "$TARBALL" "s3://v7-transfer/${S3_KEY}" --region "$AWS_REGION" > /dev/null
21
+ DOWNLOAD_URL=$(aws s3 presign "s3://v7-transfer/${S3_KEY}" --expires-in 900 --region "$AWS_REGION")
22
+ rm -rf "$TMPDIR"
23
+
24
+ echo "Creating SSM params file..."
25
+
26
+ # Write Python script to temp file to generate valid JSON
27
+ PYTHON_SCRIPT=$(mktemp --suffix=.py)
28
+ cat > "$PYTHON_SCRIPT" << 'PYEOF'
29
+ import json
30
+ import sys
31
+
32
+ url = sys.argv[1]
33
+
34
+ commands = [
35
+ "Write-Host '=== Stopping runner ==='",
36
+ "Stop-ScheduledTask -TaskName RunTestDriverAgent -ErrorAction SilentlyContinue",
37
+ "Stop-Process -Name node -Force -ErrorAction SilentlyContinue",
38
+ "Start-Sleep -Seconds 2",
39
+ "Set-Location 'C:\\testdriver\\sandbox-agent'",
40
+ "$tarball = 'C:\\Windows\\Temp\\runner-dev.tgz'",
41
+ f"Invoke-WebRequest -Uri '{url}' -OutFile $tarball",
42
+ "Write-Host 'Tarball size:'; (Get-Item $tarball).Length",
43
+ "Remove-Item -Path lib -Recurse -Force -ErrorAction SilentlyContinue",
44
+ "tar -xzf $tarball --strip-components=1 -C .",
45
+ "Get-Content 'package.json' | ConvertFrom-Json | Select-Object -ExpandProperty version",
46
+ "Write-Host '=== Starting runner ==='",
47
+ "Start-ScheduledTask -TaskName RunTestDriverAgent",
48
+ "Start-Sleep -Seconds 3",
49
+ "Get-Content 'C:\\testdriver\\log.txt' -Tail 20"
50
+ ]
51
+
52
+ params = {"commands": commands}
53
+ print(json.dumps(params))
54
+ PYEOF
55
+
56
+ python3 "$PYTHON_SCRIPT" "$DOWNLOAD_URL" > /tmp/ssm-install-params.json
57
+ rm "$PYTHON_SCRIPT"
58
+
59
+ echo "Sending SSM command..."
60
+ CMD_JSON=$(aws ssm send-command \
61
+ --region "$AWS_REGION" \
62
+ --instance-ids "$INSTANCE_ID" \
63
+ --document-name "AWS-RunPowerShellScript" \
64
+ --parameters "file:///tmp/ssm-install-params.json" \
65
+ --output json)
66
+
67
+ COMMAND_ID=$(echo "$CMD_JSON" | jq -r '.Command.CommandId')
68
+ echo "Command ID: $COMMAND_ID"
69
+
70
+ echo "Waiting for completion..."
71
+ aws ssm wait command-executed --region "$AWS_REGION" --command-id "$COMMAND_ID" --instance-id "$INSTANCE_ID" || true
72
+
73
+ echo "Getting output..."
74
+ aws ssm get-command-invocation \
75
+ --region "$AWS_REGION" \
76
+ --command-id "$COMMAND_ID" \
77
+ --instance-id "$INSTANCE_ID" \
78
+ --query 'StandardOutputContent' \
79
+ --output text
@@ -141,6 +141,140 @@ while :; do
141
141
  sleep 20
142
142
  done
143
143
 
144
+ # --- 4) Install/update runner ---
145
+ echo "Installing runner..."
146
+
147
+ # Determine environment and version
148
+ TD_ENV="${TD_ENV:-stable}"
149
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
150
+ SDK_PKG_JSON="${SCRIPT_DIR}/../../../sdk/package.json"
151
+ RUNNER_DIR="${SCRIPT_DIR}/../../../runner"
152
+
153
+ if [ -f "$SDK_PKG_JSON" ]; then
154
+ RUNNER_VERSION=$(jq -r '.version' "$SDK_PKG_JSON")
155
+ echo "Runner version from SDK: $RUNNER_VERSION"
156
+ else
157
+ RUNNER_VERSION="$TD_ENV"
158
+ echo "SDK package.json not found, using env tag: $RUNNER_VERSION"
159
+ fi
160
+
161
+ if [ "$TD_ENV" = "dev" ]; then
162
+ echo "Dev mode: packing and uploading local runner to S3..."
163
+
164
+ # Pack local runner
165
+ TMPDIR=$(mktemp -d)
166
+ pushd "$RUNNER_DIR" > /dev/null
167
+ npm pack --pack-destination "$TMPDIR" > /dev/null 2>&1
168
+ TARBALL=$(ls "$TMPDIR"/*.tgz | head -1)
169
+ popd > /dev/null
170
+
171
+ # Upload to S3
172
+ S3_BUCKET="${AWS_BUCKET_IMAGE_TRANSFER:-v7-transfer}"
173
+ S3_KEY="runner-dev/$(date +%s)-$(openssl rand -hex 4)/runner.tgz"
174
+ aws s3 cp "$TARBALL" "s3://${S3_BUCKET}/${S3_KEY}" --region "$AWS_REGION"
175
+
176
+ # Generate presigned URL (15 min)
177
+ DOWNLOAD_URL=$(aws s3 presign "s3://${S3_BUCKET}/${S3_KEY}" --expires-in 900 --region "$AWS_REGION")
178
+ rm -rf "$TMPDIR"
179
+
180
+ # Build SSM parameters JSON in a temp file to avoid shell escaping issues with URL
181
+ PARAMS_FILE=$(mktemp)
182
+ cat > "$PARAMS_FILE" << 'PARAMS_EOF'
183
+ {
184
+ "commands": [
185
+ "Write-Host '=== Starting runner dev install ==='",
186
+ "Write-Host 'Stopping existing runner processes...'",
187
+ "Stop-ScheduledTask -TaskName RunTestDriverAgent -ErrorAction SilentlyContinue",
188
+ "Stop-Process -Name node -Force -ErrorAction SilentlyContinue",
189
+ "Start-Sleep -Seconds 2",
190
+ "Write-Host 'Current runner version:'",
191
+ "Get-Content 'C:\\testdriver\\sandbox-agent\\package.json' | ConvertFrom-Json | Select-Object -ExpandProperty version",
192
+ "Set-Location 'C:\\testdriver\\sandbox-agent'",
193
+ "Write-Host 'Dev mode: downloading runner from S3...'",
194
+ "$tarball = 'C:\\Windows\\Temp\\runner-dev.tgz'",
195
+ PARAMS_EOF
196
+
197
+ # Add the URL line with proper JSON escaping
198
+ echo " \"Invoke-WebRequest -Uri '$(echo "$DOWNLOAD_URL" | sed 's/"/\\"/g')' -OutFile \$tarball\"," >> "$PARAMS_FILE"
199
+
200
+ cat >> "$PARAMS_FILE" << 'PARAMS_EOF'
201
+ "Write-Host 'Downloaded tarball size:'",
202
+ "(Get-Item $tarball).Length",
203
+ "Write-Host 'Extracting runner...'",
204
+ "tar -xzf $tarball -C 'C:\\Windows\\Temp'",
205
+ "Write-Host 'Extracted package contents:'",
206
+ "Get-ChildItem 'C:\\Windows\\Temp\\package' -Recurse | Select-Object FullName",
207
+ "Write-Host 'New runner version in package:'",
208
+ "Get-Content 'C:\\Windows\\Temp\\package\\package.json' | ConvertFrom-Json | Select-Object -ExpandProperty version",
209
+ "Write-Host 'Clearing old lib folder...'",
210
+ "Remove-Item 'C:\\testdriver\\sandbox-agent\\lib' -Recurse -Force -ErrorAction SilentlyContinue",
211
+ "Write-Host 'Copying files to sandbox-agent...'",
212
+ "xcopy 'C:\\Windows\\Temp\\package\\*' 'C:\\testdriver\\sandbox-agent\\' /E /Y /I",
213
+ "Write-Host 'Files after copy:'",
214
+ "Get-ChildItem 'C:\\testdriver\\sandbox-agent' | Select-Object Name",
215
+ "Remove-Item 'C:\\Windows\\Temp\\package' -Recurse -Force -ErrorAction SilentlyContinue",
216
+ "Remove-Item $tarball -Force -ErrorAction SilentlyContinue",
217
+ "Write-Host 'Runner version after copy:'",
218
+ "Get-Content 'C:\\testdriver\\sandbox-agent\\package.json' | ConvertFrom-Json | Select-Object -ExpandProperty version",
219
+ "Write-Host 'Installing npm dependencies...'",
220
+ "npm install --omit=dev 2>&1 | Write-Host",
221
+ "Write-Host 'Final verification - ably-service.js exists:'",
222
+ "Test-Path 'C:\\testdriver\\sandbox-agent\\lib\\ably-service.js'",
223
+ "Write-Host 'Restarting RunTestDriverAgent scheduled task...'",
224
+ "Start-ScheduledTask -TaskName RunTestDriverAgent -ErrorAction SilentlyContinue",
225
+ "Write-Host '=== Runner install complete (dev) ==='"
226
+ ]
227
+ }
228
+ PARAMS_EOF
229
+
230
+ echo "Sending SSM command to download and install runner from S3..."
231
+ INSTALL_CMD=$(aws ssm send-command \
232
+ --region "$AWS_REGION" \
233
+ --instance-ids "$INSTANCE_ID" \
234
+ --document-name "AWS-RunPowerShellScript" \
235
+ --parameters "file://$PARAMS_FILE" \
236
+ --timeout-seconds 180 \
237
+ --output json)
238
+ rm -f "$PARAMS_FILE"
239
+ else
240
+ echo "Installing @testdriverai/runner@${RUNNER_VERSION} via npm..."
241
+ INSTALL_CMD=$(aws ssm send-command \
242
+ --region "$AWS_REGION" \
243
+ --instance-ids "$INSTANCE_ID" \
244
+ --document-name "AWS-RunPowerShellScript" \
245
+ --parameters "commands=[
246
+ \"Set-Location 'C:\\\\testdriver\\\\sandbox-agent'\",
247
+ \"Write-Host 'Installing @testdriverai/runner@${RUNNER_VERSION}...'\",
248
+ \"npm install @testdriverai/runner@${RUNNER_VERSION} --omit=dev 2>&1 | Write-Host\",
249
+ \"Write-Host 'Stopping runner (config not yet provisioned)...'\",
250
+ \"Stop-Process -Name node -Force -ErrorAction SilentlyContinue\",
251
+ \"Stop-ScheduledTask -TaskName RunTestDriverAgent -ErrorAction SilentlyContinue\",
252
+ \"Start-Sleep -Seconds 2\",
253
+ \"Start-ScheduledTask -TaskName RunTestDriverAgent -ErrorAction SilentlyContinue\",
254
+ \"Write-Host 'Runner install complete'\"
255
+ ]" \
256
+ --timeout-seconds 120 \
257
+ --output json)
258
+ fi
259
+
260
+ INSTALL_CMD_ID=$(jq -r '.Command.CommandId' <<<"$INSTALL_CMD")
261
+ echo "Runner install command sent (Command ID: $INSTALL_CMD_ID)"
262
+
263
+ # Wait for install to complete
264
+ echo "Waiting for runner install to complete..."
265
+ if aws ssm wait command-executed --region "$AWS_REGION" --command-id "$INSTALL_CMD_ID" --instance-id "$INSTANCE_ID" 2>/dev/null; then
266
+ echo "✓ Runner install succeeded"
267
+ else
268
+ INSTALL_STATUS=$(aws ssm get-command-invocation \
269
+ --region "$AWS_REGION" \
270
+ --command-id "$INSTALL_CMD_ID" \
271
+ --instance-id "$INSTANCE_ID" \
272
+ --output json 2>/dev/null || echo '{}')
273
+ echo "⚠ Runner install status: $(jq -r '.Status // "Unknown"' <<<"$INSTALL_STATUS")"
274
+ echo "Output: $(jq -r '.StandardOutputContent // "No output"' <<<"$INSTALL_STATUS" | head -20)"
275
+ echo "Errors: $(jq -r '.StandardErrorContent // "No errors"' <<<"$INSTALL_STATUS" | head -10)"
276
+ fi
277
+
144
278
  echo "Getting Public IP..."
145
279
 
146
280
  # --- 5) Get instance Public IP ---
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Vitest config for runner/packer tests.
3
+ * Lives under sdk/ so vitest resolves from sdk/node_modules,
4
+ * but uses shared/resolve-env.js for environment variable loading.
5
+ */
6
+ import { defineConfig } from "vitest/config";
7
+ import { createRequire } from "module";
8
+ import { dirname, resolve } from "path";
9
+ import { fileURLToPath } from "url";
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const monoRoot = resolve(__dirname, "..");
13
+
14
+ const require = createRequire(import.meta.url);
15
+ const { resolveEnv } = require("../shared/resolve-env");
16
+
17
+ const plan = process.env.TD_PLAN || "enterprise";
18
+ const env = process.env.TD_ENV || "dev";
19
+ const resolved = resolveEnv(env, plan);
20
+
21
+ // Apply to the main process so test code sees the vars immediately
22
+ Object.assign(process.env, resolved);
23
+
24
+ export default defineConfig({
25
+ test: {
26
+ root: monoRoot,
27
+ testTimeout: 900_000, // 15 min per test
28
+ hookTimeout: 2_400_000, // 40 min for beforeAll (AMI build + spawn)
29
+ reporters: ["default"],
30
+ include: ["runner/packer/test/**/*.test.mjs"],
31
+ env: resolved,
32
+ },
33
+ });