@byokit/ui 0.0.0-stage → 0.6.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.
@@ -0,0 +1,25 @@
1
+ export type StepState = 'todo' | 'checking' | 'ok' | 'failed';
2
+ /** One step as its caller reports it: `label` names the check ("Agent runner"), `detail` says how it stands
3
+ * ("ready", "not installed on this computer"), and `fix` is the one next action when it fails. Ids are the
4
+ * caller's own; nothing here knows any app's steps. */
5
+ export type StepInput = {
6
+ id: string;
7
+ state: StepState;
8
+ label: string;
9
+ detail?: string;
10
+ fix?: string;
11
+ };
12
+ /** One row to draw: `words` is the plain sentence, `fix` the next action ("" until the step fails). */
13
+ export type StepRow = {
14
+ id: string;
15
+ state: StepState;
16
+ words: string;
17
+ fix: string;
18
+ };
19
+ /** Turn the caller's steps into the rows to draw, in order. A failed step without its own `fix` says "Try again." */
20
+ export declare function stepsView(steps: StepInput[]): StepRow[];
21
+ /** The same rows as text lines for a terminal: `✓` done, `…` checking, `○` waiting, `✗` failed with its fix.
22
+ * A phone draws the rows itself; this keeps the terminal showing the same words. */
23
+ export declare function stepsText(rows: StepRow[], opts?: {
24
+ title?: string;
25
+ }): string;
package/dist/steps.js ADDED
@@ -0,0 +1,16 @@
1
+ /** Turn the caller's steps into the rows to draw, in order. A failed step without its own `fix` says "Try again." */
2
+ export function stepsView(steps) {
3
+ return steps.map((s) => ({
4
+ id: s.id,
5
+ state: s.state,
6
+ words: s.detail ? `${s.label}: ${s.detail}` : s.label,
7
+ fix: s.state === 'failed' ? (s.fix || 'Try again.') : '',
8
+ }));
9
+ }
10
+ const mark = { ok: '✓', checking: '…', todo: '○', failed: '✗' };
11
+ /** The same rows as text lines for a terminal: `✓` done, `…` checking, `○` waiting, `✗` failed with its fix.
12
+ * A phone draws the rows itself; this keeps the terminal showing the same words. */
13
+ export function stepsText(rows, opts) {
14
+ const lines = rows.map((r) => ` ${mark[r.state]} ${r.words}${r.state === 'failed' && r.fix ? ` ${r.fix}` : ''}`);
15
+ return opts?.title ? `${opts.title}\n${lines.join('\n')}` : lines.join('\n');
16
+ }
@@ -0,0 +1,18 @@
1
+ import { type ConnectAction, type ConnectRoute, type ConnectStep, type ConnectWords, type UseConnectSignIn } from './connect.ts';
2
+ export type UseConnect = UseConnectSignIn & {
3
+ routes: readonly ConnectRoute[];
4
+ words?: ConnectWords;
5
+ };
6
+ export declare function useConnect({ routes, words, ...o }: UseConnect): {
7
+ step: ConnectStep;
8
+ act: (a: ConnectAction) => void;
9
+ pick: (key: string) => void;
10
+ back: () => void;
11
+ /** Pass to `useSignIn` in a sheet keyed by `chosen.row.key`; undefined unless a ready sign-in row is chosen. */
12
+ signIn: import("./useSignIn.ts").UseSignIn | undefined;
13
+ groups: import("./connect.ts").ConnectGroup[];
14
+ chosen?: {
15
+ row: import("./connect.ts").ConnectRow;
16
+ key?: ReturnType<typeof import("./key.ts").keyView>;
17
+ };
18
+ };
@@ -0,0 +1,17 @@
1
+ // The connect view as a React hook: holds the step, recomputes the view from the routes the app passes, and hands
2
+ // the chosen sign-in row's options to `useSignIn`. Keys never enter it: the app sends the input to its kit and
3
+ // reports only the result.
4
+ import { useState } from 'react';
5
+ import { connectStep, connectView, signInFor } from "./connect.js";
6
+ export function useConnect({ routes, words, ...o }) {
7
+ const [step, setStep] = useState({ at: 'list' });
8
+ const view = connectView(routes, step, words);
9
+ const act = (a) => setStep((s) => connectStep(s, a));
10
+ return {
11
+ ...view, step, act,
12
+ pick: (key) => act({ type: 'pick', key }),
13
+ back: () => act({ type: 'back' }),
14
+ /** Pass to `useSignIn` in a sheet keyed by `chosen.row.key`; undefined unless a ready sign-in row is chosen. */
15
+ signIn: signInFor(view, o),
16
+ };
17
+ }
@@ -0,0 +1,62 @@
1
+ import { type Approval, type ApprovalsSource, type FollowOptions } from './approvals.ts';
2
+ import { type HerdrSource } from './herdr.ts';
3
+ import { type RunSource, type RunWordsOptions } from './run.ts';
4
+ /** One run at a time: `send(message)` streams the reply in; `words` says how a failed run ended. */
5
+ export declare function useRun(source: RunSource, o: RunWordsOptions): {
6
+ send: (message: string, o?: {
7
+ sessionKey?: string;
8
+ }) => void;
9
+ stop: () => void;
10
+ phase: import("./run.ts").RunPhase;
11
+ text: string;
12
+ tools: {
13
+ name: string;
14
+ done: boolean;
15
+ }[];
16
+ tool: string | undefined;
17
+ words: string;
18
+ };
19
+ /** The approvals waiting for a yes, live; `decide` answers one (it leaves the list when the computer says so). */
20
+ export declare function useApprovals(source: ApprovalsSource & {
21
+ decide(id: string, d: {
22
+ allow: boolean;
23
+ reason?: string;
24
+ answer?: unknown;
25
+ }): Promise<void>;
26
+ }, o?: FollowOptions): {
27
+ approvals: Approval[];
28
+ decide: (id: string, d: {
29
+ allow: boolean;
30
+ reason?: string;
31
+ answer?: unknown;
32
+ }) => Promise<void>;
33
+ };
34
+ /** Herdr's agents grouped by where they run, live; `agent(paneId)` finds one. `ready` once the computer has said. */
35
+ export declare function useHerdrTree(source: HerdrSource): {
36
+ ready: boolean;
37
+ tree: import("./herdr.ts").HerdrTree | null;
38
+ groups: {
39
+ workspaceId: string;
40
+ tabId: string;
41
+ project: string;
42
+ where: string;
43
+ agents: import("./herdr.ts").AgentRow[];
44
+ }[];
45
+ agent: (paneId: string) => import("./herdr.ts").AgentRow | undefined;
46
+ };
47
+ /** The agents waiting for an answer, live; `answer` sends keys to one, with the revision it was asked at. */
48
+ export declare function useBlocked(source: HerdrSource & {
49
+ answer(paneId: string, keys: string[], revision: number): Promise<void>;
50
+ }): {
51
+ blocked: {
52
+ name: string;
53
+ paneId: string;
54
+ workspaceId: string;
55
+ tabId: string;
56
+ kind?: string;
57
+ revision: number;
58
+ prompt: string;
59
+ since: number;
60
+ }[];
61
+ answer: (paneId: string, keys: string[], revision: number) => Promise<void>;
62
+ };
@@ -0,0 +1,30 @@
1
+ // The runtime kits' view state as React hooks (React and React Native alike). Each hook reads one of the
2
+ // framework-free stores from `@byokit/ui/kits`; pass the same device client each render (e.g. from useMemo),
3
+ // since a new one starts following afresh.
4
+ import { useEffect, useMemo, useSyncExternalStore } from 'react';
5
+ import { approvalsStore } from "./approvals.js";
6
+ import { agentIn, blockedView, herdrStore, herdrTreeView } from "./herdr.js";
7
+ import { runStore, runView } from "./run.js";
8
+ const useStore = (s) => useSyncExternalStore(s.subscribe, s.get, s.get);
9
+ /** One run at a time: `send(message)` streams the reply in; `words` says how a failed run ended. */
10
+ export function useRun(source, o) {
11
+ const run = useMemo(() => runStore(source), [source]);
12
+ useEffect(() => run.stop, [run]); // leaving the screen stops listening
13
+ const state = useStore(run);
14
+ return { ...runView(state, o), send: run.send, stop: run.stop };
15
+ }
16
+ /** The approvals waiting for a yes, live; `decide` answers one (it leaves the list when the computer says so). */
17
+ export function useApprovals(source, o) {
18
+ const approvals = useStore(useMemo(() => approvalsStore(source, o), [source]));
19
+ return { approvals, decide: (id, d) => source.decide(id, d) };
20
+ }
21
+ /** Herdr's agents grouped by where they run, live; `agent(paneId)` finds one. `ready` once the computer has said. */
22
+ export function useHerdrTree(source) {
23
+ const { tree } = useStore(herdrStore(source));
24
+ return { ready: tree !== null, tree, groups: useMemo(() => herdrTreeView(tree), [tree]), agent: (paneId) => agentIn(tree, paneId) };
25
+ }
26
+ /** The agents waiting for an answer, live; `answer` sends keys to one, with the revision it was asked at. */
27
+ export function useBlocked(source) {
28
+ const state = useStore(herdrStore(source));
29
+ return { blocked: useMemo(() => blockedView(state), [state]), answer: (paneId, keys, revision) => source.answer(paneId, keys, revision) };
30
+ }
@@ -0,0 +1,32 @@
1
+ import { type AccountView, type Phase } from './phase.ts';
2
+ export type UseSignIn = {
3
+ /** This account, fresh from the back end. A rejection counts as the home computer being out of reach unless `offline` says otherwise. */
4
+ read: () => Promise<AccountView | null>;
5
+ /** Begin the sign-in on the back end. Absent when something else starts it (a CLI in a Herdr tab): the sheet
6
+ * then only watches, and nothing begins as it opens. */
7
+ start?: (body?: {
8
+ via?: 'code';
9
+ fresh?: boolean;
10
+ }) => Promise<unknown>;
11
+ cancel: () => Promise<unknown>;
12
+ offline?: (e: unknown) => boolean;
13
+ ms?: number;
14
+ /** Draw one phase regardless (design review, screenshots): nothing starts and nothing polls. */
15
+ pinned?: Phase | null;
16
+ };
17
+ export declare function useSignIn({ read, start, cancel, offline: isOffline, ms, pinned }: UseSignIn): {
18
+ phase: Phase;
19
+ account: AccountView | null;
20
+ code: string | undefined;
21
+ url: string | undefined;
22
+ /** Start again: `{ via: 'code' }` for "Having trouble?", `{ fresh: true }` to pick the account again. */
23
+ start: (body?: {
24
+ via?: "code";
25
+ fresh?: boolean;
26
+ }) => void;
27
+ cancel: () => void;
28
+ /** "Keep this one": a work plan the person chose to use anyway. */
29
+ keepWork: () => void;
30
+ /** Closing the sheet mid-way cancels the sign-in too. */
31
+ close: () => void;
32
+ };
@@ -0,0 +1,38 @@
1
+ // "Sign in with …" as a headless React hook: starts the sign-in as the sheet opens, polls the app's back end, and
2
+ // reports the phase to draw. The app keeps its own look (and its own tab for the provider's page); this is the
3
+ // behaviour every sign-in sheet shares.
4
+ import { useEffect, useState } from 'react';
5
+ import { phaseOf } from "./phase.js";
6
+ export function useSignIn({ read, start, cancel, offline: isOffline = () => true, ms = 1500, pinned }) {
7
+ const [value, setValue] = useState(null);
8
+ const [offline, setOffline] = useState(false);
9
+ const [cancelled, setCancelled] = useState(false);
10
+ const [keepWork, setKeepWork] = useState(false);
11
+ useEffect(() => {
12
+ if (pinned)
13
+ return;
14
+ let alive = true;
15
+ const pull = () => read().then((v) => { if (alive) {
16
+ setValue(v);
17
+ setOffline(false);
18
+ } }, (e) => alive && isOffline(e) && setOffline(true));
19
+ pull();
20
+ const t = setInterval(pull, ms);
21
+ return () => { alive = false; clearInterval(t); };
22
+ }, [ms, pinned]);
23
+ const begin = (body = {}) => { setCancelled(false); start?.(body).catch(() => { }); };
24
+ useEffect(() => { if (!pinned && start)
25
+ begin(); }, []);
26
+ const phase = pinned ?? phaseOf(value, { offline, cancelled, keepWork });
27
+ return {
28
+ phase, account: value, code: value?.signIn?.code, url: value?.signIn?.url,
29
+ /** Start again: `{ via: 'code' }` for "Having trouble?", `{ fresh: true }` to pick the account again. */
30
+ start: begin,
31
+ cancel: () => { setCancelled(true); cancel().catch(() => { }); },
32
+ /** "Keep this one": a work plan the person chose to use anyway. */
33
+ keepWork: () => setKeepWork(true),
34
+ /** Closing the sheet mid-way cancels the sign-in too. */
35
+ close: () => { if (phase === 'waiting' || phase === 'opening' || phase === 'code')
36
+ cancel().catch(() => { }); },
37
+ };
38
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "connect.plans": "Plans",
3
+ "connect.plans.note": "Uses a plan you already pay for.",
4
+ "connect.perUse": "Pay per use",
5
+ "connect.perUse.note": "Charged each time you use it. Used only when you pick it.",
6
+ "connect.local": "On this computer",
7
+ "connect.local.note": "Runs on this computer. Used only when you pick it.",
8
+ "connect.server": "Your own server",
9
+ "connect.server.note": "A server you run. You say how it is paid for. Used only when you pick it.",
10
+ "connect.cloud": "Cloud account",
11
+ "connect.cloud.note": "Charged to your cloud account. Used only when you pick it.",
12
+ "connect.other": "Other ways to connect",
13
+ "connect.other.note": "How these are paid for is set by each company. Used only when you pick one.",
14
+ "connect.services": "Speech, search and media",
15
+ "connect.services.note": "Services other than chat. Used only when you pick one.",
16
+ "billing.subscription": "Uses your {name} plan",
17
+ "billing.api": "Charged per use to your {company} account",
18
+ "billing.local": "Runs on this computer",
19
+ "billing.server": "Your own server",
20
+ "billing.free": "Free tier from {company}",
21
+ "billing.unknown": "Billing set by {company}",
22
+ "via.browser": "Sign in on their page",
23
+ "via.code": "Sign in with a code",
24
+ "via.paste": "Sign in, then paste the code back",
25
+ "via.session": "Sign in",
26
+ "via.setup_token": "Paste a setup code from your plan",
27
+ "via.cli": "Sign in inside its own app",
28
+ "via.key": "Paste a key",
29
+ "via.plan_key": "Paste the key from your plan",
30
+ "via.cloud": "Use your cloud account",
31
+ "via.local": "Use a model on this computer",
32
+ "via.endpoint": "Use your own server's address",
33
+ "ready.ready": "Ready",
34
+ "ready.needs_binary": "Install its app on this computer first.",
35
+ "ready.needs_plugin": "Add its add-on first.",
36
+ "ready.needs_host": "Needs the app on your computer.",
37
+ "ready.needs_client": "Not set up for this app yet.",
38
+ "ready.unsupported_platform": "Not available on this device.",
39
+ "ready.no_upstream_flow": "This way to sign in is not available yet.",
40
+ "key.label.plan": "Key from your plan",
41
+ "key.label.perUse": "Key (charged per use)",
42
+ "key.label.other": "Key",
43
+ "key.entry": "Paste your key. It stays on this device.",
44
+ "key.checking": "Checking your key…",
45
+ "key.ok": "Your key is ready. It is used only when you choose this option.",
46
+ "key.invalid": "This key could not be saved. Check it and try again.",
47
+ "key.notIncluded": "This option is not available here."
48
+ }
package/package.json CHANGED
@@ -1,6 +1,70 @@
1
1
  {
2
2
  "name": "@byokit/ui",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.6.0",
4
+ "description": "Headless sign-in, pairing and runtime-kit view state for any UI, plus plain-words copy.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/umeranjum17/byokit.git",
10
+ "directory": "packages/ui"
11
+ },
12
+ "engines": {
13
+ "node": ">=22.18"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "./phase": {
21
+ "types": "./dist/phase.d.ts",
22
+ "default": "./dist/phase.js"
23
+ },
24
+ "./route": {
25
+ "types": "./dist/route.d.ts",
26
+ "default": "./dist/route.js"
27
+ },
28
+ "./link": {
29
+ "types": "./dist/link.d.ts",
30
+ "default": "./dist/link.js"
31
+ },
32
+ "./kits": {
33
+ "types": "./dist/kits.d.ts",
34
+ "default": "./dist/kits.js"
35
+ },
36
+ "./steps": {
37
+ "types": "./dist/steps.d.ts",
38
+ "default": "./dist/steps.js"
39
+ },
40
+ "./connect": {
41
+ "types": "./dist/connect.d.ts",
42
+ "default": "./dist/connect.js"
43
+ }
44
+ },
45
+ "files": [
46
+ "dist",
47
+ "CHANGELOG.md"
48
+ ],
49
+ "scripts": {
50
+ "prepack": "tsc -b"
51
+ },
52
+ "peerDependencies": {
53
+ "react": ">=18"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "react": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "devDependencies": {
61
+ "@types/react": "^19.3.0",
62
+ "react": "^19.3.0"
63
+ },
64
+ "publishConfig": {
65
+ "access": "public"
66
+ },
67
+ "dependencies": {
68
+ "uqr": "0.1.3"
69
+ }
70
+ }