@openagt/skill-scheduler 0.0.0-stage → 0.1.10
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 +416 -0
- package/dist/cli.js.map +1 -0
- package/dist/dashboard/dashboard.css +3 -0
- package/dist/dashboard/dashboard.js +2323 -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 +83 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +89 -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 +241 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +379 -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 +119 -0
- package/dist/tick.d.ts.map +1 -0
- package/dist/tick.js +311 -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,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a shell line runs a tool: one of the tool's commands, bare or by a path, or its
|
|
3
|
+
* package's name, with or without a version, stands in the line as a word of its own. How `init`
|
|
4
|
+
* reads the project's hook lines, to write no second line beside one that already runs this tool
|
|
5
|
+
* and to take its own lines back. A name inside another word is not it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function lineRuns(line: string, tool: {
|
|
8
|
+
name: string;
|
|
9
|
+
commands: readonly string[];
|
|
10
|
+
}): boolean;
|
|
11
|
+
//# sourceMappingURL=hook-lines.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-lines.d.ts","sourceRoot":"","sources":["../src/hook-lines.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,OAAO,CAInG"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a shell line runs a tool: one of the tool's commands, bare or by a path, or its
|
|
3
|
+
* package's name, with or without a version, stands in the line as a word of its own. How `init`
|
|
4
|
+
* reads the project's hook lines, to write no second line beside one that already runs this tool
|
|
5
|
+
* and to take its own lines back. A name inside another word is not it.
|
|
6
|
+
*/
|
|
7
|
+
export function lineRuns(line, tool) {
|
|
8
|
+
return line
|
|
9
|
+
.split(/[\s"'`=;|&()<>]+/)
|
|
10
|
+
.some(word => word.replace(/@[\w.^~<>=*-]+$/, '') === tool.name || tool.commands.includes(word.slice(word.lastIndexOf('/') + 1)));
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=hook-lines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-lines.js","sourceRoot":"","sources":["../src/hook-lines.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,IAAmD;IACxF,OAAO,IAAI;SACR,KAAK,CAAC,kBAAkB,CAAC;SACzB,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AACrI,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { STATE_DIR, STATE_FILE, RUN_SKILLS_DIR, DEFAULT_MODEL, DEFAULT_PUBLISH, DEFAULT_SPEND_OFFSET, DEFAULT_CAP, MAX_AGENTS, TICK_MS } from './names.js';
|
|
2
|
+
export { skillSchedule, readSchedule, commandPrompt, promptCommand, isDue, type Schedule, type ScheduledCommand } from './schedule.js';
|
|
3
|
+
export { readStartPoint, isOnCommit, atStartOf, type StartPoint } from './start-point.js';
|
|
4
|
+
export { automationOf, automationSkill, type NewAutomation } from './automation-file.js';
|
|
5
|
+
export { addAutomation, automationProblem, editAutomation, removeAutomation, savedAutomation, tryCheck, type AutomationChange, type Removed, type SavedAutomation, type Tried, type Written } from './automation.js';
|
|
6
|
+
export { readState, writeState, updateState, statePath, DEFAULT_STATE, type State, type TickRecord, type TickDecision } from './state.js';
|
|
7
|
+
export { quotaBoundaryStatus, quotaHeadroom, parseResetsAt, boundaryFromResetsAt, type QuotaBoundaryStatus, type QuotaDecision } from './quota-boundary.js';
|
|
8
|
+
export { inFlight, lastRuns, lastStart, commandOf, type LastRun } from './records.js';
|
|
9
|
+
export { MAX_COUNT, parseInterval, parseTimeOfDay, paceInForce, dueFrom, type Interval, type Pace, type PacePick, type PaceUnit } from './pace.js';
|
|
10
|
+
export { tick, startNow, runCheck, type TickDeps, type CheckResult } from './tick.js';
|
|
11
|
+
export { tickProject, runNow, startScheduler, stopScheduler, schedulerStatus, type Status, type StatusCommand } from './scheduler.js';
|
|
12
|
+
export { runCli, USAGE, type CliIo, type CliRefusal } from './cli.js';
|
|
13
|
+
export { initHooks, removeHooks, HOOK_LINES, type InitOutcome, type RemoveOutcome } from './init.js';
|
|
14
|
+
export { WRITE_LINE_SYSTEM, writeLine, writeLinePrompt, writtenOf, lineOf, lineContext, type LineContext, type WrittenLine } from './write-line.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC1J,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACtI,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACzF,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACxF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,EAAE,KAAK,gBAAgB,EAAE,KAAK,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACpN,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AACzI,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,KAAK,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAC3J,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AACrF,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAA;AAClJ,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,KAAK,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACrI,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AACrE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAA;AACpG,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { STATE_DIR, STATE_FILE, RUN_SKILLS_DIR, DEFAULT_MODEL, DEFAULT_PUBLISH, DEFAULT_SPEND_OFFSET, DEFAULT_CAP, MAX_AGENTS, TICK_MS } from './names.js';
|
|
2
|
+
export { skillSchedule, readSchedule, commandPrompt, promptCommand, isDue } from './schedule.js';
|
|
3
|
+
export { readStartPoint, isOnCommit, atStartOf } from './start-point.js';
|
|
4
|
+
export { automationOf, automationSkill } from './automation-file.js';
|
|
5
|
+
export { addAutomation, automationProblem, editAutomation, removeAutomation, savedAutomation, tryCheck } from './automation.js';
|
|
6
|
+
export { readState, writeState, updateState, statePath, DEFAULT_STATE } from './state.js';
|
|
7
|
+
export { quotaBoundaryStatus, quotaHeadroom, parseResetsAt, boundaryFromResetsAt } from './quota-boundary.js';
|
|
8
|
+
export { inFlight, lastRuns, lastStart, commandOf } from './records.js';
|
|
9
|
+
export { MAX_COUNT, parseInterval, parseTimeOfDay, paceInForce, dueFrom } from './pace.js';
|
|
10
|
+
export { tick, startNow, runCheck } from './tick.js';
|
|
11
|
+
export { tickProject, runNow, startScheduler, stopScheduler, schedulerStatus } from './scheduler.js';
|
|
12
|
+
export { runCli, USAGE } from './cli.js';
|
|
13
|
+
export { initHooks, removeHooks, HOOK_LINES } from './init.js';
|
|
14
|
+
export { WRITE_LINE_SYSTEM, writeLine, writeLinePrompt, writtenOf, lineOf, lineContext } from './write-line.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC1J,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,EAAwC,MAAM,eAAe,CAAA;AACtI,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAmB,MAAM,kBAAkB,CAAA;AACzF,OAAO,EAAE,YAAY,EAAE,eAAe,EAAsB,MAAM,sBAAsB,CAAA;AACxF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,EAAuF,MAAM,iBAAiB,CAAA;AACpN,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAkD,MAAM,YAAY,CAAA;AACzI,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAgD,MAAM,qBAAqB,CAAA;AAC3J,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAgB,MAAM,cAAc,CAAA;AACrF,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,OAAO,EAA0D,MAAM,WAAW,CAAA;AAClJ,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAmC,MAAM,WAAW,CAAA;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAmC,MAAM,gBAAgB,CAAA;AACrI,OAAO,EAAE,MAAM,EAAE,KAAK,EAA+B,MAAM,UAAU,CAAA;AACrE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAwC,MAAM,WAAW,CAAA;AACpG,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAsC,MAAM,iBAAiB,CAAA"}
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type InitOutcome } from '@openagt/agent-runner';
|
|
2
|
+
/**
|
|
3
|
+
* `init`: this tool's lines written into a dashboard's hooks file, so the scheduler runs while the
|
|
4
|
+
* dashboard is open. The tool writes
|
|
5
|
+
* its own lines: the dashboard names no tool. The lines that start, resume and check a run are
|
|
6
|
+
* `agent-runner`'s, written by its own `init`. The writer is `agent-runner`'s too, with its rule: a
|
|
7
|
+
* person's file is never overwritten, a list gains a line only when it lacks it.
|
|
8
|
+
*
|
|
9
|
+
* The lines name the tool bare, with no `npx`, like `agent-runner`'s: the dashboard brings this
|
|
10
|
+
* tool and puts it on a line's PATH, so nothing is fetched from npm to run one. With `npx`, a
|
|
11
|
+
* project that installed nothing would get the unrelated package that holds the name `scheduler`
|
|
12
|
+
* on npm.
|
|
13
|
+
*
|
|
14
|
+
* A line that already runs the tool is the tool's line, however it is spelled: a person's own
|
|
15
|
+
* (`npx @openagt/skill-scheduler@0.1 start`) included. `init` writes no second line beside it, and
|
|
16
|
+
* `removeHooks` takes it out with the rest.
|
|
17
|
+
*/
|
|
18
|
+
/** The lines, in the order a new file lists them: `open` and `close`, each a list. */
|
|
19
|
+
export declare const HOOK_LINES: Readonly<Record<string, string | readonly string[]>>;
|
|
20
|
+
export type { InitOutcome };
|
|
21
|
+
/** What taking the lines out answered: the keys that lost a line, or why the file could not be changed. */
|
|
22
|
+
export type RemoveOutcome = {
|
|
23
|
+
ok: true;
|
|
24
|
+
file: string;
|
|
25
|
+
removed: string[];
|
|
26
|
+
} | {
|
|
27
|
+
ok: false;
|
|
28
|
+
reason: 'unreadable';
|
|
29
|
+
file: string;
|
|
30
|
+
detail?: string;
|
|
31
|
+
};
|
|
32
|
+
/** Write the lines into `<repo>/.openagent/hooks.yml`, each only where no line of its list runs the tool yet; nothing where the dashboard has no directory. */
|
|
33
|
+
export declare function initHooks(repo: string): Promise<InitOutcome>;
|
|
34
|
+
/**
|
|
35
|
+
* Take this tool's lines out of the hooks file: every `open` and `close` line that runs it. A list
|
|
36
|
+
* left empty goes with its key; every other line, key and comment stays, and a file left with
|
|
37
|
+
* nothing in it is deleted. A file that is not there has nothing to take.
|
|
38
|
+
*/
|
|
39
|
+
export declare function removeHooks(repo: string): Promise<RemoveOutcome>;
|
|
40
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxE;;;;;;;;;;;;;;;GAeG;AAEH,sFAAsF;AACtF,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAG3E,CAAA;AAOD,YAAY,EAAE,WAAW,EAAE,CAAA;AAE3B,2GAA2G;AAC3G,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAoB9I,+JAA+J;AAC/J,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAIlE;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAwBtE"}
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { isMap, isScalar, isSeq, parseDocument } from 'yaml';
|
|
4
|
+
import { OPENAGENT_DIR } from '@openagt/agent-data';
|
|
5
|
+
import { writeHookLines } from '@openagt/agent-runner';
|
|
6
|
+
import { lineRuns } from './hook-lines.js';
|
|
7
|
+
/**
|
|
8
|
+
* `init`: this tool's lines written into a dashboard's hooks file, so the scheduler runs while the
|
|
9
|
+
* dashboard is open. The tool writes
|
|
10
|
+
* its own lines: the dashboard names no tool. The lines that start, resume and check a run are
|
|
11
|
+
* `agent-runner`'s, written by its own `init`. The writer is `agent-runner`'s too, with its rule: a
|
|
12
|
+
* person's file is never overwritten, a list gains a line only when it lacks it.
|
|
13
|
+
*
|
|
14
|
+
* The lines name the tool bare, with no `npx`, like `agent-runner`'s: the dashboard brings this
|
|
15
|
+
* tool and puts it on a line's PATH, so nothing is fetched from npm to run one. With `npx`, a
|
|
16
|
+
* project that installed nothing would get the unrelated package that holds the name `scheduler`
|
|
17
|
+
* on npm.
|
|
18
|
+
*
|
|
19
|
+
* A line that already runs the tool is the tool's line, however it is spelled: a person's own
|
|
20
|
+
* (`npx @openagt/skill-scheduler@0.1 start`) included. `init` writes no second line beside it, and
|
|
21
|
+
* `removeHooks` takes it out with the rest.
|
|
22
|
+
*/
|
|
23
|
+
/** The lines, in the order a new file lists them: `open` and `close`, each a list. */
|
|
24
|
+
export const HOOK_LINES = {
|
|
25
|
+
open: ['scheduler start'],
|
|
26
|
+
close: ['scheduler stop --unless-keep-alive'],
|
|
27
|
+
};
|
|
28
|
+
/** This tool, as a hook line names it: its package or its command. */
|
|
29
|
+
const TOOL = { name: '@openagt/skill-scheduler', commands: ['scheduler'] };
|
|
30
|
+
const HOOKS_FILE = `${OPENAGENT_DIR}/hooks.yml`;
|
|
31
|
+
/** One item of a hooks list, as the line it is. */
|
|
32
|
+
function lineOf(item) {
|
|
33
|
+
return String(isScalar(item) ? item.value : item);
|
|
34
|
+
}
|
|
35
|
+
/** The lists of the hooks file that hold this tool's lines, as written; none for a file that is not there or cannot be read. */
|
|
36
|
+
async function lists(repo) {
|
|
37
|
+
const raw = await readFile(join(repo, HOOKS_FILE), 'utf8').catch(() => '');
|
|
38
|
+
const doc = parseDocument(raw);
|
|
39
|
+
if (doc.errors.length > 0 || !isMap(doc.contents))
|
|
40
|
+
return {};
|
|
41
|
+
const found = {};
|
|
42
|
+
for (const key of Object.keys(HOOK_LINES)) {
|
|
43
|
+
const list = doc.get(key, true);
|
|
44
|
+
if (isSeq(list))
|
|
45
|
+
found[key] = list.items.map(item => lineOf(item));
|
|
46
|
+
}
|
|
47
|
+
return found;
|
|
48
|
+
}
|
|
49
|
+
/** Write the lines into `<repo>/.openagent/hooks.yml`, each only where no line of its list runs the tool yet; nothing where the dashboard has no directory. */
|
|
50
|
+
export async function initHooks(repo) {
|
|
51
|
+
const present = await lists(repo);
|
|
52
|
+
const missing = Object.fromEntries(Object.entries(HOOK_LINES).filter(([key]) => !(present[key] ?? []).some(line => lineRuns(line, TOOL))));
|
|
53
|
+
return writeHookLines(repo, missing);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Take this tool's lines out of the hooks file: every `open` and `close` line that runs it. A list
|
|
57
|
+
* left empty goes with its key; every other line, key and comment stays, and a file left with
|
|
58
|
+
* nothing in it is deleted. A file that is not there has nothing to take.
|
|
59
|
+
*/
|
|
60
|
+
export async function removeHooks(repo) {
|
|
61
|
+
const file = join(repo, HOOKS_FILE);
|
|
62
|
+
const raw = await readFile(file, 'utf8').catch(() => undefined);
|
|
63
|
+
if (raw === undefined)
|
|
64
|
+
return { ok: true, file, removed: [] };
|
|
65
|
+
const doc = parseDocument(raw);
|
|
66
|
+
const error = doc.errors[0];
|
|
67
|
+
if (error)
|
|
68
|
+
return { ok: false, reason: 'unreadable', file, detail: error.message.split('\n')[0] ?? error.message };
|
|
69
|
+
if (!isMap(doc.contents))
|
|
70
|
+
return { ok: true, file, removed: [] };
|
|
71
|
+
const removed = [];
|
|
72
|
+
for (const key of Object.keys(HOOK_LINES)) {
|
|
73
|
+
const list = doc.get(key, true);
|
|
74
|
+
if (!isSeq(list))
|
|
75
|
+
continue;
|
|
76
|
+
const seq = list;
|
|
77
|
+
const kept = seq.items.filter(item => !lineRuns(lineOf(item), TOOL));
|
|
78
|
+
if (kept.length === seq.items.length)
|
|
79
|
+
continue;
|
|
80
|
+
removed.push(key);
|
|
81
|
+
if (kept.length === 0)
|
|
82
|
+
doc.delete(key);
|
|
83
|
+
else
|
|
84
|
+
seq.items = kept;
|
|
85
|
+
}
|
|
86
|
+
if (removed.length === 0)
|
|
87
|
+
return { ok: true, file, removed };
|
|
88
|
+
// A file that held nothing but these lines goes with them: it was this tool's.
|
|
89
|
+
if (doc.contents.items.length === 0 && !/^\s*#/m.test(raw))
|
|
90
|
+
await rm(file);
|
|
91
|
+
else
|
|
92
|
+
await writeFile(file, doc.toString({ lineWidth: 0 }));
|
|
93
|
+
return { ok: true, file, removed };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAgB,MAAM,MAAM,CAAA;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,cAAc,EAAoB,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE1C;;;;;;;;;;;;;;;GAeG;AAEH,sFAAsF;AACtF,MAAM,CAAC,MAAM,UAAU,GAAyD;IAC9E,IAAI,EAAE,CAAC,iBAAiB,CAAC;IACzB,KAAK,EAAE,CAAC,oCAAoC,CAAC;CAC9C,CAAA;AAED,sEAAsE;AACtE,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,0BAA0B,EAAE,QAAQ,EAAE,CAAC,WAAW,CAAC,EAAE,CAAA;AAE1E,MAAM,UAAU,GAAG,GAAG,aAAa,YAAY,CAAA;AAO/C,mDAAmD;AACnD,SAAS,MAAM,CAAC,IAAa;IAC3B,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;AACnD,CAAC;AAED,gIAAgI;AAChI,KAAK,UAAU,KAAK,CAAC,IAAY;IAC/B,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IAC1E,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;IAC9B,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAA;IAC5D,MAAM,KAAK,GAA6B,EAAE,CAAA;IAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC/B,IAAI,KAAK,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,GAAI,IAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IACjF,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,+JAA+J;AAC/J,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1I,OAAO,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IACnC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC/D,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;IAC7D,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IAC3B,IAAI,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAA;IAClH,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;IAChE,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC1B,MAAM,GAAG,GAAG,IAAe,CAAA;QAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,CAAA;QACpE,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,MAAM;YAAE,SAAQ;QAC9C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;;YACjC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAA;IACvB,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;IAC5D,+EAA+E;IAC/E,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,EAAE,CAAC,IAAI,CAAC,CAAA;;QACrE,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAC1D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;AACpC,CAAC"}
|
package/dist/names.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** The names the package hangs off. No node imports. */
|
|
2
|
+
/** The tool's own directory at the repository root, hidden from git by the tool itself. */
|
|
3
|
+
export declare const STATE_DIR = ".scheduler";
|
|
4
|
+
/** The state the tool writes for this user: on or off, the model, the last tick and what it decided. */
|
|
5
|
+
export declare const STATE_FILE = "state.json";
|
|
6
|
+
/**
|
|
7
|
+
* Under the state directory: the automations a person keeps on this machine alone, one file each
|
|
8
|
+
* (`<name>.md`), written like a skill's file. Never tracked, never in a run's checkout.
|
|
9
|
+
*/
|
|
10
|
+
export declare const OWN_AUTOMATIONS_DIR = ".scheduler/automations";
|
|
11
|
+
/** The longest text an automation kept on this machine may have, in characters: it travels to its run as one command-line argument. */
|
|
12
|
+
export declare const OWN_TEXT_MAX = 32000;
|
|
13
|
+
/** Under the state directory: the scheduler's own log. */
|
|
14
|
+
export declare const SCHEDULER_LOG = "scheduler.log";
|
|
15
|
+
/** The coding agents a scheduled run can be on, by the names `agent-runner` takes. */
|
|
16
|
+
export declare const AGENTS: readonly ['claude-code', 'codex'];
|
|
17
|
+
/** One of the coding agents a scheduled run can be on. */
|
|
18
|
+
export type AgentName = (typeof AGENTS)[number];
|
|
19
|
+
/** Whether a value names a coding agent a scheduled run can be on. */
|
|
20
|
+
export declare function isAgent(value: unknown): value is AgentName;
|
|
21
|
+
/** A coding agent's name for a person. */
|
|
22
|
+
export declare const AGENT_LABELS: Readonly<Record<AgentName, string>>;
|
|
23
|
+
/**
|
|
24
|
+
* Where each coding agent reads a project's skills. A skill that is only in another agent's folder
|
|
25
|
+
* is no command to it: sent with a slash, it finds nothing to do.
|
|
26
|
+
*/
|
|
27
|
+
export declare const AGENT_SKILLS_DIR: Readonly<Record<AgentName, string>>;
|
|
28
|
+
/** The coding agent a scheduled run is on where neither its skill nor a person names one, when it can read the skill. */
|
|
29
|
+
export declare const DEFAULT_AGENT: AgentName;
|
|
30
|
+
/** Where a shared automation is saved, and the first folder the schedule is read from: the skills folder of the agent a run is on by default. */
|
|
31
|
+
export declare const RUN_SKILLS_DIR: string;
|
|
32
|
+
/** A skill's file inside its folder: its front matter may hold the skill's `schedule`. */
|
|
33
|
+
export declare const SKILL_FILE = "SKILL.md";
|
|
34
|
+
/** How far a run of a scheduled command publishes on a machine where nobody picked a level for it: it commits its work and pushes nothing. */
|
|
35
|
+
export declare const DEFAULT_PUBLISH = "commit";
|
|
36
|
+
/** The model a scheduled run on Claude Code starts on when the state names none. */
|
|
37
|
+
export declare const DEFAULT_MODEL = "opus";
|
|
38
|
+
/**
|
|
39
|
+
* How far past the spend boundary unattended work may go, in percentage points: half a day of
|
|
40
|
+
* the week's allowance, the same cushion OpenAgent's daemon used.
|
|
41
|
+
*/
|
|
42
|
+
export declare const DEFAULT_SPEND_OFFSET: number;
|
|
43
|
+
/** Runs of one command in flight at once, where neither its skill nor a person names a number. */
|
|
44
|
+
export declare const DEFAULT_CAP = 1;
|
|
45
|
+
/** The most runs of one command a skill or a person may let be in flight at once. */
|
|
46
|
+
export declare const MAX_AGENTS = 99;
|
|
47
|
+
/** Whether a value is a number of agents at once: a whole number from 1 to 99. */
|
|
48
|
+
export declare function isAgents(value: unknown): value is number;
|
|
49
|
+
/** The name a check reads the time of its command's last start under (`$LAST_RUN`). */
|
|
50
|
+
export declare const LAST_RUN_ENV = "LAST_RUN";
|
|
51
|
+
/** Whether a value is a time as the tools write one: an ISO date and time a date reads (`2026-10-09T10:00:00.000Z`). What a switched-on command's switch holds, and a run record's start. */
|
|
52
|
+
export declare function isTime(value: unknown): value is string;
|
|
53
|
+
/** The longest name a person's own automation may have: a folder name, and a command a person types. */
|
|
54
|
+
export declare const MAX_NAME = 64;
|
|
55
|
+
/**
|
|
56
|
+
* Whether a text can name a person's own automation: lower-case letters, digits and dashes, a
|
|
57
|
+
* letter or a digit first, and never three dashes in a row, which end a skill file's front matter
|
|
58
|
+
* for a coding agent wherever they stand.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isCommandName(text: string): boolean;
|
|
61
|
+
/** How long a tick waits for origin's default branch to be fetched before it starts a run: the wait a run's own checkout gives the same fetch. */
|
|
62
|
+
export declare const START_POINT_FETCH_MS = 5000;
|
|
63
|
+
/** How often a started scheduler ticks. */
|
|
64
|
+
export declare const TICK_MS = 60000;
|
|
65
|
+
/** How often the scheduler's process looks whether a row that may be checked between ticks is due again (`check-every`). */
|
|
66
|
+
export declare const GLANCE_MS = 5000;
|
|
67
|
+
/** How long a reading of Claude's quota is used again before it is read anew: a read spawns the CLI and takes seconds, and the quota moves slowly. */
|
|
68
|
+
export declare const QUOTA_KEPT_MS: number;
|
|
69
|
+
/** How much of what a check printed a run is handed, in characters: enough for a list of work, short of a prompt nobody can read. */
|
|
70
|
+
export declare const FOUND_MAX = 8000;
|
|
71
|
+
/** How far back a check asks from when nothing says since when: a line that is only being tried, and a command started by hand that never started and is switched off. "Since now" would never find anything. */
|
|
72
|
+
export declare const NEVER_STARTED_SINCE_MS: number;
|
|
73
|
+
/** How long a check that is only being tried may run: less than a tick gives one, and less than a dashboard waits for a command's answer. */
|
|
74
|
+
export declare const TRY_TIMEOUT_MS = 20000;
|
|
75
|
+
/** How long a command started by hand may take to get ready, its pull, its check and its readings: past it nothing is written and nothing starts, since a dashboard ends a command at 30 seconds and the marker and the start are still to come. */
|
|
76
|
+
export declare const NOW_READY_MS = 20000;
|
|
77
|
+
/** How long the check of a command started by hand may run: whoever asked, a dashboard, waits for the whole answer, the pull and the start with it. */
|
|
78
|
+
export declare const NOW_CHECK_TIMEOUT_MS = 10000;
|
|
79
|
+
/** How long a command's check may run before it counts as failed. */
|
|
80
|
+
export declare const CHECK_TIMEOUT_MS = 60000;
|
|
81
|
+
/** How long the coding agent may take to write a shell line from a person's words: one turn with no tools takes seconds, and a dashboard ends a command at 30. */
|
|
82
|
+
export declare const WRITE_LINE_TIMEOUT_MS = 25000;
|
|
83
|
+
//# sourceMappingURL=names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,2FAA2F;AAC3F,eAAO,MAAM,SAAS,eAAe,CAAA;AAErC,wGAAwG;AACxG,eAAO,MAAM,UAAU,eAAe,CAAA;AAEtC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,2BAA6B,CAAA;AAE7D,uIAAuI;AACvI,eAAO,MAAM,YAAY,QAAS,CAAA;AAElC,0DAA0D;AAC1D,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAE5C,sFAAsF;AACtF,eAAO,MAAM,MAAM,YAAI,aAAa,EAAE,OAAO,CAAU,CAAA;AAEvD,0DAA0D;AAC1D,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/C,sEAAsE;AACtE,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE1D;AAED,0CAA0C;AAC1C,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAoD,CAAA;AAEjH;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAgE,CAAA;AAEjI,yHAAyH;AACzH,eAAO,MAAM,aAAa,EAAE,SAAyB,CAAA;AAErD,iJAAiJ;AACjJ,eAAO,MAAM,cAAc,QAAkC,CAAA;AAE7D,0FAA0F;AAC1F,eAAO,MAAM,UAAU,aAAa,CAAA;AAEpC,8IAA8I;AAC9I,eAAO,MAAM,eAAe,WAAW,CAAA;AAEvC,oFAAoF;AACpF,eAAO,MAAM,aAAa,SAAS,CAAA;AAEnC;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAAW,CAAA;AAE5C,kGAAkG;AAClG,eAAO,MAAM,WAAW,IAAI,CAAA;AAE5B,qFAAqF;AACrF,eAAO,MAAM,UAAU,KAAK,CAAA;AAE5B,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD;AAED,uFAAuF;AACvF,eAAO,MAAM,YAAY,aAAa,CAAA;AAEtC,6LAA6L;AAC7L,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEtD;AAED,wGAAwG;AACxG,eAAO,MAAM,QAAQ,KAAK,CAAA;AAE1B;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,kJAAkJ;AAClJ,eAAO,MAAM,oBAAoB,OAAQ,CAAA;AAEzC,2CAA2C;AAC3C,eAAO,MAAM,OAAO,QAAS,CAAA;AAE7B,4HAA4H;AAC5H,eAAO,MAAM,SAAS,OAAQ,CAAA;AAE9B,sJAAsJ;AACtJ,eAAO,MAAM,aAAa,QAAa,CAAA;AAEvC,qIAAqI;AACrI,eAAO,MAAM,SAAS,OAAQ,CAAA;AAE9B,iNAAiN;AACjN,eAAO,MAAM,sBAAsB,QAAsB,CAAA;AAEzD,6IAA6I;AAC7I,eAAO,MAAM,cAAc,QAAS,CAAA;AAEpC,oPAAoP;AACpP,eAAO,MAAM,YAAY,QAAS,CAAA;AAElC,uJAAuJ;AACvJ,eAAO,MAAM,oBAAoB,QAAS,CAAA;AAE1C,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,QAAS,CAAA;AAEtC,kKAAkK;AAClK,eAAO,MAAM,qBAAqB,QAAS,CAAA"}
|
package/dist/names.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** The names the package hangs off. No node imports. */
|
|
2
|
+
/** The tool's own directory at the repository root, hidden from git by the tool itself. */
|
|
3
|
+
export const STATE_DIR = '.scheduler';
|
|
4
|
+
/** The state the tool writes for this user: on or off, the model, the last tick and what it decided. */
|
|
5
|
+
export const STATE_FILE = 'state.json';
|
|
6
|
+
/**
|
|
7
|
+
* Under the state directory: the automations a person keeps on this machine alone, one file each
|
|
8
|
+
* (`<name>.md`), written like a skill's file. Never tracked, never in a run's checkout.
|
|
9
|
+
*/
|
|
10
|
+
export const OWN_AUTOMATIONS_DIR = `${STATE_DIR}/automations`;
|
|
11
|
+
/** The longest text an automation kept on this machine may have, in characters: it travels to its run as one command-line argument. */
|
|
12
|
+
export const OWN_TEXT_MAX = 32_000;
|
|
13
|
+
/** Under the state directory: the scheduler's own log. */
|
|
14
|
+
export const SCHEDULER_LOG = 'scheduler.log';
|
|
15
|
+
/** The coding agents a scheduled run can be on, by the names `agent-runner` takes. */
|
|
16
|
+
export const AGENTS = ['claude-code', 'codex'];
|
|
17
|
+
/** Whether a value names a coding agent a scheduled run can be on. */
|
|
18
|
+
export function isAgent(value) {
|
|
19
|
+
return AGENTS.includes(value);
|
|
20
|
+
}
|
|
21
|
+
/** A coding agent's name for a person. */
|
|
22
|
+
export const AGENT_LABELS = { 'claude-code': 'Claude Code', codex: 'Codex' };
|
|
23
|
+
/**
|
|
24
|
+
* Where each coding agent reads a project's skills. A skill that is only in another agent's folder
|
|
25
|
+
* is no command to it: sent with a slash, it finds nothing to do.
|
|
26
|
+
*/
|
|
27
|
+
export const AGENT_SKILLS_DIR = { 'claude-code': '.claude/skills', codex: '.agents/skills' };
|
|
28
|
+
/** The coding agent a scheduled run is on where neither its skill nor a person names one, when it can read the skill. */
|
|
29
|
+
export const DEFAULT_AGENT = 'claude-code';
|
|
30
|
+
/** Where a shared automation is saved, and the first folder the schedule is read from: the skills folder of the agent a run is on by default. */
|
|
31
|
+
export const RUN_SKILLS_DIR = AGENT_SKILLS_DIR[DEFAULT_AGENT];
|
|
32
|
+
/** A skill's file inside its folder: its front matter may hold the skill's `schedule`. */
|
|
33
|
+
export const SKILL_FILE = 'SKILL.md';
|
|
34
|
+
/** How far a run of a scheduled command publishes on a machine where nobody picked a level for it: it commits its work and pushes nothing. */
|
|
35
|
+
export const DEFAULT_PUBLISH = 'commit';
|
|
36
|
+
/** The model a scheduled run on Claude Code starts on when the state names none. */
|
|
37
|
+
export const DEFAULT_MODEL = 'opus';
|
|
38
|
+
/**
|
|
39
|
+
* How far past the spend boundary unattended work may go, in percentage points: half a day of
|
|
40
|
+
* the week's allowance, the same cushion OpenAgent's daemon used.
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_SPEND_OFFSET = 100 / 14;
|
|
43
|
+
/** Runs of one command in flight at once, where neither its skill nor a person names a number. */
|
|
44
|
+
export const DEFAULT_CAP = 1;
|
|
45
|
+
/** The most runs of one command a skill or a person may let be in flight at once. */
|
|
46
|
+
export const MAX_AGENTS = 99;
|
|
47
|
+
/** Whether a value is a number of agents at once: a whole number from 1 to 99. */
|
|
48
|
+
export function isAgents(value) {
|
|
49
|
+
return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= MAX_AGENTS;
|
|
50
|
+
}
|
|
51
|
+
/** The name a check reads the time of its command's last start under (`$LAST_RUN`). */
|
|
52
|
+
export const LAST_RUN_ENV = 'LAST_RUN';
|
|
53
|
+
/** Whether a value is a time as the tools write one: an ISO date and time a date reads (`2026-10-09T10:00:00.000Z`). What a switched-on command's switch holds, and a run record's start. */
|
|
54
|
+
export function isTime(value) {
|
|
55
|
+
return typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value) && !Number.isNaN(Date.parse(value));
|
|
56
|
+
}
|
|
57
|
+
/** The longest name a person's own automation may have: a folder name, and a command a person types. */
|
|
58
|
+
export const MAX_NAME = 64;
|
|
59
|
+
/**
|
|
60
|
+
* Whether a text can name a person's own automation: lower-case letters, digits and dashes, a
|
|
61
|
+
* letter or a digit first, and never three dashes in a row, which end a skill file's front matter
|
|
62
|
+
* for a coding agent wherever they stand.
|
|
63
|
+
*/
|
|
64
|
+
export function isCommandName(text) {
|
|
65
|
+
return /^[a-z0-9][a-z0-9-]*$/.test(text) && !text.includes('---');
|
|
66
|
+
}
|
|
67
|
+
/** How long a tick waits for origin's default branch to be fetched before it starts a run: the wait a run's own checkout gives the same fetch. */
|
|
68
|
+
export const START_POINT_FETCH_MS = 5_000;
|
|
69
|
+
/** How often a started scheduler ticks. */
|
|
70
|
+
export const TICK_MS = 60_000;
|
|
71
|
+
/** How often the scheduler's process looks whether a row that may be checked between ticks is due again (`check-every`). */
|
|
72
|
+
export const GLANCE_MS = 5_000;
|
|
73
|
+
/** How long a reading of Claude's quota is used again before it is read anew: a read spawns the CLI and takes seconds, and the quota moves slowly. */
|
|
74
|
+
export const QUOTA_KEPT_MS = 5 * 60_000;
|
|
75
|
+
/** How much of what a check printed a run is handed, in characters: enough for a list of work, short of a prompt nobody can read. */
|
|
76
|
+
export const FOUND_MAX = 8_000;
|
|
77
|
+
/** How far back a check asks from when nothing says since when: a line that is only being tried, and a command started by hand that never started and is switched off. "Since now" would never find anything. */
|
|
78
|
+
export const NEVER_STARTED_SINCE_MS = 24 * 60 * 60 * 1000;
|
|
79
|
+
/** How long a check that is only being tried may run: less than a tick gives one, and less than a dashboard waits for a command's answer. */
|
|
80
|
+
export const TRY_TIMEOUT_MS = 20_000;
|
|
81
|
+
/** How long a command started by hand may take to get ready, its pull, its check and its readings: past it nothing is written and nothing starts, since a dashboard ends a command at 30 seconds and the marker and the start are still to come. */
|
|
82
|
+
export const NOW_READY_MS = 20_000;
|
|
83
|
+
/** How long the check of a command started by hand may run: whoever asked, a dashboard, waits for the whole answer, the pull and the start with it. */
|
|
84
|
+
export const NOW_CHECK_TIMEOUT_MS = 10_000;
|
|
85
|
+
/** How long a command's check may run before it counts as failed. */
|
|
86
|
+
export const CHECK_TIMEOUT_MS = 60_000;
|
|
87
|
+
/** How long the coding agent may take to write a shell line from a person's words: one turn with no tools takes seconds, and a dashboard ends a command at 30. */
|
|
88
|
+
export const WRITE_LINE_TIMEOUT_MS = 25_000;
|
|
89
|
+
//# sourceMappingURL=names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"names.js","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"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"}
|
package/dist/pace.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scheduled command's pace (#2022): how often at most it starts. A skill gives a starting pace;
|
|
3
|
+
* each person may set another for their own machine, kept in the tool's state. No node imports:
|
|
4
|
+
* the dashboard part reads the same rules.
|
|
5
|
+
*
|
|
6
|
+
* A pace is an interval, a number and a unit: minutes, hours, days, weeks or months. A week is 7
|
|
7
|
+
* days and a month 30. The interval is the least time since the command last started, on any
|
|
8
|
+
* machine. A pace in days, weeks or months may also name a time of day, like a calendar event:
|
|
9
|
+
* the command is then due from that time on, on a day at least that many days after the day it
|
|
10
|
+
* last started. The time is the machine's own local time.
|
|
11
|
+
*/
|
|
12
|
+
/** An interval's unit, as written: `m`, `h`, `d`, `w`, `mo`. */
|
|
13
|
+
export type PaceUnit = 'm' | 'h' | 'd' | 'w' | 'mo';
|
|
14
|
+
export declare const PACE_UNITS: readonly PaceUnit[];
|
|
15
|
+
/** An interval as read: its count, its unit, and its length. */
|
|
16
|
+
export interface Interval {
|
|
17
|
+
count: number;
|
|
18
|
+
unit: PaceUnit;
|
|
19
|
+
ms: number;
|
|
20
|
+
/** As written, without a leading zero: `15m`, `2w`. */
|
|
21
|
+
text: string;
|
|
22
|
+
}
|
|
23
|
+
/** The largest count an interval takes: past it nobody means a pace, and far past it a date stops being one. */
|
|
24
|
+
export declare const MAX_COUNT = 9999;
|
|
25
|
+
/** An interval out of its text (`15m`, `6h`, `7d`, `2w`, `1mo`); nothing for text that is none, for a count of 0, which would spell "always", and for one above 9999. */
|
|
26
|
+
export declare function parseInterval(text: string): Interval | undefined;
|
|
27
|
+
/** Whether a time of day may go with the interval: only with days, weeks or months. */
|
|
28
|
+
export declare function takesTimeOfDay(interval: Interval): boolean;
|
|
29
|
+
/** A time of day out of its text, `HH:MM` on a 24-hour clock (`9:05`, `10:00`); nothing for text that is none. */
|
|
30
|
+
export declare function parseTimeOfDay(text: string): {
|
|
31
|
+
hour: number;
|
|
32
|
+
minute: number;
|
|
33
|
+
text: string;
|
|
34
|
+
} | undefined;
|
|
35
|
+
/** A pick with its time of day counted from now on: what switching the command on does, so a row ticked after its time waits for the next one. Any other pick is unchanged. */
|
|
36
|
+
export declare function sinceNow(pick: PacePick | undefined, now: Date): PacePick | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* A person's pick of a pace for one scheduled command, on their machine: `work` for "whenever
|
|
39
|
+
* there is work", which takes the interval away and leaves the check alone to say when; else an
|
|
40
|
+
* interval, with a time of day when it has one and when the pick was made, or the command last
|
|
41
|
+
* switched on with it.
|
|
42
|
+
*/
|
|
43
|
+
export type PacePick = {
|
|
44
|
+
work: true;
|
|
45
|
+
} | {
|
|
46
|
+
every: string;
|
|
47
|
+
at?: string;
|
|
48
|
+
since: string;
|
|
49
|
+
};
|
|
50
|
+
/** The pace a command runs at: its interval, and the time of day when it has one, with when that pick was made. Absent for a command its check alone paces. */
|
|
51
|
+
export interface Pace {
|
|
52
|
+
every: Interval;
|
|
53
|
+
at?: {
|
|
54
|
+
hour: number;
|
|
55
|
+
minute: number;
|
|
56
|
+
text: string;
|
|
57
|
+
};
|
|
58
|
+
since?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The pace in force for a command: this machine's pick, else the skill's. A pick that cannot be
|
|
62
|
+
* read (a state file edited by hand) is no pick. "Whenever there is work" holds only while the
|
|
63
|
+
* command has a check: without one nothing would say when, so the skill's pace stands.
|
|
64
|
+
*/
|
|
65
|
+
export declare function paceInForce(pick: PacePick | undefined, skill: {
|
|
66
|
+
every?: Interval;
|
|
67
|
+
when?: string;
|
|
68
|
+
}): Pace | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* From when a command is due, by its pace: a time already past means it is due now.
|
|
71
|
+
*
|
|
72
|
+
* Without a time of day: the interval after its last start, and at once when it never started.
|
|
73
|
+
*
|
|
74
|
+
* With a time of day: that time on the day the interval's days after the day it last started, so
|
|
75
|
+
* a time that was missed (the scheduler not running then) is still due when the scheduler next
|
|
76
|
+
* looks, once, since the start that follows moves the next day on. Never earlier than the first
|
|
77
|
+
* such time after the pick was made, or after the command was last switched on with it: setting
|
|
78
|
+
* "every day at 10:00" at 11:00, or ticking its switch at 11:00, starts nothing until tomorrow's
|
|
79
|
+
* 10:00, like a calendar event. Days are the machine's own local days.
|
|
80
|
+
*/
|
|
81
|
+
export declare function dueFrom(pace: Pace, lastStart: Date | undefined, now: Date): Date;
|
|
82
|
+
/** A pace as a tick's decision says it: `6h`, `2d at 10:00`. */
|
|
83
|
+
export declare function paceText(pace: Pace): string;
|
|
84
|
+
/** A local time as a tick's decision says it: `2026-10-10 10:00`. */
|
|
85
|
+
export declare function localStamp(date: Date): string;
|
|
86
|
+
//# sourceMappingURL=pace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pace.d.ts","sourceRoot":"","sources":["../src/pace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gEAAgE;AAChE,MAAM,MAAM,QAAQ,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAA;AAEnD,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAA+B,CAAA;AAQzE,gEAAgE;AAChE,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,QAAQ,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;IACV,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAA;CACb;AAED,gHAAgH;AAChH,eAAO,MAAM,SAAS,OAAO,CAAA;AAE7B,yKAAyK;AACzK,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAMhE;AAED,uFAAuF;AACvF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAE1D;AAED,kHAAkH;AAClH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAMvG;AAED,+KAA+K;AAC/K,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,GAAG,EAAE,IAAI,GAAG,QAAQ,GAAG,SAAS,CAGpF;AAED;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAErF,+JAA+J;AAC/J,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,QAAQ,CAAA;IACf,EAAE,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,QAAQ,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,CASpH;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,SAAS,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,CAUhF;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE3C;AAED,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAG7C"}
|
package/dist/pace.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scheduled command's pace (#2022): how often at most it starts. A skill gives a starting pace;
|
|
3
|
+
* each person may set another for their own machine, kept in the tool's state. No node imports:
|
|
4
|
+
* the dashboard part reads the same rules.
|
|
5
|
+
*
|
|
6
|
+
* A pace is an interval, a number and a unit: minutes, hours, days, weeks or months. A week is 7
|
|
7
|
+
* days and a month 30. The interval is the least time since the command last started, on any
|
|
8
|
+
* machine. A pace in days, weeks or months may also name a time of day, like a calendar event:
|
|
9
|
+
* the command is then due from that time on, on a day at least that many days after the day it
|
|
10
|
+
* last started. The time is the machine's own local time.
|
|
11
|
+
*/
|
|
12
|
+
export const PACE_UNITS = ['m', 'h', 'd', 'w', 'mo'];
|
|
13
|
+
/** How many days one of a unit is, for the units a time of day may go with. */
|
|
14
|
+
const UNIT_DAYS = { d: 1, w: 7, mo: 30 };
|
|
15
|
+
const MINUTE_MS = 60_000;
|
|
16
|
+
const UNIT_MS = { m: MINUTE_MS, h: 60 * MINUTE_MS, d: 1440 * MINUTE_MS, w: 7 * 1440 * MINUTE_MS, mo: 30 * 1440 * MINUTE_MS };
|
|
17
|
+
/** The largest count an interval takes: past it nobody means a pace, and far past it a date stops being one. */
|
|
18
|
+
export const MAX_COUNT = 9999;
|
|
19
|
+
/** An interval out of its text (`15m`, `6h`, `7d`, `2w`, `1mo`); nothing for text that is none, for a count of 0, which would spell "always", and for one above 9999. */
|
|
20
|
+
export function parseInterval(text) {
|
|
21
|
+
const read = /^(\d+)(mo|m|h|d|w)$/.exec(text);
|
|
22
|
+
const count = Number(read?.[1]);
|
|
23
|
+
if (!read || count < 1 || count > MAX_COUNT)
|
|
24
|
+
return undefined;
|
|
25
|
+
const unit = read[2];
|
|
26
|
+
return { count, unit, ms: count * UNIT_MS[unit], text: `${count}${unit}` };
|
|
27
|
+
}
|
|
28
|
+
/** Whether a time of day may go with the interval: only with days, weeks or months. */
|
|
29
|
+
export function takesTimeOfDay(interval) {
|
|
30
|
+
return UNIT_DAYS[interval.unit] !== undefined;
|
|
31
|
+
}
|
|
32
|
+
/** A time of day out of its text, `HH:MM` on a 24-hour clock (`9:05`, `10:00`); nothing for text that is none. */
|
|
33
|
+
export function parseTimeOfDay(text) {
|
|
34
|
+
const read = /^(\d{1,2}):(\d{2})$/.exec(text);
|
|
35
|
+
const hour = Number(read?.[1]);
|
|
36
|
+
const minute = Number(read?.[2]);
|
|
37
|
+
if (!read || hour > 23 || minute > 59)
|
|
38
|
+
return undefined;
|
|
39
|
+
return { hour, minute, text: `${String(hour).padStart(2, '0')}:${read[2]}` };
|
|
40
|
+
}
|
|
41
|
+
/** A pick with its time of day counted from now on: what switching the command on does, so a row ticked after its time waits for the next one. Any other pick is unchanged. */
|
|
42
|
+
export function sinceNow(pick, now) {
|
|
43
|
+
if (typeof pick !== 'object' || pick === null || !('every' in pick) || typeof pick.at !== 'string')
|
|
44
|
+
return pick;
|
|
45
|
+
return { ...pick, since: now.toISOString() };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The pace in force for a command: this machine's pick, else the skill's. A pick that cannot be
|
|
49
|
+
* read (a state file edited by hand) is no pick. "Whenever there is work" holds only while the
|
|
50
|
+
* command has a check: without one nothing would say when, so the skill's pace stands.
|
|
51
|
+
*/
|
|
52
|
+
export function paceInForce(pick, skill) {
|
|
53
|
+
const skills = skill.every ? { every: skill.every } : undefined;
|
|
54
|
+
if (pick === undefined || typeof pick !== 'object' || pick === null)
|
|
55
|
+
return skills;
|
|
56
|
+
if ('work' in pick && pick.work === true)
|
|
57
|
+
return skill.when !== undefined ? undefined : skills;
|
|
58
|
+
const every = 'every' in pick && typeof pick.every === 'string' ? parseInterval(pick.every) : undefined;
|
|
59
|
+
if (!every)
|
|
60
|
+
return skills;
|
|
61
|
+
const timed = pick;
|
|
62
|
+
const at = typeof timed.at === 'string' && takesTimeOfDay(every) ? parseTimeOfDay(timed.at) : undefined;
|
|
63
|
+
return { every, ...(at ? { at, ...(typeof timed.since === 'string' ? { since: timed.since } : {}) } : {}) };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* From when a command is due, by its pace: a time already past means it is due now.
|
|
67
|
+
*
|
|
68
|
+
* Without a time of day: the interval after its last start, and at once when it never started.
|
|
69
|
+
*
|
|
70
|
+
* With a time of day: that time on the day the interval's days after the day it last started, so
|
|
71
|
+
* a time that was missed (the scheduler not running then) is still due when the scheduler next
|
|
72
|
+
* looks, once, since the start that follows moves the next day on. Never earlier than the first
|
|
73
|
+
* such time after the pick was made, or after the command was last switched on with it: setting
|
|
74
|
+
* "every day at 10:00" at 11:00, or ticking its switch at 11:00, starts nothing until tomorrow's
|
|
75
|
+
* 10:00, like a calendar event. Days are the machine's own local days.
|
|
76
|
+
*/
|
|
77
|
+
export function dueFrom(pace, lastStart, now) {
|
|
78
|
+
if (!pace.at)
|
|
79
|
+
return lastStart ? new Date(lastStart.getTime() + pace.every.ms) : new Date(0);
|
|
80
|
+
const { hour, minute } = pace.at;
|
|
81
|
+
const atOn = (day, daysLater) => new Date(day.getFullYear(), day.getMonth(), day.getDate() + daysLater, hour, minute);
|
|
82
|
+
const picked = pace.since !== undefined && !Number.isNaN(Date.parse(pace.since)) ? new Date(pace.since) : undefined;
|
|
83
|
+
// The first such time at or after the pick: the same day's when it had not passed yet.
|
|
84
|
+
const firstAfterPick = picked && (atOn(picked, 0).getTime() >= picked.getTime() ? atOn(picked, 0) : atOn(picked, 1));
|
|
85
|
+
if (!lastStart)
|
|
86
|
+
return firstAfterPick ?? atOn(now, 0);
|
|
87
|
+
const afterLast = atOn(lastStart, pace.every.count * UNIT_DAYS[pace.every.unit]);
|
|
88
|
+
return firstAfterPick && firstAfterPick.getTime() > afterLast.getTime() ? firstAfterPick : afterLast;
|
|
89
|
+
}
|
|
90
|
+
/** A pace as a tick's decision says it: `6h`, `2d at 10:00`. */
|
|
91
|
+
export function paceText(pace) {
|
|
92
|
+
return pace.at ? `${pace.every.text} at ${pace.at.text}` : pace.every.text;
|
|
93
|
+
}
|
|
94
|
+
/** A local time as a tick's decision says it: `2026-10-10 10:00`. */
|
|
95
|
+
export function localStamp(date) {
|
|
96
|
+
const two = (n) => String(n).padStart(2, '0');
|
|
97
|
+
return `${date.getFullYear()}-${two(date.getMonth() + 1)}-${two(date.getDate())} ${two(date.getHours())}:${two(date.getMinutes())}`;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=pace.js.map
|
package/dist/pace.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pace.js","sourceRoot":"","sources":["../src/pace.ts"],"names":[],"mappings":"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"}
|