toolcraft 0.0.23 → 0.0.25

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.
Files changed (152) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.compile-check.js +1 -0
  3. package/dist/cli.d.ts +1 -0
  4. package/dist/cli.js +50 -13
  5. package/dist/error-report.js +32 -3
  6. package/dist/human-in-loop/approval-tasks.d.ts +1 -0
  7. package/dist/human-in-loop/approval-tasks.js +7 -5
  8. package/dist/human-in-loop/approvals-commands.js +51 -8
  9. package/dist/human-in-loop/runner.js +24 -19
  10. package/dist/human-in-loop/state-machine.d.ts +3 -3
  11. package/dist/human-in-loop/state-machine.js +13 -5
  12. package/dist/index.d.ts +5 -0
  13. package/dist/index.js +6 -1
  14. package/dist/mcp-proxy.js +85 -19
  15. package/dist/mcp.compile-check.js +1 -0
  16. package/dist/mcp.d.ts +1 -0
  17. package/dist/mcp.js +50 -8
  18. package/dist/renderer.js +119 -13
  19. package/dist/sdk.compile-check.js +1 -0
  20. package/dist/sdk.d.ts +1 -0
  21. package/dist/sdk.js +56 -11
  22. package/node_modules/@poe-code/agent-defs/dist/registry.d.ts +1 -1
  23. package/node_modules/@poe-code/agent-defs/dist/registry.js +22 -11
  24. package/node_modules/@poe-code/agent-defs/package.json +1 -1
  25. package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript-script.js +5 -1
  26. package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript.js +1 -1
  27. package/node_modules/@poe-code/agent-human-in-loop/package.json +1 -1
  28. package/node_modules/@poe-code/agent-mcp-config/dist/apply.d.ts +1 -1
  29. package/node_modules/@poe-code/agent-mcp-config/dist/apply.js +41 -92
  30. package/node_modules/@poe-code/agent-mcp-config/dist/configs.js +4 -1
  31. package/node_modules/@poe-code/agent-mcp-config/dist/shapes.d.ts +14 -2
  32. package/node_modules/@poe-code/agent-mcp-config/dist/shapes.js +11 -4
  33. package/node_modules/@poe-code/agent-mcp-config/package.json +1 -1
  34. package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +200 -22
  35. package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.js +7 -1
  36. package/node_modules/@poe-code/config-mutations/dist/formats/index.js +1 -1
  37. package/node_modules/@poe-code/config-mutations/dist/formats/json.js +11 -7
  38. package/node_modules/@poe-code/config-mutations/dist/formats/object.d.ts +4 -0
  39. package/node_modules/@poe-code/config-mutations/dist/formats/object.js +27 -0
  40. package/node_modules/@poe-code/config-mutations/dist/formats/toml.js +12 -9
  41. package/node_modules/@poe-code/config-mutations/dist/formats/yaml.js +12 -9
  42. package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.d.ts +11 -1
  43. package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.js +10 -1
  44. package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.js +25 -1
  45. package/node_modules/@poe-code/config-mutations/dist/types.d.ts +12 -2
  46. package/node_modules/@poe-code/config-mutations/package.json +1 -1
  47. package/node_modules/@poe-code/design-system/dist/acp/components.js +3 -1
  48. package/node_modules/@poe-code/design-system/dist/components/browser.d.ts +1 -1
  49. package/node_modules/@poe-code/design-system/dist/components/browser.js +6 -1
  50. package/node_modules/@poe-code/design-system/dist/components/color.js +9 -8
  51. package/node_modules/@poe-code/design-system/dist/components/command-errors.js +3 -2
  52. package/node_modules/@poe-code/design-system/dist/components/detail-card.d.ts +22 -0
  53. package/node_modules/@poe-code/design-system/dist/components/detail-card.js +69 -0
  54. package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +88 -11
  55. package/node_modules/@poe-code/design-system/dist/components/index.d.ts +1 -1
  56. package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
  57. package/node_modules/@poe-code/design-system/dist/components/table.d.ts +2 -0
  58. package/node_modules/@poe-code/design-system/dist/components/table.js +82 -5
  59. package/node_modules/@poe-code/design-system/dist/components/template.d.ts +4 -0
  60. package/node_modules/@poe-code/design-system/dist/components/template.js +198 -32
  61. package/node_modules/@poe-code/design-system/dist/components/text.js +29 -5
  62. package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +2 -2
  63. package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +77 -32
  64. package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +28 -5
  65. package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +45 -28
  66. package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.d.ts +4 -0
  67. package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.js +71 -0
  68. package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
  69. package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +6 -0
  70. package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +32 -10
  71. package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +3 -0
  72. package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +57 -6
  73. package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +1 -0
  74. package/node_modules/@poe-code/design-system/dist/explorer/state.js +12 -15
  75. package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -1
  76. package/node_modules/@poe-code/design-system/dist/index.js +2 -1
  77. package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -1
  78. package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +8 -5
  79. package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +1 -1
  80. package/node_modules/@poe-code/design-system/dist/static/menu.js +8 -2
  81. package/node_modules/@poe-code/design-system/dist/static/spinner.js +10 -4
  82. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +9 -2
  83. package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +19 -2
  84. package/node_modules/@poe-code/design-system/package.json +2 -1
  85. package/node_modules/@poe-code/process-runner/dist/docker/args.d.ts +1 -0
  86. package/node_modules/@poe-code/process-runner/dist/docker/args.js +11 -3
  87. package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +377 -130
  88. package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +78 -10
  89. package/node_modules/@poe-code/process-runner/dist/docker/env-file.d.ts +6 -0
  90. package/node_modules/@poe-code/process-runner/dist/docker/env-file.js +49 -0
  91. package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.js +3 -2
  92. package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +21 -5
  93. package/node_modules/@poe-code/process-runner/dist/index.d.ts +1 -0
  94. package/node_modules/@poe-code/process-runner/dist/index.js +1 -0
  95. package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.js +30 -8
  96. package/node_modules/@poe-code/process-runner/dist/types.d.ts +6 -0
  97. package/node_modules/@poe-code/process-runner/dist/workspace-transfer.d.ts +61 -0
  98. package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +503 -0
  99. package/node_modules/@poe-code/process-runner/package.json +1 -1
  100. package/node_modules/@poe-code/task-list/README.md +0 -2
  101. package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +3 -0
  102. package/node_modules/@poe-code/task-list/dist/backends/gh-issues-sync.js +89 -59
  103. package/node_modules/@poe-code/task-list/dist/backends/gh-issues.d.ts +9 -3
  104. package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +460 -99
  105. package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +156 -154
  106. package/node_modules/@poe-code/task-list/dist/backends/utils.d.ts +2 -0
  107. package/node_modules/@poe-code/task-list/dist/backends/utils.js +79 -0
  108. package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +120 -132
  109. package/node_modules/@poe-code/task-list/dist/index.d.ts +3 -1
  110. package/node_modules/@poe-code/task-list/dist/index.js +2 -0
  111. package/node_modules/@poe-code/task-list/dist/move.d.ts +2 -0
  112. package/node_modules/@poe-code/task-list/dist/move.js +215 -0
  113. package/node_modules/@poe-code/task-list/dist/open.js +3 -4
  114. package/node_modules/@poe-code/task-list/dist/state-machine.js +3 -1
  115. package/node_modules/@poe-code/task-list/dist/state.js +9 -0
  116. package/node_modules/@poe-code/task-list/dist/types.d.ts +48 -13
  117. package/node_modules/@poe-code/task-list/package.json +1 -2
  118. package/node_modules/auth-store/dist/create-secret-store.js +4 -1
  119. package/node_modules/auth-store/dist/encrypted-file-store.d.ts +8 -0
  120. package/node_modules/auth-store/dist/encrypted-file-store.js +104 -8
  121. package/node_modules/auth-store/dist/index.d.ts +1 -1
  122. package/node_modules/auth-store/dist/keychain-store.d.ts +4 -1
  123. package/node_modules/auth-store/dist/keychain-store.js +18 -16
  124. package/node_modules/auth-store/dist/provider-store.d.ts +5 -1
  125. package/node_modules/auth-store/dist/provider-store.js +55 -7
  126. package/node_modules/auth-store/dist/types.d.ts +3 -1
  127. package/node_modules/auth-store/package.json +2 -1
  128. package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +46 -15
  129. package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +49 -12
  130. package/node_modules/mcp-oauth/dist/client/token-endpoint.js +6 -1
  131. package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +1 -1
  132. package/node_modules/mcp-oauth/package.json +1 -0
  133. package/node_modules/tiny-mcp-client/.turbo/turbo-build.log +1 -1
  134. package/node_modules/tiny-mcp-client/dist/internal.d.ts +9 -4
  135. package/node_modules/tiny-mcp-client/dist/internal.js +244 -66
  136. package/node_modules/tiny-mcp-client/dist/oauth-discovery.d.ts +1 -1
  137. package/node_modules/tiny-mcp-client/dist/oauth-discovery.js +4 -7
  138. package/node_modules/tiny-mcp-client/package.json +2 -1
  139. package/node_modules/tiny-mcp-client/src/http-oauth.integration.test.ts +1 -1
  140. package/node_modules/tiny-mcp-client/src/http-oauth.test.ts +46 -0
  141. package/node_modules/tiny-mcp-client/src/internal.ts +287 -76
  142. package/node_modules/tiny-mcp-client/src/mcp-client-sdk.test.ts +32 -0
  143. package/node_modules/tiny-mcp-client/src/mcp-client-tiny-stdio-test-server-tools.test.ts +1 -1
  144. package/node_modules/tiny-mcp-client/src/oauth-discovery.ts +5 -10
  145. package/node_modules/tiny-mcp-client/src/transports.test.ts +588 -6
  146. package/package.json +10 -12
  147. package/node_modules/@poe-code/file-lock/README.md +0 -52
  148. package/node_modules/@poe-code/file-lock/dist/index.d.ts +0 -1
  149. package/node_modules/@poe-code/file-lock/dist/index.js +0 -1
  150. package/node_modules/@poe-code/file-lock/dist/lock.d.ts +0 -27
  151. package/node_modules/@poe-code/file-lock/dist/lock.js +0 -203
  152. package/node_modules/@poe-code/file-lock/package.json +0 -23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -39,18 +39,18 @@
