ethagent 2.1.1 → 2.2.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/package.json +1 -1
- package/src/auth/openaiOAuth/credentials.ts +47 -0
- package/src/auth/openaiOAuth/crypto.ts +23 -0
- package/src/auth/openaiOAuth/index.ts +238 -0
- package/src/auth/openaiOAuth/landingPage.ts +125 -0
- package/src/auth/openaiOAuth/listener.ts +151 -0
- package/src/auth/openaiOAuth/refresh.ts +70 -0
- package/src/auth/openaiOAuth/shared.ts +115 -0
- package/src/chat/chatSessionState.ts +2 -1
- package/src/chat/commands.ts +2 -1
- package/src/identity/ens/agentRecords.ts +5 -19
- package/src/identity/ens/ensAutomation/setup.ts +0 -1
- package/src/identity/ens/ensAutomation/types.ts +0 -1
- package/src/identity/hub/OperationalRoutes.tsx +2 -11
- package/src/identity/hub/components/IdentitySummary.tsx +8 -3
- package/src/identity/hub/components/MenuScreen.tsx +1 -2
- package/src/identity/hub/components/menuFlagsFromReconciliation.ts +1 -3
- package/src/identity/hub/effects/ens/transactions.ts +15 -15
- package/src/identity/hub/effects/index.ts +0 -1
- package/src/identity/hub/effects/profile/profileState.ts +12 -4
- package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +37 -159
- package/src/identity/hub/effects/rebackup/runRebackup.ts +2 -2
- package/src/identity/hub/effects/restoreAdmin.ts +2 -61
- package/src/identity/hub/effects/shared/sync.ts +3 -44
- package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +1 -39
- package/src/identity/hub/flows/custody/custodyFlowActions.ts +5 -3
- package/src/identity/hub/flows/custody/custodyFlowTypes.ts +1 -1
- package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +80 -175
- package/src/identity/hub/flows/ens/EnsEditFlow.tsx +20 -75
- package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +16 -56
- package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +0 -18
- package/src/identity/hub/flows/ens/EnsEditRunners.tsx +0 -136
- package/src/identity/hub/flows/ens/EnsEditShared.tsx +5 -4
- package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +56 -205
- package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +7 -0
- package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +0 -31
- package/src/identity/hub/flows/ens/ensEditCopy.ts +1 -1
- package/src/identity/hub/flows/ens/ensEditTypes.ts +6 -20
- package/src/identity/hub/flows/profile/EditProfileFlow.tsx +7 -0
- package/src/identity/hub/flows/restore/RestoreFlow.tsx +5 -5
- package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +0 -1
- package/src/identity/hub/reconciliation/agentReconciliation/run.ts +1 -34
- package/src/identity/hub/reconciliation/agentReconciliation/types.ts +0 -4
- package/src/identity/hub/reconciliation/index.ts +0 -7
- package/src/identity/hub/reconciliation/walletSetup.ts +1 -194
- package/src/identity/wallet/browserWallet/types.ts +0 -5
- package/src/identity/wallet/page/copy.ts +1 -31
- package/src/identity/wallet/walletPurposeCompat.ts +0 -2
- package/src/models/ModelPicker.tsx +246 -8
- package/src/models/catalog.ts +28 -1
- package/src/models/modelPickerOptions.ts +15 -1
- package/src/providers/openai-responses-format.ts +156 -0
- package/src/providers/openai-responses.ts +276 -0
- package/src/providers/registry.ts +85 -8
- package/src/runtime/systemPrompt.ts +1 -1
- package/src/runtime/turn.ts +0 -1
- package/src/storage/secrets.ts +4 -1
- package/src/tools/privateContinuityEditTool.ts +6 -0
- package/src/utils/openExternal.ts +20 -10
- package/src/identity/ens/ensRegistration.ts +0 -199
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export const OPENAI_OAUTH_ISSUER = 'https://auth.openai.com'
|
|
2
|
+
export const OPENAI_OAUTH_TOKEN_URL = `${OPENAI_OAUTH_ISSUER}/oauth/token`
|
|
3
|
+
export const OPENAI_OAUTH_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann'
|
|
4
|
+
export const OPENAI_OAUTH_CALLBACK_PORT = 1455
|
|
5
|
+
export const OPENAI_OAUTH_SCOPE =
|
|
6
|
+
'openid profile email offline_access api.connectors.read api.connectors.invoke'
|
|
7
|
+
export const OPENAI_OAUTH_ORIGINATOR = 'codex_cli_rs'
|
|
8
|
+
export const OPENAI_API_KEY_TOKEN_NAME = 'openai-api-key'
|
|
9
|
+
export const OPENAI_ID_TOKEN_SUBJECT_TYPE =
|
|
10
|
+
'urn:ietf:params:oauth:token-type:id_token'
|
|
11
|
+
export const OPENAI_TOKEN_EXCHANGE_GRANT =
|
|
12
|
+
'urn:ietf:params:oauth:grant-type:token-exchange'
|
|
13
|
+
|
|
14
|
+
export function asTrimmedString(value: unknown): string | undefined {
|
|
15
|
+
if (typeof value !== 'string') return undefined
|
|
16
|
+
const trimmed = value.trim()
|
|
17
|
+
return trimmed ? trimmed : undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function decodeJwtPayload(
|
|
21
|
+
token: string,
|
|
22
|
+
): Record<string, unknown> | undefined {
|
|
23
|
+
const parts = token.split('.')
|
|
24
|
+
if (parts.length < 2) return undefined
|
|
25
|
+
const segment = parts[1]
|
|
26
|
+
if (!segment) return undefined
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const normalized = segment.replace(/-/g, '+').replace(/_/g, '/')
|
|
30
|
+
const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4)
|
|
31
|
+
const json = Buffer.from(padded, 'base64').toString('utf8')
|
|
32
|
+
const parsed = JSON.parse(json) as unknown
|
|
33
|
+
return parsed && typeof parsed === 'object'
|
|
34
|
+
? (parsed as Record<string, unknown>)
|
|
35
|
+
: undefined
|
|
36
|
+
} catch {
|
|
37
|
+
return undefined
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function parseChatgptAccountId(
|
|
42
|
+
token: string | undefined,
|
|
43
|
+
): string | undefined {
|
|
44
|
+
if (!token) return undefined
|
|
45
|
+
|
|
46
|
+
const payload = decodeJwtPayload(token)
|
|
47
|
+
const nestedAuthRaw = payload?.['https://api.openai.com/auth']
|
|
48
|
+
const nestedAuth =
|
|
49
|
+
nestedAuthRaw && typeof nestedAuthRaw === 'object'
|
|
50
|
+
? (nestedAuthRaw as Record<string, unknown>)
|
|
51
|
+
: undefined
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
asTrimmedString(
|
|
55
|
+
nestedAuth?.chatgpt_account_id ??
|
|
56
|
+
payload?.['https://api.openai.com/auth.chatgpt_account_id'] ??
|
|
57
|
+
payload?.chatgpt_account_id,
|
|
58
|
+
) ?? undefined
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function escapeHtml(value: string): string {
|
|
63
|
+
return value.replace(/[&<>"']/g, char => {
|
|
64
|
+
switch (char) {
|
|
65
|
+
case '&':
|
|
66
|
+
return '&'
|
|
67
|
+
case '<':
|
|
68
|
+
return '<'
|
|
69
|
+
case '>':
|
|
70
|
+
return '>'
|
|
71
|
+
case '"':
|
|
72
|
+
return '"'
|
|
73
|
+
case '\'':
|
|
74
|
+
return '''
|
|
75
|
+
default:
|
|
76
|
+
return char
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function exchangeIdTokenForApiKey(idToken: string): Promise<string> {
|
|
82
|
+
const body = new URLSearchParams({
|
|
83
|
+
grant_type: OPENAI_TOKEN_EXCHANGE_GRANT,
|
|
84
|
+
client_id: OPENAI_OAUTH_CLIENT_ID,
|
|
85
|
+
requested_token: OPENAI_API_KEY_TOKEN_NAME,
|
|
86
|
+
subject_token: idToken,
|
|
87
|
+
subject_token_type: OPENAI_ID_TOKEN_SUBJECT_TYPE,
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const response = await fetch(OPENAI_OAUTH_TOKEN_URL, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
93
|
+
body,
|
|
94
|
+
signal: AbortSignal.timeout(15_000),
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
const bodyText = await response.text().catch(() => '')
|
|
99
|
+
throw new Error(
|
|
100
|
+
bodyText.trim()
|
|
101
|
+
? `OpenAI API key exchange failed (${response.status}): ${bodyText.trim()}`
|
|
102
|
+
: `OpenAI API key exchange failed with status ${response.status}.`,
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const payload = (await response.json()) as { access_token?: string }
|
|
107
|
+
const apiKey = asTrimmedString(payload.access_token)
|
|
108
|
+
if (!apiKey) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
'OpenAI API key exchange completed, but no key was returned.',
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return apiKey
|
|
115
|
+
}
|
|
@@ -5,6 +5,7 @@ import type { MessageRow } from './MessageList.js'
|
|
|
5
5
|
import type { ModelPickerSelection } from '../models/ModelPicker.js'
|
|
6
6
|
import { sessionMessagesToRows } from './chatScreenUtils.js'
|
|
7
7
|
import { formatModelDisplayName } from '../models/modelDisplay.js'
|
|
8
|
+
import { providerDisplayName } from '../models/modelPickerOptions.js'
|
|
8
9
|
|
|
9
10
|
export type ModelSelectionResolution =
|
|
10
11
|
| { kind: 'noop' }
|
|
@@ -69,7 +70,7 @@ export function resolveModelSelection(
|
|
|
69
70
|
return {
|
|
70
71
|
kind: 'switch',
|
|
71
72
|
config: nextConfig,
|
|
72
|
-
notice: `${selection.keyJustSet ? `${selection.provider} key saved.` : `${selection.provider} ready.`} Now using ${nextConfig.provider} · ${formatModelDisplayName(nextConfig.provider, nextConfig.model, { maxLength: 64 })}.`,
|
|
73
|
+
notice: `${selection.keyJustSet ? `${providerDisplayName(selection.provider)} key saved.` : `${providerDisplayName(selection.provider)} ready.`} Now using ${providerDisplayName(nextConfig.provider)} · ${formatModelDisplayName(nextConfig.provider, nextConfig.model, { maxLength: 64 })}.`,
|
|
73
74
|
tone: 'dim',
|
|
74
75
|
}
|
|
75
76
|
}
|
package/src/chat/commands.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { setCwd } from '../runtime/cwd.js'
|
|
|
19
19
|
import type { SessionMode } from '../runtime/sessionMode.js'
|
|
20
20
|
import type { ContextUsage } from '../runtime/compaction.js'
|
|
21
21
|
import { formatModelDisplayName } from '../models/modelDisplay.js'
|
|
22
|
+
import { providerDisplayName } from '../models/modelPickerOptions.js'
|
|
22
23
|
import type { McpManager } from '../mcp/manager.js'
|
|
23
24
|
|
|
24
25
|
export type IdentityRequestAction =
|
|
@@ -210,7 +211,7 @@ const COMMANDS: CommandSpec[] = [
|
|
|
210
211
|
}
|
|
211
212
|
await saveConfig(next)
|
|
212
213
|
ctx.onReplaceConfig(next)
|
|
213
|
-
return { kind: 'note', text: `Now using ${next.provider} · ${formatModelDisplayName(next.provider, name, { maxLength: 64 })}.` }
|
|
214
|
+
return { kind: 'note', text: `Now using ${providerDisplayName(next.provider)} · ${formatModelDisplayName(next.provider, name, { maxLength: 64 })}.` }
|
|
214
215
|
},
|
|
215
216
|
},
|
|
216
217
|
{
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
export const AGENT_RECORD_KEYS = {
|
|
2
|
-
token:
|
|
3
|
-
profile: 'org.ethagent.profile',
|
|
2
|
+
token: 'org.ethagent.token',
|
|
4
3
|
} as const
|
|
5
4
|
|
|
6
5
|
type AgentRecordKey = typeof AGENT_RECORD_KEYS[keyof typeof AGENT_RECORD_KEYS]
|
|
7
6
|
|
|
8
7
|
export const AGENT_RECORD_KEY_LIST: readonly AgentRecordKey[] = [
|
|
9
8
|
AGENT_RECORD_KEYS.token,
|
|
10
|
-
AGENT_RECORD_KEYS.profile,
|
|
11
9
|
] as const
|
|
12
10
|
|
|
13
11
|
export const AGENT_RECORD_READ_KEY_LIST: readonly string[] = AGENT_RECORD_KEY_LIST
|
|
14
12
|
|
|
15
13
|
export type AgentEnsRecords = {
|
|
16
14
|
token?: string
|
|
17
|
-
profile?: string
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
export type AgentEnsRecordState = AgentEnsRecords
|
|
@@ -28,19 +25,16 @@ export type AgentRecordDiff = {
|
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
const FIELD_FOR_KEY: Record<AgentRecordKey, keyof AgentEnsRecords> = {
|
|
31
|
-
[AGENT_RECORD_KEYS.token]:
|
|
32
|
-
[AGENT_RECORD_KEYS.profile]: 'profile',
|
|
28
|
+
[AGENT_RECORD_KEYS.token]: 'token',
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
const LABEL_FOR_FIELD: Record<keyof AgentEnsRecordState, string> = {
|
|
36
|
-
token:
|
|
37
|
-
profile: 'Agent profile',
|
|
32
|
+
token: 'Agent token',
|
|
38
33
|
}
|
|
39
34
|
|
|
40
35
|
export function recordsFromTextMap(text: Record<string, string>): AgentEnsRecordState {
|
|
41
36
|
return {
|
|
42
|
-
token:
|
|
43
|
-
profile: text[AGENT_RECORD_KEYS.profile] ?? '',
|
|
37
|
+
token: text[AGENT_RECORD_KEYS.token] ?? '',
|
|
44
38
|
}
|
|
45
39
|
}
|
|
46
40
|
|
|
@@ -71,11 +65,7 @@ export function recordLabel(field: keyof AgentEnsRecordState): string {
|
|
|
71
65
|
return LABEL_FOR_FIELD[field]
|
|
72
66
|
}
|
|
73
67
|
|
|
74
|
-
export function formatRecordValue(
|
|
75
|
-
if (field === 'profile' && value.startsWith('ipfs://')) {
|
|
76
|
-
const cid = value.slice('ipfs://'.length)
|
|
77
|
-
return cid.length > 18 ? `ipfs://${cid.slice(0, 10)}...${cid.slice(-6)}` : value
|
|
78
|
-
}
|
|
68
|
+
export function formatRecordValue(_field: keyof AgentEnsRecordState, value: string): string {
|
|
79
69
|
return value
|
|
80
70
|
}
|
|
81
71
|
|
|
@@ -83,14 +73,10 @@ export function buildAgentEnsRecords(args: {
|
|
|
83
73
|
chainId: number
|
|
84
74
|
identityRegistryAddress: string
|
|
85
75
|
agentId: string | undefined
|
|
86
|
-
agentCardCid: string | undefined
|
|
87
76
|
}): AgentEnsRecords {
|
|
88
77
|
const records: AgentEnsRecords = {}
|
|
89
78
|
if (args.agentId) {
|
|
90
79
|
records.token = `eip155:${args.chainId}:${args.identityRegistryAddress.toLowerCase()}:${args.agentId}`
|
|
91
80
|
}
|
|
92
|
-
if (args.agentCardCid) {
|
|
93
|
-
records.profile = `ipfs://${args.agentCardCid}`
|
|
94
|
-
}
|
|
95
81
|
return records
|
|
96
82
|
}
|
|
@@ -230,7 +230,6 @@ export async function preflightEnsSetup(args: EnsSetupPreflightArgs): Promise<En
|
|
|
230
230
|
chainId: args.registry.chainId,
|
|
231
231
|
identityRegistryAddress: args.registry.identityRegistryAddress,
|
|
232
232
|
agentId: String(args.agentId),
|
|
233
|
-
agentCardCid: args.agentCardCid,
|
|
234
233
|
})
|
|
235
234
|
if (currentRecords.token && nextRecords.token && currentRecords.token !== nextRecords.token) {
|
|
236
235
|
return manual(args, {
|
|
@@ -2,9 +2,6 @@ import React from 'react'
|
|
|
2
2
|
import { hasPendingPublish } from './model/continuity.js'
|
|
3
3
|
import type { ProfileUpdates } from './identityHubReducer.js'
|
|
4
4
|
import { clearPinataJwt, savePinataJwt } from '../storage/pinataJwt.js'
|
|
5
|
-
import {
|
|
6
|
-
runFixRecordsSubmit,
|
|
7
|
-
} from './effects/restoreAdmin.js'
|
|
8
5
|
import {
|
|
9
6
|
runRebackupStorageSubmit,
|
|
10
7
|
} from './effects/rebackup/runRebackup.js'
|
|
@@ -203,11 +200,13 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
|
|
|
203
200
|
<IdentityHubEnsFlow
|
|
204
201
|
step={step}
|
|
205
202
|
walletSession={walletSession}
|
|
203
|
+
reconciliation={reconciliation}
|
|
206
204
|
onSetStep={setStep}
|
|
207
205
|
onBack={back}
|
|
208
206
|
onWalletReady={setWalletSession}
|
|
209
207
|
onTriggerRebackup={triggerRebackup}
|
|
210
208
|
onTriggerPublicProfileSave={triggerPublicProfileSave}
|
|
209
|
+
onWithdrawTokenForEns={currentStep => custodyFlow.beginWithdrawToken(currentStep, currentStep, 'ens')}
|
|
211
210
|
/>
|
|
212
211
|
)
|
|
213
212
|
}
|
|
@@ -240,14 +239,6 @@ export const IdentityHubOperationalRoutes: React.FC<IdentityHubOperationalRoutes
|
|
|
240
239
|
onManageOperatorWallets={() => {
|
|
241
240
|
setStep({ kind: 'manage-ens-operators', identity: step.identity, registry: step.registry, returnTo: step })
|
|
242
241
|
}}
|
|
243
|
-
onFixRecords={async plan => {
|
|
244
|
-
try {
|
|
245
|
-
await runFixRecordsSubmit({ identity: step.identity, registry: step.registry, plan, callbacks })
|
|
246
|
-
setStep({ ...step })
|
|
247
|
-
} catch (err: unknown) {
|
|
248
|
-
handleStepError(err, step)
|
|
249
|
-
}
|
|
250
|
-
}}
|
|
251
242
|
onPrepareTransfer={openTokenTransferFlow}
|
|
252
243
|
onBack={back}
|
|
253
244
|
/>
|
|
@@ -25,11 +25,12 @@ interface IdentitySummaryProps {
|
|
|
25
25
|
config?: EthagentConfig
|
|
26
26
|
workingStatus?: ContinuityWorkingTreeStatus | null
|
|
27
27
|
hideLocalChanges?: boolean
|
|
28
|
+
hideHeader?: boolean
|
|
28
29
|
tokenLinked?: boolean
|
|
29
30
|
onchainOwner?: string
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, config, workingStatus, hideLocalChanges = false, tokenLinked = true, onchainOwner }) => {
|
|
33
|
+
export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, config, workingStatus, hideLocalChanges = false, hideHeader = false, tokenLinked = true, onchainOwner }) => {
|
|
33
34
|
if (!identity) {
|
|
34
35
|
return (
|
|
35
36
|
<Text color={theme.dim}>No agent yet. Create or load one.</Text>
|
|
@@ -60,8 +61,12 @@ export const IdentitySummary: React.FC<IdentitySummaryProps> = ({ identity, conf
|
|
|
60
61
|
|
|
61
62
|
return (
|
|
62
63
|
<Box flexDirection="column">
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
{hideHeader ? null : (
|
|
65
|
+
<>
|
|
66
|
+
<Text color={theme.accentPeriwinkle} bold>{stateName || 'Active Agent'}</Text>
|
|
67
|
+
<Text color={identity.agentId ? theme.text : theme.dim} bold={Boolean(identity.agentId)}>{tokenLine}</Text>
|
|
68
|
+
</>
|
|
69
|
+
)}
|
|
65
70
|
<Text>
|
|
66
71
|
<Text color={theme.dim}>{'ENS'.padEnd(12)}</Text>
|
|
67
72
|
{ensStatus.kind === 'linked'
|
|
@@ -85,7 +85,7 @@ export const MenuScreen: React.FC<MenuScreenProps> = ({
|
|
|
85
85
|
? menuFlagsFromReconciliation(reconciliation, perspective)
|
|
86
86
|
: (perspective === 'operator'
|
|
87
87
|
? menuFlagsFromReconciliation({
|
|
88
|
-
token: 'unknown', custody: 'unknown', agentUri: 'unknown',
|
|
88
|
+
token: 'unknown', custody: 'unknown', agentUri: 'unknown',
|
|
89
89
|
vault: 'unknown', workingTree: 'unknown', rpc: 'reachable', driftCount: 0, lastCheckedAt: '',
|
|
90
90
|
}, perspective)
|
|
91
91
|
: null)
|
|
@@ -223,7 +223,6 @@ function renderReconciliationBanner(r: AgentReconciliation, identity: EthagentId
|
|
|
223
223
|
if (r.custody === 'mid-flow-uri-pending') lines.push('Advanced setup pending. Open Custody Mode to finish.')
|
|
224
224
|
if (r.agentUri === 'local-newer') lines.push('Local state newer than chain. Save Snapshot Now to publish.')
|
|
225
225
|
if (r.agentUri === 'chain-newer') lines.push('Onchain agentURI is newer than local. Refetch Latest.')
|
|
226
|
-
if (r.ensRecords === 'drift') lines.push('ENS records out of sync. Open Custody Mode to Fix Records.')
|
|
227
226
|
if (r.vault === 'missing') lines.push('Recorded vault address has no contract at it. Open Custody Mode to redeploy.')
|
|
228
227
|
if (r.workingTree === 'dirty') lines.push('Local edits pending. Save Snapshot Now to publish.')
|
|
229
228
|
return (
|
|
@@ -30,7 +30,7 @@ export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective:
|
|
|
30
30
|
prepareTransferReason = 'Token is in the vault. Withdraw it first in Custody Mode.'
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const custodyAsterisk = r.custody === 'mid-flow-uri-pending' || r.
|
|
33
|
+
const custodyAsterisk = r.custody === 'mid-flow-uri-pending' || r.vault === 'missing'
|
|
34
34
|
let custodyHint: string | undefined
|
|
35
35
|
if (isOperator) {
|
|
36
36
|
custodyHint = undefined
|
|
@@ -38,8 +38,6 @@ export function menuFlagsFromReconciliation(r: AgentReconciliation, perspective:
|
|
|
38
38
|
custodyHint = 'Advanced setup pending. Open to finish.'
|
|
39
39
|
} else if (r.vault === 'missing') {
|
|
40
40
|
custodyHint = 'Vault contract not found. Open to redeploy.'
|
|
41
|
-
} else if (r.ensRecords === 'drift') {
|
|
42
|
-
custodyHint = 'ENS records out of sync. Open to fix.'
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
const custodyModeReason = isOperator
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type { Address, Hex, PublicClient } from 'viem'
|
|
2
|
-
import {
|
|
3
|
-
DEFAULT_ETHEREUM_RPC_URL,
|
|
4
|
-
RegisterAgentPreflightError,
|
|
5
|
-
createErc8004PublicClient,
|
|
6
|
-
supportedErc8004ChainForId,
|
|
7
|
-
} from '../../../registry/erc8004.js'
|
|
8
|
-
import { encodeSetEthagentTextRecords } from '../../../ens/ensLookup.js'
|
|
9
|
-
import { encodeEnsRecordsTransaction, encodeEnsRegistryTransaction, type EnsSetupPlan } from '../../../ens/ensAutomation.js'
|
|
10
|
-
import type { AgentEnsRecordState, AgentEnsRecords } from '../../../ens/agentRecords.js'
|
|
11
|
-
import { changedRecords } from '../../../ens/agentRecords.js'
|
|
12
|
-
import { sendBrowserWalletTransaction, type BrowserWalletSession, type WalletPurpose } from '../../../wallet/browserWallet.js'
|
|
1
|
+
import type { Address, Hex, PublicClient } from 'viem'
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_ETHEREUM_RPC_URL,
|
|
4
|
+
RegisterAgentPreflightError,
|
|
5
|
+
createErc8004PublicClient,
|
|
6
|
+
supportedErc8004ChainForId,
|
|
7
|
+
} from '../../../registry/erc8004.js'
|
|
8
|
+
import { encodeSetEthagentTextRecords } from '../../../ens/ensLookup.js'
|
|
9
|
+
import { encodeEnsRecordsTransaction, encodeEnsRegistryTransaction, type EnsSetupPlan } from '../../../ens/ensAutomation.js'
|
|
10
|
+
import type { AgentEnsRecordState, AgentEnsRecords } from '../../../ens/agentRecords.js'
|
|
11
|
+
import { changedRecords } from '../../../ens/agentRecords.js'
|
|
12
|
+
import { sendBrowserWalletTransaction, type BrowserWalletSession, type WalletPurpose } from '../../../wallet/browserWallet.js'
|
|
13
13
|
import type { EffectCallbacks } from '../types.js'
|
|
14
14
|
function chainLabel(chainId: number): string {
|
|
15
15
|
return supportedErc8004ChainForId(chainId)?.name ?? `chain ${chainId}`
|
|
@@ -84,9 +84,9 @@ export function ensRecordWritesForUpdate(args: {
|
|
|
84
84
|
clearRecords?: boolean
|
|
85
85
|
}): Record<string, string> {
|
|
86
86
|
if (args.clearRecords) {
|
|
87
|
-
return changedRecords(args.currentRecords ?? {}, { token: ''
|
|
87
|
+
return changedRecords(args.currentRecords ?? {}, { token: '' })
|
|
88
88
|
}
|
|
89
|
-
return changedRecords(args.currentRecords ?? { token: ''
|
|
89
|
+
return changedRecords(args.currentRecords ?? { token: '' }, args.records)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export async function runEnsSetupRegistryTransaction(args: {
|
|
@@ -187,7 +187,7 @@ export async function runEnsSetupRecordsTransaction(args: {
|
|
|
187
187
|
return { txHash: result.txHash }
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
export function createMainnetEnsPublicClient(): PublicClient {
|
|
190
|
+
export function createMainnetEnsPublicClient(): PublicClient {
|
|
191
191
|
return createErc8004PublicClient({
|
|
192
192
|
chainId: 1,
|
|
193
193
|
rpcUrl: DEFAULT_ETHEREUM_RPC_URL,
|
|
@@ -71,11 +71,19 @@ export function applyEnsValidationState(
|
|
|
71
71
|
throw new Error('Advanced custody requires owner wallet and operator wallet addresses')
|
|
72
72
|
}
|
|
73
73
|
const ownerAddress = getAddress(ownerAddressValue)
|
|
74
|
-
const existingOperators = mergeApprovedOperatorWallets(baseState.approvedOperatorWallets, profile.approvedOperatorWallets ?? [], { walletAddress: ownerAddress })
|
|
75
|
-
const approvedOperatorWallets = upsertApprovedOperatorWallet(existingOperators, getAddress(operatorWallet), { walletAddress: ownerAddress })
|
|
76
74
|
setOwnerAddressField(state, getAddress(ownerAddress))
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
const operatorTouched =
|
|
76
|
+
profile.approvedOperatorWallets !== undefined
|
|
77
|
+
|| profile.activeOperatorAddress !== undefined
|
|
78
|
+
if (operatorTouched) {
|
|
79
|
+
const existingOperators = mergeApprovedOperatorWallets(baseState.approvedOperatorWallets, profile.approvedOperatorWallets ?? [], { walletAddress: ownerAddress })
|
|
80
|
+
const approvedOperatorWallets = upsertApprovedOperatorWallet(existingOperators, getAddress(operatorWallet), { walletAddress: ownerAddress })
|
|
81
|
+
state.approvedOperatorWallets = approvedOperatorWallets
|
|
82
|
+
state.activeOperatorAddress = getAddress(operatorWallet)
|
|
83
|
+
} else {
|
|
84
|
+
state.approvedOperatorWallets = normalizeApprovedOperatorWallets(baseState.approvedOperatorWallets)
|
|
85
|
+
state.activeOperatorAddress = getAddress(operatorWallet)
|
|
86
|
+
}
|
|
79
87
|
return
|
|
80
88
|
}
|
|
81
89
|
clearOwnerAddressField(state)
|