@miadi/chronicle-tui 0.2.0 → 0.4.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 +53 -0
- package/dist/App.d.ts +16 -5
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +626 -251
- package/dist/App.js.map +1 -1
- package/dist/actions.d.ts +52 -9
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +97 -20
- package/dist/actions.js.map +1 -1
- package/dist/api.d.ts +132 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +272 -0
- package/dist/api.js.map +1 -0
- package/dist/attention.d.ts +50 -0
- package/dist/attention.d.ts.map +1 -0
- package/dist/attention.js +69 -0
- package/dist/attention.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +42 -4
- package/dist/bin.js.map +1 -1
- package/dist/data.d.ts +48 -12
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +117 -23
- package/dist/data.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/model.d.ts +10 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +46 -36
- package/dist/model.js.map +1 -1
- package/dist/text.d.ts +23 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +57 -0
- package/dist/text.js.map +1 -0
- package/package.json +4 -3
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser as a known reader of the Miadi app (jgwill/Miadi#706).
|
|
3
|
+
*
|
|
4
|
+
* Reads that need no one (the catalog, files, search) stay on the local
|
|
5
|
+
* checkout. Anything that depends on who is reading, and every write, goes
|
|
6
|
+
* through the app's own routes, so the app keeps the one gate it already has
|
|
7
|
+
* (`lib/api-access.ts`, `lib/identity.ts`, `lib/ceremony-access.ts`).
|
|
8
|
+
*
|
|
9
|
+
* Two tokens, each for what it is:
|
|
10
|
+
* - `MIADI_PERSON_TOKEN` is a person. It is the bearer of every read, so a
|
|
11
|
+
* ceremony or circle shows what that person's seat allows and nothing more.
|
|
12
|
+
* - `MIADI_API_TOKEN_WRITER` passes the write gate. It is the bearer of writes,
|
|
13
|
+
* with the person named beside it in `x-miadi-person-token`, the way the app
|
|
14
|
+
* expects an agent acting for someone (`lib/review-ceremony.ts`).
|
|
15
|
+
* Either alone works for what it can do. A token is only ever sent to the app
|
|
16
|
+
* chosen at start, and never over plain http to a host that is not this one.
|
|
17
|
+
*/
|
|
18
|
+
export declare const TOKEN_ENV = "MIADI_API_TOKEN_WRITER";
|
|
19
|
+
export declare const PERSON_TOKEN_ENV = "MIADI_PERSON_TOKEN";
|
|
20
|
+
export declare const PERSON_TOKEN_HEADER = "x-miadi-person-token";
|
|
21
|
+
/** What `--env` may set. Anything else in the file (PATH included) is left alone. */
|
|
22
|
+
export declare const ENV_FILE_KEYS: readonly ["MIADI_API_TOKEN_WRITER", "MIADI_PERSON_TOKEN", "MIADI_CHRONICLE_ROOT", "MIADI_WEB_URL", "MIADI_URL_BASE_INTERNAL", "MIADI_URL_BASE", "MIADI_CHRONICLE_MW_URL"];
|
|
23
|
+
/**
|
|
24
|
+
* Load the allowed `KEY=value` lines of a file into the environment: `export`
|
|
25
|
+
* prefixes, quotes and `#` comments are understood, nothing is expanded. A key
|
|
26
|
+
* the file sets wins over one already set, since the file was named on purpose.
|
|
27
|
+
* Returns the keys it set.
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadEnvFile(path: string, env?: NodeJS.ProcessEnv): string[];
|
|
30
|
+
export interface Reader {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
role?: string;
|
|
34
|
+
tier?: string;
|
|
35
|
+
/** Circles the reader sits in. */
|
|
36
|
+
circles: Array<{
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
export declare class ApiError extends Error {
|
|
42
|
+
readonly status: number;
|
|
43
|
+
readonly code?: string | undefined;
|
|
44
|
+
constructor(message: string, status: number, code?: string | undefined);
|
|
45
|
+
}
|
|
46
|
+
/** A token may travel to https, or to plain http on this machine only. */
|
|
47
|
+
export declare function mayCarryToken(base: string): boolean;
|
|
48
|
+
export interface Tokens {
|
|
49
|
+
writer?: string;
|
|
50
|
+
person?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare class MiadiApi {
|
|
53
|
+
readonly base: string;
|
|
54
|
+
private readonly tokens;
|
|
55
|
+
/** Set once the app has answered who is reading; false when it refused the token. */
|
|
56
|
+
accepted: boolean | undefined;
|
|
57
|
+
constructor(base: string, tokens?: Tokens);
|
|
58
|
+
/** A token is set (whether or not the app accepted it yet). */
|
|
59
|
+
get signed(): boolean;
|
|
60
|
+
/** A person is named, so seats apply. */
|
|
61
|
+
get asPerson(): boolean;
|
|
62
|
+
/** Writes are offered: a token is set and the app did not refuse it. */
|
|
63
|
+
get mayWrite(): boolean;
|
|
64
|
+
private headers;
|
|
65
|
+
private call;
|
|
66
|
+
get<T>(path: string): Promise<T>;
|
|
67
|
+
post<T>(path: string, body: unknown): Promise<T>;
|
|
68
|
+
/** Who the app says is reading. Marks the client refused on 401 or 403. */
|
|
69
|
+
me(): Promise<Reader | null>;
|
|
70
|
+
/** Would the app record a write from this client? The web room asks the attention door the same. */
|
|
71
|
+
mayAnswer(): Promise<boolean>;
|
|
72
|
+
}
|
|
73
|
+
export interface Connection {
|
|
74
|
+
api: MiadiApi | null;
|
|
75
|
+
/** Why a token was not attached, when one was set. */
|
|
76
|
+
warning?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The app this browser talks to. `--url` wins; otherwise the local app, then
|
|
80
|
+
* the tailnet one (`MIADI_URL_BASE_INTERNAL`). The public front is used only
|
|
81
|
+
* when named with `--url`, so a token never goes there by accident. Probes
|
|
82
|
+
* carry no token.
|
|
83
|
+
*/
|
|
84
|
+
export declare function connectApi(url?: string, env?: NodeJS.ProcessEnv): Promise<Connection>;
|
|
85
|
+
export declare function answerAttention(api: MiadiApi, episode: string, id: string, answer: string): Promise<{
|
|
86
|
+
ok: boolean;
|
|
87
|
+
result?: {
|
|
88
|
+
synchronized?: boolean;
|
|
89
|
+
};
|
|
90
|
+
}>;
|
|
91
|
+
export interface CeremonyCard {
|
|
92
|
+
kind: "ceremony";
|
|
93
|
+
id: string;
|
|
94
|
+
type?: string;
|
|
95
|
+
direction?: string;
|
|
96
|
+
intentions: string[];
|
|
97
|
+
episode?: string;
|
|
98
|
+
circle?: string;
|
|
99
|
+
closed: boolean;
|
|
100
|
+
/** What this reader may do here, as the app says (`can`). */
|
|
101
|
+
can: string[];
|
|
102
|
+
}
|
|
103
|
+
export interface CircleCard {
|
|
104
|
+
kind: "circle";
|
|
105
|
+
id: string;
|
|
106
|
+
name: string;
|
|
107
|
+
intention?: string;
|
|
108
|
+
direction?: string;
|
|
109
|
+
type?: string;
|
|
110
|
+
active: boolean;
|
|
111
|
+
facilitator?: string;
|
|
112
|
+
ceremonies: number;
|
|
113
|
+
can: string[];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* What a ceremony shows a seated reader: its kind, direction, intention,
|
|
117
|
+
* episode, circle and whether it is closed. Never its turns: those are
|
|
118
|
+
* people's words, read in the circle (jgwill/Miadi#680, D3).
|
|
119
|
+
*/
|
|
120
|
+
export declare function ceremonyCard(api: MiadiApi, id: string): Promise<CeremonyCard>;
|
|
121
|
+
export declare function circleCard(api: MiadiApi, id: string): Promise<CircleCard>;
|
|
122
|
+
export declare function addReview(api: MiadiApi, episode: string, review: string): Promise<{
|
|
123
|
+
ok?: boolean;
|
|
124
|
+
review?: {
|
|
125
|
+
title?: string;
|
|
126
|
+
id?: string;
|
|
127
|
+
};
|
|
128
|
+
ceremony?: {
|
|
129
|
+
state?: string;
|
|
130
|
+
};
|
|
131
|
+
}>;
|
|
132
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser as a known reader of the Miadi app (jgwill/Miadi#706).
|
|
3
|
+
*
|
|
4
|
+
* Reads that need no one (the catalog, files, search) stay on the local
|
|
5
|
+
* checkout. Anything that depends on who is reading, and every write, goes
|
|
6
|
+
* through the app's own routes, so the app keeps the one gate it already has
|
|
7
|
+
* (`lib/api-access.ts`, `lib/identity.ts`, `lib/ceremony-access.ts`).
|
|
8
|
+
*
|
|
9
|
+
* Two tokens, each for what it is:
|
|
10
|
+
* - `MIADI_PERSON_TOKEN` is a person. It is the bearer of every read, so a
|
|
11
|
+
* ceremony or circle shows what that person's seat allows and nothing more.
|
|
12
|
+
* - `MIADI_API_TOKEN_WRITER` passes the write gate. It is the bearer of writes,
|
|
13
|
+
* with the person named beside it in `x-miadi-person-token`, the way the app
|
|
14
|
+
* expects an agent acting for someone (`lib/review-ceremony.ts`).
|
|
15
|
+
* Either alone works for what it can do. A token is only ever sent to the app
|
|
16
|
+
* chosen at start, and never over plain http to a host that is not this one.
|
|
17
|
+
*/
|
|
18
|
+
import fs from "node:fs";
|
|
19
|
+
import { readWebBases } from "@miadi/inquiry-weave";
|
|
20
|
+
export const TOKEN_ENV = "MIADI_API_TOKEN_WRITER";
|
|
21
|
+
export const PERSON_TOKEN_ENV = "MIADI_PERSON_TOKEN";
|
|
22
|
+
export const PERSON_TOKEN_HEADER = "x-miadi-person-token";
|
|
23
|
+
/** What `--env` may set. Anything else in the file (PATH included) is left alone. */
|
|
24
|
+
export const ENV_FILE_KEYS = [
|
|
25
|
+
TOKEN_ENV,
|
|
26
|
+
PERSON_TOKEN_ENV,
|
|
27
|
+
"MIADI_CHRONICLE_ROOT",
|
|
28
|
+
"MIADI_WEB_URL",
|
|
29
|
+
"MIADI_URL_BASE_INTERNAL",
|
|
30
|
+
"MIADI_URL_BASE",
|
|
31
|
+
"MIADI_CHRONICLE_MW_URL",
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Load the allowed `KEY=value` lines of a file into the environment: `export`
|
|
35
|
+
* prefixes, quotes and `#` comments are understood, nothing is expanded. A key
|
|
36
|
+
* the file sets wins over one already set, since the file was named on purpose.
|
|
37
|
+
* Returns the keys it set.
|
|
38
|
+
*/
|
|
39
|
+
export function loadEnvFile(path, env = process.env) {
|
|
40
|
+
const allowed = new Set(ENV_FILE_KEYS);
|
|
41
|
+
const loaded = [];
|
|
42
|
+
for (const raw of fs.readFileSync(path, "utf8").split(/\r?\n/)) {
|
|
43
|
+
const line = raw.trim();
|
|
44
|
+
if (!line || line.startsWith("#"))
|
|
45
|
+
continue;
|
|
46
|
+
const m = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
|
|
47
|
+
if (!m || !allowed.has(m[1]))
|
|
48
|
+
continue;
|
|
49
|
+
let value = m[2];
|
|
50
|
+
const quoted = /^(["'])(.*?)\1(?:\s+#.*)?$/.exec(value);
|
|
51
|
+
if (quoted)
|
|
52
|
+
value = quoted[2];
|
|
53
|
+
else
|
|
54
|
+
value = value.replace(/\s+#.*$/, "").trim();
|
|
55
|
+
env[m[1]] = value;
|
|
56
|
+
loaded.push(m[1]);
|
|
57
|
+
}
|
|
58
|
+
return loaded;
|
|
59
|
+
}
|
|
60
|
+
export class ApiError extends Error {
|
|
61
|
+
status;
|
|
62
|
+
code;
|
|
63
|
+
constructor(message, status, code) {
|
|
64
|
+
super(message);
|
|
65
|
+
this.status = status;
|
|
66
|
+
this.code = code;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** A token may travel to https, or to plain http on this machine only. */
|
|
70
|
+
export function mayCarryToken(base) {
|
|
71
|
+
try {
|
|
72
|
+
const url = new URL(base);
|
|
73
|
+
if (url.protocol === "https:")
|
|
74
|
+
return true;
|
|
75
|
+
return url.protocol === "http:" && ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export class MiadiApi {
|
|
82
|
+
base;
|
|
83
|
+
tokens;
|
|
84
|
+
/** Set once the app has answered who is reading; false when it refused the token. */
|
|
85
|
+
accepted;
|
|
86
|
+
constructor(base, tokens = {}) {
|
|
87
|
+
this.base = base;
|
|
88
|
+
this.tokens = tokens;
|
|
89
|
+
}
|
|
90
|
+
/** A token is set (whether or not the app accepted it yet). */
|
|
91
|
+
get signed() {
|
|
92
|
+
return Boolean(this.tokens.writer || this.tokens.person);
|
|
93
|
+
}
|
|
94
|
+
/** A person is named, so seats apply. */
|
|
95
|
+
get asPerson() {
|
|
96
|
+
return Boolean(this.tokens.person);
|
|
97
|
+
}
|
|
98
|
+
/** Writes are offered: a token is set and the app did not refuse it. */
|
|
99
|
+
get mayWrite() {
|
|
100
|
+
return this.signed && this.accepted !== false;
|
|
101
|
+
}
|
|
102
|
+
headers(write) {
|
|
103
|
+
const { writer, person } = this.tokens;
|
|
104
|
+
const bearer = write ? (writer ?? person) : (person ?? writer);
|
|
105
|
+
const headers = {};
|
|
106
|
+
if (bearer)
|
|
107
|
+
headers.authorization = `Bearer ${bearer}`;
|
|
108
|
+
if (write && writer && person)
|
|
109
|
+
headers[PERSON_TOKEN_HEADER] = person;
|
|
110
|
+
return headers;
|
|
111
|
+
}
|
|
112
|
+
async call(method, path, body) {
|
|
113
|
+
const controller = new AbortController();
|
|
114
|
+
const timer = setTimeout(() => controller.abort(), 20_000);
|
|
115
|
+
try {
|
|
116
|
+
const response = await fetch(`${this.base}${path}`, {
|
|
117
|
+
method,
|
|
118
|
+
signal: controller.signal,
|
|
119
|
+
headers: {
|
|
120
|
+
accept: "application/json",
|
|
121
|
+
...(body === undefined ? {} : { "content-type": "application/json" }),
|
|
122
|
+
...this.headers(method === "POST"),
|
|
123
|
+
},
|
|
124
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
125
|
+
});
|
|
126
|
+
const text = await response.text();
|
|
127
|
+
let json = null;
|
|
128
|
+
try {
|
|
129
|
+
json = text ? JSON.parse(text) : null;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
json = null;
|
|
133
|
+
}
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
const detail = json;
|
|
136
|
+
throw new ApiError(detail?.message ?? detail?.error ?? `${method} ${path}: HTTP ${response.status}`, response.status, detail?.error);
|
|
137
|
+
}
|
|
138
|
+
return json;
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
clearTimeout(timer);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
get(path) {
|
|
145
|
+
return this.call("GET", path);
|
|
146
|
+
}
|
|
147
|
+
post(path, body) {
|
|
148
|
+
return this.call("POST", path, body);
|
|
149
|
+
}
|
|
150
|
+
/** Who the app says is reading. Marks the client refused on 401 or 403. */
|
|
151
|
+
async me() {
|
|
152
|
+
if (!this.signed)
|
|
153
|
+
return null;
|
|
154
|
+
try {
|
|
155
|
+
const me = await this.get("/api/identity/me");
|
|
156
|
+
this.accepted = Boolean(me.person);
|
|
157
|
+
if (!me.person)
|
|
158
|
+
return null;
|
|
159
|
+
return {
|
|
160
|
+
id: me.person.id,
|
|
161
|
+
name: me.person.name,
|
|
162
|
+
role: me.person.role,
|
|
163
|
+
tier: me.api_tier,
|
|
164
|
+
circles: (me.circles ?? []).map((circle) => ({ id: circle.id, name: circle.name })),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
if (error instanceof ApiError && (error.status === 401 || error.status === 403))
|
|
169
|
+
this.accepted = false;
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** Would the app record a write from this client? The web room asks the attention door the same. */
|
|
174
|
+
async mayAnswer() {
|
|
175
|
+
if (!this.mayWrite)
|
|
176
|
+
return false;
|
|
177
|
+
try {
|
|
178
|
+
const body = await this.call("GET", "/api/chronicle/attention?capabilities=1");
|
|
179
|
+
return body?.capabilities?.answer === true;
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function answers(base) {
|
|
187
|
+
const controller = new AbortController();
|
|
188
|
+
const timer = setTimeout(() => controller.abort(), 2500);
|
|
189
|
+
try {
|
|
190
|
+
const response = await fetch(`${base}/api/health`, { signal: controller.signal });
|
|
191
|
+
return response.ok;
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
clearTimeout(timer);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The app this browser talks to. `--url` wins; otherwise the local app, then
|
|
202
|
+
* the tailnet one (`MIADI_URL_BASE_INTERNAL`). The public front is used only
|
|
203
|
+
* when named with `--url`, so a token never goes there by accident. Probes
|
|
204
|
+
* carry no token.
|
|
205
|
+
*/
|
|
206
|
+
export async function connectApi(url, env = process.env) {
|
|
207
|
+
const tokens = {
|
|
208
|
+
writer: env[TOKEN_ENV]?.trim() || undefined,
|
|
209
|
+
person: env[PERSON_TOKEN_ENV]?.trim() || undefined,
|
|
210
|
+
};
|
|
211
|
+
const make = (base) => {
|
|
212
|
+
const clean = base.replace(/\/+$/, "");
|
|
213
|
+
if ((tokens.writer || tokens.person) && !mayCarryToken(clean)) {
|
|
214
|
+
return { api: new MiadiApi(clean), warning: `${clean} is plain http on another host: the token stays home and the app is read-only.` };
|
|
215
|
+
}
|
|
216
|
+
return { api: new MiadiApi(clean, tokens) };
|
|
217
|
+
};
|
|
218
|
+
if (url)
|
|
219
|
+
return make(url);
|
|
220
|
+
const bases = readWebBases(env);
|
|
221
|
+
for (const base of [bases.local, bases.internal]) {
|
|
222
|
+
if (base && (await answers(base.replace(/\/+$/, ""))))
|
|
223
|
+
return make(base);
|
|
224
|
+
}
|
|
225
|
+
return { api: null };
|
|
226
|
+
}
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
// The routes the browser uses, typed as the app answers them (checked 2026-10-01)
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
export function answerAttention(api, episode, id, answer) {
|
|
231
|
+
return api.post("/api/chronicle/attention", { episode, id, answer, expectedState: "open" });
|
|
232
|
+
}
|
|
233
|
+
const trueKeys = (can) => can && typeof can === "object" ? Object.entries(can).filter(([, v]) => v === true).map(([k]) => k) : [];
|
|
234
|
+
/**
|
|
235
|
+
* What a ceremony shows a seated reader: its kind, direction, intention,
|
|
236
|
+
* episode, circle and whether it is closed. Never its turns: those are
|
|
237
|
+
* people's words, read in the circle (jgwill/Miadi#680, D3).
|
|
238
|
+
*/
|
|
239
|
+
export async function ceremonyCard(api, id) {
|
|
240
|
+
const view = await api.get(`/api/ceremony/${encodeURIComponent(id)}`);
|
|
241
|
+
const intentions = view.ceremony.intentions;
|
|
242
|
+
return {
|
|
243
|
+
kind: "ceremony",
|
|
244
|
+
id: view.ceremony.id,
|
|
245
|
+
type: view.ceremony.type,
|
|
246
|
+
direction: view.ceremony.direction,
|
|
247
|
+
intentions: (Array.isArray(intentions) ? intentions : intentions ? [intentions] : []).map((line) => line.replace(/\s+/g, " ").trim()),
|
|
248
|
+
episode: view.episode?.episode_path,
|
|
249
|
+
circle: view.circle?.name,
|
|
250
|
+
closed: Boolean(view.closed),
|
|
251
|
+
can: trueKeys(view.can),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
export async function circleCard(api, id) {
|
|
255
|
+
const view = await api.get(`/api/circles/${encodeURIComponent(id)}`);
|
|
256
|
+
return {
|
|
257
|
+
kind: "circle",
|
|
258
|
+
id: view.circle.id,
|
|
259
|
+
name: view.circle.name,
|
|
260
|
+
intention: view.circle.intention?.replace(/\s+/g, " ").trim(),
|
|
261
|
+
direction: view.circle.direction,
|
|
262
|
+
type: view.circle.circle_type,
|
|
263
|
+
active: view.circle.active !== false,
|
|
264
|
+
facilitator: view.circle.facilitator_name,
|
|
265
|
+
ceremonies: Array.isArray(view.ceremonies) ? view.ceremonies.length : 0,
|
|
266
|
+
can: trueKeys(view.can),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
export function addReview(api, episode, review) {
|
|
270
|
+
return api.post(`/api/chronicle/episodes/${encodeURIComponent(episode)}/reviews`, { review });
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD,MAAM,CAAC,MAAM,SAAS,GAAG,wBAAwB,CAAA;AACjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,oBAAoB,CAAA;AACpD,MAAM,CAAC,MAAM,mBAAmB,GAAG,sBAAsB,CAAA;AAEzD,qFAAqF;AACrF,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,SAAS;IACT,gBAAgB;IAChB,sBAAsB;IACtB,eAAe;IACf,yBAAyB;IACzB,gBAAgB;IAChB,wBAAwB;CAChB,CAAA;AAEV;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC5E,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,aAAa,CAAC,CAAA;IAC9C,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;QACvB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAQ;QAC3C,MAAM,CAAC,GAAG,qDAAqD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1E,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC;YAAE,SAAQ;QACvC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAE,CAAA;QACjB,MAAM,MAAM,GAAG,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvD,IAAI,MAAM;YAAE,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAA;;YACzB,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QAChD,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,GAAG,KAAK,CAAA;QAClB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAA;IACpB,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAWD,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGtB;IACA;IAHX,YACE,OAAe,EACN,MAAc,EACd,IAAa;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAA;QAHL,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAS;IAGxB,CAAC;CACF;AAED,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;QACzB,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QAC1C,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC/F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAOD,MAAM,OAAO,QAAQ;IAKR;IACQ;IALnB,qFAAqF;IACrF,QAAQ,CAAqB;IAE7B,YACW,IAAY,EACJ,SAAiB,EAAE;QAD3B,SAAI,GAAJ,IAAI,CAAQ;QACJ,WAAM,GAAN,MAAM,CAAa;IACnC,CAAC;IAEJ,+DAA+D;IAC/D,IAAI,MAAM;QACR,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC1D,CAAC;IAED,yCAAyC;IACzC,IAAI,QAAQ;QACV,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;IAED,wEAAwE;IACxE,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAA;IAC/C,CAAC;IAEO,OAAO,CAAC,KAAc;QAC5B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAA;QACtC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,CAAA;QAC9D,MAAM,OAAO,GAA2B,EAAE,CAAA;QAC1C,IAAI,MAAM;YAAE,OAAO,CAAC,aAAa,GAAG,UAAU,MAAM,EAAE,CAAA;QACtD,IAAI,KAAK,IAAI,MAAM,IAAI,MAAM;YAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAA;QACpE,OAAO,OAAO,CAAA;IAChB,CAAC;IAEO,KAAK,CAAC,IAAI,CAAI,MAAsB,EAAE,IAAY,EAAE,IAAc;QACxE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;QACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAA;QAC1D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,EAAE,EAAE;gBAClD,MAAM;gBACN,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,OAAO,EAAE;oBACP,MAAM,EAAE,kBAAkB;oBAC1B,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;oBACrE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC;iBACnC;gBACD,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC5D,CAAC,CAAA;YACF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAClC,IAAI,IAAI,GAAY,IAAI,CAAA;YACxB,IAAI,CAAC;gBACH,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,GAAG,IAAI,CAAA;YACb,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,MAAM,GAAG,IAAmD,CAAA;gBAClE,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,IAAI,MAAM,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,IAAI,UAAU,QAAQ,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;YACtI,CAAC;YACD,OAAO,IAAS,CAAA;QAClB,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IAED,GAAG,CAAI,IAAY;QACjB,OAAO,IAAI,CAAC,IAAI,CAAI,KAAK,EAAE,IAAI,CAAC,CAAA;IAClC,CAAC;IAED,IAAI,CAAI,IAAY,EAAE,IAAa;QACjC,OAAO,IAAI,CAAC,IAAI,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IACzC,CAAC;IAED,2EAA2E;IAC3E,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QAC7B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAItB,kBAAkB,CAAC,CAAA;YACtB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAA;YAClC,IAAI,CAAC,EAAE,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAA;YAC3B,OAAO;gBACL,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE;gBAChB,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI;gBACpB,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI;gBACpB,IAAI,EAAE,EAAE,CAAC,QAAQ;gBACjB,OAAO,EAAE,CAAC,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;aACpF,CAAA;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,QAAQ,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,CAAC;gBAAE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAA;YACtG,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED,oGAAoG;IACpG,KAAK,CAAC,SAAS;QACb,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAA;QAChC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAA0C,KAAK,EAAE,yCAAyC,CAAC,CAAA;YACvH,OAAO,IAAI,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;CACF;AAED,KAAK,UAAU,OAAO,CAAC,IAAY;IACjC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAA;IACxD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,aAAa,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;QACjF,OAAO,QAAQ,CAAC,EAAE,CAAA;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAA;IACrB,CAAC;AACH,CAAC;AAQD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAY,EAAE,MAAyB,OAAO,CAAC,GAAG;IACjF,MAAM,MAAM,GAAW;QACrB,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS;QAC3C,MAAM,EAAE,GAAG,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS;KACnD,CAAA;IACD,MAAM,IAAI,GAAG,CAAC,IAAY,EAAc,EAAE;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QACtC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9D,OAAO,EAAE,GAAG,EAAE,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,gFAAgF,EAAE,CAAA;QACxI,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,IAAI,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAA;IAC7C,CAAC,CAAA;IACD,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAA;IACzB,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;IAC/B,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjD,IAAI,IAAI,IAAI,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC,CAAA;IAC1E,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;AACtB,CAAC;AAED,8EAA8E;AAC9E,kFAAkF;AAClF,8EAA8E;AAE9E,MAAM,UAAU,eAAe,CAC7B,GAAa,EACb,OAAe,EACf,EAAU,EACV,MAAc;IAEd,OAAO,GAAG,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAA;AAC7F,CAAC;AA4BD,MAAM,QAAQ,GAAG,CAAC,GAAY,EAAY,EAAE,CAC1C,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAA8B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAEpI;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAa,EAAE,EAAU;IAC1D,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,GAAG,CAMvB,iBAAiB,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAA;IAC3C,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE;QACpB,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;QACxB,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS;QAClC,UAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACrI,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY;QACnC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;QACzB,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;QAC5B,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;KACxB,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAa,EAAE,EAAU;IACxD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,GAAG,CAIvB,gBAAgB,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IAC5C,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE;QAClB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;QACtB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE;QAC7D,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;QAChC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;QAC7B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,KAAK;QACpC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,gBAAgB;QACzC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;KACxB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,GAAa,EACb,OAAe,EACf,MAAc;IAEd,OAAO,GAAG,CAAC,IAAI,CAAC,2BAA2B,kBAAkB,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;AAC/F,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chronicle ATTENTION in the terminal, as the web room has it (jgwill/Miadi#706).
|
|
3
|
+
*
|
|
4
|
+
* The room shows attention twice: a chronicle-wide board of every open item,
|
|
5
|
+
* newest-asked first, filtered by the same search box as the episodes
|
|
6
|
+
* (`app/chronicle/components/ChronicleAttention.tsx`), and each episode's own
|
|
7
|
+
* panel with Open and History and the cited context of each question
|
|
8
|
+
* (`EpisodeAttention.tsx`). Both are hosts of `@miadi/attention-core`, whose
|
|
9
|
+
* plain functions decide which list shows, what each heading says and whether
|
|
10
|
+
* an answer may be recorded. This module is the terminal's host of the same
|
|
11
|
+
* core: it reads through `@miadi/inquiry-weave` exactly as the app does, and
|
|
12
|
+
* answers through the app's `POST /api/chronicle/attention`.
|
|
13
|
+
*/
|
|
14
|
+
import { type AttentionPanelLabels, type AttentionView } from "@miadi/attention-core";
|
|
15
|
+
import { type ChronicleAttentionItem } from "@miadi/inquiry-weave";
|
|
16
|
+
export type { AttentionView };
|
|
17
|
+
export type BoardItem = ChronicleAttentionItem;
|
|
18
|
+
/** The board's words, as the web room's board says them. */
|
|
19
|
+
export declare const BOARD_LABELS: Required<AttentionPanelLabels>;
|
|
20
|
+
/** One episode's panel keeps the core's own words. */
|
|
21
|
+
export declare const ROOM_LABELS: Required<AttentionPanelLabels>;
|
|
22
|
+
export declare function readOnlyLabel(tokenEnv: string): string;
|
|
23
|
+
/** Every open item across the chronicle, newest-asked first: what the board lists. */
|
|
24
|
+
export declare function loadBoard(root: string): Promise<BoardItem[]>;
|
|
25
|
+
export interface EpisodePanel {
|
|
26
|
+
episode: string;
|
|
27
|
+
items: BoardItem[];
|
|
28
|
+
parseError?: string;
|
|
29
|
+
}
|
|
30
|
+
/** One episode's items with the cited context of each, as the room reads them. */
|
|
31
|
+
export declare function loadEpisodePanel(root: string, episode: string): Promise<EpisodePanel>;
|
|
32
|
+
/** The board answers the episode search box: question, episode and what it unlocks. */
|
|
33
|
+
export declare function filterBoard(items: readonly BoardItem[], query: string): BoardItem[];
|
|
34
|
+
/** The list a panel shows, and its heading, decided by the core. */
|
|
35
|
+
export declare function panelView(items: readonly BoardItem[], selected: AttentionView, labels: Required<AttentionPanelLabels>, parseError?: string): {
|
|
36
|
+
view: AttentionView;
|
|
37
|
+
shown: BoardItem[];
|
|
38
|
+
heading: string;
|
|
39
|
+
meta: string;
|
|
40
|
+
};
|
|
41
|
+
/** Check an answer the way every host checks it, before anything is sent. */
|
|
42
|
+
export declare function checkAnswer(draft: string, maxLength?: number): import("@miadi/attention-core").AnswerValidation;
|
|
43
|
+
/** What the room says once an answer is recorded: the wheel is reported, never assumed. */
|
|
44
|
+
export declare function answeredMessage(result: {
|
|
45
|
+
synchronized?: boolean;
|
|
46
|
+
} | undefined): {
|
|
47
|
+
tone: "ok" | "warn";
|
|
48
|
+
message: string;
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=attention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention.d.ts","sourceRoot":"","sources":["../src/attention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAQL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACnB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,aAAa,EAAE,CAAA;AAC7B,MAAM,MAAM,SAAS,GAAG,sBAAsB,CAAA;AAE9C,4DAA4D;AAC5D,eAAO,MAAM,YAAY,gCAKvB,CAAA;AAEF,sDAAsD;AACtD,eAAO,MAAM,WAAW,gCAAkB,CAAA;AAE1C,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,sFAAsF;AACtF,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAGlE;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,kFAAkF;AAClF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAG3F;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAOnF;AAED,oEAAoE;AACpE,wBAAgB,SAAS,CACvB,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,QAAQ,EAAE,aAAa,EACvB,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC,EACtC,UAAU,CAAC,EAAE,MAAM,GAClB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAS5E;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAA4B,oDAE/E;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAIxH"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chronicle ATTENTION in the terminal, as the web room has it (jgwill/Miadi#706).
|
|
3
|
+
*
|
|
4
|
+
* The room shows attention twice: a chronicle-wide board of every open item,
|
|
5
|
+
* newest-asked first, filtered by the same search box as the episodes
|
|
6
|
+
* (`app/chronicle/components/ChronicleAttention.tsx`), and each episode's own
|
|
7
|
+
* panel with Open and History and the cited context of each question
|
|
8
|
+
* (`EpisodeAttention.tsx`). Both are hosts of `@miadi/attention-core`, whose
|
|
9
|
+
* plain functions decide which list shows, what each heading says and whether
|
|
10
|
+
* an answer may be recorded. This module is the terminal's host of the same
|
|
11
|
+
* core: it reads through `@miadi/inquiry-weave` exactly as the app does, and
|
|
12
|
+
* answers through the app's `POST /api/chronicle/attention`.
|
|
13
|
+
*/
|
|
14
|
+
import { DEFAULT_ANSWER_MAX_LENGTH, headingMeta, headingText, partitionItems, resolveLabels, resolveView, validateAnswer, } from "@miadi/attention-core";
|
|
15
|
+
import { listChronicleAttention, readEpisodeAttention, } from "@miadi/inquiry-weave";
|
|
16
|
+
/** The board's words, as the web room's board says them. */
|
|
17
|
+
export const BOARD_LABELS = resolveLabels({
|
|
18
|
+
title: "Needs your attention",
|
|
19
|
+
completeTitle: "Nothing waits on you",
|
|
20
|
+
openSuffix: "newest first — answer one here, or open its room",
|
|
21
|
+
completeSuffix: "decisions recorded across the chronicle",
|
|
22
|
+
});
|
|
23
|
+
/** One episode's panel keeps the core's own words. */
|
|
24
|
+
export const ROOM_LABELS = resolveLabels();
|
|
25
|
+
export function readOnlyLabel(tokenEnv) {
|
|
26
|
+
return `View only — answering needs the Miadi app and ${tokenEnv} or MIADI_PERSON_TOKEN (or --env <file>).`;
|
|
27
|
+
}
|
|
28
|
+
/** Every open item across the chronicle, newest-asked first: what the board lists. */
|
|
29
|
+
export async function loadBoard(root) {
|
|
30
|
+
const items = await listChronicleAttention(root, { state: "open" });
|
|
31
|
+
return items.filter((item) => item.state === "open");
|
|
32
|
+
}
|
|
33
|
+
/** One episode's items with the cited context of each, as the room reads them. */
|
|
34
|
+
export async function loadEpisodePanel(root, episode) {
|
|
35
|
+
const attention = await readEpisodeAttention(root, episode, { includeContext: true });
|
|
36
|
+
return { episode, items: attention?.items ?? [], parseError: attention?.parseError };
|
|
37
|
+
}
|
|
38
|
+
/** The board answers the episode search box: question, episode and what it unlocks. */
|
|
39
|
+
export function filterBoard(items, query) {
|
|
40
|
+
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
41
|
+
if (words.length === 0)
|
|
42
|
+
return [...items];
|
|
43
|
+
return items.filter((item) => {
|
|
44
|
+
const haystack = [item.question, item.episode, item.unlocks, item.id].filter(Boolean).join(" ").toLowerCase();
|
|
45
|
+
return words.every((word) => haystack.includes(word));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/** The list a panel shows, and its heading, decided by the core. */
|
|
49
|
+
export function panelView(items, selected, labels, parseError) {
|
|
50
|
+
const { open, answered } = partitionItems(items);
|
|
51
|
+
const view = resolveView(selected, open.length, answered.length);
|
|
52
|
+
return {
|
|
53
|
+
view,
|
|
54
|
+
shown: view === "history" ? answered : view === "open" ? open : [],
|
|
55
|
+
heading: headingText(open.length > 0, labels),
|
|
56
|
+
meta: headingMeta({ parseError, openCount: open.length, answeredCount: answered.length, labels }),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Check an answer the way every host checks it, before anything is sent. */
|
|
60
|
+
export function checkAnswer(draft, maxLength = DEFAULT_ANSWER_MAX_LENGTH) {
|
|
61
|
+
return validateAnswer(draft, ROOM_LABELS, maxLength);
|
|
62
|
+
}
|
|
63
|
+
/** What the room says once an answer is recorded: the wheel is reported, never assumed. */
|
|
64
|
+
export function answeredMessage(result) {
|
|
65
|
+
return result?.synchronized === true
|
|
66
|
+
? { tone: "ok", message: "Answer recorded in the episode and verified on the wheel." }
|
|
67
|
+
: { tone: "warn", message: "Answer recorded in the episode; the wheel projection is pending." };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=attention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention.js","sourceRoot":"","sources":["../src/attention.ts"],"names":[],"mappings":"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"}
|
package/dist/bin.d.ts
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* miadi-chronicle 352 open on one episode (number, ep352 or folder name)
|
|
8
8
|
* miadi-chronicle --search "talking circle" open in full-text mode
|
|
9
9
|
* miadi-chronicle --filter review open with a title filter
|
|
10
|
+
* miadi-chronicle --env ~/.env.miadi load the tokens, the root and the app's address first
|
|
11
|
+
* miadi-chronicle --attention open on the chronicle's attention board
|
|
12
|
+
* miadi-chronicle --url https://miadi.tail3b11eb.ts.net act through this app
|
|
10
13
|
*
|
|
11
14
|
* Exit codes: 0 ok, 2 usage (no root, no terminal).
|
|
12
15
|
*/
|
package/dist/bin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.tsx"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.tsx"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG"}
|
package/dist/bin.js
CHANGED
|
@@ -3,7 +3,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
import { App } from "./App.js";
|
|
6
|
-
|
|
6
|
+
import { ENV_FILE_KEYS, PERSON_TOKEN_ENV, TOKEN_ENV, loadEnvFile } from "./api.js";
|
|
7
|
+
const USAGE = `miadi-chronicle [episode] [--root <dir>] [--search <text>] [--filter <text>] [--attention] [--env <file>] [--url <url>]
|
|
7
8
|
|
|
8
9
|
Browse the Miadi Chronicle: list and filter episodes, search their text, read a
|
|
9
10
|
vessel's files, and open the room, the wheel or the vessel.
|
|
@@ -12,15 +13,46 @@ vessel's files, and open the room, the wheel or the vessel.
|
|
|
12
13
|
--root <dir> the chronicle (default: $MIADI_CHRONICLE_ROOT)
|
|
13
14
|
--search <text> start in full-text mode
|
|
14
15
|
--filter <text> start with a title filter
|
|
16
|
+
--attention open on the chronicle's attention board
|
|
17
|
+
--env <file> load these keys from a KEY=value file, nothing else:
|
|
18
|
+
${ENV_FILE_KEYS.join(", ")}
|
|
19
|
+
--url <url> the Miadi app to act through (default: MIADI_WEB_URL or 127.0.0.1:3335,
|
|
20
|
+
then MIADI_URL_BASE_INTERNAL; the public front only when named here)
|
|
15
21
|
--version, --help
|
|
16
22
|
|
|
17
23
|
Keys: / filter · Tab title|full text · ↑↓ j k · Enter open · Esc back
|
|
18
|
-
o doors · r QR · p five-stage proof ·
|
|
24
|
+
o doors · r QR · p five-stage proof · a attention · ! attention board
|
|
25
|
+
v hold a review · ? all keys · q quit
|
|
26
|
+
|
|
27
|
+
${PERSON_TOKEN_ENV} names you: the header says who is reading, and a ceremony
|
|
28
|
+
or circle shows what your seat allows. ${TOKEN_ENV} passes the write gate:
|
|
29
|
+
answering attention and holding a review. Without either, the browser shows
|
|
30
|
+
what a visitor of the web room sees.
|
|
19
31
|
`;
|
|
20
32
|
function main(argv) {
|
|
33
|
+
// --env comes first, wherever it is written: everything below may read what it sets.
|
|
34
|
+
const withheld = [];
|
|
35
|
+
for (let index = 0; index < argv.length; index++) {
|
|
36
|
+
if (argv[index] !== "--env")
|
|
37
|
+
continue;
|
|
38
|
+
const file = argv[index + 1];
|
|
39
|
+
if (!file) {
|
|
40
|
+
process.stderr.write("--env needs a file.\n");
|
|
41
|
+
return 2;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
withheld.push(...loadEnvFile(file));
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
process.stderr.write(`Cannot read ${file}: ${error.message}\n`);
|
|
48
|
+
return 2;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
21
51
|
let root = process.env.MIADI_CHRONICLE_ROOT?.trim() || "";
|
|
52
|
+
let url;
|
|
22
53
|
let search;
|
|
23
54
|
let filter;
|
|
55
|
+
let attention = false;
|
|
24
56
|
let episode;
|
|
25
57
|
for (let index = 0; index < argv.length; index++) {
|
|
26
58
|
const arg = argv[index];
|
|
@@ -33,12 +65,18 @@ function main(argv) {
|
|
|
33
65
|
process.stdout.write(`${pkg.version}\n`);
|
|
34
66
|
return 0;
|
|
35
67
|
}
|
|
36
|
-
if (arg === "--
|
|
68
|
+
if (arg === "--env")
|
|
69
|
+
index++;
|
|
70
|
+
else if (arg === "--url")
|
|
71
|
+
url = argv[++index];
|
|
72
|
+
else if (arg === "--root")
|
|
37
73
|
root = argv[++index] ?? "";
|
|
38
74
|
else if (arg === "--search")
|
|
39
75
|
search = argv[++index];
|
|
40
76
|
else if (arg === "--filter")
|
|
41
77
|
filter = argv[++index];
|
|
78
|
+
else if (arg === "--attention")
|
|
79
|
+
attention = true;
|
|
42
80
|
else if (arg.startsWith("--")) {
|
|
43
81
|
process.stderr.write(`Unknown option ${arg}\n\n${USAGE}`);
|
|
44
82
|
return 2;
|
|
@@ -54,7 +92,7 @@ function main(argv) {
|
|
|
54
92
|
process.stderr.write("miadi-chronicle needs a terminal. For a plain list, run: inquiry-weave catalog\n");
|
|
55
93
|
return 2;
|
|
56
94
|
}
|
|
57
|
-
render(_jsx(App, { root: root, search: search, query: filter, episode: episode }), { alternateScreen: true });
|
|
95
|
+
render(_jsx(App, { root: root, search: search, query: filter, episode: episode, attention: attention, url: url, withheld: withheld }), { alternateScreen: true });
|
|
58
96
|
return 0;
|
|
59
97
|
}
|
|
60
98
|
const code = main(process.argv.slice(2));
|