@ottocode/openclaw-setu 0.1.2 → 0.1.4

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/src/cli.ts DELETED
@@ -1,272 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import {
4
- loadWallet,
5
- saveWallet,
6
- ensureWallet,
7
- exportWalletKey,
8
- getWalletKeyPath,
9
- getSetuBalance,
10
- } from "./wallet.ts";
11
- import { injectConfig, removeConfig, isConfigured, getConfigPath } from "./config.ts";
12
- import { createProxy } from "./proxy.ts";
13
- import { isValidPrivateKey } from "@ottocode/ai-sdk";
14
- import * as readline from "node:readline";
15
-
16
- function prompt(question: string): Promise<string> {
17
- const rl = readline.createInterface({
18
- input: process.stdin,
19
- output: process.stdout,
20
- });
21
- return new Promise((resolve) => {
22
- rl.question(question, (answer) => {
23
- rl.close();
24
- resolve(answer.trim());
25
- });
26
- });
27
- }
28
-
29
- function printHelp() {
30
- console.log(`
31
- openclaw-setu — Pay for AI with Solana USDC
32
-
33
- Usage:
34
- openclaw-setu setup Interactive setup (wallet + config)
35
- openclaw-setu start Start the local proxy server
36
- openclaw-setu stop Stop (placeholder — use Ctrl+C)
37
-
38
- openclaw-setu wallet generate Generate a new Solana wallet
39
- openclaw-setu wallet import Import an existing private key
40
- openclaw-setu wallet export Export your private key
41
- openclaw-setu wallet info Show wallet address and balances
42
-
43
- openclaw-setu config inject Inject Setu provider into openclaw.json
44
- openclaw-setu config remove Remove Setu provider from openclaw.json
45
- openclaw-setu config status Check if Setu is configured
46
-
47
- openclaw-setu help Show this help
48
- `);
49
- }
50
-
51
- async function cmdSetup() {
52
- console.log("\n Setu — Pay for AI with Solana USDC\n");
53
-
54
- const existing = loadWallet();
55
- let wallet = existing;
56
-
57
- if (existing) {
58
- console.log(` Existing wallet found: ${existing.publicKey}`);
59
- const choice = await prompt(" Use existing wallet? (Y/n): ");
60
- if (choice.toLowerCase() === "n") {
61
- const action = await prompt(" (g)enerate new or (i)mport existing? ");
62
- if (action.toLowerCase() === "i") {
63
- const key = await prompt(" Enter Solana private key (base58): ");
64
- if (!isValidPrivateKey(key)) {
65
- console.error(" Invalid private key.");
66
- process.exit(1);
67
- }
68
- wallet = saveWallet(key);
69
- console.log(` Wallet imported: ${wallet.publicKey}`);
70
- } else {
71
- wallet = ensureWallet();
72
- console.log(` New wallet generated: ${wallet.publicKey}`);
73
- }
74
- }
75
- } else {
76
- const action = await prompt(
77
- " No wallet found. (g)enerate new or (i)mport existing? ",
78
- );
79
- if (action.toLowerCase() === "i") {
80
- const key = await prompt(" Enter Solana private key (base58): ");
81
- if (!isValidPrivateKey(key)) {
82
- console.error(" Invalid private key.");
83
- process.exit(1);
84
- }
85
- wallet = saveWallet(key);
86
- console.log(` Wallet imported: ${wallet.publicKey}`);
87
- } else {
88
- wallet = ensureWallet();
89
- console.log(` New wallet generated: ${wallet.publicKey}`);
90
- }
91
- }
92
-
93
- if (!wallet) {
94
- console.error(" Failed to set up wallet.");
95
- process.exit(1);
96
- }
97
-
98
- console.log(`\n Wallet: ${wallet.publicKey}`);
99
- console.log(` Key stored at: ${getWalletKeyPath()}`);
100
-
101
- await injectConfig();
102
- console.log(` OpenClaw config updated: ${getConfigPath()}`);
103
-
104
- console.log(`
105
- Setup complete!
106
-
107
- Next steps:
108
- 1. Fund your wallet with USDC on Solana:
109
- ${wallet.publicKey}
110
-
111
- 2. Start the proxy:
112
- openclaw-setu start
113
-
114
- 3. Restart OpenClaw:
115
- openclaw gateway restart
116
-
117
- Your wallet address is your identity — no API keys needed.
118
- `);
119
- }
120
-
121
- async function cmdWalletGenerate() {
122
- const existing = loadWallet();
123
- if (existing) {
124
- const choice = await prompt(
125
- `Wallet exists (${existing.publicKey}). Overwrite? (y/N): `,
126
- );
127
- if (choice.toLowerCase() !== "y") {
128
- console.log("Cancelled.");
129
- return;
130
- }
131
- }
132
- const wallet = ensureWallet();
133
- console.log(`Wallet generated: ${wallet.publicKey}`);
134
- console.log(`Key stored at: ${getWalletKeyPath()}`);
135
- console.log(`\nFund with USDC on Solana: ${wallet.publicKey}`);
136
- }
137
-
138
- async function cmdWalletImport() {
139
- const key = await prompt("Enter Solana private key (base58): ");
140
- if (!isValidPrivateKey(key)) {
141
- console.error("Invalid private key.");
142
- process.exit(1);
143
- }
144
- const wallet = saveWallet(key);
145
- console.log(`Wallet imported: ${wallet.publicKey}`);
146
- }
147
-
148
- function cmdWalletExport() {
149
- const key = exportWalletKey();
150
- if (!key) {
151
- console.error("No wallet found. Run `openclaw-setu setup` first.");
152
- process.exit(1);
153
- }
154
- console.log(key);
155
- }
156
-
157
- async function cmdWalletInfo() {
158
- const wallet = loadWallet();
159
- if (!wallet) {
160
- console.error("No wallet found. Run `openclaw-setu setup` first.");
161
- process.exit(1);
162
- }
163
-
164
- console.log(`\nWallet: ${wallet.publicKey}`);
165
- console.log(`Key path: ${getWalletKeyPath()}`);
166
-
167
- console.log("\nFetching balances...");
168
- const balances = await getSetuBalance(wallet.privateKey);
169
-
170
- if (balances.setu) {
171
- console.log(`\nSetu Balance: $${balances.setu.balance.toFixed(4)}`);
172
- console.log(`Total Spent: $${balances.setu.totalSpent.toFixed(4)}`);
173
- console.log(`Requests: ${balances.setu.requestCount}`);
174
- } else {
175
- console.log("\nSetu Balance: (not available — wallet may not be registered yet)");
176
- }
177
-
178
- if (balances.wallet) {
179
- console.log(
180
- `\nOn-chain USDC: $${balances.wallet.usdcBalance.toFixed(4)} (${balances.wallet.network})`,
181
- );
182
- }
183
- }
184
-
185
- async function cmdConfigInject() {
186
- await injectConfig();
187
- console.log(`Setu provider injected into ${getConfigPath()}`);
188
- }
189
-
190
- function cmdConfigRemove() {
191
- removeConfig();
192
- console.log(`Setu provider removed from ${getConfigPath()}`);
193
- }
194
-
195
- function cmdConfigStatus() {
196
- if (isConfigured()) {
197
- console.log(`Setu is configured in ${getConfigPath()}`);
198
- } else {
199
- console.log("Setu is not configured. Run `openclaw-setu setup`.");
200
- }
201
- }
202
-
203
- function cmdStart() {
204
- const port = parseInt(process.env.SETU_PROXY_PORT ?? "8403", 10);
205
- const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
206
-
207
- try {
208
- const { wallet } = createProxy({ port, verbose });
209
- console.log(`\nSetu proxy running on http://localhost:${port}`);
210
- console.log(`Wallet: ${wallet.publicKey}`);
211
- console.log(`\nPress Ctrl+C to stop.\n`);
212
- } catch (err) {
213
- console.error((err as Error).message);
214
- process.exit(1);
215
- }
216
- }
217
-
218
- const [cmd, sub] = process.argv.slice(2);
219
-
220
- switch (cmd) {
221
- case "setup":
222
- cmdSetup();
223
- break;
224
- case "start":
225
- cmdStart();
226
- break;
227
- case "wallet":
228
- switch (sub) {
229
- case "generate":
230
- cmdWalletGenerate();
231
- break;
232
- case "import":
233
- cmdWalletImport();
234
- break;
235
- case "export":
236
- cmdWalletExport();
237
- break;
238
- case "info":
239
- cmdWalletInfo();
240
- break;
241
- default:
242
- console.error('Unknown wallet command. Use: generate, import, export, info');
243
- process.exit(1);
244
- }
245
- break;
246
- case "config":
247
- switch (sub) {
248
- case "inject":
249
- cmdConfigInject();
250
- break;
251
- case "remove":
252
- cmdConfigRemove();
253
- break;
254
- case "status":
255
- cmdConfigStatus();
256
- break;
257
- default:
258
- console.error("Unknown config command. Use: inject, remove, status");
259
- process.exit(1);
260
- }
261
- break;
262
- case "help":
263
- case "--help":
264
- case "-h":
265
- case undefined:
266
- printHelp();
267
- break;
268
- default:
269
- console.error(`Unknown command: ${cmd}`);
270
- printHelp();
271
- process.exit(1);
272
- }
package/src/config.ts DELETED
@@ -1,270 +0,0 @@
1
- import { readFileSync, writeFileSync, existsSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { homedir } from "node:os";
4
- import type { ModelApi } from "./types.ts";
5
-
6
- const OPENCLAW_DIR = join(homedir(), ".openclaw");
7
- const OPENCLAW_CONFIG_PATH = join(OPENCLAW_DIR, "openclaw.json");
8
-
9
- const PROVIDER_KEY = "setu";
10
- const DEFAULT_PROXY_PORT = 8403;
11
- const DEFAULT_BASE_URL = "https://api.setu.ottocode.io";
12
-
13
- export interface SetuModelConfig {
14
- id: string;
15
- name: string;
16
- api?: ModelApi;
17
- reasoning?: boolean;
18
- input?: Array<"text" | "image">;
19
- contextWindow?: number;
20
- maxTokens?: number;
21
- }
22
-
23
- export interface SetuProviderConfig {
24
- baseUrl: string;
25
- apiKey: string;
26
- api: ModelApi;
27
- authHeader: boolean;
28
- models: SetuModelConfig[];
29
- }
30
-
31
- function readOpenClawConfig(): Record<string, unknown> {
32
- if (!existsSync(OPENCLAW_CONFIG_PATH)) return {};
33
- try {
34
- return JSON.parse(readFileSync(OPENCLAW_CONFIG_PATH, "utf-8"));
35
- } catch {
36
- return {};
37
- }
38
- }
39
-
40
- function writeOpenClawConfig(config: Record<string, unknown>): void {
41
- writeFileSync(OPENCLAW_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
42
- }
43
-
44
- interface CatalogModel {
45
- id: string;
46
- owned_by: string;
47
- context_length: number;
48
- max_output: number;
49
- capabilities?: { tool_call?: boolean; reasoning?: boolean };
50
- }
51
-
52
- function apiForOwner(owner: string): ModelApi {
53
- switch (owner) {
54
- case "anthropic":
55
- return "anthropic-messages";
56
- case "google":
57
- return "google-generative-ai";
58
- default:
59
- return "openai-completions";
60
- }
61
- }
62
-
63
- function displayName(id: string, owner: string): string {
64
- return `${id} (${owner}, via Setu)`;
65
- }
66
-
67
- export async function fetchModelsFromCatalog(
68
- baseURL: string = DEFAULT_BASE_URL,
69
- ): Promise<SetuModelConfig[]> {
70
- try {
71
- const resp = await fetch(`${baseURL}/v1/models`);
72
- if (!resp.ok) return getDefaultModels();
73
- const data = (await resp.json()) as { data: CatalogModel[] };
74
- return data.data.map((m) => ({
75
- id: m.id,
76
- name: displayName(m.id, m.owned_by),
77
- api: apiForOwner(m.owned_by),
78
- reasoning: m.capabilities?.reasoning ?? false,
79
- input: ["text"] as Array<"text" | "image">,
80
- contextWindow: m.context_length,
81
- maxTokens: m.max_output,
82
- }));
83
- } catch {
84
- return getDefaultModels();
85
- }
86
- }
87
-
88
- export function getDefaultModels(): SetuModelConfig[] {
89
- return [
90
- {
91
- id: "claude-sonnet-4-6",
92
- name: "Claude Sonnet 4.6 (anthropic, via Setu)",
93
- api: "anthropic-messages",
94
- reasoning: true,
95
- input: ["text", "image"],
96
- contextWindow: 200000,
97
- maxTokens: 64000,
98
- },
99
- {
100
- id: "claude-sonnet-4-5",
101
- name: "Claude Sonnet 4.5 (anthropic, via Setu)",
102
- api: "anthropic-messages",
103
- reasoning: true,
104
- input: ["text", "image"],
105
- contextWindow: 200000,
106
- maxTokens: 64000,
107
- },
108
- {
109
- id: "claude-opus-4-6",
110
- name: "Claude Opus 4.6 (anthropic, via Setu)",
111
- api: "anthropic-messages",
112
- reasoning: true,
113
- input: ["text", "image"],
114
- contextWindow: 200000,
115
- maxTokens: 128000,
116
- },
117
- {
118
- id: "claude-3-5-haiku-20241022",
119
- name: "Claude 3.5 Haiku (anthropic, via Setu)",
120
- api: "anthropic-messages",
121
- reasoning: false,
122
- input: ["text", "image"],
123
- contextWindow: 200000,
124
- maxTokens: 8192,
125
- },
126
- {
127
- id: "gpt-5.1-codex",
128
- name: "GPT-5.1 Codex (openai, via Setu)",
129
- api: "openai-completions",
130
- reasoning: true,
131
- input: ["text", "image"],
132
- contextWindow: 400000,
133
- maxTokens: 128000,
134
- },
135
- {
136
- id: "gpt-5",
137
- name: "GPT-5 (openai, via Setu)",
138
- api: "openai-completions",
139
- reasoning: true,
140
- input: ["text", "image"],
141
- contextWindow: 400000,
142
- maxTokens: 128000,
143
- },
144
- {
145
- id: "gpt-5-mini",
146
- name: "GPT-5 Mini (openai, via Setu)",
147
- api: "openai-completions",
148
- reasoning: true,
149
- input: ["text", "image"],
150
- contextWindow: 400000,
151
- maxTokens: 128000,
152
- },
153
- {
154
- id: "codex-mini-latest",
155
- name: "Codex Mini (openai, via Setu)",
156
- api: "openai-completions",
157
- reasoning: true,
158
- input: ["text"],
159
- contextWindow: 200000,
160
- maxTokens: 100000,
161
- },
162
- {
163
- id: "gemini-3-pro-preview",
164
- name: "Gemini 3 Pro (google, via Setu)",
165
- api: "openai-completions",
166
- reasoning: true,
167
- input: ["text", "image"],
168
- contextWindow: 1000000,
169
- maxTokens: 64000,
170
- },
171
- {
172
- id: "gemini-3-flash-preview",
173
- name: "Gemini 3 Flash (google, via Setu)",
174
- api: "openai-completions",
175
- reasoning: true,
176
- input: ["text", "image"],
177
- contextWindow: 1048576,
178
- maxTokens: 65536,
179
- },
180
- {
181
- id: "kimi-k2.5",
182
- name: "Kimi K2.5 (moonshot, via Setu)",
183
- api: "openai-completions",
184
- reasoning: true,
185
- input: ["text"],
186
- contextWindow: 262144,
187
- maxTokens: 262144,
188
- },
189
- {
190
- id: "glm-5",
191
- name: "GLM-5 (zai, via Setu)",
192
- api: "openai-completions",
193
- reasoning: true,
194
- input: ["text"],
195
- contextWindow: 204800,
196
- maxTokens: 131072,
197
- },
198
- {
199
- id: "MiniMax-M2.5",
200
- name: "MiniMax M2.5 (minimax, via Setu)",
201
- api: "openai-completions",
202
- reasoning: true,
203
- input: ["text"],
204
- contextWindow: 204800,
205
- maxTokens: 131072,
206
- },
207
- ];
208
- }
209
-
210
- export function buildProviderConfig(
211
- port: number = DEFAULT_PROXY_PORT,
212
- ): SetuProviderConfig {
213
- return {
214
- baseUrl: `http://localhost:${port}/v1`,
215
- apiKey: "setu-proxy-handles-auth",
216
- api: "openai-completions",
217
- authHeader: false,
218
- models: getDefaultModels(),
219
- };
220
- }
221
-
222
- export async function buildProviderConfigWithCatalog(
223
- port: number = DEFAULT_PROXY_PORT,
224
- baseURL: string = DEFAULT_BASE_URL,
225
- ): Promise<SetuProviderConfig> {
226
- const models = await fetchModelsFromCatalog(baseURL);
227
- return {
228
- baseUrl: `http://localhost:${port}/v1`,
229
- apiKey: "setu-proxy-handles-auth",
230
- api: "openai-completions",
231
- authHeader: false,
232
- models,
233
- };
234
- }
235
-
236
- export async function injectConfig(port: number = DEFAULT_PROXY_PORT): Promise<void> {
237
- const config = readOpenClawConfig();
238
-
239
- if (!config.models) config.models = {};
240
- const models = config.models as Record<string, unknown>;
241
- if (!models.providers) models.providers = {};
242
- const providers = models.providers as Record<string, unknown>;
243
-
244
- providers[PROVIDER_KEY] = await buildProviderConfigWithCatalog(port);
245
-
246
- writeOpenClawConfig(config);
247
- }
248
-
249
- export function removeConfig(): void {
250
- const config = readOpenClawConfig();
251
-
252
- const models = config.models as Record<string, unknown> | undefined;
253
- if (!models?.providers) return;
254
- const providers = models.providers as Record<string, unknown>;
255
- delete providers[PROVIDER_KEY];
256
-
257
- writeOpenClawConfig(config);
258
- }
259
-
260
- export function isConfigured(): boolean {
261
- const config = readOpenClawConfig();
262
- const models = config.models as Record<string, unknown> | undefined;
263
- if (!models?.providers) return false;
264
- const providers = models.providers as Record<string, unknown>;
265
- return PROVIDER_KEY in providers;
266
- }
267
-
268
- export function getConfigPath(): string {
269
- return OPENCLAW_CONFIG_PATH;
270
- }