chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:44:08 +08:00
commit 983960e2dd
1244 changed files with 281996 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
{
"$schema": "../../../schemas/skill-schemas/skill-locale-config.json",
"variables": {
"hours": "hours",
"minutes": "minutes",
"seconds": "seconds"
},
"actions": {
"set_timer": {
"answers": {
"timer_set": [
{
"speech": "Done. I will let you know when time is up."
}
],
"cannot_get_duration": [
"You should provide a duration for the timer.",
"You didn't provide a duration for the timer."
],
"unit_not_supported": [
"Sorry, I can't set a timer for this unit. Use {{ hours }}, {{ minutes }} or {{ seconds }} instead.",
"I can't set a timer for this duration. Use {{ hours }}, {{ minutes }} or {{ seconds }} instead."
]
}
},
"check_timer": {
"answers": {
"no_timer_set": ["No timer is set.", "There is no timer set."],
"timer_status": [
"The timer has {{ remaining_time }} seconds remaining.",
"Timer in progress. {{ remaining_time }} seconds left."
]
}
},
"cancel_timer": {
"answers": {
"timer_canceled": ["The timer is canceled.", "Timer is stopped."]
}
}
},
"widget_contents": {
"second_unit": "second",
"seconds_unit": "seconds",
"minutes_unit": "minutes",
"minute_unit": "minute",
"total_time": "Total {{ value }} {{ unit }}",
"times_up": ["Time's up!", "The timer is up!", "The timer has ended!"]
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"$schema": "../../../schemas/skill-schemas/skill.json",
"name": "Timer",
"icon_name": "timer-line",
"bridge": "nodejs",
"version": "1.0.0",
"description": "Set timers to remind you of things.",
"author": {
"name": "Louis Grenard",
"email": "louis@getleon.ai",
"url": "https://github.com/louistiti"
},
"actions": {
"set_timer": {
"type": "logic",
"description": "Set a timer for a specified duration.",
"parameters": {
"duration": {
"type": "object",
"properties": {
"value": {
"type": "number",
"description": "The numeric value of the duration."
},
"unit": {
"type": "string",
"enum": ["HOURS", "MINUTES", "SECONDS"],
"description": "The unit of time for the duration."
}
}
}
}
},
"cancel_timer": {
"type": "logic",
"description": "Cancel an active timer."
},
"check_timer": {
"type": "logic",
"description": "Check the remaining time on the active timer."
}
}
}
@@ -0,0 +1,10 @@
import type { ActionFunction } from '@sdk/types'
import { leon } from '@sdk/leon'
import { deleteAllTimersMemory } from '../lib/memory'
export const run: ActionFunction = async function () {
await deleteAllTimersMemory()
await leon.answer({ key: 'timer_canceled' })
}
@@ -0,0 +1,45 @@
import type { ActionFunction } from '@sdk/types'
import { leon } from '@sdk/leon'
import { TimerWidget } from '../widgets/timer-widget'
import { getTimerMemoryByWidgetId, getNewestTimerMemory } from '../lib/memory'
export const run: ActionFunction = async function (_params, paramsHelper) {
const widgetId = paramsHelper.getWidgetId()
const timerMemory = widgetId
? await getTimerMemoryByWidgetId(widgetId)
: await getNewestTimerMemory()
if (!timerMemory) {
await leon.answer({ key: 'no_timer_set' })
return
}
const { interval, finishedAt, duration } = timerMemory
let remainingTime = finishedAt - Math.floor(Date.now() / 1_000)
if (remainingTime <= 0) {
remainingTime = 0
}
const initialProgress = 100 - (remainingTime / duration) * 100
const timerWidget = new TimerWidget({
params: {
seconds: remainingTime,
initialProgress,
initialDuration: duration,
interval
},
onFetch: {
widgetId: widgetId ?? timerMemory.widgetId,
actionName: 'check_timer'
}
})
await leon.answer({
widget: timerWidget,
key: 'timer_status',
data: {
remaining_time: remainingTime
}
})
}
@@ -0,0 +1,57 @@
import type { ActionFunction } from '@sdk/types'
import { leon } from '@sdk/leon'
import { TimerWidget } from '../widgets/timer-widget'
import { createTimerMemory } from '../lib/memory'
interface TimerDuration {
value?: number
unit?: string
}
export const run: ActionFunction = async function (params) {
const supportedUnits = ['hours', 'minutes', 'seconds']
const duration = (params.action_arguments['duration'] as TimerDuration) || null
if (
!duration ||
typeof duration.value !== 'number' ||
typeof duration.unit !== 'string'
) {
await leon.answer({ key: 'cannot_get_duration' })
return
}
const normalizedUnit = duration.unit.toLowerCase()
if (!supportedUnits.includes(normalizedUnit)) {
await leon.answer({ key: 'unit_not_supported' })
return
}
const { value: durationValue } = duration
const seconds =
normalizedUnit === 'hours'
? Number(durationValue) * 3_600
: normalizedUnit === 'minutes'
? Number(durationValue) * 60
: Number(durationValue)
const interval = 1_000
const timerWidget = new TimerWidget({
params: {
seconds,
initialProgress: 0,
interval
},
onFetch: {
actionName: 'check_timer'
}
})
await Promise.all([
createTimerMemory(timerWidget.id, seconds, interval),
leon.answer({
widget: timerWidget,
key: 'timer_set'
})
])
}
@@ -0,0 +1,55 @@
import { Memory } from '@sdk/memory'
export interface TimerMemory {
widgetId: string
duration: number
interval: number
createdAt: number
finishedAt: number
}
const TIMERS_MEMORY = new Memory<TimerMemory[]>({
name: 'timers',
defaultMemory: []
})
export async function createTimerMemory(
widgetId: string,
duration: number,
interval: number
): Promise<TimerMemory> {
const createdAt = Math.floor(Date.now() / 1_000)
const newTimerMemory: TimerMemory = {
duration,
widgetId,
interval,
createdAt,
finishedAt: createdAt + duration
}
const timersMemory = await TIMERS_MEMORY.read()
await TIMERS_MEMORY.write([...timersMemory, newTimerMemory])
return newTimerMemory
}
export async function getTimerMemoryByWidgetId(
widgetId: string
): Promise<TimerMemory | null> {
const timersMemory = await TIMERS_MEMORY.read()
return (
timersMemory.find((timerMemory) => timerMemory.widgetId === widgetId) ||
null
)
}
export async function getNewestTimerMemory(): Promise<TimerMemory | null> {
const timersMemory = await TIMERS_MEMORY.read()
return timersMemory[timersMemory.length - 1] || null
}
export function deleteAllTimersMemory(): Promise<TimerMemory[]> {
return TIMERS_MEMORY.write([])
}
@@ -0,0 +1 @@
{}
@@ -0,0 +1,16 @@
import type { WidgetEventMethod } from '@sdk/widget'
import { WidgetComponent } from '@sdk/widget-component'
interface TimerProps {
initialTime: number
initialProgress: number
interval: number
totalTimeContent: string
onEnd?: () => WidgetEventMethod
}
export class Timer extends WidgetComponent<TimerProps> {
constructor(props: TimerProps) {
super(props)
}
}
@@ -0,0 +1,53 @@
import type { WidgetComponent } from '@sdk/widget-component'
import { Widget, type WidgetEventMethod, type WidgetOptions } from '@sdk/widget'
import { Timer } from './components/timer'
interface Params {
seconds: number
interval: number
initialProgress: number
initialDuration?: number
}
export class TimerWidget extends Widget<Params> {
constructor(options: WidgetOptions<Params>) {
super(options)
}
public render(): WidgetComponent {
const { seconds, interval, initialDuration, initialProgress } = this.params
const secondUnitContent = this.content('second_unit')
const secondsUnitContent = this.content('seconds_unit')
const minuteUnitContent = this.content('minute_unit')
const minutesUnitContent = this.content('minutes_unit')
const totalTime = initialDuration || seconds
let totalTimeContent = ''
if (totalTime >= 60) {
const minutes = totalTime / 60
totalTimeContent = this.content('total_time', {
value: minutes % 1 === 0 ? minutes : minutes.toFixed(2),
unit: minutes > 1 ? minutesUnitContent : minuteUnitContent
})
} else {
totalTimeContent = this.content('total_time', {
value: totalTime,
unit: totalTime > 1 ? secondsUnitContent : secondUnitContent
})
}
return new Timer({
initialTime: seconds,
initialProgress,
interval,
totalTimeContent,
onEnd: (): WidgetEventMethod => {
return this.sendUtterance('times_up', {
from: 'leon'
})
}
})
}
}