@timurproko/a1 0.2.7-dev.1238 → 0.2.7-dev.1239
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/dist/composition/owned-settings.js +1 -1
- package/dist/composition/release-update-observation.js +43 -14
- package/dist/features/owned-ui/plus-menu-dialog.d.ts +13 -7
- package/dist/features/owned-ui/plus-menu-dialog.js +17 -12
- package/dist/features/owned-ui/settings-app.js +13 -6
- package/dist/foundation/release/automatic-update.d.ts +12 -0
- package/dist/foundation/release/automatic-update.js +87 -5
- package/dist/integrations/pi/engine/diagnostics-state.js +6 -5
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/darwin-arm64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +1 -1
- package/dist/native/darwin-x64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/linux-x64/terminal-host.manifest.json +3 -3
- package/dist/native/linux-x64/terminal-host.node +0 -0
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +3 -3
- package/dist/native/win32-x64/terminal-host.node +0 -0
- package/dist/ui/settings/declarations.d.ts +23 -2
- package/dist/ui/settings/declarations.js +18 -2
- package/dist/ui/settings/migrations.js +7 -0
- package/package.json +3 -3
|
@@ -14,7 +14,7 @@ export function createOwnedSettingsManager(options) {
|
|
|
14
14
|
legacyConfigDir: productPaths.configDir,
|
|
15
15
|
...(options.agent === undefined ? {} : { agent: options.agent }),
|
|
16
16
|
...(options.agentProvider === undefined ? {} : { agentProvider: options.agentProvider }),
|
|
17
|
-
...(options.hideTabSettings ? { hiddenOwnedSettingIds: ["tabsAutoName", "tabsPosition", "tabBarPlusMenu"] } : {}),
|
|
17
|
+
...(options.hideTabSettings ? { hiddenOwnedSettingIds: ["tabsAutoName", "tabsPosition", "tabBarPlusMenu", "tabBarRightItems"] } : {}),
|
|
18
18
|
hiddenAgentSettingIds: ["fullscreenWheelScrollLines"],
|
|
19
19
|
agentSettingLabelOverrides: { fullscreenCopyOnSelect: "Copy on select" },
|
|
20
20
|
});
|
|
@@ -4,13 +4,41 @@ class ReleaseUpdateObservation {
|
|
|
4
4
|
#controller = new AbortController();
|
|
5
5
|
#timer;
|
|
6
6
|
#announced = null;
|
|
7
|
+
#installedAnnounced = false;
|
|
7
8
|
#eligible;
|
|
8
9
|
#cadence = null;
|
|
9
10
|
#nextDelay = null;
|
|
10
11
|
#started = false;
|
|
11
12
|
#running = false;
|
|
12
13
|
#refresh = false;
|
|
14
|
+
#stopActiveReleaseObservation = () => { };
|
|
13
15
|
constructor(input) { this.#input = input; this.#eligible = input.mode === "auto"; }
|
|
16
|
+
#announce(status, version) {
|
|
17
|
+
const key = `${status}:${version}`;
|
|
18
|
+
if (key === this.#announced || (status === "installed" && this.#installedAnnounced) || this.#controller.signal.aborted)
|
|
19
|
+
return;
|
|
20
|
+
this.#announced = key;
|
|
21
|
+
if (status === "installed") {
|
|
22
|
+
this.#installedAnnounced = true;
|
|
23
|
+
this.#eligible = false;
|
|
24
|
+
if (this.#timer !== undefined)
|
|
25
|
+
clearTimeout(this.#timer);
|
|
26
|
+
}
|
|
27
|
+
this.#input.backend.extensions.announceReleaseUpdate({ status, version });
|
|
28
|
+
}
|
|
29
|
+
async #startActiveReleaseObservation() {
|
|
30
|
+
const runningVersion = this.#input.packageVersion ?? await this.#input.readPackageVersion();
|
|
31
|
+
if (this.#controller.signal.aborted)
|
|
32
|
+
return;
|
|
33
|
+
const module = await import("../foundation/release/automatic-update.js");
|
|
34
|
+
if (this.#controller.signal.aborted)
|
|
35
|
+
return;
|
|
36
|
+
this.#stopActiveReleaseObservation = module.observeNewerActiveRelease({
|
|
37
|
+
runningVersion,
|
|
38
|
+
signal: this.#controller.signal,
|
|
39
|
+
onNewer: version => this.#announce("installed", version),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
14
42
|
async #observe() {
|
|
15
43
|
// Performance: registry modules and their locks stay outside the first-frame startup graph.
|
|
16
44
|
const releases = await import("../foundation/release/latest-release.js");
|
|
@@ -41,22 +69,18 @@ class ReleaseUpdateObservation {
|
|
|
41
69
|
}
|
|
42
70
|
if (input.mode !== "auto")
|
|
43
71
|
return;
|
|
44
|
-
const announce = (status, version) => {
|
|
45
|
-
const actual = input.clientVersion?.();
|
|
46
|
-
const key = `${status}:${version}`;
|
|
47
|
-
if (key === this.#announced || this.#controller.signal.aborted
|
|
48
|
-
|| status !== "blocked-cleared" && actual && !releases.isNewerRelease(version, actual))
|
|
49
|
-
return;
|
|
50
|
-
this.#announced = key;
|
|
51
|
-
input.backend.extensions.announceReleaseUpdate({ status, version });
|
|
52
|
-
};
|
|
53
72
|
const installed = await import("../foundation/release/automatic-update.js")
|
|
54
73
|
.then(module => module.requestAutomaticUpdate({
|
|
55
74
|
release, runningVersion, signal: this.#controller.signal,
|
|
56
|
-
onActivationBlocked: version =>
|
|
75
|
+
onActivationBlocked: version => {
|
|
76
|
+
const clientVersion = input.clientVersion?.();
|
|
77
|
+
if (version !== null && clientVersion && !releases.isNewerRelease(version, clientVersion))
|
|
78
|
+
return;
|
|
79
|
+
this.#announce(version === null ? "blocked-cleared" : "blocked", version ?? release.version);
|
|
80
|
+
},
|
|
57
81
|
}));
|
|
58
82
|
if (installed !== null)
|
|
59
|
-
announce("installed", installed);
|
|
83
|
+
this.#announce("installed", installed);
|
|
60
84
|
}
|
|
61
85
|
async #run() {
|
|
62
86
|
if (this.#running) {
|
|
@@ -83,13 +107,18 @@ class ReleaseUpdateObservation {
|
|
|
83
107
|
if (this.#started || this.#controller.signal.aborted)
|
|
84
108
|
return;
|
|
85
109
|
this.#started = true;
|
|
110
|
+
void this.#startActiveReleaseObservation().catch(() => undefined);
|
|
86
111
|
void this.#run();
|
|
87
112
|
}
|
|
88
|
-
stop() {
|
|
89
|
-
|
|
113
|
+
stop() {
|
|
114
|
+
this.#controller.abort();
|
|
115
|
+
this.#stopActiveReleaseObservation();
|
|
116
|
+
if (this.#timer !== undefined)
|
|
117
|
+
clearTimeout(this.#timer);
|
|
118
|
+
}
|
|
90
119
|
clientChanged() {
|
|
91
120
|
this.#announced = null;
|
|
92
|
-
if (!this.#started || this.#controller.signal.aborted || this.#input.mode === "no")
|
|
121
|
+
if (!this.#started || this.#controller.signal.aborted || this.#installedAnnounced || this.#input.mode === "no")
|
|
93
122
|
return;
|
|
94
123
|
if (this.#timer !== undefined)
|
|
95
124
|
clearTimeout(this.#timer);
|
|
@@ -6,17 +6,23 @@ export interface PlusMenuKind {
|
|
|
6
6
|
readonly key: string;
|
|
7
7
|
readonly label: string;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
/** Presentation copy for a searchable ordered flag multi-select dialog. */
|
|
10
|
+
export interface FlagToggleDialogPresentation {
|
|
11
|
+
readonly title: string;
|
|
12
|
+
readonly description: string;
|
|
13
|
+
readonly empty: string;
|
|
14
|
+
}
|
|
15
|
+
/** Owns only filtering and selection; the Settings screen owns enabled state and persistence. */
|
|
16
|
+
export declare class FlagToggleDialog {
|
|
15
17
|
#private;
|
|
16
|
-
constructor(kinds: readonly PlusMenuKind[]);
|
|
18
|
+
constructor(kinds: readonly PlusMenuKind[], presentation: FlagToggleDialogPresentation);
|
|
17
19
|
/** The kinds the filter keeps, in kind order. */
|
|
18
20
|
shown(): readonly PlusMenuKind[];
|
|
19
21
|
/** Handles one key by its resolved name; `toggle` flips a kind. Returns whether the dialog closed. */
|
|
20
22
|
handle(name: string, data: string, toggle: (key: string) => void): "close" | "handled";
|
|
21
23
|
lines(enabled: Readonly<Record<string, boolean | string>>, width: number, theme: UiTheme): readonly string[];
|
|
22
24
|
}
|
|
25
|
+
/** The tab bar's specialized flag dialog, retained as the public Plus Menu surface. */
|
|
26
|
+
export declare class PlusMenuDialog extends FlagToggleDialog {
|
|
27
|
+
constructor(kinds: readonly PlusMenuKind[]);
|
|
28
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { LineInput, handleLineInputKey, padToWidth, renderInputRow, renderShortcutHintsWithClose, truncateToWidth, } from "../../ui/components/index.js";
|
|
2
2
|
/** The A1 setting holding the tab kinds the strip's `+` offers. */
|
|
3
3
|
export const PLUS_MENU_SETTING_ID = "tabBarPlusMenu";
|
|
4
|
-
const
|
|
5
|
-
|
|
4
|
+
const PLUS_MENU_PRESENTATION = Object.freeze({
|
|
5
|
+
title: "Plus Menu",
|
|
6
|
+
description: "Choose what the + button in the tab bar can add.",
|
|
7
|
+
empty: "No tabs match",
|
|
8
|
+
});
|
|
6
9
|
const HINTS = [
|
|
7
10
|
{ key: "Type", action: "search" },
|
|
8
11
|
{ key: "↑↓", action: "navigate" },
|
|
@@ -10,17 +13,15 @@ const HINTS = [
|
|
|
10
13
|
{ key: "esc", action: "close" },
|
|
11
14
|
];
|
|
12
15
|
const PADDING_X = 2;
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
* text. It owns only its filter and the row in hand; the enabled record and its saving stay with the
|
|
16
|
-
* Settings screen that opened it.
|
|
17
|
-
*/
|
|
18
|
-
export class PlusMenuDialog {
|
|
16
|
+
/** Owns only filtering and selection; the Settings screen owns enabled state and persistence. */
|
|
17
|
+
export class FlagToggleDialog {
|
|
19
18
|
#kinds;
|
|
19
|
+
#presentation;
|
|
20
20
|
#filter = new LineInput("");
|
|
21
21
|
#index = 0;
|
|
22
|
-
constructor(kinds) {
|
|
22
|
+
constructor(kinds, presentation) {
|
|
23
23
|
this.#kinds = kinds;
|
|
24
|
+
this.#presentation = presentation;
|
|
24
25
|
}
|
|
25
26
|
/** The kinds the filter keeps, in kind order. */
|
|
26
27
|
shown() {
|
|
@@ -60,7 +61,7 @@ export class PlusMenuDialog {
|
|
|
60
61
|
const shown = this.shown();
|
|
61
62
|
const index = Math.min(this.#index, Math.max(0, shown.length - 1));
|
|
62
63
|
const rows = shown.length === 0
|
|
63
|
-
? [row(theme.fg("dim",
|
|
64
|
+
? [row(theme.fg("dim", this.#presentation.empty))]
|
|
64
65
|
: shown.map((kind, position) => {
|
|
65
66
|
const selected = position === index;
|
|
66
67
|
const cursor = selected ? "→ " : " ";
|
|
@@ -70,8 +71,8 @@ export class PlusMenuDialog {
|
|
|
70
71
|
});
|
|
71
72
|
return [
|
|
72
73
|
blank,
|
|
73
|
-
row(theme.bold(theme.fg("accent",
|
|
74
|
-
row(theme.fg("dim",
|
|
74
|
+
row(theme.bold(theme.fg("accent", this.#presentation.title))),
|
|
75
|
+
row(theme.fg("dim", this.#presentation.description)),
|
|
75
76
|
blank,
|
|
76
77
|
...renderInputRow(this.#filter, contentWidth, { ruled: false, promptGlyph: "> ", theme }).lines.map(line => row(line)),
|
|
77
78
|
blank,
|
|
@@ -82,3 +83,7 @@ export class PlusMenuDialog {
|
|
|
82
83
|
];
|
|
83
84
|
}
|
|
84
85
|
}
|
|
86
|
+
/** The tab bar's specialized flag dialog, retained as the public Plus Menu surface. */
|
|
87
|
+
export class PlusMenuDialog extends FlagToggleDialog {
|
|
88
|
+
constructor(kinds) { super(kinds, PLUS_MENU_PRESENTATION); }
|
|
89
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DIALOG_BACK_SHORTCUT_HINT, DIALOG_CLOSE_SHORTCUT_HINT } from "../../contracts/presentation/index.js";
|
|
2
2
|
import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, numericValues, RAIL_COLUMNS, renderCenteredDialogOverlay, renderDialogPanel, renderSteppedDialogPanel, renderEmptyState, renderGroupHeader, renderListRow, renderNote, renderShortcutHintsWithClose, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, formatLocalTime, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, layoutList, moveSelection, rowKey, scrollForSelection, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, ScrollbarRails, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
|
|
3
|
-
import { PLUS_MENU_SETTING_ID, PlusMenuDialog } from "./plus-menu-dialog.js";
|
|
3
|
+
import { FlagToggleDialog, PLUS_MENU_SETTING_ID, PlusMenuDialog } from "./plus-menu-dialog.js";
|
|
4
4
|
import { SETTINGS_APP_ID } from "./settings-route.js";
|
|
5
5
|
export { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
|
|
6
6
|
const SCOPE = SETTINGS_APP_ID;
|
|
@@ -22,6 +22,7 @@ const SCROLL_LINGER_MS = 900;
|
|
|
22
22
|
const SCROLL_LINGER_REPAINT_MS = 925;
|
|
23
23
|
/** What a structured value offers instead of printing itself. */
|
|
24
24
|
const CONFIGURE = "configure";
|
|
25
|
+
const TAB_BAR_RIGHT_ITEMS_SETTING_ID = "tabBarRightItems";
|
|
25
26
|
export const SETTINGS_SHORTCUTS = new ShortcutRegistry();
|
|
26
27
|
SETTINGS_SHORTCUTS.declare({ key: "edit", scope: SCOPE, description: "Search settings", section: "Change", hint: { keys: "Type", does: "search" } }, "edit-filter");
|
|
27
28
|
SETTINGS_SHORTCUTS.declare({ key: "up", scope: SCOPE, description: "Previous setting", section: "Navigate", hint: { keys: "↑↓", does: "navigate" } }, "move-up");
|
|
@@ -539,7 +540,13 @@ export class SettingsApp {
|
|
|
539
540
|
entry,
|
|
540
541
|
flags: entry.flags.map(flag => flag.key),
|
|
541
542
|
modelDialog,
|
|
542
|
-
|
|
543
|
+
flagDialog: entry.backend !== "a1" ? null
|
|
544
|
+
: entry.id === PLUS_MENU_SETTING_ID ? new PlusMenuDialog(entry.flags)
|
|
545
|
+
: entry.id === TAB_BAR_RIGHT_ITEMS_SETTING_ID ? new FlagToggleDialog(entry.flags, {
|
|
546
|
+
title: "Tab Bar Right Side",
|
|
547
|
+
description: "Choose what the right side of the tab bar shows.",
|
|
548
|
+
empty: "No items match",
|
|
549
|
+
}) : null,
|
|
543
550
|
filter: modelDialog === null ? null : new LineInput(""),
|
|
544
551
|
step: "model",
|
|
545
552
|
modelKey: null,
|
|
@@ -555,8 +562,8 @@ export class SettingsApp {
|
|
|
555
562
|
return { consumed: false };
|
|
556
563
|
if (open.modelDialog !== null)
|
|
557
564
|
return this.#modelDialogKey(open, data);
|
|
558
|
-
if (open.
|
|
559
|
-
const outcome = open.
|
|
565
|
+
if (open.flagDialog !== null) {
|
|
566
|
+
const outcome = open.flagDialog.handle(KEYS[data] ?? data, data, key => this.#toggleFlag(open.flags.indexOf(key)));
|
|
560
567
|
if (outcome === "close")
|
|
561
568
|
this.#structured = null;
|
|
562
569
|
return { consumed: true };
|
|
@@ -1029,8 +1036,8 @@ export class SettingsApp {
|
|
|
1029
1036
|
#dialogLines(open, width, height, theme) {
|
|
1030
1037
|
if (open.modelDialog !== null)
|
|
1031
1038
|
return this.#modelDialogLines(open, width, height, theme);
|
|
1032
|
-
if (open.
|
|
1033
|
-
return open.
|
|
1039
|
+
if (open.flagDialog !== null)
|
|
1040
|
+
return open.flagDialog.lines(open.record, width, theme);
|
|
1034
1041
|
const rows = open.flags.map(key => {
|
|
1035
1042
|
const declared = open.entry.flags.find(flag => flag.key === key);
|
|
1036
1043
|
return {
|
|
@@ -2,6 +2,7 @@ import { type AvailableRelease, type ReleaseChannel } from "./latest-release.js"
|
|
|
2
2
|
export declare const AUTOMATIC_UPDATE_STATE_SCHEMA = 1;
|
|
3
3
|
export declare const AUTOMATIC_UPDATE_RETRY_MS: number;
|
|
4
4
|
export declare const AUTOMATIC_UPDATE_ACTIVATION_RETRY_MS: number;
|
|
5
|
+
export declare const ACTIVE_RELEASE_FALLBACK_MS = 5000;
|
|
5
6
|
export interface AutomaticUpdateState {
|
|
6
7
|
readonly schema: typeof AUTOMATIC_UPDATE_STATE_SCHEMA;
|
|
7
8
|
readonly channel: ReleaseChannel;
|
|
@@ -18,6 +19,17 @@ export declare class AutomaticUpdateStateStore {
|
|
|
18
19
|
read(): Promise<AutomaticUpdateState | null>;
|
|
19
20
|
write(value: AutomaticUpdateState): Promise<void>;
|
|
20
21
|
}
|
|
22
|
+
export interface ActiveReleaseObservationOptions {
|
|
23
|
+
readonly runningVersion: string;
|
|
24
|
+
readonly environment?: NodeJS.ProcessEnv;
|
|
25
|
+
readonly signal?: AbortSignal;
|
|
26
|
+
readonly onNewer: (version: string) => void;
|
|
27
|
+
readonly fallbackMs?: number;
|
|
28
|
+
}
|
|
29
|
+
/** Read the approved active release only when this process executes an older release on that channel. */
|
|
30
|
+
export declare function readNewerActiveRelease(runningVersion: string, environment?: NodeJS.ProcessEnv): Promise<string | null>;
|
|
31
|
+
/** Observe local release activation promptly, with a bounded fallback for missed filesystem events. */
|
|
32
|
+
export declare function observeNewerActiveRelease(options: ActiveReleaseObservationOptions): () => void;
|
|
21
33
|
export interface AutomaticUpdateRequestOptions {
|
|
22
34
|
readonly release: AvailableRelease;
|
|
23
35
|
readonly runningVersion: string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { watch } from "node:fs";
|
|
4
|
+
import { mkdir, open, readFile, rename, rm, stat } from "node:fs/promises";
|
|
5
|
+
import { basename, dirname, resolve } from "node:path";
|
|
5
6
|
import { valid as validSemver } from "semver";
|
|
6
7
|
import { readLaunchContext } from "../launch-context/index.js";
|
|
7
8
|
import { resolveProductPaths } from "../lifecycle/index.js";
|
|
@@ -19,6 +20,7 @@ const AUTOMATIC_UPDATE_STATE_FILENAME = "automatic-update.json";
|
|
|
19
20
|
const MAX_STATE_BYTES = 16_384;
|
|
20
21
|
const OBSERVER_POLL_MS = 250;
|
|
21
22
|
const WORKER_START_GRACE_MS = 5_000;
|
|
23
|
+
export const ACTIVE_RELEASE_FALLBACK_MS = 5_000;
|
|
22
24
|
export class AutomaticUpdateStateStore {
|
|
23
25
|
path;
|
|
24
26
|
constructor(dataDir) { this.path = resolve(dataDir, AUTOMATIC_UPDATE_STATE_FILENAME); }
|
|
@@ -59,6 +61,82 @@ export class AutomaticUpdateStateStore {
|
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
}
|
|
64
|
+
/** Read the approved active release only when this process executes an older release on that channel. */
|
|
65
|
+
export async function readNewerActiveRelease(runningVersion, environment = process.env) {
|
|
66
|
+
const channel = releaseChannelOf(runningVersion);
|
|
67
|
+
if (channel === null)
|
|
68
|
+
return null;
|
|
69
|
+
try {
|
|
70
|
+
const paths = resolveProductPaths(environment);
|
|
71
|
+
const active = await activeRelease(paths.dataDir);
|
|
72
|
+
return activeNewerPackageVersion(active, runningVersion, channel) ?? null;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Observe local release activation promptly, with a bounded fallback for missed filesystem events. */
|
|
79
|
+
export function observeNewerActiveRelease(options) {
|
|
80
|
+
if (releaseChannelOf(options.runningVersion) === null || options.signal?.aborted)
|
|
81
|
+
return () => { };
|
|
82
|
+
const paths = resolveProductPaths(options.environment ?? process.env);
|
|
83
|
+
const store = new CohortStateStore(paths.dataDir);
|
|
84
|
+
let closed = false;
|
|
85
|
+
let checking = false;
|
|
86
|
+
let requested = false;
|
|
87
|
+
let watcher = null;
|
|
88
|
+
let signature;
|
|
89
|
+
const stop = () => {
|
|
90
|
+
if (closed)
|
|
91
|
+
return;
|
|
92
|
+
closed = true;
|
|
93
|
+
clearInterval(fallback);
|
|
94
|
+
watcher?.close();
|
|
95
|
+
options.signal?.removeEventListener("abort", stop);
|
|
96
|
+
};
|
|
97
|
+
const check = async () => {
|
|
98
|
+
if (closed)
|
|
99
|
+
return;
|
|
100
|
+
if (checking) {
|
|
101
|
+
requested = true;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
checking = true;
|
|
105
|
+
try {
|
|
106
|
+
const version = await readNewerActiveRelease(options.runningVersion, options.environment);
|
|
107
|
+
if (version !== null && !closed) {
|
|
108
|
+
stop();
|
|
109
|
+
options.onNewer(version);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
checking = false;
|
|
114
|
+
if (requested && !closed) {
|
|
115
|
+
requested = false;
|
|
116
|
+
void check();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const checkChanged = async (force) => {
|
|
121
|
+
const next = await stat(store.path).then(value => `${value.dev}:${value.ino}:${value.ctimeMs}:${value.mtimeMs}:${value.size}`, () => null);
|
|
122
|
+
if (!force && next === signature)
|
|
123
|
+
return;
|
|
124
|
+
signature = next;
|
|
125
|
+
await check();
|
|
126
|
+
};
|
|
127
|
+
const fallback = setInterval(() => { void checkChanged(false); }, options.fallbackMs ?? ACTIVE_RELEASE_FALLBACK_MS);
|
|
128
|
+
fallback.unref?.();
|
|
129
|
+
try {
|
|
130
|
+
watcher = watch(dirname(store.path), { persistent: false }, (_event, filename) => {
|
|
131
|
+
if (filename === null || filename.toString() === basename(store.path))
|
|
132
|
+
void checkChanged(false);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch { /* Rationale: the bounded metadata fallback remains authoritative when watching is unavailable. */ }
|
|
136
|
+
options.signal?.addEventListener("abort", stop, { once: true });
|
|
137
|
+
void checkChanged(true);
|
|
138
|
+
return stop;
|
|
139
|
+
}
|
|
62
140
|
/** Start or observe one silent update and resolve with the active newer version once restart is useful. */
|
|
63
141
|
export async function requestAutomaticUpdate(options) {
|
|
64
142
|
if (options.signal?.aborted)
|
|
@@ -292,9 +370,13 @@ async function spawnAutomaticUpdateWorker(entry, arguments_, environment) {
|
|
|
292
370
|
}
|
|
293
371
|
async function activeNewerVersion(dataDir, runningVersion, target) {
|
|
294
372
|
const active = await activeRelease(dataDir);
|
|
295
|
-
return active
|
|
296
|
-
|
|
297
|
-
|
|
373
|
+
return activeNewerPackageVersion(active, runningVersion, target.channel, target.version);
|
|
374
|
+
}
|
|
375
|
+
function activeNewerPackageVersion(active, runningVersion, channel, minimum) {
|
|
376
|
+
if (active === null || releaseChannelOf(active.packageVersion) !== channel
|
|
377
|
+
|| !isNewerRelease(active.packageVersion, runningVersion))
|
|
378
|
+
return null;
|
|
379
|
+
return minimum === undefined || active.packageVersion === minimum || isNewerRelease(active.packageVersion, minimum)
|
|
298
380
|
? active.packageVersion : null;
|
|
299
381
|
}
|
|
300
382
|
async function activeRelease(dataDir) {
|
|
@@ -22,11 +22,12 @@ export class EngineDiagnostics {
|
|
|
22
22
|
if (validSemver(version) === null)
|
|
23
23
|
return false;
|
|
24
24
|
return this.#remove(diagnostic => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// Invariant: restart readiness describes this long-lived process, not the outer client that
|
|
26
|
+
// attached to it. A newer client can clear activation blocking, but only process exit clears
|
|
27
|
+
// an installed-update notice.
|
|
28
|
+
const target = diagnostic.code === "release-update-blocked"
|
|
29
|
+
? /^Update (\S+) prepared; activation blocked\.$/u.exec(diagnostic.message)?.[1]
|
|
30
|
+
: undefined;
|
|
30
31
|
return target !== undefined && validSemver(target) !== null && gte(version, target);
|
|
31
32
|
});
|
|
32
33
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"platform": "darwin",
|
|
8
8
|
"architecture": "arm64",
|
|
9
9
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-11-
|
|
10
|
+
"builtAt": "2026-11-03T20:15:18.883Z",
|
|
11
11
|
"artifact": {
|
|
12
12
|
"filename": "process-guardian.node",
|
|
13
13
|
"sha256": "a87eaaffcd986b624c8b044b756446cc1bdaf4334a1775b97820542fe34f9fb7",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"architecture": "arm64",
|
|
10
10
|
"target": "aarch64-apple-darwin",
|
|
11
11
|
"capability": "supported",
|
|
12
|
-
"builtAt": "2026-11-
|
|
12
|
+
"builtAt": "2026-11-03T20:18:38.194Z",
|
|
13
13
|
"artifact": {
|
|
14
14
|
"filename": "terminal-host.node",
|
|
15
|
-
"sha256": "
|
|
16
|
-
"size":
|
|
15
|
+
"sha256": "b14f69c00c7d472bdc4d742c7fc7d8a517d23aa1233a3e1e9e76b5f884999baa",
|
|
16
|
+
"size": 6585648
|
|
17
17
|
},
|
|
18
18
|
"entry": {
|
|
19
19
|
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
Binary file
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"platform": "darwin",
|
|
8
8
|
"architecture": "x64",
|
|
9
9
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-11-
|
|
10
|
+
"builtAt": "2026-11-03T20:15:26.152Z",
|
|
11
11
|
"artifact": {
|
|
12
12
|
"filename": "process-guardian.node",
|
|
13
13
|
"sha256": "783a65f0de287a1c02cd5429dbe97bc8633f4cc8a7b92e3e961180b9bdcff814",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"architecture": "x64",
|
|
10
10
|
"target": "x86_64-apple-darwin",
|
|
11
11
|
"capability": "supported",
|
|
12
|
-
"builtAt": "2026-11-
|
|
12
|
+
"builtAt": "2026-11-03T20:21:22.213Z",
|
|
13
13
|
"artifact": {
|
|
14
14
|
"filename": "terminal-host.node",
|
|
15
|
-
"sha256": "
|
|
16
|
-
"size":
|
|
15
|
+
"sha256": "1c0471bb3f2f9520acd1808cce080600b7e3dd485c4d3a8bc6f8aa342768136b",
|
|
16
|
+
"size": 7182168
|
|
17
17
|
},
|
|
18
18
|
"entry": {
|
|
19
19
|
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
Binary file
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"platform": "linux",
|
|
8
8
|
"architecture": "x64",
|
|
9
9
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-11-
|
|
10
|
+
"builtAt": "2026-11-03T20:15:23.280Z",
|
|
11
11
|
"artifact": {
|
|
12
12
|
"filename": "process-guardian.node",
|
|
13
13
|
"sha256": "118e5954e3864672bd8482ab7edf419cb27dcdf2533720316979b7f5cc5a53a5",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"architecture": "x64",
|
|
10
10
|
"target": "x86_64-unknown-linux-gnu",
|
|
11
11
|
"capability": "supported",
|
|
12
|
-
"builtAt": "2026-11-
|
|
12
|
+
"builtAt": "2026-11-03T20:18:59.521Z",
|
|
13
13
|
"artifact": {
|
|
14
14
|
"filename": "terminal-host.node",
|
|
15
|
-
"sha256": "
|
|
16
|
-
"size":
|
|
15
|
+
"sha256": "54eb39f88b6bfe88b6fd6815be1196d678450d06d1de1169a6de79bd4bf5ebec",
|
|
16
|
+
"size": 7961616
|
|
17
17
|
},
|
|
18
18
|
"entry": {
|
|
19
19
|
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
Binary file
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
"platform": "win32",
|
|
8
8
|
"architecture": "x64",
|
|
9
9
|
"capability": "supported",
|
|
10
|
-
"builtAt": "2026-11-
|
|
10
|
+
"builtAt": "2026-11-03T20:16:09.523Z",
|
|
11
11
|
"artifact": {
|
|
12
12
|
"filename": "process-guardian.node",
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "9489c6ea57e8cf13e14afb173cef1b4bb87c2cadaf14e583f93f58e0b4dd57dd",
|
|
14
14
|
"size": 402432
|
|
15
15
|
},
|
|
16
16
|
"provenance": {
|
|
Binary file
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"architecture": "x64",
|
|
10
10
|
"target": "x86_64-pc-windows-msvc",
|
|
11
11
|
"capability": "supported",
|
|
12
|
-
"builtAt": "2026-11-
|
|
12
|
+
"builtAt": "2026-11-03T20:20:00.737Z",
|
|
13
13
|
"artifact": {
|
|
14
14
|
"filename": "terminal-host.node",
|
|
15
|
-
"sha256": "
|
|
16
|
-
"size":
|
|
15
|
+
"sha256": "1b8c8a4dbdfad9873a196a5f2746fc86a92678876bfacdf4b28830f40e95f963",
|
|
16
|
+
"size": 7080960
|
|
17
17
|
},
|
|
18
18
|
"entry": {
|
|
19
19
|
"filename": "dist/foundation/resident-tabs/host-entry.js",
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export declare const OWNED_UI_SETTINGS_VERSION = 20;
|
|
2
2
|
export type OwnedUiSettingValue = string | number | boolean;
|
|
3
3
|
export type OwnedUiSettingApplication = "live" | "restart";
|
|
4
4
|
export interface OwnedUiSettingDeclaration {
|
|
@@ -42,7 +42,7 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
42
42
|
id: "generic";
|
|
43
43
|
title: "Generic";
|
|
44
44
|
}>;
|
|
45
|
-
description: "Format used by submitted-prompt timestamps and the resident tab clock.";
|
|
45
|
+
description: "Format used by submitted-prompt timestamps and the resident tab clock when Time is enabled.";
|
|
46
46
|
application: "live";
|
|
47
47
|
defaultValue: "8:00 PM";
|
|
48
48
|
allowedValues: readonly ["20:00", "8:00 PM"];
|
|
@@ -228,6 +228,27 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
|
|
|
228
228
|
description: "Show the one Changelog tab.";
|
|
229
229
|
}>)[];
|
|
230
230
|
}>;
|
|
231
|
+
readonly tabBarRightItems: Readonly<{
|
|
232
|
+
id: "tabBarRightItems";
|
|
233
|
+
label: "Right side";
|
|
234
|
+
section: Readonly<{
|
|
235
|
+
id: "tabs";
|
|
236
|
+
title: "Tab Bar";
|
|
237
|
+
}>;
|
|
238
|
+
description: "Choose whether the tab bar's right side shows the executing Version, Time, both, or neither.";
|
|
239
|
+
application: "live";
|
|
240
|
+
defaultValue: "version,time";
|
|
241
|
+
allowedValues: readonly ["", "version", "time", "version,time"];
|
|
242
|
+
flags: readonly (Readonly<{
|
|
243
|
+
key: "version";
|
|
244
|
+
label: "Version";
|
|
245
|
+
description: "Show the executing A1 version.";
|
|
246
|
+
}> | Readonly<{
|
|
247
|
+
key: "time";
|
|
248
|
+
label: "Time";
|
|
249
|
+
description: "Show the current time.";
|
|
250
|
+
}>)[];
|
|
251
|
+
}>;
|
|
231
252
|
readonly tabsAutoName: Readonly<{
|
|
232
253
|
id: "tabsAutoName";
|
|
233
254
|
label: "Auto name";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OWNED_UI_SETTINGS_VERSION =
|
|
1
|
+
export const OWNED_UI_SETTINGS_VERSION = 20;
|
|
2
2
|
const UI_ACCENT_COLOR_CHOICES = Object.freeze([
|
|
3
3
|
"blue", "purple", "cyan", "green", "orange", "pink",
|
|
4
4
|
]);
|
|
@@ -19,6 +19,12 @@ const PLUS_MENU_KINDS = Object.freeze([
|
|
|
19
19
|
const PLUS_MENU_VALUES = Object.freeze([
|
|
20
20
|
"", "agent", "settings", "changelog", "agent,settings", "agent,changelog", "settings,changelog", "agent,settings,changelog",
|
|
21
21
|
]);
|
|
22
|
+
/** The right-side strip items, in their fixed presentation order. */
|
|
23
|
+
const TAB_BAR_RIGHT_ITEMS = Object.freeze([
|
|
24
|
+
Object.freeze({ key: "version", label: "Version", description: "Show the executing A1 version." }),
|
|
25
|
+
Object.freeze({ key: "time", label: "Time", description: "Show the current time." }),
|
|
26
|
+
]);
|
|
27
|
+
const TAB_BAR_RIGHT_ITEM_VALUES = Object.freeze(["", "version", "time", "version,time"]);
|
|
22
28
|
export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
23
29
|
updateCheck: Object.freeze({
|
|
24
30
|
id: "updateCheck",
|
|
@@ -33,7 +39,7 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
33
39
|
id: "timeFormat",
|
|
34
40
|
label: "Time format",
|
|
35
41
|
section: GENERIC_SECTION,
|
|
36
|
-
description: "Format used by submitted-prompt timestamps and the resident tab clock.",
|
|
42
|
+
description: "Format used by submitted-prompt timestamps and the resident tab clock when Time is enabled.",
|
|
37
43
|
application: "live",
|
|
38
44
|
defaultValue: "8:00 PM",
|
|
39
45
|
allowedValues: Object.freeze([
|
|
@@ -169,6 +175,16 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
|
|
|
169
175
|
allowedValues: PLUS_MENU_VALUES,
|
|
170
176
|
flags: PLUS_MENU_KINDS,
|
|
171
177
|
}),
|
|
178
|
+
tabBarRightItems: Object.freeze({
|
|
179
|
+
id: "tabBarRightItems",
|
|
180
|
+
label: "Right side",
|
|
181
|
+
section: TABS_SECTION,
|
|
182
|
+
description: "Choose whether the tab bar's right side shows the executing Version, Time, both, or neither.",
|
|
183
|
+
application: "live",
|
|
184
|
+
defaultValue: "version,time",
|
|
185
|
+
allowedValues: TAB_BAR_RIGHT_ITEM_VALUES,
|
|
186
|
+
flags: TAB_BAR_RIGHT_ITEMS,
|
|
187
|
+
}),
|
|
172
188
|
tabsAutoName: Object.freeze({
|
|
173
189
|
id: "tabsAutoName",
|
|
174
190
|
label: "Auto name",
|
|
@@ -155,6 +155,13 @@ export const OWNED_UI_SETTINGS_MIGRATIONS = Object.freeze([
|
|
|
155
155
|
return { ...values };
|
|
156
156
|
},
|
|
157
157
|
}),
|
|
158
|
+
Object.freeze({
|
|
159
|
+
to: 20,
|
|
160
|
+
description: "Introduce the resident tab-bar Version and Time defaults.",
|
|
161
|
+
migrate(values) {
|
|
162
|
+
return { ...values };
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
158
165
|
]);
|
|
159
166
|
/** Shape-selected compatibility for the metadata-free product object. */
|
|
160
167
|
export function normalizeOwnedUiSettingsValues(source) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timurproko/a1",
|
|
3
|
-
"version": "0.2.7-dev.
|
|
3
|
+
"version": "0.2.7-dev.1239",
|
|
4
4
|
"description": "Standalone terminal workspace for supervised native and managed agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"privateLaunchContract": "neutral-launch-v1",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"@mariozechner/clipboard": "^0.3.9"
|
|
124
124
|
},
|
|
125
125
|
"publication": {
|
|
126
|
-
"source": "
|
|
127
|
-
"pullRequest":
|
|
126
|
+
"source": "19a43afd07e6df67b85be09199298621b4ffc023",
|
|
127
|
+
"pullRequest": 864
|
|
128
128
|
}
|
|
129
129
|
}
|