@nickmeriano/task 0.11.0 → 0.12.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 +175 -1
- package/dist/board/github.d.ts +68 -0
- package/dist/board/github.d.ts.map +1 -0
- package/dist/board/github.js +112 -0
- package/dist/board/github.js.map +1 -0
- package/dist/board/handler.d.ts +50 -0
- package/dist/board/handler.d.ts.map +1 -0
- package/dist/board/handler.js +183 -0
- package/dist/board/handler.js.map +1 -0
- package/dist/board/handler.test.d.ts +11 -0
- package/dist/board/handler.test.d.ts.map +1 -0
- package/dist/board/handler.test.js +229 -0
- package/dist/board/handler.test.js.map +1 -0
- package/dist/board/pages-function.d.ts +23 -0
- package/dist/board/pages-function.d.ts.map +1 -0
- package/dist/board/pages-function.js +34 -0
- package/dist/board/pages-function.js.map +1 -0
- package/dist/board/source.d.ts +118 -0
- package/dist/board/source.d.ts.map +1 -0
- package/dist/board/source.js +333 -0
- package/dist/board/source.js.map +1 -0
- package/dist/board/source.test.d.ts +12 -0
- package/dist/board/source.test.d.ts.map +1 -0
- package/dist/board/source.test.js +165 -0
- package/dist/board/source.test.js.map +1 -0
- package/dist/board/tar.d.ts +28 -0
- package/dist/board/tar.d.ts.map +1 -0
- package/dist/board/tar.js +188 -0
- package/dist/board/tar.js.map +1 -0
- package/dist/board/tar.test.d.ts +9 -0
- package/dist/board/tar.test.d.ts.map +1 -0
- package/dist/board/tar.test.js +110 -0
- package/dist/board/tar.test.js.map +1 -0
- package/dist/cli.js +37 -2
- package/dist/cli.js.map +1 -1
- package/dist/export.d.ts +31 -0
- package/dist/export.d.ts.map +1 -1
- package/dist/export.js +61 -1
- package/dist/export.js.map +1 -1
- package/dist/export.test.js +80 -1
- package/dist/export.test.js.map +1 -1
- package/dist/functions/board.js +709 -0
- package/dist/git-serve.d.ts +14 -1
- package/dist/git-serve.d.ts.map +1 -1
- package/dist/git-serve.js +30 -2
- package/dist/git-serve.js.map +1 -1
- package/dist/git-serve.test.d.ts +1 -0
- package/dist/git-serve.test.d.ts.map +1 -1
- package/dist/git-serve.test.js +36 -1
- package/dist/git-serve.test.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/skill/SKILL.md +10 -3
- package/src/board/github.ts +151 -0
- package/src/board/handler.test.ts +276 -0
- package/src/board/handler.ts +228 -0
- package/src/board/pages-function.ts +42 -0
- package/src/board/source.test.ts +203 -0
- package/src/board/source.ts +422 -0
- package/src/board/tar.test.ts +128 -0
- package/src/board/tar.ts +199 -0
- package/src/cli.ts +36 -2
- package/src/export.test.ts +108 -1
- package/src/export.ts +79 -1
- package/src/git-serve.test.ts +41 -1
- package/src/git-serve.ts +30 -2
- package/src/index.ts +10 -0
- package/ui/dist/assets/index-B8M_DaOt.js +229 -0
- package/ui/dist/index.html +1 -1
- package/ui/dist/assets/index-mJmm4sWq.js +0 -229
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A minimal streaming tar reader — just enough to walk a GitHub repository
|
|
3
|
+
* tarball. One pass, bounded memory: every entry's *path* is recorded, but
|
|
4
|
+
* bytes are kept only for the paths the caller's `keep` predicate selects;
|
|
5
|
+
* everything else is discarded chunk by chunk as it streams through, so a big
|
|
6
|
+
* repository costs no more memory than its boards.
|
|
7
|
+
*
|
|
8
|
+
* Understands what GitHub's tarballs actually contain: ustar headers with the
|
|
9
|
+
* split `prefix` field, a leading `pax_global_header`, and pax extended
|
|
10
|
+
* headers (`x`) or GNU longname entries (`L`) for paths longer than the
|
|
11
|
+
* header's 100 bytes. Checksums are not verified — the bytes come off a TLS
|
|
12
|
+
* connection from GitHub, not a tape from 1988.
|
|
13
|
+
*/
|
|
14
|
+
export class TarError extends Error {
|
|
15
|
+
}
|
|
16
|
+
const BLOCK = 512;
|
|
17
|
+
/** Reads exact byte counts off a stream, buffering only what's unconsumed. */
|
|
18
|
+
class ByteReader {
|
|
19
|
+
reader;
|
|
20
|
+
chunks = [];
|
|
21
|
+
have = 0;
|
|
22
|
+
done = false;
|
|
23
|
+
constructor(stream) {
|
|
24
|
+
this.reader = stream.getReader();
|
|
25
|
+
}
|
|
26
|
+
async pull() {
|
|
27
|
+
if (this.done)
|
|
28
|
+
return false;
|
|
29
|
+
const { done, value } = await this.reader.read();
|
|
30
|
+
if (done) {
|
|
31
|
+
this.done = true;
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (value && value.length > 0) {
|
|
35
|
+
this.chunks.push(value);
|
|
36
|
+
this.have += value.length;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
/** Exactly `n` bytes; null at a clean end-of-stream, throws mid-entry. */
|
|
41
|
+
async read(n) {
|
|
42
|
+
while (this.have < n) {
|
|
43
|
+
if (!(await this.pull())) {
|
|
44
|
+
if (this.have === 0)
|
|
45
|
+
return null;
|
|
46
|
+
throw new TarError("truncated archive");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const out = new Uint8Array(n);
|
|
50
|
+
let filled = 0;
|
|
51
|
+
while (filled < n) {
|
|
52
|
+
const head = this.chunks[0];
|
|
53
|
+
const take = Math.min(head.length, n - filled);
|
|
54
|
+
out.set(head.subarray(0, take), filled);
|
|
55
|
+
filled += take;
|
|
56
|
+
if (take === head.length)
|
|
57
|
+
this.chunks.shift();
|
|
58
|
+
else
|
|
59
|
+
this.chunks[0] = head.subarray(take);
|
|
60
|
+
}
|
|
61
|
+
this.have -= n;
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
/** Discard `n` bytes without accumulating them. */
|
|
65
|
+
async skip(n) {
|
|
66
|
+
while (n > 0) {
|
|
67
|
+
if (this.chunks.length === 0) {
|
|
68
|
+
if (!(await this.pull()))
|
|
69
|
+
throw new TarError("truncated archive");
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const head = this.chunks[0];
|
|
73
|
+
const take = Math.min(head.length, n);
|
|
74
|
+
if (take === head.length)
|
|
75
|
+
this.chunks.shift();
|
|
76
|
+
else
|
|
77
|
+
this.chunks[0] = head.subarray(take);
|
|
78
|
+
this.have -= take;
|
|
79
|
+
n -= take;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const decoder = new TextDecoder();
|
|
84
|
+
function field(header, start, length) {
|
|
85
|
+
const slice = header.subarray(start, start + length);
|
|
86
|
+
const nul = slice.indexOf(0);
|
|
87
|
+
return decoder.decode(nul < 0 ? slice : slice.subarray(0, nul));
|
|
88
|
+
}
|
|
89
|
+
function octal(header, start, length) {
|
|
90
|
+
const text = field(header, start, length).trim();
|
|
91
|
+
if (text === "")
|
|
92
|
+
return 0;
|
|
93
|
+
const value = parseInt(text, 8);
|
|
94
|
+
if (Number.isNaN(value) || value < 0)
|
|
95
|
+
throw new TarError("bad size field");
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
/** The `path=` record of a pax extended header, if it has one. */
|
|
99
|
+
function paxPath(data) {
|
|
100
|
+
// Records are "<len> <key>=<value>\n", len covering the whole record.
|
|
101
|
+
const text = decoder.decode(data);
|
|
102
|
+
for (let at = 0; at < text.length;) {
|
|
103
|
+
const space = text.indexOf(" ", at);
|
|
104
|
+
if (space < 0)
|
|
105
|
+
break;
|
|
106
|
+
const length = Number(text.slice(at, space));
|
|
107
|
+
if (!Number.isInteger(length) || length <= 0)
|
|
108
|
+
break;
|
|
109
|
+
const record = text.slice(space + 1, at + length);
|
|
110
|
+
if (record.startsWith("path="))
|
|
111
|
+
return record.slice(5).replace(/\n$/, "");
|
|
112
|
+
at += length;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Walk a (decompressed) tar stream. Paths are reported with the archive's
|
|
118
|
+
* root directory stripped — GitHub tarballs wrap everything in
|
|
119
|
+
* `<owner>-<repo>-<sha>/` — matching what a git tree listing would say.
|
|
120
|
+
*/
|
|
121
|
+
export async function readTar(stream, keep) {
|
|
122
|
+
const bytes = new ByteReader(stream);
|
|
123
|
+
const paths = [];
|
|
124
|
+
const files = new Map();
|
|
125
|
+
let overridePath = null;
|
|
126
|
+
for (;;) {
|
|
127
|
+
const header = await bytes.read(BLOCK);
|
|
128
|
+
if (header === null)
|
|
129
|
+
break;
|
|
130
|
+
if (header.every((b) => b === 0)) {
|
|
131
|
+
// End marker: two zero blocks, then maybe padding. Nothing left to parse.
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
const size = octal(header, 124, 12);
|
|
135
|
+
const padded = Math.ceil(size / BLOCK) * BLOCK;
|
|
136
|
+
const type = header[156];
|
|
137
|
+
// Extended headers name the *next* entry; the global one ('g') is skipped.
|
|
138
|
+
if (type === 0x78 /* x */ || type === 0x4c /* L */) {
|
|
139
|
+
const data = await bytes.read(size);
|
|
140
|
+
if (data === null)
|
|
141
|
+
throw new TarError("truncated archive");
|
|
142
|
+
overridePath =
|
|
143
|
+
type === 0x78 ? (paxPath(data) ?? overridePath) : decoder.decode(data).replace(/\0+$/, "");
|
|
144
|
+
await bytes.skip(padded - size);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
let name = field(header, 0, 100);
|
|
148
|
+
// ustar splits long paths into prefix + name.
|
|
149
|
+
const prefix = field(header, 257, 6).startsWith("ustar") ? field(header, 345, 155) : "";
|
|
150
|
+
if (prefix)
|
|
151
|
+
name = `${prefix}/${name}`;
|
|
152
|
+
if (overridePath !== null) {
|
|
153
|
+
name = overridePath;
|
|
154
|
+
overridePath = null;
|
|
155
|
+
}
|
|
156
|
+
// Regular file entries only — directories, links and the global header
|
|
157
|
+
// carry no board bytes. Type '0' or the ancient NUL both mean "file".
|
|
158
|
+
if (type !== 0x30 && type !== 0) {
|
|
159
|
+
await bytes.skip(padded);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
// Strip the tarball's root directory. A path without one (no slash) is
|
|
163
|
+
// the metadata GitHub puts at the top level — not repository content.
|
|
164
|
+
const slash = name.indexOf("/");
|
|
165
|
+
if (slash < 0) {
|
|
166
|
+
await bytes.skip(padded);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const path = name.slice(slash + 1);
|
|
170
|
+
if (path === "") {
|
|
171
|
+
await bytes.skip(padded);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
paths.push(path);
|
|
175
|
+
if (keep(path)) {
|
|
176
|
+
const data = await bytes.read(size);
|
|
177
|
+
if (data === null)
|
|
178
|
+
throw new TarError("truncated archive");
|
|
179
|
+
files.set(path, data);
|
|
180
|
+
await bytes.skip(padded - size);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
await bytes.skip(padded);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return { paths, files };
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=tar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tar.js","sourceRoot":"","sources":["../../src/board/tar.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tar walk is hand-written against a binary format, so it's tested the
|
|
3
|
+
* way the SQLite reader used to be: against archives built independently —
|
|
4
|
+
* here by a minimal tar *writer* that produces the same shapes GitHub's
|
|
5
|
+
* tarballs contain (ustar headers, a pax_global_header, pax path overrides,
|
|
6
|
+
* a wrapping root directory), gzipped through node:zlib.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=tar.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tar.test.d.ts","sourceRoot":"","sources":["../../src/board/tar.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tar walk is hand-written against a binary format, so it's tested the
|
|
3
|
+
* way the SQLite reader used to be: against archives built independently —
|
|
4
|
+
* here by a minimal tar *writer* that produces the same shapes GitHub's
|
|
5
|
+
* tarballs contain (ustar headers, a pax_global_header, pax path overrides,
|
|
6
|
+
* a wrapping root directory), gzipped through node:zlib.
|
|
7
|
+
*/
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { test } from "node:test";
|
|
10
|
+
import { gzipSync } from "node:zlib";
|
|
11
|
+
import { readTar, TarError } from "./tar.js";
|
|
12
|
+
const encoder = new TextEncoder();
|
|
13
|
+
function header(name, type, size) {
|
|
14
|
+
const block = new Uint8Array(512);
|
|
15
|
+
block.set(encoder.encode(name).subarray(0, 100), 0);
|
|
16
|
+
block.set(encoder.encode("0000644\0"), 100);
|
|
17
|
+
block.set(encoder.encode(size.toString(8).padStart(11, "0") + "\0"), 124);
|
|
18
|
+
block.set(encoder.encode("00000000000\0"), 136);
|
|
19
|
+
block[156] = type.charCodeAt(0);
|
|
20
|
+
block.set(encoder.encode("ustar\x0000"), 257);
|
|
21
|
+
// Checksum: field treated as spaces while summing.
|
|
22
|
+
block.set(encoder.encode(" "), 148);
|
|
23
|
+
const sum = block.reduce((a, b) => a + b, 0);
|
|
24
|
+
block.set(encoder.encode(sum.toString(8).padStart(6, "0") + "\0 "), 148);
|
|
25
|
+
return block;
|
|
26
|
+
}
|
|
27
|
+
function tarball(entries) {
|
|
28
|
+
const blocks = [];
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
if (entry.paxPath) {
|
|
31
|
+
const record = ` path=${entry.paxPath}\n`;
|
|
32
|
+
const body = `${record.length + String(record.length + 2).length}${record}`;
|
|
33
|
+
const bytes = encoder.encode(body);
|
|
34
|
+
blocks.push(header("./PaxHeaders/x", "x", bytes.length), pad(bytes));
|
|
35
|
+
}
|
|
36
|
+
const data = encoder.encode(entry.data ?? "");
|
|
37
|
+
blocks.push(header(entry.name, entry.type ?? "0", data.length));
|
|
38
|
+
if (data.length > 0)
|
|
39
|
+
blocks.push(pad(data));
|
|
40
|
+
}
|
|
41
|
+
blocks.push(new Uint8Array(1024));
|
|
42
|
+
const total = blocks.reduce((n, b) => n + b.length, 0);
|
|
43
|
+
const out = new Uint8Array(total);
|
|
44
|
+
let at = 0;
|
|
45
|
+
for (const block of blocks) {
|
|
46
|
+
out.set(block, at);
|
|
47
|
+
at += block.length;
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
function pad(data) {
|
|
52
|
+
const padded = new Uint8Array(Math.ceil(data.length / 512) * 512);
|
|
53
|
+
padded.set(data);
|
|
54
|
+
return padded;
|
|
55
|
+
}
|
|
56
|
+
function stream(bytes) {
|
|
57
|
+
return new Response(gzipSync(bytes)).body.pipeThrough(new DecompressionStream("gzip"));
|
|
58
|
+
}
|
|
59
|
+
test("walks a GitHub-shaped tarball: root stripped, kept bytes kept, rest skipped", async () => {
|
|
60
|
+
const bytes = tarball([
|
|
61
|
+
{ name: "pax_global_header", type: "g", data: "52 comment=1234\n" },
|
|
62
|
+
{ name: "owner-repo-abc123/", type: "5" },
|
|
63
|
+
{ name: "owner-repo-abc123/README.md", data: "# hi" },
|
|
64
|
+
{ name: "owner-repo-abc123/.task/config.json", data: '{"name":"x","prefix":"X","version":2}' },
|
|
65
|
+
{ name: "owner-repo-abc123/.task/tickets/1/ticket.md", data: "---\nstatus: todo\n---\n\n# T\n" },
|
|
66
|
+
{ name: "owner-repo-abc123/big/blob.bin", data: "z".repeat(5000) },
|
|
67
|
+
{ name: "owner-repo-abc123/pkg/.task/config.json", data: "{}" },
|
|
68
|
+
]);
|
|
69
|
+
const { paths, files } = await readTar(stream(bytes), (path) => path.includes(".task/"));
|
|
70
|
+
assert.deepEqual(paths, [
|
|
71
|
+
"README.md",
|
|
72
|
+
".task/config.json",
|
|
73
|
+
".task/tickets/1/ticket.md",
|
|
74
|
+
"big/blob.bin",
|
|
75
|
+
"pkg/.task/config.json",
|
|
76
|
+
]);
|
|
77
|
+
assert.deepEqual([...files.keys()], [
|
|
78
|
+
".task/config.json",
|
|
79
|
+
".task/tickets/1/ticket.md",
|
|
80
|
+
"pkg/.task/config.json",
|
|
81
|
+
]);
|
|
82
|
+
assert.equal(new TextDecoder().decode(files.get(".task/tickets/1/ticket.md")), "---\nstatus: todo\n---\n\n# T\n");
|
|
83
|
+
});
|
|
84
|
+
test("pax path records override the header name, as GitHub does for long paths", async () => {
|
|
85
|
+
const longPath = `owner-repo-abc123/${"deeply/".repeat(20)}.task/config.json`;
|
|
86
|
+
const bytes = tarball([
|
|
87
|
+
{ name: "owner-repo-abc123/x", paxPath: longPath, data: "{}" },
|
|
88
|
+
]);
|
|
89
|
+
const { paths, files } = await readTar(stream(bytes), () => true);
|
|
90
|
+
assert.deepEqual(paths, [longPath.slice("owner-repo-abc123/".length)]);
|
|
91
|
+
assert.equal(files.size, 1);
|
|
92
|
+
});
|
|
93
|
+
test("ustar prefix fields are joined back onto the name", async () => {
|
|
94
|
+
const bytes = tarball([{ name: "owner-repo-abc123/plain.txt", data: "ok" }]);
|
|
95
|
+
// Craft one entry using the prefix field by hand.
|
|
96
|
+
const block = header("tail.txt", "0", 2);
|
|
97
|
+
block.set(encoder.encode("owner-repo-abc123/some/dir"), 345);
|
|
98
|
+
const data = pad(encoder.encode("ok"));
|
|
99
|
+
const joined = new Uint8Array(bytes.length + block.length + data.length);
|
|
100
|
+
joined.set(block, 0);
|
|
101
|
+
joined.set(data, block.length);
|
|
102
|
+
joined.set(bytes, block.length + data.length);
|
|
103
|
+
const { paths } = await readTar(stream(joined), () => false);
|
|
104
|
+
assert.deepEqual(paths, ["some/dir/tail.txt", "plain.txt"]);
|
|
105
|
+
});
|
|
106
|
+
test("a truncated archive throws instead of returning half a repository", async () => {
|
|
107
|
+
const bytes = tarball([{ name: "owner-repo-abc123/a.txt", data: "hello" }]);
|
|
108
|
+
await assert.rejects(readTar(stream(bytes.subarray(0, 600)), () => true), TarError);
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=tar.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tar.test.js","sourceRoot":"","sources":["../../src/board/tar.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -60,6 +60,7 @@ const BOOLEAN_FLAGS = new Set([
|
|
|
60
60
|
"fix",
|
|
61
61
|
"next",
|
|
62
62
|
"force",
|
|
63
|
+
"no-functions",
|
|
63
64
|
"reopen",
|
|
64
65
|
"lock-only",
|
|
65
66
|
"dry-run",
|
|
@@ -1287,6 +1288,24 @@ function cmdExport(args) {
|
|
|
1287
1288
|
// every asset copied.
|
|
1288
1289
|
const out = str(args.flags, "out") ?? join(serveRoot, DEFAULT_OUT_DIR);
|
|
1289
1290
|
const outDir = isAbsolute(out) ? out : resolve(process.cwd(), out);
|
|
1291
|
+
// The live proxy is written unless told not to. It is inert until the host
|
|
1292
|
+
// has a token — it answers 503 and the board reads the snapshot — so the
|
|
1293
|
+
// default costs nothing, and turning a deploy live becomes one secret
|
|
1294
|
+
// rather than a re-export. The repository it reads is baked in: the export
|
|
1295
|
+
// already knows (the same remote detection `task publish` uses), and one
|
|
1296
|
+
// fewer setting on the host is one fewer thing to get wrong.
|
|
1297
|
+
let functions;
|
|
1298
|
+
let noRemote = false;
|
|
1299
|
+
if (!args.flags["no-functions"]) {
|
|
1300
|
+
const slug = str(args.flags, "repo");
|
|
1301
|
+
const ref = slug ? parseSlug(slug) : detectRepo(serveRoot);
|
|
1302
|
+
if (slug && !ref)
|
|
1303
|
+
fail(`--repo must be owner/name — got ${JSON.stringify(slug)}`);
|
|
1304
|
+
if (ref)
|
|
1305
|
+
functions = { repo: `${ref.owner}/${ref.repo}` };
|
|
1306
|
+
else
|
|
1307
|
+
noRemote = true;
|
|
1308
|
+
}
|
|
1290
1309
|
let summary;
|
|
1291
1310
|
try {
|
|
1292
1311
|
summary = writeExport({
|
|
@@ -1295,6 +1314,7 @@ function cmdExport(args) {
|
|
|
1295
1314
|
uiDir: UI_DIR,
|
|
1296
1315
|
force: Boolean(args.flags.force),
|
|
1297
1316
|
base: str(args.flags, "base"),
|
|
1317
|
+
functions,
|
|
1298
1318
|
});
|
|
1299
1319
|
}
|
|
1300
1320
|
catch (error) {
|
|
@@ -1308,6 +1328,14 @@ function cmdExport(args) {
|
|
|
1308
1328
|
console.log(`Exported ${what}${summary.commit ? ` at ${summary.commit.slice(0, 7)}` : ""}`);
|
|
1309
1329
|
console.log(` ${summary.outDir}`);
|
|
1310
1330
|
console.log("Serve that directory from any static host — the board reads snapshot.json.");
|
|
1331
|
+
if (summary.functions && functions) {
|
|
1332
|
+
// One line, every run: this tier has no viewer auth, and the moment the
|
|
1333
|
+
// function is written is the moment to say so. The README says the rest.
|
|
1334
|
+
console.log(` ${summary.functions} reads ${functions.repo} live once the host has a TASK_GITHUB_TOKEN secret — to anyone who can reach it (README: "Live data for an exported board")`);
|
|
1335
|
+
}
|
|
1336
|
+
else if (noRemote) {
|
|
1337
|
+
console.log(" no live function written — no GitHub remote to read from; pass --repo owner/name to add one");
|
|
1338
|
+
}
|
|
1311
1339
|
}
|
|
1312
1340
|
/**
|
|
1313
1341
|
* `task publish` — the board, at a URL.
|
|
@@ -1584,7 +1612,7 @@ Usage
|
|
|
1584
1612
|
fails instead). Serves every board at or below
|
|
1585
1613
|
here — in a monorepo the header becomes a
|
|
1586
1614
|
board switcher
|
|
1587
|
-
task export [--out <dir>] [--base <path>] [--force]
|
|
1615
|
+
task export [--out <dir>] [--base <path>] [--force] [--no-functions | --repo <owner/name>]
|
|
1588
1616
|
write the board out as static files — the same
|
|
1589
1617
|
UI plus a snapshot.json it reads instead of an
|
|
1590
1618
|
API, so any static host serves it read-only
|
|
@@ -1596,7 +1624,14 @@ Usage
|
|
|
1596
1624
|
domain root. Re-exporting refreshes an earlier
|
|
1597
1625
|
export in place and leaves anything else in
|
|
1598
1626
|
there alone; a non-empty directory that isn't
|
|
1599
|
-
one needs --force
|
|
1627
|
+
one needs --force. Also writes a Cloudflare
|
|
1628
|
+
Pages Function that reads the repo live from
|
|
1629
|
+
GitHub: inert until the host has a
|
|
1630
|
+
TASK_GITHUB_TOKEN secret (a fine-grained PAT,
|
|
1631
|
+
Contents: read-only), at which point the same
|
|
1632
|
+
deploy upgrades itself — no viewer auth, mind.
|
|
1633
|
+
The repo comes from your remotes; --repo sets
|
|
1634
|
+
it, --no-functions skips the function
|
|
1600
1635
|
task publish [--public | --private] [--repo <owner/name>]
|
|
1601
1636
|
give this repo's board a URL at
|
|
1602
1637
|
task.nickmeriano.com, read-only, updated from
|