@sofer_agent/cli 0.2.1 → 0.3.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/dist/bin.js +6 -27
- package/dist/bin.js.map +1 -1
- package/dist/env.d.ts +6 -7
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +7 -25
- package/dist/env.js.map +1 -1
- package/dist/init.d.ts +4 -3
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +141 -57
- package/dist/init.js.map +1 -1
- package/dist/secrets.d.ts +1 -1
- package/dist/secrets.d.ts.map +1 -1
- package/dist/secrets.js +5 -5
- package/dist/secrets.js.map +1 -1
- package/package.json +2 -2
- package/src/bin.ts +6 -30
- package/src/env.ts +7 -24
- package/src/init.ts +167 -67
- package/src/secrets.ts +5 -5
package/dist/bin.js
CHANGED
|
@@ -1,38 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { agentPaths } from "@sofer_agent/core";
|
|
2
3
|
import { chatCommand } from "./chat.js";
|
|
3
|
-
import { loadDotenv } from "./env.js";
|
|
4
4
|
import { infoCommand } from "./info.js";
|
|
5
5
|
import { initCommand } from "./init.js";
|
|
6
6
|
function printHelp() {
|
|
7
7
|
console.log(`sofer — sovereign AI agent on Sui (memory on Walrus)
|
|
8
8
|
|
|
9
9
|
sofer chat with your agent (default)
|
|
10
|
-
sofer init mint
|
|
10
|
+
sofer init interactive setup — mint agent + provision memory
|
|
11
11
|
sofer info print on-chain agent state
|
|
12
|
-
sofer setup print the .env template to configure
|
|
13
12
|
sofer help show this help
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function printSetup() {
|
|
18
|
-
console.log(`# Copy this into a .env file in your project directory, then fill in:
|
|
19
|
-
# - ANTHROPIC_API_KEY (your Claude API key)
|
|
20
|
-
# - SOFER_SUI_SECRET_KEY (or let \`sofer init\` generate one)
|
|
21
|
-
#
|
|
22
|
-
# Then run: sofer init
|
|
23
|
-
|
|
24
|
-
# --- Sui chain ---
|
|
25
|
-
SUI_NETWORK=testnet
|
|
26
|
-
SUI_RPC_URL=https://fullnode.testnet.sui.io:443
|
|
27
|
-
SOFER_PACKAGE_ID=0xd5291ac6fd0de787f9fe0bc16c677ef5bafffd920936a05b14e7a8a142e46434
|
|
28
|
-
SOFER_AGENT_OBJECT_ID=
|
|
29
|
-
|
|
30
|
-
# --- Brain (Claude) ---
|
|
31
|
-
ANTHROPIC_API_KEY=
|
|
32
|
-
SOFER_MODEL=claude-opus-4-8`);
|
|
14
|
+
Config + secrets stored in ${agentPaths.root}/
|
|
15
|
+
Use env vars (SOFER_*) to override any setting.`);
|
|
33
16
|
}
|
|
34
17
|
async function main() {
|
|
35
|
-
loadDotenv();
|
|
36
18
|
const cmd = process.argv[2] ?? "chat";
|
|
37
19
|
switch (cmd) {
|
|
38
20
|
case "chat":
|
|
@@ -44,9 +26,6 @@ async function main() {
|
|
|
44
26
|
case "info":
|
|
45
27
|
await infoCommand();
|
|
46
28
|
break;
|
|
47
|
-
case "setup":
|
|
48
|
-
printSetup();
|
|
49
|
-
break;
|
|
50
29
|
case "help":
|
|
51
30
|
case "-h":
|
|
52
31
|
case "--help":
|
|
@@ -61,8 +40,8 @@ async function main() {
|
|
|
61
40
|
main().catch((e) => {
|
|
62
41
|
const msg = e instanceof Error ? e.message : String(e);
|
|
63
42
|
console.error(msg);
|
|
64
|
-
if (msg.includes("
|
|
65
|
-
console.error(
|
|
43
|
+
if (msg.includes("SOFER_SUI_SECRET_KEY") || msg.includes("ANTHROPIC_API_KEY") || msg.includes("SOFER_PACKAGE_ID")) {
|
|
44
|
+
console.error(`\nFirst time? Run \`sofer init\` — an interactive wizard (creates ${agentPaths.root}/).`);
|
|
66
45
|
}
|
|
67
46
|
process.exit(1);
|
|
68
47
|
});
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CACT;;;;;;;6BAOyB,UAAU,CAAC,IAAI;gDACI,CAC7C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IACtC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM,CAAC;QACZ,KAAK,IAAI,CAAC;QACV,KAAK,QAAQ;YACX,SAAS,EAAE,CAAC;YACZ,MAAM;QACR;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;YAC3C,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACjB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACvD,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,IAAI,GAAG,CAAC,QAAQ,CAAC,sBAAsB,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAClH,OAAO,CAAC,KAAK,CAAC,qEAAqE,UAAU,CAAC,IAAI,KAAK,CAAC,CAAC;IAC3G,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/env.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Load
|
|
3
|
-
*
|
|
4
|
-
* Resolution order, so `sofer` works from any working directory:
|
|
5
|
-
* 1. `.env` in the current directory (a per-project override), else
|
|
6
|
-
* 2. `.env` at the repo root, found relative to this installed binary
|
|
7
|
-
* (`<repo>/packages/cli/dist/env.js` → `<repo>/.env`).
|
|
2
|
+
* Load secrets from ~/.sofer/ (sui.key, brain.key).
|
|
3
|
+
* Falls back to SOFER_SUI_SECRET_KEY and ANTHROPIC_API_KEY env vars.
|
|
8
4
|
*/
|
|
9
|
-
export declare function
|
|
5
|
+
export declare function loadSecrets(): {
|
|
6
|
+
suiSecretKey: string | null;
|
|
7
|
+
anthropicApiKey: string | null;
|
|
8
|
+
};
|
|
10
9
|
//# sourceMappingURL=env.d.ts.map
|
package/dist/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,WAAW,IAAI;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAI7F"}
|
package/dist/env.js
CHANGED
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
1
|
+
import { agentPaths, readSecret } from "@sofer_agent/core";
|
|
4
2
|
/**
|
|
5
|
-
* Load
|
|
6
|
-
*
|
|
7
|
-
* Resolution order, so `sofer` works from any working directory:
|
|
8
|
-
* 1. `.env` in the current directory (a per-project override), else
|
|
9
|
-
* 2. `.env` at the repo root, found relative to this installed binary
|
|
10
|
-
* (`<repo>/packages/cli/dist/env.js` → `<repo>/.env`).
|
|
3
|
+
* Load secrets from ~/.sofer/ (sui.key, brain.key).
|
|
4
|
+
* Falls back to SOFER_SUI_SECRET_KEY and ANTHROPIC_API_KEY env vars.
|
|
11
5
|
*/
|
|
12
|
-
export function
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const cwdEnv = resolve(process.cwd(), ".env");
|
|
17
|
-
const here = dirname(fileURLToPath(import.meta.url)); // <repo>/packages/cli/dist
|
|
18
|
-
const repoEnv = resolve(here, "../../../.env");
|
|
19
|
-
const target = existsSync(cwdEnv) ? cwdEnv : existsSync(repoEnv) ? repoEnv : null;
|
|
20
|
-
if (!target)
|
|
21
|
-
return;
|
|
22
|
-
try {
|
|
23
|
-
loader(target);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
/* ignore malformed env file */
|
|
27
|
-
}
|
|
6
|
+
export function loadSecrets() {
|
|
7
|
+
const suiKey = readSecret(agentPaths.suiKey) ?? process.env.SOFER_SUI_SECRET_KEY ?? null;
|
|
8
|
+
const brainKey = readSecret(agentPaths.brainKey) ?? process.env.ANTHROPIC_API_KEY ?? null;
|
|
9
|
+
return { suiSecretKey: suiKey, anthropicApiKey: brainKey };
|
|
28
10
|
}
|
|
29
11
|
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,IAAI,CAAC;IACzF,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC;IAC1F,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC;AAC7D,CAAC"}
|
package/dist/init.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Interactive init wizard — mint the agent + provision Walrus memory.
|
|
3
|
+
* Stores config to ~/.sofer/config.json and secrets to ~/.sofer/{sui,brain}.key
|
|
4
|
+
*
|
|
5
|
+
* Idempotent: re-running on an already-set-up agent prints status and exits.
|
|
5
6
|
*/
|
|
6
7
|
export declare function initCommand(): Promise<void>;
|
|
7
8
|
//# sourceMappingURL=init.d.ts.map
|
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAyCA;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAsKjD"}
|
package/dist/init.js
CHANGED
|
@@ -1,92 +1,176 @@
|
|
|
1
|
-
import { AgentClient, generateKeypair, keypairFromSecret, loadConfig, makeSuiClient, provisionMemwal, requestFaucet, suiBalance, waitForBalance, } from "@sofer_agent/core";
|
|
1
|
+
import { AgentClient, agentPaths, generateKeypair, keypairFromSecret, loadConfig, makeSuiClient, provisionMemwal, requestFaucet, shortAgentId, suiBalance, waitForBalance, writePersistedConfig, writeSecret, } from "@sofer_agent/core";
|
|
2
|
+
/** Simple spinner that logs to stdout. */
|
|
3
|
+
function spin(label, fn) {
|
|
4
|
+
process.stdout.write(`${label}… `);
|
|
5
|
+
return fn().then((result) => {
|
|
6
|
+
console.log("done");
|
|
7
|
+
return result;
|
|
8
|
+
}, (err) => {
|
|
9
|
+
console.log("failed");
|
|
10
|
+
throw err;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
async function ask(question) {
|
|
14
|
+
const rl = await import("node:readline/promises");
|
|
15
|
+
const iface = rl.createInterface({ input: process.stdin, output: process.stdout });
|
|
16
|
+
const answer = await iface.question(question);
|
|
17
|
+
iface.close();
|
|
18
|
+
return answer.trim();
|
|
19
|
+
}
|
|
2
20
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
21
|
+
* Interactive init wizard — mint the agent + provision Walrus memory.
|
|
22
|
+
* Stores config to ~/.sofer/config.json and secrets to ~/.sofer/{sui,brain}.key
|
|
23
|
+
*
|
|
24
|
+
* Idempotent: re-running on an already-set-up agent prints status and exits.
|
|
6
25
|
*/
|
|
7
26
|
export async function initCommand() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const net = config.network === "mainnet" ? "mainnet" : "testnet";
|
|
12
|
-
// Already fully initialized? Don't re-mint / re-provision.
|
|
13
|
-
if (config.agentObjectId && config.memwal.accountId && config.memwal.delegateKey) {
|
|
27
|
+
// ── Already initialized? ───────────────────────────────────────────────
|
|
28
|
+
const existing = loadConfig();
|
|
29
|
+
if (existing.agentObjectId && existing.memwal.accountId && existing.memwal.delegateKey) {
|
|
14
30
|
console.log("Already initialized:");
|
|
15
|
-
console.log(`
|
|
16
|
-
console.log(`
|
|
17
|
-
console.log(
|
|
18
|
-
console.log(
|
|
31
|
+
console.log(` config: ${agentPaths.config}`);
|
|
32
|
+
console.log(` sui key: ${agentPaths.suiKey}`);
|
|
33
|
+
console.log(` brain key: ${agentPaths.brainKey}`);
|
|
34
|
+
console.log(` agent: ${existing.agentObjectId}`);
|
|
35
|
+
console.log(` address: ${existing.memwal.accountId}`);
|
|
36
|
+
console.log(`\nRun \`sofer\` to chat. To start fresh, delete ~/.sofer/.`);
|
|
19
37
|
return;
|
|
20
38
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
console.log(
|
|
39
|
+
console.log("╔══════════════════════════════════════════╗");
|
|
40
|
+
console.log("║ sofer init — agent setup ║");
|
|
41
|
+
console.log("╚══════════════════════════════════════════╝\n");
|
|
42
|
+
// ── 1. Network ─────────────────────────────────────────────────────────
|
|
43
|
+
const network = (await ask("Network? [testnet/mainnet] (testnet): ")) || "testnet";
|
|
44
|
+
const net = network === "mainnet" ? "mainnet" : "testnet";
|
|
45
|
+
console.log(` → ${net}\n`);
|
|
46
|
+
// ── 2. Anthropic API key ───────────────────────────────────────────────
|
|
47
|
+
const apiKey = await ask("Anthropic API key: ");
|
|
48
|
+
if (!apiKey)
|
|
49
|
+
throw new Error("ANTHROPIC_API_KEY is required");
|
|
50
|
+
console.log(" → saved (encrypted to disk)\n");
|
|
51
|
+
// ── 3. Sui wallet ─────────────────────────────────────────────────────
|
|
52
|
+
const existingSecret = process.env.SOFER_SUI_SECRET_KEY;
|
|
53
|
+
let secret;
|
|
54
|
+
let address;
|
|
55
|
+
if (existingSecret) {
|
|
56
|
+
const kp = keypairFromSecret(existingSecret);
|
|
57
|
+
secret = existingSecret;
|
|
58
|
+
address = kp.toSuiAddress();
|
|
59
|
+
console.log(` wallet: ${address} (from env)\n`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const kp = generateKeypair();
|
|
63
|
+
secret = kp.getSecretKey();
|
|
64
|
+
address = kp.toSuiAddress();
|
|
65
|
+
console.log(` wallet: ${address} (new)\n`);
|
|
66
|
+
}
|
|
67
|
+
const config = loadConfig();
|
|
28
68
|
const sui = makeSuiClient(config.network);
|
|
29
|
-
//
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
69
|
+
// ── 4. Fund from faucet (testnet only) ─────────────────────────────────
|
|
70
|
+
if (net !== "mainnet") {
|
|
71
|
+
const bal = await suiBalance(sui, address);
|
|
72
|
+
if (bal === 0n) {
|
|
73
|
+
console.log("Requesting testnet faucet…");
|
|
74
|
+
try {
|
|
75
|
+
await requestFaucet(config.network, address);
|
|
76
|
+
await waitForBalance(sui, address, 1n);
|
|
77
|
+
console.log(" → funded\n");
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
console.warn(` faucet failed: ${String(e).slice(0, 80)}`);
|
|
81
|
+
console.warn(` fund ${address} at https://faucet.sui.io/ and re-run\n`);
|
|
82
|
+
}
|
|
35
83
|
}
|
|
36
|
-
|
|
37
|
-
console.
|
|
84
|
+
else {
|
|
85
|
+
console.log(` balance: ${(Number(bal) / 1e9).toFixed(4)} SUI\n`);
|
|
38
86
|
}
|
|
39
87
|
}
|
|
40
|
-
// 3. Mint the on-chain Agent object (reuse if one is already configured).
|
|
41
88
|
const agents = new AgentClient(sui, config.packageId);
|
|
89
|
+
// ── 5. Mint agent ─────────────────────────────────────────────────────
|
|
42
90
|
let agentId = config.agentObjectId;
|
|
43
|
-
if (agentId) {
|
|
44
|
-
console.log(
|
|
91
|
+
if (!agentId) {
|
|
92
|
+
console.log("Minting agent identity object on-chain…");
|
|
93
|
+
try {
|
|
94
|
+
const keypair = keypairFromSecret(secret);
|
|
95
|
+
const minted = await spin(" minting", () => agents.mint(keypair, "Sofer", "a sovereign AI agent on Sui with encrypted memory on Walrus", new TextEncoder().encode("sofer")));
|
|
96
|
+
agentId = minted.agentId;
|
|
97
|
+
console.log(` → agent: ${agentId}\n`);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
console.error(` mint failed: ${String(e).slice(0, 120)}`);
|
|
101
|
+
console.error(" (maybe already minted? set SOFER_AGENT_OBJECT_ID and re-run)");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
45
104
|
}
|
|
46
105
|
else {
|
|
47
|
-
console.log(
|
|
48
|
-
const minted = await agents.mint(keypair, "Sofer", "a sovereign AI agent on Sui with encrypted memory on Walrus", new TextEncoder().encode("sofer"));
|
|
49
|
-
agentId = minted.agentId;
|
|
50
|
-
console.log(` agent object: ${agentId}`);
|
|
106
|
+
console.log(` reusing agent: ${agentId}\n`);
|
|
51
107
|
}
|
|
52
|
-
//
|
|
108
|
+
// ── 6. Provision MemWal memory ────────────────────────────────────────
|
|
53
109
|
let accountId = config.memwal.accountId;
|
|
54
110
|
let delegateKey = config.memwal.delegateKey;
|
|
55
|
-
if (accountId
|
|
56
|
-
console.log(
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
console.log("• provisioning Walrus memory (MemWal account + delegate key)…");
|
|
111
|
+
if (!accountId || !delegateKey) {
|
|
112
|
+
console.log("Provisioning Walrus memory (MemWal)…");
|
|
60
113
|
try {
|
|
61
|
-
const prov = await provisionMemwal({
|
|
114
|
+
const prov = await spin(" provisioning", () => provisionMemwal({
|
|
62
115
|
ownerSecret: secret,
|
|
63
116
|
packageId: config.memwal.packageId,
|
|
64
117
|
registryId: config.memwal.registryId,
|
|
65
118
|
network: net,
|
|
66
119
|
label: "sofer-cli",
|
|
67
|
-
});
|
|
120
|
+
}));
|
|
68
121
|
accountId = prov.accountId;
|
|
69
122
|
delegateKey = prov.delegateKey;
|
|
70
|
-
console.log(`
|
|
123
|
+
console.log(` → account: ${accountId}\n`);
|
|
71
124
|
}
|
|
72
125
|
catch (e) {
|
|
73
126
|
const msg = String(e);
|
|
74
127
|
if (msg.includes("abort code: 3") || msg.includes("create_account")) {
|
|
75
|
-
throw new Error("This
|
|
76
|
-
"
|
|
77
|
-
" • Or start fresh: unset SOFER_SUI_SECRET_KEY in .env and re-run `sofer init` to mint a new wallet.");
|
|
128
|
+
throw new Error("This wallet already owns a MemWal account (one per address).\n" +
|
|
129
|
+
"Delete ~/.sofer/ and re-run `sofer init`, or set the env vars manually.");
|
|
78
130
|
}
|
|
79
131
|
throw e;
|
|
80
132
|
}
|
|
81
133
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
console.log(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
134
|
+
else {
|
|
135
|
+
console.log(` reusing memory account: ${accountId}\n`);
|
|
136
|
+
}
|
|
137
|
+
// ── 7. Link memory to agent on-chain ───────────────────────────────────
|
|
138
|
+
console.log("Linking memory account to agent on-chain…");
|
|
139
|
+
try {
|
|
140
|
+
const keypair = keypairFromSecret(secret);
|
|
141
|
+
await spin(" linking", () => agents.setMemoryAccount(keypair, agentId, accountId));
|
|
142
|
+
console.log(" → linked\n");
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
console.warn(` link failed (may already be linked): ${String(e).slice(0, 80)}\n`);
|
|
146
|
+
}
|
|
147
|
+
// ── 8. Persist config + secrets to ~/.sofer/ ───────────────────────────
|
|
148
|
+
console.log("Saving config to ~/.sofer/…");
|
|
149
|
+
writeSecret(agentPaths.suiKey, secret);
|
|
150
|
+
writeSecret(agentPaths.brainKey, apiKey);
|
|
151
|
+
const persisted = {
|
|
152
|
+
network: net,
|
|
153
|
+
agentObjectId: agentId,
|
|
154
|
+
agentAddress: address,
|
|
155
|
+
brain: { model: config.brain.model },
|
|
156
|
+
memwal: {
|
|
157
|
+
accountId: accountId,
|
|
158
|
+
delegateKey: delegateKey,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
writePersistedConfig(persisted);
|
|
162
|
+
const agentDir = agentPaths.agent(shortAgentId(address));
|
|
163
|
+
console.log(` config: ${agentPaths.config}`);
|
|
164
|
+
console.log(` keys: ${agentPaths.suiKey} / ${agentPaths.brainKey}`);
|
|
165
|
+
console.log(` agent: ${agentDir.dir}\n`);
|
|
166
|
+
// ── 9. Summary ────────────────────────────────────────────────────────
|
|
167
|
+
console.log("┌─────────────────────────────────────────┐");
|
|
168
|
+
console.log(`│ agent id ${agentId.slice(0, 30)}… │`);
|
|
169
|
+
console.log(`│ address ${address.slice(0, 10)}… │`);
|
|
170
|
+
console.log(`│ network ${net.padEnd(23)} │`);
|
|
171
|
+
console.log(`│ memory ${accountId.slice(0, 10)}… │`);
|
|
172
|
+
console.log("├─────────────────────────────────────────┤");
|
|
173
|
+
console.log("│ Run `sofer` to chat with your agent. │");
|
|
174
|
+
console.log("└─────────────────────────────────────────┘");
|
|
91
175
|
}
|
|
92
176
|
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/dist/secrets.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { AgentSecrets } from "@sofer_agent/core";
|
|
2
|
-
/** Pull required secrets from
|
|
2
|
+
/** Pull required secrets from ~/.sofer/ or env vars, with actionable errors. */
|
|
3
3
|
export declare function requireSecrets(): AgentSecrets;
|
|
4
4
|
//# sourceMappingURL=secrets.d.ts.map
|
package/dist/secrets.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,gFAAgF;AAChF,wBAAgB,cAAc,IAAI,YAAY,CAS7C"}
|
package/dist/secrets.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { loadSecrets } from "./env.js";
|
|
2
|
+
/** Pull required secrets from ~/.sofer/ or env vars, with actionable errors. */
|
|
2
3
|
export function requireSecrets() {
|
|
3
|
-
const suiSecretKey =
|
|
4
|
-
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
4
|
+
const { suiSecretKey, anthropicApiKey } = loadSecrets();
|
|
5
5
|
if (!suiSecretKey) {
|
|
6
|
-
throw new Error("SOFER_SUI_SECRET_KEY not
|
|
6
|
+
throw new Error("SOFER_SUI_SECRET_KEY not found — run `sofer init` first (creates ~/.sofer/sui.key)");
|
|
7
7
|
}
|
|
8
8
|
if (!anthropicApiKey) {
|
|
9
|
-
throw new Error("ANTHROPIC_API_KEY not
|
|
9
|
+
throw new Error("ANTHROPIC_API_KEY not found — run `sofer init` first (creates ~/.sofer/brain.key)");
|
|
10
10
|
}
|
|
11
11
|
return { suiSecretKey, anthropicApiKey };
|
|
12
12
|
}
|
package/dist/secrets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,gFAAgF;AAChF,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,GAAG,WAAW,EAAE,CAAC;IACxD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC,CAAC;IACxG,CAAC;IACD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,mFAAmF,CAAC,CAAC;IACvG,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;AAC3C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sofer_agent/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sofer": "./dist/bin.js"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"cli"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@sofer_agent/core": "0.
|
|
28
|
+
"@sofer_agent/core": "0.3.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^22.10.2",
|
package/src/bin.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { agentPaths } from "@sofer_agent/core";
|
|
2
3
|
import { chatCommand } from "./chat.js";
|
|
3
|
-
import { loadDotenv } from "./env.js";
|
|
4
4
|
import { infoCommand } from "./info.js";
|
|
5
5
|
import { initCommand } from "./init.js";
|
|
6
6
|
|
|
@@ -9,37 +9,16 @@ function printHelp(): void {
|
|
|
9
9
|
`sofer — sovereign AI agent on Sui (memory on Walrus)
|
|
10
10
|
|
|
11
11
|
sofer chat with your agent (default)
|
|
12
|
-
sofer init mint
|
|
12
|
+
sofer init interactive setup — mint agent + provision memory
|
|
13
13
|
sofer info print on-chain agent state
|
|
14
|
-
sofer setup print the .env template to configure
|
|
15
14
|
sofer help show this help
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function printSetup(): void {
|
|
22
|
-
console.log(
|
|
23
|
-
`# Copy this into a .env file in your project directory, then fill in:
|
|
24
|
-
# - ANTHROPIC_API_KEY (your Claude API key)
|
|
25
|
-
# - SOFER_SUI_SECRET_KEY (or let \`sofer init\` generate one)
|
|
26
|
-
#
|
|
27
|
-
# Then run: sofer init
|
|
28
|
-
|
|
29
|
-
# --- Sui chain ---
|
|
30
|
-
SUI_NETWORK=testnet
|
|
31
|
-
SUI_RPC_URL=https://fullnode.testnet.sui.io:443
|
|
32
|
-
SOFER_PACKAGE_ID=0xd5291ac6fd0de787f9fe0bc16c677ef5bafffd920936a05b14e7a8a142e46434
|
|
33
|
-
SOFER_AGENT_OBJECT_ID=
|
|
34
|
-
|
|
35
|
-
# --- Brain (Claude) ---
|
|
36
|
-
ANTHROPIC_API_KEY=
|
|
37
|
-
SOFER_MODEL=claude-opus-4-8`,
|
|
16
|
+
Config + secrets stored in ${agentPaths.root}/
|
|
17
|
+
Use env vars (SOFER_*) to override any setting.`,
|
|
38
18
|
);
|
|
39
19
|
}
|
|
40
20
|
|
|
41
21
|
async function main(): Promise<void> {
|
|
42
|
-
loadDotenv();
|
|
43
22
|
const cmd = process.argv[2] ?? "chat";
|
|
44
23
|
switch (cmd) {
|
|
45
24
|
case "chat":
|
|
@@ -51,9 +30,6 @@ async function main(): Promise<void> {
|
|
|
51
30
|
case "info":
|
|
52
31
|
await infoCommand();
|
|
53
32
|
break;
|
|
54
|
-
case "setup":
|
|
55
|
-
printSetup();
|
|
56
|
-
break;
|
|
57
33
|
case "help":
|
|
58
34
|
case "-h":
|
|
59
35
|
case "--help":
|
|
@@ -69,8 +45,8 @@ async function main(): Promise<void> {
|
|
|
69
45
|
main().catch((e) => {
|
|
70
46
|
const msg = e instanceof Error ? e.message : String(e);
|
|
71
47
|
console.error(msg);
|
|
72
|
-
if (msg.includes("
|
|
73
|
-
console.error(
|
|
48
|
+
if (msg.includes("SOFER_SUI_SECRET_KEY") || msg.includes("ANTHROPIC_API_KEY") || msg.includes("SOFER_PACKAGE_ID")) {
|
|
49
|
+
console.error(`\nFirst time? Run \`sofer init\` — an interactive wizard (creates ${agentPaths.root}/).`);
|
|
74
50
|
}
|
|
75
51
|
process.exit(1);
|
|
76
52
|
});
|
package/src/env.ts
CHANGED
|
@@ -1,28 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
1
|
+
import { agentPaths, readSecret } from "@sofer_agent/core";
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
|
-
* Load
|
|
7
|
-
*
|
|
8
|
-
* Resolution order, so `sofer` works from any working directory:
|
|
9
|
-
* 1. `.env` in the current directory (a per-project override), else
|
|
10
|
-
* 2. `.env` at the repo root, found relative to this installed binary
|
|
11
|
-
* (`<repo>/packages/cli/dist/env.js` → `<repo>/.env`).
|
|
4
|
+
* Load secrets from ~/.sofer/ (sui.key, brain.key).
|
|
5
|
+
* Falls back to SOFER_SUI_SECRET_KEY and ANTHROPIC_API_KEY env vars.
|
|
12
6
|
*/
|
|
13
|
-
export function
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const cwdEnv = resolve(process.cwd(), ".env");
|
|
18
|
-
const here = dirname(fileURLToPath(import.meta.url)); // <repo>/packages/cli/dist
|
|
19
|
-
const repoEnv = resolve(here, "../../../.env");
|
|
20
|
-
|
|
21
|
-
const target = existsSync(cwdEnv) ? cwdEnv : existsSync(repoEnv) ? repoEnv : null;
|
|
22
|
-
if (!target) return;
|
|
23
|
-
try {
|
|
24
|
-
loader(target);
|
|
25
|
-
} catch {
|
|
26
|
-
/* ignore malformed env file */
|
|
27
|
-
}
|
|
7
|
+
export function loadSecrets(): { suiSecretKey: string | null; anthropicApiKey: string | null } {
|
|
8
|
+
const suiKey = readSecret(agentPaths.suiKey) ?? process.env.SOFER_SUI_SECRET_KEY ?? null;
|
|
9
|
+
const brainKey = readSecret(agentPaths.brainKey) ?? process.env.ANTHROPIC_API_KEY ?? null;
|
|
10
|
+
return { suiSecretKey: suiKey, anthropicApiKey: brainKey };
|
|
28
11
|
}
|
package/src/init.ts
CHANGED
|
@@ -1,114 +1,214 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type PersistedConfig,
|
|
3
|
+
type SoferConfig,
|
|
2
4
|
AgentClient,
|
|
5
|
+
agentPaths,
|
|
3
6
|
generateKeypair,
|
|
4
7
|
keypairFromSecret,
|
|
5
8
|
loadConfig,
|
|
6
9
|
makeSuiClient,
|
|
7
10
|
provisionMemwal,
|
|
8
11
|
requestFaucet,
|
|
12
|
+
shortAgentId,
|
|
9
13
|
suiBalance,
|
|
10
14
|
waitForBalance,
|
|
15
|
+
writePersistedConfig,
|
|
16
|
+
writeSecret,
|
|
11
17
|
} from "@sofer_agent/core";
|
|
12
18
|
|
|
19
|
+
/** Simple spinner that logs to stdout. */
|
|
20
|
+
function spin<T>(label: string, fn: () => Promise<T>): Promise<T> {
|
|
21
|
+
process.stdout.write(`${label}… `);
|
|
22
|
+
return fn().then(
|
|
23
|
+
(result) => {
|
|
24
|
+
console.log("done");
|
|
25
|
+
return result;
|
|
26
|
+
},
|
|
27
|
+
(err) => {
|
|
28
|
+
console.log("failed");
|
|
29
|
+
throw err;
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function ask(question: string): Promise<string> {
|
|
35
|
+
const rl = await import("node:readline/promises");
|
|
36
|
+
const iface = rl.createInterface({ input: process.stdin, output: process.stdout });
|
|
37
|
+
const answer = await iface.question(question);
|
|
38
|
+
iface.close();
|
|
39
|
+
return answer.trim();
|
|
40
|
+
}
|
|
41
|
+
|
|
13
42
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
43
|
+
* Interactive init wizard — mint the agent + provision Walrus memory.
|
|
44
|
+
* Stores config to ~/.sofer/config.json and secrets to ~/.sofer/{sui,brain}.key
|
|
45
|
+
*
|
|
46
|
+
* Idempotent: re-running on an already-set-up agent prints status and exits.
|
|
17
47
|
*/
|
|
18
48
|
export async function initCommand(): Promise<void> {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Already fully initialized? Don't re-mint / re-provision.
|
|
24
|
-
if (config.agentObjectId && config.memwal.accountId && config.memwal.delegateKey) {
|
|
49
|
+
// ── Already initialized? ───────────────────────────────────────────────
|
|
50
|
+
const existing = loadConfig();
|
|
51
|
+
if (existing.agentObjectId && existing.memwal.accountId && existing.memwal.delegateKey) {
|
|
25
52
|
console.log("Already initialized:");
|
|
26
|
-
console.log(`
|
|
27
|
-
console.log(`
|
|
28
|
-
console.log(
|
|
29
|
-
console.log(
|
|
53
|
+
console.log(` config: ${agentPaths.config}`);
|
|
54
|
+
console.log(` sui key: ${agentPaths.suiKey}`);
|
|
55
|
+
console.log(` brain key: ${agentPaths.brainKey}`);
|
|
56
|
+
console.log(` agent: ${existing.agentObjectId}`);
|
|
57
|
+
console.log(` address: ${existing.memwal.accountId}`);
|
|
58
|
+
console.log(`\nRun \`sofer\` to chat. To start fresh, delete ~/.sofer/.`);
|
|
30
59
|
return;
|
|
31
60
|
}
|
|
32
61
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
62
|
+
console.log("╔══════════════════════════════════════════╗");
|
|
63
|
+
console.log("║ sofer init — agent setup ║");
|
|
64
|
+
console.log("╚══════════════════════════════════════════╝\n");
|
|
65
|
+
|
|
66
|
+
// ── 1. Network ─────────────────────────────────────────────────────────
|
|
67
|
+
const network = (await ask("Network? [testnet/mainnet] (testnet): ")) || "testnet";
|
|
68
|
+
const net = network === "mainnet" ? "mainnet" : "testnet";
|
|
69
|
+
console.log(` → ${net}\n`);
|
|
70
|
+
|
|
71
|
+
// ── 2. Anthropic API key ───────────────────────────────────────────────
|
|
72
|
+
const apiKey = await ask("Anthropic API key: ");
|
|
73
|
+
if (!apiKey) throw new Error("ANTHROPIC_API_KEY is required");
|
|
74
|
+
console.log(" → saved (encrypted to disk)\n");
|
|
40
75
|
|
|
76
|
+
// ── 3. Sui wallet ─────────────────────────────────────────────────────
|
|
77
|
+
const existingSecret = process.env.SOFER_SUI_SECRET_KEY;
|
|
78
|
+
let secret: string;
|
|
79
|
+
let address: string;
|
|
80
|
+
if (existingSecret) {
|
|
81
|
+
const kp = keypairFromSecret(existingSecret);
|
|
82
|
+
secret = existingSecret;
|
|
83
|
+
address = kp.toSuiAddress();
|
|
84
|
+
console.log(` wallet: ${address} (from env)\n`);
|
|
85
|
+
} else {
|
|
86
|
+
const kp = generateKeypair();
|
|
87
|
+
secret = kp.getSecretKey();
|
|
88
|
+
address = kp.toSuiAddress();
|
|
89
|
+
console.log(` wallet: ${address} (new)\n`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const config = loadConfig();
|
|
41
93
|
const sui = makeSuiClient(config.network);
|
|
42
94
|
|
|
43
|
-
//
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
95
|
+
// ── 4. Fund from faucet (testnet only) ─────────────────────────────────
|
|
96
|
+
if (net !== "mainnet") {
|
|
97
|
+
const bal = await suiBalance(sui, address);
|
|
98
|
+
if (bal === 0n) {
|
|
99
|
+
console.log("Requesting testnet faucet…");
|
|
100
|
+
try {
|
|
101
|
+
await requestFaucet(config.network, address);
|
|
102
|
+
await waitForBalance(sui, address, 1n);
|
|
103
|
+
console.log(" → funded\n");
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.warn(` faucet failed: ${String(e).slice(0, 80)}`);
|
|
106
|
+
console.warn(` fund ${address} at https://faucet.sui.io/ and re-run\n`);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
console.log(` balance: ${(Number(bal) / 1e9).toFixed(4)} SUI\n`);
|
|
53
110
|
}
|
|
54
111
|
}
|
|
55
112
|
|
|
56
|
-
// 3. Mint the on-chain Agent object (reuse if one is already configured).
|
|
57
113
|
const agents = new AgentClient(sui, config.packageId);
|
|
114
|
+
|
|
115
|
+
// ── 5. Mint agent ─────────────────────────────────────────────────────
|
|
58
116
|
let agentId = config.agentObjectId;
|
|
59
|
-
if (agentId) {
|
|
60
|
-
console.log(
|
|
117
|
+
if (!agentId) {
|
|
118
|
+
console.log("Minting agent identity object on-chain…");
|
|
119
|
+
try {
|
|
120
|
+
const keypair = keypairFromSecret(secret);
|
|
121
|
+
const minted = await spin(" minting", () =>
|
|
122
|
+
agents.mint(
|
|
123
|
+
keypair,
|
|
124
|
+
"Sofer",
|
|
125
|
+
"a sovereign AI agent on Sui with encrypted memory on Walrus",
|
|
126
|
+
new TextEncoder().encode("sofer"),
|
|
127
|
+
),
|
|
128
|
+
);
|
|
129
|
+
agentId = minted.agentId;
|
|
130
|
+
console.log(` → agent: ${agentId}\n`);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.error(` mint failed: ${String(e).slice(0, 120)}`);
|
|
133
|
+
console.error(" (maybe already minted? set SOFER_AGENT_OBJECT_ID and re-run)");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
61
136
|
} else {
|
|
62
|
-
console.log(
|
|
63
|
-
const minted = await agents.mint(
|
|
64
|
-
keypair,
|
|
65
|
-
"Sofer",
|
|
66
|
-
"a sovereign AI agent on Sui with encrypted memory on Walrus",
|
|
67
|
-
new TextEncoder().encode("sofer"),
|
|
68
|
-
);
|
|
69
|
-
agentId = minted.agentId;
|
|
70
|
-
console.log(` agent object: ${agentId}`);
|
|
137
|
+
console.log(` reusing agent: ${agentId}\n`);
|
|
71
138
|
}
|
|
72
139
|
|
|
73
|
-
//
|
|
140
|
+
// ── 6. Provision MemWal memory ────────────────────────────────────────
|
|
74
141
|
let accountId = config.memwal.accountId;
|
|
75
142
|
let delegateKey = config.memwal.delegateKey;
|
|
76
|
-
if (accountId
|
|
77
|
-
console.log(
|
|
78
|
-
} else {
|
|
79
|
-
console.log("• provisioning Walrus memory (MemWal account + delegate key)…");
|
|
143
|
+
if (!accountId || !delegateKey) {
|
|
144
|
+
console.log("Provisioning Walrus memory (MemWal)…");
|
|
80
145
|
try {
|
|
81
|
-
const prov = await
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
146
|
+
const prov = await spin(" provisioning", () =>
|
|
147
|
+
provisionMemwal({
|
|
148
|
+
ownerSecret: secret,
|
|
149
|
+
packageId: config.memwal.packageId,
|
|
150
|
+
registryId: config.memwal.registryId,
|
|
151
|
+
network: net,
|
|
152
|
+
label: "sofer-cli",
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
88
155
|
accountId = prov.accountId;
|
|
89
156
|
delegateKey = prov.delegateKey;
|
|
90
|
-
console.log(`
|
|
157
|
+
console.log(` → account: ${accountId}\n`);
|
|
91
158
|
} catch (e) {
|
|
92
159
|
const msg = String(e);
|
|
93
160
|
if (msg.includes("abort code: 3") || msg.includes("create_account")) {
|
|
94
161
|
throw new Error(
|
|
95
|
-
"This
|
|
96
|
-
"
|
|
97
|
-
" • Or start fresh: unset SOFER_SUI_SECRET_KEY in .env and re-run `sofer init` to mint a new wallet.",
|
|
162
|
+
"This wallet already owns a MemWal account (one per address).\n" +
|
|
163
|
+
"Delete ~/.sofer/ and re-run `sofer init`, or set the env vars manually.",
|
|
98
164
|
);
|
|
99
165
|
}
|
|
100
166
|
throw e;
|
|
101
167
|
}
|
|
168
|
+
} else {
|
|
169
|
+
console.log(` reusing memory account: ${accountId}\n`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ── 7. Link memory to agent on-chain ───────────────────────────────────
|
|
173
|
+
console.log("Linking memory account to agent on-chain…");
|
|
174
|
+
try {
|
|
175
|
+
const keypair = keypairFromSecret(secret);
|
|
176
|
+
await spin(" linking", () => agents.setMemoryAccount(keypair, agentId!, accountId!));
|
|
177
|
+
console.log(" → linked\n");
|
|
178
|
+
} catch (e) {
|
|
179
|
+
console.warn(` link failed (may already be linked): ${String(e).slice(0, 80)}\n`);
|
|
102
180
|
}
|
|
103
181
|
|
|
104
|
-
//
|
|
105
|
-
console.log("
|
|
106
|
-
|
|
182
|
+
// ── 8. Persist config + secrets to ~/.sofer/ ───────────────────────────
|
|
183
|
+
console.log("Saving config to ~/.sofer/…");
|
|
184
|
+
|
|
185
|
+
writeSecret(agentPaths.suiKey, secret);
|
|
186
|
+
writeSecret(agentPaths.brainKey, apiKey);
|
|
187
|
+
|
|
188
|
+
const persisted: PersistedConfig = {
|
|
189
|
+
network: net,
|
|
190
|
+
agentObjectId: agentId!,
|
|
191
|
+
agentAddress: address,
|
|
192
|
+
brain: { model: config.brain.model },
|
|
193
|
+
memwal: {
|
|
194
|
+
accountId: accountId!,
|
|
195
|
+
delegateKey: delegateKey!,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
writePersistedConfig(persisted);
|
|
199
|
+
|
|
200
|
+
const agentDir = agentPaths.agent(shortAgentId(address));
|
|
201
|
+
console.log(` config: ${agentPaths.config}`);
|
|
202
|
+
console.log(` keys: ${agentPaths.suiKey} / ${agentPaths.brainKey}`);
|
|
203
|
+
console.log(` agent: ${agentDir.dir}\n`);
|
|
107
204
|
|
|
108
|
-
|
|
109
|
-
console.log(
|
|
110
|
-
console.log(
|
|
111
|
-
console.log(
|
|
112
|
-
console.log(
|
|
113
|
-
console.log(
|
|
205
|
+
// ── 9. Summary ────────────────────────────────────────────────────────
|
|
206
|
+
console.log("┌─────────────────────────────────────────┐");
|
|
207
|
+
console.log(`│ agent id ${agentId!.slice(0, 30)}… │`);
|
|
208
|
+
console.log(`│ address ${address.slice(0, 10)}… │`);
|
|
209
|
+
console.log(`│ network ${net.padEnd(23)} │`);
|
|
210
|
+
console.log(`│ memory ${accountId!.slice(0, 10)}… │`);
|
|
211
|
+
console.log("├─────────────────────────────────────────┤");
|
|
212
|
+
console.log("│ Run `sofer` to chat with your agent. │");
|
|
213
|
+
console.log("└─────────────────────────────────────────┘");
|
|
114
214
|
}
|
package/src/secrets.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { AgentSecrets } from "@sofer_agent/core";
|
|
2
|
+
import { loadSecrets } from "./env.js";
|
|
2
3
|
|
|
3
|
-
/** Pull required secrets from
|
|
4
|
+
/** Pull required secrets from ~/.sofer/ or env vars, with actionable errors. */
|
|
4
5
|
export function requireSecrets(): AgentSecrets {
|
|
5
|
-
const suiSecretKey =
|
|
6
|
-
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
6
|
+
const { suiSecretKey, anthropicApiKey } = loadSecrets();
|
|
7
7
|
if (!suiSecretKey) {
|
|
8
|
-
throw new Error("SOFER_SUI_SECRET_KEY not
|
|
8
|
+
throw new Error("SOFER_SUI_SECRET_KEY not found — run `sofer init` first (creates ~/.sofer/sui.key)");
|
|
9
9
|
}
|
|
10
10
|
if (!anthropicApiKey) {
|
|
11
|
-
throw new Error("ANTHROPIC_API_KEY not
|
|
11
|
+
throw new Error("ANTHROPIC_API_KEY not found — run `sofer init` first (creates ~/.sofer/brain.key)");
|
|
12
12
|
}
|
|
13
13
|
return { suiSecretKey, anthropicApiKey };
|
|
14
14
|
}
|