downcity 1.1.89 → 1.1.90

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.
Files changed (50) hide show
  1. package/city/admin/commands/config.d.ts +1 -1
  2. package/city/admin/commands/config.js +1 -1
  3. package/city/admin/loop.d.ts +8 -2
  4. package/city/admin/loop.d.ts.map +1 -1
  5. package/city/admin/loop.js +17 -6
  6. package/city/admin/loop.js.map +1 -1
  7. package/city/app.d.ts +6 -5
  8. package/city/app.d.ts.map +1 -1
  9. package/city/app.js +51 -56
  10. package/city/app.js.map +1 -1
  11. package/city/auth/admin.js +1 -1
  12. package/city/auth/admin.js.map +1 -1
  13. package/city/auth/server-switch.d.ts +10 -11
  14. package/city/auth/server-switch.d.ts.map +1 -1
  15. package/city/auth/server-switch.js +77 -100
  16. package/city/auth/server-switch.js.map +1 -1
  17. package/city/core/ui.d.ts.map +1 -1
  18. package/city/core/ui.js +5 -4
  19. package/city/core/ui.js.map +1 -1
  20. package/city/deploy/runtime/CloudflareWorkersDeployer.js +1 -1
  21. package/city/deploy/runtime/CloudflareWorkersDeployer.js.map +1 -1
  22. package/city/home/HomeMenu.d.ts +17 -0
  23. package/city/home/HomeMenu.d.ts.map +1 -0
  24. package/city/home/HomeMenu.js +72 -0
  25. package/city/home/HomeMenu.js.map +1 -0
  26. package/city/types/Interactive.d.ts +25 -0
  27. package/city/types/Interactive.d.ts.map +1 -0
  28. package/city/types/Interactive.js +10 -0
  29. package/city/types/Interactive.js.map +1 -0
  30. package/city/user/loop.d.ts +3 -2
  31. package/city/user/loop.d.ts.map +1 -1
  32. package/city/user/loop.js +15 -8
  33. package/city/user/loop.js.map +1 -1
  34. package/city/workspace/ServerManagement.d.ts +13 -0
  35. package/city/workspace/ServerManagement.d.ts.map +1 -0
  36. package/city/workspace/ServerManagement.js +98 -0
  37. package/city/workspace/ServerManagement.js.map +1 -0
  38. package/city/workspace/ServerWorkspace.d.ts +12 -0
  39. package/city/workspace/ServerWorkspace.d.ts.map +1 -0
  40. package/city/workspace/ServerWorkspace.js +98 -0
  41. package/city/workspace/ServerWorkspace.js.map +1 -0
  42. package/package.json +1 -1
  43. package/town/agent/http/sdk/Router.d.ts +1 -1
  44. package/town/agent/http/sdk/Router.js +1 -1
  45. package/town/control-plane/ControlPlaneCommand.d.ts +1 -1
  46. package/town/control-plane/ControlPlaneCommand.js +1 -1
  47. package/city/auth/mode-select.d.ts +0 -11
  48. package/city/auth/mode-select.d.ts.map +0 -1
  49. package/city/auth/mode-select.js +0 -33
  50. package/city/auth/mode-select.js.map +0 -1
@@ -2,7 +2,7 @@
2
2
  * 旧的 Admin Server URL 命令兼容壳。
3
3
  *
4
4
  * 关键说明(中文)
5
- * - 新交互下,server 管理已经提升到顶层 Manage Servers
5
+ * - 新交互下,server 管理已经提升到当前 City 的 server management
6
6
  * - 这里保留空壳是为了避免历史导入直接报错
7
7
  */
8
8
  import { type AdminSession } from "../../core/session.js";
@@ -2,7 +2,7 @@
2
2
  * 旧的 Admin Server URL 命令兼容壳。
3
3
  *
4
4
  * 关键说明(中文)
5
- * - 新交互下,server 管理已经提升到顶层 Manage Servers
5
+ * - 新交互下,server 管理已经提升到当前 City 的 server management
6
6
  * - 这里保留空壳是为了避免历史导入直接报错
7
7
  */
