autohand-cli 0.7.4 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -43
- package/README.md +3 -1
- package/dist/SessionManager-M5ZLCLCW.cjs +9 -0
- package/dist/{SessionManager-IMW2HGR3.js → SessionManager-XDBEQUPG.js} +1 -1
- package/dist/chunk-2W3QTBNG.cjs +151 -0
- package/dist/{chunk-546JQ3ND.js → chunk-4KZCGK7D.js} +4 -101
- package/dist/chunk-5MCDN53U.js +102 -0
- package/dist/{chunk-UPR5PKX4.cjs → chunk-67NJKV5A.cjs} +5 -1
- package/dist/chunk-723DZKBU.js +613 -0
- package/dist/chunk-JYTXG6OV.cjs +102 -0
- package/dist/chunk-K6NBYSME.cjs +613 -0
- package/dist/{chunk-XJZYEURA.cjs → chunk-KJ67C72C.cjs} +6 -103
- package/dist/chunk-R5KNHJ27.js +151 -0
- package/dist/{chunk-2FLBGPE3.js → chunk-VO3JKFUH.js} +5 -1
- package/dist/index.cjs +1979 -585
- package/dist/index.js +1941 -547
- package/dist/resume-ANISKRWL.cjs +9 -0
- package/dist/{resume-2NERFSTD.js → resume-CWYAK6XR.js} +2 -1
- package/dist/share-3PSV53CQ.js +10 -0
- package/dist/share-4ACH6626.cjs +10 -0
- package/dist/status-4U5CPUVT.cjs +9 -0
- package/dist/{status-UT4UQN3H.js → status-GPAZ67ZZ.js} +2 -1
- package/package.json +3 -2
- package/dist/SessionManager-VZNWGX4O.cjs +0 -9
- package/dist/chunk-55DQY6B5.js +0 -49
- package/dist/chunk-RYY5I7QN.cjs +0 -49
- package/dist/resume-OYZMJRNO.cjs +0 -8
- package/dist/status-U5NH6SYY.cjs +0 -8
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
var _chunkJYTXG6OVcjs = require('./chunk-JYTXG6OV.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
var _chunkXTHHDIBGcjs = require('./chunk-XTHHDIBG.cjs');
|
|
7
|
+
|
|
8
|
+
// src/commands/share.ts
|
|
9
|
+
var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk);
|
|
10
|
+
var _enquirer = require('enquirer'); var _enquirer2 = _interopRequireDefault(_enquirer);
|
|
11
|
+
var _ora = require('ora'); var _ora2 = _interopRequireDefault(_ora);
|
|
12
|
+
|
|
13
|
+
// src/share/costEstimator.ts
|
|
14
|
+
var DEFAULT_COST_PER_1K_TOKENS = 3e-3;
|
|
15
|
+
function estimateCost(totalTokens) {
|
|
16
|
+
if (totalTokens <= 0) return 0;
|
|
17
|
+
const cost = totalTokens / 1e3 * DEFAULT_COST_PER_1K_TOKENS;
|
|
18
|
+
return Math.round(cost * 1e4) / 1e4;
|
|
19
|
+
}
|
|
20
|
+
function formatCost(cost) {
|
|
21
|
+
if (cost < 0.01) {
|
|
22
|
+
if (cost === 0) return "$0.00";
|
|
23
|
+
return `$${cost.toFixed(4)}`;
|
|
24
|
+
}
|
|
25
|
+
return `$${cost.toFixed(2)}`;
|
|
26
|
+
}
|
|
27
|
+
function formatTokens(tokens) {
|
|
28
|
+
if (tokens >= 1e6) {
|
|
29
|
+
return `${(tokens / 1e6).toFixed(1)}M`;
|
|
30
|
+
}
|
|
31
|
+
if (tokens >= 1e3) {
|
|
32
|
+
return `${(tokens / 1e3).toFixed(1)}K`;
|
|
33
|
+
}
|
|
34
|
+
return tokens.toString();
|
|
35
|
+
}
|
|
36
|
+
function formatDuration(seconds) {
|
|
37
|
+
if (seconds < 60) {
|
|
38
|
+
return `${seconds}s`;
|
|
39
|
+
}
|
|
40
|
+
const minutes = Math.floor(seconds / 60);
|
|
41
|
+
const remainingSeconds = seconds % 60;
|
|
42
|
+
if (minutes < 60) {
|
|
43
|
+
if (remainingSeconds === 0) return `${minutes}m`;
|
|
44
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
45
|
+
}
|
|
46
|
+
const hours = Math.floor(minutes / 60);
|
|
47
|
+
const remainingMinutes = minutes % 60;
|
|
48
|
+
if (remainingMinutes === 0) return `${hours}h`;
|
|
49
|
+
return `${hours}h ${remainingMinutes}m`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/share/sessionSerializer.ts
|
|
53
|
+
function extractToolUsage(messages) {
|
|
54
|
+
const toolCounts = /* @__PURE__ */ new Map();
|
|
55
|
+
for (const message of messages) {
|
|
56
|
+
if (message.role === "assistant" && message.toolCalls) {
|
|
57
|
+
for (const toolCall of message.toolCalls) {
|
|
58
|
+
const toolName = _optionalChain([toolCall, 'access', _ => _.function, 'optionalAccess', _2 => _2.name]) || toolCall.name || "unknown";
|
|
59
|
+
const current = toolCounts.get(toolName) || { total: 0, success: 0 };
|
|
60
|
+
current.total++;
|
|
61
|
+
toolCounts.set(toolName, current);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (message.role === "tool" && message.name) {
|
|
65
|
+
const current = toolCounts.get(message.name);
|
|
66
|
+
if (current) {
|
|
67
|
+
const isError = message.content.toLowerCase().includes("error") || message.content.toLowerCase().includes("failed");
|
|
68
|
+
if (!isError) {
|
|
69
|
+
current.success++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const result = [];
|
|
75
|
+
for (const [name, counts] of toolCounts) {
|
|
76
|
+
result.push({
|
|
77
|
+
name,
|
|
78
|
+
count: counts.total,
|
|
79
|
+
successRate: counts.total > 0 ? counts.success / counts.total : void 0
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return result.sort((a, b) => b.count - a.count);
|
|
83
|
+
}
|
|
84
|
+
function estimateTokensFromMessages(messages) {
|
|
85
|
+
let input = 0;
|
|
86
|
+
let output = 0;
|
|
87
|
+
for (const message of messages) {
|
|
88
|
+
const chars = message.content.length;
|
|
89
|
+
const tokens = Math.ceil(chars / 4);
|
|
90
|
+
if (message.role === "user" || message.role === "system") {
|
|
91
|
+
input += tokens;
|
|
92
|
+
} else {
|
|
93
|
+
output += tokens;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
total: input + output,
|
|
98
|
+
input,
|
|
99
|
+
output
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function parseGitDiff(diffContent) {
|
|
103
|
+
if (!diffContent || diffContent.trim().length === 0) {
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
const lines = diffContent.split("\n");
|
|
107
|
+
const filesChanged = /* @__PURE__ */ new Set();
|
|
108
|
+
let linesAdded = 0;
|
|
109
|
+
let linesRemoved = 0;
|
|
110
|
+
for (const line of lines) {
|
|
111
|
+
if (line.startsWith("diff --git")) {
|
|
112
|
+
const match = line.match(/diff --git a\/(.+) b\/(.+)/);
|
|
113
|
+
if (match) {
|
|
114
|
+
filesChanged.add(match[2]);
|
|
115
|
+
}
|
|
116
|
+
} else if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
117
|
+
linesAdded++;
|
|
118
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
119
|
+
linesRemoved++;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (filesChanged.size === 0 && linesAdded === 0 && linesRemoved === 0) {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
filesChanged: Array.from(filesChanged),
|
|
127
|
+
linesAdded,
|
|
128
|
+
linesRemoved,
|
|
129
|
+
diffContent
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function calculateDuration(startedAt, endedAt) {
|
|
133
|
+
const start = new Date(startedAt).getTime();
|
|
134
|
+
const end = new Date(endedAt).getTime();
|
|
135
|
+
return Math.max(0, Math.floor((end - start) / 1e3));
|
|
136
|
+
}
|
|
137
|
+
function serializeSession(session, options) {
|
|
138
|
+
const messages = session.getMessages();
|
|
139
|
+
const { metadata: metadata2 } = session;
|
|
140
|
+
const endedAt = metadata2.closedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
141
|
+
const durationSeconds = calculateDuration(metadata2.createdAt, endedAt);
|
|
142
|
+
const sessionMetadata = {
|
|
143
|
+
sessionId: metadata2.sessionId,
|
|
144
|
+
projectName: metadata2.projectName,
|
|
145
|
+
model: options.model || metadata2.model,
|
|
146
|
+
provider: options.provider || "openrouter",
|
|
147
|
+
startedAt: metadata2.createdAt,
|
|
148
|
+
endedAt,
|
|
149
|
+
durationSeconds,
|
|
150
|
+
messageCount: messages.length,
|
|
151
|
+
status: metadata2.status,
|
|
152
|
+
summary: metadata2.summary
|
|
153
|
+
};
|
|
154
|
+
let usage;
|
|
155
|
+
if (options.totalTokens && options.totalTokens > 0) {
|
|
156
|
+
const inputTokens = Math.floor(options.totalTokens * 0.3);
|
|
157
|
+
const outputTokens = options.totalTokens - inputTokens;
|
|
158
|
+
usage = {
|
|
159
|
+
totalTokens: options.totalTokens,
|
|
160
|
+
inputTokens,
|
|
161
|
+
outputTokens,
|
|
162
|
+
estimatedCost: estimateCost(options.totalTokens)
|
|
163
|
+
};
|
|
164
|
+
} else {
|
|
165
|
+
const estimated = estimateTokensFromMessages(messages);
|
|
166
|
+
usage = {
|
|
167
|
+
totalTokens: estimated.total,
|
|
168
|
+
inputTokens: estimated.input,
|
|
169
|
+
outputTokens: estimated.output,
|
|
170
|
+
estimatedCost: estimateCost(estimated.total)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const toolUsage = extractToolUsage(messages);
|
|
174
|
+
const gitDiff = options.gitDiff ? parseGitDiff(options.gitDiff) : void 0;
|
|
175
|
+
const client = {
|
|
176
|
+
cliVersion: _chunkJYTXG6OVcjs.package_default.version,
|
|
177
|
+
platform: process.platform,
|
|
178
|
+
deviceId: options.deviceId
|
|
179
|
+
};
|
|
180
|
+
const sharedMessages = messages.map((msg) => ({
|
|
181
|
+
role: msg.role,
|
|
182
|
+
content: msg.content,
|
|
183
|
+
timestamp: msg.timestamp,
|
|
184
|
+
name: msg.name,
|
|
185
|
+
toolCalls: msg.toolCalls,
|
|
186
|
+
tool_call_id: msg.tool_call_id
|
|
187
|
+
}));
|
|
188
|
+
return {
|
|
189
|
+
metadata: sessionMetadata,
|
|
190
|
+
usage,
|
|
191
|
+
toolUsage,
|
|
192
|
+
gitDiff,
|
|
193
|
+
messages: sharedMessages,
|
|
194
|
+
visibility: options.visibility,
|
|
195
|
+
client,
|
|
196
|
+
userId: options.userId
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/share/ShareApiClient.ts
|
|
201
|
+
var _fsextra = require('fs-extra'); var _fsextra2 = _interopRequireDefault(_fsextra);
|
|
202
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
203
|
+
function getBaseUrl() {
|
|
204
|
+
return process.env.AUTOHAND_SHARE_URL || "https://autohand.link/api";
|
|
205
|
+
}
|
|
206
|
+
var DEFAULT_CONFIG = {
|
|
207
|
+
baseUrl: getBaseUrl(),
|
|
208
|
+
timeout: 3e4,
|
|
209
|
+
// 30s for large payloads
|
|
210
|
+
maxRetries: 3,
|
|
211
|
+
offlineQueue: true,
|
|
212
|
+
cliVersion: _chunkJYTXG6OVcjs.package_default.version
|
|
213
|
+
};
|
|
214
|
+
var ShareApiClient = class {
|
|
215
|
+
constructor(configOverrides) {
|
|
216
|
+
this.deviceId = null;
|
|
217
|
+
this.config = { ...DEFAULT_CONFIG, ...configOverrides };
|
|
218
|
+
const dataDir = _path2.default.join(_chunkXTHHDIBGcjs.AUTOHAND_PATHS.config, "share");
|
|
219
|
+
this.queuePath = _path2.default.join(dataDir, "queue.json");
|
|
220
|
+
this.deviceIdPath = _path2.default.join(_chunkXTHHDIBGcjs.AUTOHAND_PATHS.feedback, ".device-id");
|
|
221
|
+
}
|
|
222
|
+
// ============ Device ID ============
|
|
223
|
+
/**
|
|
224
|
+
* Get or create anonymous device identifier
|
|
225
|
+
* Shared with FeedbackApiClient for consistency
|
|
226
|
+
*/
|
|
227
|
+
async getDeviceId() {
|
|
228
|
+
if (this.deviceId) return this.deviceId;
|
|
229
|
+
try {
|
|
230
|
+
if (await _fsextra2.default.pathExists(this.deviceIdPath)) {
|
|
231
|
+
this.deviceId = (await _fsextra2.default.readFile(this.deviceIdPath, "utf8")).trim();
|
|
232
|
+
return this.deviceId;
|
|
233
|
+
}
|
|
234
|
+
} catch (e) {
|
|
235
|
+
}
|
|
236
|
+
this.deviceId = `anon_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
237
|
+
try {
|
|
238
|
+
await _fsextra2.default.ensureDir(_path2.default.dirname(this.deviceIdPath));
|
|
239
|
+
await _fsextra2.default.writeFile(this.deviceIdPath, this.deviceId);
|
|
240
|
+
} catch (e2) {
|
|
241
|
+
}
|
|
242
|
+
return this.deviceId;
|
|
243
|
+
}
|
|
244
|
+
// ============ Create Share ============
|
|
245
|
+
/**
|
|
246
|
+
* Upload a session to create a shareable link
|
|
247
|
+
*/
|
|
248
|
+
async createShare(payload) {
|
|
249
|
+
try {
|
|
250
|
+
const response = await this.sendToApi("POST", "/share", payload);
|
|
251
|
+
if (response.success) {
|
|
252
|
+
this.flushQueue().catch(() => {
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return response;
|
|
256
|
+
} catch (error) {
|
|
257
|
+
if (this.config.offlineQueue) {
|
|
258
|
+
await this.addToQueue(payload);
|
|
259
|
+
return {
|
|
260
|
+
success: false,
|
|
261
|
+
error: `Queued for retry: ${error.message}`
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
success: false,
|
|
266
|
+
error: error.message
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// ============ Delete Share ============
|
|
271
|
+
/**
|
|
272
|
+
* Delete a shared session (owner only)
|
|
273
|
+
*/
|
|
274
|
+
async deleteShare(shareId) {
|
|
275
|
+
const deviceId = await this.getDeviceId();
|
|
276
|
+
try {
|
|
277
|
+
const response = await this.sendToApi(
|
|
278
|
+
"DELETE",
|
|
279
|
+
`/share/${shareId}`,
|
|
280
|
+
void 0,
|
|
281
|
+
{ "X-Device-ID": deviceId }
|
|
282
|
+
);
|
|
283
|
+
return response;
|
|
284
|
+
} catch (error) {
|
|
285
|
+
return {
|
|
286
|
+
success: false,
|
|
287
|
+
error: error.message
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// ============ API Communication ============
|
|
292
|
+
/**
|
|
293
|
+
* Send request to API endpoint
|
|
294
|
+
*/
|
|
295
|
+
async sendToApi(method, endpoint, body, extraHeaders) {
|
|
296
|
+
const controller = new AbortController();
|
|
297
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
298
|
+
try {
|
|
299
|
+
const headers = {
|
|
300
|
+
"Content-Type": "application/json",
|
|
301
|
+
"X-CLI-Version": this.config.cliVersion,
|
|
302
|
+
...extraHeaders
|
|
303
|
+
};
|
|
304
|
+
const response = await fetch(`${this.config.baseUrl}${endpoint}`, {
|
|
305
|
+
method,
|
|
306
|
+
headers,
|
|
307
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
308
|
+
signal: controller.signal
|
|
309
|
+
});
|
|
310
|
+
clearTimeout(timeoutId);
|
|
311
|
+
if (!response.ok) {
|
|
312
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
313
|
+
throw new Error(`API error: ${response.status} ${errorText}`);
|
|
314
|
+
}
|
|
315
|
+
const data = await response.json();
|
|
316
|
+
return data;
|
|
317
|
+
} catch (error) {
|
|
318
|
+
clearTimeout(timeoutId);
|
|
319
|
+
if (error.name === "AbortError") {
|
|
320
|
+
throw new Error("Request timeout");
|
|
321
|
+
}
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// ============ Offline Queue ============
|
|
326
|
+
/**
|
|
327
|
+
* Add share to offline queue
|
|
328
|
+
*/
|
|
329
|
+
async addToQueue(payload) {
|
|
330
|
+
try {
|
|
331
|
+
let queue = [];
|
|
332
|
+
if (await _fsextra2.default.pathExists(this.queuePath)) {
|
|
333
|
+
queue = await _fsextra2.default.readJson(this.queuePath);
|
|
334
|
+
}
|
|
335
|
+
queue.push({
|
|
336
|
+
payload,
|
|
337
|
+
attempts: 0,
|
|
338
|
+
lastAttempt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
339
|
+
id: `q_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`
|
|
340
|
+
});
|
|
341
|
+
if (queue.length > 10) {
|
|
342
|
+
queue = queue.slice(-10);
|
|
343
|
+
}
|
|
344
|
+
await _fsextra2.default.ensureDir(_path2.default.dirname(this.queuePath));
|
|
345
|
+
await _fsextra2.default.writeJson(this.queuePath, queue, { spaces: 2 });
|
|
346
|
+
} catch (e3) {
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Attempt to flush queued shares
|
|
351
|
+
*/
|
|
352
|
+
async flushQueue() {
|
|
353
|
+
let sent = 0;
|
|
354
|
+
let failed = 0;
|
|
355
|
+
try {
|
|
356
|
+
if (!await _fsextra2.default.pathExists(this.queuePath)) {
|
|
357
|
+
return { sent, failed };
|
|
358
|
+
}
|
|
359
|
+
const queue = await _fsextra2.default.readJson(this.queuePath);
|
|
360
|
+
const remaining = [];
|
|
361
|
+
for (const item of queue) {
|
|
362
|
+
if (item.attempts >= this.config.maxRetries) {
|
|
363
|
+
failed++;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
const response = await this.sendToApi("POST", "/share", item.payload);
|
|
368
|
+
if (response.success) {
|
|
369
|
+
sent++;
|
|
370
|
+
} else {
|
|
371
|
+
item.attempts++;
|
|
372
|
+
item.lastAttempt = (/* @__PURE__ */ new Date()).toISOString();
|
|
373
|
+
remaining.push(item);
|
|
374
|
+
failed++;
|
|
375
|
+
}
|
|
376
|
+
} catch (e4) {
|
|
377
|
+
item.attempts++;
|
|
378
|
+
item.lastAttempt = (/* @__PURE__ */ new Date()).toISOString();
|
|
379
|
+
remaining.push(item);
|
|
380
|
+
failed++;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (remaining.length > 0) {
|
|
384
|
+
await _fsextra2.default.writeJson(this.queuePath, remaining, { spaces: 2 });
|
|
385
|
+
} else {
|
|
386
|
+
await _fsextra2.default.remove(this.queuePath);
|
|
387
|
+
}
|
|
388
|
+
} catch (e5) {
|
|
389
|
+
}
|
|
390
|
+
return { sent, failed };
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Get queue status
|
|
394
|
+
*/
|
|
395
|
+
async getQueueStatus() {
|
|
396
|
+
try {
|
|
397
|
+
if (!await _fsextra2.default.pathExists(this.queuePath)) {
|
|
398
|
+
return { pending: 0, oldestItem: null };
|
|
399
|
+
}
|
|
400
|
+
const queue = await _fsextra2.default.readJson(this.queuePath);
|
|
401
|
+
return {
|
|
402
|
+
pending: queue.length,
|
|
403
|
+
oldestItem: _optionalChain([queue, 'access', _3 => _3[0], 'optionalAccess', _4 => _4.lastAttempt]) || null
|
|
404
|
+
};
|
|
405
|
+
} catch (e6) {
|
|
406
|
+
return { pending: 0, oldestItem: null };
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// ============ Health Check ============
|
|
410
|
+
/**
|
|
411
|
+
* Check if API is reachable
|
|
412
|
+
*/
|
|
413
|
+
async healthCheck() {
|
|
414
|
+
try {
|
|
415
|
+
const controller = new AbortController();
|
|
416
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
417
|
+
const response = await fetch(`${this.config.baseUrl}/health`, {
|
|
418
|
+
method: "GET",
|
|
419
|
+
signal: controller.signal
|
|
420
|
+
});
|
|
421
|
+
clearTimeout(timeoutId);
|
|
422
|
+
return response.ok;
|
|
423
|
+
} catch (e7) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
var instance = null;
|
|
429
|
+
var instanceBaseUrl = null;
|
|
430
|
+
function getShareApiClient(config) {
|
|
431
|
+
const currentBaseUrl = _optionalChain([config, 'optionalAccess', _5 => _5.baseUrl]) || getBaseUrl();
|
|
432
|
+
if (instance && instanceBaseUrl !== currentBaseUrl) {
|
|
433
|
+
instance = null;
|
|
434
|
+
}
|
|
435
|
+
if (!instance) {
|
|
436
|
+
instance = new ShareApiClient(config);
|
|
437
|
+
instanceBaseUrl = currentBaseUrl;
|
|
438
|
+
}
|
|
439
|
+
return instance;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/commands/share.ts
|
|
443
|
+
function terminalLink(url, text) {
|
|
444
|
+
const displayText = text || url;
|
|
445
|
+
const OSC = "\x1B]8;;";
|
|
446
|
+
const ST = "\x1B\\";
|
|
447
|
+
return `${OSC}${url}${ST}${_chalk2.default.cyan.underline(displayText)}${OSC}${ST}`;
|
|
448
|
+
}
|
|
449
|
+
var metadata = {
|
|
450
|
+
command: "/share",
|
|
451
|
+
description: "Share current session via public URL",
|
|
452
|
+
implemented: true
|
|
453
|
+
};
|
|
454
|
+
async function execute(_args, context) {
|
|
455
|
+
if (_optionalChain([context, 'optionalAccess', _6 => _6.config, 'optionalAccess', _7 => _7.share, 'optionalAccess', _8 => _8.enabled]) === false) {
|
|
456
|
+
console.log(_chalk2.default.yellow("Session sharing is disabled."));
|
|
457
|
+
console.log(
|
|
458
|
+
_chalk2.default.gray("To enable, set share.enabled: true in your config file.")
|
|
459
|
+
);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (!_optionalChain([context, 'optionalAccess', _9 => _9.currentSession])) {
|
|
463
|
+
console.log(_chalk2.default.yellow("No active session to share."));
|
|
464
|
+
console.log(
|
|
465
|
+
_chalk2.default.gray("Start a conversation first, then use /share to share it.")
|
|
466
|
+
);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const session = context.currentSession;
|
|
470
|
+
const messages = session.getMessages();
|
|
471
|
+
if (messages.length === 0) {
|
|
472
|
+
console.log(_chalk2.default.yellow("Session has no messages to share."));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const client = getShareApiClient();
|
|
476
|
+
const deviceId = await client.getDeviceId();
|
|
477
|
+
const totalTokens = _nullishCoalesce(_optionalChain([context, 'access', _10 => _10.getTotalTokensUsed, 'optionalCall', _11 => _11()]), () => ( 0));
|
|
478
|
+
const duration = calculateDuration2(session.metadata.createdAt);
|
|
479
|
+
console.log();
|
|
480
|
+
console.log(_chalk2.default.bold("Session Summary"));
|
|
481
|
+
console.log(_chalk2.default.gray("\u2500".repeat(40)));
|
|
482
|
+
console.log(` Project: ${_chalk2.default.cyan(session.metadata.projectName)}`);
|
|
483
|
+
console.log(` Model: ${_chalk2.default.cyan(context.model)}`);
|
|
484
|
+
console.log(` Messages: ${_chalk2.default.cyan(messages.length)}`);
|
|
485
|
+
console.log(` Tokens: ${_chalk2.default.cyan(formatTokens(totalTokens))}`);
|
|
486
|
+
console.log(
|
|
487
|
+
` Est. Cost: ${_chalk2.default.green(formatCost(totalTokens / 1e3 * 3e-3))}`
|
|
488
|
+
);
|
|
489
|
+
console.log(` Duration: ${_chalk2.default.cyan(formatDuration(duration))}`);
|
|
490
|
+
console.log();
|
|
491
|
+
const { Select, Confirm } = _enquirer2.default;
|
|
492
|
+
const visibilityPrompt = new Select({
|
|
493
|
+
name: "visibility",
|
|
494
|
+
message: "Share visibility:",
|
|
495
|
+
choices: [
|
|
496
|
+
{ name: "public", message: "Public - Anyone with the link can view" },
|
|
497
|
+
{ name: "private", message: "Private - Requires one-time passcode" }
|
|
498
|
+
]
|
|
499
|
+
});
|
|
500
|
+
let visibility;
|
|
501
|
+
try {
|
|
502
|
+
visibility = await visibilityPrompt.run();
|
|
503
|
+
} catch (e8) {
|
|
504
|
+
console.log(_chalk2.default.gray("Cancelled."));
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const confirmPrompt = new Confirm({
|
|
508
|
+
name: "confirm",
|
|
509
|
+
message: `Share this session as ${visibility}?`,
|
|
510
|
+
initial: true
|
|
511
|
+
});
|
|
512
|
+
let confirmed;
|
|
513
|
+
try {
|
|
514
|
+
confirmed = await confirmPrompt.run();
|
|
515
|
+
} catch (e9) {
|
|
516
|
+
console.log(_chalk2.default.gray("Cancelled."));
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (!confirmed) {
|
|
520
|
+
console.log(_chalk2.default.gray("Cancelled."));
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
console.log();
|
|
524
|
+
const spinner = _ora2.default.call(void 0, "Uploading session...").start();
|
|
525
|
+
try {
|
|
526
|
+
const payload = serializeSession(session, {
|
|
527
|
+
model: context.model,
|
|
528
|
+
provider: context.provider,
|
|
529
|
+
totalTokens,
|
|
530
|
+
visibility,
|
|
531
|
+
deviceId
|
|
532
|
+
});
|
|
533
|
+
const response = await client.createShare(payload);
|
|
534
|
+
spinner.stop();
|
|
535
|
+
if (response.success && response.url) {
|
|
536
|
+
console.log();
|
|
537
|
+
console.log(_chalk2.default.green.bold("Session shared successfully!"));
|
|
538
|
+
console.log();
|
|
539
|
+
console.log(`${_chalk2.default.bold("URL:")} ${terminalLink(response.url)}`);
|
|
540
|
+
if (visibility === "private" && response.passcode) {
|
|
541
|
+
console.log();
|
|
542
|
+
console.log(`${_chalk2.default.bold("Passcode:")} ${_chalk2.default.yellow.bold(response.passcode)}`);
|
|
543
|
+
console.log(
|
|
544
|
+
_chalk2.default.gray(" Share this passcode with people who need access.")
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
console.log();
|
|
548
|
+
console.log(_chalk2.default.gray("Tip: Click the URL or copy it to share with others!"));
|
|
549
|
+
console.log();
|
|
550
|
+
} else {
|
|
551
|
+
console.log();
|
|
552
|
+
console.log(
|
|
553
|
+
_chalk2.default.red(`Failed to share: ${response.error || "Unknown error"}`)
|
|
554
|
+
);
|
|
555
|
+
console.log();
|
|
556
|
+
}
|
|
557
|
+
} catch (error) {
|
|
558
|
+
spinner.stop();
|
|
559
|
+
console.log();
|
|
560
|
+
console.log(_chalk2.default.red(`Failed to share: ${error.message}`));
|
|
561
|
+
console.log();
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function calculateDuration2(startedAt) {
|
|
565
|
+
const start = new Date(startedAt).getTime();
|
|
566
|
+
const now = Date.now();
|
|
567
|
+
return Math.max(0, Math.floor((now - start) / 1e3));
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
exports.metadata = metadata; exports.execute = execute;
|
|
574
|
+
/**
|
|
575
|
+
* @license
|
|
576
|
+
* Copyright 2025 Autohand AI LLC
|
|
577
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
578
|
+
*
|
|
579
|
+
* Cost Estimator
|
|
580
|
+
* Simplified token cost estimation for session sharing
|
|
581
|
+
*/
|
|
582
|
+
/**
|
|
583
|
+
* @license
|
|
584
|
+
* Copyright 2025 Autohand AI LLC
|
|
585
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
586
|
+
*
|
|
587
|
+
* Session Serializer
|
|
588
|
+
* Converts session data to ShareSessionPayload format
|
|
589
|
+
*/
|
|
590
|
+
/**
|
|
591
|
+
* @license
|
|
592
|
+
* Copyright 2025 Autohand AI LLC
|
|
593
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
594
|
+
*
|
|
595
|
+
* Share API Client
|
|
596
|
+
* Handles communication with autohand.link for session sharing
|
|
597
|
+
*/
|
|
598
|
+
/**
|
|
599
|
+
* @license
|
|
600
|
+
* Copyright 2025 Autohand AI LLC
|
|
601
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
602
|
+
*
|
|
603
|
+
* Share Module
|
|
604
|
+
* Session sharing functionality
|
|
605
|
+
*/
|
|
606
|
+
/**
|
|
607
|
+
* @license
|
|
608
|
+
* Copyright 2025 Autohand AI LLC
|
|
609
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
610
|
+
*
|
|
611
|
+
* /share Command
|
|
612
|
+
* Share current session via public URL
|
|
613
|
+
*/
|