draftgo-cli 4.0.23 → 4.0.25
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 +3 -1
- package/bin/draftgo.js +8 -8
- package/package.json +72 -72
- package/resources/custom-service-sdk/auth_test.go +1 -1
- package/resources/custom-service-sdk/manifest.json +11 -11
- package/resources/custom-service-sdk/platform.go +10 -3
- 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 +4 -3
- 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 +4 -4
- package/resources/skill/references/data.md +168 -168
- package/resources/skill/references/methods.md +3 -0
- package/resources/skill/references/modules.md +47 -47
- package/resources/skill/references/runtime.md +95 -96
- package/resources/skill/story/SKILL.md +264 -264
- package/src/commands/help.js +72 -71
- 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 +24 -30
- 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 +167 -167
- package/src/prompt.js +94 -94
- package/src/updateCheck.js +16 -16
package/README.md
CHANGED
|
@@ -75,6 +75,8 @@ draftgo connect [target...]
|
|
|
75
75
|
|
|
76
76
|
`draftgo status` 会通过只读 MCP/API 诊断显示服务连接健康状态、服务版本和当前用户 API Key 的 `platform`/`space` 上下文;`--output json` 适合读取 `connection.health`、`workspace_id` 与 `space_id`。
|
|
77
77
|
|
|
78
|
+
`draftgo update` 和 `draftgo update all` 只刷新项目中已经安装的 DraftGo Skill,不会因为检测到 `.cursor`、`AGENTS.md` 等工具痕迹而新增配置。显式指定 target 时(例如 `draftgo update cursor`),如果该 Skill 尚未安装,会按你的明确请求直接安装并更新它;首次接入也可以使用 `draftgo init <target>`。
|
|
79
|
+
|
|
78
80
|
### 发现与正文
|
|
79
81
|
|
|
80
82
|
```text
|
|
@@ -172,7 +174,7 @@ draftgo data-range list
|
|
|
172
174
|
`role` 管理无作用域的 Role 模板;授权使用 `grant` 创建带 `platform` 或 `space` 范围的 AccessGrant。工作区成员关系不直接授予权限。资源归属由服务端持久化 ResourceOwnership 决定,DB 的 DataRange
|
|
173
175
|
只在该范围内应用 `none`、`owner` 或 `all` 记录策略。交互式 CLI、MCP 和普通 HTTP API 均使用当前用户的
|
|
174
176
|
API Key,并始终按该用户的 AccessGrant 授权;API Key 不能创建或伪造服务身份。CI、定时任务和共享服务等
|
|
175
|
-
|
|
177
|
+
无人值守自动化由运行时使用 system 身份;定时和事件处理器没有调用者,必须在代码中显式使用 `ctx.Admin.*`,不创建服务主体 AccessGrant,也不复用个人 API Key。
|
|
176
178
|
|
|
177
179
|
### 交付与记录
|
|
178
180
|
|
package/bin/draftgo.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
Promise.resolve(require('../src/index.js').run(process.argv.slice(2)))
|
|
5
|
-
.then((code) => { process.exitCode = Number(code) || 0; })
|
|
6
|
-
.catch((err) => {
|
|
7
|
-
console.error(err && err.stack ? err.stack : String(err));
|
|
8
|
-
process.exitCode = 1;
|
|
9
|
-
});
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Promise.resolve(require('../src/index.js').run(process.argv.slice(2)))
|
|
5
|
+
.then((code) => { process.exitCode = Number(code) || 0; })
|
|
6
|
+
.catch((err) => {
|
|
7
|
+
console.error(err && err.stack ? err.stack : String(err));
|
|
8
|
+
process.exitCode = 1;
|
|
9
|
+
});
|
package/package.json
CHANGED
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "draftgo-cli",
|
|
3
|
-
"version": "4.0.
|
|
4
|
-
"description": "Install and manage the DraftGo skill across AI coding agents (Claude Code, Codex, Cursor, Windsurf, Antigravity, Copilot, Gemini, Kiro, Pi).",
|
|
5
|
-
"bin": {
|
|
6
|
-
"draftgo": "bin/draftgo.js"
|
|
7
|
-
},
|
|
8
|
-
"main": "src/index.js",
|
|
9
|
-
"engines": {
|
|
10
|
-
"node": ">=20.19"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"bin/",
|
|
14
|
-
"src/",
|
|
15
|
-
"resources/",
|
|
16
|
-
"!resources/**/__pycache__/**",
|
|
17
|
-
"!resources/**/*.py[cod]",
|
|
18
|
-
"LICENSE",
|
|
19
|
-
"README.md"
|
|
20
|
-
],
|
|
21
|
-
"keywords": [
|
|
22
|
-
"draftgo",
|
|
23
|
-
"skill",
|
|
24
|
-
"cli",
|
|
25
|
-
"claude-code",
|
|
26
|
-
"codex",
|
|
27
|
-
"cursor",
|
|
28
|
-
"windsurf",
|
|
29
|
-
"antigravity",
|
|
30
|
-
"copilot",
|
|
31
|
-
"gemini",
|
|
32
|
-
"kiro",
|
|
33
|
-
"pi"
|
|
34
|
-
],
|
|
35
|
-
"author": "draftgo <cabinai@163.com> (https://github.com/draftgo)",
|
|
36
|
-
"license": "MIT",
|
|
37
|
-
"homepage": "https://github.com/draftgo/draftgo-cli#readme",
|
|
38
|
-
"bugs": {
|
|
39
|
-
"url": "https://github.com/draftgo/draftgo-cli/issues"
|
|
40
|
-
},
|
|
41
|
-
"repository": {
|
|
42
|
-
"type": "git",
|
|
43
|
-
"url": "git+https://github.com/draftgo/draftgo-cli.git"
|
|
44
|
-
},
|
|
45
|
-
"scripts": {
|
|
46
|
-
"lint": "node scripts/check-syntax.js",
|
|
47
|
-
"validate:skill": "node scripts/validate-skill.js",
|
|
48
|
-
"sync:custom-service-sdk": "node scripts/sync-custom-service-sdk.js",
|
|
49
|
-
"build:release": "node scripts/build-release.js",
|
|
50
|
-
"verify:package": "node scripts/verify-package.js",
|
|
51
|
-
"test": "npm run lint && npm run validate:skill && npm run verify:package && npm run test:unit && npm run test:components && npm run test:capabilities && npm run test:workflow && npm run test:worklog && npm run test:mcp && npm run test:worktree && npm run test:integration && npm run test:local && npm run test:e2e",
|
|
52
|
-
"test:unit": "node tests/unit.js",
|
|
53
|
-
"test:components": "node --test tests/components.test.js",
|
|
54
|
-
"test:capabilities": "node --test tests/capabilities.test.js",
|
|
55
|
-
"test:workflow": "node tests/workflow.test.js",
|
|
56
|
-
"test:worklog": "node tests/worklog.test.js",
|
|
57
|
-
"test:mcp": "node tests/mcp.test.js",
|
|
58
|
-
"test:worktree": "node tests/worktree.test.js",
|
|
59
|
-
"test:integration": "node tests/integration.test.js",
|
|
60
|
-
"test:local": "node tests/local-runtime.js",
|
|
61
|
-
"test:e2e": "node tests/e2e.js"
|
|
62
|
-
},
|
|
63
|
-
"dependencies": {
|
|
64
|
-
"@grpc/grpc-js": "1.14.1",
|
|
65
|
-
"adm-zip": "0.5.16",
|
|
66
|
-
"parse5": "6.0.1",
|
|
67
|
-
"playwright-core": "1.61.1"
|
|
68
|
-
},
|
|
69
|
-
"directories": {
|
|
70
|
-
"test": "tests"
|
|
71
|
-
}
|
|
72
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "draftgo-cli",
|
|
3
|
+
"version": "4.0.25",
|
|
4
|
+
"description": "Install and manage the DraftGo skill across AI coding agents (Claude Code, Codex, Cursor, Windsurf, Antigravity, Copilot, Gemini, Kiro, Pi).",
|
|
5
|
+
"bin": {
|
|
6
|
+
"draftgo": "bin/draftgo.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "src/index.js",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20.19"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin/",
|
|
14
|
+
"src/",
|
|
15
|
+
"resources/",
|
|
16
|
+
"!resources/**/__pycache__/**",
|
|
17
|
+
"!resources/**/*.py[cod]",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"draftgo",
|
|
23
|
+
"skill",
|
|
24
|
+
"cli",
|
|
25
|
+
"claude-code",
|
|
26
|
+
"codex",
|
|
27
|
+
"cursor",
|
|
28
|
+
"windsurf",
|
|
29
|
+
"antigravity",
|
|
30
|
+
"copilot",
|
|
31
|
+
"gemini",
|
|
32
|
+
"kiro",
|
|
33
|
+
"pi"
|
|
34
|
+
],
|
|
35
|
+
"author": "draftgo <cabinai@163.com> (https://github.com/draftgo)",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"homepage": "https://github.com/draftgo/draftgo-cli#readme",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/draftgo/draftgo-cli/issues"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/draftgo/draftgo-cli.git"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"lint": "node scripts/check-syntax.js",
|
|
47
|
+
"validate:skill": "node scripts/validate-skill.js",
|
|
48
|
+
"sync:custom-service-sdk": "node scripts/sync-custom-service-sdk.js",
|
|
49
|
+
"build:release": "node scripts/build-release.js",
|
|
50
|
+
"verify:package": "node scripts/verify-package.js",
|
|
51
|
+
"test": "npm run lint && npm run validate:skill && npm run verify:package && npm run test:unit && npm run test:components && npm run test:capabilities && npm run test:workflow && npm run test:worklog && npm run test:mcp && npm run test:worktree && npm run test:integration && npm run test:local && npm run test:e2e",
|
|
52
|
+
"test:unit": "node tests/unit.js",
|
|
53
|
+
"test:components": "node --test tests/components.test.js",
|
|
54
|
+
"test:capabilities": "node --test tests/capabilities.test.js",
|
|
55
|
+
"test:workflow": "node tests/workflow.test.js",
|
|
56
|
+
"test:worklog": "node tests/worklog.test.js",
|
|
57
|
+
"test:mcp": "node tests/mcp.test.js",
|
|
58
|
+
"test:worktree": "node tests/worktree.test.js",
|
|
59
|
+
"test:integration": "node tests/integration.test.js",
|
|
60
|
+
"test:local": "node tests/local-runtime.js",
|
|
61
|
+
"test:e2e": "node tests/e2e.js"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@grpc/grpc-js": "1.14.1",
|
|
65
|
+
"adm-zip": "0.5.16",
|
|
66
|
+
"parse5": "6.0.1",
|
|
67
|
+
"playwright-core": "1.61.1"
|
|
68
|
+
},
|
|
69
|
+
"directories": {
|
|
70
|
+
"test": "tests"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -38,7 +38,7 @@ func TestAuthAssertionsUseHostAuthorization(t *testing.T) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
client.err = errors.New("denied by host")
|
|
41
|
-
if err := draftgo.Auth.RequireAdmin(); err == nil || err.Error() != "denied by host" {
|
|
41
|
+
if err := draftgo.Auth.RequireAdmin(); err == nil || err.Error() != "require_admin failed: denied by host" {
|
|
42
42
|
t.Fatalf("host denial was not returned: %v", err)
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"module": "draftgo/sdk",
|
|
4
|
-
"fingerprint": "
|
|
4
|
+
"fingerprint": "4d0fc5d9b9dd8e0f3332fc1f61c544579d1d0bd1c96b39bc1a25006bb172bcc3",
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": "ai.go",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"path": "auth_test.go",
|
|
18
|
-
"sha256": "
|
|
19
|
-
"bytes":
|
|
18
|
+
"sha256": "2905126322a678df82289ebf13c15f6595fb8def2f98cbd5bb53c406bf5ac31c",
|
|
19
|
+
"bytes": 1626
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"path": "billing.go",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"path": "platform.go",
|
|
38
|
-
"sha256": "
|
|
39
|
-
"bytes":
|
|
38
|
+
"sha256": "cc696f69c3a8efe093968ea4f6bfea1f257944ebcd83591b5ffd75ae85a8b583",
|
|
39
|
+
"bytes": 12182
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
"path": "platform_logger_test.go",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
"path": "resources.go",
|
|
53
|
-
"sha256": "
|
|
54
|
-
"bytes":
|
|
53
|
+
"sha256": "78f465a598ace6dc2858fea4a448488d195a5d1a8dd3452465d73ce9727b6a72",
|
|
54
|
+
"bytes": 9761
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
"path": "resources_billing_test.go",
|
|
@@ -65,13 +65,13 @@
|
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"path": "resources_scope_test.go",
|
|
68
|
-
"sha256": "
|
|
69
|
-
"bytes":
|
|
68
|
+
"sha256": "f9b19b592c0b2ddc0564715828fd840c974216f8e99e9690af43f2cf4a0d7fb5",
|
|
69
|
+
"bytes": 3870
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
"path": "sdk.go",
|
|
73
|
-
"sha256": "
|
|
74
|
-
"bytes":
|
|
73
|
+
"sha256": "18872d1f8535cdba53ddb3f487792cf74f10035f22a3023ec5cff3c3e597a8c0",
|
|
74
|
+
"bytes": 8342
|
|
75
75
|
}
|
|
76
76
|
]
|
|
77
77
|
}
|
|
@@ -329,11 +329,11 @@ type authClient struct {
|
|
|
329
329
|
func (a authClient) CurrentUser() map[string]any { return a.user }
|
|
330
330
|
func (a authClient) RequireLogin() error {
|
|
331
331
|
_, err := a.call(a.context, "auth.require_login", map[string]any{})
|
|
332
|
-
return err
|
|
332
|
+
return authAssertionError("require_login", err)
|
|
333
333
|
}
|
|
334
334
|
func (a authClient) RequireAdmin() error {
|
|
335
335
|
_, err := a.call(a.context, "auth.require_admin", map[string]any{})
|
|
336
|
-
return err
|
|
336
|
+
return authAssertionError("require_admin", err)
|
|
337
337
|
}
|
|
338
338
|
func (a authClient) RequireRole(role string) error {
|
|
339
339
|
role = strings.TrimSpace(role)
|
|
@@ -341,5 +341,12 @@ func (a authClient) RequireRole(role string) error {
|
|
|
341
341
|
return errors.New("require_role failed: role is required")
|
|
342
342
|
}
|
|
343
343
|
_, err := a.call(a.context, "auth.require_role", map[string]any{"role": role})
|
|
344
|
-
return err
|
|
344
|
+
return authAssertionError("require_role", err)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
func authAssertionError(assertion string, err error) error {
|
|
348
|
+
if err == nil {
|
|
349
|
+
return nil
|
|
350
|
+
}
|
|
351
|
+
return fmt.Errorf("%s failed: %w", assertion, err)
|
|
345
352
|
}
|
|
@@ -94,6 +94,7 @@ type ResourceContext struct {
|
|
|
94
94
|
ScopeType string `json:"scope_type"`
|
|
95
95
|
WorkspaceID int64 `json:"workspace_id,omitempty"`
|
|
96
96
|
SpaceID int64 `json:"space_id,omitempty"`
|
|
97
|
+
OwnerUserID int64 `json:"owner_user_id,omitempty"`
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
// ResourceOwnership identifies the persisted authorization boundary of a
|
|
@@ -16,7 +16,7 @@ func (client *scopeRecordingClient) Call(_ context.Context, operation string, ar
|
|
|
16
16
|
case "resource.context.current":
|
|
17
17
|
return map[string]any{"scope_type": ScopeSpace, "workspace_id": float64(2), "space_id": float64(8)}, nil
|
|
18
18
|
case "scope.principal.current":
|
|
19
|
-
return map[string]any{"subject_type": "
|
|
19
|
+
return map[string]any{"subject_type": "system", "subject_id": "0", "username": "System", "is_admin": true}, nil
|
|
20
20
|
default:
|
|
21
21
|
return map[string]any{"ok": true}, nil
|
|
22
22
|
}
|
|
@@ -38,7 +38,7 @@ func TestScopeClientUsesResourceAndPermissionOperations(t *testing.T) {
|
|
|
38
38
|
if err != nil || recorder.operation != "scope.principal.current" {
|
|
39
39
|
t.Fatalf("principal operation=%q err=%v", recorder.operation, err)
|
|
40
40
|
}
|
|
41
|
-
if principal.SubjectType != "
|
|
41
|
+
if principal.SubjectType != "system" || principal.SubjectID != "0" || principal.Username != "System" || !principal.IsAdmin {
|
|
42
42
|
t.Fatalf("principal=%#v", principal)
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -75,13 +75,18 @@ func TestContextCarriesPersistedResourceAndPrincipal(t *testing.T) {
|
|
|
75
75
|
recorder := &scopeRecordingClient{}
|
|
76
76
|
ctx := NewContextWithResource(context.Background(), nil, nil, nil, recorder,
|
|
77
77
|
ResourceContext{ScopeType: ScopeSpace, WorkspaceID: 11, SpaceID: 13},
|
|
78
|
-
ScopePrincipal{SubjectType: "
|
|
79
|
-
if ctx.Platform || ctx.WorkspaceID != 11 || ctx.SpaceID != 13 || ctx.Principal.SubjectID != "
|
|
78
|
+
ScopePrincipal{SubjectType: "system", SubjectID: "0", Username: "System"})
|
|
79
|
+
if ctx.Platform || ctx.WorkspaceID != 11 || ctx.SpaceID != 13 || ctx.Principal.SubjectID != "0" {
|
|
80
80
|
t.Fatalf("runtime context = %+v", ctx)
|
|
81
81
|
}
|
|
82
82
|
platform := NewContextWithResource(context.Background(), nil, nil, nil, recorder,
|
|
83
|
-
ResourceContext{ScopeType: ScopePlatform}, ScopePrincipal{SubjectType: "
|
|
83
|
+
ResourceContext{ScopeType: ScopePlatform}, ScopePrincipal{SubjectType: "system", SubjectID: "0"})
|
|
84
84
|
if !platform.Platform || platform.WorkspaceID != 0 || platform.SpaceID != 0 {
|
|
85
85
|
t.Fatalf("platform runtime context = %+v", platform)
|
|
86
86
|
}
|
|
87
|
+
personal := NewContextWithResource(context.Background(), nil, nil, nil, recorder,
|
|
88
|
+
ResourceContext{ScopeType: "user", OwnerUserID: 17}, ScopePrincipal{SubjectType: "system", SubjectID: "0"})
|
|
89
|
+
if personal.Platform || personal.OwnerUserID != 17 || personal.WorkspaceID != 0 || personal.SpaceID != 0 {
|
|
90
|
+
t.Fatalf("personal runtime context = %+v", personal)
|
|
91
|
+
}
|
|
87
92
|
}
|
|
@@ -106,6 +106,7 @@ type Context struct {
|
|
|
106
106
|
Platform bool `json:"platform"`
|
|
107
107
|
WorkspaceID int64 `json:"workspace_id,omitempty"`
|
|
108
108
|
SpaceID int64 `json:"space_id,omitempty"`
|
|
109
|
+
OwnerUserID int64 `json:"owner_user_id,omitempty"`
|
|
109
110
|
Principal ScopePrincipal `json:"principal"`
|
|
110
111
|
|
|
111
112
|
DB Database `json:"-"`
|
|
@@ -137,7 +138,7 @@ func NewContext(draftgo context.Context, input, user map[string]any, headers htt
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
// NewContextWithResource constructs a handler context with persisted resource
|
|
140
|
-
// ownership and the
|
|
141
|
+
// ownership and the verified caller/system principal supplied by DraftGo. Custom services
|
|
141
142
|
// cannot override these values through Input or request headers.
|
|
142
143
|
func NewContextWithResource(draftgo context.Context, input, user map[string]any, headers http.Header, client Client, resource ResourceContext, principal ScopePrincipal) *Context {
|
|
143
144
|
if draftgo == nil {
|
|
@@ -150,7 +151,7 @@ func NewContextWithResource(draftgo context.Context, input, user map[string]any,
|
|
|
150
151
|
adminPlatform := platformClient{client: client, context: draftgo, admin: true}
|
|
151
152
|
return &Context{
|
|
152
153
|
Input: input, User: user, Headers: headers, context: draftgo,
|
|
153
|
-
Platform: resource.ScopeType == ScopePlatform, WorkspaceID: resource.WorkspaceID, SpaceID: resource.SpaceID, Principal: principal,
|
|
154
|
+
Platform: resource.ScopeType == ScopePlatform, WorkspaceID: resource.WorkspaceID, SpaceID: resource.SpaceID, OwnerUserID: resource.OwnerUserID, Principal: principal,
|
|
154
155
|
DB: dbClient{platform}, Users: usersClient{platform}, Auth: authClient{platformClient: platform, user: user}, Notify: notifierClient{platform},
|
|
155
156
|
HTTP: httpClient{platform}, Cache: cacheClient{platform}, Config: configClient{platform}, AIHub: aiHubClient{platform},
|
|
156
157
|
Knowledge: knowledgeClient{platform}, Memory: memoryClient{platform}, Files: fileStoreClient{platform}, Scope: scopeClient{platform}, Billing: billingClient{platform},
|
|
@@ -165,7 +166,7 @@ func NewContextWithResource(draftgo context.Context, input, user map[string]any,
|
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
// Admin exposes explicitly elevated platform capabilities to trusted custom
|
|
168
|
-
// services. The runtime
|
|
169
|
+
// services. The runtime uses a system principal (actor_id=0), scopes elevation to one
|
|
169
170
|
// audited RPC call, and never exposes platform credentials.
|
|
170
171
|
type Admin struct {
|
|
171
172
|
DB Database
|
package/resources/skill/SKILL.md
CHANGED
|
@@ -47,7 +47,7 @@ description: Use this skill to inspect, develop, debug, or deliver a DraftGo app
|
|
|
47
47
|
- 同一文件或 DraftGo 资源全程只能由一个 Agent 修改;不同资源、operation 和 owner 不冲突的单元可以并发,不设置客户端并发上限;同一资源的依赖步骤保持串行。主 Agent 负责 owner 分配、汇总、验证、交付和 worklog 状态。
|
|
48
48
|
- 用户 API Key 只保存在 `.draftgo/config.json`,不得进入宿主配置、命令参数、Skill、日志、manifest 或错误消息。宿主 MCP 配置只运行 `draftgo mcp serve`。
|
|
49
49
|
- 页面优先使用可信本地资源;禁止境外 CDN。净化不可信 HTML,关键状态不能只靠颜色或动效表达。页面通过 `window.parent.App` 使用认证、权限、反馈和主题;详细规则见前端与运行时 Reference。
|
|
50
|
-
- 普通自定义服务 SDK
|
|
50
|
+
- Route 普通自定义服务 SDK 调用继承真实调用者和服务归属;Event/Scheduled 没有调用者,普通 `ctx.xxx` 返回 403,必须显式使用 `ctx.Admin.*`。Admin RPC 使用 system actor_id=0,按 `*:*:all` 单次执行并进入审计,不创建服务主体 AccessGrant,也不会泄漏到后续调用。
|
|
51
51
|
- `.draftgo/tmp/` 可清理;先运行 `draftgo clean --dry-run`,再在需要时使用 `--yes`。用户可查验的证据放入已注册的 `.draftgo/artifacts/`。
|
|
52
52
|
|
|
53
53
|
## 完成条件
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
---
|
|
2
|
-
read_when: 创建或调优 AI Agent 时 · 需要工具/子智能体/记忆/多轮/结构化输出/多模态时
|
|
3
|
-
---
|
|
4
|
-
|
|
1
|
+
---
|
|
2
|
+
read_when: 创建或调优 AI Agent 时 · 需要工具/子智能体/记忆/多轮/结构化输出/多模态时
|
|
3
|
+
---
|
|
4
|
+
|
|
5
5
|
# AIHub / Agent 资源
|
|
6
6
|
|
|
7
7
|
## 最短管理流程
|
|
@@ -24,86 +24,86 @@ AIHub 与知识库是独立权限域:Agent、模型和 AI 运行使用 `aihub:
|
|
|
24
24
|
|
|
25
25
|
AIHub 是结构化远端资源,不 checkout,也不生成本地镜像。先使用 MCP `draftgo_resource_search`/`draftgo_resource_list`
|
|
26
26
|
定位资产;未知 operation 才用 `draftgo_api_search`,首次使用或 registry revision 变化时 `draftgo_api_describe`,然后通过 `draftgo_api_call` 读写。
|
|
27
|
-
更新 AIHub 资产时只发送实时契约允许的字段;常见字段包括
|
|
28
|
-
`type, name, data, priority, version, tags, describe, permission, status`。供应商和模型使用各自的实时接口,
|
|
29
|
-
不要经旧 AIHub 资产接口写入。任何输出、工作区文件或调用参数都不得保存 API Key 或 header 值;只可处理
|
|
30
|
-
“是否已配置”以及 header 名称等非秘密元数据。
|
|
31
|
-
**Agent 的全部行为都在 `data`(尤其 `data.spec`)里**——本页就是 `data.spec` 的字段地图。
|
|
32
|
-
|
|
33
|
-
## 条目骨架
|
|
34
|
-
|
|
35
|
-
```jsonc
|
|
36
|
-
{
|
|
37
|
-
"type": "agent", // AIHub 资产类型
|
|
38
|
-
"name": "产品顾问",
|
|
39
|
-
"describe": "面向用户的产品答疑助手",
|
|
40
|
-
"status": "active",
|
|
41
|
-
"data": {
|
|
42
|
-
"mode": "chat", // chat | image_generation
|
|
43
|
-
"spec": { /* 见下表 */ }
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
页面对话 UI 使用 `<dg-chat protocol="draftgo-agent" agent-id="AGENT_ID">` 或 `DraftGoChat.create()`;
|
|
49
|
-
旧代码/无 UI 文本调用可用 `DraftGoAI.chat(...)`,图片模式使用 `DraftGoAI.images(...)`。调用前都必须加载
|
|
27
|
+
更新 AIHub 资产时只发送实时契约允许的字段;常见字段包括
|
|
28
|
+
`type, name, data, priority, version, tags, describe, permission, status`。供应商和模型使用各自的实时接口,
|
|
29
|
+
不要经旧 AIHub 资产接口写入。任何输出、工作区文件或调用参数都不得保存 API Key 或 header 值;只可处理
|
|
30
|
+
“是否已配置”以及 header 名称等非秘密元数据。
|
|
31
|
+
**Agent 的全部行为都在 `data`(尤其 `data.spec`)里**——本页就是 `data.spec` 的字段地图。
|
|
32
|
+
|
|
33
|
+
## 条目骨架
|
|
34
|
+
|
|
35
|
+
```jsonc
|
|
36
|
+
{
|
|
37
|
+
"type": "agent", // AIHub 资产类型
|
|
38
|
+
"name": "产品顾问",
|
|
39
|
+
"describe": "面向用户的产品答疑助手",
|
|
40
|
+
"status": "active",
|
|
41
|
+
"data": {
|
|
42
|
+
"mode": "chat", // chat | image_generation
|
|
43
|
+
"spec": { /* 见下表 */ }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
页面对话 UI 使用 `<dg-chat protocol="draftgo-agent" agent-id="AGENT_ID">` 或 `DraftGoChat.create()`;
|
|
49
|
+
旧代码/无 UI 文本调用可用 `DraftGoAI.chat(...)`,图片模式使用 `DraftGoAI.images(...)`。调用前都必须加载
|
|
50
50
|
`/assets/draftgo-chat.js`,完整用法见 `references/chat-sdk.md`。后端 operation 与可调用 Agent 列表通过 MCP 实时确认;已知契约可复用项目缓存。
|
|
51
|
-
|
|
52
|
-
## `data.spec` 字段地图
|
|
53
|
-
|
|
54
|
-
留空即维持默认/旧行为;除标注外都是可选。运行时统一在 `parseOrchestrationConfig` + 就地读取时带默认值与 clamp。
|
|
55
|
-
|
|
56
|
-
| 字段 | 类型 / 取值 | 说明 |
|
|
57
|
-
|---|---|---|
|
|
51
|
+
|
|
52
|
+
## `data.spec` 字段地图
|
|
53
|
+
|
|
54
|
+
留空即维持默认/旧行为;除标注外都是可选。运行时统一在 `parseOrchestrationConfig` + 就地读取时带默认值与 clamp。
|
|
55
|
+
|
|
56
|
+
| 字段 | 类型 / 取值 | 说明 |
|
|
57
|
+
|---|---|---|
|
|
58
58
|
| `mode` | `chat` / `image_generation` | 决定主要交互形态;具体 Responses、Embedding、Rerank、TTS、ASR、Video operation 以 MCP 和模型 capability 为准 |
|
|
59
|
-
| `model` | string | 主模型(逻辑模型名,映射到供应商路由) |
|
|
60
|
-
| `fallback_models` | string[] | 主模型失败后按序回退(跨模型 failover) |
|
|
61
|
-
| `model_selection.user_selectable` | bool | 是否允许调用方在请求里覆盖 `model`(配合 `selectable-models`) |
|
|
62
|
-
| `ttft_timeout` | number(秒,1–600,空=不启用) | 首字超时 failover:首个 SSE data 事件超时即跨模型+跨供应商切换,推理模型不误杀 |
|
|
59
|
+
| `model` | string | 主模型(逻辑模型名,映射到供应商路由) |
|
|
60
|
+
| `fallback_models` | string[] | 主模型失败后按序回退(跨模型 failover) |
|
|
61
|
+
| `model_selection.user_selectable` | bool | 是否允许调用方在请求里覆盖 `model`(配合 `selectable-models`) |
|
|
62
|
+
| `ttft_timeout` | number(秒,1–600,空=不启用) | 首字超时 failover:首个 SSE data 事件超时即跨模型+跨供应商切换,推理模型不误杀 |
|
|
63
63
|
| `sync_request_timeout` | number(秒,1–600,默认 100) | 非流式请求上限 |
|
|
64
64
|
| `stream_ttl` | number(秒,1–3600,默认 600) | 流式请求上限 |
|
|
65
65
|
| `max_tokens` | number / 空 | 最大输出 token;留空时不写入请求,即不由 Agent 额外限制 |
|
|
66
66
|
| `reasoning_effort` | `off`/`minimal`/`low`/`medium`/`high` | `off`/留空均不透传;其它值仅 OpenAI 系模型生效 |
|
|
67
|
-
| `system_prompt_template` | string | 系统提示模板 |
|
|
68
|
-
| `context.max_history` | number(默认 20) | 工作窗口:保留最近 N 条;关闭持续对话时即滑动窗口硬上限 |
|
|
69
|
-
| `output_format.mode` | `text` / `json` | JSON 时按 `output_format.json.{schema,schema_name,strategy}` 约束/校验/降级 |
|
|
70
|
-
| `capabilities.vision.{enabled,input,max_mb}` | 见值 | 图片/视觉输入(`image_url` part),`input`⊂{base64,url},默认 5MB |
|
|
71
|
-
| `capabilities.files.{enabled,allowed_ext,max_mb}` | 见值 | 文件附件抽取成文本注入;白名单 `.txt .md .docx .pdf .xlsx .json .csv`(pptx 不支持),默认 8MB |
|
|
72
|
-
| `tools.max_iterations` | 1–50(默认 10) | ReAct 工具循环步数上限 |
|
|
73
|
-
| `tools.sources[].{type,id}` | `mcp` / `custom_script` | 绑定 MCP 与「自定义服务作为工具」 |
|
|
74
|
-
| `knowledge_base_ids` | int[] | 绑定知识库,生成检索工具 |
|
|
75
|
-
| `skills` | 见运行时 | 绑定 Skill |
|
|
76
|
-
| `sub_agent_ids` | int[] | 子智能体:为每个 id 生成 `agent_{id}` 委派工具(用法同 `knowledge_base_ids`) |
|
|
67
|
+
| `system_prompt_template` | string | 系统提示模板 |
|
|
68
|
+
| `context.max_history` | number(默认 20) | 工作窗口:保留最近 N 条;关闭持续对话时即滑动窗口硬上限 |
|
|
69
|
+
| `output_format.mode` | `text` / `json` | JSON 时按 `output_format.json.{schema,schema_name,strategy}` 约束/校验/降级 |
|
|
70
|
+
| `capabilities.vision.{enabled,input,max_mb}` | 见值 | 图片/视觉输入(`image_url` part),`input`⊂{base64,url},默认 5MB |
|
|
71
|
+
| `capabilities.files.{enabled,allowed_ext,max_mb}` | 见值 | 文件附件抽取成文本注入;白名单 `.txt .md .docx .pdf .xlsx .json .csv`(pptx 不支持),默认 8MB |
|
|
72
|
+
| `tools.max_iterations` | 1–50(默认 10) | ReAct 工具循环步数上限 |
|
|
73
|
+
| `tools.sources[].{type,id}` | `mcp` / `custom_script` | 绑定 MCP 与「自定义服务作为工具」 |
|
|
74
|
+
| `knowledge_base_ids` | int[] | 绑定知识库,生成检索工具 |
|
|
75
|
+
| `skills` | 见运行时 | 绑定 Skill |
|
|
76
|
+
| `sub_agent_ids` | int[] | 子智能体:为每个 id 生成 `agent_{id}` 委派工具(用法同 `knowledge_base_ids`) |
|
|
77
77
|
| `call_permissions` | 角色配置 | 哪些角色可以调用此 Agent;调用接口与筛选条件以 MCP 实时契约为准 |
|
|
78
78
|
| `billing_mode` | `disabled` / `inherit_model` / `per_call` / `usage` | Agent 零售计费;公开 Agent 强制 `disabled`,订阅/会员额度由业务服务管理 |
|
|
79
|
-
|
|
80
|
-
### `orchestration.*`(编排开关)
|
|
81
|
-
|
|
82
|
-
| 字段 | 默认 | 说明 |
|
|
83
|
-
|---|---|---|
|
|
84
|
-
| `tool_concurrency` | 8(1–32) | 单步内并发执行工具数 |
|
|
85
|
-
| `on_max_steps` | `error` | 达步数上限:`error` 报错 / `stop` 返回最后一条 |
|
|
86
|
-
| `tool_disclosure.{mode,threshold_tools}` | `off` | 工具渐进披露:`off`/`auto`/`always`,首轮只给目录+`load_tools` |
|
|
87
|
-
| `planning.{enabled,prompt}` | false | 规划层:执行前先让模型列步骤,提示折叠进 system |
|
|
88
|
-
| `memory.{enabled,scope}` | false / `user_agent` | 长期记忆:对话后自动提炼、下轮召回注入;作用域 `agent`/`user`/`user_agent` |
|
|
89
|
-
| `compaction.{enabled,keep_recent,trigger_messages,trigger_tokens,preset}` | 关闭 | **持续对话/上下文闭环**:见下 |
|
|
90
|
-
| `checkpoint_input_mode` / `checkpoint_max_messages` / `checkpoint_ttl_seconds` | 全量 / 100 / 86400 | 会话历史持久化(配合请求 `session_id`) |
|
|
79
|
+
|
|
80
|
+
### `orchestration.*`(编排开关)
|
|
81
|
+
|
|
82
|
+
| 字段 | 默认 | 说明 |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `tool_concurrency` | 8(1–32) | 单步内并发执行工具数 |
|
|
85
|
+
| `on_max_steps` | `error` | 达步数上限:`error` 报错 / `stop` 返回最后一条 |
|
|
86
|
+
| `tool_disclosure.{mode,threshold_tools}` | `off` | 工具渐进披露:`off`/`auto`/`always`,首轮只给目录+`load_tools` |
|
|
87
|
+
| `planning.{enabled,prompt}` | false | 规划层:执行前先让模型列步骤,提示折叠进 system |
|
|
88
|
+
| `memory.{enabled,scope}` | false / `user_agent` | 长期记忆:对话后自动提炼、下轮召回注入;作用域 `agent`/`user`/`user_agent` |
|
|
89
|
+
| `compaction.{enabled,keep_recent,trigger_messages,trigger_tokens,preset}` | 关闭 | **持续对话/上下文闭环**:见下 |
|
|
90
|
+
| `checkpoint_input_mode` / `checkpoint_max_messages` / `checkpoint_ttl_seconds` | 全量 / 100 / 86400 | 会话历史持久化(配合请求 `session_id`) |
|
|
91
91
|
| `delegation.{max_depth,max_total_calls}` | 2 / 8 | 最大嵌套层数与整棵调用树共享的子 Agent 调用次数;并行分支也从同一预算扣减 |
|
|
92
92
|
|
|
93
93
|
同一模型步骤返回多个子 Agent 工具调用时,运行时立即按 `tool_concurrency` 并发执行;不同步骤自然串行,由模型自行决定编排方式。委派成功后,子 Agent 的每个模型回合 token 会汇总到入口 run,总量也写入对应委派 span。一次入口请求只创建一条主 run,Agent 列归属入口 Agent,委派链路从 span 查看。
|
|
94
|
-
|
|
95
|
-
### 持续对话(上下文闭环)
|
|
96
|
-
|
|
97
|
-
- `compaction.enabled=true` = 闭环:填满工作窗口后把溢出旧消息**摘要成一条滚动 summary** 续接,
|
|
98
|
-
而非直接丢弃。此时后端**跳过 `context.max_history` 硬砍**,让完整 checkpoint 历史进入压缩器蒸馏。
|
|
99
|
-
- 触发为双通道任一命中:`trigger_messages`(条数)或 `trigger_tokens`(估算 token,256–2000000)。
|
|
100
|
-
- `keep_recent`:保留最近 N 条不压缩。`preset`(`aggressive`/`balanced`/`conservative`)是管理台档位回显,
|
|
101
|
-
后端只认 `keep_recent`/`trigger_messages`/`trigger_tokens` 三个底层字段。
|
|
102
|
-
- 配合请求体 `session_id` 才会加载/续写会话历史;`<dg-chat>` 为每个 UI thread 自动维护该值,兼容门面可通过 `DraftGoAI.chat(..., {sessionId})` 显式传入。不传即无状态单轮。
|
|
103
|
-
- 关闭时逐字回退为 `max_history` 滑动窗口(旧行为,零影响)。
|
|
104
|
-
|
|
105
|
-
## 观测
|
|
106
|
-
|
|
94
|
+
|
|
95
|
+
### 持续对话(上下文闭环)
|
|
96
|
+
|
|
97
|
+
- `compaction.enabled=true` = 闭环:填满工作窗口后把溢出旧消息**摘要成一条滚动 summary** 续接,
|
|
98
|
+
而非直接丢弃。此时后端**跳过 `context.max_history` 硬砍**,让完整 checkpoint 历史进入压缩器蒸馏。
|
|
99
|
+
- 触发为双通道任一命中:`trigger_messages`(条数)或 `trigger_tokens`(估算 token,256–2000000)。
|
|
100
|
+
- `keep_recent`:保留最近 N 条不压缩。`preset`(`aggressive`/`balanced`/`conservative`)是管理台档位回显,
|
|
101
|
+
后端只认 `keep_recent`/`trigger_messages`/`trigger_tokens` 三个底层字段。
|
|
102
|
+
- 配合请求体 `session_id` 才会加载/续写会话历史;`<dg-chat>` 为每个 UI thread 自动维护该值,兼容门面可通过 `DraftGoAI.chat(..., {sessionId})` 显式传入。不传即无状态单轮。
|
|
103
|
+
- 关闭时逐字回退为 `max_history` 滑动窗口(旧行为,零影响)。
|
|
104
|
+
|
|
105
|
+
## 观测
|
|
106
|
+
|
|
107
107
|
每次调用都开一条 AI run,管理台 `/admin/ai-runs` 展示状态、tokens、延迟、`ttft_ms` 与 span 链路。
|
|
108
108
|
运行记录接口通过 MCP 实时发现,不维护静态路径表;已知 operation 复用 registry revision 未变化的契约缓存。
|
|
109
109
|
|
|
@@ -112,5 +112,5 @@ profile、transport readiness 和实际 route;不要只根据供应商名称
|
|
|
112
112
|
能力名大小写敏感,使用实时 describe 返回的 canonical 值,不使用历史别名。
|
|
113
113
|
|
|
114
114
|
Agent 对外响应(`/api/agents/{id}/chat`、图片接口、Go/Script SDK Agent 调用)不返回内部 `model`、`fallback_models` 或 `upstream_model`;错误也不暴露内部模型名、供应商名称、服务地址或运行时实现名。只有显式启用 `model_selection.user_selectable` 后,`/selectable-models` 才作为授权的模型选择目录返回可选逻辑模型名。运行日志仍在服务端保留真实模型、供应商与链路信息用于定位。
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
> 权威细节以 DraftGo `docs/modules/ai-platform/agent-runtime.md` 和实时 MCP schema 为准;本页是基座开发者视角的字段速查。
|