strom-research 1.16.0-beta.3 → 1.16.0-beta.4
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.cs.md +2 -0
- package/CHANGELOG.de.md +2 -0
- package/CHANGELOG.md +2 -0
- package/dist/cli/menu-archives.js +5 -4
- package/dist/cli/menu-links.js +3 -2
- package/dist/cli/menu-waiting.js +1 -1
- package/dist/commands/archives.js +13 -2
- package/dist/commands/meta.js +1 -1
- package/dist/core/archiveasks.js +4 -3
- package/dist/core/terms.js +65 -2
- package/dist/core/tree.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.cs.md
CHANGED
|
@@ -22,6 +22,8 @@ Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou n
|
|
|
22
22
|
v aplikaci; jméno doložené záznamem zůstává. `strom intake` čte `_MARNM` jiných programů jako jméno po sňatku.
|
|
23
23
|
Výzkum založený dřív z takového rodokmenu (MyHeritage, Legacy, PAF) je doplní ze souboru, který má uložený:
|
|
24
24
|
`strom input names I…` je ukáže, `--apply` přidá jen tato jména s citací importu, `--undo` je vezme zpět.
|
|
25
|
+
- **Jeden archiv, jedna otázka.** Když obrazy jednoho archivu přicházejí z více adres (jeho web, album, server
|
|
26
|
+
obrazů), svolení archivu se žádá jednou a platí pro všechny, ne adresu po adrese.
|
|
25
27
|
- **Linie výzkumu.** České texty všude říkají „linie výzkumu“, nikde už „směry“.
|
|
26
28
|
|
|
27
29
|
## 1.15.0
|
package/CHANGELOG.de.md
CHANGED
|
@@ -25,6 +25,8 @@ Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Ins
|
|
|
25
25
|
Stammbaum begonnen wurde (MyHeritage, Legacy, PAF), bekommt sie aus der Datei, die sie aufbewahrt:
|
|
26
26
|
`strom input names I…` zeigt sie, `--apply` fügt nur diese Namen hinzu, mit dem Import als Quelle, `--undo` nimmt
|
|
27
27
|
sie zurück.
|
|
28
|
+
- **Ein Archiv, eine Frage.** Kommen die Bilder eines Archivs von mehreren Adressen (seine Seite, ein Album, ein
|
|
29
|
+
Bildserver), wird die Erlaubnis des Archivs einmal erfragt und gilt für alle, nicht Adresse für Adresse.
|
|
28
30
|
- **Forschungslinien auf Tschechisch.** Die tschechischen Texte sagen jetzt überall „linie výzkumu“ (Forschungslinien).
|
|
29
31
|
|
|
30
32
|
## 1.15.0
|
package/CHANGELOG.md
CHANGED
|
@@ -22,6 +22,8 @@ The changes of each version in short. The full notes and the install lines are o
|
|
|
22
22
|
in the app gave it; one a record gives stays. `strom intake` reads `_MARNM` of other programs as a married name.
|
|
23
23
|
A research started earlier from such a family tree (MyHeritage, Legacy, PAF) gets them from the file it keeps:
|
|
24
24
|
`strom input names I…` shows them, `--apply` adds only these names, citing the import, `--undo` takes them back.
|
|
25
|
+
- **One archive, one question.** When one archive's images come from several hosts (its site, an album, an image
|
|
26
|
+
server), the permission it needs is asked once and decided for all of them, not host by host.
|
|
25
27
|
- **Lines of research in Czech.** The Czech texts say „linie výzkumu“ (lines of research) everywhere now.
|
|
26
28
|
|
|
27
29
|
## 1.15.0
|
|
@@ -11,7 +11,7 @@ import { humanDay } from "./human.js";
|
|
|
11
11
|
import { Tree } from "../core/tree.js";
|
|
12
12
|
import { isArchive } from "../core/mode.js";
|
|
13
13
|
import { bareHost, listConnectors, routeOf, routesOf } from "../core/connector.js";
|
|
14
|
-
import { claimOf, loadArchives, rank } from "../core/terms.js";
|
|
14
|
+
import { claimOf, loadArchives, pendingQuestions, questionHosts, rank } from "../core/terms.js";
|
|
15
15
|
import { connectorFor, levelWords, tasksStanding, tasksWaitingOn, termsText, waysOffered, wokenLines } from "../core/archiveasks.js";
|
|
16
16
|
import { wakeForArchive } from "../core/archivewait.js";
|
|
17
17
|
import { archivesOverview, capWords, levelsOf, paceFor } from "../commands/archives.js";
|
|
@@ -53,8 +53,9 @@ export async function decideArchive(ctx, run, lang, root, host, q, all) {
|
|
|
53
53
|
const title = a?.title ?? q?.title ?? c.manifest.title ?? host;
|
|
54
54
|
const tree = root ? Tree.open(root, ctx.env) : undefined;
|
|
55
55
|
const hosts = all && all.length > 1 ? all : [host];
|
|
56
|
-
// the command's target: the downloader for all its hosts at once, else the host
|
|
57
|
-
|
|
56
|
+
// the command's target: the downloader for all its hosts at once, else the host — with every host of its question
|
|
57
|
+
// (strom allow archive decides a question of one archive for all its hosts)
|
|
58
|
+
const target = hosts.length > 1 && hosts.some((h) => !q?.archiveHosts?.includes(h)) ? c.name : host;
|
|
58
59
|
out("");
|
|
59
60
|
out(` ${t("ui.archive.item", { title, host: hosts.join(", ") })}`);
|
|
60
61
|
out(` ${termsText(lang, ctx.env, { host, ...(a?.quote ? { quote: a.quote } : q?.quote ? { quote: q.quote } : {}), ...(a?.terms ?? q?.terms ? { terms: (a?.terms ?? q?.terms) } : {}), ...(a?.quoteBy ? { quoteBy: a.quoteBy } : q?.quoteBy ? { quoteBy: q.quoteBy } : {}) })}`);
|
|
@@ -222,7 +223,7 @@ function hostStates(ctx, lang, c) {
|
|
|
222
223
|
/** Settings → Archives → permissions: each archive of a downloader here, then the same choices as in What waits. */
|
|
223
224
|
export async function archivePermissions(ctx, run, lang, root) {
|
|
224
225
|
const t = translator(lang);
|
|
225
|
-
const pending = (hosts) =>
|
|
226
|
+
const pending = (hosts) => pendingQuestions(ctx.env).find((p) => p.kind === "permission" && questionHosts(p).some((h) => hosts.includes(h)));
|
|
226
227
|
await subMenu(ctx, lang, () => {
|
|
227
228
|
const rows = archivesOverview(ctx);
|
|
228
229
|
const connectors = connectorsHere(ctx);
|
package/dist/cli/menu-links.js
CHANGED
|
@@ -35,6 +35,7 @@ import { foldText, safeFolderName } from "../core/text.js";
|
|
|
35
35
|
import { StromError } from "../core/errors.js";
|
|
36
36
|
import { forgetTree } from "../commands/trees.js";
|
|
37
37
|
import { archiveQuestions } from "../core/archiveasks.js";
|
|
38
|
+
import { questionHosts } from "../core/terms.js";
|
|
38
39
|
import { bareHost, listConnectors } from "../core/connector.js";
|
|
39
40
|
import { decideArchive } from "./menu-archives.js";
|
|
40
41
|
import { inboxOf, requestFile, sameGroup } from "../core/waiting.js";
|
|
@@ -173,11 +174,11 @@ export async function archiveAsked(ctx, run, lang, root, host) {
|
|
|
173
174
|
const tree = Tree.open(root, ctx.env);
|
|
174
175
|
if (isArchive(tree))
|
|
175
176
|
return stop(ctx, t("ui.link.archive.none"));
|
|
176
|
-
const q = archiveQuestions(tree).find((p) => p.kind === "permission" && p.host
|
|
177
|
+
const q = archiveQuestions(tree).find((p) => p.kind === "permission" && questionHosts(p).includes(host));
|
|
177
178
|
const known = q ? true : listConnectors(ctx.settings.shared()?.value, ctx.env).some((c) => c.manifest.hosts.map(bareHost).includes(host));
|
|
178
179
|
if (!known)
|
|
179
180
|
return stop(ctx, t("ui.link.archive.none"));
|
|
180
|
-
return decideArchive(ctx, run, lang, root, host, q);
|
|
181
|
+
return decideArchive(ctx, run, lang, root, host, q, q?.archiveHosts);
|
|
181
182
|
}
|
|
182
183
|
/** A direction of the research paused, ended (each with the person's reason, if they give one) or taken up again. */
|
|
183
184
|
export async function directionDo(ctx, run, lang, root, id, what) {
|
package/dist/cli/menu-waiting.js
CHANGED
|
@@ -426,7 +426,7 @@ async function decide(ctx, run, lang, root, w) {
|
|
|
426
426
|
return void (await decideStory(ctx, run, lang, root, w.story, storyOf(tree, w.story)));
|
|
427
427
|
const q = w.question;
|
|
428
428
|
if (q?.kind === "permission" && q.host)
|
|
429
|
-
return void (await decideArchive(ctx, run, lang, root, q.host, q));
|
|
429
|
+
return void (await decideArchive(ctx, run, lang, root, q.host, q, q.archiveHosts));
|
|
430
430
|
if (q?.kind === "takeover")
|
|
431
431
|
return decideTakeover(ctx, run, lang, q);
|
|
432
432
|
if (q?.kind === "milder" && q.connector) {
|
|
@@ -19,7 +19,7 @@ import { currentSession } from "../core/session.js";
|
|
|
19
19
|
import { noteSessionWait, wakeForArchive } from "../core/archivewait.js";
|
|
20
20
|
import { connectorFor, levelWords, waysOffered, wokenLines } from "../core/archiveasks.js";
|
|
21
21
|
import { claudeHere } from "../cli/menu-parts.js";
|
|
22
|
-
import { acceptConnectors, allows, keepReadings, askLater, claimOf, connectorLevels, grantWay, hostOf, humanPace, keepNetVerdict, keyHost, latchedPace, LEVELS, loadArchives, pendingQuestions, rank, settlePending, revokeWay, setFloor, strictest, takenOverWaiting, windowShown, WAYS, } from "../core/terms.js";
|
|
22
|
+
import { acceptConnectors, allows, keepReadings, askLater, claimOf, connectorLevels, grantWay, hostOf, humanPace, keepNetVerdict, keyHost, latchedPace, LEVELS, loadArchives, pendingQuestions, questionHosts, rank, settlePending, revokeWay, setFloor, strictest, takenOverWaiting, windowShown, WAYS, } from "../core/terms.js";
|
|
23
23
|
/** The images (or pages) of an archive wait for the person's decision: nothing was asked of the archive. */
|
|
24
24
|
export class NeedsPermissionError extends StromError {
|
|
25
25
|
hosts;
|
|
@@ -466,7 +466,10 @@ export function syncTrees(ctx) {
|
|
|
466
466
|
}
|
|
467
467
|
return synced;
|
|
468
468
|
}
|
|
469
|
-
/**
|
|
469
|
+
/**
|
|
470
|
+
* The hosts a name stands for: a connector's (its name), else the connector hosts on that host or site — never wider,
|
|
471
|
+
* but for the other hosts of one archive whose question waits with it (one archive, one decision).
|
|
472
|
+
*/
|
|
470
473
|
function targetHosts(ctx, target) {
|
|
471
474
|
const all = listConnectors(shared(ctx), ctx.env);
|
|
472
475
|
const byName = all.find((c) => c.name === target);
|
|
@@ -477,6 +480,14 @@ function targetHosts(ctx, target) {
|
|
|
477
480
|
const hosts = [...new Set(connectors.flatMap((c) => c.manifest.hosts.map(bareHost).filter((h) => hostAllowed(h, [given]) || hostAllowed(given, [h]))))];
|
|
478
481
|
if (!hosts.length)
|
|
479
482
|
throw new UsageError(`no connector reaches ${given}`, { hint: all.length ? `connectors: ${all.map((c) => `${c.name} (${c.manifest.hosts.join(", ")})`).join(", ")}` : "none yet: strom connector list" });
|
|
483
|
+
const reached = new Set(connectors.flatMap((c) => c.manifest.hosts.map(bareHost)));
|
|
484
|
+
for (const q of pendingQuestions(ctx.env)) {
|
|
485
|
+
const its = questionHosts(q);
|
|
486
|
+
if (q.kind === "permission" && its.some((h) => hosts.includes(h)))
|
|
487
|
+
for (const h of its)
|
|
488
|
+
if (reached.has(h) && !hosts.includes(h))
|
|
489
|
+
hosts.push(h);
|
|
490
|
+
}
|
|
480
491
|
return { hosts, connectors };
|
|
481
492
|
}
|
|
482
493
|
const WAY_TEXT = { slow: "images slowly, at a person's pace", browser: "images through the browser on this computer", manual: "images by hand only" };
|
package/dist/commands/meta.js
CHANGED
|
@@ -113,7 +113,7 @@ function orientation(ctx) {
|
|
|
113
113
|
// the ways as the menu and the bridge offer them (a site only a browser goes to, a bot check: never slowly)
|
|
114
114
|
const connectors = listConnectors(ctx.settings.shared()?.value, ctx.env);
|
|
115
115
|
const claude = claudeHere(ctx, tree.config);
|
|
116
|
-
base.permissions = asked.map((q) => ({ host: q.host, title: q.title ?? q.host, ...(q.connector ? { connector: q.connector } : {}), since: q.at, tries: q.tries ?? 1, tasks: tasksStanding(tree, q), command: `strom allow archive ${q.host} --way ${waysOffered(connectorFor(connectors, q.host, q.connector), claude, q.way, !!q.browserOnly).join("|")}` }));
|
|
116
|
+
base.permissions = asked.map((q) => ({ host: q.host, ...(q.archiveHosts ? { hosts: q.archiveHosts } : {}), title: q.title ?? q.host, ...(q.connector ? { connector: q.connector } : {}), since: q.at, tries: q.tries ?? 1, tasks: tasksStanding(tree, q), command: `strom allow archive ${q.host} --way ${waysOffered(connectorFor(connectors, q.host, q.connector), claude, q.way, !!q.browserOnly).join("|")}` }));
|
|
117
117
|
}
|
|
118
118
|
const stories = ctx.settings.stories(tree.config);
|
|
119
119
|
base.stories = !stories.on ? "off" : stories.said || told.stories ? "on" : "tell";
|
package/dist/core/archiveasks.js
CHANGED
|
@@ -7,7 +7,7 @@ import { actsAsArchive } from "./state.js";
|
|
|
7
7
|
import { waitsOnHost } from "./archivewait.js";
|
|
8
8
|
import { waitsForDecision } from "./queue.js";
|
|
9
9
|
export { savedInInbox } from "./archivewait.js";
|
|
10
|
-
import { claimOf, pendingQuestions, termsFor } from "./terms.js";
|
|
10
|
+
import { claimOf, pendingQuestions, questionHosts, termsFor } from "./terms.js";
|
|
11
11
|
import { bareHost, listConnectors, routesOf } from "./connector.js";
|
|
12
12
|
import { ui } from "../cli/ui.js";
|
|
13
13
|
import { humanDay } from "../cli/human.js";
|
|
@@ -52,7 +52,7 @@ export function tasksWaitingOn(tree, host) {
|
|
|
52
52
|
* those the queue holds back for it, and those a fetch needed it for (still to be done) — their IDs.
|
|
53
53
|
*/
|
|
54
54
|
export function tasksStanding(tree, q) {
|
|
55
|
-
const ids = new Set(tasksWaitingOn(tree,
|
|
55
|
+
const ids = new Set(questionHosts(q).flatMap((h) => tasksWaitingOn(tree, h).map((t) => t.id)));
|
|
56
56
|
const held = waitsForDecision(tree, [q]);
|
|
57
57
|
for (const t of tree.list("task"))
|
|
58
58
|
if (t.state === "open" && held(t))
|
|
@@ -169,12 +169,13 @@ export function archiveWaitingItems(tree, shared, claude) {
|
|
|
169
169
|
{
|
|
170
170
|
kind: "permission",
|
|
171
171
|
id: q.key,
|
|
172
|
-
what: ui(lang, "ui.archive.item", { title, host: q.
|
|
172
|
+
what: ui(lang, "ui.archive.item", { title, host: questionHosts(q).join(", ") }),
|
|
173
173
|
on: termsText(lang, tree.env, q),
|
|
174
174
|
at: q.at,
|
|
175
175
|
since: q.at,
|
|
176
176
|
tries: q.tries ?? 1,
|
|
177
177
|
host: q.host,
|
|
178
|
+
...(q.archiveHosts ? { hosts: q.archiveHosts } : {}),
|
|
178
179
|
title,
|
|
179
180
|
...(q.terms ? { terms: q.terms } : {}),
|
|
180
181
|
...(q.quote ? { quote: q.quote } : {}),
|
package/dist/core/terms.js
CHANGED
|
@@ -186,12 +186,75 @@ export function updateArchives(env, change) {
|
|
|
186
186
|
* know (a list grown since covers more) — none left, no question.
|
|
187
187
|
*/
|
|
188
188
|
export function pendingQuestions(env) {
|
|
189
|
-
return loadArchives(env).pending.flatMap((q) => {
|
|
189
|
+
return oneArchive(loadArchives(env).pending.flatMap((q) => {
|
|
190
190
|
if (q.kind !== "takeover" || !q.hosts)
|
|
191
191
|
return [q];
|
|
192
192
|
const left = (q.connectors ?? []).filter((n) => !q.hosts?.[n] || !coveredByTerms(env, q.hosts[n]));
|
|
193
193
|
return left.length ? [{ ...q, connectors: left }] : [];
|
|
194
|
-
});
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
/** The hosts a question is for: those of its archive, else its own. */
|
|
197
|
+
export function questionHosts(q) {
|
|
198
|
+
return q.archiveHosts?.length ? q.archiveHosts : q.host ? [q.host] : [];
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The questions about the images of one archive — its hosts (the site's pages, its image servers) asked one by one,
|
|
202
|
+
* the same connector, site and terms — read as one: since the first time asked, the tasks of them all (each once), the
|
|
203
|
+
* ways every host may go. Each host keeps its own question in the file (an older strom reads them as before).
|
|
204
|
+
*/
|
|
205
|
+
function oneArchive(qs) {
|
|
206
|
+
const groups = new Map();
|
|
207
|
+
const out = [];
|
|
208
|
+
for (const q of qs) {
|
|
209
|
+
if (q.kind !== "permission" || !q.host) {
|
|
210
|
+
out.push(q);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const key = JSON.stringify([q.connector ?? "", webDomain(q.host), q.terms ?? ""]);
|
|
214
|
+
const g = groups.get(key);
|
|
215
|
+
if (g)
|
|
216
|
+
g.push(q);
|
|
217
|
+
else {
|
|
218
|
+
const fresh = [q];
|
|
219
|
+
groups.set(key, fresh);
|
|
220
|
+
out.push(fresh);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return out.map((x) => (Array.isArray(x) ? (x.length === 1 ? x[0] : merged(x)) : x));
|
|
224
|
+
}
|
|
225
|
+
function merged(qs) {
|
|
226
|
+
const all = [...qs].sort((a, b) => a.at.localeCompare(b.at));
|
|
227
|
+
const first = all[0];
|
|
228
|
+
const latest = (f) => all.map(f).filter((x) => !!x).sort().at(-1);
|
|
229
|
+
const tasks = [];
|
|
230
|
+
for (const q of all)
|
|
231
|
+
for (const t of q.tasks ?? (q.tree && q.task ? [{ tree: q.tree, task: q.task }] : []))
|
|
232
|
+
if (!tasks.some((x) => x.tree === t.tree && x.task === t.task))
|
|
233
|
+
tasks.push(t);
|
|
234
|
+
const lastAsked = [...all].sort((a, b) => (a.last ?? a.at).localeCompare(b.last ?? b.at)).at(-1);
|
|
235
|
+
const browserOnly = all.some((q) => q.browserOnly);
|
|
236
|
+
const choices = all.every((q) => q.choices) ? first.choices.filter((w) => all.every((q) => q.choices.includes(w)) && !(browserOnly && w === "slow")) : undefined;
|
|
237
|
+
const title = all.find((q) => q.title)?.title;
|
|
238
|
+
const level = all.some((q) => q.level) ? strictest(...all.map((q) => q.level)) : undefined;
|
|
239
|
+
const way = browserOnly ? "browser" : first.way;
|
|
240
|
+
const window = latest((q) => q.window);
|
|
241
|
+
const last = latest((q) => q.last);
|
|
242
|
+
return {
|
|
243
|
+
...first,
|
|
244
|
+
archiveHosts: [...new Set(all.map((q) => q.host))],
|
|
245
|
+
...(title ? { title } : {}),
|
|
246
|
+
...(level ? { level } : {}),
|
|
247
|
+
...(way ? { way } : {}),
|
|
248
|
+
...(choices ? { choices: choices.includes("manual") ? choices : [...choices, "manual"] } : {}),
|
|
249
|
+
...(browserOnly ? { browserOnly } : {}),
|
|
250
|
+
...(window ? { window } : {}),
|
|
251
|
+
...(lastAsked.tree ? { tree: lastAsked.tree } : {}),
|
|
252
|
+
...(lastAsked.task ? { task: lastAsked.task } : {}),
|
|
253
|
+
...(tasks.length ? { tasks } : {}),
|
|
254
|
+
// one command asks all the archive's hosts at once: the most any host was needed
|
|
255
|
+
tries: Math.max(...all.map((q) => q.tries ?? 1)),
|
|
256
|
+
...(last && last > first.at ? { last } : {}),
|
|
257
|
+
};
|
|
195
258
|
}
|
|
196
259
|
export function dropPending(s, key) {
|
|
197
260
|
s.pending = s.pending.filter((p) => p.key !== key);
|
package/dist/core/tree.js
CHANGED
|
@@ -27,7 +27,7 @@ import { lastBackup } from "./backup.js";
|
|
|
27
27
|
export const TREE_FILE = "strom.json";
|
|
28
28
|
// between releases the candidate being made (a bridge run from the sources says what it is: the Strom app goes by it);
|
|
29
29
|
// at a release the release's own (package.json)
|
|
30
|
-
export const VERSION = "1.16.0-beta.
|
|
30
|
+
export const VERSION = "1.16.0-beta.4";
|
|
31
31
|
const GITIGNORE = `# Strom Research — local state and large files are not versioned
|
|
32
32
|
.strom/
|
|
33
33
|
/media/
|