diff --git a/src/cli/skills-cli.clawhub-install.e2e.test.ts b/src/cli/skills-cli.clawhub-install.e2e.test.ts index 71b13f34af13..db3b529a3704 100644 --- a/src/cli/skills-cli.clawhub-install.e2e.test.ts +++ b/src/cli/skills-cli.clawhub-install.e2e.test.ts @@ -108,20 +108,23 @@ describe("openclaw skills install ClawHub GitHub-backed E2E", () => { const registry = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-clawhub-cli-e2e-")); try { - const result = await spawnOpenClaw(["skills", "install", "aiq-deploy", "--global"], { - cwd: process.cwd(), - env: { - ...process.env, - OPENCLAW_STATE_DIR: stateDir, - OPENCLAW_CONFIG_PATH: path.join(stateDir, "openclaw.json"), - OPENCLAW_CLAWHUB_URL: registry, - OPENCLAW_CLAWHUB_TOKEN: "test-token", - OPENCLAW_CLAWHUB_GITHUB_CODELOAD_BASE_URL: registry, - CLAWHUB_DISABLE_TELEMETRY: "", - CLAWDHUB_DISABLE_TELEMETRY: "", - OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1", + const result = await spawnOpenClaw( + ["skills", "install", "@demo-owner/aiq-deploy", "--global"], + { + cwd: process.cwd(), + env: { + ...process.env, + OPENCLAW_STATE_DIR: stateDir, + OPENCLAW_CONFIG_PATH: path.join(stateDir, "openclaw.json"), + OPENCLAW_CLAWHUB_URL: registry, + OPENCLAW_CLAWHUB_TOKEN: "test-token", + OPENCLAW_CLAWHUB_GITHUB_CODELOAD_BASE_URL: registry, + CLAWHUB_DISABLE_TELEMETRY: "", + CLAWDHUB_DISABLE_TELEMETRY: "", + OPENCLAW_DISABLE_BUNDLED_PLUGINS: "1", + }, }, - }); + ); expect(result.status, result.stderr || result.stdout).toBe(0); await expect( @@ -137,11 +140,10 @@ describe("openclaw skills install ClawHub GitHub-backed E2E", () => { throw new Error(`Expected one install telemetry request, saw: ${requestLog.join(", ")}`); } expect(telemetryBodies[0]).toMatchObject({ - roots: [ - { - skills: [{ slug: "aiq-deploy", version: commit }], - }, - ], + event: "install", + slug: "aiq-deploy", + ownerHandle: "demo-owner", + version: commit, }); } finally { await new Promise((resolve) => { diff --git a/src/infra/clawhub.ts b/src/infra/clawhub.ts index 7cf9987b3932..bedb71c755c0 100644 --- a/src/infra/clawhub.ts +++ b/src/infra/clawhub.ts @@ -404,10 +404,6 @@ export type ClawHubDownloadResult = { cleanup: () => Promise; }; -export type ClawHubInstallTelemetrySkill = { - version?: string | null; -}; - type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise; type ClawHubRequestParams = { @@ -1619,8 +1615,9 @@ export async function downloadClawHubGitHubSkillArchive(params: { export async function reportClawHubSkillInstallTelemetry(params: { baseUrl?: string; token?: string; - root: string; - skills: Record; + slug: string; + ownerHandle?: string; + version?: string | null; timeoutMs?: number; fetchImpl?: FetchLike; }): Promise { @@ -1628,12 +1625,10 @@ export async function reportClawHubSkillInstallTelemetry(params: { if (!token || isClawHubTelemetryDisabled()) { return; } - const skills = Object.entries(params.skills) - .map(([slug, entry]) => ({ - slug, - version: entry.version ?? null, - })) - .filter((entry) => entry.slug.length > 0); + const slug = params.slug.trim(); + if (!slug) { + return; + } const { response, url, hasToken } = await clawhubRequest({ baseUrl: params.baseUrl, @@ -1643,13 +1638,10 @@ export async function reportClawHubSkillInstallTelemetry(params: { timeoutMs: params.timeoutMs, fetchImpl: params.fetchImpl, json: { - roots: [ - { - rootId: digestSha256Hex(path.resolve(params.root)), - label: formatTelemetryRootLabel(params.root), - skills, - }, - ], + event: "install", + slug, + ...(params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}), + version: params.version ?? undefined, }, }); if (!response.ok) { @@ -1665,20 +1657,6 @@ function isClawHubTelemetryDisabled(): boolean { return ["1", "true", "yes", "on"].includes(raw.trim().toLowerCase()); } -function formatTelemetryRootLabel(root: string): string { - const home = os.homedir(); - const absolute = path.resolve(root); - if (absolute === home) { - return "~"; - } - const normalized = absolute.replaceAll("\\", "/"); - const normalizedHome = home.replaceAll("\\", "/"); - const withinHome = normalized.startsWith(`${normalizedHome}/`); - const stripped = withinHome ? normalized.slice(normalizedHome.length + 1) : normalized; - const tail = stripped.split("/").filter(Boolean).slice(-2).join("/"); - return withinHome ? `~/${tail}` : tail || absolute; -} - /** Resolves the preferred latest package version from detail metadata. */ export function resolveLatestVersionFromPackage(detail: ClawHubPackageDetail): string | null { return detail.package?.latestVersion ?? detail.package?.tags?.latest ?? null; diff --git a/src/skills/lifecycle/clawhub.test.ts b/src/skills/lifecycle/clawhub.test.ts index a77ceb03f578..227b2e32eedb 100644 --- a/src/skills/lifecycle/clawhub.test.ts +++ b/src/skills/lifecycle/clawhub.test.ts @@ -318,23 +318,9 @@ describe("skills-clawhub", () => { expect(archiveCleanupMock).toHaveBeenCalledTimes(1); expect(reportClawHubSkillInstallTelemetryMock).toHaveBeenCalledWith({ baseUrl: undefined, - root: "/tmp/workspace", - skills: expect.objectContaining({ - agentreceipt: expect.objectContaining({ - version: "1.0.0", - installedAt: expect.any(Number), - registry: "https://clawhub.ai", - }), - }), + slug: "agentreceipt", + version: "1.0.0", }); - const telemetrySkills = reportClawHubSkillInstallTelemetryMock.mock.calls[0]?.[0]?.skills as - | Record> - | undefined; - expect(Object.keys(telemetrySkills?.agentreceipt ?? {}).toSorted()).toEqual([ - "installedAt", - "registry", - "version", - ]); }); it("bypasses ClawHub trust checks for official skill install resolutions", async () => { @@ -895,6 +881,12 @@ describe("skills-clawhub", () => { ownerHandle: "demo-owner", installedVersion: "1.0.0", }); + expect(reportClawHubSkillInstallTelemetryMock).toHaveBeenCalledWith({ + baseUrl: undefined, + slug: "weather", + ownerHandle: "demo-owner", + version: "1.0.0", + }); }); it("does not require acknowledgement for owner-qualified clean skills missing only cards", async () => { diff --git a/src/skills/lifecycle/clawhub.ts b/src/skills/lifecycle/clawhub.ts index 7931e43640ef..2b05c95a7eec 100644 --- a/src/skills/lifecycle/clawhub.ts +++ b/src/skills/lifecycle/clawhub.ts @@ -466,21 +466,6 @@ function buildDownloadedArtifactLock( }; } -function buildInstallTelemetrySkills( - skills: ClawHubSkillsLockfile["skills"], -): Record { - return Object.fromEntries( - Object.entries(skills).map(([slug, entry]) => [ - slug, - { - version: entry.version, - installedAt: entry.installedAt, - ...(entry.registry ? { registry: entry.registry } : {}), - }, - ]), - ); -} - function snapshotClawHubSkillVerification( verification: ClawHubSkillVerificationResponse, ): ClawHubSkillVerificationLock { @@ -1508,8 +1493,9 @@ async function performClawHubSkillInstall( await writeClawHubSkillsLockfile(params.workspaceDir, lock); await reportClawHubSkillInstallTelemetry({ baseUrl: params.baseUrl, - root: params.workspaceDir, - skills: buildInstallTelemetrySkills(lock.skills), + slug: params.slug, + ...(params.ownerHandle ? { ownerHandle: params.ownerHandle } : {}), + version, }).catch(() => undefined); return {