@apso/cli 0.8.6 → 0.10.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 (91) hide show
  1. package/README.md +221 -1653
  2. package/dist/commands/config.d.ts +13 -0
  3. package/dist/commands/config.js +114 -0
  4. package/dist/commands/deploy.d.ts +11 -0
  5. package/dist/commands/deploy.js +141 -0
  6. package/dist/commands/dev.d.ts +15 -0
  7. package/dist/commands/dev.js +95 -0
  8. package/dist/commands/generate.d.ts +12 -0
  9. package/dist/commands/generate.js +179 -0
  10. package/dist/commands/init.d.ts +16 -0
  11. package/dist/commands/init.js +313 -0
  12. package/dist/commands/link.d.ts +11 -0
  13. package/dist/commands/link.js +139 -0
  14. package/dist/commands/login.d.ts +22 -0
  15. package/dist/commands/login.js +345 -0
  16. package/dist/commands/logout.d.ts +9 -0
  17. package/dist/commands/logout.js +38 -0
  18. package/dist/commands/logs.d.ts +10 -0
  19. package/dist/commands/logs.js +64 -0
  20. package/dist/commands/mcp/serve.d.ts +11 -0
  21. package/dist/commands/mcp/serve.js +818 -0
  22. package/dist/commands/migrate.d.ts +11 -0
  23. package/dist/commands/migrate.js +137 -0
  24. package/dist/commands/open.d.ts +10 -0
  25. package/dist/commands/open.js +79 -0
  26. package/dist/commands/projects.d.ts +10 -0
  27. package/dist/commands/projects.js +106 -0
  28. package/dist/commands/schema/diff.d.ts +7 -0
  29. package/dist/commands/schema/diff.js +63 -0
  30. package/dist/commands/schema/pull.d.ts +9 -0
  31. package/dist/commands/schema/pull.js +81 -0
  32. package/dist/commands/schema/push.d.ts +9 -0
  33. package/dist/commands/schema/push.js +97 -0
  34. package/dist/commands/schema/validate.d.ts +9 -0
  35. package/dist/commands/schema/validate.js +90 -0
  36. package/dist/commands/server/new.d.ts +2 -19
  37. package/dist/commands/server/new.js +6 -192
  38. package/dist/commands/server/scaffold.d.ts +2 -25
  39. package/dist/commands/server/scaffold.js +6 -312
  40. package/dist/commands/status.d.ts +7 -0
  41. package/dist/commands/status.js +53 -0
  42. package/dist/commands/unlink.d.ts +9 -0
  43. package/dist/commands/unlink.js +47 -0
  44. package/dist/commands/whoami.d.ts +10 -0
  45. package/dist/commands/whoami.js +97 -0
  46. package/dist/lib/api/client.js +1 -1
  47. package/dist/lib/api/services.js +3 -3
  48. package/dist/lib/api/types.d.ts +9 -2
  49. package/dist/lib/apsorc-parser.d.ts +0 -19
  50. package/dist/lib/apsorc-parser.js +2 -73
  51. package/dist/lib/guards.d.ts +3 -20
  52. package/dist/lib/guards.js +1 -113
  53. package/dist/lib/index.d.ts +1 -9
  54. package/dist/lib/index.js +1 -18
  55. package/dist/lib/migrate/entity-generator.d.ts +14 -0
  56. package/dist/lib/migrate/entity-generator.js +7 -0
  57. package/dist/lib/migrate/sandbox.d.ts +42 -0
  58. package/dist/lib/migrate/sandbox.js +337 -0
  59. package/dist/lib/migrate/snapshot.d.ts +80 -0
  60. package/dist/lib/migrate/snapshot.js +100 -0
  61. package/dist/lib/templates/python/models/model-col-datetime.eta +1 -1
  62. package/dist/lib/templates/python/models/model-col-string.eta +1 -1
  63. package/dist/lib/templates/python/models/model-col-text.eta +1 -1
  64. package/dist/lib/templates/python/models/model-col-varchar.eta +1 -1
  65. package/dist/lib/utils/field.d.ts +12 -0
  66. package/dist/lib/utils/field.js +80 -1
  67. package/dist/lib/utils/schema-convert.d.ts +71 -0
  68. package/dist/lib/utils/schema-convert.js +169 -0
  69. package/dist/lib/utils/spinner.d.ts +19 -0
  70. package/dist/lib/utils/spinner.js +87 -0
  71. package/dist/lib/utils/template.d.ts +12 -0
  72. package/dist/lib/utils/template.js +49 -0
  73. package/npm-shrinkwrap.json +18118 -0
  74. package/oclif.manifest.json +547 -41
  75. package/package.json +26 -5
  76. package/dist/lib/controller.d.ts +0 -10
  77. package/dist/lib/controller.js +0 -99
  78. package/dist/lib/dto.d.ts +0 -16
  79. package/dist/lib/dto.js +0 -92
  80. package/dist/lib/entity.d.ts +0 -19
  81. package/dist/lib/entity.js +0 -56
  82. package/dist/lib/enums.d.ts +0 -6
  83. package/dist/lib/enums.js +0 -28
  84. package/dist/lib/gql-dto.d.ts +0 -3
  85. package/dist/lib/gql-dto.js +0 -39
  86. package/dist/lib/index-module.d.ts +0 -13
  87. package/dist/lib/index-module.js +0 -26
  88. package/dist/lib/module.d.ts +0 -13
  89. package/dist/lib/module.js +0 -42
  90. package/dist/lib/service.d.ts +0 -2
  91. package/dist/lib/service.js +0 -61
