@synctek/forgeos 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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +386 -0
  3. package/dist/cli/commands/analyze.d.ts +14 -0
  4. package/dist/cli/commands/analyze.d.ts.map +1 -0
  5. package/dist/cli/commands/analyze.js +94 -0
  6. package/dist/cli/commands/analyze.js.map +1 -0
  7. package/dist/cli/commands/build.d.ts +11 -0
  8. package/dist/cli/commands/build.d.ts.map +1 -0
  9. package/dist/cli/commands/build.js +86 -0
  10. package/dist/cli/commands/build.js.map +1 -0
  11. package/dist/cli/commands/changeset.d.ts +13 -0
  12. package/dist/cli/commands/changeset.d.ts.map +1 -0
  13. package/dist/cli/commands/changeset.js +174 -0
  14. package/dist/cli/commands/changeset.js.map +1 -0
  15. package/dist/cli/commands/evidence.d.ts +12 -0
  16. package/dist/cli/commands/evidence.d.ts.map +1 -0
  17. package/dist/cli/commands/evidence.js +94 -0
  18. package/dist/cli/commands/evidence.js.map +1 -0
  19. package/dist/cli/commands/federation.d.ts +13 -0
  20. package/dist/cli/commands/federation.d.ts.map +1 -0
  21. package/dist/cli/commands/federation.js +127 -0
  22. package/dist/cli/commands/federation.js.map +1 -0
  23. package/dist/cli/commands/gate.d.ts +15 -0
  24. package/dist/cli/commands/gate.d.ts.map +1 -0
  25. package/dist/cli/commands/gate.js +178 -0
  26. package/dist/cli/commands/gate.js.map +1 -0
  27. package/dist/cli/commands/initiative.d.ts +13 -0
  28. package/dist/cli/commands/initiative.d.ts.map +1 -0
  29. package/dist/cli/commands/initiative.js +130 -0
  30. package/dist/cli/commands/initiative.js.map +1 -0
  31. package/dist/cli/commands/mind.d.ts +16 -0
  32. package/dist/cli/commands/mind.d.ts.map +1 -0
  33. package/dist/cli/commands/mind.js +139 -0
  34. package/dist/cli/commands/mind.js.map +1 -0
  35. package/dist/cli/commands/outcome.d.ts +12 -0
  36. package/dist/cli/commands/outcome.d.ts.map +1 -0
  37. package/dist/cli/commands/outcome.js +85 -0
  38. package/dist/cli/commands/outcome.js.map +1 -0
  39. package/dist/cli/commands/project.d.ts +13 -0
  40. package/dist/cli/commands/project.d.ts.map +1 -0
  41. package/dist/cli/commands/project.js +128 -0
  42. package/dist/cli/commands/project.js.map +1 -0
  43. package/dist/cli/commands/review.d.ts +15 -0
  44. package/dist/cli/commands/review.d.ts.map +1 -0
  45. package/dist/cli/commands/review.js +167 -0
  46. package/dist/cli/commands/review.js.map +1 -0
  47. package/dist/cli/commands/score.d.ts +17 -0
  48. package/dist/cli/commands/score.d.ts.map +1 -0
  49. package/dist/cli/commands/score.js +168 -0
  50. package/dist/cli/commands/score.js.map +1 -0
  51. package/dist/cli/commands/session.d.ts +13 -0
  52. package/dist/cli/commands/session.d.ts.map +1 -0
  53. package/dist/cli/commands/session.js +133 -0
  54. package/dist/cli/commands/session.js.map +1 -0
  55. package/dist/cli/commands/trust.d.ts +16 -0
  56. package/dist/cli/commands/trust.d.ts.map +1 -0
  57. package/dist/cli/commands/trust.js +261 -0
  58. package/dist/cli/commands/trust.js.map +1 -0
  59. package/dist/cli/index.d.ts +19 -0
  60. package/dist/cli/index.d.ts.map +1 -0
  61. package/dist/cli/index.js +99 -0
  62. package/dist/cli/index.js.map +1 -0
  63. package/dist/cli/output.d.ts +48 -0
  64. package/dist/cli/output.d.ts.map +1 -0
  65. package/dist/cli/output.js +139 -0
  66. package/dist/cli/output.js.map +1 -0
  67. package/dist/client.d.ts +46 -0
  68. package/dist/client.d.ts.map +1 -0
  69. package/dist/client.js +146 -0
  70. package/dist/client.js.map +1 -0
  71. package/dist/handlers.d.ts +11 -0
  72. package/dist/handlers.d.ts.map +1 -0
  73. package/dist/handlers.js +424 -0
  74. package/dist/handlers.js.map +1 -0
  75. package/dist/http-server.d.ts +25 -0
  76. package/dist/http-server.d.ts.map +1 -0
  77. package/dist/http-server.js +246 -0
  78. package/dist/http-server.js.map +1 -0
  79. package/dist/index.d.ts +3 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +40 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/mcp/handlers.d.ts +11 -0
  84. package/dist/mcp/handlers.d.ts.map +1 -0
  85. package/dist/mcp/handlers.js +553 -0
  86. package/dist/mcp/handlers.js.map +1 -0
  87. package/dist/mcp/http-server.d.ts +25 -0
  88. package/dist/mcp/http-server.d.ts.map +1 -0
  89. package/dist/mcp/http-server.js +246 -0
  90. package/dist/mcp/http-server.js.map +1 -0
  91. package/dist/mcp/index.d.ts +3 -0
  92. package/dist/mcp/index.d.ts.map +1 -0
  93. package/dist/mcp/index.js +40 -0
  94. package/dist/mcp/index.js.map +1 -0
  95. package/dist/mcp/tools.d.ts +944 -0
  96. package/dist/mcp/tools.d.ts.map +1 -0
  97. package/dist/mcp/tools.js +531 -0
  98. package/dist/mcp/tools.js.map +1 -0
  99. package/dist/shared/client.d.ts +59 -0
  100. package/dist/shared/client.d.ts.map +1 -0
  101. package/dist/shared/client.js +171 -0
  102. package/dist/shared/client.js.map +1 -0
  103. package/dist/shared/errors.d.ts +25 -0
  104. package/dist/shared/errors.d.ts.map +1 -0
  105. package/dist/shared/errors.js +44 -0
  106. package/dist/shared/errors.js.map +1 -0
  107. package/dist/shared/types.d.ts +111 -0
  108. package/dist/shared/types.d.ts.map +1 -0
  109. package/dist/shared/types.js +10 -0
  110. package/dist/shared/types.js.map +1 -0
  111. package/dist/tools.d.ts +944 -0
  112. package/dist/tools.d.ts.map +1 -0
  113. package/dist/tools.js +513 -0
  114. package/dist/tools.js.map +1 -0
  115. package/dist/trust/chain.d.ts +86 -0
  116. package/dist/trust/chain.d.ts.map +1 -0
  117. package/dist/trust/chain.js +176 -0
  118. package/dist/trust/chain.js.map +1 -0
  119. package/dist/trust/git-binding.d.ts +61 -0
  120. package/dist/trust/git-binding.d.ts.map +1 -0
  121. package/dist/trust/git-binding.js +133 -0
  122. package/dist/trust/git-binding.js.map +1 -0
  123. package/dist/trust/index.d.ts +20 -0
  124. package/dist/trust/index.d.ts.map +1 -0
  125. package/dist/trust/index.js +17 -0
  126. package/dist/trust/index.js.map +1 -0
  127. package/dist/trust/ledger.d.ts +144 -0
  128. package/dist/trust/ledger.d.ts.map +1 -0
  129. package/dist/trust/ledger.js +351 -0
  130. package/dist/trust/ledger.js.map +1 -0
  131. package/dist/trust/signing.d.ts +134 -0
  132. package/dist/trust/signing.d.ts.map +1 -0
  133. package/dist/trust/signing.js +249 -0
  134. package/dist/trust/signing.js.map +1 -0
  135. package/dist/trust/transmission.d.ts +129 -0
  136. package/dist/trust/transmission.d.ts.map +1 -0
  137. package/dist/trust/transmission.js +390 -0
  138. package/dist/trust/transmission.js.map +1 -0
  139. package/dist/types.d.ts +183 -0
  140. package/dist/types.d.ts.map +1 -0
  141. package/dist/types.js +3 -0
  142. package/dist/types.js.map +1 -0
  143. package/package.json +61 -0
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Ed25519 key management for Trust Ledger entry signing.
3
+ *
4
+ * Binary-compatible with the Python signing.py implementation.
5
+ *
6
+ * Key files:
7
+ * .forgeos/keys/private.pem (mode 0o600 — owner read/write only)
8
+ * .forgeos/keys/public.pem (world-readable)
9
+ *
10
+ * Key format: PKCS8 PEM for private, SPKI PEM for public — matching
11
+ * Python's serialization.PrivateFormat.PKCS8 and
12
+ * serialization.PublicFormat.SubjectPublicKeyInfo.
13
+ *
14
+ * Fingerprint: first 16 hex chars of SHA-256(raw_32_byte_public_key).
15
+ * This matches Python's get_fingerprint() which calls:
16
+ * public_key.public_bytes(Encoding.Raw, PublicFormat.Raw) → 32 bytes
17
+ * SHA-256(raw_bytes).hexdigest()[:16]
18
+ *
19
+ * Signatures: Ed25519 raw signatures, base64-encoded (standard alphabet),
20
+ * produced over the canonical UTF-8 bytes of the entry dict. This matches
21
+ * Python's base64.b64encode(raw_sig).decode("ascii").
22
+ *
23
+ * Security invariants (matching Python):
24
+ * - load_private_key raises if file mode is broader than 0o600
25
+ * - Key rotation archives old_public.pem before overwriting
26
+ */
27
+ import { generateKeyPairSync, sign, verify, createHash, createPrivateKey, createPublicKey, } from 'crypto';
28
+ import { pythonJsonDumps } from './chain.js';
29
+ import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync, statSync, copyFileSync, } from 'fs';
30
+ import { join } from 'path';
31
+ const PRIVATE_KEY_FILENAME = 'private.pem';
32
+ const PUBLIC_KEY_FILENAME = 'public.pem';
33
+ const OLD_PUBLIC_KEY_FILENAME = 'old_public.pem';
34
+ const REQUIRED_PRIVATE_MODE = 0o600;
35
+ const REQUIRED_KEY_DIR_MODE = 0o700;
36
+ export class SigningError extends Error {
37
+ constructor(message) {
38
+ super(message);
39
+ this.name = 'SigningError';
40
+ }
41
+ }
42
+ export class KeyNotFoundError extends SigningError {
43
+ constructor(message) {
44
+ super(message);
45
+ this.name = 'KeyNotFoundError';
46
+ }
47
+ }
48
+ export class KeyPermissionError extends SigningError {
49
+ constructor(message) {
50
+ super(message);
51
+ this.name = 'KeyPermissionError';
52
+ }
53
+ }
54
+ /**
55
+ * Manages Ed25519 keypairs for a ForgeOS project.
56
+ *
57
+ * All key files are stored under {projectPath}/.forgeos/keys/.
58
+ *
59
+ * Example:
60
+ * const km = new KeyManager('/my/project');
61
+ * km.generateKeypair();
62
+ * const sig = km.signEntry({ seq: 0, ... });
63
+ * const ok = km.verifySignature({ seq: 0, ... }, sig);
64
+ */
65
+ export class KeyManager {
66
+ keysDir;
67
+ constructor(projectPath) {
68
+ this.keysDir = join(projectPath, '.forgeos', 'keys');
69
+ }
70
+ get privateKeyPath() {
71
+ return join(this.keysDir, PRIVATE_KEY_FILENAME);
72
+ }
73
+ get publicKeyPath() {
74
+ return join(this.keysDir, PUBLIC_KEY_FILENAME);
75
+ }
76
+ // ------------------------------------------------------------------
77
+ // Key lifecycle
78
+ // ------------------------------------------------------------------
79
+ /**
80
+ * Generate a new Ed25519 keypair and persist it to disk.
81
+ *
82
+ * Creates the key directory (mode 0o700) if it does not exist.
83
+ * Overwrites any existing keypair — rotate deliberately via rotateKey().
84
+ *
85
+ * @returns The key fingerprint (first 16 hex chars of SHA-256 of raw public key bytes).
86
+ */
87
+ generateKeypair() {
88
+ mkdirSync(this.keysDir, { recursive: true });
89
+ chmodSync(this.keysDir, REQUIRED_KEY_DIR_MODE);
90
+ const { privateKey, publicKey } = generateKeyPairSync('ed25519');
91
+ const privatePem = privateKey.export({ type: 'pkcs8', format: 'pem' });
92
+ const publicPem = publicKey.export({ type: 'spki', format: 'pem' });
93
+ writeFileSync(this.privateKeyPath, privatePem, { mode: REQUIRED_PRIVATE_MODE });
94
+ writeFileSync(this.publicKeyPath, publicPem);
95
+ return this.getFingerprint();
96
+ }
97
+ /**
98
+ * Rotate the keypair, returning [oldFingerprint, newFingerprint].
99
+ *
100
+ * The old public key is preserved at old_public.pem during the transition
101
+ * so callers can construct a capability_declared ledger entry referencing
102
+ * both keys. After the transition entry is written, the caller may delete
103
+ * old_public.pem.
104
+ *
105
+ * @returns Tuple of [oldFingerprint, newFingerprint].
106
+ * @throws KeyNotFoundError if no existing keypair is found.
107
+ */
108
+ rotateKey() {
109
+ if (!existsSync(this.publicKeyPath)) {
110
+ throw new KeyNotFoundError(`No existing public key at ${this.publicKeyPath}. Cannot rotate a key that does not exist.`);
111
+ }
112
+ const oldFingerprint = this.getFingerprint();
113
+ // Archive old public key before overwriting
114
+ const oldPublicPath = join(this.keysDir, OLD_PUBLIC_KEY_FILENAME);
115
+ copyFileSync(this.publicKeyPath, oldPublicPath);
116
+ const newFingerprint = this.generateKeypair();
117
+ return [oldFingerprint, newFingerprint];
118
+ }
119
+ // ------------------------------------------------------------------
120
+ // Key loading
121
+ // ------------------------------------------------------------------
122
+ /**
123
+ * Load the private key from disk, enforcing strict file permissions.
124
+ *
125
+ * @throws KeyNotFoundError if the private key file does not exist.
126
+ * @throws KeyPermissionError if the file mode is broader than 0o600.
127
+ */
128
+ loadPrivateKey() {
129
+ if (!existsSync(this.privateKeyPath)) {
130
+ throw new KeyNotFoundError(`Private key not found at ${this.privateKeyPath}. Run 'forgeos analyze --init' to generate keys.`);
131
+ }
132
+ this.assertPrivateKeyPermissions();
133
+ const pem = readFileSync(this.privateKeyPath, 'utf8');
134
+ return createPrivateKey(pem);
135
+ }
136
+ /**
137
+ * Load the public key from disk.
138
+ *
139
+ * @throws KeyNotFoundError if the public key file does not exist.
140
+ */
141
+ loadPublicKey() {
142
+ if (!existsSync(this.publicKeyPath)) {
143
+ throw new KeyNotFoundError(`Public key not found at ${this.publicKeyPath}. Run 'forgeos analyze --init' to generate keys.`);
144
+ }
145
+ const pem = readFileSync(this.publicKeyPath, 'utf8');
146
+ return createPublicKey(pem);
147
+ }
148
+ /**
149
+ * Return the raw 32-byte Ed25519 public key material.
150
+ *
151
+ * This replicates Python's:
152
+ * public_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
153
+ * which returns the 32-byte raw Ed25519 public key (not PEM, not DER SPKI).
154
+ */
155
+ getPublicKeyBytes() {
156
+ const publicKey = this.loadPublicKey();
157
+ // Export as DER (SPKI format) then extract the raw 32-byte key material.
158
+ // DER SPKI for Ed25519 is: 12-byte header + 32-byte raw key = 44 bytes total.
159
+ // The raw key starts at offset 12.
160
+ const derBytes = publicKey.export({ type: 'spki', format: 'der' });
161
+ return derBytes.slice(12);
162
+ }
163
+ /**
164
+ * Return the key fingerprint as 16 hex characters.
165
+ *
166
+ * Fingerprint = first 16 hex chars of SHA-256(raw_public_key_bytes).
167
+ *
168
+ * This matches Python's:
169
+ * raw_bytes = public_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
170
+ * digest = hashlib.sha256(raw_bytes).hexdigest()
171
+ * return digest[:16]
172
+ */
173
+ getFingerprint() {
174
+ const rawBytes = this.getPublicKeyBytes();
175
+ const digest = createHash('sha256').update(rawBytes).digest('hex');
176
+ return digest.substring(0, 16);
177
+ }
178
+ // ------------------------------------------------------------------
179
+ // Sign / verify
180
+ // ------------------------------------------------------------------
181
+ /**
182
+ * Sign an entry dict with the project's private key.
183
+ *
184
+ * The entry is serialized to canonical JSON (sorted keys, UTF-8) before
185
+ * signing, matching Python's:
186
+ * entry_bytes = json.dumps(entry, sort_keys=True, ensure_ascii=False).encode("utf-8")
187
+ * raw_sig = private_key.sign(entry_bytes)
188
+ * return base64.b64encode(raw_sig).decode("ascii")
189
+ *
190
+ * @param entry - The entry dict to sign (before signature field is set).
191
+ * @returns Base64-encoded (standard alphabet) signature string.
192
+ */
193
+ signEntry(entry) {
194
+ const privateKey = this.loadPrivateKey();
195
+ // Use Python-compatible serialization: sort_keys=True, ": " and ", " separators
196
+ const canonical = pythonJsonDumps(entry);
197
+ const entryBytes = Buffer.from(canonical, 'utf8');
198
+ // Ed25519 signing in Node.js uses createSign with 'sha512' alg name
199
+ // but for Ed25519, the digest algorithm is ignored — signing is done
200
+ // over the raw message. Using null/undefined as algorithm.
201
+ // Ed25519 signing: algorithm must be null (no digest; Ed25519 is pre-hash)
202
+ const rawSig = sign(null, entryBytes, privateKey);
203
+ return rawSig.toString('base64');
204
+ }
205
+ /**
206
+ * Verify an Ed25519 signature against an entry dict.
207
+ *
208
+ * Returns false on any verification failure — never throws.
209
+ *
210
+ * Matches Python's verify_signature which catches all exceptions and
211
+ * returns False rather than propagating.
212
+ *
213
+ * @param entry - The entry dict that was signed.
214
+ * @param signature - Base64-encoded signature string.
215
+ * @returns true if the signature is valid, false otherwise.
216
+ */
217
+ verifySignature(entry, signature) {
218
+ try {
219
+ const publicKey = this.loadPublicKey();
220
+ const canonical = pythonJsonDumps(entry);
221
+ const entryBytes = Buffer.from(canonical, 'utf8');
222
+ const sigBuffer = Buffer.from(signature, 'base64');
223
+ // Ed25519 verification: algorithm must be null (no digest)
224
+ return verify(null, entryBytes, publicKey, sigBuffer);
225
+ }
226
+ catch {
227
+ return false;
228
+ }
229
+ }
230
+ // ------------------------------------------------------------------
231
+ // Internal helpers
232
+ // ------------------------------------------------------------------
233
+ assertPrivateKeyPermissions() {
234
+ const st = statSync(this.privateKeyPath);
235
+ // Extract permission bits (last 9 bits of mode)
236
+ const mode = st.mode & 0o777;
237
+ // Check that no bits beyond 0o600 are set
238
+ if (mode & ~REQUIRED_PRIVATE_MODE) {
239
+ throw new KeyPermissionError(`Private key at ${this.privateKeyPath} has permissions ${oct(mode)} — expected ${oct(REQUIRED_PRIVATE_MODE)}. Run: chmod 600 .forgeos/keys/private.pem`);
240
+ }
241
+ }
242
+ }
243
+ // ------------------------------------------------------------------
244
+ // Module-level helpers
245
+ // ------------------------------------------------------------------
246
+ function oct(n) {
247
+ return '0o' + n.toString(8);
248
+ }
249
+ //# sourceMappingURL=signing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.js","sourceRoot":"","sources":["../../src/trust/signing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Shareable metadata transmission for the ForgeOS Trust Ledger.
3
+ *
4
+ * Controls exactly what leaves the developer's machine.
5
+ *
6
+ * Design is fail-closed: any privacy violation raises rather than returning
7
+ * a partial payload. New fields added to the ledger schema are automatically
8
+ * excluded until explicitly added to TRANSMITTABLE_FIELDS.
9
+ *
10
+ * Binary-compatible with the Python transmission.py implementation:
11
+ * - Same TRANSMITTABLE_FIELDS allowlist
12
+ * - Same sensitive pattern regexes (adapted for JavaScript RegExp)
13
+ * - Same transmission payload structure
14
+ * - Same manifest format (both box-drawing and plain-text versions)
15
+ *
16
+ * Privacy model (architecture §7.1):
17
+ * - LOCAL_ONLY_FIELDS (signature, source_tree_hash) are NEVER transmitted.
18
+ * - TRANSMITTABLE_FIELDS is the explicit allowlist. Any field not in this
19
+ * list is stripped. New fields are automatically excluded.
20
+ * - The privacy filter validates before transmission and raises
21
+ * PrivacyViolationError on any sensitive pattern match.
22
+ *
23
+ * Transmission log:
24
+ * Every attempt (approved or rejected) is appended to
25
+ * .forgeos/transmissions.log as a JSON line.
26
+ */
27
+ import type { LedgerEntry } from './ledger.js';
28
+ /**
29
+ * Explicit allowlist: only these top-level fields may appear in a
30
+ * transmitted payload. Matches Python's TRANSMITTABLE_FIELDS exactly.
31
+ */
32
+ export declare const TRANSMITTABLE_FIELDS: Set<string>;
33
+ /**
34
+ * Fields that are local-only and must be stripped before transmission.
35
+ * Matches Python's LOCAL_ONLY_FIELDS.
36
+ */
37
+ export declare const LOCAL_ONLY_FIELDS: Set<string>;
38
+ export declare class TransmissionError extends Error {
39
+ constructor(message: string);
40
+ }
41
+ export declare class PrivacyViolationError extends TransmissionError {
42
+ constructor(message: string);
43
+ }
44
+ export interface TransmissionPayload {
45
+ schema_version: string;
46
+ transmitted_at: string;
47
+ entry_count: number;
48
+ entries: Record<string, unknown>[];
49
+ }
50
+ export interface TransmissionResult {
51
+ status: 'sent' | 'cancelled' | 'error';
52
+ http_status: number | null;
53
+ response_body: Record<string, unknown> | null;
54
+ payload_hash: string;
55
+ }
56
+ /**
57
+ * Build a transmission payload from a list of ledger entries.
58
+ *
59
+ * Strips all LOCAL_ONLY_FIELDS and any fields not in TRANSMITTABLE_FIELDS.
60
+ * Runs a paranoia check for sensitive patterns in the resulting payload.
61
+ *
62
+ * Matches Python's prepare_transmission() exactly.
63
+ *
64
+ * @param entries - List of ledger entry dicts.
65
+ * @param fieldsToShare - Optional additional field names beyond TRANSMITTABLE_FIELDS.
66
+ * @returns Filtered payload dict suitable for transmission.
67
+ * @throws PrivacyViolationError if any value matches a sensitive pattern.
68
+ */
69
+ export declare function prepareTransmission(entries: LedgerEntry[], fieldsToShare?: string[]): TransmissionPayload;
70
+ /**
71
+ * Detect sensitive content in a string. Returns matching pattern sources.
72
+ *
73
+ * Convenience function for callers that want to pre-screen values.
74
+ */
75
+ export declare function detectSensitiveContent(text: string): string[];
76
+ /**
77
+ * Generate a box-drawing manifest of what will be transmitted.
78
+ *
79
+ * Matches Python's generate_manifest() box-drawing format exactly.
80
+ *
81
+ * @param transmissionData - Filtered payload from prepareTransmission().
82
+ * @param destination - Target URL the data will be sent to.
83
+ * @returns Multiline string suitable for printing to a terminal.
84
+ */
85
+ export declare function generateManifest(transmissionData: TransmissionPayload, destination: string): string;
86
+ /**
87
+ * Generate a plain-text (no box-drawing) manifest for non-TTY output.
88
+ *
89
+ * Matches Python's generate_plain_manifest() exactly.
90
+ */
91
+ export declare function generatePlainManifest(transmissionData: TransmissionPayload, destination: string): string;
92
+ export interface TransmissionLogRecord {
93
+ timestamp: string;
94
+ approved: boolean;
95
+ auto_confirmed: boolean;
96
+ payload_hash: string;
97
+ destination: string;
98
+ http_status: number | null;
99
+ error: string | null;
100
+ }
101
+ /**
102
+ * Append a transmission record to .forgeos/transmissions.log.
103
+ *
104
+ * Append-only log — every attempt is recorded whether approved,
105
+ * rejected, or errored. Matches Python's _log_transmission().
106
+ */
107
+ export declare function logTransmission(projectPath: string, approved: boolean, autoConfirmed: boolean, payloadHash: string, destination: string, httpStatus?: number | null, error?: string | null): void;
108
+ /**
109
+ * Load allowed endpoints from .forgeos/config.toml.
110
+ *
111
+ * Falls back to DEFAULT_ALLOWED_ENDPOINTS if the config file is missing
112
+ * or unreadable. Uses simple regex parsing (no TOML library dependency).
113
+ */
114
+ export declare function loadAllowedEndpoints(projectPath: string): string[];
115
+ /**
116
+ * Send a transmission payload to a ForgeOS Trust API endpoint.
117
+ *
118
+ * Checks the URL against the allowed endpoints list.
119
+ * Returns a TransmissionResult; all errors are caught and returned as status='error'.
120
+ *
121
+ * @param url - Target endpoint URL (must be HTTPS).
122
+ * @param data - Filtered payload from prepareTransmission().
123
+ * @param projectPath - Project root for config and logging.
124
+ * @param noConfirm - Skip interactive confirmation.
125
+ * @returns TransmissionResult.
126
+ * @throws TransmissionError if the URL is not in the allowed list.
127
+ */
128
+ export declare function sendTransmission(url: string, data: TransmissionPayload, projectPath: string, noConfirm?: boolean): Promise<TransmissionResult>;
129
+ //# sourceMappingURL=transmission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transmission.d.ts","sourceRoot":"","sources":["../../src/trust/transmission.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C;;;GAGG;AACH,eAAO,MAAM,oBAAoB,aAO/B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,iBAAiB,aAA6C,CAAC;AA6B5E,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,qBAAsB,SAAQ,iBAAiB;gBAC9C,OAAO,EAAE,MAAM;CAI5B;AAMD,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;IACvC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9C,YAAY,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,WAAW,EAAE,EACtB,aAAa,CAAC,EAAE,MAAM,EAAE,GACvB,mBAAmB,CA6BrB;AAkDD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAI7D;AAMD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,gBAAgB,EAAE,mBAAmB,EACrC,WAAW,EAAE,MAAM,GAClB,MAAM,CAuCR;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,gBAAgB,EAAE,mBAAmB,EACrC,WAAW,EAAE,MAAM,GAClB,MAAM,CAeR;AAMD,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,EACjB,aAAa,EAAE,OAAO,EACtB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,GAAE,MAAM,GAAG,IAAW,EAChC,KAAK,GAAE,MAAM,GAAG,IAAW,GAC1B,IAAI,CAmBN;AAMD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAyDlE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,mBAAmB,EACzB,WAAW,EAAE,MAAM,EACnB,SAAS,UAAQ,GAChB,OAAO,CAAC,kBAAkB,CAAC,CAwD7B"}