39
39
  "test": "cd ../.. && vitest run packages/toolcraft/src",
40
40
  "test:unit": "cd ../.. && vitest run packages/toolcraft/src",
41
41
  "lint": "cd ../.. && eslint packages/toolcraft/src --ext ts && tsc -p packages/toolcraft/tsconfig.json --noEmit",
42
- "prepack": "node ../../scripts/manage-bundled-workspace-deps.mjs prepare . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations @poe-code/process-runner tiny-mcp-client mcp-oauth auth-store",
43
- "postpack": "node ../../scripts/manage-bundled-workspace-deps.mjs cleanup . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/file-lock @poe-code/agent-defs @poe-code/config-mutations @poe-code/process-runner tiny-mcp-client mcp-oauth auth-store"
42
+ "prepack": "node ../../scripts/manage-bundled-workspace-deps.mjs prepare . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/agent-defs @poe-code/config-mutations @poe-code/process-runner tiny-mcp-client mcp-oauth auth-store",
43
+ "postpack": "node ../../scripts/manage-bundled-workspace-deps.mjs cleanup . @poe-code/design-system @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/agent-defs @poe-code/config-mutations @poe-code/process-runner tiny-mcp-client mcp-oauth auth-store"
44
44
  },
45
45
  "dependencies": {
46
46
  "@clack/core": "^1.0.0",
47
47
  "@clack/prompts": "^1.0.0",
48
+ "toolcraft-schema": "0.0.25",
48
49
  "commander": "^14.0.3",
49
50
  "jose": "^6.1.2",
50
51
  "jsonc-parser": "^3.3.1",
51
52
  "smol-toml": "^1.3.0",
52
53
  "tiny-stdio-mcp-server": "^0.1.0",
53
- "toolcraft-schema": "^0.0.23",
54
54
  "yaml": "^2.8.2"
55
55
  },
