lupacode 1.0.0 → 1.0.2
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 +39 -11
- package/bin/lupacode +4 -1
- package/dist/index.js +466 -127
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,30 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
AI-powered terminal coding assistant.
|
|
4
4
|
|
|
5
|
-
## Install
|
|
5
|
+
## Install globally
|
|
6
|
+
|
|
7
|
+
Requires [Bun](https://bun.sh) >= 1.0.
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
# Remove any old global install first (important on Windows)
|
|
11
|
+
npm uninstall -g lupacode
|
|
12
|
+
|
|
13
|
+
# Install latest
|
|
10
14
|
bun install -g lupacode
|
|
15
|
+
# or
|
|
16
|
+
npm install -g lupacode
|
|
11
17
|
```
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
Then run from **any directory**:
|
|
14
20
|
|
|
15
21
|
```bash
|
|
16
22
|
lupacode
|
|
17
23
|
```
|
|
18
24
|
|
|
25
|
+
Verify the version in the footer (e.g. `v1.0.2`).
|
|
26
|
+
|
|
19
27
|
## Configuration
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
|
29
|
+
Global config lives in `~/.lupacode/` (same on every machine/user):
|
|
30
|
+
|
|
31
|
+
| File | Purpose |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `~/.lupacode/.env` | Optional env overrides (see below) |
|
|
34
|
+
| `~/.lupacode/auth.json` | Saved login token (created by `/login`) |
|
|
35
|
+
| `~/.lupacode/preferences.json` | Theme, model, mode preferences |
|
|
24
36
|
|
|
25
|
-
|
|
37
|
+
| Env var | Default | Description |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `API_URL` | `https://lupacodeserver-production.up.railway.app` | LupaCode API server |
|
|
26
40
|
|
|
27
|
-
|
|
41
|
+
Example `~/.lupacode/.env` (only if you need a custom server):
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
```env
|
|
44
|
+
API_URL=https://lupacodeserver-production.up.railway.app
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The CLI does **not** read `.env` from your current project folder, so running `lupacode` in a repo with `API_URL=http://localhost:3000` will not break the global install.
|
|
48
|
+
|
|
49
|
+
### Authentication
|
|
50
|
+
|
|
51
|
+
Type `/login` in the CLI — it opens your browser for Clerk OAuth.
|
|
52
|
+
|
|
53
|
+
## Local development (monorepo)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
bun run dev:cli
|
|
57
|
+
```
|
|
30
58
|
|
|
31
|
-
|
|
59
|
+
Dev mode uses Bun's normal `.env` loading from the repo root for local server testing.
|
package/bin/lupacode
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import dotenv from "dotenv";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
// Load user config only — never cwd .env, so `lupacode` works the same in every directory.
|
|
7
|
+
dotenv.config({ path: join(homedir(), ".lupacode", ".env"), quiet: true });
|
|
5
8
|
|
|
6
9
|
// @ts-expect-error - dist/index.js is a Bun bundle with no exports
|
|
7
10
|
await import("../dist/index.js");
|
package/dist/index.js
CHANGED
|
@@ -85693,6 +85693,12 @@ var toolInputSchemas = {
|
|
|
85693
85693
|
command: exports_external.string().describe("Shell command to run"),
|
|
85694
85694
|
description: exports_external.string().optional().describe("Short description of the command"),
|
|
85695
85695
|
timeout: exports_external.number().optional().describe("Timeout in milliseconds")
|
|
85696
|
+
}),
|
|
85697
|
+
deleteFile: exports_external.object({
|
|
85698
|
+
path: exports_external.string().describe("Relative path to the file to delete")
|
|
85699
|
+
}),
|
|
85700
|
+
webSearch: exports_external.object({
|
|
85701
|
+
query: exports_external.string().describe("Search query for web search")
|
|
85696
85702
|
})
|
|
85697
85703
|
};
|
|
85698
85704
|
// node_modules/ai/node_modules/@ai-sdk/provider/dist/index.js
|
|
@@ -93401,10 +93407,18 @@ var readOnlyToolContracts = {
|
|
|
93401
93407
|
grep: tool({
|
|
93402
93408
|
description: "Search file contents with a regular expression under the current project directory.",
|
|
93403
93409
|
inputSchema: toolInputSchemas.grep
|
|
93410
|
+
}),
|
|
93411
|
+
webSearch: tool({
|
|
93412
|
+
description: "Search the web for up-to-date information on any topic.",
|
|
93413
|
+
inputSchema: toolInputSchemas.webSearch
|
|
93404
93414
|
})
|
|
93405
93415
|
};
|
|
93406
93416
|
var buildToolContracts = {
|
|
93407
93417
|
...readOnlyToolContracts,
|
|
93418
|
+
deleteFile: tool({
|
|
93419
|
+
description: "Delete a file under the current project directory.",
|
|
93420
|
+
inputSchema: toolInputSchemas.deleteFile
|
|
93421
|
+
}),
|
|
93408
93422
|
writeFile: tool({
|
|
93409
93423
|
description: "Create or overwrite a file under the current project directory.",
|
|
93410
93424
|
inputSchema: toolInputSchemas.writeFile
|
|
@@ -93421,7 +93435,7 @@ var buildToolContracts = {
|
|
|
93421
93435
|
// src/lib/shared/models.ts
|
|
93422
93436
|
var SUPPORTED_CHAT_MODELS = [
|
|
93423
93437
|
{
|
|
93424
|
-
id: "claude-sonnet-
|
|
93438
|
+
id: "claude-sonnet-5",
|
|
93425
93439
|
provider: "anthropic",
|
|
93426
93440
|
pricing: {
|
|
93427
93441
|
inputUsdPerMillionTokens: 3,
|
|
@@ -93429,7 +93443,7 @@ var SUPPORTED_CHAT_MODELS = [
|
|
|
93429
93443
|
}
|
|
93430
93444
|
},
|
|
93431
93445
|
{
|
|
93432
|
-
id: "claude-
|
|
93446
|
+
id: "claude-haiku-4-5",
|
|
93433
93447
|
provider: "anthropic",
|
|
93434
93448
|
pricing: {
|
|
93435
93449
|
inputUsdPerMillionTokens: 1,
|
|
@@ -93437,7 +93451,7 @@ var SUPPORTED_CHAT_MODELS = [
|
|
|
93437
93451
|
}
|
|
93438
93452
|
},
|
|
93439
93453
|
{
|
|
93440
|
-
id: "claude-opus-4-
|
|
93454
|
+
id: "claude-opus-4-8",
|
|
93441
93455
|
provider: "anthropic",
|
|
93442
93456
|
pricing: {
|
|
93443
93457
|
inputUsdPerMillionTokens: 5,
|
|
@@ -93445,7 +93459,7 @@ var SUPPORTED_CHAT_MODELS = [
|
|
|
93445
93459
|
}
|
|
93446
93460
|
},
|
|
93447
93461
|
{
|
|
93448
|
-
id: "gpt-
|
|
93462
|
+
id: "gpt-5.5",
|
|
93449
93463
|
provider: "openai",
|
|
93450
93464
|
pricing: {
|
|
93451
93465
|
inputUsdPerMillionTokens: 2.5,
|
|
@@ -93453,7 +93467,7 @@ var SUPPORTED_CHAT_MODELS = [
|
|
|
93453
93467
|
}
|
|
93454
93468
|
},
|
|
93455
93469
|
{
|
|
93456
|
-
id: "gpt-4
|
|
93470
|
+
id: "gpt-5.4-mini",
|
|
93457
93471
|
provider: "openai",
|
|
93458
93472
|
pricing: {
|
|
93459
93473
|
inputUsdPerMillionTokens: 0.75,
|
|
@@ -93461,7 +93475,7 @@ var SUPPORTED_CHAT_MODELS = [
|
|
|
93461
93475
|
}
|
|
93462
93476
|
},
|
|
93463
93477
|
{
|
|
93464
|
-
id: "gpt-4
|
|
93478
|
+
id: "gpt-5.4-nano",
|
|
93465
93479
|
provider: "openai",
|
|
93466
93480
|
pricing: {
|
|
93467
93481
|
inputUsdPerMillionTokens: 0.2,
|
|
@@ -93469,19 +93483,19 @@ var SUPPORTED_CHAT_MODELS = [
|
|
|
93469
93483
|
}
|
|
93470
93484
|
},
|
|
93471
93485
|
{
|
|
93472
|
-
id: "
|
|
93486
|
+
id: "cohere/north-mini-code:free",
|
|
93473
93487
|
provider: "openrouter",
|
|
93474
93488
|
pricing: {
|
|
93475
|
-
inputUsdPerMillionTokens: 0
|
|
93476
|
-
outputUsdPerMillionTokens:
|
|
93489
|
+
inputUsdPerMillionTokens: 0,
|
|
93490
|
+
outputUsdPerMillionTokens: 0
|
|
93477
93491
|
}
|
|
93478
93492
|
},
|
|
93479
93493
|
{
|
|
93480
|
-
id: "
|
|
93481
|
-
provider: "
|
|
93494
|
+
id: "deepseek/deepseek-chat-v3",
|
|
93495
|
+
provider: "openrouter",
|
|
93482
93496
|
pricing: {
|
|
93483
|
-
inputUsdPerMillionTokens: 0.
|
|
93484
|
-
outputUsdPerMillionTokens:
|
|
93497
|
+
inputUsdPerMillionTokens: 0.27,
|
|
93498
|
+
outputUsdPerMillionTokens: 1.1
|
|
93485
93499
|
}
|
|
93486
93500
|
},
|
|
93487
93501
|
{
|
|
@@ -93493,11 +93507,67 @@ var SUPPORTED_CHAT_MODELS = [
|
|
|
93493
93507
|
}
|
|
93494
93508
|
},
|
|
93495
93509
|
{
|
|
93496
|
-
id: "deepseek/deepseek-
|
|
93510
|
+
id: "deepseek/deepseek-v3.2",
|
|
93497
93511
|
provider: "openrouter",
|
|
93498
93512
|
pricing: {
|
|
93499
|
-
inputUsdPerMillionTokens: 0.
|
|
93500
|
-
outputUsdPerMillionTokens:
|
|
93513
|
+
inputUsdPerMillionTokens: 0.2288,
|
|
93514
|
+
outputUsdPerMillionTokens: 0.3432
|
|
93515
|
+
}
|
|
93516
|
+
},
|
|
93517
|
+
{
|
|
93518
|
+
id: "deepseek/deepseek-v4-flash",
|
|
93519
|
+
provider: "openrouter",
|
|
93520
|
+
pricing: {
|
|
93521
|
+
inputUsdPerMillionTokens: 0.1,
|
|
93522
|
+
outputUsdPerMillionTokens: 0.2
|
|
93523
|
+
}
|
|
93524
|
+
},
|
|
93525
|
+
{
|
|
93526
|
+
id: "deepseek/deepseek-v4-pro",
|
|
93527
|
+
provider: "openrouter",
|
|
93528
|
+
pricing: {
|
|
93529
|
+
inputUsdPerMillionTokens: 0.435,
|
|
93530
|
+
outputUsdPerMillionTokens: 0.87
|
|
93531
|
+
}
|
|
93532
|
+
},
|
|
93533
|
+
{
|
|
93534
|
+
id: "nvidia/nemotron-3-super-120b-a12b:free",
|
|
93535
|
+
provider: "openrouter",
|
|
93536
|
+
pricing: {
|
|
93537
|
+
inputUsdPerMillionTokens: 0,
|
|
93538
|
+
outputUsdPerMillionTokens: 0
|
|
93539
|
+
}
|
|
93540
|
+
},
|
|
93541
|
+
{
|
|
93542
|
+
id: "nvidia/nemotron-3-ultra-550b-a55b:free",
|
|
93543
|
+
provider: "openrouter",
|
|
93544
|
+
pricing: {
|
|
93545
|
+
inputUsdPerMillionTokens: 0,
|
|
93546
|
+
outputUsdPerMillionTokens: 0
|
|
93547
|
+
}
|
|
93548
|
+
},
|
|
93549
|
+
{
|
|
93550
|
+
id: "poolside/laguna-m.1:free",
|
|
93551
|
+
provider: "openrouter",
|
|
93552
|
+
pricing: {
|
|
93553
|
+
inputUsdPerMillionTokens: 0,
|
|
93554
|
+
outputUsdPerMillionTokens: 0
|
|
93555
|
+
}
|
|
93556
|
+
},
|
|
93557
|
+
{
|
|
93558
|
+
id: "qwen/qwen3-coder",
|
|
93559
|
+
provider: "openrouter",
|
|
93560
|
+
pricing: {
|
|
93561
|
+
inputUsdPerMillionTokens: 0.3,
|
|
93562
|
+
outputUsdPerMillionTokens: 1.2
|
|
93563
|
+
}
|
|
93564
|
+
},
|
|
93565
|
+
{
|
|
93566
|
+
id: "qwen/qwen3-coder-next",
|
|
93567
|
+
provider: "openrouter",
|
|
93568
|
+
pricing: {
|
|
93569
|
+
inputUsdPerMillionTokens: 0.11,
|
|
93570
|
+
outputUsdPerMillionTokens: 0.8
|
|
93501
93571
|
}
|
|
93502
93572
|
},
|
|
93503
93573
|
{
|
|
@@ -93508,6 +93578,22 @@ var SUPPORTED_CHAT_MODELS = [
|
|
|
93508
93578
|
outputUsdPerMillionTokens: 0
|
|
93509
93579
|
}
|
|
93510
93580
|
},
|
|
93581
|
+
{
|
|
93582
|
+
id: "gemini-3-flash-lite",
|
|
93583
|
+
provider: "google",
|
|
93584
|
+
pricing: {
|
|
93585
|
+
inputUsdPerMillionTokens: 0.25,
|
|
93586
|
+
outputUsdPerMillionTokens: 1.5
|
|
93587
|
+
}
|
|
93588
|
+
},
|
|
93589
|
+
{
|
|
93590
|
+
id: "gemini-3-flash-preview",
|
|
93591
|
+
provider: "google",
|
|
93592
|
+
pricing: {
|
|
93593
|
+
inputUsdPerMillionTokens: 0.5,
|
|
93594
|
+
outputUsdPerMillionTokens: 3
|
|
93595
|
+
}
|
|
93596
|
+
},
|
|
93511
93597
|
{
|
|
93512
93598
|
id: "llama-3.3-70b-versatile",
|
|
93513
93599
|
provider: "groq",
|
|
@@ -93555,7 +93641,7 @@ function findSupportedChatModel(modelId) {
|
|
|
93555
93641
|
function isFreeModel(model) {
|
|
93556
93642
|
return model.pricing.inputUsdPerMillionTokens === 0 && model.pricing.outputUsdPerMillionTokens === 0;
|
|
93557
93643
|
}
|
|
93558
|
-
var DEFAULT_CHAT_MODEL_ID = "
|
|
93644
|
+
var DEFAULT_CHAT_MODEL_ID = "deepseek/deepseek-v4-flash";
|
|
93559
93645
|
// src/lib/shared/domains.ts
|
|
93560
93646
|
var DOMAINS = [
|
|
93561
93647
|
{
|
|
@@ -93823,11 +93909,8 @@ function DialogProvider({ children }) {
|
|
|
93823
93909
|
const [dialogStack, setDialogStack] = import_react24.useState([]);
|
|
93824
93910
|
const { push, pop } = useKeyboardLayer();
|
|
93825
93911
|
const close = import_react24.useCallback(() => {
|
|
93826
|
-
setDialogStack((prev) =>
|
|
93827
|
-
|
|
93828
|
-
pop("dialog");
|
|
93829
|
-
return next;
|
|
93830
|
-
});
|
|
93912
|
+
setDialogStack((prev) => prev.slice(0, -1));
|
|
93913
|
+
pop("dialog");
|
|
93831
93914
|
}, [pop]);
|
|
93832
93915
|
const open = import_react24.useCallback((config2) => {
|
|
93833
93916
|
setDialogStack((prev) => [...prev, config2]);
|
|
@@ -93994,7 +94077,7 @@ function RootLayout() {
|
|
|
93994
94077
|
}
|
|
93995
94078
|
|
|
93996
94079
|
// src/screens/home.tsx
|
|
93997
|
-
var
|
|
94080
|
+
var import_react42 = __toESM(require_react(), 1);
|
|
93998
94081
|
|
|
93999
94082
|
// src/components/header.tsx
|
|
94000
94083
|
function Header() {
|
|
@@ -94022,7 +94105,7 @@ function Header() {
|
|
|
94022
94105
|
}
|
|
94023
94106
|
|
|
94024
94107
|
// src/components/input-bar.tsx
|
|
94025
|
-
var
|
|
94108
|
+
var import_react40 = __toESM(require_react(), 1);
|
|
94026
94109
|
import { readdir } from "fs/promises";
|
|
94027
94110
|
import { isAbsolute as isAbsolute3, relative, resolve as resolve5 } from "path";
|
|
94028
94111
|
|
|
@@ -94524,6 +94607,12 @@ function clearAuth() {
|
|
|
94524
94607
|
} catch {}
|
|
94525
94608
|
}
|
|
94526
94609
|
|
|
94610
|
+
// src/lib/env.ts
|
|
94611
|
+
var DEFAULT_API_URL = "https://lupacodeserver-production.up.railway.app";
|
|
94612
|
+
function getApiUrl() {
|
|
94613
|
+
return process.env.API_URL ?? DEFAULT_API_URL;
|
|
94614
|
+
}
|
|
94615
|
+
|
|
94527
94616
|
// src/lib/api-client.ts
|
|
94528
94617
|
var RETRY_ATTEMPTS = 3;
|
|
94529
94618
|
var RETRY_BASE_DELAY_MS = 1000;
|
|
@@ -94546,7 +94635,7 @@ async function withRetry(fn, options) {
|
|
|
94546
94635
|
}
|
|
94547
94636
|
throw lastError;
|
|
94548
94637
|
}
|
|
94549
|
-
var apiClient = hc(
|
|
94638
|
+
var apiClient = hc(getApiUrl(), {
|
|
94550
94639
|
fetch: async (input, init) => {
|
|
94551
94640
|
const headers = new Headers(init?.headers);
|
|
94552
94641
|
const auth = getAuth();
|
|
@@ -96333,7 +96422,7 @@ var import_react35 = __toESM(require_react(), 1);
|
|
|
96333
96422
|
// package.json
|
|
96334
96423
|
var package_default2 = {
|
|
96335
96424
|
name: "lupacode",
|
|
96336
|
-
version: "1.0.
|
|
96425
|
+
version: "1.0.2",
|
|
96337
96426
|
description: "AI-powered terminal coding assistant",
|
|
96338
96427
|
type: "module",
|
|
96339
96428
|
bin: {
|
|
@@ -96415,7 +96504,7 @@ async function checkForUpdate(currentVersion) {
|
|
|
96415
96504
|
const cache = readCache();
|
|
96416
96505
|
if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL_MS) {
|
|
96417
96506
|
cachedLatest = cache.latest;
|
|
96418
|
-
return cache.latest
|
|
96507
|
+
return isNewer(cache.latest, currentVersion) ? cache.latest : null;
|
|
96419
96508
|
}
|
|
96420
96509
|
try {
|
|
96421
96510
|
const res = await fetch("https://registry.npmjs.org/lupacode/latest");
|
|
@@ -96426,13 +96515,32 @@ async function checkForUpdate(currentVersion) {
|
|
|
96426
96515
|
const result = { latest, checkedAt: Date.now() };
|
|
96427
96516
|
writeCache(result);
|
|
96428
96517
|
cachedLatest = latest;
|
|
96429
|
-
return latest
|
|
96518
|
+
return isNewer(latest, currentVersion) ? latest : null;
|
|
96430
96519
|
} catch {
|
|
96431
96520
|
return null;
|
|
96432
96521
|
}
|
|
96433
96522
|
}
|
|
96523
|
+
function isNewer(latest, current) {
|
|
96524
|
+
if (!latest)
|
|
96525
|
+
return false;
|
|
96526
|
+
const a = parseSemver(latest);
|
|
96527
|
+
const b2 = parseSemver(current);
|
|
96528
|
+
if (!a || !b2)
|
|
96529
|
+
return false;
|
|
96530
|
+
if (a.major !== b2.major)
|
|
96531
|
+
return a.major > b2.major;
|
|
96532
|
+
if (a.minor !== b2.minor)
|
|
96533
|
+
return a.minor > b2.minor;
|
|
96534
|
+
return a.patch > b2.patch;
|
|
96535
|
+
}
|
|
96536
|
+
function parseSemver(v2) {
|
|
96537
|
+
const m2 = /^v?(\d+)\.(\d+)\.(\d+)/.exec(v2.trim());
|
|
96538
|
+
if (!m2 || m2[1] == null || m2[2] == null || m2[3] == null)
|
|
96539
|
+
return null;
|
|
96540
|
+
return { major: Number(m2[1]), minor: Number(m2[2]), patch: Number(m2[3]) };
|
|
96541
|
+
}
|
|
96434
96542
|
function hasUpdate() {
|
|
96435
|
-
return cachedLatest
|
|
96543
|
+
return isNewer(cachedLatest, cachedCurrent);
|
|
96436
96544
|
}
|
|
96437
96545
|
|
|
96438
96546
|
// src/components/dialogs/settings-dialog.tsx
|
|
@@ -96460,7 +96568,7 @@ var SettingsDialogContent = () => {
|
|
|
96460
96568
|
break;
|
|
96461
96569
|
case "model":
|
|
96462
96570
|
dialog.open({ title: "Select Model", children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ModelsDialogContent, {
|
|
96463
|
-
models: SUPPORTED_CHAT_MODELS,
|
|
96571
|
+
models: [...SUPPORTED_CHAT_MODELS],
|
|
96464
96572
|
onSelectModel: setModel
|
|
96465
96573
|
}, undefined, false, undefined, this) });
|
|
96466
96574
|
break;
|
|
@@ -96549,7 +96657,7 @@ var shortcuts = [
|
|
|
96549
96657
|
{ key: "ctrl+d", description: "Switch domain" },
|
|
96550
96658
|
{ key: "ctrl+s", description: "Open settings" },
|
|
96551
96659
|
{ key: "ctrl+r", description: "Regenerate last assistant response" },
|
|
96552
|
-
{ key: "e", description: "Edit last user message" },
|
|
96660
|
+
{ key: "ctrl+e", description: "Edit last user message" },
|
|
96553
96661
|
{ key: "escape", description: "Interrupt response / Cancel / Close dialog" }
|
|
96554
96662
|
];
|
|
96555
96663
|
var KeyboardHelpDialogContent = () => {
|
|
@@ -96616,6 +96724,9 @@ function StatusBar() {
|
|
|
96616
96724
|
}, undefined, true, undefined, this);
|
|
96617
96725
|
}
|
|
96618
96726
|
|
|
96727
|
+
// src/components/command-menu/commands.tsx
|
|
96728
|
+
var import_react37 = __toESM(require_react(), 1);
|
|
96729
|
+
|
|
96619
96730
|
// ../../node_modules/open/index.js
|
|
96620
96731
|
import process9 from "process";
|
|
96621
96732
|
import path4 from "path";
|
|
@@ -97229,9 +97340,6 @@ var open_default = open;
|
|
|
97229
97340
|
// src/lib/oauth.ts
|
|
97230
97341
|
var LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
97231
97342
|
var POLL_INTERVAL_MS = 1000;
|
|
97232
|
-
function getApiUrl() {
|
|
97233
|
-
return process.env.API_URL ?? "https://lupacodeserver-production.up.railway.app";
|
|
97234
|
-
}
|
|
97235
97343
|
async function performLogin() {
|
|
97236
97344
|
const apiUrl = getApiUrl();
|
|
97237
97345
|
const initRes = await fetch(`${apiUrl}/auth/init`, { method: "POST" });
|
|
@@ -97440,6 +97548,85 @@ var COMMANDS = [
|
|
|
97440
97548
|
}
|
|
97441
97549
|
}
|
|
97442
97550
|
},
|
|
97551
|
+
{
|
|
97552
|
+
name: "rename",
|
|
97553
|
+
description: "Rename the current session",
|
|
97554
|
+
value: "/rename",
|
|
97555
|
+
action: (ctx) => {
|
|
97556
|
+
if (!ctx.sessionId) {
|
|
97557
|
+
ctx.toast.show({ variant: "error", message: "No active session" });
|
|
97558
|
+
return;
|
|
97559
|
+
}
|
|
97560
|
+
const RenameDialog = () => {
|
|
97561
|
+
const [text2, setText] = import_react37.useState("");
|
|
97562
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
97563
|
+
flexDirection: "column",
|
|
97564
|
+
gap: 1,
|
|
97565
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("input", {
|
|
97566
|
+
focused: true,
|
|
97567
|
+
placeholder: "New session name",
|
|
97568
|
+
onContentChange: (event) => setText(String(event.value ?? event)),
|
|
97569
|
+
onSubmit: () => {
|
|
97570
|
+
const name23 = text2.trim();
|
|
97571
|
+
if (!name23)
|
|
97572
|
+
return;
|
|
97573
|
+
const baseUrl = getApiUrl();
|
|
97574
|
+
const token = getAuth()?.token ?? "";
|
|
97575
|
+
fetch(`${baseUrl}/sessions/${ctx.sessionId}`, {
|
|
97576
|
+
method: "PATCH",
|
|
97577
|
+
headers: {
|
|
97578
|
+
"Content-Type": "application/json",
|
|
97579
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
97580
|
+
},
|
|
97581
|
+
body: JSON.stringify({ title: name23 })
|
|
97582
|
+
}).then((res) => {
|
|
97583
|
+
if (!res.ok)
|
|
97584
|
+
throw new Error("Failed to rename");
|
|
97585
|
+
ctx.toast.show({ variant: "success", message: `Renamed to "${name23}"` });
|
|
97586
|
+
}).catch(() => {
|
|
97587
|
+
ctx.toast.show({ variant: "error", message: "Failed to rename session" });
|
|
97588
|
+
});
|
|
97589
|
+
ctx.dialog.close();
|
|
97590
|
+
}
|
|
97591
|
+
}, undefined, false, undefined, this)
|
|
97592
|
+
}, undefined, false, undefined, this);
|
|
97593
|
+
};
|
|
97594
|
+
ctx.dialog.open({ title: "Rename Session", children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(RenameDialog, {}, undefined, false, undefined, this) });
|
|
97595
|
+
}
|
|
97596
|
+
},
|
|
97597
|
+
{
|
|
97598
|
+
name: "export",
|
|
97599
|
+
description: "Export conversation as markdown",
|
|
97600
|
+
value: "/export",
|
|
97601
|
+
action: (ctx) => {
|
|
97602
|
+
if (!ctx.messages || ctx.messages.length === 0) {
|
|
97603
|
+
ctx.toast.show({ variant: "error", message: "No messages to export" });
|
|
97604
|
+
return;
|
|
97605
|
+
}
|
|
97606
|
+
const lines = [];
|
|
97607
|
+
lines.push("# Chat Export");
|
|
97608
|
+
lines.push("");
|
|
97609
|
+
for (const msg of ctx.messages) {
|
|
97610
|
+
const role = msg.role === "user" ? "User" : "Assistant";
|
|
97611
|
+
const text2 = msg.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
97612
|
+
if (!text2)
|
|
97613
|
+
continue;
|
|
97614
|
+
lines.push(`**${role}:**`);
|
|
97615
|
+
lines.push("");
|
|
97616
|
+
lines.push(text2);
|
|
97617
|
+
lines.push("");
|
|
97618
|
+
}
|
|
97619
|
+
const content = lines.join(`
|
|
97620
|
+
`);
|
|
97621
|
+
const filename = `chat-export-${Date.now()}.md`;
|
|
97622
|
+
try {
|
|
97623
|
+
Bun.write(filename, content);
|
|
97624
|
+
ctx.toast.show({ variant: "success", message: `Exported to ${filename}` });
|
|
97625
|
+
} catch {
|
|
97626
|
+
ctx.toast.show({ variant: "error", message: "Failed to write export file" });
|
|
97627
|
+
}
|
|
97628
|
+
}
|
|
97629
|
+
},
|
|
97443
97630
|
{
|
|
97444
97631
|
name: "exit",
|
|
97445
97632
|
description: "Quit the application",
|
|
@@ -97520,15 +97707,15 @@ function CommandMenu({
|
|
|
97520
97707
|
}
|
|
97521
97708
|
|
|
97522
97709
|
// src/components/command-menu/use-command-menu.ts
|
|
97523
|
-
var
|
|
97710
|
+
var import_react38 = __toESM(require_react(), 1);
|
|
97524
97711
|
function useCommandMenu() {
|
|
97525
|
-
const [textValue, setTextValue] =
|
|
97526
|
-
const [selectedIndex, setSelectedIndex] =
|
|
97527
|
-
const [showCommandMenu, setShowCommandMenu] =
|
|
97528
|
-
const scrollRef =
|
|
97712
|
+
const [textValue, setTextValue] = import_react38.useState("");
|
|
97713
|
+
const [selectedIndex, setSelectedIndex] = import_react38.useState(0);
|
|
97714
|
+
const [showCommandMenu, setShowCommandMenu] = import_react38.useState(false);
|
|
97715
|
+
const scrollRef = import_react38.useRef(null);
|
|
97529
97716
|
const { push, pop, isTopLayer } = useKeyboardLayer();
|
|
97530
97717
|
const commandQuery = showCommandMenu && textValue.startsWith("/") ? textValue.slice(1) : "";
|
|
97531
|
-
const filteredCommands =
|
|
97718
|
+
const filteredCommands = import_react38.useMemo(() => getFilteredCommands(commandQuery), [commandQuery]);
|
|
97532
97719
|
const close = () => {
|
|
97533
97720
|
setShowCommandMenu(false);
|
|
97534
97721
|
pop("command");
|
|
@@ -97785,21 +97972,21 @@ var TEXTAREA_KEY_BINDINGS = [
|
|
|
97785
97972
|
{ name: "return", shift: true, action: "newline" },
|
|
97786
97973
|
{ name: "enter", shift: true, action: "newline" }
|
|
97787
97974
|
];
|
|
97788
|
-
function InputBar({ onSubmit, disabled = false }) {
|
|
97975
|
+
function InputBar({ onSubmit, disabled = false, sessionId, messages }) {
|
|
97789
97976
|
const { mode, toggleMode, setMode, setModel, domain: domain2, setDomain } = usePromptConfig();
|
|
97790
|
-
const textareaRef =
|
|
97791
|
-
const onSubmitRef =
|
|
97792
|
-
const activeMentionRef =
|
|
97793
|
-
const mentionScrollRef =
|
|
97977
|
+
const textareaRef = import_react40.useRef(null);
|
|
97978
|
+
const onSubmitRef = import_react40.useRef(() => {});
|
|
97979
|
+
const activeMentionRef = import_react40.useRef(null);
|
|
97980
|
+
const mentionScrollRef = import_react40.useRef(null);
|
|
97794
97981
|
const renderer = useRenderer();
|
|
97795
97982
|
const toast = useToast();
|
|
97796
97983
|
const navigate = useNavigate();
|
|
97797
97984
|
const dialog = useDialog();
|
|
97798
97985
|
const { colors } = useTheme();
|
|
97799
97986
|
const { isTopLayer, setResponder, push, pop } = useKeyboardLayer();
|
|
97800
|
-
const [activeMention, setActiveMention] =
|
|
97801
|
-
const [mentionCandidates, setMentionCandidates] =
|
|
97802
|
-
const [mentionSelectedIndex, setMentionSelectedIndex] =
|
|
97987
|
+
const [activeMention, setActiveMention] = import_react40.useState(null);
|
|
97988
|
+
const [mentionCandidates, setMentionCandidates] = import_react40.useState([]);
|
|
97989
|
+
const [mentionSelectedIndex, setMentionSelectedIndex] = import_react40.useState(0);
|
|
97803
97990
|
const {
|
|
97804
97991
|
showCommandMenu,
|
|
97805
97992
|
commandQuery,
|
|
@@ -97810,13 +97997,13 @@ function InputBar({ onSubmit, disabled = false }) {
|
|
|
97810
97997
|
setSelectedIndex
|
|
97811
97998
|
} = useCommandMenu();
|
|
97812
97999
|
const showMentionMenu = activeMention !== null;
|
|
97813
|
-
const closeMentionMenu =
|
|
98000
|
+
const closeMentionMenu = import_react40.useCallback(() => {
|
|
97814
98001
|
activeMentionRef.current = null;
|
|
97815
98002
|
setActiveMention(null);
|
|
97816
98003
|
setMentionCandidates([]);
|
|
97817
98004
|
pop("mention");
|
|
97818
98005
|
}, [pop]);
|
|
97819
|
-
const syncMentionMenu =
|
|
98006
|
+
const syncMentionMenu = import_react40.useCallback((text2, cursorOffset) => {
|
|
97820
98007
|
const nextMention = findActiveMention(text2, cursorOffset);
|
|
97821
98008
|
const previousMention = activeMentionRef.current;
|
|
97822
98009
|
const mentionChanged = previousMention?.start !== nextMention?.start || previousMention?.end !== nextMention?.end || previousMention?.query !== nextMention?.query;
|
|
@@ -97839,7 +98026,7 @@ function InputBar({ onSubmit, disabled = false }) {
|
|
|
97839
98026
|
mentionScrollRef.current?.scrollTo(0);
|
|
97840
98027
|
}
|
|
97841
98028
|
}, [closeMentionMenu, push]);
|
|
97842
|
-
const handleTextareaContentChange =
|
|
98029
|
+
const handleTextareaContentChange = import_react40.useCallback(() => {
|
|
97843
98030
|
const textarea = textareaRef.current;
|
|
97844
98031
|
if (!textarea)
|
|
97845
98032
|
return;
|
|
@@ -97847,7 +98034,7 @@ function InputBar({ onSubmit, disabled = false }) {
|
|
|
97847
98034
|
handleContentChange(textarea.plainText);
|
|
97848
98035
|
syncMentionMenu(text2, textarea.cursorOffset);
|
|
97849
98036
|
}, [handleContentChange, syncMentionMenu]);
|
|
97850
|
-
const handleSubmit =
|
|
98037
|
+
const handleSubmit = import_react40.useCallback(() => {
|
|
97851
98038
|
if (disabled)
|
|
97852
98039
|
return;
|
|
97853
98040
|
const textarea = textareaRef.current;
|
|
@@ -97859,7 +98046,7 @@ function InputBar({ onSubmit, disabled = false }) {
|
|
|
97859
98046
|
onSubmit(text2);
|
|
97860
98047
|
textarea.setText("");
|
|
97861
98048
|
}, [disabled, onSubmit]);
|
|
97862
|
-
const handleMentionExecute =
|
|
98049
|
+
const handleMentionExecute = import_react40.useCallback((index) => {
|
|
97863
98050
|
const textarea = textareaRef.current;
|
|
97864
98051
|
const mention = activeMentionRef.current;
|
|
97865
98052
|
const candidate = mentionCandidates[index];
|
|
@@ -97871,13 +98058,13 @@ function InputBar({ onSubmit, disabled = false }) {
|
|
|
97871
98058
|
textarea.cursorOffset = mention.start + insertion.length + 1;
|
|
97872
98059
|
syncMentionMenu(nextText, textarea.cursorOffset);
|
|
97873
98060
|
}, [mentionCandidates, syncMentionMenu]);
|
|
97874
|
-
const handleTextareaCursorChange =
|
|
98061
|
+
const handleTextareaCursorChange = import_react40.useCallback(() => {
|
|
97875
98062
|
const textarea = textareaRef.current;
|
|
97876
98063
|
if (!textarea)
|
|
97877
98064
|
return;
|
|
97878
98065
|
syncMentionMenu(textarea.plainText, textarea.cursorOffset);
|
|
97879
98066
|
}, [syncMentionMenu]);
|
|
97880
|
-
const handleCommand =
|
|
98067
|
+
const handleCommand = import_react40.useCallback((command) => {
|
|
97881
98068
|
const textarea = textareaRef.current;
|
|
97882
98069
|
if (!textarea || !command)
|
|
97883
98070
|
return;
|
|
@@ -97891,17 +98078,19 @@ function InputBar({ onSubmit, disabled = false }) {
|
|
|
97891
98078
|
mode,
|
|
97892
98079
|
setMode,
|
|
97893
98080
|
setModel,
|
|
97894
|
-
setDomain
|
|
98081
|
+
setDomain,
|
|
98082
|
+
sessionId,
|
|
98083
|
+
messages
|
|
97895
98084
|
});
|
|
97896
98085
|
} else {
|
|
97897
98086
|
textarea.insertText(command.value + " ");
|
|
97898
98087
|
}
|
|
97899
98088
|
}, [renderer, toast, dialog, navigate, mode, setMode, setModel, setDomain]);
|
|
97900
|
-
const handleCommandExecute =
|
|
98089
|
+
const handleCommandExecute = import_react40.useCallback((index) => {
|
|
97901
98090
|
const command = resolveCommand(index);
|
|
97902
98091
|
handleCommand(command);
|
|
97903
98092
|
}, [resolveCommand, handleCommand]);
|
|
97904
|
-
|
|
98093
|
+
import_react40.useEffect(() => {
|
|
97905
98094
|
if (!activeMention) {
|
|
97906
98095
|
setMentionCandidates([]);
|
|
97907
98096
|
return;
|
|
@@ -97924,7 +98113,7 @@ function InputBar({ onSubmit, disabled = false }) {
|
|
|
97924
98113
|
ignore = true;
|
|
97925
98114
|
};
|
|
97926
98115
|
}, [activeMention]);
|
|
97927
|
-
|
|
98116
|
+
import_react40.useEffect(() => {
|
|
97928
98117
|
const textare = textareaRef.current;
|
|
97929
98118
|
if (!textare)
|
|
97930
98119
|
return;
|
|
@@ -97982,7 +98171,7 @@ function InputBar({ onSubmit, disabled = false }) {
|
|
|
97982
98171
|
});
|
|
97983
98172
|
}
|
|
97984
98173
|
});
|
|
97985
|
-
|
|
98174
|
+
import_react40.useEffect(() => {
|
|
97986
98175
|
setResponder("base", () => {
|
|
97987
98176
|
if (disabled)
|
|
97988
98177
|
return false;
|
|
@@ -98100,7 +98289,7 @@ function InputBar({ onSubmit, disabled = false }) {
|
|
|
98100
98289
|
function Home() {
|
|
98101
98290
|
const navigate = useNavigate();
|
|
98102
98291
|
const { mode, model, domain: domain2 } = usePromptConfig();
|
|
98103
|
-
const handleSubmit =
|
|
98292
|
+
const handleSubmit = import_react42.useCallback((text2) => {
|
|
98104
98293
|
navigate("/sessions/new", { state: { message: text2, mode, model, domain: domain2 } });
|
|
98105
98294
|
}, [navigate, mode, model, domain2]);
|
|
98106
98295
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
@@ -98178,7 +98367,7 @@ function Home() {
|
|
|
98178
98367
|
}
|
|
98179
98368
|
|
|
98180
98369
|
// src/screens/new-session.tsx
|
|
98181
|
-
var
|
|
98370
|
+
var import_react45 = __toESM(require_react(), 1);
|
|
98182
98371
|
|
|
98183
98372
|
// src/components/messages/error-message.tsx
|
|
98184
98373
|
function ErrorMessage({ message: message2 }) {
|
|
@@ -98369,7 +98558,45 @@ function prettyMilliseconds(milliseconds, options) {
|
|
|
98369
98558
|
}
|
|
98370
98559
|
|
|
98371
98560
|
// src/components/messages/bot-message.tsx
|
|
98372
|
-
var globalSyntaxStyle = SyntaxStyle.
|
|
98561
|
+
var globalSyntaxStyle = SyntaxStyle.fromStyles({
|
|
98562
|
+
default: { fg: "#D4D4D4" },
|
|
98563
|
+
keyword: { fg: "#569CD6" },
|
|
98564
|
+
"keyword.control": { fg: "#C586C0" },
|
|
98565
|
+
"keyword.import": { fg: "#C586C0" },
|
|
98566
|
+
"keyword.function": { fg: "#DCDCAA" },
|
|
98567
|
+
string: { fg: "#CE9178" },
|
|
98568
|
+
"string.special": { fg: "#CE9178" },
|
|
98569
|
+
number: { fg: "#B5CEA8" },
|
|
98570
|
+
"number.float": { fg: "#B5CEA8" },
|
|
98571
|
+
comment: { fg: "#6A9955", italic: true },
|
|
98572
|
+
function: { fg: "#DCDCAA" },
|
|
98573
|
+
"function.call": { fg: "#DCDCAA" },
|
|
98574
|
+
"function.method": { fg: "#DCDCAA" },
|
|
98575
|
+
type: { fg: "#4EC9B0" },
|
|
98576
|
+
"type.builtin": { fg: "#4EC9B0" },
|
|
98577
|
+
variable: { fg: "#9CDCFE" },
|
|
98578
|
+
"variable.parameter": { fg: "#9CDCFE" },
|
|
98579
|
+
"variable.builtin": { fg: "#9CDCFE" },
|
|
98580
|
+
constant: { fg: "#4FC1FF" },
|
|
98581
|
+
"constant.builtin": { fg: "#569CD6" },
|
|
98582
|
+
property: { fg: "#9CDCFE" },
|
|
98583
|
+
operator: { fg: "#D4D4D4" },
|
|
98584
|
+
"punctuation.delimiter": { fg: "#D4D4D4" },
|
|
98585
|
+
"punctuation.bracket": { fg: "#D4D4D4" },
|
|
98586
|
+
boolean: { fg: "#569CD6" },
|
|
98587
|
+
label: { fg: "#DCDCAA" },
|
|
98588
|
+
"markup.heading": { fg: "#569CD6", bold: true },
|
|
98589
|
+
"markup.italic": { italic: true },
|
|
98590
|
+
"markup.strong": { fg: "#DCDCAA", bold: true },
|
|
98591
|
+
"markup.strikethrough": { fg: "#888888", italic: true, dim: true },
|
|
98592
|
+
"markup.raw": { fg: "#CE9178" },
|
|
98593
|
+
"markup.link": { fg: "#569CD6", underline: true },
|
|
98594
|
+
"markup.link.label": { fg: "#569CD6", underline: true },
|
|
98595
|
+
"markup.link.url": { fg: "#4FC1FF" },
|
|
98596
|
+
"markup.list": { fg: "#569CD6" },
|
|
98597
|
+
"markup.quote": { fg: "#6A9955", italic: true },
|
|
98598
|
+
conceal: { fg: "#555555" }
|
|
98599
|
+
});
|
|
98373
98600
|
function formatToolName(name23) {
|
|
98374
98601
|
return name23.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (c) => c.toUpperCase());
|
|
98375
98602
|
}
|
|
@@ -98383,6 +98610,79 @@ function formatToolArgs(tc) {
|
|
|
98383
98610
|
return String(tc.input);
|
|
98384
98611
|
return Object.values(tc.input).map(String).join(" ");
|
|
98385
98612
|
}
|
|
98613
|
+
function isBashTool(tc) {
|
|
98614
|
+
const name23 = tc.type === "dynamic-tool" ? tc.toolName : tc.type.slice("tool-".length);
|
|
98615
|
+
return name23 === "bash";
|
|
98616
|
+
}
|
|
98617
|
+
function isToolRunning(tc) {
|
|
98618
|
+
return tc.state !== "output-available" && tc.state !== "output-error";
|
|
98619
|
+
}
|
|
98620
|
+
function renderToolCall(tc, colors, j2, renderKey) {
|
|
98621
|
+
const toolName = tc.type === "dynamic-tool" ? tc.toolName : tc.type.slice("tool-".length);
|
|
98622
|
+
const running = isToolRunning(tc);
|
|
98623
|
+
const done = tc.state === "output-available";
|
|
98624
|
+
if (isBashTool(tc)) {
|
|
98625
|
+
const command = typeof tc.input === "object" && tc.input != null ? String(tc.input.command ?? "") : "";
|
|
98626
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
98627
|
+
width: "100%",
|
|
98628
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
98629
|
+
attributes: TextAttributes.DIM,
|
|
98630
|
+
children: [
|
|
98631
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("em", {
|
|
98632
|
+
fg: colors.success,
|
|
98633
|
+
attributes: TextAttributes.BOLD,
|
|
98634
|
+
children: "$"
|
|
98635
|
+
}, undefined, false, undefined, this),
|
|
98636
|
+
" ",
|
|
98637
|
+
command,
|
|
98638
|
+
running ? "" : "",
|
|
98639
|
+
tc.state === "output-error" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("em", {
|
|
98640
|
+
fg: colors.error,
|
|
98641
|
+
children: [
|
|
98642
|
+
" ",
|
|
98643
|
+
"\u2716",
|
|
98644
|
+
" ",
|
|
98645
|
+
tc.errorText
|
|
98646
|
+
]
|
|
98647
|
+
}, undefined, true, undefined, this) : null
|
|
98648
|
+
]
|
|
98649
|
+
}, undefined, true, undefined, this)
|
|
98650
|
+
}, renderKey, false, undefined, this);
|
|
98651
|
+
}
|
|
98652
|
+
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
98653
|
+
width: "100%",
|
|
98654
|
+
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
98655
|
+
attributes: TextAttributes.DIM,
|
|
98656
|
+
children: [
|
|
98657
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("em", {
|
|
98658
|
+
fg: colors.info,
|
|
98659
|
+
children: [
|
|
98660
|
+
formatToolName(toolName),
|
|
98661
|
+
":"
|
|
98662
|
+
]
|
|
98663
|
+
}, undefined, true, undefined, this),
|
|
98664
|
+
" ",
|
|
98665
|
+
formatToolArgs(tc),
|
|
98666
|
+
running ? "..." : done ? " \u2713" : "",
|
|
98667
|
+
tc.state === "output-error" ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("em", {
|
|
98668
|
+
fg: colors.error,
|
|
98669
|
+
children: [
|
|
98670
|
+
" ",
|
|
98671
|
+
"\u2716",
|
|
98672
|
+
" ",
|
|
98673
|
+
tc.errorText
|
|
98674
|
+
]
|
|
98675
|
+
}, undefined, true, undefined, this) : null
|
|
98676
|
+
]
|
|
98677
|
+
}, undefined, true, undefined, this)
|
|
98678
|
+
}, renderKey, false, undefined, this);
|
|
98679
|
+
}
|
|
98680
|
+
function formatTokens(usage) {
|
|
98681
|
+
const total = usage.totalTokens ?? usage.inputTokens ?? usage.outputTokens;
|
|
98682
|
+
if (total == null)
|
|
98683
|
+
return "";
|
|
98684
|
+
return `${total.toLocaleString()} tok`;
|
|
98685
|
+
}
|
|
98386
98686
|
function mergeReasoningParts(parts) {
|
|
98387
98687
|
const reasoningParts = parts.filter((p) => p.type === "reasoning");
|
|
98388
98688
|
if (reasoningParts.length <= 1)
|
|
@@ -98420,6 +98720,7 @@ function BotMessage({
|
|
|
98420
98720
|
model,
|
|
98421
98721
|
mode,
|
|
98422
98722
|
durationMs,
|
|
98723
|
+
usage,
|
|
98423
98724
|
streaming = false
|
|
98424
98725
|
}) {
|
|
98425
98726
|
const { colors } = useTheme();
|
|
@@ -98445,22 +98746,7 @@ function BotMessage({
|
|
|
98445
98746
|
flexDirection: "column",
|
|
98446
98747
|
children: group.parts.map((part, j2) => {
|
|
98447
98748
|
const tc = part;
|
|
98448
|
-
|
|
98449
|
-
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
98450
|
-
attributes: TextAttributes.DIM,
|
|
98451
|
-
children: [
|
|
98452
|
-
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("em", {
|
|
98453
|
-
fg: colors.info,
|
|
98454
|
-
children: [
|
|
98455
|
-
formatToolName(toolName),
|
|
98456
|
-
":"
|
|
98457
|
-
]
|
|
98458
|
-
}, undefined, true, undefined, this),
|
|
98459
|
-
formatToolArgs(tc),
|
|
98460
|
-
tc.state !== "output-available" && tc.state !== "output-error" ? "..." : "",
|
|
98461
|
-
tc.state === "output-error" ? ` ${tc.errorText}` : ""
|
|
98462
|
-
]
|
|
98463
|
-
}, tc.toolCallId, true, undefined, this);
|
|
98749
|
+
return renderToolCall(tc, colors, j2, tc.toolCallId);
|
|
98464
98750
|
})
|
|
98465
98751
|
}, undefined, false, undefined, this)
|
|
98466
98752
|
}, group.key, false, undefined, this);
|
|
@@ -98493,7 +98779,6 @@ function BotMessage({
|
|
|
98493
98779
|
}, `reasoning-${j2}`, false, undefined, this);
|
|
98494
98780
|
}
|
|
98495
98781
|
if (isToolPart(part)) {
|
|
98496
|
-
const toolName = part.type === "dynamic-tool" ? part.toolName : part.type.slice("tool-".length);
|
|
98497
98782
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
98498
98783
|
border: ["left"],
|
|
98499
98784
|
borderColor: colors.thinkingBorder,
|
|
@@ -98503,21 +98788,7 @@ function BotMessage({
|
|
|
98503
98788
|
},
|
|
98504
98789
|
width: "100%",
|
|
98505
98790
|
paddingX: 2,
|
|
98506
|
-
children:
|
|
98507
|
-
attributes: TextAttributes.DIM,
|
|
98508
|
-
children: [
|
|
98509
|
-
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("em", {
|
|
98510
|
-
fg: colors.info,
|
|
98511
|
-
children: [
|
|
98512
|
-
formatToolName(toolName),
|
|
98513
|
-
":"
|
|
98514
|
-
]
|
|
98515
|
-
}, undefined, true, undefined, this),
|
|
98516
|
-
formatToolArgs(part),
|
|
98517
|
-
part.state !== "output-available" && part.state !== "output-error" ? "..." : "",
|
|
98518
|
-
part.state === "output-error" ? ` ${part.errorText}` : ""
|
|
98519
|
-
]
|
|
98520
|
-
}, undefined, true, undefined, this)
|
|
98791
|
+
children: renderToolCall(part, colors, j2, part.toolCallId)
|
|
98521
98792
|
}, part.toolCallId, false, undefined, this);
|
|
98522
98793
|
}
|
|
98523
98794
|
if (part.type === "text") {
|
|
@@ -98527,7 +98798,17 @@ function BotMessage({
|
|
|
98527
98798
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("markdown", {
|
|
98528
98799
|
content: part.text,
|
|
98529
98800
|
syntaxStyle: globalSyntaxStyle,
|
|
98530
|
-
conceal: true
|
|
98801
|
+
conceal: true,
|
|
98802
|
+
concealCode: true,
|
|
98803
|
+
streaming,
|
|
98804
|
+
tableOptions: {
|
|
98805
|
+
style: "grid",
|
|
98806
|
+
borders: true,
|
|
98807
|
+
outerBorder: true,
|
|
98808
|
+
borderStyle: "rounded",
|
|
98809
|
+
cellPadding: 1,
|
|
98810
|
+
borderColor: "#555555"
|
|
98811
|
+
}
|
|
98531
98812
|
}, undefined, false, undefined, this)
|
|
98532
98813
|
}, `text-${j2}`, false, undefined, this);
|
|
98533
98814
|
}
|
|
@@ -98576,6 +98857,19 @@ function BotMessage({
|
|
|
98576
98857
|
children: prettyMilliseconds(durationMs)
|
|
98577
98858
|
}, undefined, false, undefined, this)
|
|
98578
98859
|
]
|
|
98860
|
+
}, undefined, true, undefined, this),
|
|
98861
|
+
usage && formatTokens(usage) && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
|
|
98862
|
+
children: [
|
|
98863
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
98864
|
+
attributes: TextAttributes.DIM,
|
|
98865
|
+
fg: colors.dimSeparator,
|
|
98866
|
+
children: ">"
|
|
98867
|
+
}, undefined, false, undefined, this),
|
|
98868
|
+
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
|
|
98869
|
+
attributes: TextAttributes.DIM,
|
|
98870
|
+
children: formatTokens(usage)
|
|
98871
|
+
}, undefined, false, undefined, this)
|
|
98872
|
+
]
|
|
98579
98873
|
}, undefined, true, undefined, this)
|
|
98580
98874
|
]
|
|
98581
98875
|
}, undefined, true, undefined, this)
|
|
@@ -100570,7 +100864,9 @@ function SessionShell({
|
|
|
100570
100864
|
onSubmit,
|
|
100571
100865
|
inputDisabled = false,
|
|
100572
100866
|
loading = false,
|
|
100573
|
-
interruptible = false
|
|
100867
|
+
interruptible = false,
|
|
100868
|
+
sessionId,
|
|
100869
|
+
messages
|
|
100574
100870
|
}) {
|
|
100575
100871
|
const { mode } = usePromptConfig();
|
|
100576
100872
|
return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
@@ -100595,7 +100891,9 @@ function SessionShell({
|
|
|
100595
100891
|
flexShrink: 0,
|
|
100596
100892
|
children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(InputBar, {
|
|
100597
100893
|
onSubmit,
|
|
100598
|
-
disabled: inputDisabled
|
|
100894
|
+
disabled: inputDisabled,
|
|
100895
|
+
sessionId,
|
|
100896
|
+
messages
|
|
100599
100897
|
}, undefined, false, undefined, this)
|
|
100600
100898
|
}, undefined, false, undefined, this),
|
|
100601
100899
|
/* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
|
|
@@ -100684,17 +100982,17 @@ function NewSession() {
|
|
|
100684
100982
|
const navigate = useNavigate();
|
|
100685
100983
|
const location = useLocation();
|
|
100686
100984
|
const toast = useToast();
|
|
100687
|
-
const hasStartedRef =
|
|
100688
|
-
const state =
|
|
100985
|
+
const hasStartedRef = import_react45.useRef(false);
|
|
100986
|
+
const state = import_react45.useMemo(() => {
|
|
100689
100987
|
const parsed = newSessionStateSchema.safeParse(location.state);
|
|
100690
100988
|
return parsed.success ? parsed.data : null;
|
|
100691
100989
|
}, [location.state]);
|
|
100692
|
-
|
|
100990
|
+
import_react45.useEffect(() => {
|
|
100693
100991
|
if (!state?.message) {
|
|
100694
100992
|
navigate("/", { replace: true });
|
|
100695
100993
|
}
|
|
100696
100994
|
}, [state, navigate]);
|
|
100697
|
-
|
|
100995
|
+
import_react45.useEffect(() => {
|
|
100698
100996
|
if (!state || hasStartedRef.current)
|
|
100699
100997
|
return;
|
|
100700
100998
|
hasStartedRef.current = true;
|
|
@@ -100742,13 +101040,13 @@ function NewSession() {
|
|
|
100742
101040
|
}
|
|
100743
101041
|
|
|
100744
101042
|
// src/screens/sessions.tsx
|
|
100745
|
-
var
|
|
101043
|
+
var import_react54 = __toESM(require_react(), 1);
|
|
100746
101044
|
|
|
100747
101045
|
// src/hooks/use-chat.ts
|
|
100748
|
-
var
|
|
101046
|
+
var import_react52 = __toESM(require_react(), 1);
|
|
100749
101047
|
|
|
100750
101048
|
// ../../node_modules/@ai-sdk/react/dist/index.js
|
|
100751
|
-
var
|
|
101049
|
+
var import_react46 = __toESM(require_react(), 1);
|
|
100752
101050
|
|
|
100753
101051
|
// ../../node_modules/@ai-sdk/react/node_modules/@ai-sdk/provider/dist/index.js
|
|
100754
101052
|
var marker23 = "vercel.ai.error";
|
|
@@ -105501,11 +105799,11 @@ var AbstractChat = class {
|
|
|
105501
105799
|
|
|
105502
105800
|
// ../../node_modules/@ai-sdk/react/dist/index.js
|
|
105503
105801
|
var import_throttleit = __toESM(require_throttleit(), 1);
|
|
105504
|
-
var import_react46 = __toESM(require_react(), 1);
|
|
105505
105802
|
var import_react47 = __toESM(require_react(), 1);
|
|
105506
105803
|
var import_react48 = __toESM(require_react(), 1);
|
|
105507
105804
|
var import_react49 = __toESM(require_react(), 1);
|
|
105508
105805
|
var import_react50 = __toESM(require_react(), 1);
|
|
105806
|
+
var import_react51 = __toESM(require_react(), 1);
|
|
105509
105807
|
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
105510
105808
|
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
105511
105809
|
var __accessCheck = (obj, member, msg) => {
|
|
@@ -105642,7 +105940,7 @@ function useChat({
|
|
|
105642
105940
|
resume = false,
|
|
105643
105941
|
...options
|
|
105644
105942
|
} = {}) {
|
|
105645
|
-
const callbacksRef =
|
|
105943
|
+
const callbacksRef = import_react46.useRef(!("chat" in options) ? {
|
|
105646
105944
|
onToolCall: options.onToolCall,
|
|
105647
105945
|
onData: options.onData,
|
|
105648
105946
|
onFinish: options.onFinish,
|
|
@@ -105681,22 +105979,22 @@ function useChat({
|
|
|
105681
105979
|
return (_c = (_b17 = (_a29 = callbacksRef.current).sendAutomaticallyWhen) == null ? undefined : _b17.call(_a29, arg)) != null ? _c : false;
|
|
105682
105980
|
}
|
|
105683
105981
|
};
|
|
105684
|
-
const chatRef =
|
|
105982
|
+
const chatRef = import_react46.useRef("chat" in options ? options.chat : new Chat(optionsWithCallbacks));
|
|
105685
105983
|
const shouldRecreateChat = "chat" in options && options.chat !== chatRef.current || "id" in options && chatRef.current.id !== options.id;
|
|
105686
105984
|
if (shouldRecreateChat) {
|
|
105687
105985
|
chatRef.current = "chat" in options ? options.chat : new Chat(optionsWithCallbacks);
|
|
105688
105986
|
}
|
|
105689
|
-
const subscribeToMessages =
|
|
105690
|
-
const messages =
|
|
105691
|
-
const status =
|
|
105692
|
-
const error51 =
|
|
105693
|
-
const setMessages =
|
|
105987
|
+
const subscribeToMessages = import_react46.useCallback((update) => chatRef.current["~registerMessagesCallback"](update, throttleWaitMs), [throttleWaitMs, chatRef.current.id]);
|
|
105988
|
+
const messages = import_react46.useSyncExternalStore(subscribeToMessages, () => chatRef.current.messages, () => chatRef.current.messages);
|
|
105989
|
+
const status = import_react46.useSyncExternalStore(chatRef.current["~registerStatusCallback"], () => chatRef.current.status, () => chatRef.current.status);
|
|
105990
|
+
const error51 = import_react46.useSyncExternalStore(chatRef.current["~registerErrorCallback"], () => chatRef.current.error, () => chatRef.current.error);
|
|
105991
|
+
const setMessages = import_react46.useCallback((messagesParam) => {
|
|
105694
105992
|
if (typeof messagesParam === "function") {
|
|
105695
105993
|
messagesParam = messagesParam(chatRef.current.messages);
|
|
105696
105994
|
}
|
|
105697
105995
|
chatRef.current.messages = messagesParam;
|
|
105698
105996
|
}, [chatRef]);
|
|
105699
|
-
|
|
105997
|
+
import_react46.useEffect(() => {
|
|
105700
105998
|
if (resume) {
|
|
105701
105999
|
chatRef.current.resumeStream();
|
|
105702
106000
|
}
|
|
@@ -105719,7 +106017,7 @@ function useChat({
|
|
|
105719
106017
|
}
|
|
105720
106018
|
|
|
105721
106019
|
// src/lib/local-tools.ts
|
|
105722
|
-
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir2, stat, writeFile as writeFile2 } from "fs/promises";
|
|
106020
|
+
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir2, stat, writeFile as writeFile2, unlink as unlink2 } from "fs/promises";
|
|
105723
106021
|
import { dirname as dirname2, isAbsolute as isAbsolute5, join as join5, relative as relative2, resolve as resolve7 } from "path";
|
|
105724
106022
|
var MAX_FILE_SIZE = 1e4;
|
|
105725
106023
|
var MAX_RESULTS = 200;
|
|
@@ -105739,7 +106037,7 @@ function truncate(value, limit) {
|
|
|
105739
106037
|
return value.length > limit ? `${value.slice(0, limit)}n... (truncated, ${value.length} total chars)` : value;
|
|
105740
106038
|
}
|
|
105741
106039
|
async function executeLocalTool(toolName, input, mode) {
|
|
105742
|
-
if (mode === Mode.PLAN && !["readFile", "listDirectory", "glob", "grep"].includes(toolName)) {
|
|
106040
|
+
if (mode === Mode.PLAN && !["readFile", "listDirectory", "glob", "grep", "webSearch"].includes(toolName)) {
|
|
105743
106041
|
throw new Error(`Tool ${toolName} is not available in PLAN mode`);
|
|
105744
106042
|
}
|
|
105745
106043
|
switch (toolName) {
|
|
@@ -105849,11 +106147,15 @@ async function executeLocalTool(toolName, input, mode) {
|
|
|
105849
106147
|
}
|
|
105850
106148
|
case "bash": {
|
|
105851
106149
|
const { command, timeout = DEFAULT_TIMEOUT } = toolInputSchemas.bash.parse(input);
|
|
105852
|
-
const
|
|
106150
|
+
const isWindows = process.platform === "win32";
|
|
106151
|
+
const shell = isWindows ? "cmd.exe" : "bash";
|
|
106152
|
+
const shellFlag = isWindows ? "/c" : "-c";
|
|
106153
|
+
const env2 = isWindows ? { ...process.env } : { ...process.env, TERM: "dumb" };
|
|
106154
|
+
const proc = Bun.spawn([shell, shellFlag, command], {
|
|
105853
106155
|
cwd: resolveInsideCwd(".").resolved,
|
|
105854
106156
|
stdout: "pipe",
|
|
105855
106157
|
stderr: "pipe",
|
|
105856
|
-
env:
|
|
106158
|
+
env: env2
|
|
105857
106159
|
});
|
|
105858
106160
|
const timer = setTimeout(() => proc.kill(), timeout);
|
|
105859
106161
|
const [stdout, stderr] = await Promise.all([
|
|
@@ -105868,6 +106170,40 @@ async function executeLocalTool(toolName, input, mode) {
|
|
|
105868
106170
|
exitCode
|
|
105869
106171
|
};
|
|
105870
106172
|
}
|
|
106173
|
+
case "deleteFile": {
|
|
106174
|
+
const { path: path5 } = toolInputSchemas.deleteFile.parse(input);
|
|
106175
|
+
const { cwd, resolved } = resolveInsideCwd(path5);
|
|
106176
|
+
await unlink2(resolved);
|
|
106177
|
+
return { success: true, path: relative2(cwd, resolved) };
|
|
106178
|
+
}
|
|
106179
|
+
case "webSearch": {
|
|
106180
|
+
const { query } = toolInputSchemas.webSearch.parse(input);
|
|
106181
|
+
const apiKey = process.env.TAVILY_API_KEY;
|
|
106182
|
+
if (!apiKey)
|
|
106183
|
+
throw new Error("TAVILY_API_KEY is not set");
|
|
106184
|
+
const res = await fetch("https://api.tavily.com/search", {
|
|
106185
|
+
method: "POST",
|
|
106186
|
+
headers: { "Content-Type": "application/json" },
|
|
106187
|
+
body: JSON.stringify({
|
|
106188
|
+
api_key: apiKey,
|
|
106189
|
+
query,
|
|
106190
|
+
search_depth: "advanced",
|
|
106191
|
+
include_answer: true,
|
|
106192
|
+
max_results: 5
|
|
106193
|
+
})
|
|
106194
|
+
});
|
|
106195
|
+
if (!res.ok)
|
|
106196
|
+
throw new Error(`Web search failed: ${res.status} ${res.statusText}`);
|
|
106197
|
+
const data2 = await res.json();
|
|
106198
|
+
return {
|
|
106199
|
+
results: data2.results.map((r) => ({
|
|
106200
|
+
title: r.title,
|
|
106201
|
+
url: r.url,
|
|
106202
|
+
content: r.content
|
|
106203
|
+
})),
|
|
106204
|
+
answer: data2.answer
|
|
106205
|
+
};
|
|
106206
|
+
}
|
|
105871
106207
|
default:
|
|
105872
106208
|
throw new Error(`Unknown tool ${toolName}`);
|
|
105873
106209
|
}
|
|
@@ -105875,7 +106211,7 @@ async function executeLocalTool(toolName, input, mode) {
|
|
|
105875
106211
|
|
|
105876
106212
|
// src/hooks/use-chat.ts
|
|
105877
106213
|
function useChat2(sessionId, initialMessages) {
|
|
105878
|
-
const transport =
|
|
106214
|
+
const transport = import_react52.useMemo(() => {
|
|
105879
106215
|
return new DefaultChatTransport({
|
|
105880
106216
|
api: apiClient.chat.$url().toString(),
|
|
105881
106217
|
headers() {
|
|
@@ -105963,6 +106299,7 @@ function ChatMessage({ msg }) {
|
|
|
105963
106299
|
model: msg.metadata?.model ?? "unknown",
|
|
105964
106300
|
mode: msg.metadata?.mode ?? "BUILD",
|
|
105965
106301
|
durationMs: msg.metadata?.durationMs,
|
|
106302
|
+
usage: msg.metadata?.usage,
|
|
105966
106303
|
streaming: false
|
|
105967
106304
|
}, undefined, false, undefined, this);
|
|
105968
106305
|
}
|
|
@@ -105986,14 +106323,14 @@ function SessionChat({
|
|
|
105986
106323
|
session,
|
|
105987
106324
|
initialPrompt
|
|
105988
106325
|
}) {
|
|
105989
|
-
const [initialMessages] =
|
|
106326
|
+
const [initialMessages] = import_react54.useState(() => session.messages);
|
|
105990
106327
|
const { mode, model, domain: domain2 } = usePromptConfig();
|
|
105991
106328
|
const { isTopLayer } = useKeyboardLayer();
|
|
105992
106329
|
const { messages, status, submit, setMessages, reload, abort, interrupt, error: error51 } = useChat2(session.id, initialMessages);
|
|
105993
|
-
const hasSubmittedInitialPromptRef =
|
|
106330
|
+
const hasSubmittedInitialPromptRef = import_react54.useRef(false);
|
|
105994
106331
|
const toast = useToast();
|
|
105995
106332
|
const dialog = useDialog();
|
|
105996
|
-
|
|
106333
|
+
import_react54.useEffect(() => {
|
|
105997
106334
|
return () => {
|
|
105998
106335
|
abort();
|
|
105999
106336
|
};
|
|
@@ -106014,7 +106351,7 @@ function SessionChat({
|
|
|
106014
106351
|
}
|
|
106015
106352
|
});
|
|
106016
106353
|
useKeyboard((key) => {
|
|
106017
|
-
if (key.name === "e" &&
|
|
106354
|
+
if (key.name === "e" && key.ctrl && isTopLayer("base") && status !== "streaming" && status !== "submitted") {
|
|
106018
106355
|
const lastUserMsg = messages.findLast((m2) => m2.role === "user");
|
|
106019
106356
|
if (!lastUserMsg)
|
|
106020
106357
|
return;
|
|
@@ -106052,7 +106389,7 @@ function SessionChat({
|
|
|
106052
106389
|
});
|
|
106053
106390
|
}
|
|
106054
106391
|
});
|
|
106055
|
-
|
|
106392
|
+
import_react54.useEffect(() => {
|
|
106056
106393
|
if (!initialPrompt || hasSubmittedInitialPromptRef.current)
|
|
106057
106394
|
return;
|
|
106058
106395
|
hasSubmittedInitialPromptRef.current = true;
|
|
@@ -106066,6 +106403,8 @@ function SessionChat({
|
|
|
106066
106403
|
onSubmit: (text3) => submit({ userText: text3, mode, model, domain: domain2 }),
|
|
106067
106404
|
loading: status === "submitted" || status === "streaming",
|
|
106068
106405
|
interruptible: status === "submitted" || status === "streaming",
|
|
106406
|
+
sessionId: session.id,
|
|
106407
|
+
messages,
|
|
106069
106408
|
children: [
|
|
106070
106409
|
messages.map((msg) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ChatMessage, {
|
|
106071
106410
|
msg
|
|
@@ -106081,12 +106420,12 @@ function Session() {
|
|
|
106081
106420
|
const location = useLocation();
|
|
106082
106421
|
const navigate = useNavigate();
|
|
106083
106422
|
const toast = useToast();
|
|
106084
|
-
const prefetched =
|
|
106423
|
+
const prefetched = import_react54.useMemo(() => {
|
|
106085
106424
|
const parsed = sessionLocationSchema.safeParse(location.state);
|
|
106086
106425
|
return parsed.success ? parsed.data : null;
|
|
106087
106426
|
}, [location.state]);
|
|
106088
|
-
const [session, setSession] =
|
|
106089
|
-
|
|
106427
|
+
const [session, setSession] = import_react54.useState(prefetched?.session ?? null);
|
|
106428
|
+
import_react54.useEffect(() => {
|
|
106090
106429
|
if (prefetched?.session)
|
|
106091
106430
|
return;
|
|
106092
106431
|
setSession(null);
|