@weotro/dx 0.1.19 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -718
- package/bin/dx.js +100 -173
- package/example/.env.development +1 -0
- package/example/README.md +24 -0
- package/example/dx/scripts/build +2 -0
- package/example/dx/scripts/db +2 -0
- package/example/dx/scripts/deploy +2 -0
- package/example/dx/scripts/setup +2 -0
- package/example/dx/scripts/start +12 -0
- package/example/dx/scripts/test +2 -0
- package/lib/env.js +40 -257
- package/lib/logger.js +5 -238
- package/lib/setup.js +72 -0
- package/package.json +27 -13
- package/skills/ask_3th/SKILL.md +2 -0
- package/skills/delegate_3th/SKILL.md +4 -2
- package/skills/delegate_3th/scripts/delegate_3th.py +37 -41
- package/skills/delegate_3th/scripts/test_delegate_3th.py +43 -17
- package/bin/dx-with-version-env.js +0 -8
- package/lib/artifact-deploy/artifact-builder.js +0 -144
- package/lib/artifact-deploy/config.js +0 -180
- package/lib/artifact-deploy/remote-script.js +0 -301
- package/lib/artifact-deploy/remote-transport.js +0 -86
- package/lib/artifact-deploy.js +0 -70
- package/lib/backend-artifact-deploy/artifact-builder.js +0 -267
- package/lib/backend-artifact-deploy/config.js +0 -218
- package/lib/backend-artifact-deploy/path-utils.js +0 -18
- package/lib/backend-artifact-deploy/remote-phases.js +0 -14
- package/lib/backend-artifact-deploy/remote-result.js +0 -44
- package/lib/backend-artifact-deploy/remote-script.js +0 -507
- package/lib/backend-artifact-deploy/remote-transport.js +0 -123
- package/lib/backend-artifact-deploy/rollback.js +0 -5
- package/lib/backend-artifact-deploy/runtime-package.js +0 -46
- package/lib/backend-artifact-deploy.js +0 -91
- package/lib/backend-package.js +0 -674
- package/lib/cli/args.js +0 -38
- package/lib/cli/command-result.js +0 -1
- package/lib/cli/commands/contracts.js +0 -60
- package/lib/cli/commands/core.js +0 -533
- package/lib/cli/commands/db.js +0 -231
- package/lib/cli/commands/deploy.js +0 -175
- package/lib/cli/commands/env.js +0 -120
- package/lib/cli/commands/export.js +0 -39
- package/lib/cli/commands/package.js +0 -22
- package/lib/cli/commands/release.js +0 -55
- package/lib/cli/commands/stack.js +0 -427
- package/lib/cli/commands/start.js +0 -58
- package/lib/cli/commands/worktree.js +0 -145
- package/lib/cli/dx-cli.js +0 -1072
- package/lib/cli/flags.js +0 -123
- package/lib/cli/help-model.js +0 -222
- package/lib/cli/help-renderer.js +0 -137
- package/lib/cli/help-schema.js +0 -552
- package/lib/cli/help.js +0 -141
- package/lib/cli/index.js +0 -4
- package/lib/cli/nx-command.js +0 -13
- package/lib/confirm.js +0 -213
- package/lib/env-policy.js +0 -134
- package/lib/env-profile.js +0 -531
- package/lib/exec.js +0 -692
- package/lib/nx-ignore.js +0 -45
- package/lib/run-with-version-env.js +0 -163
- package/lib/sdk-build.js +0 -424
- package/lib/start-dev.js +0 -401
- package/lib/telegram-webhook.js +0 -431
- package/lib/validate-env.js +0 -317
- package/lib/vercel-deploy.js +0 -549
- package/lib/worktree.js +0 -1052
- package/skills/delegate_3th/scripts/__pycache__/delegate_3th.cpython-314.pyc +0 -0
- package/skills/delegate_3th/scripts/__pycache__/delegate_cc.cpython-314.pyc +0 -0
- package/skills/delegate_3th/scripts/__pycache__/test_delegate_cc.cpython-314.pyc +0 -0
package/lib/confirm.js
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { createInterface } from 'node:readline'
|
|
2
|
-
import { logger } from './logger.js'
|
|
3
|
-
|
|
4
|
-
export class ConfirmManager {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.rl = null
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// 判断是否启用自动确认(CI 或显式环境变量)
|
|
10
|
-
isAutoYes(skipFlag = false) {
|
|
11
|
-
if (skipFlag) return true
|
|
12
|
-
const ci = String(process.env.CI || '').toLowerCase()
|
|
13
|
-
const autoYes = String(process.env.AI_CLI_YES || process.env.YES || '').toLowerCase()
|
|
14
|
-
// 常见 CI 环境会设置 CI=true;也支持自定义 AI_CLI_YES/YES 变量
|
|
15
|
-
return ci === 'true' || ci === '1' || autoYes === 'true' || autoYes === '1'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// 基础确认
|
|
19
|
-
async confirm(message, defaultValue = false, skipFlag = false) {
|
|
20
|
-
if (this.isAutoYes(skipFlag)) {
|
|
21
|
-
logger.info(`跳过确认: ${message}`)
|
|
22
|
-
return true
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const prompt = defaultValue ? `${message} [Y/n]: ` : `${message} [y/N]: `
|
|
26
|
-
|
|
27
|
-
return new Promise(resolve => {
|
|
28
|
-
this.rl = createInterface({
|
|
29
|
-
input: process.stdin,
|
|
30
|
-
output: process.stdout,
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
this.rl.question(prompt, answer => {
|
|
34
|
-
this.rl.close()
|
|
35
|
-
|
|
36
|
-
const normalized = answer.toLowerCase().trim()
|
|
37
|
-
if (normalized === '') {
|
|
38
|
-
resolve(defaultValue)
|
|
39
|
-
} else {
|
|
40
|
-
resolve(['y', 'yes', '是', 'true', '1'].includes(normalized))
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// 危险操作确认
|
|
47
|
-
async confirmDangerous(operation, environment = 'unknown', skipFlag = false) {
|
|
48
|
-
if (this.isAutoYes(skipFlag)) {
|
|
49
|
-
logger.warn(`跳过危险操作确认: ${operation}`)
|
|
50
|
-
return true
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
logger.separator()
|
|
54
|
-
logger.warn('警告: 即将执行危险操作')
|
|
55
|
-
console.log(`操作: ${operation}`)
|
|
56
|
-
console.log(`环境: ${environment}`)
|
|
57
|
-
|
|
58
|
-
if (environment === 'production' || environment === '生产环境') {
|
|
59
|
-
console.log(`🔥 此操作将在生产环境执行,可能导致数据丢失或服务中断`)
|
|
60
|
-
console.log(`🔥 请确保您已经:`)
|
|
61
|
-
console.log(` 1. 备份了重要数据`)
|
|
62
|
-
console.log(` 2. 通知了相关团队成员`)
|
|
63
|
-
console.log(` 3. 确认了操作的必要性`)
|
|
64
|
-
} else {
|
|
65
|
-
console.log(`此操作可能导致数据丢失,请谨慎操作`)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
logger.separator()
|
|
69
|
-
|
|
70
|
-
// 对于生产环境,需要两次确认
|
|
71
|
-
if (environment === 'production' || environment === '生产环境') {
|
|
72
|
-
const firstConfirm = await this.confirm('您确定要在生产环境执行此危险操作吗?', false, false)
|
|
73
|
-
if (!firstConfirm) {
|
|
74
|
-
return false
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
console.log(`\n请再次确认,输入操作名称以继续: ${operation}`)
|
|
78
|
-
return new Promise(resolve => {
|
|
79
|
-
this.rl = createInterface({
|
|
80
|
-
input: process.stdin,
|
|
81
|
-
output: process.stdout,
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
this.rl.question('请输入操作名称: ', answer => {
|
|
85
|
-
this.rl.close()
|
|
86
|
-
resolve(answer.trim() === operation)
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
} else {
|
|
90
|
-
return this.confirm('确定要继续吗?', false, false)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 批量操作确认
|
|
95
|
-
async confirmBatch(operations, skipFlag = false) {
|
|
96
|
-
if (this.isAutoYes(skipFlag)) {
|
|
97
|
-
logger.info('跳过批量操作确认')
|
|
98
|
-
return true
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
logger.separator()
|
|
102
|
-
console.log('即将执行以下操作:')
|
|
103
|
-
operations.forEach((op, index) => {
|
|
104
|
-
console.log(`${index + 1}. ${op}`)
|
|
105
|
-
})
|
|
106
|
-
logger.separator()
|
|
107
|
-
|
|
108
|
-
return this.confirm('确认执行所有操作?', false, false)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 端口冲突确认
|
|
112
|
-
async confirmPortCleanup(port, processes, skipFlag = false) {
|
|
113
|
-
if (this.isAutoYes(skipFlag)) {
|
|
114
|
-
logger.info(`跳过端口清理确认: 端口 ${port}`)
|
|
115
|
-
return true
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
logger.warn(`端口 ${port} 被以下进程占用:`)
|
|
119
|
-
processes.forEach(pid => {
|
|
120
|
-
console.log(` 进程 ID: ${pid}`)
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
return this.confirm(`是否杀死这些进程以释放端口 ${port}?`, true, false)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// 环境切换确认
|
|
127
|
-
async confirmEnvironmentSwitch(from, to, skipFlag = false) {
|
|
128
|
-
if (this.isAutoYes(skipFlag)) {
|
|
129
|
-
logger.info(`跳过环境切换确认: ${from} -> ${to}`)
|
|
130
|
-
return true
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (to === 'production' || to === '生产环境') {
|
|
134
|
-
logger.warn(`即将从 ${from} 切换到 ${to}`)
|
|
135
|
-
logger.warn('生产环境操作需要额外谨慎')
|
|
136
|
-
return this.confirm('确定要切换到生产环境吗?', false, false)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return this.confirm(`确定要从 ${from} 切换到 ${to} 吗?`, true, false)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// 文件覆盖确认
|
|
143
|
-
async confirmOverwrite(filePath, skipFlag = false) {
|
|
144
|
-
if (this.isAutoYes(skipFlag)) {
|
|
145
|
-
logger.info(`跳过文件覆盖确认: ${filePath}`)
|
|
146
|
-
return true
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
logger.warn(`文件已存在: ${filePath}`)
|
|
150
|
-
return this.confirm('是否覆盖现有文件?', false, false)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// 清理操作确认
|
|
154
|
-
async confirmCleanup(targets, skipFlag = false) {
|
|
155
|
-
if (this.isAutoYes(skipFlag)) {
|
|
156
|
-
logger.info('跳过清理操作确认')
|
|
157
|
-
return true
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
logger.warn('即将清理以下内容:')
|
|
161
|
-
targets.forEach(target => {
|
|
162
|
-
console.log(` - ${target}`)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
return this.confirm('确定要执行清理操作吗?', false, false)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// 服务启动确认(用于可能有端口冲突的情况)
|
|
169
|
-
async confirmServiceStart(service, port, skipFlag = false) {
|
|
170
|
-
if (this.isAutoYes(skipFlag)) {
|
|
171
|
-
return true
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return this.confirm(`确定要启动 ${service} 服务 (端口 ${port}) 吗?`, true, false)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// 版本发布确认
|
|
178
|
-
async confirmRelease(version, isProduction = false, skipFlag = false) {
|
|
179
|
-
if (this.isAutoYes(skipFlag)) {
|
|
180
|
-
logger.info(`跳过版本发布确认: ${version}`)
|
|
181
|
-
return true
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (isProduction) {
|
|
185
|
-
logger.warn(`即将发布生产版本: ${version}`)
|
|
186
|
-
logger.warn('此操作将创建正式发布标签')
|
|
187
|
-
return this.confirmDangerous(`发布版本 ${version}`, '生产环境', false)
|
|
188
|
-
} else {
|
|
189
|
-
return this.confirm(`确定要发布开发版本 ${version} 吗?`, true, false)
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// 数据库操作确认
|
|
194
|
-
async confirmDatabaseOperation(operation, environment, skipFlag = false) {
|
|
195
|
-
const dangerousOps = ['reset', 'drop', 'migrate:reset', 'db:reset']
|
|
196
|
-
const isDangerous = dangerousOps.some(op => operation.includes(op))
|
|
197
|
-
|
|
198
|
-
if (isDangerous) {
|
|
199
|
-
return this.confirmDangerous(
|
|
200
|
-
`数据库操作: ${operation}`,
|
|
201
|
-
environment,
|
|
202
|
-
this.isAutoYes(skipFlag),
|
|
203
|
-
)
|
|
204
|
-
} else {
|
|
205
|
-
if (this.isAutoYes(skipFlag)) {
|
|
206
|
-
return true
|
|
207
|
-
}
|
|
208
|
-
return this.confirm(`确定要执行数据库操作: ${operation} (${environment}) 吗?`, true, false)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export const confirmManager = new ConfirmManager()
|
package/lib/env-policy.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
import stripJsonComments from 'strip-json-comments'
|
|
4
|
-
|
|
5
|
-
const DEFAULT_POLICY_PATH = 'env-policy.jsonc'
|
|
6
|
-
|
|
7
|
-
let cachedPolicy = null
|
|
8
|
-
let cachedPolicyDir = null
|
|
9
|
-
|
|
10
|
-
function assertStringArray(value, fieldPath) {
|
|
11
|
-
if (!Array.isArray(value)) {
|
|
12
|
-
throw new Error(`${fieldPath} 必须是数组`)
|
|
13
|
-
}
|
|
14
|
-
const invalid = value.filter(v => typeof v !== 'string' || v.trim().length === 0)
|
|
15
|
-
if (invalid.length > 0) {
|
|
16
|
-
throw new Error(`${fieldPath} 中存在非法值,请使用非空字符串`)
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function assertRecord(value, fieldPath) {
|
|
21
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
22
|
-
throw new Error(`${fieldPath} 必须是对象`)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function loadEnvPolicy(configDir) {
|
|
27
|
-
if (cachedPolicy && cachedPolicyDir === configDir) return cachedPolicy
|
|
28
|
-
|
|
29
|
-
const policyPath = join(configDir, DEFAULT_POLICY_PATH)
|
|
30
|
-
if (!existsSync(policyPath)) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
`缺少配置文件 ${DEFAULT_POLICY_PATH}。
|
|
33
|
-
请在目标工程根目录创建 dx/config/${DEFAULT_POLICY_PATH}(或用 DX_CONFIG_DIR / --config-dir 指定配置目录)。
|
|
34
|
-
当前配置目录: ${configDir}`,
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
let parsed
|
|
39
|
-
try {
|
|
40
|
-
const raw = readFileSync(policyPath, 'utf8')
|
|
41
|
-
parsed = JSON.parse(stripJsonComments(raw) || '{}')
|
|
42
|
-
} catch (error) {
|
|
43
|
-
throw new Error(`无法解析 ${DEFAULT_POLICY_PATH}: ${error.message}`)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
validateEnvPolicy(parsed)
|
|
47
|
-
cachedPolicy = parsed
|
|
48
|
-
cachedPolicyDir = configDir
|
|
49
|
-
return cachedPolicy
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function validateEnvPolicy(policy) {
|
|
53
|
-
assertRecord(policy, 'env-policy')
|
|
54
|
-
|
|
55
|
-
if (policy.version !== 1) {
|
|
56
|
-
throw new Error('env-policy.jsonc.version 必须为 1')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
assertStringArray(policy.environments, 'env-policy.jsonc.environments')
|
|
60
|
-
|
|
61
|
-
if (typeof policy.secretPlaceholder !== 'string' || policy.secretPlaceholder.trim().length === 0) {
|
|
62
|
-
throw new Error('env-policy.jsonc.secretPlaceholder 必须为非空字符串')
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
assertRecord(policy.keys, 'env-policy.jsonc.keys')
|
|
66
|
-
assertStringArray(policy.keys.secret || [], 'env-policy.jsonc.keys.secret')
|
|
67
|
-
assertStringArray(policy.keys.localOnly || [], 'env-policy.jsonc.keys.localOnly')
|
|
68
|
-
assertStringArray(policy.keys.localOverride || [], 'env-policy.jsonc.keys.localOverride')
|
|
69
|
-
|
|
70
|
-
assertRecord(policy.layout, 'env-policy.jsonc.layout')
|
|
71
|
-
assertStringArray(policy.layout.forbidExact || [], 'env-policy.jsonc.layout.forbidExact')
|
|
72
|
-
assertStringArray(policy.layout.allowRoot || [], 'env-policy.jsonc.layout.allowRoot')
|
|
73
|
-
assertStringArray(policy.layout.allowSubdirGlobs || [], 'env-policy.jsonc.layout.allowSubdirGlobs')
|
|
74
|
-
|
|
75
|
-
assertRecord(policy.appToTarget || {}, 'env-policy.jsonc.appToTarget')
|
|
76
|
-
for (const [app, target] of Object.entries(policy.appToTarget || {})) {
|
|
77
|
-
if (typeof app !== 'string' || app.trim().length === 0) {
|
|
78
|
-
throw new Error('env-policy.jsonc.appToTarget 中存在非法 app key')
|
|
79
|
-
}
|
|
80
|
-
if (typeof target !== 'string' || target.trim().length === 0) {
|
|
81
|
-
throw new Error('env-policy.jsonc.appToTarget 中存在非法 target value')
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
assertRecord(policy.targets, 'env-policy.jsonc.targets')
|
|
86
|
-
const targetIds = Object.keys(policy.targets)
|
|
87
|
-
if (targetIds.length === 0) {
|
|
88
|
-
throw new Error('env-policy.jsonc.targets 不能为空')
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
for (const [targetId, target] of Object.entries(policy.targets)) {
|
|
92
|
-
if (typeof targetId !== 'string' || targetId.trim().length === 0) {
|
|
93
|
-
throw new Error('env-policy.jsonc.targets 中存在非法 targetId')
|
|
94
|
-
}
|
|
95
|
-
assertRecord(target, `env-policy.jsonc.targets.${targetId}`)
|
|
96
|
-
assertRecord(target.files, `env-policy.jsonc.targets.${targetId}.files`)
|
|
97
|
-
if (typeof target.files.committed !== 'string' || target.files.committed.trim().length === 0) {
|
|
98
|
-
throw new Error(`env-policy.jsonc.targets.${targetId}.files.committed 必须为非空字符串`)
|
|
99
|
-
}
|
|
100
|
-
if (typeof target.files.local !== 'string' || target.files.local.trim().length === 0) {
|
|
101
|
-
throw new Error(`env-policy.jsonc.targets.${targetId}.files.local 必须为非空字符串`)
|
|
102
|
-
}
|
|
103
|
-
assertRecord(target.required || {}, `env-policy.jsonc.targets.${targetId}.required`)
|
|
104
|
-
|
|
105
|
-
for (const [group, keys] of Object.entries(target.required || {})) {
|
|
106
|
-
assertStringArray(keys, `env-policy.jsonc.targets.${targetId}.required.${group}`)
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Ensure appToTarget does not point to missing targets.
|
|
111
|
-
for (const [app, target] of Object.entries(policy.appToTarget || {})) {
|
|
112
|
-
if (!policy.targets[target]) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
`env-policy.jsonc.appToTarget.${app} 指向不存在的 target: ${target}(请在 env-policy.jsonc.targets 中定义)`,
|
|
115
|
-
)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function resolvePolicyTargetId(policy, app) {
|
|
121
|
-
if (!policy || !app) return null
|
|
122
|
-
const mapped = policy.appToTarget?.[app]
|
|
123
|
-
return mapped || null
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function resolveTargetRequiredVars(policy, targetId, environment) {
|
|
127
|
-
if (!policy || !targetId) return []
|
|
128
|
-
const target = policy.targets?.[targetId]
|
|
129
|
-
if (!target) return []
|
|
130
|
-
const required = target.required || {}
|
|
131
|
-
const common = Array.isArray(required._common) ? required._common : []
|
|
132
|
-
const envSpecific = Array.isArray(required[environment]) ? required[environment] : []
|
|
133
|
-
return Array.from(new Set([...common, ...envSpecific]))
|
|
134
|
-
}
|