@juspay/neurolink 11.21.3 → 11.22.0
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/CHANGELOG.md +3 -3
- package/dist/browser/neurolink.min.js +265 -265
- package/dist/cli/commands/usage.d.ts +17 -0
- package/dist/cli/commands/usage.js +146 -0
- package/dist/cli/parser.js +2 -0
- package/dist/localUsage/index.d.ts +2 -2
- package/dist/localUsage/index.js +8 -1
- package/dist/types/localUsage.d.ts +17 -0
- package/dist/utils/redirectDispatcher.d.ts +24 -13
- package/dist/utils/redirectDispatcher.js +29 -19
- package/package.json +5 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CommandModule } from "yargs";
|
|
2
|
+
import type { LocalUsageCommandArgs } from "../../types/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* `neurolink usage local` — token spend read from each CLI's own session logs.
|
|
5
|
+
*
|
|
6
|
+
* The proxy's ledger only sees traffic that went through it, which is a
|
|
7
|
+
* fraction of what a developer actually spends: it depends on each vendor
|
|
8
|
+
* shipping a base-URL override, and most do not. Every CLI writes a local
|
|
9
|
+
* transcript regardless, so this reads those instead — no auth, no vendor
|
|
10
|
+
* cooperation, no proxy in the request path, and it recovers history from
|
|
11
|
+
* before the proxy was ever installed.
|
|
12
|
+
*/
|
|
13
|
+
export declare class UsageCommandFactory {
|
|
14
|
+
static createUsageCommands(): CommandModule<object, LocalUsageCommandArgs>;
|
|
15
|
+
private static formatTokens;
|
|
16
|
+
private static executeLocal;
|
|
17
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { logger } from "../../utils/logger.js";
|
|
3
|
+
/**
|
|
4
|
+
* `neurolink usage local` — token spend read from each CLI's own session logs.
|
|
5
|
+
*
|
|
6
|
+
* The proxy's ledger only sees traffic that went through it, which is a
|
|
7
|
+
* fraction of what a developer actually spends: it depends on each vendor
|
|
8
|
+
* shipping a base-URL override, and most do not. Every CLI writes a local
|
|
9
|
+
* transcript regardless, so this reads those instead — no auth, no vendor
|
|
10
|
+
* cooperation, no proxy in the request path, and it recovers history from
|
|
11
|
+
* before the proxy was ever installed.
|
|
12
|
+
*/
|
|
13
|
+
export class UsageCommandFactory {
|
|
14
|
+
static createUsageCommands() {
|
|
15
|
+
return {
|
|
16
|
+
command: "usage <subcommand>",
|
|
17
|
+
describe: "Token usage read from local CLI session logs",
|
|
18
|
+
builder: (yargs) => yargs.command({
|
|
19
|
+
command: "local",
|
|
20
|
+
describe: "Summarise token spend from each installed CLI's own session logs",
|
|
21
|
+
builder: (sub) => sub
|
|
22
|
+
.option("since", {
|
|
23
|
+
type: "number",
|
|
24
|
+
default: 30,
|
|
25
|
+
description: "Only read sessions modified within this many days (0 = all history)",
|
|
26
|
+
})
|
|
27
|
+
.option("cli", {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Limit to one CLI id (e.g. claude-code, codex, opencode)",
|
|
30
|
+
})
|
|
31
|
+
.option("json", {
|
|
32
|
+
type: "boolean",
|
|
33
|
+
default: false,
|
|
34
|
+
description: "Emit the raw report as JSON",
|
|
35
|
+
}),
|
|
36
|
+
handler: async (argv) => {
|
|
37
|
+
// Single assertion, not a double. yargs types the sub-builder's
|
|
38
|
+
// argv structurally; the fields below are the ones the builder
|
|
39
|
+
// declares, so this stays overlap-checked by the compiler.
|
|
40
|
+
await UsageCommandFactory.executeLocal({
|
|
41
|
+
since: Number(argv.since ?? 30),
|
|
42
|
+
json: Boolean(argv.json),
|
|
43
|
+
...(typeof argv.cli === "string" ? { cli: argv.cli } : {}),
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
handler: () => {
|
|
48
|
+
// yargs prints subcommand help when none is given.
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
static formatTokens(value) {
|
|
53
|
+
// Plain grouped digits rather than 1.2M: these are billing-adjacent
|
|
54
|
+
// figures and a reader comparing two rows needs the magnitudes to line up,
|
|
55
|
+
// not to be rounded into looking similar.
|
|
56
|
+
return value.toLocaleString("en-US");
|
|
57
|
+
}
|
|
58
|
+
static async executeLocal(argv) {
|
|
59
|
+
const { readAllLocalUsage, getLocalUsageDescriptors } = await import("../../localUsage/index.js");
|
|
60
|
+
// `--since 0` means all history — Infinity is the reader's sentinel for
|
|
61
|
+
// "no time filter", but 0 is what a person types. A NEGATIVE value is a
|
|
62
|
+
// mistake and must be rejected rather than folded in with 0: the previous
|
|
63
|
+
// expression sent -1 down the all-history path, so a typo produced the
|
|
64
|
+
// most expensive possible scan while the option's own help text says 0 is
|
|
65
|
+
// the way to ask for that.
|
|
66
|
+
if (!Number.isFinite(argv.since) || argv.since < 0) {
|
|
67
|
+
console.error(chalk.red(`--since must be zero or greater (0 means all history). Received: ${String(argv.since)}`));
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const sinceDays = argv.since > 0 ? argv.since : Infinity;
|
|
72
|
+
// Validate BEFORE scanning. A typo should cost nothing, not a full sweep
|
|
73
|
+
// of every store followed by an empty result.
|
|
74
|
+
// `undefined` means the flag was not given. An empty string means it was
|
|
75
|
+
// given with no value, which is a mistake and must be rejected — a
|
|
76
|
+
// truthiness check treats the two as the same and silently scans every
|
|
77
|
+
// reader instead, reporting everything for a request that named nothing.
|
|
78
|
+
const wanted = argv.cli;
|
|
79
|
+
const known = getLocalUsageDescriptors().map((d) => d.id);
|
|
80
|
+
if (wanted !== undefined &&
|
|
81
|
+
!known.includes(wanted)) {
|
|
82
|
+
console.error(chalk.red(`Unknown CLI "${wanted}". Known readers: ${known.join(", ")}`));
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const report = await readAllLocalUsage({
|
|
87
|
+
sinceDays,
|
|
88
|
+
// Passed down so only the requested reader opens its store at all.
|
|
89
|
+
...(wanted !== undefined
|
|
90
|
+
? { only: [wanted] }
|
|
91
|
+
: {}),
|
|
92
|
+
});
|
|
93
|
+
const rows = Object.entries(report.totals);
|
|
94
|
+
if (argv.json) {
|
|
95
|
+
logger.always(JSON.stringify(wanted ? { ...report, totals: Object.fromEntries(rows) } : report, null, 2));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const window = sinceDays === Infinity ? "all history" : `last ${argv.since} days`;
|
|
99
|
+
logger.always(chalk.bold(`\nLocal CLI token usage — ${window}\n`));
|
|
100
|
+
// A reader that ran and found nothing in the window is a different fact
|
|
101
|
+
// from one that is not installed, and from one that failed. Printing a
|
|
102
|
+
// block of zeros for it buries the rows that matter, so it gets one line.
|
|
103
|
+
const quiet = rows.filter(([, t]) => t && t.requests === 0);
|
|
104
|
+
const active = rows.filter(([, t]) => t && t.requests > 0);
|
|
105
|
+
for (const [cliId, totals] of active) {
|
|
106
|
+
if (!totals) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const cached = totals.cacheReadTokens + totals.cacheCreationTokens;
|
|
110
|
+
logger.always(chalk.cyan(` ${cliId}`));
|
|
111
|
+
logger.always(` turns ${UsageCommandFactory.formatTokens(totals.requests)}`);
|
|
112
|
+
logger.always(` input ${UsageCommandFactory.formatTokens(totals.inputTokens)}` +
|
|
113
|
+
` output ${UsageCommandFactory.formatTokens(totals.outputTokens)}` +
|
|
114
|
+
` cached ${UsageCommandFactory.formatTokens(cached)}`);
|
|
115
|
+
// Cost and its confidence are printed together, always. A dollar figure
|
|
116
|
+
// shown without saying how it was arrived at is the thing this whole
|
|
117
|
+
// subsystem is trying not to do: "unavailable" means the CLI is a
|
|
118
|
+
// subscription and a per-token price would be invented, not that the
|
|
119
|
+
// lookup failed.
|
|
120
|
+
if (totals.costConfidence === "modeled") {
|
|
121
|
+
logger.always(` cost ${chalk.green(`$${totals.costUsd.toFixed(2)}`)} (modeled)` +
|
|
122
|
+
(totals.unpricedRequests > 0
|
|
123
|
+
? chalk.dim(` — ${totals.unpricedRequests} turns unpriced: ${totals.unpricedModels.join(", ")}`)
|
|
124
|
+
: ""));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
logger.always(` cost ${chalk.dim("unavailable")} ` +
|
|
128
|
+
chalk.dim(totals.costConfidence === "heuristic"
|
|
129
|
+
? "(estimated, not measured)"
|
|
130
|
+
: "(subscription — a per-token price would be invented)"));
|
|
131
|
+
}
|
|
132
|
+
logger.always("");
|
|
133
|
+
}
|
|
134
|
+
if (quiet.length > 0) {
|
|
135
|
+
logger.always(chalk.dim(` no usage in this window: ${quiet.map(([id]) => id).join(", ")}`));
|
|
136
|
+
}
|
|
137
|
+
if (report.notInstalled.length > 0) {
|
|
138
|
+
logger.always(chalk.dim(` not installed: ${report.notInstalled.join(", ")}`));
|
|
139
|
+
}
|
|
140
|
+
for (const failure of report.failures) {
|
|
141
|
+
logger.always(chalk.yellow(` ${failure.cliId} failed: ${failure.message}`));
|
|
142
|
+
}
|
|
143
|
+
logger.always("");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=usage.js.map
|
package/dist/cli/parser.js
CHANGED
|
@@ -29,6 +29,7 @@ import { TaskCommandFactory } from "./commands/task.js";
|
|
|
29
29
|
import { AutoresearchCommandFactory } from "./commands/autoresearch.js";
|
|
30
30
|
import { voiceServerCommand } from "./commands/voiceServer.js";
|
|
31
31
|
import { DocsCommandFactory } from "./commands/docs.js";
|
|
32
|
+
import { UsageCommandFactory } from "./commands/usage.js";
|
|
32
33
|
// Enhanced CLI with Professional UX
|
|
33
34
|
export function initializeCliParser() {
|
|
34
35
|
return (yargs(hideBin(process.argv))
|
|
@@ -156,6 +157,7 @@ export function initializeCliParser() {
|
|
|
156
157
|
.command(CLICommandFactory.createGenerateCommand())
|
|
157
158
|
// Docs MCP Server Command
|
|
158
159
|
.command(DocsCommandFactory.createDocsCommand())
|
|
160
|
+
.command(UsageCommandFactory.createUsageCommands())
|
|
159
161
|
// Stream Text Command - Using CLICommandFactory
|
|
160
162
|
.command(CLICommandFactory.createStreamCommand())
|
|
161
163
|
// Batch Processing Command - Using CLICommandFactory
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* a local transcript regardless, so reading those covers the rest — and covers
|
|
7
7
|
* history from before the proxy existed.
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
9
|
+
import type { LocalUsageAggregateOptions, LocalUsageAggregateReport } from "../types/index.js";
|
|
10
10
|
export { createLocalUsageReader, getLocalUsageDescriptors, getRegisteredLocalUsageCliIds, registerLocalUsageReader, } from "./localUsageReaderRegistry.js";
|
|
11
11
|
/**
|
|
12
12
|
* Scan every registered reader whose CLI is actually present on this machine.
|
|
@@ -15,4 +15,4 @@ export { createLocalUsageReader, getLocalUsageDescriptors, getRegisteredLocalUsa
|
|
|
15
15
|
* the user never installed is not an error, and collapsing the two would make
|
|
16
16
|
* a broken reader indistinguishable from an absent one.
|
|
17
17
|
*/
|
|
18
|
-
export declare function readAllLocalUsage(options?:
|
|
18
|
+
export declare function readAllLocalUsage(options?: LocalUsageAggregateOptions): Promise<LocalUsageAggregateReport>;
|
package/dist/localUsage/index.js
CHANGED
|
@@ -19,7 +19,14 @@ export async function readAllLocalUsage(options) {
|
|
|
19
19
|
const totals = {};
|
|
20
20
|
const failures = [];
|
|
21
21
|
const notInstalled = [];
|
|
22
|
-
|
|
22
|
+
// Filtered BEFORE construction, not after: `only` decides which stores are
|
|
23
|
+
// opened at all. Reading all of them and discarding the rest cost 28s for a
|
|
24
|
+
// single-CLI query that needs 10.
|
|
25
|
+
const requested = options?.only;
|
|
26
|
+
const cliIds = requested
|
|
27
|
+
? getRegisteredLocalUsageCliIds().filter((id) => requested.includes(id))
|
|
28
|
+
: getRegisteredLocalUsageCliIds();
|
|
29
|
+
for (const cliId of cliIds) {
|
|
23
30
|
try {
|
|
24
31
|
const reader = await createLocalUsageReader(cliId);
|
|
25
32
|
if (!(await reader.detect())) {
|
|
@@ -169,3 +169,20 @@ export type LocalUsageSqliteDatabase = {
|
|
|
169
169
|
export type LocalUsageSqliteDatabaseCtor = new (path: string, options?: {
|
|
170
170
|
readOnly?: boolean;
|
|
171
171
|
}) => LocalUsageSqliteDatabase;
|
|
172
|
+
/** Arguments for the `neurolink usage local` command. */
|
|
173
|
+
export type LocalUsageCommandArgs = {
|
|
174
|
+
since: number;
|
|
175
|
+
json: boolean;
|
|
176
|
+
cli?: string;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Options for scanning every registered reader at once.
|
|
180
|
+
*
|
|
181
|
+
* `only` is not a convenience filter applied to the results — it decides which
|
|
182
|
+
* readers are constructed and run at all. Scanning everything and discarding
|
|
183
|
+
* the rest turned a 10s single-CLI query into 28s of reading two other stores
|
|
184
|
+
* nobody asked for, one of them 742 MB.
|
|
185
|
+
*/
|
|
186
|
+
export type LocalUsageAggregateOptions = LocalUsageScanOptions & {
|
|
187
|
+
only?: LocalUsageCliId[];
|
|
188
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Dispatcher } from "undici";
|
|
2
2
|
/**
|
|
3
|
-
* A dispatcher that follows redirects
|
|
3
|
+
* A dispatcher that follows redirects.
|
|
4
4
|
*
|
|
5
5
|
* `getGlobalDispatcher()` returns Node's **built-in** undici dispatcher, whose
|
|
6
6
|
* major tracks the runtime rather than this package's dependency. The composed
|
|
@@ -10,19 +10,30 @@ import type { Dispatcher } from "undici";
|
|
|
10
10
|
* node 24 built-in 7.24.4 + npm 7.28.0 request() succeeds
|
|
11
11
|
* node 22 built-in 6.28.0 + npm 7.28.0 throws "invalid onError method"
|
|
12
12
|
*
|
|
13
|
-
* Node 22 is this package's declared minimum, so the broken combination is
|
|
14
|
-
*
|
|
15
|
-
* compose(), which is why it surfaces as an opaque runtime error instead of
|
|
16
|
-
* something recognisably about versions.
|
|
13
|
+
* Node 22 is this package's declared minimum, so the broken combination is the
|
|
14
|
+
* floor, not an edge case.
|
|
17
15
|
*
|
|
18
|
-
* When the majors
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* streaming size guard on the GET, so the size protection is unchanged and the
|
|
22
|
-
* cost is one extra round trip.
|
|
16
|
+
* When the majors match, compose onto the global dispatcher — that preserves
|
|
17
|
+
* whatever the host application configured globally, such as a corporate
|
|
18
|
+
* ProxyAgent.
|
|
23
19
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
20
|
+
* When they do not, compose onto a fresh `Agent` that npm undici owns, so the
|
|
21
|
+
* handler contract is self-consistent. Redirects still get followed.
|
|
22
|
+
*
|
|
23
|
+
* The earlier version of this function returned the global dispatcher
|
|
24
|
+
* *uncomposed* in the mismatch case, on the stated grounds that it "drops
|
|
25
|
+
* redirect-following from the pre-flight HEAD only". That was wrong: the same
|
|
26
|
+
* dispatcher feeds the real GET in fileDetector.ts and messageBuilder.ts, and
|
|
27
|
+
* that path treats any non-200 as fatal — so on Node 22 a redirecting URL threw
|
|
28
|
+
* `HTTP 302 fetching …` instead of downloading. Verified against a local
|
|
29
|
+
* redirecting server: node 24 -> 200 (followed), node 22 -> 302 (not followed).
|
|
30
|
+
*
|
|
31
|
+
* The cost of the fresh Agent is narrow and worth naming: on a runtime whose
|
|
32
|
+
* built-in undici major differs from ours, a globally-configured dispatcher
|
|
33
|
+
* (e.g. a ProxyAgent set via setGlobalDispatcher) is not inherited for these
|
|
34
|
+
* requests. Losing a proxy on one Node version is recoverable; silently failing
|
|
35
|
+
* every redirecting download is not.
|
|
36
|
+
*
|
|
37
|
+
* @param maxRedirections How many redirects to follow before giving up.
|
|
27
38
|
*/
|
|
28
39
|
export declare function redirectFollowingDispatcher(maxRedirections: number): Dispatcher;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getGlobalDispatcher, interceptors } from "undici";
|
|
1
|
+
import { Agent, getGlobalDispatcher, interceptors } from "undici";
|
|
2
2
|
/**
|
|
3
3
|
* The major of the `undici` this package depends on.
|
|
4
4
|
*
|
|
@@ -8,7 +8,7 @@ import { getGlobalDispatcher, interceptors } from "undici";
|
|
|
8
8
|
*/
|
|
9
9
|
const NPM_UNDICI_MAJOR = 7;
|
|
10
10
|
/**
|
|
11
|
-
* A dispatcher that follows redirects
|
|
11
|
+
* A dispatcher that follows redirects.
|
|
12
12
|
*
|
|
13
13
|
* `getGlobalDispatcher()` returns Node's **built-in** undici dispatcher, whose
|
|
14
14
|
* major tracks the runtime rather than this package's dependency. The composed
|
|
@@ -18,26 +18,36 @@ const NPM_UNDICI_MAJOR = 7;
|
|
|
18
18
|
* node 24 built-in 7.24.4 + npm 7.28.0 request() succeeds
|
|
19
19
|
* node 22 built-in 6.28.0 + npm 7.28.0 throws "invalid onError method"
|
|
20
20
|
*
|
|
21
|
-
* Node 22 is this package's declared minimum, so the broken combination is
|
|
22
|
-
*
|
|
23
|
-
* compose(), which is why it surfaces as an opaque runtime error instead of
|
|
24
|
-
* something recognisably about versions.
|
|
21
|
+
* Node 22 is this package's declared minimum, so the broken combination is the
|
|
22
|
+
* floor, not an edge case.
|
|
25
23
|
*
|
|
26
|
-
* When the majors
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* streaming size guard on the GET, so the size protection is unchanged and the
|
|
30
|
-
* cost is one extra round trip.
|
|
24
|
+
* When the majors match, compose onto the global dispatcher — that preserves
|
|
25
|
+
* whatever the host application configured globally, such as a corporate
|
|
26
|
+
* ProxyAgent.
|
|
31
27
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
28
|
+
* When they do not, compose onto a fresh `Agent` that npm undici owns, so the
|
|
29
|
+
* handler contract is self-consistent. Redirects still get followed.
|
|
30
|
+
*
|
|
31
|
+
* The earlier version of this function returned the global dispatcher
|
|
32
|
+
* *uncomposed* in the mismatch case, on the stated grounds that it "drops
|
|
33
|
+
* redirect-following from the pre-flight HEAD only". That was wrong: the same
|
|
34
|
+
* dispatcher feeds the real GET in fileDetector.ts and messageBuilder.ts, and
|
|
35
|
+
* that path treats any non-200 as fatal — so on Node 22 a redirecting URL threw
|
|
36
|
+
* `HTTP 302 fetching …` instead of downloading. Verified against a local
|
|
37
|
+
* redirecting server: node 24 -> 200 (followed), node 22 -> 302 (not followed).
|
|
38
|
+
*
|
|
39
|
+
* The cost of the fresh Agent is narrow and worth naming: on a runtime whose
|
|
40
|
+
* built-in undici major differs from ours, a globally-configured dispatcher
|
|
41
|
+
* (e.g. a ProxyAgent set via setGlobalDispatcher) is not inherited for these
|
|
42
|
+
* requests. Losing a proxy on one Node version is recoverable; silently failing
|
|
43
|
+
* every redirecting download is not.
|
|
44
|
+
*
|
|
45
|
+
* @param maxRedirections How many redirects to follow before giving up.
|
|
35
46
|
*/
|
|
36
47
|
export function redirectFollowingDispatcher(maxRedirections) {
|
|
37
|
-
const globalDispatcher = getGlobalDispatcher();
|
|
38
48
|
const builtinMajor = Number.parseInt(process.versions.undici?.split(".")[0] ?? "", 10);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
49
|
+
const base = Number.isFinite(builtinMajor) && builtinMajor === NPM_UNDICI_MAJOR
|
|
50
|
+
? getGlobalDispatcher()
|
|
51
|
+
: new Agent();
|
|
52
|
+
return base.compose(interceptors.redirect({ maxRedirections }));
|
|
43
53
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.22.0",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
|
|
6
6
|
"author": {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit --strict",
|
|
43
43
|
"check:ci-scripts": "svelte-kit sync && tsc -p tsconfig.ci-scripts.json",
|
|
44
44
|
"check:test-parse": "node scripts/parse-check-tests.mjs",
|
|
45
|
+
"check:tools-tests": "svelte-kit sync && tsc -p tsconfig.tools-tests.json",
|
|
45
46
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
46
47
|
"typecheck": "tsc --noEmit",
|
|
47
48
|
"modelServer": "tsx scripts/modelServer.ts",
|
|
@@ -475,6 +476,7 @@
|
|
|
475
476
|
"@sveltejs/package": "^2.5.7",
|
|
476
477
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
477
478
|
"@types/cors": "^2.8.19",
|
|
479
|
+
"@types/diff": "^8.0.0",
|
|
478
480
|
"@types/express": "^5.0.6",
|
|
479
481
|
"@types/fluent-ffmpeg": "^2.1.28",
|
|
480
482
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -491,8 +493,10 @@
|
|
|
491
493
|
"@vercel/ncc": "^0.38.4",
|
|
492
494
|
"concurrently": "^9.2.1",
|
|
493
495
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
496
|
+
"diff": "^9.0.0",
|
|
494
497
|
"esbuild": "^0.28.1",
|
|
495
498
|
"eslint": "^10.0.2",
|
|
499
|
+
"glob": "^13.0.6",
|
|
496
500
|
"husky": "^9.1.7",
|
|
497
501
|
"playwright": "^1.58.2",
|
|
498
502
|
"prettier": "^3.8.1",
|