@rse/ase 1.0.7 → 2.0.1
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/dst/ase-agent-definition-api.d.ts +184 -0
- package/dst/ase-agent-definition-api.js +6 -0
- package/dst/ase-agent-definition-builtin/condenser.js +42 -0
- package/dst/ase-agent-definition-builtin/editor.js +45 -0
- package/dst/ase-agent-definition-builtin/estimator.js +98 -0
- package/dst/ase-agent-definition-builtin/implementer.js +121 -0
- package/dst/ase-agent-definition-builtin/integrator.js +56 -0
- package/dst/ase-agent-definition-builtin/qualifier.js +61 -0
- package/dst/ase-agent-definition-builtin/rebooter.js +41 -0
- package/dst/ase-agent-definition-builtin/researcher.js +77 -0
- package/dst/ase-agent-definition-core.js +827 -0
- package/dst/ase-agent-fleet.js +772 -0
- package/dst/ase-agent-process.js +232 -0
- package/dst/ase-agent-relay-client.js +509 -0
- package/dst/ase-agent-relay-core.js +213 -0
- package/dst/ase-agent-relay-server-bind.js +242 -0
- package/dst/ase-agent-relay-server-cli.js +50 -0
- package/dst/ase-agent-runtime-claim.js +189 -0
- package/dst/ase-agent-runtime-engine.js +1100 -0
- package/dst/ase-agent-runtime-exec.js +275 -0
- package/dst/ase-agent-runtime-phase.js +581 -0
- package/dst/ase-agent-runtime-request.js +272 -0
- package/dst/ase-agent-runtime-tool.js +207 -0
- package/dst/ase-agent-runtime.js +1316 -0
- package/dst/ase-agent.js +65 -0
- package/dst/ase-artifact.js +6 -5
- package/dst/ase-config-core.js +465 -0
- package/dst/ase-config-schema.js +357 -0
- package/dst/ase-config-scope.js +172 -0
- package/dst/ase-config.js +25 -585
- package/dst/ase-hook.js +228 -111
- package/dst/ase-lib-daemon-cli.js +130 -0
- package/dst/ase-lib-hash.js +20 -0
- package/dst/{ase-log.js → ase-lib-log.js} +40 -7
- package/dst/ase-lib-server-bind.js +115 -0
- package/dst/ase-lib-server-cli.js +474 -0
- package/dst/{ase-table.js → ase-lib-table.js} +1 -1
- package/dst/{ase-version.js → ase-lib-version.js} +8 -1
- package/dst/ase-man.js +96 -0
- package/dst/ase-mcp.js +166 -13
- package/dst/{ase-getopt.js → ase-service-getopt.js} +82 -80
- package/dst/{ase-markdown.js → ase-service-markdown.js} +18 -11
- package/dst/ase-service-otel.js +1246 -0
- package/dst/{ase-skills.js → ase-service-skills.js} +29 -64
- package/dst/ase-service-state.js +101 -0
- package/dst/ase-service.js +269 -81
- package/dst/ase-setup-common.js +141 -0
- package/dst/ase-setup-mcp.js +359 -0
- package/dst/ase-setup-settings.js +837 -0
- package/dst/ase-setup.js +113 -869
- package/dst/ase-spec.js +31 -19
- package/dst/ase-statusline.js +108 -43
- package/dst/ase-task-board-core.js +739 -0
- package/dst/ase-task-board-filter.js +127 -0
- package/dst/ase-task-board-flow.js +237 -0
- package/dst/ase-task-board-graph.js +497 -0
- package/dst/ase-task-board-stats.js +263 -0
- package/dst/ase-task-board-tui-control.js +775 -0
- package/dst/ase-task-board-tui-model.js +1008 -0
- package/dst/ase-task-board-tui-popup.js +626 -0
- package/dst/ase-task-board-tui-style.js +106 -0
- package/dst/ase-task-board-tui-view.js +847 -0
- package/dst/ase-task-board-tui.js +189 -0
- package/dst/ase-task-board-web-client.css +1 -0
- package/dst/ase-task-board-web-client.html +20 -0
- package/dst/ase-task-board-web-client.js +81 -0
- package/dst/ase-task-board-web-server.js +823 -0
- package/dst/ase-task-board.js +134 -0
- package/dst/ase-task-format.js +757 -0
- package/dst/ase-task-request.js +208 -0
- package/dst/ase-task-store-client.js +495 -0
- package/dst/ase-task-store-core.js +804 -0
- package/dst/ase-task-store-issues.js +237 -0
- package/dst/ase-task-store-plugin-api.js +6 -0
- package/dst/ase-task-store-plugin-ase.js +484 -0
- package/dst/ase-task-store-plugin-delegate.js +137 -0
- package/dst/ase-task-store-plugin-gitea.js +666 -0
- package/dst/ase-task-store-plugin-github.js +696 -0
- package/dst/ase-task-store-plugin-gitlab.js +668 -0
- package/dst/ase-task-store-server-bind.js +538 -0
- package/dst/ase-task-store-server-cli.js +154 -0
- package/dst/ase-task.js +1335 -357
- package/dst/{ase-compat.js → ase-util-compat.js} +2 -2
- package/dst/{ase-diagram.js → ase-util-diagram.js} +107 -32
- package/dst/{ase-meta.js → ase-util-meta.js} +2 -2
- package/dst/{ase-metric.js → ase-util-metric.js} +2 -2
- package/dst/{ase-mint.js → ase-util-mint.js} +19 -12
- package/dst/{ase-worktree.js → ase-util-worktree.js} +30 -19
- package/dst/ase-util.js +20 -0
- package/dst/ase.js +108 -32
- package/dst/man/ase-agent-adhoc.1 +515 -0
- package/dst/man/ase-agent-condenser.1 +193 -0
- package/dst/man/ase-agent-editor.1 +212 -0
- package/dst/man/ase-agent-estimator.1 +214 -0
- package/dst/man/ase-agent-fleet.1 +185 -0
- package/dst/man/ase-agent-implementer.1 +233 -0
- package/dst/man/ase-agent-integrator.1 +213 -0
- package/dst/man/ase-agent-qualifier.1 +214 -0
- package/dst/man/ase-agent-rebooter.1 +193 -0
- package/dst/man/ase-agent-relay.1 +128 -0
- package/dst/man/ase-agent-researcher.1 +217 -0
- package/dst/man/ase-agent.1 +264 -0
- package/dst/man/ase-artifact-list.1 +38 -0
- package/dst/man/ase-artifact-name.1 +39 -0
- package/dst/man/ase-artifact.1 +56 -0
- package/dst/man/ase-config-delete.1 +45 -0
- package/dst/man/ase-config-edit.1 +48 -0
- package/dst/man/ase-config-get.1 +46 -0
- package/dst/man/ase-config-init.1 +60 -0
- package/dst/man/ase-config-list.1 +42 -0
- package/dst/man/ase-config-set.1 +52 -0
- package/dst/man/ase-config.1 +231 -0
- package/dst/man/ase-hook-permission-request.1 +36 -0
- package/dst/man/ase-hook-pre-tool-use.1 +59 -0
- package/dst/man/ase-hook-session-end.1 +47 -0
- package/dst/man/ase-hook-session-persona.1 +37 -0
- package/dst/man/ase-hook-session-start.1 +81 -0
- package/dst/man/ase-hook-stop.1 +34 -0
- package/dst/man/ase-hook-user-prompt-submit.1 +34 -0
- package/dst/man/ase-hook.1 +51 -0
- package/dst/man/ase-man.1 +67 -0
- package/dst/man/ase-mcp.1 +61 -0
- package/dst/man/ase-service-log.1 +43 -0
- package/dst/man/ase-service-send.1 +47 -0
- package/dst/man/ase-service-start.1 +54 -0
- package/dst/man/ase-service-status.1 +46 -0
- package/dst/man/ase-service-stop.1 +37 -0
- package/dst/man/ase-service.1 +85 -0
- package/dst/man/ase-setup-disable.1 +44 -0
- package/dst/man/ase-setup-enable.1 +45 -0
- package/dst/man/ase-setup-install.1 +73 -0
- package/dst/man/ase-setup-mcp.1 +124 -0
- package/dst/man/ase-setup-status.1 +60 -0
- package/dst/man/ase-setup-statusline.1 +92 -0
- package/dst/man/ase-setup-telemetry.1 +92 -0
- package/dst/man/ase-setup-uninstall.1 +59 -0
- package/dst/man/ase-setup-update.1 +64 -0
- package/dst/man/ase-setup.1 +93 -0
- package/dst/man/ase-spec-export.1 +62 -0
- package/dst/man/ase-spec-lint.1 +40 -0
- package/dst/man/ase-spec-preview.1 +40 -0
- package/dst/man/ase-spec.1 +46 -0
- package/dst/man/ase-statusline.1 +189 -0
- package/dst/man/ase-task-assign.1 +71 -0
- package/dst/man/ase-task-board.1 +384 -0
- package/dst/man/ase-task-delete.1 +35 -0
- package/dst/man/ase-task-edit.1 +46 -0
- package/dst/man/ase-task-idscheme.1 +65 -0
- package/dst/man/ase-task-lifecycle.1 +57 -0
- package/dst/man/ase-task-list.1 +47 -0
- package/dst/man/ase-task-load.1 +36 -0
- package/dst/man/ase-task-newid.1 +61 -0
- package/dst/man/ase-task-purge.1 +37 -0
- package/dst/man/ase-task-rename.1 +44 -0
- package/dst/man/ase-task-request.1 +61 -0
- package/dst/man/ase-task-save.1 +48 -0
- package/dst/man/ase-task-status.1 +55 -0
- package/dst/man/ase-task-store.1 +194 -0
- package/dst/man/ase-task-view.1 +40 -0
- package/dst/man/ase-task.1 +160 -0
- package/dst/man/ase-util-compat.1 +25 -0
- package/dst/man/ase-util-diagram.1 +76 -0
- package/dst/man/ase-util-meta.1 +45 -0
- package/dst/man/ase-util-metric.1 +54 -0
- package/dst/man/ase-util-mint.1 +59 -0
- package/dst/man/ase-util-worktree.1 +72 -0
- package/dst/man/ase-util.1 +50 -0
- package/dst/man/ase.1 +124 -0
- package/package.json +81 -21
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.github/plugin/plugin.json +1 -1
- package/plugin/agents/ase-meta-diagram.md +12 -8
- package/plugin/agents/ase-repo-review.md +322 -0
- package/plugin/hooks/hooks-codex.json +5 -0
- package/plugin/hooks/hooks-copilot.json +5 -0
- package/plugin/hooks/hooks.json +8 -0
- package/plugin/meta/ase-common-approach.md +117 -0
- package/plugin/meta/ase-common-changeset.md +256 -0
- package/plugin/meta/ase-common-code.md +10 -173
- package/plugin/meta/ase-common-grill.md +317 -0
- package/plugin/meta/ase-common-resolve.md +305 -0
- package/plugin/meta/ase-common-task.md +83 -4
- package/plugin/meta/ase-constitution.md +0 -2
- package/plugin/meta/ase-format-specbook.md +208 -25
- package/plugin/meta/ase-format-specbook.yaml +242 -851
- package/plugin/meta/ase-format-task.md +669 -125
- package/plugin/meta/ase-persona.md +3 -4
- package/plugin/meta/ase-skill.md +9 -0
- package/plugin/mods/ase-mods.ts +409 -0
- package/plugin/mods/claude-code.d.ts +13417 -0
- package/plugin/skills/ase-arch-analyze/SKILL.md +1 -1
- package/plugin/skills/ase-arch-analyze/help.md +2 -2
- package/plugin/skills/ase-code-analyze/SKILL.md +59 -8
- package/plugin/skills/ase-code-analyze/help.md +29 -4
- package/plugin/skills/ase-code-edit/SKILL.md +107 -181
- package/plugin/skills/ase-code-edit/help.md +118 -31
- package/plugin/skills/ase-code-lint/SKILL.md +64 -13
- package/plugin/skills/ase-code-lint/help.md +32 -2
- package/plugin/skills/ase-docs-proofread/SKILL.md +4 -4
- package/plugin/skills/ase-docs-proofread/help.md +1 -1
- package/plugin/skills/ase-docs-refine/SKILL.md +4 -4
- package/plugin/skills/ase-docs-shorten/SKILL.md +4 -4
- package/plugin/skills/ase-help-intent/data.md +2164 -1087
- package/plugin/skills/ase-help-intent/help.md +1 -1
- package/plugin/skills/ase-help-skill/catalog.md +12 -9
- package/plugin/skills/ase-meta-brainstorm/SKILL.md +1 -1
- package/plugin/skills/ase-meta-config/SKILL.md +3 -4
- package/plugin/skills/ase-meta-config/help.md +31 -10
- package/plugin/skills/ase-meta-mint/SKILL.md +5 -5
- package/plugin/skills/ase-meta-mint/help.md +10 -5
- package/plugin/skills/ase-meta-why/help.md +1 -1
- package/plugin/skills/ase-meta-workflow/SKILL.md +7 -7
- package/plugin/skills/ase-meta-workflow/help.md +4 -4
- package/plugin/skills/ase-meta-workflow/sample.md +3 -3
- package/plugin/skills/ase-meta-workflow/workflow.txt +22 -18
- package/plugin/skills/{ase-meta-changelog → ase-repo-changelog}/SKILL.md +4 -3
- package/plugin/skills/{ase-meta-changelog → ase-repo-changelog}/help.md +5 -5
- package/plugin/skills/{ase-meta-commit → ase-repo-commit}/SKILL.md +6 -6
- package/plugin/skills/{ase-meta-commit → ase-repo-commit}/help.md +5 -5
- package/plugin/skills/{ase-meta-diff → ase-repo-diff}/SKILL.md +6 -6
- package/plugin/skills/{ase-meta-diff → ase-repo-diff}/help.md +8 -8
- package/plugin/skills/{ase-code-dissect → ase-repo-dissect}/SKILL.md +19 -19
- package/plugin/skills/{ase-code-dissect → ase-repo-dissect}/help.md +10 -12
- package/plugin/skills/ase-repo-merge/SKILL.md +346 -0
- package/plugin/skills/ase-repo-merge/help.md +107 -0
- package/plugin/skills/ase-repo-resolve/SKILL.md +158 -0
- package/plugin/skills/ase-repo-resolve/help.md +102 -0
- package/plugin/skills/ase-repo-review/SKILL.md +406 -0
- package/plugin/skills/ase-repo-review/help.md +181 -0
- package/plugin/skills/ase-spec-edit/SKILL.md +117 -183
- package/plugin/skills/ase-spec-edit/help.md +107 -26
- package/plugin/skills/ase-sync-reconcile/help.md +1 -1
- package/plugin/skills/ase-task-assign/SKILL.md +111 -0
- package/plugin/skills/ase-task-assign/help.md +119 -0
- package/plugin/skills/ase-task-condense/SKILL.md +21 -21
- package/plugin/skills/ase-task-condense/help.md +12 -10
- package/plugin/skills/ase-task-delete/SKILL.md +1 -1
- package/plugin/skills/ase-task-dissect/SKILL.md +71 -48
- package/plugin/skills/ase-task-dissect/help.md +10 -9
- package/plugin/skills/ase-task-edit/SKILL.md +379 -94
- package/plugin/skills/ase-task-edit/help.md +95 -22
- package/plugin/skills/ase-task-estimate/SKILL.md +492 -0
- package/plugin/skills/ase-task-estimate/help.md +158 -0
- package/plugin/skills/ase-task-grill/SKILL.md +246 -138
- package/plugin/skills/ase-task-grill/help.md +129 -26
- package/plugin/skills/ase-task-id/SKILL.md +8 -1
- package/plugin/skills/ase-task-id/help.md +6 -3
- package/plugin/skills/ase-task-implement/SKILL.md +535 -145
- package/plugin/skills/ase-task-implement/help.md +138 -47
- package/plugin/skills/ase-task-integrate/SKILL.md +549 -0
- package/plugin/skills/ase-task-integrate/help.md +187 -0
- package/plugin/skills/ase-task-list/SKILL.md +55 -23
- package/plugin/skills/ase-task-list/help.md +45 -21
- package/plugin/skills/ase-task-reboot/SKILL.md +84 -43
- package/plugin/skills/ase-task-reboot/help.md +28 -8
- package/plugin/skills/ase-task-rename/SKILL.md +10 -2
- package/plugin/skills/ase-task-research/SKILL.md +386 -0
- package/plugin/skills/ase-task-research/help.md +102 -0
- package/plugin/skills/ase-task-status/SKILL.md +137 -0
- package/plugin/skills/ase-task-status/help.md +80 -0
- package/plugin/skills/ase-task-view/SKILL.md +49 -24
- package/plugin/skills/ase-task-view/help.md +19 -9
- package/plugin/agents/ase-meta-review.md +0 -187
- package/plugin/skills/ase-code-craft/SKILL.md +0 -264
- package/plugin/skills/ase-code-craft/help.md +0 -122
- package/plugin/skills/ase-code-refactor/SKILL.md +0 -232
- package/plugin/skills/ase-code-refactor/help.md +0 -109
- package/plugin/skills/ase-code-resolve/SKILL.md +0 -289
- package/plugin/skills/ase-code-resolve/help.md +0 -116
- package/plugin/skills/ase-meta-review/SKILL.md +0 -210
- package/plugin/skills/ase-meta-review/help.md +0 -96
- package/plugin/skills/ase-task-preflight/SKILL.md +0 -166
- package/plugin/skills/ase-task-preflight/help.md +0 -82
- /package/dst/{ase-ignore.js → ase-lib-ignore.js} +0 -0
- /package/dst/{ase-stdio.js → ase-lib-stdio.js} +0 -0
- /package/dst/{ase-kv.js → ase-service-kv.js} +0 -0
- /package/dst/{ase-sleep.js → ase-service-sleep.js} +0 -0
- /package/dst/{ase-timestamp.js → ase-service-timestamp.js} +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** Agentic Software Engineering (ASE)
|
|
3
|
+
** Copyright (c) 2025-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
|
+
*/
|
|
6
|
+
import crypto from "node:crypto";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import lockfile from "proper-lockfile";
|
|
11
|
+
import { Task } from "./ase-task.js";
|
|
12
|
+
import { userStateDir } from "./ase-config-scope.js";
|
|
13
|
+
import { pidAlive, pidStart } from "./ase-agent-runtime-exec.js";
|
|
14
|
+
/* the duration after which the run lock of a terminated agent process counts as stale */
|
|
15
|
+
const runStale = 30 * 1000;
|
|
16
|
+
/* the identity of the per-user state directory on this host, which holds the run locks,
|
|
17
|
+
so only the runs of the same user (and home directory) on the same host are recovered */
|
|
18
|
+
export const runOwner = crypto.createHash("sha1")
|
|
19
|
+
.update(`${os.hostname()}\n${userStateDir()}`).digest("hex").slice(0, 12);
|
|
20
|
+
/* the run of an agent process of a kind, guarded by a run lock in the per-user state
|
|
21
|
+
directory (hence per host and user), below a sub-directory per task store (as the
|
|
22
|
+
recorded task ids are unique per task store only, so no run of another project is
|
|
23
|
+
ever recovered against the wrong tasks), and recording the process id and the claims
|
|
24
|
+
of the tasks processed by it, so the agents of the same kind started later for the
|
|
25
|
+
same task store can recover the tasks left behind by the terminated runs (whose run
|
|
26
|
+
lock is gone or stale, and whose process is no longer alive, where the recorded start
|
|
27
|
+
time of the process guards against a process id reused meanwhile by another process) */
|
|
28
|
+
export class AgentRun {
|
|
29
|
+
log;
|
|
30
|
+
kind;
|
|
31
|
+
id = crypto.randomUUID();
|
|
32
|
+
unlock = null;
|
|
33
|
+
claims = new Map();
|
|
34
|
+
seized = new Map();
|
|
35
|
+
start = "";
|
|
36
|
+
_dir = "";
|
|
37
|
+
constructor(log, kind) {
|
|
38
|
+
this.log = log;
|
|
39
|
+
this.kind = kind;
|
|
40
|
+
}
|
|
41
|
+
/* the directory of the runs of the agent kind for the task store, and the files of a run */
|
|
42
|
+
get dir() {
|
|
43
|
+
if (this._dir === "")
|
|
44
|
+
this._dir = path.join(userStateDir(), `agent-${this.kind}`, crypto.createHash("sha1").update(Task.storeKey(this.log)).digest("hex").slice(0, 12));
|
|
45
|
+
return this._dir;
|
|
46
|
+
}
|
|
47
|
+
file(run, ext = "") {
|
|
48
|
+
return path.join(this.dir, `${run}${ext}`);
|
|
49
|
+
}
|
|
50
|
+
/* the record (process id, its start time, and claims) of a run */
|
|
51
|
+
record(run) {
|
|
52
|
+
try {
|
|
53
|
+
const data = JSON.parse(fs.readFileSync(this.file(run, ".json"), "utf8"));
|
|
54
|
+
return { pid: Number(data.pid ?? 0), start: String(data.start ?? ""), claims: data.claims ?? {} };
|
|
55
|
+
}
|
|
56
|
+
catch (_err) {
|
|
57
|
+
return { pid: 0, start: "", claims: {} };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/* save the record of a run (atomically) */
|
|
61
|
+
store(run, record) {
|
|
62
|
+
const file = this.file(run, ".json");
|
|
63
|
+
fs.writeFileSync(`${file}.tmp`, JSON.stringify(record));
|
|
64
|
+
fs.renameSync(`${file}.tmp`, file);
|
|
65
|
+
}
|
|
66
|
+
save() {
|
|
67
|
+
this.store(this.id, { pid: process.pid, start: this.start, claims: Object.fromEntries(this.claims) });
|
|
68
|
+
}
|
|
69
|
+
/* acquire the run lock (refreshed periodically, where a compromised one, e.g. one
|
|
70
|
+
removed as stale while this process was suspended, is reported instead of thrown) */
|
|
71
|
+
async acquire() {
|
|
72
|
+
fs.mkdirSync(this.dir, { recursive: true });
|
|
73
|
+
this.start = await pidStart(process.pid);
|
|
74
|
+
this.unlock = await lockfile.lock(this.file(this.id), {
|
|
75
|
+
realpath: false, stale: runStale, update: runStale / 3,
|
|
76
|
+
onCompromised: (err) => {
|
|
77
|
+
this.log.write("warning", `agent: run lock of ${this.kind} compromised: ${err.message}`);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
this.save();
|
|
81
|
+
}
|
|
82
|
+
/* release the run lock (and the run locks of the terminated runs still seized), and remove
|
|
83
|
+
the record of this run, unless it still records claims (to be recovered by a later run) */
|
|
84
|
+
async release() {
|
|
85
|
+
for (const run of [...this.seized.keys()])
|
|
86
|
+
await this.free(run);
|
|
87
|
+
if (this.unlock === null)
|
|
88
|
+
return;
|
|
89
|
+
await this.unlock().catch(() => { });
|
|
90
|
+
this.unlock = null;
|
|
91
|
+
if (this.claims.size === 0)
|
|
92
|
+
fs.rmSync(this.file(this.id, ".json"), { force: true });
|
|
93
|
+
}
|
|
94
|
+
/* whether a run is alive: this run, a run whose process (by its given or recorded
|
|
95
|
+
process id, and, if recorded, by the start time of the process, so a reused process
|
|
96
|
+
id does not count) is still alive, or one whose run lock is still held */
|
|
97
|
+
async alive(run, pid = 0) {
|
|
98
|
+
if (run === "")
|
|
99
|
+
return false;
|
|
100
|
+
else if (run === this.id)
|
|
101
|
+
return true;
|
|
102
|
+
const record = this.record(run);
|
|
103
|
+
const id = pid > 0 ? pid : record.pid;
|
|
104
|
+
const start = id === record.pid ? record.start : "";
|
|
105
|
+
if (pidAlive(id) && (start === "" || await pidStart(id) === start))
|
|
106
|
+
return true;
|
|
107
|
+
return lockfile.check(this.file(run), { realpath: false, stale: runStale }).catch(() => false);
|
|
108
|
+
}
|
|
109
|
+
/* the runs of the agent kind for the task store (by their run lock or record) */
|
|
110
|
+
runs() {
|
|
111
|
+
return [...new Set(fs.readdirSync(this.dir)
|
|
112
|
+
.filter((entry) => /\.(?:lock|json)$/.test(entry))
|
|
113
|
+
.map((entry) => entry.replace(/\.(?:lock|json)$/, "")))];
|
|
114
|
+
}
|
|
115
|
+
/* the terminated runs of the agent kind for the task store, with their recorded claims */
|
|
116
|
+
async dead() {
|
|
117
|
+
const dead = [];
|
|
118
|
+
for (const run of this.runs())
|
|
119
|
+
if (!(await this.alive(run)))
|
|
120
|
+
dead.push({ run, claims: this.record(run).claims });
|
|
121
|
+
return dead;
|
|
122
|
+
}
|
|
123
|
+
/* seize a terminated run for its recovery, by taking over its (stale or removed) run lock,
|
|
124
|
+
so the agents started concurrently never recover the same run, returning its claims
|
|
125
|
+
recorded at that time (as a concurrent recovery might have reduced them meanwhile),
|
|
126
|
+
or null if the run is seized by another agent (or is alive again) */
|
|
127
|
+
async seize(run) {
|
|
128
|
+
if (this.seized.has(run))
|
|
129
|
+
return this.record(run).claims;
|
|
130
|
+
try {
|
|
131
|
+
const unlock = await lockfile.lock(this.file(run), {
|
|
132
|
+
realpath: false, stale: runStale, update: runStale / 3, onCompromised: () => { }
|
|
133
|
+
});
|
|
134
|
+
this.seized.set(run, unlock);
|
|
135
|
+
}
|
|
136
|
+
catch (_err) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return this.record(run).claims;
|
|
140
|
+
}
|
|
141
|
+
/* release the run lock of a seized terminated run, without removing its record */
|
|
142
|
+
async free(run) {
|
|
143
|
+
const unlock = this.seized.get(run);
|
|
144
|
+
if (unlock === undefined)
|
|
145
|
+
return;
|
|
146
|
+
this.seized.delete(run);
|
|
147
|
+
await unlock().catch(() => { });
|
|
148
|
+
}
|
|
149
|
+
/* forget a recovered claim of a seized terminated run (so it is never recovered again,
|
|
150
|
+
even if the recovery of another claim of the run fails and is retried by a later run) */
|
|
151
|
+
forget(run, id) {
|
|
152
|
+
const record = this.record(run);
|
|
153
|
+
if (record.pid === 0 || !Object.hasOwn(record.claims, id))
|
|
154
|
+
return;
|
|
155
|
+
record.claims = Object.fromEntries(Object.entries(record.claims).filter(([key]) => key !== id));
|
|
156
|
+
this.store(run, record);
|
|
157
|
+
}
|
|
158
|
+
/* whether a task is claimed by this run or by another run whose process is still
|
|
159
|
+
alive (independent of its run lock, which is also held while a terminated run is
|
|
160
|
+
seized for its recovery), i.e., by an agent still processing it */
|
|
161
|
+
async claimed(id) {
|
|
162
|
+
if (this.claims.has(id))
|
|
163
|
+
return true;
|
|
164
|
+
for (const run of this.runs()) {
|
|
165
|
+
const record = this.record(run);
|
|
166
|
+
if (run !== this.id && Object.hasOwn(record.claims, id) && pidAlive(record.pid)
|
|
167
|
+
&& (record.start === "" || await pidStart(record.pid) === record.start))
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
/* remove the record and the run lock of a (seized) terminated run */
|
|
173
|
+
async purge(run) {
|
|
174
|
+
fs.rmSync(this.file(run, ".json"), { force: true });
|
|
175
|
+
if (this.seized.has(run))
|
|
176
|
+
await this.free(run);
|
|
177
|
+
else
|
|
178
|
+
fs.rmSync(this.file(run, ".lock"), { recursive: true, force: true });
|
|
179
|
+
}
|
|
180
|
+
/* record resp. forget the claim of a task by this run */
|
|
181
|
+
claim(id, claim) {
|
|
182
|
+
this.claims.set(id, claim);
|
|
183
|
+
this.save();
|
|
184
|
+
}
|
|
185
|
+
unclaim(id) {
|
|
186
|
+
if (this.claims.delete(id))
|
|
187
|
+
this.save();
|
|
188
|
+
}
|
|
189
|
+
}
|