thinyai 0.1.0 → 0.1.2
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 +143 -19
- package/dist/bin.js +66 -43
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,38 +1,162 @@
|
|
|
1
|
-
#
|
|
1
|
+
# thinyai
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> A beautiful terminal AI agent — interactive chat, tools, cross-session memory on **Walrus**, and grounded **Sui** execution. The command is `thiny`.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/thinyai)
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
_____ _ _ _ ___
|
|
9
|
+
|_ _| |__ (_)_ __ _ _ / \ |_ _|
|
|
10
|
+
| | | '_ \| | '_ \| | | | / _ \ | |
|
|
11
|
+
| | | | | | | | | | |_| |/ ___ \ | |
|
|
12
|
+
|_| |_| |_|_|_| |_|\__, /_/ \_\___|
|
|
13
|
+
|___/
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
6
17
|
|
|
7
18
|
## Install
|
|
8
19
|
|
|
9
20
|
```bash
|
|
10
|
-
pnpm add -g
|
|
21
|
+
bun add -g thinyai # or: npm i -g thinyai / pnpm add -g thinyai
|
|
11
22
|
```
|
|
12
23
|
|
|
13
|
-
|
|
24
|
+
This installs one command: **`thiny`**. No `.env`, no config files to hand-edit — the first run sets you up.
|
|
14
25
|
|
|
15
26
|
```bash
|
|
16
|
-
#
|
|
17
|
-
|
|
27
|
+
thiny # first launch runs setup, then starts chatting
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Commands
|
|
33
|
+
|
|
34
|
+
| Command | What it does |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `thiny` | Start the interactive agent. Runs first-time setup if needed. |
|
|
37
|
+
| `thiny init` | (Re)run base setup — pick a model + agent name + API key. |
|
|
38
|
+
| `thiny sui init` | Add Sui on-chain capabilities — pick a network + wallet. |
|
|
39
|
+
| `thiny help` | Show all commands. |
|
|
40
|
+
| `thiny --version` | Print the version. |
|
|
41
|
+
|
|
42
|
+
Everything is saved to **`~/.thiny/config.json`** (chmod `0600` — it holds your API key and any Sui key). No `.env` required.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## First run / `thiny init`
|
|
47
|
+
|
|
48
|
+
The first time you run `thiny` (or any time you run `thiny init`), an arrow-key wizard asks:
|
|
49
|
+
|
|
50
|
+
1. **Agent name** — what the assistant calls itself (default `ThinyAI`).
|
|
51
|
+
2. **Model** — pick from the list, or **Custom** for any OpenAI-compatible endpoint:
|
|
52
|
+
|
|
53
|
+
| Choice | Notes |
|
|
54
|
+
|---|---|
|
|
55
|
+
| OpenAI · gpt-4o-mini | fast, cheap |
|
|
56
|
+
| OpenAI · gpt-4o | |
|
|
57
|
+
| Anthropic · claude-haiku-4-5 | |
|
|
58
|
+
| Anthropic · claude-sonnet-4-6 | |
|
|
59
|
+
| Ollama | local, no key (`http://localhost:11434/v1`) |
|
|
60
|
+
| Custom | enter model id + base URL + key — works with Groq, Together, OpenRouter, LM Studio, vLLM, Mimo, Azure, … |
|
|
61
|
+
|
|
62
|
+
3. **API key** — pasted securely (masked); skipped for keyless setups like Ollama.
|
|
63
|
+
|
|
64
|
+
That's it — `thiny` starts.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Sui setup / `thiny sui init`
|
|
69
|
+
|
|
70
|
+
Adds grounded, capped on-chain execution. If you haven't run base setup yet, this runs it first.
|
|
71
|
+
|
|
72
|
+
1. **Network** — `Testnet` (recommended) or `Mainnet`. You can change this later by re-running `thiny sui init`.
|
|
73
|
+
2. **Wallet** — choose one:
|
|
74
|
+
|
|
75
|
+
| Option | What happens |
|
|
76
|
+
|---|---|
|
|
77
|
+
| **Paste an existing private key** | Enter a `suiprivkey…` key (masked). |
|
|
78
|
+
| **Generate a new key pair locally** | A fresh Ed25519 key is created and stored in your config. |
|
|
79
|
+
| **Agent wallet (Rill)** | Generates a signer key and stores your per-user Rill MCP URL for grounded execution. |
|
|
80
|
+
|
|
81
|
+
After setup it prints your **address** — you must **fund it** before sending transactions:
|
|
18
82
|
|
|
19
|
-
|
|
20
|
-
|
|
83
|
+
```text
|
|
84
|
+
⚠ Fund this address (testnet) before sending transactions
|
|
85
|
+
0xabc…
|
|
86
|
+
Faucet: https://faucet.sui.io (or `sui client faucet`)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
> Mainnet is gated: the signer refuses to sign on mainnet unless explicitly enabled, and per-tx / budget / expiry caps are enforced **on-chain** by the agent-wallet contract — the local policy is only a soft UX layer.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## In-chat commands
|
|
94
|
+
|
|
95
|
+
While chatting, type a slash command:
|
|
96
|
+
|
|
97
|
+
| Command | Description |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `/new` | Start a fresh session (long-term memory carries over). |
|
|
100
|
+
| `/tools` | List available tools. |
|
|
101
|
+
| `/skills` | List available skills. |
|
|
102
|
+
| `/session` | Show the current session id. |
|
|
103
|
+
| `/stats` | Session totals (turns, tokens, tool calls). |
|
|
104
|
+
| `/verify <blobId>` | Re-fetch and replay a stored Walrus audit trail. |
|
|
105
|
+
| `/clear` | Clear the screen. |
|
|
106
|
+
| `/help` | Show these commands. |
|
|
21
107
|
|
|
22
|
-
|
|
23
|
-
|
|
108
|
+
Press **Ctrl-D** (or Ctrl-C) to exit.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Memory (Walrus)
|
|
113
|
+
|
|
114
|
+
Thiny remembers across sessions. Durable facts about you are stored on **Walrus** (content-addressed, verifiable, portable — not locked to one machine) and auto-injected at the start of each conversation. When a fact is saved you'll see a compact, verifiable line:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
✓ memory saved on Walrus · https://walruscan.com/testnet/blob/…
|
|
24
118
|
```
|
|
25
119
|
|
|
26
|
-
|
|
120
|
+
Writes are non-blocking — you can keep chatting while they upload. Set `MEMWAL_*` env vars to use MemWal (semantic memory) instead.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Output
|
|
125
|
+
|
|
126
|
+
Responses render as **markdown** in the terminal — bold, italics, `inline code`, lists, headings, blockquotes, fenced code blocks, and clickable `[links](url)`. Model reasoning (`<think>…</think>`) streams dimmed.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Config reference
|
|
131
|
+
|
|
132
|
+
`~/.thiny/config.json`:
|
|
133
|
+
|
|
134
|
+
```jsonc
|
|
135
|
+
{
|
|
136
|
+
"agentName": "ThinyAI",
|
|
137
|
+
"userId": "default",
|
|
138
|
+
"model": "openai:gpt-4o-mini", // or "anthropic:…", or a bare id with baseUrl
|
|
139
|
+
"baseUrl": "https://…/v1", // optional — for OpenAI-compatible endpoints
|
|
140
|
+
"apiKey": "sk-…",
|
|
141
|
+
"sui": { // present after `thiny sui init`
|
|
142
|
+
"network": "testnet",
|
|
143
|
+
"address": "0x…",
|
|
144
|
+
"wallet": { "type": "generated", "secretKey": "suiprivkey…" },
|
|
145
|
+
"rillMcpUrl": "https://…" // optional — Rill agent wallet
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Environment variables override the config when set (handy for CI/dev): `THINY_MODEL`, `THINY_OPENAI_API_KEY`, `THINY_ANTHROPIC_API_KEY`, `THINY_OPENAI_BASE_URL`, `THINY_PERSONA_NAME`, `THINY_USER_ID`, `SUI_NETWORK`, `SUI_SECRET_KEY`, `MCP_URL`.
|
|
151
|
+
|
|
152
|
+
---
|
|
27
153
|
|
|
28
|
-
|
|
29
|
-
- Streaming SSE responses
|
|
30
|
-
- Multi-session support
|
|
31
|
-
- Tool call visualization
|
|
32
|
-
- EVM + Solana wallet integration
|
|
154
|
+
## Troubleshooting
|
|
33
155
|
|
|
34
|
-
|
|
156
|
+
- **`(model returned empty response)` / API error** — wrong model id, base URL, or key. Re-run `thiny init`, or edit `~/.thiny/config.json`. The CLI prints the underlying provider error.
|
|
157
|
+
- **Transactions fail** — make sure the address from `thiny sui init` is funded.
|
|
158
|
+
- **Logs** — written to `~/.thiny/cli.log` (never to the chat). Tail with `tail -f ~/.thiny/cli.log`.
|
|
35
159
|
|
|
36
160
|
---
|
|
37
161
|
|
|
38
|
-
*Part of the [Thiny
|
|
162
|
+
*Part of the [Thiny](https://github.com/ESES-Labs/thinyai-walrus) framework — a tiny, hexagonal agent kernel for Web2 + Web3.*
|
package/dist/bin.js
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
4
|
import { createInterface } from "readline/promises";
|
|
5
|
-
import { clearLine, cursorTo } from "readline";
|
|
5
|
+
import { clearLine, cursorTo, emitKeypressEvents } from "readline";
|
|
6
6
|
import { stdin, stdout } from "process";
|
|
7
|
+
import { mkdirSync } from "fs";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { join } from "path";
|
|
7
10
|
import { z as z4 } from "zod";
|
|
8
11
|
|
|
9
12
|
// ../../packages/core/src/domain/messages.ts
|
|
@@ -449,9 +452,9 @@ async function createAgent(config) {
|
|
|
449
452
|
const seed = config.systemPrompt && !history.some((m) => m.role === "system") ? [systemMessage(config.systemPrompt), ...history] : history;
|
|
450
453
|
const generate = composeModel(extensions.middleware.model, async (req) => {
|
|
451
454
|
if (opts.onToken && config.model.stream) {
|
|
452
|
-
return assembleStream(config.model.stream(req.messages, req.tools), opts.onToken);
|
|
455
|
+
return assembleStream(config.model.stream(req.messages, req.tools, opts.signal), opts.onToken);
|
|
453
456
|
}
|
|
454
|
-
return config.model.generate(req.messages, req.tools);
|
|
457
|
+
return config.model.generate(req.messages, req.tools, opts.signal);
|
|
455
458
|
});
|
|
456
459
|
const runComposedTool = composeTool(
|
|
457
460
|
extensions.middleware.tool,
|
|
@@ -826,44 +829,38 @@ function buildToolOptions(tools) {
|
|
|
826
829
|
if (tools.length === 0) return { tools: void 0, toolChoice: void 0 };
|
|
827
830
|
return { tools: toAiTools(tools), toolChoice: "auto" };
|
|
828
831
|
}
|
|
832
|
+
var KNOWN_PROVIDERS = /* @__PURE__ */ new Set(["openai", "openai-compat", "anthropic"]);
|
|
829
833
|
function resolveModel(model, opts) {
|
|
830
834
|
if (typeof model !== "string") return model;
|
|
831
835
|
const colonIdx = model.indexOf(":");
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
+
const prefix = colonIdx === -1 ? "" : model.slice(0, colonIdx);
|
|
837
|
+
if (KNOWN_PROVIDERS.has(prefix)) {
|
|
838
|
+
const modelId = model.slice(colonIdx + 1);
|
|
839
|
+
if (prefix === "anthropic") {
|
|
840
|
+
return createAnthropic({ baseURL: opts.anthropic?.baseURL, apiKey: opts.anthropic?.apiKey })(
|
|
841
|
+
modelId
|
|
836
842
|
);
|
|
837
843
|
}
|
|
838
|
-
return createOpenAI({ baseURL: opts.openai?.baseURL, apiKey: opts.openai?.apiKey })(model);
|
|
839
|
-
}
|
|
840
|
-
const provider = model.slice(0, colonIdx);
|
|
841
|
-
const modelId = model.slice(colonIdx + 1);
|
|
842
|
-
if (provider === "openai" || provider === "openai-compat") {
|
|
843
844
|
return createOpenAI({ baseURL: opts.openai?.baseURL, apiKey: opts.openai?.apiKey })(modelId);
|
|
844
845
|
}
|
|
845
|
-
if (
|
|
846
|
-
return createAnthropic({ baseURL: opts.anthropic
|
|
847
|
-
|
|
846
|
+
if (opts.anthropic?.baseURL) {
|
|
847
|
+
return createAnthropic({ baseURL: opts.anthropic.baseURL, apiKey: opts.anthropic.apiKey })(
|
|
848
|
+
model
|
|
848
849
|
);
|
|
849
850
|
}
|
|
850
|
-
|
|
851
|
-
`unknown provider "${provider}" in model string "${model}"
|
|
852
|
-
Supported prefixes: "openai:<id>", "openai-compat:<id>", "anthropic:<id>"
|
|
853
|
-
Or omit the prefix and set THINY_OPENAI_BASE_URL / THINY_ANTHROPIC_BASE_URL instead.
|
|
854
|
-
Or pass a LanguageModel instance directly.`
|
|
855
|
-
);
|
|
851
|
+
return createOpenAI({ baseURL: opts.openai?.baseURL, apiKey: opts.openai?.apiKey })(model);
|
|
856
852
|
}
|
|
857
853
|
function aiSdkModel(opts) {
|
|
858
854
|
const model = resolveModel(opts.model, opts);
|
|
859
855
|
const maxRetries = opts.maxRetries ?? 2;
|
|
860
856
|
return {
|
|
861
|
-
async generate(messages, tools) {
|
|
857
|
+
async generate(messages, tools, signal) {
|
|
862
858
|
const result = await generateText({
|
|
863
859
|
model,
|
|
864
860
|
messages: toCoreMessages(messages),
|
|
865
861
|
...buildToolOptions(tools),
|
|
866
|
-
maxRetries
|
|
862
|
+
maxRetries,
|
|
863
|
+
abortSignal: signal
|
|
867
864
|
});
|
|
868
865
|
return {
|
|
869
866
|
text: result.text || void 0,
|
|
@@ -877,12 +874,13 @@ function aiSdkModel(opts) {
|
|
|
877
874
|
usage: normalizeUsage(result.usage)
|
|
878
875
|
};
|
|
879
876
|
},
|
|
880
|
-
async *stream(messages, tools) {
|
|
877
|
+
async *stream(messages, tools, signal) {
|
|
881
878
|
const result = streamText({
|
|
882
879
|
model,
|
|
883
880
|
messages: toCoreMessages(messages),
|
|
884
881
|
...buildToolOptions(tools),
|
|
885
|
-
maxRetries
|
|
882
|
+
maxRetries,
|
|
883
|
+
abortSignal: signal
|
|
886
884
|
});
|
|
887
885
|
for await (const part of result.fullStream) {
|
|
888
886
|
if (part.type === "text-delta") {
|
|
@@ -953,7 +951,7 @@ function pinoLogger(opts = {}) {
|
|
|
953
951
|
);
|
|
954
952
|
}
|
|
955
953
|
if (opts.file) {
|
|
956
|
-
const destination = pino2.destination({ dest: opts.file, sync:
|
|
954
|
+
const destination = pino2.destination({ dest: opts.file, sync: true });
|
|
957
955
|
return adaptPinoLogger(pino2(pinoConfig(level), destination));
|
|
958
956
|
}
|
|
959
957
|
const usePretty = opts.pretty ?? (opts.file === void 0 && process.env.NODE_ENV !== "production");
|
|
@@ -1792,8 +1790,10 @@ function parseSkillArgs() {
|
|
|
1792
1790
|
}
|
|
1793
1791
|
var currentSessionId = `cli-${(/* @__PURE__ */ new Date()).getTime().toString()}`;
|
|
1794
1792
|
async function runCli() {
|
|
1793
|
+
const thinyDir = join(homedir(), ".thiny");
|
|
1794
|
+
mkdirSync(thinyDir, { recursive: true });
|
|
1795
1795
|
const envLogFile = process.env.THINY_LOG_FILE?.trim();
|
|
1796
|
-
const logFile = envLogFile && envLogFile.length > 0 ? envLogFile :
|
|
1796
|
+
const logFile = envLogFile && envLogFile.length > 0 ? envLogFile : join(thinyDir, "cli.log");
|
|
1797
1797
|
const fileLogger = pinoLogger({ level: process.env.LOG_LEVEL ?? "info", file: logFile });
|
|
1798
1798
|
const turn = { inputTokens: 0, outputTokens: 0, toolCalls: 0, modelCalls: 0 };
|
|
1799
1799
|
const session = { inputTokens: 0, outputTokens: 0, toolCalls: 0, turns: 0 };
|
|
@@ -1822,7 +1822,9 @@ async function runCli() {
|
|
|
1822
1822
|
namespace: process.env.MEMWAL_NAMESPACE ?? userId
|
|
1823
1823
|
}) : walrusMemoryPlugin({
|
|
1824
1824
|
client: walrus,
|
|
1825
|
-
|
|
1825
|
+
// Stable per-user location (~/.thiny) so cross-session memory works no matter which
|
|
1826
|
+
// directory `thiny` is launched from — a cwd-relative file would fragment per folder.
|
|
1827
|
+
pointers: filePointerStore(process.env.WALRUS_POINTERS ?? join(thinyDir, "thiny-pointers.json")),
|
|
1826
1828
|
userId,
|
|
1827
1829
|
onStoreStart: () => pendingWrites += 1,
|
|
1828
1830
|
onStore: (ref) => {
|
|
@@ -1894,6 +1896,7 @@ async function runCli() {
|
|
|
1894
1896
|
if (walrusAudit)
|
|
1895
1897
|
renderInfo(`Walrus audit: ON (${network}) \u2014 each turn's action log is stored + verifiable`);
|
|
1896
1898
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
1899
|
+
emitKeypressEvents(stdin);
|
|
1897
1900
|
const spinner = new Spinner();
|
|
1898
1901
|
const flushMemory = memoryPlugin.flush;
|
|
1899
1902
|
const PROMPT = "\x1B[36mYou\x1B[0m \x1B[2m\u203A\x1B[0m ";
|
|
@@ -2006,10 +2009,15 @@ Audit trail ${blobId}
|
|
|
2006
2009
|
continue;
|
|
2007
2010
|
}
|
|
2008
2011
|
renderAgentLabel(personaName);
|
|
2009
|
-
spinner.start("thinking\u2026");
|
|
2012
|
+
spinner.start("thinking\u2026 (esc to cancel)");
|
|
2010
2013
|
budget.reset();
|
|
2011
2014
|
resetTurn(turn);
|
|
2012
2015
|
const startedAt = Date.now();
|
|
2016
|
+
const ac = new AbortController();
|
|
2017
|
+
const onKey = (_s, key) => {
|
|
2018
|
+
if (key?.name === "escape") ac.abort();
|
|
2019
|
+
};
|
|
2020
|
+
stdin.on("keypress", onKey);
|
|
2013
2021
|
try {
|
|
2014
2022
|
let firstToken = true;
|
|
2015
2023
|
const stream = createMarkdownWriter((s) => stdout.write(s));
|
|
@@ -2021,15 +2029,28 @@ Audit trail ${blobId}
|
|
|
2021
2029
|
spinner.start("running\u2026");
|
|
2022
2030
|
};
|
|
2023
2031
|
agent.events.on("beforeToolCall", toolHandler);
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2032
|
+
let reply;
|
|
2033
|
+
try {
|
|
2034
|
+
reply = await agent.run(trimmed, {
|
|
2035
|
+
sessionId: currentSessionId,
|
|
2036
|
+
signal: ac.signal,
|
|
2037
|
+
onToken: (delta) => {
|
|
2038
|
+
spinner.stop();
|
|
2039
|
+
firstToken = false;
|
|
2040
|
+
stream.push(delta);
|
|
2041
|
+
}
|
|
2042
|
+
});
|
|
2043
|
+
} catch (err) {
|
|
2044
|
+
if (ac.signal.aborted) {
|
|
2027
2045
|
spinner.stop();
|
|
2028
|
-
|
|
2029
|
-
|
|
2046
|
+
stream.end();
|
|
2047
|
+
stdout.write("\n \x1B[2m\u2298 cancelled (Esc)\x1B[0m\n");
|
|
2048
|
+
continue;
|
|
2030
2049
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2050
|
+
throw err;
|
|
2051
|
+
} finally {
|
|
2052
|
+
agent.events.off("beforeToolCall", toolHandler);
|
|
2053
|
+
}
|
|
2033
2054
|
spinner.stop();
|
|
2034
2055
|
if (firstToken) {
|
|
2035
2056
|
stream.push(reply.length > 0 ? reply : "\x1B[2m(model returned empty response)\x1B[0m");
|
|
@@ -2074,6 +2095,8 @@ Audit trail ${blobId}
|
|
|
2074
2095
|
looksLikeModelError ? `${msg}
|
|
2075
2096
|
\u21B3 Check your model, base URL, and API key (run \`thiny init\`, or edit ~/.thiny/config.json / .env).` : msg
|
|
2076
2097
|
);
|
|
2098
|
+
} finally {
|
|
2099
|
+
stdin.off("keypress", onKey);
|
|
2077
2100
|
}
|
|
2078
2101
|
}
|
|
2079
2102
|
} finally {
|
|
@@ -2082,16 +2105,16 @@ Audit trail ${blobId}
|
|
|
2082
2105
|
}
|
|
2083
2106
|
|
|
2084
2107
|
// src/onboarding.ts
|
|
2085
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync, chmodSync } from "fs";
|
|
2086
|
-
import { homedir } from "os";
|
|
2087
|
-
import { join, dirname as dirname2 } from "path";
|
|
2108
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2, chmodSync } from "fs";
|
|
2109
|
+
import { homedir as homedir2 } from "os";
|
|
2110
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
2088
2111
|
import { fileURLToPath } from "url";
|
|
2089
2112
|
import * as p from "@clack/prompts";
|
|
2090
|
-
var THINY_DIR =
|
|
2091
|
-
var CONFIG =
|
|
2113
|
+
var THINY_DIR = join2(homedir2(), ".thiny");
|
|
2114
|
+
var CONFIG = join2(THINY_DIR, "config.json");
|
|
2092
2115
|
function version() {
|
|
2093
2116
|
try {
|
|
2094
|
-
const pkg =
|
|
2117
|
+
const pkg = join2(dirname2(fileURLToPath(import.meta.url)), "../package.json");
|
|
2095
2118
|
return JSON.parse(readFileSync2(pkg, "utf8")).version ?? "0.0.0";
|
|
2096
2119
|
} catch {
|
|
2097
2120
|
return "0.0.0";
|
|
@@ -2101,7 +2124,7 @@ function loadConfig() {
|
|
|
2101
2124
|
return existsSync2(CONFIG) ? JSON.parse(readFileSync2(CONFIG, "utf8")) : null;
|
|
2102
2125
|
}
|
|
2103
2126
|
function saveConfig(cfg) {
|
|
2104
|
-
|
|
2127
|
+
mkdirSync2(THINY_DIR, { recursive: true });
|
|
2105
2128
|
chmodSync(THINY_DIR, 448);
|
|
2106
2129
|
writeFileSync(CONFIG, JSON.stringify(cfg, null, 2));
|
|
2107
2130
|
chmodSync(CONFIG, 384);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinyai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"tsup": "^8.5.1",
|
|
38
38
|
"typescript": "^5.5.0",
|
|
39
|
-
"@thiny/core": "0.1.0",
|
|
40
|
-
"@thiny/mcp": "0.1.0",
|
|
41
|
-
"@thiny/model-aisdk": "0.1.0",
|
|
42
39
|
"@thiny/logger-pino": "0.1.0",
|
|
40
|
+
"@thiny/model-aisdk": "0.1.0",
|
|
41
|
+
"@thiny/core": "0.1.0",
|
|
43
42
|
"@thiny/memory-memwal": "0.1.0",
|
|
44
|
-
"@thiny/
|
|
43
|
+
"@thiny/mcp": "0.1.0",
|
|
44
|
+
"@thiny/plugin-agents": "0.1.0",
|
|
45
45
|
"@thiny/skills": "0.1.0",
|
|
46
|
-
"@thiny/
|
|
46
|
+
"@thiny/walrus": "0.1.0"
|
|
47
47
|
},
|
|
48
48
|
"author": "Thiny AI",
|
|
49
49
|
"engines": {
|