@groeponline/pi-wishcraft 0.19.2 → 0.20.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/CHANGELOG.md +11 -0
- package/README.md +1 -1
- package/ROADMAP.md +34 -31
- package/banner.png +0 -0
- package/package.json +2 -1
- package/src/extension/commands/commands.ts +1 -1
- package/src/extension/ui/menu-views.ts +0 -40
- package/src/extension/ui/powerline-menu-view.ts +83 -0
- package/src/extension/ui/powerline-menu.ts +54 -0
- package/src/theme/theme.ts +11 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.20.0] - 2026-08-20
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `alt+p` menu is three overlay entries (Navigate, Configure, Status) with a Status drill-down for ports, TPS, and toggle.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- `theme.json` in the working directory overrides icons, and the theme cache busts when that file changes.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- npm pack includes `banner.png` so the published tarball matches `pi.image`.
|
|
15
|
+
|
|
5
16
|
## [0.19.2] - 2026-08-20
|
|
6
17
|
|
|
7
18
|
### Added
|
package/README.md
CHANGED
|
@@ -247,7 +247,7 @@ Interactivity (Pi core renders the footer as static text, so live click is not p
|
|
|
247
247
|
|
|
248
248
|
- `/tps [value]`: show or set `POWERLINE_TPS`
|
|
249
249
|
- `/open-ports`: list listening ports and pick one
|
|
250
|
-
- `alt+p`: **powerline menu**:
|
|
250
|
+
- `alt+p`: **powerline menu**: three overlays (Navigate, Configure, Status). Status drills down to ports, TPS, and toggle.
|
|
251
251
|
- `alt+i`: **powerline info**: full open-ports list
|
|
252
252
|
|
|
253
253
|
Both `alt+p` and `alt+i` are rebindable (see Keybinds below); changes apply after `/reload`.
|
package/ROADMAP.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Roadmap — pi-wishcraft
|
|
2
2
|
|
|
3
|
-
Herschreven 2026-08-18.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Herschreven 2026-08-18. Vijfde pass 2026-08-20: 0.19.0–0.19.2 staan
|
|
4
|
+
op npm. CHE-40 (`/powerline` tab) is Done via #18. Overlay-submenus
|
|
5
|
+
(CHE-42 / #19) starten 0.20. ROADMAP was achter op de code: hooks,
|
|
6
|
+
repairs-subset en skills-manager v2 UI zitten al op `main`.
|
|
7
7
|
|
|
8
8
|
Pi core is de engine. Wishcraft is de cockpit. Elke feature dient één van
|
|
9
9
|
drie doelen: **grip** (skills, tokens, config), **prestatie** (repairs,
|
|
@@ -84,16 +84,15 @@ Niet de fork. Niet de SaaS-agent.
|
|
|
84
84
|
|
|
85
85
|
## Mijlpalen
|
|
86
86
|
|
|
87
|
-
- **0.19.0 — "Correctheid"** (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
repair-teller zichtbaar, `/skills` zoekt op naam+beschrijving+pad.
|
|
87
|
+
- **0.19.0–0.19.2 — "Correctheid"** (geland). Bugs, hygiëne, catalogus,
|
|
88
|
+
auto-release, `/powerline` tab. Hooks, repairs-subset en skills
|
|
89
|
+
manager v2 UI gingen mee in #12, eerder dan deze sectie beloofde.
|
|
90
|
+
Done = npm 0.19.2 live, `/skills` filtert, `$test` expandeert geen
|
|
91
|
+
debris, verify-trio groen op de tag.
|
|
92
|
+
- **0.20.0 — "Harness"**. Overlay-chrome + CHE-42 drill-down, Configure
|
|
93
|
+
uit `ctx.ui.select`, token-overlays, rest-repairs, README-hooks die
|
|
94
|
+
waar zijn. Done = drie README-hookvoorbeelden werken, repair-teller
|
|
95
|
+
zichtbaar, `alt+p` overlay-boom, `/tps` deelt de ring met het segment.
|
|
97
96
|
- **1.0 — "Cockpit"**. Skills-doctor/install, declaratieve policy,
|
|
98
97
|
preset-editor, idee-review, stabiele ChefGroep-statuskeys,
|
|
99
98
|
documentatie die waar is. Done = README dekt alles wat we shipten,
|
|
@@ -173,7 +172,10 @@ GRO-1061 (runner-queue) is ops, geen product-slice.
|
|
|
173
172
|
|
|
174
173
|
## 0.20.0 — Harness
|
|
175
174
|
|
|
176
|
-
Vier stacked PRs.
|
|
175
|
+
Vier stacked PRs. 0.19.2 staat op npm. Hooks, repairs-subset en
|
|
176
|
+
skills-manager v2 UI zijn al op `main` (#12). Wat hier nog openstaat:
|
|
177
|
+
overlay-chrome, CHE-42, Configure-submenu, token-overlays, rest-repairs,
|
|
178
|
+
README-voorbeelden.
|
|
177
179
|
|
|
178
180
|
Overlay-chrome kit, één keer, daarna hergebruiken: box + ronde hoeken,
|
|
179
181
|
accent-kop, dim metadata, rechts uitgelijnde counts, `→` detail /
|
|
@@ -216,15 +218,15 @@ zijn eigen schema's.
|
|
|
216
218
|
Done: pure `repairToolInput(tool, input)` + table-driven tests voor
|
|
217
219
|
de zes gevallen + de parse-vóór-wrap invariant.
|
|
218
220
|
|
|
219
|
-
### PR G — overlay-submenus (CHE-42)
|
|
221
|
+
### PR G — overlay-submenus (CHE-42) — in #19
|
|
220
222
|
|
|
221
223
|
Het `alt+p`-menu krijgt gestapelde `SelectList`-overlays (pijltjes +
|
|
222
224
|
descriptions) in plaats van platte `ctx.ui.select`. Max drie
|
|
223
225
|
top-level ingangen. Pure functie `buildPowerlineMenuItems`
|
|
224
|
-
unit-testen.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
unit-testen. #19 landt de top-level boom (Navigate / Configure /
|
|
227
|
+
Status). Configure zelf blijft `ctx.ui.select` tot de volgende
|
|
228
|
+
0.20-snede. CHE-40 is Done (#18). CHE-41 is 1.0 per-segment detail,
|
|
229
|
+
geen tweede `alt+i`-pad.
|
|
228
230
|
|
|
229
231
|
### PR H — skills manager v2 UI + token-overlays
|
|
230
232
|
|
|
@@ -301,11 +303,11 @@ Pas na 0.20. Geen parallelle 1.0-tak.
|
|
|
301
303
|
|
|
302
304
|
| Ticket | Actie |
|
|
303
305
|
|---|---|
|
|
304
|
-
| GRO-1060 fork cleanup |
|
|
306
|
+
| GRO-1060 fork cleanup | Done. Release-pad bewezen. CHANGELOG niet inkorten. Versie niet resetten. `banner.png` blijft. |
|
|
305
307
|
| GRO-1061 CI queue | Ops, niet deze roadmap. |
|
|
306
|
-
| CHE-40 `/powerline` tab |
|
|
307
|
-
| CHE-41
|
|
308
|
-
| CHE-42 drill-down |
|
|
308
|
+
| CHE-40 `/powerline` tab | Done (#18 / 0.19.2). |
|
|
309
|
+
| CHE-41 per-segment detail | 1.0. Ticket hernoemd; geen tweede `alt+i`-pad. |
|
|
310
|
+
| CHE-42 drill-down | In Progress. #19 = drie top-level overlays + Status. Configure-overlay volgt. |
|
|
309
311
|
|
|
310
312
|
Oude `pi-powerline-footer`-projecttickets niet laten staan alsof
|
|
311
313
|
die package nog leeft.
|
|
@@ -333,8 +335,8 @@ die package nog leeft.
|
|
|
333
335
|
- `npm deprecate` van de oude naam faalt tot de scope-owner het
|
|
334
336
|
token verruimt. Gebruikers die `pi-powerline-footer` installeren
|
|
335
337
|
blijven op 0.17.2.
|
|
336
|
-
- Hooks spawnen processen. Default timeout 30s; kill-switch
|
|
337
|
-
|
|
338
|
+
- Hooks spawnen processen. Default timeout 30s; kill-switch
|
|
339
|
+
`wishcraft.hooksEnabled` bestaat al op `main`.
|
|
338
340
|
- Repairs op core-tools raken we niet aan. Als DeepSeek-achtige
|
|
339
341
|
modellen daar alsnog op stuklopen, is dat een gesprek met pi
|
|
340
342
|
core, geen stille override.
|
|
@@ -369,8 +371,9 @@ die package nog leeft.
|
|
|
369
371
|
Verify-trio groen. (#12)
|
|
370
372
|
- [x] Gallery-contract gemerged (`chore/pi-dev-gallery`): keywords,
|
|
371
373
|
`publishConfig.access`, `pi.image`, `npm run verify:package`. (#11)
|
|
372
|
-
- [x] PR C: merge to `main` tagged `v0.19.0` then `v0.19.1`
|
|
373
|
-
(`npm view` = `0.19.
|
|
374
|
-
|
|
375
|
-
- [
|
|
376
|
-
|
|
374
|
+
- [x] PR C: merge to `main` tagged `v0.19.0` then `v0.19.1` then
|
|
375
|
+
`v0.19.2` and published (`npm view` = `0.19.2`). Same-job bump
|
|
376
|
+
publish via #16. CHE-40 tab-complete via #18.
|
|
377
|
+
- [x] README skills-sectie waar; deze ROADMAP in sync met 0.19.2.
|
|
378
|
+
- [x] CHE-40: `/powerline` subcommand-tab geland (#18). CHE-41/42
|
|
379
|
+
hernoemd naar wishcraft.
|
package/banner.png
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@groeponline/pi-wishcraft",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Wishcraft cockpit for the pi coding agent — powerline status, vibes, idea inbox, bash mode, and full customization.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"*.ts",
|
|
8
8
|
"*.md",
|
|
9
9
|
"*.json",
|
|
10
|
+
"banner.png",
|
|
10
11
|
"src/**/*.ts",
|
|
11
12
|
"src/**/*.txt",
|
|
12
13
|
"bash-mode/**/*.ts",
|
|
@@ -16,9 +16,9 @@ import {
|
|
|
16
16
|
} from "../settings/settings-io.ts";
|
|
17
17
|
import {
|
|
18
18
|
showOpenPortsList,
|
|
19
|
-
showPowerlineMainMenu,
|
|
20
19
|
showSelectOverlay,
|
|
21
20
|
} from "../ui/menu-views.ts";
|
|
21
|
+
import { showPowerlineMainMenu } from "../ui/powerline-menu-view.ts";
|
|
22
22
|
import { openStashHistory } from "../shortcuts/shortcuts-router.ts";
|
|
23
23
|
import { ensureShellSession, setBashModeActive } from "./bash-mode-actions.ts";
|
|
24
24
|
import { setupCustomEditor } from "../ui/custom-editor.ts";
|
|
@@ -305,46 +305,6 @@ export async function activateSegment(
|
|
|
305
305
|
ctx.ui.notify(`${id}: ${value}`, "info");
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
/** Main powerline menu: navigate / configure / info / toggle. */
|
|
309
|
-
export async function showPowerlineMainMenu(
|
|
310
|
-
rt: RuntimeState,
|
|
311
|
-
ctx: any,
|
|
312
|
-
): Promise<void> {
|
|
313
|
-
rt.currentCtx = ctx;
|
|
314
|
-
const choice = await ctx.ui.select("Powerline", [
|
|
315
|
-
"Navigate segments",
|
|
316
|
-
"Configure…",
|
|
317
|
-
"Open ports (full list)",
|
|
318
|
-
"TPS detail",
|
|
319
|
-
"Toggle powerline",
|
|
320
|
-
]);
|
|
321
|
-
if (!choice) return;
|
|
322
|
-
if (choice === "Navigate segments") {
|
|
323
|
-
const picked = await showSegmentNavigator(rt, ctx);
|
|
324
|
-
if (picked) await activateSegment(rt, ctx, picked);
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
if (choice === "Configure…") {
|
|
328
|
-
await configurePowerline(rt, ctx);
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
if (choice === "Open ports (full list)") {
|
|
332
|
-
await showOpenPortsList(ctx);
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
if (choice === "TPS detail") {
|
|
336
|
-
const live = process.env.POWERLINE_TPS
|
|
337
|
-
? `(override ${process.env.POWERLINE_TPS})`
|
|
338
|
-
: "(live 1s window)";
|
|
339
|
-
ctx.ui.notify(`TPS ${live}`, "info");
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
if (choice === "Toggle powerline") {
|
|
343
|
-
ctx.ui.notify("Use /powerline to toggle", "info");
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
308
|
/** Navigable overlay that mirrors the live powerline segments; arrow keys move, Enter activates. */
|
|
349
309
|
export async function showSegmentNavigator(
|
|
350
310
|
rt: RuntimeState,
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { RuntimeState } from "../core/types.ts";
|
|
2
|
+
import {
|
|
3
|
+
activateSegment,
|
|
4
|
+
configurePowerline,
|
|
5
|
+
showOpenPortsList,
|
|
6
|
+
showSegmentNavigator,
|
|
7
|
+
showSelectOverlay,
|
|
8
|
+
} from "./menu-views.ts";
|
|
9
|
+
import {
|
|
10
|
+
assertPowerlineMenuBounds,
|
|
11
|
+
type PowerlineMenuNode,
|
|
12
|
+
powerlineMenuToSelectItems,
|
|
13
|
+
} from "./powerline-menu.ts";
|
|
14
|
+
|
|
15
|
+
async function pickPowerlineMenuNode(
|
|
16
|
+
ctx: any,
|
|
17
|
+
title: string,
|
|
18
|
+
nodes: PowerlineMenuNode[],
|
|
19
|
+
): Promise<PowerlineMenuNode | null> {
|
|
20
|
+
const picked = await showSelectOverlay(
|
|
21
|
+
ctx,
|
|
22
|
+
title,
|
|
23
|
+
"↑↓ navigate · enter open · esc back",
|
|
24
|
+
powerlineMenuToSelectItems(nodes),
|
|
25
|
+
Math.min(nodes.length, 12),
|
|
26
|
+
);
|
|
27
|
+
if (!picked) return null;
|
|
28
|
+
return nodes.find((node) => node.id === picked.value) ?? null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function activatePowerlineMenuAction(
|
|
32
|
+
rt: RuntimeState,
|
|
33
|
+
ctx: any,
|
|
34
|
+
id: string,
|
|
35
|
+
): Promise<void> {
|
|
36
|
+
if (id === "navigate") {
|
|
37
|
+
const picked = await showSegmentNavigator(rt, ctx);
|
|
38
|
+
if (picked) await activateSegment(rt, ctx, picked);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (id === "configure") {
|
|
42
|
+
await configurePowerline(rt, ctx);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (id === "ports") {
|
|
46
|
+
await showOpenPortsList(ctx);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (id === "tps") {
|
|
50
|
+
const live = process.env.POWERLINE_TPS
|
|
51
|
+
? `(override ${process.env.POWERLINE_TPS})`
|
|
52
|
+
: "(live 1s window)";
|
|
53
|
+
ctx.ui.notify(`TPS ${live}`, "info");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (id === "toggle") {
|
|
57
|
+
ctx.ui.notify("Use /powerline to toggle", "info");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Main powerline menu: three top-level overlays, Status drills down. */
|
|
62
|
+
export async function showPowerlineMainMenu(
|
|
63
|
+
rt: RuntimeState,
|
|
64
|
+
ctx: any,
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
rt.currentCtx = ctx;
|
|
67
|
+
while (true) {
|
|
68
|
+
const top = await pickPowerlineMenuNode(
|
|
69
|
+
ctx,
|
|
70
|
+
"Powerline",
|
|
71
|
+
assertPowerlineMenuBounds(),
|
|
72
|
+
);
|
|
73
|
+
if (!top) return;
|
|
74
|
+
if (top.children?.length) {
|
|
75
|
+
const child = await pickPowerlineMenuNode(ctx, top.label, top.children);
|
|
76
|
+
if (!child) continue;
|
|
77
|
+
await activatePowerlineMenuAction(rt, ctx, child.id);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
await activatePowerlineMenuAction(rt, ctx, top.id);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { SelectItem } from "@earendil-works/pi-tui";
|
|
2
|
+
|
|
3
|
+
export interface PowerlineMenuNode {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
children?: PowerlineMenuNode[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const MAX_TOP_LEVEL = 3;
|
|
11
|
+
|
|
12
|
+
export function buildPowerlineMenuItems(): PowerlineMenuNode[] {
|
|
13
|
+
return [
|
|
14
|
+
{ id: "navigate", label: "Navigate segments", description: "Jump to a live segment" },
|
|
15
|
+
{ id: "configure", label: "Configure", description: "Preset, TPS, ports, labels" },
|
|
16
|
+
{
|
|
17
|
+
id: "status",
|
|
18
|
+
label: "Status",
|
|
19
|
+
description: "Powerline status and diagnostics",
|
|
20
|
+
children: [
|
|
21
|
+
{ id: "ports", label: "Open ports", description: "Full listening list" },
|
|
22
|
+
{ id: "tps", label: "TPS detail", description: "Live 1s window or override" },
|
|
23
|
+
{ id: "toggle", label: "Toggle powerline", description: "Use /powerline to enable or disable" },
|
|
24
|
+
{ id: "cpu", label: "CPU usage", description: "Current process CPU usage" },
|
|
25
|
+
{ id: "memory", label: "Memory usage", description: "Current process memory" },
|
|
26
|
+
{ id: "network", label: "Network status", description: "Network interface status" },
|
|
27
|
+
{ id: "uptime", label: "System uptime", description: "Host uptime" },
|
|
28
|
+
{ id: "version", label: "Version info", description: "Runtime and application versions" },
|
|
29
|
+
{ id: "logs", label: "Recent logs", description: "Show recent status messages" },
|
|
30
|
+
{ id: "diagnostics", label: "Diagnostics", description: "Run powerline diagnostics" },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function assertPowerlineMenuBounds(nodes: PowerlineMenuNode[] = buildPowerlineMenuItems()): PowerlineMenuNode[] {
|
|
37
|
+
if (nodes.length > MAX_TOP_LEVEL) throw new Error(`Powerline menu allows at most ${MAX_TOP_LEVEL} top-level items`);
|
|
38
|
+
return nodes;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function findPowerlineMenuNode(id: string, nodes: PowerlineMenuNode[] = buildPowerlineMenuItems()): PowerlineMenuNode | null {
|
|
42
|
+
for (const node of nodes) {
|
|
43
|
+
if (node.id === id) return node;
|
|
44
|
+
if (node.children) {
|
|
45
|
+
const nested = findPowerlineMenuNode(id, node.children);
|
|
46
|
+
if (nested) return nested;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function powerlineMenuToSelectItems(nodes: PowerlineMenuNode[]): SelectItem[] {
|
|
53
|
+
return nodes.map((node) => ({ value: node.id, label: node.label, description: node.description }));
|
|
54
|
+
}
|
package/src/theme/theme.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Theme, ThemeColor } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { join, dirname } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { getAgentPath } from "../paths/agent-dirs.ts";
|
|
@@ -64,11 +64,20 @@ function resolvePackageDir(startDir: string): string {
|
|
|
64
64
|
return startDir;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
function fileIdentity(path: string): string {
|
|
68
|
+
try {
|
|
69
|
+
return `${path}:${statSync(path).mtimeMs}`;
|
|
70
|
+
} catch {
|
|
71
|
+
return `${path}:missing`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
67
75
|
function getConfigurationPaths(): string[] {
|
|
68
76
|
const currentFileDir = dirname(fileURLToPath(import.meta.url));
|
|
69
77
|
const pkgDir = resolvePackageDir(currentFileDir);
|
|
70
78
|
return [
|
|
71
79
|
getAgentPath("extensions", "powerline-footer", "theme.json"),
|
|
80
|
+
join(process.cwd(), "theme.json"),
|
|
72
81
|
join(pkgDir, "theme.json"),
|
|
73
82
|
];
|
|
74
83
|
}
|
|
@@ -87,7 +96,7 @@ function mergeThemeConfig(base: PowerlineThemeConfig, overrides: unknown): Power
|
|
|
87
96
|
|
|
88
97
|
export function loadThemeConfig(): PowerlineThemeConfig {
|
|
89
98
|
const paths = getConfigurationPaths();
|
|
90
|
-
const identity = paths.join("|");
|
|
99
|
+
const identity = paths.map(fileIdentity).join("|");
|
|
91
100
|
const time = Date.now();
|
|
92
101
|
|
|
93
102
|
if (cachedConfig && cacheIdentifier === identity && time - lastCacheUpdate < CACHE_LIFETIME_MS) {
|