@tracemarketplace/cli 0.0.13 → 0.0.15
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/api-client.d.ts +2 -2
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +2 -2
- package/dist/api-client.js.map +1 -1
- package/dist/cli.js +45 -14
- package/dist/cli.js.map +1 -1
- package/dist/commands/auto-submit.d.ts +2 -1
- package/dist/commands/auto-submit.d.ts.map +1 -1
- package/dist/commands/auto-submit.js +43 -56
- package/dist/commands/auto-submit.js.map +1 -1
- package/dist/commands/daemon.d.ts +8 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +118 -62
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/history.d.ts +3 -1
- package/dist/commands/history.d.ts.map +1 -1
- package/dist/commands/history.js +8 -4
- package/dist/commands/history.js.map +1 -1
- package/dist/commands/login.d.ts +5 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +25 -9
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/register.d.ts +1 -0
- package/dist/commands/register.d.ts.map +1 -1
- package/dist/commands/register.js +4 -39
- package/dist/commands/register.js.map +1 -1
- package/dist/commands/remove-hook.d.ts +6 -0
- package/dist/commands/remove-hook.d.ts.map +1 -0
- package/dist/commands/remove-hook.js +174 -0
- package/dist/commands/remove-hook.js.map +1 -0
- package/dist/commands/setup-hook.d.ts +2 -0
- package/dist/commands/setup-hook.d.ts.map +1 -1
- package/dist/commands/setup-hook.js +85 -41
- package/dist/commands/setup-hook.js.map +1 -1
- package/dist/commands/status.d.ts +3 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +8 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/submit.d.ts +1 -0
- package/dist/commands/submit.d.ts.map +1 -1
- package/dist/commands/submit.js +136 -83
- package/dist/commands/submit.js.map +1 -1
- package/dist/commands/whoami.d.ts +3 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +8 -4
- package/dist/commands/whoami.js.map +1 -1
- package/dist/config.d.ts +33 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +163 -17
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +16 -0
- package/dist/constants.js.map +1 -0
- package/dist/flush.d.ts +46 -0
- package/dist/flush.d.ts.map +1 -0
- package/dist/flush.js +338 -0
- package/dist/flush.js.map +1 -0
- package/dist/flush.test.d.ts +2 -0
- package/dist/flush.test.d.ts.map +1 -0
- package/dist/flush.test.js +175 -0
- package/dist/flush.test.js.map +1 -0
- package/dist/submitter.d.ts.map +1 -1
- package/dist/submitter.js +5 -2
- package/dist/submitter.js.map +1 -1
- package/package.json +8 -7
- package/src/api-client.ts +3 -3
- package/src/cli.ts +51 -14
- package/src/commands/auto-submit.ts +80 -40
- package/src/commands/daemon.ts +166 -59
- package/src/commands/history.ts +9 -4
- package/src/commands/login.ts +37 -9
- package/src/commands/register.ts +5 -49
- package/src/commands/remove-hook.ts +194 -0
- package/src/commands/setup-hook.ts +93 -43
- package/src/commands/status.ts +8 -4
- package/src/commands/submit.ts +189 -83
- package/src/commands/whoami.ts +8 -4
- package/src/config.ts +223 -21
- package/src/constants.ts +18 -0
- package/src/flush.test.ts +214 -0
- package/src/flush.ts +505 -0
- package/vitest.config.ts +8 -0
- package/src/submitter.ts +0 -110
package/src/commands/submit.ts
CHANGED
|
@@ -1,22 +1,54 @@
|
|
|
1
1
|
import { existsSync } from "fs";
|
|
2
|
-
import {
|
|
3
|
-
import { join } from "path";
|
|
2
|
+
import { readFile } from "fs/promises";
|
|
4
3
|
import chalk from "chalk";
|
|
5
4
|
import ora from "ora";
|
|
6
5
|
import inquirer from "inquirer";
|
|
7
|
-
import { extractClaudeCode, extractCodex, extractCursor,
|
|
8
|
-
import { loadConfig } from "../config.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
6
|
+
import { extractClaudeCode, extractCodex, extractCursor, type NormalizedTrace } from "@tracemarketplace/shared";
|
|
7
|
+
import { loadConfig, loadState, resolveProfile, stateKey } from "../config.js";
|
|
8
|
+
import { loginCommandForProfile } from "../constants.js";
|
|
9
|
+
import {
|
|
10
|
+
buildCursorSessionSource,
|
|
11
|
+
buildFileSessionSource,
|
|
12
|
+
createFreshSessionState,
|
|
13
|
+
flushTrackedSessions,
|
|
14
|
+
migrateLegacySessionState,
|
|
15
|
+
planSessionUploads,
|
|
16
|
+
type SessionSource,
|
|
17
|
+
} from "../flush.js";
|
|
18
|
+
import { CURSOR_DB_PATH, findFiles } from "../sessions.js";
|
|
12
19
|
|
|
13
20
|
interface SubmitOptions {
|
|
21
|
+
profile?: string;
|
|
14
22
|
tool?: string;
|
|
15
23
|
session?: string;
|
|
16
24
|
dryRun?: boolean;
|
|
17
25
|
since?: string;
|
|
18
26
|
}
|
|
19
27
|
|
|
28
|
+
interface DiscoveredSession {
|
|
29
|
+
source: SessionSource;
|
|
30
|
+
trace: NormalizedTrace;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface PlannedSession extends DiscoveredSession {
|
|
34
|
+
readyChunks: number;
|
|
35
|
+
pending: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function pushSessionIfNonEmpty(
|
|
39
|
+
sessions: DiscoveredSession[],
|
|
40
|
+
source: SessionSource,
|
|
41
|
+
trace: NormalizedTrace,
|
|
42
|
+
emptyLabel: string,
|
|
43
|
+
skippedEmpty: string[],
|
|
44
|
+
): void {
|
|
45
|
+
if (trace.turn_count > 0) {
|
|
46
|
+
sessions.push({ source, trace });
|
|
47
|
+
} else {
|
|
48
|
+
skippedEmpty.push(emptyLabel);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
20
52
|
function parseSinceMs(since: string): number {
|
|
21
53
|
const m = since.match(/^(\d+)(m|h|d)$/);
|
|
22
54
|
if (!m) return 30 * 24 * 60 * 60 * 1000;
|
|
@@ -27,9 +59,10 @@ function parseSinceMs(since: string): number {
|
|
|
27
59
|
}
|
|
28
60
|
|
|
29
61
|
export async function submitCommand(opts: SubmitOptions): Promise<void> {
|
|
30
|
-
const
|
|
62
|
+
const profile = resolveProfile(opts.profile);
|
|
63
|
+
const config = loadConfig(profile);
|
|
31
64
|
if (!config) {
|
|
32
|
-
console.error(chalk.red(
|
|
65
|
+
console.error(chalk.red(`Not authenticated for profile '${profile}'. Run: ${loginCommandForProfile(profile)}`));
|
|
33
66
|
process.exit(1);
|
|
34
67
|
}
|
|
35
68
|
|
|
@@ -37,8 +70,9 @@ export async function submitCommand(opts: SubmitOptions): Promise<void> {
|
|
|
37
70
|
const sinceDays = sinceMs / (24 * 60 * 60 * 1000);
|
|
38
71
|
const spinner = ora("Discovering sessions...").start();
|
|
39
72
|
|
|
40
|
-
const
|
|
73
|
+
const discovered: DiscoveredSession[] = [];
|
|
41
74
|
const errors: string[] = [];
|
|
75
|
+
const skippedEmpty: string[] = [];
|
|
42
76
|
|
|
43
77
|
const toolAlias: Record<string, string> = {
|
|
44
78
|
"claude-code": "claude_code",
|
|
@@ -50,11 +84,17 @@ export async function submitCommand(opts: SubmitOptions): Promise<void> {
|
|
|
50
84
|
if (tools.includes("claude_code")) {
|
|
51
85
|
const files = findFiles("claude_code", sinceDays);
|
|
52
86
|
spinner.text = `Found ${files.length} Claude Code sessions`;
|
|
53
|
-
for (const
|
|
87
|
+
for (const filePath of files) {
|
|
54
88
|
try {
|
|
55
|
-
|
|
89
|
+
pushSessionIfNonEmpty(
|
|
90
|
+
discovered,
|
|
91
|
+
buildFileSessionSource("claude_code", filePath),
|
|
92
|
+
await extractClaudeCode(filePath, config.email),
|
|
93
|
+
`Claude Code ${filePath}`,
|
|
94
|
+
skippedEmpty,
|
|
95
|
+
);
|
|
56
96
|
} catch (e) {
|
|
57
|
-
errors.push(`Claude Code ${
|
|
97
|
+
errors.push(`Claude Code ${filePath}: ${e}`);
|
|
58
98
|
}
|
|
59
99
|
}
|
|
60
100
|
}
|
|
@@ -62,105 +102,163 @@ export async function submitCommand(opts: SubmitOptions): Promise<void> {
|
|
|
62
102
|
if (tools.includes("codex_cli")) {
|
|
63
103
|
const files = findFiles("codex_cli", sinceDays);
|
|
64
104
|
spinner.text = `Found ${files.length} Codex sessions`;
|
|
65
|
-
for (const
|
|
105
|
+
for (const filePath of files) {
|
|
66
106
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
107
|
+
pushSessionIfNonEmpty(
|
|
108
|
+
discovered,
|
|
109
|
+
buildFileSessionSource("codex_cli", filePath),
|
|
110
|
+
await extractCodex(await readFile(filePath), config.email),
|
|
111
|
+
`Codex ${filePath}`,
|
|
112
|
+
skippedEmpty,
|
|
113
|
+
);
|
|
70
114
|
} catch (e) {
|
|
71
|
-
errors.push(`Codex ${
|
|
115
|
+
errors.push(`Codex ${filePath}: ${e}`);
|
|
72
116
|
}
|
|
73
117
|
}
|
|
74
118
|
}
|
|
75
119
|
|
|
76
|
-
if (tools.includes("cursor")) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
if (tools.includes("cursor") && existsSync(CURSOR_DB_PATH)) {
|
|
121
|
+
try {
|
|
122
|
+
const { default: Database } = await import("better-sqlite3");
|
|
123
|
+
const db = new Database(CURSOR_DB_PATH, { readonly: true });
|
|
124
|
+
const cutoff = Date.now() - sinceMs;
|
|
125
|
+
const allRows = db.prepare("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'").all() as Array<{ key: string; value: string }>;
|
|
126
|
+
const rows = allRows.filter((row) => {
|
|
127
|
+
try {
|
|
128
|
+
const data = JSON.parse(row.value);
|
|
129
|
+
return (data.createdAt ?? 0) >= cutoff;
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
db.close();
|
|
135
|
+
spinner.text = `Found ${rows.length} Cursor sessions`;
|
|
136
|
+
for (const { key } of rows) {
|
|
137
|
+
const sessionId = key.replace("composerData:", "");
|
|
138
|
+
try {
|
|
139
|
+
pushSessionIfNonEmpty(
|
|
140
|
+
discovered,
|
|
141
|
+
buildCursorSessionSource(sessionId),
|
|
142
|
+
await extractCursor(CURSOR_DB_PATH, sessionId, config.email),
|
|
143
|
+
`Cursor ${sessionId}`,
|
|
144
|
+
skippedEmpty,
|
|
145
|
+
);
|
|
146
|
+
} catch (e) {
|
|
147
|
+
errors.push(`Cursor ${sessionId}: ${e}`);
|
|
95
148
|
}
|
|
96
|
-
} catch (e) {
|
|
97
|
-
errors.push(`Cursor DB: ${e}`);
|
|
98
149
|
}
|
|
150
|
+
} catch (e) {
|
|
151
|
+
errors.push(`Cursor DB: ${e}`);
|
|
99
152
|
}
|
|
100
153
|
}
|
|
101
154
|
|
|
102
155
|
spinner.stop();
|
|
103
156
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
: traces;
|
|
157
|
+
const filtered = opts.session
|
|
158
|
+
? discovered.filter((session) => session.trace.source_session_id.startsWith(opts.session!))
|
|
159
|
+
: discovered;
|
|
108
160
|
|
|
109
|
-
if (opts.session &&
|
|
161
|
+
if (opts.session && filtered.length === 0) {
|
|
110
162
|
console.log(chalk.yellow(`No session found matching ID: ${opts.session}`));
|
|
111
163
|
return;
|
|
112
164
|
}
|
|
113
165
|
|
|
114
166
|
if (errors.length > 0) {
|
|
115
167
|
console.log(chalk.yellow(`\n${errors.length} extraction error(s):`));
|
|
116
|
-
errors.slice(0, 3).forEach((
|
|
168
|
+
errors.slice(0, 3).forEach((error) => console.log(chalk.gray(` ${error}`)));
|
|
117
169
|
if (errors.length > 3) console.log(chalk.gray(` ...and ${errors.length - 3} more`));
|
|
118
170
|
}
|
|
119
171
|
|
|
120
|
-
if (
|
|
121
|
-
console.log(chalk.
|
|
172
|
+
if (skippedEmpty.length > 0) {
|
|
173
|
+
console.log(chalk.gray(`\nSkipped ${skippedEmpty.length} empty/in-progress session(s).`));
|
|
174
|
+
skippedEmpty.slice(0, 3).forEach((label) => console.log(chalk.gray(` ${label}`)));
|
|
175
|
+
if (skippedEmpty.length > 3) {
|
|
176
|
+
console.log(chalk.gray(` ...and ${skippedEmpty.length - 3} more`));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (filtered.length === 0) {
|
|
181
|
+
console.log(chalk.yellow(`No sessions found in the last ${sinceDays} days.`));
|
|
122
182
|
return;
|
|
123
183
|
}
|
|
124
184
|
|
|
125
|
-
|
|
126
|
-
|
|
185
|
+
const state = loadState(config.profile);
|
|
186
|
+
const now = new Date();
|
|
187
|
+
const plannedSessions = filtered.map((session) => {
|
|
188
|
+
const key = stateKey(session.trace.source_tool, session.trace.source_session_id);
|
|
189
|
+
const existing = state.sessions[key];
|
|
190
|
+
const legacyChunkIndex = state.chunks[key];
|
|
191
|
+
const cursor = existing
|
|
192
|
+
? {
|
|
193
|
+
...existing,
|
|
194
|
+
locator: session.source.locator,
|
|
195
|
+
sourceTool: session.trace.source_tool,
|
|
196
|
+
sourceSessionId: session.trace.source_session_id,
|
|
197
|
+
}
|
|
198
|
+
: typeof legacyChunkIndex === "number"
|
|
199
|
+
? migrateLegacySessionState(session.source, session.trace, legacyChunkIndex)
|
|
200
|
+
: createFreshSessionState(session.source, session.trace);
|
|
201
|
+
const plan = planSessionUploads(session.trace, cursor, now);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
...session,
|
|
205
|
+
readyChunks: plan.uploads.length,
|
|
206
|
+
pending: plan.pending,
|
|
207
|
+
} satisfies PlannedSession;
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const readyChunkCount = plannedSessions.reduce((sum, session) => sum + session.readyChunks, 0);
|
|
211
|
+
const readySessionCount = plannedSessions.filter((session) => session.readyChunks > 0).length;
|
|
212
|
+
const pendingSessionCount = plannedSessions.filter((session) => session.pending).length;
|
|
213
|
+
|
|
214
|
+
console.log(chalk.gray(`Target: ${config.profile} (${config.serverUrl})`));
|
|
215
|
+
console.log(`\n${chalk.bold("Sessions scanned:")} (${plannedSessions.length} total)\n`);
|
|
127
216
|
console.log(
|
|
128
217
|
chalk.gray(
|
|
129
218
|
" Tool".padEnd(16) +
|
|
130
219
|
"Session ID".padEnd(20) +
|
|
131
220
|
"Turns".padEnd(8) +
|
|
132
|
-
"
|
|
133
|
-
"
|
|
221
|
+
"Ready".padEnd(8) +
|
|
222
|
+
"Status"
|
|
134
223
|
)
|
|
135
224
|
);
|
|
136
|
-
console.log(chalk.gray(" " + "─".repeat(
|
|
225
|
+
console.log(chalk.gray(" " + "─".repeat(62)));
|
|
137
226
|
|
|
138
|
-
for (const
|
|
139
|
-
const tokens = (t.total_input_tokens ?? 0) + (t.total_output_tokens ?? 0);
|
|
140
|
-
const estPayout = t.content_fidelity === "full" ? "$0.04" : "$0.02";
|
|
227
|
+
for (const session of plannedSessions.slice(0, 20)) {
|
|
141
228
|
console.log(
|
|
142
229
|
" " +
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
String(
|
|
146
|
-
String(
|
|
147
|
-
|
|
230
|
+
session.trace.source_tool.padEnd(16) +
|
|
231
|
+
session.trace.source_session_id.slice(0, 18).padEnd(20) +
|
|
232
|
+
String(session.trace.turn_count).padEnd(8) +
|
|
233
|
+
String(session.readyChunks).padEnd(8) +
|
|
234
|
+
describeSessionStatus(session)
|
|
148
235
|
);
|
|
149
236
|
}
|
|
150
|
-
if (
|
|
151
|
-
console.log(chalk.gray(` ... and ${
|
|
237
|
+
if (plannedSessions.length > 20) {
|
|
238
|
+
console.log(chalk.gray(` ... and ${plannedSessions.length - 20} more`));
|
|
152
239
|
}
|
|
153
240
|
|
|
241
|
+
console.log(
|
|
242
|
+
chalk.gray(
|
|
243
|
+
`\nReady chunks: ${readyChunkCount} across ${readySessionCount} session(s); pending sessions: ${pendingSessionCount}`
|
|
244
|
+
)
|
|
245
|
+
);
|
|
246
|
+
|
|
154
247
|
if (opts.dryRun) {
|
|
155
248
|
console.log(chalk.cyan("\nDry run — nothing submitted."));
|
|
156
249
|
return;
|
|
157
250
|
}
|
|
158
251
|
|
|
252
|
+
if (readyChunkCount === 0) {
|
|
253
|
+
console.log(chalk.yellow("\nNo finalized chunks are ready to submit yet."));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
159
257
|
const { confirm } = await inquirer.prompt([
|
|
160
258
|
{
|
|
161
259
|
type: "confirm",
|
|
162
260
|
name: "confirm",
|
|
163
|
-
message: `Submit ${
|
|
261
|
+
message: `Submit ${readyChunkCount} finalized chunk(s) from ${readySessionCount} session(s) to ${config.profile} (${config.serverUrl})?`,
|
|
164
262
|
default: true,
|
|
165
263
|
},
|
|
166
264
|
]);
|
|
@@ -170,36 +268,44 @@ export async function submitCommand(opts: SubmitOptions): Promise<void> {
|
|
|
170
268
|
return;
|
|
171
269
|
}
|
|
172
270
|
|
|
173
|
-
const uploadSpinner = ora(`Submitting ${
|
|
174
|
-
const client = new ApiClient(config.serverUrl, config.apiKey);
|
|
271
|
+
const uploadSpinner = ora(`Submitting ${readyChunkCount} finalized chunk(s) to ${config.profile}...`).start();
|
|
175
272
|
|
|
176
273
|
try {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
submission_id: string;
|
|
183
|
-
accepted: number;
|
|
184
|
-
duplicate: number;
|
|
185
|
-
total: number;
|
|
186
|
-
traces: Array<{ payout_cents?: number }>;
|
|
187
|
-
};
|
|
274
|
+
const result = await flushTrackedSessions(
|
|
275
|
+
config,
|
|
276
|
+
plannedSessions.map((session) => session.source),
|
|
277
|
+
{ includeIdleTracked: false }
|
|
278
|
+
);
|
|
188
279
|
|
|
189
280
|
uploadSpinner.stop();
|
|
190
281
|
|
|
191
|
-
const
|
|
192
|
-
result.traces?.reduce((s, t) => s + (t.payout_cents ?? 0), 0) ?? 0;
|
|
282
|
+
const failedSessions = result.results.filter((session) => session.error && session.error !== "Empty session");
|
|
193
283
|
|
|
194
284
|
console.log(chalk.green("\nSubmission complete!"));
|
|
195
|
-
console.log(`
|
|
196
|
-
console.log(`
|
|
197
|
-
console.log(`
|
|
198
|
-
console.log(` Payout:
|
|
199
|
-
|
|
285
|
+
console.log(` Uploaded chunks: ${chalk.bold(result.uploadedChunks)}`);
|
|
286
|
+
console.log(` Duplicate chunks: ${chalk.gray(result.duplicateChunks)}`);
|
|
287
|
+
console.log(` Pending sessions: ${result.pendingSessions}`);
|
|
288
|
+
console.log(` Payout: ${chalk.green("$" + (result.payoutCents / 100).toFixed(2))}`);
|
|
289
|
+
|
|
290
|
+
if (failedSessions.length > 0) {
|
|
291
|
+
console.log(chalk.yellow(`\n${failedSessions.length} session(s) failed during submit:`));
|
|
292
|
+
failedSessions.slice(0, 3).forEach((session) => {
|
|
293
|
+
console.log(chalk.gray(` ${session.source.label}: ${session.error}`));
|
|
294
|
+
});
|
|
295
|
+
if (failedSessions.length > 3) {
|
|
296
|
+
console.log(chalk.gray(` ...and ${failedSessions.length - 3} more`));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
200
299
|
} catch (e) {
|
|
201
300
|
uploadSpinner.fail("Submission failed");
|
|
202
301
|
console.error(chalk.red(String(e)));
|
|
203
302
|
process.exit(1);
|
|
204
303
|
}
|
|
205
304
|
}
|
|
305
|
+
|
|
306
|
+
function describeSessionStatus(session: PlannedSession): string {
|
|
307
|
+
if (session.readyChunks > 0 && session.pending) return "ready + pending";
|
|
308
|
+
if (session.readyChunks > 0) return "ready";
|
|
309
|
+
if (session.pending) return "pending";
|
|
310
|
+
return "up-to-date";
|
|
311
|
+
}
|
package/src/commands/whoami.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { loadConfig } from "../config.js";
|
|
2
|
+
import { loadConfig, resolveProfile } from "../config.js";
|
|
3
3
|
import { ApiClient } from "../api-client.js";
|
|
4
|
+
import { loginCommandForProfile } from "../constants.js";
|
|
4
5
|
|
|
5
|
-
export async function whoamiCommand(): Promise<void> {
|
|
6
|
-
const
|
|
6
|
+
export async function whoamiCommand(opts: { profile?: string } = {}): Promise<void> {
|
|
7
|
+
const profile = resolveProfile(opts.profile);
|
|
8
|
+
const config = loadConfig(profile);
|
|
7
9
|
if (!config) {
|
|
8
|
-
console.error(chalk.red(
|
|
10
|
+
console.error(chalk.red(`Not authenticated for profile '${profile}'. Run: ${loginCommandForProfile(profile)}`));
|
|
9
11
|
process.exit(1);
|
|
10
12
|
}
|
|
11
13
|
|
|
@@ -18,5 +20,7 @@ export async function whoamiCommand(): Promise<void> {
|
|
|
18
20
|
|
|
19
21
|
const balance = (user.balance_cents ?? user.balanceCents ?? 0) / 100;
|
|
20
22
|
console.log(chalk.bold(user.email));
|
|
23
|
+
console.log(chalk.gray("Profile:"), config.profile);
|
|
24
|
+
console.log(chalk.gray("Server:"), config.serverUrl);
|
|
21
25
|
console.log(chalk.gray("Balance:"), chalk.green(`$${balance.toFixed(2)}`));
|
|
22
26
|
}
|