aimeat 1.16.2 → 1.18.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 +6 -1
- package/dist/locales/en.json +166 -4
- package/dist/locales/fi.json +166 -4
- package/dist/public/components/Card.js +17 -3
- package/dist/public/components/Collapsible.js +39 -0
- package/dist/public/components/CopyButton.js +55 -24
- package/dist/public/components/DataTable.js +57 -0
- package/dist/public/components/EmptyState.js +28 -0
- package/dist/public/components/KeyValueRow.js +37 -0
- package/dist/public/components/Modal.js +22 -5
- package/dist/public/components/Pagination.js +70 -0
- package/dist/public/components/SearchBar.js +44 -0
- package/dist/public/components/StatusDot.js +29 -0
- package/dist/public/components/TagList.js +53 -0
- package/dist/public/components/ToggleSwitch.js +28 -0
- package/dist/public/components/index.js +41 -1
- package/dist/public/css/components/tags.css +24 -0
- package/dist/public/css/style-testbed.css +135 -0
- package/dist/public/css/theme.css +556 -34
- package/dist/public/css/views/admin-agent-integration.css +2 -33
- package/dist/public/css/views/admin.css +730 -686
- package/dist/public/css/views/agents-detail.css +402 -14
- package/dist/public/css/views/help.css +2 -1
- package/dist/public/css/views/hobbies.css +53 -33
- package/dist/public/css/views/marketplace.css +73 -60
- package/dist/public/css/views/openclaw.css +12 -6
- package/dist/public/css/views/portal-classic.css +34 -26
- package/dist/public/css/views/portal-dev.css +41 -32
- package/dist/public/css/views/portal.css +14 -18
- package/dist/public/css/views/profile.css +252 -164
- package/dist/public/css/views/public-knowledge-viewer.css +18 -6
- package/dist/public/css/views/scheduler.css +100 -0
- package/dist/public/js/format.js +41 -0
- package/dist/public/js/services/agent-tasks.js +14 -1
- package/dist/public/js/services/generator-spec-validate.js +5 -1
- package/dist/public/js/services/schedules.js +53 -0
- package/dist/public/js/style-testbed.js +170 -0
- package/dist/public/js/theme.js +114 -0
- package/dist/public/spa.html +74 -16
- package/dist/public/style-testbed.html +420 -0
- package/dist/public/views/admin/agent-integration-tab.js +23 -38
- package/dist/public/views/admin/agent-tasks-tab.js +14 -21
- package/dist/public/views/admin/chat-instances-tab.js +1 -1
- package/dist/public/views/admin/config-tab.js +7 -2
- package/dist/public/views/admin/cortex-tab.js +20 -14
- package/dist/public/views/admin/csm-tab.js +22 -18
- package/dist/public/views/admin/directory-tab.js +3 -3
- package/dist/public/views/admin/email-tab.js +23 -28
- package/dist/public/views/admin/federation-tab.js +15 -5
- package/dist/public/views/admin/generator-debug-tab.js +10 -8
- package/dist/public/views/admin/genesis-tab.js +4 -4
- package/dist/public/views/admin/ghii-tab.js +3 -3
- package/dist/public/views/admin/knowledge-tab.js +15 -5
- package/dist/public/views/admin/matching-tab.js +3 -3
- package/dist/public/views/admin/memory-tab.js +16 -20
- package/dist/public/views/admin/msm-tab.js +30 -17
- package/dist/public/views/admin/overview-tab.js +7 -7
- package/dist/public/views/admin/packages-tab.js +10 -9
- package/dist/public/views/admin/portal-tab.js +14 -4
- package/dist/public/views/admin/prompts-tab.js +5 -5
- package/dist/public/views/admin/push-tab.js +15 -4
- package/dist/public/views/admin/realtime-tab.js +3 -3
- package/dist/public/views/admin/services-tab.js +18 -27
- package/dist/public/views/admin/shared.js +48 -58
- package/dist/public/views/admin/sharing-groups-tab.js +5 -10
- package/dist/public/views/admin/stats-tab.js +3 -3
- package/dist/public/views/guides.js +9 -13
- package/dist/public/views/help.js +3 -24
- package/dist/public/views/hobbies.js +47 -41
- package/dist/public/views/marketplace.js +42 -34
- package/dist/public/views/openclaw.js +0 -1
- package/dist/public/views/portal-classic.js +4 -15
- package/dist/public/views/portal-dev.js +28 -30
- package/dist/public/views/portal.js +16 -21
- package/dist/public/views/portfolio.js +11 -12
- package/dist/public/views/profile/access-tab.js +250 -1
- package/dist/public/views/profile/agents/agent-card.js +75 -4
- package/dist/public/views/profile/agents/tab-activity.js +15 -13
- package/dist/public/views/profile/agents/tab-agent-config.js +60 -6
- package/dist/public/views/profile/agents/tab-integration.js +37 -60
- package/dist/public/views/profile/agents/tab-messages.js +10 -1
- package/dist/public/views/profile/agents/tab-quality.js +25 -19
- package/dist/public/views/profile/agents/tab-schedules.js +110 -0
- package/dist/public/views/profile/agents-activity-subtab.js +4 -4
- package/dist/public/views/profile/agents-services-subtab.js +5 -4
- package/dist/public/views/profile/agents-tab.js +141 -37
- package/dist/public/views/profile/agents-tasks-subtab.js +221 -26
- package/dist/public/views/profile/calibrator-batch.js +5 -11
- package/dist/public/views/profile/calibrator-tab.js +4 -9
- package/dist/public/views/profile/chat-sessions-tab.js +9 -5
- package/dist/public/views/profile/email-tab.js +2 -2
- package/dist/public/views/profile/extensions-tab.js +27 -22
- package/dist/public/views/profile/federation-tab.js +4 -1
- package/dist/public/views/profile/foundry-detail.js +22 -23
- package/dist/public/views/profile/foundry-tab.js +12 -13
- package/dist/public/views/profile/generator-dashboard/DebugPanel.js +4 -7
- package/dist/public/views/profile/generator-dashboard/use-dashboard-core.js +4 -1
- package/dist/public/views/profile/generator-dashboard/use-edit-mode.js +3 -2
- package/dist/public/views/profile/generator-detail.js +26 -30
- package/dist/public/views/profile/generator-tab.js +5 -5
- package/dist/public/views/profile/knowledge-tab.js +12 -12
- package/dist/public/views/profile/landing-page.js +100 -149
- package/dist/public/views/profile/memory-tab.js +15 -11
- package/dist/public/views/profile/node-stats-tab.js +3 -3
- package/dist/public/views/profile/nodes-tab.js +10 -3
- package/dist/public/views/profile/notifications-tab.js +3 -3
- package/dist/public/views/profile/packages-tab.js +14 -14
- package/dist/public/views/profile/scheduler-tab.js +340 -0
- package/dist/public/views/profile/services-tab.js +1 -1
- package/dist/public/views/profile/shared.js +16 -12
- package/dist/public/views/profile/wallet-tab.js +27 -50
- package/dist/public/views/profile/work-tab.js +29 -31
- package/dist/public/views/profile.js +24 -0
- package/dist/public/views/public-knowledge-viewer.js +9 -18
- package/dist/src/auth/middleware.d.ts +3 -2
- package/dist/src/auth/middleware.d.ts.map +1 -1
- package/dist/src/auth/middleware.js +78 -3
- package/dist/src/auth/middleware.js.map +1 -1
- package/dist/src/cli/init-wizard.d.ts.map +1 -1
- package/dist/src/cli/init-wizard.js +13 -1
- package/dist/src/cli/init-wizard.js.map +1 -1
- package/dist/src/config.d.ts +6 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +12 -1
- package/dist/src/config.js.map +1 -1
- package/dist/src/generated/api-types.d.ts +708 -8
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/mcp/agent-schedules.d.ts +23 -0
- package/dist/src/mcp/agent-schedules.d.ts.map +1 -0
- package/dist/src/mcp/agent-schedules.js +175 -0
- package/dist/src/mcp/agent-schedules.js.map +1 -0
- package/dist/src/mcp/annotations.d.ts.map +1 -1
- package/dist/src/mcp/annotations.js +6 -0
- package/dist/src/mcp/annotations.js.map +1 -1
- package/dist/src/mcp/catalog/definitions.d.ts.map +1 -1
- package/dist/src/mcp/catalog/definitions.js +55 -0
- package/dist/src/mcp/catalog/definitions.js.map +1 -1
- package/dist/src/mcp/catalog/scopes.d.ts.map +1 -1
- package/dist/src/mcp/catalog/scopes.js +12 -0
- package/dist/src/mcp/catalog/scopes.js.map +1 -1
- package/dist/src/mcp/catalog/surfaces.d.ts.map +1 -1
- package/dist/src/mcp/catalog/surfaces.js +2 -0
- package/dist/src/mcp/catalog/surfaces.js.map +1 -1
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js +2 -0
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/models/agent-task-schemas.d.ts +6 -0
- package/dist/src/models/agent-task-schemas.d.ts.map +1 -1
- package/dist/src/models/agent-task-schemas.js +6 -0
- package/dist/src/models/agent-task-schemas.js.map +1 -1
- package/dist/src/models/schemas.d.ts +1 -0
- package/dist/src/models/schemas.d.ts.map +1 -1
- package/dist/src/models/schemas.js +4 -0
- package/dist/src/models/schemas.js.map +1 -1
- package/dist/src/routes/access-tokens.d.ts +17 -0
- package/dist/src/routes/access-tokens.d.ts.map +1 -0
- package/dist/src/routes/access-tokens.js +164 -0
- package/dist/src/routes/access-tokens.js.map +1 -0
- package/dist/src/routes/agent-tasks.d.ts +2 -0
- package/dist/src/routes/agent-tasks.d.ts.map +1 -1
- package/dist/src/routes/agent-tasks.js +118 -23
- package/dist/src/routes/agent-tasks.js.map +1 -1
- package/dist/src/routes/agents.d.ts.map +1 -1
- package/dist/src/routes/agents.js +46 -0
- package/dist/src/routes/agents.js.map +1 -1
- package/dist/src/routes/ai.d.ts +6 -9
- package/dist/src/routes/ai.d.ts.map +1 -1
- package/dist/src/routes/ai.js +26 -175
- package/dist/src/routes/ai.js.map +1 -1
- package/dist/src/routes/apps.d.ts.map +1 -1
- package/dist/src/routes/apps.js +104 -34
- package/dist/src/routes/apps.js.map +1 -1
- package/dist/src/routes/auth.d.ts.map +1 -1
- package/dist/src/routes/auth.js +77 -4
- package/dist/src/routes/auth.js.map +1 -1
- package/dist/src/routes/cortex.d.ts +2 -1
- package/dist/src/routes/cortex.d.ts.map +1 -1
- package/dist/src/routes/cortex.js +13 -8
- 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 +85 -51
- package/dist/src/routes/extensions.js.map +1 -1
- package/dist/src/routes/generator.d.ts.map +1 -1
- package/dist/src/routes/generator.js +109 -43
- package/dist/src/routes/generator.js.map +1 -1
- package/dist/src/routes/ghii.d.ts.map +1 -1
- package/dist/src/routes/ghii.js +34 -13
- package/dist/src/routes/ghii.js.map +1 -1
- package/dist/src/routes/libs.d.ts +15 -0
- package/dist/src/routes/libs.d.ts.map +1 -1
- package/dist/src/routes/libs.js +178 -55
- package/dist/src/routes/libs.js.map +1 -1
- package/dist/src/routes/portal.d.ts.map +1 -1
- package/dist/src/routes/portal.js +1 -0
- package/dist/src/routes/portal.js.map +1 -1
- package/dist/src/routes/schedules.d.ts +33 -0
- package/dist/src/routes/schedules.d.ts.map +1 -0
- package/dist/src/routes/schedules.js +380 -0
- package/dist/src/routes/schedules.js.map +1 -0
- package/dist/src/server-bootstrap/config-init.js +2 -2
- package/dist/src/server-bootstrap/config-init.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +7 -0
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/server-bootstrap/service-init.d.ts.map +1 -1
- package/dist/src/server-bootstrap/service-init.js +2 -1
- package/dist/src/server-bootstrap/service-init.js.map +1 -1
- package/dist/src/services/access-token.d.ts +28 -0
- package/dist/src/services/access-token.d.ts.map +1 -0
- package/dist/src/services/access-token.js +37 -0
- package/dist/src/services/access-token.js.map +1 -0
- package/dist/src/services/ai-completion.d.ts +83 -0
- package/dist/src/services/ai-completion.d.ts.map +1 -0
- package/dist/src/services/ai-completion.js +187 -0
- package/dist/src/services/ai-completion.js.map +1 -0
- package/dist/src/services/config-schema.d.ts.map +1 -1
- package/dist/src/services/config-schema.js +3 -0
- package/dist/src/services/config-schema.js.map +1 -1
- package/dist/src/services/generator-prompt-seeds.d.ts.map +1 -1
- package/dist/src/services/generator-prompt-seeds.js +6 -3
- package/dist/src/services/generator-prompt-seeds.js.map +1 -1
- package/dist/src/services/generator-prompts/spec-validate.d.ts.map +1 -1
- package/dist/src/services/generator-prompts/spec-validate.js +6 -2
- package/dist/src/services/generator-prompts/spec-validate.js.map +1 -1
- package/dist/src/services/generator-registration.d.ts +12 -4
- package/dist/src/services/generator-registration.d.ts.map +1 -1
- package/dist/src/services/generator-registration.js +71 -0
- package/dist/src/services/generator-registration.js.map +1 -1
- package/dist/src/services/generator-validate.d.ts +10 -0
- package/dist/src/services/generator-validate.d.ts.map +1 -1
- package/dist/src/services/generator-validate.js +180 -36
- package/dist/src/services/generator-validate.js.map +1 -1
- package/dist/src/services/owner-session.d.ts +43 -0
- package/dist/src/services/owner-session.d.ts.map +1 -0
- package/dist/src/services/owner-session.js +175 -0
- package/dist/src/services/owner-session.js.map +1 -0
- package/dist/src/services/schedule-constraints.d.ts +65 -0
- package/dist/src/services/schedule-constraints.d.ts.map +1 -0
- package/dist/src/services/schedule-constraints.js +106 -0
- package/dist/src/services/schedule-constraints.js.map +1 -0
- package/dist/src/services/scheduler.d.ts +34 -0
- package/dist/src/services/scheduler.d.ts.map +1 -1
- package/dist/src/services/scheduler.js +266 -22
- package/dist/src/services/scheduler.js.map +1 -1
- package/dist/src/services/skill-bundle/generator.js +19 -0
- package/dist/src/services/skill-bundle/generator.js.map +1 -1
- package/dist/src/storage/interface.d.ts +48 -4
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/interface.js.map +1 -1
- package/dist/src/storage/providers/mongodb/index.d.ts +31 -0
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +187 -9
- package/dist/src/storage/providers/mongodb/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/index.d.ts +31 -0
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +131 -8
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/agent-task.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/agent-task.js +6 -4
- package/dist/src/storage/providers/sqlite/repos/agent-task.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +79 -8
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/pat.repository.d.ts +40 -0
- package/dist/src/storage/repositories/pat.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/pat.repository.js +14 -0
- package/dist/src/storage/repositories/pat.repository.js.map +1 -0
- package/dist/src/storage/repositories/scheduler.repository.d.ts +2 -0
- package/dist/src/storage/repositories/scheduler.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories/session.repository.d.ts +51 -0
- package/dist/src/storage/repositories/session.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories/session.repository.js +12 -0
- package/dist/src/storage/repositories/session.repository.js.map +1 -1
- package/dist/src/utils/env-config.d.ts.map +1 -1
- package/dist/src/utils/env-config.js +25 -1
- package/dist/src/utils/env-config.js.map +1 -1
- package/dist/src/utils/env-validator.d.ts.map +1 -1
- package/dist/src/utils/env-validator.js +4 -0
- package/dist/src/utils/env-validator.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +48 -0
|
@@ -6,16 +6,67 @@
|
|
|
6
6
|
* @version-history
|
|
7
7
|
* v1.0.0 -- 2026-05-24 -- Initial creation for Agent Detail Tab-View
|
|
8
8
|
* v1.1.0 -- 2026-05-24 -- Add edit/copy/download buttons (F8), edit mode (F9), upload (F10), file metadata (F11)
|
|
9
|
+
* v1.2.0 -- 2026-06-02 -- Component unification: route handleCopy through the shared
|
|
10
|
+
* copyToClipboard (/js/utils.js) instead of raw navigator.clipboard.writeText, so the
|
|
11
|
+
* insecure-context fallback applies; toast preserved (TIER 3, stays a handler)
|
|
9
12
|
*/
|
|
10
13
|
|
|
11
14
|
import { h } from 'preact';
|
|
12
15
|
import { useState, useEffect, useRef } from 'preact/hooks';
|
|
13
16
|
import htm from 'htm';
|
|
14
17
|
import { t } from '/js/i18n.js';
|
|
15
|
-
import {
|
|
18
|
+
import { copyToClipboard } from '/js/utils.js';
|
|
19
|
+
import { apiGet, apiPut, apiPatch } from '/js/api.js';
|
|
16
20
|
|
|
17
21
|
const html = htm.bind(h);
|
|
18
22
|
|
|
23
|
+
/** Opt-in budget guards applied as defaults to schedules created for this agent. */
|
|
24
|
+
function ScheduleBudgetSection({ agent, agentName, showToast }) {
|
|
25
|
+
const defaults = Array.isArray(agent?.schedule_constraint_defaults) ? agent.schedule_constraint_defaults : [];
|
|
26
|
+
const findC = (type) => defaults.find(c => c.type === type);
|
|
27
|
+
const [maxRuns, setMaxRuns] = useState({ enabled: !!findC('max_runs')?.enabled, limit: findC('max_runs')?.params?.limit ?? 7 });
|
|
28
|
+
const [dailyLimit, setDailyLimit] = useState({ enabled: !!findC('daily_limit')?.enabled, limit: agent?.daily_spend_limit ?? findC('daily_limit')?.params?.limit ?? 1 });
|
|
29
|
+
const [saving, setSaving] = useState(false);
|
|
30
|
+
|
|
31
|
+
const save = async () => {
|
|
32
|
+
const constraints = [];
|
|
33
|
+
if (maxRuns.enabled) constraints.push({ type: 'max_runs', enabled: true, params: { limit: Number(maxRuns.limit) } });
|
|
34
|
+
if (dailyLimit.enabled) constraints.push({ type: 'daily_limit', enabled: true, params: { limit: Number(dailyLimit.limit) } });
|
|
35
|
+
setSaving(true);
|
|
36
|
+
try {
|
|
37
|
+
await apiPatch(`/v1/agents/${encodeURIComponent(agentName)}/schedule-constraints`, {
|
|
38
|
+
constraints,
|
|
39
|
+
daily_spend_limit: dailyLimit.enabled ? Number(dailyLimit.limit) : null,
|
|
40
|
+
});
|
|
41
|
+
showToast?.(t('profile.scheduler.budgetSaved'));
|
|
42
|
+
} catch (e) { showToast?.(e.message, true); }
|
|
43
|
+
finally { setSaving(false); }
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return html`
|
|
47
|
+
<div class="sch-form sch-budget-section">
|
|
48
|
+
<div class="section-title">${t('profile.scheduler.budgetTitle')}</div>
|
|
49
|
+
<div class="section-desc">${t('profile.scheduler.budgetDesc')}</div>
|
|
50
|
+
<div class="sch-constraints">
|
|
51
|
+
<label class="sch-check">
|
|
52
|
+
<input type="checkbox" checked=${maxRuns.enabled} onChange=${e => setMaxRuns(s => ({ ...s, enabled: e.target.checked }))} />
|
|
53
|
+
${t('profile.scheduler.maxRuns')}
|
|
54
|
+
<input type="number" min="1" value=${maxRuns.limit} disabled=${!maxRuns.enabled}
|
|
55
|
+
onInput=${e => setMaxRuns(s => ({ ...s, limit: e.target.value }))} class="sch-num" />
|
|
56
|
+
</label>
|
|
57
|
+
<label class="sch-check">
|
|
58
|
+
<input type="checkbox" checked=${dailyLimit.enabled} onChange=${e => setDailyLimit(s => ({ ...s, enabled: e.target.checked }))} />
|
|
59
|
+
${t('profile.scheduler.dailyLimit')}
|
|
60
|
+
<input type="number" min="0" step="0.1" value=${dailyLimit.limit} disabled=${!dailyLimit.enabled}
|
|
61
|
+
onInput=${e => setDailyLimit(s => ({ ...s, limit: e.target.value }))} class="sch-num" />
|
|
62
|
+
</label>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="sch-form-actions">
|
|
65
|
+
<button class="btn-primary btn-sm" disabled=${saving} onClick=${save}>${saving ? t('profile.scheduler.saving') : t('profile.scheduler.budgetSave')}</button>
|
|
66
|
+
</div>
|
|
67
|
+
</div>`;
|
|
68
|
+
}
|
|
69
|
+
|
|
19
70
|
export default function TabAgentConfig({ agent, agentName, session, showToast }) {
|
|
20
71
|
const [files, setFiles] = useState([]);
|
|
21
72
|
const [selectedFile, setSelectedFile] = useState(null);
|
|
@@ -25,8 +76,8 @@ export default function TabAgentConfig({ agent, agentName, session, showToast })
|
|
|
25
76
|
const [editContent, setEditContent] = useState('');
|
|
26
77
|
const fileInputRef = useRef(null);
|
|
27
78
|
|
|
28
|
-
async function loadFiles() {
|
|
29
|
-
setLoading(true);
|
|
79
|
+
async function loadFiles({ showSpinner = true } = {}) {
|
|
80
|
+
if (showSpinner) setLoading(true);
|
|
30
81
|
try {
|
|
31
82
|
const gaii = agent?.gaii || agentName;
|
|
32
83
|
const resp = await apiGet(`/v1/memory?prefix=agents.config.&agent=${encodeURIComponent(gaii)}`);
|
|
@@ -46,7 +97,7 @@ export default function TabAgentConfig({ agent, agentName, session, showToast })
|
|
|
46
97
|
setPreview(configFiles[0].content);
|
|
47
98
|
}
|
|
48
99
|
} catch {
|
|
49
|
-
setFiles([]);
|
|
100
|
+
if (showSpinner) setFiles([]); // keep old files on a transient live-update refetch
|
|
50
101
|
}
|
|
51
102
|
setLoading(false);
|
|
52
103
|
}
|
|
@@ -56,7 +107,8 @@ export default function TabAgentConfig({ agent, agentName, session, showToast })
|
|
|
56
107
|
const loadRef = useRef(loadFiles);
|
|
57
108
|
loadRef.current = loadFiles;
|
|
58
109
|
useEffect(() => {
|
|
59
|
-
|
|
110
|
+
// Silent on live-update so the tab doesn't flash blank; first mount shows the spinner.
|
|
111
|
+
const handler = () => loadRef.current({ showSpinner: false });
|
|
60
112
|
window.addEventListener('aimeat-live-update', handler);
|
|
61
113
|
return () => window.removeEventListener('aimeat-live-update', handler);
|
|
62
114
|
}, []);
|
|
@@ -90,7 +142,7 @@ export default function TabAgentConfig({ agent, agentName, session, showToast })
|
|
|
90
142
|
}
|
|
91
143
|
|
|
92
144
|
function handleCopy() {
|
|
93
|
-
|
|
145
|
+
copyToClipboard(preview).then(() => {
|
|
94
146
|
showToast(t('profile.agents.detail.agent_config.copy'));
|
|
95
147
|
});
|
|
96
148
|
}
|
|
@@ -139,6 +191,7 @@ export default function TabAgentConfig({ agent, agentName, session, showToast })
|
|
|
139
191
|
if (files.length === 0) {
|
|
140
192
|
return html`
|
|
141
193
|
<div>
|
|
194
|
+
<${ScheduleBudgetSection} agent=${agent} agentName=${agentName} showToast=${showToast} />
|
|
142
195
|
<div class="pf-agd-config-upload">
|
|
143
196
|
<button class="btn-outline btn-sm" onClick=${handleUploadClick}>+ ${t('profile.agents.detail.agent_config.upload')}</button>
|
|
144
197
|
<input ref=${fileInputRef} type="file" accept=".md,.yaml,.yml,.json" class="pf-agd-hidden-input" onChange=${handleFileUpload} />
|
|
@@ -150,6 +203,7 @@ export default function TabAgentConfig({ agent, agentName, session, showToast })
|
|
|
150
203
|
|
|
151
204
|
return html`
|
|
152
205
|
<div>
|
|
206
|
+
<${ScheduleBudgetSection} agent=${agent} agentName=${agentName} showToast=${showToast} />
|
|
153
207
|
<div class="pf-agd-config-upload">
|
|
154
208
|
<button class="btn-outline btn-sm" onClick=${handleUploadClick}>+ ${t('profile.agents.detail.agent_config.upload')}</button>
|
|
155
209
|
<input ref=${fileInputRef} type="file" accept=".md,.yaml,.yml,.json" class="pf-agd-hidden-input" onChange=${handleFileUpload} />
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
* during onboarding or production status (connection, platform, readiness,
|
|
5
5
|
* identity, delivery log) after completion.
|
|
6
6
|
* @version-history
|
|
7
|
+
* v1.6.0 -- 2026-06-02 -- Component unification (#1): the 3 copy buttons (agent prompt,
|
|
8
|
+
* install command, curl) now use the canonical <CopyButton> with precomputed text
|
|
9
|
+
* payloads -- dropped 3 copied-state useState hooks, 3 hand-rolled handlers, the
|
|
10
|
+
* copied-flag params threaded through both render functions, and the local
|
|
11
|
+
* copyToClipboard import.
|
|
7
12
|
* v1.5.0 -- 2026-05-31 -- Live-update refresh no longer toggles the full-tab loading
|
|
8
13
|
* placeholder (added a showSpinner option). During Hello Integration the agent
|
|
9
14
|
* posts steps/telemetry rapidly, so the frequent SSE ticks were re-rendering the
|
|
@@ -22,7 +27,8 @@ import { h } from 'preact';
|
|
|
22
27
|
import { useState, useEffect, useRef } from 'preact/hooks';
|
|
23
28
|
import htm from 'htm';
|
|
24
29
|
import { t } from '/js/i18n.js';
|
|
25
|
-
import { timeAgo
|
|
30
|
+
import { timeAgo } from '/js/utils.js';
|
|
31
|
+
import { CopyButton } from '/components/CopyButton.js';
|
|
26
32
|
import { detectAgentState } from './state-detector.js';
|
|
27
33
|
import {
|
|
28
34
|
getOnboarding, startOnboarding,
|
|
@@ -42,15 +48,12 @@ export default function TabIntegration({ agent, onboarding, session, showToast,
|
|
|
42
48
|
const [loading, setLoading] = useState(true);
|
|
43
49
|
const [testing, setTesting] = useState(false);
|
|
44
50
|
const [rerunning, setRerunning] = useState(false);
|
|
45
|
-
const [copiedCmd, setCopiedCmd] = useState(false);
|
|
46
|
-
const [copiedInstall, setCopiedInstall] = useState(false);
|
|
47
51
|
const [showAllDeliveries, setShowAllDeliveries] = useState(false);
|
|
48
52
|
const [allDeliveries, setAllDeliveries] = useState(null);
|
|
49
53
|
const [editingWebhook, setEditingWebhook] = useState(false);
|
|
50
54
|
const [webhookDraft, setWebhookDraft] = useState('');
|
|
51
55
|
const [savingWebhook, setSavingWebhook] = useState(false);
|
|
52
56
|
const [updatingBundle, setUpdatingBundle] = useState(false);
|
|
53
|
-
const [copiedPrompt, setCopiedPrompt] = useState(false);
|
|
54
57
|
|
|
55
58
|
async function loadData({ showSpinner = true } = {}) {
|
|
56
59
|
if (showSpinner) setLoading(true);
|
|
@@ -113,44 +116,23 @@ export default function TabIntegration({ agent, onboarding, session, showToast,
|
|
|
113
116
|
setRerunning(false);
|
|
114
117
|
}
|
|
115
118
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
function handleCopyInstallCommand(e) {
|
|
126
|
-
e.stopPropagation();
|
|
127
|
-
const platform = onboarding?.platformName || onboarding?.detectedPlatform || '';
|
|
128
|
-
const url = `${location.origin}${getSkillBundleUrl(agentName)}`;
|
|
129
|
-
let cmd;
|
|
130
|
-
if (platform.toLowerCase().includes('hermes') || platform.toLowerCase().includes('openclaw')) {
|
|
131
|
-
cmd = `hermes skills install ${url}`;
|
|
132
|
-
} else {
|
|
133
|
-
cmd = `curl -o skill-bundle.zip ${url} && unzip skill-bundle.zip`;
|
|
134
|
-
}
|
|
135
|
-
copyToClipboard(cmd).then(() => {
|
|
136
|
-
setCopiedInstall(true);
|
|
137
|
-
setTimeout(() => setCopiedInstall(false), 2000);
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function handleCopyAgentPrompt(e) {
|
|
142
|
-
e.stopPropagation();
|
|
143
|
-
const url = `${location.origin}${getSkillBundleUrl(agentName)}`;
|
|
144
|
-
const prompt = `Download and install your skill bundle from AIMEAT.
|
|
119
|
+
// Copy-button payloads (the canonical <CopyButton> owns the copy + copied-state).
|
|
120
|
+
const skillBundleUrl = `${location.origin}${getSkillBundleUrl(agentName)}`;
|
|
121
|
+
const curlText = `curl -o skill-bundle.zip ${skillBundleUrl}`;
|
|
122
|
+
const installPlatform = onboarding?.platformName || onboarding?.detectedPlatform || '';
|
|
123
|
+
const installCmdText = (installPlatform.toLowerCase().includes('hermes') || installPlatform.toLowerCase().includes('openclaw'))
|
|
124
|
+
? `hermes skills install ${skillBundleUrl}`
|
|
125
|
+
: `curl -o skill-bundle.zip ${skillBundleUrl} && unzip skill-bundle.zip`;
|
|
126
|
+
const agentPromptText = `Download and install your skill bundle from AIMEAT.
|
|
145
127
|
|
|
146
|
-
Your skill bundle URL: ${
|
|
128
|
+
Your skill bundle URL: ${skillBundleUrl}
|
|
147
129
|
|
|
148
130
|
Steps:
|
|
149
131
|
1. Authenticate with your agent token: POST ${location.origin}/v1/auth/token
|
|
150
132
|
Sign the challenge with your private key to get a JWT.
|
|
151
133
|
|
|
152
134
|
2. Download the skill bundle ZIP:
|
|
153
|
-
GET ${
|
|
135
|
+
GET ${skillBundleUrl}
|
|
154
136
|
Header: Authorization: Bearer <your-jwt>
|
|
155
137
|
Save the response as a ZIP file.
|
|
156
138
|
|
|
@@ -162,12 +144,7 @@ Steps:
|
|
|
162
144
|
4. Read SKILL.md first -- it has your personalized directives, rules, and all the API endpoints you need.
|
|
163
145
|
|
|
164
146
|
If you already have a JWT token, you can do this in one step:
|
|
165
|
-
curl -H "Authorization: Bearer <jwt>" -o skill-bundle.zip "${
|
|
166
|
-
copyToClipboard(prompt).then(() => {
|
|
167
|
-
setCopiedPrompt(true);
|
|
168
|
-
setTimeout(() => setCopiedPrompt(false), 3000);
|
|
169
|
-
});
|
|
170
|
-
}
|
|
147
|
+
curl -H "Authorization: Bearer <jwt>" -o skill-bundle.zip "${skillBundleUrl}" && unzip skill-bundle.zip && cat */SKILL.md`;
|
|
171
148
|
|
|
172
149
|
function handleEditWebhook(e) {
|
|
173
150
|
e.stopPropagation();
|
|
@@ -212,7 +189,7 @@ curl -H "Authorization: Bearer <jwt>" -o skill-bundle.zip "${url}" && unzip skil
|
|
|
212
189
|
const isOnboarding = state === 'new' || state === 'onboarding';
|
|
213
190
|
|
|
214
191
|
if (isOnboarding) {
|
|
215
|
-
return renderOnboardingView(onboarding, agentName, handleRerun, rerunning,
|
|
192
|
+
return renderOnboardingView(onboarding, agentName, handleRerun, rerunning, curlText, installCmdText, agentPromptText);
|
|
216
193
|
}
|
|
217
194
|
|
|
218
195
|
async function handleShowAll() {
|
|
@@ -226,10 +203,10 @@ curl -H "Authorization: Bearer <jwt>" -o skill-bundle.zip "${url}" && unzip skil
|
|
|
226
203
|
|
|
227
204
|
const displayDeliveries = showAllDeliveries && allDeliveries ? allDeliveries : deliveries;
|
|
228
205
|
|
|
229
|
-
return renderProductionView(agent, onboarding, webhook, bundleVersion, displayDeliveries, handleTestWebhook, testing, handleRerun, rerunning,
|
|
206
|
+
return renderProductionView(agent, onboarding, webhook, bundleVersion, displayDeliveries, handleTestWebhook, testing, handleRerun, rerunning, curlText, handleShowAll, showAllDeliveries, editingWebhook, webhookDraft, setWebhookDraft, handleEditWebhook, handleSaveWebhook, handleCancelWebhook, savingWebhook, handleUpdateBundle, updatingBundle, agentPromptText, postChecklist);
|
|
230
207
|
}
|
|
231
208
|
|
|
232
|
-
function renderOnboardingView(onboarding, agentName, handleRerun, rerunning,
|
|
209
|
+
function renderOnboardingView(onboarding, agentName, handleRerun, rerunning, curlText, installCmdText, agentPromptText) {
|
|
233
210
|
const steps = onboarding?.steps || [];
|
|
234
211
|
const passed = steps.filter(s => s.status === 'passed').length;
|
|
235
212
|
const total = steps.length || 11;
|
|
@@ -269,15 +246,15 @@ function renderOnboardingView(onboarding, agentName, handleRerun, rerunning, han
|
|
|
269
246
|
<div class="pf-agd-section">
|
|
270
247
|
<div class="pf-agd-section-label">${t('profile.agents.skillBundle.title')}</div>
|
|
271
248
|
<div class="pf-agd-form-actions">
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
249
|
+
<${CopyButton} text=${agentPromptText} className="btn-primary btn-sm"
|
|
250
|
+
label=${t('profile.agents.detail.integration.copyAgentPrompt')}
|
|
251
|
+
copiedLabel=${'✓ ' + t('profile.agents.detail.integration.promptCopied')} />
|
|
252
|
+
<${CopyButton} text=${installCmdText} className="btn-outline btn-sm"
|
|
253
|
+
label=${t('profile.agents.detail.integration.copyInstallCommand')}
|
|
254
|
+
copiedLabel=${'✓ ' + t('profile.agents.detail.integration.installCommandCopied')} />
|
|
255
|
+
<${CopyButton} text=${curlText} className="btn-outline btn-sm"
|
|
256
|
+
label=${t('profile.agents.skillBundle.copyCurl')}
|
|
257
|
+
copiedLabel=${'✓ ' + t('profile.agents.copied')} />
|
|
281
258
|
<a class="btn-outline btn-sm" href=${getSkillBundleUrl(agentName)} download>
|
|
282
259
|
${t('profile.agents.skillBundle.downloadZip')}
|
|
283
260
|
</a>
|
|
@@ -293,7 +270,7 @@ function renderOnboardingView(onboarding, agentName, handleRerun, rerunning, han
|
|
|
293
270
|
`;
|
|
294
271
|
}
|
|
295
272
|
|
|
296
|
-
function renderProductionView(agent, onboarding, webhook, bundleVersion, displayDeliveries, handleTestWebhook, testing, handleRerun, rerunning,
|
|
273
|
+
function renderProductionView(agent, onboarding, webhook, bundleVersion, displayDeliveries, handleTestWebhook, testing, handleRerun, rerunning, curlText, handleShowAll, showAllDeliveries, editingWebhook, webhookDraft, setWebhookDraft, handleEditWebhook, handleSaveWebhook, handleCancelWebhook, savingWebhook, handleUpdateBundle, updatingBundle, agentPromptText, postChecklist) {
|
|
297
274
|
const steps = onboarding?.steps || [];
|
|
298
275
|
const agentName = agent.name;
|
|
299
276
|
|
|
@@ -397,12 +374,12 @@ function renderProductionView(agent, onboarding, webhook, bundleVersion, display
|
|
|
397
374
|
</div>
|
|
398
375
|
` : ''}
|
|
399
376
|
<div class="pf-agd-form-actions">
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
377
|
+
<${CopyButton} text=${agentPromptText} className="btn-primary btn-sm"
|
|
378
|
+
label=${t('profile.agents.detail.integration.copyAgentPrompt')}
|
|
379
|
+
copiedLabel=${'✓ ' + t('profile.agents.detail.integration.promptCopied')} />
|
|
380
|
+
<${CopyButton} text=${curlText} className="btn-outline btn-sm"
|
|
381
|
+
label=${t('profile.agents.skillBundle.reinstall')}
|
|
382
|
+
copiedLabel=${'✓ ' + t('profile.agents.copied')} />
|
|
406
383
|
<button class="btn-outline btn-sm" onClick=${handleUpdateBundle} disabled=${updatingBundle}>
|
|
407
384
|
${updatingBundle ? '...' : t('profile.agents.detail.integration.update')}
|
|
408
385
|
</button>
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
* @description Messages tab with command palette, "/" autocomplete, and chat area.
|
|
4
4
|
* Wraps the existing messages subtab and adds command discovery.
|
|
5
5
|
* @version-history
|
|
6
|
+
* v1.4.0 -- 2026-06-02 -- Render agent (outbound) message bodies as Markdown via the
|
|
7
|
+
* shared safe vnode Markdown component, so LLM replies (bold, lists, code,
|
|
8
|
+
* tables) display formatted. Owner inbound messages stay literal text.
|
|
6
9
|
* v1.3.0 -- 2026-05-30 -- Render agent single-select option-prompts (metadata.prompt) as
|
|
7
10
|
* clickable chips + an implicit "Other"; clicking sends a correlated prompt_answer. A
|
|
8
11
|
* prompt locks (read-only, chosen chip highlighted) once a newer message exists.
|
|
@@ -18,6 +21,7 @@ import { t } from '/js/i18n.js';
|
|
|
18
21
|
import { timeAgo } from '/js/utils.js';
|
|
19
22
|
import { sendMessage, listMessages, listThreads } from '/js/services/agent-messages.js';
|
|
20
23
|
import { getAgentCommands } from '/js/services/agent-integration.js';
|
|
24
|
+
import { Markdown } from '/components/Markdown.js';
|
|
21
25
|
|
|
22
26
|
const html = htm.bind(h);
|
|
23
27
|
|
|
@@ -222,7 +226,12 @@ export default function TabMessages({ agent, agentName, session, showToast }) {
|
|
|
222
226
|
<div key=${msg.id || msg.createdAt}>
|
|
223
227
|
<div class="pf-agd-msg-bubble ${msg.direction === 'inbound' ? 'pf-agd-msg-inbound' : 'pf-agd-msg-outbound'} ${isCommand ? 'pf-agd-msg-command' : ''} ${isReply ? 'pf-agd-msg-reply' : ''}">
|
|
224
228
|
${isCommand && html`<span class="pf-agd-command-badge">${t('profile.agents.detail.messages.command')}</span>`}
|
|
225
|
-
${msg.
|
|
229
|
+
${msg.direction === 'outbound'
|
|
230
|
+
// Agent replies are markdown (LLM output). Render them safely via the
|
|
231
|
+
// shared vnode Markdown component. Owner-typed inbound messages stay
|
|
232
|
+
// literal — the input is a plain text field, not markdown.
|
|
233
|
+
? html`<${Markdown} text=${msg.content || ''} />`
|
|
234
|
+
: msg.content}
|
|
226
235
|
</div>
|
|
227
236
|
<div class="pf-agd-msg-meta ${msg.direction === 'inbound' ? 'pf-agd-msg-meta-right' : ''}">
|
|
228
237
|
${msg.createdAt ? html`<span class="pf-agd-msg-time">${new Date(msg.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span> ${timeAgo(msg.createdAt)}` : ''}
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* @structure default export TabQuality({ agentName })
|
|
11
11
|
* @usage rendered by agent-card.js renderTabContent() for the 'quality' tab
|
|
12
12
|
* @version-history
|
|
13
|
+
* v1.2.0 -- 2026-06-01 -- Stop the empty flash on refresh: only show the loading
|
|
14
|
+
* state on first load; live-update refetches swap data in place (and keep the
|
|
15
|
+
* last good data on a transient error) instead of blanking the tab.
|
|
13
16
|
* v1.1.0 -- 2026-05-31 -- Add "Rate deliverables" list: completed tasks the owner
|
|
14
17
|
* can rate inline via the shared RateModal (unrated first); refreshes the
|
|
15
18
|
* rollups on submit.
|
|
@@ -50,15 +53,18 @@ export default function TabQuality({ agentName, showToast }) {
|
|
|
50
53
|
const [sendingRate, setSendingRate] = useState(false);
|
|
51
54
|
|
|
52
55
|
async function loadData() {
|
|
53
|
-
|
|
56
|
+
// NB: do NOT blank `data`/`loading` here. The initial render shows the
|
|
57
|
+
// loading state (loading starts true); every later call -- including
|
|
58
|
+
// live-update refetches -- swaps in fresh data in place without flashing an
|
|
59
|
+
// empty tab. A transient error keeps the last good data rather than clearing.
|
|
54
60
|
try {
|
|
55
61
|
const [statsResp, tasksResp] = await Promise.all([
|
|
56
62
|
getAgentStatistics(agentName),
|
|
57
63
|
listTasks(agentName, { status: 'done', per_page: 100 }).catch(() => null),
|
|
58
64
|
]);
|
|
59
|
-
|
|
65
|
+
if (statsResp?.data) setData(statsResp.data);
|
|
60
66
|
setDoneTasks(tasksResp?.data?.tasks || []);
|
|
61
|
-
} catch {
|
|
67
|
+
} catch { /* keep showing the last good data on a transient error */ }
|
|
62
68
|
setLoading(false);
|
|
63
69
|
}
|
|
64
70
|
|
|
@@ -104,26 +110,26 @@ export default function TabQuality({ agentName, showToast }) {
|
|
|
104
110
|
<!-- Performance -->
|
|
105
111
|
<div class="pf-agd-section-title">${t('profile.agents.detail.quality.performanceTitle')}</div>
|
|
106
112
|
<div class="pf-agd-quality-desc">${t('profile.agents.detail.quality.performanceDesc')}</div>
|
|
107
|
-
<div class="
|
|
108
|
-
<div class="
|
|
109
|
-
<div class="
|
|
110
|
-
<div class="
|
|
113
|
+
<div class="stat-grid">
|
|
114
|
+
<div class="stat-card">
|
|
115
|
+
<div class="stat-card-value">${perf.tasks?.total ?? 0}</div>
|
|
116
|
+
<div class="stat-card-label">${t('profile.agents.detail.quality.tasksTotal')}</div>
|
|
111
117
|
</div>
|
|
112
|
-
<div class="
|
|
113
|
-
<div class="
|
|
114
|
-
<div class="
|
|
118
|
+
<div class="stat-card">
|
|
119
|
+
<div class="stat-card-value">${perf.tasks?.completed ?? 0}</div>
|
|
120
|
+
<div class="stat-card-label">${t('profile.agents.detail.quality.tasksCompleted')}</div>
|
|
115
121
|
</div>
|
|
116
|
-
<div class="
|
|
117
|
-
<div class="
|
|
118
|
-
<div class="
|
|
122
|
+
<div class="stat-card">
|
|
123
|
+
<div class="stat-card-value">${perf.tasks?.successRate != null ? `${Math.round(perf.tasks.successRate * 100)}%` : '-'}</div>
|
|
124
|
+
<div class="stat-card-label">${t('profile.agents.detail.quality.successRate')}</div>
|
|
119
125
|
</div>
|
|
120
|
-
<div class="
|
|
121
|
-
<div class="
|
|
122
|
-
<div class="
|
|
126
|
+
<div class="stat-card">
|
|
127
|
+
<div class="stat-card-value">${fmtSeconds(perf.duration?.avgCompletionSeconds)}</div>
|
|
128
|
+
<div class="stat-card-label">${t('profile.agents.detail.quality.avgTime')}</div>
|
|
123
129
|
</div>
|
|
124
|
-
<div class="
|
|
125
|
-
<div class="
|
|
126
|
-
<div class="
|
|
130
|
+
<div class="stat-card">
|
|
131
|
+
<div class="stat-card-value">${perf.events?.total ?? 0}</div>
|
|
132
|
+
<div class="stat-card-label">${t('profile.agents.detail.quality.events')}</div>
|
|
127
133
|
</div>
|
|
128
134
|
</div>
|
|
129
135
|
${durKeys.length > 0 && html`
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file tab-schedules.js
|
|
3
|
+
* @description Agent detail › Schedules sub-tab. Shows this agent's schedules in
|
|
4
|
+
* two groups: AIMEAT-dispatched (server-managed; full controls) and Agent
|
|
5
|
+
* internal (the agent's self-reported mirror, read-only). Lets the owner create
|
|
6
|
+
* a new schedule targeting this agent (reusing the master view's CreateForm).
|
|
7
|
+
* @version-history
|
|
8
|
+
* v1.0.0 -- 2026-06-03 -- Initial agent Schedules sub-tab
|
|
9
|
+
*/
|
|
10
|
+
import { h } from 'preact';
|
|
11
|
+
import { useState, useEffect, useRef, useCallback } from 'preact/hooks';
|
|
12
|
+
import htm from 'htm';
|
|
13
|
+
import { t } from '/js/i18n.js';
|
|
14
|
+
import { timeAgo } from '/js/utils.js';
|
|
15
|
+
import { listAgentSchedules, setScheduleEnabled, triggerSchedule, deleteSchedule } from '/js/services/schedules.js';
|
|
16
|
+
import { CreateForm } from '../scheduler-tab.js';
|
|
17
|
+
|
|
18
|
+
const html = htm.bind(h);
|
|
19
|
+
|
|
20
|
+
const KIND_LABEL = { ai: 'profile.scheduler.kind.ai', agent_task: 'profile.scheduler.kind.agent_task', extension: 'profile.scheduler.kind.extension', core: 'profile.scheduler.kind.core' };
|
|
21
|
+
|
|
22
|
+
export default function TabSchedules({ agentName, allAgents = [], showToast }) {
|
|
23
|
+
const [managed, setManaged] = useState([]);
|
|
24
|
+
const [internal, setInternal] = useState([]);
|
|
25
|
+
const [loading, setLoading] = useState(true);
|
|
26
|
+
const [showForm, setShowForm] = useState(false);
|
|
27
|
+
|
|
28
|
+
const loadData = useCallback(async () => {
|
|
29
|
+
try {
|
|
30
|
+
const res = await listAgentSchedules(agentName);
|
|
31
|
+
setManaged(res?.data?.managed || []);
|
|
32
|
+
setInternal(res?.data?.agentInternal || []);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
showToast?.(e.message, true);
|
|
35
|
+
} finally { setLoading(false); }
|
|
36
|
+
}, [agentName, showToast]);
|
|
37
|
+
|
|
38
|
+
const loadRef = useRef(loadData);
|
|
39
|
+
loadRef.current = loadData;
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
loadData();
|
|
42
|
+
const handler = () => loadRef.current();
|
|
43
|
+
window.addEventListener('aimeat-live-update', handler);
|
|
44
|
+
const poller = setInterval(() => loadRef.current(), 15000);
|
|
45
|
+
return () => { window.removeEventListener('aimeat-live-update', handler); clearInterval(poller); };
|
|
46
|
+
}, [loadData]);
|
|
47
|
+
|
|
48
|
+
const onToggle = async (j) => { try { await setScheduleEnabled(j.id, !j.enabled); await loadData(); } catch (e) { showToast?.(e.message, true); } };
|
|
49
|
+
const onTrigger = async (j) => { try { await triggerSchedule(j.id); showToast?.(t('profile.scheduler.triggered')); await loadData(); } catch (e) { showToast?.(e.message, true); } };
|
|
50
|
+
const onCancel = async (j) => {
|
|
51
|
+
if (!window.confirm(t('profile.scheduler.confirmCancel'))) return;
|
|
52
|
+
try { await deleteSchedule(j.id); showToast?.(t('profile.scheduler.cancelled')); await loadData(); } catch (e) { showToast?.(e.message, true); }
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (loading) return html`<div class="sch-loading">${t('profile.scheduler.loading')}</div>`;
|
|
56
|
+
|
|
57
|
+
return html`
|
|
58
|
+
<div class="sch-tab">
|
|
59
|
+
<div class="sch-head">
|
|
60
|
+
<div class="section-desc">${t('profile.scheduler.agentDesc')}</div>
|
|
61
|
+
<button class="btn-primary" onClick=${() => setShowForm(v => !v)}>
|
|
62
|
+
${showForm ? t('profile.scheduler.close') : t('profile.scheduler.newSchedule')}
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
${showForm && html`<${CreateForm} agents=${allAgents} lockedAgent=${agentName} showToast=${showToast}
|
|
67
|
+
onCreated=${() => { setShowForm(false); loadData(); }} />`}
|
|
68
|
+
|
|
69
|
+
<div class="sch-section">
|
|
70
|
+
<h3 class="sch-section-title">${t('profile.scheduler.dispatchedTitle')}</h3>
|
|
71
|
+
${managed.length === 0
|
|
72
|
+
? html`<div class="sch-empty">${t('profile.scheduler.noDispatched')}</div>`
|
|
73
|
+
: html`<table class="sch-table"><thead><tr>
|
|
74
|
+
<th>${t('profile.scheduler.col.name')}</th>
|
|
75
|
+
<th>${t('profile.scheduler.col.kind')}</th>
|
|
76
|
+
<th>${t('profile.scheduler.col.cron')}</th>
|
|
77
|
+
<th>${t('profile.scheduler.col.lastRun')}</th>
|
|
78
|
+
<th>${t('profile.scheduler.col.nextRun')}</th>
|
|
79
|
+
<th></th>
|
|
80
|
+
</tr></thead><tbody>
|
|
81
|
+
${managed.map(j => html`<tr key=${j.id}>
|
|
82
|
+
<td><div class="sch-name">${j.displayName || j.name}</div>${j.createdByAgent && html`<span class="sch-tag">${t('profile.scheduler.byAgent')}</span>`}</td>
|
|
83
|
+
<td><span class="sch-badge sch-badge--${j.type === 'ai' ? 'ai' : j.type === 'extension' ? 'ext' : 'agent'}">${t(KIND_LABEL[j.type] || KIND_LABEL.core)}</span></td>
|
|
84
|
+
<td><code class="sch-cron">${j.cron}</code>${j.timezone && html`<div class="sch-muted">${j.timezone}</div>`}</td>
|
|
85
|
+
<td>${j.lastRunAt ? timeAgo(j.lastRunAt) : html`<span class="sch-muted">${t('profile.scheduler.never')}</span>`} ${j.lastRunResult ? html`<span class="sch-badge ${j.lastRunResult === 'error' ? 'sch-badge--err' : 'sch-badge--ok'}">${j.lastRunResult}</span>` : ''}</td>
|
|
86
|
+
<td>${j.nextRunAt ? timeAgo(j.nextRunAt) : html`<span class="sch-muted">—</span>`}</td>
|
|
87
|
+
<td class="sch-actions">
|
|
88
|
+
<button class="btn-ghost sch-btn" onClick=${() => onToggle(j)}>${j.enabled ? t('profile.scheduler.pause') : t('profile.scheduler.resume')}</button>
|
|
89
|
+
<button class="btn-ghost sch-btn" onClick=${() => onTrigger(j)}>${t('profile.scheduler.runNow')}</button>
|
|
90
|
+
<button class="btn-danger sch-btn" onClick=${() => onCancel(j)}>${t('profile.scheduler.cancel')}</button>
|
|
91
|
+
</td>
|
|
92
|
+
</tr>`)}
|
|
93
|
+
</tbody></table>`}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="sch-section">
|
|
97
|
+
<h3 class="sch-section-title">${t('profile.scheduler.internalTitle')}</h3>
|
|
98
|
+
<div class="sch-muted sch-internal-note">${t('profile.scheduler.internalNote')}</div>
|
|
99
|
+
${internal.length === 0
|
|
100
|
+
? html`<div class="sch-empty">${t('profile.scheduler.noInternal')}</div>`
|
|
101
|
+
: html`<table class="sch-table"><tbody>
|
|
102
|
+
${internal.map((e, i) => html`<tr key=${e.id || i}>
|
|
103
|
+
<td>${e.name}${e.purpose && html`<div class="sch-muted">${e.purpose}</div>`}</td>
|
|
104
|
+
<td><code class="sch-cron">${e.cron || e.schedule || '—'}</code>${e.timezone && html`<div class="sch-muted">${e.timezone}</div>`}</td>
|
|
105
|
+
<td>${e.status || 'active'}</td>
|
|
106
|
+
</tr>`)}
|
|
107
|
+
</tbody></table>`}
|
|
108
|
+
</div>
|
|
109
|
+
</div>`;
|
|
110
|
+
}
|
|
@@ -35,11 +35,11 @@ function StatsCards({ stats }) {
|
|
|
35
35
|
];
|
|
36
36
|
|
|
37
37
|
return html`
|
|
38
|
-
<div class="
|
|
38
|
+
<div class="stat-grid">
|
|
39
39
|
${cards.map(c => html`
|
|
40
|
-
<div class="
|
|
41
|
-
<div class="
|
|
42
|
-
<div class="
|
|
40
|
+
<div class="stat-card" key=${c.key}>
|
|
41
|
+
<div class="stat-card-value">${c.value}</div>
|
|
42
|
+
<div class="stat-card-label">${t(`profile.agents.activity.${c.key}`)}</div>
|
|
43
43
|
</div>
|
|
44
44
|
`)}
|
|
45
45
|
</div>
|
|
@@ -55,13 +55,13 @@ export default function AgentServicesSubtab({ agentName, session, showToast }) {
|
|
|
55
55
|
const [services, setServices] = useState([]);
|
|
56
56
|
const [loading, setLoading] = useState(true);
|
|
57
57
|
|
|
58
|
-
async function loadServices() {
|
|
59
|
-
setLoading(true);
|
|
58
|
+
async function loadServices({ showSpinner = true } = {}) {
|
|
59
|
+
if (showSpinner) setLoading(true);
|
|
60
60
|
try {
|
|
61
61
|
const res = await getAgentServices(agentName);
|
|
62
62
|
setServices(res.actions || []);
|
|
63
63
|
} catch (err) {
|
|
64
|
-
showToast(err.message || t('profile.agents.services.loadError'), true);
|
|
64
|
+
if (showSpinner) showToast(err.message || t('profile.agents.services.loadError'), true);
|
|
65
65
|
}
|
|
66
66
|
setLoading(false);
|
|
67
67
|
}
|
|
@@ -88,7 +88,8 @@ export default function AgentServicesSubtab({ agentName, session, showToast }) {
|
|
|
88
88
|
useEffect(() => { loadServices(); }, [agentName]);
|
|
89
89
|
|
|
90
90
|
useEffect(() => {
|
|
91
|
-
|
|
91
|
+
// Silent refetch on live-update so the tab doesn't flash blank.
|
|
92
|
+
const handler = () => { loadServices({ showSpinner: false }); };
|
|
92
93
|
window.addEventListener('aimeat-live-update', handler);
|
|
93
94
|
return () => window.removeEventListener('aimeat-live-update', handler);
|
|
94
95
|
}, [agentName]);
|