@freesyntax/notch-cli 0.5.20 → 0.5.22
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/dist/{apply-patch-D5PDUXUC.js → apply-patch-U6K67CMT.js} +1 -0
- package/dist/auth-UAMMP5IJ.js +29 -0
- package/dist/chunk-4HPRBCSY.js +167 -0
- package/dist/chunk-6NKRMZTX.js +198 -0
- package/dist/{chunk-YBYF7L4A.js → chunk-EPSOOCNB.js} +1832 -1331
- package/dist/chunk-FZVPGJJW.js +511 -0
- package/dist/chunk-GFVLHUSS.js +155 -0
- package/dist/chunk-J66N6AFH.js +137 -0
- package/dist/chunk-JXQ4HZ47.js +544 -0
- package/dist/chunk-KCAR5DOB.js +52 -0
- package/dist/chunk-KFQGP6VL.js +33 -0
- package/dist/chunk-O6AKZ4OH.js +0 -0
- package/dist/{chunk-6M6CXXWR.js → chunk-PKZKVOAN.js} +209 -1
- package/dist/{chunk-FIFC4V2R.js → chunk-PPEBWOMJ.js} +91 -7
- package/dist/compression-YJLWEHCC.js +33 -0
- package/dist/config-set-3IWEVZQ4.js +110 -0
- package/dist/{edit-JEFEK43H.js → edit-6QYAXVNU.js} +1 -0
- package/dist/{git-5T5TSQTX.js → git-DNQ5EELH.js} +1 -0
- package/dist/{github-DWRGWX6U.js → github-34T4QQIH.js} +1 -0
- package/dist/{glob-BI3P4C7Q.js → glob-XT43LEJ4.js} +1 -0
- package/dist/{grep-VZ3I5GNW.js → grep-T2CXYNRI.js} +1 -0
- package/dist/index.js +2606 -960
- package/dist/{lsp-UPY6I3L7.js → lsp-JXQVU7NP.js} +1 -0
- package/dist/model-download-3NDKS3VM.js +176 -0
- package/dist/{notebook-FXJBTSPA.js → notebook-MFODW345.js} +1 -0
- package/dist/ollama-bench-5V5CCOCQ.js +194 -0
- package/dist/ollama-launch-P5KBK7AJ.js +22 -0
- package/dist/ollama-usage-3PROM2WC.js +70 -0
- package/dist/{plugins-OG2P75K5.js → plugins-PNGRZLFW.js} +1 -0
- package/dist/{read-OVJG2XKW.js → read-B64XE7N3.js} +1 -0
- package/dist/{server-W7FRCVRZ.js → server-IGOZHW52.js} +17 -15
- package/dist/session-index-7FWEVP6E.js +22 -0
- package/dist/{shell-4X545EVN.js → shell-BOZTHQUT.js} +1 -0
- package/dist/{task-OS3E5F3X.js → task-67G4KLYC.js} +1 -0
- package/dist/{tools-Q7CDHB4K.js → tools-XWKCW4RN.js} +4 -1
- package/dist/{web-fetch-KNIV3Z3W.js → web-fetch-OTNDICGJ.js} +1 -0
- package/dist/{write-NNHLOTYK.js → write-ZOSB7I4J.js} +1 -0
- package/package.json +2 -1
- package/dist/auth-JQX6MHJG.js +0 -16
- package/dist/compression-UTB2Y4BB.js +0 -16
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/providers/ollama-credentials.ts
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
function notchConfigDir() {
|
|
5
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
6
|
+
if (xdg) return path.join(xdg, "notch");
|
|
7
|
+
const appdata = process.env.APPDATA;
|
|
8
|
+
if (appdata && process.platform === "win32") return path.join(appdata, "notch");
|
|
9
|
+
const home = process.env.HOME ?? process.env.USERPROFILE;
|
|
10
|
+
if (!home) {
|
|
11
|
+
throw new Error("Cannot determine home directory for credential storage.");
|
|
12
|
+
}
|
|
13
|
+
return path.join(home, ".config", "notch");
|
|
14
|
+
}
|
|
15
|
+
function ollamaCredsPath() {
|
|
16
|
+
return path.join(notchConfigDir(), "ollama.json");
|
|
17
|
+
}
|
|
18
|
+
async function readOllamaCreds() {
|
|
19
|
+
try {
|
|
20
|
+
const raw = await fs.readFile(ollamaCredsPath(), "utf-8");
|
|
21
|
+
const parsed = JSON.parse(raw);
|
|
22
|
+
if (typeof parsed.apiKey !== "string" || typeof parsed.endpoint !== "string") return null;
|
|
23
|
+
return {
|
|
24
|
+
apiKey: parsed.apiKey,
|
|
25
|
+
endpoint: parsed.endpoint,
|
|
26
|
+
createdAt: typeof parsed.createdAt === "number" ? parsed.createdAt : Date.now()
|
|
27
|
+
};
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function writeOllamaCreds(creds) {
|
|
33
|
+
const dir = notchConfigDir();
|
|
34
|
+
await fs.mkdir(dir, { recursive: true, mode: 448 });
|
|
35
|
+
const file = ollamaCredsPath();
|
|
36
|
+
await fs.writeFile(file, JSON.stringify(creds, null, 2) + "\n", { mode: 384 });
|
|
37
|
+
}
|
|
38
|
+
async function clearOllamaCreds() {
|
|
39
|
+
try {
|
|
40
|
+
await fs.unlink(ollamaCredsPath());
|
|
41
|
+
} catch (err) {
|
|
42
|
+
const code = err.code;
|
|
43
|
+
if (code !== "ENOENT") throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
ollamaCredsPath,
|
|
49
|
+
readOllamaCreds,
|
|
50
|
+
writeOllamaCreds,
|
|
51
|
+
clearOllamaCreds
|
|
52
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
|
+
}) : x)(function(x) {
|
|
8
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
|
+
});
|
|
11
|
+
var __esm = (fn, res) => function __init() {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
};
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
__require,
|
|
30
|
+
__esm,
|
|
31
|
+
__export,
|
|
32
|
+
__toCommonJS
|
|
33
|
+
};
|
|
File without changes
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/agent/compression.ts
|
|
2
2
|
import { generateText } from "ai";
|
|
3
|
+
import { createHash } from "crypto";
|
|
3
4
|
function estimateTokens(messages) {
|
|
4
5
|
let chars = 0;
|
|
5
6
|
for (const msg of messages) {
|
|
@@ -15,6 +16,112 @@ function estimateTokens(messages) {
|
|
|
15
16
|
}
|
|
16
17
|
return Math.ceil(chars / 4);
|
|
17
18
|
}
|
|
19
|
+
var InMemoryArtifactStore = class {
|
|
20
|
+
map = /* @__PURE__ */ new Map();
|
|
21
|
+
put(key, value) {
|
|
22
|
+
this.map.set(key, value);
|
|
23
|
+
}
|
|
24
|
+
get(key) {
|
|
25
|
+
return this.map.get(key);
|
|
26
|
+
}
|
|
27
|
+
has(key) {
|
|
28
|
+
return this.map.has(key);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var globalArtifactStore = new InMemoryArtifactStore();
|
|
32
|
+
function setArtifactStore(store) {
|
|
33
|
+
const prev = globalArtifactStore;
|
|
34
|
+
globalArtifactStore = store;
|
|
35
|
+
return () => {
|
|
36
|
+
globalArtifactStore = prev;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function getArtifactStore() {
|
|
40
|
+
return globalArtifactStore;
|
|
41
|
+
}
|
|
42
|
+
var ARTIFACT_URI_PREFIX = "notch-artifact://";
|
|
43
|
+
function artifactKey(content) {
|
|
44
|
+
return createHash("sha1").update(content).digest("hex").slice(0, 16);
|
|
45
|
+
}
|
|
46
|
+
function makeArtifactUri(key) {
|
|
47
|
+
return `${ARTIFACT_URI_PREFIX}${key}`;
|
|
48
|
+
}
|
|
49
|
+
function parseArtifactUri(text) {
|
|
50
|
+
const match = text.match(/notch-artifact:\/\/([a-f0-9]{8,64})/);
|
|
51
|
+
return match ? match[1] : null;
|
|
52
|
+
}
|
|
53
|
+
var BUDGET_TOOL_RESULT_MAX = 3e3;
|
|
54
|
+
var BUDGET_PREVIEW_CHARS = 400;
|
|
55
|
+
function budgetReduce(messages, opts = {}) {
|
|
56
|
+
const maxChars = opts.maxToolResultChars ?? BUDGET_TOOL_RESULT_MAX;
|
|
57
|
+
const previewChars = opts.previewChars ?? BUDGET_PREVIEW_CHARS;
|
|
58
|
+
const keepLastN = opts.keepLastN ?? 4;
|
|
59
|
+
const store = opts.store ?? globalArtifactStore;
|
|
60
|
+
const toolIdx = [];
|
|
61
|
+
for (let i = 0; i < messages.length; i++) {
|
|
62
|
+
if (messages[i].role === "tool") toolIdx.push(i);
|
|
63
|
+
}
|
|
64
|
+
const eligibleUpTo = toolIdx.length - keepLastN;
|
|
65
|
+
if (eligibleUpTo <= 0) return { messages, stashed: 0, savedChars: 0 };
|
|
66
|
+
const eligible = new Set(toolIdx.slice(0, eligibleUpTo));
|
|
67
|
+
let stashed = 0;
|
|
68
|
+
let savedChars = 0;
|
|
69
|
+
const out = messages.map((msg, idx) => {
|
|
70
|
+
if (msg.role !== "tool" || !eligible.has(idx)) return msg;
|
|
71
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
72
|
+
let touched = false;
|
|
73
|
+
const newContent = msg.content.map((block) => {
|
|
74
|
+
const b = block;
|
|
75
|
+
if (b.type !== "tool-result") return block;
|
|
76
|
+
const resultStr = typeof b.result === "string" ? b.result : JSON.stringify(b.result ?? "");
|
|
77
|
+
if (resultStr.length <= maxChars) return block;
|
|
78
|
+
const key = artifactKey(resultStr);
|
|
79
|
+
try {
|
|
80
|
+
void store.put(key, resultStr);
|
|
81
|
+
} catch {
|
|
82
|
+
return block;
|
|
83
|
+
}
|
|
84
|
+
const preview = resultStr.slice(0, previewChars);
|
|
85
|
+
const replacement = `${preview}
|
|
86
|
+
... [+${resultStr.length - previewChars} chars stashed at ${makeArtifactUri(key)}]`;
|
|
87
|
+
savedChars += resultStr.length - replacement.length;
|
|
88
|
+
stashed++;
|
|
89
|
+
touched = true;
|
|
90
|
+
return {
|
|
91
|
+
...b,
|
|
92
|
+
result: replacement
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
if (!touched) return msg;
|
|
96
|
+
return { ...msg, content: newContent };
|
|
97
|
+
});
|
|
98
|
+
return { messages: out, stashed, savedChars };
|
|
99
|
+
}
|
|
100
|
+
function snip(messages, opts = {}) {
|
|
101
|
+
const keepStart = opts.keepStart ?? 1;
|
|
102
|
+
const keepRecent = opts.keepRecent ?? 6;
|
|
103
|
+
const trigger = opts.triggerMiddleCount ?? 40;
|
|
104
|
+
const snipCount = opts.snipCount ?? 10;
|
|
105
|
+
if (messages.length < keepStart + keepRecent + trigger) {
|
|
106
|
+
return { messages, snipped: 0 };
|
|
107
|
+
}
|
|
108
|
+
const head = messages.slice(0, keepStart);
|
|
109
|
+
const tail = messages.slice(-keepRecent);
|
|
110
|
+
const middle = messages.slice(keepStart, messages.length - keepRecent);
|
|
111
|
+
let cut = Math.min(snipCount, middle.length);
|
|
112
|
+
while (cut < middle.length && middle[cut].role === "tool") cut++;
|
|
113
|
+
const dropped = middle.slice(0, cut);
|
|
114
|
+
const kept = middle.slice(cut);
|
|
115
|
+
if (dropped.length === 0) return { messages, snipped: 0 };
|
|
116
|
+
const marker = {
|
|
117
|
+
role: "user",
|
|
118
|
+
content: `[snip: ${dropped.length} older messages removed by age]`
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
messages: [...head, marker, ...kept, ...tail],
|
|
122
|
+
snipped: dropped.length
|
|
123
|
+
};
|
|
124
|
+
}
|
|
18
125
|
var TEXT_BLOCK_MAX = 8e3;
|
|
19
126
|
function microCompact(messages) {
|
|
20
127
|
const result = [];
|
|
@@ -35,6 +142,48 @@ function microCompact(messages) {
|
|
|
35
142
|
}
|
|
36
143
|
return result;
|
|
37
144
|
}
|
|
145
|
+
function contextCollapse(messages, opts = {}) {
|
|
146
|
+
const keepStart = opts.keepStart ?? 1;
|
|
147
|
+
const keepRecent = opts.keepRecent ?? 4;
|
|
148
|
+
const trigger = opts.triggerMiddleCount ?? 12;
|
|
149
|
+
const store = opts.store ?? globalArtifactStore;
|
|
150
|
+
if (messages.length < keepStart + keepRecent + trigger) {
|
|
151
|
+
return { messages, collapsed: false };
|
|
152
|
+
}
|
|
153
|
+
const head = messages.slice(0, keepStart);
|
|
154
|
+
const middle = messages.slice(keepStart, messages.length - keepRecent);
|
|
155
|
+
const tail = messages.slice(-keepRecent);
|
|
156
|
+
if (middle.length === 0) return { messages, collapsed: false };
|
|
157
|
+
let key;
|
|
158
|
+
try {
|
|
159
|
+
const serialized = JSON.stringify(middle);
|
|
160
|
+
key = artifactKey(serialized);
|
|
161
|
+
void store.put(key, serialized);
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
const summaryText = buildDeterministicSummary(middle);
|
|
165
|
+
const ref = key ? ` (originals at ${makeArtifactUri(key)})` : "";
|
|
166
|
+
const summaryMarker = {
|
|
167
|
+
role: "user",
|
|
168
|
+
content: `[Context collapsed \u2014 ${middle.length} messages summarized${ref}]
|
|
169
|
+
${summaryText}`
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
messages: [...head, summaryMarker, ...tail],
|
|
173
|
+
collapsed: true,
|
|
174
|
+
artifactKey: key
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async function restoreCollapsedSpan(artifactKeyOrUri, store = globalArtifactStore) {
|
|
178
|
+
const key = artifactKeyOrUri.startsWith(ARTIFACT_URI_PREFIX) ? artifactKeyOrUri.slice(ARTIFACT_URI_PREFIX.length) : artifactKeyOrUri;
|
|
179
|
+
const raw = await store.get(key);
|
|
180
|
+
if (!raw) return null;
|
|
181
|
+
try {
|
|
182
|
+
return JSON.parse(raw);
|
|
183
|
+
} catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
38
187
|
var RESERVE_BUFFER_TOKENS = 13e3;
|
|
39
188
|
var MAX_SUMMARY_TOKENS = 2e4;
|
|
40
189
|
var MAX_COMPRESSION_FAILURES = 3;
|
|
@@ -180,10 +329,19 @@ ${firstContent}`
|
|
|
180
329
|
return { messages: compressed, compressed: true };
|
|
181
330
|
}
|
|
182
331
|
async function autoCompress(messages, model, contextWindow, onCompress) {
|
|
183
|
-
let result = microCompact(messages);
|
|
184
332
|
const threshold = (contextWindow - RESERVE_BUFFER_TOKENS) * 0.75;
|
|
333
|
+
let result = budgetReduce(messages).messages;
|
|
334
|
+
result = snip(result).messages;
|
|
335
|
+
result = microCompact(result);
|
|
185
336
|
let tokens = estimateTokens(result);
|
|
186
337
|
if (tokens < threshold) return result;
|
|
338
|
+
const collapsed = contextCollapse(result);
|
|
339
|
+
if (collapsed.collapsed) {
|
|
340
|
+
onCompress?.();
|
|
341
|
+
result = collapsed.messages;
|
|
342
|
+
tokens = estimateTokens(result);
|
|
343
|
+
}
|
|
344
|
+
if (tokens < threshold) return result;
|
|
187
345
|
const auto = await autoCompactSummarize(result, { model, contextWindow });
|
|
188
346
|
if (auto.compressed) {
|
|
189
347
|
onCompress?.();
|
|
@@ -198,6 +356,48 @@ async function autoCompress(messages, model, contextWindow, onCompress) {
|
|
|
198
356
|
}
|
|
199
357
|
return result;
|
|
200
358
|
}
|
|
359
|
+
async function autoCompressWithStats(messages, model, contextWindow) {
|
|
360
|
+
const stats = {
|
|
361
|
+
budgetStashed: 0,
|
|
362
|
+
snipped: 0,
|
|
363
|
+
collapsed: false,
|
|
364
|
+
autoCompacted: false,
|
|
365
|
+
fullCompacted: false
|
|
366
|
+
};
|
|
367
|
+
const threshold = (contextWindow - RESERVE_BUFFER_TOKENS) * 0.75;
|
|
368
|
+
const br = budgetReduce(messages);
|
|
369
|
+
let result = br.messages;
|
|
370
|
+
stats.budgetStashed = br.stashed;
|
|
371
|
+
const sn = snip(result);
|
|
372
|
+
result = sn.messages;
|
|
373
|
+
stats.snipped = sn.snipped;
|
|
374
|
+
result = microCompact(result);
|
|
375
|
+
let tokens = estimateTokens(result);
|
|
376
|
+
if (tokens >= threshold) {
|
|
377
|
+
const collapsed = contextCollapse(result);
|
|
378
|
+
if (collapsed.collapsed) {
|
|
379
|
+
result = collapsed.messages;
|
|
380
|
+
stats.collapsed = true;
|
|
381
|
+
tokens = estimateTokens(result);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (tokens >= threshold) {
|
|
385
|
+
const auto = await autoCompactSummarize(result, { model, contextWindow });
|
|
386
|
+
if (auto.compressed) {
|
|
387
|
+
result = auto.messages;
|
|
388
|
+
stats.autoCompacted = true;
|
|
389
|
+
tokens = estimateTokens(result);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (tokens >= threshold) {
|
|
393
|
+
const full = await fullCompact(result, model);
|
|
394
|
+
if (full.compressed) {
|
|
395
|
+
result = full.messages;
|
|
396
|
+
stats.fullCompacted = true;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return { messages: result, stats };
|
|
400
|
+
}
|
|
201
401
|
async function compressHistory(messages, opts) {
|
|
202
402
|
const result = await autoCompactSummarize(messages, opts);
|
|
203
403
|
return result;
|
|
@@ -205,9 +405,17 @@ async function compressHistory(messages, opts) {
|
|
|
205
405
|
|
|
206
406
|
export {
|
|
207
407
|
estimateTokens,
|
|
408
|
+
setArtifactStore,
|
|
409
|
+
getArtifactStore,
|
|
410
|
+
parseArtifactUri,
|
|
411
|
+
budgetReduce,
|
|
412
|
+
snip,
|
|
208
413
|
microCompact,
|
|
414
|
+
contextCollapse,
|
|
415
|
+
restoreCollapsedSpan,
|
|
209
416
|
autoCompactSummarize,
|
|
210
417
|
fullCompact,
|
|
211
418
|
autoCompress,
|
|
419
|
+
autoCompressWithStats,
|
|
212
420
|
compressHistory
|
|
213
421
|
};
|
|
@@ -1,12 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__esm,
|
|
3
|
+
__export,
|
|
4
|
+
__require
|
|
5
|
+
} from "./chunk-KFQGP6VL.js";
|
|
6
|
+
|
|
1
7
|
// src/auth.ts
|
|
8
|
+
var auth_exports = {};
|
|
9
|
+
__export(auth_exports, {
|
|
10
|
+
clearCredentials: () => clearCredentials,
|
|
11
|
+
getByokSyncPath: () => getByokSyncPath,
|
|
12
|
+
getConfigDir: () => getConfigDir,
|
|
13
|
+
getCredentialsPath: () => getCredentialsPath,
|
|
14
|
+
invalidateSyncedKeysCache: () => invalidateSyncedKeysCache,
|
|
15
|
+
loadCredentials: () => loadCredentials,
|
|
16
|
+
loadSyncedByokKeys: () => loadSyncedByokKeys,
|
|
17
|
+
loadSyncedByokKeysSync: () => loadSyncedByokKeysSync,
|
|
18
|
+
login: () => login,
|
|
19
|
+
saveCredentials: () => saveCredentials,
|
|
20
|
+
syncByokKeys: () => syncByokKeys
|
|
21
|
+
});
|
|
2
22
|
import http from "http";
|
|
3
23
|
import { exec } from "child_process";
|
|
4
24
|
import os from "os";
|
|
5
25
|
import path from "path";
|
|
6
26
|
import fs from "fs/promises";
|
|
7
|
-
var FREESYNTAX_URL = "https://freesyntax.dev";
|
|
8
|
-
var PREFERRED_PORT = 9721;
|
|
9
|
-
var AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
10
27
|
function getConfigDir() {
|
|
11
28
|
if (process.platform === "win32") {
|
|
12
29
|
return path.join(process.env["APPDATA"] ?? os.homedir(), "notch");
|
|
@@ -45,6 +62,58 @@ async function clearCredentials() {
|
|
|
45
62
|
} catch {
|
|
46
63
|
}
|
|
47
64
|
}
|
|
65
|
+
function getByokSyncPath() {
|
|
66
|
+
return path.join(getConfigDir(), "byok-sync.json");
|
|
67
|
+
}
|
|
68
|
+
async function syncByokKeys(token, baseUrl = FREESYNTAX_URL) {
|
|
69
|
+
const url = `${baseUrl.replace(/\/$/, "")}/api/cli/keys`;
|
|
70
|
+
const res = await fetch(url, {
|
|
71
|
+
method: "GET",
|
|
72
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
73
|
+
});
|
|
74
|
+
if (!res.ok) {
|
|
75
|
+
const body = await res.text().catch(() => "");
|
|
76
|
+
throw new Error(`Key sync failed (${res.status}): ${body.slice(0, 200) || res.statusText}`);
|
|
77
|
+
}
|
|
78
|
+
const data = await res.json();
|
|
79
|
+
const dir = getConfigDir();
|
|
80
|
+
await fs.mkdir(dir, { recursive: true });
|
|
81
|
+
await fs.writeFile(
|
|
82
|
+
getByokSyncPath(),
|
|
83
|
+
JSON.stringify({ ...data, syncedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
84
|
+
{ encoding: "utf-8", mode: 384 }
|
|
85
|
+
);
|
|
86
|
+
return data;
|
|
87
|
+
}
|
|
88
|
+
async function loadSyncedByokKeys() {
|
|
89
|
+
try {
|
|
90
|
+
const raw = await fs.readFile(getByokSyncPath(), "utf-8");
|
|
91
|
+
const parsed = JSON.parse(raw);
|
|
92
|
+
if (parsed && typeof parsed === "object" && parsed.keys) return parsed;
|
|
93
|
+
return null;
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function loadSyncedByokKeysSync() {
|
|
99
|
+
if (syncedKeysCache !== void 0) return syncedKeysCache;
|
|
100
|
+
try {
|
|
101
|
+
const fsSync = __require("fs");
|
|
102
|
+
const raw = fsSync.readFileSync(getByokSyncPath(), "utf-8");
|
|
103
|
+
const parsed = JSON.parse(raw);
|
|
104
|
+
if (parsed && typeof parsed === "object" && parsed.keys) {
|
|
105
|
+
syncedKeysCache = parsed;
|
|
106
|
+
} else {
|
|
107
|
+
syncedKeysCache = null;
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
syncedKeysCache = null;
|
|
111
|
+
}
|
|
112
|
+
return syncedKeysCache;
|
|
113
|
+
}
|
|
114
|
+
function invalidateSyncedKeysCache() {
|
|
115
|
+
syncedKeysCache = void 0;
|
|
116
|
+
}
|
|
48
117
|
function openBrowser(url) {
|
|
49
118
|
let cmd;
|
|
50
119
|
if (process.platform === "win32") {
|
|
@@ -130,7 +199,13 @@ async function login() {
|
|
|
130
199
|
}, AUTH_TIMEOUT_MS);
|
|
131
200
|
});
|
|
132
201
|
}
|
|
133
|
-
var
|
|
202
|
+
var FREESYNTAX_URL, PREFERRED_PORT, AUTH_TIMEOUT_MS, syncedKeysCache, HTML_BASE, HTML_SUCCESS, HTML_ERROR;
|
|
203
|
+
var init_auth = __esm({
|
|
204
|
+
"src/auth.ts"() {
|
|
205
|
+
FREESYNTAX_URL = "https://freesyntax.dev";
|
|
206
|
+
PREFERRED_PORT = 9721;
|
|
207
|
+
AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
208
|
+
HTML_BASE = (body) => `<!DOCTYPE html>
|
|
134
209
|
<html lang="en">
|
|
135
210
|
<head>
|
|
136
211
|
<meta charset="utf-8">
|
|
@@ -157,16 +232,18 @@ var HTML_BASE = (body) => `<!DOCTYPE html>
|
|
|
157
232
|
</head>
|
|
158
233
|
<body><div class="card">${body}</div></body>
|
|
159
234
|
</html>`;
|
|
160
|
-
|
|
235
|
+
HTML_SUCCESS = HTML_BASE(`
|
|
161
236
|
<div class="brand">notch_</div>
|
|
162
237
|
<h1>CLI authorized</h1>
|
|
163
238
|
<p>You can close this tab and return to your terminal.</p>
|
|
164
239
|
`);
|
|
165
|
-
|
|
240
|
+
HTML_ERROR = HTML_BASE(`
|
|
166
241
|
<div class="brand">notch_</div>
|
|
167
242
|
<h1>Authorization failed</h1>
|
|
168
243
|
<p style="color:#CE2127">Run <code>notch login</code> again to retry.</p>
|
|
169
244
|
`);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
170
247
|
|
|
171
248
|
export {
|
|
172
249
|
getConfigDir,
|
|
@@ -174,5 +251,12 @@ export {
|
|
|
174
251
|
loadCredentials,
|
|
175
252
|
saveCredentials,
|
|
176
253
|
clearCredentials,
|
|
177
|
-
|
|
254
|
+
getByokSyncPath,
|
|
255
|
+
syncByokKeys,
|
|
256
|
+
loadSyncedByokKeys,
|
|
257
|
+
loadSyncedByokKeysSync,
|
|
258
|
+
invalidateSyncedKeysCache,
|
|
259
|
+
login,
|
|
260
|
+
auth_exports,
|
|
261
|
+
init_auth
|
|
178
262
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
autoCompactSummarize,
|
|
3
|
+
autoCompress,
|
|
4
|
+
autoCompressWithStats,
|
|
5
|
+
budgetReduce,
|
|
6
|
+
compressHistory,
|
|
7
|
+
contextCollapse,
|
|
8
|
+
estimateTokens,
|
|
9
|
+
fullCompact,
|
|
10
|
+
getArtifactStore,
|
|
11
|
+
microCompact,
|
|
12
|
+
parseArtifactUri,
|
|
13
|
+
restoreCollapsedSpan,
|
|
14
|
+
setArtifactStore,
|
|
15
|
+
snip
|
|
16
|
+
} from "./chunk-PKZKVOAN.js";
|
|
17
|
+
import "./chunk-KFQGP6VL.js";
|
|
18
|
+
export {
|
|
19
|
+
autoCompactSummarize,
|
|
20
|
+
autoCompress,
|
|
21
|
+
autoCompressWithStats,
|
|
22
|
+
budgetReduce,
|
|
23
|
+
compressHistory,
|
|
24
|
+
contextCollapse,
|
|
25
|
+
estimateTokens,
|
|
26
|
+
fullCompact,
|
|
27
|
+
getArtifactStore,
|
|
28
|
+
microCompact,
|
|
29
|
+
parseArtifactUri,
|
|
30
|
+
restoreCollapsedSpan,
|
|
31
|
+
setArtifactStore,
|
|
32
|
+
snip
|
|
33
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
persistConfigPatch
|
|
3
|
+
} from "./chunk-J66N6AFH.js";
|
|
4
|
+
import "./chunk-KCAR5DOB.js";
|
|
5
|
+
import "./chunk-JXQ4HZ47.js";
|
|
6
|
+
import "./chunk-PPEBWOMJ.js";
|
|
7
|
+
import "./chunk-KFQGP6VL.js";
|
|
8
|
+
|
|
9
|
+
// src/commands/config-set.ts
|
|
10
|
+
import fs from "fs/promises";
|
|
11
|
+
import path from "path";
|
|
12
|
+
var HELP = `notch config \u2014 manage .notch.json settings
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
notch config get [key]
|
|
16
|
+
notch config set <key> <value>
|
|
17
|
+
notch config unset <key>
|
|
18
|
+
notch config yolo [on|off] # auto-allow every tool call (sticks)
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
notch config set permissionMode trust
|
|
22
|
+
notch config set autoConfirm true
|
|
23
|
+
notch config set model pyre
|
|
24
|
+
notch config yolo on
|
|
25
|
+
notch config yolo off
|
|
26
|
+
`;
|
|
27
|
+
function parseValue(raw) {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(raw);
|
|
30
|
+
} catch {
|
|
31
|
+
return raw;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function readCurrent(projectRoot) {
|
|
35
|
+
const p = path.resolve(projectRoot, ".notch.json");
|
|
36
|
+
try {
|
|
37
|
+
const raw = await fs.readFile(p, "utf-8");
|
|
38
|
+
const parsed = JSON.parse(raw);
|
|
39
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
async function runConfigCli(argv, projectRoot) {
|
|
47
|
+
const sub = argv[0];
|
|
48
|
+
if (!sub || sub === "-h" || sub === "--help" || sub === "help") {
|
|
49
|
+
process.stdout.write(HELP);
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
if (sub === "get") {
|
|
53
|
+
const key = argv[1];
|
|
54
|
+
const cur = await readCurrent(projectRoot);
|
|
55
|
+
if (!key) {
|
|
56
|
+
process.stdout.write(JSON.stringify(cur, null, 2) + "\n");
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
if (key in cur) {
|
|
60
|
+
process.stdout.write(JSON.stringify(cur[key]) + "\n");
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
process.stderr.write(`notch: no such key: ${key}
|
|
64
|
+
`);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
if (sub === "set") {
|
|
68
|
+
const key = argv[1];
|
|
69
|
+
const value = argv.slice(2).join(" ");
|
|
70
|
+
if (!key || value.length === 0) {
|
|
71
|
+
process.stderr.write("usage: notch config set <key> <value>\n");
|
|
72
|
+
return 2;
|
|
73
|
+
}
|
|
74
|
+
const parsed = parseValue(value);
|
|
75
|
+
const merged = await persistConfigPatch(projectRoot, { [key]: parsed });
|
|
76
|
+
process.stdout.write(`${key} = ${JSON.stringify(merged[key])}
|
|
77
|
+
`);
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
if (sub === "unset") {
|
|
81
|
+
const key = argv[1];
|
|
82
|
+
if (!key) {
|
|
83
|
+
process.stderr.write("usage: notch config unset <key>\n");
|
|
84
|
+
return 2;
|
|
85
|
+
}
|
|
86
|
+
await persistConfigPatch(projectRoot, { [key]: void 0 });
|
|
87
|
+
process.stdout.write(`removed ${key}
|
|
88
|
+
`);
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
if (sub === "yolo") {
|
|
92
|
+
const arg = (argv[1] ?? "").toLowerCase();
|
|
93
|
+
const cur = await readCurrent(projectRoot);
|
|
94
|
+
const currentlyOn = cur.permissionMode === "trust";
|
|
95
|
+
const turnOn = arg === "" ? !currentlyOn : arg === "on" || arg === "true" || arg === "1" || arg === "yes";
|
|
96
|
+
await persistConfigPatch(projectRoot, {
|
|
97
|
+
permissionMode: turnOn ? "trust" : "auto",
|
|
98
|
+
autoConfirm: turnOn ? true : void 0
|
|
99
|
+
});
|
|
100
|
+
process.stdout.write(turnOn ? "YOLO mode ON \u2014 every tool call auto-allowed. Saved to .notch.json.\n" : "YOLO mode OFF \u2014 prompts restored. Saved to .notch.json.\n");
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
process.stderr.write(`notch: unknown config subcommand: ${sub}
|
|
104
|
+
`);
|
|
105
|
+
process.stderr.write(HELP);
|
|
106
|
+
return 2;
|
|
107
|
+
}
|
|
108
|
+
export {
|
|
109
|
+
runConfigCli
|
|
110
|
+
};
|