@aligndottech/cli 0.18.0 → 0.19.1
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 +10 -8
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +18 -1
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/setup.js +1 -1
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/why.d.ts.map +1 -1
- package/dist/commands/why.js +26 -8
- package/dist/commands/why.js.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/hook-payload.js +34 -4
- package/dist/lib/hook-payload.js.map +1 -1
- package/dist/lib/local-db.d.ts +54 -0
- package/dist/lib/local-db.d.ts.map +1 -1
- package/dist/lib/local-db.js +180 -6
- package/dist/lib/local-db.js.map +1 -1
- package/dist/lib/local-embeddings.d.ts.map +1 -1
- package/dist/lib/local-embeddings.js +13 -2
- package/dist/lib/local-embeddings.js.map +1 -1
- package/dist/lib/local-gateway-client.d.ts +36 -1
- package/dist/lib/local-gateway-client.d.ts.map +1 -1
- package/dist/lib/local-gateway-client.js +96 -8
- package/dist/lib/local-gateway-client.js.map +1 -1
- package/dist/lib/local-llm.d.ts +78 -15
- package/dist/lib/local-llm.d.ts.map +1 -1
- package/dist/lib/local-llm.js +327 -88
- package/dist/lib/local-llm.js.map +1 -1
- package/dist/lib/local-relationship-classifier.d.ts +22 -4
- package/dist/lib/local-relationship-classifier.d.ts.map +1 -1
- package/dist/lib/local-relationship-classifier.js +19 -9
- package/dist/lib/local-relationship-classifier.js.map +1 -1
- package/dist/lib/usage-telemetry.d.ts +36 -1
- package/dist/lib/usage-telemetry.d.ts.map +1 -1
- package/dist/lib/usage-telemetry.js +73 -3
- package/dist/lib/usage-telemetry.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type DecisionRelationship } from '@aligndottech/connector-core';
|
|
2
|
+
import { type LlmFailure } from './local-llm.js';
|
|
2
3
|
export declare const RELATIONSHIP_TYPES: readonly ["supersedes", "conflicts_with", "contradicts", "duplicates", "clarifies", "relates", "refines", "supports", "questions", "blocks"];
|
|
3
4
|
export type RelationshipType = DecisionRelationship;
|
|
4
5
|
export interface ClassifiedRelationship {
|
|
@@ -7,9 +8,11 @@ export interface ClassifiedRelationship {
|
|
|
7
8
|
reason?: string;
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
|
-
* Why a candidate edge could not be typed.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Why a candidate edge could not be typed. This enum stays coarse on purpose: since
|
|
12
|
+
* ALI-692 `callChat` CAN tell a timeout from a 429 from an empty body, but the finer
|
|
13
|
+
* detail travels on the outcome's `failure` (provider, model, detail) rather than by adding
|
|
14
|
+
* a reason per failure mode, so presenters name the model without this union growing
|
|
15
|
+
* a member every time a provider invents a new way to fail.
|
|
13
16
|
*/
|
|
14
17
|
export type ClassifierFailureReason =
|
|
15
18
|
/** Nothing to call: no provider key in the environment, no local Ollama. */
|
|
@@ -27,14 +30,29 @@ export type ClassifierFailureReason =
|
|
|
27
30
|
export type ClassificationOutcome = {
|
|
28
31
|
ok: true;
|
|
29
32
|
relationship: ClassifiedRelationship;
|
|
30
|
-
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* `failure` carries WHICH provider and model failed, when one did. It is on the
|
|
36
|
+
* value rather than on a module getter so a presenter reads the diagnosis for the
|
|
37
|
+
* candidate it actually asked about - the reason `align check` could previously
|
|
38
|
+
* report classifier_error with no remedy attached.
|
|
39
|
+
*/
|
|
40
|
+
| {
|
|
31
41
|
ok: false;
|
|
32
42
|
reason: ClassifierFailureReason;
|
|
43
|
+
failure?: LlmFailure;
|
|
33
44
|
};
|
|
34
45
|
interface DecisionLite {
|
|
35
46
|
title: string;
|
|
36
47
|
summary: string;
|
|
37
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Design constraint for any future multi-model adjudication here (ALI-692): if two
|
|
51
|
+
* models ever judge one question (a conflict verdict plus a second opinion, a jury),
|
|
52
|
+
* the pair must span model FAMILIES. A family's leniency is systematic, so same-family
|
|
53
|
+
* agreement is one opinion counted twice, not independent confirmation. Today exactly
|
|
54
|
+
* one model answers, so there is nothing to enforce - this note is the requirement.
|
|
55
|
+
*/
|
|
38
56
|
export declare function classifyRelationship(subject: DecisionLite, candidate: DecisionLite): Promise<ClassificationOutcome>;
|
|
39
57
|
export {};
|
|
40
58
|
//# sourceMappingURL=local-relationship-classifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-relationship-classifier.d.ts","sourceRoot":"","sources":["../../src/lib/local-relationship-classifier.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,oBAAoB,EAG1B,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"local-relationship-classifier.d.ts","sourceRoot":"","sources":["../../src/lib/local-relationship-classifier.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,oBAAoB,EAG1B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAA2C,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAK1F,eAAO,MAAM,kBAAkB,8IAAyB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAEpD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,gBAAgB,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB;AACjC,4EAA4E;AAC1E,YAAY;AACd,sEAAsE;GACpE,kBAAkB;AACpB,sFAAsF;GACpF,wBAAwB;AAC1B;;;;GAIG;GACD,sBAAsB,CAAC;AAE3B,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,sBAAsB,CAAA;CAAE;AACpD;;;;;GAKG;GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,uBAAuB,CAAC;IAAC,OAAO,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC;AAEzE,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAYD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,YAAY,GACtB,OAAO,CAAC,qBAAqB,CAAC,CAoBhC"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// returned a bare null for all three causes, and the caller - unable to tell them
|
|
8
8
|
// apart, or apart from "classified, no conflict" - reported `aligned`.
|
|
9
9
|
import { DECISION_RELATIONSHIPS, DETERMINISTIC_TEMPERATURE, isDecisionRelationship, } from '@aligndottech/connector-core';
|
|
10
|
-
import {
|
|
10
|
+
import { callChatDetailed, hasConfiguredProvider } from './local-llm.js';
|
|
11
11
|
// ALI-219: the canonical decision-graph vocabulary is the single source of truth
|
|
12
12
|
// (connector-core). The local classifier must only emit types the graph accepts,
|
|
13
13
|
// or a local edge is invalid on personal->org sync. Re-exported for callers/tests.
|
|
@@ -19,21 +19,31 @@ const SYSTEM_PROMPT = "You classify how decision B relates to decision A in a so
|
|
|
19
19
|
function buildUserPrompt(a, b) {
|
|
20
20
|
return `Decision A: ${a.title}. ${a.summary}\n\nDecision B: ${b.title}. ${b.summary}`;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Design constraint for any future multi-model adjudication here (ALI-692): if two
|
|
24
|
+
* models ever judge one question (a conflict verdict plus a second opinion, a jury),
|
|
25
|
+
* the pair must span model FAMILIES. A family's leniency is systematic, so same-family
|
|
26
|
+
* agreement is one opinion counted twice, not independent confirmation. Today exactly
|
|
27
|
+
* one model answers, so there is nothing to enforce - this note is the requirement.
|
|
28
|
+
*/
|
|
22
29
|
export async function classifyRelationship(subject, candidate) {
|
|
23
30
|
// ALI-218/219: pin the shared deterministic temperature so the same decision
|
|
24
31
|
// pair types the same way every run - offline relationship detection must be
|
|
25
32
|
// deterministic, and the value is the one both paths share (connector-core).
|
|
26
|
-
const
|
|
33
|
+
const result = await callChatDetailed(SYSTEM_PROMPT, buildUserPrompt(subject, candidate), {
|
|
27
34
|
temperature: DETERMINISTIC_TEMPERATURE,
|
|
28
35
|
});
|
|
29
|
-
if (!
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
if (!result.ok) {
|
|
37
|
+
// Each mapping below reads the failure the call RETURNED, so it describes this
|
|
38
|
+
// candidate's attempt and nothing else. `hasConfiguredProvider()` is still the
|
|
39
|
+
// tiebreak for no_provider, because it is env-only and so is the only thing that
|
|
40
|
+
// can tell "keys exist but every one was rejected" from "nothing is configured".
|
|
41
|
+
const reason = result.failure.kind === 'unrecognised_local_models' ? 'unvetted_local_model'
|
|
42
|
+
: result.failure.kind === 'provider_stopped' ? 'classifier_error'
|
|
43
|
+
: hasConfiguredProvider() ? 'classifier_error' : 'no_llm_key';
|
|
44
|
+
return { ok: false, reason, failure: result.failure };
|
|
35
45
|
}
|
|
36
|
-
const relationship = parseRelationship(
|
|
46
|
+
const relationship = parseRelationship(result.text);
|
|
37
47
|
return relationship ? { ok: true, relationship } : { ok: false, reason: 'classifier_unparseable' };
|
|
38
48
|
}
|
|
39
49
|
function parseRelationship(text) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-relationship-classifier.js","sourceRoot":"","sources":["../../src/lib/local-relationship-classifier.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,kFAAkF;AAClF,kFAAkF;AAClF,6EAA6E;AAC7E,EAAE;AACF,6EAA6E;AAC7E,kFAAkF;AAClF,uEAAuE;AAEvE,OAAO,EACL,sBAAsB,EAEtB,yBAAyB,EACzB,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"local-relationship-classifier.js","sourceRoot":"","sources":["../../src/lib/local-relationship-classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,8 +8,43 @@ import type { EnvironmentConfig } from './config.js';
|
|
|
8
8
|
* contact us and have no tenant, so counting them needs an explicit consent flow and its own
|
|
9
9
|
* storage (`telemetry_events.tenant_id` is `NOT NULL REFERENCES tenants(id)`).
|
|
10
10
|
*
|
|
11
|
-
* Set `ALIGN_TELEMETRY=0` to opt out entirely.
|
|
11
|
+
* Set `ALIGN_TELEMETRY=0` (or `false` / `no` / `off`) to opt out entirely.
|
|
12
12
|
*/
|
|
13
13
|
export declare const TELEMETRY_TIMEOUT_MS = 2000;
|
|
14
14
|
export declare function recordCommandUsage(env: EnvironmentConfig, command: string): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* The `--env` the user actually typed, including one Commander handed to a parent.
|
|
17
|
+
*
|
|
18
|
+
* `align import git --env local` leaves the subcommand's own `opts()` empty, because `--env`
|
|
19
|
+
* is declared on both and Commander resolves that in the parent's favour (align-cli#79, which
|
|
20
|
+
* fixed the same read for the import commands via subcommandOpts). Reading `.opts()` here
|
|
21
|
+
* would send a local command's event to the cloud default - this slice's own bug, one layer up.
|
|
22
|
+
*/
|
|
23
|
+
export declare function envFlagOf(cmd: {
|
|
24
|
+
optsWithGlobals(): Record<string, unknown>;
|
|
25
|
+
}): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the env the command actually addressed, then report against THAT.
|
|
28
|
+
*
|
|
29
|
+
* The postAction hook used to hand over `getEnvironment(getDefaultEnv())`, and
|
|
30
|
+
* `align setup --local` deliberately leaves the default env alone (local-mode.ts), so a
|
|
31
|
+
* machine that had ever run `align login` reported every `ask --env local` to the cloud -
|
|
32
|
+
* the one thing PR #77 said local mode does not do. Only the three-member `local` command
|
|
33
|
+
* group was excluded, and no local user types `align local ask`.
|
|
34
|
+
*
|
|
35
|
+
* `preferLocalEmbedded` is deliberately NOT passed: it is a routing preference for the
|
|
36
|
+
* read commands, and asking for it here would be a second opinion on where the command
|
|
37
|
+
* went. The flag and ALIGN_ENV are what the user chose explicitly, and those are what a
|
|
38
|
+
* consent decision may rest on. Note that preference and this gate are today mutually
|
|
39
|
+
* exclusive by arithmetic rather than by design - the redirect fires only when the cloud env
|
|
40
|
+
* has no token, which is the same condition recordCommandUsage returns on - so if that token
|
|
41
|
+
* check is ever relaxed to count tokenless users, pass the preference here as well.
|
|
42
|
+
*
|
|
43
|
+
* `setup` is suppressed once local-embedded is configured. The interactive "Local only" choice
|
|
44
|
+
* sets no flag, and the default env stays cloud on purpose, so the only evidence the session was
|
|
45
|
+
* local is what the run left behind - and a `setup` that ends with the machine in local mode is
|
|
46
|
+
* a local session. It costs one activation count on a once-per-machine command, in the direction
|
|
47
|
+
* that cannot leak.
|
|
48
|
+
*/
|
|
49
|
+
export declare function recordInvocationUsage(envFlag: string | undefined, command: string): Promise<void>;
|
|
15
50
|
//# sourceMappingURL=usage-telemetry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage-telemetry.d.ts","sourceRoot":"","sources":["../../src/lib/usage-telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,OAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"usage-telemetry.d.ts","sourceRoot":"","sources":["../../src/lib/usage-telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAiB1C,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkD/F;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE;IAAE,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CASjG;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -7,17 +7,36 @@
|
|
|
7
7
|
* contact us and have no tenant, so counting them needs an explicit consent flow and its own
|
|
8
8
|
* storage (`telemetry_events.tenant_id` is `NOT NULL REFERENCES tenants(id)`).
|
|
9
9
|
*
|
|
10
|
-
* Set `ALIGN_TELEMETRY=0` to opt out entirely.
|
|
10
|
+
* Set `ALIGN_TELEMETRY=0` (or `false` / `no` / `off`) to opt out entirely.
|
|
11
11
|
*/
|
|
12
12
|
export const TELEMETRY_TIMEOUT_MS = 2_000;
|
|
13
|
+
/**
|
|
14
|
+
* Set and not recognisably ON means OFF. Enumerating the falsy words instead - `0|false|no|off` -
|
|
15
|
+
* guesses at what a user will type, and every guess that misses is a live send by someone who
|
|
16
|
+
* believes they opted out: `disabled` and `n` both sent. Trimmed, because a trailing space or
|
|
17
|
+
* newline comes free from a `.env` file or a here-doc. Unset is ON, which is the documented
|
|
18
|
+
* cloud default and the only value this cannot see.
|
|
19
|
+
*/
|
|
20
|
+
const OPT_IN_VALUES = new Set(['1', 'true', 'yes', 'on']);
|
|
21
|
+
function telemetryOptedOut() {
|
|
22
|
+
const raw = process.env['ALIGN_TELEMETRY'];
|
|
23
|
+
if (raw === undefined || raw.trim() === '')
|
|
24
|
+
return false;
|
|
25
|
+
return !OPT_IN_VALUES.has(raw.trim().toLowerCase());
|
|
26
|
+
}
|
|
13
27
|
export async function recordCommandUsage(env, command) {
|
|
14
|
-
if (
|
|
28
|
+
if (telemetryOptedOut())
|
|
15
29
|
return;
|
|
16
30
|
// `align local ...` is the explicitly-offline path. Its caller may still hold a cloud token
|
|
17
|
-
// (the hook
|
|
31
|
+
// (the hook may resolve an env other than the one the command used), so the token check below
|
|
18
32
|
// is not enough on its own.
|
|
19
33
|
if (command === 'local' || command.startsWith('local '))
|
|
20
34
|
return;
|
|
35
|
+
// The mode is the consent boundary (PR #77: cloud is opt-out, local is no-phone-home). It has
|
|
36
|
+
// to gate on its own because a token can be in scope without cloud consent: ALIGN_TOKEN
|
|
37
|
+
// exported into the shell, or a logged-in default env resolved by the caller.
|
|
38
|
+
if (env.mode === 'local-embedded')
|
|
39
|
+
return;
|
|
21
40
|
if (!env.authToken || !env.tenantId)
|
|
22
41
|
return;
|
|
23
42
|
// A blackholing proxy hangs rather than rejecting, so a bare await would freeze the CLI after
|
|
@@ -60,4 +79,55 @@ export async function recordCommandUsage(env, command) {
|
|
|
60
79
|
clearTimeout(timer);
|
|
61
80
|
}
|
|
62
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* The `--env` the user actually typed, including one Commander handed to a parent.
|
|
84
|
+
*
|
|
85
|
+
* `align import git --env local` leaves the subcommand's own `opts()` empty, because `--env`
|
|
86
|
+
* is declared on both and Commander resolves that in the parent's favour (align-cli#79, which
|
|
87
|
+
* fixed the same read for the import commands via subcommandOpts). Reading `.opts()` here
|
|
88
|
+
* would send a local command's event to the cloud default - this slice's own bug, one layer up.
|
|
89
|
+
*/
|
|
90
|
+
export function envFlagOf(cmd) {
|
|
91
|
+
const opts = cmd.optsWithGlobals();
|
|
92
|
+
const value = opts['env'];
|
|
93
|
+
if (typeof value === 'string')
|
|
94
|
+
return value;
|
|
95
|
+
// `align setup --local` is how a user ENTERS local mode, and it is not spelled `--env local`,
|
|
96
|
+
// so reading only `env` reported the one command whose whole purpose is going private. Checked
|
|
97
|
+
// after `env` so an explicitly typed cloud env still wins.
|
|
98
|
+
if (opts['local'] === true)
|
|
99
|
+
return 'local';
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Resolve the env the command actually addressed, then report against THAT.
|
|
104
|
+
*
|
|
105
|
+
* The postAction hook used to hand over `getEnvironment(getDefaultEnv())`, and
|
|
106
|
+
* `align setup --local` deliberately leaves the default env alone (local-mode.ts), so a
|
|
107
|
+
* machine that had ever run `align login` reported every `ask --env local` to the cloud -
|
|
108
|
+
* the one thing PR #77 said local mode does not do. Only the three-member `local` command
|
|
109
|
+
* group was excluded, and no local user types `align local ask`.
|
|
110
|
+
*
|
|
111
|
+
* `preferLocalEmbedded` is deliberately NOT passed: it is a routing preference for the
|
|
112
|
+
* read commands, and asking for it here would be a second opinion on where the command
|
|
113
|
+
* went. The flag and ALIGN_ENV are what the user chose explicitly, and those are what a
|
|
114
|
+
* consent decision may rest on. Note that preference and this gate are today mutually
|
|
115
|
+
* exclusive by arithmetic rather than by design - the redirect fires only when the cloud env
|
|
116
|
+
* has no token, which is the same condition recordCommandUsage returns on - so if that token
|
|
117
|
+
* check is ever relaxed to count tokenless users, pass the preference here as well.
|
|
118
|
+
*
|
|
119
|
+
* `setup` is suppressed once local-embedded is configured. The interactive "Local only" choice
|
|
120
|
+
* sets no flag, and the default env stays cloud on purpose, so the only evidence the session was
|
|
121
|
+
* local is what the run left behind - and a `setup` that ends with the machine in local mode is
|
|
122
|
+
* a local session. It costs one activation count on a once-per-machine command, in the direction
|
|
123
|
+
* that cannot leak.
|
|
124
|
+
*/
|
|
125
|
+
export async function recordInvocationUsage(envFlag, command) {
|
|
126
|
+
const { createConfigStore } = await import('./config.js');
|
|
127
|
+
const { resolveEnv } = await import('./resolve-env.js');
|
|
128
|
+
const config = createConfigStore();
|
|
129
|
+
if (command === 'setup' && config.getEnvironment('local').mode === 'local-embedded')
|
|
130
|
+
return;
|
|
131
|
+
await recordCommandUsage(config.getEnvironment(resolveEnv(envFlag)), command);
|
|
132
|
+
}
|
|
63
133
|
//# sourceMappingURL=usage-telemetry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage-telemetry.js","sourceRoot":"","sources":["../../src/lib/usage-telemetry.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAE1C,MAAM,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"usage-telemetry.js","sourceRoot":"","sources":["../../src/lib/usage-telemetry.ts"],"names":[],"mappings":"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"}
|