@catalyst-cloud/cli 0.14.6 → 0.14.8
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/CHANGELOG.md +14 -0
- package/README.md +7 -5
- package/dist/args.js +68 -18
- package/dist/browser.js +37 -3
- package/dist/cli.js +58 -12
- package/dist/config.js +17 -5
- package/dist/consent-browser.js +58 -0
- package/dist/daemon-authority-lifetime.js +171 -0
- package/dist/daemon-authority.js +216 -0
- package/dist/daemon-cache.js +348 -0
- package/dist/daemon-store.js +370 -0
- package/dist/daemon-supervisor.js +460 -0
- package/dist/env/onboard-draft-store.js +250 -0
- package/dist/env/onboard-draft.js +33 -0
- package/dist/env/selected-import-writer.js +163 -0
- package/dist/env/settings-authority.js +205 -0
- package/dist/events-cloud-tail.js +146 -0
- package/dist/events-cloud.js +619 -0
- package/dist/events.js +186 -31
- package/dist/oauth.js +129 -34
- package/dist/onboard-accounts.js +259 -0
- package/dist/onboard-automation-management.js +15 -0
- package/dist/onboard-bootstrap.js +162 -0
- package/dist/onboard-capabilities.js +15 -7
- package/dist/onboard-capacity.js +226 -0
- package/dist/onboard-existing.js +106 -32
- package/dist/onboard-file-snapshot.js +45 -0
- package/dist/onboard-github.js +14 -3
- package/dist/onboard-login-candidate.js +261 -0
- package/dist/onboard-next.js +139 -0
- package/dist/onboard-personal.js +14 -3
- package/dist/onboard-projects.js +350 -0
- package/dist/onboard-repositories.js +146 -37
- package/dist/onboard-runtime.js +108 -13
- package/dist/onboard-settings-context.js +141 -0
- package/dist/onboard-settings-import.js +127 -0
- package/dist/onboard-settings.js +234 -0
- package/dist/onboard-ui.js +85 -59
- package/dist/onboard-workflow.js +316 -0
- package/dist/onboard-workspace.js +14 -3
- package/dist/onboard.js +422 -39
- package/dist/owned-fetch.js +182 -0
- package/dist/pagination.js +13 -1
- package/dist/query.js +104 -31
- package/dist/ready.js +143 -35
- package/dist/replica.js +11 -4
- package/dist/repository-id.js +5 -0
- package/dist/sdk.js +33 -0
- package/dist/watch.js +2 -2
- package/package.json +3 -2
- package/skills/catalyst-github/SKILL.md +4 -2
- package/skills/catalyst-github/references/is-it-mergeable.md +1 -1
- package/skills/catalyst-github/references/local-replica.md +78 -0
- package/skills/catalyst-github/references/reading-from-the-cloud.md +24 -0
- package/skills/catalyst-github/references/what-a-pr-accumulates.md +1 -1
- package/skills/catalyst-github/scripts/is-it-mergeable.mjs +210 -45
- package/skills/catalyst-github/scripts/read-pr.mjs +51 -15
- package/skills/catalyst-linear/SKILL.md +5 -3
- package/skills/catalyst-linear/references/local-replica.md +91 -0
- package/skills/catalyst-linear/references/reading-a-ticket.md +3 -3
- package/skills/catalyst-linear/references/reading-from-the-cloud.md +38 -0
- package/skills/catalyst-linear/scripts/read-ticket.mjs +37 -12
- package/skills/catalyst-onboard/SKILL.md +6 -4
- package/skills/catalyst-onboard/references/local-replica.md +40 -0
- package/skills/catalyst-onboard/references/local-sync.md +8 -4
- package/skills/catalyst-onboard/scripts/local-sync.mjs +2 -2
- package/skills/catalyst-onboard/scripts/where-am-i.mjs +1 -1
- package/skills/run-this-project/SKILL.md +1 -1
- package/skills/unstick/SKILL.md +1 -1
- package/skills/what-needs-me/SKILL.md +1 -1
- package/skills/what-this-repo-needs/SKILL.md +1 -1
- package/skills/whats-happening/SKILL.md +4 -2
- package/skills/whats-happening/references/local-replica.md +51 -0
- package/skills/whats-happening/references/reading-from-the-cloud.md +21 -0
- package/skills/whats-happening/references/status-reply.md +1 -1
- package/skills/whats-happening/scripts/snapshot.mjs +104 -26
- package/vendor/settings/index.d.ts +4 -0
- package/vendor/settings/index.js +2937 -0
- package/vendor/settings/provenance.json +148 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { cloudEvents } from "./events-cloud.js";
|
|
2
|
+
// A lease causation names its ticket: `lease:<ticket>/<phase>#<nonce>` (event-index/fields.ts).
|
|
3
|
+
const CAUSED_BY_LEASE = /^lease:([^/]+)\/[^#]+#/;
|
|
4
|
+
// The index stores at most this many UTF-8 bytes of ticket; a longer value is not indexed.
|
|
5
|
+
const MAX_INDEXED_TICKET_BYTES = 64;
|
|
6
|
+
function boundedTicket(value) {
|
|
7
|
+
if (typeof value !== "string" || value.length === 0)
|
|
8
|
+
return null;
|
|
9
|
+
return new TextEncoder().encode(value).length > MAX_INDEXED_TICKET_BYTES ? null : value;
|
|
10
|
+
}
|
|
11
|
+
/** The ticket an event is about, resolved exactly as the cloud's event index resolves it
|
|
12
|
+
* (catalyst-cloud apps/mirror/src/event-index/fields.ts): a `ticket` entity's id, then
|
|
13
|
+
* `payload.ticket`, then the lease that caused it, each at most 64 bytes. A body-unavailable stub
|
|
14
|
+
* carries the index's own `ticket`. Nothing else counts, so `--from-cache` matches exactly what the
|
|
15
|
+
* cloud path returns. */
|
|
16
|
+
export function eventTicket(event) {
|
|
17
|
+
const e = event;
|
|
18
|
+
if (e.bodyUnavailable === true)
|
|
19
|
+
return boundedTicket(e.ticket);
|
|
20
|
+
const fromEntity = e.entity?.type === "ticket" ? boundedTicket(e.entity.id) : null;
|
|
21
|
+
const payload = e.payload && typeof e.payload === "object" ? e.payload : undefined;
|
|
22
|
+
const lease = typeof e.causationId === "string" ? CAUSED_BY_LEASE.exec(e.causationId) : null;
|
|
23
|
+
return fromEntity ?? boundedTicket(payload?.ticket) ?? boundedTicket(lease?.[1]);
|
|
24
|
+
}
|
|
25
|
+
/** The type and ticket filter, the same on the cloud and the local cache paths. */
|
|
26
|
+
export function eventMatches(filter) {
|
|
27
|
+
const ticket = filter.ticket?.toUpperCase();
|
|
28
|
+
return (event) => (!filter.type || event.type === filter.type) &&
|
|
29
|
+
(!ticket || eventTicket(event)?.toUpperCase() === ticket);
|
|
30
|
+
}
|
|
31
|
+
function pipeClosed(error) {
|
|
32
|
+
const code = error?.code;
|
|
33
|
+
return code === "EPIPE" || code === "ERR_STREAM_DESTROYED";
|
|
34
|
+
}
|
|
35
|
+
// Resolves once the line has left this process, so a consumer sees it before the next is read.
|
|
36
|
+
function writeLine(out, line, signal) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
if (signal.aborted)
|
|
39
|
+
return resolve();
|
|
40
|
+
const abort = () => resolve();
|
|
41
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
42
|
+
const settle = (error) => {
|
|
43
|
+
signal.removeEventListener("abort", abort);
|
|
44
|
+
if (error)
|
|
45
|
+
reject(error);
|
|
46
|
+
else
|
|
47
|
+
resolve();
|
|
48
|
+
};
|
|
49
|
+
try {
|
|
50
|
+
out.write(line, (error) => settle(error));
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
settle(error);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** Print every matching cloud event as one NDJSON line until aborted, matched, or the pipe closes. */
|
|
58
|
+
export async function tailCloudEvents(ctx, options, deps = {}) {
|
|
59
|
+
const { out } = options;
|
|
60
|
+
const filter = options.filter ?? {};
|
|
61
|
+
const filtered = eventMatches(filter);
|
|
62
|
+
const matches = (event) => filtered(event) && (options.where?.(event) ?? true);
|
|
63
|
+
const reader = new AbortController();
|
|
64
|
+
const stop = () => reader.abort(options.signal.reason);
|
|
65
|
+
if (options.signal.aborted)
|
|
66
|
+
stop();
|
|
67
|
+
else
|
|
68
|
+
options.signal.addEventListener("abort", stop, { once: true });
|
|
69
|
+
let closed = false;
|
|
70
|
+
let failure;
|
|
71
|
+
const broken = (error) => {
|
|
72
|
+
if (pipeClosed(error))
|
|
73
|
+
closed = true;
|
|
74
|
+
else
|
|
75
|
+
failure ??= error;
|
|
76
|
+
reader.abort(error);
|
|
77
|
+
};
|
|
78
|
+
out.on("error", broken);
|
|
79
|
+
let cursor = options.after;
|
|
80
|
+
let written = 0;
|
|
81
|
+
let matched;
|
|
82
|
+
// The server narrows what it sends when it can; every event is still matched here.
|
|
83
|
+
const serverFilter = {
|
|
84
|
+
...(filter.type ? { type: filter.type } : {}),
|
|
85
|
+
...(filter.ticket ? { ticket: filter.ticket.toUpperCase() } : {}),
|
|
86
|
+
};
|
|
87
|
+
try {
|
|
88
|
+
const events = (deps.events ?? cloudEvents)(ctx, { after: options.after, filter: serverFilter, signal: reader.signal }, deps);
|
|
89
|
+
for await (const event of events) {
|
|
90
|
+
if (reader.signal.aborted)
|
|
91
|
+
break;
|
|
92
|
+
// A reconnect may replay what was already printed; the sequence is the identity.
|
|
93
|
+
if (cursor !== undefined && event.sequence <= cursor)
|
|
94
|
+
continue;
|
|
95
|
+
if (matches(event)) {
|
|
96
|
+
await writeLine(out, `${JSON.stringify(event)}\n`, reader.signal);
|
|
97
|
+
// Aborted mid-write: the line was not confirmed, so it does not move the cursor.
|
|
98
|
+
if (reader.signal.aborted)
|
|
99
|
+
break;
|
|
100
|
+
written += 1;
|
|
101
|
+
cursor = event.sequence;
|
|
102
|
+
if (options.once) {
|
|
103
|
+
matched = event;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
cursor = event.sequence;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (pipeClosed(error))
|
|
113
|
+
closed = true;
|
|
114
|
+
else if (!reader.signal.aborted)
|
|
115
|
+
failure ??= error;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
options.signal.removeEventListener("abort", stop);
|
|
119
|
+
reader.abort();
|
|
120
|
+
// After EPIPE the stream can still emit its error; the listener stays so that cannot crash.
|
|
121
|
+
if (!closed)
|
|
122
|
+
out.off("error", broken);
|
|
123
|
+
}
|
|
124
|
+
if (failure !== undefined)
|
|
125
|
+
throw failure;
|
|
126
|
+
if (matched)
|
|
127
|
+
return { outcome: "matched", cursor, written, event: matched };
|
|
128
|
+
if (closed)
|
|
129
|
+
return { outcome: "closed", cursor, written };
|
|
130
|
+
return { outcome: options.signal.aborted ? "aborted" : "ended", cursor, written };
|
|
131
|
+
}
|
|
132
|
+
/** Print the first matching cloud event and stop, or report a timeout with nothing printed. */
|
|
133
|
+
export async function waitForCloudEvent(ctx, options, deps = {}) {
|
|
134
|
+
const timer = new AbortController();
|
|
135
|
+
const timeout = setTimeout(() => timer.abort(new Error("event wait timed out")), options.timeoutMs);
|
|
136
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timer.signal]) : timer.signal;
|
|
137
|
+
try {
|
|
138
|
+
const result = await tailCloudEvents(ctx, { after: options.after, filter: options.filter, out: options.out, signal, where: options.predicate, once: true }, deps);
|
|
139
|
+
if (result.outcome === "aborted" && timer.signal.aborted && !options.signal?.aborted)
|
|
140
|
+
return { ...result, outcome: "timeout" };
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
clearTimeout(timeout);
|
|
145
|
+
}
|
|
146
|
+
}
|