(&self)
| 117 | |
| 118 | impl Create { |
| 119 | async fn execute(&self) -> CliResult<()> { |
| 120 | let store = IdentityStore::open_default().map_err(|e| { |
| 121 | CliError::Internal(anyhow::anyhow!("Failed to open identity store: {e}")) |
| 122 | })?; |
| 123 | |
| 124 | // 1. The delegator. An agent cannot delegate — allowing it would make |
| 125 | // the chain of custody a graph and the revocation story unbounded. |
| 126 | let delegator = load_delegator(&store, self.identity.as_deref())?; |
| 127 | if delegator.identity_type.is_delegated() || delegator.identity_type.is_agent() { |
| 128 | return Err(CliError::InvalidArgument { |
| 129 | message: format!( |
| 130 | "'{}' is itself an agent identity and cannot delegate.\n \ |
| 131 | Pass a human identity with --identity <name>.", |
| 132 | delegator.name |
| 133 | ), |
| 134 | }); |
| 135 | } |
| 136 | |
| 137 | let agent_name = format!("{}+{}", delegator.name, self.name); |
| 138 | if store.exists_by_name(&agent_name) { |
| 139 | return Err(CliError::IdentityAlreadyExists(agent_name)); |
| 140 | } |
| 141 | |
| 142 | // 2. The server the certificate will be bound to. Resolved before |
| 143 | // signing because it is part of what gets signed. |
| 144 | let server_url = if self.local { |
| 145 | None |
| 146 | } else { |
| 147 | Some(crate::commands::client::apex_server_url( |
| 148 | self.server.as_deref(), |
| 149 | )?) |
| 150 | }; |
| 151 | |
| 152 | // 3. The agent's own keypair. |
| 153 | let keypair = KeyPair::generate(); |
| 154 | let mut agent_builder = Identity::builder(&agent_name) |
| 155 | .identity_type(IdentityType::Agent) |
| 156 | .usage(IdentityUsage::Bot) |
| 157 | .public_key(keypair.public.clone()) |
| 158 | .delegated_by(delegator.id) |
| 159 | .description(format!( |
| 160 | "Agent identity acting on behalf of {}", |
| 161 | delegator.name |
| 162 | )); |
| 163 | if let Some(email) = delegator.email.as_deref() { |
| 164 | agent_builder = agent_builder.email(plus_tag_email(email, &self.name)); |
| 165 | } |
| 166 | let agent = agent_builder |
| 167 | .build() |
| 168 | .map_err(|e| CliError::Internal(anyhow::anyhow!("Failed to build identity: {e}")))?; |
| 169 | |
| 170 | // 4. The scope, then the certificate. |
| 171 | let scope = self.build_scope(server_url.as_deref())?; |
| 172 | let expires = self |
| 173 | .expires |
| 174 | .as_deref() |
| 175 | .map(parse_duration) |
| 176 | .transpose()? |
no test coverage detected