diff --git a/bin/hermes-web-ui.mjs b/bin/hermes-web-ui.mjs old mode 100644 new mode 100755 diff --git a/packages/client/src/api/hermes/cron-history.ts b/packages/client/src/api/hermes/cron-history.ts new file mode 100644 index 0000000..6ab212e --- /dev/null +++ b/packages/client/src/api/hermes/cron-history.ts @@ -0,0 +1,27 @@ +import { request } from '../client' + +export interface RunEntry { + jobId: string + fileName: string + runTime: string + size: number +} + +export interface RunDetail { + jobId: string + fileName: string + runTime: string + content: string +} + +export async function listCronRuns(jobId?: string): Promise { + const params = new URLSearchParams() + if (jobId) params.set('jobId', jobId) + const qs = params.toString() + const res = await request<{ runs: RunEntry[] }>(`/api/cron-history${qs ? `?${qs}` : ''}`) + return res.runs +} + +export async function readCronRun(jobId: string, fileName: string): Promise { + return request(`/api/cron-history/${encodeURIComponent(jobId)}/${encodeURIComponent(fileName)}`) +} diff --git a/packages/client/src/api/hermes/jobs.ts b/packages/client/src/api/hermes/jobs.ts index 5873bb4..161e4f6 100644 --- a/packages/client/src/api/hermes/jobs.ts +++ b/packages/client/src/api/hermes/jobs.ts @@ -52,6 +52,8 @@ export interface UpdateJobRequest { skill?: string repeat?: number enabled?: boolean + model?: string + provider?: string } function unwrap(res: { job: Job }): Job { diff --git a/packages/client/src/components/hermes/jobs/JobCard.vue b/packages/client/src/components/hermes/jobs/JobCard.vue index f2afbcb..1d91a08 100644 --- a/packages/client/src/components/hermes/jobs/JobCard.vue +++ b/packages/client/src/components/hermes/jobs/JobCard.vue @@ -5,9 +5,14 @@ import type { Job } from '@/api/hermes/jobs' import { useJobsStore } from '@/stores/hermes/jobs' import { useI18n } from 'vue-i18n' -const props = defineProps<{ job: Job }>() +const props = defineProps<{ + job: Job + selected?: boolean +}>() + const emit = defineEmits<{ edit: [jobId: string] + select: [jobId: string] }>() const { t } = useI18n() @@ -76,10 +81,16 @@ async function handleDelete() { message.error(e.message) } } + +function handleCardClick(e: MouseEvent) { + const target = e.target as HTMLElement + if (target.closest('.card-actions')) return + emit('select', jobId.value) +} @@ -150,10 +165,16 @@ async function handleDelete() { border-radius: $radius-md; padding: 16px; transition: border-color $transition-fast; + cursor: pointer; &:hover { border-color: rgba(var(--accent-primary-rgb), 0.3); } + + &.selected { + border-color: rgba(var(--accent-primary-rgb), 0.6); + background-color: rgba(var(--accent-primary-rgb), 0.04); + } } .card-header { diff --git a/packages/client/src/components/hermes/jobs/JobFormModal.vue b/packages/client/src/components/hermes/jobs/JobFormModal.vue index 0f57b5e..f52e146 100644 --- a/packages/client/src/components/hermes/jobs/JobFormModal.vue +++ b/packages/client/src/components/hermes/jobs/JobFormModal.vue @@ -2,6 +2,7 @@ import { ref, onMounted, computed } from 'vue' import { NModal, NForm, NFormItem, NInput, NButton, NSelect, NInputNumber, useMessage } from 'naive-ui' import { useJobsStore } from '@/stores/hermes/jobs' +import type { CreateJobRequest, UpdateJobRequest } from '@/api/hermes/jobs' import { useI18n } from 'vue-i18n' const { t } = useI18n() @@ -83,26 +84,32 @@ async function handleSave() { loading.value = true try { - const payload = { - name: formData.value.name, - schedule: formData.value.schedule, - prompt: formData.value.prompt, - deliver: formData.value.deliver, - repeat: formData.value.repeat_times ?? undefined, - } - - if (isEdit.value && originalSchedule.value) { - (payload as any).schedule = { - kind: originalSchedule.value.kind, - expr: formData.value.schedule, - display: formData.value.schedule, - } - } - if (isEdit.value) { + const payload: UpdateJobRequest = { + name: formData.value.name, + prompt: formData.value.prompt, + deliver: formData.value.deliver, + repeat: formData.value.repeat_times ?? undefined, + } + if (originalSchedule.value) { + payload.schedule = { + kind: originalSchedule.value.kind, + expr: formData.value.schedule, + display: formData.value.schedule, + } + } else { + payload.schedule = formData.value.schedule + } await jobsStore.updateJob(props.jobId!, payload) message.success(t('jobs.jobUpdated')) } else { + const payload: CreateJobRequest = { + name: formData.value.name, + schedule: formData.value.schedule, + prompt: formData.value.prompt, + deliver: formData.value.deliver, + repeat: formData.value.repeat_times ?? undefined, + } await jobsStore.createJob(payload) message.success(t('jobs.jobCreated')) } diff --git a/packages/client/src/components/hermes/jobs/JobRunHistory.vue b/packages/client/src/components/hermes/jobs/JobRunHistory.vue new file mode 100644 index 0000000..f65a6b4 --- /dev/null +++ b/packages/client/src/components/hermes/jobs/JobRunHistory.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/packages/client/src/components/hermes/jobs/JobsPanel.vue b/packages/client/src/components/hermes/jobs/JobsPanel.vue index 9f35af4..60ea785 100644 --- a/packages/client/src/components/hermes/jobs/JobsPanel.vue +++ b/packages/client/src/components/hermes/jobs/JobsPanel.vue @@ -3,13 +3,28 @@ import JobCard from './JobCard.vue' import { useJobsStore } from '@/stores/hermes/jobs' import { useI18n } from 'vue-i18n' -const { t } = useI18n() +const props = defineProps<{ + selectedJobId: string | null +}>() const emit = defineEmits<{ edit: [jobId: string] + select: [jobId: string | null] }>() +const { t } = useI18n() + const jobsStore = useJobsStore() + +function handleSelect(jobId: string) { + emit('select', props.selectedJobId === jobId ? null : jobId) +} + +function handleDeselect() { + if (props.selectedJobId) { + emit('select', null) + } +} diff --git a/packages/client/src/i18n/locales/de.ts b/packages/client/src/i18n/locales/de.ts index a851bfd..69b23ba 100644 --- a/packages/client/src/i18n/locales/de.ts +++ b/packages/client/src/i18n/locales/de.ts @@ -167,6 +167,7 @@ export default { origin: 'Herkunft', local: 'Lokal', repeatCount: 'Wiederholungsanzahl (optional)', + modelPlaceholder: 'Standardmodell', repeatPlaceholder: 'Leer lassen fur unendlich', jobCreated: 'Aufgabe erstellt', jobUpdated: 'Aufgabe aktualisiert', @@ -175,7 +176,8 @@ export default { loadFailed: 'Laden der Aufgabe fehlgeschlagen', jobPaused: 'Aufgabe pausiert', jobResumed: 'Aufgabe fortgesetzt', - jobTriggered: 'Aufgabe ausgelost', +jobTriggered: 'Job ausgelost', + modelUpdated: 'Modell aktualisiert', jobDeleted: 'Aufgabe geloscht', status: { running: 'Lauft', @@ -184,6 +186,7 @@ export default { scheduled: 'Geplant', }, info: { + model: 'Modell', schedule: 'Zeitplan', lastRun: 'Letzte Ausfuhrung', nextRun: 'Nachste Ausfuhrung', @@ -196,7 +199,12 @@ export default { resume: 'Fortsetzen', resumeJob: 'Aufgabe fortsetzen', runNow: 'Jetzt ausfuhren', - triggerImmediately: 'Sofort auslosen', + triggerImmediately: 'Sofort auslösen', + }, + runHistory: { + title: 'Verlauf', + runs: 'Läufe', + noRuns: 'Kein Verlauf gefunden.', }, }, diff --git a/packages/client/src/i18n/locales/en.ts b/packages/client/src/i18n/locales/en.ts index da39239..6364257 100644 --- a/packages/client/src/i18n/locales/en.ts +++ b/packages/client/src/i18n/locales/en.ts @@ -190,6 +190,7 @@ export default { origin: 'Origin', local: 'Local', repeatCount: 'Repeat Count (optional)', + modelPlaceholder: 'Default model', repeatPlaceholder: 'Leave empty for infinite', jobCreated: 'Job created', jobUpdated: 'Job updated', @@ -199,6 +200,7 @@ export default { jobPaused: 'Job paused', jobResumed: 'Job resumed', jobTriggered: 'Job triggered', + modelUpdated: 'Model updated', jobDeleted: 'Job deleted', status: { running: 'Running', @@ -207,6 +209,7 @@ export default { scheduled: 'Scheduled', }, info: { + model: 'Model', schedule: 'Schedule', lastRun: 'Last Run', nextRun: 'Next Run', @@ -221,6 +224,11 @@ export default { runNow: 'Run Now', triggerImmediately: 'Trigger immediately', }, + runHistory: { + title: 'Run History', + runs: 'runs', + noRuns: 'No run history found.', + }, }, // Skills diff --git a/packages/client/src/i18n/locales/es.ts b/packages/client/src/i18n/locales/es.ts index 178e2c1..88f2fce 100644 --- a/packages/client/src/i18n/locales/es.ts +++ b/packages/client/src/i18n/locales/es.ts @@ -167,6 +167,7 @@ export default { origin: 'Origen', local: 'Local', repeatCount: 'Repeticiones (opcional)', + modelPlaceholder: 'Modelo por defecto', repeatPlaceholder: 'Dejar vacio para infinito', jobCreated: 'Tarea creada', jobUpdated: 'Tarea actualizada', @@ -175,7 +176,8 @@ export default { loadFailed: 'Error al cargar la tarea', jobPaused: 'Tarea en pausa', jobResumed: 'Tarea reanudada', - jobTriggered: 'Tarea ejecutada', +jobTriggered: 'Job ejecutado', + modelUpdated: 'Modelo actualizado', jobDeleted: 'Tarea eliminada', status: { running: 'En ejecucion', @@ -184,6 +186,7 @@ export default { scheduled: 'Programada', }, info: { + model: 'Modelo', schedule: 'Programacion', lastRun: 'Ultima ejecucion', nextRun: 'Proxima ejecucion', @@ -198,6 +201,11 @@ export default { runNow: 'Ejecutar ahora', triggerImmediately: 'Ejecutar inmediatamente', }, + runHistory: { + title: 'Historial', + runs: 'ejecuciones', + noRuns: 'No se encontró historial.', + }, }, // Skills diff --git a/packages/client/src/i18n/locales/fr.ts b/packages/client/src/i18n/locales/fr.ts index 4bcbd8f..c4c2f22 100644 --- a/packages/client/src/i18n/locales/fr.ts +++ b/packages/client/src/i18n/locales/fr.ts @@ -167,6 +167,7 @@ export default { origin: 'Origine', local: 'Local', repeatCount: 'Nombre de repetitions (facultatif)', + modelPlaceholder: 'Modele par defaut', repeatPlaceholder: 'Laisser vide pour infini', jobCreated: 'Tache creee', jobUpdated: 'Tache mise a jour', @@ -175,7 +176,8 @@ export default { loadFailed: 'Echec du chargement de la tache', jobPaused: 'Tache en pause', jobResumed: 'Tache reprise', - jobTriggered: 'Tache declenchee', +jobTriggered: 'Job declenche', + modelUpdated: 'Modele mis a jour', jobDeleted: 'Tache supprimee', status: { running: 'En cours', @@ -184,6 +186,7 @@ export default { scheduled: 'Planifiee', }, info: { + model: 'Modele', schedule: 'Planification', lastRun: 'Derniere execution', nextRun: 'Prochaine execution', @@ -196,7 +199,12 @@ export default { resume: 'Reprendre', resumeJob: 'Reprendre la tache', runNow: 'Executer maintenant', - triggerImmediately: 'Declencher immediatement', + triggerImmediately: 'Déclencher immédiatement', + }, + runHistory: { + title: 'Historique', + runs: 'exécutions', + noRuns: 'Aucun historique trouvé.', }, }, diff --git a/packages/client/src/i18n/locales/ja.ts b/packages/client/src/i18n/locales/ja.ts index 0d371df..78535e6 100644 --- a/packages/client/src/i18n/locales/ja.ts +++ b/packages/client/src/i18n/locales/ja.ts @@ -167,6 +167,7 @@ export default { origin: '配信元', local: 'ローカル', repeatCount: '繰り返し回数(任意)', + modelPlaceholder: 'Default model', repeatPlaceholder: '空白の場合は無制限', jobCreated: 'ジョブを作成しました', jobUpdated: 'ジョブを更新しました', @@ -176,6 +177,7 @@ export default { jobPaused: 'ジョブを一時停止しました', jobResumed: 'ジョブを再開しました', jobTriggered: 'ジョブをトリガーしました', + modelUpdated: 'Model updated', jobDeleted: 'ジョブを削除しました', status: { running: '実行中', @@ -184,7 +186,8 @@ export default { scheduled: 'スケジュール済み', }, info: { - schedule: 'スケジュール', + model: 'Model', + schedule: 'Schedule', lastRun: '前回実行', nextRun: '次回実行', deliver: '配信', @@ -198,6 +201,11 @@ export default { runNow: '今すぐ実行', triggerImmediately: 'すぐにトリガー', }, + runHistory: { + title: '実行履歴', + runs: '件', + noRuns: '実行履歴がありません。', + }, }, // スキル diff --git a/packages/client/src/i18n/locales/ko.ts b/packages/client/src/i18n/locales/ko.ts index 7bcbfe3..0e2fda4 100644 --- a/packages/client/src/i18n/locales/ko.ts +++ b/packages/client/src/i18n/locales/ko.ts @@ -167,6 +167,7 @@ export default { origin: '출처', local: '로컬', repeatCount: '반복 횟수 (선택)', + modelPlaceholder: 'Default model', repeatPlaceholder: '비워두면 무한 반복', jobCreated: '작업이 생성되었습니다', jobUpdated: '작업이 업데이트되었습니다', @@ -176,6 +177,7 @@ export default { jobPaused: '작업이 일시 정지되었습니다', jobResumed: '작업이 재개되었습니다', jobTriggered: '작업이 실행되었습니다', + modelUpdated: 'Model updated', jobDeleted: '작업이 삭제되었습니다', status: { running: '실행 중', @@ -184,7 +186,8 @@ export default { scheduled: '예약됨', }, info: { - schedule: '스케줄', + model: 'Model', + schedule: 'Schedule', lastRun: '마지막 실행', nextRun: '다음 실행', deliver: '전송', @@ -198,6 +201,11 @@ export default { runNow: '즉시 실행', triggerImmediately: '즉시 실행', }, + runHistory: { + title: '실행 기록', + runs: '회 실행', + noRuns: '실행 기록이 없습니다.', + }, }, // 스킬 diff --git a/packages/client/src/i18n/locales/pt.ts b/packages/client/src/i18n/locales/pt.ts index 316972d..a834e11 100644 --- a/packages/client/src/i18n/locales/pt.ts +++ b/packages/client/src/i18n/locales/pt.ts @@ -167,6 +167,7 @@ export default { origin: 'Origem', local: 'Local', repeatCount: 'Contagem de repeticoes (opcional)', + modelPlaceholder: 'Modelo padrao', repeatPlaceholder: 'Deixar vazio para infinito', jobCreated: 'Tarefa criada', jobUpdated: 'Tarefa atualizada', @@ -175,7 +176,8 @@ export default { loadFailed: 'Falha ao carregar a tarefa', jobPaused: 'Tarefa pausada', jobResumed: 'Tarefa retomada', - jobTriggered: 'Tarefa acionada', +jobTriggered: 'Job acionado', + modelUpdated: 'Modelo atualizado', jobDeleted: 'Tarefa excluida', status: { running: 'Em execucao', @@ -184,6 +186,7 @@ export default { scheduled: 'Agendada', }, info: { + model: 'Modelo', schedule: 'Agendamento', lastRun: 'Ultima execucao', nextRun: 'Proxima execucao', @@ -198,6 +201,11 @@ export default { runNow: 'Executar agora', triggerImmediately: 'Acionar imediatamente', }, + runHistory: { + title: 'Histórico', + runs: 'execuções', + noRuns: 'Nenhum histórico encontrado.', + }, }, // Skills diff --git a/packages/client/src/i18n/locales/zh.ts b/packages/client/src/i18n/locales/zh.ts index cc02e2c..dee5823 100644 --- a/packages/client/src/i18n/locales/zh.ts +++ b/packages/client/src/i18n/locales/zh.ts @@ -190,6 +190,7 @@ export default { origin: '来源', local: '本地', repeatCount: '重复次数(可选)', + modelPlaceholder: 'Default model', repeatPlaceholder: '留空表示无限重复', jobCreated: '任务已创建', jobUpdated: '任务已更新', @@ -199,6 +200,7 @@ export default { jobPaused: '任务已暂停', jobResumed: '任务已恢复', jobTriggered: '任务已触发', + modelUpdated: 'Model updated', jobDeleted: '任务已删除', status: { running: '运行中', @@ -207,7 +209,8 @@ export default { scheduled: '已调度', }, info: { - schedule: '调度', + model: 'Model', + schedule: 'Schedule', lastRun: '上次运行', nextRun: '下次运行', deliver: '投递', @@ -221,6 +224,11 @@ export default { runNow: '立即运行', triggerImmediately: '立即触发', }, + runHistory: { + title: '运行历史', + runs: '次运行', + noRuns: '暂无运行历史。', + }, }, // 技能 diff --git a/packages/client/src/views/hermes/JobsView.vue b/packages/client/src/views/hermes/JobsView.vue index f7e293f..8566420 100644 --- a/packages/client/src/views/hermes/JobsView.vue +++ b/packages/client/src/views/hermes/JobsView.vue @@ -1,8 +1,9 @@