@cspeach/cli 1.1.51 → 1.1.52
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/agent/chat-prompt.js +4 -1
- package/dist/agent/conversation-prompt.js +30 -13
- package/dist/agent/inline-skill-block.js +341 -0
- package/dist/agent/loop.js +208 -41
- package/dist/agent/provider-shape.js +8 -4
- package/dist/agent/providers/ai-hub-provider.js +2 -2
- package/dist/agent/providers/azure-provider.js +2 -2
- package/dist/agent/providers/byok-provider.js +2 -2
- package/dist/agent/providers/local-provider.js +2 -2
- package/dist/agent/repair-partial.js +12 -0
- package/dist/agent/skill-block-key.js +134 -0
- package/dist/agent/steering-queue.js +50 -14
- package/dist/agent/system-prompt.js +22 -0
- package/dist/agent/tool-dispatch.js +19 -7
- package/dist/agent/turn-watchdog.js +1 -1
- package/dist/approvals/advisory-render.js +6 -3
- package/dist/approvals/approval-prompt.js +9 -4
- package/dist/approvals/op-labels.js +14 -0
- package/dist/approvals/plan-grant.js +8 -0
- package/dist/approvals/render.js +19 -8
- package/dist/approvals/risk-floor.js +32 -4
- package/dist/approvals/visible-changes.js +50 -0
- package/dist/cli.js +10 -2
- package/dist/commands/compact.js +6 -1
- package/dist/commands/config-set.js +13 -0
- package/dist/commands/config-show.js +16 -4
- package/dist/commands/export-conversation.js +7 -4
- package/dist/commands/finish-check-guard.js +124 -0
- package/dist/commands/plan-reground.js +27 -4
- package/dist/commands/plan-resume.js +111 -1
- package/dist/config/loader.js +5 -1
- package/dist/cost/cost-log.js +6 -0
- package/dist/cost/system-block.js +6 -0
- package/dist/models/server-config.js +1 -1
- package/dist/projects/save-command.js +5 -1
- package/dist/projects/yes-no.js +5 -1
- package/dist/renderer/change-card.js +3 -1
- package/dist/renderer/steering-echo.js +53 -8
- package/dist/renderer/tool-labels.js +3 -1
- package/dist/repl/chat-lane.js +5 -2
- package/dist/repl/inline-mode.js +51 -0
- package/dist/repl/leftover.js +90 -0
- package/dist/repl/reroute-turn.js +7 -1
- package/dist/repl/turn-stop.js +64 -0
- package/dist/repl.js +144 -22
- package/dist/router/classifier.js +1 -1
- package/dist/sap/reachability-state.js +7 -0
- package/dist/sap/unreachable.js +2 -2
- package/dist/session/queued-step.js +3 -1
- package/dist/session/user-prompt.js +10 -1
- package/dist/skills/manifest-client.js +3 -2
- package/dist/skills/preamble.js +6 -4
- package/dist/skills/source-authed.js +3 -2
- package/dist/tools/approval.js +37 -7
- package/dist/tools/dispatch-skill.js +1 -1
- package/dist/tools/hand-off.js +49 -7
- package/dist/tools/lane/app-lane-resolve.js +9 -0
- package/dist/tools/method-only-change.js +241 -0
- package/dist/tools/rap/skeleton-resolve.js +4 -0
- package/dist/tools/sap-write.js +78 -3
- package/dist/tools/snapshot.js +7 -2
- package/dist/tools/subagent/agent_run.js +14 -0
- package/dist/tools/tool-loading.js +16 -8
- package/dist/tools/transport-fit-check.js +70 -0
- package/dist/tools/transport.js +17 -8
- package/dist/tools/write-pipeline.js +46 -5
- package/dist/ui/app.js +112 -13
- package/dist/ui/approval-modal.js +11 -6
- package/dist/ui/ascii-chars.js +21 -3
- package/dist/ui/footer.js +128 -7
- package/dist/ui/interrupt.js +35 -1
- package/dist/ui/live-area.js +21 -4
- package/dist/ui/queue-box-emitter.js +14 -0
- package/dist/ui/rewind-panel.js +2 -1
- package/dist/ui/session-timeline.js +2 -1
- package/dist/ui/text-input.js +31 -2
- package/dist/ui/widgets/bar-chart.js +2 -1
- package/dist/ui/widgets/confirm-card.js +2 -1
- package/dist/ui/widgets/dep-graph.js +2 -1
- package/dist/ui/widgets/diff-viewer.js +2 -1
- package/dist/ui/widgets/question-card.js +2 -1
- package/dist/ui/widgets/shortcuts-sheet.js +2 -2
- package/dist/ui/widgets/stack-frames.js +2 -1
- package/dist/util/connect-retry.js +65 -0
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/renderer/steering-echo.ts
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// How a mid-turn message is shown (spec §7). LT-15 Feature 1 (D5): typing it
|
|
4
|
+
// leaves no scrollback receipt any more; the App's live queue row shows it
|
|
5
|
+
// while it waits, and the loop prints "↳ read · …" when the model reads it.
|
|
6
6
|
import { paint } from './theme.js';
|
|
7
7
|
import { sanitizeControlChars } from './sanitize.js';
|
|
8
8
|
/** Wave1 fast-follow — only the first line, clipped, with a line count. */
|
|
@@ -20,12 +20,57 @@ export function formatSteeringEcho(text) {
|
|
|
20
20
|
return out;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* One waiting or read message as shown: first line, clipped, no control
|
|
24
|
+
* characters, and each tab as one space (a tab is 0 wide to string-width, so
|
|
25
|
+
* Ink's truncate-end would cut too late and the line could wrap).
|
|
26
26
|
*/
|
|
27
|
-
|
|
28
|
-
return
|
|
27
|
+
function shownSteer(text) {
|
|
28
|
+
return sanitizeControlChars(formatSteeringEcho(text)).replace(/\t/g, ' ');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* LT-15 Feature 1 (D5, F7) — the receipt the loop prints when the model reads
|
|
32
|
+
* a waiting message: `↳ read · <first line…>` in the dim role, one line per
|
|
33
|
+
* message (the caller adds the newline). The text may come from a paste, so
|
|
34
|
+
* it is clipped and control characters are neutralised.
|
|
35
|
+
*/
|
|
36
|
+
export function steeringReadLine(text) {
|
|
37
|
+
return paint('dim', `↳ read · ${shownSteer(text)}`);
|
|
38
|
+
}
|
|
39
|
+
/** The queue row shows at most this many messages; the rest are counted. */
|
|
40
|
+
export const QUEUE_ROW_MAX_ITEMS = 3;
|
|
41
|
+
/**
|
|
42
|
+
* LT-15 Feature 1 — the live row's lines (plain text, one terminal row each;
|
|
43
|
+
* App paints them dim and truncates to the width). Empty queue → no lines.
|
|
44
|
+
*
|
|
45
|
+
* 4 waiting · use ZPKG
|
|
46
|
+
* · also check the tests… (+1 line)
|
|
47
|
+
* · and the docs
|
|
48
|
+
* + 1 more
|
|
49
|
+
*
|
|
50
|
+
* `maxLines` is the room left in the live area. When the full row does not
|
|
51
|
+
* fit it folds to ONE line, never fewer: `4 waiting · use ZPKG + 3 more`.
|
|
52
|
+
*
|
|
53
|
+
* LT-15 Feature 2 (review I6) — `canEdit` adds `· ↑ to edit` right after the
|
|
54
|
+
* count, only while ↑ would really take the last message back (the App
|
|
55
|
+
* decides). It sits before the messages so a long message never cuts it off.
|
|
56
|
+
* Without it the bytes are exactly the Task 2 ones.
|
|
57
|
+
*/
|
|
58
|
+
export function formatQueueRow(items, maxLines = Infinity, opts = {}) {
|
|
59
|
+
if (items.length === 0)
|
|
60
|
+
return [];
|
|
61
|
+
const count = `${items.length} waiting${opts.canEdit ? ' · ↑ to edit' : ''}`;
|
|
62
|
+
const full = Math.min(items.length, QUEUE_ROW_MAX_ITEMS) + (items.length > QUEUE_ROW_MAX_ITEMS ? 1 : 0);
|
|
63
|
+
if (full > Math.max(1, maxLines)) {
|
|
64
|
+
return [`${count} · ${shownSteer(items[0])} + ${items.length - 1} more`];
|
|
65
|
+
}
|
|
66
|
+
const head = `${count} `;
|
|
67
|
+
const pad = ' '.repeat(head.length);
|
|
68
|
+
const shown = items.slice(0, QUEUE_ROW_MAX_ITEMS);
|
|
69
|
+
const lines = shown.map((t, i) => `${i === 0 ? head : pad}· ${shownSteer(t)}`);
|
|
70
|
+
const rest = items.length - shown.length;
|
|
71
|
+
if (rest > 0)
|
|
72
|
+
lines.push(`${pad}+ ${rest} more`);
|
|
73
|
+
return lines;
|
|
29
74
|
}
|
|
30
75
|
export function classifyMidTurnLine(text) {
|
|
31
76
|
const first = text.trim().split(/\s+/)[0] ?? '';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spillLabelFor } from '../lib/spill-labels.js';
|
|
2
|
+
import { writtenObjectName } from '../approvals/op-labels.js';
|
|
2
3
|
const OBJECT_MAX = 60;
|
|
3
4
|
/**
|
|
4
5
|
* Every value shown in a label goes through here: ANSI escape sequences and
|
|
@@ -108,7 +109,8 @@ const RULES = {
|
|
|
108
109
|
},
|
|
109
110
|
background_run: (a) => ({ verb: 'Started', object: shortRight(commandLine(a)) }),
|
|
110
111
|
sap_get_source: sapObject('Read'),
|
|
111
|
-
|
|
112
|
+
// LT-11 — an access control, metadata extension or behavior definition names its kind.
|
|
113
|
+
sap_set_source: (a) => ({ verb: 'Wrote', object: shortRight(writtenObjectName(str(a['type']), str(a['name']) ?? '')) }),
|
|
112
114
|
sap_activate: (a) => ({ verb: 'Activated', object: shortRight(activateObject(a)) }),
|
|
113
115
|
sap_syntax_check: sapObject('Checked syntax'),
|
|
114
116
|
sap_atc_run: sapObject('Ran ATC on'),
|
package/dist/repl/chat-lane.js
CHANGED
|
@@ -113,8 +113,11 @@ export function turnStatusLabel(skill) {
|
|
|
113
113
|
}
|
|
114
114
|
/** D11 — the one dim line shown in place of the echo when the message re-runs through today's router. */
|
|
115
115
|
export const CHAT_UNAVAILABLE_LINE = "Chat isn't available on this server yet — using skill routing.";
|
|
116
|
-
export function chatFallbackOrigin() {
|
|
117
|
-
|
|
116
|
+
export function chatFallbackOrigin(prose = false) {
|
|
117
|
+
// LT-15 Task 4 fix round 1 — a withdrawn prose replay re-runs as prose.
|
|
118
|
+
return prose
|
|
119
|
+
? { by: 'chat_fallback', line: CHAT_UNAVAILABLE_LINE, prose: true }
|
|
120
|
+
: { by: 'chat_fallback', line: CHAT_UNAVAILABLE_LINE };
|
|
118
121
|
}
|
|
119
122
|
/**
|
|
120
123
|
* The one place a process lane's tool side effect is applied (D5, §6.1).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// cspeach-cli/src/repl/inline-mode.ts
|
|
2
|
+
//
|
|
3
|
+
// Stage 1A, Task 1A.1 — when `assistant.skills = inline` is active.
|
|
4
|
+
//
|
|
5
|
+
// Decided at EVERY submit from the inputs as they are then (rev 4 M4), never
|
|
6
|
+
// cached, never stored in the session file: a resumed session re-decides from
|
|
7
|
+
// today's config and today's proxy. The history is NOT an input (rev 5 S-B):
|
|
8
|
+
// `assistant.skills = system`, `/chat off`, a non-TTY and a non-serving proxy
|
|
9
|
+
// always win. The REPL passes the result to the loop as `ctx.inlineSkills`.
|
|
10
|
+
// Piped stdin and one-shot never get it; reader and agent_run children never
|
|
11
|
+
// carry it (their ctx is built field by field).
|
|
12
|
+
import { servedInlineSkills } from '../models/server-config.js';
|
|
13
|
+
/** True only for `skills = inline` in the chat lane, on a TTY, where the provider can serve it. */
|
|
14
|
+
export function inlineActive({ skills, lane, interactive, served }) {
|
|
15
|
+
return interactive && served && skills === 'inline' && lane === 'chat';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Can this provider serve the inline system block? Managed: only when the
|
|
19
|
+
* proxy's served config says `inline_skills: true` (an old proxy, the kill
|
|
20
|
+
* switch or a stale cache ⇒ false). byok / ai-hub / azure / local build the
|
|
21
|
+
* system block on the laptop ⇒ true. Read at submit time: the served config
|
|
22
|
+
* loads after the config is read at startup.
|
|
23
|
+
*/
|
|
24
|
+
export function inlineServed(mode) {
|
|
25
|
+
if (mode === undefined)
|
|
26
|
+
return false;
|
|
27
|
+
return mode === 'managed' ? servedInlineSkills() : true;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The `config show` comment for a stored `inline` that would not be active in
|
|
31
|
+
* an interactive session; null when there is nothing to say.
|
|
32
|
+
*/
|
|
33
|
+
export function inlineInactiveReason(i) {
|
|
34
|
+
if (i.skills !== 'inline')
|
|
35
|
+
return null;
|
|
36
|
+
if (i.lane !== 'chat')
|
|
37
|
+
return 'not active: skill routing is on';
|
|
38
|
+
if (!i.served)
|
|
39
|
+
return 'not active: this server does not serve it';
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Stage 1A, Task 1A.2 (S5) — an interactive chat-lane process: lane `chat` and
|
|
44
|
+
* a TTY, the condition applyLaneTools uses, whatever `assistant.skills` and the
|
|
45
|
+
* proxy say. In such a process the loop neutralises `<skill`, `<playbook` and
|
|
46
|
+
* `<session_context` in what the person sends (ctx.chatLaneProcess). Decided
|
|
47
|
+
* at every turn site like inlineActive.
|
|
48
|
+
*/
|
|
49
|
+
export function isChatLaneProcess(lane, interactive) {
|
|
50
|
+
return lane === 'chat' && interactive;
|
|
51
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LT-15 Feature 4 — messages typed while CSPeach worked that are still
|
|
3
|
+
* waiting when a dispatch ends (Ink REPL only; classic, headless and one-shot
|
|
4
|
+
* never queue anything).
|
|
5
|
+
*
|
|
6
|
+
* The runner holds the per-dispatch flags (plan review I4): a Ctrl+C press,
|
|
7
|
+
* a turn error, a scheduled hop. They are set during the dispatch, read ONCE
|
|
8
|
+
* by `settle` at that dispatch's end (handleSubmit's finally, after release,
|
|
9
|
+
* or a restore's end), and reset right there, so a stale flag can never touch
|
|
10
|
+
* the next hop or turn.
|
|
11
|
+
*
|
|
12
|
+
* `settle` acts on leftoverDecision:
|
|
13
|
+
* - run: on the next tick, drain the queue THERE (a message typed
|
|
14
|
+
* between the end and the tick rides along) and submit it once
|
|
15
|
+
* as the person's prose. Nothing when a dispatch is already busy
|
|
16
|
+
* (that turn reads the queue at its round 0, or its own end
|
|
17
|
+
* decides) or the session is ending (I4.1, I4.3).
|
|
18
|
+
* - to-input: ask the App to put the text back into the input box (D1, I5).
|
|
19
|
+
* - wait / report-unsent / none: touch nothing. A queued hop reads the
|
|
20
|
+
* message at its round 0 (D3); the exit reports what is left.
|
|
21
|
+
* Every message ends in exactly one place (I1.1).
|
|
22
|
+
*/
|
|
23
|
+
import { drainSteering, drainSteeringText, steeringSnapshot } from '../agent/steering-queue.js';
|
|
24
|
+
import { leftoverDecision, unsentLine } from '../ui/interrupt.js';
|
|
25
|
+
import { formatInfo } from '../renderer/notice-shape.js';
|
|
26
|
+
import { parseShortcut } from '../router/classifier.js';
|
|
27
|
+
export function createLeftoverRunner(deps) {
|
|
28
|
+
let ctrlCStop = false;
|
|
29
|
+
let errorEnd = false;
|
|
30
|
+
let dispatchScheduled = false;
|
|
31
|
+
const reset = () => {
|
|
32
|
+
ctrlCStop = false;
|
|
33
|
+
errorEnd = false;
|
|
34
|
+
dispatchScheduled = false;
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
noteCtrlC() { ctrlCStop = true; },
|
|
38
|
+
noteError() { errorEnd = true; },
|
|
39
|
+
noteDispatchScheduled() { dispatchScheduled = true; },
|
|
40
|
+
reset,
|
|
41
|
+
settle(s) {
|
|
42
|
+
const decision = leftoverDecision({
|
|
43
|
+
done: deps.isDone(),
|
|
44
|
+
exitRequested: s.exitRequested,
|
|
45
|
+
forceExit: s.forceExit === true,
|
|
46
|
+
ctrlCStop,
|
|
47
|
+
braked: s.braked === true,
|
|
48
|
+
errorEnd,
|
|
49
|
+
dispatchScheduled,
|
|
50
|
+
restoreActive: s.restoreActive === true,
|
|
51
|
+
waiting: steeringSnapshot().length,
|
|
52
|
+
});
|
|
53
|
+
reset();
|
|
54
|
+
if (decision === 'run') {
|
|
55
|
+
deps.schedule(() => {
|
|
56
|
+
if (deps.isDone() || deps.isBusy())
|
|
57
|
+
return;
|
|
58
|
+
const text = drainSteeringText();
|
|
59
|
+
if (text !== null)
|
|
60
|
+
deps.submit(text);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else if (decision === 'to-input') {
|
|
64
|
+
deps.toBox();
|
|
65
|
+
}
|
|
66
|
+
return decision;
|
|
67
|
+
},
|
|
68
|
+
toBoxOrReport() {
|
|
69
|
+
if (steeringSnapshot().length === 0)
|
|
70
|
+
return;
|
|
71
|
+
deps.toBox();
|
|
72
|
+
const left = drainSteering();
|
|
73
|
+
if (left.length > 0)
|
|
74
|
+
deps.print(formatInfo(unsentLine(left.length)));
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** At exit: drain what still waits and return the one line to print, or null. */
|
|
79
|
+
export function takeUnsentReport() {
|
|
80
|
+
const left = drainSteering();
|
|
81
|
+
return left.length > 0 ? formatInfo(unsentLine(left.length)) : null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* LT-15 F4 — how a submitted line is read. A replayed waiting message is
|
|
85
|
+
* prose: never a slash command or an @-shortcut, so "/sap/bc/adt/x is slow"
|
|
86
|
+
* reaches the model as text. Anything typed at the prompt is read as today.
|
|
87
|
+
*/
|
|
88
|
+
export function parseSubmitted(input, prose) {
|
|
89
|
+
return prose ? { prompt: input } : parseShortcut(input);
|
|
90
|
+
}
|
|
@@ -13,7 +13,13 @@ export async function runRerouteTurn(p) {
|
|
|
13
13
|
await p.run();
|
|
14
14
|
}
|
|
15
15
|
catch (err) {
|
|
16
|
-
|
|
16
|
+
const stopped = p.signal?.aborted ?? false;
|
|
17
|
+
const line = stopped ? p.stopLine?.() ?? null : null;
|
|
18
|
+
if (line !== null)
|
|
19
|
+
p.print(line);
|
|
20
|
+
else
|
|
21
|
+
await renderTurnError({ err, session: p.session, userInterrupted: stopped, inSession: true, print: p.print });
|
|
22
|
+
p.onError?.();
|
|
17
23
|
}
|
|
18
24
|
finally {
|
|
19
25
|
p.onFinally();
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LT-15 Task 5b — how the current dispatch was stopped (Ink REPL only;
|
|
3
|
+
* classic, headless and one-shot have no Esc brake).
|
|
4
|
+
*
|
|
5
|
+
* Per dispatch (plan review I4): set by the presses, read by autoDispatch
|
|
6
|
+
* (a queued hop / lane plan / switch is not started after ANY stop) and by
|
|
7
|
+
* the leftover runner's settle, then reset in handleSubmit's finally right
|
|
8
|
+
* after that settle, and at a restore's end. Never reset at controller
|
|
9
|
+
* creation, so a stale stop can never touch the next hop or turn.
|
|
10
|
+
*
|
|
11
|
+
* `press` is the Esc brake. It reuses what Ctrl+C already does — the SAME
|
|
12
|
+
* `currentTurnAbort.abort()` (deps.abortTurn) or, in the post-turn window,
|
|
13
|
+
* the SAME pending cancel (deps.cancelPending) — and never touches Ctrl+C's
|
|
14
|
+
* press count (review C1): after an Esc, one Ctrl+C is still Ctrl+C's FIRST
|
|
15
|
+
* press, never the force-exit.
|
|
16
|
+
*/
|
|
17
|
+
import { steeringSnapshot } from '../agent/steering-queue.js';
|
|
18
|
+
import { decideEscape, brakeLine, brakeEndLine } from '../ui/interrupt.js';
|
|
19
|
+
export function createTurnStop(deps) {
|
|
20
|
+
let braked = false;
|
|
21
|
+
let ctrlC = false;
|
|
22
|
+
const waiting = deps.waiting ?? (() => steeringSnapshot().length);
|
|
23
|
+
const decide = (ui) => decideEscape({
|
|
24
|
+
...ui,
|
|
25
|
+
turnActive: deps.turnActive(),
|
|
26
|
+
busy: deps.busy(),
|
|
27
|
+
restoreActive: deps.restoreActive(),
|
|
28
|
+
phase: deps.phase(),
|
|
29
|
+
canChain: deps.canChain(),
|
|
30
|
+
alreadyStopped: braked || ctrlC || deps.exitRequested(),
|
|
31
|
+
});
|
|
32
|
+
const brake = () => {
|
|
33
|
+
braked = true;
|
|
34
|
+
deps.print(brakeLine(waiting() > 0));
|
|
35
|
+
deps.abortTurn();
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
stopFromCard() {
|
|
39
|
+
// The card itself is the overlay that owned the key: decide as if
|
|
40
|
+
// nothing else were open. Only a running turn is stopped.
|
|
41
|
+
if (decide({ overlayOpen: false, bufferCount: 0, fuzzyOpen: false }) !== 'brake')
|
|
42
|
+
return 'none';
|
|
43
|
+
brake();
|
|
44
|
+
return 'brake';
|
|
45
|
+
},
|
|
46
|
+
press(ui) {
|
|
47
|
+
const action = decide(ui);
|
|
48
|
+
if (action === 'brake') {
|
|
49
|
+
brake();
|
|
50
|
+
}
|
|
51
|
+
else if (action === 'cancel-pending') {
|
|
52
|
+
braked = true;
|
|
53
|
+
deps.cancelPending();
|
|
54
|
+
}
|
|
55
|
+
return action;
|
|
56
|
+
},
|
|
57
|
+
noteCtrlCStop() { ctrlC = true; },
|
|
58
|
+
get braked() { return braked; },
|
|
59
|
+
get stopped() { return braked || ctrlC; },
|
|
60
|
+
get brakedOnly() { return braked && !ctrlC; },
|
|
61
|
+
endLine() { return brakeEndLine(waiting() > 0); },
|
|
62
|
+
reset() { braked = false; ctrlC = false; },
|
|
63
|
+
};
|
|
64
|
+
}
|