@h-ai/deploy 0.1.0-alpha.66 → 0.1.0-alpha.68

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.d.ts CHANGED
@@ -2,11 +2,27 @@ import { z } from 'zod';
2
2
  import * as _h_ai_core from '@h-ai/core';
3
3
  import { HaiResult, HaiError } from '@h-ai/core';
4
4
 
5
- /** 镜像制品清单,例如 `{ version: 2, appVersion: '1.2.3', buildId: '1.2.3-build1', images: [{ name: 'app', file: 'app-1.2.3.tar', imageRef: 'localhost/demo:1.2.3-build1', sha256: '...' }] }`。 */
5
+ /**
6
+ * 镜像制品清单,例如 `{ version: 3, appVersion: '1.2.3', buildId: '1.2.3-build1', partial: false, hasWeb: false, images: [...] }`。
7
+ * 清单格式版本为 3;完整产物必须包含 app,声明 hasWeb 时还须包含 web。结构验证不能代替真实归档校验。
8
+ * @example
9
+ * ```ts
10
+ * import { readFile } from 'node:fs/promises'
11
+ * import { DeploymentArtifactSchema } from '@h-ai/deploy'
12
+ * const content = await readFile('.hai/build/docker/build-id/manifest.json', 'utf8')
13
+ * const parsed = DeploymentArtifactSchema.safeParse(JSON.parse(content))
14
+ * if (!parsed.success) throw new Error('Invalid artifact manifest')
15
+ * // Schema 只验证结构;文件路径与 SHA-256 核验使用 readDeploymentArtifact。
16
+ * ```
17
+ */
6
18
  declare const DeploymentArtifactSchema: z.ZodObject<{
7
- version: z.ZodLiteral<2>;
19
+ version: z.ZodLiteral<3>;
8
20
  appVersion: z.ZodString;
9
21
  buildId: z.ZodString;
22
+ partial: z.ZodDefault<z.ZodBoolean>;
23
+ hasWeb: z.ZodDefault<z.ZodBoolean>;
24
+ appContainerPort: z.ZodOptional<z.ZodNumber>;
25
+ webContainerPort: z.ZodOptional<z.ZodNumber>;
10
26
  images: z.ZodArray<z.ZodObject<{
11
27
  name: z.ZodEnum<{
12
28
  app: "app";
@@ -18,23 +34,50 @@ declare const DeploymentArtifactSchema: z.ZodObject<{
18
34
  containerPort: z.ZodOptional<z.ZodNumber>;
19
35
  }, z.core.$strip>>;
20
36
  }, z.core.$strip>;
21
- /** 构建器与部署器共享的镜像交付结构;参见 DeploymentArtifactSchema 示例。 */
37
+ /**
38
+ * 构建器与部署器共享的镜像交付结构;参见 DeploymentArtifactSchema 示例。
39
+ * @example
40
+ * ```ts
41
+ * import type { DeploymentArtifact } from '@h-ai/deploy'
42
+ * import { readDeploymentArtifact } from '@h-ai/deploy'
43
+ * const directory = '.hai/build/docker/build-id'
44
+ * const artifact = await readDeploymentArtifact(directory)
45
+ * const verified: DeploymentArtifact = artifact
46
+ * const references = verified.images.map(image => image.imageRef)
47
+ * ```
48
+ */
22
49
  type DeploymentArtifact = z.infer<typeof DeploymentArtifactSchema>;
23
50
  /**
24
51
  * 流式计算制品校验和,避免将大镜像一次性读入内存。
25
- * @example `const checksum = await artifactChecksum("/release/app-1.2.3.tar")`
52
+ * @throws Error - 文件读取失败;返回真实 tar 内容的 SHA-256,不校验清单其它字段。
53
+ * @param file - 真实镜像归档文件路径。
54
+ * @returns 文件内容的 SHA-256 十六进制摘要。
55
+ * @example
56
+ * ```ts
57
+ * import { artifactChecksum } from '@h-ai/deploy'
58
+ * const digest = await artifactChecksum('.hai/build/docker/build-id/app-1.0.0.tar')
59
+ * ```
26
60
  */
27
61
  declare function artifactChecksum(file: string): Promise<string>;
28
62
  /**
29
63
  * 校验完整构建产物;缺失、损坏或非法清单均抛错,由部署边界转换为 HaiResult。
30
- * @example `const artifact = await readDeploymentArtifact("/release/build-1")`
64
+ * @throws Error - 清单、路径、镜像 tar 或摘要校验失败;不能用未验证的清单绕过本函数。
65
+ * @param directory - 包含已准备制品或部署文件的目录;具体见函数用途。
66
+ * @returns 已经验证的构建制品清单。
67
+ * @example
68
+ * ```ts
69
+ * import { readDeploymentArtifact } from '@h-ai/deploy'
70
+ * const directory = '.hai/build/docker/build-id'
71
+ * const artifact = await readDeploymentArtifact(directory)
72
+ * const images = artifact.images.map(image => ({ name: image.name, imageRef: image.imageRef }))
73
+ * ```
31
74
  */
32
75
  declare function readDeploymentArtifact(directory: string): Promise<DeploymentArtifact>;
33
76
 
34
77
  /**
35
78
  * @h-ai/deploy — 部署配置 Schema
36
79
  *
37
- * 本文件定义部署模块的错误码、Zod Schema 和配置类型。
80
+ * 本文件定义部署模块的 Zod Schema 和配置类型;错误定义位于 deploy-types.ts。
38
81
  * @module deploy-config
39
82
  */
40
83
 
@@ -80,7 +123,17 @@ declare const DockerSshProviderConfigSchema: z.ZodObject<{
80
123
  hostPort: z.ZodNumber;
81
124
  }, z.core.$strip>;
82
125
  }, z.core.$strip>;
83
- /** 部署平台配置联合类型(后续可扩展更多平台) */
126
+ /**
127
+ * 部署平台配置联合类型(后续可扩展更多平台)
128
+ * 按 type 区分 Vercel 与 docker-ssh;仅校验配置形状,不验证远端连通性或云凭据。
129
+ * @example
130
+ * ```ts
131
+ * import { ProviderConfigSchema } from '@h-ai/deploy'
132
+ * const result = ProviderConfigSchema.safeParse({ type: 'docker-ssh', expose: { hostPort: 8080 } })
133
+ * if (!result.success) throw new Error('Invalid deployment provider')
134
+ * const provider = result.data
135
+ * ```
136
+ */
84
137
  declare const ProviderConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
85
138
  type: z.ZodLiteral<"vercel">;
86
139
  token: z.ZodString;
@@ -109,13 +162,45 @@ declare const ProviderConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
109
162
  hostPort: z.ZodNumber;
110
163
  }, z.core.$strip>;
