@weotro/dx 0.1.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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +755 -0
  3. package/bin/dx-with-version-env.js +8 -0
  4. package/bin/dx.js +187 -0
  5. package/lib/artifact-deploy/artifact-builder.js +144 -0
  6. package/lib/artifact-deploy/config.js +180 -0
  7. package/lib/artifact-deploy/remote-script.js +301 -0
  8. package/lib/artifact-deploy/remote-transport.js +86 -0
  9. package/lib/artifact-deploy.js +70 -0
  10. package/lib/backend-artifact-deploy/artifact-builder.js +267 -0
  11. package/lib/backend-artifact-deploy/config.js +218 -0
  12. package/lib/backend-artifact-deploy/path-utils.js +18 -0
  13. package/lib/backend-artifact-deploy/remote-phases.js +14 -0
  14. package/lib/backend-artifact-deploy/remote-result.js +44 -0
  15. package/lib/backend-artifact-deploy/remote-script.js +507 -0
  16. package/lib/backend-artifact-deploy/remote-transport.js +123 -0
  17. package/lib/backend-artifact-deploy/rollback.js +5 -0
  18. package/lib/backend-artifact-deploy/runtime-package.js +46 -0
  19. package/lib/backend-artifact-deploy.js +91 -0
  20. package/lib/backend-package.js +674 -0
  21. package/lib/cli/args.js +38 -0
  22. package/lib/cli/command-result.js +1 -0
  23. package/lib/cli/commands/contracts.js +60 -0
  24. package/lib/cli/commands/core.js +533 -0
  25. package/lib/cli/commands/db.js +231 -0
  26. package/lib/cli/commands/deploy.js +175 -0
  27. package/lib/cli/commands/env.js +120 -0
  28. package/lib/cli/commands/export.js +39 -0
  29. package/lib/cli/commands/package.js +22 -0
  30. package/lib/cli/commands/release.js +55 -0
  31. package/lib/cli/commands/stack.js +427 -0
  32. package/lib/cli/commands/start.js +58 -0
  33. package/lib/cli/commands/worktree.js +145 -0
  34. package/lib/cli/dx-cli.js +1072 -0
  35. package/lib/cli/flags.js +123 -0
  36. package/lib/cli/help-model.js +222 -0
  37. package/lib/cli/help-renderer.js +137 -0
  38. package/lib/cli/help-schema.js +552 -0
  39. package/lib/cli/help.js +141 -0
  40. package/lib/cli/index.js +4 -0
  41. package/lib/cli/nx-command.js +13 -0
  42. package/lib/codex-initial.js +271 -0
  43. package/lib/confirm.js +213 -0
  44. package/lib/env-policy.js +134 -0
  45. package/lib/env-profile.js +435 -0
  46. package/lib/env.js +261 -0
  47. package/lib/exec.js +692 -0
  48. package/lib/logger.js +239 -0
  49. package/lib/nx-ignore.js +45 -0
  50. package/lib/run-with-version-env.js +163 -0
  51. package/lib/sdk-build.js +424 -0
  52. package/lib/start-dev.js +401 -0
  53. package/lib/telegram-webhook.js +431 -0
  54. package/lib/validate-env.js +317 -0
  55. package/lib/vercel-deploy.js +549 -0
  56. package/lib/version.js +14 -0
  57. package/lib/worktree.js +1052 -0
  58. package/package.json +45 -0
  59. package/skills/create-issue/SKILL.md +90 -0
  60. package/skills/delivering-design-handoff/SKILL.md +290 -0
  61. package/skills/doctor/SKILL.md +76 -0
  62. package/skills/gh-dependabot-cleanup/SKILL.md +54 -0
  63. package/skills/gh-dependabot-cleanup/agents/openai.yaml +7 -0
  64. package/skills/git-release/SKILL.md +194 -0
  65. package/skills/git-release/agents/openai.yaml +7 -0
  66. package/skills/online-debug-guard/SKILL.md +111 -0
  67. package/skills/ship-issue-pr/SKILL.md +676 -0
  68. package/skills/stagewise-ui-debugging/SKILL.md +48 -0
