@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.
- package/README.md +101 -8
- package/dist/{chunk-S2BMLXAD.js → chunk-3IVO2MSZ.js} +584 -20
- package/dist/chunk-3IVO2MSZ.js.map +1 -0
- package/dist/{chunk-UZZ2JKEC.js → chunk-J6Y3U645.js} +522 -204
- package/dist/chunk-J6Y3U645.js.map +1 -0
- package/dist/{chunk-HMED76GU.js → chunk-S3KKQ4IC.js} +104 -56
- package/dist/chunk-S3KKQ4IC.js.map +1 -0
- package/dist/chunk-XQ2AWNHQ.js +0 -0
- package/dist/chunk-XQ2AWNHQ.js.map +1 -0
- package/dist/{ci-serve-PYEHIBN7.js → ci-serve-T33FCQVT.js} +154 -39
- package/dist/ci-serve-T33FCQVT.js.map +1 -0
- package/dist/cli/rig.js +5 -3
- package/dist/cli/rig.js.map +1 -1
- package/dist/index.d.ts +478 -23
- package/dist/index.js +531 -7
- package/dist/index.js.map +1 -1
- package/dist/{publisher-Cf6tKnZ3.d.ts → publisher-DgbfziOm.d.ts} +152 -26
- package/dist/standalone/index.d.ts +1 -1
- package/dist/{standalone-mode-W3ZEKC6M.js → standalone-mode-FY3XVQQK.js} +16 -4
- package/dist/standalone-mode-FY3XVQQK.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-HMED76GU.js.map +0 -1
- package/dist/chunk-S2BMLXAD.js.map +0 -1
- package/dist/chunk-UZZ2JKEC.js.map +0 -1
- package/dist/ci-serve-PYEHIBN7.js.map +0 -1
- package/dist/standalone-mode-W3ZEKC6M.js.map +0 -1
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ARWEAVE_FETCH_TIMEOUT_MS,
|
|
3
3
|
ARWEAVE_GATEWAYS,
|
|
4
|
+
MAINTAINERS_TAG,
|
|
5
|
+
MAX_ARWEAVE_TAGS_PER_EVENT,
|
|
4
6
|
MissingIdentityError,
|
|
7
|
+
PAYOUT_TAG,
|
|
5
8
|
authorizedStatusAuthors,
|
|
6
|
-
buildRepoAnnouncement,
|
|
7
9
|
buildRepoRefs,
|
|
8
10
|
defaultWebSocketFactory,
|
|
9
11
|
isValidArweaveTxId,
|
|
12
|
+
parseMaintainers,
|
|
10
13
|
queryRelay,
|
|
11
14
|
resolveIdentity
|
|
12
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-S3KKQ4IC.js";
|
|
13
16
|
import {
|
|
14
17
|
defaultDaemonPort,
|
|
15
18
|
standaloneForced,
|
|
@@ -65,6 +68,203 @@ function createGitTag(opts) {
|
|
|
65
68
|
return hashGitObject("tag", Buffer.from(lines.join("\n"), "utf-8"));
|
|
66
69
|
}
|
|
67
70
|
|
|
71
|
+
// src/repo-announcement.ts
|
|
72
|
+
import { REPOSITORY_ANNOUNCEMENT_KIND } from "@toon-protocol/core/nip34";
|
|
73
|
+
var RELAYS_TAG = "relays";
|
|
74
|
+
var WEB_TAG = "web";
|
|
75
|
+
var EARLIEST_UNIQUE_COMMIT_MARKER = "euc";
|
|
76
|
+
function multiValueTag(name, values2) {
|
|
77
|
+
const kept = [];
|
|
78
|
+
const seen = /* @__PURE__ */ new Set();
|
|
79
|
+
for (const value of values2) {
|
|
80
|
+
const trimmed = value.trim();
|
|
81
|
+
if (trimmed.length === 0 || seen.has(trimmed)) continue;
|
|
82
|
+
seen.add(trimmed);
|
|
83
|
+
kept.push(trimmed);
|
|
84
|
+
}
|
|
85
|
+
return kept.length > 0 ? [[name, ...kept]] : [];
|
|
86
|
+
}
|
|
87
|
+
var named = (name) => (tag) => tag[0] === name;
|
|
88
|
+
var SLOTS = /* @__PURE__ */ new Map([
|
|
89
|
+
["d", { matches: named("d"), rewrite: (e) => [["d", e.repoId]] }],
|
|
90
|
+
[
|
|
91
|
+
"name",
|
|
92
|
+
{
|
|
93
|
+
matches: named("name"),
|
|
94
|
+
rewrite: (e) => e.name === void 0 ? null : [["name", e.name]]
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
"description",
|
|
99
|
+
{
|
|
100
|
+
matches: named("description"),
|
|
101
|
+
rewrite: (e) => e.description === void 0 ? null : [["description", e.description]]
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
[
|
|
105
|
+
"maintainers",
|
|
106
|
+
{
|
|
107
|
+
matches: named(MAINTAINERS_TAG),
|
|
108
|
+
rewrite: (e) => {
|
|
109
|
+
if (e.maintainers === void 0) return null;
|
|
110
|
+
const declared = parseMaintainers([
|
|
111
|
+
[MAINTAINERS_TAG, ...e.maintainers]
|
|
112
|
+
]);
|
|
113
|
+
return declared.length > 0 ? [[MAINTAINERS_TAG, ...declared]] : [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
[
|
|
118
|
+
"payout",
|
|
119
|
+
{
|
|
120
|
+
matches: named(PAYOUT_TAG),
|
|
121
|
+
rewrite: (e) => e.payout === void 0 ? null : e.payout ? [[PAYOUT_TAG, e.payout.chain, e.payout.address]] : []
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
[
|
|
125
|
+
"relays",
|
|
126
|
+
{
|
|
127
|
+
matches: named(RELAYS_TAG),
|
|
128
|
+
rewrite: (e) => e.relays === void 0 ? null : e.relays ? multiValueTag(RELAYS_TAG, e.relays) : []
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
[
|
|
132
|
+
"web",
|
|
133
|
+
{
|
|
134
|
+
matches: named(WEB_TAG),
|
|
135
|
+
rewrite: (e) => e.web === void 0 ? null : e.web ? multiValueTag(WEB_TAG, e.web) : []
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
[
|
|
139
|
+
"euc",
|
|
140
|
+
{
|
|
141
|
+
// Only the euc marker is ours. Every other `r` tag is someone else's.
|
|
142
|
+
matches: (tag) => tag[0] === "r" && tag[2] === EARLIEST_UNIQUE_COMMIT_MARKER,
|
|
143
|
+
rewrite: (e) => e.earliestUniqueCommit === void 0 ? null : e.earliestUniqueCommit ? [["r", e.earliestUniqueCommit, EARLIEST_UNIQUE_COMMIT_MARKER]] : []
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
]);
|
|
147
|
+
function slotOf(tag) {
|
|
148
|
+
for (const [slot, spec] of SLOTS) {
|
|
149
|
+
if (spec.matches(tag)) return slot;
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
function rewrittenSlots(edits) {
|
|
154
|
+
const out = /* @__PURE__ */ new Map();
|
|
155
|
+
for (const [slot, spec] of SLOTS) {
|
|
156
|
+
const next = spec.rewrite(edits);
|
|
157
|
+
if (next !== null) out.set(slot, next);
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
function amendRepoAnnouncement(current, edits) {
|
|
162
|
+
const rewritten = rewrittenSlots(edits);
|
|
163
|
+
const tags = [["d", edits.repoId]];
|
|
164
|
+
const emitted = /* @__PURE__ */ new Set(["d"]);
|
|
165
|
+
for (const tag of current?.tags ?? []) {
|
|
166
|
+
const slot = slotOf(tag);
|
|
167
|
+
const replacement = slot === null ? void 0 : rewritten.get(slot);
|
|
168
|
+
if (slot === null || replacement === void 0) {
|
|
169
|
+
tags.push([...tag]);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (emitted.has(slot)) continue;
|
|
173
|
+
for (const next of replacement) tags.push([...next]);
|
|
174
|
+
emitted.add(slot);
|
|
175
|
+
}
|
|
176
|
+
for (const [slot, replacement] of rewritten) {
|
|
177
|
+
if (emitted.has(slot)) continue;
|
|
178
|
+
for (const next of replacement) tags.push([...next]);
|
|
179
|
+
emitted.add(slot);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
kind: REPOSITORY_ANNOUNCEMENT_KIND,
|
|
183
|
+
content: current?.content ?? "",
|
|
184
|
+
tags,
|
|
185
|
+
created_at: Math.floor(Date.now() / 1e3)
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function buildRepoAnnouncement(repoId, name, description, maintainers = [], payout) {
|
|
189
|
+
return amendRepoAnnouncement(null, {
|
|
190
|
+
repoId,
|
|
191
|
+
name,
|
|
192
|
+
description,
|
|
193
|
+
maintainers,
|
|
194
|
+
payout: payout ?? null
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
async function earliestUniqueCommit(reader) {
|
|
198
|
+
const all = await reader.rootCommits();
|
|
199
|
+
if (all.length === 0) return null;
|
|
200
|
+
if (all.length === 1) return all[0] ?? null;
|
|
201
|
+
const onDefaultBranch = await reader.rootCommits("HEAD");
|
|
202
|
+
const candidates = onDefaultBranch.length > 0 ? onDefaultBranch : all;
|
|
203
|
+
return [...candidates].sort()[0] ?? null;
|
|
204
|
+
}
|
|
205
|
+
function announcedEuc(current) {
|
|
206
|
+
for (const tag of current?.tags ?? []) {
|
|
207
|
+
if (tag[0] === "r" && tag[2] === EARLIEST_UNIQUE_COMMIT_MARKER) {
|
|
208
|
+
const sha = tag[1];
|
|
209
|
+
if (sha !== void 0 && sha.length > 0) return sha;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
function unionValues(current, name, additions) {
|
|
215
|
+
const existing = [];
|
|
216
|
+
for (const tag of current?.tags ?? []) {
|
|
217
|
+
if (tag[0] === name) existing.push(...tag.slice(1));
|
|
218
|
+
}
|
|
219
|
+
const out = [];
|
|
220
|
+
const seen = /* @__PURE__ */ new Set();
|
|
221
|
+
for (const value of [...existing, ...additions]) {
|
|
222
|
+
const trimmed = value.trim();
|
|
223
|
+
if (trimmed.length === 0 || seen.has(trimmed)) continue;
|
|
224
|
+
seen.add(trimmed);
|
|
225
|
+
out.push(trimmed);
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
function conformanceEdits(current, facts) {
|
|
230
|
+
const edits = {};
|
|
231
|
+
const relays = unionValues(current, RELAYS_TAG, facts.relays);
|
|
232
|
+
if (relays.length > 0) edits.relays = relays;
|
|
233
|
+
const web = unionValues(current, WEB_TAG, facts.web === null ? [] : [facts.web]);
|
|
234
|
+
if (web.length > 0) edits.web = web;
|
|
235
|
+
if (announcedEuc(current) === null && facts.earliestUniqueCommit !== null) {
|
|
236
|
+
edits.earliestUniqueCommit = facts.earliestUniqueCommit;
|
|
237
|
+
}
|
|
238
|
+
return edits;
|
|
239
|
+
}
|
|
240
|
+
function tagKey(tag) {
|
|
241
|
+
return JSON.stringify(tag);
|
|
242
|
+
}
|
|
243
|
+
function diffAnnouncementTags(current, next) {
|
|
244
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
245
|
+
for (const tag of current?.tags ?? []) {
|
|
246
|
+
remaining.set(tagKey(tag), (remaining.get(tagKey(tag)) ?? 0) + 1);
|
|
247
|
+
}
|
|
248
|
+
const added = [];
|
|
249
|
+
for (const tag of next.tags) {
|
|
250
|
+
const key = tagKey(tag);
|
|
251
|
+
const count = remaining.get(key) ?? 0;
|
|
252
|
+
if (count > 0) remaining.set(key, count - 1);
|
|
253
|
+
else added.push([...tag]);
|
|
254
|
+
}
|
|
255
|
+
const removed = [];
|
|
256
|
+
for (const [key, count] of remaining) {
|
|
257
|
+
for (let i = 0; i < count; i += 1) {
|
|
258
|
+
removed.push(JSON.parse(key));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
added,
|
|
263
|
+
removed,
|
|
264
|
+
unchanged: added.length === 0 && removed.length === 0
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
68
268
|
// src/repo-reader.ts
|
|
69
269
|
import { execFile, spawn } from "child_process";
|
|
70
270
|
import { promisify } from "util";
|
|
@@ -261,6 +461,140 @@ var GitRepoReader = class {
|
|
|
261
461
|
}
|
|
262
462
|
return objects;
|
|
263
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* Run git and hand each stdout line to `visit` as it arrives, never holding
|
|
466
|
+
* the whole output in memory. Returning `false` from `visit` ends the walk:
|
|
467
|
+
* the child is killed and the promise resolves with what was seen.
|
|
468
|
+
*
|
|
469
|
+
* Every other git call here buffers into one `execFile` string, which is
|
|
470
|
+
* fine for bounded output. These object walks are not bounded — they grow
|
|
471
|
+
* with the size of the repository — so on a repo big enough to need the
|
|
472
|
+
* object-map cap they would be the thing that breaks (#162).
|
|
473
|
+
*/
|
|
474
|
+
streamLines(args, visit) {
|
|
475
|
+
return new Promise((resolve, reject) => {
|
|
476
|
+
const child = spawn("git", args, {
|
|
477
|
+
cwd: this.repoPath,
|
|
478
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
479
|
+
});
|
|
480
|
+
let pending = "";
|
|
481
|
+
let stderr = "";
|
|
482
|
+
let settled = false;
|
|
483
|
+
const finish = (err) => {
|
|
484
|
+
if (settled) return;
|
|
485
|
+
settled = true;
|
|
486
|
+
if (err) reject(err);
|
|
487
|
+
else resolve();
|
|
488
|
+
};
|
|
489
|
+
child.stdout.setEncoding("utf-8");
|
|
490
|
+
child.stdout.on("data", (chunk) => {
|
|
491
|
+
if (settled) return;
|
|
492
|
+
pending += chunk;
|
|
493
|
+
let idx = pending.indexOf("\n");
|
|
494
|
+
while (idx !== -1) {
|
|
495
|
+
const line = pending.slice(0, idx);
|
|
496
|
+
pending = pending.slice(idx + 1);
|
|
497
|
+
if (line && !visit(line)) {
|
|
498
|
+
child.kill("SIGTERM");
|
|
499
|
+
return finish();
|
|
500
|
+
}
|
|
501
|
+
idx = pending.indexOf("\n");
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
child.stderr.on("data", (chunk) => {
|
|
505
|
+
stderr += chunk.toString("utf-8");
|
|
506
|
+
});
|
|
507
|
+
child.on("error", (err) => {
|
|
508
|
+
finish(
|
|
509
|
+
new GitError(
|
|
510
|
+
`failed to spawn git ${args[0]}: ${err.message}`,
|
|
511
|
+
void 0,
|
|
512
|
+
""
|
|
513
|
+
)
|
|
514
|
+
);
|
|
515
|
+
});
|
|
516
|
+
child.on("close", (code) => {
|
|
517
|
+
if (settled) return;
|
|
518
|
+
if (code !== 0) {
|
|
519
|
+
return finish(
|
|
520
|
+
new GitError(
|
|
521
|
+
`git ${args[0]} failed (exit ${code}): ${stderr.trim()}`,
|
|
522
|
+
code ?? void 0,
|
|
523
|
+
stderr.trim()
|
|
524
|
+
)
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
if (pending) visit(pending);
|
|
528
|
+
finish();
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Every object SHA in the local object database, in no particular order
|
|
534
|
+
* (`git cat-file --batch-all-objects --batch-check`, no bodies read).
|
|
535
|
+
*
|
|
536
|
+
* This is the honest answer to "what does this repository already have",
|
|
537
|
+
* which `rig fetch` needs to compute its delta. It deliberately does NOT
|
|
538
|
+
* consult the remote's kind:30618 `arweave` map: that map is capped (#162)
|
|
539
|
+
* and is a hint cache, never an index of local presence.
|
|
540
|
+
*
|
|
541
|
+
* Streamed, so the listing never has to fit in one buffer.
|
|
542
|
+
*/
|
|
543
|
+
async listAllObjectShas() {
|
|
544
|
+
const shas = [];
|
|
545
|
+
await this.streamLines(
|
|
546
|
+
[
|
|
547
|
+
"cat-file",
|
|
548
|
+
"--batch-check=%(objectname)",
|
|
549
|
+
"--batch-all-objects",
|
|
550
|
+
"--unordered"
|
|
551
|
+
],
|
|
552
|
+
(line) => {
|
|
553
|
+
shas.push(line);
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
);
|
|
557
|
+
return shas;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* SHAs of every object reachable from `tips`, newest first: commits in
|
|
561
|
+
* reverse-chronological order, each commit immediately followed by the
|
|
562
|
+
* trees and blobs it is the first to reference
|
|
563
|
+
* (`git rev-list --objects --in-commit-order`).
|
|
564
|
+
*
|
|
565
|
+
* Used to rank the kind:30618 object map when it exceeds
|
|
566
|
+
* `MAX_ARWEAVE_TAGS_PER_EVENT` (#162): the entries most likely to be wanted
|
|
567
|
+
* by the next clone or fetch are the ones nearest the tips. Tips that do
|
|
568
|
+
* not resolve locally (a remote ref we never fetched) are dropped rather
|
|
569
|
+
* than failing the walk.
|
|
570
|
+
*
|
|
571
|
+
* `limit` stops the walk once that many SHAs have been seen — the ranking
|
|
572
|
+
* caller never needs more than the cap, and a full walk of a repository big
|
|
573
|
+
* enough to hit the cap is exactly the work worth not doing. The walk is
|
|
574
|
+
* streamed, so neither its output nor this array is ever bigger than that.
|
|
575
|
+
*/
|
|
576
|
+
async reachableObjectsNewestFirst(tips, limit = Infinity) {
|
|
577
|
+
for (const tip of tips) assertRevision(tip, "tip");
|
|
578
|
+
const known = await this.filterExisting([...new Set(tips)]);
|
|
579
|
+
if (known.length === 0 || limit < 1) return [];
|
|
580
|
+
const shas = [];
|
|
581
|
+
await this.streamLines(
|
|
582
|
+
[
|
|
583
|
+
"rev-list",
|
|
584
|
+
"--objects",
|
|
585
|
+
"--in-commit-order",
|
|
586
|
+
...known,
|
|
587
|
+
"--"
|
|
588
|
+
// nothing user-supplied can become a pathspec
|
|
589
|
+
],
|
|
590
|
+
(line) => {
|
|
591
|
+
const spaceIdx = line.indexOf(" ");
|
|
592
|
+
shas.push(spaceIdx === -1 ? line : line.slice(0, spaceIdx));
|
|
593
|
+
return shas.length < limit;
|
|
594
|
+
}
|
|
595
|
+
);
|
|
596
|
+
return shas;
|
|
597
|
+
}
|
|
264
598
|
/**
|
|
265
599
|
* List every blob (file) reachable from a ref's root tree, recursively,
|
|
266
600
|
* with the path it is served at (#368: the ar.io site manifest join key).
|
|
@@ -485,6 +819,46 @@ var GitRepoReader = class {
|
|
|
485
819
|
}
|
|
486
820
|
return parents;
|
|
487
821
|
}
|
|
822
|
+
/**
|
|
823
|
+
* SHAs of the repo's ROOT commits — the parentless commits a history starts
|
|
824
|
+
* from — via `git rev-list --max-parents=0`.
|
|
825
|
+
*
|
|
826
|
+
* The repo's *earliest unique commit* (NIP-34's `euc` fork-identity tag) is
|
|
827
|
+
* chosen from these; the choosing rule lives in `repo-announcement.ts`.
|
|
828
|
+
*
|
|
829
|
+
* @param rev - Consider only roots reachable from this revision. Omit for
|
|
830
|
+
* every root in the repository (`--all`).
|
|
831
|
+
* @returns The roots in `rev-list` order, or `[]` when the revision does not
|
|
832
|
+
* resolve — an unborn `HEAD` in a repo with no commits is not an error.
|
|
833
|
+
*/
|
|
834
|
+
async rootCommits(rev) {
|
|
835
|
+
const args = ["rev-list", "--max-parents=0"];
|
|
836
|
+
if (rev === void 0) {
|
|
837
|
+
args.push("--all");
|
|
838
|
+
} else {
|
|
839
|
+
assertRevision(rev, "rev");
|
|
840
|
+
args.push(rev);
|
|
841
|
+
}
|
|
842
|
+
args.push("--");
|
|
843
|
+
const { stdout, exitCode } = await this.git(args, {
|
|
844
|
+
allowExitCodes: [128]
|
|
845
|
+
});
|
|
846
|
+
if (exitCode !== 0) return [];
|
|
847
|
+
const roots = [];
|
|
848
|
+
for (const line of stdout.split("\n")) {
|
|
849
|
+
const sha = line.trim();
|
|
850
|
+
if (!sha) continue;
|
|
851
|
+
if (!FULL_SHA_RE.test(sha)) {
|
|
852
|
+
throw new GitError(
|
|
853
|
+
`unexpected rev-list --max-parents=0 line: ${JSON.stringify(line)}`,
|
|
854
|
+
void 0,
|
|
855
|
+
""
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
roots.push(sha);
|
|
859
|
+
}
|
|
860
|
+
return roots;
|
|
861
|
+
}
|
|
488
862
|
/**
|
|
489
863
|
* Resolve a ref/revision to a full SHA via `git rev-parse --verify`.
|
|
490
864
|
* Throws {@link GitError} when the name doesn't resolve.
|
|
@@ -1140,6 +1514,7 @@ async function planPush(options) {
|
|
|
1140
1514
|
if (refname !== headSymref) newRefs[refname] = sha;
|
|
1141
1515
|
}
|
|
1142
1516
|
const announceNeeded = !remoteState.announced;
|
|
1517
|
+
const euc = announceNeeded ? await earliestUniqueCommit(repoReader) : null;
|
|
1143
1518
|
const totalObjectBytes = objects.reduce((sum, o) => sum + o.size, 0);
|
|
1144
1519
|
const uploadFee = objects.reduce(
|
|
1145
1520
|
(sum, o) => sum + uploadChargeFor(feeRates, o.size),
|
|
@@ -1158,7 +1533,9 @@ async function planPush(options) {
|
|
|
1158
1533
|
announceNeeded,
|
|
1159
1534
|
announcement: {
|
|
1160
1535
|
name: options.announcement?.name ?? repoId,
|
|
1161
|
-
description: options.announcement?.description ?? ""
|
|
1536
|
+
description: options.announcement?.description ?? "",
|
|
1537
|
+
...options.announcement?.web ? { web: options.announcement.web } : {},
|
|
1538
|
+
...euc ? { earliestUniqueCommit: euc } : {}
|
|
1162
1539
|
},
|
|
1163
1540
|
estimate: {
|
|
1164
1541
|
objectCount: objects.length,
|
|
@@ -1172,6 +1549,32 @@ async function planPush(options) {
|
|
|
1172
1549
|
};
|
|
1173
1550
|
}
|
|
1174
1551
|
var READ_BATCH_SIZE = 100;
|
|
1552
|
+
async function boundObjectMap(merged, plan, repoReader) {
|
|
1553
|
+
if (merged.size <= MAX_ARWEAVE_TAGS_PER_EVENT) return merged;
|
|
1554
|
+
const kept = /* @__PURE__ */ new Map();
|
|
1555
|
+
const take = (sha) => {
|
|
1556
|
+
if (kept.size >= MAX_ARWEAVE_TAGS_PER_EVENT || kept.has(sha)) return;
|
|
1557
|
+
const txId = merged.get(sha);
|
|
1558
|
+
if (txId !== void 0) kept.set(sha, txId);
|
|
1559
|
+
};
|
|
1560
|
+
for (const object of plan.objects) take(object.sha);
|
|
1561
|
+
if (kept.size < MAX_ARWEAVE_TAGS_PER_EVENT) {
|
|
1562
|
+
const tips = [...new Set(Object.values(plan.newRefs))];
|
|
1563
|
+
const reachable = await repoReader.reachableObjectsNewestFirst(
|
|
1564
|
+
tips,
|
|
1565
|
+
MAX_ARWEAVE_TAGS_PER_EVENT
|
|
1566
|
+
);
|
|
1567
|
+
for (const sha of reachable) {
|
|
1568
|
+
if (kept.size >= MAX_ARWEAVE_TAGS_PER_EVENT) break;
|
|
1569
|
+
take(sha);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
for (const [sha, txId] of merged) {
|
|
1573
|
+
if (kept.size >= MAX_ARWEAVE_TAGS_PER_EVENT) break;
|
|
1574
|
+
if (!kept.has(sha)) kept.set(sha, txId);
|
|
1575
|
+
}
|
|
1576
|
+
return kept;
|
|
1577
|
+
}
|
|
1175
1578
|
async function executePush(options) {
|
|
1176
1579
|
const { plan, publisher, remoteState, repoReader, relayUrls } = options;
|
|
1177
1580
|
const merged = new Map([...remoteState.shaToTxId, ...plan.knownShaToTxId]);
|
|
@@ -1230,18 +1633,22 @@ async function executePush(options) {
|
|
|
1230
1633
|
}
|
|
1231
1634
|
let announceReceipt = null;
|
|
1232
1635
|
if (plan.announceNeeded && !remoteState.announced) {
|
|
1233
|
-
const announceEvent =
|
|
1234
|
-
plan.repoId,
|
|
1235
|
-
plan.announcement.name,
|
|
1236
|
-
plan.announcement.description
|
|
1237
|
-
|
|
1636
|
+
const announceEvent = amendRepoAnnouncement(null, {
|
|
1637
|
+
repoId: plan.repoId,
|
|
1638
|
+
name: plan.announcement.name,
|
|
1639
|
+
description: plan.announcement.description,
|
|
1640
|
+
relays: relayUrls,
|
|
1641
|
+
...plan.announcement.web ? { web: [plan.announcement.web] } : {},
|
|
1642
|
+
...plan.announcement.earliestUniqueCommit ? { earliestUniqueCommit: plan.announcement.earliestUniqueCommit } : {}
|
|
1643
|
+
});
|
|
1238
1644
|
announceReceipt = await publisher.publishEvent(announceEvent, relayUrls);
|
|
1239
1645
|
totalFeePaid += announceReceipt.feePaid;
|
|
1240
1646
|
}
|
|
1647
|
+
const published = await boundObjectMap(merged, plan, repoReader);
|
|
1241
1648
|
const refsEvent = buildRepoRefs(
|
|
1242
1649
|
plan.repoId,
|
|
1243
1650
|
plan.newRefs,
|
|
1244
|
-
Object.fromEntries(
|
|
1651
|
+
Object.fromEntries(published)
|
|
1245
1652
|
);
|
|
1246
1653
|
const refsReceipt = await publisher.publishEvent(refsEvent, relayUrls);
|
|
1247
1654
|
totalFeePaid += refsReceipt.feePaid;
|
|
@@ -1256,7 +1663,7 @@ async function executePush(options) {
|
|
|
1256
1663
|
uploads,
|
|
1257
1664
|
announceReceipt,
|
|
1258
1665
|
refsReceipt,
|
|
1259
|
-
arweaveMap:
|
|
1666
|
+
arweaveMap: published,
|
|
1260
1667
|
totalFeePaid
|
|
1261
1668
|
};
|
|
1262
1669
|
}
|
|
@@ -1270,10 +1677,16 @@ var CI_MANUAL_TRIGGER_KIND = 9840;
|
|
|
1270
1677
|
var CI_JOB_RESULT_KIND = 9841;
|
|
1271
1678
|
var CI_WORKFLOW_RESULT_KIND = 9842;
|
|
1272
1679
|
var CI_WORKFLOW_PROGRESS_KIND = 39842;
|
|
1680
|
+
var CI_LIVE_LOG_TAIL_KIND = 39841;
|
|
1273
1681
|
var CI_RUNNER_FAMILY = "act";
|
|
1274
1682
|
var CI_SOFTWARE = "rig";
|
|
1275
1683
|
var CI_MAX_ADVERTISEMENT_TTL = 30 * 60;
|
|
1276
1684
|
var CI_MAX_PROGRESS_TTL = 30 * 60;
|
|
1685
|
+
var CI_MAX_LIVE_LOG_TAIL_TTL = 30 * 60;
|
|
1686
|
+
var CI_LIVE_LOG_TAIL_INTERVAL_MS = 1e4;
|
|
1687
|
+
var CI_LIVE_LOG_TAIL_JOB_BYTES = 16 * 1024;
|
|
1688
|
+
var CI_LIVE_LOG_TAIL_MAX_BYTES = 64 * 1024;
|
|
1689
|
+
var CI_RUNNER_CHANNEL_KEY = "~runner";
|
|
1277
1690
|
var REPO_ANNOUNCEMENT_KIND = 30617;
|
|
1278
1691
|
var CONCLUSIONS = /* @__PURE__ */ new Set([
|
|
1279
1692
|
"success",
|
|
@@ -2005,6 +2418,138 @@ function parseCiWorkflowProgress(ev) {
|
|
|
2005
2418
|
if (provenance) result.provenance = provenance;
|
|
2006
2419
|
return result;
|
|
2007
2420
|
}
|
|
2421
|
+
function liveLogTailBudget(entries) {
|
|
2422
|
+
if (!Number.isFinite(entries) || entries <= 1) {
|
|
2423
|
+
return CI_LIVE_LOG_TAIL_JOB_BYTES;
|
|
2424
|
+
}
|
|
2425
|
+
const share = Math.floor(CI_LIVE_LOG_TAIL_MAX_BYTES / entries);
|
|
2426
|
+
return Math.max(1, Math.min(CI_LIVE_LOG_TAIL_JOB_BYTES, share));
|
|
2427
|
+
}
|
|
2428
|
+
function liveLogTailEventBudget(runTimeoutMs) {
|
|
2429
|
+
if (!Number.isFinite(runTimeoutMs) || runTimeoutMs <= 0) return 1;
|
|
2430
|
+
return Math.ceil(runTimeoutMs / CI_LIVE_LOG_TAIL_INTERVAL_MS) + 1;
|
|
2431
|
+
}
|
|
2432
|
+
function sliceLogTail(text, maxBytes) {
|
|
2433
|
+
const bytes = Buffer.from(text, "utf8");
|
|
2434
|
+
if (bytes.length <= maxBytes) return { tail: text, omitted: 0 };
|
|
2435
|
+
if (maxBytes <= 0) return { tail: "", omitted: bytes.length };
|
|
2436
|
+
let start = bytes.length - maxBytes;
|
|
2437
|
+
while (start < bytes.length && (bytes[start] & 192) === 128) {
|
|
2438
|
+
start += 1;
|
|
2439
|
+
}
|
|
2440
|
+
return { tail: bytes.subarray(start).toString("utf8"), omitted: start };
|
|
2441
|
+
}
|
|
2442
|
+
function assertOmitted(omitted, what) {
|
|
2443
|
+
if (!Number.isSafeInteger(omitted) || omitted < 0) {
|
|
2444
|
+
throw new Error(`${what}: omitted must be a non-negative byte count`);
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
function buildCiLiveLogTail(input, createdAt = now()) {
|
|
2448
|
+
if (input.runId === "") throw new Error("a live log tail needs a run id");
|
|
2449
|
+
assertExpiration(createdAt, input.expiresAt, CI_MAX_LIVE_LOG_TAIL_TTL);
|
|
2450
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2451
|
+
for (const job of input.jobs) {
|
|
2452
|
+
if (job.job === "") throw new Error("a live log tail job needs a job id");
|
|
2453
|
+
if (job.job === CI_RUNNER_CHANNEL_KEY) {
|
|
2454
|
+
throw new Error(
|
|
2455
|
+
`the runner channel is not a job: ${CI_RUNNER_CHANNEL_KEY} may not appear in jobs`
|
|
2456
|
+
);
|
|
2457
|
+
}
|
|
2458
|
+
if (seen.has(job.job)) {
|
|
2459
|
+
throw new Error(`job ${job.job} appears twice in one live log tail`);
|
|
2460
|
+
}
|
|
2461
|
+
seen.add(job.job);
|
|
2462
|
+
assertOmitted(job.omitted, `job ${job.job}`);
|
|
2463
|
+
}
|
|
2464
|
+
if (input.runner) assertOmitted(input.runner.omitted, "runner channel");
|
|
2465
|
+
const content = {
|
|
2466
|
+
jobs: input.jobs.map((j) => ({
|
|
2467
|
+
job: j.job,
|
|
2468
|
+
tail: j.tail,
|
|
2469
|
+
omitted: j.omitted
|
|
2470
|
+
}))
|
|
2471
|
+
};
|
|
2472
|
+
if (input.runner) {
|
|
2473
|
+
content.runner = {
|
|
2474
|
+
tail: input.runner.tail,
|
|
2475
|
+
omitted: input.runner.omitted
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
return {
|
|
2479
|
+
kind: CI_LIVE_LOG_TAIL_KIND,
|
|
2480
|
+
content: JSON.stringify(content),
|
|
2481
|
+
tags: [
|
|
2482
|
+
...commonTriggerTags(input.trigger),
|
|
2483
|
+
["d", input.runId],
|
|
2484
|
+
["expiration", String(input.expiresAt)]
|
|
2485
|
+
],
|
|
2486
|
+
created_at: createdAt
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2489
|
+
function isRecord(value) {
|
|
2490
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2491
|
+
}
|
|
2492
|
+
function parseOmitted(value) {
|
|
2493
|
+
if (value === void 0) return 0;
|
|
2494
|
+
if (!Number.isSafeInteger(value) || value < 0) return null;
|
|
2495
|
+
return value;
|
|
2496
|
+
}
|
|
2497
|
+
function parseTailChannel(value) {
|
|
2498
|
+
if (!isRecord(value)) return null;
|
|
2499
|
+
const omitted = parseOmitted(value["omitted"]);
|
|
2500
|
+
const tail = value["tail"];
|
|
2501
|
+
if (typeof tail !== "string" || omitted === null) return null;
|
|
2502
|
+
return { tail, omitted };
|
|
2503
|
+
}
|
|
2504
|
+
function parseCiLiveLogTail(ev) {
|
|
2505
|
+
if (ev.kind !== CI_LIVE_LOG_TAIL_KIND) return null;
|
|
2506
|
+
const { tags } = ev;
|
|
2507
|
+
const trigger = parseCiTriggerContext(tags);
|
|
2508
|
+
if (!trigger) return null;
|
|
2509
|
+
const d = single(tags, "d");
|
|
2510
|
+
const exp = single(tags, "expiration");
|
|
2511
|
+
if (!d || !d[1] || !exp) return null;
|
|
2512
|
+
const expiresAt = Number(exp[1]);
|
|
2513
|
+
if (!Number.isInteger(expiresAt) || expiresAt <= ev.created_at || expiresAt - ev.created_at > CI_MAX_LIVE_LOG_TAIL_TTL) {
|
|
2514
|
+
return null;
|
|
2515
|
+
}
|
|
2516
|
+
let content;
|
|
2517
|
+
try {
|
|
2518
|
+
content = JSON.parse(ev.content);
|
|
2519
|
+
} catch {
|
|
2520
|
+
return null;
|
|
2521
|
+
}
|
|
2522
|
+
if (!isRecord(content) || !Array.isArray(content["jobs"])) return null;
|
|
2523
|
+
const jobs = [];
|
|
2524
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2525
|
+
for (const entry of content["jobs"]) {
|
|
2526
|
+
if (!isRecord(entry)) return null;
|
|
2527
|
+
const job = entry["job"];
|
|
2528
|
+
if (typeof job !== "string" || job === "") return null;
|
|
2529
|
+
if (job === CI_RUNNER_CHANNEL_KEY || seen.has(job)) return null;
|
|
2530
|
+
const channel = parseTailChannel(entry);
|
|
2531
|
+
if (!channel) return null;
|
|
2532
|
+
seen.add(job);
|
|
2533
|
+
jobs.push({ job, tail: channel.tail, omitted: channel.omitted });
|
|
2534
|
+
}
|
|
2535
|
+
let runner;
|
|
2536
|
+
if (content["runner"] !== void 0) {
|
|
2537
|
+
const parsed = parseTailChannel(content["runner"]);
|
|
2538
|
+
if (!parsed) return null;
|
|
2539
|
+
runner = parsed;
|
|
2540
|
+
}
|
|
2541
|
+
const result = {
|
|
2542
|
+
eventId: ev.id,
|
|
2543
|
+
pubkey: ev.pubkey.toLowerCase(),
|
|
2544
|
+
createdAt: ev.created_at,
|
|
2545
|
+
trigger,
|
|
2546
|
+
runId: d[1],
|
|
2547
|
+
jobs,
|
|
2548
|
+
expiresAt
|
|
2549
|
+
};
|
|
2550
|
+
if (runner) result.runner = runner;
|
|
2551
|
+
return result;
|
|
2552
|
+
}
|
|
2008
2553
|
|
|
2009
2554
|
// src/ci/secrets.ts
|
|
2010
2555
|
import { v2 as nip44 } from "nostr-tools/nip44";
|
|
@@ -2244,7 +2789,7 @@ var WORKFLOW_FILE_RE = /\.ya?ml$/i;
|
|
|
2244
2789
|
function sha256Hex(bytes) {
|
|
2245
2790
|
return createHash2("sha256").update(bytes).digest("hex");
|
|
2246
2791
|
}
|
|
2247
|
-
function
|
|
2792
|
+
function isRecord2(value) {
|
|
2248
2793
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2249
2794
|
}
|
|
2250
2795
|
function stringList(value) {
|
|
@@ -2255,7 +2800,7 @@ function stringList(value) {
|
|
|
2255
2800
|
return void 0;
|
|
2256
2801
|
}
|
|
2257
2802
|
function refFilters(value) {
|
|
2258
|
-
if (!
|
|
2803
|
+
if (!isRecord2(value)) return {};
|
|
2259
2804
|
const out = {};
|
|
2260
2805
|
const branches = stringList(value["branches"]);
|
|
2261
2806
|
const tags = stringList(value["tags"]);
|
|
@@ -2273,17 +2818,17 @@ function parseTriggers(on) {
|
|
|
2273
2818
|
};
|
|
2274
2819
|
if (typeof on === "string") enable(on, void 0);
|
|
2275
2820
|
else if (Array.isArray(on)) for (const name of on) enable(name, void 0);
|
|
2276
|
-
else if (
|
|
2821
|
+
else if (isRecord2(on)) {
|
|
2277
2822
|
for (const [name, options] of Object.entries(on)) enable(name, options);
|
|
2278
2823
|
}
|
|
2279
2824
|
return triggers;
|
|
2280
2825
|
}
|
|
2281
2826
|
function parseJobs(jobs) {
|
|
2282
|
-
if (!
|
|
2827
|
+
if (!isRecord2(jobs)) return [];
|
|
2283
2828
|
const out = [];
|
|
2284
2829
|
for (const [id, spec] of Object.entries(jobs)) {
|
|
2285
2830
|
const job = { id };
|
|
2286
|
-
if (
|
|
2831
|
+
if (isRecord2(spec)) {
|
|
2287
2832
|
if (typeof spec["name"] === "string") job.name = spec["name"];
|
|
2288
2833
|
const runsOn = stringList(spec["runs-on"]);
|
|
2289
2834
|
if (runsOn) job.runsOn = runsOn;
|
|
@@ -2309,7 +2854,7 @@ function parseWorkflow(path, content) {
|
|
|
2309
2854
|
parseError: err instanceof Error ? err.message : String(err)
|
|
2310
2855
|
};
|
|
2311
2856
|
}
|
|
2312
|
-
if (!
|
|
2857
|
+
if (!isRecord2(doc)) {
|
|
2313
2858
|
return { ...base, parseError: "workflow is not a YAML mapping" };
|
|
2314
2859
|
}
|
|
2315
2860
|
const on = doc["on"] ?? doc["true"];
|
|
@@ -2886,7 +3431,7 @@ function emitCliError(io, json, command, err) {
|
|
|
2886
3431
|
|
|
2887
3432
|
// src/cli/ci-status.ts
|
|
2888
3433
|
import { parseArgs as parseArgs2 } from "util";
|
|
2889
|
-
import { REPOSITORY_ANNOUNCEMENT_KIND } from "@toon-protocol/core/nip34";
|
|
3434
|
+
import { REPOSITORY_ANNOUNCEMENT_KIND as REPOSITORY_ANNOUNCEMENT_KIND2 } from "@toon-protocol/core/nip34";
|
|
2890
3435
|
|
|
2891
3436
|
// src/cli/git-config.ts
|
|
2892
3437
|
import { execFile as execFile3 } from "child_process";
|
|
@@ -3541,7 +4086,7 @@ async function runCiStatus(args, deps) {
|
|
|
3541
4086
|
"#a": [repoAddr]
|
|
3542
4087
|
},
|
|
3543
4088
|
{
|
|
3544
|
-
kinds: [
|
|
4089
|
+
kinds: [REPOSITORY_ANNOUNCEMENT_KIND2],
|
|
3545
4090
|
authors: [owner],
|
|
3546
4091
|
"#d": [repoId]
|
|
3547
4092
|
}
|
|
@@ -3550,7 +4095,7 @@ async function runCiStatus(args, deps) {
|
|
|
3550
4095
|
);
|
|
3551
4096
|
let announce = null;
|
|
3552
4097
|
for (const ev of events) {
|
|
3553
|
-
if (ev.kind !==
|
|
4098
|
+
if (ev.kind !== REPOSITORY_ANNOUNCEMENT_KIND2 || ev.pubkey.toLowerCase() !== owner)
|
|
3554
4099
|
continue;
|
|
3555
4100
|
if (!ev.tags.some((t) => t[0] === "d" && t[1] === repoId)) continue;
|
|
3556
4101
|
if (announce === null || ev.created_at > announce.created_at || ev.created_at === announce.created_at && ev.id < announce.id) {
|
|
@@ -3646,6 +4191,14 @@ export {
|
|
|
3646
4191
|
createGitTree,
|
|
3647
4192
|
createGitCommit,
|
|
3648
4193
|
createGitTag,
|
|
4194
|
+
RELAYS_TAG,
|
|
4195
|
+
WEB_TAG,
|
|
4196
|
+
EARLIEST_UNIQUE_COMMIT_MARKER,
|
|
4197
|
+
amendRepoAnnouncement,
|
|
4198
|
+
buildRepoAnnouncement,
|
|
4199
|
+
earliestUniqueCommit,
|
|
4200
|
+
conformanceEdits,
|
|
4201
|
+
diffAnnouncementTags,
|
|
3649
4202
|
GitError,
|
|
3650
4203
|
GitRepoReader,
|
|
3651
4204
|
DEFAULT_CONCURRENCY,
|
|
@@ -3683,10 +4236,16 @@ export {
|
|
|
3683
4236
|
CI_JOB_RESULT_KIND,
|
|
3684
4237
|
CI_WORKFLOW_RESULT_KIND,
|
|
3685
4238
|
CI_WORKFLOW_PROGRESS_KIND,
|
|
4239
|
+
CI_LIVE_LOG_TAIL_KIND,
|
|
3686
4240
|
CI_RUNNER_FAMILY,
|
|
3687
4241
|
CI_SOFTWARE,
|
|
3688
4242
|
CI_MAX_ADVERTISEMENT_TTL,
|
|
3689
4243
|
CI_MAX_PROGRESS_TTL,
|
|
4244
|
+
CI_MAX_LIVE_LOG_TAIL_TTL,
|
|
4245
|
+
CI_LIVE_LOG_TAIL_INTERVAL_MS,
|
|
4246
|
+
CI_LIVE_LOG_TAIL_JOB_BYTES,
|
|
4247
|
+
CI_LIVE_LOG_TAIL_MAX_BYTES,
|
|
4248
|
+
CI_RUNNER_CHANNEL_KEY,
|
|
3690
4249
|
isCiConclusion,
|
|
3691
4250
|
repoAddress,
|
|
3692
4251
|
parseRepoAddress,
|
|
@@ -3709,6 +4268,11 @@ export {
|
|
|
3709
4268
|
parseCiWorkflowResult,
|
|
3710
4269
|
buildCiWorkflowProgress,
|
|
3711
4270
|
parseCiWorkflowProgress,
|
|
4271
|
+
liveLogTailBudget,
|
|
4272
|
+
liveLogTailEventBudget,
|
|
4273
|
+
sliceLogTail,
|
|
4274
|
+
buildCiLiveLogTail,
|
|
4275
|
+
parseCiLiveLogTail,
|
|
3712
4276
|
SECRET_NAME_RE,
|
|
3713
4277
|
RESERVED_SECRET_NAMES,
|
|
3714
4278
|
MAX_SECRET_NAMES,
|
|
@@ -3759,4 +4323,4 @@ export {
|
|
|
3759
4323
|
assembleCiStatus,
|
|
3760
4324
|
runCiStatus
|
|
3761
4325
|
};
|
|
3762
|
-
//# sourceMappingURL=chunk-
|
|
4326
|
+
//# sourceMappingURL=chunk-3IVO2MSZ.js.map
|