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
|
@@ -1,507 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function continuityVaultRef(identity: Pick<EthagentIdentity, 'chainId' | 'identityRegistryAddress' | 'agentId' | 'address'>): ContinuityVaultRef {
|
|
36
|
-
const dir = path.join(getConfigDir(), 'continuity', continuityVaultId(identity))
|
|
37
|
-
return {
|
|
38
|
-
dir,
|
|
39
|
-
soulPath: path.join(dir, 'SOUL.md'),
|
|
40
|
-
memoryPath: path.join(dir, 'MEMORY.md'),
|
|
41
|
-
publicSkillsPath: path.join(dir, 'skills.json'),
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function ensureContinuityVault(identity: EthagentIdentity): Promise<ContinuityVaultRef> {
|
|
46
|
-
const ref = continuityVaultRef(identity)
|
|
47
|
-
await fs.mkdir(ref.dir, { recursive: true, mode: 0o700 })
|
|
48
|
-
return ref
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export async function ensureContinuityFiles(identity: EthagentIdentity): Promise<ContinuityFiles> {
|
|
52
|
-
const ref = await ensureContinuityVault(identity)
|
|
53
|
-
const defaults = defaultContinuityFiles(identity)
|
|
54
|
-
await writeMissingPrivateFile(ref.soulPath, defaults['SOUL.md'])
|
|
55
|
-
await writeMissingPrivateFile(ref.memoryPath, defaults['MEMORY.md'])
|
|
56
|
-
return readContinuityFiles(identity)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export async function readContinuityFiles(identity: EthagentIdentity): Promise<ContinuityFiles> {
|
|
60
|
-
const ref = await ensureContinuityVault(identity)
|
|
61
|
-
const defaults = defaultContinuityFiles(identity)
|
|
62
|
-
return {
|
|
63
|
-
'SOUL.md': await readOrDefault(ref.soulPath, defaults['SOUL.md']),
|
|
64
|
-
'MEMORY.md': await readOrDefault(ref.memoryPath, defaults['MEMORY.md']),
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export async function writeContinuityFiles(identity: EthagentIdentity, files: ContinuityFiles): Promise<ContinuityVaultRef> {
|
|
69
|
-
const ref = await ensureContinuityVault(identity)
|
|
70
|
-
await atomicWriteText(ref.soulPath, ensureTrailingNewline(files['SOUL.md']), { mode: 0o600 })
|
|
71
|
-
await atomicWriteText(ref.memoryPath, ensureTrailingNewline(files['MEMORY.md']), { mode: 0o600 })
|
|
72
|
-
return ref
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export async function ensureIdentityMarkdownScaffold(
|
|
76
|
-
identity: EthagentIdentity,
|
|
77
|
-
options: { publicSkillsFallback?: string | (() => Promise<string>) } = {},
|
|
78
|
-
): Promise<IdentityMarkdownScaffold> {
|
|
79
|
-
const privateFiles = await ensureContinuityFiles(identity)
|
|
80
|
-
const publicSkills = await ensurePublicSkillsFile(identity, { fallback: options.publicSkillsFallback })
|
|
81
|
-
return {
|
|
82
|
-
...privateFiles,
|
|
83
|
-
'skills.json': publicSkills,
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export async function writeIdentityMarkdownScaffold(
|
|
88
|
-
identity: EthagentIdentity,
|
|
89
|
-
files: IdentityMarkdownScaffold,
|
|
90
|
-
): Promise<ContinuityVaultRef> {
|
|
91
|
-
const ref = await writeContinuityFiles(identity, {
|
|
92
|
-
'SOUL.md': files['SOUL.md'],
|
|
93
|
-
'MEMORY.md': files['MEMORY.md'],
|
|
94
|
-
})
|
|
95
|
-
await writePublicSkillsFile(identity, files['skills.json'])
|
|
96
|
-
return ref
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export async function syncIdentityMarkdownScaffold(identity: EthagentIdentity): Promise<IdentityMarkdownScaffold> {
|
|
100
|
-
const next = await prepareSyncedIdentityMarkdownScaffold(identity)
|
|
101
|
-
await writeIdentityMarkdownScaffold(identity, next)
|
|
102
|
-
return next
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export async function prepareSyncedIdentityMarkdownScaffold(identity: EthagentIdentity): Promise<IdentityMarkdownScaffold> {
|
|
106
|
-
await ensureIdentityMarkdownScaffold(identity)
|
|
107
|
-
const privateFiles = await readContinuityFiles(identity)
|
|
108
|
-
const publicSkills = await readPublicSkillsFile(identity)
|
|
109
|
-
const privateDefaults = defaultContinuityFiles(identity)
|
|
110
|
-
const publicDefault = defaultPublicSkillsJson(identity)
|
|
111
|
-
return {
|
|
112
|
-
'SOUL.md': syncGeneratedMarkdown(privateFiles['SOUL.md'], privateDefaults['SOUL.md'], [
|
|
113
|
-
{ marker: 'identity', legacyHeading: 'Identity' },
|
|
114
|
-
]),
|
|
115
|
-
'MEMORY.md': syncGeneratedMarkdown(privateFiles['MEMORY.md'], privateDefaults['MEMORY.md'], [
|
|
116
|
-
{ marker: 'identity' },
|
|
117
|
-
]),
|
|
118
|
-
'skills.json': syncSkillsJson(publicSkills, publicDefault),
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export async function prepareSyncedPublicSkillsJson(identity: EthagentIdentity): Promise<string> {
|
|
123
|
-
await ensurePublicSkillsFile(identity)
|
|
124
|
-
const publicSkills = await readPublicSkillsFile(identity)
|
|
125
|
-
return syncSkillsJson(publicSkills, defaultPublicSkillsJson(identity))
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function syncSkillsJson(existing: string, fresh: string): string {
|
|
129
|
-
try {
|
|
130
|
-
const existingParsed = JSON.parse(existing)
|
|
131
|
-
const freshParsed = JSON.parse(fresh)
|
|
132
|
-
const merged = {
|
|
133
|
-
...existingParsed,
|
|
134
|
-
...freshParsed,
|
|
135
|
-
skills: existingParsed.skills || freshParsed.skills,
|
|
136
|
-
inputModes: existingParsed.inputModes || freshParsed.inputModes,
|
|
137
|
-
outputModes: existingParsed.outputModes || freshParsed.outputModes,
|
|
138
|
-
}
|
|
139
|
-
return `${JSON.stringify(merged, null, 2)}\n`
|
|
140
|
-
} catch {
|
|
141
|
-
return fresh
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export async function ensurePublicSkillsFile(
|
|
146
|
-
identity: EthagentIdentity,
|
|
147
|
-
options: { fallback?: string | (() => Promise<string>) } = {},
|
|
148
|
-
): Promise<string> {
|
|
149
|
-
const ref = await ensureContinuityVault(identity)
|
|
150
|
-
if (await exists(ref.publicSkillsPath)) return readPublicSkillsFile(identity)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const fallback = await resolvePublicSkillsFallback(identity, options.fallback)
|
|
154
|
-
await atomicWriteText(ref.publicSkillsPath, ensureTrailingNewline(fallback), { mode: 0o644 })
|
|
155
|
-
return readPublicSkillsFile(identity)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export async function readPublicSkillsFile(identity: EthagentIdentity): Promise<string> {
|
|
159
|
-
const ref = await ensureContinuityVault(identity)
|
|
160
|
-
return readOrDefault(ref.publicSkillsPath, defaultPublicSkillsJson(identity))
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export async function writePublicSkillsFile(identity: EthagentIdentity, content: string): Promise<ContinuityVaultRef> {
|
|
164
|
-
const ref = await ensureContinuityVault(identity)
|
|
165
|
-
await atomicWriteText(ref.publicSkillsPath, ensureTrailingNewline(content), { mode: 0o644 })
|
|
166
|
-
return ref
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export async function continuityVaultStatus(identity: EthagentIdentity): Promise<{ ready: boolean; files: ContinuityVaultRef }> {
|
|
170
|
-
const ref = continuityVaultRef(identity)
|
|
171
|
-
const [soul, memory] = await Promise.all([exists(ref.soulPath), exists(ref.memoryPath)])
|
|
172
|
-
return { ready: soul && memory, files: ref }
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export async function continuityWorkingTreeStatus(
|
|
176
|
-
identity: EthagentIdentity,
|
|
177
|
-
publishedSnapshot?: { contentHashes?: ContinuitySnapshotContentHashes },
|
|
178
|
-
): Promise<ContinuityWorkingTreeStatus> {
|
|
179
|
-
const ref = continuityVaultRef(identity)
|
|
180
|
-
const stats = await Promise.all([
|
|
181
|
-
statIfExists(ref.soulPath),
|
|
182
|
-
statIfExists(ref.memoryPath),
|
|
183
|
-
statIfExists(ref.publicSkillsPath),
|
|
184
|
-
])
|
|
185
|
-
const newestMs = Math.max(0, ...stats.flatMap(stat => stat ? [stat.mtimeMs] : []))
|
|
186
|
-
const ready = Boolean(stats[0] && stats[1])
|
|
187
|
-
const localContentHashes = ready
|
|
188
|
-
? await localContinuitySnapshotContentHashes(identity).catch(() => undefined)
|
|
189
|
-
: undefined
|
|
190
|
-
const publishedContentHashes = publishedSnapshot?.contentHashes
|
|
191
|
-
const publishState: ContinuityPublishState = !ready
|
|
192
|
-
? 'not-restored'
|
|
193
|
-
: !identity.backup?.cid
|
|
194
|
-
? 'not-published'
|
|
195
|
-
: !localContentHashes || !publishedContentHashes
|
|
196
|
-
? 'verify-needed'
|
|
197
|
-
: equalContinuitySnapshotHashes(localContentHashes, publishedContentHashes)
|
|
198
|
-
? 'published'
|
|
199
|
-
: 'local-changes'
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
ready,
|
|
203
|
-
...(newestMs > 0 ? { newestLocalChangeAt: new Date(newestMs).toISOString() } : {}),
|
|
204
|
-
localChangedAfterBackup: publishState === 'local-changes',
|
|
205
|
-
publishState,
|
|
206
|
-
...(localContentHashes ? { localContentHashes } : {}),
|
|
207
|
-
...(publishedContentHashes ? { publishedContentHashes } : {}),
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export async function localContinuitySnapshotContentHashes(
|
|
212
|
-
identity: EthagentIdentity,
|
|
213
|
-
): Promise<ContinuitySnapshotContentHashes> {
|
|
214
|
-
const privateFiles = await readContinuityFiles(identity)
|
|
215
|
-
const publicSkills = await readPublicSkillsFile(identity)
|
|
216
|
-
return continuitySnapshotContentHashes(privateFiles, publicSkills)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export function continuitySnapshotContentHashes(
|
|
220
|
-
privateFiles: ContinuityFiles,
|
|
221
|
-
publicSkills: string,
|
|
222
|
-
): ContinuitySnapshotContentHashes {
|
|
223
|
-
return {
|
|
224
|
-
'SOUL.md': hashContinuitySnapshotContent(privateFiles['SOUL.md']),
|
|
225
|
-
'MEMORY.md': hashContinuitySnapshotContent(privateFiles['MEMORY.md']),
|
|
226
|
-
'skills.json': hashContinuitySnapshotContent(publicSkills),
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export function equalContinuitySnapshotHashes(
|
|
231
|
-
a: ContinuitySnapshotContentHashes,
|
|
232
|
-
b: ContinuitySnapshotContentHashes,
|
|
233
|
-
): boolean {
|
|
234
|
-
return a['SOUL.md'] === b['SOUL.md']
|
|
235
|
-
&& a['MEMORY.md'] === b['MEMORY.md']
|
|
236
|
-
&& a['skills.json'] === b['skills.json']
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function hashContinuitySnapshotContent(value: string): string {
|
|
240
|
-
return createHash('sha256').update(normalizeSnapshotContent(value), 'utf8').digest('hex')
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function normalizeSnapshotContent(value: string): string {
|
|
244
|
-
const normalized = value.replace(/\r\n?/g, '\n')
|
|
245
|
-
return normalized.endsWith('\n') ? normalized : `${normalized}\n`
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
export function continuityAgentSnapshot(identity: EthagentIdentity): ContinuityAgentSnapshot {
|
|
249
|
-
const state = identity.state ?? {}
|
|
250
|
-
return {
|
|
251
|
-
...(identity.chainId ? { chainId: identity.chainId } : {}),
|
|
252
|
-
...(identity.identityRegistryAddress ? { identityRegistryAddress: identity.identityRegistryAddress } : {}),
|
|
253
|
-
...(identity.agentId ? { agentId: identity.agentId } : {}),
|
|
254
|
-
...(identity.agentUri ? { agentUri: identity.agentUri } : {}),
|
|
255
|
-
...(identity.metadataCid ? { metadataCid: identity.metadataCid } : {}),
|
|
256
|
-
...(typeof state.name === 'string' ? { name: state.name } : {}),
|
|
257
|
-
...(typeof state.description === 'string' ? { description: state.description } : {}),
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export function defaultContinuityFiles(identity: EthagentIdentity, now = new Date()): ContinuityFiles {
|
|
262
|
-
const owner = identity.ownerAddress ?? identity.address
|
|
263
|
-
const created = now.toISOString().slice(0, 10)
|
|
264
|
-
const identityBlock = renderPrivateIdentityBlock({
|
|
265
|
-
owner,
|
|
266
|
-
token: identity.agentId ? `#${identity.agentId}` : 'pending registration',
|
|
267
|
-
chainId: identity.chainId ? identity.chainId.toString() : 'unknown',
|
|
268
|
-
registry: identity.identityRegistryAddress ?? 'unknown',
|
|
269
|
-
})
|
|
270
|
-
return {
|
|
271
|
-
'SOUL.md': [
|
|
272
|
-
'# SOUL.md',
|
|
273
|
-
'',
|
|
274
|
-
identityBlock,
|
|
275
|
-
'',
|
|
276
|
-
'## Persona',
|
|
277
|
-
'',
|
|
278
|
-
'- Describe the private agent persona, voice, and collaboration style.',
|
|
279
|
-
'- Keep standing behavior that should survive model switches and device restores.',
|
|
280
|
-
'- Prefer stable guidance over session-specific preferences.',
|
|
281
|
-
'',
|
|
282
|
-
'## Operating Principles',
|
|
283
|
-
'',
|
|
284
|
-
'- Record durable values, decision preferences, and owner-approved working principles.',
|
|
285
|
-
'- Keep implementation-specific facts in MEMORY.md unless they define behavior.',
|
|
286
|
-
'',
|
|
287
|
-
'## Private Instructions',
|
|
288
|
-
'',
|
|
289
|
-
'- Keep owner-specific standing instructions in this file.',
|
|
290
|
-
'- Do not share this file directly; save it via the Identity Hub encrypted snapshot.',
|
|
291
|
-
'- Public capabilities belong in skills.json.',
|
|
292
|
-
'',
|
|
293
|
-
'## Boundaries',
|
|
294
|
-
'',
|
|
295
|
-
'- Record private behavioral limits and owner-approved constraints here.',
|
|
296
|
-
'- Do not store seed phrases, private keys, raw wallet signatures, or API keys.',
|
|
297
|
-
'- Do not place public delegation claims here; keep them in skills.json.',
|
|
298
|
-
'',
|
|
299
|
-
'## Maintenance Rules',
|
|
300
|
-
'',
|
|
301
|
-
'- Keep the generated Agent Identity block intact; edit owner-authored sections below it.',
|
|
302
|
-
'- Do not duplicate the mutable public agent name here; it lives in token metadata and the Agent Card.',
|
|
303
|
-
'- Move factual project memory to MEMORY.md when it is not persona or instruction material.',
|
|
304
|
-
'- Revise or remove stale guidance instead of accumulating contradictions.',
|
|
305
|
-
'',
|
|
306
|
-
'## Change Notes',
|
|
307
|
-
'',
|
|
308
|
-
'- Add dated notes when the persona or long-lived private guidance changes.',
|
|
309
|
-
'',
|
|
310
|
-
`Created: ${created}`,
|
|
311
|
-
].join('\n') + '\n',
|
|
312
|
-
'MEMORY.md': [
|
|
313
|
-
'# MEMORY.md',
|
|
314
|
-
'',
|
|
315
|
-
identityBlock,
|
|
316
|
-
'',
|
|
317
|
-
'## Durable User Preferences',
|
|
318
|
-
'',
|
|
319
|
-
'- Add long-lived owner preferences that should survive across sessions and model switches.',
|
|
320
|
-
'',
|
|
321
|
-
'## Project Context',
|
|
322
|
-
'',
|
|
323
|
-
'- Add stable project facts, repo conventions, and active workstreams.',
|
|
324
|
-
'',
|
|
325
|
-
'## Decisions and Rationale',
|
|
326
|
-
'',
|
|
327
|
-
'- Record important decisions and why they were made.',
|
|
328
|
-
'',
|
|
329
|
-
'## Facts to Revalidate',
|
|
330
|
-
'',
|
|
331
|
-
'- Add time-sensitive facts that should be checked before reuse, with dates or source context when available.',
|
|
332
|
-
'',
|
|
333
|
-
'## Maintenance Rules',
|
|
334
|
-
'',
|
|
335
|
-
'- Prefer stable facts, preferences, and decisions over chat transcripts.',
|
|
336
|
-
'- Do not duplicate the mutable public agent name here; it lives in token metadata and the Agent Card.',
|
|
337
|
-
'- Add dates or source context when a note may become stale or environment-specific.',
|
|
338
|
-
'- Remove or rewrite stale memory instead of accumulating contradictions.',
|
|
339
|
-
'',
|
|
340
|
-
'## Boundaries',
|
|
341
|
-
'',
|
|
342
|
-
'- Do not store seed phrases, private keys, raw wallet signatures, or API keys.',
|
|
343
|
-
'- Do not store secrets unless the user explicitly asks and the risk is clear.',
|
|
344
|
-
'- Keep public capabilities in skills.json.',
|
|
345
|
-
'',
|
|
346
|
-
`Created: ${created}`,
|
|
347
|
-
].join('\n') + '\n',
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
export function defaultPublicSkillsJson(identity: EthagentIdentity): string {
|
|
352
|
-
return renderPublicSkillsJson(defaultPublicSkillsProfile(identity))
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function continuityVaultId(identity: Pick<EthagentIdentity, 'chainId' | 'identityRegistryAddress' | 'agentId' | 'address'>): string {
|
|
356
|
-
const chain = identity.chainId?.toString() ?? 'unknown-chain'
|
|
357
|
-
const registry = sanitizePathPart(identity.identityRegistryAddress ?? 'unknown-registry')
|
|
358
|
-
const token = sanitizePathPart(identity.agentId ?? identity.address)
|
|
359
|
-
return `${chain}-${registry}-${token}`
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function sanitizePathPart(value: string): string {
|
|
363
|
-
return value.trim().toLowerCase().replace(/^0x/, '').replace(/[^a-z0-9._-]+/g, '-').slice(0, 120) || 'unknown'
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
async function writeMissingPrivateFile(file: string, content: string): Promise<void> {
|
|
367
|
-
if (await exists(file)) return
|
|
368
|
-
await atomicWriteText(file, ensureTrailingNewline(content), { mode: 0o600 })
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
async function resolvePublicSkillsFallback(
|
|
372
|
-
identity: EthagentIdentity,
|
|
373
|
-
fallback: string | (() => Promise<string>) | undefined,
|
|
374
|
-
): Promise<string> {
|
|
375
|
-
if (typeof fallback === 'string') return fallback
|
|
376
|
-
if (fallback) {
|
|
377
|
-
try {
|
|
378
|
-
return await fallback()
|
|
379
|
-
} catch {
|
|
380
|
-
return defaultPublicSkillsJson(identity)
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
return defaultPublicSkillsJson(identity)
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
async function readOrDefault(file: string, fallback: string): Promise<string> {
|
|
387
|
-
try {
|
|
388
|
-
return await fs.readFile(file, 'utf8')
|
|
389
|
-
} catch (err: unknown) {
|
|
390
|
-
if ((err as NodeJS.ErrnoException).code === 'ENOENT') return fallback
|
|
391
|
-
throw err
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
async function exists(file: string): Promise<boolean> {
|
|
396
|
-
try {
|
|
397
|
-
await fs.access(file)
|
|
398
|
-
return true
|
|
399
|
-
} catch {
|
|
400
|
-
return false
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
async function statIfExists(file: string): Promise<import('node:fs').Stats | null> {
|
|
405
|
-
try {
|
|
406
|
-
return await fs.stat(file)
|
|
407
|
-
} catch {
|
|
408
|
-
return null
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function ensureTrailingNewline(value: string): string {
|
|
413
|
-
return value.endsWith('\n') ? value : `${value}\n`
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
type SyncBlock = {
|
|
417
|
-
marker: string
|
|
418
|
-
legacyHeading?: string
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
function renderPrivateIdentityBlock(args: {
|
|
422
|
-
owner: string
|
|
423
|
-
token: string
|
|
424
|
-
chainId: string
|
|
425
|
-
registry: string
|
|
426
|
-
}): string {
|
|
427
|
-
return [
|
|
428
|
-
'<!-- ethagent:identity:start -->',
|
|
429
|
-
'## Agent Identity',
|
|
430
|
-
`- Owner wallet: ${args.owner}`,
|
|
431
|
-
`- ERC-8004 token: ${args.token}`,
|
|
432
|
-
`- Chain ID: ${args.chainId}`,
|
|
433
|
-
`- Registry: ${args.registry}`,
|
|
434
|
-
'- Visibility: private local working file; encrypted before IPFS backup.',
|
|
435
|
-
'<!-- ethagent:identity:end -->',
|
|
436
|
-
].join('\n')
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function syncGeneratedMarkdown(existing: string, fresh: string, blocks: SyncBlock[]): string {
|
|
440
|
-
let next = replaceFirstHeading(existing, firstHeading(fresh))
|
|
441
|
-
for (const block of blocks) {
|
|
442
|
-
next = replaceOrInsertMarkedBlock(next, fresh, block)
|
|
443
|
-
}
|
|
444
|
-
return ensureTrailingNewline(next)
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function firstHeading(markdown: string): string {
|
|
448
|
-
return markdown.split(/\r?\n/).find(line => line.startsWith('# ')) ?? ''
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
function replaceFirstHeading(markdown: string, heading: string): string {
|
|
452
|
-
if (!heading) return markdown
|
|
453
|
-
const lines = markdown.split(/\r?\n/)
|
|
454
|
-
const index = lines.findIndex(line => line.startsWith('# '))
|
|
455
|
-
if (index === -1) return `${heading}\n\n${markdown.trimStart()}`
|
|
456
|
-
lines[index] = heading
|
|
457
|
-
return lines.join('\n')
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function replaceOrInsertMarkedBlock(markdown: string, fresh: string, block: SyncBlock): string {
|
|
461
|
-
const freshBlock = extractMarkedBlock(fresh, block.marker)
|
|
462
|
-
if (!freshBlock) return markdown
|
|
463
|
-
const replaced = replaceMarkedBlock(markdown, block.marker, freshBlock)
|
|
464
|
-
if (replaced) return replaced
|
|
465
|
-
if (block.legacyHeading) {
|
|
466
|
-
const replacedLegacy = replaceMarkdownSection(markdown, block.legacyHeading, freshBlock)
|
|
467
|
-
if (replacedLegacy) return replacedLegacy
|
|
468
|
-
}
|
|
469
|
-
return insertAfterFirstHeading(markdown, freshBlock)
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function extractMarkedBlock(markdown: string, marker: string): string | null {
|
|
473
|
-
const start = `<!-- ethagent:${marker}:start -->`
|
|
474
|
-
const end = `<!-- ethagent:${marker}:end -->`
|
|
475
|
-
const startIndex = markdown.indexOf(start)
|
|
476
|
-
const endIndex = markdown.indexOf(end, startIndex + start.length)
|
|
477
|
-
if (startIndex === -1 || endIndex === -1) return null
|
|
478
|
-
return markdown.slice(startIndex, endIndex + end.length).trim()
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
function replaceMarkedBlock(markdown: string, marker: string, replacement: string): string | null {
|
|
482
|
-
const start = `<!-- ethagent:${marker}:start -->`
|
|
483
|
-
const end = `<!-- ethagent:${marker}:end -->`
|
|
484
|
-
const startIndex = markdown.indexOf(start)
|
|
485
|
-
const endIndex = markdown.indexOf(end, startIndex + start.length)
|
|
486
|
-
if (startIndex === -1 || endIndex === -1) return null
|
|
487
|
-
return `${markdown.slice(0, startIndex)}${replacement}${markdown.slice(endIndex + end.length)}`
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function replaceMarkdownSection(markdown: string, heading: string, replacement: string): string | null {
|
|
491
|
-
const lines = markdown.split(/\r?\n/)
|
|
492
|
-
const start = lines.findIndex(line => line.trim() === `## ${heading}`)
|
|
493
|
-
if (start === -1) return null
|
|
494
|
-
const end = lines.findIndex((line, index) => index > start && /^##\s+/.test(line))
|
|
495
|
-
const before = lines.slice(0, start)
|
|
496
|
-
const after = end === -1 ? [] : lines.slice(end)
|
|
497
|
-
return [...before, replacement, '', ...after].join('\n')
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function insertAfterFirstHeading(markdown: string, block: string): string {
|
|
501
|
-
const lines = markdown.split(/\r?\n/)
|
|
502
|
-
const headingIndex = lines.findIndex(line => line.startsWith('# '))
|
|
503
|
-
if (headingIndex === -1) return `${block}\n\n${markdown.trimStart()}`
|
|
504
|
-
const before = lines.slice(0, headingIndex + 1)
|
|
505
|
-
const after = lines.slice(headingIndex + 1)
|
|
506
|
-
return [...before, '', block, '', ...after].join('\n')
|
|
507
|
-
}
|
|
1
|
+
export type {
|
|
2
|
+
ContinuityPublishState,
|
|
3
|
+
ContinuitySnapshotContentHashes,
|
|
4
|
+
ContinuitySnapshotFile,
|
|
5
|
+
ContinuityVaultRef,
|
|
6
|
+
ContinuityWorkingTreeStatus,
|
|
7
|
+
IdentityMarkdownScaffold,
|
|
8
|
+
PrivateContinuityFile,
|
|
9
|
+
} from './storage/types.js'
|
|
10
|
+
export { continuityVaultRef } from './storage/paths.js'
|
|
11
|
+
export {
|
|
12
|
+
ensureContinuityFiles,
|
|
13
|
+
ensureContinuityVault,
|
|
14
|
+
readContinuityFiles,
|
|
15
|
+
writeContinuityFiles,
|
|
16
|
+
} from './storage/files.js'
|
|
17
|
+
export { continuityAgentSnapshot, defaultContinuityFiles } from './storage/defaults.js'
|
|
18
|
+
export {
|
|
19
|
+
ensureIdentityMarkdownScaffold,
|
|
20
|
+
ensurePublicSkillsFile,
|
|
21
|
+
prepareSyncedIdentityMarkdownScaffold,
|
|
22
|
+
prepareSyncedPublicSkillsJson,
|
|
23
|
+
readPublicSkillsFile,
|
|
24
|
+
syncIdentityMarkdownScaffold,
|
|
25
|
+
writeIdentityMarkdownScaffold,
|
|
26
|
+
writePublicSkillsFile,
|
|
27
|
+
} from './storage/scaffold.js'
|
|
28
|
+
export {
|
|
29
|
+
continuityVaultStatus,
|
|
30
|
+
continuityWorkingTreeStatus,
|
|
31
|
+
localContinuitySnapshotContentHashes,
|
|
32
|
+
} from './storage/status.js'
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export type ZipEntry = {
|
|
2
|
+
name: string
|
|
3
|
+
data: Buffer
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const crcTable = (() => {
|
|
7
|
+
const table = new Uint32Array(256)
|
|
8
|
+
for (let i = 0; i < 256; i++) {
|
|
9
|
+
let c = i
|
|
10
|
+
for (let k = 0; k < 8; k++) c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)
|
|
11
|
+
table[i] = c
|
|
12
|
+
}
|
|
13
|
+
return table
|
|
14
|
+
})()
|
|
15
|
+
|
|
16
|
+
export function crc32(buf: Buffer): number {
|
|
17
|
+
let c = 0xFFFFFFFF
|
|
18
|
+
for (let i = 0; i < buf.length; i++) {
|
|
19
|
+
const byte = buf[i] as number
|
|
20
|
+
c = (crcTable[(c ^ byte) & 0xff] as number) ^ (c >>> 8)
|
|
21
|
+
}
|
|
22
|
+
return (c ^ 0xFFFFFFFF) >>> 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function buildZip(entries: ZipEntry[], modifiedAt: Date = new Date()): Buffer {
|
|
26
|
+
const dosTime = toDosTime(modifiedAt)
|
|
27
|
+
const dosDate = toDosDate(modifiedAt)
|
|
28
|
+
const localParts: Buffer[] = []
|
|
29
|
+
const centralParts: Buffer[] = []
|
|
30
|
+
let offset = 0
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
const nameBuf = Buffer.from(entry.name, 'utf8')
|
|
33
|
+
const crc = crc32(entry.data)
|
|
34
|
+
const size = entry.data.length
|
|
35
|
+
|
|
36
|
+
const localHeader = Buffer.alloc(30)
|
|
37
|
+
localHeader.writeUInt32LE(0x04034b50, 0)
|
|
38
|
+
localHeader.writeUInt16LE(20, 4)
|
|
39
|
+
localHeader.writeUInt16LE(0x0800, 6)
|
|
40
|
+
localHeader.writeUInt16LE(0, 8)
|
|
41
|
+
localHeader.writeUInt16LE(dosTime, 10)
|
|
42
|
+
localHeader.writeUInt16LE(dosDate, 12)
|
|
43
|
+
localHeader.writeUInt32LE(crc, 14)
|
|
44
|
+
localHeader.writeUInt32LE(size, 18)
|
|
45
|
+
localHeader.writeUInt32LE(size, 22)
|
|
46
|
+
localHeader.writeUInt16LE(nameBuf.length, 26)
|
|
47
|
+
localHeader.writeUInt16LE(0, 28)
|
|
48
|
+
localParts.push(localHeader, nameBuf, entry.data)
|
|
49
|
+
|
|
50
|
+
const centralHeader = Buffer.alloc(46)
|
|
51
|
+
centralHeader.writeUInt32LE(0x02014b50, 0)
|
|
52
|
+
centralHeader.writeUInt16LE(20, 4)
|
|
53
|
+
centralHeader.writeUInt16LE(20, 6)
|
|
54
|
+
centralHeader.writeUInt16LE(0x0800, 8)
|
|
55
|
+
centralHeader.writeUInt16LE(0, 10)
|
|
56
|
+
centralHeader.writeUInt16LE(dosTime, 12)
|
|
57
|
+
centralHeader.writeUInt16LE(dosDate, 14)
|
|
58
|
+
centralHeader.writeUInt32LE(crc, 16)
|
|
59
|
+
centralHeader.writeUInt32LE(size, 20)
|
|
60
|
+
centralHeader.writeUInt32LE(size, 24)
|
|
61
|
+
centralHeader.writeUInt16LE(nameBuf.length, 28)
|
|
62
|
+
centralHeader.writeUInt16LE(0, 30)
|
|
63
|
+
centralHeader.writeUInt16LE(0, 32)
|
|
64
|
+
centralHeader.writeUInt16LE(0, 34)
|
|
65
|
+
centralHeader.writeUInt16LE(0, 36)
|
|
66
|
+
centralHeader.writeUInt32LE(0, 38)
|
|
67
|
+
centralHeader.writeUInt32LE(offset, 42)
|
|
68
|
+
centralParts.push(centralHeader, nameBuf)
|
|
69
|
+
|
|
70
|
+
offset += localHeader.length + nameBuf.length + entry.data.length
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const centralBuf = Buffer.concat(centralParts)
|
|
74
|
+
const localBuf = Buffer.concat(localParts)
|
|
75
|
+
const eocd = Buffer.alloc(22)
|
|
76
|
+
eocd.writeUInt32LE(0x06054b50, 0)
|
|
77
|
+
eocd.writeUInt16LE(0, 4)
|
|
78
|
+
eocd.writeUInt16LE(0, 6)
|
|
79
|
+
eocd.writeUInt16LE(entries.length, 8)
|
|
80
|
+
eocd.writeUInt16LE(entries.length, 10)
|
|
81
|
+
eocd.writeUInt32LE(centralBuf.length, 12)
|
|
82
|
+
eocd.writeUInt32LE(localBuf.length, 16)
|
|
83
|
+
eocd.writeUInt16LE(0, 20)
|
|
84
|
+
|
|
85
|
+
return Buffer.concat([localBuf, centralBuf, eocd])
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function toDosTime(date: Date): number {
|
|
89
|
+
return ((date.getHours() & 0x1f) << 11) | ((date.getMinutes() & 0x3f) << 5) | ((date.getSeconds() / 2) & 0x1f)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function toDosDate(date: Date): number {
|
|
93
|
+
const year = Math.max(1980, date.getFullYear()) - 1980
|
|
94
|
+
return ((year & 0x7f) << 9) | (((date.getMonth() + 1) & 0x0f) << 5) | (date.getDate() & 0x1f)
|
|
95
|
+
}
|