opencode-jobs 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/LICENSE +21 -0
- package/README.md +204 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +405 -0
- package/dist/cron.d.ts +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1312 -0
- package/dist/install.d.ts +46 -0
- package/dist/internals.d.ts +19 -0
- package/dist/job.d.ts +43 -0
- package/dist/json.d.ts +4 -0
- package/dist/paths.d.ts +24 -0
- package/dist/project.d.ts +2 -0
- package/dist/registry.d.ts +14 -0
- package/dist/runs.d.ts +16 -0
- package/dist/systemd.d.ts +26 -0
- package/dist/tools.d.ts +2 -0
- package/package.json +76 -0
- package/skill/opencode-jobs/SKILL.md +51 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const PACKAGE_NAME = "opencode-jobs";
|
|
2
|
+
export declare const SKILL_NAME = "opencode-jobs";
|
|
3
|
+
export type ConfigInstall = {
|
|
4
|
+
status: "added" | "present";
|
|
5
|
+
configPath: string;
|
|
6
|
+
} | {
|
|
7
|
+
status: "manual";
|
|
8
|
+
configPath: string;
|
|
9
|
+
};
|
|
10
|
+
export interface SkillInstall {
|
|
11
|
+
status: "written" | "unchanged";
|
|
12
|
+
skillPath: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ProjectInstall {
|
|
15
|
+
projectDirectory: string;
|
|
16
|
+
plugin: ConfigInstall;
|
|
17
|
+
skill: SkillInstall;
|
|
18
|
+
}
|
|
19
|
+
export declare function installPluginConfig(projectDirectory: string): ConfigInstall;
|
|
20
|
+
export declare function installSkill(projectDirectory: string, packageDirectory: string): SkillInstall;
|
|
21
|
+
export declare function installProject(projectDirectory: string, packageDirectory: string): ProjectInstall;
|
|
22
|
+
export type ConfigUninstall = {
|
|
23
|
+
status: "removed" | "absent";
|
|
24
|
+
configPath: string;
|
|
25
|
+
} | {
|
|
26
|
+
status: "manual";
|
|
27
|
+
configPath: string;
|
|
28
|
+
};
|
|
29
|
+
export interface SkillUninstall {
|
|
30
|
+
status: "removed" | "absent" | "kept-modified";
|
|
31
|
+
skillPath: string;
|
|
32
|
+
}
|
|
33
|
+
export interface PurgeResult {
|
|
34
|
+
paths: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface ProjectUninstall {
|
|
37
|
+
projectDirectory: string;
|
|
38
|
+
disabled: boolean;
|
|
39
|
+
plugin: ConfigUninstall;
|
|
40
|
+
skill: SkillUninstall;
|
|
41
|
+
purge?: PurgeResult;
|
|
42
|
+
}
|
|
43
|
+
export declare function uninstallPluginConfig(projectDirectory: string): ConfigUninstall;
|
|
44
|
+
export declare function uninstallSkill(projectDirectory: string, packageDirectory: string): SkillUninstall;
|
|
45
|
+
export declare function purgeProjectData(projectDirectory: string): PurgeResult;
|
|
46
|
+
export declare function uninstallProject(projectDirectory: string, packageDirectory: string, shouldPurge: boolean): ProjectUninstall;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { deriveScopeId, shQuote, slugify } from "./paths.js";
|
|
2
|
+
import { cronToOnCalendar, describeCron, parseCron } from "./cron.js";
|
|
3
|
+
import { validateGuard, validateRunSpec, validateSession } from "./job.js";
|
|
4
|
+
import { buildOpencodeArguments, runScriptContent, serviceContent, timerContent } from "./systemd.js";
|
|
5
|
+
export declare const internals: {
|
|
6
|
+
slugify: typeof slugify;
|
|
7
|
+
shQuote: typeof shQuote;
|
|
8
|
+
deriveScopeId: typeof deriveScopeId;
|
|
9
|
+
parseCron: typeof parseCron;
|
|
10
|
+
cronToOnCalendar: typeof cronToOnCalendar;
|
|
11
|
+
describeCron: typeof describeCron;
|
|
12
|
+
validateRunSpec: typeof validateRunSpec;
|
|
13
|
+
validateGuard: typeof validateGuard;
|
|
14
|
+
validateSession: typeof validateSession;
|
|
15
|
+
buildOpencodeArguments: typeof buildOpencodeArguments;
|
|
16
|
+
runScriptContent: typeof runScriptContent;
|
|
17
|
+
serviceContent: typeof serviceContent;
|
|
18
|
+
timerContent: typeof timerContent;
|
|
19
|
+
};
|
package/dist/job.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
interface RunOptions {
|
|
2
|
+
agent?: string;
|
|
3
|
+
model?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface PromptRun extends RunOptions {
|
|
6
|
+
prompt: string;
|
|
7
|
+
}
|
|
8
|
+
export interface CommandRun extends RunOptions {
|
|
9
|
+
command: string;
|
|
10
|
+
arguments?: string;
|
|
11
|
+
}
|
|
12
|
+
export type RunSpec = PromptRun | CommandRun;
|
|
13
|
+
export declare const SESSION_MODES: readonly ["new", "persist", "compact", "compact+last"];
|
|
14
|
+
export type SessionMode = (typeof SESSION_MODES)[number];
|
|
15
|
+
export interface Job {
|
|
16
|
+
slug: string;
|
|
17
|
+
name: string;
|
|
18
|
+
schedule: string;
|
|
19
|
+
run: RunSpec;
|
|
20
|
+
session?: SessionMode;
|
|
21
|
+
guard?: string;
|
|
22
|
+
timeoutSeconds?: number;
|
|
23
|
+
createdAt: string;
|
|
24
|
+
updatedAt: string;
|
|
25
|
+
}
|
|
26
|
+
export type JobResult = {
|
|
27
|
+
ok: true;
|
|
28
|
+
job: Job;
|
|
29
|
+
} | {
|
|
30
|
+
ok: false;
|
|
31
|
+
error: string;
|
|
32
|
+
};
|
|
33
|
+
export declare function validateRunSpec(run: unknown, context: string): RunSpec;
|
|
34
|
+
export declare function validateSession(value: unknown, context: string): SessionMode;
|
|
35
|
+
export declare function validateTimeout(value: unknown, context: string): number | undefined;
|
|
36
|
+
export declare function validateGuard(value: unknown, context: string): string | undefined;
|
|
37
|
+
export declare function loadJobFile(file: string, expectedSlug?: string): JobResult;
|
|
38
|
+
export declare function loadJobs(workdir: string): {
|
|
39
|
+
jobs: Job[];
|
|
40
|
+
errors: string[];
|
|
41
|
+
};
|
|
42
|
+
export declare function saveJob(workdir: string, job: Job): void;
|
|
43
|
+
export {};
|
package/dist/json.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
2
|
+
export declare function stringProperty(record: Record<string, unknown>, key: string): string | undefined;
|
|
3
|
+
export declare function numberProperty(record: Record<string, unknown>, key: string): number | undefined;
|
|
4
|
+
export declare function errorMessage(error: unknown): string;
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare function configRoot(): string;
|
|
2
|
+
export declare function schedulerDirectory(): string;
|
|
3
|
+
export declare function registryPath(): string;
|
|
4
|
+
export declare function scopeDirectory(scopeId: string): string;
|
|
5
|
+
export declare function runsDirectory(scopeId: string): string;
|
|
6
|
+
export declare function runsFile(scopeId: string, slug: string): string;
|
|
7
|
+
export declare function sessionStateDirectory(scopeId: string): string;
|
|
8
|
+
export declare function sessionStateFile(scopeId: string, slug: string): string;
|
|
9
|
+
export declare function logDirectory(scopeId: string): string;
|
|
10
|
+
export declare function logFile(scopeId: string, slug: string): string;
|
|
11
|
+
export declare function systemdUserDirectory(): string;
|
|
12
|
+
export declare function jobsDirectory(workdir: string): string;
|
|
13
|
+
export declare function unitBase(scopeId: string, slug: string): string;
|
|
14
|
+
export declare function timerUnit(base: string): string;
|
|
15
|
+
export declare function serviceUnit(base: string): string;
|
|
16
|
+
export declare function runScriptPath(scopeId: string, slug: string): string;
|
|
17
|
+
export declare function slugify(input: string): string;
|
|
18
|
+
export declare function shQuote(value: string): string;
|
|
19
|
+
export declare function unitQuote(value: string): string;
|
|
20
|
+
export declare function escapeUnitText(value: string): string;
|
|
21
|
+
export declare function atomicWrite(file: string, content: string): void;
|
|
22
|
+
export declare function atomicWriteExecutable(file: string, content: string): void;
|
|
23
|
+
export declare function nowIso(): string;
|
|
24
|
+
export declare function deriveScopeId(workdir: string): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface RegistryEntry {
|
|
2
|
+
scopeId: string;
|
|
3
|
+
workdir: string;
|
|
4
|
+
enabledAt: string;
|
|
5
|
+
updatedAt: string;
|
|
6
|
+
jobs: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface Registry {
|
|
9
|
+
version: 1;
|
|
10
|
+
projects: Record<string, RegistryEntry>;
|
|
11
|
+
}
|
|
12
|
+
export declare function loadRegistry(): Registry;
|
|
13
|
+
export declare function saveRegistry(registry: Registry): void;
|
|
14
|
+
export declare function registryEntry(workdir: string): RegistryEntry | undefined;
|
package/dist/runs.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface RunRecord {
|
|
2
|
+
runId?: string;
|
|
3
|
+
slug?: string;
|
|
4
|
+
scopeId?: string;
|
|
5
|
+
startedAt?: number;
|
|
6
|
+
finishedAt?: number;
|
|
7
|
+
durationMs?: number;
|
|
8
|
+
status?: string;
|
|
9
|
+
exitCode?: number;
|
|
10
|
+
sessionId?: string;
|
|
11
|
+
startedBy?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function readRunRecords(scopeId: string, slug: string, limit: number): RunRecord[];
|
|
14
|
+
export declare function lastFinishedRun(records: RunRecord[]): RunRecord | undefined;
|
|
15
|
+
export declare function formatRunLine(record: RunRecord): string;
|
|
16
|
+
export declare function tailFile(file: string, lines: number, maxChars: number): string | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Job } from "./job.js";
|
|
2
|
+
export declare function findOpencode(): string;
|
|
3
|
+
export declare function buildOpencodeArguments(job: Job, sessionId?: string): string[];
|
|
4
|
+
export declare function runScriptContent(job: Job, scopeId: string, opencodeBin: string): string;
|
|
5
|
+
export declare function serviceContent(job: Job, options: {
|
|
6
|
+
workdir: string;
|
|
7
|
+
runScript: string;
|
|
8
|
+
log: string;
|
|
9
|
+
pathEnvironment: string;
|
|
10
|
+
}): string;
|
|
11
|
+
export declare function timerContent(job: Job, onCalendars: string[]): string;
|
|
12
|
+
export interface SystemctlResult {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
stdout: string;
|
|
15
|
+
stderr: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function systemctl(systemctlArguments: string[]): SystemctlResult;
|
|
18
|
+
export declare function systemdHint(stderr: string): string;
|
|
19
|
+
export interface TimerStatus {
|
|
20
|
+
next: string | undefined;
|
|
21
|
+
last: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
export declare function timerStatus(base: string): TimerStatus;
|
|
24
|
+
export declare function writeJobUnits(job: Job, workdir: string, scopeId: string, opencodeBin: string, pathEnvironment: string): string;
|
|
25
|
+
export declare function removeJobUnits(scopeId: string, slug: string): void;
|
|
26
|
+
export declare function removeStaleUnits(scopeId: string, expectedSlugs: Set<string>): string[];
|
package/dist/tools.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-jobs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "opencode plugin that schedules recurring agent jobs as systemd user timers, with git-committable job definitions, run history, and session continuity",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"bin": {
|
|
12
|
+
"opencode-jobs": "dist/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"skill",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "bun build src/index.ts --outdir dist --target bun --format esm --external @opencode-ai/plugin && bun build src/cli.ts --outfile dist/cli.js --target node --format esm && tsc -p tsconfig.build.json",
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
30
|
+
"format": "prettier --write .",
|
|
31
|
+
"lint": "eslint",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"check": "prettier --check . && eslint && tsc --noEmit",
|
|
34
|
+
"smoke": "npm run build && node scripts/smoke.mjs && node scripts/cli-smoke.mjs",
|
|
35
|
+
"release:patch": "npm version patch && npm publish",
|
|
36
|
+
"release:minor": "npm version minor && npm publish",
|
|
37
|
+
"release:major": "npm version major && npm publish"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"opencode",
|
|
41
|
+
"opencode-plugin",
|
|
42
|
+
"plugin",
|
|
43
|
+
"scheduler",
|
|
44
|
+
"cron",
|
|
45
|
+
"jobs",
|
|
46
|
+
"timers",
|
|
47
|
+
"systemd",
|
|
48
|
+
"automation",
|
|
49
|
+
"agent"
|
|
50
|
+
],
|
|
51
|
+
"author": "Connor Fraser-Grant",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/Fraser-Grant/opencode-jobs.git"
|
|
56
|
+
},
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/Fraser-Grant/opencode-jobs/issues"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://github.com/Fraser-Grant/opencode-jobs#readme",
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@opencode-ai/plugin": "^1.18.18"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"@opencode-ai/plugin": ">=1.0.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@types/node": "^26.2.0",
|
|
70
|
+
"eslint": "^10.9.0",
|
|
71
|
+
"eslint-plugin-unicorn": "^73.0.0",
|
|
72
|
+
"prettier": "^3.9.6",
|
|
73
|
+
"typescript": "^5.9.3",
|
|
74
|
+
"typescript-eslint": "^8.67.0"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opencode-jobs
|
|
3
|
+
description: Schedule and manage recurring OpenCode agent jobs with project-local definitions and systemd user timers. Use when asked to schedule, inspect, run, enable, disable, or troubleshoot recurring jobs and their logs.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: opencode on Linux with systemd
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# OpenCode Jobs
|
|
9
|
+
|
|
10
|
+
Use the `opencode-jobs` tools instead of editing systemd units or global
|
|
11
|
+
scheduler state directly.
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
1. Create or update a definition with `schedule_job`. Definitions belong in
|
|
16
|
+
`.opencode/scheduler/jobs/<slug>.json` and should be committed with the
|
|
17
|
+
project.
|
|
18
|
+
2. Inspect definitions with `list_jobs` or `get_job`.
|
|
19
|
+
3. Run `enable_project` after the first job is created. Run it again to
|
|
20
|
+
reconcile timers after externally editing definitions.
|
|
21
|
+
4. Use `run_job` for a manual fire-and-forget run and `job_logs` to inspect
|
|
22
|
+
its output.
|
|
23
|
+
5. Use `disable_project` to remove timers while retaining definitions,
|
|
24
|
+
history, and logs. Use `delete_job` only when the definition should also
|
|
25
|
+
be removed.
|
|
26
|
+
|
|
27
|
+
## Scheduling Rules
|
|
28
|
+
|
|
29
|
+
- Schedules are five-field cron expressions: minute, hour, day of month,
|
|
30
|
+
month, day of week.
|
|
31
|
+
- Set exactly one of `prompt` or `command`. `arguments` only applies to a
|
|
32
|
+
custom command.
|
|
33
|
+
- Default to `session: new`. Use `persist` when full continuity is needed,
|
|
34
|
+
`compact` for summary continuity, or `compact+last` when the previous final
|
|
35
|
+
result must also remain visible.
|
|
36
|
+
- A `guard` is a shell command that must exit zero for the job to run.
|
|
37
|
+
- Scheduled jobs require Linux, a systemd user session, and an `opencode`
|
|
38
|
+
executable available to the timer environment.
|
|
39
|
+
|
|
40
|
+
## Project Installation
|
|
41
|
+
|
|
42
|
+
When the plugin or this skill is missing, install both from a globally
|
|
43
|
+
installed package:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
opencode-jobs install /path/to/project # add plugin entry + skill
|
|
47
|
+
opencode-jobs uninstall /path/to/project # reverse (add --purge to wipe data)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The commands default to the current directory. Restart OpenCode after
|
|
51
|
+
installation so it loads the plugin and discovers the skill.
|