@catheadowl/ukp 0.2.3 → 0.2.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.
@@ -48,15 +48,15 @@ export function printTaskPreflightError(input: {
48
48
  allowAnonymous: boolean;
49
49
  }): string | undefined {
50
50
  if (input.platform !== "win32") {
51
- return "--print-task is Windows-only; on Linux prefer systemd socket activation (ukp serve --systemd-socket) — the deployment handbook carries that recipe";
51
+ return "--print-task is Windows-only; on Linux prefer systemd socket activation (ukp serve --systemd-socket) - the deployment handbook carries that recipe";
52
52
  }
53
53
  const loopback =
54
54
  input.host === "127.0.0.1" || input.host === "localhost" || input.host === "::1" || input.host === "[::1]";
55
55
  if (!loopback && !input.hasTls) {
56
- return "refusing to print an off-loopback door without TLS: clients refuse to register plain-http urls — pass --tls (self-signed, pinned at registration) or --tls-cert/--tls-key, or bind loopback behind a TLS-terminating proxy";
56
+ return "refusing to print an off-loopback door without TLS: clients refuse to register plain-http urls - pass --tls (self-signed, pinned at registration) or --tls-cert/--tls-key, or bind loopback behind a TLS-terminating proxy";
57
57
  }
58
58
  if (input.allowAnonymous && !loopback) {
59
- return "refusing to print this shape: --allow-anonymous is loopback-only and serve would refuse it at task start — set the token in the generated script instead";
59
+ return "refusing to print this shape: --allow-anonymous is loopback-only and serve would refuse it at task start - set the token in the generated script instead";
60
60
  }
61
61
  return undefined;
62
62
  }
