@matuto-ai/opencode-psst-connector 0.0.0-stage → 0.6.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/LICENSE +21 -0
  3. package/README.md +157 -2
  4. package/docs/active-conversation.md +68 -0
  5. package/docs/message-lifecycle.md +31 -0
  6. package/docs/migration-0.6.0.md +21 -0
  7. package/docs/native-automation.md +37 -0
  8. package/docs/presence-notices.md +21 -0
  9. package/docs/publishing.md +19 -0
  10. package/docs/reference-snapshot.sha256 +7 -0
  11. package/docs/setup-acceptance.md +96 -0
  12. package/docs/verification.md +39 -0
  13. package/package.json +42 -4
  14. package/plugin/index.mjs +18 -0
  15. package/plugin/tui.mjs +160 -0
  16. package/src/active-presence.mjs +97 -0
  17. package/src/approval-helper.mjs +11 -0
  18. package/src/config.mjs +50 -0
  19. package/src/enrollment.mjs +59 -0
  20. package/src/identity-recovery.mjs +94 -0
  21. package/src/installation.mjs +127 -0
  22. package/src/message-format.mjs +26 -0
  23. package/src/message-preview.mjs +29 -0
  24. package/src/native-delivery.mjs +169 -0
  25. package/src/native-inbox.mjs +37 -0
  26. package/src/native-incarnation.mjs +67 -0
  27. package/src/native-lock.mjs +17 -0
  28. package/src/native-metadata.mjs +15 -0
  29. package/src/native-plugin.mjs +335 -0
  30. package/src/native-setup.mjs +208 -0
  31. package/src/opencode-adapter.mjs +223 -0
  32. package/src/presence-notices.mjs +90 -0
  33. package/src/project-metadata.mjs +21 -0
  34. package/src/reference-pin.mjs +21 -0
  35. package/src/reference.mjs +20 -0
  36. package/src/setup-rpc.mjs +16 -0
  37. package/src/tool-schema.mjs +5 -0
  38. package/src/tool-scope.mjs +26 -0
  39. package/vendor/broker/LICENSE +21 -0
  40. package/vendor/broker/ORIGIN.md +12 -0
  41. package/vendor/broker/reference/client.mjs +439 -0
  42. package/vendor/broker/reference/errors.mjs +43 -0
  43. package/vendor/broker/reference/inbox.mjs +334 -0
  44. package/vendor/broker/reference/mcp-stdio.mjs +160 -0
  45. package/vendor/broker/reference/runtime.mjs +329 -0
  46. package/vendor/broker/reference/tools.mjs +88 -0
  47. package/vendor/broker/src/protocol.mjs +325 -0
  48. package/vendor/enrollment/LICENSE +21 -0
  49. package/vendor/enrollment/ORIGIN.md +20 -0
  50. package/vendor/enrollment/approval.mjs +181 -0
  51. package/vendor/enrollment/enrollment.mjs +137 -0
