@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/CHANGELOG.md +32 -3
- package/dist/cli.js +353 -126
- package/dist/index.d.ts +429 -332
- package/dist/index.js +293 -36
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
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)
|
|
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
|
|
6260
|
+
async function requestDeviceCode(options = {}) {
|
|
6208
6261
|
const appUrl = options.url || resolveAppUrl();
|
|
6209
6262
|
const f2 = options.fetch ?? globalThis.fetch;
|
|
6210
|
-
const
|
|
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 (!
|
|
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
|
-
|
|
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
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
const
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
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 {
|
|
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,
|