@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 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 the agent + provision Walrus memory
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
- Reads config + secrets from a .env file or the environment.`);
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("SOFER_PACKAGE_ID") || msg.includes("SOFER_SUI_SECRET_KEY") || msg.includes("ANTHROPIC_API_KEY")) {
65
- console.error("\nFirst time? Run `sofer setup` for the .env template, then `sofer init`.");
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,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CACT;;;;;;;;4DAQwD,CACzD,CAAC;AACJ,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,CAAC,GAAG,CACT;;;;;;;;;;;;;;4BAcwB,CACzB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,UAAU,EAAE,CAAC;IACb,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,OAAO;YACV,UAAU,EAAE,CAAC;YACb,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,kBAAkB,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,sBAAsB,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAClH,OAAO,CAAC,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
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 `.env` into process.env (Node 22 built-in, no dotenv dep).
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 loadDotenv(): void;
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":"AAIA;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAejC"}
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 { existsSync } from "node:fs";
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 `.env` into process.env (Node 22 built-in, no dotenv dep).
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 loadDotenv() {
13
- const loader = process.loadEnvFile;
14
- if (!loader)
15
- return; // Node < 22
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,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAI,OAAgE,CAAC,WAAW,CAAC;IAC7F,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,YAAY;IAEjC,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,2BAA2B;IACjF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IAE/C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,IAAI,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;AACH,CAAC"}
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
- * Provision a sovereign agent: wallet fund mint the Agent object →
3
- * provision Walrus memory (MemWal account + delegate key) → link them on-chain.
4
- * Idempotent: re-running on an already-set-up agent is a no-op with guidance.
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
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAgGjD"}
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
- * Provision a sovereign agent: wallet fund mint the Agent object →
4
- * provision Walrus memory (MemWal account + delegate key) → link them on-chain.
5
- * Idempotent: re-running on an already-set-up agent is a no-op with guidance.
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
- const config = loadConfig();
9
- if (!config.packageId)
10
- throw new Error("SOFER_PACKAGE_ID not set");
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(` agent object: ${config.agentObjectId}`);
16
- console.log(` memory account: ${config.memwal.accountId}`);
17
- console.log("\nRun `sofer` to chat. To start fresh, unset SOFER_AGENT_OBJECT_ID,");
18
- console.log("MEMWAL_ACCOUNT_ID, MEMWAL_DELEGATE_KEY (and SOFER_SUI_SECRET_KEY) in .env.");
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
- // 1. Wallet (reuse if provided, else generate a fresh one).
22
- const keypair = process.env.SOFER_SUI_SECRET_KEY
23
- ? keypairFromSecret(process.env.SOFER_SUI_SECRET_KEY)
24
- : generateKeypair();
25
- const secret = keypair.getSecretKey();
26
- const address = keypair.toSuiAddress();
27
- console.log(`• wallet: ${address}`);
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
- // 2. Fund (testnet/devnet only).
30
- if ((await suiBalance(sui, address)) === 0n && config.network !== "mainnet") {
31
- console.log("• funding from faucet…");
32
- try {
33
- await requestFaucet(config.network, address);
34
- await waitForBalance(sui, address, 1n);
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
- catch (e) {
37
- console.warn(` faucet failed (${String(e).slice(0, 60)}); fund ${address} at https://faucet.sui.io/ and re-run`);
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(`• reusing agent object: ${agentId}`);
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("• minting agent identity object…");
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
- // 4. Provision Walrus memory — unless this wallet already has an account.
108
+ // ── 6. Provision MemWal memory ────────────────────────────────────────
53
109
  let accountId = config.memwal.accountId;
54
110
  let delegateKey = config.memwal.delegateKey;
55
- if (accountId && delegateKey) {
56
- console.log(`• reusing memory account: ${accountId}`);
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(` memory account: ${accountId}`);
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 Sui wallet already owns a MemWal account (one per address).\n" +
76
- " Reuse it: set MEMWAL_ACCOUNT_ID and MEMWAL_DELEGATE_KEY in .env, then run `sofer`.\n" +
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
- // 5. Link the memory account into the agent object on-chain.
83
- console.log("• linking memory account on-chain…");
84
- await agents.setMemoryAccount(keypair, agentId, accountId);
85
- console.log("\n✅ Agent ready. Persist these in your .env (keep them secret):\n");
86
- console.log(`SOFER_SUI_SECRET_KEY=${secret}`);
87
- console.log(`SOFER_AGENT_OBJECT_ID=${agentId}`);
88
- console.log(`MEMWAL_ACCOUNT_ID=${accountId}`);
89
- console.log(`MEMWAL_DELEGATE_KEY=${delegateKey}`);
90
- console.log("\nThen run `sofer` to chat.");
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":"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"}
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 the environment, with actionable errors. */
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
@@ -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;AAEtD,0EAA0E;AAC1E,wBAAgB,cAAc,IAAI,YAAY,CAU7C"}
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
- /** Pull required secrets from the environment, with actionable errors. */
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 = process.env.SOFER_SUI_SECRET_KEY;
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 set — run `sofer init` or add it to .env");
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 setadd it to .env");
9
+ throw new Error("ANTHROPIC_API_KEY not foundrun `sofer init` first (creates ~/.sofer/brain.key)");
10
10
  }
