@link-assistant/agent 0.0.8 → 0.0.11
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/EXAMPLES.md +80 -1
- package/MODELS.md +72 -24
- package/README.md +95 -2
- package/TOOLS.md +20 -0
- package/package.json +36 -2
- package/src/agent/agent.ts +68 -54
- package/src/auth/claude-oauth.ts +426 -0
- package/src/auth/index.ts +28 -26
- package/src/auth/plugins.ts +876 -0
- package/src/bun/index.ts +53 -43
- package/src/bus/global.ts +5 -5
- package/src/bus/index.ts +59 -53
- package/src/cli/bootstrap.js +12 -12
- package/src/cli/bootstrap.ts +6 -6
- package/src/cli/cmd/agent.ts +97 -92
- package/src/cli/cmd/auth.ts +468 -0
- package/src/cli/cmd/cmd.ts +2 -2
- package/src/cli/cmd/export.ts +41 -41
- package/src/cli/cmd/mcp.ts +210 -53
- package/src/cli/cmd/models.ts +30 -29
- package/src/cli/cmd/run.ts +269 -213
- package/src/cli/cmd/stats.ts +185 -146
- package/src/cli/error.ts +17 -13
- package/src/cli/ui.ts +78 -0
- package/src/command/index.ts +26 -26
- package/src/config/config.ts +528 -288
- package/src/config/markdown.ts +15 -15
- package/src/file/ripgrep.ts +201 -169
- package/src/file/time.ts +21 -18
- package/src/file/watcher.ts +51 -42
- package/src/file.ts +1 -1
- package/src/flag/flag.ts +26 -11
- package/src/format/formatter.ts +206 -162
- package/src/format/index.ts +61 -61
- package/src/global/index.ts +21 -21
- package/src/id/id.ts +47 -33
- package/src/index.js +554 -332
- package/src/json-standard/index.ts +173 -0
- package/src/mcp/index.ts +135 -128
- package/src/patch/index.ts +336 -267
- package/src/project/bootstrap.ts +15 -15
- package/src/project/instance.ts +43 -36
- package/src/project/project.ts +47 -47
- package/src/project/state.ts +37 -33
- package/src/provider/models-macro.ts +5 -5
- package/src/provider/models.ts +32 -32
- package/src/provider/opencode.js +19 -19
- package/src/provider/provider.ts +518 -277
- package/src/provider/transform.ts +143 -102
- package/src/server/project.ts +21 -21
- package/src/server/server.ts +111 -105
- package/src/session/agent.js +66 -60
- package/src/session/compaction.ts +136 -111
- package/src/session/index.ts +189 -156
- package/src/session/message-v2.ts +312 -268
- package/src/session/message.ts +73 -57
- package/src/session/processor.ts +180 -166
- package/src/session/prompt.ts +678 -533
- package/src/session/retry.ts +26 -23
- package/src/session/revert.ts +76 -62
- package/src/session/status.ts +26 -26
- package/src/session/summary.ts +97 -76
- package/src/session/system.ts +77 -63
- package/src/session/todo.ts +22 -16
- package/src/snapshot/index.ts +92 -76
- package/src/storage/storage.ts +157 -120
- package/src/tool/bash.ts +116 -106
- package/src/tool/batch.ts +73 -59
- package/src/tool/codesearch.ts +60 -53
- package/src/tool/edit.ts +319 -263
- package/src/tool/glob.ts +32 -28
- package/src/tool/grep.ts +72 -53
- package/src/tool/invalid.ts +7 -7
- package/src/tool/ls.ts +77 -64
- package/src/tool/multiedit.ts +30 -21
- package/src/tool/patch.ts +121 -94
- package/src/tool/read.ts +140 -122
- package/src/tool/registry.ts +38 -38
- package/src/tool/task.ts +93 -60
- package/src/tool/todo.ts +16 -16
- package/src/tool/tool.ts +45 -36
- package/src/tool/webfetch.ts +97 -74
- package/src/tool/websearch.ts +78 -64
- package/src/tool/write.ts +21 -15
- package/src/util/binary.ts +27 -19
- package/src/util/context.ts +8 -8
- package/src/util/defer.ts +7 -5
- package/src/util/error.ts +24 -19
- package/src/util/eventloop.ts +16 -10
- package/src/util/filesystem.ts +37 -33
- package/src/util/fn.ts +11 -8
- package/src/util/iife.ts +1 -1
- package/src/util/keybind.ts +44 -44
- package/src/util/lazy.ts +7 -7
- package/src/util/locale.ts +20 -16
- package/src/util/lock.ts +43 -38
- package/src/util/log.ts +95 -85
- package/src/util/queue.ts +8 -8
- package/src/util/rpc.ts +35 -23
- package/src/util/scrap.ts +4 -4
- package/src/util/signal.ts +5 -5
- package/src/util/timeout.ts +6 -6
- package/src/util/token.ts +2 -2
- package/src/util/wildcard.ts +38 -27
package/src/util/log.ts
CHANGED
|
@@ -1,105 +1,111 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import fs from
|
|
3
|
-
import { Global } from
|
|
4
|
-
import z from
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import { Global } from '../global';
|
|
4
|
+
import z from 'zod';
|
|
5
5
|
|
|
6
6
|
export namespace Log {
|
|
7
|
-
export const Level = z
|
|
8
|
-
|
|
7
|
+
export const Level = z
|
|
8
|
+
.enum(['DEBUG', 'INFO', 'WARN', 'ERROR'])
|
|
9
|
+
.meta({ ref: 'LogLevel', description: 'Log level' });
|
|
10
|
+
export type Level = z.infer<typeof Level>;
|
|
9
11
|
|
|
10
12
|
const levelPriority: Record<Level, number> = {
|
|
11
13
|
DEBUG: 0,
|
|
12
14
|
INFO: 1,
|
|
13
15
|
WARN: 2,
|
|
14
16
|
ERROR: 3,
|
|
15
|
-
}
|
|
17
|
+
};
|
|
16
18
|
|
|
17
|
-
let level: Level =
|
|
19
|
+
let level: Level = 'INFO';
|
|
18
20
|
|
|
19
21
|
function shouldLog(input: Level): boolean {
|
|
20
|
-
return levelPriority[input] >= levelPriority[level]
|
|
22
|
+
return levelPriority[input] >= levelPriority[level];
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export type Logger = {
|
|
24
|
-
debug(message?: any, extra?: Record<string, any>): void
|
|
25
|
-
info(message?: any, extra?: Record<string, any>): void
|
|
26
|
-
error(message?: any, extra?: Record<string, any>): void
|
|
27
|
-
warn(message?: any, extra?: Record<string, any>): void
|
|
28
|
-
tag(key: string, value: string): Logger
|
|
29
|
-
clone(): Logger
|
|
26
|
+
debug(message?: any, extra?: Record<string, any>): void;
|
|
27
|
+
info(message?: any, extra?: Record<string, any>): void;
|
|
28
|
+
error(message?: any, extra?: Record<string, any>): void;
|
|
29
|
+
warn(message?: any, extra?: Record<string, any>): void;
|
|
30
|
+
tag(key: string, value: string): Logger;
|
|
31
|
+
clone(): Logger;
|
|
30
32
|
time(
|
|
31
33
|
message: string,
|
|
32
|
-
extra?: Record<string, any
|
|
34
|
+
extra?: Record<string, any>
|
|
33
35
|
): {
|
|
34
|
-
stop(): void
|
|
35
|
-
[Symbol.dispose](): void
|
|
36
|
-
}
|
|
37
|
-
}
|
|
36
|
+
stop(): void;
|
|
37
|
+
[Symbol.dispose](): void;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
38
40
|
|
|
39
|
-
const loggers = new Map<string, Logger>()
|
|
41
|
+
const loggers = new Map<string, Logger>();
|
|
40
42
|
|
|
41
|
-
export const Default = create({ service:
|
|
43
|
+
export const Default = create({ service: 'default' });
|
|
42
44
|
|
|
43
45
|
export interface Options {
|
|
44
|
-
print: boolean
|
|
45
|
-
dev?: boolean
|
|
46
|
-
level?: Level
|
|
46
|
+
print: boolean;
|
|
47
|
+
dev?: boolean;
|
|
48
|
+
level?: Level;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
let logpath =
|
|
51
|
+
let logpath = '';
|
|
50
52
|
export function file() {
|
|
51
|
-
return logpath
|
|
53
|
+
return logpath;
|
|
52
54
|
}
|
|
53
|
-
let write = (msg: any) => Bun.stderr.write(msg)
|
|
55
|
+
let write = (msg: any) => Bun.stderr.write(msg);
|
|
54
56
|
|
|
55
57
|
export async function init(options: Options) {
|
|
56
|
-
if (options.level) level = options.level
|
|
57
|
-
cleanup(Global.Path.log)
|
|
58
|
-
if (options.print) return
|
|
58
|
+
if (options.level) level = options.level;
|
|
59
|
+
cleanup(Global.Path.log);
|
|
60
|
+
if (options.print) return;
|
|
59
61
|
logpath = path.join(
|
|
60
62
|
Global.Path.log,
|
|
61
|
-
options.dev
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
63
|
+
options.dev
|
|
64
|
+
? 'dev.log'
|
|
65
|
+
: new Date().toISOString().split('.')[0].replace(/:/g, '') + '.log'
|
|
66
|
+
);
|
|
67
|
+
const logfile = Bun.file(logpath);
|
|
68
|
+
await fs.truncate(logpath).catch(() => {});
|
|
69
|
+
const writer = logfile.writer();
|
|
66
70
|
write = async (msg: any) => {
|
|
67
|
-
const num = writer.write(msg)
|
|
68
|
-
writer.flush()
|
|
69
|
-
return num
|
|
70
|
-
}
|
|
71
|
+
const num = writer.write(msg);
|
|
72
|
+
writer.flush();
|
|
73
|
+
return num;
|
|
74
|
+
};
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
async function cleanup(dir: string) {
|
|
74
|
-
const glob = new Bun.Glob(
|
|
78
|
+
const glob = new Bun.Glob('????-??-??T??????.log');
|
|
75
79
|
const files = await Array.fromAsync(
|
|
76
80
|
glob.scan({
|
|
77
81
|
cwd: dir,
|
|
78
82
|
absolute: true,
|
|
79
|
-
})
|
|
80
|
-
)
|
|
81
|
-
if (files.length <= 5) return
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
if (files.length <= 5) return;
|
|
82
86
|
|
|
83
|
-
const filesToDelete = files.slice(0, -10)
|
|
84
|
-
await Promise.all(
|
|
87
|
+
const filesToDelete = files.slice(0, -10);
|
|
88
|
+
await Promise.all(
|
|
89
|
+
filesToDelete.map((file) => fs.unlink(file).catch(() => {}))
|
|
90
|
+
);
|
|
85
91
|
}
|
|
86
92
|
|
|
87
93
|
function formatError(error: Error, depth = 0): string {
|
|
88
|
-
const result = error.message
|
|
94
|
+
const result = error.message;
|
|
89
95
|
return error.cause instanceof Error && depth < 10
|
|
90
|
-
? result +
|
|
91
|
-
: result
|
|
96
|
+
? result + ' Caused by: ' + formatError(error.cause, depth + 1)
|
|
97
|
+
: result;
|
|
92
98
|
}
|
|
93
99
|
|
|
94
|
-
let last = Date.now()
|
|
100
|
+
let last = Date.now();
|
|
95
101
|
export function create(tags?: Record<string, any>) {
|
|
96
|
-
tags = tags || {}
|
|
102
|
+
tags = tags || {};
|
|
97
103
|
|
|
98
|
-
const service = tags[
|
|
99
|
-
if (service && typeof service ===
|
|
100
|
-
const cached = loggers.get(service)
|
|
104
|
+
const service = tags['service'];
|
|
105
|
+
if (service && typeof service === 'string') {
|
|
106
|
+
const cached = loggers.get(service);
|
|
101
107
|
if (cached) {
|
|
102
|
-
return cached
|
|
108
|
+
return cached;
|
|
103
109
|
}
|
|
104
110
|
}
|
|
105
111
|
|
|
@@ -110,68 +116,72 @@ export namespace Log {
|
|
|
110
116
|
})
|
|
111
117
|
.filter(([_, value]) => value !== undefined && value !== null)
|
|
112
118
|
.map(([key, value]) => {
|
|
113
|
-
const prefix = `${key}
|
|
114
|
-
if (value instanceof Error) return prefix + formatError(value)
|
|
115
|
-
if (typeof value ===
|
|
116
|
-
return prefix + value
|
|
119
|
+
const prefix = `${key}=`;
|
|
120
|
+
if (value instanceof Error) return prefix + formatError(value);
|
|
121
|
+
if (typeof value === 'object') return prefix + JSON.stringify(value);
|
|
122
|
+
return prefix + value;
|
|
117
123
|
})
|
|
118
|
-
.join(
|
|
119
|
-
const next = new Date()
|
|
120
|
-
const diff = next.getTime() - last
|
|
121
|
-
last = next.getTime()
|
|
122
|
-
return
|
|
124
|
+
.join(' ');
|
|
125
|
+
const next = new Date();
|
|
126
|
+
const diff = next.getTime() - last;
|
|
127
|
+
last = next.getTime();
|
|
128
|
+
return (
|
|
129
|
+
[next.toISOString().split('.')[0], '+' + diff + 'ms', prefix, message]
|
|
130
|
+
.filter(Boolean)
|
|
131
|
+
.join(' ') + '\n'
|
|
132
|
+
);
|
|
123
133
|
}
|
|
124
134
|
const result: Logger = {
|
|
125
135
|
debug(message?: any, extra?: Record<string, any>) {
|
|
126
|
-
if (shouldLog(
|
|
127
|
-
write(
|
|
136
|
+
if (shouldLog('DEBUG')) {
|
|
137
|
+
write('DEBUG ' + build(message, extra));
|
|
128
138
|
}
|
|
129
139
|
},
|
|
130
140
|
info(message?: any, extra?: Record<string, any>) {
|
|
131
|
-
if (shouldLog(
|
|
132
|
-
write(
|
|
141
|
+
if (shouldLog('INFO')) {
|
|
142
|
+
write('INFO ' + build(message, extra));
|
|
133
143
|
}
|
|
134
144
|
},
|
|
135
145
|
error(message?: any, extra?: Record<string, any>) {
|
|
136
|
-
if (shouldLog(
|
|
137
|
-
write(
|
|
146
|
+
if (shouldLog('ERROR')) {
|
|
147
|
+
write('ERROR ' + build(message, extra));
|
|
138
148
|
}
|
|
139
149
|
},
|
|
140
150
|
warn(message?: any, extra?: Record<string, any>) {
|
|
141
|
-
if (shouldLog(
|
|
142
|
-
write(
|
|
151
|
+
if (shouldLog('WARN')) {
|
|
152
|
+
write('WARN ' + build(message, extra));
|
|
143
153
|
}
|
|
144
154
|
},
|
|
145
155
|
tag(key: string, value: string) {
|
|
146
|
-
if (tags) tags[key] = value
|
|
147
|
-
return result
|
|
156
|
+
if (tags) tags[key] = value;
|
|
157
|
+
return result;
|
|
148
158
|
},
|
|
149
159
|
clone() {
|
|
150
|
-
return Log.create({ ...tags })
|
|
160
|
+
return Log.create({ ...tags });
|
|
151
161
|
},
|
|
152
162
|
time(message: string, extra?: Record<string, any>) {
|
|
153
|
-
const now = Date.now()
|
|
154
|
-
result.info(message, { status:
|
|
163
|
+
const now = Date.now();
|
|
164
|
+
result.info(message, { status: 'started', ...extra });
|
|
155
165
|
function stop() {
|
|
156
166
|
result.info(message, {
|
|
157
|
-
status:
|
|
167
|
+
status: 'completed',
|
|
158
168
|
duration: Date.now() - now,
|
|
159
169
|
...extra,
|
|
160
|
-
})
|
|
170
|
+
});
|
|
161
171
|
}
|
|
162
172
|
return {
|
|
163
173
|
stop,
|
|
164
174
|
[Symbol.dispose]() {
|
|
165
|
-
stop()
|
|
175
|
+
stop();
|
|
166
176
|
},
|
|
167
|
-
}
|
|
177
|
+
};
|
|
168
178
|
},
|
|
169
|
-
}
|
|
179
|
+
};
|
|
170
180
|
|
|
171
|
-
if (service && typeof service ===
|
|
172
|
-
loggers.set(service, result)
|
|
181
|
+
if (service && typeof service === 'string') {
|
|
182
|
+
loggers.set(service, result);
|
|
173
183
|
}
|
|
174
184
|
|
|
175
|
-
return result
|
|
185
|
+
return result;
|
|
176
186
|
}
|
|
177
187
|
}
|
package/src/util/queue.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
export class AsyncQueue<T> implements AsyncIterable<T> {
|
|
2
|
-
private queue: T[] = []
|
|
3
|
-
private resolvers: ((value: T) => void)[] = []
|
|
2
|
+
private queue: T[] = [];
|
|
3
|
+
private resolvers: ((value: T) => void)[] = [];
|
|
4
4
|
|
|
5
5
|
push(item: T) {
|
|
6
|
-
const resolve = this.resolvers.shift()
|
|
7
|
-
if (resolve) resolve(item)
|
|
8
|
-
else this.queue.push(item)
|
|
6
|
+
const resolve = this.resolvers.shift();
|
|
7
|
+
if (resolve) resolve(item);
|
|
8
|
+
else this.queue.push(item);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
async next(): Promise<T> {
|
|
12
|
-
if (this.queue.length > 0) return this.queue.shift()
|
|
13
|
-
return new Promise((resolve) => this.resolvers.push(resolve))
|
|
12
|
+
if (this.queue.length > 0) return this.queue.shift()!;
|
|
13
|
+
return new Promise((resolve) => this.resolvers.push(resolve));
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
async *[Symbol.asyncIterator]() {
|
|
17
|
-
while (true) yield await this.next()
|
|
17
|
+
while (true) yield await this.next();
|
|
18
18
|
}
|
|
19
19
|
}
|
package/src/util/rpc.ts
CHANGED
|
@@ -1,42 +1,54 @@
|
|
|
1
1
|
export namespace Rpc {
|
|
2
2
|
type Definition = {
|
|
3
|
-
[method: string]: (input: any) => any
|
|
4
|
-
}
|
|
3
|
+
[method: string]: (input: any) => any;
|
|
4
|
+
};
|
|
5
5
|
|
|
6
6
|
export function listen(rpc: Definition) {
|
|
7
7
|
onmessage = async (evt) => {
|
|
8
|
-
const parsed = JSON.parse(evt.data)
|
|
9
|
-
if (parsed.type ===
|
|
10
|
-
const result = await rpc[parsed.method](parsed.input)
|
|
11
|
-
postMessage(
|
|
8
|
+
const parsed = JSON.parse(evt.data);
|
|
9
|
+
if (parsed.type === 'rpc.request') {
|
|
10
|
+
const result = await rpc[parsed.method](parsed.input);
|
|
11
|
+
postMessage(
|
|
12
|
+
JSON.stringify({ type: 'rpc.result', result, id: parsed.id })
|
|
13
|
+
);
|
|
12
14
|
}
|
|
13
|
-
}
|
|
15
|
+
};
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function client<T extends Definition>(target: {
|
|
17
|
-
postMessage: (data: string) => void | null
|
|
18
|
-
onmessage: ((this: Worker, ev: MessageEvent<any>) => any) | null
|
|
19
|
+
postMessage: (data: string) => void | null;
|
|
20
|
+
onmessage: ((this: Worker, ev: MessageEvent<any>) => any) | null;
|
|
19
21
|
}) {
|
|
20
|
-
const pending = new Map<number, (result: any) => void>()
|
|
21
|
-
let id = 0
|
|
22
|
+
const pending = new Map<number, (result: any) => void>();
|
|
23
|
+
let id = 0;
|
|
22
24
|
target.onmessage = async (evt) => {
|
|
23
|
-
const parsed = JSON.parse(evt.data)
|
|
24
|
-
if (parsed.type ===
|
|
25
|
-
const resolve = pending.get(parsed.id)
|
|
25
|
+
const parsed = JSON.parse(evt.data);
|
|
26
|
+
if (parsed.type === 'rpc.result') {
|
|
27
|
+
const resolve = pending.get(parsed.id);
|
|
26
28
|
if (resolve) {
|
|
27
|
-
resolve(parsed.result)
|
|
28
|
-
pending.delete(parsed.id)
|
|
29
|
+
resolve(parsed.result);
|
|
30
|
+
pending.delete(parsed.id);
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
|
-
}
|
|
33
|
+
};
|
|
32
34
|
return {
|
|
33
|
-
call<Method extends keyof T>(
|
|
34
|
-
|
|
35
|
+
call<Method extends keyof T>(
|
|
36
|
+
method: Method,
|
|
37
|
+
input: Parameters<T[Method]>[0]
|
|
38
|
+
): Promise<ReturnType<T[Method]>> {
|
|
39
|
+
const requestId = id++;
|
|
35
40
|
return new Promise((resolve) => {
|
|
36
|
-
pending.set(requestId, resolve)
|
|
37
|
-
target.postMessage(
|
|
38
|
-
|
|
41
|
+
pending.set(requestId, resolve);
|
|
42
|
+
target.postMessage(
|
|
43
|
+
JSON.stringify({
|
|
44
|
+
type: 'rpc.request',
|
|
45
|
+
method,
|
|
46
|
+
input,
|
|
47
|
+
id: requestId,
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
});
|
|
39
51
|
},
|
|
40
|
-
}
|
|
52
|
+
};
|
|
41
53
|
}
|
|
42
54
|
}
|
package/src/util/scrap.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export const foo: string =
|
|
2
|
-
export const bar: number = 123
|
|
1
|
+
export const foo: string = '42';
|
|
2
|
+
export const bar: number = 123;
|
|
3
3
|
|
|
4
4
|
export function dummyFunction(): void {
|
|
5
|
-
console.log(
|
|
5
|
+
console.log('This is a dummy function');
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function randomHelper(): boolean {
|
|
9
|
-
return Math.random() > 0.5
|
|
9
|
+
return Math.random() > 0.5;
|
|
10
10
|
}
|
package/src/util/signal.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export function signal() {
|
|
2
|
-
let resolve: any
|
|
3
|
-
const promise = new Promise((r) => (resolve = r))
|
|
2
|
+
let resolve: any;
|
|
3
|
+
const promise = new Promise((r) => (resolve = r));
|
|
4
4
|
return {
|
|
5
5
|
trigger() {
|
|
6
|
-
return resolve()
|
|
6
|
+
return resolve();
|
|
7
7
|
},
|
|
8
8
|
wait() {
|
|
9
|
-
return promise
|
|
9
|
+
return promise;
|
|
10
10
|
},
|
|
11
|
-
}
|
|
11
|
+
};
|
|
12
12
|
}
|
package/src/util/timeout.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
|
2
|
-
let timeout: NodeJS.Timeout
|
|
2
|
+
let timeout: NodeJS.Timeout;
|
|
3
3
|
return Promise.race([
|
|
4
4
|
promise.then((result) => {
|
|
5
|
-
clearTimeout(timeout)
|
|
6
|
-
return result
|
|
5
|
+
clearTimeout(timeout);
|
|
6
|
+
return result;
|
|
7
7
|
}),
|
|
8
8
|
new Promise<never>((_, reject) => {
|
|
9
9
|
timeout = setTimeout(() => {
|
|
10
|
-
reject(new Error(`Operation timed out after ${ms}ms`))
|
|
11
|
-
}, ms)
|
|
10
|
+
reject(new Error(`Operation timed out after ${ms}ms`));
|
|
11
|
+
}, ms);
|
|
12
12
|
}),
|
|
13
|
-
])
|
|
13
|
+
]);
|
|
14
14
|
}
|
package/src/util/token.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export namespace Token {
|
|
2
|
-
const CHARS_PER_TOKEN = 4
|
|
2
|
+
const CHARS_PER_TOKEN = 4;
|
|
3
3
|
|
|
4
4
|
export function estimate(input: string) {
|
|
5
|
-
return Math.max(0, Math.round((input ||
|
|
5
|
+
return Math.max(0, Math.round((input || '').length / CHARS_PER_TOKEN));
|
|
6
6
|
}
|
|
7
7
|
}
|
package/src/util/wildcard.ts
CHANGED
|
@@ -1,54 +1,65 @@
|
|
|
1
|
-
import { sortBy, pipe } from
|
|
1
|
+
import { sortBy, pipe } from 'remeda';
|
|
2
2
|
|
|
3
3
|
export namespace Wildcard {
|
|
4
4
|
export function match(str: string, pattern: string) {
|
|
5
5
|
const regex = new RegExp(
|
|
6
|
-
|
|
6
|
+
'^' +
|
|
7
7
|
pattern
|
|
8
|
-
.replace(/[.+^${}()|[\]\\]/g,
|
|
9
|
-
.replace(/\*/g,
|
|
10
|
-
.replace(/\?/g,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
)
|
|
14
|
-
return regex.test(str)
|
|
8
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape special regex chars
|
|
9
|
+
.replace(/\*/g, '.*') // * becomes .*
|
|
10
|
+
.replace(/\?/g, '.') + // ? becomes .
|
|
11
|
+
'$',
|
|
12
|
+
's' // s flag enables multiline matching
|
|
13
|
+
);
|
|
14
|
+
return regex.test(str);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function all(input: string, patterns: Record<string, any>) {
|
|
18
|
-
const sorted = pipe(
|
|
19
|
-
|
|
18
|
+
const sorted = pipe(
|
|
19
|
+
patterns,
|
|
20
|
+
Object.entries,
|
|
21
|
+
sortBy([([key]) => key.length, 'asc'], [([key]) => key, 'asc'])
|
|
22
|
+
);
|
|
23
|
+
let result = undefined;
|
|
20
24
|
for (const [pattern, value] of sorted) {
|
|
21
25
|
if (match(input, pattern)) {
|
|
22
|
-
result = value
|
|
23
|
-
continue
|
|
26
|
+
result = value;
|
|
27
|
+
continue;
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
|
-
return result
|
|
30
|
+
return result;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
export function allStructured(
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
export function allStructured(
|
|
34
|
+
input: { head: string; tail: string[] },
|
|
35
|
+
patterns: Record<string, any>
|
|
36
|
+
) {
|
|
37
|
+
const sorted = pipe(
|
|
38
|
+
patterns,
|
|
39
|
+
Object.entries,
|
|
40
|
+
sortBy([([key]) => key.length, 'asc'], [([key]) => key, 'asc'])
|
|
41
|
+
);
|
|
42
|
+
let result = undefined;
|
|
32
43
|
for (const [pattern, value] of sorted) {
|
|
33
|
-
const parts = pattern.split(/\s+/)
|
|
34
|
-
if (!match(input.head, parts[0])) continue
|
|
44
|
+
const parts = pattern.split(/\s+/);
|
|
45
|
+
if (!match(input.head, parts[0])) continue;
|
|
35
46
|
if (parts.length === 1 || matchSequence(input.tail, parts.slice(1))) {
|
|
36
|
-
result = value
|
|
37
|
-
continue
|
|
47
|
+
result = value;
|
|
48
|
+
continue;
|
|
38
49
|
}
|
|
39
50
|
}
|
|
40
|
-
return result
|
|
51
|
+
return result;
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
function matchSequence(items: string[], patterns: string[]): boolean {
|
|
44
|
-
if (patterns.length === 0) return true
|
|
45
|
-
const [pattern, ...rest] = patterns
|
|
46
|
-
if (pattern ===
|
|
55
|
+
if (patterns.length === 0) return true;
|
|
56
|
+
const [pattern, ...rest] = patterns;
|
|
57
|
+
if (pattern === '*') return matchSequence(items, rest);
|
|
47
58
|
for (let i = 0; i < items.length; i++) {
|
|
48
59
|
if (match(items[i], pattern) && matchSequence(items.slice(i + 1), rest)) {
|
|
49
|
-
return true
|
|
60
|
+
return true;
|
|
50
61
|
}
|
|
51
62
|
}
|
|
52
|
-
return false
|
|
63
|
+
return false;
|
|
53
64
|
}
|
|
54
65
|
}
|