@apso/cli 0.19.0 → 0.21.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.
- package/dist/commands/deploy.d.ts +12 -0
- package/dist/commands/deploy.js +80 -3
- package/dist/commands/generate.js +27 -8
- package/dist/commands/github/connect.d.ts +19 -0
- package/dist/commands/github/connect.js +95 -0
- package/dist/commands/init.js +5 -3
- package/dist/commands/link.js +3 -2
- package/dist/hooks/init/telemetry.d.ts +7 -0
- package/dist/hooks/init/telemetry.js +14 -0
- package/dist/hooks/postrun/telemetry.d.ts +8 -0
- package/dist/hooks/postrun/telemetry.js +18 -0
- package/dist/lib/api/client.d.ts +9 -0
- package/dist/lib/api/client.js +60 -1
- package/dist/lib/api/services.d.ts +45 -90
- package/dist/lib/api/services.js +259 -126
- package/dist/lib/base-command.d.ts +8 -0
- package/dist/lib/base-command.js +21 -0
- package/dist/lib/biome/biome.json +21 -0
- package/dist/lib/config/types.js +4 -1
- package/dist/lib/git.d.ts +25 -0
- package/dist/lib/git.js +113 -0
- package/dist/lib/telemetry/telemetry.d.ts +21 -0
- package/dist/lib/telemetry/telemetry.js +162 -0
- package/dist/lib/templates/go/header.eta +4 -3
- package/dist/lib/templates/guards/guards.module.eta +18 -12
- package/dist/lib/templates/header.eta +5 -7
- package/dist/lib/templates/python/header.eta +4 -4
- package/dist/lib/upgrade.d.ts +19 -0
- package/dist/lib/upgrade.js +86 -0
- package/npm-shrinkwrap.json +589 -12
- package/oclif.manifest.json +56 -1
- package/package.json +9 -2
|
@@ -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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 {};
|
package/dist/lib/api/services.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
100
|
+
async logout() {
|
|
101
|
+
return;
|
|
31
102
|
},
|
|
32
103
|
};
|
|
33
104
|
/**
|
|
34
105
|
* Workspaces API
|
|
35
106
|
*/
|
|
36
107
|
exports.workspacesApi = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
validate(
|
|
123
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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 :
|
|
161
|
-
const pollInterval = (_b = options === null || options === void 0 ? void 0 : options.pollInterval) !== null && _b !== void 0 ? _b :
|
|
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(
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
};
|