@mind-harness/mh-browser 0.2.5 → 0.2.6

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.
@@ -0,0 +1,142 @@
1
+ import { type ReactElement, type ReactNode } from 'react';
2
+ /**
3
+ * A staged settings form drawn by this plugin. It follows the host's settings
4
+ * form: edits stay local until Save, leaving the page drops them, and a field
5
+ * is overridden while the user layer carries it. Styles use host theme tokens
6
+ * only, so the page matches the host without loading its components.
7
+ */
8
+ /** What `ctx.configForms.get()` serves for one Host entry. */
9
+ export interface SettingsFormScopeSnapshot<T> {
10
+ status: 'loading' | 'ready' | 'unavailable';
11
+ value: T | undefined;
12
+ /** The layer a cleared field falls back to. */
13
+ base: unknown;
14
+ /** The user layer as stored. A field present here is overridden. */
15
+ user: unknown;
16
+ writable: boolean;
17
+ revision: number | undefined;
18
+ }
19
+ export type SettingsFormPathOp = {
20
+ op: 'set';
21
+ path: readonly string[];
22
+ value: unknown;
23
+ } | {
24
+ op: 'unset';
25
+ path: readonly string[];
26
+ };
27
+ export interface SettingsFormScope<T> {
28
+ getSnapshot(): SettingsFormScopeSnapshot<T>;
29
+ subscribe(listener: () => void): () => void;
30
+ /** One revision-fenced write. Resolves to whether the Host accepted it. */
31
+ mutate(ops: readonly SettingsFormPathOp[], expectedRevision?: number): Promise<boolean>;
32
+ }
33
+ export type FieldWrite = {
34
+ kind: 'set';
35
+ value: unknown;
36
+ } | {
37
+ kind: 'clear';
38
+ };
39
+ export interface FieldSpec {
40
+ field: string;
41
+ /** Stored value as draft text. */
42
+ format: (value: unknown) => string;
43
+ /** The write a draft stages. Undefined blocks the save. */
44
+ parse: (text: string) => FieldWrite | undefined;
45
+ }
46
+ export interface FieldState {
47
+ text: string;
48
+ /** Saving would leave this field in the user layer. */
49
+ overridden: boolean;
50
+ invalid: boolean;
51
+ }
52
+ export interface FormShell {
53
+ available: boolean;
54
+ writable: boolean;
55
+ dirty: boolean;
56
+ invalid: boolean;
57
+ saving: boolean;
58
+ /** The last save did not land. Cleared by the next edit or save. */
59
+ failed: boolean;
60
+ }
61
+ export interface FormLabels {
62
+ unavailable: string;
63
+ readOnly: string;
64
+ saveFailed: string;
65
+ discard: string;
66
+ save: string;
67
+ saving: string;
68
+ }
69
+ export interface Store<S> {
70
+ getSnapshot(): S;
71
+ subscribe(listener: () => void): () => void;
72
+ }
73
+ /** Drafts over one entry's fields, written together on save. */
74
+ export declare class StagedForm<T> {
75
+ private readonly scope;
76
+ private readonly specs;
77
+ private readonly staged;
78
+ private readonly listeners;
79
+ private baseline;
80
+ private saving;
81
+ private failed;
82
+ private readonly unsubscribe;
83
+ constructor(scope: SettingsFormScope<T>, specs: readonly FieldSpec[]);
84
+ /** A store over `project`, rebuilt whenever the entry or a draft changes. */
85
+ bind<S>(project: () => S): Store<S>;
86
+ shell(): FormShell;
87
+ field(field: string): FieldState;
88
+ edit(field: string, text: string): void;
89
+ /** Stage removing the field from the user layer. The draft shows what it falls back to. */
90
+ reset(field: string): void;
91
+ discard(): void;
92
+ /**
93
+ * Write every staged edit. A save the Host refuses keeps the drafts, and the
94
+ * next save is fenced on what the Host holds now, so trying again can land.
95
+ */
96
+ save(): Promise<void>;
97
+ dispose(): void;
98
+ private plan;
99
+ private stage;
100
+ private spec;
101
+ private read;
102
+ private stored;
103
+ private publish;
104
+ }
105
+ /** Free text. An empty draft clears the field. */
106
+ export declare function textField(field: string): FieldSpec;
107
+ /** The page frame: read-only notice, the fields, and Save. Unmounting drops every draft. */
108
+ export declare function SettingsFrame(props: {
109
+ labels: FormLabels;
110
+ state: FormShell;
111
+ onSave: () => void;
112
+ onDiscard: () => void;
113
+ children?: ReactNode;
114
+ }): ReactElement;
115
+ export interface FieldProps extends FieldState {
116
+ id: string;
117
+ label: string;
118
+ hint?: string;
119
+ overriddenLabel: string;
120
+ resetLabel: string;
121
+ /** Shown instead of the hint while the draft is invalid. A field that cannot be invalid omits it. */
122
+ invalidLabel?: string;
123
+ disabled: boolean;
124
+ /** Text behind the information button beside the label. */
125
+ help?: {
126
+ label: string;
127
+ content: ReactNode;
128
+ };
129
+ onReset: () => void;
130
+ }
131
+ export declare function TextField(props: FieldProps & {
132
+ placeholder?: string;
133
+ onEdit: (text: string) => void;
134
+ }): ReactElement;
135
+ export declare function ChoiceField(props: FieldProps & {
136
+ options: readonly {
137
+ value: string;
138
+ label: string;
139
+ }[];
140
+ onEdit: (value: string) => void;
141
+ }): ReactElement;
142
+ //# sourceMappingURL=settings-form.d.ts.map
@@ -0,0 +1,121 @@
1
+ import { type ReactElement } from 'react';
2
+ import { type Translate } from './locales.ts';
3
+ import { type FieldState, type FieldWrite, type FormShell, type SettingsFormScope } from './settings-form.ts';
4
+ /** Loader row id, and the settings namespace for that row. */
5
+ export declare const ENTRY_ID = "mh-browser";
6
+ /** `plugins.row.config` key: the bundle package, then the row id. */
7
+ export declare const ROW_KEY = "@mind-harness/mh-bundle#mh-browser";
8
+ export declare const BROWSER_CHOICES: readonly ["chrome", "msedge", "chromium"];
9
+ export type BrowserChoice = (typeof BROWSER_CHOICES)[number];
10
+ /** Matches the host's `BrowserLocation`. The client bundle cannot import the host file. */
11
+ export type BrowserLocation = {
12
+ found: string;
13
+ } | {
14
+ missing: {
15
+ path: string;
16
+ reason: string;
17
+ }[];
18
+ };
19
+ export type LocateResult = {
20
+ ok: true;
21
+ value: BrowserLocation;
22
+ } | {
23
+ ok: false;
24
+ error: {
25
+ code: string;
26
+ message: string;
27
+ };
28
+ };
29
+ export type LocateFor = (browser: BrowserChoice, signal?: AbortSignal) => Promise<LocateResult>;
30
+ export type LookupView = {
31
+ status: 'loading';
32
+ } | {
33
+ status: 'found';
34
+ path: string;
35
+ } | {
36
+ status: 'missing';
37
+ tried: {
38
+ path: string;
39
+ reason: string;
40
+ }[];
41
+ } | {
42
+ status: 'failed';
43
+ };
44
+ /** The host entry's config. Only these two fields are edited here. */
45
+ export interface BrowserSettingsConfig {
46
+ browser?: string;
47
+ executablePath?: string;
48
+ }
49
+ interface BrowserState extends FormShell {
50
+ browser: FieldState;
51
+ executablePath: FieldState;
52
+ }
53
+ interface ConfigForms {
54
+ get<T>(entryId: string): SettingsFormScope<T>;
55
+ }
56
+ export interface SettingsHost {
57
+ slots: {
58
+ inject(name: string, register: () => unknown): unknown;
59
+ register(options: {
60
+ name: string;
61
+ key: string;
62
+ locale: string;
63
+ }, component: (props: {
64
+ t: Translate;
65
+ view?: 'summary' | 'page';
66
+ }) => ReactElement | null): () => void;
67
+ };
68
+ configForms: ConfigForms;
69
+ remote: {
70
+ mhBrowser: {
71
+ locateFor: LocateFor;
72
+ };
73
+ };
74
+ effect(execute: () => () => void): unknown;
75
+ }
76
+ /** Empty text clears the field. Anything else must be absolute, or start with `~`. */
77
+ export declare function parseExecutable(text: string): FieldWrite | undefined;
78
+ /** Placeholder for an empty path field: the path an empty value would use. */
79
+ export declare function pathPlaceholder(lookup: LookupView, browser: BrowserChoice, t: Translate): string | undefined;
80
+ /** The full path, or every place that was tried. Absent while looking or after a failed lookup. */
81
+ export declare function pathHelpText(lookup: LookupView, t: Translate): string | undefined;
82
+ /**
83
+ * One browser name's lookup, kept until the settings page unmounts.
84
+ * A newer lookup cancels the one still running.
85
+ */
86
+ export declare class ExecutableLookup {
87
+ private readonly locate;
88
+ private readonly cache;
89
+ private inflight;
90
+ private controller;
91
+ private readonly listeners;
92
+ constructor(locate: LocateFor);
93
+ subscribe: (listener: () => void) => (() => void);
94
+ /** Cached answer for `browser`, or loading when that answer is not in yet. */
95
+ view(browser: BrowserChoice): LookupView;
96
+ watch(browser: BrowserChoice): void;
97
+ dispose(): void;
98
+ private publish;
99
+ }
100
+ /** Staged browser choice and path. A save the host accepts restarts the browser when it is idle. */
101
+ export declare class BrowserForm {
102
+ private readonly form;
103
+ readonly lookup: ExecutableLookup;
104
+ readonly state: {
105
+ getSnapshot(): BrowserState;
106
+ subscribe(listener: () => void): () => void;
107
+ };
108
+ constructor(scope: SettingsFormScope<BrowserSettingsConfig>, locate: LocateFor);
109
+ edit(field: 'browser' | 'executablePath', text: string): void;
110
+ reset(field: 'browser' | 'executablePath'): void;
111
+ save(): void;
112
+ discard(): void;
113
+ dispose(): void;
114
+ }
115
+ export declare function registerSettings(ctx: SettingsHost): void;
116
+ export declare function BrowserSettings(props: {
117
+ t: Translate;
118
+ form: BrowserForm;
119
+ }): ReactElement;
120
+ export {};
121
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1,49 @@
1
+ import type { Volatile } from '@deepseek-ai/cordis';
2
+ import z from '@deepseek-ai/schemastery';
3
+ interface Full {
4
+ presence: {
5
+ maxBytes: number;
6
+ };
7
+ browser: Volatile<'chrome' | 'msedge' | 'chromium'>;
8
+ executablePath: Volatile<string | undefined>;
9
+ profileDir?: string;
10
+ headless: boolean;
11
+ }
12
+ export declare const full: z<Full>;
13
+ declare const oldShape: z<Schemastery.ObjectS<NoInfer<{
14
+ browser: z<"chrome" | "msedge" | "chromium", "chrome" | "msedge" | "chromium", "defined">;
15
+ profileDir: z<string, string, "plain">;
16
+ headless: z<boolean, boolean, "defined">;
17
+ }>>, Schemastery.ObjectT<NoInfer<{
18
+ browser: z<"chrome" | "msedge" | "chromium", "chrome" | "msedge" | "chromium", "defined">;
19
+ profileDir: z<string, string, "plain">;
20
+ headless: z<boolean, boolean, "defined">;
21
+ }>>, "plain">;
22
+ declare const newShape: z<Schemastery.ObjectS<NoInfer<{
23
+ browser: z<"chrome" | "msedge" | "chromium", "chrome" | "msedge" | "chromium", "volatile-defined">;
24
+ executablePath: z<string, string, "volatile">;
25
+ profileDir: z<string, string, "plain">;
26
+ headless: z<boolean, boolean, "defined">;
27
+ }>>, Schemastery.ObjectT<NoInfer<{
28
+ browser: z<"chrome" | "msedge" | "chromium", "chrome" | "msedge" | "chromium", "volatile-defined">;
29
+ executablePath: z<string, string, "volatile">;
30
+ profileDir: z<string, string, "plain">;
31
+ headless: z<boolean, boolean, "defined">;
32
+ }>>, "plain">;
33
+ type Old = ReturnType<typeof oldShape>;
34
+ type New = ReturnType<typeof newShape>;
35
+ export type Probe = {
36
+ oldProfile: Old['profileDir'];
37
+ oldBrowser: Old['browser'];
38
+ newProfile: New['profileDir'];
39
+ newBrowser: New['browser'];
40
+ newPath: New['executablePath'];
41
+ oldKeys: keyof Old;
42
+ newKeys: keyof New;
43
+ };
44
+ export declare const showOldProfile: Old['profileDir'];
45
+ export declare const showNewProfile: New['profileDir'];
46
+ export declare const showNewPath: New['executablePath'];
47
+ export declare const showNewBrowser: New['browser'];
48
+ export {};
49
+ //# sourceMappingURL=typeprobe.d.ts.map
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@mind-harness/mh-browser",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Opens pages in a real browser for the agent, keeps chosen Agent Views in context, and hands the browser to you when a person is needed.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "files": [
7
7
  "lib/index.js",