@@ -0,0 +1,137 @@
1
+ // Single-file helper deliberately vendorable by native connectors. Never emits secrets.
2
+ import { randomBytes, randomUUID, createHash } from 'node:crypto';
3
+ import { openSync, closeSync, fstatSync, readFileSync, writeFileSync, fsyncSync, renameSync, unlinkSync, mkdirSync, lstatSync, realpathSync, readdirSync, constants } from 'node:fs';
4
+ import { resolve, dirname, join, isAbsolute } from 'node:path';
5
+ import https from 'node:https';
6
+ import { DatabaseSync } from 'node:sqlite';
7
+
8
+ const TYPES = ['pi','claude-code','opencode','antigravity-cli'];
9
+ const fail = (code, effect = 'not_applied') => { const e = new Error(code); e.code = code; e.effect = effect; throw e; };
10
+ const hash = value => createHash('sha256').update(value).digest('hex');
11
+ function privateDir(path) {
12
+ if (!isAbsolute(path)) fail('private_directory_required');
13
+ mkdirSync(path, {recursive:true, mode:0o700});
14
+ const s = lstatSync(path);
15
+ if (!s.isDirectory() || s.isSymbolicLink() || realpathSync(path) !== resolve(path) || (s.mode & 0o077) || (process.getuid && s.uid !== process.getuid())) fail('private_directory_required');
16
+ }
17
+ function privateRead(path) {
18
+ if (!isAbsolute(path) || realpathSync(dirname(path)) !== resolve(dirname(path))) fail('private_file_required');
19
+ const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
20
+ try {
21
+ const s = fstatSync(fd);
22
+ if (!s.isFile() || s.size > 65536 || (s.mode & 0o077) || (process.getuid && s.uid !== process.getuid())) fail('private_file_required');
23
+ return readFileSync(fd,'utf8');
24
+ } finally { closeSync(fd); }
25
+ }
26
+ function save(path, text) {
27
+ // Reject unsafe existing destinations rather than silently repairing their mode.
28
+ try { privateRead(path); } catch(e) { if(e.code !== 'ENOENT') throw e; }
29
+ const temp = `${path}.${randomUUID()}.tmp`;
30
+ const fd = openSync(temp, constants.O_WRONLY|constants.O_CREAT|constants.O_EXCL|constants.O_NOFOLLOW,0o600);
31
+ try { writeFileSync(fd,text); fsyncSync(fd); } finally { closeSync(fd); }
32
+ renameSync(temp,path);
33
+ const d = openSync(dirname(path),constants.O_RDONLY);try { fsyncSync(d); } finally { closeSync(d); }
34
+ }
35
+ function origin(value) {
36
+ let u; try { u = new URL(value); } catch { fail('https_origin_required'); }
37
+ if (typeof value !== 'string' || /[\u0000-\u0020\u007f]/u.test(value) || u.protocol !== 'https:' || u.username || u.password || u.pathname !== '/' || u.search || u.hash) fail('https_origin_required');
38
+ return u.origin;
39
+ }
40
+ function post(url, secret, body) {
41
+ return new Promise((resolvePromise,reject) => {
42
+ const request = https.request(new URL('/v1/enrollments',url), {method:'POST',headers:{Authorization:`Pairing ${secret}`,'Content-Type':'application/json','Content-Length':Buffer.byteLength(body)}, timeout:10000}, response => {
43
+ let size=0;const chunks=[];
44
+ response.on('data',chunk=>{size+=chunk.length;if(size>16384)response.destroy(new Error('invalid_response'));else chunks.push(chunk);});
45
+ response.on('error',()=>reject(Object.assign(new Error('enrollment_transport_error'),{code:'enrollment_transport_error',effect:'unknown'})));
46
+ response.on('end',()=>{
47
+ try { resolvePromise({status:response.statusCode,body:JSON.parse(Buffer.concat(chunks).toString('utf8'))}); }
48
+ catch { reject(Object.assign(new Error('invalid_response'),{code:'invalid_response',effect:'unknown'})); }
49
+ });
50
+ });
51
+ const deadline=setTimeout(()=>request.destroy(),10000);
52
+ request.on('close',()=>clearTimeout(deadline));
53
+ request.on('timeout',()=>request.destroy());
54
+ request.on('error',()=>reject(Object.assign(new Error('enrollment_transport_error'),{code:'enrollment_transport_error',effect:'unknown'})));
55
+ request.end(body);
56
+ });
57
+ }
58
+ /** Explicit connect only. The optional transport is a test seam, never a user configuration field. */
59
+ export async function connectPairingFile({pairingFile,directory,clientType,brokerUrl}, {transport = post} = {}) {
60
+ let lock, lockPath;
61
+ try {
62
+ if (!TYPES.includes(clientType)) fail('invalid_client_type');
63
+ const pair = JSON.parse(privateRead(pairingFile));
64
+ if (!pair || pair.format !== 'psst-pairing-v1' || pair.client_type !== clientType || typeof pair.pairing_secret !== 'string'
65
+ || !/^[A-Za-z0-9_-]{43}$/.test(pair.pairing_secret) || !Number.isFinite(Date.parse(pair.expires_at))) fail('invalid_pairing_file');
66
+ const broker = origin(pair.broker_url);
67
+ if (brokerUrl !== undefined && origin(brokerUrl) !== broker) fail('broker_origin_mismatch');
68
+ privateDir(directory);
69
+ lockPath = join(directory,'.enrollment-owner.sqlite');
70
+ // Kernel-held SQLite lock survives no process crash; no stale PID or lock deletion.
71
+ try { privateRead(lockPath); } catch(e) { if(e.code!=='ENOENT')throw e; }
72
+ const ownerFd=openSync(lockPath,constants.O_WRONLY|constants.O_CREAT|constants.O_NOFOLLOW,0o600);closeSync(ownerFd);
73
+ try {
74
+ lock = new DatabaseSync(lockPath);
75
+ lock.exec('PRAGMA journal_mode=DELETE; PRAGMA locking_mode=EXCLUSIVE; BEGIN EXCLUSIVE; CREATE TABLE IF NOT EXISTS owner (id INTEGER);');
76
+ } catch { try{lock?.close();}catch{}lock=undefined;fail('enrollment_busy'); }
77
+ // Per-grant attempts preserve old credentials for uncertain recovery and explicit replacement.
78
+ const attempts = join(directory,'enrollments');privateDir(attempts);
79
+ const key = hash(pair.pairing_secret), attemptDir = join(attempts,key);
80
+ if (!readdirSync(attempts).includes(key) && readdirSync(attempts).length >= 64) fail('enrollment_capacity');
81
+ privateDir(attemptDir);
82
+ const stateFile = join(attemptDir,'attempt.json');
83
+ let state;
84
+ try { state = JSON.parse(privateRead(stateFile)); }
85
+ catch(e) {
86
+ if(e.code !== 'ENOENT')throw e;
87
+ state = {broker_url:broker,client_type:clientType,enrollment_id:randomUUID(),token:randomBytes(32).toString('base64url')};
88
+ save(stateFile,JSON.stringify(state)+'\n'); // fsync before any issuance can happen
89
+ }
90
+ if (state.broker_url !== broker || state.client_type !== clientType || !/^[A-Za-z0-9_-]{43}$/.test(state.token)
91
+ || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(state.enrollment_id)) fail('invalid_enrollment_state');
92
+ const response = await transport(broker,pair.pairing_secret,JSON.stringify({enrollment_id:state.enrollment_id,client_type:clientType,token_sha256:hash(state.token)}));
93
+ if (![200,201].includes(response.status)) {
94
+ const code = response.body?.error?.code;
95
+ fail(['unauthenticated','forbidden','invalid_request','idempotency_conflict','rate_limited','not_ready','storage_unavailable'].includes(code)?code:'invalid_response',response.body?.error?.effect==='not_applied'?'not_applied':'unknown');
96
+ }
97
+ const result = response.body;
98
+ if (!result || result.protocol_version !== '1' || result.client_type !== clientType || !/^enrolled-[0-9a-f-]{36}$/.test(result.credential_id)
99
+ || !/^installation-[0-9a-f-]{36}$/.test(result.adapter_id) || !Array.isArray(result.scopes) || !result.scopes.length || result.scopes.length>32
100
+ || result.scopes.some(s=>typeof s!=='string'||!/^[-a-zA-Z0-9_.]{1,128}$/.test(s)) || !Number.isFinite(Date.parse(result.expires_at))) fail('invalid_response','unknown');
101
+ if(Date.parse(result.expires_at)<=Date.now())fail('credential_expired','unknown');
102
+ save(join(attemptDir,'token'),state.token+'\n');
103
+ const bundle = {format:'broker-client-bundle-v1',protocol_version:'1',broker_url:broker,token_file:'token',enrollment_receipt_file:'enrollment.json',file_paths_relative_to:'connection_file_directory',authorization_header:'Authorization',authorization_scheme:'Bearer',insecure_development:false,allowed_client_types:[clientType],scope:result.scopes[0]};
104
+ const connectionFile = join(attemptDir,'connection.json');
105
+ // Publish receipt before bundle: the returned bundle always has its token-bound receipt.
106
+ save(join(attemptDir,'enrollment.json'),JSON.stringify({format:'psst-enrollment-receipt-v1',broker_url:broker,...result,token_sha256:hash(state.token)})+'\n');
107
+ save(connectionFile,JSON.stringify(bundle,null,2)+'\n');
108
+ return {connectionFile,credentialId:result.credential_id,expiresAt:result.expires_at};
109
+ } catch(e) {
110
+ if (e.effect && /^[a-z_]+$/.test(e.code)) throw e;
111
+ fail('enrollment_local_error','unknown');
112
+ } finally {
113
+ if (lock !== undefined) { try { lock.close(); } catch { /* Never replace a sanitized result with raw filesystem details. */ } }
114
+ }
115
+ }
116
+
117
+ /** Read only for a helper-enrolled bundle. Missing/corrupt receipt is not a legacy fallback. */
118
+ export function readEnrollmentReceipt({connectionFile,clientType}) {
119
+ try {
120
+ if(!TYPES.includes(clientType))fail('invalid_client_type');
121
+ const bundle=JSON.parse(privateRead(connectionFile));
122
+ const receipt=JSON.parse(privateRead(join(dirname(connectionFile),'enrollment.json')));
123
+ const token=privateRead(join(dirname(connectionFile),'token')).trim();
124
+ if(bundle?.format!=='broker-client-bundle-v1'||bundle.protocol_version!=='1'||bundle.token_file!=='token'||bundle.enrollment_receipt_file!=='enrollment.json'
125
+ ||bundle.authorization_scheme!=='Bearer'||bundle.authorization_header!=='Authorization'||bundle.insecure_development!==false
126
+ ||!Array.isArray(bundle.allowed_client_types)||bundle.allowed_client_types.length!==1||bundle.allowed_client_types[0]!==clientType
127
+ ||receipt?.format!=='psst-enrollment-receipt-v1'||receipt.protocol_version!=='1'||receipt.client_type!==clientType
128
+ ||receipt.broker_url!==origin(bundle.broker_url)||!/^[A-Za-z0-9_-]{43}$/.test(token)||receipt.token_sha256!==hash(token)
129
+ ||!/^enrolled-[0-9a-f-]{36}$/.test(receipt.credential_id)||!/^installation-[0-9a-f-]{36}$/.test(receipt.adapter_id)
130
+ ||!Array.isArray(receipt.scopes)||!receipt.scopes.length||receipt.scopes[0]!==bundle.scope||!Number.isFinite(Date.parse(receipt.expires_at)))fail('invalid_enrollment_receipt');
131
+ if(Date.parse(receipt.expires_at)<=Date.now())fail('credential_expired');
132
+ return {credentialId:receipt.credential_id,expiresAt:receipt.expires_at};
133
+ } catch(e) {
134
+ if(e.effect&&/^[a-z_]+$/.test(e.code))throw e;
135
+ fail('invalid_enrollment_receipt');
136
+ }
137
+ }