draftgo-cli 3.0.56 → 4.0.22
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 +169 -297
- package/package.json +12 -7
- package/resources/custom-service-sdk/ai.go +520 -0
- package/resources/custom-service-sdk/ai_test.go +156 -0
- package/resources/custom-service-sdk/billing.go +596 -0
- package/resources/custom-service-sdk/billing_test.go +150 -0
- package/resources/custom-service-sdk/go.mod +3 -0
- package/resources/custom-service-sdk/manifest.json +72 -0
- package/resources/custom-service-sdk/platform.go +360 -0
- package/resources/custom-service-sdk/platform_logger_test.go +24 -0
- package/resources/custom-service-sdk/registration_test.go +39 -0
- package/resources/custom-service-sdk/resources.go +246 -0
- package/resources/custom-service-sdk/resources_billing_test.go +115 -0
- package/resources/custom-service-sdk/resources_files_test.go +57 -0
- package/resources/custom-service-sdk/resources_scope_test.go +87 -0
- package/resources/custom-service-sdk/sdk.go +208 -0
- package/resources/skill/SKILL.md +36 -87
- package/resources/skill/init/SKILL.md +9 -14
- package/resources/skill/manifest.json +6 -2
- package/resources/skill/references/aihub.md +28 -5
- package/resources/skill/references/app-api.md +56 -6
- package/resources/skill/references/architecture.md +2 -2
- package/resources/skill/references/chat-sdk.md +4 -2
- package/resources/skill/references/checkout.md +21 -7
- package/resources/skill/references/custom-services.md +124 -222
- package/resources/skill/references/data.md +22 -6
- package/resources/skill/references/delivery.md +33 -0
- package/resources/skill/references/diagnostics.md +51 -0
- package/resources/skill/references/frontend.md +93 -499
- package/resources/skill/references/mcp.md +65 -101
- package/resources/skill/references/methods.md +189 -0
- package/resources/skill/references/modules.md +36 -8
- package/resources/skill/references/runtime.md +26 -3
- package/resources/skill/story/SKILL.md +1 -2
- package/src/apiContractCache.js +112 -0
- package/src/cli.js +24 -20
- package/src/commandRegistry.js +15 -12
- package/src/commands/api.js +41 -10
- package/src/commands/apiKey.js +34 -0
- package/src/commands/capabilities.js +93 -0
- package/src/commands/check.js +1 -10
- package/src/commands/checkout.js +1 -1
- package/src/commands/commit.js +1 -1
- package/src/commands/components.js +550 -0
- package/src/commands/conflict.js +1 -1
- package/src/commands/connect.js +18 -8
- package/src/commands/customService.js +22 -8
- package/src/commands/dataRange.js +33 -0
- package/src/commands/delete.js +34 -46
- package/src/commands/deploy.js +1 -1
- package/src/commands/diff.js +18 -2
- package/src/commands/grant.js +29 -0
- package/src/commands/group.js +38 -0
- package/src/commands/help.js +80 -51
- package/src/commands/init.js +6 -12
- package/src/commands/listTargets.js +1 -1
- package/src/commands/local.js +2 -6
- package/src/commands/map.js +145 -28
- package/src/commands/mcp.js +2 -2
- package/src/commands/reconcile.js +1 -1
- package/src/commands/role.js +32 -0
- package/src/commands/space.js +41 -0
- package/src/commands/status.js +111 -8
- package/src/commands/uninstall.js +3 -3
- package/src/commands/update.js +24 -12
- package/src/commands/verify.js +118 -21
- package/src/commands/{verifyUi.js → visualVerify.js} +28 -116
- package/src/commands/worklog.js +90 -0
- package/src/consoleEncoding.js +34 -0
- package/src/contractCompatibility.js +57 -0
- package/src/customServices.js +278 -41
- package/src/diffReport.js +106 -0
- package/src/index.js +2 -0
- package/src/{localdev → localRuntime}/compose.js +14 -17
- package/src/{localdev → localRuntime}/detect.js +1 -1
- package/src/{localdev → localRuntime}/index.js +22 -23
- package/src/{localdev → localRuntime}/mysqlClient.js +1 -1
- package/src/{localdev → localRuntime}/services.js +28 -37
- package/src/mcp/client.js +11 -2
- package/src/mcp/protocol.js +2 -2
- package/src/mcp/tools.js +14 -1
- package/src/platforms.js +9 -0
- package/src/projectConfig.js +8 -4
- package/src/releaseInstall.js +105 -0
- package/src/{installers/index.js → targets.js} +3 -5
- package/src/updateCheck.js +48 -28
- package/src/worklog.js +275 -0
- package/src/workspaceHealth.js +1 -1
- package/src/worktree/backend.js +1 -1
- package/src/worktree/index.js +86 -51
- package/src/changelog.js +0 -276
- package/src/commands/changelog.js +0 -24
- package/src/commands/localDev.js +0 -9
- package/src/commands/sync.js +0 -46
- package/src/commands/task.js +0 -408
- package/src/commands/verifyUiCompat.js +0 -16
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const SCHEMA_VERSION = 1;
|
|
8
|
+
const WAIT = new Int32Array(new SharedArrayBuffer(4));
|
|
9
|
+
|
|
10
|
+
function cachePath(projectDir) {
|
|
11
|
+
return path.join(projectDir, '.draftgo', 'api-contract-cache.json');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function emptyCache(server) {
|
|
15
|
+
return { schema_version: SCHEMA_VERSION, server, operations: {} };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function readCache(projectDir, server) {
|
|
19
|
+
const file = cachePath(projectDir);
|
|
20
|
+
if (!fs.existsSync(file)) return emptyCache(server);
|
|
21
|
+
try {
|
|
22
|
+
const value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
23
|
+
if (!value || value.schema_version !== SCHEMA_VERSION || value.server !== server
|
|
24
|
+
|| !value.operations || Array.isArray(value.operations)) return emptyCache(server);
|
|
25
|
+
return value;
|
|
26
|
+
} catch {
|
|
27
|
+
return emptyCache(server);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function normalizeDescription(value) {
|
|
32
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
33
|
+
const operation = value.operation && typeof value.operation === 'object' ? value.operation : value;
|
|
34
|
+
const registryRevision = String(value.registry_revision || operation.registry_revision || '').trim();
|
|
35
|
+
const contractHash = String(value.contract_hash || operation.contract_hash || '').trim();
|
|
36
|
+
const operationId = String(operation.operation_id || '').trim();
|
|
37
|
+
if (!operationId || !registryRevision || !contractHash) return null;
|
|
38
|
+
const normalized = { ...operation };
|
|
39
|
+
delete normalized.registry_revision;
|
|
40
|
+
delete normalized.contract_hash;
|
|
41
|
+
return { operation: normalized, registry_revision: registryRevision, contract_hash: contractHash };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function cachedDescription(projectDir, server, operationId) {
|
|
45
|
+
const cached = readCache(projectDir, server).operations[String(operationId)];
|
|
46
|
+
return normalizeDescription(cached);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function acquire(file) {
|
|
50
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
51
|
+
const lock = `${file}.lock`;
|
|
52
|
+
const started = Date.now();
|
|
53
|
+
while (true) {
|
|
54
|
+
try {
|
|
55
|
+
const descriptor = fs.openSync(lock, 'wx', 0o600);
|
|
56
|
+
fs.closeSync(descriptor);
|
|
57
|
+
return lock;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (!error || error.code !== 'EEXIST') throw error;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
if (Date.now() - fs.statSync(lock).mtimeMs > 30_000) {
|
|
63
|
+
fs.rmSync(lock, { force: true });
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (!error || error.code !== 'ENOENT') throw error;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (Date.now() - started >= 2000) return null;
|
|
71
|
+
Atomics.wait(WAIT, 0, 0, 10);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function writeAtomic(file, value) {
|
|
76
|
+
const temporary = `${file}.tmp-${process.pid}-${crypto.randomBytes(8).toString('hex')}`;
|
|
77
|
+
try {
|
|
78
|
+
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
79
|
+
fs.renameSync(temporary, file);
|
|
80
|
+
} finally {
|
|
81
|
+
try { fs.rmSync(temporary, { force: true }); } catch { /* Best-effort cleanup. */ }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function storeDescription(projectDir, server, value) {
|
|
86
|
+
const normalized = normalizeDescription(value);
|
|
87
|
+
if (!normalized) return null;
|
|
88
|
+
const file = cachePath(projectDir);
|
|
89
|
+
const lock = acquire(file);
|
|
90
|
+
if (!lock) return normalized;
|
|
91
|
+
try {
|
|
92
|
+
const cache = readCache(projectDir, server);
|
|
93
|
+
cache.operations[normalized.operation.operation_id] = {
|
|
94
|
+
...normalized,
|
|
95
|
+
described_at: new Date().toISOString(),
|
|
96
|
+
};
|
|
97
|
+
cache.updated_at = new Date().toISOString();
|
|
98
|
+
writeAtomic(file, cache);
|
|
99
|
+
} finally {
|
|
100
|
+
try { fs.rmSync(lock, { force: true }); } catch { /* Cache writes are best effort. */ }
|
|
101
|
+
}
|
|
102
|
+
return normalized;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = {
|
|
106
|
+
SCHEMA_VERSION,
|
|
107
|
+
cachePath,
|
|
108
|
+
readCache,
|
|
109
|
+
normalizeDescription,
|
|
110
|
+
cachedDescription,
|
|
111
|
+
storeDescription,
|
|
112
|
+
};
|
package/src/cli.js
CHANGED
|
@@ -8,13 +8,12 @@ const VALUE_FLAGS = new Set([
|
|
|
8
8
|
'target',
|
|
9
9
|
'server',
|
|
10
10
|
'mcp-url',
|
|
11
|
+
'api-key',
|
|
11
12
|
'token',
|
|
12
13
|
'output',
|
|
13
14
|
'url',
|
|
14
15
|
'browser',
|
|
15
16
|
'browser-path',
|
|
16
|
-
'resource',
|
|
17
|
-
'mobile-check',
|
|
18
17
|
'ui',
|
|
19
18
|
'frame',
|
|
20
19
|
'viewport',
|
|
@@ -31,30 +30,35 @@ const VALUE_FLAGS = new Set([
|
|
|
31
30
|
'params',
|
|
32
31
|
'input',
|
|
33
32
|
'type',
|
|
33
|
+
'route',
|
|
34
34
|
'title',
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'id',
|
|
38
|
-
'owner',
|
|
39
|
-
'original',
|
|
40
|
-
'original-requirement',
|
|
41
|
-
'clarified',
|
|
42
|
-
'clarified-requirement',
|
|
43
|
-
'expected-effect',
|
|
44
|
-
'effect',
|
|
45
|
-
'scope',
|
|
46
|
-
'out-of-scope',
|
|
47
|
-
'content',
|
|
48
|
-
'acceptance',
|
|
49
|
-
'module-logic',
|
|
50
|
-
'user-journey',
|
|
51
|
-
'item',
|
|
35
|
+
'limit',
|
|
36
|
+
'cursor',
|
|
52
37
|
'date',
|
|
53
38
|
'handler',
|
|
39
|
+
'source',
|
|
54
40
|
'headers',
|
|
55
41
|
'user',
|
|
56
42
|
'side-effect-policy',
|
|
57
|
-
'
|
|
43
|
+
'page',
|
|
44
|
+
'instance',
|
|
45
|
+
'note',
|
|
46
|
+
'file',
|
|
47
|
+
'library',
|
|
48
|
+
'name',
|
|
49
|
+
'slug',
|
|
50
|
+
'category',
|
|
51
|
+
'description',
|
|
52
|
+
'tags',
|
|
53
|
+
'source-type',
|
|
54
|
+
'status',
|
|
55
|
+
'scope-type',
|
|
56
|
+
'space-id',
|
|
57
|
+
'module',
|
|
58
|
+
'resource-type',
|
|
59
|
+
'method',
|
|
60
|
+
'risk',
|
|
61
|
+
'permission',
|
|
58
62
|
]);
|
|
59
63
|
|
|
60
64
|
function parse(argv) {
|
package/src/commandRegistry.js
CHANGED
|
@@ -5,15 +5,13 @@
|
|
|
5
5
|
const commands = [
|
|
6
6
|
{ name: 'init', run: (dir, args, flags) => require('./commands/init')(dir, args, flags) },
|
|
7
7
|
{ name: 'update', run: (dir, args, flags) => require('./commands/update')(dir, args, flags) },
|
|
8
|
-
{ name: 'uninstall',
|
|
9
|
-
{ name: 'status', run: (dir) => require('./commands/status')(dir) },
|
|
10
|
-
{ name: '
|
|
11
|
-
{ name: 'task', run: (dir, args, flags) => require('./commands/task')(dir, args, flags) },
|
|
8
|
+
{ name: 'uninstall', run: (dir, args, flags) => require('./commands/uninstall')(dir, args, flags) },
|
|
9
|
+
{ name: 'status', run: (dir, args, flags) => require('./commands/status')(dir, args, flags) },
|
|
10
|
+
{ name: 'work', run: (dir, args, flags) => require('./commands/worklog')(dir, args, flags) },
|
|
12
11
|
{ name: 'clean', run: (dir, args, flags) => require('./commands/clean')(dir, args, flags) },
|
|
13
12
|
{ name: 'map', run: (dir, _args, flags) => require('./commands/map')(dir, flags) },
|
|
14
13
|
{ name: 'check', run: (dir, args, flags) => require('./commands/check')(dir, args, flags) },
|
|
15
14
|
{ name: 'verify', run: (dir, args, flags) => require('./commands/verify')(dir, args, flags) },
|
|
16
|
-
{ name: 'verify-ui', aliases: ['verifyui'], run: (dir, args, flags) => require('./commands/verifyUiCompat')(dir, args, flags) },
|
|
17
15
|
{ name: 'mcp', run: (dir, args, flags) => require('./commands/mcp')(dir, args, flags) },
|
|
18
16
|
{ name: 'checkout', run: (dir, args, flags) => require('./commands/checkout')(dir, args, flags) },
|
|
19
17
|
{ name: 'refresh', run: (dir, args, flags) => require('./commands/checkout')(dir, args, flags) },
|
|
@@ -26,15 +24,20 @@ const commands = [
|
|
|
26
24
|
{ name: 'conflicts', run: (dir, args, flags) => require('./commands/conflicts')(dir, args, flags) },
|
|
27
25
|
{ name: 'conflict', run: (dir, args, flags) => require('./commands/conflict')(dir, args, flags) },
|
|
28
26
|
{ name: 'api', run: (dir, args, flags) => require('./commands/api')(dir, args, flags) },
|
|
29
|
-
{ name: '
|
|
27
|
+
{ name: 'space', run: (dir, args, flags) => require('./commands/space')(dir, args, flags) },
|
|
28
|
+
{ name: 'group', run: (dir, args, flags) => require('./commands/group')(dir, args, flags) },
|
|
29
|
+
{ name: 'grant', run: (dir, args, flags) => require('./commands/grant')(dir, args, flags) },
|
|
30
|
+
{ name: 'api-key', run: (dir, args, flags) => require('./commands/apiKey')(dir, args, flags) },
|
|
31
|
+
{ name: 'role', run: (dir, args, flags) => require('./commands/role')(dir, args, flags) },
|
|
32
|
+
{ name: 'data-range', run: (dir, args, flags) => require('./commands/dataRange')(dir, args, flags) },
|
|
33
|
+
{ name: 'delete', run: (dir, args, flags) => require('./commands/delete')(dir, args, flags) },
|
|
30
34
|
{ name: 'deploy', run: (dir, args, flags) => require('./commands/deploy')(dir, args, flags) },
|
|
31
|
-
{ name: 'auto-push',
|
|
32
|
-
{ name: 'pull', run: (dir, args, flags) => require('./commands/sync')(dir, 'pull', args, flags) },
|
|
33
|
-
{ name: 'push', run: (dir, args, flags) => require('./commands/sync')(dir, 'push', args, flags) },
|
|
35
|
+
{ name: 'auto-push', run: (dir, args, flags) => require('./commands/autoPush')(dir, args, flags) },
|
|
34
36
|
{ name: 'local', run: (dir, args, flags) => require('./commands/local')(dir, args, flags) },
|
|
35
|
-
{ name: 'list-targets',
|
|
36
|
-
{ name: '
|
|
37
|
-
{ name: '
|
|
37
|
+
{ name: 'list-targets', run: () => require('./commands/listTargets')() },
|
|
38
|
+
{ name: 'connect', run: (dir, args, flags) => require('./commands/connect')(dir, args, flags) },
|
|
39
|
+
{ name: 'components', run: (dir, args, flags) => require('./commands/components')(dir, args, flags) },
|
|
40
|
+
{ name: 'capabilities', run: (dir, args, flags) => require('./commands/capabilities')(dir, args, flags) },
|
|
38
41
|
];
|
|
39
42
|
|
|
40
43
|
const aliases = new Map();
|
package/src/commands/api.js
CHANGED
|
@@ -5,7 +5,15 @@ const path = require('path');
|
|
|
5
5
|
const log = require('../logger');
|
|
6
6
|
const { confirm } = require('../prompt');
|
|
7
7
|
const { loadProjectConfig } = require('../projectConfig');
|
|
8
|
-
const { TOOL_NAMES, openToolSession, callStructured } = require('../mcp/tools');
|
|
8
|
+
const { TOOL_NAMES, openToolSession, callStructured, configuredScope } = require('../mcp/tools');
|
|
9
|
+
const { cachedDescription, normalizeDescription, storeDescription } = require('../apiContractCache');
|
|
10
|
+
const {
|
|
11
|
+
isContractCompatibilityError,
|
|
12
|
+
isHardContractIncompatibility,
|
|
13
|
+
isDangerousOperation,
|
|
14
|
+
sameOperationContract,
|
|
15
|
+
incompatibleOperationError,
|
|
16
|
+
} = require('../contractCompatibility');
|
|
9
17
|
|
|
10
18
|
function readCallInput(projectDir, flags) {
|
|
11
19
|
const raw = flags.input
|
|
@@ -66,27 +74,49 @@ async function callOperation(projectDir, operationId, flags = {}) {
|
|
|
66
74
|
let session;
|
|
67
75
|
let description;
|
|
68
76
|
let input;
|
|
77
|
+
let config;
|
|
69
78
|
try {
|
|
70
|
-
|
|
79
|
+
config = loadProjectConfig(projectDir);
|
|
71
80
|
session = await openToolSession(config, [TOOL_NAMES.apiDescribe, TOOL_NAMES.apiCall]);
|
|
72
|
-
description =
|
|
81
|
+
description = cachedDescription(projectDir, config.server, operationId);
|
|
82
|
+
if (!description) {
|
|
83
|
+
const described = await callStructured(session, TOOL_NAMES.apiDescribe, { operation_id: operationId });
|
|
84
|
+
description = storeDescription(projectDir, config.server, described) || normalizeDescription(described);
|
|
85
|
+
}
|
|
86
|
+
if (!description) throw new Error('DraftGo API description is missing contract revision metadata.');
|
|
73
87
|
input = readCallInput(projectDir, flags);
|
|
74
88
|
} catch (error) {
|
|
75
89
|
return printCallError(operationId, error, flags);
|
|
76
90
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
if (destructive && !flags.yes && !flags.y) {
|
|
91
|
+
const dangerous = isDangerousOperation(description);
|
|
92
|
+
if (dangerous && !flags.yes && !flags.y) {
|
|
80
93
|
const approved = await confirm(`Call destructive DraftGo operation ${operationId}?`, { default: false });
|
|
81
94
|
if (!approved) return 0;
|
|
82
95
|
}
|
|
83
96
|
let result;
|
|
84
97
|
try {
|
|
85
|
-
|
|
86
|
-
...input,
|
|
87
|
-
|
|
88
|
-
...(
|
|
98
|
+
const invoke = (contract, confirmed) => callStructured(session, TOOL_NAMES.apiCall, {
|
|
99
|
+
...input, operation_id: operationId, registry_revision: contract.registry_revision,
|
|
100
|
+
...configuredScope(config, contract),
|
|
101
|
+
...(confirmed ? { confirm: true } : {}),
|
|
89
102
|
});
|
|
103
|
+
try {
|
|
104
|
+
result = await invoke(description, dangerous);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (!isContractCompatibilityError(error)) throw error;
|
|
107
|
+
if (isHardContractIncompatibility(error)) {
|
|
108
|
+
throw incompatibleOperationError(operationId, error);
|
|
109
|
+
}
|
|
110
|
+
const previousDescription = description;
|
|
111
|
+
const described = await callStructured(session, TOOL_NAMES.apiDescribe, { operation_id: operationId });
|
|
112
|
+
description = storeDescription(projectDir, config.server, described) || normalizeDescription(described);
|
|
113
|
+
if (!description) throw new Error('DraftGo API description is missing contract revision metadata.');
|
|
114
|
+
const changedDangerous = isDangerousOperation(description);
|
|
115
|
+
if ((dangerous || changedDangerous) && !sameOperationContract(previousDescription, description)) {
|
|
116
|
+
throw incompatibleOperationError(operationId, error);
|
|
117
|
+
}
|
|
118
|
+
result = await invoke(description, changedDangerous);
|
|
119
|
+
}
|
|
90
120
|
} catch (error) {
|
|
91
121
|
return printCallError(operationId, error, flags);
|
|
92
122
|
}
|
|
@@ -130,6 +160,7 @@ async function apiCommand(projectDir, positional, flags = {}) {
|
|
|
130
160
|
const result = await callStructured(session, expected, describe
|
|
131
161
|
? { operation_id: query }
|
|
132
162
|
: { query });
|
|
163
|
+
if (describe) storeDescription(projectDir, config.server, result);
|
|
133
164
|
if (flags.output === 'json') console.log(JSON.stringify(result, null, 2));
|
|
134
165
|
else printResult(describe ? `DraftGo API ${query}` : `DraftGo API search: ${query}`, result);
|
|
135
166
|
return 0;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const log = require('../logger');
|
|
4
|
+
const { callOperation } = require('./api');
|
|
5
|
+
|
|
6
|
+
const OPERATIONS = Object.freeze({
|
|
7
|
+
status: 'getCurrentUserAPIKey',
|
|
8
|
+
create: 'createCurrentUserAPIKey',
|
|
9
|
+
update: 'updateCurrentUserAPIKey',
|
|
10
|
+
delete: 'deleteCurrentUserAPIKey',
|
|
11
|
+
rotate: 'rotateCurrentUserAPIKey',
|
|
12
|
+
reset: 'resetCurrentUserAPIKey',
|
|
13
|
+
enable: 'enableCurrentUserAPIKey',
|
|
14
|
+
disable: 'disableCurrentUserAPIKey',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function operationKey(value) {
|
|
18
|
+
const action = String(value || 'status').trim().toLowerCase();
|
|
19
|
+
return OPERATIONS[action] ? action : '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function apiKeyCommand(projectDir, positional, flags = {}) {
|
|
23
|
+
const action = operationKey(positional[0]);
|
|
24
|
+
if (!action) {
|
|
25
|
+
log.err('Usage: draftgo api-key status|create|update|delete|rotate|reset|enable|disable [--input <json-file>]');
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
if (action === 'reset') log.warn('api-key reset is deprecated; prefer rotate.');
|
|
29
|
+
return callOperation(projectDir, OPERATIONS[action], flags);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = apiKeyCommand;
|
|
33
|
+
module.exports.OPERATIONS = OPERATIONS;
|
|
34
|
+
module.exports.operationKey = operationKey;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const log = require('../logger');
|
|
4
|
+
const { loadProjectConfig } = require('../projectConfig');
|
|
5
|
+
const { TOOL_NAMES, openToolSession, callStructured } = require('../mcp/tools');
|
|
6
|
+
|
|
7
|
+
function itemsOf(value) {
|
|
8
|
+
if (Array.isArray(value)) return value;
|
|
9
|
+
return value && Array.isArray(value.items) ? value.items : [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function nextCursorOf(value) {
|
|
13
|
+
return value && (value.next_cursor ?? value.nextCursor) || null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function print(value, flags, title) {
|
|
17
|
+
if (flags.output === 'json') console.log(JSON.stringify(value, null, 2));
|
|
18
|
+
else {
|
|
19
|
+
log.title(title);
|
|
20
|
+
if (Array.isArray(value)) value.forEach(item => log.plain(` ${item.operation_id || item.name || JSON.stringify(item)}`));
|
|
21
|
+
else console.log(JSON.stringify(value, null, 2));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function searchArguments(query, flags, cursor) {
|
|
26
|
+
const input = { limit: Math.min(100, Math.max(1, Number(flags.limit) || 100)) };
|
|
27
|
+
if (query) input.query = query;
|
|
28
|
+
if (flags.module) input.module = String(flags.module);
|
|
29
|
+
if (flags['resource-type'] || flags.type) input.resource_type = String(flags['resource-type'] || flags.type);
|
|
30
|
+
if (flags.method) input.method = String(flags.method).toUpperCase();
|
|
31
|
+
if (flags.risk) input.risk = String(flags.risk);
|
|
32
|
+
if (flags.permission) input.permission = String(flags.permission);
|
|
33
|
+
if (cursor) input.cursor = cursor;
|
|
34
|
+
return input;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function collect(session, query, flags, allPages) {
|
|
38
|
+
const result = [];
|
|
39
|
+
let cursor = flags.cursor || null;
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
do {
|
|
42
|
+
const page = await callStructured(session, TOOL_NAMES.apiSearch, searchArguments(query, flags, cursor));
|
|
43
|
+
result.push(...itemsOf(page));
|
|
44
|
+
cursor = nextCursorOf(page);
|
|
45
|
+
if (!allPages || !cursor) break;
|
|
46
|
+
if (seen.has(cursor)) throw new Error('DraftGo capability search repeated a cursor.');
|
|
47
|
+
seen.add(cursor);
|
|
48
|
+
} while (true);
|
|
49
|
+
return { items: result, next_cursor: cursor };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function capabilities(projectDir, positional = [], flags = {}) {
|
|
53
|
+
const action = String(positional[0] || 'list').toLowerCase();
|
|
54
|
+
const config = loadProjectConfig(projectDir);
|
|
55
|
+
if (action === 'show') {
|
|
56
|
+
const operationID = String(positional[1] || '').trim();
|
|
57
|
+
if (!operationID) throw new Error('Usage: draftgo capabilities show <operation_id>');
|
|
58
|
+
const session = await openToolSession(config, [TOOL_NAMES.apiDescribe]);
|
|
59
|
+
const result = await callStructured(session, TOOL_NAMES.apiDescribe, { operation_id: operationID });
|
|
60
|
+
print(result, flags, `DraftGo capability ${operationID}`);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
if (action === 'audit') {
|
|
64
|
+
const session = await openToolSession(config, [TOOL_NAMES.projectOverview, TOOL_NAMES.apiSearch]);
|
|
65
|
+
const overview = await callStructured(session, TOOL_NAMES.projectOverview, {});
|
|
66
|
+
const catalog = await collect(session, '', flags, true);
|
|
67
|
+
const registry = overview && (overview.operation_registry || overview.registry) || {};
|
|
68
|
+
const result = {
|
|
69
|
+
server: config.server,
|
|
70
|
+
registry_revision: registry.registry_revision || overview.registry_revision || null,
|
|
71
|
+
mapped_operations: registry.mapped_operations ?? registry.mapped ?? catalog.items.length,
|
|
72
|
+
excluded_operations: registry.excluded_operations ?? registry.excluded ?? null,
|
|
73
|
+
total_operations: registry.total_operations ?? registry.total ?? null,
|
|
74
|
+
discovered_operations: catalog.items.length,
|
|
75
|
+
complete: (registry.mapped_operations ?? registry.mapped ?? catalog.items.length) === catalog.items.length,
|
|
76
|
+
};
|
|
77
|
+
print(result, flags, 'DraftGo capability audit');
|
|
78
|
+
return result.complete ? 0 : 1;
|
|
79
|
+
}
|
|
80
|
+
if (action !== 'list' && action !== 'search') throw new Error('Usage: draftgo capabilities list|search|show|audit');
|
|
81
|
+
const query = action === 'search' ? positional.slice(1).join(' ').trim() : '';
|
|
82
|
+
if (action === 'search' && !query && !flags.module && !flags.type && !flags.method && !flags.risk && !flags.permission) {
|
|
83
|
+
throw new Error('Capability search needs a query or filter.');
|
|
84
|
+
}
|
|
85
|
+
const session = await openToolSession(config, [TOOL_NAMES.apiSearch]);
|
|
86
|
+
const result = await collect(session, query, flags, action === 'list' && !flags.cursor);
|
|
87
|
+
print(flags.output === 'json' ? result : result.items, flags, action === 'search' ? `DraftGo capabilities: ${query || 'filtered'}` : 'DraftGo capabilities');
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = capabilities;
|
|
92
|
+
module.exports.searchArguments = searchArguments;
|
|
93
|
+
module.exports.itemsOf = itemsOf;
|
package/src/commands/check.js
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
3
|
const log = require('../logger');
|
|
6
4
|
const { analyzeProject } = require('../projectMap');
|
|
7
5
|
const { inspectRemoteCheckouts } = require('../worktree/status');
|
|
8
6
|
const customServices = require('../customServices');
|
|
9
7
|
const { workspaceHealth } = require('../workspaceHealth');
|
|
10
8
|
|
|
11
|
-
function legacyCacheDetected(projectDir) {
|
|
12
|
-
return ['pages', 'navigations', 'docs', 'db_meta', 'custom_scripts', 'aihub', 'roles', 'users', 'system_config']
|
|
13
|
-
.some((name) => fs.existsSync(path.join(projectDir, '.draftgo', name, 'index.json')));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
9
|
async function check(projectDir, positional = [], flags = {}) {
|
|
17
10
|
if (!Array.isArray(positional)) {
|
|
18
11
|
flags = positional || {};
|
|
@@ -94,7 +87,7 @@ async function check(projectDir, positional = [], flags = {}) {
|
|
|
94
87
|
workspace_hygiene: hygiene,
|
|
95
88
|
};
|
|
96
89
|
if (flags.output === 'json') {
|
|
97
|
-
console.log(JSON.stringify(
|
|
90
|
+
console.log(JSON.stringify(output, null, 2));
|
|
98
91
|
return result.errors.length || (flags.strict && result.warnings.length) ? 1 : 0;
|
|
99
92
|
}
|
|
100
93
|
|
|
@@ -111,8 +104,6 @@ async function check(projectDir, positional = [], flags = {}) {
|
|
|
111
104
|
if (remote) log.info(`Remote checkout comparison: ${remote.length} resource(s)`);
|
|
112
105
|
for (const message of result.errors) log.err(message);
|
|
113
106
|
for (const detail of result.warningDetails) log.warn(`[${detail.code}/${detail.confidence}] ${detail.message}`);
|
|
114
|
-
if (legacyCacheDetected(projectDir)) log.dim(' Legacy .draftgo index caches were detected and ignored.');
|
|
115
|
-
|
|
116
107
|
if (result.errors.length || (flags.strict && result.warnings.length)) return 1;
|
|
117
108
|
log.ok(result.warnings.length ? 'Local checkout validation completed with warnings.' : 'Local checkout validation passed.');
|
|
118
109
|
return 0;
|
package/src/commands/checkout.js
CHANGED
|
@@ -6,7 +6,7 @@ const { checkoutResources } = require('../worktree');
|
|
|
6
6
|
async function checkout(projectDir, positional, flags = {}) {
|
|
7
7
|
const [resourceType, ...ids] = positional;
|
|
8
8
|
if (!resourceType || !ids.length) {
|
|
9
|
-
log.err('Usage: draftgo checkout <pages|nav|docs> <id...>');
|
|
9
|
+
log.err('Usage: draftgo checkout <pages|nav|docs|custom-services> <id...>');
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
12
12
|
if (require('./customService').isServiceType(resourceType)) {
|
package/src/commands/commit.js
CHANGED
|
@@ -6,7 +6,7 @@ const { commitResources } = require('../worktree');
|
|
|
6
6
|
async function commit(projectDir, positional, flags = {}) {
|
|
7
7
|
const [resourceType, ...ids] = positional;
|
|
8
8
|
if (!resourceType || !ids.length) {
|
|
9
|
-
log.err('Usage: draftgo commit <pages|nav|docs> <id...>');
|
|
9
|
+
log.err('Usage: draftgo commit <pages|nav|docs|custom-services> <id...>');
|
|
10
10
|
return 1;
|
|
11
11
|
}
|
|
12
12
|
if (require('./customService').isServiceType(resourceType)) {
|