@respan/cli 0.5.1 → 0.5.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/dist/assets/codex_hook.py +897 -0
- package/dist/assets/hook.py +12 -4
- package/dist/commands/integrate/claude-code.js +3 -3
- package/dist/commands/integrate/codex-cli.d.ts +8 -0
- package/dist/commands/integrate/codex-cli.js +143 -50
- package/dist/commands/integrate/gemini-cli.js +27 -6
- package/dist/lib/config.js +1 -1
- package/dist/lib/integrate.d.ts +4 -0
- package/dist/lib/integrate.js +8 -0
- package/oclif.manifest.json +502 -501
- package/package.json +2 -1
package/dist/assets/hook.py
CHANGED
|
@@ -492,6 +492,7 @@ def create_respan_spans(
|
|
|
492
492
|
"span_name": root_span_name,
|
|
493
493
|
"span_workflow_name": workflow_name,
|
|
494
494
|
"model": model,
|
|
495
|
+
"provider_id": "",
|
|
495
496
|
"span_path": "",
|
|
496
497
|
"input": json.dumps(prompt_messages) if prompt_messages else "",
|
|
497
498
|
"output": json.dumps(completion_message) if completion_message else "",
|
|
@@ -524,6 +525,7 @@ def create_respan_spans(
|
|
|
524
525
|
"span_path": "claude_chat",
|
|
525
526
|
"model": model,
|
|
526
527
|
"provider_id": "anthropic",
|
|
528
|
+
"metadata": {},
|
|
527
529
|
"input": json.dumps(prompt_messages) if prompt_messages else "",
|
|
528
530
|
"output": json.dumps(completion_message) if completion_message else "",
|
|
529
531
|
"prompt_messages": prompt_messages,
|
|
@@ -560,6 +562,8 @@ def create_respan_spans(
|
|
|
560
562
|
"span_name": f"Thinking {thinking_num}",
|
|
561
563
|
"span_workflow_name": workflow_name,
|
|
562
564
|
"span_path": "thinking",
|
|
565
|
+
"provider_id": "",
|
|
566
|
+
"metadata": {},
|
|
563
567
|
"input": "",
|
|
564
568
|
"output": thinking_text,
|
|
565
569
|
"timestamp": thinking_ts,
|
|
@@ -610,13 +614,14 @@ def create_respan_spans(
|
|
|
610
614
|
"span_name": f"Tool: {td['name']}",
|
|
611
615
|
"span_workflow_name": workflow_name,
|
|
612
616
|
"span_path": f"tool_{td['name'].lower()}",
|
|
617
|
+
"provider_id": "",
|
|
618
|
+
"metadata": td.get("result_metadata") or {},
|
|
613
619
|
"input": format_tool_input(td["name"], td["input"]),
|
|
614
620
|
"output": format_tool_output(td["name"], td.get("output")),
|
|
615
621
|
"timestamp": tool_ts,
|
|
616
622
|
"start_time": tool_start,
|
|
617
623
|
}
|
|
618
624
|
if td.get("result_metadata"):
|
|
619
|
-
tool_span["metadata"] = td["result_metadata"]
|
|
620
625
|
duration_ms = td["result_metadata"].get("duration_ms")
|
|
621
626
|
if duration_ms:
|
|
622
627
|
tool_span["latency"] = duration_ms / 1000.0
|
|
@@ -937,17 +942,17 @@ def main():
|
|
|
937
942
|
debug("Tracing disabled (TRACE_TO_RESPAN != true)")
|
|
938
943
|
sys.exit(0)
|
|
939
944
|
|
|
940
|
-
# Resolve API key: env var > ~/.
|
|
945
|
+
# Resolve API key: env var > ~/.respan/credentials.json
|
|
941
946
|
api_key = os.getenv("RESPAN_API_KEY")
|
|
942
947
|
base_url = os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api")
|
|
943
948
|
|
|
944
949
|
if not api_key:
|
|
945
|
-
creds_file = Path.home() / ".
|
|
950
|
+
creds_file = Path.home() / ".respan" / "credentials.json"
|
|
946
951
|
if creds_file.exists():
|
|
947
952
|
try:
|
|
948
953
|
creds = json.loads(creds_file.read_text(encoding="utf-8"))
|
|
949
954
|
# Find the active profile's credential
|
|
950
|
-
config_file = Path.home() / ".
|
|
955
|
+
config_file = Path.home() / ".respan" / "config.json"
|
|
951
956
|
profile = "default"
|
|
952
957
|
if config_file.exists():
|
|
953
958
|
cfg = json.loads(config_file.read_text(encoding="utf-8"))
|
|
@@ -956,6 +961,9 @@ def main():
|
|
|
956
961
|
api_key = cred.get("apiKey") or cred.get("accessToken")
|
|
957
962
|
if not base_url or base_url == "https://api.respan.ai/api":
|
|
958
963
|
base_url = cred.get("baseUrl", base_url)
|
|
964
|
+
# Ensure base_url ends with /api (credentials store the host only)
|
|
965
|
+
if base_url and not base_url.rstrip("/").endswith("/api"):
|
|
966
|
+
base_url = base_url.rstrip("/") + "/api"
|
|
959
967
|
if api_key:
|
|
960
968
|
debug(f"Using API key from credentials.json (profile: {profile})")
|
|
961
969
|
except (json.JSONDecodeError, IOError) as e:
|
|
@@ -7,7 +7,7 @@ class IntegrateClaudeCode extends BaseCommand {
|
|
|
7
7
|
const { flags } = await this.parse(IntegrateClaudeCode);
|
|
8
8
|
this.globalFlags = flags;
|
|
9
9
|
try {
|
|
10
|
-
// Verify the user is authenticated (key is read by hook from ~/.
|
|
10
|
+
// Verify the user is authenticated (key is read by hook from ~/.respan/)
|
|
11
11
|
this.resolveApiKey();
|
|
12
12
|
const baseUrl = flags['base-url'];
|
|
13
13
|
const projectId = flags['project-id'];
|
|
@@ -93,7 +93,7 @@ class IntegrateClaudeCode extends BaseCommand {
|
|
|
93
93
|
const projectRoot = findProjectRoot();
|
|
94
94
|
const localSettingsPath = `${projectRoot}/.claude/settings.local.json`;
|
|
95
95
|
const localSettings = readJsonFile(localSettingsPath);
|
|
96
|
-
// settings.local.json: enable flag only (API key from ~/.
|
|
96
|
+
// settings.local.json: enable flag only (API key from ~/.respan/)
|
|
97
97
|
const envBlock = {
|
|
98
98
|
TRACE_TO_RESPAN: 'true',
|
|
99
99
|
};
|
|
@@ -150,7 +150,7 @@ class IntegrateClaudeCode extends BaseCommand {
|
|
|
150
150
|
this.log('Claude Code tracing enabled for this project.');
|
|
151
151
|
}
|
|
152
152
|
this.log('');
|
|
153
|
-
this.log('Auth: ~/.
|
|
153
|
+
this.log('Auth: ~/.respan/credentials.json (from `respan auth login`)');
|
|
154
154
|
this.log('Config: .claude/respan.json (shareable, non-secret)');
|
|
155
155
|
this.log('');
|
|
156
156
|
this.log('Set properties via integrate flags or edit .claude/respan.json:');
|
|
@@ -19,5 +19,13 @@ export default class IntegrateCodexCli extends BaseCommand {
|
|
|
19
19
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
20
20
|
};
|
|
21
21
|
run(): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Update or insert the `notify` line in a TOML config string.
|
|
24
|
+
*
|
|
25
|
+
* TOML bare keys must appear before the first [table] header.
|
|
26
|
+
* If a `notify` line already exists, replace it in-place.
|
|
27
|
+
* Otherwise, insert the notify line before the first [table] header.
|
|
28
|
+
*/
|
|
29
|
+
private updateTomlNotify;
|
|
22
30
|
private resolveApiKey;
|
|
23
31
|
}
|
|
@@ -1,69 +1,161 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
2
3
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
3
|
-
import { integrateFlags, readTextFile, writeTextFile, expandHome, parseAttrs,
|
|
4
|
+
import { integrateFlags, readTextFile, writeTextFile, writeJsonFile, readJsonFile, expandHome, parseAttrs, getCodexHookScript, resolveScope, findProjectRoot, } from '../../lib/integrate.js';
|
|
4
5
|
class IntegrateCodexCli extends BaseCommand {
|
|
5
6
|
async run() {
|
|
6
7
|
const { flags } = await this.parse(IntegrateCodexCli);
|
|
7
8
|
this.globalFlags = flags;
|
|
8
9
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
// Verify the user is authenticated (key is read by hook from ~/.respan/)
|
|
11
|
+
this.resolveApiKey();
|
|
11
12
|
const projectId = flags['project-id'];
|
|
13
|
+
const customerId = flags['customer-id'];
|
|
14
|
+
const spanName = flags['span-name'];
|
|
15
|
+
const workflowName = flags['workflow-name'];
|
|
12
16
|
const attrs = parseAttrs(flags.attrs);
|
|
13
17
|
const dryRun = flags['dry-run'];
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
// Codex CLI default: both global + local
|
|
19
|
+
const scope = resolveScope(flags, 'both');
|
|
20
|
+
const doGlobal = scope === 'global' || scope === 'both';
|
|
21
|
+
const doLocal = scope === 'local' || scope === 'both';
|
|
22
|
+
// ── Global: hook script + notify registration ──────────────────
|
|
23
|
+
if (doGlobal) {
|
|
24
|
+
// 1. Ensure Python 'requests' is available
|
|
25
|
+
if (!dryRun) {
|
|
26
|
+
try {
|
|
27
|
+
execSync('python3 -c "import requests"', { stdio: 'pipe' });
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
this.log('Installing Python requests package...');
|
|
31
|
+
try {
|
|
32
|
+
execSync('pip3 install requests', { stdio: 'pipe' });
|
|
33
|
+
this.log('Installed requests.');
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
this.warn('Could not install Python requests package automatically.\n' +
|
|
37
|
+
'Install it manually: pip3 install requests');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.log('[dry-run] Would verify Python requests package');
|
|
43
|
+
}
|
|
44
|
+
// 2. Write hook script
|
|
45
|
+
const hookPath = expandHome('~/.respan/codex_hook.py');
|
|
46
|
+
if (dryRun) {
|
|
47
|
+
this.log(`[dry-run] Would write hook script to: ${hookPath}`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
writeTextFile(hookPath, getCodexHookScript());
|
|
51
|
+
fs.chmodSync(hookPath, 0o755);
|
|
52
|
+
this.log(`Wrote hook script: ${hookPath}`);
|
|
53
|
+
}
|
|
54
|
+
// 3. Register notify hook in ~/.codex/config.toml
|
|
55
|
+
const configPath = expandHome('~/.codex/config.toml');
|
|
56
|
+
const existing = readTextFile(configPath);
|
|
57
|
+
const notifyValue = `notify = ["python3", "${hookPath}"]`;
|
|
58
|
+
const updated = this.updateTomlNotify(existing, notifyValue);
|
|
59
|
+
if (dryRun) {
|
|
60
|
+
this.log(`[dry-run] Would update: ${configPath}`);
|
|
61
|
+
this.log(updated);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
writeTextFile(configPath, updated);
|
|
65
|
+
this.log(`Updated config: ${configPath}`);
|
|
66
|
+
}
|
|
25
67
|
}
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
68
|
+
// ── Local: .codex/respan.json ──────────────────────────────────
|
|
69
|
+
if (doLocal) {
|
|
70
|
+
const projectRoot = findProjectRoot();
|
|
71
|
+
const respanConfigPath = `${projectRoot}/.codex/respan.json`;
|
|
72
|
+
const respanConfig = readJsonFile(respanConfigPath);
|
|
73
|
+
const newConfig = { ...respanConfig };
|
|
74
|
+
if (customerId) {
|
|
75
|
+
newConfig.customer_id = customerId;
|
|
76
|
+
}
|
|
77
|
+
if (spanName) {
|
|
78
|
+
newConfig.span_name = spanName;
|
|
79
|
+
}
|
|
80
|
+
if (workflowName) {
|
|
81
|
+
newConfig.workflow_name = workflowName;
|
|
82
|
+
}
|
|
83
|
+
if (projectId) {
|
|
84
|
+
newConfig.project_id = projectId;
|
|
85
|
+
}
|
|
86
|
+
// Custom attrs go as top-level keys (unknown keys = custom properties)
|
|
87
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
88
|
+
newConfig[k] = v;
|
|
89
|
+
}
|
|
90
|
+
if (Object.keys(newConfig).length > 0) {
|
|
91
|
+
if (dryRun) {
|
|
92
|
+
this.log(`[dry-run] Would write: ${respanConfigPath}`);
|
|
93
|
+
this.log(JSON.stringify(newConfig, null, 2));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
writeJsonFile(respanConfigPath, newConfig);
|
|
97
|
+
this.log(`Wrote Respan config: ${respanConfigPath}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
33
100
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
''
|
|
38
|
-
'# Respan observability (added by respan integrate codex-cli)',
|
|
39
|
-
'[otel]',
|
|
40
|
-
`endpoint = "${baseUrl}/v2/traces"`,
|
|
41
|
-
`api_key = "${apiKey}"`,
|
|
42
|
-
];
|
|
43
|
-
if (otelResStr) {
|
|
44
|
-
lines.push(`resource_attributes = "${otelResStr}"`);
|
|
101
|
+
// ── Done ────────────────────────────────────────────────────────
|
|
102
|
+
this.log('');
|
|
103
|
+
if (doGlobal && doLocal) {
|
|
104
|
+
this.log('Codex CLI integration complete (global hook + project config).');
|
|
45
105
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (dryRun) {
|
|
50
|
-
this.log(`[dry-run] Would append to: ${configPath}`);
|
|
51
|
-
this.log(block);
|
|
106
|
+
else if (doGlobal) {
|
|
107
|
+
this.log('Codex CLI global hook installed.');
|
|
108
|
+
this.log('Run without --global in a project to configure tracing there.');
|
|
52
109
|
}
|
|
53
110
|
else {
|
|
54
|
-
|
|
55
|
-
this.log(`Updated config: ${configPath}`);
|
|
111
|
+
this.log('Codex CLI tracing configured for this project.');
|
|
56
112
|
}
|
|
57
113
|
this.log('');
|
|
58
|
-
this.log(`
|
|
114
|
+
this.log('Auth: ~/.respan/credentials.json (from `respan auth login`)');
|
|
115
|
+
this.log('Config: .codex/respan.json (shareable, non-secret)');
|
|
116
|
+
this.log('');
|
|
117
|
+
this.log('Set properties via integrate flags or edit .codex/respan.json:');
|
|
118
|
+
this.log(' respan integrate codex-cli --customer-id "frank" --span-name "my-app"');
|
|
119
|
+
this.log(' respan integrate codex-cli --attrs \'{"team":"platform","env":"staging"}\'');
|
|
59
120
|
this.log('');
|
|
60
|
-
this.log('
|
|
61
|
-
this.log(' export
|
|
121
|
+
this.log('Override per-session with env vars:');
|
|
122
|
+
this.log(' export RESPAN_CUSTOMER_ID="your-name"');
|
|
123
|
+
this.log(" export RESPAN_METADATA='{\"task_id\":\"T-123\"}'");
|
|
124
|
+
this.log('');
|
|
125
|
+
this.log('Debug: CODEX_RESPAN_DEBUG=true → check ~/.codex/state/respan_hook.log');
|
|
62
126
|
}
|
|
63
127
|
catch (error) {
|
|
64
128
|
this.handleError(error);
|
|
65
129
|
}
|
|
66
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Update or insert the `notify` line in a TOML config string.
|
|
133
|
+
*
|
|
134
|
+
* TOML bare keys must appear before the first [table] header.
|
|
135
|
+
* If a `notify` line already exists, replace it in-place.
|
|
136
|
+
* Otherwise, insert the notify line before the first [table] header.
|
|
137
|
+
*/
|
|
138
|
+
updateTomlNotify(existing, notifyValue) {
|
|
139
|
+
const lines = existing.split('\n');
|
|
140
|
+
// Check if notify line already exists
|
|
141
|
+
const notifyIdx = lines.findIndex((line) => /^\s*notify\s*=/.test(line));
|
|
142
|
+
if (notifyIdx >= 0) {
|
|
143
|
+
// Replace existing notify line
|
|
144
|
+
lines[notifyIdx] = notifyValue;
|
|
145
|
+
return lines.join('\n');
|
|
146
|
+
}
|
|
147
|
+
// Find the first [table] header to insert before it
|
|
148
|
+
const firstTableIdx = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
149
|
+
if (firstTableIdx >= 0) {
|
|
150
|
+
// Insert notify + comment before the first table header
|
|
151
|
+
lines.splice(firstTableIdx, 0, '# Respan observability (added by respan integrate codex-cli)', notifyValue, '');
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// No tables at all — append to end
|
|
155
|
+
lines.push('', '# Respan observability (added by respan integrate codex-cli)', notifyValue);
|
|
156
|
+
}
|
|
157
|
+
return lines.join('\n');
|
|
158
|
+
}
|
|
67
159
|
resolveApiKey() {
|
|
68
160
|
const auth = this.getAuth();
|
|
69
161
|
if (auth.apiKey)
|
|
@@ -77,17 +169,18 @@ class IntegrateCodexCli extends BaseCommand {
|
|
|
77
169
|
}
|
|
78
170
|
IntegrateCodexCli.description = `Integrate Respan with Codex CLI.
|
|
79
171
|
|
|
80
|
-
|
|
81
|
-
|
|
172
|
+
Installs a notify hook that reads session JSONL files and sends
|
|
173
|
+
them to Respan as structured spans (chat, tool, reasoning).
|
|
82
174
|
|
|
83
175
|
Scope:
|
|
84
|
-
--
|
|
85
|
-
--
|
|
86
|
-
|
|
176
|
+
--global Install hook script + register notify in ~/.codex/config.toml
|
|
177
|
+
--local Write .codex/respan.json with customer_id, span_name, etc.
|
|
178
|
+
(default) Both: install hook globally + config for current project`;
|
|
87
179
|
IntegrateCodexCli.examples = [
|
|
88
180
|
'respan integrate codex-cli',
|
|
89
181
|
'respan integrate codex-cli --global',
|
|
90
|
-
'respan integrate codex-cli --
|
|
182
|
+
'respan integrate codex-cli --local --customer-id frank',
|
|
183
|
+
'respan integrate codex-cli --attrs \'{"env":"prod"}\'',
|
|
91
184
|
'respan integrate codex-cli --dry-run',
|
|
92
185
|
];
|
|
93
186
|
IntegrateCodexCli.flags = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
2
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
3
|
-
import { integrateFlags, deepMerge, readJsonFile, writeJsonFile, expandHome, parseAttrs, toOtelResourceAttrs, resolveScope, findProjectRoot, } from '../../lib/integrate.js';
|
|
3
|
+
import { integrateFlags, deepMerge, readJsonFile, writeJsonFile, readTextFile, writeTextFile, expandHome, parseAttrs, toOtelResourceAttrs, resolveScope, findProjectRoot, } from '../../lib/integrate.js';
|
|
4
4
|
class IntegrateGeminiCli extends BaseCommand {
|
|
5
5
|
async run() {
|
|
6
6
|
const { flags } = await this.parse(IntegrateGeminiCli);
|
|
@@ -25,27 +25,48 @@ class IntegrateGeminiCli extends BaseCommand {
|
|
|
25
25
|
if (projectId) {
|
|
26
26
|
resourceAttrs['respan.project_id'] = projectId;
|
|
27
27
|
}
|
|
28
|
+
// settings.json — only telemetry fields Gemini CLI supports
|
|
28
29
|
const patch = {
|
|
29
30
|
telemetry: {
|
|
30
31
|
enabled: true,
|
|
31
32
|
otlpEndpoint: `${baseUrl}/v2/traces`,
|
|
32
|
-
|
|
33
|
-
Authorization: `Bearer ${apiKey}`,
|
|
34
|
-
},
|
|
33
|
+
otlpProtocol: 'http',
|
|
35
34
|
},
|
|
36
35
|
};
|
|
36
|
+
const merged = deepMerge(existing, patch);
|
|
37
|
+
// .env file — OTel SDK picks up headers & resource attrs from env
|
|
38
|
+
const envDir = scope === 'global'
|
|
39
|
+
? expandHome('~/.gemini')
|
|
40
|
+
: path.join(findProjectRoot(), '.gemini');
|
|
41
|
+
const envPath = path.join(envDir, '.env');
|
|
42
|
+
const envLines = [];
|
|
43
|
+
envLines.push(`OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer ${apiKey}`);
|
|
37
44
|
const otelResStr = toOtelResourceAttrs(resourceAttrs);
|
|
38
45
|
if (otelResStr) {
|
|
39
|
-
|
|
46
|
+
envLines.push(`OTEL_RESOURCE_ATTRIBUTES=${otelResStr}`);
|
|
40
47
|
}
|
|
41
|
-
|
|
48
|
+
// Merge with existing .env (replace our keys, keep the rest)
|
|
49
|
+
const existingEnv = readTextFile(envPath);
|
|
50
|
+
const envKeysToSet = new Set(envLines.map(l => l.split('=')[0]));
|
|
51
|
+
const keptLines = existingEnv
|
|
52
|
+
.split('\n')
|
|
53
|
+
.filter(line => {
|
|
54
|
+
const key = line.split('=')[0];
|
|
55
|
+
return !envKeysToSet.has(key);
|
|
56
|
+
});
|
|
57
|
+
const finalEnv = [...keptLines.filter(l => l.trim() !== ''), ...envLines].join('\n') + '\n';
|
|
42
58
|
if (dryRun) {
|
|
43
59
|
this.log(`[dry-run] Would update: ${settingsPath}`);
|
|
44
60
|
this.log(JSON.stringify(merged, null, 2));
|
|
61
|
+
this.log('');
|
|
62
|
+
this.log(`[dry-run] Would update: ${envPath}`);
|
|
63
|
+
this.log(finalEnv);
|
|
45
64
|
}
|
|
46
65
|
else {
|
|
47
66
|
writeJsonFile(settingsPath, merged);
|
|
48
67
|
this.log(`Updated settings: ${settingsPath}`);
|
|
68
|
+
writeTextFile(envPath, finalEnv);
|
|
69
|
+
this.log(`Updated env: ${envPath}`);
|
|
49
70
|
}
|
|
50
71
|
this.log('');
|
|
51
72
|
this.log(`Gemini CLI integration complete (${scope}).`);
|
package/dist/lib/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as os from 'node:os';
|
|
4
|
-
const CONFIG_DIR = path.join(os.homedir(), '.
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), '.respan');
|
|
5
5
|
const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
|
|
6
6
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
7
7
|
function ensureConfigDir() {
|
package/dist/lib/integrate.d.ts
CHANGED
|
@@ -35,6 +35,10 @@ export declare function findProjectRoot(): string;
|
|
|
35
35
|
* version — upgrading the CLI and re-running integrate updates the hook.
|
|
36
36
|
*/
|
|
37
37
|
export declare function getHookScript(): string;
|
|
38
|
+
/**
|
|
39
|
+
* Return the bundled Codex CLI hook script contents.
|
|
40
|
+
*/
|
|
41
|
+
export declare function getCodexHookScript(): string;
|
|
38
42
|
/**
|
|
39
43
|
* Deep merge source into target.
|
|
40
44
|
* Objects are recursively merged; arrays and primitives from source overwrite.
|
package/dist/lib/integrate.js
CHANGED
|
@@ -87,6 +87,14 @@ export function getHookScript() {
|
|
|
87
87
|
const hookPath = path.join(dir, '..', 'assets', 'hook.py');
|
|
88
88
|
return fs.readFileSync(hookPath, 'utf-8');
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Return the bundled Codex CLI hook script contents.
|
|
92
|
+
*/
|
|
93
|
+
export function getCodexHookScript() {
|
|
94
|
+
const dir = path.dirname(fileURLToPath(import.meta.url));
|
|
95
|
+
const hookPath = path.join(dir, '..', 'assets', 'codex_hook.py');
|
|
96
|
+
return fs.readFileSync(hookPath, 'utf-8');
|
|
97
|
+
}
|
|
90
98
|
// ── Utilities ─────────────────────────────────────────────────────────────
|
|
91
99
|
/**
|
|
92
100
|
* Deep merge source into target.
|