clixad 0.0.1-beta.30 → 0.0.1-beta.31
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 +4 -0
- package/dist/clixad.mjs +115 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,10 @@ cover it, for example `task ≈ 560–3.1k · free` or `task ≈ 7.5k–47k · n
|
|
|
15
15
|
estimate from the size of the conversation and how tasks usually run; what is charged is what the
|
|
16
16
|
model actually used.
|
|
17
17
|
|
|
18
|
+
The same account also works in the browser, at <https://clixad.onrender.com/chat>: sign in with the
|
|
19
|
+
GitHub account you use here. A model, a reasoning setting, your name and instructions set there or on
|
|
20
|
+
its account page hold here too (from 0.0.1-beta.31), and `/model` and `/effort` here hold there.
|
|
21
|
+
|
|
18
22
|
`/effort` sets how long the model thinks before it answers, saved separately for each model.
|
|
19
23
|
Thinking is billed as output tokens like the answer, so a higher setting costs more credits per turn
|
|
20
24
|
and a lower one costs less. `auto` leaves the model at its own default, and a model with nothing to
|
package/dist/clixad.mjs
CHANGED
|
@@ -432,6 +432,22 @@ function extraFormatIds(raw) {
|
|
|
432
432
|
}
|
|
433
433
|
return out;
|
|
434
434
|
}
|
|
435
|
+
function parseAccountSettings(body) {
|
|
436
|
+
if (!body || typeof body !== "object") return null;
|
|
437
|
+
const b = body;
|
|
438
|
+
const efforts = {};
|
|
439
|
+
if (b.efforts && typeof b.efforts === "object" && !Array.isArray(b.efforts)) {
|
|
440
|
+
for (const [k, v] of Object.entries(b.efforts)) if (typeof v === "string") efforts[k] = v;
|
|
441
|
+
}
|
|
442
|
+
const str = (v) => typeof v === "string" && v.trim() ? v : null;
|
|
443
|
+
return {
|
|
444
|
+
model: str(b.model),
|
|
445
|
+
efforts,
|
|
446
|
+
name: str(b.name),
|
|
447
|
+
instructions: str(b.instructions),
|
|
448
|
+
sponsorLine: typeof b.sponsor_line === "boolean" ? b.sponsor_line : null
|
|
449
|
+
};
|
|
450
|
+
}
|
|
435
451
|
function imagePart(image) {
|
|
436
452
|
return { type: "image_url", image_url: { url: `data:${image.mimeType};base64,${image.data}` } };
|
|
437
453
|
}
|
|
@@ -840,6 +856,42 @@ var init_client = __esm({
|
|
|
840
856
|
* is "say nothing about a streak". A missed grant is claimed by the next
|
|
841
857
|
* command; a session that died over one would be a real loss.
|
|
842
858
|
*/
|
|
859
|
+
/**
|
|
860
|
+
* The account's preferences (gateway 0039), kept with the account so they
|
|
861
|
+
* hold in the browser's chat and here. Null when they cannot be read, and on
|
|
862
|
+
* a gateway too old to have them (404): a preference is never a reason for
|
|
863
|
+
* the REPL not to open.
|
|
864
|
+
*/
|
|
865
|
+
async getSettings(signal) {
|
|
866
|
+
try {
|
|
867
|
+
const res = await gatewayFetch(`${this.config.gatewayUrl}/v1/account/settings`, {
|
|
868
|
+
headers: this.headers(),
|
|
869
|
+
...signal ? { signal } : {}
|
|
870
|
+
});
|
|
871
|
+
if (!res.ok) return null;
|
|
872
|
+
return parseAccountSettings(await res.json());
|
|
873
|
+
} catch {
|
|
874
|
+
return null;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Change the preferences a request names. Best-effort and never throws: a
|
|
879
|
+
* model picked here is saved locally whatever the gateway says, and a
|
|
880
|
+
* failed sync costs only that the browser's chat does not follow.
|
|
881
|
+
*/
|
|
882
|
+
async saveSettings(change) {
|
|
883
|
+
try {
|
|
884
|
+
const res = await gatewayFetch(`${this.config.gatewayUrl}/v1/account/settings`, {
|
|
885
|
+
method: "PUT",
|
|
886
|
+
headers: { ...this.headers(), "Content-Type": "application/json" },
|
|
887
|
+
body: JSON.stringify(change)
|
|
888
|
+
});
|
|
889
|
+
if (!res.ok) return null;
|
|
890
|
+
return parseAccountSettings(await res.json());
|
|
891
|
+
} catch {
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
843
895
|
async claimStreak(signal) {
|
|
844
896
|
try {
|
|
845
897
|
const res = await gatewayFetch(`${this.config.gatewayUrl}/v1/streak/claim`, {
|
|
@@ -1638,6 +1690,46 @@ var init_config = __esm({
|
|
|
1638
1690
|
}
|
|
1639
1691
|
});
|
|
1640
1692
|
|
|
1693
|
+
// src/preferences.ts
|
|
1694
|
+
async function syncPreferences(client, config, settings) {
|
|
1695
|
+
if (!settings) return { name: null, instructions: null, sponsorLine: null };
|
|
1696
|
+
const upload = {};
|
|
1697
|
+
if (settings.model && settings.model !== config.model) {
|
|
1698
|
+
config.model = settings.model;
|
|
1699
|
+
saveConfig(config);
|
|
1700
|
+
} else if (!settings.model && config.model && config.model !== FALLBACK_MODEL) {
|
|
1701
|
+
upload.model = config.model;
|
|
1702
|
+
}
|
|
1703
|
+
const local = config.effort ?? {};
|
|
1704
|
+
for (const [model, level] of Object.entries(settings.efforts)) {
|
|
1705
|
+
if (local[model] !== level) saveEffort(config, model, level);
|
|
1706
|
+
}
|
|
1707
|
+
const localOnly = {};
|
|
1708
|
+
for (const [model, level] of Object.entries(local)) {
|
|
1709
|
+
if (!(model in settings.efforts)) localOnly[model] = level;
|
|
1710
|
+
}
|
|
1711
|
+
if (Object.keys(localOnly).length) upload.efforts = localOnly;
|
|
1712
|
+
if (Object.keys(upload).length) await client.saveSettings(upload).catch(() => null);
|
|
1713
|
+
return { name: settings.name, instructions: settings.instructions, sponsorLine: settings.sponsorLine };
|
|
1714
|
+
}
|
|
1715
|
+
function preferencesInstruction(prefs) {
|
|
1716
|
+
if (!prefs) return "";
|
|
1717
|
+
const parts = [];
|
|
1718
|
+
if (prefs.name) parts.push(`The person's name is ${prefs.name}.`);
|
|
1719
|
+
if (prefs.instructions) parts.push(`What the person wants you to keep in mind in every answer:
|
|
1720
|
+
${prefs.instructions}`);
|
|
1721
|
+
return parts.join("\n\n");
|
|
1722
|
+
}
|
|
1723
|
+
function sponsorPreference(config, prefs) {
|
|
1724
|
+
return prefs?.sponsorLine === false ? false : config.sponsor;
|
|
1725
|
+
}
|
|
1726
|
+
var init_preferences = __esm({
|
|
1727
|
+
"src/preferences.ts"() {
|
|
1728
|
+
"use strict";
|
|
1729
|
+
init_config();
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1732
|
+
|
|
1641
1733
|
// src/browser.ts
|
|
1642
1734
|
import { spawn } from "node:child_process";
|
|
1643
1735
|
function openBrowser(url) {
|
|
@@ -6224,9 +6316,11 @@ function App({
|
|
|
6224
6316
|
sponsorServe,
|
|
6225
6317
|
sponsorFormats,
|
|
6226
6318
|
sponsorDirect,
|
|
6227
|
-
initHint
|
|
6319
|
+
initHint,
|
|
6320
|
+
preferences
|
|
6228
6321
|
}) {
|
|
6229
6322
|
const { exit } = useApp();
|
|
6323
|
+
const preferencesText = preferencesInstruction(preferences);
|
|
6230
6324
|
const { stdout, write: writeToStdout } = useStdout();
|
|
6231
6325
|
const idRef = useRef(1);
|
|
6232
6326
|
const { rows, cols } = useTerminalSize();
|
|
@@ -6270,7 +6364,8 @@ function App({
|
|
|
6270
6364
|
const switchModel = useCallback((id) => {
|
|
6271
6365
|
modelRef.current = id;
|
|
6272
6366
|
setModel2(id);
|
|
6273
|
-
|
|
6367
|
+
void client.saveSettings?.({ model: id });
|
|
6368
|
+
}, [client]);
|
|
6274
6369
|
const [hist, setHist] = useState(() => loadHistory());
|
|
6275
6370
|
const [histIdx, setHistIdx] = useState(-1);
|
|
6276
6371
|
const [sel, setSel] = useState(0);
|
|
@@ -6544,7 +6639,7 @@ function App({
|
|
|
6544
6639
|
if (!sponsorEnabled({
|
|
6545
6640
|
isTTY: stdout.isTTY,
|
|
6546
6641
|
env: process.env,
|
|
6547
|
-
configEnabled: config
|
|
6642
|
+
configEnabled: sponsorPreference(config, preferences)
|
|
6548
6643
|
})) {
|
|
6549
6644
|
setSponsor(null);
|
|
6550
6645
|
setAdSlots({});
|
|
@@ -6817,7 +6912,11 @@ function App({
|
|
|
6817
6912
|
|
|
6818
6913
|
${modeInstruction(permRef.current.mode)}` + (searchRef.current ? "" : `
|
|
6819
6914
|
|
|
6820
|
-
${NO_SEARCH_INSTRUCTION}`),
|
|
6915
|
+
${NO_SEARCH_INSTRUCTION}`) + // The account's name and instructions (gateway 0039), set in the
|
|
6916
|
+
// browser or on the account page, said on every turn.
|
|
6917
|
+
(preferencesText ? `
|
|
6918
|
+
|
|
6919
|
+
${preferencesText}` : ""),
|
|
6821
6920
|
permit,
|
|
6822
6921
|
approvePlan,
|
|
6823
6922
|
askUser: askQuestion,
|
|
@@ -7442,6 +7541,7 @@ ${NO_SEARCH_INSTRUCTION}`),
|
|
|
7442
7541
|
const defaultWords = fallback ? effortLabel(fallback) : "its default";
|
|
7443
7542
|
const apply2 = (choice, note = "") => {
|
|
7444
7543
|
saveEffort(config, model, choice);
|
|
7544
|
+
void client.saveSettings?.({ efforts: { [model]: choice ?? "auto" } });
|
|
7445
7545
|
setEffortBy({ ...config.effort ?? {} });
|
|
7446
7546
|
const now = choice ? effortLabel(choice) : `auto (${defaultWords})`;
|
|
7447
7547
|
push({ kind: "notice", text: ` ${name}: ${now}${note}` });
|
|
@@ -8459,6 +8559,7 @@ var init_app = __esm({
|
|
|
8459
8559
|
init_todos();
|
|
8460
8560
|
init_shells();
|
|
8461
8561
|
init_config();
|
|
8562
|
+
init_preferences();
|
|
8462
8563
|
init_effort();
|
|
8463
8564
|
init_login();
|
|
8464
8565
|
init_banner();
|
|
@@ -8568,6 +8669,7 @@ var init_tui = __esm({
|
|
|
8568
8669
|
// src/main.ts
|
|
8569
8670
|
init_client();
|
|
8570
8671
|
init_config();
|
|
8672
|
+
init_preferences();
|
|
8571
8673
|
init_login();
|
|
8572
8674
|
init_free_tier();
|
|
8573
8675
|
init_agent();
|
|
@@ -9076,7 +9178,8 @@ async function repl(client, config, opts = {}) {
|
|
|
9076
9178
|
session = loadSession(opts.resume);
|
|
9077
9179
|
if (!session) return console.log(c.red(`no such session: ${opts.resume}`));
|
|
9078
9180
|
}
|
|
9079
|
-
const { streak, wallet } = await startupReads(client, config.gatewayUrl);
|
|
9181
|
+
const { streak, wallet, settings } = await startupReads(client, config.gatewayUrl);
|
|
9182
|
+
const preferences = await syncPreferences(client, config, settings);
|
|
9080
9183
|
const cwd = process.cwd();
|
|
9081
9184
|
const initHint = shouldSuggestInit(cwd) ? { returning: hasSessionIn(cwd) } : void 0;
|
|
9082
9185
|
clearScreen();
|
|
@@ -9088,6 +9191,7 @@ async function repl(client, config, opts = {}) {
|
|
|
9088
9191
|
streak,
|
|
9089
9192
|
session,
|
|
9090
9193
|
newAccount,
|
|
9194
|
+
preferences,
|
|
9091
9195
|
initialTask: opts.task,
|
|
9092
9196
|
...opts.notices?.length ? { notices: opts.notices } : {},
|
|
9093
9197
|
...initHint ? { initHint } : {}
|
|
@@ -9166,10 +9270,15 @@ async function startupReads(client, gatewayUrl) {
|
|
|
9166
9270
|
)
|
|
9167
9271
|
];
|
|
9168
9272
|
try {
|
|
9169
|
-
const [streak, wallet] = await Promise.all([
|
|
9273
|
+
const [streak, wallet, settings] = await Promise.all([
|
|
9274
|
+
client.claimStreak(),
|
|
9275
|
+
safeWallet(client, { premium: true }),
|
|
9276
|
+
client.getSettings()
|
|
9277
|
+
]);
|
|
9170
9278
|
const balance = streak?.claimed ? streak.balance : wallet?.balance;
|
|
9171
9279
|
return {
|
|
9172
9280
|
streak,
|
|
9281
|
+
settings,
|
|
9173
9282
|
wallet: wallet && balance !== void 0 ? { ...wallet, balance } : wallet
|
|
9174
9283
|
};
|
|
9175
9284
|
} finally {
|