@walkeros/cli 4.1.0 → 4.1.1-next-1779822275564

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
@@ -2895,6 +2895,16 @@ export default async function(context = {}) {
2895
2895
 
2896
2896
  const result = await startFlow(config);
2897
2897
 
2898
+ // Telemetry observer installation: the host (pipeline.ts) builds the
2899
+ // observer functions via createTelemetryObserver + createBatchedPoster
2900
+ // and forwards them through context. Added to collector.observers so the
2901
+ // runtime self-emission loop drives them.
2902
+ if (context.observers) {
2903
+ for (const observer of context.observers) {
2904
+ result.collector.observers.add(observer);
2905
+ }
2906
+ }
2907
+
2898
2908
  const httpSource = Object.values(result.collector.sources || {})
2899
2909
  .find(s => 'httpHandler' in s && typeof s.httpHandler === 'function');
2900
2910
 
@@ -2926,13 +2936,29 @@ function generateWebEntry(stage1Path, dataPayload, options = {}) {
2926
2936
  }
2927
2937
  }` : "";
2928
2938
  const stage1Specifier = toFileImportSpecifier(stage1Path);
2929
- return `import { startFlow, wireConfig } from '${stage1Specifier}';
2939
+ const telemetryImport = options.telemetry ? `
2940
+ import { createBatchedPoster as __cbp, createTelemetryObserver as __cto } from '@walkeros/core';` : "";
2941
+ const telemetryBlock = options.telemetry ? `
2942
+ // --- Telemetry wiring ---
2943
+ {
2944
+ const __emit = __cbp({
2945
+ url: ${JSON.stringify(options.telemetry.observerUrl)},
2946
+ token: ${JSON.stringify(options.telemetry.ingestToken)},
2947
+ });
2948
+ const __observer = __cto(__emit, {
2949
+ flowId: ${JSON.stringify(options.telemetry.flowId)},
2950
+ level: ${JSON.stringify(options.telemetry.level ?? "standard")},
2951
+ sample: ${JSON.stringify(options.telemetry.sample ?? 1)},
2952
+ });
2953
+ collector.observers.add(__observer);
2954
+ }` : "";
2955
+ return `import { startFlow, wireConfig } from '${stage1Specifier}';${telemetryImport}
2930
2956
 
2931
2957
  const __configData = ${dataPayload};
2932
2958
 
2933
2959
  (async () => {
2934
2960
  const config = wireConfig(__configData);${envBlock}
2935
- const { collector, elb } = await startFlow(config);${assignmentCode}
2961
+ const { collector, elb } = await startFlow(config);${telemetryBlock}${assignmentCode}
2936
2962
  })();`;
2937
2963
  }
2938
2964
  function generateWrapEntry(stage1Path, options = {}) {
@@ -3014,11 +3040,27 @@ function generateWrapEntry(stage1Path, options = {}) {
3014
3040
  }
3015
3041
  }` : "";
3016
3042
  const stage1Specifier = toFileImportSpecifier(stage1Path);
3017
- return `import { startFlow, wireConfig, __configData } from '${stage1Specifier}';
3043
+ const telemetryImport = options.telemetry ? `
3044
+ import { createBatchedPoster as __cbp, createTelemetryObserver as __cto } from '@walkeros/core';` : "";
3045
+ const telemetryBlock = options.telemetry ? `
3046
+ // --- Telemetry wiring ---
3047
+ {
3048
+ const __emit = __cbp({
3049
+ url: ${JSON.stringify(options.telemetry.observerUrl)},
3050
+ token: ${JSON.stringify(options.telemetry.ingestToken)},
3051
+ });
3052
+ const __observer = __cto(__emit, {
3053
+ flowId: ${JSON.stringify(options.telemetry.flowId)},
3054
+ level: ${JSON.stringify(options.telemetry.level ?? "standard")},
3055
+ sample: ${JSON.stringify(options.telemetry.sample ?? 1)},
3056
+ });
3057
+ collector.observers.add(__observer);
3058
+ }` : "";
3059
+ return `import { startFlow, wireConfig, __configData } from '${stage1Specifier}';${telemetryImport}
3018
3060
 
3019
3061
  (async () => {${preflightBlock}
3020
3062
  const config = wireConfig(__configData);${envBlock}
3021
- const { collector, elb } = await startFlow(config);${assignmentCode}
3063
+ const { collector, elb } = await startFlow(config);${telemetryBlock}${assignmentCode}
3022
3064
  })();`;