@@ -0,0 +1,345 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const core_1 = require("@oclif/core");
5
+ const http = tslib_1.__importStar(require("http"));
6
+ const crypto = tslib_1.__importStar(require("crypto"));
7
+ const child_process_1 = require("child_process");
8
+ const base_command_1 = tslib_1.__importDefault(require("../lib/base-command"));
9
+ const config_1 = require("../lib/config");
10
+ const services_1 = require("../lib/api/services");
11
+ /**
12
+ * Open a URL in the default browser
13
+ */
14
+ function openBrowser(url) {
15
+ return new Promise((resolve, reject) => {
16
+ const platform = process.platform;
17
+ let command;
18
+ if (platform === "darwin") {
19
+ command = `open "${url}"`;
20
+ }
21
+ else if (platform === "win32") {
22
+ command = `start "" "${url}"`;
23
+ }
24
+ else {
25
+ command = `xdg-open "${url}"`;
26
+ }
27
+ (0, child_process_1.exec)(command, (error) => {
28
+ if (error) {
29
+ reject(error);
30
+ }
31
+ else {
32
+ resolve();
33
+ }
34
+ });
35
+ });
36
+ }
37
+ /**
38
+ * Generate a random state token for CSRF protection
39
+ */
40
+ function generateState() {
41
+ return crypto.randomBytes(16).toString("hex");
42
+ }
43
+ /**
44
+ * Create a simple HTML response for the callback
45
+ */
46
+ function createHtmlResponse(success, message) {
47
+ const color = success ? "#10b981" : "#ef4444";
48
+ return `<!DOCTYPE html>
49
+ <html>
50
+ <head>
51
+ <meta charset="utf-8">
52
+ <title>Apso CLI ${success ? "Login Successful" : "Login Failed"}</title>
53
+ <style>
54
+ body {
55
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
56
+ display: flex;
57
+ justify-content: center;
58
+ align-items: center;
59
+ min-height: 100vh;
60
+ margin: 0;
61
+ background: #0f172a;
62
+ color: #f8fafc;
63
+ }
64
+ .container {
65
+ text-align: center;
66
+ padding: 2rem;
67
+ }
68
+ .logo {
69
+ margin-bottom: 1.5rem;
70
+ }
71
+ .logo svg {
72
+ width: 48px;
73
+ height: 48px;
74
+ }
75
+ .status-icon {
76
+ font-size: 2rem;
77
+ color: ${color};
78
+ margin-bottom: 0.5rem;
79
+ }
80
+ h1 { margin: 0 0 0.5rem; font-size: 1.5rem; }
81
+ p { color: #94a3b8; margin: 0; }
82
+ </style>
83
+ </head>
84
+ <body>
85
+ <div class="container">
86
+ <div class="logo">
87
+ <svg width="48" height="48" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
88
+ <ellipse cx="18" cy="17.9947" rx="18" ry="17.9947" fill="white"/>
89
+ <path d="M35.9775 17.0728C35.4957 7.56425 27.6321 0 17.9988 0C17.6236 0 17.2531 0.0153841 16.8837 0.039052C16.8683 0.039052 16.8565 0.0426022 16.8411 0.0426022C16.5014 0.0639032 16.1652 0.0946714 15.8326 0.13609C15.7805 0.13964 15.7331 0.147924 15.681 0.153841C15.3721 0.196443 15.0691 0.241412 14.766 0.295848C14.7175 0.301765 14.6725 0.313599 14.624 0.319516C13.5906 0.515959 12.582 0.801157 11.6149 1.16801C11.5604 1.18931 11.506 1.21298 11.448 1.23191C11.1899 1.33487 10.939 1.44137 10.6868 1.55261C10.6146 1.58338 10.5448 1.61651 10.4714 1.64965C10.2287 1.76207 9.98369 1.88041 9.74694 2.00348C9.68657 2.03425 9.6262 2.06384 9.56465 2.09697C8.97396 2.40939 8.39747 2.75731 7.8494 3.13244C7.79495 3.16913 7.74286 3.20818 7.68605 3.24723C7.47415 3.39515 7.26818 3.54663 7.05866 3.7052C6.97698 3.76911 6.90122 3.82946 6.82191 3.89336C6.63962 4.03537 6.46442 4.18093 6.28804 4.33004C6.20873 4.39986 6.13297 4.46613 6.0513 4.53595C5.87255 4.69925 5.69381 4.86256 5.51743 5.03297C5.45706 5.08741 5.39669 5.14776 5.33513 5.20575C5.10193 5.43651 4.87465 5.672 4.6533 5.9146C4.64146 5.93235 4.62607 5.95128 4.61068 5.96312C4.40708 6.19033 4.21413 6.42109 4.02236 6.65067C3.96199 6.72996 3.90162 6.80806 3.84006 6.8838C3.69801 7.06604 3.55833 7.2471 3.4222 7.43171C3.39498 7.47431 3.36183 7.51691 3.33105 7.55951C3.33105 7.55951 3.3346 7.55951 3.3346 7.56307C1.23938 10.5062 0 14.1072 0 18.0006C0 21.894 1.23938 25.4915 3.33934 28.4369L3.33579 28.4405C3.39971 28.5316 3.46955 28.6156 3.53584 28.7067C3.65066 28.8582 3.76667 29.0132 3.88386 29.1612C3.9833 29.2949 4.09338 29.4192 4.19874 29.5434C4.31356 29.6854 4.43193 29.8251 4.55268 29.9611C4.6675 30.0949 4.78943 30.2191 4.90662 30.3434C5.02499 30.4736 5.14336 30.5978 5.26766 30.7185C5.39787 30.8522 5.53163 30.9765 5.66776 31.1031C5.78614 31.2155 5.90451 31.3268 6.02289 31.4333C6.17085 31.567 6.32237 31.6913 6.47389 31.8155C6.58871 31.9126 6.70472 32.012 6.81954 32.1066C6.98645 32.2344 7.15573 32.3575 7.32264 32.4889C7.40195 32.5433 7.47415 32.6072 7.55583 32.6616C7.55583 32.6616 7.55583 32.6581 7.55938 32.6581C10.5022 34.761 14.1019 36 17.9988 36C21.8957 36 25.4919 34.761 28.4406 32.6581V32.6616C28.4678 32.6439 28.4927 32.6226 28.5164 32.6013C28.7472 32.4404 28.9674 32.2676 29.1887 32.096C29.2314 32.0629 29.274 32.0297 29.3166 31.9966C29.841 31.5729 30.3382 31.1209 30.8105 30.6451C30.8472 30.6061 30.8862 30.5635 30.9253 30.5244C31.1135 30.3339 31.2946 30.1304 31.4769 29.928C31.5373 29.8617 31.5953 29.7943 31.6557 29.7245C31.8107 29.5399 31.9646 29.3552 32.1138 29.1635C32.1718 29.0914 32.2321 29.0156 32.289 28.9422C32.4464 28.7363 32.5956 28.5328 32.7471 28.321C32.7861 28.2606 32.8288 28.2026 32.8714 28.1423C33.0596 27.8665 33.2383 27.5908 33.4135 27.3056C33.423 27.2843 33.4348 27.263 33.4467 27.2417C33.6041 26.9837 33.7497 26.721 33.8918 26.4571C33.9367 26.366 33.9829 26.2749 34.0279 26.1909C34.1309 25.9849 34.2315 25.779 34.3309 25.5696C34.3795 25.4666 34.428 25.3637 34.473 25.2572C34.5606 25.0536 34.6482 24.8477 34.7275 24.6394C34.7701 24.5365 34.8127 24.4335 34.8518 24.3306C34.9453 24.088 35.0305 23.8371 35.1122 23.585C35.1299 23.5211 35.1548 23.4572 35.1761 23.3909C35.2791 23.0762 35.3667 22.7578 35.4484 22.4336C35.4697 22.3484 35.4851 22.2644 35.5064 22.1792C35.5667 21.9366 35.6188 21.6975 35.6674 21.449C35.6887 21.3366 35.71 21.2182 35.7313 21.1011C35.768 20.8798 35.8035 20.6585 35.8307 20.4372C35.8485 20.3129 35.8639 20.1922 35.8793 20.068C35.9065 19.8349 35.9278 19.6017 35.9455 19.365C35.9515 19.2585 35.9633 19.1473 35.9692 19.0408C35.987 18.6952 36 18.3473 36 18.0018C36 17.6929 35.9941 17.384 35.9763 17.0752L35.9775 17.0728ZM9.37525 14.4043C9.13258 14.5285 8.90885 14.6409 8.90885 14.6409C9.07221 13.6256 9.6842 12.7238 10.6028 12.26C11.2598 11.9262 12.0635 11.7262 12.9028 11.7262C13.8628 11.7262 14.7542 11.9925 15.4692 12.4836C15.8172 12.6954 16.106 12.9416 16.3203 13.2043C16.4718 13.3712 16.6292 13.5925 16.7807 13.8433L17.2862 14.719L16.3771 14.2765C15.7284 13.964 15.017 13.8007 14.3079 13.8007C12.1712 13.8007 10.4418 15.1853 10.0263 17.39L9.92687 17.9592C9.92687 17.9592 8.80231 16.532 9.96357 14.1214C9.88426 14.1522 9.62146 14.2824 9.37525 14.4031V14.4043ZM23.7471 26.0583L22.7563 25.4157C22.7468 25.4063 22.735 25.4004 22.7231 25.3921L22.1751 25.0347L21.608 24.6678L20.1663 23.7282C19.2477 23.1223 18.6878 22.0679 18.6878 20.9236V20.2088C19.1305 20.0124 19.4335 19.636 19.4335 19.2006C19.4335 18.7887 19.1672 18.4278 18.7671 18.2254C18.7339 18.2101 18.7031 18.1947 18.67 18.1828C18.4735 18.0976 18.2427 18.0467 18 18.0467C17.7573 18.0467 17.5277 18.0988 17.33 18.1828C17.2969 18.1947 17.2661 18.2101 17.2365 18.2254C16.834 18.429 16.5665 18.7887 16.5665 19.2006C16.5665 19.6372 16.8695 20.0124 17.3122 20.2088V20.9236C17.3122 22.0691 16.7512 23.1223 15.8337 23.7282L14.3943 24.6678L13.8273 25.0347L13.2757 25.3921C13.2638 25.4015 13.2544 25.4074 13.2425 25.4157L12.2517 26.0583L11.9452 22.1389C11.9274 21.1934 12.1144 20.3094 12.4932 19.5307C12.4932 19.5248 12.4968 19.5213 12.4968 19.5213C12.833 18.8302 13.3183 18.2219 13.954 17.7249C15.217 16.7284 16.7085 16.5675 17.9988 16.8764C19.2891 16.5675 20.7806 16.7284 22.0437 17.7249C22.677 18.2219 23.1647 18.8302 23.5009 19.5213C23.5009 19.5213 23.5044 19.5248 23.5044 19.5307C23.8832 20.3094 24.0714 21.1934 24.056 22.1389L23.7471 26.0583ZM21.6874 13.8019C20.9818 13.8019 20.2657 13.9652 19.6205 14.2776L18.7079 14.7202L19.2204 13.8445C19.366 13.5936 19.527 13.3723 19.6809 13.2055C19.8928 12.9416 20.184 12.6966 20.5297 12.4848C21.2423 11.9937 22.136 11.7274 23.096 11.7274C23.9353 11.7274 24.7379 11.9274 25.396 12.2611C26.3111 12.725 26.9266 13.6268 27.0864 14.6421C27.0864 14.6421 26.8651 14.5297 26.6224 14.4054C26.3797 14.2847 26.1169 14.1546 26.0341 14.1238C27.1918 16.5356 26.0708 17.9615 26.0708 17.9615L25.9713 17.3923C25.5535 15.1865 23.8264 13.8031 21.6862 13.8031L21.6874 13.8019ZM26.8591 30.4014C24.6739 26.6334 26.8591 22.8194 26.8591 22.8194L27.7564 24.7246C29.4409 22.6016 31.0318 19.3627 30.3263 15.1131C29.9629 12.9262 28.8384 11.0482 26.9775 9.52986C25.75 8.52398 24.3022 8.01512 22.6746 8.01512C20.9049 8.01512 19.2867 8.60327 18.2261 9.04467L17.9988 9.13816L17.7715 9.04467C16.7109 8.60208 15.0927 8.01512 13.3207 8.01512C11.6966 8.01512 10.2512 8.52398 9.01776 9.52986C7.16046 11.047 6.03354 12.9262 5.6725 15.1131C4.96699 19.3627 6.55675 22.6016 8.2424 24.7246L9.13613 22.8194C9.13613 22.8194 11.3213 26.6334 9.13613 30.4014C5.25464 27.6322 2.72379 23.0915 2.72379 17.9604C2.72379 9.52395 9.55991 2.68394 17.9988 2.68394C26.4377 2.68394 33.2774 9.52395 33.2774 17.9604C33.2774 23.0915 30.7442 27.6334 26.8591 30.4014Z" fill="#0C0C0C"/>
90
+ </svg>
91
+ </div>
92
+ <div class="status-icon">${success ? "&#10003;" : "&#10007;"}</div>
93
+ <h1>${success ? "Login Successful" : "Login Failed"}</h1>
94
+ <p>${message}</p>
95
+ </div>
96
+ </body>
97
+ </html>`;
98
+ }
99
+ class Login extends base_command_1.default {
100
+ async run() {
101
+ const { flags } = await this.parse(Login);
102
+ // Check if already logged in
103
+ if (config_1.credentials.isValid()) {
104
+ const creds = config_1.credentials.read();
105
+ if (creds) {
106
+ this.log(`Already logged in as ${creds.user.email}`);
107
+ this.log("Run 'apso logout' first to switch accounts.");
108
+ return;
109
+ }
110
+ }
111
+ // Handle direct token login (for CI/CD)
112
+ if (flags.token) {
113
+ await this.loginWithToken(flags.token);
114
+ return;
115
+ }
116
+ // Browser-based OAuth flow
117
+ await this.loginWithBrowser();
118
+ }
119
+ /**
120
+ * Login using a direct API token (for CI/CD)
121
+ */
122
+ async loginWithToken(token) {
123
+ this.log("Validating token...");
124
+ try {
125
+ // Store the token temporarily to make the API call
126
+ config_1.credentials.write({
127
+ tokens: {
128
+ accessToken: token,
129
+ refreshToken: token,
130
+ expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(),
131
+ tokenType: "Bearer",
132
+ },
133
+ user: {
134
+ id: "",
135
+ email: "",
136
+ },
137
+ });
138
+ // Validate by fetching profile
139
+ const profile = await services_1.authApi.getProfile();
140
+ // Update with real user info
141
+ config_1.credentials.write({
142
+ tokens: {
143
+ accessToken: token,
144
+ refreshToken: token,
145
+ expiresAt: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(),
146
+ tokenType: "Bearer",
147
+ },
148
+ user: {
149
+ id: profile.id,
150
+ email: profile.email,
151
+ name: profile.name,
152
+ avatarUrl: profile.avatarUrl,
153
+ },
154
+ });
155
+ this.log(`Logged in as ${profile.email}`);
156
+ }
157
+ catch (error) {
158
+ config_1.credentials.clear();
159
+ this.error("Invalid token. Please check your token and try again.");
160
+ }
161
+ }
162
+ /**
163
+ * Login using browser-based OAuth flow
164
+ */
165
+ async loginWithBrowser() {
166
+ const config = config_1.globalConfig.read();
167
+ const state = generateState();
168
+ // Find an available port
169
+ const port = await this.findAvailablePort();
170
+ const callbackUrl = `http://localhost:${port}/callback`;
171
+ // Track server for cleanup
172
+ let callbackServer = null;
173
+ let timeoutId = null;
174
+ const cleanup = () => {
175
+ if (timeoutId) {
176
+ clearTimeout(timeoutId);
177
+ timeoutId = null;
178
+ }
179
+ if (callbackServer) {
180
+ try {
181
+ callbackServer.closeAllConnections();
182
+ callbackServer.close();
183
+ }
184
+ catch {
185
+ // Ignore cleanup errors
186
+ }
187
+ callbackServer = null;
188
+ }
189
+ };
190
+ // Create promise to wait for callback
191
+ const tokenPromise = new Promise((resolve, reject) => {
192
+ const server = http.createServer((req, res) => {
193
+ const url = new URL(req.url || "/", `http://localhost:${port}`);
194
+ if (url.pathname === "/callback") {
195
+ const code = url.searchParams.get("code");
196
+ const receivedState = url.searchParams.get("state");
197
+ const error = url.searchParams.get("error");
198
+ // Close connection header to ensure browser doesn't keep connection open
199
+ res.setHeader("Connection", "close");
200
+ if (error) {
201
+ res.writeHead(200, { "Content-Type": "text/html" });
202
+ res.end(createHtmlResponse(false, "Authorization was denied. You can close this window."), () => {
203
+ cleanup();
204
+ reject(new Error(error === "access_denied" ? "Authorization denied by user" : error));
205
+ });
206
+ return;
207
+ }
208
+ if (!code) {
209
+ res.writeHead(200, { "Content-Type": "text/html" });
210
+ res.end(createHtmlResponse(false, "No authorization code received. Please try again."), () => {
211
+ cleanup();
212
+ reject(new Error("No authorization code received"));
213
+ });
214
+ return;
215
+ }
216
+ res.writeHead(200, { "Content-Type": "text/html" });
217
+ res.end(createHtmlResponse(true, "You can close this window and return to the terminal."), () => {
218
+ cleanup();
219
+ resolve({ code, receivedState: receivedState || "" });
220
+ });
221
+ }
222
+ else {
223
+ res.writeHead(404);
224
+ res.end("Not found");
225
+ }
226
+ });
227
+ callbackServer = server;
228
+ server.listen(port, "127.0.0.1", () => {
229
+ // Server is ready
230
+ });
231
+ // Timeout after 5 minutes
232
+ timeoutId = setTimeout(() => {
233
+ cleanup();
234
+ reject(new Error("Login timed out. Please try again."));
235
+ }, 5 * 60 * 1000);
236
+ });
237
+ // Build authorization URL
238
+ const authUrl = `${config.webUrl}/cli-login?callback=${encodeURIComponent(callbackUrl)}&state=${state}`;
239
+ this.log("Opening browser for authentication...");
240
+ this.log("");
241
+ this.log("If the browser doesn't open, visit this URL:");
242
+ this.log(authUrl);
243
+ this.log("");
244
+ try {
245
+ await openBrowser(authUrl);
246
+ }
247
+ catch {
248
+ this.log("Could not open browser automatically. Please visit the URL above.");
249
+ }
250
+ this.log("Waiting for authorization...");
251
+ try {
252
+ const { code, receivedState } = await tokenPromise;
253
+ // Verify state to prevent CSRF
254
+ if (receivedState !== state) {
255
+ this.error("Security error: State mismatch. Please try again.");
256
+ }
257
+ this.log("Exchanging authorization code...");
258
+ // Exchange code for tokens
259
+ let tokenResponse;
260
+ try {
261
+ tokenResponse = await services_1.authApi.exchangeCode(code);
262
+ }
263
+ catch (exchangeError) {
264
+ const err = exchangeError;
265
+ this.error(err.message || "Failed to exchange authorization code. Please try again.");
266
+ }
267
+ // Validate response has required fields
268
+ if (!tokenResponse || !tokenResponse.user) {
269
+ this.error("Server returned invalid response. Please try again.");
270
+ }
271
+ // Use expiresAt directly from response, or calculate from expiresIn, or default to 7 days
272
+ let expiresAt;
273
+ if (tokenResponse.expiresAt) {
274
+ expiresAt = tokenResponse.expiresAt;
275
+ }
276
+ else if (tokenResponse.expiresIn) {
277
+ expiresAt = new Date(Date.now() + tokenResponse.expiresIn * 1000).toISOString();
278
+ }
279
+ else {
280
+ expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
281
+ }
282
+ // Store credentials
283
+ config_1.credentials.write({
284
+ tokens: {
285
+ accessToken: tokenResponse.accessToken,
286
+ refreshToken: tokenResponse.refreshToken,
287
+ expiresAt,
288
+ tokenType: tokenResponse.tokenType || "Bearer",
289
+ },
290
+ user: {
291
+ id: tokenResponse.user.id,
292
+ email: tokenResponse.user.email,
293
+ name: tokenResponse.user.name,
294
+ avatarUrl: tokenResponse.user.avatarUrl,
295
+ },
296
+ });
297
+ this.log("");
298
+ this.log(`Successfully logged in as ${tokenResponse.user.email}`);
299
+ this.log("");
300
+ this.log("Next steps:");
301
+ this.log(" apso whoami - View your account info");
302
+ this.log(" apso services - List your services");
303
+ this.log(" apso tui - Launch interactive UI");
304
+ }
305
+ catch (error) {
306
+ if (error instanceof Error) {
307
+ this.error(error.message);
308
+ }
309
+ this.error("Login failed. Please try again.");
310
+ }
311
+ }
312
+ /**
313
+ * Find an available port for the callback server
314
+ */
315
+ findAvailablePort() {
316
+ return new Promise((resolve, reject) => {
317
+ const server = http.createServer();
318
+ server.listen(0, "127.0.0.1", () => {
319
+ const address = server.address();
320
+ if (address && typeof address === "object") {
321
+ const port = address.port;
322
+ server.close(() => resolve(port));
323
+ }
324
+ else {
325
+ server.close();
326
+ reject(new Error("Could not find available port"));
327
+ }
328
+ });
329
+ server.on("error", reject);
330
+ });
331
+ }
332
+ }
333
+ Login.description = "Authenticate with the Apso platform";
334
+ Login.examples = [
335
+ "$ apso login",
336
+ "$ apso login --token <api-token>",
337
+ ];
338
+ Login.flags = {
339
+ help: core_1.Flags.help({ char: "h" }),
340
+ token: core_1.Flags.string({
341
+ char: "t",
342
+ description: "Use an API token directly (for CI/CD)",
343
+ }),
344
+ };
345
+ exports.default = Login;
@@ -0,0 +1,9 @@
1
+ import BaseCommand from "../lib/base-command";
2
+ export default class Logout extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const core_1 = require("@oclif/core");
5
+ const base_command_1 = tslib_1.__importDefault(require("../lib/base-command"));
6
+ const config_1 = require("../lib/config");
7
+ const api_1 = require("../lib/api");
8
+ class Logout extends base_command_1.default {
9
+ async run() {
10
+ await this.parse(Logout);
11
+ if (!config_1.credentials.exists()) {
12
+ this.log("Not logged in.");
13
+ return;
14
+ }
15
+ const creds = config_1.credentials.read();
16
+ const email = (creds === null || creds === void 0 ? void 0 : creds.user.email) || "unknown";
17
+ // Revoke session on the server
18
+ try {
19
+ await api_1.authApi.logout();
20
+ }
21
+ catch {
22
+ // Server-side revocation is best-effort.
23
+ // If the server is unreachable or the session is already expired,
24
+ // we still clear local credentials.
25
+ }
26
+ // Clear local credentials
27
+ config_1.credentials.clear();
28
+ this.log(`Logged out from ${email}`);
29
+ }
30
+ }
31
+ Logout.description = "Log out from the Apso platform";
32
+ Logout.examples = [
33
+ "$ apso logout",
34
+ ];
35
+ Logout.flags = {
36
+ help: core_1.Flags.help({ char: "h" }),
37
+ };
38
+ exports.default = Logout;
@@ -0,0 +1,10 @@
1
+ import BaseCommand from "../lib/base-command";
2
+ export default class Logs extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ buildId: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
7
+ };
8
+ static flags: {};
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const core_1 = require("@oclif/core");
5
+ const base_command_1 = tslib_1.__importDefault(require("../lib/base-command"));
6
+ const config_1 = require("../lib/config");
7
+ const services_1 = require("../lib/api/services");
8
+ class Logs extends base_command_1.default {
9
+ async run() {
10
+ const { args } = await this.parse(Logs);
11
+ // Auth guard
12
+ if (!config_1.credentials.isValid()) {
13
+ this.error("Not logged in. Run 'apso login' first.");
14
+ }
15
+ // Link guard
16
+ const link = config_1.projectLink.read();
17
+ if (!link) {
18
+ this.error("Project not linked. Run 'apso link' first.");
19
+ }
20
+ let build;
21
+ if (args.buildId) {
22
+ build = await services_1.buildApi.getStatus(args.buildId);
23
+ }
24
+ else {
25
+ build = await services_1.buildApi.getLatest(link.workspaceSlug, link.serviceSlug);
26
+ if (!build) {
27
+ this.log("No builds found. Run 'apso deploy' to trigger one.");
28
+ return;
29
+ }
30
+ }
31
+ this.log(`Build: ${build.id}`);
32
+ this.log(`Status: ${build.status}`);
33
+ this.log(`Started: ${new Date(build.startedAt).toLocaleString()}`);
34
+ if (build.completedAt) {
35
+ this.log(`Completed: ${new Date(build.completedAt).toLocaleString()}`);
36
+ }
37
+ if (build.error) {
38
+ this.log(`Error: ${build.error}`);
39
+ }
40
+ this.log("\n--- Logs ---\n");
41
+ if (build.logs) {
42
+ this.log(build.logs);
43
+ }
44
+ else {
45
+ this.log("No logs available.");
46
+ if (build.status === "pending" || build.status === "building") {
47
+ this.log("Build is still in progress. Try again when it completes.");
48
+ }
49
+ }
50
+ }
51
+ }
52
+ Logs.description = "View build logs for the linked service";
53
+ Logs.examples = [
54
+ `$ apso logs`,
55
+ `$ apso logs <build-id>`,
56
+ ];
57
+ Logs.args = {
58
+ buildId: core_1.Args.string({
59
+ description: "Specific build ID (defaults to latest)",
60
+ required: false,
61
+ }),
62
+ };
63
+ Logs.flags = {};
64
+ exports.default = Logs;
@@ -0,0 +1,11 @@
1
+ import BaseCommand from "../../lib/base-command";
2
+ export default class McpServe extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
7
+ };
8
+ run(): Promise<void>;
9
+ private registerTools;
10
+ private registerResources;
11
+ }