@toon-protocol/rig 4.1.0 → 4.3.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 (35) hide show
  1. package/README.md +194 -4
  2. package/dist/chunk-3RWM7PBM.js +3705 -0
  3. package/dist/chunk-3RWM7PBM.js.map +1 -0
  4. package/dist/chunk-5HFOPKAB.js +7668 -0
  5. package/dist/chunk-5HFOPKAB.js.map +1 -0
  6. package/dist/{chunk-NCEMQUPA.js → chunk-GWA3CSUR.js} +198 -6
  7. package/dist/chunk-GWA3CSUR.js.map +1 -0
  8. package/dist/chunk-U5A7MHBP.js +198 -0
  9. package/dist/chunk-U5A7MHBP.js.map +1 -0
  10. package/dist/{chunk-CDSFVZDV.js → chunk-UE5TAFEU.js} +3 -14
  11. package/dist/chunk-UE5TAFEU.js.map +1 -0
  12. package/dist/{chunk-YZRR2UO6.js → chunk-XCLUB2CY.js} +1 -183
  13. package/dist/chunk-XCLUB2CY.js.map +1 -0
  14. package/dist/ci-serve-ZVJBZ3W3.js +2303 -0
  15. package/dist/ci-serve-ZVJBZ3W3.js.map +1 -0
  16. package/dist/cli/rig.js +10 -7815
  17. package/dist/cli/rig.js.map +1 -1
  18. package/dist/index.d.ts +754 -6
  19. package/dist/index.js +170 -10
  20. package/dist/index.js.map +1 -1
  21. package/dist/{publisher-DMJ06cuL.d.ts → publisher-Cf6tKnZ3.d.ts} +4 -3
  22. package/dist/standalone/index.d.ts +1 -1
  23. package/dist/standalone/index.js +11 -10
  24. package/dist/{standalone-mode-KTYRTUS6.js → standalone-mode-JIEECDTI.js} +10 -11
  25. package/dist/{standalone-mode-KTYRTUS6.js.map → standalone-mode-JIEECDTI.js.map} +1 -1
  26. package/package.json +3 -2
  27. package/dist/chunk-7VCVR4GK.js +0 -1250
  28. package/dist/chunk-7VCVR4GK.js.map +0 -1
  29. package/dist/chunk-CDSFVZDV.js.map +0 -1
  30. package/dist/chunk-GVXBUL3C.js +0 -71
  31. package/dist/chunk-GVXBUL3C.js.map +0 -1
  32. package/dist/chunk-LTFMPOHU.js +0 -194
  33. package/dist/chunk-LTFMPOHU.js.map +0 -1
  34. package/dist/chunk-NCEMQUPA.js.map +0 -1
  35. package/dist/chunk-YZRR2UO6.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,33 +1,98 @@
1
1
  import {
2
+ CI_ADVERTISEMENT_KIND,
3
+ CI_JOB_RESULT_KIND,
4
+ CI_MANUAL_TRIGGER_KIND,
5
+ CI_MAX_ADVERTISEMENT_TTL,
6
+ CI_MAX_PROGRESS_TTL,
7
+ CI_RUNNER_FAMILY,
8
+ CI_SECRET_UPDATE_KIND,
9
+ CI_SERVICE_REQUEST_KIND,
10
+ CI_SERVICE_STOP_KIND,
11
+ CI_SOFTWARE,
12
+ CI_TRUST_ORDER,
13
+ CI_WORKFLOW_PROGRESS_KIND,
14
+ CI_WORKFLOW_RESULT_KIND,
2
15
  DEFAULT_CONCURRENCY,
3
16
  GitError,
4
17
  GitRepoReader,
18
+ MAX_OBJECT_SIZE,
19
+ MAX_SECRET_CIPHERTEXT_BYTES,
20
+ MAX_SECRET_NAMES,
21
+ MAX_SECRET_PLAINTEXT_BYTES,
22
+ MAX_SECRET_VALUE_BYTES,
5
23
  NonFastForwardError,
6
24
  ObjectIntegrityError,
7
25
  ObjectWriteMismatchError,
8
26
  OversizeObjectsError,
27
+ RESERVED_SECRET_NAMES,
28
+ SECRET_NAME_RE,
29
+ WORKFLOW_DIRS,
30
+ applySecretUpdate,
31
+ assembleCiStatus,
32
+ assertSecretUpdateShape,
33
+ buildCiAdvertisement,
34
+ buildCiJobResult,
35
+ buildCiManualTrigger,
36
+ buildCiSecretUpdate,
37
+ buildCiServiceRequest,
38
+ buildCiServiceStop,
39
+ buildCiWorkflowProgress,
40
+ buildCiWorkflowResult,
9
41
  collectRepoObjects,
42
+ commonTriggerTags,
43
+ controlOrder,
44
+ createGitBlob,
45
+ createGitCommit,
46
+ createGitTag,
47
+ createGitTree,
48
+ decryptSecretUpdate,
49
+ deriveTrustLevel,
50
+ discoverWorkflows,
10
51
  downloadGitObjects,
52
+ effectiveSecrets,
53
+ encryptSecretUpdate,
11
54
  executePush,
12
55
  fetchTxBytes,
56
+ generateSecretsKey,
57
+ hashGitObject,
13
58
  hexToNpub,
59
+ isCiConclusion,
60
+ isCiTrustLevel,
14
61
  isSafeRefname,
62
+ matchesPullRequest,
63
+ matchesPush,
15
64
  missingObjectsMessage,
16
65
  npubToHex,
17
66
  ownerToHex,
67
+ parseCiAdvertisement,
68
+ parseCiJobResult,
69
+ parseCiManualTrigger,
70
+ parseCiSecretUpdate,
71
+ parseCiServiceControl,
72
+ parseCiTriggerContext,
73
+ parseCiWorkflowProgress,
74
+ parseCiWorkflowResult,
75
+ parseRepoAddress,
76
+ parseWorkflow,
18
77
  planPush,
78
+ refMatchesPatterns,
19
79
  referencedShas,
80
+ repoAddress,
81
+ selectServiceRequests,
20
82
  serializeEventReceipt,
21
83
  serializeFeeEstimate,
22
84
  serializePushPlan,
23
85
  serializePushResult,
24
86
  setHeadSymref,
87
+ sha256Hex,
88
+ trustAtLeast,
25
89
  updateRef,
90
+ validateSecretUpdate,
26
91
  verifyObjectBody,
27
92
  walkClosure,
28
93
  writeGitObject,
29
94
  writeGitObjects
30
- } from "./chunk-7VCVR4GK.js";
95
+ } from "./chunk-3RWM7PBM.js";
31
96
  import {
32
97
  COMMENT_KIND,
33
98
  MAINTAINERS_TAG,
@@ -45,15 +110,8 @@ import {
45
110
  parseMaintainers,
46
111
  parsePayout,
47
112
  queryRelay
48
- } from "./chunk-NCEMQUPA.js";
49
- import {
50
- MAX_OBJECT_SIZE,
51
- createGitBlob,
52
- createGitCommit,
53
- createGitTag,
54
- createGitTree,
55
- hashGitObject
56
- } from "./chunk-GVXBUL3C.js";
113
+ } from "./chunk-GWA3CSUR.js";
114
+ import "./chunk-U5A7MHBP.js";
57
115
 
58
116
  // src/factory-job-gate.ts
59
117
  function gatePassed(gate) {
@@ -328,23 +386,96 @@ async function executeFactoryJob(options) {
328
386
  }
329
387
  return haltWith("completed");
330
388
  }
