@bevel-software/platform-core-backend 0.29.0 → 0.31.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/agent-guide/access-control.md +49 -4
- package/agent-guide/html-views.md +76 -2
- package/dist/__tests__/test-repositories.d.ts +3 -0
- package/dist/__tests__/test-repositories.d.ts.map +1 -0
- package/dist/__tests__/test-repositories.js +18 -0
- package/dist/__tests__/test-repositories.js.map +1 -0
- package/dist/core/catalog-revision.js +3 -3
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +3 -2
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +3 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +35 -5
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +8 -0
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +4 -0
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +20 -0
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +32 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +135 -17
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +20 -6
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.js +1 -1
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +66 -6
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/audit/audit.contract.d.ts +52 -8
- package/dist/modules/audit/audit.contract.d.ts.map +1 -1
- package/dist/modules/audit/audit.contract.js +37 -1
- package/dist/modules/audit/audit.contract.js.map +1 -1
- package/dist/modules/audit/audit.routes.d.ts +1 -1
- package/dist/modules/audit/audit.routes.d.ts.map +1 -1
- package/dist/modules/audit/audit.routes.js +15 -13
- package/dist/modules/audit/audit.routes.js.map +1 -1
- package/dist/modules/audit/request-audit.d.ts +21 -10
- package/dist/modules/audit/request-audit.d.ts.map +1 -1
- package/dist/modules/audit/request-audit.js +26 -10
- package/dist/modules/audit/request-audit.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +22 -3
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +15 -6
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/diff/diff.service.d.ts.map +1 -1
- package/dist/modules/diff/diff.service.js +7 -1
- package/dist/modules/diff/diff.service.js.map +1 -1
- package/dist/modules/kb-fs/git-dir.d.ts +21 -0
- package/dist/modules/kb-fs/git-dir.d.ts.map +1 -0
- package/dist/modules/kb-fs/git-dir.js +58 -0
- package/dist/modules/kb-fs/git-dir.js.map +1 -0
- package/dist/modules/kb-fs/index.d.ts +3 -0
- package/dist/modules/kb-fs/index.d.ts.map +1 -1
- package/dist/modules/kb-fs/index.js +3 -1
- package/dist/modules/kb-fs/index.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +3 -12
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/mutex.d.ts +11 -0
- package/dist/modules/kb-fs/mutex.d.ts.map +1 -1
- package/dist/modules/kb-fs/mutex.js +14 -0
- package/dist/modules/kb-fs/mutex.js.map +1 -1
- package/dist/modules/kb-fs/ref-files.d.ts +63 -0
- package/dist/modules/kb-fs/ref-files.d.ts.map +1 -0
- package/dist/modules/kb-fs/ref-files.js +249 -0
- package/dist/modules/kb-fs/ref-files.js.map +1 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +14 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +4 -0
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts +5 -0
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +67 -0
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +107 -27
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +257 -50
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/request-context.d.ts +25 -0
- package/dist/modules/mcp/request-context.d.ts.map +1 -0
- package/dist/modules/mcp/request-context.js +11 -0
- package/dist/modules/mcp/request-context.js.map +1 -0
- package/dist/modules/mcp/shared-definitions.d.ts +56 -0
- package/dist/modules/mcp/shared-definitions.d.ts.map +1 -0
- package/dist/modules/mcp/shared-definitions.js +117 -0
- package/dist/modules/mcp/shared-definitions.js.map +1 -0
- package/dist/modules/mcp/surface-cache.d.ts +70 -0
- package/dist/modules/mcp/surface-cache.d.ts.map +1 -0
- package/dist/modules/mcp/surface-cache.js +104 -0
- package/dist/modules/mcp/surface-cache.js.map +1 -0
- package/dist/modules/plugins/teams.routes.d.ts +4 -0
- package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
- package/dist/modules/plugins/teams.routes.js +9 -1
- package/dist/modules/plugins/teams.routes.js.map +1 -1
- package/dist/modules/tool-auth/internal-token.service.d.ts +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts +1 -2
- package/dist/modules/tool-auth/tool-auth.middleware.d.ts.map +1 -1
- package/dist/modules/tool-auth/tool-auth.middleware.js +1 -2
- package/dist/modules/tool-auth/tool-auth.middleware.js.map +1 -1
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +33 -12
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-helpers/tool-handler.d.ts +2 -2
- package/dist/modules/tool-helpers/tool.contract.d.ts +10 -2
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js +3 -14
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +18 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +453 -2
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +142 -4
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +645 -128
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/read-on-branch.d.ts +35 -0
- package/dist/modules/workflow/read-on-branch.d.ts.map +1 -0
- package/dist/modules/workflow/read-on-branch.js +58 -0
- package/dist/modules/workflow/read-on-branch.js.map +1 -0
- package/dist/modules/workflow/workflow-hooks.d.ts +1 -1
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +22 -41
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +48 -8
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +163 -16
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-access.gate.d.ts +2 -2
- package/dist/modules/workspace/agent-access.gate.js +2 -2
- package/dist/modules/workspace/bounded-matcher.d.ts +79 -0
- package/dist/modules/workspace/bounded-matcher.d.ts.map +1 -0
- package/dist/modules/workspace/bounded-matcher.js +261 -0
- package/dist/modules/workspace/bounded-matcher.js.map +1 -0
- package/dist/modules/workspace/fetch-instant.d.ts +9 -0
- package/dist/modules/workspace/fetch-instant.d.ts.map +1 -0
- package/dist/modules/workspace/fetch-instant.js +13 -0
- package/dist/modules/workspace/fetch-instant.js.map +1 -0
- package/dist/modules/workspace/index.d.ts +9 -0
- package/dist/modules/workspace/index.d.ts.map +1 -1
- package/dist/modules/workspace/index.js +10 -0
- package/dist/modules/workspace/index.js.map +1 -1
- package/dist/modules/workspace/kb-repository.contract.d.ts +124 -0
- package/dist/modules/workspace/kb-repository.contract.d.ts.map +1 -0
- package/dist/modules/workspace/kb-repository.contract.js +19 -0
- package/dist/modules/workspace/kb-repository.contract.js.map +1 -0
- package/dist/modules/workspace/kb-repository.d.ts +236 -0
- package/dist/modules/workspace/kb-repository.d.ts.map +1 -0
- package/dist/modules/workspace/kb-repository.js +811 -0
- package/dist/modules/workspace/kb-repository.js.map +1 -0
- package/dist/modules/workspace/pattern-matcher.contract.d.ts +50 -0
- package/dist/modules/workspace/pattern-matcher.contract.d.ts.map +1 -0
- package/dist/modules/workspace/pattern-matcher.contract.js +27 -0
- package/dist/modules/workspace/pattern-matcher.contract.js.map +1 -0
- package/dist/modules/workspace/routine-write-policy.d.ts +0 -7
- package/dist/modules/workspace/routine-write-policy.d.ts.map +1 -1
- package/dist/modules/workspace/routine-write-policy.js +0 -5
- package/dist/modules/workspace/routine-write-policy.js.map +1 -1
- package/dist/modules/workspace/set-aside-clone.d.ts +40 -0
- package/dist/modules/workspace/set-aside-clone.d.ts.map +1 -1
- package/dist/modules/workspace/set-aside-clone.js +139 -0
- package/dist/modules/workspace/set-aside-clone.js.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +99 -0
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +407 -29
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +68 -32
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +240 -161
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +13 -4
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +162 -336
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/shared/git.contract.d.ts +7 -0
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js +9 -0
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/no-link-below.d.ts +10 -0
- package/dist/shared/no-link-below.d.ts.map +1 -0
- package/dist/shared/no-link-below.js +30 -0
- package/dist/shared/no-link-below.js.map +1 -0
- package/dist/test-setup.js +4 -0
- package/dist/test-setup.js.map +1 -1
- package/migrations/0018_agent_events_page.sql +11 -0
- package/migrations/meta/0018_snapshot.json +2429 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +3 -3
- package/src/__tests__/test-repositories.ts +22 -0
- package/src/__tests__/workspace-service.ts +69 -0
- package/src/core/__tests__/lifecycle.test.ts +16 -0
- package/src/core/catalog-revision.ts +3 -3
- package/src/core/create-core-server.ts +3 -2
- package/src/core/create-core-services.ts +39 -4
- package/src/core/lifecycle.ts +10 -0
- package/src/modules/access/__tests__/access-control.atref-cache.test.ts +144 -4
- package/src/modules/access/__tests__/access-control.group-read.test.ts +73 -0
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +66 -0
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -0
- package/src/modules/access/access-control.interface.ts +22 -0
- package/src/modules/access/access-control.service.ts +138 -15
- package/src/modules/access/access.routes.ts +22 -6
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +22 -1
- package/src/modules/agent-instructions/shared-file-rules.ts +1 -1
- package/src/modules/audit/__tests__/agent-audit.service.test.ts +78 -1
- package/src/modules/audit/__tests__/audit.routes.test.ts +43 -5
- package/src/modules/audit/agent-audit.service.ts +66 -6
- package/src/modules/audit/audit.contract.ts +62 -10
- package/src/modules/audit/audit.routes.ts +14 -12
- package/src/modules/audit/request-audit.ts +28 -13
- package/src/modules/database/core-schema.ts +15 -6
- package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
- package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
- package/src/modules/diff/__tests__/diff.service.worktree.test.ts +76 -0
- package/src/modules/diff/diff.service.ts +6 -4
- package/src/modules/kb-fs/__tests__/git-dir.test.ts +77 -0
- package/src/modules/kb-fs/__tests__/mutex.canonical.test.ts +57 -0
- package/src/modules/kb-fs/__tests__/ref-files.test.ts +338 -0
- package/src/modules/kb-fs/git-dir.ts +53 -0
- package/src/modules/kb-fs/index.ts +3 -0
- package/src/modules/kb-fs/locking-filesystem.ts +3 -12
- package/src/modules/kb-fs/mutex.ts +16 -0
- package/src/modules/kb-fs/ref-files.ts +279 -0
- package/src/modules/mcp/__tests__/mcp-routes-harness.ts +7 -1
- package/src/modules/mcp/__tests__/mcp.routes.page.test.ts +115 -0
- package/src/modules/mcp/__tests__/mcp.surface-cache.test.ts +521 -0
- package/src/modules/mcp/__tests__/shared-definitions.test.ts +28 -0
- package/src/modules/mcp/__tests__/surface-cache.test.ts +127 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +21 -0
- package/src/modules/mcp/mcp.routes.ts +68 -0
- package/src/modules/mcp/mcp.service.ts +315 -51
- package/src/modules/mcp/request-context.ts +34 -0
- package/src/modules/mcp/shared-definitions.ts +121 -0
- package/src/modules/mcp/surface-cache.ts +154 -0
- package/src/modules/plugins/__tests__/teams.routes.test.ts +55 -0
- package/src/modules/plugins/teams.routes.ts +14 -1
- package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +2 -2
- package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +2 -2
- package/src/modules/tool-auth/internal-token.service.ts +1 -1
- package/src/modules/tool-auth/tool-auth.middleware.ts +1 -2
- package/src/modules/tool-helpers/__tests__/branch-declaration.test.ts +2 -2
- package/src/modules/tool-helpers/tool-context.ts +37 -18
- package/src/modules/tool-helpers/tool-handler.ts +2 -2
- package/src/modules/tool-helpers/tool.contract.ts +10 -2
- package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +1 -1
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +7 -2
- package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +23 -12
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +250 -4
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +464 -4
- package/src/modules/workflow/agent-tools/change-request-read.tools.ts +4 -22
- package/src/modules/workflow/agent-tools/workflow.tools.ts +462 -1
- package/src/modules/workflow/git/__tests__/git.service.changedPathsMemo.test.ts +175 -0
- package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +76 -11
- package/src/modules/workflow/git/__tests__/git.service.mergeIntoBranch.test.ts +721 -0
- package/src/modules/workflow/git/__tests__/git.service.worktrees.test.ts +284 -0
- package/src/modules/workflow/git/git.service.ts +719 -126
- package/src/modules/workflow/read-on-branch.ts +79 -0
- package/src/modules/workflow/workflow-hooks.ts +1 -1
- package/src/modules/workflow/workflow.routes.ts +21 -39
- package/src/modules/workflow/workflow.service.ts +203 -16
- package/src/modules/workspace/__tests__/agent-downloads.test.ts +2 -2
- package/src/modules/workspace/__tests__/bounded-matcher.test.ts +213 -0
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +22 -7
- package/src/modules/workspace/__tests__/kb-repository.test.ts +481 -0
- package/src/modules/workspace/__tests__/rest-tool-arguments.test.ts +14 -0
- package/src/modules/workspace/__tests__/routine-write-policy.test.ts +0 -11
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +2 -2
- package/src/modules/workspace/__tests__/workspace.routes.folder-zip-access.test.ts +2 -2
- package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +4 -3
- package/src/modules/workspace/__tests__/workspace.service.any-workspace-credentials.test.ts +2 -2
- package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
- package/src/modules/workspace/__tests__/workspace.service.forget-clone-races.test.ts +12 -5
- package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -6
- package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.test.ts +24 -23
- package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.worktrees.test.ts +620 -0
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +15 -1
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +71 -5
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +153 -298
- package/src/modules/workspace/agent-access.gate.ts +2 -2
- package/src/modules/workspace/bounded-matcher.ts +328 -0
- package/src/modules/workspace/fetch-instant.ts +13 -0
- package/src/modules/workspace/index.ts +23 -0
- package/src/modules/workspace/kb-repository.contract.ts +124 -0
- package/src/modules/workspace/kb-repository.ts +844 -0
- package/src/modules/workspace/pattern-matcher.contract.ts +69 -0
- package/src/modules/workspace/routine-write-policy.ts +0 -15
- package/src/modules/workspace/set-aside-clone.ts +140 -0
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +3 -2
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +12 -10
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.worktrees.test.ts +613 -0
- package/src/modules/workspace/startup/kb-startup-runner.ts +422 -29
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +4 -3
- package/src/modules/workspace/workspace.service.ts +247 -169
- package/src/modules/workspace/workspace.tools.ts +179 -365
- package/src/shared/__tests__/no-link-below.test.ts +45 -0
- package/src/shared/git.contract.ts +10 -0
- package/src/shared/no-link-below.ts +29 -0
- package/src/test-setup.ts +5 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ToolError } from '../tool-helpers/tool.contract.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The contract for running a CALLER'S regular expression: what `grep` (REST,
|
|
5
|
+
* MCP and `call_tool_chain` alike, one handler) depends on, so it never holds
|
|
6
|
+
* a concrete engine. The one implementation is `WorkerPatternMatcher`
|
|
7
|
+
* (`bounded-matcher.ts`), owned by the composition root and closed with the
|
|
8
|
+
* graph.
|
|
9
|
+
*
|
|
10
|
+
* JavaScript's engine backtracks, so a pattern like `^(a+)+$` over a line of
|
|
11
|
+
* forty `a`s and a `!` never finishes. An implementation must therefore never
|
|
12
|
+
* run the pattern on the request thread, must stop a search whose matching
|
|
13
|
+
* outlives its budget with {@link PatternTooExpensiveError}, and must refuse
|
|
14
|
+
* work it cannot take on with {@link SearchBusyError} rather than queue it
|
|
15
|
+
* without bound. Within budget, a pattern matches exactly the lines
|
|
16
|
+
* `RegExp#test` does.
|
|
17
|
+
*/
|
|
18
|
+
export interface IPatternMatcher {
|
|
19
|
+
/**
|
|
20
|
+
* One search's matcher for `source`/`flags`: its budget is spent across
|
|
21
|
+
* every call it makes. Throws the 400 `Invalid regex` the caller's syntax
|
|
22
|
+
* earns, or the 400 `pattern_too_long` an over-long pattern earns.
|
|
23
|
+
*/
|
|
24
|
+
search(source: string, flags: string): IPatternSearch;
|
|
25
|
+
/** Ends the workers; a search after this is refused. */
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** One search, under one budget. */
|
|
30
|
+
export interface IPatternSearch {
|
|
31
|
+
/**
|
|
32
|
+
* The indices of the lines the pattern matches, in order, at most `limit` of
|
|
33
|
+
* them (the first `limit`, exactly as a loop that stops at the cap finds).
|
|
34
|
+
* Throws {@link PatternTooExpensiveError} once the search's budget is spent.
|
|
35
|
+
*/
|
|
36
|
+
matchingLines(lines: string[], limit: number): Promise<number[]>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A pattern stopped for taking too long — a 400, worded for the caller. */
|
|
40
|
+
export class PatternTooExpensiveError extends ToolError {
|
|
41
|
+
constructor(pattern: string, budgetMs: number) {
|
|
42
|
+
super(
|
|
43
|
+
`Pattern too expensive: /${pattern.length > 80 ? `${pattern.slice(0, 80)}…` : pattern}/ did not finish matching within this search's ${budgetMs / 1000} s budget, ` +
|
|
44
|
+
'so it was stopped and nothing was returned. Patterns with nested or overlapping repetition such as ' +
|
|
45
|
+
'`(a+)+` or `(a|aa)*` can take exponential time on some lines. Use a simpler pattern — a literal, ' +
|
|
46
|
+
'an anchor, a character class, a single repetition — or narrow `path`.',
|
|
47
|
+
400,
|
|
48
|
+
{ code: 'pattern_too_expensive' },
|
|
49
|
+
);
|
|
50
|
+
this.name = 'PatternTooExpensiveError';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Every worker busy and the queue for one full — a 503, so the caller retries rather than reads a partial answer. */
|
|
55
|
+
export class SearchBusyError extends ToolError {
|
|
56
|
+
constructor() {
|
|
57
|
+
super('Too many searches are running at once; try again in a few seconds.', 503, { code: 'search_busy' });
|
|
58
|
+
this.name = 'SearchBusyError';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether `err` ends a whole search rather than one file's part of it: a
|
|
64
|
+
* directory walk that skipped a file for either would answer an incomplete
|
|
65
|
+
* result as if it were the whole one.
|
|
66
|
+
*/
|
|
67
|
+
export function endsTheSearch(err: unknown): boolean {
|
|
68
|
+
return err instanceof PatternTooExpensiveError || err instanceof SearchBusyError;
|
|
69
|
+
}
|
|
@@ -33,12 +33,6 @@ export interface IRoutineWritePolicy {
|
|
|
33
33
|
* always passes — so this is a no-op for every non-restricted caller.
|
|
34
34
|
*/
|
|
35
35
|
assertPathWritable(sessionId: string | undefined, wsPath: string): void;
|
|
36
|
-
/**
|
|
37
|
-
* Throw a 403 `ToolError` when the session is restricted at all — for write
|
|
38
|
-
* tools with no single target path to check (arbitrary shell), which a
|
|
39
|
-
* restricted run must not be able to use as a write bypass.
|
|
40
|
-
*/
|
|
41
|
-
assertUnrestricted(sessionId: string | undefined): void;
|
|
42
36
|
}
|
|
43
37
|
|
|
44
38
|
export class RoutineWritePolicyService implements IRoutineWritePolicy {
|
|
@@ -66,13 +60,4 @@ export class RoutineWritePolicyService implements IRoutineWritePolicy {
|
|
|
66
60
|
403,
|
|
67
61
|
);
|
|
68
62
|
}
|
|
69
|
-
|
|
70
|
-
assertUnrestricted(sessionId: string | undefined): void {
|
|
71
|
-
if (sessionId && this.restrictions.has(sessionId)) {
|
|
72
|
-
throw new ToolError(
|
|
73
|
-
'This run is restricted to writing dashboard views only, so shell commands (which could write any file) are disabled.',
|
|
74
|
-
403,
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
63
|
}
|
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { logger } from '../../shared/logging.js';
|
|
4
|
+
import { isAbsence } from '../../shared/fs.contract.js';
|
|
5
|
+
import { readGitfile } from '../kb-fs/index.js';
|
|
6
|
+
import type { IKbRepository } from './kb-repository.contract.js';
|
|
7
|
+
|
|
8
|
+
const log = logger('set-aside');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `p` as the file system names it, for comparing two paths that may be
|
|
12
|
+
* spelled differently: git writes a worktree's link with forward slashes and
|
|
13
|
+
* the long folder names (`C:/Users/runneradmin/…`) where node may hold the
|
|
14
|
+
* same folder by its 8.3 short name (`C:\Users\RUNNER~1\…`). The nearest
|
|
15
|
+
* part that exists is resolved by the OS, the rest appended; on Windows the
|
|
16
|
+
* result is case-folded, as its file system is. Only an absent part is
|
|
17
|
+
* stepped over: a part that cannot be read (a permission or I/O error) throws,
|
|
18
|
+
* since "could not tell" is no reason to move someone's worktree.
|
|
19
|
+
*/
|
|
20
|
+
async function comparablePath(p: string): Promise<string> {
|
|
21
|
+
let existing = path.resolve(p);
|
|
22
|
+
const rest: string[] = [];
|
|
23
|
+
for (;;) {
|
|
24
|
+
try {
|
|
25
|
+
existing = await fs.realpath(existing);
|
|
26
|
+
break;
|
|
27
|
+
} catch (err) {
|
|
28
|
+
if (!isAbsence(err)) throw err;
|
|
29
|
+
const parent = path.dirname(existing);
|
|
30
|
+
if (parent === existing) break;
|
|
31
|
+
rest.unshift(path.basename(existing));
|
|
32
|
+
existing = parent;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const joined = path.join(existing, ...rest);
|
|
36
|
+
return process.platform === 'win32' ? joined.toLowerCase() : joined;
|
|
37
|
+
}
|
|
3
38
|
|
|
4
39
|
/**
|
|
5
40
|
* Moving a working copy of a repository that is no longer the configured one
|
|
@@ -94,3 +129,108 @@ export async function setAsideClone(repoDir: string, dest: string): Promise<void
|
|
|
94
129
|
}
|
|
95
130
|
await fs.rm(repoDir, { recursive: true, force: true });
|
|
96
131
|
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Set aside the knowledge base's repository AND EVERY WORKTREE OF IT, together,
|
|
135
|
+
* into `dest`: each worktree's folder as `dest/<workspace id>` (where a clone
|
|
136
|
+
* lands), the repository beside them under its own name.
|
|
137
|
+
*
|
|
138
|
+
* Together, because neither is any use without the other: a worktree's
|
|
139
|
+
* commits live in the repository, and a repository of another address is
|
|
140
|
+
* exactly as unusable as a clone of one. Nothing is deleted. The moved
|
|
141
|
+
* worktrees are re-linked to the moved repository, so `git log` in
|
|
142
|
+
* `dest/<workspace id>` shows the work that was never pushed.
|
|
143
|
+
*
|
|
144
|
+
* All or nothing: when a move fails part-way (the repository will not move
|
|
145
|
+
* after its worktrees did), the worktrees already moved are put back where
|
|
146
|
+
* they were, so a retry finds them with their repository and moves them
|
|
147
|
+
* together. A repository whose presence cannot be read (a permission or I/O
|
|
148
|
+
* error, not an absence) stops the call before anything moves.
|
|
149
|
+
*
|
|
150
|
+
* A worktree is found by its `.git` FILE naming a folder inside the
|
|
151
|
+
* repository — whether or not that folder is still there — so a worktree
|
|
152
|
+
* git had already lost track of goes with the rest. As the repository's one
|
|
153
|
+
* administrator (`exclusive`): no creation of a fresh repository and no
|
|
154
|
+
* config write runs while the folders move. Git operations in the worktrees
|
|
155
|
+
* are the caller's to hold off: the startup phase runs before anything is
|
|
156
|
+
* served, and the workspace service wraps the move in what surrounds a
|
|
157
|
+
* set-aside. Answers the workspace ids moved.
|
|
158
|
+
*
|
|
159
|
+
* `stillApplies`, when given, is asked again as the administrator, before
|
|
160
|
+
* anything moves: a caller decided to set the repository aside on what it
|
|
161
|
+
* read before taking the reservation, and another caller may have set it
|
|
162
|
+
* aside and made a fresh one of the configured address meanwhile. When the
|
|
163
|
+
* answer is no, nothing moves and the call answers no ids.
|
|
164
|
+
*
|
|
165
|
+
* A workspaces root, or a working copy's `.git`, that cannot be read (a
|
|
166
|
+
* permission or I/O error, not an absence) stops the call before anything
|
|
167
|
+
* moves, as an unreadable repository does: moving the repository without
|
|
168
|
+
* worktrees it could not see would leave every one of them pointing at
|
|
169
|
+
* nothing.
|
|
170
|
+
*/
|
|
171
|
+
export async function setAsideRepository(
|
|
172
|
+
repository: IKbRepository,
|
|
173
|
+
workspacesRoot: string,
|
|
174
|
+
kbDirName: string,
|
|
175
|
+
dest: string,
|
|
176
|
+
stillApplies?: () => Promise<boolean>,
|
|
177
|
+
): Promise<string[]> {
|
|
178
|
+
return repository.exclusive(async () => {
|
|
179
|
+
if (stillApplies && !(await stillApplies())) return [];
|
|
180
|
+
const repositoryPath = await comparablePath(repository.dir);
|
|
181
|
+
const inside = async (p: string): Promise<boolean> => {
|
|
182
|
+
const at = await comparablePath(p);
|
|
183
|
+
return at === repositoryPath || at.startsWith(repositoryPath + path.sep);
|
|
184
|
+
};
|
|
185
|
+
const worktrees: Array<{ id: string; dir: string }> = [];
|
|
186
|
+
let entries: import('node:fs').Dirent[] = [];
|
|
187
|
+
try {
|
|
188
|
+
entries = await fs.readdir(workspacesRoot, { withFileTypes: true });
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
191
|
+
// No workspaces root: only the repository to move.
|
|
192
|
+
if (code !== 'ENOENT' && code !== 'ENOTDIR') throw err;
|
|
193
|
+
}
|
|
194
|
+
for (const entry of entries) {
|
|
195
|
+
if (!entry.isDirectory()) continue;
|
|
196
|
+
const dir = path.join(workspacesRoot, entry.name, kbDirName);
|
|
197
|
+
// Only absence shows a folder is no worktree: a `.git` that could not be
|
|
198
|
+
// read (EACCES, EIO) stops the call, as an unreadable root does.
|
|
199
|
+
const gitdir = await readGitfile(path.join(dir, '.git'));
|
|
200
|
+
if (gitdir !== null && (await inside(gitdir))) worktrees.push({ id: entry.name, dir });
|
|
201
|
+
}
|
|
202
|
+
const repositoryThere = await fs.access(repository.dir).then(
|
|
203
|
+
() => true,
|
|
204
|
+
(err: NodeJS.ErrnoException) => {
|
|
205
|
+
if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return false;
|
|
206
|
+
throw err;
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
if (!repositoryThere && worktrees.length === 0) return [];
|
|
210
|
+
const moved: Array<{ dir: string; to: string }> = [];
|
|
211
|
+
const movedRepository = path.join(dest, path.basename(repository.dir));
|
|
212
|
+
try {
|
|
213
|
+
for (const { id, dir } of worktrees) {
|
|
214
|
+
const to = path.join(dest, id);
|
|
215
|
+
await setAsideClone(dir, to);
|
|
216
|
+
moved.push({ dir, to });
|
|
217
|
+
}
|
|
218
|
+
if (repositoryThere) await setAsideClone(repository.dir, movedRepository);
|
|
219
|
+
} catch (err) {
|
|
220
|
+
// Back where they were, beside the repository that did not move.
|
|
221
|
+
for (const { dir, to } of moved.reverse()) {
|
|
222
|
+
await setAsideClone(to, dir).catch((backErr: unknown) => {
|
|
223
|
+
log.error(`the set-aside worktree ${to} could not be put back at ${dir}; it is kept there:`, {
|
|
224
|
+
detail: backErr instanceof Error ? backErr.message : String(backErr),
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
throw err;
|
|
229
|
+
}
|
|
230
|
+
if (repositoryThere) {
|
|
231
|
+
await repository.relinkMovedCopy(movedRepository, moved.map(({ to }) => to));
|
|
232
|
+
}
|
|
233
|
+
repository.forget();
|
|
234
|
+
return worktrees.map(({ id }) => id);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
@@ -4,10 +4,11 @@ import { promisify } from 'node:util';
|
|
|
4
4
|
import fs from 'node:fs/promises';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import { KbRemoteUnreachableError
|
|
7
|
+
import { KbRemoteUnreachableError } from '../kb-startup-runner.js';
|
|
8
8
|
import { NodeGitRunner } from '../../../workflow/git/node-git-runner.js';
|
|
9
9
|
import { GitRunError, NO_GIT_CREDENTIALS, type IGitRunner } from '../../../../shared/git.contract.js';
|
|
10
10
|
import type { OnServerStart, ServerStartContext, StepResult } from '../on-server-start.js';
|
|
11
|
+
import { newKbStartupRunner } from '../../../../__tests__/workspace-service.js';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* What the runner does when the remote is not there — the one failure a boot
|
|
@@ -66,7 +67,7 @@ async function waitFor(condition: () => boolean, timeoutMs = 20_000): Promise<vo
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
function makeRunner(steps: OnServerStart[], url: () => string, gitRunner: IGitRunner = new NodeGitRunner()) {
|
|
69
|
-
return
|
|
70
|
+
return newKbStartupRunner({
|
|
70
71
|
gitRunner,
|
|
71
72
|
kbRepoUrl: url,
|
|
72
73
|
workspacesRoot,
|
|
@@ -6,12 +6,11 @@ import { promisify } from 'node:util';
|
|
|
6
6
|
import fs from 'node:fs/promises';
|
|
7
7
|
import os from 'node:os';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
-
import { KbStartupRunner } from '../kb-startup-runner.js';
|
|
10
|
-
import { WorkspaceService } from '../../workspace.service.js';
|
|
11
9
|
import { NodeGitRunner } from '../../../workflow/git/node-git-runner.js';
|
|
12
10
|
import { ClassifiedFailure, classifyGitFailure, failureOf } from '../../../../shared/git-failure.js';
|
|
13
11
|
import { GitRunError, gitCredentials } from '../../../../shared/git.contract.js';
|
|
14
12
|
import type { OnServerStart, ServerStartContext, StepResult } from '../on-server-start.js';
|
|
13
|
+
import { newKbStartupRunner, newWorkspaceService } from '../../../../__tests__/workspace-service.js';
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* The token in effect for the runners these suites build, changed by the
|
|
@@ -71,7 +70,7 @@ async function populatedUpstream(): Promise<void> {
|
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
function makeRunner(steps: OnServerStart[], overrides: Partial<Parameters<typeof runnerOpts>[1]> = {}) {
|
|
74
|
-
return
|
|
73
|
+
return newKbStartupRunner(runnerOpts(steps, overrides));
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
function runnerOpts(steps: OnServerStart[], overrides: Record<string, unknown> = {}) {
|
|
@@ -996,8 +995,11 @@ describe('KbStartupRunner — a working copy whose clone was never finished', ()
|
|
|
996
995
|
// The clone is sound and has a commit, but git cannot read the branch it
|
|
997
996
|
// is to follow: the ref the fetch updates is damaged. Not a state this
|
|
998
997
|
// phase repairs, so the start stops, and says which working copy.
|
|
999
|
-
|
|
1000
|
-
|
|
998
|
+
// The refs are where git says they are: a worktree's live in the
|
|
999
|
+
// repository it shares with every other branch.
|
|
1000
|
+
const gitDir = (await git(local(), ['rev-parse', '--path-format=absolute', '--git-common-dir'])).trim();
|
|
1001
|
+
const refs = path.join(gitDir, 'refs', 'remotes', 'origin');
|
|
1002
|
+
await fs.rm(path.join(gitDir, 'packed-refs'), { force: true });
|
|
1001
1003
|
await fs.mkdir(refs, { recursive: true });
|
|
1002
1004
|
await fs.writeFile(path.join(refs, DEFAULT_BRANCH), 'not a commit id\n', 'utf8');
|
|
1003
1005
|
|
|
@@ -1129,7 +1131,7 @@ describe('KbStartupRunner credentials', () => {
|
|
|
1129
1131
|
await git(repo, ['config', '--add', 'credential.helper',
|
|
1130
1132
|
'!f() { echo "username=stale-user"; echo "password=$GITHUB_TOKEN"; }; f']);
|
|
1131
1133
|
|
|
1132
|
-
const ws =
|
|
1134
|
+
const ws = newWorkspaceService(workspacesRoot, upstream, testKbContext(), new NodeFs(), new NodeGitRunner(undefined, credentials));
|
|
1133
1135
|
await ws.getOrCreateForBranch(DEFAULT_BRANCH);
|
|
1134
1136
|
|
|
1135
1137
|
const all = (await git(repo, ['config', '--local', '--get-all', 'credential.helper'])).trim();
|
|
@@ -1147,14 +1149,14 @@ describe('KbStartupRunner credentials', () => {
|
|
|
1147
1149
|
const repo = await materializeDefaultBranchClone();
|
|
1148
1150
|
await git(repo, ['config', '--add', 'credential.helper', 'cache --timeout=300']);
|
|
1149
1151
|
|
|
1150
|
-
const ws =
|
|
1152
|
+
const ws = newWorkspaceService(workspacesRoot, upstream, testKbContext(), new NodeFs(), new NodeGitRunner(undefined, credentials));
|
|
1151
1153
|
await ws.getOrCreateForBranch(DEFAULT_BRANCH); // stamp with token
|
|
1152
1154
|
let all = (await git(repo, ['config', '--local', '--get-all', 'credential.helper'])).trim();
|
|
1153
1155
|
expect(all).toContain('cache --timeout=300');
|
|
1154
1156
|
expect(all).toContain('password=$GITHUB_TOKEN');
|
|
1155
1157
|
|
|
1156
1158
|
token = null;
|
|
1157
|
-
const ws2 =
|
|
1159
|
+
const ws2 =newWorkspaceService(workspacesRoot, upstream, testKbContext(), new NodeFs(), new NodeGitRunner(undefined, credentials));
|
|
1158
1160
|
await ws2.getOrCreateForBranch(DEFAULT_BRANCH); // unset OUR helper only
|
|
1159
1161
|
all = (await git(repo, ['config', '--local', '--get-all', 'credential.helper'])).trim();
|
|
1160
1162
|
expect(all).toContain('cache --timeout=300');
|
|
@@ -1170,7 +1172,7 @@ describe('KbStartupRunner credentials', () => {
|
|
|
1170
1172
|
const repo = await materializeDefaultBranchClone(); // stamped
|
|
1171
1173
|
token = null;
|
|
1172
1174
|
|
|
1173
|
-
const ws =
|
|
1175
|
+
const ws = newWorkspaceService(workspacesRoot, upstream, testKbContext(), new NodeFs(), new NodeGitRunner(undefined, credentials));
|
|
1174
1176
|
await ws.getOrCreateForBranch(DEFAULT_BRANCH);
|
|
1175
1177
|
|
|
1176
1178
|
await expect(git(repo, ['config', '--local', '--get', 'credential.helper']))
|
|
@@ -1185,7 +1187,7 @@ describe('KbStartupRunner credentials', () => {
|
|
|
1185
1187
|
await populatedUpstream();
|
|
1186
1188
|
const repo = await materializeDefaultBranchClone(); // no helper
|
|
1187
1189
|
|
|
1188
|
-
const ws =
|
|
1190
|
+
const ws = newWorkspaceService(workspacesRoot, upstream, testKbContext(), new NodeFs(), new NodeGitRunner(undefined, credentials));
|
|
1189
1191
|
await ws.getOrCreateForBranch(DEFAULT_BRANCH); // adopt, tokenless
|
|
1190
1192
|
token = 'ghp_late';
|
|
1191
1193
|
await ws.getOrCreateForBranch(DEFAULT_BRANCH); // cached fast path
|