@contrail/cli 3.5.0-alpha.dd20a19 → 3.5.1-alpha.63493a5
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/README.md +3 -35
- package/dist/api/types.d.ts +0 -5
- package/dist/api/types.js +1 -36
- package/dist/commands/types/getAll.js +0 -18
- package/dist/commands/types/loadAll.js +0 -2
- package/dist/commands/types/mergeTypeProperties.js +0 -5
- package/dist/commands/types/uploadOptionSetHierarchies.js +0 -117
- package/dist/commands/types/uploadOptionSets.js +1 -1
- package/dist/commands/types/uploadRuleSets.js +2 -85
- package/dist/commands/types/uploadTypePolicies.js +2 -86
- package/dist/commands/types/uploadTypePropertyPolicies.d.ts +0 -1
- package/dist/commands/types/uploadTypePropertyPolicies.js +3 -139
- package/dist/commands/types/uploadUsageInfo.d.ts +3 -0
- package/dist/commands/types/uploadUsageInfo.js +4 -1
- package/dist/helpers/types/type-uploader.js +1 -1
- package/dist/templates/typescript/tsup.config.ts +0 -0
- package/node_modules/@contrail/loader/lib/color-processor.js +9 -23
- package/node_modules/@contrail/loader/lib/color-processor.spec.js +112 -62
- package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.d.ts +1 -1
- package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.js +2 -0
- package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.spec.js +4 -0
- package/node_modules/@contrail/loader/lib/common/color-validation.d.ts +26 -0
- package/node_modules/@contrail/loader/lib/common/color-validation.js +163 -0
- package/node_modules/@contrail/loader/lib/common/color-validation.spec.js +102 -0
- package/node_modules/@contrail/loader/lib/common/size-range-helpers.js +0 -1
- package/node_modules/@contrail/loader/lib/constants.d.ts +0 -3
- package/node_modules/@contrail/loader/lib/constants.js +1 -4
- package/node_modules/@contrail/loader/lib/entities-v2/colors-v2/color-processor-v2.js +9 -23
- package/node_modules/@contrail/loader/lib/entities-v2/colors-v2/color-processor-v2.spec.js +112 -62
- package/node_modules/@contrail/loader/lib/entities-v2/entities-helper.js +0 -1
- package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/assortment-processor.js +0 -1
- package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/item-processor.js +8 -22
- package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/item-processor.spec.js +43 -1
- package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.d.ts +5 -0
- package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.js +562 -0
- package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.spec.js +231 -0
- package/node_modules/@contrail/loader/lib/pipeline/item-execute-helpers.js +77 -3
- package/node_modules/@contrail/loader/lib/pipeline/item-execute-helpers.spec.js +82 -0
- package/node_modules/@contrail/loader/lib/pipeline/preview.js +0 -5
- package/node_modules/@contrail/loader/lib/pipeline/preview.spec.js +2 -2
- package/node_modules/@contrail/loader/lib/pipeline/project-item-execute-helpers.js +1 -1
- package/node_modules/@contrail/loader/lib/pipeline/transform.js +18 -0
- package/node_modules/@contrail/loader/lib/pipeline/transform.spec.js +3 -2
- package/node_modules/@contrail/loader/lib/pipeline/types.d.ts +5 -2
- package/node_modules/@contrail/loader/lib/pipeline/types.js +2 -0
- package/node_modules/@contrail/loader/lib/pipeline/validate.js +184 -437
- package/node_modules/@contrail/loader/lib/pipeline/validate.spec.js +157 -53
- package/node_modules/@contrail/loader/package.json +3 -2
- package/node_modules/@contrail/sdk/.github/workflows/publish-to-npm.yml +120 -0
- package/node_modules/@contrail/sdk/lib/auth/login.js +42 -3
- package/node_modules/@contrail/sdk/lib/core/request.d.ts +3 -0
- package/node_modules/@contrail/sdk/lib/core/request.js +100 -4
- package/node_modules/@contrail/sdk/lib/entities/entities.client.js +1 -1
- package/node_modules/@contrail/sdk/lib/entities/entity.resource.registry.js +1 -0
- package/node_modules/@contrail/sdk/lib/files/files.d.ts +13 -3
- package/node_modules/@contrail/sdk/lib/files/files.js +66 -25
- package/node_modules/@contrail/sdk/package.json +4 -1
- package/node_modules/validate-color/LICENSE +21 -0
- package/node_modules/validate-color/README.md +265 -0
- package/node_modules/validate-color/lib/index.d.ts +10 -0
- package/node_modules/validate-color/lib/index.js +1 -0
- package/node_modules/validate-color/package.json +91 -0
- package/node_modules/validate-color/test/redos.js +29 -0
- package/oclif.manifest.json +5 -80
- package/package.json +3 -6
- package/dist/api/dash0/client.d.ts +0 -13
- package/dist/api/dash0/client.js +0 -53
- package/dist/api/dash0/spans.d.ts +0 -8
- package/dist/api/dash0/spans.js +0 -91
- package/dist/api/dash0/types.d.ts +0 -84
- package/dist/api/dash0/types.js +0 -4
- package/dist/commands/event-workflows/assess-impact.d.ts +0 -19
- package/dist/commands/event-workflows/assess-impact.js +0 -104
- package/dist/helpers/event-workflow-impact/aggregate.d.ts +0 -23
- package/dist/helpers/event-workflow-impact/aggregate.js +0 -49
- package/dist/helpers/event-workflow-impact/aggregate.spec.js +0 -109
- package/dist/helpers/event-workflow-impact/classify.d.ts +0 -13
- package/dist/helpers/event-workflow-impact/classify.js +0 -135
- package/dist/helpers/event-workflow-impact/classify.spec.js +0 -138
- package/dist/helpers/event-workflow-impact/render.d.ts +0 -12
- package/dist/helpers/event-workflow-impact/render.js +0 -62
- package/dist/helpers/event-workflow-impact/subtree.d.ts +0 -20
- package/dist/helpers/event-workflow-impact/subtree.js +0 -93
- package/dist/helpers/event-workflow-impact/subtree.spec.d.ts +0 -1
- package/dist/helpers/event-workflow-impact/subtree.spec.js +0 -103
- /package/{dist/helpers/event-workflow-impact/aggregate.spec.d.ts → node_modules/@contrail/loader/lib/common/color-validation.spec.d.ts} +0 -0
- /package/{dist/helpers/event-workflow-impact/classify.spec.d.ts → node_modules/@contrail/loader/lib/pipeline/identifier-resolver.spec.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrail/cli",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.1-alpha.63493a5",
|
|
4
4
|
"description": "VibeIQ's Contrail Platform CLI.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"contrail-cli": "./bin/run",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@contrail/entity-types": "^1.6.1",
|
|
34
|
-
"@contrail/loader": "^2.0.
|
|
34
|
+
"@contrail/loader": "^2.0.9",
|
|
35
35
|
"@contrail/org-config-migration": "^1.1.13",
|
|
36
|
-
"@contrail/sdk": "^1.5.
|
|
36
|
+
"@contrail/sdk": "^1.5.13",
|
|
37
37
|
"@contrail/types": "^3.1.5",
|
|
38
38
|
"@contrail/util": "^1.2.0",
|
|
39
39
|
"@oclif/core": "^2",
|
|
@@ -122,9 +122,6 @@
|
|
|
122
122
|
"workflow-templates": {
|
|
123
123
|
"description": "Manage workflow templates"
|
|
124
124
|
},
|
|
125
|
-
"event-workflows": {
|
|
126
|
-
"description": "Inspect event-workflow action behavior"
|
|
127
|
-
},
|
|
128
125
|
"instance": {
|
|
129
126
|
"description": "Manage instance (org) configuration data"
|
|
130
127
|
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { GetSpansRequest, GetSpansResponse } from './types';
|
|
2
|
-
export interface Dash0ClientOptions {
|
|
3
|
-
token: string;
|
|
4
|
-
dataset: string;
|
|
5
|
-
apiUrl?: string;
|
|
6
|
-
}
|
|
7
|
-
export declare class Dash0Client {
|
|
8
|
-
private readonly http;
|
|
9
|
-
private readonly dataset;
|
|
10
|
-
constructor(opts: Dash0ClientOptions);
|
|
11
|
-
getSpans(req: GetSpansRequest): Promise<GetSpansResponse>;
|
|
12
|
-
}
|
|
13
|
-
export declare function dash0ClientFromEnv(dataset: string): Dash0Client;
|
package/dist/api/dash0/client.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Minimal Dash0 REST client.
|
|
3
|
-
//
|
|
4
|
-
// Contract verified by hand against api.us-west-2.aws.dash0.com on 2026-05-08:
|
|
5
|
-
// POST /api/spans
|
|
6
|
-
// headers: Authorization: Bearer ${DASH0_TOKEN}
|
|
7
|
-
// Content-Type: application/json
|
|
8
|
-
// Dash0-Dataset: <dataset> (REQUIRED — body field is ignored)
|
|
9
|
-
// body: { filter?: AttributeFilter[], timeRange: {from,to}, pagination?: {limit,cursor} }
|
|
10
|
-
// NOTE: the field is "filter" (singular). "filters" is silently ignored.
|
|
11
|
-
// Response uses OTLP/JSON: traceId/spanId are base64.
|
|
12
|
-
// Pagination via cursors.after → next call's pagination.cursor.
|
|
13
|
-
//
|
|
14
|
-
// Trace expansion uses the same endpoint: filter on `otel.trace.id` (hex).
|
|
15
|
-
// There is no separate trace-details endpoint (the binary's /api/trace-details path 404s).
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.dash0ClientFromEnv = exports.Dash0Client = void 0;
|
|
18
|
-
const tslib_1 = require("tslib");
|
|
19
|
-
const axios_1 = tslib_1.__importDefault(require("axios"));
|
|
20
|
-
const DEFAULT_API_URL = 'https://api.us-west-2.aws.dash0.com';
|
|
21
|
-
class Dash0Client {
|
|
22
|
-
http;
|
|
23
|
-
dataset;
|
|
24
|
-
constructor(opts) {
|
|
25
|
-
this.dataset = opts.dataset;
|
|
26
|
-
this.http = axios_1.default.create({
|
|
27
|
-
baseURL: opts.apiUrl ?? DEFAULT_API_URL,
|
|
28
|
-
headers: {
|
|
29
|
-
Authorization: `Bearer ${opts.token}`,
|
|
30
|
-
'Content-Type': 'application/json',
|
|
31
|
-
'Dash0-Dataset': opts.dataset,
|
|
32
|
-
},
|
|
33
|
-
timeout: 30000,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
async getSpans(req) {
|
|
37
|
-
const { data } = await this.http.post('/api/spans', req);
|
|
38
|
-
return data;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
exports.Dash0Client = Dash0Client;
|
|
42
|
-
function dash0ClientFromEnv(dataset) {
|
|
43
|
-
const token = process.env.DASH0_TOKEN;
|
|
44
|
-
if (!token) {
|
|
45
|
-
throw new Error('DASH0_TOKEN environment variable is required. Create one at https://app.dash0.com/settings/auth-tokens.');
|
|
46
|
-
}
|
|
47
|
-
return new Dash0Client({
|
|
48
|
-
token,
|
|
49
|
-
dataset,
|
|
50
|
-
apiUrl: process.env.DASH0_API_URL,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
exports.dash0ClientFromEnv = dash0ClientFromEnv;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Dash0Client } from './client';
|
|
2
|
-
import { AttributeFilter, FlatSpan, OtlpAttribute, OtlpResourceSpans, OtlpSpan, TimeReferenceRange } from './types';
|
|
3
|
-
export declare function paginateSpans(client: Dash0Client, filter: AttributeFilter[], timeRange: TimeReferenceRange, limit?: number): AsyncGenerator<OtlpResourceSpans, void, void>;
|
|
4
|
-
export declare function sampleTraceIdsForAction(client: Dash0Client, actionIdentifier: string, timeRange: TimeReferenceRange, maxTraces: number): Promise<string[]>;
|
|
5
|
-
export declare function fetchTraceSpans(client: Dash0Client, traceIdHex: string, timeRange: TimeReferenceRange): Promise<FlatSpan[]>;
|
|
6
|
-
export declare function toFlat(sp: OtlpSpan, resourceAttrs: Record<string, string | number | boolean>): FlatSpan;
|
|
7
|
-
export declare function flattenAttrs(attrs: OtlpAttribute[]): Record<string, string | number | boolean>;
|
|
8
|
-
export declare function b64ToHex(b64: string): string;
|
package/dist/api/dash0/spans.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.b64ToHex = exports.flattenAttrs = exports.toFlat = exports.fetchTraceSpans = exports.sampleTraceIdsForAction = exports.paginateSpans = void 0;
|
|
4
|
-
const PAGE_LIMIT = 50;
|
|
5
|
-
const MAX_PAGES_PER_QUERY = 200;
|
|
6
|
-
async function* paginateSpans(client, filter, timeRange, limit = PAGE_LIMIT) {
|
|
7
|
-
let cursor;
|
|
8
|
-
for (let page = 0; page < MAX_PAGES_PER_QUERY; page++) {
|
|
9
|
-
const req = {
|
|
10
|
-
filter,
|
|
11
|
-
timeRange,
|
|
12
|
-
pagination: { limit, cursor },
|
|
13
|
-
};
|
|
14
|
-
const resp = await client.getSpans(req);
|
|
15
|
-
for (const r of resp.resourceSpans ?? [])
|
|
16
|
-
yield r;
|
|
17
|
-
cursor = resp.cursors?.after;
|
|
18
|
-
if (!cursor)
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
exports.paginateSpans = paginateSpans;
|
|
23
|
-
async function sampleTraceIdsForAction(client, actionIdentifier, timeRange, maxTraces) {
|
|
24
|
-
const seen = new Set();
|
|
25
|
-
for await (const rs of paginateSpans(client, [
|
|
26
|
-
{
|
|
27
|
-
key: 'contrail.event_workflow.action.identifier',
|
|
28
|
-
operator: 'is',
|
|
29
|
-
value: actionIdentifier,
|
|
30
|
-
},
|
|
31
|
-
], timeRange)) {
|
|
32
|
-
for (const ss of rs.scopeSpans ?? []) {
|
|
33
|
-
for (const sp of ss.spans ?? []) {
|
|
34
|
-
seen.add(b64ToHex(sp.traceId));
|
|
35
|
-
if (seen.size >= maxTraces)
|
|
36
|
-
return [...seen];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return [...seen];
|
|
41
|
-
}
|
|
42
|
-
exports.sampleTraceIdsForAction = sampleTraceIdsForAction;
|
|
43
|
-
async function fetchTraceSpans(client, traceIdHex, timeRange) {
|
|
44
|
-
const out = [];
|
|
45
|
-
for await (const rs of paginateSpans(client, [{ key: 'otel.trace.id', operator: 'is', value: traceIdHex }], timeRange)) {
|
|
46
|
-
const resourceAttrs = flattenAttrs(rs.resource?.attributes ?? []);
|
|
47
|
-
for (const ss of rs.scopeSpans ?? []) {
|
|
48
|
-
for (const sp of ss.spans ?? [])
|
|
49
|
-
out.push(toFlat(sp, resourceAttrs));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return out;
|
|
53
|
-
}
|
|
54
|
-
exports.fetchTraceSpans = fetchTraceSpans;
|
|
55
|
-
function toFlat(sp, resourceAttrs) {
|
|
56
|
-
const startNs = BigInt(sp.startTimeUnixNano);
|
|
57
|
-
const endNs = BigInt(sp.endTimeUnixNano);
|
|
58
|
-
return {
|
|
59
|
-
name: sp.name,
|
|
60
|
-
traceId: b64ToHex(sp.traceId),
|
|
61
|
-
spanId: b64ToHex(sp.spanId),
|
|
62
|
-
parentSpanId: sp.parentSpanId ? b64ToHex(sp.parentSpanId) : '',
|
|
63
|
-
kind: sp.kind,
|
|
64
|
-
startNs,
|
|
65
|
-
endNs,
|
|
66
|
-
durationNs: endNs - startNs,
|
|
67
|
-
attrs: flattenAttrs(sp.attributes ?? []),
|
|
68
|
-
resourceAttrs,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
exports.toFlat = toFlat;
|
|
72
|
-
function flattenAttrs(attrs) {
|
|
73
|
-
const out = {};
|
|
74
|
-
for (const a of attrs) {
|
|
75
|
-
const v = a.value ?? {};
|
|
76
|
-
if (v.stringValue !== undefined)
|
|
77
|
-
out[a.key] = v.stringValue;
|
|
78
|
-
else if (v.intValue !== undefined)
|
|
79
|
-
out[a.key] = Number(v.intValue);
|
|
80
|
-
else if (v.boolValue !== undefined)
|
|
81
|
-
out[a.key] = v.boolValue;
|
|
82
|
-
else if (v.doubleValue !== undefined)
|
|
83
|
-
out[a.key] = v.doubleValue;
|
|
84
|
-
}
|
|
85
|
-
return out;
|
|
86
|
-
}
|
|
87
|
-
exports.flattenAttrs = flattenAttrs;
|
|
88
|
-
function b64ToHex(b64) {
|
|
89
|
-
return Buffer.from(b64, 'base64').toString('hex');
|
|
90
|
-
}
|
|
91
|
-
exports.b64ToHex = b64ToHex;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
export type AttributeFilterOperator = 'is' | 'is_not' | 'is_set' | 'is_not_set' | 'is_one_of' | 'is_not_one_of' | 'gt' | 'gte' | 'lt' | 'lte' | 'matches' | 'does_not_match' | 'contains' | 'does_not_contain' | 'starts_with' | 'does_not_start_with' | 'ends_with' | 'does_not_end_with';
|
|
2
|
-
export interface AttributeFilter {
|
|
3
|
-
key: string;
|
|
4
|
-
operator: AttributeFilterOperator;
|
|
5
|
-
value?: string;
|
|
6
|
-
values?: string[];
|
|
7
|
-
}
|
|
8
|
-
export interface TimeReferenceRange {
|
|
9
|
-
from: string;
|
|
10
|
-
to: string;
|
|
11
|
-
}
|
|
12
|
-
export interface CursorPagination {
|
|
13
|
-
limit?: number;
|
|
14
|
-
cursor?: string;
|
|
15
|
-
}
|
|
16
|
-
export interface GetSpansRequest {
|
|
17
|
-
filter?: AttributeFilter[];
|
|
18
|
-
timeRange: TimeReferenceRange;
|
|
19
|
-
pagination?: CursorPagination;
|
|
20
|
-
}
|
|
21
|
-
export interface OtlpAttribute {
|
|
22
|
-
key: string;
|
|
23
|
-
value: {
|
|
24
|
-
stringValue?: string;
|
|
25
|
-
intValue?: string | number;
|
|
26
|
-
boolValue?: boolean;
|
|
27
|
-
doubleValue?: number;
|
|
28
|
-
arrayValue?: {
|
|
29
|
-
values: OtlpAttribute['value'][];
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export interface OtlpSpan {
|
|
34
|
-
name: string;
|
|
35
|
-
traceId: string;
|
|
36
|
-
spanId: string;
|
|
37
|
-
parentSpanId: string;
|
|
38
|
-
kind: number;
|
|
39
|
-
startTimeUnixNano: string;
|
|
40
|
-
endTimeUnixNano: string;
|
|
41
|
-
attributes: OtlpAttribute[];
|
|
42
|
-
status: {
|
|
43
|
-
code: number;
|
|
44
|
-
message?: string;
|
|
45
|
-
};
|
|
46
|
-
events?: unknown[];
|
|
47
|
-
links?: unknown[];
|
|
48
|
-
}
|
|
49
|
-
export interface OtlpScopeSpans {
|
|
50
|
-
scope?: {
|
|
51
|
-
name?: string;
|
|
52
|
-
version?: string;
|
|
53
|
-
};
|
|
54
|
-
spans: OtlpSpan[];
|
|
55
|
-
}
|
|
56
|
-
export interface OtlpResourceSpans {
|
|
57
|
-
resource: {
|
|
58
|
-
attributes: OtlpAttribute[];
|
|
59
|
-
};
|
|
60
|
-
scopeSpans: OtlpScopeSpans[];
|
|
61
|
-
}
|
|
62
|
-
export interface NextCursors {
|
|
63
|
-
after?: string;
|
|
64
|
-
before?: string;
|
|
65
|
-
}
|
|
66
|
-
export interface GetSpansResponse {
|
|
67
|
-
resourceSpans: OtlpResourceSpans[];
|
|
68
|
-
cursors?: NextCursors;
|
|
69
|
-
executionTime?: string;
|
|
70
|
-
}
|
|
71
|
-
export interface FlatSpan {
|
|
72
|
-
name: string;
|
|
73
|
-
traceId: string;
|
|
74
|
-
spanId: string;
|
|
75
|
-
parentSpanId: string;
|
|
76
|
-
kind: number;
|
|
77
|
-
startNs: bigint;
|
|
78
|
-
endNs: bigint;
|
|
79
|
-
durationNs: bigint;
|
|
80
|
-
attrs: Record<string, string | number | boolean>;
|
|
81
|
-
resourceAttrs: Record<string, string | number | boolean>;
|
|
82
|
-
depthFromAction?: number;
|
|
83
|
-
serverAncestorCount?: number;
|
|
84
|
-
}
|
package/dist/api/dash0/types.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { BaseCommand } from '../../basecommand';
|
|
2
|
-
export default class AssessImpact extends BaseCommand {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static args: {
|
|
6
|
-
identifier: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
|
|
7
|
-
};
|
|
8
|
-
static flags: {
|
|
9
|
-
samples: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
|
-
lookback: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
11
|
-
dataset: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
12
|
-
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
-
alwaysConfirmEnv: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
14
|
-
logLevel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
15
|
-
};
|
|
16
|
-
skipValidateEnv: boolean;
|
|
17
|
-
skipConfirmEnv: boolean;
|
|
18
|
-
run(): Promise<void>;
|
|
19
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const core_1 = require("@oclif/core");
|
|
4
|
-
const basecommand_1 = require("../../basecommand");
|
|
5
|
-
const client_1 = require("../../api/dash0/client");
|
|
6
|
-
const spans_1 = require("../../api/dash0/spans");
|
|
7
|
-
const subtree_1 = require("../../helpers/event-workflow-impact/subtree");
|
|
8
|
-
const aggregate_1 = require("../../helpers/event-workflow-impact/aggregate");
|
|
9
|
-
const render_1 = require("../../helpers/event-workflow-impact/render");
|
|
10
|
-
const LOOKBACK_PATTERN = /^\d+(?:m|h|d)$/;
|
|
11
|
-
class AssessImpact extends basecommand_1.BaseCommand {
|
|
12
|
-
static description = "Sample Dash0 traces of an event-workflow action and report per-invocation downstream impact.";
|
|
13
|
-
static examples = [
|
|
14
|
-
'contrail event-workflows assess-impact @gap-automations:bomCcCostUpdate',
|
|
15
|
-
'contrail event-workflows assess-impact @gap-automations:bomCcCostUpdate --samples 25 --lookback 6h',
|
|
16
|
-
];
|
|
17
|
-
static args = {
|
|
18
|
-
identifier: core_1.Args.string({
|
|
19
|
-
description: 'Action identifier (contrail.event_workflow.action.identifier).',
|
|
20
|
-
required: true,
|
|
21
|
-
}),
|
|
22
|
-
};
|
|
23
|
-
static flags = {
|
|
24
|
-
...basecommand_1.BaseCommand.baseFlags,
|
|
25
|
-
samples: core_1.Flags.integer({
|
|
26
|
-
char: 's',
|
|
27
|
-
description: 'Number of distinct traces to sample (10-100).',
|
|
28
|
-
default: 50,
|
|
29
|
-
min: 10,
|
|
30
|
-
max: 100,
|
|
31
|
-
}),
|
|
32
|
-
lookback: core_1.Flags.string({
|
|
33
|
-
description: 'How far back to search. Format: 30m | 6h | 2d.',
|
|
34
|
-
default: '2d',
|
|
35
|
-
}),
|
|
36
|
-
dataset: core_1.Flags.string({
|
|
37
|
-
description: 'Dash0 dataset.',
|
|
38
|
-
default: 'prod',
|
|
39
|
-
}),
|
|
40
|
-
json: core_1.Flags.boolean({
|
|
41
|
-
char: 'j',
|
|
42
|
-
description: 'JSON output (not yet implemented; flag reserved).',
|
|
43
|
-
default: false,
|
|
44
|
-
}),
|
|
45
|
-
};
|
|
46
|
-
// No vibeiq auth or env confirmation — this hits Dash0, not the contrail API.
|
|
47
|
-
skipValidateEnv = true;
|
|
48
|
-
skipConfirmEnv = true;
|
|
49
|
-
async run() {
|
|
50
|
-
const { identifier } = this.args;
|
|
51
|
-
const { samples, lookback, dataset } = this.flags;
|
|
52
|
-
if (!LOOKBACK_PATTERN.test(lookback)) {
|
|
53
|
-
this.error(`--lookback must look like 30m, 6h, or 2d (got: ${lookback}).`);
|
|
54
|
-
}
|
|
55
|
-
const client = (0, client_1.dash0ClientFromEnv)(dataset);
|
|
56
|
-
const timeRange = { from: `now-${lookback}`, to: 'now' };
|
|
57
|
-
this.log(`Action: ${identifier}`);
|
|
58
|
-
this.log(`Dataset: ${dataset}`);
|
|
59
|
-
this.log(`Lookback: ${lookback}`);
|
|
60
|
-
this.log(`Samples: target ${samples}`);
|
|
61
|
-
this.log('');
|
|
62
|
-
this.log('Sampling trace IDs...');
|
|
63
|
-
const traceIds = await (0, spans_1.sampleTraceIdsForAction)(client, identifier, timeRange, samples);
|
|
64
|
-
if (traceIds.length === 0) {
|
|
65
|
-
this.warn(`No spans found for action.identifier="${identifier}" in the last ${lookback}. ` +
|
|
66
|
-
'Verify the identifier and dataset.');
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
this.log(`Got ${traceIds.length} trace(s). Fetching subtrees...`);
|
|
70
|
-
const perTraceDescendants = [];
|
|
71
|
-
let dropped = 0;
|
|
72
|
-
let totalOrphans = 0;
|
|
73
|
-
let preciseRootCount = 0;
|
|
74
|
-
for (const traceId of traceIds) {
|
|
75
|
-
const spans = await (0, spans_1.fetchTraceSpans)(client, traceId, timeRange);
|
|
76
|
-
const result = (0, subtree_1.extractActionSubtree)(spans, identifier);
|
|
77
|
-
if (!result) {
|
|
78
|
-
dropped++;
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
perTraceDescendants.push(result.descendants);
|
|
82
|
-
totalOrphans += result.orphanCount;
|
|
83
|
-
if (result.preciseRoot)
|
|
84
|
-
preciseRootCount++;
|
|
85
|
-
}
|
|
86
|
-
if (perTraceDescendants.length === 0) {
|
|
87
|
-
this.warn('No traces yielded a usable action subtree.');
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const agg = (0, aggregate_1.aggregate)({ perTraceDescendants });
|
|
91
|
-
this.log('');
|
|
92
|
-
this.log((0, render_1.renderReport)(agg, {
|
|
93
|
-
identifier,
|
|
94
|
-
dataset,
|
|
95
|
-
lookback,
|
|
96
|
-
samplesRequested: samples,
|
|
97
|
-
samplesObtained: perTraceDescendants.length,
|
|
98
|
-
tracesDropped: dropped,
|
|
99
|
-
totalOrphanCount: totalOrphans,
|
|
100
|
-
preciseRootCount,
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
exports.default = AssessImpact;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { FlatSpan } from '../../api/dash0/types';
|
|
2
|
-
import { Category } from './classify';
|
|
3
|
-
export interface BucketKey {
|
|
4
|
-
category: Category;
|
|
5
|
-
service: string;
|
|
6
|
-
operation: string;
|
|
7
|
-
}
|
|
8
|
-
export interface BucketStats {
|
|
9
|
-
key: BucketKey;
|
|
10
|
-
median: number;
|
|
11
|
-
p95: number;
|
|
12
|
-
max: number;
|
|
13
|
-
coverage: number;
|
|
14
|
-
}
|
|
15
|
-
export interface AggregateInput {
|
|
16
|
-
perTraceDescendants: FlatSpan[][];
|
|
17
|
-
}
|
|
18
|
-
export interface AggregateResult {
|
|
19
|
-
traceCount: number;
|
|
20
|
-
buckets: BucketStats[];
|
|
21
|
-
}
|
|
22
|
-
export declare function aggregate(input: AggregateInput): AggregateResult;
|
|
23
|
-
export declare function percentile(values: number[], p: number): number;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.percentile = exports.aggregate = void 0;
|
|
4
|
-
const classify_1 = require("./classify");
|
|
5
|
-
const keyId = (k) => `${k.category}\x1f${k.service}\x1f${k.operation}`;
|
|
6
|
-
function aggregate(input) {
|
|
7
|
-
const traceCount = input.perTraceDescendants.length;
|
|
8
|
-
// bucketKey -> per-trace count vector (length = traceCount)
|
|
9
|
-
const counts = new Map();
|
|
10
|
-
input.perTraceDescendants.forEach((spans, traceIdx) => {
|
|
11
|
-
for (const span of spans) {
|
|
12
|
-
const c = (0, classify_1.classifySpan)(span);
|
|
13
|
-
if (!c)
|
|
14
|
-
continue;
|
|
15
|
-
bumpBucket(counts, c, traceIdx, traceCount);
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
const buckets = [];
|
|
19
|
-
for (const { key, counts: vec } of counts.values()) {
|
|
20
|
-
buckets.push({
|
|
21
|
-
key,
|
|
22
|
-
median: percentile(vec, 0.5),
|
|
23
|
-
p95: percentile(vec, 0.95),
|
|
24
|
-
max: vec.reduce((a, b) => Math.max(a, b), 0),
|
|
25
|
-
coverage: vec.filter((n) => n > 0).length / Math.max(traceCount, 1),
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
return { traceCount, buckets };
|
|
29
|
-
}
|
|
30
|
-
exports.aggregate = aggregate;
|
|
31
|
-
function bumpBucket(counts, c, traceIdx, traceCount) {
|
|
32
|
-
const k = { category: c.category, service: c.service, operation: c.operation };
|
|
33
|
-
const id = keyId(k);
|
|
34
|
-
let entry = counts.get(id);
|
|
35
|
-
if (!entry) {
|
|
36
|
-
entry = { key: k, counts: new Array(traceCount).fill(0) };
|
|
37
|
-
counts.set(id, entry);
|
|
38
|
-
}
|
|
39
|
-
entry.counts[traceIdx] += 1;
|
|
40
|
-
}
|
|
41
|
-
function percentile(values, p) {
|
|
42
|
-
if (values.length === 0)
|
|
43
|
-
return 0;
|
|
44
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
45
|
-
// Nearest-rank, clamped.
|
|
46
|
-
const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(p * sorted.length) - 1));
|
|
47
|
-
return sorted[idx];
|
|
48
|
-
}
|
|
49
|
-
exports.percentile = percentile;
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const aggregate_1 = require("./aggregate");
|
|
4
|
-
function clientHttp(target, service = 'contrail-items') {
|
|
5
|
-
return {
|
|
6
|
-
name: 'GET',
|
|
7
|
-
traceId: 't',
|
|
8
|
-
spanId: Math.random().toString(36),
|
|
9
|
-
parentSpanId: '',
|
|
10
|
-
kind: 3,
|
|
11
|
-
startNs: 0n,
|
|
12
|
-
endNs: 0n,
|
|
13
|
-
durationNs: 0n,
|
|
14
|
-
attrs: { 'http.request.method': 'GET', 'http.target': target },
|
|
15
|
-
resourceAttrs: { 'service.name': service },
|
|
16
|
-
depthFromAction: 1,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
function dynamoQuery() {
|
|
20
|
-
return {
|
|
21
|
-
name: 'DynamoDB.Query',
|
|
22
|
-
traceId: 't',
|
|
23
|
-
spanId: Math.random().toString(36),
|
|
24
|
-
parentSpanId: '',
|
|
25
|
-
kind: 4,
|
|
26
|
-
startNs: 0n,
|
|
27
|
-
endNs: 0n,
|
|
28
|
-
durationNs: 0n,
|
|
29
|
-
attrs: {},
|
|
30
|
-
resourceAttrs: {},
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
describe('percentile', () => {
|
|
34
|
-
it('handles empty arrays', () => {
|
|
35
|
-
expect((0, aggregate_1.percentile)([], 0.5)).toBe(0);
|
|
36
|
-
});
|
|
37
|
-
it('computes nearest-rank percentiles', () => {
|
|
38
|
-
expect((0, aggregate_1.percentile)([0, 0, 1, 1, 2, 2, 3, 3, 4, 4], 0.5)).toBe(2);
|
|
39
|
-
expect((0, aggregate_1.percentile)([0, 0, 1, 1, 2, 2, 3, 3, 4, 4], 0.95)).toBe(4);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
describe('aggregate', () => {
|
|
43
|
-
it('counts occurrences per trace and computes median/p95/max/coverage', () => {
|
|
44
|
-
// 4 traces; varying fan-out for two HTTP buckets and one dynamo bucket.
|
|
45
|
-
const perTrace = [
|
|
46
|
-
// trace 1: 2 GET /items, 1 PUT /batch, 5 dynamo
|
|
47
|
-
[
|
|
48
|
-
clientHttp('/api/items'),
|
|
49
|
-
clientHttp('/api/items'),
|
|
50
|
-
{ ...clientHttp('/api/items/batch'), name: 'PUT', attrs: { 'http.request.method': 'PUT', 'http.target': '/api/items/batch' } },
|
|
51
|
-
dynamoQuery(),
|
|
52
|
-
dynamoQuery(),
|
|
53
|
-
dynamoQuery(),
|
|
54
|
-
dynamoQuery(),
|
|
55
|
-
dynamoQuery(),
|
|
56
|
-
],
|
|
57
|
-
// trace 2: 4 GET /items, no batch, 2 dynamo
|
|
58
|
-
[
|
|
59
|
-
clientHttp('/api/items'),
|
|
60
|
-
clientHttp('/api/items'),
|
|
61
|
-
clientHttp('/api/items'),
|
|
62
|
-
clientHttp('/api/items'),
|
|
63
|
-
dynamoQuery(),
|
|
64
|
-
dynamoQuery(),
|
|
65
|
-
],
|
|
66
|
-
// trace 3: empty action subtree
|
|
67
|
-
[],
|
|
68
|
-
// trace 4: 1 GET /items
|
|
69
|
-
[clientHttp('/api/items')],
|
|
70
|
-
];
|
|
71
|
-
const result = (0, aggregate_1.aggregate)({ perTraceDescendants: perTrace });
|
|
72
|
-
expect(result.traceCount).toBe(4);
|
|
73
|
-
const items = result.buckets.find((b) => b.key.category === 'http_direct' && b.key.operation === 'GET /api/items');
|
|
74
|
-
expect(items).toBeDefined();
|
|
75
|
-
// counts vector [2,4,0,1] sorted [0,1,2,4]; nearest-rank p50=1, p95=4.
|
|
76
|
-
expect(items.median).toBe(1);
|
|
77
|
-
expect(items.p95).toBe(4);
|
|
78
|
-
expect(items.max).toBe(4);
|
|
79
|
-
expect(items.coverage).toBeCloseTo(0.75, 5);
|
|
80
|
-
const dynamo = result.buckets.find((b) => b.key.category === 'dynamodb');
|
|
81
|
-
expect(dynamo).toBeDefined();
|
|
82
|
-
// [5,2,0,0] sorted [0,0,2,5]; nearest-rank p50=0, p95=5.
|
|
83
|
-
expect(dynamo.median).toBe(0);
|
|
84
|
-
expect(dynamo.p95).toBe(5);
|
|
85
|
-
expect(dynamo.max).toBe(5);
|
|
86
|
-
expect(dynamo.coverage).toBeCloseTo(0.5, 5);
|
|
87
|
-
});
|
|
88
|
-
it('skips SERVER spans (classified to null)', () => {
|
|
89
|
-
const result = (0, aggregate_1.aggregate)({
|
|
90
|
-
perTraceDescendants: [
|
|
91
|
-
[
|
|
92
|
-
{
|
|
93
|
-
name: 'GET /prod/api/items',
|
|
94
|
-
traceId: 't',
|
|
95
|
-
spanId: 's',
|
|
96
|
-
parentSpanId: '',
|
|
97
|
-
kind: 2,
|
|
98
|
-
startNs: 0n,
|
|
99
|
-
endNs: 0n,
|
|
100
|
-
durationNs: 0n,
|
|
101
|
-
attrs: { 'http.request.method': 'GET', 'http.target': '/api/items' },
|
|
102
|
-
resourceAttrs: {},
|
|
103
|
-
},
|
|
104
|
-
],
|
|
105
|
-
],
|
|
106
|
-
});
|
|
107
|
-
expect(result.buckets).toHaveLength(0);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { FlatSpan } from '../../api/dash0/types';
|
|
2
|
-
export type Category = 'http_direct' | 'http_transitive' | 'opensearch' | 'dynamodb' | 's3' | 'timestream' | 'lambda' | 'other';
|
|
3
|
-
export declare const CATEGORY_ORDER: Category[];
|
|
4
|
-
export declare const CATEGORY_LABEL: Record<Category, string>;
|
|
5
|
-
export interface Classified {
|
|
6
|
-
category: Category;
|
|
7
|
-
service: string;
|
|
8
|
-
operation: string;
|
|
9
|
-
}
|
|
10
|
-
export declare function classifySpan(span: FlatSpan): Classified | null;
|
|
11
|
-
export declare function templatePath(target: string): string;
|
|
12
|
-
export declare function looksLikeId(seg: string): boolean;
|
|
13
|
-
export declare function normalizeRouteName(name: string): string;
|