aimeat 1.2.5 → 1.3.0
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/.env.example +4 -0
- package/dist/locales/en.json +74 -2
- package/dist/locales/fi.json +74 -2
- package/dist/public/css/views/capabilities.css +211 -0
- package/dist/public/llms-template.txt +152 -89
- package/dist/public/spa.html +2 -62
- package/dist/public/views/admin/capabilities-tab.js +114 -0
- package/dist/public/views/admin.js +2 -0
- package/dist/public/views/aimeat-os.js +194 -34
- package/dist/public/views/profile/capabilities-tab.js +252 -0
- package/dist/public/views/profile/extensions-tab.js +404 -50
- package/dist/public/views/profile/landing-page.js +2 -0
- package/dist/public/views/profile.js +2 -0
- package/dist/src/config.d.ts +11 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +11 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/mcp/apps.d.ts +18 -0
- package/dist/src/mcp/apps.d.ts.map +1 -0
- package/dist/src/mcp/apps.js +241 -0
- package/dist/src/mcp/apps.js.map +1 -0
- package/dist/src/mcp/capabilities.d.ts +12 -0
- package/dist/src/mcp/capabilities.d.ts.map +1 -0
- package/dist/src/mcp/capabilities.js +187 -0
- package/dist/src/mcp/capabilities.js.map +1 -0
- package/dist/src/mcp/cortex.d.ts +17 -0
- package/dist/src/mcp/cortex.d.ts.map +1 -0
- package/dist/src/mcp/cortex.js +237 -0
- package/dist/src/mcp/cortex.js.map +1 -0
- package/dist/src/mcp/extensions.d.ts +4 -3
- package/dist/src/mcp/extensions.d.ts.map +1 -1
- package/dist/src/mcp/extensions.js +233 -3
- package/dist/src/mcp/extensions.js.map +1 -1
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js +6 -0
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/routes/admin-capabilities.d.ts +11 -0
- package/dist/src/routes/admin-capabilities.d.ts.map +1 -0
- package/dist/src/routes/admin-capabilities.js +54 -0
- package/dist/src/routes/admin-capabilities.js.map +1 -0
- package/dist/src/routes/capabilities.d.ts +11 -0
- package/dist/src/routes/capabilities.d.ts.map +1 -0
- package/dist/src/routes/capabilities.js +289 -0
- package/dist/src/routes/capabilities.js.map +1 -0
- package/dist/src/routes/cortex.d.ts.map +1 -1
- package/dist/src/routes/cortex.js +2 -0
- package/dist/src/routes/cortex.js.map +1 -1
- package/dist/src/routes/extensions.d.ts.map +1 -1
- package/dist/src/routes/extensions.js +9 -0
- package/dist/src/routes/extensions.js.map +1 -1
- package/dist/src/routes/lib-capabilities.d.ts +9 -0
- package/dist/src/routes/lib-capabilities.d.ts.map +1 -0
- package/dist/src/routes/lib-capabilities.js +190 -0
- package/dist/src/routes/lib-capabilities.js.map +1 -0
- package/dist/src/routes/libs.d.ts.map +1 -1
- package/dist/src/routes/libs.js +13 -0
- package/dist/src/routes/libs.js.map +1 -1
- package/dist/src/routes/verification.d.ts +10 -1
- package/dist/src/routes/verification.d.ts.map +1 -1
- package/dist/src/routes/verification.js +174 -24
- package/dist/src/routes/verification.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +35 -2
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/services/capability-aggregator.d.ts +8 -0
- package/dist/src/services/capability-aggregator.d.ts.map +1 -0
- package/dist/src/services/capability-aggregator.js +239 -0
- package/dist/src/services/capability-aggregator.js.map +1 -0
- package/dist/src/services/capability-invoke.d.ts +20 -0
- package/dist/src/services/capability-invoke.d.ts.map +1 -0
- package/dist/src/services/capability-invoke.js +95 -0
- package/dist/src/services/capability-invoke.js.map +1 -0
- package/dist/src/services/config-schema.d.ts.map +1 -1
- package/dist/src/services/config-schema.js +4 -0
- package/dist/src/services/config-schema.js.map +1 -1
- package/dist/src/services/core-jobs.d.ts.map +1 -1
- package/dist/src/services/core-jobs.js +16 -0
- package/dist/src/services/core-jobs.js.map +1 -1
- package/dist/src/services/did-document.d.ts +25 -0
- package/dist/src/services/did-document.d.ts.map +1 -0
- package/dist/src/services/did-document.js +31 -0
- package/dist/src/services/did-document.js.map +1 -0
- package/dist/src/services/eudiw.d.ts +11 -1
- package/dist/src/services/eudiw.d.ts.map +1 -1
- package/dist/src/services/eudiw.js +43 -48
- package/dist/src/services/eudiw.js.map +1 -1
- package/dist/src/services/extension-runtime.d.ts.map +1 -1
- package/dist/src/services/extension-runtime.js +8 -5
- package/dist/src/services/extension-runtime.js.map +1 -1
- package/dist/src/services/job-seeding.d.ts.map +1 -1
- package/dist/src/services/job-seeding.js +4 -0
- package/dist/src/services/job-seeding.js.map +1 -1
- package/dist/src/services/oidc-client.d.ts +32 -0
- package/dist/src/services/oidc-client.d.ts.map +1 -0
- package/dist/src/services/oidc-client.js +67 -0
- package/dist/src/services/oidc-client.js.map +1 -0
- package/dist/src/services/sd-jwt.d.ts +21 -0
- package/dist/src/services/sd-jwt.d.ts.map +1 -0
- package/dist/src/services/sd-jwt.js +87 -0
- package/dist/src/services/sd-jwt.js.map +1 -0
- package/dist/src/services/vc-issuer.d.ts +20 -1
- package/dist/src/services/vc-issuer.d.ts.map +1 -1
- package/dist/src/services/vc-issuer.js +75 -15
- package/dist/src/services/vc-issuer.js.map +1 -1
- package/dist/src/storage/interface.d.ts +118 -1
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.d.ts +37 -1
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +282 -0
- package/dist/src/storage/providers/mongodb/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/index.d.ts +37 -1
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +241 -0
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +77 -0
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/capability.repository.d.ts +35 -0
- package/dist/src/storage/repositories/capability.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/capability.repository.js +2 -0
- package/dist/src/storage/repositories/capability.repository.js.map +1 -0
- package/dist/src/storage/repositories/index.d.ts +1 -0
- package/dist/src/storage/repositories/index.d.ts.map +1 -1
- package/dist/src/storage/repositories/node.repository.d.ts +5 -1
- package/dist/src/storage/repositories/node.repository.d.ts.map +1 -1
- package/dist/static/app-catalog.html +156 -29
- package/package.json +6 -1
- package/prisma/schema.prisma +86 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file capabilities-tab.js
|
|
3
|
+
* @description Profile tab showing all node capabilities (auto-aggregated from extensions,
|
|
4
|
+
* cortex, actions) plus capability policy settings and optional manual creation.
|
|
5
|
+
* @version-history
|
|
6
|
+
* v1.0.0 — 2026-05-02 — Initial capability tab (CRUD form)
|
|
7
|
+
* v2.0.0 — 2026-05-02 — Redesigned: node capabilities listing, policy display, source filter
|
|
8
|
+
*/
|
|
9
|
+
import { h } from 'preact';
|
|
10
|
+
import { useState, useEffect, useCallback } from 'preact/hooks';
|
|
11
|
+
import htm from 'htm';
|
|
12
|
+
const html = htm.bind(h);
|
|
13
|
+
import { t } from '/js/i18n.js';
|
|
14
|
+
import { escHtml, copyToClipboard } from '/js/utils.js';
|
|
15
|
+
import { Spinner } from './shared.js';
|
|
16
|
+
import { getSession } from '/js/services/auth.js';
|
|
17
|
+
|
|
18
|
+
const SOURCE_ICONS = { extension: '\u{1F50C}', cortex: '\u{1F9E0}', action: '\u{1F6E0}️', manual: '✍️', app: '\u{1F4F1}' };
|
|
19
|
+
const POLICY_LABELS = {
|
|
20
|
+
publishing: { disabled: 'policyDisabled', self_only: 'policySelfOnly', moderated: 'policyModerated', open: 'policyOpen' },
|
|
21
|
+
publishers: { all_users: 'policyAllUsers', trusted_only: 'policyTrustedOnly', allowlist: 'policyAllowlist' },
|
|
22
|
+
webhooks: { disabled: 'policyDisabled', allowlist_only: 'policyAllowlistOnly', open: 'policyOpen' },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default function CapabilitiesTab() {
|
|
26
|
+
const [caps, setCaps] = useState([]);
|
|
27
|
+
const [policy, setPolicy] = useState(null);
|
|
28
|
+
const [loading, setLoading] = useState(true);
|
|
29
|
+
const [filter, setFilter] = useState('all');
|
|
30
|
+
const [search, setSearch] = useState('');
|
|
31
|
+
const [showCreate, setShowCreate] = useState(false);
|
|
32
|
+
const [newCap, setNewCap] = useState({ id: '', name: '', summary: '', callable: false, visibility: 'private', tags: '' });
|
|
33
|
+
const [detail, setDetail] = useState(null);
|
|
34
|
+
|
|
35
|
+
const loadCaps = useCallback(async () => {
|
|
36
|
+
const session = getSession();
|
|
37
|
+
if (!session) return;
|
|
38
|
+
try {
|
|
39
|
+
const res = await session.fetch('/v1/capabilities?per_page=100');
|
|
40
|
+
setCaps(res.data?.capabilities || []);
|
|
41
|
+
if (res.data?.policy) setPolicy(res.data.policy);
|
|
42
|
+
} catch { setCaps([]); }
|
|
43
|
+
setLoading(false);
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
useEffect(() => { loadCaps(); }, [loadCaps]);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const handler = () => loadCaps();
|
|
50
|
+
window.addEventListener('aimeat-live-update', handler);
|
|
51
|
+
return () => window.removeEventListener('aimeat-live-update', handler);
|
|
52
|
+
}, [loadCaps]);
|
|
53
|
+
|
|
54
|
+
const handleCreate = async () => {
|
|
55
|
+
const session = getSession();
|
|
56
|
+
if (!session) return;
|
|
57
|
+
try {
|
|
58
|
+
await session.fetch('/v1/capabilities', {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
body: JSON.stringify({
|
|
61
|
+
id: newCap.id || undefined,
|
|
62
|
+
name: newCap.name,
|
|
63
|
+
summary: newCap.summary,
|
|
64
|
+
callable: newCap.callable,
|
|
65
|
+
visibility: newCap.visibility,
|
|
66
|
+
source: { type: 'manual', ref: 'manual', version: '1.0.0' },
|
|
67
|
+
tags: newCap.tags ? newCap.tags.split(',').map(s => s.trim()).filter(Boolean) : [],
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
setShowCreate(false);
|
|
71
|
+
setNewCap({ id: '', name: '', summary: '', callable: false, visibility: 'private', tags: '' });
|
|
72
|
+
loadCaps();
|
|
73
|
+
} catch (e) {
|
|
74
|
+
alert(e.message || 'Failed to create');
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const handleDelete = async (id) => {
|
|
79
|
+
if (!confirm(t('capabilities.deleteConfirm'))) return;
|
|
80
|
+
const session = getSession();
|
|
81
|
+
if (!session) return;
|
|
82
|
+
try {
|
|
83
|
+
await session.fetch('/v1/capabilities/' + encodeURIComponent(id), { method: 'DELETE' });
|
|
84
|
+
loadCaps();
|
|
85
|
+
} catch (e) {
|
|
86
|
+
alert(e.message || 'Failed to delete');
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (loading) return html`<${Spinner} />`;
|
|
91
|
+
|
|
92
|
+
const filtered = caps.filter(c => {
|
|
93
|
+
if (filter !== 'all' && c.source?.type !== filter) return false;
|
|
94
|
+
if (search && !c.name.toLowerCase().includes(search.toLowerCase()) && !c.summary?.toLowerCase().includes(search.toLowerCase())) return false;
|
|
95
|
+
return true;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const sourceCounts = {};
|
|
99
|
+
for (const c of caps) {
|
|
100
|
+
const st = c.source?.type || 'manual';
|
|
101
|
+
sourceCounts[st] = (sourceCounts[st] || 0) + 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const canCreate = policy && policy.publishing !== 'disabled';
|
|
105
|
+
|
|
106
|
+
return html`
|
|
107
|
+
<div class="pf-section">
|
|
108
|
+
<div class="section-title">${t('capabilities.nodeCapabilities')}</div>
|
|
109
|
+
<div class="section-desc">${t('capabilities.nodeCapabilitiesDesc')}</div>
|
|
110
|
+
|
|
111
|
+
<!-- Filter bar -->
|
|
112
|
+
<div class="cap-filter-bar">
|
|
113
|
+
<input
|
|
114
|
+
type="text"
|
|
115
|
+
class="cap-search-input"
|
|
116
|
+
placeholder="${t('common.search') || 'Search...'}"
|
|
117
|
+
value=${search}
|
|
118
|
+
onInput=${e => setSearch(e.target.value)}
|
|
119
|
+
/>
|
|
120
|
+
<select class="cap-filter-select" value=${filter} onChange=${e => setFilter(e.target.value)}>
|
|
121
|
+
<option value="all">${t('capabilities.allTypes')} (${caps.length})</option>
|
|
122
|
+
${Object.entries(sourceCounts).map(([type, count]) => html`
|
|
123
|
+
<option value=${type}>${SOURCE_ICONS[type] || ''} ${t('capabilities.' + type) || type} (${count})</option>
|
|
124
|
+
`)}
|
|
125
|
+
</select>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<!-- Capability list -->
|
|
129
|
+
${!filtered.length ? html`<div class="pf-empty">${t('capabilities.noCapabilities')}</div>` : ''}
|
|
130
|
+
|
|
131
|
+
${filtered.map(c => {
|
|
132
|
+
const s = c.stats || {};
|
|
133
|
+
const srcType = c.source?.type || 'manual';
|
|
134
|
+
const icon = SOURCE_ICONS[srcType] || '';
|
|
135
|
+
const isSelected = detail?.id === c.id;
|
|
136
|
+
return html`
|
|
137
|
+
<div class="cap-item ${isSelected ? 'cap-item-selected' : ''}" onClick=${() => setDetail(isSelected ? null : c)}>
|
|
138
|
+
<div class="cap-item-header">
|
|
139
|
+
<div class="cap-item-name">
|
|
140
|
+
<span class="cap-source-icon">${icon}</span>
|
|
141
|
+
<strong>${escHtml(c.name)}</strong>
|
|
142
|
+
${c.callable ? html`<span class="cap-badge cap-badge-callable">${t('capabilities.callable')}</span>` : ''}
|
|
143
|
+
${srcType === 'cortex' ? html`<span class="cap-badge cap-badge-browser">${t('capabilities.browserOnly')}</span>` : ''}
|
|
144
|
+
${c.status !== 'active' ? html`<span class="cap-badge cap-badge-status">${c.status}</span>` : ''}
|
|
145
|
+
</div>
|
|
146
|
+
<div class="cap-item-stats">
|
|
147
|
+
${s.totalInvocations ? html`<span>${s.totalInvocations} ${t('capabilities.invocations').toLowerCase()}</span>` : ''}
|
|
148
|
+
${c.trust?.vouchCount ? html`<span>${c.trust.vouchCount} ${t('capabilities.vouchCount').toLowerCase()}</span>` : ''}
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
${c.summary ? html`<div class="cap-item-summary">${escHtml(c.summary)}</div>` : ''}
|
|
152
|
+
${(c.tags || []).length ? html`
|
|
153
|
+
<div class="cap-item-tags">
|
|
154
|
+
${c.tags.map(tag => html`<span class="cap-tag">${escHtml(tag)}</span>`)}
|
|
155
|
+
</div>
|
|
156
|
+
` : ''}
|
|
157
|
+
|
|
158
|
+
${isSelected ? html`
|
|
159
|
+
<div class="cap-detail" onClick=${e => e.stopPropagation()}>
|
|
160
|
+
<div class="cap-detail-row"><span class="cap-detail-label">ID:</span> <code class="cap-detail-code">${c.id}</code></div>
|
|
161
|
+
<div class="cap-detail-row"><span class="cap-detail-label">${t('capabilities.source')}:</span> ${srcType} ${c.source?.ref ? html` — <code class="cap-detail-code">${c.source.ref}</code>` : ''}</div>
|
|
162
|
+
<div class="cap-detail-row"><span class="cap-detail-label">${t('capabilities.visibility')}:</span> ${c.visibility === 'public' ? '\u{1F310} Public' : '\u{1F512} Private'}</div>
|
|
163
|
+
<div class="cap-detail-row"><span class="cap-detail-label">${t('capabilities.authRequired')}:</span> ${c.authRequired || 'registered'}</div>
|
|
164
|
+
${c.usage ? html`<div class="cap-detail-row"><span class="cap-detail-label">Usage:</span> <code class="cap-detail-code">${c.usage}</code></div>` : ''}
|
|
165
|
+
${c.whenToUse ? html`<div class="cap-detail-row"><span class="cap-detail-label">When to use:</span> ${escHtml(c.whenToUse)}</div>` : ''}
|
|
166
|
+
${s.totalInvocations ? html`
|
|
167
|
+
<div class="cap-detail-row">
|
|
168
|
+
<span class="cap-detail-label">Stats:</span>
|
|
169
|
+
${s.totalInvocations} calls, ${s.successCount} ok, ${s.errorCount} errors
|
|
170
|
+
${s.avgResponseMs ? html` — avg ${Math.round(s.avgResponseMs)}ms` : ''}
|
|
171
|
+
</div>
|
|
172
|
+
` : ''}
|
|
173
|
+
${c.inputSchema && Object.keys(c.inputSchema).length > 1 ? html`
|
|
174
|
+
<details class="cap-schema-details">
|
|
175
|
+
<summary>Input Schema</summary>
|
|
176
|
+
<pre class="cap-schema-pre">${JSON.stringify(c.inputSchema, null, 2)}</pre>
|
|
177
|
+
</details>
|
|
178
|
+
` : ''}
|
|
179
|
+
${c.outputSchema && Object.keys(c.outputSchema).length > 1 ? html`
|
|
180
|
+
<details class="cap-schema-details">
|
|
181
|
+
<summary>Output Schema</summary>
|
|
182
|
+
<pre class="cap-schema-pre">${JSON.stringify(c.outputSchema, null, 2)}</pre>
|
|
183
|
+
</details>
|
|
184
|
+
` : ''}
|
|
185
|
+
${c.source?.type === 'manual' ? html`
|
|
186
|
+
<button class="btn-danger" style="margin-top:8px;font-size:.8rem" onClick=${() => handleDelete(c.id)}>
|
|
187
|
+
${t('capabilities.delete')}
|
|
188
|
+
</button>
|
|
189
|
+
` : ''}
|
|
190
|
+
</div>
|
|
191
|
+
` : ''}
|
|
192
|
+
</div>
|
|
193
|
+
`;
|
|
194
|
+
})}
|
|
195
|
+
|
|
196
|
+
<!-- How capabilities are created -->
|
|
197
|
+
<div class="cap-info-section">
|
|
198
|
+
<div class="cap-info-title">${t('capabilities.howCreated')}</div>
|
|
199
|
+
<div class="cap-info-desc">${t('capabilities.howCreatedDesc')}</div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<!-- Policy settings -->
|
|
203
|
+
${policy ? html`
|
|
204
|
+
<div class="cap-info-section">
|
|
205
|
+
<div class="cap-info-title">${t('capabilities.policyTitle')}</div>
|
|
206
|
+
<div class="cap-policy-grid">
|
|
207
|
+
<div class="cap-policy-item">
|
|
208
|
+
<span class="cap-policy-label">${t('capabilities.policyPublishing')}</span>
|
|
209
|
+
<span class="cap-policy-value">${t('capabilities.' + (POLICY_LABELS.publishing[policy.publishing] || 'policyDisabled'))}</span>
|
|
210
|
+
</div>
|
|
211
|
+
<div class="cap-policy-item">
|
|
212
|
+
<span class="cap-policy-label">${t('capabilities.policyPublishers')}</span>
|
|
213
|
+
<span class="cap-policy-value">${t('capabilities.' + (POLICY_LABELS.publishers[policy.publishers] || 'policyAllUsers'))}</span>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="cap-policy-item">
|
|
216
|
+
<span class="cap-policy-label">${t('capabilities.policyWebhooks')}</span>
|
|
217
|
+
<span class="cap-policy-value">${t('capabilities.' + (POLICY_LABELS.webhooks[policy.webhooks] || 'policyDisabled'))}</span>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
` : ''}
|
|
222
|
+
|
|
223
|
+
<!-- Create manual capability (if allowed) -->
|
|
224
|
+
${canCreate ? html`
|
|
225
|
+
<div class="cap-create-section">
|
|
226
|
+
<button class="btn-outline" onClick=${() => setShowCreate(!showCreate)}>
|
|
227
|
+
${showCreate ? t('common.cancel') : t('capabilities.create')}
|
|
228
|
+
</button>
|
|
229
|
+
${showCreate ? html`
|
|
230
|
+
<div class="cap-create-form">
|
|
231
|
+
<input class="cap-form-input" placeholder="ID (optional)" value=${newCap.id} onInput=${e => setNewCap({ ...newCap, id: e.target.value })} />
|
|
232
|
+
<input class="cap-form-input" placeholder=${t('capabilities.name')} value=${newCap.name} onInput=${e => setNewCap({ ...newCap, name: e.target.value })} />
|
|
233
|
+
<textarea class="cap-form-input" placeholder=${t('capabilities.summary')} value=${newCap.summary} onInput=${e => setNewCap({ ...newCap, summary: e.target.value })} rows="2" />
|
|
234
|
+
<input class="cap-form-input" placeholder="Tags (comma-separated)" value=${newCap.tags} onInput=${e => setNewCap({ ...newCap, tags: e.target.value })} />
|
|
235
|
+
<label class="cap-form-checkbox">
|
|
236
|
+
<input type="checkbox" checked=${newCap.callable} onChange=${e => setNewCap({ ...newCap, callable: e.target.checked })} />
|
|
237
|
+
${t('capabilities.callable')}
|
|
238
|
+
</label>
|
|
239
|
+
<select class="cap-form-input" value=${newCap.visibility} onChange=${e => setNewCap({ ...newCap, visibility: e.target.value })}>
|
|
240
|
+
<option value="private">Private</option>
|
|
241
|
+
<option value="public">Public</option>
|
|
242
|
+
</select>
|
|
243
|
+
<button class="btn-primary" onClick=${handleCreate} disabled=${!newCap.name}>
|
|
244
|
+
${t('capabilities.create')}
|
|
245
|
+
</button>
|
|
246
|
+
</div>
|
|
247
|
+
` : ''}
|
|
248
|
+
</div>
|
|
249
|
+
` : ''}
|
|
250
|
+
</div>
|
|
251
|
+
`;
|
|
252
|
+
}
|