feat(omo-claude): copy telemetry component sync script

Product-agnostic sync-telemetry-component.mjs (verbatim from omo-codex) +
test; generates the telemetry component's atomic-write/data-path/env-flags/
posthog-activity-state from src/telemetry. product-identity is authored
per-package, not synced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
YeonGyu-Kim
2026-05-29 13:01:15 +09:00
parent 234429737a
commit c39d16c378
3 changed files with 181 additions and 0 deletions
@@ -0,0 +1,111 @@
#!/usr/bin/env node
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
export const TELEMETRY_SYNC_FILES = [
"atomic-write.ts",
"data-path.ts",
"env-flags.ts",
"posthog-activity-state.ts",
];
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
const PACKAGE_ROOT = dirname(SCRIPT_DIR);
const DEFAULT_SOURCE_DIR = join(PACKAGE_ROOT, "src", "telemetry");
const DEFAULT_COMPONENT_DIR = join(PACKAGE_ROOT, "plugin", "components", "telemetry", "src");
export async function syncTelemetryComponent(options = {}) {
const sourceDir = resolve(options.sourceDir ?? DEFAULT_SOURCE_DIR);
const componentDir = resolve(options.componentDir ?? DEFAULT_COMPONENT_DIR);
const files = options.files ?? TELEMETRY_SYNC_FILES;
const check = options.check ?? false;
const changed = [];
for (const fileName of files) {
const sourcePath = join(sourceDir, fileName);
const componentPath = join(componentDir, fileName);
const sourceText = await readFile(sourcePath, "utf8");
const componentText = await readOptionalText(componentPath);
const nextText = toComponentSource(sourceText);
if (componentText === nextText) continue;
changed.push(fileName);
if (!check) {
await mkdir(dirname(componentPath), { recursive: true });
await writeFile(componentPath, nextText);
}
}
if (check && changed.length > 0) {
throw new Error(`telemetry component out of sync: ${changed.join(", ")}`);
}
return { checked: check, changed };
}
function toComponentSource(sourceText) {
return sourceText
.replaceAll(/\bprocess\.env\.([A-Z0-9_]+)/g, 'process.env["$1"]')
.replaceAll(/from "(\.\/[^"]+)"/g, 'from "$1.js"');
}
async function readOptionalText(path) {
try {
return await readFile(path, "utf8");
} catch (error) {
if (isNodeError(error) && error.code === "ENOENT") return null;
throw error;
}
}
function isNodeError(error) {
return error instanceof Error && "code" in error;
}
function parseArgs(args) {
const parsed = {
check: false,
sourceDir: DEFAULT_SOURCE_DIR,
componentDir: DEFAULT_COMPONENT_DIR,
};
for (let index = 0; index < args.length; index += 1) {
const arg = args[index];
if (arg === "--check") {
parsed.check = true;
continue;
}
if (arg === "--source-dir") {
const value = args[index + 1];
if (value === undefined) throw new Error("--source-dir requires a value");
parsed.sourceDir = value;
index += 1;
continue;
}
if (arg === "--component-dir") {
const value = args[index + 1];
if (value === undefined) throw new Error("--component-dir requires a value");
parsed.componentDir = value;
index += 1;
continue;
}
throw new Error(`unknown argument: ${arg}`);
}
return parsed;
}
async function main() {
const result = await syncTelemetryComponent(parseArgs(process.argv.slice(2)));
if (result.changed.length === 0) {
console.log("telemetry component in sync");
return;
}
console.log(`synced telemetry component: ${result.changed.join(", ")}`);
}
const invokedPath = process.argv[1] ? resolve(process.argv[1]) : "";
if (invokedPath === fileURLToPath(import.meta.url)) {
main().catch((error) => {
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
});
}
@@ -0,0 +1,70 @@
import assert from "node:assert/strict";
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
const SCRIPT_PATH = new URL("./sync-telemetry-component.mjs", import.meta.url);
async function makeTempDir() {
return mkdtemp(join(tmpdir(), "omo-claude-telemetry-sync-"));
}
async function runSync(args) {
const { syncTelemetryComponent } = await import(SCRIPT_PATH);
return syncTelemetryComponent(args);
}
test("#given stale telemetry component files #when sync runs #then pure package telemetry source rewrites the component copy", async () => {
// given
const root = await makeTempDir();
const sourceDir = join(root, "source");
const componentDir = join(root, "component");
await mkdir(sourceDir);
await mkdir(componentDir);
await writeFile(join(sourceDir, "atomic-write.ts"), "export const source = true\n", { flush: true });
await writeFile(join(componentDir, "atomic-write.ts"), "export const stale = true\n", { flush: true });
try {
// when
const result = await runSync({
sourceDir,
componentDir,
files: ["atomic-write.ts"],
check: false,
});
// then
assert.deepEqual(result, {
checked: false,
changed: ["atomic-write.ts"],
});
assert.equal(await readFile(join(componentDir, "atomic-write.ts"), "utf8"), "export const source = true\n");
} finally {
await rm(root, { recursive: true, force: true });
}
});
test("#given a missing pure telemetry source file #when sync runs #then it fails with the missing source path", async () => {
// given
const root = await makeTempDir();
const sourceDir = join(root, "source");
const componentDir = join(root, "component");
await mkdir(componentDir);
await writeFile(join(componentDir, "atomic-write.ts"), "export const stale = true\n", { flush: true });
try {
// when / then
await assert.rejects(
runSync({
sourceDir,
componentDir,
files: ["atomic-write.ts"],
check: false,
}),
(error) => error instanceof Error && error.message.includes(join(sourceDir, "atomic-write.ts")),
);
} finally {
await rm(root, { recursive: true, force: true });
}
});