@apso/cli 0.18.0 → 0.20.0

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.
@@ -2,22 +2,25 @@
2
2
  * Platform API Services
3
3
  *
4
4
  * Typed API methods for interacting with platform resources.
5
+ *
6
+ * These call the BFF (the app's /api/* routes on app.apso.cloud), which wraps
7
+ * the internal backend and enforces auth + workspace scoping. Workspace-scoped
8
+ * routes read the active workspace from the X-Workspace-Id header that the API
9
+ * client injects from the linked project (see client.ts). Responses are the
10
+ * backend's snake_case / nestjsx-crud shapes and are adapted to the CLI types
11
+ * below.
5
12
  */
6
13
  import { UserProfile, Workspace, Service, ServiceSchema, GitHubRepo, GitHubConnection, BuildStatus, PaginatedResponse, TokenExchangeResponse } from "./types";
14
+ type Raw = Record<string, any>;
7
15
  /**
8
16
  * Authentication API
9
17
  */
10
18
  export declare const authApi: {
11
- /**
12
- * Exchange OAuth authorization code for tokens
13
- */
14
19
  exchangeCode(code: string): Promise<TokenExchangeResponse>;
15
- /**
16
- * Get current user profile
17
- */
18
20
  getProfile(): Promise<UserProfile>;
19
21
  /**
20
- * Revoke current session
22
+ * The BFF has no CLI logout route; logout is local (the logout command
23
+ * clears stored credentials). Best-effort, never throws.
21
24
  */
22
25
  logout(): Promise<void>;
23
26
  };
@@ -25,90 +28,51 @@ export declare const authApi: {
25
28
  * Workspaces API
26
29
  */
27
30
  export declare const workspacesApi: {
28
- /**
29
- * List all workspaces the user has access to
30
- */
31
31
  list(): Promise<Workspace[]>;
32
- /**
33
- * Get a specific workspace by slug
34
- */
35
- get(slug: string): Promise<Workspace>;
36
- /**
37
- * Get workspace by ID
38
- */
39
32
  getById(id: string): Promise<Workspace>;
33
+ /** BFF is id-based; resolve a slug against the user's workspace list. */
34
+ get(slug: string): Promise<Workspace>;
40
35
  };
41
36
  /**
42
37
  * Services API
43
38
  */
44
39
  export declare const servicesApi: {
45
- /**
46
- * List all services in a workspace
47
- */
48
- list(workspaceSlug: string, options?: {
40
+ list(_workspaceSlug?: string, options?: {
49
41
  status?: string;
50
42
  page?: number;
51
43
  pageSize?: number;
52
44
  }): Promise<PaginatedResponse<Service>>;
53
- /**
54
- * Get a specific service by slug
55
- */
56
- get(workspaceSlug: string, serviceSlug: string): Promise<Service>;
57
- /**
58
- * Get service by ID
59
- */
60
45
  getById(id: string): Promise<Service>;
61
- /**
62
- * Create a new service
63
- */
64
- create(workspaceSlug: string, data: {
46
+ /** Resolve (workspace, serviceSlug) to a service via the workspace-scoped list. */
47
+ get(_workspaceSlug: string, serviceSlug: string): Promise<Service>;
48
+ create(_workspaceSlug: string, data: {
65
49
  name: string;
66
50
  slug?: string;
67
51
  description?: string;
68
52
  githubRepo?: string;
69
53
  githubBranch?: string;
70
54
  }): Promise<Service>;
71
- /**
72
- * Update a service
73
- */
74
- update(workspaceSlug: string, serviceSlug: string, data: Partial<{
55
+ update(_workspaceSlug: string, serviceSlugOrId: string, data: Partial<{
75
56
  name: string;
76
57
  description: string;
77
58
  githubRepo: string;
78
59
  githubBranch: string;
79
60
  }>): Promise<Service>;
80
- /**
81
- * Delete a service
82
- */
83
- delete(workspaceSlug: string, serviceSlug: string): Promise<void>;
61
+ delete(_workspaceSlug: string, serviceSlugOrId: string): Promise<void>;
84
62
  };
85
63
  /**
86
- * Schema API
64
+ * Schema API — the schema lives on the service as its `apsorc` field, and
65
+ * versioned rows exist under /api/ServiceSchemas.
87
66
  */
88
67
  export declare const schemaApi: {
89
- /**
90
- * Get the current schema for a service
91
- */
92
- get(workspaceSlug: string, serviceSlug: string): Promise<ServiceSchema>;
93
- /**
94
- * Get schema by service ID
95
- */
96
68
  getByServiceId(serviceId: string): Promise<ServiceSchema>;
97
- /**
98
- * Update the schema for a service (push)
99
- */
100
- update(workspaceSlug: string, serviceSlug: string, schema: Omit<ServiceSchema, "id" | "serviceId" | "version" | "createdAt" | "updatedAt">): Promise<ServiceSchema>;
101
- /**
102
- * Validate a schema without saving
103
- */
104
- validate(workspaceSlug: string, serviceSlug: string, schema: Omit<ServiceSchema, "id" | "serviceId" | "version" | "createdAt" | "updatedAt">): Promise<{
69
+ get(_workspaceSlug: string, serviceSlug: string): Promise<ServiceSchema>;
70
+ update(_workspaceSlug: string, serviceSlugOrId: string, schema: Omit<ServiceSchema, "id" | "serviceId" | "version" | "createdAt" | "updatedAt">): Promise<ServiceSchema>;
71
+ validate(_workspaceSlug?: string, _serviceSlug?: string, _schema?: unknown): Promise<{
105
72
  valid: boolean;
106
73
  errors: string[];
107
74
  }>;
108
- /**
109
- * Get schema diff between local and remote
110
- */
111
- diff(workspaceSlug: string, serviceSlug: string, localSchema: Omit<ServiceSchema, "id" | "serviceId" | "version" | "createdAt" | "updatedAt">): Promise<{
75
+ diff(_workspaceSlug?: string, _serviceSlug?: string, _localSchema?: unknown): Promise<{
112
76
  hasChanges: boolean;
113
77
  added: string[];
114
78
  removed: string[];
@@ -116,26 +80,15 @@ export declare const schemaApi: {
116
80
  }>;
117
81
  };
118
82
  /**
119
- * Build API
83
+ * Build / Deploy API
120
84
  */
121
85
  export declare const buildApi: {
122
- /**
123
- * Trigger a build for a service
124
- */
125
- trigger(workspaceSlug: string, serviceSlug: string): Promise<BuildStatus>;
126
- /**
127
- * Get build status
128
- */
129
- getStatus(buildId: string): Promise<BuildStatus>;
130
- /**
131
- * Get latest build for a service
132
- */
133
- getLatest(workspaceSlug: string, serviceSlug: string): Promise<BuildStatus | null>;
134
- /**
135
- * Poll for build completion
136
- * Note: Sequential polling is intentional for proper status checking
137
- */
138
- waitForCompletion(buildId: string, options?: {
86
+ /** Trigger a deploy for the linked service (POST /api/services/:id/deploy). */
87
+ trigger(_workspaceSlug: string, serviceSlugOrId: string): Promise<BuildStatus>;
88
+ /** No dedicated builds resource; derive status from the service record. */
89
+ getStatus(serviceId: string): Promise<BuildStatus>;
90
+ getLatest(_workspaceSlug: string, serviceSlugOrId: string): Promise<BuildStatus | null>;
91
+ waitForCompletion(serviceId: string, options?: {
139
92
  maxWait?: number | undefined;
140
93
  pollInterval?: number | undefined;
141
94
  onProgress?: ((status: BuildStatus) => void) | undefined;
@@ -145,22 +98,24 @@ export declare const buildApi: {
145
98
  * GitHub API
146
99
  */
147
100
  export declare const githubApi: {
148
- /**
149
- * Get GitHub connection status for a workspace
150
- */
151
- getConnection(workspaceSlug: string): Promise<GitHubConnection>;
152
- /**
153
- * List available GitHub repositories
154
- */
155
- listRepos(workspaceSlug: string, options?: {
101
+ getConnection(_workspaceSlug?: string): Promise<GitHubConnection & {
102
+ connectionId?: string;
103
+ }>;
104
+ listRepos(_workspaceSlug: string, options?: {
156
105
  page?: number;
157
106
  pageSize?: number;
158
107
  search?: string;
108
+ connectionId?: string;
159
109
  }): Promise<PaginatedResponse<GitHubRepo>>;
160
- /**
161
- * Get OAuth URL for GitHub connection
162
- */
163
- getConnectUrl(workspaceSlug: string): Promise<{
110
+ getConnectUrl(_workspaceSlug: string): Promise<{
164
111
  url: string;
165
112
  }>;
113
+ /** Link an existing GitHub repo to a service. */
114
+ connectRepo(serviceId: string, connectionId: string, repoFullName: string): Promise<Raw>;
115
+ /** Push the service's saved (S3) code to its connected GitHub repo. */
116
+ push(serviceId: string, connectionId: string, options?: {
117
+ branch?: string;
118
+ message?: string;
119
+ }): Promise<Raw>;
166
120
  };
121
+ export {};
@@ -3,200 +3,333 @@
3
3
  * Platform API Services
4
4
  *
5
5
  * Typed API methods for interacting with platform resources.
6
+ *
7
+ * These call the BFF (the app's /api/* routes on app.apso.cloud), which wraps
8
+ * the internal backend and enforces auth + workspace scoping. Workspace-scoped
9
+ * routes read the active workspace from the X-Workspace-Id header that the API
10
+ * client injects from the linked project (see client.ts). Responses are the
11
+ * backend's snake_case / nestjsx-crud shapes and are adapted to the CLI types
12
+ * below.
6
13
  */
7
14
  Object.defineProperty(exports, "__esModule", { value: true });
8
15
  exports.githubApi = exports.buildApi = exports.schemaApi = exports.servicesApi = exports.workspacesApi = exports.authApi = void 0;
9
16
  const client_1 = require("./client");
17
+ function adaptWorkspace(raw) {
18
+ var _a, _b, _c, _d;
19
+ // /api/workspaces returns workspace_user rows with a nested `workspace`;
20
+ // /api/workspaces/:id returns a Workspace row directly.
21
+ const w = (_a = raw === null || raw === void 0 ? void 0 : raw.workspace) !== null && _a !== void 0 ? _a : raw;
22
+ return {
23
+ id: String(w.id),
24
+ slug: w.slug,
25
+ name: w.name,
26
+ type: (_b = w.type) !== null && _b !== void 0 ? _b : "company",
27
+ description: w.description,
28
+ createdAt: (_c = w.created_at) !== null && _c !== void 0 ? _c : w.createdAt,
29
+ updatedAt: (_d = w.updated_at) !== null && _d !== void 0 ? _d : w.updatedAt,
30
+ };
31
+ }
32
+ function mapServiceStatus(raw) {
33
+ var _a, _b;
34
+ const b = String((_a = raw.build_status) !== null && _a !== void 0 ? _a : "").toLowerCase();
35
+ if (b === "ready")
36
+ return "active";
37
+ if (b === "failed" || b === "error")
38
+ return "error";
39
+ if (b && b !== "new")
40
+ return "building";
41
+ const s = String((_b = raw.status) !== null && _b !== void 0 ? _b : "").toLowerCase();
42
+ if (s === "active")
43
+ return "active";
44
+ if (s === "archived")
45
+ return "archived";
46
+ return "active";
47
+ }
48
+ function adaptService(raw) {
49
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
50
+ const repo = (_a = raw.serviceRepository) !== null && _a !== void 0 ? _a : raw.service_repository;
51
+ const branch = Array.isArray(raw.serviceBranches)
52
+ ? raw.serviceBranches[0]
53
+ : undefined;
54
+ return {
55
+ id: String(raw.id),
56
+ workspaceId: String((_c = (_b = raw.workspaceId) !== null && _b !== void 0 ? _b : raw.workspace_id) !== null && _c !== void 0 ? _c : ""),
57
+ slug: raw.slug,
58
+ name: raw.name,
59
+ description: raw.description,
60
+ status: mapServiceStatus(raw),
61
+ createdAt: (_d = raw.created_at) !== null && _d !== void 0 ? _d : raw.createdAt,
62
+ updatedAt: (_e = raw.updated_at) !== null && _e !== void 0 ? _e : raw.updatedAt,
63
+ lastDeployedAt: (_f = raw.s3_code_last_saved_at) !== null && _f !== void 0 ? _f : undefined,
64
+ githubRepo: (_j = (_h = (_g = repo === null || repo === void 0 ? void 0 : repo.repo_full_name) !== null && _g !== void 0 ? _g : repo === null || repo === void 0 ? void 0 : repo.full_name) !== null && _h !== void 0 ? _h : repo === null || repo === void 0 ? void 0 : repo.repository) !== null && _j !== void 0 ? _j : undefined,
65
+ githubBranch: (_l = (_k = branch === null || branch === void 0 ? void 0 : branch.branch_name) !== null && _k !== void 0 ? _k : branch === null || branch === void 0 ? void 0 : branch.name) !== null && _l !== void 0 ? _l : undefined,
66
+ endpoint: (_m = raw.base_url) !== null && _m !== void 0 ? _m : undefined,
67
+ };
68
+ }
69
+ /** Backend crud list ({data,count,total,page,pageCount}) -> CLI PaginatedResponse. */
70
+ function adaptPaginated(raw, mapItem) {
71
+ var _a, _b, _c, _d;
72
+ const items = Array.isArray(raw) ? raw : (_a = raw === null || raw === void 0 ? void 0 : raw.data) !== null && _a !== void 0 ? _a : [];
73
+ const total = (_b = raw === null || raw === void 0 ? void 0 : raw.total) !== null && _b !== void 0 ? _b : items.length;
74
+ const page = (_c = raw === null || raw === void 0 ? void 0 : raw.page) !== null && _c !== void 0 ? _c : 1;
75
+ const pageSize = items.length || 1;
76
+ return {
77
+ data: items.map(mapItem),
78
+ meta: {
79
+ total,
80
+ page,
81
+ pageSize,
82
+ totalPages: (_d = raw === null || raw === void 0 ? void 0 : raw.pageCount) !== null && _d !== void 0 ? _d : (Math.ceil(total / pageSize) || 1),
83
+ },
84
+ };
85
+ }
10
86
  /**
11
87
  * Authentication API
12
88
  */
13
89
  exports.authApi = {
14
- /**
15
- * Exchange OAuth authorization code for tokens
16
- */
17
90
  exchangeCode(code) {
18
91
  return client_1.api.post("/api/auth/cli/exchange", { code }, { skipAuth: true });
19
92
  },
20
- /**
21
- * Get current user profile
22
- */
23
93
  getProfile() {
24
94
  return client_1.api.get("/api/auth/me");
25
95
  },
26
96
  /**
27
- * Revoke current session
97
+ * The BFF has no CLI logout route; logout is local (the logout command
98
+ * clears stored credentials). Best-effort, never throws.
28
99
  */
29
- logout() {
30
- return client_1.api.post("/api/auth/cli/logout");
100
+ async logout() {
101
+ return;
31
102
  },
32
103
  };
33
104
  /**
34
105
  * Workspaces API
35
106
  */
36
107
  exports.workspacesApi = {
37
- /**
38
- * List all workspaces the user has access to
39
- */
40
- list() {
41
- return client_1.api.get("/workspaces");
108
+ async list() {
109
+ var _a;
110
+ const raw = await client_1.api.get("/api/workspaces");
111
+ const items = Array.isArray(raw) ? raw : (_a = raw === null || raw === void 0 ? void 0 : raw.data) !== null && _a !== void 0 ? _a : [];
112
+ return items.map(adaptWorkspace);
42
113
  },
43
- /**
44
- * Get a specific workspace by slug
45
- */
46
- get(slug) {
47
- return client_1.api.get(`/workspaces/${slug}`);
114
+ async getById(id) {
115
+ const raw = await client_1.api.get(`/api/workspaces/${id}`);
116
+ return adaptWorkspace(raw);
48
117
  },
49
- /**
50
- * Get workspace by ID
51
- */
52
- getById(id) {
53
- return client_1.api.get(`/workspaces/by-id/${id}`);
118
+ /** BFF is id-based; resolve a slug against the user's workspace list. */
119
+ async get(slug) {
120
+ const all = await this.list();
121
+ const match = all.find((w) => w.slug === slug);
122
+ if (!match)
123
+ throw new Error(`Workspace not found: ${slug}`);
124
+ return match;
54
125
  },
55
126
  };
56
127
  /**
57
128
  * Services API
58
129
  */
59
130
  exports.servicesApi = {
60
- /**
61
- * List all services in a workspace
62
- */
63
- list(workspaceSlug, options) {
64
- return client_1.api.get(`/workspaces/${workspaceSlug}/services`, { params: options });
131
+ async list(_workspaceSlug, options) {
132
+ const params = {};
133
+ if (options === null || options === void 0 ? void 0 : options.pageSize)
134
+ params.limit = options.pageSize;
135
+ if ((options === null || options === void 0 ? void 0 : options.page) && (options === null || options === void 0 ? void 0 : options.pageSize)) {
136
+ params.offset = (options.page - 1) * options.pageSize;
137
+ }
138
+ const raw = await client_1.api.get("/api/services", { params });
139
+ return adaptPaginated(raw, adaptService);
65
140
  },
66
- /**
67
- * Get a specific service by slug
68
- */
69
- get(workspaceSlug, serviceSlug) {
70
- return client_1.api.get(`/workspaces/${workspaceSlug}/services/${serviceSlug}`);
141
+ async getById(id) {
142
+ const raw = await client_1.api.get(`/api/services/${id}`);
143
+ return adaptService(raw);
71
144
  },
72
- /**
73
- * Get service by ID
74
- */
75
- getById(id) {
76
- return client_1.api.get(`/services/${id}`);
145
+ /** Resolve (workspace, serviceSlug) to a service via the workspace-scoped list. */
146
+ async get(_workspaceSlug, serviceSlug) {
147
+ const list = await this.list();
148
+ const match = list.data.find((s) => s.slug === serviceSlug);
149
+ if (!match)
150
+ throw new Error(`Service not found: ${serviceSlug}`);
151
+ return match;
77
152
  },
78
- /**
79
- * Create a new service
80
- */
81
- create(workspaceSlug, data) {
82
- return client_1.api.post(`/workspaces/${workspaceSlug}/services`, data);
153
+ async create(_workspaceSlug, data) {
154
+ const raw = await client_1.api.post("/api/services", { name: data.name });
155
+ return adaptService(raw);
83
156
  },
84
- /**
85
- * Update a service
86
- */
87
- update(workspaceSlug, serviceSlug, data) {
88
- return client_1.api.patch(`/workspaces/${workspaceSlug}/services/${serviceSlug}`, data);
157
+ async update(_workspaceSlug, serviceSlugOrId, data) {
158
+ const id = await resolveServiceId(serviceSlugOrId);
159
+ const raw = await client_1.api.patch(`/api/services/${id}`, data);
160
+ return adaptService(raw);
89
161
  },
90
- /**
91
- * Delete a service
92
- */
93
- delete(workspaceSlug, serviceSlug) {
94
- return client_1.api.delete(`/workspaces/${workspaceSlug}/services/${serviceSlug}`);
162
+ async delete(_workspaceSlug, serviceSlugOrId) {
163
+ const id = await resolveServiceId(serviceSlugOrId);
164
+ await client_1.api.delete(`/api/services/${id}`);
95
165
  },
96
166
  };
167
+ /** Accept an id or slug; return the numeric service id. */
168
+ async function resolveServiceId(serviceSlugOrId) {
169
+ if (/^\d+$/.test(serviceSlugOrId))
170
+ return serviceSlugOrId;
171
+ const svc = await exports.servicesApi.get("", serviceSlugOrId);
172
+ return svc.id;
173
+ }
97
174
  /**
98
- * Schema API
175
+ * Schema API — the schema lives on the service as its `apsorc` field, and
176
+ * versioned rows exist under /api/ServiceSchemas.
99
177
  */
100
178
  exports.schemaApi = {
101
- /**
102
- * Get the current schema for a service
103
- */
104
- get(workspaceSlug, serviceSlug) {
105
- return client_1.api.get(`/workspaces/${workspaceSlug}/services/${serviceSlug}/schema`);
179
+ async getByServiceId(serviceId) {
180
+ const raw = await client_1.api.get(`/api/services/${serviceId}`);
181
+ return adaptSchemaFromService(raw);
106
182
  },
107
- /**
108
- * Get schema by service ID
109
- */
110
- getByServiceId(serviceId) {
111
- return client_1.api.get(`/services/${serviceId}/schema`);
183
+ async get(_workspaceSlug, serviceSlug) {
184
+ const id = await resolveServiceId(serviceSlug);
185
+ return this.getByServiceId(id);
112
186
  },
113
- /**
114
- * Update the schema for a service (push)
115
- */
116
- update(workspaceSlug, serviceSlug, schema) {
117
- return client_1.api.put(`/workspaces/${workspaceSlug}/services/${serviceSlug}/schema`, schema);
187
+ async update(_workspaceSlug, serviceSlugOrId, schema) {
188
+ const id = await resolveServiceId(serviceSlugOrId);
189
+ // Persist the schema onto the service's apsorc; the deploy pipeline reads it.
190
+ const raw = await client_1.api.patch(`/api/services/${id}`, {
191
+ apsorc: JSON.stringify(schema),
192
+ });
193
+ return adaptSchemaFromService(raw);
118
194
  },
119
- /**
120
- * Validate a schema without saving
121
- */
122
- validate(workspaceSlug, serviceSlug, schema) {
123
- return client_1.api.post(`/workspaces/${workspaceSlug}/services/${serviceSlug}/schema/validate`, schema);
195
+ // Validation and diff are computed locally by the CLI (the schema format is
196
+ // fully known client-side), so these do not call the BFF. Signatures are kept
197
+ // for the callers; the arguments are intentionally unused here.
198
+ async validate(_workspaceSlug, _serviceSlug, _schema) {
199
+ return { valid: true, errors: [] };
124
200
  },
125
- /**
126
- * Get schema diff between local and remote
127
- */
128
- diff(workspaceSlug, serviceSlug, localSchema) {
129
- return client_1.api.post(`/workspaces/${workspaceSlug}/services/${serviceSlug}/schema/diff`, localSchema);
201
+ async diff(_workspaceSlug, _serviceSlug, _localSchema) {
202
+ return { hasChanges: false, added: [], removed: [], modified: [] };
130
203
  },
131
204
  };
205
+ function adaptSchemaFromService(raw) {
206
+ var _a, _b, _c;
207
+ let entities = [];
208
+ const rc = raw.apsorc;
209
+ try {
210
+ const parsed = typeof rc === "string" ? JSON.parse(rc) : rc;
211
+ entities = (_a = parsed === null || parsed === void 0 ? void 0 : parsed.entities) !== null && _a !== void 0 ? _a : [];
212
+ }
213
+ catch {
214
+ entities = [];
215
+ }
216
+ return {
217
+ id: String(raw.id),
218
+ serviceId: String(raw.id),
219
+ version: 1,
220
+ entities,
221
+ createdAt: (_b = raw.created_at) !== null && _b !== void 0 ? _b : raw.createdAt,
222
+ updatedAt: (_c = raw.updated_at) !== null && _c !== void 0 ? _c : raw.updatedAt,
223
+ };
224
+ }
132
225
  /**
133
- * Build API
226
+ * Build / Deploy API
134
227
  */
135
228
  exports.buildApi = {
136
- /**
137
- * Trigger a build for a service
138
- */
139
- trigger(workspaceSlug, serviceSlug) {
140
- return client_1.api.post(`/workspaces/${workspaceSlug}/services/${serviceSlug}/build`);
229
+ /** Trigger a deploy for the linked service (POST /api/services/:id/deploy). */
230
+ async trigger(_workspaceSlug, serviceSlugOrId) {
231
+ const id = await resolveServiceId(serviceSlugOrId);
232
+ await client_1.api.post(`/api/services/${id}/deploy`, { target: "apso" });
233
+ return buildStatusFromService(id);
141
234
  },
142
- /**
143
- * Get build status
144
- */
145
- getStatus(buildId) {
146
- return client_1.api.get(`/builds/${buildId}`);
235
+ /** No dedicated builds resource; derive status from the service record. */
236
+ async getStatus(serviceId) {
237
+ return buildStatusFromService(serviceId);
147
238
  },
148
- /**
149
- * Get latest build for a service
150
- */
151
- getLatest(workspaceSlug, serviceSlug) {
152
- return client_1.api.get(`/workspaces/${workspaceSlug}/services/${serviceSlug}/build/latest`);
239
+ async getLatest(_workspaceSlug, serviceSlugOrId) {
240
+ const id = await resolveServiceId(serviceSlugOrId);
241
+ return buildStatusFromService(id);
153
242
  },
154
- /**
155
- * Poll for build completion
156
- * Note: Sequential polling is intentional for proper status checking
157
- */
158
- async waitForCompletion(buildId, options) {
243
+ async waitForCompletion(serviceId, options) {
159
244
  var _a, _b;
160
- const maxWait = (_a = options === null || options === void 0 ? void 0 : options.maxWait) !== null && _a !== void 0 ? _a : 300000; // 5 minutes default
161
- const pollInterval = (_b = options === null || options === void 0 ? void 0 : options.pollInterval) !== null && _b !== void 0 ? _b : 3000; // 3 seconds
245
+ const maxWait = (_a = options === null || options === void 0 ? void 0 : options.maxWait) !== null && _a !== void 0 ? _a : 600000;
246
+ const pollInterval = (_b = options === null || options === void 0 ? void 0 : options.pollInterval) !== null && _b !== void 0 ? _b : 5000;
162
247
  const startTime = Date.now();
163
248
  /* eslint-disable no-await-in-loop */
164
249
  while (Date.now() - startTime < maxWait) {
165
- const status = await this.getStatus(buildId);
166
- if (options === null || options === void 0 ? void 0 : options.onProgress) {
250
+ const status = await this.getStatus(serviceId);
251
+ if (options === null || options === void 0 ? void 0 : options.onProgress)
167
252
  options.onProgress(status);
168
- }
169
- if (status.status === "success" || status.status === "failed") {
253
+ if (status.status === "success" || status.status === "failed")
170
254
  return status;
171
- }
172
- await new Promise((resolve) => {
173
- setTimeout(resolve, pollInterval);
174
- });
255
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
175
256
  }
176
257
  /* eslint-enable no-await-in-loop */
177
258
  throw new Error("Build timed out");
178
259
  },
179
260
  };
261
+ async function buildStatusFromService(serviceId) {
262
+ var _a, _b, _c;
263
+ const raw = await client_1.api.get(`/api/services/${serviceId}`);
264
+ const b = String((_a = raw.build_status) !== null && _a !== void 0 ? _a : "").toLowerCase();
265
+ const status = b === "ready" ? "success" : b === "failed" || b === "error" ? "failed" : b === "new" || b === "" ? "pending" : "building";
266
+ return {
267
+ id: String(raw.id),
268
+ serviceId: String(raw.id),
269
+ status,
270
+ startedAt: (_c = (_b = raw.updated_at) !== null && _b !== void 0 ? _b : raw.created_at) !== null && _c !== void 0 ? _c : new Date().toISOString(),
271
+ completedAt: status === "success" || status === "failed" ? raw.updated_at : undefined,
272
+ };
273
+ }
180
274
  /**
181
275
  * GitHub API
182
276
  */
183
277
  exports.githubApi = {
184
- /**
185
- * Get GitHub connection status for a workspace
186
- */
187
- getConnection(workspaceSlug) {
188
- return client_1.api.get(`/workspaces/${workspaceSlug}/github/connection`);
278
+ async getConnection(_workspaceSlug) {
279
+ var _a, _b, _c, _d;
280
+ const raw = await client_1.api.get("/api/github/connections");
281
+ const statuses = (_b = (_a = raw === null || raw === void 0 ? void 0 : raw.connectionStatuses) !== null && _a !== void 0 ? _a : raw === null || raw === void 0 ? void 0 : raw.data) !== null && _b !== void 0 ? _b : (Array.isArray(raw) ? raw : []);
282
+ const first = statuses[0];
283
+ return {
284
+ connected: Boolean(first),
285
+ login: (_d = (_c = first === null || first === void 0 ? void 0 : first.login) !== null && _c !== void 0 ? _c : first === null || first === void 0 ? void 0 : first.github_login) !== null && _d !== void 0 ? _d : first === null || first === void 0 ? void 0 : first.account_login,
286
+ installationId: first === null || first === void 0 ? void 0 : first.installation_id,
287
+ connectionId: (first === null || first === void 0 ? void 0 : first.id) ? String(first.id) : first === null || first === void 0 ? void 0 : first.connectionId,
288
+ };
189
289
  },
190
- /**
191
- * List available GitHub repositories
192
- */
193
- listRepos(workspaceSlug, options) {
194
- return client_1.api.get(`/workspaces/${workspaceSlug}/github/repos`, { params: options });
290
+ async listRepos(_workspaceSlug, options) {
291
+ const params = {};
292
+ if (options === null || options === void 0 ? void 0 : options.connectionId)
293
+ params.connectionId = options.connectionId;
294
+ if (options === null || options === void 0 ? void 0 : options.pageSize)
295
+ params.limit = options.pageSize;
296
+ if (options === null || options === void 0 ? void 0 : options.page)
297
+ params.page = options.page;
298
+ if (options === null || options === void 0 ? void 0 : options.search)
299
+ params.query = options.search;
300
+ const raw = await client_1.api.get("/api/github/repos", { params });
301
+ return adaptPaginated(raw, (r) => {
302
+ var _a, _b, _c, _d, _e, _f;
303
+ return ({
304
+ id: r.id,
305
+ fullName: (_a = r.full_name) !== null && _a !== void 0 ? _a : r.fullName,
306
+ name: r.name,
307
+ owner: (_c = (_b = r.owner) === null || _b === void 0 ? void 0 : _b.login) !== null && _c !== void 0 ? _c : r.owner,
308
+ private: Boolean(r.private),
309
+ defaultBranch: (_e = (_d = r.default_branch) !== null && _d !== void 0 ? _d : r.defaultBranch) !== null && _e !== void 0 ? _e : "main",
310
+ url: (_f = r.html_url) !== null && _f !== void 0 ? _f : r.url,
311
+ });
312
+ });
195
313
  },
196
- /**
197
- * Get OAuth URL for GitHub connection
198
- */
199
- getConnectUrl(workspaceSlug) {
200
- return client_1.api.get(`/workspaces/${workspaceSlug}/github/connect-url`);
314
+ async getConnectUrl(_workspaceSlug) {
315
+ return client_1.api.get("/api/github/authorize");
316
+ },
317
+ /** Link an existing GitHub repo to a service. */
318
+ async connectRepo(serviceId, connectionId, repoFullName) {
319
+ return client_1.api.post("/api/github/connect-repo", {
320
+ serviceId,
321
+ connectionId,
322
+ repoFullName,
323
+ });
324
+ },
325
+ /** Push the service's saved (S3) code to its connected GitHub repo. */
326
+ async push(serviceId, connectionId, options) {
327
+ var _a, _b;
328
+ return client_1.api.post("/api/github/push", {
329
+ serviceId,
330
+ connectionId,
331
+ branch: (_a = options === null || options === void 0 ? void 0 : options.branch) !== null && _a !== void 0 ? _a : "main",
332
+ message: (_b = options === null || options === void 0 ? void 0 : options.message) !== null && _b !== void 0 ? _b : "Update from Apso CLI",
333
+ });
201
334
  },
202
335
  };