@walkeros/cli 3.4.1-next-1776790594143 → 3.4.2

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
@@ -170,6 +170,26 @@ function deleteConfig() {
170
170
  }
171
171
  return false;
172
172
  }
173
+ function setFeedbackPreference(anonymous) {
174
+ const config = readConfig();
175
+ if (!config) return;
176
+ writeConfig({ ...config, anonymousFeedback: anonymous });
177
+ }
178
+ function getFeedbackPreference() {
179
+ const config = readConfig();
180
+ return config?.anonymousFeedback;
181
+ }
182
+ function setDefaultProject(projectId) {
183
+ const config = readConfig();
184
+ if (!config) {
185
+ throw new Error("Not authenticated. Run `walkeros login` first.");
186
+ }
187
+ writeConfig({ ...config, defaultProjectId: projectId });
188
+ }
189
+ function getDefaultProject() {
190
+ const config = readConfig();
191
+ return config?.defaultProjectId ?? null;
192
+ }
173
193
  function resolveToken() {
174
194
  const envToken = process.env.WALKEROS_TOKEN;
175
195
  if (envToken) return { token: envToken, source: "env" };
@@ -579,8 +599,11 @@ function resolveRunToken() {
579
599
  return resolveDeployToken() ?? resolveToken()?.token ?? null;
580
600
  }
581
601
  function requireProjectId() {
582
- const projectId = process.env.WALKEROS_PROJECT_ID;
583
- if (!projectId) throw new Error("WALKEROS_PROJECT_ID not set.");
602
+ const projectId = process.env.WALKEROS_PROJECT_ID || getDefaultProject();
603
+ if (!projectId)
604
+ throw new Error(
605
+ "No project selected. Set WALKEROS_PROJECT_ID or configure a default project."
606
+ );
584
607
  return projectId;
585
608
  }
586
609
  var init_auth = __esm({
@@ -4595,6 +4618,37 @@ async function resolveBundle(bundleEnv) {
4595
4618
 
4596
4619
  // src/runtime/config-fetcher.ts
4597
4620
  init_http();
4621
+
4622
+ // src/runtime/runner-auth-error.ts
4623
+ var RunnerAuthError = class extends Error {
4624
+ constructor(status, reason, code, message) {
4625
+ super(message);
4626
+ this.status = status;
4627
+ this.reason = reason;
4628
+ this.code = code;
4629
+ this.name = "RunnerAuthError";
4630
+ }
4631
+ };
4632
+ function isAppErrorBody(value) {
4633
+ return typeof value === "object" && value !== null && "error" in value && typeof value.error === "object";
4634
+ }
4635
+ async function throwIfRunnerAuthFailure(res) {
4636
+ if (res.status !== 401 && res.status !== 403) return;
4637
+ let code = null;
4638
+ let message = res.statusText;
4639
+ try {
4640
+ const body = await res.clone().json();
4641
+ if (isAppErrorBody(body) && body.error) {
4642
+ if (typeof body.error.code === "string") code = body.error.code;
4643
+ if (typeof body.error.message === "string") message = body.error.message;
4644
+ }
4645
+ } catch {
4646
+ }
4647
+ const reason = res.status === 401 ? "unauthorised" : code === "FORBIDDEN_FLOW" ? "flow" : code === "FORBIDDEN_SCOPE" ? "scope" : "forbidden";
4648
+ throw new RunnerAuthError(res.status, reason, code, message);
4649
+ }
4650
+
4651
+ // src/runtime/config-fetcher.ts
4598
4652
  async function fetchConfig(options) {
4599
4653
  const url = `${options.appUrl}/api/projects/${options.projectId}/flows/${options.flowId}`;
4600
4654
  const headers = mergeAuthHeaders(
@@ -4608,12 +4662,8 @@ async function fetchConfig(options) {
4608
4662
  if (response.status === 304) {
4609
4663
  return { changed: false };
4610
4664
  }
4665
+ await throwIfRunnerAuthFailure(response);
4611
4666
  if (!response.ok) {
4612
- if (response.status === 401 || response.status === 403) {
4613
- throw new Error(
4614
- `Config fetch failed (${response.status}): token may have expired \u2014 redeploy to rotate`
4615
- );
4616
- }
4617
4667
  throw new Error(
4618
4668
  `Config fetch failed: ${response.status} ${response.statusText}`
4619
4669
  );
@@ -5069,6 +5119,7 @@ async function fetchSecrets(options) {
5069
5119
  const res = await fetch(url, {
5070
5120
  headers: mergeAuthHeaders(token, { "Content-Type": "application/json" })
5071
5121
  });
5122
+ await throwIfRunnerAuthFailure(res);
5072
5123
  if (!res.ok) {
5073
5124
  throw new SecretsHttpError(res.status, res.statusText);
5074
5125
  }
@@ -6179,6 +6230,8 @@ init_cli_logger();
6179
6230
  init_config_file();
6180
6231
  import { hostname } from "os";
6181
6232
  var POLL_TIMEOUT_BUFFER_MS = 5e3;
6233
+ var DEFAULT_POLL_TIMEOUT_MS = 6e4;
6234
+ var DEFAULT_POLL_INTERVAL_MS = 5e3;
6182
6235
  async function openInBrowser(url) {
6183
6236
  const { default: open } = await import("open");
6184
6237
  await open(url);
@@ -6204,25 +6257,143 @@ async function loginCommand(options) {
6204
6257
  process.exit(1);
6205
6258
  }
6206
6259
  }
6207
- async function login(options = {}) {
6260
+ async function requestDeviceCode(options = {}) {
6208
6261
  const appUrl = options.url || resolveAppUrl();
6209
6262
  const f2 = options.fetch ?? globalThis.fetch;
6210
- const codeResponse = await f2(`${appUrl}/api/auth/device/code`, {
6263
+ const response = await f2(`${appUrl}/api/auth/device/code`, {
6211
6264
  method: "POST",
6212
6265
  headers: { "Content-Type": "application/json" },
6213
6266
  body: JSON.stringify({})
6214
6267
  });
6215
- if (!codeResponse.ok) {
6268
+ if (!response.ok) {
6269
+ throw new Error("Failed to request device code");
6270
+ }
6271
+ const data = await response.json();
6272
+ return {
6273
+ deviceCode: data.deviceCode,
6274
+ userCode: data.userCode,
6275
+ verificationUri: data.verificationUri,
6276
+ verificationUriComplete: data.verificationUriComplete,
6277
+ expiresIn: data.expiresIn,
6278
+ interval: data.interval
6279
+ };
6280
+ }
6281
+ async function pollForToken(deviceCode, options = {}) {
6282
+ const appUrl = options.url || resolveAppUrl();
6283
+ const f2 = options.fetch ?? globalThis.fetch;
6284
+ const timeoutMs = options.timeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
6285
+ let intervalMs = options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
6286
+ const deadline = Date.now() + timeoutMs;
6287
+ while (Date.now() < deadline) {
6288
+ await new Promise((r2) => setTimeout(r2, intervalMs));
6289
+ if (Date.now() >= deadline) break;
6290
+ const remaining = Math.max(1, deadline - Date.now());
6291
+ const controller = new AbortController();
6292
+ const timeoutHandle = setTimeout(() => controller.abort(), remaining);
6293
+ let tokenResponse;
6294
+ try {
6295
+ tokenResponse = await f2(`${appUrl}/api/auth/device/token`, {
6296
+ method: "POST",
6297
+ headers: { "Content-Type": "application/json" },
6298
+ body: JSON.stringify({ deviceCode, hostname: hostname() }),
6299
+ signal: controller.signal
6300
+ });
6301
+ } catch (err) {
6302
+ clearTimeout(timeoutHandle);
6303
+ if (err instanceof Error && err.name === "AbortError") {
6304
+ break;
6305
+ }
6306
+ throw err;
6307
+ } finally {
6308
+ clearTimeout(timeoutHandle);
6309
+ }
6310
+ const data = await safeJsonParse(tokenResponse);
6311
+ if (data === MALFORMED) {
6312
+ return {
6313
+ success: false,
6314
+ status: "error",
6315
+ error: "Server returned malformed response"
6316
+ };
6317
+ }
6318
+ if (tokenResponse.ok) {
6319
+ const token = data.token;
6320
+ const email = data.email;
6321
+ if (typeof token === "string" && token.length > 0) {
6322
+ if (typeof email !== "string" || email.length === 0) {
6323
+ return {
6324
+ success: false,
6325
+ status: "error",
6326
+ error: "Server returned malformed response (missing email)"
6327
+ };
6328
+ }
6329
+ writeConfig({ token, email, appUrl });
6330
+ const configPath = getConfigPath();
6331
+ return {
6332
+ success: true,
6333
+ status: "authenticated",
6334
+ email,
6335
+ configPath
6336
+ };
6337
+ }
6338
+ if (token !== void 0) {
6339
+ return {
6340
+ success: false,
6341
+ status: "error",
6342
+ error: "Server returned malformed response (invalid token type)"
6343
+ };
6344
+ }
6345
+ continue;
6346
+ }
6347
+ if (data.error === "authorization_pending") continue;
6348
+ if (data.error === "slow_down") {
6349
+ intervalMs += 5e3;
6350
+ continue;
6351
+ }
6352
+ const errField = data.error;
6353
+ let errorMsg;
6354
+ if (typeof errField === "string") {
6355
+ errorMsg = errField;
6356
+ } else if (errField && typeof errField === "object" && "message" in errField && typeof errField.message === "string") {
6357
+ errorMsg = errField.message;
6358
+ } else {
6359
+ errorMsg = "Authorization failed";
6360
+ }
6361
+ return { success: false, status: "error", error: errorMsg };
6362
+ }
6363
+ return { success: false, status: "pending" };
6364
+ }
6365
+ var MALFORMED = /* @__PURE__ */ Symbol("malformed-json");
6366
+ async function safeJsonParse(response) {
6367
+ try {
6368
+ const parsed = await response.json();
6369
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
6370
+ return parsed;
6371
+ }
6372
+ return MALFORMED;
6373
+ } catch {
6374
+ return MALFORMED;
6375
+ }
6376
+ }
6377
+ async function login(options = {}) {
6378
+ const fetchOption = options.fetch ?? globalThis.fetch;
6379
+ const urlOption = options.url;
6380
+ let codeResult;
6381
+ try {
6382
+ codeResult = await requestDeviceCode({
6383
+ url: urlOption,
6384
+ fetch: fetchOption
6385
+ });
6386
+ } catch {
6216
6387
  return { success: false, error: "Failed to request device code" };
6217
6388
  }
6218
6389
  const {
6219
- deviceCode,
6220
6390
  userCode,
6221
6391
  verificationUri,
6222
6392
  verificationUriComplete,
6223
6393
  expiresIn,
6224
- interval
6225
- } = await codeResponse.json();
6394
+ interval,
6395
+ deviceCode
6396
+ } = codeResult;
6226
6397
  const prompt = (msg) => process.stderr.write(msg + "\n");
6227
6398
  prompt(`
6228
6399
  ! Your one-time code: ${userCode}`);
@@ -6236,30 +6407,95 @@ async function login(options = {}) {
6236
6407
  prompt(" Could not open browser. Visit the URL manually.");
6237
6408
  }
6238
6409
  prompt(" Waiting for authorization... (press Ctrl+C to cancel)\n");
6239
- const deadline = Date.now() + expiresIn * 1e3 + POLL_TIMEOUT_BUFFER_MS;
6240
- let pollInterval = (interval ?? 5) * 1e3;
6241
- const maxAttempts = options.maxPollAttempts ?? Infinity;
6242
- let attempts = 0;
6243
- while (Date.now() < deadline && attempts < maxAttempts) {
6244
- attempts++;
6245
- await new Promise((r2) => setTimeout(r2, pollInterval));
6246
- const tokenResponse = await f2(`${appUrl}/api/auth/device/token`, {
6247
- method: "POST",
6248
- headers: { "Content-Type": "application/json" },
6249
- body: JSON.stringify({ deviceCode, hostname: hostname() })
6250
- });
6251
- const data = await tokenResponse.json();
6252
- if (tokenResponse.ok && data.token) {
6253
- writeConfig({ token: data.token, email: data.email, appUrl });
6254
- const configPath = getConfigPath();
6255
- return { success: true, email: data.email, configPath };
6256
- }
6257
- if (data.error === "authorization_pending") continue;
6258
- if (data.error === "slow_down") {
6259
- pollInterval += 5e3;
6260
- continue;
6410
+ const timeoutMs = expiresIn * 1e3 + POLL_TIMEOUT_BUFFER_MS;
6411
+ const intervalMs = (interval ?? 5) * 1e3;
6412
+ if (options.maxPollAttempts !== void 0) {
6413
+ const appUrl = urlOption || resolveAppUrl();
6414
+ const f2 = fetchOption;
6415
+ let pollInterval = intervalMs;
6416
+ let attempts = 0;
6417
+ const deadline = Date.now() + timeoutMs;
6418
+ while (Date.now() < deadline && attempts < options.maxPollAttempts) {
6419
+ attempts++;
6420
+ await new Promise((r2) => setTimeout(r2, pollInterval));
6421
+ const remaining = Math.max(1, deadline - Date.now());
6422
+ const controller = new AbortController();
6423
+ const timeoutHandle = setTimeout(() => controller.abort(), remaining);
6424
+ let tokenResponse;
6425
+ try {
6426
+ tokenResponse = await f2(`${appUrl}/api/auth/device/token`, {
6427
+ method: "POST",
6428
+ headers: { "Content-Type": "application/json" },
6429
+ body: JSON.stringify({ deviceCode, hostname: hostname() }),
6430
+ signal: controller.signal
6431
+ });
6432
+ } catch (err) {
6433
+ clearTimeout(timeoutHandle);
6434
+ if (err instanceof Error && err.name === "AbortError") {
6435
+ break;
6436
+ }
6437
+ throw err;
6438
+ } finally {
6439
+ clearTimeout(timeoutHandle);
6440
+ }
6441
+ const data = await safeJsonParse(tokenResponse);
6442
+ if (data === MALFORMED) {
6443
+ return {
6444
+ success: false,
6445
+ error: "Server returned malformed response"
6446
+ };
6447
+ }
6448
+ if (tokenResponse.ok) {
6449
+ const token = data.token;
6450
+ const email = data.email;
6451
+ if (typeof token === "string" && token.length > 0) {
6452
+ if (typeof email !== "string" || email.length === 0) {
6453
+ return {
6454
+ success: false,
6455
+ error: "Server returned malformed response (missing email)"
6456
+ };
6457
+ }
6458
+ writeConfig({ token, email, appUrl });
6459
+ const configPath = getConfigPath();
6460
+ return { success: true, email, configPath };
6461
+ }
6462
+ if (token !== void 0) {
6463
+ return {
6464
+ success: false,
6465
+ error: "Server returned malformed response (invalid token type)"
6466
+ };
6467
+ }
6468
+ continue;
6469
+ }
6470
+ if (data.error === "authorization_pending") continue;
6471
+ if (data.error === "slow_down") {
6472
+ pollInterval += 5e3;
6473
+ continue;
6474
+ }
6475
+ const errField = data.error;
6476
+ const errorMsg = typeof errField === "string" ? errField : "Authorization failed";
6477
+ return { success: false, error: errorMsg };
6261
6478
  }
6262
- return { success: false, error: data.error || "Authorization failed" };
6479
+ return {
6480
+ success: false,
6481
+ error: "Authorization timed out. Please try again."
6482
+ };
6483
+ }
6484
+ const pollResult = await pollForToken(deviceCode, {
6485
+ url: urlOption,
6486
+ fetch: fetchOption,
6487
+ timeoutMs,
6488
+ intervalMs
6489
+ });
6490
+ if (pollResult.success) {
6491
+ return {
6492
+ success: true,
6493
+ email: pollResult.email,
6494
+ configPath: pollResult.configPath
6495
+ };
6496
+ }
6497
+ if (pollResult.status === "error") {
6498
+ return { success: false, error: pollResult.error };
6263
6499
  }
6264
6500
  return {
6265
6501
  success: false,
@@ -6478,6 +6714,18 @@ async function listFlows(options = {}) {
6478
6714
  if (error) throwApiError(error, "Failed to list flows");
6479
6715
  return data;
6480
6716
  }
6717
+ async function listAllFlows(options) {
6718
+ const { projects } = await listProjects();
6719
+ const results = [];
6720
+ for (const project of projects) {
6721
+ const data = await listFlows({ ...options, projectId: project.id });
6722
+ results.push({
6723
+ project: { id: project.id, name: project.name },
6724
+ flows: data.flows
6725
+ });
6726
+ }
6727
+ return results;
6728
+ }
6481
6729
  async function getFlow(options) {
6482
6730
  const id = options.projectId ?? requireProjectId();
6483
6731
  const client = createApiClient();
@@ -7378,6 +7626,7 @@ export {
7378
7626
  createPreview,
7379
7627
  createProject,
7380
7628
  createProjectCommand,
7629
+ deleteConfig,
7381
7630
  deleteDeployment,
7382
7631
  deleteDeploymentCommand,
7383
7632
  deleteFlow,
@@ -7395,16 +7644,19 @@ export {
7395
7644
  findExample,
7396
7645
  getAuthHeaders,
7397
7646
  getClientContext,
7647
+ getDefaultProject,
7398
7648
  getDeployment,
7399
7649
  getDeploymentBySlug,
7400
7650
  getDeploymentBySlugCommand,
7401
7651
  getDeploymentCommand,
7652
+ getFeedbackPreference,
7402
7653
  getFlow,
7403
7654
  getFlowCommand,
7404
7655
  getPreview,
7405
7656
  getProject,
7406
7657
  getProjectCommand,
7407
7658
  getToken,
7659
+ listAllFlows,
7408
7660
  listDeployments,
7409
7661
  listDeploymentsCommand,
7410
7662
  listFlows,
@@ -7418,15 +7670,20 @@ export {
7418
7670
  logoutCommand,
7419
7671
  mergeAuthHeaders,
7420
7672
  parseSSEEvents,
7673
+ pollForToken,
7421
7674
  publicFetch,
7422
7675
  push,
7423
7676
  pushCommand,
7424
7677
  readConfig,
7678
+ requestDeviceCode,
7425
7679
  requireProjectId,
7426
7680
  resetClientContext,
7681
+ resolveToken,
7427
7682
  run,
7428
7683
  runCommand,
7429
7684
  setClientContext,
7685
+ setDefaultProject,
7686
+ setFeedbackPreference,
7430
7687
  simulateDestination,
7431
7688
  simulateSource,
7432
7689
  simulateTransformer,