@prism-d1/cli 1.0.60 → 1.0.64

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.
@@ -0,0 +1,302 @@
1
+ /**
2
+ * PRISM D1 — OTEL Collector construct (opt-in).
3
+ *
4
+ * Server side of `codeburn sync`: an HTTP API serving the codeburn discovery
5
+ * doc and an OTLP/HTTP traces endpoint, authorized by OIDC JWT.
6
+ *
7
+ * Enable with: cdk deploy -c enableOtelCollector=true
8
+ * BYO IdP with: -c otelIssuer=... -c otelClientId=... [-c otelIdentityClaim=email]
9
+ *
10
+ * Default mode provisions a Cognito User Pool (admin-create-user only,
11
+ * username = email, PKCE public client) with loopback callback URLs matching
12
+ * codeburn's fixed callback ports (19876-19878). BYO mode skips Cognito and
13
+ * wires the JWT authorizer to the external issuer.
14
+ *
15
+ * Data flow: codeburn sync push → API Gateway (JWT authorizer) → receiver
16
+ * Lambda → S3 raw OTLP archive (external contract) + DynamoDB span/aggregate
17
+ * rows (PRISM dashboards).
18
+ */
19
+
20
+ import * as cdk from 'aws-cdk-lib';
21
+ import * as lambda from 'aws-cdk-lib/aws-lambda';
22
+ import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
23
+ import * as cognito from 'aws-cdk-lib/aws-cognito';
24
+ import * as s3 from 'aws-cdk-lib/aws-s3';
25
+ import * as kms from 'aws-cdk-lib/aws-kms';
26
+ import * as logs from 'aws-cdk-lib/aws-logs';
27
+ import * as apigwv2 from 'aws-cdk-lib/aws-apigatewayv2';
28
+ import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
29
+ import { HttpJwtAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
30
+ import * as path from 'path';
31
+ import { Construct } from 'constructs';
32
+ import { NagSuppressions } from 'cdk-nag';
33
+
34
+ /** codeburn sync fixed loopback callback ports (see codeburn src/sync/auth.ts). */
35
+ const CODEBURN_CALLBACK_PORTS = [19876, 19877, 19878];
36
+
37
+ export interface OtelCollectorConstructProps {
38
+ /** The prism-d1-ai-usage table — receiver writes SPAN# and OTEL#DAY# items. */
39
+ aiUsageTable: dynamodb.Table;
40
+ /** KMS key for the S3 archive bucket. */
41
+ kmsKey: kms.IKey;
42
+ /** VPC props spread applied to the receiver Lambda (matches stack pattern). */
43
+ lambdaVpcProps?: { vpc?: cdk.aws_ec2.IVpc; securityGroups?: cdk.aws_ec2.ISecurityGroup[] };
44
+ /** BYO IdP: OIDC issuer URL. When set, Cognito is NOT provisioned. */
45
+ externalIssuer?: string;
46
+ /** BYO IdP: OAuth client ID registered for codeburn (public client + PKCE). */
47
+ externalClientId?: string;
48
+ /** JWT claim used as the user identity key (default: username for Cognito, sub for BYO). */
49
+ identityClaim?: string;
50
+ }
51
+
52
+ export class OtelCollectorConstruct extends Construct {
53
+ public readonly httpApi: apigwv2.HttpApi;
54
+ public readonly archiveBucket: s3.Bucket;
55
+ public readonly userPool?: cognito.UserPool;
56
+ public readonly userPoolClient?: cognito.UserPoolClient;
57
+
58
+ constructor(scope: Construct, id: string, props: OtelCollectorConstructProps) {
59
+ super(scope, id);
60
+
61
+ const byoIdp = !!props.externalIssuer;
62
+ if (byoIdp && !props.externalClientId) {
63
+ throw new Error('otelClientId is required when otelIssuer is set');
64
+ }
65
+
66
+ // -------------------------------------------------------
67
+ // OIDC provider — Cognito (default) or BYO issuer
68
+ // -------------------------------------------------------
69
+ let issuer: string;
70
+ let clientId: string;
71
+ let identityClaim: string;
72
+
73
+ if (byoIdp) {
74
+ issuer = props.externalIssuer!.replace(/\/$/, '');
75
+ clientId = props.externalClientId!;
76
+ identityClaim = props.identityClaim ?? 'sub';
77
+ } else {
78
+ // Admin-create-user only (no self-signup); username = email so DDB keys
79
+ // line up with the USER#<email> key convention. We do NOT use
80
+ // signInAliases: { email: true } because that generates a random UUID
81
+ // as the internal username — the access token's `username` claim would
82
+ // carry the UUID, not the email. Instead, admins create users with
83
+ // --username dev@example.com and the `username` claim = the email.
84
+ this.userPool = new cognito.UserPool(this, 'UserPool', {
85
+ userPoolName: 'prism-d1-otel-users',
86
+ selfSignUpEnabled: false,
87
+ passwordPolicy: {
88
+ minLength: 12,
89
+ requireLowercase: true,
90
+ requireUppercase: true,
91
+ requireDigits: true,
92
+ requireSymbols: true,
93
+ },
94
+ advancedSecurityMode: cognito.AdvancedSecurityMode.ENFORCED,
95
+ featurePlan: cognito.FeaturePlan.PLUS,
96
+ removalPolicy: cdk.RemovalPolicy.RETAIN,
97
+ });
98
+
99
+ // Hosted UI domain — prefix must be globally unique per region.
100
+ this.userPool.addDomain('Domain', {
101
+ cognitoDomain: { domainPrefix: `prism-d1-otel-${cdk.Aws.ACCOUNT_ID}` },
102
+ });
103
+
104
+ // Public client (no secret) — codeburn does Authorization Code + PKCE
105
+ // with loopback redirects. Cognito allows http:// for localhost, 127.0.0.1,
106
+ // and [::1] (per CreateUserPoolClient API docs). codeburn uses 127.0.0.1.
107
+ const callbackUrls = CODEBURN_CALLBACK_PORTS.map(
108
+ (port) => `http://127.0.0.1:${port}/callback`,
109
+ );
110
+ this.userPoolClient = this.userPool.addClient('CodeburnClient', {
111
+ userPoolClientName: 'codeburn-sync',
112
+ generateSecret: false,
113
+ authFlows: { userSrp: true },
114
+ oAuth: {
115
+ flows: { authorizationCodeGrant: true },
116
+ scopes: [cognito.OAuthScope.OPENID, cognito.OAuthScope.EMAIL, cognito.OAuthScope.PROFILE],
117
+ callbackUrls,
118
+ },
119
+ preventUserExistenceErrors: true,
120
+ accessTokenValidity: cdk.Duration.hours(1),
121
+ refreshTokenValidity: cdk.Duration.days(30),
122
+ });
123
+
124
+ issuer = `https://cognito-idp.${cdk.Aws.REGION}.amazonaws.com/${this.userPool.userPoolId}`;
125
+ clientId = this.userPoolClient.userPoolClientId;
126
+ // Cognito ACCESS tokens carry `username` (no email claim) — with
127
+ // email-alias sign-in and admin-created users, username == email.
128
+ identityClaim = props.identityClaim ?? 'username';
129
+ }
130
+
131
+ // -------------------------------------------------------
132
+ // S3 archive — raw OTLP batches, the external consumption contract
133
+ // -------------------------------------------------------
134
+ this.archiveBucket = new s3.Bucket(this, 'ArchiveBucket', {
135
+ encryption: s3.BucketEncryption.KMS,
136
+ encryptionKey: props.kmsKey,
137
+ blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
138
+ enforceSSL: true,
139
+ removalPolicy: cdk.RemovalPolicy.RETAIN,
140
+ lifecycleRules: [{
141
+ id: 'expire-raw-otlp',
142
+ expiration: cdk.Duration.days(365),
143
+ }],
144
+ });
145
+
146
+ // -------------------------------------------------------
147
+ // Receiver Lambda — discovery doc + OTLP traces ingestion
148
+ // -------------------------------------------------------
149
+ const receiver = new lambda.Function(this, 'Receiver', {
150
+ functionName: 'prism-d1-otel-receiver',
151
+ runtime: lambda.Runtime.NODEJS_22_X,
152
+ handler: 'otel-receiver.handler',
153
+ ...(props.lambdaVpcProps ?? {}),
154
+ code: lambda.Code.fromAsset(path.join(__dirname, '..', 'lambda'), {
155
+ bundling: {
156
+ image: lambda.Runtime.NODEJS_22_X.bundlingImage,
157
+ command: [
158
+ 'bash', '-c',
159
+ [
160
+ 'npm init -y > /dev/null 2>&1',
161
+ 'npm install --save @aws-sdk/client-dynamodb @aws-sdk/client-s3 esbuild > /dev/null 2>&1',
162
+ 'npx esbuild otel-receiver.ts --bundle --platform=node --target=node22 --outfile=/asset-output/otel-receiver.js --external:@aws-sdk/*',
163
+ ].join(' && '),
164
+ ],
165
+ local: {
166
+ tryBundle(outputDir: string): boolean {
167
+ try {
168
+ const { execSync } = require('child_process');
169
+ execSync(
170
+ `npx esbuild ${path.join(__dirname, '..', 'lambda', 'otel-receiver.ts')} --bundle --platform=node --target=node22 --outfile=${path.join(outputDir, 'otel-receiver.js')} --external:@aws-sdk/*`,
171
+ { stdio: 'pipe' },
172
+ );
173
+ return true;
174
+ } catch {
175
+ return false;
176
+ }
177
+ },
178
+ },
179
+ },
180
+ }),
181
+ timeout: cdk.Duration.seconds(60),
182
+ memorySize: 512,
183
+ environment: {
184
+ AI_USAGE_TABLE: props.aiUsageTable.tableName,
185
+ ARCHIVE_BUCKET: this.archiveBucket.bucketName,
186
+ IDENTITY_CLAIM: identityClaim,
187
+ OIDC_ISSUER: issuer,
188
+ OIDC_CLIENT_ID: clientId,
189
+ MAX_BATCH_SIZE: '1000',
190
+ SPAN_TTL_DAYS: '90',
191
+ },
192
+ logRetention: logs.RetentionDays.ONE_MONTH,
193
+ description: 'Receives codeburn sync OTLP traces: S3 archive + per-user span/daily-aggregate rows',
194
+ });
195
+
196
+ props.aiUsageTable.grantReadWriteData(receiver);
197
+ this.archiveBucket.grantPut(receiver);
198
+ props.kmsKey.grantEncryptDecrypt(receiver);
199
+
200
+ // -------------------------------------------------------
201
+ // HTTP API — discovery (public) + traces (JWT)
202
+ // -------------------------------------------------------
203
+ this.httpApi = new apigwv2.HttpApi(this, 'Api', {
204
+ apiName: 'prism-d1-otel-collector',
205
+ description: 'PRISM D1 OTEL collector for codeburn sync',
206
+ });
207
+
208
+ const integration = new HttpLambdaIntegration('ReceiverIntegration', receiver);
209
+
210
+ // HTTP API JWT authorizers validate the `aud` claim, or `client_id` for
211
+ // Cognito access tokens — both paths work with jwtAudience = [clientId].
212
+ const authorizer = new HttpJwtAuthorizer('OtelJwtAuthorizer', issuer, {
213
+ jwtAudience: [clientId],
214
+ });
215
+
216
+ // Discovery doc — unauthenticated by design (it only exposes issuer +
217
+ // client_id, both public values in an OAuth public-client flow).
218
+ this.httpApi.addRoutes({
219
+ path: '/.well-known/codeburn-export.json',
220
+ methods: [apigwv2.HttpMethod.GET],
221
+ integration,
222
+ });
223
+
224
+ this.httpApi.addRoutes({
225
+ path: '/v1/traces',
226
+ methods: [apigwv2.HttpMethod.POST],
227
+ integration,
228
+ authorizer,
229
+ });
230
+
231
+ // Access logging on the default stage (AwsSolutions-APIG1).
232
+ const accessLogGroup = new logs.LogGroup(this, 'ApiAccessLogs', {
233
+ logGroupName: '/aws/apigateway/prism-d1-otel-collector',
234
+ retention: logs.RetentionDays.ONE_MONTH,
235
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
236
+ });
237
+ const defaultStage = this.httpApi.defaultStage!.node.defaultChild as apigwv2.CfnStage;
238
+ defaultStage.accessLogSettings = {
239
+ destinationArn: accessLogGroup.logGroupArn,
240
+ format: JSON.stringify({
241
+ requestId: '$context.requestId',
242
+ ip: '$context.identity.sourceIp',
243
+ requestTime: '$context.requestTime',
244
+ httpMethod: '$context.httpMethod',
245
+ path: '$context.path',
246
+ status: '$context.status',
247
+ responseLength: '$context.responseLength',
248
+ userAgent: '$context.identity.userAgent',
249
+ }),
250
+ };
251
+
252
+ // -------------------------------------------------------
253
+ // cdk-nag suppressions (with evidence)
254
+ // -------------------------------------------------------
255
+ NagSuppressions.addResourceSuppressions(receiver, [
256
+ {
257
+ id: 'AwsSolutions-IAM5',
258
+ reason: 'grantPut generates s3:Abort* + bucket/* wildcard scoped to the single OTLP archive bucket',
259
+ appliesTo: ['Action::s3:Abort*', { regex: '/^Resource::<OtelCollectorArchiveBucket.*\\.Arn>\\/\\*$/g' }],
260
+ },
261
+ {
262
+ id: 'AwsSolutions-IAM5',
263
+ reason: 'grantReadWriteData generates a wildcard for the AI-usage table GSI index ARNs',
264
+ appliesTo: [{ regex: '/^Resource::<AiUsageTable.*\\.Arn>\\/index\\/\\*$/g' }],
265
+ },
266
+ ], true);
267
+ NagSuppressions.addResourceSuppressions(this.httpApi, [
268
+ {
269
+ id: 'AwsSolutions-APIG4',
270
+ reason: 'The codeburn discovery doc route is unauthenticated by design — it only publishes the OIDC issuer and public client_id (both non-secret in an OAuth public-client PKCE flow). The traces route is JWT-authorized.',
271
+ },
272
+ ], true);
273
+ NagSuppressions.addResourceSuppressions(this.archiveBucket, [
274
+ {
275
+ id: 'AwsSolutions-S1',
276
+ reason: 'Telemetry archive bucket for a sample project; access is limited to the receiver Lambda (put) and account principals. Enable S3 server access logging or CloudTrail data events for production hardening.',
277
+ },
278
+ ]);
279
+ if (this.userPool) {
280
+ NagSuppressions.addResourceSuppressions(this.userPool, [
281
+ {
282
+ id: 'AwsSolutions-COG2',
283
+ reason: 'MFA is not required for the codeburn telemetry-push user pool: users are admin-created, the client is scoped to telemetry writes only, and MFA on a CLI OIDC device flow adds friction disproportionate to the data sensitivity. Enable MFA for production hardening.',
284
+ },
285
+ ]);
286
+ }
287
+
288
+ // -------------------------------------------------------
289
+ // Outputs — everything needed to run `codeburn sync setup <url>`
290
+ // -------------------------------------------------------
291
+ new cdk.CfnOutput(this, 'OtelCollectorUrl', {
292
+ value: this.httpApi.apiEndpoint,
293
+ description: 'Run: codeburn sync setup <this URL>',
294
+ });
295
+ if (this.userPool) {
296
+ new cdk.CfnOutput(this, 'OtelUserPoolId', {
297
+ value: this.userPool.userPoolId,
298
+ description: 'Create users: aws cognito-idp admin-create-user --user-pool-id <this> --username <email>',
299
+ });
300
+ }
301
+ }
302
+ }
@@ -46,6 +46,11 @@ export class PrismVpcConstruct extends Construct {
46
46
  service: ec2.GatewayVpcEndpointAwsService.DYNAMODB,
47
47
  });
