@alisio/alisio-code 0.1.0-alpha.11 → 0.1.0-alpha.12
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/tui/app.js +26 -3
- package/dist/tui/components.d.ts +2 -0
- package/dist/tui/components.js +15 -0
- package/dist/tui/exit.d.ts +10 -0
- package/dist/tui/exit.js +15 -0
- package/dist/tui/git-branch.d.ts +48 -0
- package/dist/tui/git-branch.js +99 -0
- package/dist/tui/settings.js +2 -2
- package/dist/tui/state.js +1 -1
- package/package.json +8 -8
package/dist/tui/app.js
CHANGED
|
@@ -5,6 +5,8 @@ import { MAX_ATTACHMENTS_PER_MESSAGE, MAX_IMAGE_BYTES, pasteImageFromClipboard,
|
|
|
5
5
|
import { copyText, nodeSpawn } from "./clipboard.js";
|
|
6
6
|
import { AttachmentsBar, BannerBlock, clock, Footer, Header, QuestionPanel, Switch, TranscriptSync, TreePanel, } from "./components.js";
|
|
7
7
|
import { ConnectInputPrompt } from "./connect-input.js";
|
|
8
|
+
import { bounded, EXIT_PENDING_CAP_MS, EXIT_SESSION_END_CAP_MS } from "./exit.js";
|
|
9
|
+
import { BRANCH_REFRESH_MS, createBranchCache } from "./git-branch.js";
|
|
8
10
|
import { initialPanelState, reducePanel, visibleRows } from "./panel.js";
|
|
9
11
|
import { summarizeAnswers } from "./questions.js";
|
|
10
12
|
import { InteractiveQueue } from "./queue.js";
|
|
@@ -153,6 +155,21 @@ export async function runTui(options) {
|
|
|
153
155
|
}, ms);
|
|
154
156
|
tui.requestRender();
|
|
155
157
|
};
|
|
158
|
+
// Git branch of the workspace for the header. Reading refs is a read operation, so it stays on
|
|
159
|
+
// under --read-only; the TTL cache means git is spawned at most once per ~10s, never per frame,
|
|
160
|
+
// and a missing repo/git or a slow spawn simply yields no branch segment.
|
|
161
|
+
const branchCache = createBranchCache();
|
|
162
|
+
let branchName;
|
|
163
|
+
const refreshBranch = () => {
|
|
164
|
+
void branchCache.read(app.workspace).then((branch) => {
|
|
165
|
+
if (branch !== branchName) {
|
|
166
|
+
branchName = branch;
|
|
167
|
+
tui.requestRender();
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
refreshBranch();
|
|
172
|
+
const branchTimer = setInterval(refreshBranch, BRANCH_REFRESH_MS);
|
|
156
173
|
const headerInfo = () => {
|
|
157
174
|
const policy = app.runner.policy, ask = app.runner.approvals;
|
|
158
175
|
return {
|
|
@@ -162,6 +179,7 @@ export async function runTui(options) {
|
|
|
162
179
|
provider: app.providers.get(activeProvider?.id ?? "")?.name,
|
|
163
180
|
cwd: shortenPath(app.workspace, homedir()),
|
|
164
181
|
session: shortId(session),
|
|
182
|
+
branch: branchName,
|
|
165
183
|
write: policy.write ? "on" : ask ? "ask" : "off",
|
|
166
184
|
process: policy.process ? "on" : ask ? "ask" : "off",
|
|
167
185
|
mcp: app.mcpRuntimePermission() === "granted",
|
|
@@ -479,7 +497,7 @@ export async function runTui(options) {
|
|
|
479
497
|
const endSession = async (reason) => {
|
|
480
498
|
if (!view.stats.runs || !app.plugins.hasSessionEndHooks)
|
|
481
499
|
return;
|
|
482
|
-
flashHint("Running session-end plugin hooks
|
|
500
|
+
flashHint("Running session-end plugin hooks…", 60_000);
|
|
483
501
|
tui.renderNow?.();
|
|
484
502
|
try {
|
|
485
503
|
const { failures } = await app.endSession(session, reason);
|
|
@@ -499,8 +517,12 @@ export async function runTui(options) {
|
|
|
499
517
|
return;
|
|
500
518
|
stopping = true;
|
|
501
519
|
controller?.abort(new Error("Exiting"));
|
|
502
|
-
|
|
503
|
-
|
|
520
|
+
// Exit must feel instant: an in-flight turn gets up to 3s (the abort above settles it
|
|
521
|
+
// immediately in practice), then session-end hooks get a short cap — /clear still honors
|
|
522
|
+
// the full pluginHooks.sessionEndTimeoutMs. app.close() later in the main path is itself
|
|
523
|
+
// parallel and capped, so the whole exit path is bounded end to end.
|
|
524
|
+
await bounded(pending?.catch(() => { }), EXIT_PENDING_CAP_MS);
|
|
525
|
+
await bounded(endSession("exit"), EXIT_SESSION_END_CAP_MS);
|
|
504
526
|
resolveExit();
|
|
505
527
|
};
|
|
506
528
|
const askInput = (input) => new Promise((resolve) => {
|
|
@@ -1540,6 +1562,7 @@ export async function runTui(options) {
|
|
|
1540
1562
|
}
|
|
1541
1563
|
finally {
|
|
1542
1564
|
clearInterval(ticker);
|
|
1565
|
+
clearInterval(branchTimer);
|
|
1543
1566
|
clearTimeout(hintTimer);
|
|
1544
1567
|
process.off("SIGTERM", onSignal);
|
|
1545
1568
|
process.off("SIGHUP", onSignal);
|
package/dist/tui/components.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface HeaderInfo {
|
|
|
17
17
|
provider?: string;
|
|
18
18
|
cwd: string;
|
|
19
19
|
session: string;
|
|
20
|
+
/** Git branch (or short SHA on a detached HEAD) of the session workspace; undefined when unavailable. */
|
|
21
|
+
branch?: string;
|
|
20
22
|
write: PermissionState;
|
|
21
23
|
process: PermissionState;
|
|
22
24
|
mcp: boolean;
|
package/dist/tui/components.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Container, getCapabilities, Image, Key, Markdown, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "@earendil-works/pi-tui";
|
|
2
|
+
import { terminalCapabilities } from "../banner.js";
|
|
2
3
|
import { attachmentCaption, MAX_ATTACHMENTS_PER_MESSAGE, } from "./attachments.js";
|
|
4
|
+
import { branchDisplay } from "./git-branch.js";
|
|
3
5
|
import { initialQuestionState, reduceQuestions, } from "./questions.js";
|
|
4
6
|
import { contextLevel, contextPercent, editSummary, fitSegments, formatContext, formatDuration, formatTokens, } from "./state.js";
|
|
5
7
|
import { imageTheme, levelColor, markdownTheme, style } from "./theme.js";
|
|
@@ -53,6 +55,19 @@ export class Header {
|
|
|
53
55
|
const line2 = line([
|
|
54
56
|
{ text: i.cwd, priority: 6, paint: style.cyan },
|
|
55
57
|
{ text: `session ${i.session}`, priority: 4, paint: style.gray },
|
|
58
|
+
// Priority 3 (below session's 4): the branch is ambient context, so on narrow terminals it
|
|
59
|
+
// drops first — before the session ID and permissions. At equal priority fitSegments drops
|
|
60
|
+
// the FIRST lowest-priority segment, which would mis-drop the session when branch sits
|
|
61
|
+
// after it, so the branch keeps a strictly lower priority instead.
|
|
62
|
+
...(i.branch
|
|
63
|
+
? [
|
|
64
|
+
{
|
|
65
|
+
text: branchDisplay(i.branch, terminalCapabilities({ env: process.env, columns: width, tty: true }).unicode),
|
|
66
|
+
priority: 3,
|
|
67
|
+
paint: style.cyan,
|
|
68
|
+
},
|
|
69
|
+
]
|
|
70
|
+
: []),
|
|
56
71
|
...perms,
|
|
57
72
|
], width);
|
|
58
73
|
return fit([line1, line2, style.gray("─".repeat(Math.max(0, width)))], width);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive-exit bounds: `/exit`, double Ctrl+C on an empty input, Ctrl+D and SIGINT/SIGTERM
|
|
3
|
+
* must feel instant, so each shutdown step is raced against a cap instead of awaiting teardown
|
|
4
|
+
* without limits. The plugin host's own hook timeout (`pluginHooks.sessionEndTimeoutMs`) still
|
|
5
|
+
* applies to `/clear`; exit deliberately uses shorter user-facing caps on the same hooks.
|
|
6
|
+
*/
|
|
7
|
+
export declare const EXIT_PENDING_CAP_MS = 3000;
|
|
8
|
+
export declare const EXIT_SESSION_END_CAP_MS = 1500;
|
|
9
|
+
/** Resolves when `work` settles or after `capMs`, whichever comes first; never rejects. */
|
|
10
|
+
export declare function bounded(work: Promise<unknown> | undefined, capMs: number): Promise<void>;
|
package/dist/tui/exit.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive-exit bounds: `/exit`, double Ctrl+C on an empty input, Ctrl+D and SIGINT/SIGTERM
|
|
3
|
+
* must feel instant, so each shutdown step is raced against a cap instead of awaiting teardown
|
|
4
|
+
* without limits. The plugin host's own hook timeout (`pluginHooks.sessionEndTimeoutMs`) still
|
|
5
|
+
* applies to `/clear`; exit deliberately uses shorter user-facing caps on the same hooks.
|
|
6
|
+
*/
|
|
7
|
+
export const EXIT_PENDING_CAP_MS = 3_000;
|
|
8
|
+
export const EXIT_SESSION_END_CAP_MS = 1_500;
|
|
9
|
+
/** Resolves when `work` settles or after `capMs`, whichever comes first; never rejects. */
|
|
10
|
+
export function bounded(work, capMs) {
|
|
11
|
+
return Promise.race([
|
|
12
|
+
Promise.resolve(work).catch(() => { }),
|
|
13
|
+
new Promise((resolve) => setTimeout(resolve, capMs)),
|
|
14
|
+
]).then(() => undefined);
|
|
15
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
/** Hard cap for branch names shown in the header, so a hostile/long ref never breaks layout. */
|
|
3
|
+
export declare const BRANCH_MAX_LENGTH = 24;
|
|
4
|
+
/** How long a resolved branch is reused before git is asked again (avoids spawning per frame). */
|
|
5
|
+
export declare const BRANCH_TTL_MS = 10000;
|
|
6
|
+
/** Re-check cadence; matches the cache TTL so the header follows branch switches within ~10s. */
|
|
7
|
+
export declare const BRANCH_REFRESH_MS = 10000;
|
|
8
|
+
/** Bounded spawn: a slow or hung git must never stall the TUI. */
|
|
9
|
+
export declare const BRANCH_TIMEOUT_MS = 800;
|
|
10
|
+
type Spawn = typeof execFile;
|
|
11
|
+
/**
|
|
12
|
+
* Strips ANSI escapes and control characters, then truncates to `BRANCH_MAX_LENGTH` chars with an
|
|
13
|
+
* ellipsis. Branch names come from the workspace repository, not from user input, but a hostile or
|
|
14
|
+
* corrupted ref must never inject escape sequences or overflow the header line.
|
|
15
|
+
*/
|
|
16
|
+
export declare function sanitizeBranchName(name: string): string;
|
|
17
|
+
/** Header segment text: `⎇ main` on unicode terminals, `branch main` otherwise. */
|
|
18
|
+
export declare function branchDisplay(branch: string, unicode: boolean): string;
|
|
19
|
+
export interface ReadBranchOptions {
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
/** Injectable for tests; defaults to node:child_process execFile. */
|
|
22
|
+
spawn?: Spawn;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Reads the branch of the git repository at `workspace` (read-only ref query, safe under
|
|
26
|
+
* `--read-only`). Returns the branch name, the short commit SHA on a detached HEAD, or `undefined`
|
|
27
|
+
* when the directory is missing, is not a git repository, git itself is unavailable, or the call
|
|
28
|
+
* times out. All failures are silent: stderr is captured and never printed.
|
|
29
|
+
*/
|
|
30
|
+
export declare function readGitBranch(workspace: string, options?: ReadBranchOptions): Promise<string | undefined>;
|
|
31
|
+
export interface BranchCache {
|
|
32
|
+
/** Cached branch for `workspace`, or a fresh read when the entry expired; dedupes in-flight reads. */
|
|
33
|
+
read(workspace: string): Promise<string | undefined>;
|
|
34
|
+
clear(workspace: string): void;
|
|
35
|
+
}
|
|
36
|
+
export interface BranchCacheOptions {
|
|
37
|
+
ttlMs?: number;
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
spawn?: Spawn;
|
|
40
|
+
/** Injectable clock for expiry tests. */
|
|
41
|
+
now?: () => number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* TTL cache keyed by workspace so the TUI never spawns git on every frame: one read per workspace
|
|
45
|
+
* per `ttlMs` (hits and misses are both cached), with a single in-flight promise per key.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createBranchCache(options?: BranchCacheOptions): BranchCache;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
/** Hard cap for branch names shown in the header, so a hostile/long ref never breaks layout. */
|
|
4
|
+
export const BRANCH_MAX_LENGTH = 24;
|
|
5
|
+
/** How long a resolved branch is reused before git is asked again (avoids spawning per frame). */
|
|
6
|
+
export const BRANCH_TTL_MS = 10_000;
|
|
7
|
+
/** Re-check cadence; matches the cache TTL so the header follows branch switches within ~10s. */
|
|
8
|
+
export const BRANCH_REFRESH_MS = BRANCH_TTL_MS;
|
|
9
|
+
/** Bounded spawn: a slow or hung git must never stall the TUI. */
|
|
10
|
+
export const BRANCH_TIMEOUT_MS = 800;
|
|
11
|
+
/**
|
|
12
|
+
* Strips ANSI escapes and control characters, then truncates to `BRANCH_MAX_LENGTH` chars with an
|
|
13
|
+
* ellipsis. Branch names come from the workspace repository, not from user input, but a hostile or
|
|
14
|
+
* corrupted ref must never inject escape sequences or overflow the header line.
|
|
15
|
+
*/
|
|
16
|
+
export function sanitizeBranchName(name) {
|
|
17
|
+
const clean = name
|
|
18
|
+
.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "")
|
|
19
|
+
.replace(/[\u0000-\u001f\u007f]/g, "")
|
|
20
|
+
.trim();
|
|
21
|
+
const chars = [...clean];
|
|
22
|
+
return chars.length > BRANCH_MAX_LENGTH
|
|
23
|
+
? `${chars.slice(0, BRANCH_MAX_LENGTH - 1).join("")}…`
|
|
24
|
+
: clean;
|
|
25
|
+
}
|
|
26
|
+
/** Header segment text: `⎇ main` on unicode terminals, `branch main` otherwise. */
|
|
27
|
+
export function branchDisplay(branch, unicode) {
|
|
28
|
+
return `${unicode ? "⎇" : "branch"} ${branch}`;
|
|
29
|
+
}
|
|
30
|
+
function runGit(spawn, args, cwd, timeoutMs) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
spawn("git", args, { cwd, timeout: timeoutMs, windowsHide: true }, (error, stdout) => {
|
|
33
|
+
if (error)
|
|
34
|
+
reject(error);
|
|
35
|
+
else
|
|
36
|
+
resolve(stdout);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reads the branch of the git repository at `workspace` (read-only ref query, safe under
|
|
42
|
+
* `--read-only`). Returns the branch name, the short commit SHA on a detached HEAD, or `undefined`
|
|
43
|
+
* when the directory is missing, is not a git repository, git itself is unavailable, or the call
|
|
44
|
+
* times out. All failures are silent: stderr is captured and never printed.
|
|
45
|
+
*/
|
|
46
|
+
export async function readGitBranch(workspace, options = {}) {
|
|
47
|
+
const timeoutMs = options.timeoutMs ?? BRANCH_TIMEOUT_MS;
|
|
48
|
+
if (!existsSync(workspace))
|
|
49
|
+
return undefined;
|
|
50
|
+
const spawn = options.spawn ?? execFile;
|
|
51
|
+
try {
|
|
52
|
+
const ref = (await runGit(spawn, ["rev-parse", "--abbrev-ref", "HEAD"], workspace, timeoutMs)).trim();
|
|
53
|
+
if (ref && ref !== "HEAD")
|
|
54
|
+
return sanitizeBranchName(ref);
|
|
55
|
+
if (ref === "HEAD") {
|
|
56
|
+
const sha = (await runGit(spawn, ["rev-parse", "--short", "HEAD"], workspace, timeoutMs)).trim();
|
|
57
|
+
return sha ? sanitizeBranchName(sha) : undefined;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Not a repository, git missing, or timed out: the header simply shows no branch segment.
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* TTL cache keyed by workspace so the TUI never spawns git on every frame: one read per workspace
|
|
68
|
+
* per `ttlMs` (hits and misses are both cached), with a single in-flight promise per key.
|
|
69
|
+
*/
|
|
70
|
+
export function createBranchCache(options = {}) {
|
|
71
|
+
const ttlMs = options.ttlMs ?? BRANCH_TTL_MS;
|
|
72
|
+
const now = options.now ?? Date.now;
|
|
73
|
+
const entries = new Map();
|
|
74
|
+
const inflight = new Map();
|
|
75
|
+
const read = (workspace) => {
|
|
76
|
+
const hit = entries.get(workspace);
|
|
77
|
+
if (hit && hit.expires > now())
|
|
78
|
+
return Promise.resolve(hit.branch);
|
|
79
|
+
const pending = inflight.get(workspace);
|
|
80
|
+
if (pending)
|
|
81
|
+
return pending;
|
|
82
|
+
const promise = readGitBranch(workspace, {
|
|
83
|
+
timeoutMs: options.timeoutMs,
|
|
84
|
+
spawn: options.spawn,
|
|
85
|
+
})
|
|
86
|
+
.then((branch) => {
|
|
87
|
+
entries.set(workspace, { branch, expires: now() + ttlMs });
|
|
88
|
+
inflight.delete(workspace);
|
|
89
|
+
return branch;
|
|
90
|
+
})
|
|
91
|
+
.catch(() => {
|
|
92
|
+
inflight.delete(workspace);
|
|
93
|
+
return undefined;
|
|
94
|
+
});
|
|
95
|
+
inflight.set(workspace, promise);
|
|
96
|
+
return promise;
|
|
97
|
+
};
|
|
98
|
+
return { read, clear: (workspace) => void entries.delete(workspace) };
|
|
99
|
+
}
|
package/dist/tui/settings.js
CHANGED
|
@@ -11,7 +11,7 @@ export const WEBSEARCH_PROVIDERS = [
|
|
|
11
11
|
export const defaultConfig = {
|
|
12
12
|
compaction: { auto: true, threshold: 0.85, keepTurns: 2, maxOutputTokens: 16_000 },
|
|
13
13
|
context: { claudeMdFallback: false, maxBytes: 32 * 1024 },
|
|
14
|
-
limits: { maxTurns: 20, maxOutputTokens:
|
|
14
|
+
limits: { maxTurns: 20, maxOutputTokens: 16_384, maxContextChars: 800_000, timeoutMs: 300_000 },
|
|
15
15
|
tui: { paddingX: 1, skillSlashCommands: true },
|
|
16
16
|
mcp: { allow: false },
|
|
17
17
|
websearch: { provider: undefined },
|
|
@@ -128,7 +128,7 @@ export const SETTINGS_DEFINITIONS = [
|
|
|
128
128
|
label: "Context char budget",
|
|
129
129
|
category: "Limits",
|
|
130
130
|
valueType: "number",
|
|
131
|
-
values: [80_000, 120_000, 160_000, 240_000, 320_000],
|
|
131
|
+
values: [80_000, 120_000, 160_000, 240_000, 320_000, 800_000],
|
|
132
132
|
read: (config) => config.limits.maxContextChars,
|
|
133
133
|
description: "Hard context limit in characters (instruction files + transcript + tool list) per run; the budget fallback that auto-compaction measures when the model window is unknown. Applied from the next run.",
|
|
134
134
|
},
|
package/dist/tui/state.js
CHANGED
|
@@ -591,7 +591,7 @@ export function reduceEvent(state, event) {
|
|
|
591
591
|
case "response_truncated":
|
|
592
592
|
return addItem(state, {
|
|
593
593
|
kind: "notice",
|
|
594
|
-
text: "Response cut by max output tokens — the answer may be incomplete. Raise limits.maxOutputTokens to allow longer answers.",
|
|
594
|
+
text: "Response cut by max output tokens — the answer may be incomplete. Raise limits.maxOutputTokens (/settings → Agent max output tokens) to allow longer answers.",
|
|
595
595
|
});
|
|
596
596
|
case "model_changed":
|
|
597
597
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alisio/alisio-code",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.12",
|
|
4
4
|
"description": "Alisio: an extensible, provider-agnostic coding-agent harness for your terminal. TUI, OpenAI-compatible providers, permissioned local tools, context compaction, persistent memory and a typed plugin SDK.",
|
|
5
5
|
"author": "Gustavo Gutiérrez",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"alisio": "./dist/main.js"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@alisio/core": "0.1.0-alpha.
|
|
49
|
-
"@alisio/plugin-deepseek": "0.1.0-alpha.
|
|
48
|
+
"@alisio/core": "0.1.0-alpha.10",
|
|
49
|
+
"@alisio/plugin-deepseek": "0.1.0-alpha.8",
|
|
50
50
|
"@alisio/plugin-memory": "0.1.0-alpha.6",
|
|
51
|
-
"@alisio/plugin-openai-compatible": "0.1.0-alpha.
|
|
52
|
-
"@alisio/plugin-opencode": "0.1.0-alpha.
|
|
53
|
-
"@alisio/plugin-opencode-go": "0.1.0-alpha.
|
|
54
|
-
"@alisio/plugin-subagents": "0.1.0-alpha.
|
|
55
|
-
"@alisio/sdk": "0.1.0-alpha.
|
|
51
|
+
"@alisio/plugin-openai-compatible": "0.1.0-alpha.8",
|
|
52
|
+
"@alisio/plugin-opencode": "0.1.0-alpha.7",
|
|
53
|
+
"@alisio/plugin-opencode-go": "0.1.0-alpha.7",
|
|
54
|
+
"@alisio/plugin-subagents": "0.1.0-alpha.7",
|
|
55
|
+
"@alisio/sdk": "0.1.0-alpha.6",
|
|
56
56
|
"@earendil-works/pi-tui": "0.87.1",
|
|
57
57
|
"commander": "15.0.0"
|
|
58
58
|
},
|