@walkeros/cli 4.2.0-next-1781000333052 → 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,6 +1,38 @@
1
1
  # @walkeros/cli
2
2
 
3
- ## 4.2.0-next-1781000333052
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
+
35
+ ## 4.2.0
4
36
 
5
37
  ### Minor Changes
6
38
 
@@ -47,7 +79,7 @@
47
79
  custom domains, entitlements, LLM settings, chat sessions, MCP tokens,
48
80
  runners, and the package catalog. No runtime behavior change; clients gain
49
81
  accurate types for these endpoints.
50
- - 18c9469: Browser flow bundles are now emitted as an IIFE so all internal code
82
+ - 5b1a134: Browser flow bundles are now emitted as an IIFE so all internal code
51
83
  stays inside a private scope. Previously the bundled helper functions could
52
84
  leak onto the global `window` object and collide with other scripts on the
53
85
  page, such as Google Analytics or a consent manager. Server bundles are
@@ -86,7 +118,7 @@
86
118
  without a trace poll. When the telemetry options omit `traceUrl`, the bundle
87
119
  emits at a fixed level with no polling, suited to short-lived, URL-opted-in
88
120
  sessions. Bundles that pass `traceUrl` keep the existing poll behavior.
89
- - f1c74e8: New `@walkeros/transformer-validate` transformer validates events
121
+ - 23d4b86: New `@walkeros/transformer-validate` transformer validates events
90
122
  against JSON Schema contracts. It runs in both web and server flows, supports
91
123
  strict and pass modes, and writes the verdict and error list to configurable
92
124
  paths so you can gate or observe event quality.
@@ -99,7 +131,7 @@
99
131
  examples against the resolved contract.
100
132
 
101
133
  - Updated dependencies [76d32c1]
102
- - Updated dependencies [18c9469]
134
+ - Updated dependencies [5b1a134]
103
135
  - Updated dependencies [908d6f0]
104
136
  - Updated dependencies [654ba38]
105
137
  - Updated dependencies [c27d3c1]
@@ -114,14 +146,15 @@
114
146
  - Updated dependencies [654ba38]
115
147
  - Updated dependencies [6a72a32]
116
148
  - Updated dependencies [3eb2467]
117
- - Updated dependencies [7d8ebf9]
118
- - Updated dependencies [f1c74e8]
149
+ - Updated dependencies [5b1a134]
150
+ - Updated dependencies [23d4b86]
119
151
  - Updated dependencies [18c9469]
120
- - @walkeros/core@4.2.0-next-1781000333052
121
- - @walkeros/collector@4.2.0-next-1781000333052
122
- - @walkeros/server-core@4.2.0-next-1781000333052
123
- - @walkeros/server-destination-api@4.2.0-next-1781000333052
124
- - @walkeros/transformer-validate@4.2.0-next-1781000333052
152
+ - Updated dependencies [0cad016]
153
+ - @walkeros/core@4.2.0
154
+ - @walkeros/collector@4.2.0
155
+ - @walkeros/server-core@4.2.0
156
+ - @walkeros/server-destination-api@4.2.0
157
+ - @walkeros/transformer-validate@4.2.0
125
158
 
126
159
  ## 4.1.2
127
160
 
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;
@@ -2770,9 +2818,8 @@ function validateReference(type, name, ref) {
2770
2818
  return;
2771
2819
  }
2772
2820
  const hasPackage = !!ref.package;
2773
- const hasInlineCode = isInlineCode(ref.code);
2774
2821
  const hasCode = hasCodeReference(ref.code);
2775
- if (hasPackage && hasInlineCode) {
2822
+ if (hasPackage && hasCode) {
2776
2823
  throw new Error(
2777
2824
  `${type} "${name}": Cannot specify both package and code. Use one or the other.`
2778
2825
  );
@@ -2812,13 +2859,13 @@ function validateStoreReferences(flowSettings, storeIds) {
2812
2859
  collectRefs(component, `${section}.${id}`);
2813
2860
  }
2814
2861
  }
2815
- for (const { ref, location: location2 } of refs) {
2816
- if (!storeIds.has(ref)) {
2817
- const available = storeIds.size > 0 ? `Available stores: ${Array.from(storeIds).join(", ")}` : "No stores defined";
2818
- throw new Error(
2819
- `Store reference "$store.${ref}" in ${location2} \u2014 store "${ref}" not found. ${available}`
2820
- );
2821
- }
2862
+ const missing = refs.filter(({ ref }) => !storeIds.has(ref));
2863
+ if (missing.length > 0) {
2864
+ const available = storeIds.size > 0 ? `Available stores: ${Array.from(storeIds).join(", ")}` : "No stores defined";
2865
+ const details = missing.map(
2866
+ ({ ref, location: location2 }) => `"$store.${ref}" in ${location2} (store "${ref}" not found)`
2867
+ ).join("; ");
2868
+ throw new Error(`Invalid store references: ${details}. ${available}`);
2822
2869
  }
2823
2870
  }
2824
2871
  var VALID_JS_IDENTIFIER;
@@ -4571,7 +4618,7 @@ ${destinationsEntries.join(",\n")}
4571
4618
  stores${collectorStr}
4572
4619
  }`;
4573
4620
  const dataPayload = JSON.stringify(dataPayloadObj, null, 2);
4574
- return { storesDeclaration, codeConfigObject, dataPayload };
4621
+ return { storesDeclaration, codeConfigObject, dataPayloadObj, dataPayload };
4575
4622
  }
4576
4623
  function generateSplitWireConfigModule(storesDeclaration, codeConfigObject, userCode) {
4577
4624
  const codeSection = userCode ? `
