@sigrun/tui 0.8.0 → 0.10.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 +12 -0
- package/dist/actions.d.ts +6 -0
- package/dist/api.d.ts +8 -2
- package/dist/api.js +8 -0
- package/dist/blocks/ext-transport.d.ts +15 -7
- package/dist/blocks/ext-transport.js +32 -15
- package/dist/blocks/form/form.js +22 -0
- package/dist/blocks/surface.d.ts +12 -2
- package/dist/blocks/surface.js +26 -3
- package/dist/keymaps/emacs.js +5 -0
- package/dist/keymaps/standard.js +6 -0
- package/dist/keymaps/vim.js +5 -0
- package/dist/ui/app.d.ts +4 -0
- package/dist/ui/app.js +81 -9
- package/dist/ui/interactions.d.ts +12 -0
- package/dist/ui/interactions.js +79 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
## [0.10.0] - 2026-11-06
|
|
10
|
+
|
|
11
|
+
- Extension views render in the main pane (mount, key routing, overlay-aware Esc
|
|
12
|
+
back-to-list/close, dispose) with block navigation (`SurfaceState.onNavigate`), notice
|
|
13
|
+
flashes on the status bar, and the ext-transport URL fix (suffix routes after `?params=`).
|
|
14
|
+
|
|
15
|
+
## [0.9.0] - 2026-11-05
|
|
16
|
+
|
|
17
|
+
- Decline on declinable interaction cards (`alt+s` in answer mode; `alt+d` is the
|
|
18
|
+
notices dismiss), the armed-hint chord, and the stepper review fixes (answer
|
|
19
|
+
labels, "Not answered yet" naming, Enter guard).
|
|
20
|
+
|
|
9
21
|
## [0.8.0] - 2026-11-04
|
|
10
22
|
|
|
11
23
|
- Notices: an inline line for the current conversation's newest notice (a key dismisses
|
package/dist/actions.d.ts
CHANGED
|
@@ -63,6 +63,12 @@ export type Action = {
|
|
|
63
63
|
readonly reverse: boolean;
|
|
64
64
|
} | {
|
|
65
65
|
readonly type: "answer-interaction";
|
|
66
|
+
}
|
|
67
|
+
/** Ask-user Task 5 (spec §5.2): decline the armed interaction record
|
|
68
|
+
* (alt+s in every keymap — alt+d is the notices dismiss). Acts ONLY
|
|
69
|
+
* in answer mode on a declinable record. */
|
|
70
|
+
| {
|
|
71
|
+
readonly type: "decline-interaction";
|
|
66
72
|
} | {
|
|
67
73
|
readonly type: "cancel-run";
|
|
68
74
|
} | {
|
package/dist/api.d.ts
CHANGED
|
@@ -21,8 +21,8 @@ import type { NoticeActionResponse, NoticesResponse } from "@sigrun/shared/notic
|
|
|
21
21
|
import type { JobsResponse, JobRunResponse } from "@sigrun/shared/views";
|
|
22
22
|
import type { JobRevisionWire, JobRunWire } from "@sigrun/shared/jobs";
|
|
23
23
|
import type { SessionMeta, SessionMetaPatch } from "@sigrun/shared/session-meta";
|
|
24
|
-
import type { AbortResponse, AmbientList, CompactResponse, ConfigureResponse, ConversationCreateResponse, ConversationForkResponse, ConversationList, ConversationViewWire, InteractionAnswered, InteractionList, InteractionWire, MetaResponse, ModelsResponse, SettingsResponse, SettingsSectionsResponse, SubmissionResponse, WhoamiResponse } from "@sigrun/shared/views";
|
|
25
|
-
export type { AbortResponse, AmbientList, AmbientWire, CompactResponse, ConfigureResponse, ConversationCreateResponse, ConversationForkResponse, ConversationList, ConversationSummary, ConversationViewWire, InteractionAnswered, InteractionList, InteractionWire, MetaResponse, ModelEntry, ModelsResponse, SettingsResponse, SettingsSectionsResponse, SettingsSectionEntry, SettingsSectionIndexEntry, SubmissionResponse, WhoamiResponse, } from "@sigrun/shared/views";
|
|
24
|
+
import type { AbortResponse, AmbientList, CompactResponse, ConfigureResponse, ConversationCreateResponse, ConversationForkResponse, ConversationList, ConversationViewWire, InteractionAnswered, InteractionDeclined, InteractionList, InteractionWire, MetaResponse, ModelsResponse, SettingsResponse, SettingsSectionsResponse, SubmissionResponse, WhoamiResponse } from "@sigrun/shared/views";
|
|
25
|
+
export type { AbortResponse, AmbientList, AmbientWire, CompactResponse, ConfigureResponse, ConversationCreateResponse, ConversationForkResponse, ConversationList, ConversationSummary, ConversationViewWire, InteractionAnswered, InteractionDeclined, InteractionList, InteractionWire, MetaResponse, ModelEntry, ModelsResponse, SettingsResponse, SettingsSectionsResponse, SettingsSectionEntry, SettingsSectionIndexEntry, SubmissionResponse, WhoamiResponse, } from "@sigrun/shared/views";
|
|
26
26
|
export type { CommandRunResponse, CommandsResponse, SlashCommand } from "@sigrun/shared/commands";
|
|
27
27
|
export type { JobSpec, JobSchedule, JobAction, JobWire, JobRunWire, JobRevisionWire, } from "@sigrun/shared/jobs";
|
|
28
28
|
export type { JobsResponse, JobRunResponse } from "@sigrun/shared/views";
|
|
@@ -151,6 +151,12 @@ export declare class SigrunClient {
|
|
|
151
151
|
getInteraction(id: number, callId: string): Promise<InteractionWire>;
|
|
152
152
|
/** `POST .../interactions/:callId/answer` — the ONE generic answer route. */
|
|
153
153
|
answerInteraction(id: number, callId: string, answer: InteractionAnswerInput): Promise<InteractionAnswered>;
|
|
154
|
+
/** `POST .../interactions/:callId/decline` — decline a pending
|
|
155
|
+
* declinable record (ask-user spec §5.2; settles `skipped` with
|
|
156
|
+
* `declinedVia: "button"`). A 404 `not_pending` means the record
|
|
157
|
+
* settled under the user's fingers — the caller refetches through
|
|
158
|
+
* {@link getInteraction} (the mid-stepper settle path). */
|
|
159
|
+
declineInteraction(id: number, callId: string): Promise<InteractionDeclined>;
|
|
154
160
|
/** `GET /v1/conversations/:id/ambient`. */
|
|
155
161
|
getAmbient(id: number): Promise<AmbientList>;
|
|
156
162
|
/** `GET /v1/commands` — the slash-command catalog (a boot constant). */
|
package/dist/api.js
CHANGED
|
@@ -181,6 +181,14 @@ export class SigrunClient {
|
|
|
181
181
|
answerInteraction(id, callId, answer) {
|
|
182
182
|
return this.#send("POST", `/v1/conversations/${id}/interactions/${encodeURIComponent(callId)}/answer`, answer);
|
|
183
183
|
}
|
|
184
|
+
/** `POST .../interactions/:callId/decline` — decline a pending
|
|
185
|
+
* declinable record (ask-user spec §5.2; settles `skipped` with
|
|
186
|
+
* `declinedVia: "button"`). A 404 `not_pending` means the record
|
|
187
|
+
* settled under the user's fingers — the caller refetches through
|
|
188
|
+
* {@link getInteraction} (the mid-stepper settle path). */
|
|
189
|
+
declineInteraction(id, callId) {
|
|
190
|
+
return this.#send("POST", `/v1/conversations/${id}/interactions/${encodeURIComponent(callId)}/decline`, {});
|
|
191
|
+
}
|
|
184
192
|
/** `GET /v1/conversations/:id/ambient`. */
|
|
185
193
|
getAmbient(id) {
|
|
186
194
|
return this.#get(`/v1/conversations/${id}/ambient`);
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* The TUI's extension-surface transport (UI-blocks U3 Task 5): implements
|
|
3
3
|
* the shared `SurfaceTransport` contract over the U3 routes (spec §7.2)
|
|
4
4
|
* as the TUI's remote client. One transport = one surface instance
|
|
5
|
-
* (slug + id + params); the
|
|
6
|
-
*
|
|
5
|
+
* (slug + id + params); the hub routes change events by (slug, id) —
|
|
6
|
+
* the wire `k` is not matched (see ExtSurfaceHub).
|
|
7
7
|
*
|
|
8
8
|
* Like the webui twin, every fetch carries `X-Sigrun-Stream` once the
|
|
9
9
|
* launcher hands the stream id in (the watcher entry's stream);
|
|
@@ -52,14 +52,22 @@ export declare function createExtSurfaceTransport(options: ExtSurfaceTransportOp
|
|
|
52
52
|
/**
|
|
53
53
|
* The change-event router (the launcher's dispatch seam): `attach`
|
|
54
54
|
* creates a transport whose `onChanged` subscribes into the hub under
|
|
55
|
-
* the instance's (slug, id
|
|
56
|
-
*
|
|
55
|
+
* the instance's (slug, id); `dispatch` fans a `surface.changed` event
|
|
56
|
+
* out to every subscription of the event's view. The event's `k` is NOT
|
|
57
|
+
* matched: the server hashes the principal-scoped instance key (jobs /
|
|
58
|
+
* skills / curator fold the user in), which no client hash can
|
|
59
|
+
* reproduce. The server already targets the event at the streams
|
|
60
|
+
* watching that exact instance (watchers.streamsFor), so matching the
|
|
61
|
+
* view is safe; a second instance of the same view refetches once.
|
|
57
62
|
*/
|
|
58
63
|
export declare class ExtSurfaceHub {
|
|
59
64
|
#private;
|
|
60
|
-
/** Create a transport whose `onChanged` subscribes into this hub.
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
/** Create a transport whose `onChanged` subscribes into this hub. The
|
|
66
|
+
* `keyHash` argument is retained for call-site compatibility (dispatch
|
|
67
|
+
* no longer routes on it — see the class doc). */
|
|
68
|
+
attach(options: ExtSurfaceTransportOptions, _keyHash: string): ExtSurfaceTransportHandle;
|
|
69
|
+
/** Fire every subscription of the event's (slug, id), whatever the
|
|
70
|
+
* event's `k`. */
|
|
63
71
|
dispatch(event: {
|
|
64
72
|
readonly slug: string;
|
|
65
73
|
readonly id: string;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* The TUI's extension-surface transport (UI-blocks U3 Task 5): implements
|
|
3
3
|
* the shared `SurfaceTransport` contract over the U3 routes (spec §7.2)
|
|
4
4
|
* as the TUI's remote client. One transport = one surface instance
|
|
5
|
-
* (slug + id + params); the
|
|
6
|
-
*
|
|
5
|
+
* (slug + id + params); the hub routes change events by (slug, id) —
|
|
6
|
+
* the wire `k` is not matched (see ExtSurfaceHub).
|
|
7
7
|
*
|
|
8
8
|
* Like the webui twin, every fetch carries `X-Sigrun-Stream` once the
|
|
9
9
|
* launcher hands the stream id in (the watcher entry's stream);
|
|
@@ -28,13 +28,24 @@ export function surfaceKeyHash(slug, id, key) {
|
|
|
28
28
|
export function transportKey(options) {
|
|
29
29
|
return instanceKey(options.params ?? {});
|
|
30
30
|
}
|
|
31
|
+
/** The surface path WITHOUT any query — suffix routes (/data, /actions,
|
|
32
|
+
* /uploads) build from this and add their own query last (appending a
|
|
33
|
+
* suffix after `?params=` would land after the query string — the
|
|
34
|
+
* webui's E8 find, now the TUI's fetchData fix). */
|
|
35
|
+
function surfaceBase(options) {
|
|
36
|
+
return `/v1/ext/${encodeURIComponent(options.slug)}/surfaces/${encodeURIComponent(options.id)}`;
|
|
37
|
+
}
|
|
38
|
+
/** The URL-encoded params JSON. */
|
|
39
|
+
function paramsQuery(options) {
|
|
40
|
+
return encodeURIComponent(JSON.stringify(options.params ?? {}));
|
|
41
|
+
}
|
|
31
42
|
/** The base path for one surface instance (`?params=` omitted when `{}`). */
|
|
32
43
|
function surfacePath(options) {
|
|
33
|
-
const base =
|
|
44
|
+
const base = surfaceBase(options);
|
|
34
45
|
if (options.params === undefined || Object.keys(options.params).length === 0) {
|
|
35
46
|
return base;
|
|
36
47
|
}
|
|
37
|
-
return `${base}?params=${
|
|
48
|
+
return `${base}?params=${paramsQuery(options)}`;
|
|
38
49
|
}
|
|
39
50
|
function withQuery(path, key, value) {
|
|
40
51
|
return `${path}${path.includes("?") ? "&" : "?"}${key}=${value}`;
|
|
@@ -75,11 +86,11 @@ export function createExtSurfaceTransport(options) {
|
|
|
75
86
|
return get(surfacePath(options));
|
|
76
87
|
},
|
|
77
88
|
fetchData(since) {
|
|
78
|
-
const path = `${
|
|
89
|
+
const path = `${surfaceBase(options)}/data?params=${paramsQuery(options)}`;
|
|
79
90
|
return get(since === undefined ? path : withQuery(path, "since", String(since)));
|
|
80
91
|
},
|
|
81
92
|
async act(body) {
|
|
82
|
-
const response = await doFetch(`${baseUrl}${
|
|
93
|
+
const response = await doFetch(`${baseUrl}${surfaceBase(options)}/actions`, {
|
|
83
94
|
method: "POST",
|
|
84
95
|
headers: { ...headers(), "content-type": "application/json" },
|
|
85
96
|
body: JSON.stringify({ ...body, params: options.params ?? {} }),
|
|
@@ -87,7 +98,7 @@ export function createExtSurfaceTransport(options) {
|
|
|
87
98
|
return (await response.json());
|
|
88
99
|
},
|
|
89
100
|
async upload(body) {
|
|
90
|
-
const base =
|
|
101
|
+
const base = surfaceBase(options);
|
|
91
102
|
const path = withQuery(`${baseUrl}${base}/uploads/${encodeURIComponent(body.field)}/${encodeURIComponent(body.name)}`, "params", encodeURIComponent(JSON.stringify(options.params ?? {})));
|
|
92
103
|
const response = await doFetch(path, {
|
|
93
104
|
method: "PUT",
|
|
@@ -119,13 +130,20 @@ export function createExtSurfaceTransport(options) {
|
|
|
119
130
|
/**
|
|
120
131
|
* The change-event router (the launcher's dispatch seam): `attach`
|
|
121
132
|
* creates a transport whose `onChanged` subscribes into the hub under
|
|
122
|
-
* the instance's (slug, id
|
|
123
|
-
*
|
|
133
|
+
* the instance's (slug, id); `dispatch` fans a `surface.changed` event
|
|
134
|
+
* out to every subscription of the event's view. The event's `k` is NOT
|
|
135
|
+
* matched: the server hashes the principal-scoped instance key (jobs /
|
|
136
|
+
* skills / curator fold the user in), which no client hash can
|
|
137
|
+
* reproduce. The server already targets the event at the streams
|
|
138
|
+
* watching that exact instance (watchers.streamsFor), so matching the
|
|
139
|
+
* view is safe; a second instance of the same view refetches once.
|
|
124
140
|
*/
|
|
125
141
|
export class ExtSurfaceHub {
|
|
126
142
|
#subscriptions = new Set();
|
|
127
|
-
/** Create a transport whose `onChanged` subscribes into this hub.
|
|
128
|
-
|
|
143
|
+
/** Create a transport whose `onChanged` subscribes into this hub. The
|
|
144
|
+
* `keyHash` argument is retained for call-site compatibility (dispatch
|
|
145
|
+
* no longer routes on it — see the class doc). */
|
|
146
|
+
attach(options, _keyHash) {
|
|
129
147
|
const base = createExtSurfaceTransport(options);
|
|
130
148
|
const hub = this;
|
|
131
149
|
const transport = {
|
|
@@ -138,7 +156,6 @@ export class ExtSurfaceHub {
|
|
|
138
156
|
const subscription = {
|
|
139
157
|
slug: options.slug,
|
|
140
158
|
id: options.id,
|
|
141
|
-
keyHash,
|
|
142
159
|
listener,
|
|
143
160
|
};
|
|
144
161
|
hub.#subscriptions.add(subscription);
|
|
@@ -152,12 +169,12 @@ export class ExtSurfaceHub {
|
|
|
152
169
|
setStreamId: (next) => base.setStreamId(next),
|
|
153
170
|
};
|
|
154
171
|
}
|
|
155
|
-
/** Fire every subscription
|
|
172
|
+
/** Fire every subscription of the event's (slug, id), whatever the
|
|
173
|
+
* event's `k`. */
|
|
156
174
|
dispatch(event) {
|
|
157
175
|
for (const subscription of this.#subscriptions) {
|
|
158
176
|
if (subscription.slug === event.slug &&
|
|
159
|
-
subscription.id === event.id
|
|
160
|
-
subscription.keyHash === event.k) {
|
|
177
|
+
subscription.id === event.id) {
|
|
161
178
|
subscription.listener({ rev: event.rev, treeRev: event.treeRev });
|
|
162
179
|
}
|
|
163
180
|
}
|
package/dist/blocks/form/form.js
CHANGED
|
@@ -60,6 +60,21 @@ export function stepperSections(block, interaction) {
|
|
|
60
60
|
child.type === "choice"));
|
|
61
61
|
return allChoice ? sections : null;
|
|
62
62
|
}
|
|
63
|
+
/** The unanswered REQUIRED choices of the stepper (the review names
|
|
64
|
+
* them and holds the submit — §7.2 of the ask-user design). Answered
|
|
65
|
+
* means choiceValueText yields text (absent/""/[]/{other: ""} do not
|
|
66
|
+
* count). */
|
|
67
|
+
function unansweredSections(sections, ctx, values) {
|
|
68
|
+
return sections.filter((section) => {
|
|
69
|
+
for (const field of sectionFields(section)) {
|
|
70
|
+
if (field.type !== "choice" || field.required !== true)
|
|
71
|
+
continue;
|
|
72
|
+
if (choiceValueText(field, ctx, values[field.name] ?? null) === "")
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
63
78
|
/** The answerable fields of a stepper section (every field the review
|
|
64
79
|
* summarizes — §9.1's review step covers ALL of a section's fields,
|
|
65
80
|
* not only the choice). */
|
|
@@ -310,6 +325,9 @@ export class FormComponent {
|
|
|
310
325
|
const summary = answers.filter((text) => text !== "").join(", ");
|
|
311
326
|
lines.push(truncate(` ${title}: ${summary}`, width));
|
|
312
327
|
});
|
|
328
|
+
for (const section of unansweredSections(sections, this.#ctx, this.#values)) {
|
|
329
|
+
lines.push(truncate(`Not answered yet: ${bindText(section.title, this.#ctx)}`, width));
|
|
330
|
+
}
|
|
313
331
|
if (!this.#readOnly && !this.#submitted) {
|
|
314
332
|
lines.push(truncate(`> [ ${this.#submitLabel} ]`, width));
|
|
315
333
|
}
|
|
@@ -435,6 +453,10 @@ export class FormComponent {
|
|
|
435
453
|
return;
|
|
436
454
|
}
|
|
437
455
|
if (data === "\r" && !this.#submitted) {
|
|
456
|
+
// The review holds the submit while a required question is
|
|
457
|
+
// unanswered (§7.2) — Enter is ignored, not an error.
|
|
458
|
+
if (unansweredSections(sections, this.#ctx, this.#values).length > 0)
|
|
459
|
+
return;
|
|
438
460
|
if (this.#submit())
|
|
439
461
|
this.#submitted = true;
|
|
440
462
|
}
|
package/dist/blocks/surface.d.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* identical to U1 (a move against an unknown handler still goes to the
|
|
28
28
|
* transport, unoverlaid).
|
|
29
29
|
*/
|
|
30
|
-
import type { Action, JsonObject, JsonValue, SurfaceTransport, Tree } from "@sigrun/shared/blocks";
|
|
30
|
+
import type { Action, CoreTarget, JsonObject, JsonValue, SurfaceTransport, Tree } from "@sigrun/shared/blocks";
|
|
31
31
|
/** An action has 10 s to answer before it counts as a reject (§6.3). */
|
|
32
32
|
export declare const ACT_TIMEOUT_MS = 10000;
|
|
33
33
|
/** The notice cap: pending notices drop oldest-first past 50 (a flood
|
|
@@ -49,6 +49,14 @@ export interface SurfaceView {
|
|
|
49
49
|
/** The surface state container (class shape — the TUI has no hook layer). */
|
|
50
50
|
export declare class SurfaceState {
|
|
51
51
|
#private;
|
|
52
|
+
/** The shell's navigate sink (jobs-editor Task 7): navigate actions
|
|
53
|
+
* and accepted results' `navigate` route here — shell-local, never
|
|
54
|
+
* posted (spec §6.1; the webui's useSurface.onNavigate). */
|
|
55
|
+
onNavigate: ((action: {
|
|
56
|
+
type: "navigate";
|
|
57
|
+
params?: JsonObject;
|
|
58
|
+
target?: CoreTarget;
|
|
59
|
+
}) => void) | undefined;
|
|
52
60
|
constructor(transport: SurfaceTransport);
|
|
53
61
|
/** The current snapshot: confirmed data with the overlay applied. */
|
|
54
62
|
get view(): SurfaceView;
|
|
@@ -75,6 +83,8 @@ export declare class SurfaceState {
|
|
|
75
83
|
load(): Promise<void>;
|
|
76
84
|
/** Stop listening and cancel every pending action timer. */
|
|
77
85
|
dispose(): void;
|
|
78
|
-
/** Dispatch an action to the surface; a move joins the overlay first.
|
|
86
|
+
/** Dispatch an action to the surface; a move joins the overlay first.
|
|
87
|
+
* A navigate action is SHELL-LOCAL: it routes through `onNavigate`
|
|
88
|
+
* and never reaches the transport (spec §6.1). */
|
|
79
89
|
dispatch(action: Action, payload: JsonValue): void;
|
|
80
90
|
}
|
package/dist/blocks/surface.js
CHANGED
|
@@ -190,6 +190,10 @@ export class SurfaceState {
|
|
|
190
190
|
* never reaches the transport. */
|
|
191
191
|
#viewBranch = {};
|
|
192
192
|
#actionSeq = 0;
|
|
193
|
+
/** The shell's navigate sink (jobs-editor Task 7): navigate actions
|
|
194
|
+
* and accepted results' `navigate` route here — shell-local, never
|
|
195
|
+
* posted (spec §6.1; the webui's useSurface.onNavigate). */
|
|
196
|
+
onNavigate;
|
|
193
197
|
/** The highest rev a change event announced (gap detect). */
|
|
194
198
|
#announcedRev = 0;
|
|
195
199
|
#timers = new Map();
|
|
@@ -403,8 +407,14 @@ export class SurfaceState {
|
|
|
403
407
|
// event or keepalive retries (§7.3).
|
|
404
408
|
}
|
|
405
409
|
}
|
|
406
|
-
/** Dispatch an action to the surface; a move joins the overlay first.
|
|
410
|
+
/** Dispatch an action to the surface; a move joins the overlay first.
|
|
411
|
+
* A navigate action is SHELL-LOCAL: it routes through `onNavigate`
|
|
412
|
+
* and never reaches the transport (spec §6.1). */
|
|
407
413
|
dispatch(action, payload) {
|
|
414
|
+
if (action.type === "navigate") {
|
|
415
|
+
this.onNavigate?.(action);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
408
418
|
const payloadObject = typeof payload === "object" && payload !== null && !Array.isArray(payload)
|
|
409
419
|
? payload
|
|
410
420
|
: {};
|
|
@@ -451,8 +461,17 @@ export class SurfaceState {
|
|
|
451
461
|
return;
|
|
452
462
|
}
|
|
453
463
|
this.#serverErrors = {};
|
|
454
|
-
|
|
455
|
-
|
|
464
|
+
// An accepted act's `navigate` routes through the shell LAST
|
|
465
|
+
// (jobs-editor Task 7, plan-review P2): after the toast and
|
|
466
|
+
// after the non-move refetch is kicked off — the app's
|
|
467
|
+
// handler may dispose this state.
|
|
468
|
+
const finishAccept = () => {
|
|
469
|
+
if (result.toast !== undefined)
|
|
470
|
+
this.#notice(result.toast);
|
|
471
|
+
if (result.navigate?.type === "navigate") {
|
|
472
|
+
this.onNavigate?.(result.navigate);
|
|
473
|
+
}
|
|
474
|
+
};
|
|
456
475
|
if (!isMove) {
|
|
457
476
|
// A non-move accept (the settings save): the rev already
|
|
458
477
|
// bumped server-side (the optimistic check needs the form's
|
|
@@ -462,6 +481,7 @@ export class SurfaceState {
|
|
|
462
481
|
// surface.changed hub — the app owns the probe).
|
|
463
482
|
this.#rev = result.rev;
|
|
464
483
|
void this.#fullRefetch();
|
|
484
|
+
finishAccept();
|
|
465
485
|
return;
|
|
466
486
|
}
|
|
467
487
|
if (result.ops !== undefined) {
|
|
@@ -471,6 +491,7 @@ export class SurfaceState {
|
|
|
471
491
|
catch (error) {
|
|
472
492
|
if (error instanceof DataOpError) {
|
|
473
493
|
void this.#fullRefetch();
|
|
494
|
+
finishAccept();
|
|
474
495
|
return;
|
|
475
496
|
}
|
|
476
497
|
throw error;
|
|
@@ -483,6 +504,7 @@ export class SurfaceState {
|
|
|
483
504
|
}
|
|
484
505
|
this.#overlay = this.#overlay.filter((entry) => entry.actionId !== actionId);
|
|
485
506
|
this.#emit();
|
|
507
|
+
finishAccept();
|
|
486
508
|
}
|
|
487
509
|
else {
|
|
488
510
|
// Accept without ops: hold the entry until confirmed reaches
|
|
@@ -495,6 +517,7 @@ export class SurfaceState {
|
|
|
495
517
|
this.#rev = Math.max(this.#rev, result.rev);
|
|
496
518
|
this.#overlay = this.#overlay.map((entry) => entry.actionId === actionId ? { ...entry, untilRev: result.rev } : entry);
|
|
497
519
|
this.#emit();
|
|
520
|
+
finishAccept();
|
|
498
521
|
}
|
|
499
522
|
})
|
|
500
523
|
.catch(() => {
|
package/dist/keymaps/emacs.js
CHANGED
|
@@ -32,6 +32,7 @@ const BINDINGS = [
|
|
|
32
32
|
{ keys: "C-n / C-p", action: "move down / up" },
|
|
33
33
|
{ keys: "enter", action: "open / fold" },
|
|
34
34
|
{ keys: "C-o", action: "answer interaction" },
|
|
35
|
+
{ keys: "M-s", action: "decline interaction (answer mode)" },
|
|
35
36
|
{ keys: "C-l", action: "redraw" },
|
|
36
37
|
{ keys: "C-h", action: "key reference" },
|
|
37
38
|
{ keys: "M-N", action: "notices" },
|
|
@@ -147,6 +148,10 @@ export function createEmacsKeymap() {
|
|
|
147
148
|
return { type: "prompt-jump", delta: 1 };
|
|
148
149
|
if (data === "\x0f")
|
|
149
150
|
return { type: "answer-interaction" };
|
|
151
|
+
// Ask-user Task 5: M-s declines the armed interaction (M-d stays
|
|
152
|
+
// the notices dismiss; the app gates decline to answer mode).
|
|
153
|
+
if (data === "\x1bs")
|
|
154
|
+
return { type: "decline-interaction" };
|
|
150
155
|
if (data === "\x0c")
|
|
151
156
|
return { type: "redraw" };
|
|
152
157
|
if (data === "\x08")
|
package/dist/keymaps/standard.js
CHANGED
|
@@ -21,6 +21,7 @@ const BINDINGS = [
|
|
|
21
21
|
{ keys: "ctrl+n", action: "new conversation" },
|
|
22
22
|
{ keys: "ctrl+t", action: "cycle thinking" },
|
|
23
23
|
{ keys: "ctrl+o", action: "answer interaction" },
|
|
24
|
+
{ keys: "alt+s", action: "decline interaction (answer mode)" },
|
|
24
25
|
{ keys: "ctrl+g", action: "notices" },
|
|
25
26
|
{ keys: "alt+d", action: "dismiss newest notice" },
|
|
26
27
|
{ keys: "ctrl+l", action: "redraw" },
|
|
@@ -60,6 +61,11 @@ export function createStandardKeymap() {
|
|
|
60
61
|
return { type: "cycle-thinking" };
|
|
61
62
|
if (matchesKey(data, Key.ctrl("o")))
|
|
62
63
|
return { type: "answer-interaction" };
|
|
64
|
+
// Ask-user Task 5: alt+s declines the armed interaction (answer
|
|
65
|
+
// mode only — the app gates the action; outside answer mode this
|
|
66
|
+
// falls through as a no-op). alt+d stays the notices dismiss.
|
|
67
|
+
if (matchesKey(data, Key.alt("s")))
|
|
68
|
+
return { type: "decline-interaction" };
|
|
63
69
|
// Notices Task 8: ctrl+g (the notices overlay — a control code,
|
|
64
70
|
// never typed text) and alt+d (dismiss the inline notice; M-d is
|
|
65
71
|
// readline's kill-word, a chord reserved readline never sends).
|
package/dist/keymaps/vim.js
CHANGED
|
@@ -47,6 +47,7 @@ const BINDINGS = [
|
|
|
47
47
|
{ keys: ":", action: "command line" },
|
|
48
48
|
{ keys: "ctrl+p", action: "conversation picker" },
|
|
49
49
|
{ keys: "ctrl+o", action: "answer interaction" },
|
|
50
|
+
{ keys: "alt+s", action: "decline interaction (answer mode)" },
|
|
50
51
|
{ keys: "ctrl+l", action: "redraw" },
|
|
51
52
|
{ keys: "ctrl+c", action: "abort run (busy)" },
|
|
52
53
|
{ keys: "g?", action: "key reference" },
|
|
@@ -132,6 +133,10 @@ export function createVimKeymap(options) {
|
|
|
132
133
|
}
|
|
133
134
|
if (matchesKey(data, Key.ctrl("o")))
|
|
134
135
|
return { type: "answer-interaction" };
|
|
136
|
+
// Ask-user Task 5: alt+s declines the armed interaction (the app
|
|
137
|
+
// gates the action to answer mode + a declinable record).
|
|
138
|
+
if (matchesKey(data, Key.alt("s")))
|
|
139
|
+
return { type: "decline-interaction" };
|
|
135
140
|
return "pass";
|
|
136
141
|
};
|
|
137
142
|
/** Block-surface keys (spec §12.4): NORMAL mode over a `surface`
|
package/dist/ui/app.d.ts
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
import { Editor, type Component, type TUI } from "@earendil-works/pi-tui";
|
|
50
50
|
import type { SigrunClient } from "../api.js";
|
|
51
51
|
import type { CoreTarget, JsonObject } from "@sigrun/shared/blocks";
|
|
52
|
+
import { SurfaceState } from "../blocks/surface.js";
|
|
52
53
|
import { type Unwatch, type WatchHandlers, type WatchOptions } from "../sse.js";
|
|
53
54
|
import { type TuiTheme } from "../theme.js";
|
|
54
55
|
import type { Action, ActionHost, Focus } from "../actions.js";
|
|
@@ -150,7 +151,10 @@ export declare class TuiApp implements Component, ActionHost {
|
|
|
150
151
|
get extView(): {
|
|
151
152
|
readonly slug: string;
|
|
152
153
|
readonly id: string;
|
|
154
|
+
readonly params: JsonObject;
|
|
153
155
|
} | undefined;
|
|
156
|
+
/** The open view's surface state (tests + Task 7's navigation). */
|
|
157
|
+
get extViewState(): SurfaceState | undefined;
|
|
154
158
|
/**
|
|
155
159
|
* Open an extension VIEW in the main pane (U3 Task 5, spec §7.1):
|
|
156
160
|
* the sidebar's view selection swaps the pane's content to the
|
package/dist/ui/app.js
CHANGED
|
@@ -50,6 +50,8 @@ import { Editor, HStack, Key, matchesKey, VStack, } from "@earendil-works/pi-tui
|
|
|
50
50
|
import { SigrunApiError } from "../api.js";
|
|
51
51
|
import { ExtSurfaceHub, surfaceKeyHash, transportKey, } from "../blocks/ext-transport.js";
|
|
52
52
|
import { ChatSurfaceHub } from "../blocks/chat-transport.js";
|
|
53
|
+
import { SurfaceState } from "../blocks/surface.js";
|
|
54
|
+
import { SurfaceView } from "../blocks/surface-view.js";
|
|
53
55
|
import { watchConversation } from "../sse.js";
|
|
54
56
|
import { spawnSync } from "node:child_process";
|
|
55
57
|
import { plainTuiTheme } from "../theme.js";
|
|
@@ -306,7 +308,15 @@ export class TuiApp {
|
|
|
306
308
|
get extView() {
|
|
307
309
|
return this.#extView === undefined
|
|
308
310
|
? undefined
|
|
309
|
-
: {
|
|
311
|
+
: {
|
|
312
|
+
slug: this.#extView.slug,
|
|
313
|
+
id: this.#extView.id,
|
|
314
|
+
params: this.#extView.params,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/** The open view's surface state (tests + Task 7's navigation). */
|
|
318
|
+
get extViewState() {
|
|
319
|
+
return this.#extView?.state;
|
|
310
320
|
}
|
|
311
321
|
/**
|
|
312
322
|
* Open an extension VIEW in the main pane (U3 Task 5, spec §7.1):
|
|
@@ -315,11 +325,12 @@ export class TuiApp {
|
|
|
315
325
|
* the transport + identity here so U2's mount is one prop pass.
|
|
316
326
|
*/
|
|
317
327
|
async openExtView(slug, id, params) {
|
|
318
|
-
const
|
|
328
|
+
const instanceParams = params ?? {};
|
|
329
|
+
const key = transportKey({ slug, id, baseUrl: this.#watchOptions.baseUrl, params: instanceParams });
|
|
319
330
|
const handle = this.#extHub.attach({
|
|
320
331
|
slug,
|
|
321
332
|
id,
|
|
322
|
-
|
|
333
|
+
params: instanceParams,
|
|
323
334
|
baseUrl: this.#watchOptions.baseUrl,
|
|
324
335
|
...(this.#watchOptions.user === undefined
|
|
325
336
|
? {}
|
|
@@ -330,8 +341,44 @@ export class TuiApp {
|
|
|
330
341
|
...(this.#eventsStreamId === undefined
|
|
331
342
|
? {}
|
|
332
343
|
: { streamId: this.#eventsStreamId }),
|
|
344
|
+
// A test-injected client fetch carries the view's loads (the
|
|
345
|
+
// settings view's shape; production's client leaves it ambient).
|
|
346
|
+
...(this.#client.fetch === undefined
|
|
347
|
+
? {}
|
|
348
|
+
: { fetch: this.#client.fetch }),
|
|
333
349
|
}, surfaceKeyHash(slug, id, key));
|
|
334
|
-
|
|
350
|
+
// A swap disposes the previous view's state BEFORE the new one
|
|
351
|
+
// opens (the settings view's openSection shape): no orphaned
|
|
352
|
+
// subscriptions or action timers survive a re-open.
|
|
353
|
+
this.#extView?.state.dispose();
|
|
354
|
+
const state = new SurfaceState(handle.transport);
|
|
355
|
+
// Navigation wiring (jobs-editor Task 7): a navigate action or an
|
|
356
|
+
// accepted result's `navigate` reopens THIS view with the params
|
|
357
|
+
// (params-only) or routes a core target through the shell — never
|
|
358
|
+
// posted to the server (SurfaceState.dispatch short-circuits it).
|
|
359
|
+
state.onNavigate = (nav) => nav.target !== undefined
|
|
360
|
+
? void this.navigateCoreTarget(nav.target)
|
|
361
|
+
: void this.openExtView(slug, id, nav.params ?? {});
|
|
362
|
+
const view = new SurfaceView(state, {
|
|
363
|
+
theme: this.#theme,
|
|
364
|
+
unicode: this.#unicode,
|
|
365
|
+
});
|
|
366
|
+
state.onChange(() => {
|
|
367
|
+
// The surface's notices (action toasts, rejections) land as
|
|
368
|
+
// pane notices (the post-close backlog) AND flash the status
|
|
369
|
+
// bar while mounted — the pane is not painted while this view
|
|
370
|
+
// owns the main slot, so the flash is the visible channel
|
|
371
|
+
// (SurfaceState's contract names the status bar as the host
|
|
372
|
+
// notice seam).
|
|
373
|
+
for (const notice of state.consumeNotices()) {
|
|
374
|
+
this.pane.showNotice(notice);
|
|
375
|
+
this.#flashStatus(notice);
|
|
376
|
+
}
|
|
377
|
+
this.#tui.requestRender();
|
|
378
|
+
});
|
|
379
|
+
this.#extView = { slug, id, params: instanceParams, handle, state, view };
|
|
380
|
+
this.mainContent.set(view);
|
|
381
|
+
void state.load();
|
|
335
382
|
this.#tui.requestRender();
|
|
336
383
|
}
|
|
337
384
|
/**
|
|
@@ -388,7 +435,9 @@ export class TuiApp {
|
|
|
388
435
|
closeExtView() {
|
|
389
436
|
if (this.#extView === undefined)
|
|
390
437
|
return false;
|
|
438
|
+
this.#extView.state.dispose();
|
|
391
439
|
this.#extView = undefined;
|
|
440
|
+
this.mainContent.set(this.pane);
|
|
392
441
|
this.#tui.requestRender();
|
|
393
442
|
return true;
|
|
394
443
|
}
|
|
@@ -725,11 +774,28 @@ export class TuiApp {
|
|
|
725
774
|
this.#abortActive();
|
|
726
775
|
return true;
|
|
727
776
|
}
|
|
728
|
-
//
|
|
729
|
-
//
|
|
730
|
-
//
|
|
731
|
-
|
|
732
|
-
|
|
777
|
+
// The open extension view owns its keys (Task 6): Esc forwards to
|
|
778
|
+
// the view while its page has an overlay (actions menu, sidebar
|
|
779
|
+
// drawer, pushed detail — spec §12.3's page contract); otherwise
|
|
780
|
+
// Esc with params reopens the view at the list (`{}`), and Esc at
|
|
781
|
+
// the list closes the view. Non-Esc keys route to the view.
|
|
782
|
+
if (this.#extView !== undefined) {
|
|
783
|
+
if (matchesKey(data, Key.escape)) {
|
|
784
|
+
const inner = this.#extView.view.inner;
|
|
785
|
+
if (inner?.hasOverlay === true) {
|
|
786
|
+
this.#extView.view.handleInput(data);
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
if (Object.keys(this.#extView.params).length > 0) {
|
|
790
|
+
void this.openExtView(this.#extView.slug, this.#extView.id, {}).catch((error) => {
|
|
791
|
+
this.pane.showError(describeCommandError("open view", error));
|
|
792
|
+
});
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
return this.closeExtView();
|
|
796
|
+
}
|
|
797
|
+
this.#extView.view.handleInput(data);
|
|
798
|
+
return true;
|
|
733
799
|
}
|
|
734
800
|
return false;
|
|
735
801
|
}
|
|
@@ -970,6 +1036,8 @@ export class TuiApp {
|
|
|
970
1036
|
this.#restartGeneration += 1;
|
|
971
1037
|
this.interactions.dispose();
|
|
972
1038
|
this.ambient.dispose();
|
|
1039
|
+
this.#extView?.state.dispose();
|
|
1040
|
+
this.#extView = undefined;
|
|
973
1041
|
// The pane's chat-block surface states hold live hub subscriptions
|
|
974
1042
|
// + action timers — the shell owns their teardown (the pane never
|
|
975
1043
|
// leaves a subscription behind).
|
|
@@ -1149,6 +1217,10 @@ export class TuiApp {
|
|
|
1149
1217
|
if (!this.interactions.answerMode)
|
|
1150
1218
|
this.interactions.beginAnswer();
|
|
1151
1219
|
return;
|
|
1220
|
+
case "decline-interaction":
|
|
1221
|
+
// Gated inside: answer mode + a declinable record only.
|
|
1222
|
+
await this.interactions.decline();
|
|
1223
|
+
return;
|
|
1152
1224
|
case "cancel-run":
|
|
1153
1225
|
this.#abortActive();
|
|
1154
1226
|
return;
|
|
@@ -79,6 +79,18 @@ export declare class InteractionPrompter implements Component {
|
|
|
79
79
|
* keep typing while a prompt waits).
|
|
80
80
|
*/
|
|
81
81
|
handleKey(data: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Decline the armed interaction (ask-user spec §5.2 — the keymap's
|
|
84
|
+
* `decline-interaction` action routes here). Gated exactly like the
|
|
85
|
+
* affordance: answer mode armed on the top record, and the record is
|
|
86
|
+
* declinable. Success exits answer mode (the list poll drops the
|
|
87
|
+
* settled record). A 404 `not_pending` — the record settled under
|
|
88
|
+
* the user's fingers — refetches through
|
|
89
|
+
* {@link SigrunClient.getInteraction} and swaps the settled envelope
|
|
90
|
+
* in read-only (the mid-edit settle path); any other failure reports
|
|
91
|
+
* loud and keeps the card writable.
|
|
92
|
+
*/
|
|
93
|
+
decline(): Promise<void>;
|
|
82
94
|
/** Detach (the app calls this on dispose): stale fetches never land. */
|
|
83
95
|
dispose(): void;
|
|
84
96
|
/**
|
package/dist/ui/interactions.js
CHANGED
|
@@ -174,6 +174,13 @@ export class InteractionPrompter {
|
|
|
174
174
|
this.#changed();
|
|
175
175
|
return true;
|
|
176
176
|
}
|
|
177
|
+
// The decline chord is the KEYMAP's (decline-interaction action,
|
|
178
|
+
// dispatched by the launcher): pass it through so the keymap sees
|
|
179
|
+
// it — the surface fall-through below would swallow it and the
|
|
180
|
+
// chord would never fire (review fix; the same ownership rule as
|
|
181
|
+
// the prompt's other action keys).
|
|
182
|
+
if (matchesKey(data, Key.alt("s")))
|
|
183
|
+
return false;
|
|
177
184
|
this.#ensureSurface(top);
|
|
178
185
|
if (this.#surface === undefined) {
|
|
179
186
|
this.#exitAnswer();
|
|
@@ -332,6 +339,67 @@ export class InteractionPrompter {
|
|
|
332
339
|
this.#answerMode = false;
|
|
333
340
|
this.#answerCallId = undefined;
|
|
334
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Decline the armed interaction (ask-user spec §5.2 — the keymap's
|
|
344
|
+
* `decline-interaction` action routes here). Gated exactly like the
|
|
345
|
+
* affordance: answer mode armed on the top record, and the record is
|
|
346
|
+
* declinable. Success exits answer mode (the list poll drops the
|
|
347
|
+
* settled record). A 404 `not_pending` — the record settled under
|
|
348
|
+
* the user's fingers — refetches through
|
|
349
|
+
* {@link SigrunClient.getInteraction} and swaps the settled envelope
|
|
350
|
+
* in read-only (the mid-edit settle path); any other failure reports
|
|
351
|
+
* loud and keeps the card writable.
|
|
352
|
+
*/
|
|
353
|
+
async decline() {
|
|
354
|
+
const top = this.#pending[0];
|
|
355
|
+
if (!this.#answerMode || top === undefined)
|
|
356
|
+
return;
|
|
357
|
+
if (this.#answerCallId !== top.callId)
|
|
358
|
+
return;
|
|
359
|
+
if (top.declinable !== true)
|
|
360
|
+
return;
|
|
361
|
+
const id = this.#conversationId;
|
|
362
|
+
if (id === undefined)
|
|
363
|
+
return;
|
|
364
|
+
try {
|
|
365
|
+
await this.#client.declineInteraction(id, top.callId);
|
|
366
|
+
}
|
|
367
|
+
catch (cause) {
|
|
368
|
+
const isNotPending = typeof cause === "object" &&
|
|
369
|
+
cause !== null &&
|
|
370
|
+
cause.status === 404;
|
|
371
|
+
if (!isNotPending) {
|
|
372
|
+
this.onError?.(describeCommandError("decline", cause));
|
|
373
|
+
this.#changed();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
// 404 not_pending: the settle raced the user. Refetch; a still-
|
|
377
|
+
// pending record must NOT wedge the card read-only.
|
|
378
|
+
try {
|
|
379
|
+
const fresh = await this.#client.getInteraction(id, top.callId);
|
|
380
|
+
if (this.#surface?.record.callId !== top.callId)
|
|
381
|
+
return;
|
|
382
|
+
if (fresh.status === "pending") {
|
|
383
|
+
this.onError?.(describeCommandError("decline", cause));
|
|
384
|
+
this.#changed();
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
this.#surface =
|
|
388
|
+
this.#surface === undefined
|
|
389
|
+
? undefined
|
|
390
|
+
: { ...this.#surface, settled: fresh, access: "read" };
|
|
391
|
+
this.#surface?.state.setAccess("read");
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
this.onError?.(describeCommandError("decline", cause));
|
|
395
|
+
this.#changed();
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
this.#exitAnswer();
|
|
400
|
+
void this.#refresh();
|
|
401
|
+
this.#changed();
|
|
402
|
+
}
|
|
335
403
|
/** Generation-guarded, coalesced fetch of the pending list. */
|
|
336
404
|
async #refresh() {
|
|
337
405
|
const id = this.#conversationId;
|
|
@@ -408,11 +476,18 @@ export class InteractionPrompter {
|
|
|
408
476
|
return [];
|
|
409
477
|
const lines = [];
|
|
410
478
|
const shown = this.#surface?.settled ?? top;
|
|
479
|
+
// Settled declines name their outcome (spec §5.2's "declined" /
|
|
480
|
+
// "declined — replied in chat"), never the raw 'skipped'.
|
|
481
|
+
const statusText = shown.status === "skipped" && shown.declinedVia !== undefined
|
|
482
|
+
? shown.declinedVia === "message"
|
|
483
|
+
? "declined — replied in chat"
|
|
484
|
+
: "declined"
|
|
485
|
+
: shown.status;
|
|
411
486
|
const suffix = this.#answerMode
|
|
412
487
|
? " — answering"
|
|
413
488
|
: shown.status === "pending"
|
|
414
489
|
? ""
|
|
415
|
-
: ` — ${
|
|
490
|
+
: ` — ${statusText}`;
|
|
416
491
|
lines.push(`[interaction] ${shown.kind}${suffix}`);
|
|
417
492
|
if (top.body !== undefined) {
|
|
418
493
|
this.#ensureSurface(top);
|
|
@@ -428,7 +503,9 @@ export class InteractionPrompter {
|
|
|
428
503
|
}
|
|
429
504
|
if (top.body !== undefined) {
|
|
430
505
|
lines.push(this.#answerMode
|
|
431
|
-
?
|
|
506
|
+
? top.declinable === true
|
|
507
|
+
? "1-9 answer · alt+s decline · Tab next · esc cancel"
|
|
508
|
+
: "1-9 answer · Tab next · esc cancel"
|
|
432
509
|
: "ctrl+o to answer");
|
|
433
510
|
}
|
|
434
511
|
return lines;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigrun/tui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "sigrun terminal client (pi-tui based) over the \u00a75 API \u2014 spawned by the sigrun CLI, or standalone against a server URL",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@earendil-works/pi-tui": "^1.3.0",
|
|
24
|
-
"@sigrun/shared": "0.
|
|
24
|
+
"@sigrun/shared": "0.10.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"typescript": "^7.0.2",
|
|
@@ -48,4 +48,4 @@
|
|
|
48
48
|
"access": "public",
|
|
49
49
|
"provenance": true
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|