@psnext/slingcli 3.3.20260914-1 → 3.3.20260915-1

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 (57) hide show
  1. package/README.md +17 -0
  2. package/bin/sling.js +117 -43
  3. package/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{chunk-BRL3MDQL.js → chunk-VY7PRZDT.js} +1 -1
  4. package/node_modules/@earendil-works/pi-coding-agent/dist/bundle/cli.js +1 -1
  5. package/node_modules/@earendil-works/pi-coding-agent/dist/bundle/index.js +1 -1
  6. package/node_modules/@earendil-works/pi-coding-agent/dist/bundle/rpc-entry.js +1 -1
  7. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +2149 -248
  8. package/node_modules/@earendil-works/pi-coding-agent/package.json +1 -0
  9. package/node_modules/brace-expansion/dist/commonjs/index.d.ts +4 -0
  10. package/node_modules/brace-expansion/dist/commonjs/index.d.ts.map +1 -1
  11. package/node_modules/brace-expansion/dist/commonjs/index.js +69 -25
  12. package/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -1
  13. package/node_modules/brace-expansion/dist/esm/index.d.ts +4 -0
  14. package/node_modules/brace-expansion/dist/esm/index.d.ts.map +1 -1
  15. package/node_modules/brace-expansion/dist/esm/index.js +68 -24
  16. package/node_modules/brace-expansion/dist/esm/index.js.map +1 -1
  17. package/node_modules/brace-expansion/package.json +2 -1
  18. package/node_modules/fast-uri/index.js +10 -6
  19. package/node_modules/fast-uri/package.json +1 -1
  20. package/node_modules/fast-uri/test/security-normalization.test.js +38 -0
  21. package/node_modules/hono/README.md +1 -1
  22. package/node_modules/hono/dist/adapter/aws-lambda/handler.js +5 -4
  23. package/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +5 -4
  24. package/node_modules/hono/dist/cjs/context.js +1 -1
  25. package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +1 -1
  26. package/node_modules/hono/dist/cjs/jsx/dom/render.js +44 -8
  27. package/node_modules/hono/dist/cjs/middleware/language/language.js +4 -1
  28. package/node_modules/hono/dist/cjs/request.js +4 -1
  29. package/node_modules/hono/dist/cjs/utils/accept.js +4 -7
  30. package/node_modules/hono/dist/context.js +1 -1
  31. package/node_modules/hono/dist/helper/accepts/accepts.js +1 -1
  32. package/node_modules/hono/dist/jsx/dom/render.js +44 -8
  33. package/node_modules/hono/dist/middleware/language/language.js +4 -1
  34. package/node_modules/hono/dist/request.js +4 -1
  35. package/node_modules/hono/dist/types/helper/proxy/index.d.ts +1 -1
  36. package/node_modules/hono/dist/types/helper/route/index.d.ts +2 -2
  37. package/node_modules/hono/dist/types/middleware/combine/index.d.ts +3 -1
  38. package/node_modules/hono/dist/types/utils/types.d.ts +1 -1
  39. package/node_modules/hono/dist/utils/accept.js +4 -7
  40. package/node_modules/hono/package.json +1 -1
  41. package/node_modules/ip-address/README.md +114 -114
  42. package/node_modules/ip-address/dist/common.d.ts +5 -0
  43. package/node_modules/ip-address/dist/common.js +12 -0
  44. package/node_modules/ip-address/dist/common.js.map +1 -1
  45. package/node_modules/ip-address/dist/ipv4.d.ts +5 -1
  46. package/node_modules/ip-address/dist/ipv4.js +12 -1
  47. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  48. package/node_modules/ip-address/dist/ipv6.d.ts +5 -1
  49. package/node_modules/ip-address/dist/ipv6.js +15 -1
  50. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  51. package/node_modules/ip-address/package.json +1 -1
  52. package/node_modules/proxy-addr/HISTORY.md +60 -54
  53. package/node_modules/proxy-addr/README.md +1 -1
  54. package/node_modules/proxy-addr/index.js +32 -0
  55. package/node_modules/proxy-addr/package.json +8 -4
  56. package/package.json +2 -2
  57. package/slingshot/index.js +267 -264