8
8
  export async function changeServerUrl(_session) {
@@ -1,6 +1,12 @@
1
1
  /**
2
- * Admin 命令循环。返回 "logout" | "quit" | "switch_identity"。
2
+ * Admin 命令循环。
3
+ *
4
+ * 关键说明(中文)
5
+ * - embedded 模式用于 user 工作区下的 server management
6
+ * - 此时 admin 只作为低频管理工具,不再承担顶层导航职责
3
7
  */
4
8
  import { type AdminSession } from "../core/session.js";
5
- export declare function adminLoop(session: AdminSession): Promise<"logout" | "quit" | "switch_identity">;
9
+ export declare function adminLoop(session: AdminSession, options?: {
10
+ embedded?: boolean;
11
+ }): Promise<"logout" | "quit" | "switch_identity" | "back">;
6
12
  //# sourceMappingURL=loop.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/admin/loop.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAyBvD,wBAAsB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,GAAG,MAAM,GAAG,iBAAiB,CAAC,CAyCrG"}
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/admin/loop.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAyBvD,wBAAsB,SAAS,CAC7B,OAAO,EAAE,YAAY,EACrB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,OAAO,CAAC,QAAQ,GAAG,MAAM,GAAG,iBAAiB,GAAG,MAAM,CAAC,CA+CzD"}
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Admin 命令循环。返回 "logout" | "quit" | "switch_identity"。
2
+ * Admin 命令循环。
3
+ *
4
+ * 关键说明(中文)
5
+ * - embedded 模式用于 user 工作区下的 server management
6
+ * - 此时 admin 只作为低频管理工具,不再承担顶层导航职责
3
7
  */
4
8
  import { City } from "@downcity/city";
5
9
  import { select, isCancel } from "@clack/prompts";
@@ -25,15 +29,16 @@ const commands = {
25
29
  payment: managePayment,
26
30
  custom: manageCustom,
27
31
  };
28
- export async function adminLoop(session) {
32
+ export async function adminLoop(session, options) {
29
33
  const admin = new City({
30
34
  role: "admin",
31
35
  city_url: session.base_url,
32
36
  admin_secret_key: session.admin_secret_key,
33
37
  });
38
+ const embedded = options?.embedded === true;
34
39
  while (true) {
35
40
  const svc = await select({
36
- message: "Manage Service",
41
+ message: embedded ? "Server management" : "Manage Service",
37
42
  options: [
38
43
  { label: "Env", value: "env", hint: "View & configure environment variables" },
39
44
  { label: "City Instruction", value: "instruction", hint: "Read aggregated city/service guidance" },
@@ -44,15 +49,21 @@ export async function adminLoop(session) {
44
49
  { label: "Usage", value: "usage" },
45
50
  { label: "Payment (Stripe)", value: "payment" },
46
51
  { label: "Custom service...", value: "custom" },
47
- { label: "Switch to User", value: "switch" },
48
- { label: "Logout", value: "logout" },
52
+ ...(embedded
53
+ ? [{ label: "Back", value: "back" }]
54
+ : [
55
+ { label: "Switch to User", value: "switch" },
56
+ { label: "Logout", value: "logout" },
57
+ ]),
49
58
  { label: "Quit", value: "quit" },
50
59
  ],
51
60
  });
52
61
  if (!svc || isCancel(svc))
53
- return "quit";
62
+ return embedded ? "back" : "quit";
54
63
  if (svc === "quit")
55
64
  return "quit";
65
+ if (svc === "back")
66
+ return "back";
56
67
  if (svc === "logout") {
57
68
  showSuccess("left admin mode");
58
69
  return "logout";
@@ -1 +1 @@
1
- {"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/admin/loop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/admin/loop.ts"],"names":[],"mappings":"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"}
package/city/app.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Downcity City 管理入口与状态机调度。
3
+ * Downcity City 交互入口与工作区调度。
4
4
  *
5
5
  * 状态流转:
6
- * selectIdentityAdminadminLoopselectIdentity (switch identity)
7
- * selectIdentity → User → userLoop → selectIdentity (switch identity)
8
- * selectIdentity → Manage Servers → selectIdentity
6
+ * welcome/homeconnect/switch City server workspace server management/admin tools
9
7
  *
10
- * 不再持有 config 快照,每次需要时从磁盘 readConfig()。
8
+ * 关键说明(中文)
9
+ * - 顶层不再要求先选择 admin / user 身份
10
+ * - connect City 后默认进入 user sign in / user 工作区
11
+ * - admin 能力只作为低频的 server management 入口出现
11
12
  */
12
13
  export declare function runCityApp(argv?: string[]): Promise<void>;
13
14
  //# sourceMappingURL=app.d.ts.map
package/city/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":";AAEA;;;;;;;;;GASG;AAeH,wBAAsB,UAAU,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DnE"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG;AAYH,wBAAsB,UAAU,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEnE"}
package/city/app.js CHANGED
@@ -1,26 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Downcity City 管理入口与状态机调度。
3
+ * Downcity City 交互入口与工作区调度。
4
4
  *
5
5
  * 状态流转:
6
- * selectIdentityAdminadminLoopselectIdentity (switch identity)
7
- * selectIdentity → User → userLoop → selectIdentity (switch identity)
8
- * selectIdentity → Manage Servers → selectIdentity
6
+ * welcome/homeconnect/switch City server workspace server management/admin tools
9
7
  *
10
- * 不再持有 config 快照,每次需要时从磁盘 readConfig()。
8
+ * 关键说明(中文)
9
+ * - 顶层不再要求先选择 admin / user 身份
10
+ * - connect City 后默认进入 user sign in / user 工作区
11
+ * - admin 能力只作为低频的 server management 入口出现
11
12
  */
12
13
  import { readFileSync } from "node:fs";
13
14
  import { intro } from "./core/ui.js";
14
- import { readActiveServer, readConfig, writeConfig } from "./core/session.js";
15
+ import { readActiveServer } from "./core/session.js";
15
16
  import { parseArgs } from "./core/env.js";
16
- import { selectIdentity } from "./auth/mode-select.js";
17
- import { adminAuth } from "./auth/admin.js";
18
- import { userAuth } from "./auth/user.js";
19
- import { ensureServerConfigured, manageServersMenu } from "./auth/server-switch.js";
20
- import { userLoop } from "./user/loop.js";
21
- import { adminLoop } from "./admin/loop.js";
17
+ import { promptAddServer, promptSelectActiveServer } from "./auth/server-switch.js";
22
18
  import { show, showError, showSuccess } from "./core/ui.js";
23
19
  import { updateCli } from "./core/update.js";
20
+ import { selectHomeAction, selectWelcomeAction } from "./home/HomeMenu.js";
21
+ import { openServerWorkspace } from "./workspace/ServerWorkspace.js";
24
22
  export async function runCityApp(argv = []) {
25
23
  const cli = parseArgs(argv);
26
24
  if (cli.command === "update") {
@@ -28,64 +26,61 @@ export async function runCityApp(argv = []) {
28
26
  return;
29
27
  }
30
28
  intro(`Downcity City v${readCliVersion()} (Esc to go back, Ctrl+C to exit)`);
31
- if (!(await ensureServerConfigured())) {
32
- return;
33
- }
34
- let identity = await selectIdentity();
35
- while (identity !== "quit") {
36
- if (identity === "update") {
37
- await runSelfUpdate();
38
- return;
39
- }
40
- if (identity === "servers") {
41
- await manageServersMenu();
42
- if (!(await ensureServerConfigured())) {
43
- return;
44
- }
45
- identity = await selectIdentity();
46
- continue;
47
- }
48
- // 每次从磁盘读取最新 config,不持快照
49
- const cfg = readConfig();
29
+ while (true) {
50
30
  const activeServer = readActiveServer();
51
31
  if (!activeServer) {
52
- if (!(await ensureServerConfigured())) {
32
+ const welcomeAction = await selectWelcomeAction();
33
+ if (welcomeAction === "quit") {
34
+ return;
35
+ }
36
+ if (welcomeAction === "update") {
37
+ await runSelfUpdate();
38
+ return;
39
+ }
40
+ const connectedServer = await promptAddServer();
41
+ if (!connectedServer) {
42
+ continue;
43
+ }
44
+ const result = await openServerWorkspace(connectedServer.base_url);
45
+ if (result === "quit") {
53
46
  return;
54
47
  }
55
- identity = await selectIdentity();
56
48
  continue;
57
49
  }
58
- writeConfig({ ...cfg, last_identity: identity });
59
- if (identity === "admin") {
60
- const session = await adminAuth(activeServer);
61
- if (!session) {
62
- identity = await selectIdentity();
50
+ const homeAction = await selectHomeAction();
51
+ if (homeAction === "quit") {
52
+ return;
53
+ }
54
+ if (homeAction === "update") {
55
+ await runSelfUpdate();
56
+ return;
57
+ }
58
+ if (homeAction === "connect_city") {
59
+ const connectedServer = await promptAddServer();
60
+ if (!connectedServer) {
63
61
  continue;
64
62
  }
65
- const result = await adminLoop(session);
66
- if (result === "quit")
67
- break;
68
- if (result === "switch_identity") {
69
- identity = await selectIdentity();
70
- continue;
63
+ const result = await openServerWorkspace(connectedServer.base_url);
64
+ if (result === "quit") {
65
+ return;
71
66
  }
72
- identity = await selectIdentity();
73
67
  continue;
74
68
  }
75
- // identity === "user"
76
- const ctx = await userAuth(activeServer.base_url);
77
- if (!ctx) {
78
- identity = await selectIdentity();
69
+ if (homeAction === "switch_city") {
70
+ const selectedServer = await promptSelectActiveServer();
71
+ if (!selectedServer) {
72
+ continue;
73
+ }
74
+ const result = await openServerWorkspace(selectedServer.base_url);
75
+ if (result === "quit") {
76
+ return;
77
+ }
79
78
  continue;
80
79
  }
81
- const result = await userLoop(ctx);
82
- if (result === "quit")
83
- break;
84
- if (result === "switch_identity") {
85
- identity = await selectIdentity();
86
- continue;
80
+ const result = await openServerWorkspace(activeServer.base_url);
81
+ if (result === "quit") {
82
+ return;
87
83
  }
88
- identity = await selectIdentity();
89
84
  }
90
85
  }
91
86
  /**
package/city/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,7 @@ import { showError } from "../core/ui.js";
7
7
  export async function adminAuth(server) {
8
8
  const adminSecretKey = String(server.admin_secret_key ?? "").trim();
9
9
  if (!adminSecretKey) {
10
- showError("Current server is missing admin_secret_key. Use Manage Servers -> Edit Server.");
10
+ showError("Current City is missing admin_secret_key. Open Server management -> Configure admin access.");
11
11
  return undefined;
12
12
  }
13
13
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAqB;IACnD,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpE,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,SAAS,CAAC,gFAAgF,CAAC,CAAC;QAC5F,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,gBAAgB,EAAE,cAAc;KACjC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/auth/admin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAqB;IACnD,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpE,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,SAAS,CAAC,6FAA6F,CAAC,CAAC;QACzG,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,gBAAgB,EAAE,cAAc;KACjC,CAAC;AACJ,CAAC"}
@@ -2,21 +2,20 @@
2
2
  * Server 管理模块。
3
3
  *
4
4
  * 关键说明(中文)
5
- * - 不再提供“切换当前 server”的临时入口
6
- * - 所有 server 操作都统一收口到 Manage Servers
7
- * - 没有 server 时,CLI 会先强制进入添加流程
5
+ * - connect City 不再强制要求 admin_secret_key
6
+ * - admin access 只在低频管理场景中单独配置
7
+ * - server 仍然作为本地连接记录持久化保存
8
8
  */
9
9
  import { type ServerProfile } from "../core/session.js";
10
- /**
11
- * 确保至少存在一个 server。
12
- */
13
- export declare function ensureServerConfigured(): Promise<boolean>;
14
- /**
15
- * 顶层 server 管理菜单。
16
- */
17
- export declare function manageServersMenu(): Promise<void>;
18
10
  /**
19
11
  * 添加 server。
20
12
  */
21
13
  export declare function promptAddServer(): Promise<ServerProfile | undefined>;
14
+ export declare function promptSelectActiveServer(): Promise<ServerProfile | undefined>;
15
+ export declare function promptEditServer(baseUrl?: string): Promise<ServerProfile | undefined>;
16
+ /**
17
+ * 为当前 server 单独配置 admin access。
18
+ */
19
+ export declare function promptConfigureAdminAccess(baseUrl: string): Promise<ServerProfile | undefined>;
20
+ export declare function promptRemoveServer(baseUrl?: string): Promise<boolean>;
22
21
  //# sourceMappingURL=server-switch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-switch.d.ts","sourceRoot":"","sources":["../../src/auth/server-switch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAOL,KAAK,aAAa,EAEnB,MAAM,oBAAoB,CAAC;AAG5B;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,OAAO,CAAC,CAY/D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA+CvD;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CA2B1E"}
1
+ {"version":3,"file":"server-switch.d.ts","sourceRoot":"","sources":["../../src/auth/server-switch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAOL,KAAK,aAAa,EAEnB,MAAM,oBAAoB,CAAC;AAG5B;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAqB1E;AAED,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAwBnF;AAED,wBAAsB,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAwE3F;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAyBpC;AAED,wBAAsB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA8B3E"}
@@ -2,73 +2,14 @@
2
2
  * Server 管理模块。
3
3
  *
4
4
  * 关键说明(中文)
5
- * - 不再提供“切换当前 server”的临时入口
6
- * - 所有 server 操作都统一收口到 Manage Servers
7
- * - 没有 server 时,CLI 会先强制进入添加流程
5
+ * - connect City 不再强制要求 admin_secret_key
6
+ * - admin access 只在低频管理场景中单独配置
7
+ * - server 仍然作为本地连接记录持久化保存
8
8
  */
9
9
  import { City } from "@downcity/city";
10
10
  import { isCancel, password, select, text } from "@clack/prompts";
11
11
  import { addServer, readActiveServer, readConfig, readServer, removeServer, setActiveServer, updateServer, } from "../core/session.js";
12
- import { show, showError, showSuccess } from "../core/ui.js";
13
- /**
14
- * 确保至少存在一个 server。
15
- */
16
- export async function ensureServerConfigured() {
17
- if (readConfig().servers.length > 0) {
18
- return true;
19
- }
20
- show("No servers configured. Add a server before continuing.");
21
- const created = await promptAddServer();
22
- if (!created) {
23
- showError("No server configured. Exiting.");
24
- return false;
25
- }
26
- return true;
27
- }
28
- /**
29
- * 顶层 server 管理菜单。
30
- */
31
- export async function manageServersMenu() {
32
- while (true) {
33
- const config = readConfig();
34
- const active = readActiveServer();
35
- const selected = await select({
36
- message: active
37
- ? `Manage Servers [current: ${active.name}]`
38
- : "Manage Servers [no active server]",
39
- options: [
40
- { label: "List Servers", value: "list", hint: `${config.servers.length} configured` },
41
- { label: "Add Server", value: "add", hint: "Create a new Infra server profile" },
42
- { label: "Select Active Server", value: "select", hint: active ? active.base_url : "Choose current server" },
43
- { label: "Edit Server", value: "edit", hint: "Update name, server URL, or admin key" },
44
- { label: "Remove Server", value: "remove", hint: "Delete a server profile" },
45
- { label: "Back", value: "back", hint: "Return to main menu" },
46
- ],
47
- });
48
- if (!selected || isCancel(selected) || selected === "back") {
49
- return;
50
- }
51
- if (selected === "list") {
52
- printServers(config.servers, active?.base_url);
53
- continue;
54
- }
55
- if (selected === "add") {
56
- await promptAddServer();
57
- continue;
58
- }
59
- if (selected === "select") {
60
- await promptSelectActiveServer();
61
- continue;
62
- }
63
- if (selected === "edit") {
64
- await promptEditServer();
65
- continue;
66
- }
67
- if (selected === "remove") {
68
- await promptRemoveServer();
69
- }
70
- }
71
- }
12
+ import { showError, showSuccess } from "../core/ui.js";
72
13
  /**
73
14
  * 添加 server。
74
15
  */
@@ -80,28 +21,23 @@ export async function promptAddServer() {
80
21
  if (!baseUrl || isCancel(baseUrl) || !String(baseUrl).trim()) {
81
22
  return undefined;
82
23
  }
83
- const adminSecretKey = await password({ message: "admin_secret_key" });
84
- if (!adminSecretKey || isCancel(adminSecretKey) || !String(adminSecretKey).trim()) {
85
- return undefined;
86
- }
87
24
  const server = addServer({
88
25
  base_url: String(baseUrl).trim(),
89
- admin_secret_key: String(adminSecretKey).trim(),
90
26
  });
91
- const verified = await verifyServerAdminAccess(server);
27
+ const verified = await verifyServerPublicAccess(server);
92
28
  if (verified) {
93
- showSuccess(`Server added and activated: ${server.name}`);
29
+ showSuccess(`City connected: ${server.name}`);
94
30
  }
95
31
  else {
96
- showError(`Server saved, but admin verification failed: ${server.name}`);
32
+ showError(`City saved, but the public reachability check failed: ${server.name}`);
97
33
  }
98
34
  return server;
99
35
  }
100
- async function promptSelectActiveServer() {
36
+ export async function promptSelectActiveServer() {
101
37
  const config = readConfig();
102
38
  if (config.servers.length === 0) {
103
39
  showError("No servers configured.");
104
- return;
40
+ return undefined;
105
41
  }
106
42
  const selected = await select({
107
43
  message: "Select active server",
@@ -112,18 +48,20 @@ async function promptSelectActiveServer() {
112
48
  })),
113
49
  });
114
50
  if (!selected || isCancel(selected)) {
115
- return;
51
+ return undefined;
116
52
  }
117
- setActiveServer(String(selected));
118
- showSuccess(`Active server: ${String(selected)}`);
53
+ const selected_base_url = String(selected);
54
+ setActiveServer(selected_base_url);
55
+ showSuccess(`Current City: ${selected_base_url}`);
56
+ return readServer(selected_base_url);
119
57
  }
120
- async function promptEditServer() {
58
+ export async function promptEditServer(baseUrl) {
121
59
  const config = readConfig();
122
60
  if (config.servers.length === 0) {
123
61
  showError("No servers configured.");
124
- return;
62
+ return undefined;
125
63
  }
126
- const targetBaseUrl = await select({
64
+ const targetBaseUrl = baseUrl ?? await select({
127
65
  message: "Edit server",
128
66
  options: config.servers.map((server) => ({
129
67
  label: server.base_url === config.active_server_url ? `★ ${server.name}` : ` ${server.name}`,
@@ -132,12 +70,12 @@ async function promptEditServer() {
132
70
  })),
133
71
  });
134
72
  if (!targetBaseUrl || isCancel(targetBaseUrl)) {
135
- return;
73
+ return undefined;
136
74
  }
137
75
  const current = readServer(String(targetBaseUrl));
138
76
  if (!current) {
139
77
  showError("Selected server no longer exists.");
140
- return;
78
+ return undefined;
141
79
  }
142
80
  const field = await select({
143
81
  message: `Edit ${current.name}`,
@@ -149,43 +87,80 @@ async function promptEditServer() {
149
87
  ],
150
88
  });
151
89
  if (!field || isCancel(field) || field === "cancel") {
152
- return;
90
+ return undefined;
153
91
  }
154
92
  const next = { ...current };
155
93
  if (field === "name") {
156
94
  const name = await text({ message: "Display name", initialValue: current.name });
157
95
  if (!name || isCancel(name))
158
- return;
96
+ return undefined;
159
97
  next.name = String(name).trim() || current.name;
160
98
  }
161
99
  else if (field === "base_url") {
162
100
  const baseUrl = await text({ message: "Server URL", initialValue: current.base_url });
163
101
  if (!baseUrl || isCancel(baseUrl))
164
- return;
102
+ return undefined;
165
103
  next.base_url = String(baseUrl).trim() || current.base_url;
166
104
  }
167
105
  else if (field === "admin_secret_key") {
168
106
  const adminSecretKey = await password({ message: "admin_secret_key" });
169
107
  if (!adminSecretKey || isCancel(adminSecretKey))
170
- return;
108
+ return undefined;
171
109
  next.admin_secret_key = String(adminSecretKey).trim();
172
110
  }
173
111
  const updated = updateServer(current.base_url, next);
112
+ if (field === "admin_secret_key") {
113
+ const verified = await verifyServerAdminAccess(updated);
114
+ if (verified) {
115
+ showSuccess(`Admin access updated: ${updated.name}`);
116
+ }
117
+ else {
118
+ showError(`City saved, but admin verification failed: ${updated.name}`);
119
+ }
120
+ return updated;
121
+ }
122
+ const verified = await verifyServerPublicAccess(updated);
123
+ if (verified) {
124
+ showSuccess(`City updated: ${updated.name}`);
125
+ }
126
+ else {
127
+ showError(`City saved, but the public reachability check failed: ${updated.name}`);
128
+ }
129
+ return updated;
130
+ }
131
+ /**
132
+ * 为当前 server 单独配置 admin access。
133
+ */
134
+ export async function promptConfigureAdminAccess(baseUrl) {
135
+ const current = readServer(baseUrl);
136
+ if (!current) {
137
+ showError("Selected server no longer exists.");
138
+ return undefined;
139
+ }
140
+ const adminSecretKey = await password({ message: "admin_secret_key" });
141
+ if (!adminSecretKey || isCancel(adminSecretKey) || !String(adminSecretKey).trim()) {
142
+ return undefined;
143
+ }
144
+ const updated = updateServer(current.base_url, {
145
+ ...current,
146
+ admin_secret_key: String(adminSecretKey).trim(),
147
+ });
174
148
  const verified = await verifyServerAdminAccess(updated);
175
149
  if (verified) {
176
- showSuccess(`Server updated: ${updated.name}`);
150
+ showSuccess(`Admin access configured: ${updated.name}`);
177
151
  }
178
152
  else {
179
- showError(`Server updated, but admin verification failed: ${updated.name}`);
153
+ showError(`City saved, but admin verification failed: ${updated.name}`);
180
154
  }
155
+ return updated;
181
156
  }
182
- async function promptRemoveServer() {
157
+ export async function promptRemoveServer(baseUrl) {
183
158
  const config = readConfig();
184
159
  if (config.servers.length === 0) {
185
160
  showError("No servers configured.");
186
- return;
161
+ return false;
187
162
  }
188
- const selected = await select({
163
+ const selected = baseUrl ?? await select({
189
164
  message: "Remove server",
190
165
  options: [
191
166
  ...config.servers.map((server) => ({
@@ -197,13 +172,14 @@ async function promptRemoveServer() {
197
172
  ],
198
173
  });
199
174
  if (!selected || isCancel(selected) || selected === "cancel") {
200
- return;
175
+ return false;
201
176
  }
202
177
  removeServer(String(selected));
203
178
  const nextActive = readActiveServer();
204
179
  showSuccess(nextActive
205
180
  ? `Server removed. Current server: ${nextActive.name}`
206
181
  : "Server removed. No servers configured.");
182
+ return true;
207
183
  }
208
184
  async function verifyServerAdminAccess(server) {
209
185
  try {
@@ -219,17 +195,18 @@ async function verifyServerAdminAccess(server) {
219
195
  return false;
220
196
  }
221
197
  }
222
- function printServers(servers, activeBaseUrl) {
223
- if (servers.length === 0) {
224
- show("No servers configured.");
225
- return;
198
+ async function verifyServerPublicAccess(server) {
199
+ try {
200
+ const user = new City({
201
+ role: "user",
202
+ city_url: server.base_url,
203
+ });
204
+ await user.service("accounts").get("providers");
205
+ return true;
226
206
  }
227
- console.log(`\n${servers.length} servers:\n`);
228
- for (const server of servers) {
229
- const marker = server.base_url === activeBaseUrl ? "★" : " ";
230
- console.log(` ${marker} ${server.name.padEnd(24)} ${server.base_url} admin=${maskSecret(server.admin_secret_key)}`);
207
+ catch {
208
+ return false;
231
209
  }
232
- console.log("");
233
210
  }
234
211
  function maskSecret(value) {
235
212
  const normalized = String(value ?? "").trim();