@toon-protocol/rig 4.6.0 → 5.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.
- package/README.md +118 -25
- package/dist/{chunk-X232W4TG.js → chunk-2TG4YHEC.js} +89 -59
- package/dist/chunk-2TG4YHEC.js.map +1 -0
- package/dist/{chunk-DCGV6ALH.js → chunk-4WVH2ITA.js} +158 -9
- package/dist/chunk-4WVH2ITA.js.map +1 -0
- package/dist/{chunk-U5A7MHBP.js → chunk-7VIVQHRG.js} +1 -1
- package/dist/chunk-7VIVQHRG.js.map +1 -0
- package/dist/chunk-CHVOXCDV.js +0 -0
- package/dist/chunk-CHVOXCDV.js.map +1 -0
- package/dist/{chunk-XCLUB2CY.js → chunk-LMZSAFAB.js} +44 -15
- package/dist/chunk-LMZSAFAB.js.map +1 -0
- package/dist/{chunk-JBFBD7HV.js → chunk-QKL53YZS.js} +7 -3
- package/dist/chunk-QKL53YZS.js.map +1 -0
- package/dist/{chunk-UE5TAFEU.js → chunk-ZQGXFFBP.js} +4 -4
- package/dist/chunk-ZQGXFFBP.js.map +1 -0
- package/dist/{ci-serve-AYFZOY4O.js → ci-serve-BU7NDT7O.js} +152 -26
- package/dist/ci-serve-BU7NDT7O.js.map +1 -0
- package/dist/cli/rig.js +6 -5
- package/dist/cli/rig.js.map +1 -1
- package/dist/index.d.ts +117 -4
- package/dist/index.js +40 -7
- package/dist/index.js.map +1 -1
- package/dist/standalone/index.d.ts +109 -74
- package/dist/standalone/index.js +13 -3
- package/dist/{standalone-mode-UQSP4677.js → standalone-mode-AT4S5EZG.js} +148 -50
- package/dist/standalone-mode-AT4S5EZG.js.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-DCGV6ALH.js.map +0 -1
- package/dist/chunk-JBFBD7HV.js.map +0 -1
- package/dist/chunk-U5A7MHBP.js.map +0 -1
- package/dist/chunk-UE5TAFEU.js.map +0 -1
- package/dist/chunk-X232W4TG.js.map +0 -1
- package/dist/chunk-XCLUB2CY.js.map +0 -1
- package/dist/ci-serve-AYFZOY4O.js.map +0 -1
- package/dist/standalone-mode-UQSP4677.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/standalone/connector-publisher.ts"],"sourcesContent":["/**\n * The paid write path on `@toon-protocol/client` 4.x: a {@link Publisher}\n * that pays a TOON connector per request.\n *\n * Everything a paid command needs comes from one connector URL: the client\n * reads the node's own `GET /ilp` (addresses, prices, x402 batch-settlement\n * terms, sealing key: connector ADR 0050), opens or resumes an x402 channel,\n * and pays each request with a signed voucher for the running total\n * (connector ADR 0075). There is no relay to discover peers\n * on (kind:10032 was removed by ADR 0046) and no topology to negotiate: a\n * destination is a route the node prices, and `send()` pays what the route\n * costs — including the per-kibibyte part of an ADR 0065 schedule, which the\n * client works out from the sealed payload so this publisher never has to.\n *\n * Three request shapes, all ordinary paid POSTs:\n *\n * 1. `publishEvent` — a NIP-34 event signed with the OWNER's Nostr key,\n * sent as `{ event }` to the publish route; the relay answers\n * `{ eventId, storedAt, payment? }`.\n * 2. `uploadGitObject` / `uploadBlob` — a kind:5094 store job carrying the\n * bytes base64 in an `i` tag; the store answers the Arweave tx id.\n * 3. `submitStoreJob` — a kind:5095 / 5096 job with `param` tags; the\n * DVM's answer comes back as data, refusal included (ADR 0020: an answer\n * is an answer, and the payer was charged for it).\n *\n * The store route may terminate on a different node than the one addressed\n * (the edge forwards it). Then the payload must be sealed to THAT node's\n * identity — `sealTo` — and, when the store node holds its own channel, paid\n * through its own client. Both are options here; the common single-node case\n * needs neither.\n */\n\nimport { finalizeEvent } from 'nostr-tools/pure';\nimport { contentTypeForPath, DEFAULT_CONTENT_TYPE } from '../mime.js';\nimport type { UnsignedEvent } from '../nip34-events.js';\nimport {\n uploadChargeFor,\n type BlobUpload,\n type FeeRates,\n type GitObjectUpload,\n type PublishReceipt,\n type Publisher,\n type StoreJobRequest,\n type StoreJobResponse,\n type UploadReceipt,\n} from '../publisher.js';\n\n// ---------------------------------------------------------------------------\n// The slice of ToonClient this publisher drives (structural, so tests mock it)\n// ---------------------------------------------------------------------------\n\n/** A paid request, as `ToonClient.send` takes it. */\nexport interface PaidRequest {\n method?: string;\n target?: string;\n headers?: Record<string, string> | [string, string][];\n body?: string | Uint8Array | object;\n}\n\nexport interface PaidSendOptions {\n /** The terminating connector's `GET /ilp` URL or raw key, on a forwarded route. */\n sealTo?: string | Uint8Array;\n timeoutMs?: number;\n}\n\nexport interface PaidFulfilled {\n fulfilled: true;\n status: number;\n body: Uint8Array;\n /** What this request paid; absent on a route priced at zero. */\n claim?: { amount: bigint };\n}\n\nexport interface PaidRefused {\n fulfilled: false;\n code: string;\n message: string;\n refusedBy?: string;\n}\n\nexport type PaidSendResult = PaidFulfilled | PaidRefused;\n\n/** The route terms a connector publishes: a base price and an optional slope. */\nexport interface RouteTerms {\n price: bigint;\n pricePerKib?: bigint;\n}\n\n/** The slice of `ToonClient` (4.x) this publisher needs. */\nexport interface PaidClientLike {\n send(\n destination: string,\n request: PaidRequest,\n options?: PaidSendOptions\n ): Promise<PaidSendResult>;\n /** The node's terms for a destination, or `null` when it prices no such route. */\n routePrice(destination: string): Promise<RouteTerms | null>;\n}\n\n/** One leg of the write path: who pays, where to, and whom to seal to. */\nexport interface PaidLeg {\n client: PaidClientLike;\n destination: string;\n sealTo?: string | Uint8Array;\n}\n\nexport interface ConnectorPublisherOptions {\n /** Pays the publish route and signs nothing — the Nostr key below signs. */\n publish: PaidLeg;\n /** Pays the store route (often the same client as `publish`). */\n store: PaidLeg;\n /** The owner's Nostr secret key: signs every event this publisher sends. */\n nostrSecretKey: Uint8Array;\n /**\n * A configured per-event fee. Informational floor only: the connector's\n * published price for the publish route wins when it is higher, and the\n * client pays that price whatever this says.\n */\n eventFee?: bigint;\n /** Per-packet timeout for uploads (the store's Arweave round trip). */\n uploadTimeoutMs?: number;\n warn?(line: string): void;\n}\n\nexport class ConnectorPublishError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConnectorPublishError';\n }\n}\n\nconst ARWEAVE_TX_ID_REGEX = /^[A-Za-z0-9_-]{43}$/;\n\nfunction decodeUtf8(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes);\n}\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nfunction describeRefusal(result: PaidRefused): string {\n const by = result.refusedBy ? ` by the ${result.refusedBy}` : '';\n return `${result.code}${by}: ${result.message}`;\n}\n\n/**\n * Read the Arweave tx id out of a store's kind:5094 answer.\n *\n * The store answers one of two ways — `{ txId }` as JSON, or the bare id\n * base64 in `data` — and refuses with `{ accept: false, error }`. A non-2xx\n * status is a refusal too; both are thrown, because an upload without an id\n * is an upload that did not happen (even though the packet was paid for).\n */\nexport function extractArweaveTxId(status: number, body: Uint8Array): string {\n const text = decodeUtf8(body);\n if (status < 200 || status >= 300) {\n throw new ConnectorPublishError(\n `the store answered HTTP ${String(status)}${text ? ` - ${text}` : ''}`\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n if (ARWEAVE_TX_ID_REGEX.test(text.trim())) return text.trim();\n throw new ConnectorPublishError(\n `the store's answer was not valid JSON: ${JSON.stringify(text.slice(0, 200))}`\n );\n }\n const answer = (parsed ?? {}) as {\n accept?: unknown;\n error?: unknown;\n txId?: unknown;\n data?: unknown;\n result?: unknown;\n };\n if (answer.accept === false) {\n const reason = typeof answer.error === 'string' ? `: ${answer.error}` : '';\n throw new ConnectorPublishError(\n `upload refused by the store (accept:false)${reason}`\n );\n }\n if (\n typeof answer.txId === 'string' &&\n ARWEAVE_TX_ID_REGEX.test(answer.txId)\n ) {\n return answer.txId;\n }\n const nested = answer.result as { txId?: unknown } | undefined;\n if (\n nested &&\n typeof nested.txId === 'string' &&\n ARWEAVE_TX_ID_REGEX.test(nested.txId)\n ) {\n return nested.txId;\n }\n if (typeof answer.data === 'string' && answer.data.length > 0) {\n const decoded = Buffer.from(answer.data, 'base64').toString('utf8');\n if (ARWEAVE_TX_ID_REGEX.test(decoded)) return decoded;\n }\n throw new ConnectorPublishError(\n `the store's answer carried no Arweave tx id: ${JSON.stringify(text.slice(0, 200))}`\n );\n}\n\n// ---------------------------------------------------------------------------\n// The publisher\n// ---------------------------------------------------------------------------\n\nexport class ConnectorPublisher implements Publisher {\n private readonly publish: PaidLeg;\n private readonly store: PaidLeg;\n private readonly nostrSecretKey: Uint8Array;\n private readonly configuredEventFee: bigint;\n private readonly uploadTimeoutMs: number | undefined;\n private readonly warn: (line: string) => void;\n private rates: Promise<FeeRates> | undefined;\n\n constructor(options: ConnectorPublisherOptions) {\n this.publish = options.publish;\n this.store = options.store;\n this.nostrSecretKey = options.nostrSecretKey;\n this.configuredEventFee = options.eventFee ?? 0n;\n this.uploadTimeoutMs = options.uploadTimeoutMs;\n this.warn = options.warn ?? (() => undefined);\n }\n\n /** Where events go, and where objects go. */\n get destinations(): { publish: string; store: string } {\n return { publish: this.publish.destination, store: this.store.destination };\n }\n\n /**\n * The two routes' published terms, read once per publisher. A route the\n * node does not price is a configuration error and says which knob to fix.\n */\n getFeeRates(): Promise<FeeRates> {\n this.rates ??= this.readFeeRates();\n return this.rates;\n }\n\n private async readFeeRates(): Promise<FeeRates> {\n const [publishTerms, storeTerms] = await Promise.all([\n this.publish.client.routePrice(this.publish.destination),\n this.store.client.routePrice(this.store.destination),\n ]);\n if (!publishTerms) {\n throw new ConnectorPublishError(\n `the connector prices no route for the publish destination ${this.publish.destination} ` +\n '(set publishDestination / TOON_CLIENT_PUBLISH_DESTINATION to one of its GET /ilp routes)'\n );\n }\n if (!storeTerms) {\n throw new ConnectorPublishError(\n `the connector prices no route for the store destination ${this.store.destination} ` +\n '(set storeDestination / TOON_CLIENT_STORE_DESTINATION to one of its GET /ilp routes)'\n );\n }\n if (\n publishTerms.pricePerKib !== undefined &&\n publishTerms.pricePerKib > 0n\n ) {\n this.warn(\n `rig: the publish route ${this.publish.destination} meters by size ` +\n `(${String(publishTerms.pricePerKib)}/KiB); the per-event estimate shows its base price only`\n );\n }\n const eventFee =\n this.configuredEventFee > publishTerms.price\n ? this.configuredEventFee\n : publishTerms.price;\n return {\n uploadFee: storeTerms.price,\n ...(storeTerms.pricePerKib !== undefined && storeTerms.pricePerKib > 0n\n ? { uploadPerKib: storeTerms.pricePerKib }\n : {}),\n eventFee,\n };\n }\n\n private sign(template: UnsignedEvent) {\n return finalizeEvent(\n {\n kind: template.kind,\n content: template.content,\n tags: template.tags,\n created_at: template.created_at,\n },\n this.nostrSecretKey\n );\n }\n\n /** One paid kind:5094 write; returns the tx id and what the voucher spent. */\n private async storeWrite(\n tags: string[][],\n bodyBytes: number,\n what: string\n ): Promise<UploadReceipt> {\n const rates = await this.getFeeRates();\n const estimate = uploadChargeFor(rates, bodyBytes);\n const event = this.sign({\n kind: 5094,\n content: '',\n created_at: nowSeconds(),\n tags: [...tags, ['bid', estimate.toString(), 'usdc']],\n });\n const result = await this.store.client.send(\n this.store.destination,\n { body: { event } },\n {\n ...(this.store.sealTo !== undefined\n ? { sealTo: this.store.sealTo }\n : {}),\n ...(this.uploadTimeoutMs !== undefined\n ? { timeoutMs: this.uploadTimeoutMs }\n : {}),\n }\n );\n if (!result.fulfilled) {\n throw new ConnectorPublishError(\n `${what} rejected — ${describeRefusal(result)}`\n );\n }\n let txId: string;\n try {\n txId = extractArweaveTxId(result.status, result.body);\n } catch (err) {\n throw new ConnectorPublishError(\n `${what}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n return { txId, feePaid: result.claim?.amount ?? estimate };\n }\n\n async uploadGitObject(upload: GitObjectUpload): Promise<UploadReceipt> {\n // No size gate (#102): the store route prices per KiB, so a body above\n // FREE_TIER_MAX_ITEM_BYTES is a paid upload rather than a refusal.\n const contentType =\n upload.type === 'blob'\n ? contentTypeForPath(upload.path)\n : DEFAULT_CONTENT_TYPE;\n return this.storeWrite(\n [\n ['i', upload.body.toString('base64'), 'blob'],\n ['output', contentType],\n ['Git-SHA', upload.sha],\n ['Git-Type', upload.type],\n ['Repo', upload.repoId],\n ],\n upload.body.length,\n `git-object upload (${upload.sha})`\n );\n }\n\n async uploadBlob(upload: BlobUpload): Promise<UploadReceipt> {\n // No size gate (#102), matching uploadGitObject: the store route prices\n // per KiB, so a blob above FREE_TIER_MAX_ITEM_BYTES costs more rather than\n // being refused.\n return this.storeWrite(\n [\n ['i', upload.body.toString('base64'), 'blob'],\n ['output', upload.contentType],\n ...(upload.repoId ? [['Repo', upload.repoId]] : []),\n ],\n upload.body.length,\n 'blob upload'\n );\n }\n\n /**\n * One NIP-90 job to the store node's DVM, over the paid path. A refusal is\n * returned, not thrown: `accept: false` and a non-2xx status are the\n * answer's content, and the payer was charged for the answer (ADR 0020).\n */\n async submitStoreJob(request: StoreJobRequest): Promise<StoreJobResponse> {\n const rates = await this.getFeeRates();\n const fee = rates.uploadFee;\n const event = this.sign({\n kind: request.kind,\n content: '',\n created_at: nowSeconds(),\n tags: [\n ...request.params.map(([key, value]) => ['param', key, value]),\n ['bid', fee.toString(), 'usdc'],\n ],\n });\n const result = await this.store.client.send(\n this.store.destination,\n { body: { event } },\n this.store.sealTo !== undefined ? { sealTo: this.store.sealTo } : {}\n );\n if (!result.fulfilled) {\n throw new ConnectorPublishError(\n `kind:${String(request.kind)} job rejected — ${describeRefusal(result)}`\n );\n }\n const text = decodeUtf8(result.body);\n let body: {\n accept?: unknown;\n code?: unknown;\n message?: unknown;\n result?: unknown;\n };\n try {\n body = JSON.parse(text) as typeof body;\n } catch {\n throw new ConnectorPublishError(\n `kind:${String(request.kind)} job answer was not valid JSON ` +\n `(HTTP ${String(result.status)}): ${JSON.stringify(text.slice(0, 200))}`\n );\n }\n return {\n status: result.status,\n accept: body.accept === true,\n ...(typeof body.code === 'string' ? { code: body.code } : {}),\n ...(typeof body.message === 'string' ? { message: body.message } : {}),\n ...(body.result !== null && typeof body.result === 'object'\n ? { result: body.result as Record<string, unknown> }\n : {}),\n feePaid: result.claim?.amount ?? fee,\n };\n }\n\n /**\n * Sign the event with the owner's key and pay to publish it. `relayUrls`\n * is the seam's plural surface: this publisher routes to one configured\n * destination, so more than one relay is refused rather than half-done.\n */\n async publishEvent(\n event: UnsignedEvent,\n relayUrls: string[]\n ): Promise<PublishReceipt> {\n if (relayUrls.length > 1) {\n throw new ConnectorPublishError(\n `multi-relay publish is not supported (got ${String(relayUrls.length)} relays) — ` +\n 'the connector publisher routes to a single publish destination'\n );\n }\n const rates = await this.getFeeRates();\n const signed = this.sign(event);\n const result = await this.publish.client.send(\n this.publish.destination,\n { body: { event: signed } },\n this.publish.sealTo !== undefined ? { sealTo: this.publish.sealTo } : {}\n );\n if (!result.fulfilled) {\n throw new ConnectorPublishError(\n `publish rejected (kind ${String(event.kind)}) — ${describeRefusal(result)}`\n );\n }\n if (result.status < 200 || result.status >= 300) {\n throw new ConnectorPublishError(\n `publish (kind ${String(event.kind)}): the relay answered HTTP ${String(result.status)} - ${decodeUtf8(result.body)}`\n );\n }\n let eventId = signed.id;\n try {\n const answer = JSON.parse(decodeUtf8(result.body)) as {\n eventId?: unknown;\n };\n if (typeof answer.eventId === 'string' && answer.eventId)\n eventId = answer.eventId;\n } catch {\n // A 2xx without a JSON body still stored the event we signed.\n }\n return { eventId, feePaid: result.claim?.amount ?? rates.eventFee };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
PREFERRED_GATEWAY,
|
|
3
2
|
WS_URL_RE,
|
|
4
3
|
identityReport,
|
|
5
4
|
loadPaidSession,
|
|
6
5
|
renderIdentityLine,
|
|
7
6
|
rigVersion,
|
|
8
7
|
withForcedStandalone
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-2TG4YHEC.js";
|
|
10
9
|
import {
|
|
10
|
+
CI_LIVE_LOG_TAIL_INTERVAL_MS,
|
|
11
11
|
CI_MANUAL_TRIGGER_KIND,
|
|
12
|
+
CI_RUNNER_CHANNEL_KEY,
|
|
12
13
|
CI_SECRET_UPDATE_KIND,
|
|
13
14
|
CI_SERVICE_REQUEST_KIND,
|
|
14
15
|
CI_SERVICE_STOP_KIND,
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
applySecretUpdate,
|
|
17
18
|
buildCiAdvertisement,
|
|
18
19
|
buildCiJobResult,
|
|
20
|
+
buildCiLiveLogTail,
|
|
19
21
|
buildCiWorkflowProgress,
|
|
20
22
|
buildCiWorkflowResult,
|
|
21
23
|
collectRepoObjects,
|
|
@@ -26,6 +28,8 @@ import {
|
|
|
26
28
|
emitCliError,
|
|
27
29
|
generateSecretsKey,
|
|
28
30
|
hexToNpub,
|
|
31
|
+
liveLogTailBudget,
|
|
32
|
+
liveLogTailEventBudget,
|
|
29
33
|
matchesPullRequest,
|
|
30
34
|
matchesPush,
|
|
31
35
|
missingObjectsMessage,
|
|
@@ -39,27 +43,33 @@ import {
|
|
|
39
43
|
repoAddress,
|
|
40
44
|
runGit,
|
|
41
45
|
selectServiceRequests,
|
|
46
|
+
sliceLogTail,
|
|
42
47
|
validateSecretUpdate,
|
|
43
48
|
writeGitObjects
|
|
44
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-4WVH2ITA.js";
|
|
50
|
+
import {
|
|
51
|
+
PREFERRED_GATEWAY,
|
|
52
|
+
configuredGateway,
|
|
53
|
+
readGatewaysFor,
|
|
54
|
+
shaResolverFor
|
|
55
|
+
} from "./chunk-CHVOXCDV.js";
|
|
45
56
|
import {
|
|
46
|
-
ARWEAVE_GATEWAYS,
|
|
47
57
|
REPOSITORY_STATE_KIND,
|
|
48
58
|
authorizedStatusAuthors,
|
|
49
59
|
defaultWebSocketFactory,
|
|
50
60
|
fetchRemoteState,
|
|
51
61
|
parseStateRefTags,
|
|
52
62
|
queryRelay
|
|
53
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-QKL53YZS.js";
|
|
54
64
|
import {
|
|
55
65
|
ChannelMapStore,
|
|
56
66
|
channelStatus,
|
|
57
67
|
contentTypeForPath,
|
|
58
68
|
resolveChannelPaths
|
|
59
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-LMZSAFAB.js";
|
|
60
70
|
import {
|
|
61
71
|
uploadChargeFor
|
|
62
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-7VIVQHRG.js";
|
|
63
73
|
|
|
64
74
|
// src/cli/ci-serve.ts
|
|
65
75
|
import { join as join4 } from "path";
|
|
@@ -431,7 +441,11 @@ var ActRunner = class {
|
|
|
431
441
|
const line = buffered.slice(0, nl);
|
|
432
442
|
buffered = buffered.slice(nl + 1);
|
|
433
443
|
const parsedLine = parseActJsonLine(line);
|
|
434
|
-
if (!parsedLine)
|
|
444
|
+
if (!parsedLine) {
|
|
445
|
+
request.onLog?.(CI_RUNNER_CHANNEL_KEY, `${line}
|
|
446
|
+
`);
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
435
449
|
lines.push(parsedLine);
|
|
436
450
|
if (request.onLog && parsedLine.exitCode === void 0) {
|
|
437
451
|
request.onLog(
|
|
@@ -444,7 +458,11 @@ var ActRunner = class {
|
|
|
444
458
|
});
|
|
445
459
|
stream.on("end", () => {
|
|
446
460
|
const parsedLine = parseActJsonLine(buffered);
|
|
447
|
-
if (parsedLine)
|
|
461
|
+
if (parsedLine) {
|
|
462
|
+
lines.push(parsedLine);
|
|
463
|
+
} else if (buffered !== "") {
|
|
464
|
+
request.onLog?.(CI_RUNNER_CHANNEL_KEY, buffered);
|
|
465
|
+
}
|
|
448
466
|
});
|
|
449
467
|
};
|
|
450
468
|
consume(child.stdout);
|
|
@@ -463,7 +481,7 @@ var ActRunner = class {
|
|
|
463
481
|
startedAtMs,
|
|
464
482
|
(note) => {
|
|
465
483
|
this.options.warn?.(note);
|
|
466
|
-
request.onLog?.(
|
|
484
|
+
request.onLog?.(CI_RUNNER_CHANNEL_KEY, `${note}
|
|
467
485
|
`);
|
|
468
486
|
}
|
|
469
487
|
);
|
|
@@ -890,6 +908,10 @@ var DEFAULT_RUN_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
|
890
908
|
var DEFAULT_ADVERTISEMENT_TTL = 30 * 60;
|
|
891
909
|
var RENEW_FRACTION = 5 / 6;
|
|
892
910
|
var LOG_TAIL_BYTES = 4096;
|
|
911
|
+
var LOG_UPLOAD_CAP_BYTES = 1024 * 1024;
|
|
912
|
+
var LOG_UPLOAD_HEAD_BYTES = 512 * 1024;
|
|
913
|
+
var LOG_UPLOAD_MARKER_BUDGET_BYTES = 256;
|
|
914
|
+
var LOG_UPLOAD_TAIL_BYTES = 512 * 1024 - LOG_UPLOAD_MARKER_BUDGET_BYTES;
|
|
893
915
|
var SECRETS_KEY_MAX_AGE = 86400;
|
|
894
916
|
function nowSeconds() {
|
|
895
917
|
return Math.floor(Date.now() / 1e3);
|
|
@@ -903,6 +925,20 @@ function logTail(log) {
|
|
|
903
925
|
omitted: bytes.byteLength - LOG_TAIL_BYTES
|
|
904
926
|
};
|
|
905
927
|
}
|
|
928
|
+
function boundLogForUpload(log) {
|
|
929
|
+
const bytes = Buffer.from(log, "utf-8");
|
|
930
|
+
if (bytes.byteLength <= LOG_UPLOAD_CAP_BYTES) return bytes;
|
|
931
|
+
const head = bytes.subarray(0, LOG_UPLOAD_HEAD_BYTES);
|
|
932
|
+
const tail = bytes.subarray(bytes.byteLength - LOG_UPLOAD_TAIL_BYTES);
|
|
933
|
+
const omitted = bytes.byteLength - head.byteLength - tail.byteLength;
|
|
934
|
+
const marker = Buffer.from(
|
|
935
|
+
`
|
|
936
|
+
[... ${omitted} bytes omitted (log capped at ${LOG_UPLOAD_CAP_BYTES} bytes) ...]
|
|
937
|
+
`,
|
|
938
|
+
"utf-8"
|
|
939
|
+
);
|
|
940
|
+
return Buffer.concat([head, marker, tail]);
|
|
941
|
+
}
|
|
906
942
|
function gatewayRawUrl(gatewayUrl, txId) {
|
|
907
943
|
return `${gatewayUrl.replace(/\/+$/, "")}/raw/${txId}`;
|
|
908
944
|
}
|
|
@@ -1210,6 +1246,62 @@ async function startCoordinator(opts) {
|
|
|
1210
1246
|
);
|
|
1211
1247
|
};
|
|
1212
1248
|
scheduleRenew();
|
|
1249
|
+
const secretValues = Object.values(run.secrets);
|
|
1250
|
+
const streamed = /* @__PURE__ */ new Map();
|
|
1251
|
+
const finishedJobs = /* @__PURE__ */ new Set();
|
|
1252
|
+
let tailClosed = false;
|
|
1253
|
+
const recordLog = (jobId, chunk) => {
|
|
1254
|
+
if (tailClosed || chunk === "") return;
|
|
1255
|
+
streamed.set(jobId, (streamed.get(jobId) ?? "") + chunk);
|
|
1256
|
+
};
|
|
1257
|
+
const publishLiveLogTail = async () => {
|
|
1258
|
+
const live = [];
|
|
1259
|
+
for (const [key, text] of streamed) {
|
|
1260
|
+
if (key !== CI_RUNNER_CHANNEL_KEY && finishedJobs.has(key)) continue;
|
|
1261
|
+
live.push([key, redactSecretValues(text, secretValues)]);
|
|
1262
|
+
}
|
|
1263
|
+
const budget = liveLogTailBudget(live.length);
|
|
1264
|
+
const jobs = [];
|
|
1265
|
+
let runnerChannel;
|
|
1266
|
+
for (const [key, text] of live) {
|
|
1267
|
+
const sliced = sliceLogTail(text, budget);
|
|
1268
|
+
if (key === CI_RUNNER_CHANNEL_KEY) runnerChannel = sliced;
|
|
1269
|
+
else jobs.push({ job: key, ...sliced });
|
|
1270
|
+
}
|
|
1271
|
+
const now2 = clock();
|
|
1272
|
+
try {
|
|
1273
|
+
await publish(
|
|
1274
|
+
buildCiLiveLogTail(
|
|
1275
|
+
{
|
|
1276
|
+
trigger,
|
|
1277
|
+
runId: run.runId,
|
|
1278
|
+
jobs,
|
|
1279
|
+
...runnerChannel ? { runner: runnerChannel } : {},
|
|
1280
|
+
expiresAt: now2 + adTtl
|
|
1281
|
+
},
|
|
1282
|
+
now2
|
|
1283
|
+
)
|
|
1284
|
+
);
|
|
1285
|
+
} catch (err) {
|
|
1286
|
+
log(
|
|
1287
|
+
`[ci] ${label}: live log tail not published: ${redactSecretValues(
|
|
1288
|
+
err instanceof Error ? err.message : String(err),
|
|
1289
|
+
secretValues
|
|
1290
|
+
)}`
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
let tailTimer = null;
|
|
1295
|
+
let tailPublished = false;
|
|
1296
|
+
const scheduleTail = () => {
|
|
1297
|
+
tailTimer = scheduler.setTimeout(() => {
|
|
1298
|
+
if (tailClosed) return;
|
|
1299
|
+
tailPublished = true;
|
|
1300
|
+
void publishLiveLogTail();
|
|
1301
|
+
scheduleTail();
|
|
1302
|
+
}, CI_LIVE_LOG_TAIL_INTERVAL_MS);
|
|
1303
|
+
};
|
|
1304
|
+
scheduleTail();
|
|
1213
1305
|
const abort = new AbortController();
|
|
1214
1306
|
activeAborts.add(abort);
|
|
1215
1307
|
run.abort = abort;
|
|
@@ -1227,6 +1319,7 @@ async function startCoordinator(opts) {
|
|
|
1227
1319
|
trigger,
|
|
1228
1320
|
secrets: run.secrets,
|
|
1229
1321
|
timeoutMs,
|
|
1322
|
+
onLog: recordLog,
|
|
1230
1323
|
signal: abort.signal
|
|
1231
1324
|
}),
|
|
1232
1325
|
new Promise((resolve2) => {
|
|
@@ -1260,19 +1353,19 @@ async function startCoordinator(opts) {
|
|
|
1260
1353
|
} finally {
|
|
1261
1354
|
scheduler.clearTimeout(timeoutHandle);
|
|
1262
1355
|
scheduler.clearTimeout(renewTimer);
|
|
1356
|
+
scheduler.clearTimeout(tailTimer);
|
|
1263
1357
|
activeAborts.delete(abort);
|
|
1264
1358
|
}
|
|
1265
1359
|
let conclusion = result.conclusion;
|
|
1266
1360
|
if (timedOut) conclusion = "timed_out";
|
|
1267
1361
|
else if (run.superseded || stopping && abort.signal.aborted)
|
|
1268
1362
|
conclusion = "cancelled";
|
|
1269
|
-
const secretValues = Object.values(run.secrets);
|
|
1270
1363
|
for (const job of result.jobs) {
|
|
1271
1364
|
const jobLog = redactSecretValues(job.log, secretValues);
|
|
1272
1365
|
const { tail, omitted } = logTail(jobLog);
|
|
1273
1366
|
const logReceipt = await serial.run(
|
|
1274
1367
|
() => uploadBlob({
|
|
1275
|
-
body:
|
|
1368
|
+
body: boundLogForUpload(jobLog),
|
|
1276
1369
|
contentType: "text/plain; charset=utf-8",
|
|
1277
1370
|
repoId: repo.repoId
|
|
1278
1371
|
})
|
|
@@ -1334,8 +1427,11 @@ async function startCoordinator(opts) {
|
|
|
1334
1427
|
pubkey: me,
|
|
1335
1428
|
jobId: job.jobId
|
|
1336
1429
|
});
|
|
1430
|
+
finishedJobs.add(job.jobId);
|
|
1337
1431
|
await publish(progress("in_progress"));
|
|
1338
1432
|
}
|
|
1433
|
+
if (tailPublished) await publishLiveLogTail();
|
|
1434
|
+
tailClosed = true;
|
|
1339
1435
|
const now = clock();
|
|
1340
1436
|
const resultEventId = await publish(
|
|
1341
1437
|
buildCiWorkflowResult(
|
|
@@ -2021,6 +2117,11 @@ Options:
|
|
|
2021
2117
|
--act-bin <path> the act executable (default: RIG_ACT_BIN, else PATH)
|
|
2022
2118
|
--platform <label=img> runs-on label \u2192 Docker image; repeatable
|
|
2023
2119
|
(default: ubuntu-latest=${DEFAULT_ACT_PLATFORMS["ubuntu-latest"]})
|
|
2120
|
+
--pull pull the runner image before each run (act's default)
|
|
2121
|
+
--no-pull never pull: run the image already on this host \u2014
|
|
2122
|
+
what a locally built or otherwise private runner
|
|
2123
|
+
image needs, since act cannot pull it (act's own
|
|
2124
|
+
spelling, --pull=false, is accepted too)
|
|
2024
2125
|
--workdir <dir> where commits are checked out for runs (default:
|
|
2025
2126
|
<state-dir>/work)
|
|
2026
2127
|
--once stop after the first run concludes (its Workflow
|
|
@@ -2046,9 +2147,18 @@ half-published (story 15).
|
|
|
2046
2147
|
Stop with Ctrl-C (SIGINT) or SIGTERM: active runs conclude \`cancelled\`.`;
|
|
2047
2148
|
var ServeUsageError = class extends Error {
|
|
2048
2149
|
};
|
|
2150
|
+
function pullSpelling(arg) {
|
|
2151
|
+
if (!arg.startsWith("--pull=")) return arg;
|
|
2152
|
+
const value = arg.slice("--pull=".length);
|
|
2153
|
+
if (value === "false") return "--no-pull";
|
|
2154
|
+
if (value === "true") return "--pull";
|
|
2155
|
+
throw new ServeUsageError(
|
|
2156
|
+
`--pull takes no value \u2014 use --pull or --no-pull, got ${JSON.stringify(arg)}`
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2049
2159
|
function parseServeArgs(args) {
|
|
2050
2160
|
const { values } = parseArgs({
|
|
2051
|
-
args,
|
|
2161
|
+
args: args.map(pullSpelling),
|
|
2052
2162
|
options: {
|
|
2053
2163
|
relay: { type: "string" },
|
|
2054
2164
|
repo: { type: "string", multiple: true },
|
|
@@ -2058,6 +2168,8 @@ function parseServeArgs(args) {
|
|
|
2058
2168
|
gateway: { type: "string" },
|
|
2059
2169
|
"act-bin": { type: "string" },
|
|
2060
2170
|
platform: { type: "string", multiple: true },
|
|
2171
|
+
pull: { type: "boolean" },
|
|
2172
|
+
"no-pull": { type: "boolean" },
|
|
2061
2173
|
workdir: { type: "string" },
|
|
2062
2174
|
once: { type: "boolean", default: false },
|
|
2063
2175
|
json: { type: "boolean", default: false },
|
|
@@ -2133,6 +2245,10 @@ function parseServeArgs(args) {
|
|
|
2133
2245
|
platforms[spec.slice(0, eq)] = spec.slice(eq + 1);
|
|
2134
2246
|
}
|
|
2135
2247
|
}
|
|
2248
|
+
if (values.pull && values["no-pull"]) {
|
|
2249
|
+
throw new ServeUsageError("--pull and --no-pull cannot both be given");
|
|
2250
|
+
}
|
|
2251
|
+
const pull = values.pull ? true : values["no-pull"] ? false : void 0;
|
|
2136
2252
|
return {
|
|
2137
2253
|
relay: values.relay,
|
|
2138
2254
|
repos,
|
|
@@ -2142,18 +2258,26 @@ function parseServeArgs(args) {
|
|
|
2142
2258
|
...values.gateway !== void 0 ? { gateway: values.gateway } : {},
|
|
2143
2259
|
...values["act-bin"] !== void 0 ? { actBin: values["act-bin"] } : {},
|
|
2144
2260
|
...platforms ? { platforms } : {},
|
|
2261
|
+
...pull !== void 0 ? { pull } : {},
|
|
2145
2262
|
...values.workdir !== void 0 ? { workdir: values.workdir } : {},
|
|
2146
2263
|
once: values.once,
|
|
2147
2264
|
json: values.json
|
|
2148
2265
|
};
|
|
2149
2266
|
}
|
|
2150
|
-
function
|
|
2151
|
-
return
|
|
2267
|
+
function actRunnerOptions(flags) {
|
|
2268
|
+
return {
|
|
2269
|
+
...flags.actBin !== void 0 ? { actBin: flags.actBin } : {},
|
|
2270
|
+
...flags.platforms ? { platforms: flags.platforms } : {},
|
|
2271
|
+
...flags.pull !== void 0 ? { pull: flags.pull } : {}
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
function estimateRunCost(estimate, runTimeoutMs) {
|
|
2275
|
+
return BigInt(estimate.events) * estimate.rates.eventFee + BigInt(estimate.uploads) * uploadChargeFor(estimate.rates, LOG_UPLOAD_CAP_BYTES) + BigInt(liveLogTailEventBudget(runTimeoutMs)) * estimate.rates.eventFee;
|
|
2152
2276
|
}
|
|
2153
2277
|
function makeAffordabilityCheck(args) {
|
|
2154
|
-
const { ctx, env, log } = args;
|
|
2278
|
+
const { ctx, env, log, runTimeoutMs } = args;
|
|
2155
2279
|
return async (estimate) => {
|
|
2156
|
-
const cost = estimateRunCost(estimate);
|
|
2280
|
+
const cost = estimateRunCost(estimate, runTimeoutMs);
|
|
2157
2281
|
const store = new ChannelMapStore(resolveChannelPaths(env));
|
|
2158
2282
|
const mine = store.list().filter(
|
|
2159
2283
|
(record) => record.identity.toLowerCase() === ctx.ownerPubkey.toLowerCase() && record.supersededAt === void 0
|
|
@@ -2239,16 +2363,13 @@ async function runCiServe(args, deps) {
|
|
|
2239
2363
|
io.err(CI_SERVE_USAGE);
|
|
2240
2364
|
return 2;
|
|
2241
2365
|
}
|
|
2242
|
-
const gateway = (flags.gateway
|
|
2243
|
-
const readGateways =
|
|
2366
|
+
const gateway = configuredGateway(flags.gateway, forced.env) ?? PREFERRED_GATEWAY;
|
|
2367
|
+
const readGateways = readGatewaysFor(gateway);
|
|
2244
2368
|
let runner;
|
|
2245
2369
|
if (forced.runner) {
|
|
2246
2370
|
runner = forced.runner;
|
|
2247
2371
|
} else {
|
|
2248
|
-
const act = new ActRunner(
|
|
2249
|
-
...flags.actBin !== void 0 ? { actBin: flags.actBin } : {},
|
|
2250
|
-
...flags.platforms ? { platforms: flags.platforms } : {}
|
|
2251
|
-
});
|
|
2372
|
+
const act = new ActRunner(actRunnerOptions(flags));
|
|
2252
2373
|
if (act.binary(forced.env) === null) {
|
|
2253
2374
|
io.err(
|
|
2254
2375
|
"rig ci serve: the act executable was not found \u2014 install nektos/act (https://github.com/nektos/act) on this host, or point --act-bin / RIG_ACT_BIN at it. Nothing was started or paid."
|
|
@@ -2291,11 +2412,14 @@ async function runCiServe(args, deps) {
|
|
|
2291
2412
|
canAfford: forced.canAfford ?? makeAffordabilityCheck({
|
|
2292
2413
|
ctx,
|
|
2293
2414
|
env: forced.env,
|
|
2294
|
-
log: (line) => io.err(line)
|
|
2415
|
+
log: (line) => io.err(line),
|
|
2416
|
+
runTimeoutMs: flags.timeoutMs
|
|
2295
2417
|
}),
|
|
2296
2418
|
...forced.webSocketFactory ? { webSocketFactory: forced.webSocketFactory } : {},
|
|
2297
2419
|
...forced.fetchFn ? { fetchFn: forced.fetchFn } : {},
|
|
2298
|
-
|
|
2420
|
+
// SHAs the object map does not cover are resolved against the SAME
|
|
2421
|
+
// permaweb the objects are read from, not arweave.net (#183).
|
|
2422
|
+
resolveSha: forced.resolveSha ?? shaResolverFor(flags.gateway, forced.env),
|
|
2299
2423
|
...forced.clock ? { clock: forced.clock } : {},
|
|
2300
2424
|
...firstRun ? { onRunConcluded: (run) => concludeFirstRun(run) } : {},
|
|
2301
2425
|
log: (line) => io.err(line)
|
|
@@ -2365,8 +2489,10 @@ async function runCiServe(args, deps) {
|
|
|
2365
2489
|
}
|
|
2366
2490
|
export {
|
|
2367
2491
|
CI_SERVE_USAGE,
|
|
2492
|
+
actRunnerOptions,
|
|
2368
2493
|
estimateRunCost,
|
|
2369
2494
|
makeAffordabilityCheck,
|
|
2495
|
+
parseServeArgs,
|
|
2370
2496
|
runCiServe
|
|
2371
2497
|
};
|
|
2372
|
-
//# sourceMappingURL=ci-serve-
|
|
2498
|
+
//# sourceMappingURL=ci-serve-BU7NDT7O.js.map
|