3023
3065
  }
3024
3066
  function generateWrapEntryServer(stage1Path) {
@@ -3040,6 +3082,16 @@ export default async function(context = {}) {
3040
3082
 
3041
3083
  const result = await startFlow(config);
3042
3084
 
3085
+ // Telemetry observer installation: the host (pipeline.ts) builds the
3086
+ // observer functions via createTelemetryObserver + createBatchedPoster
3087
+ // and forwards them through context. Added to collector.observers so the
3088
+ // runtime self-emission loop drives them.
3089
+ if (context.observers) {
3090
+ for (const observer of context.observers) {
3091
+ result.collector.observers.add(observer);
3092
+ }
3093
+ }
3094
+
3043
3095
  const httpSource = Object.values(result.collector.sources || {})
3044
3096
  .find(s => 'httpHandler' in s && typeof s.httpHandler === 'function');
3045
3097
 
@@ -3906,7 +3958,14 @@ function startDrainPump(pending, options = {}) {
3906
3958
  }
3907
3959
 
3908
3960
  // src/commands/push/flow-context.ts
3909
- async function withFlowContext(options, fn) {
3961
+ function defaultPushError(error, startTime) {
3962
+ return {
3963
+ success: false,
3964
+ duration: Date.now() - startTime,
3965
+ error: getErrorMessage(error)
3966
+ };
3967
+ }
3968
+ async function withFlowContext(options, fn, onError) {
3910
3969
  const {
3911
3970
  esmPath,
3912
3971
  platform,
@@ -3998,11 +4057,7 @@ async function withFlowContext(options, fn) {
3998
4057
  }
3999
4058
  return await fn(flowModule);
4000
4059
  } catch (error) {
4001
- return {
4002
- success: false,
4003
- duration: Date.now() - startTime,
4004
- error: getErrorMessage(error)
4005
- };
4060
+ return (onError ?? defaultPushError)(error, startTime);
4006
4061
  } finally {
4007
4062
  if (timerControl) timerControl.restore();
4008
4063
  if (savedFetch !== void 0) {
@@ -4064,6 +4119,28 @@ function cleanupNetworkPolyfills(savedFetch) {
4064
4119
  global.fetch = savedFetch;
4065
4120
  }
4066
4121
 
4122
+ // src/commands/push/simulation-result.ts
4123
+ init_utils2();
4124
+ function hasEvent(entry) {
4125
+ return entry.event != null;
4126
+ }
4127
+ function toError(error) {
4128
+ return error instanceof Error ? error : new Error(getErrorMessage(error));
4129
+ }
4130
+ function buildSimulationResult(args) {
4131
+ const { step, name, startTime, captured, usage, error } = args;
4132
+ const events = (captured ?? []).filter(hasEvent).map((entry) => entry.event);
4133
+ const calls = usage ? Object.values(usage).flat().map((call) => ({ fn: call.fn, args: call.args, ts: call.ts })) : [];
4134
+ return {
4135
+ step,
4136
+ name,
4137
+ events,
4138
+ calls,
4139
+ duration: Date.now() - startTime,
4140
+ ...error !== void 0 ? { error: toError(error) } : {}
4141
+ };
4142
+ }
4143
+
4067
4144
  // src/commands/push/prepare.ts
4068
4145
  init_cli_logger();
4069
4146
  init_tmp();
@@ -4167,6 +4244,13 @@ function dispatchSimulate(flags) {
4167
4244
  // src/commands/push/run.ts
4168
4245
  init_core();
4169
4246
  init_config();
4247
+ function simulationToPushResult(result) {
4248
+ return {
4249
+ success: !result.error,
4250
+ duration: result.duration,
4251
+ ...result.error ? { error: result.error.message } : {}
4252
+ };
4253
+ }
4170
4254
  async function runPushCommand(options) {
4171
4255
  const startTime = Date.now();
4172
4256
  try {
@@ -4197,26 +4281,30 @@ async function runPushCommand(options) {
4197
4281
  });
4198
4282
  break;
4199
4283
  case "source":
4200
- result = await simulateSource(config, resolvedEvent, {
4201
- sourceId: plan.ids[0],
4202
- flow: options.flow,
4203
- silent: options.silent,
4204
- verbose: options.verbose,
4205
- snapshot: options.snapshot
4206
- });
4207
- break;
4208
- case "transformer":
4209
- result = await simulateTransformer(
4210
- config,
4211
- resolvedEvent,
4212
- {
4213
- transformerId: plan.ids[0],
4284
+ result = simulationToPushResult(
4285
+ await simulateSource(config, resolvedEvent, {
4286
+ sourceId: plan.ids[0],
4214
4287
  flow: options.flow,
4215
- mock: options.mock,
4216
4288
  silent: options.silent,
4217
4289
  verbose: options.verbose,
4218
4290
  snapshot: options.snapshot
4219
- }
4291
+ })
4292
+ );
4293
+ break;
4294
+ case "transformer":
4295
+ result = simulationToPushResult(
4296
+ await simulateTransformer(
4297
+ config,
4298
+ resolvedEvent,
4299
+ {
4300
+ transformerId: plan.ids[0],
4301
+ flow: options.flow,
4302
+ mock: options.mock,
4303
+ silent: options.silent,
4304
+ verbose: options.verbose,
4305
+ snapshot: options.snapshot
4306
+ }
4307
+ )
4220
4308
  );
4221
4309
  break;
4222
4310
  case "destination":
@@ -4239,7 +4327,6 @@ async function runPushCommand(options) {
4239
4327
  }
4240
4328
  async function runDestinationSimulationLoop(config, event, destinationIds, options) {
4241
4329
  const startTime = Date.now();
4242
- const perDestination = {};
4243
4330
  for (const destinationId of destinationIds) {
4244
4331
  const r = await simulateDestination(config, event, {
4245
4332
  destinationId,
@@ -4249,20 +4336,17 @@ async function runDestinationSimulationLoop(config, event, destinationIds, optio
4249
4336
  verbose: options.verbose,
4250
4337
  snapshot: options.snapshot
4251
4338
  });
4252
- perDestination[destinationId] = r;
4253
- if (!r.success) {
4339
+ if (r.error) {
4254
4340
  return {
4255
4341
  success: false,
4256
4342
  duration: Date.now() - startTime,
4257
- error: `simulate destination.${destinationId}: ${r.error ?? "unknown error"}`,
4258
- perDestination
4343
+ error: `simulate destination.${destinationId}: ${r.error.message}`
4259
4344
  };
4260
4345
  }
4261
4346
  }
4262
4347
  return {
4263
4348
  success: true,
4264
- duration: Date.now() - startTime,
4265
- perDestination
4349
+ duration: Date.now() - startTime
4266
4350
  };
4267
4351
  }
4268
4352
 
@@ -4626,20 +4710,27 @@ async function simulateSource(configOrPath, input, options) {
4626
4710
  if (instance.flow?.collector?.command) {
4627
4711
  await instance.flow.collector.command("shutdown");
4628
4712
  }
4629
- return {
4630
- success: true,
4631
- ...captured.length > 0 ? { captured } : {},
4632
- ...networkCalls.length > 0 ? { networkCalls } : {},
4633
- duration: Date.now() - startTime
4634
- };
4635
- }
4713
+ return buildSimulationResult({
4714
+ step: "source",
4715
+ name: options.sourceId,
4716
+ startTime,
4717
+ captured
4718
+ });
4719
+ },
4720
+ (error) => buildSimulationResult({
4721
+ step: "source",
4722
+ name: options.sourceId,
4723
+ startTime,
4724
+ error
4725
+ })
4636
4726
  );
4637
4727
  } catch (error) {
4638
- return {
4639
- success: false,
4640
- duration: Date.now() - startTime,
4641
- error: getErrorMessage(error)
4642
- };
4728
+ return buildSimulationResult({
4729
+ step: "source",
4730
+ name: options.sourceId,
4731
+ startTime,
4732
+ error
4733
+ });
4643
4734
  } finally {
4644
4735
  await prepared.cleanup();
4645
4736
  }
@@ -4648,11 +4739,12 @@ async function simulateTransformer(configOrPath, event, options) {
4648
4739
  const startTime = Date.now();
4649
4740
  const parsed = schemas3.PartialEventSchema.safeParse(event);
4650
4741
  if (!parsed.success) {
4651
- return {
4652
- success: false,
4653
- duration: 0,
4742
+ return buildSimulationResult({
4743
+ step: "transformer",
4744
+ name: options.transformerId,
4745
+ startTime,
4654
4746
  error: parsed.error.message
4655
- };
4747
+ });
4656
4748
  }
4657
4749
  let config;
4658
4750
  if (typeof configOrPath === "string") {
@@ -4728,7 +4820,6 @@ async function simulateTransformer(configOrPath, event, options) {
4728
4820
  const inputEvent = event;
4729
4821
  const ingest = createIngest(options.transformerId);
4730
4822
  const captured = [];
4731
- captured.push({ event: { ...inputEvent }, timestamp: Date.now() });
4732
4823
  logger.info(`Simulating transformer: ${options.transformerId}`);
4733
4824
  let processedEvent = inputEvent;
4734
4825
  const before = transformer.config.before;
@@ -4752,11 +4843,12 @@ async function simulateTransformer(configOrPath, event, options) {
4752
4843
  if (beforeResult === null) {
4753
4844
  captured.push({ event: null, timestamp: Date.now() });
4754
4845
  await collector.command("shutdown");
4755
- return {
4756
- success: true,
4757
- captured,
4758
- duration: Date.now() - startTime
4759
- };
4846
+ return buildSimulationResult({
4847
+ step: "transformer",
4848
+ name: options.transformerId,
4849
+ startTime,
4850
+ captured
4851
+ });
4760
4852
  }
4761
4853
  processedEvent = Array.isArray(beforeResult) ? beforeResult[0] : beforeResult;
4762
4854
  }
@@ -4783,20 +4875,27 @@ async function simulateTransformer(configOrPath, event, options) {
4783
4875
  captured.push({ event: processedEvent, timestamp: Date.now() });
4784
4876
  }
4785
4877
  await collector.command("shutdown");
4786
- return {
4787
- success: true,
4788
- captured,
4789
- ...networkCalls.length > 0 ? { networkCalls } : {},
4790
- duration: Date.now() - startTime
4791
- };
4792
- }
4878
+ return buildSimulationResult({
4879
+ step: "transformer",
4880
+ name: options.transformerId,
4881
+ startTime,
4882
+ captured
4883
+ });
4884
+ },
4885
+ (error) => buildSimulationResult({
4886
+ step: "transformer",
4887
+ name: options.transformerId,
4888
+ startTime,
4889
+ error
4890
+ })
4793
4891
  );
4794
4892
  } catch (error) {
4795
- return {
4796
- success: false,
4797
- duration: Date.now() - startTime,
4798
- error: getErrorMessage(error)
4799
- };
4893
+ return buildSimulationResult({
4894
+ step: "transformer",
4895
+ name: options.transformerId,
4896
+ startTime,
4897
+ error
4898
+ });
4800
4899
  } finally {
4801
4900
  await prepared.cleanup();
4802
4901
  }
@@ -4805,11 +4904,12 @@ async function simulateDestination(configOrPath, event, options) {
4805
4904
  const startTime = Date.now();
4806
4905
  const parsed = schemas3.PartialEventSchema.safeParse(event);
4807
4906
  if (!parsed.success) {
4808
- return {
4809
- success: false,
4810
- duration: 0,
4907
+ return buildSimulationResult({
4908
+ step: "destination",
4909
+ name: options.destinationId,
4910
+ startTime,
4811
4911
  error: parsed.error.message
4812
- };
4912
+ });
4813
4913
  }
4814
4914
  let config;
4815
4915
  if (typeof configOrPath === "string") {
@@ -4892,25 +4992,31 @@ async function simulateDestination(configOrPath, event, options) {
4892
4992
  );
4893
4993
  }
4894
4994
  logger.info(`Simulating destination: ${options.destinationId}`);
4895
- const elbResult = await collector.push(event, {
4995
+ await collector.push(event, {
4896
4996
  include: [options.destinationId]
4897
4997
  });
4898
4998
  await collector.command("shutdown");
4899
- return {
4900
- success: true,
4901
- elbResult,
4902
- ...trackedCalls.length > 0 ? { usage: { [options.destinationId]: trackedCalls } } : {},
4903
- ...networkCalls.length > 0 ? { networkCalls } : {},
4904
- duration: Date.now() - startTime
4905
- };
4906
- }
4999
+ return buildSimulationResult({
5000
+ step: "destination",
5001
+ name: options.destinationId,
5002
+ startTime,
5003
+ usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0
5004
+ });
5005
+ },
5006
+ (error) => buildSimulationResult({
5007
+ step: "destination",
5008
+ name: options.destinationId,
5009
+ startTime,
5010
+ error
5011
+ })
4907
5012
  );
4908
5013
  } catch (error) {
4909
- return {
4910
- success: false,
4911
- duration: Date.now() - startTime,
4912
- error: getErrorMessage(error)
4913
- };
5014
+ return buildSimulationResult({
5015
+ step: "destination",
5016
+ name: options.destinationId,
5017
+ startTime,
5018
+ error
5019
+ });
4914
5020
  } finally {
4915
5021
  await prepared.cleanup();
4916
5022
  }
@@ -5169,6 +5275,11 @@ init_utils3();
5169
5275
  init_tmp();
5170
5276
  import { writeFileSync as writeFileSync4 } from "fs";
5171
5277
  import fs17 from "fs-extra";
5278
+ import {
5279
+ createBatchedPoster,
5280
+ createTelemetryObserver,
5281
+ resolveTelemetryOptions
5282
+ } from "@walkeros/core";
5172
5283
 
5173
5284
  // src/runtime/health-server.ts
5174
5285
  import http from "http";
@@ -5244,14 +5355,15 @@ async function loadBundle(file, context2, logger) {
5244
5355
  }
5245
5356
 
5246
5357
  // src/runtime/runner.ts
5247
- async function loadFlow(file, config, logger, loggerConfig, healthServer) {
5358
+ async function loadFlow(file, config, logger, loggerConfig, healthServer, observers) {
5248
5359
  const absolutePath = resolve3(file);
5249
5360
  const flowDir = dirname4(absolutePath);
5250
5361
  process.chdir(flowDir);
5251
5362
  const flowContext = {
5252
5363
  ...config,
5253
5364
  ...loggerConfig ? { logger: loggerConfig } : {},
5254
- ...healthServer ? { sourceSettings: { port: void 0 } } : {}
5365
+ ...healthServer ? { sourceSettings: { port: void 0 } } : {},
5366
+ ...observers ? { observers } : {}
5255
5367
  };
5256
5368
  const result = await loadBundle(absolutePath, flowContext, logger);
5257
5369
  if (healthServer && typeof result.httpHandler === "function") {
@@ -5266,7 +5378,7 @@ async function loadFlow(file, config, logger, loggerConfig, healthServer) {
5266
5378
  httpHandler: result.httpHandler
5267
5379
  };
5268
5380
  }
5269
- async function swapFlow(currentHandle, newFile, config, logger, loggerConfig, healthServer) {
5381
+ async function swapFlow(currentHandle, newFile, config, logger, loggerConfig, healthServer, observers) {
5270
5382
  logger.info("Shutting down current flow for hot-swap...");
5271
5383
  if (healthServer) {
5272
5384
  healthServer.setFlowHandler(null);
@@ -5283,7 +5395,8 @@ async function swapFlow(currentHandle, newFile, config, logger, loggerConfig, he
5283
5395
  config,
5284
5396
  logger,
5285
5397
  loggerConfig,
5286
- healthServer
5398
+ healthServer,
5399
+ observers
5287
5400
  );
5288
5401
  logger.info("Flow swapped successfully");
5289
5402
  return newHandle;
@@ -5353,6 +5466,24 @@ var instanceId = randomBytes(8).toString("hex");
5353
5466
  function getInstanceId() {
5354
5467
  return instanceId;
5355
5468
  }
5469
+ async function applyTraceUntilFromResponse(response, logger) {
5470
+ let parsed;
5471
+ try {
5472
+ parsed = await response.json();
5473
+ } catch (error) {
5474
+ const message = error instanceof Error ? error.message : String(error);
5475
+ logger.debug(`Heartbeat response parse failed: ${message}`);
5476
+ return;
5477
+ }
5478
+ if (typeof parsed !== "object" || parsed === null) return;
5479
+ if (!("traceUntil" in parsed)) return;
5480
+ const value = parsed.traceUntil;
5481
+ if (typeof value === "string" && value.length > 0) {
5482
+ process.env.WALKEROS_TRACE_UNTIL = value;
5483
+ } else if (value === null) {
5484
+ delete process.env.WALKEROS_TRACE_UNTIL;
5485
+ }
5486
+ }
5356
5487
  function createHeartbeat(config, logger) {
5357
5488
  let timer = null;
5358
5489
  const startTime = Date.now();
@@ -5401,6 +5532,9 @@ function createHeartbeat(config, logger) {
5401
5532
  if (response.ok && counters && current) {
5402
5533
  lastReported = current;
5403
5534
  }
5535
+ if (response.ok) {
5536
+ await applyTraceUntilFromResponse(response, logger);
5537
+ }
5404
5538
  if (response.status === 401 || response.status === 403) {
5405
5539
  logger.error(
5406
5540
  `Heartbeat auth failed (${response.status}). Token may have expired \u2014 redeploy to rotate.`
@@ -5502,6 +5636,7 @@ async function runPipeline(options) {
5502
5636
  logger.info(`walkeros/flow v${VERSION}`);
5503
5637
  logger.info(`Instance: ${getInstanceId()}`);
5504
5638
  const healthServer = await createHealthServer(port, logger);
5639
+ const telemetryObservers = buildTelemetryObservers(api?.flowId ?? "flow");
5505
5640
  const runtimeConfig = { port };
5506
5641
  let handle;
5507
5642
  try {
@@ -5510,7 +5645,8 @@ async function runPipeline(options) {
5510
5645
  runtimeConfig,
5511
5646
  logger,
5512
5647
  loggerConfig,
5513
- healthServer
5648
+ healthServer,
5649
+ telemetryObservers
5514
5650
  );
5515
5651
  } catch (error) {
5516
5652
  await healthServer.close();
@@ -5576,7 +5712,8 @@ async function runPipeline(options) {
5576
5712
  runtimeConfig,
5577
5713
  logger,
5578
5714
  loggerConfig,
5579
- healthServer
5715
+ healthServer,
5716
+ telemetryObservers
5580
5717
  );
5581
5718
  writeCache(
5582
5719
  api.cacheDir,
@@ -5635,6 +5772,15 @@ async function runPipeline(options) {
5635
5772
  await new Promise(() => {
5636
5773
  });
5637
5774
  }
5775
+ function buildTelemetryObservers(flowId) {
5776
+ const url = process.env.WALKEROS_OBSERVER_URL;
5777
+ const token = process.env.WALKEROS_INGEST_TOKEN;
5778
+ if (!url || !token) return void 0;
5779
+ const opts = resolveTelemetryOptions({ flowId });
5780
+ if (!opts) return void 0;
5781
+ const emit = createBatchedPoster({ url, token });
5782
+ return [createTelemetryObserver(emit, opts)];
5783
+ }
5638
5784
  async function injectSecrets(api, logger) {
5639
5785
  try {
5640
5786
  const secrets = await fetchSecrets({
@@ -5871,7 +6017,7 @@ import chalk2 from "chalk";
5871
6017
  // src/commands/validate/validators/contract.ts
5872
6018
  var SECTION_KEYS = ["globals", "context", "custom", "user", "consent"];
5873
6019
  var KNOWN_KEYS = /* @__PURE__ */ new Set([
5874
- "extends",
6020
+ "extend",
5875
6021
  "description",
5876
6022
  "events",
5877
6023
  ...SECTION_KEYS
@@ -5901,18 +6047,18 @@ function validateContract(input) {
5901
6047
  continue;
5902
6048
  }
5903
6049
  const obj = entry;
5904
- if (obj.extends !== void 0) {
5905
- if (typeof obj.extends !== "string") {
6050
+ if (obj.extend !== void 0) {
6051
+ if (typeof obj.extend !== "string") {
5906
6052
  errors.push({
5907
- path: `${name}.extends`,
5908
- message: "extends must be a string",
6053
+ path: `${name}.extend`,
6054
+ message: "extend must be a string",
5909
6055
  code: "INVALID_EXTENDS"
5910
6056
  });
5911
- } else if (!contractNames.includes(obj.extends)) {
6057
+ } else if (!contractNames.includes(obj.extend)) {
5912
6058
  errors.push({
5913
- path: `${name}.extends`,
5914
- message: `extends references non-existent contract "${obj.extends}"`,
5915
- value: obj.extends,
6059
+ path: `${name}.extend`,
6060
+ message: `extend references non-existent contract "${obj.extend}"`,
6061
+ value: obj.extend,
5916
6062
  code: "INVALID_EXTENDS"
5917
6063
  });
5918
6064
  }
@@ -5951,15 +6097,15 @@ function validateContract(input) {
5951
6097
  while (current) {
5952
6098
  if (visited.has(current)) {
5953
6099
  errors.push({
5954
- path: `${name}.extends`,
5955
- message: `Circular extends chain: ${[...visited, current].join(" \u2192 ")}`,
6100
+ path: `${name}.extend`,
6101
+ message: `Circular extend chain: ${[...visited, current].join(" \u2192 ")}`,
5956
6102
  code: "CIRCULAR_EXTENDS"
5957
6103
  });
5958
6104
  break;
5959
6105
  }
5960
6106
  visited.add(current);
5961
6107
  const entry = contracts[current];
5962
- current = entry?.extends || "";
6108
+ current = entry?.extend || "";
5963
6109
  }
5964
6110
  }
5965
6111
  return {
@@ -6528,7 +6674,7 @@ function validateMapping(input) {
6528
6674
  // src/commands/validate/validators/entry.ts
6529
6675
  import Ajv from "ajv";
6530
6676
  import { fetchPackageSchema } from "@walkeros/core";
6531
- var CLIENT_HEADER = "walkeros-cli/4.1.0";
6677
+ var CLIENT_HEADER = "walkeros-cli/4.1.1-next-1779822275564";
6532
6678
  var SECTIONS = ["destinations", "sources", "transformers"];
6533
6679
  function resolveEntry(path20, flowConfig) {
6534
6680
  const flows = flowConfig.flows;
@@ -7181,9 +7327,11 @@ async function whoamiCommand(options) {
7181
7327
  // src/commands/projects/index.ts
7182
7328
  init_auth();
7183
7329
  init_output();
7184
- async function listProjects() {
7330
+ async function listProjects(options = {}) {
7185
7331
  const client = createApiClient();
7186
- const { data, error } = await client.GET("/api/projects");
7332
+ const { data, error } = await client.GET("/api/projects", {
7333
+ params: { query: { cursor: options.cursor, limit: options.limit } }
7334
+ });
7187
7335
  if (error) throw new Error(error.error?.message || "Failed to list projects");
7188
7336
  return data;
7189
7337
  }
@@ -7235,7 +7383,10 @@ async function handleResult(fn, options) {
7235
7383
  }
7236
7384
  }
7237
7385
  async function listProjectsCommand(options) {
7238
- await handleResult(() => listProjects(), options);
7386
+ await handleResult(
7387
+ () => listProjects({ cursor: options.cursor, limit: options.limit }),
7388
+ options
7389
+ );
7239
7390
  }
7240
7391
  async function getProjectCommand(projectId, options) {
7241
7392
  await handleResult(
@@ -7279,7 +7430,9 @@ async function listFlows(options = {}) {
7279
7430
  query: {
7280
7431
  sort: options.sort,
7281
7432
  order: options.order,
7282
- include_deleted: options.includeDeleted ? "true" : void 0
7433
+ include_deleted: options.includeDeleted ? "true" : void 0,
7434
+ cursor: options.cursor,
7435
+ limit: options.limit
7283
7436
  }
7284
7437
  }
7285
7438
  });
@@ -7383,7 +7536,9 @@ async function listFlowsCommand(options) {
7383
7536
  projectId: options.project,
7384
7537
  sort: options.sort,
7385
7538
  order: options.order,
7386
- includeDeleted: options.includeDeleted
7539
+ includeDeleted: options.includeDeleted,
7540
+ cursor: options.cursor,
7541
+ limit: options.limit
7387
7542
  }),
7388
7543
  options
7389
7544
  );
@@ -7685,6 +7840,8 @@ async function listDeployments(options = {}) {
7685
7840
  if (options.type) params.set("type", options.type);
7686
7841
  if (options.status) params.set("status", options.status);
7687
7842
  if (options.flowId) params.set("flowId", options.flowId);
7843
+ if (options.cursor) params.set("cursor", options.cursor);
7844
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
7688
7845
  const qs = params.toString();
7689
7846
  const response = await apiFetch(
7690
7847
  `/api/projects/${id}/deployments${qs ? `?${qs}` : ""}`
@@ -7782,7 +7939,9 @@ async function listDeploymentsCommand(options) {
7782
7939
  () => listDeployments({
7783
7940
  projectId: options.project,
7784
7941
  type: options.type,
7785
- status: options.status
7942
+ status: options.status,
7943
+ cursor: options.cursor,
7944
+ limit: options.limit
7786
7945
  }),
7787
7946
  options
7788
7947
  );
@@ -8031,8 +8190,22 @@ init_bundle();
8031
8190
  init_bundler();
8032
8191
  import * as path19 from "path";
8033
8192
  import * as os3 from "os";
8193
+ import { fileURLToPath as fileURLToPath2 } from "url";
8034
8194
  import fs18 from "fs-extra";
8035
8195
  import * as esbuild2 from "esbuild";
8196
+ function getNodeResolutionPaths() {
8197
+ const here = path19.dirname(fileURLToPath2(import.meta.url));
8198
+ const candidates = [];
8199
+ let dir = here;
8200
+ for (let i = 0; i < 8; i++) {
8201
+ const candidate = path19.join(dir, "node_modules");
8202
+ candidates.push(candidate);
8203
+ const parent = path19.dirname(dir);
8204
+ if (parent === dir) break;
8205
+ dir = parent;
8206
+ }
8207
+ return candidates;
8208
+ }
8036
8209
  async function wrapSkeleton(options) {
8037
8210
  const {
8038
8211
  skeletonPath,
@@ -8058,12 +8231,22 @@ async function wrapSkeleton(options) {
8058
8231
  throw new Error(`wrapSkeleton: skeleton not found at ${skeletonPath}`);
8059
8232
  }
8060
8233
  const absoluteSkeletonPath = path19.resolve(skeletonPath);
8234
+ const tInput = options.telemetry;
8235
+ const tLevel = tInput?.level ?? "standard";
8236
+ const telemetry = tInput && tLevel !== "off" ? {
8237
+ observerUrl: tInput.observerUrl,
8238
+ ingestToken: tInput.ingestToken,
8239
+ flowId: tInput.flowId,
8240
+ level: tLevel,
8241
+ ...tInput.sample !== void 0 ? { sample: tInput.sample } : {}
8242
+ } : void 0;
8061
8243
  const entryText = platform === "browser" ? generateWrapEntry(absoluteSkeletonPath, {
8062
8244
  ...windowCollector ? { windowCollector } : {},
8063
8245
  ...windowElb ? { windowElb } : {},
8064
8246
  ...options.previewOrigin ? { previewOrigin: options.previewOrigin } : {},
8065
8247
  ...options.previewScope ? { previewScope: options.previewScope } : {},
8066
- platform
8248
+ platform,
8249
+ ...telemetry ? { telemetry } : {}
8067
8250
  }) : generateWrapEntryServer(absoluteSkeletonPath);
8068
8251
  const entryDir = await fs18.mkdtemp(path19.join(os3.tmpdir(), "walkeros-wrap-"));
8069
8252
  const entryPath = path19.join(entryDir, "flow.mjs");
@@ -8076,6 +8259,12 @@ async function wrapSkeleton(options) {
8076
8259
  format: "esm",
8077
8260
  platform,
8078
8261
  outfile: outputPath,
8262
+ // Resolve `@walkeros/core` (used by the telemetry block) from the CLI
8263
+ // package's own dependency tree and the workspace root. Without this,
8264
+ // esbuild looks under the temp entryDir which has no node_modules.
8265
+ // The wrap step always runs from inside the CLI process; both the
8266
+ // CLI's local node_modules and the workspace root are safe anchors.
8267
+ nodePaths: getNodeResolutionPaths(),
8079
8268
  treeShaking: true,
8080
8269
  logLevel: "error",
8081
8270
  minify,