@h-ai/cli 0.1.0-alpha.53 → 0.1.0-alpha.54
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/README.md +14 -0
- package/dist/{chunk-U4PA632G.js → chunk-FVDXJYMO.js} +34 -67
- package/dist/chunk-FVDXJYMO.js.map +1 -0
- package/dist/cli-config-templates-WGN2I23J.js +5 -0
- package/dist/{cli-config-templates-GSXKLGU5.js.map → cli-config-templates-WGN2I23J.js.map} +1 -1
- package/dist/index.js +174 -105
- package/dist/index.js.map +1 -1
- package/dist/{src-QNEJJ3BV.js → src-6ZBWDBUB.js} +127 -87
- package/dist/{src-QNEJJ3BV.js.map → src-6ZBWDBUB.js.map} +1 -1
- package/package.json +13 -4
- package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +1 -0
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -0
- package/templates/apps/api/package.json.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +1 -0
- package/templates/apps/fullstack/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +1 -0
- package/templates/base/eslint.config.js.hbs +1 -0
- package/templates/base/package.json.hbs +3 -1
- package/templates/skills/hai-ai/reference.md +5 -1
- package/templates/skills/hai-app-review/SKILL.md +6 -0
- package/templates/skills/hai-build/SKILL.md +8 -0
- package/templates/skills/hai-cache/SKILL.md +2 -0
- package/templates/skills/hai-capacitor/SKILL.md +2 -0
- package/templates/skills/hai-ci/SKILL.md +12 -0
- package/templates/skills/hai-core/SKILL.md +2 -0
- package/templates/skills/hai-crypto/SKILL.md +2 -0
- package/templates/skills/hai-deploy/SKILL.md +12 -0
- package/templates/skills/hai-iam/SKILL.md +6 -0
- package/templates/skills/hai-scheduler/SKILL.md +12 -0
- package/templates/skills/hai-ui/SKILL.md +4 -0
- package/dist/chunk-U4PA632G.js.map +0 -1
- package/dist/cli-config-templates-GSXKLGU5.js +0 -5
|
@@ -14542,7 +14542,9 @@ var en_US_default = {
|
|
|
14542
14542
|
deploy_credentialMissing: "Missing credential fields: {fields}",
|
|
14543
14543
|
deploy_apiError: "{service} API error ({status})",
|
|
14544
14544
|
deploy_fileUploadFailed: "File upload failed ({status})",
|
|
14545
|
-
deploy_provisionNoResult: "No connection info returned from {service}"
|
|
14545
|
+
deploy_provisionNoResult: "No connection info returned from {service}",
|
|
14546
|
+
deploy_recoveryFailure: "Deployment stopped at {stage}; inspect the recovery details.",
|
|
14547
|
+
deploy_recoverySuggestion: "Inspect listed resources in the provider console before retrying with the same project name. Unknown means the request may have created a resource. Reused resources are never deleted automatically; remove newly created resources manually only after checking their use."
|
|
14546
14548
|
};
|
|
14547
14549
|
|
|
14548
14550
|
// ../deploy/messages/zh-CN.json
|
|
@@ -14570,7 +14572,9 @@ var zh_CN_default2 = {
|
|
|
14570
14572
|
deploy_credentialMissing: "\u7F3A\u5C11\u51ED\u8BC1\u5B57\u6BB5\uFF1A{fields}",
|
|
14571
14573
|
deploy_apiError: "{service} API \u8BF7\u6C42\u5931\u8D25\uFF08{status}\uFF09",
|
|
14572
14574
|
deploy_fileUploadFailed: "\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25\uFF08{status}\uFF09",
|
|
14573
|
-
deploy_provisionNoResult: "{service} \u672A\u8FD4\u56DE\u8FDE\u63A5\u4FE1\u606F"
|
|
14575
|
+
deploy_provisionNoResult: "{service} \u672A\u8FD4\u56DE\u8FDE\u63A5\u4FE1\u606F",
|
|
14576
|
+
deploy_recoveryFailure: "\u90E8\u7F72\u5728 {stage} \u9636\u6BB5\u505C\u6B62\uFF0C\u8BF7\u67E5\u770B\u6062\u590D\u6E05\u5355\u3002",
|
|
14577
|
+
deploy_recoverySuggestion: "\u91CD\u8BD5\u524D\u5728\u670D\u52A1\u5546\u63A7\u5236\u53F0\u6838\u5BF9\u6E05\u5355\uFF0C\u5E76\u4F7F\u7528\u76F8\u540C\u9879\u76EE\u540D\u3002unknown \u8868\u793A\u8BF7\u6C42\u53EF\u80FD\u5DF2\u521B\u5EFA\u8D44\u6E90\u3002\u590D\u7528\u8D44\u6E90\u4E0D\u4F1A\u81EA\u52A8\u5220\u9664\uFF1B\u65B0\u5EFA\u8D44\u6E90\u4EC5\u5728\u786E\u8BA4\u672A\u88AB\u4F7F\u7528\u540E\u4EBA\u5DE5\u6E05\u7406\u3002"
|
|
14574
14578
|
};
|
|
14575
14579
|
|
|
14576
14580
|
// ../deploy/src/deploy-i18n.ts
|
|
@@ -14598,15 +14602,19 @@ var UpstashServiceSchema = external_exports.object({
|
|
|
14598
14602
|
});
|
|
14599
14603
|
var R2ServiceSchema = external_exports.object({
|
|
14600
14604
|
provisioner: external_exports.literal("cloudflare-r2"),
|
|
14605
|
+
accessKeyId: external_exports.string().min(1),
|
|
14606
|
+
secretAccessKey: external_exports.string().min(1),
|
|
14601
14607
|
accountId: external_exports.string().min(1),
|
|
14602
14608
|
apiToken: external_exports.string().min(1)
|
|
14603
14609
|
});
|
|
14604
14610
|
var ResendServiceSchema = external_exports.object({
|
|
14605
14611
|
provisioner: external_exports.literal("resend"),
|
|
14612
|
+
from: external_exports.string().email(),
|
|
14606
14613
|
apiKey: external_exports.string().min(1)
|
|
14607
14614
|
});
|
|
14608
14615
|
var AliyunSmsServiceSchema = external_exports.object({
|
|
14609
14616
|
provisioner: external_exports.literal("aliyun"),
|
|
14617
|
+
signName: external_exports.string().min(1),
|
|
14610
14618
|
accessKeyId: external_exports.string().min(1),
|
|
14611
14619
|
accessKeySecret: external_exports.string().min(1)
|
|
14612
14620
|
});
|
|
@@ -14761,13 +14769,13 @@ function saveCredentials(entries) {
|
|
|
14761
14769
|
}
|
|
14762
14770
|
}
|
|
14763
14771
|
var logger2 = core.logger.child({ module: "deploy", scope: "functions" });
|
|
14764
|
-
function buildApp(appDir, buildCommand) {
|
|
14772
|
+
function buildApp(appDir, buildCommand, envVars = {}) {
|
|
14765
14773
|
logger2.info("Building application", { appDir, buildCommand });
|
|
14766
14774
|
try {
|
|
14767
14775
|
execSync(buildCommand, {
|
|
14768
14776
|
cwd: appDir,
|
|
14769
14777
|
stdio: "inherit",
|
|
14770
|
-
env: { ...process2.env }
|
|
14778
|
+
env: { ...process2.env, ...envVars }
|
|
14771
14779
|
});
|
|
14772
14780
|
return ok(void 0);
|
|
14773
14781
|
} catch (error51) {
|
|
@@ -14793,6 +14801,32 @@ function resolveOutputDir(appDir, providerType) {
|
|
|
14793
14801
|
return join(appDir, "build");
|
|
14794
14802
|
}
|
|
14795
14803
|
}
|
|
14804
|
+
var RecoverySchema = external_exports.object({
|
|
14805
|
+
projectName: external_exports.string(),
|
|
14806
|
+
stage: external_exports.string(),
|
|
14807
|
+
platformProjectId: external_exports.string().optional(),
|
|
14808
|
+
resources: external_exports.array(external_exports.object({
|
|
14809
|
+
serviceType: external_exports.enum(["db", "cache", "storage", "email", "sms"]),
|
|
14810
|
+
provisionerName: external_exports.string(),
|
|
14811
|
+
resourceInfo: external_exports.string(),
|
|
14812
|
+
resourceStatus: external_exports.enum(["created", "reused", "unknown"])
|
|
14813
|
+
}))
|
|
14814
|
+
});
|
|
14815
|
+
function getDeployRecovery(error51) {
|
|
14816
|
+
const parsed = RecoverySchema.safeParse(error51.ext?.recovery);
|
|
14817
|
+
return parsed.success ? parsed.data : void 0;
|
|
14818
|
+
}
|
|
14819
|
+
function recoveryFailure(error51, recovery) {
|
|
14820
|
+
return err({
|
|
14821
|
+
code: error51.code,
|
|
14822
|
+
httpStatus: error51.httpStatus,
|
|
14823
|
+
system: error51.system,
|
|
14824
|
+
module: error51.module,
|
|
14825
|
+
message: deployM("deploy_recoveryFailure", { params: { stage: recovery.stage } }),
|
|
14826
|
+
suggestion: deployM("deploy_recoverySuggestion"),
|
|
14827
|
+
ext: { recovery }
|
|
14828
|
+
});
|
|
14829
|
+
}
|
|
14796
14830
|
var logger3 = core.logger.child({ module: "deploy", scope: "scanner" });
|
|
14797
14831
|
function readYaml(filePath) {
|
|
14798
14832
|
if (!existsSync(filePath)) {
|
|
@@ -15204,6 +15238,7 @@ var logger5 = core.logger.child({ module: "deploy", scope: "provisioner-aliyun"
|
|
|
15204
15238
|
function createAliyunProvisioner() {
|
|
15205
15239
|
let accessKey = null;
|
|
15206
15240
|
let secretKey = null;
|
|
15241
|
+
let signName = "";
|
|
15207
15242
|
return {
|
|
15208
15243
|
name: "aliyun",
|
|
15209
15244
|
serviceType: "sms",
|
|
@@ -15212,11 +15247,12 @@ function createAliyunProvisioner() {
|
|
|
15212
15247
|
try {
|
|
15213
15248
|
const akId = credentials.accessKeyId ?? credentials.access_key_id ?? credentials.access_key ?? "";
|
|
15214
15249
|
const akSecret = credentials.accessKeySecret ?? credentials.access_key_secret ?? credentials.secret_key ?? "";
|
|
15215
|
-
if (!akId || !akSecret) {
|
|
15216
|
-
throw new Error(deployM("deploy_credentialMissing", { params: { fields: "
|
|
15250
|
+
if (!akId || !akSecret || !credentials.signName) {
|
|
15251
|
+
throw new Error(deployM("deploy_credentialMissing", { params: { fields: "accessKeyId, accessKeySecret, signName" } }));
|
|
15217
15252
|
}
|
|
15218
15253
|
accessKey = akId;
|
|
15219
15254
|
secretKey = akSecret;
|
|
15255
|
+
signName = credentials.signName;
|
|
15220
15256
|
logger5.info("Aliyun authenticated", { accessKeyId: `${akId.slice(0, 6)}***` });
|
|
15221
15257
|
return ok(akId);
|
|
15222
15258
|
} catch (error51) {
|
|
@@ -15241,9 +15277,9 @@ function createAliyunProvisioner() {
|
|
|
15241
15277
|
return ok({
|
|
15242
15278
|
serviceType: "sms",
|
|
15243
15279
|
provisionerName: "aliyun",
|
|
15280
|
+
resourceStatus: "reused",
|
|
15244
15281
|
envVars: {
|
|
15245
|
-
|
|
15246
|
-
HAI_REACH_SMS_SECRET_KEY: secretKey
|
|
15282
|
+
HAI_REACH_PROVIDERS: JSON.stringify([{ name: "sms", type: "aliyun-sms", accessKeyId: accessKey, accessKeySecret: secretKey, signName }])
|
|
15247
15283
|
},
|
|
15248
15284
|
resourceInfo: "aliyun:verify-only"
|
|
15249
15285
|
});
|
|
@@ -15292,12 +15328,13 @@ async function getNeonConnectionUri(token, projectId) {
|
|
|
15292
15328
|
}
|
|
15293
15329
|
return data.uri;
|
|
15294
15330
|
}
|
|
15295
|
-
function buildNeonProvisionResult(projectId, connectionUri) {
|
|
15331
|
+
function buildNeonProvisionResult(projectId, connectionUri, resourceStatus) {
|
|
15296
15332
|
return {
|
|
15297
15333
|
serviceType: "db",
|
|
15298
15334
|
provisionerName: "neon",
|
|
15335
|
+
resourceStatus,
|
|
15299
15336
|
envVars: {
|
|
15300
|
-
|
|
15337
|
+
HAI_DB: JSON.stringify({ type: "postgresql", url: connectionUri, database: decodeURIComponent(new URL(connectionUri).pathname.slice(1)) })
|
|
15301
15338
|
},
|
|
15302
15339
|
resourceInfo: `neon-project:${projectId}`
|
|
15303
15340
|
};
|
|
@@ -15337,16 +15374,18 @@ function createNeonProvisioner() {
|
|
|
15337
15374
|
);
|
|
15338
15375
|
}
|
|
15339
15376
|
logger6.debug("Provisioning Neon database", { appName });
|
|
15377
|
+
let resource;
|
|
15340
15378
|
try {
|
|
15341
15379
|
const projectName = `${appName}-db`;
|
|
15342
15380
|
const existingProject = await findExistingNeonProject(token, projectName);
|
|
15343
15381
|
if (existingProject !== null) {
|
|
15382
|
+
resource = { resourceInfo: `neon-project:${existingProject.id}`, resourceStatus: "reused" };
|
|
15344
15383
|
const connectionUri2 = await getNeonConnectionUri(token, existingProject.id);
|
|
15345
15384
|
logger6.info("Neon project reused", {
|
|
15346
15385
|
projectId: existingProject.id,
|
|
15347
15386
|
projectName
|
|
15348
15387
|
});
|
|
15349
|
-
return ok(buildNeonProvisionResult(existingProject.id, connectionUri2));
|
|
15388
|
+
return ok(buildNeonProvisionResult(existingProject.id, connectionUri2, "reused"));
|
|
15350
15389
|
}
|
|
15351
15390
|
const data = await neonFetch(token, "/projects", {
|
|
15352
15391
|
method: "POST",
|
|
@@ -15358,14 +15397,23 @@ function createNeonProvisioner() {
|
|
|
15358
15397
|
})
|
|
15359
15398
|
});
|
|
15360
15399
|
const projectId = data.project?.id ?? "";
|
|
15400
|
+
if (projectId)
|
|
15401
|
+
resource = { resourceInfo: `neon-project:${projectId}`, resourceStatus: "created" };
|
|
15361
15402
|
const connectionUri = data.connection_uris?.[0]?.connection_uri ?? "";
|
|
15362
15403
|
if (!projectId || !connectionUri) {
|
|
15363
15404
|
throw new Error(deployM("deploy_provisionNoResult", { params: { service: "Neon" } }));
|
|
15364
15405
|
}
|
|
15365
15406
|
logger6.info("Neon database provisioned", { projectId });
|
|
15366
|
-
return ok(buildNeonProvisionResult(projectId, connectionUri));
|
|
15407
|
+
return ok(buildNeonProvisionResult(projectId, connectionUri, "created"));
|
|
15367
15408
|
} catch (error51) {
|
|
15368
|
-
logger6.error("Neon provisioning failed", { appName
|
|
15409
|
+
logger6.error("Neon provisioning failed", { appName });
|
|
15410
|
+
if (resource) {
|
|
15411
|
+
return recoveryFailure({ ...HaiDeployError.PROVISION_FAILED}, {
|
|
15412
|
+
projectName: appName,
|
|
15413
|
+
stage: "provision:db",
|
|
15414
|
+
resources: [{ serviceType: "db", provisionerName: "neon", ...resource }]
|
|
15415
|
+
});
|
|
15416
|
+
}
|
|
15369
15417
|
return err(
|
|
15370
15418
|
HaiDeployError.PROVISION_FAILED,
|
|
15371
15419
|
deployM("deploy_provisionFailed", {
|
|
@@ -15385,6 +15433,8 @@ var CF_API = "https://api.cloudflare.com/client/v4";
|
|
|
15385
15433
|
function createR2Provisioner() {
|
|
15386
15434
|
let token = null;
|
|
15387
15435
|
let accountId = null;
|
|
15436
|
+
let accessKey = "";
|
|
15437
|
+
let secretKey = "";
|
|
15388
15438
|
return {
|
|
15389
15439
|
name: "r2",
|
|
15390
15440
|
serviceType: "storage",
|
|
@@ -15393,8 +15443,8 @@ function createR2Provisioner() {
|
|
|
15393
15443
|
try {
|
|
15394
15444
|
const acctId = credentials.accountId ?? credentials.account_id ?? "";
|
|
15395
15445
|
const apiTok = credentials.apiToken ?? credentials.api_token ?? credentials.token ?? "";
|
|
15396
|
-
if (!acctId || !apiTok) {
|
|
15397
|
-
throw new Error(deployM("deploy_credentialMissing", { params: { fields: "
|
|
15446
|
+
if (!acctId || !apiTok || !credentials.accessKeyId || !credentials.secretAccessKey) {
|
|
15447
|
+
throw new Error(deployM("deploy_credentialMissing", { params: { fields: "accountId, apiToken, accessKeyId, secretAccessKey" } }));
|
|
15398
15448
|
}
|
|
15399
15449
|
const res = await fetch(`${CF_API}/accounts/${acctId}/r2/buckets`, {
|
|
15400
15450
|
headers: { Authorization: `Bearer ${apiTok}` }
|
|
@@ -15404,6 +15454,8 @@ function createR2Provisioner() {
|
|
|
15404
15454
|
}
|
|
15405
15455
|
accountId = acctId;
|
|
15406
15456
|
token = apiTok;
|
|
15457
|
+
accessKey = credentials.accessKeyId;
|
|
15458
|
+
secretKey = credentials.secretAccessKey;
|
|
15407
15459
|
logger7.info("Cloudflare R2 authenticated", { accountId: acctId });
|
|
15408
15460
|
return ok(acctId);
|
|
15409
15461
|
} catch (error51) {
|
|
@@ -15441,42 +15493,20 @@ function createR2Provisioner() {
|
|
|
15441
15493
|
if (!createRes.ok && createRes.status !== 409) {
|
|
15442
15494
|
throw new Error(deployM("deploy_apiError", { params: { service: "Cloudflare", status: String(createRes.status) } }));
|
|
15443
15495
|
}
|
|
15444
|
-
const tokenRes = await fetch(
|
|
15445
|
-
`${CF_API}/user/tokens`,
|
|
15446
|
-
{
|
|
15447
|
-
method: "POST",
|
|
15448
|
-
headers: {
|
|
15449
|
-
"Authorization": `Bearer ${token}`,
|
|
15450
|
-
"Content-Type": "application/json"
|
|
15451
|
-
},
|
|
15452
|
-
body: JSON.stringify({
|
|
15453
|
-
name: `${appName}-r2-token`,
|
|
15454
|
-
policies: [{
|
|
15455
|
-
effect: "allow",
|
|
15456
|
-
permission_groups: [{ id: "r2-read-write" }],
|
|
15457
|
-
resources: { [`com.cloudflare.edge.r2.bucket.${accountId}_default_${bucketName}`]: "*" }
|
|
15458
|
-
}]
|
|
15459
|
-
})
|
|
15460
|
-
}
|
|
15461
|
-
);
|
|
15462
|
-
if (!tokenRes.ok) {
|
|
15463
|
-
throw new Error(deployM("deploy_apiError", { params: { service: "Cloudflare", status: String(tokenRes.status) } }));
|
|
15464
|
-
}
|
|
15465
|
-
const tokenData = await tokenRes.json();
|
|
15466
|
-
const accessKey = tokenData.result?.id ?? "";
|
|
15467
|
-
const secretKey = tokenData.result?.value ?? "";
|
|
15468
|
-
if (!accessKey || !secretKey) {
|
|
15469
|
-
throw new Error(deployM("deploy_provisionNoResult", { params: { service: "Cloudflare R2" } }));
|
|
15470
|
-
}
|
|
15471
15496
|
logger7.info("R2 bucket provisioned", { bucketName });
|
|
15472
15497
|
return ok({
|
|
15473
15498
|
serviceType: "storage",
|
|
15474
15499
|
provisionerName: "r2",
|
|
15500
|
+
resourceStatus: createRes.status === 409 ? "reused" : "created",
|
|
15475
15501
|
envVars: {
|
|
15476
|
-
|
|
15477
|
-
|
|
15478
|
-
|
|
15479
|
-
|
|
15502
|
+
HAI_STORAGE: JSON.stringify({
|
|
15503
|
+
type: "s3",
|
|
15504
|
+
endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
|
|
15505
|
+
bucket: bucketName,
|
|
15506
|
+
region: "auto",
|
|
15507
|
+
accessKeyId: accessKey,
|
|
15508
|
+
secretAccessKey: secretKey
|
|
15509
|
+
})
|
|
15480
15510
|
},
|
|
15481
15511
|
resourceInfo: `r2-bucket:${bucketName}`
|
|
15482
15512
|
});
|
|
@@ -15500,6 +15530,7 @@ var logger8 = core.logger.child({ module: "deploy", scope: "provisioner-resend"
|
|
|
15500
15530
|
var RESEND_API = "https://api.resend.com";
|
|
15501
15531
|
function createResendProvisioner() {
|
|
15502
15532
|
let token = null;
|
|
15533
|
+
let from = "";
|
|
15503
15534
|
return {
|
|
15504
15535
|
name: "resend",
|
|
15505
15536
|
serviceType: "email",
|
|
@@ -15507,8 +15538,8 @@ function createResendProvisioner() {
|
|
|
15507
15538
|
logger8.debug("Authenticating with Resend");
|
|
15508
15539
|
try {
|
|
15509
15540
|
const apiToken = credentials.apiKey ?? credentials.api_key ?? credentials.token ?? "";
|
|
15510
|
-
if (!apiToken) {
|
|
15511
|
-
throw new Error(deployM("deploy_credentialMissing", { params: { fields: "
|
|
15541
|
+
if (!apiToken || !credentials.from) {
|
|
15542
|
+
throw new Error(deployM("deploy_credentialMissing", { params: { fields: "apiKey, from" } }));
|
|
15512
15543
|
}
|
|
15513
15544
|
const res = await fetch(`${RESEND_API}/domains`, {
|
|
15514
15545
|
headers: { Authorization: `Bearer ${apiToken}` }
|
|
@@ -15517,6 +15548,7 @@ function createResendProvisioner() {
|
|
|
15517
15548
|
throw new Error(deployM("deploy_apiError", { params: { service: "Resend", status: String(res.status) } }));
|
|
15518
15549
|
}
|
|
15519
15550
|
token = apiToken;
|
|
15551
|
+
from = credentials.from;
|
|
15520
15552
|
logger8.info("Resend authenticated");
|
|
15521
15553
|
return ok("resend-user");
|
|
15522
15554
|
} catch (error51) {
|
|
@@ -15541,8 +15573,9 @@ function createResendProvisioner() {
|
|
|
15541
15573
|
return ok({
|
|
15542
15574
|
serviceType: "email",
|
|
15543
15575
|
provisionerName: "resend",
|
|
15576
|
+
resourceStatus: "reused",
|
|
15544
15577
|
envVars: {
|
|
15545
|
-
|
|
15578
|
+
HAI_REACH_PROVIDERS: JSON.stringify([{ name: "email", type: "smtp", host: "smtp.resend.com", port: 465, secure: true, user: "resend", pass: token, from }])
|
|
15546
15579
|
},
|
|
15547
15580
|
resourceInfo: "resend:verify-only"
|
|
15548
15581
|
});
|
|
@@ -15578,38 +15611,17 @@ async function findExistingUpstashDatabase(email3, apiKey, databaseName) {
|
|
|
15578
15611
|
async function getUpstashDatabase(email3, apiKey, databaseId) {
|
|
15579
15612
|
return upstashFetch(email3, apiKey, `/v2/redis/database/${databaseId}`);
|
|
15580
15613
|
}
|
|
15581
|
-
function
|
|
15582
|
-
if (typeof database.rest_url === "string" && database.rest_url.length > 0) {
|
|
15583
|
-
return database.rest_url;
|
|
15584
|
-
}
|
|
15585
|
-
const endpoint = database.endpoint?.trim() ?? "";
|
|
15586
|
-
if (!endpoint) {
|
|
15587
|
-
return "";
|
|
15588
|
-
}
|
|
15589
|
-
if (endpoint.startsWith("http://") || endpoint.startsWith("https://")) {
|
|
15590
|
-
return endpoint;
|
|
15591
|
-
}
|
|
15592
|
-
if (endpoint.includes(".")) {
|
|
15593
|
-
return `https://${endpoint}`;
|
|
15594
|
-
}
|
|
15595
|
-
return `https://${endpoint}.upstash.io`;
|
|
15596
|
-
}
|
|
15597
|
-
function resolveUpstashRestToken(database) {
|
|
15598
|
-
return database.rest_token ?? database.token ?? database.password ?? "";
|
|
15599
|
-
}
|
|
15600
|
-
function buildUpstashProvisionResult(database) {
|
|
15614
|
+
function buildUpstashProvisionResult(database, resourceStatus) {
|
|
15601
15615
|
const databaseId = database.database_id ?? "";
|
|
15602
|
-
|
|
15603
|
-
const restToken = resolveUpstashRestToken(database);
|
|
15604
|
-
if (!databaseId || !restUrl || !restToken) {
|
|
15616
|
+
if (!databaseId || !database.endpoint || !database.password || !database.port) {
|
|
15605
15617
|
throw new Error(deployM("deploy_provisionNoResult", { params: { service: "Upstash" } }));
|
|
15606
15618
|
}
|
|
15607
15619
|
return {
|
|
15608
15620
|
serviceType: "cache",
|
|
15609
15621
|
provisionerName: "upstash",
|
|
15622
|
+
resourceStatus,
|
|
15610
15623
|
envVars: {
|
|
15611
|
-
|
|
15612
|
-
HAI_CACHE_UPSTASH_TOKEN: restToken
|
|
15624
|
+
HAI_CACHE: JSON.stringify({ type: "redis", url: `rediss://default:${encodeURIComponent(database.password)}@${database.endpoint}:${database.port}` })
|
|
15613
15625
|
},
|
|
15614
15626
|
resourceInfo: `upstash-db:${databaseId}`
|
|
15615
15627
|
};
|
|
@@ -15652,6 +15664,7 @@ function createUpstashProvisioner() {
|
|
|
15652
15664
|
);
|
|
15653
15665
|
}
|
|
15654
15666
|
logger9.debug("Provisioning Upstash Redis", { appName });
|
|
15667
|
+
let resource;
|
|
15655
15668
|
try {
|
|
15656
15669
|
const databaseName = `${appName}-cache`;
|
|
15657
15670
|
const existingDatabase = await findExistingUpstashDatabase(email3, apiKey, databaseName);
|
|
@@ -15660,9 +15673,10 @@ function createUpstashProvisioner() {
|
|
|
15660
15673
|
if (!databaseId) {
|
|
15661
15674
|
throw new Error(deployM("deploy_provisionNoResult", { params: { service: "Upstash" } }));
|
|
15662
15675
|
}
|
|
15676
|
+
resource = { resourceInfo: `upstash-db:${databaseId}`, resourceStatus: "reused" };
|
|
15663
15677
|
const detail = await getUpstashDatabase(email3, apiKey, databaseId);
|
|
15664
15678
|
logger9.info("Upstash Redis reused", { databaseId, databaseName });
|
|
15665
|
-
return ok(buildUpstashProvisionResult({ ...existingDatabase, ...detail }));
|
|
15679
|
+
return ok(buildUpstashProvisionResult({ ...existingDatabase, ...detail }, "reused"));
|
|
15666
15680
|
}
|
|
15667
15681
|
const data = await upstashFetch(email3, apiKey, "/v2/redis/database", {
|
|
15668
15682
|
method: "POST",
|
|
@@ -15672,10 +15686,19 @@ function createUpstashProvisioner() {
|
|
|
15672
15686
|
tls: true
|
|
15673
15687
|
})
|
|
15674
15688
|
});
|
|
15689
|
+
if (data.database_id)
|
|
15690
|
+
resource = { resourceInfo: `upstash-db:${data.database_id}`, resourceStatus: "created" };
|
|
15675
15691
|
logger9.info("Upstash Redis provisioned", { databaseId: data.database_id });
|
|
15676
|
-
return ok(buildUpstashProvisionResult(data));
|
|
15692
|
+
return ok(buildUpstashProvisionResult(data, "created"));
|
|
15677
15693
|
} catch (error51) {
|
|
15678
|
-
logger9.error("Upstash provisioning failed", { appName
|
|
15694
|
+
logger9.error("Upstash provisioning failed", { appName });
|
|
15695
|
+
if (resource) {
|
|
15696
|
+
return recoveryFailure({ ...HaiDeployError.PROVISION_FAILED}, {
|
|
15697
|
+
projectName: appName,
|
|
15698
|
+
stage: "provision:cache",
|
|
15699
|
+
resources: [{ serviceType: "cache", provisionerName: "upstash", ...resource }]
|
|
15700
|
+
});
|
|
15701
|
+
}
|
|
15679
15702
|
return err(
|
|
15680
15703
|
HaiDeployError.PROVISION_FAILED,
|
|
15681
15704
|
deployM("deploy_provisionFailed", {
|
|
@@ -15856,9 +15879,18 @@ var deploy = {
|
|
|
15856
15879
|
logger10.error("Service provisioning failed", {
|
|
15857
15880
|
serviceType,
|
|
15858
15881
|
provisioner: provisioner.name,
|
|
15859
|
-
|
|
15882
|
+
code: provResult.error.code
|
|
15883
|
+
});
|
|
15884
|
+
return recoveryFailure(provResult.error, {
|
|
15885
|
+
projectName,
|
|
15886
|
+
stage: `provision:${serviceType}`,
|
|
15887
|
+
resources: [
|
|
15888
|
+
...results.map(({ envVars: _envVars, ...resource }) => resource),
|
|
15889
|
+
...getDeployRecovery(provResult.error)?.resources ?? [
|
|
15890
|
+
{ serviceType, provisionerName: provisioner.name, resourceInfo: projectName, resourceStatus: "unknown" }
|
|
15891
|
+
]
|
|
15892
|
+
]
|
|
15860
15893
|
});
|
|
15861
|
-
return err(provResult.error);
|
|
15862
15894
|
}
|
|
15863
15895
|
results.push(provResult.data);
|
|
15864
15896
|
logger10.info("Service provisioned", { serviceType, provisioner: provisioner.name });
|
|
@@ -15885,36 +15917,44 @@ var deploy = {
|
|
|
15885
15917
|
);
|
|
15886
15918
|
}
|
|
15887
15919
|
let allEnvVars = {};
|
|
15920
|
+
const recovery = { projectName, stage: "provision", resources: [] };
|
|
15888
15921
|
if (!options?.skipProvision) {
|
|
15889
15922
|
const provResults = await deploy.provisionAll(projectName);
|
|
15890
15923
|
if (!provResults.success) {
|
|
15891
15924
|
return err(provResults.error);
|
|
15892
15925
|
}
|
|
15926
|
+
recovery.resources = provResults.data.map(({ envVars: _envVars, ...resource }) => resource);
|
|
15927
|
+
const reachProviders = [];
|
|
15893
15928
|
for (const prov of provResults.data) {
|
|
15929
|
+
if (prov.envVars.HAI_REACH_PROVIDERS)
|
|
15930
|
+
reachProviders.push(...JSON.parse(prov.envVars.HAI_REACH_PROVIDERS));
|
|
15894
15931
|
allEnvVars = { ...allEnvVars, ...prov.envVars };
|
|
15895
15932
|
}
|
|
15933
|
+
if (reachProviders.length > 0)
|
|
15934
|
+
allEnvVars.HAI_REACH_PROVIDERS = JSON.stringify(reachProviders);
|
|
15896
15935
|
}
|
|
15897
15936
|
const projectResult = await currentProvider.createProject(projectName);
|
|
15898
15937
|
if (!projectResult.success) {
|
|
15899
|
-
return
|
|
15938
|
+
return recoveryFailure(projectResult.error, { ...recovery, stage: "create-project" });
|
|
15900
15939
|
}
|
|
15901
15940
|
const projectId = projectResult.data;
|
|
15941
|
+
recovery.platformProjectId = projectId;
|
|
15902
15942
|
if (Object.keys(allEnvVars).length > 0) {
|
|
15903
15943
|
const envResult = await currentProvider.setEnvVars(projectId, allEnvVars);
|
|
15904
15944
|
if (!envResult.success) {
|
|
15905
|
-
return
|
|
15945
|
+
return recoveryFailure(envResult.error, { ...recovery, stage: "set-env" });
|
|
15906
15946
|
}
|
|
15907
15947
|
}
|
|
15908
15948
|
if (!options?.skipBuild) {
|
|
15909
|
-
const buildResult = buildApp(appDir, scan.buildCommand);
|
|
15949
|
+
const buildResult = buildApp(appDir, scan.buildCommand, allEnvVars);
|
|
15910
15950
|
if (!buildResult.success) {
|
|
15911
|
-
return
|
|
15951
|
+
return recoveryFailure(buildResult.error, { ...recovery, stage: "build" });
|
|
15912
15952
|
}
|
|
15913
15953
|
}
|
|
15914
15954
|
const outputDir = resolveOutputDir(appDir, currentConfig?.provider.type ?? "vercel");
|
|
15915
15955
|
const deployResult = await currentProvider.deploy(projectId, outputDir);
|
|
15916
15956
|
if (!deployResult.success) {
|
|
15917
|
-
return
|
|
15957
|
+
return recoveryFailure(deployResult.error, { ...recovery, stage: "deploy" });
|
|
15918
15958
|
}
|
|
15919
15959
|
const result = {
|
|
15920
15960
|
...deployResult.data,
|
|
@@ -15929,6 +15969,6 @@ var deploy = {
|
|
|
15929
15969
|
}
|
|
15930
15970
|
};
|
|
15931
15971
|
|
|
15932
|
-
export { DeployConfigSchema, HaiDeployError, ProviderConfigSchema, deploy };
|
|
15933
|
-
//# sourceMappingURL=src-
|
|
15934
|
-
//# sourceMappingURL=src-
|
|
15972
|
+
export { DeployConfigSchema, HaiDeployError, ProviderConfigSchema, deploy, getDeployRecovery };
|
|
15973
|
+
//# sourceMappingURL=src-6ZBWDBUB.js.map
|
|
15974
|
+
//# sourceMappingURL=src-6ZBWDBUB.js.map
|