@@ -86,10 +86,10 @@ function parentDirOf(path: string): string | undefined {
86
86
  export function renderServeTaskArtifacts(input: ServeTaskInput): string {
87
87
  const serveLine = serveCommandLine(input);
88
88
  const out: string[] = [
89
- "ukp serve --print-task — Windows resident-door artifacts",
89
+ "ukp serve --print-task - Windows resident-door artifacts",
90
90
  "",
91
91
  "Print-only: review these, then apply them yourself. UKP installs nothing,",
92
- "starts nothing, and supervises nothing — the door's lifecycle belongs to",
92
+ "starts nothing, and supervises nothing - the door's lifecycle belongs to",
93
93
  "you and your process manager.",
94
94
  "",
95
95
  ];
@@ -97,7 +97,7 @@ export function renderServeTaskArtifacts(input: ServeTaskInput): string {
97
97
  if (input.tls?.mode === "certificates") {
98
98
  const certDir = parentDirOf(input.tls.certPath);
99
99
  out.push(
100
- "0) Certificate — generate ONCE (Git for Windows' openssl works; this",
100
+ "0) Certificate - generate ONCE (Git for Windows' openssl works; this",
101
101
  " avoids any runtime openssl dependency of --tls):",
102
102
  "",
103
103
  " openssl req -x509 -newkey rsa:2048 -nodes -days 825 -keyout",
@@ -115,7 +115,7 @@ export function renderServeTaskArtifacts(input: ServeTaskInput): string {
115
115
  }
116
116
 
117
117
  out.push(
118
- "1) Start script — save as %USERPROFILE%\\.ukp\\start-door.cmd",
118
+ "1) Start script - save as %USERPROFILE%\\.ukp\\start-door.cmd",
119
119
  " (write it with a real editor and CRLF line endings; writing cmd",
120
120
  " files over ssh echo mangles % escaping)",
121
121
  "",
@@ -125,13 +125,13 @@ export function renderServeTaskArtifacts(input: ServeTaskInput): string {
125
125
  " rem (e.g. openssl rand -hex 32), paste it in, keep this file private.",
126
126
  " set \"UKP_SERVE_TOKEN=<paste-your-token>\"",
127
127
  " rem ukp resolves through the logged-on task's per-user PATH; the",
128
- " rem launcher finds bun itself — no PATH export needed.",
128
+ " rem launcher finds bun itself - no PATH export needed.",
129
129
  " if not exist \"%USERPROFILE%\\.ukp\" mkdir \"%USERPROFILE%\\.ukp\"",
130
130
  ` ${serveLine} >> "%USERPROFILE%\\.ukp\\door.log" 2>&1`,
131
131
  "",
132
- "2) Hidden launcher — save as %USERPROFILE%\\.ukp\\start-door-hidden.vbs",
132
+ "2) Hidden launcher - save as %USERPROFILE%\\.ukp\\start-door-hidden.vbs",
133
133
  " (CRLF and a real editor again: the vbs carries literal % signs that",
134
- " ssh echo would expand — copy the file instead)",
134
+ " ssh echo would expand - copy the file instead)",
135
135
  "",
136
136
  " ' UKP door hidden launcher: runs start-door.cmd with NO visible",
137
137
  " ' window (hidden at process creation - nothing flashes), captures",
@@ -150,7 +150,7 @@ export function renderServeTaskArtifacts(input: ServeTaskInput): string {
150
150
  " Next",
151
151
  " WScript.Quit rc",
152
152
  "",
153
- "3) Scheduled task — starts the door at LOGON, hidden",
153
+ "3) Scheduled task - starts the door at LOGON, hidden",
154
154
  " (create it from an elevated shell: creating an ONLOGON task from a",
155
155
  " plain shell fails with Access denied)",
156
156
  "",
@@ -167,29 +167,29 @@ export function renderServeTaskArtifacts(input: ServeTaskInput): string {
167
167
  "- --tls self-signs at first start using an openssl on PATH (Git",
168
168
  " for Windows' openssl qualifies). On a host with no openssl",
169
169
  " anywhere, pre-generate the certificate once instead: re-run with",
170
- " --tls-cert/--tls-key — the handbook's Windows section carries",
170
+ " --tls-cert/--tls-key - the handbook's Windows section carries",
171
171
  " that recipe.",
172
172
  ]
173
173
  : []),
174
174
  "- No visible window, by design: the hidden launcher starts the console",
175
175
  " hidden at process creation (nothing ever flashes on the desktop) and",
176
- " captures everything to door.log — that redirect is the paper trail (a",
176
+ " captures everything to door.log - that redirect is the paper trail (a",
177
177
  " silent EADDRINUSE exit leaves a line).",
178
178
  "- Crash self-recovery is the launcher's bounded retry: a non-zero exit",
179
179
  " is retried up to 3 times, 30s apart; exhausted retries leave the",
180
180
  " failure visible (non-zero task result + door.log). Do not rely on",
181
- " Task Scheduler's restart-on-failure setting — it does not fire on",
181
+ " Task Scheduler's restart-on-failure setting - it does not fire on",
182
182
  " exit codes.",
183
183
  "- To restart the door by hand (new token, ukp upgrade, new cert):",
184
184
  " taskkill /PID <door-pid> /T /F, then schtasks /Run /TN ukp-door",
185
185
  " (schtasks /End does not reliably kill the process tree).",
186
- "- LOGON, not boot: the door starts when someone logs on — nobody",
186
+ "- LOGON, not boot: the door starts when someone logs on - nobody",
187
187
  " logged on means no door. Unattended-boot always-on needs a service",
188
- " wrapper (WinSW-class) — deliberately outside this recipe; prefer the",
188
+ " wrapper (WinSW-class) - deliberately outside this recipe; prefer the",
189
189
  " ssh:// path or a Linux host (systemd socket activation) for that.",
190
190
  "- Re-run ukp serve --print-task with different flags to regenerate;",
191
191
  " edits land by saving the files and re-running schtasks /Run.",
192
- "- Full recipes and hardening: the deployment handbook —",
192
+ "- Full recipes and hardening: the deployment handbook -",
193
193
  " docs/remote-deployment.md in this package, or",
194
194
  " https://github.com/CatheadOwl/ukp/blob/main/docs/remote-deployment.md",
195
195
  "",
@@ -34,23 +34,23 @@ function parseServeTokens(raw: string | undefined): string[] {
34
34
  * over `/e/<name>/` routing. */
35
35
  export const SERVE_SPEC: UkpCommandSpec = {
36
36
  name: "serve",
37
- summary: "serve one endpoint — or the whole registry as a host door, one HTTP server for every endpoint — for remote UKP clients",
37
+ summary: "serve one endpoint - or the whole registry as a host door, one HTTP server for every endpoint - for remote UKP clients",
38
38
  group: "operations",
39
39
  description:
40
- "Expose a registered endpoint over HTTP using the ukp-remote wire, or — without --endpoint — serve every local endpoint as one host door routed by name.",
41
- usage: "[--endpoint <name>] [--host <addr>] [--port <n>] [--tls [--tls-san <ip|dns>]… | --tls-cert <pem> --tls-key <pem>] [--max-idle <seconds>] [--systemd-socket] [--print-task]",
40
+ "Expose a registered endpoint over HTTP using the ukp-remote wire, or - without --endpoint - serve every local endpoint as one host door routed by name.",
41
+ usage: "[--endpoint <name>] [--host <addr>] [--port <n>] [--tls [--tls-san <ip|dns>]... | --tls-cert <pem> --tls-key <pem>] [--max-idle <seconds>] [--systemd-socket] [--print-task]",
42
42
  options: [
43
43
  { flags: "--endpoint <name>", help: "the registered endpoint to expose; omit it to serve the whole registry as a host door (/e/<name>/ routing, all endpoints, one port)" },
44
44
  { flags: "--host <addr>", help: "listen address (default 127.0.0.1, loopback only without a token)" },
45
45
  { flags: "--port <n>", help: "listen port (default 8570)" },
46
46
  { flags: "--tls", help: "serve HTTPS with a self-signed identity (auto-generated under .ukp/tls/, SAN covers this host's addresses; clients pin it at registration)" },
47
47
  { flags: "--tls-san <ip|dns>", help: "extra SAN entry for the --tls self-signed identity (repeatable): a public/NAT IP or hostname no NIC of this host carries; a persisted certificate missing an entry is re-signed over the same key (pin unchanged, pinned clients re-anchor)", multi: true },
48
- { flags: "--tls-cert <pem>", help: "TLS certificate (chain) PEM path — Let's Encrypt, mkcert, or a private CA; pair with --tls-key" },
48
+ { flags: "--tls-cert <pem>", help: "TLS certificate (chain) PEM path - Let's Encrypt, mkcert, or a private CA; pair with --tls-key" },
49
49
  { flags: "--tls-key <pem>", help: "TLS private key PEM path; pair with --tls-cert" },
50
50
  { flags: "--allow-anonymous", help: "permit tokenless access on loopback (local testing, or an ssh-forwarded host door where SSH carries encryption and auth; reverse-proxy deployments still require UKP_SERVE_TOKEN)" },
51
- { flags: "--max-idle <seconds>", help: "exit after <seconds> without requests (self-reap; the orphan backstop for on-demand-woken doors — fractional values accepted for tests)" },
52
- { flags: "--systemd-socket", help: "serve on the systemd socket-activation listener (LISTEN_FDS fd 3) instead of binding a port — the port belongs to your .socket unit; Linux-only" },
53
- { flags: "--print-task", help: "Windows: print this door's process-manager artifacts (start script, hidden launcher, Task Scheduler command, firewall rule) instead of serving — review and apply them yourself; nothing is installed or started for you, and the token is yours to paste (never printed)" },
51
+ { flags: "--max-idle <seconds>", help: "exit after <seconds> without requests (self-reap; the orphan backstop for on-demand-woken doors - fractional values accepted for tests)" },
52
+ { flags: "--systemd-socket", help: "serve on the systemd socket-activation listener (LISTEN_FDS fd 3) instead of binding a port - the port belongs to your .socket unit; Linux-only" },
53
+ { flags: "--print-task", help: "Windows: print this door's process-manager artifacts (start script, hidden launcher, Task Scheduler command, firewall rule) instead of serving - review and apply them yourself; nothing is installed or started for you, and the token is yours to paste (never printed)" },
54
54
  ],
55
55
  helpSuffix: [
56
56
  "",
@@ -58,9 +58,9 @@ export const SERVE_SPEC: UkpCommandSpec = {
58
58
  ` GET ${DISCOVERY_PATH} discovery document (manifest projection,`,
59
59
  " protocol version, instance identity)",
60
60
  " POST /v1/search {query, limit} -> ukp.search.v1 envelope",
61
- " GET /v1/read?ref=…|uri=… endpoint-relative ref or ukp:// URI",
61
+ " GET /v1/read?ref=...|uri=... endpoint-relative ref or ukp:// URI",
62
62
  " GET /v1/nav?path=&depth= markdown route view (ukp.nav.v1)",
63
- " GET /v1/rg?query=… lexical search (ukp.rg.v1, ukp_uri handoff)",
63
+ " GET /v1/rg?query=... lexical search (ukp.rg.v1, ukp_uri handoff)",
64
64
  "",
65
65
  "Wire (ukp-remote v1), host door (no --endpoint):",
66
66
  ` GET ${DISCOVERY_PATH} door document (scope:"host", endpoint roster)`,
@@ -75,26 +75,26 @@ export const SERVE_SPEC: UkpCommandSpec = {
75
75
  " --allow-anonymous, loopback binds only. The discovery document stays",
76
76
  " public. A reverse proxy on the same host forwards from the public side",
77
77
  " to the loopback bind, so proxy deployments treat the token as",
78
- " mandatory — serve cannot see past its own bind address.",
78
+ " mandatory - serve cannot see past its own bind address.",
79
79
  "",
80
80
  " Loopback-without-token covers testing/dogfood AND the ssh door",
81
81
  " deployment: bind the door on the remote host's loopback and let ssh",
82
82
  " forwarding carry encryption and authentication (clients register",
83
- " 'ukp register --url ssh://<host>' — zero tokens, docker",
83
+ " 'ukp register --url ssh://<host>' - zero tokens, docker",
84
84
  " DOCKER_HOST=ssh:// posture). It is not the way to consume a",
85
- " same-machine endpoint — register its local path instead.",
85
+ " same-machine endpoint - register its local path instead.",
86
86
  " TLS: pass --tls to serve HTTPS with a self-signed identity",
87
- " (generated under .ukp/tls/ — under the registry directory in door",
88
- " mode —, SAN covers this host's addresses; remote clients TOFU-pin it",
87
+ " (generated under .ukp/tls/ - under the registry directory in door",
88
+ " mode -, SAN covers this host's addresses; remote clients TOFU-pin it",
89
89
  " at registration and refresh by re-registering), or --tls-cert/--tls-key",
90
- " for your own certificate (Let's Encrypt — IP certs available since",
91
- " 2026-01 —, mkcert, a private CA). Plain HTTP remains loopback-only by",
90
+ " for your own certificate (Let's Encrypt - IP certs available since",
91
+ " 2026-01 -, mkcert, a private CA). Plain HTTP remains loopback-only by",
92
92
  " admission; public exposure needs TLS or the ssh:// transport.",
93
93
  "",
94
94
  " A cloud NAT/EIP host's public IP is on no NIC (--tls alone cannot",
95
95
  " cover it): add --tls-san <public-ip> next to --tls. Repeatable; DNS",
96
96
  " names accepted. The persisted certificate re-signs over the same key",
97
- " when a new entry appears — the pin is unchanged and pinned clients",
97
+ " when a new entry appears - the pin is unchanged and pinned clients",
98
98
  " re-anchor transparently; dropping entries never re-signs.",
99
99
  "",
100
100
  ].join("\n"),
@@ -136,7 +136,7 @@ function toParsedServe(parsed: KitParsed): ParsedServe {
136
136
  }
137
137
  const printTask = options.printTask === true;
138
138
  if (printTask && systemdSocket) {
139
- throw new KitUsageError("--print-task is the Windows resident form; --systemd-socket is the Linux one (systemd holds the port — see the deployment handbook)");
139
+ throw new KitUsageError("--print-task is the Windows resident form; --systemd-socket is the Linux one (systemd holds the port - see the deployment handbook)");
140
140
  }
141
141
  let maxIdleSeconds: number | undefined;
142
142
  if (options.maxIdle !== undefined) {
@@ -183,10 +183,10 @@ export function renderServeBanner(info: ServeInfo): string {
183
183
  `serving host door (ukp-remote v1)`,
184
184
  ` listening: ${info.url}`,
185
185
  ` discovery: ${info.url.startsWith("http") ? `${info.url}${DISCOVERY_PATH}` : info.url} (host door)`,
186
- ` endpoints: ${info.door!.endpoints.length > 0 ? info.door!.endpoints.join(", ") : "(none — register endpoints on this host)"}`,
186
+ ` endpoints: ${info.door!.endpoints.length > 0 ? info.door!.endpoints.join(", ") : "(none - register endpoints on this host)"}`,
187
187
  ` write: ${info.door!.write.length > 0 ? info.door!.write.join(", ") + " (propose via PUT /e/<name>/v1/propose/<id>)" : "(no endpoint declares propose)"}`,
188
188
  ` auth: ${info.authRequired ? "bearer token required" : "no token (loopback bind; ssh-forwarded clients authenticate by SSH key)"}`,
189
- ` rg: ${info.rg === "ok" ? "ok" : "missing (rg calls skip with a warning — install ripgrep on this door's PATH)"}`,
189
+ ` rg: ${info.rg === "ok" ? "ok" : "missing (rg calls skip with a warning - install ripgrep on this door's PATH)"}`,
190
190
  ...(info.maxIdleSeconds !== undefined
191
191
  ? [` idle: exits after ${info.maxIdleSeconds}s without requests (--max-idle)`]
192
192
  : []),
@@ -201,7 +201,7 @@ export function renderServeBanner(info: ServeInfo): string {
201
201
  ` listening: ${info.url}`,
202
202
  ` discovery: ${info.url.startsWith("http") ? `${info.url}${DISCOVERY_PATH}` : info.url}`,
203
203
  ` auth: ${info.authRequired ? "bearer token required" : "no token (loopback only)"}`,
204
- ` rg: ${info.rg === "ok" ? "ok" : "missing (rg calls skip with a warning — install ripgrep on this door's PATH)"}`,
204
+ ` rg: ${info.rg === "ok" ? "ok" : "missing (rg calls skip with a warning - install ripgrep on this door's PATH)"}`,
205
205
  ...(info.maxIdleSeconds !== undefined
206
206
  ? [` idle: exits after ${info.maxIdleSeconds}s without requests (--max-idle)`]
207
207
  : []),
@@ -295,7 +295,7 @@ export function executeServeCommand(
295
295
  // premise of --allow-anonymous is unverifiable here (RQ-18 review P1).
296
296
  if (systemdSocket && tokens.length === 0) {
297
297
  throw new Error(
298
- "refusing to serve on --systemd-socket without a token: the listener's bind address belongs to the socket unit and may be public — tokenless loopback serving cannot be verified here; set UKP_SERVE_TOKEN",
298
+ "refusing to serve on --systemd-socket without a token: the listener's bind address belongs to the socket unit and may be public - tokenless loopback serving cannot be verified here; set UKP_SERVE_TOKEN",
299
299
  );
300
300
  }
301
301
  const decision = serveAuthDecision(host, tokens, allowAnonymous);
package/src/registry.ts CHANGED
@@ -230,7 +230,7 @@ export function assertRegistrableRemoteUrl(raw: string): void {
230
230
  + `${parts.endpointName !== undefined ? `/${parts.endpointName}` : ""}`;
231
231
  throw new RegistryError(
232
232
  `ssh:// urls take no port: the woken door listens on a client-chosen loopback port and the url port selects nothing`
233
- + ` — register '${portless}' instead`
233
+ + ` - register '${portless}' instead`
234
234
  + ` (for a non-standard sshd port, use an ssh config Host alias)`,
235
235
  );
236
236
  }
@@ -240,7 +240,7 @@ export class RegistryBusyError extends RegistryError {
240
240
  constructor(path: string) {
241
241
  super(
242
242
  `registry_busy: ${path} (another ukp process holds the registry lock; ` +
243
- `stale locks are reclaimed automatically — close other ukp commands or retry shortly)`,
243
+ `stale locks are reclaimed automatically - close other ukp commands or retry shortly)`,
244
244
  );
245
245
  this.name = "RegistryBusyError";
246
246
  }
package/src/server.ts CHANGED
@@ -407,7 +407,7 @@ function parseReadParams(
407
407
  pin: string | null,
408
408
  ): { request: ReadRequest } | { error: string; class?: string } {
409
409
  if ((ref === null) === (uri === null)) {
410
- return { error: "exactly one of 'ref' (endpoint-relative) or 'uri' (ukp://…) is required" };
410
+ return { error: "exactly one of 'ref' (endpoint-relative) or 'uri' (ukp://...) is required" };
411
411
  }
412
412
  const args = uri !== null
413
413
  ? [
@@ -546,14 +546,14 @@ export function parseListenFds(env: NodeJS.ProcessEnv): { fd: number } | { error
546
546
  };
547
547
  }
548
548
  if (Number(env.LISTEN_PID) !== process.pid) {
549
- return { error: `LISTEN_PID ${env.LISTEN_PID} does not match this process (${process.pid}) — inherited activation variables belong to another process` };
549
+ return { error: `LISTEN_PID ${env.LISTEN_PID} does not match this process (${process.pid}) - inherited activation variables belong to another process` };
550
550
  }
551
551
  const count = Number(env.LISTEN_FDS);
552
552
  if (!Number.isSafeInteger(count) || count < 1) {
553
553
  return { error: `LISTEN_FDS=${String(env.LISTEN_FDS)} is not a positive integer` };
554
554
  }
555
555
  if (count !== 1) {
556
- return { error: `expected exactly one listening fd (LISTEN_FDS=1), got ${count} — run one socket unit per serve instance` };
556
+ return { error: `expected exactly one listening fd (LISTEN_FDS=1), got ${count} - run one socket unit per serve instance` };
557
557
  }
558
558
  delete env.LISTEN_FDS;
559
559
  delete env.LISTEN_PID;
@@ -645,7 +645,7 @@ function startServeListener(
645
645
  isSocket = false;
646
646
  }
647
647
  if (!isSocket) {
648
- throw new ServeSetupError(`fd ${parsed.fd} is not an open socket — socket activation must pass a bound listening socket`);
648
+ throw new ServeSetupError(`fd ${parsed.fd} is not an open socket - socket activation must pass a bound listening socket`);
649
649
  }
650
650
  const server = Bun.serve({ hostname: "127.0.0.1", port: 0, ...tls, fetch });
651
651
  const acceptor = netCreateServer();