skill-flow 1.0.8 → 1.3.3
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/README.md +209 -131
- package/README.zh.md +170 -131
- package/dist/bridge-command.d.ts +9 -0
- package/dist/bridge-command.js +422 -0
- package/dist/bridge-command.js.map +1 -0
- package/dist/cli.js +68 -8
- package/dist/cli.js.map +1 -1
- package/package.json +11 -2
- package/dist/adapters/channel-adapters.d.ts +0 -8
- package/dist/adapters/channel-adapters.js +0 -64
- package/dist/adapters/channel-adapters.js.map +0 -1
- package/dist/domain/types.d.ts +0 -234
- package/dist/domain/types.js +0 -2
- package/dist/domain/types.js.map +0 -1
- package/dist/services/config-coordinator.d.ts +0 -38
- package/dist/services/config-coordinator.js +0 -83
- package/dist/services/config-coordinator.js.map +0 -1
- package/dist/services/deployment-applier.d.ts +0 -10
- package/dist/services/deployment-applier.js +0 -84
- package/dist/services/deployment-applier.js.map +0 -1
- package/dist/services/deployment-planner.d.ts +0 -16
- package/dist/services/deployment-planner.js +0 -366
- package/dist/services/deployment-planner.js.map +0 -1
- package/dist/services/doctor-service.d.ts +0 -7
- package/dist/services/doctor-service.js +0 -204
- package/dist/services/doctor-service.js.map +0 -1
- package/dist/services/inventory-service.d.ts +0 -17
- package/dist/services/inventory-service.js +0 -216
- package/dist/services/inventory-service.js.map +0 -1
- package/dist/services/skill-flow.d.ts +0 -136
- package/dist/services/skill-flow.js +0 -1210
- package/dist/services/skill-flow.js.map +0 -1
- package/dist/services/source-service.d.ts +0 -57
- package/dist/services/source-service.js +0 -809
- package/dist/services/source-service.js.map +0 -1
- package/dist/services/workflow-service.d.ts +0 -5
- package/dist/services/workflow-service.js +0 -45
- package/dist/services/workflow-service.js.map +0 -1
- package/dist/services/workspace-bootstrap-service.d.ts +0 -25
- package/dist/services/workspace-bootstrap-service.js +0 -140
- package/dist/services/workspace-bootstrap-service.js.map +0 -1
- package/dist/state/store.d.ts +0 -35
- package/dist/state/store.js +0 -151
- package/dist/state/store.js.map +0 -1
- package/dist/tests/add-flow-model.test.d.ts +0 -1
- package/dist/tests/add-flow-model.test.js +0 -108
- package/dist/tests/add-flow-model.test.js.map +0 -1
- package/dist/tests/add-flow-ui.test.d.ts +0 -1
- package/dist/tests/add-flow-ui.test.js +0 -16
- package/dist/tests/add-flow-ui.test.js.map +0 -1
- package/dist/tests/add-prepare-flow.test.d.ts +0 -1
- package/dist/tests/add-prepare-flow.test.js +0 -166
- package/dist/tests/add-prepare-flow.test.js.map +0 -1
- package/dist/tests/add-selection-and-find-command.test.d.ts +0 -1
- package/dist/tests/add-selection-and-find-command.test.js +0 -89
- package/dist/tests/add-selection-and-find-command.test.js.map +0 -1
- package/dist/tests/clawhub.test.d.ts +0 -1
- package/dist/tests/clawhub.test.js +0 -63
- package/dist/tests/clawhub.test.js.map +0 -1
- package/dist/tests/cli-utils.test.d.ts +0 -1
- package/dist/tests/cli-utils.test.js +0 -24
- package/dist/tests/cli-utils.test.js.map +0 -1
- package/dist/tests/config-coordinator.test.d.ts +0 -1
- package/dist/tests/config-coordinator.test.js +0 -219
- package/dist/tests/config-coordinator.test.js.map +0 -1
- package/dist/tests/config-integration.test.d.ts +0 -1
- package/dist/tests/config-integration.test.js +0 -276
- package/dist/tests/config-integration.test.js.map +0 -1
- package/dist/tests/config-ui-utils.test.d.ts +0 -1
- package/dist/tests/config-ui-utils.test.js +0 -523
- package/dist/tests/config-ui-utils.test.js.map +0 -1
- package/dist/tests/find-and-naming-utils.test.d.ts +0 -1
- package/dist/tests/find-and-naming-utils.test.js +0 -127
- package/dist/tests/find-and-naming-utils.test.js.map +0 -1
- package/dist/tests/inventory-service-precedence.test.d.ts +0 -1
- package/dist/tests/inventory-service-precedence.test.js +0 -42
- package/dist/tests/inventory-service-precedence.test.js.map +0 -1
- package/dist/tests/skill-flow.test.d.ts +0 -1
- package/dist/tests/skill-flow.test.js +0 -991
- package/dist/tests/skill-flow.test.js.map +0 -1
- package/dist/tests/source-lifecycle.test.d.ts +0 -1
- package/dist/tests/source-lifecycle.test.js +0 -644
- package/dist/tests/source-lifecycle.test.js.map +0 -1
- package/dist/tests/source-parsing-compatibility.test.d.ts +0 -1
- package/dist/tests/source-parsing-compatibility.test.js +0 -72
- package/dist/tests/source-parsing-compatibility.test.js.map +0 -1
- package/dist/tests/target-definitions.test.d.ts +0 -1
- package/dist/tests/target-definitions.test.js +0 -51
- package/dist/tests/target-definitions.test.js.map +0 -1
- package/dist/tests/test-helpers.d.ts +0 -18
- package/dist/tests/test-helpers.js +0 -123
- package/dist/tests/test-helpers.js.map +0 -1
- package/dist/tui/add-flow-model.d.ts +0 -62
- package/dist/tui/add-flow-model.js +0 -206
- package/dist/tui/add-flow-model.js.map +0 -1
- package/dist/tui/add-flow.d.ts +0 -25
- package/dist/tui/add-flow.js +0 -534
- package/dist/tui/add-flow.js.map +0 -1
- package/dist/tui/config-app.d.ts +0 -178
- package/dist/tui/config-app.js +0 -1551
- package/dist/tui/config-app.js.map +0 -1
- package/dist/tui/find-app.d.ts +0 -9
- package/dist/tui/find-app.js +0 -150
- package/dist/tui/find-app.js.map +0 -1
- package/dist/tui/selection-state.d.ts +0 -8
- package/dist/tui/selection-state.js +0 -32
- package/dist/tui/selection-state.js.map +0 -1
- package/dist/utils/builtin-git-sources.d.ts +0 -5
- package/dist/utils/builtin-git-sources.js +0 -23
- package/dist/utils/builtin-git-sources.js.map +0 -1
- package/dist/utils/clawhub.d.ts +0 -41
- package/dist/utils/clawhub.js +0 -94
- package/dist/utils/clawhub.js.map +0 -1
- package/dist/utils/cli.d.ts +0 -2
- package/dist/utils/cli.js +0 -19
- package/dist/utils/cli.js.map +0 -1
- package/dist/utils/constants.d.ts +0 -23
- package/dist/utils/constants.js +0 -195
- package/dist/utils/constants.js.map +0 -1
- package/dist/utils/find-command.d.ts +0 -2
- package/dist/utils/find-command.js +0 -29
- package/dist/utils/find-command.js.map +0 -1
- package/dist/utils/format.d.ts +0 -7
- package/dist/utils/format.js +0 -68
- package/dist/utils/format.js.map +0 -1
- package/dist/utils/fs.d.ts +0 -16
- package/dist/utils/fs.js +0 -144
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/git.d.ts +0 -3
- package/dist/utils/git.js +0 -12
- package/dist/utils/git.js.map +0 -1
- package/dist/utils/github-catalog.d.ts +0 -1
- package/dist/utils/github-catalog.js +0 -25
- package/dist/utils/github-catalog.js.map +0 -1
- package/dist/utils/naming.d.ts +0 -29
- package/dist/utils/naming.js +0 -115
- package/dist/utils/naming.js.map +0 -1
- package/dist/utils/result.d.ts +0 -4
- package/dist/utils/result.js +0 -15
- package/dist/utils/result.js.map +0 -1
- package/dist/utils/source-id.d.ts +0 -2
- package/dist/utils/source-id.js +0 -49
- package/dist/utils/source-id.js.map +0 -1
package/dist/tui/config-app.js
DELETED
|
@@ -1,1551 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
4
|
-
import { TARGET_LABELS, TARGET_ORDER } from "../utils/constants.js";
|
|
5
|
-
import { buildProjectedSkillName, formatGroupLabel, parseGitHubRepo, resolveProjectedSkillNames, } from "../utils/naming.js";
|
|
6
|
-
import { getParentSelectionState, toggleChild, toggleParent, } from "./selection-state.js";
|
|
7
|
-
import { ADD_BADGE_TEXT } from "./add-flow.js";
|
|
8
|
-
const EMPTY_DRAFT = {
|
|
9
|
-
enabledTargets: [],
|
|
10
|
-
selectedLeafIds: [],
|
|
11
|
-
};
|
|
12
|
-
const EMPTY_CONFIG_GROUP = {
|
|
13
|
-
id: "__empty__",
|
|
14
|
-
title: "",
|
|
15
|
-
kind: "source",
|
|
16
|
-
summaries: [],
|
|
17
|
-
};
|
|
18
|
-
const EMPTY_PREVIEW = {
|
|
19
|
-
actions: [],
|
|
20
|
-
blockedCount: 0,
|
|
21
|
-
errorMessage: undefined,
|
|
22
|
-
loading: false,
|
|
23
|
-
requestId: 0,
|
|
24
|
-
};
|
|
25
|
-
const PANE_CHROME_ROWS = 5;
|
|
26
|
-
const UPDATED_FEEDBACK_MS = 1_200;
|
|
27
|
-
const CLAWHUB_GROUP_ID = "__clawhub_skills__";
|
|
28
|
-
export function normalizeDraft(draft) {
|
|
29
|
-
return {
|
|
30
|
-
enabledTargets: [...draft.enabledTargets].sort(),
|
|
31
|
-
selectedLeafIds: [...draft.selectedLeafIds].sort(),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export function draftsEqual(left, right) {
|
|
35
|
-
const nextLeft = normalizeDraft(left);
|
|
36
|
-
const nextRight = normalizeDraft(right);
|
|
37
|
-
return JSON.stringify(nextLeft) === JSON.stringify(nextRight);
|
|
38
|
-
}
|
|
39
|
-
export function buildDraftsFromSummaries(summaries) {
|
|
40
|
-
return Object.fromEntries(summaries.map((summary) => {
|
|
41
|
-
const enabledTargets = Object.entries(summary.bindings.targets)
|
|
42
|
-
.filter(([, value]) => value?.enabled)
|
|
43
|
-
.map(([target]) => target);
|
|
44
|
-
const selectedLeafIds = [
|
|
45
|
-
...new Set((summary.bindings.selectedLeafIds && summary.bindings.selectedLeafIds.length > 0
|
|
46
|
-
? summary.bindings.selectedLeafIds
|
|
47
|
-
: enabledTargets.flatMap((target) => summary.bindings.targets[target]?.leafIds ?? []))),
|
|
48
|
-
];
|
|
49
|
-
return [summary.source.id, normalizeDraft({ enabledTargets, selectedLeafIds })];
|
|
50
|
-
}));
|
|
51
|
-
}
|
|
52
|
-
export function buildConfigGroups(summaries) {
|
|
53
|
-
const clawhubSummaries = summaries.filter((summary) => summary.source.kind === "clawhub");
|
|
54
|
-
const groups = [];
|
|
55
|
-
let clawhubGroupInserted = false;
|
|
56
|
-
for (const summary of summaries) {
|
|
57
|
-
if (summary.source.kind === "clawhub") {
|
|
58
|
-
if (!clawhubGroupInserted && clawhubSummaries.length > 0) {
|
|
59
|
-
groups.push({
|
|
60
|
-
id: CLAWHUB_GROUP_ID,
|
|
61
|
-
title: "ClawHub Skills",
|
|
62
|
-
kind: "clawhub",
|
|
63
|
-
summaries: clawhubSummaries,
|
|
64
|
-
});
|
|
65
|
-
clawhubGroupInserted = true;
|
|
66
|
-
}
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
groups.push({
|
|
70
|
-
id: summary.source.id,
|
|
71
|
-
title: formatGroupLabel(summary.source),
|
|
72
|
-
kind: "source",
|
|
73
|
-
summaries: [summary],
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
if (!clawhubGroupInserted && clawhubSummaries.length > 0) {
|
|
77
|
-
groups.push({
|
|
78
|
-
id: CLAWHUB_GROUP_ID,
|
|
79
|
-
title: "ClawHub Skills",
|
|
80
|
-
kind: "clawhub",
|
|
81
|
-
summaries: clawhubSummaries,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
return groups;
|
|
85
|
-
}
|
|
86
|
-
export function buildConfigGroupSkillRows(group) {
|
|
87
|
-
return group.summaries.flatMap((summary) => summary.leafs.map((leaf) => ({
|
|
88
|
-
summary,
|
|
89
|
-
leaf,
|
|
90
|
-
})));
|
|
91
|
-
}
|
|
92
|
-
export function getPaneViewportCount(paneHeight, reservedRows = 0) {
|
|
93
|
-
return Math.max(1, paneHeight - PANE_CHROME_ROWS - reservedRows);
|
|
94
|
-
}
|
|
95
|
-
export function getPaneWidths(terminalColumns) {
|
|
96
|
-
const available = Math.max(56, terminalColumns - 1);
|
|
97
|
-
const left = Math.max(20, Math.min(30, Math.floor(available * 0.28)));
|
|
98
|
-
return [left, Math.max(32, available - left)];
|
|
99
|
-
}
|
|
100
|
-
export function getActionChangeCount(actions) {
|
|
101
|
-
return actions.filter((action) => action.kind !== "noop").length;
|
|
102
|
-
}
|
|
103
|
-
export function getGroupSelectedLeafCount({ drafts, group, }) {
|
|
104
|
-
return group.summaries.reduce((count, summary) => count + (drafts[summary.source.id]?.selectedLeafIds.length ?? 0), 0);
|
|
105
|
-
}
|
|
106
|
-
export function getStatusDisplay({ deleteState, isSelectedDelete, saveState, updateState, }) {
|
|
107
|
-
if (isSelectedDelete && deleteState.phase === "deleting") {
|
|
108
|
-
return { kind: "deleting", label: "Deleting", color: "yellow" };
|
|
109
|
-
}
|
|
110
|
-
if (updateState.phase === "updating") {
|
|
111
|
-
return { kind: "updating", label: "Updating", color: "cyan" };
|
|
112
|
-
}
|
|
113
|
-
if (updateState.phase === "failed") {
|
|
114
|
-
return { kind: "update-failed", label: "Update Failed", color: "red" };
|
|
115
|
-
}
|
|
116
|
-
if (saveState.phase === "saving") {
|
|
117
|
-
return { kind: "saving", label: "Saving", color: "cyan" };
|
|
118
|
-
}
|
|
119
|
-
if (saveState.phase === "failed") {
|
|
120
|
-
return { kind: "failed", label: "Failed", color: "red" };
|
|
121
|
-
}
|
|
122
|
-
if (updateState.phase === "updated") {
|
|
123
|
-
return { kind: "updated", label: "Updated", color: "green" };
|
|
124
|
-
}
|
|
125
|
-
if (saveState.phase === "saved") {
|
|
126
|
-
return { kind: "saved", label: "Saved", color: "green" };
|
|
127
|
-
}
|
|
128
|
-
return { kind: "clean", label: "Clean", color: "gray" };
|
|
129
|
-
}
|
|
130
|
-
export function buildTopBar({ width, isDirty, changeCount, showDelete, statusLabel, }) {
|
|
131
|
-
const topBar = {
|
|
132
|
-
title: "Skill Flow",
|
|
133
|
-
titleColor: "blue",
|
|
134
|
-
};
|
|
135
|
-
if (changeCount > 0) {
|
|
136
|
-
topBar.detail = `Changes: ${changeCount}`;
|
|
137
|
-
return topBar;
|
|
138
|
-
}
|
|
139
|
-
if (statusLabel === "Saving" ||
|
|
140
|
-
statusLabel === "Updating" ||
|
|
141
|
-
statusLabel === "Deleting" ||
|
|
142
|
-
statusLabel === "Failed" ||
|
|
143
|
-
statusLabel === "Update Failed") {
|
|
144
|
-
topBar.detail = `Status: ${statusLabel}`;
|
|
145
|
-
topBar.detailColor =
|
|
146
|
-
statusLabel === "Deleting"
|
|
147
|
-
? "yellow"
|
|
148
|
-
: statusLabel === "Saving" || statusLabel === "Updating"
|
|
149
|
-
? "cyan"
|
|
150
|
-
: "red";
|
|
151
|
-
}
|
|
152
|
-
return topBar;
|
|
153
|
-
}
|
|
154
|
-
export function prioritizeAlerts(alerts) {
|
|
155
|
-
const seen = new Set();
|
|
156
|
-
const priority = {
|
|
157
|
-
error: 0,
|
|
158
|
-
blocked: 1,
|
|
159
|
-
warning: 2,
|
|
160
|
-
};
|
|
161
|
-
return alerts
|
|
162
|
-
.filter((alert) => {
|
|
163
|
-
const key = `${alert.level}:${alert.message}`;
|
|
164
|
-
if (seen.has(key)) {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
seen.add(key);
|
|
168
|
-
return true;
|
|
169
|
-
})
|
|
170
|
-
.sort((left, right) => priority[left.level] - priority[right.level])
|
|
171
|
-
.slice(0, 2);
|
|
172
|
-
}
|
|
173
|
-
export function getInitialDetailFocus({ hasAgents, hasSkills, }) {
|
|
174
|
-
if (hasAgents) {
|
|
175
|
-
return "detail.agents";
|
|
176
|
-
}
|
|
177
|
-
if (hasSkills) {
|
|
178
|
-
return "detail.skills";
|
|
179
|
-
}
|
|
180
|
-
return "detail.actions";
|
|
181
|
-
}
|
|
182
|
-
export function moveDetailFocus({ actionCursor, actionCount, agentCount, agentCursor, direction, focus, skillCount, skillCursor, }) {
|
|
183
|
-
if (focus === "detail.agents") {
|
|
184
|
-
if (direction === -1) {
|
|
185
|
-
if (agentCount > 0 && agentCursor > 0) {
|
|
186
|
-
return { focus, agentCursor: agentCursor - 1, skillCursor, actionCursor };
|
|
187
|
-
}
|
|
188
|
-
return { focus, agentCursor, skillCursor, actionCursor };
|
|
189
|
-
}
|
|
190
|
-
if (agentCount > 0 && agentCursor < agentCount - 1) {
|
|
191
|
-
return { focus, agentCursor: agentCursor + 1, skillCursor, actionCursor };
|
|
192
|
-
}
|
|
193
|
-
if (skillCount > 0) {
|
|
194
|
-
return { focus: "detail.skills", agentCursor, skillCursor: 0, actionCursor };
|
|
195
|
-
}
|
|
196
|
-
return {
|
|
197
|
-
focus: "detail.actions",
|
|
198
|
-
agentCursor,
|
|
199
|
-
skillCursor,
|
|
200
|
-
actionCursor: Math.min(actionCursor, Math.max(0, actionCount - 1)),
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
if (focus === "detail.skills") {
|
|
204
|
-
if (direction === -1) {
|
|
205
|
-
if (skillCount > 0 && skillCursor > 0) {
|
|
206
|
-
return { focus, agentCursor, skillCursor: skillCursor - 1, actionCursor };
|
|
207
|
-
}
|
|
208
|
-
if (agentCount > 0) {
|
|
209
|
-
return {
|
|
210
|
-
focus: "detail.agents",
|
|
211
|
-
agentCursor: Math.max(0, agentCount - 1),
|
|
212
|
-
skillCursor,
|
|
213
|
-
actionCursor,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
return { focus, agentCursor, skillCursor, actionCursor };
|
|
217
|
-
}
|
|
218
|
-
if (skillCount > 0 && skillCursor < skillCount - 1) {
|
|
219
|
-
return { focus, agentCursor, skillCursor: skillCursor + 1, actionCursor };
|
|
220
|
-
}
|
|
221
|
-
return {
|
|
222
|
-
focus: "detail.actions",
|
|
223
|
-
agentCursor,
|
|
224
|
-
skillCursor,
|
|
225
|
-
actionCursor: Math.min(actionCursor, Math.max(0, actionCount - 1)),
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
if (direction === 1) {
|
|
229
|
-
if (actionCursor < actionCount - 1) {
|
|
230
|
-
return { focus, agentCursor, skillCursor, actionCursor: actionCursor + 1 };
|
|
231
|
-
}
|
|
232
|
-
return { focus, agentCursor, skillCursor, actionCursor };
|
|
233
|
-
}
|
|
234
|
-
if (actionCursor > 0) {
|
|
235
|
-
return { focus, agentCursor, skillCursor, actionCursor: actionCursor - 1 };
|
|
236
|
-
}
|
|
237
|
-
if (skillCount > 0) {
|
|
238
|
-
return {
|
|
239
|
-
focus: "detail.skills",
|
|
240
|
-
agentCursor,
|
|
241
|
-
skillCursor: Math.max(0, skillCount - 1),
|
|
242
|
-
actionCursor,
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
if (agentCount > 0) {
|
|
246
|
-
return {
|
|
247
|
-
focus: "detail.agents",
|
|
248
|
-
agentCursor: Math.max(0, agentCount - 1),
|
|
249
|
-
skillCursor,
|
|
250
|
-
actionCursor,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
return { focus, agentCursor, skillCursor, actionCursor };
|
|
254
|
-
}
|
|
255
|
-
export function getNextSelectionIndexAfterDelete(currentIndex, nextCount) {
|
|
256
|
-
if (nextCount <= 0) {
|
|
257
|
-
return -1;
|
|
258
|
-
}
|
|
259
|
-
return Math.min(currentIndex, nextCount - 1);
|
|
260
|
-
}
|
|
261
|
-
export function captureFocusSnapshot({ actionCursor, agentCursor, availableTargets, focus, groupId, selectedGroupIndex, selectedSummary, skillCursor, }) {
|
|
262
|
-
return {
|
|
263
|
-
focus,
|
|
264
|
-
groupIndex: selectedGroupIndex,
|
|
265
|
-
groupId,
|
|
266
|
-
sourceId: selectedSummary?.source.id,
|
|
267
|
-
agentTarget: agentCursor > 0 ? availableTargets[agentCursor - 1] : undefined,
|
|
268
|
-
skillId: selectedSummary && skillCursor > 0
|
|
269
|
-
? selectedSummary.leafs[skillCursor - 1]?.id
|
|
270
|
-
: undefined,
|
|
271
|
-
action: actionCursor === 1 ? "delete" : "update",
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
export function reconcileFocusAfterReload({ availableTargets, nextGroups, snapshot, }) {
|
|
275
|
-
const selectedGroupIndex = nextGroups.findIndex((group) => group.id === snapshot.groupId);
|
|
276
|
-
const fallbackGroupIndex = Math.min(snapshot.groupIndex, Math.max(0, nextGroups.length - 1));
|
|
277
|
-
const resolvedGroupIndex = nextGroups.length === 0
|
|
278
|
-
? -1
|
|
279
|
-
: selectedGroupIndex >= 0 && nextGroups[selectedGroupIndex]
|
|
280
|
-
? selectedGroupIndex
|
|
281
|
-
: fallbackGroupIndex;
|
|
282
|
-
const group = resolvedGroupIndex >= 0 ? nextGroups[resolvedGroupIndex] : undefined;
|
|
283
|
-
const skillRows = group ? buildConfigGroupSkillRows(group) : [];
|
|
284
|
-
const hasAgents = availableTargets.length > 0;
|
|
285
|
-
const hasSkills = skillRows.length > 0;
|
|
286
|
-
let focus = snapshot.focus;
|
|
287
|
-
let agentCursor = 0;
|
|
288
|
-
let skillCursor = 0;
|
|
289
|
-
let actionCursor = snapshot.action === "delete" && group?.kind !== "clawhub" ? 1 : 0;
|
|
290
|
-
if (focus === "detail.agents") {
|
|
291
|
-
if (hasAgents) {
|
|
292
|
-
const nextAgentIndex = snapshot.agentTarget
|
|
293
|
-
? availableTargets.indexOf(snapshot.agentTarget)
|
|
294
|
-
: -1;
|
|
295
|
-
agentCursor = nextAgentIndex >= 0 ? nextAgentIndex + 1 : 0;
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
focus = getInitialDetailFocus({ hasAgents, hasSkills });
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
if (focus === "detail.skills") {
|
|
302
|
-
if (hasSkills) {
|
|
303
|
-
const nextSkillIndex = snapshot.sourceId && snapshot.skillId
|
|
304
|
-
? skillRows.findIndex((row) => row.summary.source.id === snapshot.sourceId && row.leaf.id === snapshot.skillId)
|
|
305
|
-
: -1;
|
|
306
|
-
skillCursor = nextSkillIndex >= 0 ? nextSkillIndex + 1 : 0;
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
focus = getInitialDetailFocus({ hasAgents, hasSkills });
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
if (focus === "detail.actions") {
|
|
313
|
-
focus = "detail.actions";
|
|
314
|
-
}
|
|
315
|
-
return {
|
|
316
|
-
actionCursor,
|
|
317
|
-
agentCursor,
|
|
318
|
-
focus: resolvedGroupIndex >= 0 ? focus : "groups",
|
|
319
|
-
groupCursor: resolvedGroupIndex,
|
|
320
|
-
selectedGroupIndex: resolvedGroupIndex,
|
|
321
|
-
skillCursor,
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
export function getRequestedAction({ actionCursor, canDelete, focus, input, keyReturn, }) {
|
|
325
|
-
if (input === "u") {
|
|
326
|
-
return "update";
|
|
327
|
-
}
|
|
328
|
-
if (input === "d" && canDelete) {
|
|
329
|
-
return "delete";
|
|
330
|
-
}
|
|
331
|
-
if (focus === "detail.actions" && keyReturn) {
|
|
332
|
-
return actionCursor === 1 && canDelete ? "delete" : "update";
|
|
333
|
-
}
|
|
334
|
-
return undefined;
|
|
335
|
-
}
|
|
336
|
-
export function buildProjectionWarningMap({ drafts, summaries, sourceId, }) {
|
|
337
|
-
const currentDraft = drafts[sourceId] ?? EMPTY_DRAFT;
|
|
338
|
-
const currentSummary = summaries.find((summary) => summary.source.id === sourceId);
|
|
339
|
-
if (!currentSummary || currentDraft.enabledTargets.length === 0) {
|
|
340
|
-
return {};
|
|
341
|
-
}
|
|
342
|
-
const currentSelectedLeafIds = new Set(currentDraft.selectedLeafIds);
|
|
343
|
-
const currentEnabledTargets = new Set(currentDraft.enabledTargets);
|
|
344
|
-
const otherSelectedLeafs = summaries.flatMap((summary) => {
|
|
345
|
-
if (summary.source.id === sourceId) {
|
|
346
|
-
return [];
|
|
347
|
-
}
|
|
348
|
-
const otherDraft = drafts[summary.source.id] ?? EMPTY_DRAFT;
|
|
349
|
-
const hasTargetOverlap = otherDraft.enabledTargets.some((target) => currentEnabledTargets.has(target));
|
|
350
|
-
if (!hasTargetOverlap) {
|
|
351
|
-
return [];
|
|
352
|
-
}
|
|
353
|
-
return otherDraft.selectedLeafIds
|
|
354
|
-
.map((leafId) => summary.leafs.find((leaf) => leaf.id === leafId))
|
|
355
|
-
.filter((leaf) => Boolean(leaf))
|
|
356
|
-
.map((leaf) => ({
|
|
357
|
-
source: summary.source,
|
|
358
|
-
leaf,
|
|
359
|
-
exactKey: getExactDuplicateKey(leaf.linkName, leaf.name, leaf.description),
|
|
360
|
-
}));
|
|
361
|
-
});
|
|
362
|
-
const projectedNames = resolveProjectedSkillNames([
|
|
363
|
-
...otherSelectedLeafs.map((candidate) => ({
|
|
364
|
-
leafId: candidate.leaf.id,
|
|
365
|
-
groupId: candidate.source.id,
|
|
366
|
-
groupName: candidate.source.displayName,
|
|
367
|
-
groupAuthor: parseGitHubRepo(candidate.source.locator)?.owner,
|
|
368
|
-
skillName: candidate.leaf.linkName,
|
|
369
|
-
})),
|
|
370
|
-
...currentSummary.leafs
|
|
371
|
-
.filter((leaf) => currentSelectedLeafIds.has(leaf.id))
|
|
372
|
-
.map((leaf) => ({
|
|
373
|
-
leafId: leaf.id,
|
|
374
|
-
groupId: currentSummary.source.id,
|
|
375
|
-
groupName: currentSummary.source.displayName,
|
|
376
|
-
groupAuthor: parseGitHubRepo(currentSummary.source.locator)?.owner,
|
|
377
|
-
skillName: leaf.linkName,
|
|
378
|
-
})),
|
|
379
|
-
]);
|
|
380
|
-
const warningsByLeafId = {};
|
|
381
|
-
for (const leaf of currentSummary.leafs) {
|
|
382
|
-
if (!currentSelectedLeafIds.has(leaf.id)) {
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
const exactKey = getExactDuplicateKey(leaf.linkName, leaf.name, leaf.description);
|
|
386
|
-
const exactDuplicate = otherSelectedLeafs.find((candidate) => candidate.exactKey === exactKey);
|
|
387
|
-
if (exactDuplicate) {
|
|
388
|
-
warningsByLeafId[leaf.id] = [
|
|
389
|
-
`identical skill already selected in ${formatGroupLabel(exactDuplicate.source)}, this one will be skipped`,
|
|
390
|
-
];
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
const renameConflict = otherSelectedLeafs.find((candidate) => candidate.leaf.linkName === leaf.linkName);
|
|
394
|
-
if (renameConflict) {
|
|
395
|
-
const projectedName = projectedNames.get(leaf.id) ??
|
|
396
|
-
buildProjectedSkillName(currentSummary.source.displayName, leaf.linkName, parseGitHubRepo(currentSummary.source.locator)?.owner);
|
|
397
|
-
warningsByLeafId[leaf.id] = [
|
|
398
|
-
`conflicts with ${formatGroupLabel(renameConflict.source)}, will deploy as ${projectedName}`,
|
|
399
|
-
];
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return warningsByLeafId;
|
|
403
|
-
}
|
|
404
|
-
function buildProjectionNameMap({ drafts, summaries, sourceId, }) {
|
|
405
|
-
const currentDraft = drafts[sourceId] ?? EMPTY_DRAFT;
|
|
406
|
-
const currentSummary = summaries.find((summary) => summary.source.id === sourceId);
|
|
407
|
-
if (!currentSummary || currentDraft.enabledTargets.length === 0) {
|
|
408
|
-
return new Map();
|
|
409
|
-
}
|
|
410
|
-
const currentEnabledTargets = new Set(currentDraft.enabledTargets);
|
|
411
|
-
const selectedLeafIds = new Set(currentDraft.selectedLeafIds);
|
|
412
|
-
return resolveProjectedSkillNames([
|
|
413
|
-
...summaries.flatMap((summary) => {
|
|
414
|
-
if (summary.source.id === sourceId) {
|
|
415
|
-
return [];
|
|
416
|
-
}
|
|
417
|
-
const draft = drafts[summary.source.id] ?? EMPTY_DRAFT;
|
|
418
|
-
const hasTargetOverlap = draft.enabledTargets.some((target) => currentEnabledTargets.has(target));
|
|
419
|
-
if (!hasTargetOverlap) {
|
|
420
|
-
return [];
|
|
421
|
-
}
|
|
422
|
-
return draft.selectedLeafIds
|
|
423
|
-
.map((leafId) => summary.leafs.find((leaf) => leaf.id === leafId))
|
|
424
|
-
.filter((leaf) => Boolean(leaf))
|
|
425
|
-
.map((leaf) => ({
|
|
426
|
-
leafId: leaf.id,
|
|
427
|
-
groupId: summary.source.id,
|
|
428
|
-
groupName: summary.source.displayName,
|
|
429
|
-
groupAuthor: parseGitHubRepo(summary.source.locator)?.owner,
|
|
430
|
-
skillName: leaf.linkName,
|
|
431
|
-
}));
|
|
432
|
-
}),
|
|
433
|
-
...currentSummary.leafs
|
|
434
|
-
.filter((leaf) => selectedLeafIds.has(leaf.id))
|
|
435
|
-
.map((leaf) => ({
|
|
436
|
-
leafId: leaf.id,
|
|
437
|
-
groupId: currentSummary.source.id,
|
|
438
|
-
groupName: currentSummary.source.displayName,
|
|
439
|
-
groupAuthor: parseGitHubRepo(currentSummary.source.locator)?.owner,
|
|
440
|
-
skillName: leaf.linkName,
|
|
441
|
-
})),
|
|
442
|
-
]);
|
|
443
|
-
}
|
|
444
|
-
export function buildScrollableRows(items, cursorIndex, visibleCount, keyPrefix = "scroll", reserveHintSlots = false) {
|
|
445
|
-
const safeVisibleCount = Math.max(1, visibleCount);
|
|
446
|
-
const needsHints = items.length > safeVisibleCount;
|
|
447
|
-
const hasHintSlots = needsHints || (reserveHintSlots && safeVisibleCount >= 3);
|
|
448
|
-
const adjustedVisibleCount = hasHintSlots ? Math.max(1, safeVisibleCount - 2) : safeVisibleCount;
|
|
449
|
-
const windowed = getWindowedRows(items, cursorIndex, adjustedVisibleCount);
|
|
450
|
-
const hasUp = windowed.start > 0;
|
|
451
|
-
const hasDown = windowed.end < items.length;
|
|
452
|
-
const rows = [];
|
|
453
|
-
if (hasHintSlots) {
|
|
454
|
-
rows.push({
|
|
455
|
-
key: `__scroll_up__:${keyPrefix}`,
|
|
456
|
-
text: hasUp ? "↑ more" : "",
|
|
457
|
-
active: false,
|
|
458
|
-
color: "gray",
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
rows.push(...windowed.rows);
|
|
462
|
-
if (hasHintSlots) {
|
|
463
|
-
rows.push({
|
|
464
|
-
key: `__scroll_down__:${keyPrefix}`,
|
|
465
|
-
text: hasDown ? "↓ more" : "",
|
|
466
|
-
active: false,
|
|
467
|
-
color: "gray",
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
return {
|
|
471
|
-
rows,
|
|
472
|
-
start: windowed.start,
|
|
473
|
-
end: windowed.end,
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
export function ConfigApp({ app, availableTargets, summaries, initialDrafts, bootStatus, }) {
|
|
477
|
-
const { exit } = useApp();
|
|
478
|
-
const { stdout } = useStdout();
|
|
479
|
-
const terminalSize = useTerminalSize(stdout);
|
|
480
|
-
const previewRequestIds = useRef({});
|
|
481
|
-
const saveRequestIds = useRef({});
|
|
482
|
-
const updateRequestIds = useRef({});
|
|
483
|
-
const updatedTimers = useRef({});
|
|
484
|
-
const [summaryList, setSummaryList] = useState(summaries);
|
|
485
|
-
const groupViews = buildConfigGroups(summaryList);
|
|
486
|
-
const [selectedGroupIndex, setSelectedGroupIndex] = useState(groupViews.length > 0 ? 0 : -1);
|
|
487
|
-
const [groupCursor, setGroupCursor] = useState(groupViews.length > 0 ? 0 : -1);
|
|
488
|
-
const [focus, setFocus] = useState("groups");
|
|
489
|
-
const [skillCursor, setSkillCursor] = useState(0);
|
|
490
|
-
const [targetCursor, setTargetCursor] = useState(0);
|
|
491
|
-
const [actionCursor, setActionCursor] = useState(0);
|
|
492
|
-
const [drafts, setDrafts] = useState(initialDrafts);
|
|
493
|
-
const [savedDrafts, setSavedDrafts] = useState(initialDrafts);
|
|
494
|
-
const [previewBySourceId, setPreviewBySourceId] = useState({});
|
|
495
|
-
const [saveStateBySourceId, setSaveStateBySourceId] = useState({});
|
|
496
|
-
const [updateStateBySourceId, setUpdateStateBySourceId] = useState({});
|
|
497
|
-
const [deleteState, setDeleteState] = useState({
|
|
498
|
-
phase: "idle",
|
|
499
|
-
sourceId: undefined,
|
|
500
|
-
message: undefined,
|
|
501
|
-
});
|
|
502
|
-
const selectedGroup = groupViews[selectedGroupIndex] ?? EMPTY_CONFIG_GROUP;
|
|
503
|
-
const selectedSkillRows = buildConfigGroupSkillRows(selectedGroup);
|
|
504
|
-
const selectedSkillRow = skillCursor > 0 ? selectedSkillRows[skillCursor - 1] : undefined;
|
|
505
|
-
const activeSummary = selectedSkillRow?.summary ?? selectedGroup.summaries[0];
|
|
506
|
-
const selectedSourceId = activeSummary?.source.id ?? "";
|
|
507
|
-
const selectedDraft = drafts[selectedSourceId] ?? EMPTY_DRAFT;
|
|
508
|
-
const savedDraft = savedDrafts[selectedSourceId] ?? EMPTY_DRAFT;
|
|
509
|
-
const isDirty = !draftsEqual(selectedDraft, savedDraft);
|
|
510
|
-
const leafIds = activeSummary?.leafs.map((leaf) => leaf.id) ?? [];
|
|
511
|
-
const groupSelectedLeafCount = getGroupSelectedLeafCount({
|
|
512
|
-
drafts,
|
|
513
|
-
group: selectedGroup,
|
|
514
|
-
});
|
|
515
|
-
const visibleTargets = availableTargets;
|
|
516
|
-
const agentInteractiveCount = visibleTargets.length > 0 ? visibleTargets.length + 1 : 0;
|
|
517
|
-
const skillInteractiveCount = selectedSkillRows.length > 0 ? selectedSkillRows.length + 1 : 0;
|
|
518
|
-
const treeState = selectedGroup.kind === "clawhub"
|
|
519
|
-
? {
|
|
520
|
-
allLeafIds: selectedSkillRows.map((row) => row.leaf.id),
|
|
521
|
-
selectedLeafIds: selectedGroup.summaries.flatMap((summary) => drafts[summary.source.id]?.selectedLeafIds ?? []),
|
|
522
|
-
}
|
|
523
|
-
: {
|
|
524
|
-
allLeafIds: leafIds,
|
|
525
|
-
selectedLeafIds: selectedDraft.selectedLeafIds,
|
|
526
|
-
};
|
|
527
|
-
const parentSelectionState = getParentSelectionState(treeState);
|
|
528
|
-
const visibleEnabledTargets = visibleTargets.filter((target) => selectedDraft.enabledTargets.includes(target));
|
|
529
|
-
const allTargetsSelected = visibleTargets.length > 0 && visibleEnabledTargets.length === visibleTargets.length;
|
|
530
|
-
const projectionWarningsByLeafId = buildProjectionWarningMap({
|
|
531
|
-
drafts,
|
|
532
|
-
summaries: summaryList,
|
|
533
|
-
sourceId: selectedSourceId,
|
|
534
|
-
});
|
|
535
|
-
const projectedNamesByLeafId = buildProjectionNameMap({
|
|
536
|
-
drafts,
|
|
537
|
-
summaries: summaryList,
|
|
538
|
-
sourceId: selectedSourceId,
|
|
539
|
-
});
|
|
540
|
-
const failedBootBySourceId = new Map(bootStatus.failedSources.map((item) => [item.sourceId, item.message]));
|
|
541
|
-
const previewState = previewBySourceId[selectedSourceId] ?? EMPTY_PREVIEW;
|
|
542
|
-
const changeCount = getActionChangeCount(previewState.actions);
|
|
543
|
-
const saveState = saveStateBySourceId[selectedSourceId] ?? {
|
|
544
|
-
phase: "idle",
|
|
545
|
-
message: undefined,
|
|
546
|
-
};
|
|
547
|
-
const updateState = updateStateBySourceId[selectedSourceId] ?? {
|
|
548
|
-
phase: "idle",
|
|
549
|
-
message: undefined,
|
|
550
|
-
};
|
|
551
|
-
const isSelectedDelete = deleteState.sourceId === selectedSourceId;
|
|
552
|
-
const canDelete = selectedGroup.kind === "clawhub" ? focus === "detail.skills" && skillCursor > 0 : true;
|
|
553
|
-
const showDeleteAction = selectedGroup.kind !== "clawhub";
|
|
554
|
-
const actionCount = showDeleteAction ? 2 : 1;
|
|
555
|
-
const statusDisplay = getStatusDisplay({
|
|
556
|
-
deleteState,
|
|
557
|
-
isSelectedDelete,
|
|
558
|
-
saveState,
|
|
559
|
-
updateState,
|
|
560
|
-
});
|
|
561
|
-
const canEditSelected = activeSummary !== undefined &&
|
|
562
|
-
updateState.phase !== "updating" &&
|
|
563
|
-
deleteState.phase !== "deleting";
|
|
564
|
-
const canRunActions = activeSummary !== undefined &&
|
|
565
|
-
saveState.phase !== "saving" &&
|
|
566
|
-
updateState.phase !== "updating" &&
|
|
567
|
-
deleteState.phase !== "deleting";
|
|
568
|
-
useEffect(() => {
|
|
569
|
-
return () => {
|
|
570
|
-
for (const timer of Object.values(updatedTimers.current)) {
|
|
571
|
-
if (timer) {
|
|
572
|
-
clearTimeout(timer);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
};
|
|
576
|
-
}, []);
|
|
577
|
-
useEffect(() => {
|
|
578
|
-
if (!activeSummary) {
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
const sourceId = activeSummary.source.id;
|
|
582
|
-
const requestId = (previewRequestIds.current[sourceId] ?? 0) + 1;
|
|
583
|
-
previewRequestIds.current[sourceId] = requestId;
|
|
584
|
-
setPreviewBySourceId((current) => ({
|
|
585
|
-
...current,
|
|
586
|
-
[sourceId]: {
|
|
587
|
-
...(current[sourceId] ?? EMPTY_PREVIEW),
|
|
588
|
-
errorMessage: undefined,
|
|
589
|
-
loading: true,
|
|
590
|
-
requestId,
|
|
591
|
-
},
|
|
592
|
-
}));
|
|
593
|
-
let disposed = false;
|
|
594
|
-
void app.previewDraft(sourceId, selectedDraft).then((result) => {
|
|
595
|
-
if (disposed) {
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
setPreviewBySourceId((current) => {
|
|
599
|
-
const currentState = current[sourceId] ?? EMPTY_PREVIEW;
|
|
600
|
-
if (currentState.requestId !== requestId) {
|
|
601
|
-
return current;
|
|
602
|
-
}
|
|
603
|
-
if (!result.ok) {
|
|
604
|
-
return {
|
|
605
|
-
...current,
|
|
606
|
-
[sourceId]: {
|
|
607
|
-
actions: [],
|
|
608
|
-
blockedCount: 0,
|
|
609
|
-
errorMessage: firstErrorMessage(result),
|
|
610
|
-
loading: false,
|
|
611
|
-
requestId,
|
|
612
|
-
},
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
return {
|
|
616
|
-
...current,
|
|
617
|
-
[sourceId]: {
|
|
618
|
-
actions: result.data.plan.actions,
|
|
619
|
-
blockedCount: result.data.plan.blocked.length,
|
|
620
|
-
errorMessage: undefined,
|
|
621
|
-
loading: false,
|
|
622
|
-
requestId,
|
|
623
|
-
},
|
|
624
|
-
};
|
|
625
|
-
});
|
|
626
|
-
});
|
|
627
|
-
return () => {
|
|
628
|
-
disposed = true;
|
|
629
|
-
};
|
|
630
|
-
}, [app, activeSummary, selectedDraft]);
|
|
631
|
-
useEffect(() => {
|
|
632
|
-
if (!activeSummary || draftsEqual(selectedDraft, savedDraft)) {
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
if (saveState.phase === "saving" || saveState.phase === "failed") {
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
const sourceId = activeSummary.source.id;
|
|
639
|
-
const requestId = (saveRequestIds.current[sourceId] ?? 0) + 1;
|
|
640
|
-
saveRequestIds.current[sourceId] = requestId;
|
|
641
|
-
const draftToSave = normalizeDraft(selectedDraft);
|
|
642
|
-
setSaveStateBySourceId((current) => ({
|
|
643
|
-
...current,
|
|
644
|
-
[sourceId]: {
|
|
645
|
-
phase: "saving",
|
|
646
|
-
message: "saving changes...",
|
|
647
|
-
},
|
|
648
|
-
}));
|
|
649
|
-
void app.applyDraft(sourceId, draftToSave).then((result) => {
|
|
650
|
-
setSaveStateBySourceId((current) => {
|
|
651
|
-
if ((saveRequestIds.current[sourceId] ?? 0) !== requestId) {
|
|
652
|
-
return current;
|
|
653
|
-
}
|
|
654
|
-
if (!result.ok) {
|
|
655
|
-
return {
|
|
656
|
-
...current,
|
|
657
|
-
[sourceId]: {
|
|
658
|
-
phase: "failed",
|
|
659
|
-
message: firstErrorMessage(result),
|
|
660
|
-
},
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
const appliedDraft = normalizeDraft(result.data.draft);
|
|
664
|
-
setDrafts((draftsCurrent) => ({
|
|
665
|
-
...draftsCurrent,
|
|
666
|
-
[sourceId]: appliedDraft,
|
|
667
|
-
}));
|
|
668
|
-
setSavedDrafts((savedCurrent) => ({
|
|
669
|
-
...savedCurrent,
|
|
670
|
-
[sourceId]: appliedDraft,
|
|
671
|
-
}));
|
|
672
|
-
setPreviewBySourceId((previewCurrent) => ({
|
|
673
|
-
...previewCurrent,
|
|
674
|
-
[sourceId]: {
|
|
675
|
-
actions: result.data.actions,
|
|
676
|
-
blockedCount: result.data.actions.filter((action) => action.kind === "blocked")
|
|
677
|
-
.length,
|
|
678
|
-
errorMessage: undefined,
|
|
679
|
-
loading: false,
|
|
680
|
-
requestId: previewRequestIds.current[sourceId] ?? 0,
|
|
681
|
-
},
|
|
682
|
-
}));
|
|
683
|
-
return {
|
|
684
|
-
...current,
|
|
685
|
-
[sourceId]: {
|
|
686
|
-
phase: "saved",
|
|
687
|
-
message: "saved",
|
|
688
|
-
},
|
|
689
|
-
};
|
|
690
|
-
});
|
|
691
|
-
});
|
|
692
|
-
}, [app, saveState.phase, savedDraft, selectedDraft, activeSummary]);
|
|
693
|
-
const updateSelectedDraft = (updater) => {
|
|
694
|
-
if (!activeSummary || !canEditSelected) {
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
const sourceId = activeSummary.source.id;
|
|
698
|
-
setDrafts((current) => {
|
|
699
|
-
const currentDraft = current[sourceId] ?? EMPTY_DRAFT;
|
|
700
|
-
const nextDraft = normalizeDraft(updater(currentDraft));
|
|
701
|
-
if (draftsEqual(currentDraft, nextDraft)) {
|
|
702
|
-
return current;
|
|
703
|
-
}
|
|
704
|
-
return {
|
|
705
|
-
...current,
|
|
706
|
-
[sourceId]: nextDraft,
|
|
707
|
-
};
|
|
708
|
-
});
|
|
709
|
-
setSaveStateBySourceId((current) => {
|
|
710
|
-
const state = current[sourceId];
|
|
711
|
-
if (!state || state.phase === "idle") {
|
|
712
|
-
return current;
|
|
713
|
-
}
|
|
714
|
-
return {
|
|
715
|
-
...current,
|
|
716
|
-
[sourceId]: {
|
|
717
|
-
phase: "idle",
|
|
718
|
-
message: undefined,
|
|
719
|
-
},
|
|
720
|
-
};
|
|
721
|
-
});
|
|
722
|
-
};
|
|
723
|
-
const handleUpdate = () => {
|
|
724
|
-
if (!activeSummary || !selectedGroup || !canRunActions) {
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
const sourceId = activeSummary.source.id;
|
|
728
|
-
const sourceIds = selectedGroup.kind === "clawhub"
|
|
729
|
-
? selectedGroup.summaries.map((summary) => summary.source.id)
|
|
730
|
-
: [sourceId];
|
|
731
|
-
const requestId = (updateRequestIds.current[sourceId] ?? 0) + 1;
|
|
732
|
-
updateRequestIds.current[sourceId] = requestId;
|
|
733
|
-
for (const id of sourceIds) {
|
|
734
|
-
previewRequestIds.current[id] = (previewRequestIds.current[id] ?? 0) + 1;
|
|
735
|
-
saveRequestIds.current[id] = (saveRequestIds.current[id] ?? 0) + 1;
|
|
736
|
-
}
|
|
737
|
-
if (updatedTimers.current[sourceId]) {
|
|
738
|
-
clearTimeout(updatedTimers.current[sourceId]);
|
|
739
|
-
updatedTimers.current[sourceId] = undefined;
|
|
740
|
-
}
|
|
741
|
-
const snapshot = captureFocusSnapshot({
|
|
742
|
-
actionCursor,
|
|
743
|
-
agentCursor: targetCursor,
|
|
744
|
-
availableTargets,
|
|
745
|
-
focus,
|
|
746
|
-
groupId: selectedGroup.id,
|
|
747
|
-
selectedGroupIndex,
|
|
748
|
-
selectedSummary: activeSummary,
|
|
749
|
-
skillCursor,
|
|
750
|
-
});
|
|
751
|
-
setUpdateStateBySourceId((current) => ({
|
|
752
|
-
...current,
|
|
753
|
-
[sourceId]: {
|
|
754
|
-
phase: "updating",
|
|
755
|
-
message: `updating ${selectedGroup.title}...`,
|
|
756
|
-
},
|
|
757
|
-
}));
|
|
758
|
-
void app.updateSources(sourceIds).then(async (result) => {
|
|
759
|
-
if ((updateRequestIds.current[sourceId] ?? 0) !== requestId) {
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
if (!result.ok) {
|
|
763
|
-
setUpdateStateBySourceId((current) => ({
|
|
764
|
-
...current,
|
|
765
|
-
[sourceId]: {
|
|
766
|
-
phase: "failed",
|
|
767
|
-
message: firstErrorMessage(result),
|
|
768
|
-
},
|
|
769
|
-
}));
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
const configResult = await app.getConfigData();
|
|
773
|
-
if ((updateRequestIds.current[sourceId] ?? 0) !== requestId) {
|
|
774
|
-
return;
|
|
775
|
-
}
|
|
776
|
-
if (!configResult.ok) {
|
|
777
|
-
setUpdateStateBySourceId((current) => ({
|
|
778
|
-
...current,
|
|
779
|
-
[sourceId]: {
|
|
780
|
-
phase: "failed",
|
|
781
|
-
message: firstErrorMessage(configResult),
|
|
782
|
-
},
|
|
783
|
-
}));
|
|
784
|
-
return;
|
|
785
|
-
}
|
|
786
|
-
const nextSummaries = configResult.data.summaries;
|
|
787
|
-
const nextDrafts = buildDraftsFromSummaries(nextSummaries);
|
|
788
|
-
const nextIds = new Set(nextSummaries.map((summary) => summary.source.id));
|
|
789
|
-
const nextGroups = buildConfigGroups(nextSummaries);
|
|
790
|
-
const nextFocusState = reconcileFocusAfterReload({
|
|
791
|
-
availableTargets,
|
|
792
|
-
nextGroups,
|
|
793
|
-
snapshot,
|
|
794
|
-
});
|
|
795
|
-
setSummaryList(nextSummaries);
|
|
796
|
-
setDrafts(nextDrafts);
|
|
797
|
-
setSavedDrafts(nextDrafts);
|
|
798
|
-
setPreviewBySourceId((current) => pruneSourceMap(current, nextIds));
|
|
799
|
-
setSaveStateBySourceId((current) => ({
|
|
800
|
-
...pruneSourceMap(current, nextIds),
|
|
801
|
-
[sourceId]: {
|
|
802
|
-
phase: "saved",
|
|
803
|
-
message: "saved",
|
|
804
|
-
},
|
|
805
|
-
}));
|
|
806
|
-
setSelectedGroupIndex(nextFocusState.selectedGroupIndex);
|
|
807
|
-
setGroupCursor(nextFocusState.groupCursor);
|
|
808
|
-
setFocus(nextFocusState.focus);
|
|
809
|
-
setTargetCursor(nextFocusState.agentCursor);
|
|
810
|
-
setSkillCursor(nextFocusState.skillCursor);
|
|
811
|
-
setActionCursor(nextFocusState.actionCursor);
|
|
812
|
-
setUpdateStateBySourceId((current) => ({
|
|
813
|
-
...pruneSourceMap(current, nextIds),
|
|
814
|
-
[sourceId]: {
|
|
815
|
-
phase: "updated",
|
|
816
|
-
message: "updated",
|
|
817
|
-
},
|
|
818
|
-
}));
|
|
819
|
-
updatedTimers.current[sourceId] = setTimeout(() => {
|
|
820
|
-
if ((updateRequestIds.current[sourceId] ?? 0) !== requestId) {
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
setUpdateStateBySourceId((current) => {
|
|
824
|
-
const state = current[sourceId];
|
|
825
|
-
if (!state || state.phase !== "updated") {
|
|
826
|
-
return current;
|
|
827
|
-
}
|
|
828
|
-
return {
|
|
829
|
-
...current,
|
|
830
|
-
[sourceId]: {
|
|
831
|
-
phase: "idle",
|
|
832
|
-
message: undefined,
|
|
833
|
-
},
|
|
834
|
-
};
|
|
835
|
-
});
|
|
836
|
-
updatedTimers.current[sourceId] = undefined;
|
|
837
|
-
}, UPDATED_FEEDBACK_MS);
|
|
838
|
-
});
|
|
839
|
-
};
|
|
840
|
-
const handleDelete = () => {
|
|
841
|
-
if (!activeSummary || !canRunActions) {
|
|
842
|
-
return;
|
|
843
|
-
}
|
|
844
|
-
const sourceId = activeSummary.source.id;
|
|
845
|
-
previewRequestIds.current[sourceId] = (previewRequestIds.current[sourceId] ?? 0) + 1;
|
|
846
|
-
saveRequestIds.current[sourceId] = (saveRequestIds.current[sourceId] ?? 0) + 1;
|
|
847
|
-
updateRequestIds.current[sourceId] = (updateRequestIds.current[sourceId] ?? 0) + 1;
|
|
848
|
-
if (updatedTimers.current[sourceId]) {
|
|
849
|
-
clearTimeout(updatedTimers.current[sourceId]);
|
|
850
|
-
updatedTimers.current[sourceId] = undefined;
|
|
851
|
-
}
|
|
852
|
-
setDeleteState({
|
|
853
|
-
phase: "deleting",
|
|
854
|
-
sourceId,
|
|
855
|
-
message: `deleting ${formatGroupLabel(activeSummary.source)}...`,
|
|
856
|
-
});
|
|
857
|
-
void app.uninstall([sourceId]).then((result) => {
|
|
858
|
-
if (!result.ok) {
|
|
859
|
-
setDeleteState({
|
|
860
|
-
phase: "failed",
|
|
861
|
-
sourceId,
|
|
862
|
-
message: firstErrorMessage(result),
|
|
863
|
-
});
|
|
864
|
-
return;
|
|
865
|
-
}
|
|
866
|
-
const nextSummaries = summaryList.filter((summary) => summary.source.id !== sourceId);
|
|
867
|
-
const nextGroups = buildConfigGroups(nextSummaries);
|
|
868
|
-
const nextSelectedGroupIndex = getNextSelectionIndexAfterDelete(selectedGroupIndex, nextGroups.length);
|
|
869
|
-
setSummaryList(nextSummaries);
|
|
870
|
-
setDrafts((current) => removeSourceFromMap(current, sourceId));
|
|
871
|
-
setSavedDrafts((current) => removeSourceFromMap(current, sourceId));
|
|
872
|
-
setPreviewBySourceId((current) => removeSourceFromMap(current, sourceId));
|
|
873
|
-
setSaveStateBySourceId((current) => removeSourceFromMap(current, sourceId));
|
|
874
|
-
setUpdateStateBySourceId((current) => removeSourceFromMap(current, sourceId));
|
|
875
|
-
setDeleteState({
|
|
876
|
-
phase: "idle",
|
|
877
|
-
sourceId: undefined,
|
|
878
|
-
message: undefined,
|
|
879
|
-
});
|
|
880
|
-
setSelectedGroupIndex(nextSelectedGroupIndex);
|
|
881
|
-
setGroupCursor(nextSelectedGroupIndex);
|
|
882
|
-
setFocus("groups");
|
|
883
|
-
setTargetCursor(0);
|
|
884
|
-
setSkillCursor(0);
|
|
885
|
-
setActionCursor(0);
|
|
886
|
-
});
|
|
887
|
-
};
|
|
888
|
-
useInput((input, key) => {
|
|
889
|
-
if (!activeSummary) {
|
|
890
|
-
if (input === "q" || key.escape || (input === "c" && key.ctrl)) {
|
|
891
|
-
exit();
|
|
892
|
-
}
|
|
893
|
-
return;
|
|
894
|
-
}
|
|
895
|
-
if (input === "c" && key.ctrl) {
|
|
896
|
-
exit();
|
|
897
|
-
return;
|
|
898
|
-
}
|
|
899
|
-
const requestedAction = getRequestedAction({
|
|
900
|
-
actionCursor,
|
|
901
|
-
canDelete,
|
|
902
|
-
focus,
|
|
903
|
-
input,
|
|
904
|
-
keyReturn: Boolean(key.return),
|
|
905
|
-
});
|
|
906
|
-
if (requestedAction === "update") {
|
|
907
|
-
handleUpdate();
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
if (requestedAction === "delete") {
|
|
911
|
-
handleDelete();
|
|
912
|
-
return;
|
|
913
|
-
}
|
|
914
|
-
if (input === "q" || key.escape) {
|
|
915
|
-
if (focus !== "groups") {
|
|
916
|
-
setFocus("groups");
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
exit();
|
|
920
|
-
return;
|
|
921
|
-
}
|
|
922
|
-
if (key.tab) {
|
|
923
|
-
if (focus === "groups") {
|
|
924
|
-
setFocus(getInitialDetailFocus({
|
|
925
|
-
hasAgents: agentInteractiveCount > 0,
|
|
926
|
-
hasSkills: skillInteractiveCount > 0,
|
|
927
|
-
}));
|
|
928
|
-
return;
|
|
929
|
-
}
|
|
930
|
-
setFocus("groups");
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
if (key.rightArrow && focus === "groups") {
|
|
934
|
-
setFocus(getInitialDetailFocus({
|
|
935
|
-
hasAgents: agentInteractiveCount > 0,
|
|
936
|
-
hasSkills: skillInteractiveCount > 0,
|
|
937
|
-
}));
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
if (key.leftArrow && focus !== "groups") {
|
|
941
|
-
setFocus("groups");
|
|
942
|
-
return;
|
|
943
|
-
}
|
|
944
|
-
if (focus === "groups") {
|
|
945
|
-
if (key.downArrow) {
|
|
946
|
-
const next = Math.min(groupCursor + 1, Math.max(0, groupViews.length - 1));
|
|
947
|
-
setGroupCursor(next);
|
|
948
|
-
setSelectedGroupIndex(next);
|
|
949
|
-
setTargetCursor(0);
|
|
950
|
-
setSkillCursor(0);
|
|
951
|
-
setActionCursor(0);
|
|
952
|
-
}
|
|
953
|
-
if (key.upArrow) {
|
|
954
|
-
const next = Math.max(groupCursor - 1, 0);
|
|
955
|
-
setGroupCursor(next);
|
|
956
|
-
setSelectedGroupIndex(next);
|
|
957
|
-
setTargetCursor(0);
|
|
958
|
-
setSkillCursor(0);
|
|
959
|
-
setActionCursor(0);
|
|
960
|
-
}
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
|
-
if (key.downArrow || key.upArrow) {
|
|
964
|
-
const next = moveDetailFocus({
|
|
965
|
-
actionCursor,
|
|
966
|
-
actionCount,
|
|
967
|
-
agentCount: agentInteractiveCount,
|
|
968
|
-
agentCursor: targetCursor,
|
|
969
|
-
direction: key.downArrow ? 1 : -1,
|
|
970
|
-
focus: focus,
|
|
971
|
-
skillCount: skillInteractiveCount,
|
|
972
|
-
skillCursor,
|
|
973
|
-
});
|
|
974
|
-
setFocus(next.focus);
|
|
975
|
-
setTargetCursor(next.agentCursor);
|
|
976
|
-
setSkillCursor(next.skillCursor);
|
|
977
|
-
setActionCursor(next.actionCursor);
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
if (focus === "detail.agents" && input === " " && agentInteractiveCount > 0) {
|
|
981
|
-
updateSelectedDraft((currentDraft) => {
|
|
982
|
-
if (targetCursor === 0) {
|
|
983
|
-
const enabledTargets = new Set(currentDraft.enabledTargets);
|
|
984
|
-
const nextSelectAll = !visibleTargets.every((target) => enabledTargets.has(target));
|
|
985
|
-
for (const target of visibleTargets) {
|
|
986
|
-
if (nextSelectAll) {
|
|
987
|
-
enabledTargets.add(target);
|
|
988
|
-
}
|
|
989
|
-
else {
|
|
990
|
-
enabledTargets.delete(target);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
return {
|
|
994
|
-
...currentDraft,
|
|
995
|
-
enabledTargets: TARGET_ORDER.filter((target) => enabledTargets.has(target)),
|
|
996
|
-
};
|
|
997
|
-
}
|
|
998
|
-
const target = visibleTargets[targetCursor - 1];
|
|
999
|
-
if (!target) {
|
|
1000
|
-
return currentDraft;
|
|
1001
|
-
}
|
|
1002
|
-
const enabledTargets = new Set(currentDraft.enabledTargets);
|
|
1003
|
-
if (enabledTargets.has(target)) {
|
|
1004
|
-
enabledTargets.delete(target);
|
|
1005
|
-
}
|
|
1006
|
-
else {
|
|
1007
|
-
enabledTargets.add(target);
|
|
1008
|
-
}
|
|
1009
|
-
return {
|
|
1010
|
-
...currentDraft,
|
|
1011
|
-
enabledTargets: TARGET_ORDER.filter((item) => enabledTargets.has(item)),
|
|
1012
|
-
};
|
|
1013
|
-
});
|
|
1014
|
-
return;
|
|
1015
|
-
}
|
|
1016
|
-
if (focus === "detail.skills" && input === " " && skillInteractiveCount > 0) {
|
|
1017
|
-
updateSelectedDraft((currentDraft) => {
|
|
1018
|
-
if (selectedGroup.kind === "clawhub") {
|
|
1019
|
-
if (skillCursor === 0) {
|
|
1020
|
-
return currentDraft;
|
|
1021
|
-
}
|
|
1022
|
-
const row = selectedSkillRows[skillCursor - 1];
|
|
1023
|
-
if (!row) {
|
|
1024
|
-
return currentDraft;
|
|
1025
|
-
}
|
|
1026
|
-
if (row.summary.source.id !== selectedSourceId) {
|
|
1027
|
-
return currentDraft;
|
|
1028
|
-
}
|
|
1029
|
-
const baseState = {
|
|
1030
|
-
allLeafIds: row.summary.leafs.map((leaf) => leaf.id),
|
|
1031
|
-
selectedLeafIds: currentDraft.selectedLeafIds,
|
|
1032
|
-
};
|
|
1033
|
-
const nextState = toggleChild(baseState, row.leaf.id);
|
|
1034
|
-
return {
|
|
1035
|
-
...currentDraft,
|
|
1036
|
-
selectedLeafIds: nextState.selectedLeafIds,
|
|
1037
|
-
};
|
|
1038
|
-
}
|
|
1039
|
-
const baseState = {
|
|
1040
|
-
allLeafIds: leafIds,
|
|
1041
|
-
selectedLeafIds: currentDraft.selectedLeafIds,
|
|
1042
|
-
};
|
|
1043
|
-
const nextState = skillCursor === 0
|
|
1044
|
-
? toggleParent(baseState)
|
|
1045
|
-
: toggleChild(baseState, leafIds[skillCursor - 1]);
|
|
1046
|
-
return {
|
|
1047
|
-
...currentDraft,
|
|
1048
|
-
selectedLeafIds: nextState.selectedLeafIds,
|
|
1049
|
-
};
|
|
1050
|
-
});
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
if (groupViews.length === 0) {
|
|
1054
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "No skills groups yet" }), _jsx(Text, { children: "Run skill-flow add <source> to install your first group." }), _jsx(Text, { dimColor: true, children: deleteState.message ?? "Press q or esc to exit." })] }));
|
|
1055
|
-
}
|
|
1056
|
-
const renderSummary = activeSummary;
|
|
1057
|
-
const terminalRows = terminalSize.rows;
|
|
1058
|
-
const terminalColumns = terminalSize.columns;
|
|
1059
|
-
const paneHeight = Math.max(12, terminalRows - 3);
|
|
1060
|
-
const [groupsWidth, detailWidth] = getPaneWidths(terminalColumns);
|
|
1061
|
-
const bodyRowCount = getPaneViewportCount(paneHeight);
|
|
1062
|
-
const groupRows = buildScrollableRows(groupViews.map((group, index) => {
|
|
1063
|
-
const isCursor = groupCursor === index;
|
|
1064
|
-
const isSelected = selectedGroupIndex === index;
|
|
1065
|
-
return {
|
|
1066
|
-
key: group.id,
|
|
1067
|
-
text: `${group.title}${group.kind === "clawhub"
|
|
1068
|
-
? group.summaries.some((summary) => failedBootBySourceId.has(summary.source.id))
|
|
1069
|
-
? " !"
|
|
1070
|
-
: ""
|
|
1071
|
-
: failedBootBySourceId.has(group.summaries[0]?.source.id ?? "")
|
|
1072
|
-
? " !"
|
|
1073
|
-
: ""}`,
|
|
1074
|
-
active: focus === "groups" && isCursor,
|
|
1075
|
-
activeColor: "cyan",
|
|
1076
|
-
bold: isSelected,
|
|
1077
|
-
color: isSelected ? "white" : "gray",
|
|
1078
|
-
};
|
|
1079
|
-
}), Math.max(0, groupCursor), bodyRowCount, "groups");
|
|
1080
|
-
const agentRows = visibleTargets.length > 0
|
|
1081
|
-
? [
|
|
1082
|
-
{
|
|
1083
|
-
key: "__all_targets__",
|
|
1084
|
-
text: `${selectionMarker(allTargetsSelected ? "full" : visibleEnabledTargets.length > 0 ? "partial" : "empty")} All Agents`,
|
|
1085
|
-
active: focus === "detail.agents" && targetCursor === 0,
|
|
1086
|
-
bold: true,
|
|
1087
|
-
color: undefined,
|
|
1088
|
-
},
|
|
1089
|
-
...visibleTargets.map((target, index) => ({
|
|
1090
|
-
key: target,
|
|
1091
|
-
text: `${selectionMarker(selectedDraft.enabledTargets.includes(target) ? "full" : "empty")} ${TARGET_LABELS[target]}`,
|
|
1092
|
-
active: focus === "detail.agents" && targetCursor === index + 1,
|
|
1093
|
-
color: "gray",
|
|
1094
|
-
})),
|
|
1095
|
-
]
|
|
1096
|
-
: [
|
|
1097
|
-
{
|
|
1098
|
-
key: "__no_targets__",
|
|
1099
|
-
text: "No detected agent targets",
|
|
1100
|
-
active: false,
|
|
1101
|
-
color: "gray",
|
|
1102
|
-
},
|
|
1103
|
-
];
|
|
1104
|
-
const skillRows = selectedSkillRows.length > 0
|
|
1105
|
-
? [
|
|
1106
|
-
{
|
|
1107
|
-
key: "__all__",
|
|
1108
|
-
text: `${selectionMarker(parentSelectionState)} All Skills`,
|
|
1109
|
-
active: focus === "detail.skills" && skillCursor === 0,
|
|
1110
|
-
bold: true,
|
|
1111
|
-
color: undefined,
|
|
1112
|
-
},
|
|
1113
|
-
...selectedSkillRows.map((row, index) => {
|
|
1114
|
-
const rowDraft = drafts[row.summary.source.id] ?? EMPTY_DRAFT;
|
|
1115
|
-
const rowSelected = rowDraft.selectedLeafIds.includes(row.leaf.id);
|
|
1116
|
-
const warnings = [
|
|
1117
|
-
...row.leaf.metadataWarnings,
|
|
1118
|
-
...(row.summary.source.id === selectedSourceId
|
|
1119
|
-
? (projectionWarningsByLeafId[row.leaf.id] ?? [])
|
|
1120
|
-
: []),
|
|
1121
|
-
];
|
|
1122
|
-
const inlineWarning = warnings[0] ? ` (${warnings[0]})` : "";
|
|
1123
|
-
const projectedLabel = row.summary.source.id === selectedSourceId && rowSelected
|
|
1124
|
-
? (projectedNamesByLeafId.get(row.leaf.id) ?? row.leaf.linkName)
|
|
1125
|
-
: row.leaf.linkName;
|
|
1126
|
-
const label = selectedGroup.kind === "clawhub"
|
|
1127
|
-
? `${projectedLabel} · ${formatGroupLabel(row.summary.source)}`
|
|
1128
|
-
: projectedLabel;
|
|
1129
|
-
return {
|
|
1130
|
-
key: row.leaf.id,
|
|
1131
|
-
text: `${selectionMarker(rowSelected ? "full" : "empty")} ${label}${inlineWarning}`,
|
|
1132
|
-
active: focus === "detail.skills" && skillCursor === index + 1,
|
|
1133
|
-
color: warnings.length > 0 ? "yellow" : "gray",
|
|
1134
|
-
};
|
|
1135
|
-
}),
|
|
1136
|
-
]
|
|
1137
|
-
: [
|
|
1138
|
-
{
|
|
1139
|
-
key: "__no_skills__",
|
|
1140
|
-
text: "No skills in this group",
|
|
1141
|
-
active: false,
|
|
1142
|
-
color: "gray",
|
|
1143
|
-
},
|
|
1144
|
-
];
|
|
1145
|
-
const previewLabel = buildPreviewLabel({
|
|
1146
|
-
blockedCount: previewState.blockedCount,
|
|
1147
|
-
changeCount,
|
|
1148
|
-
errorMessage: previewState.errorMessage,
|
|
1149
|
-
loading: previewState.loading,
|
|
1150
|
-
});
|
|
1151
|
-
const metadataRows = buildDetailMetadataRows({
|
|
1152
|
-
detailWidth,
|
|
1153
|
-
group: selectedGroup,
|
|
1154
|
-
summary: renderSummary,
|
|
1155
|
-
});
|
|
1156
|
-
const actionRows = buildActionRows({
|
|
1157
|
-
actionCursor,
|
|
1158
|
-
canRunActions,
|
|
1159
|
-
deleteState,
|
|
1160
|
-
focus,
|
|
1161
|
-
isSelectedDelete,
|
|
1162
|
-
showDeleteAction,
|
|
1163
|
-
updateState,
|
|
1164
|
-
});
|
|
1165
|
-
const fixedRows = metadataRows.length +
|
|
1166
|
-
4 +
|
|
1167
|
-
actionRows.length;
|
|
1168
|
-
const sectionBudget = Math.max(2, bodyRowCount - fixedRows);
|
|
1169
|
-
const hasAgentSection = visibleTargets.length > 0;
|
|
1170
|
-
const hasSkillSection = selectedSkillRows.length > 0;
|
|
1171
|
-
const agentBudget = hasAgentSection && hasSkillSection
|
|
1172
|
-
? Math.min(Math.max(1, sectionBudget - 1), Math.max(4, Math.floor(sectionBudget * 0.4)))
|
|
1173
|
-
: sectionBudget;
|
|
1174
|
-
const skillBudget = hasAgentSection && hasSkillSection ? Math.max(1, sectionBudget - agentBudget) : sectionBudget;
|
|
1175
|
-
const visibleAgentRows = buildScrollableRows(agentRows, Math.min(targetCursor, Math.max(0, agentRows.length - 1)), Math.max(1, agentBudget), "agents", true);
|
|
1176
|
-
const visibleSkillRows = buildScrollableRows(skillRows, Math.min(skillCursor, Math.max(0, skillRows.length - 1)), Math.max(1, skillBudget), "skills", true);
|
|
1177
|
-
const filledSkillRows = [...visibleSkillRows.rows];
|
|
1178
|
-
const skillPaddingCount = Math.max(0, skillBudget - filledSkillRows.length);
|
|
1179
|
-
for (let index = 0; index < skillPaddingCount; index += 1) {
|
|
1180
|
-
filledSkillRows.push({
|
|
1181
|
-
key: `__skills_fill__:${index}`,
|
|
1182
|
-
text: "",
|
|
1183
|
-
active: false,
|
|
1184
|
-
color: undefined,
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
const detailRows = [
|
|
1188
|
-
...metadataRows,
|
|
1189
|
-
{
|
|
1190
|
-
key: "__agents_gap__",
|
|
1191
|
-
text: "",
|
|
1192
|
-
active: false,
|
|
1193
|
-
color: undefined,
|
|
1194
|
-
},
|
|
1195
|
-
{
|
|
1196
|
-
key: "__agents_header__",
|
|
1197
|
-
text: `Select Agents (${visibleEnabledTargets.length}/${availableTargets.length})`,
|
|
1198
|
-
active: false,
|
|
1199
|
-
bold: true,
|
|
1200
|
-
color: undefined,
|
|
1201
|
-
},
|
|
1202
|
-
...visibleAgentRows.rows,
|
|
1203
|
-
{
|
|
1204
|
-
key: "__skills_gap__",
|
|
1205
|
-
text: "",
|
|
1206
|
-
active: false,
|
|
1207
|
-
color: undefined,
|
|
1208
|
-
},
|
|
1209
|
-
{
|
|
1210
|
-
key: "__skills_header__",
|
|
1211
|
-
text: `Select Skills (${selectedGroup.kind === "clawhub" ? groupSelectedLeafCount : selectedDraft.selectedLeafIds.length}/${selectedSkillRows.length})`,
|
|
1212
|
-
active: false,
|
|
1213
|
-
bold: true,
|
|
1214
|
-
color: undefined,
|
|
1215
|
-
},
|
|
1216
|
-
...filledSkillRows,
|
|
1217
|
-
];
|
|
1218
|
-
const topBar = buildTopBar({
|
|
1219
|
-
width: terminalColumns,
|
|
1220
|
-
isDirty,
|
|
1221
|
-
changeCount,
|
|
1222
|
-
showDelete: canDelete,
|
|
1223
|
-
statusLabel: statusDisplay.label,
|
|
1224
|
-
});
|
|
1225
|
-
return (_jsxs(Box, { flexDirection: "column", height: terminalRows, children: [_jsx(ConfigHeader, {}), _jsxs(Box, { children: [_jsx(Pane, { active: focus === "groups", footer: `${groupViews.length} groups`, gapAfter: true, height: paneHeight, title: "Skills Groups", width: groupsWidth, children: renderPaneRows(groupRows.rows, bodyRowCount, groupsWidth) }), _jsx(Pane, { active: focus !== "groups", footer: buildCommandBar(focus), height: paneHeight, title: "Group Detail", width: detailWidth, children: renderPaneRows(detailRows, bodyRowCount, detailWidth, actionRows) })] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: buildFooterHints(focus, canDelete) })] }));
|
|
1226
|
-
}
|
|
1227
|
-
export function ConfigBootstrapApp({ app }) {
|
|
1228
|
-
const { exit } = useApp();
|
|
1229
|
-
const { stdout } = useStdout();
|
|
1230
|
-
const terminalSize = useTerminalSize(stdout);
|
|
1231
|
-
const [state, setState] = useState({
|
|
1232
|
-
phase: "loading",
|
|
1233
|
-
logs: ["Booting config..."],
|
|
1234
|
-
});
|
|
1235
|
-
useEffect(() => {
|
|
1236
|
-
let cancelled = false;
|
|
1237
|
-
void app.configCoordinator.bootstrapWorkspaceState((event) => {
|
|
1238
|
-
if (cancelled) {
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
setState((current) => {
|
|
1242
|
-
const nextLogs = [...current.logs, event.message].slice(-6);
|
|
1243
|
-
return {
|
|
1244
|
-
...current,
|
|
1245
|
-
logs: nextLogs,
|
|
1246
|
-
};
|
|
1247
|
-
});
|
|
1248
|
-
}).then((result) => {
|
|
1249
|
-
if (cancelled) {
|
|
1250
|
-
return;
|
|
1251
|
-
}
|
|
1252
|
-
if (!result.ok) {
|
|
1253
|
-
setState((current) => ({
|
|
1254
|
-
phase: "error",
|
|
1255
|
-
logs: current.logs,
|
|
1256
|
-
message: firstErrorMessage(result),
|
|
1257
|
-
}));
|
|
1258
|
-
return;
|
|
1259
|
-
}
|
|
1260
|
-
setState((current) => ({
|
|
1261
|
-
phase: "ready",
|
|
1262
|
-
logs: current.logs,
|
|
1263
|
-
availableTargets: result.data.availableTargets,
|
|
1264
|
-
summaries: result.data.summaries,
|
|
1265
|
-
initialDrafts: result.data.initialDrafts,
|
|
1266
|
-
audit: result.data.audit,
|
|
1267
|
-
bootStatus: result.data.bootStatus,
|
|
1268
|
-
}));
|
|
1269
|
-
});
|
|
1270
|
-
return () => {
|
|
1271
|
-
cancelled = true;
|
|
1272
|
-
};
|
|
1273
|
-
}, [app]);
|
|
1274
|
-
useInput((input, key) => {
|
|
1275
|
-
if (state.phase === "ready") {
|
|
1276
|
-
return;
|
|
1277
|
-
}
|
|
1278
|
-
if (input === "q" || key.escape || (input === "c" && key.ctrl)) {
|
|
1279
|
-
exit();
|
|
1280
|
-
}
|
|
1281
|
-
});
|
|
1282
|
-
if (state.phase === "ready") {
|
|
1283
|
-
return (_jsx(ConfigApp, { app: app, availableTargets: state.availableTargets, summaries: state.summaries, initialDrafts: state.initialDrafts, bootStatus: state.bootStatus }));
|
|
1284
|
-
}
|
|
1285
|
-
const rows = terminalSize.rows;
|
|
1286
|
-
const bootLogs = state.logs.slice(-4);
|
|
1287
|
-
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsxs(Box, { flexGrow: 1, flexDirection: "column", children: [_jsx(ConfigHeader, { title: "Skill Flow Config" }), _jsx(Text, { color: "gray", children: state.phase === "loading"
|
|
1288
|
-
? "Checking groups, skills, targets, and current paths..."
|
|
1289
|
-
: "Bootstrap failed" }), state.phase === "error" ? _jsx(Text, { color: "red", children: state.message }) : null] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "BOOT LOG" }), bootLogs.map((log) => (_jsx(Text, { color: "gray", children: log }, log))), _jsx(Text, { color: "gray", children: "Press q or Esc to exit." })] })] }));
|
|
1290
|
-
}
|
|
1291
|
-
function ConfigHeader({ title }) {
|
|
1292
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { backgroundColor: "cyan", color: "black", children: ADD_BADGE_TEXT }), title ? _jsx(Text, { bold: true, children: title }) : null] }));
|
|
1293
|
-
}
|
|
1294
|
-
function useTerminalSize(stdout) {
|
|
1295
|
-
const [size, setSize] = useState(() => ({
|
|
1296
|
-
rows: stdout.rows ?? 24,
|
|
1297
|
-
columns: stdout.columns ?? 120,
|
|
1298
|
-
}));
|
|
1299
|
-
useEffect(() => {
|
|
1300
|
-
const handleResize = () => {
|
|
1301
|
-
setSize({
|
|
1302
|
-
rows: stdout.rows ?? 24,
|
|
1303
|
-
columns: stdout.columns ?? 120,
|
|
1304
|
-
});
|
|
1305
|
-
};
|
|
1306
|
-
handleResize();
|
|
1307
|
-
stdout.on("resize", handleResize);
|
|
1308
|
-
return () => {
|
|
1309
|
-
stdout.off("resize", handleResize);
|
|
1310
|
-
};
|
|
1311
|
-
}, [stdout]);
|
|
1312
|
-
return size;
|
|
1313
|
-
}
|
|
1314
|
-
function buildSaveStatusLabel(saveState) {
|
|
1315
|
-
if (saveState.phase === "saving") {
|
|
1316
|
-
return "Saving";
|
|
1317
|
-
}
|
|
1318
|
-
if (saveState.phase === "saved") {
|
|
1319
|
-
return "Saved";
|
|
1320
|
-
}
|
|
1321
|
-
if (saveState.phase === "failed") {
|
|
1322
|
-
return "Failed";
|
|
1323
|
-
}
|
|
1324
|
-
return "Clean";
|
|
1325
|
-
}
|
|
1326
|
-
function buildPreviewLabel({ blockedCount, changeCount, errorMessage, loading, }) {
|
|
1327
|
-
if (loading) {
|
|
1328
|
-
return "planning...";
|
|
1329
|
-
}
|
|
1330
|
-
if (errorMessage) {
|
|
1331
|
-
return "failed";
|
|
1332
|
-
}
|
|
1333
|
-
if (blockedCount > 0 && changeCount > 0) {
|
|
1334
|
-
return `${changeCount} changes, ${blockedCount} blocked`;
|
|
1335
|
-
}
|
|
1336
|
-
if (blockedCount > 0) {
|
|
1337
|
-
return `${blockedCount} blocked`;
|
|
1338
|
-
}
|
|
1339
|
-
return `${changeCount} changes`;
|
|
1340
|
-
}
|
|
1341
|
-
function buildAlerts({ deleteState, failedBootMessages, isSelectedDelete, previewState, projectionWarningsByLeafId, saveState, selectedDraft, selectedSummary, updateState, }) {
|
|
1342
|
-
const alerts = [];
|
|
1343
|
-
if (updateState.phase === "failed" && updateState.message) {
|
|
1344
|
-
alerts.push({ level: "error", message: `Update failed: ${updateState.message}` });
|
|
1345
|
-
}
|
|
1346
|
-
if (isSelectedDelete && deleteState.phase === "failed" && deleteState.message) {
|
|
1347
|
-
alerts.push({ level: "error", message: `Delete failed: ${deleteState.message}` });
|
|
1348
|
-
}
|
|
1349
|
-
if (saveState.phase === "failed" && saveState.message) {
|
|
1350
|
-
alerts.push({ level: "error", message: `Save failed: ${saveState.message}` });
|
|
1351
|
-
}
|
|
1352
|
-
if (previewState.errorMessage) {
|
|
1353
|
-
alerts.push({ level: "error", message: `Preview failed: ${previewState.errorMessage}` });
|
|
1354
|
-
}
|
|
1355
|
-
for (const failedBootMessage of failedBootMessages) {
|
|
1356
|
-
alerts.push({ level: "error", message: `Boot issue: ${failedBootMessage}` });
|
|
1357
|
-
}
|
|
1358
|
-
for (const action of previewState.actions) {
|
|
1359
|
-
if (action.kind === "blocked" && action.reason) {
|
|
1360
|
-
alerts.push({ level: "blocked", message: action.reason });
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
for (const leaf of selectedSummary.leafs) {
|
|
1364
|
-
if (!selectedDraft.selectedLeafIds.includes(leaf.id)) {
|
|
1365
|
-
continue;
|
|
1366
|
-
}
|
|
1367
|
-
for (const warning of leaf.metadataWarnings) {
|
|
1368
|
-
alerts.push({ level: "warning", message: warning });
|
|
1369
|
-
}
|
|
1370
|
-
for (const warning of projectionWarningsByLeafId[leaf.id] ?? []) {
|
|
1371
|
-
alerts.push({ level: "warning", message: warning });
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
if ((selectedSummary.lock?.invalidLeafs.length ?? 0) > 0) {
|
|
1375
|
-
alerts.push({
|
|
1376
|
-
level: "warning",
|
|
1377
|
-
message: `${selectedSummary.lock?.invalidLeafs.length ?? 0} invalid skill entries skipped`,
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
return alerts;
|
|
1381
|
-
}
|
|
1382
|
-
export function buildDetailMetadataRows({ detailWidth, group, summary, }) {
|
|
1383
|
-
const sourceSummary = group.kind === "clawhub"
|
|
1384
|
-
? `Sources: ${group.summaries.length} clawhub source${group.summaries.length === 1 ? "" : "s"}`
|
|
1385
|
-
: `Source: ${summary.source.locator}`;
|
|
1386
|
-
const rows = [
|
|
1387
|
-
{
|
|
1388
|
-
key: "__title__",
|
|
1389
|
-
text: group.title,
|
|
1390
|
-
active: false,
|
|
1391
|
-
bold: true,
|
|
1392
|
-
color: undefined,
|
|
1393
|
-
},
|
|
1394
|
-
{
|
|
1395
|
-
key: "__source__",
|
|
1396
|
-
text: fitPaneLine(sourceSummary, getPaneInnerWidth(detailWidth) - 2),
|
|
1397
|
-
active: false,
|
|
1398
|
-
color: "gray",
|
|
1399
|
-
},
|
|
1400
|
-
];
|
|
1401
|
-
if (summary.lock?.checkoutPath) {
|
|
1402
|
-
rows.push({
|
|
1403
|
-
key: "__local_path__",
|
|
1404
|
-
text: fitPaneLine(`Local Path: ${summary.lock.checkoutPath}`, getPaneInnerWidth(detailWidth) - 2),
|
|
1405
|
-
active: false,
|
|
1406
|
-
color: "gray",
|
|
1407
|
-
});
|
|
1408
|
-
}
|
|
1409
|
-
if (group.kind === "clawhub") {
|
|
1410
|
-
rows.push({
|
|
1411
|
-
key: "__focused_source__",
|
|
1412
|
-
text: fitPaneLine(`Focused Source: ${formatGroupLabel(summary.source)}`, getPaneInnerWidth(detailWidth) - 2),
|
|
1413
|
-
active: false,
|
|
1414
|
-
color: "gray",
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1417
|
-
return rows;
|
|
1418
|
-
}
|
|
1419
|
-
export function buildActionRows({ actionCursor, canRunActions, deleteState, focus, isSelectedDelete, showDeleteAction, updateState, }) {
|
|
1420
|
-
const updateText = updateState.phase === "updating"
|
|
1421
|
-
? "Update · UPDATING..."
|
|
1422
|
-
: updateState.phase === "failed"
|
|
1423
|
-
? "Update · FAILED"
|
|
1424
|
-
: "Update";
|
|
1425
|
-
const rows = [
|
|
1426
|
-
{
|
|
1427
|
-
key: "__actions_separator__",
|
|
1428
|
-
text: "────────────────────────",
|
|
1429
|
-
active: false,
|
|
1430
|
-
color: "gray",
|
|
1431
|
-
},
|
|
1432
|
-
{
|
|
1433
|
-
key: "__action_update__",
|
|
1434
|
-
text: `[${updateText}]`,
|
|
1435
|
-
active: focus === "detail.actions" && actionCursor === 0,
|
|
1436
|
-
color: canRunActions || updateState.phase !== "idle" ? undefined : "gray",
|
|
1437
|
-
bold: true,
|
|
1438
|
-
},
|
|
1439
|
-
];
|
|
1440
|
-
if (showDeleteAction) {
|
|
1441
|
-
const deleteText = isSelectedDelete && deleteState.phase === "deleting"
|
|
1442
|
-
? "Delete · DELETING..."
|
|
1443
|
-
: isSelectedDelete && deleteState.phase === "failed"
|
|
1444
|
-
? "Delete · FAILED"
|
|
1445
|
-
: "Delete";
|
|
1446
|
-
rows.push({
|
|
1447
|
-
key: "__action_delete__",
|
|
1448
|
-
text: `[${deleteText}]`,
|
|
1449
|
-
active: focus === "detail.actions" && actionCursor === 1,
|
|
1450
|
-
color: isSelectedDelete && deleteState.phase === "failed"
|
|
1451
|
-
? "red"
|
|
1452
|
-
: canRunActions || (isSelectedDelete && deleteState.phase !== "idle")
|
|
1453
|
-
? "red"
|
|
1454
|
-
: "gray",
|
|
1455
|
-
bold: true,
|
|
1456
|
-
});
|
|
1457
|
-
}
|
|
1458
|
-
return rows;
|
|
1459
|
-
}
|
|
1460
|
-
export function buildCommandBar(focus) {
|
|
1461
|
-
if (focus === "groups") {
|
|
1462
|
-
return "[Tab/→] Edit";
|
|
1463
|
-
}
|
|
1464
|
-
if (focus === "detail.actions") {
|
|
1465
|
-
return "[Enter] Action";
|
|
1466
|
-
}
|
|
1467
|
-
return "[Space] Toggle";
|
|
1468
|
-
}
|
|
1469
|
-
export function buildFooterHints(focus, canDelete) {
|
|
1470
|
-
if (focus === "groups") {
|
|
1471
|
-
return canDelete
|
|
1472
|
-
? "[↑↓] Move [Tab/→] Switch pane [u] Update [d] Delete [q] Exit"
|
|
1473
|
-
: "[↑↓] Move [Tab/→] Switch pane [u] Update [q] Exit";
|
|
1474
|
-
}
|
|
1475
|
-
if (focus === "detail.actions") {
|
|
1476
|
-
return canDelete
|
|
1477
|
-
? "[↑↓] Move [Enter] Action [Tab/←/Esc] Back [u] Update [d] Delete"
|
|
1478
|
-
: "[↑↓] Move [Enter] Action [Tab/←/Esc] Back [u] Update";
|
|
1479
|
-
}
|
|
1480
|
-
return canDelete
|
|
1481
|
-
? "[↑↓] Move [Space] Toggle [Tab/←/Esc] Back [u] Update [d] Delete"
|
|
1482
|
-
: "[↑↓] Move [Space] Toggle [Tab/←/Esc] Back [u] Update";
|
|
1483
|
-
}
|
|
1484
|
-
function RowText({ row, width }) {
|
|
1485
|
-
const color = row.active ? row.activeColor ?? "cyan" : row.color;
|
|
1486
|
-
const prefix = row.active ? "❯ " : " ";
|
|
1487
|
-
const contentWidth = Math.max(1, getPaneInnerWidth(width) - prefix.length);
|
|
1488
|
-
const content = fitPaneLine(row.text, contentWidth);
|
|
1489
|
-
return (_jsxs(Text, { wrap: "truncate-end", ...(color ? { color } : {}), ...(row.bold ? { bold: true } : {}), children: [prefix, content] }));
|
|
1490
|
-
}
|
|
1491
|
-
function Pane({ title, active, width, children, height, footer, gapAfter = false, }) {
|
|
1492
|
-
return (_jsxs(Box, { flexDirection: "column", width: width, height: height, marginRight: gapAfter ? 1 : 0, paddingX: 1, borderStyle: "round", borderColor: active ? "cyan" : "gray", children: [_jsx(Text, { bold: true, wrap: "truncate-end", children: fitPaneLine(title, getPaneInnerWidth(width)) }), _jsx(Text, { children: " " }), children, _jsx(Text, { dimColor: true, wrap: "truncate-middle", children: fitPaneLine(footer, getPaneInnerWidth(width)) })] }));
|
|
1493
|
-
}
|
|
1494
|
-
function renderPaneRows(rows, bodyRowCount, paneWidth, tailRows = []) {
|
|
1495
|
-
const items = rows.map((row) => (_jsx(RowText, { row: row, width: paneWidth }, row.key)));
|
|
1496
|
-
const blankCount = Math.max(0, bodyRowCount - rows.length - tailRows.length);
|
|
1497
|
-
for (let index = 0; index < blankCount; index += 1) {
|
|
1498
|
-
items.push(_jsx(Text, { children: " " }, `__blank__:${index}`));
|
|
1499
|
-
}
|
|
1500
|
-
for (const row of tailRows) {
|
|
1501
|
-
items.push(_jsx(RowText, { row: row, width: paneWidth }, row.key));
|
|
1502
|
-
}
|
|
1503
|
-
return items;
|
|
1504
|
-
}
|
|
1505
|
-
function fitPaneLine(text, width) {
|
|
1506
|
-
if (text.length <= width) {
|
|
1507
|
-
return text.padEnd(width, " ");
|
|
1508
|
-
}
|
|
1509
|
-
if (width <= 1) {
|
|
1510
|
-
return "…";
|
|
1511
|
-
}
|
|
1512
|
-
return `${text.slice(0, width - 1)}…`;
|
|
1513
|
-
}
|
|
1514
|
-
function getPaneInnerWidth(width) {
|
|
1515
|
-
return Math.max(1, width - 4);
|
|
1516
|
-
}
|
|
1517
|
-
export function selectionMarker(state) {
|
|
1518
|
-
if (state === "full") {
|
|
1519
|
-
return "●";
|
|
1520
|
-
}
|
|
1521
|
-
if (state === "partial") {
|
|
1522
|
-
return "◐";
|
|
1523
|
-
}
|
|
1524
|
-
return "○";
|
|
1525
|
-
}
|
|
1526
|
-
function getExactDuplicateKey(linkName, name, description) {
|
|
1527
|
-
return `${linkName}\n${name}\n${description}`;
|
|
1528
|
-
}
|
|
1529
|
-
function getWindowedRows(items, cursorIndex, visibleCount) {
|
|
1530
|
-
const safeVisibleCount = Math.max(1, visibleCount);
|
|
1531
|
-
const maxStart = Math.max(0, items.length - safeVisibleCount);
|
|
1532
|
-
const start = Math.min(Math.max(0, cursorIndex - Math.floor(safeVisibleCount / 2)), maxStart);
|
|
1533
|
-
const end = Math.min(items.length, start + safeVisibleCount);
|
|
1534
|
-
return {
|
|
1535
|
-
rows: items.slice(start, end),
|
|
1536
|
-
start,
|
|
1537
|
-
end,
|
|
1538
|
-
};
|
|
1539
|
-
}
|
|
1540
|
-
function pruneSourceMap(sourceMap, allowedIds) {
|
|
1541
|
-
return Object.fromEntries(Object.entries(sourceMap).filter(([sourceId]) => allowedIds.has(sourceId)));
|
|
1542
|
-
}
|
|
1543
|
-
function removeSourceFromMap(sourceMap, sourceId) {
|
|
1544
|
-
const next = { ...sourceMap };
|
|
1545
|
-
delete next[sourceId];
|
|
1546
|
-
return next;
|
|
1547
|
-
}
|
|
1548
|
-
function firstErrorMessage(result) {
|
|
1549
|
-
return result.errors[0]?.message ?? "operation failed";
|
|
1550
|
-
}
|
|
1551
|
-
//# sourceMappingURL=config-app.js.map
|