@ipul-labs/forge 1.22.0 → 1.35.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/LICENSE +21 -0
- package/README.md +153 -78
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/index-CNvSOdpC.js +27 -0
- package/dist/assets/index-CSH1OQYQ.css +1 -0
- package/dist/boot-check.js +98 -0
- package/dist/favicon.svg +8 -0
- package/dist/icon-192.png +0 -0
- package/dist/icon-512.png +0 -0
- package/dist/icon.svg +12 -0
- package/dist/index.html +33 -0
- package/dist/manifest.webmanifest +18 -0
- package/dist/theme-init.js +11 -0
- package/lib/bin/cli/analyze.d.ts.map +1 -1
- package/lib/bin/cli/analyze.js +3 -44
- package/lib/bin/cli/analyze.js.map +1 -1
- package/lib/bin/cli/apply.d.ts.map +1 -1
- package/lib/bin/cli/apply.js +2 -20
- package/lib/bin/cli/apply.js.map +1 -1
- package/lib/bin/cli/cifix.d.ts.map +1 -1
- package/lib/bin/cli/cifix.js +4 -28
- package/lib/bin/cli/cifix.js.map +1 -1
- package/lib/bin/cli/close.d.ts.map +1 -1
- package/lib/bin/cli/close.js +2 -33
- package/lib/bin/cli/close.js.map +1 -1
- package/lib/bin/cli/createIssue.d.ts.map +1 -1
- package/lib/bin/cli/createIssue.js +3 -12
- package/lib/bin/cli/createIssue.js.map +1 -1
- package/lib/bin/cli/doctor.d.ts.map +1 -1
- package/lib/bin/cli/doctor.js +3 -4
- package/lib/bin/cli/doctor.js.map +1 -1
- package/lib/bin/cli/init.js +1 -10
- package/lib/bin/cli/init.js.map +1 -1
- package/lib/bin/cli/issue.d.ts.map +1 -1
- package/lib/bin/cli/issue.js +3 -12
- package/lib/bin/cli/issue.js.map +1 -1
- package/lib/bin/cli/issues.d.ts.map +1 -1
- package/lib/bin/cli/issues.js +3 -12
- package/lib/bin/cli/issues.js.map +1 -1
- package/lib/bin/cli/merge.d.ts.map +1 -1
- package/lib/bin/cli/merge.js +4 -20
- package/lib/bin/cli/merge.js.map +1 -1
- package/lib/bin/cli/refine.d.ts.map +1 -1
- package/lib/bin/cli/refine.js +2 -26
- package/lib/bin/cli/refine.js.map +1 -1
- package/lib/bin/cli/repl.d.ts.map +1 -1
- package/lib/bin/cli/repl.js +4 -13
- package/lib/bin/cli/repl.js.map +1 -1
- package/lib/bin/cli/repos.d.ts.map +1 -1
- package/lib/bin/cli/repos.js +1 -11
- package/lib/bin/cli/repos.js.map +1 -1
- package/lib/bin/cli/resetPassword.d.ts +21 -0
- package/lib/bin/cli/resetPassword.d.ts.map +1 -0
- package/lib/bin/cli/resetPassword.js +69 -0
- package/lib/bin/cli/resetPassword.js.map +1 -0
- package/lib/bin/cli/resolution.d.ts.map +1 -1
- package/lib/bin/cli/resolution.js +2 -19
- package/lib/bin/cli/resolution.js.map +1 -1
- package/lib/bin/cli/serverClient.d.ts +2 -0
- package/lib/bin/cli/serverClient.d.ts.map +1 -1
- package/lib/bin/cli/serverClient.js +8 -4
- package/lib/bin/cli/serverClient.js.map +1 -1
- package/lib/bin/cli/session.d.ts.map +1 -1
- package/lib/bin/cli/session.js.map +1 -1
- package/lib/bin/cli/sessionStore.d.ts +1 -1
- package/lib/bin/cli/sessionStore.js +2 -2
- package/lib/bin/cli/sessionStore.js.map +1 -1
- package/lib/bin/cli/ship.d.ts.map +1 -1
- package/lib/bin/cli/ship.js +15 -23
- package/lib/bin/cli/ship.js.map +1 -1
- package/lib/bin/cli/update.d.ts.map +1 -1
- package/lib/bin/cli/update.js +34 -28
- package/lib/bin/cli/update.js.map +1 -1
- package/lib/bin/cli/updatePlan.d.ts +30 -0
- package/lib/bin/cli/updatePlan.d.ts.map +1 -0
- package/lib/bin/cli/updatePlan.js +50 -0
- package/lib/bin/cli/updatePlan.js.map +1 -0
- package/lib/bin/cli/utils.d.ts +3 -0
- package/lib/bin/cli/utils.d.ts.map +1 -1
- package/lib/bin/cli/utils.js +7 -23
- package/lib/bin/cli/utils.js.map +1 -1
- package/lib/bin/cli/validate.d.ts.map +1 -1
- package/lib/bin/cli/validate.js +3 -25
- package/lib/bin/cli/validate.js.map +1 -1
- package/lib/bin/cli/watch.d.ts.map +1 -1
- package/lib/bin/cli/watch.js +3 -12
- package/lib/bin/cli/watch.js.map +1 -1
- package/lib/bin/forge-agent.js +10 -22
- package/lib/bin/forge-agent.js.map +1 -1
- package/lib/server/app.d.ts +24 -0
- package/lib/server/app.d.ts.map +1 -0
- package/lib/server/app.js +58 -0
- package/lib/server/app.js.map +1 -0
- package/lib/server/forge/analyses/analysisStore.d.ts +53 -0
- package/lib/server/forge/analyses/analysisStore.d.ts.map +1 -0
- package/lib/server/forge/analyses/analysisStore.js +77 -0
- package/lib/server/forge/analyses/analysisStore.js.map +1 -0
- package/lib/server/forge/auth/apiTokens.d.ts +34 -0
- package/lib/server/forge/auth/apiTokens.d.ts.map +1 -0
- package/lib/server/forge/auth/apiTokens.js +44 -0
- package/lib/server/forge/auth/apiTokens.js.map +1 -0
- package/lib/server/forge/auth/auditLog.d.ts +36 -0
- package/lib/server/forge/auth/auditLog.d.ts.map +1 -0
- package/lib/server/forge/auth/auditLog.js +56 -0
- package/lib/server/forge/auth/auditLog.js.map +1 -0
- package/lib/server/forge/auth/authConfig.d.ts +13 -0
- package/lib/server/forge/auth/authConfig.d.ts.map +1 -0
- package/lib/server/forge/auth/authConfig.js +37 -0
- package/lib/server/forge/auth/authConfig.js.map +1 -0
- package/lib/server/forge/auth/createAuth.d.ts +130 -0
- package/lib/server/forge/auth/createAuth.d.ts.map +1 -0
- package/lib/server/forge/auth/createAuth.js +387 -0
- package/lib/server/forge/auth/createAuth.js.map +1 -0
- package/lib/server/forge/auth/middleware.d.ts +10 -0
- package/lib/server/forge/auth/middleware.d.ts.map +1 -0
- package/lib/server/forge/auth/middleware.js +22 -0
- package/lib/server/forge/auth/middleware.js.map +1 -0
- package/lib/server/forge/auth/oidc.d.ts +38 -0
- package/lib/server/forge/auth/oidc.d.ts.map +1 -0
- package/lib/server/forge/auth/oidc.js +63 -0
- package/lib/server/forge/auth/oidc.js.map +1 -0
- package/lib/server/forge/auth/passwords.d.ts +7 -0
- package/lib/server/forge/auth/passwords.d.ts.map +1 -0
- package/lib/server/forge/auth/passwords.js +28 -0
- package/lib/server/forge/auth/passwords.js.map +1 -0
- package/lib/server/forge/auth/resetPassword.d.ts +14 -0
- package/lib/server/forge/auth/resetPassword.d.ts.map +1 -0
- package/lib/server/forge/auth/resetPassword.js +29 -0
- package/lib/server/forge/auth/resetPassword.js.map +1 -0
- package/lib/server/forge/auth/roles.d.ts +7 -0
- package/lib/server/forge/auth/roles.d.ts.map +1 -0
- package/lib/server/forge/auth/roles.js +15 -0
- package/lib/server/forge/auth/roles.js.map +1 -0
- package/lib/server/forge/auth/sessions.d.ts +27 -0
- package/lib/server/forge/auth/sessions.d.ts.map +1 -0
- package/lib/server/forge/auth/sessions.js +37 -0
- package/lib/server/forge/auth/sessions.js.map +1 -0
- package/lib/server/forge/auth/testHarness.d.ts +104 -0
- package/lib/server/forge/auth/testHarness.d.ts.map +1 -0
- package/lib/server/forge/auth/testHarness.js +53 -0
- package/lib/server/forge/auth/testHarness.js.map +1 -0
- package/lib/server/forge/auth/tokenHash.d.ts +3 -0
- package/lib/server/forge/auth/tokenHash.d.ts.map +1 -0
- package/lib/server/forge/auth/tokenHash.js +4 -0
- package/lib/server/forge/auth/tokenHash.js.map +1 -0
- package/lib/server/forge/auth/users.d.ts +51 -0
- package/lib/server/forge/auth/users.d.ts.map +1 -0
- package/lib/server/forge/auth/users.js +118 -0
- package/lib/server/forge/auth/users.js.map +1 -0
- package/lib/server/forge/core/ForgeAgent.d.ts +43 -1
- package/lib/server/forge/core/ForgeAgent.d.ts.map +1 -1
- package/lib/server/forge/core/ForgeAgent.js +209 -61
- package/lib/server/forge/core/ForgeAgent.js.map +1 -1
- package/lib/server/forge/core/agentClient.d.ts +7 -23
- package/lib/server/forge/core/agentClient.d.ts.map +1 -1
- package/lib/server/forge/core/agentClient.js +10 -25
- package/lib/server/forge/core/agentClient.js.map +1 -1
- package/lib/server/forge/core/claudeClient.d.ts +24 -1
- package/lib/server/forge/core/claudeClient.d.ts.map +1 -1
- package/lib/server/forge/core/claudeClient.js +25 -7
- package/lib/server/forge/core/claudeClient.js.map +1 -1
- package/lib/server/forge/core/configStore.d.ts +11 -1
- package/lib/server/forge/core/configStore.d.ts.map +1 -1
- package/lib/server/forge/core/configStore.js +33 -2
- package/lib/server/forge/core/configStore.js.map +1 -1
- package/lib/server/forge/core/dependencyPaths.d.ts +19 -0
- package/lib/server/forge/core/dependencyPaths.d.ts.map +1 -0
- package/lib/server/forge/core/dependencyPaths.js +32 -0
- package/lib/server/forge/core/dependencyPaths.js.map +1 -0
- package/lib/server/forge/core/evidence.d.ts +43 -0
- package/lib/server/forge/core/evidence.d.ts.map +1 -0
- package/lib/server/forge/core/evidence.js +108 -0
- package/lib/server/forge/core/evidence.js.map +1 -0
- package/lib/server/forge/core/jobs.d.ts +2 -0
- package/lib/server/forge/core/jobs.d.ts.map +1 -1
- package/lib/server/forge/core/jobs.js +8 -0
- package/lib/server/forge/core/jobs.js.map +1 -1
- package/lib/server/forge/core/localRepo.d.ts +31 -10
- package/lib/server/forge/core/localRepo.d.ts.map +1 -1
- package/lib/server/forge/core/localRepo.js +48 -82
- package/lib/server/forge/core/localRepo.js.map +1 -1
- package/lib/server/forge/core/prompts.d.ts +3 -1
- package/lib/server/forge/core/prompts.d.ts.map +1 -1
- package/lib/server/forge/core/prompts.js +14 -5
- package/lib/server/forge/core/prompts.js.map +1 -1
- package/lib/server/forge/core/providers/azuredevops.d.ts +4 -2
- package/lib/server/forge/core/providers/azuredevops.d.ts.map +1 -1
- package/lib/server/forge/core/providers/azuredevops.js +12 -2
- package/lib/server/forge/core/providers/azuredevops.js.map +1 -1
- package/lib/server/forge/core/providers/base.d.ts +29 -4
- package/lib/server/forge/core/providers/base.d.ts.map +1 -1
- package/lib/server/forge/core/providers/base.js +21 -3
- package/lib/server/forge/core/providers/base.js.map +1 -1
- package/lib/server/forge/core/providers/github.d.ts +7 -7
- package/lib/server/forge/core/providers/github.d.ts.map +1 -1
- package/lib/server/forge/core/providers/github.js +13 -4
- package/lib/server/forge/core/providers/github.js.map +1 -1
- package/lib/server/forge/core/providers/gitlab.d.ts +20 -3
- package/lib/server/forge/core/providers/gitlab.d.ts.map +1 -1
- package/lib/server/forge/core/providers/gitlab.js +65 -5
- package/lib/server/forge/core/providers/gitlab.js.map +1 -1
- package/lib/server/forge/core/providers/types.d.ts +20 -0
- package/lib/server/forge/core/providers/types.d.ts.map +1 -1
- package/lib/server/forge/core/secretBox.d.ts +17 -0
- package/lib/server/forge/core/secretBox.d.ts.map +1 -0
- package/lib/server/forge/core/secretBox.js +74 -0
- package/lib/server/forge/core/secretBox.js.map +1 -0
- package/lib/server/forge/core/types.d.ts +7 -1
- package/lib/server/forge/core/types.d.ts.map +1 -1
- package/lib/server/forge/core/validator/clone.d.ts +5 -4
- package/lib/server/forge/core/validator/clone.d.ts.map +1 -1
- package/lib/server/forge/core/validator/clone.js +6 -86
- package/lib/server/forge/core/validator/clone.js.map +1 -1
- package/lib/server/forge/core/validator/cmd.d.ts.map +1 -1
- package/lib/server/forge/core/validator/cmd.js +4 -1
- package/lib/server/forge/core/validator/cmd.js.map +1 -1
- package/lib/server/forge/core/validator/projectType.d.ts.map +1 -1
- package/lib/server/forge/core/validator/projectType.js +2 -1
- package/lib/server/forge/core/validator/projectType.js.map +1 -1
- package/lib/server/forge/core/validator/runners/node.d.ts.map +1 -1
- package/lib/server/forge/core/validator/runners/node.js +6 -2
- package/lib/server/forge/core/validator/runners/node.js.map +1 -1
- package/lib/server/forge/core/validator.d.ts.map +1 -1
- package/lib/server/forge/core/validator.js +28 -22
- package/lib/server/forge/core/validator.js.map +1 -1
- package/lib/server/forge/core/workspaces.d.ts +70 -0
- package/lib/server/forge/core/workspaces.d.ts.map +1 -0
- package/lib/server/forge/core/workspaces.js +348 -0
- package/lib/server/forge/core/workspaces.js.map +1 -0
- package/lib/server/forge/db/database.d.ts +22 -0
- package/lib/server/forge/db/database.d.ts.map +1 -0
- package/lib/server/forge/db/database.js +71 -0
- package/lib/server/forge/db/database.js.map +1 -0
- package/lib/server/forge/db/migrations.d.ts +11 -0
- package/lib/server/forge/db/migrations.d.ts.map +1 -0
- package/lib/server/forge/db/migrations.js +87 -0
- package/lib/server/forge/db/migrations.js.map +1 -0
- package/lib/server/forge/routes.d.ts.map +1 -1
- package/lib/server/forge/routes.js +413 -163
- package/lib/server/forge/routes.js.map +1 -1
- package/lib/server/forge/sse.d.ts +10 -0
- package/lib/server/forge/sse.d.ts.map +1 -0
- package/lib/server/forge/sse.js +22 -0
- package/lib/server/forge/sse.js.map +1 -0
- package/lib/server/spa.d.ts +12 -0
- package/lib/server/spa.d.ts.map +1 -0
- package/lib/server/spa.js +23 -0
- package/lib/server/spa.js.map +1 -0
- package/lib/server.js +9 -24
- package/lib/server.js.map +1 -1
- package/lib/shared/events.d.ts +3 -0
- package/lib/shared/events.d.ts.map +1 -1
- package/lib/shared/events.js +3 -0
- package/lib/shared/events.js.map +1 -1
- package/lib/shared/packageRoot.d.ts +11 -0
- package/lib/shared/packageRoot.d.ts.map +1 -0
- package/lib/shared/packageRoot.js +23 -0
- package/lib/shared/packageRoot.js.map +1 -0
- package/lib/shared/project.d.ts +3 -0
- package/lib/shared/project.d.ts.map +1 -0
- package/lib/shared/project.js +3 -0
- package/lib/shared/project.js.map +1 -0
- package/package.json +23 -10
- package/lib/bin/cli/improve.d.ts +0 -2
- package/lib/bin/cli/improve.d.ts.map +0 -1
- package/lib/bin/cli/improve.js +0 -189
- package/lib/bin/cli/improve.js.map +0 -1
- package/lib/bin/cli/stats.d.ts +0 -2
- package/lib/bin/cli/stats.d.ts.map +0 -1
- package/lib/bin/cli/stats.js +0 -78
- package/lib/bin/cli/stats.js.map +0 -1
- package/lib/server/forge/core/k8s.d.ts +0 -18
- package/lib/server/forge/core/k8s.d.ts.map +0 -1
- package/lib/server/forge/core/k8s.js +0 -49
- package/lib/server/forge/core/k8s.js.map +0 -1
- package/lib/server/forge/core/openclawClient.d.ts +0 -61
- package/lib/server/forge/core/openclawClient.d.ts.map +0 -1
- package/lib/server/forge/core/openclawClient.js +0 -111
- package/lib/server/forge/core/openclawClient.js.map +0 -1
- package/lib/server/forge/core/quotaClient.d.ts +0 -48
- package/lib/server/forge/core/quotaClient.d.ts.map +0 -1
- package/lib/server/forge/core/quotaClient.js +0 -88
- package/lib/server/forge/core/quotaClient.js.map +0 -1
- package/lib/server/forge/core/selfImprove.d.ts +0 -43
- package/lib/server/forge/core/selfImprove.d.ts.map +0 -1
- package/lib/server/forge/core/selfImprove.js +0 -372
- package/lib/server/forge/core/selfImprove.js.map +0 -1
- package/lib/server/forge/core/telemetry.d.ts +0 -100
- package/lib/server/forge/core/telemetry.d.ts.map +0 -1
- package/lib/server/forge/core/telemetry.js +0 -273
- package/lib/server/forge/core/telemetry.js.map +0 -1
- package/scripts/fix-shebang.cjs +0 -11
- package/scripts/inject-readme.cjs +0 -23
- package/scripts/local-rollout.sh +0 -134
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server/forge/core/workspaces.ts
|
|
3
|
+
*
|
|
4
|
+
* Every local checkout Forge works in, and the only code that creates or
|
|
5
|
+
* deletes one. Layout under the cache root:
|
|
6
|
+
*
|
|
7
|
+
* repos/<repo>/ shallow base clone, one per repo
|
|
8
|
+
* worktrees/<repo>/issue-<iid>/ git worktree per issue, on its forge branch
|
|
9
|
+
*
|
|
10
|
+
* One worktree per issue means two jobs on different issues never share a
|
|
11
|
+
* tree. Git work on a repo (fetch, worktree add/remove) is serialised per repo,
|
|
12
|
+
* and a job holds a lease on its worktree so deletes and purges skip it.
|
|
13
|
+
*
|
|
14
|
+
* Anything else found under the root — clones from the old one-clone-per-repo
|
|
15
|
+
* layout, worktree folders git no longer knows about — is listed as `legacy` /
|
|
16
|
+
* `orphan` so the UI can clean it up.
|
|
17
|
+
*/
|
|
18
|
+
export type WorkspaceKind = "worktree" | "base" | "legacy" | "orphan";
|
|
19
|
+
export interface WorkspaceEntry {
|
|
20
|
+
/** Path relative to the cache root — the handle the API and UI use. */
|
|
21
|
+
id: string;
|
|
22
|
+
kind: WorkspaceKind;
|
|
23
|
+
/** Cache key of the repo (e.g. "gitlab_group/app"); null for legacy/orphan clutter it can't tell. */
|
|
24
|
+
repo: string | null;
|
|
25
|
+
branch: string | null;
|
|
26
|
+
issueIid: number | null;
|
|
27
|
+
path: string;
|
|
28
|
+
sizeBytes: number;
|
|
29
|
+
lastUsedAt: string | null;
|
|
30
|
+
inUse: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface WorkspaceLease {
|
|
33
|
+
dir: string;
|
|
34
|
+
/**
|
|
35
|
+
* Ends the job's hold on the worktree. Safe to call more than once. The
|
|
36
|
+
* returned promise settles once the last-used time is saved; callers may
|
|
37
|
+
* ignore it, but should await it before deleting the cache root.
|
|
38
|
+
*/
|
|
39
|
+
release: () => Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export declare class WorkspaceBusyError extends Error {
|
|
42
|
+
constructor(id: string);
|
|
43
|
+
}
|
|
44
|
+
interface GitEnv {
|
|
45
|
+
env: Record<string, string>;
|
|
46
|
+
cleanup: () => void;
|
|
47
|
+
}
|
|
48
|
+
interface Options {
|
|
49
|
+
root: string;
|
|
50
|
+
cloneUrl: (repo: string) => string;
|
|
51
|
+
/** Credentials for clone/fetch; called per network operation. */
|
|
52
|
+
gitEnv: () => GitEnv;
|
|
53
|
+
}
|
|
54
|
+
/** Cache key for a repo id — "gitlab:group/app" → "gitlab_group/app". */
|
|
55
|
+
export declare function repoKey(repo: string): string;
|
|
56
|
+
export declare function createWorkspaces({ root, cloneUrl, gitEnv }: Options): {
|
|
57
|
+
acquire: (repo: string, branchName: string, onLog?: (line: string) => void) => Promise<WorkspaceLease>;
|
|
58
|
+
list: () => Promise<WorkspaceEntry[]>;
|
|
59
|
+
remove: (id: string) => Promise<void>;
|
|
60
|
+
purge: ({ includeBases }?: {
|
|
61
|
+
includeBases?: boolean;
|
|
62
|
+
}) => Promise<{
|
|
63
|
+
removed: string[];
|
|
64
|
+
skipped: string[];
|
|
65
|
+
freedBytes: number;
|
|
66
|
+
}>;
|
|
67
|
+
};
|
|
68
|
+
export type Workspaces = ReturnType<typeof createWorkspaces>;
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=workspaces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../../../../server/forge/core/workspaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEtE,MAAM,WAAW,cAAc;IAC9B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,aAAa,CAAC;IACpB,qGAAqG;IACrG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAChC,EAAE,EAAE,MAAM;CAItB;AAED,UAAU,MAAM;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,UAAU,OAAO;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,iEAAiE;IACjE,MAAM,EAAE,MAAM,MAAM,CAAC;CACrB;AAsDD,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,UAEnC;AAQD,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO;oBAqEtC,MAAM,cAAc,MAAM,UAAU,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAG,OAAO,CAAC,cAAc,CAAC;gBA2D1F,OAAO,CAAC,cAAc,EAAE,CAAC;iBA0FtB,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;+BAMD;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE;;;;;EA6BzE;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* server/forge/core/workspaces.ts
|
|
3
|
+
*
|
|
4
|
+
* Every local checkout Forge works in, and the only code that creates or
|
|
5
|
+
* deletes one. Layout under the cache root:
|
|
6
|
+
*
|
|
7
|
+
* repos/<repo>/ shallow base clone, one per repo
|
|
8
|
+
* worktrees/<repo>/issue-<iid>/ git worktree per issue, on its forge branch
|
|
9
|
+
*
|
|
10
|
+
* One worktree per issue means two jobs on different issues never share a
|
|
11
|
+
* tree. Git work on a repo (fetch, worktree add/remove) is serialised per repo,
|
|
12
|
+
* and a job holds a lease on its worktree so deletes and purges skip it.
|
|
13
|
+
*
|
|
14
|
+
* Anything else found under the root — clones from the old one-clone-per-repo
|
|
15
|
+
* layout, worktree folders git no longer knows about — is listed as `legacy` /
|
|
16
|
+
* `orphan` so the UI can clean it up.
|
|
17
|
+
*/
|
|
18
|
+
import { spawn } from "node:child_process";
|
|
19
|
+
import fs from "node:fs/promises";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
export class WorkspaceBusyError extends Error {
|
|
22
|
+
constructor(id) {
|
|
23
|
+
super(`Workspace ${id} is in use by a running job.`);
|
|
24
|
+
this.name = "WorkspaceBusyError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const REPOS_DIR = "repos";
|
|
28
|
+
const WORKTREES_DIR = "worktrees";
|
|
29
|
+
const META_FILE = "workspaces.json";
|
|
30
|
+
const ISSUE_DIR_RE = /^issue-(\d+)$/;
|
|
31
|
+
function run(args, cwd, opts = {}) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
const proc = spawn("git", args, {
|
|
34
|
+
cwd,
|
|
35
|
+
env: { ...process.env, ...opts.env, FORCE_COLOR: "0", NO_COLOR: "1", TERM: "dumb" },
|
|
36
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
37
|
+
});
|
|
38
|
+
let stdout = "";
|
|
39
|
+
const log = (buf) => {
|
|
40
|
+
for (const line of buf.toString().split("\n"))
|
|
41
|
+
if (line.trim())
|
|
42
|
+
opts.onLog?.(line.replace(/\r$/, ""));
|
|
43
|
+
};
|
|
44
|
+
proc.stdout.on("data", (d) => { stdout += d.toString(); log(d); });
|
|
45
|
+
proc.stderr.on("data", log);
|
|
46
|
+
proc.on("error", () => resolve({ code: -1, stdout }));
|
|
47
|
+
proc.on("close", (code) => resolve({ code: code ?? -1, stdout }));
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function mustRun(args, cwd, opts = {}) {
|
|
51
|
+
const res = await run(args, cwd, opts);
|
|
52
|
+
if (res.code !== 0)
|
|
53
|
+
throw new Error(`git ${args[0]} failed (code ${res.code})`);
|
|
54
|
+
return res.stdout.trim();
|
|
55
|
+
}
|
|
56
|
+
async function exists(p) {
|
|
57
|
+
try {
|
|
58
|
+
await fs.access(p);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function isDir(p) {
|
|
66
|
+
try {
|
|
67
|
+
return (await fs.stat(p)).isDirectory();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function subdirs(p) {
|
|
74
|
+
const entries = await fs.readdir(p, { withFileTypes: true }).catch(() => []);
|
|
75
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
76
|
+
}
|
|
77
|
+
function sizeOf(p) {
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
const proc = spawn("du", ["-sk", p], { stdio: ["ignore", "pipe", "ignore"] });
|
|
80
|
+
let out = "";
|
|
81
|
+
proc.stdout.on("data", (d) => { out += d.toString(); });
|
|
82
|
+
proc.on("error", () => resolve(0));
|
|
83
|
+
proc.on("close", () => resolve((parseInt(out, 10) || 0) * 1024));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/** Cache key for a repo id — "gitlab:group/app" → "gitlab_group/app". */
|
|
87
|
+
export function repoKey(repo) {
|
|
88
|
+
return repo.replace(/[^a-zA-Z0-9_./-]/g, "_");
|
|
89
|
+
}
|
|
90
|
+
/** Worktree folder for a branch: one per issue, so a renamed issue keeps its tree. */
|
|
91
|
+
function worktreeName(branchName) {
|
|
92
|
+
const m = branchName.match(/^forge\/issue-(\d+)/);
|
|
93
|
+
return m ? `issue-${m[1]}` : branchName.replace(/[^a-zA-Z0-9_.-]/g, "-");
|
|
94
|
+
}
|
|
95
|
+
export function createWorkspaces({ root, cloneUrl, gitEnv }) {
|
|
96
|
+
const locks = new Map();
|
|
97
|
+
const holds = new Map();
|
|
98
|
+
let meta = null;
|
|
99
|
+
const basePath = (key) => path.join(root, REPOS_DIR, key);
|
|
100
|
+
const idOf = (p) => path.relative(root, p).split(path.sep).join("/");
|
|
101
|
+
/** Runs `fn` after any earlier git work on the same repo has finished. */
|
|
102
|
+
function withRepoLock(key, fn) {
|
|
103
|
+
const prev = locks.get(key) ?? Promise.resolve();
|
|
104
|
+
const next = prev.catch(() => { }).then(fn);
|
|
105
|
+
locks.set(key, next);
|
|
106
|
+
return next;
|
|
107
|
+
}
|
|
108
|
+
async function loadMeta() {
|
|
109
|
+
if (!meta) {
|
|
110
|
+
meta = JSON.parse(await fs.readFile(path.join(root, META_FILE), "utf8").catch(() => "{}"));
|
|
111
|
+
}
|
|
112
|
+
return meta;
|
|
113
|
+
}
|
|
114
|
+
async function saveMeta() {
|
|
115
|
+
await fs.mkdir(root, { recursive: true });
|
|
116
|
+
await fs.writeFile(path.join(root, META_FILE), JSON.stringify(meta ?? {}, null, "\t"));
|
|
117
|
+
}
|
|
118
|
+
async function touch(id) {
|
|
119
|
+
(await loadMeta())[id] = new Date().toISOString();
|
|
120
|
+
await saveMeta();
|
|
121
|
+
}
|
|
122
|
+
async function forget(prefix) {
|
|
123
|
+
const m = await loadMeta();
|
|
124
|
+
for (const k of Object.keys(m))
|
|
125
|
+
if (k === prefix || k.startsWith(`${prefix}/`))
|
|
126
|
+
delete m[k];
|
|
127
|
+
await saveMeta();
|
|
128
|
+
}
|
|
129
|
+
/** Removes now-empty folders between `p` and the root. */
|
|
130
|
+
async function pruneEmptyParents(p) {
|
|
131
|
+
for (let dir = path.dirname(p); dir.startsWith(root + path.sep); dir = path.dirname(dir)) {
|
|
132
|
+
if ((await fs.readdir(dir).catch(() => ["?"])).length)
|
|
133
|
+
return;
|
|
134
|
+
await fs.rmdir(dir).catch(() => { });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function rmTree(p) {
|
|
138
|
+
await fs.rm(p, { recursive: true, force: true });
|
|
139
|
+
await pruneEmptyParents(p);
|
|
140
|
+
}
|
|
141
|
+
async function ensureBase(repo, key, onLog) {
|
|
142
|
+
const base = basePath(key);
|
|
143
|
+
const { env, cleanup } = gitEnv();
|
|
144
|
+
try {
|
|
145
|
+
if (!(await exists(path.join(base, ".git")))) {
|
|
146
|
+
onLog?.(`Cloning ${repo} → ${base}`);
|
|
147
|
+
await fs.mkdir(path.dirname(base), { recursive: true });
|
|
148
|
+
await mustRun(["clone", "--depth=1", cloneUrl(repo), base], root, { env, onLog });
|
|
149
|
+
}
|
|
150
|
+
await mustRun(["fetch", "--depth=1", "origin", "HEAD"], base, { env, onLog });
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
cleanup();
|
|
154
|
+
}
|
|
155
|
+
await run(["worktree", "prune"], base);
|
|
156
|
+
return mustRun(["rev-parse", "FETCH_HEAD"], base);
|
|
157
|
+
}
|
|
158
|
+
async function acquire(repo, branchName, onLog) {
|
|
159
|
+
const key = repoKey(repo);
|
|
160
|
+
const dir = path.join(root, WORKTREES_DIR, key, worktreeName(branchName));
|
|
161
|
+
await withRepoLock(key, async () => {
|
|
162
|
+
const sha = await ensureBase(repo, key, onLog);
|
|
163
|
+
const base = basePath(key);
|
|
164
|
+
if (await exists(path.join(dir, ".git"))) {
|
|
165
|
+
onLog?.(`Reusing worktree at ${dir}`);
|
|
166
|
+
// Wipe any edits a previous run left behind. `clean -fd` keeps
|
|
167
|
+
// ignored files, so node_modules survives between runs.
|
|
168
|
+
await mustRun(["reset", "--hard", "-q"], dir);
|
|
169
|
+
await mustRun(["clean", "-fdq"], dir);
|
|
170
|
+
await mustRun(["checkout", "-q", "-B", branchName, sha], dir, { onLog });
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
174
|
+
await fs.mkdir(path.dirname(dir), { recursive: true });
|
|
175
|
+
onLog?.(`Creating worktree ${dir} on ${branchName}`);
|
|
176
|
+
await mustRun(["worktree", "add", "-q", "-B", branchName, dir, sha], base, { onLog });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
holds.set(dir, (holds.get(dir) ?? 0) + 1);
|
|
180
|
+
await touch(idOf(dir));
|
|
181
|
+
let released = false;
|
|
182
|
+
return {
|
|
183
|
+
dir,
|
|
184
|
+
release: () => {
|
|
185
|
+
if (released)
|
|
186
|
+
return Promise.resolve();
|
|
187
|
+
released = true;
|
|
188
|
+
const n = (holds.get(dir) ?? 1) - 1;
|
|
189
|
+
if (n > 0)
|
|
190
|
+
holds.set(dir, n);
|
|
191
|
+
else
|
|
192
|
+
holds.delete(dir);
|
|
193
|
+
return touch(idOf(dir)).catch(() => { });
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async function findGitDirs(dir, depth, skip) {
|
|
198
|
+
if (skip.has(dir))
|
|
199
|
+
return [];
|
|
200
|
+
if (await exists(path.join(dir, ".git")))
|
|
201
|
+
return [dir];
|
|
202
|
+
if (depth === 0)
|
|
203
|
+
return [];
|
|
204
|
+
const out = [];
|
|
205
|
+
for (const name of await subdirs(dir))
|
|
206
|
+
out.push(...(await findGitDirs(path.join(dir, name), depth - 1, skip)));
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
async function entry(p, fields) {
|
|
210
|
+
const id = idOf(p);
|
|
211
|
+
return {
|
|
212
|
+
id,
|
|
213
|
+
path: p,
|
|
214
|
+
...fields,
|
|
215
|
+
sizeBytes: await sizeOf(p),
|
|
216
|
+
lastUsedAt: (await loadMeta())[id] ?? null,
|
|
217
|
+
inUse: (holds.get(p) ?? 0) > 0,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
async function list() {
|
|
221
|
+
const out = [];
|
|
222
|
+
const registered = new Set();
|
|
223
|
+
const reposRoot = path.join(root, REPOS_DIR);
|
|
224
|
+
for (const base of await findGitDirs(reposRoot, 4, new Set())) {
|
|
225
|
+
const key = idOf(base).slice(REPOS_DIR.length + 1);
|
|
226
|
+
const porcelain = (await run(["worktree", "list", "--porcelain"], base)).stdout;
|
|
227
|
+
const worktrees = [];
|
|
228
|
+
for (const block of porcelain.split("\n\n")) {
|
|
229
|
+
const wtPath = block.match(/^worktree (.+)$/m)?.[1];
|
|
230
|
+
if (!wtPath || (await fs.realpath(wtPath).catch(() => wtPath)) === (await fs.realpath(base)))
|
|
231
|
+
continue;
|
|
232
|
+
if (!(await isDir(wtPath)))
|
|
233
|
+
continue;
|
|
234
|
+
const dir = path.join(root, WORKTREES_DIR, key, path.basename(wtPath));
|
|
235
|
+
registered.add(dir);
|
|
236
|
+
const branch = block.match(/^branch refs\/heads\/(.+)$/m)?.[1] ?? null;
|
|
237
|
+
const issue = path.basename(dir).match(ISSUE_DIR_RE);
|
|
238
|
+
worktrees.push(await entry(dir, { kind: "worktree", repo: key, branch, issueIid: issue ? Number(issue[1]) : null }));
|
|
239
|
+
}
|
|
240
|
+
const own = await entry(base, { kind: "base", repo: key, branch: null, issueIid: null });
|
|
241
|
+
const latest = [own.lastUsedAt, ...worktrees.map((w) => w.lastUsedAt)].filter(Boolean).sort().at(-1) ?? null;
|
|
242
|
+
out.push({ ...own, lastUsedAt: latest }, ...worktrees);
|
|
243
|
+
}
|
|
244
|
+
// Folders under worktrees/ that git doesn't know about.
|
|
245
|
+
async function orphans(dir, depth) {
|
|
246
|
+
for (const name of await subdirs(dir)) {
|
|
247
|
+
const p = path.join(dir, name);
|
|
248
|
+
if (registered.has(p))
|
|
249
|
+
continue;
|
|
250
|
+
if (ISSUE_DIR_RE.test(name) || (await exists(path.join(p, ".git")))) {
|
|
251
|
+
out.push(await entry(p, { kind: "orphan", repo: null, branch: null, issueIid: Number(name.match(ISSUE_DIR_RE)?.[1]) || null }));
|
|
252
|
+
}
|
|
253
|
+
else if (depth > 0) {
|
|
254
|
+
await orphans(p, depth - 1);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
await orphans(path.join(root, WORKTREES_DIR), 4);
|
|
259
|
+
// Clones from before workspaces existed (~/.forge/cache/<repo>/).
|
|
260
|
+
const skip = new Set([reposRoot, path.join(root, WORKTREES_DIR)]);
|
|
261
|
+
for (const legacy of await findGitDirs(root, 3, skip)) {
|
|
262
|
+
if (legacy === root)
|
|
263
|
+
continue;
|
|
264
|
+
out.push(await entry(legacy, { kind: "legacy", repo: null, branch: null, issueIid: null }));
|
|
265
|
+
}
|
|
266
|
+
return out.sort((a, b) => a.id.localeCompare(b.id));
|
|
267
|
+
}
|
|
268
|
+
async function find(id) {
|
|
269
|
+
const resolved = path.resolve(root, id);
|
|
270
|
+
const hit = resolved.startsWith(root + path.sep) ? (await list()).find((e) => e.path === resolved) : undefined;
|
|
271
|
+
if (!hit)
|
|
272
|
+
throw new Error(`Unknown workspace: ${id}`);
|
|
273
|
+
return hit;
|
|
274
|
+
}
|
|
275
|
+
async function removeWorktree(e) {
|
|
276
|
+
const base = basePath(e.repo);
|
|
277
|
+
await withRepoLock(e.repo, async () => {
|
|
278
|
+
if ((await run(["worktree", "remove", "--force", e.path], base)).code !== 0) {
|
|
279
|
+
await fs.rm(e.path, { recursive: true, force: true });
|
|
280
|
+
await run(["worktree", "prune"], base);
|
|
281
|
+
}
|
|
282
|
+
if (e.branch)
|
|
283
|
+
await run(["branch", "-D", e.branch], base);
|
|
284
|
+
});
|
|
285
|
+
await pruneEmptyParents(e.path);
|
|
286
|
+
await forget(e.id);
|
|
287
|
+
}
|
|
288
|
+
async function removeEntry(e, all) {
|
|
289
|
+
if (e.kind === "worktree") {
|
|
290
|
+
if (e.inUse)
|
|
291
|
+
throw new WorkspaceBusyError(e.id);
|
|
292
|
+
await removeWorktree(e);
|
|
293
|
+
}
|
|
294
|
+
else if (e.kind === "base") {
|
|
295
|
+
const children = all.filter((w) => w.kind === "worktree" && w.repo === e.repo);
|
|
296
|
+
const busy = children.find((w) => w.inUse);
|
|
297
|
+
if (busy)
|
|
298
|
+
throw new WorkspaceBusyError(busy.id);
|
|
299
|
+
await withRepoLock(e.repo, async () => {
|
|
300
|
+
for (const w of children)
|
|
301
|
+
await fs.rm(w.path, { recursive: true, force: true });
|
|
302
|
+
await rmTree(e.path);
|
|
303
|
+
await rmTree(path.join(root, WORKTREES_DIR, e.repo));
|
|
304
|
+
});
|
|
305
|
+
await forget(e.id);
|
|
306
|
+
await forget(`${WORKTREES_DIR}/${e.repo}`);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
if (e.inUse)
|
|
310
|
+
throw new WorkspaceBusyError(e.id);
|
|
311
|
+
await rmTree(e.path);
|
|
312
|
+
await forget(e.id);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function remove(id) {
|
|
316
|
+
const e = await find(id);
|
|
317
|
+
await removeEntry(e, await list());
|
|
318
|
+
}
|
|
319
|
+
/** Removes every idle worktree and stray folder; with `includeBases`, base clones left without worktrees too. */
|
|
320
|
+
async function purge({ includeBases = false } = {}) {
|
|
321
|
+
const removed = [];
|
|
322
|
+
const skipped = [];
|
|
323
|
+
let freedBytes = 0;
|
|
324
|
+
const all = await list();
|
|
325
|
+
for (const e of all.filter((x) => x.kind !== "base")) {
|
|
326
|
+
if (e.inUse) {
|
|
327
|
+
skipped.push(e.id);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
await removeEntry(e, all);
|
|
331
|
+
removed.push(e.id);
|
|
332
|
+
freedBytes += e.sizeBytes;
|
|
333
|
+
}
|
|
334
|
+
if (includeBases) {
|
|
335
|
+
const now = await list();
|
|
336
|
+
for (const base of now.filter((x) => x.kind === "base")) {
|
|
337
|
+
if (now.some((w) => w.kind === "worktree" && w.repo === base.repo))
|
|
338
|
+
continue;
|
|
339
|
+
await removeEntry(base, now);
|
|
340
|
+
removed.push(base.id);
|
|
341
|
+
freedBytes += base.sizeBytes;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return { removed, skipped, freedBytes };
|
|
345
|
+
}
|
|
346
|
+
return { acquire, list, remove, purge };
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=workspaces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../../../server/forge/core/workspaces.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SQLInputValue } from "node:sqlite";
|
|
2
|
+
export type SqlParams = SQLInputValue[];
|
|
3
|
+
/**
|
|
4
|
+
* Async database interface. Implemented on node:sqlite today; the async shape
|
|
5
|
+
* is deliberate so a Postgres adapter can slot in for multi-replica installs.
|
|
6
|
+
* SQL uses `?` placeholders.
|
|
7
|
+
*/
|
|
8
|
+
export interface Db {
|
|
9
|
+
run(sql: string, params?: SqlParams): Promise<{
|
|
10
|
+
changes: number;
|
|
11
|
+
}>;
|
|
12
|
+
get<T>(sql: string, params?: SqlParams): Promise<T | undefined>;
|
|
13
|
+
all<T>(sql: string, params?: SqlParams): Promise<T[]>;
|
|
14
|
+
exec(sql: string): Promise<void>;
|
|
15
|
+
transaction<T>(fn: () => Promise<T>): Promise<T>;
|
|
16
|
+
close(): void;
|
|
17
|
+
}
|
|
18
|
+
export declare function defaultDbPath(): string;
|
|
19
|
+
export declare function openDatabase({ path: dbPath }: {
|
|
20
|
+
path: string;
|
|
21
|
+
}): Promise<Db>;
|
|
22
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../../server/forge/db/database.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAI/D,MAAM,MAAM,SAAS,GAAG,aAAa,EAAE,CAAC;AAExC;;;;GAIG;AACH,MAAM,WAAW,EAAE;IAClB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAChE,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACtD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,KAAK,IAAI,IAAI,CAAC;CACd;AAED,wBAAgB,aAAa,WAE5B;AAED,wBAAsB,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAuDlF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { chmodSync, closeSync, mkdirSync, openSync } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { DatabaseSync } from "node:sqlite";
|
|
6
|
+
import { MIGRATIONS } from "./migrations.js";
|
|
7
|
+
export function defaultDbPath() {
|
|
8
|
+
return process.env.FORGE_DB_PATH ?? path.join(os.homedir(), ".forge", "forge.db");
|
|
9
|
+
}
|
|
10
|
+
export async function openDatabase({ path: dbPath }) {
|
|
11
|
+
if (dbPath !== ":memory:") {
|
|
12
|
+
mkdirSync(path.dirname(dbPath), { recursive: true, mode: 0o700 });
|
|
13
|
+
// Holds password and session hashes: owner-only. SQLite gives the WAL
|
|
14
|
+
// and shared-memory files the same mode as the main file.
|
|
15
|
+
closeSync(openSync(dbPath, "a", 0o600));
|
|
16
|
+
chmodSync(dbPath, 0o600);
|
|
17
|
+
}
|
|
18
|
+
const sqlite = new DatabaseSync(dbPath);
|
|
19
|
+
sqlite.exec("PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 5000;");
|
|
20
|
+
// One connection: serialise transactions so statements from concurrent
|
|
21
|
+
// requests can't land inside another request's BEGIN … COMMIT. Statements
|
|
22
|
+
// issued from inside a transaction bypass the queue via AsyncLocalStorage.
|
|
23
|
+
const inTx = new AsyncLocalStorage();
|
|
24
|
+
let queue = Promise.resolve();
|
|
25
|
+
const exclusive = (fn) => {
|
|
26
|
+
if (inTx.getStore())
|
|
27
|
+
return fn();
|
|
28
|
+
const next = queue.then(fn, fn);
|
|
29
|
+
queue = next.catch(() => { });
|
|
30
|
+
return next;
|
|
31
|
+
};
|
|
32
|
+
const db = {
|
|
33
|
+
run: (sql, params = []) => exclusive(async () => {
|
|
34
|
+
const r = sqlite.prepare(sql).run(...params);
|
|
35
|
+
return { changes: Number(r.changes) };
|
|
36
|
+
}),
|
|
37
|
+
get: (sql, params = []) => exclusive(async () => sqlite.prepare(sql).get(...params)),
|
|
38
|
+
all: (sql, params = []) => exclusive(async () => sqlite.prepare(sql).all(...params)),
|
|
39
|
+
exec: (sql) => exclusive(async () => {
|
|
40
|
+
sqlite.exec(sql);
|
|
41
|
+
}),
|
|
42
|
+
transaction: (fn) => exclusive(() => inTx.run(true, async () => {
|
|
43
|
+
sqlite.exec("BEGIN");
|
|
44
|
+
try {
|
|
45
|
+
const result = await fn();
|
|
46
|
+
sqlite.exec("COMMIT");
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
sqlite.exec("ROLLBACK");
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
})),
|
|
54
|
+
close: () => sqlite.close(),
|
|
55
|
+
};
|
|
56
|
+
await migrate(db);
|
|
57
|
+
return db;
|
|
58
|
+
}
|
|
59
|
+
async function migrate(db) {
|
|
60
|
+
await db.exec("CREATE TABLE IF NOT EXISTS schema_migrations (version INTEGER PRIMARY KEY, name TEXT NOT NULL, applied_at INTEGER NOT NULL)");
|
|
61
|
+
const done = new Set((await db.all("SELECT version FROM schema_migrations")).map((r) => r.version));
|
|
62
|
+
for (const m of MIGRATIONS) {
|
|
63
|
+
if (done.has(m.version))
|
|
64
|
+
continue;
|
|
65
|
+
await db.transaction(async () => {
|
|
66
|
+
await db.exec(m.sql);
|
|
67
|
+
await db.run("INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)", [m.version, m.name, Date.now()]);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../../../server/forge/db/database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordered schema migrations. Append only — never edit a shipped entry.
|
|
3
|
+
* Keep SQL portable (TEXT/INTEGER, no SQLite-only functions) so the planned
|
|
4
|
+
* Postgres adapter can run the same list.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MIGRATIONS: {
|
|
7
|
+
version: number;
|
|
8
|
+
name: string;
|
|
9
|
+
sql: string;
|
|
10
|
+
}[];
|
|
11
|
+
//# sourceMappingURL=migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../../server/forge/db/migrations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAgFtE,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ordered schema migrations. Append only — never edit a shipped entry.
|
|
3
|
+
* Keep SQL portable (TEXT/INTEGER, no SQLite-only functions) so the planned
|
|
4
|
+
* Postgres adapter can run the same list.
|
|
5
|
+
*/
|
|
6
|
+
export const MIGRATIONS = [
|
|
7
|
+
{
|
|
8
|
+
version: 1,
|
|
9
|
+
name: "identity_and_audit",
|
|
10
|
+
sql: `
|
|
11
|
+
CREATE TABLE users (
|
|
12
|
+
id TEXT PRIMARY KEY,
|
|
13
|
+
email TEXT NOT NULL UNIQUE,
|
|
14
|
+
name TEXT NOT NULL,
|
|
15
|
+
role TEXT NOT NULL,
|
|
16
|
+
password_hash TEXT,
|
|
17
|
+
auth_provider TEXT NOT NULL,
|
|
18
|
+
oidc_subject TEXT UNIQUE,
|
|
19
|
+
disabled INTEGER NOT NULL DEFAULT 0,
|
|
20
|
+
failed_logins INTEGER NOT NULL DEFAULT 0,
|
|
21
|
+
locked_until INTEGER,
|
|
22
|
+
created_at INTEGER NOT NULL,
|
|
23
|
+
last_login_at INTEGER
|
|
24
|
+
);
|
|
25
|
+
CREATE TABLE sessions (
|
|
26
|
+
id_hash TEXT PRIMARY KEY,
|
|
27
|
+
user_id TEXT NOT NULL,
|
|
28
|
+
created_at INTEGER NOT NULL,
|
|
29
|
+
last_seen_at INTEGER NOT NULL,
|
|
30
|
+
expires_at INTEGER NOT NULL,
|
|
31
|
+
ip TEXT,
|
|
32
|
+
user_agent TEXT
|
|
33
|
+
);
|
|
34
|
+
CREATE INDEX sessions_user ON sessions (user_id);
|
|
35
|
+
CREATE TABLE api_tokens (
|
|
36
|
+
id TEXT PRIMARY KEY,
|
|
37
|
+
user_id TEXT NOT NULL,
|
|
38
|
+
name TEXT NOT NULL,
|
|
39
|
+
token_hash TEXT NOT NULL UNIQUE,
|
|
40
|
+
prefix TEXT NOT NULL,
|
|
41
|
+
created_at INTEGER NOT NULL,
|
|
42
|
+
last_used_at INTEGER,
|
|
43
|
+
revoked_at INTEGER
|
|
44
|
+
);
|
|
45
|
+
CREATE INDEX api_tokens_user ON api_tokens (user_id);
|
|
46
|
+
CREATE TABLE audit_events (
|
|
47
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
48
|
+
at INTEGER NOT NULL,
|
|
49
|
+
actor_id TEXT,
|
|
50
|
+
actor_label TEXT NOT NULL,
|
|
51
|
+
action TEXT NOT NULL,
|
|
52
|
+
target TEXT,
|
|
53
|
+
repo TEXT,
|
|
54
|
+
outcome TEXT NOT NULL,
|
|
55
|
+
ip TEXT,
|
|
56
|
+
user_agent TEXT,
|
|
57
|
+
details TEXT
|
|
58
|
+
);
|
|
59
|
+
CREATE INDEX audit_events_action ON audit_events (action);
|
|
60
|
+
CREATE INDEX audit_events_actor ON audit_events (actor_id);
|
|
61
|
+
CREATE TABLE oidc_states (
|
|
62
|
+
state TEXT PRIMARY KEY,
|
|
63
|
+
code_verifier TEXT NOT NULL,
|
|
64
|
+
nonce TEXT NOT NULL,
|
|
65
|
+
return_to TEXT NOT NULL,
|
|
66
|
+
created_at INTEGER NOT NULL
|
|
67
|
+
);
|
|
68
|
+
`,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
version: 2,
|
|
72
|
+
name: "analyses",
|
|
73
|
+
sql: `
|
|
74
|
+
CREATE TABLE analyses (
|
|
75
|
+
repo TEXT NOT NULL,
|
|
76
|
+
issue_iid INTEGER NOT NULL,
|
|
77
|
+
id TEXT NOT NULL,
|
|
78
|
+
ordinal INTEGER NOT NULL,
|
|
79
|
+
data TEXT NOT NULL,
|
|
80
|
+
updated_at INTEGER NOT NULL,
|
|
81
|
+
PRIMARY KEY (repo, issue_iid, id),
|
|
82
|
+
UNIQUE (repo, issue_iid, ordinal)
|
|
83
|
+
);
|
|
84
|
+
`,
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
//# sourceMappingURL=migrations.js.map
|