@posthog/agent 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +221 -219
- package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +21 -0
- package/dist/adapters/claude/conversion/tool-use-to-acp.js +547 -0
- package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -0
- package/dist/adapters/claude/permissions/permission-options.d.ts +13 -0
- package/dist/adapters/claude/permissions/permission-options.js +117 -0
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -0
- package/dist/adapters/claude/questions/utils.d.ts +132 -0
- package/dist/adapters/claude/questions/utils.js +63 -0
- package/dist/adapters/claude/questions/utils.js.map +1 -0
- package/dist/adapters/claude/tools.d.ts +18 -0
- package/dist/adapters/claude/tools.js +95 -0
- package/dist/adapters/claude/tools.js.map +1 -0
- package/dist/agent-DBQY1BfC.d.ts +123 -0
- package/dist/agent.d.ts +5 -0
- package/dist/agent.js +3656 -0
- package/dist/agent.js.map +1 -0
- package/dist/claude-cli/cli.js +3695 -2746
- package/dist/claude-cli/vendor/ripgrep/COPYING +3 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-darwin/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-darwin/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-linux/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/arm64-linux/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-darwin/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-darwin/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-linux/rg +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-linux/ripgrep.node +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-win32/rg.exe +0 -0
- package/dist/claude-cli/vendor/ripgrep/x64-win32/ripgrep.node +0 -0
- package/dist/gateway-models.d.ts +24 -0
- package/dist/gateway-models.js +93 -0
- package/dist/gateway-models.js.map +1 -0
- package/dist/index.d.ts +170 -1157
- package/dist/index.js +3252 -5074
- package/dist/index.js.map +1 -1
- package/dist/logger-DDBiMOOD.d.ts +24 -0
- package/dist/posthog-api.d.ts +40 -0
- package/dist/posthog-api.js +175 -0
- package/dist/posthog-api.js.map +1 -0
- package/dist/server/agent-server.d.ts +41 -0
- package/dist/server/agent-server.js +4451 -0
- package/dist/server/agent-server.js.map +1 -0
- package/dist/server/bin.d.ts +1 -0
- package/dist/server/bin.js +4507 -0
- package/dist/server/bin.js.map +1 -0
- package/dist/types.d.ts +129 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -14
- package/src/acp-extensions.ts +98 -16
- package/src/adapters/acp-connection.ts +494 -0
- package/src/adapters/base-acp-agent.ts +150 -0
- package/src/adapters/claude/claude-agent.ts +596 -0
- package/src/adapters/claude/conversion/acp-to-sdk.ts +102 -0
- package/src/adapters/claude/conversion/sdk-to-acp.ts +571 -0
- package/src/adapters/claude/conversion/tool-use-to-acp.ts +618 -0
- package/src/adapters/claude/hooks.ts +64 -0
- package/src/adapters/claude/mcp/tool-metadata.ts +102 -0
- package/src/adapters/claude/permissions/permission-handlers.ts +433 -0
- package/src/adapters/claude/permissions/permission-options.ts +103 -0
- package/src/adapters/claude/plan/utils.ts +56 -0
- package/src/adapters/claude/questions/utils.ts +92 -0
- package/src/adapters/claude/session/commands.ts +38 -0
- package/src/adapters/claude/session/mcp-config.ts +37 -0
- package/src/adapters/claude/session/models.ts +12 -0
- package/src/adapters/claude/session/options.ts +236 -0
- package/src/adapters/claude/tool-meta.ts +143 -0
- package/src/adapters/claude/tools.ts +53 -688
- package/src/adapters/claude/types.ts +61 -0
- package/src/adapters/codex/spawn.ts +130 -0
- package/src/agent.ts +96 -587
- package/src/execution-mode.ts +43 -0
- package/src/gateway-models.ts +135 -0
- package/src/index.ts +79 -0
- package/src/otel-log-writer.test.ts +105 -0
- package/src/otel-log-writer.ts +94 -0
- package/src/posthog-api.ts +75 -235
- package/src/resume.ts +115 -0
- package/src/sagas/apply-snapshot-saga.test.ts +690 -0
- package/src/sagas/apply-snapshot-saga.ts +88 -0
- package/src/sagas/capture-tree-saga.test.ts +892 -0
- package/src/sagas/capture-tree-saga.ts +141 -0
- package/src/sagas/resume-saga.test.ts +558 -0
- package/src/sagas/resume-saga.ts +332 -0
- package/src/sagas/test-fixtures.ts +250 -0
- package/src/server/agent-server.test.ts +220 -0
- package/src/server/agent-server.ts +748 -0
- package/src/server/bin.ts +88 -0
- package/src/server/jwt.ts +65 -0
- package/src/server/schemas.ts +47 -0
- package/src/server/types.ts +13 -0
- package/src/server/utils/retry.test.ts +122 -0
- package/src/server/utils/retry.ts +61 -0
- package/src/server/utils/sse-parser.test.ts +93 -0
- package/src/server/utils/sse-parser.ts +46 -0
- package/src/session-log-writer.test.ts +140 -0
- package/src/session-log-writer.ts +137 -0
- package/src/test/assertions.ts +114 -0
- package/src/test/controllers/sse-controller.ts +107 -0
- package/src/test/fixtures/api.ts +111 -0
- package/src/test/fixtures/config.ts +33 -0
- package/src/test/fixtures/notifications.ts +92 -0
- package/src/test/mocks/claude-sdk.ts +251 -0
- package/src/test/mocks/msw-handlers.ts +48 -0
- package/src/test/setup.ts +114 -0
- package/src/test/wait.ts +41 -0
- package/src/tree-tracker.ts +173 -0
- package/src/types.ts +54 -137
- package/src/utils/acp-content.ts +58 -0
- package/src/utils/async-mutex.test.ts +104 -0
- package/src/utils/async-mutex.ts +31 -0
- package/src/utils/common.ts +15 -0
- package/src/utils/gateway.ts +9 -6
- package/src/utils/logger.ts +0 -30
- package/src/utils/streams.ts +220 -0
- package/CLAUDE.md +0 -331
- package/src/adapters/claude/claude.ts +0 -1947
- package/src/adapters/claude/mcp-server.ts +0 -810
- package/src/adapters/claude/utils.ts +0 -267
- package/src/adapters/connection.ts +0 -95
- package/src/file-manager.ts +0 -273
- package/src/git-manager.ts +0 -577
- package/src/schemas.ts +0 -241
- package/src/session-store.ts +0 -259
- package/src/task-manager.ts +0 -163
- package/src/todo-manager.ts +0 -180
- package/src/tools/registry.ts +0 -134
- package/src/tools/types.ts +0 -133
- package/src/utils/tapped-stream.ts +0 -60
- package/src/worktree-manager.ts +0 -974
package/src/types.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
// import and export to keep a single type file
|
|
2
|
-
|
|
3
|
-
import type { SessionNotification } from "@agentclientprotocol/sdk";
|
|
4
|
-
import type {
|
|
5
|
-
CanUseTool,
|
|
6
|
-
PermissionResult,
|
|
7
|
-
} from "@anthropic-ai/claude-agent-sdk";
|
|
8
|
-
export type { CanUseTool, PermissionResult, SessionNotification };
|
|
9
|
-
|
|
10
1
|
/**
|
|
11
2
|
* Stored custom notification following ACP extensibility model.
|
|
12
3
|
* Custom notifications use underscore-prefixed methods (e.g., `_posthog/phase_start`).
|
|
@@ -29,7 +20,7 @@ export interface StoredNotification {
|
|
|
29
20
|
*/
|
|
30
21
|
export type StoredEntry = StoredNotification;
|
|
31
22
|
|
|
32
|
-
// PostHog Task model (matches
|
|
23
|
+
// PostHog Task model (matches Twig's OpenAPI schema)
|
|
33
24
|
export interface Task {
|
|
34
25
|
id: string;
|
|
35
26
|
task_number?: number;
|
|
@@ -64,7 +55,8 @@ export type ArtifactType =
|
|
|
64
55
|
| "context"
|
|
65
56
|
| "reference"
|
|
66
57
|
| "output"
|
|
67
|
-
| "artifact"
|
|
58
|
+
| "artifact"
|
|
59
|
+
| "tree_snapshot";
|
|
68
60
|
|
|
69
61
|
export interface TaskRunArtifact {
|
|
70
62
|
name: string;
|
|
@@ -104,88 +96,23 @@ export interface TaskRun {
|
|
|
104
96
|
completed_at: string | null;
|
|
105
97
|
}
|
|
106
98
|
|
|
107
|
-
export interface
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface TaskArtifactUploadPayload {
|
|
115
|
-
name: string;
|
|
116
|
-
type: ArtifactType;
|
|
117
|
-
content: string;
|
|
118
|
-
content_type?: string;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export enum PermissionMode {
|
|
122
|
-
PLAN = "plan",
|
|
123
|
-
DEFAULT = "default",
|
|
124
|
-
ACCEPT_EDITS = "acceptEdits",
|
|
125
|
-
BYPASS = "bypassPermissions",
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export interface ExecutionOptions {
|
|
129
|
-
repositoryPath?: string;
|
|
130
|
-
permissionMode?: PermissionMode;
|
|
99
|
+
export interface ProcessSpawnedCallback {
|
|
100
|
+
onProcessSpawned?: (info: {
|
|
101
|
+
pid: number;
|
|
102
|
+
command: string;
|
|
103
|
+
sessionId?: string;
|
|
104
|
+
}) => void;
|
|
105
|
+
onProcessExited?: (pid: number) => void;
|
|
131
106
|
}
|
|
132
107
|
|
|
133
108
|
export interface TaskExecutionOptions {
|
|
134
109
|
repositoryPath?: string;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
queryOverrides?: Record<string, unknown>;
|
|
140
|
-
// Fine-grained permission control (only applied to build phase)
|
|
141
|
-
// See: https://docs.claude.com/en/api/agent-sdk/permissions
|
|
142
|
-
canUseTool?: CanUseTool;
|
|
143
|
-
skipGitBranch?: boolean; // Skip creating a task-specific git branch
|
|
144
|
-
framework?: "claude"; // Agent framework to use (defaults to "claude")
|
|
145
|
-
task?: Task; // Pre-fetched task to avoid redundant API call
|
|
146
|
-
isReconnect?: boolean; // Session recreation - skip RUN_STARTED notification
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export interface ExecutionResult {
|
|
150
|
-
// biome-ignore lint/suspicious/noExplicitAny: Results array contains varying SDK response types
|
|
151
|
-
results: any[];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export interface PlanResult {
|
|
155
|
-
plan: string;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export interface TaskExecutionResult {
|
|
159
|
-
task: Task;
|
|
160
|
-
plan?: string;
|
|
161
|
-
executionResult?: ExecutionResult;
|
|
110
|
+
adapter?: "claude" | "codex";
|
|
111
|
+
model?: string;
|
|
112
|
+
codexBinaryPath?: string;
|
|
113
|
+
processCallbacks?: ProcessSpawnedCallback;
|
|
162
114
|
}
|
|
163
115
|
|
|
164
|
-
// MCP Server configuration types (re-exported from Claude SDK for convenience)
|
|
165
|
-
export type McpServerConfig =
|
|
166
|
-
| {
|
|
167
|
-
type?: "stdio";
|
|
168
|
-
command: string;
|
|
169
|
-
args?: string[];
|
|
170
|
-
env?: Record<string, string>;
|
|
171
|
-
}
|
|
172
|
-
| {
|
|
173
|
-
type: "sse";
|
|
174
|
-
url: string;
|
|
175
|
-
headers?: Record<string, string>;
|
|
176
|
-
}
|
|
177
|
-
| {
|
|
178
|
-
type: "http";
|
|
179
|
-
url: string;
|
|
180
|
-
headers?: Record<string, string>;
|
|
181
|
-
}
|
|
182
|
-
| {
|
|
183
|
-
type: "sdk";
|
|
184
|
-
name: string;
|
|
185
|
-
// biome-ignore lint/suspicious/noExplicitAny: McpServer instance type from external SDK
|
|
186
|
-
instance?: any;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
116
|
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
190
117
|
|
|
191
118
|
export type OnLogCallback = (
|
|
@@ -195,67 +122,57 @@ export type OnLogCallback = (
|
|
|
195
122
|
data?: unknown,
|
|
196
123
|
) => void;
|
|
197
124
|
|
|
198
|
-
export interface
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
getPosthogApiKey?: () => string;
|
|
204
|
-
posthogProjectId?: number;
|
|
205
|
-
|
|
206
|
-
// PostHog MCP configuration
|
|
207
|
-
posthogMcpUrl?: string;
|
|
125
|
+
export interface PostHogAPIConfig {
|
|
126
|
+
apiUrl: string;
|
|
127
|
+
getApiKey: () => string;
|
|
128
|
+
projectId: number;
|
|
129
|
+
}
|
|
208
130
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
131
|
+
export interface OtelTransportConfig {
|
|
132
|
+
/** PostHog ingest host, e.g., "https://us.i.posthog.com" */
|
|
133
|
+
host: string;
|
|
134
|
+
/** Project API key */
|
|
135
|
+
apiKey: string;
|
|
136
|
+
/** Override the logs endpoint path (default: /i/v1/logs) */
|
|
137
|
+
logsPath?: string;
|
|
138
|
+
}
|
|
213
139
|
|
|
214
|
-
|
|
140
|
+
export interface AgentConfig {
|
|
141
|
+
posthog?: PostHogAPIConfig;
|
|
142
|
+
/** OTEL transport config for shipping logs to PostHog Logs */
|
|
143
|
+
otelTransport?: OtelTransportConfig;
|
|
215
144
|
debug?: boolean;
|
|
216
145
|
onLog?: OnLogCallback;
|
|
217
|
-
|
|
218
|
-
// Fine-grained permission control for direct run() calls
|
|
219
|
-
// See: https://docs.claude.com/en/api/agent-sdk/permissions
|
|
220
|
-
canUseTool?: CanUseTool;
|
|
221
146
|
}
|
|
222
147
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
148
|
+
// Device info for tracking where work happens
|
|
149
|
+
export interface DeviceInfo {
|
|
150
|
+
type: "local" | "cloud";
|
|
151
|
+
name?: string;
|
|
227
152
|
}
|
|
228
153
|
|
|
229
|
-
//
|
|
230
|
-
export type
|
|
231
|
-
| "error"
|
|
232
|
-
| "experiment"
|
|
233
|
-
| "insight"
|
|
234
|
-
| "feature_flag"
|
|
235
|
-
| "generic";
|
|
154
|
+
// Agent execution mode - for tracking interactive vs background runs, when backgrounded an agent will continue working without asking questions
|
|
155
|
+
export type AgentMode = "interactive" | "background";
|
|
236
156
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
// biome-ignore lint/suspicious/noExplicitAny: Metadata contains varying resource-specific fields
|
|
244
|
-
metadata?: Record<string, any>;
|
|
157
|
+
// Git file status codes
|
|
158
|
+
export type FileStatus = "A" | "M" | "D";
|
|
159
|
+
|
|
160
|
+
export interface FileChange {
|
|
161
|
+
path: string;
|
|
162
|
+
status: FileStatus;
|
|
245
163
|
}
|
|
246
164
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
165
|
+
// Tree snapshot - what TreeTracker captures
|
|
166
|
+
export interface TreeSnapshot {
|
|
167
|
+
treeHash: string;
|
|
168
|
+
baseCommit: string | null;
|
|
169
|
+
archiveUrl?: string;
|
|
170
|
+
changes: FileChange[];
|
|
171
|
+
timestamp: string;
|
|
172
|
+
interrupted?: boolean;
|
|
252
173
|
}
|
|
253
174
|
|
|
254
|
-
//
|
|
255
|
-
export interface
|
|
256
|
-
|
|
257
|
-
worktreeName: string;
|
|
258
|
-
branchName: string;
|
|
259
|
-
baseBranch: string;
|
|
260
|
-
createdAt: string;
|
|
175
|
+
// Tree snapshot event - includes device info when sent as notification
|
|
176
|
+
export interface TreeSnapshotEvent extends TreeSnapshot {
|
|
177
|
+
device?: DeviceInfo;
|
|
261
178
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ContentBlock, ToolCallContent } from "@agentclientprotocol/sdk";
|
|
2
|
+
|
|
3
|
+
export function text(value: string): ContentBlock {
|
|
4
|
+
return { type: "text", text: value };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function image(
|
|
8
|
+
data: string,
|
|
9
|
+
mimeType: string,
|
|
10
|
+
uri?: string,
|
|
11
|
+
): ContentBlock {
|
|
12
|
+
return { type: "image", data, mimeType, uri };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function resourceLink(
|
|
16
|
+
uri: string,
|
|
17
|
+
name: string,
|
|
18
|
+
options?: {
|
|
19
|
+
mimeType?: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
size?: bigint;
|
|
23
|
+
},
|
|
24
|
+
): ContentBlock {
|
|
25
|
+
return {
|
|
26
|
+
type: "resource_link",
|
|
27
|
+
uri,
|
|
28
|
+
name,
|
|
29
|
+
...options,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class ToolContentBuilder {
|
|
34
|
+
private items: ToolCallContent[] = [];
|
|
35
|
+
|
|
36
|
+
text(value: string): this {
|
|
37
|
+
this.items.push({ type: "content", content: text(value) });
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
image(data: string, mimeType: string, uri?: string): this {
|
|
42
|
+
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
diff(path: string, oldText: string | null, newText: string): this {
|
|
47
|
+
this.items.push({ type: "diff", path, oldText, newText });
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
build(): ToolCallContent[] {
|
|
52
|
+
return this.items;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function toolContent(): ToolContentBuilder {
|
|
57
|
+
return new ToolContentBuilder();
|
|
58
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { AsyncMutex } from "./async-mutex.js";
|
|
3
|
+
|
|
4
|
+
describe("AsyncMutex", () => {
|
|
5
|
+
it("acquires lock when unlocked", async () => {
|
|
6
|
+
const mutex = new AsyncMutex();
|
|
7
|
+
|
|
8
|
+
expect(mutex.isLocked()).toBe(false);
|
|
9
|
+
await mutex.acquire();
|
|
10
|
+
expect(mutex.isLocked()).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("releases lock", async () => {
|
|
14
|
+
const mutex = new AsyncMutex();
|
|
15
|
+
|
|
16
|
+
await mutex.acquire();
|
|
17
|
+
expect(mutex.isLocked()).toBe(true);
|
|
18
|
+
|
|
19
|
+
mutex.release();
|
|
20
|
+
expect(mutex.isLocked()).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("queues concurrent acquires", async () => {
|
|
24
|
+
const mutex = new AsyncMutex();
|
|
25
|
+
const order: number[] = [];
|
|
26
|
+
|
|
27
|
+
await mutex.acquire();
|
|
28
|
+
expect(mutex.queueLength).toBe(0);
|
|
29
|
+
|
|
30
|
+
const promise1 = mutex.acquire().then(() => order.push(1));
|
|
31
|
+
const promise2 = mutex.acquire().then(() => order.push(2));
|
|
32
|
+
|
|
33
|
+
expect(mutex.queueLength).toBe(2);
|
|
34
|
+
|
|
35
|
+
mutex.release();
|
|
36
|
+
await promise1;
|
|
37
|
+
expect(order).toEqual([1]);
|
|
38
|
+
|
|
39
|
+
mutex.release();
|
|
40
|
+
await promise2;
|
|
41
|
+
expect(order).toEqual([1, 2]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("processes queue in FIFO order", async () => {
|
|
45
|
+
const mutex = new AsyncMutex();
|
|
46
|
+
const order: number[] = [];
|
|
47
|
+
|
|
48
|
+
await mutex.acquire();
|
|
49
|
+
|
|
50
|
+
const promises = [1, 2, 3, 4, 5].map((n) =>
|
|
51
|
+
mutex.acquire().then(() => {
|
|
52
|
+
order.push(n);
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(mutex.queueLength).toBe(5);
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < 5; i++) {
|
|
59
|
+
mutex.release();
|
|
60
|
+
await promises[i];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
expect(order).toEqual([1, 2, 3, 4, 5]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("serializes concurrent operations", async () => {
|
|
67
|
+
const mutex = new AsyncMutex();
|
|
68
|
+
const results: string[] = [];
|
|
69
|
+
|
|
70
|
+
const operation = async (id: string, delay: number) => {
|
|
71
|
+
await mutex.acquire();
|
|
72
|
+
try {
|
|
73
|
+
results.push(`${id}-start`);
|
|
74
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
75
|
+
results.push(`${id}-end`);
|
|
76
|
+
} finally {
|
|
77
|
+
mutex.release();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
await Promise.all([
|
|
82
|
+
operation("a", 10),
|
|
83
|
+
operation("b", 5),
|
|
84
|
+
operation("c", 1),
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
expect(results).toEqual([
|
|
88
|
+
"a-start",
|
|
89
|
+
"a-end",
|
|
90
|
+
"b-start",
|
|
91
|
+
"b-end",
|
|
92
|
+
"c-start",
|
|
93
|
+
"c-end",
|
|
94
|
+
]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("handles release without acquire gracefully", () => {
|
|
98
|
+
const mutex = new AsyncMutex();
|
|
99
|
+
|
|
100
|
+
expect(mutex.isLocked()).toBe(false);
|
|
101
|
+
mutex.release();
|
|
102
|
+
expect(mutex.isLocked()).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class AsyncMutex {
|
|
2
|
+
private locked = false;
|
|
3
|
+
private queue: Array<() => void> = [];
|
|
4
|
+
|
|
5
|
+
async acquire(): Promise<void> {
|
|
6
|
+
if (!this.locked) {
|
|
7
|
+
this.locked = true;
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
this.queue.push(resolve);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
release(): void {
|
|
16
|
+
const next = this.queue.shift();
|
|
17
|
+
if (next) {
|
|
18
|
+
next();
|
|
19
|
+
} else {
|
|
20
|
+
this.locked = false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
isLocked(): boolean {
|
|
25
|
+
return this.locked;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get queueLength(): number {
|
|
29
|
+
return this.queue.length;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Logger } from "./logger.js";
|
|
2
|
+
|
|
3
|
+
export const IS_ROOT =
|
|
4
|
+
typeof process !== "undefined" &&
|
|
5
|
+
(process.geteuid?.() ?? process.getuid?.()) === 0;
|
|
6
|
+
|
|
7
|
+
export function unreachable(value: never, logger: Logger): void {
|
|
8
|
+
let valueAsString: string;
|
|
9
|
+
try {
|
|
10
|
+
valueAsString = JSON.stringify(value);
|
|
11
|
+
} catch {
|
|
12
|
+
valueAsString = value;
|
|
13
|
+
}
|
|
14
|
+
logger.error(`Unexpected case: ${valueAsString}`);
|
|
15
|
+
}
|
package/src/utils/gateway.ts
CHANGED
|
@@ -2,14 +2,17 @@ export function getLlmGatewayUrl(posthogHost: string): string {
|
|
|
2
2
|
const url = new URL(posthogHost);
|
|
3
3
|
const hostname = url.hostname;
|
|
4
4
|
|
|
5
|
+
// Local development (normalize 127.0.0.1 to localhost)
|
|
5
6
|
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
6
|
-
return `${url.protocol}//localhost:3308`;
|
|
7
|
+
return `${url.protocol}//localhost:3308/twig`;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
// Docker containers accessing host
|
|
11
|
+
if (hostname === "host.docker.internal") {
|
|
12
|
+
return `${url.protocol}//host.docker.internal:3308/twig`;
|
|
13
|
+
}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
// Production - extract region from hostname, default to US
|
|
16
|
+
const region = hostname.match(/^(us|eu)\.posthog\.com$/)?.[1] ?? "us";
|
|
17
|
+
return `https://gateway.${region}.posthog.com/twig`;
|
|
15
18
|
}
|
package/src/utils/logger.ts
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
import type { LogLevel as LogLevelType, OnLogCallback } from "../types.js";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Simple logger utility with configurable debug mode and external log forwarding
|
|
5
|
-
*/
|
|
6
|
-
export enum LogLevel {
|
|
7
|
-
ERROR = 0,
|
|
8
|
-
WARN = 1,
|
|
9
|
-
INFO = 2,
|
|
10
|
-
DEBUG = 3,
|
|
11
|
-
}
|
|
12
|
-
|
|
13
3
|
export interface LoggerConfig {
|
|
14
4
|
debug?: boolean;
|
|
15
5
|
prefix?: string;
|
|
@@ -30,14 +20,6 @@ export class Logger {
|
|
|
30
20
|
this.onLog = config.onLog;
|
|
31
21
|
}
|
|
32
22
|
|
|
33
|
-
setDebug(enabled: boolean) {
|
|
34
|
-
this.debugEnabled = enabled;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
setOnLog(onLog: OnLogCallback | undefined) {
|
|
38
|
-
this.onLog = onLog;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
23
|
private formatMessage(
|
|
42
24
|
level: string,
|
|
43
25
|
message: string,
|
|
@@ -87,18 +69,6 @@ export class Logger {
|
|
|
87
69
|
this.emitLog("debug", message, data);
|
|
88
70
|
}
|
|
89
71
|
|
|
90
|
-
log(level: LogLevelType, message: string, data?: unknown, scope?: string) {
|
|
91
|
-
const originalScope = this.scope;
|
|
92
|
-
if (scope) {
|
|
93
|
-
this.scope = scope;
|
|
94
|
-
}
|
|
95
|
-
this.emitLog(level, message, data);
|
|
96
|
-
this.scope = originalScope;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Create a child logger with additional prefix and scope
|
|
101
|
-
*/
|
|
102
72
|
child(childPrefix: string): Logger {
|
|
103
73
|
return new Logger({
|
|
104
74
|
debug: this.debugEnabled,
|