innernote 0.5.0 → 0.6.1
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/dist/index.js +101 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1098,7 +1098,9 @@ function parseWhen(input) {
|
|
|
1098
1098
|
return Number.isFinite(t) ? t : null;
|
|
1099
1099
|
}
|
|
1100
1100
|
async function queue(args) {
|
|
1101
|
-
const { json, rest, value } = parseFlags(args, ["at"]);
|
|
1101
|
+
const { json, rest, value, has } = parseFlags(args, ["at"]);
|
|
1102
|
+
if (has("ready"))
|
|
1103
|
+
return queueReady(json, rest, value("at"));
|
|
1102
1104
|
const ref = rest[0] ? resolveRef(rest[0]) : null;
|
|
1103
1105
|
if (!ref) {
|
|
1104
1106
|
fail("Which post?");
|
|
@@ -1133,6 +1135,40 @@ async function queue(args) {
|
|
|
1133
1135
|
note(dim2("`week` to see where it landed"));
|
|
1134
1136
|
return 0;
|
|
1135
1137
|
}
|
|
1138
|
+
async function queueReady(json, rest, at) {
|
|
1139
|
+
if (rest.length || at !== undefined) {
|
|
1140
|
+
fail("--ready queues every Ready post in order, so it takes no post and no time.");
|
|
1141
|
+
note(dim2(`${CMD} queue --ready or ${CMD} queue <post-id>`));
|
|
1142
|
+
return 1;
|
|
1143
|
+
}
|
|
1144
|
+
const res = await request("/api/posts/queue-ready", { method: "POST", timeoutMs: 90000 });
|
|
1145
|
+
if (json) {
|
|
1146
|
+
out(JSON.stringify(res, null, 2));
|
|
1147
|
+
return 0;
|
|
1148
|
+
}
|
|
1149
|
+
if (res.queued.length === 0) {
|
|
1150
|
+
fail("Nothing was queued.");
|
|
1151
|
+
if (res.stopped)
|
|
1152
|
+
note(res.stopped);
|
|
1153
|
+
return 1;
|
|
1154
|
+
}
|
|
1155
|
+
const n = res.queued.length;
|
|
1156
|
+
ok(`Queued ${n} ${n === 1 ? "post" : "posts"}, in your Ready order. Each goes live on its own.`);
|
|
1157
|
+
for (const q of res.queued) {
|
|
1158
|
+
const when = new Date(q.scheduledFor).toLocaleString(undefined, {
|
|
1159
|
+
weekday: "short",
|
|
1160
|
+
day: "numeric",
|
|
1161
|
+
month: "short",
|
|
1162
|
+
hour: "numeric",
|
|
1163
|
+
minute: "2-digit"
|
|
1164
|
+
});
|
|
1165
|
+
out(` ${dim2(when.padEnd(22))} ${q.title || q.id}`);
|
|
1166
|
+
}
|
|
1167
|
+
if (res.waiting > 0)
|
|
1168
|
+
note(`${res.waiting} still in Ready. ${res.stopped ?? ""}`.trim());
|
|
1169
|
+
note(dim2("`week` to see where they landed"));
|
|
1170
|
+
return 0;
|
|
1171
|
+
}
|
|
1136
1172
|
async function save(args) {
|
|
1137
1173
|
const { json } = parseFlags(args);
|
|
1138
1174
|
const held = getDraft();
|
|
@@ -1439,13 +1475,22 @@ async function askAboutPost(refArg, message, target, json) {
|
|
|
1439
1475
|
}
|
|
1440
1476
|
}
|
|
1441
1477
|
if (json) {
|
|
1442
|
-
out(JSON.stringify({
|
|
1478
|
+
out(JSON.stringify({
|
|
1479
|
+
id: post._id,
|
|
1480
|
+
changed: !!result.changed,
|
|
1481
|
+
post: stored,
|
|
1482
|
+
passage: result.fragment ?? null,
|
|
1483
|
+
removed: !!result.removed,
|
|
1484
|
+
reply: result.reply ?? null
|
|
1485
|
+
}, null, 2));
|
|
1443
1486
|
return 0;
|
|
1444
1487
|
}
|
|
1445
1488
|
out();
|
|
1446
1489
|
out(stored);
|
|
1447
1490
|
out();
|
|
1448
|
-
if (result.changed && result.
|
|
1491
|
+
if (result.changed && result.removed && target.passage !== undefined) {
|
|
1492
|
+
note(dim2(`took out: ${target.passage}`));
|
|
1493
|
+
} else if (result.changed && result.fragment && target.passage !== undefined) {
|
|
1449
1494
|
note(dim2(`was: ${target.passage}`));
|
|
1450
1495
|
note(dim2(`now: ${result.fragment}`));
|
|
1451
1496
|
}
|
|
@@ -1492,7 +1537,7 @@ async function showPost(refArg, json) {
|
|
|
1492
1537
|
return 0;
|
|
1493
1538
|
}
|
|
1494
1539
|
async function edit(args) {
|
|
1495
|
-
const { json, rest, value } = parseFlags(args, ["text", "file", "title", "first-comment", "series", "status"]);
|
|
1540
|
+
const { json, rest, value, has } = parseFlags(args, ["text", "file", "title", "first-comment", "series", "status", "after"]);
|
|
1496
1541
|
const refArg = rest[0];
|
|
1497
1542
|
if (!refArg) {
|
|
1498
1543
|
fail("Edit which post?");
|
|
@@ -1504,17 +1549,23 @@ async function edit(args) {
|
|
|
1504
1549
|
const firstComment = value("first-comment");
|
|
1505
1550
|
const seriesName = value("series");
|
|
1506
1551
|
const status = value("status");
|
|
1552
|
+
const first = has("first");
|
|
1553
|
+
const afterRef = value("after");
|
|
1507
1554
|
const file = value("file");
|
|
1508
1555
|
let text = value("text");
|
|
1509
1556
|
if (file)
|
|
1510
1557
|
text = await readFile2(file, "utf8");
|
|
1511
|
-
const fieldsGiven = title !== undefined || firstComment !== undefined || seriesName !== undefined || status !== undefined;
|
|
1558
|
+
const fieldsGiven = title !== undefined || firstComment !== undefined || seriesName !== undefined || status !== undefined || first || afterRef !== undefined;
|
|
1512
1559
|
if (text === undefined && !fieldsGiven) {
|
|
1513
1560
|
text = await readStdin() ?? undefined;
|
|
1514
1561
|
}
|
|
1515
1562
|
if (text === undefined && !fieldsGiven) {
|
|
1516
1563
|
fail("Nothing to change.");
|
|
1517
|
-
note(dim2("--file, --text or piped text for the post, --title, --first-comment, --series, --status"));
|
|
1564
|
+
note(dim2("--file, --text or piped text for the post, --title, --first-comment, --series, --status, --first, --after"));
|
|
1565
|
+
return 1;
|
|
1566
|
+
}
|
|
1567
|
+
if (first && afterRef !== undefined) {
|
|
1568
|
+
fail("--first or --after, not both.");
|
|
1518
1569
|
return 1;
|
|
1519
1570
|
}
|
|
1520
1571
|
if (status !== undefined && status !== "ready" && status !== "draft") {
|
|
@@ -1581,6 +1632,17 @@ async function edit(args) {
|
|
|
1581
1632
|
await request(`/api/posts/${encodeURIComponent(post._id)}/ready`, { method: status === "ready" ? "POST" : "DELETE" });
|
|
1582
1633
|
changed.push(`status (${status})`);
|
|
1583
1634
|
}
|
|
1635
|
+
if (first || afterRef !== undefined) {
|
|
1636
|
+
let after = null;
|
|
1637
|
+
if (afterRef !== undefined) {
|
|
1638
|
+
const target = await postFromRef(afterRef, `${CMD} edit <post-id> --after <post-id>`);
|
|
1639
|
+
if (!target)
|
|
1640
|
+
return 1;
|
|
1641
|
+
after = target._id;
|
|
1642
|
+
}
|
|
1643
|
+
await request(`/api/posts/${encodeURIComponent(post._id)}/move`, { method: "POST", body: { after } });
|
|
1644
|
+
changed.push(after ? "place in Ready" : "place in Ready (top)");
|
|
1645
|
+
}
|
|
1584
1646
|
if (json) {
|
|
1585
1647
|
out(JSON.stringify({ id: post._id, changed }, null, 2));
|
|
1586
1648
|
return 0;
|
|
@@ -1781,10 +1843,10 @@ var COMMANDS = {
|
|
|
1781
1843
|
ask: {
|
|
1782
1844
|
summary: "talk to the post in your own words, or edit one passage of it",
|
|
1783
1845
|
usage: "innernote ask [--post <id>] [--passage <text> | --hook | --ending] [--preset <name> | --shape <name> | --product <name>] [what you want]",
|
|
1784
|
-
detail: "The Writer, here. The app's composer is a conversation with the post in front of you, and this is the same assistant: it can change the post, answer a question about it, or say it would rather not. In a session it works on the post in hand. With --post it works on a saved post in one command, and the change is kept in the post's history. --passage is the editor's selection: the exact words to change, and only those change. --hook and --ending are the opening and closing block. --preset is a toolbar edit by name, --shape the format picker.",
|
|
1846
|
+
detail: "The Writer, here. The app's composer is a conversation with the post in front of you, and this is the same assistant: it can change the post, answer a question about it, or say it would rather not. In a session it works on the post in hand. With --post it works on a saved post in one command, and the change is kept in the post's history. --passage is the editor's selection: the exact words to change, and only those change; ask for them to go and they are taken out. --hook and --ending are the opening and closing block. --preset is a toolbar edit by name, --shape the format picker.",
|
|
1785
1847
|
options: [
|
|
1786
1848
|
["--post <id>", "a saved post, by id, instead of the one in hand"],
|
|
1787
|
-
["--passage <text>", "change only these exact words of the post"],
|
|
1849
|
+
["--passage <text>", "change, or take out, only these exact words of the post"],
|
|
1788
1850
|
["--hook, --ending", "change only the opening, or the closing"],
|
|
1789
1851
|
["--preset <name>", "on a passage: sharpen shorter punchier simpler example deslop concrete warmer list. On the post: tighten_all as_list as_story fix_spacing rewrite_hook rewrite_ending ending_question"],
|
|
1790
1852
|
["--shape <name>", "story opinion listicle contrarian experience howto lessons casestudy framework question announcement prediction beliefbreak"],
|
|
@@ -1797,6 +1859,7 @@ var COMMANDS = {
|
|
|
1797
1859
|
'innernote ask --post p17abc... "make the opening shorter"',
|
|
1798
1860
|
'innernote ask --post p17abc... --passage "we got lucky" "say what the luck actually was"',
|
|
1799
1861
|
'innernote ask --post p17abc... --passage "we got lucky" --preset concrete',
|
|
1862
|
+
'innernote ask --post p17abc... --passage "comment voice for the link." "take this line out"',
|
|
1800
1863
|
"innernote ask --post p17abc... --preset ending_question",
|
|
1801
1864
|
"innernote ask --post p17abc... --shape story",
|
|
1802
1865
|
'innernote ask --post p17abc... --product "innernote"'
|
|
@@ -1804,7 +1867,7 @@ var COMMANDS = {
|
|
|
1804
1867
|
},
|
|
1805
1868
|
edit: {
|
|
1806
1869
|
summary: "change a saved post: its text, title, first comment, series or status",
|
|
1807
|
-
usage: "innernote edit <id> [--file <path> | --text <text>] [--title <text>] [--first-comment <text>] [--series <name>] [--status ready|draft]",
|
|
1870
|
+
usage: "innernote edit <id> [--file <path> | --text <text>] [--title <text>] [--first-comment <text>] [--series <name>] [--status ready|draft] [--first | --after <id>]",
|
|
1808
1871
|
detail: "Puts your own words into a saved post. New text comes from --file, --text or a pipe, and is saved the way the app saves a draft, so the old text is one Undo away in the post's history. A post already live on LinkedIn keeps its text. The id comes from `drafts --json` or `show`.",
|
|
1809
1872
|
options: [
|
|
1810
1873
|
["--file <path>", "the new text of the post, from a file"],
|
|
@@ -1812,13 +1875,16 @@ var COMMANDS = {
|
|
|
1812
1875
|
["--title <text>", "the post's title in the app"],
|
|
1813
1876
|
["--first-comment <text>", "the comment posted under it when it goes live"],
|
|
1814
1877
|
["--series <name>", "file it into a series, or `none` to take it out"],
|
|
1815
|
-
["--status <ready|draft>", "mark it ready, or send it back to draft"]
|
|
1878
|
+
["--status <ready|draft>", "mark it ready, or send it back to draft"],
|
|
1879
|
+
["--first", "put a Ready post at the top of Ready, so it goes out next"],
|
|
1880
|
+
["--after <id>", "put a Ready post right after another one in Ready"]
|
|
1816
1881
|
],
|
|
1817
1882
|
examples: [
|
|
1818
1883
|
"innernote edit p17abc... --file post.txt",
|
|
1819
1884
|
"pbpaste | innernote edit p17abc...",
|
|
1820
1885
|
'innernote edit p17abc... --title "The sales call" --first-comment "link: innernote.space"',
|
|
1821
|
-
'innernote edit p17abc... --series "Build in Public" --status ready'
|
|
1886
|
+
'innernote edit p17abc... --series "Build in Public" --status ready',
|
|
1887
|
+
"innernote edit 3 --first"
|
|
1822
1888
|
]
|
|
1823
1889
|
},
|
|
1824
1890
|
captures: {
|
|
@@ -1961,10 +2027,13 @@ var COMMANDS = {
|
|
|
1961
2027
|
},
|
|
1962
2028
|
queue: {
|
|
1963
2029
|
summary: "schedule a post into your next open slot",
|
|
1964
|
-
usage: "innernote queue <post-id> [--at <time>]",
|
|
1965
|
-
detail: "A queued post GOES LIVE on LinkedIn on its own at your next cadence slot. Nobody presses anything again. Needs a connection you allowed to publish: tick that box in Settings when you generate the code. Takes a number from the last list, or a full id. It never assumes the draft in hand: this is the one command that makes you say which post.",
|
|
1966
|
-
options: [
|
|
1967
|
-
|
|
2030
|
+
usage: "innernote queue <post-id> [--at <time>] | queue --ready",
|
|
2031
|
+
detail: "A queued post GOES LIVE on LinkedIn on its own at your next cadence slot. Nobody presses anything again. Needs a connection you allowed to publish: tick that box in Settings when you generate the code. Takes a number from the last list, or a full id. It never assumes the draft in hand: this is the one command that makes you say which post. `--ready` queues every Ready post at once, in your Ready order, the top one first: Queue these in the app.",
|
|
2032
|
+
options: [
|
|
2033
|
+
["--at <time>", `a time you pick, like "2026-10-12 12:31" (this machine's timezone), instead of the next open slot`],
|
|
2034
|
+
["--ready", "every Ready post, in your Ready order, into your next open slots"]
|
|
2035
|
+
],
|
|
2036
|
+
examples: ["innernote drafts", "innernote queue 3", "innernote queue p17a1yk936ywnan10643efjtc58dhkah", "innernote drafts ready", "innernote queue --ready"]
|
|
1968
2037
|
}
|
|
1969
2038
|
};
|
|
1970
2039
|
function commandHelp(name) {
|
|
@@ -2077,7 +2146,7 @@ function suggest(state) {
|
|
|
2077
2146
|
// package.json
|
|
2078
2147
|
var package_default = {
|
|
2079
2148
|
name: "innernote",
|
|
2080
|
-
version: "0.
|
|
2149
|
+
version: "0.6.1",
|
|
2081
2150
|
description: "Write LinkedIn posts in your voice, from the terminal.",
|
|
2082
2151
|
keywords: [
|
|
2083
2152
|
"linkedin",
|
|
@@ -2768,10 +2837,10 @@ var COMMANDS2 = {
|
|
|
2768
2837
|
ask: {
|
|
2769
2838
|
summary: "talk to the post in your own words, or edit one passage of it",
|
|
2770
2839
|
usage: "innernote ask [--post <id>] [--passage <text> | --hook | --ending] [--preset <name> | --shape <name> | --product <name>] [what you want]",
|
|
2771
|
-
detail: "The Writer, here. The app's composer is a conversation with the post in front of you, and this is the same assistant: it can change the post, answer a question about it, or say it would rather not. In a session it works on the post in hand. With --post it works on a saved post in one command, and the change is kept in the post's history. --passage is the editor's selection: the exact words to change, and only those change. --hook and --ending are the opening and closing block. --preset is a toolbar edit by name, --shape the format picker.",
|
|
2840
|
+
detail: "The Writer, here. The app's composer is a conversation with the post in front of you, and this is the same assistant: it can change the post, answer a question about it, or say it would rather not. In a session it works on the post in hand. With --post it works on a saved post in one command, and the change is kept in the post's history. --passage is the editor's selection: the exact words to change, and only those change; ask for them to go and they are taken out. --hook and --ending are the opening and closing block. --preset is a toolbar edit by name, --shape the format picker.",
|
|
2772
2841
|
options: [
|
|
2773
2842
|
["--post <id>", "a saved post, by id, instead of the one in hand"],
|
|
2774
|
-
["--passage <text>", "change only these exact words of the post"],
|
|
2843
|
+
["--passage <text>", "change, or take out, only these exact words of the post"],
|
|
2775
2844
|
["--hook, --ending", "change only the opening, or the closing"],
|
|
2776
2845
|
["--preset <name>", "on a passage: sharpen shorter punchier simpler example deslop concrete warmer list. On the post: tighten_all as_list as_story fix_spacing rewrite_hook rewrite_ending ending_question"],
|
|
2777
2846
|
["--shape <name>", "story opinion listicle contrarian experience howto lessons casestudy framework question announcement prediction beliefbreak"],
|
|
@@ -2784,6 +2853,7 @@ var COMMANDS2 = {
|
|
|
2784
2853
|
'innernote ask --post p17abc... "make the opening shorter"',
|
|
2785
2854
|
'innernote ask --post p17abc... --passage "we got lucky" "say what the luck actually was"',
|
|
2786
2855
|
'innernote ask --post p17abc... --passage "we got lucky" --preset concrete',
|
|
2856
|
+
'innernote ask --post p17abc... --passage "comment voice for the link." "take this line out"',
|
|
2787
2857
|
"innernote ask --post p17abc... --preset ending_question",
|
|
2788
2858
|
"innernote ask --post p17abc... --shape story",
|
|
2789
2859
|
'innernote ask --post p17abc... --product "innernote"'
|
|
@@ -2791,7 +2861,7 @@ var COMMANDS2 = {
|
|
|
2791
2861
|
},
|
|
2792
2862
|
edit: {
|
|
2793
2863
|
summary: "change a saved post: its text, title, first comment, series or status",
|
|
2794
|
-
usage: "innernote edit <id> [--file <path> | --text <text>] [--title <text>] [--first-comment <text>] [--series <name>] [--status ready|draft]",
|
|
2864
|
+
usage: "innernote edit <id> [--file <path> | --text <text>] [--title <text>] [--first-comment <text>] [--series <name>] [--status ready|draft] [--first | --after <id>]",
|
|
2795
2865
|
detail: "Puts your own words into a saved post. New text comes from --file, --text or a pipe, and is saved the way the app saves a draft, so the old text is one Undo away in the post's history. A post already live on LinkedIn keeps its text. The id comes from `drafts --json` or `show`.",
|
|
2796
2866
|
options: [
|
|
2797
2867
|
["--file <path>", "the new text of the post, from a file"],
|
|
@@ -2799,13 +2869,16 @@ var COMMANDS2 = {
|
|
|
2799
2869
|
["--title <text>", "the post's title in the app"],
|
|
2800
2870
|
["--first-comment <text>", "the comment posted under it when it goes live"],
|
|
2801
2871
|
["--series <name>", "file it into a series, or `none` to take it out"],
|
|
2802
|
-
["--status <ready|draft>", "mark it ready, or send it back to draft"]
|
|
2872
|
+
["--status <ready|draft>", "mark it ready, or send it back to draft"],
|
|
2873
|
+
["--first", "put a Ready post at the top of Ready, so it goes out next"],
|
|
2874
|
+
["--after <id>", "put a Ready post right after another one in Ready"]
|
|
2803
2875
|
],
|
|
2804
2876
|
examples: [
|
|
2805
2877
|
"innernote edit p17abc... --file post.txt",
|
|
2806
2878
|
"pbpaste | innernote edit p17abc...",
|
|
2807
2879
|
'innernote edit p17abc... --title "The sales call" --first-comment "link: innernote.space"',
|
|
2808
|
-
'innernote edit p17abc... --series "Build in Public" --status ready'
|
|
2880
|
+
'innernote edit p17abc... --series "Build in Public" --status ready',
|
|
2881
|
+
"innernote edit 3 --first"
|
|
2809
2882
|
]
|
|
2810
2883
|
},
|
|
2811
2884
|
captures: {
|
|
@@ -2948,10 +3021,13 @@ var COMMANDS2 = {
|
|
|
2948
3021
|
},
|
|
2949
3022
|
queue: {
|
|
2950
3023
|
summary: "schedule a post into your next open slot",
|
|
2951
|
-
usage: "innernote queue <post-id> [--at <time>]",
|
|
2952
|
-
detail: "A queued post GOES LIVE on LinkedIn on its own at your next cadence slot. Nobody presses anything again. Needs a connection you allowed to publish: tick that box in Settings when you generate the code. Takes a number from the last list, or a full id. It never assumes the draft in hand: this is the one command that makes you say which post.",
|
|
2953
|
-
options: [
|
|
2954
|
-
|
|
3024
|
+
usage: "innernote queue <post-id> [--at <time>] | queue --ready",
|
|
3025
|
+
detail: "A queued post GOES LIVE on LinkedIn on its own at your next cadence slot. Nobody presses anything again. Needs a connection you allowed to publish: tick that box in Settings when you generate the code. Takes a number from the last list, or a full id. It never assumes the draft in hand: this is the one command that makes you say which post. `--ready` queues every Ready post at once, in your Ready order, the top one first: Queue these in the app.",
|
|
3026
|
+
options: [
|
|
3027
|
+
["--at <time>", `a time you pick, like "2026-10-12 12:31" (this machine's timezone), instead of the next open slot`],
|
|
3028
|
+
["--ready", "every Ready post, in your Ready order, into your next open slots"]
|
|
3029
|
+
],
|
|
3030
|
+
examples: ["innernote drafts", "innernote queue 3", "innernote queue p17a1yk936ywnan10643efjtc58dhkah", "innernote drafts ready", "innernote queue --ready"]
|
|
2955
3031
|
}
|
|
2956
3032
|
};
|
|
2957
3033
|
function commandHelp2(name) {
|