@polardbx/pxbaas 0.0.0-stage → 0.1.0-beta.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.
Files changed (264) hide show
  1. package/bin/pxbaas.js +13 -0
  2. package/dist/app/preflight.d.ts +38 -0
  3. package/dist/app/preflight.d.ts.map +1 -0
  4. package/dist/app/preflight.js +79 -0
  5. package/dist/app/preflight.js.map +1 -0
  6. package/dist/app/spec-lint.d.ts +45 -0
  7. package/dist/app/spec-lint.d.ts.map +1 -0
  8. package/dist/app/spec-lint.js +407 -0
  9. package/dist/app/spec-lint.js.map +1 -0
  10. package/dist/app/templates.d.ts +143 -0
  11. package/dist/app/templates.d.ts.map +1 -0
  12. package/dist/app/templates.js +151 -0
  13. package/dist/app/templates.js.map +1 -0
  14. package/dist/cli/root.d.ts +3 -0
  15. package/dist/cli/root.d.ts.map +1 -0
  16. package/dist/cli/root.js +69 -0
  17. package/dist/cli/root.js.map +1 -0
  18. package/dist/commands/app/index.d.ts +8 -0
  19. package/dist/commands/app/index.d.ts.map +1 -0
  20. package/dist/commands/app/index.js +1267 -0
  21. package/dist/commands/app/index.js.map +1 -0
  22. package/dist/commands/auth/index.d.ts +3 -0
  23. package/dist/commands/auth/index.d.ts.map +1 -0
  24. package/dist/commands/auth/index.js +198 -0
  25. package/dist/commands/auth/index.js.map +1 -0
  26. package/dist/commands/completion/index.d.ts +3 -0
  27. package/dist/commands/completion/index.d.ts.map +1 -0
  28. package/dist/commands/completion/index.js +10 -0
  29. package/dist/commands/completion/index.js.map +1 -0
  30. package/dist/commands/data/index.d.ts +3 -0
  31. package/dist/commands/data/index.d.ts.map +1 -0
  32. package/dist/commands/data/index.js +245 -0
  33. package/dist/commands/data/index.js.map +1 -0
  34. package/dist/commands/db/index.d.ts +14 -0
  35. package/dist/commands/db/index.d.ts.map +1 -0
  36. package/dist/commands/db/index.js +355 -0
  37. package/dist/commands/db/index.js.map +1 -0
  38. package/dist/commands/doctor/index.d.ts +3 -0
  39. package/dist/commands/doctor/index.d.ts.map +1 -0
  40. package/dist/commands/doctor/index.js +305 -0
  41. package/dist/commands/doctor/index.js.map +1 -0
  42. package/dist/commands/functions/delete.d.ts +3 -0
  43. package/dist/commands/functions/delete.d.ts.map +1 -0
  44. package/dist/commands/functions/delete.js +55 -0
  45. package/dist/commands/functions/delete.js.map +1 -0
  46. package/dist/commands/functions/deploy.d.ts +3 -0
  47. package/dist/commands/functions/deploy.d.ts.map +1 -0
  48. package/dist/commands/functions/deploy.js +118 -0
  49. package/dist/commands/functions/deploy.js.map +1 -0
  50. package/dist/commands/functions/index.d.ts +3 -0
  51. package/dist/commands/functions/index.d.ts.map +1 -0
  52. package/dist/commands/functions/index.js +15 -0
  53. package/dist/commands/functions/index.js.map +1 -0
  54. package/dist/commands/functions/list.d.ts +6 -0
  55. package/dist/commands/functions/list.d.ts.map +1 -0
  56. package/dist/commands/functions/list.js +63 -0
  57. package/dist/commands/functions/list.js.map +1 -0
  58. package/dist/commands/functions/new.d.ts +3 -0
  59. package/dist/commands/functions/new.d.ts.map +1 -0
  60. package/dist/commands/functions/new.js +57 -0
  61. package/dist/commands/functions/new.js.map +1 -0
  62. package/dist/commands/gen/index.d.ts +3 -0
  63. package/dist/commands/gen/index.d.ts.map +1 -0
  64. package/dist/commands/gen/index.js +320 -0
  65. package/dist/commands/gen/index.js.map +1 -0
  66. package/dist/commands/instance/index.d.ts +8 -0
  67. package/dist/commands/instance/index.d.ts.map +1 -0
  68. package/dist/commands/instance/index.js +361 -0
  69. package/dist/commands/instance/index.js.map +1 -0
  70. package/dist/commands/login/index.d.ts +3 -0
  71. package/dist/commands/login/index.d.ts.map +1 -0
  72. package/dist/commands/login/index.js +185 -0
  73. package/dist/commands/login/index.js.map +1 -0
  74. package/dist/commands/login/login.d.ts +3 -0
  75. package/dist/commands/login/login.d.ts.map +1 -0
  76. package/dist/commands/login/login.js +185 -0
  77. package/dist/commands/login/login.js.map +1 -0
  78. package/dist/commands/logout/index.d.ts +3 -0
  79. package/dist/commands/logout/index.d.ts.map +1 -0
  80. package/dist/commands/logout/index.js +41 -0
  81. package/dist/commands/logout/index.js.map +1 -0
  82. package/dist/commands/profile/import-env.d.ts +13 -0
  83. package/dist/commands/profile/import-env.d.ts.map +1 -0
  84. package/dist/commands/profile/import-env.js +81 -0
  85. package/dist/commands/profile/import-env.js.map +1 -0
  86. package/dist/commands/profile/index.d.ts +3 -0
  87. package/dist/commands/profile/index.d.ts.map +1 -0
  88. package/dist/commands/profile/index.js +109 -0
  89. package/dist/commands/profile/index.js.map +1 -0
  90. package/dist/commands/rest/index.d.ts +3 -0
  91. package/dist/commands/rest/index.d.ts.map +1 -0
  92. package/dist/commands/rest/index.js +102 -0
  93. package/dist/commands/rest/index.js.map +1 -0
  94. package/dist/commands/skill/index.d.ts +11 -0
  95. package/dist/commands/skill/index.d.ts.map +1 -0
  96. package/dist/commands/skill/index.js +153 -0
  97. package/dist/commands/skill/index.js.map +1 -0
  98. package/dist/commands/status/index.d.ts +3 -0
  99. package/dist/commands/status/index.d.ts.map +1 -0
  100. package/dist/commands/status/index.js +59 -0
  101. package/dist/commands/status/index.js.map +1 -0
  102. package/dist/commands/storage/index.d.ts +3 -0
  103. package/dist/commands/storage/index.d.ts.map +1 -0
  104. package/dist/commands/storage/index.js +198 -0
  105. package/dist/commands/storage/index.js.map +1 -0
  106. package/dist/commands/whoami/index.d.ts +3 -0
  107. package/dist/commands/whoami/index.d.ts.map +1 -0
  108. package/dist/commands/whoami/index.js +82 -0
  109. package/dist/commands/whoami/index.js.map +1 -0
  110. package/dist/credentials/commit.d.ts +38 -0
  111. package/dist/credentials/commit.d.ts.map +1 -0
  112. package/dist/credentials/commit.js +57 -0
  113. package/dist/credentials/commit.js.map +1 -0
  114. package/dist/credentials/credential-store.d.ts +57 -0
  115. package/dist/credentials/credential-store.d.ts.map +1 -0
  116. package/dist/credentials/credential-store.js +180 -0
  117. package/dist/credentials/credential-store.js.map +1 -0
  118. package/dist/credentials/env-file.d.ts +44 -0
  119. package/dist/credentials/env-file.d.ts.map +1 -0
  120. package/dist/credentials/env-file.js +118 -0
  121. package/dist/credentials/env-file.js.map +1 -0
  122. package/dist/credentials/formatter.d.ts +39 -0
  123. package/dist/credentials/formatter.d.ts.map +1 -0
  124. package/dist/credentials/formatter.js +79 -0
  125. package/dist/credentials/formatter.js.map +1 -0
  126. package/dist/credentials/index.d.ts +11 -0
  127. package/dist/credentials/index.d.ts.map +1 -0
  128. package/dist/credentials/index.js +11 -0
  129. package/dist/credentials/index.js.map +1 -0
  130. package/dist/credentials/jwt-introspect.d.ts +32 -0
  131. package/dist/credentials/jwt-introspect.d.ts.map +1 -0
  132. package/dist/credentials/jwt-introspect.js +64 -0
  133. package/dist/credentials/jwt-introspect.js.map +1 -0
  134. package/dist/credentials/masker.d.ts +20 -0
  135. package/dist/credentials/masker.d.ts.map +1 -0
  136. package/dist/credentials/masker.js +37 -0
  137. package/dist/credentials/masker.js.map +1 -0
  138. package/dist/credentials/profile.d.ts +83 -0
  139. package/dist/credentials/profile.d.ts.map +1 -0
  140. package/dist/credentials/profile.js +184 -0
  141. package/dist/credentials/profile.js.map +1 -0
  142. package/dist/credentials/tenant-guard.d.ts +16 -0
  143. package/dist/credentials/tenant-guard.d.ts.map +1 -0
  144. package/dist/credentials/tenant-guard.js +53 -0
  145. package/dist/credentials/tenant-guard.js.map +1 -0
  146. package/dist/credentials/tty-guard.d.ts +19 -0
  147. package/dist/credentials/tty-guard.d.ts.map +1 -0
  148. package/dist/credentials/tty-guard.js +47 -0
  149. package/dist/credentials/tty-guard.js.map +1 -0
  150. package/dist/db/apply-schema.d.ts +57 -0
  151. package/dist/db/apply-schema.d.ts.map +1 -0
  152. package/dist/db/apply-schema.js +192 -0
  153. package/dist/db/apply-schema.js.map +1 -0
  154. package/dist/db/sql-file.d.ts +49 -0
  155. package/dist/db/sql-file.d.ts.map +1 -0
  156. package/dist/db/sql-file.js +167 -0
  157. package/dist/db/sql-file.js.map +1 -0
  158. package/dist/instance/provision.d.ts +89 -0
  159. package/dist/instance/provision.d.ts.map +1 -0
  160. package/dist/instance/provision.js +139 -0
  161. package/dist/instance/provision.js.map +1 -0
  162. package/dist/instance/zero-store.d.ts +47 -0
  163. package/dist/instance/zero-store.d.ts.map +1 -0
  164. package/dist/instance/zero-store.js +91 -0
  165. package/dist/instance/zero-store.js.map +1 -0
  166. package/dist/main.d.ts +3 -0
  167. package/dist/main.d.ts.map +1 -0
  168. package/dist/main.js +46 -0
  169. package/dist/main.js.map +1 -0
  170. package/dist/skill/assets.d.ts +38 -0
  171. package/dist/skill/assets.d.ts.map +1 -0
  172. package/dist/skill/assets.js +111 -0
  173. package/dist/skill/assets.js.map +1 -0
  174. package/dist/skill/hint.d.ts +39 -0
  175. package/dist/skill/hint.d.ts.map +1 -0
  176. package/dist/skill/hint.js +109 -0
  177. package/dist/skill/hint.js.map +1 -0
  178. package/dist/skill/hosts.d.ts +21 -0
  179. package/dist/skill/hosts.d.ts.map +1 -0
  180. package/dist/skill/hosts.js +38 -0
  181. package/dist/skill/hosts.js.map +1 -0
  182. package/dist/skill/installer.d.ts +88 -0
  183. package/dist/skill/installer.d.ts.map +1 -0
  184. package/dist/skill/installer.js +203 -0
  185. package/dist/skill/installer.js.map +1 -0
  186. package/dist/skill/ownership.d.ts +35 -0
  187. package/dist/skill/ownership.d.ts.map +1 -0
  188. package/dist/skill/ownership.js +52 -0
  189. package/dist/skill/ownership.js.map +1 -0
  190. package/package.json +63 -3
  191. package/skills/pxbaas-app-spec/SKILL.md +163 -0
  192. package/skills/pxbaas-app-spec/references/deploy-options.md +157 -0
  193. package/skills/pxbaas-app-spec/references/guardrails-pitfalls.md +89 -0
  194. package/skills/pxbaas-app-spec/references/runtime-contract.md +151 -0
  195. package/skills/pxbaas-app-spec/references/ssr-session.md +121 -0
  196. package/skills/pxbaas-app-spec/references/templates-examples.md +308 -0
  197. package/skills/pxbaas-cli/SKILL.md +168 -0
  198. package/skills/pxbaas-cli/references/api-reference.md +68 -0
  199. package/skills/pxbaas-cli/references/app-deploy.md +143 -0
  200. package/skills/pxbaas-cli/references/auth-and-credentials.md +88 -0
  201. package/skills/pxbaas-cli/references/data-postgrest.md +85 -0
  202. package/skills/pxbaas-cli/references/db-and-meta.md +85 -0
  203. package/skills/pxbaas-cli/references/edge-functions.md +71 -0
  204. package/skills/pxbaas-cli/references/multi-tenant-apps.md +107 -0
  205. package/skills/pxbaas-cli/references/provision.md +86 -0
  206. package/skills/pxbaas-cli/references/safety-and-two-phase.md +49 -0
  207. package/skills/pxbaas-cli/references/storage.md +61 -0
  208. package/skills/pxbaas-cli/references/troubleshooting.md +73 -0
  209. package/templates/webapp/api/index.ts +55 -0
  210. package/templates/webapp/csr-bff/README.md +81 -0
  211. package/templates/webapp/csr-bff/backend/index.ts +168 -0
  212. package/templates/webapp/csr-bff/db/schema.sql +69 -0
  213. package/templates/webapp/csr-bff/frontend/dist/admin.html +101 -0
  214. package/templates/webapp/csr-bff/frontend/dist/article.html +44 -0
  215. package/templates/webapp/csr-bff/frontend/dist/blog.js +206 -0
  216. package/templates/webapp/csr-bff/frontend/dist/blogger.html +53 -0
  217. package/templates/webapp/csr-bff/frontend/dist/index.html +70 -0
  218. package/templates/webapp/csr-bff/frontend/dist/login.html +56 -0
  219. package/templates/webapp/csr-bff/frontend/dist/profile.html +59 -0
  220. package/templates/webapp/csr-bff/frontend/dist/signup.html +78 -0
  221. package/templates/webapp/csr-bff/frontend/dist/style.css +27 -0
  222. package/templates/webapp/manifest.json +296 -0
  223. package/templates/webapp/mpa/about.html +29 -0
  224. package/templates/webapp/mpa/app.js +42 -0
  225. package/templates/webapp/mpa/index.html +42 -0
  226. package/templates/webapp/mpa/index.ts +60 -0
  227. package/templates/webapp/mpa/style.css +42 -0
  228. package/templates/webapp/react-vite/frontend/index.html +12 -0
  229. package/templates/webapp/react-vite/frontend/package-lock.json +1826 -0
  230. package/templates/webapp/react-vite/frontend/package.json +23 -0
  231. package/templates/webapp/react-vite/frontend/src/App.tsx +34 -0
  232. package/templates/webapp/react-vite/frontend/src/main.tsx +13 -0
  233. package/templates/webapp/react-vite/frontend/src/style.css +46 -0
  234. package/templates/webapp/react-vite/frontend/tsconfig.json +20 -0
  235. package/templates/webapp/react-vite/frontend/vite.config.ts +10 -0
  236. package/templates/webapp/react-vite-hono/backend/index.ts +21 -0
  237. package/templates/webapp/react-vite-hono/frontend/index.html +12 -0
  238. package/templates/webapp/react-vite-hono/frontend/package-lock.json +1826 -0
  239. package/templates/webapp/react-vite-hono/frontend/package.json +23 -0
  240. package/templates/webapp/react-vite-hono/frontend/src/App.tsx +47 -0
  241. package/templates/webapp/react-vite-hono/frontend/src/main.tsx +13 -0
  242. package/templates/webapp/react-vite-hono/frontend/src/style.css +66 -0
  243. package/templates/webapp/react-vite-hono/frontend/tsconfig.json +20 -0
  244. package/templates/webapp/react-vite-hono/frontend/vite.config.ts +10 -0
  245. package/templates/webapp/spa/index.ts +119 -0
  246. package/templates/webapp/ssr-auth/backend/index.ts +133 -0
  247. package/templates/webapp/ssr-auth/frontend/dist/index.html +15 -0
  248. package/templates/webapp/ssr-blog/README.md +111 -0
  249. package/templates/webapp/ssr-blog/backend/index.ts +764 -0
  250. package/templates/webapp/ssr-blog/db/schema.sql +71 -0
  251. package/templates/webapp/ssr-blog/frontend/dist/index.html +39 -0
  252. package/templates/webapp/ssr-frameworks/README.md +64 -0
  253. package/templates/webapp/ssr-frameworks/backend/index.ts +241 -0
  254. package/templates/webapp/ssr-frameworks/frontend/dist/index.html +31 -0
  255. package/templates/webapp/ssr-hono/backend/index.ts +92 -0
  256. package/templates/webapp/ssr-hono/frontend/dist/index.html +12 -0
  257. package/templates/webapp/ssr-shop/README.md +80 -0
  258. package/templates/webapp/ssr-shop/backend/index.ts +491 -0
  259. package/templates/webapp/ssr-shop/db/schema.sql +120 -0
  260. package/templates/webapp/ssr-shop/frontend/dist/index.html +12 -0
  261. package/templates/webapp/vanilla-static/frontend/app.js +6 -0
  262. package/templates/webapp/vanilla-static/frontend/index.html +19 -0
  263. package/templates/webapp/vanilla-static/frontend/style.css +43 -0
  264. package/README.md +0 -3
