@@ -8,10 +8,45 @@ const DEFAULT_RELAYS = [
88 'wss://relay.nostr.band'
99]
1010
11+ /**
12+ * Create Bitcoin anchor for commit using blocktrails
13+ * Returns txo URI or null if anchoring fails/skipped
14+ */
15+ async function createAnchor ( commit , repoPath , options = { } ) {
16+ const { network = 'tbtc4' , dryRun = false } = options
17+
18+ try {
19+ const args = [ 'mark' , commit ]
20+ if ( dryRun ) args . push ( '--dry' )
21+
22+ const output = execSync ( `blocktrails ${ args . join ( ' ' ) } ` , {
23+ cwd : repoPath ,
24+ encoding : 'utf-8' ,
25+ stdio : [ 'pipe' , 'pipe' , 'pipe' ]
26+ } )
27+
28+ // Parse TXID from output: "TXID: <txid>"
29+ const txidMatch = output . match ( / T X I D : \s * ( [ a - f 0 - 9 ] { 64 } ) / i)
30+ if ( txidMatch ) {
31+ const txid = txidMatch [ 1 ]
32+ // Output is always vout 0 for mark command
33+ return `txo:${ network } :${ txid } :0`
34+ }
35+
36+ console . log ( '[anchor] Could not parse TXID from blocktrails output' )
37+ return null
38+ } catch ( err ) {
39+ console . log ( `[anchor] Blocktrails not available or failed: ${ err . message } ` )
40+ return null
41+ }
42+ }
43+
1144/**
1245 * Publish a 30618 repo state event
1346 */
14- export async function publishState ( privateKeyHex , relays , repoId , repoPath ) {
47+ export async function publishState ( privateKeyHex , relays , repoId , repoPath , options = { } ) {
48+ const { anchor = false , network = 'tbtc4' , dryRun = false } = options
49+
1550 // Get current branch and commit
1651 const branch = execSync ( 'git rev-parse --abbrev-ref HEAD' , {
1752 cwd : repoPath ,
@@ -23,14 +58,27 @@ export async function publishState(privateKeyHex, relays, repoId, repoPath) {
2358 encoding : 'utf-8'
2459 } ) . trim ( )
2560
61+ // Build tags
62+ const tags = [
63+ [ 'd' , repoId ] ,
64+ [ `refs/heads/${ branch } ` , commit ]
65+ ]
66+
67+ // Create Bitcoin anchor if requested
68+ if ( anchor ) {
69+ console . log ( `[anchor] Creating Bitcoin anchor for ${ commit . slice ( 0 , 7 ) } ...` )
70+ const txoUri = await createAnchor ( commit , repoPath , { network, dryRun } )
71+ if ( txoUri ) {
72+ tags . push ( [ 'c' , txoUri ] )
73+ console . log ( `[anchor] ✓ Anchored: ${ txoUri } ` )
74+ }
75+ }
76+
2677 // Create and sign event
2778 const event = finalizeEvent ( {
2879 kind : 30618 ,
2980 created_at : Math . floor ( Date . now ( ) / 1000 ) ,
30- tags : [
31- [ 'd' , repoId ] ,
32- [ `refs/heads/${ branch } ` , commit ]
33- ] ,
81+ tags,
3482 content : ''
3583 } , hexToBytes ( privateKeyHex ) )
3684
@@ -102,14 +150,43 @@ function hexToBytes(hex) {
102150
103151// CLI usage
104152if ( process . argv [ 1 ] . endsWith ( 'publish.js' ) ) {
153+ const args = process . argv . slice ( 2 )
154+ const flags = args . filter ( a => a . startsWith ( '-' ) )
155+ const positional = args . filter ( a => ! a . startsWith ( '-' ) )
156+
157+ const anchor = flags . includes ( '--anchor' ) || flags . includes ( '-a' )
158+ const help = flags . includes ( '--help' ) || flags . includes ( '-h' )
159+
160+ if ( help ) {
161+ console . log ( `
162+ nostr-git-sync publish - Publish 30618 repo state event
163+
164+ Usage:
165+ node src/publish.js [options] [privkey] [repo-id] [repo-path]
166+
167+ Options:
168+ -a, --anchor Create Bitcoin anchor (requires blocktrails CLI)
169+ -h, --help Show this help
170+
171+ If no arguments provided, reads from git config:
172+ git config nostr.privkey <hex>
173+ git config nostr.repoid <id> (optional, defaults to directory name)
174+
175+ Examples:
176+ node src/publish.js # Use git config
177+ node src/publish.js --anchor # With Bitcoin anchor
178+ node src/publish.js <key> my-repo . # Explicit args
179+ ` )
180+ process . exit ( 0 )
181+ }
182+
105183 let privkey , repoId , repoPath = '.'
106184
107- // Check args
108- if ( process . argv [ 2 ] && ! process . argv [ 2 ] . startsWith ( '-' ) ) {
185+ if ( positional . length > 0 ) {
109186 // Args provided: privkey repoId [repoPath]
110- privkey = process . argv [ 2 ]
111- repoId = process . argv [ 3 ]
112- repoPath = process . argv [ 4 ] || '.'
187+ privkey = positional [ 0 ]
188+ repoId = positional [ 1 ]
189+ repoPath = positional [ 2 ] || '.'
113190 } else {
114191 // Read from git config
115192 try {
@@ -124,12 +201,12 @@ if (process.argv[1].endsWith('publish.js')) {
124201 }
125202
126203 if ( ! privkey ) {
127- console . log ( 'Usage: node src/publish.js <privkey-hex> <repo-id> [repo-path]' )
128- console . log ( ' or: git config nostr.privkey <hex> && node src/publish.js' )
204+ console . log ( 'Usage: node src/publish.js [--anchor] <privkey-hex> <repo-id> [repo-path]' )
205+ console . log ( ' or: git config nostr.privkey <hex> && node src/publish.js [--anchor] ' )
129206 process . exit ( 1 )
130207 }
131208
132- publishState ( privkey , DEFAULT_RELAYS , repoId , repoPath )
209+ publishState ( privkey , DEFAULT_RELAYS , repoId , repoPath , { anchor } )
133210 . then ( ( ) => process . exit ( 0 ) )
134211 . catch ( err => {
135212 console . error ( 'Error:' , err . message )
0 commit comments