@@ -0,0 +1,123 @@
1
+ export const FLAG_DEFINITIONS = {
2
+ _global: [
3
+ { flag: '--dev' },
4
+ { flag: '--prod' },
5
+ { flag: '--staging' },
6
+ { flag: '--test' },
7
+ { flag: '--e2e' },
8
+ { flag: '--no-env-check' },
9
+ { flag: '-Y' },
10
+ { flag: '--yes' },
11
+ { flag: '-v' },
12
+ { flag: '--verbose' },
13
+ { flag: '-h' },
14
+ { flag: '--help' },
15
+ { flag: '-V' },
16
+ { flag: '--version' },
17
+ { flag: '--parallel' },
18
+ { flag: '-P' },
19
+ ],
20
+ db: [
21
+ { flag: '--name', expectsValue: true },
22
+ { flag: '-n', expectsValue: true },
23
+ ],
24
+ test: [
25
+ { flag: '-t', expectsValue: true },
26
+ { flag: '--name', expectsValue: true },
27
+ { flag: '--test-name-pattern', expectsValue: true },
28
+ ],
29
+ package: [
30
+ { flag: '--skip-build' },
31
+ { flag: '--keep-workdir' },
32
+ ],
33
+ worktree: [
34
+ { flag: '--base', expectsValue: true },
35
+ { flag: '-b', expectsValue: true },
36
+ { flag: '--all' },
37
+ ],
38
+ lint: [
39
+ { flag: '--fix' },
40
+ ],
41
+ deploy: [
42
+ { flag: '--BR' },
43
+ { flag: '--build-only' },
44
+ { flag: '--artifact', expectsValue: true },
45
+ { flag: '--skip-migration' },
46
+ { flag: '--webhook-path', expectsValue: true },
47
+ { flag: '--webhook-dry-run' },
48
+ { flag: '--strict-webhook' },
49
+ { flag: '--no-strict-webhook' },
50
+ ],
51
+ }
52
+
53
+ export function parseFlags(args = []) {
54
+ const flags = {}
55
+
56
+ for (let index = 0; index < args.length; index += 1) {
57
+ const flag = args[index]
58
+ if (flag === '--') break
59
+ if (!flag.startsWith('-')) continue
60
+ switch (flag) {
61
+ case '--dev':
62
+ flags.dev = true
63
+ break
64
+ case '--prod':
65
+ flags.prod = true
66
+ break
67
+ case '--staging':
68
+ flags.staging = true
69
+ break
70
+ case '--test':
71
+ flags.test = true
72
+ break
73
+ case '--e2e':
74
+ flags.e2e = true
75
+ break
76
+ case '-Y':
77
+ case '--yes':
78
+ flags.Y = true
79
+ break
80
+ case '-v':
81
+ case '--verbose':
82
+ flags.verbose = true
83
+ break
84
+ case '-h':
85
+ case '--help':
86
+ flags.help = true
87
+ break
88
+ case '-V':
89
+ case '--version':
90
+ flags.version = true
91
+ break
92
+ case '--no-env-check':
93
+ flags.noEnvCheck = true
94
+ break
95
+ case '--fix':
96
+ flags.fix = true
97
+ break
98
+ case '--parallel':
99
+ case '-P':
100
+ flags.parallel = true
101
+ break
102
+ case '--all':
103
+ flags.all = true
104
+ break
105
+ case '--BR':
106
+ flags.BR = true
107
+ break
108
+ case '--build-only':
109
+ flags.buildOnly = true
110
+ break
111
+ case '--artifact':
112
+ flags.artifact = args[index + 1]
113
+ break
114
+ case '--skip-migration':
115
+ flags.skipMigration = true
116
+ break
117
+ default:
118
+ break
119
+ }
120
+ }
121
+
122
+ return flags
123
+ }
@@ -0,0 +1,222 @@
1
+ const ENVIRONMENT_KEYS = new Set(['development', 'production', 'staging', 'test', 'e2e'])
2
+ const COMMAND_LIST_HIDDEN = new Set(['help'])
3
+ const META_KEYS = new Set(['help', 'description', 'args', 'interactive', 'dangerous'])
4
+ const INTERNAL_CONFIG_KEYS = new Set([
5
+ 'services',
6
+ 'urls',
7
+ 'preflight',
8
+ 'ecosystemConfig',
9
+ 'pm2Bin',
10
+ 'backendDeploy',
11
+ 'artifactDeploy',
12
+ 'telegramWebhook',
13
+ ])
14
+ const BUILTIN_SUMMARIES = {
15
+ env: '安全校验并临时装配本地品牌环境配置',
16
+ }
17
+
18
+ export function buildHelpRuntimeContext(cli = {}) {
19
+ return {
20
+ registeredCommands: getRegisteredCommands(cli),
21
+ knownFlags: getKnownFlags(cli),
22
+ }
23
+ }
24
+
25
+ export function getRegisteredCommands(cli = {}) {
26
+ const handlers = cli?.commandHandlers
27
+ if (!handlers || typeof handlers !== 'object') return []
28
+
29
+ return Object.keys(handlers).filter(name => !COMMAND_LIST_HIDDEN.has(name))
30
+ }
31
+
32
+ export function getKnownFlags(cli = {}) {
33
+ const definitions = cli?.flagDefinitions
34
+ const knownFlags = new Map()
35
+
36
+ if (!definitions || typeof definitions !== 'object') return knownFlags
37
+
38
+ for (const entries of Object.values(definitions)) {
39
+ if (!Array.isArray(entries)) continue
40
+ for (const entry of entries) {
41
+ if (!entry?.flag) continue
42
+ knownFlags.set(entry.flag, {
43
+ expectsValue: Boolean(entry.expectsValue),
44
+ })
45
+ }
46
+ }
47
+
48
+ return knownFlags
49
+ }
50
+
51
+ export function classifyCommandNode(node = {}) {
52
+ if (node?.help?.nodeType) return node.help.nodeType
53
+ if (looksLikeInternalConfigBag(node)) return 'internal-config-bag'
54
+ if (node?.command || node?.internal) return 'target-leaf'
55
+ if (node?.concurrent || node?.sequential) return 'orchestration-node'
56
+ if (looksLikeEnvContainer(node)) return 'env-container'
57
+ if (looksLikeCategoryNode(node)) return 'category-node'
58
+ return 'unknown-node'
59
+ }
60
+
61
+ export function isVisibleHelpNode(name, node, nodeType = classifyCommandNode(node)) {
62
+ void name
63
+
64
+ if (node?.help?.expose === false) return false
65
+ if (nodeType === 'internal-config-bag') return false
66
+ if (nodeType === 'category-node') return false
67
+ if (nodeType === 'orchestration-node') return node?.help?.expose === true
68
+ return true
69
+ }
70
+
71
+ export function getGlobalHelpModel(commands = {}, context = {}) {
72
+ const registeredCommands = Array.isArray(context?.registeredCommands)
73
+ ? context.registeredCommands
74
+ : []
75
+
76
+ return {
77
+ summary: commands?.help?.summary ?? '',
78
+ commands: registeredCommands.map(name => getCommandHelpModel(commands, name, context)),
79
+ globalOptions: normalizeArray(commands?.help?.globalOptions),
80
+ examples: normalizeArray(commands?.help?.examples),
81
+ }
82
+ }
83
+
84
+ export function getCommandHelpModel(commands = {}, commandName, context = {}) {
85
+ const commandConfig = commands?.[commandName] ?? {}
86
+ const commandHelp = getCommandHelpConfig(commands, commandName)
87
+
88
+ return {
89
+ name: commandName,
90
+ summary: resolveSummary(commandConfig, commandHelp) || BUILTIN_SUMMARIES[commandName] || '',
91
+ usage: resolveUsage(commandName, commandHelp, commandConfig),
92
+ args: normalizeArray(commandHelp?.args),
93
+ notes: normalizeArray(commandHelp?.notes),
94
+ examples: normalizeArray(commandHelp?.examples),
95
+ options: normalizeArray(commandHelp?.options),
96
+ targets: resolveVisibleTargets(commands, commandName, commandConfig, context),
97
+ }
98
+ }
99
+
100
+ export function resolveSummary(node = {}, help = null) {
101
+ return help?.summary || node?.help?.summary || node?.description || ''
102
+ }
103
+
104
+ function resolveUsage(commandName, commandHelp = {}, commandConfig = {}) {
105
+ return commandHelp?.usage || generateUsage(commandName, commandConfig)
106
+ }
107
+
108
+ function generateUsage(commandName, commandConfig = {}) {
109
+ switch (commandName) {
110
+ case 'start':
111
+ return 'dx start <service> [环境标志]'
112
+ case 'build':
113
+ case 'package':
114
+ return `dx ${commandName} <target> [环境标志]`
115
+ case 'db':
116
+ return 'dx db <action> [name] [环境标志]'
117
+ case 'test':
118
+ return 'dx test [type] <target> [path]'
119
+ case 'deploy':
120
+ case 'clean':
121
+ case 'cache':
122
+ return `dx ${commandName} <target>`
123
+ case 'help':
124
+ return 'dx help [command]'
125
+ case 'worktree':
126
+ return 'dx worktree [action] [args...]'
127
+ case 'env':
128
+ return 'dx env status | validate <profile> [环境标志] | exec <profile> [环境标志] -- <command>'
129
+ case 'lint':
130
+ case 'status':
131
+ return `dx ${commandName}`
132
+ default:
133
+ return hasVisibleTargets(commandConfig) ? `dx ${commandName} <target>` : `dx ${commandName}`
134
+ }
135
+ }
136
+
137
+ function hasVisibleTargets(commandConfig) {
138
+ if (!isPlainObject(commandConfig)) return false
139
+
140
+ return Object.entries(commandConfig).some(([name, node]) => {
141
+ if (name === 'help') return false
142
+ return isVisibleHelpNode(name, node)
143
+ })
144
+ }
145
+
146
+ function resolveVisibleTargets(commands, commandName, commandConfig, context) {
147
+ void context
148
+
149
+ if (!isPlainObject(commandConfig)) return []
150
+
151
+ return Object.entries(commandConfig)
152
+ .filter(([name]) => name !== 'help')
153
+ .map(([name, node]) => {
154
+ const nodeType = classifyCommandNode(node)
155
+ if (!isVisibleHelpNode(name, node, nodeType)) return null
156
+
157
+ const targetHelp = getTargetHelpConfig(commands, commandName, name)
158
+
159
+ return {
160
+ name,
161
+ nodeType,
162
+ summary: resolveSummary(node, targetHelp),
163
+ notes: normalizeArray(targetHelp?.notes),
164
+ options: normalizeArray(targetHelp?.options),
165
+ examples: normalizeArray(targetHelp?.examples),
166
+ }
167
+ })
168
+ .filter(Boolean)
169
+ }
170
+
171
+ function getCommandHelpConfig(commands, commandName) {
172
+ const config = commands?.help?.commands?.[commandName]
173
+ return isPlainObject(config) ? config : {}
174
+ }
175
+
176
+ function getTargetHelpConfig(commands, commandName, targetName) {
177
+ const config = commands?.help?.targets?.[commandName]?.[targetName]
178
+ return isPlainObject(config) ? config : {}
179
+ }
180
+
181
+ function looksLikeInternalConfigBag(node) {
182
+ if (!isPlainObject(node)) return false
183
+ if (node.command || node.internal || node.concurrent || node.sequential) return false
184
+ if (looksLikeEnvContainer(node)) return false
185
+
186
+ const visibleKeys = getVisibleKeys(node)
187
+ if (visibleKeys.length === 0) return false
188
+
189
+ return visibleKeys.some(key => INTERNAL_CONFIG_KEYS.has(key))
190
+ }
191
+
192
+ function looksLikeEnvContainer(node) {
193
+ if (!isPlainObject(node)) return false
194
+ const visibleKeys = getVisibleKeys(node)
195
+ if (visibleKeys.length === 0) return false
196
+
197
+ const envKeys = visibleKeys.filter(key => ENVIRONMENT_KEYS.has(key))
198
+ return envKeys.length > 0 && envKeys.length === visibleKeys.length
199
+ }
200
+
201
+ function looksLikeCategoryNode(node) {
202
+ if (!isPlainObject(node)) return false
203
+ if (node.command || node.internal || node.concurrent || node.sequential) return false
204
+ if (looksLikeEnvContainer(node) || looksLikeInternalConfigBag(node)) return false
205
+
206
+ const visibleKeys = getVisibleKeys(node)
207
+ if (visibleKeys.length === 0) return false
208
+
209
+ return visibleKeys.every(key => isPlainObject(node[key]))
210
+ }
211
+
212
+ function getVisibleKeys(node) {
213
+ return Object.keys(node).filter(key => !META_KEYS.has(key))
214
+ }
215
+
216
+ function normalizeArray(value) {
217
+ return Array.isArray(value) ? value : []
218
+ }
219
+
220
+ function isPlainObject(value) {
221
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
222
+ }
@@ -0,0 +1,137 @@
1
+ function normalizeArray(value) {
2
+ return Array.isArray(value) ? value : []
3
+ }
4
+
5
+ function pushSection(lines, title, entries = []) {
6
+ const normalizedEntries = entries.filter(Boolean)
7
+ if (normalizedEntries.length === 0) return
8
+
9
+ lines.push('', title, ...normalizedEntries)
10
+ }
11
+
12
+ function formatExample(example = {}, indent = ' ') {
13
+ const command = typeof example.command === 'string' ? example.command : ''
14
+ const description = typeof example.description === 'string' ? example.description : ''
15
+
16
+ if (!command) return ''
17
+ if (!description) return `${indent}${command}`
18
+ return `${indent}${command} # ${description}`
19
+ }
20
+
21
+ function formatOption(option = {}, indent = ' ') {
22
+ const flags = normalizeArray(option.flags).filter(Boolean).join(', ')
23
+ const description = typeof option.description === 'string' ? option.description : ''
24
+
25
+ if (!flags && !description) return ''
26
+ if (!description) return `${indent}${flags}`
27
+ return `${indent}${flags} ${description}`
28
+ }
29
+
30
+ function getVisibleTargets(targets = []) {
31
+ return normalizeArray(targets).filter(target => target?.nodeType !== 'orchestration-node')
32
+ }
33
+
34
+ function formatTarget(target = {}, width = 0) {
35
+ const name = typeof target.name === 'string' ? target.name : ''
36
+ const summary = typeof target.summary === 'string' ? target.summary : ''
37
+ const lines = []
38
+
39
+ if (!name) return lines
40
+
41
+ lines.push(summary ? ` ${name.padEnd(width)} ${summary}` : ` ${name}`)
42
+
43
+ normalizeArray(target.notes).forEach(note => {
44
+ if (!note) return
45
+ lines.push(` 提示: ${note}`)
46
+ })
47
+
48
+ normalizeArray(target.options).forEach(option => {
49
+ const rendered = formatOption(option, ' 选项: ')
50
+ if (rendered) lines.push(rendered)
51
+ })
52
+
53
+ normalizeArray(target.examples).forEach(example => {
54
+ const rendered = formatExample(example, ' 示例: ')
55
+ if (rendered) lines.push(rendered)
56
+ })
57
+
58
+ return lines
59
+ }
60
+
61
+ export function renderGlobalHelp(model = {}) {
62
+ const lines = []
63
+ const invocation = model.invocation || 'dx'
64
+ const title = [model.title, model.summary].filter(Boolean).join(' - ') || model.title || model.summary
65
+ const commands = normalizeArray(model.commands)
66
+ const commandWidth = Math.max(0, ...commands.map(command => String(command?.name || '').length))
67
+
68
+ if (title) {
69
+ lines.push('', title)
70
+ }
71
+
72
+ pushSection(lines, '用法:', [` ${invocation} <命令> [选项] [参数...]`])
73
+ pushSection(
74
+ lines,
75
+ '命令:',
76
+ commands.map(command => {
77
+ const name = String(command?.name || '')
78
+ const summary = String(command?.summary || '')
79
+ if (!name) return ''
80
+ return summary ? ` ${name.padEnd(commandWidth)} ${summary}` : ` ${name}`
81
+ }),
82
+ )
83
+ pushSection(lines, '选项:', normalizeArray(model.globalOptions).map(option => formatOption(option)))
84
+ pushSection(lines, '示例:', normalizeArray(model.examples).map(example => formatExample(example)))
85
+
86
+ lines.push('', `运行 ${invocation} help <命令> 查看子命令的详细用法和示例`)
87
+
88
+ return lines.join('\n')
89
+ }
90
+
91
+ export function renderCommandHelp(model = {}) {
92
+ const name = typeof model.name === 'string' ? model.name : ''
93
+ const usage = typeof model.usage === 'string' ? model.usage : ''
94
+ const args = normalizeArray(model.args)
95
+ const notes = normalizeArray(model.notes)
96
+ const options = normalizeArray(model.options)
97
+ const examples = normalizeArray(model.examples)
98
+ const targets = getVisibleTargets(model.targets)
99
+ const targetWidth = Math.max(0, ...targets.map(target => String(target?.name || '').length))
100
+ const lines = []
101
+
102
+ if (name) {
103
+ lines.push('', `${name} 命令用法:`)
104
+ }
105
+
106
+ if (usage) {
107
+ lines.push(` ${usage}`)
108
+ }
109
+
110
+ if (model.summary) {
111
+ pushSection(lines, '摘要:', [` ${model.summary}`])
112
+ }
113
+
114
+ pushSection(
115
+ lines,
116
+ '参数说明:',
117
+ args.map(arg => {
118
+ const argName = typeof arg.name === 'string' ? arg.name : ''
119
+ const description = typeof arg.description === 'string' ? arg.description : ''
120
+
121
+ if (!argName && !description) return ''
122
+ if (!description) return ` ${argName}`
123
+ return ` ${argName}: ${description}`
124
+ }),
125
+ )
126
+
127
+ pushSection(
128
+ lines,
129
+ '可用 target:',
130
+ targets.flatMap(target => formatTarget(target, targetWidth)),
131
+ )
132
+ pushSection(lines, '选项:', options.map(option => formatOption(option)))
133
+ pushSection(lines, '提示:', notes.map(note => (note ? ` ${note}` : '')))
134
+ pushSection(lines, '示例:', examples.map(example => formatExample(example)))
135
+
136
+ return lines.join('\n')
137
+ }