48
48
 
49
+ // --- Gateway endpoint: S3 (free; used by the OTEL receiver archive writes) ---
50
+ this.vpc.addGatewayEndpoint('S3Endpoint', {
51
+ service: ec2.GatewayVpcEndpointAwsService.S3,
52
+ });
53
+
49
54
  // --- Interface endpoints ---
50
55
  const interfaceEndpoints = [
51
56
  { id: 'EventBridgeEndpoint', service: ec2.InterfaceVpcEndpointAwsService.EVENTBRIDGE },
@@ -151,6 +151,7 @@ const EVENTS_TABLE = process.env.EVENTS_TABLE!;
151
151
  const METADATA_TABLE = process.env.METADATA_TABLE!;
152
152
  const METRIC_NAMESPACE = process.env.METRIC_NAMESPACE ?? 'PRISM/D1/Velocity';
153
153
 
154
+
154
155
  // ---- Handler ----
155
156
 
156
157
  export async function handler(event: EventBridgeEvent): Promise<void> {
@@ -0,0 +1,369 @@
1
+ /**
2
+ * PRISM D1 — OTLP receiver for codeburn sync.
3
+ *
4
+ * Serves two routes on the OTEL collector HTTP API:
5
+ * GET /.well-known/codeburn-export.json — discovery doc (no auth)
6
+ * POST /v1/traces — OTLP/HTTP JSON traces (JWT-authorized)
7
+ *
8
+ * Trace flow: validate (JWT done by API Gateway) → archive raw OTLP batch to S3
9
+ * (external contract — standard OTLP JSON, Athena/replay-friendly) → per-span
10
+ * conditional DynamoDB write (dedup gate) → daily aggregate ADD (only for
11
+ * newly-seen spans, bucketed by SPAN date, not arrival date).
12
+ *
13
+ * Item shapes in the AI-usage table:
14
+ * pk=USER#<identity> sk=SPAN#<timestamp>#<spanId> raw span, TTL 90d
15
+ * pk=USER#<identity> sk=OTEL#DAY#<yyyy-mm-dd>#<tool> daily aggregate
16
+ */
17
+
18
+ import {
19
+ DynamoDBClient,
20
+ PutItemCommand,
21
+ UpdateItemCommand,
22
+ ConditionalCheckFailedException,
23
+ } from '@aws-sdk/client-dynamodb';
24
+ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
25
+ import { randomUUID } from 'crypto';
26
+
27
+ const dynamoClient = new DynamoDBClient({});
28
+ const s3Client = new S3Client({});
29
+
30
+ const AI_USAGE_TABLE = process.env.AI_USAGE_TABLE || 'prism-d1-ai-usage';
31
+ const ARCHIVE_BUCKET = process.env.ARCHIVE_BUCKET || '';
32
+ const IDENTITY_CLAIM = process.env.IDENTITY_CLAIM || 'username';
33
+ const OIDC_ISSUER = process.env.OIDC_ISSUER || '';
34
+ const OIDC_CLIENT_ID = process.env.OIDC_CLIENT_ID || '';
35
+ const MAX_BATCH_SIZE = Number(process.env.MAX_BATCH_SIZE || '1000');
36
+ const SPAN_TTL_DAYS = Number(process.env.SPAN_TTL_DAYS || '90');
37
+
38
+ /** codeburn provider names → PRISM tool names. */
39
+ const PROVIDER_TO_TOOL: Record<string, string> = {
40
+ claude: 'claude-code',
41
+ kiro: 'kiro',
42
+ cursor: 'cursor',
43
+ codex: 'codex',
44
+ copilot: 'copilot',
45
+ };
46
+
47
+ // ---- Types (API Gateway HTTP API v2 proxy + OTLP JSON) ----
48
+
49
+ interface HttpApiEvent {
50
+ rawPath: string;
51
+ requestContext: {
52
+ http: { method: string; path: string };
53
+ authorizer?: { jwt?: { claims?: Record<string, unknown> } };
54
+ };
55
+ body?: string;
56
+ isBase64Encoded?: boolean;
57
+ }
58
+
59
+ interface HttpApiResponse {
60
+ statusCode: number;
61
+ headers: Record<string, string>;
62
+ body: string;
63
+ }
64
+
65
+ type OtlpValue =
66
+ | { stringValue?: string; intValue?: string | number; doubleValue?: number; boolValue?: boolean };
67
+
68
+ interface OtlpAttribute {
69
+ key: string;
70
+ value: OtlpValue;
71
+ }
72
+
73
+ interface OtlpSpan {
74
+ traceId?: string;
75
+ spanId?: string;
76
+ name?: string;
77
+ startTimeUnixNano?: string;
78
+ attributes?: OtlpAttribute[];
79
+ }
80
+
81
+ interface OtlpPayload {
82
+ resourceSpans?: Array<{
83
+ resource?: { attributes?: OtlpAttribute[] };
84
+ scopeSpans?: Array<{ spans?: OtlpSpan[] }>;
85
+ }>;
86
+ }
87
+
88
+ interface ParsedSpan {
89
+ spanId: string;
90
+ timestamp: string; // ISO from span start time
91
+ tool: string;
92
+ model: string;
93
+ inputTokens: number;
94
+ outputTokens: number;
95
+ costUsd: number;
96
+ project: string;
97
+ costEstimated: boolean;
98
+ deviceId: string;
99
+ }
100
+
101
+ // ---- Helpers ----
102
+
103
+ function jsonResponse(statusCode: number, body: unknown): HttpApiResponse {
104
+ return {
105
+ statusCode,
106
+ headers: { 'Content-Type': 'application/json' },
107
+ body: JSON.stringify(body),
108
+ };
109
+ }
110
+
111
+ function attrMap(attrs: OtlpAttribute[] | undefined): Map<string, OtlpValue> {
112
+ const m = new Map<string, OtlpValue>();
113
+ for (const a of attrs ?? []) {
114
+ if (a && typeof a.key === 'string' && a.value) m.set(a.key, a.value);
115
+ }
116
+ return m;
117
+ }
118
+
119
+ function str(v: OtlpValue | undefined): string {
120
+ return typeof v?.stringValue === 'string' ? v.stringValue : '';
121
+ }
122
+
123
+ function num(v: OtlpValue | undefined): number {
124
+ if (v?.intValue !== undefined) {
125
+ const n = Number(v.intValue);
126
+ return Number.isFinite(n) ? n : 0;
127
+ }
128
+ if (typeof v?.doubleValue === 'number' && Number.isFinite(v.doubleValue)) return v.doubleValue;
129
+ return 0;
130
+ }
131
+
132
+ function bool(v: OtlpValue | undefined): boolean {
133
+ return v?.boolValue === true;
134
+ }
135
+
136
+ /** Convert OTLP unix-nano string to ISO timestamp; empty string if invalid. */
137
+ function nanoToIso(nano: string | undefined): string {
138
+ if (!nano || !/^\d+$/.test(nano)) return '';
139
+ try {
140
+ const ms = Number(BigInt(nano) / 1_000_000n);
141
+ if (!Number.isFinite(ms) || ms <= 0) return '';
142
+ const d = new Date(ms);
143
+ return isNaN(d.getTime()) ? '' : d.toISOString();
144
+ } catch {
145
+ return '';
146
+ }
147
+ }
148
+
149
+ /** Resolve caller identity from JWT claims (validated by the API Gateway authorizer). */
150
+ function resolveIdentity(event: HttpApiEvent): string | null {
151
+ const claims = event.requestContext.authorizer?.jwt?.claims;
152
+ if (!claims) return null;
153
+ for (const claim of [IDENTITY_CLAIM, 'username', 'email', 'sub']) {
154
+ const v = claims[claim];
155
+ if (typeof v === 'string' && v.trim()) return v.trim().toLowerCase();
156
+ }
157
+ return null;
158
+ }
159
+
160
+ /** Extract and sanity-check spans from an OTLP payload. Returns spans + rejected count. */
161
+ export function parseOtlpSpans(payload: OtlpPayload): { spans: ParsedSpan[]; rejected: number } {
162
+ const spans: ParsedSpan[] = [];
163
+ let rejected = 0;
164
+
165
+ for (const rs of payload.resourceSpans ?? []) {
166
+ const resourceAttrs = attrMap(rs.resource?.attributes);
167
+ const deviceId = str(resourceAttrs.get('codeburn.device_id')).slice(0, 64);
168
+
169
+ for (const ss of rs.scopeSpans ?? []) {
170
+ for (const span of ss.spans ?? []) {
171
+ const attrs = attrMap(span.attributes);
172
+ const spanId = typeof span.spanId === 'string' ? span.spanId : '';
173
+ const timestamp = nanoToIso(span.startTimeUnixNano);
174
+ const provider = str(attrs.get('ai.provider'));
175
+
176
+ // Minimum viable span: identity key parts must be present and sane.
177
+ if (!/^[0-9a-f]{16}$/i.test(spanId) || !timestamp || !provider) {
178
+ rejected++;
179
+ continue;
180
+ }
181
+
182
+ const inputTokens = num(attrs.get('ai.input_tokens'));
183
+ const outputTokens = num(attrs.get('ai.output_tokens'));
184
+ const costUsd = num(attrs.get('ai.cost_usd'));
185
+ // Clamp: reject absurd values (mirrors the CI trailer clamps).
186
+ if (
187
+ inputTokens < 0 || outputTokens < 0 || costUsd < 0 ||
188
+ inputTokens > 100_000_000 || outputTokens > 100_000_000 || costUsd > 10_000
189
+ ) {
190
+ rejected++;
191
+ continue;
192
+ }
193
+
194
+ spans.push({
195
+ spanId: spanId.toLowerCase(),
196
+ timestamp,
197
+ tool: PROVIDER_TO_TOOL[provider] ?? provider.slice(0, 32),
198
+ model: str(attrs.get('ai.model')).slice(0, 128),
199
+ inputTokens,
200
+ outputTokens,
201
+ costUsd,
202
+ project: str(attrs.get('ai.project')).slice(0, 256),
203
+ costEstimated: bool(attrs.get('ai.cost_estimated')),
204
+ deviceId,
205
+ });
206
+ }
207
+ }
208
+ }
209
+
210
+ return { spans, rejected };
211
+ }
212
+
213
+ // ---- Route: discovery doc ----
214
+
215
+ function handleDiscovery(): HttpApiResponse {
216
+ return jsonResponse(200, {
217
+ version: 1,
218
+ issuer: OIDC_ISSUER,
219
+ client_id: OIDC_CLIENT_ID,
220
+ scopes: ['openid', 'email', 'profile'],
221
+ traces_path: '/v1/traces',
222
+ max_batch_size: MAX_BATCH_SIZE,
223
+ });
224
+ }
225
+
226
+ // ---- Route: traces ----
227
+
228
+ async function archiveToS3(rawBody: string): Promise<void> {
229
+ if (!ARCHIVE_BUCKET) return;
230
+ const now = new Date();
231
+ const dt = now.toISOString().slice(0, 10);
232
+ const key = `otlp/dt=${dt}/${now.toISOString().replace(/[:.]/g, '-')}-${randomUUID()}.json`;
233
+ await s3Client.send(new PutObjectCommand({
234
+ Bucket: ARCHIVE_BUCKET,
235
+ Key: key,
236
+ Body: rawBody,
237
+ ContentType: 'application/json',
238
+ }));
239
+ }
240
+
241
+ /** Conditional raw-span write. Returns true if the span is NEW (write succeeded). */
242
+ async function writeSpanIfNew(user: string, s: ParsedSpan): Promise<boolean> {
243
+ const ttl = Math.floor(Date.now() / 1000) + SPAN_TTL_DAYS * 24 * 60 * 60;
244
+ try {
245
+ await dynamoClient.send(new PutItemCommand({
246
+ TableName: AI_USAGE_TABLE,
247
+ Item: {
248
+ pk: { S: `USER#${user}` },
249
+ sk: { S: `SPAN#${s.timestamp}#${s.spanId}` },
250
+ record_type: { S: 'OTEL_SPAN' },
251
+ tool: { S: s.tool },
252
+ model: { S: s.model },
253
+ input_tokens: { N: String(s.inputTokens) },
254
+ output_tokens: { N: String(s.outputTokens) },
255
+ cost_usd: { N: String(s.costUsd) },
256
+ project: { S: s.project },
257
+ device_id: { S: s.deviceId },
258
+ cost_estimated: { BOOL: s.costEstimated },
259
+ timestamp: { S: s.timestamp },
260
+ ttl: { N: String(ttl) },
261
+ },
262
+ // Dedup gate: deterministic span IDs make retried batches no-ops.
263
+ ConditionExpression: 'attribute_not_exists(pk)',
264
+ }));
265
+ return true;
266
+ } catch (e) {
267
+ if (e instanceof ConditionalCheckFailedException) return false;
268
+ throw e;
269
+ }
270
+ }
271
+
272
+ /** ADD-increment the daily aggregate for a newly-seen span (bucketed by SPAN date). */
273
+ async function bumpDailyAggregate(user: string, s: ParsedSpan): Promise<void> {
274
+ const day = s.timestamp.slice(0, 10);
275
+ await dynamoClient.send(new UpdateItemCommand({
276
+ TableName: AI_USAGE_TABLE,
277
+ Key: {
278
+ pk: { S: `USER#${user}` },
279
+ sk: { S: `OTEL#DAY#${day}#${s.tool}` },
280
+ },
281
+ UpdateExpression:
282
+ 'ADD input_tokens :in, output_tokens :out, cost_usd :cost, call_count :one ' +
283
+ 'SET record_type = :rt, tool = :tool, #day = :day, updated_at = :now',
284
+ ExpressionAttributeNames: { '#day': 'day' },
285
+ ExpressionAttributeValues: {
286
+ ':in': { N: String(s.inputTokens) },
287
+ ':out': { N: String(s.outputTokens) },
288
+ ':cost': { N: String(s.costUsd) },
289
+ ':one': { N: '1' },
290
+ ':rt': { S: 'OTEL_DAY' },
291
+ ':tool': { S: s.tool },
292
+ ':day': { S: day },
293
+ ':now': { S: new Date().toISOString() },
294
+ },
295
+ }));
296
+ }
297
+
298
+ /** Process spans with bounded concurrency. Returns count of newly-written spans. */
299
+ async function processSpans(user: string, spans: ParsedSpan[]): Promise<number> {
300
+ const CONCURRENCY = 20;
301
+ let written = 0;
302
+ for (let i = 0; i < spans.length; i += CONCURRENCY) {
303
+ const chunk = spans.slice(i, i + CONCURRENCY);
304
+ const results = await Promise.all(chunk.map(async (s) => {
305
+ const isNew = await writeSpanIfNew(user, s);
306
+ if (isNew) await bumpDailyAggregate(user, s);
307
+ return isNew;
308
+ }));
309
+ written += results.filter(Boolean).length;
310
+ }
311
+ return written;
312
+ }
313
+
314
+ async function handleTraces(event: HttpApiEvent): Promise<HttpApiResponse> {
315
+ const user = resolveIdentity(event);
316
+ if (!user) {
317
+ return jsonResponse(401, { message: 'No resolvable identity claim in token' });
318
+ }
319
+
320
+ const rawBody = event.isBase64Encoded
321
+ ? Buffer.from(event.body ?? '', 'base64').toString('utf-8')
322
+ : (event.body ?? '');
323
+
324
+ let payload: OtlpPayload;
325
+ try {
326
+ payload = JSON.parse(rawBody) as OtlpPayload;
327
+ } catch {
328
+ return jsonResponse(400, { message: 'Body is not valid JSON' });
329
+ }
330
+
331
+ const { spans, rejected } = parseOtlpSpans(payload);
332
+
333
+ if (spans.length > MAX_BATCH_SIZE) {
334
+ return jsonResponse(400, { message: `Batch exceeds max_batch_size (${MAX_BATCH_SIZE})` });
335
+ }
336
+
337
+ // Archive the raw batch first — the S3 OTLP archive is the external contract.
338
+ // Archive failures are fatal (client retries the batch; dedup gate makes it safe).
339
+ await archiveToS3(rawBody);
340
+
341
+ const written = await processSpans(user, spans);
342
+ console.log(
343
+ `[otel-receiver] user=${user} received=${spans.length + rejected} accepted=${spans.length} ` +
344
+ `new=${written} duplicates=${spans.length - written} rejected=${rejected}`,
345
+ );
346
+
347
+ // OTLP/HTTP success response; report malformed spans via partialSuccess.
348
+ if (rejected > 0) {
349
+ return jsonResponse(200, {
350
+ partialSuccess: { rejectedSpans: rejected, errorMessage: 'Spans missing required ai.* attributes or failed validation' },
351
+ });
352
+ }
353
+ return jsonResponse(200, {});
354
+ }
355
+
356
+ // ---- Handler ----
357
+
358
+ export async function handler(event: HttpApiEvent): Promise<HttpApiResponse> {
359
+ const method = event.requestContext.http.method;
360
+ const path = event.rawPath;
361
+
362
+ if (method === 'GET' && path === '/.well-known/codeburn-export.json') {
363
+ return handleDiscovery();
364
+ }
365
+ if (method === 'POST' && path === '/v1/traces') {
366
+ return handleTraces(event);
367
+ }
368
+ return jsonResponse(404, { message: 'Not found' });
369
+ }
@@ -13,15 +13,18 @@ import * as ec2 from 'aws-cdk-lib/aws-ec2';
13
13
  import { PrismVpcConstruct } from './constructs/prism-vpc-construct';
14
14
  import { GuardrailEnforcerConstruct } from './constructs/guardrail-enforcer-construct';
15
15
  import { SecurityAgentConstruct } from './constructs/security-agent-construct';
16
+ import { OtelCollectorConstruct } from './constructs/otel-collector-construct';
16
17
  import { NagSuppressions } from 'cdk-nag';
17
18
 
18
19
  export class MetricsPipelineStack extends cdk.Stack {
19
20
  public readonly eventBus: events.EventBus;
20
21
  public readonly eventsTable: dynamodb.Table;
22
+ public readonly aiUsageTable: dynamodb.Table;
21
23
  public readonly metadataTable: dynamodb.Table;
22
24
  public readonly kmsKey: kms.Key;
23
25
  public readonly guardrail: BedrockGuardrailConstruct;
24
26
  public readonly securityAgent?: SecurityAgentConstruct;
27
+ public readonly otelCollector?: OtelCollectorConstruct;
25
28
 
26
29
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
27
30
  super(scope, id, props);
@@ -127,6 +130,38 @@ export class MetricsPipelineStack extends cdk.Stack {
127
130
  encryptionKey: prismKmsKey,
128
131
  });
129
132
 
133
+ // -------------------------------------------------------
134
+ // DynamoDB AI-usage table — user-keyed, KMS encrypted
135
+ // pk = USER#<email>
136
+ // sk = COMMIT#<ts>#<sha>#<tool> | SPAN#<ts>#<spanId> | OTEL#DAY#<date>#<tool>
137
+ // Sparse GSIs (only commit-level items set them) invert the user key so the
138
+ // same table answers by-repo / by-PR / by-commit / global-by-date.
139
+ // -------------------------------------------------------
140
+ this.aiUsageTable = new dynamodb.Table(this, 'AiUsageTable', {
141
+ tableName: 'prism-d1-ai-usage',
142
+ partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
143
+ sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING },
144
+ billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
145
+ pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: true },
146
+ removalPolicy: cdk.RemovalPolicy.RETAIN,
147
+ timeToLiveAttribute: 'ttl',
148
+ encryption: dynamodb.TableEncryption.CUSTOMER_MANAGED,
149
+ encryptionKey: prismKmsKey,
150
+ });
151
+ for (const [indexName, pkAttr, skAttr] of [
152
+ ['by-repo', 'gsi_repo', 'gsi_repo_sk'],
153
+ ['by-pr', 'gsi_pr', 'gsi_pr_sk'],
154
+ ['by-commit', 'gsi_commit', 'gsi_commit_sk'],
155
+ ['by-date', 'gsi_date', 'gsi_date_sk'],
156
+ ] as const) {
157
+ this.aiUsageTable.addGlobalSecondaryIndex({
158
+ indexName,
159
+ partitionKey: { name: pkAttr, type: dynamodb.AttributeType.STRING },
160
+ sortKey: { name: skAttr, type: dynamodb.AttributeType.STRING },
161
+ projectionType: dynamodb.ProjectionType.ALL,
162
+ });
163
+ }
164
+
130
165
  // -------------------------------------------------------
