skalpel 3.0.0 → 3.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skalpel",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Skalpel — local proxy and TUI for coding agents (skalpel + skalpeld bundle).",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://skalpel.ai",
@@ -54,10 +54,10 @@
54
54
  "x64"
55
55
  ],
56
56
  "optionalDependencies": {
57
- "@skalpelai/skalpel-darwin-arm64": "3.0.0",
58
- "@skalpelai/skalpel-darwin-x64": "3.0.0",
59
- "@skalpelai/skalpel-linux-arm64": "3.0.0",
60
- "@skalpelai/skalpel-linux-x64": "3.0.0",
61
- "@skalpelai/skalpel-win32-x64": "3.0.0"
57
+ "@skalpelai/skalpel-darwin-arm64": "3.0.2",
58
+ "@skalpelai/skalpel-darwin-x64": "3.0.2",
59
+ "@skalpelai/skalpel-linux-arm64": "3.0.2",
60
+ "@skalpelai/skalpel-linux-x64": "3.0.2",
61
+ "@skalpelai/skalpel-win32-x64": "3.0.2"
62
62
  }
63
63
  }
@@ -1,13 +1,19 @@
1
1
  // Step 1: detect prior install.
2
2
  //
3
- // SPEC.md §9.6 read <configDir>/auth.json existence, config.toml
4
- // existence, and presence of LaunchAgent / systemd unit / Task. If any
5
- // of these are present, the wizard short-circuits and the TUI is
6
- // launched directly.
3
+ // v3.0.1 fix: `prior` now means specifically "user has auth state"
4
+ // i.e. auth.json exists. v3.0.0 treated any of {auth.json, config.toml,
5
+ // lock file, service registration} as "prior" and skipped sign-in.
6
+ // That meant once a machine had ever installed skalpel, the systemd /
7
+ // launchd unit file persists outside configDir; every subsequent
8
+ // `npm install skalpel` would skip sign-in forever, and the user
9
+ // could never get logged in via the install flow on re-install.
7
10
  //
8
- // We also check the lock file (which the daemon writes) so an active
9
- // install with running daemon is detected even if config.toml hasn't
10
- // been written yet.
11
+ // The other probes (config.toml / lock file / service registration)
12
+ // are informational only they get reported in the log but no longer
13
+ // gate sign-in. SPEC.md §9.6: a fresh user with no auth.json must
14
+ // always be offered the sign-in step, regardless of mechanical
15
+ // install state (service units, rc snippets) that survived a prior
16
+ // install.
11
17
 
12
18
  'use strict';
13
19
 
