@tidegap/cli 0.18.0 → 0.19.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 +13 -2
- package/dist/commands/clients.d.ts +20 -0
- package/dist/commands/clients.d.ts.map +1 -0
- package/dist/commands/clients.js +36 -0
- package/dist/commands/clients.js.map +1 -0
- package/dist/commands/env-file.d.ts +16 -15
- package/dist/commands/env-file.d.ts.map +1 -1
- package/dist/commands/env-file.js +49 -23
- package/dist/commands/env-file.js.map +1 -1
- package/dist/commands/install.d.ts +2 -3
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +13 -15
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/tick.d.ts +1 -1
- package/dist/commands/tick.d.ts.map +1 -1
- package/dist/commands/tick.js +3 -3
- package/dist/commands/tick.js.map +1 -1
- package/dist/commands/ui.d.ts +8 -0
- package/dist/commands/ui.d.ts.map +1 -1
- package/dist/commands/ui.js +41 -5
- package/dist/commands/ui.js.map +1 -1
- package/dist/env.d.ts +12 -10
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +50 -35
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +1 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -20
- package/dist/index.js.map +1 -1
- package/dist/ui/api.d.ts +7 -0
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +180 -26
- package/dist/ui/api.js.map +1 -1
- package/dist/ui/clients.d.ts +65 -0
- package/dist/ui/clients.d.ts.map +1 -0
- package/dist/ui/clients.js +179 -0
- package/dist/ui/clients.js.map +1 -0
- package/dist/ui/discovery.d.ts +25 -0
- package/dist/ui/discovery.d.ts.map +1 -0
- package/dist/ui/discovery.js +46 -0
- package/dist/ui/discovery.js.map +1 -0
- package/dist/ui/server.d.ts +24 -9
- package/dist/ui/server.d.ts.map +1 -1
- package/dist/ui/server.js +99 -35
- package/dist/ui/server.js.map +1 -1
- package/dist/ui/summary.d.ts +47 -0
- package/dist/ui/summary.d.ts.map +1 -0
- package/dist/ui/summary.js +120 -0
- package/dist/ui/summary.js.map +1 -0
- package/package.json +4 -5
- package/ui/api.js +3 -0
- package/ui/app.css +9 -3
- package/ui/app.js +2 -0
- package/ui/clients.js +104 -0
- package/ui/dialog.js +5 -0
- package/ui/index.html +2 -1
- package/ui/tokens.css +2 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery file for `tidegap ui` (native clients KTD4). While the server
|
|
3
|
+
* runs, `<tidegap home>/ui.json` (mode 0600) names its pid and port so the
|
|
4
|
+
* Mac app can find it without the user typing anything. It holds no token.
|
|
5
|
+
* The file is removed on close only when it still names this process, so a
|
|
6
|
+
* second server's file is never deleted by the first one.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DISCOVERY_FILE = "ui.json";
|
|
9
|
+
/** Default port of `tidegap ui`; clients look here first. */
|
|
10
|
+
export declare const DEFAULT_UI_PORT = 47821;
|
|
11
|
+
export interface UiDiscovery {
|
|
12
|
+
version: 1;
|
|
13
|
+
pid: number;
|
|
14
|
+
port: number;
|
|
15
|
+
url: string;
|
|
16
|
+
started_at: string;
|
|
17
|
+
cli_version: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function discoveryPath(stateDir: string): string;
|
|
20
|
+
export declare function writeDiscovery(stateDir: string, info: Omit<UiDiscovery, "version">): void;
|
|
21
|
+
/** The discovery record, or `undefined` when the file is missing or malformed. */
|
|
22
|
+
export declare function readDiscovery(stateDir: string): UiDiscovery | undefined;
|
|
23
|
+
/** Remove the file when it names `pid`; leave another server's file alone. */
|
|
24
|
+
export declare function removeDiscovery(stateDir: string, pid?: number): void;
|
|
25
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/ui/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,GAAG,IAAI,CAOzF;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAOvE;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAAoB,GAAG,IAAI,CAOjF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery file for `tidegap ui` (native clients KTD4). While the server
|
|
3
|
+
* runs, `<tidegap home>/ui.json` (mode 0600) names its pid and port so the
|
|
4
|
+
* Mac app can find it without the user typing anything. It holds no token.
|
|
5
|
+
* The file is removed on close only when it still names this process, so a
|
|
6
|
+
* second server's file is never deleted by the first one.
|
|
7
|
+
*/
|
|
8
|
+
import { randomBytes } from "node:crypto";
|
|
9
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
export const DISCOVERY_FILE = "ui.json";
|
|
12
|
+
/** Default port of `tidegap ui`; clients look here first. */
|
|
13
|
+
export const DEFAULT_UI_PORT = 47821;
|
|
14
|
+
export function discoveryPath(stateDir) {
|
|
15
|
+
return join(stateDir, DISCOVERY_FILE);
|
|
16
|
+
}
|
|
17
|
+
export function writeDiscovery(stateDir, info) {
|
|
18
|
+
mkdirSync(stateDir, { recursive: true, mode: 0o700 });
|
|
19
|
+
const path = discoveryPath(stateDir);
|
|
20
|
+
const tmp = `${path}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
|
|
21
|
+
writeFileSync(tmp, `${JSON.stringify({ version: 1, ...info }, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
22
|
+
chmodSync(tmp, 0o600);
|
|
23
|
+
renameSync(tmp, path);
|
|
24
|
+
}
|
|
25
|
+
/** The discovery record, or `undefined` when the file is missing or malformed. */
|
|
26
|
+
export function readDiscovery(stateDir) {
|
|
27
|
+
try {
|
|
28
|
+
const d = JSON.parse(readFileSync(discoveryPath(stateDir), "utf8"));
|
|
29
|
+
return d.version === 1 && Number.isInteger(d.pid) && Number.isInteger(d.port) ? d : undefined;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Remove the file when it names `pid`; leave another server's file alone. */
|
|
36
|
+
export function removeDiscovery(stateDir, pid = process.pid) {
|
|
37
|
+
if (readDiscovery(stateDir)?.pid !== pid)
|
|
38
|
+
return;
|
|
39
|
+
try {
|
|
40
|
+
unlinkSync(discoveryPath(stateDir));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// already gone
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/ui/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpG,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC;AAExC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC;AAWrC,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,IAAkC;IACjF,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3E,aAAa,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/G,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACtB,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAyB,CAAC;QAC5F,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,CAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;IACjH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,MAAc,OAAO,CAAC,GAAG;IACzE,IAAI,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,KAAK,GAAG;QAAE,OAAO;IACjD,IAAI,CAAC;QACH,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;AACH,CAAC"}
|
package/dist/ui/server.d.ts
CHANGED
|
@@ -6,19 +6,29 @@
|
|
|
6
6
|
* Security model:
|
|
7
7
|
* - binds 127.0.0.1 only; there is no host option;
|
|
8
8
|
* - every `/api/*` request (JSON and SSE) needs `Authorization: Bearer
|
|
9
|
-
* <token
|
|
9
|
+
* <token>`, compared in constant time. Two scopes (native clients KTD3):
|
|
10
|
+
* `page`, the random per-run token in the page link, and `client`, a
|
|
11
|
+
* long-lived `tgc_` token from the registry (`ui-clients.json`, SHA-256
|
|
12
|
+
* only; `tidegap clients`). A client token never reaches `/api/clients*`;
|
|
10
13
|
* - a `Host` header whose name is not 127.0.0.1, localhost or [::1] (with
|
|
11
14
|
* no port or this server's port) gets 403, which blocks DNS-rebinding
|
|
12
|
-
* pages;
|
|
13
|
-
*
|
|
15
|
+
* pages;
|
|
16
|
+
* - an `Origin` header, when sent, must name the same loopback host, or be
|
|
17
|
+
* a browser extension origin (`chrome-extension://`, `moz-extension://`,
|
|
18
|
+
* `safari-web-extension://` plus an id). Any other origin, `null`
|
|
19
|
+
* included, gets 403. An extension origin reaches `/api/*` only, and only
|
|
20
|
+
* with a client token: the page token from an extension gets 403, and so
|
|
21
|
+
* does a static file. The origin only keeps web pages out; the token is
|
|
22
|
+
* the authentication;
|
|
14
23
|
* - no CORS headers, so other origins cannot read responses;
|
|
15
24
|
* - static files come from a fixed map; no request path touches the disk;
|
|
16
25
|
* - every write is a POST with the bearer header; routes that read a body
|
|
17
|
-
* (cancel, reschedule, reply) need `Content-Type:
|
|
18
|
-
* (415 otherwise: a cross-site form cannot send it
|
|
19
|
-
* which gets no CORS answer) and at most 64 KiB
|
|
20
|
-
* (read, unread, run-now, dispatcher/start
|
|
21
|
-
* (400); bodies are strict JSON objects.
|
|
26
|
+
* (create, cancel, reschedule, reply, clients) need `Content-Type:
|
|
27
|
+
* application/json` (415 otherwise: a cross-site form cannot send it
|
|
28
|
+
* without a preflight, which gets no CORS answer) and at most 64 KiB
|
|
29
|
+
* (413); body-less routes (read, unread, run-now, dispatcher/start,
|
|
30
|
+
* revoke) refuse a non-empty body (400); bodies are strict JSON objects.
|
|
31
|
+
* No cookie is ever set.
|
|
22
32
|
* The static page holds no data; it reads the token from the URL fragment,
|
|
23
33
|
* which browsers never send to the server.
|
|
24
34
|
*/
|
|
@@ -30,7 +40,7 @@ import { TaskEvents } from "./events.js";
|
|
|
30
40
|
export declare const MAX_BODY_BYTES: number;
|
|
31
41
|
export declare const LOOPBACK = "127.0.0.1";
|
|
32
42
|
/** ES modules of the page (ui v2). Each is listed: no path touches the disk. */
|
|
33
|
-
export declare const UI_MODULES: readonly ["app.js", "api.js", "state.js", "toast.js", "dom.js", "views.js", "row.js", "agenda.js", "detail.js", "journal.js", "timeline.js", "dnd.js", "keys.js", "dialog.js"];
|
|
43
|
+
export declare const UI_MODULES: readonly ["app.js", "api.js", "state.js", "toast.js", "dom.js", "views.js", "row.js", "agenda.js", "detail.js", "journal.js", "timeline.js", "dnd.js", "keys.js", "dialog.js", "clients.js"];
|
|
34
44
|
export declare const STATIC_FILES: Readonly<Record<string, {
|
|
35
45
|
file: string;
|
|
36
46
|
type: string;
|
|
@@ -47,6 +57,8 @@ export interface UiServerOptions extends Omit<ApiContext, "store" | "dispatcher"
|
|
|
47
57
|
logDir?: string;
|
|
48
58
|
/** 0 (default) picks a free port. */
|
|
49
59
|
port?: number;
|
|
60
|
+
/** Directory of the client registry (`ui-clients.json`); default: the Tidegap home. */
|
|
61
|
+
stateDir?: string;
|
|
50
62
|
assetDir?: string;
|
|
51
63
|
/** data_version poll interval for the change stream. */
|
|
52
64
|
pollMs?: number;
|
|
@@ -65,5 +77,8 @@ export interface UiServer {
|
|
|
65
77
|
* DNS name that resolves to 127.0.0.1, is refused.
|
|
66
78
|
*/
|
|
67
79
|
export declare function isLoopbackHost(host: string, port: number): boolean;
|
|
80
|
+
export type OriginClass = "none" | "loopback" | "extension" | "foreign";
|
|
81
|
+
/** Classify an `Origin` header for a server on `port`. */
|
|
82
|
+
export declare function classifyOrigin(origin: string | undefined, port: number): OriginClass;
|
|
68
83
|
export declare function startUiServer(opts: UiServerOptions): Promise<UiServer>;
|
|
69
84
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/ui/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/ui/server.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/ui/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AASjG,OAAO,EAA2B,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAEpE,OAAO,EAAqB,KAAK,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,wCAAwC;AACxC,eAAO,MAAM,cAAc,QAAY,CAAC;AAExC,eAAO,MAAM,QAAQ,cAAc,CAAC;AAapC,gFAAgF;AAChF,eAAO,MAAM,UAAU,8LAgBb,CAAC;AAEX,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAKjF,CAAC;AAEF,+EAA+E;AAC/E,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,MAAM,WAAW,eACf,SAAQ,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,YAAY,CAAC;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,yEAAyE;IACzE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAQD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAIlE;AAKD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAExE,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAKpF;AA6DD,wBAAsB,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwI5E"}
|
package/dist/ui/server.js
CHANGED
|
@@ -6,19 +6,29 @@
|
|
|
6
6
|
* Security model:
|
|
7
7
|
* - binds 127.0.0.1 only; there is no host option;
|
|
8
8
|
* - every `/api/*` request (JSON and SSE) needs `Authorization: Bearer
|
|
9
|
-
* <token
|
|
9
|
+
* <token>`, compared in constant time. Two scopes (native clients KTD3):
|
|
10
|
+
* `page`, the random per-run token in the page link, and `client`, a
|
|
11
|
+
* long-lived `tgc_` token from the registry (`ui-clients.json`, SHA-256
|
|
12
|
+
* only; `tidegap clients`). A client token never reaches `/api/clients*`;
|
|
10
13
|
* - a `Host` header whose name is not 127.0.0.1, localhost or [::1] (with
|
|
11
14
|
* no port or this server's port) gets 403, which blocks DNS-rebinding
|
|
12
|
-
* pages;
|
|
13
|
-
*
|
|
15
|
+
* pages;
|
|
16
|
+
* - an `Origin` header, when sent, must name the same loopback host, or be
|
|
17
|
+
* a browser extension origin (`chrome-extension://`, `moz-extension://`,
|
|
18
|
+
* `safari-web-extension://` plus an id). Any other origin, `null`
|
|
19
|
+
* included, gets 403. An extension origin reaches `/api/*` only, and only
|
|
20
|
+
* with a client token: the page token from an extension gets 403, and so
|
|
21
|
+
* does a static file. The origin only keeps web pages out; the token is
|
|
22
|
+
* the authentication;
|
|
14
23
|
* - no CORS headers, so other origins cannot read responses;
|
|
15
24
|
* - static files come from a fixed map; no request path touches the disk;
|
|
16
25
|
* - every write is a POST with the bearer header; routes that read a body
|
|
17
|
-
* (cancel, reschedule, reply) need `Content-Type:
|
|
18
|
-
* (415 otherwise: a cross-site form cannot send it
|
|
19
|
-
* which gets no CORS answer) and at most 64 KiB
|
|
20
|
-
* (read, unread, run-now, dispatcher/start
|
|
21
|
-
* (400); bodies are strict JSON objects.
|
|
26
|
+
* (create, cancel, reschedule, reply, clients) need `Content-Type:
|
|
27
|
+
* application/json` (415 otherwise: a cross-site form cannot send it
|
|
28
|
+
* without a preflight, which gets no CORS answer) and at most 64 KiB
|
|
29
|
+
* (413); body-less routes (read, unread, run-now, dispatcher/start,
|
|
30
|
+
* revoke) refuse a non-empty body (400); bodies are strict JSON objects.
|
|
31
|
+
* No cookie is ever set.
|
|
22
32
|
* The static page holds no data; it reads the token from the URL fragment,
|
|
23
33
|
* which browsers never send to the server.
|
|
24
34
|
*/
|
|
@@ -31,6 +41,7 @@ import { TaskStore } from "@tidegap/core";
|
|
|
31
41
|
import { resolveLauncher } from "../commands/install.js";
|
|
32
42
|
import { actionScheduler } from "../commands/queue.js";
|
|
33
43
|
import { handleApi, postBodyKind } from "./api.js";
|
|
44
|
+
import { ClientRegistry } from "./clients.js";
|
|
34
45
|
import { DispatcherControl } from "./dispatcher.js";
|
|
35
46
|
import { TaskEvents } from "./events.js";
|
|
36
47
|
/** Largest POST body read, in bytes. */
|
|
@@ -60,6 +71,7 @@ export const UI_MODULES = [
|
|
|
60
71
|
"dnd.js",
|
|
61
72
|
"keys.js",
|
|
62
73
|
"dialog.js",
|
|
74
|
+
"clients.js",
|
|
63
75
|
];
|
|
64
76
|
export const STATIC_FILES = {
|
|
65
77
|
"/": { file: "index.html", type: "text/html; charset=utf-8" },
|
|
@@ -86,6 +98,26 @@ export function isLoopbackHost(host, port) {
|
|
|
86
98
|
return false;
|
|
87
99
|
return m[2] === undefined || Number(m[2]) === port;
|
|
88
100
|
}
|
|
101
|
+
/** A browser extension origin: scheme plus an id, nothing else (KTD3). */
|
|
102
|
+
const EXTENSION_ORIGIN = /^(?:chrome-extension|moz-extension|safari-web-extension):\/\/[a-z0-9-]+$/i;
|
|
103
|
+
/** Classify an `Origin` header for a server on `port`. */
|
|
104
|
+
export function classifyOrigin(origin, port) {
|
|
105
|
+
if (origin === undefined)
|
|
106
|
+
return "none";
|
|
107
|
+
const m = /^http:\/\/([^/]+)$/i.exec(origin);
|
|
108
|
+
if (m && isLoopbackHost(m[1], port))
|
|
109
|
+
return "loopback";
|
|
110
|
+
return EXTENSION_ORIGIN.test(origin) ? "extension" : "foreign";
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `/api/clients` and everything under it: page token only. Splits the path
|
|
114
|
+
* the way the router does (empty segments dropped), so `/api//clients`
|
|
115
|
+
* cannot route to the clients handlers past this check.
|
|
116
|
+
*/
|
|
117
|
+
function isClientsRoute(pathname) {
|
|
118
|
+
const segs = pathname.split("/").filter(Boolean);
|
|
119
|
+
return segs[0] === "api" && segs[1] === "clients";
|
|
120
|
+
}
|
|
89
121
|
function send(res, status, body, type = "application/json; charset=utf-8") {
|
|
90
122
|
res.writeHead(status, { ...SECURITY_HEADERS, ...(body === undefined ? {} : { "content-type": type }) });
|
|
91
123
|
if (body === undefined)
|
|
@@ -153,52 +185,73 @@ export async function startUiServer(opts) {
|
|
|
153
185
|
...(opts.logDir ? { logDir: opts.logDir } : {}),
|
|
154
186
|
...(opts.now ? { now: opts.now } : {}),
|
|
155
187
|
});
|
|
156
|
-
const
|
|
188
|
+
const clients = opts.clients ?? new ClientRegistry(opts.stateDir);
|
|
189
|
+
const ctx = { ...opts, store, scheduler, dispatcher, clients };
|
|
157
190
|
const assetDir = opts.assetDir ?? defaultAssetDir();
|
|
158
191
|
const tokenDigest = digest(opts.token);
|
|
159
192
|
let port = 0;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
193
|
+
/** The bearer's scope: the per-run page token, a registry client token, or none. */
|
|
194
|
+
const scope = (bearer) => {
|
|
195
|
+
if (bearer === undefined)
|
|
196
|
+
return undefined;
|
|
197
|
+
if (timingSafeEqual(digest(bearer), tokenDigest))
|
|
198
|
+
return "page";
|
|
199
|
+
return clients.verify(bearer) ? "client" : undefined;
|
|
164
200
|
};
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const m = /^http:\/\/([^/]+)$/i.exec(origin);
|
|
172
|
-
return !!m && isLoopbackHost(m[1], port);
|
|
173
|
-
};
|
|
174
|
-
const openStream = (req, res) => {
|
|
201
|
+
/**
|
|
202
|
+
* The change stream. For a client token (`clientBearer`), the bearer is
|
|
203
|
+
* checked again before each event and on each keep-alive: once it is
|
|
204
|
+
* revoked the stream ends, and a reconnect gets 401.
|
|
205
|
+
*/
|
|
206
|
+
const openStream = (req, res, clientBearer) => {
|
|
175
207
|
res.writeHead(200, {
|
|
176
208
|
...SECURITY_HEADERS,
|
|
177
209
|
"content-type": "text/event-stream; charset=utf-8",
|
|
178
210
|
connection: "keep-alive",
|
|
179
211
|
});
|
|
180
212
|
res.write(`event: hello\ndata: {}\n\n`);
|
|
213
|
+
const valid = () => clientBearer === undefined || clients.verify(clientBearer) !== undefined;
|
|
214
|
+
let open = true;
|
|
215
|
+
const end = () => {
|
|
216
|
+
if (!open)
|
|
217
|
+
return;
|
|
218
|
+
open = false;
|
|
219
|
+
clearInterval(keepAlive);
|
|
220
|
+
unsubscribe();
|
|
221
|
+
res.end();
|
|
222
|
+
};
|
|
181
223
|
const unsubscribe = events.subscribe((ids) => {
|
|
224
|
+
if (!valid())
|
|
225
|
+
return end();
|
|
182
226
|
res.write(`event: tasks\ndata: ${JSON.stringify({ ids })}\n\n`);
|
|
183
227
|
});
|
|
184
|
-
const keepAlive = setInterval(() => res.write(`: keep-alive\n\n`), 15_000);
|
|
228
|
+
const keepAlive = setInterval(() => (valid() ? res.write(`: keep-alive\n\n`) : end()), 15_000);
|
|
185
229
|
keepAlive.unref();
|
|
186
|
-
req.on("close",
|
|
187
|
-
clearInterval(keepAlive);
|
|
188
|
-
unsubscribe();
|
|
189
|
-
});
|
|
230
|
+
req.on("close", end);
|
|
190
231
|
};
|
|
191
232
|
const server = createServer((req, res) => {
|
|
192
233
|
void (async () => {
|
|
193
|
-
if (!
|
|
234
|
+
if (!isLoopbackHost(req.headers.host ?? "", port))
|
|
194
235
|
return send(res, 403, { error: "forbidden host" });
|
|
236
|
+
const origin = classifyOrigin(req.headers.origin, port);
|
|
237
|
+
if (origin === "foreign")
|
|
238
|
+
return send(res, 403, { error: "forbidden origin" });
|
|
195
239
|
const url = new URL(req.url ?? "/", `http://${LOOPBACK}`);
|
|
196
240
|
const method = req.method ?? "GET";
|
|
197
241
|
if (url.pathname === "/api" || url.pathname.startsWith("/api/")) {
|
|
198
|
-
|
|
242
|
+
const bearer = /^Bearer (.+)$/.exec(req.headers.authorization ?? "")?.[1];
|
|
243
|
+
const who = scope(bearer);
|
|
244
|
+
if (!who)
|
|
199
245
|
return send(res, 401, { error: "unauthorized" });
|
|
246
|
+
// An extension uses a client token; the page token stays in the page.
|
|
247
|
+
if (origin === "extension" && who === "page")
|
|
248
|
+
return send(res, 403, { error: "forbidden origin" });
|
|
249
|
+
if (who === "client" && isClientsRoute(url.pathname))
|
|
250
|
+
return send(res, 403, { error: "forbidden" });
|
|
200
251
|
if (url.pathname === "/api/events") {
|
|
201
|
-
return method === "GET"
|
|
252
|
+
return method === "GET"
|
|
253
|
+
? openStream(req, res, who === "client" ? bearer : undefined)
|
|
254
|
+
: send(res, 405, { error: "method not allowed" });
|
|
202
255
|
}
|
|
203
256
|
let body = {};
|
|
204
257
|
const kind = method === "POST" ? postBodyKind(url.pathname) : undefined;
|
|
@@ -213,6 +266,8 @@ export async function startUiServer(opts) {
|
|
|
213
266
|
events.emit(out.changedIds);
|
|
214
267
|
return send(res, out.status, out.body);
|
|
215
268
|
}
|
|
269
|
+
if (origin === "extension")
|
|
270
|
+
return send(res, 403, { error: "forbidden origin" });
|
|
216
271
|
const asset = Object.hasOwn(STATIC_FILES, url.pathname) ? STATIC_FILES[url.pathname] : undefined;
|
|
217
272
|
if (!asset)
|
|
218
273
|
return send(res, 404, "not found", "text/plain; charset=utf-8");
|
|
@@ -232,10 +287,19 @@ export async function startUiServer(opts) {
|
|
|
232
287
|
res.end();
|
|
233
288
|
});
|
|
234
289
|
});
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
290
|
+
try {
|
|
291
|
+
await new Promise((resolve, reject) => {
|
|
292
|
+
server.once("error", reject);
|
|
293
|
+
server.listen(opts.port ?? 0, LOOPBACK, () => resolve());
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
// Port taken (the CLI retries on a free port): release what was opened.
|
|
298
|
+
if (ownScheduler)
|
|
299
|
+
scheduler.shutdown();
|
|
300
|
+
store.close();
|
|
301
|
+
throw err;
|
|
302
|
+
}
|
|
239
303
|
const addr = server.address();
|
|
240
304
|
port = addr.port;
|
|
241
305
|
events.start();
|
package/dist/ui/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ui/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,YAAY,EAA0D,MAAM,WAAW,CAAC;AAEjG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAmB,MAAM,UAAU,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAgB,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,wCAAwC;AACxC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,CAAC;AAExC,MAAM,CAAC,MAAM,QAAQ,GAAG,WAAW,CAAC;AAEpC,MAAM,gBAAgB,GAA2B;IAC/C,yBAAyB,EACvB,2HAA2H;IAC7H,wBAAwB,EAAE,SAAS;IACnC,iBAAiB,EAAE,aAAa;IAChC,eAAe,EAAE,UAAU;CAC5B,CAAC;AAEF,MAAM,EAAE,GAAG,gCAAgC,CAAC;AAC5C,MAAM,GAAG,GAAG,yBAAyB,CAAC;AAEtC,gFAAgF;AAChF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,UAAU;IACV,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,WAAW;IACX,WAAW;IACX,YAAY;IACZ,aAAa;IACb,QAAQ;IACR,SAAS;IACT,WAAW;CACH,CAAC;AAEX,MAAM,CAAC,MAAM,YAAY,GAA6D;IACpF,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,0BAA0B,EAAE;IAC7D,aAAa,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,EAAE;IAChD,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE;IAC1C,GAAG,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;CAC/E,CAAC;AAEF,+EAA+E;AAC/E,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACzE,CAAC;AA2BD,SAAS,MAAM,CAAC,CAAS;IACvB,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;AAEjE;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAY;IACvD,MAAM,CAAC,GAAG,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IACzE,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,SAAS,IAAI,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAc,EAAE,IAAI,GAAG,iCAAiC;IACzG,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,GAAG,gBAAgB,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACxG,IAAI,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,GAAG,EAAE,CAAC;;QAC7B,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChG,CAAC;AAED,oFAAoF;AACpF,SAAS,QAAQ,CAAC,GAAoB,EAAE,GAAW;IACjD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/B,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;YACrB,6DAA6D;YAC7D,IAAI,IAAI,IAAI,GAAG;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAID,0CAA0C;AAC1C,KAAK,UAAU,SAAS,CAAC,GAAoB,EAAE,IAAqB;IAClE,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrF,IAAI,IAAI,KAAK,kBAAkB,EAAE,CAAC;YAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,uCAAuC,EAAE,CAAC;QACpF,CAAC;IACH,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAChD,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC7E,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC;IACnH,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACpD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAiC,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAqB;IACvD,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,sEAAsE;IACtE,kEAAkE;IAClE,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC;IAClD,MAAM,SAAS,GACb,IAAI,CAAC,SAAS;QACd,eAAe,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IACxF,MAAM,UAAU,GAAG,IAAI,iBAAiB,CAAC,KAAK,EAAE;QAC9C,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,eAAe,CAAC,SAAS,EAAE,EAAE,CAAC;QACzD,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvC,CAAC,CAAC;IACH,MAAM,GAAG,GAAe,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;IAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,eAAe,EAAE,CAAC;IACpD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,MAAM,UAAU,GAAG,CAAC,GAAoB,EAAW,EAAE;QACnD,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;QAC1C,MAAM,CAAC,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClC,OAAO,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,EAAE,WAAW,CAAC,CAAC;IAC5D,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,GAAoB,EAAW,EAAE;QACpD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QAChE,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;QAClC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACtC,MAAM,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7C,OAAO,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,GAAoB,EAAE,GAAmB,EAAQ,EAAE;QACrE,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;YACjB,GAAG,gBAAgB;YACnB,cAAc,EAAE,kCAAkC;YAClD,UAAU,EAAE,YAAY;SACzB,CAAC,CAAC;QACH,GAAG,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACxC,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE;YAC3C,GAAG,CAAC,KAAK,CAAC,uBAAuB,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC,CAAC;QAC3E,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,aAAa,CAAC,SAAS,CAAC,CAAC;YACzB,WAAW,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACvC,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;YAC1E,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,QAAQ,EAAE,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC;YACnC,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBAChE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;gBACvE,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;oBACnC,OAAO,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;gBACnG,CAAC;gBACD,IAAI,IAAI,GAA4B,EAAE,CAAC;gBACvC,MAAM,IAAI,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACxE,IAAI,IAAI,EAAE,CAAC;oBACT,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;oBAC3C,IAAI,CAAC,OAAO,CAAC,EAAE;wBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;oBAC5E,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;gBACtB,CAAC;gBACD,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;gBAC/E,IAAI,GAAG,CAAC,UAAU;oBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAChD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YACzC,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACjG,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,2BAA2B,CAAC,CAAC;YAC5E,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,oBAAoB,EAAE,2BAA2B,CAAC,CAAC;YACpH,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9E,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,2BAA2B,CAAC,CAAC;YAClE,CAAC;QACH,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;YACd,wEAAwE;YACxE,IAAI,CAAC,GAAG,CAAC,WAAW;gBAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;;gBAC7D,GAAG,CAAC,GAAG,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAiB,CAAC;IAC7C,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACjB,MAAM,CAAC,KAAK,EAAE,CAAC;IAEf,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,GAAG,EAAE,UAAU,QAAQ,IAAI,IAAI,GAAG;QAClC,MAAM;QACN,MAAM;QACN,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,CAAC,mBAAmB,EAAE,CAAC;YAC7B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACpE,IAAI,YAAY;gBAAE,SAAS,CAAC,QAAQ,EAAE,CAAC;YACvC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ui/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/summary` (native clients KTD5): the one answer a compact client
|
|
3
|
+
* renders. Queue counts, unread count, the next run, the next clean grid
|
|
4
|
+
* window and, with `since`, the tasks finished after that cursor. The clean
|
|
5
|
+
* window rule lives here once, so the extension badge and the Mac menu bar
|
|
6
|
+
* show the same numbers.
|
|
7
|
+
*/
|
|
8
|
+
import type { TaskStore } from "@tidegap/core";
|
|
9
|
+
/** One forecast hour, as `/api/forecast` returns it. */
|
|
10
|
+
export interface ForecastPoint {
|
|
11
|
+
/** ISO start of the hour. */
|
|
12
|
+
t: string;
|
|
13
|
+
g: number;
|
|
14
|
+
band: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CleanWindow {
|
|
17
|
+
start: string;
|
|
18
|
+
end: string;
|
|
19
|
+
/** `very_clean` when every hour is very clean, else `clean` (or the fallback hour's band). */
|
|
20
|
+
band: string;
|
|
21
|
+
/** Lowest intensity in the window, gCO2e/kWh. */
|
|
22
|
+
g: number;
|
|
23
|
+
/** True when the window contains now. */
|
|
24
|
+
now: boolean;
|
|
25
|
+
/** True when no hour is clean: the window is the single cleanest hour. */
|
|
26
|
+
fallback: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Most finished tasks one summary lists. */
|
|
29
|
+
export declare const SUMMARY_FINISHED_MAX = 20;
|
|
30
|
+
export declare const ACTIVE_STATUSES: readonly ["queued", "scheduled", "running", "submitted"];
|
|
31
|
+
/**
|
|
32
|
+
* The first run of consecutive clean or very clean hours that ends after
|
|
33
|
+
* `now`. Without one, the cleanest hour that ends after `now`, flagged
|
|
34
|
+
* `fallback`. `null` when the forecast has no hour after `now`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function nextCleanWindow(points: readonly ForecastPoint[], now: Date): CleanWindow | null;
|
|
37
|
+
export interface SummaryInput {
|
|
38
|
+
store: TaskStore;
|
|
39
|
+
region: string;
|
|
40
|
+
now: Date;
|
|
41
|
+
/** Forecast points, or `null` when the feed failed. */
|
|
42
|
+
points: readonly ForecastPoint[] | null;
|
|
43
|
+
since?: Date;
|
|
44
|
+
}
|
|
45
|
+
/** Assemble the summary body (KTD5). */
|
|
46
|
+
export declare function buildSummary(input: SummaryInput): Record<string, unknown>;
|
|
47
|
+
//# sourceMappingURL=summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../../src/ui/summary.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAA0B,SAAS,EAAE,MAAM,eAAe,CAAC;AAIvE,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,8FAA8F;IAC9F,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,CAAC,EAAE,MAAM,CAAC;IACV,yCAAyC;IACzC,GAAG,EAAE,OAAO,CAAC;IACb,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;CACnB;AAID,6CAA6C;AAC7C,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,eAAe,0DAA2F,CAAC;AAExH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,EAAE,GAAG,EAAE,IAAI,GAAG,WAAW,GAAG,IAAI,CAmC/F;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,IAAI,CAAC;IACV,uDAAuD;IACvD,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAC;IACxC,KAAK,CAAC,EAAE,IAAI,CAAC;CACd;AAuBD,wCAAwC;AACxC,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsCzE"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /api/summary` (native clients KTD5): the one answer a compact client
|
|
3
|
+
* renders. Queue counts, unread count, the next run, the next clean grid
|
|
4
|
+
* window and, with `since`, the tasks finished after that cursor. The clean
|
|
5
|
+
* window rule lives here once, so the extension badge and the Mac menu bar
|
|
6
|
+
* show the same numbers.
|
|
7
|
+
*/
|
|
8
|
+
import { taskSummary } from "../commands/queue.js";
|
|
9
|
+
const HOUR_MS = 3_600_000;
|
|
10
|
+
const CLEAN_BANDS = new Set(["very_clean", "clean"]);
|
|
11
|
+
/** Most finished tasks one summary lists. */
|
|
12
|
+
export const SUMMARY_FINISHED_MAX = 20;
|
|
13
|
+
export const ACTIVE_STATUSES = ["queued", "scheduled", "running", "submitted"];
|
|
14
|
+
/**
|
|
15
|
+
* The first run of consecutive clean or very clean hours that ends after
|
|
16
|
+
* `now`. Without one, the cleanest hour that ends after `now`, flagged
|
|
17
|
+
* `fallback`. `null` when the forecast has no hour after `now`.
|
|
18
|
+
*/
|
|
19
|
+
export function nextCleanWindow(points, now) {
|
|
20
|
+
const t = now.getTime();
|
|
21
|
+
const hours = points
|
|
22
|
+
.map((p) => ({ ...p, start: Date.parse(p.t) }))
|
|
23
|
+
.filter((p) => Number.isFinite(p.start))
|
|
24
|
+
.sort((a, b) => a.start - b.start);
|
|
25
|
+
const window = (run, fallback) => {
|
|
26
|
+
const start = run[0].start;
|
|
27
|
+
const end = run[run.length - 1].start + HOUR_MS;
|
|
28
|
+
return {
|
|
29
|
+
start: new Date(start).toISOString(),
|
|
30
|
+
end: new Date(end).toISOString(),
|
|
31
|
+
band: fallback ? run[0].band : run.every((h) => h.band === "very_clean") ? "very_clean" : "clean",
|
|
32
|
+
g: Math.min(...run.map((h) => h.g)),
|
|
33
|
+
now: start <= t && t < end,
|
|
34
|
+
fallback,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
// Runs of consecutive clean hours; the first one that ends after now wins,
|
|
38
|
+
// with the hours of it that already passed (so "now" shows the whole run).
|
|
39
|
+
let run = [];
|
|
40
|
+
for (const h of hours) {
|
|
41
|
+
const prev = run[run.length - 1];
|
|
42
|
+
if (CLEAN_BANDS.has(h.band) && (!prev || h.start === prev.start + HOUR_MS)) {
|
|
43
|
+
run.push(h);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
if (run.length > 0 && run[run.length - 1].start + HOUR_MS > t)
|
|
47
|
+
return window(run, false);
|
|
48
|
+
run = CLEAN_BANDS.has(h.band) ? [h] : [];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (run.length > 0 && run[run.length - 1].start + HOUR_MS > t)
|
|
52
|
+
return window(run, false);
|
|
53
|
+
const ahead = hours.filter((h) => h.start + HOUR_MS > t);
|
|
54
|
+
if (ahead.length === 0)
|
|
55
|
+
return null;
|
|
56
|
+
const cleanest = ahead.reduce((best, h) => (h.g < best.g ? h : best));
|
|
57
|
+
return window([cleanest], true);
|
|
58
|
+
}
|
|
59
|
+
function brief(r) {
|
|
60
|
+
return { task_id: r.taskId, title: String(taskSummary(r).display_title) };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* How many of `times` (ascending) one summary lists: at most
|
|
64
|
+
* SUMMARY_FINISHED_MAX, cut so that tasks sharing a `completed_at` are never
|
|
65
|
+
* split across two polls (the next `since` is that instant, and `>` would
|
|
66
|
+
* skip the rest). Only when more than the cap share the first instant does
|
|
67
|
+
* the cut grow past the cap to take all of them.
|
|
68
|
+
*/
|
|
69
|
+
function capCut(times) {
|
|
70
|
+
if (times.length <= SUMMARY_FINISHED_MAX)
|
|
71
|
+
return times.length;
|
|
72
|
+
let cut = SUMMARY_FINISHED_MAX;
|
|
73
|
+
while (cut > 0 && times[cut] === times[cut - 1])
|
|
74
|
+
cut--;
|
|
75
|
+
if (cut > 0)
|
|
76
|
+
return cut;
|
|
77
|
+
cut = SUMMARY_FINISHED_MAX;
|
|
78
|
+
while (cut < times.length && times[cut] === times[cut - 1])
|
|
79
|
+
cut++;
|
|
80
|
+
return cut;
|
|
81
|
+
}
|
|
82
|
+
/** Assemble the summary body (KTD5). */
|
|
83
|
+
export function buildSummary(input) {
|
|
84
|
+
const { store, now } = input;
|
|
85
|
+
const generatedAt = now.toISOString();
|
|
86
|
+
const counts = Object.fromEntries(ACTIVE_STATUSES.map((s) => [s, store.list({ status: s }).length]));
|
|
87
|
+
const active = ACTIVE_STATUSES.reduce((n, s) => n + counts[s], 0);
|
|
88
|
+
const upcoming = [...store.list({ status: "scheduled" }), ...store.list({ status: "queued" })]
|
|
89
|
+
.filter((r) => r.scheduledFor && Number.isFinite(Date.parse(r.scheduledFor)))
|
|
90
|
+
.sort((a, b) => Date.parse(a.scheduledFor) - Date.parse(b.scheduledFor));
|
|
91
|
+
const next = upcoming[0];
|
|
92
|
+
const body = {
|
|
93
|
+
generated_at: generatedAt,
|
|
94
|
+
region: input.region,
|
|
95
|
+
counts,
|
|
96
|
+
active,
|
|
97
|
+
unread: store.list({ unread: true }).length,
|
|
98
|
+
next_run: next ? { ...brief(next), scheduled_for: next.scheduledFor } : null,
|
|
99
|
+
clean_window: input.points ? nextCleanWindow(input.points, now) : null,
|
|
100
|
+
forecast_ok: input.points !== null,
|
|
101
|
+
};
|
|
102
|
+
if (input.since === undefined)
|
|
103
|
+
return { ...body, cursor: generatedAt };
|
|
104
|
+
const since = input.since.getTime();
|
|
105
|
+
// Over the cap, take the oldest after `since`: the cursor stops at the
|
|
106
|
+
// newest of those, so the next poll lists the rest and none is skipped.
|
|
107
|
+
const after = [...store.list({ status: "completed" }), ...store.list({ status: "failed" })]
|
|
108
|
+
.filter((r) => r.completedAt && Date.parse(r.completedAt) > since)
|
|
109
|
+
.sort((a, b) => Date.parse(a.completedAt) - Date.parse(b.completedAt));
|
|
110
|
+
const finished = after.slice(0, capCut(after.map((r) => Date.parse(r.completedAt)))).reverse();
|
|
111
|
+
return {
|
|
112
|
+
...body,
|
|
113
|
+
finished: finished.map((r) => ({ ...brief(r), status: r.status, completed_at: r.completedAt })),
|
|
114
|
+
// The server's own clock: clients never compare their clock with ours.
|
|
115
|
+
// With nothing new, keep the caller's cursor so a task stamped just
|
|
116
|
+
// before `generated_at` but written after it is still reported.
|
|
117
|
+
cursor: finished[0]?.completedAt ?? input.since.toISOString(),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/ui/summary.ts"],"names":[],"mappings":"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"}
|