draftgo-cli 3.0.35 → 3.0.39
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 +220 -272
- package/package.json +6 -2
- package/resources/skill/SKILL.md +114 -55
- package/resources/skill/init/SKILL.md +29 -15
- package/resources/skill/manifest.json +5 -4
- package/resources/skill/push/SKILL.md +41 -29
- package/resources/skill/references/aihub.md +8 -5
- package/resources/skill/references/api-endpoints.md +5 -3
- package/resources/skill/references/architecture.md +1 -1
- package/resources/skill/references/checkout.md +116 -0
- package/resources/skill/references/custom-services.md +9 -10
- package/resources/skill/references/data.md +4 -2
- package/resources/skill/references/frontend.md +1 -1
- package/resources/skill/references/mcp.md +101 -0
- package/resources/skill/references/modules.md +8 -8
- package/resources/skill/references/parallel.md +6 -3
- package/resources/skill/references/runtime.md +7 -10
- package/resources/skill/scripts/README.md +8 -0
- package/resources/skill/story/SKILL.md +8 -8
- package/src/cli.js +5 -0
- package/src/commandRegistry.js +7 -1
- package/src/commands/api.js +24 -187
- package/src/commands/autoPush.js +48 -17
- package/src/commands/check.js +17 -47
- package/src/commands/checkout.js +18 -0
- package/src/commands/commit.js +21 -0
- package/src/commands/conflict.js +30 -0
- package/src/commands/conflicts.js +16 -0
- package/src/commands/connect.js +60 -48
- package/src/commands/delete.js +79 -64
- package/src/commands/deploy.js +18 -10
- package/src/commands/diff.js +23 -0
- package/src/commands/help.js +99 -75
- package/src/commands/init.js +4 -10
- package/src/commands/local.js +23 -6
- package/src/commands/map.js +89 -89
- package/src/commands/mcp.js +126 -0
- package/src/commands/sync.js +28 -43
- package/src/commands/verifyUi.js +3 -2
- package/src/localdev/index.js +37 -7
- package/src/localdev/mysqlClient.js +1 -1
- package/src/mcp/client.js +275 -0
- package/src/mcp/hosts.js +520 -0
- package/src/mcp/protocol.js +173 -0
- package/src/mcp/stdio.js +300 -0
- package/src/mcp/tools.js +47 -0
- package/src/platforms.js +3 -4
- package/src/projectConfig.js +91 -49
- package/src/projectMap.js +123 -460
- package/src/skill.js +6 -28
- package/src/worktree/backend.js +326 -0
- package/src/worktree/errors.js +28 -0
- package/src/worktree/index.js +461 -0
- package/src/worktree/manifest.js +75 -0
- package/src/worktree/streams.js +200 -0
- package/src/worktree/types.js +103 -0
- package/src/worktree/validate.js +37 -0
- package/resources/skill/pull/SKILL.md +0 -33
- package/resources/skill/references/api.json +0 -20248
- package/resources/skill/scripts/draftgo_delete.py +0 -149
- package/resources/skill/scripts/draftgo_init.py +0 -80
- package/resources/skill/scripts/draftgo_pull.py +0 -427
- package/resources/skill/scripts/draftgo_push.py +0 -1022
- package/src/python.js +0 -27
package/src/commands/help.js
CHANGED
|
@@ -1,107 +1,131 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { all } = require('../installers');
|
|
4
|
+
const { HOSTS } = require('../mcp/hosts');
|
|
4
5
|
const { getPackageVersion } = require('../skill');
|
|
5
6
|
|
|
6
7
|
function help() {
|
|
7
8
|
const targets = all.map((i) => ` ${i.name.padEnd(12)} ${i.displayName}`).join('\n');
|
|
8
|
-
|
|
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
|
|
9
15
|
|
|
10
16
|
DraftGo Next frontend baseline: React + Vite.
|
|
11
17
|
Database pages use standard HTML + Tailwind CSS.
|
|
12
|
-
|
|
18
|
+
MCP handles live discovery and structured resources; complete page, navigation,
|
|
19
|
+
and document bodies use checkout/commit outside MCP context.
|
|
13
20
|
|
|
14
21
|
Usage:
|
|
15
|
-
draftgo init [<target>...] Install
|
|
16
|
-
Use "all" to install every target.
|
|
22
|
+
draftgo init [<target>...] Install the DraftGo Skill. No target = detect.
|
|
23
|
+
Use "all" to install every Skill target.
|
|
17
24
|
draftgo update [<target>...] Fully update the global CLI, then atomically
|
|
18
|
-
refresh
|
|
19
|
-
draftgo uninstall <target|all> Remove
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
draftgo
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
refresh installed or detected Skills.
|
|
26
|
+
draftgo uninstall <target|all> Remove Skill files. --purge also removes
|
|
27
|
+
the entire .draftgo/ runtime directory.
|
|
28
|
+
draftgo status Show installed targets and Skill version.
|
|
29
|
+
|
|
30
|
+
draftgo connect [<target>...] Verify and save server/SAT, then configure
|
|
31
|
+
a detected or explicit MCP host. This never
|
|
32
|
+
downloads DraftGo business resources.
|
|
33
|
+
draftgo mcp setup [<target>...] Merge project-level stdio MCP configuration.
|
|
34
|
+
draftgo mcp status [<target>...] Check host configuration and secret safety.
|
|
35
|
+
draftgo mcp test Test initialize, tools/list, and tools/call.
|
|
36
|
+
draftgo mcp serve Bridge local stdio to the remote /mcp endpoint.
|
|
37
|
+
|
|
38
|
+
draftgo map Read remote overview/resources through MCP and
|
|
39
|
+
overlay local checkout state.
|
|
40
|
+
draftgo checkout <type> <id...> Download pages/nav/docs body + verified base.
|
|
41
|
+
--force explicitly replaces local changes.
|
|
42
|
+
draftgo check Validate checked-out worktree/base files only.
|
|
43
|
+
draftgo diff <type> <id> Show checkout base versus local body.
|
|
44
|
+
draftgo commit <type> <id...> Validate and upload complete checked-out bodies.
|
|
45
|
+
draftgo conflicts List unresolved conflicts; --all includes resolved.
|
|
46
|
+
draftgo conflict show <type> <id>
|
|
47
|
+
Show preserved base/local/remote paths.
|
|
48
|
+
draftgo conflict resolve <type> <id>
|
|
49
|
+
Mark a merged worktree file ready against the
|
|
50
|
+
preserved remote base; then check/diff/commit.
|
|
27
51
|
draftgo verify-ui <url> Run a deterministic browser smoke check.
|
|
28
|
-
|
|
29
|
-
draftgo api <
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
draftgo
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Supports --batch <type> <ids> ... too.
|
|
52
|
+
|
|
53
|
+
draftgo api <query> Search the live API contract through MCP.
|
|
54
|
+
draftgo api describe <operation_id>
|
|
55
|
+
Describe one live API operation through MCP.
|
|
56
|
+
draftgo delete <operation_id> [id]
|
|
57
|
+
Confirm and call a live delete operation.
|
|
58
|
+
Legacy <type> <id> works only when unambiguous.
|
|
59
|
+
draftgo deploy [<type> <id...>] Check, then diff or commit checked-out content.
|
|
60
|
+
--delivery local needs no IDs; preview/deploy
|
|
61
|
+
require an explicit type and IDs.
|
|
62
|
+
draftgo auto-push [<type> <id...>]
|
|
63
|
+
With config.auto_push=true, check and commit
|
|
64
|
+
changed checkouts. Any conflict stops the run.
|
|
65
|
+
draftgo pull Migration notice only; always downloads nothing.
|
|
66
|
+
draftgo push <type> <id...> Deprecated alias to commit for pages/nav/docs.
|
|
67
|
+
Push-all and structured-resource push are removed.
|
|
68
|
+
|
|
46
69
|
draftgo local setup|start|stop|logs|status
|
|
47
|
-
Manage .
|
|
48
|
-
|
|
49
|
-
compatible aliases for start/stop.
|
|
50
|
-
draftgo list-targets List supported AI tools.
|
|
51
|
-
draftgo connect Bind this project to an existing DraftGo
|
|
52
|
-
server. Prompts for BaseURL + access
|
|
53
|
-
token (or pass --server / --token).
|
|
54
|
-
draftgo local setup One-click local DraftGo stack via Docker
|
|
55
|
-
(shared MySQL, Redis, Milvus + app). It
|
|
56
|
-
generates an app-only project compose,
|
|
57
|
-
<project>/.draftgo/docker/docker-compose.yaml,
|
|
58
|
-
then guides you to finish setup and paste
|
|
59
|
-
an access token.
|
|
70
|
+
Manage the existing local Docker stack. up/down
|
|
71
|
+
remain aliases for start/stop; status also has ps.
|
|
60
72
|
draftgo local-dev Compatibility alias for \`draftgo local setup\`.
|
|
73
|
+
draftgo list-targets List Skill installation targets.
|
|
61
74
|
draftgo -v | --version Print CLI version.
|
|
62
75
|
draftgo -h | --help Show this help.
|
|
63
76
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
draftgo check Local resource closure gate.
|
|
67
|
-
draftgo pull|push First-class resource sync wrappers.
|
|
77
|
+
Resource types:
|
|
78
|
+
pages | nav/navigations | docs/articles
|
|
68
79
|
|
|
69
|
-
|
|
80
|
+
Important flags:
|
|
70
81
|
--project <dir> Operate on <dir> instead of the current directory.
|
|
71
|
-
--
|
|
72
|
-
--
|
|
73
|
-
--
|
|
74
|
-
--
|
|
75
|
-
--connect
|
|
76
|
-
|
|
77
|
-
--no-setup
|
|
78
|
-
--
|
|
79
|
-
--
|
|
80
|
-
--
|
|
81
|
-
--
|
|
82
|
-
--
|
|
82
|
+
--target <name,...> Select one or more MCP setup/status targets.
|
|
83
|
+
--server <url> (connect) DraftGo base URL.
|
|
84
|
+
--token <sat> (connect) SAT for non-interactive use.
|
|
85
|
+
--timeout <ms> (connect/mcp test) Network timeout.
|
|
86
|
+
--allow-offline (connect) Save only after explicitly accepting a
|
|
87
|
+
failed MCP validation.
|
|
88
|
+
--no-mcp-setup (connect) Do not write host MCP configuration.
|
|
89
|
+
--force (init/checkout) Overwrite Skill or local changes.
|
|
90
|
+
--purge (uninstall) Also remove the .draftgo/ directory.
|
|
91
|
+
--skip-update-check (update) Refresh Skills without contacting npm.
|
|
92
|
+
--connect (init) Continue into DraftGo server connection.
|
|
93
|
+
--local-dev (init) Continue into local Docker setup.
|
|
94
|
+
--no-setup (init) Install the Skill without either setup flow.
|
|
95
|
+
--output json Print machine-readable output where supported.
|
|
96
|
+
--strict Treat check warnings as failures.
|
|
97
|
+
--yes Skip supported confirmation prompts.
|
|
98
|
+
--operation-id <id> (delete) Select an operation explicitly.
|
|
99
|
+
--params <json> (delete) Pass an API parameter object.
|
|
100
|
+
--input <file> (delete) Read the parameter object from JSON.
|
|
101
|
+
--delivery <mode> (deploy) local | preview | deploy.
|
|
102
|
+
--dry-run (push) Show diffs without committing.
|
|
103
|
+
--mobile-check <mode> (verify-ui) auto | always | never.
|
|
83
104
|
--screenshot <mode> (verify-ui) on-failure | always | never.
|
|
84
105
|
--browser <name> (verify-ui) chromium | chrome | msedge.
|
|
85
|
-
--selector <css> (verify-ui) Require a key element
|
|
106
|
+
--selector <css> (verify-ui) Require a visible key element.
|
|
86
107
|
--width/--height <px> (verify-ui) Override the default 390x844 viewport.
|
|
87
|
-
--delivery <mode> (deploy) local | preview | deploy.
|
|
88
108
|
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
Security:
|
|
110
|
+
.draftgo/config.json stores the server and SAT and is gitignored. Host MCP
|
|
111
|
+
files contain only "draftgo mcp serve"; they never contain the SAT.
|
|
112
|
+
|
|
113
|
+
MCP targets (project config or status):
|
|
114
|
+
${mcpTargets}
|
|
91
115
|
|
|
92
|
-
|
|
116
|
+
Skill targets:
|
|
93
117
|
${targets}
|
|
94
118
|
|
|
95
119
|
Examples:
|
|
96
|
-
draftgo init
|
|
97
|
-
draftgo
|
|
98
|
-
draftgo
|
|
99
|
-
draftgo
|
|
100
|
-
draftgo
|
|
101
|
-
draftgo
|
|
102
|
-
draftgo
|
|
103
|
-
draftgo
|
|
104
|
-
draftgo
|
|
120
|
+
draftgo init codex
|
|
121
|
+
draftgo connect codex --server https://draftgo.example --token <sat>
|
|
122
|
+
draftgo mcp test
|
|
123
|
+
draftgo map --output json
|
|
124
|
+
draftgo checkout pages 42
|
|
125
|
+
draftgo check --strict
|
|
126
|
+
draftgo diff pages 42
|
|
127
|
+
draftgo commit pages 42
|
|
128
|
+
draftgo deploy docs 7 --delivery preview
|
|
105
129
|
`);
|
|
106
130
|
}
|
|
107
131
|
|
package/src/commands/init.js
CHANGED
|
@@ -5,18 +5,12 @@ const log = require('../logger');
|
|
|
5
5
|
const { detectTargets } = require('../detect');
|
|
6
6
|
const { resolveTargets, all } = require('../installers');
|
|
7
7
|
const { ensureRuntime, writeInstalledVersion, getPackageVersion } = require('../skill');
|
|
8
|
-
const { findPython } = require('../python');
|
|
9
8
|
const { exists } = require('../fsx');
|
|
10
9
|
const { ask } = require('../prompt');
|
|
11
10
|
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
log.warn('Python 未检测到。DraftGo 资源同步需要 Python 3.9+。');
|
|
16
|
-
log.dim(' 安装 Python 3.9+ 后运行 `draftgo pull` 获取项目资源。');
|
|
17
|
-
} else {
|
|
18
|
-
log.dim(` detected ${py.bin} (${py.version})`);
|
|
19
|
-
}
|
|
11
|
+
function showEnvironment() {
|
|
12
|
+
log.dim(` Node.js ${process.versions.node}`);
|
|
13
|
+
log.dim(' DraftGo resources use MCP discovery; long content uses checkout/commit.');
|
|
20
14
|
}
|
|
21
15
|
|
|
22
16
|
async function init(projectDir, positional, flags) {
|
|
@@ -77,7 +71,7 @@ async function init(projectDir, positional, flags) {
|
|
|
77
71
|
|
|
78
72
|
// 3) Environment check (advisory).
|
|
79
73
|
log.step('环境检查');
|
|
80
|
-
|
|
74
|
+
showEnvironment();
|
|
81
75
|
|
|
82
76
|
// 4) Optional server binding flow.
|
|
83
77
|
const cfgPath = path.join(projectDir, '.draftgo', 'config.json');
|
package/src/commands/local.js
CHANGED
|
@@ -27,12 +27,29 @@ function runCompose(projectDir, args, stdio = 'inherit') {
|
|
|
27
27
|
return 1;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
let result;
|
|
31
|
+
try {
|
|
32
|
+
result = spawnSync(docker.composeCmd, [...docker.composeArgs, '-f', file, ...args], {
|
|
33
|
+
cwd: path.dirname(file),
|
|
34
|
+
stdio,
|
|
35
|
+
shell: false,
|
|
36
|
+
});
|
|
37
|
+
} catch (error) {
|
|
38
|
+
log.err(`Failed to start Docker Compose: ${error.message}`);
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (result.error) {
|
|
43
|
+
log.err(`Failed to start Docker Compose: ${result.error.message}`);
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isInteger(result.status)) {
|
|
47
|
+
log.err(result.signal
|
|
48
|
+
? `Docker Compose terminated by signal ${result.signal}.`
|
|
49
|
+
: 'Docker Compose did not report an exit status.');
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
return result.status;
|
|
36
53
|
}
|
|
37
54
|
|
|
38
55
|
function local(projectDir, positional, flags = {}) {
|
package/src/commands/map.js
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
3
5
|
const log = require('../logger');
|
|
4
|
-
const {
|
|
6
|
+
const { loadProjectConfig } = require('../projectConfig');
|
|
7
|
+
const { loadManifest, absolutePath } = require('../worktree/manifest');
|
|
8
|
+
const { hashFile } = require('../worktree/streams');
|
|
9
|
+
const { TOOL_NAMES, openToolSession, callStructured } = require('../mcp/tools');
|
|
10
|
+
|
|
11
|
+
function itemsFrom(value) {
|
|
12
|
+
if (Array.isArray(value)) return value;
|
|
13
|
+
if (!value || typeof value !== 'object') return [];
|
|
14
|
+
if (Array.isArray(value.items)) return value.items;
|
|
15
|
+
if (Array.isArray(value.resources)) return value.resources;
|
|
16
|
+
if (value.data) return itemsFrom(value.data);
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function nextCursor(value) {
|
|
21
|
+
return value && (value.next_cursor || value.nextCursor || value.cursor && value.has_more && value.cursor);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function listRemoteResources(session, flags) {
|
|
25
|
+
const resources = [];
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
let cursor = null;
|
|
28
|
+
for (let page = 0; page < 100; page += 1) {
|
|
29
|
+
const args = {};
|
|
30
|
+
if (cursor) args.cursor = cursor;
|
|
31
|
+
if (flags.type) args.resource_type = String(flags.type);
|
|
32
|
+
const payload = await callStructured(session, TOOL_NAMES.resourceList, args);
|
|
33
|
+
resources.push(...itemsFrom(payload));
|
|
34
|
+
cursor = nextCursor(payload);
|
|
35
|
+
if (!cursor) break;
|
|
36
|
+
if (seen.has(cursor)) throw new Error('DraftGo resource_list repeated a cursor.');
|
|
37
|
+
seen.add(cursor);
|
|
38
|
+
}
|
|
39
|
+
if (cursor) throw new Error('DraftGo resource_list exceeded 100 pages.');
|
|
40
|
+
return resources;
|
|
41
|
+
}
|
|
5
42
|
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
43
|
+
async function localCheckouts(projectDir) {
|
|
44
|
+
const manifest = loadManifest(projectDir);
|
|
45
|
+
const entries = [];
|
|
46
|
+
for (const entry of Object.values(manifest.entries)) {
|
|
47
|
+
const local = absolutePath(projectDir, entry.local_path);
|
|
48
|
+
let currentHash = null;
|
|
49
|
+
if (fs.existsSync(local)) currentHash = (await hashFile(local)).hash;
|
|
50
|
+
entries.push({
|
|
51
|
+
...entry,
|
|
52
|
+
exists: currentHash !== null,
|
|
53
|
+
current_hash: currentHash,
|
|
54
|
+
changed: currentHash !== null && currentHash !== entry.base_hash,
|
|
55
|
+
});
|
|
12
56
|
}
|
|
13
|
-
|
|
57
|
+
return entries;
|
|
14
58
|
}
|
|
15
59
|
|
|
16
|
-
function
|
|
17
|
-
|
|
60
|
+
function legacyCaches(projectDir) {
|
|
61
|
+
return ['pages', 'navigations', 'docs', 'db_meta', 'custom_scripts', 'aihub', 'roles', 'users', 'system_config']
|
|
62
|
+
.filter((name) => fs.existsSync(path.join(projectDir, '.draftgo', name, 'index.json')));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function mapCommand(projectDir, flags = {}) {
|
|
66
|
+
const config = loadProjectConfig(projectDir);
|
|
67
|
+
const session = await openToolSession(config, [TOOL_NAMES.projectOverview, TOOL_NAMES.resourceList]);
|
|
68
|
+
const [overview, resources, checkouts] = await Promise.all([
|
|
69
|
+
callStructured(session, TOOL_NAMES.projectOverview, {}),
|
|
70
|
+
listRemoteResources(session, flags),
|
|
71
|
+
localCheckouts(projectDir),
|
|
72
|
+
]);
|
|
73
|
+
const result = {
|
|
74
|
+
server: config.server,
|
|
75
|
+
overview,
|
|
76
|
+
resources,
|
|
77
|
+
checkouts,
|
|
78
|
+
legacy_cache: { ignored: true, detected: legacyCaches(projectDir) },
|
|
79
|
+
};
|
|
18
80
|
|
|
19
81
|
if (flags.output === 'json') {
|
|
20
|
-
console.log(JSON.stringify(
|
|
82
|
+
console.log(JSON.stringify(result, null, 2));
|
|
21
83
|
return 0;
|
|
22
84
|
}
|
|
23
|
-
|
|
24
85
|
log.title('draftgo map');
|
|
25
|
-
log.info(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
printList('自定义脚本', map.custom_scripts, (s) => {
|
|
43
|
-
const bindings = [
|
|
44
|
-
...(s.routes || []),
|
|
45
|
-
...(s.events || []).map((event) => `EVENT ${event}`),
|
|
46
|
-
...(s.schedules || []).map((cron) => `CRON ${cron}`),
|
|
47
|
-
];
|
|
48
|
-
const summary = bindings.length ? ` [${bindings.join(' | ')}]` : '';
|
|
49
|
-
return `${s.slug || '(无 slug)'} ${s.mode || 'mode?'} ${s.name || ''}${summary}${s.code_file ? ` ${s.code_file}` : ''}`;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
printList('文档', map.docs, (d) => {
|
|
53
|
-
return `${d.id || 'new'} ${d.slug || '(无 slug)'} ${d.title || '未命名'}${d.content_file ? ` ${d.content_file}` : ''}`;
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
printList('文档分类', map.doc_categories, (c) => {
|
|
57
|
-
const parent = c.parent_id == null ? 'root' : `parent:${c.parent_id}`;
|
|
58
|
-
return `${c.id || 'new'} ${c.slug || '(无 slug)'} ${c.name || '未命名'} ${parent}`;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
printList('系统配置', map.system_config, (c) => {
|
|
62
|
-
const sensitive = c.is_sensitive ? ' sensitive' : '';
|
|
63
|
-
return `${c.config_key || '(无 key)'} ${c.category || 'default'} ${c.value_type || 'value'}${sensitive}`;
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
printList('AIHub', map.aihub, (a) => {
|
|
67
|
-
const extra = [];
|
|
68
|
-
if (a.type === 'agent') {
|
|
69
|
-
if (a.mode) extra.push(a.mode);
|
|
70
|
-
if (a.output_format && a.output_format.mode === 'json') extra.push(`json:${a.output_format.json_strategy || 'auto'}`);
|
|
71
|
-
if (a.model_selection && a.model_selection.user_selectable) extra.push('user-model');
|
|
72
|
-
if (a.tools && a.tools.sources.length) extra.push(`tools:${a.tools.sources.length}`);
|
|
73
|
-
} else if (a.type === 'model') {
|
|
74
|
-
extra.push(`models:${a.models_count || 0}`);
|
|
75
|
-
if (a.supports_response_format === true) extra.push('response_format');
|
|
76
|
-
if (a.supports_json_schema === true) extra.push('json_schema');
|
|
77
|
-
} else if (a.type === 'mcp') {
|
|
78
|
-
extra.push(a.transport || 'transport?');
|
|
79
|
-
extra.push(`tools:${a.tools_count || 0}`);
|
|
80
|
-
}
|
|
81
|
-
return `${a.id || 'new'} ${a.type || ''} ${a.name || ''}${extra.length ? ` [${extra.join(', ')}]` : ''}`;
|
|
82
|
-
});
|
|
83
|
-
printList('角色', map.roles, (r) => `${r.code || r.id || 'new'} ${r.name || ''}`);
|
|
84
|
-
|
|
85
|
-
console.log('');
|
|
86
|
-
log.info('页面分区:');
|
|
87
|
-
const areaRows = [
|
|
88
|
-
['home', map.pageGroups.home.length],
|
|
89
|
-
['admin', map.pageGroups.admin.length],
|
|
90
|
-
['business', map.pageGroups.business.length],
|
|
91
|
-
['system', map.pageGroups.system.length],
|
|
92
|
-
['unknown', map.pageGroups.unknown.length],
|
|
93
|
-
];
|
|
94
|
-
log.dim(' ' + areaRows.map(([k, v]) => `${k} ${v}`).join(' '));
|
|
95
|
-
|
|
96
|
-
console.log('');
|
|
97
|
-
log.info('入口引用:');
|
|
98
|
-
const routes = Object.keys(map.routeRefs).sort();
|
|
99
|
-
if (routes.length === 0) log.dim(' (未发现 data-page-route / href 引用)');
|
|
100
|
-
else routes.forEach((route) => log.dim(` • ${route} ← ${map.routeRefs[route].join(', ')}`));
|
|
101
|
-
|
|
86
|
+
log.info(`Remote resources: ${resources.length}`);
|
|
87
|
+
const counts = new Map();
|
|
88
|
+
for (const resource of resources) {
|
|
89
|
+
const type = resource.resource_type || resource.type || 'unknown';
|
|
90
|
+
counts.set(type, (counts.get(type) || 0) + 1);
|
|
91
|
+
}
|
|
92
|
+
for (const [type, count] of [...counts.entries()].sort()) log.plain(` ${type}: ${count}`);
|
|
93
|
+
log.info(`Local checkouts: ${checkouts.length}`);
|
|
94
|
+
for (const entry of checkouts) {
|
|
95
|
+
const state = !entry.exists ? 'missing' : entry.changed ? 'modified' : 'clean';
|
|
96
|
+
log.plain(` ${entry.resource_type} ${entry.resource_id}: ${state} (${entry.local_path})`);
|
|
97
|
+
}
|
|
98
|
+
if (result.legacy_cache.detected.length) {
|
|
99
|
+
log.warn(`Ignored legacy cache indexes: ${result.legacy_cache.detected.join(', ')}`);
|
|
100
|
+
}
|
|
102
101
|
return 0;
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
module.exports = mapCommand;
|
|
105
|
+
module.exports.itemsFrom = itemsFrom;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const log = require('../logger');
|
|
4
|
+
const { loadProjectConfig } = require('../projectConfig');
|
|
5
|
+
const { testConnection } = require('../mcp/client');
|
|
6
|
+
const { redactText } = require('../mcp/protocol');
|
|
7
|
+
const {
|
|
8
|
+
HOSTS,
|
|
9
|
+
detectHostTargets,
|
|
10
|
+
setupHosts,
|
|
11
|
+
statusHosts,
|
|
12
|
+
} = require('../mcp/hosts');
|
|
13
|
+
const { serveStdio } = require('../mcp/stdio');
|
|
14
|
+
|
|
15
|
+
function targetArgs(positional, flags) {
|
|
16
|
+
const values = positional.slice();
|
|
17
|
+
if (flags.target) values.push(...String(flags.target).split(','));
|
|
18
|
+
return values.map((value) => String(value).trim()).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function printUsage() {
|
|
22
|
+
log.plain('Usage:');
|
|
23
|
+
log.plain(' draftgo mcp setup [target...]');
|
|
24
|
+
log.plain(' draftgo mcp status [target...]');
|
|
25
|
+
log.plain(' draftgo mcp test');
|
|
26
|
+
log.plain(' draftgo mcp serve');
|
|
27
|
+
log.dim(` targets: ${HOSTS.map((host) => host.name).join(', ')}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function setup(projectDir, positional = [], flags = {}) {
|
|
31
|
+
const targets = targetArgs(positional, flags);
|
|
32
|
+
const detected = targets.length ? null : detectHostTargets(projectDir);
|
|
33
|
+
if (!targets.length && detected.length === 0) {
|
|
34
|
+
log.err('No supported AI host was detected in this project.');
|
|
35
|
+
log.dim(' Run `draftgo mcp setup <target>` to select one explicitly.');
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const results = setupHosts(projectDir, targets.length ? targets : detected.map((host) => host.name));
|
|
40
|
+
let configured = 0;
|
|
41
|
+
let unsupported = 0;
|
|
42
|
+
for (const result of results) {
|
|
43
|
+
if (!result.supported) {
|
|
44
|
+
unsupported += 1;
|
|
45
|
+
log.warn(`${result.host.displayName}: ${result.reason}`);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
configured += 1;
|
|
49
|
+
if (result.changed) log.ok(`${result.host.displayName}: ${result.host.path}`);
|
|
50
|
+
else log.info(`${result.host.displayName}: already configured`);
|
|
51
|
+
}
|
|
52
|
+
if (!configured && unsupported) return 1;
|
|
53
|
+
if (!configured) {
|
|
54
|
+
log.err('No MCP host configuration was written.');
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function status(projectDir, positional = [], flags = {}) {
|
|
61
|
+
const targets = targetArgs(positional, flags);
|
|
62
|
+
const results = statusHosts(projectDir, targets);
|
|
63
|
+
log.title('draftgo mcp status');
|
|
64
|
+
let exitCode = 0;
|
|
65
|
+
try {
|
|
66
|
+
const config = loadProjectConfig(projectDir);
|
|
67
|
+
log.plain(` project configured (${config.server}; SAT present)`);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
log.plain(` project not ready (${error.message})`);
|
|
70
|
+
exitCode = 1;
|
|
71
|
+
}
|
|
72
|
+
for (const result of results) {
|
|
73
|
+
if (!result.supported) {
|
|
74
|
+
log.plain(` - ${result.host.name.padEnd(12)} unsupported`);
|
|
75
|
+
} else if (result.configured && result.secure) {
|
|
76
|
+
log.plain(` OK ${result.host.name.padEnd(12)} ${result.host.path}`);
|
|
77
|
+
} else if (result.error) {
|
|
78
|
+
log.plain(` x ${result.host.name.padEnd(12)} invalid config`);
|
|
79
|
+
exitCode = 1;
|
|
80
|
+
} else {
|
|
81
|
+
log.plain(` - ${result.host.name.padEnd(12)} not configured`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return exitCode;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function test(projectDir, _positional = [], flags = {}) {
|
|
88
|
+
let config;
|
|
89
|
+
try {
|
|
90
|
+
config = loadProjectConfig(projectDir);
|
|
91
|
+
const result = await testConnection(config, {
|
|
92
|
+
timeoutMs: flags.timeout ? Number(flags.timeout) : undefined,
|
|
93
|
+
});
|
|
94
|
+
log.ok('DraftGo MCP initialize succeeded.');
|
|
95
|
+
log.ok(`tools/list returned ${result.tools.length} tools.`);
|
|
96
|
+
log.ok(`tools/call succeeded: ${result.testedTool}`);
|
|
97
|
+
log.dim(` protocol: ${result.protocolVersion}`);
|
|
98
|
+
return 0;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const token = config && String(config.token || config.sat || '');
|
|
101
|
+
log.err(redactText(error && error.message ? error.message : error, [token]));
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function serve(projectDir, _positional = [], _flags = {}) {
|
|
107
|
+
return serveStdio(projectDir);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function mcp(projectDir, positional = [], flags = {}) {
|
|
111
|
+
const [action, ...rest] = positional;
|
|
112
|
+
if (action === 'setup') return setup(projectDir, rest, flags);
|
|
113
|
+
if (action === 'status') return status(projectDir, rest, flags);
|
|
114
|
+
if (action === 'test') return test(projectDir, rest, flags);
|
|
115
|
+
if (action === 'serve') return serve(projectDir, rest, flags);
|
|
116
|
+
printUsage();
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
mcp.setup = setup;
|
|
121
|
+
mcp.status = status;
|
|
122
|
+
mcp.test = test;
|
|
123
|
+
mcp.serve = serve;
|
|
124
|
+
mcp.printUsage = printUsage;
|
|
125
|
+
|
|
126
|
+
module.exports = mcp;
|