@@ -26,14 +32,15 @@ function fileExists(p) {
26
32
 
27
33
  function run({ dryRun }) {
28
34
  const probes = [
29
- { label: 'auth.json', path: paths.authFile() },
30
- { label: 'config.toml', path: paths.configToml() },
31
- { label: 'lock file', path: paths.lockFile() },
32
- { label: 'service registration', path: paths.servicePath() },
35
+ { label: 'auth.json', path: paths.authFile(), gatesPrior: true },
36
+ { label: 'config.toml', path: paths.configToml(), gatesPrior: false },
37
+ { label: 'lock file', path: paths.lockFile(), gatesPrior: false },
38
+ { label: 'service registration', path: paths.servicePath(), gatesPrior: false },
33
39
  ];
34
40
 
35
41
  const findings = probes.map((p) => ({ ...p, exists: fileExists(p.path) }));
36
- const prior = findings.some((f) => f.exists);
42
+ // `prior` reflects only auth.json existence — see header comment.
43
+ const prior = findings.some((f) => f.gatesPrior && f.exists);
37
44
 
38
45
  if (dryRun) {
39
46
  log.dryRun(`step 1 detect-prior: would probe ${findings.length} paths`);
@@ -44,7 +51,11 @@ function run({ dryRun }) {
44
51
  );
45
52
  }
46
53
 
47
- log.info(prior ? 'detected prior install' : 'no prior install detected');
54
+ log.info(
55
+ prior
56
+ ? 'detected prior auth (auth.json present) — sign-in will skip'
57
+ : 'no prior auth detected — sign-in will run'
58
+ );
48
59
  return { prior, findings };
49
60
  }
50
61
 
@@ -96,13 +96,63 @@ function isNpxInvocation() {
96
96
  return false;
97
97
  }
98
98
 
99
- // Where the platform binary lives once optionalDependencies installed it.
99
+ // Where the platform binary lives once optionalDependencies installed
100
+ // it. v3.0.1 fix: previously this returned the GLOBAL-install path
101
+ // (`$HOME/.local/bin/<name>` on Linux, `/usr/local/bin/<name>` on
102
+ // macOS, `$HOME/AppData/Local/skalpel/<name>.exe` on Windows). That
103
+ // path is only correct for `npm install -g skalpel`. For local
104
+ // installs (`npm install skalpel` from any non-package directory)
105
+ // the binary lives at `node_modules/@skalpelai/skalpel-<plat>/bin/
106
+ // <name>` — the optionalDependencies-resolved sub-package's bin/.
107
+ //
108
+ // The right resolution is the same one npm-bin/skalpel.js already
109
+ // uses for runtime dispatch: `require.resolve` against the platform
110
+ // sub-package's package.json, then join with `bin/<name>{,.exe}`.
111
+ // This works uniformly for global, local, and npx-cached installs
112
+ // because node's module resolver walks up node_modules trees the
113
+ // same way in all three contexts.
114
+ //
115
+ // We fall back to the legacy global-install paths if require.resolve
116
+ // fails, so behaviour on a real global install stays unchanged.
117
+ function platformPackageName() {
118
+ // Map process.platform-process.arch → @skalpelai/skalpel-<npm-plat>.
119
+ // Mirrors PLATFORM_PACKAGES in npm-bin/skalpel.js.
120
+ const key = `${process.platform}-${process.arch}`;
121
+ const map = {
122
+ 'darwin-arm64': '@skalpelai/skalpel-darwin-arm64',
123
+ 'darwin-x64': '@skalpelai/skalpel-darwin-x64',
124
+ 'linux-arm64': '@skalpelai/skalpel-linux-arm64',
125
+ 'linux-x64': '@skalpelai/skalpel-linux-x64',
126
+ 'win32-x64': '@skalpelai/skalpel-win32-x64',
127
+ };
128
+ return map[key] || null;
129
+ }
130
+
100
131
  function binPath(name) {
101
132
  if (isNpxInvocation()) {
102
133
  throw new Error(
103
134
  'Skalpel does not support `npx skalpel` — please run `npm i -g @skalpelai/skalpel` instead.'
104
135
  );
105
136
  }
137
+ const exe = process.platform === 'win32' ? `${name}.exe` : name;
138
+
139
+ // Preferred: resolve via the platform sub-package's package.json.
140
+ // Works for global, local, and any other npm-managed install context.
141
+ const pkgName = platformPackageName();
142
+ if (pkgName) {
143
+ try {
144
+ const pkgRoot = path.dirname(require.resolve(`${pkgName}/package.json`));
145
+ return path.join(pkgRoot, 'bin', exe);
146
+ } catch (_err) {
147
+ // fall through to legacy paths below
148
+ }
149
+ }
150
+
151
+ // Fallback: legacy hardcoded global-install paths. Reached only on
152
+ // a misinstall (no @skalpelai/skalpel-<plat> sub-package in the
153
+ // resolution tree) — service-register will then error with the
154
+ // legacy "binary missing at <path>" message which is at least a
155
+ // recognisable signature.
106
156
  const h = home();
107
157
  const npmPrefix = process.env.npm_config_prefix;
108
158
  validatePath(npmPrefix, 'npm_config_prefix');