@@ -0,0 +1,32 @@
1
+ /**
2
+ * JWT 解析工具 - 无需密钥验证,仅解码 payload
3
+ * 用于从 JWT 中提取角色、租户ID、过期时间等信息
4
+ */
5
+ export interface JwtPayload {
6
+ role: 'anon' | 'service_role';
7
+ tenant_id?: string;
8
+ iss: string;
9
+ iat: number;
10
+ exp: number;
11
+ [key: string]: unknown;
12
+ }
13
+ export interface IntrospectedJwt {
14
+ payload: JwtPayload;
15
+ isExpired: boolean;
16
+ expiresIn: number;
17
+ fingerprint: string;
18
+ }
19
+ /**
20
+ * 解码 JWT payload(不验证签名)
21
+ * @throws Error 如果 JWT 格式无效
22
+ */
23
+ export declare function decodeJwt(token: string): JwtPayload;
24
+ /**
25
+ * 解析 JWT 并计算指纹
26
+ */
27
+ export declare function introspectJwt(token: string): Promise<IntrospectedJwt>;
28
+ /**
29
+ * 格式化过期时间为人类可读字符串
30
+ */
31
+ export declare function formatExpiry(expiresIn: number): string;
32
+ //# sourceMappingURL=jwt-introspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-introspect.d.ts","sourceRoot":"","sources":["../../src/credentials/jwt-introspect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,UAAU,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAanD;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAkB3E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAatD"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * JWT 解析工具 - 无需密钥验证,仅解码 payload
3
+ * 用于从 JWT 中提取角色、租户ID、过期时间等信息
4
+ */
5
+ /**
6
+ * 解码 JWT payload(不验证签名)
7
+ * @throws Error 如果 JWT 格式无效
8
+ */
9
+ export function decodeJwt(token) {
10
+ const parts = token.split('.');
11
+ if (parts.length !== 3) {
12
+ throw new Error('Invalid JWT format: expected 3 parts');
13
+ }
14
+ const payloadBase64 = parts[1];
15
+ try {
16
+ const payloadJson = Buffer.from(payloadBase64, 'base64url').toString('utf-8');
17
+ return JSON.parse(payloadJson);
18
+ }
19
+ catch (err) {
20
+ throw new Error(`Failed to decode JWT payload: ${err instanceof Error ? err.message : String(err)}`);
21
+ }
22
+ }
23
+ /**
24
+ * 解析 JWT 并计算指纹
25
+ */
26
+ export async function introspectJwt(token) {
27
+ const { createHash } = await import('node:crypto');
28
+ const payload = decodeJwt(token);
29
+ const now = Math.floor(Date.now() / 1000);
30
+ const expiresIn = payload.exp - now;
31
+ const isExpired = expiresIn <= 0;
32
+ // 计算指纹:sha256 前8字符 + 后4字符
33
+ const hash = createHash('sha256').update(token).digest('hex');
34
+ const fingerprint = `${hash.slice(0, 8)}...${hash.slice(-4)}`;
35
+ return {
36
+ payload,
37
+ isExpired,
38
+ expiresIn,
39
+ fingerprint,
40
+ };
41
+ }
42
+ /**
43
+ * 格式化过期时间为人类可读字符串
44
+ */
45
+ export function formatExpiry(expiresIn) {
46
+ if (expiresIn <= 0) {
47
+ const ago = Math.abs(expiresIn);
48
+ if (ago < 60)
49
+ return `expired ${ago}s ago`;
50
+ if (ago < 3600)
51
+ return `expired ${Math.floor(ago / 60)}m ago`;
52
+ if (ago < 86400)
53
+ return `expired ${Math.floor(ago / 3600)}h ago`;
54
+ return `expired ${Math.floor(ago / 86400)}d ago`;
55
+ }
56
+ if (expiresIn < 60)
57
+ return `expires in ${expiresIn}s`;
58
+ if (expiresIn < 3600)
59
+ return `expires in ${Math.floor(expiresIn / 60)}m`;
60
+ if (expiresIn < 86400)
61
+ return `expires in ${Math.floor(expiresIn / 3600)}h`;
62
+ return `expires in ${Math.floor(expiresIn / 86400)}d`;
63
+ }
64
+ //# sourceMappingURL=jwt-introspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-introspect.js","sourceRoot":"","sources":["../../src/credentials/jwt-introspect.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAe,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAEnD,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC;IACpC,MAAM,SAAS,GAAG,SAAS,IAAI,CAAC,CAAC;IAEjC,0BAA0B;IAC1B,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE9D,OAAO;QACL,OAAO;QACP,SAAS;QACT,SAAS;QACT,WAAW;KACZ,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAChC,IAAI,GAAG,GAAG,EAAE;YAAE,OAAO,WAAW,GAAG,OAAO,CAAC;QAC3C,IAAI,GAAG,GAAG,IAAI;YAAE,OAAO,WAAW,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC;QAC9D,IAAI,GAAG,GAAG,KAAK;YAAE,OAAO,WAAW,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;QACjE,OAAO,WAAW,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC;IACnD,CAAC;IAED,IAAI,SAAS,GAAG,EAAE;QAAE,OAAO,cAAc,SAAS,GAAG,CAAC;IACtD,IAAI,SAAS,GAAG,IAAI;QAAE,OAAO,cAAc,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,GAAG,CAAC;IACzE,IAAI,SAAS,GAAG,KAAK;QAAE,OAAO,cAAc,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC;IAC5E,OAAO,cAAc,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC;AACxD,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 密钥脱敏工具 - 只显示指纹,不暴露完整密钥
3
+ */
4
+ /**
5
+ * 计算密钥指纹:sha256 前8字符 + 后4字符
6
+ * 格式:sha256:abcd1234...ef56
7
+ */
8
+ export declare function computeFingerprint(key: string): string;
9
+ /**
10
+ * 脱敏 Authorization header
11
+ * 输入: "Bearer eyJhbGc..."
12
+ * 输出: "Bearer sha256:abcd1234...ef56"
13
+ */
14
+ export declare function maskAuthorizationHeader(header: string): string;
15
+ /**
16
+ * 脱敏任意字符串(如果看起来像 JWT)
17
+ * JWT 特征:包含两个点,三段 base64
18
+ */
19
+ export declare function maskIfJwt(value: string): string;
20
+ //# sourceMappingURL=masker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"masker.d.ts","sourceRoot":"","sources":["../../src/credentials/masker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAQ9D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/C"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 密钥脱敏工具 - 只显示指纹,不暴露完整密钥
3
+ */
4
+ import { createHash } from 'node:crypto';
5
+ /**
6
+ * 计算密钥指纹:sha256 前8字符 + 后4字符
7
+ * 格式:sha256:abcd1234...ef56
8
+ */
9
+ export function computeFingerprint(key) {
10
+ const hash = createHash('sha256').update(key).digest('hex');
11
+ return `sha256:${hash.slice(0, 8)}...${hash.slice(-4)}`;
12
+ }
13
+ /**
14
+ * 脱敏 Authorization header
15
+ * 输入: "Bearer eyJhbGc..."
16
+ * 输出: "Bearer sha256:abcd1234...ef56"
17
+ */
18
+ export function maskAuthorizationHeader(header) {
19
+ if (!header.startsWith('Bearer ')) {
20
+ return header;
21
+ }
22
+ const token = header.slice(7);
23
+ const fingerprint = computeFingerprint(token);
24
+ return `Bearer ${fingerprint}`;
25
+ }
26
+ /**
27
+ * 脱敏任意字符串(如果看起来像 JWT)
28
+ * JWT 特征:包含两个点,三段 base64
29
+ */
30
+ export function maskIfJwt(value) {
31
+ const parts = value.split('.');
32
+ if (parts.length === 3 && parts.every(p => p.length > 0)) {
33
+ return computeFingerprint(value);
34
+ }
35
+ return value;
36
+ }
37
+ //# sourceMappingURL=masker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"masker.js","sourceRoot":"","sources":["../../src/credentials/masker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO,UAAU,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACpD,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,WAAW,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,UAAU,WAAW,EAAE,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;QACzD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Profile 管理器 - 多环境配置管理
3
+ */
4
+ export type ProfileMode = 'single-tenant' | 'multi-tenant';
5
+ /** Where a profile's instance came from: a customer's own instance (login) or a Zero-provisioned one. */
6
+ export type ProfileSource = 'login' | 'zero';
7
+ export interface BaasProfile {
8
+ name: string;
9
+ endpoint: string;
10
+ /** Canonical App origin supplied by the platform; never derived by the CLI. */
11
+ appEndpoint?: string;
12
+ tenantId?: string;
13
+ mode: ProfileMode;
14
+ /** Instance origin. Absent on legacy profiles (treated as 'login'). */
15
+ source?: ProfileSource;
16
+ /** Zero instance id (sbx_*), only when source === 'zero'. */
17
+ zeroInstanceId?: string;
18
+ /** Zero instance expiry (ISO 8601), only when source === 'zero'. */
19
+ expiresAt?: string;
20
+ createdAt: string;
21
+ updatedAt: string;
22
+ }
23
+ interface ProfilesStore {
24
+ version: 1;
25
+ current: string;
26
+ profiles: Record<string, BaasProfile>;
27
+ }
28
+ /** 设置进程级 profile 覆盖(由 main.ts 的 preAction hook 调用)。 */
29
+ export declare function setCliProfileOverrideGlobal(profileName: string | undefined): void;
30
+ /** 读取进程级 profile 覆盖(供测试/诊断)。 */
31
+ export declare function getCliProfileOverrideGlobal(): string | undefined;
32
+ export declare class ProfileManager {
33
+ private homeDir;
34
+ private cliProfileOverride?;
35
+ private store;
36
+ constructor(homeDir?: string);
37
+ /**
38
+ * 设置 CLI profile 覆盖(来自 --profile flag)。
39
+ * 同时写入进程级变量,使其它命令实例(各自 new ProfileManager)也能看到。
40
+ */
41
+ setCliProfileOverride(profileName: string | undefined): void;
42
+ /** 生效的 --profile 覆盖:实例级优先,其次进程级。 */
43
+ private get effectiveCliProfileOverride();
44
+ /**
45
+ * 获取 profiles.json 路径
46
+ */
47
+ private getProfilesFilePath;
48
+ /**
49
+ * 加载 profiles store
50
+ */
51
+ load(): Promise<ProfilesStore>;
52
+ /**
53
+ * 保存 profiles store
54
+ */
55
+ private save;
56
+ /**
57
+ * 获取当前激活的 profile
58
+ * 优先级:--profile flag > BAAS_PROFILE env > store.current
59
+ */
60
+ getCurrentProfile(): Promise<BaasProfile | null>;
61
+ /**
62
+ * 列出所有 profiles
63
+ */
64
+ listProfiles(): Promise<BaasProfile[]>;
65
+ /**
66
+ * 获取指定 profile
67
+ */
68
+ getProfile(name: string): Promise<BaasProfile | null>;
69
+ /**
70
+ * 创建或更新 profile
71
+ */
72
+ setProfile(profile: BaasProfile): Promise<void>;
73
+ /**
74
+ * 切换当前 profile
75
+ */
76
+ useProfile(name: string): Promise<void>;
77
+ /**
78
+ * 删除 profile
79
+ */
80
+ removeProfile(name: string): Promise<boolean>;
81
+ }
82
+ export {};
83
+ //# sourceMappingURL=profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/credentials/profile.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,cAAc,CAAC;AAE3D,yGAAyG;AACzG,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,WAAW,CAAC;IAClB,uEAAuE;IACvE,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACvC;AAYD,uDAAuD;AACvD,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAEjF;AAED,gCAAgC;AAChC,wBAAgB,2BAA2B,IAAI,MAAM,GAAG,SAAS,CAEhE;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,kBAAkB,CAAC,CAAS;IACpC,OAAO,CAAC,KAAK,CAA8B;gBAE/B,OAAO,CAAC,EAAE,MAAM;IAI5B;;;OAGG;IACH,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAK5D,oCAAoC;IACpC,OAAO,KAAK,2BAA2B,GAEtC;IAED;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC;IA2BpC;;OAEG;YACW,IAAI;IAYlB;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAsCtD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAK5C;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAK3D;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBrD;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7C;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAiBpD"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Profile 管理器 - 多环境配置管理
3
+ */
4
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import { existsSync } from 'node:fs';
6
+ import { homedir } from 'node:os';
7
+ import { join } from 'node:path';
8
+ /**
9
+ * Profile 管理器
10
+ *
11
+ * 存储路径:~/.baas/profiles.json
12
+ * 优先级:--profile flag > BAAS_PROFILE env > store.current
13
+ */
14
+ // 进程级 --profile 覆盖:各命令实现自己 new ProfileManager(),实例字段无法跨实例共享,
15
+ // 因此用模块级变量承载(仅本进程生效,绝不写回 profiles.json)。
16
+ let processCliProfileOverride;
17
+ /** 设置进程级 profile 覆盖(由 main.ts 的 preAction hook 调用)。 */
18
+ export function setCliProfileOverrideGlobal(profileName) {
19
+ processCliProfileOverride = profileName;
20
+ }
21
+ /** 读取进程级 profile 覆盖(供测试/诊断)。 */
22
+ export function getCliProfileOverrideGlobal() {
23
+ return processCliProfileOverride;
24
+ }
25
+ export class ProfileManager {
26
+ homeDir;
27
+ cliProfileOverride; // --profile flag
28
+ store = null;
29
+ constructor(homeDir) {
30
+ this.homeDir = homeDir ?? join(homedir(), '.baas');
31
+ }
32
+ /**
33
+ * 设置 CLI profile 覆盖(来自 --profile flag)。
34
+ * 同时写入进程级变量,使其它命令实例(各自 new ProfileManager)也能看到。
35
+ */
36
+ setCliProfileOverride(profileName) {
37
+ this.cliProfileOverride = profileName;
38
+ processCliProfileOverride = profileName;
39
+ }
40
+ /** 生效的 --profile 覆盖:实例级优先,其次进程级。 */
41
+ get effectiveCliProfileOverride() {
42
+ return this.cliProfileOverride ?? processCliProfileOverride;
43
+ }
44
+ /**
45
+ * 获取 profiles.json 路径
46
+ */
47
+ getProfilesFilePath() {
48
+ return join(this.homeDir, 'profiles.json');
49
+ }
50
+ /**
51
+ * 加载 profiles store
52
+ */
53
+ async load() {
54
+ if (this.store) {
55
+ return this.store;
56
+ }
57
+ const filePath = this.getProfilesFilePath();
58
+ if (!existsSync(filePath)) {
59
+ this.store = { version: 1, current: '', profiles: {} };
60
+ return this.store;
61
+ }
62
+ try {
63
+ const content = await readFile(filePath, 'utf-8');
64
+ const data = JSON.parse(content);
65
+ if (data.version !== 1) {
66
+ throw new Error(`Unsupported profiles file version: ${data.version}`);
67
+ }
68
+ this.store = data;
69
+ return this.store;
70
+ }
71
+ catch (err) {
72
+ throw new Error(`Failed to read profiles file: ${err instanceof Error ? err.message : String(err)}`);
73
+ }
74
+ }
75
+ /**
76
+ * 保存 profiles store
77
+ */
78
+ async save(store) {
79
+ if (!existsSync(this.homeDir)) {
80
+ await mkdir(this.homeDir, { mode: 0o700, recursive: true });
81
+ }
82
+ const filePath = this.getProfilesFilePath();
83
+ const content = JSON.stringify(store, null, 2);
84
+ await writeFile(filePath, content, { mode: 0o644 });
85
+ this.store = store;
86
+ }
87
+ /**
88
+ * 获取当前激活的 profile
89
+ * 优先级:--profile flag > BAAS_PROFILE env > store.current
90
+ */
91
+ async getCurrentProfile() {
92
+ const store = await this.load();
93
+ // 1. CLI --profile flag
94
+ const cliOverride = this.effectiveCliProfileOverride;
95
+ if (cliOverride) {
96
+ const profile = store.profiles[cliOverride];
97
+ if (!profile) {
98
+ throw new Error(`Profile '${cliOverride}' not found (from --profile flag)`);
99
+ }
100
+ return profile;
101
+ }
102
+ // 2. BAAS_PROFILE 环境变量
103
+ const envProfile = process.env.BAAS_PROFILE;
104
+ if (envProfile) {
105
+ const profile = store.profiles[envProfile];
106
+ if (!profile) {
107
+ throw new Error(`Profile '${envProfile}' not found (from BAAS_PROFILE env)`);
108
+ }
109
+ return profile;
110
+ }
111
+ // 3. store.current
112
+ if (store.current) {
113
+ const profile = store.profiles[store.current];
114
+ if (!profile) {
115
+ // current 指向不存在的 profile,清除它
116
+ store.current = '';
117
+ await this.save(store);
118
+ return null;
119
+ }
120
+ return profile;
121
+ }
122
+ return null;
123
+ }
124
+ /**
125
+ * 列出所有 profiles
126
+ */
127
+ async listProfiles() {
128
+ const store = await this.load();
129
+ return Object.values(store.profiles);
130
+ }
131
+ /**
132
+ * 获取指定 profile
133
+ */
134
+ async getProfile(name) {
135
+ const store = await this.load();
136
+ return store.profiles[name] ?? null;
137
+ }
138
+ /**
139
+ * 创建或更新 profile
140
+ */
141
+ async setProfile(profile) {
142
+ const store = await this.load();
143
+ const now = new Date().toISOString();
144
+ if (store.profiles[profile.name]) {
145
+ // 更新
146
+ profile.updatedAt = now;
147
+ }
148
+ else {
149
+ // 创建
150
+ profile.createdAt = now;
151
+ profile.updatedAt = now;
152
+ }
153
+ store.profiles[profile.name] = profile;
154
+ await this.save(store);
155
+ }
156
+ /**
157
+ * 切换当前 profile
158
+ */
159
+ async useProfile(name) {
160
+ const store = await this.load();
161
+ if (!store.profiles[name]) {
162
+ throw new Error(`Profile '${name}' not found`);
163
+ }
164
+ store.current = name;
165
+ await this.save(store);
166
+ }
167
+ /**
168
+ * 删除 profile
169
+ */
170
+ async removeProfile(name) {
171
+ const store = await this.load();
172
+ if (!store.profiles[name]) {
173
+ return false;
174
+ }
175
+ delete store.profiles[name];
176
+ // 如果删除的是当前 profile,清除 current
177
+ if (store.current === name) {
178
+ store.current = '';
179
+ }
180
+ await this.save(store);
181
+ return true;
182
+ }
183
+ }
184
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/credentials/profile.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA8BjC;;;;;GAKG;AACH,6DAA6D;AAC7D,yCAAyC;AACzC,IAAI,yBAA6C,CAAC;AAElD,uDAAuD;AACvD,MAAM,UAAU,2BAA2B,CAAC,WAA+B;IACzE,yBAAyB,GAAG,WAAW,CAAC;AAC1C,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,2BAA2B;IACzC,OAAO,yBAAyB,CAAC;AACnC,CAAC;AAED,MAAM,OAAO,cAAc;IACjB,OAAO,CAAS;IAChB,kBAAkB,CAAU,CAAC,iBAAiB;IAC9C,KAAK,GAAyB,IAAI,CAAC;IAE3C,YAAY,OAAgB;QAC1B,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,qBAAqB,CAAC,WAA+B;QACnD,IAAI,CAAC,kBAAkB,GAAG,WAAW,CAAC;QACtC,yBAAyB,GAAG,WAAW,CAAC;IAC1C,CAAC;IAED,oCAAoC;IACpC,IAAY,2BAA2B;QACrC,OAAO,IAAI,CAAC,kBAAkB,IAAI,yBAAyB,CAAC;IAC9D,CAAC;IAED;;OAEG;IACK,mBAAmB;QACzB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAE5C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YACvD,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAkB,CAAC;YAElD,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,sCAAsC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YACxE,CAAC;YAED,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvG,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,IAAI,CAAC,KAAoB;QACrC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAE/C,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,iBAAiB;QACrB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAEhC,wBAAwB;QACxB,MAAM,WAAW,GAAG,IAAI,CAAC,2BAA2B,CAAC;QACrD,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,YAAY,WAAW,mCAAmC,CAAC,CAAC;YAC9E,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,uBAAuB;QACvB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAC5C,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,YAAY,UAAU,qCAAqC,CAAC,CAAC;YAC/E,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,mBAAmB;QACnB,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,6BAA6B;gBAC7B,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;gBACnB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACvB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAChC,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,OAAoB;QACnC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAErC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,KAAK;YACL,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,KAAK;YACL,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC;YACxB,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC;QAC1B,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;QACvC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAEhC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,aAAa,CAAC,CAAC;QACjD,CAAC;QAED,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QACrB,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,IAAY;QAC9B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAEhC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAE5B,8BAA8B;QAC9B,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC3B,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;QACrB,CAAC;QAED,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 租户一致性守卫 - 验证 profile 和 JWT 的 tenant_id 一致
3
+ */
4
+ import type { BaasProfile } from './profile.js';
5
+ import type { IntrospectedJwt } from './jwt-introspect.js';
6
+ /**
7
+ * 验证 profile 和 JWT 的 tenant_id 一致性
8
+ *
9
+ * 规则:
10
+ * - 多租户模式:profile.tenantId == anonJwt.tenantId == serviceJwt.tenantId
11
+ * - 单租户模式:profile.tenantId 为空,JWT 的 tenant_id 也应为空(或不存在)
12
+ *
13
+ * @throws Error 如果不一致
14
+ */
15
+ export declare function validateTenantConsistency(profile: BaasProfile, anonJwt: IntrospectedJwt, serviceJwt: IntrospectedJwt): void;
16
+ //# sourceMappingURL=tenant-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-guard.d.ts","sourceRoot":"","sources":["../../src/credentials/tenant-guard.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,eAAe,GAC1B,IAAI,CAwDN"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 租户一致性守卫 - 验证 profile 和 JWT 的 tenant_id 一致
3
+ */
4
+ /**
5
+ * 验证 profile 和 JWT 的 tenant_id 一致性
6
+ *
7
+ * 规则:
8
+ * - 多租户模式:profile.tenantId == anonJwt.tenantId == serviceJwt.tenantId
9
+ * - 单租户模式:profile.tenantId 为空,JWT 的 tenant_id 也应为空(或不存在)
10
+ *
11
+ * @throws Error 如果不一致
12
+ */
13
+ export function validateTenantConsistency(profile, anonJwt, serviceJwt) {
14
+ const profileTenant = profile.tenantId;
15
+ const anonTenant = anonJwt.payload.tenant_id;
16
+ const serviceTenant = serviceJwt.payload.tenant_id;
17
+ // 多租户模式
18
+ if (profile.mode === 'multi-tenant') {
19
+ if (!profileTenant) {
20
+ throw new Error('Error: multi-tenant profile must have tenantId');
21
+ }
22
+ if (anonTenant !== profileTenant) {
23
+ throw new Error(`Error: tenant_id mismatch. Profile: ${profileTenant}, Anon JWT: ${anonTenant ?? 'none'}. ` +
24
+ `Ensure you pasted the correct keys for this tenant.`);
25
+ }
26
+ if (serviceTenant !== profileTenant) {
27
+ throw new Error(`Error: tenant_id mismatch. Profile: ${profileTenant}, Service JWT: ${serviceTenant ?? 'none'}. ` +
28
+ `Ensure you pasted the correct keys for this tenant.`);
29
+ }
30
+ }
31
+ // 单租户模式
32
+ if (profile.mode === 'single-tenant') {
33
+ if (profileTenant) {
34
+ throw new Error('Error: single-tenant profile must not have tenantId');
35
+ }
36
+ // 单租户模式下,JWT 不应该包含 tenant_id(或者 tenant_id 为空字符串)
37
+ if (anonTenant && anonTenant !== '') {
38
+ throw new Error(`Error: single-tenant profile but anon JWT has tenant_id: ${anonTenant}. ` +
39
+ `This key is for multi-tenant mode.`);
40
+ }
41
+ if (serviceTenant && serviceTenant !== '') {
42
+ throw new Error(`Error: single-tenant profile but service JWT has tenant_id: ${serviceTenant}. ` +
43
+ `This key is for multi-tenant mode.`);
44
+ }
45
+ }
46
+ // 两把 key 的 tenant_id 必须一致(无论单租户还是多租户)
47
+ if (anonTenant !== serviceTenant) {
48
+ throw new Error(`Error: anon and service JWT have different tenant_id. ` +
49
+ `Anon: ${anonTenant ?? 'none'}, Service: ${serviceTenant ?? 'none'}. ` +
50
+ `Ensure both keys are for the same tenant.`);
51
+ }
52
+ }
53
+ //# sourceMappingURL=tenant-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-guard.js","sourceRoot":"","sources":["../../src/credentials/tenant-guard.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAAoB,EACpB,OAAwB,EACxB,UAA2B;IAE3B,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;IACvC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;IAC7C,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC;IAEnD,QAAQ;IACR,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACpC,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,UAAU,KAAK,aAAa,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,uCAAuC,aAAa,eAAe,UAAU,IAAI,MAAM,IAAI;gBAC3F,qDAAqD,CACtD,CAAC;QACJ,CAAC;QAED,IAAI,aAAa,KAAK,aAAa,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,uCAAuC,aAAa,kBAAkB,aAAa,IAAI,MAAM,IAAI;gBACjG,qDAAqD,CACtD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,QAAQ;IACR,IAAI,OAAO,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACrC,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACzE,CAAC;QAED,iDAAiD;QACjD,IAAI,UAAU,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,4DAA4D,UAAU,IAAI;gBAC1E,oCAAoC,CACrC,CAAC;QACJ,CAAC;QAED,IAAI,aAAa,IAAI,aAAa,KAAK,EAAE,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,+DAA+D,aAAa,IAAI;gBAChF,oCAAoC,CACrC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,IAAI,UAAU,KAAK,aAAa,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,wDAAwD;YACxD,SAAS,UAAU,IAAI,MAAM,cAAc,aAAa,IAAI,MAAM,IAAI;YACtE,2CAA2C,CAC5C,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * TTY 守卫 - 防止 agent 执行敏感命令
3
+ */
4
+ /**
5
+ * 断言当前运行在交互式 TTY 中
6
+ * @param isTTY - 可选参数,用于测试。如果不提供,则从 process.stdin.isTTY 读取
7
+ * @param ciEnv - 可选参数,用于测试。如果不提供,则从 process.env.CI 读取
8
+ * @throws Error 如果非 TTY 环境
9
+ */
10
+ export declare function assertTty(isTTY?: boolean, ciEnv?: string): void;
11
+ /**
12
+ * 断言命令行参数中不包含密钥参数
13
+ * 防止通过 argv 传递密钥(会出现在 shell history 中)
14
+ *
15
+ * @param argv - 命令行参数数组
16
+ * @throws Error 如果发现密钥参数
17
+ */
18
+ export declare function assertNoKeyArgs(argv: string[]): void;
19
+ //# sourceMappingURL=tty-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tty-guard.d.ts","sourceRoot":"","sources":["../../src/credentials/tty-guard.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAkB/D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAkBpD"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * TTY 守卫 - 防止 agent 执行敏感命令
3
+ */
4
+ /**
5
+ * 断言当前运行在交互式 TTY 中
6
+ * @param isTTY - 可选参数,用于测试。如果不提供,则从 process.stdin.isTTY 读取
7
+ * @param ciEnv - 可选参数,用于测试。如果不提供,则从 process.env.CI 读取
8
+ * @throws Error 如果非 TTY 环境
9
+ */
10
+ export function assertTty(isTTY, ciEnv) {
11
+ // 检查 stdin 是否是 TTY
12
+ const actualIsTTY = isTTY !== undefined ? isTTY : process.stdin.isTTY;
13
+ if (!actualIsTTY) {
14
+ throw new Error('Error: pxbaas login requires an interactive TTY. ' +
15
+ 'Use --anon-key-stdin and --service-key-stdin for piped input.');
16
+ }
17
+ // 额外防护:CI 环境
18
+ const actualCI = ciEnv !== undefined ? ciEnv : process.env.CI;
19
+ if (actualCI === 'true' || actualCI === '1') {
20
+ throw new Error('Error: pxbaas login is disabled in CI environments. ' +
21
+ 'Use --anon-key-stdin and --service-key-stdin for non-interactive input.');
22
+ }
23
+ }
24
+ /**
25
+ * 断言命令行参数中不包含密钥参数
26
+ * 防止通过 argv 传递密钥(会出现在 shell history 中)
27
+ *
28
+ * @param argv - 命令行参数数组
29
+ * @throws Error 如果发现密钥参数
30
+ */
31
+ export function assertNoKeyArgs(argv) {
32
+ const forbiddenArgs = [
33
+ '--anon-key',
34
+ '--service-key',
35
+ '--token',
36
+ ];
37
+ for (const arg of argv) {
38
+ // 检查 --anon-key=value 或 --anon-key value 形式
39
+ for (const forbidden of forbiddenArgs) {
40
+ if (arg === forbidden || arg.startsWith(`${forbidden}=`)) {
41
+ throw new Error(`Error: passing keys via argv is forbidden (${arg}). ` +
42
+ `Use ${forbidden}-stdin or environment variables.`);
43
+ }
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=tty-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tty-guard.js","sourceRoot":"","sources":["../../src/credentials/tty-guard.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,KAAe,EAAE,KAAc;IACvD,mBAAmB;IACnB,MAAM,WAAW,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IACtE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,mDAAmD;YACnD,+DAA+D,CAChE,CAAC;IACJ,CAAC;IAED,aAAa;IACb,MAAM,QAAQ,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;IAC9D,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,sDAAsD;YACtD,yEAAyE,CAC1E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,IAAc;IAC5C,MAAM,aAAa,GAAG;QACpB,YAAY;QACZ,eAAe;QACf,SAAS;KACV,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,4CAA4C;QAC5C,KAAK,MAAM,SAAS,IAAI,aAAa,EAAE,CAAC;YACtC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,SAAS,GAAG,CAAC,EAAE,CAAC;gBACzD,MAAM,IAAI,KAAK,CACb,8CAA8C,GAAG,KAAK;oBACtD,OAAO,SAAS,kCAAkC,CACnD,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}