acryl-cli-linux-x64 0.1.31 → 0.1.35

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.
Files changed (28) hide show
  1. package/package.json +1 -1
  2. package/runtime/lib/bin.js +831 -220
  3. package/runtime/lib/index.js +831 -220
  4. package/runtime/lib/types/buildInfo.generated.d.ts +4 -0
  5. package/runtime/lib/types/tui/acrylMark.d.ts +14 -0
  6. package/runtime/lib/types/tui/actions.d.ts +8 -6
  7. package/runtime/lib/types/tui/auth-guidance.d.ts +0 -2
  8. package/runtime/lib/types/tui/login/LoginOverlay.d.ts +46 -11
  9. package/runtime/lib/types/tui/login/types.d.ts +5 -1
  10. package/runtime/lib/types/tui/modelProfile/ModelProfileOverlay.d.ts +55 -3
  11. package/runtime/lib/types/tui/modelProfile/types.d.ts +23 -0
  12. package/runtime/lib/types/tui/store.d.ts +10 -0
  13. package/runtime/node_modules/.modules.yaml +2 -2
  14. package/runtime/node_modules/.package-map.json +1 -1
  15. package/runtime/node_modules/@earendil-works/pi-ai/dist/auth/oauth/oauth-page.js +1 -1
  16. package/runtime/node_modules/acryl-control/package.json +1 -1
  17. package/runtime/node_modules/acryl-harness-runtime/lib/index.mjs +129 -2
  18. package/runtime/node_modules/acryl-harness-runtime/lib/types/acryl-home.d.ts +35 -0
  19. package/runtime/node_modules/acryl-harness-runtime/lib/types/index.d.ts +2 -0
  20. package/runtime/node_modules/acryl-harness-runtime/lib/types/session-bridge.d.ts +13 -0
  21. package/runtime/node_modules/acryl-harness-runtime/lib/types/session-log-exporter.d.ts +31 -0
  22. package/runtime/node_modules/acryl-harness-runtime/package.json +1 -1
  23. package/runtime/node_modules/acryl-harness-runtime/src/acryl-home.ts +50 -0
  24. package/runtime/node_modules/acryl-harness-runtime/src/index.ts +16 -0
  25. package/runtime/node_modules/acryl-harness-runtime/src/session-bridge.ts +24 -1
  26. package/runtime/node_modules/acryl-harness-runtime/src/session-log-exporter.ts +69 -0
  27. package/runtime/package.json +3 -1
  28. package/runtime/receipt.json +2 -2