131
166
  // VPC for Lambda isolation (Pillar 6)
132
167
  // Opt out with: cdk deploy -c skipVpc=true
@@ -253,6 +288,25 @@ export class MetricsPipelineStack extends cdk.Stack {
253
288
  // -------------------------------------------------------
254
289
  this.guardrail = new BedrockGuardrailConstruct(this, 'PrismGuardrail', createDefaultPrismGuardrailProps());
255
290
 
291
+ // -------------------------------------------------------
292
+ // OTEL Collector (opt-in — server side of `codeburn sync`)
293
+ // Enable with: npx cdk deploy --context enableOtelCollector=true
294
+ // BYO IdP: -c otelIssuer=... -c otelClientId=... [-c otelIdentityClaim=email]
295
+ // Per-user AI usage flows directly from codeburn sync to the
296
+ // ai-usage table (spans + daily aggregates).
297
+ // -------------------------------------------------------
298
+ const enableOtelCollector = this.node.tryGetContext('enableOtelCollector') === 'true';
299
+ if (enableOtelCollector) {
300
+ this.otelCollector = new OtelCollectorConstruct(this, 'OtelCollector', {
301
+ aiUsageTable: this.aiUsageTable,
302
+ kmsKey: prismKmsKey,
303
+ lambdaVpcProps,
304
+ externalIssuer: this.node.tryGetContext('otelIssuer') as string | undefined,
305
+ externalClientId: this.node.tryGetContext('otelClientId') as string | undefined,
306
+ identityClaim: this.node.tryGetContext('otelIdentityClaim') as string | undefined,
307
+ });
308
+ }
309
+
256
310
  // -------------------------------------------------------
257
311
  // AWS Security Agent (opt-in — requires Security Agent access)
258
312
  // Enable with: npx cdk deploy --context enableSecurityAgent=true
@@ -681,7 +735,7 @@ export class MetricsPipelineStack extends cdk.Stack {
681
735
  {
682
736
  id: 'AwsSolutions-IAM5',
683
737
  reason: 'CDK grantReadData on DynamoDB generates wildcard for GSI index ARNs via token reference',
684
- appliesTo: ['Resource::<EventsTableD24865E5.Arn>/index/*'],
738
+ appliesTo: ['Resource::<EventsTableD24865E5.Arn>/index/*', 'Resource::<AiUsageTable79F37CE0.Arn>/index/*'],
685
739
  },
686
740
  { id: 'AwsSolutions-L1', reason: 'All Lambdas use nodejs22.x which is the latest Node.js runtime available' },
687
741
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"commit-trailers.d.ts","sourceRoot":"","sources":["../../../../src/commands/git/commit-trailers.ts"],"names":[],"mappings":";;;;;;iBAkIqB;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;;AAP/E,wBA0FE"}
1
+ {"version":3,"file":"commit-trailers.d.ts","sourceRoot":"","sources":["../../../../src/commands/git/commit-trailers.ts"],"names":[],"mappings":";;;;;;iBAwJqB;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;;AAP/E,wBAgGE"}
@@ -38,6 +38,18 @@ function detectTool() {
38
38
  origin = 'ai-generated';
39
39
  tool = 'q-developer';
40
40
  }
41
+ // Cursor (VS Code fork). Checked LAST so any commit made from within Cursor
42
+ // wins the single AI-Tool slot, even when another agent's session env vars are
43
+ // also present (e.g. a CLI agent run inside Cursor's integrated terminal).
44
+ // TERM_PROGRAM=vscode is NOT usable (Cursor inherits VS Code's value), so key
45
+ // off CURSOR_AGENT / CURSOR_TRACE_ID or the askpass path Cursor injects into
46
+ // its terminal + Source Control panel, which contains 'cursor'.
47
+ const cursorAskpass = `${env.VSCODE_GIT_ASKPASS_NODE || ''} ${env.VSCODE_GIT_ASKPASS_MAIN || ''} ${env.GIT_ASKPASS || ''}`.toLowerCase();
48
+ if (env.CURSOR_AGENT === '1' || env.CURSOR_TRACE_ID || cursorAskpass.includes('cursor')) {
49
+ origin = 'ai-generated';
50
+ tool = 'cursor';
51
+ model = '';
52
+ }
41
53
  return { origin, tool, model };
42
54
  }