11
11
  return { suiSecretKey, anthropicApiKey };
12
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAEA,0EAA0E;AAC1E,MAAM,UAAU,cAAc;IAC5B,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IACtD,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACtD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACvF,CAAC;IACD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;AAC3C,CAAC"}
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.2.1",
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.2.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 the agent + provision Walrus memory
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
- Reads config + secrets from a .env file or the environment.`,
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("SOFER_PACKAGE_ID") || msg.includes("SOFER_SUI_SECRET_KEY") || msg.includes("ANTHROPIC_API_KEY")) {
73
- console.error("\nFirst time? Run `sofer setup` for the .env template, then `sofer init`.");
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 { existsSync } from "node:fs";
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 `.env` into process.env (Node 22 built-in, no dotenv dep).
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 loadDotenv(): void {
14
- const loader = (process as unknown as { loadEnvFile?: (path?: string) => void }).loadEnvFile;
15
- if (!loader) return; // Node < 22
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
- * Provision a sovereign agent: wallet fund mint the Agent object →
15
- * provision Walrus memory (MemWal account + delegate key) → link them on-chain.
16
- * Idempotent: re-running on an already-set-up agent is a no-op with guidance.
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
- const config = loadConfig();
20
- if (!config.packageId) throw new Error("SOFER_PACKAGE_ID not set");
21
- const net: "testnet" | "mainnet" = config.network === "mainnet" ? "mainnet" : "testnet";
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(` agent object: ${config.agentObjectId}`);
27
- console.log(` memory account: ${config.memwal.accountId}`);
28
- console.log("\nRun `sofer` to chat. To start fresh, unset SOFER_AGENT_OBJECT_ID,");
29
- console.log("MEMWAL_ACCOUNT_ID, MEMWAL_DELEGATE_KEY (and SOFER_SUI_SECRET_KEY) in .env.");
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
- // 1. Wallet (reuse if provided, else generate a fresh one).
34
- const keypair = process.env.SOFER_SUI_SECRET_KEY
35
- ? keypairFromSecret(process.env.SOFER_SUI_SECRET_KEY)
36
- : generateKeypair();
37
- const secret = keypair.getSecretKey();
38
- const address = keypair.toSuiAddress();
39
- console.log(`• wallet: ${address}`);
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
- // 2. Fund (testnet/devnet only).
44
- if ((await suiBalance(sui, address)) === 0n && config.network !== "mainnet") {
45
- console.log("• funding from faucet…");
46
- try {
47
- await requestFaucet(config.network, address);
48
- await waitForBalance(sui, address, 1n);
49
- } catch (e) {
50
- console.warn(
51
- ` faucet failed (${String(e).slice(0, 60)}); fund ${address} at https://faucet.sui.io/ and re-run`,
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(`• reusing agent object: ${agentId}`);
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("• minting agent identity object…");
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
- // 4. Provision Walrus memory — unless this wallet already has an account.
140
+ // ── 6. Provision MemWal memory ────────────────────────────────────────
74
141
  let accountId = config.memwal.accountId;
75
142
  let delegateKey = config.memwal.delegateKey;
76
- if (accountId && delegateKey) {
77
- console.log(`• reusing memory account: ${accountId}`);
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 provisionMemwal({
82
- ownerSecret: secret,
83
- packageId: config.memwal.packageId,
84
- registryId: config.memwal.registryId,
85
- network: net,
86
- label: "sofer-cli",
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(` memory account: ${accountId}`);
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 Sui wallet already owns a MemWal account (one per address).\n" +
96
- " Reuse it: set MEMWAL_ACCOUNT_ID and MEMWAL_DELEGATE_KEY in .env, then run `sofer`.\n" +
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
- // 5. Link the memory account into the agent object on-chain.
105
- console.log(" linking memory account on-chain…");
106
- await agents.setMemoryAccount(keypair, agentId, accountId);
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
- console.log("\n✅ Agent ready. Persist these in your .env (keep them secret):\n");
109
- console.log(`SOFER_SUI_SECRET_KEY=${secret}`);
110
- console.log(`SOFER_AGENT_OBJECT_ID=${agentId}`);
111
- console.log(`MEMWAL_ACCOUNT_ID=${accountId}`);
112
- console.log(`MEMWAL_DELEGATE_KEY=${delegateKey}`);
113
- console.log("\nThen run `sofer` to chat.");
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 the environment, with actionable errors. */
4
+ /** Pull required secrets from ~/.sofer/ or env vars, with actionable errors. */
4
5
  export function requireSecrets(): AgentSecrets {
5
- const suiSecretKey = process.env.SOFER_SUI_SECRET_KEY;
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 set — run `sofer init` or add it to .env");
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 setadd it to .env");
11
+ throw new Error("ANTHROPIC_API_KEY not foundrun `sofer init` first (creates ~/.sofer/brain.key)");
12
12
  }
13
13
  return { suiSecretKey, anthropicApiKey };
14
14
  }