@pmate/cli 0.24.0 → 0.24.2
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 +618 -106
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -9633,9 +9633,10 @@ function loadRepoConfig(cwd) {
|
|
|
9633
9633
|
return normalizeRepoConfig(parsed, paths.configPath);
|
|
9634
9634
|
}
|
|
9635
9635
|
function loadAppConfig(cwd, options = {}) {
|
|
9636
|
-
const
|
|
9636
|
+
const searchParents = options.searchParents ?? true;
|
|
9637
|
+
const paths = findRepoConfigPaths(cwd, searchParents);
|
|
9637
9638
|
if (!paths) {
|
|
9638
|
-
const suffix =
|
|
9639
|
+
const suffix = searchParents ? " or parent directories" : "";
|
|
9639
9640
|
throw new Error(`No .pmate.yaml found in ${cwd}${suffix}`);
|
|
9640
9641
|
}
|
|
9641
9642
|
const parsed = loadMergedRepoYaml(paths);
|
|
@@ -10135,12 +10136,84 @@ var LoginCommandHandler = class {
|
|
|
10135
10136
|
// src/services/AliyunService.ts
|
|
10136
10137
|
var import_alidns20150109 = __toESM(require("@alicloud/alidns20150109"));
|
|
10137
10138
|
var import_cdn20180510 = __toESM(require("@alicloud/cdn20180510"));
|
|
10139
|
+
var import_fc20230330 = __toESM(require("@alicloud/fc20230330"));
|
|
10138
10140
|
var import_openapi_client = require("@alicloud/openapi-client");
|
|
10141
|
+
var import_sts20150401 = __toESM(require("@alicloud/sts20150401"));
|
|
10139
10142
|
var import_ali_oss = __toESM(require("ali-oss"));
|
|
10140
10143
|
var import_node_crypto = require("crypto");
|
|
10141
10144
|
var import_node_fs3 = require("fs");
|
|
10142
10145
|
var import_node_path4 = __toESM(require("path"));
|
|
10143
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) {
|
|
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
|
+
routeConfig: new import_fc20230330.RouteConfig({ routes })
|
|
10195
|
+
})
|
|
10196
|
+
})
|
|
10197
|
+
);
|
|
10198
|
+
ui.success(`Updated FC custom domain route: ${input.domainName}${route.path}`);
|
|
10199
|
+
return;
|
|
10200
|
+
} catch (error) {
|
|
10201
|
+
if (!this.isNotFoundError(error)) {
|
|
10202
|
+
throw error;
|
|
10203
|
+
}
|
|
10204
|
+
}
|
|
10205
|
+
await client.createCustomDomain(
|
|
10206
|
+
new import_fc20230330.CreateCustomDomainRequest({
|
|
10207
|
+
body: new import_fc20230330.CreateCustomDomainInput({
|
|
10208
|
+
domainName: input.domainName,
|
|
10209
|
+
protocol: input.protocol ?? "HTTP",
|
|
10210
|
+
authConfig: new import_fc20230330.AuthConfig({ authType: "anonymous", authInfo: "{}" }),
|
|
10211
|
+
routeConfig: new import_fc20230330.RouteConfig({ routes: [route] })
|
|
10212
|
+
})
|
|
10213
|
+
})
|
|
10214
|
+
);
|
|
10215
|
+
ui.success(`Created FC custom domain: ${input.domainName}`);
|
|
10216
|
+
}
|
|
10144
10217
|
/** Add a DNS record via Aliyun DNS. */
|
|
10145
10218
|
static async addRecord(input) {
|
|
10146
10219
|
const userConfig = loadPmateConfig();
|
|
@@ -10796,6 +10869,48 @@ var AliyunService = class {
|
|
|
10796
10869
|
return void 0;
|
|
10797
10870
|
}
|
|
10798
10871
|
}
|
|
10872
|
+
static buildFcPathConfig(input) {
|
|
10873
|
+
return new import_fc20230330.PathConfig({
|
|
10874
|
+
path: input.routePath ?? "/*",
|
|
10875
|
+
functionName: input.functionName,
|
|
10876
|
+
qualifier: input.qualifier ?? "LATEST",
|
|
10877
|
+
methods: ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"]
|
|
10878
|
+
});
|
|
10879
|
+
}
|
|
10880
|
+
static createStsClient() {
|
|
10881
|
+
const cfg = loadPmateConfig();
|
|
10882
|
+
const accessKeyId = cfg.aliyun?.ak?.trim();
|
|
10883
|
+
const accessKeySecret = cfg.aliyun?.sk?.trim();
|
|
10884
|
+
if (!accessKeyId) throw new Error("Missing aliyun.ak in ~/.pmate/config.yaml.");
|
|
10885
|
+
if (!accessKeySecret) throw new Error("Missing aliyun.sk in ~/.pmate/config.yaml.");
|
|
10886
|
+
return new import_sts20150401.default(
|
|
10887
|
+
new import_openapi_client.Config({
|
|
10888
|
+
accessKeyId,
|
|
10889
|
+
accessKeySecret,
|
|
10890
|
+
regionId: cfg.aliyun?.region?.trim() || "cn-hangzhou"
|
|
10891
|
+
})
|
|
10892
|
+
);
|
|
10893
|
+
}
|
|
10894
|
+
static async createFcClient(region) {
|
|
10895
|
+
const cfg = loadPmateConfig();
|
|
10896
|
+
const accessKeyId = cfg.aliyun?.ak?.trim();
|
|
10897
|
+
const accessKeySecret = cfg.aliyun?.sk?.trim();
|
|
10898
|
+
if (!accessKeyId) throw new Error("Missing aliyun.ak in ~/.pmate/config.yaml.");
|
|
10899
|
+
if (!accessKeySecret) throw new Error("Missing aliyun.sk in ~/.pmate/config.yaml.");
|
|
10900
|
+
const accountId = await this.getAccountId();
|
|
10901
|
+
return new import_fc20230330.default(
|
|
10902
|
+
new import_openapi_client.Config({
|
|
10903
|
+
accessKeyId,
|
|
10904
|
+
accessKeySecret,
|
|
10905
|
+
regionId: region,
|
|
10906
|
+
endpoint: `${accountId}.${region}.fc.aliyuncs.com`
|
|
10907
|
+
})
|
|
10908
|
+
);
|
|
10909
|
+
}
|
|
10910
|
+
static isNotFoundError(error) {
|
|
10911
|
+
const maybe = error;
|
|
10912
|
+
return maybe?.statusCode === 404 || maybe?.code === "DomainNameNotFound" || maybe?.code === "ResourceNotFound" || /not found|not exist/i.test(maybe?.message || "");
|
|
10913
|
+
}
|
|
10799
10914
|
};
|
|
10800
10915
|
|
|
10801
10916
|
// src/commands/AddDnsCommandHandler.ts
|
|
@@ -15931,10 +16046,12 @@ var CollectCommandHandler = class {
|
|
|
15931
16046
|
return best?.page ?? null;
|
|
15932
16047
|
}
|
|
15933
16048
|
async extractThreadsFromConversationData(page) {
|
|
15934
|
-
const result = await this.evaluateInPage(page, buildListThreadsExpr()).catch(
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
16049
|
+
const result = await this.evaluateInPage(page, buildListThreadsExpr()).catch(
|
|
16050
|
+
(error) => ({
|
|
16051
|
+
ok: false,
|
|
16052
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
16053
|
+
})
|
|
16054
|
+
);
|
|
15938
16055
|
if (!result || !("ok" in result) || !result.ok || !Array.isArray(result.threads)) {
|
|
15939
16056
|
throw new Error(
|
|
15940
16057
|
result && "reason" in result && typeof result.reason === "string" ? result.reason : "unknown error"
|
|
@@ -15966,7 +16083,12 @@ var CollectCommandHandler = class {
|
|
|
15966
16083
|
const result = await this.evaluateInPage(
|
|
15967
16084
|
page,
|
|
15968
16085
|
buildGetMessagesByConversationExpr(thread.conversationId, limit)
|
|
15969
|
-
).catch(
|
|
16086
|
+
).catch(
|
|
16087
|
+
(error) => ({
|
|
16088
|
+
ok: false,
|
|
16089
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
16090
|
+
})
|
|
16091
|
+
);
|
|
15970
16092
|
if (!result || !("ok" in result) || !result.ok || !Array.isArray(result.messages)) {
|
|
15971
16093
|
return {
|
|
15972
16094
|
error: result && "reason" in result && typeof result.reason === "string" ? result.reason : "unknown error"
|
|
@@ -17944,6 +18066,159 @@ var import_node_path22 = __toESM(require("path"));
|
|
|
17944
18066
|
var import_node_child_process10 = require("child_process");
|
|
17945
18067
|
var import_node_readline = require("readline");
|
|
17946
18068
|
var import_chalk3 = __toESM(require("chalk"));
|
|
18069
|
+
|
|
18070
|
+
// src/services/ServiceRegistryClient.ts
|
|
18071
|
+
init_sessionStore();
|
|
18072
|
+
var ServiceRegistryNotFoundError = class extends Error {
|
|
18073
|
+
constructor(namespace, name) {
|
|
18074
|
+
super(`Service not found: ${namespace}/${name}`);
|
|
18075
|
+
this.name = "ServiceRegistryNotFoundError";
|
|
18076
|
+
}
|
|
18077
|
+
};
|
|
18078
|
+
var ServiceRegistryClient = class {
|
|
18079
|
+
constructor(baseUrl) {
|
|
18080
|
+
this.baseUrl = baseUrl;
|
|
18081
|
+
}
|
|
18082
|
+
async listServices(filters) {
|
|
18083
|
+
const searchParams = new URLSearchParams();
|
|
18084
|
+
const repository = normalizeRepository(filters?.repository);
|
|
18085
|
+
if (repository) {
|
|
18086
|
+
searchParams.set("repo", repository);
|
|
18087
|
+
}
|
|
18088
|
+
const server = filters?.server?.trim();
|
|
18089
|
+
if (server) {
|
|
18090
|
+
searchParams.set("server", server);
|
|
18091
|
+
}
|
|
18092
|
+
const payload = await this.request(
|
|
18093
|
+
"GET",
|
|
18094
|
+
`/services${searchParams.size > 0 ? `?${searchParams.toString()}` : ""}`
|
|
18095
|
+
);
|
|
18096
|
+
const namespace = filters?.namespace?.trim();
|
|
18097
|
+
return payload.items.filter((item) => {
|
|
18098
|
+
if (namespace && item.namespace !== namespace) {
|
|
18099
|
+
return false;
|
|
18100
|
+
}
|
|
18101
|
+
return true;
|
|
18102
|
+
});
|
|
18103
|
+
}
|
|
18104
|
+
async getService(namespace, name) {
|
|
18105
|
+
return this.request(
|
|
18106
|
+
"GET",
|
|
18107
|
+
`/services/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`
|
|
18108
|
+
);
|
|
18109
|
+
}
|
|
18110
|
+
async updateService(namespace, name, payload) {
|
|
18111
|
+
return this.requestWithBody(
|
|
18112
|
+
"PUT",
|
|
18113
|
+
`/services/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`,
|
|
18114
|
+
payload
|
|
18115
|
+
);
|
|
18116
|
+
}
|
|
18117
|
+
async listEnvItems(appId, options = {}) {
|
|
18118
|
+
const params = new URLSearchParams();
|
|
18119
|
+
if (options.scope) {
|
|
18120
|
+
params.set("scope", options.scope);
|
|
18121
|
+
}
|
|
18122
|
+
if (options.includeSecretMeta) {
|
|
18123
|
+
params.set("includeSecretMeta", "true");
|
|
18124
|
+
}
|
|
18125
|
+
const payload = await this.request(
|
|
18126
|
+
"GET",
|
|
18127
|
+
`/apps/by-id/${encodeURIComponent(appId)}/env${params.size > 0 ? `?${params.toString()}` : ""}`
|
|
18128
|
+
);
|
|
18129
|
+
return payload.items;
|
|
18130
|
+
}
|
|
18131
|
+
async resolveEnv(appId, options) {
|
|
18132
|
+
return this.requestWithBody(
|
|
18133
|
+
"POST",
|
|
18134
|
+
`/apps/by-id/${encodeURIComponent(appId)}/env/resolve`,
|
|
18135
|
+
{
|
|
18136
|
+
stage: options.stage,
|
|
18137
|
+
...options.branch ? { branch: options.branch } : {},
|
|
18138
|
+
...options.includeSecretValue ? { includeSecretValue: true } : {}
|
|
18139
|
+
}
|
|
18140
|
+
);
|
|
18141
|
+
}
|
|
18142
|
+
resolveBaseUrl() {
|
|
18143
|
+
return (this.baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
|
|
18144
|
+
}
|
|
18145
|
+
readToken() {
|
|
18146
|
+
return readRequiredSession({
|
|
18147
|
+
preferredApp: DEFAULT_APP_ID,
|
|
18148
|
+
fallbackToActiveApp: true
|
|
18149
|
+
}).token;
|
|
18150
|
+
}
|
|
18151
|
+
async request(method, pathname) {
|
|
18152
|
+
const response = await fetch(`${this.resolveBaseUrl()}${pathname}`, {
|
|
18153
|
+
method,
|
|
18154
|
+
headers: {
|
|
18155
|
+
Authorization: `Bearer ${this.readToken()}`,
|
|
18156
|
+
Accept: "application/json"
|
|
18157
|
+
}
|
|
18158
|
+
});
|
|
18159
|
+
let payload = null;
|
|
18160
|
+
try {
|
|
18161
|
+
payload = await response.json();
|
|
18162
|
+
} catch {
|
|
18163
|
+
payload = null;
|
|
18164
|
+
}
|
|
18165
|
+
if (response.status === 404) {
|
|
18166
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
18167
|
+
throw new ServiceRegistryNotFoundError(
|
|
18168
|
+
decodeURIComponent(segments[1] || "unknown"),
|
|
18169
|
+
decodeURIComponent(segments[2] || "unknown")
|
|
18170
|
+
);
|
|
18171
|
+
}
|
|
18172
|
+
if (!response.ok || !payload?.success || payload.data === void 0) {
|
|
18173
|
+
throw new Error(payload?.message || `Request failed with status ${response.status}`);
|
|
18174
|
+
}
|
|
18175
|
+
return payload.data;
|
|
18176
|
+
}
|
|
18177
|
+
async requestWithBody(method, pathname, body) {
|
|
18178
|
+
const response = await fetch(`${this.resolveBaseUrl()}${pathname}`, {
|
|
18179
|
+
method,
|
|
18180
|
+
headers: {
|
|
18181
|
+
Authorization: `Bearer ${this.readToken()}`,
|
|
18182
|
+
Accept: "application/json",
|
|
18183
|
+
"Content-Type": "application/json"
|
|
18184
|
+
},
|
|
18185
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
18186
|
+
});
|
|
18187
|
+
let payload = null;
|
|
18188
|
+
try {
|
|
18189
|
+
payload = await response.json();
|
|
18190
|
+
} catch {
|
|
18191
|
+
payload = null;
|
|
18192
|
+
}
|
|
18193
|
+
if (response.status === 404) {
|
|
18194
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
18195
|
+
throw new ServiceRegistryNotFoundError(
|
|
18196
|
+
decodeURIComponent(segments[1] || "unknown"),
|
|
18197
|
+
decodeURIComponent(segments[2] || "unknown")
|
|
18198
|
+
);
|
|
18199
|
+
}
|
|
18200
|
+
if (!response.ok || !payload?.success || payload.data === void 0) {
|
|
18201
|
+
throw new Error(payload?.message || `Request failed with status ${response.status}`);
|
|
18202
|
+
}
|
|
18203
|
+
return payload.data;
|
|
18204
|
+
}
|
|
18205
|
+
};
|
|
18206
|
+
function normalizeRepository(repository) {
|
|
18207
|
+
if (!repository) {
|
|
18208
|
+
return void 0;
|
|
18209
|
+
}
|
|
18210
|
+
const trimmed = repository.trim().replace(/\/+$/, "").replace(/\.git$/i, "");
|
|
18211
|
+
if (!trimmed) {
|
|
18212
|
+
return void 0;
|
|
18213
|
+
}
|
|
18214
|
+
const match = trimmed.match(/^https?:\/\/github\.com\/([^/]+\/[^/]+)$/i);
|
|
18215
|
+
if (match) {
|
|
18216
|
+
return match[1];
|
|
18217
|
+
}
|
|
18218
|
+
return trimmed.replace(/^\/+/, "");
|
|
18219
|
+
}
|
|
18220
|
+
|
|
18221
|
+
// src/services/ServiceRuntimeRunner.ts
|
|
17947
18222
|
var ServiceRuntimeRunner = class {
|
|
17948
18223
|
async deploy(service, options = {}) {
|
|
17949
18224
|
switch (this.getType(service)) {
|
|
@@ -17960,6 +18235,9 @@ var ServiceRuntimeRunner = class {
|
|
|
17960
18235
|
case "python":
|
|
17961
18236
|
await this.deployScriptManaged(service);
|
|
17962
18237
|
return;
|
|
18238
|
+
case "fc":
|
|
18239
|
+
await this.deployFc(service);
|
|
18240
|
+
return;
|
|
17963
18241
|
case "ios":
|
|
17964
18242
|
case "android":
|
|
17965
18243
|
throw new Error(
|
|
@@ -17982,6 +18260,9 @@ var ServiceRuntimeRunner = class {
|
|
|
17982
18260
|
case "python":
|
|
17983
18261
|
await this.initServerApp(service, options, { ensurePackageJson: false });
|
|
17984
18262
|
return;
|
|
18263
|
+
case "fc":
|
|
18264
|
+
await this.initFc(service, options);
|
|
18265
|
+
return;
|
|
17985
18266
|
case "ios":
|
|
17986
18267
|
case "android":
|
|
17987
18268
|
throw new Error(
|
|
@@ -18083,13 +18364,21 @@ var ServiceRuntimeRunner = class {
|
|
|
18083
18364
|
return service.network;
|
|
18084
18365
|
}
|
|
18085
18366
|
const deploy = this.isLegacy(service) ? service.deploy ?? {} : {};
|
|
18086
|
-
|
|
18367
|
+
if (deploy.port === void 0) {
|
|
18368
|
+
return {
|
|
18369
|
+
exposeType: "internal",
|
|
18370
|
+
repository: String(deploy.repository ?? ""),
|
|
18371
|
+
dir: String(deploy.dir ?? ""),
|
|
18372
|
+
server: String(deploy.server ?? "")
|
|
18373
|
+
};
|
|
18374
|
+
}
|
|
18375
|
+
const exposeType = deploy.serviceType === "ws" ? "websocket" : "rest";
|
|
18087
18376
|
return {
|
|
18088
18377
|
exposeType,
|
|
18089
18378
|
repository: String(deploy.repository ?? ""),
|
|
18090
18379
|
dir: String(deploy.dir ?? ""),
|
|
18091
18380
|
server: String(deploy.server ?? ""),
|
|
18092
|
-
|
|
18381
|
+
port: Number(deploy.port),
|
|
18093
18382
|
...deploy.domain ? { domain: deploy.domain } : {},
|
|
18094
18383
|
...deploy.subdomain ? { subdomain: deploy.subdomain } : {}
|
|
18095
18384
|
};
|
|
@@ -18103,17 +18392,28 @@ var ServiceRuntimeRunner = class {
|
|
|
18103
18392
|
script: String(deploy.script ?? "")
|
|
18104
18393
|
};
|
|
18105
18394
|
}
|
|
18395
|
+
getFc(service) {
|
|
18396
|
+
if (!this.isLegacy(service) && service.fc) {
|
|
18397
|
+
return service.fc;
|
|
18398
|
+
}
|
|
18399
|
+
throw new Error(`Missing fc config for ${service.id}`);
|
|
18400
|
+
}
|
|
18106
18401
|
resolveRepoRoot(service, cwd) {
|
|
18107
18402
|
if (cwd) {
|
|
18108
18403
|
return import_node_path22.default.resolve(process.cwd(), cwd);
|
|
18109
18404
|
}
|
|
18110
|
-
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();
|
|
18405
|
+
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();
|
|
18111
18406
|
if (repository) {
|
|
18112
18407
|
const repoName = repository.replace(/\/+$/, "").split("/").pop()?.replace(/\.git$/i, "");
|
|
18113
18408
|
if (repoName) {
|
|
18114
|
-
const
|
|
18115
|
-
|
|
18116
|
-
|
|
18409
|
+
const candidates = [
|
|
18410
|
+
import_node_path22.default.join(import_node_os8.default.homedir(), "dev", "pmate", repoName),
|
|
18411
|
+
import_node_path22.default.join(import_node_os8.default.homedir(), "dev", "pmate-envs", repoName)
|
|
18412
|
+
];
|
|
18413
|
+
for (const candidate of candidates) {
|
|
18414
|
+
if ((0, import_node_fs17.existsSync)(candidate)) {
|
|
18415
|
+
return candidate;
|
|
18416
|
+
}
|
|
18117
18417
|
}
|
|
18118
18418
|
}
|
|
18119
18419
|
}
|
|
@@ -18121,7 +18421,7 @@ var ServiceRuntimeRunner = class {
|
|
|
18121
18421
|
}
|
|
18122
18422
|
resolveLocalDir(service, cwd) {
|
|
18123
18423
|
const repoRoot = this.resolveRepoRoot(service, cwd);
|
|
18124
|
-
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();
|
|
18424
|
+
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();
|
|
18125
18425
|
if (!dir) {
|
|
18126
18426
|
throw new Error(`Missing service dir for ${service.id}`);
|
|
18127
18427
|
}
|
|
@@ -18163,6 +18463,120 @@ var ServiceRuntimeRunner = class {
|
|
|
18163
18463
|
}
|
|
18164
18464
|
await AliyunService.uploadDirectory(srcDir, bucketName);
|
|
18165
18465
|
}
|
|
18466
|
+
async deployFc(service) {
|
|
18467
|
+
const fc = this.getFc(service);
|
|
18468
|
+
this.validateFcConfig(service, fc);
|
|
18469
|
+
const repoRoot = this.resolveRepoRoot(service);
|
|
18470
|
+
const appDir = this.resolveLocalDir(service);
|
|
18471
|
+
const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
|
|
18472
|
+
if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
|
|
18473
|
+
throw new Error(`No package.json found at ${packageJsonPath}`);
|
|
18474
|
+
}
|
|
18475
|
+
const pkg2 = this.loadPackageJson(packageJsonPath);
|
|
18476
|
+
const pkgName = pkg2.name?.trim();
|
|
18477
|
+
if (!pkgName) {
|
|
18478
|
+
throw new Error(`Missing package name in ${packageJsonPath}`);
|
|
18479
|
+
}
|
|
18480
|
+
await this.ensurePnpmWorkspaceRoot(repoRoot);
|
|
18481
|
+
await this.runCommand("pnpm", ["--filter", pkgName, "build"], repoRoot);
|
|
18482
|
+
const registry = new ServiceRegistryClient();
|
|
18483
|
+
const appId = `${service.namespace}:${service.name}`;
|
|
18484
|
+
const stage = fc.deployStage ?? "prod";
|
|
18485
|
+
const requiredEnv = await this.syncFcRequiredEnv(service, registry, stage);
|
|
18486
|
+
const resolved = await registry.resolveEnv(appId, {
|
|
18487
|
+
stage,
|
|
18488
|
+
includeSecretValue: true
|
|
18489
|
+
});
|
|
18490
|
+
const missing = requiredEnv.filter((key) => !this.hasResolvedEnvValue(resolved.values, key));
|
|
18491
|
+
if (missing.length > 0) {
|
|
18492
|
+
throw new Error(`Missing resolved env for ${service.id}: ${missing.join(", ")}`);
|
|
18493
|
+
}
|
|
18494
|
+
const deployEnv = {
|
|
18495
|
+
...resolved.values,
|
|
18496
|
+
...await this.resolveAliyunDeploymentEnv(),
|
|
18497
|
+
...this.resolveFcNoProxyEnv()
|
|
18498
|
+
};
|
|
18499
|
+
const deployCommand = fc.deployCommand?.trim();
|
|
18500
|
+
if (deployCommand) {
|
|
18501
|
+
await this.runShellCommand(deployCommand, repoRoot, deployEnv);
|
|
18502
|
+
} else if (pkg2.scripts?.release) {
|
|
18503
|
+
await this.runCommandWithEnv("pnpm", ["--filter", pkgName, "release"], repoRoot, deployEnv);
|
|
18504
|
+
} else {
|
|
18505
|
+
await this.runCommandWithEnv("pnpm", ["release"], appDir, deployEnv);
|
|
18506
|
+
}
|
|
18507
|
+
const functionInfo = await AliyunService.getFcFunction(fc.functionName, {
|
|
18508
|
+
region: fc.region,
|
|
18509
|
+
qualifier: fc.qualifier
|
|
18510
|
+
});
|
|
18511
|
+
ui.success(`Deployed FC function ${fc.functionName}`, {
|
|
18512
|
+
region: fc.region,
|
|
18513
|
+
runtime: functionInfo.runtime,
|
|
18514
|
+
state: functionInfo.state,
|
|
18515
|
+
lastModifiedTime: functionInfo.lastModifiedTime,
|
|
18516
|
+
url: this.buildFcUrl(fc)
|
|
18517
|
+
});
|
|
18518
|
+
}
|
|
18519
|
+
async initFc(service, options) {
|
|
18520
|
+
const fc = this.getFc(service);
|
|
18521
|
+
this.validateFcConfig(service, fc);
|
|
18522
|
+
const repoRoot = this.resolveRepoRoot(service, options.cwd);
|
|
18523
|
+
const appDir = this.resolveLocalDir(service, options.cwd);
|
|
18524
|
+
await this.ensurePnpmWorkspaceRoot(repoRoot);
|
|
18525
|
+
const configPath = import_node_path22.default.join(appDir, fc.configFile ?? "s.yaml");
|
|
18526
|
+
if (!(0, import_node_fs17.existsSync)(configPath)) {
|
|
18527
|
+
throw new Error(`Missing FC config file at ${configPath}`);
|
|
18528
|
+
}
|
|
18529
|
+
const packageJsonPath = import_node_path22.default.join(appDir, "package.json");
|
|
18530
|
+
if (!(0, import_node_fs17.existsSync)(packageJsonPath)) {
|
|
18531
|
+
throw new Error(`No package.json found at ${packageJsonPath}`);
|
|
18532
|
+
}
|
|
18533
|
+
const rawConfig = (0, import_node_fs17.readFileSync)(configPath, "utf8");
|
|
18534
|
+
if (!rawConfig.includes(fc.functionName)) {
|
|
18535
|
+
throw new Error(`FC config ${configPath} does not mention functionName ${fc.functionName}`);
|
|
18536
|
+
}
|
|
18537
|
+
const accountId = await AliyunService.getAccountId();
|
|
18538
|
+
await AliyunService.getFcFunction(fc.functionName, {
|
|
18539
|
+
region: fc.region,
|
|
18540
|
+
qualifier: fc.qualifier
|
|
18541
|
+
});
|
|
18542
|
+
const host = this.buildFcHost(fc);
|
|
18543
|
+
if (host) {
|
|
18544
|
+
const cnameTarget = `${accountId}.${fc.region}.fc.aliyuncs.com`;
|
|
18545
|
+
const rr = fc.subdomain?.trim() || "@";
|
|
18546
|
+
const domain = fc.domain.trim();
|
|
18547
|
+
const existing = await AliyunService.getCnameRecordValue(domain, rr);
|
|
18548
|
+
if (existing) {
|
|
18549
|
+
if (this.normalizeDnsValue(existing) !== this.normalizeDnsValue(cnameTarget)) {
|
|
18550
|
+
throw new Error(
|
|
18551
|
+
`Existing CNAME for ${host} points to ${existing}, not ${cnameTarget}.`
|
|
18552
|
+
);
|
|
18553
|
+
}
|
|
18554
|
+
ui.success(`CNAME already exists: ${host} -> ${cnameTarget}`);
|
|
18555
|
+
} else {
|
|
18556
|
+
await AliyunService.addRecord({
|
|
18557
|
+
domainName: domain,
|
|
18558
|
+
rr,
|
|
18559
|
+
type: "CNAME",
|
|
18560
|
+
value: cnameTarget,
|
|
18561
|
+
ttl: 600
|
|
18562
|
+
});
|
|
18563
|
+
ui.success(`Added CNAME ${host} -> ${cnameTarget}`);
|
|
18564
|
+
}
|
|
18565
|
+
await AliyunService.ensureFcCustomDomain({
|
|
18566
|
+
domainName: host,
|
|
18567
|
+
region: fc.region,
|
|
18568
|
+
functionName: fc.functionName,
|
|
18569
|
+
routePath: fc.routePath,
|
|
18570
|
+
protocol: fc.protocol,
|
|
18571
|
+
qualifier: fc.qualifier
|
|
18572
|
+
});
|
|
18573
|
+
}
|
|
18574
|
+
ui.success(`Initialized FC service ${service.id}`, {
|
|
18575
|
+
functionName: fc.functionName,
|
|
18576
|
+
region: fc.region,
|
|
18577
|
+
url: this.buildFcUrl(fc)
|
|
18578
|
+
});
|
|
18579
|
+
}
|
|
18166
18580
|
async deployNode(service) {
|
|
18167
18581
|
const network = this.getNetwork(service);
|
|
18168
18582
|
const server = network.server?.trim();
|
|
@@ -18827,9 +19241,154 @@ var ServiceRuntimeRunner = class {
|
|
|
18827
19241
|
(0, import_node_fs17.writeFileSync)(packageJsonPath, `${serialized}
|
|
18828
19242
|
`, "utf8");
|
|
18829
19243
|
}
|
|
19244
|
+
validateFcConfig(service, fc) {
|
|
19245
|
+
if (fc.provider !== "aliyun") {
|
|
19246
|
+
throw new Error(`Unsupported fc.provider for ${service.id}: ${fc.provider}`);
|
|
19247
|
+
}
|
|
19248
|
+
if (!fc.region?.trim()) {
|
|
19249
|
+
throw new Error(`Missing fc.region for ${service.id}`);
|
|
19250
|
+
}
|
|
19251
|
+
if (!fc.repository?.trim()) {
|
|
19252
|
+
throw new Error(`Missing fc.repository for ${service.id}`);
|
|
19253
|
+
}
|
|
19254
|
+
if (!fc.dir?.trim()) {
|
|
19255
|
+
throw new Error(`Missing fc.dir for ${service.id}`);
|
|
19256
|
+
}
|
|
19257
|
+
if (!fc.functionName?.trim()) {
|
|
19258
|
+
throw new Error(`Missing fc.functionName for ${service.id}`);
|
|
19259
|
+
}
|
|
19260
|
+
if (fc.domain && !fc.domain.trim()) {
|
|
19261
|
+
throw new Error(`Invalid fc.domain for ${service.id}`);
|
|
19262
|
+
}
|
|
19263
|
+
if (fc.subdomain && !fc.domain?.trim()) {
|
|
19264
|
+
throw new Error(`Missing fc.domain for ${service.id} (required when fc.subdomain is set)`);
|
|
19265
|
+
}
|
|
19266
|
+
}
|
|
19267
|
+
async ensurePnpmWorkspaceRoot(repoRoot) {
|
|
19268
|
+
const workspacePath = import_node_path22.default.join(repoRoot, "pnpm-workspace.yaml");
|
|
19269
|
+
if (!(0, import_node_fs17.existsSync)(workspacePath)) {
|
|
19270
|
+
throw new Error(`Missing pnpm-workspace.yaml at ${workspacePath}`);
|
|
19271
|
+
}
|
|
19272
|
+
}
|
|
19273
|
+
async syncFcRequiredEnv(service, registry, stage) {
|
|
19274
|
+
if (this.isLegacy(service)) {
|
|
19275
|
+
return [];
|
|
19276
|
+
}
|
|
19277
|
+
const appId = `${service.namespace}:${service.name}`;
|
|
19278
|
+
const items = await registry.listEnvItems(appId, {
|
|
19279
|
+
scope: stage,
|
|
19280
|
+
includeSecretMeta: true
|
|
19281
|
+
});
|
|
19282
|
+
const requiredEnv = Array.from(
|
|
19283
|
+
new Set(
|
|
19284
|
+
items.map((item) => item.key ?? item.name).filter((key) => typeof key === "string" && key.trim().length > 0).map((key) => key.trim())
|
|
19285
|
+
)
|
|
19286
|
+
).sort();
|
|
19287
|
+
const current = service.fc?.requiredEnv ?? [];
|
|
19288
|
+
if (this.sameStringSet(current, requiredEnv)) {
|
|
19289
|
+
return requiredEnv;
|
|
19290
|
+
}
|
|
19291
|
+
service.fc = {
|
|
19292
|
+
...service.fc,
|
|
19293
|
+
requiredEnv
|
|
19294
|
+
};
|
|
19295
|
+
await registry.updateService(service.namespace, service.name, service);
|
|
19296
|
+
ui.success(`Synced fc.requiredEnv for ${service.id}`, requiredEnv);
|
|
19297
|
+
return requiredEnv;
|
|
19298
|
+
}
|
|
19299
|
+
sameStringSet(left, right) {
|
|
19300
|
+
if (left.length !== right.length) {
|
|
19301
|
+
return false;
|
|
19302
|
+
}
|
|
19303
|
+
const normalizedLeft = [...left].sort();
|
|
19304
|
+
const normalizedRight = [...right].sort();
|
|
19305
|
+
return normalizedLeft.every((value, index) => value === normalizedRight[index]);
|
|
19306
|
+
}
|
|
19307
|
+
hasResolvedEnvValue(values, key) {
|
|
19308
|
+
return Object.prototype.hasOwnProperty.call(values, key) && values[key] !== void 0 && values[key] !== "";
|
|
19309
|
+
}
|
|
19310
|
+
buildFcHost(fc) {
|
|
19311
|
+
const domain = fc.domain?.trim();
|
|
19312
|
+
if (!domain) {
|
|
19313
|
+
return void 0;
|
|
19314
|
+
}
|
|
19315
|
+
const subdomain = fc.subdomain?.trim();
|
|
19316
|
+
return subdomain ? `${subdomain}.${domain}` : domain;
|
|
19317
|
+
}
|
|
19318
|
+
buildFcUrl(fc) {
|
|
19319
|
+
const host = this.buildFcHost(fc);
|
|
19320
|
+
return host ? `https://${host}` : void 0;
|
|
19321
|
+
}
|
|
19322
|
+
normalizeDnsValue(value) {
|
|
19323
|
+
return value.trim().replace(/\.$/, "").toLowerCase();
|
|
19324
|
+
}
|
|
19325
|
+
async resolveAliyunDeploymentEnv() {
|
|
19326
|
+
const config = loadPmateConfig();
|
|
19327
|
+
const accessKeyId = config.aliyun?.ak?.trim();
|
|
19328
|
+
const accessKeySecret = config.aliyun?.sk?.trim();
|
|
19329
|
+
if (!accessKeyId) {
|
|
19330
|
+
throw new Error("Missing aliyun.ak in ~/.pmate/config.yaml.");
|
|
19331
|
+
}
|
|
19332
|
+
if (!accessKeySecret) {
|
|
19333
|
+
throw new Error("Missing aliyun.sk in ~/.pmate/config.yaml.");
|
|
19334
|
+
}
|
|
19335
|
+
return {
|
|
19336
|
+
AccessKeyID: accessKeyId,
|
|
19337
|
+
AccessKeySecret: accessKeySecret,
|
|
19338
|
+
AccountID: await AliyunService.getAccountId()
|
|
19339
|
+
};
|
|
19340
|
+
}
|
|
19341
|
+
resolveFcNoProxyEnv() {
|
|
19342
|
+
const noProxy = /* @__PURE__ */ new Set(
|
|
19343
|
+
[
|
|
19344
|
+
"localhost",
|
|
19345
|
+
"127.0.0.1",
|
|
19346
|
+
"api.devsapp.cn",
|
|
19347
|
+
"devsapp.cn",
|
|
19348
|
+
"aliyuncs.com"
|
|
19349
|
+
]
|
|
19350
|
+
);
|
|
19351
|
+
for (const value of [process.env.NO_PROXY, process.env.no_proxy]) {
|
|
19352
|
+
for (const item of value?.split(",") ?? []) {
|
|
19353
|
+
const trimmed = item.trim();
|
|
19354
|
+
if (trimmed) {
|
|
19355
|
+
noProxy.add(trimmed);
|
|
19356
|
+
}
|
|
19357
|
+
}
|
|
19358
|
+
}
|
|
19359
|
+
return {
|
|
19360
|
+
HTTP_PROXY: "",
|
|
19361
|
+
HTTPS_PROXY: "",
|
|
19362
|
+
ALL_PROXY: "",
|
|
19363
|
+
http_proxy: "",
|
|
19364
|
+
https_proxy: "",
|
|
19365
|
+
all_proxy: "",
|
|
19366
|
+
NO_PROXY: Array.from(noProxy).join(","),
|
|
19367
|
+
no_proxy: Array.from(noProxy).join(",")
|
|
19368
|
+
};
|
|
19369
|
+
}
|
|
18830
19370
|
runCommand(command, args, cwd) {
|
|
19371
|
+
return this.runCommandWithEnv(command, args, cwd);
|
|
19372
|
+
}
|
|
19373
|
+
runCommandWithEnv(command, args, cwd, env) {
|
|
18831
19374
|
return new Promise((resolve, reject) => {
|
|
18832
|
-
const child = (0, import_node_child_process10.spawn)(command, args, {
|
|
19375
|
+
const child = (0, import_node_child_process10.spawn)(command, args, {
|
|
19376
|
+
cwd,
|
|
19377
|
+
stdio: "inherit",
|
|
19378
|
+
env: { ...process.env, ...env ?? {} }
|
|
19379
|
+
});
|
|
19380
|
+
child.on("error", reject);
|
|
19381
|
+
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`${command} exited with code ${code}`)));
|
|
19382
|
+
});
|
|
19383
|
+
}
|
|
19384
|
+
runShellCommand(command, cwd, env) {
|
|
19385
|
+
return new Promise((resolve, reject) => {
|
|
19386
|
+
const child = (0, import_node_child_process10.spawn)(command, [], {
|
|
19387
|
+
cwd,
|
|
19388
|
+
stdio: "inherit",
|
|
19389
|
+
shell: true,
|
|
19390
|
+
env: { ...process.env, ...env ?? {} }
|
|
19391
|
+
});
|
|
18833
19392
|
child.on("error", reject);
|
|
18834
19393
|
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`${command} exited with code ${code}`)));
|
|
18835
19394
|
});
|
|
@@ -18876,97 +19435,6 @@ var ServiceRuntimeRunner = class {
|
|
|
18876
19435
|
}
|
|
18877
19436
|
};
|
|
18878
19437
|
|
|
18879
|
-
// src/services/ServiceRegistryClient.ts
|
|
18880
|
-
init_sessionStore();
|
|
18881
|
-
var ServiceRegistryNotFoundError = class extends Error {
|
|
18882
|
-
constructor(namespace, name) {
|
|
18883
|
-
super(`Service not found: ${namespace}/${name}`);
|
|
18884
|
-
this.name = "ServiceRegistryNotFoundError";
|
|
18885
|
-
}
|
|
18886
|
-
};
|
|
18887
|
-
var ServiceRegistryClient = class {
|
|
18888
|
-
constructor(baseUrl) {
|
|
18889
|
-
this.baseUrl = baseUrl;
|
|
18890
|
-
}
|
|
18891
|
-
async listServices(filters) {
|
|
18892
|
-
const searchParams = new URLSearchParams();
|
|
18893
|
-
const repository = normalizeRepository(filters?.repository);
|
|
18894
|
-
if (repository) {
|
|
18895
|
-
searchParams.set("repo", repository);
|
|
18896
|
-
}
|
|
18897
|
-
const server = filters?.server?.trim();
|
|
18898
|
-
if (server) {
|
|
18899
|
-
searchParams.set("server", server);
|
|
18900
|
-
}
|
|
18901
|
-
const payload = await this.request(
|
|
18902
|
-
"GET",
|
|
18903
|
-
`/services${searchParams.size > 0 ? `?${searchParams.toString()}` : ""}`
|
|
18904
|
-
);
|
|
18905
|
-
const namespace = filters?.namespace?.trim();
|
|
18906
|
-
return payload.items.filter((item) => {
|
|
18907
|
-
if (namespace && item.namespace !== namespace) {
|
|
18908
|
-
return false;
|
|
18909
|
-
}
|
|
18910
|
-
return true;
|
|
18911
|
-
});
|
|
18912
|
-
}
|
|
18913
|
-
async getService(namespace, name) {
|
|
18914
|
-
return this.request(
|
|
18915
|
-
"GET",
|
|
18916
|
-
`/services/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`
|
|
18917
|
-
);
|
|
18918
|
-
}
|
|
18919
|
-
resolveBaseUrl() {
|
|
18920
|
-
return (this.baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
|
|
18921
|
-
}
|
|
18922
|
-
readToken() {
|
|
18923
|
-
return readRequiredSession({
|
|
18924
|
-
preferredApp: DEFAULT_APP_ID,
|
|
18925
|
-
fallbackToActiveApp: true
|
|
18926
|
-
}).token;
|
|
18927
|
-
}
|
|
18928
|
-
async request(method, pathname) {
|
|
18929
|
-
const response = await fetch(`${this.resolveBaseUrl()}${pathname}`, {
|
|
18930
|
-
method,
|
|
18931
|
-
headers: {
|
|
18932
|
-
Authorization: `Bearer ${this.readToken()}`,
|
|
18933
|
-
Accept: "application/json"
|
|
18934
|
-
}
|
|
18935
|
-
});
|
|
18936
|
-
let payload = null;
|
|
18937
|
-
try {
|
|
18938
|
-
payload = await response.json();
|
|
18939
|
-
} catch {
|
|
18940
|
-
payload = null;
|
|
18941
|
-
}
|
|
18942
|
-
if (response.status === 404) {
|
|
18943
|
-
const segments = pathname.split("/").filter(Boolean);
|
|
18944
|
-
throw new ServiceRegistryNotFoundError(
|
|
18945
|
-
decodeURIComponent(segments[1] || "unknown"),
|
|
18946
|
-
decodeURIComponent(segments[2] || "unknown")
|
|
18947
|
-
);
|
|
18948
|
-
}
|
|
18949
|
-
if (!response.ok || !payload?.success || payload.data === void 0) {
|
|
18950
|
-
throw new Error(payload?.message || `Request failed with status ${response.status}`);
|
|
18951
|
-
}
|
|
18952
|
-
return payload.data;
|
|
18953
|
-
}
|
|
18954
|
-
};
|
|
18955
|
-
function normalizeRepository(repository) {
|
|
18956
|
-
if (!repository) {
|
|
18957
|
-
return void 0;
|
|
18958
|
-
}
|
|
18959
|
-
const trimmed = repository.trim().replace(/\/+$/, "").replace(/\.git$/i, "");
|
|
18960
|
-
if (!trimmed) {
|
|
18961
|
-
return void 0;
|
|
18962
|
-
}
|
|
18963
|
-
const match = trimmed.match(/^https?:\/\/github\.com\/([^/]+\/[^/]+)$/i);
|
|
18964
|
-
if (match) {
|
|
18965
|
-
return match[1];
|
|
18966
|
-
}
|
|
18967
|
-
return trimmed.replace(/^\/+/, "");
|
|
18968
|
-
}
|
|
18969
|
-
|
|
18970
19438
|
// src/commands/ServiceCommandHandler.ts
|
|
18971
19439
|
init_namespacedRef();
|
|
18972
19440
|
var ServiceCommandHandler = class {
|
|
@@ -19121,6 +19589,7 @@ var ServiceCommandHandler = class {
|
|
|
19121
19589
|
}
|
|
19122
19590
|
);
|
|
19123
19591
|
ui.success(`Updated env ${args.key} for ${args.appId}`);
|
|
19592
|
+
await this.syncFcRequiredEnvFromServiceEnv(registry, args.appId, args.scope);
|
|
19124
19593
|
this.renderJson(payload, args.json);
|
|
19125
19594
|
return;
|
|
19126
19595
|
}
|
|
@@ -19133,6 +19602,7 @@ var ServiceCommandHandler = class {
|
|
|
19133
19602
|
}).toString()}`
|
|
19134
19603
|
);
|
|
19135
19604
|
ui.success(`Deleted env ${payload.key} (${payload.scope}) from ${args.appId}`);
|
|
19605
|
+
await this.syncFcRequiredEnvFromServiceEnv(registry, args.appId, args.scope);
|
|
19136
19606
|
this.renderJson(payload, args.json);
|
|
19137
19607
|
return;
|
|
19138
19608
|
}
|
|
@@ -19318,6 +19788,48 @@ var ServiceCommandHandler = class {
|
|
|
19318
19788
|
);
|
|
19319
19789
|
}
|
|
19320
19790
|
}
|
|
19791
|
+
async syncFcRequiredEnvFromServiceEnv(registry, appId, scope) {
|
|
19792
|
+
const [namespace, name, extra] = appId.split(":");
|
|
19793
|
+
if (!namespace || !name || extra !== void 0) {
|
|
19794
|
+
return;
|
|
19795
|
+
}
|
|
19796
|
+
let service;
|
|
19797
|
+
try {
|
|
19798
|
+
service = await registry.getService(namespace, name);
|
|
19799
|
+
} catch (error) {
|
|
19800
|
+
if (error instanceof ServiceRegistryNotFoundError) {
|
|
19801
|
+
return;
|
|
19802
|
+
}
|
|
19803
|
+
throw error;
|
|
19804
|
+
}
|
|
19805
|
+
if (!("type" in service) || service.type !== "fc" || !service.fc) {
|
|
19806
|
+
return;
|
|
19807
|
+
}
|
|
19808
|
+
const items = await registry.listEnvItems(appId, {
|
|
19809
|
+
scope,
|
|
19810
|
+
includeSecretMeta: true
|
|
19811
|
+
});
|
|
19812
|
+
const requiredEnv = Array.from(
|
|
19813
|
+
new Set(
|
|
19814
|
+
items.map((item) => item.key ?? item.name).filter((key) => typeof key === "string" && key.trim().length > 0).map((key) => key.trim())
|
|
19815
|
+
)
|
|
19816
|
+
).sort();
|
|
19817
|
+
const current = service.fc.requiredEnv ?? [];
|
|
19818
|
+
if (this.sameStringSet(current, requiredEnv)) {
|
|
19819
|
+
return;
|
|
19820
|
+
}
|
|
19821
|
+
service.fc = { ...service.fc, requiredEnv };
|
|
19822
|
+
await registry.updateService(namespace, name, service);
|
|
19823
|
+
ui.success(`Synced fc.requiredEnv for ${appId}`);
|
|
19824
|
+
}
|
|
19825
|
+
sameStringSet(left, right) {
|
|
19826
|
+
if (left.length !== right.length) {
|
|
19827
|
+
return false;
|
|
19828
|
+
}
|
|
19829
|
+
const normalizedLeft = [...left].sort();
|
|
19830
|
+
const normalizedRight = [...right].sort();
|
|
19831
|
+
return normalizedLeft.every((value, index) => value === normalizedRight[index]);
|
|
19832
|
+
}
|
|
19321
19833
|
renderList(items, asJson) {
|
|
19322
19834
|
if (asJson) {
|
|
19323
19835
|
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.2",
|
|
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",
|