Remove telemetry fallback and root preflight hooks

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dottaandPaperclip committed 2026-04-09 10:53:28 -05:00
1 parent d03efda207
commit 5f80aa9aa0
4 files changed
+25 -182

No files matched your search

+4 -5
View File
@@ -3,7 +3,6 @@
"private": true,
"type": "module",
"scripts": {
"preflight:workspace-links": "pnpm exec tsx scripts/ensure-workspace-package-links.ts",
"dev": "pnpm --filter @paperclipai/server exec tsx ../scripts/dev-runner.ts watch",
"dev:watch": "pnpm --filter @paperclipai/server exec tsx ../scripts/dev-runner.ts watch",
"dev:once": "pnpm --filter @paperclipai/server exec tsx ../scripts/dev-runner.ts dev",
@@ -11,10 +10,10 @@
"dev:stop": "pnpm --filter @paperclipai/server exec tsx ../scripts/dev-service.ts stop",
"dev:server": "pnpm --filter @paperclipai/server dev",
"dev:ui": "pnpm --filter @paperclipai/ui dev",
"build": "pnpm run preflight:workspace-links && pnpm -r build",
"typecheck": "pnpm run preflight:workspace-links && pnpm -r typecheck",
"test": "pnpm run preflight:workspace-links && vitest",
"test:run": "pnpm run preflight:workspace-links && vitest run",
"build": "pnpm -r build",
"typecheck": "pnpm -r typecheck",
"test": "vitest",
"test:run": "vitest run",
"db:generate": "pnpm --filter @paperclipai/db generate",
"db:migrate": "pnpm --filter @paperclipai/db migrate",
"secrets:migrate-inline-env": "tsx scripts/migrate-inline-env-secrets.ts",
+21 -36
View File
@@ -6,10 +6,7 @@ import type {
TelemetryState,
} from "./types.js";
const DEFAULT_ENDPOINTS = [
"https://telemetry.paperclip.ing/ingest",
"https://rusqrrg391.execute-api.us-east-1.amazonaws.com/ingest",
] as const;
const DEFAULT_ENDPOINT = "https://telemetry.paperclip.ing/ingest";
const BATCH_SIZE = 50;
const SEND_TIMEOUT_MS = 5_000;
@@ -47,35 +44,28 @@ export class TelemetryClient {
const events = this.queue.splice(0);
const state = this.getState();
const endpoints = this.resolveEndpoints();
const endpoint = this.config.endpoint ?? DEFAULT_ENDPOINT;
const app = this.config.app ?? "paperclip";
const schemaVersion = this.config.schemaVersion ?? "1";
const body = JSON.stringify({
app,
schemaVersion,
installId: state.installId,
version: this.version,
events,
});
for (const endpoint of endpoints) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS);
try {
const response = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
signal: controller.signal,
});
if (response.ok) {
return;
}
} catch {
// Try the next built-in endpoint before dropping the batch.
} finally {
clearTimeout(timer);
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS);
try {
await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
app,
schemaVersion,
installId: state.installId,
version: this.version,
events,
}),
signal: controller.signal,
});
} catch {
// Fire-and-forget: silent failure, no retries
} finally {
clearTimeout(timer);
}
}
@@ -111,9 +101,4 @@ export class TelemetryClient {
}
return this.state;
}
private resolveEndpoints(): readonly string[] {
const configured = this.config.endpoint?.trim();
return configured ? [configured] : DEFAULT_ENDPOINTS;
}
}
-126
View File
@@ -1,126 +0,0 @@
#!/usr/bin/env -S node --import tsx
import fs from "node:fs/promises";
import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync } from "node:fs";
import path from "node:path";
import { repoRoot } from "./dev-service-profile.ts";
type WorkspaceLinkMismatch = {
workspaceDir: string;
packageName: string;
expectedPath: string;
actualPath: string | null;
};
function readJsonFile(filePath: string): Record<string, unknown> {
return JSON.parse(readFileSync(filePath, "utf8")) as Record<string, unknown>;
}
function discoverWorkspacePackagePaths(rootDir: string): Map<string, string> {
const packagePaths = new Map<string, string>();
const ignoredDirNames = new Set([".git", ".paperclip", "dist", "node_modules"]);
function visit(dirPath: string) {
const packageJsonPath = path.join(dirPath, "package.json");
if (existsSync(packageJsonPath)) {
const packageJson = readJsonFile(packageJsonPath);
if (typeof packageJson.name === "string" && packageJson.name.length > 0) {
packagePaths.set(packageJson.name, dirPath);
}
}
for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
if (ignoredDirNames.has(entry.name)) continue;
visit(path.join(dirPath, entry.name));
}
}
visit(path.join(rootDir, "packages"));
visit(path.join(rootDir, "server"));
visit(path.join(rootDir, "ui"));
visit(path.join(rootDir, "cli"));
return packagePaths;
}
function isLinkedGitWorktreeCheckout(rootDir: string) {
const gitMetadataPath = path.join(rootDir, ".git");
if (!existsSync(gitMetadataPath)) return false;
const stat = lstatSync(gitMetadataPath);
if (!stat.isFile()) return false;
return readFileSync(gitMetadataPath, "utf8").trimStart().startsWith("gitdir:");
}
if (!isLinkedGitWorktreeCheckout(repoRoot)) {
process.exit(0);
}
const workspacePackagePaths = discoverWorkspacePackagePaths(repoRoot);
const workspaceDirs = Array.from(
new Set(
Array.from(workspacePackagePaths.values())
.map((packagePath) => path.relative(repoRoot, packagePath))
.filter((workspaceDir) => workspaceDir.length > 0),
),
).sort();
function findWorkspaceLinkMismatches(workspaceDir: string): WorkspaceLinkMismatch[] {
const packageJson = readJsonFile(path.join(repoRoot, workspaceDir, "package.json"));
const dependencies = {
...(packageJson.dependencies as Record<string, unknown> | undefined),
...(packageJson.devDependencies as Record<string, unknown> | undefined),
};
const mismatches: WorkspaceLinkMismatch[] = [];
for (const [packageName, version] of Object.entries(dependencies)) {
if (typeof version !== "string" || !version.startsWith("workspace:")) continue;
const expectedPath = workspacePackagePaths.get(packageName);
if (!expectedPath) continue;
const linkPath = path.join(repoRoot, workspaceDir, "node_modules", ...packageName.split("/"));
const actualPath = existsSync(linkPath) ? path.resolve(realpathSync(linkPath)) : null;
if (actualPath === path.resolve(expectedPath)) continue;
mismatches.push({
workspaceDir,
packageName,
expectedPath: path.resolve(expectedPath),
actualPath,
});
}
return mismatches;
}
async function ensureWorkspaceLinksCurrent(workspaceDir: string) {
const mismatches = findWorkspaceLinkMismatches(workspaceDir);
if (mismatches.length === 0) return;
console.log(`[paperclip] detected stale workspace package links for ${workspaceDir}; relinking dependencies...`);
for (const mismatch of mismatches) {
console.log(
`[paperclip] ${mismatch.packageName}: ${mismatch.actualPath ?? "missing"} -> ${mismatch.expectedPath}`,
);
}
for (const mismatch of mismatches) {
const linkPath = path.join(repoRoot, mismatch.workspaceDir, "node_modules", ...mismatch.packageName.split("/"));
await fs.mkdir(path.dirname(linkPath), { recursive: true });
await fs.rm(linkPath, { recursive: true, force: true });
await fs.symlink(mismatch.expectedPath, linkPath);
}
const remainingMismatches = findWorkspaceLinkMismatches(workspaceDir);
if (remainingMismatches.length === 0) return;
throw new Error(
`Workspace relink did not repair all ${workspaceDir} package links: ${remainingMismatches.map((item) => item.packageName).join(", ")}`,
);
}
for (const workspaceDir of workspaceDirs) {
await ensureWorkspaceLinksCurrent(workspaceDir);
}
@@ -76,21 +76,6 @@ describe("TelemetryClient periodic flush", () => {
expect(fetch).not.toHaveBeenCalled();
});
it("falls back to the api gateway ingest url when the default hostname fails", async () => {
vi.mocked(fetch)
.mockRejectedValueOnce(new TypeError("getaddrinfo ENOTFOUND telemetry.paperclip.ing"))
.mockResolvedValueOnce({ ok: true });
const client = makeClient({ endpoint: undefined });
client.track("install.started");
await client.flush();
expect(fetch).toHaveBeenCalledTimes(2);
expect(vi.mocked(fetch).mock.calls[0]?.[0]).toBe("https://telemetry.paperclip.ing/ingest");
expect(vi.mocked(fetch).mock.calls[1]?.[0]).toBe("https://rusqrrg391.execute-api.us-east-1.amazonaws.com/ingest");
});
it("startPeriodicFlush is idempotent", () => {
const client = makeClient();
client.startPeriodicFlush(1000);