@walkeros/cli 3.0.2 → 4.0.0-next-1773967844643

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/index.js CHANGED
@@ -193,50 +193,44 @@ var init_config_file = __esm({
193
193
  }
194
194
  });
195
195
 
196
- // src/core/auth.ts
197
- function getToken() {
198
- const result = resolveToken();
199
- return result?.token;
200
- }
201
- function getAuthHeaders() {
202
- const token = getToken();
203
- if (!token) return {};
204
- return { Authorization: `Bearer ${token}` };
205
- }
206
- async function authenticatedFetch(url, init) {
207
- const authHeaders = getAuthHeaders();
208
- const existingHeaders = init?.headers instanceof Headers ? Object.fromEntries(init.headers.entries()) : Array.isArray(init?.headers) ? Object.fromEntries(init.headers) : init?.headers ?? {};
209
- return fetch(url, {
196
+ // src/core/http.ts
197
+ function normalizeHeaders(headers) {
198
+ if (!headers) return {};
199
+ if (headers instanceof Headers) return Object.fromEntries(headers.entries());
200
+ if (Array.isArray(headers)) return Object.fromEntries(headers);
201
+ return headers;
202
+ }
203
+ function mergeAuthHeaders(token, headers) {
204
+ const normalized = normalizeHeaders(headers);
205
+ if (token) normalized.Authorization = `Bearer ${token}`;
206
+ return normalized;
207
+ }
208
+ async function apiFetch(path15, init) {
209
+ const baseUrl = resolveAppUrl();
210
+ const token = resolveToken()?.token;
211
+ return fetch(`${baseUrl}${path15}`, {
210
212
  ...init,
211
- headers: { ...existingHeaders, ...authHeaders }
213
+ headers: mergeAuthHeaders(token, init?.headers)
212
214
  });
213
215
  }
214
- async function deployAuthenticatedFetch(url, init) {
215
- const deployToken = resolveDeployToken();
216
- const token = deployToken ?? getToken();
216
+ async function publicFetch(path15, init) {
217
+ const baseUrl = resolveAppUrl();
218
+ return fetch(`${baseUrl}${path15}`, init);
219
+ }
220
+ async function deployFetch(path15, init) {
221
+ const baseUrl = resolveAppUrl();
222
+ const token = resolveDeployToken() ?? resolveToken()?.token;
217
223
  if (!token)
218
224
  throw new Error(
219
225
  "No authentication token available. Set WALKEROS_DEPLOY_TOKEN or run walkeros auth login."
220
226
  );
221
- const existingHeaders = init?.headers instanceof Headers ? Object.fromEntries(init.headers.entries()) : Array.isArray(init?.headers) ? Object.fromEntries(init.headers) : init?.headers ?? {};
222
- return fetch(url, {
227
+ return fetch(`${baseUrl}${path15}`, {
223
228
  ...init,
224
- headers: { ...existingHeaders, Authorization: `Bearer ${token}` }
229
+ headers: mergeAuthHeaders(token, init?.headers)
225
230
  });
226
231
  }
227
- function resolveRunToken() {
228
- return resolveDeployToken() ?? resolveToken()?.token ?? null;
229
- }
230
- function resolveBaseUrl() {
231
- return resolveAppUrl();
232
- }
233
- function requireProjectId() {
234
- const projectId = process.env.WALKEROS_PROJECT_ID;
235
- if (!projectId) throw new Error("WALKEROS_PROJECT_ID not set.");
236
- return projectId;
237
- }
238
- var init_auth = __esm({
239
- "src/core/auth.ts"() {
232
+ var init_http = __esm({
233
+ "src/core/http.ts"() {
240
234
  "use strict";
241
235
  init_config_file();
242
236
  }
@@ -259,7 +253,10 @@ async function downloadFromUrl(url) {
259
253
  throw new Error(`Invalid URL: ${url}`);
260
254
  }
261
255
  try {
262
- const response = await authenticatedFetch(url);
256
+ const token = resolveToken()?.token;
257
+ const response = await fetch(url, {
258
+ headers: mergeAuthHeaders(token)
259
+ });
263
260
  if (!response.ok) {
264
261
  throw new Error(
265
262
  `Failed to download ${url}: ${response.status} ${response.statusText}`
@@ -280,32 +277,44 @@ async function downloadFromUrl(url) {
280
277
  }
281
278
  }
282
279
  async function loadJsonConfig(configPath) {
283
- let absolutePath;
284
- let isTemporary = false;
285
- if (isUrl(configPath)) {
286
- absolutePath = await downloadFromUrl(configPath);
287
- isTemporary = true;
288
- } else {
289
- absolutePath = path3.resolve(configPath);
290
- if (!await fs2.pathExists(absolutePath)) {
291
- throw new Error(`Configuration file not found: ${absolutePath}`);
292
- }
293
- }
294
- try {
295
- const rawConfig = await fs2.readJson(absolutePath);
296
- return rawConfig;
297
- } catch (error) {
298
- throw new Error(
299
- `Invalid JSON in config file: ${configPath}. ${error instanceof Error ? error.message : error}`
300
- );
301
- } finally {
302
- if (isTemporary) {
280
+ const trimmed = configPath.trim();
281
+ if (isUrl(trimmed)) {
282
+ const absolutePath2 = await downloadFromUrl(trimmed);
283
+ try {
284
+ const rawConfig = await fs2.readJson(absolutePath2);
285
+ return rawConfig;
286
+ } catch (error) {
287
+ throw new Error(
288
+ `Invalid JSON in config file: ${configPath}. ${error instanceof Error ? error.message : error}`
289
+ );
290
+ } finally {
303
291
  try {
304
- await fs2.remove(absolutePath);
292
+ await fs2.remove(absolutePath2);
305
293
  } catch {
306
294
  }
307
295
  }
308
296
  }
297
+ const absolutePath = path3.resolve(trimmed);
298
+ if (await fs2.pathExists(absolutePath)) {
299
+ try {
300
+ const rawConfig = await fs2.readJson(absolutePath);
301
+ return rawConfig;
302
+ } catch (error) {
303
+ throw new Error(
304
+ `Invalid JSON in config file: ${configPath}. ${error instanceof Error ? error.message : error}`
305
+ );
306
+ }
307
+ }
308
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
309
+ try {
310
+ return JSON.parse(trimmed);
311
+ } catch (jsonError) {
312
+ throw new Error(
313
+ `Input appears to be JSON but contains errors: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}`
314
+ );
315
+ }
316
+ }
317
+ throw new Error(`Configuration file not found: ${absolutePath}`);
309
318
  }
310
319
  async function loadJsonFromSource(source, options) {
311
320
  const paramName = options?.name || "input";
@@ -365,7 +374,8 @@ var init_utils = __esm({
365
374
  "use strict";
366
375
  init_core();
367
376
  init_tmp();
368
- init_auth();
377
+ init_http();
378
+ init_config_file();
369
379
  }
370
380
  });
371
381
 
@@ -495,7 +505,10 @@ function detectPlatformFromPath(inputPath) {
495
505
  }
496
506
  async function loadContent(inputPath) {
497
507
  if (isUrl(inputPath)) {
498
- const response = await authenticatedFetch(inputPath);
508
+ const token = resolveToken()?.token;
509
+ const response = await fetch(inputPath, {
510
+ headers: mergeAuthHeaders(token)
511
+ });
499
512
  if (!response.ok) {
500
513
  throw new Error(`Failed to fetch ${inputPath}: ${response.status}`);
501
514
  }
@@ -507,7 +520,8 @@ var init_input_detector = __esm({
507
520
  "src/core/input-detector.ts"() {
508
521
  "use strict";
509
522
  init_utils();
510
- init_auth();
523
+ init_http();
524
+ init_config_file();
511
525
  }
512
526
  });
513
527
 
@@ -532,6 +546,31 @@ var init_stdin = __esm({
532
546
  }
533
547
  });
534
548
 
549
+ // src/core/auth.ts
550
+ function getToken() {
551
+ const result = resolveToken();
552
+ return result?.token;
553
+ }
554
+ function getAuthHeaders() {
555
+ const token = getToken();
556
+ if (!token) return {};
557
+ return { Authorization: `Bearer ${token}` };
558
+ }
559
+ function resolveRunToken() {
560
+ return resolveDeployToken() ?? resolveToken()?.token ?? null;
561
+ }
562
+ function requireProjectId() {
563
+ const projectId = process.env.WALKEROS_PROJECT_ID;
564
+ if (!projectId) throw new Error("WALKEROS_PROJECT_ID not set.");
565
+ return projectId;
566
+ }
567
+ var init_auth = __esm({
568
+ "src/core/auth.ts"() {
569
+ "use strict";
570
+ init_config_file();
571
+ }
572
+ });
573
+
535
574
  // src/core/sse.ts
536
575
  function parseSSEEvents(buffer) {
537
576
  const events = [];
@@ -574,6 +613,7 @@ var init_core = __esm({
574
613
  init_input_detector();
575
614
  init_stdin();
576
615
  init_auth();
616
+ init_http();
577
617
  init_sse();
578
618
  }
579
619
  });
@@ -1116,6 +1156,7 @@ var init_build_cache = __esm({
1116
1156
  // src/commands/bundle/bundler.ts
1117
1157
  import crypto2 from "crypto";
1118
1158
  import esbuild from "esbuild";
1159
+ import { builtinModules } from "module";
1119
1160
  import path9 from "path";
1120
1161
  import fs8 from "fs-extra";
1121
1162
  import { packageNameToVariable, ENV_MARKER_PREFIX } from "@walkeros/core";
@@ -1287,6 +1328,12 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false)
1287
1328
  if (hasSourcesOrDests && !buildOptions.packages["@walkeros/collector"]) {
1288
1329
  buildOptions.packages["@walkeros/collector"] = {};
1289
1330
  }
1331
+ const stepPackages = collectStepPackages(flowSettings);
1332
+ for (const pkg of stepPackages) {
1333
+ if (!buildOptions.packages[pkg]) {
1334
+ buildOptions.packages[pkg] = {};
1335
+ }
1336
+ }
1290
1337
  logger.debug("Downloading packages");
1291
1338
  const packagesArray = Object.entries(buildOptions.packages).map(
1292
1339
  ([name, packageConfig]) => ({
@@ -1457,23 +1504,8 @@ function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, pack
1457
1504
  };
1458
1505
  baseOptions.external = buildOptions.external || [];
1459
1506
  } else if (buildOptions.platform === "node") {
1460
- const nodeBuiltins = [
1461
- "crypto",
1462
- "fs",
1463
- "path",
1464
- "os",
1465
- "util",
1466
- "stream",
1467
- "buffer",
1468
- "events",
1469
- "http",
1470
- "https",
1471
- "url",
1472
- "querystring",
1473
- "zlib"
1474
- ];
1475
- const npmPackages = ["express", "express/*", "cors", "cors/*"];
1476
- baseOptions.external = buildOptions.external ? [...nodeBuiltins, ...npmPackages, ...buildOptions.external] : [...nodeBuiltins, ...npmPackages];
1507
+ const nodeExternals = getNodeExternals();
1508
+ baseOptions.external = buildOptions.external ? [...nodeExternals, ...buildOptions.external] : nodeExternals;
1477
1509
  if (buildOptions.format === "esm") {
1478
1510
  baseOptions.banner = {
1479
1511
  js: `import { createRequire } from 'module';const require = createRequire(import.meta.url);`
@@ -1548,6 +1580,36 @@ function detectStorePackages(flowSettings) {
1548
1580
  }
1549
1581
  return storePackages;
1550
1582
  }
1583
+ function getNodeExternals() {
1584
+ const externals = [];
1585
+ for (const mod of builtinModules) {
1586
+ if (mod.startsWith("_")) continue;
1587
+ externals.push(mod, `node:${mod}`, `${mod}/*`, `node:${mod}/*`);
1588
+ }
1589
+ return externals;
1590
+ }
1591
+ function collectStepPackages(flowSettings) {
1592
+ const allPackages = /* @__PURE__ */ new Set();
1593
+ for (const pkg of detectSourcePackages(flowSettings)) {
1594
+ allPackages.add(pkg);
1595
+ }
1596
+ for (const pkg of detectDestinationPackages(flowSettings)) {
1597
+ allPackages.add(pkg);
1598
+ }
1599
+ for (const pkg of detectTransformerPackages(flowSettings)) {
1600
+ allPackages.add(pkg);
1601
+ }
1602
+ for (const pkg of detectStorePackages(flowSettings)) {
1603
+ allPackages.add(pkg);
1604
+ }
1605
+ const npmPackages = /* @__PURE__ */ new Set();
1606
+ for (const pkg of allPackages) {
1607
+ if (!pkg.startsWith(".") && !pkg.startsWith("/")) {
1608
+ npmPackages.add(pkg);
1609
+ }
1610
+ }
1611
+ return npmPackages;
1612
+ }
1551
1613
  function detectExplicitCodeImports(flowSettings) {
1552
1614
  const explicitCodeImports = /* @__PURE__ */ new Map();
1553
1615
  const destinations = flowSettings.destinations;
@@ -2133,7 +2195,7 @@ function createApiClient() {
2133
2195
  const token = getToken();
2134
2196
  if (!token) throw new Error("WALKEROS_TOKEN not set.");
2135
2197
  return createClient({
2136
- baseUrl: resolveBaseUrl(),
2198
+ baseUrl: resolveAppUrl(),
2137
2199
  headers: {
2138
2200
  Authorization: `Bearer ${token}`,
2139
2201
  "Content-Type": "application/json"
@@ -2144,6 +2206,7 @@ var init_api_client = __esm({
2144
2206
  "src/core/api-client.ts"() {
2145
2207
  "use strict";
2146
2208
  init_auth();
2209
+ init_config_file();
2147
2210
  }
2148
2211
  });
2149
2212
 
@@ -2423,14 +2486,14 @@ __export(cache_exports, {
2423
2486
  });
2424
2487
  import {
2425
2488
  existsSync as existsSync4,
2426
- mkdirSync as mkdirSync2,
2489
+ mkdirSync as mkdirSync3,
2427
2490
  copyFileSync,
2428
2491
  writeFileSync as writeFileSync3,
2429
2492
  readFileSync as readFileSync2
2430
2493
  } from "fs";
2431
2494
  import { join as join2 } from "path";
2432
2495
  function writeCache(cacheDir, bundlePath, configContent, version) {
2433
- mkdirSync2(cacheDir, { recursive: true });
2496
+ mkdirSync3(cacheDir, { recursive: true });
2434
2497
  copyFileSync(bundlePath, join2(cacheDir, "bundle.mjs"));
2435
2498
  writeFileSync3(join2(cacheDir, "config.json"), configContent, "utf-8");
2436
2499
  const meta = { version, timestamp: Date.now() };
@@ -2937,7 +3000,7 @@ function rn(n, e2) {
2937
3000
  }, () => J({ ok: false }))(), "Push", n.hooks);
2938
3001
  }
2939
3002
  async function un(n) {
2940
- const e2 = r({ globalsStatic: {}, sessionStatic: {}, tagging: 0, run: true }, n, { merge: false, extend: false }), t2 = { level: n.logger?.level, handler: n.logger?.handler }, o2 = i(t2), s2 = { ...e2.globalsStatic, ...n.globals }, a2 = { allowed: false, config: e2, consent: n.consent || {}, count: 0, custom: n.custom || {}, destinations: {}, transformers: {}, stores: {}, globals: s2, group: "", hooks: {}, logger: o2, on: {}, queue: [], round: 0, session: void 0, status: { startedAt: Date.now(), in: 0, out: 0, failed: 0, sources: {}, destinations: {} }, timing: Date.now(), user: n.user || {}, version: "3.0.1", sources: {}, pending: { sources: {}, destinations: {} }, push: void 0, command: void 0 };
3003
+ const e2 = r({ globalsStatic: {}, sessionStatic: {}, tagging: 0, run: true }, n, { merge: false, extend: false }), t2 = { level: n.logger?.level, handler: n.logger?.handler }, o2 = i(t2), s2 = { ...e2.globalsStatic, ...n.globals }, a2 = { allowed: false, config: e2, consent: n.consent || {}, count: 0, custom: n.custom || {}, destinations: {}, transformers: {}, stores: {}, globals: s2, group: "", hooks: {}, logger: o2, on: {}, queue: [], round: 0, session: void 0, status: { startedAt: Date.now(), in: 0, out: 0, failed: 0, sources: {}, destinations: {} }, timing: Date.now(), user: n.user || {}, version: "3.0.2", sources: {}, pending: { sources: {}, destinations: {} }, push: void 0, command: void 0 };
2941
3004
  a2.push = rn(a2, (n2) => ({ timing: Math.round((Date.now() - a2.timing) / 10) / 100, source: { type: "collector", id: "", previous_id: "" }, ...n2 })), a2.command = (function(n2, e3) {
2942
3005
  return an(async (t3, o3, s3) => await cn(async () => await e3(n2, t3, o3, s3), () => J({ ok: false }))(), "Command", n2.hooks);
2943
3006
  })(a2, Y);
@@ -3034,14 +3097,8 @@ async function dn(n) {
3034
3097
  })(n, e2);
3035
3098
  case "source":
3036
3099
  return await (async function(n2, e3) {
3037
- const { code: t2, config: o2 = {}, setup: s2, input: r2, env: i2, consent: a2 } = n2, c2 = { functional: true, marketing: true, analytics: true };
3038
- let u2;
3039
- if (s2) {
3040
- const n3 = s2(r2, i2);
3041
- "function" == typeof n3 && (u2 = n3);
3042
- }
3043
- const f2 = [], { collector: l2 } = await fn({ consent: a2 || c2, sources: { sim: { code: t2, config: o2, env: i2, next: "spy" } }, transformers: { spy: { code: () => ({ type: "spy", config: {}, push: (n3) => (f2.push(JSON.parse(JSON.stringify(n3))), { event: n3 }) }) } } });
3044
- u2 && u2();
3100
+ const { code: t2, config: o2 = {}, createTrigger: s2, input: r2, consent: i2 } = n2, { content: a2, trigger: c2 } = r2, u2 = { functional: true, marketing: true, analytics: true }, f2 = [], l2 = { consent: i2 || u2, sources: { sim: { code: t2, config: o2, next: "spy" } }, transformers: { spy: { code: () => ({ type: "spy", config: {}, push: (n3) => (f2.push(JSON.parse(JSON.stringify(n3))), { event: n3 }) }) } } }, { trigger: g2 } = await s2(l2);
3101
+ void 0 !== a2 && await g2(c2?.type, c2?.options)(a2);
3045
3102
  return { step: "source", name: n2.name, events: f2, calls: [], duration: Date.now() - e3 };
3046
3103
  })(n, e2);
3047
3104
  case "destination":
@@ -3141,9 +3198,6 @@ function formatSimulationResult(result, options = {}) {
3141
3198
  if (result.capturedEvents) {
3142
3199
  output.capturedEvents = result.capturedEvents;
3143
3200
  }
3144
- if (result.exampleMatch) {
3145
- output.exampleMatch = result.exampleMatch;
3146
- }
3147
3201
  return JSON.stringify(output, null, 2);
3148
3202
  }
3149
3203
  const lines = [];
@@ -3158,17 +3212,6 @@ function formatSimulationResult(result, options = {}) {
3158
3212
  lines.push(` - ${evt.name || "unknown"}`);
3159
3213
  }
3160
3214
  }
3161
- if (result.exampleMatch) {
3162
- const em = result.exampleMatch;
3163
- if (em.match) {
3164
- lines.push(`Example "${em.name}" (${em.step}): PASS`);
3165
- } else {
3166
- lines.push(`Example "${em.name}" (${em.step}): FAIL`);
3167
- if (em.diff) {
3168
- lines.push(em.diff);
3169
- }
3170
- }
3171
- }
3172
3215
  return lines.join("\n");
3173
3216
  }
3174
3217
  async function executeSimulation(event, inputPath, platformOverride, options = {}) {
@@ -3299,7 +3342,6 @@ async function executeConfigSimulation(_content, configPath, typedEvent, tempDir
3299
3342
  }
3300
3343
 
3301
3344
  // src/commands/simulate/source-simulator.ts
3302
- import { JSDOM, VirtualConsole } from "jsdom";
3303
3345
  init_core();
3304
3346
  async function loadSourcePackage(packageName) {
3305
3347
  const mainModule = await import(packageName);
@@ -3307,18 +3349,18 @@ async function loadSourcePackage(packageName) {
3307
3349
  if (!code || typeof code !== "function") {
3308
3350
  throw new Error(`Package ${packageName} missing source init function`);
3309
3351
  }
3310
- let setup;
3352
+ let createTrigger;
3311
3353
  try {
3312
3354
  const devModule = await import(`${packageName}/dev`);
3313
3355
  const examples = devModule.examples || devModule.default?.examples;
3314
- if (examples?.setup && typeof examples.setup === "function") {
3315
- setup = examples.setup;
3356
+ if (examples?.createTrigger && typeof examples.createTrigger === "function") {
3357
+ createTrigger = examples.createTrigger;
3316
3358
  }
3317
3359
  } catch {
3318
3360
  }
3319
- return { code, setup };
3361
+ return { code, createTrigger };
3320
3362
  }
3321
- async function simulateSourceCLI(flowConfig, setupInput, options) {
3363
+ async function simulateSourceCLI(flowConfig, sourceInput, options) {
3322
3364
  const startTime = Date.now();
3323
3365
  try {
3324
3366
  const sources = flowConfig.sources;
@@ -3335,32 +3377,22 @@ async function simulateSourceCLI(flowConfig, setupInput, options) {
3335
3377
  if (!sourceConfig.package) {
3336
3378
  throw new Error(`Source "${options.sourceStep}" has no package field`);
3337
3379
  }
3338
- const { code, setup } = await loadSourcePackage(sourceConfig.package);
3339
- const virtualConsole = new VirtualConsole();
3340
- const dom = new JSDOM(
3341
- "<!DOCTYPE html><html><head></head><body></body></html>",
3342
- {
3343
- url: "http://localhost",
3344
- runScripts: "dangerously",
3345
- pretendToBeVisual: true,
3346
- virtualConsole
3347
- }
3380
+ const { code, createTrigger } = await loadSourcePackage(
3381
+ sourceConfig.package
3348
3382
  );
3349
- const env = {
3350
- window: dom.window,
3351
- document: dom.window.document,
3352
- localStorage: dom.window.localStorage
3353
- };
3383
+ if (!createTrigger) {
3384
+ throw new Error(
3385
+ `Source package ${sourceConfig.package} has no createTrigger export. Ensure the package exports createTrigger from its /dev subpath.`
3386
+ );
3387
+ }
3354
3388
  const result = await dn({
3355
3389
  step: "source",
3356
3390
  name: options.sourceStep,
3357
3391
  code,
3358
3392
  config: sourceConfig.config || {},
3359
- setup,
3360
- input: setupInput,
3361
- env
3393
+ createTrigger,
3394
+ input: sourceInput || { content: void 0 }
3362
3395
  });
3363
- dom.window.close();
3364
3396
  return {
3365
3397
  success: !result.error,
3366
3398
  error: result.error?.message,
@@ -3507,7 +3539,6 @@ async function simulateCommand(options) {
3507
3539
  verbose: options.verbose,
3508
3540
  silent: options.silent,
3509
3541
  platform: options.platform,
3510
- example: options.example,
3511
3542
  step: options.step
3512
3543
  });
3513
3544
  const resultWithDuration = {
@@ -3518,8 +3549,7 @@ async function simulateCommand(options) {
3518
3549
  json: options.json
3519
3550
  });
3520
3551
  await writeResult(formatted + "\n", { output: options.output });
3521
- const exitCode = !result.success || result.exampleMatch && !result.exampleMatch.match ? 1 : 0;
3522
- process.exit(exitCode);
3552
+ process.exit(result.success ? 0 : 1);
3523
3553
  } catch (error) {
3524
3554
  const errorMessage = getErrorMessage(error);
3525
3555
  if (options.json) {
@@ -3542,49 +3572,14 @@ async function simulateCommand(options) {
3542
3572
  async function simulate(configOrPath, event, options = {}) {
3543
3573
  if (typeof configOrPath !== "string") {
3544
3574
  throw new Error(
3545
- "simulate() currently only supports config file paths. Config object support will be added in a future version. Please provide a path to a configuration file."
3575
+ "simulate() currently only supports config file paths. Please provide a path to a configuration file."
3546
3576
  );
3547
3577
  }
3548
3578
  let resolvedEvent = event;
3549
3579
  if (typeof event === "string") {
3550
3580
  resolvedEvent = await loadJsonFromSource(event, { name: "event" });
3551
3581
  }
3552
- let exampleContext;
3553
- if (options.example) {
3554
- const rawConfig = await loadJsonConfig(configOrPath);
3555
- const setup = validateFlowConfig(rawConfig);
3556
- const flowNames = Object.keys(setup.flows);
3557
- let flowName = options.flow;
3558
- if (!flowName) {
3559
- if (flowNames.length === 1) {
3560
- flowName = flowNames[0];
3561
- } else {
3562
- throw new Error(
3563
- `Multiple flows found. Use --flow to specify which flow contains the example.
3564
- Available flows: ${flowNames.join(", ")}`
3565
- );
3566
- }
3567
- }
3568
- const flowSettings = setup.flows[flowName];
3569
- if (!flowSettings) {
3570
- throw new Error(
3571
- `Flow "${flowName}" not found. Available: ${flowNames.join(", ")}`
3572
- );
3573
- }
3574
- const found = findExample(flowSettings, options.example, options.step);
3575
- if (found.example.in === void 0) {
3576
- throw new Error(
3577
- `Example "${options.example}" in ${found.stepType}.${found.stepName} has no "in" value`
3578
- );
3579
- }
3580
- resolvedEvent = found.example.in;
3581
- exampleContext = {
3582
- stepType: found.stepType,
3583
- stepName: found.stepName,
3584
- expected: found.example.out
3585
- };
3586
- }
3587
- const isSourceSimulation = exampleContext?.stepType === "source" || options.step?.startsWith("source.");
3582
+ const isSourceSimulation = options.step?.startsWith("source.");
3588
3583
  let result;
3589
3584
  if (isSourceSimulation) {
3590
3585
  const rawConfig = await loadJsonConfig(configOrPath);
@@ -3603,7 +3598,7 @@ Available: ${flowNames.join(", ")}`
3603
3598
  `Flow "${flowName}" not found. Available: ${flowNames.join(", ")}`
3604
3599
  );
3605
3600
  }
3606
- const sourceStep = exampleContext?.stepName || options.step.substring("source.".length);
3601
+ const sourceStep = options.step.substring("source.".length);
3607
3602
  result = await simulateSourceCLI(
3608
3603
  flowSettings,
3609
3604
  resolvedEvent,
@@ -3616,38 +3611,15 @@ Available: ${flowNames.join(", ")}`
3616
3611
  }
3617
3612
  );
3618
3613
  } else {
3619
- const stepTarget = exampleContext ? `${exampleContext.stepType}.${exampleContext.stepName}` : options.step;
3620
3614
  result = await simulateCore(configOrPath, resolvedEvent, {
3621
3615
  json: options.json ?? false,
3622
3616
  verbose: options.verbose ?? false,
3623
3617
  silent: options.silent ?? false,
3624
3618
  flow: options.flow,
3625
3619
  platform: options.platform,
3626
- step: stepTarget
3620
+ step: options.step
3627
3621
  });
3628
3622
  }
3629
- if (exampleContext && result.success) {
3630
- const stepKey = `${exampleContext.stepType}.${exampleContext.stepName}`;
3631
- if (exampleContext.expected === false) {
3632
- const calls = result.usage?.[exampleContext.stepName];
3633
- const wasFiltered = !calls || calls.length === 0;
3634
- result.exampleMatch = {
3635
- name: options.example,
3636
- step: stepKey,
3637
- expected: false,
3638
- actual: wasFiltered ? false : calls,
3639
- match: wasFiltered,
3640
- diff: wasFiltered ? void 0 : `Expected event to be filtered, but ${calls.length} API call(s) were made`
3641
- };
3642
- } else if (exampleContext.expected !== void 0) {
3643
- const actual = result.usage?.[exampleContext.stepName] ?? [];
3644
- result.exampleMatch = {
3645
- name: options.example,
3646
- step: stepKey,
3647
- ...compareOutput(exampleContext.expected, actual)
3648
- };
3649
- }
3650
- }
3651
3623
  return result;
3652
3624
  }
3653
3625
 
@@ -3658,7 +3630,7 @@ init_tmp();
3658
3630
  init_config();
3659
3631
  init_bundler();
3660
3632
  import path12 from "path";
3661
- import { JSDOM as JSDOM2, VirtualConsole as VirtualConsole2 } from "jsdom";
3633
+ import { JSDOM, VirtualConsole } from "jsdom";
3662
3634
  import fs13 from "fs-extra";
3663
3635
  import { getPlatform as getPlatform3 } from "@walkeros/core";
3664
3636
  import { schemas as schemas2 } from "@walkeros/core/dev";
@@ -3889,8 +3861,8 @@ async function executeBundlePush(bundleContent, platform, validatedEvent, logger
3889
3861
  async function executeWebPush(bundlePath, event, logger) {
3890
3862
  const startTime = Date.now();
3891
3863
  try {
3892
- const virtualConsole = new VirtualConsole2();
3893
- const dom = new JSDOM2("<!DOCTYPE html><html><body></body></html>", {
3864
+ const virtualConsole = new VirtualConsole();
3865
+ const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
3894
3866
  url: "http://localhost",
3895
3867
  runScripts: "dangerously",
3896
3868
  resources: "usable",
@@ -3998,19 +3970,29 @@ init_core();
3998
3970
  init_config_file();
3999
3971
  init_auth();
4000
3972
  import path14 from "path";
4001
- import { createRequire } from "module";
4002
3973
  import { writeFileSync as writeFileSync5 } from "fs";
4003
3974
  import { homedir as homedir2 } from "os";
4004
3975
  import { join as join4 } from "path";
4005
3976
 
4006
3977
  // src/runtime/resolve-bundle.ts
4007
3978
  init_stdin();
4008
- import { existsSync as existsSync3, writeFileSync as writeFileSync2 } from "fs";
4009
- var TEMP_BUNDLE_PATH = "/tmp/walkeros-bundle.mjs";
3979
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
3980
+ import { dirname } from "path";
3981
+ function getDefaultWritePath() {
3982
+ if (existsSync3("/app/flow")) return "/app/flow/bundle.mjs";
3983
+ return "/tmp/walkeros-bundle.mjs";
3984
+ }
4010
3985
  function isUrl2(value) {
4011
3986
  return value.startsWith("http://") || value.startsWith("https://");
4012
3987
  }
4013
- async function fetchBundle(url) {
3988
+ function writeBundleToDisk(writePath, content) {
3989
+ const dir = dirname(writePath);
3990
+ if (!existsSync3(dir)) {
3991
+ mkdirSync2(dir, { recursive: true });
3992
+ }
3993
+ writeFileSync2(writePath, content, "utf-8");
3994
+ }
3995
+ async function fetchBundle(url, writePath) {
4014
3996
  const response = await fetch(url, { signal: AbortSignal.timeout(3e4) });
4015
3997
  if (!response.ok) {
4016
3998
  throw new Error(
@@ -4021,38 +4003,38 @@ async function fetchBundle(url) {
4021
4003
  if (!content.trim()) {
4022
4004
  throw new Error(`Bundle fetched from ${url} is empty`);
4023
4005
  }
4024
- writeFileSync2(TEMP_BUNDLE_PATH, content, "utf-8");
4025
- return TEMP_BUNDLE_PATH;
4006
+ writeBundleToDisk(writePath, content);
4007
+ return writePath;
4026
4008
  }
4027
- async function readBundleFromStdin() {
4009
+ async function readBundleFromStdin(writePath) {
4028
4010
  const content = await readStdin();
4029
- writeFileSync2(TEMP_BUNDLE_PATH, content, "utf-8");
4030
- return TEMP_BUNDLE_PATH;
4011
+ writeBundleToDisk(writePath, content);
4012
+ return writePath;
4031
4013
  }
4032
4014
  async function resolveBundle(bundleEnv) {
4015
+ const writePath = getDefaultWritePath();
4033
4016
  if (!isUrl2(bundleEnv) && existsSync3(bundleEnv)) {
4034
4017
  return { path: bundleEnv, source: "file" };
4035
4018
  }
4036
- if (isStdinPiped()) {
4037
- const path15 = await readBundleFromStdin();
4038
- return { path: path15, source: "stdin" };
4039
- }
4040
4019
  if (isUrl2(bundleEnv)) {
4041
- const path15 = await fetchBundle(bundleEnv);
4020
+ const path15 = await fetchBundle(bundleEnv, writePath);
4042
4021
  return { path: path15, source: "url" };
4043
4022
  }
4023
+ if (isStdinPiped()) {
4024
+ const path15 = await readBundleFromStdin(writePath);
4025
+ return { path: path15, source: "stdin" };
4026
+ }
4044
4027
  return { path: bundleEnv, source: "file" };
4045
4028
  }
4046
4029
 
4047
4030
  // src/runtime/config-fetcher.ts
4031
+ init_http();
4048
4032
  async function fetchConfig(options) {
4049
4033
  const url = `${options.appUrl}/api/projects/${options.projectId}/flows/${options.flowId}`;
4050
- const headers = {
4051
- Authorization: `Bearer ${options.token}`
4052
- };
4053
- if (options.lastEtag) {
4054
- headers["If-None-Match"] = options.lastEtag;
4055
- }
4034
+ const headers = mergeAuthHeaders(
4035
+ options.token,
4036
+ options.lastEtag ? { "If-None-Match": options.lastEtag } : void 0
4037
+ );
4056
4038
  const response = await fetch(url, {
4057
4039
  headers,
4058
4040
  signal: AbortSignal.timeout(3e4)
@@ -4131,7 +4113,7 @@ var BundleOptionsSchema = z5.object({
4131
4113
  });
4132
4114
  var BundleInputShape = {
4133
4115
  configPath: FilePathSchema.describe(
4134
- "Path to flow configuration file (JSON or JavaScript)"
4116
+ "Path to flow configuration file (JSON or JavaScript), URL, or inline JSON string"
4135
4117
  ),
4136
4118
  flow: z5.string().optional().describe("Flow name for multi-flow configs"),
4137
4119
  stats: z5.boolean().optional().default(true).describe("Return bundle statistics"),
@@ -4148,17 +4130,16 @@ var SimulateOptionsSchema = z6.object({
4148
4130
  json: z6.boolean().optional().describe("Format output as JSON")
4149
4131
  });
4150
4132
  var SimulateInputShape = {
4151
- configPath: FilePathSchema.describe("Path to flow configuration file"),
4133
+ configPath: FilePathSchema.describe(
4134
+ "Path to flow configuration file, URL, or inline JSON string"
4135
+ ),
4152
4136
  event: z6.string().min(1).optional().describe(
4153
- "Event as JSON string, file path, or URL. Optional when example is provided."
4137
+ "Event as JSON string, file path, or URL. For sources: { content, trigger?, env? }."
4154
4138
  ),
4155
4139
  flow: z6.string().optional().describe("Flow name for multi-flow configs"),
4156
4140
  platform: PlatformSchema.optional().describe("Override platform detection"),
4157
- example: z6.string().optional().describe(
4158
- 'Name of a step example to use as event input (uses its "in" value)'
4159
- ),
4160
4141
  step: z6.string().optional().describe(
4161
- 'Step target in type.name format (e.g. "destination.gtag") to narrow example lookup'
4142
+ 'Step target in type.name format (e.g. "source.browser", "destination.gtag")'
4162
4143
  )
4163
4144
  };
4164
4145
  var SimulateInputSchema = z6.object(SimulateInputShape);
@@ -4253,10 +4234,10 @@ function createHealthServer(port, logger) {
4253
4234
 
4254
4235
  // src/runtime/runner.ts
4255
4236
  import { pathToFileURL } from "url";
4256
- import { resolve, dirname } from "path";
4237
+ import { resolve, dirname as dirname2 } from "path";
4257
4238
  async function loadFlow(file, config, logger, loggerConfig, healthServer) {
4258
4239
  const absolutePath = resolve(file);
4259
- const flowDir = dirname(absolutePath);
4240
+ const flowDir = dirname2(absolutePath);
4260
4241
  process.chdir(flowDir);
4261
4242
  const fileUrl = pathToFileURL(absolutePath).href;
4262
4243
  const module = await import(`${fileUrl}?t=${Date.now()}`);
@@ -4315,9 +4296,9 @@ import { randomBytes } from "crypto";
4315
4296
  // src/version.ts
4316
4297
  import { readFileSync as readFileSync3 } from "fs";
4317
4298
  import { fileURLToPath as fileURLToPath2 } from "url";
4318
- import { dirname as dirname2, join as join3 } from "path";
4299
+ import { dirname as dirname3, join as join3 } from "path";
4319
4300
  var versionFilename = fileURLToPath2(import.meta.url);
4320
- var versionDirname = dirname2(versionFilename);
4301
+ var versionDirname = dirname3(versionFilename);
4321
4302
  function findPackageJson() {
4322
4303
  const paths = [
4323
4304
  join3(versionDirname, "../package.json"),
@@ -4336,6 +4317,7 @@ function findPackageJson() {
4336
4317
  var VERSION = JSON.parse(findPackageJson()).version;
4337
4318
 
4338
4319
  // src/runtime/heartbeat.ts
4320
+ init_http();
4339
4321
  function computeCounterDelta(current, last) {
4340
4322
  const destinations = {};
4341
4323
  for (const [name, dest] of Object.entries(current.destinations)) {
@@ -4357,6 +4339,17 @@ function computeCounterDelta(current, last) {
4357
4339
  destinations
4358
4340
  };
4359
4341
  }
4342
+ function snapshotDestinations(destinations) {
4343
+ const result = {};
4344
+ for (const [name, dest] of Object.entries(destinations)) {
4345
+ result[name] = {
4346
+ count: dest.count,
4347
+ failed: dest.failed,
4348
+ duration: dest.duration
4349
+ };
4350
+ }
4351
+ return result;
4352
+ }
4360
4353
  var instanceId = randomBytes(8).toString("hex");
4361
4354
  function getInstanceId() {
4362
4355
  return instanceId;
@@ -4374,13 +4367,14 @@ function createHeartbeat(config, logger) {
4374
4367
  async function sendOnce() {
4375
4368
  try {
4376
4369
  let counters;
4370
+ let current;
4377
4371
  const status = config.getCounters?.();
4378
4372
  if (status) {
4379
- const current = {
4373
+ current = {
4380
4374
  in: status.in,
4381
4375
  out: status.out,
4382
4376
  failed: status.failed,
4383
- destinations: { ...status.destinations }
4377
+ destinations: snapshotDestinations(status.destinations)
4384
4378
  };
4385
4379
  counters = computeCounterDelta(current, lastReported);
4386
4380
  }
@@ -4388,10 +4382,9 @@ function createHeartbeat(config, logger) {
4388
4382
  `${config.appUrl}/api/projects/${config.projectId}/runners/heartbeat`,
4389
4383
  {
4390
4384
  method: "POST",
4391
- headers: {
4392
- Authorization: `Bearer ${config.token}`,
4385
+ headers: mergeAuthHeaders(config.token, {
4393
4386
  "Content-Type": "application/json"
4394
- },
4387
+ }),
4395
4388
  body: JSON.stringify({
4396
4389
  instanceId,
4397
4390
  flowId: config.flowId,
@@ -4406,13 +4399,8 @@ function createHeartbeat(config, logger) {
4406
4399
  signal: AbortSignal.timeout(1e4)
4407
4400
  }
4408
4401
  );
4409
- if (response.ok && status) {
4410
- lastReported = {
4411
- in: status.in,
4412
- out: status.out,
4413
- failed: status.failed,
4414
- destinations: { ...status.destinations }
4415
- };
4402
+ if (response.ok && counters && current) {
4403
+ lastReported = current;
4416
4404
  }
4417
4405
  if (response.status === 401 || response.status === 403) {
4418
4406
  logger.error(
@@ -4482,6 +4470,7 @@ function createPoller(config, logger) {
4482
4470
  }
4483
4471
 
4484
4472
  // src/runtime/secrets-fetcher.ts
4473
+ init_http();
4485
4474
  var SecretsHttpError = class extends Error {
4486
4475
  constructor(status, statusText) {
4487
4476
  super(`Failed to fetch secrets: ${status} ${statusText}`);
@@ -4493,10 +4482,7 @@ async function fetchSecrets(options) {
4493
4482
  const { appUrl, token, projectId, flowId } = options;
4494
4483
  const url = `${appUrl}/api/projects/${encodeURIComponent(projectId)}/flows/${encodeURIComponent(flowId)}/secrets/values`;
4495
4484
  const res = await fetch(url, {
4496
- headers: {
4497
- Authorization: `Bearer ${token}`,
4498
- "Content-Type": "application/json"
4499
- }
4485
+ headers: mergeAuthHeaders(token, { "Content-Type": "application/json" })
4500
4486
  });
4501
4487
  if (!res.ok) {
4502
4488
  throw new SecretsHttpError(res.status, res.statusText);
@@ -4541,6 +4527,7 @@ async function runPipeline(options) {
4541
4527
  token: api.token,
4542
4528
  projectId: api.projectId,
4543
4529
  flowId: api.flowId,
4530
+ deploymentId: api.deploymentId,
4544
4531
  configVersion,
4545
4532
  intervalMs: api.heartbeatIntervalMs,
4546
4533
  getCounters: () => handle.collector.status
@@ -4653,7 +4640,6 @@ async function injectSecrets(api, logger) {
4653
4640
  }
4654
4641
 
4655
4642
  // src/commands/run/index.ts
4656
- var esmRequire = createRequire(import.meta.url);
4657
4643
  function defaultCacheDir() {
4658
4644
  const xdgCache = process.env.XDG_CACHE_HOME;
4659
4645
  const base = xdgCache || join4(homedir2(), ".cache");
@@ -4672,19 +4658,6 @@ async function runCommand(options) {
4672
4658
  if (options.port !== void 0) {
4673
4659
  validatePort(options.port);
4674
4660
  }
4675
- const runtimeDeps = ["express", "cors"];
4676
- for (const dep of runtimeDeps) {
4677
- try {
4678
- esmRequire.resolve(dep);
4679
- } catch {
4680
- logger.error(
4681
- `Missing runtime dependency "${dep}"
4682
- Server flows require express and cors when running outside Docker.
4683
- Run: npm install express cors`
4684
- );
4685
- process.exit(1);
4686
- }
4687
- }
4688
4661
  const flowId = options.flowId;
4689
4662
  const projectId = options.project;
4690
4663
  const token = resolveRunToken();
@@ -4719,6 +4692,7 @@ Run: npm install express cors`
4719
4692
  token,
4720
4693
  projectId,
4721
4694
  flowId,
4695
+ deploymentId: options.deploymentId,
4722
4696
  heartbeatIntervalMs: parseInt(
4723
4697
  process.env.WALKEROS_HEARTBEAT_INTERVAL ?? process.env.HEARTBEAT_INTERVAL ?? "60",
4724
4698
  10
@@ -5868,6 +5842,30 @@ async function deleteProjectCommand(projectId, options) {
5868
5842
 
5869
5843
  // src/commands/flows/index.ts
5870
5844
  init_api_client();
5845
+
5846
+ // src/core/api-error.ts
5847
+ var ApiError = class extends Error {
5848
+ code;
5849
+ details;
5850
+ constructor(message, options) {
5851
+ super(message);
5852
+ this.name = "ApiError";
5853
+ this.code = options?.code;
5854
+ this.details = options?.details;
5855
+ }
5856
+ };
5857
+ function throwApiError(error, fallbackMessage) {
5858
+ if (error && typeof error === "object" && "error" in error && typeof error.error === "object") {
5859
+ const inner = error.error;
5860
+ const message = inner.message || fallbackMessage;
5861
+ const code = inner.code;
5862
+ const details = inner.details?.errors;
5863
+ throw new ApiError(message, { code, details });
5864
+ }
5865
+ throw new ApiError(fallbackMessage);
5866
+ }
5867
+
5868
+ // src/commands/flows/index.ts
5871
5869
  init_auth();
5872
5870
  init_cli_logger();
5873
5871
  init_output();
@@ -5885,7 +5883,7 @@ async function listFlows(options = {}) {
5885
5883
  }
5886
5884
  }
5887
5885
  });
5888
- if (error) throw new Error(error.error?.message || "Failed to list flows");
5886
+ if (error) throwApiError(error, "Failed to list flows");
5889
5887
  return data;
5890
5888
  }
5891
5889
  async function getFlow(options) {
@@ -5900,7 +5898,7 @@ async function getFlow(options) {
5900
5898
  }
5901
5899
  }
5902
5900
  );
5903
- if (error) throw new Error(error.error?.message || "Failed to get flow");
5901
+ if (error) throwApiError(error, "Failed to get flow");
5904
5902
  return data;
5905
5903
  }
5906
5904
  async function createFlow(options) {
@@ -5911,7 +5909,7 @@ async function createFlow(options) {
5911
5909
  // Config is user-provided JSON; server validates the full schema
5912
5910
  body: { name: options.name, config: options.content }
5913
5911
  });
5914
- if (error) throw new Error(error.error?.message || "Failed to create flow");
5912
+ if (error) throwApiError(error, "Failed to create flow");
5915
5913
  return data;
5916
5914
  }
5917
5915
  async function updateFlow(options) {
@@ -5931,7 +5929,7 @@ async function updateFlow(options) {
5931
5929
  }
5932
5930
  }
5933
5931
  );
5934
- if (error) throw new Error(error.error?.message || "Failed to update flow");
5932
+ if (error) throwApiError(error, "Failed to update flow");
5935
5933
  return data;
5936
5934
  }
5937
5935
  async function deleteFlow(options) {
@@ -5943,7 +5941,7 @@ async function deleteFlow(options) {
5943
5941
  params: { path: { projectId: id, flowId: options.flowId } }
5944
5942
  }
5945
5943
  );
5946
- if (error) throw new Error(error.error?.message || "Failed to delete flow");
5944
+ if (error) throwApiError(error, "Failed to delete flow");
5947
5945
  return data ?? { success: true };
5948
5946
  }
5949
5947
  async function duplicateFlow(options) {
@@ -5956,8 +5954,7 @@ async function duplicateFlow(options) {
5956
5954
  body: { name: options.name }
5957
5955
  }
5958
5956
  );
5959
- if (error)
5960
- throw new Error(error.error?.message || "Failed to duplicate flow");
5957
+ if (error) throwApiError(error, "Failed to duplicate flow");
5961
5958
  return data;
5962
5959
  }
5963
5960
  async function handleResult2(fn2, options) {
@@ -6028,6 +6025,7 @@ async function readFlowStdin() {
6028
6025
  // src/commands/deploy/index.ts
6029
6026
  init_api_client();
6030
6027
  init_auth();
6028
+ init_http();
6031
6029
  init_sse();
6032
6030
  init_cli_logger();
6033
6031
  init_output();
@@ -6057,10 +6055,9 @@ async function getAvailableFlowNames(options) {
6057
6055
  return settings?.map((c2) => c2.name) ?? [];
6058
6056
  }
6059
6057
  async function streamDeploymentStatus(projectId, deploymentId, options) {
6060
- const base = resolveBaseUrl();
6061
6058
  const timeoutMs = options.timeout ?? 12e4;
6062
- const response = await authenticatedFetch(
6063
- `${base}/api/projects/${projectId}/deployments/${deploymentId}/stream`,
6059
+ const response = await apiFetch(
6060
+ `/api/projects/${projectId}/deployments/${deploymentId}/stream`,
6064
6061
  {
6065
6062
  headers: { Accept: "text/event-stream" },
6066
6063
  signal: options.signal ?? AbortSignal.timeout(timeoutMs)
@@ -6120,19 +6117,22 @@ async function deploy(options) {
6120
6117
  { params: { path: { projectId, flowId: options.flowId } } }
6121
6118
  );
6122
6119
  if (error) {
6123
- const msg = error.error?.message || "Failed to start deployment";
6124
- const code = error.error?.code;
6125
- if (code === "AMBIGUOUS_CONFIG") {
6126
- const names = await getAvailableFlowNames({
6127
- flowId: options.flowId,
6128
- projectId
6129
- });
6130
- throw new Error(
6131
- `This flow has multiple settings. Use --flow <name> to specify one.
6132
- Available: ${names.join(", ")}`
6133
- );
6120
+ try {
6121
+ throwApiError(error, "Failed to start deployment");
6122
+ } catch (e2) {
6123
+ if (e2 instanceof ApiError && e2.code === "AMBIGUOUS_CONFIG") {
6124
+ const names = await getAvailableFlowNames({
6125
+ flowId: options.flowId,
6126
+ projectId
6127
+ });
6128
+ throw new ApiError(
6129
+ `This flow has multiple settings. Use --flow <name> to specify one.
6130
+ Available: ${names.join(", ")}`,
6131
+ { code: "AMBIGUOUS_CONFIG" }
6132
+ );
6133
+ }
6134
+ throw e2;
6134
6135
  }
6135
- throw new Error(msg);
6136
6136
  }
6137
6137
  if (!options.wait) return data;
6138
6138
  const result = await streamDeploymentStatus(projectId, data.deploymentId, {
@@ -6144,16 +6144,13 @@ Available: ${names.join(", ")}`
6144
6144
  }
6145
6145
  async function deploySettings(options) {
6146
6146
  const { flowId, projectId, settingsId } = options;
6147
- const base = resolveBaseUrl();
6148
- const response = await authenticatedFetch(
6149
- `${base}/api/projects/${projectId}/flows/${flowId}/settings/${settingsId}/deploy`,
6147
+ const response = await apiFetch(
6148
+ `/api/projects/${projectId}/flows/${flowId}/settings/${settingsId}/deploy`,
6150
6149
  { method: "POST" }
6151
6150
  );
6152
6151
  if (!response.ok) {
6153
6152
  const body = await response.json().catch(() => ({}));
6154
- throw new Error(
6155
- body.error?.message || `Deploy failed (${response.status})`
6156
- );
6153
+ throwApiError(body, `Deploy failed (${response.status})`);
6157
6154
  }
6158
6155
  const data = await response.json();
6159
6156
  if (!options.wait) return data;
@@ -6172,13 +6169,12 @@ async function getDeployment(options) {
6172
6169
  projectId,
6173
6170
  flowName: options.flowName
6174
6171
  });
6175
- const base = resolveBaseUrl();
6176
- const response = await authenticatedFetch(
6177
- `${base}/api/projects/${projectId}/flows/${options.flowId}/settings/${settingsId}/deploy`
6172
+ const response = await apiFetch(
6173
+ `/api/projects/${projectId}/flows/${options.flowId}/settings/${settingsId}/deploy`
6178
6174
  );
6179
6175
  if (!response.ok) {
6180
6176
  const body = await response.json().catch(() => ({}));
6181
- throw new Error(body.error?.message || "Failed to get deployment");
6177
+ throwApiError(body, "Failed to get deployment");
6182
6178
  }
6183
6179
  return response.json();
6184
6180
  }
@@ -6187,8 +6183,7 @@ async function getDeployment(options) {
6187
6183
  "/api/projects/{projectId}/flows/{flowId}/deploy",
6188
6184
  { params: { path: { projectId, flowId: options.flowId } } }
6189
6185
  );
6190
- if (error)
6191
- throw new Error(error.error?.message || "Failed to get deployment");
6186
+ if (error) throwApiError(error, "Failed to get deployment");
6192
6187
  return data;
6193
6188
  }
6194
6189
  var statusLabels = {
@@ -6272,73 +6267,59 @@ async function getDeploymentCommand(flowId, options) {
6272
6267
 
6273
6268
  // src/commands/deployments/index.ts
6274
6269
  init_auth();
6270
+ init_http();
6271
+ import { getPlatform as getPlatform4 } from "@walkeros/core";
6275
6272
  init_cli_logger();
6276
6273
  init_output();
6277
6274
  init_loader();
6278
- import { getPlatform as getPlatform4 } from "@walkeros/core";
6279
6275
  async function listDeployments(options = {}) {
6280
6276
  const id = options.projectId ?? requireProjectId();
6281
- const base = resolveBaseUrl();
6282
6277
  const params = new URLSearchParams();
6283
6278
  if (options.type) params.set("type", options.type);
6284
6279
  if (options.status) params.set("status", options.status);
6285
6280
  const qs = params.toString();
6286
- const response = await authenticatedFetch(
6287
- `${base}/api/projects/${id}/deployments${qs ? `?${qs}` : ""}`
6281
+ const response = await apiFetch(
6282
+ `/api/projects/${id}/deployments${qs ? `?${qs}` : ""}`
6288
6283
  );
6289
6284
  if (!response.ok) {
6290
6285
  const body = await response.json().catch(() => ({}));
6291
- throw new Error(
6292
- body.error?.message || "Failed to list deployments"
6293
- );
6286
+ throwApiError(body, "Failed to list deployments");
6294
6287
  }
6295
6288
  return response.json();
6296
6289
  }
6297
6290
  async function getDeploymentBySlug(options) {
6298
6291
  const id = options.projectId ?? requireProjectId();
6299
- const base = resolveBaseUrl();
6300
- const response = await authenticatedFetch(
6301
- `${base}/api/projects/${id}/deployments/${options.slug}`
6292
+ const response = await apiFetch(
6293
+ `/api/projects/${id}/deployments/${options.slug}`
6302
6294
  );
6303
6295
  if (!response.ok) {
6304
6296
  const body = await response.json().catch(() => ({}));
6305
- throw new Error(
6306
- body.error?.message || "Failed to get deployment"
6307
- );
6297
+ throwApiError(body, "Failed to get deployment");
6308
6298
  }
6309
6299
  return response.json();
6310
6300
  }
6311
6301
  async function createDeployment(options) {
6312
6302
  const id = options.projectId ?? requireProjectId();
6313
- const base = resolveBaseUrl();
6314
- const response = await authenticatedFetch(
6315
- `${base}/api/projects/${id}/deployments`,
6316
- {
6317
- method: "POST",
6318
- headers: { "Content-Type": "application/json" },
6319
- body: JSON.stringify({ type: options.type, label: options.label })
6320
- }
6321
- );
6303
+ const response = await apiFetch(`/api/projects/${id}/deployments`, {
6304
+ method: "POST",
6305
+ headers: { "Content-Type": "application/json" },
6306
+ body: JSON.stringify({ type: options.type, label: options.label })
6307
+ });
6322
6308
  if (!response.ok) {
6323
6309
  const body = await response.json().catch(() => ({}));
6324
- throw new Error(
6325
- body.error?.message || "Failed to create deployment"
6326
- );
6310
+ throwApiError(body, "Failed to create deployment");
6327
6311
  }
6328
6312
  return response.json();
6329
6313
  }
6330
6314
  async function deleteDeployment(options) {
6331
6315
  const id = options.projectId ?? requireProjectId();
6332
- const base = resolveBaseUrl();
6333
- const response = await authenticatedFetch(
6334
- `${base}/api/projects/${id}/deployments/${options.slug}`,
6316
+ const response = await apiFetch(
6317
+ `/api/projects/${id}/deployments/${options.slug}`,
6335
6318
  { method: "DELETE" }
6336
6319
  );
6337
6320
  if (!response.ok) {
6338
6321
  const body = await response.json().catch(() => ({}));
6339
- throw new Error(
6340
- body.error?.message || "Failed to delete deployment"
6341
- );
6322
+ throwApiError(body, "Failed to delete deployment");
6342
6323
  }
6343
6324
  const data = await response.json().catch(() => null);
6344
6325
  return data ?? { success: true };
@@ -6398,15 +6379,10 @@ async function createDeployCommand(config, options) {
6398
6379
  const isRemoteFlow = config.startsWith("cfg_");
6399
6380
  if (isRemoteFlow) {
6400
6381
  const id = options.project ?? requireProjectId();
6401
- const base = resolveBaseUrl();
6402
- const resp = await authenticatedFetch(
6403
- `${base}/api/projects/${id}/flows/${config}`
6404
- );
6382
+ const resp = await apiFetch(`/api/projects/${id}/flows/${config}`);
6405
6383
  if (!resp.ok) {
6406
6384
  const body = await resp.json().catch(() => ({}));
6407
- throw new Error(
6408
- body.error?.message || `Failed to fetch flow ${config}`
6409
- );
6385
+ throwApiError(body, `Failed to fetch flow ${config}`);
6410
6386
  }
6411
6387
  const flow = await resp.json();
6412
6388
  if (!flow.config) throw new Error("Flow has no config");
@@ -6466,12 +6442,16 @@ async function createDeployCommand(config, options) {
6466
6442
 
6467
6443
  // src/commands/feedback/index.ts
6468
6444
  init_config_file();
6445
+ init_http();
6469
6446
  init_cli_logger();
6470
6447
  import { createInterface } from "readline";
6471
6448
  async function feedback(text, options) {
6472
6449
  const config = readConfig();
6473
6450
  const anonymous = options?.anonymous ?? config?.anonymousFeedback ?? true;
6474
6451
  const payload = { text };
6452
+ if (options?.version) {
6453
+ payload.version = options.version;
6454
+ }
6475
6455
  if (!anonymous && config?.email) {
6476
6456
  payload.userId = config.email;
6477
6457
  const projectId = process.env.WALKEROS_PROJECT_ID;
@@ -6479,8 +6459,7 @@ async function feedback(text, options) {
6479
6459
  payload.projectId = projectId;
6480
6460
  }
6481
6461
  }
6482
- const appUrl = resolveAppUrl();
6483
- const response = await fetch(`${appUrl}/api/feedback`, {
6462
+ const response = await publicFetch("/api/feedback", {
6484
6463
  method: "POST",
6485
6464
  headers: { "Content-Type": "application/json" },
6486
6465
  body: JSON.stringify(payload)
@@ -6538,11 +6517,14 @@ function promptUser(question) {
6538
6517
  // src/index.ts
6539
6518
  init_bundle();
6540
6519
  init_auth();
6520
+ init_http();
6541
6521
  init_api_client();
6542
6522
  init_config_file();
6543
6523
  init_sse();
6544
6524
  init_utils();
6545
6525
  export {
6526
+ ApiError,
6527
+ apiFetch,
6546
6528
  bundle,
6547
6529
  bundleCommand,
6548
6530
  bundleRemote,
@@ -6562,8 +6544,8 @@ export {
6562
6544
  deleteProject,
6563
6545
  deleteProjectCommand,
6564
6546
  deploy,
6565
- deployAuthenticatedFetch,
6566
6547
  deployCommand,
6548
+ deployFetch,
6567
6549
  duplicateFlow,
6568
6550
  duplicateFlowCommand,
6569
6551
  feedback,
@@ -6588,16 +6570,18 @@ export {
6588
6570
  loadJsonConfig,
6589
6571
  loginCommand,
6590
6572
  logoutCommand,
6573
+ mergeAuthHeaders,
6591
6574
  parseSSEEvents,
6575
+ publicFetch,
6592
6576
  push,
6593
6577
  pushCommand,
6594
6578
  readConfig,
6595
6579
  requireProjectId,
6596
- resolveBaseUrl,
6597
6580
  run,
6598
6581
  runCommand,
6599
6582
  simulate,
6600
6583
  simulateCommand,
6584
+ throwApiError,
6601
6585
  updateFlow,
6602
6586
  updateFlowCommand,
6603
6587
  updateProject,