@walkeros/cli 1.2.0 → 1.4.0-next-1771252576264

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
@@ -2,7 +2,6 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk3 from "chalk";
6
5
 
7
6
  // src/version.ts
8
7
  import { readFileSync } from "fs";
@@ -27,29 +26,33 @@ function findPackageJson() {
27
26
  }
28
27
  var VERSION = JSON.parse(findPackageJson()).version;
29
28
 
30
- // src/commands/bundle/index.ts
31
- import path9 from "path";
32
- import fs8 from "fs-extra";
33
- import { getPlatform as getPlatform2 } from "@walkeros/core";
29
+ // src/core/banner.ts
30
+ import chalk2 from "chalk";
34
31
 
35
32
  // src/core/logger.ts
36
33
  import chalk from "chalk";
37
34
  var BRAND_COLOR = "#01b5e2";
38
35
  function createLogger(options = {}) {
39
- const { verbose = false, silent = false, json = false } = options;
36
+ const {
37
+ verbose = false,
38
+ silent = false,
39
+ json = false,
40
+ stderr = false
41
+ } = options;
40
42
  const shouldLog = !silent && !json;
41
43
  const shouldDebug = verbose && !silent && !json;
44
+ const out = stderr ? console.error : console.log;
42
45
  return {
43
46
  log: (...args) => {
44
47
  if (shouldLog) {
45
48
  const message = args.map((arg) => String(arg)).join(" ");
46
- console.log(message);
49
+ out(message);
47
50
  }
48
51
  },
49
52
  brand: (...args) => {
50
53
  if (shouldLog) {
51
54
  const message = args.map((arg) => String(arg)).join(" ");
52
- console.log(chalk.hex(BRAND_COLOR)(message));
55
+ out(chalk.hex(BRAND_COLOR)(message));
53
56
  }
54
57
  },
55
58
  error: (...args) => {
@@ -61,43 +64,43 @@ function createLogger(options = {}) {
61
64
  debug: (...args) => {
62
65
  if (shouldDebug) {
63
66
  const message = args.map((arg) => String(arg)).join(" ");
64
- console.log(` ${message}`);
67
+ out(` ${message}`);
65
68
  }
66
69
  },
67
70
  json: (data) => {
68
71
  if (!silent) {
69
- console.log(JSON.stringify(data, null, 2));
72
+ out(JSON.stringify(data, null, 2));
70
73
  }
71
74
  },
72
75
  // Backward-compatible methods (all use default terminal color per design)
73
76
  info: (...args) => {
74
77
  if (shouldLog) {
75
78
  const message = args.map((arg) => String(arg)).join(" ");
76
- console.log(message);
79
+ out(message);
77
80
  }
78
81
  },
79
82
  success: (...args) => {
80
83
  if (shouldLog) {
81
84
  const message = args.map((arg) => String(arg)).join(" ");
82
- console.log(message);
85
+ out(message);
83
86
  }
84
87
  },
85
88
  warning: (...args) => {
86
89
  if (shouldLog) {
87
90
  const message = args.map((arg) => String(arg)).join(" ");
88
- console.log(message);
91
+ out(message);
89
92
  }
90
93
  },
91
94
  warn: (...args) => {
92
95
  if (shouldLog) {
93
96
  const message = args.map((arg) => String(arg)).join(" ");
94
- console.log(message);
97
+ out(message);
95
98
  }
96
99
  },
97
100
  gray: (...args) => {
98
101
  if (shouldLog) {
99
102
  const message = args.map((arg) => String(arg)).join(" ");
100
- console.log(message);
103
+ out(message);
101
104
  }
102
105
  }
103
106
  };
@@ -106,10 +109,25 @@ function createCommandLogger(options) {
106
109
  return createLogger({
107
110
  verbose: options.verbose,
108
111
  silent: options.silent ?? false,
109
- json: options.json
112
+ json: options.json,
113
+ stderr: options.stderr
110
114
  });
111
115
  }
112
116
 
117
+ // src/core/banner.ts
118
+ function printBanner(version) {
119
+ const b = chalk2.hex(BRAND_COLOR);
120
+ console.error(`${b(" \u2571\u2571")}`);
121
+ console.error(`${b(" \u2571\u2571 \u2571\u2571")} ${b("walkerOS")}`);
122
+ console.error(`${b("\u2571\u2571 \u2571\u2571 \u2571\u2571")} v${version}`);
123
+ console.error("");
124
+ }
125
+
126
+ // src/commands/bundle/index.ts
127
+ import path10 from "path";
128
+ import fs9 from "fs-extra";
129
+ import { getPlatform as getPlatform2 } from "@walkeros/core";
130
+
113
131
  // src/core/collector-logger.ts
114
132
  function createCollectorLoggerConfig(cliLogger, verbose) {
115
133
  return {
@@ -152,6 +170,18 @@ function createTimer() {
152
170
  }
153
171
 
154
172
  // src/core/output.ts
173
+ import fs from "fs-extra";
174
+ import path from "path";
175
+ async function writeResult(content, options) {
176
+ if (options.output) {
177
+ const outputPath = path.resolve(options.output);
178
+ await fs.ensureDir(path.dirname(outputPath));
179
+ await fs.writeFile(outputPath, content);
180
+ } else {
181
+ process.stdout.write(content);
182
+ process.stdout.write("\n");
183
+ }
184
+ }
155
185
  function createJsonOutput(success, data, error, duration) {
156
186
  return {
157
187
  success,
@@ -171,25 +201,107 @@ function formatBytes(bytes) {
171
201
  }
172
202
 
173
203
  // src/core/tmp.ts
174
- import path from "path";
204
+ import path2 from "path";
175
205
  var DEFAULT_TMP_ROOT = ".tmp";
176
206
  function getTmpPath(tmpDir, ...segments) {
177
207
  const root = tmpDir || DEFAULT_TMP_ROOT;
178
- const absoluteRoot = path.isAbsolute(root) ? root : path.resolve(root);
179
- return path.join(absoluteRoot, ...segments);
180
- }
181
- function getDefaultTmpRoot() {
182
- return DEFAULT_TMP_ROOT;
208
+ const absoluteRoot = path2.isAbsolute(root) ? root : path2.resolve(root);
209
+ return path2.join(absoluteRoot, ...segments);
183
210
  }
184
211
 
185
212
  // src/core/asset-resolver.ts
186
213
  import { fileURLToPath as fileURLToPath2 } from "url";
187
- import { existsSync } from "fs";
214
+ import { existsSync as existsSync2 } from "fs";
215
+ import path4 from "path";
216
+
217
+ // src/config/utils.ts
218
+ import fs2 from "fs-extra";
188
219
  import path3 from "path";
189
220
 
221
+ // src/lib/config-file.ts
222
+ import {
223
+ readFileSync as readFileSync2,
224
+ writeFileSync,
225
+ mkdirSync,
226
+ unlinkSync,
227
+ existsSync
228
+ } from "fs";
229
+ import { join as join2 } from "path";
230
+ import { homedir } from "os";
231
+ function getConfigDir() {
232
+ const xdgConfig = process.env.XDG_CONFIG_HOME;
233
+ const base = xdgConfig || join2(homedir(), ".config");
234
+ return join2(base, "walkeros");
235
+ }
236
+ function getConfigPath() {
237
+ return join2(getConfigDir(), "config.json");
238
+ }
239
+ function readConfig() {
240
+ const configPath = getConfigPath();
241
+ try {
242
+ const content = readFileSync2(configPath, "utf-8");
243
+ return JSON.parse(content);
244
+ } catch {
245
+ return null;
246
+ }
247
+ }
248
+ function writeConfig(config) {
249
+ const dir = getConfigDir();
250
+ mkdirSync(dir, { recursive: true });
251
+ const configPath = getConfigPath();
252
+ writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
253
+ }
254
+ function deleteConfig() {
255
+ const configPath = getConfigPath();
256
+ if (existsSync(configPath)) {
257
+ unlinkSync(configPath);
258
+ return true;
259
+ }
260
+ return false;
261
+ }
262
+ function resolveToken() {
263
+ const envToken = process.env.WALKEROS_TOKEN;
264
+ if (envToken) return { token: envToken, source: "env" };
265
+ const config = readConfig();
266
+ if (config?.token) return { token: config.token, source: "config" };
267
+ return null;
268
+ }
269
+ function resolveAppUrl() {
270
+ const envUrl = process.env.WALKEROS_APP_URL;
271
+ if (envUrl) return envUrl;
272
+ const config = readConfig();
273
+ if (config?.appUrl) return config.appUrl;
274
+ return "https://app.walkeros.io";
275
+ }
276
+
277
+ // src/core/auth.ts
278
+ function getToken() {
279
+ const result = resolveToken();
280
+ return result?.token;
281
+ }
282
+ function getAuthHeaders() {
283
+ const token = getToken();
284
+ if (!token) return {};
285
+ return { Authorization: `Bearer ${token}` };
286
+ }
287
+ async function authenticatedFetch(url, init) {
288
+ const authHeaders = getAuthHeaders();
289
+ const existingHeaders = init?.headers instanceof Headers ? Object.fromEntries(init.headers.entries()) : Array.isArray(init?.headers) ? Object.fromEntries(init.headers) : init?.headers ?? {};
290
+ return fetch(url, {
291
+ ...init,
292
+ headers: { ...existingHeaders, ...authHeaders }
293
+ });
294
+ }
295
+ function resolveBaseUrl() {
296
+ return resolveAppUrl();
297
+ }
298
+ function requireProjectId() {
299
+ const projectId = process.env.WALKEROS_PROJECT_ID;
300
+ if (!projectId) throw new Error("WALKEROS_PROJECT_ID not set.");
301
+ return projectId;
302
+ }
303
+
190
304
  // src/config/utils.ts
191
- import fs from "fs-extra";
192
- import path2 from "path";
193
305
  function isUrl(str) {
194
306
  try {
195
307
  const url = new URL(str);
@@ -203,7 +315,7 @@ async function downloadFromUrl(url) {
203
315
  throw new Error(`Invalid URL: ${url}`);
204
316
  }
205
317
  try {
206
- const response = await fetch(url);
318
+ const response = await authenticatedFetch(url);
207
319
  if (!response.ok) {
208
320
  throw new Error(
209
321
  `Failed to download ${url}: ${response.status} ${response.statusText}`
@@ -211,9 +323,9 @@ async function downloadFromUrl(url) {
211
323
  }
212
324
  const content = await response.text();
213
325
  const downloadsDir = getTmpPath(void 0, "downloads");
214
- await fs.ensureDir(downloadsDir);
215
- const tempPath = path2.join(downloadsDir, "flow.json");
216
- await fs.writeFile(tempPath, content, "utf-8");
326
+ await fs2.ensureDir(downloadsDir);
327
+ const tempPath = path3.join(downloadsDir, "flow.json");
328
+ await fs2.writeFile(tempPath, content, "utf-8");
217
329
  return tempPath;
218
330
  } catch (error) {
219
331
  if (error instanceof Error) {
@@ -229,13 +341,13 @@ async function loadJsonConfig(configPath) {
229
341
  absolutePath = await downloadFromUrl(configPath);
230
342
  isTemporary = true;
231
343
  } else {
232
- absolutePath = path2.resolve(configPath);
233
- if (!await fs.pathExists(absolutePath)) {
344
+ absolutePath = path3.resolve(configPath);
345
+ if (!await fs2.pathExists(absolutePath)) {
234
346
  throw new Error(`Configuration file not found: ${absolutePath}`);
235
347
  }
236
348
  }
237
349
  try {
238
- const rawConfig = await fs.readJson(absolutePath);
350
+ const rawConfig = await fs2.readJson(absolutePath);
239
351
  return rawConfig;
240
352
  } catch (error) {
241
353
  throw new Error(
@@ -244,7 +356,7 @@ async function loadJsonConfig(configPath) {
244
356
  } finally {
245
357
  if (isTemporary) {
246
358
  try {
247
- await fs.remove(absolutePath);
359
+ await fs2.remove(absolutePath);
248
360
  } catch {
249
361
  }
250
362
  }
@@ -266,11 +378,11 @@ async function loadJsonFromSource(source, options) {
266
378
  try {
267
379
  const tempPath = await downloadFromUrl(trimmedSource);
268
380
  try {
269
- const data = await fs.readJson(tempPath);
381
+ const data = await fs2.readJson(tempPath);
270
382
  return data;
271
383
  } finally {
272
384
  try {
273
- await fs.remove(tempPath);
385
+ await fs2.remove(tempPath);
274
386
  } catch {
275
387
  }
276
388
  }
@@ -280,10 +392,10 @@ async function loadJsonFromSource(source, options) {
280
392
  );
281
393
  }
282
394
  }
283
- const resolvedPath = path2.resolve(trimmedSource);
284
- if (await fs.pathExists(resolvedPath)) {
395
+ const resolvedPath = path3.resolve(trimmedSource);
396
+ if (await fs2.pathExists(resolvedPath)) {
285
397
  try {
286
- const data = await fs.readJson(resolvedPath);
398
+ const data = await fs2.readJson(resolvedPath);
287
399
  return data;
288
400
  } catch (error) {
289
401
  throw new Error(
@@ -309,15 +421,15 @@ var cachedAssetDir;
309
421
  function getAssetDir() {
310
422
  if (cachedAssetDir) return cachedAssetDir;
311
423
  const currentFile = fileURLToPath2(import.meta.url);
312
- let dir = path3.dirname(currentFile);
313
- while (dir !== path3.dirname(dir)) {
314
- if (existsSync(path3.join(dir, "examples"))) {
424
+ let dir = path4.dirname(currentFile);
425
+ while (dir !== path4.dirname(dir)) {
426
+ if (existsSync2(path4.join(dir, "examples"))) {
315
427
  cachedAssetDir = dir;
316
428
  return dir;
317
429
  }
318
- dir = path3.dirname(dir);
430
+ dir = path4.dirname(dir);
319
431
  }
320
- cachedAssetDir = path3.dirname(currentFile);
432
+ cachedAssetDir = path4.dirname(currentFile);
321
433
  return cachedAssetDir;
322
434
  }
323
435
  function resolveAsset(assetPath, assetType, baseDir) {
@@ -326,12 +438,12 @@ function resolveAsset(assetPath, assetType, baseDir) {
326
438
  }
327
439
  if (!assetPath.includes("/") && !assetPath.includes("\\")) {
328
440
  const assetDir = getAssetDir();
329
- return path3.join(assetDir, "examples", assetPath);
441
+ return path4.join(assetDir, "examples", assetPath);
330
442
  }
331
- if (path3.isAbsolute(assetPath)) {
443
+ if (path4.isAbsolute(assetPath)) {
332
444
  return assetPath;
333
445
  }
334
- return path3.resolve(baseDir || process.cwd(), assetPath);
446
+ return path4.resolve(baseDir || process.cwd(), assetPath);
335
447
  }
336
448
 
337
449
  // src/core/utils.ts
@@ -340,23 +452,23 @@ function getErrorMessage(error) {
340
452
  }
341
453
 
342
454
  // src/core/local-packages.ts
343
- import path4 from "path";
344
- import fs2 from "fs-extra";
455
+ import path5 from "path";
456
+ import fs3 from "fs-extra";
345
457
  async function resolveLocalPackage(packageName, localPath, configDir, logger2) {
346
- const absolutePath = path4.isAbsolute(localPath) ? localPath : path4.resolve(configDir, localPath);
347
- if (!await fs2.pathExists(absolutePath)) {
458
+ const absolutePath = path5.isAbsolute(localPath) ? localPath : path5.resolve(configDir, localPath);
459
+ if (!await fs3.pathExists(absolutePath)) {
348
460
  throw new Error(
349
461
  `Local package path not found: ${localPath} (resolved to ${absolutePath})`
350
462
  );
351
463
  }
352
- const pkgJsonPath = path4.join(absolutePath, "package.json");
353
- if (!await fs2.pathExists(pkgJsonPath)) {
464
+ const pkgJsonPath = path5.join(absolutePath, "package.json");
465
+ if (!await fs3.pathExists(pkgJsonPath)) {
354
466
  throw new Error(
355
467
  `No package.json found at ${absolutePath}. Is this a valid package directory?`
356
468
  );
357
469
  }
358
- const distPath = path4.join(absolutePath, "dist");
359
- const hasDistFolder = await fs2.pathExists(distPath);
470
+ const distPath = path5.join(absolutePath, "dist");
471
+ const hasDistFolder = await fs3.pathExists(distPath);
360
472
  if (!hasDistFolder) {
361
473
  logger2.warn(
362
474
  `\u26A0\uFE0F ${packageName}: No dist/ folder found. Using package root.`
@@ -370,21 +482,21 @@ async function resolveLocalPackage(packageName, localPath, configDir, logger2) {
370
482
  };
371
483
  }
372
484
  async function copyLocalPackage(localPkg, targetDir, logger2) {
373
- const packageDir = path4.join(targetDir, "node_modules", localPkg.name);
374
- await fs2.ensureDir(path4.dirname(packageDir));
375
- await fs2.copy(
376
- path4.join(localPkg.absolutePath, "package.json"),
377
- path4.join(packageDir, "package.json")
485
+ const packageDir = path5.join(targetDir, "node_modules", localPkg.name);
486
+ await fs3.ensureDir(path5.dirname(packageDir));
487
+ await fs3.copy(
488
+ path5.join(localPkg.absolutePath, "package.json"),
489
+ path5.join(packageDir, "package.json")
378
490
  );
379
491
  if (localPkg.hasDistFolder) {
380
- await fs2.copy(localPkg.distPath, path4.join(packageDir, "dist"));
492
+ await fs3.copy(localPkg.distPath, path5.join(packageDir, "dist"));
381
493
  } else {
382
- const entries = await fs2.readdir(localPkg.absolutePath);
494
+ const entries = await fs3.readdir(localPkg.absolutePath);
383
495
  for (const entry of entries) {
384
496
  if (!["node_modules", ".turbo", ".git"].includes(entry)) {
385
- await fs2.copy(
386
- path4.join(localPkg.absolutePath, entry),
387
- path4.join(packageDir, entry)
497
+ await fs3.copy(
498
+ path5.join(localPkg.absolutePath, entry),
499
+ path5.join(packageDir, entry)
388
500
  );
389
501
  }
390
502
  }
@@ -394,7 +506,7 @@ async function copyLocalPackage(localPkg, targetDir, logger2) {
394
506
  }
395
507
 
396
508
  // src/core/input-detector.ts
397
- import fs3 from "fs-extra";
509
+ import fs4 from "fs-extra";
398
510
  async function detectInput(inputPath, platformOverride) {
399
511
  const content = await loadContent(inputPath);
400
512
  try {
@@ -411,13 +523,29 @@ function detectPlatformFromPath(inputPath) {
411
523
  }
412
524
  async function loadContent(inputPath) {
413
525
  if (isUrl(inputPath)) {
414
- const response = await fetch(inputPath);
526
+ const response = await authenticatedFetch(inputPath);
415
527
  if (!response.ok) {
416
528
  throw new Error(`Failed to fetch ${inputPath}: ${response.status}`);
417
529
  }
418
530
  return response.text();
419
531
  }
420
- return fs3.readFile(inputPath, "utf8");
532
+ return fs4.readFile(inputPath, "utf8");
533
+ }
534
+
535
+ // src/core/stdin.ts
536
+ function isStdinPiped() {
537
+ return !process.stdin.isTTY;
538
+ }
539
+ async function readStdin() {
540
+ const chunks = [];
541
+ for await (const chunk of process.stdin) {
542
+ chunks.push(chunk);
543
+ }
544
+ const content = Buffer.concat(chunks).toString("utf-8");
545
+ if (!content.trim()) {
546
+ throw new Error("No input received on stdin");
547
+ }
548
+ return content;
421
549
  }
422
550
 
423
551
  // src/config/validators.ts
@@ -430,8 +558,8 @@ function validateFlowSetup(data) {
430
558
  const result = safeParseSetup(data);
431
559
  if (!result.success) {
432
560
  const errors = result.error.issues.map((issue) => {
433
- const path14 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
434
- return ` - ${path14}: ${issue.message}`;
561
+ const path15 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
562
+ return ` - ${path15}: ${issue.message}`;
435
563
  }).join("\n");
436
564
  throw new Error(`Invalid configuration:
437
565
  ${errors}`);
@@ -473,8 +601,8 @@ function getDefaultOutput(platform) {
473
601
  }
474
602
 
475
603
  // src/config/loader.ts
476
- import path5 from "path";
477
- import fs4 from "fs-extra";
604
+ import path6 from "path";
605
+ import fs5 from "fs-extra";
478
606
  import { getFlowConfig, getPlatform } from "@walkeros/core";
479
607
  var DEFAULT_INCLUDE_FOLDER = "./shared";
480
608
  function loadBundleConfig(rawConfig, options) {
@@ -491,11 +619,11 @@ function loadBundleConfig(rawConfig, options) {
491
619
  const buildDefaults = getBuildDefaults(platform);
492
620
  const packages = flowConfig.packages || {};
493
621
  const output = options.buildOverrides?.output || getDefaultOutput(platform);
494
- const configDir = isUrl(options.configPath) ? process.cwd() : path5.dirname(options.configPath);
622
+ const configDir = isUrl(options.configPath) ? process.cwd() : path6.dirname(options.configPath);
495
623
  let includes = setup.include;
496
624
  if (!includes) {
497
- const defaultIncludePath = path5.resolve(configDir, DEFAULT_INCLUDE_FOLDER);
498
- if (fs4.pathExistsSync(defaultIncludePath)) {
625
+ const defaultIncludePath = path6.resolve(configDir, DEFAULT_INCLUDE_FOLDER);
626
+ if (fs5.pathExistsSync(defaultIncludePath)) {
499
627
  includes = [DEFAULT_INCLUDE_FOLDER];
500
628
  }
501
629
  }
@@ -561,14 +689,14 @@ async function loadFlowConfig(configPath, options) {
561
689
 
562
690
  // src/commands/bundle/bundler.ts
563
691
  import esbuild from "esbuild";
564
- import path8 from "path";
565
- import fs7 from "fs-extra";
692
+ import path9 from "path";
693
+ import fs8 from "fs-extra";
566
694
  import { packageNameToVariable } from "@walkeros/core";
567
695
 
568
696
  // src/commands/bundle/package-manager.ts
569
697
  import pacote from "pacote";
570
- import path6 from "path";
571
- import fs5 from "fs-extra";
698
+ import path7 from "path";
699
+ import fs6 from "fs-extra";
572
700
 
573
701
  // src/core/cache-utils.ts
574
702
  import { getHashServer } from "@walkeros/server-core";
@@ -609,16 +737,16 @@ async function withTimeout(promise, ms, errorMessage) {
609
737
  return Promise.race([promise, timeout]);
610
738
  }
611
739
  function getPackageDirectory(baseDir, packageName, version) {
612
- return path6.join(baseDir, "node_modules", packageName);
740
+ return path7.join(baseDir, "node_modules", packageName);
613
741
  }
614
742
  async function getCachedPackagePath(pkg, tmpDir) {
615
743
  const cacheDir = getTmpPath(tmpDir, "cache", "packages");
616
744
  const cacheKey = await getPackageCacheKey(pkg.name, pkg.version);
617
- return path6.join(cacheDir, cacheKey);
745
+ return path7.join(cacheDir, cacheKey);
618
746
  }
619
747
  async function isPackageCached(pkg, tmpDir) {
620
748
  const cachedPath = await getCachedPackagePath(pkg, tmpDir);
621
- return fs5.pathExists(cachedPath);
749
+ return fs6.pathExists(cachedPath);
622
750
  }
623
751
  function validateNoDuplicatePackages(packages) {
624
752
  const packageMap = /* @__PURE__ */ new Map();
@@ -652,9 +780,9 @@ async function resolveDependencies(pkg, packageDir, logger2, visited = /* @__PUR
652
780
  }
653
781
  visited.add(pkgKey);
654
782
  try {
655
- const packageJsonPath = path6.join(packageDir, "package.json");
656
- if (await fs5.pathExists(packageJsonPath)) {
657
- const packageJson = await fs5.readJson(packageJsonPath);
783
+ const packageJsonPath = path7.join(packageDir, "package.json");
784
+ if (await fs6.pathExists(packageJsonPath)) {
785
+ const packageJson = await fs6.readJson(packageJsonPath);
658
786
  const deps = {
659
787
  ...packageJson.dependencies,
660
788
  ...packageJson.peerDependencies
@@ -682,7 +810,7 @@ async function downloadPackages(packages, targetDir, logger2, useCache = true, c
682
810
  }
683
811
  }
684
812
  validateNoDuplicatePackages(packages);
685
- await fs5.ensureDir(targetDir);
813
+ await fs6.ensureDir(targetDir);
686
814
  while (downloadQueue.length > 0) {
687
815
  const pkg = downloadQueue.shift();
688
816
  const pkgKey = `${pkg.name}@${pkg.version}`;
@@ -719,8 +847,8 @@ async function downloadPackages(packages, targetDir, logger2, useCache = true, c
719
847
  logger2.debug(`Downloading ${packageSpec} (cached)`);
720
848
  }
721
849
  try {
722
- await fs5.ensureDir(path6.dirname(packageDir));
723
- await fs5.copy(cachedPath, packageDir);
850
+ await fs6.ensureDir(path7.dirname(packageDir));
851
+ await fs6.copy(cachedPath, packageDir);
724
852
  packagePaths.set(pkg.name, packageDir);
725
853
  const deps = await resolveDependencies(pkg, packageDir, logger2);
726
854
  for (const dep of deps) {
@@ -737,7 +865,7 @@ async function downloadPackages(packages, targetDir, logger2, useCache = true, c
737
865
  }
738
866
  }
739
867
  try {
740
- await fs5.ensureDir(path6.dirname(packageDir));
868
+ await fs6.ensureDir(path7.dirname(packageDir));
741
869
  const cacheDir = process.env.NPM_CACHE_DIR || getTmpPath(void 0, "cache", "npm");
742
870
  await withTimeout(
743
871
  pacote.extract(packageSpec, packageDir, {
@@ -754,15 +882,15 @@ async function downloadPackages(packages, targetDir, logger2, useCache = true, c
754
882
  `Package download timed out after ${PACKAGE_DOWNLOAD_TIMEOUT_MS / 1e3}s: ${packageSpec}`
755
883
  );
756
884
  if (userSpecifiedPackages.has(pkg.name)) {
757
- const pkgStats = await fs5.stat(path6.join(packageDir, "package.json"));
885
+ const pkgStats = await fs6.stat(path7.join(packageDir, "package.json"));
758
886
  const pkgJsonSize = pkgStats.size;
759
887
  const sizeKB = (pkgJsonSize / 1024).toFixed(1);
760
888
  logger2.debug(`Downloading ${packageSpec} (${sizeKB} KB)`);
761
889
  }
762
890
  if (useCache) {
763
891
  try {
764
- await fs5.ensureDir(path6.dirname(cachedPath));
765
- await fs5.copy(packageDir, cachedPath);
892
+ await fs6.ensureDir(path7.dirname(cachedPath));
893
+ await fs6.copy(packageDir, cachedPath);
766
894
  } catch (cacheError) {
767
895
  }
768
896
  }
@@ -782,26 +910,26 @@ async function downloadPackages(packages, targetDir, logger2, useCache = true, c
782
910
  }
783
911
 
784
912
  // src/core/build-cache.ts
785
- import fs6 from "fs-extra";
786
- import path7 from "path";
913
+ import fs7 from "fs-extra";
914
+ import path8 from "path";
787
915
  async function getBuildCachePath(configContent, tmpDir) {
788
916
  const cacheDir = getTmpPath(tmpDir, "cache", "builds");
789
917
  const cacheKey = await getFlowConfigCacheKey(configContent);
790
- return path7.join(cacheDir, `${cacheKey}.js`);
918
+ return path8.join(cacheDir, `${cacheKey}.js`);
791
919
  }
792
920
  async function isBuildCached(configContent, tmpDir) {
793
921
  const cachePath = await getBuildCachePath(configContent, tmpDir);
794
- return fs6.pathExists(cachePath);
922
+ return fs7.pathExists(cachePath);
795
923
  }
796
924
  async function cacheBuild(configContent, buildOutput, tmpDir) {
797
925
  const cachePath = await getBuildCachePath(configContent, tmpDir);
798
- await fs6.ensureDir(path7.dirname(cachePath));
799
- await fs6.writeFile(cachePath, buildOutput, "utf-8");
926
+ await fs7.ensureDir(path8.dirname(cachePath));
927
+ await fs7.writeFile(cachePath, buildOutput, "utf-8");
800
928
  }
801
929
  async function getCachedBuild(configContent, tmpDir) {
802
930
  const cachePath = await getBuildCachePath(configContent, tmpDir);
803
- if (await fs6.pathExists(cachePath)) {
804
- return await fs6.readFile(cachePath, "utf-8");
931
+ if (await fs7.pathExists(cachePath)) {
932
+ return await fs7.readFile(cachePath, "utf-8");
805
933
  }
806
934
  return null;
807
935
  }
@@ -854,11 +982,11 @@ function generateInlineCode(inline, config, env, chain, chainPropertyName, isDes
854
982
  }
855
983
  async function copyIncludes(includes, sourceDir, outputDir, logger2) {
856
984
  for (const include of includes) {
857
- const sourcePath = path8.resolve(sourceDir, include);
858
- const folderName = path8.basename(include);
859
- const destPath = path8.join(outputDir, folderName);
860
- if (await fs7.pathExists(sourcePath)) {
861
- await fs7.copy(sourcePath, destPath);
985
+ const sourcePath = path9.resolve(sourceDir, include);
986
+ const folderName = path9.basename(include);
987
+ const destPath = path9.join(outputDir, folderName);
988
+ if (await fs8.pathExists(sourcePath)) {
989
+ await fs8.copy(sourcePath, destPath);
862
990
  logger2.debug(`Copied ${include} to output`);
863
991
  } else {
864
992
  logger2.debug(`Include folder not found: ${include}`);
@@ -927,14 +1055,14 @@ async function bundleCore(flowConfig, buildOptions, logger2, showStats = false)
927
1055
  const cachedBuild = await getCachedBuild(configContent);
928
1056
  if (cachedBuild) {
929
1057
  logger2.debug("Using cached build");
930
- const outputPath = path8.resolve(buildOptions.output);
931
- await fs7.ensureDir(path8.dirname(outputPath));
932
- await fs7.writeFile(outputPath, cachedBuild);
933
- const stats = await fs7.stat(outputPath);
1058
+ const outputPath = path9.resolve(buildOptions.output);
1059
+ await fs8.ensureDir(path9.dirname(outputPath));
1060
+ await fs8.writeFile(outputPath, cachedBuild);
1061
+ const stats = await fs8.stat(outputPath);
934
1062
  const sizeKB = (stats.size / 1024).toFixed(1);
935
1063
  logger2.log(`Output: ${outputPath} (${sizeKB} KB, cached)`);
936
1064
  if (showStats) {
937
- const stats2 = await fs7.stat(outputPath);
1065
+ const stats2 = await fs8.stat(outputPath);
938
1066
  const packageStats = Object.entries(buildOptions.packages).map(
939
1067
  ([name, pkg]) => ({
940
1068
  name: `${name}@${pkg.version || "latest"}`,
@@ -954,7 +1082,7 @@ async function bundleCore(flowConfig, buildOptions, logger2, showStats = false)
954
1082
  }
955
1083
  }
956
1084
  try {
957
- await fs7.ensureDir(TEMP_DIR);
1085
+ await fs8.ensureDir(TEMP_DIR);
958
1086
  const hasSourcesOrDests = Object.keys(
959
1087
  flowConfig.sources || {}
960
1088
  ).length > 0 || Object.keys(
@@ -982,8 +1110,8 @@ async function bundleCore(flowConfig, buildOptions, logger2, showStats = false)
982
1110
  );
983
1111
  for (const [pkgName, pkgPath] of packagePaths.entries()) {
984
1112
  if (pkgName.startsWith("@walkeros/")) {
985
- const pkgJsonPath = path8.join(pkgPath, "package.json");
986
- const pkgJson = await fs7.readJSON(pkgJsonPath);
1113
+ const pkgJsonPath = path9.join(pkgPath, "package.json");
1114
+ const pkgJson = await fs8.readJSON(pkgJsonPath);
987
1115
  if (!pkgJson.exports && pkgJson.module) {
988
1116
  pkgJson.exports = {
989
1117
  ".": {
@@ -991,12 +1119,12 @@ async function bundleCore(flowConfig, buildOptions, logger2, showStats = false)
991
1119
  require: pkgJson.main
992
1120
  }
993
1121
  };
994
- await fs7.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
1122
+ await fs8.writeJSON(pkgJsonPath, pkgJson, { spaces: 2 });
995
1123
  }
996
1124
  }
997
1125
  }
998
- const packageJsonPath = path8.join(TEMP_DIR, "package.json");
999
- await fs7.writeFile(
1126
+ const packageJsonPath = path9.join(TEMP_DIR, "package.json");
1127
+ await fs8.writeFile(
1000
1128
  packageJsonPath,
1001
1129
  JSON.stringify({ type: "module" }, null, 2)
1002
1130
  );
@@ -1006,13 +1134,13 @@ async function bundleCore(flowConfig, buildOptions, logger2, showStats = false)
1006
1134
  buildOptions,
1007
1135
  packagePaths
1008
1136
  );
1009
- const entryPath = path8.join(TEMP_DIR, "entry.js");
1010
- await fs7.writeFile(entryPath, entryContent);
1137
+ const entryPath = path9.join(TEMP_DIR, "entry.js");
1138
+ await fs8.writeFile(entryPath, entryContent);
1011
1139
  logger2.debug(
1012
1140
  `Running esbuild (target: ${buildOptions.target || "es2018"}, format: ${buildOptions.format})`
1013
1141
  );
1014
- const outputPath = path8.resolve(buildOptions.output);
1015
- await fs7.ensureDir(path8.dirname(outputPath));
1142
+ const outputPath = path9.resolve(buildOptions.output);
1143
+ await fs8.ensureDir(path9.dirname(outputPath));
1016
1144
  const esbuildOptions = createEsbuildOptions(
1017
1145
  buildOptions,
1018
1146
  entryPath,
@@ -1031,13 +1159,13 @@ async function bundleCore(flowConfig, buildOptions, logger2, showStats = false)
1031
1159
  } finally {
1032
1160
  await esbuild.stop();
1033
1161
  }
1034
- const outputStats = await fs7.stat(outputPath);
1162
+ const outputStats = await fs8.stat(outputPath);
1035
1163
  const sizeKB = (outputStats.size / 1024).toFixed(1);
1036
1164
  const buildTime = ((Date.now() - bundleStartTime) / 1e3).toFixed(1);
1037
1165
  logger2.log(`Output: ${outputPath} (${sizeKB} KB, ${buildTime}s)`);
1038
1166
  if (buildOptions.cache !== false) {
1039
1167
  const configContent = generateCacheKeyContent(flowConfig, buildOptions);
1040
- const buildOutput = await fs7.readFile(outputPath, "utf-8");
1168
+ const buildOutput = await fs8.readFile(outputPath, "utf-8");
1041
1169
  await cacheBuild(configContent, buildOutput);
1042
1170
  logger2.debug("Build cached for future use");
1043
1171
  }
@@ -1051,7 +1179,7 @@ async function bundleCore(flowConfig, buildOptions, logger2, showStats = false)
1051
1179
  );
1052
1180
  }
1053
1181
  if (buildOptions.include && buildOptions.include.length > 0) {
1054
- const outputDir = path8.dirname(outputPath);
1182
+ const outputDir = path9.dirname(outputPath);
1055
1183
  await copyIncludes(
1056
1184
  buildOptions.include,
1057
1185
  buildOptions.configDir || process.cwd(),
@@ -1065,7 +1193,7 @@ async function bundleCore(flowConfig, buildOptions, logger2, showStats = false)
1065
1193
  }
1066
1194
  }
1067
1195
  async function collectBundleStats(outputPath, packages, startTime, entryContent) {
1068
- const stats = await fs7.stat(outputPath);
1196
+ const stats = await fs8.stat(outputPath);
1069
1197
  const totalSize = stats.size;
1070
1198
  const buildTime = Date.now() - startTime;
1071
1199
  const packageStats = Object.entries(packages).map(([name, pkg]) => {
@@ -1576,17 +1704,62 @@ Package Breakdown:`);
1576
1704
  logger2.info("\u2500".repeat(50));
1577
1705
  }
1578
1706
 
1707
+ // src/core/api-client.ts
1708
+ import createClient from "openapi-fetch";
1709
+ function createApiClient() {
1710
+ const token = getToken();
1711
+ if (!token) throw new Error("WALKEROS_TOKEN not set.");
1712
+ return createClient({
1713
+ baseUrl: resolveBaseUrl(),
1714
+ headers: {
1715
+ Authorization: `Bearer ${token}`,
1716
+ "Content-Type": "application/json"
1717
+ }
1718
+ });
1719
+ }
1720
+
1579
1721
  // src/commands/bundle/index.ts
1722
+ function resolveOutputPath(output, buildOptions) {
1723
+ const resolved = path10.resolve(output);
1724
+ const ext = path10.extname(resolved);
1725
+ if (output.endsWith("/") || output.endsWith(path10.sep) || !ext) {
1726
+ const filename = buildOptions.platform === "browser" ? "walker.js" : "bundle.mjs";
1727
+ return path10.join(resolved, filename);
1728
+ }
1729
+ return resolved;
1730
+ }
1580
1731
  async function bundleCommand(options) {
1581
1732
  const timer = createTimer();
1582
1733
  timer.start();
1583
- const logger2 = createCommandLogger(options);
1734
+ const writingToStdout = !options.output;
1735
+ const logger2 = createCommandLogger({
1736
+ ...options,
1737
+ stderr: writingToStdout
1738
+ });
1584
1739
  try {
1585
1740
  if (options.flow && options.all) {
1586
1741
  throw new Error("Cannot use both --flow and --all flags together");
1587
1742
  }
1588
- const configPath = resolveAsset(options.config, "config");
1589
- const rawConfig = await loadJsonConfig(configPath);
1743
+ if (options.all && writingToStdout) {
1744
+ throw new Error(
1745
+ "Cannot use --all without --output (multiple bundles need file output)"
1746
+ );
1747
+ }
1748
+ let rawConfig;
1749
+ let configPath;
1750
+ if (isStdinPiped() && !options.config) {
1751
+ const stdinContent = await readStdin();
1752
+ try {
1753
+ rawConfig = JSON.parse(stdinContent);
1754
+ } catch {
1755
+ throw new Error("Invalid JSON received on stdin");
1756
+ }
1757
+ configPath = path10.resolve(process.cwd(), "stdin.config.json");
1758
+ } else {
1759
+ const file = options.config || "bundle.config.json";
1760
+ configPath = resolveAsset(file, "config");
1761
+ rawConfig = await loadJsonConfig(configPath);
1762
+ }
1590
1763
  const configsToBundle = options.all ? loadAllFlows(rawConfig, { configPath, logger: logger2 }) : [
1591
1764
  loadBundleConfig(rawConfig, {
1592
1765
  configPath,
@@ -1605,11 +1778,16 @@ async function bundleCommand(options) {
1605
1778
  if (options.cache !== void 0) {
1606
1779
  buildOptions.cache = options.cache;
1607
1780
  }
1608
- const configBasename = path9.basename(configPath);
1781
+ if (options.output) {
1782
+ buildOptions.output = resolveOutputPath(options.output, buildOptions);
1783
+ } else {
1784
+ const ext = buildOptions.platform === "browser" ? ".js" : ".mjs";
1785
+ buildOptions.output = getTmpPath(void 0, "stdout-bundle" + ext);
1786
+ }
1609
1787
  if (isMultiFlow || options.all) {
1610
- logger2.log(`Bundling ${configBasename} (flow: ${flowName})...`);
1788
+ logger2.log(`Bundling flow: ${flowName}...`);
1611
1789
  } else {
1612
- logger2.log(`Bundling ${configBasename}...`);
1790
+ logger2.log("Bundling...");
1613
1791
  }
1614
1792
  const shouldCollectStats = options.stats || options.json;
1615
1793
  const stats = await bundleCore(
@@ -1618,29 +1796,25 @@ async function bundleCommand(options) {
1618
1796
  logger2,
1619
1797
  shouldCollectStats
1620
1798
  );
1621
- results.push({
1622
- flowName,
1623
- success: true,
1624
- stats
1625
- });
1799
+ results.push({ flowName, success: true, stats });
1626
1800
  if (!options.json && !options.all && options.stats && stats) {
1627
1801
  displayStats(stats, logger2);
1628
1802
  }
1629
- if (options.dockerfile && !options.all) {
1803
+ if (writingToStdout && !options.json) {
1804
+ const bundleContent = await fs9.readFile(buildOptions.output);
1805
+ await writeResult(bundleContent, {});
1806
+ }
1807
+ if (options.dockerfile && options.output) {
1630
1808
  const platform = getPlatform2(flowConfig);
1631
1809
  if (platform) {
1632
- const outputDir = path9.dirname(buildOptions.output);
1810
+ const outputDir = path10.dirname(buildOptions.output);
1633
1811
  const customFile = typeof options.dockerfile === "string" ? options.dockerfile : void 0;
1634
1812
  await generateDockerfile(outputDir, platform, logger2, customFile);
1635
1813
  }
1636
1814
  }
1637
1815
  } catch (error) {
1638
1816
  const errorMessage = getErrorMessage(error);
1639
- results.push({
1640
- flowName,
1641
- success: false,
1642
- error: errorMessage
1643
- });
1817
+ results.push({ flowName, success: false, error: errorMessage });
1644
1818
  if (!options.all) {
1645
1819
  throw error;
1646
1820
  }
@@ -1650,7 +1824,7 @@ async function bundleCommand(options) {
1650
1824
  const successCount = results.filter((r) => r.success).length;
1651
1825
  const failureCount = results.filter((r) => !r.success).length;
1652
1826
  if (options.json) {
1653
- const output = failureCount === 0 ? createSuccessOutput(
1827
+ const jsonResult = failureCount === 0 ? createSuccessOutput(
1654
1828
  {
1655
1829
  flows: results,
1656
1830
  summary: {
@@ -1664,7 +1838,9 @@ async function bundleCommand(options) {
1664
1838
  `${failureCount} flow(s) failed to build`,
1665
1839
  duration
1666
1840
  );
1667
- logger2.json(output);
1841
+ await writeResult(JSON.stringify(jsonResult, null, 2) + "\n", {
1842
+ output: options.output
1843
+ });
1668
1844
  } else {
1669
1845
  if (options.all) {
1670
1846
  logger2.log(
@@ -1684,8 +1860,10 @@ Build Summary: ${successCount}/${results.length} succeeded`
1684
1860
  const duration = timer.getElapsed() / 1e3;
1685
1861
  const errorMessage = getErrorMessage(error);
1686
1862
  if (options.json) {
1687
- const output = createErrorOutput(errorMessage, duration);
1688
- logger2.json(output);
1863
+ const jsonError = createErrorOutput(errorMessage, duration);
1864
+ await writeResult(JSON.stringify(jsonError, null, 2) + "\n", {
1865
+ output: options.output
1866
+ });
1689
1867
  } else {
1690
1868
  logger2.error(`Error: ${errorMessage}`);
1691
1869
  }
@@ -1694,7 +1872,7 @@ Build Summary: ${successCount}/${results.length} succeeded`
1694
1872
  }
1695
1873
  async function bundle(configOrPath, options = {}) {
1696
1874
  let rawConfig;
1697
- let configPath = path9.resolve(process.cwd(), "walkeros.config.json");
1875
+ let configPath = path10.resolve(process.cwd(), "walkeros.config.json");
1698
1876
  if (typeof configOrPath === "string") {
1699
1877
  configPath = resolveAsset(configOrPath, "config");
1700
1878
  rawConfig = await loadJsonConfig(configPath);
@@ -1718,9 +1896,9 @@ async function bundle(configOrPath, options = {}) {
1718
1896
  );
1719
1897
  }
1720
1898
  async function generateDockerfile(outputDir, platform, logger2, customFile) {
1721
- const destPath = path9.join(outputDir, "Dockerfile");
1722
- if (customFile && await fs8.pathExists(customFile)) {
1723
- await fs8.copy(customFile, destPath);
1899
+ const destPath = path10.join(outputDir, "Dockerfile");
1900
+ if (customFile && await fs9.pathExists(customFile)) {
1901
+ await fs9.copy(customFile, destPath);
1724
1902
  logger2.log(`Dockerfile: ${destPath} (copied from ${customFile})`);
1725
1903
  return;
1726
1904
  }
@@ -1737,13 +1915,29 @@ ENV BUNDLE=/app/flow/${bundleFile}
1737
1915
 
1738
1916
  EXPOSE 8080
1739
1917
  `;
1740
- await fs8.writeFile(destPath, dockerfile);
1918
+ await fs9.writeFile(destPath, dockerfile);
1741
1919
  logger2.log(`Dockerfile: ${destPath}`);
1742
1920
  }
1921
+ async function bundleRemote(options) {
1922
+ const client = createApiClient();
1923
+ const { data, error, response } = await client.POST("/api/bundle", {
1924
+ body: { flow: options.content },
1925
+ parseAs: "text"
1926
+ });
1927
+ if (error)
1928
+ throw new Error(typeof error === "string" ? error : "Bundle failed");
1929
+ const js = data;
1930
+ const statsHeader = response.headers.get("X-Bundle-Stats");
1931
+ return {
1932
+ bundle: js,
1933
+ size: js.length,
1934
+ stats: statsHeader ? JSON.parse(statsHeader) : void 0
1935
+ };
1936
+ }
1743
1937
 
1744
1938
  // src/commands/simulate/simulator.ts
1745
- import path10 from "path";
1746
- import fs10 from "fs-extra";
1939
+ import path11 from "path";
1940
+ import fs11 from "fs-extra";
1747
1941
  import { getPlatform as getPlatform3 } from "@walkeros/core";
1748
1942
 
1749
1943
  // src/commands/simulate/tracker.ts
@@ -1780,9 +1974,9 @@ var CallTracker = class {
1780
1974
  }
1781
1975
  for (const fullPath of paths) {
1782
1976
  const [destKey, ...pathParts] = fullPath.split(":");
1783
- const path14 = pathParts.join(":");
1784
- if (!path14) continue;
1785
- const cleanPath = path14.replace(/^call:/, "");
1977
+ const path15 = pathParts.join(":");
1978
+ if (!path15) continue;
1979
+ const cleanPath = path15.replace(/^call:/, "");
1786
1980
  const parts = cleanPath.split(".");
1787
1981
  let current = wrapped;
1788
1982
  let source = env;
@@ -1826,7 +2020,7 @@ var CallTracker = class {
1826
2020
 
1827
2021
  // src/commands/simulate/jsdom-executor.ts
1828
2022
  import { JSDOM, VirtualConsole } from "jsdom";
1829
- import fs9 from "fs-extra";
2023
+ import fs10 from "fs-extra";
1830
2024
  function buildSandboxFromEnvs(envs, destinations, tracker) {
1831
2025
  const baseBrowserMocks = {
1832
2026
  Image: class MockImage {
@@ -1895,7 +2089,7 @@ async function executeInJSDOM(bundlePath, destinations, event, tracker, envs, ti
1895
2089
  const sandbox = buildSandboxFromEnvs(envs, destinations, tracker);
1896
2090
  Object.assign(window, sandbox.window);
1897
2091
  Object.assign(window.document, sandbox.document);
1898
- const bundleCode = await fs9.readFile(bundlePath, "utf8");
2092
+ const bundleCode = await fs10.readFile(bundlePath, "utf8");
1899
2093
  try {
1900
2094
  window.eval(bundleCode);
1901
2095
  } catch (error) {
@@ -2089,7 +2283,7 @@ async function executeSimulation(event, inputPath, platformOverride, options = {
2089
2283
  const tempDir = getTmpPath();
2090
2284
  const collectorLoggerConfig = options.logger ? createCollectorLoggerConfig(options.logger, options.verbose) : void 0;
2091
2285
  try {
2092
- await fs10.ensureDir(tempDir);
2286
+ await fs11.ensureDir(tempDir);
2093
2287
  const detected = await detectInput(inputPath, platformOverride);
2094
2288
  if (!isObject(event) || !("name" in event) || typeof event.name !== "string") {
2095
2289
  throw new Error(
@@ -2126,7 +2320,7 @@ async function executeSimulation(event, inputPath, platformOverride, options = {
2126
2320
  };
2127
2321
  } finally {
2128
2322
  if (tempDir) {
2129
- await fs10.remove(tempDir).catch(() => {
2323
+ await fs11.remove(tempDir).catch(() => {
2130
2324
  });
2131
2325
  }
2132
2326
  }
@@ -2137,7 +2331,7 @@ async function executeConfigSimulation(_content, configPath, typedEvent, tempDir
2137
2331
  });
2138
2332
  const platform = getPlatform3(flowConfig);
2139
2333
  const tracker = new CallTracker();
2140
- const tempOutput = path10.join(
2334
+ const tempOutput = path11.join(
2141
2335
  tempDir,
2142
2336
  `simulation-bundle-${generateId()}.${platform === "web" ? "js" : "mjs"}`
2143
2337
  );
@@ -2195,11 +2389,11 @@ async function executeConfigSimulation(_content, configPath, typedEvent, tempDir
2195
2389
  };
2196
2390
  }
2197
2391
  async function executeBundleSimulation(bundleContent, platform, typedEvent, tempDir, startTime, loggerConfig2) {
2198
- const tempOutput = path10.join(
2392
+ const tempOutput = path11.join(
2199
2393
  tempDir,
2200
2394
  `bundle-${generateId()}.${platform === "server" ? "mjs" : "js"}`
2201
2395
  );
2202
- await fs10.writeFile(tempOutput, bundleContent, "utf8");
2396
+ await fs11.writeFile(tempOutput, bundleContent, "utf8");
2203
2397
  const tracker = new CallTracker();
2204
2398
  let result;
2205
2399
  if (platform === "web") {
@@ -2234,13 +2428,25 @@ async function executeBundleSimulation(bundleContent, platform, typedEvent, temp
2234
2428
 
2235
2429
  // src/commands/simulate/index.ts
2236
2430
  async function simulateCommand(options) {
2237
- const logger2 = createCommandLogger(options);
2431
+ const logger2 = createCommandLogger({ ...options, stderr: true });
2238
2432
  const startTime = Date.now();
2239
2433
  try {
2434
+ let config;
2435
+ if (isStdinPiped() && !options.config) {
2436
+ const stdinContent = await readStdin();
2437
+ const fs15 = await import("fs-extra");
2438
+ const path15 = await import("path");
2439
+ const tmpPath = path15.default.resolve(".tmp", "stdin-simulate.json");
2440
+ await fs15.default.ensureDir(path15.default.dirname(tmpPath));
2441
+ await fs15.default.writeFile(tmpPath, stdinContent, "utf-8");
2442
+ config = tmpPath;
2443
+ } else {
2444
+ config = options.config || "bundle.config.json";
2445
+ }
2240
2446
  const event = await loadJsonFromSource(options.event, {
2241
2447
  name: "event"
2242
2448
  });
2243
- const result = await simulateCore(options.config, event, {
2449
+ const result = await simulateCore(config, event, {
2244
2450
  flow: options.flow,
2245
2451
  json: options.json,
2246
2452
  verbose: options.verbose,
@@ -2250,23 +2456,24 @@ async function simulateCommand(options) {
2250
2456
  ...result,
2251
2457
  duration: (Date.now() - startTime) / 1e3
2252
2458
  };
2253
- const output = formatSimulationResult(resultWithDuration, {
2459
+ const formatted = formatSimulationResult(resultWithDuration, {
2254
2460
  json: options.json
2255
2461
  });
2256
- if (options.json) {
2257
- console.log(output);
2258
- } else {
2259
- logger2.log(output);
2260
- }
2462
+ await writeResult(formatted + "\n", { output: options.output });
2261
2463
  process.exit(result.success ? 0 : 1);
2262
2464
  } catch (error) {
2263
2465
  const errorMessage = getErrorMessage(error);
2264
2466
  if (options.json) {
2265
- logger2.json({
2266
- success: false,
2267
- error: errorMessage,
2268
- duration: (Date.now() - startTime) / 1e3
2269
- });
2467
+ const errorOutput = JSON.stringify(
2468
+ {
2469
+ success: false,
2470
+ error: errorMessage,
2471
+ duration: (Date.now() - startTime) / 1e3
2472
+ },
2473
+ null,
2474
+ 2
2475
+ );
2476
+ await writeResult(errorOutput + "\n", { output: options.output });
2270
2477
  } else {
2271
2478
  logger2.error(`Error: ${errorMessage}`);
2272
2479
  }
@@ -2281,28 +2488,33 @@ async function simulate(configOrPath, event, options = {}) {
2281
2488
  }
2282
2489
  return await simulateCore(configOrPath, event, {
2283
2490
  json: options.json ?? false,
2284
- verbose: options.verbose ?? false
2491
+ verbose: options.verbose ?? false,
2492
+ flow: options.flow,
2493
+ platform: options.platform
2285
2494
  });
2286
2495
  }
2287
2496
 
2288
2497
  // src/commands/push/index.ts
2289
- import path11 from "path";
2498
+ import path12 from "path";
2290
2499
  import { JSDOM as JSDOM2, VirtualConsole as VirtualConsole2 } from "jsdom";
2291
- import fs11 from "fs-extra";
2500
+ import fs12 from "fs-extra";
2292
2501
  import {
2293
2502
  getPlatform as getPlatform4
2294
2503
  } from "@walkeros/core";
2295
2504
  import { schemas as schemas2 } from "@walkeros/core/dev";
2296
- async function pushCommand(options) {
2297
- const logger2 = createCommandLogger(options);
2505
+ async function pushCore(inputPath, event, options = {}) {
2506
+ const logger2 = createCommandLogger({
2507
+ silent: options.silent,
2508
+ verbose: options.verbose
2509
+ });
2298
2510
  const startTime = Date.now();
2299
2511
  let tempDir;
2300
2512
  try {
2301
- logger2.debug("Loading event");
2302
- const event = await loadJsonFromSource(options.event, {
2303
- name: "event"
2304
- });
2305
- const eventResult = schemas2.PartialEventSchema.safeParse(event);
2513
+ let loadedEvent = event;
2514
+ if (typeof event === "string") {
2515
+ loadedEvent = await loadJsonFromSource(event, { name: "event" });
2516
+ }
2517
+ const eventResult = schemas2.PartialEventSchema.safeParse(loadedEvent);
2306
2518
  if (!eventResult.success) {
2307
2519
  const errors = eventResult.error.issues.map((issue) => `${String(issue.path.join("."))}: ${issue.message}`).join(", ");
2308
2520
  throw new Error(`Invalid event: ${errors}`);
@@ -2321,11 +2533,18 @@ async function pushCommand(options) {
2321
2533
  );
2322
2534
  }
2323
2535
  logger2.debug("Detecting input type");
2324
- const detected = await detectInput(options.config, options.platform);
2536
+ const detected = await detectInput(
2537
+ inputPath,
2538
+ options.platform
2539
+ );
2325
2540
  let result;
2326
2541
  if (detected.type === "config") {
2327
2542
  result = await executeConfigPush(
2328
- options,
2543
+ {
2544
+ config: inputPath,
2545
+ flow: options.flow,
2546
+ verbose: options.verbose
2547
+ },
2329
2548
  validatedEvent,
2330
2549
  logger2,
2331
2550
  (dir) => {
@@ -2348,55 +2567,104 @@ async function pushCommand(options) {
2348
2567
  { logger: collectorLoggerConfig }
2349
2568
  );
2350
2569
  }
2570
+ return result;
2571
+ } catch (error) {
2572
+ return {
2573
+ success: false,
2574
+ duration: Date.now() - startTime,
2575
+ error: getErrorMessage(error)
2576
+ };
2577
+ } finally {
2578
+ if (tempDir) {
2579
+ await fs12.remove(tempDir).catch(() => {
2580
+ });
2581
+ }
2582
+ }
2583
+ }
2584
+ async function pushCommand(options) {
2585
+ const logger2 = createCommandLogger({ ...options, stderr: true });
2586
+ const startTime = Date.now();
2587
+ try {
2588
+ let config;
2589
+ if (isStdinPiped() && !options.config) {
2590
+ const stdinContent = await readStdin();
2591
+ const tmpPath = path12.resolve(".tmp", "stdin-push.json");
2592
+ await fs12.ensureDir(path12.dirname(tmpPath));
2593
+ await fs12.writeFile(tmpPath, stdinContent, "utf-8");
2594
+ config = tmpPath;
2595
+ } else {
2596
+ config = options.config || "bundle.config.json";
2597
+ }
2598
+ const event = await loadJsonFromSource(options.event, { name: "event" });
2599
+ const result = await pushCore(config, event, {
2600
+ flow: options.flow,
2601
+ json: options.json,
2602
+ verbose: options.verbose,
2603
+ silent: options.silent,
2604
+ platform: options.platform
2605
+ });
2351
2606
  const duration = Date.now() - startTime;
2607
+ let output;
2352
2608
  if (options.json) {
2353
- logger2.json({
2354
- success: result.success,
2355
- event: result.elbResult,
2356
- duration
2357
- });
2609
+ output = JSON.stringify(
2610
+ {
2611
+ success: result.success,
2612
+ event: result.elbResult,
2613
+ duration
2614
+ },
2615
+ null,
2616
+ 2
2617
+ );
2358
2618
  } else {
2619
+ const lines = [];
2359
2620
  if (result.success) {
2360
- logger2.log("Event pushed successfully");
2621
+ lines.push("Event pushed successfully");
2361
2622
  if (result.elbResult && typeof result.elbResult === "object") {
2362
2623
  const pushResult = result.elbResult;
2363
- if ("id" in pushResult && pushResult.id) {
2364
- logger2.log(` Event ID: ${pushResult.id}`);
2365
- }
2366
- if ("entity" in pushResult && pushResult.entity) {
2367
- logger2.log(` Entity: ${pushResult.entity}`);
2368
- }
2369
- if ("action" in pushResult && pushResult.action) {
2370
- logger2.log(` Action: ${pushResult.action}`);
2371
- }
2624
+ if ("id" in pushResult && pushResult.id)
2625
+ lines.push(` Event ID: ${pushResult.id}`);
2626
+ if ("entity" in pushResult && pushResult.entity)
2627
+ lines.push(` Entity: ${pushResult.entity}`);
2628
+ if ("action" in pushResult && pushResult.action)
2629
+ lines.push(` Action: ${pushResult.action}`);
2372
2630
  }
2373
- logger2.log(` Duration: ${duration}ms`);
2631
+ lines.push(` Duration: ${duration}ms`);
2374
2632
  } else {
2375
- logger2.error(`Error: ${result.error}`);
2376
- process.exit(1);
2633
+ lines.push(`Error: ${result.error}`);
2377
2634
  }
2635
+ output = lines.join("\n");
2378
2636
  }
2379
- process.exit(0);
2637
+ await writeResult(output + "\n", { output: options.output });
2638
+ process.exit(result.success ? 0 : 1);
2380
2639
  } catch (error) {
2381
2640
  const duration = Date.now() - startTime;
2382
2641
  const errorMessage = getErrorMessage(error);
2383
2642
  if (options.json) {
2384
- logger2.json({
2385
- success: false,
2386
- error: errorMessage,
2387
- duration
2388
- });
2643
+ const errorOutput = JSON.stringify(
2644
+ { success: false, error: errorMessage, duration },
2645
+ null,
2646
+ 2
2647
+ );
2648
+ await writeResult(errorOutput + "\n", { output: options.output });
2389
2649
  } else {
2390
2650
  logger2.error(`Error: ${errorMessage}`);
2391
2651
  }
2392
2652
  process.exit(1);
2393
- } finally {
2394
- if (tempDir) {
2395
- await fs11.remove(tempDir).catch(() => {
2396
- });
2397
- }
2398
2653
  }
2399
2654
  }
2655
+ async function push(configOrPath, event, options = {}) {
2656
+ if (typeof configOrPath !== "string") {
2657
+ throw new Error(
2658
+ "push() currently only supports config file paths. Config object support will be added in a future version. Please provide a path to a configuration file."
2659
+ );
2660
+ }
2661
+ return await pushCore(configOrPath, event, {
2662
+ json: options.json ?? false,
2663
+ verbose: options.verbose ?? false,
2664
+ flow: options.flow,
2665
+ platform: options.platform
2666
+ });
2667
+ }
2400
2668
  async function executeConfigPush(options, validatedEvent, logger2, setTempDir) {
2401
2669
  logger2.debug("Loading flow configuration");
2402
2670
  const { flowConfig, buildOptions } = await loadFlowConfig(options.config, {
@@ -2406,14 +2674,14 @@ async function executeConfigPush(options, validatedEvent, logger2, setTempDir) {
2406
2674
  const platform = getPlatform4(flowConfig);
2407
2675
  logger2.debug("Bundling flow configuration");
2408
2676
  const configDir = buildOptions.configDir || process.cwd();
2409
- const tempDir = path11.join(
2677
+ const tempDir = path12.join(
2410
2678
  configDir,
2411
2679
  ".tmp",
2412
2680
  `push-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
2413
2681
  );
2414
2682
  setTempDir(tempDir);
2415
- await fs11.ensureDir(tempDir);
2416
- const tempPath = path11.join(
2683
+ await fs12.ensureDir(tempDir);
2684
+ const tempPath = path12.join(
2417
2685
  tempDir,
2418
2686
  `bundle.${platform === "web" ? "js" : "mjs"}`
2419
2687
  );
@@ -2446,18 +2714,18 @@ async function executeConfigPush(options, validatedEvent, logger2, setTempDir) {
2446
2714
  }
2447
2715
  }
2448
2716
  async function executeBundlePush(bundleContent, platform, validatedEvent, logger2, setTempDir, context = {}) {
2449
- const tempDir = path11.join(
2717
+ const tempDir = path12.join(
2450
2718
  process.cwd(),
2451
2719
  ".tmp",
2452
2720
  `push-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
2453
2721
  );
2454
2722
  setTempDir(tempDir);
2455
- await fs11.ensureDir(tempDir);
2456
- const tempPath = path11.join(
2723
+ await fs12.ensureDir(tempDir);
2724
+ const tempPath = path12.join(
2457
2725
  tempDir,
2458
2726
  `bundle.${platform === "server" ? "mjs" : "js"}`
2459
2727
  );
2460
- await fs11.writeFile(tempPath, bundleContent, "utf8");
2728
+ await fs12.writeFile(tempPath, bundleContent, "utf8");
2461
2729
  logger2.debug(`Bundle written to: ${tempPath}`);
2462
2730
  if (platform === "web") {
2463
2731
  logger2.debug("Executing in web environment (JSDOM)");
@@ -2479,7 +2747,7 @@ async function executeWebPush(bundlePath, event, logger2) {
2479
2747
  });
2480
2748
  const { window } = dom;
2481
2749
  logger2.debug("Loading bundle...");
2482
- const bundleCode = await fs11.readFile(bundlePath, "utf8");
2750
+ const bundleCode = await fs12.readFile(bundlePath, "utf8");
2483
2751
  window.eval(bundleCode);
2484
2752
  logger2.debug("Waiting for collector...");
2485
2753
  await waitForWindowProperty2(
@@ -2571,10 +2839,10 @@ function waitForWindowProperty2(window, prop, timeout = 5e3) {
2571
2839
  }
2572
2840
 
2573
2841
  // src/commands/run/index.ts
2574
- import path13 from "path";
2842
+ import path14 from "path";
2575
2843
 
2576
2844
  // src/commands/run/validators.ts
2577
- import { existsSync as existsSync2 } from "fs";
2845
+ import { existsSync as existsSync3 } from "fs";
2578
2846
 
2579
2847
  // src/schemas/primitives.ts
2580
2848
  import { z } from "@walkeros/core/dev";
@@ -2591,6 +2859,74 @@ var RunOptionsSchema = z2.object({
2591
2859
  flowName: z2.string().optional().describe("Specific flow name to run")
2592
2860
  });
2593
2861
 
2862
+ // src/schemas/validate.ts
2863
+ import { z as z3 } from "@walkeros/core/dev";
2864
+ var ValidationTypeSchema = z3.union([
2865
+ z3.enum(["event", "flow", "mapping"]),
2866
+ z3.string().regex(/^(destinations|sources|transformers)\.\w+$|^\w+$/)
2867
+ ]).describe(
2868
+ 'Validation type: "event", "flow", "mapping", or dot-notation path (e.g., "destinations.snowplow") to validate a specific entry against its package schema'
2869
+ );
2870
+ var ValidateOptionsSchema = z3.object({
2871
+ flow: z3.string().optional().describe("Flow name for multi-flow configs")
2872
+ });
2873
+ var ValidateInputShape = {
2874
+ type: ValidationTypeSchema,
2875
+ input: z3.string().min(1).describe("JSON string, file path, or URL to validate"),
2876
+ flow: z3.string().optional().describe("Flow name for multi-flow configs")
2877
+ };
2878
+ var ValidateInputSchema = z3.object(ValidateInputShape);
2879
+
2880
+ // src/schemas/bundle.ts
2881
+ import { z as z4 } from "@walkeros/core/dev";
2882
+ var BundleOptionsSchema = z4.object({
2883
+ silent: z4.boolean().optional().describe("Suppress all output"),
2884
+ verbose: z4.boolean().optional().describe("Enable verbose logging"),
2885
+ stats: z4.boolean().optional().default(true).describe("Return bundle statistics"),
2886
+ cache: z4.boolean().optional().default(true).describe("Enable package caching"),
2887
+ flowName: z4.string().optional().describe("Flow name for multi-flow configs")
2888
+ });
2889
+ var BundleInputShape = {
2890
+ configPath: FilePathSchema.describe(
2891
+ "Path to flow configuration file (JSON or JavaScript)"
2892
+ ),
2893
+ flow: z4.string().optional().describe("Flow name for multi-flow configs"),
2894
+ stats: z4.boolean().optional().default(true).describe("Return bundle statistics"),
2895
+ output: z4.string().optional().describe("Output file path (defaults to config-defined)")
2896
+ };
2897
+ var BundleInputSchema = z4.object(BundleInputShape);
2898
+
2899
+ // src/schemas/simulate.ts
2900
+ import { z as z5 } from "@walkeros/core/dev";
2901
+ var PlatformSchema = z5.enum(["web", "server"]).describe("Platform type for event processing");
2902
+ var SimulateOptionsSchema = z5.object({
2903
+ silent: z5.boolean().optional().describe("Suppress all output"),
2904
+ verbose: z5.boolean().optional().describe("Enable verbose logging"),
2905
+ json: z5.boolean().optional().describe("Format output as JSON")
2906
+ });
2907
+ var SimulateInputShape = {
2908
+ configPath: FilePathSchema.describe("Path to flow configuration file"),
2909
+ event: z5.string().min(1).describe("Event as JSON string, file path, or URL"),
2910
+ flow: z5.string().optional().describe("Flow name for multi-flow configs"),
2911
+ platform: PlatformSchema.optional().describe("Override platform detection")
2912
+ };
2913
+ var SimulateInputSchema = z5.object(SimulateInputShape);
2914
+
2915
+ // src/schemas/push.ts
2916
+ import { z as z6 } from "@walkeros/core/dev";
2917
+ var PushOptionsSchema = z6.object({
2918
+ silent: z6.boolean().optional().describe("Suppress all output"),
2919
+ verbose: z6.boolean().optional().describe("Enable verbose logging"),
2920
+ json: z6.boolean().optional().describe("Format output as JSON")
2921
+ });
2922
+ var PushInputShape = {
2923
+ configPath: FilePathSchema.describe("Path to flow configuration file"),
2924
+ event: z6.string().min(1).describe("Event as JSON string, file path, or URL"),
2925
+ flow: z6.string().optional().describe("Flow name for multi-flow configs"),
2926
+ platform: PlatformSchema.optional().describe("Override platform detection")
2927
+ };
2928
+ var PushInputSchema = z6.object(PushInputShape);
2929
+
2594
2930
  // src/commands/run/validators.ts
2595
2931
  function validateMode(mode) {
2596
2932
  const result = RunModeSchema.safeParse(mode);
@@ -2604,7 +2940,7 @@ function validateMode(mode) {
2604
2940
  }
2605
2941
  function validateFlowFile(filePath) {
2606
2942
  const absolutePath = resolveAsset(filePath, "bundle");
2607
- if (!existsSync2(absolutePath)) {
2943
+ if (!existsSync3(absolutePath)) {
2608
2944
  throw new Error(
2609
2945
  `Flow file not found: ${filePath}
2610
2946
  Resolved path: ${absolutePath}
@@ -2625,17 +2961,17 @@ function validatePort(port) {
2625
2961
  }
2626
2962
 
2627
2963
  // src/commands/run/utils.ts
2628
- import path12 from "path";
2629
- import fs12 from "fs-extra";
2964
+ import path13 from "path";
2965
+ import fs13 from "fs-extra";
2630
2966
  async function prepareBundleForRun(configPath, options) {
2631
- const configDir = path12.dirname(path12.resolve(configPath));
2632
- const tempDir = path12.join(
2967
+ const configDir = path13.dirname(path13.resolve(configPath));
2968
+ const tempDir = path13.join(
2633
2969
  configDir,
2634
2970
  ".tmp",
2635
2971
  `run-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
2636
2972
  );
2637
- await fs12.ensureDir(tempDir);
2638
- const tempPath = path12.join(tempDir, "bundle.mjs");
2973
+ await fs13.ensureDir(tempDir);
2974
+ const tempPath = path13.join(tempDir, "bundle.mjs");
2639
2975
  await bundle(configPath, {
2640
2976
  cache: true,
2641
2977
  verbose: options.verbose,
@@ -2826,8 +3162,8 @@ async function runCommand(mode, options) {
2826
3162
  let flowPath = null;
2827
3163
  if (mode === "collect") {
2828
3164
  if (isPreBuilt) {
2829
- flowPath = path13.resolve(configPath);
2830
- logger2.debug(`Using pre-built flow: ${path13.basename(flowPath)}`);
3165
+ flowPath = path14.resolve(configPath);
3166
+ logger2.debug(`Using pre-built flow: ${path14.basename(flowPath)}`);
2831
3167
  } else {
2832
3168
  logger2.debug("Building flow bundle");
2833
3169
  flowPath = await prepareBundleForRun(configPath, {
@@ -2877,7 +3213,7 @@ async function run(mode, options) {
2877
3213
  const isPreBuilt = isPreBuiltConfig(flowFile);
2878
3214
  let flowPath;
2879
3215
  if (isPreBuilt) {
2880
- flowPath = path13.resolve(flowFile);
3216
+ flowPath = path14.resolve(flowFile);
2881
3217
  } else {
2882
3218
  flowPath = await prepareBundleForRun(flowFile, {
2883
3219
  verbose: options.verbose,
@@ -2906,7 +3242,7 @@ async function run(mode, options) {
2906
3242
  }
2907
3243
 
2908
3244
  // src/commands/validate/index.ts
2909
- import chalk2 from "chalk";
3245
+ import chalk3 from "chalk";
2910
3246
 
2911
3247
  // src/commands/validate/validators/event.ts
2912
3248
  import { schemas as schemas3 } from "@walkeros/core/dev";
@@ -2951,10 +3287,10 @@ function validateEvent(input) {
2951
3287
  const zodResult = PartialEventSchema.safeParse(input);
2952
3288
  if (!zodResult.success) {
2953
3289
  for (const issue of zodResult.error.issues) {
2954
- const path14 = issue.path.join(".");
2955
- if (path14 === "name") continue;
3290
+ const path15 = issue.path.join(".");
3291
+ if (path15 === "name") continue;
2956
3292
  errors.push({
2957
- path: path14 || "root",
3293
+ path: path15 || "root",
2958
3294
  message: issue.message,
2959
3295
  code: "SCHEMA_VALIDATION"
2960
3296
  });
@@ -2990,9 +3326,9 @@ function validateFlow(input, options = {}) {
2990
3326
  const zodResult = SetupSchema.safeParse(input);
2991
3327
  if (!zodResult.success) {
2992
3328
  for (const issue of zodResult.error.issues) {
2993
- const path14 = issue.path.join(".");
3329
+ const path15 = issue.path.join(".");
2994
3330
  errors.push({
2995
- path: path14 || "root",
3331
+ path: path15 || "root",
2996
3332
  message: issue.message,
2997
3333
  code: "SCHEMA_VALIDATION"
2998
3334
  });
@@ -3097,8 +3433,144 @@ function validateMapping(input) {
3097
3433
  };
3098
3434
  }
3099
3435
 
3436
+ // src/commands/validate/validators/entry.ts
3437
+ import Ajv from "ajv";
3438
+ import { fetchPackageSchema } from "@walkeros/core";
3439
+ var SECTIONS = ["destinations", "sources", "transformers"];
3440
+ function resolveEntry(path15, flowConfig) {
3441
+ const flows = flowConfig.flows;
3442
+ if (!flows || typeof flows !== "object") return "No flows found in config";
3443
+ const flowName = Object.keys(flows)[0];
3444
+ const flow = flows[flowName];
3445
+ if (!flow) return `Flow "${flowName}" is empty`;
3446
+ const parts = path15.split(".");
3447
+ if (parts.length === 2) {
3448
+ const [section, key] = parts;
3449
+ if (!SECTIONS.includes(section)) {
3450
+ return `Unknown section "${section}". Must be one of: ${SECTIONS.join(", ")}`;
3451
+ }
3452
+ const sectionData = flow[section];
3453
+ if (!sectionData || !(key in sectionData)) {
3454
+ return `Entry "${key}" not found in ${section}`;
3455
+ }
3456
+ return {
3457
+ section,
3458
+ key,
3459
+ entry: sectionData[key]
3460
+ };
3461
+ }
3462
+ if (parts.length === 1) {
3463
+ const key = parts[0];
3464
+ const matches = [];
3465
+ for (const section of SECTIONS) {
3466
+ const sectionData = flow[section];
3467
+ if (sectionData && key in sectionData) {
3468
+ matches.push({
3469
+ section,
3470
+ entry: sectionData[key]
3471
+ });
3472
+ }
3473
+ }
3474
+ if (matches.length === 0) {
3475
+ return `Entry "${key}" not found in any section`;
3476
+ }
3477
+ if (matches.length > 1) {
3478
+ const sections = matches.map((m) => m.section).join(", ");
3479
+ return `Ambiguous key "${key}" found in multiple sections: ${sections}. Use dot-notation (e.g., destinations.${key})`;
3480
+ }
3481
+ return { section: matches[0].section, key, entry: matches[0].entry };
3482
+ }
3483
+ return `Invalid path "${path15}". Use "section.key" or just "key"`;
3484
+ }
3485
+ async function validateEntry(path15, flowConfig) {
3486
+ const resolved = resolveEntry(path15, flowConfig);
3487
+ if (typeof resolved === "string") {
3488
+ return {
3489
+ valid: false,
3490
+ type: "entry",
3491
+ errors: [{ path: path15, message: resolved, code: "ENTRY_VALIDATION" }],
3492
+ warnings: [],
3493
+ details: {}
3494
+ };
3495
+ }
3496
+ const { section, key, entry } = resolved;
3497
+ const packageName = entry.package;
3498
+ if (!packageName) {
3499
+ return {
3500
+ valid: true,
3501
+ type: "entry",
3502
+ errors: [],
3503
+ warnings: [],
3504
+ details: {
3505
+ section,
3506
+ key,
3507
+ skipped: true,
3508
+ reason: "No package field \u2014 skipping remote schema validation"
3509
+ }
3510
+ };
3511
+ }
3512
+ let schemas5;
3513
+ try {
3514
+ const info = await fetchPackageSchema(packageName);
3515
+ schemas5 = info.schemas;
3516
+ } catch (error) {
3517
+ return {
3518
+ valid: false,
3519
+ type: "entry",
3520
+ errors: [
3521
+ {
3522
+ path: path15,
3523
+ message: error instanceof Error ? error.message : "Unknown error",
3524
+ code: "ENTRY_VALIDATION"
3525
+ }
3526
+ ],
3527
+ warnings: [],
3528
+ details: { section, key, package: packageName }
3529
+ };
3530
+ }
3531
+ const settingsSchema = schemas5?.settings;
3532
+ if (!settingsSchema) {
3533
+ return {
3534
+ valid: true,
3535
+ type: "entry",
3536
+ errors: [],
3537
+ warnings: [],
3538
+ details: { section, key, note: "Package has no settings schema" }
3539
+ };
3540
+ }
3541
+ const config = entry.config;
3542
+ const settings = config?.settings;
3543
+ const ajv = new Ajv({ allErrors: true });
3544
+ const validate2 = ajv.compile(settingsSchema);
3545
+ const isValid = validate2(settings || {});
3546
+ if (!isValid) {
3547
+ const errors = (validate2.errors || []).map((e) => ({
3548
+ path: e.instancePath || "/",
3549
+ message: e.message || "Unknown error",
3550
+ code: e.keyword
3551
+ }));
3552
+ return {
3553
+ valid: false,
3554
+ type: "entry",
3555
+ errors,
3556
+ warnings: [],
3557
+ details: { section, key, package: packageName }
3558
+ };
3559
+ }
3560
+ return {
3561
+ valid: true,
3562
+ type: "entry",
3563
+ errors: [],
3564
+ warnings: [],
3565
+ details: { section, key, package: packageName }
3566
+ };
3567
+ }
3568
+
3100
3569
  // src/commands/validate/index.ts
3101
3570
  async function validate(type, input, options = {}) {
3571
+ if (type.includes(".") || !["event", "flow", "mapping"].includes(type)) {
3572
+ return validateEntry(type, input);
3573
+ }
3102
3574
  switch (type) {
3103
3575
  case "event":
3104
3576
  return validateEvent(input);
@@ -3127,16 +3599,16 @@ function formatResult(result, options) {
3127
3599
  }
3128
3600
  lines.push("Validation Results:");
3129
3601
  for (const error of result.errors) {
3130
- lines.push(chalk2.red(` \u2717 ${error.path}: ${error.message}`));
3602
+ lines.push(chalk3.red(` \u2717 ${error.path}: ${error.message}`));
3131
3603
  }
3132
3604
  for (const warning of result.warnings) {
3133
- lines.push(chalk2.yellow(` \u26A0 ${warning.path}: ${warning.message}`));
3605
+ lines.push(chalk3.yellow(` \u26A0 ${warning.path}: ${warning.message}`));
3134
3606
  if (warning.suggestion) {
3135
- lines.push(chalk2.gray(` \u2192 ${warning.suggestion}`));
3607
+ lines.push(chalk3.gray(` \u2192 ${warning.suggestion}`));
3136
3608
  }
3137
3609
  }
3138
3610
  if (result.valid) {
3139
- lines.push(chalk2.green(` \u2713 All checks passed`));
3611
+ lines.push(chalk3.green(` \u2713 All checks passed`));
3140
3612
  }
3141
3613
  lines.push("");
3142
3614
  lines.push(
@@ -3145,24 +3617,30 @@ function formatResult(result, options) {
3145
3617
  return lines.join("\n");
3146
3618
  }
3147
3619
  async function validateCommand(options) {
3148
- const logger2 = createCommandLogger(options);
3620
+ const logger2 = createCommandLogger({ ...options, stderr: true });
3149
3621
  try {
3150
- const input = await loadJsonFromSource(options.input, {
3151
- name: options.type,
3152
- required: true
3153
- });
3622
+ let input;
3623
+ if (isStdinPiped() && !options.input) {
3624
+ const stdinContent = await readStdin();
3625
+ try {
3626
+ input = JSON.parse(stdinContent);
3627
+ } catch {
3628
+ throw new Error("Invalid JSON received on stdin");
3629
+ }
3630
+ } else {
3631
+ input = await loadJsonFromSource(options.input, {
3632
+ name: options.type,
3633
+ required: true
3634
+ });
3635
+ }
3154
3636
  const result = await validate(options.type, input, {
3155
3637
  flow: options.flow
3156
3638
  });
3157
- const output = formatResult(result, {
3639
+ const formatted = formatResult(result, {
3158
3640
  json: options.json,
3159
3641
  verbose: options.verbose
3160
3642
  });
3161
- if (options.json) {
3162
- console.log(output);
3163
- } else {
3164
- logger2.log(output);
3165
- }
3643
+ await writeResult(formatted + "\n", { output: options.output });
3166
3644
  if (!result.valid) {
3167
3645
  process.exit(1);
3168
3646
  }
@@ -3173,13 +3651,20 @@ async function validateCommand(options) {
3173
3651
  } catch (error) {
3174
3652
  const errorMessage = getErrorMessage(error);
3175
3653
  if (options.json) {
3176
- logger2.json({
3177
- valid: false,
3178
- type: options.type,
3179
- errors: [{ path: "input", message: errorMessage, code: "INPUT_ERROR" }],
3180
- warnings: [],
3181
- details: {}
3182
- });
3654
+ const errorOutput = JSON.stringify(
3655
+ {
3656
+ valid: false,
3657
+ type: options.type,
3658
+ errors: [
3659
+ { path: "input", message: errorMessage, code: "INPUT_ERROR" }
3660
+ ],
3661
+ warnings: [],
3662
+ details: {}
3663
+ },
3664
+ null,
3665
+ 2
3666
+ );
3667
+ await writeResult(errorOutput + "\n", { output: options.output });
3183
3668
  } else {
3184
3669
  logger2.error(`Error: ${errorMessage}`);
3185
3670
  }
@@ -3188,21 +3673,22 @@ async function validateCommand(options) {
3188
3673
  }
3189
3674
 
3190
3675
  // src/commands/cache.ts
3191
- import fs13 from "fs-extra";
3676
+ import fs14 from "fs-extra";
3192
3677
  function registerCacheCommand(program2) {
3193
3678
  const cache = program2.command("cache").description("Manage the CLI cache");
3194
3679
  cache.command("clear").description("Clear all cached packages and builds").option("--packages", "Clear only package cache").option("--builds", "Clear only build cache").option("--tmp-dir <dir>", "Custom temp directory").option("--silent", "Suppress output").action(async (options) => {
3195
3680
  const logger2 = createLogger({ silent: options.silent });
3196
3681
  const tmpDir = options.tmpDir;
3197
3682
  if (options.packages) {
3198
- await fs13.remove(getTmpPath(tmpDir, "cache", "packages"));
3683
+ await fs14.remove(getTmpPath(tmpDir, "cache", "packages"));
3199
3684
  logger2.log("Package cache cleared");
3200
3685
  } else if (options.builds) {
3201
- await fs13.remove(getTmpPath(tmpDir, "cache", "builds"));
3686
+ await fs14.remove(getTmpPath(tmpDir, "cache", "builds"));
3202
3687
  logger2.log("Build cache cleared");
3203
3688
  } else {
3204
- await fs13.remove(getTmpPath(tmpDir, "cache"));
3205
- logger2.log("All caches cleared");
3689
+ const cacheDir = getTmpPath(tmpDir, "cache");
3690
+ await fs14.remove(cacheDir);
3691
+ logger2.log(`Cache cleared: ${cacheDir}`);
3206
3692
  }
3207
3693
  });
3208
3694
  cache.command("info").description("Show cache statistics").option("--tmp-dir <dir>", "Custom temp directory").option("--silent", "Suppress output").action(async (options) => {
@@ -3217,35 +3703,395 @@ function registerCacheCommand(program2) {
3217
3703
  logger2.log(`Cached builds: ${buildCount}`);
3218
3704
  });
3219
3705
  }
3220
- function registerCleanCommand(program2) {
3221
- program2.command("clean").description("Clear the entire temp directory (.tmp/)").option("--tmp-dir <dir>", "Custom temp directory").option("--silent", "Suppress output").action(async (options) => {
3222
- const logger2 = createLogger({ silent: options.silent });
3223
- const tmpDir = options.tmpDir || getDefaultTmpRoot();
3224
- await fs13.remove(tmpDir);
3225
- logger2.log(`Temp directory cleared: ${tmpDir}`);
3226
- });
3227
- }
3228
3706
  async function countEntries(dir) {
3229
- if (!await fs13.pathExists(dir)) return 0;
3230
- const entries = await fs13.readdir(dir);
3707
+ if (!await fs14.pathExists(dir)) return 0;
3708
+ const entries = await fs14.readdir(dir);
3231
3709
  return entries.length;
3232
3710
  }
3233
3711
 
3712
+ // src/commands/login/index.ts
3713
+ import { hostname } from "os";
3714
+ var POLL_TIMEOUT_BUFFER_MS = 5e3;
3715
+ async function openInBrowser(url) {
3716
+ const { default: open } = await import("open");
3717
+ await open(url);
3718
+ }
3719
+ async function loginCommand(options) {
3720
+ const logger2 = createLogger({
3721
+ verbose: options.verbose,
3722
+ silent: options.silent,
3723
+ json: options.json
3724
+ });
3725
+ try {
3726
+ const result = await login({ url: options.url });
3727
+ if (options.json) {
3728
+ console.log(JSON.stringify(result, null, 2));
3729
+ } else if (result.success) {
3730
+ logger2.success(`Logged in as ${result.email}`);
3731
+ logger2.log(`Token stored in ${result.configPath}`);
3732
+ }
3733
+ process.exit(result.success ? 0 : 1);
3734
+ } catch (error) {
3735
+ const message = error instanceof Error ? error.message : String(error);
3736
+ if (options.json) {
3737
+ console.log(JSON.stringify({ success: false, error: message }, null, 2));
3738
+ } else {
3739
+ logger2.error(message);
3740
+ }
3741
+ process.exit(1);
3742
+ }
3743
+ }
3744
+ async function login(options = {}) {
3745
+ const appUrl = options.url || resolveAppUrl();
3746
+ const f = options.fetch ?? globalThis.fetch;
3747
+ const codeResponse = await f(`${appUrl}/api/auth/device/code`, {
3748
+ method: "POST",
3749
+ headers: { "Content-Type": "application/json" },
3750
+ body: JSON.stringify({})
3751
+ });
3752
+ if (!codeResponse.ok) {
3753
+ return { success: false, error: "Failed to request device code" };
3754
+ }
3755
+ const { deviceCode, userCode, verificationUri, expiresIn, interval } = await codeResponse.json();
3756
+ console.error(`
3757
+ ! Your one-time code: ${userCode}`);
3758
+ console.error(` Authorize here: ${verificationUri}
3759
+ `);
3760
+ const opener = options.openUrl ?? openInBrowser;
3761
+ try {
3762
+ await opener(verificationUri);
3763
+ console.error(" Opening browser...");
3764
+ } catch {
3765
+ console.error(" Could not open browser. Visit the URL manually.");
3766
+ }
3767
+ console.error(" Waiting for authorization... (press Ctrl+C to cancel)\n");
3768
+ const deadline = Date.now() + expiresIn * 1e3 + POLL_TIMEOUT_BUFFER_MS;
3769
+ let pollInterval = (interval ?? 5) * 1e3;
3770
+ const maxAttempts = options.maxPollAttempts ?? Infinity;
3771
+ let attempts = 0;
3772
+ while (Date.now() < deadline && attempts < maxAttempts) {
3773
+ attempts++;
3774
+ await new Promise((r) => setTimeout(r, pollInterval));
3775
+ const tokenResponse = await f(`${appUrl}/api/auth/device/token`, {
3776
+ method: "POST",
3777
+ headers: { "Content-Type": "application/json" },
3778
+ body: JSON.stringify({ deviceCode, hostname: hostname() })
3779
+ });
3780
+ const data = await tokenResponse.json();
3781
+ if (tokenResponse.ok && data.token) {
3782
+ writeConfig({ token: data.token, email: data.email, appUrl });
3783
+ const configPath = getConfigPath();
3784
+ return { success: true, email: data.email, configPath };
3785
+ }
3786
+ if (data.error === "authorization_pending") continue;
3787
+ if (data.error === "slow_down") {
3788
+ pollInterval += 5e3;
3789
+ continue;
3790
+ }
3791
+ return { success: false, error: data.error || "Authorization failed" };
3792
+ }
3793
+ return {
3794
+ success: false,
3795
+ error: "Authorization timed out. Please try again."
3796
+ };
3797
+ }
3798
+
3799
+ // src/commands/logout/index.ts
3800
+ async function logoutCommand(options) {
3801
+ const logger2 = createLogger({
3802
+ verbose: options.verbose,
3803
+ silent: options.silent,
3804
+ json: options.json
3805
+ });
3806
+ const deleted = deleteConfig();
3807
+ const configPath = getConfigPath();
3808
+ if (options.json) {
3809
+ console.log(JSON.stringify({ success: true, deleted }));
3810
+ } else if (deleted) {
3811
+ logger2.success(`Logged out. Token removed from ${configPath}`);
3812
+ } else {
3813
+ logger2.log("No stored credentials found.");
3814
+ }
3815
+ process.exit(0);
3816
+ }
3817
+
3818
+ // src/commands/auth/index.ts
3819
+ async function whoami() {
3820
+ const client = createApiClient();
3821
+ const { data, error } = await client.GET("/api/auth/whoami");
3822
+ if (error) throw new Error(error.error?.message || "Not authenticated");
3823
+ return data;
3824
+ }
3825
+ async function whoamiCommand(options) {
3826
+ const logger2 = createCommandLogger(options);
3827
+ try {
3828
+ const result = await whoami();
3829
+ if (options.json) {
3830
+ await writeResult(JSON.stringify(result, null, 2), options);
3831
+ } else {
3832
+ const data = result;
3833
+ if (data.email) logger2.log(`${data.email}`);
3834
+ if (data.userId) logger2.log(`User: ${data.userId}`);
3835
+ if (data.projectId) logger2.log(`Project: ${data.projectId}`);
3836
+ }
3837
+ } catch (error) {
3838
+ logger2.error(error instanceof Error ? error.message : String(error));
3839
+ process.exit(1);
3840
+ }
3841
+ }
3842
+
3843
+ // src/commands/projects/index.ts
3844
+ async function listProjects() {
3845
+ const client = createApiClient();
3846
+ const { data, error } = await client.GET("/api/projects");
3847
+ if (error) throw new Error(error.error?.message || "Failed to list projects");
3848
+ return data;
3849
+ }
3850
+ async function getProject(options = {}) {
3851
+ const id = options.projectId ?? requireProjectId();
3852
+ const client = createApiClient();
3853
+ const { data, error } = await client.GET("/api/projects/{projectId}", {
3854
+ params: { path: { projectId: id } }
3855
+ });
3856
+ if (error) throw new Error(error.error?.message || "Failed to get project");
3857
+ return data;
3858
+ }
3859
+ async function createProject(options) {
3860
+ const client = createApiClient();
3861
+ const { data, error } = await client.POST("/api/projects", {
3862
+ body: { name: options.name }
3863
+ });
3864
+ if (error)
3865
+ throw new Error(error.error?.message || "Failed to create project");
3866
+ return data;
3867
+ }
3868
+ async function updateProject(options) {
3869
+ const id = options.projectId ?? requireProjectId();
3870
+ const client = createApiClient();
3871
+ const { data, error } = await client.PATCH("/api/projects/{projectId}", {
3872
+ params: { path: { projectId: id } },
3873
+ body: { name: options.name }
3874
+ });
3875
+ if (error)
3876
+ throw new Error(error.error?.message || "Failed to update project");
3877
+ return data;
3878
+ }
3879
+ async function deleteProject(options = {}) {
3880
+ const id = options.projectId ?? requireProjectId();
3881
+ const client = createApiClient();
3882
+ const { data, error } = await client.DELETE("/api/projects/{projectId}", {
3883
+ params: { path: { projectId: id } }
3884
+ });
3885
+ if (error)
3886
+ throw new Error(error.error?.message || "Failed to delete project");
3887
+ return data ?? { success: true };
3888
+ }
3889
+ async function handleResult(fn, options) {
3890
+ const logger2 = createCommandLogger(options);
3891
+ try {
3892
+ const result = await fn();
3893
+ await writeResult(JSON.stringify(result, null, 2), options);
3894
+ } catch (error) {
3895
+ logger2.error(error instanceof Error ? error.message : String(error));
3896
+ process.exit(1);
3897
+ }
3898
+ }
3899
+ async function listProjectsCommand(options) {
3900
+ await handleResult(() => listProjects(), options);
3901
+ }
3902
+ async function getProjectCommand(projectId, options) {
3903
+ await handleResult(
3904
+ () => getProject({ projectId: projectId ?? options.project }),
3905
+ options
3906
+ );
3907
+ }
3908
+ async function createProjectCommand(name, options) {
3909
+ await handleResult(() => createProject({ name }), options);
3910
+ }
3911
+ async function updateProjectCommand(projectId, options) {
3912
+ const name = options.name;
3913
+ if (!name) {
3914
+ throw new Error("Missing required option: --name <name>");
3915
+ }
3916
+ await handleResult(
3917
+ () => updateProject({
3918
+ projectId: projectId ?? options.project,
3919
+ name
3920
+ }),
3921
+ options
3922
+ );
3923
+ }
3924
+ async function deleteProjectCommand(projectId, options) {
3925
+ await handleResult(
3926
+ () => deleteProject({ projectId: projectId ?? options.project }),
3927
+ options
3928
+ );
3929
+ }
3930
+
3931
+ // src/commands/flows/index.ts
3932
+ async function listFlows(options = {}) {
3933
+ const id = options.projectId ?? requireProjectId();
3934
+ const client = createApiClient();
3935
+ const { data, error } = await client.GET("/api/projects/{projectId}/flows", {
3936
+ params: {
3937
+ path: { projectId: id },
3938
+ query: {
3939
+ sort: options.sort,
3940
+ order: options.order,
3941
+ include_deleted: options.includeDeleted ? "true" : void 0
3942
+ }
3943
+ }
3944
+ });
3945
+ if (error) throw new Error(error.error?.message || "Failed to list flows");
3946
+ return data;
3947
+ }
3948
+ async function getFlow(options) {
3949
+ const id = options.projectId ?? requireProjectId();
3950
+ const client = createApiClient();
3951
+ const { data, error } = await client.GET(
3952
+ "/api/projects/{projectId}/flows/{flowId}",
3953
+ {
3954
+ params: { path: { projectId: id, flowId: options.flowId } }
3955
+ }
3956
+ );
3957
+ if (error) throw new Error(error.error?.message || "Failed to get flow");
3958
+ return data;
3959
+ }
3960
+ async function createFlow(options) {
3961
+ const id = options.projectId ?? requireProjectId();
3962
+ const client = createApiClient();
3963
+ const { data, error } = await client.POST("/api/projects/{projectId}/flows", {
3964
+ params: { path: { projectId: id } },
3965
+ // Content is user-provided JSON; server validates the full schema
3966
+ body: { name: options.name, content: options.content }
3967
+ });
3968
+ if (error) throw new Error(error.error?.message || "Failed to create flow");
3969
+ return data;
3970
+ }
3971
+ async function updateFlow(options) {
3972
+ const id = options.projectId ?? requireProjectId();
3973
+ const client = createApiClient();
3974
+ const body = {};
3975
+ if (options.name !== void 0) body.name = options.name;
3976
+ if (options.content !== void 0) body.content = options.content;
3977
+ const { data, error } = await client.PATCH(
3978
+ "/api/projects/{projectId}/flows/{flowId}",
3979
+ {
3980
+ params: { path: { projectId: id, flowId: options.flowId } },
3981
+ // Dynamically constructed body; server validates the full schema
3982
+ body
3983
+ }
3984
+ );
3985
+ if (error) throw new Error(error.error?.message || "Failed to update flow");
3986
+ return data;
3987
+ }
3988
+ async function deleteFlow(options) {
3989
+ const id = options.projectId ?? requireProjectId();
3990
+ const client = createApiClient();
3991
+ const { data, error } = await client.DELETE(
3992
+ "/api/projects/{projectId}/flows/{flowId}",
3993
+ {
3994
+ params: { path: { projectId: id, flowId: options.flowId } }
3995
+ }
3996
+ );
3997
+ if (error) throw new Error(error.error?.message || "Failed to delete flow");
3998
+ return data ?? { success: true };
3999
+ }
4000
+ async function duplicateFlow(options) {
4001
+ const id = options.projectId ?? requireProjectId();
4002
+ const client = createApiClient();
4003
+ const { data, error } = await client.POST(
4004
+ "/api/projects/{projectId}/flows/{flowId}/duplicate",
4005
+ {
4006
+ params: { path: { projectId: id, flowId: options.flowId } },
4007
+ body: { name: options.name }
4008
+ }
4009
+ );
4010
+ if (error)
4011
+ throw new Error(error.error?.message || "Failed to duplicate flow");
4012
+ return data;
4013
+ }
4014
+ async function handleResult2(fn, options) {
4015
+ const logger2 = createCommandLogger(options);
4016
+ try {
4017
+ const result = await fn();
4018
+ await writeResult(JSON.stringify(result, null, 2), options);
4019
+ } catch (error) {
4020
+ logger2.error(error instanceof Error ? error.message : String(error));
4021
+ process.exit(1);
4022
+ }
4023
+ }
4024
+ async function listFlowsCommand(options) {
4025
+ await handleResult2(
4026
+ () => listFlows({
4027
+ projectId: options.project,
4028
+ sort: options.sort,
4029
+ order: options.order,
4030
+ includeDeleted: options.includeDeleted
4031
+ }),
4032
+ options
4033
+ );
4034
+ }
4035
+ async function getFlowCommand(flowId, options) {
4036
+ await handleResult2(
4037
+ () => getFlow({ flowId, projectId: options.project }),
4038
+ options
4039
+ );
4040
+ }
4041
+ async function createFlowCommand(name, options) {
4042
+ const content = options.content ? JSON.parse(options.content) : JSON.parse(await readFlowStdin());
4043
+ await handleResult2(
4044
+ () => createFlow({ name, content, projectId: options.project }),
4045
+ options
4046
+ );
4047
+ }
4048
+ async function updateFlowCommand(flowId, options) {
4049
+ const content = options.content ? JSON.parse(options.content) : void 0;
4050
+ await handleResult2(
4051
+ () => updateFlow({
4052
+ flowId,
4053
+ name: options.name,
4054
+ content,
4055
+ projectId: options.project
4056
+ }),
4057
+ options
4058
+ );
4059
+ }
4060
+ async function deleteFlowCommand(flowId, options) {
4061
+ await handleResult2(
4062
+ () => deleteFlow({ flowId, projectId: options.project }),
4063
+ options
4064
+ );
4065
+ }
4066
+ async function duplicateFlowCommand(flowId, options) {
4067
+ await handleResult2(
4068
+ () => duplicateFlow({ flowId, name: options.name, projectId: options.project }),
4069
+ options
4070
+ );
4071
+ }
4072
+ async function readFlowStdin() {
4073
+ if (!isStdinPiped()) {
4074
+ throw new Error("Content required: use --content or pipe via stdin");
4075
+ }
4076
+ return readStdin();
4077
+ }
4078
+
3234
4079
  // src/index.ts
3235
4080
  var program = new Command();
3236
4081
  program.name("walkeros").description("walkerOS CLI - Bundle and deploy walkerOS components").version(VERSION);
3237
4082
  program.hook("preAction", (thisCommand, actionCommand) => {
3238
4083
  const options = actionCommand.opts();
3239
- if (!options.silent && !options.json) {
3240
- console.log(`${chalk3.hex("#01b5e2")("walkerOS")} v${VERSION}`);
4084
+ if (!options.silent && !options.json && process.stdout.isTTY) {
4085
+ printBanner(VERSION);
3241
4086
  }
3242
4087
  });
3243
- program.command("bundle [file]").description("Bundle NPM packages with custom code").option("--flow <name>", "flow name for multi-flow configs").option("--all", "build all flows for multi-flow configs").option("--stats", "show bundle statistics").option("--json", "output as JSON (implies --stats)").option("--no-cache", "disable package caching").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").option(
4088
+ program.command("bundle [file]").description("Bundle NPM packages with custom code").option("-o, --output <path>", "write bundle to file or directory").option("--flow <name>", "flow name for multi-flow configs").option("--all", "build all flows for multi-flow configs").option("--stats", "show bundle statistics").option("--json", "output as JSON (implies --stats)").option("--no-cache", "disable package caching").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").option(
3244
4089
  "--dockerfile [file]",
3245
4090
  "generate Dockerfile (or copy custom file) to dist/"
3246
4091
  ).action(async (file, options) => {
3247
4092
  await bundleCommand({
3248
- config: file || "bundle.config.json",
4093
+ config: file,
4094
+ output: options.output,
3249
4095
  flow: options.flow,
3250
4096
  all: options.all,
3251
4097
  stats: options.stats,
@@ -3256,12 +4102,13 @@ program.command("bundle [file]").description("Bundle NPM packages with custom co
3256
4102
  dockerfile: options.dockerfile
3257
4103
  });
3258
4104
  });
3259
- program.command("simulate [file]").description("Simulate event processing and capture API calls").option(
4105
+ program.command("simulate [file]").description("Simulate event processing and capture API calls").option("-o, --output <path>", "write result to file").option(
3260
4106
  "-e, --event <source>",
3261
4107
  "event to simulate (JSON string, file path, or URL)"
3262
4108
  ).option("--flow <name>", "flow name for multi-flow configs").option("-p, --platform <platform>", "platform override (web or server)").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (file, options) => {
3263
4109
  await simulateCommand({
3264
- config: file || "bundle.config.json",
4110
+ config: file,
4111
+ output: options.output,
3265
4112
  event: options.event,
3266
4113
  flow: options.flow,
3267
4114
  platform: options.platform,
@@ -3273,9 +4120,10 @@ program.command("simulate [file]").description("Simulate event processing and ca
3273
4120
  program.command("push [file]").description("Push an event through the flow with real API execution").requiredOption(
3274
4121
  "-e, --event <source>",
3275
4122
  "event to push (JSON string, file path, or URL)"
3276
- ).option("--flow <name>", "flow name for multi-flow configs").option("-p, --platform <platform>", "platform override (web or server)").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (file, options) => {
4123
+ ).option("-o, --output <path>", "write result to file").option("--flow <name>", "flow name for multi-flow configs").option("-p, --platform <platform>", "platform override (web or server)").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (file, options) => {
3277
4124
  await pushCommand({
3278
- config: file || "bundle.config.json",
4125
+ config: file,
4126
+ output: options.output,
3279
4127
  event: options.event,
3280
4128
  flow: options.flow,
3281
4129
  platform: options.platform,
@@ -3284,10 +4132,11 @@ program.command("push [file]").description("Push an event through the flow with
3284
4132
  silent: options.silent
3285
4133
  });
3286
4134
  });
3287
- program.command("validate <type> [input]").description("Validate event, flow, or mapping configuration").option("--flow <name>", "flow name for multi-flow configs").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").option("--strict", "fail on warnings").action(async (type, input, options) => {
4135
+ program.command("validate <type> [input]").description("Validate event, flow, or mapping configuration").option("-o, --output <path>", "write result to file").option("--flow <name>", "flow name for multi-flow configs").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").option("--strict", "fail on warnings").action(async (type, input, options) => {
3288
4136
  await validateCommand({
3289
4137
  type,
3290
4138
  input,
4139
+ output: options.output,
3291
4140
  flow: options.flow,
3292
4141
  json: options.json,
3293
4142
  verbose: options.verbose,
@@ -3295,6 +4144,65 @@ program.command("validate <type> [input]").description("Validate event, flow, or
3295
4144
  strict: options.strict
3296
4145
  });
3297
4146
  });
4147
+ var authCmd = program.command("auth").description("Authentication and identity");
4148
+ authCmd.command("login").description("Log in to walkerOS via browser").option("--url <url>", "custom app URL").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (options) => {
4149
+ await loginCommand({
4150
+ url: options.url,
4151
+ json: options.json,
4152
+ verbose: options.verbose,
4153
+ silent: options.silent
4154
+ });
4155
+ });
4156
+ authCmd.command("logout").description("Remove stored credentials").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (options) => {
4157
+ await logoutCommand({
4158
+ json: options.json,
4159
+ verbose: options.verbose,
4160
+ silent: options.silent
4161
+ });
4162
+ });
4163
+ authCmd.command("whoami").description("Show current user identity").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (options) => {
4164
+ await whoamiCommand({
4165
+ output: options.output,
4166
+ json: options.json,
4167
+ verbose: options.verbose,
4168
+ silent: options.silent
4169
+ });
4170
+ });
4171
+ var projectsCmd = program.command("projects").description("Manage walkerOS projects");
4172
+ projectsCmd.command("list").description("List all projects").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (options) => {
4173
+ await listProjectsCommand(options);
4174
+ });
4175
+ projectsCmd.command("get [projectId]").description("Get project details").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (projectId, options) => {
4176
+ await getProjectCommand(projectId, options);
4177
+ });
4178
+ projectsCmd.command("create <name>").description("Create a new project").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (name, options) => {
4179
+ await createProjectCommand(name, options);
4180
+ });
4181
+ projectsCmd.command("update [projectId]").description("Update a project").requiredOption("--name <name>", "new project name").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (projectId, options) => {
4182
+ await updateProjectCommand(projectId, options);
4183
+ });
4184
+ projectsCmd.command("delete [projectId]").description("Delete a project").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (projectId, options) => {
4185
+ await deleteProjectCommand(projectId, options);
4186
+ });
4187
+ var flowsCmd = program.command("flows").description("Manage walkerOS flows");
4188
+ flowsCmd.command("list").description("List all flows in a project").option("--project <id>", "project ID (defaults to WALKEROS_PROJECT_ID)").option("--sort <field>", "sort by: name, updated_at, created_at").option("--order <dir>", "sort order: asc, desc").option("--include-deleted", "include soft-deleted flows").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (options) => {
4189
+ await listFlowsCommand(options);
4190
+ });
4191
+ flowsCmd.command("get <flowId>").description("Get a flow with its full content").option("--project <id>", "project ID (defaults to WALKEROS_PROJECT_ID)").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (flowId, options) => {
4192
+ await getFlowCommand(flowId, options);
4193
+ });
4194
+ flowsCmd.command("create <name>").description("Create a new flow").option("--project <id>", "project ID (defaults to WALKEROS_PROJECT_ID)").option("-c, --content <json>", "Flow.Setup JSON string or file path").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (name, options) => {
4195
+ await createFlowCommand(name, options);
4196
+ });
4197
+ flowsCmd.command("update <flowId>").description("Update a flow").option("--project <id>", "project ID (defaults to WALKEROS_PROJECT_ID)").option("--name <name>", "new flow name").option("-c, --content <json>", "new Flow.Setup JSON string or file path").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (flowId, options) => {
4198
+ await updateFlowCommand(flowId, options);
4199
+ });
4200
+ flowsCmd.command("delete <flowId>").description("Delete a flow").option("--project <id>", "project ID (defaults to WALKEROS_PROJECT_ID)").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (flowId, options) => {
4201
+ await deleteFlowCommand(flowId, options);
4202
+ });
4203
+ flowsCmd.command("duplicate <flowId>").description("Duplicate a flow").option("--project <id>", "project ID (defaults to WALKEROS_PROJECT_ID)").option("--name <name>", "name for the copy").option("-o, --output <path>", "output file path").option("--json", "output as JSON").option("-v, --verbose", "verbose output").option("-s, --silent", "suppress output").action(async (flowId, options) => {
4204
+ await duplicateFlowCommand(flowId, options);
4205
+ });
3298
4206
  var runCmd = program.command("run").description("Run walkerOS flows in collect or serve mode");
3299
4207
  runCmd.command("collect [file]").description(
3300
4208
  "Run collector mode (event collection endpoint). Defaults to server-collect.mjs if no file specified."
@@ -3323,16 +4231,34 @@ runCmd.command("serve [file]").description(
3323
4231
  });
3324
4232
  });
3325
4233
  registerCacheCommand(program);
3326
- registerCleanCommand(program);
3327
4234
  program.parse();
3328
4235
  export {
3329
4236
  bundle,
3330
4237
  bundleCommand,
4238
+ bundleRemote,
4239
+ createApiClient,
4240
+ createFlow,
4241
+ createProject,
4242
+ deleteFlow,
4243
+ deleteProject,
4244
+ duplicateFlow,
4245
+ getAuthHeaders,
4246
+ getFlow,
4247
+ getProject,
4248
+ getToken,
4249
+ listFlows,
4250
+ listProjects,
4251
+ push,
3331
4252
  pushCommand,
4253
+ requireProjectId,
4254
+ resolveBaseUrl,
3332
4255
  run,
3333
4256
  runCommand,
3334
4257
  simulate,
3335
4258
  simulateCommand,
3336
- validate
4259
+ updateFlow,
4260
+ updateProject,
4261
+ validate,
4262
+ whoami
3337
4263
  };
3338
4264
  //# sourceMappingURL=index.js.map