@toon-protocol/rig 4.5.0 → 4.7.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.
@@ -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-UZZ2JKEC.js";
8
+ } from "./chunk-J6Y3U645.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,17 +43,24 @@ import {
39
43
  repoAddress,
40
44
  runGit,
41
45
  selectServiceRequests,
46
+ sliceLogTail,
42
47
  validateSecretUpdate,
43
48
  writeGitObjects
44
- } from "./chunk-S2BMLXAD.js";
49
+ } from "./chunk-3IVO2MSZ.js";
50
+ import {
51
+ PREFERRED_GATEWAY,
52
+ configuredGateway,
53
+ readGatewaysFor,
54
+ shaResolverFor
55
+ } from "./chunk-XQ2AWNHQ.js";
45
56
  import {
46
- ARWEAVE_GATEWAYS,
47
57
  REPOSITORY_STATE_KIND,
48
58
  authorizedStatusAuthors,
49
59
  defaultWebSocketFactory,
50
60
  fetchRemoteState,
61
+ parseStateRefTags,
51
62
  queryRelay
52
- } from "./chunk-HMED76GU.js";
63
+ } from "./chunk-S3KKQ4IC.js";
53
64
  import {
54
65
  ChannelMapStore,
55
66
  channelStatus,
@@ -430,7 +441,11 @@ var ActRunner = class {
430
441
  const line = buffered.slice(0, nl);
431
442
  buffered = buffered.slice(nl + 1);
432
443
  const parsedLine = parseActJsonLine(line);
433
- if (!parsedLine) continue;
444
+ if (!parsedLine) {
445
+ request.onLog?.(CI_RUNNER_CHANNEL_KEY, `${line}
446
+ `);
447
+ continue;
448
+ }
434
449
  lines.push(parsedLine);
435
450
  if (request.onLog && parsedLine.exitCode === void 0) {
436
451
  request.onLog(
@@ -443,7 +458,11 @@ var ActRunner = class {
443
458
  });
444
459
  stream.on("end", () => {
445
460
  const parsedLine = parseActJsonLine(buffered);
446
- if (parsedLine) lines.push(parsedLine);
461
+ if (parsedLine) {
462
+ lines.push(parsedLine);
463
+ } else if (buffered !== "") {
464
+ request.onLog?.(CI_RUNNER_CHANNEL_KEY, buffered);
465
+ }
447
466
  });
448
467
  };
449
468
  consume(child.stdout);
@@ -462,7 +481,7 @@ var ActRunner = class {
462
481
  startedAtMs,
463
482
  (note) => {
464
483
  this.options.warn?.(note);
465
- request.onLog?.("runner", `${note}
484
+ request.onLog?.(CI_RUNNER_CHANNEL_KEY, `${note}
466
485
  `);
467
486
  }
468
487
  );
@@ -889,6 +908,10 @@ var DEFAULT_RUN_TIMEOUT_MS = 30 * 60 * 1e3;
889
908
  var DEFAULT_ADVERTISEMENT_TTL = 30 * 60;
890
909
  var RENEW_FRACTION = 5 / 6;
891
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;
892
915
  var SECRETS_KEY_MAX_AGE = 86400;
893
916
  function nowSeconds() {
894
917
  return Math.floor(Date.now() / 1e3);
@@ -902,6 +925,20 @@ function logTail(log) {
902
925
  omitted: bytes.byteLength - LOG_TAIL_BYTES
903
926
  };
904
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
+ }
905
942
  function gatewayRawUrl(gatewayUrl, txId) {
906
943
  return `${gatewayUrl.replace(/\/+$/, "")}/raw/${txId}`;
907
944
  }
@@ -912,19 +949,6 @@ function movedRefs(before, after) {
912
949
  }
913
950
  return moved;
914
951
  }
915
- function parseRefsTags(ev) {
916
- const refs = {};
917
- let head = null;
918
- for (const [name, v1, v2] of ev.tags) {
919
- if (name === "r" && v1 && v2) {
920
- if (v1 === "HEAD" && v2.startsWith("ref: ")) head = v2.slice(5);
921
- else refs[v1] = v2;
922
- } else if (name === "HEAD" && v1?.startsWith("ref: ")) {
923
- head = v1.slice(5);
924
- }
925
- }
926
- return { refs, head };
927
- }
928
952
  function tagValues(tags, name) {
929
953
  return tags.filter((t) => t[0] === name).map((t) => t[1] ?? "").filter((v) => v !== "");
930
954
  }
@@ -1222,6 +1246,62 @@ async function startCoordinator(opts) {
1222
1246
  );
1223
1247
  };
1224
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();
1225
1305
  const abort = new AbortController();
1226
1306
  activeAborts.add(abort);
1227
1307
  run.abort = abort;
@@ -1239,6 +1319,7 @@ async function startCoordinator(opts) {
1239
1319
  trigger,
1240
1320
  secrets: run.secrets,
1241
1321
  timeoutMs,
1322
+ onLog: recordLog,
1242
1323
  signal: abort.signal
1243
1324
  }),
1244
1325
  new Promise((resolve2) => {
@@ -1272,19 +1353,19 @@ async function startCoordinator(opts) {
1272
1353
  } finally {
1273
1354
  scheduler.clearTimeout(timeoutHandle);
1274
1355
  scheduler.clearTimeout(renewTimer);
1356
+ scheduler.clearTimeout(tailTimer);
1275
1357
  activeAborts.delete(abort);
1276
1358
  }
1277
1359
  let conclusion = result.conclusion;
1278
1360
  if (timedOut) conclusion = "timed_out";
1279
1361
  else if (run.superseded || stopping && abort.signal.aborted)
1280
1362
  conclusion = "cancelled";
1281
- const secretValues = Object.values(run.secrets);
1282
1363
  for (const job of result.jobs) {
1283
1364
  const jobLog = redactSecretValues(job.log, secretValues);
1284
1365
  const { tail, omitted } = logTail(jobLog);
1285
1366
  const logReceipt = await serial.run(
1286
1367
  () => uploadBlob({
1287
- body: Buffer.from(jobLog, "utf-8"),
1368
+ body: boundLogForUpload(jobLog),
1288
1369
  contentType: "text/plain; charset=utf-8",
1289
1370
  repoId: repo.repoId
1290
1371
  })
@@ -1346,8 +1427,11 @@ async function startCoordinator(opts) {
1346
1427
  pubkey: me,
1347
1428
  jobId: job.jobId
1348
1429
  });
1430
+ finishedJobs.add(job.jobId);
1349
1431
  await publish(progress("in_progress"));
1350
1432
  }
1433
+ if (tailPublished) await publishLiveLogTail();
1434
+ tailClosed = true;
1351
1435
  const now = clock();
1352
1436
  const resultEventId = await publish(
1353
1437
  buildCiWorkflowResult(
@@ -1628,8 +1712,9 @@ async function startCoordinator(opts) {
1628
1712
  const handleRepoState = async (repo, ev) => {
1629
1713
  const cursor = repo.cursor;
1630
1714
  if (repoSeen(repo, ev) || ev.created_at < cursor.lastCreatedAt) return;
1631
- const { refs, head } = parseRefsTags(ev);
1632
- if (head) repo.defaultBranch = head;
1715
+ const { refs: refMap, headSymref } = parseStateRefTags(ev.tags);
1716
+ const refs = Object.fromEntries(refMap);
1717
+ if (headSymref) repo.defaultBranch = headSymref;
1633
1718
  const moved = movedRefs(cursor.refs, refs);
1634
1719
  cursor.refs = refs;
1635
1720
  markRepoProcessed(repo, ev);
@@ -2032,6 +2117,11 @@ Options:
2032
2117
  --act-bin <path> the act executable (default: RIG_ACT_BIN, else PATH)
2033
2118
  --platform <label=img> runs-on label \u2192 Docker image; repeatable
2034
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)
2035
2125
  --workdir <dir> where commits are checked out for runs (default:
2036
2126
  <state-dir>/work)
2037
2127
  --once stop after the first run concludes (its Workflow
@@ -2057,9 +2147,18 @@ half-published (story 15).
2057
2147
  Stop with Ctrl-C (SIGINT) or SIGTERM: active runs conclude \`cancelled\`.`;
2058
2148
  var ServeUsageError = class extends Error {
2059
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
+ }
2060
2159
  function parseServeArgs(args) {
2061
2160
  const { values } = parseArgs({
2062
- args,
2161
+ args: args.map(pullSpelling),
2063
2162
  options: {
2064
2163
  relay: { type: "string" },
2065
2164
  repo: { type: "string", multiple: true },
@@ -2069,6 +2168,8 @@ function parseServeArgs(args) {
2069
2168
  gateway: { type: "string" },
2070
2169
  "act-bin": { type: "string" },
2071
2170
  platform: { type: "string", multiple: true },
2171
+ pull: { type: "boolean" },
2172
+ "no-pull": { type: "boolean" },
2072
2173
  workdir: { type: "string" },
2073
2174
  once: { type: "boolean", default: false },
2074
2175
  json: { type: "boolean", default: false },
@@ -2144,6 +2245,10 @@ function parseServeArgs(args) {
2144
2245
  platforms[spec.slice(0, eq)] = spec.slice(eq + 1);
2145
2246
  }
2146
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;
2147
2252
  return {
2148
2253
  relay: values.relay,
2149
2254
  repos,
@@ -2153,18 +2258,26 @@ function parseServeArgs(args) {
2153
2258
  ...values.gateway !== void 0 ? { gateway: values.gateway } : {},
2154
2259
  ...values["act-bin"] !== void 0 ? { actBin: values["act-bin"] } : {},
2155
2260
  ...platforms ? { platforms } : {},
2261
+ ...pull !== void 0 ? { pull } : {},
2156
2262
  ...values.workdir !== void 0 ? { workdir: values.workdir } : {},
2157
2263
  once: values.once,
2158
2264
  json: values.json
2159
2265
  };
2160
2266
  }
2161
- function estimateRunCost(estimate) {
2162
- return BigInt(estimate.events) * estimate.rates.eventFee + BigInt(estimate.uploads) * uploadChargeFor(estimate.rates, 64 * 1024);
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;
2163
2276
  }
2164
2277
  function makeAffordabilityCheck(args) {
2165
- const { ctx, env, log } = args;
2278
+ const { ctx, env, log, runTimeoutMs } = args;
2166
2279
  return async (estimate) => {
2167
- const cost = estimateRunCost(estimate);
2280
+ const cost = estimateRunCost(estimate, runTimeoutMs);
2168
2281
  const store = new ChannelMapStore(resolveChannelPaths(env));
2169
2282
  const mine = store.list().filter(
2170
2283
  (record) => record.identity.toLowerCase() === ctx.ownerPubkey.toLowerCase() && record.supersededAt === void 0
@@ -2250,16 +2363,13 @@ async function runCiServe(args, deps) {
2250
2363
  io.err(CI_SERVE_USAGE);
2251
2364
  return 2;
2252
2365
  }
2253
- const gateway = (flags.gateway ?? forced.env["RIG_ARWEAVE_GATEWAY"] ?? PREFERRED_GATEWAY).replace(/\/+$/, "");
2254
- const readGateways = [`${gateway}/raw`, ...ARWEAVE_GATEWAYS];
2366
+ const gateway = configuredGateway(flags.gateway, forced.env) ?? PREFERRED_GATEWAY;
2367
+ const readGateways = readGatewaysFor(gateway);
2255
2368
  let runner;
2256
2369
  if (forced.runner) {
2257
2370
  runner = forced.runner;
2258
2371
  } else {
2259
- const act = new ActRunner({
2260
- ...flags.actBin !== void 0 ? { actBin: flags.actBin } : {},
2261
- ...flags.platforms ? { platforms: flags.platforms } : {}
2262
- });
2372
+ const act = new ActRunner(actRunnerOptions(flags));
2263
2373
  if (act.binary(forced.env) === null) {
2264
2374
  io.err(
2265
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."
@@ -2302,11 +2412,14 @@ async function runCiServe(args, deps) {
2302
2412
  canAfford: forced.canAfford ?? makeAffordabilityCheck({
2303
2413
  ctx,
2304
2414
  env: forced.env,
2305
- log: (line) => io.err(line)
2415
+ log: (line) => io.err(line),
2416
+ runTimeoutMs: flags.timeoutMs
2306
2417
  }),
2307
2418
  ...forced.webSocketFactory ? { webSocketFactory: forced.webSocketFactory } : {},
2308
2419
  ...forced.fetchFn ? { fetchFn: forced.fetchFn } : {},
2309
- ...forced.resolveSha ? { resolveSha: forced.resolveSha } : {},
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),
2310
2423
  ...forced.clock ? { clock: forced.clock } : {},
2311
2424
  ...firstRun ? { onRunConcluded: (run) => concludeFirstRun(run) } : {},
2312
2425
  log: (line) => io.err(line)
@@ -2376,8 +2489,10 @@ async function runCiServe(args, deps) {
2376
2489
  }
2377
2490
  export {
2378
2491
  CI_SERVE_USAGE,
2492
+ actRunnerOptions,
2379
2493
  estimateRunCost,
2380
2494
  makeAffordabilityCheck,
2495
+ parseServeArgs,
2381
2496
  runCiServe
2382
2497
  };
2383
- //# sourceMappingURL=ci-serve-PYEHIBN7.js.map
2498
+ //# sourceMappingURL=ci-serve-T33FCQVT.js.map