@walkeros/cli 4.2.0 → 4.3.0-next-1781171238534

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/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @walkeros/cli
2
2
 
3
+ ## 4.3.0-next-1781171238534
4
+
5
+ ### Minor Changes
6
+
7
+ - 5cbcd23: `walkeros run` reads two new environment variables.
8
+ `WALKEROS_OBSERVE_LEVEL` sets the runtime's baseline telemetry level (`off`,
9
+ `standard`, or `trace`). `WALKEROS_CONFIG_FROZEN` (`1` or `true`) serves the
10
+ bundle as an immutable snapshot: secrets are still injected at boot, but
11
+ config hot-swap and heartbeat are disabled.
12
+
13
+ ### Patch Changes
14
+
15
+ - b03bfce: `walkeros deploy` now waits long enough to cover a full server deploy
16
+ by default, so a slow but healthy deploy is no longer aborted early and
17
+ reported as a failure. Each run sends a fresh idempotency key, so retrying
18
+ after a failure starts a new deploy instead of replaying the previous result.
19
+ Failures print a stable, machine-readable error code (with a `Retry-After`
20
+ hint on rate limits), and `deploy create` no longer prints an empty token
21
+ placeholder.
22
+ - 5cbcd23: All four simulate functions (`simulateSource`, `simulateTransformer`,
23
+ `simulateCollector`, `simulateDestination`) accept a new `data` option to run
24
+ an existing bundle with updated configuration values, without rebundling. The
25
+ new `buildDataPayload`, `classifyStepProperties`, and `containsCodeMarkers`
26
+ exports build and inspect that payload. Destination simulation results now
27
+ include `mappingKey`, the entity-action key of the matched mapping rule.
28
+ - Updated dependencies [5cbcd23]
29
+ - @walkeros/core@4.3.0-next-1781171238534
30
+ - @walkeros/collector@4.3.0-next-1781171238534
31
+ - @walkeros/server-core@4.3.0-next-1781171238534
32
+ - @walkeros/server-destination-api@4.3.0-next-1781171238534
33
+ - @walkeros/transformer-validate@4.3.0-next-1781171238534
34
+
3
35
  ## 4.2.0
4
36
 
5
37
  ### Minor Changes
package/dist/cli.js CHANGED
@@ -31,25 +31,62 @@ var init_client_context = __esm({
31
31
  });
32
32
 
33
33
  // src/core/api-error.ts
