@revenium/claude-code-metering 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -28
- package/README.md +323 -139
- package/dist/cli/commands/backfill.d.ts +106 -1
- package/dist/cli/commands/backfill.d.ts.map +1 -1
- package/dist/cli/commands/backfill.js +359 -146
- package/dist/cli/commands/backfill.js.map +1 -1
- package/dist/cli/commands/setup.d.ts +2 -0
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +55 -49
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +23 -30
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/test.d.ts.map +1 -1
- package/dist/cli/commands/test.js +4 -3
- package/dist/cli/commands/test.js.map +1 -1
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +44 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/core/api/client.d.ts +17 -8
- package/dist/core/api/client.d.ts.map +1 -1
- package/dist/core/api/client.js +58 -49
- package/dist/core/api/client.js.map +1 -1
- package/dist/core/config/loader.d.ts +5 -13
- package/dist/core/config/loader.d.ts.map +1 -1
- package/dist/core/config/loader.js +70 -46
- package/dist/core/config/loader.js.map +1 -1
- package/dist/core/config/validator.d.ts +5 -1
- package/dist/core/config/validator.d.ts.map +1 -1
- package/dist/core/config/validator.js +37 -22
- package/dist/core/config/validator.js.map +1 -1
- package/dist/core/config/writer.d.ts +1 -1
- package/dist/core/config/writer.d.ts.map +1 -1
- package/dist/core/config/writer.js +82 -74
- package/dist/core/config/writer.js.map +1 -1
- package/dist/core/shell/detector.d.ts +8 -1
- package/dist/core/shell/detector.d.ts.map +1 -1
- package/dist/core/shell/detector.js +38 -24
- package/dist/core/shell/detector.js.map +1 -1
- package/dist/core/shell/profile-updater.d.ts +1 -1
- package/dist/core/shell/profile-updater.d.ts.map +1 -1
- package/dist/core/shell/profile-updater.js +40 -27
- package/dist/core/shell/profile-updater.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +30 -25
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +2 -2
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/hashing.d.ts +18 -0
- package/dist/utils/hashing.d.ts.map +1 -0
- package/dist/utils/hashing.js +27 -0
- package/dist/utils/hashing.js.map +1 -0
- package/package.json +6 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReveniumConfig, ValidationResult } from
|
|
1
|
+
import type { ReveniumConfig, ValidationResult } from "../../types/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Validates that an API key has the correct format.
|
|
4
4
|
* Valid format: hak_{tenant}_{random}
|
|
@@ -12,6 +12,10 @@ export declare function validateEmail(email: string): ValidationResult;
|
|
|
12
12
|
* Validates a subscription tier.
|
|
13
13
|
*/
|
|
14
14
|
export declare function validateSubscriptionTier(tier: string): ValidationResult;
|
|
15
|
+
/**
|
|
16
|
+
* Validates an endpoint URL and ensures it uses HTTPS.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateEndpointUrl(endpoint: string): ValidationResult;
|
|
15
19
|
/**
|
|
16
20
|
* Validates a complete Revenium configuration.
|
|
17
21
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/core/config/validator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/core/config/validator.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7E;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CA2B/D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAiB7D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAmBvE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAuBtE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,GAC9B,gBAAgB,CAqBlB"}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.validateApiKey = validateApiKey;
|
|
4
4
|
exports.validateEmail = validateEmail;
|
|
5
5
|
exports.validateSubscriptionTier = validateSubscriptionTier;
|
|
6
|
+
exports.validateEndpointUrl = validateEndpointUrl;
|
|
6
7
|
exports.validateConfig = validateConfig;
|
|
7
8
|
const constants_js_1 = require("../../utils/constants.js");
|
|
8
9
|
const VALID_TIERS = Object.keys(constants_js_1.SUBSCRIPTION_TIER_CONFIG);
|
|
@@ -12,21 +13,21 @@ const VALID_TIERS = Object.keys(constants_js_1.SUBSCRIPTION_TIER_CONFIG);
|
|
|
12
13
|
*/
|
|
13
14
|
function validateApiKey(apiKey) {
|
|
14
15
|
const errors = [];
|
|
15
|
-
if (!apiKey || apiKey.trim() ===
|
|
16
|
-
errors.push(
|
|
16
|
+
if (!apiKey || apiKey.trim() === "") {
|
|
17
|
+
errors.push("API key is required");
|
|
17
18
|
return { valid: false, errors };
|
|
18
19
|
}
|
|
19
20
|
if (!apiKey.startsWith(constants_js_1.API_KEY_PREFIX)) {
|
|
20
21
|
errors.push(`API key must start with "${constants_js_1.API_KEY_PREFIX}"`);
|
|
21
22
|
}
|
|
22
23
|
// Check for at least two underscores (hak_tenant_random)
|
|
23
|
-
const parts = apiKey.split(
|
|
24
|
+
const parts = apiKey.split("_");
|
|
24
25
|
if (parts.length < 3) {
|
|
25
|
-
errors.push(
|
|
26
|
+
errors.push("API key format should be: hak_{tenant}_{key}");
|
|
26
27
|
}
|
|
27
28
|
// Minimum length check
|
|
28
29
|
if (apiKey.length < 12) {
|
|
29
|
-
errors.push(
|
|
30
|
+
errors.push("API key appears too short");
|
|
30
31
|
}
|
|
31
32
|
return {
|
|
32
33
|
valid: errors.length === 0,
|
|
@@ -38,13 +39,13 @@ function validateApiKey(apiKey) {
|
|
|
38
39
|
*/
|
|
39
40
|
function validateEmail(email) {
|
|
40
41
|
const errors = [];
|
|
41
|
-
if (!email || email.trim() ===
|
|
42
|
+
if (!email || email.trim() === "") {
|
|
42
43
|
// Email is optional
|
|
43
44
|
return { valid: true, errors: [] };
|
|
44
45
|
}
|
|
45
46
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
46
47
|
if (!emailRegex.test(email)) {
|
|
47
|
-
errors.push(
|
|
48
|
+
errors.push("Invalid email format");
|
|
48
49
|
}
|
|
49
50
|
return {
|
|
50
51
|
valid: errors.length === 0,
|
|
@@ -56,13 +57,36 @@ function validateEmail(email) {
|
|
|
56
57
|
*/
|
|
57
58
|
function validateSubscriptionTier(tier) {
|
|
58
59
|
const errors = [];
|
|
59
|
-
if (!tier || tier.trim() ===
|
|
60
|
+
if (!tier || tier.trim() === "") {
|
|
60
61
|
// Tier is optional
|
|
61
62
|
return { valid: true, errors: [] };
|
|
62
63
|
}
|
|
63
64
|
const lowerTier = tier.toLowerCase();
|
|
64
65
|
if (!VALID_TIERS.includes(lowerTier)) {
|
|
65
|
-
errors.push(`Invalid subscription tier. Valid options: ${VALID_TIERS.join(
|
|
66
|
+
errors.push(`Invalid subscription tier. Valid options: ${VALID_TIERS.join(", ")}`);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
valid: errors.length === 0,
|
|
70
|
+
errors,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Validates an endpoint URL and ensures it uses HTTPS.
|
|
75
|
+
*/
|
|
76
|
+
function validateEndpointUrl(endpoint) {
|
|
77
|
+
const errors = [];
|
|
78
|
+
if (!endpoint || endpoint.trim() === "") {
|
|
79
|
+
errors.push("Endpoint URL is required");
|
|
80
|
+
return { valid: false, errors };
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const url = new URL(endpoint);
|
|
84
|
+
if (url.protocol !== "https:") {
|
|
85
|
+
errors.push("Insecure endpoint: HTTPS is required. Only HTTPS endpoints are allowed.");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
errors.push("Invalid endpoint URL format");
|
|
66
90
|
}
|
|
67
91
|
return {
|
|
68
92
|
valid: errors.length === 0,
|
|
@@ -74,25 +98,16 @@ function validateSubscriptionTier(tier) {
|
|
|
74
98
|
*/
|
|
75
99
|
function validateConfig(config) {
|
|
76
100
|
const allErrors = [];
|
|
77
|
-
const apiKeyResult = validateApiKey(config.apiKey ||
|
|
101
|
+
const apiKeyResult = validateApiKey(config.apiKey || "");
|
|
78
102
|
allErrors.push(...apiKeyResult.errors);
|
|
79
|
-
const emailResult = validateEmail(config.email ||
|
|
103
|
+
const emailResult = validateEmail(config.email || "");
|
|
80
104
|
allErrors.push(...emailResult.errors);
|
|
81
105
|
if (config.subscriptionTier) {
|
|
82
106
|
const tierResult = validateSubscriptionTier(config.subscriptionTier);
|
|
83
107
|
allErrors.push(...tierResult.errors);
|
|
84
108
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
try {
|
|
90
|
-
new URL(config.endpoint);
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
allErrors.push('Invalid endpoint URL format');
|
|
94
|
-
}
|
|
95
|
-
}
|
|
109
|
+
const endpointResult = validateEndpointUrl(config.endpoint || "");
|
|
110
|
+
allErrors.push(...endpointResult.errors);
|
|
96
111
|
return {
|
|
97
112
|
valid: allErrors.length === 0,
|
|
98
113
|
errors: allErrors,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/core/config/validator.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/core/config/validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReveniumConfig } from
|
|
1
|
+
import type { ReveniumConfig } from "../../types/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Writes the Revenium configuration to ~/.claude/revenium.env.
|
|
4
4
|
* Creates the directory if it doesn't exist and sets file permissions to 600.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../../src/core/config/writer.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../../src/core/config/writer.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA8K3D;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAezE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
|
|
@@ -7,6 +7,13 @@ const node_path_1 = require("node:path");
|
|
|
7
7
|
const promises_1 = require("node:fs/promises");
|
|
8
8
|
const constants_js_1 = require("../../utils/constants.js");
|
|
9
9
|
const loader_js_1 = require("./loader.js");
|
|
10
|
+
/**
|
|
11
|
+
* Escapes a value for use in shell export statements.
|
|
12
|
+
* Wraps the value in double quotes and escapes special characters.
|
|
13
|
+
*/
|
|
14
|
+
function escapeShellValue(value) {
|
|
15
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`")}"`;
|
|
16
|
+
}
|
|
10
17
|
/**
|
|
11
18
|
* Escapes a value for use in OTEL_RESOURCE_ATTRIBUTES.
|
|
12
19
|
* OTEL_RESOURCE_ATTRIBUTES uses comma as delimiter and equals as key-value separator.
|
|
@@ -15,10 +22,10 @@ const loader_js_1 = require("./loader.js");
|
|
|
15
22
|
*/
|
|
16
23
|
function escapeResourceAttributeValue(value) {
|
|
17
24
|
return value
|
|
18
|
-
.replace(/%/g,
|
|
19
|
-
.replace(/,/g,
|
|
20
|
-
.replace(/=/g,
|
|
21
|
-
.replace(/"/g,
|
|
25
|
+
.replace(/%/g, "%25") // Escape % first to avoid double-encoding
|
|
26
|
+
.replace(/,/g, "%2C")
|
|
27
|
+
.replace(/=/g, "%3D")
|
|
28
|
+
.replace(/"/g, "%22");
|
|
22
29
|
}
|
|
23
30
|
/**
|
|
24
31
|
* Gets the path to the Claude config directory.
|
|
@@ -32,93 +39,94 @@ function getClaudeConfigDir() {
|
|
|
32
39
|
function generateEnvContent(config) {
|
|
33
40
|
const fullEndpoint = (0, loader_js_1.getFullOtlpEndpoint)(config.endpoint);
|
|
34
41
|
const lines = [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
"# Revenium Claude Code Metering Configuration",
|
|
43
|
+
"# Generated by @revenium/claude-code-metering",
|
|
44
|
+
"#",
|
|
45
|
+
"# To load these variables, add to your shell profile:",
|
|
46
|
+
"# source ~/.claude/revenium.env",
|
|
47
|
+
"",
|
|
48
|
+
"# Enable Claude Code telemetry export",
|
|
42
49
|
`export ${constants_js_1.ENV_VARS.TELEMETRY_ENABLED}=1`,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
`export ${constants_js_1.ENV_VARS.OTLP_ENDPOINT}=${fullEndpoint}`,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
`export ${constants_js_1.ENV_VARS.OTLP_HEADERS}
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
"",
|
|
51
|
+
"# OTLP endpoint for Revenium metering",
|
|
52
|
+
`export ${constants_js_1.ENV_VARS.OTLP_ENDPOINT}=${escapeShellValue(fullEndpoint)}`,
|
|
53
|
+
"",
|
|
54
|
+
"# Authentication header with API key",
|
|
55
|
+
`export ${constants_js_1.ENV_VARS.OTLP_HEADERS}=${escapeShellValue(`x-api-key=${config.apiKey}`)}`,
|
|
56
|
+
"",
|
|
57
|
+
"# OTLP protocol (required for Claude Code)",
|
|
51
58
|
`export ${constants_js_1.ENV_VARS.OTLP_PROTOCOL}=http/json`,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
"",
|
|
60
|
+
"# Enable OTLP logs exporter (required to send telemetry)",
|
|
61
|
+
"export OTEL_LOGS_EXPORTER=otlp",
|
|
55
62
|
];
|
|
56
63
|
// Add optional fields
|
|
57
64
|
if (config.email) {
|
|
58
|
-
lines.push(
|
|
59
|
-
lines.push(
|
|
60
|
-
lines.push(`export ${constants_js_1.ENV_VARS.SUBSCRIBER_EMAIL}=${config.email}`);
|
|
65
|
+
lines.push("");
|
|
66
|
+
lines.push("# Subscriber email for attribution");
|
|
67
|
+
lines.push(`export ${constants_js_1.ENV_VARS.SUBSCRIBER_EMAIL}=${escapeShellValue(config.email)}`);
|
|
61
68
|
}
|
|
62
69
|
if (config.subscriptionTier) {
|
|
63
70
|
const tier = config.subscriptionTier;
|
|
64
71
|
const costMultiplier = config.costMultiplierOverride ?? (0, constants_js_1.getCostMultiplier)(tier);
|
|
65
72
|
const discountPercent = Math.round((1 - costMultiplier) * 100);
|
|
66
|
-
lines.push(
|
|
67
|
-
lines.push(
|
|
68
|
-
lines.push(`export ${constants_js_1.ENV_VARS.SUBSCRIPTION}=${config.subscriptionTier}`);
|
|
69
|
-
lines.push(
|
|
70
|
-
lines.push(
|
|
71
|
-
lines.push(
|
|
73
|
+
lines.push("");
|
|
74
|
+
lines.push("# Claude Code subscription tier");
|
|
75
|
+
lines.push(`export ${constants_js_1.ENV_VARS.SUBSCRIPTION}=${escapeShellValue(config.subscriptionTier)}`);
|
|
76
|
+
lines.push("");
|
|
77
|
+
lines.push("# Cost multiplier for subscription tier");
|
|
78
|
+
lines.push("# This adjusts Claude Code costs based on your subscription discount");
|
|
72
79
|
lines.push(`# ${tier}: ${discountPercent}% discount vs API rates`);
|
|
73
80
|
if (config.costMultiplierOverride !== undefined) {
|
|
74
|
-
lines.push(
|
|
81
|
+
lines.push("# (custom override applied)");
|
|
75
82
|
lines.push(`export ${constants_js_1.ENV_VARS.COST_MULTIPLIER}=${costMultiplier}`);
|
|
76
83
|
}
|
|
77
|
-
// Build OTEL_RESOURCE_ATTRIBUTES with cost_multiplier and optional
|
|
78
|
-
// Special characters (,=") in values are URL-encoded to ensure safe parsing
|
|
84
|
+
// Build OTEL_RESOURCE_ATTRIBUTES with cost_multiplier and optional organization.name/product.name.
|
|
85
|
+
// Special characters (,=") in values are URL-encoded to ensure safe parsing.
|
|
86
|
+
// Note: The backend ONLY reads organization.name and product.name from resourceAttributes,
|
|
87
|
+
// ignoring any auto-generated values in log record attributes from Claude Code.
|
|
79
88
|
const resourceAttrs = [`cost_multiplier=${costMultiplier}`];
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
// Support both new (organizationName) and old (organizationId) field names with fallback
|
|
90
|
+
const organizationValue = config.organizationName || config.organizationId;
|
|
91
|
+
if (organizationValue) {
|
|
92
|
+
resourceAttrs.push(`organization.name=${escapeResourceAttributeValue(organizationValue)}`);
|
|
82
93
|
}
|
|
83
|
-
|
|
84
|
-
|
|
94
|
+
// Support both new (productName) and old (productId) field names with fallback
|
|
95
|
+
const productValue = config.productName || config.productId;
|
|
96
|
+
if (productValue) {
|
|
97
|
+
resourceAttrs.push(`product.name=${escapeResourceAttributeValue(productValue)}`);
|
|
85
98
|
}
|
|
86
|
-
lines.push(`export OTEL_RESOURCE_ATTRIBUTES="${resourceAttrs.join(
|
|
87
|
-
}
|
|
88
|
-
// Add advanced configuration section (commented out by default)
|
|
89
|
-
lines.push('');
|
|
90
|
-
lines.push('# ─────────────────────────────────────────────────────────────────────────────');
|
|
91
|
-
lines.push('# Advanced Configuration (Optional)');
|
|
92
|
-
lines.push('# ─────────────────────────────────────────────────────────────────────────────');
|
|
93
|
-
lines.push('#');
|
|
94
|
-
lines.push('# IMPORTANT: If you enable organization/product attribution below, you must also');
|
|
95
|
-
lines.push('# update OTEL_RESOURCE_ATTRIBUTES above to include them. For example:');
|
|
96
|
-
lines.push('# export OTEL_RESOURCE_ATTRIBUTES="cost_multiplier=0.08,organization.id=my-org,product.id=my-product"');
|
|
97
|
-
lines.push('# Otherwise, the telemetry sent to Revenium will not include the attribution data.');
|
|
98
|
-
lines.push('# Run `npx @revenium/claude-code-metering setup` again to regenerate this file with');
|
|
99
|
-
lines.push('# the correct OTEL_RESOURCE_ATTRIBUTES if you want automatic configuration.');
|
|
100
|
-
lines.push('#');
|
|
101
|
-
lines.push('# Organization ID: Attribute Claude Code costs to a specific customer or company.');
|
|
102
|
-
lines.push('# Use this when you want to track AI development costs by client/organization.');
|
|
103
|
-
lines.push('# Example: Your consulting firm tracks costs per client project.');
|
|
104
|
-
if (config.organizationId) {
|
|
105
|
-
lines.push(`export ${constants_js_1.ENV_VARS.ORGANIZATION_ID}=${config.organizationId}`);
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
lines.push(`# export ${constants_js_1.ENV_VARS.ORGANIZATION_ID}=your-organization-id`);
|
|
109
|
-
}
|
|
110
|
-
lines.push('#');
|
|
111
|
-
lines.push('# Product ID: Attribute Claude Code costs to a specific product or project.');
|
|
112
|
-
lines.push('# Use this when you want to track AI development costs by internal product.');
|
|
113
|
-
lines.push('# Example: Separate AI costs for "mobile-app" vs "backend-api" development.');
|
|
114
|
-
if (config.productId) {
|
|
115
|
-
lines.push(`export ${constants_js_1.ENV_VARS.PRODUCT_ID}=${config.productId}`);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
lines.push(`# export ${constants_js_1.ENV_VARS.PRODUCT_ID}=your-product-id`);
|
|
99
|
+
lines.push(`export OTEL_RESOURCE_ATTRIBUTES="${resourceAttrs.join(",")}"`);
|
|
119
100
|
}
|
|
120
|
-
|
|
121
|
-
|
|
101
|
+
// Add advanced configuration section
|
|
102
|
+
lines.push("");
|
|
103
|
+
lines.push("# ─────────────────────────────────────────────────────────────────────────────");
|
|
104
|
+
lines.push("# Organization & Product Attribution (Optional)");
|
|
105
|
+
lines.push("# ─────────────────────────────────────────────────────────────────────────────");
|
|
106
|
+
lines.push("#");
|
|
107
|
+
lines.push("# To attribute Claude Code costs to a specific organization or product, you must");
|
|
108
|
+
lines.push("# add them to OTEL_RESOURCE_ATTRIBUTES above. The backend ONLY reads these values");
|
|
109
|
+
lines.push("# from OTEL_RESOURCE_ATTRIBUTES - standalone environment variables are NOT sent.");
|
|
110
|
+
lines.push("#");
|
|
111
|
+
lines.push("# HOW TO CONFIGURE:");
|
|
112
|
+
lines.push("# Edit the OTEL_RESOURCE_ATTRIBUTES line above to include organization.name and/or product.name:");
|
|
113
|
+
lines.push("#");
|
|
114
|
+
// Get current cost multiplier for the example
|
|
115
|
+
const exampleMultiplier = config.subscriptionTier && config.costMultiplierOverride === undefined
|
|
116
|
+
? (0, constants_js_1.getCostMultiplier)(config.subscriptionTier)
|
|
117
|
+
: (config.costMultiplierOverride ?? "0.08");
|
|
118
|
+
lines.push(`# OTEL_RESOURCE_ATTRIBUTES="cost_multiplier=${exampleMultiplier},organization.name=my-org,product.name=my-product"`);
|
|
119
|
+
lines.push("#");
|
|
120
|
+
lines.push("# ATTRIBUTE DESCRIPTIONS:");
|
|
121
|
+
lines.push("# organization.name - Attribute costs to a customer/company (e.g., client name, team)");
|
|
122
|
+
lines.push("# product.name - Attribute costs to a product/project (e.g., mobile-app, backend-api)");
|
|
123
|
+
lines.push("#");
|
|
124
|
+
lines.push("# After editing, restart your terminal or run: source ~/.claude/revenium.env");
|
|
125
|
+
lines.push("#");
|
|
126
|
+
lines.push("# Alternatively, re-run setup with --organization and --product flags:");
|
|
127
|
+
lines.push("# npx @revenium/claude-code-metering setup --organization my-org --product my-product");
|
|
128
|
+
lines.push("");
|
|
129
|
+
return lines.join("\n");
|
|
122
130
|
}
|
|
123
131
|
/**
|
|
124
132
|
* Writes the Revenium configuration to ~/.claude/revenium.env.
|
|
@@ -131,7 +139,7 @@ async function writeConfig(config) {
|
|
|
131
139
|
await (0, promises_1.mkdir)(configDir, { recursive: true });
|
|
132
140
|
// Generate and write the content
|
|
133
141
|
const content = generateEnvContent(config);
|
|
134
|
-
await (0, promises_1.writeFile)(configPath, content, { encoding:
|
|
142
|
+
await (0, promises_1.writeFile)(configPath, content, { encoding: "utf-8" });
|
|
135
143
|
// Set restrictive permissions (owner read/write only)
|
|
136
144
|
await (0, promises_1.chmod)(configPath, constants_js_1.CONFIG_FILE_MODE);
|
|
137
145
|
return configPath;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../../src/core/config/writer.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../../src/core/config/writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ShellType } from
|
|
1
|
+
import type { ShellType } from "../../types/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Detects the current shell type based on environment variables.
|
|
4
4
|
*/
|
|
@@ -7,8 +7,15 @@ export declare function detectShell(): ShellType;
|
|
|
7
7
|
* Gets the profile file path for a given shell type.
|
|
8
8
|
*/
|
|
9
9
|
export declare function getProfilePath(shellType: ShellType): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Validates that a config path contains only safe characters.
|
|
12
|
+
* Throws an error if the path contains potentially dangerous characters.
|
|
13
|
+
* Allows spaces since paths are properly quoted in shell commands.
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateConfigPath(path: string): void;
|
|
10
16
|
/**
|
|
11
17
|
* Generates the source command for a given shell type.
|
|
18
|
+
* Validates the config path before generating the command.
|
|
12
19
|
*/
|
|
13
20
|
export declare function getSourceCommand(shellType: ShellType, configPath: string): string;
|
|
14
21
|
//# sourceMappingURL=detector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../../src/core/shell/detector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;GAEG;AACH,wBAAgB,WAAW,IAAI,SAAS,CA0BvC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAiBlE;AAED
|
|
1
|
+
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../../src/core/shell/detector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;GAEG;AACH,wBAAgB,WAAW,IAAI,SAAS,CA0BvC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAiBlE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQrD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,CAWR"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.detectShell = detectShell;
|
|
4
4
|
exports.getProfilePath = getProfilePath;
|
|
5
|
+
exports.validateConfigPath = validateConfigPath;
|
|
5
6
|
exports.getSourceCommand = getSourceCommand;
|
|
6
7
|
const node_os_1 = require("node:os");
|
|
7
8
|
const node_path_1 = require("node:path");
|
|
@@ -10,28 +11,28 @@ const node_fs_1 = require("node:fs");
|
|
|
10
11
|
* Detects the current shell type based on environment variables.
|
|
11
12
|
*/
|
|
12
13
|
function detectShell() {
|
|
13
|
-
const shell = process.env.SHELL ||
|
|
14
|
-
if (shell.includes(
|
|
15
|
-
return
|
|
14
|
+
const shell = process.env.SHELL || "";
|
|
15
|
+
if (shell.includes("zsh")) {
|
|
16
|
+
return "zsh";
|
|
16
17
|
}
|
|
17
|
-
if (shell.includes(
|
|
18
|
-
return
|
|
18
|
+
if (shell.includes("fish")) {
|
|
19
|
+
return "fish";
|
|
19
20
|
}
|
|
20
|
-
if (shell.includes(
|
|
21
|
-
return
|
|
21
|
+
if (shell.includes("bash")) {
|
|
22
|
+
return "bash";
|
|
22
23
|
}
|
|
23
24
|
// Fallback: check for rc files
|
|
24
25
|
const home = (0, node_os_1.homedir)();
|
|
25
|
-
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(home,
|
|
26
|
-
return
|
|
26
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(home, ".zshrc"))) {
|
|
27
|
+
return "zsh";
|
|
27
28
|
}
|
|
28
|
-
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(home,
|
|
29
|
-
return
|
|
29
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(home, ".config", "fish", "config.fish"))) {
|
|
30
|
+
return "fish";
|
|
30
31
|
}
|
|
31
|
-
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(home,
|
|
32
|
-
return
|
|
32
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(home, ".bashrc"))) {
|
|
33
|
+
return "bash";
|
|
33
34
|
}
|
|
34
|
-
return
|
|
35
|
+
return "unknown";
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* Gets the profile file path for a given shell type.
|
|
@@ -39,28 +40,41 @@ function detectShell() {
|
|
|
39
40
|
function getProfilePath(shellType) {
|
|
40
41
|
const home = (0, node_os_1.homedir)();
|
|
41
42
|
switch (shellType) {
|
|
42
|
-
case
|
|
43
|
-
return (0, node_path_1.join)(home,
|
|
44
|
-
case
|
|
43
|
+
case "zsh":
|
|
44
|
+
return (0, node_path_1.join)(home, ".zshrc");
|
|
45
|
+
case "bash":
|
|
45
46
|
// Prefer .bashrc, fallback to .bash_profile
|
|
46
|
-
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(home,
|
|
47
|
-
return (0, node_path_1.join)(home,
|
|
47
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(home, ".bashrc"))) {
|
|
48
|
+
return (0, node_path_1.join)(home, ".bashrc");
|
|
48
49
|
}
|
|
49
|
-
return (0, node_path_1.join)(home,
|
|
50
|
-
case
|
|
51
|
-
return (0, node_path_1.join)(home,
|
|
50
|
+
return (0, node_path_1.join)(home, ".bash_profile");
|
|
51
|
+
case "fish":
|
|
52
|
+
return (0, node_path_1.join)(home, ".config", "fish", "config.fish");
|
|
52
53
|
default:
|
|
53
54
|
return null;
|
|
54
55
|
}
|
|
55
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Validates that a config path contains only safe characters.
|
|
59
|
+
* Throws an error if the path contains potentially dangerous characters.
|
|
60
|
+
* Allows spaces since paths are properly quoted in shell commands.
|
|
61
|
+
*/
|
|
62
|
+
function validateConfigPath(path) {
|
|
63
|
+
const unsafeCharsRegex = /[;|&$`"'\\<>(){}[\]!*?#\n\r\t]/;
|
|
64
|
+
if (unsafeCharsRegex.test(path)) {
|
|
65
|
+
throw new Error(`Invalid config path: contains unsafe characters. Path must not contain shell metacharacters like semicolons, pipes, backticks, or quotes.`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
56
68
|
/**
|
|
57
69
|
* Generates the source command for a given shell type.
|
|
70
|
+
* Validates the config path before generating the command.
|
|
58
71
|
*/
|
|
59
72
|
function getSourceCommand(shellType, configPath) {
|
|
73
|
+
validateConfigPath(configPath);
|
|
60
74
|
switch (shellType) {
|
|
61
|
-
case
|
|
75
|
+
case "fish":
|
|
62
76
|
// Fish uses a different syntax for sourcing env files
|
|
63
|
-
return `# Source Revenium Claude Code metering config\nif test -f ${configPath}\n export (cat ${configPath} | grep -v '^#' | xargs -L 1)\nend`;
|
|
77
|
+
return `# Source Revenium Claude Code metering config\nif test -f "${configPath}"\n export (cat "${configPath}" | grep -v '^#' | xargs -L 1)\nend`;
|
|
64
78
|
default:
|
|
65
79
|
// Bash and Zsh use the same syntax
|
|
66
80
|
return `# Source Revenium Claude Code metering config\nif [ -f "${configPath}" ]; then\n source "${configPath}"\nfi`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../../../src/core/shell/detector.ts"],"names":[],"mappings":";;AAQA,kCA0BC;AAKD,wCAiBC;
|
|
1
|
+
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../../../src/core/shell/detector.ts"],"names":[],"mappings":";;AAQA,kCA0BC;AAKD,wCAiBC;AAOD,gDAQC;AAMD,4CAcC;AA3FD,qCAAkC;AAClC,yCAAiC;AACjC,qCAAqC;AAGrC;;GAEG;AACH,SAAgB,WAAW;IACzB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;IAEtC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,+BAA+B;IAC/B,MAAM,IAAI,GAAG,IAAA,iBAAO,GAAE,CAAC;IACvB,IAAI,IAAA,oBAAU,EAAC,IAAA,gBAAI,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QACrC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,IAAA,oBAAU,EAAC,IAAA,gBAAI,EAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC;QAC7D,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,IAAA,oBAAU,EAAC,IAAA,gBAAI,EAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,SAAoB;IACjD,MAAM,IAAI,GAAG,IAAA,iBAAO,GAAE,CAAC;IAEvB,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,KAAK;YACR,OAAO,IAAA,gBAAI,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC9B,KAAK,MAAM;YACT,4CAA4C;YAC5C,IAAI,IAAA,oBAAU,EAAC,IAAA,gBAAI,EAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;gBACtC,OAAO,IAAA,gBAAI,EAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO,IAAA,gBAAI,EAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QACrC,KAAK,MAAM;YACT,OAAO,IAAA,gBAAI,EAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;QACtD;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,IAAY;IAC7C,MAAM,gBAAgB,GAAG,gCAAgC,CAAC;IAE1D,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,2IAA2I,CAC5I,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAC9B,SAAoB,EACpB,UAAkB;IAElB,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE/B,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,sDAAsD;YACtD,OAAO,8DAA8D,UAAU,uBAAuB,UAAU,qCAAqC,CAAC;QACxJ;YACE,mCAAmC;YACnC,OAAO,2DAA2D,UAAU,0BAA0B,UAAU,OAAO,CAAC;IAC5H,CAAC;AACH,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ShellType, ShellUpdateResult } from
|
|
1
|
+
import type { ShellType, ShellUpdateResult } from "../../types/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Updates the shell profile to source the Revenium configuration file.
|
|
4
4
|
* Returns details about the update operation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-updater.d.ts","sourceRoot":"","sources":["../../../src/core/shell/profile-updater.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"profile-updater.d.ts","sourceRoot":"","sources":["../../../src/core/shell/profile-updater.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AA+CzE;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAkErE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAMlE"}
|