@miosa/cli 1.0.79 → 1.0.81
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 +38 -0
- package/dist/bin/miosa.js +1 -0
- package/dist/bin/miosa.js.map +1 -1
- package/dist/commands/capabilities.d.ts.map +1 -1
- package/dist/commands/capabilities.js +85 -4
- package/dist/commands/capabilities.js.map +1 -1
- package/dist/commands/completion.d.ts.map +1 -1
- package/dist/commands/completion.js +12 -1
- package/dist/commands/completion.js.map +1 -1
- package/dist/commands/computers.d.ts.map +1 -1
- package/dist/commands/computers.js +44 -0
- package/dist/commands/computers.js.map +1 -1
- package/dist/commands/connectors.d.ts.map +1 -1
- package/dist/commands/connectors.js +509 -1
- package/dist/commands/connectors.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +147 -0
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/devices.d.ts.map +1 -1
- package/dist/commands/devices.js +410 -35
- package/dist/commands/devices.js.map +1 -1
- package/dist/commands/osa.d.ts +3 -0
- package/dist/commands/osa.d.ts.map +1 -0
- package/dist/commands/osa.js +663 -0
- package/dist/commands/osa.js.map +1 -0
- package/dist/osa/build.d.ts +6 -0
- package/dist/osa/build.d.ts.map +1 -0
- package/dist/osa/build.js +23 -0
- package/dist/osa/build.js.map +1 -0
- package/dist/osa/builtin-skills.d.ts +7 -0
- package/dist/osa/builtin-skills.d.ts.map +1 -0
- package/dist/osa/builtin-skills.js +85 -0
- package/dist/osa/builtin-skills.js.map +1 -0
- package/dist/osa/computer.d.ts +10 -0
- package/dist/osa/computer.d.ts.map +1 -0
- package/dist/osa/computer.js +37 -0
- package/dist/osa/computer.js.map +1 -0
- package/dist/osa/deployments.d.ts +44 -0
- package/dist/osa/deployments.d.ts.map +1 -0
- package/dist/osa/deployments.js +140 -0
- package/dist/osa/deployments.js.map +1 -0
- package/dist/osa/discovery.d.ts +8 -0
- package/dist/osa/discovery.d.ts.map +1 -0
- package/dist/osa/discovery.js +668 -0
- package/dist/osa/discovery.js.map +1 -0
- package/dist/osa/doctor.d.ts +9 -0
- package/dist/osa/doctor.d.ts.map +1 -0
- package/dist/osa/doctor.js +41 -0
- package/dist/osa/doctor.js.map +1 -0
- package/dist/osa/eval.d.ts +7 -0
- package/dist/osa/eval.d.ts.map +1 -0
- package/dist/osa/eval.js +108 -0
- package/dist/osa/eval.js.map +1 -0
- package/dist/osa/integrations.d.ts +28 -0
- package/dist/osa/integrations.d.ts.map +1 -0
- package/dist/osa/integrations.js +65 -0
- package/dist/osa/integrations.js.map +1 -0
- package/dist/osa/paths.d.ts +8 -0
- package/dist/osa/paths.d.ts.map +1 -0
- package/dist/osa/paths.js +31 -0
- package/dist/osa/paths.js.map +1 -0
- package/dist/osa/plans.d.ts +9 -0
- package/dist/osa/plans.d.ts.map +1 -0
- package/dist/osa/plans.js +55 -0
- package/dist/osa/plans.js.map +1 -0
- package/dist/osa/projects.d.ts +45 -0
- package/dist/osa/projects.d.ts.map +1 -0
- package/dist/osa/projects.js +92 -0
- package/dist/osa/projects.js.map +1 -0
- package/dist/osa/runtime.d.ts +19 -0
- package/dist/osa/runtime.d.ts.map +1 -0
- package/dist/osa/runtime.js +92 -0
- package/dist/osa/runtime.js.map +1 -0
- package/dist/osa/scaffold.d.ts +11 -0
- package/dist/osa/scaffold.d.ts.map +1 -0
- package/dist/osa/scaffold.js +245 -0
- package/dist/osa/scaffold.js.map +1 -0
- package/dist/osa/skills.d.ts +19 -0
- package/dist/osa/skills.d.ts.map +1 -0
- package/dist/osa/skills.js +55 -0
- package/dist/osa/skills.js.map +1 -0
- package/dist/osa/types.d.ts +157 -0
- package/dist/osa/types.d.ts.map +1 -0
- package/dist/osa/types.js +2 -0
- package/dist/osa/types.js.map +1 -0
- package/dist/osa/yaml.d.ts +7 -0
- package/dist/osa/yaml.d.ts.map +1 -0
- package/dist/osa/yaml.js +59 -0
- package/dist/osa/yaml.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import YAML from "yaml";
|
|
4
|
+
import { artifactRoot, relativePath, resolveTarget, sourceRoot, sourceRootName, } from "./paths.js";
|
|
5
|
+
import { booleanValue, readYamlObject, recordValue, stringArray, stringValue, } from "./yaml.js";
|
|
6
|
+
export function discoverOsaProject(options = {}) {
|
|
7
|
+
const projectRoot = resolveTarget(options.target, options.cwd);
|
|
8
|
+
const root = sourceRoot(projectRoot);
|
|
9
|
+
const rootName = sourceRootName(projectRoot);
|
|
10
|
+
const diagnostics = [];
|
|
11
|
+
if (!fs.existsSync(root)) {
|
|
12
|
+
diagnostics.push({
|
|
13
|
+
severity: "error",
|
|
14
|
+
code: "agent.root.missing",
|
|
15
|
+
message: "No agent/ or legacy osa/ directory found.",
|
|
16
|
+
path: "agent",
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const legacyAgentPath = path.join(root, "agent.yml");
|
|
20
|
+
const agentConfigPath = firstExistingPath([
|
|
21
|
+
path.join(root, "agent.ts"),
|
|
22
|
+
path.join(root, "agent.js"),
|
|
23
|
+
legacyAgentPath,
|
|
24
|
+
]);
|
|
25
|
+
const agentConfig = readAgentMetadata(projectRoot, agentConfigPath, legacyAgentPath, diagnostics);
|
|
26
|
+
const instructionsPath = path.join(root, "instructions.md");
|
|
27
|
+
if (!fs.existsSync(instructionsPath)) {
|
|
28
|
+
diagnostics.push({
|
|
29
|
+
severity: "warning",
|
|
30
|
+
code: "instructions.missing",
|
|
31
|
+
message: `${rootName}/instructions.md is missing.`,
|
|
32
|
+
path: `${rootName}/instructions.md`,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const permissionsPath = path.join(root, "permissions.yml");
|
|
36
|
+
const permissions = fs.existsSync(permissionsPath)
|
|
37
|
+
? readYamlObject(projectRoot, permissionsPath, diagnostics)
|
|
38
|
+
: missing("warning", "permissions.missing", `${rootName}/permissions.yml is missing.`, `${rootName}/permissions.yml`, diagnostics);
|
|
39
|
+
checkPermissions(projectRoot, permissionsPath, permissions, diagnostics);
|
|
40
|
+
const sandboxConfigPath = firstExistingPath([
|
|
41
|
+
path.join(root, "sandbox.ts"),
|
|
42
|
+
path.join(root, "sandbox.js"),
|
|
43
|
+
path.join(root, "sandbox", "sandbox.ts"),
|
|
44
|
+
path.join(root, "sandbox", "sandbox.js"),
|
|
45
|
+
path.join(root, "sandbox.yml"),
|
|
46
|
+
]);
|
|
47
|
+
const manifest = {
|
|
48
|
+
version: 1,
|
|
49
|
+
projectRoot,
|
|
50
|
+
osaRoot: root,
|
|
51
|
+
sourceRoot: rootName,
|
|
52
|
+
agent: {
|
|
53
|
+
name: stringValue(agentConfig["name"]) ?? readPackageName(projectRoot) ?? path.basename(projectRoot),
|
|
54
|
+
...(stringValue(agentConfig["description"]) ? { description: stringValue(agentConfig["description"]) } : {}),
|
|
55
|
+
...(agentModelName(agentConfig["runtimeProfile"]) ? { model: agentModelName(agentConfig["runtimeProfile"]) } : {}),
|
|
56
|
+
...(agentConfigPath ? { config: relativePath(projectRoot, agentConfigPath) } : {}),
|
|
57
|
+
},
|
|
58
|
+
runtimeProfile: runtimeProfileValue(agentConfig["runtimeProfile"]),
|
|
59
|
+
context: {
|
|
60
|
+
...(fs.existsSync(path.join(root, "AGENTS.md")) ? { agentsMd: `${rootName}/AGENTS.md` } : {}),
|
|
61
|
+
instructions: fs.existsSync(instructionsPath) ? [`${rootName}/instructions.md`] : [],
|
|
62
|
+
docs: listFiles(path.join(root, "docs"), projectRoot),
|
|
63
|
+
},
|
|
64
|
+
skills: discoverSkills(projectRoot, root, diagnostics),
|
|
65
|
+
connections: discoverConnections(projectRoot, root, diagnostics),
|
|
66
|
+
channels: discoverChannels(projectRoot, root, diagnostics),
|
|
67
|
+
schedules: discoverSchedules(projectRoot, root, diagnostics),
|
|
68
|
+
computers: discoverComputers(projectRoot, root, diagnostics),
|
|
69
|
+
subagents: discoverSubagents(projectRoot, root, diagnostics),
|
|
70
|
+
hooks: moduleFiles(path.join(root, "hooks"), projectRoot).map((item) => item.path),
|
|
71
|
+
sandbox: {
|
|
72
|
+
...(sandboxConfigPath ? { config: relativePath(projectRoot, sandboxConfigPath) } : {}),
|
|
73
|
+
workspace: listFiles(path.join(root, "sandbox", "workspace"), projectRoot),
|
|
74
|
+
},
|
|
75
|
+
evals: discoverEvals(projectRoot, root),
|
|
76
|
+
diagnostics: {
|
|
77
|
+
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
78
|
+
warnings: diagnostics.filter((item) => item.severity === "warning").length,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
const discovery = { manifest, diagnostics };
|
|
82
|
+
if (options.writeArtifacts ?? true)
|
|
83
|
+
writeArtifacts(projectRoot, discovery);
|
|
84
|
+
return discovery;
|
|
85
|
+
}
|
|
86
|
+
function missing(severity, code, message, filePath, diagnostics) {
|
|
87
|
+
diagnostics.push({ severity, code, message, path: filePath });
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
function checkPermissions(projectRoot, permissionsPath, permissions, diagnostics) {
|
|
91
|
+
const network = recordValue(permissions["network"]);
|
|
92
|
+
if (stringValue(network["default"]) === "allow") {
|
|
93
|
+
diagnostics.push({
|
|
94
|
+
severity: "warning",
|
|
95
|
+
code: "permissions.network.allow_all",
|
|
96
|
+
message: "Network policy defaults to allow.",
|
|
97
|
+
path: fs.existsSync(permissionsPath)
|
|
98
|
+
? relativePath(projectRoot, permissionsPath)
|
|
99
|
+
: "agent/permissions.yml",
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export function discoverSkills(projectRoot, root = sourceRoot(projectRoot), diagnostics = []) {
|
|
104
|
+
const skillsRoot = path.join(root, "skills");
|
|
105
|
+
if (!fs.existsSync(skillsRoot))
|
|
106
|
+
return [];
|
|
107
|
+
const skills = [];
|
|
108
|
+
for (const entry of sortedDirEntries(skillsRoot)) {
|
|
109
|
+
if (entry.name.startsWith("."))
|
|
110
|
+
continue;
|
|
111
|
+
const fullPath = path.join(skillsRoot, entry.name);
|
|
112
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
113
|
+
const name = entry.name.replace(/\.md$/, "");
|
|
114
|
+
const source = fs.readFileSync(fullPath, "utf8");
|
|
115
|
+
const frontmatter = extractFrontmatter(source);
|
|
116
|
+
skills.push({
|
|
117
|
+
name,
|
|
118
|
+
path: relativePath(projectRoot, fullPath),
|
|
119
|
+
source: "project",
|
|
120
|
+
trust: trustValue(frontmatter["trust"]),
|
|
121
|
+
description: stringValue(frontmatter["description"]) ??
|
|
122
|
+
firstContentLine(source) ??
|
|
123
|
+
`Instructions for the ${name} skill.`,
|
|
124
|
+
permissions: flattenPermissions(frontmatter["permissions"]),
|
|
125
|
+
});
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (!entry.isDirectory())
|
|
129
|
+
continue;
|
|
130
|
+
const skillPath = path.join(fullPath, "SKILL.md");
|
|
131
|
+
if (!fs.existsSync(skillPath))
|
|
132
|
+
continue;
|
|
133
|
+
const source = fs.readFileSync(skillPath, "utf8");
|
|
134
|
+
const frontmatter = extractFrontmatter(source);
|
|
135
|
+
const name = stringValue(frontmatter["name"]) ?? entry.name;
|
|
136
|
+
const description = stringValue(frontmatter["description"]);
|
|
137
|
+
if (!description) {
|
|
138
|
+
diagnostics.push({
|
|
139
|
+
severity: "warning",
|
|
140
|
+
code: "skill.description.missing",
|
|
141
|
+
message: `Packaged skill ${name} is missing description frontmatter.`,
|
|
142
|
+
path: relativePath(projectRoot, skillPath),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
skills.push({
|
|
146
|
+
name,
|
|
147
|
+
path: relativePath(projectRoot, skillPath),
|
|
148
|
+
source: "project",
|
|
149
|
+
trust: trustValue(frontmatter["trust"]),
|
|
150
|
+
description: description ?? `Instructions for the ${name} skill.`,
|
|
151
|
+
permissions: flattenPermissions(frontmatter["permissions"]),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return skills;
|
|
155
|
+
}
|
|
156
|
+
function discoverConnections(projectRoot, root, diagnostics) {
|
|
157
|
+
const dir = path.join(root, "connections");
|
|
158
|
+
if (!fs.existsSync(dir))
|
|
159
|
+
return [];
|
|
160
|
+
const connections = [];
|
|
161
|
+
for (const filePath of yamlFiles(dir)) {
|
|
162
|
+
const config = readYamlObject(projectRoot, filePath, diagnostics);
|
|
163
|
+
const url = stringValue(config["url"]);
|
|
164
|
+
const type = stringValue(config["type"]) ?? "unknown";
|
|
165
|
+
const hasAuth = Object.keys(recordValue(config["auth"])).length > 0;
|
|
166
|
+
warnUnauthedRemote(projectRoot, filePath, url, hasAuth, diagnostics);
|
|
167
|
+
connections.push({
|
|
168
|
+
name: path.basename(filePath).replace(/\.(ya?ml)$/, ""),
|
|
169
|
+
path: relativePath(projectRoot, filePath),
|
|
170
|
+
type,
|
|
171
|
+
description: stringValue(config["description"]) ?? "",
|
|
172
|
+
hasAuth,
|
|
173
|
+
...(url ? { url } : {}),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
for (const item of moduleFiles(dir, projectRoot)) {
|
|
177
|
+
const source = fs.readFileSync(path.join(projectRoot, item.path), "utf8");
|
|
178
|
+
connections.push({
|
|
179
|
+
name: item.name,
|
|
180
|
+
path: item.path,
|
|
181
|
+
type: readStringProperty(source, "type") ?? "module",
|
|
182
|
+
description: readStringProperty(source, "description") ?? "",
|
|
183
|
+
hasAuth: /\bauth\s*:/.test(source),
|
|
184
|
+
...(readStringProperty(source, "baseUrl") ? { url: readStringProperty(source, "baseUrl") } : {}),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return connections;
|
|
188
|
+
}
|
|
189
|
+
function discoverChannels(projectRoot, root, diagnostics) {
|
|
190
|
+
const dir = path.join(root, "channels");
|
|
191
|
+
if (!fs.existsSync(dir))
|
|
192
|
+
return [];
|
|
193
|
+
const channels = [];
|
|
194
|
+
for (const filePath of yamlFiles(dir)) {
|
|
195
|
+
const config = readYamlObject(projectRoot, filePath, diagnostics);
|
|
196
|
+
channels.push({
|
|
197
|
+
name: path.basename(filePath).replace(/\.(ya?ml)$/, ""),
|
|
198
|
+
path: relativePath(projectRoot, filePath),
|
|
199
|
+
type: stringValue(config["type"]) ?? "unknown",
|
|
200
|
+
description: stringValue(config["description"]) ?? "",
|
|
201
|
+
...(stringValue(config["entrypoint"]) ? { entrypoint: stringValue(config["entrypoint"]) } : {}),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
for (const item of moduleFiles(dir, projectRoot)) {
|
|
205
|
+
const source = fs.readFileSync(path.join(projectRoot, item.path), "utf8");
|
|
206
|
+
channels.push({
|
|
207
|
+
name: item.name,
|
|
208
|
+
path: item.path,
|
|
209
|
+
type: readStringProperty(source, "type") ?? "module",
|
|
210
|
+
description: readStringProperty(source, "description") ?? "",
|
|
211
|
+
...(readStringProperty(source, "entrypoint") ? { entrypoint: readStringProperty(source, "entrypoint") } : {}),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return channels;
|
|
215
|
+
}
|
|
216
|
+
function discoverSchedules(projectRoot, root, diagnostics) {
|
|
217
|
+
const dir = path.join(root, "schedules");
|
|
218
|
+
if (!fs.existsSync(dir))
|
|
219
|
+
return [];
|
|
220
|
+
const schedules = [];
|
|
221
|
+
for (const filePath of yamlFiles(dir)) {
|
|
222
|
+
const config = readYamlObject(projectRoot, filePath, diagnostics);
|
|
223
|
+
schedules.push({
|
|
224
|
+
name: stringValue(config["name"]) ?? path.basename(filePath).replace(/\.(ya?ml)$/, ""),
|
|
225
|
+
path: relativePath(projectRoot, filePath),
|
|
226
|
+
...(stringValue(config["cron"]) ? { cron: stringValue(config["cron"]) } : {}),
|
|
227
|
+
...(stringValue(config["prompt"]) ? { prompt: stringValue(config["prompt"]) } : {}),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
for (const filePath of markdownFiles(dir)) {
|
|
231
|
+
const source = fs.readFileSync(filePath, "utf8");
|
|
232
|
+
const frontmatter = extractFrontmatter(source);
|
|
233
|
+
schedules.push({
|
|
234
|
+
name: path.basename(filePath).replace(/\.md$/, ""),
|
|
235
|
+
path: relativePath(projectRoot, filePath),
|
|
236
|
+
...(stringValue(frontmatter["cron"]) ? { cron: stringValue(frontmatter["cron"]) } : {}),
|
|
237
|
+
prompt: stripFrontmatter(source).trim(),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
for (const item of moduleFiles(dir, projectRoot)) {
|
|
241
|
+
const source = fs.readFileSync(path.join(projectRoot, item.path), "utf8");
|
|
242
|
+
schedules.push({
|
|
243
|
+
name: item.name,
|
|
244
|
+
path: item.path,
|
|
245
|
+
...(readStringProperty(source, "cron") ? { cron: readStringProperty(source, "cron") } : {}),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return schedules;
|
|
249
|
+
}
|
|
250
|
+
function discoverComputers(projectRoot, root, diagnostics) {
|
|
251
|
+
const dir = path.join(root, "computers");
|
|
252
|
+
if (!fs.existsSync(dir))
|
|
253
|
+
return [];
|
|
254
|
+
const computers = [];
|
|
255
|
+
for (const filePath of yamlFiles(dir)) {
|
|
256
|
+
const config = readYamlObject(projectRoot, filePath, diagnostics);
|
|
257
|
+
const capabilities = Object.entries(recordValue(config["capabilities"]))
|
|
258
|
+
.filter(([, value]) => value === true)
|
|
259
|
+
.map(([key]) => key);
|
|
260
|
+
computers.push({
|
|
261
|
+
name: path.basename(filePath).replace(/\.(ya?ml)$/, ""),
|
|
262
|
+
path: relativePath(projectRoot, filePath),
|
|
263
|
+
enabled: booleanValue(config["enabled"]),
|
|
264
|
+
...(stringValue(config["kind"]) ? { kind: stringValue(config["kind"]) } : {}),
|
|
265
|
+
...(stringValue(config["size"]) ? { size: stringValue(config["size"]) } : {}),
|
|
266
|
+
capabilities,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
return computers;
|
|
270
|
+
}
|
|
271
|
+
function discoverSubagents(projectRoot, root, diagnostics) {
|
|
272
|
+
const dir = path.join(root, "subagents");
|
|
273
|
+
if (!fs.existsSync(dir))
|
|
274
|
+
return [];
|
|
275
|
+
const subagents = [];
|
|
276
|
+
for (const entry of sortedDirEntries(dir)) {
|
|
277
|
+
if (!entry.isDirectory() || entry.name.startsWith("."))
|
|
278
|
+
continue;
|
|
279
|
+
const subagentRoot = path.join(dir, entry.name);
|
|
280
|
+
const legacyAgentPath = path.join(subagentRoot, "agent.yml");
|
|
281
|
+
const agentPath = firstExistingPath([
|
|
282
|
+
path.join(subagentRoot, "agent.ts"),
|
|
283
|
+
path.join(subagentRoot, "agent.js"),
|
|
284
|
+
legacyAgentPath,
|
|
285
|
+
]);
|
|
286
|
+
const config = readAgentMetadata(projectRoot, agentPath, legacyAgentPath, diagnostics);
|
|
287
|
+
const description = stringValue(config["description"]);
|
|
288
|
+
if (!description) {
|
|
289
|
+
diagnostics.push({
|
|
290
|
+
severity: "error",
|
|
291
|
+
code: "subagent.description.missing",
|
|
292
|
+
message: `Subagent ${entry.name} is missing a description.`,
|
|
293
|
+
path: agentPath ? relativePath(projectRoot, agentPath) : relativePath(projectRoot, subagentRoot),
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
const instructionsPath = path.join(subagentRoot, "instructions.md");
|
|
297
|
+
subagents.push({
|
|
298
|
+
name: stringValue(config["name"]) ?? entry.name,
|
|
299
|
+
path: relativePath(projectRoot, subagentRoot),
|
|
300
|
+
...(description ? { description } : {}),
|
|
301
|
+
...(stringValue(config["model"]) ? { model: stringValue(config["model"]) } : {}),
|
|
302
|
+
...(agentPath ? { config: relativePath(projectRoot, agentPath) } : {}),
|
|
303
|
+
...(fs.existsSync(instructionsPath) ? { instructions: relativePath(projectRoot, instructionsPath) } : {}),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
return subagents;
|
|
307
|
+
}
|
|
308
|
+
function discoverEvals(projectRoot, root) {
|
|
309
|
+
return Array.from(new Set([...listFiles(path.join(projectRoot, "evals"), projectRoot), ...listFiles(path.join(root, "evals"), projectRoot)]))
|
|
310
|
+
.filter((filePath) => /\.(ya?ml|json|md|ts)$/.test(filePath))
|
|
311
|
+
.map((filePath) => ({
|
|
312
|
+
name: path.basename(filePath).replace(/\.(ya?ml|json|md|ts)$/, ""),
|
|
313
|
+
path: filePath,
|
|
314
|
+
}));
|
|
315
|
+
}
|
|
316
|
+
function listFiles(dir, projectRoot) {
|
|
317
|
+
if (!fs.existsSync(dir))
|
|
318
|
+
return [];
|
|
319
|
+
const result = [];
|
|
320
|
+
for (const entry of sortedDirEntries(dir)) {
|
|
321
|
+
const fullPath = path.join(dir, entry.name);
|
|
322
|
+
if (entry.isDirectory()) {
|
|
323
|
+
result.push(...listFiles(fullPath, projectRoot));
|
|
324
|
+
}
|
|
325
|
+
else if (entry.isFile() && entry.name !== ".gitkeep") {
|
|
326
|
+
result.push(relativePath(projectRoot, fullPath));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
function yamlFiles(dir) {
|
|
332
|
+
if (!fs.existsSync(dir))
|
|
333
|
+
return [];
|
|
334
|
+
return sortedDirEntries(dir)
|
|
335
|
+
.filter((entry) => entry.isFile() && /\.(ya?ml)$/.test(entry.name))
|
|
336
|
+
.map((entry) => path.join(dir, entry.name));
|
|
337
|
+
}
|
|
338
|
+
function markdownFiles(dir) {
|
|
339
|
+
if (!fs.existsSync(dir))
|
|
340
|
+
return [];
|
|
341
|
+
return sortedDirEntries(dir)
|
|
342
|
+
.filter((entry) => entry.isFile() && /\.md$/.test(entry.name))
|
|
343
|
+
.map((entry) => path.join(dir, entry.name));
|
|
344
|
+
}
|
|
345
|
+
function moduleFiles(dir, projectRoot) {
|
|
346
|
+
if (!fs.existsSync(dir))
|
|
347
|
+
return [];
|
|
348
|
+
return sortedDirEntries(dir)
|
|
349
|
+
.filter((entry) => entry.isFile() && /\.(mjs|js|ts)$/.test(entry.name))
|
|
350
|
+
.map((entry) => {
|
|
351
|
+
const fullPath = path.join(dir, entry.name);
|
|
352
|
+
return {
|
|
353
|
+
name: entry.name.replace(/\.(mjs|js|ts)$/, ""),
|
|
354
|
+
path: relativePath(projectRoot, fullPath),
|
|
355
|
+
};
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
function sortedDirEntries(dir) {
|
|
359
|
+
return fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
360
|
+
}
|
|
361
|
+
function extractFrontmatter(source) {
|
|
362
|
+
if (!source.startsWith("---"))
|
|
363
|
+
return {};
|
|
364
|
+
const end = source.indexOf("\n---", 3);
|
|
365
|
+
if (end === -1)
|
|
366
|
+
return {};
|
|
367
|
+
try {
|
|
368
|
+
const value = YAML.parse(source.slice(3, end));
|
|
369
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
370
|
+
? value
|
|
371
|
+
: {};
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
return {};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function stripFrontmatter(source) {
|
|
378
|
+
if (!source.startsWith("---"))
|
|
379
|
+
return source;
|
|
380
|
+
const end = source.indexOf("\n---", 3);
|
|
381
|
+
if (end === -1)
|
|
382
|
+
return source;
|
|
383
|
+
return source.slice(end + 4).replace(/^\r?\n/, "");
|
|
384
|
+
}
|
|
385
|
+
function firstContentLine(source) {
|
|
386
|
+
return stripFrontmatter(source)
|
|
387
|
+
.split(/\r?\n/)
|
|
388
|
+
.map((line) => line.trim().replace(/^[#>*-]\s*/, ""))
|
|
389
|
+
.find((line) => line.length > 0 && line !== "---");
|
|
390
|
+
}
|
|
391
|
+
function trustValue(value) {
|
|
392
|
+
return value === "builtin" ||
|
|
393
|
+
value === "verified" ||
|
|
394
|
+
value === "workspace" ||
|
|
395
|
+
value === "third_party"
|
|
396
|
+
? value
|
|
397
|
+
: "local";
|
|
398
|
+
}
|
|
399
|
+
function flattenPermissions(value) {
|
|
400
|
+
if (!value)
|
|
401
|
+
return [];
|
|
402
|
+
if (Array.isArray(value))
|
|
403
|
+
return stringArray(value);
|
|
404
|
+
const record = recordValue(value);
|
|
405
|
+
return Object.entries(record).flatMap(([key, raw]) => stringArray(raw).map((item) => `${key}:${item}`));
|
|
406
|
+
}
|
|
407
|
+
function warnUnauthedRemote(projectRoot, filePath, url, hasAuth, diagnostics) {
|
|
408
|
+
if (url && !isLocalUrl(url) && !hasAuth) {
|
|
409
|
+
diagnostics.push({
|
|
410
|
+
severity: "warning",
|
|
411
|
+
code: "connection.auth.missing",
|
|
412
|
+
message: "Non-local connection has no auth declaration.",
|
|
413
|
+
path: relativePath(projectRoot, filePath),
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function isLocalUrl(url) {
|
|
418
|
+
return /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(?::|\/|$)/i.test(url);
|
|
419
|
+
}
|
|
420
|
+
function writeArtifacts(projectRoot, discovery) {
|
|
421
|
+
const dir = artifactRoot(projectRoot);
|
|
422
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
423
|
+
fs.writeFileSync(path.join(dir, "osa-manifest.json"), `${JSON.stringify(discovery.manifest, null, 2)}\n`, "utf8");
|
|
424
|
+
fs.writeFileSync(path.join(dir, "osa-diagnostics.json"), `${JSON.stringify({ version: 1, items: discovery.diagnostics }, null, 2)}\n`, "utf8");
|
|
425
|
+
}
|
|
426
|
+
function firstExistingPath(paths) {
|
|
427
|
+
return paths.find((filePath) => fs.existsSync(filePath));
|
|
428
|
+
}
|
|
429
|
+
function readAgentMetadata(projectRoot, agentPath, legacyAgentPath, diagnostics) {
|
|
430
|
+
if (fs.existsSync(legacyAgentPath)) {
|
|
431
|
+
const config = readYamlObject(projectRoot, legacyAgentPath, diagnostics);
|
|
432
|
+
return {
|
|
433
|
+
...config,
|
|
434
|
+
runtimeProfile: legacyRuntimeProfile(config),
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
if (!agentPath || !fs.existsSync(agentPath))
|
|
438
|
+
return {};
|
|
439
|
+
const source = fs.readFileSync(agentPath, "utf8");
|
|
440
|
+
const runtimeProfile = readRuntimeProfile(source);
|
|
441
|
+
return {
|
|
442
|
+
...(readStringProperty(source, "name") ? { name: readStringProperty(source, "name") } : {}),
|
|
443
|
+
...(readStringProperty(source, "description") ? { description: readStringProperty(source, "description") } : {}),
|
|
444
|
+
runtimeProfile,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
function readStringProperty(source, property) {
|
|
448
|
+
const pattern = new RegExp(`${property}:\\s*["']([^"']+)["']`);
|
|
449
|
+
return pattern.exec(source)?.[1];
|
|
450
|
+
}
|
|
451
|
+
function readRuntimeProfile(source) {
|
|
452
|
+
const model = readModelConfig(source);
|
|
453
|
+
const harness = readObjectConfig(source, "harness");
|
|
454
|
+
const runtime = readObjectConfig(source, "runtime");
|
|
455
|
+
const sandbox = readObjectConfig(source, "sandbox");
|
|
456
|
+
const policy = readObjectConfig(source, "policy");
|
|
457
|
+
const capabilities = readObjectConfig(source, "capabilities");
|
|
458
|
+
const provider = readStringProperty(source, "provider") ?? stringField(harness, "engine");
|
|
459
|
+
return compactProfile({
|
|
460
|
+
...(model !== undefined ? { model } : {}),
|
|
461
|
+
...(provider ? { provider } : {}),
|
|
462
|
+
...(harness ? { harness } : {}),
|
|
463
|
+
...(runtime ? { runtime } : {}),
|
|
464
|
+
...(sandbox ? { sandbox } : {}),
|
|
465
|
+
...(policy ? { policy } : {}),
|
|
466
|
+
...(capabilities ? { capabilities } : {}),
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
function legacyRuntimeProfile(config) {
|
|
470
|
+
const profile = {};
|
|
471
|
+
const model = stringValue(config["model"]);
|
|
472
|
+
const provider = stringValue(config["provider"]);
|
|
473
|
+
const runtime = recordValue(config["runtime"]);
|
|
474
|
+
const sandbox = recordValue(config["sandbox"]);
|
|
475
|
+
if (model)
|
|
476
|
+
profile.model = model;
|
|
477
|
+
if (provider)
|
|
478
|
+
profile.provider = provider;
|
|
479
|
+
if (Object.keys(runtime).length > 0)
|
|
480
|
+
profile.runtime = runtimeProfileRecord(runtime);
|
|
481
|
+
if (Object.keys(sandbox).length > 0)
|
|
482
|
+
profile.sandbox = runtimeProfileRecord(sandbox);
|
|
483
|
+
return profile;
|
|
484
|
+
}
|
|
485
|
+
function readModelConfig(source) {
|
|
486
|
+
const block = readObjectBlock(source, "model");
|
|
487
|
+
if (!block)
|
|
488
|
+
return readStringProperty(source, "model");
|
|
489
|
+
return compactRecord({
|
|
490
|
+
...(readStringInBlock(block, "primary") ? { primary: readStringInBlock(block, "primary") } : {}),
|
|
491
|
+
...(readStringInBlock(block, "id") ? { id: readStringInBlock(block, "id") } : {}),
|
|
492
|
+
...(readStringInBlock(block, "default") ? { default: readStringInBlock(block, "default") } : {}),
|
|
493
|
+
...(readStringArrayInBlock(block, "fallback") ? { fallback: readStringArrayInBlock(block, "fallback") } : {}),
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
function readObjectConfig(source, property) {
|
|
497
|
+
const block = readObjectBlock(source, property);
|
|
498
|
+
if (!block)
|
|
499
|
+
return undefined;
|
|
500
|
+
const record = {};
|
|
501
|
+
for (const key of [
|
|
502
|
+
"engine",
|
|
503
|
+
"mode",
|
|
504
|
+
"target",
|
|
505
|
+
"durability",
|
|
506
|
+
"isolation",
|
|
507
|
+
"backend",
|
|
508
|
+
"network",
|
|
509
|
+
"profile",
|
|
510
|
+
]) {
|
|
511
|
+
const value = readStringInBlock(block, key);
|
|
512
|
+
if (value)
|
|
513
|
+
record[key] = value;
|
|
514
|
+
}
|
|
515
|
+
for (const key of ["durable", "checkpointing", "streaming", "codeEditing", "shell", "browser", "github"]) {
|
|
516
|
+
const value = readBooleanInBlock(block, key);
|
|
517
|
+
if (value !== undefined)
|
|
518
|
+
record[key] = value;
|
|
519
|
+
}
|
|
520
|
+
for (const key of ["allowed", "approvals", "required"]) {
|
|
521
|
+
const value = readStringArrayInBlock(block, key);
|
|
522
|
+
if (value)
|
|
523
|
+
record[key] = value;
|
|
524
|
+
}
|
|
525
|
+
const resourcesBlock = readObjectBlock(block, "resources");
|
|
526
|
+
if (resourcesBlock) {
|
|
527
|
+
const resources = {};
|
|
528
|
+
for (const key of ["cpu", "memoryGb", "diskGb", "gpu"]) {
|
|
529
|
+
const number = readNumberInBlock(resourcesBlock, key);
|
|
530
|
+
const string = readStringInBlock(resourcesBlock, key);
|
|
531
|
+
if (number !== undefined)
|
|
532
|
+
resources[key] = number;
|
|
533
|
+
else if (string)
|
|
534
|
+
resources[key] = string;
|
|
535
|
+
}
|
|
536
|
+
if (Object.keys(resources).length > 0)
|
|
537
|
+
record["resources"] = resources;
|
|
538
|
+
}
|
|
539
|
+
return Object.keys(record).length > 0 ? record : undefined;
|
|
540
|
+
}
|
|
541
|
+
function readObjectBlock(source, property) {
|
|
542
|
+
const pattern = new RegExp(`${property}:\\s*\\{`);
|
|
543
|
+
const match = pattern.exec(source);
|
|
544
|
+
if (!match)
|
|
545
|
+
return undefined;
|
|
546
|
+
const open = source.indexOf("{", match.index);
|
|
547
|
+
if (open === -1)
|
|
548
|
+
return undefined;
|
|
549
|
+
let depth = 0;
|
|
550
|
+
let quote;
|
|
551
|
+
for (let index = open; index < source.length; index += 1) {
|
|
552
|
+
const char = source[index];
|
|
553
|
+
const previous = source[index - 1];
|
|
554
|
+
if (quote) {
|
|
555
|
+
if (char === quote && previous !== "\\")
|
|
556
|
+
quote = undefined;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
560
|
+
quote = char;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (char === "{")
|
|
564
|
+
depth += 1;
|
|
565
|
+
if (char === "}") {
|
|
566
|
+
depth -= 1;
|
|
567
|
+
if (depth === 0)
|
|
568
|
+
return source.slice(open + 1, index);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return undefined;
|
|
572
|
+
}
|
|
573
|
+
function readStringInBlock(block, property) {
|
|
574
|
+
return readStringProperty(block, property);
|
|
575
|
+
}
|
|
576
|
+
function readBooleanInBlock(block, property) {
|
|
577
|
+
const pattern = new RegExp(`${property}:\\s*(true|false)\\b`);
|
|
578
|
+
const value = pattern.exec(block)?.[1];
|
|
579
|
+
return value === "true" ? true : value === "false" ? false : undefined;
|
|
580
|
+
}
|
|
581
|
+
function readNumberInBlock(block, property) {
|
|
582
|
+
const pattern = new RegExp(`${property}:\\s*(\\d+(?:\\.\\d+)?)\\b`);
|
|
583
|
+
const raw = pattern.exec(block)?.[1];
|
|
584
|
+
return raw ? Number(raw) : undefined;
|
|
585
|
+
}
|
|
586
|
+
function readStringArrayInBlock(block, property) {
|
|
587
|
+
const pattern = new RegExp(`${property}:\\s*\\[([^\\]]*)\\]`);
|
|
588
|
+
const body = pattern.exec(block)?.[1];
|
|
589
|
+
if (!body)
|
|
590
|
+
return undefined;
|
|
591
|
+
const quotedValue = new RegExp("[\"']([^\"']+)[\"']", "g");
|
|
592
|
+
const values = Array.from(body.matchAll(quotedValue))
|
|
593
|
+
.map((match) => match[1])
|
|
594
|
+
.filter((value) => typeof value === "string");
|
|
595
|
+
return values.length > 0 ? values : undefined;
|
|
596
|
+
}
|
|
597
|
+
function runtimeProfileValue(value) {
|
|
598
|
+
return isRuntimeProfile(value) ? value : {};
|
|
599
|
+
}
|
|
600
|
+
function runtimeProfileRecord(value) {
|
|
601
|
+
const record = {};
|
|
602
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
603
|
+
const next = runtimeProfileEntry(raw);
|
|
604
|
+
if (next !== undefined)
|
|
605
|
+
record[key] = next;
|
|
606
|
+
}
|
|
607
|
+
return record;
|
|
608
|
+
}
|
|
609
|
+
function runtimeProfileEntry(value) {
|
|
610
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
611
|
+
return value;
|
|
612
|
+
}
|
|
613
|
+
if (Array.isArray(value)) {
|
|
614
|
+
const scalars = value.filter((item) => typeof item === "string" || typeof item === "number" || typeof item === "boolean" || item === null);
|
|
615
|
+
return scalars.length === value.length ? scalars : undefined;
|
|
616
|
+
}
|
|
617
|
+
if (value && typeof value === "object") {
|
|
618
|
+
return runtimeProfileRecord(value);
|
|
619
|
+
}
|
|
620
|
+
return undefined;
|
|
621
|
+
}
|
|
622
|
+
function isRuntimeProfile(value) {
|
|
623
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
624
|
+
}
|
|
625
|
+
function compactProfile(profile) {
|
|
626
|
+
return Object.fromEntries(Object.entries(profile).filter(([, value]) => {
|
|
627
|
+
if (value === undefined)
|
|
628
|
+
return false;
|
|
629
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
630
|
+
return Object.keys(value).length > 0;
|
|
631
|
+
}
|
|
632
|
+
return true;
|
|
633
|
+
}));
|
|
634
|
+
}
|
|
635
|
+
function compactRecord(record) {
|
|
636
|
+
const compact = Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
637
|
+
return Object.keys(compact).length > 0 ? compact : undefined;
|
|
638
|
+
}
|
|
639
|
+
function stringField(record, key) {
|
|
640
|
+
const value = record?.[key];
|
|
641
|
+
return typeof value === "string" ? value : undefined;
|
|
642
|
+
}
|
|
643
|
+
function agentModelName(value) {
|
|
644
|
+
const profile = runtimeProfileValue(value);
|
|
645
|
+
if (typeof profile.model === "string")
|
|
646
|
+
return profile.model;
|
|
647
|
+
if (profile.model && typeof profile.model === "object") {
|
|
648
|
+
for (const key of ["primary", "id", "default"]) {
|
|
649
|
+
const candidate = profile.model[key];
|
|
650
|
+
if (typeof candidate === "string")
|
|
651
|
+
return candidate;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return undefined;
|
|
655
|
+
}
|
|
656
|
+
function readPackageName(projectRoot) {
|
|
657
|
+
const packagePath = path.join(projectRoot, "package.json");
|
|
658
|
+
if (!fs.existsSync(packagePath))
|
|
659
|
+
return undefined;
|
|
660
|
+
try {
|
|
661
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, "utf8"));
|
|
662
|
+
return typeof pkg.name === "string" && pkg.name.trim().length > 0 ? pkg.name : undefined;
|
|
663
|
+
}
|
|
664
|
+
catch {
|
|
665
|
+
return undefined;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
//# sourceMappingURL=discovery.js.map
|