centralu 0.1.0-beta.3 → 0.1.0-beta.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.
package/bin/centralu.mjs CHANGED
@@ -16,7 +16,7 @@
16
16
  * instead of being rediscovered in every function.
17
17
  */
18
18
  import { execFileSync, spawn } from 'node:child_process'
19
- import { isNewer } from './semver.mjs'
19
+ import { copyDiffers, isNewer } from './semver.mjs'
20
20
  import { chmodSync, existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
21
21
  import { createRequire } from 'node:module'
22
22
  import { homedir } from 'node:os'
@@ -238,6 +238,8 @@ async function update() {
238
238
  const latest = res.version
239
239
  if (!isNewer(latest, pkg.version)) {
240
240
  console.log(`이미 최신입니다 (${pkg.version}).`)
241
+ // 패키지가 최신이어도 사본은 아닐 수 있다 — `npm i -g`로 올린 사람이 정확히 그 상태다
242
+ notifyIfCopyStale()
241
243
  return
242
244
  }
243
245
  console.log(`${pkg.version} → ${latest} 로 올립니다.`)
@@ -253,6 +255,43 @@ async function update() {
253
255
  console.log('끝났습니다. 앱이 떠 있다면 다시 시작해 주세요.')
254
256
  }
255
257
 
258
+ /**
259
+ * /Applications에 넣어둔 사본의 버전. 견줄 것이 없으면 null.
260
+ *
261
+ * `defaults`를 쓰는 이유는 Info.plist가 XML이라는 보장이 없어서다 — 오늘 Tauri가 XML로
262
+ * 쓰고 있을 뿐이고, 바이너리 plist로 바뀌면 정규식으로 읽던 쪽이 조용히 못 읽게 된다.
263
+ * `defaults`는 맥에 항상 있고 둘 다 읽는다.
264
+ */
265
+ function installedCopyVersion() {
266
+ if (process.platform !== 'darwin' || !existsSync(INSTALLED)) return null
267
+ try {
268
+ const out = execFileSync('/usr/bin/defaults', ['read', join(INSTALLED, 'Contents/Info.plist'), 'CFBundleShortVersionString'], {
269
+ encoding: 'utf8',
270
+ stdio: ['ignore', 'pipe', 'ignore'],
271
+ })
272
+ return out.trim()
273
+ } catch {
274
+ // 못 읽는 것은 말할 거리가 아니다 — 앱은 이미 떴고, 여기서 할 수 있는 말이 없다
275
+ return null
276
+ }
277
+ }
278
+
279
+ /**
280
+ * 패키지와 /Applications 사본이 어긋났으면 한 줄.
281
+ *
282
+ * `npm i -g centralu`는 패키지만 바꾼다. 그래서 런처는 새것인데 Spotlight에서 열리는 앱은
283
+ * 옛것인 상태가 조용히 만들어진다 — 실제로 그렇게 됐고(beta.1이 /Applications에 남아 있는
284
+ * 채로 패키지만 beta.3), 화면 어디에도 그 사실이 없었다.
285
+ *
286
+ * **말하되 대신 하지 않는다.** 앱 안의 업데이트 줄과 같은 규칙이다. 묻지도 않고 남의
287
+ * /Applications를 덮는 일은 이 패키지가 `install`을 따로 둔 이유 그 자체다.
288
+ */
289
+ function notifyIfCopyStale() {
290
+ const version = installedCopyVersion()
291
+ if (!copyDiffers(pkg.version, version)) return
292
+ console.log(`\n/Applications 사본은 ${version}입니다 (이 패키지는 ${pkg.version}).\n centralu install`)
293
+ }
294
+
256
295
  /** 앱을 띄운 뒤에만 알린다 — 업데이트 확인 때문에 실행이 늦어지면 안 된다 */
257
296
  async function notifyIfOutdated() {
258
297
  // 여기서는 실패를 **전부 삼킨다.** 앱을 띄우러 온 사람에게 레지스트리 사정을 말할 이유가 없다
@@ -294,5 +333,6 @@ switch (cmd) {
294
333
  break
295
334
  default:
296
335
  run(cmd ? [cmd, ...rest] : [])
336
+ notifyIfCopyStale()
297
337
  await notifyIfOutdated()
298
338
  }
package/bin/semver.mjs CHANGED
@@ -49,6 +49,25 @@ export function isNewer(a, b) {
49
49
  return false
50
50
  }
51
51
 
52
+ /**
53
+ * Whether the copy in /Applications is a different app than the one this package carries.
54
+ *
55
+ * `npm i -g centralu` replaces the package and nothing else. That is deliberate — this
56
+ * project does not write into someone's /Applications unless asked (`centralu install`) —
57
+ * but the cost is that the two drift apart in silence, and the copy is the one people
58
+ * actually click. So the launcher says so. It still does not act.
59
+ *
60
+ * Not `isNewer`. Drift in either direction is worth a line: a copy *newer* than the package
61
+ * means an update went backwards, which is more surprising, not less.
62
+ *
63
+ * Lives beside `isNewer` because it ships and is tested for the same reason — a launcher
64
+ * already on someone's machine cannot be corrected later. 0.1.0-beta.1 is still out there
65
+ * answering "이미 최신입니다" and always will be.
66
+ */
67
+ export function copyDiffers(pkgVersion, copyVersion) {
68
+ return typeof copyVersion === 'string' && copyVersion !== '' && copyVersion !== pkgVersion
69
+ }
70
+
52
71
  function split(v) {
53
72
  const dash = v.indexOf('-')
54
73
  const core = (dash === -1 ? v : v.slice(0, dash)).split('.').map(Number)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "centralu",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0-beta.5",
4
4
  "description": "Run, watch and steer several Claude Code and Codex CLI sessions from one window",
5
5
  "license": "MIT",
6
6
  "author": "ijun17",
@@ -35,8 +35,8 @@
35
35
  "linux"
36
36
  ],
37
37
  "optionalDependencies": {
38
- "centralu-darwin-arm64": "0.1.0-beta.3",
39
- "centralu-linux-arm64": "0.1.0-beta.3",
40
- "centralu-linux-x64": "0.1.0-beta.3"
38
+ "centralu-darwin-arm64": "0.1.0-beta.5",
39
+ "centralu-linux-arm64": "0.1.0-beta.5",
40
+ "centralu-linux-x64": "0.1.0-beta.5"
41
41
  }
42
42
  }