@octo-cyber/ai 0.5.1 → 0.5.3
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 +1 -0
- package/dist/ai.module.d.ts.map +1 -1
- package/dist/ai.module.js +21 -2
- package/dist/ai.module.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 +5 -4
- 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
|
@@ -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.3",
|
|
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/
|
|
26
|
-
"@octo-cyber/
|
|
27
|
-
"@octo-cyber/
|
|
25
|
+
"@octo-cyber/core": "^0.5.4",
|
|
26
|
+
"@octo-cyber/auth": "^0.5.5",
|
|
27
|
+
"@octo-cyber/task": "^0.5.1"
|
|
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
|
@@ -5,10 +5,11 @@ export const aiFrontendManifest: IFrontendManifest = {
|
|
|
5
5
|
icon: 'Terminal',
|
|
6
6
|
color: 'cyan',
|
|
7
7
|
position: 'main',
|
|
8
|
-
titleKey: '
|
|
9
|
-
descriptionKey: '
|
|
8
|
+
titleKey: 'aiTools.title',
|
|
9
|
+
descriptionKey: 'aiTools.description',
|
|
10
10
|
pages: [
|
|
11
|
-
{ path: '/ai-tools', titleKey: '
|
|
12
|
-
{ path: '/ai-tools/workspaces', titleKey: '
|
|
11
|
+
{ path: '/ai-tools', titleKey: 'aiTools.pages.tools', icon: 'Terminal', component: '@octo-cyber/ai/web/pages/AiCliToolsPage' },
|
|
12
|
+
{ path: '/ai-tools/workspaces', titleKey: 'aiTools.pages.workspaces', icon: 'Terminal', component: '@octo-cyber/ai/web/pages/WorkspacesPage' },
|
|
13
|
+
{ path: '/ai-tools/skills', titleKey: 'aiTools.pages.skills', icon: 'Sparkles', component: '@octo-cyber/ai/web/pages/SkillsPage' },
|
|
13
14
|
],
|
|
14
15
|
};
|
package/web/messages/en-US.json
CHANGED
|
@@ -77,5 +77,58 @@
|
|
|
77
77
|
"pullFailed": "Pull failed"
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
},
|
|
81
|
+
"aiSkills": {
|
|
82
|
+
"title": "AI Skills",
|
|
83
|
+
"description": "Manage SKILL.md files generated from system APIs, install to Claude Code, Cursor, and more",
|
|
84
|
+
"empty": "No skills yet. Click the button below to generate.",
|
|
85
|
+
"tabs": {
|
|
86
|
+
"all": "All",
|
|
87
|
+
"enabled": "Enabled",
|
|
88
|
+
"installed": "Installed"
|
|
89
|
+
},
|
|
90
|
+
"status": {
|
|
91
|
+
"enabled": "Enabled",
|
|
92
|
+
"disabled": "Disabled"
|
|
93
|
+
},
|
|
94
|
+
"meta": {
|
|
95
|
+
"source": "Source",
|
|
96
|
+
"edited": "Edited"
|
|
97
|
+
},
|
|
98
|
+
"actions": {
|
|
99
|
+
"generate": "Generate Skills",
|
|
100
|
+
"install": "Install",
|
|
101
|
+
"edit": "Edit",
|
|
102
|
+
"enable": "Enable",
|
|
103
|
+
"disable": "Disable",
|
|
104
|
+
"delete": "Delete"
|
|
105
|
+
},
|
|
106
|
+
"installDialog": {
|
|
107
|
+
"title": "Install Skill",
|
|
108
|
+
"description": "Select AI tools to install {name}",
|
|
109
|
+
"noTargets": "No installed AI tools detected",
|
|
110
|
+
"apiKeyNote": "An API Key will be auto-created and embedded in the Skill file. AI tools can call the API directly."
|
|
111
|
+
},
|
|
112
|
+
"editor": {
|
|
113
|
+
"title": "Edit {name}",
|
|
114
|
+
"description": "Modify SKILL.md content. Saving marks it as 'Edited' to prevent auto-regeneration overwrite."
|
|
115
|
+
},
|
|
116
|
+
"toast": {
|
|
117
|
+
"loadFailed": "Failed to load skills",
|
|
118
|
+
"generateDone": "Generation complete: {created} created, {updated} updated",
|
|
119
|
+
"generateFailed": "Generation failed",
|
|
120
|
+
"installed": "Installed to {targets}",
|
|
121
|
+
"installFailed": "Installation failed",
|
|
122
|
+
"installPartial": "Partial installation failure: {failed}",
|
|
123
|
+
"apiKeyCreated": "API Key auto-created",
|
|
124
|
+
"saved": "Saved",
|
|
125
|
+
"saveFailed": "Save failed",
|
|
126
|
+
"toggleFailed": "Operation failed",
|
|
127
|
+
"deleted": "Deleted",
|
|
128
|
+
"deleteFailed": "Delete failed"
|
|
129
|
+
},
|
|
130
|
+
"pages": {
|
|
131
|
+
"skills": "AI Skills"
|
|
132
|
+
}
|
|
80
133
|
}
|
|
81
134
|
}
|
package/web/messages/zh-CN.json
CHANGED
|
@@ -77,5 +77,58 @@
|
|
|
77
77
|
"pullFailed": "拉取失败"
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
},
|
|
81
|
+
"aiSkills": {
|
|
82
|
+
"title": "AI Skills",
|
|
83
|
+
"description": "管理系统 API 生成的 Skill 文件,安装到 Claude Code、Cursor 等 AI 工具",
|
|
84
|
+
"empty": "暂无 Skill,点击下方按钮生成",
|
|
85
|
+
"tabs": {
|
|
86
|
+
"all": "全部",
|
|
87
|
+
"enabled": "已启用",
|
|
88
|
+
"installed": "已安装"
|
|
89
|
+
},
|
|
90
|
+
"status": {
|
|
91
|
+
"enabled": "已启用",
|
|
92
|
+
"disabled": "已禁用"
|
|
93
|
+
},
|
|
94
|
+
"meta": {
|
|
95
|
+
"source": "来源",
|
|
96
|
+
"edited": "已编辑"
|
|
97
|
+
},
|
|
98
|
+
"actions": {
|
|
99
|
+
"generate": "生成 Skills",
|
|
100
|
+
"install": "安装",
|
|
101
|
+
"edit": "编辑",
|
|
102
|
+
"enable": "启用",
|
|
103
|
+
"disable": "禁用",
|
|
104
|
+
"delete": "删除"
|
|
105
|
+
},
|
|
106
|
+
"installDialog": {
|
|
107
|
+
"title": "安装 Skill",
|
|
108
|
+
"description": "选择要安装 {name} 的 AI 工具",
|
|
109
|
+
"noTargets": "未检测到已安装的 AI 工具",
|
|
110
|
+
"apiKeyNote": "安装时会自动创建 API Key 并写入 Skill 文件,AI 工具可直接调用 API。"
|
|
111
|
+
},
|
|
112
|
+
"editor": {
|
|
113
|
+
"title": "编辑 {name}",
|
|
114
|
+
"description": "修改 SKILL.md 内容。保存后将标记为「已编辑」,自动生成不会覆盖。"
|
|
115
|
+
},
|
|
116
|
+
"toast": {
|
|
117
|
+
"loadFailed": "加载 Skill 列表失败",
|
|
118
|
+
"generateDone": "生成完成:{created} 个新建,{updated} 个更新",
|
|
119
|
+
"generateFailed": "生成失败",
|
|
120
|
+
"installed": "已安装到 {targets}",
|
|
121
|
+
"installFailed": "安装失败",
|
|
122
|
+
"installPartial": "部分安装失败:{failed}",
|
|
123
|
+
"apiKeyCreated": "已自动创建 API Key",
|
|
124
|
+
"saved": "已保存",
|
|
125
|
+
"saveFailed": "保存失败",
|
|
126
|
+
"toggleFailed": "操作失败",
|
|
127
|
+
"deleted": "已删除",
|
|
128
|
+
"deleteFailed": "删除失败"
|
|
129
|
+
},
|
|
130
|
+
"pages": {
|
|
131
|
+
"skills": "AI Skills"
|
|
132
|
+
}
|
|
80
133
|
}
|
|
81
134
|
}
|