@walkeros/cli 3.4.1 → 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 CHANGED
@@ -1,5 +1,34 @@
1
1
  # @walkeros/cli
2
2
 
3
+ ## 3.4.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 2d25eda: Replace `api` mega-tool with four focused management tools: `auth`
8
+ (device code login), `project_manage`, `flow_manage`, and `deploy_manage`.
9
+ Enforce strict CLI/MCP separation of concern — MCP no longer reads config
10
+ files or checks env vars directly. All tools are always registered regardless
11
+ of auth state.
12
+
13
+ CLI exports new functions: `requestDeviceCode`, `pollForToken`,
14
+ `setDefaultProject`, `getDefaultProject`, `listAllFlows`,
15
+ `setFeedbackPreference`, `getFeedbackPreference`, `resolveToken`,
16
+ `deleteConfig`.
17
+
18
+ Preview CRUD (`preview_list`, `preview_get`, `preview_create`,
19
+ `preview_delete`) is now part of `flow_manage` — previews are a flow-scoped
20
+ concern and belong alongside the flow lifecycle actions rather than in a
21
+ separate tool.
22
+
23
+ - cb4c069: Runtime fetchers (`fetchConfig`, `fetchSecrets`) now classify 401/403
24
+ responses from the app as a typed `RunnerAuthError` with a structured `reason`
25
+ (`'unauthorised' | 'flow' | 'scope' | 'forbidden'`) and the app's error `code`
26
+ (`FORBIDDEN_FLOW` / `FORBIDDEN_SCOPE`). Callers can log a specific reason
27
+ instead of a generic "token may have expired" message, and exit cleanly rather
28
+ than retry on scope/flow mismatches.
29
+ - @walkeros/core@3.4.2
30
+ - @walkeros/server-core@3.4.2
31
+
3
32
  ## 3.4.1
4
33
 
5
34
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -665,6 +665,10 @@ function deleteConfig() {
665
665
  }
666
666
  return false;
667
667
  }
