jeo-code 0.7.23 → 0.7.24
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.md +14 -0
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/package.json +1 -1
- package/src/agent/memory.ts +47 -10
- package/src/auth/callback-server.ts +43 -17
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
The README mirrors the latest 5 entries — regenerate with `bun run changelog:sync`.
|
|
8
8
|
|
|
9
|
+
## [0.7.24] - 2026-07-02
|
|
10
|
+
_OAuth callback page redesign with embedded jeo wordmark + Close button; memory budget O(n²)→O(n) incremental tracking fix._
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **OAuth callback page: jeo wordmark + Close button.** The post-login browser page now embeds the jeo synthwave wordmark as an inline WebP data URI (no static-asset pipeline required in a compiled binary), and gains an explicit `id="jeo-close"` button that calls `window.close()` immediately — the countdown timer remains as a fallback (bumped 3→5 s). Both success and failure pages now auto-close so OAuth tabs never linger.
|
|
14
|
+
- **`HEADER_BY_TYPE` lookup for memory budget tracking.** Extracted from `TYPE_LAYOUT` alongside the existing `DIR_BY_TYPE`, enabling incremental section-header length computation in `selectWithinBudget` without a full re-render.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **`selectWithinBudget` O(n²)→O(n) budget tracking.** Extracted `renderConceptItem` and introduced per-type accumulated section-length tracking so each candidate's budget contribution is computed incrementally (header + item delta) rather than by re-rendering the entire accumulated selection. Produces identical truncation boundaries with O(n) work instead of O(n²); locked by a new regression test (`memory-search-okf.test.ts`) that verifies the exact 50-of-60 cutoff against a hand-computed derivation.
|
|
18
|
+
- **OAuth failure page now shows auto-close / Close button.** Previously the failure page intentionally suppressed `window.close()`; it now uses the same countdown + Close button as the success page so users are not stranded on an error screen.
|
|
19
|
+
|
|
20
|
+
### Verified
|
|
21
|
+
- `bun run typecheck` clean; full suite green — 2064 pass / 0 fail across 237 files — including the new `selectWithinBudget` incremental-budget regression test and updated OAuth callback page assertions (`id="jeo-countdown"`, `id="jeo-close"`, `window.close()` present on both success and failure pages).
|
|
22
|
+
|
|
9
23
|
## [0.7.23] - 2026-07-01
|
|
10
24
|
_Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model._
|
|
11
25
|
|
package/README.ja.md
CHANGED
|
@@ -205,11 +205,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
|
|
|
205
205
|
## 変更履歴 (Changelog)
|
|
206
206
|
|
|
207
207
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
208
|
+
- **[0.7.24]** (2026-07-02) — OAuth callback page redesign with embedded jeo wordmark + Close button; memory budget O(n²)→O(n) incremental tracking fix.
|
|
208
209
|
- **[0.7.23]** (2026-07-01) — Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model.
|
|
209
210
|
- **[0.7.22]** (2026-06-30) — gajae-code 0.7.3–0.7.8 parity sweep: terminal-bell notifications, a gruvbox-dark TUI theme, a resilient `jeo update` runtime check, and an ultragoal artifact gate. Ports the user-facing improvements from upstream gajae-code that jeo did not yet have, re-implemented to fit jeo's config, command, theming, and skill surfaces.
|
|
210
211
|
- **[0.7.21]** (2026-06-26) — Global llm-wiki vault integration, Gemini/Antigravity thinking indicators, generous file-reading windows, and autopilot flag validation. Adds a shared global wiki root configuration with a /wiki slash command, fires reasoning start signals up front for Gemini/Antigravity models, adjusts the large-file reading discipline to use generous windows, and validates autopilot goal and integer flags.
|
|
211
212
|
- **[0.7.20]** (2026-06-26) — OKF concept-memory retrieval gains a hybrid reranker ported from memsearch. Injection priority no longer rides one raw keyword score — it fuses two complementary ranked channels by Reciprocal Rank Fusion (RRF): IDF-weighted lexical relevance (the sparse/BM25 channel, so rare discriminating terms steer recall) and concept-graph proximity (the local dense/semantic-neighbour channel, so a hub linked from multiple query hits surfaces even with no keyword of its own). All embedding-free and deterministic, layered atop the existing failure-first tier and pinned-invariant reserved budget.
|
|
212
|
-
- **[0.7.19]** (2026-06-26) — The live model picker gains gajae-code's `/model` provider tabs, and skill invocation is consolidated onto a single `$` entrypoint. The picker now shows an `ALL` tab plus one tab per provider that `tab`/`shift+tab` cycles, and skills (including their declared aliases) are invoked only via `$` — the slash palette stays builtins-only.
|
|
213
213
|
|
|
214
214
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
215
215
|
<!-- CHANGELOG:END -->
|
package/README.ko.md
CHANGED
|
@@ -205,11 +205,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
|
|
|
205
205
|
## 변경 이력 (Changelog)
|
|
206
206
|
|
|
207
207
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
208
|
+
- **[0.7.24]** (2026-07-02) — OAuth callback page redesign with embedded jeo wordmark + Close button; memory budget O(n²)→O(n) incremental tracking fix.
|
|
208
209
|
- **[0.7.23]** (2026-07-01) — Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model.
|
|
209
210
|
- **[0.7.22]** (2026-06-30) — gajae-code 0.7.3–0.7.8 parity sweep: terminal-bell notifications, a gruvbox-dark TUI theme, a resilient `jeo update` runtime check, and an ultragoal artifact gate. Ports the user-facing improvements from upstream gajae-code that jeo did not yet have, re-implemented to fit jeo's config, command, theming, and skill surfaces.
|
|
210
211
|
- **[0.7.21]** (2026-06-26) — Global llm-wiki vault integration, Gemini/Antigravity thinking indicators, generous file-reading windows, and autopilot flag validation. Adds a shared global wiki root configuration with a /wiki slash command, fires reasoning start signals up front for Gemini/Antigravity models, adjusts the large-file reading discipline to use generous windows, and validates autopilot goal and integer flags.
|
|
211
212
|
- **[0.7.20]** (2026-06-26) — OKF concept-memory retrieval gains a hybrid reranker ported from memsearch. Injection priority no longer rides one raw keyword score — it fuses two complementary ranked channels by Reciprocal Rank Fusion (RRF): IDF-weighted lexical relevance (the sparse/BM25 channel, so rare discriminating terms steer recall) and concept-graph proximity (the local dense/semantic-neighbour channel, so a hub linked from multiple query hits surfaces even with no keyword of its own). All embedding-free and deterministic, layered atop the existing failure-first tier and pinned-invariant reserved budget.
|
|
212
|
-
- **[0.7.19]** (2026-06-26) — The live model picker gains gajae-code's `/model` provider tabs, and skill invocation is consolidated onto a single `$` entrypoint. The picker now shows an `ALL` tab plus one tab per provider that `tab`/`shift+tab` cycles, and skills (including their declared aliases) are invoked only via `$` — the slash palette stays builtins-only.
|
|
213
213
|
|
|
214
214
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
215
215
|
<!-- CHANGELOG:END -->
|
package/README.md
CHANGED
|
@@ -232,11 +232,11 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
|
|
|
232
232
|
## Changelog
|
|
233
233
|
|
|
234
234
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
235
|
+
- **[0.7.24]** (2026-07-02) — OAuth callback page redesign with embedded jeo wordmark + Close button; memory budget O(n²)→O(n) incremental tracking fix.
|
|
235
236
|
- **[0.7.23]** (2026-07-01) — Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model.
|
|
236
237
|
- **[0.7.22]** (2026-06-30) — gajae-code 0.7.3–0.7.8 parity sweep: terminal-bell notifications, a gruvbox-dark TUI theme, a resilient `jeo update` runtime check, and an ultragoal artifact gate. Ports the user-facing improvements from upstream gajae-code that jeo did not yet have, re-implemented to fit jeo's config, command, theming, and skill surfaces.
|
|
237
238
|
- **[0.7.21]** (2026-06-26) — Global llm-wiki vault integration, Gemini/Antigravity thinking indicators, generous file-reading windows, and autopilot flag validation. Adds a shared global wiki root configuration with a /wiki slash command, fires reasoning start signals up front for Gemini/Antigravity models, adjusts the large-file reading discipline to use generous windows, and validates autopilot goal and integer flags.
|
|
238
239
|
- **[0.7.20]** (2026-06-26) — OKF concept-memory retrieval gains a hybrid reranker ported from memsearch. Injection priority no longer rides one raw keyword score — it fuses two complementary ranked channels by Reciprocal Rank Fusion (RRF): IDF-weighted lexical relevance (the sparse/BM25 channel, so rare discriminating terms steer recall) and concept-graph proximity (the local dense/semantic-neighbour channel, so a hub linked from multiple query hits surfaces even with no keyword of its own). All embedding-free and deterministic, layered atop the existing failure-first tier and pinned-invariant reserved budget.
|
|
239
|
-
- **[0.7.19]** (2026-06-26) — The live model picker gains gajae-code's `/model` provider tabs, and skill invocation is consolidated onto a single `$` entrypoint. The picker now shows an `ALL` tab plus one tab per provider that `tab`/`shift+tab` cycles, and skills (including their declared aliases) are invoked only via `$` — the slash palette stays builtins-only.
|
|
240
240
|
|
|
241
241
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
242
242
|
<!-- CHANGELOG:END -->
|
package/README.zh.md
CHANGED
|
@@ -205,11 +205,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
|
|
|
205
205
|
## 更新日志 (Changelog)
|
|
206
206
|
|
|
207
207
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
208
|
+
- **[0.7.24]** (2026-07-02) — OAuth callback page redesign with embedded jeo wordmark + Close button; memory budget O(n²)→O(n) incremental tracking fix.
|
|
208
209
|
- **[0.7.23]** (2026-07-01) — Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model.
|
|
209
210
|
- **[0.7.22]** (2026-06-30) — gajae-code 0.7.3–0.7.8 parity sweep: terminal-bell notifications, a gruvbox-dark TUI theme, a resilient `jeo update` runtime check, and an ultragoal artifact gate. Ports the user-facing improvements from upstream gajae-code that jeo did not yet have, re-implemented to fit jeo's config, command, theming, and skill surfaces.
|
|
210
211
|
- **[0.7.21]** (2026-06-26) — Global llm-wiki vault integration, Gemini/Antigravity thinking indicators, generous file-reading windows, and autopilot flag validation. Adds a shared global wiki root configuration with a /wiki slash command, fires reasoning start signals up front for Gemini/Antigravity models, adjusts the large-file reading discipline to use generous windows, and validates autopilot goal and integer flags.
|
|
211
212
|
- **[0.7.20]** (2026-06-26) — OKF concept-memory retrieval gains a hybrid reranker ported from memsearch. Injection priority no longer rides one raw keyword score — it fuses two complementary ranked channels by Reciprocal Rank Fusion (RRF): IDF-weighted lexical relevance (the sparse/BM25 channel, so rare discriminating terms steer recall) and concept-graph proximity (the local dense/semantic-neighbour channel, so a hub linked from multiple query hits surfaces even with no keyword of its own). All embedding-free and deterministic, layered atop the existing failure-first tier and pinned-invariant reserved budget.
|
|
212
|
-
- **[0.7.19]** (2026-06-26) — The live model picker gains gajae-code's `/model` provider tabs, and skill invocation is consolidated onto a single `$` entrypoint. The picker now shows an `ALL` tab plus one tab per provider that `tab`/`shift+tab` cycles, and skills (including their declared aliases) are invoked only via `$` — the slash palette stays builtins-only.
|
|
213
213
|
|
|
214
214
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
215
215
|
<!-- CHANGELOG:END -->
|
package/package.json
CHANGED
package/src/agent/memory.ts
CHANGED
|
@@ -39,6 +39,7 @@ const TYPE_LAYOUT = [
|
|
|
39
39
|
] as const;
|
|
40
40
|
|
|
41
41
|
const DIR_BY_TYPE: Record<string, string> = Object.fromEntries(TYPE_LAYOUT.map(t => [t.type, t.dir]));
|
|
42
|
+
const HEADER_BY_TYPE: Record<string, string> = Object.fromEntries(TYPE_LAYOUT.map(t => [t.type, t.header]));
|
|
42
43
|
|
|
43
44
|
export function memoryFilePath(cwd: string): string {
|
|
44
45
|
return path.join(cwd, ".jeo", "memory", "MEMORY.md");
|
|
@@ -180,17 +181,23 @@ export async function migrateLegacyMemory(cwd: string): Promise<MigrationResult>
|
|
|
180
181
|
return { migrated: true, conceptCount: written.length, backupPath };
|
|
181
182
|
}
|
|
182
183
|
|
|
184
|
+
/** Render one concept as its bullet + indented body lines (no header) — the atomic
|
|
185
|
+
* unit `renderConceptSection` stacks under a `## header`. Extracted so budget
|
|
186
|
+
* selection (`selectWithinBudget`) can compute a candidate's exact contribution
|
|
187
|
+
* without re-rendering the whole accumulated section (see there for why that
|
|
188
|
+
* matters). */
|
|
189
|
+
function renderConceptItem(c: { title: string; description: string; body: string }): string {
|
|
190
|
+
const lines = [`- **${c.title}**${c.description ? `: ${c.description}` : ""}`];
|
|
191
|
+
if (c.body) {
|
|
192
|
+
for (const bodyLine of c.body.split("\n")) lines.push(` ${bodyLine}`);
|
|
193
|
+
}
|
|
194
|
+
return lines.join("\n");
|
|
195
|
+
}
|
|
196
|
+
|
|
183
197
|
/** Render a single index.md-style section: a `## header` followed by one bullet
|
|
184
198
|
* per concept (`**title**: description`), with the concept body indented beneath. */
|
|
185
199
|
function renderConceptSection(header: string, list: { title: string; description: string; body: string }[]): string {
|
|
186
|
-
|
|
187
|
-
for (const c of list) {
|
|
188
|
-
lines.push(`- **${c.title}**${c.description ? `: ${c.description}` : ""}`);
|
|
189
|
-
if (c.body) {
|
|
190
|
-
for (const bodyLine of c.body.split("\n")) lines.push(` ${bodyLine}`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return lines.join("\n");
|
|
200
|
+
return [`## ${header}`, ...list.map(renderConceptItem)].join("\n");
|
|
194
201
|
}
|
|
195
202
|
|
|
196
203
|
/** A loaded OKF concept: frontmatter fields + body + bundle-relative path. */
|
|
@@ -532,12 +539,42 @@ function renderConcepts(concepts: Concept[]): string {
|
|
|
532
539
|
* Pinned (load-bearing) invariants get a RESERVED budget: they are selected FIRST,
|
|
533
540
|
* before any query/recency fill, so a tight cap can never evict an invariant the
|
|
534
541
|
* gates depend on (philosophy-synthesis consensus #3). The remaining budget then
|
|
535
|
-
* holds the failure-first / core / query-relevant fill.
|
|
542
|
+
* holds the failure-first / core / query-relevant fill.
|
|
543
|
+
*
|
|
544
|
+
* Perf: computes each candidate's exact incremental contribution to the final
|
|
545
|
+
* `renderConcepts` output (header + item lines, "\n" within a section, "\n\n"
|
|
546
|
+
* between sections) instead of re-rendering the whole accumulated set per
|
|
547
|
+
* candidate — O(n) instead of O(n²) for a bundle of n concepts. This mirrors
|
|
548
|
+
* `renderConceptSection`/`renderConcepts`'s exact join rules; a mismatch here
|
|
549
|
+
* would silently mis-size the budget, so `memory-search-okf.test.ts` locks an
|
|
550
|
+
* exact hand-computed truncation boundary as a regression check. */
|
|
551
|
+
|
|
536
552
|
function selectWithinBudget(concepts: Concept[], query: string | undefined, budget: number): Concept[] {
|
|
537
553
|
const selected: Concept[] = [];
|
|
554
|
+
const sectionLenByType = new Map<string, number>(); // type -> rendered length of that type's section so far
|
|
555
|
+
let total = 0; // rendered length of `selected` under renderConcepts
|
|
556
|
+
|
|
538
557
|
const take = (pool: Concept[]) => {
|
|
539
558
|
for (const c of priorityOrder(pool, query)) {
|
|
540
|
-
|
|
559
|
+
const itemLen = renderConceptItem(c).length;
|
|
560
|
+
const priorSectionLen = sectionLenByType.get(c.type);
|
|
561
|
+
let newSectionLen: number;
|
|
562
|
+
let newTotal: number;
|
|
563
|
+
if (priorSectionLen === undefined) {
|
|
564
|
+
// New section: "## header" + "\n" + item — plus a "\n\n" join if a section already exists.
|
|
565
|
+
const header = HEADER_BY_TYPE[c.type] ?? c.type;
|
|
566
|
+
newSectionLen = `## ${header}`.length + 1 + itemLen;
|
|
567
|
+
newTotal = total === 0 ? newSectionLen : total + 2 + newSectionLen;
|
|
568
|
+
} else {
|
|
569
|
+
// Existing section: one more "\n" + item line.
|
|
570
|
+
newSectionLen = priorSectionLen + 1 + itemLen;
|
|
571
|
+
newTotal = total - priorSectionLen + newSectionLen;
|
|
572
|
+
}
|
|
573
|
+
if (newTotal <= budget) {
|
|
574
|
+
selected.push(c);
|
|
575
|
+
sectionLenByType.set(c.type, newSectionLen);
|
|
576
|
+
total = newTotal;
|
|
577
|
+
}
|
|
541
578
|
}
|
|
542
579
|
};
|
|
543
580
|
take(concepts.filter(c => c.pinned)); // reserved budget: invariants survive the cap
|
|
@@ -27,32 +27,58 @@ export interface OAuthCallbackFlowOptions {
|
|
|
27
27
|
|
|
28
28
|
export type CallbackResult = { code: string; state: string };
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
30
|
+
// "jeo" wordmark — bold forged monospace lettering on a neon blue→violet→pink
|
|
31
|
+
// gradient (the mascot's synthwave palette), generated via god-tibo-imagen and
|
|
32
|
+
// matching jeo-pi's bold-wordmark typographic treatment. Embedded as a data URI
|
|
33
|
+
// (not a served static file) so the callback page renders standalone even from
|
|
34
|
+
// a bundled/compiled `jeo` binary with no static-asset pipeline.
|
|
35
|
+
const JEO_WORDMARK_DATA_URI =
|
|
36
|
+
"data:image/webp;base64,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";
|
|
37
|
+
|
|
38
|
+
const PAGE_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>jeo — status</title>
|
|
39
|
+
<style>:root{color-scheme:dark}*{box-sizing:border-box}
|
|
40
|
+
body{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;background:#0b0a10;color:#e7e7ec;display:flex;align-items:center;justify-content:center;height:100vh;margin:0}
|
|
41
|
+
.card{text-align:center;padding:2.5rem 3rem;border:1px solid #2a2a35;border-radius:20px;background:#12131c;max-width:26rem}
|
|
42
|
+
.wordmark{width:88px;height:88px;display:block;margin:0 auto 1.25rem;border-radius:14px}
|
|
43
|
+
h1{margin:0 0 .5rem;font-size:1.1rem;font-weight:600}
|
|
44
|
+
h1.ok{color:#22d3ee}h1.fail{color:#f472b6}
|
|
45
|
+
p{margin:.25rem 0;color:#8b8b96;font-size:.92rem}
|
|
46
|
+
button{margin-top:1.5rem;padding:.55rem 1.6rem;border-radius:999px;border:1px solid #2a2a35;background:transparent;color:#e7e7ec;font:inherit;font-size:.88rem;cursor:pointer}
|
|
47
|
+
button:hover{background:#1b1c27}
|
|
48
|
+
.hint{margin-top:.9rem;font-size:.78rem;color:#5f5f6b}</style></head>
|
|
49
|
+
<body><div class="card">
|
|
50
|
+
<img class="wordmark" alt="jeo" src="${JEO_WORDMARK_DATA_URI}">
|
|
51
|
+
<h1 class="__STATUS__">__TITLE__</h1>
|
|
52
|
+
<p>__MSG__</p>
|
|
53
|
+
<button id="jeo-close" type="button">Close</button>
|
|
54
|
+
<p class="hint">Closing automatically in <span id="jeo-countdown">__SECONDS__</span>s…</p>
|
|
55
|
+
</div>
|
|
56
|
+
<script>__SCRIPT__</script>
|
|
57
|
+
</body></html>`;
|
|
58
|
+
|
|
59
|
+
// Seconds before the tab auto-closes — applies to BOTH the success and the
|
|
60
|
+
// failure page now (previously only success auto-closed). Pressing "Close"
|
|
61
|
+
// closes immediately without waiting out the countdown. `window.close()` only
|
|
37
62
|
// succeeds on tabs the script itself opened; browsers may ignore it for
|
|
38
|
-
// OS-launched OAuth tabs, so the
|
|
39
|
-
|
|
40
|
-
const AUTO_CLOSE_SECONDS = 3;
|
|
63
|
+
// OS-launched OAuth tabs, so the button remains as the reliable fallback.
|
|
64
|
+
const AUTO_CLOSE_SECONDS = 5;
|
|
41
65
|
|
|
42
66
|
function renderHtml(ok: boolean, msg: string): string {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
const script = `(function(){
|
|
68
|
+
var n=${AUTO_CLOSE_SECONDS},el=document.getElementById("jeo-countdown"),btn=document.getElementById("jeo-close"),t;
|
|
69
|
+
function closeNow(){clearInterval(t);window.close();}
|
|
70
|
+
t=setInterval(function(){n-=1;if(el)el.textContent=String(Math.max(n,0));if(n<=0)closeNow();},1000);
|
|
71
|
+
if(btn)btn.addEventListener("click",closeNow);
|
|
72
|
+
})();`;
|
|
49
73
|
return PAGE_HTML
|
|
74
|
+
.replace("__STATUS__", ok ? "ok" : "fail")
|
|
50
75
|
.replace("__TITLE__", ok ? "Login complete \u2713" : "Login failed")
|
|
51
76
|
.replace("__MSG__", msg)
|
|
52
|
-
.replace("
|
|
77
|
+
.replace("__SECONDS__", String(AUTO_CLOSE_SECONDS))
|
|
53
78
|
.replace("__SCRIPT__", script);
|
|
54
79
|
}
|
|
55
80
|
|
|
81
|
+
|
|
56
82
|
export abstract class OAuthCallbackFlow {
|
|
57
83
|
protected ctrl: OAuthController;
|
|
58
84
|
protected preferredPort: number;
|