Skip to content

Commit bc437e4

Browse files
authored
Merge branch 'main' into posthog-code/fix-cloud-queue-wedge
2 parents a3e8b00 + c617988 commit bc437e4

161 files changed

Lines changed: 7170 additions & 2411 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/code/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"@types/node": "^24.0.0",
6464
"@types/react": "^19.1.0",
6565
"@types/react-dom": "^19.1.0",
66-
"@types/semver": "^7.5.0",
66+
"@types/semver": "^7.7.1",
6767
"@vitejs/plugin-react": "^4.2.1",
6868
"@vitest/ui": "^4.0.10",
6969
"adm-zip": "^0.5.16",
@@ -126,14 +126,14 @@
126126
"@opentelemetry/semantic-conventions": "^1.39.0",
127127
"@parcel/watcher": "^2.5.6",
128128
"@phosphor-icons/react": "^2.1.10",
129-
"@pierre/diffs": "^1.1.7",
129+
"@pierre/diffs": "^1.1.21",
130130
"@posthog/agent": "workspace:*",
131131
"@posthog/electron-trpc": "workspace:*",
132132
"@posthog/enricher": "workspace:*",
133133
"@posthog/git": "workspace:*",
134134
"@posthog/hedgehog-mode": "^0.0.48",
135135
"@posthog/platform": "workspace:*",
136-
"@posthog/quill": "0.1.0-alpha.7",
136+
"@posthog/quill": "0.3.0-beta.1",
137137
"@posthog/shared": "workspace:*",
138138
"@radix-ui/react-collapsible": "^1.1.12",
139139
"@radix-ui/react-icons": "^1.3.2",
@@ -186,16 +186,17 @@
186186
"react-markdown": "^10.1.0",
187187
"react-resizable-panels": "^3.0.6",
188188
"reflect-metadata": "^0.2.2",
189-
"semver": "^7.6.0",
190189
"rehype-raw": "^7.0.0",
191190
"rehype-sanitize": "^6.0.0",
192191
"remark-breaks": "^4.0.0",
193192
"remark-gfm": "^4.0.1",
193+
"semver": "^7.6.0",
194194
"shadcn": "^4.1.2",
195195
"smol-toml": "^1.6.0",
196196
"sonner": "^2.0.7",
197197
"striptags": "^3.2.0",
198198
"tailwind-merge": "^3.5.0",
199+
"tailwindcss-scroll-mask": "^0.0.3",
199200
"tippy.js": "^6.3.7",
200201
"tw-animate-css": "^1.4.0",
201202
"vaul": "^1.1.2",

