thinyai 0.1.7 → 0.1.9
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/bin.js +305 -27
- package/package.json +6 -6
package/dist/bin.js
CHANGED
|
@@ -1829,24 +1829,83 @@ function applyConfig(cfg) {
|
|
|
1829
1829
|
const set = (k, v) => {
|
|
1830
1830
|
if (v && !process.env[k]) process.env[k] = v;
|
|
1831
1831
|
};
|
|
1832
|
-
|
|
1833
|
-
if (
|
|
1834
|
-
set(
|
|
1832
|
+
const prov = activeProvider(cfg);
|
|
1833
|
+
if (prov) {
|
|
1834
|
+
set("THINY_MODEL", prov.model);
|
|
1835
|
+
if (prov.apiKey) {
|
|
1836
|
+
set(prov.model.startsWith("anthropic") ? "THINY_ANTHROPIC_API_KEY" : "THINY_OPENAI_API_KEY", prov.apiKey);
|
|
1837
|
+
}
|
|
1838
|
+
set("THINY_OPENAI_BASE_URL", prov.baseUrl);
|
|
1835
1839
|
}
|
|
1836
|
-
set("THINY_OPENAI_BASE_URL", cfg.baseUrl);
|
|
1837
1840
|
set("THINY_PERSONA_NAME", cfg.agentName);
|
|
1838
1841
|
set("THINY_USER_ID", cfg.userId);
|
|
1839
1842
|
if (cfg.sui?.network) {
|
|
1840
1843
|
set("SUI_NETWORK", cfg.sui.network);
|
|
1841
1844
|
if (cfg.sui.network === "mainnet") set("SUI_ALLOW_MAINNET", "1");
|
|
1842
1845
|
}
|
|
1843
|
-
const
|
|
1844
|
-
if (
|
|
1845
|
-
set("SUI_SECRET_KEY",
|
|
1846
|
-
set("THINY_SUI_SECRET_KEY",
|
|
1846
|
+
const active = activeSuiWallet(cfg);
|
|
1847
|
+
if (active) {
|
|
1848
|
+
set("SUI_SECRET_KEY", active.secretKey);
|
|
1849
|
+
set("THINY_SUI_SECRET_KEY", active.secretKey);
|
|
1847
1850
|
}
|
|
1848
1851
|
set("MCP_URL", cfg.sui?.rillMcpUrl);
|
|
1849
1852
|
}
|
|
1853
|
+
function suiWalletsOf(cfg) {
|
|
1854
|
+
const s = cfg?.sui;
|
|
1855
|
+
if (!s) return [];
|
|
1856
|
+
if (s.wallets && s.wallets.length > 0) return s.wallets;
|
|
1857
|
+
if (s.wallet && s.address) {
|
|
1858
|
+
return [{ label: "default", address: s.address, secretKey: s.wallet.secretKey, source: s.wallet.type }];
|
|
1859
|
+
}
|
|
1860
|
+
return [];
|
|
1861
|
+
}
|
|
1862
|
+
function activeSuiWallet(cfg) {
|
|
1863
|
+
const all = suiWalletsOf(cfg);
|
|
1864
|
+
const active = cfg?.sui?.activeAddress ?? cfg?.sui?.address;
|
|
1865
|
+
return all.find((w) => w.address === active) ?? all[0];
|
|
1866
|
+
}
|
|
1867
|
+
function saveSuiWallet(cfg, network, wallet, makeActive) {
|
|
1868
|
+
cfg.sui ??= { network };
|
|
1869
|
+
cfg.sui.network = network;
|
|
1870
|
+
const all = suiWalletsOf(cfg).filter((w) => w.address !== wallet.address);
|
|
1871
|
+
all.push(wallet);
|
|
1872
|
+
cfg.sui.wallets = all;
|
|
1873
|
+
delete cfg.sui.wallet;
|
|
1874
|
+
delete cfg.sui.address;
|
|
1875
|
+
if (makeActive || !cfg.sui.activeAddress) cfg.sui.activeAddress = wallet.address;
|
|
1876
|
+
saveConfig(cfg);
|
|
1877
|
+
}
|
|
1878
|
+
function providersOf(cfg) {
|
|
1879
|
+
if (!cfg) return [];
|
|
1880
|
+
if (cfg.providers && cfg.providers.length > 0) return cfg.providers;
|
|
1881
|
+
if (cfg.model) {
|
|
1882
|
+
const id = cfg.model.startsWith("anthropic") ? "anthropic" : cfg.baseUrl ? "custom" : "openai";
|
|
1883
|
+
return [{ id, label: cfg.model, model: cfg.model, apiKey: cfg.apiKey, baseUrl: cfg.baseUrl }];
|
|
1884
|
+
}
|
|
1885
|
+
return [];
|
|
1886
|
+
}
|
|
1887
|
+
function activeProvider(cfg) {
|
|
1888
|
+
const all = providersOf(cfg);
|
|
1889
|
+
return all.find((x) => x.id === cfg?.activeProviderId) ?? all[0];
|
|
1890
|
+
}
|
|
1891
|
+
function saveProvider(cfg, provider, makeActive) {
|
|
1892
|
+
const all = providersOf(cfg).filter((x) => x.id !== provider.id);
|
|
1893
|
+
all.push(provider);
|
|
1894
|
+
cfg.providers = all;
|
|
1895
|
+
delete cfg.model;
|
|
1896
|
+
delete cfg.apiKey;
|
|
1897
|
+
delete cfg.baseUrl;
|
|
1898
|
+
if (makeActive || !cfg.activeProviderId) cfg.activeProviderId = provider.id;
|
|
1899
|
+
saveConfig(cfg);
|
|
1900
|
+
}
|
|
1901
|
+
function setActiveProvider(cfg, id) {
|
|
1902
|
+
const prov = providersOf(cfg).find((x) => x.id === id);
|
|
1903
|
+
if (!prov) return void 0;
|
|
1904
|
+
cfg.providers = providersOf(cfg);
|
|
1905
|
+
cfg.activeProviderId = id;
|
|
1906
|
+
saveConfig(cfg);
|
|
1907
|
+
return prov;
|
|
1908
|
+
}
|
|
1850
1909
|
function bail(v) {
|
|
1851
1910
|
if (p.isCancel(v)) {
|
|
1852
1911
|
p.cancel("Cancelled.");
|
|
@@ -1870,26 +1929,30 @@ async function baseSetup() {
|
|
|
1870
1929
|
const choice = bail(
|
|
1871
1930
|
await p.select({ message: "Pick a model", options: MODELS.map(({ value, label, hint }) => ({ value, label, hint })) })
|
|
1872
1931
|
);
|
|
1873
|
-
const pick = MODELS.find((m) => m.value === choice)
|
|
1874
|
-
|
|
1932
|
+
const pick = MODELS.find((m) => m.value === choice);
|
|
1933
|
+
if (!pick) throw new Error(`unknown model choice: ${choice}`);
|
|
1934
|
+
const cfg = loadConfig() ?? {};
|
|
1935
|
+
cfg.agentName = agentName;
|
|
1936
|
+
cfg.userId ??= "default";
|
|
1937
|
+
let provider;
|
|
1875
1938
|
if (pick.custom) {
|
|
1876
|
-
|
|
1939
|
+
const model = bail(
|
|
1877
1940
|
await p.text({ message: "Model id", placeholder: "e.g. MiniMax-M3", validate: (v) => v ? void 0 : "Required" })
|
|
1878
1941
|
);
|
|
1879
|
-
|
|
1942
|
+
const baseUrl = bail(
|
|
1880
1943
|
await p.text({
|
|
1881
1944
|
message: "Base URL (OpenAI-compatible)",
|
|
1882
1945
|
placeholder: "https://api.example.com/v1",
|
|
1883
|
-
validate: (v) => /^https?:\/\//.test(v) ? void 0 : "Must start with http(s)://"
|
|
1946
|
+
validate: (v) => v && /^https?:\/\//.test(v) ? void 0 : "Must start with http(s)://"
|
|
1884
1947
|
})
|
|
1885
1948
|
);
|
|
1886
|
-
|
|
1949
|
+
const apiKey = bail(await p.password({ message: "API key" }));
|
|
1950
|
+
provider = { id: model, label: model, model, baseUrl, apiKey };
|
|
1887
1951
|
} else {
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
cfg.apiKey = pick.apiKey ?? (pick.needsKey ? bail(await p.password({ message: "API key" })) : void 0);
|
|
1952
|
+
const apiKey = pick.apiKey ?? (pick.needsKey ? bail(await p.password({ message: "API key" })) : void 0);
|
|
1953
|
+
provider = { id: pick.value, label: pick.label, model: pick.model ?? "", baseUrl: pick.baseUrl, apiKey };
|
|
1891
1954
|
}
|
|
1892
|
-
|
|
1955
|
+
saveProvider(cfg, provider, true);
|
|
1893
1956
|
p.outro(`Saved ${CONFIG} \u2014 run \`thiny\` to start, or \`thiny sui init\` for Sui.`);
|
|
1894
1957
|
return cfg;
|
|
1895
1958
|
}
|
|
@@ -1927,20 +1990,22 @@ async function suiInit() {
|
|
|
1927
1990
|
const sk = bail(
|
|
1928
1991
|
await p.password({
|
|
1929
1992
|
message: "Private key (suiprivkey\u2026)",
|
|
1930
|
-
validate: (v) => v
|
|
1993
|
+
validate: (v) => v?.startsWith("suiprivkey") ? void 0 : "Expected a suiprivkey\u2026 string"
|
|
1931
1994
|
})
|
|
1932
1995
|
);
|
|
1933
1996
|
wallet = { type: "imported", secretKey: sk };
|
|
1934
1997
|
address = Ed25519Keypair2.fromSecretKey(sk).getPublicKey().toSuiAddress();
|
|
1935
1998
|
}
|
|
1936
|
-
cfg
|
|
1999
|
+
saveSuiWallet(cfg, network, { label: choice, address, secretKey: wallet.secretKey, source: wallet.type }, true);
|
|
1937
2000
|
if (choice === "rill") {
|
|
1938
2001
|
const url = bail(
|
|
1939
2002
|
await p.text({ message: "Rill MCP URL", placeholder: "leave blank to add later", defaultValue: "" })
|
|
1940
2003
|
);
|
|
1941
|
-
if (url
|
|
2004
|
+
if (url && cfg.sui) {
|
|
2005
|
+
cfg.sui.rillMcpUrl = url;
|
|
2006
|
+
saveConfig(cfg);
|
|
2007
|
+
}
|
|
1942
2008
|
}
|
|
1943
|
-
saveConfig(cfg);
|
|
1944
2009
|
const faucet = network === "testnet" ? "\nFaucet: https://faucet.sui.io (or `sui client faucet`)" : "";
|
|
1945
2010
|
p.note(`${address}${faucet}`, `\u26A0 Fund this address (${network}) before sending transactions`);
|
|
1946
2011
|
p.outro(`Sui configured (${network}).`);
|
|
@@ -2324,9 +2389,22 @@ async function runCli() {
|
|
|
2324
2389
|
const turn = { inputTokens: 0, outputTokens: 0, toolCalls: 0, modelCalls: 0 };
|
|
2325
2390
|
const session = { inputTokens: 0, outputTokens: 0, toolCalls: 0, turns: 0 };
|
|
2326
2391
|
const logger = captureStats(fileLogger, turn);
|
|
2327
|
-
const activeModelName = process.env.THINY_MODEL ?? process.env.AGENT_MODEL ?? "openai:gpt-4o-mini";
|
|
2328
2392
|
const personaName = process.env.THINY_PERSONA_NAME ?? "Thiny";
|
|
2329
|
-
const
|
|
2393
|
+
const buildModel = (p2) => aiSdkModel({
|
|
2394
|
+
model: p2.model,
|
|
2395
|
+
openai: { baseURL: p2.baseUrl, apiKey: p2.apiKey },
|
|
2396
|
+
anthropic: { apiKey: p2.apiKey }
|
|
2397
|
+
});
|
|
2398
|
+
const startProvider = activeProvider(loadConfig());
|
|
2399
|
+
let activeModel = startProvider ? buildModel(startProvider) : loadThinyConfig();
|
|
2400
|
+
let activeModelName = startProvider?.model ?? process.env.THINY_MODEL ?? process.env.AGENT_MODEL ?? "openai:gpt-4o-mini";
|
|
2401
|
+
const model = {
|
|
2402
|
+
generate: (m, t, s) => activeModel.generate(m, t, s),
|
|
2403
|
+
stream: (m, t, s) => {
|
|
2404
|
+
if (!activeModel.stream) throw new Error("active model has no streaming");
|
|
2405
|
+
return activeModel.stream(m, t, s);
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2330
2408
|
const network = process.env.WALRUS_NETWORK === "mainnet" ? "mainnet" : "testnet";
|
|
2331
2409
|
const walrus = walrusClient({
|
|
2332
2410
|
network,
|
|
@@ -2421,6 +2499,125 @@ async function runCli() {
|
|
|
2421
2499
|
};
|
|
2422
2500
|
}
|
|
2423
2501
|
});
|
|
2502
|
+
const activateSigner = (secretKey) => {
|
|
2503
|
+
suiSignerRef = suiSigner({ network: suiNetwork, secretKey, allowMainnet });
|
|
2504
|
+
};
|
|
2505
|
+
const suiWalletsTool = defineTool({
|
|
2506
|
+
name: "sui_wallets",
|
|
2507
|
+
description: "List every Sui wallet the user has: the local agent wallets (with addresses) and the Rill MCP signer if connected. Use to answer 'what wallets/addresses do I have', or to pick one. Does NOT reveal private keys (use sui_export_wallet for that).",
|
|
2508
|
+
parameters: z7.object({}),
|
|
2509
|
+
execute: () => {
|
|
2510
|
+
const cfg = loadConfig();
|
|
2511
|
+
const active = activeSuiWallet(cfg)?.address;
|
|
2512
|
+
return {
|
|
2513
|
+
network: cfg?.sui?.network ?? suiNetwork,
|
|
2514
|
+
activeAddress: active,
|
|
2515
|
+
agentWallets: suiWalletsOf(cfg).map((w) => ({
|
|
2516
|
+
label: w.label,
|
|
2517
|
+
address: w.address,
|
|
2518
|
+
source: w.source,
|
|
2519
|
+
active: w.address === active
|
|
2520
|
+
})),
|
|
2521
|
+
rill: cfg?.sui?.rillMcpUrl ? { source: "rill", mcpUrl: cfg.sui.rillMcpUrl } : null
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
});
|
|
2525
|
+
const suiCreateWalletTool = defineTool({
|
|
2526
|
+
name: "sui_create_wallet",
|
|
2527
|
+
description: "Generate a NEW Sui agent wallet (key pair) locally and save it. Returns the new address \u2014 remind the user to fund it. Use when the user asks for a new/another wallet or address.",
|
|
2528
|
+
sensitive: true,
|
|
2529
|
+
parameters: z7.object({
|
|
2530
|
+
label: z7.string().optional().describe("A name for the wallet (default: wallet-N)."),
|
|
2531
|
+
activate: z7.boolean().optional().describe("Make it the active signing wallet (default true).")
|
|
2532
|
+
}),
|
|
2533
|
+
execute: async ({ label, activate }) => {
|
|
2534
|
+
const { Ed25519Keypair: Ed25519Keypair2 } = await import("@mysten/sui/keypairs/ed25519");
|
|
2535
|
+
const kp = Ed25519Keypair2.generate();
|
|
2536
|
+
const address = kp.getPublicKey().toSuiAddress();
|
|
2537
|
+
const cfg = loadConfig() ?? {};
|
|
2538
|
+
const makeActive = activate ?? true;
|
|
2539
|
+
const n = suiWalletsOf(cfg).length + 1;
|
|
2540
|
+
saveSuiWallet(
|
|
2541
|
+
cfg,
|
|
2542
|
+
suiNetwork,
|
|
2543
|
+
{ label: label ?? `wallet-${String(n)}`, address, secretKey: kp.getSecretKey(), source: "generated" },
|
|
2544
|
+
makeActive
|
|
2545
|
+
);
|
|
2546
|
+
if (makeActive) activateSigner(kp.getSecretKey());
|
|
2547
|
+
return {
|
|
2548
|
+
address,
|
|
2549
|
+
active: makeActive,
|
|
2550
|
+
note: `New wallet on ${suiNetwork}. Fund ${address} before transacting${suiNetwork === "testnet" ? " (faucet: https://faucet.sui.io)" : ""}.`
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
});
|
|
2554
|
+
const suiImportWalletTool = defineTool({
|
|
2555
|
+
name: "sui_import_wallet",
|
|
2556
|
+
description: "Import an existing Sui wallet from its private key (suiprivkey\u2026) and save it. Use when the user wants to add/restore a wallet they already have.",
|
|
2557
|
+
sensitive: true,
|
|
2558
|
+
parameters: z7.object({
|
|
2559
|
+
secretKey: z7.string().min(1).describe("The private key, a suiprivkey\u2026 string."),
|
|
2560
|
+
label: z7.string().optional().describe("A name for the wallet."),
|
|
2561
|
+
activate: z7.boolean().optional().describe("Make it the active signing wallet (default true).")
|
|
2562
|
+
}),
|
|
2563
|
+
execute: async ({ secretKey, label, activate }) => {
|
|
2564
|
+
if (!secretKey.startsWith("suiprivkey")) {
|
|
2565
|
+
throw new Error("sui_import_wallet: expected a private key starting with suiprivkey\u2026");
|
|
2566
|
+
}
|
|
2567
|
+
const { Ed25519Keypair: Ed25519Keypair2 } = await import("@mysten/sui/keypairs/ed25519");
|
|
2568
|
+
const address = Ed25519Keypair2.fromSecretKey(secretKey).getPublicKey().toSuiAddress();
|
|
2569
|
+
const cfg = loadConfig() ?? {};
|
|
2570
|
+
const makeActive = activate ?? true;
|
|
2571
|
+
saveSuiWallet(
|
|
2572
|
+
cfg,
|
|
2573
|
+
suiNetwork,
|
|
2574
|
+
{ label: label ?? "imported", address, secretKey, source: "imported" },
|
|
2575
|
+
makeActive
|
|
2576
|
+
);
|
|
2577
|
+
if (makeActive) activateSigner(secretKey);
|
|
2578
|
+
return { address, active: makeActive, note: `Imported wallet ${address} on ${suiNetwork}.` };
|
|
2579
|
+
}
|
|
2580
|
+
});
|
|
2581
|
+
const suiExportWalletTool = defineTool({
|
|
2582
|
+
name: "sui_export_wallet",
|
|
2583
|
+
description: "Reveal the PRIVATE KEY of a saved wallet so the user can back it up or move it elsewhere. Defaults to the active wallet. SENSITIVE \u2014 only when the user explicitly asks to export/back up.",
|
|
2584
|
+
sensitive: true,
|
|
2585
|
+
parameters: z7.object({
|
|
2586
|
+
address: z7.string().optional().describe("Which wallet to export (default: the active one).")
|
|
2587
|
+
}),
|
|
2588
|
+
execute: ({ address }) => {
|
|
2589
|
+
const cfg = loadConfig();
|
|
2590
|
+
const all = suiWalletsOf(cfg);
|
|
2591
|
+
const w = address ? all.find((x) => x.address === address) : activeSuiWallet(cfg);
|
|
2592
|
+
if (!w) throw new Error("sui_export_wallet: no matching wallet found.");
|
|
2593
|
+
return {
|
|
2594
|
+
address: w.address,
|
|
2595
|
+
secretKey: w.secretKey,
|
|
2596
|
+
warning: "Keep this private key secret \u2014 anyone who has it controls the wallet."
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
});
|
|
2600
|
+
const suiUseWalletTool = defineTool({
|
|
2601
|
+
name: "sui_use_wallet",
|
|
2602
|
+
description: "Switch the active signing wallet to a saved one by address. Use to send from a different wallet.",
|
|
2603
|
+
parameters: z7.object({ address: z7.string().min(1).describe("Address of the wallet to make active.") }),
|
|
2604
|
+
execute: ({ address }) => {
|
|
2605
|
+
const cfg = loadConfig();
|
|
2606
|
+
const w = suiWalletsOf(cfg).find((x) => x.address === address);
|
|
2607
|
+
if (!w || !cfg?.sui) throw new Error(`sui_use_wallet: no saved wallet ${address}.`);
|
|
2608
|
+
cfg.sui.activeAddress = address;
|
|
2609
|
+
saveConfig(cfg);
|
|
2610
|
+
activateSigner(w.secretKey);
|
|
2611
|
+
return { activeAddress: address, note: `Now signing as ${address}.` };
|
|
2612
|
+
}
|
|
2613
|
+
});
|
|
2614
|
+
const walletTools = [
|
|
2615
|
+
suiWalletsTool,
|
|
2616
|
+
suiCreateWalletTool,
|
|
2617
|
+
suiImportWalletTool,
|
|
2618
|
+
suiExportWalletTool,
|
|
2619
|
+
suiUseWalletTool
|
|
2620
|
+
];
|
|
2424
2621
|
const fetchUrlTool = defineTool({
|
|
2425
2622
|
name: "fetch_url",
|
|
2426
2623
|
description: "Fetch the contents of an http(s) URL (markdown, text, JSON, HTML). ALWAYS use this when the user shares a link \u2014 e.g. a skill.md, docs page, or an API/MCP endpoint \u2014 instead of saying you can't open URLs. Returns the response text (truncated if very large).",
|
|
@@ -2492,8 +2689,8 @@ HOW TO ACT: When a request maps to one of your tools, CALL THE TOOL automaticall
|
|
|
2492
2689
|
YOUR TOOLS:
|
|
2493
2690
|
\u2022 Memory \u2014 remember_fact, recall_memory: durable memory across sessions (stored on Walrus). Known facts are injected each turn under \u201C[User Memory \u2026]\u201D. Immediately save anything durable the user shares (name, role, preferences, projects, goals). Answer \u201Cwhat do you remember\u201D from it. You DO remember across sessions \u2014 never say otherwise.
|
|
2494
2691
|
\u2022 Links \u2014 fetch_url: read ANY URL the user shares (a skill.md, docs, JSON, an API/MCP endpoint). Always fetch shared links instead of saying you can't open URLs.
|
|
2495
|
-
` + (webSearchOn ? "\u2022 Web search \u2014 web_search: search the web for anything you don't know (news, prices, docs). web_search FINDS pages by query; fetch_url READS a specific URL \u2014 use them together.\n" : "") + "\u2022 Planning \u2014 update_plan (track multi-step work), delegate_task (hand a focused subtask to a sub-agent).\n\u2022 Sui blockchain \u2014 you transact yourself; NEVER tell the user to install a browser wallet. " + (suiSignerRef ? `
|
|
2496
|
-
tools: [echoTool, suiSetupTool, fetchUrlTool, ...webTools],
|
|
2692
|
+
` + (webSearchOn ? "\u2022 Web search \u2014 web_search: search the web for anything you don't know (news, prices, docs). web_search FINDS pages by query; fetch_url READS a specific URL \u2014 use them together.\n" : "") + "\u2022 Planning \u2014 update_plan (track multi-step work), delegate_task (hand a focused subtask to a sub-agent).\n\u2022 Sui blockchain \u2014 you transact yourself; NEVER tell the user to install a browser wallet. " + (suiSignerRef ? `The active wallet is on ${suiNetwork} at ${suiSignerRef.address ?? "?"}. ` : "No wallet yet \u2014 call sui_create_wallet (or sui_import_wallet) when the user wants Sui, then have them fund the address. ") + "Wallets: sui_wallets (list ALL the user's wallets + addresses \u2014 use this to answer 'what's my address / what wallets do I have'), sui_create_wallet (new key pair), sui_import_wallet (restore from a suiprivkey), sui_export_wallet (reveal a private key \u2014 only when asked), sui_use_wallet (switch the active wallet). On-chain: sui_balance & sui_object (read), sui_transfer (send SUI/any coin \u2014 amounts in MIST, 1 SUI = 1e9), sui_move_call (call ANY Move function), sui_execute_ptb (sign a builder/Rill PTB). Prefer sui_transfer for sends and sui_move_call for contract calls; confirm details before signing.",
|
|
2693
|
+
tools: [echoTool, suiSetupTool, ...walletTools, fetchUrlTool, ...webTools],
|
|
2497
2694
|
plugins: [
|
|
2498
2695
|
{
|
|
2499
2696
|
name: "observability",
|
|
@@ -2578,6 +2775,76 @@ YOUR TOOLS:
|
|
|
2578
2775
|
});
|
|
2579
2776
|
} else memoryRefs.push(ref);
|
|
2580
2777
|
};
|
|
2778
|
+
const handleConnect = async (arg) => {
|
|
2779
|
+
const cfg = loadConfig() ?? {};
|
|
2780
|
+
const provs = providersOf(cfg);
|
|
2781
|
+
if (provs.length === 0) {
|
|
2782
|
+
renderInfo("No providers configured \u2014 run `thiny init` to add one.");
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
let id = arg;
|
|
2786
|
+
if (!id) {
|
|
2787
|
+
renderInfo("\nProviders:");
|
|
2788
|
+
provs.forEach((pr, i) => {
|
|
2789
|
+
renderInfo(
|
|
2790
|
+
` ${String(i + 1)}. ${pr.label} (${pr.model})${pr.id === cfg.activeProviderId ? " \xB7 active" : ""}`
|
|
2791
|
+
);
|
|
2792
|
+
});
|
|
2793
|
+
const ans = (await rl.question("Switch to (number, blank to cancel): ")).trim();
|
|
2794
|
+
if (!ans) return;
|
|
2795
|
+
const idx = Number(ans) - 1;
|
|
2796
|
+
const chosen = provs[idx];
|
|
2797
|
+
if (!chosen) {
|
|
2798
|
+
renderWarning("Invalid choice.");
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
id = chosen.id;
|
|
2802
|
+
}
|
|
2803
|
+
const match = provs.find((pr) => pr.id === id || pr.label === id || pr.model === id);
|
|
2804
|
+
const prov = match ? setActiveProvider(cfg, match.id) : void 0;
|
|
2805
|
+
if (!prov) {
|
|
2806
|
+
renderWarning(`No provider "${id}".`);
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2809
|
+
activeModel = buildModel(prov);
|
|
2810
|
+
activeModelName = prov.model;
|
|
2811
|
+
renderInfo(`Connected: ${prov.label} \xB7 ${prov.model}`);
|
|
2812
|
+
};
|
|
2813
|
+
const handleModels = async (arg) => {
|
|
2814
|
+
const cfg = loadConfig() ?? {};
|
|
2815
|
+
const prov = activeProvider(cfg);
|
|
2816
|
+
if (!prov) {
|
|
2817
|
+
renderInfo("No provider configured \u2014 run `thiny init`.");
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
let modelId = arg;
|
|
2821
|
+
if (!modelId) {
|
|
2822
|
+
renderInfo(`
|
|
2823
|
+
Active: ${prov.label} \xB7 current model: ${prov.model}`);
|
|
2824
|
+
renderInfo("Configured providers:");
|
|
2825
|
+
providersOf(cfg).forEach((pr) => {
|
|
2826
|
+
renderInfo(` \u2022 ${pr.label}: ${pr.model}`);
|
|
2827
|
+
});
|
|
2828
|
+
modelId = (await rl.question("New model id for the active provider (blank to cancel): ")).trim();
|
|
2829
|
+
if (!modelId) return;
|
|
2830
|
+
}
|
|
2831
|
+
prov.model = modelId;
|
|
2832
|
+
saveProvider(cfg, prov, true);
|
|
2833
|
+
activeModel = buildModel(prov);
|
|
2834
|
+
activeModelName = modelId;
|
|
2835
|
+
renderInfo(`Model set: ${modelId}`);
|
|
2836
|
+
};
|
|
2837
|
+
const showSlashMenu = () => {
|
|
2838
|
+
renderInfo(
|
|
2839
|
+
"\nCommands: /new \xB7 /connect \xB7 /models \xB7 /tools \xB7 /skills \xB7 /session \xB7 /stats \xB7 /verify <blobId> \xB7 /clear \xB7 /help"
|
|
2840
|
+
);
|
|
2841
|
+
renderInfo(`Tools: ${agent.registry.all().map((t) => t.name).join(", ")}`);
|
|
2842
|
+
const cats = [...defaultRegistry.byCategory()].map(
|
|
2843
|
+
([cat, defs]) => `${cat}(${defs.map((d) => d.id).join(",")})`
|
|
2844
|
+
);
|
|
2845
|
+
renderInfo(`Skills: ${cats.join(" ")}
|
|
2846
|
+
`);
|
|
2847
|
+
};
|
|
2581
2848
|
try {
|
|
2582
2849
|
for (; ; ) {
|
|
2583
2850
|
for (const ref of memoryRefs.splice(0))
|
|
@@ -2591,7 +2858,18 @@ YOUR TOOLS:
|
|
|
2591
2858
|
if (trimmed.startsWith("/")) {
|
|
2592
2859
|
const parts = trimmed.slice(1).split(" ");
|
|
2593
2860
|
const cmd = parts[0];
|
|
2861
|
+
const arg = parts.slice(1).join(" ").trim() || void 0;
|
|
2594
2862
|
switch (cmd) {
|
|
2863
|
+
case "":
|
|
2864
|
+
showSlashMenu();
|
|
2865
|
+
break;
|
|
2866
|
+
case "connect":
|
|
2867
|
+
await handleConnect(arg);
|
|
2868
|
+
break;
|
|
2869
|
+
case "models":
|
|
2870
|
+
case "model":
|
|
2871
|
+
await handleModels(arg);
|
|
2872
|
+
break;
|
|
2595
2873
|
case "new": {
|
|
2596
2874
|
currentSessionId = `cli-${(/* @__PURE__ */ new Date()).getTime().toString()}`;
|
|
2597
2875
|
renderInfo("New session started \u2014 long-term memory carries over");
|
|
@@ -2662,7 +2940,7 @@ Audit trail ${blobId}
|
|
|
2662
2940
|
break;
|
|
2663
2941
|
case "help":
|
|
2664
2942
|
renderInfo(
|
|
2665
|
-
"\n/new \xB7 /tools \xB7 /skills \xB7 /stats \xB7 /session \xB7 /verify <blobId> \xB7 /clear \xB7 /help\n"
|
|
2943
|
+
"\n/new \xB7 /connect \xB7 /models \xB7 /tools \xB7 /skills \xB7 /stats \xB7 /session \xB7 /verify <blobId> \xB7 /clear \xB7 /help\n(type just `/` to see commands + all tools + skills)\n"
|
|
2666
2944
|
);
|
|
2667
2945
|
break;
|
|
2668
2946
|
default:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinyai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Thiny AI — a beautiful terminal agent: interactive chat, tools, Walrus memory, and Sui execution.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,16 +38,16 @@
|
|
|
38
38
|
"tsup": "^8.5.1",
|
|
39
39
|
"typescript": "^5.5.0",
|
|
40
40
|
"@thiny/core": "0.1.0",
|
|
41
|
+
"@thiny/model-aisdk": "0.1.0",
|
|
41
42
|
"@thiny/logger-pino": "0.1.0",
|
|
42
|
-
"@thiny/plugin-agents": "0.1.0",
|
|
43
|
-
"@thiny/memory-memwal": "0.1.0",
|
|
44
43
|
"@thiny/mcp": "0.1.0",
|
|
45
44
|
"@thiny/walrus": "0.1.0",
|
|
46
|
-
"@thiny/
|
|
45
|
+
"@thiny/plugin-agents": "0.1.0",
|
|
47
46
|
"@thiny/plugin-web-search": "0.1.0",
|
|
48
|
-
"@thiny/
|
|
47
|
+
"@thiny/signer-sui": "0.1.0",
|
|
49
48
|
"@thiny/plugin-sui": "0.1.0",
|
|
50
|
-
"@thiny/skills": "0.1.0"
|
|
49
|
+
"@thiny/skills": "0.1.0",
|
|
50
|
+
"@thiny/memory-memwal": "0.1.0"
|
|
51
51
|
},
|
|
52
52
|
"author": "Thiny AI",
|
|
53
53
|
"engines": {
|