111
164
  }, z.core.$strip>], "type">;
112
- /** SSH 连接配置(parse 后) */
165
+ /**
166
+ * SSH 连接配置(parse 后)
167
+ * 这是 Schema 解析后的类型,默认字段已必填;私钥路径可省略以使用 ssh-agent/默认密钥。
168
+ * @example
169
+ * ```ts
170
+ * import type { SshConfig } from '@h-ai/deploy'
171
+ * const config: SshConfig = { host: 'deploy.example.net', username: 'deploy', port: 22, strictHostKeyChecking: true, connectTimeoutSeconds: 10 }
172
+ * ```
173
+ */
113
174
  type SshConfig = z.infer<typeof SshConfigSchema>;
114
- /** 端口暴露配置(parse 后) */
175
+ /**
176
+ * 端口暴露配置(parse 后)
177
+ * @example
178
+ * ```ts
179
+ * import type { PortExposeConfig } from '@h-ai/deploy'
180
+ * const ports: PortExposeConfig = { type: 'port', hostPort: 8080, containerPort: 3000 }
181
+ * ```
182
+ */
115
183
  type PortExposeConfig = z.infer<typeof PortExposeSchema>;
116
- /** docker-ssh 部署平台配置(parse 后) */
184
+ /**
185
+ * docker-ssh 部署平台配置(parse 后)
186
+ * SSH 可省略用于离线打包;真正在线发布必须提供 SSH 配置。
187
+ * @example
188
+ * ```ts
189
+ * import type { DockerSshProviderConfig } from '@h-ai/deploy'
190
+ * const provider: DockerSshProviderConfig = { type: 'docker-ssh', remote: { baseDir: '/opt/hai/apps', runtime: 'auto' }, expose: { type: 'port', hostPort: 8080, containerPort: 3000 } }
191
+ * ```
192
+ */
117
193
  type DockerSshProviderConfig = z.infer<typeof DockerSshProviderConfigSchema>;
118
- /** 部署模块完整配置 Schema */
194
+ /**
195
+ * 部署模块完整配置 Schema
196
+ * @example
197
+ * ```ts
198
+ * import { DeployConfigSchema } from '@h-ai/deploy'
199
+ * const parsed = DeployConfigSchema.safeParse({ environment: 'prod', provider: { type: 'docker-ssh', expose: { hostPort: 8080 } } })
200
+ * if (!parsed.success) throw new Error('Invalid deployment configuration')
201
+ * const baseDir = parsed.data.provider.type === 'docker-ssh' ? parsed.data.provider.remote.baseDir : undefined
202
+ * ```
203
+ */
119
204
  declare const DeployConfigSchema: z.ZodObject<{
120
205
  environment: z.ZodOptional<z.ZodString>;
121
206
  provider: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -182,14 +267,36 @@ declare const DeployConfigSchema: z.ZodObject<{
182
267
  }>>;
183
268
  }, z.core.$strip>>;
184
269
  }, z.core.$strip>;
185
- /** 部署配置类型(parse 后) */
270
+ /**
271
+ * 部署配置类型(parse 后)
272
+ * @example
273
+ * ```ts
274
+ * import type { DeployConfig } from '@h-ai/deploy'
275
+ * const config: DeployConfig = { environment: 'prod', provider: { type: 'docker-ssh', remote: { baseDir: '/opt/hai/apps', runtime: 'auto' }, expose: { type: 'port', hostPort: 8080, containerPort: 3000 } } }
276
+ * ```
277
+ */
186
278
  type DeployConfig = z.infer<typeof DeployConfigSchema>;
187
- /** 部署配置输入类型(用户输入) */
279
+ /**
280
+ * 部署配置输入类型(用户输入)
281
+ * @example
282
+ * ```ts
283
+ * import type { DeployConfigInput } from '@h-ai/deploy'
284
+ * const input: DeployConfigInput = { provider: { type: 'docker-ssh', expose: { hostPort: 8080 } } } // 由 init 应用 remote 等默认值。
285
+ * ```
286
+ */
188
287
  type DeployConfigInput = z.input<typeof DeployConfigSchema>;
189
288
 
190
289
  /** 部署环境的加载和持久化;公开环境文件不保存真实凭据。 */
191
290
 
