Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/cli/src/commands/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,12 +539,29 @@ class CLIEngine {
context.telemetryProperties[TelemetryProperty.Skill] = "true";
CliTelemetry.reporter?.addSharedProperty(TelemetryProperty.Skill, "true");
}
// Tag the invoking tool (e.g. wiqd) so wrapper-driven runs are distinguishable.
const caller = this.sanitizeCallerSource(process.env.ATK_CALLER);
if (caller) {
context.telemetryProperties[TelemetryProperty.CallerSource] = caller;
CliTelemetry.reporter?.addSharedProperty(TelemetryProperty.CallerSource, caller);
}
// context.telemetryProperties[TelemetryProperty.CorrelationId] =
// context.optionValues.correlationId;

return ok(undefined);
}

// ATK_CALLER is free-form env input; clamp to a short safe slug before it
// becomes a telemetry dimension to avoid PII leakage and high cardinality.
sanitizeCallerSource(raw?: string): string | undefined {
Comment thread
JimmyCalhoun marked this conversation as resolved.
Outdated
const slug = (raw ?? "")
.trim()
.toLowerCase()
.replace(/[^a-z0-9._-]/g, "")
.slice(0, 40);
return slug || undefined;
Comment thread
JimmyCalhoun marked this conversation as resolved.
Outdated
}

validateOptionsAndArguments(
command: CLIFoundCommand
): Result<
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/telemetry/cliTelemetryEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export enum TelemetryProperty {
CommandVerbose = "command-verbose",
CommandInteractive = "command-interactive",
BinName = "bin-name", // the input binary name: atk
CallerSource = "caller-source", // invoking tool/surface (e.g. "wiqd"), from ATK_CALLER
}

export enum TelemetrySuccess {
Expand Down
91 changes: 91 additions & 0 deletions packages/cli/tests/unit/engine.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -746,4 +746,95 @@ describe("CLI Engine", () => {
mockedEnvRestore();
});
});

describe("ATK_CALLER env var", () => {
it("sets CallerSource telemetry property from ATK_CALLER", async () => {
const mockedEnvRestore = mockedEnv({
ATK_CALLER: "wiqd",
});
const command: CLIFoundCommand = {
name: "test",
fullName: "test",
description: "test command",
};
const ctx: CLIContext = {
command: command,
optionValues: {},
globalOptionValues: {},
argumentValues: [],
telemetryProperties: {},
};
const result = engine.parseArgs(ctx, rootCommand, []);
assert.isTrue(result.isOk());
assert.equal(ctx.telemetryProperties[TelemetryProperty.CallerSource], "wiqd");
mockedEnvRestore();
});

it("does not set CallerSource when ATK_CALLER is not set", async () => {
const mockedEnvRestore = mockedEnv({
ATK_CALLER: undefined,
});
const command: CLIFoundCommand = {
name: "test",
fullName: "test",
description: "test command",
};
const ctx: CLIContext = {
command: command,
optionValues: {},
globalOptionValues: {},
argumentValues: [],
telemetryProperties: {},
};
const result = engine.parseArgs(ctx, rootCommand, []);
assert.isTrue(result.isOk());
assert.notProperty(ctx.telemetryProperties, TelemetryProperty.CallerSource);
mockedEnvRestore();
});

it("sanitizes ATK_CALLER to a lowercase slug capped at 40 chars", async () => {
const mockedEnvRestore = mockedEnv({
ATK_CALLER: " WIQD/CLI!! " + "x".repeat(40),
});
const command: CLIFoundCommand = {
name: "test",
fullName: "test",
description: "test command",
};
const ctx: CLIContext = {
command: command,
optionValues: {},
globalOptionValues: {},
argumentValues: [],
telemetryProperties: {},
};
const result = engine.parseArgs(ctx, rootCommand, []);
assert.isTrue(result.isOk());
const value = ctx.telemetryProperties[TelemetryProperty.CallerSource];
assert.equal(value, ("wiqdcli" + "x".repeat(40)).slice(0, 40));
mockedEnvRestore();
});

it("does not set CallerSource when ATK_CALLER has no safe characters", async () => {
const mockedEnvRestore = mockedEnv({
ATK_CALLER: "!!! @@@ ///",
});
const command: CLIFoundCommand = {
name: "test",
fullName: "test",
description: "test command",
};
const ctx: CLIContext = {
command: command,
optionValues: {},
globalOptionValues: {},
argumentValues: [],
telemetryProperties: {},
};
const result = engine.parseArgs(ctx, rootCommand, []);
assert.isTrue(result.isOk());
assert.notProperty(ctx.telemetryProperties, TelemetryProperty.CallerSource);
mockedEnvRestore();
});
});
});
Loading