@principal-ai/principal-studio-cli 0.35.3
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 +172 -0
- package/dist/commands/agent-session.d.ts +3 -0
- package/dist/commands/agent-session.d.ts.map +1 -0
- package/dist/commands/agent-session.js +67 -0
- package/dist/commands/agent-sessions.d.ts +11 -0
- package/dist/commands/agent-sessions.d.ts.map +1 -0
- package/dist/commands/agent-sessions.js +39 -0
- package/dist/commands/auxiliary/index.d.ts +3 -0
- package/dist/commands/auxiliary/index.d.ts.map +1 -0
- package/dist/commands/auxiliary/index.js +9 -0
- package/dist/commands/auxiliary/validate.d.ts +3 -0
- package/dist/commands/auxiliary/validate.d.ts.map +1 -0
- package/dist/commands/auxiliary/validate.js +123 -0
- package/dist/commands/collector/check.d.ts +6 -0
- package/dist/commands/collector/check.d.ts.map +1 -0
- package/dist/commands/collector/check.js +153 -0
- package/dist/commands/collector/diagnose.d.ts +6 -0
- package/dist/commands/collector/diagnose.d.ts.map +1 -0
- package/dist/commands/collector/diagnose.js +229 -0
- package/dist/commands/collector/index.d.ts +6 -0
- package/dist/commands/collector/index.d.ts.map +1 -0
- package/dist/commands/collector/index.js +16 -0
- package/dist/commands/collector/status.d.ts +6 -0
- package/dist/commands/collector/status.d.ts.map +1 -0
- package/dist/commands/collector/status.js +77 -0
- package/dist/commands/collector/utils.d.ts +92 -0
- package/dist/commands/collector/utils.d.ts.map +1 -0
- package/dist/commands/collector/utils.js +154 -0
- package/dist/commands/coverage.d.ts +9 -0
- package/dist/commands/coverage.d.ts.map +1 -0
- package/dist/commands/coverage.js +252 -0
- package/dist/commands/create.d.ts +6 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +50 -0
- package/dist/commands/events/index.d.ts +3 -0
- package/dist/commands/events/index.d.ts.map +1 -0
- package/dist/commands/events/index.js +9 -0
- package/dist/commands/events/validate.d.ts +3 -0
- package/dist/commands/events/validate.d.ts.map +1 -0
- package/dist/commands/events/validate.js +124 -0
- package/dist/commands/formats.d.ts +6 -0
- package/dist/commands/formats.d.ts.map +1 -0
- package/dist/commands/formats.js +1118 -0
- package/dist/commands/hooks.d.ts +9 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +295 -0
- package/dist/commands/inbox.d.ts +13 -0
- package/dist/commands/inbox.d.ts.map +1 -0
- package/dist/commands/inbox.js +179 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +220 -0
- package/dist/commands/lint.d.ts +11 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +71 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +78 -0
- package/dist/commands/migrate-nodes.d.ts +9 -0
- package/dist/commands/migrate-nodes.d.ts.map +1 -0
- package/dist/commands/migrate-nodes.js +363 -0
- package/dist/commands/migrate-scopes-to-canvas.d.ts +3 -0
- package/dist/commands/migrate-scopes-to-canvas.d.ts.map +1 -0
- package/dist/commands/migrate-scopes-to-canvas.js +143 -0
- package/dist/commands/migration.d.ts +6 -0
- package/dist/commands/migration.d.ts.map +1 -0
- package/dist/commands/migration.js +247 -0
- package/dist/commands/open-studio.d.ts +10 -0
- package/dist/commands/open-studio.d.ts.map +1 -0
- package/dist/commands/open-studio.js +32 -0
- package/dist/commands/opencode/fetch.d.ts +3 -0
- package/dist/commands/opencode/fetch.d.ts.map +1 -0
- package/dist/commands/opencode/fetch.js +25 -0
- package/dist/commands/opencode/index.d.ts +3 -0
- package/dist/commands/opencode/index.d.ts.map +1 -0
- package/dist/commands/opencode/index.js +11 -0
- package/dist/commands/opencode/list-sessions.d.ts +3 -0
- package/dist/commands/opencode/list-sessions.d.ts.map +1 -0
- package/dist/commands/opencode/list-sessions.js +20 -0
- package/dist/commands/repo.d.ts +18 -0
- package/dist/commands/repo.d.ts.map +1 -0
- package/dist/commands/repo.js +233 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +367 -0
- package/dist/commands/scopes/index.d.ts +3 -0
- package/dist/commands/scopes/index.d.ts.map +1 -0
- package/dist/commands/scopes/index.js +9 -0
- package/dist/commands/scopes/validate.d.ts +3 -0
- package/dist/commands/scopes/validate.d.ts.map +1 -0
- package/dist/commands/scopes/validate.js +187 -0
- package/dist/commands/setup.d.ts +6 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +406 -0
- package/dist/commands/starred-collections.d.ts +12 -0
- package/dist/commands/starred-collections.d.ts.map +1 -0
- package/dist/commands/starred-collections.js +310 -0
- package/dist/commands/subsystem-model.d.ts +11 -0
- package/dist/commands/subsystem-model.d.ts.map +1 -0
- package/dist/commands/subsystem-model.js +249 -0
- package/dist/commands/topic.d.ts +15 -0
- package/dist/commands/topic.d.ts.map +1 -0
- package/dist/commands/topic.js +683 -0
- package/dist/commands/tour.d.ts +23 -0
- package/dist/commands/tour.d.ts.map +1 -0
- package/dist/commands/tour.js +783 -0
- package/dist/commands/trace/flow.d.ts +6 -0
- package/dist/commands/trace/flow.d.ts.map +1 -0
- package/dist/commands/trace/flow.js +158 -0
- package/dist/commands/trace/index.d.ts +6 -0
- package/dist/commands/trace/index.d.ts.map +1 -0
- package/dist/commands/trace/index.js +20 -0
- package/dist/commands/trace/inspect.d.ts +6 -0
- package/dist/commands/trace/inspect.d.ts.map +1 -0
- package/dist/commands/trace/inspect.js +187 -0
- package/dist/commands/trace/list.d.ts +6 -0
- package/dist/commands/trace/list.d.ts.map +1 -0
- package/dist/commands/trace/list.js +145 -0
- package/dist/commands/trace/registrations.d.ts +6 -0
- package/dist/commands/trace/registrations.d.ts.map +1 -0
- package/dist/commands/trace/registrations.js +81 -0
- package/dist/commands/trace/validate.d.ts +6 -0
- package/dist/commands/trace/validate.d.ts.map +1 -0
- package/dist/commands/trace/validate.js +276 -0
- package/dist/commands/trail.d.ts +36 -0
- package/dist/commands/trail.d.ts.map +1 -0
- package/dist/commands/trail.js +622 -0
- package/dist/commands/validate.d.ts +12 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +3334 -0
- package/dist/commands/workflow/implementation.d.ts +3 -0
- package/dist/commands/workflow/implementation.d.ts.map +1 -0
- package/dist/commands/workflow/implementation.js +51 -0
- package/dist/commands/workflow/index.d.ts +3 -0
- package/dist/commands/workflow/index.d.ts.map +1 -0
- package/dist/commands/workflow/index.js +19 -0
- package/dist/commands/workflow/inspect.d.ts +3 -0
- package/dist/commands/workflow/inspect.d.ts.map +1 -0
- package/dist/commands/workflow/inspect.js +123 -0
- package/dist/commands/workflow/list.d.ts +3 -0
- package/dist/commands/workflow/list.d.ts.map +1 -0
- package/dist/commands/workflow/list.js +95 -0
- package/dist/commands/workflow/render.d.ts +3 -0
- package/dist/commands/workflow/render.d.ts.map +1 -0
- package/dist/commands/workflow/render.js +88 -0
- package/dist/commands/workflow/test.d.ts +3 -0
- package/dist/commands/workflow/test.d.ts.map +1 -0
- package/dist/commands/workflow/test.js +141 -0
- package/dist/commands/workflow/utils.d.ts +49 -0
- package/dist/commands/workflow/utils.d.ts.map +1 -0
- package/dist/commands/workflow/utils.js +164 -0
- package/dist/commands/workflow/validate.d.ts +3 -0
- package/dist/commands/workflow/validate.d.ts.map +1 -0
- package/dist/commands/workflow/validate.js +344 -0
- package/dist/file-utils.d.ts +15 -0
- package/dist/file-utils.d.ts.map +1 -0
- package/dist/file-utils.js +29 -0
- package/dist/index.cjs +285718 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +81 -0
- package/dist/lib/bridge-ipc.d.ts +29 -0
- package/dist/lib/bridge-ipc.d.ts.map +1 -0
- package/dist/lib/bridge-ipc.js +128 -0
- package/dist/lib/github-user.d.ts +17 -0
- package/dist/lib/github-user.d.ts.map +1 -0
- package/dist/lib/github-user.js +53 -0
- package/dist/lib/open-url.d.ts +11 -0
- package/dist/lib/open-url.d.ts.map +1 -0
- package/dist/lib/open-url.js +45 -0
- package/dist/lib/subsystem-model-store.d.ts +74 -0
- package/dist/lib/subsystem-model-store.d.ts.map +1 -0
- package/dist/lib/subsystem-model-store.js +292 -0
- package/dist/lib/tour-cache.d.ts +26 -0
- package/dist/lib/tour-cache.d.ts.map +1 -0
- package/dist/lib/tour-cache.js +46 -0
- package/dist/lib/trail-cache.d.ts +40 -0
- package/dist/lib/trail-cache.d.ts.map +1 -0
- package/dist/lib/trail-cache.js +97 -0
- package/dist/lib/viewer-ipc.d.ts +44 -0
- package/dist/lib/viewer-ipc.d.ts.map +1 -0
- package/dist/lib/viewer-ipc.js +68 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../../src/commands/trace/flow.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCpC,wBAAgB,iBAAiB,IAAI,OAAO,CA4B3C"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trace flow - Check end-to-end trace flow through the pipeline
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { checkHealth, getServiceStats, DEFAULT_COLLECTOR_PORT, } from '../collector/utils.js';
|
|
7
|
+
const DEFAULT_APP_PORT = 3043;
|
|
8
|
+
export function createFlowCommand() {
|
|
9
|
+
const command = new Command('flow');
|
|
10
|
+
command
|
|
11
|
+
.description('Check end-to-end trace flow through the pipeline')
|
|
12
|
+
.option('-p, --port <port>', 'Electron-app port (default: 3043, dev: 3045)')
|
|
13
|
+
.option('--json', 'Output as JSON')
|
|
14
|
+
.action(async (options) => {
|
|
15
|
+
try {
|
|
16
|
+
const appPort = options.port ? parseInt(options.port, 10) : DEFAULT_APP_PORT;
|
|
17
|
+
const diagnostic = await runFlowDiagnostic(appPort);
|
|
18
|
+
if (options.json) {
|
|
19
|
+
console.log(JSON.stringify(diagnostic, null, 2));
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
printFlowDiagnostic(diagnostic);
|
|
23
|
+
}
|
|
24
|
+
if (!diagnostic.healthy) {
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error(chalk.red('Error:'), error.message);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return command;
|
|
34
|
+
}
|
|
35
|
+
async function runFlowDiagnostic(appPort) {
|
|
36
|
+
const issues = [];
|
|
37
|
+
// Check collector
|
|
38
|
+
const collectorHealth = await checkHealth(DEFAULT_COLLECTOR_PORT);
|
|
39
|
+
const collectorHealthy = collectorHealth !== null;
|
|
40
|
+
const collectorStats = collectorHealthy
|
|
41
|
+
? await getServiceStats(DEFAULT_COLLECTOR_PORT)
|
|
42
|
+
: null;
|
|
43
|
+
const collector = {
|
|
44
|
+
healthy: collectorHealthy,
|
|
45
|
+
traceCount: collectorStats?.summary?.totalTraces ?? 0,
|
|
46
|
+
serviceCount: collectorStats?.summary?.totalServices ?? 0,
|
|
47
|
+
aliveServices: collectorStats?.summary?.aliveServices ?? 0,
|
|
48
|
+
endpoint: `http://localhost:${DEFAULT_COLLECTOR_PORT}`,
|
|
49
|
+
};
|
|
50
|
+
if (!collectorHealthy) {
|
|
51
|
+
issues.push('Collector is not reachable');
|
|
52
|
+
}
|
|
53
|
+
// Check electron-app
|
|
54
|
+
let appHealthy = false;
|
|
55
|
+
let appTraceCount = 0;
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch(`http://localhost:${appPort}/health`, {
|
|
58
|
+
signal: AbortSignal.timeout(2000),
|
|
59
|
+
});
|
|
60
|
+
appHealthy = response.ok;
|
|
61
|
+
// Try to get trace count from the app
|
|
62
|
+
if (appHealthy) {
|
|
63
|
+
try {
|
|
64
|
+
const tracesResponse = await fetch(`http://localhost:${appPort}/otel/traces?limit=1`, {
|
|
65
|
+
signal: AbortSignal.timeout(2000),
|
|
66
|
+
});
|
|
67
|
+
if (tracesResponse.ok) {
|
|
68
|
+
const data = (await tracesResponse.json());
|
|
69
|
+
appTraceCount = data.count ?? 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Endpoint may not be available
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
appHealthy = false;
|
|
79
|
+
}
|
|
80
|
+
const app = {
|
|
81
|
+
healthy: appHealthy,
|
|
82
|
+
traceCount: appTraceCount,
|
|
83
|
+
endpoint: `http://localhost:${appPort}`,
|
|
84
|
+
};
|
|
85
|
+
if (!appHealthy) {
|
|
86
|
+
issues.push('Electron-app is not reachable');
|
|
87
|
+
}
|
|
88
|
+
// Determine overall health
|
|
89
|
+
const healthy = collectorHealthy && appHealthy;
|
|
90
|
+
if (collectorHealthy && collector.traceCount > 0 && !appHealthy) {
|
|
91
|
+
issues.push('Traces reaching collector but electron-app is unreachable');
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
collector,
|
|
95
|
+
app,
|
|
96
|
+
healthy,
|
|
97
|
+
issues,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function printFlowDiagnostic(diagnostic) {
|
|
101
|
+
console.log(chalk.bold('\nTrace Flow Diagnostic'));
|
|
102
|
+
console.log('━'.repeat(60));
|
|
103
|
+
// Step 1: Collector
|
|
104
|
+
console.log(chalk.bold('\n 1. Collector'));
|
|
105
|
+
if (diagnostic.collector.healthy) {
|
|
106
|
+
console.log(` Status: ${chalk.green('✓')} healthy`);
|
|
107
|
+
console.log(` Traces: ${diagnostic.collector.traceCount} received`);
|
|
108
|
+
console.log(` Services: ${diagnostic.collector.serviceCount} total, ${diagnostic.collector.aliveServices} active`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.log(` Status: ${chalk.red('✗')} unreachable`);
|
|
112
|
+
}
|
|
113
|
+
// Step 2: Electron-app
|
|
114
|
+
console.log(chalk.bold('\n 2. Electron-app'));
|
|
115
|
+
if (diagnostic.app.healthy) {
|
|
116
|
+
console.log(` Status: ${chalk.green('✓')} healthy`);
|
|
117
|
+
if (diagnostic.app.traceCount > 0) {
|
|
118
|
+
console.log(` Traces: ${diagnostic.app.traceCount} stored`);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
console.log(chalk.gray(' Traces: 0 stored (buffer may be empty)'));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(` Status: ${chalk.red('✗')} unreachable`);
|
|
126
|
+
}
|
|
127
|
+
// Step 3: Flow summary
|
|
128
|
+
console.log(chalk.bold('\n 3. Pipeline'));
|
|
129
|
+
if (diagnostic.healthy) {
|
|
130
|
+
console.log(` Status: ${chalk.green('✓')} both endpoints reachable`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.log(` Status: ${chalk.red('✗')} issues detected`);
|
|
134
|
+
}
|
|
135
|
+
// Issues
|
|
136
|
+
if (diagnostic.issues.length > 0) {
|
|
137
|
+
console.log(chalk.bold('\n Issues:'));
|
|
138
|
+
for (const issue of diagnostic.issues) {
|
|
139
|
+
console.log(chalk.yellow(` • ${issue}`));
|
|
140
|
+
}
|
|
141
|
+
console.log(chalk.bold('\n Troubleshooting:'));
|
|
142
|
+
if (diagnostic.issues.some((i) => i.toLowerCase().includes('collector'))) {
|
|
143
|
+
console.log(chalk.gray(' • Run: principal-ai collector status'));
|
|
144
|
+
console.log(chalk.gray(' • Run: principal-ai collector diagnose'));
|
|
145
|
+
}
|
|
146
|
+
if (diagnostic.issues.some((i) => i.toLowerCase().includes('electron'))) {
|
|
147
|
+
console.log(chalk.gray(' • Check if the Principal desktop app is running'));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
console.log('\n' + '━'.repeat(60));
|
|
151
|
+
if (diagnostic.healthy) {
|
|
152
|
+
console.log(chalk.green('✓ Pipeline is healthy.'));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
console.log(chalk.red(`${diagnostic.issues.length} issue(s) detected.`));
|
|
156
|
+
}
|
|
157
|
+
console.log();
|
|
158
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/trace/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,kBAAkB,IAAI,OAAO,CAY5C"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace commands - List, inspect, and validate traces
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { createListCommand } from './list.js';
|
|
6
|
+
import { createFlowCommand } from './flow.js';
|
|
7
|
+
import { createValidateCommand } from './validate.js';
|
|
8
|
+
import { createInspectCommand } from './inspect.js';
|
|
9
|
+
import { createRegistrationsCommand } from './registrations.js';
|
|
10
|
+
export function createTraceCommand() {
|
|
11
|
+
const command = new Command('trace');
|
|
12
|
+
command
|
|
13
|
+
.description('List, inspect, and validate traces from collector and electron-app')
|
|
14
|
+
.addCommand(createListCommand())
|
|
15
|
+
.addCommand(createFlowCommand())
|
|
16
|
+
.addCommand(createValidateCommand())
|
|
17
|
+
.addCommand(createInspectCommand())
|
|
18
|
+
.addCommand(createRegistrationsCommand());
|
|
19
|
+
return command;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../../src/commands/trace/inspect.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyDpC,wBAAgB,oBAAoB,IAAI,OAAO,CA6B9C"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trace inspect - Show detailed trace information
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
const DEFAULT_APP_PORT = 3043;
|
|
7
|
+
const SPAN_KINDS = {
|
|
8
|
+
0: 'UNSPECIFIED',
|
|
9
|
+
1: 'INTERNAL',
|
|
10
|
+
2: 'SERVER',
|
|
11
|
+
3: 'CLIENT',
|
|
12
|
+
4: 'PRODUCER',
|
|
13
|
+
5: 'CONSUMER',
|
|
14
|
+
};
|
|
15
|
+
const STATUS_CODES = {
|
|
16
|
+
0: 'UNSET',
|
|
17
|
+
1: 'OK',
|
|
18
|
+
2: 'ERROR',
|
|
19
|
+
};
|
|
20
|
+
export function createInspectCommand() {
|
|
21
|
+
const command = new Command('inspect');
|
|
22
|
+
command
|
|
23
|
+
.description('Show detailed information about a specific trace')
|
|
24
|
+
.argument('<traceId>', 'Trace ID to inspect')
|
|
25
|
+
.option('-p, --port <port>', 'Electron-app port (default: 3043, dev: 3045)')
|
|
26
|
+
.option('--json', 'Output as JSON')
|
|
27
|
+
.option('-e, --events', 'Show all events')
|
|
28
|
+
.option('-a, --attributes', 'Show all attributes')
|
|
29
|
+
.action(async (traceId, options) => {
|
|
30
|
+
try {
|
|
31
|
+
const appPort = options.port ? parseInt(options.port, 10) : DEFAULT_APP_PORT;
|
|
32
|
+
// Fetch the specific trace
|
|
33
|
+
const trace = await fetchTrace(appPort, traceId);
|
|
34
|
+
if (options.json) {
|
|
35
|
+
console.log(JSON.stringify(trace, null, 2));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
outputText(trace, options);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error(chalk.red('Error:'), error.message);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return command;
|
|
47
|
+
}
|
|
48
|
+
async function fetchTrace(port, traceId) {
|
|
49
|
+
const url = `http://localhost:${port}/otel/traces/${traceId}`;
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
throw new Error(`Failed to fetch trace: ${response.statusText}`);
|
|
54
|
+
}
|
|
55
|
+
const data = (await response.json());
|
|
56
|
+
if (!data.success || !data.trace) {
|
|
57
|
+
throw new Error(data.error || 'Trace not found');
|
|
58
|
+
}
|
|
59
|
+
return data.trace;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error.name === 'AbortError') {
|
|
63
|
+
throw new Error('Request timeout - electron-app not responding');
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function outputText(trace, options) {
|
|
69
|
+
console.log(chalk.bold('\nTrace Inspection'));
|
|
70
|
+
console.log('━'.repeat(60));
|
|
71
|
+
console.log(chalk.gray('Trace ID:'), chalk.cyan(trace.traceId));
|
|
72
|
+
const resourceSpans = trace.data.resourceSpans ?? [];
|
|
73
|
+
for (const rs of resourceSpans) {
|
|
74
|
+
// Resource info
|
|
75
|
+
const resourceAttrs = rs.resource?.attributes ?? [];
|
|
76
|
+
const serviceName = getAttrValue(resourceAttrs, 'service.name') ?? 'unknown';
|
|
77
|
+
const serviceVersion = getAttrValue(resourceAttrs, 'service.version');
|
|
78
|
+
console.log();
|
|
79
|
+
console.log(chalk.bold('Resource:'), chalk.yellow(serviceName));
|
|
80
|
+
if (serviceVersion) {
|
|
81
|
+
console.log(chalk.gray(' Version:'), serviceVersion);
|
|
82
|
+
}
|
|
83
|
+
if (options.attributes) {
|
|
84
|
+
console.log(chalk.gray(' Attributes:'));
|
|
85
|
+
for (const attr of resourceAttrs) {
|
|
86
|
+
console.log(chalk.gray(` ${attr.key}:`), formatAttrValue(attr.value));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Scope spans
|
|
90
|
+
const scopeSpans = rs.scopeSpans ?? [];
|
|
91
|
+
for (const ss of scopeSpans) {
|
|
92
|
+
const scopeName = ss.scope?.name ?? 'unknown';
|
|
93
|
+
const scopeVersion = ss.scope?.version;
|
|
94
|
+
console.log();
|
|
95
|
+
console.log(chalk.bold(' Scope:'), chalk.magenta(scopeName));
|
|
96
|
+
if (scopeVersion) {
|
|
97
|
+
console.log(chalk.gray(' Version:'), scopeVersion);
|
|
98
|
+
}
|
|
99
|
+
// Spans
|
|
100
|
+
const spans = ss.spans ?? [];
|
|
101
|
+
for (const span of spans) {
|
|
102
|
+
const duration = calculateDuration(span.startTimeUnixNano, span.endTimeUnixNano);
|
|
103
|
+
const kind = SPAN_KINDS[span.kind] ?? 'UNKNOWN';
|
|
104
|
+
const status = STATUS_CODES[span.status?.code ?? 0] ?? 'UNKNOWN';
|
|
105
|
+
const statusColor = span.status?.code === 2 ? chalk.red : span.status?.code === 1 ? chalk.green : chalk.gray;
|
|
106
|
+
console.log();
|
|
107
|
+
console.log(chalk.bold(' Span:'), chalk.blue(span.name));
|
|
108
|
+
console.log(chalk.gray(' Span ID:'), span.spanId);
|
|
109
|
+
if (span.parentSpanId) {
|
|
110
|
+
console.log(chalk.gray(' Parent:'), span.parentSpanId);
|
|
111
|
+
}
|
|
112
|
+
console.log(chalk.gray(' Kind:'), kind);
|
|
113
|
+
console.log(chalk.gray(' Status:'), statusColor(status));
|
|
114
|
+
console.log(chalk.gray(' Duration:'), formatDuration(duration));
|
|
115
|
+
console.log(chalk.gray(' Start:'), formatTimestamp(span.startTimeUnixNano));
|
|
116
|
+
// Span attributes
|
|
117
|
+
const spanAttrs = span.attributes ?? [];
|
|
118
|
+
if (spanAttrs.length > 0 && options.attributes) {
|
|
119
|
+
console.log(chalk.gray(' Attributes:'));
|
|
120
|
+
for (const attr of spanAttrs) {
|
|
121
|
+
console.log(chalk.gray(` ${attr.key}:`), formatAttrValue(attr.value));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Events
|
|
125
|
+
const events = span.events ?? [];
|
|
126
|
+
if (events.length > 0) {
|
|
127
|
+
console.log(chalk.gray(` Events (${events.length}):`));
|
|
128
|
+
const displayEvents = options.events ? events : events.slice(0, 5);
|
|
129
|
+
for (const event of displayEvents) {
|
|
130
|
+
const eventTime = formatTimestamp(event.timeUnixNano);
|
|
131
|
+
console.log(chalk.green(` • ${event.name}`) + chalk.gray(` @ ${eventTime}`));
|
|
132
|
+
if (options.attributes && event.attributes) {
|
|
133
|
+
for (const attr of event.attributes) {
|
|
134
|
+
console.log(chalk.gray(` ${attr.key}:`), formatAttrValue(attr.value));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!options.events && events.length > 5) {
|
|
139
|
+
console.log(chalk.gray(` ... and ${events.length - 5} more (use --events to see all)`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
console.log('\n' + '━'.repeat(60));
|
|
146
|
+
console.log();
|
|
147
|
+
}
|
|
148
|
+
function getAttrValue(attrs, key) {
|
|
149
|
+
const attr = attrs.find((a) => a.key === key);
|
|
150
|
+
if (!attr)
|
|
151
|
+
return undefined;
|
|
152
|
+
const value = attr.value;
|
|
153
|
+
return value.stringValue ?? value.intValue?.toString() ?? value.boolValue?.toString();
|
|
154
|
+
}
|
|
155
|
+
function formatAttrValue(value) {
|
|
156
|
+
if (typeof value === 'object' && value !== null) {
|
|
157
|
+
const v = value;
|
|
158
|
+
if (v.stringValue !== undefined)
|
|
159
|
+
return `"${v.stringValue}"`;
|
|
160
|
+
if (v.intValue !== undefined)
|
|
161
|
+
return String(v.intValue);
|
|
162
|
+
if (v.boolValue !== undefined)
|
|
163
|
+
return String(v.boolValue);
|
|
164
|
+
if (v.arrayValue)
|
|
165
|
+
return JSON.stringify(v.arrayValue.values);
|
|
166
|
+
return JSON.stringify(value);
|
|
167
|
+
}
|
|
168
|
+
return String(value);
|
|
169
|
+
}
|
|
170
|
+
function calculateDuration(startNano, endNano) {
|
|
171
|
+
const start = BigInt(startNano);
|
|
172
|
+
const end = BigInt(endNano);
|
|
173
|
+
return Number((end - start) / BigInt(1_000_000)); // Convert to ms
|
|
174
|
+
}
|
|
175
|
+
function formatDuration(ms) {
|
|
176
|
+
if (ms < 1) {
|
|
177
|
+
return `${(ms * 1000).toFixed(0)}µs`;
|
|
178
|
+
}
|
|
179
|
+
if (ms < 1000) {
|
|
180
|
+
return `${ms.toFixed(2)}ms`;
|
|
181
|
+
}
|
|
182
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
183
|
+
}
|
|
184
|
+
function formatTimestamp(nanoStr) {
|
|
185
|
+
const ms = Number(BigInt(nanoStr) / BigInt(1_000_000));
|
|
186
|
+
return new Date(ms).toISOString();
|
|
187
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/trace/list.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqCpC,wBAAgB,iBAAiB,IAAI,OAAO,CAqI3C"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trace list - List recent traces from electron-app
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { checkHealth, getServiceStats, DEFAULT_COLLECTOR_PORT } from '../collector/utils.js';
|
|
7
|
+
const DEFAULT_APP_PORT = 3043;
|
|
8
|
+
export function createListCommand() {
|
|
9
|
+
const command = new Command('list');
|
|
10
|
+
command
|
|
11
|
+
.description('List recent traces from electron-app')
|
|
12
|
+
.option('-n, --limit <count>', 'Number of traces to show (default: 10)')
|
|
13
|
+
.option('-p, --port <port>', 'Electron-app port (default: 3043, dev: 3045)')
|
|
14
|
+
.option('--json', 'Output as JSON')
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
try {
|
|
17
|
+
const limit = options.limit ? parseInt(options.limit, 10) : 10;
|
|
18
|
+
const appPort = options.port ? parseInt(options.port, 10) : DEFAULT_APP_PORT;
|
|
19
|
+
// Check if electron-app is reachable
|
|
20
|
+
let appHealthy = false;
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch(`http://localhost:${appPort}/health`, {
|
|
23
|
+
signal: AbortSignal.timeout(2000),
|
|
24
|
+
});
|
|
25
|
+
appHealthy = response.ok;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
appHealthy = false;
|
|
29
|
+
}
|
|
30
|
+
// Get collector stats for context
|
|
31
|
+
const collectorHealth = await checkHealth(DEFAULT_COLLECTOR_PORT);
|
|
32
|
+
const collectorHealthy = collectorHealth !== null;
|
|
33
|
+
const collectorStats = collectorHealthy ? await getServiceStats(DEFAULT_COLLECTOR_PORT) : null;
|
|
34
|
+
// Try to fetch traces from electron-app
|
|
35
|
+
let tracesResponse = null;
|
|
36
|
+
if (appHealthy) {
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(`http://localhost:${appPort}/otel/traces?limit=${limit}`, { signal: AbortSignal.timeout(5000) });
|
|
39
|
+
if (response.ok) {
|
|
40
|
+
tracesResponse = (await response.json());
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Endpoint may not be implemented yet
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (options.json) {
|
|
48
|
+
console.log(JSON.stringify({
|
|
49
|
+
success: tracesResponse?.success ?? false,
|
|
50
|
+
traces: tracesResponse?.traces ?? [],
|
|
51
|
+
count: tracesResponse?.count ?? 0,
|
|
52
|
+
collector: {
|
|
53
|
+
healthy: collectorHealthy,
|
|
54
|
+
totalTraces: collectorStats?.summary?.totalTraces ?? 0,
|
|
55
|
+
services: Object.keys(collectorStats?.services ?? {}),
|
|
56
|
+
},
|
|
57
|
+
app: {
|
|
58
|
+
healthy: appHealthy,
|
|
59
|
+
endpoint: `http://localhost:${appPort}/otel/traces`,
|
|
60
|
+
},
|
|
61
|
+
}, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
console.log(chalk.bold('\nTrace List'));
|
|
65
|
+
console.log('━'.repeat(60));
|
|
66
|
+
// Show current pipeline status
|
|
67
|
+
console.log(chalk.bold('\n Pipeline Status:'));
|
|
68
|
+
if (collectorHealthy && collectorStats) {
|
|
69
|
+
const serviceCount = Object.keys(collectorStats.services).length;
|
|
70
|
+
console.log(` Collector: ${chalk.green('✓')} receiving traces (${collectorStats.summary.totalTraces} total, ${serviceCount} services)`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.log(` Collector: ${chalk.red('✗')} not reachable`);
|
|
74
|
+
}
|
|
75
|
+
if (appHealthy) {
|
|
76
|
+
console.log(` Electron-app: ${chalk.green('✓')} running`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(` Electron-app: ${chalk.red('✗')} not reachable`);
|
|
80
|
+
}
|
|
81
|
+
// Show traces if available
|
|
82
|
+
if (tracesResponse?.success && tracesResponse.traces.length > 0) {
|
|
83
|
+
console.log(chalk.bold(`\n Recent Traces (${tracesResponse.count}):`));
|
|
84
|
+
for (const trace of tracesResponse.traces) {
|
|
85
|
+
const serviceName = extractServiceName(trace);
|
|
86
|
+
const spanCount = countSpans(trace);
|
|
87
|
+
const timestamp = extractTimestamp(trace);
|
|
88
|
+
console.log(` ${chalk.cyan(trace.traceId.slice(0, 16))}...`);
|
|
89
|
+
console.log(` Service: ${serviceName}`);
|
|
90
|
+
console.log(` Spans: ${spanCount}`);
|
|
91
|
+
if (timestamp) {
|
|
92
|
+
console.log(` Time: ${timestamp}`);
|
|
93
|
+
}
|
|
94
|
+
console.log();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (tracesResponse?.success && tracesResponse.traces.length === 0) {
|
|
98
|
+
console.log(chalk.bold('\n Recent Traces:'));
|
|
99
|
+
console.log(chalk.gray(' No traces stored yet.'));
|
|
100
|
+
console.log(chalk.gray(' Traces will appear here once your instrumented'));
|
|
101
|
+
console.log(chalk.gray(' application sends data to the collector.'));
|
|
102
|
+
}
|
|
103
|
+
else if (appHealthy && !tracesResponse) {
|
|
104
|
+
// Endpoint not implemented
|
|
105
|
+
console.log(chalk.bold('\n Trace Retrieval:'));
|
|
106
|
+
console.log(chalk.yellow(' Endpoint not available.'));
|
|
107
|
+
console.log();
|
|
108
|
+
console.log(chalk.gray(' The electron-app needs to be restarted to'));
|
|
109
|
+
console.log(chalk.gray(' enable the /otel/traces endpoint.'));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(chalk.bold('\n Trace Retrieval:'));
|
|
113
|
+
console.log(chalk.red(' Cannot connect to electron-app.'));
|
|
114
|
+
}
|
|
115
|
+
console.log('\n' + '━'.repeat(60));
|
|
116
|
+
console.log();
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error(chalk.red('Error:'), error.message);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return command;
|
|
124
|
+
}
|
|
125
|
+
function extractServiceName(trace) {
|
|
126
|
+
const attrs = trace.data.resourceSpans?.[0]?.resource?.attributes ?? [];
|
|
127
|
+
const serviceAttr = attrs.find((a) => a.key === 'service.name');
|
|
128
|
+
return serviceAttr?.value?.stringValue ?? 'unknown';
|
|
129
|
+
}
|
|
130
|
+
function countSpans(trace) {
|
|
131
|
+
let count = 0;
|
|
132
|
+
for (const rs of trace.data.resourceSpans ?? []) {
|
|
133
|
+
for (const ss of rs.scopeSpans ?? []) {
|
|
134
|
+
count += ss.spans?.length ?? 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return count;
|
|
138
|
+
}
|
|
139
|
+
function extractTimestamp(trace) {
|
|
140
|
+
const startNano = trace.data.resourceSpans?.[0]?.scopeSpans?.[0]?.spans?.[0]?.startTimeUnixNano;
|
|
141
|
+
if (!startNano)
|
|
142
|
+
return null;
|
|
143
|
+
const ms = parseInt(startNano, 10) / 1_000_000;
|
|
144
|
+
return new Date(ms).toISOString();
|
|
145
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registrations.d.ts","sourceRoot":"","sources":["../../../src/commands/trace/registrations.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,wBAAgB,0BAA0B,IAAI,OAAO,CAyBpD"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trace registrations - Show active port registrations for trace routing
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
const DEFAULT_APP_PORT = 3043;
|
|
7
|
+
export function createRegistrationsCommand() {
|
|
8
|
+
const command = new Command('registrations');
|
|
9
|
+
command
|
|
10
|
+
.description('Show active port registrations for trace routing')
|
|
11
|
+
.option('-p, --port <port>', 'Electron-app port (default: 3043, dev: 3045)')
|
|
12
|
+
.option('--json', 'Output as JSON')
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
try {
|
|
15
|
+
const appPort = options.port ? parseInt(options.port, 10) : DEFAULT_APP_PORT;
|
|
16
|
+
const data = await fetchRegistrations(appPort);
|
|
17
|
+
if (options.json) {
|
|
18
|
+
console.log(JSON.stringify(data, null, 2));
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
outputText(data);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.error(chalk.red('Error:'), error.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return command;
|
|
30
|
+
}
|
|
31
|
+
async function fetchRegistrations(port) {
|
|
32
|
+
const url = `http://localhost:${port}/otel/registrations`;
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error(`Failed to fetch registrations: ${response.statusText}`);
|
|
37
|
+
}
|
|
38
|
+
return (await response.json());
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (error.name === 'AbortError') {
|
|
42
|
+
throw new Error('Request timeout - electron-app not responding');
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function outputText(data) {
|
|
48
|
+
console.log(chalk.bold('\nPort Registrations'));
|
|
49
|
+
console.log('━'.repeat(60));
|
|
50
|
+
if (data.registrations.length === 0) {
|
|
51
|
+
console.log(chalk.yellow('\nNo active registrations found.'));
|
|
52
|
+
console.log(chalk.gray('Windows register when they start listening for traces from a service.'));
|
|
53
|
+
console.log();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Group by window
|
|
57
|
+
const byWindow = new Map();
|
|
58
|
+
for (const reg of data.registrations) {
|
|
59
|
+
const existing = byWindow.get(reg.windowId) || [];
|
|
60
|
+
existing.push(reg);
|
|
61
|
+
byWindow.set(reg.windowId, existing);
|
|
62
|
+
}
|
|
63
|
+
// Output by window
|
|
64
|
+
for (const [windowId, regs] of byWindow) {
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(chalk.cyan(`Window: ${windowId}`));
|
|
67
|
+
for (const reg of regs) {
|
|
68
|
+
const time = new Date(reg.registeredAt).toLocaleTimeString();
|
|
69
|
+
const isWildcard = reg.serviceName === '*';
|
|
70
|
+
const serviceName = isWildcard ? chalk.yellow('* (all services)') : reg.serviceName;
|
|
71
|
+
console.log(chalk.gray(` └─ ${serviceName}`) + chalk.gray(` @ ${time}`));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Summary
|
|
75
|
+
console.log();
|
|
76
|
+
console.log('━'.repeat(60));
|
|
77
|
+
console.log(chalk.gray(`Windows: ${byWindow.size}`));
|
|
78
|
+
console.log(chalk.gray(`Registrations: ${data.registrations.length}`));
|
|
79
|
+
console.log(chalk.gray(`Active services: ${data.services.join(', ') || 'none'}`));
|
|
80
|
+
console.log();
|
|
81
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/commands/trace/validate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC,wBAAgB,qBAAqB,IAAI,OAAO,CAqD/C"}
|