@respan/cli 0.10.0 → 0.12.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/commands/demo/clear.d.ts +23 -0
- package/dist/commands/demo/clear.js +114 -0
- package/dist/commands/demo/index.d.ts +15 -0
- package/dist/commands/demo/index.js +81 -0
- package/dist/commands/setup/doctor.d.ts +0 -1
- package/dist/commands/setup/doctor.js +25 -40
- package/dist/commands/setup/gateway.d.ts +16 -0
- package/dist/commands/setup/gateway.js +33 -0
- package/dist/commands/setup/index.d.ts +4 -33
- package/dist/commands/setup/index.js +11 -762
- package/dist/commands/setup/tracing.d.ts +16 -0
- package/dist/commands/setup/tracing.js +33 -0
- package/dist/lib/agents.d.ts +16 -0
- package/dist/lib/agents.js +75 -0
- package/dist/lib/auth.js +24 -0
- package/dist/lib/colors.d.ts +7 -0
- package/dist/lib/colors.js +10 -0
- package/dist/lib/demo/dataset-fixtures.d.ts +32 -0
- package/dist/lib/demo/dataset-fixtures.js +194 -0
- package/dist/lib/demo/evaluator-fixtures.d.ts +41 -0
- package/dist/lib/demo/evaluator-fixtures.js +251 -0
- package/dist/lib/demo/prompt-fixtures.d.ts +54 -0
- package/dist/lib/demo/prompt-fixtures.js +395 -0
- package/dist/lib/demo/seed-datasets.d.ts +15 -0
- package/dist/lib/demo/seed-datasets.js +107 -0
- package/dist/lib/demo/seed-evaluators.d.ts +22 -0
- package/dist/lib/demo/seed-evaluators.js +180 -0
- package/dist/lib/demo/seed-prompts.d.ts +10 -0
- package/dist/lib/demo/seed-prompts.js +111 -0
- package/dist/lib/demo/seed-traces.d.ts +27 -0
- package/dist/lib/demo/seed-traces.js +158 -0
- package/dist/lib/demo/seeders.d.ts +8 -0
- package/dist/lib/demo/seeders.js +11 -0
- package/dist/lib/demo/trace-fixtures.d.ts +31 -0
- package/dist/lib/demo/trace-fixtures.js +79 -0
- package/dist/lib/demo/types.d.ts +57 -0
- package/dist/lib/demo/types.js +9 -0
- package/dist/lib/integrate.d.ts +6 -0
- package/dist/lib/integrate.js +11 -0
- package/dist/lib/setup-base-command.d.ts +84 -0
- package/dist/lib/setup-base-command.js +649 -0
- package/dist/lib/skill-content.d.ts +1 -0
- package/dist/lib/skill-content.js +16 -0
- package/dist/lib/skill-refs.generated.d.ts +3 -2
- package/dist/lib/skill-refs.generated.js +423 -102
- package/oclif.manifest.json +945 -642
- package/package.json +2 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BaseCommand } from '../../lib/base-command.js';
|
|
2
|
+
/**
|
|
3
|
+
* Tear down what `respan demo` created. Each seeder owns how it identifies its own
|
|
4
|
+
* demo resources — prompts by exact fixture name, traces by the `demo` workflow —
|
|
5
|
+
* so a resource that was manually renamed/relabeled may not be matched, the same
|
|
6
|
+
* identity contract used when seeding.
|
|
7
|
+
*/
|
|
8
|
+
export default class DemoClear extends BaseCommand {
|
|
9
|
+
static description: string;
|
|
10
|
+
static examples: string[];
|
|
11
|
+
static flags: {
|
|
12
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
19
|
+
};
|
|
20
|
+
run(): Promise<void>;
|
|
21
|
+
private previewDeletions;
|
|
22
|
+
private printSummary;
|
|
23
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { confirm } from '@inquirer/prompts';
|
|
3
|
+
import { BaseCommand } from '../../lib/base-command.js';
|
|
4
|
+
import { getActiveProfile, getCredential } from '../../lib/config.js';
|
|
5
|
+
import { GREEN, DIM, BOLD, RESET } from '../../lib/colors.js';
|
|
6
|
+
import { SEEDERS } from '../../lib/demo/seeders.js';
|
|
7
|
+
/**
|
|
8
|
+
* Tear down what `respan demo` created. Each seeder owns how it identifies its own
|
|
9
|
+
* demo resources — prompts by exact fixture name, traces by the `demo` workflow —
|
|
10
|
+
* so a resource that was manually renamed/relabeled may not be matched, the same
|
|
11
|
+
* identity contract used when seeding.
|
|
12
|
+
*/
|
|
13
|
+
class DemoClear extends BaseCommand {
|
|
14
|
+
async run() {
|
|
15
|
+
const { flags } = await this.parse(DemoClear);
|
|
16
|
+
this.globalFlags = flags;
|
|
17
|
+
const profile = flags.profile || getActiveProfile();
|
|
18
|
+
if (!getCredential(profile) && !flags['api-key']) {
|
|
19
|
+
this.error('Not authenticated. Run `respan auth login` (or set RESPAN_API_KEY) before running `respan demo clear`.', { exit: 1 });
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const client = this.getClient();
|
|
23
|
+
const ctx = {
|
|
24
|
+
client,
|
|
25
|
+
authHeader: this.getAuthHeader(),
|
|
26
|
+
baseUrl: this.getBaseUrl(),
|
|
27
|
+
};
|
|
28
|
+
// Preview what exists before deleting anything.
|
|
29
|
+
const existing = [];
|
|
30
|
+
for (const seeder of SEEDERS) {
|
|
31
|
+
const matches = await this.spin(`Finding ${seeder.label}`, () => seeder.findExisting(ctx));
|
|
32
|
+
existing.push({ label: seeder.label, matches });
|
|
33
|
+
}
|
|
34
|
+
const total = existing.reduce((n, e) => n + e.matches.length, 0);
|
|
35
|
+
if (total === 0) {
|
|
36
|
+
this.log('');
|
|
37
|
+
this.log(`Nothing to clear in account ${DIM}(${profile})${RESET}.`);
|
|
38
|
+
this.log('');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!flags.yes && !flags.json) {
|
|
42
|
+
this.previewDeletions(profile, existing);
|
|
43
|
+
if (!process.stdin.isTTY) {
|
|
44
|
+
this.error('Refusing to delete without confirmation. Re-run with --yes.', { exit: 1 });
|
|
45
|
+
}
|
|
46
|
+
const proceed = await confirm({
|
|
47
|
+
message: `Delete ${total} demo resource(s) from account "${profile}"?`,
|
|
48
|
+
default: false,
|
|
49
|
+
});
|
|
50
|
+
if (!proceed) {
|
|
51
|
+
this.log('Aborted. Nothing was deleted.');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const results = [];
|
|
56
|
+
for (const seeder of SEEDERS) {
|
|
57
|
+
const result = await this.spin(`Clearing ${seeder.label}`, () => seeder.clear(ctx));
|
|
58
|
+
results.push(result);
|
|
59
|
+
}
|
|
60
|
+
this.printSummary(profile, results);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
this.handleError(error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
previewDeletions(profile, existing) {
|
|
67
|
+
this.log('');
|
|
68
|
+
this.log(`${BOLD}respan demo clear${RESET} will delete from account ${DIM}(${profile})${RESET}:`);
|
|
69
|
+
for (const { label, matches } of existing) {
|
|
70
|
+
if (matches.length === 0)
|
|
71
|
+
continue;
|
|
72
|
+
this.log('');
|
|
73
|
+
this.log(` ${BOLD}${label}${RESET}`);
|
|
74
|
+
for (const m of matches) {
|
|
75
|
+
this.log(` ${DIM}- ${m.name} (id: ${m.id})${RESET}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
this.log('');
|
|
79
|
+
}
|
|
80
|
+
printSummary(profile, results) {
|
|
81
|
+
if (this.globalFlags.json) {
|
|
82
|
+
this.outputResult(results);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this.log('');
|
|
86
|
+
this.log(`${GREEN}✓${RESET} ${BOLD}respan demo clear${RESET} — account ${DIM}(${profile})${RESET}`);
|
|
87
|
+
for (const result of results) {
|
|
88
|
+
if (result.items.length === 0)
|
|
89
|
+
continue; // e.g. traces, which are never deleted
|
|
90
|
+
this.log('');
|
|
91
|
+
this.log(` ${BOLD}${result.label}${RESET}`);
|
|
92
|
+
for (const item of result.items) {
|
|
93
|
+
if (item.status === 'deleted') {
|
|
94
|
+
this.log(` ${GREEN}Deleted${RESET} ${item.name} ${DIM}(id: ${item.id})${RESET}`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.log(` ${DIM}Skipped ${item.name} (${item.reason})${RESET}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const deletedCount = results.reduce((n, r) => n + r.items.filter((i) => i.status === 'deleted').length, 0);
|
|
102
|
+
const skippedCount = results.reduce((n, r) => n + r.items.filter((i) => i.status === 'skipped').length, 0);
|
|
103
|
+
this.log('');
|
|
104
|
+
this.log(` ${DIM}${deletedCount} deleted, ${skippedCount} not found${RESET}`);
|
|
105
|
+
this.log('');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
DemoClear.description = 'Delete the sample prompts, datasets, and evaluators created by `respan demo`. Demo traces are left in place (each run is timestamped history). Idempotent — safe to re-run.';
|
|
109
|
+
DemoClear.examples = ['<%= config.bin %> demo clear', '<%= config.bin %> demo clear --yes'];
|
|
110
|
+
DemoClear.flags = {
|
|
111
|
+
...BaseCommand.baseFlags,
|
|
112
|
+
yes: Flags.boolean({ char: 'y', description: 'Skip the confirmation prompt', default: false }),
|
|
113
|
+
};
|
|
114
|
+
export default DemoClear;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaseCommand } from '../../lib/base-command.js';
|
|
2
|
+
export default class Demo extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
private printSummary;
|
|
15
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BaseCommand } from '../../lib/base-command.js';
|
|
2
|
+
import { getActiveProfile, getCredential } from '../../lib/config.js';
|
|
3
|
+
import { GREEN, DIM, BOLD, RESET } from '../../lib/colors.js';
|
|
4
|
+
import { SEEDERS } from '../../lib/demo/seeders.js';
|
|
5
|
+
const PLATFORM_BASE = 'https://platform.respan.ai/platform';
|
|
6
|
+
/** Dashboard link for a seeded service, deep-linked/filtered where possible so the
|
|
7
|
+
* user lands directly on what was just created. Traces are pre-filtered to the
|
|
8
|
+
* `demo` workflow so they're isolated from any existing traffic. */
|
|
9
|
+
function viewLink(service) {
|
|
10
|
+
switch (service) {
|
|
11
|
+
case 'prompts':
|
|
12
|
+
return `${PLATFORM_BASE}/prompts`;
|
|
13
|
+
case 'datasets':
|
|
14
|
+
return `${PLATFORM_BASE}/datasets`;
|
|
15
|
+
case 'evaluators':
|
|
16
|
+
return `${PLATFORM_BASE}/evaluators`;
|
|
17
|
+
case 'traces':
|
|
18
|
+
return `${PLATFORM_BASE}/traces?span_workflow_name=demo`;
|
|
19
|
+
default:
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
class Demo extends BaseCommand {
|
|
24
|
+
async run() {
|
|
25
|
+
const { flags } = await this.parse(Demo);
|
|
26
|
+
this.globalFlags = flags;
|
|
27
|
+
const profile = flags.profile || getActiveProfile();
|
|
28
|
+
if (!getCredential(profile) && !flags['api-key']) {
|
|
29
|
+
this.error('Not authenticated. Run `respan auth login` (or set RESPAN_API_KEY) before running `respan demo`.', { exit: 1 });
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const client = this.getClient();
|
|
33
|
+
const ctx = {
|
|
34
|
+
client,
|
|
35
|
+
authHeader: this.getAuthHeader(),
|
|
36
|
+
baseUrl: this.getBaseUrl(),
|
|
37
|
+
};
|
|
38
|
+
const results = [];
|
|
39
|
+
for (const seeder of SEEDERS) {
|
|
40
|
+
const result = await this.spin(`Seeding ${seeder.label}`, () => seeder.run(ctx));
|
|
41
|
+
results.push(result);
|
|
42
|
+
}
|
|
43
|
+
this.printSummary(profile, results);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
this.handleError(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
printSummary(profile, results) {
|
|
50
|
+
if (this.globalFlags.json) {
|
|
51
|
+
this.outputResult(results);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.log('');
|
|
55
|
+
this.log(`${GREEN}✓${RESET} ${BOLD}respan demo${RESET} — seeded into account ${DIM}(${profile})${RESET}`);
|
|
56
|
+
for (const result of results) {
|
|
57
|
+
this.log('');
|
|
58
|
+
this.log(` ${BOLD}${result.label}${RESET}`);
|
|
59
|
+
for (const item of result.items) {
|
|
60
|
+
if (item.status === 'created') {
|
|
61
|
+
this.log(` ${GREEN}Created${RESET} ${item.name} ${DIM}(id: ${item.id})${RESET}`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.log(` ${DIM}Skipped ${item.name} (${item.reason})${RESET}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const link = viewLink(result.service);
|
|
68
|
+
if (link)
|
|
69
|
+
this.log(` ${DIM}View:${RESET} ${link}`);
|
|
70
|
+
}
|
|
71
|
+
const createdCount = results.reduce((n, r) => n + r.items.filter((i) => i.status === 'created').length, 0);
|
|
72
|
+
const skippedCount = results.reduce((n, r) => n + r.items.filter((i) => i.status === 'skipped').length, 0);
|
|
73
|
+
this.log('');
|
|
74
|
+
this.log(` ${DIM}${createdCount} created, ${skippedCount} skipped${RESET}`);
|
|
75
|
+
this.log('');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
Demo.description = 'Seed your account with sample data (prompts and traces) so you have something to explore. Idempotent — safe to re-run.';
|
|
79
|
+
Demo.examples = ['<%= config.bin %> demo'];
|
|
80
|
+
Demo.flags = { ...BaseCommand.baseFlags };
|
|
81
|
+
export default Demo;
|
|
@@ -5,12 +5,8 @@ import { execSync } from 'node:child_process';
|
|
|
5
5
|
import { Flags } from '@oclif/core';
|
|
6
6
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
7
7
|
import { findProjectRoot, expandHome, readTextFile } from '../../lib/integrate.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const YELLOW = '\x1b[33m';
|
|
11
|
-
const DIM = '\x1b[2m';
|
|
12
|
-
const BOLD = '\x1b[1m';
|
|
13
|
-
const RESET = '\x1b[0m';
|
|
8
|
+
import { GREEN, RED, YELLOW, DIM, BOLD, RESET } from '../../lib/colors.js';
|
|
9
|
+
import { isBinaryInstalled } from '../../lib/agents.js';
|
|
14
10
|
class SetupDoctor extends BaseCommand {
|
|
15
11
|
async run() {
|
|
16
12
|
const { flags } = await this.parse(SetupDoctor);
|
|
@@ -43,7 +39,7 @@ class SetupDoctor extends BaseCommand {
|
|
|
43
39
|
this.log('');
|
|
44
40
|
// ── 2. Respan CLI ──────────────────────────────────────────────
|
|
45
41
|
this.log(` ${BOLD}Respan CLI${RESET}`);
|
|
46
|
-
const respanInstalled =
|
|
42
|
+
const respanInstalled = isBinaryInstalled('respan');
|
|
47
43
|
if (respanInstalled) {
|
|
48
44
|
try {
|
|
49
45
|
const version = execSync('respan --version', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
@@ -66,44 +62,44 @@ class SetupDoctor extends BaseCommand {
|
|
|
66
62
|
name: 'Claude Code',
|
|
67
63
|
globalConfigs: ['~/.claude/settings.json'],
|
|
68
64
|
localConfigs: ['.claude/settings.local.json'],
|
|
69
|
-
globalSkills: ['~/.claude/skills/respan/SKILL.md'
|
|
70
|
-
localSkills: ['.claude/skills/respan/SKILL.md'
|
|
65
|
+
globalSkills: ['~/.claude/skills/respan/SKILL.md'],
|
|
66
|
+
localSkills: ['.claude/skills/respan/SKILL.md'],
|
|
71
67
|
},
|
|
72
68
|
'cursor': {
|
|
73
69
|
binary: 'cursor',
|
|
74
70
|
name: 'Cursor',
|
|
75
71
|
globalConfigs: ['~/.cursor/rules'],
|
|
76
72
|
localConfigs: ['.cursor/rules'],
|
|
77
|
-
globalSkills: ['~/.
|
|
78
|
-
localSkills: ['.
|
|
73
|
+
globalSkills: ['~/.agents/skills/respan/SKILL.md', '~/.cursor/skills/respan/SKILL.md'],
|
|
74
|
+
localSkills: ['.agents/skills/respan/SKILL.md', '.cursor/skills/respan/SKILL.md'],
|
|
79
75
|
},
|
|
80
76
|
'codex-cli': {
|
|
81
77
|
binary: 'codex',
|
|
82
78
|
name: 'Codex CLI',
|
|
83
79
|
globalConfigs: ['~/.codex/config.toml'],
|
|
84
80
|
localConfigs: ['.codex/respan.json'],
|
|
85
|
-
globalSkills: ['~/.
|
|
86
|
-
localSkills: ['.
|
|
81
|
+
globalSkills: ['~/.agents/skills/respan/SKILL.md', '~/.codex/skills/respan/SKILL.md'],
|
|
82
|
+
localSkills: ['.agents/skills/respan/SKILL.md', '.codex/skills/respan/SKILL.md'],
|
|
87
83
|
},
|
|
88
84
|
'gemini-cli': {
|
|
89
85
|
binary: 'gemini',
|
|
90
86
|
name: 'Gemini CLI',
|
|
91
87
|
globalConfigs: ['~/.gemini/settings.json'],
|
|
92
88
|
localConfigs: ['.gemini/settings.json'],
|
|
93
|
-
globalSkills: ['~/.
|
|
94
|
-
localSkills: ['.
|
|
89
|
+
globalSkills: ['~/.agents/skills/respan/SKILL.md', '~/.gemini/skills/respan/SKILL.md'],
|
|
90
|
+
localSkills: ['.agents/skills/respan/SKILL.md', '.gemini/skills/respan/SKILL.md'],
|
|
95
91
|
},
|
|
96
92
|
'opencode': {
|
|
97
93
|
binary: 'opencode',
|
|
98
94
|
name: 'OpenCode',
|
|
99
95
|
globalConfigs: ['~/.config/opencode'],
|
|
100
96
|
localConfigs: ['.opencode'],
|
|
101
|
-
globalSkills: ['~/.
|
|
102
|
-
localSkills: ['.
|
|
97
|
+
globalSkills: ['~/.agents/skills/respan/SKILL.md', '~/.config/opencode/skills/respan/SKILL.md'],
|
|
98
|
+
localSkills: ['.agents/skills/respan/SKILL.md', '.opencode/skills/respan/SKILL.md'],
|
|
103
99
|
},
|
|
104
100
|
};
|
|
105
101
|
for (const [id, tool] of Object.entries(tools)) {
|
|
106
|
-
const binaryFound =
|
|
102
|
+
const binaryFound = isBinaryInstalled(tool.binary);
|
|
107
103
|
this.log(` ${BOLD}${tool.name}${RESET} ${DIM}(${tool.binary})${RESET}`);
|
|
108
104
|
// Binary
|
|
109
105
|
if (binaryFound) {
|
|
@@ -129,24 +125,22 @@ class SetupDoctor extends BaseCommand {
|
|
|
129
125
|
}
|
|
130
126
|
}
|
|
131
127
|
}
|
|
132
|
-
// Skills
|
|
128
|
+
// Skills \u2014 each tool only lists dirs it actually reads (the shared
|
|
129
|
+
// ~/.agents, or ~/.claude for Claude Code) plus its own dir as a
|
|
130
|
+
// fallback. Report the first match.
|
|
133
131
|
let hasSkill = false;
|
|
134
132
|
if (checkGlobal) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
hasSkill = true;
|
|
140
|
-
}
|
|
133
|
+
const found = tool.globalSkills.find((skill) => fs.existsSync(expandHome(skill)));
|
|
134
|
+
if (found) {
|
|
135
|
+
this.log(` ${GREEN}\u2713${RESET} Global skill: ${DIM}${found}${RESET}`);
|
|
136
|
+
hasSkill = true;
|
|
141
137
|
}
|
|
142
138
|
}
|
|
143
139
|
if (checkLocal) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
hasSkill = true;
|
|
149
|
-
}
|
|
140
|
+
const found = tool.localSkills.find((skill) => fs.existsSync(path.join(projectRoot, skill)));
|
|
141
|
+
if (found) {
|
|
142
|
+
this.log(` ${GREEN}\u2713${RESET} Local skill: ${DIM}${found}${RESET}`);
|
|
143
|
+
hasSkill = true;
|
|
150
144
|
}
|
|
151
145
|
}
|
|
152
146
|
if (binaryFound && !hasSkill) {
|
|
@@ -193,15 +187,6 @@ class SetupDoctor extends BaseCommand {
|
|
|
193
187
|
}
|
|
194
188
|
this.log('');
|
|
195
189
|
}
|
|
196
|
-
isBinaryInstalled(binary) {
|
|
197
|
-
try {
|
|
198
|
-
execSync(`command -v ${binary}`, { stdio: 'pipe' });
|
|
199
|
-
return true;
|
|
200
|
-
}
|
|
201
|
-
catch {
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
190
|
}
|
|
206
191
|
SetupDoctor.description = 'Diagnose coding-agent setup for Respan';
|
|
207
192
|
SetupDoctor.examples = [
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SetupBaseCommand } from '../../lib/setup-base-command.js';
|
|
2
|
+
export default class SetupGateway extends SetupBaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
agent: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
'no-instrument': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { SetupBaseCommand } from '../../lib/setup-base-command.js';
|
|
3
|
+
class SetupGateway extends SetupBaseCommand {
|
|
4
|
+
async run() {
|
|
5
|
+
const { flags } = await this.parse(SetupGateway);
|
|
6
|
+
this.globalFlags = flags;
|
|
7
|
+
await this.runSetup('gateway', {
|
|
8
|
+
agent: flags.agent,
|
|
9
|
+
noInstrument: flags['no-instrument'],
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
SetupGateway.description = `Set up Respan gateway routing in this project.
|
|
14
|
+
|
|
15
|
+
Runs the gateway setup flow directly (skips the "Tracing or Gateway?"
|
|
16
|
+
question). Sets up your API key, installs the Respan skill, and optionally
|
|
17
|
+
opens your coding agent to route the detected framework through the gateway.`;
|
|
18
|
+
SetupGateway.examples = [
|
|
19
|
+
'respan setup gateway',
|
|
20
|
+
'respan setup gateway --agent claude-code',
|
|
21
|
+
'respan setup gateway --no-instrument',
|
|
22
|
+
];
|
|
23
|
+
SetupGateway.flags = {
|
|
24
|
+
...SetupBaseCommand.baseFlags,
|
|
25
|
+
agent: Flags.string({
|
|
26
|
+
description: 'Agent to configure (claude-code, cursor, codex-cli, gemini-cli, opencode)',
|
|
27
|
+
}),
|
|
28
|
+
'no-instrument': Flags.boolean({
|
|
29
|
+
description: 'Skip opening the agent after setup',
|
|
30
|
+
default: false,
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
export default SetupGateway;
|
|
@@ -1,45 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class Setup extends
|
|
1
|
+
import { SetupBaseCommand } from '../../lib/setup-base-command.js';
|
|
2
|
+
export default class Setup extends SetupBaseCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
6
|
agent: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
'no-instrument': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
10
|
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
11
|
'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
12
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
-
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
13
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
14
|
};
|
|
15
15
|
run(): Promise<void>;
|
|
16
|
-
private askApiKey;
|
|
17
|
-
private verifyApiKey;
|
|
18
|
-
/**
|
|
19
|
-
* Build a rich demo OTLP trace that mimics a customer support agent workflow:
|
|
20
|
-
* workflow: customer_support_pipeline
|
|
21
|
-
* └── agent: support_agent
|
|
22
|
-
* ├── tool: lookup_order
|
|
23
|
-
* ├── chat: openai.chat (classify intent)
|
|
24
|
-
* ├── tool: process_refund
|
|
25
|
-
* ├── chat: openai.chat (generate response)
|
|
26
|
-
* └── task: log_resolution
|
|
27
|
-
*/
|
|
28
|
-
private buildDemoTrace;
|
|
29
|
-
private detectAgents;
|
|
30
|
-
private selectAgent;
|
|
31
|
-
private installSkill;
|
|
32
|
-
private runInstrumentAgent;
|
|
33
|
-
private getSkillMd;
|
|
34
|
-
private logStep;
|
|
35
|
-
private isBinaryInstalled;
|
|
36
|
-
private extractEnvVar;
|
|
37
|
-
private saveToEnv;
|
|
38
|
-
private getGitEmail;
|
|
39
|
-
/**
|
|
40
|
-
* Send a lightweight setup notification (email only).
|
|
41
|
-
* This helps the team know when new users set up Respan so we can
|
|
42
|
-
* reach out and help with onboarding.
|
|
43
|
-
*/
|
|
44
|
-
private notifySetup;
|
|
45
16
|
}
|