pi-model-profiles 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/package.json +66 -66
- package/src/agent-writer.ts +28 -6
- package/src/command-handler.ts +137 -0
- package/src/errors.ts +18 -16
- package/src/frontmatter-parser.ts +250 -249
- package/src/import-service.ts +60 -60
- package/src/index.ts +3 -125
- package/src/profile-fields.ts +93 -83
- package/src/profile-removal-service.ts +106 -106
- package/src/profile-sort-service.ts +105 -105
- package/src/profile-update-service.ts +134 -134
|
@@ -1,249 +1,250 @@
|
|
|
1
|
-
import { PROFILE_FIELD_KEYS, type ProfileFieldKey, type ProfileFields } from "./types.js";
|
|
2
|
-
import { ModelProfilesError } from "./errors.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
let
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
1
|
+
import { PROFILE_FIELD_KEYS, type ProfileFieldKey, type ProfileFields } from "./types.js";
|
|
2
|
+
import { ModelProfilesError } from "./errors.js";
|
|
3
|
+
import { parseCompleteNumericScalar } from "./profile-fields.js";
|
|
4
|
+
|
|
5
|
+
interface FrontmatterDocument {
|
|
6
|
+
frontmatter: string;
|
|
7
|
+
body: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface FrontmatterBlock {
|
|
11
|
+
key: string | null;
|
|
12
|
+
lines: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ANCHOR_KEYS = new Set(["name", "mode", "color", "description"]);
|
|
16
|
+
const SAFE_UNQUOTED_SCALAR = /^[A-Za-z0-9._/@:-]+$/;
|
|
17
|
+
const PROFILE_KEY_SET = new Set<string>(PROFILE_FIELD_KEYS);
|
|
18
|
+
|
|
19
|
+
function normalizeNewlines(value: string): string {
|
|
20
|
+
return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function extractTopLevelKey(line: string): string | null {
|
|
24
|
+
if (!line || line.startsWith(" ") || line.startsWith("\t")) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const match = /^([A-Za-z0-9_-]+):(?:\s*(.*))?$/.exec(line);
|
|
29
|
+
if (!match) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return match[1] ?? null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseScalarText(value: string): string {
|
|
37
|
+
const trimmed = value.trim();
|
|
38
|
+
if (!trimmed) {
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(trimmed) as string;
|
|
44
|
+
} catch {
|
|
45
|
+
return trimmed.slice(1, -1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
49
|
+
return trimmed.slice(1, -1).replace(/''/g, "'");
|
|
50
|
+
}
|
|
51
|
+
return trimmed;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function splitFrontmatterBlocks(frontmatter: string): FrontmatterBlock[] {
|
|
55
|
+
const lines = frontmatter.split("\n");
|
|
56
|
+
const starts: Array<{ index: number; key: string }> = [];
|
|
57
|
+
|
|
58
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
59
|
+
const key = extractTopLevelKey(lines[index] ?? "");
|
|
60
|
+
if (key) {
|
|
61
|
+
starts.push({ index, key });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (starts.length === 0) {
|
|
66
|
+
return [{ key: null, lines }];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const blocks: FrontmatterBlock[] = [];
|
|
70
|
+
if ((starts[0]?.index ?? 0) > 0) {
|
|
71
|
+
blocks.push({ key: null, lines: lines.slice(0, starts[0]?.index) });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (let index = 0; index < starts.length; index += 1) {
|
|
75
|
+
const current = starts[index];
|
|
76
|
+
const next = starts[index + 1];
|
|
77
|
+
blocks.push({
|
|
78
|
+
key: current?.key ?? null,
|
|
79
|
+
lines: lines.slice(current?.index ?? 0, next ? next.index : lines.length),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return blocks.filter((block) => block.lines.length > 0);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function trimBlankEdges(lines: string[]): string[] {
|
|
87
|
+
let start = 0;
|
|
88
|
+
let end = lines.length;
|
|
89
|
+
while (start < end && !(lines[start] ?? "").trim()) {
|
|
90
|
+
start += 1;
|
|
91
|
+
}
|
|
92
|
+
while (end > start && !(lines[end - 1] ?? "").trim()) {
|
|
93
|
+
end -= 1;
|
|
94
|
+
}
|
|
95
|
+
return lines.slice(start, end);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function joinBlocks(blocks: FrontmatterBlock[]): string {
|
|
99
|
+
const lines = blocks.flatMap((block) => block.lines);
|
|
100
|
+
return trimBlankEdges(lines).join("\n");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function serializeScalar(value: string): string {
|
|
104
|
+
return SAFE_UNQUOTED_SCALAR.test(value) ? value : JSON.stringify(value);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function serializeProfileLines(fields: ProfileFields): string[] {
|
|
108
|
+
const lines: string[] = [];
|
|
109
|
+
if (fields.model !== undefined) {
|
|
110
|
+
lines.push(`model: ${serializeScalar(fields.model)}`);
|
|
111
|
+
}
|
|
112
|
+
if (fields.temperature !== undefined) {
|
|
113
|
+
lines.push(`temperature: ${String(fields.temperature)}`);
|
|
114
|
+
}
|
|
115
|
+
if (fields.reasoningEffort !== undefined) {
|
|
116
|
+
lines.push(`reasoningEffort: ${serializeScalar(fields.reasoningEffort)}`);
|
|
117
|
+
}
|
|
118
|
+
return lines;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function findInsertIndex(blocks: FrontmatterBlock[]): number {
|
|
122
|
+
const firstProfileIndex = blocks.findIndex((block) => block.key !== null && PROFILE_KEY_SET.has(block.key));
|
|
123
|
+
if (firstProfileIndex !== -1) {
|
|
124
|
+
let keptBefore = 0;
|
|
125
|
+
for (let index = 0; index < firstProfileIndex; index += 1) {
|
|
126
|
+
if (!PROFILE_KEY_SET.has(blocks[index]?.key ?? "")) {
|
|
127
|
+
keptBefore += 1;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return keptBefore;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let lastAnchorIndex = -1;
|
|
134
|
+
for (let index = 0; index < blocks.length; index += 1) {
|
|
135
|
+
const key = blocks[index]?.key;
|
|
136
|
+
if (key && ANCHOR_KEYS.has(key)) {
|
|
137
|
+
lastAnchorIndex = index;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (lastAnchorIndex !== -1) {
|
|
142
|
+
return lastAnchorIndex + 1;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return blocks[0]?.key === null ? 1 : 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function extractFrontmatterDocument(markdown: string): FrontmatterDocument {
|
|
149
|
+
const normalized = normalizeNewlines(markdown);
|
|
150
|
+
const lines = normalized.split("\n");
|
|
151
|
+
if ((lines[0] ?? "") !== "---") {
|
|
152
|
+
throw new ModelProfilesError("Agent markdown is missing opening frontmatter delimiter.", "INVALID_FRONTMATTER");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let endIndex = -1;
|
|
156
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
157
|
+
if ((lines[index] ?? "") === "---") {
|
|
158
|
+
endIndex = index;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (endIndex === -1) {
|
|
164
|
+
throw new ModelProfilesError("Agent markdown is missing closing frontmatter delimiter.", "INVALID_FRONTMATTER");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
frontmatter: lines.slice(1, endIndex).join("\n"),
|
|
169
|
+
body: lines.slice(endIndex + 1).join("\n"),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function readTopLevelScalarMap(frontmatter: string): Record<string, string> {
|
|
174
|
+
const values: Record<string, string> = {};
|
|
175
|
+
for (const line of frontmatter.split("\n")) {
|
|
176
|
+
const key = extractTopLevelKey(line);
|
|
177
|
+
if (!key) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const separatorIndex = line.indexOf(":");
|
|
181
|
+
const rawValue = line.slice(separatorIndex + 1).trim();
|
|
182
|
+
if (!rawValue) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
values[key] = parseScalarText(rawValue);
|
|
186
|
+
}
|
|
187
|
+
return values;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function readAgentNameFromMarkdown(markdown: string): string {
|
|
191
|
+
const { frontmatter } = extractFrontmatterDocument(markdown);
|
|
192
|
+
const name = readTopLevelScalarMap(frontmatter).name?.trim();
|
|
193
|
+
if (!name) {
|
|
194
|
+
throw new ModelProfilesError("Agent markdown frontmatter is missing a non-empty 'name' field.", "INVALID_AGENT_NAME");
|
|
195
|
+
}
|
|
196
|
+
return name;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function readProfileFieldsFromMarkdown(markdown: string): ProfileFields {
|
|
200
|
+
const { frontmatter } = extractFrontmatterDocument(markdown);
|
|
201
|
+
const values = readTopLevelScalarMap(frontmatter);
|
|
202
|
+
const fields: ProfileFields = {};
|
|
203
|
+
|
|
204
|
+
const model = values.model?.trim();
|
|
205
|
+
if (model) {
|
|
206
|
+
fields.model = model;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (values.temperature !== undefined) {
|
|
210
|
+
const parsedTemperature = parseCompleteNumericScalar(values.temperature);
|
|
211
|
+
if (parsedTemperature === undefined) {
|
|
212
|
+
throw new ModelProfilesError(
|
|
213
|
+
`Frontmatter field 'temperature' must be numeric, received '${values.temperature}'.`,
|
|
214
|
+
"INVALID_TEMPERATURE",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
fields.temperature = parsedTemperature;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const reasoningEffort = values.reasoningEffort?.trim();
|
|
221
|
+
if (reasoningEffort) {
|
|
222
|
+
fields.reasoningEffort = reasoningEffort;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return fields;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function updateMarkdownProfileFields(markdown: string, fields: ProfileFields): string {
|
|
229
|
+
const document = extractFrontmatterDocument(markdown);
|
|
230
|
+
const originalBlocks = splitFrontmatterBlocks(document.frontmatter);
|
|
231
|
+
const keptBlocks = originalBlocks.filter((block) => !PROFILE_KEY_SET.has(block.key ?? ""));
|
|
232
|
+
const insertIndex = findInsertIndex(originalBlocks);
|
|
233
|
+
const profileLines = serializeProfileLines(fields);
|
|
234
|
+
const nextBlocks = [...keptBlocks];
|
|
235
|
+
|
|
236
|
+
if (profileLines.length > 0) {
|
|
237
|
+
nextBlocks.splice(insertIndex, 0, { key: null, lines: profileLines });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const nextFrontmatter = joinBlocks(nextBlocks);
|
|
241
|
+
return `---\n${nextFrontmatter}\n---\n${document.body}`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function listAppliedKeys(fields: ProfileFields): ProfileFieldKey[] {
|
|
245
|
+
return PROFILE_FIELD_KEYS.filter((key) => fields[key] !== undefined);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function listRemovedKeys(fields: ProfileFields): ProfileFieldKey[] {
|
|
249
|
+
return PROFILE_FIELD_KEYS.filter((key) => fields[key] === undefined);
|
|
250
|
+
}
|
package/src/import-service.ts
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import { AGENTS_DIR, INITIAL_PROFILE_NAME, PROFILE_STORE_PATH } from "./constants.js";
|
|
2
|
-
import { captureAgentSnapshots, type AgentSelectionOptions } from "./agent-writer.js";
|
|
3
|
-
import { appendProfile, createProfile, loadProfilesFile, resolveUniqueProfileName, saveProfilesFile } from "./profile-store.js";
|
|
4
|
-
import { loadMultiProfilesConfig } from "./config.js";
|
|
5
|
-
import { multiProfilesDebugLogger } from "./debug-logger.js";
|
|
6
|
-
import type { ImportProfilesResult, ProfilesFile } from "./types.js";
|
|
7
|
-
|
|
8
|
-
export function ensureProfilesImported(
|
|
9
|
-
data: ProfilesFile,
|
|
10
|
-
agentOptions: string | AgentSelectionOptions = AGENTS_DIR,
|
|
11
|
-
): ImportProfilesResult {
|
|
12
|
-
if (data.importedAt) {
|
|
13
|
-
return {
|
|
14
|
-
data,
|
|
15
|
-
imported: false,
|
|
16
|
-
importedCount: 0,
|
|
17
|
-
warnings: [],
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const snapshot = captureAgentSnapshots(agentOptions);
|
|
22
|
-
const timestamp = new Date().toISOString();
|
|
23
|
-
const profile = createProfile(resolveUniqueProfileName(INITIAL_PROFILE_NAME, data.profiles), snapshot.agents, { timestamp });
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
data: {
|
|
27
|
-
...appendProfile(data, profile),
|
|
28
|
-
importedAt: timestamp,
|
|
29
|
-
},
|
|
30
|
-
imported: true,
|
|
31
|
-
importedCount: 1,
|
|
32
|
-
warnings: snapshot.warnings,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function loadAndPrepareProfiles(
|
|
37
|
-
storePath = PROFILE_STORE_PATH,
|
|
38
|
-
agentOptions: string | AgentSelectionOptions = AGENTS_DIR,
|
|
39
|
-
): ImportProfilesResult {
|
|
40
|
-
const configLoad = loadMultiProfilesConfig();
|
|
41
|
-
|
|
42
|
-
multiProfilesDebugLogger.log("extension.initialized", {
|
|
43
|
-
configCreated: configLoad.created,
|
|
44
|
-
timestamp: new Date().toISOString(),
|
|
45
|
-
profilesVersion: 2,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const loaded = loadProfilesFile(storePath, agentOptions);
|
|
49
|
-
const prepared = ensureProfilesImported(loaded.data, agentOptions);
|
|
50
|
-
|
|
51
|
-
if (loaded.needsSave || prepared.imported) {
|
|
52
|
-
saveProfilesFile(prepared.data, storePath);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const warnings = [configLoad.warning, loaded.warning, ...prepared.warnings].filter((message): message is string => Boolean(message));
|
|
56
|
-
return {
|
|
57
|
-
...prepared,
|
|
58
|
-
warnings,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
1
|
+
import { AGENTS_DIR, INITIAL_PROFILE_NAME, PROFILE_STORE_PATH } from "./constants.js";
|
|
2
|
+
import { captureAgentSnapshots, type AgentSelectionOptions } from "./agent-writer.js";
|
|
3
|
+
import { appendProfile, createProfile, loadProfilesFile, resolveUniqueProfileName, saveProfilesFile } from "./profile-store.js";
|
|
4
|
+
import { loadMultiProfilesConfig } from "./config.js";
|
|
5
|
+
import { multiProfilesDebugLogger } from "./debug-logger.js";
|
|
6
|
+
import type { ImportProfilesResult, ProfilesFile } from "./types.js";
|
|
7
|
+
|
|
8
|
+
export function ensureProfilesImported(
|
|
9
|
+
data: ProfilesFile,
|
|
10
|
+
agentOptions: string | AgentSelectionOptions = AGENTS_DIR,
|
|
11
|
+
): ImportProfilesResult {
|
|
12
|
+
if (data.importedAt) {
|
|
13
|
+
return {
|
|
14
|
+
data,
|
|
15
|
+
imported: false,
|
|
16
|
+
importedCount: 0,
|
|
17
|
+
warnings: [],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const snapshot = captureAgentSnapshots(agentOptions);
|
|
22
|
+
const timestamp = new Date().toISOString();
|
|
23
|
+
const profile = createProfile(resolveUniqueProfileName(INITIAL_PROFILE_NAME, data.profiles), snapshot.agents, { timestamp });
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
data: {
|
|
27
|
+
...appendProfile(data, profile),
|
|
28
|
+
importedAt: timestamp,
|
|
29
|
+
},
|
|
30
|
+
imported: true,
|
|
31
|
+
importedCount: 1,
|
|
32
|
+
warnings: snapshot.warnings,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function loadAndPrepareProfiles(
|
|
37
|
+
storePath = PROFILE_STORE_PATH,
|
|
38
|
+
agentOptions: string | AgentSelectionOptions = AGENTS_DIR,
|
|
39
|
+
): ImportProfilesResult {
|
|
40
|
+
const configLoad = loadMultiProfilesConfig();
|
|
41
|
+
|
|
42
|
+
multiProfilesDebugLogger.log("extension.initialized", {
|
|
43
|
+
configCreated: configLoad.created,
|
|
44
|
+
timestamp: new Date().toISOString(),
|
|
45
|
+
profilesVersion: 2,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const loaded = loadProfilesFile(storePath, agentOptions);
|
|
49
|
+
const prepared = ensureProfilesImported(loaded.data, agentOptions);
|
|
50
|
+
|
|
51
|
+
if (loaded.needsSave || prepared.imported) {
|
|
52
|
+
saveProfilesFile(prepared.data, storePath);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const warnings = [configLoad.warning, loaded.warning, ...prepared.warnings].filter((message): message is string => Boolean(message));
|
|
56
|
+
return {
|
|
57
|
+
...prepared,
|
|
58
|
+
warnings,
|
|
59
|
+
};
|
|
60
|
+
}
|