@useguano/create 0.1.4 → 0.1.5

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.
Files changed (2) hide show
  1. package/index.js +48 -3
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -13,6 +13,7 @@
13
13
  // The server binds the token to the first admin at /admin setup, and the
14
14
  // setup form shows the permissions instead of asking them again.
15
15
  import { spawnSync } from 'node:child_process'
16
+ import { createServer } from 'node:net'
16
17
  import { existsSync } from 'node:fs'
17
18
  import { mkdir, readdir, writeFile } from 'node:fs/promises'
18
19
  import { basename, join, resolve } from 'node:path'
@@ -80,6 +81,38 @@ if ((await readdir(target)).length) {
80
81
  process.exit(1)
81
82
  }
82
83
 
84
+ /**
85
+ * A free port for THIS project, written into .env as an explicit PORT.
86
+ *
87
+ * Every project used to default to 4174, so a second one on the same machine
88
+ * lost the race and walked to the next free port — which left its MCP entry,
89
+ * the Vite proxy and any open tab addressing the FIRST project's socket, where
90
+ * its own token is unknown and the 401 reads as "the token is dead". An
91
+ * explicit port is a contract the server honours instead of walking, and
92
+ * putting it in a file the user can read makes "which port is this site on" a
93
+ * question with an answer.
94
+ *
95
+ * 4174 is still preferred, so a first project looks exactly as documented.
96
+ */
97
+ async function pickPort(first = 4174, tries = 40) {
98
+ // A site needs its own port AND the preview port above it, so step by two.
99
+ for (let port = first; port < first + tries * 2; port += 2) {
100
+ if ((await free(port)) && (await free(port + 1))) return port
101
+ }
102
+ return first
103
+ }
104
+
105
+ function free(port) {
106
+ return new Promise((resolve) => {
107
+ const probe = createServer()
108
+ probe.once('error', () => resolve(false))
109
+ probe.once('listening', () => probe.close(() => resolve(true)))
110
+ probe.listen(port, '0.0.0.0')
111
+ })
112
+ }
113
+
114
+ const port = await pickPort()
115
+
83
116
  const files = {
84
117
  'package.json':
85
118
  JSON.stringify(
@@ -107,7 +140,16 @@ const files = {
107
140
  export default {}
108
141
  `,
109
142
 
110
- '.env.example': `# PORT=4174
143
+ // A real .env, not just the example: the port is this project's identity on
144
+ // the machine, and leaving it to the 4174 default is what makes two projects
145
+ // fight over one address.
146
+ '.env': `# This project's own port. Keep it — two Guano sites on one machine
147
+ # must not share one, or whichever starts first answers for both and the
148
+ # other's MCP token is refused with a 401.
149
+ PORT=${port}
150
+ `,
151
+
152
+ '.env.example': `PORT=${port}
111
153
  # GUANO_DATA_DIR=./data # users, media, drafts, the published site — back this up
112
154
  # COOKIE_SECURE=1 # set 0 only for plain-HTTP LAN setups
113
155
  # PUBLISH_TOKEN= # optional: token for CI publishes
@@ -128,8 +170,11 @@ A [Guano](https://www.npmjs.com/package/@useguano/guano) site.
128
170
  npm run dev
129
171
  \`\`\`
130
172
 
131
- Open http://localhost:4174/admin, create the first account, then build your
132
- site. Your published site is served at http://localhost:4174/.
173
+ Open http://localhost:${port}/admin, create the first account, then build your
174
+ site. Your published site is served at http://localhost:${port}/.
175
+
176
+ This project runs on port ${port} (set in \`.env\`). Each Guano site on a
177
+ machine needs its own, so two of them never answer for one address.
133
178
 
134
179
  If you said yes to Claude Desktop when scaffolding, it is already connected:
135
180
  the agent token is bound to the admin account you create, and the permissions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useguano/create",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "license": "AGPL-3.0-only",
5
5
  "publishConfig": {
6
6
  "access": "public"