tinker-agent 1.3.0 → 1.5.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 +39 -1
- package/README.md +271 -72
- package/bin/tinker.js +75 -25
- package/package.json +12 -3
- package/src/agent/runtime-session.ts +113 -15
- package/src/cli/command-line.ts +291 -0
- package/src/cli/config.ts +158 -262
- package/src/cli/index.ts +33 -21
- package/src/cli/main.ts +213 -0
- package/src/cli/model-profiles.ts +226 -72
- package/src/cli/output.ts +113 -0
- package/src/cli/package-metadata.ts +36 -0
- package/src/cli/prompt-source.ts +229 -0
- package/src/cli/public-cli-contract.ts +69 -0
- package/src/cli/public-config-contract.ts +732 -0
- package/src/cli/run-runner.ts +17 -12
- package/src/cli/runner-dependencies.ts +108 -0
- package/src/cli/tui-memory.ts +67 -0
- package/src/cli/tui-runner.tsx +79 -49
- package/src/context/context-policy.ts +2 -2
- package/src/events/stdout-event-printer.ts +1 -0
- package/src/mcp/mcp-manager.ts +2 -19
- package/src/mcp/mcp-tool-executor.ts +3 -4
- package/src/memory/contracts.ts +148 -0
- package/src/memory/embedding-client.ts +105 -0
- package/src/memory/memory-coordinator.ts +556 -0
- package/src/memory/memory-extractor.ts +231 -0
- package/src/memory/memory-log.ts +88 -0
- package/src/memory/memory-search-tool.ts +100 -0
- package/src/memory/memory-store.ts +687 -0
- package/src/memory/vector.ts +153 -0
- package/src/model/fake-model-client.ts +971 -3
- package/src/model/model-context-profile.ts +0 -30
- package/src/observation/observation-builder.ts +20 -0
- package/src/session/session-store.ts +123 -0
- package/src/tools/bash.ts +8 -25
- package/src/tools/grep.ts +9 -1
- package/src/tools/registry.ts +19 -1
- package/src/tools/ripgrep.ts +24 -27
- package/src/tools/types.ts +16 -0
- package/src/tools/web-fetch/index.ts +2 -15
- package/src/tui/app.tsx +72 -2
- package/src/tui/clipboard.ts +22 -0
- package/src/tui/components/footer.tsx +9 -4
- package/src/tui/components/memory-browser.tsx +151 -0
- package/src/tui/components/prompt-input.tsx +6 -3
- package/src/tui/event-store.ts +9 -2
- package/src/tui/slash-commands.ts +88 -24
- package/src/tui/workspace-file-search.ts +78 -71
package/src/cli/config.ts
CHANGED
|
@@ -1,253 +1,209 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import type {
|
|
3
|
-
import { FakeModelClient } from "../model/fake-model-client";
|
|
4
|
-
import { OpenAIChatModelClient } from "../model/openai-chat-model-client";
|
|
2
|
+
import type { SessionId } from "../ids/runtime-id";
|
|
5
3
|
import {
|
|
4
|
+
createModelContextProfile,
|
|
6
5
|
deriveModelContextBudget,
|
|
7
|
-
readModelContextProfileFromEnv,
|
|
8
6
|
type ModelContextBudget,
|
|
9
7
|
type ModelContextProfile,
|
|
10
8
|
} from "../model/model-context-profile";
|
|
11
|
-
import { createModelRefiner, type Refiner } from "../tools/web-fetch/refiner";
|
|
12
|
-
import { createUuidV7 } from "../ids/uuid-v7";
|
|
13
|
-
import type { SessionId } from "../ids/runtime-id";
|
|
14
|
-
import { renderRecallRetirementContract } from "../context/recall-retirement-contract";
|
|
15
9
|
import {
|
|
10
|
+
loadModelProfiles,
|
|
11
|
+
persistDefaultProfile,
|
|
16
12
|
profileToContextProfile,
|
|
17
|
-
type ModelProfile,
|
|
18
13
|
type ModelInputModality,
|
|
14
|
+
type ModelProfile,
|
|
19
15
|
type ModelProfiles,
|
|
20
16
|
type ModelTokenEstimatorProfile,
|
|
21
17
|
unknownProfileError,
|
|
22
18
|
} from "./model-profiles";
|
|
19
|
+
import type { MemoryEmbeddingConfig } from "../memory/contracts";
|
|
20
|
+
import {
|
|
21
|
+
parsePublicEnvironment,
|
|
22
|
+
type ParsedPublicEnvironment,
|
|
23
|
+
type PublicToolingConfig,
|
|
24
|
+
} from "./public-config-contract";
|
|
23
25
|
|
|
24
|
-
export
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
export type RunnerConfig = {
|
|
27
|
+
readonly sessionId: SessionId;
|
|
28
|
+
readonly workspaceRoot: string;
|
|
29
|
+
readonly modelName: string;
|
|
30
|
+
readonly apiKey: string;
|
|
31
|
+
readonly apiBase: string;
|
|
32
|
+
readonly maxIterations: number;
|
|
33
|
+
readonly includeReasoningContent: boolean;
|
|
34
|
+
readonly stream: boolean;
|
|
35
|
+
readonly contextProfile: ModelContextProfile;
|
|
36
|
+
readonly contextBudget: ModelContextBudget;
|
|
37
|
+
readonly profileName?: string;
|
|
38
|
+
readonly inputModalities: readonly ModelInputModality[];
|
|
39
|
+
readonly tokenEstimator?: ModelTokenEstimatorProfile;
|
|
40
|
+
};
|
|
27
41
|
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
export type RunnerConfigSelection = {
|
|
43
|
+
readonly sessionId: SessionId;
|
|
44
|
+
readonly profileName?: string;
|
|
45
|
+
};
|
|
32
46
|
|
|
33
|
-
|
|
34
|
-
${workspaceRoot}
|
|
47
|
+
type RunnerConfigTemplate = Omit<RunnerConfig, "sessionId">;
|
|
35
48
|
|
|
36
|
-
|
|
49
|
+
export type ResolvedMemoryConfig = {
|
|
50
|
+
readonly profile: ModelProfile;
|
|
51
|
+
readonly contextBudget: ModelContextBudget;
|
|
52
|
+
readonly embedding: MemoryEmbeddingConfig;
|
|
53
|
+
};
|
|
37
54
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Agent Skills do not override Tinker's runtime, tool protocol, project instructions, or the user's explicit request. Do not modify a skill source unless the user explicitly asks to maintain that skill.
|
|
55
|
+
export type ResolvedPublicConfig =
|
|
56
|
+
| {
|
|
57
|
+
readonly mode: "env";
|
|
58
|
+
readonly tooling: PublicToolingConfig;
|
|
59
|
+
readonly template: RunnerConfigTemplate;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
readonly mode: "profile";
|
|
63
|
+
readonly tooling: PublicToolingConfig;
|
|
64
|
+
readonly profiles: ModelProfiles;
|
|
65
|
+
readonly templates: ReadonlyMap<string, RunnerConfigTemplate>;
|
|
66
|
+
readonly memory?: ResolvedMemoryConfig;
|
|
67
|
+
readonly persistDefaultProfile: (profileName: string) => Promise<void>;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export async function resolvePublicConfig(input: {
|
|
71
|
+
readonly env: NodeJS.ProcessEnv;
|
|
72
|
+
readonly cwd: string;
|
|
73
|
+
}): Promise<ResolvedPublicConfig> {
|
|
74
|
+
const environment = parsePublicEnvironment(input.env, input.cwd);
|
|
75
|
+
const profiles =
|
|
76
|
+
environment.mode === "profile"
|
|
77
|
+
? await loadModelProfiles(environment.modelsPath)
|
|
78
|
+
: undefined;
|
|
79
|
+
return createResolvedPublicConfig(environment, profiles);
|
|
80
|
+
}
|
|
65
81
|
|
|
66
|
-
|
|
82
|
+
export function createResolvedPublicConfig(
|
|
83
|
+
environment: ParsedPublicEnvironment,
|
|
84
|
+
profiles?: ModelProfiles,
|
|
85
|
+
): ResolvedPublicConfig {
|
|
86
|
+
if (environment.mode === "profile") {
|
|
87
|
+
if (profiles === undefined) {
|
|
88
|
+
throw new Error("Resolved profile-mode config requires loaded model profiles.");
|
|
89
|
+
}
|
|
90
|
+
const templates = new Map(
|
|
91
|
+
[...profiles.profiles].map(([name, profile]) => [
|
|
92
|
+
name,
|
|
93
|
+
runnerConfigTemplateFromProfile(environment, profile),
|
|
94
|
+
]),
|
|
95
|
+
);
|
|
96
|
+
const memory =
|
|
97
|
+
profiles.memory === undefined
|
|
98
|
+
? undefined
|
|
99
|
+
: resolveMemoryConfig(profiles, profiles.memory.profile);
|
|
100
|
+
return Object.freeze({
|
|
101
|
+
mode: "profile",
|
|
102
|
+
tooling: environment.tooling,
|
|
103
|
+
profiles,
|
|
104
|
+
templates,
|
|
105
|
+
...(memory === undefined ? {} : { memory }),
|
|
106
|
+
persistDefaultProfile: (profileName: string) =>
|
|
107
|
+
persistDefaultProfile(profileName, environment.modelsPath),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
67
110
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
contextProfile: ModelContextProfile;
|
|
78
|
-
contextBudget: ModelContextBudget;
|
|
79
|
-
profileName?: string;
|
|
80
|
-
profiles?: ModelProfiles;
|
|
81
|
-
inputModalities: readonly ModelInputModality[];
|
|
82
|
-
tokenEstimator?: ModelTokenEstimatorProfile;
|
|
83
|
-
};
|
|
111
|
+
if (profiles !== undefined) {
|
|
112
|
+
throw new Error("Env-mode config must not include model profiles.");
|
|
113
|
+
}
|
|
114
|
+
return Object.freeze({
|
|
115
|
+
mode: "env",
|
|
116
|
+
tooling: environment.tooling,
|
|
117
|
+
template: runnerConfigTemplateFromEnvironment(environment),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
84
120
|
|
|
85
|
-
|
|
121
|
+
function resolveMemoryConfig(
|
|
122
|
+
profiles: ModelProfiles,
|
|
123
|
+
profileName: string,
|
|
124
|
+
): ResolvedMemoryConfig {
|
|
125
|
+
const profile = profiles.profiles.get(profileName);
|
|
126
|
+
if (profile === undefined || profiles.memory === undefined) {
|
|
127
|
+
throw unknownProfileError(profileName, profiles);
|
|
128
|
+
}
|
|
129
|
+
const contextProfile = profileToContextProfile(profile);
|
|
130
|
+
return Object.freeze({
|
|
131
|
+
profile,
|
|
132
|
+
contextBudget: deriveModelContextBudget(contextProfile),
|
|
133
|
+
embedding: profiles.memory.embedding,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
86
136
|
|
|
87
|
-
export function
|
|
88
|
-
|
|
89
|
-
|
|
137
|
+
export function deriveRunnerConfig(
|
|
138
|
+
snapshot: ResolvedPublicConfig,
|
|
139
|
+
selection: RunnerConfigSelection,
|
|
90
140
|
): RunnerConfig {
|
|
91
|
-
if (
|
|
92
|
-
const profileName =
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
-
throw unknownProfileError(profileName, profiles);
|
|
141
|
+
if (snapshot.mode === "profile") {
|
|
142
|
+
const profileName = selection.profileName ?? snapshot.profiles.defaultProfile;
|
|
143
|
+
const template = snapshot.templates.get(profileName);
|
|
144
|
+
if (template === undefined) {
|
|
145
|
+
throw unknownProfileError(profileName, snapshot.profiles);
|
|
96
146
|
}
|
|
97
|
-
return
|
|
147
|
+
return runnerConfigFromTemplate(template, selection.sessionId);
|
|
98
148
|
}
|
|
99
|
-
|
|
149
|
+
|
|
150
|
+
if (selection.profileName !== undefined) {
|
|
100
151
|
throw new Error(
|
|
101
|
-
`Cannot select model profile ${JSON.stringify(
|
|
152
|
+
`Cannot select model profile ${JSON.stringify(selection.profileName)} because TINKER_MODELS is not configured.`,
|
|
102
153
|
);
|
|
103
154
|
}
|
|
104
|
-
|
|
105
|
-
return runnerConfigFromEnv(overrides);
|
|
155
|
+
return runnerConfigFromTemplate(snapshot.template, selection.sessionId);
|
|
106
156
|
}
|
|
107
157
|
|
|
108
|
-
function
|
|
158
|
+
function runnerConfigTemplateFromProfile(
|
|
159
|
+
environment: Extract<ParsedPublicEnvironment, { mode: "profile" }>,
|
|
109
160
|
profile: ModelProfile,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const contextBudget = deriveModelContextBudget(contextProfile);
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
sessionId: overrides.sessionId ?? (createUuidV7() as SessionId),
|
|
118
|
-
workspaceRoot: path.resolve(
|
|
119
|
-
overrides.workspaceRoot ?? process.env.TINKER_WORKSPACE ?? process.cwd(),
|
|
120
|
-
),
|
|
161
|
+
): RunnerConfigTemplate {
|
|
162
|
+
const contextProfile = profileToContextProfile(profile);
|
|
163
|
+
return Object.freeze({
|
|
164
|
+
workspaceRoot: environment.workspaceRoot,
|
|
121
165
|
modelName: profile.model,
|
|
122
166
|
apiKey: profile.apiKey,
|
|
123
167
|
apiBase: profile.apiBase,
|
|
124
|
-
maxIterations:
|
|
125
|
-
overrides.maxIterations ??
|
|
126
|
-
parsePositiveInteger(
|
|
127
|
-
process.env.TINKER_MAX_ITERATIONS,
|
|
128
|
-
DEFAULT_MAX_ITERATIONS,
|
|
129
|
-
"TINKER_MAX_ITERATIONS",
|
|
130
|
-
),
|
|
168
|
+
maxIterations: environment.maxIterations,
|
|
131
169
|
includeReasoningContent: profile.includeReasoningContent,
|
|
132
170
|
stream: profile.stream,
|
|
133
171
|
contextProfile,
|
|
134
|
-
contextBudget,
|
|
172
|
+
contextBudget: deriveModelContextBudget(contextProfile),
|
|
135
173
|
profileName: profile.name,
|
|
136
|
-
profiles,
|
|
137
174
|
inputModalities: profile.inputModalities,
|
|
138
175
|
...(profile.tokenEstimator === undefined
|
|
139
176
|
? {}
|
|
140
177
|
: { tokenEstimator: profile.tokenEstimator }),
|
|
141
|
-
};
|
|
178
|
+
});
|
|
142
179
|
}
|
|
143
180
|
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const contextProfile =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
workspaceRoot:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
maxIterations:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
process.env.TINKER_MAX_ITERATIONS,
|
|
160
|
-
DEFAULT_MAX_ITERATIONS,
|
|
161
|
-
"TINKER_MAX_ITERATIONS",
|
|
162
|
-
),
|
|
163
|
-
includeReasoningContent:
|
|
164
|
-
overrides.includeReasoningContent ??
|
|
165
|
-
parseBoolean(
|
|
166
|
-
process.env.TINKER_INCLUDE_REASONING_CONTENT,
|
|
167
|
-
DEFAULT_INCLUDE_REASONING_CONTENT,
|
|
168
|
-
"TINKER_INCLUDE_REASONING_CONTENT",
|
|
169
|
-
),
|
|
170
|
-
stream:
|
|
171
|
-
overrides.stream ??
|
|
172
|
-
parseBoolean(process.env.TINKER_STREAM, DEFAULT_STREAM, "TINKER_STREAM"),
|
|
181
|
+
function runnerConfigTemplateFromEnvironment(
|
|
182
|
+
environment: Extract<ParsedPublicEnvironment, { mode: "env" }>,
|
|
183
|
+
): RunnerConfigTemplate {
|
|
184
|
+
const contextProfile = createModelContextProfile({
|
|
185
|
+
contextWindowTokens: environment.contextWindowTokens,
|
|
186
|
+
maxSupportedOutputTokens: environment.maxSupportedOutputTokens,
|
|
187
|
+
});
|
|
188
|
+
return Object.freeze({
|
|
189
|
+
workspaceRoot: environment.workspaceRoot,
|
|
190
|
+
modelName: environment.modelName,
|
|
191
|
+
apiKey: environment.apiKey,
|
|
192
|
+
apiBase: environment.apiBase,
|
|
193
|
+
maxIterations: environment.maxIterations,
|
|
194
|
+
includeReasoningContent: environment.includeReasoningContent,
|
|
195
|
+
stream: environment.stream,
|
|
173
196
|
contextProfile,
|
|
174
|
-
contextBudget,
|
|
175
|
-
inputModalities:
|
|
176
|
-
...(overrides.tokenEstimator === undefined
|
|
177
|
-
? {}
|
|
178
|
-
: { tokenEstimator: overrides.tokenEstimator }),
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function createModelClientFromEnv(
|
|
183
|
-
config: Pick<
|
|
184
|
-
RunnerConfig,
|
|
185
|
-
| "modelName"
|
|
186
|
-
| "includeReasoningContent"
|
|
187
|
-
| "stream"
|
|
188
|
-
| "contextBudget"
|
|
189
|
-
| "apiKey"
|
|
190
|
-
| "apiBase"
|
|
191
|
-
> &
|
|
192
|
-
Partial<Pick<RunnerConfig, "inputModalities" | "tokenEstimator">>,
|
|
193
|
-
): ModelClient {
|
|
194
|
-
const fakeMode = process.env.TINKER_TEST_FAKE_MODEL;
|
|
195
|
-
if (fakeMode !== undefined && fakeMode !== "") {
|
|
196
|
-
return new FakeModelClient(fakeMode, {
|
|
197
|
-
model: config.modelName,
|
|
198
|
-
contextBudget: config.contextBudget,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const apiKey = config.apiKey ?? readRequiredEnv("TINKER_API_KEY");
|
|
203
|
-
const baseURL = config.apiBase ?? readRequiredEnv("TINKER_BASE_URL");
|
|
204
|
-
|
|
205
|
-
return new OpenAIChatModelClient({
|
|
206
|
-
apiKey,
|
|
207
|
-
baseURL,
|
|
208
|
-
includeReasoningContent: config.includeReasoningContent,
|
|
209
|
-
model: config.modelName,
|
|
210
|
-
stream: config.stream,
|
|
211
|
-
contextBudget: config.contextBudget,
|
|
212
|
-
inputModalities: config.inputModalities ?? Object.freeze(["text"]),
|
|
213
|
-
...(config.tokenEstimator === undefined
|
|
214
|
-
? {}
|
|
215
|
-
: { tokenEstimator: config.tokenEstimator }),
|
|
197
|
+
contextBudget: deriveModelContextBudget(contextProfile),
|
|
198
|
+
inputModalities: Object.freeze(["text"] as const),
|
|
216
199
|
});
|
|
217
200
|
}
|
|
218
201
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
| "stream"
|
|
225
|
-
| "contextBudget"
|
|
226
|
-
| "apiKey"
|
|
227
|
-
| "apiBase"
|
|
228
|
-
> &
|
|
229
|
-
Partial<Pick<RunnerConfig, "inputModalities" | "tokenEstimator">>,
|
|
230
|
-
injected?: ModelClient,
|
|
231
|
-
): ModelClient {
|
|
232
|
-
return injected ?? createModelClientFromEnv(config);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
export function createWebFetchRefinerFromEnv(
|
|
236
|
-
config: Pick<
|
|
237
|
-
RunnerConfig,
|
|
238
|
-
| "modelName"
|
|
239
|
-
| "includeReasoningContent"
|
|
240
|
-
| "stream"
|
|
241
|
-
| "contextBudget"
|
|
242
|
-
| "apiKey"
|
|
243
|
-
| "apiBase"
|
|
244
|
-
> &
|
|
245
|
-
Partial<Pick<RunnerConfig, "inputModalities" | "tokenEstimator">>,
|
|
246
|
-
): Refiner {
|
|
247
|
-
return createModelRefiner({
|
|
248
|
-
createModelClient: () => createModelClientFromEnv(config),
|
|
249
|
-
contextBudget: config.contextBudget,
|
|
250
|
-
});
|
|
202
|
+
function runnerConfigFromTemplate(
|
|
203
|
+
template: RunnerConfigTemplate,
|
|
204
|
+
sessionId: SessionId,
|
|
205
|
+
): RunnerConfig {
|
|
206
|
+
return Object.freeze({ ...template, sessionId });
|
|
251
207
|
}
|
|
252
208
|
|
|
253
209
|
export function eventLogPath(workspaceRoot: string, sessionId: SessionId): string {
|
|
@@ -264,63 +220,3 @@ export function observationLogPath(
|
|
|
264
220
|
export function promptHistoryPath(workspaceRoot: string): string {
|
|
265
221
|
return path.join(workspaceRoot, ".tinker", "prompt-history.jsonl");
|
|
266
222
|
}
|
|
267
|
-
|
|
268
|
-
function readRequiredEnv(name: string): string {
|
|
269
|
-
const value = process.env[name];
|
|
270
|
-
if (value === undefined || value.trim() === "") {
|
|
271
|
-
throw new Error(`${name} is required. Put it in .env or the process environment.`);
|
|
272
|
-
}
|
|
273
|
-
return value.trim();
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function parsePositiveInteger(
|
|
277
|
-
value: string | undefined,
|
|
278
|
-
fallback: number,
|
|
279
|
-
name: string,
|
|
280
|
-
): number {
|
|
281
|
-
if (value === undefined) {
|
|
282
|
-
return fallback;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const parsed = Number(value);
|
|
286
|
-
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
287
|
-
throw new Error(`${name} must be a positive integer; received ${value}`);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return parsed;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function parseBoolean(
|
|
294
|
-
value: string | undefined,
|
|
295
|
-
fallback: boolean,
|
|
296
|
-
name: string,
|
|
297
|
-
): boolean {
|
|
298
|
-
if (value === undefined || value.trim() === "") {
|
|
299
|
-
return fallback;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const normalized = value.trim().toLowerCase();
|
|
303
|
-
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
304
|
-
return true;
|
|
305
|
-
}
|
|
306
|
-
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
throw new Error(
|
|
311
|
-
`${name} must be one of true/false, 1/0, yes/no, or on/off; received ${value}`,
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function validateWebFetchRefinerModel(mainModelName: string): void {
|
|
316
|
-
const refinerModel = process.env.TINKER_WEBFETCH_REFINE_MODEL;
|
|
317
|
-
if (
|
|
318
|
-
refinerModel !== undefined &&
|
|
319
|
-
refinerModel.trim() !== "" &&
|
|
320
|
-
refinerModel !== mainModelName
|
|
321
|
-
) {
|
|
322
|
-
throw new Error(
|
|
323
|
-
`TINKER_WEBFETCH_REFINE_MODEL must match TINKER_MODEL in F2; received ${JSON.stringify(refinerModel)} for main model ${JSON.stringify(mainModelName)}.`,
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
}
|
package/src/cli/index.ts
CHANGED
|
@@ -1,29 +1,41 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import { runOneShot } from "./run-runner";
|
|
3
|
-
import { runTui } from "./tui-runner";
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
type MainModule = typeof import("./main");
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
export async function runExecutable(
|
|
6
|
+
input: {
|
|
7
|
+
readonly args: readonly string[];
|
|
8
|
+
readonly stdin: AsyncIterable<unknown>;
|
|
9
|
+
readonly stdout: NodeJS.WriteStream;
|
|
10
|
+
readonly stderr: NodeJS.WriteStream;
|
|
11
|
+
readonly cwd: string;
|
|
12
|
+
readonly env: NodeJS.ProcessEnv;
|
|
13
|
+
} = {
|
|
14
|
+
args: process.argv.slice(2),
|
|
15
|
+
stdin: process.stdin,
|
|
16
|
+
stdout: process.stdout,
|
|
17
|
+
stderr: process.stderr,
|
|
18
|
+
cwd: process.cwd(),
|
|
19
|
+
env: process.env,
|
|
20
|
+
},
|
|
21
|
+
loadMain: () => Promise<MainModule> = () => import("./main"),
|
|
22
|
+
): Promise<number> {
|
|
23
|
+
let mainModule: MainModule;
|
|
24
|
+
try {
|
|
25
|
+
mainModule = await loadMain();
|
|
26
|
+
} catch {
|
|
27
|
+
input.stderr.write("Tinker failed to start. Reinstall tinker-agent.\n");
|
|
28
|
+
return 1;
|
|
13
29
|
}
|
|
14
30
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const profileName = args[0];
|
|
21
|
-
if (profileName === undefined) {
|
|
22
|
-
process.stderr.write("Usage: tinker --profile <profile-name>\n");
|
|
23
|
-
process.exit(2);
|
|
31
|
+
try {
|
|
32
|
+
return await mainModule.main(input);
|
|
33
|
+
} catch {
|
|
34
|
+
input.stderr.write("Tinker failed unexpectedly.\n");
|
|
35
|
+
return 1;
|
|
24
36
|
}
|
|
25
|
-
await runTui({ profileName });
|
|
26
|
-
process.exit(0);
|
|
27
37
|
}
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
if (import.meta.main) {
|
|
40
|
+
process.exitCode = await runExecutable();
|
|
41
|
+
}
|