syntaur 0.16.2 → 0.16.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/dist/dashboard/server.js +178 -70
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +178 -70
- package/dist/index.js.map +1 -1
- package/dist/launch/index.js +97 -55
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
package/dist/launch/index.js
CHANGED
|
@@ -1715,6 +1715,10 @@ var init_overviewCopy = __esm({
|
|
|
1715
1715
|
// src/dashboard/api.ts
|
|
1716
1716
|
import { readdir as readdir6, readFile as readFile8, writeFile as writeFile3 } from "fs/promises";
|
|
1717
1717
|
import { resolve as resolve11, dirname as dirname2, basename } from "path";
|
|
1718
|
+
function accumulatePhase(traces, label, ms) {
|
|
1719
|
+
if (!traces) return;
|
|
1720
|
+
traces.subPhases.set(label, (traces.subPhases.get(label) ?? 0) + ms);
|
|
1721
|
+
}
|
|
1718
1722
|
async function listStandaloneRecords(assignmentsDir) {
|
|
1719
1723
|
if (!assignmentsDir) return [];
|
|
1720
1724
|
if (!await fileExists(assignmentsDir)) return [];
|
|
@@ -2169,7 +2173,7 @@ async function buildStandaloneAssignmentDetail(resolved) {
|
|
|
2169
2173
|
};
|
|
2170
2174
|
return detail;
|
|
2171
2175
|
}
|
|
2172
|
-
async function listProjectRecords(projectsDir) {
|
|
2176
|
+
async function listProjectRecords(projectsDir, traces) {
|
|
2173
2177
|
if (!await fileExists(projectsDir)) {
|
|
2174
2178
|
return [];
|
|
2175
2179
|
}
|
|
@@ -2179,61 +2183,75 @@ async function listProjectRecords(projectsDir) {
|
|
|
2179
2183
|
}
|
|
2180
2184
|
const entries = await readdir6(projectsDir, { withFileTypes: true });
|
|
2181
2185
|
const projectDirs = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
|
|
2182
|
-
const
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
}
|
|
2189
|
-
const projectContent = await readFile8(projectMdPath, "utf-8");
|
|
2190
|
-
const project = parseProject(projectContent);
|
|
2191
|
-
const assignments = await listAssignmentRecords(projectPath);
|
|
2192
|
-
const rollup = await buildProjectRollup(projectPath, project, assignments);
|
|
2193
|
-
const updated = getProjectActivityTimestamp(project.updated, assignments);
|
|
2194
|
-
records.push({
|
|
2195
|
-
projectPath,
|
|
2196
|
-
project,
|
|
2197
|
-
assignments,
|
|
2198
|
-
dependencyGraph: await loadDependencyGraph(projectPath, assignments),
|
|
2199
|
-
summary: {
|
|
2200
|
-
slug: project.slug || entry.name,
|
|
2201
|
-
title: project.title,
|
|
2202
|
-
status: rollup.status,
|
|
2203
|
-
statusOverride: project.statusOverride,
|
|
2204
|
-
archived: project.archived,
|
|
2205
|
-
archivedAt: project.archivedAt,
|
|
2206
|
-
archivedReason: project.archivedReason,
|
|
2207
|
-
created: project.created,
|
|
2208
|
-
updated,
|
|
2209
|
-
tags: project.tags,
|
|
2210
|
-
progress: rollup.progress,
|
|
2211
|
-
needsAttention: rollup.needsAttention,
|
|
2212
|
-
workspace: project.workspace
|
|
2186
|
+
const maybeRecords = await Promise.all(
|
|
2187
|
+
projectDirs.map(async (entry) => {
|
|
2188
|
+
const projectPath = resolve11(projectsDir, entry.name);
|
|
2189
|
+
const projectMdPath = resolve11(projectPath, "project.md");
|
|
2190
|
+
if (!await fileExists(projectMdPath)) {
|
|
2191
|
+
return null;
|
|
2213
2192
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2193
|
+
const t0 = traces ? performance.now() : 0;
|
|
2194
|
+
const projectContent = await readFile8(projectMdPath, "utf-8");
|
|
2195
|
+
const project = parseProject(projectContent);
|
|
2196
|
+
if (traces) accumulatePhase(traces, "parse-project-md", performance.now() - t0);
|
|
2197
|
+
const t1 = traces ? performance.now() : 0;
|
|
2198
|
+
const assignments = await listAssignmentRecords(projectPath, traces);
|
|
2199
|
+
if (traces) accumulatePhase(traces, "list-assignments", performance.now() - t1);
|
|
2200
|
+
const t2 = traces ? performance.now() : 0;
|
|
2201
|
+
const rollup = await buildProjectRollup(projectPath, project, assignments, traces);
|
|
2202
|
+
if (traces) accumulatePhase(traces, "build-rollup", performance.now() - t2);
|
|
2203
|
+
const updated = getProjectActivityTimestamp(project.updated, assignments);
|
|
2204
|
+
const t3 = traces ? performance.now() : 0;
|
|
2205
|
+
const dependencyGraph = await loadDependencyGraph(projectPath, assignments);
|
|
2206
|
+
if (traces) accumulatePhase(traces, "load-dep-graph", performance.now() - t3);
|
|
2207
|
+
return {
|
|
2208
|
+
projectPath,
|
|
2209
|
+
project,
|
|
2210
|
+
assignments,
|
|
2211
|
+
dependencyGraph,
|
|
2212
|
+
summary: {
|
|
2213
|
+
slug: project.slug || entry.name,
|
|
2214
|
+
title: project.title,
|
|
2215
|
+
status: rollup.status,
|
|
2216
|
+
statusOverride: project.statusOverride,
|
|
2217
|
+
archived: project.archived,
|
|
2218
|
+
archivedAt: project.archivedAt,
|
|
2219
|
+
archivedReason: project.archivedReason,
|
|
2220
|
+
created: project.created,
|
|
2221
|
+
updated,
|
|
2222
|
+
tags: project.tags,
|
|
2223
|
+
progress: rollup.progress,
|
|
2224
|
+
needsAttention: rollup.needsAttention,
|
|
2225
|
+
workspace: project.workspace
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
})
|
|
2229
|
+
);
|
|
2230
|
+
const records = maybeRecords.filter((r) => r !== null);
|
|
2216
2231
|
records.sort((left, right) => compareTimestamps(right.summary.updated, left.summary.updated));
|
|
2217
2232
|
return records;
|
|
2218
2233
|
}
|
|
2219
|
-
async function listAssignmentRecords(projectPath) {
|
|
2234
|
+
async function listAssignmentRecords(projectPath, traces) {
|
|
2220
2235
|
const assignmentsDir = resolve11(projectPath, "assignments");
|
|
2221
2236
|
if (!await fileExists(assignmentsDir)) {
|
|
2222
2237
|
return [];
|
|
2223
2238
|
}
|
|
2224
2239
|
const entries = await readdir6(assignmentsDir, { withFileTypes: true });
|
|
2225
|
-
const
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2240
|
+
const dirEntries = entries.filter((entry) => entry.isDirectory());
|
|
2241
|
+
const maybeRecords = await Promise.all(
|
|
2242
|
+
dirEntries.map(async (entry) => {
|
|
2243
|
+
const assignmentMd = resolve11(assignmentsDir, entry.name, "assignment.md");
|
|
2244
|
+
if (!await fileExists(assignmentMd)) {
|
|
2245
|
+
return null;
|
|
2246
|
+
}
|
|
2247
|
+
const t0 = traces ? performance.now() : 0;
|
|
2248
|
+
const content = await readFile8(assignmentMd, "utf-8");
|
|
2249
|
+
const parsed = parseAssignmentFull(content);
|
|
2250
|
+
if (traces) accumulatePhase(traces, "read-assignment-md", performance.now() - t0);
|
|
2251
|
+
return parsed;
|
|
2252
|
+
})
|
|
2253
|
+
);
|
|
2254
|
+
const records = maybeRecords.filter((r) => r !== null);
|
|
2237
2255
|
records.sort((left, right) => compareTimestamps(right.updated, left.updated));
|
|
2238
2256
|
return records;
|
|
2239
2257
|
}
|
|
@@ -2249,13 +2267,20 @@ async function loadDependencyGraph(projectPath, assignments) {
|
|
|
2249
2267
|
}
|
|
2250
2268
|
return buildDependencyGraph(assignments);
|
|
2251
2269
|
}
|
|
2252
|
-
async function buildProjectRollup(projectPath, project, assignments) {
|
|
2270
|
+
async function buildProjectRollup(projectPath, project, assignments, traces) {
|
|
2253
2271
|
const progress = { total: assignments.length };
|
|
2272
|
+
const perAssignment = await Promise.all(
|
|
2273
|
+
assignments.map(async (assignment) => {
|
|
2274
|
+
const t0 = traces ? performance.now() : 0;
|
|
2275
|
+
const openQuestions2 = await countOpenQuestions(projectPath, assignment.slug);
|
|
2276
|
+
if (traces) accumulatePhase(traces, "count-open-questions", performance.now() - t0);
|
|
2277
|
+
return { status: assignment.status, openQuestions: openQuestions2 };
|
|
2278
|
+
})
|
|
2279
|
+
);
|
|
2254
2280
|
let openQuestions = 0;
|
|
2255
|
-
for (const
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
openQuestions += await countOpenQuestions(projectPath, assignment.slug);
|
|
2281
|
+
for (const entry of perAssignment) {
|
|
2282
|
+
progress[entry.status] = (progress[entry.status] ?? 0) + 1;
|
|
2283
|
+
openQuestions += entry.openQuestions;
|
|
2259
2284
|
}
|
|
2260
2285
|
const needsAttention = {
|
|
2261
2286
|
blockedCount: progress["blocked"] ?? 0,
|
|
@@ -2308,18 +2333,26 @@ function buildDependencyGraph(assignments) {
|
|
|
2308
2333
|
function findAssignmentStatus(assignments, slug) {
|
|
2309
2334
|
return assignments.find((assignment) => assignment.slug === slug)?.status ?? "pending";
|
|
2310
2335
|
}
|
|
2311
|
-
async function getAvailableTransitions(projectsDir, projectSlug, assignmentSlug, assignment) {
|
|
2336
|
+
async function getAvailableTransitions(projectsDir, projectSlug, assignmentSlug, assignment, options) {
|
|
2312
2337
|
const config = await getStatusConfig();
|
|
2313
2338
|
const transitionDefs = getTransitionDefinitions(config);
|
|
2314
2339
|
const actions = [];
|
|
2315
2340
|
const projectPath = resolve11(projectsDir, projectSlug);
|
|
2341
|
+
const traces = options?.traces;
|
|
2316
2342
|
for (const definition of transitionDefs) {
|
|
2317
2343
|
let warning = null;
|
|
2318
2344
|
if (definition.command === "start" && !assignment.assignee) {
|
|
2319
2345
|
warning = "No assignee set \u2014 consider assigning before starting.";
|
|
2320
2346
|
}
|
|
2321
2347
|
if (definition.command === "start" && assignment.dependsOn.length > 0) {
|
|
2322
|
-
const
|
|
2348
|
+
const t0 = traces ? performance.now() : 0;
|
|
2349
|
+
const unmetDependencies = await getUnmetDependencies(
|
|
2350
|
+
projectPath,
|
|
2351
|
+
assignment.dependsOn,
|
|
2352
|
+
config.terminalStatuses,
|
|
2353
|
+
options?.dependencyStatusMap
|
|
2354
|
+
);
|
|
2355
|
+
if (traces) accumulatePhase(traces, "get-unmet-dependencies", performance.now() - t0);
|
|
2323
2356
|
if (unmetDependencies.length > 0) {
|
|
2324
2357
|
warning = `Unmet dependencies: ${unmetDependencies.join(", ")}.`;
|
|
2325
2358
|
}
|
|
@@ -2338,10 +2371,19 @@ async function getAvailableTransitions(projectsDir, projectSlug, assignmentSlug,
|
|
|
2338
2371
|
}
|
|
2339
2372
|
return actions;
|
|
2340
2373
|
}
|
|
2341
|
-
async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses) {
|
|
2374
|
+
async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses, dependencyStatusMap) {
|
|
2342
2375
|
const terminals = terminalStatuses ?? /* @__PURE__ */ new Set(["completed"]);
|
|
2343
2376
|
const unmet = [];
|
|
2344
2377
|
for (const dependency of dependsOn) {
|
|
2378
|
+
if (dependencyStatusMap) {
|
|
2379
|
+
const mappedStatus = dependencyStatusMap.get(dependency);
|
|
2380
|
+
if (mappedStatus !== void 0) {
|
|
2381
|
+
if (!terminals.has(mappedStatus)) {
|
|
2382
|
+
unmet.push(`${dependency} (${mappedStatus})`);
|
|
2383
|
+
}
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2345
2387
|
const dependencyPath = resolve11(projectPath, "assignments", dependency, "assignment.md");
|
|
2346
2388
|
if (!await fileExists(dependencyPath)) {
|
|
2347
2389
|
unmet.push(`${dependency} (missing)`);
|