@@ -0,0 +1,4 @@
1
+ /** Short git commit this build was produced from, or undefined outside a git checkout. */
2
+ export declare const BUILD_COMMIT: string | undefined;
3
+ /** ISO timestamp this build was produced at. */
4
+ export declare const BUILD_TIME: string;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The compact ACRYL wordmark shown in the fixed header, next to the pet.
3
+ *
4
+ * This is the real pixelized brand wordmark (`assets/pixelized/`), rendered
5
+ * natively as half-block characters (each terminal row packs 2 pixel rows
6
+ * via \u2580/\u2584/\u2588) instead of downsampled or replaced with a different mark \u2014 54
7
+ * columns wide at the artwork's own resolution, not simplified. Generated
8
+ * from the SVG's rect grid and verified by printing it before wiring into
9
+ * the header \u2014 same proven-before-embedding convention as
10
+ * `yly/compile-sprites.mjs`.
11
+ * @module @tomowave/dsh-tui/tui/acrylMark
12
+ */
13
+ /** The plain (uncolored) rows of the ACRYL mark \u2014 11 lines, 54 columns. */
14
+ export declare const ACRYL_MARK_ROWS: readonly string[];
@@ -42,26 +42,28 @@ export interface TuiActions {
42
42
  logout(): void;
43
43
  /** Close the `/login` overlay. */
44
44
  closeLogin(): void;
45
- /** Move the `/login` overlay's selection cursor. */
46
- selectLoginFlow(index: number): void;
47
- /** Run the authorization flow for the selected provider (pi-ai OAuth). */
48
- beginAuthorization(key: string): void;
45
+ /** Run the authorization flow for the selected provider (pi-ai OAuth). `method` defaults to the flow's first (its preferred) method when omitted. */
46
+ beginAuthorization(key: string, method?: string): void;
49
47
  /** Answer the in-flight authorization prompt (typed text/secret, or a chosen option id). */
50
48
  answerAuthorizationPrompt(value: string): void;
51
49
  /** Close the `/model` overlay, discarding any in-progress edit. */
52
50
  closeModelProfile(): void;
53
51
  /** Return from the add/edit form to the provider list without saving. */
54
52
  backToProviderList(): void;
55
- /** Move the provider list's selection cursor. */
56
- selectProvider(index: number): void;
57
53
  /** Open a blank draft for a new custom provider. */
58
54
  createProvider(): void;
55
+ /** Switch to `/model`'s custom-provider form directly, from wherever the caller is (e.g. `/login`'s provider list, which offers no custom-provider path of its own). */
56
+ addCustomProvider(): void;
59
57
  /** Open an existing provider's stored profile for editing. */
60
58
  editProvider(route: string): void;
59
+ /** Open `/model` and jump straight to editing this provider — the entry point `/login`'s provider list uses for an already-configured route, since it offers no edit/models path of its own. */
60
+ openProviderEditor(route: string): void;
61
61
  /** Persist a draft via `ctx.settings`/`ctx.credentials`, then reload the list. */
62
62
  saveProvider(draft: ProviderDraft): void;
63
63
  /** Remove a provider's settings section and credential. */
64
64
  deleteProvider(row: ProviderRow): void;
65
+ /** Remove only a draft's stored credential (both the `apiKeyEnv` reference and any `/login` record), keeping its settings profile (baseURL/api/models) intact. */
66
+ clearApiKey(draft: ProviderDraft): void;
65
67
  /** Probe a draft's endpoint via `ctx.llm.discoverModels`. */
66
68
  discoverModelsForDraft(draft: ProviderDraft): void;
67
69
  /** Save `{provider, model}` as the Agent's default model selection. */
@@ -6,5 +6,3 @@
6
6
  */
7
7
  export declare function loginHelp(): string;
8
8
  export declare function noApiKeyMessage(provider: string): string;
9
- export declare function logoutSuccessMessage(provider: string): string;
10
- export declare function logoutNoneMessage(): string;
@@ -1,27 +1,62 @@
1
1
  /**
2
- * `/login` overlay: the list of `ctx.authorization` sign-in flows (Anthropic,
3
- * OpenAI Codex, GitHub Copilot, …) registered by `dsh-llm-pi-ai`. Selecting one
4
- * and pressing enter runs its flow through `ctx.authorization.begin`, which
5
- * drives pi-ai's own OAuth strategies and persists the grant. While a flow is
6
- * running it may ask a question (a `select` account picker, a `secret` API-key
7
- * field, or a `text` manual-code field) the overlay renders that prompt
8
- * inline and routes keystrokes to it until the flow settles.
2
+ * `/login` overlay: a pi.dev-style two-step sign-in flow over the
3
+ * `ctx.authorization` flows (Anthropic, OpenAI Codex, GitHub Copilot, …)
4
+ * registered by `dsh-llm-pi-ai`. Step one asks "sign in with an account"
5
+ * (OAuth) vs "sign in with an API key"; step two shows the fuzzy-searchable
6
+ * provider list for that method, filtered from the same flow list step one
7
+ * partitioned. If every registered flow only offers one of the two method
8
+ * types, step one is skipped automatically (there's nothing to choose).
9
+ * Selecting a provider and pressing enter runs its flow through
10
+ * `ctx.authorization.begin`, which drives pi-ai's own OAuth/API-key
11
+ * strategies and persists the grant. While a flow is running it may ask a
12
+ * question (a `select` account picker, a `secret` API-key field, or a
13
+ * `text` manual-code field) — the overlay renders that prompt inline and
14
+ * routes keystrokes to it until the flow settles.
9
15
  * @module @tomowang/dsh-tui/tui/login/LoginOverlay
10
16
  */
11
- import type { Component } from '@earendil-works/pi-tui';
17
+ import type { Component, TUI } from '@earendil-works/pi-tui';
12
18
  import type { TuiActions } from '../actions.js';
13
19
  import type { TuiStore } from '../store.js';
20
+ /** A non-secret reminder that replacing an API-key prompt will overwrite an existing stored key. */
21
+ export declare function formatStoredApiKeyHint(preview: string | undefined): string | undefined;
14
22
  export declare class LoginOverlay implements Component {
23
+ private readonly tui;
15
24
  private readonly store;
16
25
  private readonly actions;
26
+ private step;
27
+ private authType;
28
+ private authTypeCursor;
29
+ private chooserSkipped;
30
+ private autoSkipChecked;
31
+ private listCursor;
32
+ private searchQuery;
17
33
  private promptField;
18
34
  private promptCursor;
19
- constructor(store: TuiStore, actions: TuiActions);
35
+ constructor(tui: TUI, store: TuiStore, actions: TuiActions);
20
36
  invalidate(): void;
21
- private renderList;
37
+ /** See `ModelProfileOverlay.listWindow` — same overflow bug, same fix: a long provider list (~35 catalog entries) must never push the key-legend hint line past the bottom of the terminal. */
38
+ private listWindow;
39
+ /** The `[start, end)` slice of `count` items to show so `selected` stays visible within `maxVisible` rows, biased to keep it centered. */
40
+ private visibleRange;
41
+ /**
42
+ * The global one-line notice (`store.setNotice`), rendered inline here.
43
+ * `/login` runs as a full-screen overlay that paints over the whole
44
+ * terminal, so the notice dock underneath — where sign-in progress,
45
+ * success ("Signed in to X."), and failure text otherwise land — is
46
+ * completely hidden for as long as this overlay stays open. Surfacing it
47
+ * here is what makes an OAuth callback's result actually visible instead
48
+ * of the screen appearing to do nothing once the browser redirects back.
49
+ */
50
+ private noticeLines;
51
+ /** Partition the loaded flows by method type, once, the first time they arrive; auto-advances past the chooser if only one type is on offer. */
52
+ private maybeAutoSkipChooser;
53
+ private filteredFlows;
54
+ private renderAuthTypeChooser;
55
+ private renderProviderList;
22
56
  private renderPrompt;
23
57
  render(_width: number): string[];
24
58
  handleInput(data: string): void;
25
59
  private handlePromptInput;
26
- private handleListInput;
60
+ private handleAuthTypeChooserInput;
61
+ private handleProviderListInput;
27
62
  }
@@ -18,6 +18,10 @@ export interface AuthorizationFlowRow {
18
18
  readonly methods: readonly AuthorizationMethodRow[];
19
19
  /** Whether an attempt is already running for this key. */
20
20
  readonly inFlight: boolean;
21
+ /** Whether a credential is already stored for this flow's key — signed in and ready to use. */
22
+ readonly configured: boolean;
23
+ /** How the stored credential was obtained, or `undefined` when `configured` is false. */
24
+ readonly authMethod: 'oauth' | 'api-key' | undefined;
21
25
  }
22
26
  /** One choice offered by a `select` authorization prompt. */
23
27
  export interface LoginPromptOption {
@@ -34,6 +38,7 @@ export type LoginPromptState = {
34
38
  readonly kind: 'secret';
35
39
  readonly message: string;
36
40
  readonly placeholder?: string;
41
+ readonly storedApiKeyPreview?: string;
37
42
  } | {
38
43
  readonly kind: 'select';
39
44
  readonly message: string;
@@ -43,7 +48,6 @@ export type LoginPromptState = {
43
48
  export interface LoginOverlayState {
44
49
  /** Joined flow list; `undefined` until the first load settles. */
45
50
  readonly flows: readonly AuthorizationFlowRow[] | undefined;
46
- readonly selected: number;
47
51
  /** The flow being signed into right now (spinner), or undefined. */
48
52
  readonly signingIn: string | undefined;
49
53
  /** The prompt the running flow is waiting on, or undefined outside a flow. */
@@ -11,13 +11,20 @@
11
11
  * — the direct equivalent of the old remount, just explicit.
12
12
  * @module @tomowang/dsh-tui/tui/modelProfile/ModelProfileOverlay
13
13
  */
14
- import type { Component } from '@earendil-works/pi-tui';
14
+ import type { Component, TUI } from '@earendil-works/pi-tui';
15
15
  import type { TuiActions } from '../actions.js';
16
16
  import type { TuiStore } from '../store.js';
17
17
  export declare class ModelProfileOverlay implements Component {
18
+ private readonly tui;
18
19
  private readonly store;
19
20
  private readonly actions;
21
+ private mode;
22
+ private modelSearch;
23
+ private modelPickerCursor;
24
+ private providerSearch;
25
+ private providerCursor;
20
26
  private confirmDelete;
27
+ private pendingShowModels;
21
28
  private formKeySeen;
22
29
  private route;
23
30
  private displayName;
@@ -29,18 +36,63 @@ export declare class ModelProfileOverlay implements Component {
29
36
  private focused;
30
37
  private modelDraftId;
31
38
  private modelSelected;
32
- private modelInputFocused;
33
- constructor(store: TuiStore, actions: TuiActions);
39
+ constructor(tui: TUI, store: TuiStore, actions: TuiActions);
34
40
  invalidate(): void;
41
+ /**
42
+ * Rows available for a scrollable list body: terminal height minus the
43
+ * lines every such screen spends on chrome (header/hint/notice/search —
44
+ * `chrome` lines, caller-counted since it varies by screen). Long catalogs
45
+ * (~35 providers, matching that many models) previously rendered every row
46
+ * unconditionally, pushing the key-legend hint line — the only place a
47
+ * shortcut is documented — past the bottom of the terminal, invisible
48
+ * without scrolling back. Every list-shaped view here windows around the
49
+ * current selection instead, so the hint line is always the last line
50
+ * printed and always fits on screen.
51
+ */
52
+ private listWindow;
53
+ /** The `[start, end)` slice of `count` items to show so `selected` stays visible within `maxVisible` rows, biased to keep it centered. */
54
+ private visibleRange;
55
+ /**
56
+ * The global one-line notice (`store.setNotice`), rendered inline here.
57
+ * `/model` runs as a full-screen overlay that paints over the whole
58
+ * terminal, so the notice dock underneath — where "Saved X."/"Active model
59
+ * set to X." otherwise lands — is invisible for as long as this overlay
60
+ * stays open (e.g. right after `saveProvider` returns to the list view
61
+ * without closing the overlay). Surfacing it here is what makes a save
62
+ * actually visible as "done" instead of the screen looking unchanged.
63
+ */
64
+ private noticeLines;
35
65
  private textFields;
36
66
  /** Reinitialize form-local state from the store's draft when `formKey` changes — the equivalent of the old `key={formKey}` remount. */
37
67
  private syncFormState;
38
68
  private buildDraft;
39
69
  render(_width: number): string[];
70
+ private pickerItems;
71
+ private filteredPickerItems;
72
+ /**
73
+ * pi.dev's `/model` picker (see Image #45): a flat, fuzzy-searchable list of
74
+ * every model on a live (actually usable) route, not a per-provider drill-down.
75
+ * Provider add/edit/delete moves to a secondary `p` mode instead of being the
76
+ * default, since picking a model to make active is the far more common action.
77
+ */
78
+ private renderModelPicker;
79
+ private handleModelPickerInput;
80
+ private filteredProviders;
40
81
  private renderList;
41
82
  private handleListInput;
42
83
  private renderForm;
43
84
  private handleFormInput;
85
+ /**
86
+ * No Tab-toggled focus, on purpose: a hidden "which of two things am I
87
+ * typing into right now" mode with no visible indicator (the earlier
88
+ * design) is exactly the kind of invisible-state bug already fixed
89
+ * elsewhere in this file. Instead the "Add id" field is *always* live —
90
+ * every printable key, paste, and left/right/backspace/delete/home/end
91
+ * goes there, full stop, since ↑/↓ are the only keys `miniTextFieldInput`
92
+ * never claims. That leaves ↑/↓ free to always drive the list selection
93
+ * (for removal) at the same time, with no mode switch and no key that can
94
+ * ever mean two different things depending on invisible state.
95
+ */
44
96
  private renderModelListEditor;
45
97
  private addModel;
46
98
  private handleModelListEditorInput;
@@ -5,6 +5,23 @@
5
5
  * draft one add/edit form works with before a save round-trips it back.
6
6
  * @module @tomowang/dsh-tui/tui/modelProfile/types
7
7
  */
8
+ /**
9
+ * The `apiKeyEnv` reference a provider route falls back to when its settings
10
+ * profile names none, e.g. `my-proxy` -> `MY_PROXY_API_KEY`. Shared between
11
+ * the overlay (recomputing it for a just-typed custom route before save) and
12
+ * the host wiring (deriving it for every already-configured row), so the
13
+ * naming rule lives in exactly one place.
14
+ */
15
+ export declare function deriveApiKeyRef(route: string): string;
16
+ /**
17
+ * A short, identifying (never full) preview of a stored secret — first 4 and
18
+ * last 4 characters, so the API-key field isn't blank-looking when a key is
19
+ * already set and the user can tell *which* key it is without ever seeing
20
+ * enough of it to be usable on its own. Short values (<= 10 chars, where that
21
+ * split would show most of the string anyway) mask everything but the first
22
+ * and last single character instead.
23
+ */
24
+ export declare function maskKeyPreview(value: string): string;
8
25
  /** One model entry within a provider's catalog. */
9
26
  export interface ModelEntry {
10
27
  readonly id: string;
@@ -41,6 +58,8 @@ export interface ProviderRow {
41
58
  readonly baseURL: string | undefined;
42
59
  readonly apiKeyRef: string;
43
60
  readonly apiKeyConfigured: boolean;
61
+ /** How the credential behind `apiKeyConfigured` was obtained, or `undefined` when none is configured. */
62
+ readonly authMethod: 'oauth' | 'api-key' | undefined;
44
63
  readonly models: readonly ModelEntry[];
45
64
  /** Settings revision this row was read at; replayed as `expectedRevision` on write. */
46
65
  readonly revision: number | undefined;
@@ -56,8 +75,12 @@ export interface ProviderDraft {
56
75
  readonly baseURL: string;
57
76
  readonly apiKeyRef: string;
58
77
  readonly apiKeyConfigured: boolean;
78
+ /** How the credential behind `apiKeyConfigured` was obtained, or `undefined` when none is configured. */
79
+ readonly authMethod: 'oauth' | 'api-key' | undefined;
59
80
  /** Local-only plaintext key entered in this session; empty means "keep current". */
60
81
  readonly apiKeyDraft: string;
82
+ /** First/last few characters of the currently-stored key (e.g. `sk-p…9sZ4`), so the field isn't blank-looking when one is already set; `undefined` when none is configured. */
83
+ readonly apiKeyPreview: string | undefined;
61
84
  readonly models: readonly ModelEntry[];
62
85
  readonly revision: number | undefined;
63
86
  }
@@ -151,6 +151,11 @@ export interface TuiState {
151
151
  readonly stats: StatsSnapshot;
152
152
  /** Current agent preset, or `undefined` when `ctx.agentPresets` isn't composed in this profile. */
153
153
  readonly preset: PresetState | undefined;
154
+ /** Live override of the session's active provider/model set via `/model`; `undefined` until the first switch, so the status bar falls back to the mount-time default. */
155
+ readonly activeModel: {
156
+ readonly provider: string;
157
+ readonly model: string;
158
+ } | undefined;
154
159
  /** The in-flight step's accumulated text, or `undefined` when nothing is currently streaming. */
155
160
  readonly streaming: StreamingState | undefined;
156
161
  /** Tool calls sent but not yet resolved by a `tool/result`, in call order. */
@@ -205,6 +210,11 @@ export declare class TuiStore {
205
210
  setTitle(title: string | null | undefined): void;
206
211
  setStats(stats: StatsSnapshot): void;
207
212
  setPreset(preset: PresetState | undefined): void;
213
+ /** Record a live `/model` switch so the status bar reflects it immediately. */
214
+ setActiveModel(activeModel: {
215
+ provider: string;
216
+ model: string;
217
+ }): void;
208
218
  private shellRunSeq;
209
219
  /** Begin one local shell-escape run; its output accumulates via `appendShellOutput` until `finishShellRun` settles it into the transcript. */
210
220
  startShellRun(command: string): number;
@@ -660,7 +660,7 @@
660
660
  "eventsource-parser@3.1.1": {
661
661
  "eventsource-parser": "private"
662
662
  },
663
- "@earendil-works/pi-ai@0.82.1(zod@4.4.3)": {
663
+ "@earendil-works/pi-ai@0.82.1(patch_hash=ef7e0dd0e86a1b21c8e64ec36c432a2a1512afb9819470c32a4cd19c322bdffe)(zod@4.4.3)": {
664
664
  "@earendil-works/pi-ai": "private"
665
665
  },
666
666
  "@modelcontextprotocol/sdk@1.30.0(zod@4.4.3)": {
@@ -1699,7 +1699,7 @@
1699
1699
  "packageManager": "pnpm@11.8.0",
1700
1700
  "pendingBuilds": [],
1701
1701
  "publicHoistPattern": [],
1702
- "prunedAt": "Wed, 02 Sep 2026 13:25:38 GMT",
1702
+ "prunedAt": "Tue, 08 Sep 2026 14:35:40 GMT",
1703
1703
  "registries": {
1704
1704
  "default": "https://registry.npmjs.org/",
1705
1705
  "@jsr": "https://npm.jsr.io/"