apps/code/scripts/download-binaries.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const DEST_DIR = join(__dirname, "..", "resources", "codex-acp");
1919
const BINARIES = [
2020
{
2121
name: "codex-acp",
22-
version: "0.12.0",
22+
version: "0.14.0",
2323
getUrl: (version, target) => {
2424
const ext = target.includes("windows") ? "zip" : "tar.gz";
2525
return `https://github.com/zed-industries/codex-acp/releases/download/v${version}/codex-acp-${version}-${target}.${ext}`;

apps/code/src/main/services/auth/service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { IPowerManager } from "@posthog/platform/power-manager";
22
import { OAUTH_SCOPE_VERSION } from "@shared/constants/oauth";
3+
import { NotAuthenticatedError } from "@shared/errors";
34
import type { CloudRegion } from "@shared/types/regions";
45
import { type BackoffOptions, sleepWithBackoff } from "@shared/utils/backoff";
56
import { getCloudUrlFromRegion } from "@shared/utils/urls";
@@ -336,7 +337,7 @@ export class AuthService extends TypedEventEmitter<AuthServiceEvents> {
336337

337338
const storedSession = this.resolveStoredSession();
338339
if (!storedSession) {
339-
throw new Error("Not authenticated");
340+
throw new NotAuthenticatedError();
340341
}
341342

342343
return storedSession;
@@ -549,7 +550,7 @@ export class AuthService extends TypedEventEmitter<AuthServiceEvents> {
549550
}
550551
private requireSession(): InMemorySession {
551552
if (!this.session) {
552-
throw new Error("Not authenticated");
553+
throw new NotAuthenticatedError();
553554
}
554555
return this.session;
555556
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import { execSync } from "node:child_process";
2+
import * as fs from "node:fs/promises";
3+
import * as os from "node:os";
4+
import * as path from "node:path";
5+
import { makeLoggerMock } from "@test/loggerMock";
6+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
7+
8+
vi.mock("../../utils/logger.js", () => makeLoggerMock());
9+
10+
import type { AuthService } from "../auth/service";
11+
import { EnrichmentService } from "./service";
12+
13+
const stubAuthService = {
14+
getState: vi.fn(),
15+
getValidAccessToken: vi.fn(),
16+
} as unknown as AuthService;
17+
18+
async function writeFile(repoRoot: string, relPath: string, content: string) {
19+
const abs = path.join(repoRoot, relPath);
20+
await fs.mkdir(path.dirname(abs), { recursive: true });
21+
await fs.writeFile(abs, content);
22+
}
23+
24+
describe("EnrichmentService.detectPosthogInstallState", () => {
25+
let tmp: string;
26+
let service: EnrichmentService;
27+
28+
beforeEach(async () => {
29+
tmp = await fs.mkdtemp(path.join(os.tmpdir(), "posthog-detect-"));
30+
// listAllFiles uses `git ls-files` + `git ls-files -o` under the hood, so
31+
// the repo needs to be a git checkout.
32+
execSync("git init -q", { cwd: tmp, stdio: "pipe" });
33+
service = new EnrichmentService(stubAuthService);
34+
});
35+
36+
afterEach(async () => {
37+
await fs.rm(tmp, { recursive: true, force: true });
38+
service.dispose();
39+
});
40+
41+
it("returns not_installed for an empty repo", async () => {
42+
expect(await service.detectPosthogInstallState(tmp)).toBe("not_installed");
43+
});
44+
45+
it("returns installed_no_init when package.json declares posthog-js but no init call exists", async () => {
46+
await writeFile(
47+
tmp,
48+
"package.json",
49+
JSON.stringify({
50+
name: "test-app",
51+
dependencies: { "posthog-js": "^1.0.0" },
52+
}),
53+
);
54+
expect(await service.detectPosthogInstallState(tmp)).toBe(
55+
"installed_no_init",
56+
);
57+
});
58+
59+
it("returns initialized when an entry-point file calls posthog.init()", async () => {
60+
await writeFile(
61+
tmp,
62+
"package.json",
63+
JSON.stringify({
64+
name: "test-app",
65+
dependencies: { "posthog-js": "^1.0.0" },
66+
}),
67+
);
68+
await writeFile(
69+
tmp,
70+
"pages/_app.tsx",
71+
`import posthog from "posthog-js";\nposthog.init("phc_xxx", { api_host: "https://app.posthog.com" });\nexport default function App() { return null; }\n`,
72+
);
73+
expect(await service.detectPosthogInstallState(tmp)).toBe("initialized");
74+
});
75+
76+
it("returns initialized when posthog is used in a non-standard path (apps/dashboard/src/bootstrap.ts)", async () => {
77+
await writeFile(
78+
tmp,
79+
"package.json",
80+
JSON.stringify({
81+
name: "monorepo-root",
82+
}),
83+
);
84+
await writeFile(
85+
tmp,
86+
"apps/dashboard/package.json",
87+
JSON.stringify({
88+
name: "dashboard",
89+
dependencies: { "posthog-js": "^1.0.0" },
90+
}),
91+
);
92+
await writeFile(
93+
tmp,
94+
"apps/dashboard/src/bootstrap.ts",
95+
`import posthog from "posthog-js";\nposthog.init(import.meta.env.VITE_POSTHOG_KEY);\nposthog.capture("app_loaded");\n`,
96+
);
97+
expect(await service.detectPosthogInstallState(tmp)).toBe("initialized");
98+
});
99+
100+
it("picks up monorepo manifests in subdirectories (apps/api/requirements.txt)", async () => {
101+
await writeFile(tmp, "package.json", JSON.stringify({ name: "monorepo" }));
102+
await writeFile(
103+
tmp,
104+
"apps/api/requirements.txt",
105+
"django==5.0\nposthog==3.5.0\n",
106+
);
107+
expect(await service.detectPosthogInstallState(tmp)).toBe(
108+
"installed_no_init",
109+
);
110+
});
111+
112+
it("returns initialized when a Python entry point uses posthog", async () => {
113+
await writeFile(tmp, "requirements.txt", "posthog==3.5.0\n");
114+
await writeFile(
115+
tmp,
116+
"src/myapp/main.py",
117+
`import os\nimport posthog\n\nposthog.api_key = os.environ["POSTHOG_KEY"]\nposthog.host = "https://app.posthog.com"\nposthog.capture("user-id", "user_signed_up")\n`,
118+
);
119+
expect(await service.detectPosthogInstallState(tmp)).toBe("initialized");
120+
});
121+
122+
it("returns installed_no_init for a Ruby repo with a Gemfile declaring posthog", async () => {
123+
await writeFile(
124+
tmp,
125+
"Gemfile",
126+
`source "https://rubygems.org"\ngem "posthog-ruby"\n`,
127+
);
128+
expect(await service.detectPosthogInstallState(tmp)).toBe(
129+
"installed_no_init",
130+
);
131+
});
132+
133+
it("ignores files inside skip-paths like node_modules when scanning for init calls", async () => {
134+
await writeFile(
135+
tmp,
136+
"package.json",
137+
JSON.stringify({
138+
name: "test-app",
139+
dependencies: { "posthog-js": "^1.0.0" },
140+
}),
141+
);
142+
// A package vendor file containing `posthog.init` should NOT promote the
143+
// repo to "initialized" — only user code counts.
144+
await writeFile(
145+
tmp,
146+
"node_modules/some-other-pkg/dist/index.js",
147+
`posthog.init("phc_xxx");\n`,
148+
);
149+
expect(await service.detectPosthogInstallState(tmp)).toBe(
150+
"installed_no_init",
151+
);
152+
});
153+
154+
// Documents the v1 limitation: detection answers "is PostHog *used*?"
155+
// (any capture / flag / init-with-literal call). A file with init but
156+
// zero usage falls through to `installed_no_init`, which surfaces the
157+
// "Finish wiring" suggestion — appropriate guidance for that state.
158+
it("treats init-only-with-env-var (no capture) as installed_no_init", async () => {
159+
await writeFile(
160+
tmp,
161+
"package.json",
162+
JSON.stringify({ dependencies: { "posthog-js": "^1.0.0" } }),
163+
);
164+
await writeFile(
165+
tmp,
166+
"src/bootstrap.ts",
167+
`import posthog from "posthog-js";\nposthog.init(import.meta.env.VITE_POSTHOG_KEY);\n`,
168+
);
169+
expect(await service.detectPosthogInstallState(tmp)).toBe(
170+
"installed_no_init",
171+
);
172+
});
173+
174+
it("returns not_installed for empty/missing repoPath", async () => {
175+
expect(await service.detectPosthogInstallState("")).toBe("not_installed");
176+
});
177+
178+
it("returns not_installed when the directory isn't a git repo", async () => {
179+
const nonGitDir = await fs.mkdtemp(path.join(os.tmpdir(), "non-git-"));
180+
try {
181+
await writeFile(
182+
nonGitDir,
183+
"package.json",
184+
JSON.stringify({ dependencies: { "posthog-js": "^1.0.0" } }),
185+
);
186+
// listAllFiles throws on non-git dirs; detection bails to not_installed.
187+
expect(await service.detectPosthogInstallState(nonGitDir)).toBe(
188+
"not_installed",
189+
);
190+
} finally {
191+
await fs.rm(nonGitDir, { recursive: true, force: true });
192+
}
193+
});
194+
});

0 commit comments

Comments
 (0)