56
56
  "files": [
@@ -69,7 +69,6 @@
69
69
  "@poe-code/agent-mcp-config",
70
70
  "@poe-code/agent-human-in-loop",
71
71
  "@poe-code/task-list",
72
- "@poe-code/file-lock",
73
72
  "@poe-code/agent-defs",
74
73
  "@poe-code/config-mutations",
75
74
  "@poe-code/process-runner",
@@ -78,16 +77,15 @@
78
77
  "auth-store"
79
78
  ],
80
79
  "optionalDependencies": {
81
- "@poe-code/agent-defs": "*",
82
- "@poe-code/agent-human-in-loop": "*",
80
+ "@poe-code/design-system": "^0.0.2",
83
81
  "@poe-code/agent-mcp-config": "*",
82
+ "@poe-code/agent-human-in-loop": "*",
83
+ "@poe-code/task-list": "*",
84
+ "@poe-code/agent-defs": "*",
84
85
  "@poe-code/config-mutations": "*",
85
- "@poe-code/design-system": "^0.0.2",
86
- "@poe-code/file-lock": "*",
87
86
  "@poe-code/process-runner": "*",
88
- "@poe-code/task-list": "*",
89
- "auth-store": "*",
87
+ "tiny-mcp-client": "*",
90
88
  "mcp-oauth": "*",
91
- "tiny-mcp-client": "*"
89
+ "auth-store": "*"
92
90
  }
