@iseoane/agent-depot 0.4.1 → 0.5.1
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 +45 -0
- package/README.es.md +969 -0
- package/README.md +244 -23
- package/dist/src/app-flow.js +52 -51
- package/dist/src/app-recipe-removal.js +63 -0
- package/dist/src/cli.js +122 -11
- package/dist/src/executable-resolution.js +54 -0
- package/dist/src/git-source.js +150 -6
- package/dist/src/package-bundles.js +614 -0
- package/dist/src/package-cli.js +412 -0
- package/dist/src/package-content.js +158 -0
- package/dist/src/package-flow.js +645 -0
- package/dist/src/package-host-plans.js +243 -0
- package/dist/src/package-host-state.js +473 -0
- package/dist/src/package-model.js +33 -0
- package/dist/src/package-owned-skills.js +124 -0
- package/dist/src/package-state.js +404 -0
- package/dist/src/process-runner.js +9 -1
- package/dist/src/profile-cli.js +15 -9
- package/dist/src/profile-export.js +44 -9
- package/dist/src/profile-import.js +80 -17
- package/dist/src/profile.js +78 -3
- package/dist/src/skill-discovery.js +7 -2
- package/dist/src/skill-install.js +16 -0
- package/dist/src/sources.js +11 -0
- package/dist/src/tui/app-actions.js +1 -1
- package/dist/src/tui/app.js +10 -11
- package/dist/src/tui/catalog-browse.js +46 -13
- package/dist/src/tui/catalog-ownership.js +12 -0
- package/dist/src/tui/catalog-panel.js +16 -4
- package/dist/src/tui/catalog-state.js +62 -6
- package/dist/src/tui/catalog-tree.js +58 -12
- package/dist/src/tui/catalog-view.js +53 -7
- package/dist/src/tui/environment.js +8 -1
- package/dist/src/tui/installations-browse.js +20 -2
- package/dist/src/tui/installations-rows.js +2 -1
- package/dist/src/tui/installations-tree.js +14 -1
- package/dist/src/tui/installations-view.js +8 -2
- package/dist/src/tui/installations.js +12 -2
- package/dist/src/tui/manage-actions.js +2 -0
- package/dist/src/tui/manage-panel.js +5 -0
- package/dist/src/tui/package-actions.js +165 -0
- package/dist/src/tui/package-rows.js +91 -0
- package/dist/src/tui/profile-view.js +36 -14
- package/dist/src/tui/render.js +1 -1
- package/dist/src/tui/sources-mode.js +2 -0
- package/dist/src/tui/sources-recipes.js +116 -53
- package/dist/src/tui/sources-view.js +57 -19
- package/dist/src/tui/tree.js +3 -4
- package/dist/src/tui/updates-flow.js +4 -1
- package/dist/src/tui/updates-panel.js +2 -2
- package/dist/src/tui/updates.js +72 -4
- package/dist/src/tui/window.js +3 -4
- package/dist/src/user-global-skill-inventory.js +7 -2
- package/package.json +2 -1
- package/skills/agent-depot-apprecipe/SKILL.md +29 -5
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describeCoordinates } from "../package-model.js";
|
|
3
|
+
import { errorText } from "./batch.js";
|
|
4
|
+
import { answerYesNo } from "./mode-keys.js";
|
|
5
|
+
import { installedEvidenceLabel, packagePlanLines } from "./package-rows.js";
|
|
6
|
+
import { PreviewConfirm } from "./panel-parts.js";
|
|
7
|
+
/** The per-row keys of a Package; Enter is approval, handled beside these. */
|
|
8
|
+
export const PACKAGE_ACTION_KEYS = new Map([
|
|
9
|
+
["i", "install"],
|
|
10
|
+
["U", "update"],
|
|
11
|
+
["u", "uninstall"],
|
|
12
|
+
["f", "forget"],
|
|
13
|
+
]);
|
|
14
|
+
export const PACKAGE_KINDS = ["package-approve", "package-confirm", "package-forget"];
|
|
15
|
+
/** Narrows the view's mode without asserting a partial mode is a Package mode. */
|
|
16
|
+
export function isPackageMode(mode) {
|
|
17
|
+
return PACKAGE_KINDS.some(kind => kind === mode.kind);
|
|
18
|
+
}
|
|
19
|
+
/** `install`, `update`, `uninstall` and `forget` plan a step; `approve` writes the install receipt. */
|
|
20
|
+
export async function startPackageAction(context, target, input) {
|
|
21
|
+
const { manage, packages } = context;
|
|
22
|
+
manage.setMessage(undefined);
|
|
23
|
+
try {
|
|
24
|
+
if (input === "approve") {
|
|
25
|
+
manage.setMode({ kind: "busy", label: `Preparing the declaration of ${target.name}...` });
|
|
26
|
+
const plan = await packages.planLifecycle(target.selection, "install");
|
|
27
|
+
if (manage.isMounted()) {
|
|
28
|
+
manage.setMode({ kind: "package-approve", target, lines: packagePlanLines(plan, "needs approval") });
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const action = PACKAGE_ACTION_KEYS.get(input);
|
|
33
|
+
if (action === "forget") {
|
|
34
|
+
manage.setMode({ kind: "busy", label: `Preparing to forget ${target.name}...` });
|
|
35
|
+
const inspection = await packages.inspect(target.selection);
|
|
36
|
+
if (manage.isMounted()) {
|
|
37
|
+
manage.setMode({
|
|
38
|
+
kind: "package-forget",
|
|
39
|
+
target,
|
|
40
|
+
lines: [
|
|
41
|
+
`Package: ${target.name} (${target.selection.host})`,
|
|
42
|
+
`Coordinates: ${describeCoordinates(target.selection)}`,
|
|
43
|
+
`Installed: ${installedEvidenceLabel(inspection.installed)}`,
|
|
44
|
+
"Forgetting drops the Agent Depot selection record only; the host install is left as it is and no host command runs.",
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (action !== undefined) {
|
|
51
|
+
manage.setMode({ kind: "busy", label: `Preparing the ${action} of ${target.name}...` });
|
|
52
|
+
const plan = await packages.planLifecycle(target.selection, action);
|
|
53
|
+
const approval = await packages.approvalStatus(target.selection, action);
|
|
54
|
+
if (manage.isMounted())
|
|
55
|
+
manage.setMode({ kind: "package-confirm", plan, approval });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
await manage.finish({ kind: "error", text: `The action ${JSON.stringify(input)} is not available for Packages` });
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
await manage.finish({ kind: "error", text: errorText(error) });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** The `y` of a lifecycle confirmation whose receipt is missing approves it first, then asks again. */
|
|
65
|
+
async function approveThenConfirm(context, mode) {
|
|
66
|
+
const { manage, packages } = context;
|
|
67
|
+
manage.setMode({ kind: "busy", label: `Approving the ${mode.plan.action} declaration of ${mode.plan.descriptor.name}...` });
|
|
68
|
+
try {
|
|
69
|
+
await packages.approve(mode.plan.selection, mode.plan.action);
|
|
70
|
+
if (manage.isMounted())
|
|
71
|
+
manage.setMode({ ...mode, approval: "approved" });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (manage.isMounted())
|
|
75
|
+
await manage.finish({ kind: "error", text: errorText(error) });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function resultLine(plan, result) {
|
|
79
|
+
const name = plan.descriptor.name;
|
|
80
|
+
switch (result.status) {
|
|
81
|
+
case "installed":
|
|
82
|
+
return { ok: true, text: `${name}: installed (${installedEvidenceLabel(result.verified)})` };
|
|
83
|
+
case "updated":
|
|
84
|
+
return { ok: true, text: `${name}: updated (${installedEvidenceLabel(result.previous)} -> ${installedEvidenceLabel(result.verified)})` };
|
|
85
|
+
case "removed":
|
|
86
|
+
return { ok: true, text: `${name}: removed` };
|
|
87
|
+
case "selected":
|
|
88
|
+
return { ok: true, text: `${name}: selected` };
|
|
89
|
+
case "forgotten":
|
|
90
|
+
return { ok: true, text: `${name}: forgotten` };
|
|
91
|
+
case "not confirmed":
|
|
92
|
+
return { ok: true, text: `${name}: not changed` };
|
|
93
|
+
case "stale plan":
|
|
94
|
+
return { ok: false, text: `${name}: ${plan.action} not run: ${result.reason}` };
|
|
95
|
+
case "manual required":
|
|
96
|
+
return { ok: false, text: `${name}: ${plan.action} needs a manual step: ${result.reason}` };
|
|
97
|
+
case "failed":
|
|
98
|
+
return { ok: false, text: `${name}: failed: ${result.reason}${result.output === undefined ? "" : ` (${result.output})`}` };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function executePackagePlan(context, plan) {
|
|
102
|
+
const { manage, packages } = context;
|
|
103
|
+
manage.setMode({ kind: "busy", label: `Running the ${plan.action} of ${plan.descriptor.name}...` });
|
|
104
|
+
let result;
|
|
105
|
+
try {
|
|
106
|
+
result = await packages.executeLifecycle(plan, true);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
await manage.finish({ kind: "error", text: errorText(error) });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const line = resultLine(plan, result);
|
|
113
|
+
await manage.finish({ kind: line.ok ? "ok" : "error", text: line.text });
|
|
114
|
+
}
|
|
115
|
+
async function forgetSelection(context, mode) {
|
|
116
|
+
const { manage, packages } = context;
|
|
117
|
+
manage.setMode({ kind: "busy", label: `Forgetting ${mode.target.name}...` });
|
|
118
|
+
try {
|
|
119
|
+
await packages.forget(mode.target.selection, true);
|
|
120
|
+
await manage.finish({ kind: "ok", text: `Forgot ${mode.target.name}; no host command ran` });
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
await manage.finish({ kind: "error", text: errorText(error) });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Applies the y/n keys of every Package confirmation; `n` and Esc run nothing. */
|
|
127
|
+
export function handlePackageKey(context, mode, input, key) {
|
|
128
|
+
const { manage, packages } = context;
|
|
129
|
+
const cancel = (text) => {
|
|
130
|
+
manage.setMessage({ kind: "ok", text });
|
|
131
|
+
manage.browse();
|
|
132
|
+
};
|
|
133
|
+
switch (mode.kind) {
|
|
134
|
+
case "package-approve":
|
|
135
|
+
answerYesNo(input, key, () => {
|
|
136
|
+
manage.setMode({ kind: "busy", label: `Approving the install declaration of ${mode.target.name}...` });
|
|
137
|
+
void packages.approve(mode.target.selection, "install").then(() => manage.finish({ kind: "ok", text: `Approved the install declaration of ${mode.target.name}` }), error => manage.finish({ kind: "error", text: errorText(error) }));
|
|
138
|
+
}, () => cancel("Package action cancelled"));
|
|
139
|
+
return;
|
|
140
|
+
case "package-forget":
|
|
141
|
+
answerYesNo(input, key, () => void forgetSelection(context, mode), () => cancel("Package action cancelled"));
|
|
142
|
+
return;
|
|
143
|
+
case "package-confirm":
|
|
144
|
+
answerYesNo(input, key, () => {
|
|
145
|
+
if (mode.approval === "needs approval")
|
|
146
|
+
void approveThenConfirm(context, mode);
|
|
147
|
+
else
|
|
148
|
+
void executePackagePlan(context, mode.plan);
|
|
149
|
+
}, () => cancel("Package action cancelled"));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Renders a Package prompt with the existing preview panel and y/n confirmation. */
|
|
154
|
+
export function PackagePanel({ mode }) {
|
|
155
|
+
switch (mode.kind) {
|
|
156
|
+
case "package-approve":
|
|
157
|
+
return _jsx(PreviewConfirm, { lines: mode.lines, question: `Approve this install declaration for ${mode.target.name}? y/n` });
|
|
158
|
+
case "package-forget":
|
|
159
|
+
return _jsx(PreviewConfirm, { lines: mode.lines, question: `Forget the selection record of ${mode.target.name}? y/n` });
|
|
160
|
+
case "package-confirm":
|
|
161
|
+
return (_jsx(PreviewConfirm, { lines: packagePlanLines(mode.plan, mode.approval), question: mode.approval === "needs approval"
|
|
162
|
+
? "Approve this declaration and continue? y/n"
|
|
163
|
+
: `${mode.plan.action} ${mode.plan.descriptor.name}? y/n` }));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { classifyUpdate } from "../package-host-plans.js";
|
|
2
|
+
import { describeCoordinates, } from "../package-model.js";
|
|
3
|
+
/** Version evidence as a short label; `unknown` when the host or the bundle states none. */
|
|
4
|
+
export function packageVersionLabel(evidence) {
|
|
5
|
+
if (evidence === undefined)
|
|
6
|
+
return "unknown";
|
|
7
|
+
return evidence.kind === "manifest-version" ? evidence.version : evidence.commit.slice(0, 7);
|
|
8
|
+
}
|
|
9
|
+
/** What the host reports about an install, as one phrase. Never a guess. */
|
|
10
|
+
export function installedEvidenceLabel(evidence) {
|
|
11
|
+
switch (evidence.kind) {
|
|
12
|
+
case "installed": {
|
|
13
|
+
if (evidence.reason !== undefined) {
|
|
14
|
+
const reason = evidence.reason.includes("local modifications") ? "local changes" : evidence.reason;
|
|
15
|
+
return `installed (version unavailable: ${reason})`;
|
|
16
|
+
}
|
|
17
|
+
if (evidence.version !== undefined)
|
|
18
|
+
return `installed ${evidence.version}`;
|
|
19
|
+
if (evidence.commit !== undefined)
|
|
20
|
+
return `installed at ${evidence.commit.slice(0, 7)}`;
|
|
21
|
+
return "installed with no version evidence";
|
|
22
|
+
}
|
|
23
|
+
case "absent": {
|
|
24
|
+
return "not installed";
|
|
25
|
+
}
|
|
26
|
+
case "unknown": {
|
|
27
|
+
return `unknown (${evidence.reason})`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** The shared update vocabulary; the Updates list prints `up to date`, not `current`. */
|
|
32
|
+
export function packageStatusLabel(status) {
|
|
33
|
+
if (status === "current")
|
|
34
|
+
return "up to date";
|
|
35
|
+
if (status === "unknown")
|
|
36
|
+
return "cannot assess";
|
|
37
|
+
return status;
|
|
38
|
+
}
|
|
39
|
+
/** What a selection record itself remembers, for lists that do not read host evidence. */
|
|
40
|
+
export function recordedVersionLabel(record) {
|
|
41
|
+
if (record.verified !== undefined)
|
|
42
|
+
return packageVersionLabel(record.verified);
|
|
43
|
+
if (record.lastDelegatedCommit !== undefined)
|
|
44
|
+
return record.lastDelegatedCommit.slice(0, 7);
|
|
45
|
+
return "unknown";
|
|
46
|
+
}
|
|
47
|
+
export function packageRow(record, inspection) {
|
|
48
|
+
return {
|
|
49
|
+
selection: record.selection,
|
|
50
|
+
name: inspection.descriptor?.name ?? describeCoordinates(record.selection),
|
|
51
|
+
installed: installedEvidenceLabel(inspection.installed),
|
|
52
|
+
available: packageVersionLabel(inspection.available),
|
|
53
|
+
status: classifyUpdate(inspection.installed, inspection.available, inspection.availableCommit, inspection.installedCommitIsAncestor),
|
|
54
|
+
...(inspection.drift === undefined ? {} : { drift: inspection.drift }),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Host, evidence and status of one row, as the Installations line prints it. */
|
|
58
|
+
export function describePackageRow(row) {
|
|
59
|
+
const drift = row.drift === undefined ? "" : ` drift: ${row.drift}`;
|
|
60
|
+
return `${row.selection.host} ${row.installed} available ${row.available} ${packageStatusLabel(row.status)}${drift}`;
|
|
61
|
+
}
|
|
62
|
+
/** One component of the inventory; the executable effect and a host-only owner are called out. */
|
|
63
|
+
export function describePackageComponent(component) {
|
|
64
|
+
const risk = component.effect === "executable" ? " runs a host process" : "";
|
|
65
|
+
const ownership = component.ownership === "host-only" ? " host-only: Agent Depot never manages it" : "";
|
|
66
|
+
const paths = component.paths.length === 0 ? "" : ` ${component.paths.join(", ")}`;
|
|
67
|
+
return `${component.kind} ${component.ownership} ${component.effect}${risk}${ownership}${paths}`;
|
|
68
|
+
}
|
|
69
|
+
/** The preview lines of one frozen plan: origin, inventory, argv, blast radius and approval state. */
|
|
70
|
+
export function packagePlanLines(plan, approval) {
|
|
71
|
+
const { descriptor, selection } = plan;
|
|
72
|
+
return [
|
|
73
|
+
`Environment: ${plan.environment}`,
|
|
74
|
+
`Package: ${descriptor.name} (${selection.host}) ${plan.action}`,
|
|
75
|
+
`Coordinates: ${describeCoordinates(selection)}`,
|
|
76
|
+
`Origin: manifest ${plan.origin.manifestDigest.slice(0, 12)}${plan.origin.resolvedCommit === undefined ? "" : ` commit ${plan.origin.resolvedCommit.slice(0, 7)}`}`,
|
|
77
|
+
`Available: ${packageVersionLabel(descriptor.version)}`,
|
|
78
|
+
`Components (${descriptor.components.length}):`,
|
|
79
|
+
...descriptor.components.map((component) => ` ${describePackageComponent(component)}`),
|
|
80
|
+
...(plan.commands.length === 0
|
|
81
|
+
? ["This action runs no host command."]
|
|
82
|
+
: plan.commands.flatMap((command, position) => [
|
|
83
|
+
`Step ${position + 1} (${command.purpose}): ${JSON.stringify([command.executable, ...command.args])}`,
|
|
84
|
+
`Executable: ${command.resolvedPath ?? command.executable}`,
|
|
85
|
+
])),
|
|
86
|
+
`Blast radius: ${plan.affects.map(describeCoordinates).join(", ")}`,
|
|
87
|
+
`Working directory: ${plan.cwd}`,
|
|
88
|
+
`Approval: ${approval === "approved" ? "approved" : "needs approval"}`,
|
|
89
|
+
...plan.warnings.map((warning) => `Warning: ${warning}`),
|
|
90
|
+
];
|
|
91
|
+
}
|
|
@@ -3,9 +3,10 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
4
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { createAppOperations } from "../app-flow.js";
|
|
6
|
-
import { buildProfileExport, profileExportPreview, writeProfileExport, } from "../profile-export.js";
|
|
6
|
+
import { buildProfileExport, describeProfilePackage, profileExportPreview, writeProfileExport, } from "../profile-export.js";
|
|
7
7
|
import { applyProfileImport, buildProfileImport, } from "../profile-import.js";
|
|
8
8
|
import { PROFILE_FORMAT } from "../profile.js";
|
|
9
|
+
import { packageOperationsOf } from "./environment.js";
|
|
9
10
|
import { useKeys } from "./keys.js";
|
|
10
11
|
import { answerYesNo, moveCursor, toggleChoice } from "./mode-keys.js";
|
|
11
12
|
import { ChecklistLines, PreviewConfirm, PreviewLines } from "./panel-parts.js";
|
|
@@ -21,6 +22,9 @@ function rowsFromExport(plan) {
|
|
|
21
22
|
...plan.profile.apps.map((value, index) => ({
|
|
22
23
|
block: "apps", index, label: `Apps: ${value.name} (${value.platform ?? "all platforms"})`, selectable: true,
|
|
23
24
|
})),
|
|
25
|
+
...plan.profile.packages.map((value, index) => ({
|
|
26
|
+
block: "packages", index, label: `Packages: ${describeProfilePackage(value)}`, selectable: true,
|
|
27
|
+
})),
|
|
24
28
|
...plan.exclusions.map((item, index) => ({
|
|
25
29
|
block: "excluded", index, label: `Excluded ${item.label}: ${item.reason}`, selectable: false,
|
|
26
30
|
})),
|
|
@@ -29,21 +33,35 @@ function rowsFromExport(plan) {
|
|
|
29
33
|
})),
|
|
30
34
|
];
|
|
31
35
|
}
|
|
36
|
+
/** The four import blocks in one indexed list, so the checklist and the confirmation share positions. */
|
|
37
|
+
function planEntries(plan) {
|
|
38
|
+
return [...plan.items, ...plan.packages];
|
|
39
|
+
}
|
|
32
40
|
function rowsFromPlan(plan) {
|
|
33
|
-
return plan.
|
|
41
|
+
return planEntries(plan).map((item, index) => ({
|
|
34
42
|
block: item.block, index,
|
|
35
43
|
label: `${item.status}: ${item.label}${item.difference ? `; ${item.difference}` : ""}`,
|
|
36
44
|
selectable: item.status === "add",
|
|
37
45
|
}));
|
|
38
46
|
}
|
|
39
|
-
function
|
|
40
|
-
return plan.
|
|
47
|
+
function selectedPlanEntries(plan, selected) {
|
|
48
|
+
return planEntries(plan).filter((item, index) => item.status !== "add" || selected.some(row => row.index === index));
|
|
49
|
+
}
|
|
50
|
+
/** A plan whose items are the chosen subset, split back into the block the writer consumes. */
|
|
51
|
+
function chosenPlan(plan, selected) {
|
|
52
|
+
const entries = selectedPlanEntries(plan, selected);
|
|
53
|
+
return {
|
|
54
|
+
...plan,
|
|
55
|
+
items: entries.filter((item) => item.block !== "packages"),
|
|
56
|
+
packages: entries.filter((item) => item.block === "packages"),
|
|
57
|
+
};
|
|
41
58
|
}
|
|
42
59
|
/** Collect choices, display shared-core previews and confirm portable Profile transfers. */
|
|
43
60
|
export function ProfileView({ operations, environment = {}, onCapturingChange, listHeight }) {
|
|
44
61
|
const apps = useMemo(() => createAppOperations({
|
|
45
62
|
homeDirectory: environment.homeDirectory, ...environment.appEnvironment,
|
|
46
63
|
}), [environment]);
|
|
64
|
+
const packages = useMemo(() => packageOperationsOf(environment), [environment]);
|
|
47
65
|
const [mode, setModeState] = useState("browse");
|
|
48
66
|
const modeRef = useRef("browse");
|
|
49
67
|
const mounted = useRef(true);
|
|
@@ -113,6 +131,7 @@ export function ProfileView({ operations, environment = {}, onCapturingChange, l
|
|
|
113
131
|
sources: profile.sources.filter((_, index) => isChosen("sources", index)),
|
|
114
132
|
skills: profile.skills.filter((_, index) => isChosen("skills", index)),
|
|
115
133
|
apps: profile.apps.filter((_, index) => isChosen("apps", index)),
|
|
134
|
+
packages: profile.packages.filter((_, index) => isChosen("packages", index)),
|
|
116
135
|
};
|
|
117
136
|
};
|
|
118
137
|
useKeys((input, key) => {
|
|
@@ -123,7 +142,9 @@ export function ProfileView({ operations, environment = {}, onCapturingChange, l
|
|
|
123
142
|
if (input === "e") {
|
|
124
143
|
action.current = "export";
|
|
125
144
|
void run(async () => {
|
|
126
|
-
const exported = await buildProfileExport(operations, apps, {
|
|
145
|
+
const exported = await buildProfileExport(operations, apps, {
|
|
146
|
+
homeDirectory: environment.homeDirectory, packageOperations: packages,
|
|
147
|
+
});
|
|
127
148
|
if (!mounted.current)
|
|
128
149
|
return;
|
|
129
150
|
setProfile(exported.profile);
|
|
@@ -156,7 +177,7 @@ export function ProfileView({ operations, environment = {}, onCapturingChange, l
|
|
|
156
177
|
else
|
|
157
178
|
void run(async () => {
|
|
158
179
|
const raw = JSON.parse(await readFile(typed.current, "utf8"));
|
|
159
|
-
const imported = await buildProfileImport(raw, operations, apps);
|
|
180
|
+
const imported = await buildProfileImport(raw, operations, apps, {}, packages);
|
|
160
181
|
profileVersion.current = raw.agentDepotVersion;
|
|
161
182
|
if (!mounted.current)
|
|
162
183
|
return;
|
|
@@ -183,8 +204,8 @@ export function ProfileView({ operations, environment = {}, onCapturingChange, l
|
|
|
183
204
|
result = [`Exported profile to ${typed.current}`];
|
|
184
205
|
}
|
|
185
206
|
else {
|
|
186
|
-
const confirmed =
|
|
187
|
-
const outcomes = await applyProfileImport(confirmed, operations, apps, true, () => undefined, { homeDirectory: environment.homeDirectory, ...environment.installationOptions });
|
|
207
|
+
const confirmed = chosenPlan(plan, selectedRef.current);
|
|
208
|
+
const outcomes = await applyProfileImport(confirmed, operations, apps, true, () => undefined, { homeDirectory: environment.homeDirectory, packageOperations: packages, ...environment.installationOptions });
|
|
188
209
|
result = outcomes.map(outcome => `${outcome.status}: ${outcome.item.label}${outcome.detail ? `; ${outcome.detail}` : ""}`);
|
|
189
210
|
}
|
|
190
211
|
if (mounted.current) {
|
|
@@ -225,13 +246,14 @@ export function ProfileView({ operations, environment = {}, onCapturingChange, l
|
|
|
225
246
|
else
|
|
226
247
|
void run(async () => {
|
|
227
248
|
// Reuse the CLI's core preview for the exact selected subset, including argv and Source notes.
|
|
228
|
-
const
|
|
249
|
+
const entries = selectedPlanEntries(plan, selectedRef.current);
|
|
229
250
|
const preview = await buildProfileImport({
|
|
230
251
|
format: PROFILE_FORMAT, agentDepotVersion: profileVersion.current,
|
|
231
|
-
sources:
|
|
232
|
-
skills:
|
|
233
|
-
apps:
|
|
234
|
-
|
|
252
|
+
sources: entries.filter(item => item.block === "sources").map(item => item.value),
|
|
253
|
+
skills: entries.filter(item => item.block === "skills").map(item => item.value),
|
|
254
|
+
apps: entries.filter(item => item.block === "apps").map(item => item.value),
|
|
255
|
+
packages: entries.filter(item => item.block === "packages").map(item => item.value),
|
|
256
|
+
}, operations, apps, {}, packages);
|
|
235
257
|
if (mounted.current)
|
|
236
258
|
showLines(preview.preview, "confirm");
|
|
237
259
|
});
|
|
@@ -239,7 +261,7 @@ export function ProfileView({ operations, environment = {}, onCapturingChange, l
|
|
|
239
261
|
});
|
|
240
262
|
const listing = mode === "export-list" || mode === "import-list";
|
|
241
263
|
const window = computeWindow(listing ? rows.length : lines.length, cursor, height);
|
|
242
|
-
return _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Portable user-global Profile \u00B7 e export \u00B7 i import" }), mode === "busy" ? _jsx(Text, { children: "Loading Profile\u2026" }) : null, mode.endsWith("-path") ? _jsxs(Text, { children: ["Profile path: ", text, "_"] }) : null, listing ? ["sources", "skills", "apps", "excluded"].map(block => {
|
|
264
|
+
return _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Portable user-global Profile \u00B7 e export \u00B7 i import" }), mode === "busy" ? _jsx(Text, { children: "Loading Profile\u2026" }) : null, mode.endsWith("-path") ? _jsxs(Text, { children: ["Profile path: ", text, "_"] }) : null, listing ? ["sources", "skills", "apps", "packages", "excluded"].map(block => {
|
|
243
265
|
const choices = rows.slice(window.start, window.end).filter(row => row.block === block);
|
|
244
266
|
if (!choices.length)
|
|
245
267
|
return null;
|
package/dist/src/tui/render.js
CHANGED
|
@@ -3,6 +3,6 @@ import { render } from "ink";
|
|
|
3
3
|
import { App } from "./app.js";
|
|
4
4
|
/** Renders the TUI and resolves once the user exits it. */
|
|
5
5
|
export async function renderTui(operations, environment) {
|
|
6
|
-
const instance = render(_jsx(App, { operations: operations, environment: environment }));
|
|
6
|
+
const instance = render(_jsx(App, { operations: operations, environment: environment }), { alternateScreen: true });
|
|
7
7
|
await instance.waitUntilExit();
|
|
8
8
|
}
|