@hypequery/cli 1.22.0 → 1.23.0
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/dist/bin/cli.js +77 -12
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +20 -15
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +25 -5
- package/dist/commands/generate-datasets.d.ts +0 -6
- package/dist/commands/generate-datasets.d.ts.map +1 -1
- package/dist/commands/generate-datasets.js +17 -1
- package/dist/commands/generate-manifest.d.ts.map +1 -1
- package/dist/commands/generate-manifest.js +4 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +14 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +80 -55
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +47 -10
- package/dist/commands/telemetry.d.ts +3 -0
- package/dist/commands/telemetry.d.ts.map +1 -0
- package/dist/commands/telemetry.js +19 -0
- package/dist/generators/chdb.d.ts +3 -0
- package/dist/generators/chdb.d.ts.map +1 -1
- package/dist/generators/chdb.js +2 -0
- package/dist/generators/clickhouse.d.ts +3 -1
- package/dist/generators/clickhouse.d.ts.map +1 -1
- package/dist/generators/clickhouse.js +2 -0
- package/dist/typegen/generate-types.d.ts +3 -0
- package/dist/typegen/generate-types.d.ts.map +1 -1
- package/dist/typegen/generate-types.js +5 -1
- package/dist/typegen/type-parsing.d.ts +1 -1
- package/dist/typegen/type-parsing.d.ts.map +1 -1
- package/dist/typegen/type-parsing.js +11 -7
- package/dist/utils/chdb-gitignore-entry.d.ts +2 -0
- package/dist/utils/chdb-gitignore-entry.d.ts.map +1 -0
- package/dist/utils/chdb-gitignore-entry.js +17 -0
- package/dist/utils/cli-version.d.ts +2 -0
- package/dist/utils/cli-version.d.ts.map +1 -0
- package/dist/utils/cli-version.js +11 -0
- package/dist/utils/cloud-credential-store.d.ts.map +1 -1
- package/dist/utils/cloud-credential-store.js +2 -13
- package/dist/utils/command-exit.d.ts +13 -0
- package/dist/utils/command-exit.d.ts.map +1 -0
- package/dist/utils/command-exit.js +31 -0
- package/dist/utils/config-directory.d.ts +3 -0
- package/dist/utils/config-directory.d.ts.map +1 -0
- package/dist/utils/config-directory.js +14 -0
- package/dist/utils/detect-database-environment.d.ts +3 -0
- package/dist/utils/detect-database-environment.d.ts.map +1 -0
- package/dist/utils/detect-database-environment.js +8 -0
- package/dist/utils/detect-database.d.ts.map +1 -1
- package/dist/utils/detect-database.js +4 -8
- package/dist/utils/init-options.d.ts +11 -0
- package/dist/utils/init-options.d.ts.map +1 -0
- package/dist/utils/init-options.js +28 -0
- package/dist/utils/load-api.d.ts +2 -0
- package/dist/utils/load-api.d.ts.map +1 -1
- package/dist/utils/load-api.js +50 -51
- package/dist/utils/load-env-files.d.ts +10 -0
- package/dist/utils/load-env-files.d.ts.map +1 -0
- package/dist/utils/load-env-files.js +27 -0
- package/dist/utils/project-root.d.ts +14 -0
- package/dist/utils/project-root.d.ts.map +1 -0
- package/dist/utils/project-root.js +41 -0
- package/dist/utils/telemetry/buckets.d.ts +5 -0
- package/dist/utils/telemetry/buckets.d.ts.map +1 -0
- package/dist/utils/telemetry/buckets.js +23 -0
- package/dist/utils/telemetry/catalog-docs.d.ts +3 -0
- package/dist/utils/telemetry/catalog-docs.d.ts.map +1 -0
- package/dist/utils/telemetry/catalog-docs.js +52 -0
- package/dist/utils/telemetry/catalog.d.ts +1124 -0
- package/dist/utils/telemetry/catalog.d.ts.map +1 -0
- package/dist/utils/telemetry/catalog.js +154 -0
- package/dist/utils/telemetry/command-context.d.ts +17 -0
- package/dist/utils/telemetry/command-context.d.ts.map +1 -0
- package/dist/utils/telemetry/command-context.js +30 -0
- package/dist/utils/telemetry/config-schema.d.ts +12 -0
- package/dist/utils/telemetry/config-schema.d.ts.map +1 -0
- package/dist/utils/telemetry/config-schema.js +33 -0
- package/dist/utils/telemetry/config-store.d.ts +35 -0
- package/dist/utils/telemetry/config-store.d.ts.map +1 -0
- package/dist/utils/telemetry/config-store.js +147 -0
- package/dist/utils/telemetry/delivery.d.ts +13 -0
- package/dist/utils/telemetry/delivery.d.ts.map +1 -0
- package/dist/utils/telemetry/delivery.js +26 -0
- package/dist/utils/telemetry/detached-dispatch.d.ts +12 -0
- package/dist/utils/telemetry/detached-dispatch.d.ts.map +1 -0
- package/dist/utils/telemetry/detached-dispatch.js +33 -0
- package/dist/utils/telemetry/detached-sender.d.ts +2 -0
- package/dist/utils/telemetry/detached-sender.d.ts.map +1 -0
- package/dist/utils/telemetry/detached-sender.js +31 -0
- package/dist/utils/telemetry/dev-session.d.ts +15 -0
- package/dist/utils/telemetry/dev-session.d.ts.map +1 -0
- package/dist/utils/telemetry/dev-session.js +25 -0
- package/dist/utils/telemetry/domains.d.ts +69 -0
- package/dist/utils/telemetry/domains.d.ts.map +1 -0
- package/dist/utils/telemetry/domains.js +64 -0
- package/dist/utils/telemetry/endpoint.d.ts +3 -0
- package/dist/utils/telemetry/endpoint.d.ts.map +1 -0
- package/dist/utils/telemetry/endpoint.js +15 -0
- package/dist/utils/telemetry/environment.d.ts +27 -0
- package/dist/utils/telemetry/environment.d.ts.map +1 -0
- package/dist/utils/telemetry/environment.js +95 -0
- package/dist/utils/telemetry/error-code.d.ts +4 -0
- package/dist/utils/telemetry/error-code.d.ts.map +1 -0
- package/dist/utils/telemetry/error-code.js +57 -0
- package/dist/utils/telemetry/flags.d.ts +5 -0
- package/dist/utils/telemetry/flags.d.ts.map +1 -0
- package/dist/utils/telemetry/flags.js +41 -0
- package/dist/utils/telemetry/generation-metrics.d.ts +15 -0
- package/dist/utils/telemetry/generation-metrics.d.ts.map +1 -0
- package/dist/utils/telemetry/generation-metrics.js +35 -0
- package/dist/utils/telemetry/identity.d.ts +12 -0
- package/dist/utils/telemetry/identity.d.ts.map +1 -0
- package/dist/utils/telemetry/identity.js +42 -0
- package/dist/utils/telemetry/init-funnel.d.ts +15 -0
- package/dist/utils/telemetry/init-funnel.d.ts.map +1 -0
- package/dist/utils/telemetry/init-funnel.js +25 -0
- package/dist/utils/telemetry/invocation.d.ts +22 -0
- package/dist/utils/telemetry/invocation.d.ts.map +1 -0
- package/dist/utils/telemetry/invocation.js +46 -0
- package/dist/utils/telemetry/lifecycle.d.ts +32 -0
- package/dist/utils/telemetry/lifecycle.d.ts.map +1 -0
- package/dist/utils/telemetry/lifecycle.js +99 -0
- package/dist/utils/telemetry/mcp-session.d.ts +10 -0
- package/dist/utils/telemetry/mcp-session.d.ts.map +1 -0
- package/dist/utils/telemetry/mcp-session.js +36 -0
- package/dist/utils/telemetry/notice.d.ts +11 -0
- package/dist/utils/telemetry/notice.d.ts.map +1 -0
- package/dist/utils/telemetry/notice.js +24 -0
- package/dist/utils/telemetry/opt-out.d.ts +16 -0
- package/dist/utils/telemetry/opt-out.d.ts.map +1 -0
- package/dist/utils/telemetry/opt-out.js +32 -0
- package/dist/utils/telemetry/post-json.d.ts +13 -0
- package/dist/utils/telemetry/post-json.d.ts.map +1 -0
- package/dist/utils/telemetry/post-json.js +32 -0
- package/dist/utils/telemetry/project-context.d.ts +15 -0
- package/dist/utils/telemetry/project-context.d.ts.map +1 -0
- package/dist/utils/telemetry/project-context.js +86 -0
- package/dist/utils/telemetry/schema.d.ts +72 -0
- package/dist/utils/telemetry/schema.d.ts.map +1 -0
- package/dist/utils/telemetry/schema.js +17 -0
- package/dist/utils/telemetry/session-config.d.ts +6 -0
- package/dist/utils/telemetry/session-config.d.ts.map +1 -0
- package/dist/utils/telemetry/session-config.js +17 -0
- package/dist/utils/telemetry/settings-lock.d.ts +36 -0
- package/dist/utils/telemetry/settings-lock.d.ts.map +1 -0
- package/dist/utils/telemetry/settings-lock.js +102 -0
- package/dist/utils/telemetry/transport.d.ts +25 -0
- package/dist/utils/telemetry/transport.d.ts.map +1 -0
- package/dist/utils/telemetry/transport.js +76 -0
- package/dist/utils/telemetry/validation.d.ts +10 -0
- package/dist/utils/telemetry/validation.d.ts.map +1 -0
- package/dist/utils/telemetry/validation.js +77 -0
- package/dist/utils/telemetry/value-formats.d.ts +30 -0
- package/dist/utils/telemetry/value-formats.d.ts.map +1 -0
- package/dist/utils/telemetry/value-formats.js +13 -0
- package/package.json +12 -8
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { COMMON_PROPERTIES, type TelemetryEvent } from './catalog.js';
|
|
2
|
+
import type { Properties } from './schema.js';
|
|
3
|
+
import { type EnvironmentOptions } from './environment.js';
|
|
4
|
+
import { type TelemetryResolutionOptions } from './opt-out.js';
|
|
5
|
+
import { type TransportOptions } from './transport.js';
|
|
6
|
+
export type CommonTelemetryProperties = Properties<typeof COMMON_PROPERTIES>;
|
|
7
|
+
export interface InvocationOptions extends TelemetryResolutionOptions {
|
|
8
|
+
readonly cwd?: string;
|
|
9
|
+
readonly command: string;
|
|
10
|
+
readonly contextOptions?: EnvironmentOptions;
|
|
11
|
+
readonly transportOptions?: Partial<TransportOptions>;
|
|
12
|
+
}
|
|
13
|
+
/** One common context and transport per invocation; no automatic user-code loading. */
|
|
14
|
+
export declare class TelemetryInvocation {
|
|
15
|
+
readonly common: CommonTelemetryProperties;
|
|
16
|
+
private readonly transport;
|
|
17
|
+
private constructor();
|
|
18
|
+
static create(options: InvocationOptions): Promise<TelemetryInvocation | null>;
|
|
19
|
+
record(event: TelemetryEvent): void;
|
|
20
|
+
flush(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=invocation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invocation.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/invocation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG/E,OAAO,EAAyB,KAAK,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAEtF,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAG3E,MAAM,MAAM,yBAAyB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC7E,MAAM,WAAW,iBAAkB,SAAQ,0BAA0B;IACnE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,kBAAkB,CAAC;IAC7C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACvD;AAED,uFAAuF;AACvF,qBAAa,mBAAmB;IAEV,QAAQ,CAAC,MAAM,EAAE,yBAAyB;IAD9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO;WAIM,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAuBpF,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CACvB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { collectEnvironment } from './environment.js';
|
|
2
|
+
import { inspectProject } from './project-context.js';
|
|
3
|
+
import { invocationIdentity } from './identity.js';
|
|
4
|
+
import { resolveTelemetryState } from './opt-out.js';
|
|
5
|
+
import { showTelemetryNotice } from './notice.js';
|
|
6
|
+
import { TelemetryTransport } from './transport.js';
|
|
7
|
+
import { getCliVersion } from '../cli-version.js';
|
|
8
|
+
/** One common context and transport per invocation; no automatic user-code loading. */
|
|
9
|
+
export class TelemetryInvocation {
|
|
10
|
+
common;
|
|
11
|
+
transport;
|
|
12
|
+
constructor(common, options) {
|
|
13
|
+
this.common = common;
|
|
14
|
+
this.transport = new TelemetryTransport(options);
|
|
15
|
+
}
|
|
16
|
+
static async create(options) {
|
|
17
|
+
try {
|
|
18
|
+
const env = options.env ?? process.env;
|
|
19
|
+
// Collection is staged until the first-party proxy and rollout are ready.
|
|
20
|
+
// Normal runs do not inspect projects or create identities just for telemetry.
|
|
21
|
+
if (!env.HYPEQUERY_TELEMETRY_URL && env.HYPEQUERY_TELEMETRY_DEBUG !== '1')
|
|
22
|
+
return null;
|
|
23
|
+
if (options.command === 'telemetry')
|
|
24
|
+
return null;
|
|
25
|
+
const cliVersion = options.cliVersion ?? getCliVersion();
|
|
26
|
+
const state = await resolveTelemetryState({ ...options, cliVersion });
|
|
27
|
+
if (!state.enabled || !state.config)
|
|
28
|
+
return null;
|
|
29
|
+
const project = inspectProject(options.cwd ?? process.cwd(), options.contextOptions?.read);
|
|
30
|
+
const environment = collectEnvironment(project, { ...options.contextOptions, env, cliVersion });
|
|
31
|
+
const common = { ...environment, ...invocationIdentity(state.config, project), is_first_run: state.source === 'default' };
|
|
32
|
+
const invocation = new TelemetryInvocation(common, {
|
|
33
|
+
...options.transportOptions,
|
|
34
|
+
enabled: true, endpoint: env.HYPEQUERY_TELEMETRY_URL,
|
|
35
|
+
debug: env.HYPEQUERY_TELEMETRY_DEBUG === '1',
|
|
36
|
+
});
|
|
37
|
+
await showTelemetryNotice({ ...options, enabled: true, isTTY: common.is_tty, isCI: common.is_ci });
|
|
38
|
+
return invocation;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
record(event) { this.transport.enqueue(event); }
|
|
45
|
+
flush() { return this.transport.flush(); }
|
|
46
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { TelemetryCommand } from './domains.js';
|
|
2
|
+
import { type InvocationOptions } from './invocation.js';
|
|
3
|
+
import { CommandExit } from '../command-exit.js';
|
|
4
|
+
import type { CommandMetrics, SessionCommand, SessionStart, SessionEnd } from './command-context.js';
|
|
5
|
+
import type { ERROR_CODES } from './domains.js';
|
|
6
|
+
export declare class CommandLifecycle {
|
|
7
|
+
private readonly args;
|
|
8
|
+
private readonly options;
|
|
9
|
+
private invocation?;
|
|
10
|
+
private completed;
|
|
11
|
+
private started;
|
|
12
|
+
private interrupted;
|
|
13
|
+
private cancelled;
|
|
14
|
+
private metrics;
|
|
15
|
+
private errorCode?;
|
|
16
|
+
private session?;
|
|
17
|
+
command: TelemetryCommand;
|
|
18
|
+
constructor(args: readonly string[], options?: Omit<InvocationOptions, 'command'>);
|
|
19
|
+
/** Never rejects: telemetry setup must not affect the command it observes. */
|
|
20
|
+
begin(command: string, database?: string): Promise<void>;
|
|
21
|
+
markInterrupted(): void;
|
|
22
|
+
update<C extends TelemetryCommand>(command: C, metrics: CommandMetrics<C>): void;
|
|
23
|
+
cancel(): void;
|
|
24
|
+
setError(code: typeof ERROR_CODES[number]): void;
|
|
25
|
+
startSession<C extends SessionCommand>(command: C, start: SessionStart<C>, end: () => SessionEnd<C>): void;
|
|
26
|
+
/** Never rejects: a telemetry failure must not change the command's exit or skip cleanup. */
|
|
27
|
+
finish(exit: CommandExit, helpTopic?: string): Promise<void>;
|
|
28
|
+
private record;
|
|
29
|
+
/** Node retains its native fatal-error output/exit; delivery here is best-effort. */
|
|
30
|
+
crash(error: unknown): void;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/lifecycle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACrG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,qBAAa,gBAAgB;IAWf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAqB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAV9E,OAAO,CAAC,UAAU,CAAC,CAA6B;IAChD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,SAAS,CAAC,CAA6B;IAC/C,OAAO,CAAC,OAAO,CAAC,CAAkE;IAClF,OAAO,EAAE,gBAAgB,CAAa;gBAET,IAAI,EAAE,SAAS,MAAM,EAAE,EAAmB,OAAO,GAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAM;IAEvH,8EAA8E;IACxE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY9D,eAAe,IAAI,IAAI;IAEvB,MAAM,CAAC,CAAC,SAAS,gBAAgB,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI;IAKhF,MAAM,IAAI,IAAI;IAEd,QAAQ,CAAC,IAAI,EAAE,OAAO,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI;IAEhD,YAAY,CAAC,CAAC,SAAS,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;IAM1G,6FAA6F;IACvF,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YASpD,MAAM;IAyBpB,qFAAqF;IACrF,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;CAQ5B"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { durationBucket } from './buckets.js';
|
|
2
|
+
import { flagNames, knownCommand } from './flags.js';
|
|
3
|
+
import { TelemetryInvocation } from './invocation.js';
|
|
4
|
+
import { CommandExit } from '../command-exit.js';
|
|
5
|
+
import { exceptionClass, telemetryErrorCode } from './error-code.js';
|
|
6
|
+
export class CommandLifecycle {
|
|
7
|
+
args;
|
|
8
|
+
options;
|
|
9
|
+
invocation;
|
|
10
|
+
completed = false;
|
|
11
|
+
started = performance.now();
|
|
12
|
+
interrupted = false;
|
|
13
|
+
cancelled = false;
|
|
14
|
+
metrics = {};
|
|
15
|
+
errorCode;
|
|
16
|
+
session;
|
|
17
|
+
command = 'unknown';
|
|
18
|
+
constructor(args, options = {}) {
|
|
19
|
+
this.args = args;
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
22
|
+
/** Never rejects: telemetry setup must not affect the command it observes. */
|
|
23
|
+
async begin(command, database) {
|
|
24
|
+
try {
|
|
25
|
+
this.command = knownCommand(command);
|
|
26
|
+
if (this.invocation !== undefined)
|
|
27
|
+
return;
|
|
28
|
+
this.started = performance.now();
|
|
29
|
+
this.invocation = await TelemetryInvocation.create({ ...this.options, command: this.command,
|
|
30
|
+
contextOptions: { ...this.options.contextOptions, database } });
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
this.invocation = null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
markInterrupted() { this.interrupted = true; }
|
|
37
|
+
update(command, metrics) {
|
|
38
|
+
if (command !== this.command || this.completed)
|
|
39
|
+
return;
|
|
40
|
+
this.metrics = { ...this.metrics, ...Object.fromEntries(Object.entries(metrics).filter(([, value]) => value !== undefined)) };
|
|
41
|
+
}
|
|
42
|
+
cancel() { this.cancelled = true; }
|
|
43
|
+
setError(code) { this.errorCode = code; }
|
|
44
|
+
startSession(command, start, end) {
|
|
45
|
+
if (!this.invocation || this.completed || this.session || command !== this.command)
|
|
46
|
+
return;
|
|
47
|
+
this.invocation.record({ event: 'cli_session_started', properties: { ...this.invocation.common, ...start, command } });
|
|
48
|
+
this.session = { command, started: performance.now(), end };
|
|
49
|
+
}
|
|
50
|
+
/** Never rejects: a telemetry failure must not change the command's exit or skip cleanup. */
|
|
51
|
+
async finish(exit, helpTopic) {
|
|
52
|
+
if (this.completed)
|
|
53
|
+
return;
|
|
54
|
+
this.completed = true;
|
|
55
|
+
if (!this.invocation)
|
|
56
|
+
return;
|
|
57
|
+
try {
|
|
58
|
+
await this.record(exit, helpTopic);
|
|
59
|
+
}
|
|
60
|
+
catch { /* Delivery is best-effort; the command's own outcome stands. */ }
|
|
61
|
+
}
|
|
62
|
+
async record(exit, helpTopic) {
|
|
63
|
+
if (!this.invocation)
|
|
64
|
+
return;
|
|
65
|
+
const outcome = this.interrupted ? 'interrupted' : this.cancelled && exit.outcome === 'success' ? 'cancelled' : exit.outcome;
|
|
66
|
+
const errorCode = exit.errorCode && exit.errorCode !== 'unknown' ? exit.errorCode : this.errorCode ?? exit.errorCode;
|
|
67
|
+
if (this.session) {
|
|
68
|
+
try {
|
|
69
|
+
this.invocation.record({ event: 'cli_session_ended', properties: {
|
|
70
|
+
...this.invocation.common, ...this.session.end(), command: this.session.command, outcome,
|
|
71
|
+
duration_bucket: durationBucket(performance.now() - this.session.started),
|
|
72
|
+
} });
|
|
73
|
+
}
|
|
74
|
+
catch { /* Broken optional counters must not suppress command completion. */ }
|
|
75
|
+
}
|
|
76
|
+
const properties = {
|
|
77
|
+
...this.invocation.common, ...this.metrics, command: this.command,
|
|
78
|
+
flags_used: flagNames(this.command, this.args),
|
|
79
|
+
outcome,
|
|
80
|
+
duration_bucket: durationBucket(performance.now() - this.started),
|
|
81
|
+
...(errorCode && exit.outcome === 'failure' ? { error_code: errorCode } : exit.errorCode ? { error_code: exit.errorCode } : {}),
|
|
82
|
+
...(this.command === 'help' ? { help_topic: helpTopic ? knownCommand(helpTopic) : 'root' } : {}),
|
|
83
|
+
};
|
|
84
|
+
// Dynamic command discrimination is validated again by the transport.
|
|
85
|
+
this.invocation.record({ event: 'cli_command_completed', properties });
|
|
86
|
+
await this.invocation.flush();
|
|
87
|
+
}
|
|
88
|
+
/** Node retains its native fatal-error output/exit; delivery here is best-effort. */
|
|
89
|
+
crash(error) {
|
|
90
|
+
try {
|
|
91
|
+
if (!this.invocation || this.completed)
|
|
92
|
+
return;
|
|
93
|
+
this.invocation.record({ event: 'cli_crash', properties: { ...this.invocation.common,
|
|
94
|
+
command: this.command, exception_class: exceptionClass(error), error_code: telemetryErrorCode(error) } });
|
|
95
|
+
void this.finish(new CommandExit(1, 'failure', telemetryErrorCode(error))).catch(() => undefined);
|
|
96
|
+
}
|
|
97
|
+
catch { /* Preserve Node's original fatal error even for hostile thrown objects. */ }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MCPToolExecutor } from '@hypequery/mcp';
|
|
2
|
+
import { type SessionStart, type SessionEnd } from './command-context.js';
|
|
3
|
+
export declare class McpSession {
|
|
4
|
+
private calls;
|
|
5
|
+
private errors;
|
|
6
|
+
start(properties: SessionStart<'mcp'>): void;
|
|
7
|
+
instrument(executor: MCPToolExecutor): MCPToolExecutor;
|
|
8
|
+
snapshot(): SessionEnd<'mcp'>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=mcp-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-session.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/mcp-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAuB,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAM/F,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,MAAM,CAAK;IAEnB,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,KAAK,CAAC,GAAG,IAAI;IAI5C,UAAU,CAAC,QAAQ,EAAE,eAAe,GAAG,eAAe;IAetD,QAAQ,IAAI,UAAU,CAAC,KAAK,CAAC;CAI9B"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { countBucket } from './buckets.js';
|
|
2
|
+
import { startCommandSession } from './command-context.js';
|
|
3
|
+
const toolKinds = {
|
|
4
|
+
list_datasets: 'list', get_dataset_schema: 'describe', query_dataset: 'query', query_metric: 'query',
|
|
5
|
+
};
|
|
6
|
+
export class McpSession {
|
|
7
|
+
calls = { list: 0, describe: 0, query: 0 };
|
|
8
|
+
errors = 0;
|
|
9
|
+
start(properties) {
|
|
10
|
+
startCommandSession('mcp', properties, () => this.snapshot());
|
|
11
|
+
}
|
|
12
|
+
instrument(executor) {
|
|
13
|
+
return {
|
|
14
|
+
listTools: () => executor.listTools(), listPrompts: () => executor.listPrompts(),
|
|
15
|
+
getPrompt: (name, args) => executor.getPrompt(name, args), getManifestHash: () => executor.getManifestHash(),
|
|
16
|
+
callTool: async (name, args, signal) => {
|
|
17
|
+
if (Object.hasOwn(toolKinds, name))
|
|
18
|
+
this.calls[toolKinds[name]]++;
|
|
19
|
+
try {
|
|
20
|
+
const result = await executor.callTool(name, args, signal);
|
|
21
|
+
if (result.isError)
|
|
22
|
+
this.errors++;
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
this.errors++;
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
snapshot() {
|
|
33
|
+
return { tool_call_counts: { list: countBucket(this.calls.list), describe: countBucket(this.calls.describe), query: countBucket(this.calls.query) },
|
|
34
|
+
error_count_bucket: countBucket(this.errors) };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type TelemetryConfigDependencies } from './config-store.js';
|
|
2
|
+
export declare const TELEMETRY_NOTICE: string;
|
|
3
|
+
export interface NoticeOptions extends TelemetryConfigDependencies {
|
|
4
|
+
readonly enabled: boolean;
|
|
5
|
+
readonly command: string;
|
|
6
|
+
readonly isTTY: boolean;
|
|
7
|
+
readonly isCI: boolean;
|
|
8
|
+
readonly write?: (text: string) => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare function showTelemetryNotice(options: NoticeOptions): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=notice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notice.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/notice.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAE3F,eAAO,MAAM,gBAAgB,QAMjB,CAAC;AAEb,MAAM,WAAW,aAAc,SAAQ,2BAA2B;IAChE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAU/E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TELEMETRY_DOCS_URL } from './config-schema.js';
|
|
2
|
+
import { TelemetryConfigStore } from './config-store.js';
|
|
3
|
+
export const TELEMETRY_NOTICE = [
|
|
4
|
+
'Hypequery collects pseudonymous CLI usage to prioritise the roadmap.',
|
|
5
|
+
'Never code, SQL, names, paths or credentials.',
|
|
6
|
+
'Disable: hypequery telemetry disable, HYPEQUERY_TELEMETRY_DISABLED=1, or DO_NOT_TRACK=1.',
|
|
7
|
+
`Details: ${TELEMETRY_DOCS_URL}`,
|
|
8
|
+
'',
|
|
9
|
+
].join('\n');
|
|
10
|
+
export async function showTelemetryNotice(options) {
|
|
11
|
+
if (!options.enabled || !options.isTTY || options.isCI || options.command === 'mcp' || options.command === 'telemetry')
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
await new TelemetryConfigStore(options).showNotice(async () => {
|
|
15
|
+
if (options.write)
|
|
16
|
+
await options.write(TELEMETRY_NOTICE);
|
|
17
|
+
else
|
|
18
|
+
await new Promise((resolve, reject) => {
|
|
19
|
+
process.stderr.write(TELEMETRY_NOTICE, error => error ? reject(error) : resolve());
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch { /* A failed disclosure write must not affect the command. */ }
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type TelemetryConfigDependencies } from './config-store.js';
|
|
2
|
+
import type { TelemetryConfig } from './config-schema.js';
|
|
3
|
+
export type TelemetrySource = 'flag' | 'DO_NOT_TRACK' | 'HYPEQUERY_TELEMETRY_DISABLED' | 'test' | 'hypequery_ci' | 'config' | 'default' | 'config_unavailable' | 'kill_switch';
|
|
4
|
+
export interface TelemetryState {
|
|
5
|
+
readonly enabled: boolean;
|
|
6
|
+
readonly source: TelemetrySource;
|
|
7
|
+
readonly config: TelemetryConfig | null;
|
|
8
|
+
}
|
|
9
|
+
export interface TelemetryResolutionOptions extends TelemetryConfigDependencies {
|
|
10
|
+
readonly telemetry?: boolean;
|
|
11
|
+
readonly cliVersion?: string;
|
|
12
|
+
}
|
|
13
|
+
/** First matching opt-out wins; customer CI is intentionally eligible. */
|
|
14
|
+
export declare function telemetryOptOutSource(options: TelemetryResolutionOptions): TelemetrySource | null;
|
|
15
|
+
export declare function resolveTelemetryState(options?: TelemetryResolutionOptions): Promise<TelemetryState>;
|
|
16
|
+
//# sourceMappingURL=opt-out.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opt-out.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/opt-out.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAG1D,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,cAAc,GAAG,8BAA8B,GAClF,MAAM,GAAG,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,oBAAoB,GAAG,aAAa,CAAC;AAE1F,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,0BAA2B,SAAQ,2BAA2B;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,0BAA0B,GAClC,eAAe,GAAG,IAAI,CAUxB;AAED,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,cAAc,CAAC,CAWzB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { TelemetryConfigStore } from './config-store.js';
|
|
2
|
+
import { getCliVersion } from '../cli-version.js';
|
|
3
|
+
/** First matching opt-out wins; customer CI is intentionally eligible. */
|
|
4
|
+
export function telemetryOptOutSource(options) {
|
|
5
|
+
const env = options.env ?? process.env;
|
|
6
|
+
if (options.telemetry === false)
|
|
7
|
+
return 'flag';
|
|
8
|
+
if (env.DO_NOT_TRACK === '1')
|
|
9
|
+
return 'DO_NOT_TRACK';
|
|
10
|
+
if (env.HYPEQUERY_TELEMETRY_DISABLED === '1')
|
|
11
|
+
return 'HYPEQUERY_TELEMETRY_DISABLED';
|
|
12
|
+
if (env.VITEST || env.NODE_ENV === 'test')
|
|
13
|
+
return 'test';
|
|
14
|
+
if (env.GITHUB_ACTIONS === 'true' && env.GITHUB_REPOSITORY?.toLowerCase() === 'hypequery/hypequery') {
|
|
15
|
+
return 'hypequery_ci';
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
export async function resolveTelemetryState(options = {}) {
|
|
20
|
+
const store = new TelemetryConfigStore(options);
|
|
21
|
+
const source = telemetryOptOutSource(options);
|
|
22
|
+
if (source)
|
|
23
|
+
return { enabled: false, source, config: await store.peek() };
|
|
24
|
+
const existing = await store.peek();
|
|
25
|
+
const config = await store.load();
|
|
26
|
+
if (!config)
|
|
27
|
+
return { enabled: false, source: 'config_unavailable', config: null };
|
|
28
|
+
if (config.disabled_cli_versions?.includes(options.cliVersion ?? getCliVersion())) {
|
|
29
|
+
return { enabled: false, source: 'kill_switch', config };
|
|
30
|
+
}
|
|
31
|
+
return { enabled: config.enabled, source: existing ? 'config' : 'default', config };
|
|
32
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Posts a telemetry body and resolves the HTTP status; a pending send never delays exit. */
|
|
2
|
+
export type PostJson = (url: URL, body: string, signal: AbortSignal) => Promise<number>;
|
|
3
|
+
/**
|
|
4
|
+
* Built-in fetch is not used: aborting it rejects on time, but its in-progress
|
|
5
|
+
* TCP or TLS connect keeps the process alive until the connect timeout (about
|
|
6
|
+
* 10 s for an unreachable host). Here abort destroys the socket, including
|
|
7
|
+
* mid-connect, so the CLI exits once the bounded flush returns. The socket stays
|
|
8
|
+
* referenced until then: unreferencing it would let the process exit before a
|
|
9
|
+
* remote endpoint even connects, silently dropping events within the budget.
|
|
10
|
+
* Node's client never follows redirects, so a 3xx response is simply returned.
|
|
11
|
+
*/
|
|
12
|
+
export declare const postJson: PostJson;
|
|
13
|
+
//# sourceMappingURL=post-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-json.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/post-json.ts"],"names":[],"mappings":"AAGA,6FAA6F;AAC7F,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAExF;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,EAAE,QAiBrB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import https from 'node:https';
|
|
3
|
+
/**
|
|
4
|
+
* Built-in fetch is not used: aborting it rejects on time, but its in-progress
|
|
5
|
+
* TCP or TLS connect keeps the process alive until the connect timeout (about
|
|
6
|
+
* 10 s for an unreachable host). Here abort destroys the socket, including
|
|
7
|
+
* mid-connect, so the CLI exits once the bounded flush returns. The socket stays
|
|
8
|
+
* referenced until then: unreferencing it would let the process exit before a
|
|
9
|
+
* remote endpoint even connects, silently dropping events within the budget.
|
|
10
|
+
* Node's client never follows redirects, so a 3xx response is simply returned.
|
|
11
|
+
*/
|
|
12
|
+
export const postJson = (url, body, signal) => new Promise((resolve, reject) => {
|
|
13
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
14
|
+
const request = client.request(url, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: { 'content-type': 'application/json', 'content-length': Buffer.byteLength(body) },
|
|
17
|
+
agent: false,
|
|
18
|
+
}, response => {
|
|
19
|
+
resolve(response.statusCode ?? 0);
|
|
20
|
+
response.resume();
|
|
21
|
+
request.destroy();
|
|
22
|
+
});
|
|
23
|
+
const abort = () => request.destroy(new Error('Telemetry request aborted'));
|
|
24
|
+
if (signal.aborted) {
|
|
25
|
+
abort();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
29
|
+
request.on('error', reject);
|
|
30
|
+
request.on('close', () => signal.removeEventListener('abort', abort));
|
|
31
|
+
request.end(body);
|
|
32
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type ReadLocalFile = (file: string) => string | undefined;
|
|
2
|
+
export interface ProjectContext {
|
|
3
|
+
readonly directory?: string;
|
|
4
|
+
readonly rootDirectory?: string;
|
|
5
|
+
readonly package?: Record<string, unknown>;
|
|
6
|
+
readonly rootPackage?: Record<string, unknown>;
|
|
7
|
+
readonly remote?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Bounded regular-file reads only; local inspection never shells out or loads user code. */
|
|
10
|
+
export declare function readTelemetryFile(file: string): string | undefined;
|
|
11
|
+
export declare function readPackage(file: string, read: ReadLocalFile): Record<string, unknown> | undefined;
|
|
12
|
+
export declare function remoteFromGitConfig(config: string | undefined): string | undefined;
|
|
13
|
+
/** Finds the workspace root and linked-worktree git config within a small local I/O budget. */
|
|
14
|
+
export declare function inspectProject(cwd: string, read?: ReadLocalFile, budgetMs?: number): ProjectContext;
|
|
15
|
+
//# sourceMappingURL=project-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-context.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/project-context.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AACjE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMlE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAMlG;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAgBlF;AAED,+FAA+F;AAC/F,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,aAAiC,EACvC,QAAQ,SAAI,GACX,cAAc,CA6BhB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { performance } from 'node:perf_hooks';
|
|
4
|
+
/** Bounded regular-file reads only; local inspection never shells out or loads user code. */
|
|
5
|
+
export function readTelemetryFile(file) {
|
|
6
|
+
try {
|
|
7
|
+
const info = statSync(file);
|
|
8
|
+
if (!info.isFile() || info.size > 64 * 1024)
|
|
9
|
+
return undefined;
|
|
10
|
+
return readFileSync(file, 'utf8');
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function readPackage(file, read) {
|
|
17
|
+
try {
|
|
18
|
+
const value = JSON.parse(read(file) ?? 'null');
|
|
19
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
20
|
+
? value : undefined;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function remoteFromGitConfig(config) {
|
|
27
|
+
let remote = false;
|
|
28
|
+
let origin = false;
|
|
29
|
+
let fallback;
|
|
30
|
+
for (const line of (config ?? '').split(/\r?\n/)) {
|
|
31
|
+
const section = line.trim().match(/^\[remote\s+"([^"]+)"\]$/);
|
|
32
|
+
if (section) {
|
|
33
|
+
remote = true;
|
|
34
|
+
origin = section[1] === 'origin';
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (line.trim().startsWith('[')) {
|
|
38
|
+
remote = false;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const url = remote ? line.match(/^\s*url\s*=\s*(.*?)\s*$/i)?.[1] : undefined;
|
|
42
|
+
if (url) {
|
|
43
|
+
const unquoted = url.startsWith('"') && url.endsWith('"') ? url.slice(1, -1) : url;
|
|
44
|
+
if (origin)
|
|
45
|
+
return unquoted;
|
|
46
|
+
fallback ??= unquoted;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return fallback;
|
|
50
|
+
}
|
|
51
|
+
/** Finds the workspace root and linked-worktree git config within a small local I/O budget. */
|
|
52
|
+
export function inspectProject(cwd, read = readTelemetryFile, budgetMs = 5) {
|
|
53
|
+
const deadline = performance.now() + budgetMs;
|
|
54
|
+
let context = {};
|
|
55
|
+
try {
|
|
56
|
+
let directory = path.resolve(cwd);
|
|
57
|
+
for (let depth = 0; depth < 24 && performance.now() < deadline; depth++) {
|
|
58
|
+
const pkg = readPackage(path.join(directory, 'package.json'), read);
|
|
59
|
+
if (!context.directory && pkg)
|
|
60
|
+
context = { ...context, directory, package: pkg };
|
|
61
|
+
if (pkg && (pkg.workspaces || read(path.join(directory, 'pnpm-workspace.yaml')) !== undefined)) {
|
|
62
|
+
context = { ...context, rootDirectory: directory, rootPackage: pkg };
|
|
63
|
+
}
|
|
64
|
+
let gitDirectory = path.join(directory, '.git');
|
|
65
|
+
const gitFile = read(gitDirectory)?.match(/^gitdir:\s*(.+?)\s*$/)?.[1];
|
|
66
|
+
if (gitFile)
|
|
67
|
+
gitDirectory = path.resolve(directory, gitFile);
|
|
68
|
+
const common = read(path.join(gitDirectory, 'commondir'))?.trim();
|
|
69
|
+
if (common)
|
|
70
|
+
gitDirectory = path.resolve(gitDirectory, common);
|
|
71
|
+
const gitConfig = read(path.join(gitDirectory, 'config'));
|
|
72
|
+
if (gitConfig !== undefined) {
|
|
73
|
+
context = { ...context, remote: remoteFromGitConfig(gitConfig),
|
|
74
|
+
rootDirectory: context.rootDirectory ?? directory,
|
|
75
|
+
rootPackage: context.rootPackage ?? pkg };
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
const parent = path.dirname(directory);
|
|
79
|
+
if (parent === directory)
|
|
80
|
+
break;
|
|
81
|
+
directory = parent;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch { /* Optional metadata must never affect a command. */ }
|
|
85
|
+
return context;
|
|
86
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { VALUE_FORMATS } from './value-formats.js';
|
|
2
|
+
export type Field = ({
|
|
3
|
+
readonly kind: 'boolean';
|
|
4
|
+
} | {
|
|
5
|
+
readonly kind: 'enum';
|
|
6
|
+
readonly values: readonly (string | number)[];
|
|
7
|
+
} | {
|
|
8
|
+
readonly kind: 'enum_list';
|
|
9
|
+
readonly values: readonly string[];
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: 'format';
|
|
12
|
+
readonly format: keyof typeof VALUE_FORMATS;
|
|
13
|
+
} | {
|
|
14
|
+
readonly kind: 'record';
|
|
15
|
+
readonly fields: Readonly<Record<string, Field>>;
|
|
16
|
+
}) & {
|
|
17
|
+
readonly description: string;
|
|
18
|
+
readonly optional?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type Fields = Readonly<Record<string, Field>>;
|
|
21
|
+
export declare function enumeration<const V extends readonly (string | number)[]>(description: string, values: V): {
|
|
22
|
+
readonly kind: "enum";
|
|
23
|
+
readonly description: string;
|
|
24
|
+
readonly values: V;
|
|
25
|
+
};
|
|
26
|
+
export declare function boolean(description: string): {
|
|
27
|
+
readonly kind: "boolean";
|
|
28
|
+
readonly description: string;
|
|
29
|
+
};
|
|
30
|
+
export declare function enumList<const V extends readonly string[]>(description: string, values: V): {
|
|
31
|
+
readonly kind: "enum_list";
|
|
32
|
+
readonly description: string;
|
|
33
|
+
readonly values: V;
|
|
34
|
+
};
|
|
35
|
+
export declare function formatted<const F extends Extract<Field, {
|
|
36
|
+
kind: 'format';
|
|
37
|
+
}>['format']>(description: string, format: F): {
|
|
38
|
+
readonly kind: "format";
|
|
39
|
+
readonly description: string;
|
|
40
|
+
readonly format: F;
|
|
41
|
+
};
|
|
42
|
+
export declare function record<const F extends Fields>(description: string, fields: F): {
|
|
43
|
+
readonly kind: "record";
|
|
44
|
+
readonly description: string;
|
|
45
|
+
readonly fields: F;
|
|
46
|
+
};
|
|
47
|
+
export declare function optional<const F extends Field>(field: F): F & {
|
|
48
|
+
readonly optional: true;
|
|
49
|
+
};
|
|
50
|
+
export declare function fieldsFor<const K extends readonly string[], const F extends Field>(keys: K, field: F): Record<K[number], F>;
|
|
51
|
+
export type FieldValue<F extends Field> = F extends {
|
|
52
|
+
kind: 'boolean';
|
|
53
|
+
} ? boolean : F extends {
|
|
54
|
+
kind: 'enum';
|
|
55
|
+
values: readonly (infer V)[];
|
|
56
|
+
} ? V : F extends {
|
|
57
|
+
kind: 'enum_list';
|
|
58
|
+
values: readonly (infer V)[];
|
|
59
|
+
} ? readonly V[] : F extends {
|
|
60
|
+
kind: 'record';
|
|
61
|
+
fields: infer R extends Fields;
|
|
62
|
+
} ? Properties<R> : string;
|
|
63
|
+
export type Properties<F extends Fields> = {
|
|
64
|
+
[K in keyof F as F[K] extends {
|
|
65
|
+
optional: true;
|
|
66
|
+
} ? never : K]: FieldValue<F[K]>;
|
|
67
|
+
} & {
|
|
68
|
+
[K in keyof F as F[K] extends {
|
|
69
|
+
optional: true;
|
|
70
|
+
} ? K : never]?: FieldValue<F[K]>;
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function enumeration(description, values) {
|
|
2
|
+
return { kind: 'enum', description, values };
|
|
3
|
+
}
|
|
4
|
+
export function boolean(description) { return { kind: 'boolean', description }; }
|
|
5
|
+
export function enumList(description, values) {
|
|
6
|
+
return { kind: 'enum_list', description, values };
|
|
7
|
+
}
|
|
8
|
+
export function formatted(description, format) {
|
|
9
|
+
return { kind: 'format', description, format };
|
|
10
|
+
}
|
|
11
|
+
export function record(description, fields) {
|
|
12
|
+
return { kind: 'record', description, fields };
|
|
13
|
+
}
|
|
14
|
+
export function optional(field) { return { ...field, optional: true }; }
|
|
15
|
+
export function fieldsFor(keys, field) {
|
|
16
|
+
return Object.fromEntries(keys.map(key => [key, field]));
|
|
17
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DevOptions } from '../../commands/dev.js';
|
|
2
|
+
import type { SessionStart } from './command-context.js';
|
|
3
|
+
/** Explicit file arguments are classified without examining or retaining their path. */
|
|
4
|
+
export declare function entryType(resolved: string, explicit?: string): SessionStart<'dev'>['entry_type'];
|
|
5
|
+
export declare function devSessionConfig(resolved: string, explicit: string | undefined, options: DevOptions): SessionStart<'dev'>;
|
|
6
|
+
//# sourceMappingURL=session-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-config.d.ts","sourceRoot":"","sources":["../../../src/utils/telemetry/session-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAIhG;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,CAQzH"}
|