389
+
390
+ // src/ci/runner.ts
391
+ function defaultFakeRunResult(request) {
392
+ const now = Math.floor(Date.now() / 1e3);
393
+ return {
394
+ conclusion: "success",
395
+ startedAt: now,
396
+ finishedAt: now,
397
+ jobs: [
398
+ {
399
+ jobId: "build",
400
+ name: "build",
401
+ conclusion: "success",
402
+ exitCode: 0,
403
+ startedAt: now,
404
+ finishedAt: now,
405
+ log: `fake runner: ran ${request.workflow.path} at ${request.trigger.commit}
406
+ `,
407
+ artifacts: []
408
+ }
409
+ ]
410
+ };
411
+ }
412
+ var FakeRunner = class {
413
+ constructor(script = defaultFakeRunResult) {
414
+ this.script = script;
415
+ }
416
+ script;
417
+ family = "act";
418
+ selectors = ["ubuntu-latest"];
419
+ /** Every request, in call order. */
420
+ requests = [];
421
+ async run(request) {
422
+ this.requests.push(request);
423
+ const result = await this.script(request);
424
+ if (request.onLog) {
425
+ for (const job of result.jobs) request.onLog(job.jobId, job.log);
426
+ }
427
+ return result;
428
+ }
429
+ };
331
430
  export {
431
+ CI_ADVERTISEMENT_KIND,
432
+ CI_JOB_RESULT_KIND,
433
+ CI_MANUAL_TRIGGER_KIND,
434
+ CI_MAX_ADVERTISEMENT_TTL,
435
+ CI_MAX_PROGRESS_TTL,
436
+ CI_RUNNER_FAMILY,
437
+ CI_SECRET_UPDATE_KIND,
438
+ CI_SERVICE_REQUEST_KIND,
439
+ CI_SERVICE_STOP_KIND,
440
+ CI_SOFTWARE,
441
+ CI_TRUST_ORDER,
442
+ CI_WORKFLOW_PROGRESS_KIND,
443
+ CI_WORKFLOW_RESULT_KIND,
332
444
  COMMENT_KIND,
333
445
  DEFAULT_CONCURRENCY,
334
446
  FACTORY_JOB_FEEDBACK_KIND,
335
447
  FACTORY_JOB_REQUEST_KIND,
336
448
  FACTORY_JOB_RESULT_KIND,
449
+ FakeRunner,
337
450
  GitError,
338
451
  GitRepoReader,
339
452
  MAINTAINERS_TAG,
340
453
  MAX_OBJECT_SIZE,
454
+ MAX_SECRET_CIPHERTEXT_BYTES,
455
+ MAX_SECRET_NAMES,
456
+ MAX_SECRET_PLAINTEXT_BYTES,
457
+ MAX_SECRET_VALUE_BYTES,
341
458
  NonFastForwardError,
342
459
  ObjectIntegrityError,
343
460
  ObjectWriteMismatchError,
344
461
  OversizeObjectsError,
345
462
  PAYOUT_TAG,
346
463
  REPOSITORY_STATE_KIND,
464
+ RESERVED_SECRET_NAMES,
465
+ SECRET_NAME_RE,
466
+ WORKFLOW_DIRS,
467
+ applySecretUpdate,
468
+ assembleCiStatus,
469
+ assertSecretUpdateShape,
347
470
  authorizedStatusAuthors,
471
+ buildCiAdvertisement,
472
+ buildCiJobResult,
473
+ buildCiManualTrigger,
474
+ buildCiSecretUpdate,
475
+ buildCiServiceRequest,
476
+ buildCiServiceStop,
477
+ buildCiWorkflowProgress,
478
+ buildCiWorkflowResult,
348
479
  buildComment,
349
480
  buildIncrementOfferEvent,
350
481
  buildIssue,
@@ -356,36 +487,65 @@ export {
356
487
  buildResultEvent,
357
488
  buildStatus,
358
489
  collectRepoObjects,
490
+ commonTriggerTags,
491
+ controlOrder,
359
492
  createGitBlob,
360
493
  createGitCommit,
361
494
  createGitTag,
362
495
  createGitTree,
496
+ decryptSecretUpdate,
497
+ defaultFakeRunResult,
498
+ deriveTrustLevel,
499
+ discoverWorkflows,
363
500
  downloadGitObjects,
501
+ effectiveSecrets,
502
+ encryptSecretUpdate,
364
503
  executeFactoryJob,
365
504
  executePush,
366
505
  fetchRemoteState,
367
506
  fetchTxBytes,
368
507
  gatePassed,
508
+ generateSecretsKey,
369
509
  hashGitObject,
370
510
  hexToNpub,
511
+ isCiConclusion,
512
+ isCiTrustLevel,
371
513
  isSafeRefname,
372
514
  isValidEvmPayoutAddress,
515
+ matchesPullRequest,
516
+ matchesPush,
373
517
  missingObjectsMessage,
374
518
  npubToHex,
375
519
  ownerToHex,
520
+ parseCiAdvertisement,
521
+ parseCiJobResult,
522
+ parseCiManualTrigger,
523
+ parseCiSecretUpdate,
524
+ parseCiServiceControl,
525
+ parseCiTriggerContext,
526
+ parseCiWorkflowProgress,
527
+ parseCiWorkflowResult,
376
528
  parseFactoryJobRequest,
377
529
  parseMaintainers,
378
530
  parsePayout,
531
+ parseRepoAddress,
532
+ parseWorkflow,
379
533
  planFactoryJob,
380
534
  planPush,
381
535
  queryRelay,
536
+ refMatchesPatterns,
382
537
  referencedShas,
538
+ repoAddress,
539
+ selectServiceRequests,
383
540
  serializeEventReceipt,
384
541
  serializeFeeEstimate,
385
542
  serializePushPlan,
386
543
  serializePushResult,
387
544
  setHeadSymref,
545
+ sha256Hex,
546
+ trustAtLeast,
388
547
  updateRef,
548
+ validateSecretUpdate,
389
549
  verifyObjectBody,
390
550
  walkClosure,
391
551
  writeGitObject,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/factory-job-gate.ts","../src/factory-job-events.ts","../src/factory-job-plan.ts","../src/factory-job-execute.ts"],"sourcesContent":["/**\n * Factory job protocol — reproducible gate result per increment (#53).\n *\n * Decision 5 of toon-meta#262 splits trust into three layers: delivery (the\n * hashlock), the objective floor (this — \"did this increment pass the\n * repo's own gate?\"), and quality (reputation, out of scope here). Per\n * `FACTORY.md`, the pinned pipeline's gate is lint/typecheck/test/build run\n * before a PR is opened; this module is the pure, network-free shape for\n * recording exactly what ran, not a summary verdict.\n *\n * This proves conformance, not fitness for purpose — a gate-passing\n * increment can still completely miss the brief. Reputation is what\n * distinguishes those, never this.\n */\n\n/** One command the gate ran, and whether it passed. */\nexport interface GateCheck {\n /** e.g. \"lint\", \"typecheck\", \"test\", \"build\" — whatever actually ran,\n * never a canonical list (gates differ per repo and drift over time). */\n name: string;\n /** The literal command, e.g. \"eslint .\", so a buyer can reproduce it verbatim. */\n command: string;\n pass: boolean;\n}\n\n/**\n * A reproducible gate result: enough for a buyer to re-run the gate\n * themselves and get the same answer. Omitting the commit or the command\n * set turns this from a reproducible result into a provider's unverifiable\n * claim of having done well — both are required, not advisory.\n */\nexport interface GateResult {\n /** The commit this gate ran against. */\n commit: string;\n /** Toolchain versions relevant to reproducing the result, e.g. `{ node: \"20.11.0\", pnpm: \"9.1.0\" }`. */\n toolchain: Record<string, string>;\n checks: GateCheck[];\n}\n\n/** Overall pass/fail — every recorded check passed. A failing gate is not a protocol violation (it is still published, just visibly failing) — this is only the aggregate the `gate` tag and reputation's gate-pass rate read. */\nexport function gatePassed(gate: GateResult): boolean {\n return gate.checks.every((check) => check.pass);\n}\n","/**\n * Factory job protocol — pure NIP-90 event builders/parser (#52).\n *\n * Wire format per toon-meta's `docs/factory-job-protocol.md`\n * (toon-meta#263), fixed 2026-08-03: kind:5097 job request, kind:6097 job\n * result, kind:7000 job feedback (quote / increment offer / narration,\n * disambiguated by the `status` tag). Mirrors nip34-events.ts: every builder\n * returns an UnsignedEvent — the caller signs and publishes (e.g. through a\n * Publisher, publisher.ts) elsewhere. No crypto, no network, no payment.\n */\n\nimport type { UnsignedEvent } from './nip34-events.js';\nimport type { IncrementSpec } from './factory-job-plan.js';\nimport { gatePassed, type GateResult } from './factory-job-gate.js';\n\n// ---------------------------------------------------------------------------\n// Kinds\n// ---------------------------------------------------------------------------\n\n/** Factory job request (the brief). */\nexport const FACTORY_JOB_REQUEST_KIND = 5097;\n/** Factory job result — terminal state (completed/abandoned-*). */\nexport const FACTORY_JOB_RESULT_KIND = 6097;\n/** Factory job feedback — quote, increment offer, or free narration. */\nexport const FACTORY_JOB_FEEDBACK_KIND = 7000;\n\n/** The three §5.2 terminal outcomes — decision 8 has no fourth. */\nexport type FactoryJobOutcome =\n | 'completed'\n | 'abandoned-provider'\n | 'abandoned-buyer';\n\n// ---------------------------------------------------------------------------\n// kind:5097 — job request\n// ---------------------------------------------------------------------------\n\n/** A parsed kind:5097 job request (§2). */\nexport interface FactoryJobRequest {\n /** The requesting kind:5097 event's id. */\n requestEventId: string;\n /** The requesting kind:5097 event's pubkey (who to reply/bill). */\n buyerPubkey: string;\n /** The brief — free text or a chained milestone reference (§2.3). */\n brief: string;\n /** Maximum the buyer will pay across the whole job (micro-USDC, §2.1). */\n bidMicroUsdc: string;\n /** `param repo` — target repo, e.g. `toon-protocol/buzz`. */\n repo?: string;\n /** `param target` — the ticket/issue this job resolves. */\n target?: string;\n /** `param constraints` — gate requirements / scope limits. */\n constraints?: string;\n /** `output` — expected deliverable MIME type. */\n outputMime?: string;\n}\n\n/** Minimal shape of a relay event this module reads/builds — no signing. */\nexport interface RelayEvent {\n id: string;\n pubkey: string;\n tags: string[][];\n content: string;\n}\n\n/**\n * Parse a kind:5097 job request event's tags into a {@link FactoryJobRequest}.\n * Throws if the required `i` (brief) or `bid` tag is missing — both are\n * REQUIRED per §2.1.\n */\nexport function parseFactoryJobRequest(event: RelayEvent): FactoryJobRequest {\n const briefTag = event.tags.find((t) => t[0] === 'i' && t[2] === 'text');\n if (!briefTag?.[1]) {\n throw new Error(\n `kind:${FACTORY_JOB_REQUEST_KIND} event ${event.id} is missing its required brief (\"i\", \"<brief>\", \"text\") tag`\n );\n }\n const bidTag = event.tags.find((t) => t[0] === 'bid');\n if (!bidTag?.[1]) {\n throw new Error(\n `kind:${FACTORY_JOB_REQUEST_KIND} event ${event.id} is missing its required \"bid\" tag`\n );\n }\n\n const params = event.tags.filter((t) => t[0] === 'param');\n const paramValue = (name: string): string | undefined =>\n params.find((t) => t[1] === name)?.[2];\n\n const repo = paramValue('repo');\n const target = paramValue('target');\n const constraints = paramValue('constraints');\n const outputMime = event.tags.find((t) => t[0] === 'output')?.[1];\n\n return {\n requestEventId: event.id,\n buyerPubkey: event.pubkey,\n brief: briefTag[1],\n bidMicroUsdc: bidTag[1],\n ...(repo !== undefined ? { repo } : {}),\n ...(target !== undefined ? { target } : {}),\n ...(constraints !== undefined ? { constraints } : {}),\n ...(outputMime !== undefined ? { outputMime } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"quote\" — the RFQ reply (§3)\n// ---------------------------------------------------------------------------\n\n/**\n * Build the `kind:7000 status:\"quote\"` reply: the proposed increment\n * schedule, committing nothing (§3.3 — there is no accept message; payment\n * of increment 1 is acceptance).\n */\nexport function buildQuoteEvent(\n job: FactoryJobRequest,\n schedule: IncrementSpec[]\n): UnsignedEvent {\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: JSON.stringify({\n increments: schedule.map((s) => ({\n n: s.n,\n of: s.of,\n milestone: s.milestone,\n priceUsdc: s.priceUsdc,\n })),\n }),\n tags: [\n ['e', job.requestEventId, '', 'root'],\n ['p', job.buyerPubkey],\n ['status', 'quote'],\n ],\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"partial\" — the increment offer (§4)\n// ---------------------------------------------------------------------------\n\n/** Where the encrypted increment artifact lives, and its hashlock condition. */\nexport interface EncryptedArtifactRef {\n /** Arweave tx id the ciphertext was uploaded to. */\n arweaveTxId: string;\n /** sha256 of the ciphertext, hex — lets the buyer detect a bad fetch. */\n ciphertextSha256: string;\n /** `sha256(key)` hex, where `key` decrypts the artifact (§4.2 — the join). */\n conditionHex: string;\n}\n\nexport interface BuildIncrementOfferOptions {\n job: FactoryJobRequest;\n /** The quote event's id for increment 1, else the previous offer's id. */\n parentEventId: string;\n increment: IncrementSpec;\n artifact: EncryptedArtifactRef;\n /**\n * The reproducible gate result for this increment's work (#53 — the\n * objective floor), when one ran. Omitted for milestones with nothing to\n * gate (e.g. `plan` — there is no code to lint against a brief).\n */\n gate?: GateResult;\n}\n\n/** A gate result missing its commit or check list cannot be reproduced by the buyer — reject it here rather than publish an unverifiable claim. */\nfunction assertReproducible(gate: GateResult): void {\n if (gate.checks.length === 0) {\n throw new Error(\n 'gate result must record at least one check for the offer to be reproducible'\n );\n }\n if (!gate.commit) {\n throw new Error(\n 'gate result must record the commit it ran against for the offer to be reproducible'\n );\n }\n}\n\n/**\n * Build the `kind:7000 status:\"partial\"` increment offer — published once\n * the provider has done that increment's work and uploaded the encrypted\n * artifact. This is the join between the relay plane and the connector\n * plane (§4.2): `condition` here MUST equal the paying PREPARE's\n * `executionCondition`, byte for byte.\n *\n * When `gate` is supplied, the offer also carries a `[\"gate\", \"pass\"|\"fail\"]`\n * tag (cheap to scan for a gate-pass rate, decision 8) and the full\n * reproducible result in `content` — visible to the buyer before they pay,\n * per the issue: a gate-failing increment is still offered, never hidden.\n */\nexport function buildIncrementOfferEvent(\n options: BuildIncrementOfferOptions\n): UnsignedEvent {\n const { job, parentEventId, increment, artifact, gate } = options;\n if (gate) assertReproducible(gate);\n\n const tags: string[][] = [\n ['e', job.requestEventId, '', 'root'],\n ['e', parentEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['status', 'partial'],\n ['increment', String(increment.n), String(increment.of)],\n ['i', artifact.arweaveTxId, 'url'],\n ['i', artifact.ciphertextSha256, 'text', '', 'hash'],\n ['amount', increment.priceUsdc, 'usdc'],\n ['condition', artifact.conditionHex],\n ];\n if (gate) tags.push(['gate', gatePassed(gate) ? 'pass' : 'fail']);\n\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: gate ? JSON.stringify({ gate }) : '',\n tags,\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"processing\" — free narration (§6)\n// ---------------------------------------------------------------------------\n\nexport interface BuildNarrationOptions {\n job: FactoryJobRequest;\n /** The prior event in the thread (quote, offer, or narration). */\n parentEventId: string;\n message: string;\n}\n\n/**\n * Build a free narration event — no artifact, no `amount`/`condition`, MUST\n * NOT be paid against (§6). This is what makes a provider look alive between\n * increments.\n */\nexport function buildNarrationEvent(\n options: BuildNarrationOptions\n): UnsignedEvent {\n const { job, parentEventId, message } = options;\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: message,\n tags: [\n ['e', job.requestEventId, '', 'root'],\n ['e', parentEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['status', 'processing'],\n ],\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:6097 — job result (§5)\n// ---------------------------------------------------------------------------\n\nexport interface BuildResultEventOptions {\n job: FactoryJobRequest;\n /** The original kind:5097 request event, verbatim (the `request` tag). */\n requestEvent: RelayEvent;\n /** The last kind:7000 event in the thread (offer or narration). */\n lastEventId: string;\n outcome: FactoryJobOutcome;\n /** How far the job got — `n-reached == of` only when outcome is completed. */\n reachedIncrement: number;\n totalIncrements: number;\n /** Required (and only meaningful) when outcome is `\"completed\"` (§5.2). */\n finalArtifact?: { arweaveTxId: string };\n}\n\n/**\n * Build the terminal `kind:6097` job result. Reputation (decision 8) is\n * computed from exactly this event and nothing else.\n */\nexport function buildResultEvent(\n options: BuildResultEventOptions\n): UnsignedEvent {\n const {\n job,\n requestEvent,\n lastEventId,\n outcome,\n reachedIncrement,\n totalIncrements,\n finalArtifact,\n } = options;\n\n const tags: string[][] = [\n ['e', job.requestEventId, '', 'root'],\n ['e', lastEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['request', JSON.stringify(requestEvent)],\n ['outcome', outcome],\n ['increment', String(reachedIncrement), String(totalIncrements)],\n ];\n if (outcome === 'completed' && finalArtifact) {\n tags.push(['i', finalArtifact.arweaveTxId, 'url']);\n }\n\n return {\n kind: FACTORY_JOB_RESULT_KIND,\n content: '',\n tags,\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n","/**\n * Factory job protocol — increment schedule planner (#52).\n *\n * `planFactoryJob` is the network-free half of the adapter (mirrors\n * `planPush`'s split from `executePush` in push.ts): given a job's `bid` and\n * the ticket list the factory's plan phase produced, it prices the\n * milestone boundaries decision 6 of toon-meta#262 fixed as increments —\n * plan → implement ×N tickets → review — and returns the schedule that\n * becomes the `kind:7000 status:\"quote\"` content (§3.2 of\n * `docs/factory-job-protocol.md`).\n *\n * Per the issue gotcha, `implement` must be many increments, not one: this\n * always emits one increment per ticket rather than a single lump-sum\n * implement increment, using the factory's existing parallel per-issue\n * fan-out (`.sandcastle/main.ts`'s Plan phase output — `{issues: [{id,\n * title, branch}]}`) as the boundary.\n */\n\n/** One ticket the factory's plan phase fanned the implement milestone into. */\nexport interface FactoryTicket {\n id: string;\n title: string;\n branch: string;\n}\n\nexport type FactoryMilestone = 'plan' | 'implement' | 'review';\n\n/** One priced increment in the schedule (§3.2's `increments[]` shape). */\nexport interface IncrementSpec {\n n: number;\n of: number;\n milestone: FactoryMilestone;\n /** Set only for `milestone: \"implement\"` increments. */\n ticket?: FactoryTicket;\n priceUsdc: string;\n}\n\n/** Plan milestone's share of the bid, in basis points. */\nconst PLAN_SHARE_BPS = 1000n; // 10%\n/** Review milestone's share of the bid, in basis points. */\nconst REVIEW_SHARE_BPS = 1000n; // 10%\nconst BPS_DENOM = 10000n;\n\n/**\n * Build the increment schedule for a job, given the tickets its plan phase\n * produced. Plan and review each get a fixed 10% of the bid; the remaining\n * 80% (\"implement\") splits evenly across tickets, with the integer-division\n * remainder distributed one micro-USDC at a time to the earliest tickets —\n * so `sum(priceUsdc) === bid` exactly (§3.2 only requires `<=`, but an exact\n * match avoids \"where did the leftover micro-USDC go\" questions).\n *\n * Per decision 7, this schedule is agreed before any paid work starts and is\n * never repriced — a job that runs larger than `tickets` implied is a\n * lesson, not a renegotiation.\n *\n * Throws if `tickets` is empty (a job with no implementation work has no\n * increments to schedule) or `bidMicroUsdc` is not a positive integer.\n */\nexport function planFactoryJob(\n bidMicroUsdc: string,\n tickets: FactoryTicket[]\n): IncrementSpec[] {\n if (tickets.length === 0) {\n throw new Error(\n 'planFactoryJob requires at least one implementation ticket from the plan phase'\n );\n }\n\n let bid: bigint;\n try {\n bid = BigInt(bidMicroUsdc);\n } catch {\n throw new Error(`bidMicroUsdc must be an integer string, got ${bidMicroUsdc}`);\n }\n if (bid <= 0n) {\n throw new Error(`bidMicroUsdc must be positive, got ${bidMicroUsdc}`);\n }\n\n const planPrice = (bid * PLAN_SHARE_BPS) / BPS_DENOM;\n const reviewPrice = (bid * REVIEW_SHARE_BPS) / BPS_DENOM;\n const implementTotal = bid - planPrice - reviewPrice;\n\n const ticketCount = BigInt(tickets.length);\n const base = implementTotal / ticketCount;\n const remainder = implementTotal % ticketCount;\n\n const total = tickets.length + 2;\n const schedule: IncrementSpec[] = [\n { n: 1, of: total, milestone: 'plan', priceUsdc: planPrice.toString() },\n ];\n\n tickets.forEach((ticket, i) => {\n const price = base + (BigInt(i) < remainder ? 1n : 0n);\n schedule.push({\n n: i + 2,\n of: total,\n milestone: 'implement',\n ticket,\n priceUsdc: price.toString(),\n });\n });\n\n schedule.push({\n n: total,\n of: total,\n milestone: 'review',\n priceUsdc: reviewPrice.toString(),\n });\n\n return schedule;\n}\n","/**\n * executeFactoryJob — the adapter's imperative shell (#52): runs the\n * factory's own milestones (`.sandcastle/main.ts`'s plan → per-ticket\n * implement+review → merge phases) through `hooks`, and turns each\n * milestone boundary into a paid increment via `publisher` (existing,\n * publisher.ts — upload + event publish) and `delivery` (new,\n * factory-job-delivery.ts — encrypt + payment wait).\n *\n * Mirrors `executePush`'s split from `planPush`: `planFactoryJob` is the\n * network-free planning half; this is the half that spends money.\n *\n * Decision 6 (toon-meta#262): work happens BEFORE the packet, so every\n * increment's artifact is produced and uploaded before that increment is\n * offered for payment. Decision 7: the schedule is fixed once the quote is\n * published — this never reprices mid-job. Per the issue: \"Stop on\n * non-payment. One unpaid increment and work halts\" — the symmetric\n * protection that neither side is ever exposed for more than one increment.\n */\n\nimport type { Publisher } from './publisher.js';\nimport type { JobDeliveryPort } from './factory-job-delivery.js';\nimport {\n buildIncrementOfferEvent,\n buildQuoteEvent,\n buildResultEvent,\n type FactoryJobOutcome,\n type FactoryJobRequest,\n type RelayEvent,\n} from './factory-job-events.js';\nimport type { GateResult } from './factory-job-gate.js';\nimport {\n planFactoryJob,\n type FactoryTicket,\n type IncrementSpec,\n} from './factory-job-plan.js';\n\n/** One milestone's deliverable, plus the gate result that ran against it, when one did. */\nexport interface FactoryJobWork {\n artifact: Uint8Array;\n /**\n * The reproducible gate result (#53) for this milestone's work, per\n * `FACTORY.md`'s pinned pipeline (lint/typecheck/test/build). Omitted for\n * `plan` — there is no code to gate against a brief.\n */\n gate?: GateResult;\n}\n\n/** Runs one factory milestone and returns its deliverable, plus that milestone's gate result. */\nexport interface FactoryJobHooks {\n /**\n * Runs the factory's plan phase for `job` (mirrors `.sandcastle/main.ts`'s\n * Phase 1): returns the per-ticket fan-out the implement milestone splits\n * into, plus the plan document itself as increment 1's artifact. No gate\n * result — planning produces no code to lint/typecheck/test/build.\n */\n plan(\n job: FactoryJobRequest\n ): Promise<{ tickets: FactoryTicket[]; artifact: Uint8Array }>;\n /**\n * Runs one ticket's implement sandbox pipeline (mirrors Phase 2's\n * per-issue `sandbox.run`, which ends in the gate per `FACTORY.md`) and\n * returns its deliverable (e.g. the ticket's diff) plus that run's gate\n * result.\n */\n implement(ticket: FactoryTicket): Promise<FactoryJobWork>;\n /**\n * Runs the factory's merge/review phase over every completed ticket\n * (mirrors Phase 3, which re-runs the gate per `merge-prompt.md`) and\n * returns the final deliverable plus that run's gate result.\n */\n review(tickets: FactoryTicket[]): Promise<FactoryJobWork>;\n}\n\nexport interface ExecuteFactoryJobOptions {\n job: FactoryJobRequest;\n /** The original kind:5097 request event, verbatim — carried in the result's `request` tag. */\n requestEvent: RelayEvent;\n hooks: FactoryJobHooks;\n publisher: Publisher;\n delivery: JobDeliveryPort;\n relayUrls: string[];\n}\n\nexport interface FactoryJobExecution {\n outcome: FactoryJobOutcome;\n /** The last increment successfully paid for (0 if the buyer never paid increment 1). */\n reachedIncrement: number;\n totalIncrements: number;\n /** The published kind:6097 result event's id. */\n resultEventId: string;\n}\n\nasync function workFor(\n spec: IncrementSpec,\n tickets: FactoryTicket[],\n planArtifact: Uint8Array,\n hooks: FactoryJobHooks\n): Promise<FactoryJobWork> {\n if (spec.milestone === 'plan') return { artifact: planArtifact };\n if (spec.milestone === 'implement') {\n if (!spec.ticket) {\n throw new Error(\n `internal: implement increment ${spec.n} is missing its ticket`\n );\n }\n return hooks.implement(spec.ticket);\n }\n return hooks.review(tickets);\n}\n\n/**\n * Run a job end to end: plan → quote → one increment per milestone boundary\n * (encrypt, upload, offer, wait for payment) → terminal kind:6097 result.\n * Halts at the first unpaid increment (`abandoned-buyer`) or the first\n * hook failure once work is underway (`abandoned-provider`) — see §5.2 of\n * `docs/factory-job-protocol.md` for the three terminal outcomes.\n *\n * Throws if `hooks.plan` itself fails — with no quote ever published, there\n * is no prior kind:7000 event for a result's `reply` e-tag to reference, so\n * no protocol event can be honestly emitted for that failure.\n */\nexport async function executeFactoryJob(\n options: ExecuteFactoryJobOptions\n): Promise<FactoryJobExecution> {\n const { job, requestEvent, hooks, publisher, delivery, relayUrls } =\n options;\n\n if (!publisher.uploadBlob) {\n throw new Error(\n 'the active publisher cannot upload an encrypted increment artifact ' +\n '(uploadBlob unavailable) — executeFactoryJob requires the standalone publisher'\n );\n }\n const uploadBlob = publisher.uploadBlob.bind(publisher);\n\n const { tickets, artifact: planArtifact } = await hooks.plan(job);\n const schedule = planFactoryJob(job.bidMicroUsdc, tickets);\n const totalIncrements = schedule.length;\n\n const quoteEvent = buildQuoteEvent(job, schedule);\n const quoteReceipt = await publisher.publishEvent(quoteEvent, relayUrls);\n\n let parentEventId = quoteReceipt.eventId;\n let reachedIncrement = 0;\n let finalArtifactTxId: string | undefined;\n\n const haltWith = async (\n outcome: FactoryJobOutcome\n ): Promise<FactoryJobExecution> => {\n const resultEvent = buildResultEvent({\n job,\n requestEvent,\n lastEventId: parentEventId,\n outcome,\n reachedIncrement,\n totalIncrements,\n ...(outcome === 'completed' && finalArtifactTxId\n ? { finalArtifact: { arweaveTxId: finalArtifactTxId } }\n : {}),\n });\n const resultReceipt = await publisher.publishEvent(\n resultEvent,\n relayUrls\n );\n return {\n outcome,\n reachedIncrement,\n totalIncrements,\n resultEventId: resultReceipt.eventId,\n };\n };\n\n for (const spec of schedule) {\n let work: FactoryJobWork;\n try {\n work = await workFor(spec, tickets, planArtifact, hooks);\n } catch {\n // Provider-side failure mid-job: stop where we are, no renegotiation.\n return haltWith('abandoned-provider');\n }\n\n const encrypted = await delivery.encryptArtifact(work.artifact);\n const uploadReceipt = await uploadBlob({\n body: Buffer.from(encrypted.ciphertext),\n contentType: 'application/octet-stream',\n ...(job.repo ? { repoId: job.repo } : {}),\n });\n\n const offerEvent = buildIncrementOfferEvent({\n job,\n parentEventId,\n increment: spec,\n artifact: {\n arweaveTxId: uploadReceipt.txId,\n ciphertextSha256: encrypted.ciphertextSha256,\n conditionHex: encrypted.conditionHex,\n },\n ...(work.gate ? { gate: work.gate } : {}),\n });\n const offerReceipt = await publisher.publishEvent(offerEvent, relayUrls);\n\n const paid = await delivery.waitForPayment({\n offerEventId: offerReceipt.eventId,\n conditionHex: encrypted.conditionHex,\n priceUsdc: spec.priceUsdc,\n });\n if (!paid) {\n parentEventId = offerReceipt.eventId;\n return haltWith('abandoned-buyer');\n }\n\n reachedIncrement = spec.n;\n parentEventId = offerReceipt.eventId;\n if (spec.milestone === 'review') finalArtifactTxId = uploadReceipt.txId;\n }\n\n return haltWith('completed');\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/factory-job-gate.ts","../src/factory-job-events.ts","../src/factory-job-plan.ts","../src/factory-job-execute.ts","../src/ci/runner.ts"],"sourcesContent":["/**\n * Factory job protocol — reproducible gate result per increment (#53).\n *\n * Decision 5 of toon-meta#262 splits trust into three layers: delivery (the\n * hashlock), the objective floor (this — \"did this increment pass the\n * repo's own gate?\"), and quality (reputation, out of scope here). Per\n * `FACTORY.md`, the pinned pipeline's gate is lint/typecheck/test/build run\n * before a PR is opened; this module is the pure, network-free shape for\n * recording exactly what ran, not a summary verdict.\n *\n * This proves conformance, not fitness for purpose — a gate-passing\n * increment can still completely miss the brief. Reputation is what\n * distinguishes those, never this.\n */\n\n/** One command the gate ran, and whether it passed. */\nexport interface GateCheck {\n /** e.g. \"lint\", \"typecheck\", \"test\", \"build\" — whatever actually ran,\n * never a canonical list (gates differ per repo and drift over time). */\n name: string;\n /** The literal command, e.g. \"eslint .\", so a buyer can reproduce it verbatim. */\n command: string;\n pass: boolean;\n}\n\n/**\n * A reproducible gate result: enough for a buyer to re-run the gate\n * themselves and get the same answer. Omitting the commit or the command\n * set turns this from a reproducible result into a provider's unverifiable\n * claim of having done well — both are required, not advisory.\n */\nexport interface GateResult {\n /** The commit this gate ran against. */\n commit: string;\n /** Toolchain versions relevant to reproducing the result, e.g. `{ node: \"20.11.0\", pnpm: \"9.1.0\" }`. */\n toolchain: Record<string, string>;\n checks: GateCheck[];\n}\n\n/** Overall pass/fail — every recorded check passed. A failing gate is not a protocol violation (it is still published, just visibly failing) — this is only the aggregate the `gate` tag and reputation's gate-pass rate read. */\nexport function gatePassed(gate: GateResult): boolean {\n return gate.checks.every((check) => check.pass);\n}\n","/**\n * Factory job protocol — pure NIP-90 event builders/parser (#52).\n *\n * Wire format per toon-meta's `docs/factory-job-protocol.md`\n * (toon-meta#263), fixed 2026-08-03: kind:5097 job request, kind:6097 job\n * result, kind:7000 job feedback (quote / increment offer / narration,\n * disambiguated by the `status` tag). Mirrors nip34-events.ts: every builder\n * returns an UnsignedEvent — the caller signs and publishes (e.g. through a\n * Publisher, publisher.ts) elsewhere. No crypto, no network, no payment.\n */\n\nimport type { UnsignedEvent } from './nip34-events.js';\nimport type { IncrementSpec } from './factory-job-plan.js';\nimport { gatePassed, type GateResult } from './factory-job-gate.js';\n\n// ---------------------------------------------------------------------------\n// Kinds\n// ---------------------------------------------------------------------------\n\n/** Factory job request (the brief). */\nexport const FACTORY_JOB_REQUEST_KIND = 5097;\n/** Factory job result — terminal state (completed/abandoned-*). */\nexport const FACTORY_JOB_RESULT_KIND = 6097;\n/** Factory job feedback — quote, increment offer, or free narration. */\nexport const FACTORY_JOB_FEEDBACK_KIND = 7000;\n\n/** The three §5.2 terminal outcomes — decision 8 has no fourth. */\nexport type FactoryJobOutcome =\n | 'completed'\n | 'abandoned-provider'\n | 'abandoned-buyer';\n\n// ---------------------------------------------------------------------------\n// kind:5097 — job request\n// ---------------------------------------------------------------------------\n\n/** A parsed kind:5097 job request (§2). */\nexport interface FactoryJobRequest {\n /** The requesting kind:5097 event's id. */\n requestEventId: string;\n /** The requesting kind:5097 event's pubkey (who to reply/bill). */\n buyerPubkey: string;\n /** The brief — free text or a chained milestone reference (§2.3). */\n brief: string;\n /** Maximum the buyer will pay across the whole job (micro-USDC, §2.1). */\n bidMicroUsdc: string;\n /** `param repo` — target repo, e.g. `toon-protocol/buzz`. */\n repo?: string;\n /** `param target` — the ticket/issue this job resolves. */\n target?: string;\n /** `param constraints` — gate requirements / scope limits. */\n constraints?: string;\n /** `output` — expected deliverable MIME type. */\n outputMime?: string;\n}\n\n/** Minimal shape of a relay event this module reads/builds — no signing. */\nexport interface RelayEvent {\n id: string;\n pubkey: string;\n tags: string[][];\n content: string;\n}\n\n/**\n * Parse a kind:5097 job request event's tags into a {@link FactoryJobRequest}.\n * Throws if the required `i` (brief) or `bid` tag is missing — both are\n * REQUIRED per §2.1.\n */\nexport function parseFactoryJobRequest(event: RelayEvent): FactoryJobRequest {\n const briefTag = event.tags.find((t) => t[0] === 'i' && t[2] === 'text');\n if (!briefTag?.[1]) {\n throw new Error(\n `kind:${FACTORY_JOB_REQUEST_KIND} event ${event.id} is missing its required brief (\"i\", \"<brief>\", \"text\") tag`\n );\n }\n const bidTag = event.tags.find((t) => t[0] === 'bid');\n if (!bidTag?.[1]) {\n throw new Error(\n `kind:${FACTORY_JOB_REQUEST_KIND} event ${event.id} is missing its required \"bid\" tag`\n );\n }\n\n const params = event.tags.filter((t) => t[0] === 'param');\n const paramValue = (name: string): string | undefined =>\n params.find((t) => t[1] === name)?.[2];\n\n const repo = paramValue('repo');\n const target = paramValue('target');\n const constraints = paramValue('constraints');\n const outputMime = event.tags.find((t) => t[0] === 'output')?.[1];\n\n return {\n requestEventId: event.id,\n buyerPubkey: event.pubkey,\n brief: briefTag[1],\n bidMicroUsdc: bidTag[1],\n ...(repo !== undefined ? { repo } : {}),\n ...(target !== undefined ? { target } : {}),\n ...(constraints !== undefined ? { constraints } : {}),\n ...(outputMime !== undefined ? { outputMime } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"quote\" — the RFQ reply (§3)\n// ---------------------------------------------------------------------------\n\n/**\n * Build the `kind:7000 status:\"quote\"` reply: the proposed increment\n * schedule, committing nothing (§3.3 — there is no accept message; payment\n * of increment 1 is acceptance).\n */\nexport function buildQuoteEvent(\n job: FactoryJobRequest,\n schedule: IncrementSpec[]\n): UnsignedEvent {\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: JSON.stringify({\n increments: schedule.map((s) => ({\n n: s.n,\n of: s.of,\n milestone: s.milestone,\n priceUsdc: s.priceUsdc,\n })),\n }),\n tags: [\n ['e', job.requestEventId, '', 'root'],\n ['p', job.buyerPubkey],\n ['status', 'quote'],\n ],\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"partial\" — the increment offer (§4)\n// ---------------------------------------------------------------------------\n\n/** Where the encrypted increment artifact lives, and its hashlock condition. */\nexport interface EncryptedArtifactRef {\n /** Arweave tx id the ciphertext was uploaded to. */\n arweaveTxId: string;\n /** sha256 of the ciphertext, hex — lets the buyer detect a bad fetch. */\n ciphertextSha256: string;\n /** `sha256(key)` hex, where `key` decrypts the artifact (§4.2 — the join). */\n conditionHex: string;\n}\n\nexport interface BuildIncrementOfferOptions {\n job: FactoryJobRequest;\n /** The quote event's id for increment 1, else the previous offer's id. */\n parentEventId: string;\n increment: IncrementSpec;\n artifact: EncryptedArtifactRef;\n /**\n * The reproducible gate result for this increment's work (#53 — the\n * objective floor), when one ran. Omitted for milestones with nothing to\n * gate (e.g. `plan` — there is no code to lint against a brief).\n */\n gate?: GateResult;\n}\n\n/** A gate result missing its commit or check list cannot be reproduced by the buyer — reject it here rather than publish an unverifiable claim. */\nfunction assertReproducible(gate: GateResult): void {\n if (gate.checks.length === 0) {\n throw new Error(\n 'gate result must record at least one check for the offer to be reproducible'\n );\n }\n if (!gate.commit) {\n throw new Error(\n 'gate result must record the commit it ran against for the offer to be reproducible'\n );\n }\n}\n\n/**\n * Build the `kind:7000 status:\"partial\"` increment offer — published once\n * the provider has done that increment's work and uploaded the encrypted\n * artifact. This is the join between the relay plane and the connector\n * plane (§4.2): `condition` here MUST equal the paying PREPARE's\n * `executionCondition`, byte for byte.\n *\n * When `gate` is supplied, the offer also carries a `[\"gate\", \"pass\"|\"fail\"]`\n * tag (cheap to scan for a gate-pass rate, decision 8) and the full\n * reproducible result in `content` — visible to the buyer before they pay,\n * per the issue: a gate-failing increment is still offered, never hidden.\n */\nexport function buildIncrementOfferEvent(\n options: BuildIncrementOfferOptions\n): UnsignedEvent {\n const { job, parentEventId, increment, artifact, gate } = options;\n if (gate) assertReproducible(gate);\n\n const tags: string[][] = [\n ['e', job.requestEventId, '', 'root'],\n ['e', parentEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['status', 'partial'],\n ['increment', String(increment.n), String(increment.of)],\n ['i', artifact.arweaveTxId, 'url'],\n ['i', artifact.ciphertextSha256, 'text', '', 'hash'],\n ['amount', increment.priceUsdc, 'usdc'],\n ['condition', artifact.conditionHex],\n ];\n if (gate) tags.push(['gate', gatePassed(gate) ? 'pass' : 'fail']);\n\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: gate ? JSON.stringify({ gate }) : '',\n tags,\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"processing\" — free narration (§6)\n// ---------------------------------------------------------------------------\n\nexport interface BuildNarrationOptions {\n job: FactoryJobRequest;\n /** The prior event in the thread (quote, offer, or narration). */\n parentEventId: string;\n message: string;\n}\n\n/**\n * Build a free narration event — no artifact, no `amount`/`condition`, MUST\n * NOT be paid against (§6). This is what makes a provider look alive between\n * increments.\n */\nexport function buildNarrationEvent(\n options: BuildNarrationOptions\n): UnsignedEvent {\n const { job, parentEventId, message } = options;\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: message,\n tags: [\n ['e', job.requestEventId, '', 'root'],\n ['e', parentEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['status', 'processing'],\n ],\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:6097 — job result (§5)\n// ---------------------------------------------------------------------------\n\nexport interface BuildResultEventOptions {\n job: FactoryJobRequest;\n /** The original kind:5097 request event, verbatim (the `request` tag). */\n requestEvent: RelayEvent;\n /** The last kind:7000 event in the thread (offer or narration). */\n lastEventId: string;\n outcome: FactoryJobOutcome;\n /** How far the job got — `n-reached == of` only when outcome is completed. */\n reachedIncrement: number;\n totalIncrements: number;\n /** Required (and only meaningful) when outcome is `\"completed\"` (§5.2). */\n finalArtifact?: { arweaveTxId: string };\n}\n\n/**\n * Build the terminal `kind:6097` job result. Reputation (decision 8) is\n * computed from exactly this event and nothing else.\n */\nexport function buildResultEvent(\n options: BuildResultEventOptions\n): UnsignedEvent {\n const {\n job,\n requestEvent,\n lastEventId,\n outcome,\n reachedIncrement,\n totalIncrements,\n finalArtifact,\n } = options;\n\n const tags: string[][] = [\n ['e', job.requestEventId, '', 'root'],\n ['e', lastEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['request', JSON.stringify(requestEvent)],\n ['outcome', outcome],\n ['increment', String(reachedIncrement), String(totalIncrements)],\n ];\n if (outcome === 'completed' && finalArtifact) {\n tags.push(['i', finalArtifact.arweaveTxId, 'url']);\n }\n\n return {\n kind: FACTORY_JOB_RESULT_KIND,\n content: '',\n tags,\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n","/**\n * Factory job protocol — increment schedule planner (#52).\n *\n * `planFactoryJob` is the network-free half of the adapter (mirrors\n * `planPush`'s split from `executePush` in push.ts): given a job's `bid` and\n * the ticket list the factory's plan phase produced, it prices the\n * milestone boundaries decision 6 of toon-meta#262 fixed as increments —\n * plan → implement ×N tickets → review — and returns the schedule that\n * becomes the `kind:7000 status:\"quote\"` content (§3.2 of\n * `docs/factory-job-protocol.md`).\n *\n * Per the issue gotcha, `implement` must be many increments, not one: this\n * always emits one increment per ticket rather than a single lump-sum\n * implement increment, using the factory's existing parallel per-issue\n * fan-out (`.sandcastle/main.ts`'s Plan phase output — `{issues: [{id,\n * title, branch}]}`) as the boundary.\n */\n\n/** One ticket the factory's plan phase fanned the implement milestone into. */\nexport interface FactoryTicket {\n id: string;\n title: string;\n branch: string;\n}\n\nexport type FactoryMilestone = 'plan' | 'implement' | 'review';\n\n/** One priced increment in the schedule (§3.2's `increments[]` shape). */\nexport interface IncrementSpec {\n n: number;\n of: number;\n milestone: FactoryMilestone;\n /** Set only for `milestone: \"implement\"` increments. */\n ticket?: FactoryTicket;\n priceUsdc: string;\n}\n\n/** Plan milestone's share of the bid, in basis points. */\nconst PLAN_SHARE_BPS = 1000n; // 10%\n/** Review milestone's share of the bid, in basis points. */\nconst REVIEW_SHARE_BPS = 1000n; // 10%\nconst BPS_DENOM = 10000n;\n\n/**\n * Build the increment schedule for a job, given the tickets its plan phase\n * produced. Plan and review each get a fixed 10% of the bid; the remaining\n * 80% (\"implement\") splits evenly across tickets, with the integer-division\n * remainder distributed one micro-USDC at a time to the earliest tickets —\n * so `sum(priceUsdc) === bid` exactly (§3.2 only requires `<=`, but an exact\n * match avoids \"where did the leftover micro-USDC go\" questions).\n *\n * Per decision 7, this schedule is agreed before any paid work starts and is\n * never repriced — a job that runs larger than `tickets` implied is a\n * lesson, not a renegotiation.\n *\n * Throws if `tickets` is empty (a job with no implementation work has no\n * increments to schedule) or `bidMicroUsdc` is not a positive integer.\n */\nexport function planFactoryJob(\n bidMicroUsdc: string,\n tickets: FactoryTicket[]\n): IncrementSpec[] {\n if (tickets.length === 0) {\n throw new Error(\n 'planFactoryJob requires at least one implementation ticket from the plan phase'\n );\n }\n\n let bid: bigint;\n try {\n bid = BigInt(bidMicroUsdc);\n } catch {\n throw new Error(`bidMicroUsdc must be an integer string, got ${bidMicroUsdc}`);\n }\n if (bid <= 0n) {\n throw new Error(`bidMicroUsdc must be positive, got ${bidMicroUsdc}`);\n }\n\n const planPrice = (bid * PLAN_SHARE_BPS) / BPS_DENOM;\n const reviewPrice = (bid * REVIEW_SHARE_BPS) / BPS_DENOM;\n const implementTotal = bid - planPrice - reviewPrice;\n\n const ticketCount = BigInt(tickets.length);\n const base = implementTotal / ticketCount;\n const remainder = implementTotal % ticketCount;\n\n const total = tickets.length + 2;\n const schedule: IncrementSpec[] = [\n { n: 1, of: total, milestone: 'plan', priceUsdc: planPrice.toString() },\n ];\n\n tickets.forEach((ticket, i) => {\n const price = base + (BigInt(i) < remainder ? 1n : 0n);\n schedule.push({\n n: i + 2,\n of: total,\n milestone: 'implement',\n ticket,\n priceUsdc: price.toString(),\n });\n });\n\n schedule.push({\n n: total,\n of: total,\n milestone: 'review',\n priceUsdc: reviewPrice.toString(),\n });\n\n return schedule;\n}\n","/**\n * executeFactoryJob — the adapter's imperative shell (#52): runs the\n * factory's own milestones (`.sandcastle/main.ts`'s plan → per-ticket\n * implement+review → merge phases) through `hooks`, and turns each\n * milestone boundary into a paid increment via `publisher` (existing,\n * publisher.ts — upload + event publish) and `delivery` (new,\n * factory-job-delivery.ts — encrypt + payment wait).\n *\n * Mirrors `executePush`'s split from `planPush`: `planFactoryJob` is the\n * network-free planning half; this is the half that spends money.\n *\n * Decision 6 (toon-meta#262): work happens BEFORE the packet, so every\n * increment's artifact is produced and uploaded before that increment is\n * offered for payment. Decision 7: the schedule is fixed once the quote is\n * published — this never reprices mid-job. Per the issue: \"Stop on\n * non-payment. One unpaid increment and work halts\" — the symmetric\n * protection that neither side is ever exposed for more than one increment.\n */\n\nimport type { Publisher } from './publisher.js';\nimport type { JobDeliveryPort } from './factory-job-delivery.js';\nimport {\n buildIncrementOfferEvent,\n buildQuoteEvent,\n buildResultEvent,\n type FactoryJobOutcome,\n type FactoryJobRequest,\n type RelayEvent,\n} from './factory-job-events.js';\nimport type { GateResult } from './factory-job-gate.js';\nimport {\n planFactoryJob,\n type FactoryTicket,\n type IncrementSpec,\n} from './factory-job-plan.js';\n\n/** One milestone's deliverable, plus the gate result that ran against it, when one did. */\nexport interface FactoryJobWork {\n artifact: Uint8Array;\n /**\n * The reproducible gate result (#53) for this milestone's work, per\n * `FACTORY.md`'s pinned pipeline (lint/typecheck/test/build). Omitted for\n * `plan` — there is no code to gate against a brief.\n */\n gate?: GateResult;\n}\n\n/** Runs one factory milestone and returns its deliverable, plus that milestone's gate result. */\nexport interface FactoryJobHooks {\n /**\n * Runs the factory's plan phase for `job` (mirrors `.sandcastle/main.ts`'s\n * Phase 1): returns the per-ticket fan-out the implement milestone splits\n * into, plus the plan document itself as increment 1's artifact. No gate\n * result — planning produces no code to lint/typecheck/test/build.\n */\n plan(\n job: FactoryJobRequest\n ): Promise<{ tickets: FactoryTicket[]; artifact: Uint8Array }>;\n /**\n * Runs one ticket's implement sandbox pipeline (mirrors Phase 2's\n * per-issue `sandbox.run`, which ends in the gate per `FACTORY.md`) and\n * returns its deliverable (e.g. the ticket's diff) plus that run's gate\n * result.\n */\n implement(ticket: FactoryTicket): Promise<FactoryJobWork>;\n /**\n * Runs the factory's merge/review phase over every completed ticket\n * (mirrors Phase 3, which re-runs the gate per `merge-prompt.md`) and\n * returns the final deliverable plus that run's gate result.\n */\n review(tickets: FactoryTicket[]): Promise<FactoryJobWork>;\n}\n\nexport interface ExecuteFactoryJobOptions {\n job: FactoryJobRequest;\n /** The original kind:5097 request event, verbatim — carried in the result's `request` tag. */\n requestEvent: RelayEvent;\n hooks: FactoryJobHooks;\n publisher: Publisher;\n delivery: JobDeliveryPort;\n relayUrls: string[];\n}\n\nexport interface FactoryJobExecution {\n outcome: FactoryJobOutcome;\n /** The last increment successfully paid for (0 if the buyer never paid increment 1). */\n reachedIncrement: number;\n totalIncrements: number;\n /** The published kind:6097 result event's id. */\n resultEventId: string;\n}\n\nasync function workFor(\n spec: IncrementSpec,\n tickets: FactoryTicket[],\n planArtifact: Uint8Array,\n hooks: FactoryJobHooks\n): Promise<FactoryJobWork> {\n if (spec.milestone === 'plan') return { artifact: planArtifact };\n if (spec.milestone === 'implement') {\n if (!spec.ticket) {\n throw new Error(\n `internal: implement increment ${spec.n} is missing its ticket`\n );\n }\n return hooks.implement(spec.ticket);\n }\n return hooks.review(tickets);\n}\n\n/**\n * Run a job end to end: plan → quote → one increment per milestone boundary\n * (encrypt, upload, offer, wait for payment) → terminal kind:6097 result.\n * Halts at the first unpaid increment (`abandoned-buyer`) or the first\n * hook failure once work is underway (`abandoned-provider`) — see §5.2 of\n * `docs/factory-job-protocol.md` for the three terminal outcomes.\n *\n * Throws if `hooks.plan` itself fails — with no quote ever published, there\n * is no prior kind:7000 event for a result's `reply` e-tag to reference, so\n * no protocol event can be honestly emitted for that failure.\n */\nexport async function executeFactoryJob(\n options: ExecuteFactoryJobOptions\n): Promise<FactoryJobExecution> {\n const { job, requestEvent, hooks, publisher, delivery, relayUrls } =\n options;\n\n if (!publisher.uploadBlob) {\n throw new Error(\n 'the active publisher cannot upload an encrypted increment artifact ' +\n '(uploadBlob unavailable) — executeFactoryJob requires the standalone publisher'\n );\n }\n const uploadBlob = publisher.uploadBlob.bind(publisher);\n\n const { tickets, artifact: planArtifact } = await hooks.plan(job);\n const schedule = planFactoryJob(job.bidMicroUsdc, tickets);\n const totalIncrements = schedule.length;\n\n const quoteEvent = buildQuoteEvent(job, schedule);\n const quoteReceipt = await publisher.publishEvent(quoteEvent, relayUrls);\n\n let parentEventId = quoteReceipt.eventId;\n let reachedIncrement = 0;\n let finalArtifactTxId: string | undefined;\n\n const haltWith = async (\n outcome: FactoryJobOutcome\n ): Promise<FactoryJobExecution> => {\n const resultEvent = buildResultEvent({\n job,\n requestEvent,\n lastEventId: parentEventId,\n outcome,\n reachedIncrement,\n totalIncrements,\n ...(outcome === 'completed' && finalArtifactTxId\n ? { finalArtifact: { arweaveTxId: finalArtifactTxId } }\n : {}),\n });\n const resultReceipt = await publisher.publishEvent(\n resultEvent,\n relayUrls\n );\n return {\n outcome,\n reachedIncrement,\n totalIncrements,\n resultEventId: resultReceipt.eventId,\n };\n };\n\n for (const spec of schedule) {\n let work: FactoryJobWork;\n try {\n work = await workFor(spec, tickets, planArtifact, hooks);\n } catch {\n // Provider-side failure mid-job: stop where we are, no renegotiation.\n return haltWith('abandoned-provider');\n }\n\n const encrypted = await delivery.encryptArtifact(work.artifact);\n const uploadReceipt = await uploadBlob({\n body: Buffer.from(encrypted.ciphertext),\n contentType: 'application/octet-stream',\n ...(job.repo ? { repoId: job.repo } : {}),\n });\n\n const offerEvent = buildIncrementOfferEvent({\n job,\n parentEventId,\n increment: spec,\n artifact: {\n arweaveTxId: uploadReceipt.txId,\n ciphertextSha256: encrypted.ciphertextSha256,\n conditionHex: encrypted.conditionHex,\n },\n ...(work.gate ? { gate: work.gate } : {}),\n });\n const offerReceipt = await publisher.publishEvent(offerEvent, relayUrls);\n\n const paid = await delivery.waitForPayment({\n offerEventId: offerReceipt.eventId,\n conditionHex: encrypted.conditionHex,\n priceUsdc: spec.priceUsdc,\n });\n if (!paid) {\n parentEventId = offerReceipt.eventId;\n return haltWith('abandoned-buyer');\n }\n\n reachedIncrement = spec.n;\n parentEventId = offerReceipt.eventId;\n if (spec.milestone === 'review') finalArtifactTxId = uploadReceipt.txId;\n }\n\n return haltWith('completed');\n}\n","/**\n * The Runner seam (rig#125) — the ONE new interface the relay-native CI\n * layer introduces. The coordinator (./coordinator.ts) materializes a commit,\n * picks a workflow, decides the secret set, and hands all of that to a\n * Runner; the Runner executes the jobs and reports per-job conclusions, exit\n * codes, timings, logs and artifact files. Nothing about relays, payments,\n * or NIP-C1 events crosses this boundary in either direction.\n *\n * Two implementations exist today:\n *\n * - {@link FakeRunner} (here): scripted outcomes for coordinator tests.\n * - `ActRunner` (./act-runner.ts): shells out to `act` against the host\n * Docker daemon — the first-slice \"coordinator is its own runner\" shape\n * ngit's reference coordinator also uses.\n *\n * A later TOON-lease runner (rent a workload from a TOON Network provider,\n * blocked on TOON_Network Milestone 1) is a third class behind the SAME\n * interface; that is the whole point of the seam.\n */\n\nimport type { CiConclusion, CiTriggerContext } from './nip-c1-events.js';\n\n/** One artifact file a job produced (one entry per FILE, never an archive). */\nexport interface RunnerArtifact {\n /** Absolute path of the file on the coordinator host. */\n path: string;\n /** The file's path within the artifact (mirrors upload-artifact). */\n filename: string;\n /** The artifact name grouping related files. */\n name: string;\n}\n\n/** The outcome of one job within a run. */\nexport interface RunnerJobResult {\n /** Job id as declared in the workflow file. */\n jobId: string;\n /** Human-readable job name, when the workflow declares one. */\n name?: string;\n conclusion: CiConclusion;\n exitCode?: number;\n /** Unix seconds. */\n startedAt: number;\n /** Unix seconds. */\n finishedAt: number;\n /** The job's complete log output. */\n log: string;\n artifacts: RunnerArtifact[];\n}\n\n/** The outcome of one workflow run. */\nexport interface RunnerRunResult {\n /** Combined conclusion — the execution backend's own verdict, passed through. */\n conclusion: CiConclusion;\n jobs: RunnerJobResult[];\n startedAt: number;\n finishedAt: number;\n}\n\n/** Everything a Runner needs to execute one workflow once. */\nexport interface RunnerRequest {\n /** A real git repository, checked out at `trigger.commit`. */\n checkoutDir: string;\n /** Workflow file path relative to `checkoutDir`, plus its content SHA-256. */\n workflow: { path: string; sha256: string };\n trigger: CiTriggerContext;\n /** Secrets to inject; `{}` for runs the coordinator did not authorize secrets for. */\n secrets: Record<string, string>;\n /** Extra environment for the jobs. */\n env?: Record<string, string>;\n /** Wall-clock budget for the whole run; exceeding it concludes `timed_out`. */\n timeoutMs: number;\n /** Streaming log sink (per job). */\n onLog?: (jobId: string, chunk: string) => void;\n /** Cancels the run (concludes `cancelled`). */\n signal?: AbortSignal;\n}\n\n/** Executes one workflow run. See the module header. */\nexport interface Runner {\n /** Runner family, as advertised in the NIP-C1 `W` tag. */\n readonly family: 'act';\n /** Accepted selectors for the family (`R` = `act:<selector>`). */\n readonly selectors: string[];\n run(request: RunnerRequest): Promise<RunnerRunResult>;\n}\n\n// ---------------------------------------------------------------------------\n// FakeRunner — scripted outcomes for tests\n// ---------------------------------------------------------------------------\n\nexport type FakeRunnerScript = (\n request: RunnerRequest\n) => RunnerRunResult | Promise<RunnerRunResult>;\n\n/** The default script: one successful `build` job with a short log. */\nexport function defaultFakeRunResult(request: RunnerRequest): RunnerRunResult {\n const now = Math.floor(Date.now() / 1000);\n return {\n conclusion: 'success',\n startedAt: now,\n finishedAt: now,\n jobs: [\n {\n jobId: 'build',\n name: 'build',\n conclusion: 'success',\n exitCode: 0,\n startedAt: now,\n finishedAt: now,\n log: `fake runner: ran ${request.workflow.path} at ${request.trigger.commit}\\n`,\n artifacts: [],\n },\n ],\n };\n}\n\n/**\n * A Runner that returns scripted outcomes and records every request, so a\n * coordinator test can assert on what was asked (secrets, timeout, commit)\n * and drive the publish sequence from a chosen result. Never touches Docker.\n */\nexport class FakeRunner implements Runner {\n readonly family = 'act' as const;\n readonly selectors = ['ubuntu-latest'];\n /** Every request, in call order. */\n readonly requests: RunnerRequest[] = [];\n\n constructor(\n private readonly script: FakeRunnerScript = defaultFakeRunResult\n ) {}\n\n async run(request: RunnerRequest): Promise<RunnerRunResult> {\n this.requests.push(request);\n const result = await this.script(request);\n if (request.onLog) {\n for (const job of result.jobs) request.onLog(job.jobId, job.log);\n }\n return result;\n }\n}\n"],"mappings":"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","names":[]}
@@ -23,10 +23,11 @@ interface GitObject {
23
23
  body: Buffer;
24
24
  }
25
25
  /**
26
- * Maximum uploadable git object body size: 95KB safety margin under the
27
- * 100KB free tier (R10-005). Larger objects are a hard error in v1.
26
+ * @deprecated Renamed to {@link FREE_TIER_MAX_ITEM_BYTES}, and no longer a
27
+ * maximum: objects above it upload as paid writes rather than erroring. Kept
28
+ * as an alias so the 4.x export keeps resolving.
28
29
  */
29
- declare const MAX_OBJECT_SIZE: number;
30
+ declare const MAX_OBJECT_SIZE = 107520;
30
31
  /**
31
32
  * Wrap a raw object body in the git envelope (`<type> <size>\0`) and compute
32
33
  * its SHA-1. This is exactly what `git hash-object -t <type>` does.
@@ -1,4 +1,4 @@
1
- import { P as Publisher, F as FeeRates, G as GitObjectUpload, U as UploadReceipt, B as BlobUpload, S as StoreJobRequest, a as StoreJobResponse, b as UnsignedEvent, c as PublishReceipt } from '../publisher-DMJ06cuL.js';
1
+ import { P as Publisher, F as FeeRates, G as GitObjectUpload, U as UploadReceipt, B as BlobUpload, S as StoreJobRequest, a as StoreJobResponse, b as UnsignedEvent, c as PublishReceipt } from '../publisher-Cf6tKnZ3.js';
2
2
  import '@toon-protocol/core/nip34';
3
3
 
4
4
  /**
@@ -2,28 +2,29 @@ import {
2
2
  ConnectorPublishError,
3
3
  ConnectorPublisher,
4
4
  extractArweaveTxId
5
- } from "../chunk-CDSFVZDV.js";
5
+ } from "../chunk-UE5TAFEU.js";
6
6
  import {
7
7
  ChannelMapCorruptError,
8
8
  ChannelMapStore,
9
+ RIG_CHANNEL_MAP_FILENAME,
10
+ channelStatus,
11
+ counterpartyMatch,
12
+ recordKey,
13
+ resolveChannelPaths,
14
+ sameSettlementAddress
15
+ } from "../chunk-XCLUB2CY.js";
16
+ import {
9
17
  DEFAULT_DAEMON_PORT,
10
18
  DaemonIdentityConflictError,
11
19
  MAX_ACCOUNT_INDEX,
12
20
  NonceLock,
13
- RIG_CHANNEL_MAP_FILENAME,
14
21
  StandaloneLockError,
15
- channelStatus,
16
22
  checkDaemonIdentity,
17
- counterpartyMatch,
18
23
  defaultDaemonPort,
19
24
  defaultLockDir,
20
25
  deriveNostrKeyFromMnemonic,
21
- nostrDerivationPath,
22
- recordKey,
23
- resolveChannelPaths,
24
- sameSettlementAddress
25
- } from "../chunk-YZRR2UO6.js";
26
- import "../chunk-GVXBUL3C.js";
26
+ nostrDerivationPath
27
+ } from "../chunk-U5A7MHBP.js";
27
28
  export {
28
29
  ChannelMapCorruptError,
29
30
  ChannelMapStore,
@@ -1,21 +1,20 @@
1
- import {
2
- resolveIdentity
3
- } from "./chunk-LTFMPOHU.js";
4
- import {
5
- fetchRemoteState
6
- } from "./chunk-NCEMQUPA.js";
7
1
  import {
8
2
  ConnectorPublisher
9
- } from "./chunk-CDSFVZDV.js";
3
+ } from "./chunk-UE5TAFEU.js";
4
+ import {
5
+ fetchRemoteState,
6
+ resolveIdentity
7
+ } from "./chunk-GWA3CSUR.js";
10
8
  import {
11
9
  ChannelMapStore,
10
+ RIG_CHANNEL_MAP_FILENAME
11
+ } from "./chunk-XCLUB2CY.js";
12
+ import {
12
13
  NonceLock,
13
- RIG_CHANNEL_MAP_FILENAME,
14
14
  checkDaemonIdentity,
15
15
  deriveNostrKeyFromMnemonic,
16
16
  standaloneForced
17
- } from "./chunk-YZRR2UO6.js";
18
- import "./chunk-GVXBUL3C.js";
17
+ } from "./chunk-U5A7MHBP.js";
19
18
 
20
19
  // src/cli/standalone-mode.ts
21
20
  import { readFileSync } from "fs";
@@ -387,4 +386,4 @@ export {
387
386
  resolveConnectorSettings,
388
387
  resolveDestinations
389
388
  };
390
- //# sourceMappingURL=standalone-mode-KTYRTUS6.js.map
389
+ //# sourceMappingURL=standalone-mode-JIEECDTI.js.map