lyra-ai-agent 0.1.3 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lyra-ai-agent",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "lyra CLI: a Sui-native, policy-bound AI finance agent. Real on-chain work gated by deterministic policy, simulation, and approval",
6
6
  "license": "MIT",
@@ -1,18 +1,10 @@
1
1
  import { existsSync } from 'node:fs'
2
- import { mkdir, writeFile } from 'node:fs/promises'
3
- import { join } from 'node:path'
4
2
  import { cancel, intro, isCancel, outro, password, select } from '@clack/prompts'
5
3
  import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'
6
- import {
7
- type LyraNetwork,
8
- agentPaths,
9
- defineConfig,
10
- placeholderAgentId,
11
- suiRpcUrl,
12
- } from 'lyra-core'
4
+ import { type LyraNetwork, agentPaths, suiRpcUrl } from 'lyra-core'
13
5
  import { keypairFromSecret } from 'lyra-plugin-onchain'
14
- import { DEFAULT_NETWORK, resolvePackageId } from '../config/defaults'
15
- import { writeConfigTs } from '../config/render'
6
+ import { DEFAULT_NETWORK } from '../config/defaults'
7
+ import { finalizeSetup } from '../config/setup'
16
8
  import { setDotenvVar } from '../util/dotenv'
17
9
  import { type SuiAgent, loadAgent, writeAgentKey } from '../util/sui-runtime'
18
10
  import { pickBrainModel } from './init/model-picker'
@@ -156,43 +148,15 @@ export async function runInit(opts?: InitOpts): Promise<void> {
156
148
  }
157
149
 
158
150
  const modelPick = await pickBrainModel()
159
-
160
- const agentId = placeholderAgentId(agent.address)
161
- const paths = agentPaths.agent(agentId)
162
- await mkdir(paths.dir, { recursive: true })
163
-
164
- // Telegram is env-driven on Sui (TELEGRAM_BOT_TOKEN). Auto-enable the plugin
165
- // when the token is present so `lyra` brings the DM gateway online.
166
151
  const telegramEnabled = !!process.env.TELEGRAM_BOT_TOKEN
167
152
 
