@giovannijecha/jecode 0.8.5 → 0.8.7
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 +36 -9
- package/assets/tokenizers/LICENSE +21 -0
- package/assets/tokenizers/o200k-base.tiktoken.gz +0 -0
- package/dist/cli-info.js +8 -14
- package/dist/commands.js +7 -3
- package/dist/config.js +47 -53
- package/dist/context/automatic.js +16 -1
- package/dist/context/budget.js +5 -2
- package/dist/context/compactor.js +91 -37
- package/dist/context/diagnostics.js +108 -0
- package/dist/context/lifetime.js +18 -0
- package/dist/context/manager.js +67 -0
- package/dist/context/manual.js +11 -10
- package/dist/context/measurement.js +75 -0
- package/dist/context/policy.js +13 -10
- package/dist/context/request-observation.js +46 -0
- package/dist/context/request-projection.js +8 -34
- package/dist/context/request.js +22 -0
- package/dist/context/tokenizer/bpe.js +68 -0
- package/dist/context/tokenizer/o200k.js +54 -0
- package/dist/context/tokenizer/vocabulary.js +34 -0
- package/dist/controller-request.js +47 -45
- package/dist/controller.js +13 -2
- package/dist/credential-commands.js +3 -3
- package/dist/input-boundary.js +0 -62
- package/dist/launch.js +13 -9
- package/dist/model-command.js +7 -17
- package/dist/oauth-result-page.js +68 -0
- package/dist/openai-account-command.js +14 -12
- package/dist/openai-account.js +7 -5
- package/dist/openai-oauth-callback.js +15 -54
- package/dist/openai-oauth-tokens.js +9 -7
- package/dist/openai-oauth.js +8 -6
- package/dist/permission-command.js +9 -24
- package/dist/permissions.js +10 -8
- package/dist/provider-commands.js +1 -33
- package/dist/provider-errors.js +1 -10
- package/dist/provider-label.js +3 -10
- package/dist/providers/anthropic-wire.js +1 -1
- package/dist/providers/anthropic.js +3 -2
- package/dist/providers/index.js +1 -5
- package/dist/providers/input-measurement.js +85 -0
- package/dist/providers/ollama-context.js +42 -0
- package/dist/providers/ollama-endpoint.js +7 -34
- package/dist/providers/ollama-wire.js +1 -1
- package/dist/providers/ollama.js +15 -147
- package/dist/providers/openai-codex.js +7 -4
- package/dist/providers/openai-stream.js +20 -8
- package/dist/providers/openai-summary.js +37 -0
- package/dist/providers/openai-wire.js +1 -1
- package/dist/providers/openai.js +3 -1
- package/dist/sessions/bucket.js +55 -0
- package/dist/sessions/catalog-io.js +162 -0
- package/dist/sessions/catalog.js +3 -1
- package/dist/sessions/codec-messages.js +122 -0
- package/dist/sessions/codec-transcript.js +93 -0
- package/dist/sessions/codec-values.js +52 -0
- package/dist/sessions/codec.js +4 -257
- package/dist/sessions/files.js +158 -0
- package/dist/sessions/load.js +90 -0
- package/dist/sessions/snapshot.js +33 -0
- package/dist/sessions/store.js +42 -461
- package/dist/settings.js +17 -16
- package/dist/start.js +36 -53
- package/dist/timeline.js +2 -0
- package/dist/tools/file-read.js +192 -0
- package/dist/tools/file-summary.js +9 -0
- package/dist/tools/{fs.js → file-write.js} +5 -193
- package/dist/tools/glob.js +107 -0
- package/dist/tools/index.js +2 -1
- package/dist/tools/search.js +1 -105
- package/dist/tui/activity.js +1 -1
- package/dist/tui/app-input.js +34 -17
- package/dist/tui/app-workflows.js +13 -361
- package/dist/tui/app.js +55 -28
- package/dist/tui/approve.js +3 -2
- package/dist/tui/blocks.js +1 -2
- package/dist/tui/command-workflow.js +112 -0
- package/dist/tui/components/command-menu.js +7 -10
- package/dist/tui/components/menu.js +82 -43
- package/dist/tui/components/messages.js +16 -9
- package/dist/tui/components/status.js +1 -1
- package/dist/tui/components/tool-evidence.js +107 -0
- package/dist/tui/components/tool-motion.js +32 -0
- package/dist/tui/components/tool.js +48 -202
- package/dist/tui/frame.js +8 -3
- package/dist/tui/help.js +3 -2
- package/dist/tui/picker-layout.js +44 -0
- package/dist/tui/picker.js +7 -71
- package/dist/tui/screen.js +7 -0
- package/dist/tui/session-view.js +0 -1
- package/dist/tui/tool-details.js +135 -0
- package/dist/tui/transcript-grammar.js +1 -1
- package/dist/tui/transcript-view.js +30 -112
- package/dist/tui/turn-workflow.js +235 -0
- package/dist/tui/turn.js +7 -140
- package/dist/tui/view.js +4 -4
- package/dist/tui/workflow-types.js +2 -0
- package/dist/ui/render.js +0 -4
- package/package.json +16 -12
- package/dist/batch-view.js +0 -17
- package/dist/batch.js +0 -267
- package/dist/ollama-settings-command.js +0 -74
- package/dist/tui/motion.js +0 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@giovannijecha/jecode",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
4
|
"description": "An owned coding agent with zero external runtime dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
"dist/",
|
|
28
28
|
"assets/jeco-256.png",
|
|
29
29
|
"assets/wordmark-steel.svg",
|
|
30
|
+
"assets/tokenizers/o200k-base.tiktoken.gz",
|
|
31
|
+
"assets/tokenizers/LICENSE",
|
|
30
32
|
"LICENSE",
|
|
31
33
|
"README.md"
|
|
32
34
|
],
|
|
@@ -41,22 +43,24 @@
|
|
|
41
43
|
"registry": "https://registry.npmjs.org/"
|
|
42
44
|
},
|
|
43
45
|
"scripts": {
|
|
44
|
-
"build:release": "node
|
|
46
|
+
"build:release": "node scripts/build-release.ts",
|
|
45
47
|
"pack:release": "npm run build:release && npm pack --ignore-scripts",
|
|
46
48
|
"start": "node src/main.ts",
|
|
47
|
-
"tui:lab": "node dev/tui
|
|
48
|
-
"
|
|
49
|
-
"bench:
|
|
50
|
-
"bench:
|
|
51
|
-
"bench:
|
|
52
|
-
"bench:
|
|
49
|
+
"tui:lab": "node dev/tui/main.ts",
|
|
50
|
+
"web:preview": "node dev/web/main.ts",
|
|
51
|
+
"bench:context": "node dev/benchmarks/context.ts",
|
|
52
|
+
"bench:redaction": "node dev/benchmarks/redaction.ts",
|
|
53
|
+
"bench:search": "node dev/benchmarks/search.ts",
|
|
54
|
+
"bench:session": "node dev/benchmarks/session.ts",
|
|
55
|
+
"bench:transcript": "node dev/benchmarks/transcript.ts",
|
|
56
|
+
"bench:tui": "node --expose-gc dev/benchmarks/tui.ts",
|
|
53
57
|
"typecheck": "tsc --noEmit",
|
|
54
58
|
"test": "npm run build:release && node --test",
|
|
55
59
|
"coverage": "npm run build:release && node --test --experimental-test-coverage --test-coverage-include=\"src/**/*.ts\" --test-coverage-lines=80 --test-coverage-branches=75 --test-coverage-functions=75",
|
|
56
|
-
"check:source-tree": "node
|
|
57
|
-
"check:release-tag": "node
|
|
58
|
-
"check:package": "npm run build:release -- --quiet && node
|
|
59
|
-
"check:install": "npm run build:release -- --quiet && node
|
|
60
|
+
"check:source-tree": "node scripts/check-source-tree.ts",
|
|
61
|
+
"check:release-tag": "node scripts/check-release-tag.ts",
|
|
62
|
+
"check:package": "npm run build:release -- --quiet && node scripts/check-package.ts",
|
|
63
|
+
"check:install": "npm run build:release -- --quiet && node scripts/check-installed-cli.ts",
|
|
60
64
|
"check": "npm run check:source-tree && npm run typecheck && npm run coverage && npm run check:package && npm run check:install"
|
|
61
65
|
},
|
|
62
66
|
"devDependencies": {
|
package/dist/batch-view.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// Project semantic blocks into a compact transcript for pipes.
|
|
2
|
-
import { render } from "./tui/blocks.js";
|
|
3
|
-
export function renderBatch(block, width, pal) {
|
|
4
|
-
const rows = [];
|
|
5
|
-
let lastWasBlank = false;
|
|
6
|
-
for (const rendered of render(block, width, pal)) {
|
|
7
|
-
const line = rendered.trimEnd();
|
|
8
|
-
const blank = line === "";
|
|
9
|
-
if (blank && lastWasBlank)
|
|
10
|
-
continue;
|
|
11
|
-
rows.push(line);
|
|
12
|
-
lastWasBlank = blank;
|
|
13
|
-
}
|
|
14
|
-
while (rows.at(-1) === "")
|
|
15
|
-
rows.pop();
|
|
16
|
-
return rows;
|
|
17
|
-
}
|
package/dist/batch.js
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
// The non-interactive path: stdin is a pipe, so there is no screen to own.
|
|
2
|
-
//
|
|
3
|
-
// It exists so the agent can be scripted and tested. It shares the controller,
|
|
4
|
-
// the tools and the block renderer with the TUI — only the surface differs.
|
|
5
|
-
import { stdin, stdout } from "node:process";
|
|
6
|
-
import { runTurn } from "./controller.js";
|
|
7
|
-
import { resolveContextPolicy } from "./context/capacity.js";
|
|
8
|
-
import { automaticCompactionGate, automaticCompactionKey, } from "./context/automatic.js";
|
|
9
|
-
import { compactContext } from "./context/compactor.js";
|
|
10
|
-
import { compactSession } from "./context/manual.js";
|
|
11
|
-
import { isContextOverflow } from "./context/policy.js";
|
|
12
|
-
import { handleCommand } from "./commands.js";
|
|
13
|
-
import { renderBatch } from "./batch-view.js";
|
|
14
|
-
import { columns } from "./ui/render.js";
|
|
15
|
-
import { terminalText } from "./ui/terminal-text.js";
|
|
16
|
-
import { recordAuxiliaryUsage, recordRequestInput, recordUsage } from "./usage.js";
|
|
17
|
-
import { assertPromptLength, boundedInputLines } from "./input-boundary.js";
|
|
18
|
-
import { providerFailure } from "./provider-errors.js";
|
|
19
|
-
import { toolSpecs } from "./tools/index.js";
|
|
20
|
-
import { requestIdentityForSession } from "./request-identity.js";
|
|
21
|
-
export async function runBatch(session, environment = {}) {
|
|
22
|
-
const write = environment.write ?? ((text) => stdout.write(text));
|
|
23
|
-
const width = environment.width ?? columns();
|
|
24
|
-
const signal = environment.signal;
|
|
25
|
-
const source = environment.lines ?? boundedInputLines(stdin);
|
|
26
|
-
const lines = abortableLines(source, signal);
|
|
27
|
-
const automaticCompaction = automaticCompactionGate();
|
|
28
|
-
const emit = (block) => {
|
|
29
|
-
for (const line of renderBatch(block, width, session.palette))
|
|
30
|
-
write(`${line}\n`);
|
|
31
|
-
};
|
|
32
|
-
try {
|
|
33
|
-
throwIfAborted(signal);
|
|
34
|
-
for await (const raw of lines) {
|
|
35
|
-
throwIfAborted(signal);
|
|
36
|
-
assertPromptLength(raw.length);
|
|
37
|
-
const line = raw.trim();
|
|
38
|
-
if (line === "")
|
|
39
|
-
continue;
|
|
40
|
-
if (line.startsWith("/")) {
|
|
41
|
-
if ((await handleCommand(line, session, {
|
|
42
|
-
emit,
|
|
43
|
-
signal,
|
|
44
|
-
reset: () => {
|
|
45
|
-
automaticCompaction.reset();
|
|
46
|
-
},
|
|
47
|
-
compact: async () => {
|
|
48
|
-
const result = await compactSession(session, { signal });
|
|
49
|
-
if (result === "compacted")
|
|
50
|
-
automaticCompaction.reset();
|
|
51
|
-
return result;
|
|
52
|
-
},
|
|
53
|
-
})) === "exit")
|
|
54
|
-
break;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
write(`> ${terminalText(line)}\n`);
|
|
58
|
-
const parentId = session.conversation.activeNodeId;
|
|
59
|
-
const createdAt = new Date().toISOString();
|
|
60
|
-
const history = session.conversation.history;
|
|
61
|
-
const modelHistory = session.conversation.contextHistory;
|
|
62
|
-
const before = history.length;
|
|
63
|
-
const prospectiveNodeId = session.conversation.nodes.length + 1;
|
|
64
|
-
let nodeId;
|
|
65
|
-
let context;
|
|
66
|
-
const policy = () => {
|
|
67
|
-
return resolveContextPolicy({
|
|
68
|
-
provider: session.provider,
|
|
69
|
-
model: session.model,
|
|
70
|
-
compactionPercent: session.config.compactionPercent,
|
|
71
|
-
});
|
|
72
|
-
};
|
|
73
|
-
const user = { role: "user", content: [{ kind: "text", text: line }] };
|
|
74
|
-
history.push(user);
|
|
75
|
-
modelHistory.push(structuredClone(user));
|
|
76
|
-
const turn = events(emit, session);
|
|
77
|
-
const specs = toolSpecs(session.tools);
|
|
78
|
-
const commit = (checkpoint, settlement) => {
|
|
79
|
-
session.conversation = session.conversation.commit({
|
|
80
|
-
...(nodeId === undefined ? {} : { nodeId }),
|
|
81
|
-
parentId,
|
|
82
|
-
createdAt,
|
|
83
|
-
identity: {
|
|
84
|
-
providerId: session.provider.id,
|
|
85
|
-
model: session.model,
|
|
86
|
-
effort: session.config.effort,
|
|
87
|
-
},
|
|
88
|
-
messages: checkpoint.slice(before),
|
|
89
|
-
blocks: [],
|
|
90
|
-
...(context === undefined ? {} : { context }),
|
|
91
|
-
}, settlement);
|
|
92
|
-
nodeId = session.conversation.activeNodeId;
|
|
93
|
-
};
|
|
94
|
-
const compact = async (checkpoint, projected, request) => {
|
|
95
|
-
if (request.reason === "overflow" &&
|
|
96
|
-
(request.error === undefined || !isContextOverflow(request.error))) {
|
|
97
|
-
return undefined;
|
|
98
|
-
}
|
|
99
|
-
const force = request.reason === "overflow" || request.projectionSaturated;
|
|
100
|
-
const key = automaticCompactionKey(session.provider.id, session.model, nodeId ?? prospectiveNodeId, checkpoint.length);
|
|
101
|
-
const attempt = { key, reason: request.reason };
|
|
102
|
-
if (!automaticCompaction.allows(attempt))
|
|
103
|
-
return undefined;
|
|
104
|
-
let attempted = false;
|
|
105
|
-
const result = await compactContext({
|
|
106
|
-
provider: session.provider,
|
|
107
|
-
model: session.model,
|
|
108
|
-
effort: session.config.effort,
|
|
109
|
-
context: projected,
|
|
110
|
-
turn: checkpoint.slice(before),
|
|
111
|
-
nodeId: nodeId ?? prospectiveNodeId,
|
|
112
|
-
coveredMessages: context?.messageCount ?? 0,
|
|
113
|
-
lastInputTokens: Math.max(session.usage.lastInputTokens, request.inputTokens),
|
|
114
|
-
estimatedInputTokens: request.inputTokens,
|
|
115
|
-
force,
|
|
116
|
-
policy: request.policy,
|
|
117
|
-
requestEnvelope: {
|
|
118
|
-
system: session.system,
|
|
119
|
-
tools: specs,
|
|
120
|
-
maxOutputTokens: session.config.maxTokens,
|
|
121
|
-
},
|
|
122
|
-
requestIdentity: requestIdentityForSession(session),
|
|
123
|
-
onBegin: () => {
|
|
124
|
-
attempted = true;
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
if (result === undefined) {
|
|
128
|
-
if (attempted && signal?.aborted !== true)
|
|
129
|
-
automaticCompaction.failed(attempt);
|
|
130
|
-
return undefined;
|
|
131
|
-
}
|
|
132
|
-
automaticCompaction.succeeded(attempt);
|
|
133
|
-
context = result.anchor;
|
|
134
|
-
if (result.usage !== undefined)
|
|
135
|
-
recordAuxiliaryUsage(session.usage, result.usage);
|
|
136
|
-
return result.messages;
|
|
137
|
-
};
|
|
138
|
-
turn.onContext = compact;
|
|
139
|
-
turn.onCheckpoint = async (checkpoint, settlement, projected) => {
|
|
140
|
-
commit(checkpoint, settlement);
|
|
141
|
-
const compacted = await compact(checkpoint, projected, {
|
|
142
|
-
reason: "budget",
|
|
143
|
-
policy: await policy(),
|
|
144
|
-
inputTokens: session.usage.lastInputTokens,
|
|
145
|
-
projectionSaturated: false,
|
|
146
|
-
});
|
|
147
|
-
if (compacted !== undefined)
|
|
148
|
-
commit(checkpoint, settlement);
|
|
149
|
-
return compacted;
|
|
150
|
-
};
|
|
151
|
-
try {
|
|
152
|
-
await runTurn(history, options(session, policy), turn, signal, modelHistory);
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
throwIfAborted(signal);
|
|
156
|
-
if (!(error instanceof Error))
|
|
157
|
-
throw error;
|
|
158
|
-
const message = providerFailure(session.provider, error, true);
|
|
159
|
-
if (message === error.message)
|
|
160
|
-
throw error;
|
|
161
|
-
throw new Error(message, { cause: error });
|
|
162
|
-
}
|
|
163
|
-
turn.flush();
|
|
164
|
-
}
|
|
165
|
-
throwIfAborted(signal);
|
|
166
|
-
}
|
|
167
|
-
finally {
|
|
168
|
-
if (environment.lines === undefined)
|
|
169
|
-
stdin.pause();
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
function throwIfAborted(signal) {
|
|
173
|
-
if (signal?.aborted === true)
|
|
174
|
-
throw signal.reason;
|
|
175
|
-
}
|
|
176
|
-
async function* abortableLines(source, signal) {
|
|
177
|
-
const iterator = source[Symbol.asyncIterator]();
|
|
178
|
-
let exhausted = false;
|
|
179
|
-
try {
|
|
180
|
-
while (true) {
|
|
181
|
-
throwIfAborted(signal);
|
|
182
|
-
const next = signal === undefined
|
|
183
|
-
? await iterator.next()
|
|
184
|
-
: await new Promise((resolve, reject) => {
|
|
185
|
-
const abort = () => reject(signal.reason);
|
|
186
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
187
|
-
iterator.next().then((result) => {
|
|
188
|
-
signal.removeEventListener("abort", abort);
|
|
189
|
-
resolve(result);
|
|
190
|
-
}, (error) => {
|
|
191
|
-
signal.removeEventListener("abort", abort);
|
|
192
|
-
reject(error);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
if (next.done === true) {
|
|
196
|
-
exhausted = true;
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
yield next.value;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
finally {
|
|
203
|
-
if (!exhausted && iterator.return !== undefined) {
|
|
204
|
-
const closing = iterator.return();
|
|
205
|
-
if (signal?.aborted === true)
|
|
206
|
-
void closing.catch(() => { });
|
|
207
|
-
else
|
|
208
|
-
await closing;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
function options(session, contextPolicy) {
|
|
213
|
-
return {
|
|
214
|
-
provider: session.provider,
|
|
215
|
-
tools: session.tools,
|
|
216
|
-
model: session.model,
|
|
217
|
-
system: session.system,
|
|
218
|
-
maxTokens: session.config.maxTokens,
|
|
219
|
-
contextPolicy,
|
|
220
|
-
effort: session.config.effort,
|
|
221
|
-
requestIdentity: requestIdentityForSession(session),
|
|
222
|
-
maxModelRequests: session.config.maxModelRequests,
|
|
223
|
-
toolContext: { root: session.config.root },
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
// Prose is buffered rather than streamed: without a screen to repaint there is
|
|
227
|
-
// nothing to gain from partial lines, and plenty to lose in readability.
|
|
228
|
-
function events(emit, session) {
|
|
229
|
-
let answer = "";
|
|
230
|
-
const flush = () => {
|
|
231
|
-
if (answer === "")
|
|
232
|
-
return;
|
|
233
|
-
emit({ kind: "answer", text: answer });
|
|
234
|
-
answer = "";
|
|
235
|
-
};
|
|
236
|
-
return {
|
|
237
|
-
flush,
|
|
238
|
-
onStream(event) {
|
|
239
|
-
if (event.kind === "text")
|
|
240
|
-
answer += event.text;
|
|
241
|
-
},
|
|
242
|
-
onToolCall() {
|
|
243
|
-
flush();
|
|
244
|
-
},
|
|
245
|
-
onToolResult(call, result, summary) {
|
|
246
|
-
emit({
|
|
247
|
-
kind: "tool",
|
|
248
|
-
name: call.name,
|
|
249
|
-
target: "",
|
|
250
|
-
right: summary ?? "",
|
|
251
|
-
tone: result.isError ? "fail" : "ok",
|
|
252
|
-
});
|
|
253
|
-
},
|
|
254
|
-
approve() {
|
|
255
|
-
flush();
|
|
256
|
-
// Nobody is watching a pipe. Approval has to be granted up front with
|
|
257
|
-
// --auto-approve, never inferred from silence.
|
|
258
|
-
return Promise.resolve(session.config.autoApprove);
|
|
259
|
-
},
|
|
260
|
-
onUsage(usage) {
|
|
261
|
-
recordUsage(session.usage, usage);
|
|
262
|
-
},
|
|
263
|
-
onRequestInput(inputTokens) {
|
|
264
|
-
recordRequestInput(session.usage, inputTokens);
|
|
265
|
-
},
|
|
266
|
-
};
|
|
267
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
// Ollama connection choices inside the persistent settings hub.
|
|
2
|
-
import { askForKey } from "./credential-commands.js";
|
|
3
|
-
import { keyFor } from "./credentials.js";
|
|
4
|
-
import { of } from "./tui/editor.js";
|
|
5
|
-
import { heading } from "./tui/picker.js";
|
|
6
|
-
import { OLLAMA_CLOUD_HOST, OLLAMA_LOCAL_HOST, parseOllamaEndpoint, } from "./providers/ollama-endpoint.js";
|
|
7
|
-
import { configureOllama, ollamaConnection } from "./providers/ollama.js";
|
|
8
|
-
const KEY = "OLLAMA_API_KEY";
|
|
9
|
-
export function ollamaConnectionHint() {
|
|
10
|
-
const connection = ollamaConnection();
|
|
11
|
-
const origin = new URL(connection.baseUrl).host;
|
|
12
|
-
const automatic = connection.inferred ? " · automatic" : "";
|
|
13
|
-
if (connection.kind === "local")
|
|
14
|
-
return `local · this computer${automatic}`;
|
|
15
|
-
return `${connection.kind} · ${origin}${automatic}`;
|
|
16
|
-
}
|
|
17
|
-
export async function ollamaConnectionSetting(session, host, persist) {
|
|
18
|
-
if (host.choose === undefined)
|
|
19
|
-
return;
|
|
20
|
-
const current = ollamaConnection();
|
|
21
|
-
const index = await host.choose({
|
|
22
|
-
title: heading("Ollama connection", "where Ollama requests run", session.palette),
|
|
23
|
-
options: [
|
|
24
|
-
{ label: "cloud", hint: "ollama.com · API key" },
|
|
25
|
-
{ label: "local", hint: "this computer · no API key" },
|
|
26
|
-
{ label: "custom", hint: "HTTPS or loopback endpoint" },
|
|
27
|
-
],
|
|
28
|
-
index: current.kind === "cloud" ? 0 : current.kind === "local" ? 1 : 2,
|
|
29
|
-
});
|
|
30
|
-
if (index === undefined)
|
|
31
|
-
return;
|
|
32
|
-
let nextHost;
|
|
33
|
-
if (index === 0)
|
|
34
|
-
nextHost = OLLAMA_CLOUD_HOST;
|
|
35
|
-
else if (index === 1)
|
|
36
|
-
nextHost = OLLAMA_LOCAL_HOST;
|
|
37
|
-
else {
|
|
38
|
-
const custom = await customEndpoint(session, host, current.kind === "custom" ? current.baseUrl : "https://");
|
|
39
|
-
if (custom === undefined)
|
|
40
|
-
return;
|
|
41
|
-
nextHost = custom;
|
|
42
|
-
}
|
|
43
|
-
const endpoint = parseOllamaEndpoint(nextHost);
|
|
44
|
-
if (!endpoint.loopback && keyFor(KEY) === undefined) {
|
|
45
|
-
const accepted = await askForKey(KEY, host, session.palette);
|
|
46
|
-
if (!accepted)
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (!(await persist({ ollamaHost: endpoint.baseUrl })))
|
|
50
|
-
return;
|
|
51
|
-
configureOllama(endpoint.baseUrl);
|
|
52
|
-
session.config.ollamaHost = endpoint.baseUrl;
|
|
53
|
-
}
|
|
54
|
-
async function customEndpoint(session, host, initial) {
|
|
55
|
-
if (host.type === undefined)
|
|
56
|
-
return undefined;
|
|
57
|
-
const field = {
|
|
58
|
-
title: heading("Ollama endpoint", "HTTPS or exact loopback URL", session.palette),
|
|
59
|
-
right: "enter save · esc back",
|
|
60
|
-
editor: of(initial),
|
|
61
|
-
secret: false,
|
|
62
|
-
note: "Remote endpoints must use HTTPS. API keys are stored separately.",
|
|
63
|
-
};
|
|
64
|
-
const value = await host.type(field);
|
|
65
|
-
if (value === undefined)
|
|
66
|
-
return undefined;
|
|
67
|
-
try {
|
|
68
|
-
return parseOllamaEndpoint(value).baseUrl;
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
host.emit({ kind: "notice", text: error.message, tone: "error" });
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
}
|
package/dist/tui/motion.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// Small deterministic motion primitives for terminal cells.
|
|
2
|
-
export const TOOL_BIRTH_MS = 300;
|
|
3
|
-
export const TOOL_SETTLE_MS = 700;
|
|
4
|
-
export const TOOL_ROW_ARRIVAL_MS = 420;
|
|
5
|
-
export const TOOL_LEADER_MAX_MS = 1_500;
|
|
6
|
-
export function interval(now, start, duration) {
|
|
7
|
-
if (start === undefined)
|
|
8
|
-
return 1;
|
|
9
|
-
if (duration <= 0)
|
|
10
|
-
return 1;
|
|
11
|
-
return Math.max(0, Math.min(1, (now - start) / duration));
|
|
12
|
-
}
|
|
13
|
-
export function easeOut(value) {
|
|
14
|
-
const rest = 1 - Math.max(0, Math.min(1, value));
|
|
15
|
-
return 1 - rest * rest * rest;
|
|
16
|
-
}
|
|
17
|
-
export function easeInOut(value) {
|
|
18
|
-
const at = Math.max(0, Math.min(1, value));
|
|
19
|
-
return at < 0.5 ? 4 * at * at * at : 1 - Math.pow(-2 * at + 2, 3) / 2;
|
|
20
|
-
}
|
|
21
|
-
export function mix(from, to, amount) {
|
|
22
|
-
const at = Math.max(0, Math.min(1, amount));
|
|
23
|
-
return [
|
|
24
|
-
Math.round(from[0] + (to[0] - from[0]) * at),
|
|
25
|
-
Math.round(from[1] + (to[1] - from[1]) * at),
|
|
26
|
-
Math.round(from[2] + (to[2] - from[2]) * at),
|
|
27
|
-
];
|
|
28
|
-
}
|
|
29
|
-
/** A restrained pulse that never reaches either endpoint. */
|
|
30
|
-
export function breathe(now, period = 1_400) {
|
|
31
|
-
return 0.2 + ((Math.sin((now / period) * Math.PI * 2) + 1) / 2) * 0.6;
|
|
32
|
-
}
|