ethagent 3.3.3 → 4.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/.claude-plugin/marketplace.json +11 -0
- package/.claude-plugin/plugin.json +35 -0
- package/LICENSE +1 -1
- package/README.md +64 -104
- package/commands/ethagent.md +40 -0
- package/package.json +16 -16
- package/src/app/keybindings/KeybindingProvider.tsx +1 -6
- package/src/app/keybindings/types.ts +1 -6
- package/src/cli/ResetConfirmView.tsx +54 -53
- package/src/cli/demo.ts +86 -0
- package/src/cli/hookIo.ts +45 -0
- package/src/cli/main.tsx +94 -123
- package/src/cli/memoryGuard.ts +49 -0
- package/src/cli/reset.ts +28 -70
- package/src/cli/sessionStart.ts +33 -0
- package/src/cli/status.ts +46 -0
- package/src/cli/sync.ts +167 -0
- package/src/cli/syncAdapters/claude-code.ts +86 -0
- package/src/cli/syncAdapters/codex.ts +66 -0
- package/src/cli/syncAdapters/index.ts +45 -0
- package/src/cli/syncAdapters/managedBlock.ts +175 -0
- package/src/cli/syncAdapters/shared.ts +63 -0
- package/src/identity/continuity/envelopeParse.ts +20 -1
- package/src/identity/continuity/publicSkills.ts +3 -1
- package/src/identity/continuity/skills/publicSkillsSync.ts +2 -1
- package/src/identity/continuity/skills/scaffold.ts +5 -2
- package/src/identity/continuity/snapshots.ts +12 -5
- package/src/identity/continuity/storage/defaults.ts +20 -19
- package/src/identity/continuity/storage/status.ts +1 -1
- package/src/identity/ens/ensLookup/constants.ts +1 -1
- package/src/identity/manager/IdentityManager.tsx +33 -0
- package/src/identity/{hub → manager}/OperationalRoutes.tsx +37 -18
- package/src/identity/{hub → manager}/Routes.tsx +48 -34
- package/src/identity/{hub → manager}/continuity/ContinuityDashboardScreen.tsx +9 -19
- package/src/identity/{hub → manager}/continuity/RebackupStorageScreen.tsx +3 -3
- package/src/identity/manager/continuity/RecoveryConfirmScreen.tsx +102 -0
- package/src/identity/{hub → manager}/continuity/SavePromptScreen.tsx +2 -3
- package/src/identity/{hub → manager}/continuity/completion.ts +1 -1
- package/src/identity/{hub → manager}/continuity/effects.ts +1 -1
- package/src/identity/{hub → manager}/continuity/skills/DeleteSkillConfirmScreen.tsx +2 -2
- package/src/identity/{hub → manager}/continuity/skills/NewSkillScreen.tsx +0 -5
- package/src/identity/{hub → manager}/continuity/skills/NewSkillVisibilityScreen.tsx +4 -4
- package/src/identity/{hub → manager}/continuity/skills/SkillActionsScreen.tsx +6 -22
- package/src/identity/{hub → manager}/continuity/skills/SkillsTreeScreen.tsx +5 -17
- package/src/identity/{hub → manager}/continuity/snapshot.ts +1 -1
- package/src/identity/{hub → manager}/continuity/vault.ts +1 -1
- package/src/identity/{hub → manager}/create/CreateFlow.tsx +59 -32
- package/src/identity/{hub → manager}/create/effects.ts +19 -10
- package/src/identity/manager/create/importScan.ts +122 -0
- package/src/identity/{hub → manager}/custody/CustodyEditFlow.tsx +17 -61
- package/src/identity/{hub → manager}/custody/actions.ts +1 -15
- package/src/identity/{hub → manager}/custody/routes.tsx +20 -40
- package/src/identity/{hub → manager}/custody/transactions.ts +1 -0
- package/src/identity/{hub → manager}/custody/types.ts +1 -2
- package/src/identity/{hub → manager}/custody/useCustodyEffects.ts +1 -1
- package/src/identity/{hub → manager}/ens/EnsEditAdvancedScreens.tsx +2 -2
- package/src/identity/{hub → manager}/ens/EnsEditMaintenanceScreens.tsx +12 -23
- package/src/identity/{hub → manager}/ens/EnsEditReviewScreens.tsx +18 -42
- package/src/identity/{hub → manager}/ens/EnsEditRunners.tsx +1 -1
- package/src/identity/{hub → manager}/ens/EnsEditShared.tsx +0 -2
- package/src/identity/{hub → manager}/ens/EnsEditSimpleScreens.tsx +10 -19
- package/src/identity/{hub → manager}/ens/EnsFlow.tsx +133 -41
- package/src/identity/{hub → manager}/ens/EnsOperatorWalletsScreen.tsx +14 -19
- package/src/identity/{hub → manager}/ens/editCopy.ts +1 -14
- package/src/identity/{hub → manager}/profile/EditProfileFlow.tsx +99 -66
- package/src/identity/{hub → manager}/profile/effects.ts +1 -3
- package/src/identity/{hub → manager}/profile/operatorSave.ts +1 -1
- package/src/identity/{hub → manager}/profile/state.ts +1 -1
- package/src/identity/{hub/identityHubReducer.ts → manager/reducer.ts} +25 -26
- package/src/identity/{hub → manager}/restore/RestoreFlow.tsx +16 -24
- package/src/identity/{hub → manager}/restore/apply.ts +1 -1
- package/src/identity/{hub → manager}/restore/auth.ts +1 -1
- package/src/identity/{hub → manager}/restore/discover.ts +1 -1
- package/src/identity/{hub → manager}/restore/fetch.ts +1 -1
- package/src/identity/{hub → manager}/restore/restoreAdmin.ts +1 -1
- package/src/identity/{hub → manager}/restore/useRestoreEffects.ts +2 -9
- package/src/identity/{hub → manager}/settings/StorageCredentialScreen.tsx +10 -25
- package/src/identity/{hub → manager}/shared/components/DetailsScreen.tsx +5 -7
- package/src/identity/{hub → manager}/shared/components/ErrorScreen.tsx +6 -10
- package/src/identity/{hub → manager}/shared/components/FlowTimeline.tsx +4 -3
- package/src/identity/{hub → manager}/shared/components/IdentitySummary.tsx +19 -59
- package/src/identity/manager/shared/components/LazyMenu.tsx +147 -0
- package/src/identity/manager/shared/components/MenuScreen.tsx +220 -0
- package/src/identity/manager/shared/components/OperationCompleteScreen.tsx +28 -0
- package/src/identity/{hub → manager}/shared/components/UnlinkedIdentityScreen.tsx +9 -10
- package/src/identity/{hub → manager}/shared/components/WalletApprovalScreen.tsx +1 -2
- package/src/identity/manager/shared/components/Wordmark.tsx +54 -0
- package/src/identity/{hub → manager}/shared/components/menuFlagsFromReconciliation.ts +39 -15
- package/src/identity/{hub → manager}/shared/effects/profilePrep.ts +1 -1
- package/src/identity/manager/shared/effects/types.ts +30 -0
- package/src/identity/{hub → manager}/shared/model/copy.ts +0 -4
- package/src/identity/{hub → manager}/shared/model/errors.ts +32 -3
- package/src/identity/{hub → manager}/shared/model/network.ts +2 -2
- package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/hook.ts +5 -0
- package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/run.ts +1 -1
- package/src/identity/{hub/shared/reconciliation/useAgentReconciliation.ts → manager/shared/reconciliation/index.ts} +6 -0
- package/src/identity/{hub → manager}/shared/utils.ts +6 -10
- package/src/identity/{hub → manager}/transfer/TokenTransferFlow.tsx +3 -3
- package/src/identity/{hub → manager}/transfer/TokenTransferScreens.tsx +4 -10
- package/src/identity/{hub → manager}/transfer/effects.ts +1 -1
- package/src/identity/{hub → manager}/types.ts +5 -6
- package/src/identity/{hub/useIdentityHubContinuity.ts → manager/useContinuity.ts} +59 -27
- package/src/identity/{hub/useIdentityHubController.ts → manager/useController.ts} +38 -35
- package/src/identity/{hub/useIdentityHubSideEffects.ts → manager/useSideEffects.ts} +40 -4
- package/src/identity/registry/erc8004/discovery.ts +3 -17
- package/src/identity/registry/erc8004/utils.ts +1 -1
- package/src/identity/storage/ipfs.ts +21 -1
- package/src/identity/wallet/browserWallet/html.ts +10 -2
- package/src/identity/wallet/browserWallet/http.ts +18 -0
- package/src/identity/wallet/browserWallet/requestServer.ts +5 -1
- package/src/identity/wallet/browserWallet/requests.ts +10 -28
- package/src/identity/wallet/browserWallet/session.ts +26 -33
- package/src/identity/wallet/browserWallet/validation.ts +14 -0
- package/src/identity/wallet/browserWallet/walletPageSource.ts +22 -40
- package/src/identity/wallet/page/boot.ts +43 -0
- package/src/identity/wallet/page/config.ts +59 -0
- package/src/identity/wallet/page/constants.ts +12 -0
- package/src/identity/wallet/page/copy.ts +47 -68
- package/src/identity/wallet/page/css.ts +638 -0
- package/src/identity/wallet/page/{errorView.ts → errors.ts} +5 -14
- package/src/identity/wallet/page/{controller.ts → flow.ts} +4 -71
- package/src/identity/wallet/page/markup.ts +44 -34
- package/src/identity/wallet/page/{walletProvider.ts → provider.ts} +0 -3
- package/src/identity/wallet/page/resize.ts +95 -0
- package/src/identity/wallet/page/state.ts +135 -8
- package/src/identity/wallet/page/timeline.ts +161 -0
- package/src/identity/wallet/page/view.ts +22 -302
- package/src/storage/config.ts +30 -80
- package/src/storage/reset.ts +31 -0
- package/src/storage/secrets.ts +1 -16
- package/src/ui/Select.tsx +27 -5
- package/src/ui/Spinner.tsx +16 -15
- package/src/ui/Surface.tsx +21 -17
- package/src/ui/TextArea.tsx +173 -0
- package/src/ui/TextInput.tsx +31 -133
- package/src/ui/theme.ts +22 -13
- package/src/utils/clipboard.ts +0 -140
- package/src/app/FirstRun.tsx +0 -577
- package/src/app/FirstRunTimeline.tsx +0 -51
- package/src/app/firstRunConfig.ts +0 -26
- package/src/app/hooks/useCancelRequest.ts +0 -22
- package/src/app/hooks/useDoublePress.ts +0 -46
- package/src/app/hooks/useExitOnCtrlC.ts +0 -36
- package/src/auth/openaiOAuth/credentials.ts +0 -47
- package/src/auth/openaiOAuth/crypto.ts +0 -23
- package/src/auth/openaiOAuth/index.ts +0 -238
- package/src/auth/openaiOAuth/landingPage.ts +0 -116
- package/src/auth/openaiOAuth/listener.ts +0 -151
- package/src/auth/openaiOAuth/refresh.ts +0 -70
- package/src/auth/openaiOAuth/shared.ts +0 -115
- package/src/chat/ChatBottomPane.tsx +0 -296
- package/src/chat/ChatScreen.tsx +0 -1685
- package/src/chat/ConversationStack.tsx +0 -56
- package/src/chat/MessageList.tsx +0 -638
- package/src/chat/SessionStatus.tsx +0 -53
- package/src/chat/chatEnvironment.ts +0 -16
- package/src/chat/chatScreenUtils.ts +0 -194
- package/src/chat/chatSessionState.ts +0 -146
- package/src/chat/chatTurnContext.ts +0 -50
- package/src/chat/chatTurnOrchestrator.ts +0 -603
- package/src/chat/chatTurnRows.ts +0 -64
- package/src/chat/commands.ts +0 -494
- package/src/chat/continuityEditReview.ts +0 -42
- package/src/chat/display/DiffView.tsx +0 -193
- package/src/chat/display/SyntaxText.tsx +0 -192
- package/src/chat/display/toolCallDisplay.ts +0 -103
- package/src/chat/display/toolResultDisplay.ts +0 -19
- package/src/chat/input/ChatInput.tsx +0 -625
- package/src/chat/input/chatInputHelpers.ts +0 -62
- package/src/chat/input/chatInputState.ts +0 -247
- package/src/chat/input/chatPaste.ts +0 -49
- package/src/chat/input/imageRefs.ts +0 -30
- package/src/chat/input/inputRendering.tsx +0 -93
- package/src/chat/input/textCursor.ts +0 -212
- package/src/chat/messageMarkdown.ts +0 -220
- package/src/chat/messageRows.ts +0 -43
- package/src/chat/planImplementation.ts +0 -62
- package/src/chat/slashCommandHandlers.ts +0 -122
- package/src/chat/slashCommandViews.ts +0 -120
- package/src/chat/transcript/TranscriptView.tsx +0 -184
- package/src/chat/transcript/transcriptViewport.ts +0 -295
- package/src/chat/views/ContextLimitView.tsx +0 -95
- package/src/chat/views/ContinuityEditReviewView.tsx +0 -50
- package/src/chat/views/CopyPicker.tsx +0 -50
- package/src/chat/views/PermissionPrompt.tsx +0 -156
- package/src/chat/views/PermissionsView.tsx +0 -165
- package/src/chat/views/PlanApprovalView.tsx +0 -91
- package/src/chat/views/ResumeView.tsx +0 -273
- package/src/chat/views/RewindView.tsx +0 -412
- package/src/cli/preview.tsx +0 -14
- package/src/cli/updateNotice.ts +0 -54
- package/src/identity/continuity/privateEdit/apply.ts +0 -170
- package/src/identity/continuity/privateEdit/diff.ts +0 -6
- package/src/identity/continuity/privateEdit/files.ts +0 -23
- package/src/identity/continuity/privateEdit/types.ts +0 -28
- package/src/identity/continuity/privateEdit.ts +0 -46
- package/src/identity/hub/IdentityHub.tsx +0 -14
- package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +0 -104
- package/src/identity/hub/ens/effects.ts +0 -218
- package/src/identity/hub/shared/components/MenuScreen.tsx +0 -241
- package/src/identity/hub/shared/effects/types.ts +0 -53
- package/src/identity/hub/shared/reconciliation/index.ts +0 -14
- package/src/identity/wallet/page/grainient.ts +0 -278
- package/src/identity/wallet/page/html.ts +0 -28
- package/src/identity/wallet/page/styles/base.ts +0 -259
- package/src/identity/wallet/page/styles/components.ts +0 -262
- package/src/identity/wallet/page/styles/index.ts +0 -5
- package/src/identity/wallet/page/styles/responsive.ts +0 -247
- package/src/identity/wallet/page.tsx +0 -38
- package/src/mcp/approvals.ts +0 -113
- package/src/mcp/config.ts +0 -235
- package/src/mcp/manager.ts +0 -482
- package/src/mcp/managerHelpers.ts +0 -70
- package/src/mcp/names.ts +0 -19
- package/src/mcp/output.ts +0 -96
- package/src/models/ModelPicker.tsx +0 -1009
- package/src/models/catalog.ts +0 -327
- package/src/models/huggingface.ts +0 -712
- package/src/models/huggingfaceStorage.ts +0 -136
- package/src/models/llamacpp.ts +0 -848
- package/src/models/llamacppCommands.ts +0 -44
- package/src/models/llamacppConfig.ts +0 -34
- package/src/models/llamacppDiscovery.ts +0 -176
- package/src/models/llamacppOutput.ts +0 -65
- package/src/models/llamacppPreflight.ts +0 -158
- package/src/models/modelDisplay.ts +0 -180
- package/src/models/modelPickerCatalogFlow.ts +0 -56
- package/src/models/modelPickerCredentials.ts +0 -166
- package/src/models/modelPickerData.ts +0 -41
- package/src/models/modelPickerDisplay.tsx +0 -132
- package/src/models/modelPickerHfFlow.ts +0 -192
- package/src/models/modelPickerLocalRunnerFlow.ts +0 -115
- package/src/models/modelPickerOptions.ts +0 -457
- package/src/models/modelPickerTypes.ts +0 -69
- package/src/models/modelPickerUninstallFlow.ts +0 -48
- package/src/models/modelPickerViewHelpers.ts +0 -174
- package/src/models/modelRecommendation.ts +0 -139
- package/src/models/providerDisplay.ts +0 -16
- package/src/models/runtimeDetection.ts +0 -81
- package/src/models/uncensoredCatalog.ts +0 -86
- package/src/providers/anthropic.ts +0 -290
- package/src/providers/contracts.ts +0 -71
- package/src/providers/errors.ts +0 -80
- package/src/providers/gemini.ts +0 -391
- package/src/providers/openai-chat.ts +0 -474
- package/src/providers/openai-responses-format.ts +0 -177
- package/src/providers/openai-responses.ts +0 -306
- package/src/providers/openaiChatWire.ts +0 -124
- package/src/providers/registry.ts +0 -120
- package/src/providers/retry.ts +0 -58
- package/src/providers/sse.ts +0 -93
- package/src/runtime/compaction.ts +0 -395
- package/src/runtime/cwd.ts +0 -43
- package/src/runtime/providerTurn.ts +0 -38
- package/src/runtime/sessionMode.ts +0 -55
- package/src/runtime/systemPrompt.ts +0 -213
- package/src/runtime/textToolParser.ts +0 -161
- package/src/runtime/toolClaimGuards.ts +0 -143
- package/src/runtime/toolExecution.ts +0 -304
- package/src/runtime/toolIntent.ts +0 -143
- package/src/runtime/turn.ts +0 -369
- package/src/runtime/turnNudges.ts +0 -223
- package/src/runtime/turnTypes.ts +0 -86
- package/src/storage/factoryReset.ts +0 -127
- package/src/storage/history.ts +0 -58
- package/src/storage/permissions.ts +0 -76
- package/src/storage/rewind.ts +0 -266
- package/src/storage/sessionExport.ts +0 -49
- package/src/storage/sessions.ts +0 -495
- package/src/tools/bashSafety.ts +0 -186
- package/src/tools/bashTool.ts +0 -140
- package/src/tools/changeDirectoryTool.ts +0 -213
- package/src/tools/contracts.ts +0 -192
- package/src/tools/deleteFileTool.ts +0 -116
- package/src/tools/editTool.ts +0 -165
- package/src/tools/editUtils.ts +0 -170
- package/src/tools/fileDiff.ts +0 -261
- package/src/tools/listDirectoryTool.ts +0 -55
- package/src/tools/listSkillFilesTool.ts +0 -77
- package/src/tools/listSkillsTool.ts +0 -68
- package/src/tools/mcpResourceTools.ts +0 -95
- package/src/tools/permissionRules.ts +0 -85
- package/src/tools/privateContinuityEditTool.ts +0 -187
- package/src/tools/privateContinuityReadTool.ts +0 -106
- package/src/tools/readSkillTool.ts +0 -107
- package/src/tools/readTool.ts +0 -85
- package/src/tools/registry.ts +0 -103
- package/src/tools/writeFileTool.ts +0 -167
- package/src/ui/BrandSplash.tsx +0 -133
- package/src/ui/terminalTitle.ts +0 -30
- package/src/utils/images.ts +0 -140
- package/src/utils/markdownSegments.ts +0 -51
- package/src/utils/messages.ts +0 -37
- package/src/utils/withRetry.ts +0 -324
- /package/src/identity/{hub → manager}/continuity/state.ts +0 -0
- /package/src/identity/{hub → manager}/custody/helpers.ts +0 -0
- /package/src/identity/{hub → manager}/custody/preflight.ts +0 -0
- /package/src/identity/{hub → manager}/custody/state.ts +0 -0
- /package/src/identity/{hub → manager}/custody/useCustodyFlow.tsx +0 -0
- /package/src/identity/{hub → manager}/ens/EnsEditFlow.tsx +0 -0
- /package/src/identity/{hub → manager}/ens/advancedEnsValidation.ts +0 -0
- /package/src/identity/{hub → manager}/ens/state.ts +0 -0
- /package/src/identity/{hub → manager}/ens/transactions.ts +0 -0
- /package/src/identity/{hub → manager}/ens/types.ts +0 -0
- /package/src/identity/{hub → manager}/profile/identity.ts +0 -0
- /package/src/identity/{hub → manager}/restore/envelopes.ts +0 -0
- /package/src/identity/{hub → manager}/restore/helpers.ts +0 -0
- /package/src/identity/{hub → manager}/restore/recovery.ts +0 -0
- /package/src/identity/{hub → manager}/restore/resolve.ts +0 -0
- /package/src/identity/{hub → manager}/shared/components/BusyScreen.tsx +0 -0
- /package/src/identity/{hub → manager}/shared/components/NetworkScreen.tsx +0 -0
- /package/src/identity/{hub → manager}/shared/components/PinataJwtInput.tsx +0 -0
- /package/src/identity/{hub → manager}/shared/effects/receipts.ts +0 -0
- /package/src/identity/{hub → manager}/shared/effects/sync.ts +0 -0
- /package/src/identity/{hub → manager}/shared/model/format.ts +0 -0
- /package/src/identity/{hub → manager}/shared/operatorWallets.ts +0 -0
- /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/ownership.ts +0 -0
- /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/types.ts +0 -0
- /package/src/identity/{hub → manager}/shared/reconciliation/walletSetup.ts +0 -0
- /package/src/identity/{hub → manager}/shared/txGuard.ts +0 -0
- /package/src/identity/{hub → manager}/transfer/progress.ts +0 -0
- /package/src/identity/{hub → manager}/transfer/state.ts +0 -0
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { PROVIDERS, getConfigDir } from './config.js'
|
|
4
|
-
import { rmSecret } from './secrets.js'
|
|
5
|
-
|
|
6
|
-
const PRESERVED_LOCAL_MODEL_ENTRIES = new Set([
|
|
7
|
-
'local-models.json',
|
|
8
|
-
'local-runner.json',
|
|
9
|
-
'models',
|
|
10
|
-
'runners',
|
|
11
|
-
])
|
|
12
|
-
|
|
13
|
-
const SECRET_ACCOUNTS = [
|
|
14
|
-
'ethereum:default',
|
|
15
|
-
'pinata:jwt',
|
|
16
|
-
...PROVIDERS,
|
|
17
|
-
] as const
|
|
18
|
-
|
|
19
|
-
export type FactoryResetPlan = {
|
|
20
|
-
configDir: string
|
|
21
|
-
deletePaths: string[]
|
|
22
|
-
preservedPaths: string[]
|
|
23
|
-
preservedDescriptions: string[]
|
|
24
|
-
remoteDescriptions: string[]
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type FactoryResetResult = {
|
|
28
|
-
deletedPaths: string[]
|
|
29
|
-
preservedPaths: string[]
|
|
30
|
-
clearedSecretAccounts: string[]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function createFactoryResetPlan(): Promise<FactoryResetPlan> {
|
|
34
|
-
const configDir = path.resolve(getConfigDir())
|
|
35
|
-
const entries = await readConfigEntries(configDir)
|
|
36
|
-
const deletePaths: string[] = []
|
|
37
|
-
const preservedPaths: string[] = []
|
|
38
|
-
|
|
39
|
-
for (const entry of entries) {
|
|
40
|
-
const target = path.join(configDir, entry)
|
|
41
|
-
assertInsideConfigDir(configDir, target)
|
|
42
|
-
if (PRESERVED_LOCAL_MODEL_ENTRIES.has(entry)) preservedPaths.push(target)
|
|
43
|
-
else deletePaths.push(target)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
configDir,
|
|
48
|
-
deletePaths,
|
|
49
|
-
preservedPaths,
|
|
50
|
-
preservedDescriptions: [
|
|
51
|
-
'Local GGUF models and metadata',
|
|
52
|
-
'llama.cpp runner assets',
|
|
53
|
-
'External package-installed runtimes',
|
|
54
|
-
],
|
|
55
|
-
remoteDescriptions: [
|
|
56
|
-
'ERC-8004 tokens and onchain records',
|
|
57
|
-
'IPFS snapshots and public metadata',
|
|
58
|
-
],
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export async function runFactoryReset(options: { clearSecrets?: boolean } = {}): Promise<FactoryResetResult> {
|
|
63
|
-
const plan = await createFactoryResetPlan()
|
|
64
|
-
const clearedSecretAccounts: string[] = []
|
|
65
|
-
if (options.clearSecrets ?? true) {
|
|
66
|
-
for (const account of SECRET_ACCOUNTS) {
|
|
67
|
-
try {
|
|
68
|
-
await rmSecret(account)
|
|
69
|
-
clearedSecretAccounts.push(account)
|
|
70
|
-
} catch {
|
|
71
|
-
continue
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const deletedPaths: string[] = []
|
|
77
|
-
for (const target of plan.deletePaths) {
|
|
78
|
-
assertInsideConfigDir(plan.configDir, target)
|
|
79
|
-
await fs.rm(target, { recursive: true, force: true })
|
|
80
|
-
deletedPaths.push(target)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
deletedPaths,
|
|
85
|
-
preservedPaths: plan.preservedPaths,
|
|
86
|
-
clearedSecretAccounts,
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function formatFactoryResetPlan(plan: FactoryResetPlan): string {
|
|
91
|
-
return [
|
|
92
|
-
'Ethagent Reset',
|
|
93
|
-
'',
|
|
94
|
-
'Deletes:',
|
|
95
|
-
...formatDeleteSummary(plan.deletePaths.length),
|
|
96
|
-
'',
|
|
97
|
-
'Keeps:',
|
|
98
|
-
...plan.preservedDescriptions.map(item => ` - ${item}`),
|
|
99
|
-
'',
|
|
100
|
-
'Not Touched:',
|
|
101
|
-
...plan.remoteDescriptions.map(item => ` - ${item}`),
|
|
102
|
-
].join('\n')
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async function readConfigEntries(configDir: string): Promise<string[]> {
|
|
106
|
-
try {
|
|
107
|
-
return await fs.readdir(configDir)
|
|
108
|
-
} catch (err: unknown) {
|
|
109
|
-
if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []
|
|
110
|
-
throw err
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function formatDeleteSummary(count: number): string[] {
|
|
115
|
-
if (count === 0) return [' - No local ethagent data found']
|
|
116
|
-
return [
|
|
117
|
-
' - Identity files, sessions, history, credentials',
|
|
118
|
-
` - ${count} local path${count === 1 ? '' : 's'} under ~/.ethagent`,
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function assertInsideConfigDir(configDir: string, target: string): void {
|
|
123
|
-
const relative = path.relative(path.resolve(configDir), path.resolve(target))
|
|
124
|
-
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
125
|
-
throw new Error(`refusing to reset path outside ethagent config: ${target}`)
|
|
126
|
-
}
|
|
127
|
-
}
|
package/src/storage/history.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { getConfigDir } from './config.js'
|
|
4
|
-
|
|
5
|
-
const MAX_ENTRIES = 500
|
|
6
|
-
|
|
7
|
-
export function getHistoryPath(): string {
|
|
8
|
-
return path.join(getConfigDir(), 'history.jsonl')
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
type Entry = { text: string; ts: number }
|
|
12
|
-
|
|
13
|
-
export async function appendHistory(text: string): Promise<void> {
|
|
14
|
-
const clean = text.trim()
|
|
15
|
-
if (!clean) return
|
|
16
|
-
try {
|
|
17
|
-
await fs.mkdir(getConfigDir(), { recursive: true })
|
|
18
|
-
} catch {
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
const line = JSON.stringify({ text: clean, ts: Date.now() } satisfies Entry) + '\n'
|
|
22
|
-
try {
|
|
23
|
-
await fs.appendFile(getHistoryPath(), line, { mode: 0o600 })
|
|
24
|
-
} catch {
|
|
25
|
-
return
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function readHistory(): Promise<string[]> {
|
|
30
|
-
let raw: string
|
|
31
|
-
try {
|
|
32
|
-
raw = await fs.readFile(getHistoryPath(), 'utf8')
|
|
33
|
-
} catch (err: unknown) {
|
|
34
|
-
if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []
|
|
35
|
-
throw err
|
|
36
|
-
}
|
|
37
|
-
const entries: Entry[] = []
|
|
38
|
-
for (const line of raw.split('\n')) {
|
|
39
|
-
const trimmed = line.trim()
|
|
40
|
-
if (!trimmed) continue
|
|
41
|
-
try {
|
|
42
|
-
entries.push(JSON.parse(trimmed) as Entry)
|
|
43
|
-
} catch {
|
|
44
|
-
continue
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const seen = new Set<string>()
|
|
48
|
-
const out: string[] = []
|
|
49
|
-
for (let i = entries.length - 1; i >= 0; i--) {
|
|
50
|
-
const entry = entries[i]
|
|
51
|
-
if (!entry) continue
|
|
52
|
-
if (seen.has(entry.text)) continue
|
|
53
|
-
seen.add(entry.text)
|
|
54
|
-
out.unshift(entry.text)
|
|
55
|
-
if (out.length >= MAX_ENTRIES) break
|
|
56
|
-
}
|
|
57
|
-
return out
|
|
58
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { z } from 'zod'
|
|
4
|
-
import { ensureConfigDir, getConfigDir } from './config.js'
|
|
5
|
-
import { SessionPermissionRuleSchema, type SessionPermissionRule } from '../tools/contracts.js'
|
|
6
|
-
import { atomicWriteText } from './atomicWrite.js'
|
|
7
|
-
|
|
8
|
-
const StoredPermissionRuleSchema = z.object({
|
|
9
|
-
workspaceRoot: z.string().min(1),
|
|
10
|
-
rule: SessionPermissionRuleSchema,
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
type StoredPermissionRule = z.infer<typeof StoredPermissionRuleSchema>
|
|
14
|
-
|
|
15
|
-
function getPermissionsPath(): string {
|
|
16
|
-
return path.join(getConfigDir(), 'permissions.json')
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function loadPermissionRules(workspaceRoot: string): Promise<SessionPermissionRule[]> {
|
|
20
|
-
const normalizedWorkspaceRoot = path.resolve(workspaceRoot)
|
|
21
|
-
const allRules = await loadAllPermissionRules()
|
|
22
|
-
return allRules
|
|
23
|
-
.filter(entry => path.resolve(entry.workspaceRoot) === normalizedWorkspaceRoot)
|
|
24
|
-
.map(entry => entry.rule)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function deletePermissionRule(workspaceRoot: string, rule: SessionPermissionRule): Promise<void> {
|
|
28
|
-
const normalizedWorkspaceRoot = path.resolve(workspaceRoot)
|
|
29
|
-
const allRules = await loadAllPermissionRules()
|
|
30
|
-
const next = allRules.filter(entry =>
|
|
31
|
-
!(path.resolve(entry.workspaceRoot) === normalizedWorkspaceRoot && JSON.stringify(entry.rule) === JSON.stringify(rule)),
|
|
32
|
-
)
|
|
33
|
-
await writeAllPermissionRules(next)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function clearPermissionRules(workspaceRoot: string): Promise<void> {
|
|
37
|
-
const normalizedWorkspaceRoot = path.resolve(workspaceRoot)
|
|
38
|
-
const allRules = await loadAllPermissionRules()
|
|
39
|
-
const next = allRules.filter(entry => path.resolve(entry.workspaceRoot) !== normalizedWorkspaceRoot)
|
|
40
|
-
await writeAllPermissionRules(next)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export async function savePermissionRule(workspaceRoot: string, rule: SessionPermissionRule): Promise<void> {
|
|
44
|
-
const allRules = await loadAllPermissionRules()
|
|
45
|
-
const normalizedWorkspaceRoot = path.resolve(workspaceRoot)
|
|
46
|
-
const nextEntry: StoredPermissionRule = { workspaceRoot: normalizedWorkspaceRoot, rule }
|
|
47
|
-
const deduped = [...allRules.filter(entry => !sameStoredRule(entry, nextEntry)), nextEntry]
|
|
48
|
-
await writeAllPermissionRules(deduped)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function loadAllPermissionRules(): Promise<StoredPermissionRule[]> {
|
|
52
|
-
let raw: string
|
|
53
|
-
try {
|
|
54
|
-
raw = await fs.readFile(getPermissionsPath(), 'utf8')
|
|
55
|
-
} catch (error: unknown) {
|
|
56
|
-
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []
|
|
57
|
-
throw error
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
const parsed = JSON.parse(raw)
|
|
62
|
-
return z.array(StoredPermissionRuleSchema).parse(parsed)
|
|
63
|
-
} catch {
|
|
64
|
-
return []
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function writeAllPermissionRules(rules: StoredPermissionRule[]): Promise<void> {
|
|
69
|
-
await ensureConfigDir()
|
|
70
|
-
const file = getPermissionsPath()
|
|
71
|
-
await atomicWriteText(file, JSON.stringify(rules, null, 2) + '\n')
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function sameStoredRule(left: StoredPermissionRule, right: StoredPermissionRule): boolean {
|
|
75
|
-
return left.workspaceRoot === right.workspaceRoot && JSON.stringify(left.rule) === JSON.stringify(right.rule)
|
|
76
|
-
}
|
package/src/storage/rewind.ts
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { z } from 'zod'
|
|
4
|
-
import { ensureConfigDir, getConfigDir } from './config.js'
|
|
5
|
-
import { atomicWriteText } from './atomicWrite.js'
|
|
6
|
-
|
|
7
|
-
const RewindSnapshotSchema = z.object({
|
|
8
|
-
id: z.string().optional(),
|
|
9
|
-
workspaceRoot: z.string().min(1),
|
|
10
|
-
filePath: z.string().min(1),
|
|
11
|
-
relativePath: z.string().optional(),
|
|
12
|
-
existedBefore: z.boolean(),
|
|
13
|
-
previousContent: z.string(),
|
|
14
|
-
changeSummary: z.string().optional(),
|
|
15
|
-
createdAt: z.string(),
|
|
16
|
-
sessionId: z.string().optional(),
|
|
17
|
-
turnId: z.string().optional(),
|
|
18
|
-
messageRole: z.literal('user').optional(),
|
|
19
|
-
promptSnippet: z.string().optional(),
|
|
20
|
-
checkpointLabel: z.string().optional(),
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
type RewindSnapshot = z.infer<typeof RewindSnapshotSchema>
|
|
24
|
-
export type RewindEntry = {
|
|
25
|
-
id: string
|
|
26
|
-
workspaceRoot: string
|
|
27
|
-
filePath: string
|
|
28
|
-
relativePath: string
|
|
29
|
-
existedBefore: boolean
|
|
30
|
-
previousContent: string
|
|
31
|
-
changeSummary: string
|
|
32
|
-
createdAt: string
|
|
33
|
-
sessionId?: string
|
|
34
|
-
turnId?: string
|
|
35
|
-
messageRole?: 'user'
|
|
36
|
-
promptSnippet: string
|
|
37
|
-
checkpointLabel: string
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type ListRewindEntriesOptions = {
|
|
41
|
-
limit?: number
|
|
42
|
-
offset?: number
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getRewindPath(): string {
|
|
46
|
-
return path.join(getConfigDir(), 'rewind.jsonl')
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export async function recordRewindSnapshot(snapshot: RewindSnapshot): Promise<void> {
|
|
50
|
-
await ensureConfigDir()
|
|
51
|
-
const normalized = normalizeSnapshot(snapshot)
|
|
52
|
-
if (isIdentityMarkdownSnapshot(normalized)) return
|
|
53
|
-
await fs.appendFile(getRewindPath(), `${JSON.stringify(normalized)}\n`, { encoding: 'utf8', mode: 0o600 })
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export async function rewindWorkspaceEdits(
|
|
57
|
-
workspaceRoot: string,
|
|
58
|
-
steps = 1,
|
|
59
|
-
): Promise<{ reverted: number; files: string[] }> {
|
|
60
|
-
const normalizedWorkspaceRoot = path.resolve(workspaceRoot)
|
|
61
|
-
const snapshots = await loadSnapshots()
|
|
62
|
-
const candidates = snapshots
|
|
63
|
-
.map((snapshot, index) => ({ snapshot, index }))
|
|
64
|
-
.filter(entry =>
|
|
65
|
-
path.resolve(entry.snapshot.workspaceRoot) === normalizedWorkspaceRoot &&
|
|
66
|
-
!isIdentityMarkdownSnapshot(entry.snapshot),
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
if (candidates.length === 0) return { reverted: 0, files: [] }
|
|
70
|
-
|
|
71
|
-
const selected = candidates.slice(Math.max(0, candidates.length - steps))
|
|
72
|
-
const revertedFiles: string[] = []
|
|
73
|
-
|
|
74
|
-
for (let index = selected.length - 1; index >= 0; index -= 1) {
|
|
75
|
-
const snapshot = selected[index]!.snapshot
|
|
76
|
-
if (snapshot.existedBefore) {
|
|
77
|
-
await fs.mkdir(path.dirname(snapshot.filePath), { recursive: true })
|
|
78
|
-
await fs.writeFile(snapshot.filePath, snapshot.previousContent, 'utf8')
|
|
79
|
-
} else {
|
|
80
|
-
try {
|
|
81
|
-
await fs.unlink(snapshot.filePath)
|
|
82
|
-
} catch (error: unknown) {
|
|
83
|
-
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
revertedFiles.push(snapshot.filePath)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const selectedIndexes = new Set(selected.map(entry => entry.index))
|
|
90
|
-
const remaining = snapshots.filter((_snapshot, index) => !selectedIndexes.has(index))
|
|
91
|
-
await writeSnapshots(remaining)
|
|
92
|
-
|
|
93
|
-
return { reverted: selected.length, files: revertedFiles }
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function loadSnapshots(): Promise<RewindSnapshot[]> {
|
|
97
|
-
let raw: string
|
|
98
|
-
try {
|
|
99
|
-
raw = await fs.readFile(getRewindPath(), 'utf8')
|
|
100
|
-
} catch (error: unknown) {
|
|
101
|
-
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []
|
|
102
|
-
throw error
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const out: RewindSnapshot[] = []
|
|
106
|
-
for (const line of raw.split('\n')) {
|
|
107
|
-
const trimmed = line.trim()
|
|
108
|
-
if (!trimmed) continue
|
|
109
|
-
try {
|
|
110
|
-
out.push(normalizeSnapshot(RewindSnapshotSchema.parse(JSON.parse(trimmed))))
|
|
111
|
-
} catch {
|
|
112
|
-
continue
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return out
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export async function listRewindEntries(
|
|
119
|
-
workspaceRoot: string,
|
|
120
|
-
options: ListRewindEntriesOptions = {},
|
|
121
|
-
): Promise<RewindEntry[]> {
|
|
122
|
-
const normalizedWorkspaceRoot = path.resolve(workspaceRoot)
|
|
123
|
-
const limit = options.limit ?? 30
|
|
124
|
-
const offset = options.offset ?? 0
|
|
125
|
-
const snapshots = await loadSnapshots()
|
|
126
|
-
return snapshots
|
|
127
|
-
.filter(snapshot => !isIdentityMarkdownSnapshot(snapshot))
|
|
128
|
-
.filter(snapshot => isSnapshotWithinScope(snapshot, normalizedWorkspaceRoot))
|
|
129
|
-
.map(snapshot => toEntry(snapshot))
|
|
130
|
-
.reverse()
|
|
131
|
-
.slice(offset, offset + limit)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export async function groupRewindEntriesByTurn(
|
|
135
|
-
workspaceRoot: string,
|
|
136
|
-
sessionId: string,
|
|
137
|
-
): Promise<Map<string, RewindEntry[]>> {
|
|
138
|
-
const normalizedWorkspaceRoot = path.resolve(workspaceRoot)
|
|
139
|
-
const snapshots = await loadSnapshots()
|
|
140
|
-
const grouped = new Map<string, RewindEntry[]>()
|
|
141
|
-
for (const snapshot of snapshots) {
|
|
142
|
-
if (isIdentityMarkdownSnapshot(snapshot)) continue
|
|
143
|
-
if (!isSnapshotWithinScope(snapshot, normalizedWorkspaceRoot)) continue
|
|
144
|
-
if (snapshot.sessionId !== sessionId) continue
|
|
145
|
-
if (!snapshot.turnId) continue
|
|
146
|
-
const entry = toEntry(snapshot)
|
|
147
|
-
const bucket = grouped.get(snapshot.turnId)
|
|
148
|
-
if (bucket) bucket.push(entry)
|
|
149
|
-
else grouped.set(snapshot.turnId, [entry])
|
|
150
|
-
}
|
|
151
|
-
return grouped
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export async function rewindWorkspaceEditsByEntryIds(
|
|
155
|
-
workspaceRoot: string,
|
|
156
|
-
entryIds: string[],
|
|
157
|
-
): Promise<{ reverted: number; files: string[] }> {
|
|
158
|
-
const normalizedWorkspaceRoot = path.resolve(workspaceRoot)
|
|
159
|
-
const selectedIds = new Set(entryIds)
|
|
160
|
-
const snapshots = await loadSnapshots()
|
|
161
|
-
const selected = snapshots
|
|
162
|
-
.map((snapshot, index) => ({ snapshot, index }))
|
|
163
|
-
.filter(entry =>
|
|
164
|
-
path.resolve(entry.snapshot.workspaceRoot) === normalizedWorkspaceRoot &&
|
|
165
|
-
!isIdentityMarkdownSnapshot(entry.snapshot) &&
|
|
166
|
-
selectedIds.has(entry.snapshot.id!),
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
if (selected.length === 0) return { reverted: 0, files: [] }
|
|
170
|
-
|
|
171
|
-
const revertedFiles: string[] = []
|
|
172
|
-
for (let index = selected.length - 1; index >= 0; index -= 1) {
|
|
173
|
-
const snapshot = selected[index]!.snapshot
|
|
174
|
-
if (snapshot.existedBefore) {
|
|
175
|
-
await fs.mkdir(path.dirname(snapshot.filePath), { recursive: true })
|
|
176
|
-
await fs.writeFile(snapshot.filePath, snapshot.previousContent, 'utf8')
|
|
177
|
-
} else {
|
|
178
|
-
try {
|
|
179
|
-
await fs.unlink(snapshot.filePath)
|
|
180
|
-
} catch (error: unknown) {
|
|
181
|
-
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
revertedFiles.push(snapshot.filePath)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const selectedIndexes = new Set(selected.map(entry => entry.index))
|
|
188
|
-
const remaining = snapshots.filter((_snapshot, index) => !selectedIndexes.has(index))
|
|
189
|
-
await writeSnapshots(remaining)
|
|
190
|
-
|
|
191
|
-
return { reverted: selected.length, files: revertedFiles }
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async function writeSnapshots(snapshots: RewindSnapshot[]): Promise<void> {
|
|
195
|
-
await ensureConfigDir()
|
|
196
|
-
const file = getRewindPath()
|
|
197
|
-
const body = snapshots.map(snapshot => JSON.stringify(snapshot)).join('\n')
|
|
198
|
-
await atomicWriteText(file, body ? `${body}\n` : '')
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function normalizeSnapshot(snapshot: RewindSnapshot): RewindSnapshot {
|
|
202
|
-
const workspaceRoot = path.resolve(snapshot.workspaceRoot)
|
|
203
|
-
const filePath = path.resolve(snapshot.filePath)
|
|
204
|
-
return {
|
|
205
|
-
...snapshot,
|
|
206
|
-
id: snapshot.id ?? stableSnapshotId(workspaceRoot, filePath, snapshot.createdAt),
|
|
207
|
-
workspaceRoot,
|
|
208
|
-
filePath,
|
|
209
|
-
relativePath: snapshot.relativePath ?? (path.relative(workspaceRoot, filePath) || path.basename(filePath)),
|
|
210
|
-
changeSummary: snapshot.changeSummary ?? (snapshot.existedBefore ? 'restore previous file contents' : 'remove created file'),
|
|
211
|
-
promptSnippet: normalizeSnippet(snapshot.promptSnippet),
|
|
212
|
-
checkpointLabel: normalizeSnippet(snapshot.checkpointLabel) || normalizeSnippet(snapshot.promptSnippet),
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function toEntry(snapshot: RewindSnapshot): RewindEntry {
|
|
217
|
-
return {
|
|
218
|
-
id: snapshot.id!,
|
|
219
|
-
workspaceRoot: snapshot.workspaceRoot,
|
|
220
|
-
filePath: snapshot.filePath,
|
|
221
|
-
relativePath: snapshot.relativePath!,
|
|
222
|
-
existedBefore: snapshot.existedBefore,
|
|
223
|
-
previousContent: snapshot.previousContent,
|
|
224
|
-
changeSummary: snapshot.changeSummary!,
|
|
225
|
-
createdAt: snapshot.createdAt,
|
|
226
|
-
sessionId: snapshot.sessionId,
|
|
227
|
-
turnId: snapshot.turnId,
|
|
228
|
-
messageRole: snapshot.messageRole,
|
|
229
|
-
promptSnippet: snapshot.promptSnippet ?? '',
|
|
230
|
-
checkpointLabel: snapshot.checkpointLabel ?? snapshot.promptSnippet ?? 'Untitled checkpoint',
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function stableSnapshotId(workspaceRoot: string, filePath: string, createdAt: string): string {
|
|
235
|
-
const rel = path.relative(workspaceRoot, filePath) || path.basename(filePath)
|
|
236
|
-
return `${createdAt}:${rel}`.replaceAll('\\', '/')
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function isSnapshotWithinScope(snapshot: RewindSnapshot, scopeRoot: string): boolean {
|
|
240
|
-
if (path.resolve(snapshot.workspaceRoot) === scopeRoot) return true
|
|
241
|
-
const relative = path.relative(scopeRoot, path.resolve(snapshot.filePath))
|
|
242
|
-
return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function isIdentityMarkdownSnapshot(snapshot: RewindSnapshot): boolean {
|
|
246
|
-
const relativePath = (snapshot.relativePath ?? '').replaceAll('\\', '/').toLowerCase()
|
|
247
|
-
const basename = path.basename(snapshot.filePath).toLowerCase()
|
|
248
|
-
if (relativePath.startsWith('identity-vault/') && IDENTITY_MARKDOWN_FILES.has(basename)) return true
|
|
249
|
-
|
|
250
|
-
const continuityRoot = path.resolve(getConfigDir(), 'continuity')
|
|
251
|
-
const relativeToContinuity = path.relative(continuityRoot, path.resolve(snapshot.filePath))
|
|
252
|
-
return (
|
|
253
|
-
relativeToContinuity !== '' &&
|
|
254
|
-
!relativeToContinuity.startsWith('..') &&
|
|
255
|
-
!path.isAbsolute(relativeToContinuity) &&
|
|
256
|
-
IDENTITY_MARKDOWN_FILES.has(basename)
|
|
257
|
-
)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const IDENTITY_MARKDOWN_FILES = new Set(['soul.md', 'memory.md', 'agent-card.json'])
|
|
261
|
-
|
|
262
|
-
function normalizeSnippet(input?: string): string {
|
|
263
|
-
const normalized = (input ?? '').replace(/\s+/g, ' ').trim()
|
|
264
|
-
if (!normalized) return ''
|
|
265
|
-
return normalized.length <= 120 ? normalized : `${normalized.slice(0, 117)}...`
|
|
266
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { getConfigDir } from './config.js'
|
|
4
|
-
import type { SessionMessage } from './sessions.js'
|
|
5
|
-
|
|
6
|
-
export function getExportsDir(): string {
|
|
7
|
-
return path.join(getConfigDir(), 'exports')
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export async function exportSessionMarkdown(
|
|
11
|
-
id: string,
|
|
12
|
-
messages: SessionMessage[],
|
|
13
|
-
meta: { model: string; provider: string },
|
|
14
|
-
): Promise<string> {
|
|
15
|
-
await fs.mkdir(getExportsDir(), { recursive: true })
|
|
16
|
-
const file = path.join(getExportsDir(), `${id}.md`)
|
|
17
|
-
|
|
18
|
-
const userTurns = messages.filter(m => m.role === 'user').length
|
|
19
|
-
const lines: string[] = []
|
|
20
|
-
lines.push('---')
|
|
21
|
-
lines.push(`session: ${id}`)
|
|
22
|
-
lines.push(`provider: ${meta.provider}`)
|
|
23
|
-
lines.push(`model: ${meta.model}`)
|
|
24
|
-
lines.push(`turns: ${userTurns}`)
|
|
25
|
-
lines.push(`exportedAt: ${new Date().toISOString()}`)
|
|
26
|
-
lines.push('---')
|
|
27
|
-
lines.push('')
|
|
28
|
-
for (const m of messages) {
|
|
29
|
-
if (m.role === 'system') continue
|
|
30
|
-
const header =
|
|
31
|
-
m.role === 'user'
|
|
32
|
-
? '## user'
|
|
33
|
-
: m.role === 'assistant'
|
|
34
|
-
? '## assistant'
|
|
35
|
-
: m.role === 'tool_use'
|
|
36
|
-
? `## tool use · ${m.name}`
|
|
37
|
-
: `## tool result · ${m.name}`
|
|
38
|
-
lines.push(`${header} <sub>${m.createdAt}</sub>`)
|
|
39
|
-
lines.push('')
|
|
40
|
-
if (m.role === 'tool_use') {
|
|
41
|
-
lines.push(JSON.stringify(m.input, null, 2))
|
|
42
|
-
} else {
|
|
43
|
-
lines.push(m.content)
|
|
44
|
-
}
|
|
45
|
-
lines.push('')
|
|
46
|
-
}
|
|
47
|
-
await fs.writeFile(file, lines.join('\n'), { mode: 0o600 })
|
|
48
|
-
return file
|
|
49
|
-
}
|