@@ -20,21 +20,52 @@ function isDaemonAlive(): boolean {
2020 }
2121}
2222
23- async function ensureIdentity ( runtime : AgentRuntime , client : MachineClient ) : Promise < StoredIdentity > {
24- const existing = loadIdentity ( runtime ) ;
25- if ( existing ) return existing ;
23+ function missingIdentityMessage ( runtime : AgentRuntime ) : string {
24+ return [
25+ `No identity found for runtime "${ runtime } ".` ,
26+ "" ,
27+ "Create one explicitly with:" ,
28+ " ak identity create --username <username> [--name <name>]" ,
29+ "" ,
30+ "Choose the identity values yourself:" ,
31+ " --username required, user-like handle chosen by the agent" ,
32+ " --name optional full name shown in the UI" ,
33+ "" ,
34+ `This identity is reused for the same api-url + machine + runtime (${ runtime } ).` ,
35+ "" ,
36+ "Examples:" ,
37+ " ak identity create --username alex" ,
38+ ' ak identity create --username alex --name "Alex Chen"' ,
39+ ] . join ( "\n" ) ;
40+ }
2641
42+ async function restoreIdentity ( runtime : AgentRuntime , client : MachineClient ) : Promise < StoredIdentity | null > {
2743 const agents = ( await client . listAgents ( ) ) as Agent [ ] ;
28- const match = agents . find ( ( a ) => a . runtime === runtime && a . kind === "leader" ) ;
29- if ( match ) {
30- const identity : StoredIdentity = { agent_id : match . id , name : match . name , fingerprint : match . fingerprint } ;
31- saveIdentity ( runtime , identity ) ;
32- return identity ;
44+ const leaders = agents . filter ( ( agent ) => agent . kind === "leader" && agent . runtime === runtime ) ;
45+ if ( leaders . length !== 1 ) return null ;
46+ const leader = leaders [ 0 ] ;
47+ const identity : StoredIdentity = { agent_id : leader . id , name : leader . name , fingerprint : leader . fingerprint } ;
48+ saveIdentity ( runtime , identity ) ;
49+ return identity ;
50+ }
51+
52+ export async function createIdentity ( input : { runtime : AgentRuntime ; username : string ; name ?: string } ) : Promise < StoredIdentity > {
53+ const existing = loadIdentity ( input . runtime ) ;
54+ if ( existing ) {
55+ throw new Error ( `Identity for runtime "${ input . runtime } " already exists.` ) ;
3356 }
3457
35- const agent = ( await client . createAgent ( { name : runtime , runtime, kind : "leader" } ) ) as Agent ;
58+ const client = new MachineClient ( ) ;
59+ const payload : { username : string ; name ?: string ; runtime : AgentRuntime ; kind : "leader" } = {
60+ username : input . username ,
61+ runtime : input . runtime ,
62+ kind : "leader" ,
63+ } ;
64+ if ( input . name ) payload . name = input . name ;
65+
66+ const agent = ( await client . createAgent ( payload ) ) as Agent ;
3667 const identity : StoredIdentity = { agent_id : agent . id , name : agent . name , fingerprint : agent . fingerprint } ;
37- saveIdentity ( runtime , identity ) ;
68+ saveIdentity ( input . runtime , identity ) ;
3869 return identity ;
3970}
4071
@@ -72,13 +103,20 @@ export async function createClient(): Promise<ApiClient> {
72103 return cachedLeaderClient ;
73104 }
74105
106+ let identity = loadIdentity ( runtime ) ;
107+ const machineClient = new MachineClient ( ) ;
108+ if ( ! identity ) {
109+ identity = await restoreIdentity ( runtime , machineClient ) ;
110+ }
111+ if ( ! identity ) {
112+ throw new Error ( missingIdentityMessage ( runtime ) ) ;
113+ }
114+
75115 if ( ! isDaemonAlive ( ) ) {
76116 throw new Error ( "Daemon is not running. Start it with: ak start" ) ;
77117 }
78118
79- // First call — auto-init leader session
80- const machineClient = new MachineClient ( ) ;
81- const identity = await ensureIdentity ( runtime , machineClient ) ;
119+ // First call — create a leader session for an existing identity
82120 const { publicKey, privateKey } = ( await crypto . subtle . generateKey ( { name : "Ed25519" } as any , true , [ "sign" , "verify" ] ) ) as CryptoKeyPair ;
83121 const pubJwk = await crypto . subtle . exportKey ( "jwk" , publicKey ) ;
84122 const privJwk = await crypto . subtle . exportKey ( "jwk" , privateKey ) ;
0 commit comments