43
55
  /** Find a spec reference: an explicit Spec-Ref in the message wins, else a staged spec file. */
@@ -54,12 +66,17 @@ function detectSpecRef(repoRoot, msg) {
54
66
  }
55
67
  return '';
56
68
  }
57
- /** Run `codeburn report` and parse its JSON. Returns null if codeburn is absent or errors. */
58
- function runCodeburn(projectFilter) {
69
+ /** Run `codeburn report` and parse its JSON. Returns null if codeburn is absent or errors.
70
+ * `provider` (optional) maps to codeburn's `--provider` filter (e.g. 'cursor', 'claude').
71
+ * It is always a fixed internal literal — never user/LLM input — so the Windows shell
72
+ * interpolation below is not an injection vector. */
73
+ function runCodeburn(projectFilter, provider) {
59
74
  const isWin = process.platform === 'win32';
75
+ const providerArgs = provider ? ['--provider', provider] : [];
76
+ const providerCmd = provider ? `--provider ${provider} ` : '';
60
77
  const r = isWin
61
- ? spawnSync(`codeburn report -p all --project "${projectFilter}" --format json`, { encoding: 'utf8', shell: true })
62
- : spawnSync('codeburn', ['report', '-p', 'all', '--project', projectFilter, '--format', 'json'], { encoding: 'utf8' });
78
+ ? spawnSync(`codeburn report -p all ${providerCmd}--project "${projectFilter}" --format json`, { encoding: 'utf8', shell: true })
79
+ : spawnSync('codeburn', ['report', '-p', 'all', ...providerArgs, '--project', projectFilter, '--format', 'json'], { encoding: 'utf8' });
63
80
  if (!r || r.status !== 0 || !r.stdout)
64
81
  return null;
65
82
  try {
@@ -76,16 +93,23 @@ async function collectUsage(tool, projectBasename, projectFilter) {
76
93
  if (process.env.KIRO_SESSION_ID) {
77
94
  out = await collectKiroCli(process.env.KIRO_SESSION_ID, projectBasename);
78
95
  }
79
- if (!out || out.overview.calls === 0) {
80
- out = await collectKiroIde(projectBasename);
81
- }
82
- return out;
96
+ if (out && out.overview.calls > 0)
97
+ return { data: out, source: 'kiro-cli' };
98
+ out = await collectKiroIde(projectBasename);
99
+ if (out && out.overview.calls > 0)
100
+ return { data: out, source: 'kiro-ide' };
101
+ return null;
102
+ }
103
+ if (tool === 'cursor') {
104
+ let out = runCodeburn(projectFilter, 'cursor');
105
+ if (!out || out.overview.calls === 0)
106
+ out = runCodeburn(projectBasename, 'cursor');
107
+ return out ? { data: out, source: 'cursor' } : null;
83
108
  }
84
- // claude-code / q-developer → codeburn (full-path slug, then basename fallback)
85
109
  let out = runCodeburn(projectFilter);
86
110
  if (!out || out.overview.calls === 0)
87
111
  out = runCodeburn(projectBasename);
88
- return out;
112
+ return out ? { data: out, source: tool } : null;
89
113
  }
90
114
  function sumTokens(o, key) {
91
115
  return (o.models || []).reduce((s, m) => s + (m[key] || 0), 0);
@@ -140,13 +164,18 @@ export default {
140
164
  let outputTokens = null;
141
165
  let cost = null;
142
166
  if (tool) {
143
- const usage = await collectUsage(tool, projectBasename, projectFilter);
144
- if (usage) {
167
+ const result = await collectUsage(tool, projectBasename, projectFilter);
168
+ if (result) {
169
+ const { data: usage, source: usageSource } = result;
145
170
  const curIn = sumTokens(usage, 'inputTokens');
146
171
  const curOut = sumTokens(usage, 'outputTokens');
147
172
  const curCost = usage.overview?.cost ?? 0;
148
173
  const trackerDir = resolve(homedir(), '.prism', 'tokentracker');
149
- const trackerFile = resolve(trackerDir, `${projectBasename}.json`);
174
+ // Snapshot baselines are keyed by (project, source) — NOT just (project, tool).
175
+ // Kiro has two sources (kiro-cli per-session vs kiro-ide lifetime) with
176
+ // vastly different cumulative totals. Sharing a tracker file between them
177
+ // produces massive false deltas when the fallback path switches.
178
+ const trackerFile = resolve(trackerDir, `${projectBasename}.${usageSource}.json`);
150
179
  if (existsSync(trackerFile)) {
151
180
  try {
152
181
  const prev = JSON.parse(readFileSync(trackerFile, 'utf8'));
@@ -1 +1 @@
1
- {"version":3,"file":"commit-trailers.js","sourceRoot":"","sources":["../../../../src/commands/git/commit-trailers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,cAAc,EAAuB,MAAM,gCAAgC,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAWhE,MAAM,oBAAoB,GAAG,8CAA8C,CAAC;AAE5E,SAAS,OAAO,CAAC,QAAiB;IAChC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACnF,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACvD,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC;AACvB,CAAC;AAED,yEAAyE;AACzE,SAAS,UAAU;IACjB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACxB,IAAI,MAAM,GAAG,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,KAAK,GAAG,EAAE,CAAC;IAE5C,cAAc;IACd,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,sBAAsB,EAAE,CAAC;QAClD,MAAM,GAAG,cAAc,CAAC;QACxB,IAAI,GAAG,aAAa,CAAC;QACrB,KAAK,GAAG,GAAG,CAAC,eAAe,IAAI,oBAAoB,CAAC;IACtD,CAAC;IAED,kGAAkG;IAClG,IACE,GAAG,CAAC,eAAe;QACnB,GAAG,CAAC,YAAY;QAChB,GAAG,CAAC,YAAY,KAAK,MAAM;QAC3B,CAAC,GAAG,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QACpD,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EACxC,CAAC;QACD,MAAM,GAAG,cAAc,CAAC;QACxB,IAAI,GAAG,MAAM,CAAC;QACd,KAAK,GAAG,EAAE,CAAC;IACb,CAAC;IAED,qBAAqB;IACrB,IAAI,GAAG,CAAC,mBAAmB,EAAE,CAAC;QAC5B,MAAM,GAAG,cAAc,CAAC;QACxB,IAAI,GAAG,aAAa,CAAC;IACvB,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC;AAED,gGAAgG;AAChG,SAAS,aAAa,CAAC,QAAgB,EAAE,GAAW;IAClD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAClD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;IAEzC,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IACrG,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACrC,IAAI,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,8FAA8F;AAC9F,SAAS,WAAW,CAAC,aAAqB;IACxC,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;IAC3C,MAAM,CAAC,GAAG,KAAK;QACb,CAAC,CAAC,SAAS,CAAC,qCAAqC,aAAa,iBAAiB,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACnH,CAAC,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACzH,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAmB,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC/E,CAAC;AAED,mEAAmE;AACnE,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,eAAuB,EAAE,aAAqB;IACtF,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,IAAI,GAAG,GAA0B,IAAI,CAAC;QACtC,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC;YAChC,GAAG,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACrC,GAAG,GAAG,MAAM,cAAc,CAAC,eAAe,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,gFAAgF;IAChF,IAAI,GAAG,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC;QAAE,GAAG,GAAG,WAAW,CAAC,eAAe,CAAC,CAAC;IACzE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,SAAS,CAAC,CAAiB,EAAE,GAAmC;IACvE,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB;IAClC,IAAI,SAAS,GAAG,SAAS,EAAE,OAAO,GAAG,GAAG,CAAC;IACzC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;IACvD,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;YAChD,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;gBAAE,SAAS,GAAG,CAAC,CAAC,UAAU,CAAC;YAC/D,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ;gBAAE,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC,CAAC,8BAA8B,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC;AAED,+FAA+F;AAC/F,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;AACpD,CAAC;AAED,eAAe;IACb,WAAW,EAAE,mGAAmG;IAChH,OAAO,EAAE;QACP,EAAE,KAAK,EAAE,0BAA0B,EAAE,WAAW,EAAE,uFAAuF,EAAE;QAC3I,EAAE,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,uEAAuE,EAAE;QACpH,EAAE,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,6DAA6D,EAAE;KACvG;IACD,KAAK,CAAC,MAAM,CAAC,IAAgE;QAC3E,4BAA4B;QAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO;QAEhE,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEpC,IAAI,WAAW,GAAG,EAAE,CAAC;QACrB,IAAI,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;YACzD,IAAI,CAAC;gBAAC,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QACxF,CAAC;QACD,4BAA4B;QAC5B,IAAI,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC;YAAE,OAAO;QAE7C,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QAErD,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAEtE,qEAAqE;QACrE,IAAI,WAAW,GAAkB,IAAI,CAAC;QACtC,IAAI,YAAY,GAAkB,IAAI,CAAC;QACvC,IAAI,IAAI,GAAkB,IAAI,CAAC;QAE/B,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;YACvE,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;gBAC9C,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;gBAChD,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,CAAC;gBAE1C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;gBAChE,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,EAAE,GAAG,eAAe,OAAO,CAAC,CAAC;gBAEnE,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC5B,IAAI,CAAC;wBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAa,CAAC;wBACvE,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC;wBAC3D,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC;wBAC9D,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;oBACjD,CAAC;oBAAC,MAAM,CAAC;wBACP,WAAW,GAAG,CAAC,CAAC;wBAAC,YAAY,GAAG,CAAC,CAAC;wBAAC,IAAI,GAAG,CAAC,CAAC;oBAC9C,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,wDAAwD;oBACxD,WAAW,GAAG,CAAC,CAAC;oBAAC,YAAY,GAAG,CAAC,CAAC;oBAAC,IAAI,GAAG,CAAC,CAAC;gBAC9C,CAAC;gBAED,uDAAuD;gBACvD,IAAI,CAAC;oBACH,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC3C,MAAM,IAAI,GAAa,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oBACnF,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;gBAC1D,CAAC;gBAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,6BAA6B;QAC7B,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,WAAW,KAAK,IAAI;YAAE,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QACzE,IAAI,YAAY,KAAK,IAAI;YAAE,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAC5E,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,GAAG,OAAO;YAAE,IAAI,GAAG,OAAO,CAAC;QAEpD,sBAAsB;QACtB,MAAM,KAAK,GAAa,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;QACjD,IAAI,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;QACzC,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,EAAE,CAAC,CAAC;QAC5C,IAAI,WAAW,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,oBAAoB,WAAW,EAAE,CAAC,CAAC;QACxE,IAAI,YAAY,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,qBAAqB,YAAY,EAAE,CAAC,CAAC;QAC3E,IAAI,IAAI,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/D,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,OAAO,EAAE,CAAC,CAAC;QAChD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE/B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,mEAAmE;YACnE,IAAI,GAAG,GAAG,WAAW,CAAC;YACtB,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;gBAAE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC;YAC3D,GAAG,IAAI,KAAK,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC;gBAAC,aAAa,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,wCAAwC,CAAC,CAAC;QACpG,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"commit-trailers.js","sourceRoot":"","sources":["../../../../src/commands/git/commit-trailers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prism-d1/cli",
3
- "version": "1.0.60",
3
+ "version": "1.0.64",
4
4
  "description": "PRISM D1 Velocity CLI — AI development lifecycle assessment tool",
5
5
  "author": "andklee",
6
6
  "engines": {