syntaur 0.13.0 → 0.14.0

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.
@@ -28,6 +28,8 @@
28
28
  "./skills/log-progress",
29
29
  "./skills/set-workspace",
30
30
  "./skills/claim-resource",
31
- "./skills/release-resource"
31
+ "./skills/release-resource",
32
+ "./skills/extend-resource",
33
+ "./skills/list-resources"
32
34
  ]
33
35
  }
package/README.md CHANGED
@@ -238,9 +238,11 @@ syntaur lease list # pool overview
238
238
  syntaur lease --help # full subcommand list
239
239
  ```
240
240
 
241
- Two skills wrap this for agents: **claim-resource** (claim and persist into `.syntaur/context.json`) and **release-resource** (release one or `--all`).
241
+ Four skills wrap this for agents: **claim-resource**, **release-resource**, **extend-resource**, and **list-resources** see [`docs/leases/skills.md`](docs/leases/skills.md).
242
242
 
243
- **v1 scope:** static inventories (you register members), atomic claim, TTL expiry, dashboard view, fail-fast on contention (capacity-1 inventory works as a named lock). **Not in v1:** automatic provisioning / recycling, an in-process plugin SDK, cross-host coordination, blocking `--wait`. Members are recycled by the caller's own scripts between leases for now.
243
+ Admin and cleanup commands (`revoke`, `release-all --for`, `inventory delete`, `inventory update`, `member list`, `history`, plus `claim --wait`) are documented in [`docs/leases/cli-reference.md`](docs/leases/cli-reference.md).
244
+
245
+ **v1 scope:** static inventories (you register members), atomic claim, TTL expiry, dashboard view, optional `claim --wait`, fail-fast by default (capacity-1 inventory works as a named lock). **Not in v1:** automatic provisioning / recycling, an in-process plugin SDK, cross-host coordination, SessionEnd auto-release. Members are recycled by the caller's own scripts between leases for now.
244
246
 
245
247
  ---
246
248
 
@@ -8283,6 +8283,65 @@ function createServersRouter(serversDir2, projectsDir, assignmentsDir2) {
8283
8283
  import { Router as Router3 } from "express";
8284
8284
  import { resolve as resolve13 } from "path";
8285
8285
  init_fs();
8286
+
8287
+ // src/utils/transcript.ts
8288
+ import { open } from "fs/promises";
8289
+ var MAX_LINES_SCANNED = 50;
8290
+ async function derivePathFromTranscript(transcriptPath) {
8291
+ if (!transcriptPath) return null;
8292
+ let handle;
8293
+ try {
8294
+ handle = await open(transcriptPath, "r");
8295
+ } catch {
8296
+ return null;
8297
+ }
8298
+ try {
8299
+ const stream = handle.createReadStream({ encoding: "utf-8" });
8300
+ let buffer = "";
8301
+ let scanned = 0;
8302
+ for await (const chunk of stream) {
8303
+ buffer += chunk;
8304
+ let nl = buffer.indexOf("\n");
8305
+ while (nl !== -1) {
8306
+ const line = buffer.slice(0, nl);
8307
+ buffer = buffer.slice(nl + 1);
8308
+ const cwd = extractCwd(line);
8309
+ if (cwd) {
8310
+ stream.destroy();
8311
+ return cwd;
8312
+ }
8313
+ scanned++;
8314
+ if (scanned >= MAX_LINES_SCANNED) {
8315
+ stream.destroy();
8316
+ return null;
8317
+ }
8318
+ nl = buffer.indexOf("\n");
8319
+ }
8320
+ }
8321
+ if (buffer.length > 0) {
8322
+ const cwd = extractCwd(buffer);
8323
+ if (cwd) return cwd;
8324
+ }
8325
+ return null;
8326
+ } finally {
8327
+ await handle.close().catch(() => {
8328
+ });
8329
+ }
8330
+ }
8331
+ function extractCwd(line) {
8332
+ const trimmed = line.trim();
8333
+ if (trimmed.length === 0 || trimmed[0] !== "{") return null;
8334
+ try {
8335
+ const parsed = JSON.parse(trimmed);
8336
+ if (typeof parsed.cwd === "string" && parsed.cwd.length > 0) {
8337
+ return parsed.cwd;
8338
+ }
8339
+ } catch {
8340
+ }
8341
+ return null;
8342
+ }
8343
+
8344
+ // src/dashboard/api-agent-sessions.ts
8286
8345
  function createAgentSessionsRouter(projectsDir, broadcast, assignmentsDir2) {
8287
8346
  const router = Router3();
8288
8347
  router.get("/", async (_req, res) => {
@@ -8330,6 +8389,8 @@ function createAgentSessionsRouter(projectsDir, broadcast, assignmentsDir2) {
8330
8389
  return;
8331
8390
  }
8332
8391
  }
8392
+ const derivedPath = await derivePathFromTranscript(transcriptPath);
8393
+ const recordedPath = derivedPath ?? path ?? "";
8333
8394
  const session = {
8334
8395
  projectSlug: projectSlug || null,
8335
8396
  assignmentSlug: assignmentSlug || null,
@@ -8337,7 +8398,7 @@ function createAgentSessionsRouter(projectsDir, broadcast, assignmentsDir2) {
8337
8398
  sessionId,
8338
8399
  started: (/* @__PURE__ */ new Date()).toISOString(),
8339
8400
  status: "active",
8340
- path: path || "",
8401
+ path: recordedPath,
8341
8402
  description: description || null,
8342
8403
  transcriptPath: transcriptPath || null
8343
8404
  };
@@ -10340,7 +10401,7 @@ init_fs();
10340
10401
  init_config2();
10341
10402
  import { execFile as execFile2 } from "child_process";
10342
10403
  import { promisify as promisify2 } from "util";
10343
- import { cp, mkdtemp, rm as rm2, readFile as readFile14, writeFile as writeFile4, unlink as unlink3, stat, open, rename as rename5 } from "fs/promises";
10404
+ import { cp, mkdtemp, rm as rm2, readFile as readFile14, writeFile as writeFile4, unlink as unlink3, stat, open as open2, rename as rename5 } from "fs/promises";
10344
10405
  import { resolve as resolve19, join as join2 } from "path";
10345
10406
  import { tmpdir } from "os";
10346
10407
  var exec2 = promisify2(execFile2);
@@ -10395,7 +10456,7 @@ async function acquireLock() {
10395
10456
  const lockPath = resolve19(syntaurRoot(), LOCK_FILE_NAME);
10396
10457
  await ensureDir(syntaurRoot());
10397
10458
  try {
10398
- const handle = await open(lockPath, "wx");
10459
+ const handle = await open2(lockPath, "wx");
10399
10460
  await handle.write(String(process.pid));
10400
10461
  await handle.close();
10401
10462
  return lockPath;