668
+ function getDefaultProject() {
669
+ const config2 = readConfig();
670
+ return config2?.defaultProjectId ?? null;
671
+ }
668
672
  function resolveToken() {
669
673
  const envToken = process.env.WALKEROS_TOKEN;
670
674
  if (envToken) return { token: envToken, source: "env" };
@@ -1030,8 +1034,11 @@ function resolveRunToken() {
1030
1034
  return resolveDeployToken() ?? resolveToken()?.token ?? null;
1031
1035
  }
1032
1036
  function requireProjectId() {
1033
- const projectId = process.env.WALKEROS_PROJECT_ID;
1034
- if (!projectId) throw new Error("WALKEROS_PROJECT_ID not set.");
1037
+ const projectId = process.env.WALKEROS_PROJECT_ID || getDefaultProject();
1038
+ if (!projectId)
1039
+ throw new Error(
1040
+ "No project selected. Set WALKEROS_PROJECT_ID or configure a default project."
1041
+ );
1035
1042
  return projectId;
1036
1043
  }
1037
1044
  var init_auth = __esm({
@@ -18301,6 +18308,101 @@ var init_utils3 = __esm({
18301
18308
  }
18302
18309
  });
18303
18310
 
18311
+ // src/commands/projects/index.ts
18312
+ async function listProjects() {
18313
+ const client = createApiClient();
18314
+ const { data, error: error48 } = await client.GET("/api/projects");
18315
+ if (error48) throw new Error(error48.error?.message || "Failed to list projects");
18316
+ return data;
18317
+ }
18318
+ async function getProject(options = {}) {
18319
+ const id = options.projectId ?? requireProjectId();
18320
+ const client = createApiClient();
18321
+ const { data, error: error48 } = await client.GET("/api/projects/{projectId}", {
18322
+ params: { path: { projectId: id } }
18323
+ });
18324
+ if (error48) throw new Error(error48.error?.message || "Failed to get project");
18325
+ return data;
18326
+ }
18327
+ async function createProject(options) {
18328
+ const client = createApiClient();
18329
+ const { data, error: error48 } = await client.POST("/api/projects", {
18330
+ body: { name: options.name }
18331
+ });
18332
+ if (error48)
18333
+ throw new Error(error48.error?.message || "Failed to create project");
18334
+ return data;
18335
+ }
18336
+ async function updateProject(options) {
18337
+ const id = options.projectId ?? requireProjectId();
18338
+ const client = createApiClient();
18339
+ const { data, error: error48 } = await client.PATCH("/api/projects/{projectId}", {
18340
+ params: { path: { projectId: id } },
18341
+ body: { name: options.name }
18342
+ });
18343
+ if (error48)
18344
+ throw new Error(error48.error?.message || "Failed to update project");
18345
+ return data;
18346
+ }
18347
+ async function deleteProject(options = {}) {
18348
+ const id = options.projectId ?? requireProjectId();
18349
+ const client = createApiClient();
18350
+ const { data, error: error48 } = await client.DELETE("/api/projects/{projectId}", {
18351
+ params: { path: { projectId: id } }
18352
+ });
18353
+ if (error48)
18354
+ throw new Error(error48.error?.message || "Failed to delete project");
18355
+ return data ?? { success: true };
18356
+ }
18357
+ async function handleResult(fn2, options) {
18358
+ try {
18359
+ const result = await fn2();
18360
+ await writeResult(JSON.stringify(result, null, 2), options);
18361
+ } catch (error48) {
18362
+ handleCliError(error48);
18363
+ }
18364
+ }
18365
+ async function listProjectsCommand(options) {
18366
+ await handleResult(() => listProjects(), options);
18367
+ }
18368
+ async function getProjectCommand(projectId, options) {
18369
+ await handleResult(
18370
+ () => getProject({ projectId: projectId ?? options.project }),
18371
+ options
18372
+ );
18373
+ }
18374
+ async function createProjectCommand(name, options) {
18375
+ await handleResult(() => createProject({ name }), options);
18376
+ }
18377
+ async function updateProjectCommand(projectId, options) {
18378
+ const name = options.name;
18379
+ if (!name) {
18380
+ throw new Error("Missing required option: --name <name>");
18381
+ }
18382
+ await handleResult(
18383
+ () => updateProject({
18384
+ projectId: projectId ?? options.project,
18385
+ name
18386
+ }),
18387
+ options
18388
+ );
18389
+ }
18390
+ async function deleteProjectCommand(projectId, options) {
18391
+ await handleResult(
18392
+ () => deleteProject({ projectId: projectId ?? options.project }),
18393
+ options
18394
+ );
18395
+ }
18396
+ var init_projects = __esm({
18397
+ "src/commands/projects/index.ts"() {
18398
+ "use strict";
18399
+ init_api_client();
18400
+ init_api_error();
18401
+ init_auth();
18402
+ init_output();
18403
+ }
18404
+ });
18405
+
18304
18406
  // src/commands/flows/index.ts
18305
18407
  async function listFlows(options = {}) {
18306
18408
  const id = options.projectId ?? requireProjectId();
@@ -18459,6 +18561,7 @@ var init_flows = __esm({
18459
18561
  init_auth();
18460
18562
  init_output();
18461
18563
  init_stdin();
18564
+ init_projects();
18462
18565
  }
18463
18566
  });
18464
18567
 
@@ -20039,6 +20142,37 @@ async function resolveBundle(bundleEnv) {
20039
20142
 
20040
20143
  // src/runtime/config-fetcher.ts
20041
20144
  init_http();
20145
+
20146
+ // src/runtime/runner-auth-error.ts
20147
+ var RunnerAuthError = class extends Error {
20148
+ constructor(status, reason, code, message) {
20149
+ super(message);
20150
+ this.status = status;
20151
+ this.reason = reason;
20152
+ this.code = code;
20153
+ this.name = "RunnerAuthError";
20154
+ }
20155
+ };
20156
+ function isAppErrorBody(value) {
20157
+ return typeof value === "object" && value !== null && "error" in value && typeof value.error === "object";
20158
+ }
20159
+ async function throwIfRunnerAuthFailure(res) {
20160
+ if (res.status !== 401 && res.status !== 403) return;
20161
+ let code = null;
20162
+ let message = res.statusText;
20163
+ try {
20164
+ const body = await res.clone().json();
20165
+ if (isAppErrorBody(body) && body.error) {
20166
+ if (typeof body.error.code === "string") code = body.error.code;
20167
+ if (typeof body.error.message === "string") message = body.error.message;
20168
+ }
20169
+ } catch {
20170
+ }
20171
+ const reason = res.status === 401 ? "unauthorised" : code === "FORBIDDEN_FLOW" ? "flow" : code === "FORBIDDEN_SCOPE" ? "scope" : "forbidden";
20172
+ throw new RunnerAuthError(res.status, reason, code, message);
20173
+ }
20174
+
20175
+ // src/runtime/config-fetcher.ts
20042
20176
  async function fetchConfig(options) {
20043
20177
  const url2 = `${options.appUrl}/api/projects/${options.projectId}/flows/${options.flowId}`;
20044
20178
  const headers = mergeAuthHeaders(
@@ -20052,12 +20186,8 @@ async function fetchConfig(options) {
20052
20186
  if (response.status === 304) {
20053
20187
  return { changed: false };
20054
20188
  }
20189
+ await throwIfRunnerAuthFailure(response);
20055
20190
  if (!response.ok) {
20056
- if (response.status === 401 || response.status === 403) {
20057
- throw new Error(
20058
- `Config fetch failed (${response.status}): token may have expired \u2014 redeploy to rotate`
20059
- );
20060
- }
20061
20191
  throw new Error(
20062
20192
  `Config fetch failed: ${response.status} ${response.statusText}`
20063
20193
  );
@@ -20488,6 +20618,7 @@ async function fetchSecrets(options) {
20488
20618
  const res = await fetch(url2, {
20489
20619
  headers: mergeAuthHeaders(token, { "Content-Type": "application/json" })
20490
20620
  });
20621
+ await throwIfRunnerAuthFailure(res);
20491
20622
  if (!res.ok) {
20492
20623
  throw new SecretsHttpError(res.status, res.statusText);
20493
20624
  }
@@ -21611,6 +21742,8 @@ init_cli_logger();
21611
21742
  init_config_file();
21612
21743
  import { hostname as hostname3 } from "os";
21613
21744
  var POLL_TIMEOUT_BUFFER_MS = 5e3;
21745
+ var DEFAULT_POLL_TIMEOUT_MS = 6e4;
21746
+ var DEFAULT_POLL_INTERVAL_MS = 5e3;
21614
21747
  async function openInBrowser(url2) {
21615
21748
  const { default: open } = await import("open");
21616
21749
  await open(url2);
@@ -21636,25 +21769,143 @@ async function loginCommand(options) {
21636
21769
  process.exit(1);
21637
21770
  }
21638
21771
  }
21639
- async function login(options = {}) {
21772
+ async function requestDeviceCode(options = {}) {
21640
21773
  const appUrl = options.url || resolveAppUrl();
21641
21774
  const f2 = options.fetch ?? globalThis.fetch;
21642
- const codeResponse = await f2(`${appUrl}/api/auth/device/code`, {
21775
+ const response = await f2(`${appUrl}/api/auth/device/code`, {
21643
21776
  method: "POST",
21644
21777
  headers: { "Content-Type": "application/json" },
21645
21778
  body: JSON.stringify({})
21646
21779
  });
21647
- if (!codeResponse.ok) {
21780
+ if (!response.ok) {
21781
+ throw new Error("Failed to request device code");
21782
+ }
21783
+ const data = await response.json();
21784
+ return {
21785
+ deviceCode: data.deviceCode,
21786
+ userCode: data.userCode,
21787
+ verificationUri: data.verificationUri,
21788
+ verificationUriComplete: data.verificationUriComplete,
21789
+ expiresIn: data.expiresIn,
21790
+ interval: data.interval
21791
+ };
21792
+ }
21793
+ async function pollForToken(deviceCode, options = {}) {
21794
+ const appUrl = options.url || resolveAppUrl();
21795
+ const f2 = options.fetch ?? globalThis.fetch;
21796
+ const timeoutMs = options.timeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
21797
+ let intervalMs = options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
21798
+ const deadline = Date.now() + timeoutMs;
21799
+ while (Date.now() < deadline) {
21800
+ await new Promise((r2) => setTimeout(r2, intervalMs));
21801
+ if (Date.now() >= deadline) break;
21802
+ const remaining = Math.max(1, deadline - Date.now());
21803
+ const controller = new AbortController();
21804
+ const timeoutHandle = setTimeout(() => controller.abort(), remaining);
21805
+ let tokenResponse;
21806
+ try {
21807
+ tokenResponse = await f2(`${appUrl}/api/auth/device/token`, {
21808
+ method: "POST",
21809
+ headers: { "Content-Type": "application/json" },
21810
+ body: JSON.stringify({ deviceCode, hostname: hostname3() }),
21811
+ signal: controller.signal
21812
+ });
21813
+ } catch (err) {
21814
+ clearTimeout(timeoutHandle);
21815
+ if (err instanceof Error && err.name === "AbortError") {
21816
+ break;
21817
+ }
21818
+ throw err;
21819
+ } finally {
21820
+ clearTimeout(timeoutHandle);
21821
+ }
21822
+ const data = await safeJsonParse(tokenResponse);
21823
+ if (data === MALFORMED) {
21824
+ return {
21825
+ success: false,
21826
+ status: "error",
21827
+ error: "Server returned malformed response"
21828
+ };
21829
+ }
21830
+ if (tokenResponse.ok) {
21831
+ const token = data.token;
21832
+ const email3 = data.email;
21833
+ if (typeof token === "string" && token.length > 0) {
21834
+ if (typeof email3 !== "string" || email3.length === 0) {
21835
+ return {
21836
+ success: false,
21837
+ status: "error",
21838
+ error: "Server returned malformed response (missing email)"
21839
+ };
21840
+ }
21841
+ writeConfig({ token, email: email3, appUrl });
21842
+ const configPath = getConfigPath();
21843
+ return {
21844
+ success: true,
21845
+ status: "authenticated",
21846
+ email: email3,
21847
+ configPath
21848
+ };
21849
+ }
21850
+ if (token !== void 0) {
21851
+ return {
21852
+ success: false,
21853
+ status: "error",
21854
+ error: "Server returned malformed response (invalid token type)"
21855
+ };
21856
+ }
21857
+ continue;
21858
+ }
21859
+ if (data.error === "authorization_pending") continue;
21860
+ if (data.error === "slow_down") {
21861
+ intervalMs += 5e3;
21862
+ continue;
21863
+ }
21864
+ const errField = data.error;
21865
+ let errorMsg;
21866
+ if (typeof errField === "string") {
21867
+ errorMsg = errField;
21868
+ } else if (errField && typeof errField === "object" && "message" in errField && typeof errField.message === "string") {
21869
+ errorMsg = errField.message;
21870
+ } else {
21871
+ errorMsg = "Authorization failed";
21872
+ }
21873
+ return { success: false, status: "error", error: errorMsg };
21874
+ }
21875
+ return { success: false, status: "pending" };
21876
+ }
21877
+ var MALFORMED = /* @__PURE__ */ Symbol("malformed-json");
21878
+ async function safeJsonParse(response) {
21879
+ try {
21880
+ const parsed = await response.json();
21881
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
21882
+ return parsed;
21883
+ }
21884
+ return MALFORMED;
21885
+ } catch {
21886
+ return MALFORMED;
21887
+ }
21888
+ }
21889
+ async function login(options = {}) {
21890
+ const fetchOption = options.fetch ?? globalThis.fetch;
21891
+ const urlOption = options.url;
21892
+ let codeResult;
21893
+ try {
21894
+ codeResult = await requestDeviceCode({
21895
+ url: urlOption,
21896
+ fetch: fetchOption
21897
+ });
21898
+ } catch {
21648
21899
  return { success: false, error: "Failed to request device code" };
21649
21900
  }
21650
21901
  const {
21651
- deviceCode,
21652
21902
  userCode,
21653
21903
  verificationUri,
21654
21904
  verificationUriComplete,
21655
21905
  expiresIn,
21656
- interval
21657
- } = await codeResponse.json();
21906
+ interval,
21907
+ deviceCode
21908
+ } = codeResult;
21658
21909
  const prompt = (msg) => process.stderr.write(msg + "\n");
21659
21910
  prompt(`
21660
21911
  ! Your one-time code: ${userCode}`);
@@ -21668,30 +21919,95 @@ async function login(options = {}) {
21668
21919
  prompt(" Could not open browser. Visit the URL manually.");
21669
21920
  }
21670
21921
  prompt(" Waiting for authorization... (press Ctrl+C to cancel)\n");
21671
- const deadline = Date.now() + expiresIn * 1e3 + POLL_TIMEOUT_BUFFER_MS;
21672
- let pollInterval = (interval ?? 5) * 1e3;
21673
- const maxAttempts = options.maxPollAttempts ?? Infinity;
21674
- let attempts = 0;
21675
- while (Date.now() < deadline && attempts < maxAttempts) {
21676
- attempts++;
21677
- await new Promise((r2) => setTimeout(r2, pollInterval));
21678
- const tokenResponse = await f2(`${appUrl}/api/auth/device/token`, {
21679
- method: "POST",
21680
- headers: { "Content-Type": "application/json" },
21681
- body: JSON.stringify({ deviceCode, hostname: hostname3() })
21682
- });
21683
- const data = await tokenResponse.json();
21684
- if (tokenResponse.ok && data.token) {
21685
- writeConfig({ token: data.token, email: data.email, appUrl });
21686
- const configPath = getConfigPath();
21687
- return { success: true, email: data.email, configPath };
21688
- }
21689
- if (data.error === "authorization_pending") continue;
21690
- if (data.error === "slow_down") {
21691
- pollInterval += 5e3;
21692
- continue;
21922
+ const timeoutMs = expiresIn * 1e3 + POLL_TIMEOUT_BUFFER_MS;
21923
+ const intervalMs = (interval ?? 5) * 1e3;
21924
+ if (options.maxPollAttempts !== void 0) {
21925
+ const appUrl = urlOption || resolveAppUrl();
21926
+ const f2 = fetchOption;
21927
+ let pollInterval = intervalMs;
21928
+ let attempts = 0;
21929
+ const deadline = Date.now() + timeoutMs;
21930
+ while (Date.now() < deadline && attempts < options.maxPollAttempts) {
21931
+ attempts++;
21932
+ await new Promise((r2) => setTimeout(r2, pollInterval));
21933
+ const remaining = Math.max(1, deadline - Date.now());
21934
+ const controller = new AbortController();
21935
+ const timeoutHandle = setTimeout(() => controller.abort(), remaining);
21936
+ let tokenResponse;
21937
+ try {
21938
+ tokenResponse = await f2(`${appUrl}/api/auth/device/token`, {
21939
+ method: "POST",
21940
+ headers: { "Content-Type": "application/json" },
21941
+ body: JSON.stringify({ deviceCode, hostname: hostname3() }),
21942
+ signal: controller.signal
21943
+ });
21944
+ } catch (err) {
21945
+ clearTimeout(timeoutHandle);
21946
+ if (err instanceof Error && err.name === "AbortError") {
21947
+ break;
21948
+ }
21949
+ throw err;
21950
+ } finally {
21951
+ clearTimeout(timeoutHandle);
21952
+ }
21953
+ const data = await safeJsonParse(tokenResponse);
21954
+ if (data === MALFORMED) {
21955
+ return {
21956
+ success: false,
21957
+ error: "Server returned malformed response"
21958
+ };
21959
+ }
21960
+ if (tokenResponse.ok) {
21961
+ const token = data.token;
21962
+ const email3 = data.email;
21963
+ if (typeof token === "string" && token.length > 0) {
21964
+ if (typeof email3 !== "string" || email3.length === 0) {
21965
+ return {
21966
+ success: false,
21967
+ error: "Server returned malformed response (missing email)"
21968
+ };
21969
+ }
21970
+ writeConfig({ token, email: email3, appUrl });
21971
+ const configPath = getConfigPath();
21972
+ return { success: true, email: email3, configPath };
21973
+ }
21974
+ if (token !== void 0) {
21975
+ return {
21976
+ success: false,
21977
+ error: "Server returned malformed response (invalid token type)"
21978
+ };
21979
+ }
21980
+ continue;
21981
+ }
21982
+ if (data.error === "authorization_pending") continue;
21983
+ if (data.error === "slow_down") {
21984
+ pollInterval += 5e3;
21985
+ continue;
21986
+ }
21987
+ const errField = data.error;
21988
+ const errorMsg = typeof errField === "string" ? errField : "Authorization failed";
21989
+ return { success: false, error: errorMsg };
21693
21990
  }
21694
- return { success: false, error: data.error || "Authorization failed" };
21991
+ return {
21992
+ success: false,
21993
+ error: "Authorization timed out. Please try again."
21994
+ };
21995
+ }
21996
+ const pollResult = await pollForToken(deviceCode, {
21997
+ url: urlOption,
21998
+ fetch: fetchOption,
21999
+ timeoutMs,
22000
+ intervalMs
22001
+ });
22002
+ if (pollResult.success) {
22003
+ return {
22004
+ success: true,
22005
+ email: pollResult.email,
22006
+ configPath: pollResult.configPath
22007
+ };
22008
+ }
22009
+ if (pollResult.status === "error") {
22010
+ return { success: false, error: pollResult.error };
21695
22011
  }
21696
22012
  return {
21697
22013
  success: false,
@@ -21744,97 +22060,8 @@ async function whoamiCommand(options) {
21744
22060
  }
21745
22061
  }
21746
22062
 
21747
- // src/commands/projects/index.ts
21748
- init_api_client();
21749
- init_api_error();
21750
- init_auth();
21751
- init_output();
21752
- async function listProjects() {
21753
- const client = createApiClient();
21754
- const { data, error: error48 } = await client.GET("/api/projects");
21755
- if (error48) throw new Error(error48.error?.message || "Failed to list projects");
21756
- return data;
21757
- }
21758
- async function getProject(options = {}) {
21759
- const id = options.projectId ?? requireProjectId();
21760
- const client = createApiClient();
21761
- const { data, error: error48 } = await client.GET("/api/projects/{projectId}", {
21762
- params: { path: { projectId: id } }
21763
- });
21764
- if (error48) throw new Error(error48.error?.message || "Failed to get project");
21765
- return data;
21766
- }
21767
- async function createProject(options) {
21768
- const client = createApiClient();
21769
- const { data, error: error48 } = await client.POST("/api/projects", {
21770
- body: { name: options.name }
21771
- });
21772
- if (error48)
21773
- throw new Error(error48.error?.message || "Failed to create project");
21774
- return data;
21775
- }
21776
- async function updateProject(options) {
21777
- const id = options.projectId ?? requireProjectId();
21778
- const client = createApiClient();
21779
- const { data, error: error48 } = await client.PATCH("/api/projects/{projectId}", {
21780
- params: { path: { projectId: id } },
21781
- body: { name: options.name }
21782
- });
21783
- if (error48)
21784
- throw new Error(error48.error?.message || "Failed to update project");
21785
- return data;
21786
- }
21787
- async function deleteProject(options = {}) {
21788
- const id = options.projectId ?? requireProjectId();
21789
- const client = createApiClient();
21790
- const { data, error: error48 } = await client.DELETE("/api/projects/{projectId}", {
21791
- params: { path: { projectId: id } }
21792
- });
21793
- if (error48)
21794
- throw new Error(error48.error?.message || "Failed to delete project");
21795
- return data ?? { success: true };
21796
- }
21797
- async function handleResult(fn2, options) {
21798
- try {
21799
- const result = await fn2();
21800
- await writeResult(JSON.stringify(result, null, 2), options);
21801
- } catch (error48) {
21802
- handleCliError(error48);
21803
- }
21804
- }
21805
- async function listProjectsCommand(options) {
21806
- await handleResult(() => listProjects(), options);
21807
- }
21808
- async function getProjectCommand(projectId, options) {
21809
- await handleResult(
21810
- () => getProject({ projectId: projectId ?? options.project }),
21811
- options
21812
- );
21813
- }
21814
- async function createProjectCommand(name, options) {
21815
- await handleResult(() => createProject({ name }), options);
21816
- }
21817
- async function updateProjectCommand(projectId, options) {
21818
- const name = options.name;
21819
- if (!name) {
21820
- throw new Error("Missing required option: --name <name>");
21821
- }
21822
- await handleResult(
21823
- () => updateProject({
21824
- projectId: projectId ?? options.project,
21825
- name
21826
- }),
21827
- options
21828
- );
21829
- }
21830
- async function deleteProjectCommand(projectId, options) {
21831
- await handleResult(
21832
- () => deleteProject({ projectId: projectId ?? options.project }),
21833
- options
21834
- );
21835
- }
21836
-
21837
22063
  // src/cli.ts
22064
+ init_projects();
21838
22065
  init_flows();
21839
22066
 
21840
22067
  // src/commands/deploy/index.ts