opencode-cron-job 0.1.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/.cron-job/tasks.md +5 -0
- package/.github/workflows/ci.yml +19 -0
- package/.github/workflows/release.yml +42 -0
- package/.opencode/opencode.json +3 -0
- package/AGENTS.md +50 -0
- package/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/PUBLISH.md +77 -0
- package/README.md +160 -0
- package/package.json +30 -0
- package/src/index.ts +114 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
with:
|
|
16
|
+
node-version: lts/*
|
|
17
|
+
cache: npm
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npm run build
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
packages: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish-npm:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-node@v4
|
|
18
|
+
with:
|
|
19
|
+
node-version: 20
|
|
20
|
+
registry-url: 'https://registry.npmjs.org'
|
|
21
|
+
- run: npm ci
|
|
22
|
+
- run: npm run build
|
|
23
|
+
- run: npm publish
|
|
24
|
+
env:
|
|
25
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM }}
|
|
26
|
+
|
|
27
|
+
release:
|
|
28
|
+
needs: publish-npm
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
- name: Extract release notes from CHANGELOG.md
|
|
33
|
+
id: changelog
|
|
34
|
+
uses: release-flow/keep-a-changelog-action@v3
|
|
35
|
+
with:
|
|
36
|
+
command: query
|
|
37
|
+
version: ${{ github.ref_name }}
|
|
38
|
+
- name: Create GitHub Release
|
|
39
|
+
uses: softprops/action-gh-release@v2
|
|
40
|
+
with:
|
|
41
|
+
body: ${{ steps.changelog.outputs.release-notes }}
|
|
42
|
+
draft: false
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# opencode-cron-job
|
|
2
|
+
|
|
3
|
+
## Build
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm run build # tsc → dist/index.js
|
|
7
|
+
npm publish # builds + publishes to npm
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
After build, copy to plugins dir for local testing:
|
|
11
|
+
```bash
|
|
12
|
+
cp dist/index.js .opencode/plugins/
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
Single-file TypeScript OpenCode plugin (`src/index.ts`). Exports `CronPlugin` which is auto-discovered when installed as an npm plugin or placed in `.opencode/plugins/`.
|
|
18
|
+
|
|
19
|
+
### Flow
|
|
20
|
+
|
|
21
|
+
1. Plugin initializes → reads `.cron-job/tasks.md` from `directory` (project root)
|
|
22
|
+
2. Parses `##` sections → extracts `- cron:` and `- prompt:` fields
|
|
23
|
+
3. Schedules each valid cron expression via `node-cron`
|
|
24
|
+
4. On timer fire → `client.tui.appendPrompt()` → `client.tui.submitPrompt()`
|
|
25
|
+
5. Registers 4 tools: `cron_create`, `cron_list`, `cron_run`, `cron_delete`
|
|
26
|
+
|
|
27
|
+
### Key constraints
|
|
28
|
+
|
|
29
|
+
- NOT an MCP server — runs inside OpenCode process (no HTTP API, no sidecar)
|
|
30
|
+
- Tools are defined with `tool()` helper from `@opencode-ai/plugin`, not raw objects
|
|
31
|
+
- Prompt injection uses `client.tui.*` methods (TUI mode only)
|
|
32
|
+
- Cron uses 5-field expressions by default, 6-field if seconds are specified (`*/30 * * * * *`)
|
|
33
|
+
- Jobs are in-memory and volatile — reload via `cron_reload` after editing `.cron-job/tasks.md`
|
|
34
|
+
- `@opencode-ai/plugin` is a peer dependency provided by OpenCode runtime
|
|
35
|
+
- Plugin auto-updates via npm `@latest` tag are unreliable — bump pinned version in config or clear cache manually
|
|
36
|
+
|
|
37
|
+
### Published API
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"plugin": ["opencode-cron-job@latest"]
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Gotchas
|
|
46
|
+
|
|
47
|
+
- `node-cron` v3 accepts 6-field format (seconds included)
|
|
48
|
+
- Plugin runs inside Bun/OpenCode runtime — use `import` syntax, not `require`
|
|
49
|
+
- Build artifact `.opencode/plugins/index.js` is copied from `dist/` — both gitignored
|
|
50
|
+
- `.opencode/package.json` has its own `node_modules/` for local plugin dev — separate from root
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-06-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release
|
|
13
|
+
- OpenCode plugin that schedules recurring prompts via `.cron-job/tasks.md`
|
|
14
|
+
- Tools: `cron_create`, `cron_list`, `cron_run`, `cron_delete`
|
|
15
|
+
- Auto-load tasks on plugin initialization
|
|
16
|
+
- Prompt injection via `client.tui.appendPrompt()` + `client.tui.submitPrompt()`
|
|
17
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/PUBLISH.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# 发布指南
|
|
2
|
+
|
|
3
|
+
## 发布前检查
|
|
4
|
+
|
|
5
|
+
### 检查 `package.json` 版本号
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"version": "0.1.0"
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
确保版本号符合语义化版本规范。
|
|
14
|
+
|
|
15
|
+
### 检查 CHANGELOG.md 是否需要同步
|
|
16
|
+
|
|
17
|
+
CHANGELOG.md 是 GitHub Release Notes 的数据源:
|
|
18
|
+
|
|
19
|
+
- **每次发版前**:在 `[Unreleased]` 下方写好本次版本的变更说明
|
|
20
|
+
- **格式**:遵循 [Keep a Changelog](https://keepachangelog.com/) 规范,分 `Added` / `Changed` / `Fixed` / `Removed` 等分类
|
|
21
|
+
- **日期**:格式为 `YYYY-MM-DD`
|
|
22
|
+
|
|
23
|
+
### 检查 README.md 是否需要同步
|
|
24
|
+
|
|
25
|
+
- **功能增减**或**行为变化**时需要同步更新中英文说明
|
|
26
|
+
- 纯 bug 修复不需要更新
|
|
27
|
+
|
|
28
|
+
### 检查 AGENTS.md 是否需要同步
|
|
29
|
+
|
|
30
|
+
- **架构变更**、**工具变更**(新增/删除工具)时需要更新
|
|
31
|
+
- Bug 修复或内部重构不需要更新
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 发布流程
|
|
36
|
+
|
|
37
|
+
### 1. 更新 CHANGELOG.md(必做)
|
|
38
|
+
|
|
39
|
+
将 `[Unreleased]` 改为版本号和日期:
|
|
40
|
+
|
|
41
|
+
```markdown
|
|
42
|
+
## [0.1.0] - 2026-06-04
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
- 新增功能...
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. 更新版本号
|
|
49
|
+
|
|
50
|
+
编辑 `package.json` 中的 `version` 字段。
|
|
51
|
+
|
|
52
|
+
### 3. 提交代码并推送 Tag
|
|
53
|
+
|
|
54
|
+
> ⚠️ 执行前先向用户发送消息确认(已完成以上检查和修改),获得确认后才能执行。
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git add -A
|
|
58
|
+
git commit -m "release: v0.1.0"
|
|
59
|
+
git tag v0.1.0
|
|
60
|
+
git push origin main --tags
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> push 不会自动推送 tags,必须执行 `git push origin --tags` 或 `git push origin v0.1.0`。
|
|
64
|
+
|
|
65
|
+
### 4. 自动构建与发布
|
|
66
|
+
|
|
67
|
+
推送 tag 后,GitHub Actions 自动触发 `.github/workflows/release.yml`:
|
|
68
|
+
|
|
69
|
+
1. **`publish-npm`** — 安装依赖 → 构建 → 发布到 npm
|
|
70
|
+
2. **`release`** — 自动创建带 Release Notes 的 GitHub Release
|
|
71
|
+
|
|
72
|
+
> **前置条件**:在 GitHub 仓库 Settings → Secrets and variables → Actions 中配置 `NPM` secret,值为 npm Automation Token。
|
|
73
|
+
|
|
74
|
+
### 5. 检查结果
|
|
75
|
+
|
|
76
|
+
- 确认 npm 包已更新:`npm view opencode-cron-job`
|
|
77
|
+
- 确认 GitHub Release 已创建
|
package/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# opencode-cron-job
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/opencode-cron-job)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
**OpenCode plugin** — schedule recurring prompts via a simple markdown file.
|
|
7
|
+
**OpenCode 插件** — 通过简单的 markdown 文件定时执行 prompt。
|
|
8
|
+
|
|
9
|
+
> ⚠️ **Alpha** — This is an initial release and has not been extensively tested. Use at your own risk.
|
|
10
|
+
> ⚠️ **Alpha** — 此为初版,未经充分测试,请谨慎使用。
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## English
|
|
15
|
+
|
|
16
|
+
### Overview
|
|
17
|
+
|
|
18
|
+
`opencode-cron-job` is an OpenCode plugin that automatically reads cron tasks from `.cron-job/tasks.md` in your project root and injects prompts into your session on schedule. No MCP server, no HTTP API, no sidecar — runs entirely inside OpenCode.
|
|
19
|
+
|
|
20
|
+
### How it works
|
|
21
|
+
|
|
22
|
+
1. Create `.cron-job/tasks.md` in your project
|
|
23
|
+
2. Write tasks using simple markdown format
|
|
24
|
+
3. OpenCode loads the plugin automatically → reads the file → starts timers
|
|
25
|
+
4. When a timer fires, the prompt is injected into your session
|
|
26
|
+
|
|
27
|
+
### Install
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"plugin": ["opencode-cron-job@latest"]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
OpenCode auto-installs npm plugins on startup.
|
|
36
|
+
|
|
37
|
+
### Usage
|
|
38
|
+
|
|
39
|
+
Edit `.cron-job/tasks.md` to define your cron tasks. The plugin automatically loads them on startup.
|
|
40
|
+
|
|
41
|
+
To manage scheduled tasks, use natural language with the AI agent:
|
|
42
|
+
|
|
43
|
+
- "List all my cron jobs"
|
|
44
|
+
- "Run the code review task now"
|
|
45
|
+
- "Delete the backup task"
|
|
46
|
+
- "Reload the cron tasks from the file"
|
|
47
|
+
|
|
48
|
+
The agent calls the following tools internally — you don't need to use them directly:
|
|
49
|
+
|
|
50
|
+
| Tool | Description |
|
|
51
|
+
|------|-------------|
|
|
52
|
+
| `cron_create` | Create a cron job (name, schedule, prompt) |
|
|
53
|
+
| `cron_list` | List all scheduled tasks |
|
|
54
|
+
| `cron_run` | Run a task immediately |
|
|
55
|
+
| `cron_delete` | Delete a task |
|
|
56
|
+
|
|
57
|
+
### Task file format
|
|
58
|
+
|
|
59
|
+
Create `.cron-job/tasks.md`:
|
|
60
|
+
|
|
61
|
+
```markdown
|
|
62
|
+
# Cron Tasks
|
|
63
|
+
|
|
64
|
+
## Daily Code Review
|
|
65
|
+
- cron: 0 9 * * *
|
|
66
|
+
- prompt: Review all changed files and generate a report
|
|
67
|
+
|
|
68
|
+
## Hourly Backup
|
|
69
|
+
- cron: 0 */1 * * *
|
|
70
|
+
- prompt: Backup the database
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Each section starting with `##` is a task. Supported fields:
|
|
74
|
+
- `cron` — standard 5-field cron expression (supports 6-field with seconds)
|
|
75
|
+
- `prompt` — the prompt text to inject when the timer fires
|
|
76
|
+
|
|
77
|
+
### Update
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Clear plugin cache and restart OpenCode
|
|
81
|
+
rm -rf ~/.cache/opencode/node_modules/opencode-cron-job
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 中文
|
|
87
|
+
|
|
88
|
+
### 概述
|
|
89
|
+
|
|
90
|
+
`opencode-cron-job` 是一个 OpenCode 插件,自动读取项目根目录下的 `.cron-job/tasks.md` 文件,按 cron 表达式定时向当前会话注入 prompt。无需 MCP 服务、无需 HTTP API、无需 sidecar 进程,完全在 OpenCode 内部运行。
|
|
91
|
+
|
|
92
|
+
### 工作方式
|
|
93
|
+
|
|
94
|
+
1. 在项目根目录创建 `.cron-job/tasks.md`
|
|
95
|
+
2. 用简单的 markdown 格式编写任务
|
|
96
|
+
3. OpenCode 启动时自动加载插件 → 读取文件 → 启动定时器
|
|
97
|
+
4. 到时间自动向会话注入 prompt
|
|
98
|
+
|
|
99
|
+
### 安装
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"plugin": ["opencode-cron-job@latest"]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
OpenCode 启动时自动安装 npm 插件。
|
|
108
|
+
|
|
109
|
+
### 使用方式
|
|
110
|
+
|
|
111
|
+
编辑 `.cron-job/tasks.md` 定义定时任务,插件启动时自动加载。
|
|
112
|
+
|
|
113
|
+
要管理任务,直接对 AI 说自然语言即可,例如:
|
|
114
|
+
|
|
115
|
+
- "列出所有周期任务"
|
|
116
|
+
- "立即执行一下代码检查"
|
|
117
|
+
- "把备份任务删掉"
|
|
118
|
+
- "重新加载任务文件"
|
|
119
|
+
|
|
120
|
+
Agent 内部会调用以下工具来完成,你不需要直接使用:
|
|
121
|
+
|
|
122
|
+
| 工具 | 说明 |
|
|
123
|
+
|------|------|
|
|
124
|
+
| `cron_create` | 创建周期任务(名称、cron 表达式、prompt) |
|
|
125
|
+
| `cron_list` | 列出所有任务 |
|
|
126
|
+
| `cron_run` | 立即执行一个任务 |
|
|
127
|
+
| `cron_delete` | 删除任务 |
|
|
128
|
+
|
|
129
|
+
### 任务文件格式
|
|
130
|
+
|
|
131
|
+
创建 `.cron-job/tasks.md`:
|
|
132
|
+
|
|
133
|
+
```markdown
|
|
134
|
+
# 周期任务
|
|
135
|
+
|
|
136
|
+
## 每日代码检查
|
|
137
|
+
- cron: 0 9 * * *
|
|
138
|
+
- prompt: 检查所有变更文件并生成报告
|
|
139
|
+
|
|
140
|
+
## 每小时备份
|
|
141
|
+
- cron: 0 */1 * * *
|
|
142
|
+
- prompt: 备份数据库
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
每个以 `##` 开头的区块为一个任务。支持字段:
|
|
146
|
+
- `cron` — 标准 5 段 cron 表达式(也支持含秒的 6 段格式)
|
|
147
|
+
- `prompt` — 定时触发时注入的 prompt 内容
|
|
148
|
+
|
|
149
|
+
### 更新
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# 清除插件缓存后重启 OpenCode
|
|
153
|
+
rm -rf ~/.cache/opencode/node_modules/opencode-cron-job
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## License / 许可证
|
|
159
|
+
|
|
160
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-cron-job",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenCode plugin - schedule recurring prompts via markdown files",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"node-cron": "^3.0.3"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@opencode-ai/plugin": "*"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@opencode-ai/plugin": "^1.15.5",
|
|
22
|
+
"@types/node": "^22.0.0",
|
|
23
|
+
"@types/node-cron": "^3.0.11",
|
|
24
|
+
"typescript": "^5.8.0"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT"
|
|
30
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs"
|
|
2
|
+
import { join } from "path"
|
|
3
|
+
import cron from "node-cron"
|
|
4
|
+
import { tool, type Plugin } from "@opencode-ai/plugin"
|
|
5
|
+
|
|
6
|
+
interface Job {
|
|
7
|
+
id: string
|
|
8
|
+
name: string
|
|
9
|
+
schedule: string
|
|
10
|
+
prompt: string
|
|
11
|
+
task: cron.ScheduledTask | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseTasks(content: string): { name: string; schedule: string; prompt: string }[] {
|
|
15
|
+
const tasks: { name: string; schedule: string; prompt: string }[] = []
|
|
16
|
+
const sections = content.split(/^## /m).filter((s) => s.trim())
|
|
17
|
+
for (const section of sections) {
|
|
18
|
+
const lines = section.split("\n")
|
|
19
|
+
const name = lines[0]?.trim()
|
|
20
|
+
if (!name) continue
|
|
21
|
+
let schedule = "", prompt = ""
|
|
22
|
+
for (const line of lines.slice(1)) {
|
|
23
|
+
const t = line.trim()
|
|
24
|
+
if (t.startsWith("- cron:")) schedule = t.slice("- cron:".length).trim()
|
|
25
|
+
else if (t.startsWith("- prompt:")) prompt = t.slice("- prompt:".length).trim()
|
|
26
|
+
}
|
|
27
|
+
if (schedule && prompt) tasks.push({ name, schedule, prompt })
|
|
28
|
+
}
|
|
29
|
+
return tasks
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let jobs: Job[] = []
|
|
33
|
+
let nextId = 1
|
|
34
|
+
|
|
35
|
+
export const CronPlugin: Plugin = async ({ client, directory }) => {
|
|
36
|
+
const tasksFile = join(directory, ".cron-job", "tasks.md")
|
|
37
|
+
|
|
38
|
+
function fire(job: Job) {
|
|
39
|
+
client.tui.appendPrompt({ body: { text: job.prompt } })
|
|
40
|
+
.then(() => client.tui.submitPrompt())
|
|
41
|
+
.catch(() => {})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function schedule(j: Job) {
|
|
45
|
+
if (cron.validate(j.schedule)) {
|
|
46
|
+
j.task = cron.schedule(j.schedule, () => fire(j))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Load from file on startup
|
|
51
|
+
if (existsSync(tasksFile)) {
|
|
52
|
+
const items = parseTasks(readFileSync(tasksFile, "utf-8"))
|
|
53
|
+
for (const item of items) {
|
|
54
|
+
const job: Job = { ...item, id: String(nextId++), task: null }
|
|
55
|
+
schedule(job)
|
|
56
|
+
jobs.push(job)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
tool: {
|
|
62
|
+
cron_create: tool({
|
|
63
|
+
description: "Create a new cron job. The job fires on schedule by injecting the prompt into the user's session. Jobs are ephemeral (in-memory) and lost when OpenCode restarts. To persist a job permanently, also add it to .cron-job/tasks.md so it auto-loads on startup.",
|
|
64
|
+
args: {
|
|
65
|
+
name: tool.schema.string().describe("Unique name for this job"),
|
|
66
|
+
schedule: tool.schema.string().describe("Cron expression. 5-field format (min hour dom mon dow), e.g. '0 9 * * *' for daily at 9am. Supports 6-field with seconds: '*/30 * * * * *' for every 30s."),
|
|
67
|
+
prompt: tool.schema.string().describe("The prompt text that gets injected into the session when the job fires. The AI will receive this as a user message and act on it."),
|
|
68
|
+
},
|
|
69
|
+
async execute(args) {
|
|
70
|
+
const job: Job = { id: String(nextId++), name: args.name, schedule: args.schedule, prompt: args.prompt, task: null }
|
|
71
|
+
schedule(job)
|
|
72
|
+
jobs.push(job)
|
|
73
|
+
return `Created job: ${job.name} (ID: ${job.id}, cron: ${job.schedule})`
|
|
74
|
+
},
|
|
75
|
+
}),
|
|
76
|
+
|
|
77
|
+
cron_list: tool({
|
|
78
|
+
description: "List all scheduled cron jobs",
|
|
79
|
+
args: {},
|
|
80
|
+
async execute() {
|
|
81
|
+
if (jobs.length === 0) return "No jobs scheduled."
|
|
82
|
+
return jobs.map((j) => `${j.id} ${j.schedule} ${j.name}`).join("\n")
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
|
|
86
|
+
cron_run: tool({
|
|
87
|
+
description: "Run a job immediately (fire-and-forget). The job fires once right now regardless of its cron schedule. Useful for testing or one-off execution. The job remains scheduled and will continue firing on its normal cron schedule afterwards.",
|
|
88
|
+
args: {
|
|
89
|
+
jobId: tool.schema.string().describe("ID of the job to run. Get it from cron_list."),
|
|
90
|
+
},
|
|
91
|
+
async execute(args) {
|
|
92
|
+
const job = jobs.find((j) => j.id === args.jobId)
|
|
93
|
+
if (!job) return `Job ${args.jobId} not found.`
|
|
94
|
+
fire(job)
|
|
95
|
+
return `${job.name}: triggered`
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
|
|
99
|
+
cron_delete: tool({
|
|
100
|
+
description: "Delete a cron job. Stops the timer and removes it from memory. This does not modify .cron-job/tasks.md — if the job was defined there, it will reappear after OpenCode restarts.",
|
|
101
|
+
args: {
|
|
102
|
+
jobId: tool.schema.string().describe("ID of the job to delete. Get it from cron_list."),
|
|
103
|
+
},
|
|
104
|
+
async execute(args) {
|
|
105
|
+
const idx = jobs.findIndex((j) => j.id === args.jobId)
|
|
106
|
+
if (idx === -1) return `Job ${args.jobId} not found.`
|
|
107
|
+
const [job] = jobs.splice(idx, 1)
|
|
108
|
+
job.task?.stop()
|
|
109
|
+
return `${job.name}: deleted`
|
|
110
|
+
},
|
|
111
|
+
}),
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"sourceMap": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|
|
18
|
+
|