@walkeros/cli 4.1.1-next-1779485810490 → 4.1.1

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.d.ts CHANGED
@@ -729,6 +729,8 @@ interface ProjectsCommandOptions extends GlobalOptions {
729
729
  output?: string;
730
730
  project?: string;
731
731
  name?: string;
732
+ cursor?: string;
733
+ limit?: number;
732
734
  }
733
735
  declare function listProjectsCommand(options: ProjectsCommandOptions): Promise<void>;
734
736
  declare function getProjectCommand(projectId: string | undefined, options: ProjectsCommandOptions): Promise<void>;
@@ -4791,7 +4793,7 @@ interface components {
4791
4793
  total: number;
4792
4794
  limit: number;
4793
4795
  offset: number;
4794
- nextCursor?: string | null;
4796
+ nextCursor: string | null;
4795
4797
  };
4796
4798
  UpdateDeploymentResponse: {
4797
4799
  /** @example dep_a1b2c3d4 */
@@ -5419,6 +5421,8 @@ declare function listFlowsCommand(options: FlowsCommandOptions & {
5419
5421
  sort?: string;
5420
5422
  order?: string;
5421
5423
  includeDeleted?: boolean;
5424
+ cursor?: string;
5425
+ limit?: number;
5422
5426
  }): Promise<void>;
5423
5427
  declare function getFlowCommand(flowId: string, options: FlowsCommandOptions): Promise<void>;
5424
5428
  declare function createFlowCommand(name: string, options: FlowsCommandOptions & {
@@ -5523,6 +5527,8 @@ interface DeploymentsCommandOptions extends GlobalOptions {
5523
5527
  type?: string;
5524
5528
  status?: string;
5525
5529
  label?: string;
5530
+ cursor?: string;
5531
+ limit?: number;
5526
5532
  }
5527
5533
  declare function listDeploymentsCommand(options: DeploymentsCommandOptions): Promise<void>;
5528
5534
  declare function getDeploymentBySlugCommand(slug: string, options: DeploymentsCommandOptions): Promise<void>;
@@ -5602,6 +5608,16 @@ interface WrapSkeletonOptions {
5602
5608
  previewOrigin?: string;
5603
5609
  /** Browser-only: project scope for preview URL isolation. */
5604
5610
  previewScope?: string;
5611
+ /**
5612
+ * Browser-only: telemetry wiring. When provided, the wrapped bundle
5613
+ * installs an observer built via `createTelemetryObserver` onto
5614
+ * `collector.observers` and forwards FlowState records to `observerUrl`
5615
+ * using `createBatchedPoster`.
5616
+ *
5617
+ * Omit (or pass `level: 'off'`) to ship a bundle with zero telemetry
5618
+ * plumbing.
5619
+ */
5620
+ telemetry?: TelemetryBundleOptions;
5605
5621
  /**
5606
5622
  * esbuild target. @default 'es2018' for browser, 'node18' for node.
5607
5623
  */
@@ -5611,6 +5627,18 @@ interface WrapSkeletonOptions {
5611
5627
  /** Fine-grained minification options, forwarded to esbuild. */
5612
5628
  minifyOptions?: MinifyOptions;
5613
5629
  }
5630
+ interface TelemetryBundleOptions {
5631
+ /** Absolute ingest URL. POST receives JSON array of FlowState. */
5632
+ observerUrl: string;
5633
+ /** Deployment-scoped plaintext token. Sent as `Authorization: Bearer`. */
5634
+ ingestToken: string;
5635
+ /** Used as the `flowId` on every emitted FlowState. */
5636
+ flowId: string;
5637
+ /** Verbosity. Default 'standard'. 'off' suppresses the entire wiring. */
5638
+ level?: 'off' | 'standard' | 'trace';
5639
+ /** Deterministic sample fraction in [0, 1]. Default 1. */
5640
+ sample?: number;
5641
+ }
5614
5642
  declare function wrapSkeleton(options: WrapSkeletonOptions): Promise<void>;
5615
5643
 
5616
5644
  declare function createApiClient(): openapi_fetch.Client<paths, `${string}/${string}`>;
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
 
@@ -5223,6 +5275,11 @@ init_utils3();
5223
5275
  init_tmp();
5224
5276
  import { writeFileSync as writeFileSync4 } from "fs";
5225
5277
  import fs17 from "fs-extra";
5278
+ import {
5279
+ createBatchedPoster,
5280
+ createTelemetryObserver,
5281
+ resolveTelemetryOptions
5282
+ } from "@walkeros/core";
5226
5283
 
5227
5284
  // src/runtime/health-server.ts
5228
5285
  import http from "http";
@@ -5298,14 +5355,15 @@ async function loadBundle(file, context2, logger) {
5298
5355
  }
5299
5356
 
5300
5357
  // src/runtime/runner.ts
5301
- async function loadFlow(file, config, logger, loggerConfig, healthServer) {
5358
+ async function loadFlow(file, config, logger, loggerConfig, healthServer, observers) {
5302
5359
  const absolutePath = resolve3(file);
5303
5360
  const flowDir = dirname4(absolutePath);
5304
5361
  process.chdir(flowDir);
5305
5362
  const flowContext = {
5306
5363
  ...config,
5307
5364
  ...loggerConfig ? { logger: loggerConfig } : {},
5308
- ...healthServer ? { sourceSettings: { port: void 0 } } : {}
5365
+ ...healthServer ? { sourceSettings: { port: void 0 } } : {},
5366
+ ...observers ? { observers } : {}
5309
5367
  };
5310
5368
  const result = await loadBundle(absolutePath, flowContext, logger);
5311
5369
  if (healthServer && typeof result.httpHandler === "function") {
@@ -5320,7 +5378,7 @@ async function loadFlow(file, config, logger, loggerConfig, healthServer) {
5320
5378
  httpHandler: result.httpHandler
5321
5379
  };
5322
5380
  }
5323
- async function swapFlow(currentHandle, newFile, config, logger, loggerConfig, healthServer) {
5381
+ async function swapFlow(currentHandle, newFile, config, logger, loggerConfig, healthServer, observers) {
5324
5382
  logger.info("Shutting down current flow for hot-swap...");
5325
5383
  if (healthServer) {
5326
5384
  healthServer.setFlowHandler(null);
@@ -5337,7 +5395,8 @@ async function swapFlow(currentHandle, newFile, config, logger, loggerConfig, he
5337
5395
  config,
5338
5396
  logger,
5339
5397
  loggerConfig,
5340
- healthServer
5398
+ healthServer,
5399
+ observers
5341
5400
  );
5342
5401
  logger.info("Flow swapped successfully");
5343
5402
  return newHandle;
@@ -5407,6 +5466,24 @@ var instanceId = randomBytes(8).toString("hex");
5407
5466
  function getInstanceId() {
5408
5467
  return instanceId;
5409
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
+ }
5410
5487
  function createHeartbeat(config, logger) {
5411
5488
  let timer = null;
5412
5489
  const startTime = Date.now();
@@ -5455,6 +5532,9 @@ function createHeartbeat(config, logger) {
5455
5532
  if (response.ok && counters && current) {
5456
5533
  lastReported = current;
5457
5534
  }
5535
+ if (response.ok) {
5536
+ await applyTraceUntilFromResponse(response, logger);
5537
+ }
5458
5538
  if (response.status === 401 || response.status === 403) {
5459
5539
  logger.error(
5460
5540
  `Heartbeat auth failed (${response.status}). Token may have expired \u2014 redeploy to rotate.`
@@ -5556,6 +5636,7 @@ async function runPipeline(options) {
5556
5636
  logger.info(`walkeros/flow v${VERSION}`);
5557
5637
  logger.info(`Instance: ${getInstanceId()}`);
5558
5638
  const healthServer = await createHealthServer(port, logger);
5639
+ const telemetryObservers = buildTelemetryObservers(api?.flowId ?? "flow");
5559
5640
  const runtimeConfig = { port };
5560
5641
  let handle;
5561
5642
  try {
@@ -5564,7 +5645,8 @@ async function runPipeline(options) {
5564
5645
  runtimeConfig,
5565
5646
  logger,
5566
5647
  loggerConfig,
5567
- healthServer
5648
+ healthServer,
5649
+ telemetryObservers
5568
5650
  );
5569
5651
  } catch (error) {
5570
5652
  await healthServer.close();
@@ -5630,7 +5712,8 @@ async function runPipeline(options) {
5630
5712
  runtimeConfig,
5631
5713
  logger,
5632
5714
  loggerConfig,
5633
- healthServer
5715
+ healthServer,
5716
+ telemetryObservers
5634
5717
  );
5635
5718
  writeCache(
5636
5719
  api.cacheDir,
@@ -5689,6 +5772,15 @@ async function runPipeline(options) {
5689
5772
  await new Promise(() => {
5690
5773
  });
5691
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 emit = createBatchedPoster({ url, token });
5780
+ return [
5781
+ createTelemetryObserver(emit, () => resolveTelemetryOptions({ flowId }))
5782
+ ];
5783
+ }
5692
5784
  async function injectSecrets(api, logger) {
5693
5785
  try {
5694
5786
  const secrets = await fetchSecrets({
@@ -6582,7 +6674,7 @@ function validateMapping(input) {
6582
6674
  // src/commands/validate/validators/entry.ts
6583
6675
  import Ajv from "ajv";
6584
6676
  import { fetchPackageSchema } from "@walkeros/core";
6585
- var CLIENT_HEADER = "walkeros-cli/4.1.1-next-1779485810490";
6677
+ var CLIENT_HEADER = "walkeros-cli/4.1.1";
6586
6678
  var SECTIONS = ["destinations", "sources", "transformers"];
6587
6679
  function resolveEntry(path20, flowConfig) {
6588
6680
  const flows = flowConfig.flows;
@@ -7291,7 +7383,10 @@ async function handleResult(fn, options) {
7291
7383
  }
7292
7384
  }
7293
7385
  async function listProjectsCommand(options) {
7294
- await handleResult(() => listProjects(), options);
7386
+ await handleResult(
7387
+ () => listProjects({ cursor: options.cursor, limit: options.limit }),
7388
+ options
7389
+ );
7295
7390
  }
7296
7391
  async function getProjectCommand(projectId, options) {
7297
7392
  await handleResult(
@@ -7441,7 +7536,9 @@ async function listFlowsCommand(options) {
7441
7536
  projectId: options.project,
7442
7537
  sort: options.sort,
7443
7538
  order: options.order,
7444
- includeDeleted: options.includeDeleted
7539
+ includeDeleted: options.includeDeleted,
7540
+ cursor: options.cursor,
7541
+ limit: options.limit
7445
7542
  }),
7446
7543
  options
7447
7544
  );
@@ -7842,7 +7939,9 @@ async function listDeploymentsCommand(options) {
7842
7939
  () => listDeployments({
7843
7940
  projectId: options.project,
7844
7941
  type: options.type,
7845
- status: options.status
7942
+ status: options.status,
7943
+ cursor: options.cursor,
7944
+ limit: options.limit
7846
7945
  }),
7847
7946
  options
7848
7947
  );
@@ -8091,8 +8190,22 @@ init_bundle();
8091
8190
  init_bundler();
8092
8191
  import * as path19 from "path";
8093
8192
  import * as os3 from "os";
8193
+ import { fileURLToPath as fileURLToPath2 } from "url";
8094
8194
  import fs18 from "fs-extra";
8095
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
+ }
8096
8209
  async function wrapSkeleton(options) {
8097
8210
  const {
8098
8211
  skeletonPath,
@@ -8118,12 +8231,22 @@ async function wrapSkeleton(options) {
8118
8231
  throw new Error(`wrapSkeleton: skeleton not found at ${skeletonPath}`);
8119
8232
  }
8120
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;
8121
8243
  const entryText = platform === "browser" ? generateWrapEntry(absoluteSkeletonPath, {
8122
8244
  ...windowCollector ? { windowCollector } : {},
8123
8245
  ...windowElb ? { windowElb } : {},
8124
8246
  ...options.previewOrigin ? { previewOrigin: options.previewOrigin } : {},
8125
8247
  ...options.previewScope ? { previewScope: options.previewScope } : {},
8126
- platform
8248
+ platform,
8249
+ ...telemetry ? { telemetry } : {}
8127
8250
  }) : generateWrapEntryServer(absoluteSkeletonPath);
8128
8251
  const entryDir = await fs18.mkdtemp(path19.join(os3.tmpdir(), "walkeros-wrap-"));
8129
8252
  const entryPath = path19.join(entryDir, "flow.mjs");
@@ -8136,6 +8259,12 @@ async function wrapSkeleton(options) {
8136
8259
  format: "esm",
8137
8260
  platform,
8138
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(),
8139
8268
  treeShaking: true,
8140
8269
  logLevel: "error",
8141
8270
  minify,