ghostrail 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +16 -0
- package/README.md +56 -0
- package/dist/agent/claude-code.d.ts +37 -0
- package/dist/agent/claude-code.d.ts.map +1 -0
- package/dist/agent/claude-code.js +74 -0
- package/dist/agent/claude-code.js.map +1 -0
- package/dist/agent/index.d.ts +4 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +4 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/prompt.d.ts +9 -0
- package/dist/agent/prompt.d.ts.map +1 -0
- package/dist/agent/prompt.js +15 -0
- package/dist/agent/prompt.js.map +1 -0
- package/dist/agent/result.d.ts +16 -0
- package/dist/agent/result.d.ts.map +1 -0
- package/dist/agent/result.js +56 -0
- package/dist/agent/result.js.map +1 -0
- package/dist/args.d.ts +27 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +25 -0
- package/dist/args.js.map +1 -0
- package/dist/backend/container.d.ts +18 -0
- package/dist/backend/container.d.ts.map +1 -0
- package/dist/backend/container.js +70 -0
- package/dist/backend/container.js.map +1 -0
- package/dist/backend/docker.d.ts +22 -0
- package/dist/backend/docker.d.ts.map +1 -0
- package/dist/backend/docker.js +38 -0
- package/dist/backend/docker.js.map +1 -0
- package/dist/backend/fs.d.ts +9 -0
- package/dist/backend/fs.d.ts.map +1 -0
- package/dist/backend/fs.js +21 -0
- package/dist/backend/fs.js.map +1 -0
- package/dist/backend/index.d.ts +7 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +6 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/local-worktree.d.ts +17 -0
- package/dist/backend/local-worktree.d.ts.map +1 -0
- package/dist/backend/local-worktree.js +51 -0
- package/dist/backend/local-worktree.js.map +1 -0
- package/dist/backend/proc.d.ts +24 -0
- package/dist/backend/proc.d.ts.map +1 -0
- package/dist/backend/proc.js +103 -0
- package/dist/backend/proc.js.map +1 -0
- package/dist/backend/types.d.ts +75 -0
- package/dist/backend/types.d.ts.map +1 -0
- package/dist/backend/types.js +12 -0
- package/dist/backend/types.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +10 -0
- package/dist/bin.js.map +1 -0
- package/dist/board/index.d.ts +4 -0
- package/dist/board/index.d.ts.map +1 -0
- package/dist/board/index.js +4 -0
- package/dist/board/index.js.map +1 -0
- package/dist/board/page.d.ts +3 -0
- package/dist/board/page.d.ts.map +1 -0
- package/dist/board/page.js +55 -0
- package/dist/board/page.js.map +1 -0
- package/dist/board/server.d.ts +36 -0
- package/dist/board/server.d.ts.map +1 -0
- package/dist/board/server.js +110 -0
- package/dist/board/server.js.map +1 -0
- package/dist/board/stats.d.ts +26 -0
- package/dist/board/stats.d.ts.map +1 -0
- package/dist/board/stats.js +57 -0
- package/dist/board/stats.js.map +1 -0
- package/dist/cli/commands.d.ts +39 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +224 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +45 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/help.d.ts +6 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +29 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/config/duration.d.ts +14 -0
- package/dist/config/duration.d.ts.map +1 -0
- package/dist/config/duration.js +25 -0
- package/dist/config/duration.js.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/load.d.ts +26 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +306 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/result.d.ts +18 -0
- package/dist/config/result.d.ts.map +1 -0
- package/dist/config/result.js +7 -0
- package/dist/config/result.js.map +1 -0
- package/dist/config/schema.d.ts +77 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +35 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/factory/build.d.ts +39 -0
- package/dist/factory/build.d.ts.map +1 -0
- package/dist/factory/build.js +85 -0
- package/dist/factory/build.js.map +1 -0
- package/dist/gate/gate.d.ts +12 -0
- package/dist/gate/gate.d.ts.map +1 -0
- package/dist/gate/gate.js +22 -0
- package/dist/gate/gate.js.map +1 -0
- package/dist/gate/index.d.ts +2 -0
- package/dist/gate/index.d.ts.map +1 -0
- package/dist/gate/index.js +2 -0
- package/dist/gate/index.js.map +1 -0
- package/dist/global/index.d.ts +3 -0
- package/dist/global/index.d.ts.map +1 -0
- package/dist/global/index.js +3 -0
- package/dist/global/index.js.map +1 -0
- package/dist/global/stage.d.ts +13 -0
- package/dist/global/stage.d.ts.map +1 -0
- package/dist/global/stage.js +24 -0
- package/dist/global/stage.js.map +1 -0
- package/dist/global/store.d.ts +30 -0
- package/dist/global/store.d.ts.map +1 -0
- package/dist/global/store.js +65 -0
- package/dist/global/store.js.map +1 -0
- package/dist/lock/file-lock.d.ts +16 -0
- package/dist/lock/file-lock.d.ts.map +1 -0
- package/dist/lock/file-lock.js +51 -0
- package/dist/lock/file-lock.js.map +1 -0
- package/dist/loop/branch.d.ts +14 -0
- package/dist/loop/branch.d.ts.map +1 -0
- package/dist/loop/branch.js +24 -0
- package/dist/loop/branch.js.map +1 -0
- package/dist/loop/engine.d.ts +37 -0
- package/dist/loop/engine.d.ts.map +1 -0
- package/dist/loop/engine.js +204 -0
- package/dist/loop/engine.js.map +1 -0
- package/dist/loop/index.d.ts +6 -0
- package/dist/loop/index.d.ts.map +1 -0
- package/dist/loop/index.js +3 -0
- package/dist/loop/index.js.map +1 -0
- package/dist/loop/ports.d.ts +97 -0
- package/dist/loop/ports.d.ts.map +1 -0
- package/dist/loop/ports.js +2 -0
- package/dist/loop/ports.js.map +1 -0
- package/dist/loop/record.d.ts +26 -0
- package/dist/loop/record.d.ts.map +1 -0
- package/dist/loop/record.js +2 -0
- package/dist/loop/record.js.map +1 -0
- package/dist/publish/gh.d.ts +24 -0
- package/dist/publish/gh.d.ts.map +1 -0
- package/dist/publish/gh.js +109 -0
- package/dist/publish/gh.js.map +1 -0
- package/dist/publish/githost.d.ts +54 -0
- package/dist/publish/githost.d.ts.map +1 -0
- package/dist/publish/githost.js +33 -0
- package/dist/publish/githost.js.map +1 -0
- package/dist/publish/github-publisher.d.ts +30 -0
- package/dist/publish/github-publisher.d.ts.map +1 -0
- package/dist/publish/github-publisher.js +51 -0
- package/dist/publish/github-publisher.js.map +1 -0
- package/dist/publish/index.d.ts +4 -0
- package/dist/publish/index.d.ts.map +1 -0
- package/dist/publish/index.js +4 -0
- package/dist/publish/index.js.map +1 -0
- package/dist/respond/index.d.ts +4 -0
- package/dist/respond/index.d.ts.map +1 -0
- package/dist/respond/index.js +4 -0
- package/dist/respond/index.js.map +1 -0
- package/dist/respond/respond.d.ts +42 -0
- package/dist/respond/respond.d.ts.map +1 -0
- package/dist/respond/respond.js +128 -0
- package/dist/respond/respond.js.map +1 -0
- package/dist/respond/select.d.ts +22 -0
- package/dist/respond/select.d.ts.map +1 -0
- package/dist/respond/select.js +31 -0
- package/dist/respond/select.js.map +1 -0
- package/dist/respond/store.d.ts +25 -0
- package/dist/respond/store.d.ts.map +1 -0
- package/dist/respond/store.js +49 -0
- package/dist/respond/store.js.map +1 -0
- package/dist/secrets/redact.d.ts +11 -0
- package/dist/secrets/redact.d.ts.map +1 -0
- package/dist/secrets/redact.js +21 -0
- package/dist/secrets/redact.js.map +1 -0
- package/dist/source/index.d.ts +2 -0
- package/dist/source/index.d.ts.map +1 -0
- package/dist/source/index.js +2 -0
- package/dist/source/index.js.map +1 -0
- package/dist/source/linear.d.ts +61 -0
- package/dist/source/linear.d.ts.map +1 -0
- package/dist/source/linear.js +166 -0
- package/dist/source/linear.js.map +1 -0
- package/dist/store/events.d.ts +105 -0
- package/dist/store/events.d.ts.map +1 -0
- package/dist/store/events.js +64 -0
- package/dist/store/events.js.map +1 -0
- package/dist/store/file-store.d.ts +49 -0
- package/dist/store/file-store.d.ts.map +1 -0
- package/dist/store/file-store.js +188 -0
- package/dist/store/file-store.js.map +1 -0
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +3 -0
- package/dist/store/index.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +21 -0
- package/dist/version.js.map +1 -0
- package/package.json +52 -0
- package/templates/content-loop/README.md +23 -0
- package/templates/content-loop/ghostrail.toml +36 -0
- package/templates/content-loop/prompts/draft.md +29 -0
- package/templates/fix-bugs-local/README.md +23 -0
- package/templates/fix-bugs-local/ghostrail.toml +37 -0
- package/templates/fix-bugs-local/prompts/fix-bug.md +27 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The typed ghostrail configuration, normalized from `ghostrail.toml`.
|
|
3
|
+
*
|
|
4
|
+
* The TOML uses snake_case keys (`base_branch`, `allowed_tools`, ...); parsing
|
|
5
|
+
* normalizes them to the camelCase fields below. Only the Phase 0 surface is
|
|
6
|
+
* modeled here; `[store]` and `[board]` arrive in later phases.
|
|
7
|
+
*/
|
|
8
|
+
export const SOURCE_KINDS = ["linear", "github-issues", "beads"];
|
|
9
|
+
export const BACKEND_KINDS = [
|
|
10
|
+
"local-worktree",
|
|
11
|
+
"container",
|
|
12
|
+
"firecracker",
|
|
13
|
+
"e2b",
|
|
14
|
+
"remote",
|
|
15
|
+
];
|
|
16
|
+
/** Default agent tool allowlist: read + write files and run shell/search only. */
|
|
17
|
+
export const DEFAULT_ALLOWED_TOOLS = [
|
|
18
|
+
"Read",
|
|
19
|
+
"Edit",
|
|
20
|
+
"Write",
|
|
21
|
+
"Bash",
|
|
22
|
+
"Grep",
|
|
23
|
+
"Glob",
|
|
24
|
+
];
|
|
25
|
+
/** Guardrail defaults, carried over from the reference factory. */
|
|
26
|
+
export const DEFAULT_GUARDRAILS = {
|
|
27
|
+
maxItemsPerRun: 3,
|
|
28
|
+
budgetUsdPerItem: 3,
|
|
29
|
+
timeoutMs: 30 * 60_000,
|
|
30
|
+
maxCostUsdPerRun: 5,
|
|
31
|
+
claimTtlMs: 2 * 3_600_000,
|
|
32
|
+
};
|
|
33
|
+
export const DEFAULT_BASE_BRANCH = "main";
|
|
34
|
+
export const DEFAULT_BACKEND_KIND = "local-worktree";
|
|
35
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,CAAU,CAAC;AAG1E,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,gBAAgB;IAChB,WAAW;IACX,aAAa;IACb,KAAK;IACL,QAAQ;CACA,CAAC;AAuEX,kFAAkF;AAClF,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAC;AAEF,mEAAmE;AACnE,MAAM,CAAC,MAAM,kBAAkB,GAAe;IAC5C,cAAc,EAAE,CAAC;IACjB,gBAAgB,EAAE,CAAC;IACnB,SAAS,EAAE,EAAE,GAAG,MAAM;IACtB,gBAAgB,EAAE,CAAC;IACnB,UAAU,EAAE,CAAC,GAAG,SAAS;CAC1B,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAC1C,MAAM,CAAC,MAAM,oBAAoB,GAAgB,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Backend } from "../backend/types.js";
|
|
2
|
+
import type { GhostrailConfig } from "../config/schema.js";
|
|
3
|
+
import type { GlobalStore } from "../global/store.js";
|
|
4
|
+
import type { LoopDeps } from "../loop/engine.js";
|
|
5
|
+
import type { Lock, Logger, Source } from "../loop/ports.js";
|
|
6
|
+
import { type GhRunner } from "../publish/gh.js";
|
|
7
|
+
import type { RespondConfig, RespondDeps } from "../respond/respond.js";
|
|
8
|
+
/** The store directory inside a repo (gitignored). */
|
|
9
|
+
export declare const STORE_DIR = ".factory";
|
|
10
|
+
/** Runtime inputs the config cannot supply: paths, secrets, and injectables. */
|
|
11
|
+
export interface BuildContext {
|
|
12
|
+
/** Absolute path to the repo the factory operates on. */
|
|
13
|
+
repoPath: string;
|
|
14
|
+
/** Linear personal API key (from the environment, never the config). */
|
|
15
|
+
linearApiKey: string;
|
|
16
|
+
/** The global lock instance. */
|
|
17
|
+
lock: Lock;
|
|
18
|
+
/** Bot logins whose PR comments the respond pass ignores. */
|
|
19
|
+
selfLogins?: readonly string[];
|
|
20
|
+
/** Global store for staging shared artifacts. */
|
|
21
|
+
globalStore?: GlobalStore;
|
|
22
|
+
logger?: Logger;
|
|
23
|
+
fetchImpl?: typeof fetch;
|
|
24
|
+
ghRunner?: GhRunner;
|
|
25
|
+
}
|
|
26
|
+
/** Construct the isolation backend named by the config. */
|
|
27
|
+
export declare function buildBackend(config: GhostrailConfig): Backend;
|
|
28
|
+
/** Construct the tracker source named by the config (Linear only in Phase 0). */
|
|
29
|
+
export declare function buildSource(config: GhostrailConfig, ctx: BuildContext): Source;
|
|
30
|
+
/** A gh runner that shells out to `gh` in the repo directory. */
|
|
31
|
+
export declare function repoGhRunner(repoPath: string): GhRunner;
|
|
32
|
+
/** Assemble the loop dependencies for `ghostrail run`. */
|
|
33
|
+
export declare function buildLoopDeps(config: GhostrailConfig, ctx: BuildContext): LoopDeps;
|
|
34
|
+
/** Assemble the respond dependencies + config for `ghostrail respond`. */
|
|
35
|
+
export declare function buildRespondDeps(config: GhostrailConfig, ctx: BuildContext): {
|
|
36
|
+
deps: RespondDeps;
|
|
37
|
+
config: RespondConfig;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/factory/build.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIxE,sDAAsD;AACtD,eAAO,MAAM,SAAS,aAAa,CAAC;AAGpC,gFAAgF;AAChF,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,IAAI,EAAE,IAAI,CAAC;IACX,6DAA6D;IAC7D,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,iDAAiD;IACjD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAW7D;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,YAAY,GAAG,MAAM,CAa9E;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAEvD;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,YAAY,GAAG,QAAQ,CAmBlF;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,YAAY,GAChB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAkB9C"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { ClaudeCodeAgent } from "../agent/claude-code.js";
|
|
3
|
+
import { ContainerBackend } from "../backend/container.js";
|
|
4
|
+
import { LocalWorktreeBackend } from "../backend/local-worktree.js";
|
|
5
|
+
import { spawnCollect } from "../backend/proc.js";
|
|
6
|
+
import { CommandGate } from "../gate/gate.js";
|
|
7
|
+
import { GhHost } from "../publish/gh.js";
|
|
8
|
+
import { GitHubPublisher } from "../publish/github-publisher.js";
|
|
9
|
+
import { FileWatermarkStore } from "../respond/store.js";
|
|
10
|
+
import { FileStore } from "../store/file-store.js";
|
|
11
|
+
/** The store directory inside a repo (gitignored). */
|
|
12
|
+
export const STORE_DIR = ".factory";
|
|
13
|
+
import { LinearSource, createFetchRequester } from "../source/linear.js";
|
|
14
|
+
/** Construct the isolation backend named by the config. */
|
|
15
|
+
export function buildBackend(config) {
|
|
16
|
+
switch (config.backend.kind) {
|
|
17
|
+
case "local-worktree":
|
|
18
|
+
return new LocalWorktreeBackend();
|
|
19
|
+
case "container":
|
|
20
|
+
return new ContainerBackend();
|
|
21
|
+
default:
|
|
22
|
+
throw new Error(`backend "${config.backend.kind}" is not available yet (Phase 0 ships local-worktree and container)`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Construct the tracker source named by the config (Linear only in Phase 0). */
|
|
26
|
+
export function buildSource(config, ctx) {
|
|
27
|
+
if (config.source.kind !== "linear") {
|
|
28
|
+
throw new Error(`source "${config.source.kind}" is not available yet (Phase 0 ships linear)`);
|
|
29
|
+
}
|
|
30
|
+
if (config.source.project === undefined) {
|
|
31
|
+
throw new Error("source.project is required for the Linear source (used as the team key)");
|
|
32
|
+
}
|
|
33
|
+
const requester = createFetchRequester(ctx.linearApiKey, undefined, ctx.fetchImpl ?? fetch);
|
|
34
|
+
return new LinearSource(requester, {
|
|
35
|
+
teamKey: config.source.project,
|
|
36
|
+
eligible: config.source.eligible,
|
|
37
|
+
maxItems: config.guardrails.maxItemsPerRun,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** A gh runner that shells out to `gh` in the repo directory. */
|
|
41
|
+
export function repoGhRunner(repoPath) {
|
|
42
|
+
return (args) => spawnCollect(["gh", ...args], { cwd: repoPath });
|
|
43
|
+
}
|
|
44
|
+
/** Assemble the loop dependencies for `ghostrail run`. */
|
|
45
|
+
export function buildLoopDeps(config, ctx) {
|
|
46
|
+
const backend = buildBackend(config);
|
|
47
|
+
const host = new GhHost(ctx.ghRunner ?? repoGhRunner(ctx.repoPath));
|
|
48
|
+
const publisher = new GitHubPublisher(backend, host, {
|
|
49
|
+
baseBranch: config.factory.baseBranch,
|
|
50
|
+
draft: config.output.draft,
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
repoPath: ctx.repoPath,
|
|
54
|
+
source: buildSource(config, ctx),
|
|
55
|
+
backend,
|
|
56
|
+
agent: new ClaudeCodeAgent(),
|
|
57
|
+
gate: new CommandGate(),
|
|
58
|
+
publisher,
|
|
59
|
+
lock: ctx.lock,
|
|
60
|
+
events: new FileStore(join(ctx.repoPath, STORE_DIR)),
|
|
61
|
+
...(ctx.globalStore === undefined ? {} : { globalStore: ctx.globalStore }),
|
|
62
|
+
...(ctx.logger === undefined ? {} : { logger: ctx.logger }),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Assemble the respond dependencies + config for `ghostrail respond`. */
|
|
66
|
+
export function buildRespondDeps(config, ctx) {
|
|
67
|
+
const backend = buildBackend(config);
|
|
68
|
+
const host = new GhHost(ctx.ghRunner ?? repoGhRunner(ctx.repoPath));
|
|
69
|
+
const deps = {
|
|
70
|
+
repoPath: ctx.repoPath,
|
|
71
|
+
host,
|
|
72
|
+
backend,
|
|
73
|
+
agent: new ClaudeCodeAgent(),
|
|
74
|
+
watermarks: new FileWatermarkStore(join(ctx.repoPath, ".factory", "watermarks.json")),
|
|
75
|
+
...(ctx.logger === undefined ? {} : { logger: ctx.logger }),
|
|
76
|
+
};
|
|
77
|
+
const respondConfig = {
|
|
78
|
+
label: "ghostrail",
|
|
79
|
+
selfLogins: ctx.selfLogins ?? [],
|
|
80
|
+
agent: config.agent,
|
|
81
|
+
guardrails: config.guardrails,
|
|
82
|
+
};
|
|
83
|
+
return { deps, config: respondConfig };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/factory/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Gate, GateContext, GateResult } from "../loop/ports.js";
|
|
2
|
+
/**
|
|
3
|
+
* The oracle: runs the configured gate commands in order inside the workspace,
|
|
4
|
+
* through the backend, so it behaves identically on every backend. Each command
|
|
5
|
+
* is run via `sh -c`, capturing its exit code and duration. It fails fast: the
|
|
6
|
+
* first non-zero command stops the run and the gate does not pass. An empty
|
|
7
|
+
* command list passes vacuously.
|
|
8
|
+
*/
|
|
9
|
+
export declare class CommandGate implements Gate {
|
|
10
|
+
run(context: GateContext): Promise<GateResult>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/gate/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAkB,MAAM,kBAAkB,CAAC;AAEtF;;;;;;GAMG;AACH,qBAAa,WAAY,YAAW,IAAI;IAChC,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;CAkBrD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The oracle: runs the configured gate commands in order inside the workspace,
|
|
3
|
+
* through the backend, so it behaves identically on every backend. Each command
|
|
4
|
+
* is run via `sh -c`, capturing its exit code and duration. It fails fast: the
|
|
5
|
+
* first non-zero command stops the run and the gate does not pass. An empty
|
|
6
|
+
* command list passes vacuously.
|
|
7
|
+
*/
|
|
8
|
+
export class CommandGate {
|
|
9
|
+
async run(context) {
|
|
10
|
+
const steps = [];
|
|
11
|
+
const execOptions = context.timeoutMs === undefined ? {} : { timeoutMs: context.timeoutMs };
|
|
12
|
+
for (const command of context.commands) {
|
|
13
|
+
const result = await context.backend.exec(context.workspace, ["sh", "-c", command], execOptions);
|
|
14
|
+
steps.push({ name: command, exitCode: result.exitCode, durationMs: result.durationMs });
|
|
15
|
+
if (result.exitCode !== 0) {
|
|
16
|
+
return { passed: false, steps };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { passed: true, steps };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/gate/gate.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,OAAO,WAAW;IACtB,KAAK,CAAC,GAAG,CAAC,OAAoB;QAC5B,MAAM,KAAK,GAAqB,EAAE,CAAC;QACnC,MAAM,WAAW,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;QAE5F,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CACvC,OAAO,CAAC,SAAS,EACjB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EACrB,WAAW,CACZ,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;YACxF,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAClC,CAAC;QACH,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/global/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/global/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAkB,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Backend, Workspace } from "../backend/types.js";
|
|
2
|
+
import type { Logger } from "../loop/ports.js";
|
|
3
|
+
import { type GlobalStore } from "./store.js";
|
|
4
|
+
/** Where staged global artifacts land in a run's workspace. */
|
|
5
|
+
export declare const STAGED_ARTIFACTS_DIR = ".ghostrail/artifacts";
|
|
6
|
+
/**
|
|
7
|
+
* Copy the named global artifacts into the run's workspace under
|
|
8
|
+
* `.ghostrail/artifacts/<name>.md`, so the agent (which only reads files) can
|
|
9
|
+
* use shared references like writing guidelines. Missing artifacts are warned
|
|
10
|
+
* about and skipped. Returns the names actually staged.
|
|
11
|
+
*/
|
|
12
|
+
export declare function stageArtifacts(backend: Backend, workspace: Workspace, store: GlobalStore, names: readonly string[], logger?: Logger): Promise<string[]>;
|
|
13
|
+
//# sourceMappingURL=stage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../src/global/stage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,KAAK,WAAW,EAAoB,MAAM,YAAY,CAAC;AAEhE,+DAA+D;AAC/D,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,EAAE,CAAC,CAiBnB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { writeWorkspaceFile } from "../backend/fs.js";
|
|
2
|
+
import { artifactFileName } from "./store.js";
|
|
3
|
+
/** Where staged global artifacts land in a run's workspace. */
|
|
4
|
+
export const STAGED_ARTIFACTS_DIR = ".ghostrail/artifacts";
|
|
5
|
+
/**
|
|
6
|
+
* Copy the named global artifacts into the run's workspace under
|
|
7
|
+
* `.ghostrail/artifacts/<name>.md`, so the agent (which only reads files) can
|
|
8
|
+
* use shared references like writing guidelines. Missing artifacts are warned
|
|
9
|
+
* about and skipped. Returns the names actually staged.
|
|
10
|
+
*/
|
|
11
|
+
export async function stageArtifacts(backend, workspace, store, names, logger) {
|
|
12
|
+
const staged = [];
|
|
13
|
+
for (const name of names) {
|
|
14
|
+
const content = await store.readArtifact(name);
|
|
15
|
+
if (content === undefined) {
|
|
16
|
+
logger?.warn(`artifact not found in global store: ${name}`);
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
await writeWorkspaceFile(backend, workspace, `${STAGED_ARTIFACTS_DIR}/${artifactFileName(name)}`, content);
|
|
20
|
+
staged.push(name);
|
|
21
|
+
}
|
|
22
|
+
return staged;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage.js","sourceRoot":"","sources":["../../src/global/stage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,EAAoB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEhE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAgB,EAChB,SAAoB,EACpB,KAAkB,EAClB,KAAwB,EACxB,MAAe;IAEf,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,EAAE,IAAI,CAAC,uCAAuC,IAAI,EAAE,CAAC,CAAC;YAC5D,SAAS;QACX,CAAC;QACD,MAAM,kBAAkB,CACtB,OAAO,EACP,SAAS,EACT,GAAG,oBAAoB,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,EACnD,OAAO,CACR,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface GlobalEnv {
|
|
2
|
+
GHOSTRAIL_HOME?: string;
|
|
3
|
+
XDG_CONFIG_HOME?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the global ghostrail config directory that holds account-level
|
|
7
|
+
* settings and shared artifacts across all factory instances:
|
|
8
|
+
* `$GHOSTRAIL_HOME`, else `$XDG_CONFIG_HOME/ghostrail`, else
|
|
9
|
+
* `<homedir>/.config/ghostrail`. Pure given the environment and home directory.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveGlobalDir(env: GlobalEnv, homedir: string): string;
|
|
12
|
+
/** Normalize an artifact name to its `.md` file name. */
|
|
13
|
+
export declare function artifactFileName(name: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* The global store: account settings (`settings.toml`) and shared artifacts
|
|
16
|
+
* (`artifacts/<name>.md`) reusable by every local factory instance. An artifact
|
|
17
|
+
* is looked up by name with or without the `.md` suffix.
|
|
18
|
+
*/
|
|
19
|
+
export declare class GlobalStore {
|
|
20
|
+
private readonly dir;
|
|
21
|
+
constructor(dir: string);
|
|
22
|
+
settingsPath(): string;
|
|
23
|
+
/** The `settings.toml` contents, or undefined if there is none. */
|
|
24
|
+
readSettings(): Promise<string | undefined>;
|
|
25
|
+
/** Artifact base names present in the store, sorted. */
|
|
26
|
+
listArtifacts(): Promise<string[]>;
|
|
27
|
+
/** An artifact's contents by name (with or without `.md`), or undefined. */
|
|
28
|
+
readArtifact(name: string): Promise<string | undefined>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/global/store.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAOxE;AAED,yDAAyD;AACzD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,GAAG,EAAE,MAAM;IAIvB,YAAY,IAAI,MAAM;IAItB,mEAAmE;IAC7D,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAQjD,wDAAwD;IAClD,aAAa,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAQxC,4EAA4E;IACtE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAU9D"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the global ghostrail config directory that holds account-level
|
|
5
|
+
* settings and shared artifacts across all factory instances:
|
|
6
|
+
* `$GHOSTRAIL_HOME`, else `$XDG_CONFIG_HOME/ghostrail`, else
|
|
7
|
+
* `<homedir>/.config/ghostrail`. Pure given the environment and home directory.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveGlobalDir(env, homedir) {
|
|
10
|
+
if (env.GHOSTRAIL_HOME !== undefined && env.GHOSTRAIL_HOME.length > 0)
|
|
11
|
+
return env.GHOSTRAIL_HOME;
|
|
12
|
+
const base = env.XDG_CONFIG_HOME !== undefined && env.XDG_CONFIG_HOME.length > 0
|
|
13
|
+
? env.XDG_CONFIG_HOME
|
|
14
|
+
: join(homedir, ".config");
|
|
15
|
+
return join(base, "ghostrail");
|
|
16
|
+
}
|
|
17
|
+
/** Normalize an artifact name to its `.md` file name. */
|
|
18
|
+
export function artifactFileName(name) {
|
|
19
|
+
return name.endsWith(".md") ? name : `${name}.md`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The global store: account settings (`settings.toml`) and shared artifacts
|
|
23
|
+
* (`artifacts/<name>.md`) reusable by every local factory instance. An artifact
|
|
24
|
+
* is looked up by name with or without the `.md` suffix.
|
|
25
|
+
*/
|
|
26
|
+
export class GlobalStore {
|
|
27
|
+
dir;
|
|
28
|
+
constructor(dir) {
|
|
29
|
+
this.dir = dir;
|
|
30
|
+
}
|
|
31
|
+
settingsPath() {
|
|
32
|
+
return join(this.dir, "settings.toml");
|
|
33
|
+
}
|
|
34
|
+
/** The `settings.toml` contents, or undefined if there is none. */
|
|
35
|
+
async readSettings() {
|
|
36
|
+
try {
|
|
37
|
+
return await readFile(this.settingsPath(), "utf8");
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Artifact base names present in the store, sorted. */
|
|
44
|
+
async listArtifacts() {
|
|
45
|
+
try {
|
|
46
|
+
return (await readdir(join(this.dir, "artifacts"))).sort();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** An artifact's contents by name (with or without `.md`), or undefined. */
|
|
53
|
+
async readArtifact(name) {
|
|
54
|
+
for (const candidate of new Set([name, artifactFileName(name)])) {
|
|
55
|
+
try {
|
|
56
|
+
return await readFile(join(this.dir, "artifacts", candidate), "utf8");
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// try the next candidate
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/global/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAOjC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAc,EAAE,OAAe;IAC9D,IAAI,GAAG,CAAC,cAAc,KAAK,SAAS,IAAI,GAAG,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,cAAc,CAAC;IACjG,MAAM,IAAI,GACR,GAAG,CAAC,eAAe,KAAK,SAAS,IAAI,GAAG,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC;QACjE,CAAC,CAAC,GAAG,CAAC,eAAe;QACrB,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC/B,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AACjC,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,WAAW;IACL,GAAG,CAAS;IAE7B,YAAY,GAAW;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IACzC,CAAC;IAED,mEAAmE;IACnE,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,MAAM,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,wDAAwD;IACxD,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,KAAK,CAAC,YAAY,CAAC,IAAY;QAC7B,KAAK,MAAM,SAAS,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,IAAI,CAAC;gBACH,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;YACxE,CAAC;YAAC,MAAM,CAAC;gBACP,yBAAyB;YAC3B,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Lock } from "../loop/ports.js";
|
|
2
|
+
/**
|
|
3
|
+
* A single-holder lock backed by an atomic `mkdir` (the `flock` analog), so only
|
|
4
|
+
* one tick runs at a time even across processes. A lock older than `staleMs`
|
|
5
|
+
* (e.g. from a crashed run) is considered abandoned and reclaimed once.
|
|
6
|
+
*/
|
|
7
|
+
export declare class FileLock implements Lock {
|
|
8
|
+
private readonly dir;
|
|
9
|
+
private readonly staleMs;
|
|
10
|
+
constructor(dir: string, staleMs?: number);
|
|
11
|
+
acquire(): Promise<boolean>;
|
|
12
|
+
release(): Promise<void>;
|
|
13
|
+
private tryMkdir;
|
|
14
|
+
private isStale;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=file-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../../src/lock/file-lock.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C;;;;GAIG;AACH,qBAAa,QAAS,YAAW,IAAI;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,GAAG,EAAE,MAAM,EAAE,OAAO,SAAgB;IAK1C,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAS3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAIhB,QAAQ;YAYR,OAAO;CAQtB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mkdir, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
/**
|
|
5
|
+
* A single-holder lock backed by an atomic `mkdir` (the `flock` analog), so only
|
|
6
|
+
* one tick runs at a time even across processes. A lock older than `staleMs`
|
|
7
|
+
* (e.g. from a crashed run) is considered abandoned and reclaimed once.
|
|
8
|
+
*/
|
|
9
|
+
export class FileLock {
|
|
10
|
+
dir;
|
|
11
|
+
staleMs;
|
|
12
|
+
constructor(dir, staleMs = 2 * 3_600_000) {
|
|
13
|
+
this.dir = dir;
|
|
14
|
+
this.staleMs = staleMs;
|
|
15
|
+
}
|
|
16
|
+
async acquire() {
|
|
17
|
+
if (await this.tryMkdir())
|
|
18
|
+
return true;
|
|
19
|
+
if (await this.isStale()) {
|
|
20
|
+
await rm(this.dir, { recursive: true, force: true });
|
|
21
|
+
return this.tryMkdir();
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
async release() {
|
|
26
|
+
await rm(this.dir, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
async tryMkdir() {
|
|
29
|
+
try {
|
|
30
|
+
await mkdir(dirname(this.dir), { recursive: true });
|
|
31
|
+
await mkdir(this.dir);
|
|
32
|
+
await writeFile(join(this.dir, "owner"), `${process.pid}\n`, "utf8");
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error.code === "EEXIST")
|
|
37
|
+
return false;
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async isStale() {
|
|
42
|
+
try {
|
|
43
|
+
const info = await stat(this.dir);
|
|
44
|
+
return Date.now() - info.mtimeMs > this.staleMs;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=file-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../../src/lock/file-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,OAAO,MAAM,cAAc,CAAC;AAGnC;;;;GAIG;AACH,MAAM,OAAO,QAAQ;IACF,GAAG,CAAS;IACZ,OAAO,CAAS;IAEjC,YAAY,GAAW,EAAE,OAAO,GAAG,CAAC,GAAG,SAAS;QAC9C,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,MAAM,IAAI,CAAC,QAAQ,EAAE;YAAE,OAAO,IAAI,CAAC;QACvC,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtB,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;YACrE,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,KAAK,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,OAAO;QACnB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a title into a short, git-safe slug: lowercase, non-alphanumeric runs
|
|
3
|
+
* collapsed to single hyphens, leading/trailing hyphens trimmed, and truncated
|
|
4
|
+
* to `maxLength` characters (trimming a trailing hyphen left by truncation).
|
|
5
|
+
* An empty or all-punctuation title yields "".
|
|
6
|
+
*/
|
|
7
|
+
export declare function slugify(title: string, maxLength?: number): string;
|
|
8
|
+
/**
|
|
9
|
+
* Build the working branch for an item: `<prefix>/<id>-<slug(title)>`, with the
|
|
10
|
+
* id lowercased. When the title slugs to nothing, the branch is
|
|
11
|
+
* `<prefix>/<id>` with no trailing hyphen.
|
|
12
|
+
*/
|
|
13
|
+
export declare function branchName(prefix: string, id: string, title: string): string;
|
|
14
|
+
//# sourceMappingURL=branch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch.d.ts","sourceRoot":"","sources":["../../src/loop/branch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,MAAM,CAM7D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI5E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a title into a short, git-safe slug: lowercase, non-alphanumeric runs
|
|
3
|
+
* collapsed to single hyphens, leading/trailing hyphens trimmed, and truncated
|
|
4
|
+
* to `maxLength` characters (trimming a trailing hyphen left by truncation).
|
|
5
|
+
* An empty or all-punctuation title yields "".
|
|
6
|
+
*/
|
|
7
|
+
export function slugify(title, maxLength = 40) {
|
|
8
|
+
const slug = title
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
11
|
+
.replace(/^-+|-+$/g, "");
|
|
12
|
+
return slug.slice(0, maxLength).replace(/-+$/g, "");
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build the working branch for an item: `<prefix>/<id>-<slug(title)>`, with the
|
|
16
|
+
* id lowercased. When the title slugs to nothing, the branch is
|
|
17
|
+
* `<prefix>/<id>` with no trailing hyphen.
|
|
18
|
+
*/
|
|
19
|
+
export function branchName(prefix, id, title) {
|
|
20
|
+
const base = `${prefix}/${id.toLowerCase()}`;
|
|
21
|
+
const slug = slugify(title);
|
|
22
|
+
return slug === "" ? base : `${base}-${slug}`;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=branch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch.js","sourceRoot":"","sources":["../../src/loop/branch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,SAAS,GAAG,EAAE;IACnD,MAAM,IAAI,GAAG,KAAK;SACf,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,EAAU,EAAE,KAAa;IAClE,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Backend } from "../backend/types.js";
|
|
2
|
+
import type { GhostrailConfig } from "../config/schema.js";
|
|
3
|
+
import type { GlobalStore } from "../global/store.js";
|
|
4
|
+
import type { EventSink } from "../store/events.js";
|
|
5
|
+
import type { Agent, Gate, Lock, Logger, Publisher, Source } from "./ports.js";
|
|
6
|
+
import type { TickResult } from "./record.js";
|
|
7
|
+
/** The adapters and services a tick runs against. */
|
|
8
|
+
export interface LoopDeps {
|
|
9
|
+
/** Absolute path to the source git repository the factory provisions from. */
|
|
10
|
+
repoPath: string;
|
|
11
|
+
source: Source;
|
|
12
|
+
backend: Backend;
|
|
13
|
+
agent: Agent;
|
|
14
|
+
gate: Gate;
|
|
15
|
+
publisher: Publisher;
|
|
16
|
+
lock: Lock;
|
|
17
|
+
logger?: Logger;
|
|
18
|
+
/** Optional run-event sink (the store). Omit to record nothing. */
|
|
19
|
+
events?: EventSink;
|
|
20
|
+
/** Optional global store for staging shared artifacts into the workspace. */
|
|
21
|
+
globalStore?: GlobalStore;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Run one factory tick: discover eligible work, and for each item (up to the
|
|
25
|
+
* `max_items_per_run` guardrail) claim it, provision an isolated workspace, run
|
|
26
|
+
* the agent, gate a successful change, and route the outcome to a PR (done),
|
|
27
|
+
* back to a human (blocked), or to a reset (failed/noop). The whole tick runs
|
|
28
|
+
* under a single global lock so only one tick executes at a time; if the lock
|
|
29
|
+
* is already held, nothing runs.
|
|
30
|
+
*
|
|
31
|
+
* The loop owns no tracker, git, or agent specifics: every side effect goes
|
|
32
|
+
* through the injected ports, and the agent and gate run their commands through
|
|
33
|
+
* the backend. Auto-merge is never performed here; a green run ends at an open
|
|
34
|
+
* pull request.
|
|
35
|
+
*/
|
|
36
|
+
export declare function runTick(deps: LoopDeps, config: GhostrailConfig): Promise<TickResult>;
|
|
37
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/loop/engine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EACV,KAAK,EAEL,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,SAAS,EACT,MAAM,EAEP,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAa,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzD,qDAAqD;AACrD,MAAM,WAAW,QAAQ;IACvB,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAwDD;;;;;;;;;;;;GAYG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAuB1F"}
|