dexto 1.1.4 → 1.1.6
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 +480 -0
- package/dist/analytics/constants.d.ts +19 -0
- package/dist/analytics/constants.d.ts.map +1 -0
- package/dist/analytics/constants.js +24 -0
- package/dist/analytics/events.d.ts +112 -0
- package/dist/analytics/events.d.ts.map +1 -0
- package/dist/analytics/events.js +6 -0
- package/dist/analytics/index.d.ts +37 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +145 -0
- package/dist/analytics/state.d.ts +23 -0
- package/dist/analytics/state.d.ts.map +1 -0
- package/dist/analytics/state.js +74 -0
- package/dist/analytics/wrapper.d.ts +11 -0
- package/dist/analytics/wrapper.d.ts.map +1 -0
- package/dist/analytics/wrapper.js +125 -0
- package/dist/cli/cli.d.ts +5 -0
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +10 -4
- package/dist/cli/commands/{interactive-commands/session/helpers → helpers}/formatters.d.ts +1 -1
- package/dist/cli/commands/helpers/formatters.d.ts.map +1 -0
- package/dist/cli/commands/{interactive-commands/session/helpers → helpers}/formatters.js +1 -1
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +56 -2
- package/dist/cli/commands/interactive-commands/session/index.d.ts +1 -1
- package/dist/cli/commands/interactive-commands/session/index.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/session/index.js +1 -1
- package/dist/cli/commands/interactive-commands/session/session-commands.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/session/session-commands.js +10 -76
- package/dist/cli/commands/list-agents.d.ts +2 -2
- package/dist/cli/commands/session-commands.d.ts +28 -0
- package/dist/cli/commands/session-commands.d.ts.map +1 -0
- package/dist/cli/commands/session-commands.js +184 -0
- package/dist/cli/commands/setup.d.ts +2 -2
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +9 -0
- package/dist/cli/commands/uninstall.d.ts.map +1 -1
- package/dist/cli/commands/uninstall.js +42 -1
- package/dist/cli/utils/api-key-setup.js +1 -1
- package/dist/index.js +271 -75
- package/dist/webui/.next/standalone/.next/static/chunks/854-2a6d5a5297a15d52.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/app/{layout-615a56c6184a488f.js → layout-dde711766eda096b.js} +1 -1
- package/dist/webui/.next/standalone/.next/static/chunks/app/{page-24123c97236d46cb.js → page-cf95b233c1df6dcd.js} +1 -1
- package/dist/webui/.next/standalone/.next/static/css/9cdfb06589a2f6ce.css +3 -0
- package/dist/webui/.next/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- package/dist/webui/.next/standalone/package.json +2 -1
- package/dist/webui/.next/standalone/packages/webui/.next/BUILD_ID +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/app-build-manifest.json +5 -7
- package/dist/webui/.next/standalone/packages/webui/.next/build-manifest.json +2 -2
- package/dist/webui/.next/standalone/packages/webui/.next/prerender-manifest.json +3 -3
- package/dist/webui/.next/standalone/packages/webui/.next/required-server-files.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/_not-found/page.js +2 -2
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/page.js +3 -3
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/page.js.nft.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/playground/page.js +3 -3
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/playground/page.js.nft.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/playground/page_client-reference-manifest.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/43.js +1 -4
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/619.js +30 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/next-font-manifest.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/next-font-manifest.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/pages/500.html +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/pages/_error.js +2 -2
- package/dist/webui/.next/standalone/packages/webui/.next/server/server-reference-manifest.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/854-2a6d5a5297a15d52.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/app/{layout-615a56c6184a488f.js → layout-dde711766eda096b.js} +1 -1
- package/dist/webui/.next/{static/chunks/app/page-24123c97236d46cb.js → standalone/packages/webui/.next/static/chunks/app/page-cf95b233c1df6dcd.js} +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/css/9cdfb06589a2f6ce.css +3 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- package/dist/webui/.next/standalone/packages/webui/package.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/public/logos/dexto/dexto_logo.svg +31 -0
- package/dist/webui/.next/standalone/packages/webui/public/logos/dexto/dexto_logo_icon.svg +14 -0
- package/dist/webui/.next/standalone/packages/webui/public/logos/dexto/dexto_logo_icon_light.svg +17 -0
- package/dist/webui/.next/standalone/packages/webui/public/logos/dexto/dexto_logo_light.svg +31 -0
- package/dist/webui/.next/standalone/packages/webui/server.js +1 -1
- package/dist/webui/.next/standalone/public/logos/dexto/dexto_logo.svg +31 -0
- package/dist/webui/.next/standalone/public/logos/dexto/dexto_logo_icon.svg +14 -0
- package/dist/webui/.next/standalone/public/logos/dexto/dexto_logo_icon_light.svg +17 -0
- package/dist/webui/.next/standalone/public/logos/dexto/dexto_logo_light.svg +31 -0
- package/dist/webui/.next/static/chunks/854-2a6d5a5297a15d52.js +1 -0
- package/dist/webui/.next/static/chunks/app/{layout-615a56c6184a488f.js → layout-dde711766eda096b.js} +1 -1
- package/dist/webui/.next/{standalone/packages/webui/.next/static/chunks/app/page-24123c97236d46cb.js → static/chunks/app/page-cf95b233c1df6dcd.js} +1 -1
- package/dist/webui/.next/static/css/9cdfb06589a2f6ce.css +3 -0
- package/dist/webui/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- package/dist/webui/package.json +1 -1
- package/dist/webui/public/logos/dexto/dexto_logo.svg +31 -0
- package/dist/webui/public/logos/dexto/dexto_logo_icon.svg +14 -0
- package/dist/webui/public/logos/dexto/dexto_logo_icon_light.svg +17 -0
- package/dist/webui/public/logos/dexto/dexto_logo_light.svg +31 -0
- package/package.json +7 -4
- package/dist/cli/commands/interactive-commands/session/helpers/formatters.d.ts.map +0 -1
- package/dist/webui/.next/standalone/.next/static/chunks/221-608218ab04068cb2.js +0 -1
- package/dist/webui/.next/standalone/.next/static/chunks/854-47418382efcea1d4.js +0 -1
- package/dist/webui/.next/standalone/.next/static/css/75b11629ebbc461a.css +0 -3
- package/dist/webui/.next/standalone/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/387.js +0 -14
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/392.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/450.js +0 -139
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/514.js +0 -2
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/531.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/723.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/737.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/767.js +0 -20
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/89.js +0 -95
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/221-608218ab04068cb2.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/854-47418382efcea1d4.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/css/75b11629ebbc461a.css +0 -3
- package/dist/webui/.next/standalone/packages/webui/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
- package/dist/webui/.next/standalone/packages/webui/public/logos/dexto_logo.svg +0 -1
- package/dist/webui/.next/standalone/packages/webui/public/logos/dexto_logo_light.svg +0 -18
- package/dist/webui/.next/standalone/packages/webui/public/logos/dexto_logo_no_text.png +0 -0
- package/dist/webui/.next/standalone/public/logos/dexto_logo.svg +0 -1
- package/dist/webui/.next/standalone/public/logos/dexto_logo_light.svg +0 -18
- package/dist/webui/.next/standalone/public/logos/dexto_logo_no_text.png +0 -0
- package/dist/webui/.next/static/chunks/221-608218ab04068cb2.js +0 -1
- package/dist/webui/.next/static/chunks/854-47418382efcea1d4.js +0 -1
- package/dist/webui/.next/static/css/75b11629ebbc461a.css +0 -3
- package/dist/webui/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
- package/dist/webui/public/logos/dexto_logo.svg +0 -1
- package/dist/webui/public/logos/dexto_logo_light.svg +0 -18
- package/dist/webui/public/logos/dexto_logo_no_text.png +0 -0
- /package/dist/webui/.next/standalone/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_buildManifest.js +0 -0
- /package/dist/webui/.next/standalone/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_ssgManifest.js +0 -0
- /package/dist/webui/.next/standalone/packages/webui/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_buildManifest.js +0 -0
- /package/dist/webui/.next/standalone/packages/webui/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_ssgManifest.js +0 -0
- /package/dist/webui/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_buildManifest.js +0 -0
- /package/dist/webui/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AnalyticsEventName, AnalyticsEventPayload, CliCommandEndEvent, CliCommandStartEvent } from './events.js';
|
|
2
|
+
interface InitOptions {
|
|
3
|
+
appVersion: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Initialize the analytics client for the CLI.
|
|
7
|
+
*
|
|
8
|
+
* - Respects DEXTO_ANALYTICS_DISABLED.
|
|
9
|
+
* - Creates/loads the anonymous distinctId and a per-process session_id.
|
|
10
|
+
* - Emits a dexto_session_start event for each process run.
|
|
11
|
+
*/
|
|
12
|
+
export declare function initAnalytics(opts: InitOptions): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Capture a single analytics event with optional properties.
|
|
15
|
+
* Automatically enriches events with base context (app/os/node/session).
|
|
16
|
+
*/
|
|
17
|
+
export declare function capture<Name extends AnalyticsEventName>(event: Name, properties?: AnalyticsEventPayload<Name>): void;
|
|
18
|
+
/**
|
|
19
|
+
* Attempt a graceful shutdown of the analytics client, flushing queued events.
|
|
20
|
+
*/
|
|
21
|
+
export declare function shutdownAnalytics(): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Mark the start of a command for timing and emit a lightweight start event.
|
|
24
|
+
* Adds local counters as a coarse diagnostic aid.
|
|
25
|
+
*/
|
|
26
|
+
export declare function onCommandStart(name: string, extra?: Partial<Omit<CliCommandStartEvent, 'name' | 'phase'>>): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Mark the end of a command and emit a completion event with success/failure
|
|
29
|
+
* and measured duration. Accepts optional extra properties.
|
|
30
|
+
*/
|
|
31
|
+
export declare function onCommandEnd(name: string, success: boolean, extra?: Partial<Omit<CliCommandEndEvent, 'name' | 'phase' | 'success' | 'durationMs'>>): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Whether analytics are currently enabled for this process.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getEnabled(): boolean;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAQA,OAAO,EACH,kBAAkB,EAClB,qBAAqB,EAErB,kBAAkB,EAClB,oBAAoB,EACvB,MAAM,aAAa,CAAC;AAErB,UAAU,WAAW;IACjB,UAAU,EAAE,MAAM,CAAC;CACtB;AAqBD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCpE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,SAAS,kBAAkB,EACnD,KAAK,EAAE,IAAI,EACX,UAAU,GAAE,qBAAqB,CAAC,IAAI,CAAqC,GAC5E,IAAI,CAWN;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAQvD;AAMD;;;GAGG;AACH,wBAAsB,cAAc,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,CAAM,GAClE,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,KAAK,GAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,CAAC,CAAM,GAC3F,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAEpC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// packages/cli/src/analytics/index.ts
|
|
2
|
+
import { PostHog } from 'posthog-node';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { isAnalyticsDisabled, DEFAULT_POSTHOG_HOST, DEFAULT_POSTHOG_KEY } from './constants.js';
|
|
5
|
+
import { loadState, saveState } from './state.js';
|
|
6
|
+
import { getExecutionContext } from '@dexto/core';
|
|
7
|
+
import { randomUUID } from 'crypto';
|
|
8
|
+
let client = null;
|
|
9
|
+
let enabled = false;
|
|
10
|
+
let state = null;
|
|
11
|
+
let sessionId = null;
|
|
12
|
+
let appVersion = null;
|
|
13
|
+
function baseProps() {
|
|
14
|
+
return {
|
|
15
|
+
app: 'dexto',
|
|
16
|
+
app_version: appVersion || 'unknown',
|
|
17
|
+
node_version: process.version,
|
|
18
|
+
os_platform: os.platform(),
|
|
19
|
+
os_release: os.release(),
|
|
20
|
+
os_arch: os.arch(),
|
|
21
|
+
execution_context: getExecutionContext(),
|
|
22
|
+
session_id: sessionId,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Initialize the analytics client for the CLI.
|
|
27
|
+
*
|
|
28
|
+
* - Respects DEXTO_ANALYTICS_DISABLED.
|
|
29
|
+
* - Creates/loads the anonymous distinctId and a per-process session_id.
|
|
30
|
+
* - Emits a dexto_session_start event for each process run.
|
|
31
|
+
*/
|
|
32
|
+
export async function initAnalytics(opts) {
|
|
33
|
+
if (enabled || client)
|
|
34
|
+
return; // idempotent
|
|
35
|
+
if (isAnalyticsDisabled()) {
|
|
36
|
+
enabled = false;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Load or create state
|
|
40
|
+
state = await loadState();
|
|
41
|
+
sessionId = randomUUID();
|
|
42
|
+
appVersion = opts.appVersion;
|
|
43
|
+
const key = process.env.DEXTO_POSTHOG_KEY ?? DEFAULT_POSTHOG_KEY;
|
|
44
|
+
const host = process.env.DEXTO_POSTHOG_HOST ?? DEFAULT_POSTHOG_HOST;
|
|
45
|
+
if (typeof key !== 'string' || !/^phc_[A-Za-z0-9]+/.test(key) || !host) {
|
|
46
|
+
enabled = false;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
client = new PostHog(key, {
|
|
50
|
+
host,
|
|
51
|
+
flushAt: 1,
|
|
52
|
+
flushInterval: 0,
|
|
53
|
+
disableGeoip: false,
|
|
54
|
+
});
|
|
55
|
+
enabled = true;
|
|
56
|
+
process.on('exit', () => {
|
|
57
|
+
try {
|
|
58
|
+
client?.flush?.();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Ignore flush errors: analytics should never block process exit.
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
capture('dexto_session_start', {});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Capture a single analytics event with optional properties.
|
|
68
|
+
* Automatically enriches events with base context (app/os/node/session).
|
|
69
|
+
*/
|
|
70
|
+
export function capture(event, properties = {}) {
|
|
71
|
+
if (!enabled || !client || !state)
|
|
72
|
+
return;
|
|
73
|
+
try {
|
|
74
|
+
client.capture({
|
|
75
|
+
distinctId: state.distinctId,
|
|
76
|
+
event,
|
|
77
|
+
properties: { ...baseProps(), ...properties },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// swallow
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Attempt a graceful shutdown of the analytics client, flushing queued events.
|
|
86
|
+
*/
|
|
87
|
+
export async function shutdownAnalytics() {
|
|
88
|
+
if (client) {
|
|
89
|
+
try {
|
|
90
|
+
await client.shutdown();
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// ignore
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const timers = new Map();
|
|
98
|
+
/**
|
|
99
|
+
* Mark the start of a command for timing and emit a lightweight start event.
|
|
100
|
+
* Adds local counters as a coarse diagnostic aid.
|
|
101
|
+
*/
|
|
102
|
+
export async function onCommandStart(name, extra = {}) {
|
|
103
|
+
if (!enabled)
|
|
104
|
+
return;
|
|
105
|
+
timers.set(name, Date.now());
|
|
106
|
+
if (state) {
|
|
107
|
+
await saveState(state);
|
|
108
|
+
}
|
|
109
|
+
const payload = {
|
|
110
|
+
name,
|
|
111
|
+
phase: 'start',
|
|
112
|
+
...extra,
|
|
113
|
+
};
|
|
114
|
+
capture('dexto_cli_command', payload);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Mark the end of a command and emit a completion event with success/failure
|
|
118
|
+
* and measured duration. Accepts optional extra properties.
|
|
119
|
+
*/
|
|
120
|
+
export async function onCommandEnd(name, success, extra = {}) {
|
|
121
|
+
if (!enabled)
|
|
122
|
+
return;
|
|
123
|
+
const start = timers.get(name) ?? Date.now();
|
|
124
|
+
const durationMs = Date.now() - start;
|
|
125
|
+
timers.delete(name);
|
|
126
|
+
const payload = {
|
|
127
|
+
name,
|
|
128
|
+
phase: 'end',
|
|
129
|
+
success,
|
|
130
|
+
durationMs,
|
|
131
|
+
...extra,
|
|
132
|
+
};
|
|
133
|
+
capture('dexto_cli_command', payload);
|
|
134
|
+
if (state) {
|
|
135
|
+
state.commandRunCounts = state.commandRunCounts || {};
|
|
136
|
+
state.commandRunCounts[name] = (state.commandRunCounts[name] || 0) + 1;
|
|
137
|
+
await saveState(state);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Whether analytics are currently enabled for this process.
|
|
142
|
+
*/
|
|
143
|
+
export function getEnabled() {
|
|
144
|
+
return enabled;
|
|
145
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shape of the persisted analytics state written to
|
|
3
|
+
* ~/.dexto/telemetry/state.json.
|
|
4
|
+
*
|
|
5
|
+
* - distinctId: Anonymous ID (UUID) for grouping events by machine.
|
|
6
|
+
* - createdAt: ISO timestamp when the state was first created.
|
|
7
|
+
* - commandRunCounts: Local counters per command for coarse diagnostics.
|
|
8
|
+
*/
|
|
9
|
+
export interface AnalyticsState {
|
|
10
|
+
distinctId: string;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
commandRunCounts?: Record<string, number>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Load the persisted analytics state, creating a new file if missing.
|
|
16
|
+
* Returns a valid state object with defaults populated.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadState(): Promise<AnalyticsState>;
|
|
19
|
+
/**
|
|
20
|
+
* Persist the analytics state to ~/.dexto/telemetry/state.json.
|
|
21
|
+
*/
|
|
22
|
+
export declare function saveState(state: AnalyticsState): Promise<void>;
|
|
23
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/analytics/state.ts"],"names":[],"mappings":"AAcA;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7C;AAKD;;;GAGG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC,CAqBzD;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// packages/cli/src/analytics/state.ts
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { randomUUID, createHash } from 'crypto';
|
|
6
|
+
import { createRequire } from 'module';
|
|
7
|
+
const requireCJS = createRequire(import.meta.url);
|
|
8
|
+
// node-machine-id is CommonJS; import via createRequire to avoid ESM interop issues
|
|
9
|
+
const { machineIdSync } = requireCJS('node-machine-id');
|
|
10
|
+
import { getDextoGlobalPath } from '@dexto/core';
|
|
11
|
+
const STATE_DIR = getDextoGlobalPath('telemetry');
|
|
12
|
+
const STATE_FILE = path.join(STATE_DIR, 'state.json');
|
|
13
|
+
/**
|
|
14
|
+
* Load the persisted analytics state, creating a new file if missing.
|
|
15
|
+
* Returns a valid state object with defaults populated.
|
|
16
|
+
*/
|
|
17
|
+
export async function loadState() {
|
|
18
|
+
try {
|
|
19
|
+
const content = await fs.readFile(STATE_FILE, 'utf8');
|
|
20
|
+
const parsed = JSON.parse(content);
|
|
21
|
+
// Validate minimal shape
|
|
22
|
+
if (!parsed.distinctId)
|
|
23
|
+
throw new Error('invalid state');
|
|
24
|
+
return {
|
|
25
|
+
distinctId: parsed.distinctId,
|
|
26
|
+
createdAt: parsed.createdAt || new Date().toISOString(),
|
|
27
|
+
commandRunCounts: parsed.commandRunCounts ?? {},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
await fs.mkdir(STATE_DIR, { recursive: true });
|
|
32
|
+
const state = {
|
|
33
|
+
distinctId: computeDistinctId(),
|
|
34
|
+
createdAt: new Date().toISOString(),
|
|
35
|
+
commandRunCounts: {},
|
|
36
|
+
};
|
|
37
|
+
await saveState(state);
|
|
38
|
+
return state;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Persist the analytics state to ~/.dexto/telemetry/state.json.
|
|
43
|
+
*/
|
|
44
|
+
export async function saveState(state) {
|
|
45
|
+
await fs.mkdir(STATE_DIR, { recursive: true });
|
|
46
|
+
await fs.writeFile(STATE_FILE, JSON.stringify(state, null, 2), 'utf8');
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Compute a stable, privacy‑safe machine identifier so identity
|
|
50
|
+
* survives ~/.dexto deletion by default.
|
|
51
|
+
*
|
|
52
|
+
* Strategy:
|
|
53
|
+
* - Prefer node-machine-id (hashed), which abstracts platform differences.
|
|
54
|
+
* - Fallback to a salted/hashed hostname.
|
|
55
|
+
* - As a last resort, generate a random UUID.
|
|
56
|
+
*/
|
|
57
|
+
function computeDistinctId() {
|
|
58
|
+
try {
|
|
59
|
+
// machineIdSync(true) returns a hashed, stable identifier
|
|
60
|
+
const id = machineIdSync(true);
|
|
61
|
+
if (typeof id === 'string' && id.length > 0)
|
|
62
|
+
return `DEXTO-${id}`;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// fall through to hostname hash
|
|
66
|
+
}
|
|
67
|
+
// Fallback: hash hostname to avoid exposing raw value
|
|
68
|
+
const hostname = os.hostname() || 'unknown-host';
|
|
69
|
+
const digest = createHash('sha256').update(hostname).digest('hex');
|
|
70
|
+
if (digest)
|
|
71
|
+
return `DEXTO-${digest.slice(0, 32)}`;
|
|
72
|
+
// Last resort
|
|
73
|
+
return `DEXTO-${randomUUID()}`;
|
|
74
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function withAnalytics<A extends unknown[], R = unknown>(commandName: string, handler: (...args: A) => Promise<R> | R, opts?: {
|
|
2
|
+
timeoutMs?: number;
|
|
3
|
+
}): (...args: A) => Promise<R>;
|
|
4
|
+
export declare class ExitSignal extends Error {
|
|
5
|
+
code: number;
|
|
6
|
+
reason?: string | undefined;
|
|
7
|
+
commandName?: string | undefined;
|
|
8
|
+
constructor(code?: number, reason?: string, commandName?: string);
|
|
9
|
+
}
|
|
10
|
+
export declare function safeExit(commandName: string, code?: number, reason?: string): never;
|
|
11
|
+
//# sourceMappingURL=wrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../src/analytics/wrapper.ts"],"names":[],"mappings":"AAkDA,wBAAgB,aAAa,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,CAAC,GAAG,OAAO,EAC1D,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACvC,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9B,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CA+D5B;AAED,qBAAa,UAAW,SAAQ,KAAK;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;gBACrB,IAAI,GAAE,MAAU,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;CAOtE;AAED,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAEtF"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// packages/cli/src/analytics/wrapper.ts
|
|
2
|
+
import { onCommandStart, onCommandEnd, capture } from './index.js';
|
|
3
|
+
import { COMMAND_TIMEOUT_MS } from './constants.js';
|
|
4
|
+
function sanitizeOptions(obj) {
|
|
5
|
+
const redactedKeys = /key|token|secret|password|api[_-]?key|authorization|auth/i;
|
|
6
|
+
const truncate = (s, max = 256) => (s.length > max ? s.slice(0, max) + '…' : s);
|
|
7
|
+
const out = {};
|
|
8
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
9
|
+
if (typeof v === 'string') {
|
|
10
|
+
out[k] = redactedKeys.test(k) ? '[REDACTED]' : truncate(v);
|
|
11
|
+
}
|
|
12
|
+
else if (Array.isArray(v)) {
|
|
13
|
+
out[k] = { type: 'array', length: v.length };
|
|
14
|
+
}
|
|
15
|
+
else if (typeof v === 'number' || typeof v === 'boolean' || v === null) {
|
|
16
|
+
out[k] = v;
|
|
17
|
+
}
|
|
18
|
+
else if (typeof v === 'object' && v) {
|
|
19
|
+
out[k] = { type: 'object' };
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
out[k] = String(v ?? 'unknown');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
function buildArgsPayload(args) {
|
|
28
|
+
const meta = {
|
|
29
|
+
argTypes: args.map((a) => (Array.isArray(a) ? 'array' : typeof a)),
|
|
30
|
+
};
|
|
31
|
+
if (args.length > 0 && Array.isArray(args[0])) {
|
|
32
|
+
const list = args[0].map((x) => String(x));
|
|
33
|
+
const trimmed = list.map((s) => (s.length > 512 ? s.slice(0, 512) + '…' : s)).slice(0, 10);
|
|
34
|
+
meta.positionalRaw = trimmed;
|
|
35
|
+
meta.positionalCount = list.length;
|
|
36
|
+
}
|
|
37
|
+
const last = args[args.length - 1];
|
|
38
|
+
if (last && typeof last === 'object' && !Array.isArray(last)) {
|
|
39
|
+
meta.optionKeys = Object.keys(last);
|
|
40
|
+
meta.options = sanitizeOptions(last);
|
|
41
|
+
}
|
|
42
|
+
return meta;
|
|
43
|
+
}
|
|
44
|
+
export function withAnalytics(commandName, handler, opts) {
|
|
45
|
+
const timeoutMs = opts?.timeoutMs ?? COMMAND_TIMEOUT_MS;
|
|
46
|
+
return async (...args) => {
|
|
47
|
+
const argsMeta = buildArgsPayload(args);
|
|
48
|
+
await onCommandStart(commandName, { args: argsMeta });
|
|
49
|
+
const timeout = timeoutMs > 0
|
|
50
|
+
? (() => {
|
|
51
|
+
const t = setTimeout(() => {
|
|
52
|
+
try {
|
|
53
|
+
const payload = {
|
|
54
|
+
name: commandName,
|
|
55
|
+
phase: 'timeout',
|
|
56
|
+
timeoutMs,
|
|
57
|
+
args: argsMeta,
|
|
58
|
+
};
|
|
59
|
+
capture('dexto_cli_command', payload);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Timeout instrumentation is best-effort.
|
|
63
|
+
}
|
|
64
|
+
}, timeoutMs);
|
|
65
|
+
// Prevent timeout from keeping process alive
|
|
66
|
+
t.unref();
|
|
67
|
+
return t;
|
|
68
|
+
})()
|
|
69
|
+
: null;
|
|
70
|
+
try {
|
|
71
|
+
const result = await handler(...args);
|
|
72
|
+
const success = (typeof process.exitCode === 'number' ? process.exitCode : 0) === 0;
|
|
73
|
+
await onCommandEnd(commandName, success, { args: argsMeta });
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
if (err instanceof ExitSignal) {
|
|
78
|
+
process.exitCode = err.code ?? 0;
|
|
79
|
+
try {
|
|
80
|
+
const endMeta = { args: argsMeta };
|
|
81
|
+
if (typeof err.reason === 'string') {
|
|
82
|
+
endMeta.reason = err.reason;
|
|
83
|
+
}
|
|
84
|
+
if (err.commandName) {
|
|
85
|
+
endMeta.command = err.commandName;
|
|
86
|
+
}
|
|
87
|
+
await onCommandEnd(commandName, err.code === 0, endMeta);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Ignore analytics errors when propagating ExitSignal.
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
await onCommandEnd(commandName, false, {
|
|
96
|
+
error: err instanceof Error ? err.message : String(err),
|
|
97
|
+
args: argsMeta,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Ignore analytics errors when recording failures.
|
|
102
|
+
}
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
if (timeout)
|
|
107
|
+
clearTimeout(timeout);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export class ExitSignal extends Error {
|
|
112
|
+
code;
|
|
113
|
+
reason;
|
|
114
|
+
commandName;
|
|
115
|
+
constructor(code = 0, reason, commandName) {
|
|
116
|
+
super('ExitSignal');
|
|
117
|
+
this.name = 'ExitSignal';
|
|
118
|
+
this.code = code;
|
|
119
|
+
this.reason = reason;
|
|
120
|
+
this.commandName = commandName;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export function safeExit(commandName, code = 0, reason) {
|
|
124
|
+
throw new ExitSignal(code, reason, commandName);
|
|
125
|
+
}
|
package/dist/cli/cli.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { DextoAgent } from '@dexto/core';
|
|
2
|
+
/**
|
|
3
|
+
* Find and load the most recent session based on lastActivity.
|
|
4
|
+
* This provides better UX than always loading the "default" session.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadMostRecentSession(agent: DextoAgent): Promise<void>;
|
|
2
7
|
/**
|
|
3
8
|
* Run the AI CLI with the given LLM service
|
|
4
9
|
* @param agent Dexto agent instance
|
package/dist/cli/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AASzC;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAkC5E;AAgFD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,UAAU,iBAoHjD;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CvF"}
|
package/dist/cli/cli.js
CHANGED
|
@@ -7,11 +7,13 @@ import { executeCommand } from './commands/interactive-commands/commands.js';
|
|
|
7
7
|
import { getDextoPath } from '@dexto/core';
|
|
8
8
|
import { registerGracefulShutdown } from '../utils/graceful-shutdown.js';
|
|
9
9
|
import { DextoRuntimeError, DextoValidationError, ErrorScope, LLMErrorCode } from '@dexto/core';
|
|
10
|
+
import { capture } from '../analytics/index.js';
|
|
11
|
+
import { safeExit } from '../analytics/wrapper.js';
|
|
10
12
|
/**
|
|
11
13
|
* Find and load the most recent session based on lastActivity.
|
|
12
14
|
* This provides better UX than always loading the "default" session.
|
|
13
15
|
*/
|
|
14
|
-
async function loadMostRecentSession(agent) {
|
|
16
|
+
export async function loadMostRecentSession(agent) {
|
|
15
17
|
try {
|
|
16
18
|
const sessionIds = await agent.listSessions();
|
|
17
19
|
if (sessionIds.length === 0) {
|
|
@@ -46,7 +48,7 @@ async function loadMostRecentSession(agent) {
|
|
|
46
48
|
* @param agent The DextoAgent instance providing access to all required services
|
|
47
49
|
*/
|
|
48
50
|
async function _initCli(agent) {
|
|
49
|
-
|
|
51
|
+
// Note: Session loading is now handled by the main CLI logic, not here
|
|
50
52
|
registerGracefulShutdown(agent);
|
|
51
53
|
// Gather startup information
|
|
52
54
|
const llmConfig = agent.getCurrentLLMConfig();
|
|
@@ -147,6 +149,8 @@ export async function startAiCli(agent) {
|
|
|
147
149
|
}
|
|
148
150
|
else {
|
|
149
151
|
// Handle regular prompt - pass to AI
|
|
152
|
+
const llm = agent.getCurrentLLMConfig();
|
|
153
|
+
capture('dexto_prompt', { mode: 'cli', provider: llm.provider, model: llm.model });
|
|
150
154
|
return false;
|
|
151
155
|
}
|
|
152
156
|
}
|
|
@@ -211,7 +215,7 @@ export async function startAiCli(agent) {
|
|
|
211
215
|
catch (error) {
|
|
212
216
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
213
217
|
logger.error(`Error during CLI initialization: ${errorMessage}`);
|
|
214
|
-
|
|
218
|
+
safeExit('main', 1); // Exit with error code if CLI setup fails
|
|
215
219
|
}
|
|
216
220
|
}
|
|
217
221
|
/**
|
|
@@ -237,6 +241,8 @@ export async function startHeadlessCli(agent, prompt) {
|
|
|
237
241
|
// Execute the task as a regular AI prompt
|
|
238
242
|
// uncomment if we need to reset conversation for headless mode
|
|
239
243
|
// await agent.resetConversation();
|
|
244
|
+
const llm = agent.getCurrentLLMConfig();
|
|
245
|
+
capture('dexto_prompt', { mode: 'headless', provider: llm.provider, model: llm.model });
|
|
240
246
|
await agent.run(prompt);
|
|
241
247
|
}
|
|
242
248
|
}
|
|
@@ -256,6 +262,6 @@ export async function startHeadlessCli(agent, prompt) {
|
|
|
256
262
|
else {
|
|
257
263
|
logger.error(`Error in processing input: ${error instanceof Error ? error.message : String(error)}`);
|
|
258
264
|
}
|
|
259
|
-
|
|
265
|
+
safeExit('main', 1); // Exit with error code if headless execution fails
|
|
260
266
|
}
|
|
261
267
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Session Formatting Utilities
|
|
3
3
|
*
|
|
4
4
|
* This module contains formatting functions for session-related CLI output.
|
|
5
|
-
*
|
|
5
|
+
* Shared between interactive and non-interactive session commands.
|
|
6
6
|
*/
|
|
7
7
|
import type { SessionMetadata, InternalMessage } from '@dexto/core';
|
|
8
8
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/helpers/formatters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEpE;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,eAAe,EAC1B,SAAS,GAAE,OAAe,GAC3B,MAAM,CAqBR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA8DpF"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Session Formatting Utilities
|
|
3
3
|
*
|
|
4
4
|
* This module contains formatting functions for session-related CLI output.
|
|
5
|
-
*
|
|
5
|
+
* Shared between interactive and non-interactive session commands.
|
|
6
6
|
*/
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/install.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/install.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;EAOb,CAAC;AAEd,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAwC1E,wBAAsB,oBAAoB,CACtC,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,OAAO,CAAC,qBAAqB,CAAC,GACxC,OAAO,CAAC,IAAI,CAAC,CAsHf"}
|
|
@@ -3,6 +3,7 @@ import { existsSync } from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { getAgentRegistry, getDextoGlobalPath } from '@dexto/core';
|
|
6
|
+
import { capture } from '../../analytics/index.js';
|
|
6
7
|
// Zod schema for install command validation
|
|
7
8
|
const InstallCommandSchema = z
|
|
8
9
|
.object({
|
|
@@ -37,6 +38,7 @@ function validateInstallCommand(agents, options) {
|
|
|
37
38
|
}
|
|
38
39
|
return validated;
|
|
39
40
|
}
|
|
41
|
+
// TODO: move registry code into CLI and move dexto_install_agent metric into registry
|
|
40
42
|
export async function handleInstallCommand(agents, options) {
|
|
41
43
|
// Validate command with Zod
|
|
42
44
|
const validated = validateInstallCommand(agents, options);
|
|
@@ -54,6 +56,9 @@ export async function handleInstallCommand(agents, options) {
|
|
|
54
56
|
let successCount = 0;
|
|
55
57
|
let errorCount = 0;
|
|
56
58
|
const errors = [];
|
|
59
|
+
const installed = [];
|
|
60
|
+
const skipped = [];
|
|
61
|
+
const failed = [];
|
|
57
62
|
// Install each agent
|
|
58
63
|
for (const agentName of agentsToInstall) {
|
|
59
64
|
try {
|
|
@@ -63,21 +68,70 @@ export async function handleInstallCommand(agents, options) {
|
|
|
63
68
|
const installedPath = path.join(globalAgentsDir, agentName);
|
|
64
69
|
if (existsSync(installedPath) && !validated.force) {
|
|
65
70
|
console.log(`⏭️ ${agentName} already installed (use --force to reinstall)`);
|
|
66
|
-
|
|
71
|
+
skipped.push(agentName);
|
|
72
|
+
// Per-agent analytics for skipped install
|
|
73
|
+
capture('dexto_install_agent', {
|
|
74
|
+
agent: agentName,
|
|
75
|
+
status: 'skipped',
|
|
76
|
+
reason: 'already_installed',
|
|
77
|
+
force: validated.force,
|
|
78
|
+
injectPreferences: validated.injectPreferences,
|
|
79
|
+
});
|
|
67
80
|
continue;
|
|
68
81
|
}
|
|
69
82
|
await registry.installAgent(agentName, validated.injectPreferences);
|
|
70
83
|
successCount++;
|
|
71
84
|
console.log(`✅ ${agentName} installed successfully`);
|
|
85
|
+
installed.push(agentName);
|
|
86
|
+
// Per-agent analytics for successful install
|
|
87
|
+
try {
|
|
88
|
+
capture('dexto_install_agent', {
|
|
89
|
+
agent: agentName,
|
|
90
|
+
status: 'installed',
|
|
91
|
+
force: validated.force,
|
|
92
|
+
injectPreferences: validated.injectPreferences,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Analytics failures should not block CLI execution.
|
|
97
|
+
}
|
|
72
98
|
}
|
|
73
99
|
catch (error) {
|
|
74
100
|
errorCount++;
|
|
75
101
|
const errorMsg = `Failed to install ${agentName}: ${error instanceof Error ? error.message : String(error)}`;
|
|
76
102
|
errors.push(errorMsg);
|
|
103
|
+
failed.push(agentName);
|
|
77
104
|
console.error(`❌ ${errorMsg}`);
|
|
105
|
+
// Per-agent analytics for failed install
|
|
106
|
+
try {
|
|
107
|
+
capture('dexto_install_agent', {
|
|
108
|
+
agent: agentName,
|
|
109
|
+
status: 'failed',
|
|
110
|
+
error_message: error instanceof Error ? error.message : String(error),
|
|
111
|
+
force: validated.force,
|
|
112
|
+
injectPreferences: validated.injectPreferences,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Analytics failures should not block CLI execution.
|
|
117
|
+
}
|
|
78
118
|
}
|
|
79
119
|
}
|
|
80
|
-
//
|
|
120
|
+
// Emit analytics for both single- and multi-agent cases
|
|
121
|
+
try {
|
|
122
|
+
capture('dexto_install', {
|
|
123
|
+
requested: agentsToInstall,
|
|
124
|
+
installed,
|
|
125
|
+
skipped,
|
|
126
|
+
failed,
|
|
127
|
+
successCount,
|
|
128
|
+
errorCount,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Analytics failures should not block CLI execution.
|
|
133
|
+
}
|
|
134
|
+
// For single agent operations, throw error if it failed (after emitting analytics)
|
|
81
135
|
if (agentsToInstall.length === 1) {
|
|
82
136
|
if (errorCount > 0) {
|
|
83
137
|
throw new Error(errors[0]);
|
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
* - Formatter utilities from helpers (re-exported for convenience)
|
|
13
13
|
*/
|
|
14
14
|
export { sessionCommand, historyCommand, searchCommand } from './session-commands.js';
|
|
15
|
-
export { formatSessionInfo, formatHistoryMessage } from '
|
|
15
|
+
export { formatSessionInfo, formatHistoryMessage } from '../../helpers/formatters.js';
|
|
16
16
|
export declare const sessionCommands: import("../command-parser.js").CommandDefinition[];
|
|
17
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/interactive-commands/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/interactive-commands/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAItF,eAAO,MAAM,eAAe,oDAAkD,CAAC"}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - Formatter utilities from helpers (re-exported for convenience)
|
|
13
13
|
*/
|
|
14
14
|
export { sessionCommand, historyCommand, searchCommand } from './session-commands.js';
|
|
15
|
-
export { formatSessionInfo, formatHistoryMessage } from '
|
|
15
|
+
export { formatSessionInfo, formatHistoryMessage } from '../../helpers/formatters.js';
|
|
16
16
|
// Export all session commands as a convenient array
|
|
17
17
|
import { sessionCommand, historyCommand, searchCommand } from './session-commands.js';
|
|
18
18
|
export const sessionCommands = [sessionCommand, historyCommand, searchCommand];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-commands.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/interactive-commands/session/session-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"session-commands.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/interactive-commands/session/session-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAwCzD;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,iBA0L5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,iBAwB5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,iBA+H3B,CAAC"}
|