@openagt/skill-scheduler 0.0.0-stage → 0.1.11
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/SKILL.md +28 -0
- package/bin/scheduler +11 -0
- package/dist/automation-file.d.ts +61 -0
- package/dist/automation-file.d.ts.map +1 -0
- package/dist/automation-file.js +153 -0
- package/dist/automation-file.js.map +1 -0
- package/dist/automation.d.ts +189 -0
- package/dist/automation.d.ts.map +1 -0
- package/dist/automation.js +237 -0
- package/dist/automation.js.map +1 -0
- package/dist/cleanup.d.ts +31 -0
- package/dist/cleanup.d.ts.map +1 -0
- package/dist/cleanup.js +20 -0
- package/dist/cleanup.js.map +1 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +409 -0
- package/dist/cli.js.map +1 -0
- package/dist/dashboard/dashboard.css +3 -0
- package/dist/dashboard/dashboard.js +2324 -0
- package/dist/hook-lines.d.ts +11 -0
- package/dist/hook-lines.d.ts.map +1 -0
- package/dist/hook-lines.js +12 -0
- package/dist/hook-lines.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +40 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +95 -0
- package/dist/init.js.map +1 -0
- package/dist/names.d.ts +85 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +91 -0
- package/dist/names.js.map +1 -0
- package/dist/pace.d.ts +86 -0
- package/dist/pace.d.ts.map +1 -0
- package/dist/pace.js +99 -0
- package/dist/pace.js.map +1 -0
- package/dist/quota-boundary.d.ts +123 -0
- package/dist/quota-boundary.d.ts.map +1 -0
- package/dist/quota-boundary.js +181 -0
- package/dist/quota-boundary.js.map +1 -0
- package/dist/records.d.ts +45 -0
- package/dist/records.d.ts.map +1 -0
- package/dist/records.js +85 -0
- package/dist/records.js.map +1 -0
- package/dist/schedule.d.ts +256 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +390 -0
- package/dist/schedule.js.map +1 -0
- package/dist/scheduler.d.ts +75 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +244 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/start-point.d.ts +40 -0
- package/dist/start-point.d.ts.map +1 -0
- package/dist/start-point.js +88 -0
- package/dist/start-point.js.map +1 -0
- package/dist/state.d.ts +194 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +208 -0
- package/dist/state.js.map +1 -0
- package/dist/test-repo.d.ts +20 -0
- package/dist/test-repo.d.ts.map +1 -0
- package/dist/test-repo.js +56 -0
- package/dist/test-repo.js.map +1 -0
- package/dist/tick.d.ts +122 -0
- package/dist/tick.d.ts.map +1 -0
- package/dist/tick.js +334 -0
- package/dist/tick.js.map +1 -0
- package/dist/write-line.d.ts +38 -0
- package/dist/write-line.d.ts.map +1 -0
- package/dist/write-line.js +121 -0
- package/dist/write-line.js.map +1 -0
- package/package.json +65 -3
- package/README.md +0 -3
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile, realpath, rename, rmdir, unlink, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { excludeFromGit, originDefaultBranch } from '@openagt/agent-data';
|
|
4
|
+
import { HARNESS_SKILL_DIRS } from '@openagt/skill-branches';
|
|
5
|
+
import { MAX_NAME, NEVER_STARTED_SINCE_MS, OWN_AUTOMATIONS_DIR, OWN_TEXT_MAX, RUN_SKILLS_DIR, SKILL_FILE, STATE_DIR, TRY_TIMEOUT_MS, isCommandName } from './names.js';
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
import { FOLDER_LITTER, HALF_SAVED, automationOf, automationSkill, frontMatterOf, wellFormed } from './automation-file.js';
|
|
8
|
+
import { foundOutput, isDue, lastRunValue, readSchedule, skillFile, skillSchedule } from './schedule.js';
|
|
9
|
+
import { runCheck } from './tick.js';
|
|
10
|
+
/**
|
|
11
|
+
* Why an automation cannot be saved as written, or nothing: its name, its prompt, and its
|
|
12
|
+
* schedule by the very reader the tick reads skills with. What the reader reads back has to be
|
|
13
|
+
* what was typed, and the file has to read the same to a coding agent, whose reader ends the
|
|
14
|
+
* front matter at the first three dashes it meets anywhere.
|
|
15
|
+
*/
|
|
16
|
+
export function automationProblem(automation) {
|
|
17
|
+
if (!isCommandName(automation.name))
|
|
18
|
+
return { ok: false, reason: 'bad-name', detail: 'a name is lower-case letters, digits and single or double dashes, and starts with a letter or a digit' };
|
|
19
|
+
if (automation.name.length > MAX_NAME)
|
|
20
|
+
return { ok: false, reason: 'bad-name', detail: `a name is ${MAX_NAME} characters at most` };
|
|
21
|
+
// A NUL character is no text: a file holds none of it for an agent.
|
|
22
|
+
if (!automation.prompt.replaceAll('\0', '').trim())
|
|
23
|
+
return { ok: false, reason: 'no-prompt' };
|
|
24
|
+
const text = automationSkill(automation);
|
|
25
|
+
const read = skillSchedule(automation.name, text, RUN_SKILLS_DIR);
|
|
26
|
+
const unreadable = read.unreadable[0];
|
|
27
|
+
if (unreadable)
|
|
28
|
+
return { ok: false, reason: 'bad-schedule', detail: unreadable.reason };
|
|
29
|
+
const command = read.commands[0];
|
|
30
|
+
const when = automation.when === undefined ? undefined : wellFormed(automation.when.trim());
|
|
31
|
+
const waitsFor = automation.waitsFor === undefined ? undefined : wellFormed(automation.waitsFor.trim());
|
|
32
|
+
if (!command || command.when !== when || command.waitsFor !== waitsFor || frontMatterOf(text).includes('---'))
|
|
33
|
+
return { ok: false, reason: 'bad-schedule', detail: 'it would not read back as typed' };
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/** A name for what a file says: the same text has the same name, and any change another. */
|
|
37
|
+
function versionOf(text) {
|
|
38
|
+
return createHash('sha256').update(text).digest('hex').slice(0, 16);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Save an automation, shared with the project or kept on this machine alone.
|
|
42
|
+
*
|
|
43
|
+
* Shared, it is a skill file of the project, which the person commits; like any skill, its command
|
|
44
|
+
* starts no run before the file is on the commit a run's checkout starts from. Kept on this
|
|
45
|
+
* machine, it is one file in the tool's own folder, hidden from git like the state: nobody else
|
|
46
|
+
* gets the row, nothing is to commit, and a run of it is handed its text.
|
|
47
|
+
*
|
|
48
|
+
* Refused when it cannot be read back as written, and when its name is taken, whatever its
|
|
49
|
+
* capitals: by a skill in any folder a coding agent reads skills from, or by an automation kept on
|
|
50
|
+
* this machine. An automation never replaces either, and on a disk that ignores capitals `Foo` and
|
|
51
|
+
* `foo` are one name. A write that fails leaves no empty folder behind, shared or kept here.
|
|
52
|
+
*/
|
|
53
|
+
export async function addAutomation(repo, automation, git, opts = {}) {
|
|
54
|
+
const write = opts.write ?? writeFile;
|
|
55
|
+
const problem = automationProblem(automation);
|
|
56
|
+
if (problem)
|
|
57
|
+
return problem;
|
|
58
|
+
for (const dir of HARNESS_SKILL_DIRS) {
|
|
59
|
+
const there = (await readdir(join(repo, dir)).catch(() => [])).find(name => name.toLowerCase() === automation.name);
|
|
60
|
+
if (there !== undefined)
|
|
61
|
+
return { ok: false, reason: 'taken', folder: `${dir}/${there}` };
|
|
62
|
+
}
|
|
63
|
+
const kept = (await readdir(join(repo, OWN_AUTOMATIONS_DIR)).catch(() => [])).find(file => file.toLowerCase() === `${automation.name}.md`);
|
|
64
|
+
if (kept !== undefined)
|
|
65
|
+
return { ok: false, reason: 'taken', folder: `${OWN_AUTOMATIONS_DIR}/${kept}` };
|
|
66
|
+
const origin = await originDefaultBranch(repo, git).catch(() => undefined);
|
|
67
|
+
const startsFrom = origin ?? 'HEAD';
|
|
68
|
+
if (opts.onThisMachine) {
|
|
69
|
+
// Its text travels to each run as one command-line argument: a longer one could be saved and never listed.
|
|
70
|
+
const length = automation.prompt.trim().length;
|
|
71
|
+
if (length > OWN_TEXT_MAX)
|
|
72
|
+
return { ok: false, reason: 'long-prompt', detail: `the prompt is ${length} characters, and one kept on this machine has ${OWN_TEXT_MAX} at most` };
|
|
73
|
+
const file = `${OWN_AUTOMATIONS_DIR}/${automation.name}.md`;
|
|
74
|
+
await mkdir(join(repo, OWN_AUTOMATIONS_DIR), { recursive: true });
|
|
75
|
+
// The tool's folder is hidden from git before anything of a person's is in it; best-effort, as for the state.
|
|
76
|
+
await excludeFromGit(repo, `/${STATE_DIR}`, undefined, git).catch(() => { });
|
|
77
|
+
try {
|
|
78
|
+
await write(join(repo, file), automationSkill(automation), { flag: 'wx' });
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
// Another save made it since the look above.
|
|
82
|
+
if (err.code === 'EEXIST')
|
|
83
|
+
return { ok: false, reason: 'taken', folder: file };
|
|
84
|
+
// The folder made for the first automation is not left empty behind a write that failed.
|
|
85
|
+
await rmdir(join(repo, OWN_AUTOMATIONS_DIR)).catch(() => { });
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
return { ok: true, command: automation.name, file, startsFrom, onThisMachine: true };
|
|
89
|
+
}
|
|
90
|
+
const folder = join(repo, RUN_SKILLS_DIR, automation.name);
|
|
91
|
+
await mkdir(join(repo, RUN_SKILLS_DIR), { recursive: true });
|
|
92
|
+
try {
|
|
93
|
+
// Never into a folder that is there: another save may have made it since the look above.
|
|
94
|
+
await mkdir(folder);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (err.code === 'EEXIST')
|
|
98
|
+
return { ok: false, reason: 'taken', folder: `${RUN_SKILLS_DIR}/${automation.name}` };
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
await write(join(folder, SKILL_FILE), automationSkill(automation), { flag: 'wx' });
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
await rmdir(folder).catch(() => { });
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
return { ok: true, command: automation.name, file: `${RUN_SKILLS_DIR}/${automation.name}/${SKILL_FILE}`, startsFrom };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The automation of that name, where it is saved, or why the tool leaves what has the name alone.
|
|
112
|
+
*
|
|
113
|
+
* The tool rewrites and removes only what it wrote, and the schedule says what that is
|
|
114
|
+
* (`editable`): a command whose file reads as the tool writes one, in the tool's own folder, or
|
|
115
|
+
* alone in a skill folder of the project that is no link. A skill a person or a package wrote, and
|
|
116
|
+
* an automation changed by hand into something the tool does not write, are edited and removed by
|
|
117
|
+
* hand: rewriting one from here would lose what the tool does not write. What the schedule does
|
|
118
|
+
* not list is left alone too, with the schedule's own reason when it has one.
|
|
119
|
+
*/
|
|
120
|
+
export async function savedAutomation(repo, name) {
|
|
121
|
+
const none = (detail) => ({ ok: false, reason: 'not-an-automation', detail });
|
|
122
|
+
const schedule = await readSchedule(repo);
|
|
123
|
+
const command = schedule.commands.find(c => c.name === name);
|
|
124
|
+
if (!command) {
|
|
125
|
+
const unlisted = schedule.unreadable.find(u => u.skill === name);
|
|
126
|
+
if (!unlisted)
|
|
127
|
+
return none(`the schedule lists nothing named ${name}`);
|
|
128
|
+
return none(`${unlisted.own ? `the automation ${name}, kept on this machine, is not listed` : `the schedule of ${name} cannot be read`}: ${unlisted.reason}`);
|
|
129
|
+
}
|
|
130
|
+
const kept = command.text !== undefined;
|
|
131
|
+
const file = kept ? `${OWN_AUTOMATIONS_DIR}/${name}.md` : skillFile(command);
|
|
132
|
+
// The schedule says the file is the tool's to rewrite; what it holds is read from it now.
|
|
133
|
+
const text = command.editable ? await readFile(join(repo, file), 'utf8').catch(() => '') : '';
|
|
134
|
+
const automation = command.editable ? automationOf(name, text) : undefined;
|
|
135
|
+
if (automation)
|
|
136
|
+
return { automation, file, version: versionOf(text), ...(kept ? { onThisMachine: true } : {}) };
|
|
137
|
+
return none(kept ? `${file} is a link, or was changed by hand since it was saved: edit or remove the file itself` : `${dirname(file)} is a skill of the project, or an automation changed by hand since it was saved: edit or remove its files yourself`);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Save an automation again under its name, with a new prompt or a new schedule: its file is
|
|
141
|
+
* written anew, where it is, with what was given in place of what it said and the rest as it
|
|
142
|
+
* was. What a check waits for goes out with the check, unless it is given too. Its name stays,
|
|
143
|
+
* and a run is counted by the name, so its past runs, its switch and a person's other picks for
|
|
144
|
+
* it stay its own.
|
|
145
|
+
*
|
|
146
|
+
* A shared one is a file of the project: the change is the person's to commit, and a run reads
|
|
147
|
+
* the prompt from the commit its checkout starts from, so it is told the new words only once the
|
|
148
|
+
* change is there, while the tick reads the new pace and the new check from the person's checkout
|
|
149
|
+
* at once. One kept on this machine has only its file: the next tick reads all of it.
|
|
150
|
+
*
|
|
151
|
+
* Refused for a name that is no automation the tool wrote ({@link savedAutomation}), and for what
|
|
152
|
+
* could not be saved new either ({@link automationProblem}, the length of a text kept here). With
|
|
153
|
+
* `was`, the version whoever asks last read, it is refused too when the file says something else
|
|
154
|
+
* by now: a form opened a while ago does not write over a change made to the file by hand since.
|
|
155
|
+
*/
|
|
156
|
+
export async function editAutomation(repo, name, change, git, opts = {}) {
|
|
157
|
+
const found = await savedAutomation(repo, name);
|
|
158
|
+
if ('reason' in found)
|
|
159
|
+
return found;
|
|
160
|
+
if (opts.was !== undefined && opts.was !== found.version)
|
|
161
|
+
return { ok: false, reason: 'changed-since', detail: `${found.file} was changed since it was opened here: open it again to see what it says now` };
|
|
162
|
+
const was = found.automation;
|
|
163
|
+
const every = change.every === undefined ? was.every : (change.every ?? undefined);
|
|
164
|
+
const when = change.when === undefined ? was.when : (change.when ?? undefined);
|
|
165
|
+
const waitsFor = change.waitsFor === undefined ? (when === undefined ? undefined : was.waitsFor) : (change.waitsFor ?? undefined);
|
|
166
|
+
const automation = { name, prompt: change.prompt ?? was.prompt, ...(every !== undefined ? { every } : {}), ...(when !== undefined ? { when } : {}), ...(waitsFor !== undefined ? { waitsFor } : {}) };
|
|
167
|
+
const problem = automationProblem(automation);
|
|
168
|
+
if (problem)
|
|
169
|
+
return problem;
|
|
170
|
+
const length = automation.prompt.trim().length;
|
|
171
|
+
if (found.onThisMachine && length > OWN_TEXT_MAX)
|
|
172
|
+
return { ok: false, reason: 'long-prompt', detail: `the prompt is ${length} characters, and one kept on this machine has ${OWN_TEXT_MAX} at most` };
|
|
173
|
+
// Written beside the file and moved over it: a write that fails midway leaves the automation as it was, not a cut file.
|
|
174
|
+
const path = join(repo, found.file);
|
|
175
|
+
const text = automationSkill(automation);
|
|
176
|
+
try {
|
|
177
|
+
await (opts.write ?? writeFile)(`${path}.new`, text);
|
|
178
|
+
await rename(`${path}.new`, path);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
await unlink(`${path}.new`).catch(() => { });
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
const startsFrom = (await originDefaultBranch(repo, git).catch(() => undefined)) ?? 'HEAD';
|
|
185
|
+
return { ok: true, command: name, file: found.file, startsFrom, version: versionOf(text), ...(found.onThisMachine ? { onThisMachine: true } : {}) };
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Remove an automation: its file is deleted, and the folder that held it alone, with what a file
|
|
189
|
+
* manager left beside it. Nothing is committed, and nothing git holds is changed: the deletion of
|
|
190
|
+
* a shared one is a change of the person's where the file is in a commit, and everyone else keeps
|
|
191
|
+
* the command until the deletion reaches them. What was never committed, a shared one's file or
|
|
192
|
+
* its last changes, and all of one kept on this machine, cannot be brought back. The records of
|
|
193
|
+
* its past runs stay.
|
|
194
|
+
*
|
|
195
|
+
* Refused for a name that is no automation the tool wrote ({@link savedAutomation}).
|
|
196
|
+
*/
|
|
197
|
+
export async function removeAutomation(repo, name, git) {
|
|
198
|
+
const found = await savedAutomation(repo, name);
|
|
199
|
+
if ('reason' in found)
|
|
200
|
+
return found;
|
|
201
|
+
if (found.onThisMachine) {
|
|
202
|
+
await unlink(join(repo, found.file));
|
|
203
|
+
// The folder made for the first automation goes with the last one.
|
|
204
|
+
await rmdir(join(repo, OWN_AUTOMATIONS_DIR)).catch(() => { });
|
|
205
|
+
return { ok: true, command: name, file: found.file, onThisMachine: true };
|
|
206
|
+
}
|
|
207
|
+
// Git is asked about the file where it really is: the skills folder may be a link to another one.
|
|
208
|
+
const folder = join(await realpath(join(repo, RUN_SKILLS_DIR)), name);
|
|
209
|
+
await unlink(join(folder, SKILL_FILE));
|
|
210
|
+
for (const left of [FOLDER_LITTER, HALF_SAVED])
|
|
211
|
+
await unlink(join(folder, left)).catch(() => { });
|
|
212
|
+
await rmdir(folder).catch(() => { });
|
|
213
|
+
// One line for the one file, its first letter what the index holds: `A` for a file added and never committed.
|
|
214
|
+
const held = await git(['status', '--porcelain', '--', join(folder, SKILL_FILE)], repo).catch(() => undefined);
|
|
215
|
+
const startsFrom = (await originDefaultBranch(repo, git).catch(() => undefined)) ?? 'HEAD';
|
|
216
|
+
return { ok: true, command: name, file: found.file, git: held === undefined ? 'unknown' : held.trim() === '' ? 'nothing' : held.startsWith('A') ? 'staged' : 'committed', startsFrom };
|
|
217
|
+
}
|
|
218
|
+
/** How much of a failed check's last line of error is answered, in characters. */
|
|
219
|
+
const ERROR_MAX = 500;
|
|
220
|
+
/**
|
|
221
|
+
* Run a check once, as the tick would, and say what it printed and whether an agent would start:
|
|
222
|
+
* nothing is saved and nothing is started. It asks what is new since a day ago. A try has less
|
|
223
|
+
* time than a tick gives a check ({@link TRY_TIMEOUT_MS}): whoever asks, a dashboard, waits for
|
|
224
|
+
* the answer, and gives up before a tick would.
|
|
225
|
+
*/
|
|
226
|
+
export async function tryCheck(repo, shell, now, check = runCheck, budgetMs = TRY_TIMEOUT_MS) {
|
|
227
|
+
const lastRun = lastRunValue(new Date(now.getTime() - NEVER_STARTED_SINCE_MS).toISOString());
|
|
228
|
+
const started = Date.now();
|
|
229
|
+
const checked = await check(repo, shell, budgetMs, lastRun);
|
|
230
|
+
const printed = foundOutput(checked.stdout, lastRun);
|
|
231
|
+
if (checked.ok)
|
|
232
|
+
return { lastRun, ran: true, due: isDue(checked.stdout), printed };
|
|
233
|
+
const said = checked.stderr.trim().split('\n').at(-1) ?? '';
|
|
234
|
+
const error = Date.now() - started >= budgetMs ? `it took longer than ${budgetMs / 1000} seconds` : said.length > ERROR_MAX ? `${said.slice(0, ERROR_MAX)}…` : said;
|
|
235
|
+
return { lastRun, ran: false, due: false, printed, error };
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=automation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automation.js","sourceRoot":"","sources":["../src/automation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type GitRunner, type KeptEntry } from '@openagt/agent-data';
|
|
2
|
+
/**
|
|
3
|
+
* `cleanup`: what this tool left in a project, removed, for a person who takes the project out of
|
|
4
|
+
* a dashboard and asks for the tools' files to go too. The tool removes its own and nothing else:
|
|
5
|
+
* a dashboard asks for it by the `cleanup` kind the package declares under `openagent` in its
|
|
6
|
+
* package.json, and holds no list of this tool's files.
|
|
7
|
+
*
|
|
8
|
+
* What goes is the state file and the scheduler's log, then the directory once it is empty, then
|
|
9
|
+
* the rule hiding it from git (`removeOwnDirectory`). The automations a person keeps on this
|
|
10
|
+
* machine stay: they are the person's own writing and exist nowhere else, so the directory and its
|
|
11
|
+
* rule stay with them, and the answer names them among what was kept. Their folder, once empty,
|
|
12
|
+
* goes like the rest. While the state names a scheduler whose process
|
|
13
|
+
* is alive nothing is removed: it would write its state back on the next tick.
|
|
14
|
+
*/
|
|
15
|
+
export type CleanupOutcome = {
|
|
16
|
+
ok: true;
|
|
17
|
+
removed: string[];
|
|
18
|
+
kept: KeptEntry[];
|
|
19
|
+
}
|
|
20
|
+
/** The scheduler's process is alive on this machine: stop it first. */
|
|
21
|
+
| {
|
|
22
|
+
ok: false;
|
|
23
|
+
reason: 'running';
|
|
24
|
+
pid: number;
|
|
25
|
+
};
|
|
26
|
+
export interface CleanupDeps {
|
|
27
|
+
isAlive?: (pid: number) => boolean;
|
|
28
|
+
git?: GitRunner;
|
|
29
|
+
}
|
|
30
|
+
export declare function cleanup(repo: string, deps?: CleanupDeps): Promise<CleanupOutcome>;
|
|
31
|
+
//# sourceMappingURL=cleanup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqC,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAKvG;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,SAAS,EAAE,CAAA;CAAE;AACpD,uEAAuE;GACrE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;IAClC,GAAG,CAAC,EAAE,SAAS,CAAA;CAChB;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAW3F"}
|
package/dist/cleanup.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { rmdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { nodeGitRunner, removeOwnDirectory } from '@openagt/agent-data';
|
|
4
|
+
import { isPidAlive } from '@openagt/agent-runner';
|
|
5
|
+
import { OWN_AUTOMATIONS_DIR, SCHEDULER_LOG, STATE_DIR, STATE_FILE } from './names.js';
|
|
6
|
+
import { readState } from './state.js';
|
|
7
|
+
export async function cleanup(repo, deps = {}) {
|
|
8
|
+
const isAlive = deps.isAlive ?? isPidAlive;
|
|
9
|
+
const { pid } = await readState(repo);
|
|
10
|
+
// A pid of 0 or below is no process's: the probe would answer for this process's own group.
|
|
11
|
+
if (pid !== undefined && Number.isInteger(pid) && pid > 0 && isAlive(pid))
|
|
12
|
+
return { ok: false, reason: 'running', pid };
|
|
13
|
+
// The folder of the person's own automations goes when nothing is in it: the tool made it, and an empty one keeps nothing of theirs.
|
|
14
|
+
await rmdir(join(repo, OWN_AUTOMATIONS_DIR)).catch(() => { });
|
|
15
|
+
const outcome = await removeOwnDirectory(repo, STATE_DIR, [STATE_FILE, SCHEDULER_LOG], 'scheduler', deps.git ?? nodeGitRunner());
|
|
16
|
+
// The automations a person keeps on this machine are their own writing and exist nowhere else: kept, and said so.
|
|
17
|
+
const kept = outcome.kept.map(entry => (entry.path === OWN_AUTOMATIONS_DIR ? { ...entry, reason: 'your own automations, kept on this machine alone: remove the folder by hand to delete them' } : entry));
|
|
18
|
+
return { ok: true, ...outcome, kept };
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=cleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAkC,MAAM,qBAAqB,CAAA;AACvG,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AA0BtC,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAY,EAAE,IAAI,GAAgB,EAAE;IAChE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,UAAU,CAAA;IAC1C,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAA;IACrC,4FAA4F;IAC5F,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAA;IACvH,qIAAqI;IACrI,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IAC5D,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,GAAG,IAAI,aAAa,EAAE,CAAC,CAAA;IAChI,kHAAkH;IAClH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,4FAA4F,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;IACzM,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAA;AACvC,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type GitRunner } from '@openagt/agent-data';
|
|
2
|
+
/**
|
|
3
|
+
* The command line: JSON on stdout, one line for a person on stderr, and the exit code says how
|
|
4
|
+
* it went — 0 for a result, 1 for a refusal or a failure, 2 for a command that could not be read.
|
|
5
|
+
* The same contract as the skills' commands, so a person and a dashboard read it the same way.
|
|
6
|
+
*/
|
|
7
|
+
export declare const USAGE = "usage: scheduler <command>\n\n tick pull agent-data, sweep, read the commands the project's skills schedule, start what is due, each a run of agent-runner\n init this tool's lines in the dashboard's .openagent/hooks.yml, so it runs while the dashboard is open; a line already there is kept\n start [--keep-alive] the scheduler on, ticking every minute in its own process\n stop [--unless-keep-alive] the scheduler off; runs in flight go to the end; with the flag a keep-alive scheduler is left running\n status the state file, whether the scheduler's process is alive, and the schedule as the project's files say it now\n model <id> the model a scheduled run on Claude Code starts on, where neither its command's skill nor you name another (this user)\n model <command> <id|skill> the model this machine's runs of a scheduled command start on, by the id its coding agent knows it by, the command quoted\n when it has a word after it; kept with the agent the command runs on here, and holding only while it runs on that agent;\n skill for the model its skill names again, else the scheduler's own on Claude Code and the agent's own default on Codex,\n taken for any name\n agent <command> <claude-code|codex|skill>\n the coding agent this machine's runs of a scheduled command are on; refused for an agent whose own skills folder does not\n hold the command's skill (.claude/skills for Claude Code, .agents/skills for Codex); skill for the agent its skill is made\n for again, taken for any name; the command quoted when it has a word after it. The model picked for the command\n goes with either: a model is one agent's.\n Only Claude's quota can be read: a run on Codex starts without that question\n offset <points> how far past the spend boundary a run may still start (this user)\n switch <command> <on|off> whether a scheduled command runs on this machine, the command as status names it (quoted when it has a word after it); every one starts off, and off is taken for any name\n publish <command> <nothing|commit|branch|pr|merge>\n how far this machine's runs of a scheduled command publish; commit until picked.\n Of a row that also resumes runs, as its agent and model, only for the runs it starts: one it resumes goes on with its own\n pace <command> <skill|work|N<m|h|d|w|mo>> [HH:MM]\n how often at most a scheduled command starts on this machine: an interval (15m, 6h, 2d, 2w, 1mo; 1 to 9999), with a time of day\n for days, weeks or months (2d 10:00, this machine's time); work for whenever its check finds work; skill for the skill's own pace again, taken for any name\n agents <command> <skill|N> how many runs of a scheduled command may be in flight at once: this machine starts another only while fewer than N are in flight\n on any machine; a whole number from 1 to 99; skill for the skill's own number again, taken for any name\n add <name> --prompt <text> [--every <N<m|h|d|w|mo>>] [--when <shell line>] [--waits-for <line>] [--private]\n a person's own automation, saved as a command skill of the project: the prompt is the skill's text, and its schedule a pace (--every),\n a check (--when, a shell line that prints what is new; it may read $LAST_RUN), or both; --waits-for says in one plain line what the check\n waits for. The file is yours to commit: its command starts no run before it is on the commit a run's checkout starts from.\n With --private it is kept on this machine alone, in this tool's own folder: nothing to commit, nobody else gets the row, and it\n can start at once: a run is named by the automation's name and handed the prompt. The prompt is in each run's record, which\n is shared where the project shares its records.\n Refused for a name a skill of the project, or an automation kept on this machine, already has\n show <name> an automation saved with add, as it stands: its prompt, its pace, its check, its file, whether it is kept on this machine alone,\n and its version, a name for what the file says now.\n Refused, like edit and remove, for anything whose file does not read as add writes one: a skill of the project,\n or an automation changed by hand since; those are edited and removed by hand\n edit <name> [--prompt <text>] [--every <N<m|h|d|w|mo>>] [--when <shell line>] [--waits-for <line>] [--was <version>]\n save an automation again under its name: what is given takes the place of what its file said, what is left out stays,\n and --every, --when or --waits-for given empty (--when=) takes that part out; what a check waits for goes out with the check.\n Its past runs, its switch and your picks for it stay. A shared one is a change of yours to commit: a run is told the new\n prompt only once the change is on the commit a run's checkout starts from. With --was, the version show answered, it is\n refused when the file was changed since: a form opened a while ago writes over no change made by hand\n remove <name> delete an automation's file, and what this machine held for it: its switch, its pace, its number of agents, its coding agent, its model, its publish pick.\n Nothing is committed: the deletion of a shared one is yours to commit, and everyone else keeps the command until it reaches them.\n What was never committed cannot be brought back: all of one kept on this machine, and a shared one's file or its last changes.\n The records of its past runs stay\n now <command> start one run of a scheduled command now, whatever its switch, its pace and the quota left: a person asked for it. A command with a check\n starts only when the check finds work (it has 10 seconds); how many at once, where a run's checkout starts and whether the\n coding agent can start hold as on a tick. It needs no scheduler running. Not ready within 20 seconds, it starts nothing\n try --when <shell line> run a check once, as a tick would, asking what is new since a day ago: what it printed and whether an agent would start; nothing is saved or started;\n it has 20 seconds, less than a tick gives a check\n write-line --words <plain words> [--agent <claude-code|codex>]\n a shell line for --when, written from what it waits for said in plain words, by one session of the coding agent (Claude Code\n unless named) with no tools: a fraction of a cent. The line is only printed, as {line}, or {none} with why no line can do it;\n nothing is run or saved. It has 25 seconds\n cleanup remove what this tool left in the project: the state file and the scheduler's log, then .scheduler/ and the rule hiding it from git once it is empty\n (the automations kept on this machine are your own writing and stay);\n refused while the state names a scheduler that is alive; the command a dashboard asks for when a project is removed with its files\n\nJSON on stdout. Exit code 1 for a refusal or a failure (the reason on stderr), 2 for a usage error.";
|
|
8
|
+
export interface CliIo {
|
|
9
|
+
cwd: string;
|
|
10
|
+
stdout: (line: string) => void;
|
|
11
|
+
stderr: (line: string) => void;
|
|
12
|
+
}
|
|
13
|
+
export type CliRefusal = {
|
|
14
|
+
ok: false;
|
|
15
|
+
reason: string;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
};
|
|
18
|
+
export declare function runCli(argv: string[], io: CliIo, git?: GitRunner): Promise<number>;
|
|
19
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAcnE;;;;GAIG;AAEH,eAAO,MAAM,KAAK,4iQA+DkF,CAAA;AAEpG,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/B;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAA;AAU9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,GAAE,SAA2B,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BzG"}
|