93
91
  }
@@ -1,52 +0,0 @@
1
- # @poe-code/file-lock
2
-
3
- Atomic file locking built on lockfile creation with `open(..., "wx")` and timestamped stale cleanup.
4
-
5
- It creates a sibling lockfile at `<filePath>.lock`, retries with bounded backoff when the lock is already held, and automatically reclaims stale locks. Locks from the current host are reclaimed immediately when their recorded process is no longer running; other locks fall back to the lockfile timestamp and `staleMs`.
6
-
7
- ## Public API
8
-
9
- ### `acquireFileLock(filePath, options?)`
10
-
11
- ```ts
12
- function acquireFileLock(
13
- filePath: string,
14
- options?: FileLockOptions
15
- ): Promise<ReleaseLock>;
16
- ```
17
-
18
- Attempts to acquire an exclusive lock for `filePath` and resolves to an async release function. If the lock already exists, acquisition retries until it succeeds, the lock is considered stale and cleaned up, the provided abort signal fires, or retries are exhausted. When retries are exhausted, it throws `LockTimeoutError`.
19
-
20
- ## Options
21
-
22
- | Option | Type | Default | Behavior |
23
- | --- | --- | --- | --- |
24
- | `staleMs` | `number` | `1_000` | Lockfile age in milliseconds after which an existing lock is treated as stale and removed when live local PID metadata does not prove the owner is still running. |
25
- | `retries` | `number` | `20` | Number of retry attempts after the initial acquire attempt. |
26
- | `minTimeout` | `number` | `25` | Minimum retry backoff in milliseconds. |
27
- | `maxTimeout` | `number` | `250` | Maximum retry backoff in milliseconds. |
28
- | `isPidRunning` | `(pid: number) => boolean` | `process.kill(pid, 0)` check | Injectable process liveness check used to reclaim dead local lock owners. |
29
- | `signal` | `AbortSignal` | none | Cancels acquisition while waiting between retries and rejects with an abort error. |
30
- | `fs` | `FileLockFs` | `node:fs/promises` adapter | Injectable filesystem used for lock creation, metadata reading, inspection, and deletion. |
31
-
32
- ## Env vars
33
-
34
- None.
35
-
36
- ## Usage
37
-
38
- ```ts
39
- import { acquireFileLock } from "@poe-code/file-lock";
40
-
41
- const release = await acquireFileLock("/repo/workflow.md");
42
-
43
- try {
44
- // Do work while holding the lock.
45
- } finally {
46
- await release();
47
- }
48
- ```
49
-
50
- ## Notes
51
-
52
- `@poe-code/agent-harness-tools` re-exports `acquireFileLock` as `lockWorkflow`.
@@ -1 +0,0 @@
1
- export { acquireFileLock, LockTimeoutError, type FileLockFs, type FileLockOptions, type ReleaseLock } from "./lock.js";
@@ -1 +0,0 @@
1
- export { acquireFileLock, LockTimeoutError } from "./lock.js";
@@ -1,27 +0,0 @@
1
- export interface FileLockFs {
2
- open(path: string, flags: string): Promise<{
3
- close(): Promise<void>;
4
- writeFile(data: string, options?: BufferEncoding | {
5
- encoding?: BufferEncoding;
6
- }): Promise<void>;
7
- }>;
8
- stat(path: string): Promise<{
9
- mtimeMs: number;
10
- }>;
11
- readFile?(path: string, encoding: BufferEncoding): Promise<string>;
12
- unlink(path: string): Promise<void>;
13
- }
14
- export interface FileLockOptions {
15
- staleMs?: number;
16
- retries?: number;
17
- minTimeout?: number;
18
- maxTimeout?: number;
19
- fs?: FileLockFs;
20
- isPidRunning?: (pid: number) => boolean;
21
- signal?: AbortSignal;
22
- }
23
- export type ReleaseLock = () => Promise<void>;
24
- export declare class LockTimeoutError extends Error {
25
- constructor(message: string);
26
- }
27
- export declare function acquireFileLock(filePath: string, options?: FileLockOptions): Promise<ReleaseLock>;
@@ -1,203 +0,0 @@
1
- import * as fsPromises from "node:fs/promises";
2
- import * as os from "node:os";
3
- export class LockTimeoutError extends Error {
4
- constructor(message) {
5
- super(message);
6
- this.name = "LockTimeoutError";
7
- }
8
- }
9
- function createAbortError() {
10
- const error = new Error("The operation was aborted.");
11
- error.name = "AbortError";
12
- return error;
13
- }
14
- function throwIfAborted(signal) {
15
- if (signal?.aborted) {
16
- throw createAbortError();
17
- }
18
- }
19
- function sleep(ms, signal) {
20
- if (!signal) {
21
- return new Promise((resolve) => setTimeout(resolve, ms));
22
- }
23
- if (signal.aborted) {
24
- return Promise.reject(createAbortError());
25
- }
26
- return new Promise((resolve, reject) => {
27
- const timeoutId = setTimeout(() => {
28
- signal.removeEventListener("abort", onAbort);
29
- resolve();
30
- }, ms);
31
- const onAbort = () => {
32
- clearTimeout(timeoutId);
33
- signal.removeEventListener("abort", onAbort);
34
- reject(createAbortError());
35
- };
36
- signal.addEventListener("abort", onAbort, { once: true });
37
- });
38
- }
39
- function backoff(attempt, minTimeout, maxTimeout) {
40
- const delay = Math.min(maxTimeout, minTimeout * 2 ** attempt);
41
- return delay + Math.random() * delay * 0.1;
42
- }
43
- function hasErrorCode(error, code) {
44
- return (!!error &&
45
- typeof error === "object" &&
46
- "code" in error &&
47
- error.code === code);
48
- }
49
- function hasAnyErrorCode(error, codes) {
50
- return codes.some((code) => hasErrorCode(error, code));
51
- }
52
- function isPidRunning(pid) {
53
- try {
54
- process.kill(pid, 0);
55
- return true;
56
- }
57
- catch (error) {
58
- return !hasErrorCode(error, "ESRCH");
59
- }
60
- }
61
- function createDefaultFs() {
62
- return {
63
- open: (path, flags) => fsPromises.open(path, flags),
64
- readFile: (path, encoding) => fsPromises.readFile(path, encoding),
65
- stat: fsPromises.stat,
66
- unlink: fsPromises.unlink
67
- };
68
- }
69
- async function removeLockFile(fs, lockPath, signal) {
70
- for (let attempt = 0; attempt <= 4; attempt += 1) {
71
- throwIfAborted(signal);
72
- try {
73
- await fs.unlink(lockPath);
74
- return;
75
- }
76
- catch (error) {
77
- if (hasErrorCode(error, "ENOENT")) {
78
- return;
79
- }
80
- if (!hasAnyErrorCode(error, ["EPERM", "EBUSY"]) || attempt === 4) {
81
- throw error;
82
- }
83
- }
84
- await sleep(25 * 2 ** attempt, signal);
85
- }
86
- }
87
- function parseLockMetadata(content) {
88
- try {
89
- const parsed = JSON.parse(content);
90
- if (!parsed || typeof parsed !== "object" || !("host" in parsed) || !("pid" in parsed)) {
91
- return undefined;
92
- }
93
- const { host, pid } = parsed;
94
- if (typeof host === "string" && typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0) {
95
- return {
96
- host,
97
- pid
98
- };
99
- }
100
- }
101
- catch (ignoredError) {
102
- void ignoredError;
103
- }
104
- return undefined;
105
- }
106
- async function readLockMetadata(fs, lockPath) {
107
- if (!fs.readFile) {
108
- return undefined;
109
- }
110
- try {
111
- return parseLockMetadata(await fs.readFile(lockPath, "utf8"));
112
- }
113
- catch (error) {
114
- if (hasErrorCode(error, "ENOENT")) {
115
- return null;
116
- }
117
- return undefined;
118
- }
119
- }
120
- async function shouldReclaimLock(options) {
121
- const metadata = await readLockMetadata(options.fs, options.lockPath);
122
- if (metadata === null) {
123
- return "missing";
124
- }
125
- if (metadata?.host === os.hostname()) {
126
- return !options.isPidRunning(metadata.pid);
127
- }
128
- return Date.now() - options.stat.mtimeMs > options.staleMs;
129
- }
130
- async function writeLockMetadata(handle) {
131
- try {
132
- await handle.writeFile(JSON.stringify({ pid: process.pid, host: os.hostname(), acquiredAt: new Date().toISOString() }), { encoding: "utf8" });
133
- }
134
- catch (ignoredError) {
135
- void ignoredError;
136
- }
137
- try {
138
- await handle.close();
139
- }
140
- catch (ignoredError) {
141
- void ignoredError;
142
- }
143
- }
144
- export async function acquireFileLock(filePath, options = {}) {
145
- const fs = options.fs ?? createDefaultFs();
146
- const retries = options.retries ?? 20;
147
- const minTimeout = options.minTimeout ?? 25;
148
- const maxTimeout = options.maxTimeout ?? 250;
149
- const staleMs = options.staleMs ?? 1_000;
150
- const pidIsRunning = options.isPidRunning ?? isPidRunning;
151
- const lockPath = `${filePath}.lock`;
152
- let attempt = 0;
153
- while (attempt <= retries) {
154
- throwIfAborted(options.signal);
155
- try {
156
- const handle = await fs.open(lockPath, "wx");
157
- await writeLockMetadata(handle);
158
- let released = false;
159
- return async () => {
160
- if (released) {
161
- return;
162
- }
163
- released = true;
164
- await removeLockFile(fs, lockPath, options.signal);
165
- };
166
- }
167
- catch (error) {
168
- if (!hasErrorCode(error, "EEXIST")) {
169
- throw error;
170
- }
171
- }
172
- let stat;
173
- try {
174
- stat = await fs.stat(lockPath);
175
- }
176
- catch (statError) {
177
- if (hasErrorCode(statError, "ENOENT")) {
178
- continue;
179
- }
180
- throw statError;
181
- }
182
- const reclaimLock = await shouldReclaimLock({
183
- fs,
184
- isPidRunning: pidIsRunning,
185
- lockPath,
186
- staleMs,
187
- stat
188
- });
189
- if (reclaimLock === "missing") {
190
- continue;
191
- }
192
- if (reclaimLock) {
193
- await removeLockFile(fs, lockPath, options.signal);
194
- continue;
195
- }
196
- if (attempt >= retries) {
197
- break;
198
- }
199
- await sleep(backoff(attempt, minTimeout, maxTimeout), options.signal);
200
- attempt += 1;
201
- }
202
- throw new LockTimeoutError(`Failed to acquire lock on "${filePath}".`);
203
- }
@@ -1,23 +0,0 @@
1
- {
2
- "name": "@poe-code/file-lock",
3
- "version": "0.0.1",
4
- "private": true,
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
12
- }
13
- },
14
- "scripts": {
15
- "build": "tsc",
16
- "test": "cd ../.. && vitest run packages/file-lock/src",
17
- "test:unit": "cd ../.. && vitest run packages/file-lock/src"
18
- },
19
- "files": [
20
- "dist"
21
- ],
22
- "dependencies": {}
23
- }