ethagent 1.1.1 → 2.0.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/LICENSE +21 -21
- package/README.md +127 -29
- package/package.json +16 -9
- package/src/app/FirstRun.tsx +192 -146
- package/src/app/FirstRunTimeline.tsx +47 -0
- package/src/app/input/AppInputProvider.tsx +1 -1
- package/src/app/keybindings/KeybindingProvider.tsx +1 -1
- package/src/chat/ChatBottomPane.tsx +0 -1
- package/src/chat/ChatInput.tsx +6 -6
- package/src/chat/ChatScreen.tsx +43 -18
- package/src/chat/ContextLimitView.tsx +4 -4
- package/src/chat/ContinuityEditReviewView.tsx +11 -17
- package/src/chat/ConversationStack.tsx +3 -0
- package/src/chat/CopyPicker.tsx +0 -1
- package/src/chat/MessageList.tsx +62 -45
- package/src/chat/PermissionPrompt.tsx +13 -9
- package/src/chat/PlanApprovalView.tsx +3 -3
- package/src/chat/ResumeView.tsx +1 -4
- package/src/chat/RewindView.tsx +2 -2
- package/src/chat/TranscriptView.tsx +6 -0
- package/src/chat/chatInputState.ts +1 -1
- package/src/chat/chatScreenUtils.ts +22 -11
- package/src/chat/chatSessionState.ts +2 -2
- package/src/chat/chatTurnOrchestrator.ts +16 -81
- package/src/chat/commands.ts +1 -1
- package/src/chat/textCursor.ts +1 -1
- package/src/chat/transcriptViewport.ts +2 -7
- package/src/cli/ResetConfirmView.tsx +1 -1
- package/src/cli/main.tsx +9 -3
- package/src/cli/preview.tsx +0 -5
- package/src/cli/updateNotice.ts +5 -3
- package/src/identity/continuity/editor.ts +7 -107
- package/src/identity/continuity/envelope.ts +1048 -40
- package/src/identity/continuity/history.ts +4 -4
- package/src/identity/continuity/localBackup.ts +249 -0
- package/src/identity/continuity/privateEdit/apply.ts +170 -0
- package/src/identity/continuity/privateEdit/diff.ts +82 -0
- package/src/identity/continuity/privateEdit/files.ts +23 -0
- package/src/identity/continuity/privateEdit/types.ts +28 -0
- package/src/identity/continuity/privateEdit.ts +10 -298
- package/src/identity/continuity/publicSkills.ts +8 -9
- package/src/identity/continuity/snapshots.ts +17 -6
- package/src/identity/continuity/storage/defaults.ts +111 -0
- package/src/identity/continuity/storage/files.ts +72 -0
- package/src/identity/continuity/storage/markdown.ts +81 -0
- package/src/identity/continuity/storage/paths.ts +24 -0
- package/src/identity/continuity/storage/scaffold.ts +124 -0
- package/src/identity/continuity/storage/status.ts +86 -0
- package/src/identity/continuity/storage/types.ts +27 -0
- package/src/identity/continuity/storage.ts +32 -507
- package/src/identity/continuity/zipWriter.ts +95 -0
- package/src/identity/crypto/backupEnvelope.ts +14 -247
- package/src/identity/crypto/eth.ts +7 -7
- package/src/identity/ens/agentRecords.ts +96 -0
- package/src/identity/ens/ensAutomation/contracts.ts +38 -0
- package/src/identity/ens/ensAutomation/delete.ts +80 -0
- package/src/identity/ens/ensAutomation/names.ts +14 -0
- package/src/identity/ens/ensAutomation/operators.ts +29 -0
- package/src/identity/ens/ensAutomation/read.ts +114 -0
- package/src/identity/ens/ensAutomation/root.ts +63 -0
- package/src/identity/ens/ensAutomation/setup.ts +284 -0
- package/src/identity/ens/ensAutomation/transactions.ts +107 -0
- package/src/identity/ens/ensAutomation/types.ts +126 -0
- package/src/identity/ens/ensAutomation.ts +29 -0
- package/src/identity/ens/ensLookup/client.ts +43 -0
- package/src/identity/ens/ensLookup/constants.ts +26 -0
- package/src/identity/ens/ensLookup/discovery.ts +70 -0
- package/src/identity/ens/ensLookup/names.ts +34 -0
- package/src/identity/ens/ensLookup/records.ts +45 -0
- package/src/identity/ens/ensLookup/resolve.ts +75 -0
- package/src/identity/ens/ensLookup/tokenReference.ts +17 -0
- package/src/identity/ens/ensLookup/types.ts +38 -0
- package/src/identity/ens/ensLookup/validation.ts +72 -0
- package/src/identity/ens/ensLookup.ts +19 -0
- package/src/identity/ens/ensRegistration.ts +199 -0
- package/src/identity/ens/resolverDelegation.ts +48 -0
- package/src/identity/hub/IdentityHub.tsx +13 -815
- package/src/identity/hub/OperationalRoutes.tsx +370 -0
- package/src/identity/hub/Routes.tsx +361 -0
- package/src/identity/hub/advancedEnsValidation.ts +45 -0
- package/src/identity/hub/{screens → components}/DetailsScreen.tsx +14 -8
- package/src/identity/hub/{screens → components}/ErrorScreen.tsx +15 -5
- package/src/identity/hub/components/FlowTimeline.tsx +27 -0
- package/src/identity/hub/components/IdentitySummary.tsx +190 -0
- package/src/identity/hub/components/MenuScreen.tsx +237 -0
- package/src/identity/hub/{screens → components}/NetworkScreen.tsx +3 -3
- package/src/identity/hub/{screens/RebackupStorageScreen.tsx → components/PinataJwtInput.tsx} +21 -18
- package/src/identity/hub/components/UnlinkedIdentityScreen.tsx +76 -0
- package/src/identity/hub/{screens → components}/WalletApprovalScreen.tsx +9 -8
- package/src/identity/hub/components/menuFlagsFromReconciliation.ts +68 -0
- package/src/identity/hub/effects/create.ts +310 -0
- package/src/identity/hub/effects/ens/flows.ts +218 -0
- package/src/identity/hub/effects/ens/index.ts +11 -0
- package/src/identity/hub/effects/ens/transactions.ts +239 -0
- package/src/identity/hub/effects/index.ts +74 -0
- package/src/identity/hub/effects/profile/profileState.ts +173 -0
- package/src/identity/hub/effects/publicProfile/index.ts +5 -0
- package/src/identity/hub/effects/publicProfile/runPublicProfileSave.ts +646 -0
- package/src/identity/hub/effects/rebackup/index.ts +7 -0
- package/src/identity/hub/effects/rebackup/operatorVault.ts +378 -0
- package/src/identity/hub/effects/rebackup/runRebackup.ts +451 -0
- package/src/identity/hub/effects/receipts.ts +46 -0
- package/src/identity/hub/effects/restore/apply.ts +112 -0
- package/src/identity/hub/effects/restore/auth.ts +159 -0
- package/src/identity/hub/effects/restore/discover.ts +86 -0
- package/src/identity/hub/effects/restore/envelopes.ts +21 -0
- package/src/identity/hub/effects/restore/fetch.ts +25 -0
- package/src/identity/hub/effects/restore/index.ts +22 -0
- package/src/identity/hub/effects/restore/recovery.ts +135 -0
- package/src/identity/hub/effects/restore/resolve.ts +102 -0
- package/src/identity/hub/effects/restore/restoreEffects.ts +22 -0
- package/src/identity/hub/effects/restore/shared.ts +91 -0
- package/src/identity/hub/effects/restoreAdmin.ts +93 -0
- package/src/identity/hub/effects/shared/profilePrep.ts +139 -0
- package/src/identity/hub/effects/shared/snapshot.ts +336 -0
- package/src/identity/hub/effects/shared/sync.ts +190 -0
- package/src/identity/hub/effects/token-transfer/index.ts +6 -0
- package/src/identity/hub/effects/token-transfer/progress.ts +59 -0
- package/src/identity/hub/effects/token-transfer/runTokenTransfer.ts +299 -0
- package/src/identity/hub/effects/types.ts +53 -0
- package/src/identity/hub/effects/vault/preflight.ts +50 -0
- package/src/identity/hub/flows/continuity/ContinuityDashboardScreen.tsx +170 -0
- package/src/identity/hub/flows/continuity/RebackupStorageScreen.tsx +28 -0
- package/src/identity/hub/flows/continuity/RecoveryConfirmScreen.tsx +104 -0
- package/src/identity/hub/flows/continuity/SavePromptScreen.tsx +49 -0
- package/src/identity/hub/{screens → flows/create}/CreateFlow.tsx +61 -62
- package/src/identity/hub/flows/custody/CustodyEditFlow.tsx +347 -0
- package/src/identity/hub/flows/custody/custodyEffects.ts +321 -0
- package/src/identity/hub/flows/custody/custodyFlowActions.ts +236 -0
- package/src/identity/hub/flows/custody/custodyFlowEffects.ts +163 -0
- package/src/identity/hub/flows/custody/custodyFlowHelpers.ts +25 -0
- package/src/identity/hub/flows/custody/custodyFlowRoutes.tsx +239 -0
- package/src/identity/hub/flows/custody/custodyFlowTypes.ts +45 -0
- package/src/identity/hub/flows/custody/useCustodyFlow.tsx +25 -0
- package/src/identity/hub/flows/ens/EnsEditAdvancedScreens.tsx +336 -0
- package/src/identity/hub/flows/ens/EnsEditFlow.tsx +397 -0
- package/src/identity/hub/flows/ens/EnsEditMaintenanceScreens.tsx +332 -0
- package/src/identity/hub/flows/ens/EnsEditReviewScreens.tsx +471 -0
- package/src/identity/hub/flows/ens/EnsEditRunners.tsx +198 -0
- package/src/identity/hub/flows/ens/EnsEditShared.tsx +162 -0
- package/src/identity/hub/flows/ens/EnsEditSimpleScreens.tsx +518 -0
- package/src/identity/hub/flows/ens/IdentityHubEnsFlow.tsx +299 -0
- package/src/identity/hub/flows/ens/OperatorWalletsScreen.tsx +398 -0
- package/src/identity/hub/flows/ens/ensEditCopy.ts +117 -0
- package/src/identity/hub/flows/ens/ensEditTypes.ts +91 -0
- package/src/identity/hub/flows/profile/EditProfileFlow.tsx +271 -0
- package/src/identity/hub/flows/restore/RestoreFlow.tsx +324 -0
- package/src/identity/hub/flows/restore/useRestoreFlowEffects.ts +77 -0
- package/src/identity/hub/{screens → flows/settings}/StorageCredentialScreen.tsx +25 -43
- package/src/identity/hub/flows/token-transfer/IdentityHubTokenTransferFlow.tsx +162 -0
- package/src/identity/hub/flows/token-transfer/TokenTransferScreens.tsx +256 -0
- package/src/identity/hub/identityHubReducer.ts +166 -101
- package/src/identity/hub/model/continuity.ts +94 -0
- package/src/identity/hub/model/copy.ts +35 -0
- package/src/identity/hub/model/custody.ts +54 -0
- package/src/identity/hub/model/ens.ts +49 -0
- package/src/identity/hub/model/errors.ts +140 -0
- package/src/identity/hub/model/format.ts +15 -0
- package/src/identity/hub/model/identity.ts +94 -0
- package/src/identity/hub/model/network.ts +32 -0
- package/src/identity/hub/model/transfer.ts +57 -0
- package/src/identity/hub/operatorWallets.ts +131 -0
- package/src/identity/hub/reconciliation/agentReconciliation/hook.ts +46 -0
- package/src/identity/hub/reconciliation/agentReconciliation/ownership.ts +129 -0
- package/src/identity/hub/reconciliation/agentReconciliation/run.ts +302 -0
- package/src/identity/hub/reconciliation/agentReconciliation/types.ts +17 -0
- package/src/identity/hub/reconciliation/index.ts +21 -0
- package/src/identity/hub/reconciliation/useAgentReconciliation.ts +10 -0
- package/src/identity/hub/reconciliation/walletSetup.ts +220 -0
- package/src/identity/hub/txGuard.ts +51 -0
- package/src/identity/hub/types.ts +17 -0
- package/src/identity/hub/useIdentityHubContinuity.ts +136 -0
- package/src/identity/hub/useIdentityHubController.ts +396 -0
- package/src/identity/hub/useIdentityHubSideEffects.ts +309 -0
- package/src/identity/hub/utils.ts +79 -0
- package/src/identity/identityCompat.ts +34 -0
- package/src/identity/profile/agentIcon.ts +61 -0
- package/src/identity/profile/imagePicker.ts +12 -12
- package/src/identity/registry/erc8004/abi.ts +14 -0
- package/src/identity/registry/erc8004/chains.ts +150 -0
- package/src/identity/registry/erc8004/client.ts +11 -0
- package/src/identity/registry/erc8004/discovery.ts +511 -0
- package/src/identity/registry/erc8004/metadata.ts +335 -0
- package/src/identity/registry/erc8004/ownership.ts +121 -0
- package/src/identity/registry/erc8004/preflight.ts +123 -0
- package/src/identity/registry/erc8004/transactions.ts +77 -0
- package/src/identity/registry/erc8004/types.ts +88 -0
- package/src/identity/registry/erc8004/uri.ts +59 -0
- package/src/identity/registry/erc8004/utils.ts +58 -0
- package/src/identity/registry/erc8004.ts +53 -1106
- package/src/identity/registry/fieldParsers.ts +28 -0
- package/src/identity/registry/operatorVault/bytecode.ts +98 -0
- package/src/identity/registry/operatorVault/constants.ts +38 -0
- package/src/identity/registry/operatorVault/read.ts +246 -0
- package/src/identity/registry/operatorVault/transactions.ts +81 -0
- package/src/identity/registry/operatorVault.ts +44 -0
- package/src/identity/storage/ipfs.ts +26 -24
- package/src/identity/wallet/browserWallet/gas.ts +41 -0
- package/src/identity/wallet/browserWallet/html.ts +106 -0
- package/src/identity/wallet/browserWallet/http.ts +28 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +106 -0
- package/src/identity/wallet/browserWallet/requests.ts +191 -0
- package/src/identity/wallet/browserWallet/session.ts +325 -0
- package/src/identity/wallet/browserWallet/types.ts +192 -0
- package/src/identity/wallet/browserWallet/validation.ts +74 -0
- package/src/identity/wallet/browserWallet.ts +30 -393
- package/src/identity/wallet/page/constants.ts +5 -0
- package/src/identity/wallet/page/controller.ts +251 -0
- package/src/identity/wallet/page/copy.ts +340 -0
- package/src/identity/wallet/page/grainient.ts +278 -0
- package/src/identity/wallet/page/html.ts +28 -0
- package/src/identity/wallet/page/markup.ts +50 -0
- package/src/identity/wallet/page/state.ts +9 -0
- package/src/identity/wallet/page/styles/base.ts +259 -0
- package/src/identity/wallet/page/styles/components.ts +262 -0
- package/src/identity/wallet/page/styles/index.ts +5 -0
- package/src/identity/wallet/page/styles/responsive.ts +247 -0
- package/src/identity/wallet/page/types.ts +47 -0
- package/src/identity/wallet/page/view.ts +535 -0
- package/src/identity/wallet/page/walletProvider.ts +70 -0
- package/src/identity/wallet/page.tsx +38 -0
- package/src/identity/wallet/walletPurposeCompat.ts +27 -0
- package/src/mcp/manager.ts +0 -1
- package/src/models/ModelPicker.tsx +36 -30
- package/src/models/catalog.ts +5 -2
- package/src/models/huggingface.ts +9 -9
- package/src/models/llamacpp.ts +13 -13
- package/src/models/modelDisplay.ts +75 -0
- package/src/models/modelPickerOptions.ts +16 -3
- package/src/models/modelRecommendation.ts +0 -1
- package/src/providers/errors.ts +16 -0
- package/src/providers/gemini.ts +252 -39
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +1 -1
- package/src/runtime/sessionMode.ts +1 -1
- package/src/runtime/systemPrompt.ts +2 -0
- package/src/runtime/toolExecution.ts +18 -22
- package/src/runtime/toolIntent.ts +0 -20
- package/src/runtime/turn.ts +0 -92
- package/src/storage/atomicWrite.ts +4 -1
- package/src/storage/config.ts +181 -5
- package/src/storage/identity.ts +9 -3
- package/src/storage/secrets.ts +2 -2
- package/src/tools/bashSafety.ts +8 -0
- package/src/tools/changeDirectoryTool.ts +1 -1
- package/src/tools/deleteFileTool.ts +4 -4
- package/src/tools/editTool.ts +4 -4
- package/src/tools/editUtils.ts +5 -5
- package/src/tools/privateContinuityEditTool.ts +4 -5
- package/src/tools/privateContinuityReadTool.ts +1 -2
- package/src/tools/registry.ts +30 -0
- package/src/tools/writeFileTool.ts +5 -5
- package/src/ui/BrandSplash.tsx +20 -85
- package/src/ui/ProgressBar.tsx +3 -5
- package/src/ui/Select.tsx +21 -9
- package/src/ui/Spinner.tsx +38 -3
- package/src/ui/Surface.tsx +3 -3
- package/src/ui/TextInput.tsx +191 -29
- package/src/ui/theme.ts +7 -34
- package/src/utils/openExternal.ts +21 -0
- package/src/utils/withRetry.ts +47 -3
- package/src/identity/hub/identityHubEffects.ts +0 -937
- package/src/identity/hub/identityHubModel.ts +0 -291
- package/src/identity/hub/screens/ContinuityDashboardScreen.tsx +0 -144
- package/src/identity/hub/screens/EditProfileFlow.tsx +0 -145
- package/src/identity/hub/screens/IdentitySummary.tsx +0 -90
- package/src/identity/hub/screens/MenuScreen.tsx +0 -117
- package/src/identity/hub/screens/RecoveryConfirmScreen.tsx +0 -87
- package/src/identity/hub/screens/RestoreFlow.tsx +0 -206
- package/src/identity/wallet/wallet-page/wallet.html +0 -1202
- /package/src/identity/hub/{screens → components}/BusyScreen.tsx +0 -0
|
@@ -3,7 +3,6 @@ import { z } from 'zod'
|
|
|
3
3
|
import {
|
|
4
4
|
continuityVaultRef,
|
|
5
5
|
ensureContinuityFiles,
|
|
6
|
-
type PrivateContinuityFile,
|
|
7
6
|
} from '../identity/continuity/storage.js'
|
|
8
7
|
import type { Tool } from './contracts.js'
|
|
9
8
|
|
|
@@ -71,7 +70,7 @@ function preparePrivateContinuityRead(
|
|
|
71
70
|
) {
|
|
72
71
|
const identity = config?.identity
|
|
73
72
|
if (!identity) {
|
|
74
|
-
throw new Error('
|
|
73
|
+
throw new Error('No active identity; create or load an identity before reading private continuity files')
|
|
75
74
|
}
|
|
76
75
|
const ref = continuityVaultRef(identity)
|
|
77
76
|
const fullPath = input.file === 'SOUL.md' ? ref.soulPath : ref.memoryPath
|
package/src/tools/registry.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AnthropicToolDefinition } from '../providers/anthropic.js'
|
|
2
|
+
import type { GeminiToolDefinition } from '../providers/gemini.js'
|
|
2
3
|
import type { OpenAIToolDefinition } from '../providers/openai-chat.js'
|
|
3
4
|
import type { Tool } from './contracts.js'
|
|
4
5
|
import { modePolicy, type SessionMode } from '../runtime/sessionMode.js'
|
|
@@ -65,3 +66,32 @@ export function openAITools(mode: SessionMode = 'chat', context: ToolAvailabilit
|
|
|
65
66
|
},
|
|
66
67
|
}))
|
|
67
68
|
}
|
|
69
|
+
|
|
70
|
+
const GEMINI_DROP_KEYS = new Set([
|
|
71
|
+
'additionalProperties',
|
|
72
|
+
'$schema',
|
|
73
|
+
'$ref',
|
|
74
|
+
'$defs',
|
|
75
|
+
'definitions',
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
function sanitizeForGemini(schema: unknown): unknown {
|
|
79
|
+
if (Array.isArray(schema)) return schema.map(sanitizeForGemini)
|
|
80
|
+
if (schema && typeof schema === 'object') {
|
|
81
|
+
const out: Record<string, unknown> = {}
|
|
82
|
+
for (const [k, v] of Object.entries(schema as Record<string, unknown>)) {
|
|
83
|
+
if (GEMINI_DROP_KEYS.has(k)) continue
|
|
84
|
+
out[k] = sanitizeForGemini(v)
|
|
85
|
+
}
|
|
86
|
+
return out
|
|
87
|
+
}
|
|
88
|
+
return schema
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function geminiTools(mode: SessionMode = 'chat', context: ToolAvailabilityContext = {}): GeminiToolDefinition[] {
|
|
92
|
+
return toolsForMode(mode, context).map(tool => ({
|
|
93
|
+
name: tool.name,
|
|
94
|
+
description: tool.description,
|
|
95
|
+
parameters: sanitizeForGemini(tool.inputSchemaJson) as GeminiToolDefinition['parameters'],
|
|
96
|
+
}))
|
|
97
|
+
}
|
|
@@ -78,7 +78,7 @@ async function prepareWrite(
|
|
|
78
78
|
assertSafeWritePath(input.path)
|
|
79
79
|
assertNotPrivateContinuityWorkspacePath(input.path, context.config, 'write_file')
|
|
80
80
|
if (input.content.length === 0) {
|
|
81
|
-
throw new Error('write_file content is empty; provide non-empty file contents')
|
|
81
|
+
throw new Error('Tool write_file content is empty; provide non-empty file contents')
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
const fullPath = resolveWorkspacePath(context.workspaceRoot, input.path)
|
|
@@ -91,7 +91,7 @@ async function prepareWrite(
|
|
|
91
91
|
async function readExistingFile(fullPath: string): Promise<{ before: string; existedBefore: boolean }> {
|
|
92
92
|
try {
|
|
93
93
|
const stats = await fs.stat(fullPath)
|
|
94
|
-
if (stats.isDirectory()) throw new Error('write_file path points to a directory; provide a file path')
|
|
94
|
+
if (stats.isDirectory()) throw new Error('Tool write_file path points to a directory; provide a file path')
|
|
95
95
|
return { before: await fs.readFile(fullPath, 'utf8'), existedBefore: true }
|
|
96
96
|
} catch (error: unknown) {
|
|
97
97
|
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { before: '', existedBefore: false }
|
|
@@ -113,13 +113,13 @@ async function tryRecordRewindSnapshot(
|
|
|
113
113
|
function assertSafeWritePath(requestedPath: string): void {
|
|
114
114
|
const trimmed = requestedPath.trim()
|
|
115
115
|
if (trimmed !== requestedPath || trimmed.length === 0) {
|
|
116
|
-
throw new Error('write_file path must be a clean workspace-relative file path')
|
|
116
|
+
throw new Error('Tool write_file path must be a clean workspace-relative file path')
|
|
117
117
|
}
|
|
118
118
|
if (/[|;&<>`]/.test(trimmed)) {
|
|
119
|
-
throw new Error('write_file path must not contain shell operators')
|
|
119
|
+
throw new Error('Tool write_file path must not contain shell operators')
|
|
120
120
|
}
|
|
121
121
|
if (/^(?:rm|del|erase|rmdir|remove-item|mkdir|type|cat|echo|copy|move|mv|cp)\b/i.test(trimmed)) {
|
|
122
|
-
throw new Error('write_file path looks like a shell command; pass only the file path')
|
|
122
|
+
throw new Error('Tool write_file path looks like a shell command; pass only the file path')
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
package/src/ui/BrandSplash.tsx
CHANGED
|
@@ -1,56 +1,14 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react'
|
|
2
2
|
import { Text, Box } from 'ink'
|
|
3
|
-
import {
|
|
3
|
+
import { theme } from './theme.js'
|
|
4
4
|
|
|
5
5
|
const glyphs = {
|
|
6
|
-
ethagent:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
╚══════╝ ╚═╝ ╚═╝ ╚═╝`,
|
|
13
|
-
a: [
|
|
14
|
-
` █████╗ `,
|
|
15
|
-
`██╔══██╗`,
|
|
16
|
-
`███████║`,
|
|
17
|
-
`██╔══██║`,
|
|
18
|
-
`██║ ██║`,
|
|
19
|
-
`╚═╝ ╚═╝`,
|
|
20
|
-
].join('\n'),
|
|
21
|
-
g: [
|
|
22
|
-
` ██████╗ `,
|
|
23
|
-
`██╔════╝ `,
|
|
24
|
-
`██║ ███╗`,
|
|
25
|
-
`██║ ██║`,
|
|
26
|
-
`╚██████╔╝`,
|
|
27
|
-
` ╚═════╝ `,
|
|
28
|
-
].join('\n'),
|
|
29
|
-
e: [
|
|
30
|
-
`███████╗`,
|
|
31
|
-
`██╔════╝`,
|
|
32
|
-
`█████╗ `,
|
|
33
|
-
`██╔══╝ `,
|
|
34
|
-
`███████╗`,
|
|
35
|
-
`╚══════╝`,
|
|
36
|
-
].join('\n'),
|
|
37
|
-
n: [
|
|
38
|
-
`███╗ ██╗`,
|
|
39
|
-
`████╗ ██║`,
|
|
40
|
-
`██╔██╗ ██║`,
|
|
41
|
-
`██║╚██╗██║`,
|
|
42
|
-
`██║ ╚████║`,
|
|
43
|
-
`╚═╝ ╚═══╝`,
|
|
44
|
-
].join('\n'),
|
|
45
|
-
t: [
|
|
46
|
-
`████████╗`,
|
|
47
|
-
`╚══██╔══╝`,
|
|
48
|
-
` ██║ `,
|
|
49
|
-
` ██║ `,
|
|
50
|
-
` ██║ `,
|
|
51
|
-
` ╚═╝ `,
|
|
52
|
-
].join('\n'),
|
|
53
|
-
},
|
|
6
|
+
ethagent: `░░░░░░░╗░░░░░░░░╗░░╗ ░░╗ █████╗ ██████╗ ███████╗███╗ ██╗████████╗
|
|
7
|
+
░░╔════╝╚══░░╔══╝░░║ ░░║██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝
|
|
8
|
+
░░░░░╗ ░░║ ░░░░░░░║███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║
|
|
9
|
+
░░╔══╝ ░░║ ░░╔══░░║██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║
|
|
10
|
+
░░░░░░░╗ ░░║ ░░║ ░░║██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║
|
|
11
|
+
╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ `,
|
|
54
12
|
eyes: `
|
|
55
13
|
-+:
|
|
56
14
|
:=- -%@@@%.
|
|
@@ -79,33 +37,13 @@ const glyphs = {
|
|
|
79
37
|
},
|
|
80
38
|
} as const
|
|
81
39
|
|
|
82
|
-
const ethagentGlyphOrder = ['eth', 'a', 'g', 'e', 'n', 't'] as const
|
|
83
|
-
|
|
84
40
|
const Eyes = () => {
|
|
85
41
|
const lines = glyphs.eyes.split('\n')
|
|
86
42
|
return (
|
|
87
43
|
<Box flexDirection="column">
|
|
88
|
-
{lines.map((line, li) =>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
.filter(entry => entry.char.trim().length > 0)
|
|
92
|
-
.map(entry => entry.index)
|
|
93
|
-
const firstGlyph = glyphPositions[0] ?? 0
|
|
94
|
-
const lastGlyph = glyphPositions[glyphPositions.length - 1] ?? firstGlyph
|
|
95
|
-
const span = Math.max(lastGlyph - firstGlyph, 1)
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<Text key={li}>
|
|
99
|
-
{[...line].map((char, ci) => {
|
|
100
|
-
if (!char.trim()) {
|
|
101
|
-
return <Text key={ci}>{char}</Text>
|
|
102
|
-
}
|
|
103
|
-
const t = (ci - firstGlyph) / span
|
|
104
|
-
return <Text key={ci} color={eyeGradientColor(t)}>{char}</Text>
|
|
105
|
-
})}
|
|
106
|
-
</Text>
|
|
107
|
-
)
|
|
108
|
-
})}
|
|
44
|
+
{lines.map((line, li) => (
|
|
45
|
+
<Text key={li} color={theme.text}>{line}</Text>
|
|
46
|
+
))}
|
|
109
47
|
</Box>
|
|
110
48
|
)
|
|
111
49
|
}
|
|
@@ -135,19 +73,18 @@ export const BrandSplash: React.FC<SplashProps> = ({ contextLine, tipLine, updat
|
|
|
135
73
|
return (
|
|
136
74
|
<Box flexDirection="column" alignSelf="flex-start" padding={1}>
|
|
137
75
|
<Eyes />
|
|
138
|
-
<Text bold color={theme.
|
|
139
|
-
<Text color={theme.dim}>{
|
|
76
|
+
<Text bold color={theme.accentWhite}>ethagent</Text>
|
|
77
|
+
<Text color={theme.dim}>privacy-first AI agent with a portable <Text color={theme.accentPeriwinkle}>Ethereum</Text> identity</Text>
|
|
140
78
|
{contextLine ? <Text color={theme.dim}>{contextLine}</Text> : null}
|
|
141
79
|
{tipLine ? <Text color={theme.dim}>{tipLine}</Text> : null}
|
|
142
|
-
{updateNotice ? <Text color={theme.
|
|
80
|
+
{updateNotice ? <Text color={theme.accentPeriwinkle}>{updateNotice}</Text> : null}
|
|
143
81
|
</Box>
|
|
144
82
|
)
|
|
145
83
|
}
|
|
146
84
|
|
|
147
|
-
const logoLines = ethagentGlyphOrder.map(key => glyphs.ethagent[key].split('\n'))
|
|
148
|
-
const rowCount = Math.max(...logoLines.map(lines => lines.length))
|
|
149
|
-
|
|
150
85
|
const w = 69
|
|
86
|
+
const logoLines = glyphs.ethagent.split('\n').map(line => line.padEnd(w, ' '))
|
|
87
|
+
|
|
151
88
|
const topPad = Math.max(0, w - glyphs.tagline.length - 1)
|
|
152
89
|
|
|
153
90
|
const bottomInline = contextLine ? ` ${truncateToFit(contextLine, w - 4)} ` : ''
|
|
@@ -158,22 +95,20 @@ export const BrandSplash: React.FC<SplashProps> = ({ contextLine, tipLine, updat
|
|
|
158
95
|
<Eyes />
|
|
159
96
|
<Text>
|
|
160
97
|
<Text color={theme.border}>{glyphs.frame.topLeft}</Text>
|
|
161
|
-
<Text color={theme.dim}>{
|
|
98
|
+
<Text color={theme.dim}>{' privacy-first AI agent with a portable '}<Text color={theme.accentPeriwinkle}>Ethereum</Text>{' identity '}</Text>
|
|
162
99
|
<Text color={theme.border}>{glyphs.frame.horizontal.repeat(topPad)}{glyphs.frame.topRight}</Text>
|
|
163
100
|
</Text>
|
|
164
|
-
{
|
|
101
|
+
{logoLines.map((line, i) => (
|
|
165
102
|
<Box key={i}>
|
|
166
103
|
<Text color={theme.border}>{glyphs.frame.side}</Text>
|
|
167
|
-
{
|
|
168
|
-
<Text key={ethagentGlyphOrder[index]} color={theme.border}>{lines[i] ?? ''}</Text>
|
|
169
|
-
))}
|
|
104
|
+
<Text color={theme.border}>{line}</Text>
|
|
170
105
|
<Text color={theme.border}>{glyphs.frame.side}</Text>
|
|
171
106
|
</Box>
|
|
172
107
|
))}
|
|
173
108
|
{bottomInline ? (
|
|
174
109
|
<Text>
|
|
175
110
|
<Text color={theme.border}>{glyphs.frame.bottomLeft}</Text>
|
|
176
|
-
<Text color={theme.
|
|
111
|
+
<Text color={theme.accentPeriwinkle}>{bottomInline}</Text>
|
|
177
112
|
<Text color={theme.border}>{glyphs.frame.horizontal.repeat(bottomPad)}{glyphs.frame.bottomRight}</Text>
|
|
178
113
|
</Text>
|
|
179
114
|
) : (
|
|
@@ -182,7 +117,7 @@ export const BrandSplash: React.FC<SplashProps> = ({ contextLine, tipLine, updat
|
|
|
182
117
|
{tipLine || updateNotice ? (
|
|
183
118
|
<Box marginTop={1} flexDirection="column">
|
|
184
119
|
{tipLine ? <Text color={theme.dim}>{tipLine}</Text> : null}
|
|
185
|
-
{updateNotice ? <Text color={theme.
|
|
120
|
+
{updateNotice ? <Text color={theme.accentPeriwinkle}>{updateNotice}</Text> : null}
|
|
186
121
|
</Box>
|
|
187
122
|
) : null}
|
|
188
123
|
</Box>
|
package/src/ui/ProgressBar.tsx
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { Box, Text } from 'ink'
|
|
3
|
-
import { theme, gradientColor
|
|
3
|
+
import { theme, gradientColor } from './theme.js'
|
|
4
4
|
|
|
5
5
|
type ProgressBarProps = {
|
|
6
6
|
progress: number
|
|
7
7
|
width?: number
|
|
8
8
|
label?: string
|
|
9
9
|
suffix?: string
|
|
10
|
-
variant?: 'default' | 'rainbow'
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
export const ProgressBar: React.FC<ProgressBarProps> = ({ progress, width = 40, label, suffix
|
|
12
|
+
export const ProgressBar: React.FC<ProgressBarProps> = ({ progress, width = 40, label, suffix }) => {
|
|
14
13
|
const p = Math.max(0, Math.min(1, progress))
|
|
15
14
|
const filled = Math.round(p * width)
|
|
16
15
|
const empty = Math.max(0, width - filled)
|
|
17
|
-
const colorFor = variant === 'rainbow' ? eyeGradientColor : gradientColor
|
|
18
16
|
const cells: React.ReactElement[] = []
|
|
19
17
|
for (let i = 0; i < filled; i++) {
|
|
20
18
|
cells.push(
|
|
21
|
-
<Text key={`f-${i}`} color={
|
|
19
|
+
<Text key={`f-${i}`} color={gradientColor(i / Math.max(width - 1, 1))}>█</Text>,
|
|
22
20
|
)
|
|
23
21
|
}
|
|
24
22
|
for (let i = 0; i < empty; i++) {
|
package/src/ui/Select.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react'
|
|
2
2
|
import { Box, Text } from 'ink'
|
|
3
3
|
import { theme } from './theme.js'
|
|
4
4
|
import { useAppInput } from '../app/input/AppInputProvider.js'
|
|
@@ -39,9 +39,18 @@ export function Select<T>({
|
|
|
39
39
|
onCancel,
|
|
40
40
|
onHighlight,
|
|
41
41
|
}: SelectProps<T>) {
|
|
42
|
+
const optionsSignature = useMemo(
|
|
43
|
+
() => options.map(option => `${String(option.value)}:${option.disabled ? 'disabled' : 'enabled'}:${option.role ?? 'option'}`).join('|'),
|
|
44
|
+
[options],
|
|
45
|
+
)
|
|
42
46
|
const firstEnabled = Math.max(0, options.findIndex(isSelectableOption))
|
|
43
47
|
const start = isSelectableOption(options[initialIndex]) ? initialIndex : firstEnabled
|
|
44
48
|
const [index, setIndex] = useState(start === -1 ? 0 : start)
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
setIndex(start === -1 ? 0 : start)
|
|
52
|
+
}, [optionsSignature, start])
|
|
53
|
+
|
|
45
54
|
const visibleCount = Math.max(1, maxVisible ?? options.length)
|
|
46
55
|
const windowStart = Math.max(0, Math.min(
|
|
47
56
|
index - Math.floor(visibleCount / 2),
|
|
@@ -73,7 +82,7 @@ export function Select<T>({
|
|
|
73
82
|
else if (key.return) {
|
|
74
83
|
const selected = options[index]
|
|
75
84
|
if (isSelectableOption(selected)) onSubmit(selected.value)
|
|
76
|
-
} else if (key.escape) {
|
|
85
|
+
} else if (key.escape || (key.ctrl && input === 'c')) {
|
|
77
86
|
onCancel?.()
|
|
78
87
|
}
|
|
79
88
|
})
|
|
@@ -88,24 +97,27 @@ export function Select<T>({
|
|
|
88
97
|
const absoluteIndex = windowStart + visibleIndex
|
|
89
98
|
const isActive = absoluteIndex === index
|
|
90
99
|
const selectable = isSelectableOption(option)
|
|
100
|
+
const disabled = !!option.disabled
|
|
91
101
|
const cursor = !selectable ? ' ' : isActive ? '>' : ' '
|
|
92
102
|
const isSection = option.role === 'section' || option.role === 'group'
|
|
93
103
|
const prefix = option.prefix && !isSection ? `${option.prefix} ` : ''
|
|
94
104
|
const rowIndent = option.indent ?? (usesInlineSections ? isSection ? 1 : 3 : 0)
|
|
95
|
-
const prefixColor =
|
|
105
|
+
const prefixColor = disabled
|
|
96
106
|
? option.labelColor ?? theme.border
|
|
97
107
|
: isActive && selectable
|
|
98
|
-
? theme.
|
|
108
|
+
? theme.accentPeriwinkle
|
|
99
109
|
: option.labelColor ?? theme.dim
|
|
100
110
|
const labelColor = isSection
|
|
101
|
-
? option.labelColor ?? theme.
|
|
111
|
+
? option.labelColor ?? theme.textSubtle
|
|
102
112
|
: isActive && selectable
|
|
103
|
-
? theme.
|
|
104
|
-
: option.labelColor ?? (
|
|
113
|
+
? theme.accentPeriwinkle
|
|
114
|
+
: option.labelColor ?? (disabled ? theme.dim : theme.text)
|
|
105
115
|
const hintColor = isActive && selectable
|
|
106
116
|
? theme.textSubtle
|
|
107
|
-
:
|
|
108
|
-
|
|
117
|
+
: disabled
|
|
118
|
+
? theme.border
|
|
119
|
+
: option.hintColor ?? theme.dim
|
|
120
|
+
const subtextColor = disabled ? theme.border : option.subtextColor ?? theme.dim
|
|
109
121
|
const bold = option.bold ?? (isSection || (isActive && selectable))
|
|
110
122
|
const inlineHint = Boolean(option.hint && hintLayout === 'inline' && !isSection)
|
|
111
123
|
const belowHint = Boolean(option.hint && (!inlineHint || isSection))
|
package/src/ui/Spinner.tsx
CHANGED
|
@@ -193,6 +193,15 @@ export function pickVerb(): string {
|
|
|
193
193
|
return SPINNER_VERBS[idx] ?? 'thinking'
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
export function spinnerText(value: string): string {
|
|
197
|
+
const text = restoreSpinnerTerms(value.toLowerCase())
|
|
198
|
+
return text.replace(/^(\s*)([a-z])/, (_match, prefix: string, letter: string) => `${prefix}${letter.toUpperCase()}`)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function spinnerHintText(value: string): string {
|
|
202
|
+
return restoreSpinnerTerms(value.toLowerCase())
|
|
203
|
+
}
|
|
204
|
+
|
|
196
205
|
type SpinnerProps = {
|
|
197
206
|
active?: boolean
|
|
198
207
|
hint?: string
|
|
@@ -210,7 +219,7 @@ export const Spinner: React.FC<SpinnerProps> = ({
|
|
|
210
219
|
hint: rawHint,
|
|
211
220
|
label,
|
|
212
221
|
verb,
|
|
213
|
-
color = theme.
|
|
222
|
+
color = theme.accentPeriwinkle,
|
|
214
223
|
startedAt,
|
|
215
224
|
showElapsed = true,
|
|
216
225
|
}) => {
|
|
@@ -246,11 +255,11 @@ export const Spinner: React.FC<SpinnerProps> = ({
|
|
|
246
255
|
if (!active) return null
|
|
247
256
|
|
|
248
257
|
const autoLabel = stickyVerbRef.current ?? verb ?? 'thinking'
|
|
249
|
-
const text = label ?? `${autoLabel}…`
|
|
258
|
+
const text = spinnerText(label ?? `${autoLabel}…`)
|
|
250
259
|
const glyph = FRAMES[frame] ?? 'o'
|
|
251
260
|
const elapsed = showElapsed ? formatElapsedSeconds(Date.now() - (startedAt ?? internalStartedAtRef.current)) : null
|
|
252
261
|
const renderedHint = [rawHint, elapsed].filter(Boolean).join(' · ')
|
|
253
|
-
const hint = renderedHint
|
|
262
|
+
const hint = renderedHint ? spinnerHintText(renderedHint) : ''
|
|
254
263
|
|
|
255
264
|
return (
|
|
256
265
|
<Text>
|
|
@@ -267,3 +276,29 @@ function formatElapsedSeconds(milliseconds: number): string {
|
|
|
267
276
|
const minutes = Math.floor(seconds / 60)
|
|
268
277
|
return `${minutes}:${(seconds % 60).toString().padStart(2, '0')}`
|
|
269
278
|
}
|
|
279
|
+
|
|
280
|
+
function restoreSpinnerTerms(value: string): string {
|
|
281
|
+
return value
|
|
282
|
+
.replace(/\bapi\b/g, 'API')
|
|
283
|
+
.replace(/\bens\b/g, 'ENS')
|
|
284
|
+
.replace(/\berc-8004\b/g, 'ERC-8004')
|
|
285
|
+
.replace(/\bgguf\b/g, 'GGUF')
|
|
286
|
+
.replace(/\bhugging face\b/g, 'Hugging Face')
|
|
287
|
+
.replace(/\bipfs\b/g, 'IPFS')
|
|
288
|
+
.replace(/\bjson\b/g, 'JSON')
|
|
289
|
+
.replace(/\bjwt\b/g, 'JWT')
|
|
290
|
+
.replace(/\bmemory\.md\b/g, 'MEMORY.md')
|
|
291
|
+
.replace(/\bopenai\b/g, 'OpenAI')
|
|
292
|
+
.replace(/\banthropic\b/g, 'Anthropic')
|
|
293
|
+
.replace(/\bgemini\b/g, 'Gemini')
|
|
294
|
+
.replace(/\bos\b/g, 'OS')
|
|
295
|
+
.replace(/\brpc\b/g, 'RPC')
|
|
296
|
+
.replace(/\bsoul\.md\b/g, 'SOUL.md')
|
|
297
|
+
.replace(/\buri\b/g, 'URI')
|
|
298
|
+
.replace(/\burl\b/g, 'URL')
|
|
299
|
+
.replace(/\bbase\b/g, 'Base')
|
|
300
|
+
.replace(/\bethereum mainnet\b/g, 'Ethereum Mainnet')
|
|
301
|
+
.replace(/\bethereum\b/g, 'Ethereum')
|
|
302
|
+
.replace(/\bsepolia\b/g, 'Sepolia')
|
|
303
|
+
.replace(/\bbase sepolia\b/g, 'Base Sepolia')
|
|
304
|
+
}
|
package/src/ui/Surface.tsx
CHANGED
|
@@ -13,9 +13,9 @@ type SurfaceProps = {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const toneColor: Record<SurfaceTone, string> = {
|
|
16
|
-
primary: theme.
|
|
16
|
+
primary: theme.accentPeriwinkle,
|
|
17
17
|
muted: theme.border,
|
|
18
|
-
error:
|
|
18
|
+
error: theme.accentError,
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export const Surface: React.FC<SurfaceProps> = ({
|
|
@@ -27,7 +27,7 @@ export const Surface: React.FC<SurfaceProps> = ({
|
|
|
27
27
|
}) => {
|
|
28
28
|
const borderColor = toneColor[tone]
|
|
29
29
|
return (
|
|
30
|
-
<Box flexDirection="column" borderStyle="round" borderColor={borderColor} paddingX={2} paddingY={0}>
|
|
30
|
+
<Box flexDirection="column" borderStyle="round" borderColor={borderColor} paddingX={2} paddingY={0} width="100%">
|
|
31
31
|
<Box flexDirection="column">
|
|
32
32
|
<Text color={borderColor} bold>{title}</Text>
|
|
33
33
|
{subtitle ? (
|