8
8
  "lib/types/representation/agent-view.js",
9
+ "lib/types/location.js",
9
10
  "lib/types/**/*.d.ts",
10
11
  "lib/typert.host.js",
11
12
  "lib/typert.host.d.ts",
@@ -29,6 +30,10 @@
29
30
  "types": "./lib/types/representation/agent-view.d.ts",
30
31
  "default": "./lib/types/representation/agent-view.js"
31
32
  },
33
+ "./location": {
34
+ "types": "./lib/types/location.d.ts",
35
+ "default": "./lib/types/location.js"
36
+ },
32
37
  "./typert": {
33
38
  "types": "./lib/typert.host.d.ts",
34
39
  "default": "./lib/typert.host.js"
@@ -60,6 +65,7 @@
60
65
  "@deepseek-ai/dsh-agent-loop-testkit": "^0.2.2-rc.1",
61
66
  "@deepseek-ai/dsh-attachment": "^0.2.2-rc.1",
62
67
  "@deepseek-ai/dsh-browser-use": "^0.2.2-rc.1",
68
+ "@deepseek-ai/dsh-client-ui-settings": "^0.2.2-rc.1",
63
69
  "@deepseek-ai/dsh-home-paths": "^0.2.2-rc.1",
64
70
  "@deepseek-ai/dsh-llm": "^0.2.2-rc.1",
65
71
  "@deepseek-ai/dsh-llm-mock-server": "^0.2.2-rc.1",
@@ -77,6 +83,7 @@
77
83
  },
78
84
  "peerDependencies": {
79
85
  "@deepseek-ai/cordis": "^4.1.0",
86
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.2.2-rc.1 <0.2.3-0",
80
87
  "@deepseek-ai/dsh-home-paths": ">=0.2.2-rc.1 <0.2.3-0",
81
88
  "@deepseek-ai/dsh-llm": ">=0.2.2-rc.1 <0.2.3-0",
82
89
  "@deepseek-ai/dsh-tools": ">=0.2.2-rc.1 <0.2.3-0",
@@ -94,7 +101,8 @@
94
101
  "platform": "web",
95
102
  "inject": [
96
103
  "@deepseek-ai/dsh-client-locale",
97
- "@deepseek-ai/dsh-client-ui-conversation"
104
+ "@deepseek-ai/dsh-client-ui-conversation",
105
+ "@deepseek-ai/dsh-client-ui-settings"
98
106
  ]
99
107
  }
100
108
  },