@trydecember/cli 0.4.4 → 0.4.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/README.md +1 -1
- package/dist/december.js +1969 -289
- package/package.json +2 -2
package/dist/december.js
CHANGED
|
@@ -65,8 +65,8 @@ var package_default;
|
|
|
65
65
|
var init_package = __esm(() => {
|
|
66
66
|
package_default = {
|
|
67
67
|
name: "@trydecember/cli",
|
|
68
|
-
version: "0.4.
|
|
69
|
-
description: "a coding agent for terminal and cloud.",
|
|
68
|
+
version: "0.4.6",
|
|
69
|
+
description: "a (so much) better coding agent for terminal and cloud.",
|
|
70
70
|
homepage: "https://trydecember.com",
|
|
71
71
|
repository: {
|
|
72
72
|
type: "git",
|
|
@@ -497,6 +497,7 @@ function supportsModelThinking(model) {
|
|
|
497
497
|
function createProvider(config, streamImpl) {
|
|
498
498
|
return {
|
|
499
499
|
id: config.id,
|
|
500
|
+
api: config.api,
|
|
500
501
|
stream: streamImpl
|
|
501
502
|
};
|
|
502
503
|
}
|
|
@@ -66427,13 +66428,22 @@ function resolveAnthropicModel(model) {
|
|
|
66427
66428
|
function anthropicProvider(baseURL, apiKey, customClientOrDefaultHeaders, customClient) {
|
|
66428
66429
|
const isCustomClient = customClientOrDefaultHeaders instanceof Anthropic || Boolean(customClientOrDefaultHeaders && typeof customClientOrDefaultHeaders.messages?.create === "function");
|
|
66429
66430
|
const defaultHeaders = isCustomClient ? undefined : customClientOrDefaultHeaders;
|
|
66430
|
-
|
|
66431
|
+
let resolvedBaseURL = baseURL;
|
|
66432
|
+
let resolvedApiKey = apiKey;
|
|
66433
|
+
if (baseURL && !baseURL.startsWith("http") && !baseURL.startsWith("/") && apiKey === undefined) {
|
|
66434
|
+
resolvedApiKey = baseURL;
|
|
66435
|
+
resolvedBaseURL = undefined;
|
|
66436
|
+
}
|
|
66437
|
+
const isExplicitOAuth = Boolean(defaultHeaders && (defaultHeaders["anthropic-beta"]?.includes("oauth") || defaultHeaders["anthropic-beta"]?.includes("claude-code")));
|
|
66438
|
+
const isTokenOAuth = Boolean(resolvedApiKey && (resolvedApiKey.startsWith("sk-ant-oat") || resolvedApiKey.startsWith("Bearer ")));
|
|
66439
|
+
const isEnvOAuth = Boolean(!resolvedApiKey && !process.env.ANTHROPIC_API_KEY && (process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_CODE_OAUTH_TOKEN));
|
|
66440
|
+
const isOAuth = isExplicitOAuth || isTokenOAuth || isEnvOAuth;
|
|
66431
66441
|
const client = (isCustomClient ? customClientOrDefaultHeaders : customClient) || new Anthropic({
|
|
66432
|
-
baseURL,
|
|
66433
|
-
apiKey: isOAuth ? undefined :
|
|
66434
|
-
authToken: isOAuth ?
|
|
66442
|
+
baseURL: resolvedBaseURL,
|
|
66443
|
+
apiKey: isOAuth ? undefined : resolvedApiKey || process.env.ANTHROPIC_API_KEY,
|
|
66444
|
+
authToken: isOAuth ? resolvedApiKey || process.env.ANTHROPIC_AUTH_TOKEN || process.env.CLAUDE_CODE_OAUTH_TOKEN : undefined,
|
|
66435
66445
|
defaultHeaders: {
|
|
66436
|
-
...isOAuth ? { "anthropic-beta": "oauth-2024-
|
|
66446
|
+
...isOAuth ? { "anthropic-beta": "claude-code-20250219,oauth-2024-06-20" } : {},
|
|
66437
66447
|
...defaultHeaders || {}
|
|
66438
66448
|
}
|
|
66439
66449
|
});
|
|
@@ -84297,7 +84307,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84297
84307
|
var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
|
|
84298
84308
|
var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
84299
84309
|
|
|
84300
|
-
class
|
|
84310
|
+
class WebSocket2 extends EventEmitter {
|
|
84301
84311
|
constructor(address, protocols, options) {
|
|
84302
84312
|
super();
|
|
84303
84313
|
this._binaryType = BINARY_TYPES[0];
|
|
@@ -84310,7 +84320,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84310
84320
|
this._extensions = {};
|
|
84311
84321
|
this._paused = false;
|
|
84312
84322
|
this._protocol = "";
|
|
84313
|
-
this._readyState =
|
|
84323
|
+
this._readyState = WebSocket2.CONNECTING;
|
|
84314
84324
|
this._receiver = null;
|
|
84315
84325
|
this._sender = null;
|
|
84316
84326
|
this._socket = null;
|
|
@@ -84412,12 +84422,12 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84412
84422
|
socket.on("data", socketOnData);
|
|
84413
84423
|
socket.on("end", socketOnEnd);
|
|
84414
84424
|
socket.on("error", socketOnError);
|
|
84415
|
-
this._readyState =
|
|
84425
|
+
this._readyState = WebSocket2.OPEN;
|
|
84416
84426
|
this.emit("open");
|
|
84417
84427
|
}
|
|
84418
84428
|
emitClose() {
|
|
84419
84429
|
if (!this._socket) {
|
|
84420
|
-
this._readyState =
|
|
84430
|
+
this._readyState = WebSocket2.CLOSED;
|
|
84421
84431
|
this.emit("close", this._closeCode, this._closeMessage);
|
|
84422
84432
|
return;
|
|
84423
84433
|
}
|
|
@@ -84425,24 +84435,24 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84425
84435
|
this._extensions[PerMessageDeflate.extensionName].cleanup();
|
|
84426
84436
|
}
|
|
84427
84437
|
this._receiver.removeAllListeners();
|
|
84428
|
-
this._readyState =
|
|
84438
|
+
this._readyState = WebSocket2.CLOSED;
|
|
84429
84439
|
this.emit("close", this._closeCode, this._closeMessage);
|
|
84430
84440
|
}
|
|
84431
84441
|
close(code, data) {
|
|
84432
|
-
if (this.readyState ===
|
|
84442
|
+
if (this.readyState === WebSocket2.CLOSED)
|
|
84433
84443
|
return;
|
|
84434
|
-
if (this.readyState ===
|
|
84444
|
+
if (this.readyState === WebSocket2.CONNECTING) {
|
|
84435
84445
|
const msg = "WebSocket was closed before the connection was established";
|
|
84436
84446
|
abortHandshake(this, this._req, msg);
|
|
84437
84447
|
return;
|
|
84438
84448
|
}
|
|
84439
|
-
if (this.readyState ===
|
|
84449
|
+
if (this.readyState === WebSocket2.CLOSING) {
|
|
84440
84450
|
if (this._closeFrameSent && (this._closeFrameReceived || this._receiver._writableState.errorEmitted)) {
|
|
84441
84451
|
this._socket.end();
|
|
84442
84452
|
}
|
|
84443
84453
|
return;
|
|
84444
84454
|
}
|
|
84445
|
-
this._readyState =
|
|
84455
|
+
this._readyState = WebSocket2.CLOSING;
|
|
84446
84456
|
this._sender.close(code, data, !this._isServer, (err) => {
|
|
84447
84457
|
if (err)
|
|
84448
84458
|
return;
|
|
@@ -84454,14 +84464,14 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84454
84464
|
setCloseTimer(this);
|
|
84455
84465
|
}
|
|
84456
84466
|
pause() {
|
|
84457
|
-
if (this.readyState ===
|
|
84467
|
+
if (this.readyState === WebSocket2.CONNECTING || this.readyState === WebSocket2.CLOSED) {
|
|
84458
84468
|
return;
|
|
84459
84469
|
}
|
|
84460
84470
|
this._paused = true;
|
|
84461
84471
|
this._socket.pause();
|
|
84462
84472
|
}
|
|
84463
84473
|
ping(data, mask, cb) {
|
|
84464
|
-
if (this.readyState ===
|
|
84474
|
+
if (this.readyState === WebSocket2.CONNECTING) {
|
|
84465
84475
|
throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
|
|
84466
84476
|
}
|
|
84467
84477
|
if (typeof data === "function") {
|
|
@@ -84473,7 +84483,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84473
84483
|
}
|
|
84474
84484
|
if (typeof data === "number")
|
|
84475
84485
|
data = data.toString();
|
|
84476
|
-
if (this.readyState !==
|
|
84486
|
+
if (this.readyState !== WebSocket2.OPEN) {
|
|
84477
84487
|
sendAfterClose(this, data, cb);
|
|
84478
84488
|
return;
|
|
84479
84489
|
}
|
|
@@ -84482,7 +84492,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84482
84492
|
this._sender.ping(data || EMPTY_BUFFER, mask, cb);
|
|
84483
84493
|
}
|
|
84484
84494
|
pong(data, mask, cb) {
|
|
84485
|
-
if (this.readyState ===
|
|
84495
|
+
if (this.readyState === WebSocket2.CONNECTING) {
|
|
84486
84496
|
throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
|
|
84487
84497
|
}
|
|
84488
84498
|
if (typeof data === "function") {
|
|
@@ -84494,7 +84504,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84494
84504
|
}
|
|
84495
84505
|
if (typeof data === "number")
|
|
84496
84506
|
data = data.toString();
|
|
84497
|
-
if (this.readyState !==
|
|
84507
|
+
if (this.readyState !== WebSocket2.OPEN) {
|
|
84498
84508
|
sendAfterClose(this, data, cb);
|
|
84499
84509
|
return;
|
|
84500
84510
|
}
|
|
@@ -84503,7 +84513,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84503
84513
|
this._sender.pong(data || EMPTY_BUFFER, mask, cb);
|
|
84504
84514
|
}
|
|
84505
84515
|
resume() {
|
|
84506
|
-
if (this.readyState ===
|
|
84516
|
+
if (this.readyState === WebSocket2.CONNECTING || this.readyState === WebSocket2.CLOSED) {
|
|
84507
84517
|
return;
|
|
84508
84518
|
}
|
|
84509
84519
|
this._paused = false;
|
|
@@ -84511,7 +84521,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84511
84521
|
this._socket.resume();
|
|
84512
84522
|
}
|
|
84513
84523
|
send(data, options, cb) {
|
|
84514
|
-
if (this.readyState ===
|
|
84524
|
+
if (this.readyState === WebSocket2.CONNECTING) {
|
|
84515
84525
|
throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
|
|
84516
84526
|
}
|
|
84517
84527
|
if (typeof options === "function") {
|
|
@@ -84520,7 +84530,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84520
84530
|
}
|
|
84521
84531
|
if (typeof data === "number")
|
|
84522
84532
|
data = data.toString();
|
|
84523
|
-
if (this.readyState !==
|
|
84533
|
+
if (this.readyState !== WebSocket2.OPEN) {
|
|
84524
84534
|
sendAfterClose(this, data, cb);
|
|
84525
84535
|
return;
|
|
84526
84536
|
}
|
|
@@ -84537,48 +84547,48 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84537
84547
|
this._sender.send(data || EMPTY_BUFFER, opts, cb);
|
|
84538
84548
|
}
|
|
84539
84549
|
terminate() {
|
|
84540
|
-
if (this.readyState ===
|
|
84550
|
+
if (this.readyState === WebSocket2.CLOSED)
|
|
84541
84551
|
return;
|
|
84542
|
-
if (this.readyState ===
|
|
84552
|
+
if (this.readyState === WebSocket2.CONNECTING) {
|
|
84543
84553
|
const msg = "WebSocket was closed before the connection was established";
|
|
84544
84554
|
abortHandshake(this, this._req, msg);
|
|
84545
84555
|
return;
|
|
84546
84556
|
}
|
|
84547
84557
|
if (this._socket) {
|
|
84548
|
-
this._readyState =
|
|
84558
|
+
this._readyState = WebSocket2.CLOSING;
|
|
84549
84559
|
this._socket.destroy();
|
|
84550
84560
|
}
|
|
84551
84561
|
}
|
|
84552
84562
|
}
|
|
84553
|
-
Object.defineProperty(
|
|
84563
|
+
Object.defineProperty(WebSocket2, "CONNECTING", {
|
|
84554
84564
|
enumerable: true,
|
|
84555
84565
|
value: readyStates.indexOf("CONNECTING")
|
|
84556
84566
|
});
|
|
84557
|
-
Object.defineProperty(
|
|
84567
|
+
Object.defineProperty(WebSocket2.prototype, "CONNECTING", {
|
|
84558
84568
|
enumerable: true,
|
|
84559
84569
|
value: readyStates.indexOf("CONNECTING")
|
|
84560
84570
|
});
|
|
84561
|
-
Object.defineProperty(
|
|
84571
|
+
Object.defineProperty(WebSocket2, "OPEN", {
|
|
84562
84572
|
enumerable: true,
|
|
84563
84573
|
value: readyStates.indexOf("OPEN")
|
|
84564
84574
|
});
|
|
84565
|
-
Object.defineProperty(
|
|
84575
|
+
Object.defineProperty(WebSocket2.prototype, "OPEN", {
|
|
84566
84576
|
enumerable: true,
|
|
84567
84577
|
value: readyStates.indexOf("OPEN")
|
|
84568
84578
|
});
|
|
84569
|
-
Object.defineProperty(
|
|
84579
|
+
Object.defineProperty(WebSocket2, "CLOSING", {
|
|
84570
84580
|
enumerable: true,
|
|
84571
84581
|
value: readyStates.indexOf("CLOSING")
|
|
84572
84582
|
});
|
|
84573
|
-
Object.defineProperty(
|
|
84583
|
+
Object.defineProperty(WebSocket2.prototype, "CLOSING", {
|
|
84574
84584
|
enumerable: true,
|
|
84575
84585
|
value: readyStates.indexOf("CLOSING")
|
|
84576
84586
|
});
|
|
84577
|
-
Object.defineProperty(
|
|
84587
|
+
Object.defineProperty(WebSocket2, "CLOSED", {
|
|
84578
84588
|
enumerable: true,
|
|
84579
84589
|
value: readyStates.indexOf("CLOSED")
|
|
84580
84590
|
});
|
|
84581
|
-
Object.defineProperty(
|
|
84591
|
+
Object.defineProperty(WebSocket2.prototype, "CLOSED", {
|
|
84582
84592
|
enumerable: true,
|
|
84583
84593
|
value: readyStates.indexOf("CLOSED")
|
|
84584
84594
|
});
|
|
@@ -84591,10 +84601,10 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84591
84601
|
"readyState",
|
|
84592
84602
|
"url"
|
|
84593
84603
|
].forEach((property) => {
|
|
84594
|
-
Object.defineProperty(
|
|
84604
|
+
Object.defineProperty(WebSocket2.prototype, property, { enumerable: true });
|
|
84595
84605
|
});
|
|
84596
84606
|
["open", "error", "close", "message"].forEach((method) => {
|
|
84597
|
-
Object.defineProperty(
|
|
84607
|
+
Object.defineProperty(WebSocket2.prototype, `on${method}`, {
|
|
84598
84608
|
enumerable: true,
|
|
84599
84609
|
get() {
|
|
84600
84610
|
for (const listener of this.listeners(method)) {
|
|
@@ -84618,9 +84628,9 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84618
84628
|
}
|
|
84619
84629
|
});
|
|
84620
84630
|
});
|
|
84621
|
-
|
|
84622
|
-
|
|
84623
|
-
module.exports =
|
|
84631
|
+
WebSocket2.prototype.addEventListener = addEventListener;
|
|
84632
|
+
WebSocket2.prototype.removeEventListener = removeEventListener;
|
|
84633
|
+
module.exports = WebSocket2;
|
|
84624
84634
|
function initAsClient(websocket, address, protocols, options) {
|
|
84625
84635
|
const opts = {
|
|
84626
84636
|
allowSynchronousEvents: true,
|
|
@@ -84804,7 +84814,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84804
84814
|
});
|
|
84805
84815
|
req.on("upgrade", (res, socket, head) => {
|
|
84806
84816
|
websocket.emit("upgrade", res);
|
|
84807
|
-
if (websocket.readyState !==
|
|
84817
|
+
if (websocket.readyState !== WebSocket2.CONNECTING)
|
|
84808
84818
|
return;
|
|
84809
84819
|
req = websocket._req = null;
|
|
84810
84820
|
const upgrade = res.headers.upgrade;
|
|
@@ -84880,7 +84890,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84880
84890
|
}
|
|
84881
84891
|
}
|
|
84882
84892
|
function emitErrorAndClose(websocket, err) {
|
|
84883
|
-
websocket._readyState =
|
|
84893
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
84884
84894
|
websocket._errorEmitted = true;
|
|
84885
84895
|
websocket.emit("error", err);
|
|
84886
84896
|
websocket.emitClose();
|
|
@@ -84897,7 +84907,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84897
84907
|
return tls.connect(options);
|
|
84898
84908
|
}
|
|
84899
84909
|
function abortHandshake(websocket, stream, message) {
|
|
84900
|
-
websocket._readyState =
|
|
84910
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
84901
84911
|
const err = new Error(message);
|
|
84902
84912
|
Error.captureStackTrace(err, abortHandshake);
|
|
84903
84913
|
if (stream.setHeader) {
|
|
@@ -84977,10 +84987,10 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84977
84987
|
}
|
|
84978
84988
|
function senderOnError(err) {
|
|
84979
84989
|
const websocket = this[kWebSocket];
|
|
84980
|
-
if (websocket.readyState ===
|
|
84990
|
+
if (websocket.readyState === WebSocket2.CLOSED)
|
|
84981
84991
|
return;
|
|
84982
|
-
if (websocket.readyState ===
|
|
84983
|
-
websocket._readyState =
|
|
84992
|
+
if (websocket.readyState === WebSocket2.OPEN) {
|
|
84993
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
84984
84994
|
setCloseTimer(websocket);
|
|
84985
84995
|
}
|
|
84986
84996
|
this._socket.end();
|
|
@@ -84997,7 +85007,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
84997
85007
|
this.removeListener("close", socketOnClose);
|
|
84998
85008
|
this.removeListener("data", socketOnData);
|
|
84999
85009
|
this.removeListener("end", socketOnEnd);
|
|
85000
|
-
websocket._readyState =
|
|
85010
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
85001
85011
|
if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && this._readableState.length !== 0) {
|
|
85002
85012
|
const chunk = this.read(this._readableState.length);
|
|
85003
85013
|
websocket._receiver.write(chunk);
|
|
@@ -85019,7 +85029,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
85019
85029
|
}
|
|
85020
85030
|
function socketOnEnd() {
|
|
85021
85031
|
const websocket = this[kWebSocket];
|
|
85022
|
-
websocket._readyState =
|
|
85032
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
85023
85033
|
websocket._receiver.end();
|
|
85024
85034
|
this.end();
|
|
85025
85035
|
}
|
|
@@ -85028,7 +85038,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
85028
85038
|
this.removeListener("error", socketOnError);
|
|
85029
85039
|
this.on("error", NOOP);
|
|
85030
85040
|
if (websocket) {
|
|
85031
|
-
websocket._readyState =
|
|
85041
|
+
websocket._readyState = WebSocket2.CLOSING;
|
|
85032
85042
|
this.destroy();
|
|
85033
85043
|
}
|
|
85034
85044
|
}
|
|
@@ -85036,7 +85046,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
85036
85046
|
|
|
85037
85047
|
// ../../node_modules/.bun/ws@8.21.0/node_modules/ws/lib/stream.js
|
|
85038
85048
|
var require_stream = __commonJS(function(exports, module) {
|
|
85039
|
-
var
|
|
85049
|
+
var WebSocket2 = require_websocket();
|
|
85040
85050
|
var { Duplex } = __require("stream");
|
|
85041
85051
|
function emitClose(stream) {
|
|
85042
85052
|
stream.emit("close");
|
|
@@ -85191,7 +85201,7 @@ var require_websocket_server = __commonJS(function(exports, module) {
|
|
|
85191
85201
|
var extension = require_extension();
|
|
85192
85202
|
var PerMessageDeflate = require_permessage_deflate();
|
|
85193
85203
|
var subprotocol = require_subprotocol();
|
|
85194
|
-
var
|
|
85204
|
+
var WebSocket2 = require_websocket();
|
|
85195
85205
|
var { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants2();
|
|
85196
85206
|
var keyRegex = /^[+/0-9A-Za-z]{22}==$/;
|
|
85197
85207
|
var RUNNING = 0;
|
|
@@ -85219,7 +85229,7 @@ var require_websocket_server = __commonJS(function(exports, module) {
|
|
|
85219
85229
|
host: null,
|
|
85220
85230
|
path: null,
|
|
85221
85231
|
port: null,
|
|
85222
|
-
WebSocket,
|
|
85232
|
+
WebSocket: WebSocket2,
|
|
85223
85233
|
...options
|
|
85224
85234
|
};
|
|
85225
85235
|
if (options.port == null && !options.server && !options.noServer || options.port != null && (options.server || options.noServer) || options.server && options.noServer) {
|
|
@@ -108645,10 +108655,10 @@ async function fetchLiveProviderModels(provider, apiKey, baseUrl, fetchFn = fetc
|
|
|
108645
108655
|
} else if (normalized === "anthropic" || normalized === "claude") {
|
|
108646
108656
|
endpoint = "https://api.anthropic.com/v1/models";
|
|
108647
108657
|
headers["anthropic-version"] = "2023-06-01";
|
|
108648
|
-
const isOAuth = normalized === "claude" ||
|
|
108649
|
-
if (isOAuth
|
|
108658
|
+
const isOAuth = normalized === "claude" || apiKey.startsWith("sk-ant-oat") || apiKey.startsWith("Bearer ");
|
|
108659
|
+
if (isOAuth) {
|
|
108650
108660
|
headers["Authorization"] = apiKey.startsWith("Bearer ") ? apiKey : `Bearer ${apiKey}`;
|
|
108651
|
-
headers["anthropic-beta"] = "oauth-2024-
|
|
108661
|
+
headers["anthropic-beta"] = "claude-code-20250219,oauth-2024-06-20";
|
|
108652
108662
|
} else {
|
|
108653
108663
|
headers["x-api-key"] = apiKey;
|
|
108654
108664
|
}
|
|
@@ -114423,7 +114433,7 @@ function inferManagerFromPath(filePath, realPath) {
|
|
|
114423
114433
|
return "npm";
|
|
114424
114434
|
}
|
|
114425
114435
|
if (normalized.includes("/.december/bin/december") || normalized.includes("/.december/bin") || normalized.includes("/.local/bin/december") || normalized.includes("/.local/bin/")) {
|
|
114426
|
-
return "
|
|
114436
|
+
return "curl";
|
|
114427
114437
|
}
|
|
114428
114438
|
return "unknown";
|
|
114429
114439
|
}
|
|
@@ -114771,7 +114781,7 @@ function isPermissionDeniedError2(errMsg) {
|
|
|
114771
114781
|
}
|
|
114772
114782
|
function detectInstallMethod(options) {
|
|
114773
114783
|
const configMethod = options?.configInstallMethod;
|
|
114774
|
-
const validMethods = ["npm", "bun", "pnpm", "npx", "source"];
|
|
114784
|
+
const validMethods = ["npm", "bun", "pnpm", "npx", "curl", "source"];
|
|
114775
114785
|
if (configMethod && validMethods.includes(configMethod)) {
|
|
114776
114786
|
return configMethod;
|
|
114777
114787
|
}
|
|
@@ -114793,6 +114803,9 @@ function detectInstallMethod(options) {
|
|
|
114793
114803
|
if (combined.includes("/_npx/") || combined.includes("\\_npx\\") || combined.includes("/.bunx/") || env.npm_config_user_agent?.includes("npx")) {
|
|
114794
114804
|
return "npx";
|
|
114795
114805
|
}
|
|
114806
|
+
if (execPath.includes(".december/bin") || combined.includes(".december/bin") || execPath.includes(".local/bin/december") || combined.includes(".local/bin/december") || execPath.endsWith("/december") && !execPath.includes("node_modules") && !execPath.includes(".bun") && !execPath.includes("/node")) {
|
|
114807
|
+
return "curl";
|
|
114808
|
+
}
|
|
114796
114809
|
if (combined.includes(".bun/bin") || combined.includes("/.bun/") || execPath.includes("/bun") && !execPath.endsWith("/node")) {
|
|
114797
114810
|
return "bun";
|
|
114798
114811
|
}
|
|
@@ -114803,6 +114816,13 @@ function detectInstallMethod(options) {
|
|
|
114803
114816
|
}
|
|
114804
114817
|
function getUpdateCommand(method, _platform = process.platform) {
|
|
114805
114818
|
switch (method) {
|
|
114819
|
+
case "curl": {
|
|
114820
|
+
return {
|
|
114821
|
+
command: "curl -fsSL https://trydecember.com/install.sh | bash",
|
|
114822
|
+
manualCmd: "curl -fsSL https://trydecember.com/install.sh | bash",
|
|
114823
|
+
description: "Standalone binary via curl installer"
|
|
114824
|
+
};
|
|
114825
|
+
}
|
|
114806
114826
|
case "bun": {
|
|
114807
114827
|
return {
|
|
114808
114828
|
command: "bun add -g @trydecember/cli@latest",
|
|
@@ -114864,8 +114884,8 @@ async function performCliUpdate(options) {
|
|
|
114864
114884
|
});
|
|
114865
114885
|
if (!options?.configInstallMethod && preDiagnosis?.activeBinary?.manager) {
|
|
114866
114886
|
const activeMgr = preDiagnosis.activeBinary.manager;
|
|
114867
|
-
if (activeMgr === "bun" || activeMgr === "npm" || activeMgr === "pnpm" || activeMgr === "source") {
|
|
114868
|
-
detectedMethod = activeMgr;
|
|
114887
|
+
if (activeMgr === "bun" || activeMgr === "npm" || activeMgr === "pnpm" || activeMgr === "curl" || activeMgr === "standalone" || activeMgr === "source") {
|
|
114888
|
+
detectedMethod = activeMgr === "standalone" ? "curl" : activeMgr;
|
|
114869
114889
|
}
|
|
114870
114890
|
}
|
|
114871
114891
|
const method = detectedMethod;
|
|
@@ -115360,20 +115380,28 @@ Examples:`);
|
|
|
115360
115380
|
console.log("No API key provided. Operation cancelled.");
|
|
115361
115381
|
return;
|
|
115362
115382
|
}
|
|
115363
|
-
|
|
115364
|
-
const defaultModel = getDefaultModelForProvider(provider);
|
|
115383
|
+
const isSTTProvider = provider.toLowerCase() === "deepgram";
|
|
115365
115384
|
const config = await loadConfig();
|
|
115366
115385
|
config.providers = config.providers || {};
|
|
115367
115386
|
config.providers[provider] = finalKey;
|
|
115368
|
-
|
|
115369
|
-
|
|
115370
|
-
|
|
115371
|
-
|
|
115372
|
-
|
|
115373
|
-
|
|
115374
|
-
|
|
115387
|
+
if (!isSTTProvider) {
|
|
115388
|
+
await Promise.resolve().then(() => init_models5());
|
|
115389
|
+
const defaultModel = getDefaultModelForProvider(provider);
|
|
115390
|
+
config.activeProvider = provider;
|
|
115391
|
+
config.activeModel = defaultModel;
|
|
115392
|
+
config.authPriority = "byok";
|
|
115393
|
+
await saveConfig(config);
|
|
115394
|
+
await Promise.resolve().then(() => init_models5());
|
|
115395
|
+
fetchLiveProviderModels(provider, finalKey).catch(() => {});
|
|
115396
|
+
console.log(`
|
|
115375
115397
|
${GREEN}Successfully saved API key for ${provider.toUpperCase()} (Model: ${defaultModel})!${RESET}
|
|
115376
115398
|
`);
|
|
115399
|
+
} else {
|
|
115400
|
+
await saveConfig(config);
|
|
115401
|
+
console.log(`
|
|
115402
|
+
${GREEN}Successfully saved API key for DEEPGRAM (Live Voice Dictation)!${RESET}
|
|
115403
|
+
`);
|
|
115404
|
+
}
|
|
115377
115405
|
}
|
|
115378
115406
|
async function handleLoginCommand(options) {
|
|
115379
115407
|
const targetProvider = (options?.provider || "december").toLowerCase().trim();
|
|
@@ -119419,7 +119447,7 @@ var init_browser = __esm(() => {
|
|
|
119419
119447
|
});
|
|
119420
119448
|
BrowserTool = {
|
|
119421
119449
|
name: "browser",
|
|
119422
|
-
description: "Use
|
|
119450
|
+
description: "Use an interactive browser engine to navigate to a URL, execute client-side JavaScript, extract rendered page content, and capture console or network errors.",
|
|
119423
119451
|
inputSchema: browserSchema,
|
|
119424
119452
|
execute: async ({ url }, context) => {
|
|
119425
119453
|
try {
|
|
@@ -119431,6 +119459,20 @@ var init_browser = __esm(() => {
|
|
|
119431
119459
|
return `Failed to fetch URL: ${result.error}`;
|
|
119432
119460
|
}
|
|
119433
119461
|
let output = truncateOutput(result.text, 25000, 100).text;
|
|
119462
|
+
if (result.consoleErrors && result.consoleErrors.length > 0) {
|
|
119463
|
+
output += `
|
|
119464
|
+
|
|
119465
|
+
[CONSOLE ERRORS]
|
|
119466
|
+
` + result.consoleErrors.map((err) => `- ${err}`).join(`
|
|
119467
|
+
`);
|
|
119468
|
+
}
|
|
119469
|
+
if (result.networkErrors && result.networkErrors.length > 0) {
|
|
119470
|
+
output += `
|
|
119471
|
+
|
|
119472
|
+
[FAILED NETWORK REQUESTS]
|
|
119473
|
+
` + result.networkErrors.map((err) => `- ${err}`).join(`
|
|
119474
|
+
`);
|
|
119475
|
+
}
|
|
119434
119476
|
if (result.vncUrl) {
|
|
119435
119477
|
output += `
|
|
119436
119478
|
|
|
@@ -119444,6 +119486,89 @@ var init_browser = __esm(() => {
|
|
|
119444
119486
|
};
|
|
119445
119487
|
});
|
|
119446
119488
|
|
|
119489
|
+
// ../../packages/tools/src/browser_action.ts
|
|
119490
|
+
var browserActionSchema, BrowserActionTool;
|
|
119491
|
+
var init_browser_action = __esm(() => {
|
|
119492
|
+
init_src();
|
|
119493
|
+
init_esm2();
|
|
119494
|
+
browserActionSchema = Type2.Object({
|
|
119495
|
+
action: Type2.Union([
|
|
119496
|
+
Type2.Literal("navigate"),
|
|
119497
|
+
Type2.Literal("click"),
|
|
119498
|
+
Type2.Literal("type"),
|
|
119499
|
+
Type2.Literal("screenshot"),
|
|
119500
|
+
Type2.Literal("get_console_errors")
|
|
119501
|
+
], { description: "The interactive browser action to execute" }),
|
|
119502
|
+
url: Type2.Optional(Type2.String({ description: 'URL to navigate to (required for action: "navigate")' })),
|
|
119503
|
+
selector: Type2.Optional(Type2.String({ description: 'CSS selector or text target (for action: "click" or "type")' })),
|
|
119504
|
+
text: Type2.Optional(Type2.String({ description: 'Text to input (required for action: "type")' })),
|
|
119505
|
+
fullPage: Type2.Optional(Type2.Boolean({ description: 'Capture full scrollable page (for action: "screenshot")' }))
|
|
119506
|
+
});
|
|
119507
|
+
BrowserActionTool = {
|
|
119508
|
+
name: "browser_action",
|
|
119509
|
+
description: "Interact with a headless browser session. Supports navigating to URLs, clicking elements, typing into inputs, capturing screenshots, and inspecting console or network errors on localhost web apps.",
|
|
119510
|
+
inputSchema: browserActionSchema,
|
|
119511
|
+
execute: async (input, context) => {
|
|
119512
|
+
try {
|
|
119513
|
+
if (!context.operations.browser) {
|
|
119514
|
+
return `Failed to execute browser action: Browser operations are not supported in this environment.`;
|
|
119515
|
+
}
|
|
119516
|
+
if (!context.operations.browser.action) {
|
|
119517
|
+
if (input.action === "navigate" && input.url) {
|
|
119518
|
+
const navResult = await context.operations.browser.navigate(input.url);
|
|
119519
|
+
if (navResult.error) {
|
|
119520
|
+
return `Failed to navigate: ${navResult.error}`;
|
|
119521
|
+
}
|
|
119522
|
+
let out = truncateOutput(navResult.text, 25000, 100).text;
|
|
119523
|
+
if (navResult.consoleErrors && navResult.consoleErrors.length > 0) {
|
|
119524
|
+
out += `
|
|
119525
|
+
|
|
119526
|
+
[CONSOLE ERRORS]
|
|
119527
|
+
` + navResult.consoleErrors.map((err) => `- ${err}`).join(`
|
|
119528
|
+
`);
|
|
119529
|
+
}
|
|
119530
|
+
if (navResult.networkErrors && navResult.networkErrors.length > 0) {
|
|
119531
|
+
out += `
|
|
119532
|
+
|
|
119533
|
+
[FAILED NETWORK REQUESTS]
|
|
119534
|
+
` + navResult.networkErrors.map((err) => `- ${err}`).join(`
|
|
119535
|
+
`);
|
|
119536
|
+
}
|
|
119537
|
+
return out;
|
|
119538
|
+
}
|
|
119539
|
+
return `Browser interactive actions ('click', 'type', 'screenshot') require Chrome or Chromium to be installed.`;
|
|
119540
|
+
}
|
|
119541
|
+
const result = await context.operations.browser.action(input);
|
|
119542
|
+
if (result.error) {
|
|
119543
|
+
return `Failed to execute action "${input.action}": ${result.error}`;
|
|
119544
|
+
}
|
|
119545
|
+
let response = result.output || result.text || `Browser action "${input.action}" completed successfully.`;
|
|
119546
|
+
if (result.screenshotPath) {
|
|
119547
|
+
response += `
|
|
119548
|
+
[Screenshot saved: ${result.screenshotPath}]`;
|
|
119549
|
+
}
|
|
119550
|
+
if (result.consoleErrors && result.consoleErrors.length > 0) {
|
|
119551
|
+
response += `
|
|
119552
|
+
|
|
119553
|
+
[CONSOLE ERRORS]
|
|
119554
|
+
` + result.consoleErrors.map((err) => `- ${err}`).join(`
|
|
119555
|
+
`);
|
|
119556
|
+
}
|
|
119557
|
+
if (result.networkErrors && result.networkErrors.length > 0) {
|
|
119558
|
+
response += `
|
|
119559
|
+
|
|
119560
|
+
[FAILED NETWORK REQUESTS]
|
|
119561
|
+
` + result.networkErrors.map((err) => `- ${err}`).join(`
|
|
119562
|
+
`);
|
|
119563
|
+
}
|
|
119564
|
+
return response;
|
|
119565
|
+
} catch (error) {
|
|
119566
|
+
return `Failed to execute browser action: ${error.message}`;
|
|
119567
|
+
}
|
|
119568
|
+
}
|
|
119569
|
+
};
|
|
119570
|
+
});
|
|
119571
|
+
|
|
119447
119572
|
// ../../packages/tools/src/code_intelligence.ts
|
|
119448
119573
|
function formatDiagnostics(diags, path) {
|
|
119449
119574
|
const target = path ? ` for ${path}` : "";
|
|
@@ -160892,7 +161017,7 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
160892
161017
|
return null;
|
|
160893
161018
|
}
|
|
160894
161019
|
|
|
160895
|
-
class
|
|
161020
|
+
class WebSocket2 extends EventTarget {
|
|
160896
161021
|
#events = {
|
|
160897
161022
|
open: null,
|
|
160898
161023
|
error: null,
|
|
@@ -160968,11 +161093,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
160968
161093
|
this.#url = new URL(urlRecord.href);
|
|
160969
161094
|
const client = environmentSettingsObject.settingsObject;
|
|
160970
161095
|
this.#handler.controller = establishWebSocketConnection(urlRecord, protocols, client, this.#handler, options);
|
|
160971
|
-
this.#handler.readyState =
|
|
161096
|
+
this.#handler.readyState = WebSocket2.CONNECTING;
|
|
160972
161097
|
this.#binaryType = "blob";
|
|
160973
161098
|
}
|
|
160974
161099
|
close(code = undefined, reason = undefined) {
|
|
160975
|
-
webidl.brandCheck(this,
|
|
161100
|
+
webidl.brandCheck(this, WebSocket2);
|
|
160976
161101
|
const prefix = "WebSocket.close";
|
|
160977
161102
|
if (code !== undefined) {
|
|
160978
161103
|
code = webidl.converters["unsigned short"](code, prefix, "code", webidl.attributes.Clamp);
|
|
@@ -160985,7 +161110,7 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
160985
161110
|
closeWebSocketConnection(this.#handler, code, reason, true);
|
|
160986
161111
|
}
|
|
160987
161112
|
send(data) {
|
|
160988
|
-
webidl.brandCheck(this,
|
|
161113
|
+
webidl.brandCheck(this, WebSocket2);
|
|
160989
161114
|
const prefix = "WebSocket.send";
|
|
160990
161115
|
webidl.argumentLengthCheck(arguments, 1, prefix);
|
|
160991
161116
|
data = webidl.converters.WebSocketSendData(data, prefix, "data");
|
|
@@ -161019,31 +161144,31 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
161019
161144
|
}
|
|
161020
161145
|
}
|
|
161021
161146
|
get readyState() {
|
|
161022
|
-
webidl.brandCheck(this,
|
|
161147
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161023
161148
|
return this.#handler.readyState;
|
|
161024
161149
|
}
|
|
161025
161150
|
get bufferedAmount() {
|
|
161026
|
-
webidl.brandCheck(this,
|
|
161151
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161027
161152
|
return this.#bufferedAmount;
|
|
161028
161153
|
}
|
|
161029
161154
|
get url() {
|
|
161030
|
-
webidl.brandCheck(this,
|
|
161155
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161031
161156
|
return URLSerializer(this.#url);
|
|
161032
161157
|
}
|
|
161033
161158
|
get extensions() {
|
|
161034
|
-
webidl.brandCheck(this,
|
|
161159
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161035
161160
|
return this.#extensions;
|
|
161036
161161
|
}
|
|
161037
161162
|
get protocol() {
|
|
161038
|
-
webidl.brandCheck(this,
|
|
161163
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161039
161164
|
return this.#protocol;
|
|
161040
161165
|
}
|
|
161041
161166
|
get onopen() {
|
|
161042
|
-
webidl.brandCheck(this,
|
|
161167
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161043
161168
|
return this.#events.open;
|
|
161044
161169
|
}
|
|
161045
161170
|
set onopen(fn) {
|
|
161046
|
-
webidl.brandCheck(this,
|
|
161171
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161047
161172
|
if (this.#events.open) {
|
|
161048
161173
|
this.removeEventListener("open", this.#events.open);
|
|
161049
161174
|
}
|
|
@@ -161056,11 +161181,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
161056
161181
|
}
|
|
161057
161182
|
}
|
|
161058
161183
|
get onerror() {
|
|
161059
|
-
webidl.brandCheck(this,
|
|
161184
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161060
161185
|
return this.#events.error;
|
|
161061
161186
|
}
|
|
161062
161187
|
set onerror(fn) {
|
|
161063
|
-
webidl.brandCheck(this,
|
|
161188
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161064
161189
|
if (this.#events.error) {
|
|
161065
161190
|
this.removeEventListener("error", this.#events.error);
|
|
161066
161191
|
}
|
|
@@ -161073,11 +161198,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
161073
161198
|
}
|
|
161074
161199
|
}
|
|
161075
161200
|
get onclose() {
|
|
161076
|
-
webidl.brandCheck(this,
|
|
161201
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161077
161202
|
return this.#events.close;
|
|
161078
161203
|
}
|
|
161079
161204
|
set onclose(fn) {
|
|
161080
|
-
webidl.brandCheck(this,
|
|
161205
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161081
161206
|
if (this.#events.close) {
|
|
161082
161207
|
this.removeEventListener("close", this.#events.close);
|
|
161083
161208
|
}
|
|
@@ -161090,11 +161215,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
161090
161215
|
}
|
|
161091
161216
|
}
|
|
161092
161217
|
get onmessage() {
|
|
161093
|
-
webidl.brandCheck(this,
|
|
161218
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161094
161219
|
return this.#events.message;
|
|
161095
161220
|
}
|
|
161096
161221
|
set onmessage(fn) {
|
|
161097
|
-
webidl.brandCheck(this,
|
|
161222
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161098
161223
|
if (this.#events.message) {
|
|
161099
161224
|
this.removeEventListener("message", this.#events.message);
|
|
161100
161225
|
}
|
|
@@ -161107,11 +161232,11 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
161107
161232
|
}
|
|
161108
161233
|
}
|
|
161109
161234
|
get binaryType() {
|
|
161110
|
-
webidl.brandCheck(this,
|
|
161235
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161111
161236
|
return this.#binaryType;
|
|
161112
161237
|
}
|
|
161113
161238
|
set binaryType(type) {
|
|
161114
|
-
webidl.brandCheck(this,
|
|
161239
|
+
webidl.brandCheck(this, WebSocket2);
|
|
161115
161240
|
if (type !== "blob" && type !== "arraybuffer") {
|
|
161116
161241
|
this.#binaryType = "blob";
|
|
161117
161242
|
} else {
|
|
@@ -161227,13 +161352,13 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
161227
161352
|
}
|
|
161228
161353
|
}
|
|
161229
161354
|
}
|
|
161230
|
-
var { ping } =
|
|
161231
|
-
Reflect.deleteProperty(
|
|
161232
|
-
|
|
161233
|
-
|
|
161234
|
-
|
|
161235
|
-
|
|
161236
|
-
Object.defineProperties(
|
|
161355
|
+
var { ping } = WebSocket2;
|
|
161356
|
+
Reflect.deleteProperty(WebSocket2, "ping");
|
|
161357
|
+
WebSocket2.CONNECTING = WebSocket2.prototype.CONNECTING = states.CONNECTING;
|
|
161358
|
+
WebSocket2.OPEN = WebSocket2.prototype.OPEN = states.OPEN;
|
|
161359
|
+
WebSocket2.CLOSING = WebSocket2.prototype.CLOSING = states.CLOSING;
|
|
161360
|
+
WebSocket2.CLOSED = WebSocket2.prototype.CLOSED = states.CLOSED;
|
|
161361
|
+
Object.defineProperties(WebSocket2.prototype, {
|
|
161237
161362
|
CONNECTING: staticPropertyDescriptors,
|
|
161238
161363
|
OPEN: staticPropertyDescriptors,
|
|
161239
161364
|
CLOSING: staticPropertyDescriptors,
|
|
@@ -161257,7 +161382,7 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
161257
161382
|
configurable: true
|
|
161258
161383
|
}
|
|
161259
161384
|
});
|
|
161260
|
-
Object.defineProperties(
|
|
161385
|
+
Object.defineProperties(WebSocket2, {
|
|
161261
161386
|
CONNECTING: staticPropertyDescriptors,
|
|
161262
161387
|
OPEN: staticPropertyDescriptors,
|
|
161263
161388
|
CLOSING: staticPropertyDescriptors,
|
|
@@ -161304,7 +161429,7 @@ var require_websocket2 = __commonJS(function(exports, module) {
|
|
|
161304
161429
|
return webidl.converters.USVString(V);
|
|
161305
161430
|
};
|
|
161306
161431
|
module.exports = {
|
|
161307
|
-
WebSocket,
|
|
161432
|
+
WebSocket: WebSocket2,
|
|
161308
161433
|
ping
|
|
161309
161434
|
};
|
|
161310
161435
|
});
|
|
@@ -162189,7 +162314,7 @@ function makeDispatcher(fn) {
|
|
|
162189
162314
|
}, handler);
|
|
162190
162315
|
};
|
|
162191
162316
|
}
|
|
162192
|
-
var __filename = "/home/runner/work/december/december/node_modules/.bun/undici@7.28.0/node_modules/undici/index.js", Client, Dispatcher, Pool, BalancedPool, RoundRobinPool, Agent, ProxyAgent, Socks5ProxyAgent, EnvHttpProxyAgent, RetryAgent, H2CClient, errors2, util3, InvalidArgumentError, api, buildConnector, MockClient, MockCallHistory, MockCallHistoryLog, MockAgent, MockPool, SnapshotAgent, mockErrors, RetryHandler, getGlobalDispatcher, setGlobalDispatcher, DecoratorHandler, RedirectHandler, $interceptors, $cacheStores, SqliteCacheStore, fetchImpl, $Headers, $Response, $Request, $FormData, setGlobalOrigin, getGlobalOrigin, CacheStorage, kConstruct, $caches, deleteCookie, getCookies, getSetCookies, setCookie, parseCookie, parseMIMEType, serializeAMimeType, CloseEvent, ErrorEvent, MessageEvent,
|
|
162317
|
+
var __filename = "/home/runner/work/december/december/node_modules/.bun/undici@7.28.0/node_modules/undici/index.js", Client, Dispatcher, Pool, BalancedPool, RoundRobinPool, Agent, ProxyAgent, Socks5ProxyAgent, EnvHttpProxyAgent, RetryAgent, H2CClient, errors2, util3, InvalidArgumentError, api, buildConnector, MockClient, MockCallHistory, MockCallHistoryLog, MockAgent, MockPool, SnapshotAgent, mockErrors, RetryHandler, getGlobalDispatcher, setGlobalDispatcher, DecoratorHandler, RedirectHandler, $interceptors, $cacheStores, SqliteCacheStore, fetchImpl, $Headers, $Response, $Request, $FormData, setGlobalOrigin, getGlobalOrigin, CacheStorage, kConstruct, $caches, deleteCookie, getCookies, getSetCookies, setCookie, parseCookie, parseMIMEType, serializeAMimeType, CloseEvent, ErrorEvent, MessageEvent, WebSocket3, ping, $WebSocketStream, $WebSocketError, $request, $stream, $pipeline, $connect, $upgrade, EventSource;
|
|
162193
162318
|
var init_undici = __esm(() => {
|
|
162194
162319
|
Client = require_client();
|
|
162195
162320
|
Dispatcher = require_dispatcher();
|
|
@@ -162245,7 +162370,7 @@ var init_undici = __esm(() => {
|
|
|
162245
162370
|
({ deleteCookie, getCookies, getSetCookies, setCookie, parseCookie } = require_cookies());
|
|
162246
162371
|
({ parseMIMEType, serializeAMimeType } = require_data_url());
|
|
162247
162372
|
({ CloseEvent, ErrorEvent, MessageEvent } = require_events());
|
|
162248
|
-
({ WebSocket:
|
|
162373
|
+
({ WebSocket: WebSocket3, ping } = require_websocket2());
|
|
162249
162374
|
$WebSocketStream = require_websocketstream().WebSocketStream;
|
|
162250
162375
|
$WebSocketError = require_websocketerror().WebSocketError;
|
|
162251
162376
|
$request = makeDispatcher(api.request);
|
|
@@ -162835,6 +162960,7 @@ var init_src4 = __esm(() => {
|
|
|
162835
162960
|
init_ask_question();
|
|
162836
162961
|
init_bash();
|
|
162837
162962
|
init_browser();
|
|
162963
|
+
init_browser_action();
|
|
162838
162964
|
init_code_intelligence();
|
|
162839
162965
|
init_deferred_registry();
|
|
162840
162966
|
init_edit();
|
|
@@ -162862,6 +162988,7 @@ var init_src4 = __esm(() => {
|
|
|
162862
162988
|
init_manage_task();
|
|
162863
162989
|
init_manage_memory();
|
|
162864
162990
|
init_browser();
|
|
162991
|
+
init_browser_action();
|
|
162865
162992
|
init_web_search();
|
|
162866
162993
|
init_diff_preview();
|
|
162867
162994
|
init_deferred_registry();
|
|
@@ -162881,11 +163008,26 @@ var init_src4 = __esm(() => {
|
|
|
162881
163008
|
CodeIntelligenceTool,
|
|
162882
163009
|
ManageMemoryTool
|
|
162883
163010
|
];
|
|
162884
|
-
DEFERRED_TOOLS = [ManageTaskTool, BrowserTool, WebSearchTool];
|
|
163011
|
+
DEFERRED_TOOLS = [ManageTaskTool, BrowserTool, BrowserActionTool, WebSearchTool];
|
|
162885
163012
|
defaultDeferredRegistry.register(BrowserTool, {
|
|
162886
163013
|
category: "web",
|
|
162887
163014
|
keywords: ["browser", "render", "html", "scrape", "page", "url", "browse", "webpage"]
|
|
162888
163015
|
});
|
|
163016
|
+
defaultDeferredRegistry.register(BrowserActionTool, {
|
|
163017
|
+
category: "web",
|
|
163018
|
+
keywords: [
|
|
163019
|
+
"browser_action",
|
|
163020
|
+
"click",
|
|
163021
|
+
"type",
|
|
163022
|
+
"screenshot",
|
|
163023
|
+
"dom",
|
|
163024
|
+
"test",
|
|
163025
|
+
"input",
|
|
163026
|
+
"form",
|
|
163027
|
+
"ui",
|
|
163028
|
+
"localhost"
|
|
163029
|
+
]
|
|
163030
|
+
});
|
|
162889
163031
|
defaultDeferredRegistry.register(WebSearchTool, {
|
|
162890
163032
|
category: "web",
|
|
162891
163033
|
keywords: ["search", "google", "duckduckgo", "tavily", "internet", "docs", "websearch"]
|
|
@@ -169706,13 +169848,210 @@ var init_headless_runner = __esm(() => {
|
|
|
169706
169848
|
};
|
|
169707
169849
|
});
|
|
169708
169850
|
|
|
169851
|
+
// src/utils/workspace-trust.ts
|
|
169852
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
169853
|
+
import fs25 from "node:fs";
|
|
169854
|
+
import os13 from "node:os";
|
|
169855
|
+
import path26 from "node:path";
|
|
169856
|
+
import readline3 from "node:readline";
|
|
169857
|
+
function resolveWorkspaceRoot(targetDir = process.cwd()) {
|
|
169858
|
+
const resolved = path26.resolve(targetDir);
|
|
169859
|
+
try {
|
|
169860
|
+
const stdout = execSync2("git rev-parse --show-toplevel", {
|
|
169861
|
+
cwd: resolved,
|
|
169862
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
169863
|
+
encoding: "utf8"
|
|
169864
|
+
}).trim();
|
|
169865
|
+
if (stdout && fs25.existsSync(stdout)) {
|
|
169866
|
+
return path26.resolve(stdout);
|
|
169867
|
+
}
|
|
169868
|
+
} catch {}
|
|
169869
|
+
let current = resolved;
|
|
169870
|
+
const homeDir = path26.resolve(os13.homedir());
|
|
169871
|
+
while (true) {
|
|
169872
|
+
if (fs25.existsSync(path26.join(current, ".git"))) {
|
|
169873
|
+
return current;
|
|
169874
|
+
}
|
|
169875
|
+
const parent = path26.dirname(current);
|
|
169876
|
+
if (parent === current || current === homeDir) {
|
|
169877
|
+
break;
|
|
169878
|
+
}
|
|
169879
|
+
current = parent;
|
|
169880
|
+
}
|
|
169881
|
+
return resolved;
|
|
169882
|
+
}
|
|
169883
|
+
function isWorkspaceTrusted(workspaceRoot, config) {
|
|
169884
|
+
const canonicalRoot = path26.resolve(workspaceRoot);
|
|
169885
|
+
const homeDir = path26.resolve(os13.homedir());
|
|
169886
|
+
const trustedList = (config.trustedWorkspaces || []).map((p) => path26.resolve(p));
|
|
169887
|
+
for (const trustedPath of trustedList) {
|
|
169888
|
+
if (canonicalRoot === trustedPath) {
|
|
169889
|
+
return true;
|
|
169890
|
+
}
|
|
169891
|
+
if (trustedPath !== "/" && trustedPath !== homeDir) {
|
|
169892
|
+
const rel = path26.relative(trustedPath, canonicalRoot);
|
|
169893
|
+
if (!rel.startsWith("..") && !path26.isAbsolute(rel)) {
|
|
169894
|
+
return true;
|
|
169895
|
+
}
|
|
169896
|
+
}
|
|
169897
|
+
}
|
|
169898
|
+
return false;
|
|
169899
|
+
}
|
|
169900
|
+
async function addTrustedWorkspace(workspaceRoot) {
|
|
169901
|
+
const config = await loadConfig();
|
|
169902
|
+
const canonical = path26.resolve(workspaceRoot);
|
|
169903
|
+
const existing = (config.trustedWorkspaces || []).map((p) => path26.resolve(p));
|
|
169904
|
+
if (!existing.includes(canonical)) {
|
|
169905
|
+
config.trustedWorkspaces = [...existing, canonical];
|
|
169906
|
+
await saveConfig(config);
|
|
169907
|
+
}
|
|
169908
|
+
}
|
|
169909
|
+
async function promptWorkspaceTrust(workspaceRoot) {
|
|
169910
|
+
if (!process.stdin.isTTY) {
|
|
169911
|
+
return false;
|
|
169912
|
+
}
|
|
169913
|
+
const homeDir = path26.resolve(os13.homedir());
|
|
169914
|
+
const isHomeOrRoot = workspaceRoot === homeDir || workspaceRoot === "/";
|
|
169915
|
+
const BRAND = "\x1B[38;2;137;180;248m";
|
|
169916
|
+
const TEXT = "\x1B[38;2;244;244;245m";
|
|
169917
|
+
const MUTED = "\x1B[38;2;170;170;170m";
|
|
169918
|
+
const GRAY = "\x1B[38;2;119;119;119m";
|
|
169919
|
+
const YELLOW = "\x1B[38;2;253;214;99m";
|
|
169920
|
+
const BOLD = "\x1B[1m";
|
|
169921
|
+
const RESET = "\x1B[0m";
|
|
169922
|
+
const options = isHomeOrRoot ? [
|
|
169923
|
+
{ label: "No, exit", value: false },
|
|
169924
|
+
{ label: "Yes, trust folder anyway", value: true }
|
|
169925
|
+
] : [
|
|
169926
|
+
{ label: "Yes, trust folder", value: true },
|
|
169927
|
+
{ label: "No, exit", value: false }
|
|
169928
|
+
];
|
|
169929
|
+
return new Promise((resolve) => {
|
|
169930
|
+
let selectedIndex = 0;
|
|
169931
|
+
let renderedLineCount = 0;
|
|
169932
|
+
const wasRaw = process.stdin.isRaw;
|
|
169933
|
+
if (typeof process.stdin.setRawMode === "function") {
|
|
169934
|
+
process.stdin.setRawMode(true);
|
|
169935
|
+
}
|
|
169936
|
+
process.stdin.resume();
|
|
169937
|
+
readline3.emitKeypressEvents(process.stdin);
|
|
169938
|
+
const cleanup = () => {
|
|
169939
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
169940
|
+
if (typeof process.stdin.setRawMode === "function" && !wasRaw) {
|
|
169941
|
+
process.stdin.setRawMode(false);
|
|
169942
|
+
}
|
|
169943
|
+
process.stdin.pause();
|
|
169944
|
+
process.stdout.write("\x1B[?25h");
|
|
169945
|
+
};
|
|
169946
|
+
const clearRenderedLines = () => {
|
|
169947
|
+
if (renderedLineCount > 0) {
|
|
169948
|
+
readline3.cursorTo(process.stdout, 0);
|
|
169949
|
+
readline3.moveCursor(process.stdout, 0, -renderedLineCount);
|
|
169950
|
+
readline3.clearScreenDown(process.stdout);
|
|
169951
|
+
}
|
|
169952
|
+
};
|
|
169953
|
+
const render = () => {
|
|
169954
|
+
clearRenderedLines();
|
|
169955
|
+
const lines = [];
|
|
169956
|
+
lines.push("");
|
|
169957
|
+
lines.push(` ${BOLD}${TEXT}Trust workspace?${RESET}`);
|
|
169958
|
+
lines.push("");
|
|
169959
|
+
lines.push(` ${GRAY}Directory${RESET} ${BOLD}${BRAND}${workspaceRoot}${RESET}`);
|
|
169960
|
+
if (isHomeOrRoot) {
|
|
169961
|
+
lines.push(` ${YELLOW}Warning${RESET} ${YELLOW}Home or system root directory. Gives access to all personal files.${RESET}`);
|
|
169962
|
+
} else {
|
|
169963
|
+
lines.push(` ${GRAY}Permission${RESET} ${TEXT}Read, edit, and run commands${RESET}`);
|
|
169964
|
+
}
|
|
169965
|
+
lines.push("");
|
|
169966
|
+
for (let i = 0;i < options.length; i++) {
|
|
169967
|
+
const opt = options[i];
|
|
169968
|
+
if (i === selectedIndex) {
|
|
169969
|
+
lines.push(` ${BRAND}❭${RESET} ${BOLD}${TEXT}${opt.label}${RESET}`);
|
|
169970
|
+
} else {
|
|
169971
|
+
lines.push(` ${MUTED}${opt.label}${RESET}`);
|
|
169972
|
+
}
|
|
169973
|
+
}
|
|
169974
|
+
lines.push("");
|
|
169975
|
+
lines.push(` ${BRAND}Enter${RESET} ${MUTED}confirm${RESET} ${MUTED}·${RESET} ${BRAND}Esc${RESET} ${MUTED}cancel${RESET} ${MUTED}·${RESET} ${BRAND}↑↓${RESET} ${MUTED}navigate${RESET}`);
|
|
169976
|
+
lines.push("");
|
|
169977
|
+
renderedLineCount = lines.length;
|
|
169978
|
+
process.stdout.write(lines.join(`
|
|
169979
|
+
`));
|
|
169980
|
+
};
|
|
169981
|
+
const onKeypress = (str, key) => {
|
|
169982
|
+
if (!key && !str)
|
|
169983
|
+
return;
|
|
169984
|
+
const lowerStr = (str || "").toLowerCase();
|
|
169985
|
+
const keyName = key?.name;
|
|
169986
|
+
if (lowerStr === "y" || keyName === "y") {
|
|
169987
|
+
cleanup();
|
|
169988
|
+
clearRenderedLines();
|
|
169989
|
+
resolve(true);
|
|
169990
|
+
return;
|
|
169991
|
+
}
|
|
169992
|
+
if (lowerStr === "n" || keyName === "n") {
|
|
169993
|
+
cleanup();
|
|
169994
|
+
clearRenderedLines();
|
|
169995
|
+
resolve(false);
|
|
169996
|
+
return;
|
|
169997
|
+
}
|
|
169998
|
+
if (keyName === "up" || keyName === "k") {
|
|
169999
|
+
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
170000
|
+
render();
|
|
170001
|
+
} else if (keyName === "down" || keyName === "j" || keyName === "tab") {
|
|
170002
|
+
selectedIndex = Math.min(options.length - 1, selectedIndex + 1);
|
|
170003
|
+
render();
|
|
170004
|
+
} else if (keyName === "return" || keyName === "enter") {
|
|
170005
|
+
cleanup();
|
|
170006
|
+
clearRenderedLines();
|
|
170007
|
+
resolve(options[selectedIndex].value);
|
|
170008
|
+
} else if (keyName === "escape" || key?.ctrl && keyName === "c") {
|
|
170009
|
+
cleanup();
|
|
170010
|
+
clearRenderedLines();
|
|
170011
|
+
resolve(false);
|
|
170012
|
+
}
|
|
170013
|
+
};
|
|
170014
|
+
process.stdout.write("\x1B[?25l");
|
|
170015
|
+
process.stdin.on("keypress", onKeypress);
|
|
170016
|
+
render();
|
|
170017
|
+
});
|
|
170018
|
+
}
|
|
170019
|
+
async function ensureWorkspaceTrust(options = {}) {
|
|
170020
|
+
const workspaceRoot = resolveWorkspaceRoot(options.targetDir || process.cwd());
|
|
170021
|
+
const config = await loadConfig();
|
|
170022
|
+
if (isWorkspaceTrusted(workspaceRoot, config)) {
|
|
170023
|
+
return;
|
|
170024
|
+
}
|
|
170025
|
+
const isNonInteractive = Boolean(options.isHeadless) || !process.stdin.isTTY || Boolean(process.env.NON_INTERACTIVE) || Boolean(process.env.CI);
|
|
170026
|
+
if (isNonInteractive) {
|
|
170027
|
+
const RED = "\x1B[38;2;252;165;165m";
|
|
170028
|
+
const RESET = "\x1B[0m";
|
|
170029
|
+
console.error(`
|
|
170030
|
+
${RED}Error:${RESET} Workspace "${workspaceRoot}" is not trusted.`);
|
|
170031
|
+
console.error(`Please run "december" interactively first to review and trust this workspace.
|
|
170032
|
+
`);
|
|
170033
|
+
process.exit(1);
|
|
170034
|
+
}
|
|
170035
|
+
const trusted = await promptWorkspaceTrust(workspaceRoot);
|
|
170036
|
+
if (!trusted) {
|
|
170037
|
+
console.log(`
|
|
170038
|
+
Workspace untrusted. Exiting.
|
|
170039
|
+
`);
|
|
170040
|
+
process.exit(0);
|
|
170041
|
+
}
|
|
170042
|
+
await addTrustedWorkspace(workspaceRoot);
|
|
170043
|
+
}
|
|
170044
|
+
var init_workspace_trust = __esm(() => {
|
|
170045
|
+
init_config();
|
|
170046
|
+
});
|
|
170047
|
+
|
|
169709
170048
|
// src/file-session-repository.ts
|
|
169710
170049
|
var exports_file_session_repository = {};
|
|
169711
170050
|
__export(exports_file_session_repository, {
|
|
169712
170051
|
FileSessionRepository: () => FileSessionRepository
|
|
169713
170052
|
});
|
|
169714
|
-
import
|
|
169715
|
-
import
|
|
170053
|
+
import fs26 from "node:fs/promises";
|
|
170054
|
+
import path27 from "node:path";
|
|
169716
170055
|
|
|
169717
170056
|
class FileSessionRepository {
|
|
169718
170057
|
sessionDir;
|
|
@@ -169722,22 +170061,22 @@ class FileSessionRepository {
|
|
|
169722
170061
|
if (sessionDir) {
|
|
169723
170062
|
this.sessionDir = sessionDir;
|
|
169724
170063
|
} else {
|
|
169725
|
-
this.sessionDir =
|
|
169726
|
-
const legacyDir =
|
|
170064
|
+
this.sessionDir = path27.join(getConfigDir(), "sessions");
|
|
170065
|
+
const legacyDir = path27.join(getLegacyConfigDir(), "sessions");
|
|
169727
170066
|
if (legacyDir !== this.sessionDir) {
|
|
169728
170067
|
this.legacySessionDir = legacyDir;
|
|
169729
170068
|
}
|
|
169730
170069
|
}
|
|
169731
170070
|
}
|
|
169732
170071
|
async saveContext(sessionId, messages) {
|
|
169733
|
-
await
|
|
169734
|
-
const historyPath =
|
|
170072
|
+
await fs26.mkdir(this.sessionDir, { recursive: true });
|
|
170073
|
+
const historyPath = path27.join(this.sessionDir, `${sessionId}.jsonl`);
|
|
169735
170074
|
let existing = this.sessionCache[sessionId];
|
|
169736
170075
|
if (!existing) {
|
|
169737
170076
|
existing = {};
|
|
169738
170077
|
this.sessionCache[sessionId] = existing;
|
|
169739
170078
|
try {
|
|
169740
|
-
const data = await
|
|
170079
|
+
const data = await fs26.readFile(historyPath, "utf-8");
|
|
169741
170080
|
const lines = data.split(`
|
|
169742
170081
|
`).filter((l) => l.trim().length > 0);
|
|
169743
170082
|
for (const l of lines) {
|
|
@@ -169761,19 +170100,19 @@ class FileSessionRepository {
|
|
|
169761
170100
|
}
|
|
169762
170101
|
}
|
|
169763
170102
|
if (appendContent) {
|
|
169764
|
-
await
|
|
170103
|
+
await fs26.appendFile(historyPath, appendContent, "utf-8");
|
|
169765
170104
|
}
|
|
169766
170105
|
}
|
|
169767
170106
|
async loadContext(sessionId) {
|
|
169768
|
-
let historyPath =
|
|
170107
|
+
let historyPath = path27.join(this.sessionDir, `${sessionId}.jsonl`);
|
|
169769
170108
|
try {
|
|
169770
170109
|
let data;
|
|
169771
170110
|
try {
|
|
169772
|
-
data = await
|
|
170111
|
+
data = await fs26.readFile(historyPath, "utf-8");
|
|
169773
170112
|
} catch (err) {
|
|
169774
170113
|
if (this.legacySessionDir) {
|
|
169775
|
-
const legacyPath =
|
|
169776
|
-
data = await
|
|
170114
|
+
const legacyPath = path27.join(this.legacySessionDir, `${sessionId}.jsonl`);
|
|
170115
|
+
data = await fs26.readFile(legacyPath, "utf-8");
|
|
169777
170116
|
historyPath = legacyPath;
|
|
169778
170117
|
} else {
|
|
169779
170118
|
throw err;
|
|
@@ -169814,7 +170153,7 @@ class FileSessionRepository {
|
|
|
169814
170153
|
if (msgs.length < lines.length) {
|
|
169815
170154
|
const compacted = msgs.map((m) => JSON.stringify(m)).join(`
|
|
169816
170155
|
`);
|
|
169817
|
-
await
|
|
170156
|
+
await fs26.writeFile(historyPath, compacted, "utf-8").catch(() => {});
|
|
169818
170157
|
}
|
|
169819
170158
|
const cacheObj = {};
|
|
169820
170159
|
for (const msg of msgs) {
|
|
@@ -169828,12 +170167,12 @@ class FileSessionRepository {
|
|
|
169828
170167
|
}
|
|
169829
170168
|
async listSessions() {
|
|
169830
170169
|
try {
|
|
169831
|
-
await
|
|
170170
|
+
await fs26.mkdir(this.sessionDir, { recursive: true });
|
|
169832
170171
|
const sessions = [];
|
|
169833
170172
|
const seenIds = new Set;
|
|
169834
170173
|
const processDirectory = async (dirPath) => {
|
|
169835
170174
|
try {
|
|
169836
|
-
const dirFiles = await
|
|
170175
|
+
const dirFiles = await fs26.readdir(dirPath);
|
|
169837
170176
|
for (const file of dirFiles) {
|
|
169838
170177
|
if (!file.endsWith(".jsonl"))
|
|
169839
170178
|
continue;
|
|
@@ -169841,10 +170180,10 @@ class FileSessionRepository {
|
|
|
169841
170180
|
if (seenIds.has(id))
|
|
169842
170181
|
continue;
|
|
169843
170182
|
seenIds.add(id);
|
|
169844
|
-
const filePath =
|
|
170183
|
+
const filePath = path27.join(dirPath, file);
|
|
169845
170184
|
try {
|
|
169846
|
-
const stat = await
|
|
169847
|
-
const data = await
|
|
170185
|
+
const stat = await fs26.stat(filePath);
|
|
170186
|
+
const data = await fs26.readFile(filePath, "utf-8");
|
|
169848
170187
|
const lines = data.split(`
|
|
169849
170188
|
`).filter((l) => l.trim().length > 0);
|
|
169850
170189
|
let preview = "";
|
|
@@ -169878,20 +170217,20 @@ class FileSessionRepository {
|
|
|
169878
170217
|
}
|
|
169879
170218
|
}
|
|
169880
170219
|
async deleteSession(sessionId) {
|
|
169881
|
-
const historyPath =
|
|
170220
|
+
const historyPath = path27.join(this.sessionDir, `${sessionId}.jsonl`);
|
|
169882
170221
|
try {
|
|
169883
|
-
await
|
|
170222
|
+
await fs26.unlink(historyPath);
|
|
169884
170223
|
} catch (e) {}
|
|
169885
170224
|
if (this.legacySessionDir) {
|
|
169886
170225
|
try {
|
|
169887
|
-
await
|
|
170226
|
+
await fs26.unlink(path27.join(this.legacySessionDir, `${sessionId}.jsonl`));
|
|
169888
170227
|
} catch {}
|
|
169889
170228
|
}
|
|
169890
170229
|
}
|
|
169891
170230
|
async renameSession(oldSessionId, newSessionId) {
|
|
169892
|
-
const oldPath =
|
|
169893
|
-
const newPath =
|
|
169894
|
-
await
|
|
170231
|
+
const oldPath = path27.join(this.sessionDir, `${oldSessionId}.jsonl`);
|
|
170232
|
+
const newPath = path27.join(this.sessionDir, `${newSessionId}.jsonl`);
|
|
170233
|
+
await fs26.rename(oldPath, newPath);
|
|
169895
170234
|
}
|
|
169896
170235
|
}
|
|
169897
170236
|
var init_file_session_repository = __esm(() => {
|
|
@@ -175518,6 +175857,614 @@ var init_task_manager = __esm(() => {
|
|
|
175518
175857
|
}
|
|
175519
175858
|
});
|
|
175520
175859
|
|
|
175860
|
+
// src/utils/browser/detector.ts
|
|
175861
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
175862
|
+
import fs27 from "node:fs/promises";
|
|
175863
|
+
import os14 from "node:os";
|
|
175864
|
+
import path28 from "node:path";
|
|
175865
|
+
async function isExecutable(filePath) {
|
|
175866
|
+
try {
|
|
175867
|
+
await fs27.access(filePath, fs27.constants.X_OK);
|
|
175868
|
+
const stats = await fs27.stat(filePath);
|
|
175869
|
+
return stats.isFile();
|
|
175870
|
+
} catch {
|
|
175871
|
+
return false;
|
|
175872
|
+
}
|
|
175873
|
+
}
|
|
175874
|
+
async function findSystemChrome() {
|
|
175875
|
+
if (cachedChromePath !== undefined) {
|
|
175876
|
+
return cachedChromePath;
|
|
175877
|
+
}
|
|
175878
|
+
const envVars = ["CHROME_PATH", "CHROME_BIN", "CHROMIUM_PATH", "PUPPETEER_EXECUTABLE_PATH"];
|
|
175879
|
+
for (const envVar of envVars) {
|
|
175880
|
+
const val = process.env[envVar];
|
|
175881
|
+
if (val && await isExecutable(val)) {
|
|
175882
|
+
cachedChromePath = val;
|
|
175883
|
+
return val;
|
|
175884
|
+
}
|
|
175885
|
+
}
|
|
175886
|
+
const platform = os14.platform();
|
|
175887
|
+
const candidates = [];
|
|
175888
|
+
if (platform === "linux") {
|
|
175889
|
+
candidates.push("/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium", "/usr/bin/chromium-browser", "/snap/bin/chromium", "/usr/bin/brave-browser", "/usr/bin/microsoft-edge", "/usr/bin/microsoft-edge-stable");
|
|
175890
|
+
} else if (platform === "darwin") {
|
|
175891
|
+
const home = os14.homedir();
|
|
175892
|
+
candidates.push("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Chromium.app/Contents/MacOS/Chromium", "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser", "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", path28.join(home, "Applications/Google Chrome.app/Contents/MacOS/Google Chrome"), path28.join(home, "Applications/Chromium.app/Contents/MacOS/Chromium"));
|
|
175893
|
+
} else if (platform === "win32") {
|
|
175894
|
+
const localAppData = process.env.LOCALAPPDATA || "";
|
|
175895
|
+
const programFiles = process.env.PROGRAMFILES || "C:\\Program Files";
|
|
175896
|
+
const programFilesX86 = process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)";
|
|
175897
|
+
candidates.push(path28.join(programFiles, "Google\\Chrome\\Application\\chrome.exe"), path28.join(programFilesX86, "Google\\Chrome\\Application\\chrome.exe"), path28.join(localAppData, "Google\\Chrome\\Application\\chrome.exe"), path28.join(programFiles, "Microsoft\\Edge\\Application\\msedge.exe"), path28.join(programFilesX86, "Microsoft\\Edge\\Application\\msedge.exe"), path28.join(localAppData, "BraveSoftware\\Brave-Browser\\Application\\brave.exe"));
|
|
175898
|
+
}
|
|
175899
|
+
for (const candidate of candidates) {
|
|
175900
|
+
if (await isExecutable(candidate)) {
|
|
175901
|
+
cachedChromePath = candidate;
|
|
175902
|
+
return candidate;
|
|
175903
|
+
}
|
|
175904
|
+
}
|
|
175905
|
+
const binaryNames = platform === "win32" ? ["chrome.exe", "msedge.exe", "brave.exe"] : [
|
|
175906
|
+
"google-chrome",
|
|
175907
|
+
"google-chrome-stable",
|
|
175908
|
+
"chromium",
|
|
175909
|
+
"chromium-browser",
|
|
175910
|
+
"brave-browser",
|
|
175911
|
+
"microsoft-edge"
|
|
175912
|
+
];
|
|
175913
|
+
const whichCmd = platform === "win32" ? "where" : "which";
|
|
175914
|
+
for (const bin of binaryNames) {
|
|
175915
|
+
try {
|
|
175916
|
+
const out = execSync3(`${whichCmd} ${bin}`, {
|
|
175917
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
175918
|
+
encoding: "utf-8"
|
|
175919
|
+
}).trim();
|
|
175920
|
+
const firstLine = out.split(`
|
|
175921
|
+
`)[0]?.trim();
|
|
175922
|
+
if (firstLine && await isExecutable(firstLine)) {
|
|
175923
|
+
cachedChromePath = firstLine;
|
|
175924
|
+
return firstLine;
|
|
175925
|
+
}
|
|
175926
|
+
} catch {}
|
|
175927
|
+
}
|
|
175928
|
+
cachedChromePath = null;
|
|
175929
|
+
return null;
|
|
175930
|
+
}
|
|
175931
|
+
var cachedChromePath = undefined;
|
|
175932
|
+
var init_detector = () => {};
|
|
175933
|
+
|
|
175934
|
+
// src/utils/browser/cdp-client.ts
|
|
175935
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
175936
|
+
import fs28 from "node:fs/promises";
|
|
175937
|
+
import os15 from "node:os";
|
|
175938
|
+
import path29 from "node:path";
|
|
175939
|
+
|
|
175940
|
+
class CdpBrowserSession {
|
|
175941
|
+
options;
|
|
175942
|
+
chromeProc = null;
|
|
175943
|
+
ws = null;
|
|
175944
|
+
nextId = 1;
|
|
175945
|
+
pendingCommands = new Map;
|
|
175946
|
+
sessionId = null;
|
|
175947
|
+
tmpProfileDir = null;
|
|
175948
|
+
consoleErrors = [];
|
|
175949
|
+
networkErrors = [];
|
|
175950
|
+
isLoaded = false;
|
|
175951
|
+
constructor(options) {
|
|
175952
|
+
this.options = options;
|
|
175953
|
+
}
|
|
175954
|
+
async launch() {
|
|
175955
|
+
this.tmpProfileDir = path29.join(os15.tmpdir(), `december-chrome-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
|
|
175956
|
+
await fs28.mkdir(this.tmpProfileDir, { recursive: true });
|
|
175957
|
+
const isHeadless = this.options.headless ?? (process.env.DECEMBER_HEADED !== "1" && process.env.HEADED !== "1");
|
|
175958
|
+
const args = [
|
|
175959
|
+
...isHeadless ? ["--headless=new"] : [],
|
|
175960
|
+
"--disable-gpu",
|
|
175961
|
+
"--no-sandbox",
|
|
175962
|
+
"--disable-dev-shm-usage",
|
|
175963
|
+
"--remote-debugging-port=0",
|
|
175964
|
+
"--hide-scrollbars",
|
|
175965
|
+
"--window-size=1280,800",
|
|
175966
|
+
`--user-data-dir=${this.tmpProfileDir}`,
|
|
175967
|
+
"about:blank"
|
|
175968
|
+
];
|
|
175969
|
+
const proc = spawn4(this.options.chromePath, args, {
|
|
175970
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
175971
|
+
});
|
|
175972
|
+
this.chromeProc = proc;
|
|
175973
|
+
const wsUrl = await new Promise((resolve, reject) => {
|
|
175974
|
+
let buffer = "";
|
|
175975
|
+
const timeout = setTimeout(() => {
|
|
175976
|
+
reject(new Error("Timeout waiting for Chrome DevTools WebSocket URL."));
|
|
175977
|
+
}, this.options.timeoutMs || 15000);
|
|
175978
|
+
const onData = (chunk) => {
|
|
175979
|
+
buffer += chunk.toString();
|
|
175980
|
+
const match = buffer.match(/DevTools listening on (ws:\/\/[^\s]+)/);
|
|
175981
|
+
if (match && match[1]) {
|
|
175982
|
+
clearTimeout(timeout);
|
|
175983
|
+
proc.stderr?.off("data", onData);
|
|
175984
|
+
resolve(match[1]);
|
|
175985
|
+
}
|
|
175986
|
+
};
|
|
175987
|
+
proc.stderr?.on("data", onData);
|
|
175988
|
+
proc.on("error", (err) => {
|
|
175989
|
+
clearTimeout(timeout);
|
|
175990
|
+
reject(err);
|
|
175991
|
+
});
|
|
175992
|
+
proc.on("exit", (code) => {
|
|
175993
|
+
clearTimeout(timeout);
|
|
175994
|
+
reject(new Error(`Chrome exited prematurely with code ${code}.`));
|
|
175995
|
+
});
|
|
175996
|
+
});
|
|
175997
|
+
await this.connectWs(wsUrl);
|
|
175998
|
+
const createTargetRes = await this.sendCommand("Target.createTarget", {
|
|
175999
|
+
url: "about:blank"
|
|
176000
|
+
});
|
|
176001
|
+
const targetId = createTargetRes.targetId;
|
|
176002
|
+
const attachRes = await this.sendCommand("Target.attachToTarget", {
|
|
176003
|
+
targetId,
|
|
176004
|
+
flatten: true
|
|
176005
|
+
});
|
|
176006
|
+
this.sessionId = attachRes.sessionId;
|
|
176007
|
+
await this.sendSessionCommand("Page.enable");
|
|
176008
|
+
await this.sendSessionCommand("Runtime.enable");
|
|
176009
|
+
await this.sendSessionCommand("Network.enable");
|
|
176010
|
+
}
|
|
176011
|
+
async connectWs(wsUrl) {
|
|
176012
|
+
return new Promise((resolve, reject) => {
|
|
176013
|
+
const ws = new WebSocket(wsUrl);
|
|
176014
|
+
this.ws = ws;
|
|
176015
|
+
ws.onopen = () => resolve();
|
|
176016
|
+
ws.onerror = (err) => reject(err);
|
|
176017
|
+
ws.onmessage = (event) => {
|
|
176018
|
+
try {
|
|
176019
|
+
const msg = JSON.parse(event.data.toString());
|
|
176020
|
+
if (typeof msg.id === "number") {
|
|
176021
|
+
const pending = this.pendingCommands.get(msg.id);
|
|
176022
|
+
if (pending) {
|
|
176023
|
+
this.pendingCommands.delete(msg.id);
|
|
176024
|
+
if (msg.error) {
|
|
176025
|
+
pending.reject(new Error(msg.error.message || "CDP Error"));
|
|
176026
|
+
} else {
|
|
176027
|
+
pending.resolve(msg.result);
|
|
176028
|
+
}
|
|
176029
|
+
}
|
|
176030
|
+
return;
|
|
176031
|
+
}
|
|
176032
|
+
this.handleEvent(msg);
|
|
176033
|
+
} catch {}
|
|
176034
|
+
};
|
|
176035
|
+
});
|
|
176036
|
+
}
|
|
176037
|
+
handleEvent(msg) {
|
|
176038
|
+
const method = msg.method;
|
|
176039
|
+
if (method === "Page.loadEventFired" || method === "Page.domContentEventFired") {
|
|
176040
|
+
this.isLoaded = true;
|
|
176041
|
+
} else if (method === "Runtime.exceptionThrown") {
|
|
176042
|
+
const details = msg.params?.exceptionDetails;
|
|
176043
|
+
const desc = details?.exception?.description || details?.text || "Unknown runtime exception";
|
|
176044
|
+
this.consoleErrors.push(`Exception: ${desc}`);
|
|
176045
|
+
} else if (method === "Runtime.consoleAPICalled") {
|
|
176046
|
+
const type = msg.params?.type;
|
|
176047
|
+
if (type === "error" || type === "warning") {
|
|
176048
|
+
const args = (msg.params?.args || []).map((a) => a.value ?? a.description ?? "").join(" ");
|
|
176049
|
+
this.consoleErrors.push(`[console.${type}] ${args}`);
|
|
176050
|
+
}
|
|
176051
|
+
} else if (method === "Network.responseReceived") {
|
|
176052
|
+
const res = msg.params?.response;
|
|
176053
|
+
if (res && typeof res.status === "number" && res.status >= 400) {
|
|
176054
|
+
this.networkErrors.push(`HTTP ${res.status} ${res.statusText || ""} - ${res.url}`);
|
|
176055
|
+
}
|
|
176056
|
+
} else if (method === "Network.loadingFailed") {
|
|
176057
|
+
const err = msg.params;
|
|
176058
|
+
if (err) {
|
|
176059
|
+
this.networkErrors.push(`Network failure: ${err.errorText || "Unknown error"} (${err.type || "request"})`);
|
|
176060
|
+
}
|
|
176061
|
+
}
|
|
176062
|
+
}
|
|
176063
|
+
sendCommand(method, params) {
|
|
176064
|
+
return new Promise((resolve, reject) => {
|
|
176065
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
176066
|
+
return reject(new Error("WebSocket is not open"));
|
|
176067
|
+
}
|
|
176068
|
+
const id = this.nextId++;
|
|
176069
|
+
this.pendingCommands.set(id, { resolve, reject });
|
|
176070
|
+
this.ws.send(JSON.stringify({ id, method, params }));
|
|
176071
|
+
});
|
|
176072
|
+
}
|
|
176073
|
+
sendSessionCommand(method, params) {
|
|
176074
|
+
return new Promise((resolve, reject) => {
|
|
176075
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
176076
|
+
return reject(new Error("WebSocket is not open"));
|
|
176077
|
+
}
|
|
176078
|
+
const id = this.nextId++;
|
|
176079
|
+
this.pendingCommands.set(id, { resolve, reject });
|
|
176080
|
+
const payload = { id, method, params };
|
|
176081
|
+
if (this.sessionId) {
|
|
176082
|
+
payload.sessionId = this.sessionId;
|
|
176083
|
+
}
|
|
176084
|
+
this.ws.send(JSON.stringify(payload));
|
|
176085
|
+
});
|
|
176086
|
+
}
|
|
176087
|
+
async navigate(url, timeoutMs = 12000) {
|
|
176088
|
+
this.consoleErrors = [];
|
|
176089
|
+
this.networkErrors = [];
|
|
176090
|
+
this.isLoaded = false;
|
|
176091
|
+
await this.sendSessionCommand("Page.navigate", { url });
|
|
176092
|
+
const startTime = Date.now();
|
|
176093
|
+
while (!this.isLoaded && Date.now() - startTime < timeoutMs) {
|
|
176094
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
176095
|
+
}
|
|
176096
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
176097
|
+
const evalRes = await this.sendSessionCommand("Runtime.evaluate", {
|
|
176098
|
+
expression: '(function() { return document.body ? (document.body.innerText || document.documentElement.innerText || "") : ""; })()',
|
|
176099
|
+
returnByValue: true
|
|
176100
|
+
});
|
|
176101
|
+
const text = evalRes?.result?.value || "";
|
|
176102
|
+
return {
|
|
176103
|
+
text: text.trim(),
|
|
176104
|
+
consoleErrors: [...this.consoleErrors],
|
|
176105
|
+
networkErrors: [...this.networkErrors]
|
|
176106
|
+
};
|
|
176107
|
+
}
|
|
176108
|
+
async click(selector) {
|
|
176109
|
+
const expression = `
|
|
176110
|
+
(function(sel) {
|
|
176111
|
+
var el = document.querySelector(sel);
|
|
176112
|
+
if (!el) {
|
|
176113
|
+
var buttons = Array.from(document.querySelectorAll('button, a, input[type=button], input[type=submit], [role=button]'));
|
|
176114
|
+
el = buttons.find(function(b) {
|
|
176115
|
+
return (b.textContent || '').trim().toLowerCase() === sel.trim().toLowerCase();
|
|
176116
|
+
});
|
|
176117
|
+
}
|
|
176118
|
+
if (!el) {
|
|
176119
|
+
return { success: false, error: 'Could not find element matching: ' + sel };
|
|
176120
|
+
}
|
|
176121
|
+
el.scrollIntoView({ behavior: 'instant', block: 'center' });
|
|
176122
|
+
el.click();
|
|
176123
|
+
return {
|
|
176124
|
+
success: true,
|
|
176125
|
+
tagName: el.tagName,
|
|
176126
|
+
text: (el.textContent || '').trim().slice(0, 50)
|
|
176127
|
+
};
|
|
176128
|
+
})(${JSON.stringify(selector)})
|
|
176129
|
+
`;
|
|
176130
|
+
const evalRes = await this.sendSessionCommand("Runtime.evaluate", {
|
|
176131
|
+
expression,
|
|
176132
|
+
returnByValue: true
|
|
176133
|
+
});
|
|
176134
|
+
const result = evalRes?.result?.value;
|
|
176135
|
+
if (!result || !result.success) {
|
|
176136
|
+
return {
|
|
176137
|
+
success: false,
|
|
176138
|
+
output: "",
|
|
176139
|
+
error: result?.error || `Element "${selector}" not found.`,
|
|
176140
|
+
consoleErrors: [...this.consoleErrors],
|
|
176141
|
+
networkErrors: [...this.networkErrors]
|
|
176142
|
+
};
|
|
176143
|
+
}
|
|
176144
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
176145
|
+
return {
|
|
176146
|
+
success: true,
|
|
176147
|
+
output: `Clicked <${result.tagName}>: "${result.text || selector}"`,
|
|
176148
|
+
consoleErrors: [...this.consoleErrors],
|
|
176149
|
+
networkErrors: [...this.networkErrors]
|
|
176150
|
+
};
|
|
176151
|
+
}
|
|
176152
|
+
async type(selector, text) {
|
|
176153
|
+
const expression = `
|
|
176154
|
+
(function(sel, val) {
|
|
176155
|
+
var el = document.querySelector(sel);
|
|
176156
|
+
if (!el) {
|
|
176157
|
+
var inputs = Array.from(document.querySelectorAll('input, textarea'));
|
|
176158
|
+
el = inputs.find(function(i) {
|
|
176159
|
+
return i.name === sel || i.id === sel || i.placeholder === sel;
|
|
176160
|
+
});
|
|
176161
|
+
}
|
|
176162
|
+
if (!el) {
|
|
176163
|
+
return { success: false, error: 'Could not find input element matching: ' + sel };
|
|
176164
|
+
}
|
|
176165
|
+
el.focus();
|
|
176166
|
+
el.value = val;
|
|
176167
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
176168
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
176169
|
+
return { success: true, tagName: el.tagName, value: el.value };
|
|
176170
|
+
})(${JSON.stringify(selector)}, ${JSON.stringify(text)})
|
|
176171
|
+
`;
|
|
176172
|
+
const evalRes = await this.sendSessionCommand("Runtime.evaluate", {
|
|
176173
|
+
expression,
|
|
176174
|
+
returnByValue: true
|
|
176175
|
+
});
|
|
176176
|
+
const result = evalRes?.result?.value;
|
|
176177
|
+
if (!result || !result.success) {
|
|
176178
|
+
return {
|
|
176179
|
+
success: false,
|
|
176180
|
+
output: "",
|
|
176181
|
+
error: result?.error || `Input element "${selector}" not found.`,
|
|
176182
|
+
consoleErrors: [...this.consoleErrors],
|
|
176183
|
+
networkErrors: [...this.networkErrors]
|
|
176184
|
+
};
|
|
176185
|
+
}
|
|
176186
|
+
return {
|
|
176187
|
+
success: true,
|
|
176188
|
+
output: `Typed "${text}" into <${result.tagName}> (${selector})`,
|
|
176189
|
+
consoleErrors: [...this.consoleErrors],
|
|
176190
|
+
networkErrors: [...this.networkErrors]
|
|
176191
|
+
};
|
|
176192
|
+
}
|
|
176193
|
+
async screenshot(fullPage = false, outputPath) {
|
|
176194
|
+
const params = { format: "png" };
|
|
176195
|
+
if (fullPage) {
|
|
176196
|
+
params.captureBeyondViewport = true;
|
|
176197
|
+
}
|
|
176198
|
+
const res = await this.sendSessionCommand("Page.captureScreenshot", params);
|
|
176199
|
+
const base64Data = res?.data;
|
|
176200
|
+
if (!base64Data) {
|
|
176201
|
+
return {
|
|
176202
|
+
success: false,
|
|
176203
|
+
output: "",
|
|
176204
|
+
error: "Failed to capture screenshot data from Chrome."
|
|
176205
|
+
};
|
|
176206
|
+
}
|
|
176207
|
+
const targetFile = outputPath || path29.join(os15.tmpdir(), `december-screenshot-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.png`);
|
|
176208
|
+
await fs28.writeFile(targetFile, Buffer.from(base64Data, "base64"));
|
|
176209
|
+
return {
|
|
176210
|
+
success: true,
|
|
176211
|
+
output: `Screenshot captured successfully.`,
|
|
176212
|
+
screenshotPath: targetFile,
|
|
176213
|
+
screenshotBase64: base64Data,
|
|
176214
|
+
consoleErrors: [...this.consoleErrors],
|
|
176215
|
+
networkErrors: [...this.networkErrors]
|
|
176216
|
+
};
|
|
176217
|
+
}
|
|
176218
|
+
async getConsoleErrors() {
|
|
176219
|
+
return {
|
|
176220
|
+
consoleErrors: [...this.consoleErrors],
|
|
176221
|
+
networkErrors: [...this.networkErrors]
|
|
176222
|
+
};
|
|
176223
|
+
}
|
|
176224
|
+
async close() {
|
|
176225
|
+
if (this.ws) {
|
|
176226
|
+
try {
|
|
176227
|
+
this.ws.close();
|
|
176228
|
+
} catch {}
|
|
176229
|
+
this.ws = null;
|
|
176230
|
+
}
|
|
176231
|
+
if (this.chromeProc) {
|
|
176232
|
+
try {
|
|
176233
|
+
this.chromeProc.kill("SIGTERM");
|
|
176234
|
+
setTimeout(() => {
|
|
176235
|
+
if (this.chromeProc && !this.chromeProc.killed) {
|
|
176236
|
+
this.chromeProc.kill("SIGKILL");
|
|
176237
|
+
}
|
|
176238
|
+
}, 1000);
|
|
176239
|
+
} catch {}
|
|
176240
|
+
this.chromeProc = null;
|
|
176241
|
+
}
|
|
176242
|
+
if (this.tmpProfileDir) {
|
|
176243
|
+
try {
|
|
176244
|
+
await fs28.rm(this.tmpProfileDir, { recursive: true, force: true });
|
|
176245
|
+
} catch {}
|
|
176246
|
+
this.tmpProfileDir = null;
|
|
176247
|
+
}
|
|
176248
|
+
}
|
|
176249
|
+
}
|
|
176250
|
+
var init_cdp_client = () => {};
|
|
176251
|
+
|
|
176252
|
+
// src/utils/browser/browser-runner.ts
|
|
176253
|
+
class BrowserRunner {
|
|
176254
|
+
activeSession = null;
|
|
176255
|
+
cleanupRegistered = false;
|
|
176256
|
+
registerCleanup() {
|
|
176257
|
+
if (this.cleanupRegistered)
|
|
176258
|
+
return;
|
|
176259
|
+
this.cleanupRegistered = true;
|
|
176260
|
+
const cleanup = () => {
|
|
176261
|
+
if (this.activeSession) {
|
|
176262
|
+
this.activeSession.close().catch(() => {});
|
|
176263
|
+
this.activeSession = null;
|
|
176264
|
+
}
|
|
176265
|
+
};
|
|
176266
|
+
process.once("exit", cleanup);
|
|
176267
|
+
process.once("SIGINT", cleanup);
|
|
176268
|
+
process.once("SIGTERM", cleanup);
|
|
176269
|
+
}
|
|
176270
|
+
async getSession() {
|
|
176271
|
+
if (this.activeSession) {
|
|
176272
|
+
return this.activeSession;
|
|
176273
|
+
}
|
|
176274
|
+
const chromePath = await findSystemChrome();
|
|
176275
|
+
if (!chromePath) {
|
|
176276
|
+
return null;
|
|
176277
|
+
}
|
|
176278
|
+
try {
|
|
176279
|
+
await Promise.resolve().then(() => init_config());
|
|
176280
|
+
const config = await loadConfig().catch(() => null);
|
|
176281
|
+
const hasDisplay = process.platform !== "linux" || Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
176282
|
+
const isVisible = hasDisplay && config?.browserVisible !== false && process.env.HEADED !== "0" && process.env.DECEMBER_HEADED !== "0";
|
|
176283
|
+
const session = new CdpBrowserSession({
|
|
176284
|
+
chromePath,
|
|
176285
|
+
headless: !isVisible
|
|
176286
|
+
});
|
|
176287
|
+
await session.launch();
|
|
176288
|
+
this.activeSession = session;
|
|
176289
|
+
this.registerCleanup();
|
|
176290
|
+
return session;
|
|
176291
|
+
} catch {
|
|
176292
|
+
return null;
|
|
176293
|
+
}
|
|
176294
|
+
}
|
|
176295
|
+
async staticFallbackFetch(url) {
|
|
176296
|
+
try {
|
|
176297
|
+
const res = await fetch(url, {
|
|
176298
|
+
headers: {
|
|
176299
|
+
"User-Agent": "Mozilla/5.0 (compatible; DecemberAgent/1.0)"
|
|
176300
|
+
}
|
|
176301
|
+
});
|
|
176302
|
+
const html = await res.text();
|
|
176303
|
+
if (!res.ok) {
|
|
176304
|
+
return {
|
|
176305
|
+
text: "",
|
|
176306
|
+
error: `HTTP Error (${res.status}): ${html}`,
|
|
176307
|
+
networkErrors: [`HTTP ${res.status} - ${url}`]
|
|
176308
|
+
};
|
|
176309
|
+
}
|
|
176310
|
+
const cleanText = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
176311
|
+
const notice = `
|
|
176312
|
+
|
|
176313
|
+
[Note: Local Chrome/Chromium not detected. Displaying static HTML text. Install Chrome or Chromium for client-side JavaScript rendering and interactive actions.]`;
|
|
176314
|
+
return {
|
|
176315
|
+
text: cleanText + notice,
|
|
176316
|
+
consoleErrors: [],
|
|
176317
|
+
networkErrors: []
|
|
176318
|
+
};
|
|
176319
|
+
} catch (err) {
|
|
176320
|
+
return {
|
|
176321
|
+
text: "",
|
|
176322
|
+
error: err.message,
|
|
176323
|
+
networkErrors: [`Network request failed: ${err.message}`]
|
|
176324
|
+
};
|
|
176325
|
+
}
|
|
176326
|
+
}
|
|
176327
|
+
navigate = async (url) => {
|
|
176328
|
+
const session = await this.getSession();
|
|
176329
|
+
if (session) {
|
|
176330
|
+
try {
|
|
176331
|
+
return await session.navigate(url);
|
|
176332
|
+
} catch {}
|
|
176333
|
+
}
|
|
176334
|
+
return this.staticFallbackFetch(url);
|
|
176335
|
+
};
|
|
176336
|
+
action = async (params) => {
|
|
176337
|
+
const { action, url, selector, text, fullPage } = params;
|
|
176338
|
+
if (action === "navigate") {
|
|
176339
|
+
if (!url) {
|
|
176340
|
+
return {
|
|
176341
|
+
success: false,
|
|
176342
|
+
action: "navigate",
|
|
176343
|
+
error: 'Action "navigate" requires a valid "url" parameter.'
|
|
176344
|
+
};
|
|
176345
|
+
}
|
|
176346
|
+
const navResult = await this.navigate(url);
|
|
176347
|
+
if (navResult.error) {
|
|
176348
|
+
return {
|
|
176349
|
+
success: false,
|
|
176350
|
+
action: "navigate",
|
|
176351
|
+
error: navResult.error,
|
|
176352
|
+
networkErrors: navResult.networkErrors
|
|
176353
|
+
};
|
|
176354
|
+
}
|
|
176355
|
+
return {
|
|
176356
|
+
success: true,
|
|
176357
|
+
action: "navigate",
|
|
176358
|
+
text: navResult.text,
|
|
176359
|
+
consoleErrors: navResult.consoleErrors,
|
|
176360
|
+
networkErrors: navResult.networkErrors
|
|
176361
|
+
};
|
|
176362
|
+
}
|
|
176363
|
+
if (action === "get_console_errors") {
|
|
176364
|
+
if (this.activeSession) {
|
|
176365
|
+
const errors = await this.activeSession.getConsoleErrors();
|
|
176366
|
+
const count = errors.consoleErrors.length + errors.networkErrors.length;
|
|
176367
|
+
return {
|
|
176368
|
+
success: true,
|
|
176369
|
+
action: "get_console_errors",
|
|
176370
|
+
output: count > 0 ? `Captured ${errors.consoleErrors.length} console error(s) and ${errors.networkErrors.length} network error(s).` : "No console or network errors detected.",
|
|
176371
|
+
consoleErrors: errors.consoleErrors,
|
|
176372
|
+
networkErrors: errors.networkErrors
|
|
176373
|
+
};
|
|
176374
|
+
}
|
|
176375
|
+
return {
|
|
176376
|
+
success: true,
|
|
176377
|
+
action: "get_console_errors",
|
|
176378
|
+
output: "No active browser session running."
|
|
176379
|
+
};
|
|
176380
|
+
}
|
|
176381
|
+
const session = await this.getSession();
|
|
176382
|
+
if (!session) {
|
|
176383
|
+
return {
|
|
176384
|
+
success: false,
|
|
176385
|
+
action,
|
|
176386
|
+
error: `Interactive browser action "${action}" requires Google Chrome or Chromium. Please install Chrome or Chromium, or set the CHROME_PATH environment variable.`
|
|
176387
|
+
};
|
|
176388
|
+
}
|
|
176389
|
+
switch (action) {
|
|
176390
|
+
case "click": {
|
|
176391
|
+
if (!selector) {
|
|
176392
|
+
return {
|
|
176393
|
+
success: false,
|
|
176394
|
+
action: "click",
|
|
176395
|
+
error: 'Action "click" requires a "selector" parameter.'
|
|
176396
|
+
};
|
|
176397
|
+
}
|
|
176398
|
+
const res = await session.click(selector);
|
|
176399
|
+
return {
|
|
176400
|
+
success: res.success,
|
|
176401
|
+
action: "click",
|
|
176402
|
+
output: res.output,
|
|
176403
|
+
error: res.error,
|
|
176404
|
+
consoleErrors: res.consoleErrors,
|
|
176405
|
+
networkErrors: res.networkErrors
|
|
176406
|
+
};
|
|
176407
|
+
}
|
|
176408
|
+
case "type": {
|
|
176409
|
+
if (!selector) {
|
|
176410
|
+
return {
|
|
176411
|
+
success: false,
|
|
176412
|
+
action: "type",
|
|
176413
|
+
error: 'Action "type" requires a "selector" parameter.'
|
|
176414
|
+
};
|
|
176415
|
+
}
|
|
176416
|
+
const res = await session.type(selector, text || "");
|
|
176417
|
+
return {
|
|
176418
|
+
success: res.success,
|
|
176419
|
+
action: "type",
|
|
176420
|
+
output: res.output,
|
|
176421
|
+
error: res.error,
|
|
176422
|
+
consoleErrors: res.consoleErrors,
|
|
176423
|
+
networkErrors: res.networkErrors
|
|
176424
|
+
};
|
|
176425
|
+
}
|
|
176426
|
+
case "screenshot": {
|
|
176427
|
+
const res = await session.screenshot(Boolean(fullPage));
|
|
176428
|
+
return {
|
|
176429
|
+
success: res.success,
|
|
176430
|
+
action: "screenshot",
|
|
176431
|
+
output: res.output,
|
|
176432
|
+
screenshotPath: res.screenshotPath,
|
|
176433
|
+
screenshotBase64: res.screenshotBase64,
|
|
176434
|
+
error: res.error,
|
|
176435
|
+
consoleErrors: res.consoleErrors,
|
|
176436
|
+
networkErrors: res.networkErrors
|
|
176437
|
+
};
|
|
176438
|
+
}
|
|
176439
|
+
default:
|
|
176440
|
+
return {
|
|
176441
|
+
success: false,
|
|
176442
|
+
action,
|
|
176443
|
+
error: `Unsupported browser action: ${action}`
|
|
176444
|
+
};
|
|
176445
|
+
}
|
|
176446
|
+
};
|
|
176447
|
+
close = async () => {
|
|
176448
|
+
if (this.activeSession) {
|
|
176449
|
+
await this.activeSession.close();
|
|
176450
|
+
this.activeSession = null;
|
|
176451
|
+
}
|
|
176452
|
+
};
|
|
176453
|
+
}
|
|
176454
|
+
var browserRunner;
|
|
176455
|
+
var init_browser_runner = __esm(() => {
|
|
176456
|
+
init_cdp_client();
|
|
176457
|
+
init_detector();
|
|
176458
|
+
browserRunner = new BrowserRunner;
|
|
176459
|
+
});
|
|
176460
|
+
|
|
176461
|
+
// src/utils/browser/index.ts
|
|
176462
|
+
var init_browser2 = __esm(() => {
|
|
176463
|
+
init_detector();
|
|
176464
|
+
init_cdp_client();
|
|
176465
|
+
init_browser_runner();
|
|
176466
|
+
});
|
|
176467
|
+
|
|
175521
176468
|
// src/local-operations.ts
|
|
175522
176469
|
var exports_local_operations = {};
|
|
175523
176470
|
__export(exports_local_operations, {
|
|
@@ -175525,22 +176472,22 @@ __export(exports_local_operations, {
|
|
|
175525
176472
|
localOperations: () => localOperations,
|
|
175526
176473
|
setActiveScopeDir: () => setActiveScopeDir
|
|
175527
176474
|
});
|
|
175528
|
-
import { exec as exec4, spawn as
|
|
176475
|
+
import { exec as exec4, spawn as spawn5 } from "node:child_process";
|
|
175529
176476
|
import nodeFs from "node:fs";
|
|
175530
|
-
import
|
|
175531
|
-
import
|
|
176477
|
+
import fs29 from "node:fs/promises";
|
|
176478
|
+
import path30 from "node:path";
|
|
175532
176479
|
import { promisify as promisify4 } from "node:util";
|
|
175533
176480
|
function setActiveScopeDir(dir) {
|
|
175534
176481
|
activeScopeDir = dir;
|
|
175535
176482
|
}
|
|
175536
176483
|
function getScopedCwd() {
|
|
175537
176484
|
if (activeScopeDir) {
|
|
175538
|
-
return
|
|
176485
|
+
return path30.resolve(process.cwd(), activeScopeDir);
|
|
175539
176486
|
}
|
|
175540
176487
|
return process.cwd();
|
|
175541
176488
|
}
|
|
175542
176489
|
function isTypeScriptProject(workspaceDir) {
|
|
175543
|
-
return nodeFs.existsSync(
|
|
176490
|
+
return nodeFs.existsSync(path30.join(workspaceDir, "tsconfig.json")) || nodeFs.existsSync(path30.join(workspaceDir, "package.json"));
|
|
175544
176491
|
}
|
|
175545
176492
|
async function getOrLaunchLspClient(customWorkspaceDir) {
|
|
175546
176493
|
const workspaceDir = customWorkspaceDir || getScopedCwd();
|
|
@@ -175575,6 +176522,7 @@ var init_local_operations = __esm(() => {
|
|
|
175575
176522
|
init_src();
|
|
175576
176523
|
init_src4();
|
|
175577
176524
|
init_task_manager();
|
|
176525
|
+
init_browser2();
|
|
175578
176526
|
import_fast_glob = __toESM(require_out4(), 1);
|
|
175579
176527
|
execAsync2 = promisify4(exec4);
|
|
175580
176528
|
localBashOps = createLocalBashOperations();
|
|
@@ -175587,7 +176535,7 @@ var init_local_operations = __esm(() => {
|
|
|
175587
176535
|
const fallbackTimeoutMs = actualOptions.waitMsBeforeAsync ?? DEFAULT_BG_TIMEOUT_MS;
|
|
175588
176536
|
const serverDelayMs = actualOptions.serverReadyDelayMs ?? DEFAULT_SERVER_READY_DELAY_MS;
|
|
175589
176537
|
return new Promise((resolve, reject) => {
|
|
175590
|
-
const child =
|
|
176538
|
+
const child = spawn5(command, {
|
|
175591
176539
|
cwd: targetCwd,
|
|
175592
176540
|
detached: process.platform !== "win32",
|
|
175593
176541
|
env: actualOptions.env ?? process.env,
|
|
@@ -175717,19 +176665,19 @@ Failed to start process: ${err?.message || err}
|
|
|
175717
176665
|
fs: {
|
|
175718
176666
|
readFile: async (filepath) => {
|
|
175719
176667
|
const root = getScopedCwd();
|
|
175720
|
-
const absolutePath =
|
|
175721
|
-
return
|
|
176668
|
+
const absolutePath = path30.isAbsolute(filepath) ? filepath : path30.resolve(root, filepath);
|
|
176669
|
+
return fs29.readFile(absolutePath, "utf8");
|
|
175722
176670
|
},
|
|
175723
176671
|
writeFile: async (filepath, content) => {
|
|
175724
176672
|
const root = getScopedCwd();
|
|
175725
|
-
const absolutePath =
|
|
175726
|
-
await
|
|
175727
|
-
await
|
|
176673
|
+
const absolutePath = path30.isAbsolute(filepath) ? filepath : path30.resolve(root, filepath);
|
|
176674
|
+
await fs29.mkdir(path30.dirname(absolutePath), { recursive: true });
|
|
176675
|
+
await fs29.writeFile(absolutePath, content, "utf8");
|
|
175728
176676
|
},
|
|
175729
176677
|
readdir: async (dirPath) => {
|
|
175730
176678
|
const root = getScopedCwd();
|
|
175731
|
-
const absolutePath =
|
|
175732
|
-
const entries = await
|
|
176679
|
+
const absolutePath = path30.isAbsolute(dirPath) ? dirPath : path30.resolve(root, dirPath);
|
|
176680
|
+
const entries = await fs29.readdir(absolutePath, { withFileTypes: true });
|
|
175733
176681
|
const ignores = getWorkspaceIgnores(root);
|
|
175734
176682
|
return entries.filter((entry) => !isPathIgnored(entry.name, ignores)).map((entry) => {
|
|
175735
176683
|
const type = entry.isDirectory() ? "DIR " : "FILE";
|
|
@@ -175738,14 +176686,14 @@ Failed to start process: ${err?.message || err}
|
|
|
175738
176686
|
},
|
|
175739
176687
|
mkdir: async (dirPath, options) => {
|
|
175740
176688
|
const root = getScopedCwd();
|
|
175741
|
-
const absolutePath =
|
|
175742
|
-
await
|
|
176689
|
+
const absolutePath = path30.isAbsolute(dirPath) ? dirPath : path30.resolve(root, dirPath);
|
|
176690
|
+
await fs29.mkdir(absolutePath, options);
|
|
175743
176691
|
},
|
|
175744
176692
|
exists: async (filepath) => {
|
|
175745
176693
|
const root = getScopedCwd();
|
|
175746
|
-
const absolutePath =
|
|
176694
|
+
const absolutePath = path30.isAbsolute(filepath) ? filepath : path30.resolve(root, filepath);
|
|
175747
176695
|
try {
|
|
175748
|
-
await
|
|
176696
|
+
await fs29.access(absolutePath);
|
|
175749
176697
|
return true;
|
|
175750
176698
|
} catch {
|
|
175751
176699
|
return false;
|
|
@@ -175755,7 +176703,7 @@ Failed to start process: ${err?.message || err}
|
|
|
175755
176703
|
search: {
|
|
175756
176704
|
find: async (dirPath, query) => {
|
|
175757
176705
|
const root = getScopedCwd();
|
|
175758
|
-
const targetDir =
|
|
176706
|
+
const targetDir = path30.isAbsolute(dirPath) ? dirPath : path30.resolve(root, dirPath);
|
|
175759
176707
|
const ignores = getWorkspaceIgnores(root);
|
|
175760
176708
|
const files = await import_fast_glob.default([query], {
|
|
175761
176709
|
cwd: targetDir,
|
|
@@ -175767,7 +176715,7 @@ Failed to start process: ${err?.message || err}
|
|
|
175767
176715
|
},
|
|
175768
176716
|
grep: async (dirPath, query) => {
|
|
175769
176717
|
const root = getScopedCwd();
|
|
175770
|
-
const targetDir =
|
|
176718
|
+
const targetDir = path30.isAbsolute(dirPath) ? dirPath : path30.resolve(root, dirPath);
|
|
175771
176719
|
const ignores = getWorkspaceIgnores(root);
|
|
175772
176720
|
try {
|
|
175773
176721
|
const escapedQuery = query.replace(/"/g, "\\\"");
|
|
@@ -175781,7 +176729,7 @@ Failed to start process: ${err?.message || err}
|
|
|
175781
176729
|
if (!line.trim())
|
|
175782
176730
|
return false;
|
|
175783
176731
|
const rawPath = line.split(":")[0];
|
|
175784
|
-
const relPath =
|
|
176732
|
+
const relPath = path30.isAbsolute(rawPath) ? path30.relative(root, rawPath) : rawPath;
|
|
175785
176733
|
return !isPathIgnored(relPath, ignores);
|
|
175786
176734
|
});
|
|
175787
176735
|
return filtered.join(`
|
|
@@ -175806,23 +176754,9 @@ Failed to start process: ${err?.message || err}
|
|
|
175806
176754
|
}
|
|
175807
176755
|
},
|
|
175808
176756
|
browser: {
|
|
175809
|
-
navigate: async (url) =>
|
|
175810
|
-
|
|
175811
|
-
|
|
175812
|
-
headers: {
|
|
175813
|
-
"User-Agent": "Mozilla/5.0 (compatible; DecemberAgent/1.0)"
|
|
175814
|
-
}
|
|
175815
|
-
});
|
|
175816
|
-
const html = await res.text();
|
|
175817
|
-
if (!res.ok) {
|
|
175818
|
-
return { text: "", error: `HTTP Error (${res.status}): ${html}` };
|
|
175819
|
-
}
|
|
175820
|
-
const cleanText = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
175821
|
-
return { text: cleanText };
|
|
175822
|
-
} catch (error) {
|
|
175823
|
-
return { text: "", error: error.message };
|
|
175824
|
-
}
|
|
175825
|
-
}
|
|
176757
|
+
navigate: async (url) => browserRunner.navigate(url),
|
|
176758
|
+
action: async (params) => browserRunner.action(params),
|
|
176759
|
+
close: async () => browserRunner.close()
|
|
175826
176760
|
},
|
|
175827
176761
|
diagnostics: {
|
|
175828
176762
|
getDiagnostics: async (filePath) => {
|
|
@@ -177515,7 +178449,7 @@ var init_environment = __esm(() => {
|
|
|
177515
178449
|
|
|
177516
178450
|
// ../../node_modules/.bun/ansi-escapes@7.3.0/node_modules/ansi-escapes/base.js
|
|
177517
178451
|
import process2 from "node:process";
|
|
177518
|
-
import
|
|
178452
|
+
import os16 from "node:os";
|
|
177519
178453
|
var ESC = "\x1B[", SEP = ";", isTerminalApp, isWindows2, isTmux, cwdFunction, cursorTo = (x, y) => {
|
|
177520
178454
|
if (typeof x !== "number") {
|
|
177521
178455
|
throw new TypeError("The `x` argument is required");
|
|
@@ -177537,7 +178471,7 @@ var ESC = "\x1B[", SEP = ";", isTerminalApp, isWindows2, isTmux, cwdFunction, cu
|
|
|
177537
178471
|
if (isBrowser || !isWindows2) {
|
|
177538
178472
|
return false;
|
|
177539
178473
|
}
|
|
177540
|
-
const parts =
|
|
178474
|
+
const parts = os16.release().split(".");
|
|
177541
178475
|
const major = Number(parts[0]);
|
|
177542
178476
|
const build = Number(parts[2] ?? 0);
|
|
177543
178477
|
if (major < 10) {
|
|
@@ -180224,7 +181158,7 @@ var init_wrap_ansi = __esm(() => {
|
|
|
180224
181158
|
// ../../node_modules/.bun/terminal-size@4.0.1/node_modules/terminal-size/index.js
|
|
180225
181159
|
import process3 from "node:process";
|
|
180226
181160
|
import { execFileSync } from "node:child_process";
|
|
180227
|
-
import
|
|
181161
|
+
import fs30 from "node:fs";
|
|
180228
181162
|
import tty from "node:tty";
|
|
180229
181163
|
function terminalSize() {
|
|
180230
181164
|
const { env, stdout, stderr } = process3;
|
|
@@ -180272,7 +181206,7 @@ var defaultColumns = 80, defaultRows = 24, exec6 = (command, arguments_, { shell
|
|
|
180272
181206
|
return true;
|
|
180273
181207
|
}
|
|
180274
181208
|
try {
|
|
180275
|
-
const statContents =
|
|
181209
|
+
const statContents = fs30.readFileSync("/proc/self/stat", "utf8");
|
|
180276
181210
|
const closingParenthesisIndex = statContents.lastIndexOf(") ");
|
|
180277
181211
|
if (closingParenthesisIndex === -1) {
|
|
180278
181212
|
return false;
|
|
@@ -180292,8 +181226,8 @@ var defaultColumns = 80, defaultRows = 24, exec6 = (command, arguments_, { shell
|
|
|
180292
181226
|
}
|
|
180293
181227
|
}, devTty = () => {
|
|
180294
181228
|
try {
|
|
180295
|
-
const flags = process3.platform === "darwin" ?
|
|
180296
|
-
const { columns, rows } = tty.WriteStream(
|
|
181229
|
+
const flags = process3.platform === "darwin" ? fs30.constants.O_EVTONLY | fs30.constants.O_NONBLOCK : fs30.constants.O_NONBLOCK;
|
|
181230
|
+
const { columns, rows } = tty.WriteStream(fs30.openSync("/dev/tty", flags));
|
|
180297
181231
|
return { columns, rows };
|
|
180298
181232
|
} catch {}
|
|
180299
181233
|
}, tput = () => {
|
|
@@ -210285,7 +211219,7 @@ var init_ansi_styles2 = __esm(() => {
|
|
|
210285
211219
|
|
|
210286
211220
|
// ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
|
|
210287
211221
|
import process5 from "node:process";
|
|
210288
|
-
import
|
|
211222
|
+
import os17 from "node:os";
|
|
210289
211223
|
import tty2 from "node:tty";
|
|
210290
211224
|
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process5.argv) {
|
|
210291
211225
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
@@ -210343,7 +211277,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
210343
211277
|
return min;
|
|
210344
211278
|
}
|
|
210345
211279
|
if (process5.platform === "win32") {
|
|
210346
|
-
const osRelease =
|
|
211280
|
+
const osRelease = os17.release().split(".");
|
|
210347
211281
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
210348
211282
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
210349
211283
|
}
|
|
@@ -212495,7 +213429,7 @@ var init_Text = __esm(() => {
|
|
|
212495
213429
|
});
|
|
212496
213430
|
|
|
212497
213431
|
// ../../node_modules/.bun/ink@7.1.0+90ffd46201e3176f/node_modules/ink/build/components/ErrorOverview.js
|
|
212498
|
-
import * as
|
|
213432
|
+
import * as fs31 from "node:fs";
|
|
212499
213433
|
import { cwd } from "node:process";
|
|
212500
213434
|
function ErrorOverview({ error }) {
|
|
212501
213435
|
const stack = error.stack ? error.stack.split(`
|
|
@@ -212505,8 +213439,8 @@ function ErrorOverview({ error }) {
|
|
|
212505
213439
|
let excerpt;
|
|
212506
213440
|
let lineWidth = 0;
|
|
212507
213441
|
const stackLineCounts = new Map;
|
|
212508
|
-
if (filePath && origin?.line &&
|
|
212509
|
-
const sourceCode =
|
|
213442
|
+
if (filePath && origin?.line && fs31.existsSync(filePath)) {
|
|
213443
|
+
const sourceCode = fs31.readFileSync(filePath, "utf8");
|
|
212510
213444
|
excerpt = dist_default4(sourceCode, origin.line);
|
|
212511
213445
|
if (excerpt) {
|
|
212512
213446
|
for (const { line } of excerpt) {
|
|
@@ -213937,7 +214871,7 @@ var init_render_to_string = __esm(async () => {
|
|
|
213937
214871
|
});
|
|
213938
214872
|
|
|
213939
214873
|
// ../../node_modules/.bun/ink@7.1.0+90ffd46201e3176f/node_modules/ink/build/components/Static.js
|
|
213940
|
-
function
|
|
214874
|
+
function Static16(props) {
|
|
213941
214875
|
const { items, children: render, style: customStyle } = props;
|
|
213942
214876
|
const [index, setIndex] = import_react17.useState(0);
|
|
213943
214877
|
const itemsToRender = import_react17.useMemo(() => {
|
|
@@ -215180,9 +216114,9 @@ var init_paste_manager = __esm(() => {
|
|
|
215180
216114
|
});
|
|
215181
216115
|
|
|
215182
216116
|
// ../../packages/tui/src/utils/prompt-history.ts
|
|
215183
|
-
import
|
|
215184
|
-
import
|
|
215185
|
-
import
|
|
216117
|
+
import fs32 from "node:fs";
|
|
216118
|
+
import os18 from "node:os";
|
|
216119
|
+
import path31 from "node:path";
|
|
215186
216120
|
|
|
215187
216121
|
class PromptHistory {
|
|
215188
216122
|
historyPath;
|
|
@@ -215193,17 +216127,17 @@ class PromptHistory {
|
|
|
215193
216127
|
if (customPath) {
|
|
215194
216128
|
this.historyPath = customPath;
|
|
215195
216129
|
} else {
|
|
215196
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
215197
|
-
const canonicalPath =
|
|
215198
|
-
const legacyPath =
|
|
215199
|
-
this.historyPath =
|
|
216130
|
+
const home = process.env.HOME || process.env.USERPROFILE || os18.homedir();
|
|
216131
|
+
const canonicalPath = path31.join(home, ".december", "history");
|
|
216132
|
+
const legacyPath = path31.join(home, ".config", "december", "history");
|
|
216133
|
+
this.historyPath = fs32.existsSync(canonicalPath) || !fs32.existsSync(legacyPath) ? canonicalPath : legacyPath;
|
|
215200
216134
|
}
|
|
215201
216135
|
this.load();
|
|
215202
216136
|
}
|
|
215203
216137
|
load() {
|
|
215204
216138
|
try {
|
|
215205
|
-
if (
|
|
215206
|
-
const raw =
|
|
216139
|
+
if (fs32.existsSync(this.historyPath)) {
|
|
216140
|
+
const raw = fs32.readFileSync(this.historyPath, "utf8");
|
|
215207
216141
|
this.entries = raw.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
215208
216142
|
}
|
|
215209
216143
|
} catch {
|
|
@@ -215219,11 +216153,11 @@ class PromptHistory {
|
|
|
215219
216153
|
this.cursor = this.entries.length;
|
|
215220
216154
|
this.currentDraft = "";
|
|
215221
216155
|
try {
|
|
215222
|
-
const dir =
|
|
215223
|
-
if (!
|
|
215224
|
-
|
|
216156
|
+
const dir = path31.dirname(this.historyPath);
|
|
216157
|
+
if (!fs32.existsSync(dir)) {
|
|
216158
|
+
fs32.mkdirSync(dir, { recursive: true });
|
|
215225
216159
|
}
|
|
215226
|
-
|
|
216160
|
+
fs32.appendFileSync(this.historyPath, trimmed + `
|
|
215227
216161
|
`, "utf8");
|
|
215228
216162
|
} catch {}
|
|
215229
216163
|
}
|
|
@@ -215262,11 +216196,11 @@ var init_prompt_history = __esm(() => {
|
|
|
215262
216196
|
|
|
215263
216197
|
// ../../packages/tui/src/utils/clipboard.ts
|
|
215264
216198
|
import { spawnSync } from "child_process";
|
|
215265
|
-
import * as
|
|
216199
|
+
import * as os19 from "os";
|
|
215266
216200
|
function writeToClipboard(text) {
|
|
215267
|
-
const platform =
|
|
216201
|
+
const platform = os19.platform();
|
|
215268
216202
|
try {
|
|
215269
|
-
const isWsl = platform === "linux" && (!!process.env.WSL_DISTRO_NAME ||
|
|
216203
|
+
const isWsl = platform === "linux" && (!!process.env.WSL_DISTRO_NAME || os19.release().toLowerCase().includes("microsoft"));
|
|
215270
216204
|
if (platform === "darwin") {
|
|
215271
216205
|
spawnSync("pbcopy", { input: text });
|
|
215272
216206
|
} else if (platform === "win32") {
|
|
@@ -215287,16 +216221,16 @@ var init_clipboard = () => {};
|
|
|
215287
216221
|
|
|
215288
216222
|
// ../../packages/tui/src/utils/handoff.ts
|
|
215289
216223
|
import { exec as exec7 } from "node:child_process";
|
|
215290
|
-
import
|
|
215291
|
-
import
|
|
216224
|
+
import fs33 from "node:fs";
|
|
216225
|
+
import path32 from "node:path";
|
|
215292
216226
|
import { promisify as promisify5 } from "node:util";
|
|
215293
216227
|
function getHandoffExcludes(baseDir = process.cwd()) {
|
|
215294
216228
|
const excludes = [...MANDATORY_HANDOFF_EXCLUDES];
|
|
215295
216229
|
const readIgnoreFile = (fileName) => {
|
|
215296
|
-
const filePath =
|
|
216230
|
+
const filePath = path32.join(baseDir, fileName);
|
|
215297
216231
|
try {
|
|
215298
|
-
if (
|
|
215299
|
-
const lines =
|
|
216232
|
+
if (fs33.existsSync(filePath)) {
|
|
216233
|
+
const lines = fs33.readFileSync(filePath, "utf8").split(`
|
|
215300
216234
|
`);
|
|
215301
216235
|
for (const line of lines) {
|
|
215302
216236
|
const trimmed = line.trim();
|
|
@@ -215365,9 +216299,9 @@ var init_handoff = __esm(() => {
|
|
|
215365
216299
|
});
|
|
215366
216300
|
|
|
215367
216301
|
// ../../packages/tui/src/components/command-menu/commands.tsx
|
|
215368
|
-
import
|
|
215369
|
-
import
|
|
215370
|
-
import
|
|
216302
|
+
import fs34 from "node:fs";
|
|
216303
|
+
import os20 from "node:os";
|
|
216304
|
+
import path33 from "node:path";
|
|
215371
216305
|
var COMMANDS;
|
|
215372
216306
|
var init_commands2 = __esm(() => {
|
|
215373
216307
|
init_clipboard();
|
|
@@ -215470,14 +216404,14 @@ var init_commands2 = __esm(() => {
|
|
|
215470
216404
|
action: async (ctx) => {
|
|
215471
216405
|
const archivePath = ".december-handoff.tar.gz";
|
|
215472
216406
|
try {
|
|
215473
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
215474
|
-
const canonicalConfigPath =
|
|
215475
|
-
const legacyConfigPath =
|
|
215476
|
-
const configPath =
|
|
216407
|
+
const home = process.env.HOME || process.env.USERPROFILE || os20.homedir();
|
|
216408
|
+
const canonicalConfigPath = path33.join(home, ".december", "config.json");
|
|
216409
|
+
const legacyConfigPath = path33.join(home, ".config", "december", "config.json");
|
|
216410
|
+
const configPath = fs34.existsSync(canonicalConfigPath) || !fs34.existsSync(legacyConfigPath) ? canonicalConfigPath : legacyConfigPath;
|
|
215477
216411
|
let config = {};
|
|
215478
216412
|
try {
|
|
215479
|
-
if (
|
|
215480
|
-
config = JSON.parse(
|
|
216413
|
+
if (fs34.existsSync(configPath)) {
|
|
216414
|
+
config = JSON.parse(fs34.readFileSync(configPath, "utf8"));
|
|
215481
216415
|
}
|
|
215482
216416
|
} catch {}
|
|
215483
216417
|
if (!config.decemberToken) {
|
|
@@ -215512,7 +216446,7 @@ var init_commands2 = __esm(() => {
|
|
|
215512
216446
|
headers: { Authorization: `Bearer ${config.decemberToken}` }
|
|
215513
216447
|
});
|
|
215514
216448
|
if (!urlRes.ok) {
|
|
215515
|
-
await
|
|
216449
|
+
await fs34.promises.unlink(archivePath).catch(() => {});
|
|
215516
216450
|
if (urlRes.status === 401) {
|
|
215517
216451
|
ctx.toast.show({
|
|
215518
216452
|
variant: "error",
|
|
@@ -215533,7 +216467,7 @@ var init_commands2 = __esm(() => {
|
|
|
215533
216467
|
const urlJson = await urlRes.json();
|
|
215534
216468
|
const { uploadUrl, objectKey } = urlJson.data || urlJson;
|
|
215535
216469
|
ctx.toast.show({ variant: "info", message: "Uploading to MinIO..." });
|
|
215536
|
-
const fileData = await
|
|
216470
|
+
const fileData = await fs34.promises.readFile(archivePath);
|
|
215537
216471
|
const uploadRes = await fetch(uploadUrl, {
|
|
215538
216472
|
method: "PUT",
|
|
215539
216473
|
body: fileData
|
|
@@ -215557,11 +216491,11 @@ var init_commands2 = __esm(() => {
|
|
|
215557
216491
|
const errJson = await sessionRes.json().catch(() => ({}));
|
|
215558
216492
|
throw new Error(errJson.message || await sessionRes.text());
|
|
215559
216493
|
}
|
|
215560
|
-
await
|
|
216494
|
+
await fs34.promises.unlink(archivePath).catch(() => {});
|
|
215561
216495
|
ctx.toast.show({ variant: "success", message: "Handoff complete! Exiting in 3s." });
|
|
215562
216496
|
setTimeout(() => ctx.exit(), 3000);
|
|
215563
216497
|
} catch (e) {
|
|
215564
|
-
await
|
|
216498
|
+
await fs34.promises.unlink(archivePath).catch(() => {});
|
|
215565
216499
|
ctx.toast.show({ variant: "error", message: `Handoff failed: ${e.message}` });
|
|
215566
216500
|
}
|
|
215567
216501
|
}
|
|
@@ -215633,6 +216567,12 @@ var init_commands2 = __esm(() => {
|
|
|
215633
216567
|
description: "View quota usage",
|
|
215634
216568
|
value: "/usage",
|
|
215635
216569
|
action: (ctx) => {}
|
|
216570
|
+
},
|
|
216571
|
+
{
|
|
216572
|
+
name: "voice",
|
|
216573
|
+
description: "Dictate a prompt using your microphone (Deepgram)",
|
|
216574
|
+
value: "/voice",
|
|
216575
|
+
action: (ctx) => {}
|
|
215636
216576
|
}
|
|
215637
216577
|
];
|
|
215638
216578
|
});
|
|
@@ -216065,6 +217005,7 @@ function TextArea({
|
|
|
216065
217005
|
onScrollUp,
|
|
216066
217006
|
onScrollDown,
|
|
216067
217007
|
placeholder = "",
|
|
217008
|
+
placeholderColor,
|
|
216068
217009
|
focus = true,
|
|
216069
217010
|
disableHistoryNav = false
|
|
216070
217011
|
}) {
|
|
@@ -216318,7 +217259,7 @@ function TextArea({
|
|
|
216318
217259
|
});
|
|
216319
217260
|
if (!value && placeholder) {
|
|
216320
217261
|
return /* @__PURE__ */ jsx_runtime7.jsxs(Text3, {
|
|
216321
|
-
color: THEME.colors.subtle,
|
|
217262
|
+
color: placeholderColor || THEME.colors.subtle,
|
|
216322
217263
|
wrap: "wrap",
|
|
216323
217264
|
children: [
|
|
216324
217265
|
focus ? /* @__PURE__ */ jsx_runtime7.jsx(Text3, {
|
|
@@ -216543,9 +217484,13 @@ var init_input_bar = __esm(async () => {
|
|
|
216543
217484
|
queuedPrompts,
|
|
216544
217485
|
tasks,
|
|
216545
217486
|
onScrollUp,
|
|
216546
|
-
onScrollDown
|
|
217487
|
+
onScrollDown,
|
|
217488
|
+
onStartVoice
|
|
216547
217489
|
}) {
|
|
216548
217490
|
const [value, setValue] = import_react42.useState("");
|
|
217491
|
+
const [isVoiceActive, setIsVoiceActive] = import_react42.useState(false);
|
|
217492
|
+
const [isTranscribing, setIsTranscribing] = import_react42.useState(false);
|
|
217493
|
+
const voiceSessionRef = import_react42.useRef(null);
|
|
216549
217494
|
const toast = useToast();
|
|
216550
217495
|
const dialog = useDialog();
|
|
216551
217496
|
const columns = useTerminalColumns();
|
|
@@ -216553,6 +217498,71 @@ var init_input_bar = __esm(async () => {
|
|
|
216553
217498
|
const [allWorkspaceFiles, setAllWorkspaceFiles] = import_react42.useState([]);
|
|
216554
217499
|
const activeToast = toasts && toasts.length > 0 ? toasts[toasts.length - 1] : toast.currentToast ? { message: toast.currentToast.message, variant: toast.currentToast.variant } : null;
|
|
216555
217500
|
const handleContentChangeRef = import_react42.useRef(() => {});
|
|
217501
|
+
const handleStopVoice = import_react42.useCallback(async (sendPrompt = false) => {
|
|
217502
|
+
const session = voiceSessionRef.current;
|
|
217503
|
+
if (!session) {
|
|
217504
|
+
setIsVoiceActive(false);
|
|
217505
|
+
setIsTranscribing(false);
|
|
217506
|
+
return;
|
|
217507
|
+
}
|
|
217508
|
+
voiceSessionRef.current = null;
|
|
217509
|
+
setIsVoiceActive(false);
|
|
217510
|
+
if (sendPrompt) {
|
|
217511
|
+
setIsTranscribing(true);
|
|
217512
|
+
try {
|
|
217513
|
+
const finalTranscript = await session.stop();
|
|
217514
|
+
const promptToSend = (finalTranscript || value).trim();
|
|
217515
|
+
setValue("");
|
|
217516
|
+
handleContentChangeRef.current("");
|
|
217517
|
+
if (promptToSend) {
|
|
217518
|
+
onSubmit(promptToSend);
|
|
217519
|
+
}
|
|
217520
|
+
} finally {
|
|
217521
|
+
setIsTranscribing(false);
|
|
217522
|
+
}
|
|
217523
|
+
} else {
|
|
217524
|
+
session.cancel();
|
|
217525
|
+
setValue("");
|
|
217526
|
+
handleContentChangeRef.current("");
|
|
217527
|
+
setIsTranscribing(false);
|
|
217528
|
+
}
|
|
217529
|
+
}, [onSubmit, value]);
|
|
217530
|
+
const handleStartVoice = import_react42.useCallback(async () => {
|
|
217531
|
+
if (!onStartVoice) {
|
|
217532
|
+
onSubmit("/voice");
|
|
217533
|
+
return;
|
|
217534
|
+
}
|
|
217535
|
+
setIsVoiceActive(true);
|
|
217536
|
+
setValue("");
|
|
217537
|
+
handleContentChangeRef.current("");
|
|
217538
|
+
setIsTranscribing(false);
|
|
217539
|
+
try {
|
|
217540
|
+
const session = await onStartVoice({
|
|
217541
|
+
onTranscript: (text) => {
|
|
217542
|
+
setValue(text);
|
|
217543
|
+
handleContentChangeRef.current(text);
|
|
217544
|
+
},
|
|
217545
|
+
onError: (err) => {
|
|
217546
|
+
setIsVoiceActive(false);
|
|
217547
|
+
setIsTranscribing(false);
|
|
217548
|
+
voiceSessionRef.current = null;
|
|
217549
|
+
toast.show({
|
|
217550
|
+
variant: "error",
|
|
217551
|
+
message: err.message || "Voice dictation error."
|
|
217552
|
+
});
|
|
217553
|
+
}
|
|
217554
|
+
});
|
|
217555
|
+
if (!session) {
|
|
217556
|
+
setIsVoiceActive(false);
|
|
217557
|
+
setIsTranscribing(false);
|
|
217558
|
+
return;
|
|
217559
|
+
}
|
|
217560
|
+
voiceSessionRef.current = session;
|
|
217561
|
+
} catch {
|
|
217562
|
+
setIsVoiceActive(false);
|
|
217563
|
+
setIsTranscribing(false);
|
|
217564
|
+
}
|
|
217565
|
+
}, [onStartVoice, onSubmit, toast]);
|
|
216556
217566
|
const handleAutocompleteCommand = import_react42.useCallback((completedText) => {
|
|
216557
217567
|
setValue(completedText);
|
|
216558
217568
|
handleContentChangeRef.current(completedText);
|
|
@@ -216618,7 +217628,10 @@ var init_input_bar = __esm(async () => {
|
|
|
216618
217628
|
handleContentChange,
|
|
216619
217629
|
grillMode,
|
|
216620
217630
|
onSubmit,
|
|
216621
|
-
onCopy
|
|
217631
|
+
onCopy,
|
|
217632
|
+
isVoiceActive,
|
|
217633
|
+
isTranscribing,
|
|
217634
|
+
handleStopVoice
|
|
216622
217635
|
});
|
|
216623
217636
|
stateRef.current = {
|
|
216624
217637
|
showFileMenu,
|
|
@@ -216628,11 +217641,26 @@ var init_input_bar = __esm(async () => {
|
|
|
216628
217641
|
handleContentChange,
|
|
216629
217642
|
grillMode,
|
|
216630
217643
|
onSubmit,
|
|
216631
|
-
onCopy
|
|
217644
|
+
onCopy,
|
|
217645
|
+
isVoiceActive,
|
|
217646
|
+
isTranscribing,
|
|
217647
|
+
handleStopVoice
|
|
216632
217648
|
};
|
|
216633
217649
|
const isCtrlW = import_react42.useRef(false);
|
|
216634
217650
|
use_input_default((input, key) => {
|
|
216635
217651
|
const state = stateRef.current;
|
|
217652
|
+
if (key.escape) {
|
|
217653
|
+
if (state.isVoiceActive || state.isTranscribing || voiceSessionRef.current) {
|
|
217654
|
+
state.handleStopVoice(false);
|
|
217655
|
+
return;
|
|
217656
|
+
}
|
|
217657
|
+
if (state.showFileMenu && state.matchingFiles.length > 0) {
|
|
217658
|
+
const nextVal = state.value.replace(/@\S*$/, "");
|
|
217659
|
+
setValue(nextVal);
|
|
217660
|
+
state.handleContentChange(nextVal);
|
|
217661
|
+
return;
|
|
217662
|
+
}
|
|
217663
|
+
}
|
|
216636
217664
|
if (state.showFileMenu && state.matchingFiles.length > 0) {
|
|
216637
217665
|
if (key.upArrow) {
|
|
216638
217666
|
setSelectedFileIndex((prev) => Math.max(0, prev - 1));
|
|
@@ -216652,12 +217680,6 @@ var init_input_bar = __esm(async () => {
|
|
|
216652
217680
|
}
|
|
216653
217681
|
return;
|
|
216654
217682
|
}
|
|
216655
|
-
if (key.escape) {
|
|
216656
|
-
const nextVal = state.value.replace(/@\S*$/, "");
|
|
216657
|
-
setValue(nextVal);
|
|
216658
|
-
state.handleContentChange(nextVal);
|
|
216659
|
-
return;
|
|
216660
|
-
}
|
|
216661
217683
|
}
|
|
216662
217684
|
if (key.ctrl && input === "w") {
|
|
216663
217685
|
isCtrlW.current = true;
|
|
@@ -216748,6 +217770,12 @@ var init_input_bar = __esm(async () => {
|
|
|
216748
217770
|
"/handoff",
|
|
216749
217771
|
"/docs"
|
|
216750
217772
|
];
|
|
217773
|
+
if (command.value === "/voice") {
|
|
217774
|
+
setValue("");
|
|
217775
|
+
handleContentChange("");
|
|
217776
|
+
handleStartVoice();
|
|
217777
|
+
return;
|
|
217778
|
+
}
|
|
216751
217779
|
if (forwardCommands.includes(command.value) || !command.action) {
|
|
216752
217780
|
if (hasArguments) {
|
|
216753
217781
|
onSubmit(currentValue);
|
|
@@ -216766,10 +217794,24 @@ var init_input_bar = __esm(async () => {
|
|
|
216766
217794
|
onUpdateSuccess
|
|
216767
217795
|
});
|
|
216768
217796
|
}
|
|
216769
|
-
}, [
|
|
217797
|
+
}, [
|
|
217798
|
+
toast,
|
|
217799
|
+
dialog,
|
|
217800
|
+
agent,
|
|
217801
|
+
resetChat,
|
|
217802
|
+
onUpdateSuccess,
|
|
217803
|
+
handleContentChange,
|
|
217804
|
+
onSubmit,
|
|
217805
|
+
value,
|
|
217806
|
+
handleStartVoice
|
|
217807
|
+
]);
|
|
216770
217808
|
const handleSubmit = import_react42.useCallback(async (text) => {
|
|
216771
217809
|
if (disabled)
|
|
216772
217810
|
return;
|
|
217811
|
+
if (isVoiceActive || isTranscribing) {
|
|
217812
|
+
await handleStopVoice(true);
|
|
217813
|
+
return;
|
|
217814
|
+
}
|
|
216773
217815
|
if (showCommandMenu) {
|
|
216774
217816
|
const command = resolveCommand(selectedIndex);
|
|
216775
217817
|
handleCommand(command);
|
|
@@ -216781,6 +217823,12 @@ var init_input_bar = __esm(async () => {
|
|
|
216781
217823
|
const rawTrimmed = text.trim();
|
|
216782
217824
|
if (rawTrimmed.length === 0)
|
|
216783
217825
|
return;
|
|
217826
|
+
if (rawTrimmed === "/voice") {
|
|
217827
|
+
setValue("");
|
|
217828
|
+
handleContentChange("");
|
|
217829
|
+
handleStartVoice();
|
|
217830
|
+
return;
|
|
217831
|
+
}
|
|
216784
217832
|
const trimmed = expandPastes(rawTrimmed);
|
|
216785
217833
|
defaultPromptHistory.append(trimmed);
|
|
216786
217834
|
defaultPromptHistory.resetCursor();
|
|
@@ -216939,7 +217987,8 @@ var init_input_bar = __esm(async () => {
|
|
|
216939
217987
|
onHistoryDown: handleHistoryDown,
|
|
216940
217988
|
onScrollUp,
|
|
216941
217989
|
onScrollDown,
|
|
216942
|
-
placeholder: grillMode ? "" : planRefineMode ? "Enter feedback to refine plan (or Esc to cancel)..." : interactivePlanGoalMode ? "Enter your goal (e.g. add dark mode) or Esc to cancel..." : placeholder,
|
|
217990
|
+
placeholder: isTranscribing ? "Transcribing speech..." : isVoiceActive ? "Listening..." : grillMode ? "" : planRefineMode ? "Enter feedback to refine plan (or Esc to cancel)..." : interactivePlanGoalMode ? "Enter your goal (e.g. add dark mode) or Esc to cancel..." : placeholder,
|
|
217991
|
+
placeholderColor: isVoiceActive ? THEME.colors.brand : undefined,
|
|
216943
217992
|
focus: !disabled && !dialog.isOpen,
|
|
216944
217993
|
disableHistoryNav: showCommandMenu || showFileMenu || showShortcutsMenu
|
|
216945
217994
|
})
|
|
@@ -217254,7 +218303,7 @@ var init_scroll_viewport = __esm(async () => {
|
|
|
217254
218303
|
});
|
|
217255
218304
|
|
|
217256
218305
|
// ../../packages/tui/src/utils/terminal-link.ts
|
|
217257
|
-
import
|
|
218306
|
+
import path34 from "node:path";
|
|
217258
218307
|
function terminalLink(text, uri) {
|
|
217259
218308
|
if (!uri)
|
|
217260
218309
|
return text;
|
|
@@ -217263,7 +218312,7 @@ function terminalLink(text, uri) {
|
|
|
217263
218312
|
function fileLink(displayPath, targetPath, line, col) {
|
|
217264
218313
|
if (!targetPath)
|
|
217265
218314
|
return displayPath;
|
|
217266
|
-
const absPath =
|
|
218315
|
+
const absPath = path34.isAbsolute(targetPath) ? targetPath : path34.resolve(process.cwd(), targetPath);
|
|
217267
218316
|
const lineFragment = line ? `#L${line}${col ? `:${col}` : ""}` : "";
|
|
217268
218317
|
const uri = `file://${absPath}${lineFragment}`;
|
|
217269
218318
|
return terminalLink(displayPath, uri);
|
|
@@ -217272,9 +218321,9 @@ function toRelativePath(filePath, cwd = process.cwd()) {
|
|
|
217272
218321
|
if (!filePath)
|
|
217273
218322
|
return "";
|
|
217274
218323
|
const trimmed = filePath.trim();
|
|
217275
|
-
if (!
|
|
218324
|
+
if (!path34.isAbsolute(trimmed))
|
|
217276
218325
|
return trimmed;
|
|
217277
|
-
const rel =
|
|
218326
|
+
const rel = path34.relative(cwd, trimmed);
|
|
217278
218327
|
if (!rel || rel.startsWith("..")) {
|
|
217279
218328
|
return trimmed;
|
|
217280
218329
|
}
|
|
@@ -263642,27 +264691,27 @@ ${r}`;
|
|
|
263642
264691
|
});
|
|
263643
264692
|
|
|
263644
264693
|
// ../../packages/tui/src/components/header.tsx
|
|
263645
|
-
import
|
|
264694
|
+
import fs35 from "node:fs";
|
|
263646
264695
|
import { homedir } from "node:os";
|
|
263647
|
-
import
|
|
264696
|
+
import path35 from "node:path";
|
|
263648
264697
|
function getGitBranch(workspaceRoot = process.cwd()) {
|
|
263649
264698
|
if (cachedBranch !== undefined) {
|
|
263650
264699
|
return cachedBranch;
|
|
263651
264700
|
}
|
|
263652
264701
|
try {
|
|
263653
|
-
const gitPath =
|
|
263654
|
-
if (
|
|
263655
|
-
const stat =
|
|
263656
|
-
let headPath =
|
|
264702
|
+
const gitPath = path35.resolve(workspaceRoot, ".git");
|
|
264703
|
+
if (fs35.existsSync(gitPath)) {
|
|
264704
|
+
const stat = fs35.statSync(gitPath);
|
|
264705
|
+
let headPath = path35.resolve(gitPath, "HEAD");
|
|
263657
264706
|
if (stat.isFile()) {
|
|
263658
|
-
const content =
|
|
264707
|
+
const content = fs35.readFileSync(gitPath, "utf-8").trim();
|
|
263659
264708
|
if (content.startsWith("gitdir:")) {
|
|
263660
264709
|
const gitDir = content.slice(7).trim();
|
|
263661
|
-
headPath =
|
|
264710
|
+
headPath = path35.resolve(workspaceRoot, gitDir, "HEAD");
|
|
263662
264711
|
}
|
|
263663
264712
|
}
|
|
263664
|
-
if (
|
|
263665
|
-
const headContent =
|
|
264713
|
+
if (fs35.existsSync(headPath)) {
|
|
264714
|
+
const headContent = fs35.readFileSync(headPath, "utf-8").trim();
|
|
263666
264715
|
if (headContent.startsWith("ref: refs/heads/")) {
|
|
263667
264716
|
cachedBranch = headContent.replace("ref: refs/heads/", "").trim();
|
|
263668
264717
|
return cachedBranch;
|
|
@@ -263936,7 +264985,7 @@ var init_message_list = __esm(async () => {
|
|
|
263936
264985
|
flexDirection: "column",
|
|
263937
264986
|
width: "100%",
|
|
263938
264987
|
children: [
|
|
263939
|
-
staticMessages.length > 0 && /* @__PURE__ */ jsx_runtime20.jsx(
|
|
264988
|
+
staticMessages.length > 0 && /* @__PURE__ */ jsx_runtime20.jsx(Static16, {
|
|
263940
264989
|
items: staticMessages,
|
|
263941
264990
|
style: { width: "100%" },
|
|
263942
264991
|
children: (msg, index) => renderSingleMessage(msg, index, staticMessages, cliVersion, latestVersion, userEmail, expandCommands, announcement)
|
|
@@ -264504,6 +265553,7 @@ function ByokKeyMenu(props) {
|
|
|
264504
265553
|
const { selectedProvider, apiKey, setApiKey, handleKeySubmit, isStreaming, authError } = props;
|
|
264505
265554
|
const formattedProvider = formatProviderName2(selectedProvider);
|
|
264506
265555
|
const keyUrl = selectedProvider ? PROVIDER_KEY_URLS[selectedProvider] : undefined;
|
|
265556
|
+
const keyNote = selectedProvider ? PROVIDER_KEY_NOTES[selectedProvider.toLowerCase().trim()] : undefined;
|
|
264507
265557
|
return /* @__PURE__ */ jsx_runtime25.jsxs(Box_default, {
|
|
264508
265558
|
flexDirection: "column",
|
|
264509
265559
|
paddingX: THEME.padding.paddingX,
|
|
@@ -264528,7 +265578,8 @@ function ByokKeyMenu(props) {
|
|
|
264528
265578
|
/* @__PURE__ */ jsx_runtime25.jsx(Text3, {
|
|
264529
265579
|
color: THEME.colors.brand,
|
|
264530
265580
|
children: keyUrl
|
|
264531
|
-
})
|
|
265581
|
+
}),
|
|
265582
|
+
keyNote
|
|
264532
265583
|
]
|
|
264533
265584
|
})
|
|
264534
265585
|
]
|
|
@@ -264573,7 +265624,7 @@ function ByokKeyMenu(props) {
|
|
|
264573
265624
|
]
|
|
264574
265625
|
});
|
|
264575
265626
|
}
|
|
264576
|
-
var jsx_runtime25, PROVIDER_NAMES, PROVIDER_KEY_URLS;
|
|
265627
|
+
var jsx_runtime25, PROVIDER_NAMES, PROVIDER_KEY_URLS, PROVIDER_KEY_NOTES;
|
|
264577
265628
|
var init_byok_key_menu = __esm(async () => {
|
|
264578
265629
|
init_theme();
|
|
264579
265630
|
await __promiseAll([
|
|
@@ -264619,6 +265670,7 @@ var init_byok_key_menu = __esm(async () => {
|
|
|
264619
265670
|
december: "December Cloud",
|
|
264620
265671
|
december_proxy: "December Cloud",
|
|
264621
265672
|
deepseek: "DeepSeek",
|
|
265673
|
+
deepgram: "Deepgram",
|
|
264622
265674
|
fireworks: "Fireworks AI",
|
|
264623
265675
|
google: "Google",
|
|
264624
265676
|
gemini: "Google AI Studio",
|
|
@@ -264737,6 +265789,7 @@ var init_byok_key_menu = __esm(async () => {
|
|
|
264737
265789
|
deepinfra: "https://deepinfra.com/dash/api_keys",
|
|
264738
265790
|
deepseek: "https://platform.deepseek.com/api_keys",
|
|
264739
265791
|
digitalocean: "https://cloud.digitalocean.com/account/api/tokens",
|
|
265792
|
+
deepgram: "https://console.deepgram.com",
|
|
264740
265793
|
dinference: "https://dinference.com/",
|
|
264741
265794
|
drun: "https://d.run/",
|
|
264742
265795
|
ebcloud: "https://ebcloud.com/",
|
|
@@ -264910,6 +265963,9 @@ var init_byok_key_menu = __esm(async () => {
|
|
|
264910
265963
|
zhipu: "https://open.bigmodel.cn/usercenter/apikeys",
|
|
264911
265964
|
zhipuai: "https://open.bigmodel.cn/usercenter/apikeys"
|
|
264912
265965
|
};
|
|
265966
|
+
PROVIDER_KEY_NOTES = {
|
|
265967
|
+
deepgram: " ($200 free credit on signup)"
|
|
265968
|
+
};
|
|
264913
265969
|
});
|
|
264914
265970
|
|
|
264915
265971
|
// ../../packages/tui/src/components/menus/context-select-menu.tsx
|
|
@@ -266835,6 +267891,7 @@ function SettingsMainMenu(props) {
|
|
|
266835
267891
|
settingsPathGuard = true,
|
|
266836
267892
|
settingsSteeringMode = "all",
|
|
266837
267893
|
settingsFollowUpMode = "all",
|
|
267894
|
+
settingsBrowserVisible = true,
|
|
266838
267895
|
handleSettingsMainSelect
|
|
266839
267896
|
} = props;
|
|
266840
267897
|
const providerKey = selectedProvider || activeProvider || "";
|
|
@@ -266873,6 +267930,10 @@ function SettingsMainMenu(props) {
|
|
|
266873
267930
|
{
|
|
266874
267931
|
label: `Follow-Up Mode [${settingsFollowUpMode}]`,
|
|
266875
267932
|
value: "followUpMode"
|
|
267933
|
+
},
|
|
267934
|
+
{
|
|
267935
|
+
label: `Browser Window [${settingsBrowserVisible !== false ? "on" : "off"}]`,
|
|
267936
|
+
value: "browserVisible"
|
|
266876
267937
|
}
|
|
266877
267938
|
];
|
|
266878
267939
|
return /* @__PURE__ */ jsx_runtime34.jsxs(Box_default, {
|
|
@@ -269025,25 +270086,25 @@ var init_global_shortcuts = __esm(async () => {
|
|
|
269025
270086
|
|
|
269026
270087
|
// ../../packages/tui/src/utils/pager.ts
|
|
269027
270088
|
import * as cp3 from "node:child_process";
|
|
269028
|
-
import
|
|
269029
|
-
import
|
|
269030
|
-
import
|
|
270089
|
+
import fs36 from "node:fs";
|
|
270090
|
+
import os21 from "node:os";
|
|
270091
|
+
import path36 from "node:path";
|
|
269031
270092
|
function openPlanInPager(planText, title = "december-plan") {
|
|
269032
270093
|
if (Boolean(process.env.BUN_TEST) || Boolean(process.env.VITEST)) {
|
|
269033
270094
|
return Promise.resolve();
|
|
269034
270095
|
}
|
|
269035
270096
|
return new Promise((resolve) => {
|
|
269036
270097
|
try {
|
|
269037
|
-
const tmpFile =
|
|
269038
|
-
|
|
269039
|
-
const pager = process.env.PAGER || process.env.EDITOR || (
|
|
270098
|
+
const tmpFile = path36.join(os21.tmpdir(), `${title}-${Date.now()}.md`);
|
|
270099
|
+
fs36.writeFileSync(tmpFile, planText, "utf8");
|
|
270100
|
+
const pager = process.env.PAGER || process.env.EDITOR || (os21.platform() === "win32" ? "notepad" : "less -R");
|
|
269040
270101
|
const child = cp3.spawn(pager, [tmpFile], {
|
|
269041
270102
|
stdio: "inherit",
|
|
269042
270103
|
shell: true
|
|
269043
270104
|
});
|
|
269044
270105
|
child.on("exit", () => {
|
|
269045
270106
|
try {
|
|
269046
|
-
|
|
270107
|
+
fs36.unlinkSync(tmpFile);
|
|
269047
270108
|
} catch {}
|
|
269048
270109
|
resolve();
|
|
269049
270110
|
});
|
|
@@ -269203,6 +270264,7 @@ function ChatApp({
|
|
|
269203
270264
|
onSubmit: handleFormSubmit,
|
|
269204
270265
|
onScrollUp: handleScrollUp,
|
|
269205
270266
|
onScrollDown: handleScrollDown,
|
|
270267
|
+
onStartVoice: session.onStartVoice,
|
|
269206
270268
|
disabled: authMode !== "none" || Boolean(workflowUI) && !session.customInputMode,
|
|
269207
270269
|
onCopy: () => {
|
|
269208
270270
|
Promise.resolve().then(() => (init_clipboard(), {})).then((cb) => {
|
|
@@ -269579,6 +270641,8 @@ var init_store = __esm(() => {
|
|
|
269579
270641
|
setSettingsSteeringMode: (settingsSteeringMode) => set({ settingsSteeringMode }),
|
|
269580
270642
|
settingsFollowUpMode: "all",
|
|
269581
270643
|
setSettingsFollowUpMode: (settingsFollowUpMode) => set({ settingsFollowUpMode }),
|
|
270644
|
+
settingsBrowserVisible: true,
|
|
270645
|
+
setSettingsBrowserVisible: (settingsBrowserVisible) => set({ settingsBrowserVisible }),
|
|
269582
270646
|
tasksData: [],
|
|
269583
270647
|
setTasksData: (tasksData) => set({ tasksData }),
|
|
269584
270648
|
taskSelectedIndex: 0,
|
|
@@ -269612,9 +270676,9 @@ var init_store = __esm(() => {
|
|
|
269612
270676
|
});
|
|
269613
270677
|
|
|
269614
270678
|
// src/store/interceptors.ts
|
|
269615
|
-
import { execSync as
|
|
269616
|
-
import
|
|
269617
|
-
import
|
|
270679
|
+
import { execSync as execSync4 } from "node:child_process";
|
|
270680
|
+
import fs37 from "node:fs/promises";
|
|
270681
|
+
import path37 from "node:path";
|
|
269618
270682
|
async function computeToolCallDiff(toolCall) {
|
|
269619
270683
|
if (toolCall.input?.diff) {
|
|
269620
270684
|
return toolCall.input.diff;
|
|
@@ -269623,11 +270687,11 @@ async function computeToolCallDiff(toolCall) {
|
|
|
269623
270687
|
if (!targetPath)
|
|
269624
270688
|
return;
|
|
269625
270689
|
const cwd = process.cwd();
|
|
269626
|
-
const resolved =
|
|
269627
|
-
const relative =
|
|
270690
|
+
const resolved = path37.resolve(cwd, targetPath);
|
|
270691
|
+
const relative = path37.relative(cwd, resolved);
|
|
269628
270692
|
let oldContent = "";
|
|
269629
270693
|
try {
|
|
269630
|
-
oldContent = await
|
|
270694
|
+
oldContent = await fs37.readFile(resolved, "utf-8");
|
|
269631
270695
|
} catch {}
|
|
269632
270696
|
if (toolCall.name === "write_to_file" || toolCall.name === "write") {
|
|
269633
270697
|
const newContent = toolCall.input?.CodeContent ?? toolCall.input?.content ?? "";
|
|
@@ -269691,9 +270755,9 @@ function setupAgentInterceptors(agent, storeState) {
|
|
|
269691
270755
|
];
|
|
269692
270756
|
if (fileTools.includes(toolCall.name)) {
|
|
269693
270757
|
const cwd = process.cwd();
|
|
269694
|
-
const resolved =
|
|
269695
|
-
const relative =
|
|
269696
|
-
const isOutside = relative.startsWith("..") ||
|
|
270758
|
+
const resolved = path37.resolve(cwd, targetPath);
|
|
270759
|
+
const relative = path37.relative(cwd, resolved);
|
|
270760
|
+
const isOutside = relative.startsWith("..") || path37.isAbsolute(relative);
|
|
269697
270761
|
if (!config.nonWorkspaceAccess && isOutside) {
|
|
269698
270762
|
return {
|
|
269699
270763
|
block: true,
|
|
@@ -269757,10 +270821,10 @@ function setupAgentInterceptors(agent, storeState) {
|
|
|
269757
270821
|
const targetPath = toolCall.input?.TargetFile || toolCall.input?.AbsolutePath || toolCall.input?.filePath || toolCall.input?.path;
|
|
269758
270822
|
if (targetPath) {
|
|
269759
270823
|
const cwd = process.cwd();
|
|
269760
|
-
const resolved =
|
|
269761
|
-
const relative =
|
|
270824
|
+
const resolved = path37.resolve(cwd, targetPath);
|
|
270825
|
+
const relative = path37.relative(cwd, resolved);
|
|
269762
270826
|
try {
|
|
269763
|
-
|
|
270827
|
+
execSync4(`git ls-files --error-unmatch "${relative}"`, {
|
|
269764
270828
|
cwd,
|
|
269765
270829
|
stdio: "pipe"
|
|
269766
270830
|
});
|
|
@@ -269785,7 +270849,7 @@ var init_interceptors = __esm(() => {
|
|
|
269785
270849
|
});
|
|
269786
270850
|
|
|
269787
270851
|
// src/utils/direct-shell.ts
|
|
269788
|
-
import { spawn as
|
|
270852
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
269789
270853
|
function startDirectCommand(command, options = {}) {
|
|
269790
270854
|
const { timeoutMs = 20000, serverReadyDelayMs = 2500, onData, onBackground } = options;
|
|
269791
270855
|
let output = "";
|
|
@@ -269795,7 +270859,7 @@ function startDirectCommand(command, options = {}) {
|
|
|
269795
270859
|
const isWindows = process.platform === "win32";
|
|
269796
270860
|
const shellCmd = isWindows ? "cmd.exe" : "/bin/bash";
|
|
269797
270861
|
const shellArgs = isWindows ? ["/d", "/s", "/c", command] : ["-c", command];
|
|
269798
|
-
const child =
|
|
270862
|
+
const child = spawn7(shellCmd, shellArgs, {
|
|
269799
270863
|
cwd: process.cwd(),
|
|
269800
270864
|
env: process.env,
|
|
269801
270865
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -269947,15 +271011,15 @@ var init_direct_shell = __esm(() => {
|
|
|
269947
271011
|
});
|
|
269948
271012
|
|
|
269949
271013
|
// src/utils/usage-tracker.ts
|
|
269950
|
-
import
|
|
269951
|
-
import
|
|
271014
|
+
import fs38 from "node:fs/promises";
|
|
271015
|
+
import path38 from "node:path";
|
|
269952
271016
|
function getUsageFilePath() {
|
|
269953
|
-
return
|
|
271017
|
+
return path38.join(getConfigDir(), "usage.json");
|
|
269954
271018
|
}
|
|
269955
271019
|
async function loadUsageLedger() {
|
|
269956
271020
|
const filePath = getUsageFilePath();
|
|
269957
271021
|
try {
|
|
269958
|
-
const raw = await
|
|
271022
|
+
const raw = await fs38.readFile(filePath, "utf-8");
|
|
269959
271023
|
const parsed = JSON.parse(raw);
|
|
269960
271024
|
if (parsed && typeof parsed === "object" && parsed.version === 1 && parsed.days) {
|
|
269961
271025
|
return parsed;
|
|
@@ -269968,8 +271032,8 @@ async function loadUsageLedger() {
|
|
|
269968
271032
|
}
|
|
269969
271033
|
async function saveUsageLedger(ledger) {
|
|
269970
271034
|
const configDir = getConfigDir();
|
|
269971
|
-
const filePath =
|
|
269972
|
-
const tmpPath =
|
|
271035
|
+
const filePath = path38.join(configDir, "usage.json");
|
|
271036
|
+
const tmpPath = path38.join(configDir, `usage.json.tmp.${Date.now()}`);
|
|
269973
271037
|
const cutoffDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
|
269974
271038
|
const prunedDays = {};
|
|
269975
271039
|
for (const [dateKey, dayRecord] of Object.entries(ledger.days)) {
|
|
@@ -269979,12 +271043,12 @@ async function saveUsageLedger(ledger) {
|
|
|
269979
271043
|
}
|
|
269980
271044
|
ledger.days = prunedDays;
|
|
269981
271045
|
try {
|
|
269982
|
-
await
|
|
269983
|
-
await
|
|
269984
|
-
await
|
|
271046
|
+
await fs38.mkdir(configDir, { recursive: true });
|
|
271047
|
+
await fs38.writeFile(tmpPath, JSON.stringify(ledger, null, 2), "utf-8");
|
|
271048
|
+
await fs38.rename(tmpPath, filePath);
|
|
269985
271049
|
} catch {
|
|
269986
271050
|
try {
|
|
269987
|
-
await
|
|
271051
|
+
await fs38.unlink(tmpPath);
|
|
269988
271052
|
} catch {}
|
|
269989
271053
|
}
|
|
269990
271054
|
}
|
|
@@ -271421,14 +272485,14 @@ function extractJsonArray(rawText) {
|
|
|
271421
272485
|
}
|
|
271422
272486
|
|
|
271423
272487
|
// src/utils/project-context.ts
|
|
271424
|
-
import
|
|
271425
|
-
import
|
|
272488
|
+
import fs39 from "node:fs";
|
|
272489
|
+
import path39 from "node:path";
|
|
271426
272490
|
function getProjectContext(workspaceDir = process.cwd()) {
|
|
271427
272491
|
const parts = [];
|
|
271428
272492
|
try {
|
|
271429
|
-
const pkgPath =
|
|
271430
|
-
if (
|
|
271431
|
-
const pkg = JSON.parse(
|
|
272493
|
+
const pkgPath = path39.join(workspaceDir, "package.json");
|
|
272494
|
+
if (fs39.existsSync(pkgPath)) {
|
|
272495
|
+
const pkg = JSON.parse(fs39.readFileSync(pkgPath, "utf8"));
|
|
271432
272496
|
const deps = Object.keys(pkg.dependencies || {}).slice(0, 15).join(", ");
|
|
271433
272497
|
const devDeps = Object.keys(pkg.devDependencies || {}).slice(0, 10).join(", ");
|
|
271434
272498
|
parts.push(`Project: ${pkg.name || "Unnamed"}`);
|
|
@@ -271437,13 +272501,13 @@ function getProjectContext(workspaceDir = process.cwd()) {
|
|
|
271437
272501
|
if (devDeps)
|
|
271438
272502
|
parts.push(`DevDependencies: ${devDeps}`);
|
|
271439
272503
|
}
|
|
271440
|
-
const agentsPath =
|
|
271441
|
-
if (
|
|
271442
|
-
const content =
|
|
272504
|
+
const agentsPath = path39.join(workspaceDir, "AGENTS.md");
|
|
272505
|
+
if (fs39.existsSync(agentsPath)) {
|
|
272506
|
+
const content = fs39.readFileSync(agentsPath, "utf8");
|
|
271443
272507
|
parts.push(`Guidelines (from AGENTS.md):
|
|
271444
272508
|
${content.slice(0, 500)}`);
|
|
271445
272509
|
}
|
|
271446
|
-
const entries =
|
|
272510
|
+
const entries = fs39.readdirSync(workspaceDir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.isDirectory() ? `${e.name}/` : e.name).slice(0, 20);
|
|
271447
272511
|
if (entries.length > 0) {
|
|
271448
272512
|
parts.push(`Workspace layout: ${entries.join(", ")}`);
|
|
271449
272513
|
}
|
|
@@ -271598,14 +272662,14 @@ async function fetchProviderBalance(provider, apiKey, timeoutMs = 2000) {
|
|
|
271598
272662
|
}
|
|
271599
272663
|
|
|
271600
272664
|
// src/utils/formatters.ts
|
|
271601
|
-
import
|
|
272665
|
+
import path40 from "node:path";
|
|
271602
272666
|
function toRelativePath2(filePath, cwd = process.cwd()) {
|
|
271603
272667
|
if (!filePath)
|
|
271604
272668
|
return "";
|
|
271605
272669
|
const trimmed = filePath.trim();
|
|
271606
|
-
if (!
|
|
272670
|
+
if (!path40.isAbsolute(trimmed))
|
|
271607
272671
|
return trimmed;
|
|
271608
|
-
const rel =
|
|
272672
|
+
const rel = path40.relative(cwd, trimmed);
|
|
271609
272673
|
if (!rel || rel.startsWith("..")) {
|
|
271610
272674
|
return trimmed;
|
|
271611
272675
|
}
|
|
@@ -272328,6 +273392,30 @@ ${formatSubscriptionCard(bundle, targetModel)}`
|
|
|
272328
273392
|
if (!trimmedKey)
|
|
272329
273393
|
return;
|
|
272330
273394
|
const selectedProvider = useCliStore.getState().selectedProvider;
|
|
273395
|
+
if (selectedProvider === "deepgram") {
|
|
273396
|
+
setIsStreaming(true);
|
|
273397
|
+
setAuthError(null);
|
|
273398
|
+
try {
|
|
273399
|
+
const res = await fetch("https://api.deepgram.com/v1/projects", {
|
|
273400
|
+
headers: { Authorization: `Token ${trimmedKey}` }
|
|
273401
|
+
});
|
|
273402
|
+
if (!res.ok) {
|
|
273403
|
+
throw new Error("Invalid Deepgram API key. Please check your key from console.deepgram.com.");
|
|
273404
|
+
}
|
|
273405
|
+
const config = await loadConfig();
|
|
273406
|
+
config.providers = config.providers || {};
|
|
273407
|
+
config.providers.deepgram = trimmedKey;
|
|
273408
|
+
await saveConfig(config);
|
|
273409
|
+
setApiKey("");
|
|
273410
|
+
setAuthMode("none");
|
|
273411
|
+
addToast("Saved Deepgram API key for voice dictation.", "success");
|
|
273412
|
+
} catch (err) {
|
|
273413
|
+
setAuthError(err.message || "Failed to verify Deepgram API key.");
|
|
273414
|
+
} finally {
|
|
273415
|
+
setIsStreaming(false);
|
|
273416
|
+
}
|
|
273417
|
+
return;
|
|
273418
|
+
}
|
|
272331
273419
|
setIsStreaming(true);
|
|
272332
273420
|
setAuthError(null);
|
|
272333
273421
|
let testProvider;
|
|
@@ -272684,6 +273772,8 @@ function useSettingsHandlers() {
|
|
|
272684
273772
|
setSettingsSteeringMode,
|
|
272685
273773
|
settingsFollowUpMode,
|
|
272686
273774
|
setSettingsFollowUpMode,
|
|
273775
|
+
settingsBrowserVisible,
|
|
273776
|
+
setSettingsBrowserVisible,
|
|
272687
273777
|
settingsAuthPriority,
|
|
272688
273778
|
setSettingsAuthPriority,
|
|
272689
273779
|
setAuthMethod,
|
|
@@ -272694,7 +273784,7 @@ function useSettingsHandlers() {
|
|
|
272694
273784
|
setOllamaModels,
|
|
272695
273785
|
agent,
|
|
272696
273786
|
addToast
|
|
272697
|
-
} = useCliStore();
|
|
273787
|
+
} = useCliStore.getState();
|
|
272698
273788
|
const handleSettingsMainSelect = async (item) => {
|
|
272699
273789
|
const config = await loadConfig();
|
|
272700
273790
|
let updated = false;
|
|
@@ -272802,6 +273892,15 @@ function useSettingsHandlers() {
|
|
|
272802
273892
|
updated = true;
|
|
272803
273893
|
break;
|
|
272804
273894
|
}
|
|
273895
|
+
case "browserVisible": {
|
|
273896
|
+
const current = useCliStore.getState().settingsBrowserVisible;
|
|
273897
|
+
const nextVal = !(current !== false);
|
|
273898
|
+
config.browserVisible = nextVal;
|
|
273899
|
+
setSettingsBrowserVisible(nextVal);
|
|
273900
|
+
addToast(`Browser window set to ${nextVal ? "visible (on)" : "headless (off)"}`);
|
|
273901
|
+
updated = true;
|
|
273902
|
+
break;
|
|
273903
|
+
}
|
|
272805
273904
|
case "authPriority": {
|
|
272806
273905
|
const priorities = [
|
|
272807
273906
|
"subscription",
|
|
@@ -272858,6 +273957,503 @@ var init_use_settings_handlers = __esm(() => {
|
|
|
272858
273957
|
init_provider_factory();
|
|
272859
273958
|
});
|
|
272860
273959
|
|
|
273960
|
+
// src/utils/audio/detector.ts
|
|
273961
|
+
import { execSync as execSync5 } from "node:child_process";
|
|
273962
|
+
function checkBinary(name) {
|
|
273963
|
+
try {
|
|
273964
|
+
const isWindows = process.platform === "win32";
|
|
273965
|
+
const checkCmd = isWindows ? `where ${name}` : `which ${name}`;
|
|
273966
|
+
const stdout = execSync5(checkCmd, {
|
|
273967
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
273968
|
+
encoding: "utf8"
|
|
273969
|
+
}).trim();
|
|
273970
|
+
return stdout ? stdout.split(`
|
|
273971
|
+
`)[0].trim() : null;
|
|
273972
|
+
} catch {
|
|
273973
|
+
return null;
|
|
273974
|
+
}
|
|
273975
|
+
}
|
|
273976
|
+
function detectAudioBinary() {
|
|
273977
|
+
const ffmpegPath = checkBinary("ffmpeg");
|
|
273978
|
+
if (ffmpegPath) {
|
|
273979
|
+
return { available: true, binary: "ffmpeg", path: ffmpegPath };
|
|
273980
|
+
}
|
|
273981
|
+
const soxPath = checkBinary("rec") || checkBinary("sox");
|
|
273982
|
+
if (soxPath) {
|
|
273983
|
+
return { available: true, binary: "sox", path: soxPath };
|
|
273984
|
+
}
|
|
273985
|
+
if (process.platform === "linux") {
|
|
273986
|
+
const arecordPath = checkBinary("arecord");
|
|
273987
|
+
if (arecordPath) {
|
|
273988
|
+
return { available: true, binary: "arecord", path: arecordPath };
|
|
273989
|
+
}
|
|
273990
|
+
}
|
|
273991
|
+
return {
|
|
273992
|
+
available: false,
|
|
273993
|
+
binary: null,
|
|
273994
|
+
error: process.platform === "darwin" ? 'No audio recording binary found. Please install ffmpeg or sox (e.g. "brew install ffmpeg").' : process.platform === "linux" ? 'No audio recording binary found. Please install ffmpeg, sox, or alsa-utils (e.g. "sudo apt install ffmpeg").' : "No audio recording binary found. Please install ffmpeg or sox in your system PATH."
|
|
273995
|
+
};
|
|
273996
|
+
}
|
|
273997
|
+
var init_detector2 = () => {};
|
|
273998
|
+
|
|
273999
|
+
// src/utils/audio/recorder.ts
|
|
274000
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
274001
|
+
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
274002
|
+
var AudioRecorder;
|
|
274003
|
+
var init_recorder = __esm(() => {
|
|
274004
|
+
init_detector2();
|
|
274005
|
+
AudioRecorder = class AudioRecorder extends EventEmitter4 {
|
|
274006
|
+
process = null;
|
|
274007
|
+
isRecording = false;
|
|
274008
|
+
start(options = {}) {
|
|
274009
|
+
if (this.isRecording)
|
|
274010
|
+
return true;
|
|
274011
|
+
const detection = detectAudioBinary();
|
|
274012
|
+
if (!detection.available || !detection.binary) {
|
|
274013
|
+
this.emit("error", new Error(detection.error || "Audio recording binary not found."));
|
|
274014
|
+
return false;
|
|
274015
|
+
}
|
|
274016
|
+
const sampleRate = options.sampleRate ?? 16000;
|
|
274017
|
+
const channels = options.channels ?? 1;
|
|
274018
|
+
try {
|
|
274019
|
+
this.process = this.spawnRecorderProcess(detection.binary, sampleRate, channels);
|
|
274020
|
+
this.isRecording = true;
|
|
274021
|
+
this.process.stdout.on("data", (chunk) => {
|
|
274022
|
+
this.emit("data", chunk);
|
|
274023
|
+
});
|
|
274024
|
+
this.process.stderr.on("data", (data) => {
|
|
274025
|
+
const msg = data.toString();
|
|
274026
|
+
if (msg.includes("No such device") || msg.includes("Device or resource busy")) {
|
|
274027
|
+
this.emit("error", new Error(msg.trim()));
|
|
274028
|
+
}
|
|
274029
|
+
});
|
|
274030
|
+
this.process.on("error", (err) => {
|
|
274031
|
+
this.emit("error", err);
|
|
274032
|
+
this.stop();
|
|
274033
|
+
});
|
|
274034
|
+
this.process.on("close", (code) => {
|
|
274035
|
+
this.isRecording = false;
|
|
274036
|
+
this.process = null;
|
|
274037
|
+
this.emit("close", code);
|
|
274038
|
+
});
|
|
274039
|
+
return true;
|
|
274040
|
+
} catch (err) {
|
|
274041
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
274042
|
+
this.emit("error", error);
|
|
274043
|
+
this.stop();
|
|
274044
|
+
return false;
|
|
274045
|
+
}
|
|
274046
|
+
}
|
|
274047
|
+
spawnRecorderProcess(binary, sampleRate, channels) {
|
|
274048
|
+
if (binary === "ffmpeg") {
|
|
274049
|
+
const isLinux = process.platform === "linux";
|
|
274050
|
+
const isMac = process.platform === "darwin";
|
|
274051
|
+
const isWindows = process.platform === "win32";
|
|
274052
|
+
const args = [
|
|
274053
|
+
"-loglevel",
|
|
274054
|
+
"error",
|
|
274055
|
+
"-fflags",
|
|
274056
|
+
"nobuffer",
|
|
274057
|
+
"-flags",
|
|
274058
|
+
"low_delay"
|
|
274059
|
+
];
|
|
274060
|
+
if (isLinux) {
|
|
274061
|
+
args.push("-f", "pulse", "-i", "default");
|
|
274062
|
+
} else if (isMac) {
|
|
274063
|
+
args.push("-f", "avfoundation", "-i", ":default");
|
|
274064
|
+
} else if (isWindows) {
|
|
274065
|
+
args.push("-f", "dshow", "-i", "audio=default");
|
|
274066
|
+
}
|
|
274067
|
+
args.push("-ac", channels.toString(), "-ar", sampleRate.toString(), "-f", "s16le", "-flush_packets", "1", "-");
|
|
274068
|
+
return spawn8("ffmpeg", args);
|
|
274069
|
+
}
|
|
274070
|
+
if (binary === "sox") {
|
|
274071
|
+
const detection = detectAudioBinary();
|
|
274072
|
+
const binaryName = detection.path?.endsWith("rec") ? "rec" : "sox";
|
|
274073
|
+
const args = binaryName === "rec" ? [
|
|
274074
|
+
"-q",
|
|
274075
|
+
"-c",
|
|
274076
|
+
channels.toString(),
|
|
274077
|
+
"-r",
|
|
274078
|
+
sampleRate.toString(),
|
|
274079
|
+
"-b",
|
|
274080
|
+
"16",
|
|
274081
|
+
"-e",
|
|
274082
|
+
"signed",
|
|
274083
|
+
"-t",
|
|
274084
|
+
"raw",
|
|
274085
|
+
"-"
|
|
274086
|
+
] : [
|
|
274087
|
+
"-d",
|
|
274088
|
+
"-q",
|
|
274089
|
+
"-c",
|
|
274090
|
+
channels.toString(),
|
|
274091
|
+
"-r",
|
|
274092
|
+
sampleRate.toString(),
|
|
274093
|
+
"-b",
|
|
274094
|
+
"16",
|
|
274095
|
+
"-e",
|
|
274096
|
+
"signed",
|
|
274097
|
+
"-t",
|
|
274098
|
+
"raw",
|
|
274099
|
+
"-"
|
|
274100
|
+
];
|
|
274101
|
+
return spawn8(binaryName, args);
|
|
274102
|
+
}
|
|
274103
|
+
return spawn8("arecord", [
|
|
274104
|
+
"-q",
|
|
274105
|
+
"-c",
|
|
274106
|
+
channels.toString(),
|
|
274107
|
+
"-r",
|
|
274108
|
+
sampleRate.toString(),
|
|
274109
|
+
"-f",
|
|
274110
|
+
"S16_LE",
|
|
274111
|
+
"-t",
|
|
274112
|
+
"raw"
|
|
274113
|
+
]);
|
|
274114
|
+
}
|
|
274115
|
+
stop() {
|
|
274116
|
+
if (!this.isRecording && !this.process)
|
|
274117
|
+
return;
|
|
274118
|
+
this.isRecording = false;
|
|
274119
|
+
if (this.process) {
|
|
274120
|
+
try {
|
|
274121
|
+
this.process.kill("SIGTERM");
|
|
274122
|
+
} catch {}
|
|
274123
|
+
this.process = null;
|
|
274124
|
+
}
|
|
274125
|
+
}
|
|
274126
|
+
get recording() {
|
|
274127
|
+
return this.isRecording;
|
|
274128
|
+
}
|
|
274129
|
+
};
|
|
274130
|
+
});
|
|
274131
|
+
|
|
274132
|
+
// src/utils/audio/stt.ts
|
|
274133
|
+
import dns from "node:dns";
|
|
274134
|
+
async function resolveSTTKey() {
|
|
274135
|
+
const envDeepgram = process.env.DEEPGRAM_API_KEY?.trim();
|
|
274136
|
+
if (envDeepgram)
|
|
274137
|
+
return { provider: "deepgram", key: envDeepgram };
|
|
274138
|
+
const config = await loadConfig();
|
|
274139
|
+
const configDeepgram = config.providers?.deepgram?.trim();
|
|
274140
|
+
if (configDeepgram)
|
|
274141
|
+
return { provider: "deepgram", key: configDeepgram };
|
|
274142
|
+
return { provider: null, key: null };
|
|
274143
|
+
}
|
|
274144
|
+
function appendTranscript(base, next) {
|
|
274145
|
+
const trimmedBase = base.trim();
|
|
274146
|
+
const trimmedNext = next.trim();
|
|
274147
|
+
if (!trimmedBase)
|
|
274148
|
+
return trimmedNext;
|
|
274149
|
+
if (!trimmedNext)
|
|
274150
|
+
return trimmedBase;
|
|
274151
|
+
if (/^[.,!?;:]/.test(trimmedNext)) {
|
|
274152
|
+
return `${trimmedBase}${trimmedNext}`;
|
|
274153
|
+
}
|
|
274154
|
+
const baseWords = trimmedBase.split(/\s+/);
|
|
274155
|
+
const nextWords = trimmedNext.split(/\s+/);
|
|
274156
|
+
const maxOverlap = Math.min(3, baseWords.length, nextWords.length);
|
|
274157
|
+
let overlapCount = 0;
|
|
274158
|
+
const cleanWord = (w) => w.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
274159
|
+
for (let count = maxOverlap;count >= 1; count--) {
|
|
274160
|
+
const baseSlice = baseWords.slice(baseWords.length - count).map(cleanWord);
|
|
274161
|
+
const nextSlice = nextWords.slice(0, count).map(cleanWord);
|
|
274162
|
+
if (baseSlice.length === nextSlice.length && baseSlice.every((w, i) => w.length > 0 && w === nextSlice[i])) {
|
|
274163
|
+
overlapCount = count;
|
|
274164
|
+
break;
|
|
274165
|
+
}
|
|
274166
|
+
}
|
|
274167
|
+
if (overlapCount > 0) {
|
|
274168
|
+
const remainingNextWords = nextWords.slice(overlapCount);
|
|
274169
|
+
if (remainingNextWords.length === 0) {
|
|
274170
|
+
return trimmedBase;
|
|
274171
|
+
}
|
|
274172
|
+
return `${trimmedBase} ${remainingNextWords.join(" ")}`;
|
|
274173
|
+
}
|
|
274174
|
+
return `${trimmedBase} ${trimmedNext}`;
|
|
274175
|
+
}
|
|
274176
|
+
|
|
274177
|
+
class STTSession {
|
|
274178
|
+
key = null;
|
|
274179
|
+
ws = null;
|
|
274180
|
+
finalTranscript = "";
|
|
274181
|
+
interimTranscript = "";
|
|
274182
|
+
pendingChunks = [];
|
|
274183
|
+
heartbeatTimer = null;
|
|
274184
|
+
onTranscript;
|
|
274185
|
+
onError;
|
|
274186
|
+
isClosed = false;
|
|
274187
|
+
constructor(options) {
|
|
274188
|
+
this.onTranscript = options.onTranscript;
|
|
274189
|
+
this.onError = options.onError;
|
|
274190
|
+
}
|
|
274191
|
+
async init() {
|
|
274192
|
+
const resolved = await resolveSTTKey();
|
|
274193
|
+
if (!resolved.key) {
|
|
274194
|
+
this.onError(new Error("No Deepgram API key found. Set DEEPGRAM_API_KEY or configure via BYOK key menu."));
|
|
274195
|
+
return false;
|
|
274196
|
+
}
|
|
274197
|
+
this.key = resolved.key;
|
|
274198
|
+
return this.initDeepgram(this.key);
|
|
274199
|
+
}
|
|
274200
|
+
initDeepgram(key) {
|
|
274201
|
+
return new Promise((resolve) => {
|
|
274202
|
+
try {
|
|
274203
|
+
const url = "wss://api.deepgram.com/v1/listen?model=nova-2&smart_format=true&interim_results=true&endpointing=300&encoding=linear16&sample_rate=16000&channels=1";
|
|
274204
|
+
this.ws = new WebSocket(url, ["token", key]);
|
|
274205
|
+
let isHandshakeComplete = false;
|
|
274206
|
+
const handshakeTimeout = setTimeout(() => {
|
|
274207
|
+
if (!isHandshakeComplete) {
|
|
274208
|
+
isHandshakeComplete = true;
|
|
274209
|
+
try {
|
|
274210
|
+
if (this.ws && (this.ws.readyState === WebSocket.CONNECTING || this.ws.readyState === WebSocket.OPEN)) {
|
|
274211
|
+
this.ws.close();
|
|
274212
|
+
}
|
|
274213
|
+
} catch {}
|
|
274214
|
+
this.ws = null;
|
|
274215
|
+
this.onError(new Error("Deepgram WebSocket connection timed out. Check your internet connection."));
|
|
274216
|
+
resolve(false);
|
|
274217
|
+
}
|
|
274218
|
+
}, 1e4);
|
|
274219
|
+
this.ws.onopen = () => {
|
|
274220
|
+
if (isHandshakeComplete)
|
|
274221
|
+
return;
|
|
274222
|
+
isHandshakeComplete = true;
|
|
274223
|
+
clearTimeout(handshakeTimeout);
|
|
274224
|
+
this.startHeartbeat();
|
|
274225
|
+
this.flushPendingChunks();
|
|
274226
|
+
resolve(true);
|
|
274227
|
+
};
|
|
274228
|
+
this.ws.onmessage = (event) => {
|
|
274229
|
+
if (this.isClosed)
|
|
274230
|
+
return;
|
|
274231
|
+
try {
|
|
274232
|
+
const data = typeof event.data === "string" ? JSON.parse(event.data) : null;
|
|
274233
|
+
if (!data || data.type !== "Results")
|
|
274234
|
+
return;
|
|
274235
|
+
const alt = data.channel?.alternatives?.[0];
|
|
274236
|
+
const transcript = alt?.transcript?.trim();
|
|
274237
|
+
if (!transcript) {
|
|
274238
|
+
if (data.is_final) {
|
|
274239
|
+
this.interimTranscript = "";
|
|
274240
|
+
}
|
|
274241
|
+
return;
|
|
274242
|
+
}
|
|
274243
|
+
if (data.is_final) {
|
|
274244
|
+
this.finalTranscript = appendTranscript(this.finalTranscript, transcript);
|
|
274245
|
+
this.interimTranscript = "";
|
|
274246
|
+
this.onTranscript(this.finalTranscript, true);
|
|
274247
|
+
} else {
|
|
274248
|
+
this.interimTranscript = transcript;
|
|
274249
|
+
const fullText = appendTranscript(this.finalTranscript, this.interimTranscript);
|
|
274250
|
+
this.onTranscript(fullText, false);
|
|
274251
|
+
}
|
|
274252
|
+
} catch {}
|
|
274253
|
+
};
|
|
274254
|
+
this.ws.onerror = (_err) => {
|
|
274255
|
+
if (!isHandshakeComplete) {
|
|
274256
|
+
isHandshakeComplete = true;
|
|
274257
|
+
clearTimeout(handshakeTimeout);
|
|
274258
|
+
this.onError(new Error("Deepgram WebSocket connection error."));
|
|
274259
|
+
resolve(false);
|
|
274260
|
+
return;
|
|
274261
|
+
}
|
|
274262
|
+
if (!this.isClosed) {
|
|
274263
|
+
this.onError(new Error("Deepgram WebSocket connection error."));
|
|
274264
|
+
}
|
|
274265
|
+
};
|
|
274266
|
+
this.ws.onclose = () => {
|
|
274267
|
+
this.stopHeartbeat();
|
|
274268
|
+
};
|
|
274269
|
+
} catch (err) {
|
|
274270
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
274271
|
+
this.onError(error);
|
|
274272
|
+
resolve(false);
|
|
274273
|
+
}
|
|
274274
|
+
});
|
|
274275
|
+
}
|
|
274276
|
+
sendAudioChunk(chunk) {
|
|
274277
|
+
if (this.isClosed)
|
|
274278
|
+
return;
|
|
274279
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
274280
|
+
this.ws.send(chunk);
|
|
274281
|
+
} else if (!this.ws || this.ws.readyState === WebSocket.CONNECTING) {
|
|
274282
|
+
this.pendingChunks.push(chunk);
|
|
274283
|
+
}
|
|
274284
|
+
}
|
|
274285
|
+
flushPendingChunks() {
|
|
274286
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
|
|
274287
|
+
return;
|
|
274288
|
+
while (this.pendingChunks.length > 0) {
|
|
274289
|
+
const chunk = this.pendingChunks.shift();
|
|
274290
|
+
if (chunk) {
|
|
274291
|
+
try {
|
|
274292
|
+
this.ws.send(chunk);
|
|
274293
|
+
} catch {}
|
|
274294
|
+
}
|
|
274295
|
+
}
|
|
274296
|
+
}
|
|
274297
|
+
startHeartbeat() {
|
|
274298
|
+
this.stopHeartbeat();
|
|
274299
|
+
this.heartbeatTimer = setInterval(() => {
|
|
274300
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
274301
|
+
try {
|
|
274302
|
+
this.ws.send(JSON.stringify({ type: "KeepAlive" }));
|
|
274303
|
+
} catch {}
|
|
274304
|
+
}
|
|
274305
|
+
}, 4000);
|
|
274306
|
+
}
|
|
274307
|
+
stopHeartbeat() {
|
|
274308
|
+
if (this.heartbeatTimer) {
|
|
274309
|
+
clearInterval(this.heartbeatTimer);
|
|
274310
|
+
this.heartbeatTimer = null;
|
|
274311
|
+
}
|
|
274312
|
+
}
|
|
274313
|
+
async finish() {
|
|
274314
|
+
this.isClosed = true;
|
|
274315
|
+
this.stopHeartbeat();
|
|
274316
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
274317
|
+
try {
|
|
274318
|
+
this.ws.send(JSON.stringify({ type: "CloseStream" }));
|
|
274319
|
+
} catch {}
|
|
274320
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
274321
|
+
try {
|
|
274322
|
+
if (this.ws.readyState === WebSocket.OPEN) {
|
|
274323
|
+
this.ws.close();
|
|
274324
|
+
}
|
|
274325
|
+
} catch {}
|
|
274326
|
+
this.ws = null;
|
|
274327
|
+
} else if (this.ws) {
|
|
274328
|
+
try {
|
|
274329
|
+
this.ws.close();
|
|
274330
|
+
} catch {}
|
|
274331
|
+
this.ws = null;
|
|
274332
|
+
}
|
|
274333
|
+
this.pendingChunks = [];
|
|
274334
|
+
const fullText = appendTranscript(this.finalTranscript, this.interimTranscript);
|
|
274335
|
+
return fullText.trim();
|
|
274336
|
+
}
|
|
274337
|
+
cancel() {
|
|
274338
|
+
this.isClosed = true;
|
|
274339
|
+
this.stopHeartbeat();
|
|
274340
|
+
this.pendingChunks = [];
|
|
274341
|
+
if (this.ws) {
|
|
274342
|
+
try {
|
|
274343
|
+
this.ws.close();
|
|
274344
|
+
} catch {}
|
|
274345
|
+
this.ws = null;
|
|
274346
|
+
}
|
|
274347
|
+
this.finalTranscript = "";
|
|
274348
|
+
this.interimTranscript = "";
|
|
274349
|
+
}
|
|
274350
|
+
}
|
|
274351
|
+
var init_stt = __esm(() => {
|
|
274352
|
+
init_config();
|
|
274353
|
+
try {
|
|
274354
|
+
dns.setDefaultResultOrder?.("ipv4first");
|
|
274355
|
+
} catch {}
|
|
274356
|
+
});
|
|
274357
|
+
|
|
274358
|
+
// src/utils/audio/index.ts
|
|
274359
|
+
async function isVoiceConfigured() {
|
|
274360
|
+
const binaryDetection = detectAudioBinary();
|
|
274361
|
+
const sttConfig = await resolveSTTKey();
|
|
274362
|
+
if (!binaryDetection.available) {
|
|
274363
|
+
return {
|
|
274364
|
+
available: false,
|
|
274365
|
+
hasBinary: false,
|
|
274366
|
+
hasKey: Boolean(sttConfig.key),
|
|
274367
|
+
error: binaryDetection.error
|
|
274368
|
+
};
|
|
274369
|
+
}
|
|
274370
|
+
if (!sttConfig.key) {
|
|
274371
|
+
return {
|
|
274372
|
+
available: false,
|
|
274373
|
+
hasBinary: true,
|
|
274374
|
+
hasKey: false,
|
|
274375
|
+
binary: binaryDetection.binary,
|
|
274376
|
+
error: "Deepgram API key not configured."
|
|
274377
|
+
};
|
|
274378
|
+
}
|
|
274379
|
+
return {
|
|
274380
|
+
available: true,
|
|
274381
|
+
hasBinary: true,
|
|
274382
|
+
hasKey: true,
|
|
274383
|
+
binary: binaryDetection.binary,
|
|
274384
|
+
provider: "deepgram"
|
|
274385
|
+
};
|
|
274386
|
+
}
|
|
274387
|
+
function getVoiceSetupNotice(status) {
|
|
274388
|
+
if (!status.hasBinary) {
|
|
274389
|
+
return [
|
|
274390
|
+
"Voice dictation requires an audio recording tool.",
|
|
274391
|
+
"",
|
|
274392
|
+
"Install:",
|
|
274393
|
+
" • macOS: brew install ffmpeg",
|
|
274394
|
+
" • Linux: sudo apt install ffmpeg"
|
|
274395
|
+
].join(`
|
|
274396
|
+
`);
|
|
274397
|
+
}
|
|
274398
|
+
return [
|
|
274399
|
+
"Voice dictation requires a Deepgram API key for real-time live streaming.",
|
|
274400
|
+
"",
|
|
274401
|
+
"Get a free key ($200 credit) at https://console.deepgram.com",
|
|
274402
|
+
"",
|
|
274403
|
+
"Configure key:",
|
|
274404
|
+
" december key deepgram <key>"
|
|
274405
|
+
].join(`
|
|
274406
|
+
`);
|
|
274407
|
+
}
|
|
274408
|
+
async function startVoiceDictation(options) {
|
|
274409
|
+
const configCheck = await isVoiceConfigured();
|
|
274410
|
+
if (!configCheck.available) {
|
|
274411
|
+
options.onError(new Error(configCheck.error || "Voice dictation is not available."));
|
|
274412
|
+
return null;
|
|
274413
|
+
}
|
|
274414
|
+
const stt = new STTSession({
|
|
274415
|
+
onTranscript: options.onTranscript,
|
|
274416
|
+
onError: options.onError
|
|
274417
|
+
});
|
|
274418
|
+
const initialized = await stt.init();
|
|
274419
|
+
if (!initialized) {
|
|
274420
|
+
return null;
|
|
274421
|
+
}
|
|
274422
|
+
const recorder = new AudioRecorder;
|
|
274423
|
+
recorder.on("data", (chunk) => {
|
|
274424
|
+
stt.sendAudioChunk(chunk);
|
|
274425
|
+
});
|
|
274426
|
+
recorder.on("error", (err) => {
|
|
274427
|
+
options.onError(err);
|
|
274428
|
+
stt.cancel();
|
|
274429
|
+
});
|
|
274430
|
+
const started = recorder.start();
|
|
274431
|
+
if (!started) {
|
|
274432
|
+
stt.cancel();
|
|
274433
|
+
return null;
|
|
274434
|
+
}
|
|
274435
|
+
return {
|
|
274436
|
+
stop: async () => {
|
|
274437
|
+
recorder.stop();
|
|
274438
|
+
return await stt.finish();
|
|
274439
|
+
},
|
|
274440
|
+
cancel: () => {
|
|
274441
|
+
recorder.stop();
|
|
274442
|
+
stt.cancel();
|
|
274443
|
+
},
|
|
274444
|
+
get isRecording() {
|
|
274445
|
+
return recorder.recording;
|
|
274446
|
+
}
|
|
274447
|
+
};
|
|
274448
|
+
}
|
|
274449
|
+
var init_audio2 = __esm(() => {
|
|
274450
|
+
init_detector2();
|
|
274451
|
+
init_recorder();
|
|
274452
|
+
init_stt();
|
|
274453
|
+
init_detector2();
|
|
274454
|
+
init_recorder();
|
|
274455
|
+
});
|
|
274456
|
+
|
|
272861
274457
|
// src/hooks/use-agent-session.ts
|
|
272862
274458
|
var exports_use_agent_session = {};
|
|
272863
274459
|
__export(exports_use_agent_session, {
|
|
@@ -272993,7 +274589,9 @@ function useAgentSession({
|
|
|
272993
274589
|
settingsSteeringMode,
|
|
272994
274590
|
setSettingsSteeringMode,
|
|
272995
274591
|
settingsFollowUpMode,
|
|
272996
|
-
setSettingsFollowUpMode
|
|
274592
|
+
setSettingsFollowUpMode,
|
|
274593
|
+
settingsBrowserVisible,
|
|
274594
|
+
setSettingsBrowserVisible
|
|
272997
274595
|
} = state;
|
|
272998
274596
|
const [expandCommands, setExpandCommands] = import_react71.useState(false);
|
|
272999
274597
|
const toggleExpandCommands = import_react71.useCallback(() => {
|
|
@@ -274046,6 +275644,36 @@ ${decStatus}
|
|
|
274046
275644
|
}
|
|
274047
275645
|
return;
|
|
274048
275646
|
}
|
|
275647
|
+
if (text.trim() === "/voice") {
|
|
275648
|
+
await Promise.resolve().then(() => init_audio2());
|
|
275649
|
+
const status = await isVoiceConfigured();
|
|
275650
|
+
if (!status.available) {
|
|
275651
|
+
const userMsg = {
|
|
275652
|
+
id: getNextMsgId(),
|
|
275653
|
+
role: "user",
|
|
275654
|
+
displayText: "/voice",
|
|
275655
|
+
text: "/voice"
|
|
275656
|
+
};
|
|
275657
|
+
const noticeMsg = {
|
|
275658
|
+
id: getNextMsgId(),
|
|
275659
|
+
role: "assistant",
|
|
275660
|
+
blocks: [
|
|
275661
|
+
{
|
|
275662
|
+
type: "text",
|
|
275663
|
+
content: getVoiceSetupNotice(status)
|
|
275664
|
+
}
|
|
275665
|
+
]
|
|
275666
|
+
};
|
|
275667
|
+
setStaticMessages((prev) => [
|
|
275668
|
+
...prev,
|
|
275669
|
+
...useCliStore.getState().activeMessages,
|
|
275670
|
+
userMsg,
|
|
275671
|
+
noticeMsg
|
|
275672
|
+
]);
|
|
275673
|
+
setActiveMessages([]);
|
|
275674
|
+
return;
|
|
275675
|
+
}
|
|
275676
|
+
}
|
|
274049
275677
|
if (text.trim() === "/copy") {
|
|
274050
275678
|
try {
|
|
274051
275679
|
await init_src7();
|
|
@@ -274433,6 +276061,7 @@ ${decStatus}
|
|
|
274433
276061
|
setSettingsThinkingLevel(config.thinkingLevel ?? "auto");
|
|
274434
276062
|
setSettingsSteeringMode(config.steeringMode ?? "all");
|
|
274435
276063
|
setSettingsFollowUpMode(config.followUpMode ?? "all");
|
|
276064
|
+
setSettingsBrowserVisible(config.browserVisible !== false);
|
|
274436
276065
|
setAuthMode("settings_main");
|
|
274437
276066
|
});
|
|
274438
276067
|
return;
|
|
@@ -274837,7 +276466,46 @@ If it doesn't open automatically, please click here:
|
|
|
274837
276466
|
setStaticMessages
|
|
274838
276467
|
]);
|
|
274839
276468
|
const handleContextSelect = () => {};
|
|
276469
|
+
const handleStartVoice = import_react71.useCallback(async (callbacks) => {
|
|
276470
|
+
await Promise.resolve().then(() => init_audio2());
|
|
276471
|
+
const status = await isVoiceConfigured();
|
|
276472
|
+
if (!status.hasBinary) {
|
|
276473
|
+
const userMsg = {
|
|
276474
|
+
id: getNextMsgId(),
|
|
276475
|
+
role: "user",
|
|
276476
|
+
displayText: "/voice",
|
|
276477
|
+
text: "/voice"
|
|
276478
|
+
};
|
|
276479
|
+
const noticeMsg = {
|
|
276480
|
+
id: getNextMsgId(),
|
|
276481
|
+
role: "assistant",
|
|
276482
|
+
blocks: [
|
|
276483
|
+
{
|
|
276484
|
+
type: "text",
|
|
276485
|
+
content: getVoiceSetupNotice(status)
|
|
276486
|
+
}
|
|
276487
|
+
]
|
|
276488
|
+
};
|
|
276489
|
+
setStaticMessages((prev) => [
|
|
276490
|
+
...prev,
|
|
276491
|
+
...useCliStore.getState().activeMessages,
|
|
276492
|
+
userMsg,
|
|
276493
|
+
noticeMsg
|
|
276494
|
+
]);
|
|
276495
|
+
setActiveMessages([]);
|
|
276496
|
+
return null;
|
|
276497
|
+
}
|
|
276498
|
+
if (!status.hasKey) {
|
|
276499
|
+
useCliStore.getState().setSelectedProvider("deepgram");
|
|
276500
|
+
useCliStore.getState().setApiKey("");
|
|
276501
|
+
useCliStore.getState().setAuthError(null);
|
|
276502
|
+
useCliStore.getState().setAuthMode("byok_key");
|
|
276503
|
+
return null;
|
|
276504
|
+
}
|
|
276505
|
+
return await startVoiceDictation(callbacks);
|
|
276506
|
+
}, [setStaticMessages, setActiveMessages]);
|
|
274840
276507
|
return {
|
|
276508
|
+
onStartVoice: handleStartVoice,
|
|
274841
276509
|
currentPlannedPrompt,
|
|
274842
276510
|
setCurrentPlannedPrompt,
|
|
274843
276511
|
currentPlanText,
|
|
@@ -274932,6 +276600,8 @@ If it doesn't open automatically, please click here:
|
|
|
274932
276600
|
setSettingsSteeringMode,
|
|
274933
276601
|
settingsFollowUpMode,
|
|
274934
276602
|
setSettingsFollowUpMode,
|
|
276603
|
+
settingsBrowserVisible,
|
|
276604
|
+
setSettingsBrowserVisible,
|
|
274935
276605
|
handleSubmit,
|
|
274936
276606
|
sessionRepository,
|
|
274937
276607
|
handleSettingsMainSelect,
|
|
@@ -275008,7 +276678,7 @@ var init_use_agent_session = __esm(async () => {
|
|
|
275008
276678
|
|
|
275009
276679
|
// src/index.ts
|
|
275010
276680
|
init_package();
|
|
275011
|
-
import
|
|
276681
|
+
import path41 from "node:path";
|
|
275012
276682
|
|
|
275013
276683
|
// src/args.ts
|
|
275014
276684
|
import { parseArgs } from "node:util";
|
|
@@ -275146,12 +276816,12 @@ Options:
|
|
|
275146
276816
|
}
|
|
275147
276817
|
// src/constants/release-highlights.json
|
|
275148
276818
|
var release_highlights_default = {
|
|
275149
|
-
version: "0.4.
|
|
275150
|
-
title: "What's New in v0.4.
|
|
276819
|
+
version: "0.4.6",
|
|
276820
|
+
title: "What's New in v0.4.6",
|
|
275151
276821
|
bullets: [
|
|
275152
|
-
"*(
|
|
275153
|
-
"*(
|
|
275154
|
-
"*(
|
|
276822
|
+
"*(providers,scripts)* Fix anthropic user key auth and update email scripts",
|
|
276823
|
+
"*(docs,web)* Clean redundant png assets, add marathi readme, and update landing hero",
|
|
276824
|
+
"*(docs)* Update brand blue banner, navbar links, and profile cards"
|
|
275155
276825
|
],
|
|
275156
276826
|
url: "https://trydecember.com/docs/changelog"
|
|
275157
276827
|
};
|
|
@@ -275161,6 +276831,7 @@ init_env_sanitizer();
|
|
|
275161
276831
|
init_env_sanitizer();
|
|
275162
276832
|
init_commands();
|
|
275163
276833
|
init_headless_runner();
|
|
276834
|
+
init_workspace_trust();
|
|
275164
276835
|
purgeProjectEnvApiKeys();
|
|
275165
276836
|
async function readPipedStdin() {
|
|
275166
276837
|
if (process.stdin.isTTY)
|
|
@@ -275279,6 +276950,11 @@ To define project-specific instructions, create an AGENTS.md file in your projec
|
|
|
275279
276950
|
}
|
|
275280
276951
|
parsedArgs.prompt = question;
|
|
275281
276952
|
}
|
|
276953
|
+
await Promise.resolve().then(() => init_workspace_trust());
|
|
276954
|
+
await ensureWorkspaceTrust({
|
|
276955
|
+
targetDir: parsedArgs.scope,
|
|
276956
|
+
isHeadless: Boolean(parsedArgs.prompt)
|
|
276957
|
+
});
|
|
275282
276958
|
if (parsedArgs.prompt) {
|
|
275283
276959
|
const [
|
|
275284
276960
|
{ AgentHarness },
|
|
@@ -275352,7 +277028,7 @@ To define project-specific instructions, create an AGENTS.md file in your projec
|
|
|
275352
277028
|
sessionRepository,
|
|
275353
277029
|
sessionId,
|
|
275354
277030
|
runtime: "cli",
|
|
275355
|
-
workspaceDir: parsedArgs.scope ?
|
|
277031
|
+
workspaceDir: parsedArgs.scope ? path41.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
|
|
275356
277032
|
thinkingLevel: config.thinkingLevel || "auto",
|
|
275357
277033
|
steeringMode: config.steeringMode || "all",
|
|
275358
277034
|
followUpMode: config.followUpMode || "all"
|
|
@@ -275456,7 +277132,7 @@ Executing Headless Task: "${parsedArgs.prompt}"
|
|
|
275456
277132
|
sessionRepository,
|
|
275457
277133
|
sessionId,
|
|
275458
277134
|
runtime: "cli",
|
|
275459
|
-
workspaceDir: parsedArgs.scope ?
|
|
277135
|
+
workspaceDir: parsedArgs.scope ? path41.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
|
|
275460
277136
|
thinkingLevel: config.thinkingLevel || "auto",
|
|
275461
277137
|
steeringMode: config.steeringMode || "all",
|
|
275462
277138
|
followUpMode: config.followUpMode || "all"
|
|
@@ -275541,6 +277217,8 @@ Executing Headless Task: "${parsedArgs.prompt}"
|
|
|
275541
277217
|
}
|
|
275542
277218
|
main().catch(console.error);
|
|
275543
277219
|
export {
|
|
277220
|
+
addTrustedWorkspace,
|
|
277221
|
+
ensureWorkspaceTrust,
|
|
275544
277222
|
getHelpText,
|
|
275545
277223
|
handleAuthCommand,
|
|
275546
277224
|
handleDoctorCommand,
|
|
@@ -275550,9 +277228,11 @@ export {
|
|
|
275550
277228
|
handleLogoutCommand,
|
|
275551
277229
|
handleSwitchCommand,
|
|
275552
277230
|
handleUpdateCommand,
|
|
277231
|
+
isWorkspaceTrusted,
|
|
275553
277232
|
parseCliArgs,
|
|
275554
277233
|
purgeProjectEnvApiKeys,
|
|
275555
277234
|
readPipedStdin,
|
|
277235
|
+
resolveWorkspaceRoot,
|
|
275556
277236
|
restoreConsole,
|
|
275557
277237
|
runHeadlessTask,
|
|
275558
277238
|
suppressConsole
|