34
- function throwApiError(error, fallbackMessage) {
35
- if (error && typeof error === "object" && "error" in error && typeof error.error === "object") {
36
- const inner = error.error;
37
- const message = inner.message || fallbackMessage;
38
- const code = inner.code;
39
- const details = inner.details?.errors;
40
- const options = { code, details };
41
- if (code === "CLIENT_OUTDATED") {
42
- options.minVersion = inner.minVersion;
43
- options.clientVersion = inner.clientVersion;
44
- options.client = inner.client;
45
- options.upgrade = inner.upgrade;
46
- options.docs = inner.docs;
47
- }
48
- throw new ApiError(message, options);
34
+ function extractApiErrorOptions(error, fallbackMessage) {
35
+ if (!error || typeof error !== "object" || !("error" in error) || typeof error.error !== "object") {
36
+ return null;
49
37
  }
38
+ const inner = error.error;
39
+ const message = inner.message || fallbackMessage;
40
+ const code = inner.code;
41
+ const details = inner.details?.errors;
42
+ const options = { code, details };
43
+ if (code === "CLIENT_OUTDATED") {
44
+ options.minVersion = inner.minVersion;
45
+ options.clientVersion = inner.clientVersion;
46
+ options.client = inner.client;
47
+ options.upgrade = inner.upgrade;
48
+ options.docs = inner.docs;
49
+ }
50
+ return { message, options };
51
+ }
52
+ function throwApiError(error, fallbackMessage) {
53
+ const extracted = extractApiErrorOptions(error, fallbackMessage);
54
+ if (extracted) throw new ApiError(extracted.message, extracted.options);
50
55
  throw new ApiError(fallbackMessage);
51
56
  }
57
+ function parseRetryAfter(value) {
58
+ if (!value) return void 0;
59
+ const trimmed = value.trim();
60
+ if (/^\d+$/.test(trimmed)) return parseInt(trimmed, 10);
61
+ const when = Date.parse(trimmed);
62
+ if (Number.isNaN(when)) return void 0;
63
+ return Math.max(0, Math.round((when - Date.now()) / 1e3));
64
+ }
65
+ function throwApiResponseError(response, body, fallbackMessage) {
66
+ const status = response.status;
67
+ const retryAfterSeconds = parseRetryAfter(
68
+ response.headers.get("retry-after")
69
+ );
70
+ const retryable = status === 429 || status === 503 && retryAfterSeconds !== void 0;
71
+ const extracted = extractApiErrorOptions(body, fallbackMessage);
72
+ const message = extracted?.message ?? fallbackMessage;
73
+ const options = {
74
+ ...extracted?.options ?? {},
75
+ status,
76
+ retryable,
77
+ retryAfterSeconds
78
+ };
79
+ throw new ApiError(message, options);
80
+ }
81
+ function machineReadableErrorLine(err) {
82
+ const code = err instanceof ApiError ? err.code ?? "UNKNOWN" : "UNKNOWN";
83
+ const retryable = err instanceof ApiError ? err.retryable === true : false;
84
+ const retryAfter = err instanceof ApiError && err.retryAfterSeconds !== void 0 ? ` retryAfter=${err.retryAfterSeconds}` : "";
85
+ const message = err instanceof Error ? err.message : String(err);
86
+ return `error: code=${code} retryable=${retryable}${retryAfter} message=${message}`;
87
+ }
52
88
  function handleCliError(err) {
89
+ console.error(machineReadableErrorLine(err));
53
90
  if (err instanceof ApiError && err.code === "CLIENT_OUTDATED") {
54
91
  console.error(`
55
92
  ${err.message}
@@ -59,17 +96,25 @@ ${err.message}
59
96
  `);
60
97
  process.exit(2);
61
98
  }
62
- const message = err instanceof Error ? err.message : String(err);
63
- console.error(message);
99
+ if (err instanceof ApiError && err.retryable) {
100
+ const hint = err.retryAfterSeconds !== void 0 ? ` Retry after ${err.retryAfterSeconds}s.` : " This is temporary, retry shortly.";
101
+ console.error(`${err.message}${hint}`);
102
+ process.exit(EXIT_RETRYABLE);
103
+ }
104
+ console.error(err instanceof Error ? err.message : String(err));
64
105
  process.exit(1);
65
106
  }
66
- var ApiError;
107
+ var EXIT_RETRYABLE, ApiError;
67
108
  var init_api_error = __esm({
68
109
  "src/core/api-error.ts"() {
69
110
  "use strict";
111
+ EXIT_RETRYABLE = 75;
70
112
  ApiError = class extends Error {
71
113
  code;
72
114
  details;
115
+ status;
116
+ retryable;
117
+ retryAfterSeconds;
73
118
  // Populated only for CLIENT_OUTDATED responses (HTTP 426).
74
119
  minVersion;
75
120
  clientVersion;
@@ -81,6 +126,9 @@ var init_api_error = __esm({
81
126
  this.name = "ApiError";
82
127
  this.code = options?.code;
83
128
  this.details = options?.details;
129
+ this.status = options?.status;
130
+ this.retryable = options?.retryable;
131
+ this.retryAfterSeconds = options?.retryAfterSeconds;
84
132
  this.minVersion = options?.minVersion;
85
133
  this.clientVersion = options?.clientVersion;
86
134
  this.client = options?.client;
@@ -4570,7 +4618,7 @@ ${destinationsEntries.join(",\n")}
4570
4618
  stores${collectorStr}
4571
4619
  }`;
4572
4620
  const dataPayload = JSON.stringify(dataPayloadObj, null, 2);
4573
- return { storesDeclaration, codeConfigObject, dataPayload };
4621
+ return { storesDeclaration, codeConfigObject, dataPayloadObj, dataPayload };
4574
4622
  }
4575
4623
  function generateSplitWireConfigModule(storesDeclaration, codeConfigObject, userCode) {
4576
4624
  const codeSection = userCode ? `
@@ -6698,7 +6746,7 @@ async function wt(e4, t4, o4) {
6698
6746
  }
6699
6747
  }
6700
6748
  function vt(e4, t4) {
6701
- return { timing: Math.round((Date.now() - e4.timing) / 10) / 100, source: { type: "collector", schema: "4", version: "4.2.0" }, ...t4 };
6749
+ return { timing: Math.round((Date.now() - e4.timing) / 10) / 100, source: { type: "collector", schema: "4", version: "4.3.0-next-1781171238534" }, ...t4 };
6702
6750
  }
6703
6751
  function bt(e4, t4) {
6704
6752
  if (!t4.name) throw new Error("Event name is required");
@@ -7753,7 +7801,7 @@ function toError(error) {
7753
7801
  return error instanceof Error ? error : new Error(getErrorMessage(error));
7754
7802
  }
7755
7803
  function buildSimulationResult(args) {
7756
- const { step, name, startTime, captured, usage, error } = args;
7804
+ const { step, name, startTime, captured, usage, mappingKey, error } = args;
7757
7805
  const events = (captured ?? []).filter(hasEvent).map((entry) => entry.event);
7758
7806
  const calls = usage ? Object.values(usage).flat().map((call) => ({ fn: call.fn, args: call.args, ts: call.ts })) : [];
7759
7807
  return {
@@ -7762,6 +7810,7 @@ function buildSimulationResult(args) {
7762
7810
  events,
7763
7811
  calls,
7764
7812
  duration: Date.now() - startTime,
7813
+ ...mappingKey !== void 0 ? { mappingKey } : {},
7765
7814
  ...error !== void 0 ? { error: toError(error) } : {}
7766
7815
  };
7767
7816
  }
@@ -8355,7 +8404,9 @@ async function simulateSource(configOrPath, input, options) {
8355
8404
  `Source package "${sourceConfig.package}" has no createTrigger in /dev export`
8356
8405
  );
8357
8406
  }
8358
- const flowConfig = module.wireConfig(module.__configData ?? void 0);
8407
+ const flowConfig = module.wireConfig(
8408
+ options.data ?? module.__configData ?? void 0
8409
+ );
8359
8410
  applyOverrides(flowConfig, prepared.overrides);
8360
8411
  const captured = [];
8361
8412
  flowConfig.hooks = {
@@ -8460,7 +8511,9 @@ async function simulateTransformer(configOrPath, event, options) {
8460
8511
  networkCalls
8461
8512
  },
8462
8513
  async (module) => {
8463
- const flowConfig = module.wireConfig(module.__configData ?? void 0);
8514
+ const flowConfig = module.wireConfig(
8515
+ options.data ?? module.__configData ?? void 0
8516
+ );
8464
8517
  applyOverrides(flowConfig, prepared.overrides);
8465
8518
  if (flowConfig.sources) flowConfig.sources = {};
8466
8519
  if (flowConfig.destinations) flowConfig.destinations = {};
@@ -8626,7 +8679,9 @@ async function simulateCollector(configOrPath, event, options) {
8626
8679
  networkCalls
8627
8680
  },
8628
8681
  async (module) => {
8629
- const flowConfig = module.wireConfig(module.__configData ?? void 0);
8682
+ const flowConfig = module.wireConfig(
8683
+ options.data ?? module.__configData ?? void 0
8684
+ );
8630
8685
  applyOverrides(flowConfig, prepared.overrides);
8631
8686
  if (flowConfig.sources) flowConfig.sources = {};
8632
8687
  if (flowConfig.destinations) flowConfig.destinations = {};
@@ -8729,7 +8784,9 @@ async function simulateDestination(configOrPath, event, options) {
8729
8784
  networkCalls
8730
8785
  },
8731
8786
  async (module) => {
8732
- const flowConfig = module.wireConfig(module.__configData ?? void 0);
8787
+ const flowConfig = module.wireConfig(
8788
+ options.data ?? module.__configData ?? void 0
8789
+ );
8733
8790
  applyOverrides(flowConfig, prepared.overrides);
8734
8791
  const destPkg = (prepared.flowSettings.destinations ?? {})[options.destinationId];
8735
8792
  let trackedCalls = [];
@@ -8765,6 +8822,16 @@ async function simulateDestination(configOrPath, event, options) {
8765
8822
  );
8766
8823
  }
8767
8824
  logger.info(`Simulating destination: ${options.destinationId}`);
8825
+ let mappingKey;
8826
+ const targetStepId = o("destination", options.destinationId);
8827
+ const captureMappingKey = (state) => {
8828
+ if (state.stepId === targetStepId && state.mappingKey) {
8829
+ mappingKey = state.mappingKey;
8830
+ }
8831
+ };
8832
+ if (collector.observers instanceof Set) {
8833
+ collector.observers.add(captureMappingKey);
8834
+ }
8768
8835
  await collector.push(event, {
8769
8836
  include: [options.destinationId]
8770
8837
  });
@@ -8773,7 +8840,8 @@ async function simulateDestination(configOrPath, event, options) {
8773
8840
  step: "destination",
8774
8841
  name: options.destinationId,
8775
8842
  startTime,
8776
- usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0
8843
+ usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0,
8844
+ mappingKey
8777
8845
  });
8778
8846
  },
8779
8847
  (error) => buildSimulationResult({
@@ -9498,13 +9566,21 @@ init_cache();
9498
9566
  async function runPipeline(options) {
9499
9567
  const { bundlePath, port, logger, loggerConfig, api } = options;
9500
9568
  let configVersion;
9569
+ const configFrozen = readConfigFrozen();
9501
9570
  if (api) {
9502
9571
  await injectSecrets(api, logger);
9503
9572
  }
9504
9573
  logger.info(`walkeros/flow v${VERSION}`);
9505
9574
  logger.info(`Instance: ${getInstanceId()}`);
9575
+ if (configFrozen) {
9576
+ logger.info("Config frozen: hot-swap and heartbeat disabled");
9577
+ }
9506
9578
  const healthServer = await createHealthServer(port, logger);
9507
- const telemetryObservers = buildTelemetryObservers(api?.flowId ?? "flow");
9579
+ const observeLevel = readObserveLevel(logger);
9580
+ const telemetryObservers = buildTelemetryObservers(
9581
+ api?.flowId ?? "flow",
9582
+ observeLevel
9583
+ );
9508
9584
  const runtimeConfig = { port };
9509
9585
  let handle;
9510
9586
  try {
@@ -9533,20 +9609,24 @@ async function runPipeline(options) {
9533
9609
  const ingestToken = process.env.WALKEROS_INGEST_TOKEN;
9534
9610
  const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
9535
9611
  if (observerBase && ingestToken && deploymentId) {
9536
- tracePoller = createTracePoller(
9537
- {
9538
- url: `${observerBase}/trace/${deploymentId}`,
9539
- token: ingestToken,
9540
- intervalMs: 15e3
9541
- },
9542
- logger
9543
- );
9544
- tracePoller.start();
9545
- logger.info("Trace poller: active (every 15s)");
9612
+ if (observeLevel === "trace") {
9613
+ logger.info("Trace poller: skipped (observe level is trace)");
9614
+ } else {
9615
+ tracePoller = createTracePoller(
9616
+ {
9617
+ url: `${observerBase}/trace/${deploymentId}`,
9618
+ token: ingestToken,
9619
+ intervalMs: 15e3
9620
+ },
9621
+ logger
9622
+ );
9623
+ tracePoller.start();
9624
+ logger.info("Trace poller: active (every 15s)");
9625
+ }
9546
9626
  }
9547
9627
  let currentBundleCleanup;
9548
9628
  let currentConfigPath;
9549
- if (api) {
9629
+ if (api && !configFrozen) {
9550
9630
  heartbeat = createHeartbeat(
9551
9631
  {
9552
9632
  appUrl: api.appUrl,
@@ -9663,17 +9743,39 @@ async function runPipeline(options) {
9663
9743
  await new Promise(() => {
9664
9744
  });
9665
9745
  }
9666
- function buildTelemetryObservers(flowId) {
9746
+ var OBSERVE_LEVELS = [
9747
+ "off",
9748
+ "standard",
9749
+ "trace"
9750
+ ];
9751
+ function readConfigFrozen() {
9752
+ const raw = process.env.WALKEROS_CONFIG_FROZEN;
9753
+ return raw === "1" || raw === "true";
9754
+ }
9755
+ function readObserveLevel(logger) {
9756
+ const raw = process.env.WALKEROS_OBSERVE_LEVEL;
9757
+ if (raw === void 0 || raw === "") return void 0;
9758
+ const level = OBSERVE_LEVELS.find((candidate) => candidate === raw);
9759
+ if (!level) {
9760
+ logger.warn(
9761
+ `Ignoring invalid WALKEROS_OBSERVE_LEVEL "${raw}" (expected off, standard, or trace)`
9762
+ );
9763
+ return void 0;
9764
+ }
9765
+ return level;
9766
+ }
9767
+ function buildTelemetryObservers(flowId, observeLevel) {
9667
9768
  const base = process.env.WALKEROS_OBSERVER_URL;
9668
9769
  const token = process.env.WALKEROS_INGEST_TOKEN;
9669
9770
  const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
9670
9771
  if (!base || !token || !deploymentId) return void 0;
9671
9772
  const url = `${base}/ingest/${deploymentId}`;
9672
9773
  const emit = ht({ url, token });
9774
+ const observe = observeLevel !== void 0 ? { level: observeLevel } : void 0;
9673
9775
  return [
9674
9776
  ft(
9675
9777
  emit,
9676
- () => pt({ flowId, traceUntil: mt() })
9778
+ () => pt({ flowId, observe, traceUntil: mt() })
9677
9779
  )
9678
9780
  ];
9679
9781
  }
@@ -11940,7 +12042,7 @@ function validateMapping(input) {
11940
12042
  // src/commands/validate/validators/entry.ts
11941
12043
  init_dist();
11942
12044
  import Ajv from "ajv";
11943
- var CLIENT_HEADER = "walkeros-cli/4.2.0";
12045
+ var CLIENT_HEADER = "walkeros-cli/4.3.0-next-1781171238534";
11944
12046
  var SECTIONS = ["destinations", "sources", "transformers"];
11945
12047
  function resolveEntry(path19, flowConfig) {
11946
12048
  const flows = flowConfig.flows;
@@ -12575,6 +12677,7 @@ init_sse();
12575
12677
  init_cli_logger();
12576
12678
  init_output();
12577
12679
  init_flows();
12680
+ import { randomUUID as randomUUID2 } from "crypto";
12578
12681
  async function resolveSettingsId(options) {
12579
12682
  const flow = await getFlow({
12580
12683
  flowId: options.flowId,
@@ -12600,8 +12703,9 @@ async function getAvailableFlowNames(options) {
12600
12703
  const settings = flow.settings;
12601
12704
  return settings?.map((c2) => c2.name) ?? [];
12602
12705
  }
12706
+ var DEFAULT_DEPLOY_WAIT_MS = 12 * 60 * 1e3;
12603
12707
  async function streamDeploymentStatus(projectId, deploymentId, options) {
12604
- const timeoutMs = options.timeout ?? 12e4;
12708
+ const timeoutMs = options.timeout ?? DEFAULT_DEPLOY_WAIT_MS;
12605
12709
  const response = await apiFetch(
12606
12710
  `/api/projects/${projectId}/deployments/${deploymentId}/stream`,
12607
12711
  {
@@ -12660,7 +12764,10 @@ async function deploy(options) {
12660
12764
  }
12661
12765
  const { data, error } = await client.POST(
12662
12766
  "/api/projects/{projectId}/flows/{flowId}/deploy",
12663
- { params: { path: { projectId, flowId: options.flowId } } }
12767
+ {
12768
+ params: { path: { projectId, flowId: options.flowId } },
12769
+ headers: { "Idempotency-Key": randomUUID2() }
12770
+ }
12664
12771
  );
12665
12772
  if (error) {
12666
12773
  try {
@@ -12690,13 +12797,38 @@ Available: ${names.join(", ")}`,
12690
12797
  }
12691
12798
  async function deploySettings(options) {
12692
12799
  const { flowId, projectId, settingsId } = options;
12693
- const response = await apiFetch(
12800
+ const triggerDeploy = () => apiFetch(
12694
12801
  `/api/projects/${projectId}/flows/${flowId}/settings/${settingsId}/deploy`,
12695
- { method: "POST" }
12802
+ { method: "POST", headers: { "Idempotency-Key": randomUUID2() } }
12696
12803
  );
12804
+ let response = await triggerDeploy();
12805
+ if (!response.ok && options.wait) {
12806
+ let retryBody = {};
12807
+ try {
12808
+ retryBody = await response.clone().json();
12809
+ } catch {
12810
+ retryBody = {};
12811
+ }
12812
+ try {
12813
+ throwApiResponseError(
12814
+ response,
12815
+ retryBody,
12816
+ `Deploy failed (${response.status})`
12817
+ );
12818
+ } catch (e4) {
12819
+ if (e4 instanceof ApiError && e4.retryable) {
12820
+ const waitSeconds = Math.min(e4.retryAfterSeconds ?? 5, 60);
12821
+ options.onStatus?.("rate_limited", `retrying in ${waitSeconds}s`);
12822
+ await new Promise((resolve4) => setTimeout(resolve4, waitSeconds * 1e3));
12823
+ response = await triggerDeploy();
12824
+ } else {
12825
+ throw e4;
12826
+ }
12827
+ }
12828
+ }
12697
12829
  if (!response.ok) {
12698
12830
  const body = await response.json().catch(() => ({}));
12699
- throwApiError(body, `Deploy failed (${response.status})`);
12831
+ throwApiResponseError(response, body, `Deploy failed (${response.status})`);
12700
12832
  }
12701
12833
  const data = await response.json();
12702
12834
  if (!options.wait) return data;
@@ -12708,16 +12840,20 @@ async function deploySettings(options) {
12708
12840
  return { ...data, ...result };
12709
12841
  }
12710
12842
  var statusLabels = {
12711
- bundling: "Building bundle...",
12712
- "bundling:building": "Building bundle...",
12713
- "bundling:publishing": "Publishing to web...",
12714
- deploying: "Deploying container...",
12843
+ "deploying:building": "Building bundle...",
12844
+ "deploying:publishing": "Publishing to web...",
12715
12845
  "deploying:provisioning": "Provisioning container...",
12716
12846
  "deploying:starting": "Starting container...",
12847
+ deploying: "Deploying...",
12717
12848
  active: "Container is live",
12718
12849
  published: "Published",
12850
+ stopped: "Stopped",
12719
12851
  failed: "Deployment failed"
12720
12852
  };
12853
+ function renderStatusLabel(status, substatus) {
12854
+ const key = substatus ? `${status}:${substatus}` : status;
12855
+ return statusLabels[key] || statusLabels[status] || `Status: ${status}`;
12856
+ }
12721
12857
  async function deployCommand(flowId, options) {
12722
12858
  const log = createCLILogger(options);
12723
12859
  const timeoutMs = options.timeout ? parseInt(options.timeout, 10) * 1e3 : void 0;
@@ -12729,10 +12865,7 @@ async function deployCommand(flowId, options) {
12729
12865
  wait: options.wait !== false,
12730
12866
  timeout: timeoutMs,
12731
12867
  onStatus: options.json ? void 0 : (status, substatus) => {
12732
- const key = substatus ? `${status}:${substatus}` : status;
12733
- log.info(
12734
- statusLabels[key] || statusLabels[status] || `Status: ${status}`
12735
- );
12868
+ log.info(renderStatusLabel(status, substatus));
12736
12869
  }
12737
12870
  });
12738
12871
  if (options.json) {
@@ -12747,7 +12880,7 @@ async function deployCommand(flowId, options) {
12747
12880
  } else if (r5.status === "failed") {
12748
12881
  log.error(`Failed: ${r5.errorMessage || "Unknown error"}`);
12749
12882
  process.exit(1);
12750
- } else if (r5.status === "bundling") {
12883
+ } else if (r5.status === "deploying") {
12751
12884
  log.info(`Deployment started: ${r5.deploymentId} (${r5.type})`);
12752
12885
  } else {
12753
12886
  log.info(`Status: ${r5.status}`);
@@ -12902,20 +13035,16 @@ async function createDeployCommand(config, options) {
12902
13035
  log.info(`Deployment created: ${result.id}`);
12903
13036
  log.info(` Slug: ${result.slug}`);
12904
13037
  log.info(` Type: ${result.type}`);
12905
- if (result.deployToken) {
12906
- log.info(` Token: ${result.deployToken}`);
12907
- log.warn(" Save this token \u2014 it will not be shown again.");
12908
- }
12909
13038
  log.info("");
12910
13039
  log.info("Run locally:");
12911
13040
  log.info(
12912
13041
  ` walkeros run ${isRemoteFlow ? "flow.json" : config} --deploy ${result.id}`
12913
13042
  );
12914
13043
  log.info("");
12915
- log.info("Docker:");
12916
- log.info(
12917
- ` docker run -e WALKEROS_DEPLOY_TOKEN=${result.deployToken ?? "<token>"} \\`
12918
- );
13044
+ log.info("Create a deploy token for this flow in the app");
13045
+ log.info("(Settings, Self-hosted deploy token) and set it as");
13046
+ log.info("WALKEROS_DEPLOY_TOKEN, then run with Docker:");
13047
+ log.info(" docker run -e WALKEROS_DEPLOY_TOKEN \\");
12919
13048
  log.info(" -e WALKEROS_APP_URL=https://app.walkeros.io \\");
12920
13049
  log.info(" walkeros/flow:latest");
12921
13050
  } catch (err) {
@@ -13231,7 +13360,7 @@ deployCmd.command("create [config]").description(
13231
13360
  });
13232
13361
  deployCmd.command("start <flowId>").description("Deploy a flow to walkerOS cloud (auto-detects web or server)").option("--project <id>", "project ID (defaults to WALKEROS_PROJECT_ID)").option("-f, --flow <name>", "flow name for multi-config flows").option("--no-wait", "do not wait for deployment to complete").option(
13233
13362
  "--timeout <seconds>",
13234
- "timeout for deployment polling (default: 120)"
13363
+ "timeout in seconds for deployment polling (default: 720, the 12-minute server deploy budget)"
13235
13364
  ).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) => {
13236
13365
  await deployCommand(flowId, options);
13237
13366
  });