192
- /** 部署环境快照;例如 prod 的 targetValues 只来自 `.env.prod`,不包含基础/私有覆盖。 */
291
+ /**
292
+ * 部署环境快照;例如 prod 的 targetValues 只来自 `.env.prod`,不包含基础/私有覆盖。
293
+ * @example
294
+ * ```ts
295
+ * import type { DeploymentEnvironment } from '@h-ai/deploy'
296
+ * const environment: DeploymentEnvironment = { environment: 'prod', envDir: '/workspace/shop', values: { HAI_DB_TYPE: 'postgresql' }, targetValues: { HAI_DB_TYPE: 'postgresql' } }
297
+ * // values 可能还含私有凭据,不能整体写入日志。
298
+ * ```
299
+ */
193
300
  interface DeploymentEnvironment {
194
301
  /** 文件模式(dev/test/prod 或其他合法模式名)。 */
195
302
  environment: string;
@@ -207,17 +314,32 @@ interface DeploymentEnvironment {
207
314
  * @param path - YAML 文件绝对路径
208
315
  * @param values - 已加载的目标环境变量
209
316
  * @returns 配置对象;解析错误已脱敏
210
- * @example `loadDeploymentConfig('deploy', '/app/config/_deploy.yml', environment.values)`
317
+ * @example
318
+ * ```ts
319
+ * import { loadDeploymentConfig, loadDeploymentEnvironment } from '@h-ai/deploy'
320
+ * const environment = loadDeploymentEnvironment({ environment: 'prod', envDir: process.cwd() })
321
+ * if (!environment.success) throw new Error(environment.error.message)
322
+ * const config = loadDeploymentConfig('deploy', `${process.cwd()}/config/_deploy.yml`, environment.data.values)
323
+ * if (!config.success) throw new Error(config.error.message)
324
+ * // 返回对象仍需经 DeployConfigSchema 或 deploy.init 验证。
325
+ * ```
211
326
  */
212
327
  declare function loadDeploymentConfig(name: string, path: string, values: Record<string, string | undefined>): HaiResult<Record<string, unknown>>;
213
328
  /**
214
329
  * 加载部署环境;指定环境文件缺失时返回错误并要求用户创建。
330
+ * 进程环境优先于 .env.local、目标环境文件、基础文件;targetValues 只记录目标文件显式项,不包含其它覆盖。
215
331
  * @param options - 模式、环境目录与是否强制存在目标文件
216
332
  * @param options.environment - dev/test/prod 或自定义模式
217
333
  * @param options.envDir - 环境文件所在目录
218
334
  * @param options.requireFile - 默认 true;扫描无显式环境时可只读取已有基础文件
219
335
  * @returns 环境快照,可能包含私有值,不得直接输出
220
- * @example `loadDeploymentEnvironment({ environment: 'prod', envDir: '.' })`
336
+ * @example
337
+ * ```ts
338
+ * import { loadDeploymentEnvironment } from '@h-ai/deploy'
339
+ * const loaded = loadDeploymentEnvironment({ environment: 'prod', envDir: process.cwd() })
340
+ * if (!loaded.success) throw new Error(loaded.error.message)
341
+ * const explicitlyConfigured = loaded.data.targetValues.HAI_DB_TYPE
342
+ * ```
221
343
  */
222
344
  declare function loadDeploymentEnvironment(options: {
223
345
  environment: string;
@@ -232,13 +354,32 @@ declare function loadDeploymentEnvironment(options: {
232
354
  * @param options - private 为 true 时所有值只保存在本机;适用于 SSH 地址、端口及密钥路径
233
355
  * @param options.private - 是否将全部值仅保存到 .env.local
234
356
  * @returns 已保存的变量名或脱敏错误
235
- * @example `saveDeploymentEnvironment(snapshot, { HAI_DB_TYPE: 'postgresql' })`
236
- * @example `saveDeploymentEnvironment(snapshot, { HAI_DEPLOY_PROVIDER: JSON.stringify(provider) }, { private: true })`
357
+ * @example
358
+ * ```ts
359
+ * import { loadDeploymentEnvironment, saveDeploymentEnvironment } from '@h-ai/deploy'
360
+ * const loaded = loadDeploymentEnvironment({ environment: 'prod', envDir: process.cwd() })
361
+ * if (!loaded.success) throw new Error(loaded.error.message)
362
+ * const saved = saveDeploymentEnvironment(loaded.data, { HAI_DB_HOST: 'db' })
363
+ * if (!saved.success) throw new Error(saved.error.message)
364
+ * // 密钥应以 { private: true } 写入被忽略的 .env.local,不能放公共环境文件。
365
+ * ```
237
366
  */
238
367
  declare function saveDeploymentEnvironment(environment: DeploymentEnvironment, values: Record<string, string>, options?: {
239
368
  private?: boolean;
240
369
  }): HaiResult<string[]>;
241
370
 
371
+ /**
372
+ *
373
+ * @example
374
+ * ```ts
375
+ * import { err } from '@h-ai/core'
376
+ * import { HaiDeployError } from '@h-ai/deploy'
377
+ * const result = err(HaiDeployError.CONFIG_ERROR, 'Invalid deployment configuration')
378
+ * if (!result.success && result.error.code === HaiDeployError.CONFIG_ERROR.code) {
379
+ * process.stderr.write(result.error.message + '\n')
380
+ * }
381
+ * ```
382
+ */
242
383
  declare const HaiDeployError: {
243
384
  readonly DEPLOY_FAILED: _h_ai_core.HaiErrorDef;
244
385
  readonly PROJECT_CREATE_FAILED: _h_ai_core.HaiErrorDef;
@@ -261,15 +402,12 @@ declare const HaiDeployError: {
261
402
  /**
262
403
  * 部署错误接口
263
404
  *
264
- * 所有部署操作返回的错误都遵循此接口。
265
- *
405
+ * 用于组合错误定义、消息与可选 cause 的描述对象;业务操作返回的 error 遵循 core.HaiError,请按 success 分支读取 code/message。
266
406
  * @example
267
407
  * ```ts
268
- * const result = await deploy.deployApp('./apps/my-app')
269
- * if (!result.success) {
270
- * const error: DeployError = result.error
271
- * // 根据 error.code 处理
272
- * }
408
+ * import type { DeployError } from '@h-ai/deploy'
409
+ * import { HaiDeployError } from '@h-ai/deploy'
410
+ * const detail: DeployError = { def: HaiDeployError.DEPLOY_FAILED, message: 'Deployment failed' }
273
411
  * ```
274
412
  */
275
413
  interface DeployError {
@@ -280,18 +418,47 @@ interface DeployError {
280
418
  /** 原始错误(可选) */
281
419
  cause?: unknown;
282
420
  }
283
- /** 基础设施服务类型 */
421
+ /**
422
+ * 基础设施服务类型
423
+ * @example
424
+ * ```ts
425
+ * import type { ServiceType } from '@h-ai/deploy'
426
+ * const service: ServiceType = 'db'
427
+ * ```
428
+ */
284
429
  type ServiceType = 'db' | 'cache' | 'storage' | 'email' | 'sms' | 'vecdb' | 'ldap';
285
- /** 每项中间件的处理方式;keep 不修改配置,也不开通服务。 */
430
+ /**
431
+ * 每项中间件的处理方式;keep 不修改配置,也不开通服务。
432
+ * @example
433
+ * ```ts
434
+ * import type { MiddlewareMode } from '@h-ai/deploy'
435
+ * const mode: MiddlewareMode = 'keep' // 复用已有服务,不修改连接配置或自动开通。
436
+ * ```
437
+ */
286
438
  type MiddlewareMode = 'default' | 'custom' | 'keep';
287
- /** 扫描环境;例如 `{ environment: 'test', envDir: '/workspace/app' }`。 */
439
+ /**
440
+ * 扫描环境;例如 `{ environment: 'test', envDir: '/workspace/app' }`。
441
+ * @example
442
+ * ```ts
443
+ * import type { DeployScanOptions } from '@h-ai/deploy'
444
+ * const options: DeployScanOptions = { environment: 'prod', envDir: '/workspace/shop' }
445
+ * ```
446
+ */
288
447
  interface DeployScanOptions {
289
448
  /** 环境文件模式。 */
290
449
  environment?: string;
291
450
  /** 环境文件目录;默认自动定位到应用或 workspace 根。 */
292
451
  envDir?: string;
293
452
  }
294
- /** 依赖对应的中间件;例如 IAM 的 LDAP 位于 `iam.ldap`,可处于 disabled 状态。 */
453
+ /**
454
+ * 依赖对应的中间件;例如 IAM 的 LDAP 位于 `iam.ldap`,可处于 disabled 状态。
455
+ * @example
456
+ * ```ts
457
+ * import type { MiddlewareRequirement } from '@h-ai/deploy'
458
+ * const requirement: MiddlewareRequirement = { service: 'db', backend: 'postgresql', source: '@h-ai/reldb', configName: 'db', configPath: [], envPrefix: 'HAI_DB', config: { host: 'db', database: 'shop' } }
459
+ * // config 可能含私有值;展示时只选择安全字段。
460
+ * ```
461
+ */
295
462
  interface MiddlewareRequirement {
296
463
  /** 中间件类别。 */
297
464
  service: ServiceType;
@@ -312,6 +479,16 @@ interface MiddlewareRequirement {
312
479
  * 应用扫描结果
313
480
  *
314
481
  * 由 scanner 分析应用目录后返回,描述应用的框架类型、依赖和部署需求。
482
+ * 扫描是文件系统能力,不依赖 init,也不代表远程服务已经连通。
483
+ * @example
484
+ * ```ts
485
+ * import type { ScanResult } from '@h-ai/deploy'
486
+ * import { deploy } from '@h-ai/deploy'
487
+ * const result = await deploy.scan(process.cwd(), { environment: 'prod' })
488
+ * if (!result.success) throw new Error(result.error.message)
489
+ * const scan: ScanResult = result.data
490
+ * const services = scan.requiredServices
491
+ * ```
315
492
  */
316
493
  interface ScanResult {
317
494
  /** 调用者指定的部署目录,对应 lockfile 中的一个 importer。 */
@@ -339,6 +516,12 @@ interface ScanResult {
339
516
  * 部署结果
340
517
  *
341
518
  * 包含部署后的访问 URL、状态和已设置的环境变量。
519
+ * @example
520
+ * ```ts
521
+ * import type { DeployResult } from '@h-ai/deploy'
522
+ * const packaged: DeployResult = { url: '', deploymentId: 'deploy-shop-001', status: 'packaged', bundlePath: '/workspace/shop/.hai/deploy/shop-1.0.0.tar', envVarsSet: [], provider: 'docker-ssh', healthCheck: 'skipped' }
523
+ * // packaged 只代表交付包生成;不将其展示为服务已上线。
524
+ * ```
342
525
  */
343
526
  interface DeployResult {
344
527
  /** 部署后的访问 URL */
@@ -362,6 +545,12 @@ interface DeployResult {
362
545
  * 基础设施开通结果
363
546
  *
364
547
  * 由 Provisioner 开通服务后返回,包含要注入到部署平台的环境变量。
548
+ * @example
549
+ * ```ts
550
+ * import type { ProvisionResult } from '@h-ai/deploy'
551
+ * const reused: ProvisionResult = { resourceStatus: 'reused', serviceType: 'db', provisionerName: 'neon', envVars: { HAI_DB_TYPE: 'postgresql' }, resourceInfo: 'Existing shop database' }
552
+ * // envVars 可能含密码;不能直接输出。
553
+ * ```
365
554
  */
366
555
  interface ProvisionResult {
367
556
  /** 本次新建或复用;凭证型服务始终为 reused */
@@ -375,7 +564,15 @@ interface ProvisionResult {
375
564
  /** 资源标识信息(如项目名、数据库名等),用于日志和展示 */
376
565
  resourceInfo: string;
377
566
  }
378
- /** 部署失败后的安全恢复信息,不含环境变量值或原始异常。 */
567
+ /**
568
+ * 部署失败后的安全恢复信息,不含环境变量值或原始异常。
569
+ * @example
570
+ * ```ts
571
+ * import type { DeployRecoveryInfo } from '@h-ai/deploy'
572
+ * const recovery: DeployRecoveryInfo = { projectName: 'shop', stage: 'provision', resources: [{ serviceType: 'db', provisionerName: 'neon', resourceInfo: 'shop', resourceStatus: 'unknown' }] }
573
+ * // 先在供应商控制台核对 unknown 资源;避免重复创建计费资源。
574
+ * ```
575
+ */
379
576
  interface DeployRecoveryInfo {
380
577
  projectName: string;
381
578
  stage: string;
@@ -387,8 +584,47 @@ interface DeployRecoveryInfo {
387
584
  resourceStatus: 'created' | 'reused' | 'unknown';
388
585
  }>;
389
586
  }
390
- /** 部署选项 */
587
+ /**
588
+ * 命令失败后的已核验执行状态;远端 finished 仅指部署脚本及恢复已完成,不代表容器停止。
589
+ * @example
590
+ * ```ts
591
+ * import type { DeployCommandState } from '@h-ai/deploy'
592
+ * const state: DeployCommandState = { reason: 'timeout', localTermination: 'confirmed', remoteExecution: 'unknown', deploymentId: 'deploy-shop-001', resultFile: '/opt/hai/apps/shop/results/deploy-shop-001' }
593
+ * if (state.remoteExecution === 'unknown') process.stderr.write('Verify the matching remote deployment result before retrying\n')
594
+ * ```
595
+ */
596
+ interface DeployCommandState {
597
+ /** 失败触发原因。 */
598
+ reason: 'timeout' | 'output' | 'input' | 'spawn' | 'exit' | 'configuration';
599
+ /** 本地受管理树已全部退出、无法证明,或尚未启动。 */
600
+ localTermination: 'confirmed' | 'unknown' | 'not-started';
601
+ /** SSH 失败默认 unknown;读取匹配部署 ID 的完整终态结果后才为 finished。 */
602
+ remoteExecution?: 'unknown' | 'finished';
603
+ /** 本次不可变部署 ID,可与服务器结果核对。 */
604
+ deploymentId?: string;
605
+ /** 服务器端结果文件;无凭据。 */
606
+ resultFile?: string;
607
+ /** 匹配终态结果中的真实脚本退出码。 */
608
+ remoteExitCode?: number;
609
+ /** 匹配终态结果中的最后阶段。 */
610
+ remoteStage?: string;
611
+ }
612
+ /**
613
+ * 部署选项
614
+ * 仅更新指定服务时仍受完整/部分制品与首次部署约束;bundleOnly 不触发 SSH。
615
+ * @example
616
+ * ```ts
617
+ * import type { DeployAppOptions } from '@h-ai/deploy'
618
+ * const options: DeployAppOptions = { environment: 'prod', bundleOnly: true, artifactDir: '.hai/build/docker/build-id', selectedServices: ['app'], output: '.hai/deploy/shop.tar' }
619
+ * ```
620
+ */
391
621
  interface DeployAppOptions {
622
+ /** 接收实际执行阶段,用于终端进度显示;不包含凭据或配置原文。 */
623
+ onProgress?: (stage: 'packaging' | 'preflight' | 'images' | 'dependencies' | 'start' | 'health' | 'complete') => void;
624
+ /** 只更新指定的 Compose 服务;省略时完整制品部署全部,部分制品只部署其镜像。 */
625
+ selectedServices?: string[];
626
+ /** 打包中间件镜像所用的本地引擎,auto 优先 Podman。 */
627
+ runtime?: 'auto' | 'podman' | 'docker';
392
628
  /** 部署环境标识;显式指定时必须存在 .env.<environment>。 */
393
629
  environment?: string;
394
630
  /** 环境文件所在目录,默认自动定位。 */
@@ -410,15 +646,58 @@ interface DeployAppOptions {
410
646
  * Deploy 凭证文件操作。
411
647
  *
412
648
  * 这些方法仅操作 `~/.hai/credentials.yml`,不依赖 `deploy.init()`。
649
+ * @example
650
+ * ```ts
651
+ * import type { DeployCredentialOperations } from '@h-ai/deploy'
652
+ * import { deploy } from '@h-ai/deploy'
653
+ * const credentials: DeployCredentialOperations = deploy.credentials
654
+ * const path = credentials.getPath() // 无需先 init。
655
+ * ```
413
656
  */
414
657
  interface DeployCredentialOperations {
415
- /** 获取凭证文件绝对路径。 */
658
+ /**
659
+ * 获取凭证文件绝对路径。
660
+ * @example
661
+ * ```ts
662
+ * import { deploy } from '@h-ai/deploy'
663
+ * const path = deploy.credentials.getPath() // 当前用户的全局凭证文件。
664
+ * ```
665
+ */
416
666
  getPath: () => string;
417
- /** 加载凭证文件并注入 `process.env`。 */
667
+ /**
668
+ * 加载凭证文件并注入 `process.env`。
669
+ * @example
670
+ * ```ts
671
+ * import { deploy } from '@h-ai/deploy'
672
+ * const loaded = deploy.credentials.load()
673
+ * if (!loaded.success) throw new Error(loaded.error.message)
674
+ * const names = loaded.data // 不输出被注入的值。
675
+ * ```
676
+ */
418
677
  load: () => HaiResult<string[]>;
419
- /** 保存单个凭证。 */
678
+ /**
679
+ * 保存单个凭证。
680
+ * @example
681
+ * ```ts
682
+ * import { deploy } from '@h-ai/deploy'
683
+ * const token = process.env.HAI_DEPLOY_PROVIDER_TOKEN
684
+ * if (!token) throw new Error('Provide HAI_DEPLOY_PROVIDER_TOKEN privately')
685
+ * const saved = deploy.credentials.save('HAI_DEPLOY_PROVIDER_TOKEN', token)
686
+ * if (!saved.success) throw new Error(saved.error.message)
687
+ * ```
688
+ */
420
689
  save: (key: string, value: string) => HaiResult<void>;
421
- /** 批量保存凭证。 */
690
+ /**
691
+ * 批量保存凭证。
692
+ * @example
693
+ * ```ts
694
+ * import { deploy } from '@h-ai/deploy'
695
+ * const token = process.env.HAI_DEPLOY_PROVIDER_TOKEN
696
+ * if (!token) throw new Error('Provide HAI_DEPLOY_PROVIDER_TOKEN privately')
697
+ * const saved = deploy.credentials.saveAll({ HAI_DEPLOY_PROVIDER_TOKEN: token })
698
+ * if (!saved.success) throw new Error(saved.error.message)
699
+ * ```
700
+ */
422
701
  saveAll: (entries: Record<string, string>) => HaiResult<void>;
423
702
  }
424
703
  /**
@@ -431,25 +710,51 @@ interface DeployCredentialOperations {
431
710
  * - `deploy.provisionAll()` — 开通所有已配置的基础设施
432
711
  * - `deploy.deployApp()` — 完整部署流程
433
712
  * - `deploy.close()` — 关闭模块
434
- *
713
+ * scan 与 credentials 不需要 init;部署与开通遵循 init → 检查结果 → use → finally close。close 返回 Promise<void>,不伪造 HaiResult。
435
714
  * @example
436
715
  * ```ts
716
+ * import type { DeployFunctions } from '@h-ai/deploy'
437
717
  * import { deploy } from '@h-ai/deploy'
438
- *
439
- * await deploy.init({ provider: { type: 'vercel', token: 'xxx' } })
440
- * const result = await deploy.deployApp('./apps/my-app')
441
- * if (result.success) {
442
- * console.log(`Deployed: ${result.data.url}`)
443
- * }
444
- * await deploy.close()
718
+ * const operations: DeployFunctions = deploy
719
+ * const scan = await operations.scan(process.cwd())
720
+ * if (!scan.success) throw new Error(scan.error.message)
721
+ * const required = scan.data.requiredServices
445
722
  * ```
446
723
  */
447
724
  interface DeployFunctions {
448
725
  /** 全局部署凭证文件操作。 */
449
726
  readonly credentials: DeployCredentialOperations;
450
- /** 初始化模块(加载配置、创建 Provider 和 Provisioner 实例) */
727
+ /**
728
+ * 初始化模块(加载配置、创建 Provider 和 Provisioner 实例)
729
+ * @example
730
+ * ```ts
731
+ * import { deploy } from '@h-ai/deploy'
732
+ * const initialized = await deploy.init({ provider: { type: 'docker-ssh', expose: { hostPort: 8080 } } })
733
+ * if (!initialized.success) throw new Error(initialized.error.message)
734
+ * try {
735
+ * const ready = deploy.isInitialized
736
+ * }
737
+ * finally {
738
+ * await deploy.close()
739
+ * }
740
+ * ```
741
+ */
451
742
  init: (config: DeployConfigInput) => Promise<HaiResult<void>>;
452
- /** 关闭模块 */
743
+ /**
744
+ * 关闭模块
745
+ * @example
746
+ * ```ts
747
+ * import { deploy } from '@h-ai/deploy'
748
+ * const initialized = await deploy.init({ provider: { type: 'docker-ssh', expose: { hostPort: 8080 } } })
749
+ * if (!initialized.success) throw new Error(initialized.error.message)
750
+ * try {
751
+ * const provider = deploy.config?.provider.type
752
+ * }
753
+ * finally {
754
+ * await deploy.close() // Promise<void>;不检查不存在的 success 字段。
755
+ * }
756
+ * ```
757
+ */
453
758
  close: () => Promise<void>;
454
759
  /** 当前配置 */
455
760
  readonly config: DeployConfig | null;
@@ -461,6 +766,13 @@ interface DeployFunctions {
461
766
  * 这是纯文件系统分析能力,不依赖云端认证状态,可在 `deploy.init()` 之前调用。
462
767
  *
463
768
  * @param appDir - 应用根目录路径
769
+ * @example
770
+ * ```ts
771
+ * import { deploy } from '@h-ai/deploy'
772
+ * const scanned = await deploy.scan(process.cwd(), { environment: 'prod' })
773
+ * if (!scanned.success) throw new Error(scanned.error.message)
774
+ * const services = scanned.data.requiredServices // 无需 init。
775
+ * ```
464
776
  */
465
777
  scan: (appDir: string, options?: DeployScanOptions) => Promise<HaiResult<ScanResult>>;
466
778
  /**
@@ -468,6 +780,28 @@ interface DeployFunctions {
468
780
  *
469
781
  * @param projectName - 用于资源命名的项目名
470
782
  * @returns 所有 Provisioner 的结果列表
783
+ * @example
784
+ * ```ts
785
+ * import { deploy, getDeployRecovery } from '@h-ai/deploy'
786
+ * const apiKey = process.env.HAI_DEPLOY_SERVICES_DB_APIKEY
787
+ * if (!apiKey) throw new Error('Provide HAI_DEPLOY_SERVICES_DB_APIKEY privately')
788
+ * const initialized = await deploy.init({ provider: { type: 'docker-ssh', expose: { hostPort: 8080 } }, services: { db: { provisioner: 'neon', apiKey } } })
789
+ * if (!initialized.success) throw new Error(initialized.error.message)
790
+ * try {
791
+ * // 会访问真实云服务并可能新建资源。
792
+ * const result = await deploy.provisionAll('shop', ['db'])
793
+ * if (!result.success) {
794
+ * const recovery = getDeployRecovery(result.error)
795
+ * if (recovery?.resources.some(resource => resource.resourceStatus === 'unknown')) {
796
+ * process.stderr.write('Verify unknown resources at the provider before retrying\n')
797
+ * }
798
+ * throw new Error(result.error.message)
799
+ * }
800
+ * }
801
+ * finally {
802
+ * await deploy.close()
803
+ * }
804
+ * ```
471
805
  */
472
806
  provisionAll: (projectName: string, services?: ServiceType[]) => Promise<HaiResult<ProvisionResult[]>>;
473
807
  /**
@@ -475,6 +809,21 @@ interface DeployFunctions {
475
809
  *
476
810
  * @param appDir - 应用根目录路径
477
811
  * @param options - 部署选项
812
+ * @example
813
+ * ```ts
814
+ * import { deploy } from '@h-ai/deploy'
815
+ * const initialized = await deploy.init({ provider: { type: 'docker-ssh', expose: { hostPort: 8080 } } })
816
+ * if (!initialized.success) throw new Error(initialized.error.message)
817
+ * try {
818
+ * // 当前项目必须已有构建镜像清单与生产环境配置。
819
+ * const result = await deploy.deployApp(process.cwd(), { environment: 'prod', bundleOnly: true })
820
+ * if (!result.success) throw new Error(result.error.message)
821
+ * const status = result.data.status // packaged 不代表线上运行。
822
+ * }
823
+ * finally {
824
+ * await deploy.close()
825
+ * }
826
+ * ```
478
827
  */
479
828
  deployApp: (appDir: string, options?: DeployAppOptions) => Promise<HaiResult<DeployResult>>;
480
829
  }
@@ -488,14 +837,29 @@ interface DeployFunctions {
488
837
 
489
838
  /**
490
839
  * Deploy 模块单例
491
- *
840
+ * 每次 init 检查 HaiResult;只打包不会连接 SSH,成功打包也不代表服务器已部署。远端 unknown 必须先核对匹配部署 ID 的结果,不能盲目重试。
492
841
  * @example
493
842
  * ```ts
494
- * import { deploy } from '@h-ai/deploy'
495
- *
496
- * await deploy.init({ provider: { type: 'vercel', token: 'xxx' } })
497
- * const result = await deploy.deployApp('./apps/my-app')
498
- * await deploy.close()
843
+ * import { deploy, getDeployCommandState } from '@h-ai/deploy'
844
+ * const initialized = await deploy.init({ provider: { type: 'docker-ssh', expose: { hostPort: 8080 } } })
845
+ * if (!initialized.success) throw new Error(initialized.error.message)
846
+ * try {
847
+ * // 当前项目已有镜像产物与部署环境;只打包,不连接服务器。
848
+ * const result = await deploy.deployApp(process.cwd(), { environment: 'prod', bundleOnly: true })
849
+ * if (!result.success) {
850
+ * const state = getDeployCommandState(result.error)
851
+ * if (state?.remoteExecution === 'unknown') {
852
+ * process.stderr.write('Inspect the matching server result before retrying\n')
853
+ * }
854
+ * throw new Error(result.error.message)
855
+ * }
856
+ * if (result.data.status === 'packaged') {
857
+ * const bundle = result.data.bundlePath
858
+ * }
859
+ * }
860
+ * finally {
861
+ * await deploy.close()
862
+ * }
499
863
  * ```
500
864
  */
501
865
  declare const deploy: DeployFunctions;
@@ -508,17 +872,16 @@ declare const deploy: DeployFunctions;
508
872
  */
509
873
  /**
510
874
  * 获取部署模块文案
511
- *
875
+ * 使用 core 请求/全局 locale 的现有中英字典;不自行缓存调用时的语言。
512
876
  * @param key - 文案 Key
513
877
  * @param options - 插值参数与格式化选项
514
878
  * @returns 本地化后的文案字符串
515
- *
516
- * @example
879
+ * @example 模块内部上下文
517
880
  * ```ts
518
- * const text = deployM('deploy_notInitialized')
881
+ * const message = deployM('deploy_sshRequired')
519
882
  * ```
520
883
  */
521
- declare const deployM: (key: "deploy_notInitialized" | "deploy_initFailed" | "deploy_unsupportedProviderType" | "deploy_unsupportedProvisionerType" | "deploy_configTokenRequired" | "deploy_authRequired" | "deploy_authFailed" | "deploy_projectCreateFailed" | "deploy_uploadFailed" | "deploy_deployFailed" | "deploy_provisionFailed" | "deploy_adapterMissing" | "deploy_scanFailed" | "deploy_envVarFailed" | "deploy_credentialError" | "deploy_credentialInvalidKey" | "deploy_credentialSaved" | "deploy_credentialLoaded" | "deploy_configError" | "deploy_credentialMissing" | "deploy_apiError" | "deploy_fileUploadFailed" | "deploy_provisionNoResult" | "deploy_recoveryFailure" | "deploy_recoverySuggestion" | "deploy_commandTimeout" | "deploy_commandFailed" | "deploy_remoteConnectFailed" | "deploy_remoteRuntimeMissing" | "deploy_imageTransferFailed" | "deploy_healthCheckFailed" | "deploy_invalidEnvironment" | "deploy_environmentMissing" | "deploy_environmentReadFailed" | "deploy_environmentWriteFailed" | "deploy_multipleApplications" | "deploy_purposeSqlite" | "deploy_purposeDatabase" | "deploy_purposeMemory" | "deploy_purposeLocal" | "deploy_purposeCache" | "deploy_purposeStorage" | "deploy_purposeVector" | "deploy_purposeDisabled" | "deploy_purposeLdap" | "deploy_purposeConsole" | "deploy_purposeEmail" | "deploy_purposeSms" | "deploy_webProxyInvalid" | "deploy_artifactInvalid" | "deploy_sshRequired" | "deploy_bundleInstructions" | "deploy_bundleMetadata" | "deploy_bundleOnlyContainer", options?: _h_ai_core.MessageOptions) => string;
884
+ declare const deployM: (key: "deploy_notInitialized" | "deploy_initFailed" | "deploy_unsupportedProviderType" | "deploy_unsupportedProvisionerType" | "deploy_configTokenRequired" | "deploy_authRequired" | "deploy_authFailed" | "deploy_projectCreateFailed" | "deploy_uploadFailed" | "deploy_deployFailed" | "deploy_provisionFailed" | "deploy_adapterMissing" | "deploy_scanFailed" | "deploy_envVarFailed" | "deploy_credentialError" | "deploy_credentialInvalidKey" | "deploy_credentialSaved" | "deploy_credentialLoaded" | "deploy_configError" | "deploy_credentialMissing" | "deploy_apiError" | "deploy_fileUploadFailed" | "deploy_provisionNoResult" | "deploy_recoveryFailure" | "deploy_recoverySuggestion" | "deploy_commandTimeout" | "deploy_commandFailed" | "deploy_remoteConnectFailed" | "deploy_remoteRuntimeMissing" | "deploy_imageTransferFailed" | "deploy_healthCheckFailed" | "deploy_invalidEnvironment" | "deploy_environmentMissing" | "deploy_environmentReadFailed" | "deploy_environmentWriteFailed" | "deploy_multipleApplications" | "deploy_purposeSqlite" | "deploy_purposeDatabase" | "deploy_purposeMemory" | "deploy_purposeLocal" | "deploy_purposeCache" | "deploy_purposeStorage" | "deploy_purposeVector" | "deploy_purposeDisabled" | "deploy_purposeLdap" | "deploy_purposeConsole" | "deploy_purposeEmail" | "deploy_purposeSms" | "deploy_webProxyInvalid" | "deploy_artifactInvalid" | "deploy_sshRequired" | "deploy_bundleInstructions" | "deploy_bundleMetadata" | "deploy_targetHost" | "deploy_targetPort" | "deploy_targetUsername" | "deploy_targetIdentityFile" | "deploy_targetKnownHostsFile" | "deploy_targetStrictHostKeyChecking" | "deploy_targetConnectTimeoutSeconds" | "deploy_targetDirectory" | "deploy_targetBundle" | "deploy_bundleOnlyContainer" | "deploy_selectedContainerOnly" | "deploy_selectionInvalid" | "deploy_storageArtifactRequired" | "deploy_imageVersionRequired" | "deploy_bundleRuntimeRequired" | "deploy_bundleFailed" | "deploy_configFileInvalid" | "deploy_configFileUnreadable" | "deploy_bundleNameInvalid" | "deploy_secretsInvalid" | "deploy_localStopped" | "deploy_localUnknown" | "deploy_localNotStarted" | "deploy_commandOutputFailed" | "deploy_commandInputFailed" | "deploy_commandTreeActive" | "deploy_commandExecutionFailed" | "deploy_remoteUnknown" | "deploy_remoteFinished", options?: _h_ai_core.MessageOptions) => string;
522
885
 
523
886
  /**
524
887
  * 部署中间件的后端与连接字段目录,供扫描、CLI 和容器规划共同使用。
@@ -526,7 +889,14 @@ declare const deployM: (key: "deploy_notInitialized" | "deploy_initFailed" | "de
526
889
  * @module deploy-middleware
527
890
  */
528
891
 
529
- /** 连接字段;例如 `{ name: 'port', defaultValue: '5432', kind: 'port' }`。 */
892
+ /**
893
+ * 连接字段;例如 `{ name: 'port', defaultValue: '5432', kind: 'port' }`。
894
+ * @example
895
+ * ```ts
896
+ * import type { MiddlewareField } from '@h-ai/deploy'
897
+ * const field: MiddlewareField = { name: 'port', defaultValue: '5432', required: true, kind: 'port' }
898
+ * ```
899
+ */
530
900
  interface MiddlewareField {
531
901
  /** 模块配置字段名,camelCase 不拆词。 */
532
902
  name: string;
@@ -543,7 +913,14 @@ interface MiddlewareField {
543
913
  /** 数值上限;例如 Redis db 为 15。 */
544
914
  maximum?: number;
545
915
  }
546
- /** 后端目录项;例如 PostgreSQL 对应 `backend: 'postgresql'`。 */
916
+ /**
917
+ * 后端目录项;例如 PostgreSQL 对应 `backend: 'postgresql'`。
918
+ * @example
919
+ * ```ts
920
+ * import type { MiddlewareBackend } from '@h-ai/deploy'
921
+ * const backend: MiddlewareBackend = { backend: 'postgresql', label: 'PostgreSQL', purpose: 'deploy_purposeDatabase', fields: [{ name: 'database', required: true }] }
922
+ * ```
923
+ */
547
924
  interface MiddlewareBackend {
548
925
  /** 模块 Schema 接受的后端标识。 */
549
926
  backend: string;
@@ -559,16 +936,54 @@ interface MiddlewareBackend {
559
936
  /**
560
937
  * 所有可部署中间件的连接目录。
561
938
  * SQLite、memory、local 与 disabled 保留本地/禁用语义,不创建无关 sidecar。
562
- * @example `DEPLOY_MIDDLEWARE.db.map(item => item.label)` 返回数据库选择项。
939
+ * 目录同时描述本机、禁用、自建与云后端;存在依赖不等于必须开通新资源。
940
+ * @example
941
+ * ```ts
942
+ * import { DEPLOY_MIDDLEWARE, describeMiddlewareBackend } from '@h-ai/deploy'
943
+ * const choices = DEPLOY_MIDDLEWARE.db.map(backend => ({ value: backend.backend, description: describeMiddlewareBackend(backend) }))
944
+ * ```
563
945
  */
564
946
  declare const DEPLOY_MIDDLEWARE: Record<ServiceType, MiddlewareBackend[]>;
565
947
  /**
566
948
  * 返回当前语言的后端用途说明。
567
- * @example `describeMiddlewareBackend(DEPLOY_MIDDLEWARE.db[0])`
949
+ * @param backend - 部署目录中的后端条目。
950
+ * @returns 当前 locale 的用途说明。
951
+ * @example
952
+ * ```ts
953
+ * import { DEPLOY_MIDDLEWARE, describeMiddlewareBackend } from '@h-ai/deploy'
954
+ * const backend = DEPLOY_MIDDLEWARE.db.find(item => item.backend === 'postgresql')
955
+ * if (backend) {
956
+ * const description = describeMiddlewareBackend(backend)
957
+ * }
958
+ * ```
568
959
  */
569
960
  declare function describeMiddlewareBackend(backend: MiddlewareBackend): string;
570
961
 
571
- /** 读取错误中的恢复清单;仅返回明确允许展示的字段。 */
962
+ /**
963
+ * 读取命令失败的安全状态;不把本地 SSH 退出解释为远端停止。
964
+ * @param error - 部署错误
965
+ * @returns 通过结构校验的命令状态,或 undefined
966
+ * @example
967
+ * ```ts
968
+ * import { getDeployCommandState, HaiDeployError } from '@h-ai/deploy'
969
+ * const state = getDeployCommandState({ ...HaiDeployError.REMOTE_COMMAND_FAILED, message: 'SSH connection interrupted', ext: { commandExecution: { reason: 'exit', localTermination: 'confirmed', remoteExecution: 'unknown' } } })
970
+ * if (state?.remoteExecution === 'unknown') process.stderr.write('Remote execution is unknown\n')
971
+ * ```
972
+ */
973
+ declare function getDeployCommandState(error: HaiError): DeployCommandState | undefined;
974
+ /**
975
+ * 读取错误中的恢复清单;仅返回明确允许展示的字段。
976
+ * @param error - 上层捕获的部署 HaiError;不直接展示其 cause。
977
+ * @returns 安全恢复清单;结构不满足约定时为 undefined。
978
+ * @example
979
+ * ```ts
980
+ * import { getDeployRecovery, HaiDeployError } from '@h-ai/deploy'
981
+ * const recovery = getDeployRecovery({ ...HaiDeployError.PROVISION_FAILED, message: 'Provisioning failed', ext: { recovery: { projectName: 'shop', stage: 'provision', resources: [] } } })
982
+ * if (recovery) {
983
+ * const stage = recovery.stage
984
+ * }
985
+ * ```
986
+ */
572
987
  declare function getDeployRecovery(error: HaiError): DeployRecoveryInfo | undefined;
573
988
 
574
- export { DEPLOY_MIDDLEWARE, type DeployAppOptions, type DeployConfig, type DeployConfigInput, DeployConfigSchema, type DeployCredentialOperations, type DeployError, type DeployFunctions, type DeployRecoveryInfo, type DeployResult, type DeployScanOptions, type DeploymentArtifact, DeploymentArtifactSchema, type DeploymentEnvironment, type DockerSshProviderConfig, HaiDeployError, type MiddlewareBackend, type MiddlewareField, type MiddlewareMode, type MiddlewareRequirement, type PortExposeConfig, ProviderConfigSchema, type ProvisionResult, type ScanResult, type ServiceType, type SshConfig, artifactChecksum, deploy, describeMiddlewareBackend, getDeployRecovery, loadDeploymentConfig, loadDeploymentEnvironment, readDeploymentArtifact, saveDeploymentEnvironment };
989
+ export { DEPLOY_MIDDLEWARE, type DeployAppOptions, type DeployCommandState, type DeployConfig, type DeployConfigInput, DeployConfigSchema, type DeployCredentialOperations, type DeployError, type DeployFunctions, type DeployRecoveryInfo, type DeployResult, type DeployScanOptions, type DeploymentArtifact, DeploymentArtifactSchema, type DeploymentEnvironment, type DockerSshProviderConfig, HaiDeployError, type MiddlewareBackend, type MiddlewareField, type MiddlewareMode, type MiddlewareRequirement, type PortExposeConfig, ProviderConfigSchema, type ProvisionResult, type ScanResult, type ServiceType, type SshConfig, artifactChecksum, deploy, describeMiddlewareBackend, getDeployCommandState, getDeployRecovery, loadDeploymentConfig, loadDeploymentEnvironment, readDeploymentArtifact, saveDeploymentEnvironment };