syntaur 0.1.2 → 0.1.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/README.md CHANGED
@@ -114,3 +114,31 @@ Repo-local plugin linking for development:
114
114
  npx syntaur@latest install-plugin --link
115
115
  npx syntaur@latest install-codex-plugin --link
116
116
  ```
117
+
118
+ ## Release Publishing
119
+
120
+ This repo is set up for npm trusted publishing from GitHub Actions.
121
+
122
+ Release flow:
123
+
124
+ ```bash
125
+ npm version patch
126
+ git push origin main
127
+ git push origin v$(node -p "require('./package.json').version")
128
+ ```
129
+
130
+ The publish workflow lives at `.github/workflows/publish.yml` and only runs on version tags like `v0.1.4`. It checks that the tag matches `package.json`, runs the repo validation, and then publishes to npm using GitHub OIDC instead of a long-lived npm token.
131
+
132
+ One-time npm setup:
133
+
134
+ - package: `syntaur`
135
+ - GitHub repo: `prong-horn/syntaur`
136
+ - workflow filename: `publish.yml`
137
+
138
+ You can configure the trusted publisher either in the npm package settings UI or with npm CLI `11.10+`:
139
+
140
+ ```bash
141
+ npx npm@^11.10.0 trust github syntaur --repo prong-horn/syntaur --file publish.yml -y
142
+ ```
143
+
144
+ After trusted publishing is working, npm recommends switching the package publishing access to `Require two-factor authentication and disallow tokens`.
package/dist/index.js CHANGED
@@ -4773,6 +4773,7 @@ Use --slug to specify a different slug.`
4773
4773
  // src/commands/dashboard.ts
4774
4774
  init_config2();
4775
4775
  import { spawn } from "child_process";
4776
+ import { createServer as createNetServer } from "net";
4776
4777
  import { resolve as resolve18, dirname as dirname5 } from "path";
4777
4778
  import { fileURLToPath as fileURLToPath3 } from "url";
4778
4779
 
@@ -7234,6 +7235,30 @@ function resolveDashboardMode(options) {
7234
7235
  }
7235
7236
  return "static";
7236
7237
  }
7238
+ async function isPortAvailable(port) {
7239
+ return new Promise((resolveAvailability) => {
7240
+ const tester = createNetServer();
7241
+ tester.once("error", () => {
7242
+ resolveAvailability(false);
7243
+ });
7244
+ tester.once("listening", () => {
7245
+ tester.close(() => resolveAvailability(true));
7246
+ });
7247
+ tester.listen(port, "127.0.0.1");
7248
+ });
7249
+ }
7250
+ async function findAvailablePort(startPort, maxAttempts = 20) {
7251
+ for (let offset = 0; offset < maxAttempts; offset += 1) {
7252
+ const candidate = startPort + offset;
7253
+ if (candidate > 65535) {
7254
+ break;
7255
+ }
7256
+ if (await isPortAvailable(candidate)) {
7257
+ return candidate;
7258
+ }
7259
+ }
7260
+ return null;
7261
+ }
7237
7262
  async function dashboardCommand(options) {
7238
7263
  const config = await readConfig();
7239
7264
  const missionsDir = config.defaultMissionDir;
@@ -8343,6 +8368,19 @@ import { createInterface } from "readline/promises";
8343
8368
  function isInteractiveTerminal() {
8344
8369
  return Boolean(input.isTTY && output.isTTY);
8345
8370
  }
8371
+ function parseConfirmAnswer(answer, defaultValue = false) {
8372
+ const normalized = answer.trim().toLowerCase();
8373
+ if (normalized === "") {
8374
+ return defaultValue;
8375
+ }
8376
+ if (normalized === "y" || normalized === "yes") {
8377
+ return true;
8378
+ }
8379
+ if (normalized === "n" || normalized === "no") {
8380
+ return false;
8381
+ }
8382
+ return null;
8383
+ }
8346
8384
  async function confirmPrompt(question, defaultValue = false) {
8347
8385
  if (!isInteractiveTerminal()) {
8348
8386
  throw new Error("Interactive confirmation requires a TTY.");
@@ -8350,11 +8388,14 @@ async function confirmPrompt(question, defaultValue = false) {
8350
8388
  const suffix = defaultValue ? " [Y/n] " : " [y/N] ";
8351
8389
  const rl = createInterface({ input, output });
8352
8390
  try {
8353
- const answer = (await rl.question(`${question}${suffix}`)).trim().toLowerCase();
8354
- if (answer === "") {
8355
- return defaultValue;
8391
+ while (true) {
8392
+ const answer = await rl.question(`${question}${suffix}`);
8393
+ const parsed = parseConfirmAnswer(answer, defaultValue);
8394
+ if (parsed !== null) {
8395
+ return parsed;
8396
+ }
8397
+ console.log("Enter y, yes, n, no, or press Enter for the default.");
8356
8398
  }
8357
- return answer === "y" || answer === "yes";
8358
8399
  } finally {
8359
8400
  rl.close();
8360
8401
  }
@@ -8591,7 +8632,7 @@ async function setupCommand(options) {
8591
8632
  targetDir: options.claudeDir,
8592
8633
  promptForTarget: !options.yes
8593
8634
  });
8594
- } else if (!interactive && !options.yes && !initialized) {
8635
+ } else {
8595
8636
  console.log(`Skip Claude plugin for now. Install later with: ${getPluginInstallCommand("claude")}`);
8596
8637
  }
8597
8638
  if (installCodex) {
@@ -8600,12 +8641,22 @@ async function setupCommand(options) {
8600
8641
  marketplacePath: options.codexMarketplacePath,
8601
8642
  promptForTarget: !options.yes
8602
8643
  });
8603
- } else if (!interactive && !options.yes && !initialized) {
8644
+ } else {
8604
8645
  console.log(`Skip Codex plugin for now. Install later with: ${getPluginInstallCommand("codex")}`);
8605
8646
  }
8606
8647
  if (launchDashboard) {
8648
+ const preferredPort = 4800;
8649
+ const port = await findAvailablePort(preferredPort);
8650
+ if (port === null) {
8651
+ throw new Error(
8652
+ `Could not find an available dashboard port starting at ${preferredPort}. Run "syntaur dashboard --port <number>" to choose one manually.`
8653
+ );
8654
+ }
8655
+ if (port !== preferredPort) {
8656
+ console.log(`Port ${preferredPort} is busy. Launching the dashboard on port ${port} instead.`);
8657
+ }
8607
8658
  await dashboardCommand({
8608
- port: "4800",
8659
+ port: String(port),
8609
8660
  dev: false,
8610
8661
  serverOnly: false,
8611
8662
  apiOnly: false,