@pmate/cli 0.12.8 → 0.13.1

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
@@ -12407,10 +12407,10 @@ function resolveHubBaseUrl(value) {
12407
12407
  }
12408
12408
  function readSessionToken() {
12409
12409
  const sessionPath = import_node_path13.default.join(import_node_os4.default.homedir(), ".pmate", "session.json");
12410
- if (!(0, import_node_fs12.existsSync)(sessionPath)) {
12410
+ if (!(0, import_node_fs10.existsSync)(sessionPath)) {
12411
12411
  throw new Error(`Missing session file at ${sessionPath}. Run "pmate login" first.`);
12412
12412
  }
12413
- const raw = (0, import_node_fs12.readFileSync)(sessionPath, "utf8");
12413
+ const raw = (0, import_node_fs10.readFileSync)(sessionPath, "utf8");
12414
12414
  const parsed = JSON.parse(raw);
12415
12415
  const token = parsed.token?.trim();
12416
12416
  if (!token) {
@@ -12427,11 +12427,11 @@ function unwrapAgentResult(value) {
12427
12427
  }
12428
12428
  return value;
12429
12429
  }
12430
- var import_node_fs12, import_node_os4, import_node_path13, import_agent_sdk, DEFAULT_AGENT_API_BASE_URL, DEFAULT_AGENT_HUB_BASE_URL, DEFAULT_NAMESPACE, DEFAULT_FROM, AgentLlmClient;
12430
+ var import_node_fs10, import_node_os4, import_node_path13, import_agent_sdk, DEFAULT_AGENT_API_BASE_URL, DEFAULT_AGENT_HUB_BASE_URL, DEFAULT_NAMESPACE, DEFAULT_FROM, AgentLlmClient;
12431
12431
  var init_AgentLlmClient = __esm({
12432
12432
  "src/llm/AgentLlmClient.ts"() {
12433
12433
  "use strict";
12434
- import_node_fs12 = require("fs");
12434
+ import_node_fs10 = require("fs");
12435
12435
  import_node_os4 = __toESM(require("os"));
12436
12436
  import_node_path13 = __toESM(require("path"));
12437
12437
  import_agent_sdk = __toESM(require_dist4());
@@ -12479,7 +12479,7 @@ __export(index_exports, {
12479
12479
  parseCommandArgs: () => parseCommandArgs
12480
12480
  });
12481
12481
  module.exports = __toCommonJS(index_exports);
12482
- var import_node_fs19 = require("fs");
12482
+ var import_node_fs17 = require("fs");
12483
12483
  var import_node_path21 = __toESM(require("path"));
12484
12484
  var import_chalk3 = __toESM(require("chalk"));
12485
12485
 
@@ -12523,232 +12523,14 @@ var ui = {
12523
12523
  };
12524
12524
 
12525
12525
  // src/commands/ListCommandHandler.ts
12526
- var import_node_fs = require("fs");
12527
- var import_node_path = __toESM(require("path"));
12528
- var import_yaml = __toESM(require("yaml"));
12529
- var ListCommandHandler = class {
12530
- handle(args) {
12531
- const targetDir = this.resolveTargetDir(args.targetPath);
12532
- const config = this.loadConfig(targetDir);
12533
- if (config.apps.length === 0) {
12534
- ui.warn("No apps defined.");
12535
- return;
12536
- }
12537
- for (const app of config.apps) {
12538
- if (app && typeof app.app === "string") {
12539
- if (typeof app.dir !== "string" || !app.dir.trim()) {
12540
- throw new Error(`Invalid .pmate.yaml: missing dir for app ${app.app}`);
12541
- }
12542
- ui.info(`${app.app} -> ${app.dir}`);
12543
- }
12544
- }
12545
- }
12546
- resolveTargetDir(targetPath) {
12547
- return targetPath ? import_node_path.default.resolve(process.cwd(), targetPath) : process.cwd();
12548
- }
12549
- loadConfig(targetDir) {
12550
- const configPath = import_node_path.default.join(targetDir, ".pmate.yaml");
12551
- if (!(0, import_node_fs.existsSync)(configPath)) {
12552
- throw new Error(`No .pmate.yaml found in ${targetDir}`);
12553
- }
12554
- const raw = (0, import_node_fs.readFileSync)(configPath, "utf8");
12555
- const parsed = import_yaml.default.parse(raw);
12556
- if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.apps)) {
12557
- throw new Error("Invalid .pmate.yaml: missing apps array");
12558
- }
12559
- return parsed;
12560
- }
12561
- };
12562
-
12563
- // src/commands/LoginCommandHandler.ts
12564
- var import_node_fs2 = require("fs");
12565
- var import_node_http = require("http");
12566
- var import_node_os = __toESM(require("os"));
12567
12526
  var import_node_path2 = __toESM(require("path"));
12568
- var import_node_child_process = require("child_process");
12569
- var AUTH_UI_BASE_URL = "https://auth.pmate.chat";
12570
- var AUTH_API_BASE_URL = "https://auth-api-v2.pmate.chat";
12571
- var CALLBACK_PATH = "/pmate-login-callback";
12572
- var APP_ID = "@pmate/cli";
12573
- var LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
12574
- function buildHostedLoginUrl(redirectUrl) {
12575
- const url = new URL(AUTH_UI_BASE_URL);
12576
- url.searchParams.set("redirect", redirectUrl);
12577
- url.searchParams.set("app", APP_ID);
12578
- return url.toString();
12579
- }
12580
- var LoginCommandHandler = class {
12581
- async handle(_args) {
12582
- const callback = await this.startCallbackServer();
12583
- const loginUrl = buildHostedLoginUrl(callback.redirectUrl);
12584
- ui.info("Opening browser for login.", {
12585
- url: loginUrl
12586
- });
12587
- if (!this.openBrowser(loginUrl)) {
12588
- ui.warn("Unable to open the browser automatically.");
12589
- ui.plain(loginUrl);
12590
- }
12591
- ui.info("Waiting for login redirect...", {
12592
- callback: callback.redirectUrl
12593
- });
12594
- try {
12595
- const token = await callback.waitForToken;
12596
- const loginResponse = await this.fetchSession(token);
12597
- const sessionPath = this.writeSession(loginResponse);
12598
- ui.success(`Logged in. Session saved to ${sessionPath}`);
12599
- } finally {
12600
- await callback.close();
12601
- }
12602
- }
12603
- async fetchSession(token) {
12604
- const response = await fetch(`${AUTH_API_BASE_URL}/session`, {
12605
- method: "GET",
12606
- headers: {
12607
- Authorization: `Bearer ${token}`
12608
- }
12609
- });
12610
- if (!response.ok) {
12611
- const detail = await response.text();
12612
- throw new Error(
12613
- `Failed to validate session (${response.status}): ${detail || response.statusText}`
12614
- );
12615
- }
12616
- const resp = await response.json();
12617
- return {
12618
- ...resp.data,
12619
- token
12620
- };
12621
- }
12622
- async startCallbackServer() {
12623
- let resolveToken;
12624
- let rejectToken;
12625
- const waitForToken = new Promise((resolve, reject) => {
12626
- resolveToken = resolve;
12627
- rejectToken = reject;
12628
- });
12629
- const server = (0, import_node_http.createServer)((request, response) => {
12630
- this.handleCallbackRequest(request, response, resolveToken, rejectToken);
12631
- });
12632
- await new Promise((resolve, reject) => {
12633
- server.once("error", reject);
12634
- server.listen(0, "127.0.0.1", () => resolve());
12635
- });
12636
- const address = server.address();
12637
- if (!address || typeof address === "string") {
12638
- server.close();
12639
- throw new Error("Failed to start login callback server.");
12640
- }
12641
- const redirectUrl = `http://127.0.0.1:${address.port}${CALLBACK_PATH}`;
12642
- const timeout = setTimeout(() => {
12643
- rejectToken(
12644
- new Error(
12645
- `Timed out waiting for browser login after ${LOGIN_TIMEOUT_MS / 1e3} seconds.`
12646
- )
12647
- );
12648
- }, LOGIN_TIMEOUT_MS);
12649
- const close = async () => {
12650
- clearTimeout(timeout);
12651
- await new Promise((resolve) => {
12652
- server.close(() => resolve());
12653
- });
12654
- };
12655
- return {
12656
- redirectUrl,
12657
- waitForToken,
12658
- close
12659
- };
12660
- }
12661
- handleCallbackRequest(request, response, resolveToken, rejectToken) {
12662
- const url = new URL(request.url || "/", "http://127.0.0.1");
12663
- if (url.pathname !== CALLBACK_PATH) {
12664
- response.statusCode = 404;
12665
- response.end("Not found");
12666
- return;
12667
- }
12668
- const token = url.searchParams.get("sessionId")?.trim();
12669
- if (!token) {
12670
- response.statusCode = 400;
12671
- response.setHeader("Content-Type", "text/html; charset=utf-8");
12672
- response.end(this.renderHtml("Login failed", "Missing sessionId in redirect URL."));
12673
- rejectToken(new Error("Login callback did not include sessionId."));
12674
- return;
12675
- }
12676
- response.statusCode = 200;
12677
- response.setHeader("Content-Type", "text/html; charset=utf-8");
12678
- response.end(
12679
- this.renderHtml(
12680
- "Login complete",
12681
- "You can close this tab and return to the terminal."
12682
- )
12683
- );
12684
- resolveToken(token);
12685
- }
12686
- renderHtml(title, message) {
12687
- return `<!doctype html>
12688
- <html lang="en">
12689
- <head>
12690
- <meta charset="utf-8" />
12691
- <meta name="viewport" content="width=device-width, initial-scale=1" />
12692
- <title>${title}</title>
12693
- <style>
12694
- body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; padding: 32px; color: #111; }
12695
- main { max-width: 560px; margin: 10vh auto; }
12696
- h1 { font-size: 28px; margin-bottom: 12px; }
12697
- p { font-size: 16px; line-height: 1.5; }
12698
- </style>
12699
- </head>
12700
- <body>
12701
- <main>
12702
- <h1>${title}</h1>
12703
- <p>${message}</p>
12704
- </main>
12705
- </body>
12706
- </html>`;
12707
- }
12708
- openBrowser(url) {
12709
- const platform = process.platform;
12710
- const command = platform === "darwin" ? { cmd: "open", args: [url] } : platform === "win32" ? { cmd: "cmd", args: ["/c", "start", "", url] } : { cmd: "xdg-open", args: [url] };
12711
- try {
12712
- const child = (0, import_node_child_process.spawn)(command.cmd, command.args, {
12713
- detached: true,
12714
- stdio: "ignore"
12715
- });
12716
- child.unref();
12717
- return true;
12718
- } catch {
12719
- return false;
12720
- }
12721
- }
12722
- writeSession(session) {
12723
- const sessionDir = import_node_path2.default.join(import_node_os.default.homedir(), ".pmate");
12724
- const sessionPath = import_node_path2.default.join(sessionDir, "session.json");
12725
- (0, import_node_fs2.mkdirSync)(sessionDir, { recursive: true });
12726
- (0, import_node_fs2.writeFileSync)(sessionPath, JSON.stringify(session, null, 2), {
12727
- mode: 384
12728
- });
12729
- try {
12730
- (0, import_node_fs2.chmodSync)(sessionPath, 384);
12731
- } catch {
12732
- }
12733
- return sessionPath;
12734
- }
12735
- };
12736
-
12737
- // src/services/AliyunService.ts
12738
- var import_alidns20150109 = __toESM(require("@alicloud/alidns20150109"));
12739
- var import_cdn20180510 = __toESM(require("@alicloud/cdn20180510"));
12740
- var import_openapi_client = require("@alicloud/openapi-client");
12741
- var import_ali_oss = __toESM(require("ali-oss"));
12742
- var import_node_crypto = require("crypto");
12743
- var import_node_fs4 = require("fs");
12744
- var import_node_path4 = __toESM(require("path"));
12745
12527
 
12746
12528
  // src/config.ts
12747
- var import_node_fs3 = require("fs");
12748
- var import_node_os2 = __toESM(require("os"));
12749
- var import_node_path3 = __toESM(require("path"));
12529
+ var import_node_fs = require("fs");
12530
+ var import_node_os = __toESM(require("os"));
12531
+ var import_node_path = __toESM(require("path"));
12750
12532
  var import_ajv = __toESM(require("ajv"));
12751
- var import_yaml2 = __toESM(require("yaml"));
12533
+ var import_yaml = __toESM(require("yaml"));
12752
12534
  var cachedConfig;
12753
12535
  var CONFIG = new Proxy({}, {
12754
12536
  get(_target, prop) {
@@ -12870,13 +12652,13 @@ function loadPmateConfig() {
12870
12652
  if (cachedConfig !== void 0) {
12871
12653
  return cachedConfig ?? {};
12872
12654
  }
12873
- const pmateDir = import_node_path3.default.join(import_node_os2.default.homedir(), ".pmate");
12874
- const yamlPath = import_node_path3.default.join(pmateDir, "config.yaml");
12655
+ const pmateDir = import_node_path.default.join(import_node_os.default.homedir(), ".pmate");
12656
+ const yamlPath = import_node_path.default.join(pmateDir, "config.yaml");
12875
12657
  let yamlConfig = null;
12876
- if ((0, import_node_fs3.existsSync)(yamlPath)) {
12877
- const raw = (0, import_node_fs3.readFileSync)(yamlPath, "utf8");
12658
+ if ((0, import_node_fs.existsSync)(yamlPath)) {
12659
+ const raw = (0, import_node_fs.readFileSync)(yamlPath, "utf8");
12878
12660
  try {
12879
- const parsed = import_yaml2.default.parse(raw);
12661
+ const parsed = import_yaml.default.parse(raw);
12880
12662
  yamlConfig = validateParsedConfig(parsed, yamlPath);
12881
12663
  } catch (error) {
12882
12664
  const message = error instanceof Error ? error.message : String(error);
@@ -12887,19 +12669,38 @@ function loadPmateConfig() {
12887
12669
  return cachedConfig;
12888
12670
  }
12889
12671
  function loadRepoConfig(cwd) {
12890
- const configPath = findRepoConfigPath(cwd);
12891
- if (!configPath) {
12672
+ const paths = findRepoConfigPaths(cwd, true);
12673
+ if (!paths) {
12892
12674
  return null;
12893
12675
  }
12894
- const raw = (0, import_node_fs3.readFileSync)(configPath, "utf8");
12895
- let parsed;
12896
- try {
12897
- parsed = import_yaml2.default.parse(raw);
12898
- } catch (error) {
12899
- const message = error instanceof Error ? error.message : String(error);
12900
- throw new Error(`Invalid YAML in ${configPath}: ${message}`);
12676
+ const parsed = loadMergedRepoYaml(paths);
12677
+ return normalizeRepoConfig(parsed, paths.configPath);
12678
+ }
12679
+ function loadAppConfig(cwd, options = {}) {
12680
+ const paths = findRepoConfigPaths(cwd, options.searchParents ?? false);
12681
+ if (!paths) {
12682
+ const suffix = options.searchParents ? " or parent directories" : "";
12683
+ throw new Error(`No .pmate.yaml found in ${cwd}${suffix}`);
12684
+ }
12685
+ const parsed = loadMergedRepoYaml(paths);
12686
+ const appConfig = parsed;
12687
+ if (!appConfig || typeof appConfig !== "object" || !Array.isArray(appConfig.apps)) {
12688
+ throw new Error(
12689
+ `Invalid .pmate.yaml: missing apps array (${describeRepoConfigPaths(paths)})`
12690
+ );
12901
12691
  }
12902
- return normalizeRepoConfig(parsed, configPath);
12692
+ return {
12693
+ config: appConfig,
12694
+ rootDir: paths.rootDir,
12695
+ configPath: paths.configPath,
12696
+ localConfigPath: paths.localConfigPath
12697
+ };
12698
+ }
12699
+ function describeLoadedAppConfig({
12700
+ configPath,
12701
+ localConfigPath
12702
+ }) {
12703
+ return localConfigPath ? `${configPath} + ${localConfigPath}` : configPath;
12903
12704
  }
12904
12705
  function validateParsedConfig(value, configPath) {
12905
12706
  if (value === null || value === void 0) {
@@ -12915,20 +12716,93 @@ ${detail}`);
12915
12716
  }
12916
12717
  return value;
12917
12718
  }
12918
- function findRepoConfigPath(cwd) {
12919
- let current = import_node_path3.default.resolve(cwd);
12719
+ function findRepoConfigPaths(cwd, searchParents) {
12720
+ let current = import_node_path.default.resolve(cwd);
12920
12721
  while (true) {
12921
- const candidate = import_node_path3.default.join(current, ".pmate.yaml");
12922
- if ((0, import_node_fs3.existsSync)(candidate)) {
12923
- return candidate;
12722
+ const candidate = import_node_path.default.join(current, ".pmate.yaml");
12723
+ if ((0, import_node_fs.existsSync)(candidate)) {
12724
+ const localCandidate = import_node_path.default.join(current, ".pmate.local.yaml");
12725
+ return {
12726
+ rootDir: current,
12727
+ configPath: candidate,
12728
+ localConfigPath: (0, import_node_fs.existsSync)(localCandidate) ? localCandidate : void 0
12729
+ };
12730
+ }
12731
+ if (!searchParents) {
12732
+ return null;
12924
12733
  }
12925
- const parent = import_node_path3.default.dirname(current);
12734
+ const parent = import_node_path.default.dirname(current);
12926
12735
  if (parent === current) {
12927
12736
  return null;
12928
12737
  }
12929
12738
  current = parent;
12930
12739
  }
12931
12740
  }
12741
+ function loadMergedRepoYaml(paths) {
12742
+ const base = parseYamlFile(paths.configPath);
12743
+ if (!paths.localConfigPath) {
12744
+ return base;
12745
+ }
12746
+ const local = parseYamlFile(paths.localConfigPath);
12747
+ return mergeRepoConfigValues(base, local);
12748
+ }
12749
+ function parseYamlFile(configPath) {
12750
+ const raw = (0, import_node_fs.readFileSync)(configPath, "utf8");
12751
+ try {
12752
+ return import_yaml.default.parse(raw);
12753
+ } catch (error) {
12754
+ const message = error instanceof Error ? error.message : String(error);
12755
+ throw new Error(`Invalid YAML in ${configPath}: ${message}`);
12756
+ }
12757
+ }
12758
+ function describeRepoConfigPaths(paths) {
12759
+ return paths.localConfigPath ? `${paths.configPath} + ${paths.localConfigPath}` : paths.configPath;
12760
+ }
12761
+ function mergeRepoConfigValues(base, local) {
12762
+ if (Array.isArray(base) && Array.isArray(local)) {
12763
+ return [...base, ...local];
12764
+ }
12765
+ if (isPlainObject(base) && isPlainObject(local)) {
12766
+ const merged = { ...base };
12767
+ for (const [key, localValue] of Object.entries(local)) {
12768
+ const baseValue = merged[key];
12769
+ if (key === "apps" && Array.isArray(baseValue) && Array.isArray(localValue)) {
12770
+ merged[key] = mergeApps(baseValue, localValue);
12771
+ continue;
12772
+ }
12773
+ merged[key] = mergeRepoConfigValues(baseValue, localValue);
12774
+ }
12775
+ return merged;
12776
+ }
12777
+ return local === void 0 ? base : local;
12778
+ }
12779
+ function mergeApps(baseApps, localApps) {
12780
+ const merged = [...baseApps];
12781
+ for (const localApp of localApps) {
12782
+ const localName = getAppName(localApp);
12783
+ if (!localName) {
12784
+ merged.push(localApp);
12785
+ continue;
12786
+ }
12787
+ const baseIndex = merged.findIndex((entry) => getAppName(entry) === localName);
12788
+ if (baseIndex === -1) {
12789
+ merged.push(localApp);
12790
+ continue;
12791
+ }
12792
+ merged[baseIndex] = mergeRepoConfigValues(merged[baseIndex], localApp);
12793
+ }
12794
+ return merged;
12795
+ }
12796
+ function getAppName(value) {
12797
+ if (!isPlainObject(value) || typeof value.app !== "string") {
12798
+ return void 0;
12799
+ }
12800
+ const app = value.app.trim();
12801
+ return app || void 0;
12802
+ }
12803
+ function isPlainObject(value) {
12804
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
12805
+ }
12932
12806
  function normalizeRepoConfig(value, configPath) {
12933
12807
  if (!value || typeof value !== "object") {
12934
12808
  return null;
@@ -13009,7 +12883,217 @@ function getOptionalString(value) {
13009
12883
  return trimmed ? trimmed : void 0;
13010
12884
  }
13011
12885
 
12886
+ // src/commands/ListCommandHandler.ts
12887
+ var ListCommandHandler = class {
12888
+ handle(args) {
12889
+ const targetDir = this.resolveTargetDir(args.targetPath);
12890
+ const config = this.loadConfig(targetDir);
12891
+ if (config.apps.length === 0) {
12892
+ ui.warn("No apps defined.");
12893
+ return;
12894
+ }
12895
+ for (const app of config.apps) {
12896
+ if (app && typeof app.app === "string") {
12897
+ if (typeof app.dir !== "string" || !app.dir.trim()) {
12898
+ throw new Error(`Invalid .pmate.yaml: missing dir for app ${app.app}`);
12899
+ }
12900
+ ui.info(`${app.app} -> ${app.dir}`);
12901
+ }
12902
+ }
12903
+ }
12904
+ resolveTargetDir(targetPath) {
12905
+ return targetPath ? import_node_path2.default.resolve(process.cwd(), targetPath) : process.cwd();
12906
+ }
12907
+ loadConfig(targetDir) {
12908
+ return loadAppConfig(targetDir).config;
12909
+ }
12910
+ };
12911
+
12912
+ // src/commands/LoginCommandHandler.ts
12913
+ var import_node_fs2 = require("fs");
12914
+ var import_node_http = require("http");
12915
+ var import_node_os2 = __toESM(require("os"));
12916
+ var import_node_path3 = __toESM(require("path"));
12917
+ var import_node_child_process = require("child_process");
12918
+ var AUTH_UI_BASE_URL = "https://auth.pmate.chat";
12919
+ var AUTH_API_BASE_URL = "https://auth-api-v2.pmate.chat";
12920
+ var CALLBACK_PATH = "/pmate-login-callback";
12921
+ var DEFAULT_APP_ID = "@pmate/cli";
12922
+ var LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
12923
+ function buildHostedLoginUrl(redirectUrl, appId = DEFAULT_APP_ID) {
12924
+ const url = new URL(AUTH_UI_BASE_URL);
12925
+ url.searchParams.set("redirect", redirectUrl);
12926
+ url.searchParams.set("app", appId);
12927
+ return url.toString();
12928
+ }
12929
+ var LoginCommandHandler = class {
12930
+ async handle(args) {
12931
+ const callback = await this.startCallbackServer();
12932
+ const appId = args.app?.trim() || DEFAULT_APP_ID;
12933
+ const loginUrl = buildHostedLoginUrl(callback.redirectUrl, appId);
12934
+ ui.info("Opening browser for login.", {
12935
+ url: loginUrl,
12936
+ app: appId
12937
+ });
12938
+ if (!this.openBrowser(loginUrl)) {
12939
+ ui.warn("Unable to open the browser automatically.");
12940
+ ui.plain(loginUrl);
12941
+ }
12942
+ ui.info("Waiting for login redirect...", {
12943
+ callback: callback.redirectUrl,
12944
+ app: appId
12945
+ });
12946
+ try {
12947
+ const token = await callback.waitForToken;
12948
+ const loginResponse = await this.fetchSession(token);
12949
+ const sessionPath = this.writeSession(loginResponse);
12950
+ ui.success(`Logged in. Session saved to ${sessionPath}`);
12951
+ } finally {
12952
+ await callback.close();
12953
+ }
12954
+ }
12955
+ async fetchSession(token) {
12956
+ const response = await fetch(`${AUTH_API_BASE_URL}/session`, {
12957
+ method: "GET",
12958
+ headers: {
12959
+ Authorization: `Bearer ${token}`
12960
+ }
12961
+ });
12962
+ if (!response.ok) {
12963
+ const detail = await response.text();
12964
+ throw new Error(
12965
+ `Failed to validate session (${response.status}): ${detail || response.statusText}`
12966
+ );
12967
+ }
12968
+ const resp = await response.json();
12969
+ return {
12970
+ ...resp.data,
12971
+ token
12972
+ };
12973
+ }
12974
+ async startCallbackServer() {
12975
+ let resolveToken;
12976
+ let rejectToken;
12977
+ const waitForToken = new Promise((resolve, reject) => {
12978
+ resolveToken = resolve;
12979
+ rejectToken = reject;
12980
+ });
12981
+ const server = (0, import_node_http.createServer)((request, response) => {
12982
+ this.handleCallbackRequest(request, response, resolveToken, rejectToken);
12983
+ });
12984
+ await new Promise((resolve, reject) => {
12985
+ server.once("error", reject);
12986
+ server.listen(0, "127.0.0.1", () => resolve());
12987
+ });
12988
+ const address = server.address();
12989
+ if (!address || typeof address === "string") {
12990
+ server.close();
12991
+ throw new Error("Failed to start login callback server.");
12992
+ }
12993
+ const redirectUrl = `http://127.0.0.1:${address.port}${CALLBACK_PATH}`;
12994
+ const timeout = setTimeout(() => {
12995
+ rejectToken(
12996
+ new Error(
12997
+ `Timed out waiting for browser login after ${LOGIN_TIMEOUT_MS / 1e3} seconds.`
12998
+ )
12999
+ );
13000
+ }, LOGIN_TIMEOUT_MS);
13001
+ const close = async () => {
13002
+ clearTimeout(timeout);
13003
+ await new Promise((resolve) => {
13004
+ server.close(() => resolve());
13005
+ });
13006
+ };
13007
+ return {
13008
+ redirectUrl,
13009
+ waitForToken,
13010
+ close
13011
+ };
13012
+ }
13013
+ handleCallbackRequest(request, response, resolveToken, rejectToken) {
13014
+ const url = new URL(request.url || "/", "http://127.0.0.1");
13015
+ if (url.pathname !== CALLBACK_PATH) {
13016
+ response.statusCode = 404;
13017
+ response.end("Not found");
13018
+ return;
13019
+ }
13020
+ const token = url.searchParams.get("sessionId")?.trim();
13021
+ if (!token) {
13022
+ response.statusCode = 400;
13023
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
13024
+ response.end(this.renderHtml("Login failed", "Missing sessionId in redirect URL."));
13025
+ rejectToken(new Error("Login callback did not include sessionId."));
13026
+ return;
13027
+ }
13028
+ response.statusCode = 200;
13029
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
13030
+ response.end(
13031
+ this.renderHtml(
13032
+ "Login complete",
13033
+ "You can close this tab and return to the terminal."
13034
+ )
13035
+ );
13036
+ resolveToken(token);
13037
+ }
13038
+ renderHtml(title, message) {
13039
+ return `<!doctype html>
13040
+ <html lang="en">
13041
+ <head>
13042
+ <meta charset="utf-8" />
13043
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
13044
+ <title>${title}</title>
13045
+ <style>
13046
+ body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; padding: 32px; color: #111; }
13047
+ main { max-width: 560px; margin: 10vh auto; }
13048
+ h1 { font-size: 28px; margin-bottom: 12px; }
13049
+ p { font-size: 16px; line-height: 1.5; }
13050
+ </style>
13051
+ </head>
13052
+ <body>
13053
+ <main>
13054
+ <h1>${title}</h1>
13055
+ <p>${message}</p>
13056
+ </main>
13057
+ </body>
13058
+ </html>`;
13059
+ }
13060
+ openBrowser(url) {
13061
+ const platform = process.platform;
13062
+ const command = platform === "darwin" ? { cmd: "open", args: [url] } : platform === "win32" ? { cmd: "cmd", args: ["/c", "start", "", url] } : { cmd: "xdg-open", args: [url] };
13063
+ try {
13064
+ const child = (0, import_node_child_process.spawn)(command.cmd, command.args, {
13065
+ detached: true,
13066
+ stdio: "ignore"
13067
+ });
13068
+ child.unref();
13069
+ return true;
13070
+ } catch {
13071
+ return false;
13072
+ }
13073
+ }
13074
+ writeSession(session) {
13075
+ const sessionDir = import_node_path3.default.join(import_node_os2.default.homedir(), ".pmate");
13076
+ const sessionPath = import_node_path3.default.join(sessionDir, "session.json");
13077
+ (0, import_node_fs2.mkdirSync)(sessionDir, { recursive: true });
13078
+ (0, import_node_fs2.writeFileSync)(sessionPath, JSON.stringify(session, null, 2), {
13079
+ mode: 384
13080
+ });
13081
+ try {
13082
+ (0, import_node_fs2.chmodSync)(sessionPath, 384);
13083
+ } catch {
13084
+ }
13085
+ return sessionPath;
13086
+ }
13087
+ };
13088
+
13012
13089
  // src/services/AliyunService.ts
13090
+ var import_alidns20150109 = __toESM(require("@alicloud/alidns20150109"));
13091
+ var import_cdn20180510 = __toESM(require("@alicloud/cdn20180510"));
13092
+ var import_openapi_client = require("@alicloud/openapi-client");
13093
+ var import_ali_oss = __toESM(require("ali-oss"));
13094
+ var import_node_crypto = require("crypto");
13095
+ var import_node_fs3 = require("fs");
13096
+ var import_node_path4 = __toESM(require("path"));
13013
13097
  var AliyunService = class {
13014
13098
  /** Add a DNS record via Aliyun DNS. */
13015
13099
  static async addRecord(input) {
@@ -13152,7 +13236,7 @@ var AliyunService = class {
13152
13236
  const options = contentType ? { headers: { "Content-Type": contentType } } : void 0;
13153
13237
  const existing = existingMap.get(objectKey);
13154
13238
  if (existing && typeof existing.size === "number") {
13155
- const stat = await import_node_fs4.promises.stat(filePath);
13239
+ const stat = await import_node_fs3.promises.stat(filePath);
13156
13240
  if (stat.size === existing.size) {
13157
13241
  const etag = this.normalizeEtag(existing.etag);
13158
13242
  if (etag && !etag.includes("-")) {
@@ -13177,7 +13261,7 @@ var AliyunService = class {
13177
13261
  if (!region) throw new Error("Missing oss.public.region in ~/.pmate/config.yaml.");
13178
13262
  if (!accessKeyId) throw new Error("Missing aliyun.ak in ~/.pmate/config.yaml.");
13179
13263
  if (!accessKeySecret) throw new Error("Missing aliyun.sk in ~/.pmate/config.yaml.");
13180
- if (!(0, import_node_fs4.existsSync)(localFile)) {
13264
+ if (!(0, import_node_fs3.existsSync)(localFile)) {
13181
13265
  throw new Error(`File not found: ${localFile}`);
13182
13266
  }
13183
13267
  const client = new import_ali_oss.default({ region, accessKeyId, accessKeySecret, bucket });
@@ -13452,7 +13536,7 @@ var AliyunService = class {
13452
13536
  }
13453
13537
  /** Recursively collect file paths under a directory. */
13454
13538
  static async collectFiles(dir) {
13455
- const entries = await import_node_fs4.promises.readdir(dir, { withFileTypes: true });
13539
+ const entries = await import_node_fs3.promises.readdir(dir, { withFileTypes: true });
13456
13540
  const files = [];
13457
13541
  for (const entry of entries) {
13458
13542
  const fullPath = import_node_path4.default.join(dir, entry.name);
@@ -13504,7 +13588,7 @@ var AliyunService = class {
13504
13588
  static getFileMd5(filePath) {
13505
13589
  return new Promise((resolve, reject) => {
13506
13590
  const hash = (0, import_node_crypto.createHash)("md5");
13507
- const stream = (0, import_node_fs4.createReadStream)(filePath);
13591
+ const stream = (0, import_node_fs3.createReadStream)(filePath);
13508
13592
  stream.on("data", (chunk) => {
13509
13593
  hash.update(chunk);
13510
13594
  });
@@ -13589,7 +13673,7 @@ var AddDnsCommandHandler = class {
13589
13673
  };
13590
13674
 
13591
13675
  // src/commands/StsCommandHandler.ts
13592
- var import_node_fs5 = require("fs");
13676
+ var import_node_fs4 = require("fs");
13593
13677
  var import_node_os3 = __toESM(require("os"));
13594
13678
  var import_node_path5 = __toESM(require("path"));
13595
13679
  var import_chalk2 = __toESM(require("chalk"));
@@ -13621,10 +13705,10 @@ var StsCommandHandler = class {
13621
13705
  }
13622
13706
  readSession() {
13623
13707
  const sessionPath = import_node_path5.default.join(import_node_os3.default.homedir(), ".pmate", "session.json");
13624
- if (!(0, import_node_fs5.existsSync)(sessionPath)) {
13708
+ if (!(0, import_node_fs4.existsSync)(sessionPath)) {
13625
13709
  throw new Error("Missing session. Run `pmate login` first.");
13626
13710
  }
13627
- const raw = (0, import_node_fs5.readFileSync)(sessionPath, "utf8");
13711
+ const raw = (0, import_node_fs4.readFileSync)(sessionPath, "utf8");
13628
13712
  return JSON.parse(raw);
13629
13713
  }
13630
13714
  };
@@ -13639,10 +13723,9 @@ var BucketCommandHandler = class {
13639
13723
  };
13640
13724
 
13641
13725
  // src/commands/DeployCommandHandler.ts
13642
- var import_node_fs6 = require("fs");
13726
+ var import_node_fs5 = require("fs");
13643
13727
  var import_node_path7 = __toESM(require("path"));
13644
13728
  var import_node_child_process2 = require("child_process");
13645
- var import_yaml3 = __toESM(require("yaml"));
13646
13729
 
13647
13730
  // src/appDir.ts
13648
13731
  var import_node_path6 = __toESM(require("path"));
@@ -13692,11 +13775,12 @@ var DeployCommandHandler = class {
13692
13775
  );
13693
13776
  }
13694
13777
  const cwd = this.resolveCwd(args.deployCwd);
13695
- const config = this.loadConfig(cwd);
13778
+ const loadedConfig = this.loadConfig(cwd);
13779
+ const config = loadedConfig.config;
13696
13780
  const appConfig = config.apps.find((entry) => entry?.app === appName);
13697
13781
  if (!appConfig) {
13698
13782
  throw new Error(
13699
- `No app named ${appName} found in ${import_node_path7.default.join(cwd, ".pmate.yaml")}`
13783
+ `No app named ${appName} found in ${describeLoadedAppConfig(loadedConfig)}`
13700
13784
  );
13701
13785
  }
13702
13786
  if (args.deployAction === "logs") {
@@ -13740,7 +13824,7 @@ var DeployCommandHandler = class {
13740
13824
  async deployVite(appName, cwd, appConfig) {
13741
13825
  const appDir = resolveLocalAppDir(cwd, appName, appConfig.dir);
13742
13826
  const packageJsonPath = import_node_path7.default.join(appDir, "package.json");
13743
- if (!(0, import_node_fs6.existsSync)(packageJsonPath)) {
13827
+ if (!(0, import_node_fs5.existsSync)(packageJsonPath)) {
13744
13828
  throw new Error(`No package.json found at ${packageJsonPath}`);
13745
13829
  }
13746
13830
  const pkg2 = this.loadPackageJson(packageJsonPath);
@@ -13750,7 +13834,7 @@ var DeployCommandHandler = class {
13750
13834
  }
13751
13835
  await this.runCommand("pnpm", ["--filter", pkgName, "build"], cwd);
13752
13836
  const distDir = import_node_path7.default.join(appDir, "dist");
13753
- if (!(0, import_node_fs6.existsSync)(distDir)) {
13837
+ if (!(0, import_node_fs5.existsSync)(distDir)) {
13754
13838
  throw new Error(`No dist directory found at ${distDir}`);
13755
13839
  }
13756
13840
  const bucketName = appConfig.bucket?.trim();
@@ -13764,7 +13848,7 @@ var DeployCommandHandler = class {
13764
13848
  async deployStatic(appName, cwd, appConfig) {
13765
13849
  const appDir = resolveLocalAppDir(cwd, appName, appConfig.dir);
13766
13850
  const srcDir = import_node_path7.default.join(appDir, "src");
13767
- if (!(0, import_node_fs6.existsSync)(srcDir)) {
13851
+ if (!(0, import_node_fs5.existsSync)(srcDir)) {
13768
13852
  throw new Error(`No src directory found at ${srcDir}`);
13769
13853
  }
13770
13854
  const bucketName = appConfig.bucket?.trim();
@@ -13879,7 +13963,7 @@ var DeployCommandHandler = class {
13879
13963
  return target ? import_node_path7.default.resolve(process.cwd(), target) : process.cwd();
13880
13964
  }
13881
13965
  loadPackageJson(packageJsonPath) {
13882
- const raw = (0, import_node_fs6.readFileSync)(packageJsonPath, "utf8");
13966
+ const raw = (0, import_node_fs5.readFileSync)(packageJsonPath, "utf8");
13883
13967
  const parsed = JSON.parse(raw);
13884
13968
  if (!parsed || typeof parsed !== "object") {
13885
13969
  throw new Error(`Invalid package.json at ${packageJsonPath}`);
@@ -13887,16 +13971,7 @@ var DeployCommandHandler = class {
13887
13971
  return parsed;
13888
13972
  }
13889
13973
  loadConfig(cwd) {
13890
- const configPath = import_node_path7.default.join(cwd, ".pmate.yaml");
13891
- if (!(0, import_node_fs6.existsSync)(configPath)) {
13892
- throw new Error(`No .pmate.yaml found in ${cwd}`);
13893
- }
13894
- const raw = (0, import_node_fs6.readFileSync)(configPath, "utf8");
13895
- const parsed = import_yaml3.default.parse(raw);
13896
- if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.apps)) {
13897
- throw new Error("Invalid .pmate.yaml: missing apps array");
13898
- }
13899
- return parsed;
13974
+ return loadAppConfig(cwd);
13900
13975
  }
13901
13976
  runCommand(command, args, cwd) {
13902
13977
  return new Promise((resolve, reject) => {
@@ -13966,11 +14041,10 @@ var DeployCommandHandler = class {
13966
14041
  };
13967
14042
 
13968
14043
  // src/commands/LogCommandHandler.ts
13969
- var import_node_fs7 = require("fs");
14044
+ var import_node_fs6 = require("fs");
13970
14045
  var import_node_child_process3 = require("child_process");
13971
14046
  var import_node_path8 = __toESM(require("path"));
13972
14047
  var import_node_module = require("module");
13973
- var import_yaml4 = __toESM(require("yaml"));
13974
14048
  var LogCommandHandler = class {
13975
14049
  async handle(args) {
13976
14050
  const appName = args.logApp.trim();
@@ -13978,14 +14052,13 @@ var LogCommandHandler = class {
13978
14052
  throw new Error("Missing app name. Usage: log <app-name> [--cwd <dir>].");
13979
14053
  }
13980
14054
  const cwd = this.resolveCwd(args.logCwd);
13981
- const { config, configPath } = this.loadClosestConfig(cwd);
14055
+ const { config, configPath, repoRoot } = this.loadClosestConfig(cwd);
13982
14056
  const appConfig = config.apps.find((entry) => entry?.app === appName);
13983
14057
  if (!appConfig) {
13984
14058
  throw new Error(`No app named ${appName} found in ${configPath}`);
13985
14059
  }
13986
14060
  if (appConfig.type === "node") {
13987
14061
  const server = this.resolveNodeServer(appName, appConfig);
13988
- const repoRoot = import_node_path8.default.dirname(configPath);
13989
14062
  const pm2Name = this.resolvePm2AppName(repoRoot, appName, appConfig);
13990
14063
  await this.runRemotePm2Logs(server, pm2Name);
13991
14064
  return;
@@ -14002,30 +14075,12 @@ var LogCommandHandler = class {
14002
14075
  return target ? import_node_path8.default.resolve(process.cwd(), target) : process.cwd();
14003
14076
  }
14004
14077
  loadClosestConfig(cwd) {
14005
- const configPath = this.findRepoConfigPath(cwd);
14006
- if (!configPath) {
14007
- throw new Error(`No .pmate.yaml found in ${cwd} or parent directories`);
14008
- }
14009
- const raw = (0, import_node_fs7.readFileSync)(configPath, "utf8");
14010
- const parsed = import_yaml4.default.parse(raw);
14011
- if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.apps)) {
14012
- throw new Error(`Invalid .pmate.yaml: missing apps array (${configPath})`);
14013
- }
14014
- return { config: parsed, configPath };
14015
- }
14016
- findRepoConfigPath(cwd) {
14017
- let current = import_node_path8.default.resolve(cwd);
14018
- while (true) {
14019
- const candidate = import_node_path8.default.join(current, ".pmate.yaml");
14020
- if ((0, import_node_fs7.existsSync)(candidate)) {
14021
- return candidate;
14022
- }
14023
- const parent = import_node_path8.default.dirname(current);
14024
- if (parent === current) {
14025
- return null;
14026
- }
14027
- current = parent;
14028
- }
14078
+ const loaded = loadAppConfig(cwd, { searchParents: true });
14079
+ return {
14080
+ config: loaded.config,
14081
+ configPath: describeLoadedAppConfig(loaded),
14082
+ repoRoot: loaded.rootDir
14083
+ };
14029
14084
  }
14030
14085
  resolveNodeServer(appName, appConfig) {
14031
14086
  const server = appConfig.server?.trim();
@@ -14049,11 +14104,11 @@ var LogCommandHandler = class {
14049
14104
  }
14050
14105
  findEcosystemPath(appDir) {
14051
14106
  const cjs = import_node_path8.default.join(appDir, "ecosystem.config.cjs");
14052
- if ((0, import_node_fs7.existsSync)(cjs)) {
14107
+ if ((0, import_node_fs6.existsSync)(cjs)) {
14053
14108
  return cjs;
14054
14109
  }
14055
14110
  const js = import_node_path8.default.join(appDir, "ecosystem.config.js");
14056
- if ((0, import_node_fs7.existsSync)(js)) {
14111
+ if ((0, import_node_fs6.existsSync)(js)) {
14057
14112
  return js;
14058
14113
  }
14059
14114
  return null;
@@ -14154,11 +14209,11 @@ var LogCommandHandler = class {
14154
14209
  };
14155
14210
 
14156
14211
  // src/commands/InitCommandHandler.ts
14157
- var import_node_fs8 = require("fs");
14212
+ var import_node_fs7 = require("fs");
14158
14213
  var import_node_path9 = __toESM(require("path"));
14159
14214
  var import_node_child_process4 = require("child_process");
14160
14215
  var import_promises2 = require("readline/promises");
14161
- var import_yaml5 = __toESM(require("yaml"));
14216
+ var import_yaml2 = __toESM(require("yaml"));
14162
14217
 
14163
14218
  // src/serverAddress.ts
14164
14219
  var import_promises = require("dns/promises");
@@ -14186,14 +14241,14 @@ var InitCommandHandler = class {
14186
14241
  throw new Error("Missing app name. Usage: init <app-name> [--cwd <dir>].");
14187
14242
  }
14188
14243
  const cwd = this.resolveCwd(args.initCwd);
14189
- const config = this.loadConfig(cwd);
14244
+ const { config } = this.loadConfig(cwd);
14190
14245
  let appConfig = config.apps.find((entry) => entry?.app === appName);
14191
14246
  if (!appConfig) {
14192
14247
  appConfig = await this.addAppConfig(config, appName, cwd);
14193
14248
  }
14194
14249
  if (appConfig.type !== "rust") {
14195
14250
  const appDir = resolveLocalAppDir(cwd, appName, appConfig.dir);
14196
- if (!(0, import_node_fs8.existsSync)(appDir)) {
14251
+ if (!(0, import_node_fs7.existsSync)(appDir)) {
14197
14252
  throw new Error(`No app folder found at ${appDir}`);
14198
14253
  }
14199
14254
  }
@@ -14361,10 +14416,10 @@ var InitCommandHandler = class {
14361
14416
  const serverAddress = await resolveServerAddress(server);
14362
14417
  const templateName = serviceType === "ws" ? "nginx.ws.template" : "nginx.rest.template";
14363
14418
  const templatePath = import_node_path9.default.join(__dirname, "..", "templates", templateName);
14364
- if (!(0, import_node_fs8.existsSync)(templatePath)) {
14419
+ if (!(0, import_node_fs7.existsSync)(templatePath)) {
14365
14420
  throw new Error(`Missing nginx template at ${templatePath}`);
14366
14421
  }
14367
- const template = (0, import_node_fs8.readFileSync)(templatePath, "utf8");
14422
+ const template = (0, import_node_fs7.readFileSync)(templatePath, "utf8");
14368
14423
  const host = `${subdomain}.pmate.chat`;
14369
14424
  const rendered = template.replace(/\$\{\{PORT\}\}/g, String(port)).replace(/\$\{\{HOSTS\}\}/g, host).replace(/\$\{\{HOST\}\}/g, host);
14370
14425
  const remotePath = `/etc/nginx/sites-enabled/${subdomain}.conf`;
@@ -14397,15 +14452,15 @@ var InitCommandHandler = class {
14397
14452
  if (options.ensurePackageJson) {
14398
14453
  const appDir = resolveLocalAppDir(cwd, appConfig.app, appConfig.dir);
14399
14454
  ui.info(`Resolved appDir: ${appDir}`);
14400
- if (!(0, import_node_fs8.existsSync)(appDir)) {
14455
+ if (!(0, import_node_fs7.existsSync)(appDir)) {
14401
14456
  throw new Error(`Missing app folder at ${appDir}`);
14402
14457
  }
14403
14458
  const packageJsonPath = import_node_path9.default.join(appDir, "package.json");
14404
14459
  ui.info(`Resolved package.json: ${packageJsonPath}`);
14405
- if (!(0, import_node_fs8.existsSync)(packageJsonPath)) {
14460
+ if (!(0, import_node_fs7.existsSync)(packageJsonPath)) {
14406
14461
  throw new Error(`Missing package.json at ${packageJsonPath}`);
14407
14462
  }
14408
- const pkgStat = (0, import_node_fs8.statSync)(packageJsonPath);
14463
+ const pkgStat = (0, import_node_fs7.statSync)(packageJsonPath);
14409
14464
  ui.info(
14410
14465
  `package.json stat: isFile=${pkgStat.isFile()} isDirectory=${pkgStat.isDirectory()}`
14411
14466
  );
@@ -14471,16 +14526,7 @@ var InitCommandHandler = class {
14471
14526
  return new Promise((resolve) => setTimeout(resolve, ms));
14472
14527
  }
14473
14528
  loadConfig(cwd) {
14474
- const configPath = import_node_path9.default.join(cwd, ".pmate.yaml");
14475
- if (!(0, import_node_fs8.existsSync)(configPath)) {
14476
- throw new Error(`No .pmate.yaml found in ${cwd}`);
14477
- }
14478
- const raw = (0, import_node_fs8.readFileSync)(configPath, "utf8");
14479
- const parsed = import_yaml5.default.parse(raw);
14480
- if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.apps)) {
14481
- throw new Error("Invalid .pmate.yaml: missing apps array");
14482
- }
14483
- return parsed;
14529
+ return loadAppConfig(cwd);
14484
14530
  }
14485
14531
  async addAppConfig(config, appName, cwd) {
14486
14532
  ui.title(`App ${appName} not found in .pmate.yaml.`);
@@ -14598,7 +14644,7 @@ var InitCommandHandler = class {
14598
14644
  }
14599
14645
  }
14600
14646
  loadPackageJson(packageJsonPath) {
14601
- const raw = (0, import_node_fs8.readFileSync)(packageJsonPath, "utf8");
14647
+ const raw = (0, import_node_fs7.readFileSync)(packageJsonPath, "utf8");
14602
14648
  const parsed = JSON.parse(raw);
14603
14649
  if (!parsed || typeof parsed !== "object") {
14604
14650
  throw new Error(`Invalid package.json at ${packageJsonPath}`);
@@ -14607,13 +14653,13 @@ var InitCommandHandler = class {
14607
14653
  }
14608
14654
  writePackageJson(packageJsonPath, data) {
14609
14655
  const serialized = JSON.stringify(data, null, 2);
14610
- (0, import_node_fs8.writeFileSync)(packageJsonPath, `${serialized}
14656
+ (0, import_node_fs7.writeFileSync)(packageJsonPath, `${serialized}
14611
14657
  `, "utf8");
14612
14658
  }
14613
14659
  writeConfig(cwd, config) {
14614
14660
  const configPath = import_node_path9.default.join(cwd, ".pmate.yaml");
14615
- const serialized = import_yaml5.default.stringify(config).trimEnd();
14616
- (0, import_node_fs8.writeFileSync)(configPath, `${serialized}
14661
+ const serialized = import_yaml2.default.stringify(config).trimEnd();
14662
+ (0, import_node_fs7.writeFileSync)(configPath, `${serialized}
14617
14663
  `, "utf8");
14618
14664
  }
14619
14665
  async promptRequired(rl, question) {
@@ -14649,10 +14695,9 @@ var InitCommandHandler = class {
14649
14695
  };
14650
14696
 
14651
14697
  // src/commands/DevCommandHandler.ts
14652
- var import_node_fs9 = require("fs");
14698
+ var import_node_fs8 = require("fs");
14653
14699
  var import_node_path10 = __toESM(require("path"));
14654
14700
  var import_node_child_process5 = require("child_process");
14655
- var import_yaml6 = __toESM(require("yaml"));
14656
14701
  var DevCommandHandler = class {
14657
14702
  async handle(args) {
14658
14703
  const appName = args.devApp.trim();
@@ -14660,16 +14705,17 @@ var DevCommandHandler = class {
14660
14705
  throw new Error("Missing app name. Usage: dev <app-name> [--cwd <dir>].");
14661
14706
  }
14662
14707
  const cwd = this.resolveCwd(args.devCwd);
14663
- const config = this.loadConfig(cwd);
14708
+ const loadedConfig = this.loadConfig(cwd);
14709
+ const config = loadedConfig.config;
14664
14710
  const appConfig = config.apps.find((entry) => entry?.app === appName);
14665
14711
  if (!appConfig) {
14666
14712
  throw new Error(
14667
- `No app named ${appName} found in ${import_node_path10.default.join(cwd, ".pmate.yaml")}`
14713
+ `No app named ${appName} found in ${describeLoadedAppConfig(loadedConfig)}`
14668
14714
  );
14669
14715
  }
14670
14716
  const appDir = resolveLocalAppDir(cwd, appName, appConfig.dir);
14671
14717
  const packageJsonPath = import_node_path10.default.join(appDir, "package.json");
14672
- if (!(0, import_node_fs9.existsSync)(packageJsonPath)) {
14718
+ if (!(0, import_node_fs8.existsSync)(packageJsonPath)) {
14673
14719
  throw new Error(`No package.json found at ${packageJsonPath}`);
14674
14720
  }
14675
14721
  const pkg2 = this.loadPackageJson(packageJsonPath);
@@ -14686,19 +14732,10 @@ var DevCommandHandler = class {
14686
14732
  return target ? import_node_path10.default.resolve(process.cwd(), target) : process.cwd();
14687
14733
  }
14688
14734
  loadConfig(cwd) {
14689
- const configPath = import_node_path10.default.join(cwd, ".pmate.yaml");
14690
- if (!(0, import_node_fs9.existsSync)(configPath)) {
14691
- throw new Error(`No .pmate.yaml found in ${cwd}`);
14692
- }
14693
- const raw = (0, import_node_fs9.readFileSync)(configPath, "utf8");
14694
- const parsed = import_yaml6.default.parse(raw);
14695
- if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.apps)) {
14696
- throw new Error("Invalid .pmate.yaml: missing apps array");
14697
- }
14698
- return parsed;
14735
+ return loadAppConfig(cwd);
14699
14736
  }
14700
14737
  loadPackageJson(packageJsonPath) {
14701
- const raw = (0, import_node_fs9.readFileSync)(packageJsonPath, "utf8");
14738
+ const raw = (0, import_node_fs8.readFileSync)(packageJsonPath, "utf8");
14702
14739
  const parsed = JSON.parse(raw);
14703
14740
  if (!parsed || typeof parsed !== "object") {
14704
14741
  throw new Error(`Invalid package.json at ${packageJsonPath}`);
@@ -14726,7 +14763,7 @@ var DevCommandHandler = class {
14726
14763
  };
14727
14764
 
14728
14765
  // src/commands/OssCommandHandler.ts
14729
- var import_node_fs10 = require("fs");
14766
+ var import_node_fs9 = require("fs");
14730
14767
  var import_node_path11 = __toESM(require("path"));
14731
14768
  var import_promises3 = require("readline/promises");
14732
14769
  var OssCommandHandler = class {
@@ -14762,10 +14799,10 @@ var OssCommandHandler = class {
14762
14799
  }
14763
14800
  }
14764
14801
  assertFile(filePath) {
14765
- if (!(0, import_node_fs10.existsSync)(filePath)) {
14802
+ if (!(0, import_node_fs9.existsSync)(filePath)) {
14766
14803
  throw new Error(`File not found: ${filePath}`);
14767
14804
  }
14768
- const stat = (0, import_node_fs10.statSync)(filePath);
14805
+ const stat = (0, import_node_fs9.statSync)(filePath);
14769
14806
  if (!stat.isFile()) {
14770
14807
  throw new Error(`Not a file: ${filePath}`);
14771
14808
  }
@@ -14801,10 +14838,8 @@ var OssCommandHandler = class {
14801
14838
  };
14802
14839
 
14803
14840
  // src/commands/EnvCommandHandler.ts
14804
- var import_node_fs11 = require("fs");
14805
14841
  var import_node_path12 = __toESM(require("path"));
14806
14842
  var import_node_child_process6 = require("child_process");
14807
- var import_yaml7 = __toESM(require("yaml"));
14808
14843
  var EnvCommandHandler = class {
14809
14844
  async handle(args) {
14810
14845
  const appName = args.envApp.trim();
@@ -14813,14 +14848,12 @@ var EnvCommandHandler = class {
14813
14848
  "Missing app name. Usage: env <app-name> --list|--set KEY --value VALUE|--delete KEY [--cwd <dir>] [--verbose|-v]"
14814
14849
  );
14815
14850
  }
14816
- const config = this.loadConfig(args.envCwd);
14851
+ const loadedConfig = this.loadConfig(args.envCwd);
14852
+ const config = loadedConfig.config;
14817
14853
  const appConfig = config.apps.find((entry) => entry?.app === appName);
14818
14854
  if (!appConfig) {
14819
14855
  throw new Error(
14820
- `No app named ${appName} found in ${import_node_path12.default.join(
14821
- process.cwd(),
14822
- ".pmate.yaml"
14823
- )}`
14856
+ `No app named ${appName} found in ${describeLoadedAppConfig(loadedConfig)}`
14824
14857
  );
14825
14858
  }
14826
14859
  if (appConfig.type !== "node") {
@@ -15088,16 +15121,7 @@ var EnvCommandHandler = class {
15088
15121
  }
15089
15122
  }
15090
15123
  loadConfig(cwd) {
15091
- const configPath = import_node_path12.default.join(cwd, ".pmate.yaml");
15092
- if (!(0, import_node_fs11.existsSync)(configPath)) {
15093
- throw new Error(`No .pmate.yaml found in ${cwd}`);
15094
- }
15095
- const raw = (0, import_node_fs11.readFileSync)(configPath, "utf8");
15096
- const parsed = import_yaml7.default.parse(raw);
15097
- if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.apps)) {
15098
- throw new Error("Invalid .pmate.yaml: missing apps array");
15099
- }
15100
- return parsed;
15124
+ return loadAppConfig(cwd);
15101
15125
  }
15102
15126
  runRemoteCommand(server, command) {
15103
15127
  return new Promise((resolve, reject) => {
@@ -16000,7 +16024,7 @@ function truncate2(value, limit) {
16000
16024
 
16001
16025
  // src/commands/LinearCommandHandler.ts
16002
16026
  var import_node_child_process9 = require("child_process");
16003
- var import_node_fs13 = require("fs");
16027
+ var import_node_fs11 = require("fs");
16004
16028
  var import_node_os5 = __toESM(require("os"));
16005
16029
  var import_node_path15 = __toESM(require("path"));
16006
16030
  var import_promises4 = require("readline/promises");
@@ -16890,11 +16914,11 @@ var LinearCommandHandler = class {
16890
16914
  }
16891
16915
  readCachedLinearSelection(cwd) {
16892
16916
  const selectionPath = this.getLinearSelectionPath();
16893
- if (!(0, import_node_fs13.existsSync)(selectionPath)) {
16917
+ if (!(0, import_node_fs11.existsSync)(selectionPath)) {
16894
16918
  return void 0;
16895
16919
  }
16896
16920
  try {
16897
- const raw = (0, import_node_fs13.readFileSync)(selectionPath, "utf8").trim();
16921
+ const raw = (0, import_node_fs11.readFileSync)(selectionPath, "utf8").trim();
16898
16922
  if (!raw) return void 0;
16899
16923
  const parsed = JSON.parse(raw);
16900
16924
  const value = parsed[cwd];
@@ -16906,11 +16930,11 @@ var LinearCommandHandler = class {
16906
16930
  writeCachedLinearSelection(cwd, title) {
16907
16931
  const selectionPath = this.getLinearSelectionPath();
16908
16932
  const dir = import_node_path15.default.dirname(selectionPath);
16909
- (0, import_node_fs13.mkdirSync)(dir, { recursive: true });
16933
+ (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
16910
16934
  let data = {};
16911
- if ((0, import_node_fs13.existsSync)(selectionPath)) {
16935
+ if ((0, import_node_fs11.existsSync)(selectionPath)) {
16912
16936
  try {
16913
- const raw = (0, import_node_fs13.readFileSync)(selectionPath, "utf8").trim();
16937
+ const raw = (0, import_node_fs11.readFileSync)(selectionPath, "utf8").trim();
16914
16938
  if (raw) {
16915
16939
  data = JSON.parse(raw);
16916
16940
  }
@@ -16919,7 +16943,7 @@ var LinearCommandHandler = class {
16919
16943
  }
16920
16944
  }
16921
16945
  data[cwd] = title;
16922
- (0, import_node_fs13.writeFileSync)(selectionPath, `${JSON.stringify(data, null, 2)}
16946
+ (0, import_node_fs11.writeFileSync)(selectionPath, `${JSON.stringify(data, null, 2)}
16923
16947
  `, "utf8");
16924
16948
  }
16925
16949
  async lookupIssue(client, issueRef) {
@@ -17096,14 +17120,14 @@ var LinearCommandHandler = class {
17096
17120
  if (process.platform !== "darwin") {
17097
17121
  throw new Error("Screenshot capture only supports macOS.");
17098
17122
  }
17099
- const dir = await import_node_fs13.promises.mkdtemp(import_node_path15.default.join(import_node_os5.default.tmpdir(), "pmate-screenshot-"));
17123
+ const dir = await import_node_fs11.promises.mkdtemp(import_node_path15.default.join(import_node_os5.default.tmpdir(), "pmate-screenshot-"));
17100
17124
  const filePath = import_node_path15.default.join(dir, `screenshot-${index}.png`);
17101
17125
  try {
17102
17126
  const code = await this.run("screencapture", ["-i", "-x", filePath], true);
17103
17127
  if (code !== 0) {
17104
17128
  throw new Error("Screenshot capture canceled or failed.");
17105
17129
  }
17106
- const buffer = await import_node_fs13.promises.readFile(filePath);
17130
+ const buffer = await import_node_fs11.promises.readFile(filePath);
17107
17131
  if (buffer.length === 0) {
17108
17132
  throw new Error("Screenshot capture produced an empty file.");
17109
17133
  }
@@ -17115,7 +17139,7 @@ var LinearCommandHandler = class {
17115
17139
  return { buffer, mime, filename: `screenshot-${index}.${extension}` };
17116
17140
  } finally {
17117
17141
  try {
17118
- await import_node_fs13.promises.rm(dir, { recursive: true, force: true });
17142
+ await import_node_fs11.promises.rm(dir, { recursive: true, force: true });
17119
17143
  } catch {
17120
17144
  }
17121
17145
  }
@@ -17259,17 +17283,17 @@ var LinearCommandHandler = class {
17259
17283
  return compact.length > limit ? `${compact.slice(0, limit)}...` : compact;
17260
17284
  }
17261
17285
  async convertToPng(buffer, ext) {
17262
- const dir = await import_node_fs13.promises.mkdtemp(import_node_path15.default.join(import_node_os5.default.tmpdir(), "pmate-"));
17286
+ const dir = await import_node_fs11.promises.mkdtemp(import_node_path15.default.join(import_node_os5.default.tmpdir(), "pmate-"));
17263
17287
  const inputPath = import_node_path15.default.join(dir, `clipboard.${ext}`);
17264
17288
  const outputPath = import_node_path15.default.join(dir, "clipboard.png");
17265
17289
  try {
17266
- await import_node_fs13.promises.writeFile(inputPath, buffer);
17290
+ await import_node_fs11.promises.writeFile(inputPath, buffer);
17267
17291
  const stderr = await this.runCapture(
17268
17292
  "sips",
17269
17293
  ["-s", "format", "png", inputPath, "--out", outputPath],
17270
17294
  true
17271
17295
  );
17272
- const out = await import_node_fs13.promises.readFile(outputPath);
17296
+ const out = await import_node_fs11.promises.readFile(outputPath);
17273
17297
  if (this.detectImageMime(out) !== "image/png") {
17274
17298
  throw new Error(
17275
17299
  stderr?.trim() ? `Failed to convert clipboard ${ext} to PNG: ${stderr.trim()}` : `Failed to convert clipboard ${ext} to PNG.`
@@ -17278,7 +17302,7 @@ var LinearCommandHandler = class {
17278
17302
  return out;
17279
17303
  } finally {
17280
17304
  try {
17281
- await import_node_fs13.promises.rm(dir, { recursive: true, force: true });
17305
+ await import_node_fs11.promises.rm(dir, { recursive: true, force: true });
17282
17306
  } catch {
17283
17307
  }
17284
17308
  }
@@ -17624,7 +17648,7 @@ function sleep(ms) {
17624
17648
  }
17625
17649
 
17626
17650
  // src/commands/NotionCommandHandler.ts
17627
- var import_node_fs14 = require("fs");
17651
+ var import_node_fs12 = require("fs");
17628
17652
  var import_node_path16 = __toESM(require("path"));
17629
17653
 
17630
17654
  // src/services/NotionService.ts
@@ -17965,13 +17989,13 @@ var NotionCommandHandler = class {
17965
17989
  return "";
17966
17990
  }
17967
17991
  const resolved = import_node_path16.default.resolve(process.cwd(), args.filePath);
17968
- return (0, import_node_fs14.readFileSync)(resolved, "utf8");
17992
+ return (0, import_node_fs12.readFileSync)(resolved, "utf8");
17969
17993
  }
17970
17994
  };
17971
17995
 
17972
17996
  // src/commands/QrcodeCommandHandler.ts
17973
17997
  var import_node_child_process10 = require("child_process");
17974
- var import_node_fs15 = require("fs");
17998
+ var import_node_fs13 = require("fs");
17975
17999
  var import_node_os6 = __toESM(require("os"));
17976
18000
  var import_node_path17 = __toESM(require("path"));
17977
18001
  var import_promises5 = require("readline/promises");
@@ -17999,7 +18023,7 @@ var QrcodeCommandHandler = class {
17999
18023
  ui.success("QR code value:", value);
18000
18024
  } finally {
18001
18025
  try {
18002
- await import_node_fs15.promises.rm(dir, { recursive: true, force: true });
18026
+ await import_node_fs13.promises.rm(dir, { recursive: true, force: true });
18003
18027
  } catch {
18004
18028
  }
18005
18029
  }
@@ -18015,13 +18039,13 @@ var QrcodeCommandHandler = class {
18015
18039
  } finally {
18016
18040
  rl.close();
18017
18041
  }
18018
- const dir = await import_node_fs15.promises.mkdtemp(import_node_path17.default.join(import_node_os6.default.tmpdir(), "pmate-qrcode-"));
18042
+ const dir = await import_node_fs13.promises.mkdtemp(import_node_path17.default.join(import_node_os6.default.tmpdir(), "pmate-qrcode-"));
18019
18043
  const filePath = import_node_path17.default.join(dir, "qrcode.png");
18020
18044
  const code = await this.run("screencapture", ["-i", "-x", filePath], true);
18021
18045
  if (code !== 0) {
18022
18046
  throw new Error("Screenshot capture canceled or failed.");
18023
18047
  }
18024
- const buffer = await import_node_fs15.promises.readFile(filePath);
18048
+ const buffer = await import_node_fs13.promises.readFile(filePath);
18025
18049
  if (buffer.length === 0) {
18026
18050
  throw new Error("Screenshot capture produced an empty file.");
18027
18051
  }
@@ -18037,7 +18061,7 @@ var QrcodeCommandHandler = class {
18037
18061
  async decodeQr(filePath) {
18038
18062
  let buffer;
18039
18063
  try {
18040
- buffer = await import_node_fs15.promises.readFile(filePath);
18064
+ buffer = await import_node_fs13.promises.readFile(filePath);
18041
18065
  } catch {
18042
18066
  return null;
18043
18067
  }
@@ -18297,7 +18321,7 @@ function clampNumber(value, min, max, fallback) {
18297
18321
  // src/commands/CollectCommandHandler.ts
18298
18322
  var import_playwright = require("playwright");
18299
18323
  var import_node_crypto3 = require("crypto");
18300
- var import_node_fs16 = require("fs");
18324
+ var import_node_fs14 = require("fs");
18301
18325
  var import_node_os7 = __toESM(require("os"));
18302
18326
  var import_node_path18 = __toESM(require("path"));
18303
18327
  var CollectCommandHandler = class {
@@ -18674,11 +18698,11 @@ async function toDisplayableImagePath(src) {
18674
18698
  return src;
18675
18699
  }
18676
18700
  try {
18677
- const stat = await import_node_fs16.promises.stat(src);
18701
+ const stat = await import_node_fs14.promises.stat(src);
18678
18702
  if (!stat.isFile()) {
18679
18703
  return src;
18680
18704
  }
18681
- const fh = await import_node_fs16.promises.open(src, "r");
18705
+ const fh = await import_node_fs14.promises.open(src, "r");
18682
18706
  try {
18683
18707
  const buf = Buffer.alloc(32);
18684
18708
  const { bytesRead } = await fh.read(buf, 0, 32, 0);
@@ -18693,11 +18717,11 @@ async function toDisplayableImagePath(src) {
18693
18717
  "messages",
18694
18718
  "images"
18695
18719
  );
18696
- await import_node_fs16.promises.mkdir(cacheDir, { recursive: true });
18720
+ await import_node_fs14.promises.mkdir(cacheDir, { recursive: true });
18697
18721
  const base = (0, import_node_crypto3.createHash)("sha1").update(src).digest("hex");
18698
18722
  const displayPath = import_node_path18.default.join(cacheDir, `${base}.${guessedExt}`);
18699
- await import_node_fs16.promises.rm(displayPath, { force: true }).catch(() => void 0);
18700
- await import_node_fs16.promises.copyFile(src, displayPath);
18723
+ await import_node_fs14.promises.rm(displayPath, { force: true }).catch(() => void 0);
18724
+ await import_node_fs14.promises.copyFile(src, displayPath);
18701
18725
  return displayPath;
18702
18726
  } finally {
18703
18727
  await fh.close();
@@ -18753,7 +18777,7 @@ function parseMessageText(raw) {
18753
18777
  }
18754
18778
 
18755
18779
  // src/commands/AgentCommandHandler.ts
18756
- var import_node_fs17 = require("fs");
18780
+ var import_node_fs15 = require("fs");
18757
18781
  var import_node_os8 = __toESM(require("os"));
18758
18782
  var import_node_path19 = __toESM(require("path"));
18759
18783
  var AgentCommandHandler = class {
@@ -18838,10 +18862,10 @@ var AgentCommandHandler = class {
18838
18862
  }
18839
18863
  readToken() {
18840
18864
  const sessionPath = import_node_path19.default.join(import_node_os8.default.homedir(), ".pmate", "session.json");
18841
- if (!(0, import_node_fs17.existsSync)(sessionPath)) {
18865
+ if (!(0, import_node_fs15.existsSync)(sessionPath)) {
18842
18866
  throw new Error(`Missing session file at ${sessionPath}. Run "pmate login" first.`);
18843
18867
  }
18844
- const raw = (0, import_node_fs17.readFileSync)(sessionPath, "utf8");
18868
+ const raw = (0, import_node_fs15.readFileSync)(sessionPath, "utf8");
18845
18869
  const parsed = JSON.parse(raw);
18846
18870
  const token = parsed.token?.trim();
18847
18871
  if (!token) {
@@ -18851,10 +18875,10 @@ var AgentCommandHandler = class {
18851
18875
  }
18852
18876
  readJsonFile(filePath) {
18853
18877
  const absolutePath = import_node_path19.default.resolve(process.cwd(), filePath);
18854
- if (!(0, import_node_fs17.existsSync)(absolutePath)) {
18878
+ if (!(0, import_node_fs15.existsSync)(absolutePath)) {
18855
18879
  throw new Error(`JSON file not found: ${absolutePath}`);
18856
18880
  }
18857
- const raw = (0, import_node_fs17.readFileSync)(absolutePath, "utf8");
18881
+ const raw = (0, import_node_fs15.readFileSync)(absolutePath, "utf8");
18858
18882
  try {
18859
18883
  return JSON.parse(raw);
18860
18884
  } catch (error) {
@@ -18923,7 +18947,7 @@ var AgentCommandHandler = class {
18923
18947
  };
18924
18948
 
18925
18949
  // src/commands/AppCommandHandler.ts
18926
- var import_node_fs18 = require("fs");
18950
+ var import_node_fs16 = require("fs");
18927
18951
  var import_node_os9 = __toESM(require("os"));
18928
18952
  var import_node_path20 = __toESM(require("path"));
18929
18953
  var AppCommandHandler = class {
@@ -18995,10 +19019,10 @@ var AppCommandHandler = class {
18995
19019
  }
18996
19020
  readToken() {
18997
19021
  const sessionPath = import_node_path20.default.join(import_node_os9.default.homedir(), ".pmate", "session.json");
18998
- if (!(0, import_node_fs18.existsSync)(sessionPath)) {
19022
+ if (!(0, import_node_fs16.existsSync)(sessionPath)) {
18999
19023
  throw new Error(`Missing session file at ${sessionPath}. Run "pmate login" first.`);
19000
19024
  }
19001
- const raw = (0, import_node_fs18.readFileSync)(sessionPath, "utf8");
19025
+ const raw = (0, import_node_fs16.readFileSync)(sessionPath, "utf8");
19002
19026
  const parsed = JSON.parse(raw);
19003
19027
  const token = parsed.token?.trim();
19004
19028
  if (!token) {
@@ -19008,10 +19032,10 @@ var AppCommandHandler = class {
19008
19032
  }
19009
19033
  readJsonFile(filePath) {
19010
19034
  const absolutePath = import_node_path20.default.resolve(process.cwd(), filePath);
19011
- if (!(0, import_node_fs18.existsSync)(absolutePath)) {
19035
+ if (!(0, import_node_fs16.existsSync)(absolutePath)) {
19012
19036
  throw new Error(`JSON file not found: ${absolutePath}`);
19013
19037
  }
19014
- const raw = (0, import_node_fs18.readFileSync)(absolutePath, "utf8");
19038
+ const raw = (0, import_node_fs16.readFileSync)(absolutePath, "utf8");
19015
19039
  try {
19016
19040
  return JSON.parse(raw);
19017
19041
  } catch (error) {
@@ -19428,7 +19452,7 @@ function requireValue2(tokens, optionName) {
19428
19452
 
19429
19453
  // src/index.ts
19430
19454
  var pkg = JSON.parse(
19431
- (0, import_node_fs19.readFileSync)(import_node_path21.default.resolve(__dirname, "..", "package.json"), "utf8")
19455
+ (0, import_node_fs17.readFileSync)(import_node_path21.default.resolve(__dirname, "..", "package.json"), "utf8")
19432
19456
  );
19433
19457
  var HELP_TEXT = [
19434
19458
  import_chalk3.default.bold(`pmate v${pkg.version}`),
@@ -19444,6 +19468,7 @@ var HELP_TEXT = [
19444
19468
  import_chalk3.default.bold("Commands:"),
19445
19469
  ` ${import_chalk3.default.cyan("list [path]")} List apps from .pmate.yaml in the target folder`,
19446
19470
  ` ${import_chalk3.default.cyan("login")} Log in and store a session in ~/.pmate/session.json`,
19471
+ ` ${import_chalk3.default.gray(" [--app <app-id>]")}`,
19447
19472
  ` ${import_chalk3.default.cyan("add-dns")} Add a DNS record via Aliyun DNS`,
19448
19473
  ` ${import_chalk3.default.gray(" --type <t> --value <v> --rr <rr> [--ttl <n>] [--domain <name>]")}`,
19449
19474
  ` ${import_chalk3.default.cyan("sts")} Request an OSS STS token from the CI server`,
@@ -19572,10 +19597,19 @@ function parseCommandArgs(args) {
19572
19597
  return { type: "list" /* List */, targetPath };
19573
19598
  }
19574
19599
  if (args.rest[0] === "login") {
19575
- if (args.rest.length > 1) {
19576
- throw new Error("Too many arguments for login command");
19600
+ const rest = args.rest.slice(1);
19601
+ let app;
19602
+ while (rest.length > 0) {
19603
+ const token = rest.shift();
19604
+ switch (token) {
19605
+ case "--app":
19606
+ app = requireValue3(rest, token);
19607
+ break;
19608
+ default:
19609
+ throw new Error(`Unknown login option: ${token}`);
19610
+ }
19577
19611
  }
19578
- return { type: "login" /* Login */ };
19612
+ return { type: "login" /* Login */, app };
19579
19613
  }
19580
19614
  if (args.rest[0] === "add-dns") {
19581
19615
  return parseAddDnsArgs(args.rest.slice(1));