168
- await seedStarterMemoryFiles({
169
- paths,
170
- network,
153
+ const { agentId, packageId } = await finalizeSetup({
171
154
  agentAddress: agent.address,
155
+ linkedOwner,
156
+ network,
172
157
  brainProvider: modelPick?.provider ?? null,
173
158
  brainModel: modelPick?.model ?? null,
174
159
  })
175
-
176
- const cfg = defineConfig({
177
- identity: {
178
- operator: linkedOwner,
179
- agent: agent.address,
180
- },
181
- network,
182
- storage: { network },
183
- brain: {
184
- provider: modelPick?.provider ?? null,
185
- model: modelPick?.model ?? null,
186
- },
187
- plugins: telegramEnabled ? ['onchain', 'system', 'telegram'] : ['onchain', 'system'],
188
- tools: {},
189
- imports: { claudeCode: true },
190
- })
191
- await writeConfigTs(configPath, cfg, {
192
- header: '// Regenerated by `lyra init`. Edit freely; type-safe.',
193
- })
194
-
195
- const packageId = resolvePackageId()
196
160
  const lines = [
197
161
  '',
198
162
  ` agent id ${agentId}`,
@@ -211,35 +175,3 @@ export async function runInit(opts?: InitOpts): Promise<void> {
211
175
  )
212
176
  outro(lines.join('\n'))
213
177
  }
214
-
215
- interface SeedStarterOpts {
216
- paths: ReturnType<typeof agentPaths.agent>
217
- network: LyraNetwork
218
- agentAddress: string
219
- brainProvider: string | null
220
- brainModel: string | null
221
- }
222
-
223
- /**
224
- * Seed `MEMORY.md`, `/agent/identity.md`, and `/agent/persona.md` so the
225
- * brain's first turn sees a parseable memory index and introduces itself.
226
- */
227
- async function seedStarterMemoryFiles(opts: SeedStarterOpts): Promise<void> {
228
- const memDir = opts.paths.memoryDir
229
- const agentMem = `${memDir}/agent`
230
- const userMem = `${memDir}/user`
231
- await mkdir(agentMem, { recursive: true })
232
- await mkdir(userMem, { recursive: true })
233
-
234
- const now = new Date().toISOString().slice(0, 10)
235
- const identity = `---\nname: identity\ndescription: Auto-written agent identity facts.\ntype: agent-identity\n---\n# Lyra identity\n\n- Name: Lyra\n- Agent address: ${opts.agentAddress} (${opts.network})\n- Created: ${now}\n${opts.brainProvider ? `- Brain provider: ${opts.brainProvider}\n` : ''}${opts.brainModel ? `- Brain model: ${opts.brainModel}\n` : ''}`
236
- const persona =
237
- '---\nname: persona\ndescription: Voice + behavior style.\ntype: agent-persona\n---\n# Persona\n\nI am Lyra, a Sui-native autonomous finance agent. I convert goals into policy-checked PTBs, execute only within my approved protocol scope, and store auditable memory and receipts with Walrus. Every value-moving action is checked by a deterministic policy (mirrored on-chain by lyra::policy) before it runs. I am direct, concise, and factual.\n'
238
- const profile =
239
- '---\nname: profile\ndescription: User profile (local only).\ntype: user\n---\n# User profile\n\n(empty, fills as we chat)\n'
240
-
241
- await writeFile(join(agentMem, 'identity.md'), identity, 'utf8')
242
- await writeFile(join(agentMem, 'persona.md'), persona, 'utf8')
243
- await writeFile(join(userMem, 'profile.md'), profile, 'utf8')
244
- await writeFile(opts.paths.memoryIndex, '# Lyra Memory Index\n\n', 'utf8')
245
- }
@@ -14,7 +14,11 @@
14
14
  * derives for that owner — so the CLI and the web operate one identical agent.
15
15
  */
16
16
 
17
+ import { existsSync } from 'node:fs'
18
+ import { agentPaths } from 'lyra-core'
17
19
  import { keypairFromSecret } from 'lyra-plugin-onchain'
20
+ import { DEFAULT_NETWORK } from '../config/defaults'
21
+ import { finalizeSetup } from '../config/setup'
18
22
  import { writeAgentKey } from '../util/sui-runtime'
19
23
 
20
24
  /** Default web origin; override with LYRA_WEB_URL for local/staging testing. */
@@ -150,6 +154,18 @@ export async function runLogin(): Promise<void> {
150
154
  console.log(`✓ Linked agent ${result.address} (same as your web wallet)`)
151
155
  console.log(` owner ${result.owner}`)
152
156
  console.log(` key ${result.keyPath}`)
157
+
158
+ // Login used to write ONLY the agent key, leaving `lyra` (chat) with no config
159
+ // to load. Write a runnable config (defaults) when one doesn't exist yet so the
160
+ // device-link is a complete setup; never clobber an existing config.
161
+ if (!existsSync(agentPaths.config)) {
162
+ await finalizeSetup({
163
+ agentAddress: result.address,
164
+ linkedOwner: result.owner,
165
+ network: DEFAULT_NETWORK,
166
+ })
167
+ console.log(` config ${agentPaths.config}`)
168
+ }
153
169
  console.log('')
154
170
  console.log('Next: `lyra` to chat · `lyra status` for health')
155
171
  } catch (e) {
@@ -0,0 +1,83 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises'
2
+ import { join } from 'node:path'
3
+ import { type LyraNetwork, agentPaths, defineConfig, placeholderAgentId } from 'lyra-core'
4
+ import { resolvePackageId } from './defaults'
5
+ import { writeConfigTs } from './render'
6
+
7
+ /**
8
+ * Write `~/.lyra/config.ts` + seed starter memory for an agent we already have
9
+ * (a fresh key, or one linked from the web). Shared by `lyra init` and
10
+ * `lyra login` so BOTH produce a complete, runnable setup — `lyra login` used to
11
+ * write only the agent key, leaving `lyra` (chat) without a config to load.
12
+ */
13
+ export async function finalizeSetup(opts: {
14
+ agentAddress: string
15
+ linkedOwner: string | null
16
+ network: LyraNetwork
17
+ brainProvider?: string | null
18
+ brainModel?: string | null
19
+ }): Promise<{ agentId: string; configPath: string; packageId: string }> {
20
+ const configPath = agentPaths.config
21
+ const agentId = placeholderAgentId(opts.agentAddress)
22
+ const paths = agentPaths.agent(agentId)
23
+ await mkdir(paths.dir, { recursive: true })
24
+
25
+ // Telegram is env-driven on Sui (TELEGRAM_BOT_TOKEN). Auto-enable the plugin
26
+ // when the token is present so `lyra` brings the DM gateway online.
27
+ const telegramEnabled = !!process.env.TELEGRAM_BOT_TOKEN
28
+
29
+ await seedStarterMemoryFiles({
30
+ paths,
31
+ network: opts.network,
32
+ agentAddress: opts.agentAddress,
33
+ brainProvider: opts.brainProvider ?? null,
34
+ brainModel: opts.brainModel ?? null,
35
+ })
36
+
37
+ const cfg = defineConfig({
38
+ identity: { operator: opts.linkedOwner, agent: opts.agentAddress },
39
+ network: opts.network,
40
+ storage: { network: opts.network },
41
+ brain: { provider: opts.brainProvider ?? null, model: opts.brainModel ?? null },
42
+ plugins: telegramEnabled ? ['onchain', 'system', 'telegram'] : ['onchain', 'system'],
43
+ tools: {},
44
+ imports: { claudeCode: true },
45
+ })
46
+ await writeConfigTs(configPath, cfg, {
47
+ header: '// Written by lyra (init / login). Edit freely; type-safe.',
48
+ })
49
+
50
+ return { agentId, configPath, packageId: resolvePackageId() }
51
+ }
52
+
53
+ interface SeedStarterOpts {
54
+ paths: ReturnType<typeof agentPaths.agent>
55
+ network: LyraNetwork
56
+ agentAddress: string
57
+ brainProvider: string | null
58
+ brainModel: string | null
59
+ }
60
+
61
+ /**
62
+ * Seed `MEMORY.md`, `/agent/identity.md`, and `/agent/persona.md` so the brain's
63
+ * first turn sees a parseable memory index and introduces itself.
64
+ */
65
+ export async function seedStarterMemoryFiles(opts: SeedStarterOpts): Promise<void> {
66
+ const memDir = opts.paths.memoryDir
67
+ const agentMem = `${memDir}/agent`
68
+ const userMem = `${memDir}/user`
69
+ await mkdir(agentMem, { recursive: true })
70
+ await mkdir(userMem, { recursive: true })
71
+
72
+ const now = new Date().toISOString().slice(0, 10)
73
+ const identity = `---\nname: identity\ndescription: Auto-written agent identity facts.\ntype: agent-identity\n---\n# Lyra identity\n\n- Name: Lyra\n- Agent address: ${opts.agentAddress} (${opts.network})\n- Created: ${now}\n${opts.brainProvider ? `- Brain provider: ${opts.brainProvider}\n` : ''}${opts.brainModel ? `- Brain model: ${opts.brainModel}\n` : ''}`
74
+ const persona =
75
+ '---\nname: persona\ndescription: Voice + behavior style.\ntype: agent-persona\n---\n# Persona\n\nI am Lyra, a Sui-native autonomous finance agent. I convert goals into policy-checked PTBs, execute only within my approved protocol scope, and store auditable memory and receipts with Walrus. Every value-moving action is checked by a deterministic policy (mirrored on-chain by lyra::policy) before it runs. I am direct, concise, and factual.\n'
76
+ const profile =
77
+ '---\nname: profile\ndescription: User profile (local only).\ntype: user\n---\n# User profile\n\n(empty, fills as we chat)\n'
78
+
79
+ await writeFile(join(agentMem, 'identity.md'), identity, 'utf8')
80
+ await writeFile(join(agentMem, 'persona.md'), persona, 'utf8')
81
+ await writeFile(join(userMem, 'profile.md'), profile, 'utf8')
82
+ await writeFile(opts.paths.memoryIndex, '# Lyra Memory Index\n\n', 'utf8')
83
+ }
package/src/index.ts CHANGED
@@ -160,6 +160,6 @@ main()
160
160
  process.exit(0)
161
161
  })
162
162
  .catch(e => {
163
- console.error('fatal:', (e as Error).message)
163
+ console.error('fatal:', (e as Error)?.message ?? e)
164
164
  process.exit(1)
165
165
  })