bikeshed-now 0.14.1 → 0.16.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 +39 -8
- package/package.json +1 -1
- package/src/client.mjs +2 -1
- package/src/index.mjs +224 -7
package/README.md
CHANGED
|
@@ -38,14 +38,16 @@ be using the same one: revoke it at `/account` once nothing needs it.
|
|
|
38
38
|
bikeshed publish proposal.md --title "Storage migration"
|
|
39
39
|
# published storage-migration v1 — https://bikeshed.now/d/storage-migration
|
|
40
40
|
|
|
41
|
-
# Somebody highlights a sentence and says what is wrong with it.
|
|
41
|
+
# Somebody highlights a sentence and says what is wrong with it. Wait for it:
|
|
42
|
+
# the cursor comes from `publish --json`, and every `changes` prints the next one.
|
|
43
|
+
bikeshed changes storage-migration --since <cursor> --wait
|
|
42
44
|
bikeshed comments storage-migration
|
|
43
45
|
|
|
44
46
|
# Fix that sentence, say what you changed in its thread, then publish the
|
|
45
47
|
# revision. Reply first: a thread belongs to the version it was written on.
|
|
46
48
|
bikeshed reply storage-migration <thread> "Rewritten to describe the dual-write period."
|
|
47
49
|
bikeshed resolve storage-migration <thread> # closes it, with a note saying you did
|
|
48
|
-
bikeshed update storage-migration proposal.md
|
|
50
|
+
bikeshed update storage-migration proposal.md --base v1 # refused if somebody published since
|
|
49
51
|
|
|
50
52
|
# Given "Can suggest" rather than "Can edit"? Propose the change instead, for a
|
|
51
53
|
# person to accept or reject.
|
|
@@ -77,8 +79,21 @@ bikeshed publish platform.md --id 01-platform
|
|
|
77
79
|
# published 01-platform v1 — https://bikeshed.now/d/01-platform
|
|
78
80
|
```
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
`publish` says where it filed the document and who can read it there.
|
|
83
|
+
`bikeshed folders` counts who can read each folder, and `bikeshed folder <id>`
|
|
84
|
+
names them: filing a document in a folder shares it with all of them, and
|
|
85
|
+
nobody is emailed.
|
|
86
|
+
|
|
87
|
+
Ids are shared by the whole deployment and never change, so choose one every
|
|
88
|
+
time: put the date first on something time-bound
|
|
89
|
+
(`2026-10-09-storage-migration-review`), and the project on something that
|
|
90
|
+
lives on (`billing-storage-migration`, not `migration`). If the id is taken, the
|
|
91
|
+
document is still published, at the id plus a few random characters, and the
|
|
92
|
+
CLI says so on stderr.
|
|
93
|
+
|
|
94
|
+
`bikeshed open <link>` opens a document in your browser, an unlisted one's key
|
|
95
|
+
included. It opens the browser on the machine that runs it, so an agent in CI
|
|
96
|
+
or a container should hand you the link instead.
|
|
82
97
|
|
|
83
98
|
A rename changes the title and leaves the address alone, so links already sent
|
|
84
99
|
keep working:
|
|
@@ -116,10 +131,26 @@ nearest has changed since you forked it. On a document you own it also counts
|
|
|
116
131
|
its forks. The author of what you fork sees that it was forked — a count, never
|
|
117
132
|
who.
|
|
118
133
|
|
|
119
|
-
##
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
134
|
+
## Manage access and folders
|
|
135
|
+
|
|
136
|
+
`bikeshed access document <id>` and `bikeshed access folder <id>` show the
|
|
137
|
+
owner, direct grants, inherited grants and effective membership. Add or change
|
|
138
|
+
a direct role with `--add email@example.com,team:design --role editor`; revoke
|
|
139
|
+
with `--remove email@example.com`. All four roles are supported. The owner
|
|
140
|
+
cannot be removed, and inherited grants must be changed at their source.
|
|
141
|
+
Newly effective readers receive an invitation when email is enabled.
|
|
142
|
+
|
|
143
|
+
`bikeshed visibility <id>` shows settings. Add `private`, `unlisted` or `public`
|
|
144
|
+
to change visibility, `--public-edit true|false` to change open editing, or
|
|
145
|
+
`--expires <ISO-8601 timestamp with timezone|none>` to set or clear expiry.
|
|
146
|
+
Omitted settings stay unchanged. These commands are owner-only, including on
|
|
147
|
+
expired documents; the returned URL includes an unlisted key when needed.
|
|
148
|
+
|
|
149
|
+
`bikeshed rename-folder <id> <name>` and
|
|
150
|
+
`bikeshed move-folder <id> <folder|root>` require edit rights.
|
|
151
|
+
`bikeshed delete-folder <id>` is owner-only: contents move to its parent or the
|
|
152
|
+
root, and access is recomputed. No documents are deleted. All commands support
|
|
153
|
+
`--json`. Existing sign-ins work without reconnecting.
|
|
123
154
|
|
|
124
155
|
## For agents
|
|
125
156
|
|
package/package.json
CHANGED
package/src/client.mjs
CHANGED
|
@@ -95,7 +95,7 @@ export async function callTool(origin, token, name, args = {}) {
|
|
|
95
95
|
* with `update`, as a new version of that document, with `k` for an unlisted
|
|
96
96
|
* one.
|
|
97
97
|
*/
|
|
98
|
-
export async function uploadFile(origin, token, path, { title, folder, id, update, k } = {}) {
|
|
98
|
+
export async function uploadFile(origin, token, path, { title, folder, id, update, k, base } = {}) {
|
|
99
99
|
const bytes = readFileSync(path);
|
|
100
100
|
const params = new URLSearchParams({ filename: basename(path) });
|
|
101
101
|
if (title) params.set("title", title);
|
|
@@ -103,6 +103,7 @@ export async function uploadFile(origin, token, path, { title, folder, id, updat
|
|
|
103
103
|
if (id) params.set("id", id);
|
|
104
104
|
if (update) params.set("update", update);
|
|
105
105
|
if (k) params.set("k", k);
|
|
106
|
+
if (base) params.set("baseVersion", base);
|
|
106
107
|
|
|
107
108
|
const res = await fetch(`${origin}/publish?${params}`, {
|
|
108
109
|
method: "POST",
|
package/src/index.mjs
CHANGED
|
@@ -54,6 +54,9 @@ const USAGE = `bikeshed ${VERSION} — publish and revise documents
|
|
|
54
54
|
bikeshed get <id> print a document's source
|
|
55
55
|
bikeshed comments <id> unresolved feedback, with its anchored text
|
|
56
56
|
(--version <v> for an earlier version's)
|
|
57
|
+
bikeshed changes <id> --since <cursor|last|all>
|
|
58
|
+
what happened since a cursor; --wait to
|
|
59
|
+
keep checking until something does
|
|
57
60
|
bikeshed comment <id> <text> --body <comment>
|
|
58
61
|
start a thread on a passage
|
|
59
62
|
bikeshed reply <id> <thread> <text> reply in a thread
|
|
@@ -79,11 +82,23 @@ const USAGE = `bikeshed ${VERSION} — publish and revise documents
|
|
|
79
82
|
document you are not a member of needs the link: its key is in it.
|
|
80
83
|
|
|
81
84
|
Organising
|
|
82
|
-
bikeshed folders the folder tree, with ids
|
|
85
|
+
bikeshed folders the folder tree, with ids and readers
|
|
86
|
+
bikeshed folder <id> who can read a folder, and so what is in it
|
|
83
87
|
bikeshed mkdir <name> [--parent <id>] create a folder
|
|
84
88
|
bikeshed mv <id> <folder|root> move a document
|
|
85
89
|
bikeshed rename <id> <title> change a document's title; its address stays
|
|
86
90
|
|
|
91
|
+
Management
|
|
92
|
+
bikeshed access <document|folder> <id> inspect direct and inherited permissions
|
|
93
|
+
--add <principals> --role <role> grant/change roles (emails or team:<id>, comma-separated)
|
|
94
|
+
--remove <principals> revoke direct grants; inherited access remains
|
|
95
|
+
bikeshed visibility <id> [private|unlisted|public]
|
|
96
|
+
--public-edit true|false change open editing
|
|
97
|
+
--expires <ISO timestamp|none> set or clear expiry; omitted settings stay unchanged
|
|
98
|
+
bikeshed rename-folder <id> <name> rename a folder you can edit
|
|
99
|
+
bikeshed move-folder <id> <folder|root> move a folder and recompute inherited access
|
|
100
|
+
bikeshed delete-folder <id> owner-only; reparent contents, retain documents
|
|
101
|
+
|
|
87
102
|
Skills
|
|
88
103
|
bikeshed skills skills this deployment publishes
|
|
89
104
|
bikeshed skills install <dir> write them into an agent's skills directory
|
|
@@ -113,6 +128,16 @@ Options
|
|
|
113
128
|
--parent <id> parent folder for mkdir
|
|
114
129
|
--limit <n> with list: rows per page (default 20, max 50)
|
|
115
130
|
--cursor <id> with list: the page after the one that printed it
|
|
131
|
+
--since <cursor> with changes: a cursor publish, fork, get --json or
|
|
132
|
+
changes printed; "last" where this sign-in left off;
|
|
133
|
+
or "all"
|
|
134
|
+
--wait with changes: keep checking until something happens
|
|
135
|
+
--interval <s> with changes --wait: seconds between checks (default
|
|
136
|
+
60, at least 30)
|
|
137
|
+
--timeout <s> with changes --wait: give up after this long (default
|
|
138
|
+
3600), exit 3, and print the cursor to resume from
|
|
139
|
+
--base <v> with update: the version you revised; refused if
|
|
140
|
+
somebody has published since
|
|
116
141
|
--all with list: every page; with comments: resolved too
|
|
117
142
|
--with <text> with suggest or revise: what the passage should say instead
|
|
118
143
|
--delete with suggest or revise: propose deleting the passage
|
|
@@ -449,9 +474,11 @@ async function publish(flags, [path]) {
|
|
|
449
474
|
}
|
|
450
475
|
// The address is in the human line already; `--json` carried only the id, so
|
|
451
476
|
// anything reading it had to know how to build a URL out of a docId.
|
|
452
|
-
out(flags.json, { ...result, url: result.url ?? `${origin}/d/${result.docId}` }, () =>
|
|
453
|
-
console.log(`published ${result.docId} ${result.versionId} — ${origin}/d/${result.docId}`)
|
|
454
|
-
|
|
477
|
+
out(flags.json, { ...result, url: result.url ?? `${origin}/d/${result.docId}` }, () => {
|
|
478
|
+
console.log(`published ${result.docId} ${result.versionId} — ${origin}/d/${result.docId}`);
|
|
479
|
+
const where = describeAccess(result);
|
|
480
|
+
if (where) console.log(where);
|
|
481
|
+
});
|
|
455
482
|
return result;
|
|
456
483
|
};
|
|
457
484
|
|
|
@@ -476,9 +503,16 @@ async function update(flags, [ref, path]) {
|
|
|
476
503
|
const bytes = readFileSync(file);
|
|
477
504
|
// An archive goes up as bytes, to the one endpoint that takes them. Read as
|
|
478
505
|
// text, every byte above 0x7f in it would become U+FFFD on the way.
|
|
506
|
+
// --base: the version this was revised from, so an update over somebody's
|
|
507
|
+
// newer save is refused instead of replacing it (SPEC §19).
|
|
508
|
+
const base = typeof flags.base === "string" && flags.base ? flags.base : undefined;
|
|
479
509
|
const result = isArchive(file, bytes)
|
|
480
|
-
? await uploadFile(origin, token, file, { update: id, k: doc.k })
|
|
481
|
-
: await callTool(origin, token, "update_document", {
|
|
510
|
+
? await uploadFile(origin, token, file, { update: id, k: doc.k, base })
|
|
511
|
+
: await callTool(origin, token, "update_document", {
|
|
512
|
+
...doc,
|
|
513
|
+
content: bytes.toString("utf8"),
|
|
514
|
+
...(base ? { baseVersion: base } : {}),
|
|
515
|
+
});
|
|
482
516
|
out(flags.json, { ...result, url: result.url ?? `${origin}/d/${id}` }, () =>
|
|
483
517
|
console.log(
|
|
484
518
|
result.deduped
|
|
@@ -726,7 +760,9 @@ async function folders(flags) {
|
|
|
726
760
|
const depth = folder.path.split("/").length - 1;
|
|
727
761
|
const label = `${" ".repeat(depth)}${folder.name}`;
|
|
728
762
|
const note = folder.canWrite ? "" : " (read-only)";
|
|
729
|
-
|
|
763
|
+
// Filing a document here shares it with everyone counted, and emails nobody.
|
|
764
|
+
const readers = typeof folder.readers === "number" ? ` ${folder.readers} can read` : "";
|
|
765
|
+
console.log(`${label.padEnd(40)} ${folder.id}${readers}${note}`);
|
|
730
766
|
}
|
|
731
767
|
if (result.truncated) {
|
|
732
768
|
console.log("\nThere are more folders than this listing shows.");
|
|
@@ -734,6 +770,170 @@ async function folders(flags) {
|
|
|
734
770
|
});
|
|
735
771
|
}
|
|
736
772
|
|
|
773
|
+
/**
|
|
774
|
+
* Where a published document is filed and who can read it there, from what the
|
|
775
|
+
* server read after the write, for a person to be told. Empty for a deployment
|
|
776
|
+
* too old to say.
|
|
777
|
+
*/
|
|
778
|
+
function describeAccess(result) {
|
|
779
|
+
const access = result.access;
|
|
780
|
+
if (!access) return "";
|
|
781
|
+
const where = result.filedIn ? `filed in ${result.filedIn.path}` : "at the root";
|
|
782
|
+
const who = access.readers === 1 ? "only you can read it" : `${access.readers} people can read it`;
|
|
783
|
+
const also = access.alsoReadableBy ? `, and ${access.alsoReadableBy}` : "";
|
|
784
|
+
return `${where}: ${who}${also}`;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/** Who can read one folder: and so who can read anything filed in it. */
|
|
788
|
+
async function folder(flags, [id]) {
|
|
789
|
+
if (!id) fail("usage: bikeshed folder <id>");
|
|
790
|
+
const { origin, token } = resolveAuth(flags);
|
|
791
|
+
const result = await callTool(origin, token, "get_folder", { id });
|
|
792
|
+
out(flags.json, result, () => {
|
|
793
|
+
console.log(`${result.path} ${result.id}${result.canWrite ? "" : " (read-only)"}`);
|
|
794
|
+
console.log(`${result.readers} can read it${result.readers === 1 ? " (only you)" : ""}:`);
|
|
795
|
+
for (const person of result.people) console.log(` ${person}`);
|
|
796
|
+
if (result.morePeople > 0) console.log(` and ${result.morePeople} more`);
|
|
797
|
+
if (result.teams.length) console.log(`teams: ${result.teams.join(", ")}`);
|
|
798
|
+
console.log(`can edit: ${[...result.editors, ...(result.moreEditors ? [`and ${result.moreEditors} more`] : [])].join(", ")}`);
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/** Complete owner-visible grants, including why a removed direct grant may still read. */
|
|
803
|
+
function printManagedAccess(result) {
|
|
804
|
+
console.log(`${result.kind} ${result.id} — owner: ${result.owner}`);
|
|
805
|
+
for (const [role, people] of Object.entries(result.direct)) console.log(`${role}: ${people.join(", ") || "none"}`);
|
|
806
|
+
for (const grant of result.inherited) console.log(`inherited ${grant.role}: ${grant.principal} via ${grant.path}`);
|
|
807
|
+
for (const [role, people] of Object.entries(result.effective)) console.log(`${role}: ${people.join(", ") || "none"}`);
|
|
808
|
+
if (result.visibility !== undefined) printVisibility(result);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function printVisibility(result) {
|
|
812
|
+
console.log(`visibility: ${result.visibility}; open editing: ${result.publicEdit ? "on" : "off"}; expires: ${result.expiresAt === null ? "never" : new Date(result.expiresAt).toISOString()}`);
|
|
813
|
+
console.log(result.url);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
async function access(flags, [kind, ref, ...rest]) {
|
|
817
|
+
if (!["document", "folder"].includes(kind) || !ref || rest.length) fail("usage: bikeshed access <document|folder> <id> [--add <principals> --role <role>] [--remove <principals>]");
|
|
818
|
+
const args = { id: kind === "document" ? docArgs(ref).id : ref };
|
|
819
|
+
const list = raw => {
|
|
820
|
+
if (typeof raw !== "string" || !raw.trim()) fail("principal list must not be empty");
|
|
821
|
+
const values = raw.split(",").map(p => p.trim());
|
|
822
|
+
if (values.some(p => !p)) fail("principal list contains an empty entry");
|
|
823
|
+
return values;
|
|
824
|
+
};
|
|
825
|
+
if (flags.add !== undefined) {
|
|
826
|
+
if (!["viewer", "suggester", "editor", "approver"].includes(flags.role)) fail("--add requires --role viewer|suggester|editor|approver");
|
|
827
|
+
args.add = list(flags.add).map(principal => ({ principal, role: flags.role }));
|
|
828
|
+
} else if (flags.role !== undefined) fail("--role requires --add");
|
|
829
|
+
if (flags.remove !== undefined) args.remove = list(flags.remove);
|
|
830
|
+
const { origin, token } = resolveAuth(flags);
|
|
831
|
+
const result = await callTool(origin, token, `${args.add || args.remove ? "set" : "get"}_${kind}_access`, args);
|
|
832
|
+
out(flags.json, result, () => printManagedAccess(result));
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
async function visibility(flags, [ref, level, ...rest]) {
|
|
836
|
+
if (!ref || rest.length || (level !== undefined && !["private", "unlisted", "public"].includes(level))) fail("usage: bikeshed visibility <id> [private|unlisted|public] [--public-edit true|false] [--expires <ISO timestamp|none>]");
|
|
837
|
+
const args = { id: docArgs(ref).id };
|
|
838
|
+
if (level !== undefined) args.visibility = level;
|
|
839
|
+
if (flags["public-edit"] !== undefined) {
|
|
840
|
+
if (!["true", "false"].includes(flags["public-edit"])) fail("--public-edit must be true or false");
|
|
841
|
+
args.publicEdit = flags["public-edit"] === "true";
|
|
842
|
+
}
|
|
843
|
+
if (flags.expires !== undefined) {
|
|
844
|
+
if (flags.expires === "none") args.expiresAt = null;
|
|
845
|
+
else {
|
|
846
|
+
if (typeof flags.expires !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,3})?)?(?:Z|[+-]\d{2}:\d{2})$/.test(flags.expires) || !Number.isFinite(Date.parse(flags.expires))) fail("--expires must be an ISO-8601 timestamp with a timezone, or none");
|
|
847
|
+
args.expiresAt = Date.parse(flags.expires);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
const { origin, token } = resolveAuth(flags);
|
|
851
|
+
const result = await callTool(origin, token, Object.keys(args).length > 1 ? "set_document_visibility" : "get_document_access", args);
|
|
852
|
+
out(flags.json, result, () => printVisibility(result));
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
async function renameFolder(flags, [id, name, ...rest]) {
|
|
856
|
+
if (!id || !name || rest.length) fail("usage: bikeshed rename-folder <id> <name>");
|
|
857
|
+
const { origin, token } = resolveAuth(flags);
|
|
858
|
+
const result = await callTool(origin, token, "rename_folder", { id, name });
|
|
859
|
+
out(flags.json, result, () => console.log(`renamed ${id}: ${result.path}`));
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
async function moveFolder(flags, [id, parent, ...rest]) {
|
|
863
|
+
if (!id || !parent || rest.length) fail("usage: bikeshed move-folder <id> <folder|root>");
|
|
864
|
+
const { origin, token } = resolveAuth(flags);
|
|
865
|
+
const result = await callTool(origin, token, "move_folder", { id, ...(parent === "root" ? {} : { parent }) });
|
|
866
|
+
out(flags.json, result, () => console.log(`moved ${id}: ${result.path}; ${result.access.readers} can read it`));
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
async function deleteFolder(flags, [id, ...rest]) {
|
|
870
|
+
if (!id || rest.length) fail("usage: bikeshed delete-folder <id>");
|
|
871
|
+
const { origin, token } = resolveAuth(flags);
|
|
872
|
+
const result = await callTool(origin, token, "delete_folder", { id });
|
|
873
|
+
out(flags.json, result, () => console.log(`deleted ${id}; moved ${result.reparented.folders} folders and ${result.reparented.documents} documents to ${result.parent ?? "root"}`));
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/** Exit status when `changes --wait` gives up: distinct from 1, an error. */
|
|
877
|
+
const WAIT_TIMED_OUT = 3;
|
|
878
|
+
/** The shortest wait between checks: each one reads the log and the document. */
|
|
879
|
+
const MIN_INTERVAL_S = 30;
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* What happened on a document since a cursor (SPEC §19, ADR 0016).
|
|
883
|
+
*
|
|
884
|
+
* `--since` is required: a check that starts from nowhere in particular
|
|
885
|
+
* reports nothing, and nothing reads as nothing happened. Every page is read
|
|
886
|
+
* before anything is printed, and the cursor printed last is the one to pass
|
|
887
|
+
* next time: replace whatever you held with it.
|
|
888
|
+
*
|
|
889
|
+
* `--wait` keeps checking until something happens, then prints it and exits 0.
|
|
890
|
+
* A harness that wakes an agent when a background command ends (Claude Code
|
|
891
|
+
* does) gets watching from one command. On timeout it exits 3 and still prints
|
|
892
|
+
* the cursor, so the next wait resumes rather than restarts.
|
|
893
|
+
*/
|
|
894
|
+
async function changes(flags, [ref]) {
|
|
895
|
+
const usage = "usage: bikeshed changes <id> --since <cursor|last|all> [--wait] [--interval <s>] [--timeout <s>]";
|
|
896
|
+
if (!ref) fail(usage);
|
|
897
|
+
if (typeof flags.since !== "string" || !flags.since) {
|
|
898
|
+
fail(`${usage}\n--since is required: a cursor that publish, fork, get --json or changes printed; "last", where this sign-in left off; or "all"`);
|
|
899
|
+
}
|
|
900
|
+
const interval = Math.max(MIN_INTERVAL_S, flags.interval === undefined ? 60 : Number(flags.interval) || 0);
|
|
901
|
+
const timeout = flags.timeout === undefined ? 3600 : Number(flags.timeout);
|
|
902
|
+
if (!Number.isFinite(timeout) || timeout < 0) fail(usage);
|
|
903
|
+
const { origin, token } = resolveAuth(flags);
|
|
904
|
+
const doc = docArgs(ref);
|
|
905
|
+
const started = Date.now();
|
|
906
|
+
let since = flags.since;
|
|
907
|
+
const events = [];
|
|
908
|
+
for (;;) {
|
|
909
|
+
const result = await callTool(origin, token, "get_changes", { ...doc, since });
|
|
910
|
+
since = result.cursor;
|
|
911
|
+
events.push(...result.events);
|
|
912
|
+
if (result.more) continue;
|
|
913
|
+
const waitFor = Math.max(interval, Number(result.retryAfter) || 0);
|
|
914
|
+
if (!flags.wait || events.length > 0) return printChanges(flags, { ...result, events });
|
|
915
|
+
if ((Date.now() - started) / 1000 + waitFor > timeout) {
|
|
916
|
+
process.exitCode = WAIT_TIMED_OUT;
|
|
917
|
+
return printChanges(flags, { ...result, events, timedOut: true });
|
|
918
|
+
}
|
|
919
|
+
await new Promise((r) => setTimeout(r, waitFor * 1000));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function printChanges(flags, result) {
|
|
924
|
+
out(flags.json, result, () => {
|
|
925
|
+
if (result.timedOut) console.log("nothing new before the timeout");
|
|
926
|
+
for (const e of result.events) {
|
|
927
|
+
const quote = e.quote ? ` “${e.quote}”` : "";
|
|
928
|
+
const words = e.body ? `: ${e.body}` : e.replacement !== undefined ? ` → “${e.replacement}”` : "";
|
|
929
|
+
const via = e.channel && e.channel !== "web" ? ` [via ${e.channel}]` : "";
|
|
930
|
+
console.log(`${e.at} ${e.type} ${e.by ?? ""}${via}${e.threadId ? ` (${e.threadId})` : ""}${quote}${words}`);
|
|
931
|
+
}
|
|
932
|
+
if (result.needsReply?.length) console.log(`waiting on a reply: ${result.needsReply.join(", ")}`);
|
|
933
|
+
console.log(`next: --since ${result.cursor}`);
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
|
|
737
937
|
/**
|
|
738
938
|
* Your own copy of a document you can read. The original's author sees that it
|
|
739
939
|
* was forked — a count, never who — and the fork records where it came from,
|
|
@@ -935,6 +1135,7 @@ const COMMANDS = {
|
|
|
935
1135
|
lineage,
|
|
936
1136
|
get,
|
|
937
1137
|
comments,
|
|
1138
|
+
changes,
|
|
938
1139
|
comment,
|
|
939
1140
|
reply,
|
|
940
1141
|
resolve: resolveThread,
|
|
@@ -946,7 +1147,13 @@ const COMMANDS = {
|
|
|
946
1147
|
reject,
|
|
947
1148
|
commit,
|
|
948
1149
|
open,
|
|
1150
|
+
access,
|
|
1151
|
+
visibility,
|
|
1152
|
+
"rename-folder": renameFolder,
|
|
1153
|
+
"move-folder": moveFolder,
|
|
1154
|
+
"delete-folder": deleteFolder,
|
|
949
1155
|
folders,
|
|
1156
|
+
folder,
|
|
950
1157
|
mkdir,
|
|
951
1158
|
mv,
|
|
952
1159
|
rename,
|
|
@@ -962,6 +1169,11 @@ async function main() {
|
|
|
962
1169
|
allowPositionals: true,
|
|
963
1170
|
strict: false,
|
|
964
1171
|
options: {
|
|
1172
|
+
add: { type: "string" },
|
|
1173
|
+
remove: { type: "string" },
|
|
1174
|
+
role: { type: "string" },
|
|
1175
|
+
"public-edit": { type: "string" },
|
|
1176
|
+
expires: { type: "string" },
|
|
965
1177
|
json: { type: "boolean", default: false },
|
|
966
1178
|
watch: { type: "boolean", default: false },
|
|
967
1179
|
all: { type: "boolean", default: false },
|
|
@@ -984,6 +1196,11 @@ async function main() {
|
|
|
984
1196
|
body: { type: "string" },
|
|
985
1197
|
prefix: { type: "string" },
|
|
986
1198
|
suffix: { type: "string" },
|
|
1199
|
+
since: { type: "string" },
|
|
1200
|
+
wait: { type: "boolean", default: false },
|
|
1201
|
+
interval: { type: "string" },
|
|
1202
|
+
timeout: { type: "string" },
|
|
1203
|
+
base: { type: "string" },
|
|
987
1204
|
},
|
|
988
1205
|
});
|
|
989
1206
|
|