package/README.md CHANGED
@@ -100,11 +100,28 @@ sling
100
100
  | `SLINGSHOT_WORKSPACE_ID` | Workspace ID for analytics |
101
101
  | `SLINGSHOT_DEBUG` | Enable Slingshot debug logging (`true`/`false`) |
102
102
  | `SKIP_SLING_AUTO_INSTALL` | Skip first-run auto-install of default packages |
103
+ | `SKIP_SLING_AUTO_UPDATE_CHECK` | Skip the startup check for a new Sling release |
103
104
 
104
105
  Slingshot debug logs are written to `~/.sling/logs/analytics/<timestamp>.log`.
105
106
 
106
107
  ## Updating
107
108
 
109
+ Sling checks for a new release at startup. Nothing is ever installed without your
110
+ say-so, and what it offers depends on the release type:
111
+
112
+ - **Patch** (a new build within the same `major.minor` line): Sling shows the new
113
+ version and asks before installing, then restarts. Answer `n` to stay put, or pass
114
+ `-y` / `--yes` to accept without being asked. Outside an interactive terminal Sling
115
+ shows the manual command instead of prompting.
116
+ - **Minor** (a new `major.minor` line) and **major**: never installed by Sling. It
117
+ shows a notice with the new version and the command to run yourself.
118
+
119
+ The one exception is a mandatory minimum version published by the Sling team: below
120
+ that floor Sling updates without prompting.
121
+
122
+ Set `SKIP_SLING_AUTO_UPDATE_CHECK=1`, pass `--offline`, or set `PI_OFFLINE=1` to skip the
123
+ check entirely. To update manually at any time:
124
+
108
125
  ```bash
109
126
  npm install -g @psnext/slingcli@latest
110
127
  ```
package/bin/sling.js CHANGED
@@ -50,6 +50,9 @@ function findSlingPkg() {
50
50
  }
51
51
  const { slingVersion: SLING_VERSION } = findSlingPkg();
52
52
  process.env.SLING_VERSION = SLING_VERSION;
53
+ // Sling owns its version checks. Disable the bundled pi startup notice so
54
+ // users only see Sling's update policy and instructions below.
55
+ process.env.PI_SKIP_VERSION_CHECK ||= "1";
53
56
  process.env.PI_SUBAGENT_PI_BINARY_ENV = "sling";
54
57
  // if (IS_DEV) process.env.PI_SUBAGENT_PI_BINARY_ENV = "node " + process.argv[1];
55
58
  const { main } = IS_DEV
@@ -84,9 +87,9 @@ function applyNoEnv(args) {
84
87
  return filtered;
85
88
  }
86
89
 
