@link-assistant/agent 0.0.9 → 0.0.12
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 +36 -0
- package/MODELS.md +72 -24
- package/README.md +59 -2
- package/TOOLS.md +20 -0
- package/package.json +35 -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 +469 -0
- package/src/cli/cmd/cmd.ts +2 -2
- package/src/cli/cmd/export.ts +41 -41
- package/src/cli/cmd/mcp.ts +144 -119
- 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 +39 -24
- 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 +346 -199
- package/src/json-standard/index.ts +67 -51
- 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/project/bootstrap.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { Format } from
|
|
2
|
-
import { FileWatcher } from
|
|
3
|
-
import { File } from
|
|
4
|
-
import { Flag } from
|
|
5
|
-
import { Project } from
|
|
6
|
-
import { Bus } from
|
|
7
|
-
import { Command } from
|
|
8
|
-
import { Instance } from
|
|
9
|
-
import { Log } from
|
|
1
|
+
import { Format } from '../format';
|
|
2
|
+
import { FileWatcher } from '../file/watcher';
|
|
3
|
+
import { File } from '../file';
|
|
4
|
+
import { Flag } from '../flag/flag';
|
|
5
|
+
import { Project } from './project';
|
|
6
|
+
import { Bus } from '../bus';
|
|
7
|
+
import { Command } from '../command';
|
|
8
|
+
import { Instance } from './instance';
|
|
9
|
+
import { Log } from '../util/log';
|
|
10
10
|
|
|
11
11
|
export async function InstanceBootstrap() {
|
|
12
|
-
Log.Default.info(
|
|
13
|
-
Format.init()
|
|
14
|
-
FileWatcher.init()
|
|
15
|
-
File.init()
|
|
12
|
+
Log.Default.info('bootstrapping', { directory: Instance.directory });
|
|
13
|
+
Format.init();
|
|
14
|
+
FileWatcher.init();
|
|
15
|
+
File.init();
|
|
16
16
|
|
|
17
17
|
Bus.subscribe(Command.Event.Executed, async (payload) => {
|
|
18
18
|
if (payload.properties.name === Command.Default.INIT) {
|
|
19
|
-
await Project.setInitialized(Instance.project.id)
|
|
19
|
+
await Project.setInitialized(Instance.project.id);
|
|
20
20
|
}
|
|
21
|
-
})
|
|
21
|
+
});
|
|
22
22
|
}
|
package/src/project/instance.ts
CHANGED
|
@@ -1,67 +1,74 @@
|
|
|
1
|
-
import { Log } from
|
|
2
|
-
import { Context } from
|
|
3
|
-
import { Project } from
|
|
4
|
-
import { State } from
|
|
5
|
-
import { iife } from
|
|
1
|
+
import { Log } from '../util/log';
|
|
2
|
+
import { Context } from '../util/context';
|
|
3
|
+
import { Project } from './project';
|
|
4
|
+
import { State } from './state';
|
|
5
|
+
import { iife } from '../util/iife';
|
|
6
6
|
|
|
7
7
|
interface Context {
|
|
8
|
-
directory: string
|
|
9
|
-
worktree: string
|
|
10
|
-
project: Project.Info
|
|
8
|
+
directory: string;
|
|
9
|
+
worktree: string;
|
|
10
|
+
project: Project.Info;
|
|
11
11
|
}
|
|
12
|
-
const context = Context.create<Context>(
|
|
13
|
-
const cache = new Map<string, Promise<Context>>()
|
|
12
|
+
const context = Context.create<Context>('instance');
|
|
13
|
+
const cache = new Map<string, Promise<Context>>();
|
|
14
14
|
|
|
15
15
|
export const Instance = {
|
|
16
|
-
async provide<R>(input: {
|
|
17
|
-
|
|
16
|
+
async provide<R>(input: {
|
|
17
|
+
directory: string;
|
|
18
|
+
init?: () => Promise<any>;
|
|
19
|
+
fn: () => R;
|
|
20
|
+
}): Promise<R> {
|
|
21
|
+
let existing = cache.get(input.directory);
|
|
18
22
|
if (!existing) {
|
|
19
|
-
Log.Default.info(
|
|
23
|
+
Log.Default.info('creating instance', { directory: input.directory });
|
|
20
24
|
existing = iife(async () => {
|
|
21
|
-
const project = await Project.fromDirectory(input.directory)
|
|
25
|
+
const project = await Project.fromDirectory(input.directory);
|
|
22
26
|
const ctx = {
|
|
23
27
|
directory: input.directory,
|
|
24
28
|
worktree: project.worktree,
|
|
25
29
|
project,
|
|
26
|
-
}
|
|
30
|
+
};
|
|
27
31
|
await context.provide(ctx, async () => {
|
|
28
|
-
await input.init?.()
|
|
29
|
-
})
|
|
30
|
-
return ctx
|
|
31
|
-
})
|
|
32
|
-
cache.set(input.directory, existing)
|
|
32
|
+
await input.init?.();
|
|
33
|
+
});
|
|
34
|
+
return ctx;
|
|
35
|
+
});
|
|
36
|
+
cache.set(input.directory, existing);
|
|
33
37
|
}
|
|
34
|
-
const ctx = await existing
|
|
38
|
+
const ctx = await existing;
|
|
35
39
|
return context.provide(ctx, async () => {
|
|
36
|
-
return input.fn()
|
|
37
|
-
})
|
|
40
|
+
return input.fn();
|
|
41
|
+
});
|
|
38
42
|
},
|
|
39
43
|
get directory() {
|
|
40
|
-
return context.use().directory
|
|
44
|
+
return context.use().directory;
|
|
41
45
|
},
|
|
42
46
|
get worktree() {
|
|
43
|
-
return context.use().worktree
|
|
47
|
+
return context.use().worktree;
|
|
44
48
|
},
|
|
45
49
|
get project() {
|
|
46
|
-
return context.use().project
|
|
50
|
+
return context.use().project;
|
|
47
51
|
},
|
|
48
|
-
state<S>(
|
|
49
|
-
|
|
52
|
+
state<S>(
|
|
53
|
+
init: () => S,
|
|
54
|
+
dispose?: (state: Awaited<S>) => Promise<void>
|
|
55
|
+
): () => S {
|
|
56
|
+
return State.create(() => Instance.directory, init, dispose);
|
|
50
57
|
},
|
|
51
58
|
async dispose() {
|
|
52
|
-
Log.Default.info(
|
|
53
|
-
await State.dispose(Instance.directory)
|
|
59
|
+
Log.Default.info('disposing instance', { directory: Instance.directory });
|
|
60
|
+
await State.dispose(Instance.directory);
|
|
54
61
|
},
|
|
55
62
|
async disposeAll() {
|
|
56
|
-
Log.Default.info(
|
|
63
|
+
Log.Default.info('disposing all instances');
|
|
57
64
|
for (const [_key, value] of cache) {
|
|
58
|
-
const awaited = await value.catch(() => {})
|
|
65
|
+
const awaited = await value.catch(() => {});
|
|
59
66
|
if (awaited) {
|
|
60
67
|
await context.provide(await value, async () => {
|
|
61
|
-
await Instance.dispose()
|
|
62
|
-
})
|
|
68
|
+
await Instance.dispose();
|
|
69
|
+
});
|
|
63
70
|
}
|
|
64
71
|
}
|
|
65
|
-
cache.clear()
|
|
72
|
+
cache.clear();
|
|
66
73
|
},
|
|
67
|
-
}
|
|
74
|
+
};
|
package/src/project/project.ts
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import z from
|
|
2
|
-
import { Filesystem } from
|
|
3
|
-
import path from
|
|
4
|
-
import { $ } from
|
|
5
|
-
import { Storage } from
|
|
6
|
-
import { Log } from
|
|
7
|
-
import { Flag } from
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import { Filesystem } from '../util/filesystem';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { $ } from 'bun';
|
|
5
|
+
import { Storage } from '../storage/storage';
|
|
6
|
+
import { Log } from '../util/log';
|
|
7
|
+
import { Flag } from '../flag/flag';
|
|
8
8
|
|
|
9
9
|
export namespace Project {
|
|
10
|
-
const log = Log.create({ service:
|
|
10
|
+
const log = Log.create({ service: 'project' });
|
|
11
11
|
export const Info = z
|
|
12
12
|
.object({
|
|
13
13
|
id: z.string(),
|
|
14
14
|
worktree: z.string(),
|
|
15
|
-
vcs: z.literal(
|
|
15
|
+
vcs: z.literal('git').optional(),
|
|
16
16
|
time: z.object({
|
|
17
17
|
created: z.number(),
|
|
18
18
|
initialized: z.number().optional(),
|
|
19
19
|
}),
|
|
20
20
|
})
|
|
21
21
|
.meta({
|
|
22
|
-
ref:
|
|
23
|
-
})
|
|
24
|
-
export type Info = z.infer<typeof Info
|
|
22
|
+
ref: 'Project',
|
|
23
|
+
});
|
|
24
|
+
export type Info = z.infer<typeof Info>;
|
|
25
25
|
|
|
26
26
|
export async function fromDirectory(directory: string) {
|
|
27
|
-
log.info(
|
|
28
|
-
const matches = Filesystem.up({ targets: [
|
|
29
|
-
const git = await matches.next().then((x) => x.value)
|
|
30
|
-
await matches.return()
|
|
27
|
+
log.info('fromDirectory', { directory });
|
|
28
|
+
const matches = Filesystem.up({ targets: ['.git'], start: directory });
|
|
29
|
+
const git = await matches.next().then((x) => x.value);
|
|
30
|
+
await matches.return();
|
|
31
31
|
if (!git) {
|
|
32
32
|
const project: Info = {
|
|
33
|
-
id:
|
|
34
|
-
worktree:
|
|
35
|
-
vcs:
|
|
33
|
+
id: 'global',
|
|
34
|
+
worktree: '/',
|
|
35
|
+
vcs: 'none', // No VCS
|
|
36
36
|
time: {
|
|
37
37
|
created: Date.now(),
|
|
38
38
|
},
|
|
39
|
-
}
|
|
40
|
-
await Storage.write<Info>([
|
|
41
|
-
return project
|
|
39
|
+
};
|
|
40
|
+
await Storage.write<Info>(['project', 'global'], project);
|
|
41
|
+
return project;
|
|
42
42
|
}
|
|
43
|
-
let worktree = path.dirname(git)
|
|
44
|
-
const timer = log.time(
|
|
45
|
-
let id = await Bun.file(path.join(git,
|
|
43
|
+
let worktree = path.dirname(git);
|
|
44
|
+
const timer = log.time('git.rev-parse');
|
|
45
|
+
let id = await Bun.file(path.join(git, 'opencode'))
|
|
46
46
|
.text()
|
|
47
47
|
.then((x) => x.trim())
|
|
48
|
-
.catch(() => {})
|
|
48
|
+
.catch(() => {});
|
|
49
49
|
if (!id) {
|
|
50
50
|
const roots = await $`git rev-list --max-parents=0 --all`
|
|
51
51
|
.quiet()
|
|
@@ -54,52 +54,52 @@ export namespace Project {
|
|
|
54
54
|
.text()
|
|
55
55
|
.then((x) =>
|
|
56
56
|
x
|
|
57
|
-
.split(
|
|
57
|
+
.split('\n')
|
|
58
58
|
.filter(Boolean)
|
|
59
59
|
.map((x) => x.trim())
|
|
60
|
-
.toSorted()
|
|
61
|
-
)
|
|
62
|
-
id = roots[0]
|
|
63
|
-
if (id) Bun.file(path.join(git,
|
|
60
|
+
.toSorted()
|
|
61
|
+
);
|
|
62
|
+
id = roots[0];
|
|
63
|
+
if (id) Bun.file(path.join(git, 'opencode')).write(id);
|
|
64
64
|
}
|
|
65
|
-
timer.stop()
|
|
65
|
+
timer.stop();
|
|
66
66
|
if (!id) {
|
|
67
67
|
const project: Info = {
|
|
68
|
-
id:
|
|
69
|
-
worktree:
|
|
68
|
+
id: 'global',
|
|
69
|
+
worktree: '/',
|
|
70
70
|
time: {
|
|
71
71
|
created: Date.now(),
|
|
72
72
|
},
|
|
73
|
-
}
|
|
74
|
-
await Storage.write<Info>([
|
|
75
|
-
return project
|
|
73
|
+
};
|
|
74
|
+
await Storage.write<Info>(['project', 'global'], project);
|
|
75
|
+
return project;
|
|
76
76
|
}
|
|
77
77
|
worktree = await $`git rev-parse --path-format=absolute --show-toplevel`
|
|
78
78
|
.quiet()
|
|
79
79
|
.nothrow()
|
|
80
80
|
.cwd(worktree)
|
|
81
81
|
.text()
|
|
82
|
-
.then((x) => x.trim())
|
|
82
|
+
.then((x) => x.trim());
|
|
83
83
|
const project: Info = {
|
|
84
84
|
id,
|
|
85
85
|
worktree,
|
|
86
|
-
vcs:
|
|
86
|
+
vcs: 'git',
|
|
87
87
|
time: {
|
|
88
88
|
created: Date.now(),
|
|
89
89
|
},
|
|
90
|
-
}
|
|
91
|
-
await Storage.write<Info>([
|
|
92
|
-
return project
|
|
90
|
+
};
|
|
91
|
+
await Storage.write<Info>(['project', id], project);
|
|
92
|
+
return project;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
export async function setInitialized(projectID: string) {
|
|
96
|
-
await Storage.update<Info>([
|
|
97
|
-
draft.time.initialized = Date.now()
|
|
98
|
-
})
|
|
96
|
+
await Storage.update<Info>(['project', projectID], (draft) => {
|
|
97
|
+
draft.time.initialized = Date.now();
|
|
98
|
+
});
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
export async function list() {
|
|
102
|
-
const keys = await Storage.list([
|
|
103
|
-
return await Promise.all(keys.map((x) => Storage.read<Info>(x)))
|
|
102
|
+
const keys = await Storage.list(['project']);
|
|
103
|
+
return await Promise.all(keys.map((x) => Storage.read<Info>(x)));
|
|
104
104
|
}
|
|
105
105
|
}
|
package/src/project/state.ts
CHANGED
|
@@ -1,65 +1,69 @@
|
|
|
1
|
-
import { Log } from
|
|
1
|
+
import { Log } from '../util/log';
|
|
2
2
|
|
|
3
3
|
export namespace State {
|
|
4
4
|
interface Entry {
|
|
5
|
-
state: any
|
|
6
|
-
dispose?: (state: any) => Promise<void
|
|
5
|
+
state: any;
|
|
6
|
+
dispose?: (state: any) => Promise<void>;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const log = Log.create({ service:
|
|
10
|
-
const recordsByKey = new Map<string, Map<any, Entry>>()
|
|
9
|
+
const log = Log.create({ service: 'state' });
|
|
10
|
+
const recordsByKey = new Map<string, Map<any, Entry>>();
|
|
11
11
|
|
|
12
|
-
export function create<S>(
|
|
12
|
+
export function create<S>(
|
|
13
|
+
root: () => string,
|
|
14
|
+
init: () => S,
|
|
15
|
+
dispose?: (state: Awaited<S>) => Promise<void>
|
|
16
|
+
) {
|
|
13
17
|
return () => {
|
|
14
|
-
const key = root()
|
|
15
|
-
let entries = recordsByKey.get(key)
|
|
18
|
+
const key = root();
|
|
19
|
+
let entries = recordsByKey.get(key);
|
|
16
20
|
if (!entries) {
|
|
17
|
-
entries = new Map<string, Entry>()
|
|
18
|
-
recordsByKey.set(key, entries)
|
|
21
|
+
entries = new Map<string, Entry>();
|
|
22
|
+
recordsByKey.set(key, entries);
|
|
19
23
|
}
|
|
20
|
-
const exists = entries.get(init)
|
|
21
|
-
if (exists) return exists.state as S
|
|
22
|
-
const state = init()
|
|
24
|
+
const exists = entries.get(init);
|
|
25
|
+
if (exists) return exists.state as S;
|
|
26
|
+
const state = init();
|
|
23
27
|
entries.set(init, {
|
|
24
28
|
state,
|
|
25
29
|
dispose,
|
|
26
|
-
})
|
|
27
|
-
return state
|
|
28
|
-
}
|
|
30
|
+
});
|
|
31
|
+
return state;
|
|
32
|
+
};
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
export async function dispose(key: string) {
|
|
32
|
-
const entries = recordsByKey.get(key)
|
|
33
|
-
if (!entries) return
|
|
36
|
+
const entries = recordsByKey.get(key);
|
|
37
|
+
if (!entries) return;
|
|
34
38
|
|
|
35
|
-
log.info(
|
|
39
|
+
log.info('waiting for state disposal to complete', { key });
|
|
36
40
|
|
|
37
|
-
let disposalFinished = false
|
|
41
|
+
let disposalFinished = false;
|
|
38
42
|
|
|
39
43
|
setTimeout(() => {
|
|
40
44
|
if (!disposalFinished) {
|
|
41
45
|
log.warn(
|
|
42
|
-
|
|
43
|
-
{ key }
|
|
44
|
-
)
|
|
46
|
+
'state disposal is taking an unusually long time - if it does not complete in a reasonable time, please report this as a bug',
|
|
47
|
+
{ key }
|
|
48
|
+
);
|
|
45
49
|
}
|
|
46
|
-
}, 10000).unref()
|
|
50
|
+
}, 10000).unref();
|
|
47
51
|
|
|
48
|
-
const tasks: Promise<void>[] = []
|
|
52
|
+
const tasks: Promise<void>[] = [];
|
|
49
53
|
for (const entry of entries.values()) {
|
|
50
|
-
if (!entry.dispose) continue
|
|
54
|
+
if (!entry.dispose) continue;
|
|
51
55
|
|
|
52
56
|
const task = Promise.resolve(entry.state)
|
|
53
57
|
.then((state) => entry.dispose!(state))
|
|
54
58
|
.catch((error) => {
|
|
55
|
-
log.error(
|
|
56
|
-
})
|
|
59
|
+
log.error('Error while disposing state:', { error, key });
|
|
60
|
+
});
|
|
57
61
|
|
|
58
|
-
tasks.push(task)
|
|
62
|
+
tasks.push(task);
|
|
59
63
|
}
|
|
60
|
-
await Promise.all(tasks)
|
|
61
|
-
recordsByKey.delete(key)
|
|
62
|
-
disposalFinished = true
|
|
63
|
-
log.info(
|
|
64
|
+
await Promise.all(tasks);
|
|
65
|
+
recordsByKey.delete(key);
|
|
66
|
+
disposalFinished = true;
|
|
67
|
+
log.info('state disposal completed', { key });
|
|
64
68
|
}
|
|
65
69
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export async function data() {
|
|
2
|
-
const path = Bun.env.MODELS_DEV_API_JSON
|
|
2
|
+
const path = Bun.env.MODELS_DEV_API_JSON;
|
|
3
3
|
if (path) {
|
|
4
|
-
const file = Bun.file(path)
|
|
4
|
+
const file = Bun.file(path);
|
|
5
5
|
if (await file.exists()) {
|
|
6
|
-
return await file.text()
|
|
6
|
+
return await file.text();
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
const json = await fetch(
|
|
10
|
-
return json
|
|
9
|
+
const json = await fetch('https://models.dev/api.json').then((x) => x.text());
|
|
10
|
+
return json;
|
|
11
11
|
}
|
package/src/provider/models.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Global } from
|
|
2
|
-
import { Log } from
|
|
3
|
-
import path from
|
|
4
|
-
import z from
|
|
5
|
-
import { data } from
|
|
1
|
+
import { Global } from '../global';
|
|
2
|
+
import { Log } from '../util/log';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import z from 'zod';
|
|
5
|
+
import { data } from './models-macro';
|
|
6
6
|
|
|
7
7
|
export namespace ModelsDev {
|
|
8
|
-
const log = Log.create({ service:
|
|
9
|
-
const filepath = path.join(Global.Path.cache,
|
|
8
|
+
const log = Log.create({ service: 'models.dev' });
|
|
9
|
+
const filepath = path.join(Global.Path.cache, 'models.json');
|
|
10
10
|
|
|
11
11
|
export const Model = z
|
|
12
12
|
.object({
|
|
@@ -37,20 +37,20 @@ export namespace ModelsDev {
|
|
|
37
37
|
}),
|
|
38
38
|
modalities: z
|
|
39
39
|
.object({
|
|
40
|
-
input: z.array(z.enum([
|
|
41
|
-
output: z.array(z.enum([
|
|
40
|
+
input: z.array(z.enum(['text', 'audio', 'image', 'video', 'pdf'])),
|
|
41
|
+
output: z.array(z.enum(['text', 'audio', 'image', 'video', 'pdf'])),
|
|
42
42
|
})
|
|
43
43
|
.optional(),
|
|
44
44
|
experimental: z.boolean().optional(),
|
|
45
|
-
status: z.enum([
|
|
45
|
+
status: z.enum(['alpha', 'beta', 'deprecated']).optional(),
|
|
46
46
|
options: z.record(z.string(), z.any()),
|
|
47
47
|
headers: z.record(z.string(), z.string()).optional(),
|
|
48
48
|
provider: z.object({ npm: z.string() }).optional(),
|
|
49
49
|
})
|
|
50
50
|
.meta({
|
|
51
|
-
ref:
|
|
52
|
-
})
|
|
53
|
-
export type Model = z.infer<typeof Model
|
|
51
|
+
ref: 'Model',
|
|
52
|
+
});
|
|
53
|
+
export type Model = z.infer<typeof Model>;
|
|
54
54
|
|
|
55
55
|
export const Provider = z
|
|
56
56
|
.object({
|
|
@@ -62,37 +62,37 @@ export namespace ModelsDev {
|
|
|
62
62
|
models: z.record(z.string(), Model),
|
|
63
63
|
})
|
|
64
64
|
.meta({
|
|
65
|
-
ref:
|
|
66
|
-
})
|
|
65
|
+
ref: 'Provider',
|
|
66
|
+
});
|
|
67
67
|
|
|
68
|
-
export type Provider = z.infer<typeof Provider
|
|
68
|
+
export type Provider = z.infer<typeof Provider>;
|
|
69
69
|
|
|
70
70
|
export async function get() {
|
|
71
|
-
refresh()
|
|
72
|
-
const file = Bun.file(filepath)
|
|
73
|
-
const result = await file.json().catch(() => {})
|
|
74
|
-
if (result) return result as Record<string, Provider
|
|
75
|
-
const json = await data()
|
|
76
|
-
return JSON.parse(json) as Record<string, Provider
|
|
71
|
+
refresh();
|
|
72
|
+
const file = Bun.file(filepath);
|
|
73
|
+
const result = await file.json().catch(() => {});
|
|
74
|
+
if (result) return result as Record<string, Provider>;
|
|
75
|
+
const json = await data();
|
|
76
|
+
return JSON.parse(json) as Record<string, Provider>;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export async function refresh() {
|
|
80
|
-
const file = Bun.file(filepath)
|
|
81
|
-
log.info(
|
|
80
|
+
const file = Bun.file(filepath);
|
|
81
|
+
log.info('refreshing', {
|
|
82
82
|
file,
|
|
83
|
-
})
|
|
84
|
-
const result = await fetch(
|
|
83
|
+
});
|
|
84
|
+
const result = await fetch('https://models.dev/api.json', {
|
|
85
85
|
headers: {
|
|
86
|
-
|
|
86
|
+
'User-Agent': 'agent-cli/1.0.0',
|
|
87
87
|
},
|
|
88
88
|
signal: AbortSignal.timeout(10 * 1000),
|
|
89
89
|
}).catch((e) => {
|
|
90
|
-
log.error(
|
|
90
|
+
log.error('Failed to fetch models.dev', {
|
|
91
91
|
error: e,
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
if (result && result.ok) await Bun.write(file, await result.text())
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
if (result && result.ok) await Bun.write(file, await result.text());
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
setInterval(() => ModelsDev.refresh(), 60 * 1000 * 60).unref()
|
|
98
|
+
setInterval(() => ModelsDev.refresh(), 60 * 1000 * 60).unref();
|
package/src/provider/opencode.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// Permalink: https://github.com/sst/opencode/blob/main/packages/opencode/src/provider/provider.ts
|
|
2
2
|
|
|
3
|
-
import { createOpenCode } from '@opencode-ai/sdk'
|
|
3
|
+
import { createOpenCode } from '@opencode-ai/sdk';
|
|
4
4
|
|
|
5
5
|
class OpenCodeProvider {
|
|
6
6
|
constructor() {
|
|
7
|
-
this.name = 'opencode'
|
|
7
|
+
this.name = 'opencode';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
getModel(modelId) {
|
|
11
11
|
// For grok-code, return a model that uses the opencode API
|
|
12
12
|
if (modelId === 'grok-code') {
|
|
13
13
|
return {
|
|
@@ -15,33 +15,33 @@ class OpenCodeProvider {
|
|
|
15
15
|
provider: this,
|
|
16
16
|
generateText: async (options) => {
|
|
17
17
|
// Use opencode API
|
|
18
|
-
const opencode = await createOpenCode()
|
|
19
|
-
const { client } = opencode
|
|
18
|
+
const opencode = await createOpenCode();
|
|
19
|
+
const { client } = opencode;
|
|
20
20
|
|
|
21
|
-
const sessionResult = await client.session.create()
|
|
22
|
-
const sessionId = sessionResult.data?.id
|
|
21
|
+
const sessionResult = await client.session.create();
|
|
22
|
+
const sessionId = sessionResult.data?.id;
|
|
23
23
|
|
|
24
24
|
await client.session.prompt({
|
|
25
25
|
path: { id: sessionId },
|
|
26
26
|
body: {
|
|
27
|
-
agent:
|
|
28
|
-
model: { providerID:
|
|
29
|
-
parts: [{ type:
|
|
30
|
-
}
|
|
31
|
-
})
|
|
27
|
+
agent: 'build',
|
|
28
|
+
model: { providerID: 'opencode', modelID: 'grok-code' },
|
|
29
|
+
parts: [{ type: 'text', text: options.prompt }],
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
32
|
|
|
33
33
|
// For simplicity, return a mock response
|
|
34
34
|
return {
|
|
35
35
|
text: 'Hello from Grok Code!',
|
|
36
|
-
usage: { promptTokens: 10, completionTokens: 5 }
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
36
|
+
usage: { promptTokens: 10, completionTokens: 5 },
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
40
|
}
|
|
41
|
-
throw new Error(`Model ${modelId} not found`)
|
|
41
|
+
throw new Error(`Model ${modelId} not found`);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const opencodeProvider = new OpenCodeProvider()
|
|
45
|
+
const opencodeProvider = new OpenCodeProvider();
|
|
46
46
|
|
|
47
|
-
export { opencodeProvider }
|
|
47
|
+
export { opencodeProvider };
|