@octo-cyber/ai 0.5.0 → 0.5.2
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/dist/ai.module.d.ts +2 -1
- package/dist/ai.module.d.ts.map +1 -1
- package/dist/ai.module.js +22 -3
- package/dist/ai.module.js.map +1 -1
- package/dist/cli/cli-executor.service.js +1 -1
- package/dist/cli/cli-executor.service.js.map +1 -1
- package/dist/cli/cli-registry.service.js +1 -1
- package/dist/cli/cli-registry.service.js.map +1 -1
- package/dist/cli/controllers/ai-cli.controller.d.ts +1 -1
- package/dist/cli/controllers/ai-cli.controller.d.ts.map +1 -1
- package/dist/cli/controllers/ai-cli.controller.js +1 -1
- package/dist/cli/controllers/ai-cli.controller.js.map +1 -1
- package/dist/cli/workspace.service.js +1 -1
- package/dist/cli/workspace.service.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/skill/controllers/skill.controller.d.ts +33 -0
- package/dist/skill/controllers/skill.controller.d.ts.map +1 -0
- package/dist/skill/controllers/skill.controller.js +264 -0
- package/dist/skill/controllers/skill.controller.js.map +1 -0
- package/dist/skill/entities/octo-skill.entity.d.ts +22 -0
- package/dist/skill/entities/octo-skill.entity.d.ts.map +1 -0
- package/dist/skill/entities/octo-skill.entity.js +91 -0
- package/dist/skill/entities/octo-skill.entity.js.map +1 -0
- package/dist/skill/index.d.ts +7 -0
- package/dist/skill/index.d.ts.map +1 -0
- package/dist/skill/index.js +17 -0
- package/dist/skill/index.js.map +1 -0
- package/dist/skill/services/skill-generator.service.d.ts +47 -0
- package/dist/skill/services/skill-generator.service.d.ts.map +1 -0
- package/dist/skill/services/skill-generator.service.js +232 -0
- package/dist/skill/services/skill-generator.service.js.map +1 -0
- package/dist/skill/services/skill-installer.service.d.ts +38 -0
- package/dist/skill/services/skill-installer.service.d.ts.map +1 -0
- package/dist/skill/services/skill-installer.service.js +150 -0
- package/dist/skill/services/skill-installer.service.js.map +1 -0
- package/dist/skill/services/skill-registry.service.d.ts +60 -0
- package/dist/skill/services/skill-registry.service.d.ts.map +1 -0
- package/dist/skill/services/skill-registry.service.js +147 -0
- package/dist/skill/services/skill-registry.service.js.map +1 -0
- package/dist/skill/types.d.ts +45 -0
- package/dist/skill/types.d.ts.map +1 -0
- package/dist/skill/types.js +6 -0
- package/dist/skill/types.js.map +1 -0
- package/package.json +4 -4
- package/web/components/SkillCard.tsx +139 -0
- package/web/components/SkillEditorDialog.tsx +77 -0
- package/web/components/SkillInstallDialog.tsx +134 -0
- package/web/index.ts +2 -0
- package/web/manifest.ts +1 -0
- package/web/messages/en-US.json +53 -0
- package/web/messages/zh-CN.json +53 -0
- package/web/pages/SkillsPage.tsx +206 -0
- package/web/services/skill-service.ts +89 -0
- package/dist/controllers/agent.controller.d.ts +0 -13
- package/dist/controllers/agent.controller.d.ts.map +0 -1
- package/dist/controllers/agent.controller.js +0 -117
- package/dist/controllers/agent.controller.js.map +0 -1
- package/dist/controllers/capability.controller.d.ts +0 -16
- package/dist/controllers/capability.controller.d.ts.map +0 -1
- package/dist/controllers/capability.controller.js +0 -108
- package/dist/controllers/capability.controller.js.map +0 -1
- package/dist/controllers/client.controller.d.ts +0 -10
- package/dist/controllers/client.controller.d.ts.map +0 -1
- package/dist/controllers/client.controller.js +0 -79
- package/dist/controllers/client.controller.js.map +0 -1
- package/dist/controllers/execution.controller.d.ts +0 -11
- package/dist/controllers/execution.controller.d.ts.map +0 -1
- package/dist/controllers/execution.controller.js +0 -50
- package/dist/controllers/execution.controller.js.map +0 -1
- package/dist/controllers/index.d.ts +0 -5
- package/dist/controllers/index.d.ts.map +0 -1
- package/dist/controllers/index.js +0 -12
- package/dist/controllers/index.js.map +0 -1
- package/dist/entities/agent-binding.entity.d.ts +0 -17
- package/dist/entities/agent-binding.entity.d.ts.map +0 -1
- package/dist/entities/agent-binding.entity.js +0 -64
- package/dist/entities/agent-binding.entity.js.map +0 -1
- package/dist/entities/agent-definition.entity.d.ts +0 -17
- package/dist/entities/agent-definition.entity.d.ts.map +0 -1
- package/dist/entities/agent-definition.entity.js +0 -70
- package/dist/entities/agent-definition.entity.js.map +0 -1
- package/dist/entities/agent-execution.entity.d.ts +0 -23
- package/dist/entities/agent-execution.entity.d.ts.map +0 -1
- package/dist/entities/agent-execution.entity.js +0 -93
- package/dist/entities/agent-execution.entity.js.map +0 -1
- package/dist/entities/agent-instance.entity.d.ts +0 -19
- package/dist/entities/agent-instance.entity.d.ts.map +0 -1
- package/dist/entities/agent-instance.entity.js +0 -76
- package/dist/entities/agent-instance.entity.js.map +0 -1
- package/dist/entities/capability-definition.entity.d.ts +0 -20
- package/dist/entities/capability-definition.entity.d.ts.map +0 -1
- package/dist/entities/capability-definition.entity.js +0 -77
- package/dist/entities/capability-definition.entity.js.map +0 -1
- package/dist/entities/client-registration.entity.d.ts +0 -19
- package/dist/entities/client-registration.entity.d.ts.map +0 -1
- package/dist/entities/client-registration.entity.js +0 -79
- package/dist/entities/client-registration.entity.js.map +0 -1
- package/dist/entities/index.d.ts +0 -7
- package/dist/entities/index.d.ts.map +0 -1
- package/dist/entities/index.js +0 -16
- package/dist/entities/index.js.map +0 -1
- package/dist/services/agent.service.d.ts +0 -23
- package/dist/services/agent.service.d.ts.map +0 -1
- package/dist/services/agent.service.js +0 -76
- package/dist/services/agent.service.js.map +0 -1
- package/dist/services/capability-router.service.d.ts +0 -45
- package/dist/services/capability-router.service.d.ts.map +0 -1
- package/dist/services/capability-router.service.js +0 -120
- package/dist/services/capability-router.service.js.map +0 -1
- package/dist/services/client-registration.service.d.ts +0 -27
- package/dist/services/client-registration.service.d.ts.map +0 -1
- package/dist/services/client-registration.service.js +0 -83
- package/dist/services/client-registration.service.js.map +0 -1
- package/dist/services/index.d.ts +0 -5
- package/dist/services/index.d.ts.map +0 -1
- package/dist/services/index.js +0 -12
- package/dist/services/index.js.map +0 -1
- package/dist/services/sync-bridge.service.d.ts +0 -41
- package/dist/services/sync-bridge.service.d.ts.map +0 -1
- package/dist/services/sync-bridge.service.js +0 -118
- package/dist/services/sync-bridge.service.js.map +0 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { OctoSkill } from '../entities/octo-skill.entity.js';
|
|
2
|
+
import type { SkillSourceType } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manages OctoSkill CRUD and orchestrates skill (re)generation.
|
|
5
|
+
*/
|
|
6
|
+
export declare class SkillRegistryService {
|
|
7
|
+
private repo;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
private readonly generator;
|
|
10
|
+
initialize(): void;
|
|
11
|
+
/**
|
|
12
|
+
* Regenerate skills for all modules from RouteRegistry.
|
|
13
|
+
* Skips skills that have been manually edited (isEdited=true).
|
|
14
|
+
*/
|
|
15
|
+
regenerateSkills(baseUrl: string, apiKey?: string): Promise<{
|
|
16
|
+
created: string[];
|
|
17
|
+
updated: string[];
|
|
18
|
+
skipped: string[];
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* List all skills with optional filter.
|
|
22
|
+
*/
|
|
23
|
+
listSkills(filter?: {
|
|
24
|
+
isEnabled?: boolean;
|
|
25
|
+
sourceType?: SkillSourceType;
|
|
26
|
+
}): Promise<OctoSkill[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Get a single skill by ID.
|
|
29
|
+
*/
|
|
30
|
+
getSkill(id: string): Promise<OctoSkill | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Create a custom skill.
|
|
33
|
+
*/
|
|
34
|
+
createCustomSkill(data: {
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
content: string;
|
|
38
|
+
}): Promise<OctoSkill>;
|
|
39
|
+
/**
|
|
40
|
+
* Update skill content (marks as edited to prevent auto-overwrite).
|
|
41
|
+
*/
|
|
42
|
+
updateSkillContent(id: string, content: string): Promise<OctoSkill>;
|
|
43
|
+
/**
|
|
44
|
+
* Update skill metadata (name, description).
|
|
45
|
+
*/
|
|
46
|
+
updateSkill(id: string, data: Partial<Pick<OctoSkill, 'name' | 'description' | 'content'>>): Promise<OctoSkill>;
|
|
47
|
+
/**
|
|
48
|
+
* Toggle skill enabled state.
|
|
49
|
+
*/
|
|
50
|
+
toggleSkill(id: string): Promise<OctoSkill>;
|
|
51
|
+
/**
|
|
52
|
+
* Delete a skill.
|
|
53
|
+
*/
|
|
54
|
+
deleteSkill(id: string): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Update install targets for a skill (called by installer).
|
|
57
|
+
*/
|
|
58
|
+
updateInstallTargets(id: string, targets: string[]): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=skill-registry.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-registry.service.d.ts","sourceRoot":"","sources":["../../../src/skill/services/skill-registry.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAE7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;GAEG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,IAAI,CAAyB;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAElE,UAAU,IAAI,IAAI;IAKlB;;;OAGG;IACG,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAyCvE;;OAEG;IACG,UAAU,CAAC,MAAM,CAAC,EAAE;QACxB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,UAAU,CAAC,EAAE,eAAe,CAAC;KAC9B,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAOxB;;OAEG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAIrD;;OAEG;IACG,iBAAiB,CAAC,IAAI,EAAE;QAC5B,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,SAAS,CAAC;IAYtB;;OAEG;IACG,kBAAkB,CACtB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,CAAC;IAQrB;;OAEG;IACG,WAAW,CACf,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC,CAAC,GACjE,OAAO,CAAC,SAAS,CAAC;IAYrB;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAOjD;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5C;;OAEG;IACG,oBAAoB,CACxB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC;CAOjB"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SkillRegistryService = void 0;
|
|
4
|
+
const core_1 = require("@octo-cyber/core");
|
|
5
|
+
const octo_skill_entity_js_1 = require("../entities/octo-skill.entity.js");
|
|
6
|
+
const skill_generator_service_js_1 = require("./skill-generator.service.js");
|
|
7
|
+
/**
|
|
8
|
+
* Manages OctoSkill CRUD and orchestrates skill (re)generation.
|
|
9
|
+
*/
|
|
10
|
+
class SkillRegistryService {
|
|
11
|
+
repo;
|
|
12
|
+
logger = core_1.Container.get(core_1.LoggerService);
|
|
13
|
+
generator = core_1.Container.get(skill_generator_service_js_1.SkillGeneratorService);
|
|
14
|
+
initialize() {
|
|
15
|
+
const db = core_1.Container.get(core_1.DatabaseService);
|
|
16
|
+
this.repo = db.getRepository(octo_skill_entity_js_1.OctoSkill);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Regenerate skills for all modules from RouteRegistry.
|
|
20
|
+
* Skips skills that have been manually edited (isEdited=true).
|
|
21
|
+
*/
|
|
22
|
+
async regenerateSkills(baseUrl, apiKey) {
|
|
23
|
+
const results = this.generator.generateAll(baseUrl, apiKey);
|
|
24
|
+
const created = [];
|
|
25
|
+
const updated = [];
|
|
26
|
+
const skipped = [];
|
|
27
|
+
for (const result of results) {
|
|
28
|
+
const existing = await this.repo.findOneBy({ name: result.name });
|
|
29
|
+
if (existing) {
|
|
30
|
+
if (existing.isEdited) {
|
|
31
|
+
skipped.push(result.name);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
existing.description = result.description;
|
|
35
|
+
existing.content = result.content;
|
|
36
|
+
existing.version = result.version;
|
|
37
|
+
await this.repo.save(existing);
|
|
38
|
+
updated.push(result.name);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const skill = this.repo.create({
|
|
42
|
+
name: result.name,
|
|
43
|
+
description: result.description,
|
|
44
|
+
sourceModule: result.sourceModule,
|
|
45
|
+
sourceType: 'module',
|
|
46
|
+
content: result.content,
|
|
47
|
+
version: result.version,
|
|
48
|
+
isEnabled: true,
|
|
49
|
+
isEdited: false,
|
|
50
|
+
});
|
|
51
|
+
await this.repo.save(skill);
|
|
52
|
+
created.push(result.name);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.logger.info(`Skill regeneration: ${created.length} created, ${updated.length} updated, ${skipped.length} skipped`);
|
|
56
|
+
return { created, updated, skipped };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* List all skills with optional filter.
|
|
60
|
+
*/
|
|
61
|
+
async listSkills(filter) {
|
|
62
|
+
const where = {};
|
|
63
|
+
if (filter?.isEnabled !== undefined)
|
|
64
|
+
where.isEnabled = filter.isEnabled;
|
|
65
|
+
if (filter?.sourceType)
|
|
66
|
+
where.sourceType = filter.sourceType;
|
|
67
|
+
return this.repo.find({ where, order: { name: 'ASC' } });
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get a single skill by ID.
|
|
71
|
+
*/
|
|
72
|
+
async getSkill(id) {
|
|
73
|
+
return this.repo.findOneBy({ id });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create a custom skill.
|
|
77
|
+
*/
|
|
78
|
+
async createCustomSkill(data) {
|
|
79
|
+
const skill = this.repo.create({
|
|
80
|
+
...data,
|
|
81
|
+
sourceModule: 'custom',
|
|
82
|
+
sourceType: 'custom',
|
|
83
|
+
version: '1.0',
|
|
84
|
+
isEnabled: true,
|
|
85
|
+
isEdited: true,
|
|
86
|
+
});
|
|
87
|
+
return this.repo.save(skill);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Update skill content (marks as edited to prevent auto-overwrite).
|
|
91
|
+
*/
|
|
92
|
+
async updateSkillContent(id, content) {
|
|
93
|
+
const skill = await this.repo.findOneBy({ id });
|
|
94
|
+
if (!skill)
|
|
95
|
+
throw new Error(`Skill not found: ${id}`);
|
|
96
|
+
skill.content = content;
|
|
97
|
+
skill.isEdited = true;
|
|
98
|
+
return this.repo.save(skill);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Update skill metadata (name, description).
|
|
102
|
+
*/
|
|
103
|
+
async updateSkill(id, data) {
|
|
104
|
+
const skill = await this.repo.findOneBy({ id });
|
|
105
|
+
if (!skill)
|
|
106
|
+
throw new Error(`Skill not found: ${id}`);
|
|
107
|
+
if (data.name !== undefined)
|
|
108
|
+
skill.name = data.name;
|
|
109
|
+
if (data.description !== undefined)
|
|
110
|
+
skill.description = data.description;
|
|
111
|
+
if (data.content !== undefined) {
|
|
112
|
+
skill.content = data.content;
|
|
113
|
+
skill.isEdited = true;
|
|
114
|
+
}
|
|
115
|
+
return this.repo.save(skill);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Toggle skill enabled state.
|
|
119
|
+
*/
|
|
120
|
+
async toggleSkill(id) {
|
|
121
|
+
const skill = await this.repo.findOneBy({ id });
|
|
122
|
+
if (!skill)
|
|
123
|
+
throw new Error(`Skill not found: ${id}`);
|
|
124
|
+
skill.isEnabled = !skill.isEnabled;
|
|
125
|
+
return this.repo.save(skill);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Delete a skill.
|
|
129
|
+
*/
|
|
130
|
+
async deleteSkill(id) {
|
|
131
|
+
await this.repo.delete(id);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Update install targets for a skill (called by installer).
|
|
135
|
+
*/
|
|
136
|
+
async updateInstallTargets(id, targets) {
|
|
137
|
+
const skill = await this.repo.findOneBy({ id });
|
|
138
|
+
if (!skill)
|
|
139
|
+
throw new Error(`Skill not found: ${id}`);
|
|
140
|
+
skill.installTargets = targets.length > 0 ? JSON.stringify(targets) : null;
|
|
141
|
+
skill.lastInstalledAt = new Date();
|
|
142
|
+
await this.repo.save(skill);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
exports.SkillRegistryService = SkillRegistryService;
|
|
146
|
+
core_1.Container.register(SkillRegistryService);
|
|
147
|
+
//# sourceMappingURL=skill-registry.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-registry.service.js","sourceRoot":"","sources":["../../../src/skill/services/skill-registry.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill-related type definitions for the SKILL.md generation system.
|
|
3
|
+
*/
|
|
4
|
+
/** Supported AI tool targets for skill installation. */
|
|
5
|
+
export type SkillTarget = 'claude' | 'cursor' | 'codex';
|
|
6
|
+
/** Information about an AI tool installation target. */
|
|
7
|
+
export interface SkillTargetInfo {
|
|
8
|
+
id: SkillTarget;
|
|
9
|
+
name: string;
|
|
10
|
+
/** Base directory for skills (e.g. ~/.claude/skills/) */
|
|
11
|
+
skillsDir: string;
|
|
12
|
+
/** How to detect if the tool is installed */
|
|
13
|
+
detectDir: string;
|
|
14
|
+
isInstalled: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** Source type for a skill. */
|
|
17
|
+
export type SkillSourceType = 'module' | 'custom';
|
|
18
|
+
/** Parameters for generating a skill from a module. */
|
|
19
|
+
export interface SkillGenerateParams {
|
|
20
|
+
/** Module name (e.g. 'auth', 'erp'). */
|
|
21
|
+
module: string;
|
|
22
|
+
/** Base URL of the Octo server (e.g. 'http://localhost:9988'). */
|
|
23
|
+
baseUrl: string;
|
|
24
|
+
/** API Key to embed in the skill (full key string). */
|
|
25
|
+
apiKey?: string;
|
|
26
|
+
}
|
|
27
|
+
/** Result of skill generation. */
|
|
28
|
+
export interface SkillGenerateResult {
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
content: string;
|
|
32
|
+
sourceModule: string;
|
|
33
|
+
version: string;
|
|
34
|
+
}
|
|
35
|
+
/** Endpoint info extracted from RouteRegistry for skill generation. */
|
|
36
|
+
export interface EndpointInfo {
|
|
37
|
+
method: string;
|
|
38
|
+
path: string;
|
|
39
|
+
summary: string;
|
|
40
|
+
permission?: string;
|
|
41
|
+
controller: string;
|
|
42
|
+
controllerName?: string;
|
|
43
|
+
tags?: string[];
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/skill/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wDAAwD;AACxD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAExD,wDAAwD;AACxD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,WAAW,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,+BAA+B;AAC/B,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAElD,uDAAuD;AACvD,MAAM,WAAW,mBAAmB;IAClC,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kCAAkC;AAClC,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/skill/types.ts"],"names":[],"mappings":";AAAA;;GAEG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octo-cyber/ai",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Octo AI module — Agent + Capability + SyncBridge + client registration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@octo-cyber/core": "^0.5.
|
|
26
|
-
"@octo-cyber/
|
|
27
|
-
"@octo-cyber/
|
|
25
|
+
"@octo-cyber/core": "^0.5.3",
|
|
26
|
+
"@octo-cyber/auth": "^0.5.4",
|
|
27
|
+
"@octo-cyber/task": "^0.5.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": "^19.0.0",
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from 'next-intl'
|
|
4
|
+
import {
|
|
5
|
+
Download,
|
|
6
|
+
Edit,
|
|
7
|
+
MoreHorizontal,
|
|
8
|
+
Trash2,
|
|
9
|
+
ToggleLeft,
|
|
10
|
+
ToggleRight,
|
|
11
|
+
CheckCircle2,
|
|
12
|
+
} from 'lucide-react'
|
|
13
|
+
|
|
14
|
+
import { Badge } from '@octo-cyber/ui/components/ui/badge'
|
|
15
|
+
import { Button } from '@octo-cyber/ui/components/ui/button'
|
|
16
|
+
import {
|
|
17
|
+
Card,
|
|
18
|
+
CardContent,
|
|
19
|
+
CardDescription,
|
|
20
|
+
CardHeader,
|
|
21
|
+
CardTitle,
|
|
22
|
+
} from '@octo-cyber/ui/components/ui/card'
|
|
23
|
+
import {
|
|
24
|
+
DropdownMenu,
|
|
25
|
+
DropdownMenuContent,
|
|
26
|
+
DropdownMenuItem,
|
|
27
|
+
DropdownMenuSeparator,
|
|
28
|
+
DropdownMenuTrigger,
|
|
29
|
+
} from '@octo-cyber/ui/components/ui/dropdown-menu'
|
|
30
|
+
|
|
31
|
+
import type { SkillInfo, SkillTargetInfo } from '../services/skill-service'
|
|
32
|
+
|
|
33
|
+
interface SkillCardProps {
|
|
34
|
+
skill: SkillInfo
|
|
35
|
+
targets: SkillTargetInfo[]
|
|
36
|
+
onInstall: () => void
|
|
37
|
+
onEdit: () => void
|
|
38
|
+
onToggle: () => void
|
|
39
|
+
onDelete: () => void
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default function SkillCard({
|
|
43
|
+
skill,
|
|
44
|
+
targets,
|
|
45
|
+
onInstall,
|
|
46
|
+
onEdit,
|
|
47
|
+
onToggle,
|
|
48
|
+
onDelete,
|
|
49
|
+
}: SkillCardProps) {
|
|
50
|
+
const t = useTranslations('aiSkills')
|
|
51
|
+
|
|
52
|
+
const installedTargets = skill.installedTargets ?? []
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Card className="flex flex-col">
|
|
56
|
+
<CardHeader className="flex-row items-start justify-between space-y-0 pb-2">
|
|
57
|
+
<div className="space-y-1">
|
|
58
|
+
<CardTitle className="text-base font-semibold">
|
|
59
|
+
{skill.name}
|
|
60
|
+
</CardTitle>
|
|
61
|
+
<CardDescription className="line-clamp-2 text-sm">
|
|
62
|
+
{skill.description}
|
|
63
|
+
</CardDescription>
|
|
64
|
+
</div>
|
|
65
|
+
<DropdownMenu>
|
|
66
|
+
<DropdownMenuTrigger asChild>
|
|
67
|
+
<Button variant="ghost" size="icon" className="h-8 w-8">
|
|
68
|
+
<MoreHorizontal className="h-4 w-4" />
|
|
69
|
+
</Button>
|
|
70
|
+
</DropdownMenuTrigger>
|
|
71
|
+
<DropdownMenuContent align="end">
|
|
72
|
+
<DropdownMenuItem onClick={onEdit}>
|
|
73
|
+
<Edit className="mr-2 h-4 w-4" />
|
|
74
|
+
{t('actions.edit')}
|
|
75
|
+
</DropdownMenuItem>
|
|
76
|
+
<DropdownMenuItem onClick={onToggle}>
|
|
77
|
+
{skill.isEnabled ? (
|
|
78
|
+
<ToggleLeft className="mr-2 h-4 w-4" />
|
|
79
|
+
) : (
|
|
80
|
+
<ToggleRight className="mr-2 h-4 w-4" />
|
|
81
|
+
)}
|
|
82
|
+
{skill.isEnabled ? t('actions.disable') : t('actions.enable')}
|
|
83
|
+
</DropdownMenuItem>
|
|
84
|
+
<DropdownMenuSeparator />
|
|
85
|
+
<DropdownMenuItem onClick={onDelete} className="text-destructive">
|
|
86
|
+
<Trash2 className="mr-2 h-4 w-4" />
|
|
87
|
+
{t('actions.delete')}
|
|
88
|
+
</DropdownMenuItem>
|
|
89
|
+
</DropdownMenuContent>
|
|
90
|
+
</DropdownMenu>
|
|
91
|
+
</CardHeader>
|
|
92
|
+
|
|
93
|
+
<CardContent className="flex flex-1 flex-col justify-between gap-3">
|
|
94
|
+
{/* Meta info */}
|
|
95
|
+
<div className="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
|
96
|
+
<Badge variant={skill.isEnabled ? 'default' : 'secondary'} className="text-xs">
|
|
97
|
+
{skill.isEnabled ? t('status.enabled') : t('status.disabled')}
|
|
98
|
+
</Badge>
|
|
99
|
+
<span>{t('meta.source')}: {skill.sourceModule}</span>
|
|
100
|
+
<span>v{skill.version}</span>
|
|
101
|
+
{skill.isEdited && (
|
|
102
|
+
<Badge variant="outline" className="text-xs">
|
|
103
|
+
{t('meta.edited')}
|
|
104
|
+
</Badge>
|
|
105
|
+
)}
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
{/* Install status */}
|
|
109
|
+
<div className="flex flex-wrap items-center gap-1.5">
|
|
110
|
+
{targets.filter((tgt) => tgt.isInstalled).map((tgt) => {
|
|
111
|
+
const isInstalled = installedTargets.includes(tgt.id)
|
|
112
|
+
return (
|
|
113
|
+
<Badge
|
|
114
|
+
key={tgt.id}
|
|
115
|
+
variant={isInstalled ? 'default' : 'outline'}
|
|
116
|
+
className="text-xs"
|
|
117
|
+
>
|
|
118
|
+
{isInstalled && <CheckCircle2 className="mr-1 h-3 w-3" />}
|
|
119
|
+
{tgt.name}
|
|
120
|
+
</Badge>
|
|
121
|
+
)
|
|
122
|
+
})}
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
{/* Install button */}
|
|
126
|
+
<Button
|
|
127
|
+
variant="outline"
|
|
128
|
+
size="sm"
|
|
129
|
+
className="w-full"
|
|
130
|
+
onClick={onInstall}
|
|
131
|
+
disabled={!skill.isEnabled}
|
|
132
|
+
>
|
|
133
|
+
<Download className="mr-2 h-4 w-4" />
|
|
134
|
+
{t('actions.install')}
|
|
135
|
+
</Button>
|
|
136
|
+
</CardContent>
|
|
137
|
+
</Card>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { useTranslations } from 'next-intl'
|
|
5
|
+
import { Loader2 } from 'lucide-react'
|
|
6
|
+
|
|
7
|
+
import { Button } from '@octo-cyber/ui/components/ui/button'
|
|
8
|
+
import {
|
|
9
|
+
Dialog,
|
|
10
|
+
DialogContent,
|
|
11
|
+
DialogDescription,
|
|
12
|
+
DialogFooter,
|
|
13
|
+
DialogHeader,
|
|
14
|
+
DialogTitle,
|
|
15
|
+
} from '@octo-cyber/ui/components/ui/dialog'
|
|
16
|
+
import { Textarea } from '@octo-cyber/ui/components/ui/textarea'
|
|
17
|
+
|
|
18
|
+
import type { SkillInfo } from '../services/skill-service'
|
|
19
|
+
|
|
20
|
+
interface SkillEditorDialogProps {
|
|
21
|
+
skill: SkillInfo
|
|
22
|
+
open: boolean
|
|
23
|
+
onOpenChange: (open: boolean) => void
|
|
24
|
+
onSave: (id: string, content: string) => Promise<void>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default function SkillEditorDialog({
|
|
28
|
+
skill,
|
|
29
|
+
open,
|
|
30
|
+
onOpenChange,
|
|
31
|
+
onSave,
|
|
32
|
+
}: SkillEditorDialogProps) {
|
|
33
|
+
const t = useTranslations('aiSkills')
|
|
34
|
+
const tc = useTranslations('common')
|
|
35
|
+
|
|
36
|
+
const [content, setContent] = useState(skill.content)
|
|
37
|
+
const [saving, setSaving] = useState(false)
|
|
38
|
+
|
|
39
|
+
const handleSave = async () => {
|
|
40
|
+
setSaving(true)
|
|
41
|
+
try {
|
|
42
|
+
await onSave(skill.id, content)
|
|
43
|
+
} finally {
|
|
44
|
+
setSaving(false)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
50
|
+
<DialogContent className="sm:max-w-3xl max-h-[80vh]">
|
|
51
|
+
<DialogHeader>
|
|
52
|
+
<DialogTitle>{t('editor.title', { name: skill.name })}</DialogTitle>
|
|
53
|
+
<DialogDescription>
|
|
54
|
+
{t('editor.description')}
|
|
55
|
+
</DialogDescription>
|
|
56
|
+
</DialogHeader>
|
|
57
|
+
|
|
58
|
+
<Textarea
|
|
59
|
+
value={content}
|
|
60
|
+
onChange={(e) => setContent(e.target.value)}
|
|
61
|
+
className="min-h-[400px] font-mono text-sm"
|
|
62
|
+
placeholder="SKILL.md content..."
|
|
63
|
+
/>
|
|
64
|
+
|
|
65
|
+
<DialogFooter>
|
|
66
|
+
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
|
67
|
+
{tc('cancel')}
|
|
68
|
+
</Button>
|
|
69
|
+
<Button onClick={handleSave} disabled={saving}>
|
|
70
|
+
{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
71
|
+
{tc('save')}
|
|
72
|
+
</Button>
|
|
73
|
+
</DialogFooter>
|
|
74
|
+
</DialogContent>
|
|
75
|
+
</Dialog>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from 'react'
|
|
4
|
+
import { useTranslations } from 'next-intl'
|
|
5
|
+
import { toast } from 'sonner'
|
|
6
|
+
import { CheckCircle2, Loader2 } from 'lucide-react'
|
|
7
|
+
|
|
8
|
+
import { Button } from '@octo-cyber/ui/components/ui/button'
|
|
9
|
+
import { Checkbox } from '@octo-cyber/ui/components/ui/checkbox'
|
|
10
|
+
import {
|
|
11
|
+
Dialog,
|
|
12
|
+
DialogContent,
|
|
13
|
+
DialogDescription,
|
|
14
|
+
DialogFooter,
|
|
15
|
+
DialogHeader,
|
|
16
|
+
DialogTitle,
|
|
17
|
+
} from '@octo-cyber/ui/components/ui/dialog'
|
|
18
|
+
import { Label } from '@octo-cyber/ui/components/ui/label'
|
|
19
|
+
|
|
20
|
+
import { skillService } from '../services/skill-service'
|
|
21
|
+
import type { SkillInfo, SkillTargetInfo } from '../services/skill-service'
|
|
22
|
+
|
|
23
|
+
interface SkillInstallDialogProps {
|
|
24
|
+
skill: SkillInfo
|
|
25
|
+
targets: SkillTargetInfo[]
|
|
26
|
+
open: boolean
|
|
27
|
+
onOpenChange: (open: boolean) => void
|
|
28
|
+
onDone: () => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function SkillInstallDialog({
|
|
32
|
+
skill,
|
|
33
|
+
targets,
|
|
34
|
+
open,
|
|
35
|
+
onOpenChange,
|
|
36
|
+
onDone,
|
|
37
|
+
}: SkillInstallDialogProps) {
|
|
38
|
+
const t = useTranslations('aiSkills')
|
|
39
|
+
const tc = useTranslations('common')
|
|
40
|
+
|
|
41
|
+
const availableTargets = targets.filter((tgt) => tgt.isInstalled)
|
|
42
|
+
const [selected, setSelected] = useState<string[]>(
|
|
43
|
+
skill.installedTargets ?? [],
|
|
44
|
+
)
|
|
45
|
+
const [installing, setInstalling] = useState(false)
|
|
46
|
+
|
|
47
|
+
const toggleTarget = useCallback((id: string) => {
|
|
48
|
+
setSelected((prev) =>
|
|
49
|
+
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],
|
|
50
|
+
)
|
|
51
|
+
}, [])
|
|
52
|
+
|
|
53
|
+
const handleInstall = useCallback(async () => {
|
|
54
|
+
if (selected.length === 0) return
|
|
55
|
+
setInstalling(true)
|
|
56
|
+
try {
|
|
57
|
+
const result = await skillService.install(skill.id, selected)
|
|
58
|
+
if (result.failed.length > 0) {
|
|
59
|
+
toast.error(t('toast.installPartial', {
|
|
60
|
+
failed: result.failed.map((f) => f.target).join(', '),
|
|
61
|
+
}))
|
|
62
|
+
} else {
|
|
63
|
+
toast.success(t('toast.installed', {
|
|
64
|
+
targets: selected.join(', '),
|
|
65
|
+
}))
|
|
66
|
+
}
|
|
67
|
+
if (result.apiKeyCreated) {
|
|
68
|
+
toast.success(t('toast.apiKeyCreated'))
|
|
69
|
+
}
|
|
70
|
+
onDone()
|
|
71
|
+
} catch {
|
|
72
|
+
toast.error(t('toast.installFailed'))
|
|
73
|
+
} finally {
|
|
74
|
+
setInstalling(false)
|
|
75
|
+
}
|
|
76
|
+
}, [skill.id, selected, t, onDone])
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
80
|
+
<DialogContent className="sm:max-w-md">
|
|
81
|
+
<DialogHeader>
|
|
82
|
+
<DialogTitle>{t('installDialog.title')}</DialogTitle>
|
|
83
|
+
<DialogDescription>
|
|
84
|
+
{t('installDialog.description', { name: skill.name })}
|
|
85
|
+
</DialogDescription>
|
|
86
|
+
</DialogHeader>
|
|
87
|
+
|
|
88
|
+
<div className="space-y-3 py-4">
|
|
89
|
+
{availableTargets.length === 0 ? (
|
|
90
|
+
<p className="text-sm text-muted-foreground">
|
|
91
|
+
{t('installDialog.noTargets')}
|
|
92
|
+
</p>
|
|
93
|
+
) : (
|
|
94
|
+
availableTargets.map((tgt) => {
|
|
95
|
+
const isAlreadyInstalled = (skill.installedTargets ?? []).includes(tgt.id)
|
|
96
|
+
return (
|
|
97
|
+
<div key={tgt.id} className="flex items-center space-x-3">
|
|
98
|
+
<Checkbox
|
|
99
|
+
id={`target-${tgt.id}`}
|
|
100
|
+
checked={selected.includes(tgt.id)}
|
|
101
|
+
onCheckedChange={() => toggleTarget(tgt.id)}
|
|
102
|
+
/>
|
|
103
|
+
<Label htmlFor={`target-${tgt.id}`} className="flex items-center gap-2 text-sm">
|
|
104
|
+
{tgt.name}
|
|
105
|
+
{isAlreadyInstalled && (
|
|
106
|
+
<CheckCircle2 className="h-3.5 w-3.5 text-green-500" />
|
|
107
|
+
)}
|
|
108
|
+
</Label>
|
|
109
|
+
</div>
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<p className="text-xs text-muted-foreground">
|
|
116
|
+
{t('installDialog.apiKeyNote')}
|
|
117
|
+
</p>
|
|
118
|
+
|
|
119
|
+
<DialogFooter>
|
|
120
|
+
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
|
121
|
+
{tc('cancel')}
|
|
122
|
+
</Button>
|
|
123
|
+
<Button
|
|
124
|
+
onClick={handleInstall}
|
|
125
|
+
disabled={installing || selected.length === 0}
|
|
126
|
+
>
|
|
127
|
+
{installing && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
128
|
+
{t('actions.install')}
|
|
129
|
+
</Button>
|
|
130
|
+
</DialogFooter>
|
|
131
|
+
</DialogContent>
|
|
132
|
+
</Dialog>
|
|
133
|
+
)
|
|
134
|
+
}
|
package/web/index.ts
CHANGED
|
@@ -14,6 +14,8 @@ export { default as aiMessagesEnUS } from './messages/en-US.json';
|
|
|
14
14
|
// Pages
|
|
15
15
|
export { default as AiCliToolsPage } from './pages/AiCliToolsPage';
|
|
16
16
|
export { default as WorkspacesPage } from './pages/WorkspacesPage';
|
|
17
|
+
export { default as SkillsPage } from './pages/SkillsPage';
|
|
17
18
|
|
|
18
19
|
// Services
|
|
19
20
|
export { aiCliService } from './services/ai-cli-service';
|
|
21
|
+
export { skillService } from './services/skill-service';
|
package/web/manifest.ts
CHANGED
|
@@ -10,5 +10,6 @@ export const aiFrontendManifest: IFrontendManifest = {
|
|
|
10
10
|
pages: [
|
|
11
11
|
{ path: '/ai-tools', titleKey: 'aiCli.pages.tools', icon: 'Terminal' },
|
|
12
12
|
{ path: '/ai-tools/workspaces', titleKey: 'aiCli.pages.workspaces', icon: 'Terminal' },
|
|
13
|
+
{ path: '/ai-tools/skills', titleKey: 'aiSkills.pages.skills', icon: 'Sparkles' },
|
|
13
14
|
],
|
|
14
15
|
};
|