87
- async function runCommand(command, args) {
90
+ async function runCommand(command, args, options = {}) {
88
91
  return new Promise((resolve, reject) => {
89
- const proc = spawn(command, args, { stdio: "inherit", shell: false });
92
+ const proc = spawn(command, args, { stdio: "inherit", shell: false, ...options });
90
93
  proc.on("close", (code) => {
91
94
  if (code !== 0) reject(new Error(`Command failed with code ${code}`));
92
95
  else resolve();
@@ -275,67 +278,138 @@ export async function checkAndInstallPackages(argv = process.argv.slice(2)) {
275
278
  }
276
279
  }
277
280
 
278
- async function checkForUpdate(args) {
279
- if (process.env.SKIP_SLING_AUTO_UPDATE_CHECK) return;
280
- if (args.includes("--version") || args.includes("-v")) return;
281
- if (args.includes("install")) return;
282
-
283
- const autoYes = hasUpdateRequest(args);
284
-
285
- const [latestVersion, remoteConfig] = await Promise.all([
286
- fetchLatestNpmVersion(),
287
- fetchRemoteAppConfig(),
288
- ]);
281
+ export function getSlingUpdateKind(currentVersion, latestVersion) {
282
+ const current = semver.parse(currentVersion);
283
+ const latest = semver.parse(latestVersion);
284
+ if (!current || !latest || !semver.gt(latest, current)) return undefined;
285
+
286
+ if (latest.major !== current.major) return "major";
287
+ if (latest.minor !== current.minor) return "minor";
288
+ // Sling releases carry the build date in the patch position and the build
289
+ // number as a prerelease tag (3.3.20260914-1). Both move within the same
290
+ // major.minor line, so both count as a patch release for update purposes.
291
+ return "patch";
292
+ }
289
293
 
290
- if (!latestVersion || !semver.gt(latestVersion, SLING_VERSION)) return;
294
+ const MANUAL_UPDATE_COMMAND = "npm install -g @psnext/slingcli@latest";
291
295
 
292
- // Gate 2: force if current < minVersion (user is below the minimum required)
293
- const shouldForceUpdate = remoteConfig && semver.lt(SLING_VERSION, remoteConfig.minVersion);
296
+ function showManualUpdateNotice(currentVersion, latestVersion, updateKind) {
297
+ const summary =
298
+ `Sling ${updateKind} update available: ${latestVersion} (current ${currentVersion}). ` +
299
+ `This version is not installed automatically. Run: ${MANUAL_UPDATE_COMMAND}`;
294
300
 
295
- if (!shouldForceUpdate && !autoYes && (!process.stdin.isTTY || !process.stdout.isTTY)) return;
301
+ // Printed here it lands on the normal screen buffer, which the TUI hides the
302
+ // moment it switches to the alternate buffer. Hand it to the slingshot
303
+ // extension too, which re-shows it inside the session where it can be read.
304
+ process.env.SLING_UPDATE_NOTICE = summary;
296
305
 
297
- if (shouldForceUpdate) {
298
- if (remoteConfig?.updateMessage) console.log(remoteConfig.updateMessage);
299
- console.log(`Required version ${latestVersion} detected. Updating now...`);
300
- } else {
301
- console.log(`A new version ${latestVersion} is available (current ${SLING_VERSION}).`);
302
- }
306
+ console.log("\nSling update available");
307
+ console.log(
308
+ `A new ${updateKind} version ${latestVersion} is available (current ${currentVersion}).`,
309
+ );
310
+ console.log("This version is not installed automatically.");
311
+ console.log(`Run: ${MANUAL_UPDATE_COMMAND}\n`);
312
+ }
303
313
 
304
- let shouldUpdate = autoYes || shouldForceUpdate;
305
- if (!shouldUpdate) {
306
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
307
- let answer;
308
- try {
309
- answer = (await rl.question("Update now? [Y/n] ")).trim().toLowerCase();
310
- } finally {
311
- rl.close();
312
- }
313
- shouldUpdate = !answer.startsWith("n");
314
+ async function confirmUpdate(latestVersion) {
315
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
316
+ try {
317
+ const answer = (await rl.question(`Update to ${latestVersion} now? [Y/n] `)).trim().toLowerCase();
318
+ return !answer.startsWith("n");
319
+ } finally {
320
+ rl.close();
314
321
  }
322
+ }
315
323
 
316
- if (!shouldUpdate) return;
317
-
318
- console.log("Installing @psnext/slingcli@" + latestVersion + "...");
324
+ // Shared tail of every path that actually updates. `required` marks the
325
+ // remote-kill-switch path, where failing to update is fatal.
326
+ async function installAndRestart(versionSpec, args, { required }) {
327
+ console.log(`Installing @psnext/slingcli@${versionSpec}...`);
319
328
  try {
320
- await runCommand("npm", ["install", "-g", "@psnext/slingcli@latest"]);
329
+ await runCommand("npm", ["install", "-g", `@psnext/slingcli@${versionSpec}`]);
321
330
  } catch {
322
331
  console.error("✗ Auto-update failed. Run manually:");
323
- console.error(" npm install -g @psnext/slingcli@latest");
332
+ console.error(` ${MANUAL_UPDATE_COMMAND}`);
324
333
  console.error(" (you may need sudo)");
325
- if (shouldForceUpdate) process.exit(1);
334
+ if (required) process.exit(1);
326
335
  return;
327
336
  }
328
337
 
329
- console.log("✓ Updated to " + latestVersion + ". Restarting sling...");
338
+ console.log(`✓ Updated to ${versionSpec}. Restarting sling...`);
330
339
  const restartArgs = stripUpdateRequestFlags(args);
331
340
  try {
332
- await runCommand(process.execPath, [__filename, ...restartArgs]);
333
- process.exit(0);
341
+ // The restarted process must not run the update check again. If the global
342
+ // install landed on a different npm prefix than the binary running now, it
343
+ // would rediscover the same update and reinstall in a loop.
344
+ await runCommand(process.execPath, [__filename, ...restartArgs], {
345
+ env: { ...process.env, SKIP_SLING_AUTO_UPDATE_CHECK: "1" },
346
+ });
334
347
  } catch {
335
348
  console.error("✓ Updated, but failed to restart automatically.");
336
349
  console.error(" Please run: sling " + restartArgs.join(" "));
337
- process.exit(0);
338
350
  }
351
+ process.exit(0);
352
+ }
353
+
354
+ async function checkForUpdate(args) {
355
+ if (isTruthyFlag(process.env.SKIP_SLING_AUTO_UPDATE_CHECK)) return;
356
+ if (isTruthyFlag(process.env.PI_OFFLINE)) return;
357
+ if (args.includes("--version") || args.includes("-v")) return;
358
+ if (args.includes("--offline")) return;
359
+ if (args.includes("install")) return;
360
+
361
+ const [latestVersion, remoteConfig] = await Promise.all([
362
+ fetchLatestNpmVersion(),
363
+ fetchRemoteAppConfig(),
364
+ ]);
365
+
366
+ // The remote kill switch outranks the semver policy: anyone below minVersion
367
+ // is updated without a prompt, whatever the release type. It runs before the
368
+ // semver gate on purpose — behind it, an unreachable registry would leave a
369
+ // user who is below the floor sitting on their old build.
370
+ if (remoteConfig && semver.lt(SLING_VERSION, remoteConfig.minVersion)) {
371
+ if (remoteConfig.updateMessage) console.log(remoteConfig.updateMessage);
372
+ if (latestVersion) {
373
+ console.log(`Required version ${latestVersion} detected. Updating now...`);
374
+ } else {
375
+ console.log(
376
+ `Sling ${SLING_VERSION} is below the required minimum ${remoteConfig.minVersion}. Updating now...`,
377
+ );
378
+ }
379
+ // No published version in hand means the registry lookup failed; let npm
380
+ // resolve the dist-tag rather than skipping a mandatory update.
381
+ await installAndRestart(latestVersion ?? "latest", args, { required: true });
382
+ return;
383
+ }
384
+
385
+ const updateKind = latestVersion && getSlingUpdateKind(SLING_VERSION, latestVersion);
386
+ if (!updateKind) return;
387
+
388
+ // Sling only ever installs a patch itself. Minor and major are announced and
389
+ // left to the user to run manually.
390
+ if (updateKind !== "patch") {
391
+ showManualUpdateNotice(SLING_VERSION, latestVersion, updateKind);
392
+ return;
393
+ }
394
+
395
+ // Even a patch needs the user to say yes. `-y`/`--yes` is that yes.
396
+ if (!hasUpdateRequest(args)) {
397
+ // No way to ask in a pipe or CI run, so fall back to the manual notice.
398
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
399
+ showManualUpdateNotice(SLING_VERSION, latestVersion, updateKind);
400
+ return;
401
+ }
402
+ console.log("\nSling update available");
403
+ console.log(
404
+ `A new patch version ${latestVersion} is available (current ${SLING_VERSION}).`,
405
+ );
406
+ if (!(await confirmUpdate(latestVersion))) {
407
+ console.log(`Staying on ${SLING_VERSION}. Update later with: ${MANUAL_UPDATE_COMMAND}\n`);
408
+ return;
409
+ }
410
+ }
411
+
412
+ await installAndRestart(latestVersion, args, { required: false });
339
413
  }
340
414
 
341
415
  // `sling install <pkg>` defaults to global (~/.sling/agent). When run from a