draftgo-cli 4.0.22 → 4.0.24
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 +2 -2
- package/bin/draftgo.js +8 -8
- package/package.json +72 -72
- package/resources/custom-service-sdk/auth_test.go +56 -0
- package/resources/custom-service-sdk/manifest.json +14 -9
- package/resources/custom-service-sdk/platform.go +19 -27
- package/resources/custom-service-sdk/resources.go +1 -0
- package/resources/custom-service-sdk/resources_scope_test.go +10 -5
- package/resources/custom-service-sdk/sdk.go +6 -5
- package/resources/skill/SKILL.md +1 -1
- package/resources/skill/manifest.json +1 -1
- package/resources/skill/references/aihub.md +74 -74
- package/resources/skill/references/app-api.md +78 -78
- package/resources/skill/references/architecture.md +40 -40
- package/resources/skill/references/checkout.md +105 -105
- package/resources/skill/references/custom-services.md +6 -6
- package/resources/skill/references/data.md +168 -168
- package/resources/skill/references/methods.md +3 -0
- package/resources/skill/references/modules.md +48 -48
- package/resources/skill/references/runtime.md +95 -96
- package/resources/skill/story/SKILL.md +264 -264
- package/src/commands/help.js +72 -72
- package/src/commands/listTargets.js +12 -12
- package/src/commands/status.js +2 -2
- package/src/commands/uninstall.js +45 -45
- package/src/commands/update.js +20 -20
- package/src/customServices.js +5 -4
- package/src/detect.js +14 -14
- package/src/fsx.js +67 -67
- package/src/index.js +25 -25
- package/src/localRuntime/detect.js +76 -76
- package/src/localRuntime/mysqlClient.js +138 -138
- package/src/logger.js +37 -37
- package/src/mcp/client.js +586 -595
- package/src/mcp/hosts.js +520 -520
- package/src/mcp/protocol.js +184 -164
- package/src/prompt.js +94 -94
- package/src/updateCheck.js +16 -16
package/src/commands/help.js
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
3
|
const { all } = require('../targets');
|
|
4
|
-
const { HOSTS } = require('../mcp/hosts');
|
|
5
|
-
const { getPackageVersion } = require('../skill');
|
|
6
|
-
|
|
7
|
-
function help() {
|
|
8
|
-
const targets = all.map((i) => ` ${i.name.padEnd(12)} ${i.displayName}`).join('\n');
|
|
9
|
-
const mcpTargets = HOSTS.map((host) => {
|
|
10
|
-
const status = host.supported === false ? 'unsupported' : host.path;
|
|
11
|
-
return ` ${host.name.padEnd(12)} ${status}`;
|
|
12
|
-
}).join('\n');
|
|
13
|
-
|
|
14
|
-
console.log(`draftgo v${getPackageVersion()} - DraftGo workbench CLI for AI coding agents
|
|
15
|
-
|
|
4
|
+
const { HOSTS } = require('../mcp/hosts');
|
|
5
|
+
const { getPackageVersion } = require('../skill');
|
|
6
|
+
|
|
7
|
+
function help() {
|
|
8
|
+
const targets = all.map((i) => ` ${i.name.padEnd(12)} ${i.displayName}`).join('\n');
|
|
9
|
+
const mcpTargets = HOSTS.map((host) => {
|
|
10
|
+
const status = host.supported === false ? 'unsupported' : host.path;
|
|
11
|
+
return ` ${host.name.padEnd(12)} ${status}`;
|
|
12
|
+
}).join('\n');
|
|
13
|
+
|
|
14
|
+
console.log(`draftgo v${getPackageVersion()} - DraftGo workbench CLI for AI coding agents
|
|
15
|
+
|
|
16
16
|
DraftGo shell baseline: React + Vite. Database Pages default to native HTML,
|
|
17
17
|
Tailwind CSS 4, and Basecoat UI; Oat UI remains an optional Web Components layer.
|
|
18
18
|
MCP handles live discovery and structured resources; complete page, navigation,
|
|
19
19
|
document, and custom-service bodies use checkout/commit outside MCP context.
|
|
20
20
|
The installed Skill routes agents to task-specific References and MCP tools.
|
|
21
|
-
|
|
22
|
-
Usage:
|
|
23
|
-
draftgo init [<target>...] Install the DraftGo Skill. No target = detect.
|
|
24
|
-
Use "all" to install every Skill target.
|
|
25
|
-
draftgo update [<target>...] Fully update the global CLI, then atomically
|
|
26
|
-
refresh installed or detected Skills.
|
|
27
|
-
draftgo uninstall <target|all> Remove Skill files. --purge also removes
|
|
28
|
-
the entire .draftgo/ runtime directory.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
draftgo init [<target>...] Install the DraftGo Skill. No target = detect.
|
|
24
|
+
Use "all" to install every Skill target.
|
|
25
|
+
draftgo update [<target>...] Fully update the global CLI, then atomically
|
|
26
|
+
refresh installed or detected Skills.
|
|
27
|
+
draftgo uninstall <target|all> Remove Skill files. --purge also removes
|
|
28
|
+
the entire .draftgo/ runtime directory.
|
|
29
29
|
draftgo status Show connection health, current scope, targets, and Skill version.
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
draftgo connect [<target>...] Verify and save server/API Key, then configure
|
|
32
|
-
a detected or explicit MCP host. This never
|
|
33
|
-
downloads DraftGo business resources.
|
|
34
|
-
draftgo mcp setup [<target>...] Merge project-level stdio MCP configuration.
|
|
35
|
-
draftgo mcp status [<target>...] Check host configuration and secret safety.
|
|
32
|
+
a detected or explicit MCP host. This never
|
|
33
|
+
downloads DraftGo business resources.
|
|
34
|
+
draftgo mcp setup [<target>...] Merge project-level stdio MCP configuration.
|
|
35
|
+
draftgo mcp status [<target>...] Check host configuration and secret safety.
|
|
36
36
|
draftgo mcp test Test project, resource, and API MCP calls.
|
|
37
37
|
draftgo mcp serve Bridge local stdio to the remote /mcp endpoint.
|
|
38
38
|
|
|
@@ -41,7 +41,7 @@ Usage:
|
|
|
41
41
|
Use --route/--title for exact selection.
|
|
42
42
|
draftgo checkout <type> <id...> Download a long-form body + verified base,
|
|
43
43
|
including custom-service worktrees.
|
|
44
|
-
--force explicitly replaces local changes.
|
|
44
|
+
--force explicitly replaces local changes.
|
|
45
45
|
draftgo check [custom-services <id...>]
|
|
46
46
|
Validate all checkouts or selected custom services.
|
|
47
47
|
--remote also compares remote hash/version.
|
|
@@ -73,20 +73,20 @@ Usage:
|
|
|
73
73
|
Publish current validated drafts; batch errors
|
|
74
74
|
report services already completed.
|
|
75
75
|
draftgo reconcile <type> <id...> Repair stale metadata only when local equals remote.
|
|
76
|
-
draftgo conflicts List unresolved conflicts; --all includes resolved.
|
|
77
|
-
draftgo conflict show <type> <id>
|
|
78
|
-
Show preserved base/local/remote paths.
|
|
79
|
-
draftgo conflict resolve <type> <id>
|
|
80
|
-
Mark a merged worktree file ready against the
|
|
81
|
-
preserved remote base; then check/diff/commit.
|
|
76
|
+
draftgo conflicts List unresolved conflicts; --all includes resolved.
|
|
77
|
+
draftgo conflict show <type> <id>
|
|
78
|
+
Show preserved base/local/remote paths.
|
|
79
|
+
draftgo conflict resolve <type> <id>
|
|
80
|
+
Mark a merged worktree file ready against the
|
|
81
|
+
preserved remote base; then check/diff/commit.
|
|
82
82
|
draftgo clean [--dry-run|--yes] Plan or remove all tmp and registered artifacts.
|
|
83
83
|
draftgo work start <item> Add an active item to .draftgo/worklog.md.
|
|
84
84
|
draftgo work add <item> Add a pending item.
|
|
85
85
|
draftgo work start-item <ref> Mark an item active; ref is number or date#number.
|
|
86
86
|
draftgo work complete <ref> Mark an item complete; --note appends evidence.
|
|
87
87
|
draftgo work show|list Show the worklog or its structured item list.
|
|
88
|
-
|
|
89
|
-
draftgo api <query> Search the live API contract through MCP.
|
|
88
|
+
|
|
89
|
+
draftgo api <query> Search the live API contract through MCP.
|
|
90
90
|
draftgo api describe <operation_id>
|
|
91
91
|
Describe one live API operation through MCP.
|
|
92
92
|
draftgo api call <operation_id> --input <json-file>
|
|
@@ -105,24 +105,24 @@ Usage:
|
|
|
105
105
|
Manage db_meta record policies (none|owner|all).
|
|
106
106
|
draftgo delete <operation_id> [id]
|
|
107
107
|
Confirm and call a live delete operation.
|
|
108
|
-
draftgo deploy [<type> <id...>] Check, then diff or commit checked-out content.
|
|
109
|
-
--delivery local needs no IDs; preview/deploy
|
|
110
|
-
require an explicit type and IDs.
|
|
108
|
+
draftgo deploy [<type> <id...>] Check, then diff or commit checked-out content.
|
|
109
|
+
--delivery local needs no IDs; preview/deploy
|
|
110
|
+
require an explicit type and IDs.
|
|
111
111
|
draftgo auto-push [<type> <id...>]
|
|
112
112
|
With config.auto_push=true, check and commit
|
|
113
113
|
changed checkouts. Any conflict stops the run.
|
|
114
114
|
draftgo local setup|start|stop|logs|status
|
|
115
115
|
Manage the project-local Docker stack.
|
|
116
|
-
draftgo list-targets List Skill installation targets.
|
|
117
|
-
draftgo -v | --version Print CLI version.
|
|
118
|
-
draftgo -h | --help Show this help.
|
|
119
|
-
|
|
120
|
-
Resource types:
|
|
116
|
+
draftgo list-targets List Skill installation targets.
|
|
117
|
+
draftgo -v | --version Print CLI version.
|
|
118
|
+
draftgo -h | --help Show this help.
|
|
119
|
+
|
|
120
|
+
Resource types:
|
|
121
121
|
pages | nav/navigations | docs/articles | custom-services
|
|
122
|
-
|
|
123
|
-
Important flags:
|
|
124
|
-
--project <dir> Operate on <dir> instead of the current directory.
|
|
125
|
-
--target <name,...> Select one or more MCP setup/status targets.
|
|
122
|
+
|
|
123
|
+
Important flags:
|
|
124
|
+
--project <dir> Operate on <dir> instead of the current directory.
|
|
125
|
+
--target <name,...> Select one or more MCP setup/status targets.
|
|
126
126
|
--server <url> (connect) DraftGo base URL.
|
|
127
127
|
--mcp-url <url> (connect) Explicit endpoint; standard /mcp derives the
|
|
128
128
|
base URL; custom paths also need --server.
|
|
@@ -130,11 +130,11 @@ Important flags:
|
|
|
130
130
|
--scope-type space --space-id <id>
|
|
131
131
|
(connect) Save a target space for workspace resources.
|
|
132
132
|
--timeout <ms> (connect/mcp test) Network timeout.
|
|
133
|
-
--allow-offline (connect) Save only after explicitly accepting a
|
|
134
|
-
failed MCP validation.
|
|
135
|
-
--no-mcp-setup (connect) Do not write host MCP configuration.
|
|
136
|
-
--force (init/checkout) Overwrite Skill or local changes.
|
|
137
|
-
--purge (uninstall) Also remove the .draftgo/ directory.
|
|
133
|
+
--allow-offline (connect) Save only after explicitly accepting a
|
|
134
|
+
failed MCP validation.
|
|
135
|
+
--no-mcp-setup (connect) Do not write host MCP configuration.
|
|
136
|
+
--force (init/checkout) Overwrite Skill or local changes.
|
|
137
|
+
--purge (uninstall) Also remove the .draftgo/ directory.
|
|
138
138
|
--skip-update-check (update) Refresh Skills without contacting npm.
|
|
139
139
|
--connect (init) Continue into DraftGo server connection.
|
|
140
140
|
--no-setup (init) Install the Skill without either setup flow.
|
|
@@ -150,8 +150,8 @@ Important flags:
|
|
|
150
150
|
--stat (diff) Print per-file and total change statistics.
|
|
151
151
|
--strict Treat check warnings as failures.
|
|
152
152
|
--remote (check) Compare checkout hashes/versions with remote.
|
|
153
|
-
--yes Skip supported confirmation prompts.
|
|
154
|
-
--operation-id <id> (delete) Select an operation explicitly.
|
|
153
|
+
--yes Skip supported confirmation prompts.
|
|
154
|
+
--operation-id <id> (delete) Select an operation explicitly.
|
|
155
155
|
--params <json> (api call/delete) Pass an API parameter object.
|
|
156
156
|
--input <file> (api call/delete/custom-service test) Read a UTF-8
|
|
157
157
|
JSON object.
|
|
@@ -176,23 +176,23 @@ Important flags:
|
|
|
176
176
|
fallback: DRAFTGO_BROWSER_PATH.
|
|
177
177
|
--selector <css> (verify) Require a visible element in top or iframe DOM.
|
|
178
178
|
--width/--height <px> (verify) Override the default 1440x900 desktop viewport.
|
|
179
|
-
|
|
180
|
-
Security:
|
|
179
|
+
|
|
180
|
+
Security:
|
|
181
181
|
.draftgo/config.json stores the server and API Key and is gitignored. Host MCP
|
|
182
182
|
files contain only "draftgo mcp serve"; they never contain the API Key.
|
|
183
|
-
|
|
184
|
-
MCP targets (project config or status):
|
|
185
|
-
${mcpTargets}
|
|
186
|
-
|
|
187
|
-
Skill targets:
|
|
188
|
-
${targets}
|
|
189
|
-
|
|
190
|
-
Examples:
|
|
191
|
-
draftgo init codex
|
|
183
|
+
|
|
184
|
+
MCP targets (project config or status):
|
|
185
|
+
${mcpTargets}
|
|
186
|
+
|
|
187
|
+
Skill targets:
|
|
188
|
+
${targets}
|
|
189
|
+
|
|
190
|
+
Examples:
|
|
191
|
+
draftgo init codex
|
|
192
192
|
draftgo connect codex --server https://draftgo.example --api-key <key>
|
|
193
193
|
draftgo mcp test
|
|
194
194
|
draftgo map --type pages --route /admin/channel-ops --summary --output json
|
|
195
|
-
draftgo checkout pages 42
|
|
195
|
+
draftgo checkout pages 42
|
|
196
196
|
draftgo verify pages 42
|
|
197
197
|
draftgo diff pages 42 --stat
|
|
198
198
|
draftgo commit pages 42
|
|
@@ -200,8 +200,8 @@ Examples:
|
|
|
200
200
|
draftgo test custom-services 12 --handler route:POST:/health --input request.json
|
|
201
201
|
draftgo work start "Build document management and role permissions"
|
|
202
202
|
draftgo work complete 1 --note "Verification and delivery passed"
|
|
203
|
-
draftgo deploy docs 7 --delivery preview
|
|
204
|
-
`);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
module.exports = help;
|
|
203
|
+
draftgo deploy docs 7 --delivery preview
|
|
204
|
+
`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = help;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
3
|
const { all } = require('../targets');
|
|
4
|
-
|
|
5
|
-
function listTargets() {
|
|
6
|
-
console.log('draftgo 支持的 AI 工具(target):');
|
|
7
|
-
for (const t of all) {
|
|
8
|
-
console.log(` ${t.name.padEnd(12)} ${t.displayName}`);
|
|
9
|
-
}
|
|
10
|
-
return 0;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
module.exports = listTargets;
|
|
4
|
+
|
|
5
|
+
function listTargets() {
|
|
6
|
+
console.log('draftgo 支持的 AI 工具(target):');
|
|
7
|
+
for (const t of all) {
|
|
8
|
+
console.log(` ${t.name.padEnd(12)} ${t.displayName}`);
|
|
9
|
+
}
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = listTargets;
|
package/src/commands/status.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const log = require('../logger');
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const log = require('../logger');
|
|
4
4
|
const { all, resolveTargets } = require('../targets');
|
|
5
|
-
const { removePath } = require('../fsx');
|
|
6
|
-
const { dgDir } = require('../paths');
|
|
7
|
-
|
|
8
|
-
async function uninstall(projectDir, positional, flags) {
|
|
9
|
-
log.title('draftgo uninstall');
|
|
10
|
-
|
|
11
|
-
let resolved = [];
|
|
12
|
-
let unknown = [];
|
|
13
|
-
if (positional.length === 0) {
|
|
14
|
-
log.err('Please specify a target, or use `draftgo uninstall all` to remove every target.');
|
|
15
|
-
log.dim(` Supported targets: ${all.map((i) => i.name).join(', ')}`);
|
|
16
|
-
return 1;
|
|
17
|
-
}
|
|
18
|
-
if (positional.includes('all')) {
|
|
19
|
-
resolved = all.slice();
|
|
20
|
-
} else {
|
|
21
|
-
({ resolved, unknown } = resolveTargets(positional));
|
|
22
|
-
if (unknown.length) {
|
|
23
|
-
log.err(`未知 target:${unknown.join(', ')}`);
|
|
24
|
-
return 1;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
log.step('移除入口文件');
|
|
29
|
-
for (const t of resolved) {
|
|
30
|
-
try {
|
|
31
|
-
const removed = t.uninstall(projectDir);
|
|
32
|
-
if (removed) log.ok(`${t.displayName} 入口已移除`);
|
|
33
|
-
else log.dim(`${t.displayName} 入口不存在,跳过`);
|
|
34
|
-
} catch (e) {
|
|
35
|
-
log.err(`${t.displayName} 卸载失败:${e.message}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Each AI-tool skill directory is self-contained, so per-target uninstall
|
|
40
|
-
// already wiped its content above. Only --purge nukes runtime data under
|
|
5
|
+
const { removePath } = require('../fsx');
|
|
6
|
+
const { dgDir } = require('../paths');
|
|
7
|
+
|
|
8
|
+
async function uninstall(projectDir, positional, flags) {
|
|
9
|
+
log.title('draftgo uninstall');
|
|
10
|
+
|
|
11
|
+
let resolved = [];
|
|
12
|
+
let unknown = [];
|
|
13
|
+
if (positional.length === 0) {
|
|
14
|
+
log.err('Please specify a target, or use `draftgo uninstall all` to remove every target.');
|
|
15
|
+
log.dim(` Supported targets: ${all.map((i) => i.name).join(', ')}`);
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
if (positional.includes('all')) {
|
|
19
|
+
resolved = all.slice();
|
|
20
|
+
} else {
|
|
21
|
+
({ resolved, unknown } = resolveTargets(positional));
|
|
22
|
+
if (unknown.length) {
|
|
23
|
+
log.err(`未知 target:${unknown.join(', ')}`);
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
log.step('移除入口文件');
|
|
29
|
+
for (const t of resolved) {
|
|
30
|
+
try {
|
|
31
|
+
const removed = t.uninstall(projectDir);
|
|
32
|
+
if (removed) log.ok(`${t.displayName} 入口已移除`);
|
|
33
|
+
else log.dim(`${t.displayName} 入口不存在,跳过`);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
log.err(`${t.displayName} 卸载失败:${e.message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Each AI-tool skill directory is self-contained, so per-target uninstall
|
|
40
|
+
// already wiped its content above. Only --purge nukes runtime data under
|
|
41
41
|
// .draftgo/ (config/worklog/lessons/cache etc.).
|
|
42
42
|
if (flags.purge) {
|
|
43
43
|
if (removePath(dgDir(projectDir))) {
|
|
44
44
|
log.warn('已使用 --purge:.draftgo/(含 config/worklog/lessons 等运行时数据)已删除。');
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
log.title('完成');
|
|
49
|
-
return 0;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
module.exports = uninstall;
|
|
47
|
+
|
|
48
|
+
log.title('完成');
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = uninstall;
|
package/src/commands/update.js
CHANGED
|
@@ -66,7 +66,7 @@ async function updateCliIfNeeded(projectDir, positional, flags) {
|
|
|
66
66
|
}
|
|
67
67
|
return Number.isInteger(rerun.status) ? rerun.status : 1;
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
async function update(projectDir, positional, flags) {
|
|
71
71
|
log.title('draftgo update');
|
|
72
72
|
|
|
@@ -75,14 +75,14 @@ async function update(projectDir, positional, flags) {
|
|
|
75
75
|
|
|
76
76
|
const prev = readInstalledVersion(projectDir);
|
|
77
77
|
log.info(`当前已装 skill:${prev || '未安装'},CLI 版本:${getPackageVersion()}`);
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
// Determine which entry files to refresh:
|
|
80
80
|
// - no positional → refresh installed targets plus project-level tool signals
|
|
81
81
|
// - "all" → refresh every known target
|
|
82
82
|
// - names → refresh the specified targets
|
|
83
|
-
let resolved = [];
|
|
84
|
-
let unknown = [];
|
|
85
|
-
|
|
83
|
+
let resolved = [];
|
|
84
|
+
let unknown = [];
|
|
85
|
+
|
|
86
86
|
if (positional.length === 0) {
|
|
87
87
|
const byTarget = new Map();
|
|
88
88
|
for (const t of all) {
|
|
@@ -100,20 +100,20 @@ async function update(projectDir, positional, flags) {
|
|
|
100
100
|
log.info(`刷新目标:${resolved.map((r) => r.displayName).join(', ')}`);
|
|
101
101
|
}
|
|
102
102
|
} else {
|
|
103
|
-
({ resolved, unknown } = resolveTargets(positional));
|
|
104
|
-
if (unknown.length) {
|
|
105
|
-
log.err(`未知 target:${unknown.join(', ')}`);
|
|
106
|
-
return 1;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
103
|
+
({ resolved, unknown } = resolveTargets(positional));
|
|
104
|
+
if (unknown.length) {
|
|
105
|
+
log.err(`未知 target:${unknown.join(', ')}`);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
110
|
ensureRuntime(projectDir);
|
|
111
111
|
log.step('用 CLI 内置版本覆盖各 AI 工具 skill 目录');
|
|
112
112
|
const failures = [];
|
|
113
113
|
for (const t of resolved) {
|
|
114
|
-
try {
|
|
115
|
-
const r = t.install(projectDir, { force: true });
|
|
116
|
-
log.ok(`${t.displayName.padEnd(16)} → ${r.path}`);
|
|
114
|
+
try {
|
|
115
|
+
const r = t.install(projectDir, { force: true });
|
|
116
|
+
log.ok(`${t.displayName.padEnd(16)} → ${r.path}`);
|
|
117
117
|
} catch (e) {
|
|
118
118
|
log.err(`${t.displayName} 更新失败:${e.message}`);
|
|
119
119
|
failures.push(t.displayName);
|
|
@@ -132,11 +132,11 @@ async function update(projectDir, positional, flags) {
|
|
|
132
132
|
} else {
|
|
133
133
|
writeInstalledVersion(projectDir);
|
|
134
134
|
}
|
|
135
|
-
|
|
136
|
-
log.title('完成');
|
|
137
|
-
return 0;
|
|
138
|
-
}
|
|
139
|
-
|
|
135
|
+
|
|
136
|
+
log.title('完成');
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
140
|
module.exports = update;
|
|
141
141
|
module.exports.updateCliIfNeeded = updateCliIfNeeded;
|
|
142
142
|
module.exports.installedTargetsNotRefreshed = installedTargetsNotRefreshed;
|
package/src/customServices.js
CHANGED
|
@@ -316,7 +316,8 @@ async function commit(projectDir, ids, options = {}) {
|
|
|
316
316
|
if (entry.server !== config.server) throw new WorktreeError('CHECKOUT_SERVER_MISMATCH', 'Checkout belongs to a different DraftGo server.');
|
|
317
317
|
syncManagedDevelopmentFiles(entry.local_dir);
|
|
318
318
|
const state = localState(entry); if (!state.base_valid) throw new WorktreeError('BASE_HASH_MISMATCH', `Custom service ${id} base does not match its manifest.`);
|
|
319
|
-
|
|
319
|
+
const createDraft = Boolean(options.ensureDraft) && entry.checkout_source === 'live';
|
|
320
|
+
if (!state.modified && !createDraft) return { id, entry, state, unchanged: true };
|
|
320
321
|
const current = await metadata(config, id, options);
|
|
321
322
|
if (current.content_hash !== entry.base_hash || String(current.base_revision) !== String(entry.base_revision)) {
|
|
322
323
|
const remoteArchive = await download(config, current, options); const conflict = await writeConflict(projectDir, entry, current, remoteArchive);
|
|
@@ -406,7 +407,7 @@ function validationStale(error) {
|
|
|
406
407
|
&& error.details && error.details.reason === 'validation_stale';
|
|
407
408
|
}
|
|
408
409
|
async function validate(projectDir, id, options = {}) {
|
|
409
|
-
const config = options.config || loadProjectConfig(projectDir); await commit(projectDir, [id], { ...options, config });
|
|
410
|
+
const config = options.config || loadProjectConfig(projectDir); await commit(projectDir, [id], { ...options, config, ensureDraft: true });
|
|
410
411
|
const manifest = loadManifest(projectDir); const result = await validateCommitted(projectDir, id, config, manifest, options);
|
|
411
412
|
if (!result.cached) saveManifest(projectDir, manifest);
|
|
412
413
|
return result;
|
|
@@ -427,7 +428,7 @@ async function test(projectDir, id, input = {}, options = {}) {
|
|
|
427
428
|
let entry;
|
|
428
429
|
let revision;
|
|
429
430
|
if (source === 'draft') {
|
|
430
|
-
await commit(projectDir, [id], { ...options, config });
|
|
431
|
+
await commit(projectDir, [id], { ...options, config, ensureDraft: true });
|
|
431
432
|
manifest = loadManifest(projectDir);
|
|
432
433
|
const validation = await validateCommitted(projectDir, id, config, manifest, options);
|
|
433
434
|
entry = manifest.entries[entryKey(id)];
|
|
@@ -447,7 +448,7 @@ async function test(projectDir, id, input = {}, options = {}) {
|
|
|
447
448
|
}
|
|
448
449
|
async function publish(projectDir, ids, options = {}) {
|
|
449
450
|
const config = options.config || loadProjectConfig(projectDir); const targets = uniqueIDs(ids);
|
|
450
|
-
await commit(projectDir, targets, { ...options, config });
|
|
451
|
+
await commit(projectDir, targets, { ...options, config, ensureDraft: true });
|
|
451
452
|
const manifest = loadManifest(projectDir);
|
|
452
453
|
const validations = await Promise.all(targets.map((id) => validateCommitted(projectDir, id, config, manifest, options)));
|
|
453
454
|
if (validations.some((result) => !result.cached)) saveManifest(projectDir, manifest);
|
package/src/detect.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { exists } = require('./fsx');
|
|
5
5
|
const { platforms } = require('./platforms');
|
|
@@ -9,15 +9,15 @@ const { platforms } = require('./platforms');
|
|
|
9
9
|
const SIGNALS = Object.fromEntries(
|
|
10
10
|
platforms.map((p) => [p.name, p.signals || []])
|
|
11
11
|
);
|
|
12
|
-
|
|
13
|
-
function detectTargets(projectDir) {
|
|
14
|
-
const hits = [];
|
|
15
|
-
for (const [name, signals] of Object.entries(SIGNALS)) {
|
|
16
|
-
if (signals.some((s) => exists(path.join(projectDir, s)))) {
|
|
17
|
-
hits.push(name);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return hits;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
module.exports = { SIGNALS, detectTargets };
|
|
12
|
+
|
|
13
|
+
function detectTargets(projectDir) {
|
|
14
|
+
const hits = [];
|
|
15
|
+
for (const [name, signals] of Object.entries(SIGNALS)) {
|
|
16
|
+
if (signals.some((s) => exists(path.join(projectDir, s)))) {
|
|
17
|
+
hits.push(name);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return hits;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { SIGNALS, detectTargets };
|
package/src/fsx.js
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
function exists(p) {
|
|
7
|
-
try { fs.accessSync(p); return true; } catch { return false; }
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function ensureDir(dir) {
|
|
11
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function readText(p) {
|
|
15
|
-
return fs.readFileSync(p, 'utf8');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function writeText(p, content) {
|
|
19
|
-
ensureDir(path.dirname(p));
|
|
20
|
-
fs.writeFileSync(p, content, 'utf8');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function copyFile(src, dest) {
|
|
24
|
-
ensureDir(path.dirname(dest));
|
|
25
|
-
fs.copyFileSync(src, dest);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function copyDir(src, dest) {
|
|
29
|
-
if (!exists(src)) return;
|
|
30
|
-
ensureDir(dest);
|
|
31
|
-
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
32
|
-
const s = path.join(src, entry.name);
|
|
33
|
-
const d = path.join(dest, entry.name);
|
|
34
|
-
if (entry.isSymbolicLink()) {
|
|
35
|
-
ensureDir(path.dirname(d));
|
|
36
|
-
const target = fs.readlinkSync(s);
|
|
37
|
-
try { fs.symlinkSync(target, d); }
|
|
38
|
-
catch (e) { if (e.code !== 'EEXIST') throw e; }
|
|
39
|
-
} else if (entry.isDirectory()) copyDir(s, d);
|
|
40
|
-
else if (entry.isFile()) copyFile(s, d);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function removePath(p) {
|
|
45
|
-
if (!exists(p)) return false;
|
|
46
|
-
fs.rmSync(p, { recursive: true, force: true });
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function appendGitignoreLine(projectDir, line) {
|
|
51
|
-
const gi = path.join(projectDir, '.gitignore');
|
|
52
|
-
const content = exists(gi) ? readText(gi) : '';
|
|
53
|
-
if (content.split(/\r?\n/).some((l) => l.trim() === line)) return;
|
|
54
|
-
const sep = content && !content.endsWith('\n') ? '\n' : '';
|
|
55
|
-
fs.appendFileSync(gi, `${sep}${line}\n`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
module.exports = {
|
|
59
|
-
exists,
|
|
60
|
-
ensureDir,
|
|
61
|
-
readText,
|
|
62
|
-
writeText,
|
|
63
|
-
copyFile,
|
|
64
|
-
copyDir,
|
|
65
|
-
removePath,
|
|
66
|
-
appendGitignoreLine,
|
|
67
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function exists(p) {
|
|
7
|
+
try { fs.accessSync(p); return true; } catch { return false; }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function ensureDir(dir) {
|
|
11
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readText(p) {
|
|
15
|
+
return fs.readFileSync(p, 'utf8');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function writeText(p, content) {
|
|
19
|
+
ensureDir(path.dirname(p));
|
|
20
|
+
fs.writeFileSync(p, content, 'utf8');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function copyFile(src, dest) {
|
|
24
|
+
ensureDir(path.dirname(dest));
|
|
25
|
+
fs.copyFileSync(src, dest);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function copyDir(src, dest) {
|
|
29
|
+
if (!exists(src)) return;
|
|
30
|
+
ensureDir(dest);
|
|
31
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
32
|
+
const s = path.join(src, entry.name);
|
|
33
|
+
const d = path.join(dest, entry.name);
|
|
34
|
+
if (entry.isSymbolicLink()) {
|
|
35
|
+
ensureDir(path.dirname(d));
|
|
36
|
+
const target = fs.readlinkSync(s);
|
|
37
|
+
try { fs.symlinkSync(target, d); }
|
|
38
|
+
catch (e) { if (e.code !== 'EEXIST') throw e; }
|
|
39
|
+
} else if (entry.isDirectory()) copyDir(s, d);
|
|
40
|
+
else if (entry.isFile()) copyFile(s, d);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function removePath(p) {
|
|
45
|
+
if (!exists(p)) return false;
|
|
46
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function appendGitignoreLine(projectDir, line) {
|
|
51
|
+
const gi = path.join(projectDir, '.gitignore');
|
|
52
|
+
const content = exists(gi) ? readText(gi) : '';
|
|
53
|
+
if (content.split(/\r?\n/).some((l) => l.trim() === line)) return;
|
|
54
|
+
const sep = content && !content.endsWith('\n') ? '\n' : '';
|
|
55
|
+
fs.appendFileSync(gi, `${sep}${line}\n`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
exists,
|
|
60
|
+
ensureDir,
|
|
61
|
+
readText,
|
|
62
|
+
writeText,
|
|
63
|
+
copyFile,
|
|
64
|
+
copyDir,
|
|
65
|
+
removePath,
|
|
66
|
+
appendGitignoreLine,
|
|
67
|
+
};
|