@originals/sdk 1.8.0 → 1.8.2
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/dist/utils/hash.js +1 -0
- package/package.json +6 -5
- package/src/adapters/FeeOracleMock.ts +9 -0
- package/src/adapters/index.ts +5 -0
- package/src/adapters/providers/OrdHttpProvider.ts +126 -0
- package/src/adapters/providers/OrdMockProvider.ts +101 -0
- package/src/adapters/types.ts +66 -0
- package/src/bitcoin/BitcoinManager.ts +329 -0
- package/src/bitcoin/BroadcastClient.ts +54 -0
- package/src/bitcoin/OrdinalsClient.ts +120 -0
- package/src/bitcoin/PSBTBuilder.ts +106 -0
- package/src/bitcoin/fee-calculation.ts +38 -0
- package/src/bitcoin/providers/OrdNodeProvider.ts +92 -0
- package/src/bitcoin/providers/OrdinalsProvider.ts +56 -0
- package/src/bitcoin/providers/types.ts +59 -0
- package/src/bitcoin/transactions/commit.ts +465 -0
- package/src/bitcoin/transactions/index.ts +13 -0
- package/src/bitcoin/transfer.ts +43 -0
- package/src/bitcoin/utxo-selection.ts +322 -0
- package/src/bitcoin/utxo.ts +113 -0
- package/src/cel/ExternalReferenceManager.ts +87 -0
- package/src/cel/OriginalsCel.ts +460 -0
- package/src/cel/algorithms/createEventLog.ts +68 -0
- package/src/cel/algorithms/deactivateEventLog.ts +109 -0
- package/src/cel/algorithms/index.ts +11 -0
- package/src/cel/algorithms/updateEventLog.ts +99 -0
- package/src/cel/algorithms/verifyEventLog.ts +306 -0
- package/src/cel/algorithms/witnessEvent.ts +87 -0
- package/src/cel/cli/create.ts +330 -0
- package/src/cel/cli/index.ts +383 -0
- package/src/cel/cli/inspect.ts +549 -0
- package/src/cel/cli/migrate.ts +473 -0
- package/src/cel/cli/verify.ts +249 -0
- package/src/cel/hash.ts +71 -0
- package/src/cel/index.ts +16 -0
- package/src/cel/layers/BtcoCelManager.ts +408 -0
- package/src/cel/layers/PeerCelManager.ts +371 -0
- package/src/cel/layers/WebVHCelManager.ts +361 -0
- package/src/cel/layers/index.ts +27 -0
- package/src/cel/serialization/cbor.ts +189 -0
- package/src/cel/serialization/index.ts +10 -0
- package/src/cel/serialization/json.ts +209 -0
- package/src/cel/types.ts +160 -0
- package/src/cel/witnesses/BitcoinWitness.ts +184 -0
- package/src/cel/witnesses/HttpWitness.ts +241 -0
- package/src/cel/witnesses/WitnessService.ts +51 -0
- package/src/cel/witnesses/index.ts +11 -0
- package/src/contexts/credentials-v1.json +237 -0
- package/src/contexts/credentials-v2-examples.json +5 -0
- package/src/contexts/credentials-v2.json +340 -0
- package/src/contexts/credentials.json +237 -0
- package/src/contexts/data-integrity-v2.json +81 -0
- package/src/contexts/dids.json +58 -0
- package/src/contexts/ed255192020.json +93 -0
- package/src/contexts/ordinals-plus.json +23 -0
- package/src/contexts/originals.json +22 -0
- package/src/core/OriginalsSDK.ts +420 -0
- package/src/crypto/Multikey.ts +194 -0
- package/src/crypto/Signer.ts +262 -0
- package/src/crypto/noble-init.ts +138 -0
- package/src/did/BtcoDidResolver.ts +231 -0
- package/src/did/DIDManager.ts +705 -0
- package/src/did/Ed25519Verifier.ts +68 -0
- package/src/did/KeyManager.ts +239 -0
- package/src/did/WebVHManager.ts +499 -0
- package/src/did/createBtcoDidDocument.ts +60 -0
- package/src/did/providers/OrdinalsClientProviderAdapter.ts +68 -0
- package/src/events/EventEmitter.ts +222 -0
- package/src/events/index.ts +19 -0
- package/src/events/types.ts +331 -0
- package/src/examples/basic-usage.ts +78 -0
- package/src/examples/create-module-original.ts +435 -0
- package/src/examples/full-lifecycle-flow.ts +514 -0
- package/src/examples/run.ts +60 -0
- package/src/index.ts +204 -0
- package/src/kinds/KindRegistry.ts +320 -0
- package/src/kinds/index.ts +74 -0
- package/src/kinds/types.ts +470 -0
- package/src/kinds/validators/AgentValidator.ts +257 -0
- package/src/kinds/validators/AppValidator.ts +211 -0
- package/src/kinds/validators/DatasetValidator.ts +242 -0
- package/src/kinds/validators/DocumentValidator.ts +311 -0
- package/src/kinds/validators/MediaValidator.ts +269 -0
- package/src/kinds/validators/ModuleValidator.ts +225 -0
- package/src/kinds/validators/base.ts +276 -0
- package/src/kinds/validators/index.ts +12 -0
- package/src/lifecycle/BatchOperations.ts +381 -0
- package/src/lifecycle/LifecycleManager.ts +2156 -0
- package/src/lifecycle/OriginalsAsset.ts +524 -0
- package/src/lifecycle/ProvenanceQuery.ts +280 -0
- package/src/lifecycle/ResourceVersioning.ts +163 -0
- package/src/migration/MigrationManager.ts +587 -0
- package/src/migration/audit/AuditLogger.ts +176 -0
- package/src/migration/checkpoint/CheckpointManager.ts +112 -0
- package/src/migration/checkpoint/CheckpointStorage.ts +101 -0
- package/src/migration/index.ts +33 -0
- package/src/migration/operations/BaseMigration.ts +126 -0
- package/src/migration/operations/PeerToBtcoMigration.ts +105 -0
- package/src/migration/operations/PeerToWebvhMigration.ts +62 -0
- package/src/migration/operations/WebvhToBtcoMigration.ts +105 -0
- package/src/migration/rollback/RollbackManager.ts +170 -0
- package/src/migration/state/StateMachine.ts +92 -0
- package/src/migration/state/StateTracker.ts +156 -0
- package/src/migration/types.ts +356 -0
- package/src/migration/validation/BitcoinValidator.ts +107 -0
- package/src/migration/validation/CredentialValidator.ts +62 -0
- package/src/migration/validation/DIDCompatibilityValidator.ts +151 -0
- package/src/migration/validation/LifecycleValidator.ts +64 -0
- package/src/migration/validation/StorageValidator.ts +79 -0
- package/src/migration/validation/ValidationPipeline.ts +213 -0
- package/src/resources/ResourceManager.ts +655 -0
- package/src/resources/index.ts +21 -0
- package/src/resources/types.ts +202 -0
- package/src/storage/LocalStorageAdapter.ts +64 -0
- package/src/storage/MemoryStorageAdapter.ts +29 -0
- package/src/storage/StorageAdapter.ts +25 -0
- package/src/storage/index.ts +3 -0
- package/src/types/bitcoin.ts +98 -0
- package/src/types/common.ts +92 -0
- package/src/types/credentials.ts +89 -0
- package/src/types/did.ts +31 -0
- package/src/types/external-shims.d.ts +53 -0
- package/src/types/index.ts +7 -0
- package/src/types/network.ts +178 -0
- package/src/utils/EventLogger.ts +298 -0
- package/src/utils/Logger.ts +324 -0
- package/src/utils/MetricsCollector.ts +358 -0
- package/src/utils/bitcoin-address.ts +132 -0
- package/src/utils/cbor.ts +31 -0
- package/src/utils/encoding.ts +135 -0
- package/src/utils/hash.ts +12 -0
- package/src/utils/retry.ts +46 -0
- package/src/utils/satoshi-validation.ts +196 -0
- package/src/utils/serialization.ts +102 -0
- package/src/utils/telemetry.ts +44 -0
- package/src/utils/validation.ts +123 -0
- package/src/vc/CredentialManager.ts +955 -0
- package/src/vc/Issuer.ts +105 -0
- package/src/vc/Verifier.ts +54 -0
- package/src/vc/cryptosuites/bbs.ts +253 -0
- package/src/vc/cryptosuites/bbsSimple.ts +21 -0
- package/src/vc/cryptosuites/eddsa.ts +99 -0
- package/src/vc/documentLoader.ts +81 -0
- package/src/vc/proofs/data-integrity.ts +33 -0
- package/src/vc/utils/jsonld.ts +18 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI Inspect Command
|
|
4
|
+
*
|
|
5
|
+
* Inspects a CEL event log in human-readable format.
|
|
6
|
+
* Displays event timeline, current state, witness attestations, and layer history.
|
|
7
|
+
*
|
|
8
|
+
* Usage: originals-cel inspect --log <path> [options]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
import type { EventLog, LogEntry, DataIntegrityProof, WitnessProof, AssetState, ExternalReference } from '../types';
|
|
14
|
+
import { parseEventLogJson } from '../serialization/json';
|
|
15
|
+
import { parseEventLogCbor } from '../serialization/cbor';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Flags parsed from command line arguments
|
|
19
|
+
*/
|
|
20
|
+
export interface InspectFlags {
|
|
21
|
+
log?: string;
|
|
22
|
+
help?: boolean;
|
|
23
|
+
h?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Result of the inspect command
|
|
28
|
+
*/
|
|
29
|
+
export interface InspectResult {
|
|
30
|
+
success: boolean;
|
|
31
|
+
message: string;
|
|
32
|
+
state?: AssetState;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Migration event data structure
|
|
37
|
+
*/
|
|
38
|
+
interface MigrationData {
|
|
39
|
+
sourceDid?: string;
|
|
40
|
+
targetDid?: string;
|
|
41
|
+
layer?: string;
|
|
42
|
+
domain?: string;
|
|
43
|
+
migratedAt?: string;
|
|
44
|
+
txid?: string;
|
|
45
|
+
inscriptionId?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if a proof is a WitnessProof (has witnessedAt field)
|
|
50
|
+
*/
|
|
51
|
+
function isWitnessProof(proof: DataIntegrityProof | WitnessProof): proof is WitnessProof {
|
|
52
|
+
return 'witnessedAt' in proof && typeof (proof as WitnessProof).witnessedAt === 'string';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if event data contains migration information
|
|
57
|
+
*/
|
|
58
|
+
function isMigrationEvent(data: unknown): data is MigrationData {
|
|
59
|
+
if (typeof data !== 'object' || data === null) return false;
|
|
60
|
+
const d = data as Record<string, unknown>;
|
|
61
|
+
return (
|
|
62
|
+
('sourceDid' in d && 'targetDid' in d) ||
|
|
63
|
+
('layer' in d && typeof d.layer === 'string' && ['peer', 'webvh', 'btco'].includes(d.layer))
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Loads and parses an event log from a file
|
|
69
|
+
* Supports both JSON (.json) and CBOR (.cbor) formats
|
|
70
|
+
*/
|
|
71
|
+
function loadEventLog(filePath: string): EventLog {
|
|
72
|
+
if (!fs.existsSync(filePath)) {
|
|
73
|
+
throw new Error(`File not found: ${filePath}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
77
|
+
const content = fs.readFileSync(filePath);
|
|
78
|
+
|
|
79
|
+
if (ext === '.cbor') {
|
|
80
|
+
// Parse as CBOR binary
|
|
81
|
+
return parseEventLogCbor(new Uint8Array(content));
|
|
82
|
+
} else {
|
|
83
|
+
// Default to JSON parsing
|
|
84
|
+
return parseEventLogJson(content.toString('utf-8'));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Format a timestamp for display
|
|
90
|
+
*/
|
|
91
|
+
function formatTimestamp(timestamp: string): string {
|
|
92
|
+
try {
|
|
93
|
+
const date = new Date(timestamp);
|
|
94
|
+
return date.toLocaleString('en-US', {
|
|
95
|
+
year: 'numeric',
|
|
96
|
+
month: 'short',
|
|
97
|
+
day: '2-digit',
|
|
98
|
+
hour: '2-digit',
|
|
99
|
+
minute: '2-digit',
|
|
100
|
+
second: '2-digit',
|
|
101
|
+
timeZoneName: 'short',
|
|
102
|
+
});
|
|
103
|
+
} catch {
|
|
104
|
+
return timestamp;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Format ISO timestamp to relative time
|
|
110
|
+
*/
|
|
111
|
+
function formatRelativeTime(timestamp: string): string {
|
|
112
|
+
try {
|
|
113
|
+
const date = new Date(timestamp);
|
|
114
|
+
const now = new Date();
|
|
115
|
+
const diffMs = now.getTime() - date.getTime();
|
|
116
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
117
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
118
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
119
|
+
|
|
120
|
+
if (diffMins < 1) return 'just now';
|
|
121
|
+
if (diffMins < 60) return `${diffMins} minute${diffMins > 1 ? 's' : ''} ago`;
|
|
122
|
+
if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`;
|
|
123
|
+
if (diffDays < 30) return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`;
|
|
124
|
+
return formatTimestamp(timestamp);
|
|
125
|
+
} catch {
|
|
126
|
+
return timestamp;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Truncate long strings for display
|
|
132
|
+
*/
|
|
133
|
+
function truncate(str: string, maxLen: number): string {
|
|
134
|
+
if (str.length <= maxLen) return str;
|
|
135
|
+
return str.substring(0, maxLen - 3) + '...';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Format DID for display (truncate middle)
|
|
140
|
+
*/
|
|
141
|
+
function formatDid(did: string): string {
|
|
142
|
+
if (did.length <= 50) return did;
|
|
143
|
+
return did.substring(0, 25) + '...' + did.substring(did.length - 15);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get event type badge
|
|
148
|
+
*/
|
|
149
|
+
function getEventBadge(type: string): string {
|
|
150
|
+
switch (type) {
|
|
151
|
+
case 'create': return '🆕 CREATE';
|
|
152
|
+
case 'update': return '✏️ UPDATE';
|
|
153
|
+
case 'deactivate': return '🔒 DEACTIVATE';
|
|
154
|
+
default: return `📋 ${type.toUpperCase()}`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get layer badge
|
|
160
|
+
*/
|
|
161
|
+
function getLayerBadge(layer: string): string {
|
|
162
|
+
switch (layer) {
|
|
163
|
+
case 'peer': return '🏠 Peer (Layer 0)';
|
|
164
|
+
case 'webvh': return '🌐 WebVH (Layer 1)';
|
|
165
|
+
case 'btco': return '₿ Bitcoin (Layer 2)';
|
|
166
|
+
default: return layer;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Derive current state from event log (simplified version)
|
|
172
|
+
*/
|
|
173
|
+
function deriveCurrentState(log: EventLog): AssetState {
|
|
174
|
+
if (!log.events || log.events.length === 0) {
|
|
175
|
+
throw new Error('Cannot derive state from empty event log');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const createEvent = log.events[0];
|
|
179
|
+
if (createEvent.type !== 'create') {
|
|
180
|
+
throw new Error('First event must be a create event');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const createData = createEvent.data as Record<string, unknown>;
|
|
184
|
+
|
|
185
|
+
// Initialize state from create event
|
|
186
|
+
const state: AssetState = {
|
|
187
|
+
did: (createData.did as string) || 'unknown',
|
|
188
|
+
name: createData.name as string,
|
|
189
|
+
layer: (createData.layer as 'peer' | 'webvh' | 'btco') || 'peer',
|
|
190
|
+
resources: (createData.resources as ExternalReference[]) || [],
|
|
191
|
+
creator: createData.creator as string,
|
|
192
|
+
createdAt: createData.createdAt as string,
|
|
193
|
+
updatedAt: undefined,
|
|
194
|
+
deactivated: false,
|
|
195
|
+
metadata: {},
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Apply subsequent events
|
|
199
|
+
for (let i = 1; i < log.events.length; i++) {
|
|
200
|
+
const event = log.events[i];
|
|
201
|
+
|
|
202
|
+
if (event.type === 'update') {
|
|
203
|
+
const updateData = event.data as Record<string, unknown>;
|
|
204
|
+
|
|
205
|
+
// Update known fields
|
|
206
|
+
if (updateData.name !== undefined) state.name = updateData.name as string;
|
|
207
|
+
if (updateData.resources !== undefined) state.resources = updateData.resources as ExternalReference[];
|
|
208
|
+
if (updateData.updatedAt !== undefined) state.updatedAt = updateData.updatedAt as string;
|
|
209
|
+
if (updateData.did !== undefined) state.did = updateData.did as string;
|
|
210
|
+
if (updateData.targetDid !== undefined) state.did = updateData.targetDid as string;
|
|
211
|
+
if (updateData.layer !== undefined) state.layer = updateData.layer as 'peer' | 'webvh' | 'btco';
|
|
212
|
+
|
|
213
|
+
// Store migration data in metadata
|
|
214
|
+
if (isMigrationEvent(updateData)) {
|
|
215
|
+
state.metadata = state.metadata || {};
|
|
216
|
+
if (updateData.sourceDid) state.metadata.sourceDid = updateData.sourceDid;
|
|
217
|
+
if (updateData.domain) state.metadata.domain = updateData.domain;
|
|
218
|
+
if (updateData.txid) state.metadata.txid = updateData.txid;
|
|
219
|
+
if (updateData.inscriptionId) state.metadata.inscriptionId = updateData.inscriptionId;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Store other fields in metadata
|
|
223
|
+
for (const [key, value] of Object.entries(updateData)) {
|
|
224
|
+
if (!['name', 'resources', 'updatedAt', 'did', 'targetDid', 'layer', 'creator', 'createdAt', 'sourceDid', 'domain', 'txid', 'inscriptionId'].includes(key)) {
|
|
225
|
+
state.metadata = state.metadata || {};
|
|
226
|
+
state.metadata[key] = value;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} else if (event.type === 'deactivate') {
|
|
230
|
+
state.deactivated = true;
|
|
231
|
+
const deactivateData = event.data as Record<string, unknown>;
|
|
232
|
+
if (deactivateData.deactivatedAt !== undefined) {
|
|
233
|
+
state.updatedAt = deactivateData.deactivatedAt as string;
|
|
234
|
+
}
|
|
235
|
+
if (deactivateData.reason !== undefined) {
|
|
236
|
+
state.metadata = state.metadata || {};
|
|
237
|
+
state.metadata.deactivationReason = deactivateData.reason;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return state;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Extract layer history from events
|
|
247
|
+
*/
|
|
248
|
+
function extractLayerHistory(log: EventLog): Array<{ layer: string; timestamp: string; did?: string }> {
|
|
249
|
+
const history: Array<{ layer: string; timestamp: string; did?: string }> = [];
|
|
250
|
+
|
|
251
|
+
for (const event of log.events) {
|
|
252
|
+
const data = event.data as Record<string, unknown>;
|
|
253
|
+
|
|
254
|
+
if (event.type === 'create' && data.layer) {
|
|
255
|
+
history.push({
|
|
256
|
+
layer: data.layer as string,
|
|
257
|
+
timestamp: (data.createdAt as string) || event.proof[0]?.created || 'unknown',
|
|
258
|
+
did: data.did as string,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (event.type === 'update' && isMigrationEvent(event.data)) {
|
|
263
|
+
const migrationData = event.data as MigrationData;
|
|
264
|
+
history.push({
|
|
265
|
+
layer: migrationData.layer ?? 'unknown',
|
|
266
|
+
timestamp: migrationData.migratedAt ?? event.proof[0]?.created ?? 'unknown',
|
|
267
|
+
did: migrationData.targetDid ?? 'unknown',
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return history;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Extract all witness attestations from the log
|
|
277
|
+
*/
|
|
278
|
+
function extractWitnesses(log: EventLog): Array<{
|
|
279
|
+
eventIndex: number;
|
|
280
|
+
eventType: string;
|
|
281
|
+
witnessedAt: string;
|
|
282
|
+
cryptosuite: string;
|
|
283
|
+
verificationMethod: string;
|
|
284
|
+
}> {
|
|
285
|
+
const witnesses: Array<{
|
|
286
|
+
eventIndex: number;
|
|
287
|
+
eventType: string;
|
|
288
|
+
witnessedAt: string;
|
|
289
|
+
cryptosuite: string;
|
|
290
|
+
verificationMethod: string;
|
|
291
|
+
}> = [];
|
|
292
|
+
|
|
293
|
+
for (let i = 0; i < log.events.length; i++) {
|
|
294
|
+
const event = log.events[i];
|
|
295
|
+
for (const proof of event.proof) {
|
|
296
|
+
if (isWitnessProof(proof)) {
|
|
297
|
+
witnesses.push({
|
|
298
|
+
eventIndex: i,
|
|
299
|
+
eventType: event.type,
|
|
300
|
+
witnessedAt: proof.witnessedAt,
|
|
301
|
+
cryptosuite: proof.cryptosuite,
|
|
302
|
+
verificationMethod: proof.verificationMethod,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return witnesses;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Output the inspection result to stdout
|
|
313
|
+
*/
|
|
314
|
+
function outputInspection(log: EventLog, state: AssetState): void {
|
|
315
|
+
console.log('\n═══════════════════════════════════════════════════════');
|
|
316
|
+
console.log(' CEL Event Log Inspector');
|
|
317
|
+
console.log('═══════════════════════════════════════════════════════\n');
|
|
318
|
+
|
|
319
|
+
// ─────────────────────────────────────────────────────────
|
|
320
|
+
// Current State Section
|
|
321
|
+
// ─────────────────────────────────────────────────────────
|
|
322
|
+
console.log('📦 CURRENT STATE');
|
|
323
|
+
console.log('───────────────────────────────────────────────────────');
|
|
324
|
+
|
|
325
|
+
// Status badge
|
|
326
|
+
if (state.deactivated) {
|
|
327
|
+
console.log(` Status: 🔒 DEACTIVATED`);
|
|
328
|
+
if (state.metadata?.deactivationReason) {
|
|
329
|
+
console.log(` Reason: ${state.metadata.deactivationReason}`);
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
console.log(` Status: ✅ ACTIVE`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
console.log(` Name: ${state.name || '(unnamed)'}`);
|
|
336
|
+
console.log(` Layer: ${getLayerBadge(state.layer)}`);
|
|
337
|
+
console.log(` DID: ${formatDid(state.did)}`);
|
|
338
|
+
|
|
339
|
+
if (state.creator) {
|
|
340
|
+
console.log(` Creator: ${formatDid(state.creator)}`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (state.createdAt) {
|
|
344
|
+
console.log(` Created: ${formatTimestamp(state.createdAt)} (${formatRelativeTime(state.createdAt)})`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (state.updatedAt) {
|
|
348
|
+
console.log(` Updated: ${formatTimestamp(state.updatedAt)} (${formatRelativeTime(state.updatedAt)})`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Resources
|
|
352
|
+
if (state.resources && state.resources.length > 0) {
|
|
353
|
+
console.log(`\n 📎 Resources (${state.resources.length}):`);
|
|
354
|
+
for (let i = 0; i < state.resources.length; i++) {
|
|
355
|
+
const res = state.resources[i];
|
|
356
|
+
console.log(` [${i + 1}] ${res.mediaType || 'unknown'}`);
|
|
357
|
+
console.log(` Hash: ${truncate(res.digestMultibase, 50)}`);
|
|
358
|
+
if (res.url && res.url.length > 0) {
|
|
359
|
+
console.log(` URL: ${res.url[0]}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Additional metadata
|
|
365
|
+
if (state.metadata && Object.keys(state.metadata).length > 0) {
|
|
366
|
+
const displayKeys = Object.keys(state.metadata).filter(
|
|
367
|
+
k => !['deactivationReason'].includes(k)
|
|
368
|
+
);
|
|
369
|
+
if (displayKeys.length > 0) {
|
|
370
|
+
console.log(`\n 📋 Metadata:`);
|
|
371
|
+
for (const key of displayKeys) {
|
|
372
|
+
const value = state.metadata[key];
|
|
373
|
+
const displayValue = typeof value === 'string'
|
|
374
|
+
? truncate(value, 50)
|
|
375
|
+
: JSON.stringify(value);
|
|
376
|
+
console.log(` ${key}: ${displayValue}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ─────────────────────────────────────────────────────────
|
|
382
|
+
// Layer History Section
|
|
383
|
+
// ─────────────────────────────────────────────────────────
|
|
384
|
+
const layerHistory = extractLayerHistory(log);
|
|
385
|
+
if (layerHistory.length > 1) {
|
|
386
|
+
console.log('\n\n🗺️ LAYER HISTORY');
|
|
387
|
+
console.log('───────────────────────────────────────────────────────');
|
|
388
|
+
|
|
389
|
+
for (let i = 0; i < layerHistory.length; i++) {
|
|
390
|
+
const entry = layerHistory[i];
|
|
391
|
+
const isLast = i === layerHistory.length - 1;
|
|
392
|
+
const prefix = isLast ? ' └─' : ' ├─';
|
|
393
|
+
const arrow = i > 0 ? ' ← ' : '';
|
|
394
|
+
|
|
395
|
+
console.log(`${prefix} ${getLayerBadge(entry.layer)}`);
|
|
396
|
+
if (entry.timestamp) {
|
|
397
|
+
console.log(` ${isLast ? ' ' : '│'} ${formatTimestamp(entry.timestamp)}`);
|
|
398
|
+
}
|
|
399
|
+
if (entry.did) {
|
|
400
|
+
console.log(` ${isLast ? ' ' : '│'} ${formatDid(entry.did)}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ─────────────────────────────────────────────────────────
|
|
406
|
+
// Witness Attestations Section
|
|
407
|
+
// ─────────────────────────────────────────────────────────
|
|
408
|
+
const witnesses = extractWitnesses(log);
|
|
409
|
+
if (witnesses.length > 0) {
|
|
410
|
+
console.log('\n\n🔏 WITNESS ATTESTATIONS');
|
|
411
|
+
console.log('───────────────────────────────────────────────────────');
|
|
412
|
+
|
|
413
|
+
for (let i = 0; i < witnesses.length; i++) {
|
|
414
|
+
const witness = witnesses[i];
|
|
415
|
+
console.log(`\n [${i + 1}] Event #${witness.eventIndex + 1} (${witness.eventType})`);
|
|
416
|
+
console.log(` Witnessed: ${formatTimestamp(witness.witnessedAt)}`);
|
|
417
|
+
console.log(` Cryptosuite: ${witness.cryptosuite}`);
|
|
418
|
+
console.log(` Witness: ${truncate(witness.verificationMethod, 50)}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ─────────────────────────────────────────────────────────
|
|
423
|
+
// Event Timeline Section
|
|
424
|
+
// ─────────────────────────────────────────────────────────
|
|
425
|
+
console.log('\n\n📜 EVENT TIMELINE');
|
|
426
|
+
console.log('───────────────────────────────────────────────────────');
|
|
427
|
+
|
|
428
|
+
for (let i = 0; i < log.events.length; i++) {
|
|
429
|
+
const event = log.events[i];
|
|
430
|
+
const data = event.data as Record<string, unknown>;
|
|
431
|
+
const isLast = i === log.events.length - 1;
|
|
432
|
+
const prefix = isLast ? '└─' : '├─';
|
|
433
|
+
const connector = isLast ? ' ' : '│ ';
|
|
434
|
+
|
|
435
|
+
// Event header with type badge
|
|
436
|
+
console.log(`\n ${prefix} ${getEventBadge(event.type)}`);
|
|
437
|
+
|
|
438
|
+
// Timestamp from proof
|
|
439
|
+
const timestamp = event.proof[0]?.created || data.createdAt || data.updatedAt || data.migratedAt || data.deactivatedAt;
|
|
440
|
+
if (timestamp) {
|
|
441
|
+
console.log(` ${connector} 📅 ${formatTimestamp(timestamp as string)}`);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Event-specific details
|
|
445
|
+
if (event.type === 'create') {
|
|
446
|
+
if (data.name) console.log(` ${connector} Name: ${data.name}`);
|
|
447
|
+
if (data.did) console.log(` ${connector} DID: ${formatDid(data.did as string)}`);
|
|
448
|
+
if (data.layer) console.log(` ${connector} Layer: ${data.layer}`);
|
|
449
|
+
if (data.resources && Array.isArray(data.resources)) {
|
|
450
|
+
console.log(` ${connector} Resources: ${data.resources.length} file(s)`);
|
|
451
|
+
}
|
|
452
|
+
} else if (event.type === 'update') {
|
|
453
|
+
// Show what changed
|
|
454
|
+
const changes: string[] = [];
|
|
455
|
+
if (data.name) changes.push(`name → "${data.name}"`);
|
|
456
|
+
if (data.resources) changes.push(`resources updated`);
|
|
457
|
+
if (isMigrationEvent(event.data)) {
|
|
458
|
+
const migrationData = event.data as MigrationData;
|
|
459
|
+
changes.push(`migrated to ${migrationData.layer ?? 'unknown'}`);
|
|
460
|
+
if (migrationData.domain) changes.push(`domain: ${migrationData.domain}`);
|
|
461
|
+
if (migrationData.txid) changes.push(`txid: ${truncate(migrationData.txid, 20)}`);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (changes.length > 0) {
|
|
465
|
+
console.log(` ${connector} Changes: ${changes.join(', ')}`);
|
|
466
|
+
} else {
|
|
467
|
+
// Show raw data keys
|
|
468
|
+
const keys = Object.keys(data).filter(k => k !== 'updatedAt');
|
|
469
|
+
if (keys.length > 0) {
|
|
470
|
+
console.log(` ${connector} Fields: ${keys.join(', ')}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
} else if (event.type === 'deactivate') {
|
|
474
|
+
if (data.reason) console.log(` ${connector} Reason: ${data.reason}`);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Proof summary
|
|
478
|
+
const controllerProofs = event.proof.filter(p => !isWitnessProof(p));
|
|
479
|
+
const witnessProofs = event.proof.filter(p => isWitnessProof(p));
|
|
480
|
+
|
|
481
|
+
const proofParts: string[] = [];
|
|
482
|
+
if (controllerProofs.length > 0) {
|
|
483
|
+
proofParts.push(`${controllerProofs.length} controller`);
|
|
484
|
+
}
|
|
485
|
+
if (witnessProofs.length > 0) {
|
|
486
|
+
proofParts.push(`${witnessProofs.length} witness`);
|
|
487
|
+
}
|
|
488
|
+
console.log(` ${connector} 🔐 Proofs: ${proofParts.join(', ')}`);
|
|
489
|
+
|
|
490
|
+
// Hash chain link
|
|
491
|
+
if (event.previousEvent) {
|
|
492
|
+
console.log(` ${connector} 🔗 Chain: ${truncate(event.previousEvent, 40)}`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
console.log('\n═══════════════════════════════════════════════════════\n');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Execute the inspect command
|
|
501
|
+
*/
|
|
502
|
+
export async function inspectCommand(flags: InspectFlags): Promise<InspectResult> {
|
|
503
|
+
// Check for help flag
|
|
504
|
+
if (flags.help || flags.h) {
|
|
505
|
+
return {
|
|
506
|
+
success: true,
|
|
507
|
+
message: 'Use --help with the main CLI for full help text',
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Validate required arguments
|
|
512
|
+
if (!flags.log) {
|
|
513
|
+
return {
|
|
514
|
+
success: false,
|
|
515
|
+
message: 'Error: --log is required. Usage: originals-cel inspect --log <path>',
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Load the event log
|
|
520
|
+
let eventLog: EventLog;
|
|
521
|
+
try {
|
|
522
|
+
eventLog = loadEventLog(flags.log);
|
|
523
|
+
} catch (e) {
|
|
524
|
+
return {
|
|
525
|
+
success: false,
|
|
526
|
+
message: `Error: Failed to load event log: ${(e as Error).message}`,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Derive current state
|
|
531
|
+
let state: AssetState;
|
|
532
|
+
try {
|
|
533
|
+
state = deriveCurrentState(eventLog);
|
|
534
|
+
} catch (e) {
|
|
535
|
+
return {
|
|
536
|
+
success: false,
|
|
537
|
+
message: `Error: Failed to derive state: ${(e as Error).message}`,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Output the inspection
|
|
542
|
+
outputInspection(eventLog, state);
|
|
543
|
+
|
|
544
|
+
return {
|
|
545
|
+
success: true,
|
|
546
|
+
message: 'Inspection complete',
|
|
547
|
+
state,
|
|
548
|
+
};
|
|
549
|
+
}
|