@pmate/cli 0.24.1 → 0.24.3
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/index.js +654 -104
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -10136,12 +10136,90 @@ var LoginCommandHandler = class {
|
|
|
10136
10136
|
// src/services/AliyunService.ts
|
|
10137
10137
|
var import_alidns20150109 = __toESM(require("@alicloud/alidns20150109"));
|
|
10138
10138
|
var import_cdn20180510 = __toESM(require("@alicloud/cdn20180510"));
|
|
10139
|
+
var import_fc20230330 = __toESM(require("@alicloud/fc20230330"));
|
|
10139
10140
|
var import_openapi_client = require("@alicloud/openapi-client");
|
|
10141
|
+
var import_sts20150401 = __toESM(require("@alicloud/sts20150401"));
|
|
10140
10142
|
var import_ali_oss = __toESM(require("ali-oss"));
|
|
10141
10143
|
var import_node_crypto = require("crypto");
|
|
10142
10144
|
var import_node_fs3 = require("fs");
|
|
10143
10145
|
var import_node_path4 = __toESM(require("path"));
|
|
10144
10146
|
var AliyunService = class {
|
|
10147
|
+
static async getAccountId() {
|
|
10148
|
+
const client = this.createStsClient();
|
|
10149
|
+
const response = await client.getCallerIdentity();
|
|
10150
|
+
const accountId = response.body?.accountId?.trim();
|
|
10151
|
+
if (!accountId) {
|
|
10152
|
+
throw new Error("Aliyun STS GetCallerIdentity did not return AccountId.");
|
|
10153
|
+
}
|
|
10154
|
+
return accountId;
|
|
10155
|
+
}
|
|
10156
|
+
static async getFcFunction(functionName, options) {
|
|
10157
|
+
const client = await this.createFcClient(options.region);
|
|
10158
|
+
const response = await client.getFunction(
|
|
10159
|
+
functionName,
|
|
10160
|
+
new import_fc20230330.GetFunctionRequest({
|
|
10161
|
+
...options.qualifier ? { qualifier: options.qualifier } : {}
|
|
10162
|
+
})
|
|
10163
|
+
);
|
|
10164
|
+
return {
|
|
10165
|
+
functionName: response.body?.functionName,
|
|
10166
|
+
runtime: response.body?.runtime,
|
|
10167
|
+
state: response.body?.state,
|
|
10168
|
+
lastModifiedTime: response.body?.lastModifiedTime
|
|
10169
|
+
};
|
|
10170
|
+
}
|
|
10171
|
+
static async ensureFcCustomDomain(input) {
|
|
10172
|
+
const client = await this.createFcClient(input.region);
|
|
10173
|
+
const route = this.buildFcPathConfig(input);
|
|
10174
|
+
try {
|
|
10175
|
+
const response = await client.getCustomDomain(input.domainName);
|
|
10176
|
+
const existing = response.body;
|
|
10177
|
+
const routes = existing?.routeConfig?.routes ?? [];
|
|
10178
|
+
const routeIndex = routes.findIndex((item) => item?.path === route.path);
|
|
10179
|
+
if (routeIndex >= 0) {
|
|
10180
|
+
const current = routes[routeIndex];
|
|
10181
|
+
if (current?.functionName === route.functionName && (current?.qualifier || "LATEST") === route.qualifier && !input.certConfig && (existing?.protocol || "HTTP") === (input.protocol ?? existing?.protocol ?? "HTTP")) {
|
|
10182
|
+
ui.success(`FC custom domain route already exists: ${input.domainName}${route.path}`);
|
|
10183
|
+
return;
|
|
10184
|
+
}
|
|
10185
|
+
routes[routeIndex] = route;
|
|
10186
|
+
} else {
|
|
10187
|
+
routes.push(route);
|
|
10188
|
+
}
|
|
10189
|
+
await client.updateCustomDomain(
|
|
10190
|
+
input.domainName,
|
|
10191
|
+
new import_fc20230330.UpdateCustomDomainRequest({
|
|
10192
|
+
body: new import_fc20230330.UpdateCustomDomainInput({
|
|
10193
|
+
protocol: input.protocol ?? existing?.protocol ?? "HTTP",
|
|
10194
|
+
...input.certConfig ? {
|
|
10195
|
+
certConfig: new import_fc20230330.CertConfig(input.certConfig)
|
|
10196
|
+
} : {},
|
|
10197
|
+
routeConfig: new import_fc20230330.RouteConfig({ routes })
|
|
10198
|
+
})
|
|
10199
|
+
})
|
|
10200
|
+
);
|
|
10201
|
+
ui.success(`Updated FC custom domain route: ${input.domainName}${route.path}`);
|
|
10202
|
+
return;
|
|
10203
|
+
} catch (error) {
|
|
10204
|
+
if (!this.isNotFoundError(error)) {
|
|
10205
|
+
throw error;
|
|
10206
|
+
}
|
|
10207
|
+
}
|
|
10208
|
+
await client.createCustomDomain(
|
|
10209
|
+
new import_fc20230330.CreateCustomDomainRequest({
|
|
10210
|
+
body: new import_fc20230330.CreateCustomDomainInput({
|
|
10211
|
+
domainName: input.domainName,
|
|
10212
|
+
protocol: input.protocol ?? (input.certConfig ? "HTTP,HTTPS" : "HTTP"),
|
|
10213
|
+
...input.certConfig ? {
|
|
10214
|
+
certConfig: new import_fc20230330.CertConfig(input.certConfig)
|
|
10215
|
+
} : {},
|
|
10216
|
+
authConfig: new import_fc20230330.AuthConfig({ authType: "anonymous", authInfo: "{}" }),
|
|
10217
|
+
routeConfig: new import_fc20230330.RouteConfig({ routes: [route] })
|
|
10218
|
+
})
|
|
10219
|
+
})
|
|
10220
|
+
);
|
|
10221
|
+
ui.success(`Created FC custom domain: ${input.domainName}`);
|
|
10222
|
+
}
|
|
10145
10223
|
/** Add a DNS record via Aliyun DNS. */
|
|
10146
10224
|
static async addRecord(input) {
|
|
10147
10225
|
const userConfig = loadPmateConfig();
|
|
@@ -10797,6 +10875,48 @@ var AliyunService = class {
|
|
|
10797
10875
|
return void 0;
|
|
10798
10876
|
}
|
|
10799
10877
|
}
|
|
10878
|
+
static buildFcPathConfig(input) {
|
|
10879
|
+
return new import_fc20230330.PathConfig({
|
|
10880
|
+
path: input.routePath ?? "/*",
|
|
10881
|
+
functionName: input.functionName,
|
|
10882
|
+
qualifier: input.qualifier ?? "LATEST",
|
|
10883
|
+
methods: ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"]
|
|
10884
|
+
});
|
|
10885
|
+
}
|
|
10886
|
+
static createStsClient() {
|
|
10887
|
+
const cfg = loadPmateConfig();
|
|
10888
|
+
const accessKeyId = cfg.aliyun?.ak?.trim();
|
|
10889
|
+
const accessKeySecret = cfg.aliyun?.sk?.trim();
|
|
10890
|
+
if (!accessKeyId) throw new Error("Missing aliyun.ak in ~/.pmate/config.yaml.");
|
|
10891
|
+
if (!accessKeySecret) throw new Error("Missing aliyun.sk in ~/.pmate/config.yaml.");
|
|
10892
|
+
return new import_sts20150401.default(
|
|
10893
|
+
new import_openapi_client.Config({
|
|
10894
|
+
accessKeyId,
|
|
10895
|
+
accessKeySecret,
|
|
10896
|
+
regionId: cfg.aliyun?.region?.trim() || "cn-hangzhou"
|
|
10897
|
+
})
|
|
10898
|
+
);
|
|
10899
|
+
}
|
|
10900
|
+
static async createFcClient(region) {
|
|
10901
|
+
const cfg = loadPmateConfig();
|
|
10902
|
+
const accessKeyId = cfg.aliyun?.ak?.trim();
|
|
10903
|
+
const accessKeySecret = cfg.aliyun?.sk?.trim();
|
|
10904
|
+
if (!accessKeyId) throw new Error("Missing aliyun.ak in ~/.pmate/config.yaml.");
|
|
10905
|
+
if (!accessKeySecret) throw new Error("Missing aliyun.sk in ~/.pmate/config.yaml.");
|
|
10906
|
+
const accountId = await this.getAccountId();
|
|
10907
|
+
return new import_fc20230330.default(
|
|
10908
|
+
new import_openapi_client.Config({
|
|
10909
|
+
accessKeyId,
|
|
10910
|
+
accessKeySecret,
|
|
10911
|
+
regionId: region,
|
|
10912
|
+
endpoint: `${accountId}.${region}.fc.aliyuncs.com`
|
|
10913
|
+
})
|
|
10914
|
+
);
|
|
10915
|
+
}
|
|
10916
|
+
static isNotFoundError(error) {
|
|
10917
|
+
const maybe = error;
|
|
10918
|
+
return maybe?.statusCode === 404 || maybe?.code === "DomainNameNotFound" || maybe?.code === "ResourceNotFound" || /not found|not exist/i.test(maybe?.message || "");
|
|
10919
|
+
}
|
|
10800
10920
|
};
|
|
10801
10921
|
|
|
10802
10922
|
// src/commands/AddDnsCommandHandler.ts
|
|
@@ -15932,10 +16052,12 @@ var CollectCommandHandler = class {
|
|
|
15932
16052
|
return best?.page ?? null;
|
|
15933
16053
|
}
|
|
15934
16054
|
async extractThreadsFromConversationData(page) {
|
|
15935
|
-
const result = await this.evaluateInPage(page, buildListThreadsExpr()).catch(
|
|
15936
|
-
|
|
15937
|
-
|
|
15938
|
-
|
|
16055
|
+
const result = await this.evaluateInPage(page, buildListThreadsExpr()).catch(
|
|
16056
|
+
(error) => ({
|
|
16057
|
+
ok: false,
|
|
16058
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
16059
|
+
})
|
|
16060
|
+
);
|
|
15939
16061
|
if (!result || !("ok" in result) || !result.ok || !Array.isArray(result.threads)) {
|
|
15940
16062
|
throw new Error(
|
|
15941
16063
|
result && "reason" in result && typeof result.reason === "string" ? result.reason : "unknown error"
|
|
@@ -15967,7 +16089,12 @@ var CollectCommandHandler = class {
|
|
|
15967
16089
|
const result = await this.evaluateInPage(
|
|
15968
16090
|
page,
|
|
15969
16091
|
buildGetMessagesByConversationExpr(thread.conversationId, limit)
|
|
15970
|
-
).catch(
|
|
16092
|
+
).catch(
|
|
16093
|
+
(error) => ({
|
|
16094
|
+
ok: false,
|
|
16095
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
16096
|
+
})
|
|
16097
|
+
);
|
|
15971
16098
|
if (!result || !("ok" in result) || !result.ok || !Array.isArray(result.messages)) {
|
|
15972
16099
|
return {
|
|
15973
16100
|
error: result && "reason" in result && typeof result.reason === "string" ? result.reason : "unknown error"
|
|
@@ -17945,6 +18072,159 @@ var import_node_path22 = __toESM(require("path"));
|
|
|
17945
18072
|
var import_node_child_process10 = require("child_process");
|
|
17946
18073
|
var import_node_readline = require("readline");
|
|
17947
18074
|
var import_chalk3 = __toESM(require("chalk"));
|
|
18075
|
+
|
|
18076
|
+
// src/services/ServiceRegistryClient.ts
|
|
18077
|
+
init_sessionStore();
|
|
18078
|
+
var ServiceRegistryNotFoundError = class extends Error {
|
|
18079
|
+
constructor(namespace, name) {
|
|
18080
|
+
super(`Service not found: ${namespace}/${name}`);
|
|
18081
|
+
this.name = "ServiceRegistryNotFoundError";
|
|
18082
|
+
}
|
|
18083
|
+
};
|
|
18084
|
+
var ServiceRegistryClient = class {
|
|
18085
|
+
constructor(baseUrl) {
|
|
18086
|
+
this.baseUrl = baseUrl;
|
|
18087
|
+
}
|
|
18088
|
+
async listServices(filters) {
|
|
18089
|
+
const searchParams = new URLSearchParams();
|
|
18090
|
+
const repository = normalizeRepository(filters?.repository);
|
|
18091
|
+
if (repository) {
|
|
18092
|
+
searchParams.set("repo", repository);
|
|
18093
|
+
}
|
|
18094
|
+
const server = filters?.server?.trim();
|
|
18095
|
+
if (server) {
|
|
18096
|
+
searchParams.set("server", server);
|
|
18097
|
+
}
|
|
18098
|
+
const payload = await this.request(
|
|
18099
|
+
"GET",
|
|
18100
|
+
`/services${searchParams.size > 0 ? `?${searchParams.toString()}` : ""}`
|
|
18101
|
+
);
|
|
18102
|
+
const namespace = filters?.namespace?.trim();
|
|
18103
|
+
return payload.items.filter((item) => {
|
|
18104
|
+
if (namespace && item.namespace !== namespace) {
|
|
18105
|
+
return false;
|
|
18106
|
+
}
|
|
18107
|
+
return true;
|
|
18108
|
+
});
|
|
18109
|
+
}
|
|
18110
|
+
async getService(namespace, name) {
|
|
18111
|
+
return this.request(
|
|
18112
|
+
"GET",
|
|
18113
|
+
`/services/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`
|
|
18114
|
+
);
|
|
18115
|
+
}
|
|
18116
|
+
async updateService(namespace, name, payload) {
|
|
18117
|
+
return this.requestWithBody(
|
|
18118
|
+
"PUT",
|
|
18119
|
+
`/services/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`,
|
|
18120
|
+
payload
|
|
18121
|
+
);
|
|
18122
|
+
}
|
|
18123
|
+
async listEnvItems(appId, options = {}) {
|
|
18124
|
+
const params = new URLSearchParams();
|
|
18125
|
+
if (options.scope) {
|
|
18126
|
+
params.set("scope", options.scope);
|
|
18127
|
+
}
|
|
18128
|
+
if (options.includeSecretMeta) {
|
|
18129
|
+
params.set("includeSecretMeta", "true");
|
|
18130
|
+
}
|
|
18131
|
+
const payload = await this.request(
|
|
18132
|
+
"GET",
|
|
18133
|
+
`/apps/by-id/${encodeURIComponent(appId)}/env${params.size > 0 ? `?${params.toString()}` : ""}`
|
|
18134
|
+
);
|
|
18135
|
+
return payload.items;
|
|
18136
|
+
}
|
|
18137
|
+
async resolveEnv(appId, options) {
|
|
18138
|
+
return this.requestWithBody(
|
|
18139
|
+
"POST",
|
|
18140
|
+
`/apps/by-id/${encodeURIComponent(appId)}/env/resolve`,
|
|
18141
|
+
{
|
|
18142
|
+
stage: options.stage,
|
|
18143
|
+
...options.branch ? { branch: options.branch } : {},
|
|
18144
|
+
...options.includeSecretValue ? { includeSecretValue: true } : {}
|
|
18145
|
+
}
|
|
18146
|
+
);
|
|
18147
|
+
}
|
|
18148
|
+
resolveBaseUrl() {
|
|
18149
|
+
return (this.baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
|
|
18150
|
+
}
|
|
18151
|
+
readToken() {
|
|
18152
|
+
return readRequiredSession({
|
|
18153
|
+
preferredApp: DEFAULT_APP_ID,
|
|
18154
|
+
fallbackToActiveApp: true
|
|
18155
|
+
}).token;
|
|
18156
|
+
}
|
|
18157
|
+
async request(method, pathname) {
|
|
18158
|
+
const response = await fetch(`${this.resolveBaseUrl()}${pathname}`, {
|
|
18159
|
+
method,
|
|
18160
|
+
headers: {
|
|
18161
|
+
Authorization: `Bearer ${this.readToken()}`,
|
|
18162
|
+
Accept: "application/json"
|
|
18163
|
+
}
|
|
18164
|
+
});
|
|
18165
|
+
let payload = null;
|
|
18166
|
+
try {
|
|
18167
|
+
payload = await response.json();
|
|
18168
|
+
} catch {
|
|
18169
|
+
payload = null;
|
|
18170
|
+
}
|
|
18171
|
+
if (response.status === 404) {
|
|
18172
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
18173
|
+
throw new ServiceRegistryNotFoundError(
|
|
18174
|
+
decodeURIComponent(segments[1] || "unknown"),
|
|
18175
|
+
decodeURIComponent(segments[2] || "unknown")
|
|
18176
|
+
);
|
|
18177
|
+
}
|
|
18178
|
+
if (!response.ok || !payload?.success || payload.data === void 0) {
|
|
18179
|
+
throw new Error(payload?.message || `Request failed with status ${response.status}`);
|
|
18180
|
+
}
|
|
18181
|
+
return payload.data;
|
|
18182
|
+
}
|
|
18183
|
+
async requestWithBody(method, pathname, body) {
|
|
18184
|
+
const response = await fetch(`${this.resolveBaseUrl()}${pathname}`, {
|
|
18185
|
+
method,
|
|
18186
|
+
headers: {
|
|
18187
|
+
Authorization: `Bearer ${this.readToken()}`,
|
|
18188
|
+
Accept: "application/json",
|
|
18189
|
+
"Content-Type": "application/json"
|
|
18190
|
+
},
|
|
18191
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
18192
|
+
});
|
|
18193
|
+
let payload = null;
|
|
18194
|
+
try {
|
|
18195
|
+
payload = await response.json();
|
|
18196
|
+
} catch {
|
|
18197
|
+
payload = null;
|
|
18198
|
+
}
|
|
18199
|
+
if (response.status === 404) {
|
|
18200
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
18201
|
+
throw new ServiceRegistryNotFoundError(
|
|
18202
|
+
decodeURIComponent(segments[1] || "unknown"),
|
|
18203
|
+
decodeURIComponent(segments[2] || "unknown")
|
|
18204
|
+
);
|
|
18205
|
+
}
|
|
18206
|
+
if (!response.ok || !payload?.success || payload.data === void 0) {
|
|
18207
|
+
throw new Error(payload?.message || `Request failed with status ${response.status}`);
|
|
18208
|
+
}
|
|
18209
|
+
return payload.data;
|
|
18210
|
+
}
|
|
18211
|
+
};
|
|
18212
|
+
function normalizeRepository(repository) {
|
|
18213
|
+
if (!repository) {
|
|
18214
|
+
return void 0;
|
|
18215
|
+
}
|
|
18216
|
+
const trimmed = repository.trim().replace(/\/+$/, "").replace(/\.git$/i, "");
|
|
18217
|
+
if (!trimmed) {
|
|
18218
|
+
return void 0;
|
|
18219
|
+
}
|
|
18220
|
+
const match = trimmed.match(/^https?:\/\/github\.com\/([^/]+\/[^/]+)$/i);
|
|
18221
|
+
if (match) {
|
|
18222
|
+
return match[1];
|
|
18223
|
+
}
|
|
18224
|
+
return trimmed.replace(/^\/+/, "");
|
|
18225
|
+
}
|
|
18226
|
+
|
|
18227
|
+
// src/services/ServiceRuntimeRunner.ts
|
|
17948
18228
|
var ServiceRuntimeRunner = class {
|
|
17949
18229
|
async deploy(service, options = {}) {
|
|
17950
18230
|
switch (this.getType(service)) {
|
|
@@ -17961,6 +18241,9 @@ var ServiceRuntimeRunner = class {
|
|
|
17961
18241
|
case "python":
|
|
17962
18242
|
await this.deployScriptManaged(service);
|
|
17963
18243
|
return;
|
|
18244
|
+
case "fc":
|
|
18245
|
+
await this.deployFc(service);
|
|
18246
|
+
return;
|
|
17964
18247
|
case "ios":
|
|
17965
18248
|
case "android":
|
|
17966
18249
|
throw new Error(
|
|
@@ -17983,6 +18266,9 @@ var ServiceRuntimeRunner = class {
|
|
|
17983
18266
|
case "python":
|
|
17984
18267
|
await this.initServerApp(service, options, { ensurePackageJson: false });
|
|
17985
18268
|
return;
|
|
18269
|
+
case "fc":
|
|
18270
|
+
await this.initFc(service, options);
|
|
18271
|
+
return;
|
|
17986
18272
|
case "ios":
|
|
17987
18273
|
case "android":
|
|
17988
18274
|
throw new Error(
|
|
@@ -18084,13 +18370,21 @@ var ServiceRuntimeRunner = class {
|
|
|
18084
18370
|
return service.network;
|
|
18085
18371
|
}
|
|
18086
18372
|
const deploy = this.isLegacy(service) ? service.deploy ?? {} : {};
|
|
18087
|
-
|
|
18373
|
+
if (deploy.port === void 0) {
|
|
18374
|
+
return {
|
|
18375
|
+
exposeType: "internal",
|
|
18376
|
+
repository: String(deploy.repository ?? ""),
|
|
18377
|
+
dir: String(deploy.dir ?? ""),
|
|
18378
|
+
server: String(deploy.server ?? "")
|
|
18379
|
+
};
|
|
18380
|
+
}
|
|
18381
|
+
const exposeType = deploy.serviceType === "ws" ? "websocket" : "rest";
|
|
18088
18382
|
return {
|
|
18089
18383
|
exposeType,
|
|
18090
18384
|
repository: String(deploy.repository ?? ""),
|
|
18091
18385
|
dir: String(deploy.dir ?? ""),
|
|
18092
18386
|
server: String(deploy.server ?? ""),
|
|
18093
|
-
|
|
18387
|
+
port: Number(deploy.port),
|
|
18094
18388
|
...deploy.domain ? { domain: deploy.domain } : {},
|
|
18095
18389
|
...deploy.subdomain ? { subdomain: deploy.subdomain } : {}
|
|
18096
18390
|
};
|
|
@@ -18104,17 +18398,28 @@ var ServiceRuntimeRunner = class {
|
|
|
18104
18398
|
script: String(deploy.script ?? "")
|
|
18105
18399
|
};
|
|
18106
18400
|
}
|
|
18401
|
+
getFc(service) {
|
|
18402
|
+
if (!this.isLegacy(service) && service.fc) {
|
|
18403
|
+
return service.fc;
|
|
18404
|
+
}
|
|
18405
|
+
throw new Error(`Missing fc config for ${service.id}`);
|
|
18406
|
+
}
|
|
18107
18407
|
resolveRepoRoot(service, cwd) {
|
|
18108
18408
|
if (cwd) {
|
|
18109
18409
|
return import_node_path22.default.resolve(process.cwd(), cwd);
|
|
18110
18410
|
}
|
|
18111
|
-
const repository = this.getType(service) === "vite" || this.getType(service) === "static" ? this.getSite(service).repository?.trim() : this.getType(service) === "ios" || this.getType(service) === "android" ? void 0 : this.getNetwork(service).repository?.trim();
|
|
18411
|
+
const repository = this.getType(service) === "vite" || this.getType(service) === "static" ? this.getSite(service).repository?.trim() : this.getType(service) === "fc" ? this.getFc(service).repository?.trim() : this.getType(service) === "ios" || this.getType(service) === "android" ? void 0 : this.getNetwork(service).repository?.trim();
|
|
18112
18412
|
if (repository) {
|
|
18113
18413
|
const repoName = repository.replace(/\/+$/, "").split("/").pop()?.replace(/\.git$/i, "");
|
|
18114
18414
|
if (repoName) {
|
|
18115
|
-
const
|
|
18116
|
-
|
|
18117
|
-
|
|
18415
|
+
const candidates = [
|
|
18416
|
+
import_node_path22.default.join(import_node_os8.default.homedir(), "dev", "pmate", repoName),
|
|
18417
|
+
import_node_path22.default.join(import_node_os8.default.homedir(), "dev", "pmate-envs", repoName)
|
|
18418
|
+
];
|
|
18419
|
+
for (const candidate of candidates) {
|
|
18420
|
+
if ((0, import_node_fs17.existsSync)(candidate)) {
|
|
18421
|
+
return candidate;
|
|
18422
|
+
}
|
|
18118
18423
|
}
|
|
18119
18424
|
}
|
|
18120
18425
|
}
|
|
@@ -18122,7 +18427,7 @@ var ServiceRuntimeRunner = class {
|
|
|
18122
18427
|
}
|
|
18123
18428
|
resolveLocalDir(service, cwd) {
|
|
18124
18429
|
const repoRoot = this.resolveRepoRoot(service, cwd);
|
|
18125
|
-
const dir = this.getType(service) === "vite" || this.getType(service) === "static" ? this.getSite(service).dir?.trim() : this.getType(service) === "ios" || this.getType(service) === "android" ? void 0 : this.getNetwork(service).dir?.trim();
|
|
18430
|
+
const dir = this.getType(service) === "vite" || this.getType(service) === "static" ? this.getSite(service).dir?.trim() : this.getType(service) === "fc" ? this.getFc(service).dir?.trim() : this.getType(service) === "ios" || this.getType(service) === "android" ? void 0 : this.getNetwork(service).dir?.trim();
|
|
18126
18431
|
if (!dir) {
|
|
18127
18432
|
throw new Error(`Missing service dir for ${service.id}`);
|
|
18128
18433
|
}
|
|
@@ -18164,6 +18469,122 @@ var ServiceRuntimeRunner = class {
|
|
|
18164
18469
|
}
|
|
18165
18470
|
await AliyunService.uploadDirectory(srcDir, bucketName);
|
|
18166
18471
|
}
|
|
18472
|
+
async deployFc(service) {
|
|
18473
|
+
const fc = this.getFc(service);
|
|
18474
|
+
this.validateFcConfig(service, fc);
|
|
18475
|
+
const repoRoot = this.resolveRepoRoot(service);
|
|
18476
|
+
const appDir = this.resolveLocalDir(service);
|
|
18477
|
+
const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
|
|
18478
|
+
if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
|
|
18479
|
+
throw new Error(`No package.json found at ${packageJsonPath}`);
|
|
18480
|
+
}
|
|
18481
|
+
const pkg2 = this.loadPackageJson(packageJsonPath);
|
|
18482
|
+
const pkgName = pkg2.name?.trim();
|
|
18483
|
+
if (!pkgName) {
|
|
18484
|
+
throw new Error(`Missing package name in ${packageJsonPath}`);
|
|
18485
|
+
}
|
|
18486
|
+
await this.ensurePnpmWorkspaceRoot(repoRoot);
|
|
18487
|
+
await this.runCommand("pnpm", ["--filter", pkgName, "build"], repoRoot);
|
|
18488
|
+
const registry = new ServiceRegistryClient();
|
|
18489
|
+
const appId = `${service.namespace}:${service.name}`;
|
|
18490
|
+
const stage = fc.deployStage ?? "prod";
|
|
18491
|
+
const requiredEnv = await this.syncFcRequiredEnv(service, registry, stage);
|
|
18492
|
+
const resolved = await registry.resolveEnv(appId, {
|
|
18493
|
+
stage,
|
|
18494
|
+
includeSecretValue: true
|
|
18495
|
+
});
|
|
18496
|
+
const missing = requiredEnv.filter((key) => !this.hasResolvedEnvValue(resolved.values, key));
|
|
18497
|
+
if (missing.length > 0) {
|
|
18498
|
+
throw new Error(`Missing resolved env for ${service.id}: ${missing.join(", ")}`);
|
|
18499
|
+
}
|
|
18500
|
+
const deployEnv = {
|
|
18501
|
+
...resolved.values,
|
|
18502
|
+
...await this.resolveAliyunDeploymentEnv(),
|
|
18503
|
+
...this.resolveFcNoProxyEnv()
|
|
18504
|
+
};
|
|
18505
|
+
const deployCommand = fc.deployCommand?.trim();
|
|
18506
|
+
if (deployCommand) {
|
|
18507
|
+
await this.runShellCommand(deployCommand, repoRoot, deployEnv);
|
|
18508
|
+
} else if (pkg2.scripts?.release) {
|
|
18509
|
+
await this.runCommandWithEnv("pnpm", ["--filter", pkgName, "release"], repoRoot, deployEnv);
|
|
18510
|
+
} else {
|
|
18511
|
+
await this.runCommandWithEnv("pnpm", ["release"], appDir, deployEnv);
|
|
18512
|
+
}
|
|
18513
|
+
const functionInfo = await AliyunService.getFcFunction(fc.functionName, {
|
|
18514
|
+
region: fc.region,
|
|
18515
|
+
qualifier: fc.qualifier
|
|
18516
|
+
});
|
|
18517
|
+
ui.success(`Deployed FC function ${fc.functionName}`, {
|
|
18518
|
+
region: fc.region,
|
|
18519
|
+
runtime: functionInfo.runtime,
|
|
18520
|
+
state: functionInfo.state,
|
|
18521
|
+
lastModifiedTime: functionInfo.lastModifiedTime,
|
|
18522
|
+
url: this.buildFcUrl(fc)
|
|
18523
|
+
});
|
|
18524
|
+
}
|
|
18525
|
+
async initFc(service, options) {
|
|
18526
|
+
const fc = this.getFc(service);
|
|
18527
|
+
this.validateFcConfig(service, fc);
|
|
18528
|
+
const repoRoot = this.resolveRepoRoot(service, options.cwd);
|
|
18529
|
+
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
18530
|
+
await this.ensurePnpmWorkspaceRoot(repoRoot);
|
|
18531
|
+
const configPath = import_node_path22.default.join(appDir, fc.configFile ?? "s.yaml");
|
|
18532
|
+
if (!(0, import_node_fs17.existsSync)(configPath)) {
|
|
18533
|
+
throw new Error(`Missing FC config file at ${configPath}`);
|
|
18534
|
+
}
|
|
18535
|
+
const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
|
|
18536
|
+
if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
|
|
18537
|
+
throw new Error(`No package.json found at ${packageJsonPath}`);
|
|
18538
|
+
}
|
|
18539
|
+
const rawConfig = (0, import_node_fs17.readFileSync)(configPath, "utf8");
|
|
18540
|
+
if (!rawConfig.includes(fc.functionName)) {
|
|
18541
|
+
throw new Error(`FC config ${configPath} does not mention functionName ${fc.functionName}`);
|
|
18542
|
+
}
|
|
18543
|
+
const accountId = await AliyunService.getAccountId();
|
|
18544
|
+
await AliyunService.getFcFunction(fc.functionName, {
|
|
18545
|
+
region: fc.region,
|
|
18546
|
+
qualifier: fc.qualifier
|
|
18547
|
+
});
|
|
18548
|
+
const host = this.buildFcHost(fc);
|
|
18549
|
+
if (host) {
|
|
18550
|
+
const cnameTarget = `${accountId}.${fc.region}.fc.aliyuncs.com`;
|
|
18551
|
+
const rr = fc.subdomain?.trim() || "@";
|
|
18552
|
+
const domain = fc.domain.trim();
|
|
18553
|
+
const existing = await AliyunService.getCnameRecordValue(domain, rr);
|
|
18554
|
+
if (existing) {
|
|
18555
|
+
if (this.normalizeDnsValue(existing) !== this.normalizeDnsValue(cnameTarget)) {
|
|
18556
|
+
throw new Error(
|
|
18557
|
+
`Existing CNAME for ${host} points to ${existing}, not ${cnameTarget}.`
|
|
18558
|
+
);
|
|
18559
|
+
}
|
|
18560
|
+
ui.success(`CNAME already exists: ${host} -> ${cnameTarget}`);
|
|
18561
|
+
} else {
|
|
18562
|
+
await AliyunService.addRecord({
|
|
18563
|
+
domainName: domain,
|
|
18564
|
+
rr,
|
|
18565
|
+
type: "CNAME",
|
|
18566
|
+
value: cnameTarget,
|
|
18567
|
+
ttl: 600
|
|
18568
|
+
});
|
|
18569
|
+
ui.success(`Added CNAME ${host} -> ${cnameTarget}`);
|
|
18570
|
+
}
|
|
18571
|
+
const certConfig = this.resolveFcCertConfig(host);
|
|
18572
|
+
await AliyunService.ensureFcCustomDomain({
|
|
18573
|
+
domainName: host,
|
|
18574
|
+
region: fc.region,
|
|
18575
|
+
functionName: fc.functionName,
|
|
18576
|
+
routePath: fc.routePath,
|
|
18577
|
+
protocol: this.resolveFcProtocol(fc, certConfig),
|
|
18578
|
+
qualifier: fc.qualifier,
|
|
18579
|
+
certConfig
|
|
18580
|
+
});
|
|
18581
|
+
}
|
|
18582
|
+
ui.success(`Initialized FC service ${service.id}`, {
|
|
18583
|
+
functionName: fc.functionName,
|
|
18584
|
+
region: fc.region,
|
|
18585
|
+
url: this.buildFcUrl(fc)
|
|
18586
|
+
});
|
|
18587
|
+
}
|
|
18167
18588
|
async deployNode(service) {
|
|
18168
18589
|
const network = this.getNetwork(service);
|
|
18169
18590
|
const server = network.server?.trim();
|
|
@@ -18828,9 +19249,185 @@ var ServiceRuntimeRunner = class {
|
|
|
18828
19249
|
(0, import_node_fs17.writeFileSync)(packageJsonPath, `${serialized}
|
|
18829
19250
|
`, "utf8");
|
|
18830
19251
|
}
|
|
19252
|
+
validateFcConfig(service, fc) {
|
|
19253
|
+
if (fc.provider !== "aliyun") {
|
|
19254
|
+
throw new Error(`Unsupported fc.provider for ${service.id}: ${fc.provider}`);
|
|
19255
|
+
}
|
|
19256
|
+
if (!fc.region?.trim()) {
|
|
19257
|
+
throw new Error(`Missing fc.region for ${service.id}`);
|
|
19258
|
+
}
|
|
19259
|
+
if (!fc.repository?.trim()) {
|
|
19260
|
+
throw new Error(`Missing fc.repository for ${service.id}`);
|
|
19261
|
+
}
|
|
19262
|
+
if (!fc.dir?.trim()) {
|
|
19263
|
+
throw new Error(`Missing fc.dir for ${service.id}`);
|
|
19264
|
+
}
|
|
19265
|
+
if (!fc.functionName?.trim()) {
|
|
19266
|
+
throw new Error(`Missing fc.functionName for ${service.id}`);
|
|
19267
|
+
}
|
|
19268
|
+
if (fc.domain && !fc.domain.trim()) {
|
|
19269
|
+
throw new Error(`Invalid fc.domain for ${service.id}`);
|
|
19270
|
+
}
|
|
19271
|
+
if (fc.subdomain && !fc.domain?.trim()) {
|
|
19272
|
+
throw new Error(`Missing fc.domain for ${service.id} (required when fc.subdomain is set)`);
|
|
19273
|
+
}
|
|
19274
|
+
}
|
|
19275
|
+
async ensurePnpmWorkspaceRoot(repoRoot) {
|
|
19276
|
+
const workspacePath = import_node_path22.default.join(repoRoot, "pnpm-workspace.yaml");
|
|
19277
|
+
if (!(0, import_node_fs17.existsSync)(workspacePath)) {
|
|
19278
|
+
throw new Error(`Missing pnpm-workspace.yaml at ${workspacePath}`);
|
|
19279
|
+
}
|
|
19280
|
+
}
|
|
19281
|
+
async syncFcRequiredEnv(service, registry, stage) {
|
|
19282
|
+
if (this.isLegacy(service)) {
|
|
19283
|
+
return [];
|
|
19284
|
+
}
|
|
19285
|
+
const appId = `${service.namespace}:${service.name}`;
|
|
19286
|
+
const items = await registry.listEnvItems(appId, {
|
|
19287
|
+
scope: stage,
|
|
19288
|
+
includeSecretMeta: true
|
|
19289
|
+
});
|
|
19290
|
+
const requiredEnv = Array.from(
|
|
19291
|
+
new Set(
|
|
19292
|
+
items.map((item) => item.key ?? item.name).filter((key) => typeof key === "string" && key.trim().length > 0).map((key) => key.trim())
|
|
19293
|
+
)
|
|
19294
|
+
).sort();
|
|
19295
|
+
const current = service.fc?.requiredEnv ?? [];
|
|
19296
|
+
if (this.sameStringSet(current, requiredEnv)) {
|
|
19297
|
+
return requiredEnv;
|
|
19298
|
+
}
|
|
19299
|
+
service.fc = {
|
|
19300
|
+
...service.fc,
|
|
19301
|
+
requiredEnv
|
|
19302
|
+
};
|
|
19303
|
+
await registry.updateService(service.namespace, service.name, service);
|
|
19304
|
+
ui.success(`Synced fc.requiredEnv for ${service.id}`, requiredEnv);
|
|
19305
|
+
return requiredEnv;
|
|
19306
|
+
}
|
|
19307
|
+
sameStringSet(left, right) {
|
|
19308
|
+
if (left.length !== right.length) {
|
|
19309
|
+
return false;
|
|
19310
|
+
}
|
|
19311
|
+
const normalizedLeft = [...left].sort();
|
|
19312
|
+
const normalizedRight = [...right].sort();
|
|
19313
|
+
return normalizedLeft.every((value, index) => value === normalizedRight[index]);
|
|
19314
|
+
}
|
|
19315
|
+
hasResolvedEnvValue(values, key) {
|
|
19316
|
+
return Object.prototype.hasOwnProperty.call(values, key) && values[key] !== void 0 && values[key] !== "";
|
|
19317
|
+
}
|
|
19318
|
+
buildFcHost(fc) {
|
|
19319
|
+
const domain = fc.domain?.trim();
|
|
19320
|
+
if (!domain) {
|
|
19321
|
+
return void 0;
|
|
19322
|
+
}
|
|
19323
|
+
const subdomain = fc.subdomain?.trim();
|
|
19324
|
+
return subdomain ? `${subdomain}.${domain}` : domain;
|
|
19325
|
+
}
|
|
19326
|
+
buildFcUrl(fc) {
|
|
19327
|
+
const host = this.buildFcHost(fc);
|
|
19328
|
+
return host ? `https://${host}` : void 0;
|
|
19329
|
+
}
|
|
19330
|
+
normalizeDnsValue(value) {
|
|
19331
|
+
return value.trim().replace(/\.$/, "").toLowerCase();
|
|
19332
|
+
}
|
|
19333
|
+
resolveFcCertConfig(host) {
|
|
19334
|
+
const config = loadPmateConfig();
|
|
19335
|
+
const certificatePath = config.ssl?.pub?.trim();
|
|
19336
|
+
const privateKeyPath = config.ssl?.prv?.trim();
|
|
19337
|
+
if (!certificatePath && !privateKeyPath) {
|
|
19338
|
+
return void 0;
|
|
19339
|
+
}
|
|
19340
|
+
if (!certificatePath) {
|
|
19341
|
+
throw new Error("Missing ssl.pub in ~/.pmate/config.yaml.");
|
|
19342
|
+
}
|
|
19343
|
+
if (!privateKeyPath) {
|
|
19344
|
+
throw new Error("Missing ssl.prv in ~/.pmate/config.yaml.");
|
|
19345
|
+
}
|
|
19346
|
+
if (!(0, import_node_fs17.existsSync)(certificatePath)) {
|
|
19347
|
+
throw new Error(`SSL certificate file not found: ${certificatePath}`);
|
|
19348
|
+
}
|
|
19349
|
+
if (!(0, import_node_fs17.existsSync)(privateKeyPath)) {
|
|
19350
|
+
throw new Error(`SSL private key file not found: ${privateKeyPath}`);
|
|
19351
|
+
}
|
|
19352
|
+
return {
|
|
19353
|
+
certName: host,
|
|
19354
|
+
certificate: (0, import_node_fs17.readFileSync)(certificatePath, "utf8"),
|
|
19355
|
+
privateKey: (0, import_node_fs17.readFileSync)(privateKeyPath, "utf8")
|
|
19356
|
+
};
|
|
19357
|
+
}
|
|
19358
|
+
resolveFcProtocol(fc, certConfig) {
|
|
19359
|
+
if (!certConfig) {
|
|
19360
|
+
return fc.protocol;
|
|
19361
|
+
}
|
|
19362
|
+
return fc.protocol === "HTTPS" ? "HTTPS" : "HTTP,HTTPS";
|
|
19363
|
+
}
|
|
19364
|
+
async resolveAliyunDeploymentEnv() {
|
|
19365
|
+
const config = loadPmateConfig();
|
|
19366
|
+
const accessKeyId = config.aliyun?.ak?.trim();
|
|
19367
|
+
const accessKeySecret = config.aliyun?.sk?.trim();
|
|
19368
|
+
if (!accessKeyId) {
|
|
19369
|
+
throw new Error("Missing aliyun.ak in ~/.pmate/config.yaml.");
|
|
19370
|
+
}
|
|
19371
|
+
if (!accessKeySecret) {
|
|
19372
|
+
throw new Error("Missing aliyun.sk in ~/.pmate/config.yaml.");
|
|
19373
|
+
}
|
|
19374
|
+
return {
|
|
19375
|
+
AccessKeyID: accessKeyId,
|
|
19376
|
+
AccessKeySecret: accessKeySecret,
|
|
19377
|
+
AccountID: await AliyunService.getAccountId()
|
|
19378
|
+
};
|
|
19379
|
+
}
|
|
19380
|
+
resolveFcNoProxyEnv() {
|
|
19381
|
+
const noProxy = /* @__PURE__ */ new Set(
|
|
19382
|
+
[
|
|
19383
|
+
"localhost",
|
|
19384
|
+
"127.0.0.1",
|
|
19385
|
+
"api.devsapp.cn",
|
|
19386
|
+
"devsapp.cn",
|
|
19387
|
+
"aliyuncs.com"
|
|
19388
|
+
]
|
|
19389
|
+
);
|
|
19390
|
+
for (const value of [process.env.NO_PROXY, process.env.no_proxy]) {
|
|
19391
|
+
for (const item of value?.split(",") ?? []) {
|
|
19392
|
+
const trimmed = item.trim();
|
|
19393
|
+
if (trimmed) {
|
|
19394
|
+
noProxy.add(trimmed);
|
|
19395
|
+
}
|
|
19396
|
+
}
|
|
19397
|
+
}
|
|
19398
|
+
return {
|
|
19399
|
+
HTTP_PROXY: "",
|
|
19400
|
+
HTTPS_PROXY: "",
|
|
19401
|
+
ALL_PROXY: "",
|
|
19402
|
+
http_proxy: "",
|
|
19403
|
+
https_proxy: "",
|
|
19404
|
+
all_proxy: "",
|
|
19405
|
+
NO_PROXY: Array.from(noProxy).join(","),
|
|
19406
|
+
no_proxy: Array.from(noProxy).join(",")
|
|
19407
|
+
};
|
|
19408
|
+
}
|
|
18831
19409
|
runCommand(command, args, cwd) {
|
|
19410
|
+
return this.runCommandWithEnv(command, args, cwd);
|
|
19411
|
+
}
|
|
19412
|
+
runCommandWithEnv(command, args, cwd, env) {
|
|
19413
|
+
return new Promise((resolve, reject) => {
|
|
19414
|
+
const child = (0, import_node_child_process10.spawn)(command, args, {
|
|
19415
|
+
cwd,
|
|
19416
|
+
stdio: "inherit",
|
|
19417
|
+
env: { ...process.env, ...env ?? {} }
|
|
19418
|
+
});
|
|
19419
|
+
child.on("error", reject);
|
|
19420
|
+
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`${command} exited with code ${code}`)));
|
|
19421
|
+
});
|
|
19422
|
+
}
|
|
19423
|
+
runShellCommand(command, cwd, env) {
|
|
18832
19424
|
return new Promise((resolve, reject) => {
|
|
18833
|
-
const child = (0, import_node_child_process10.spawn)(command,
|
|
19425
|
+
const child = (0, import_node_child_process10.spawn)(command, [], {
|
|
19426
|
+
cwd,
|
|
19427
|
+
stdio: "inherit",
|
|
19428
|
+
shell: true,
|
|
19429
|
+
env: { ...process.env, ...env ?? {} }
|
|
19430
|
+
});
|
|
18834
19431
|
child.on("error", reject);
|
|
18835
19432
|
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`${command} exited with code ${code}`)));
|
|
18836
19433
|
});
|
|
@@ -18877,97 +19474,6 @@ var ServiceRuntimeRunner = class {
|
|
|
18877
19474
|
}
|
|
18878
19475
|
};
|
|
18879
19476
|
|
|
18880
|
-
// src/services/ServiceRegistryClient.ts
|
|
18881
|
-
init_sessionStore();
|
|
18882
|
-
var ServiceRegistryNotFoundError = class extends Error {
|
|
18883
|
-
constructor(namespace, name) {
|
|
18884
|
-
super(`Service not found: ${namespace}/${name}`);
|
|
18885
|
-
this.name = "ServiceRegistryNotFoundError";
|
|
18886
|
-
}
|
|
18887
|
-
};
|
|
18888
|
-
var ServiceRegistryClient = class {
|
|
18889
|
-
constructor(baseUrl) {
|
|
18890
|
-
this.baseUrl = baseUrl;
|
|
18891
|
-
}
|
|
18892
|
-
async listServices(filters) {
|
|
18893
|
-
const searchParams = new URLSearchParams();
|
|
18894
|
-
const repository = normalizeRepository(filters?.repository);
|
|
18895
|
-
if (repository) {
|
|
18896
|
-
searchParams.set("repo", repository);
|
|
18897
|
-
}
|
|
18898
|
-
const server = filters?.server?.trim();
|
|
18899
|
-
if (server) {
|
|
18900
|
-
searchParams.set("server", server);
|
|
18901
|
-
}
|
|
18902
|
-
const payload = await this.request(
|
|
18903
|
-
"GET",
|
|
18904
|
-
`/services${searchParams.size > 0 ? `?${searchParams.toString()}` : ""}`
|
|
18905
|
-
);
|
|
18906
|
-
const namespace = filters?.namespace?.trim();
|
|
18907
|
-
return payload.items.filter((item) => {
|
|
18908
|
-
if (namespace && item.namespace !== namespace) {
|
|
18909
|
-
return false;
|
|
18910
|
-
}
|
|
18911
|
-
return true;
|
|
18912
|
-
});
|
|
18913
|
-
}
|
|
18914
|
-
async getService(namespace, name) {
|
|
18915
|
-
return this.request(
|
|
18916
|
-
"GET",
|
|
18917
|
-
`/services/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`
|
|
18918
|
-
);
|
|
18919
|
-
}
|
|
18920
|
-
resolveBaseUrl() {
|
|
18921
|
-
return (this.baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
|
|
18922
|
-
}
|
|
18923
|
-
readToken() {
|
|
18924
|
-
return readRequiredSession({
|
|
18925
|
-
preferredApp: DEFAULT_APP_ID,
|
|
18926
|
-
fallbackToActiveApp: true
|
|
18927
|
-
}).token;
|
|
18928
|
-
}
|
|
18929
|
-
async request(method, pathname) {
|
|
18930
|
-
const response = await fetch(`${this.resolveBaseUrl()}${pathname}`, {
|
|
18931
|
-
method,
|
|
18932
|
-
headers: {
|
|
18933
|
-
Authorization: `Bearer ${this.readToken()}`,
|
|
18934
|
-
Accept: "application/json"
|
|
18935
|
-
}
|
|
18936
|
-
});
|
|
18937
|
-
let payload = null;
|
|
18938
|
-
try {
|
|
18939
|
-
payload = await response.json();
|
|
18940
|
-
} catch {
|
|
18941
|
-
payload = null;
|
|
18942
|
-
}
|
|
18943
|
-
if (response.status === 404) {
|
|
18944
|
-
const segments = pathname.split("/").filter(Boolean);
|
|
18945
|
-
throw new ServiceRegistryNotFoundError(
|
|
18946
|
-
decodeURIComponent(segments[1] || "unknown"),
|
|
18947
|
-
decodeURIComponent(segments[2] || "unknown")
|
|
18948
|
-
);
|
|
18949
|
-
}
|
|
18950
|
-
if (!response.ok || !payload?.success || payload.data === void 0) {
|
|
18951
|
-
throw new Error(payload?.message || `Request failed with status ${response.status}`);
|
|
18952
|
-
}
|
|
18953
|
-
return payload.data;
|
|
18954
|
-
}
|
|
18955
|
-
};
|
|
18956
|
-
function normalizeRepository(repository) {
|
|
18957
|
-
if (!repository) {
|
|
18958
|
-
return void 0;
|
|
18959
|
-
}
|
|
18960
|
-
const trimmed = repository.trim().replace(/\/+$/, "").replace(/\.git$/i, "");
|
|
18961
|
-
if (!trimmed) {
|
|
18962
|
-
return void 0;
|
|
18963
|
-
}
|
|
18964
|
-
const match = trimmed.match(/^https?:\/\/github\.com\/([^/]+\/[^/]+)$/i);
|
|
18965
|
-
if (match) {
|
|
18966
|
-
return match[1];
|
|
18967
|
-
}
|
|
18968
|
-
return trimmed.replace(/^\/+/, "");
|
|
18969
|
-
}
|
|
18970
|
-
|
|
18971
19477
|
// src/commands/ServiceCommandHandler.ts
|
|
18972
19478
|
init_namespacedRef();
|
|
18973
19479
|
var ServiceCommandHandler = class {
|
|
@@ -19122,6 +19628,7 @@ var ServiceCommandHandler = class {
|
|
|
19122
19628
|
}
|
|
19123
19629
|
);
|
|
19124
19630
|
ui.success(`Updated env ${args.key} for ${args.appId}`);
|
|
19631
|
+
await this.syncFcRequiredEnvFromServiceEnv(registry, args.appId, args.scope);
|
|
19125
19632
|
this.renderJson(payload, args.json);
|
|
19126
19633
|
return;
|
|
19127
19634
|
}
|
|
@@ -19134,6 +19641,7 @@ var ServiceCommandHandler = class {
|
|
|
19134
19641
|
}).toString()}`
|
|
19135
19642
|
);
|
|
19136
19643
|
ui.success(`Deleted env ${payload.key} (${payload.scope}) from ${args.appId}`);
|
|
19644
|
+
await this.syncFcRequiredEnvFromServiceEnv(registry, args.appId, args.scope);
|
|
19137
19645
|
this.renderJson(payload, args.json);
|
|
19138
19646
|
return;
|
|
19139
19647
|
}
|
|
@@ -19319,6 +19827,48 @@ var ServiceCommandHandler = class {
|
|
|
19319
19827
|
);
|
|
19320
19828
|
}
|
|
19321
19829
|
}
|
|
19830
|
+
async syncFcRequiredEnvFromServiceEnv(registry, appId, scope) {
|
|
19831
|
+
const [namespace, name, extra] = appId.split(":");
|
|
19832
|
+
if (!namespace || !name || extra !== void 0) {
|
|
19833
|
+
return;
|
|
19834
|
+
}
|
|
19835
|
+
let service;
|
|
19836
|
+
try {
|
|
19837
|
+
service = await registry.getService(namespace, name);
|
|
19838
|
+
} catch (error) {
|
|
19839
|
+
if (error instanceof ServiceRegistryNotFoundError) {
|
|
19840
|
+
return;
|
|
19841
|
+
}
|
|
19842
|
+
throw error;
|
|
19843
|
+
}
|
|
19844
|
+
if (!("type" in service) || service.type !== "fc" || !service.fc) {
|
|
19845
|
+
return;
|
|
19846
|
+
}
|
|
19847
|
+
const items = await registry.listEnvItems(appId, {
|
|
19848
|
+
scope,
|
|
19849
|
+
includeSecretMeta: true
|
|
19850
|
+
});
|
|
19851
|
+
const requiredEnv = Array.from(
|
|
19852
|
+
new Set(
|
|
19853
|
+
items.map((item) => item.key ?? item.name).filter((key) => typeof key === "string" && key.trim().length > 0).map((key) => key.trim())
|
|
19854
|
+
)
|
|
19855
|
+
).sort();
|
|
19856
|
+
const current = service.fc.requiredEnv ?? [];
|
|
19857
|
+
if (this.sameStringSet(current, requiredEnv)) {
|
|
19858
|
+
return;
|
|
19859
|
+
}
|
|
19860
|
+
service.fc = { ...service.fc, requiredEnv };
|
|
19861
|
+
await registry.updateService(namespace, name, service);
|
|
19862
|
+
ui.success(`Synced fc.requiredEnv for ${appId}`);
|
|
19863
|
+
}
|
|
19864
|
+
sameStringSet(left, right) {
|
|
19865
|
+
if (left.length !== right.length) {
|
|
19866
|
+
return false;
|
|
19867
|
+
}
|
|
19868
|
+
const normalizedLeft = [...left].sort();
|
|
19869
|
+
const normalizedRight = [...right].sort();
|
|
19870
|
+
return normalizedLeft.every((value, index) => value === normalizedRight[index]);
|
|
19871
|
+
}
|
|
19322
19872
|
renderList(items, asJson) {
|
|
19323
19873
|
if (asJson) {
|
|
19324
19874
|
ui.plain(JSON.stringify(items, null, 2));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pmate/cli",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.3",
|
|
4
4
|
"description": "CLI utilities for the pMate project",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@alicloud/alidns20150109": "^4.0.0",
|
|
38
38
|
"@alicloud/cdn20180510": "^8.0.0",
|
|
39
|
+
"@alicloud/fc20230330": "^4.7.4",
|
|
39
40
|
"@alicloud/openapi-client": "^0.4.15",
|
|
41
|
+
"@alicloud/sts20150401": "^1.2.0",
|
|
40
42
|
"@linear/sdk": "^70.0.0",
|
|
41
43
|
"@types/ali-oss": "^6.16.13",
|
|
42
44
|
"ajv": "^8.17.1",
|