@tangle-network/tcloud 0.1.0
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/LICENSE +190 -0
- package/README.md +354 -0
- package/dist/chunk-4AOQNUQ3.js +573 -0
- package/dist/chunk-YORNEPCU.js +49 -0
- package/dist/cli.cjs +895 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +273 -0
- package/dist/index.cjs +654 -0
- package/dist/index.d.cts +47 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +20 -0
- package/dist/shielded-BRhsV-s-.d.cts +343 -0
- package/dist/shielded-BRhsV-s-.d.ts +343 -0
- package/dist/shielded.cjs +608 -0
- package/dist/shielded.d.cts +2 -0
- package/dist/shielded.d.ts +2 -0
- package/dist/shielded.js +12 -0
- package/package.json +82 -0
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
TCloud
|
|
4
|
+
} from "./chunk-YORNEPCU.js";
|
|
5
|
+
import {
|
|
6
|
+
generateWallet
|
|
7
|
+
} from "./chunk-4AOQNUQ3.js";
|
|
8
|
+
|
|
9
|
+
// src/cli.ts
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
import * as readline from "readline";
|
|
14
|
+
var CONFIG_DIR = path.join(process.env.HOME || "~", ".tcloud");
|
|
15
|
+
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
16
|
+
var WALLETS_FILE = path.join(CONFIG_DIR, "wallets.json");
|
|
17
|
+
function ensureDir() {
|
|
18
|
+
if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
19
|
+
}
|
|
20
|
+
function loadConfig() {
|
|
21
|
+
ensureDir();
|
|
22
|
+
if (fs.existsSync(CONFIG_FILE)) return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
|
|
23
|
+
return { apiUrl: "https://api.tangleai.cloud", defaultModel: "gpt-4o-mini", chainId: 3799 };
|
|
24
|
+
}
|
|
25
|
+
function saveConfig(c) {
|
|
26
|
+
ensureDir();
|
|
27
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(c, null, 2), { mode: 384 });
|
|
28
|
+
}
|
|
29
|
+
function loadWallets() {
|
|
30
|
+
ensureDir();
|
|
31
|
+
if (fs.existsSync(WALLETS_FILE)) return JSON.parse(fs.readFileSync(WALLETS_FILE, "utf-8"));
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
function saveWallets(w) {
|
|
35
|
+
ensureDir();
|
|
36
|
+
fs.writeFileSync(WALLETS_FILE, JSON.stringify(w, null, 2), { mode: 384 });
|
|
37
|
+
}
|
|
38
|
+
function getClient(opts) {
|
|
39
|
+
const config = loadConfig();
|
|
40
|
+
if (opts?.private) {
|
|
41
|
+
const wallets = loadWallets();
|
|
42
|
+
if (wallets.length === 0) {
|
|
43
|
+
console.error("No shielded wallets. Run: tcloud wallet generate");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
return TCloud.shielded({ baseURL: `${config.apiUrl}/v1`, wallet: wallets[0] });
|
|
47
|
+
}
|
|
48
|
+
return new TCloud({ baseURL: `${config.apiUrl}/v1`, apiKey: config.apiKey, model: config.defaultModel });
|
|
49
|
+
}
|
|
50
|
+
var program = new Command();
|
|
51
|
+
program.name("tcloud").description("Tangle AI Cloud CLI").version("0.1.0");
|
|
52
|
+
program.command("config").description("View or update configuration").option("--api-url <url>", "API base URL").option("--api-key <key>", "API key").option("--model <model>", "Default model").option("--chain <id>", "Chain ID").action((opts) => {
|
|
53
|
+
const c = loadConfig();
|
|
54
|
+
if (opts.apiUrl) c.apiUrl = opts.apiUrl;
|
|
55
|
+
if (opts.apiKey) c.apiKey = opts.apiKey;
|
|
56
|
+
if (opts.model) c.defaultModel = opts.model;
|
|
57
|
+
if (opts.chain) c.chainId = parseInt(opts.chain);
|
|
58
|
+
saveConfig(c);
|
|
59
|
+
console.log(JSON.stringify(c, null, 2));
|
|
60
|
+
});
|
|
61
|
+
var auth = program.command("auth").description("Authentication");
|
|
62
|
+
auth.command("login").description("Log in via browser (device flow)").action(async () => {
|
|
63
|
+
const config = loadConfig();
|
|
64
|
+
try {
|
|
65
|
+
const res = await fetch(`${config.apiUrl}/api/auth/device`, { method: "POST" });
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
console.error("Auth server error");
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
const d = await res.json();
|
|
71
|
+
console.log(`
|
|
72
|
+
Open: ${d.verification_url}
|
|
73
|
+
Code: ${d.user_code}
|
|
74
|
+
|
|
75
|
+
Waiting...`);
|
|
76
|
+
const deadline = Date.now() + (d.expires_in || 600) * 1e3;
|
|
77
|
+
while (Date.now() < deadline) {
|
|
78
|
+
await new Promise((r2) => setTimeout(r2, (d.interval || 5) * 1e3));
|
|
79
|
+
const r = await fetch(`${config.apiUrl}/api/auth/device/token`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { "Content-Type": "application/json" },
|
|
82
|
+
body: JSON.stringify({ device_code: d.device_code })
|
|
83
|
+
});
|
|
84
|
+
const t = await r.json();
|
|
85
|
+
if (t.access_token) {
|
|
86
|
+
config.apiKey = t.access_token;
|
|
87
|
+
saveConfig(config);
|
|
88
|
+
console.log("\n Authenticated!");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (t.error === "expired_token") {
|
|
92
|
+
console.error("\n Code expired.");
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
process.stdout.write(".");
|
|
96
|
+
}
|
|
97
|
+
console.error("\n Timed out.");
|
|
98
|
+
} catch (e) {
|
|
99
|
+
console.error("Failed:", e.message);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
auth.command("set-key").description("Set API key directly").argument("<key>").action((key) => {
|
|
103
|
+
const c = loadConfig();
|
|
104
|
+
c.apiKey = key;
|
|
105
|
+
saveConfig(c);
|
|
106
|
+
console.log("API key saved.");
|
|
107
|
+
});
|
|
108
|
+
auth.command("status").description("Show auth status").action(() => {
|
|
109
|
+
const c = loadConfig();
|
|
110
|
+
console.log(c.apiKey ? `Authenticated: ${c.apiKey.slice(0, 15)}...` : "Not authenticated");
|
|
111
|
+
const w = loadWallets();
|
|
112
|
+
if (w.length) console.log(`Shielded wallets: ${w.length}`);
|
|
113
|
+
});
|
|
114
|
+
var wallet = program.command("wallet").description("Shielded wallet management");
|
|
115
|
+
wallet.command("generate").description("Generate ephemeral wallet").option("-l, --label <name>").action((opts) => {
|
|
116
|
+
const w = generateWallet();
|
|
117
|
+
const wallets = loadWallets();
|
|
118
|
+
wallets.push({ ...w, label: opts.label, createdAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
119
|
+
saveWallets(wallets);
|
|
120
|
+
console.log(`Wallet generated:`);
|
|
121
|
+
console.log(` Address: ${w.address}`);
|
|
122
|
+
console.log(` Commitment: ${w.commitment}`);
|
|
123
|
+
console.log(` Saved to: ${WALLETS_FILE}`);
|
|
124
|
+
console.log(`
|
|
125
|
+
Fund with: tcloud credits fund`);
|
|
126
|
+
});
|
|
127
|
+
wallet.command("list").description("List wallets").action(() => {
|
|
128
|
+
const wallets = loadWallets();
|
|
129
|
+
if (!wallets.length) {
|
|
130
|
+
console.log("No wallets. Run: tcloud wallet generate");
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
wallets.forEach((w, i) => console.log(` [${i}] ${(w.label || "default").padEnd(15)} ${w.commitment.slice(0, 20)}...`));
|
|
134
|
+
});
|
|
135
|
+
program.command("chat").description("Chat with a model").argument("[message]", "Message (or interactive if omitted)").option("-m, --model <model>", "Model").option("--private", "Use shielded credits (anonymous)").option("--stream", "Stream output", true).action(async (message, opts) => {
|
|
136
|
+
const client = getClient({ private: opts.private });
|
|
137
|
+
const model = opts.model || loadConfig().defaultModel;
|
|
138
|
+
if (!message) {
|
|
139
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
140
|
+
console.log(`tcloud chat \u2014 ${model}${opts.private ? " (private)" : ""}
|
|
141
|
+
Ctrl+C to exit.
|
|
142
|
+
`);
|
|
143
|
+
const ask = () => rl.question("> ", async (input) => {
|
|
144
|
+
if (!input.trim()) {
|
|
145
|
+
ask();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
for await (const chunk of client.askStream(input.trim(), { model })) {
|
|
150
|
+
process.stdout.write(chunk);
|
|
151
|
+
}
|
|
152
|
+
process.stdout.write("\n\n");
|
|
153
|
+
} catch (e) {
|
|
154
|
+
console.error("Error:", e.message);
|
|
155
|
+
}
|
|
156
|
+
ask();
|
|
157
|
+
});
|
|
158
|
+
ask();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
if (opts.stream) {
|
|
163
|
+
for await (const chunk of client.askStream(message, { model })) {
|
|
164
|
+
process.stdout.write(chunk);
|
|
165
|
+
}
|
|
166
|
+
process.stdout.write("\n");
|
|
167
|
+
} else {
|
|
168
|
+
const completion = await client.askFull(message, { model });
|
|
169
|
+
const text = completion.choices[0]?.message?.content || "";
|
|
170
|
+
const usedModel = completion.model || model;
|
|
171
|
+
const usage = completion.usage;
|
|
172
|
+
process.stdout.write(`[${usedModel}] ${text}
|
|
173
|
+
`);
|
|
174
|
+
if (usage) {
|
|
175
|
+
const cost = usage.total_tokens * 1e-6;
|
|
176
|
+
process.stdout.write(` \u21B3 ${usage.total_tokens} tokens \xB7 $${cost.toFixed(6)}
|
|
177
|
+
`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch (e) {
|
|
181
|
+
console.error("Error:", e.message);
|
|
182
|
+
if (e.status === 402) console.error("Add credits: tcloud credits fund");
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
program.command("models").description("List available models").option("-s, --search <query>", "Search").action(async (opts) => {
|
|
186
|
+
const client = getClient();
|
|
187
|
+
try {
|
|
188
|
+
let models = await client.models();
|
|
189
|
+
if (opts.search) {
|
|
190
|
+
const q = opts.search.toLowerCase();
|
|
191
|
+
models = models.filter((m) => m.id.toLowerCase().includes(q) || m.name.toLowerCase().includes(q));
|
|
192
|
+
}
|
|
193
|
+
console.log(`${models.length} models:`);
|
|
194
|
+
models.slice(0, 30).forEach((m) => console.log(` ${m.id.padEnd(40)} ${m.name || ""}`));
|
|
195
|
+
if (models.length > 30) console.log(` ... +${models.length - 30} more`);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
console.error("Error:", e.message);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
program.command("operators").description("List active operators").action(async () => {
|
|
201
|
+
const client = getClient();
|
|
202
|
+
try {
|
|
203
|
+
const { operators, stats } = await client.operators();
|
|
204
|
+
console.log(`${stats.activeOperators} operators, ${stats.totalModels} models:
|
|
205
|
+
`);
|
|
206
|
+
operators.forEach(
|
|
207
|
+
(o) => console.log(` ${o.slug.padEnd(20)} ${o.status.padEnd(10)} ${String(o.models.length).padEnd(3)} models ${o.reputationScore}% rep ${o.avgLatencyMs}ms`)
|
|
208
|
+
);
|
|
209
|
+
} catch (e) {
|
|
210
|
+
console.error("Error:", e.message);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
var credits = program.command("credits").description("Credit management");
|
|
214
|
+
credits.command("balance").description("Check balance").action(async () => {
|
|
215
|
+
const client = getClient();
|
|
216
|
+
try {
|
|
217
|
+
const data = await client.credits();
|
|
218
|
+
console.log(`Balance: $${data.balance.toFixed(4)}`);
|
|
219
|
+
if (data.transactions.length) {
|
|
220
|
+
console.log("\nRecent transactions:");
|
|
221
|
+
data.transactions.slice(0, 5).forEach(
|
|
222
|
+
(t) => console.log(` ${t.amount > 0 ? "+" : ""}$${Math.abs(t.amount).toFixed(4).padEnd(10)} ${t.description}`)
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
} catch (e) {
|
|
226
|
+
console.error("Error:", e.message);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
credits.command("add").description("Add credits").argument("<amount>").action(async (amount) => {
|
|
230
|
+
const client = getClient();
|
|
231
|
+
try {
|
|
232
|
+
const data = await client.addCredits(parseFloat(amount));
|
|
233
|
+
console.log(`Credits added. New balance: $${data.balance.toFixed(4)}`);
|
|
234
|
+
} catch (e) {
|
|
235
|
+
console.error("Error:", e.message);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
credits.command("fund").description("Fund shielded credits from pool").action(() => {
|
|
239
|
+
console.log("Shielded credit funding requires integration with the VAnchor pool.");
|
|
240
|
+
console.log("See: https://docs.tangleai.cloud/privacy/funding");
|
|
241
|
+
});
|
|
242
|
+
var keys = program.command("keys").description("API key management");
|
|
243
|
+
keys.command("create").description("Create API key").argument("<name>").action(async (name) => {
|
|
244
|
+
const config = loadConfig();
|
|
245
|
+
try {
|
|
246
|
+
const res = await fetch(`${config.apiUrl}/api/keys`, {
|
|
247
|
+
method: "POST",
|
|
248
|
+
headers: { "Content-Type": "application/json", ...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {} },
|
|
249
|
+
body: JSON.stringify({ name })
|
|
250
|
+
});
|
|
251
|
+
const data = await res.json();
|
|
252
|
+
if (data.key) {
|
|
253
|
+
console.log(`Key created: ${data.key}
|
|
254
|
+
Save this \u2014 shown once only.`);
|
|
255
|
+
} else console.error("Error:", data.error);
|
|
256
|
+
} catch (e) {
|
|
257
|
+
console.error("Error:", e.message);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
keys.command("list").description("List API keys").action(async () => {
|
|
261
|
+
const config = loadConfig();
|
|
262
|
+
try {
|
|
263
|
+
const res = await fetch(`${config.apiUrl}/api/keys`, {
|
|
264
|
+
headers: config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}
|
|
265
|
+
});
|
|
266
|
+
const data = await res.json();
|
|
267
|
+
if (data.keys?.length) data.keys.forEach((k) => console.log(` ${k.id.slice(0, 8)} ${k.name.padEnd(20)} ${k.keyPrefix}`));
|
|
268
|
+
else console.log("No keys.");
|
|
269
|
+
} catch (e) {
|
|
270
|
+
console.error("Error:", e.message);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
program.parse();
|