@@ -6699,7 +6746,7 @@ async function wt(e4, t4, o4) {
6699
6746
  }
6700
6747
  }
6701
6748
  function vt(e4, t4) {
6702
- return { timing: Math.round((Date.now() - e4.timing) / 10) / 100, source: { type: "collector", schema: "4", version: "4.2.0-next-1781000333052" }, ...t4 };
6749
+ return { timing: Math.round((Date.now() - e4.timing) / 10) / 100, source: { type: "collector", schema: "4", version: "4.3.0-next-1781171238534" }, ...t4 };
6703
6750
  }
6704
6751
  function bt(e4, t4) {
6705
6752
  if (!t4.name) throw new Error("Event name is required");
@@ -7754,7 +7801,7 @@ function toError(error) {
7754
7801
  return error instanceof Error ? error : new Error(getErrorMessage(error));
7755
7802
  }
7756
7803
  function buildSimulationResult(args) {
7757
- const { step, name, startTime, captured, usage, error } = args;
7804
+ const { step, name, startTime, captured, usage, mappingKey, error } = args;
7758
7805
  const events = (captured ?? []).filter(hasEvent).map((entry) => entry.event);
7759
7806
  const calls = usage ? Object.values(usage).flat().map((call) => ({ fn: call.fn, args: call.args, ts: call.ts })) : [];
7760
7807
  return {
@@ -7763,6 +7810,7 @@ function buildSimulationResult(args) {
7763
7810
  events,
7764
7811
  calls,
7765
7812
  duration: Date.now() - startTime,
7813
+ ...mappingKey !== void 0 ? { mappingKey } : {},
7766
7814
  ...error !== void 0 ? { error: toError(error) } : {}
7767
7815
  };
7768
7816
  }
@@ -8356,7 +8404,9 @@ async function simulateSource(configOrPath, input, options) {
8356
8404
  `Source package "${sourceConfig.package}" has no createTrigger in /dev export`
8357
8405
  );
8358
8406
  }
8359
- const flowConfig = module.wireConfig(module.__configData ?? void 0);
8407
+ const flowConfig = module.wireConfig(
8408
+ options.data ?? module.__configData ?? void 0
8409
+ );
8360
8410
  applyOverrides(flowConfig, prepared.overrides);
8361
8411
  const captured = [];
8362
8412
  flowConfig.hooks = {
@@ -8461,7 +8511,9 @@ async function simulateTransformer(configOrPath, event, options) {
8461
8511
  networkCalls
8462
8512
  },
8463
8513
  async (module) => {
8464
- const flowConfig = module.wireConfig(module.__configData ?? void 0);
8514
+ const flowConfig = module.wireConfig(
8515
+ options.data ?? module.__configData ?? void 0
8516
+ );
8465
8517
  applyOverrides(flowConfig, prepared.overrides);
8466
8518
  if (flowConfig.sources) flowConfig.sources = {};
8467
8519
  if (flowConfig.destinations) flowConfig.destinations = {};
@@ -8627,7 +8679,9 @@ async function simulateCollector(configOrPath, event, options) {
8627
8679
  networkCalls
8628
8680
  },
8629
8681
  async (module) => {
8630
- const flowConfig = module.wireConfig(module.__configData ?? void 0);
8682
+ const flowConfig = module.wireConfig(
8683
+ options.data ?? module.__configData ?? void 0
8684
+ );
8631
8685
  applyOverrides(flowConfig, prepared.overrides);
8632
8686
  if (flowConfig.sources) flowConfig.sources = {};
8633
8687
  if (flowConfig.destinations) flowConfig.destinations = {};
@@ -8730,7 +8784,9 @@ async function simulateDestination(configOrPath, event, options) {
8730
8784
  networkCalls
8731
8785
  },
8732
8786
  async (module) => {
8733
- const flowConfig = module.wireConfig(module.__configData ?? void 0);
8787
+ const flowConfig = module.wireConfig(
8788
+ options.data ?? module.__configData ?? void 0
8789
+ );
8734
8790
  applyOverrides(flowConfig, prepared.overrides);
8735
8791
  const destPkg = (prepared.flowSettings.destinations ?? {})[options.destinationId];
8736
8792
  let trackedCalls = [];
@@ -8766,6 +8822,16 @@ async function simulateDestination(configOrPath, event, options) {
8766
8822
  );
8767
8823
  }
8768
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
+ }
8769
8835
  await collector.push(event, {
8770
8836
  include: [options.destinationId]
8771
8837
  });
@@ -8774,7 +8840,8 @@ async function simulateDestination(configOrPath, event, options) {
8774
8840
  step: "destination",
8775
8841
  name: options.destinationId,
8776
8842
  startTime,
8777
- usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0
8843
+ usage: trackedCalls.length ? { [options.destinationId]: trackedCalls } : void 0,
8844
+ mappingKey
8778
8845
  });
8779
8846
  },
8780
8847
  (error) => buildSimulationResult({
@@ -9499,13 +9566,21 @@ init_cache();
9499
9566
  async function runPipeline(options) {
9500
9567
  const { bundlePath, port, logger, loggerConfig, api } = options;
9501
9568
  let configVersion;
9569
+ const configFrozen = readConfigFrozen();
9502
9570
  if (api) {
9503
9571
  await injectSecrets(api, logger);
9504
9572
  }
9505
9573
  logger.info(`walkeros/flow v${VERSION}`);
9506
9574
  logger.info(`Instance: ${getInstanceId()}`);
9575
+ if (configFrozen) {
9576
+ logger.info("Config frozen: hot-swap and heartbeat disabled");
9577
+ }
9507
9578
  const healthServer = await createHealthServer(port, logger);
9508
- const telemetryObservers = buildTelemetryObservers(api?.flowId ?? "flow");
9579
+ const observeLevel = readObserveLevel(logger);
9580
+ const telemetryObservers = buildTelemetryObservers(
9581
+ api?.flowId ?? "flow",
9582
+ observeLevel
9583
+ );
9509
9584
  const runtimeConfig = { port };
9510
9585
  let handle;
9511
9586
  try {
@@ -9534,20 +9609,24 @@ async function runPipeline(options) {
9534
9609
  const ingestToken = process.env.WALKEROS_INGEST_TOKEN;
9535
9610
  const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
9536
9611
  if (observerBase && ingestToken && deploymentId) {
9537
- tracePoller = createTracePoller(
9538
- {
9539
- url: `${observerBase}/trace/${deploymentId}`,
9540
- token: ingestToken,
9541
- intervalMs: 15e3
9542
- },
9543
- logger
9544
- );
9545
- tracePoller.start();
9546
- 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
+ }
9547
9626
  }
9548
9627
  let currentBundleCleanup;
9549
9628
  let currentConfigPath;
9550
- if (api) {
9629
+ if (api && !configFrozen) {
9551
9630
  heartbeat = createHeartbeat(
9552
9631
  {
9553
9632
  appUrl: api.appUrl,
@@ -9664,17 +9743,39 @@ async function runPipeline(options) {
9664
9743
  await new Promise(() => {
9665
9744
  });
9666
9745
  }
9667
- 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) {
9668
9768
  const base = process.env.WALKEROS_OBSERVER_URL;
9669
9769
  const token = process.env.WALKEROS_INGEST_TOKEN;
9670
9770
  const deploymentId = process.env.WALKEROS_DEPLOYMENT_ID;
9671
9771
  if (!base || !token || !deploymentId) return void 0;
9672
9772
  const url = `${base}/ingest/${deploymentId}`;
9673
9773
  const emit = ht({ url, token });
9774
+ const observe = observeLevel !== void 0 ? { level: observeLevel } : void 0;
9674
9775
  return [
9675
9776
  ft(
9676
9777
  emit,
9677
- () => pt({ flowId, traceUntil: mt() })
9778
+ () => pt({ flowId, observe, traceUntil: mt() })
9678
9779
  )
9679
9780
  ];
9680
9781
  }
@@ -11402,7 +11503,8 @@ function n4(e4) {
11402
11503
  return i3.set(r5, o4), o4;
11403
11504
  }
11404
11505
  function s5(e4) {
11405
- return "object" == typeof e4 && null !== e4 && "events" in e4 && "object" == typeof e4.events;
11506
+ if ("object" != typeof e4 || null === e4) return false;
11507
+ return "events" in e4 && "object" == typeof e4.events && null !== e4.events || "schema" in e4;
11406
11508
  }
11407
11509
  function o3(e4, t4, r5) {
11408
11510
  return e4[t4]?.[r5] ?? e4[t4]?.["*"] ?? e4["*"]?.[r5] ?? e4["*"]?.["*"];
@@ -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-next-1781000333052";
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
  });