diff --git a/lib/cmd-fns/dev/index.ts b/lib/cmd-fns/dev/index.ts index 16c8ae9e..63f9e79e 100644 --- a/lib/cmd-fns/dev/index.ts +++ b/lib/cmd-fns/dev/index.ts @@ -16,6 +16,8 @@ import { startEditEventWatcher } from "./start-edit-event-watcher" import { startExportRequestWatcher } from "./start-export-request-watcher" import { startFsWatcher } from "./start-fs-watcher" import { uploadExamplesFromDirectory } from "./upload-examples-from-directory" +import posthog from "lib/posthog" +import crypto from 'crypto' export const devCmd = async (ctx: AppContext, args: any) => { const params = z @@ -27,6 +29,16 @@ export const devCmd = async (ctx: AppContext, args: any) => { const { port } = params const { cwd } = ctx + const projectHash = crypto.createHash('md5').update(cwd).digest('hex') + + posthog.capture({ + distinctId: projectHash, + event: 'tsci_dev_started', + properties: { + port: port, + } + }) + // In the future we should automatically run "tsci init" if the directory // isn't properly initialized, for now we're just going to do a spot check const isInitialized = await checkIfInitialized(ctx) @@ -120,14 +132,32 @@ export const devCmd = async (ctx: AppContext, args: any) => { }) if (action === "open-in-browser") { open(serverUrl) + posthog.capture({ + distinctId: projectHash, + event: 'tsci_dev_open_browser' + }) } else if (action === "open-in-vs-code") { await $`code ${cwd}` + posthog.capture({ + distinctId: projectHash, + event: 'tsci_dev_open_vscode' + }) } else if (!action || action === "stop") { if (server.stop) server.stop() if (server.close) server.close() fs_watcher.stop() er_watcher.stop() ee_watcher.stop() + + posthog.capture({ + distinctId: projectHash, + event: 'tsci_dev_stopped' + }) + + if (posthog.shutdown) { + await posthog.shutdown() + } + break } } diff --git a/lib/posthog.ts b/lib/posthog.ts new file mode 100644 index 00000000..4a45d4fe --- /dev/null +++ b/lib/posthog.ts @@ -0,0 +1,24 @@ +import { PostHog } from 'posthog-node' + +const POSTHOG_API_KEY: string | undefined = process.env.POSTHOG_API_KEY + +let posthogInstance: PostHog | null = null + +if (POSTHOG_API_KEY) { + posthogInstance = new PostHog( + POSTHOG_API_KEY, + { host: 'https://us.i.posthog.com' } + ) +} + +const posthogProxy = new Proxy({} as PostHog, { + get(target, prop) { + if (posthogInstance) { + return Reflect.get(posthogInstance, prop) + } + // Return a no-op function for any method call if PostHog is not initialized + return () => {} + } +}) + +export default posthogProxy \ No newline at end of file diff --git a/package.json b/package.json index a71fd369..008c899a 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "node-persist": "^4.0.1", "open": "^10.1.0", "perfect-cli": "^1.0.20", + "posthog-node": "^4.0.1", "prompts": "^2.4.2", "react": "^18.2.0", "semver": "^7.6.0", @@ -70,16 +71,16 @@ "peerDependencies": { "@tscircuit/builder": "*", "@tscircuit/layout": "*", + "@tscircuit/manual-edit-events": "*", "@tscircuit/react-fiber": "*", - "@tscircuit/soup-util": "*", - "@tscircuit/manual-edit-events": "*" + "@tscircuit/soup-util": "*" }, "devDependencies": { "@tscircuit/builder": "*", "@tscircuit/layout": "^0.0.24", + "@tscircuit/manual-edit-events": "^0.0.4", "@tscircuit/react-fiber": "*", "@tscircuit/soup-util": "^0.0.11", - "@tscircuit/manual-edit-events": "^0.0.4", "@types/archiver": "^6.0.2", "@types/bun": "^1.0.8", "@types/chokidar": "^2.1.3", diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 00000000..dfd48c4e --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + env: { + POSTHOG_API_KEY: JSON.stringify(process.env.POSTHOG_API_KEY || ''), + }, +}) \ No newline at end of file