claudekit-cli 3.41.4-dev.42 → 3.41.4-dev.43

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/dist/index.js +171 -26
  2. package/package.json +4 -2
package/dist/index.js CHANGED
@@ -60755,7 +60755,7 @@ var package_default;
60755
60755
  var init_package = __esm(() => {
60756
60756
  package_default = {
60757
60757
  name: "claudekit-cli",
60758
- version: "3.41.4-dev.42",
60758
+ version: "3.41.4-dev.43",
60759
60759
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
60760
60760
  type: "module",
60761
60761
  repository: {
@@ -60779,6 +60779,8 @@ var init_package = __esm(() => {
60779
60779
  "tauri:dev": "tauri dev",
60780
60780
  "tauri:build": "tauri build",
60781
60781
  "desktop:validate-config": "bun scripts/validate-desktop-bundle-config.ts",
60782
+ "desktop:sync-version": "bun scripts/sync-desktop-bundle-config.ts",
60783
+ "desktop:check-sync": "bun scripts/sync-desktop-bundle-config.ts --check",
60782
60784
  "desktop:validate-icons": "bun scripts/validate-desktop-icon-source.ts",
60783
60785
  "icons:regen": "./scripts/regen-desktop-icons.sh",
60784
60786
  dev: "bun run src/index.ts",
@@ -60803,7 +60805,7 @@ var init_package = __esm(() => {
60803
60805
  "dev:quick": "./scripts/dev-quick-start.sh",
60804
60806
  "dev:all": "./scripts/dev-quick-start.sh all",
60805
60807
  metrics: "bun run scripts/workflow-metrics.ts",
60806
- validate: "bun run typecheck && bun run lint && bun run desktop:validate-config && bun run desktop:validate-icons && bun test && bun run ui:test && bun run build",
60808
+ validate: "bun run typecheck && bun run lint && bun run desktop:check-sync && bun run desktop:validate-config && bun run desktop:validate-icons && bun test && bun run ui:test && bun run build",
60807
60809
  "install:hooks": "./.githooks/install.sh",
60808
60810
  prepare: `node -e "try{require('child_process').execSync('git rev-parse --git-dir',{stdio:'ignore'});require('child_process').execSync('bash .githooks/install.sh',{stdio:'inherit'})}catch(e){console.warn('[i] Hook install skipped:',e.message)}"`
60809
60811
  },
@@ -79262,6 +79264,20 @@ import { join as join66 } from "node:path";
79262
79264
  function escapeRegExp2(value) {
79263
79265
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
79264
79266
  }
79267
+ var MAC_BUNDLE_VERSION_PATTERN = /<key>\s*CFBundleShortVersionString\s*<\/key>\s*<string>([^<]+)<\/string>/;
79268
+ async function readInstalledDesktopArtifactVersion(binaryPath, options2 = {}) {
79269
+ const platform7 = options2.platform || process.platform;
79270
+ if (platform7 !== "darwin") {
79271
+ return null;
79272
+ }
79273
+ const readFileFn = options2.readFileFn || readFile37;
79274
+ try {
79275
+ const infoPlist = await readFileFn(join66(binaryPath, "Contents", "Info.plist"), "utf8");
79276
+ return infoPlist.match(MAC_BUNDLE_VERSION_PATTERN)?.[1] ?? null;
79277
+ } catch {
79278
+ return null;
79279
+ }
79280
+ }
79265
79281
  async function validateInstalledDesktopArtifact(binaryPath, metadata, options2 = {}) {
79266
79282
  const platform7 = options2.platform || process.platform;
79267
79283
  const readFileFn = options2.readFileFn || readFile37;
@@ -79272,9 +79288,15 @@ async function validateInstalledDesktopArtifact(binaryPath, metadata, options2 =
79272
79288
  return fileStat.isFile() && fileStat.size === metadata.assetSize;
79273
79289
  }
79274
79290
  if (platform7 === "darwin") {
79275
- const infoPlist = await readFileFn(join66(binaryPath, "Contents", "Info.plist"), "utf8");
79276
- const versionPattern = new RegExp(`<key>\\s*CFBundleShortVersionString\\s*</key>\\s*<string>${escapeRegExp2(metadata.version)}</string>`);
79277
- return versionPattern.test(infoPlist);
79291
+ const installedVersion = await readInstalledDesktopArtifactVersion(binaryPath, {
79292
+ platform: platform7,
79293
+ readFileFn
79294
+ });
79295
+ if (!installedVersion) {
79296
+ return false;
79297
+ }
79298
+ const versionPattern = new RegExp(`^${escapeRegExp2(metadata.version)}$`);
79299
+ return versionPattern.test(installedVersion);
79278
79300
  }
79279
79301
  } catch {
79280
79302
  return false;
@@ -79291,16 +79313,28 @@ import { promisify as promisify9 } from "node:util";
79291
79313
  init_logger();
79292
79314
  var import_fs_extra7 = __toESM(require_lib3(), 1);
79293
79315
  var execFileAsync5 = promisify9(execFile8);
79294
- async function persistInstallMetadataAfterSuccess(downloadPath, readDownloadedMetadataFn, persistInstallMetadataFn) {
79316
+ async function persistInstallMetadataAfterSuccess(downloadPath, targetPath, platform7, readDownloadedMetadataFn, validateInstalledArtifactFn, persistInstallMetadataFn) {
79317
+ const metadata = await readDownloadedMetadataFn(downloadPath);
79318
+ if (!metadata) {
79319
+ return;
79320
+ }
79321
+ const isValid2 = await validateInstalledArtifactFn(targetPath, metadata, { platform: platform7 });
79322
+ if (!isValid2) {
79323
+ throw new Error(`Installed desktop artifact at ${targetPath} failed validation for release ${metadata.version}`);
79324
+ }
79295
79325
  try {
79296
- const metadata = await readDownloadedMetadataFn(downloadPath);
79297
- if (metadata) {
79298
- await persistInstallMetadataFn(metadata);
79299
- }
79326
+ await persistInstallMetadataFn(metadata);
79300
79327
  } catch (error) {
79301
79328
  logger.warning(`Desktop install succeeded, but failed to persist install metadata: ${error instanceof Error ? error.message : String(error)}`);
79302
79329
  }
79303
79330
  }
79331
+ async function removeBackupInstallPathAfterSuccess(backupInstallPath) {
79332
+ try {
79333
+ await import_fs_extra7.remove(backupInstallPath);
79334
+ } catch (error) {
79335
+ logger.warning(`Desktop install succeeded, but failed to remove backup install at ${backupInstallPath}: ${error instanceof Error ? error.message : String(error)}`);
79336
+ }
79337
+ }
79304
79338
  async function findAppBundle(rootDir) {
79305
79339
  const entries = await readdir16(rootDir, { withFileTypes: true });
79306
79340
  for (const entry of entries) {
@@ -79320,6 +79354,7 @@ async function installDesktopBinary(downloadPath, options2 = {}) {
79320
79354
  const platform7 = options2.platform || process.platform;
79321
79355
  const targetPath = getDesktopInstallPath({ platform: platform7 });
79322
79356
  const readDownloadedMetadataFn = options2.readDownloadedMetadataFn || readDownloadedDesktopMetadata;
79357
+ const validateInstalledArtifactFn = options2.validateInstalledArtifactFn || validateInstalledDesktopArtifact;
79323
79358
  const persistInstallMetadataFn = options2.persistInstallMetadataFn || ((metadata) => writeDesktopInstallMetadata(metadata, { platform: platform7 }));
79324
79359
  await import_fs_extra7.ensureDir(getDesktopInstallDirectory({ platform: platform7 }));
79325
79360
  if (platform7 === "darwin") {
@@ -79333,6 +79368,7 @@ async function installDesktopBinary(downloadPath, options2 = {}) {
79333
79368
  const stagingDir = await mkdtemp(join67(tmpdir(), "ck-desktop-app-"));
79334
79369
  const stagedInstallPath = join67(dirname24(targetPath), `${basename19(targetPath)}.new`);
79335
79370
  const backupInstallPath = join67(dirname24(targetPath), `${basename19(targetPath)}.backup`);
79371
+ let swappedInstall = false;
79336
79372
  try {
79337
79373
  await extractZipFn(downloadPath, { dir: stagingDir });
79338
79374
  const appBundlePath = await findAppBundle(stagingDir);
@@ -79344,28 +79380,70 @@ async function installDesktopBinary(downloadPath, options2 = {}) {
79344
79380
  await rename7(targetPath, backupInstallPath);
79345
79381
  }
79346
79382
  await rename7(stagedInstallPath, targetPath);
79347
- await import_fs_extra7.remove(backupInstallPath);
79383
+ swappedInstall = true;
79384
+ await persistInstallMetadataAfterSuccess(downloadPath, targetPath, platform7, readDownloadedMetadataFn, validateInstalledArtifactFn, persistInstallMetadataFn);
79385
+ await removeBackupInstallPathAfterSuccess(backupInstallPath);
79348
79386
  } catch (error) {
79349
- if (await import_fs_extra7.pathExists(backupInstallPath) && !await import_fs_extra7.pathExists(targetPath)) {
79387
+ if (await import_fs_extra7.pathExists(backupInstallPath)) {
79388
+ if (await import_fs_extra7.pathExists(targetPath)) {
79389
+ await import_fs_extra7.remove(targetPath);
79390
+ }
79350
79391
  await rename7(backupInstallPath, targetPath);
79392
+ } else if (swappedInstall && await import_fs_extra7.pathExists(targetPath)) {
79393
+ await import_fs_extra7.remove(targetPath);
79351
79394
  }
79352
79395
  throw error;
79353
79396
  } finally {
79354
79397
  await import_fs_extra7.remove(stagingDir);
79355
79398
  await import_fs_extra7.remove(stagedInstallPath);
79356
79399
  }
79357
- await persistInstallMetadataAfterSuccess(downloadPath, readDownloadedMetadataFn, persistInstallMetadataFn);
79358
79400
  return targetPath;
79359
79401
  }
79360
79402
  if (platform7 === "linux") {
79361
- await import_fs_extra7.copyFile(downloadPath, targetPath);
79362
- await chmod3(targetPath, 493);
79363
- await persistInstallMetadataAfterSuccess(downloadPath, readDownloadedMetadataFn, persistInstallMetadataFn);
79403
+ const backupInstallPath = join67(dirname24(targetPath), `${basename19(targetPath)}.backup`);
79404
+ try {
79405
+ await import_fs_extra7.remove(backupInstallPath);
79406
+ if (await import_fs_extra7.pathExists(targetPath)) {
79407
+ await rename7(targetPath, backupInstallPath);
79408
+ }
79409
+ await import_fs_extra7.copyFile(downloadPath, targetPath);
79410
+ await chmod3(targetPath, 493);
79411
+ await persistInstallMetadataAfterSuccess(downloadPath, targetPath, platform7, readDownloadedMetadataFn, validateInstalledArtifactFn, persistInstallMetadataFn);
79412
+ await removeBackupInstallPathAfterSuccess(backupInstallPath);
79413
+ } catch (error) {
79414
+ if (await import_fs_extra7.pathExists(backupInstallPath)) {
79415
+ if (await import_fs_extra7.pathExists(targetPath)) {
79416
+ await import_fs_extra7.remove(targetPath);
79417
+ }
79418
+ await rename7(backupInstallPath, targetPath);
79419
+ } else if (await import_fs_extra7.pathExists(targetPath)) {
79420
+ await import_fs_extra7.remove(targetPath);
79421
+ }
79422
+ throw error;
79423
+ }
79364
79424
  return targetPath;
79365
79425
  }
79366
79426
  if (platform7 === "win32") {
79367
- await import_fs_extra7.copyFile(downloadPath, targetPath);
79368
- await persistInstallMetadataAfterSuccess(downloadPath, readDownloadedMetadataFn, persistInstallMetadataFn);
79427
+ const backupInstallPath = join67(dirname24(targetPath), `${basename19(targetPath)}.backup`);
79428
+ try {
79429
+ await import_fs_extra7.remove(backupInstallPath);
79430
+ if (await import_fs_extra7.pathExists(targetPath)) {
79431
+ await rename7(targetPath, backupInstallPath);
79432
+ }
79433
+ await import_fs_extra7.copyFile(downloadPath, targetPath);
79434
+ await persistInstallMetadataAfterSuccess(downloadPath, targetPath, platform7, readDownloadedMetadataFn, validateInstalledArtifactFn, persistInstallMetadataFn);
79435
+ await removeBackupInstallPathAfterSuccess(backupInstallPath);
79436
+ } catch (error) {
79437
+ if (await import_fs_extra7.pathExists(backupInstallPath)) {
79438
+ if (await import_fs_extra7.pathExists(targetPath)) {
79439
+ await import_fs_extra7.remove(targetPath);
79440
+ }
79441
+ await rename7(backupInstallPath, targetPath);
79442
+ } else if (await import_fs_extra7.pathExists(targetPath)) {
79443
+ await import_fs_extra7.remove(targetPath);
79444
+ }
79445
+ throw error;
79446
+ }
79369
79447
  return targetPath;
79370
79448
  }
79371
79449
  throw new Error(`Unsupported install platform: ${platform7}`);
@@ -79784,11 +79862,48 @@ async function resolveDesktopReleaseAsset(version, options2) {
79784
79862
  platformKey
79785
79863
  };
79786
79864
  }
79865
+ async function getDesktopInstallHealth(options2 = {}) {
79866
+ const readInstallMetadata = options2.readInstallMetadata || readDesktopInstallMetadata;
79867
+ const validateInstalledArtifact = options2.validateInstalledArtifact || validateInstalledDesktopArtifact;
79868
+ const readInstalledArtifactVersion = options2.readInstalledArtifactVersion || readInstalledDesktopArtifactVersion;
79869
+ const installed = await readInstallMetadata({ platform: options2.platform });
79870
+ if (!installed) {
79871
+ return {
79872
+ currentVersion: null,
79873
+ healthy: false,
79874
+ reason: "missing-metadata"
79875
+ };
79876
+ }
79877
+ const binaryPath = options2.binaryPath || getDesktopBinaryPath({ platform: options2.platform });
79878
+ if (!binaryPath) {
79879
+ return {
79880
+ currentVersion: installed.version,
79881
+ healthy: false,
79882
+ reason: "missing-binary"
79883
+ };
79884
+ }
79885
+ if (!await validateInstalledArtifact(binaryPath, installed, { platform: options2.platform })) {
79886
+ const installedArtifactVersion = await readInstalledArtifactVersion(binaryPath, {
79887
+ platform: options2.platform
79888
+ });
79889
+ return {
79890
+ currentVersion: installedArtifactVersion || installed.version,
79891
+ healthy: false,
79892
+ reason: "artifact-invalid"
79893
+ };
79894
+ }
79895
+ return {
79896
+ currentVersion: installed.version,
79897
+ healthy: true,
79898
+ reason: "healthy"
79899
+ };
79900
+ }
79787
79901
  async function getDesktopUpdateStatus(options2 = {}) {
79788
79902
  const { manifest, entry, platformKey } = await resolveDesktopReleaseAsset(undefined, options2);
79789
79903
  const latestVersion = normalizeVersion(manifest.version);
79790
79904
  const readInstallMetadata = options2.readInstallMetadata || readDesktopInstallMetadata;
79791
79905
  const validateInstalledArtifact = options2.validateInstalledArtifact || validateInstalledDesktopArtifact;
79906
+ const readInstalledArtifactVersion = options2.readInstalledArtifactVersion || readInstalledDesktopArtifactVersion;
79792
79907
  const installed = await readInstallMetadata({ platform: options2.platform });
79793
79908
  const requestedChannel = options2.channel ?? "stable";
79794
79909
  if (!installed) {
@@ -79805,19 +79920,32 @@ async function getDesktopUpdateStatus(options2 = {}) {
79805
79920
  currentVersion: installed.version,
79806
79921
  latestVersion,
79807
79922
  updateAvailable: true,
79808
- reason: "unknown-installed-version"
79923
+ reason: "missing-binary"
79809
79924
  };
79810
79925
  }
79926
+ const sameChannel = installed.channel === requestedChannel;
79927
+ const samePlatform = installed.platformKey === platformKey;
79811
79928
  if (!await validateInstalledArtifact(binaryPath, installed, { platform: options2.platform })) {
79929
+ const installedArtifactVersion = await readInstalledArtifactVersion(binaryPath, {
79930
+ platform: options2.platform
79931
+ });
79932
+ const currentVersion = installedArtifactVersion || installed.version;
79933
+ const installedArtifactIsNewer = sameChannel && samePlatform && isNewerVersion(latestVersion, currentVersion);
79934
+ if (installedArtifactIsNewer) {
79935
+ return {
79936
+ currentVersion,
79937
+ latestVersion,
79938
+ updateAvailable: false,
79939
+ reason: "installed-newer"
79940
+ };
79941
+ }
79812
79942
  return {
79813
- currentVersion: installed.version,
79943
+ currentVersion,
79814
79944
  latestVersion,
79815
79945
  updateAvailable: true,
79816
79946
  reason: "update-available"
79817
79947
  };
79818
79948
  }
79819
- const sameChannel = installed.channel === requestedChannel;
79820
- const samePlatform = installed.platformKey === platformKey;
79821
79949
  const metadataMatchesRelease = sameChannel && installed.platformKey === platformKey && installed.assetName === entry.name && installed.assetSize === entry.size && installed.manifestDate === manifest.date;
79822
79950
  const installedIsNewer = sameChannel && samePlatform && isNewerVersion(latestVersion, installed.version);
79823
79951
  if (installedIsNewer) {
@@ -79910,6 +80038,7 @@ async function appCommand(options2 = {}, deps = {}) {
79910
80038
  const launchWeb = deps.launchWeb || configUICommand;
79911
80039
  const getBinaryPath = deps.getBinaryPath || getDesktopBinaryPath;
79912
80040
  const getInstallPath2 = deps.getInstallPath || getDesktopInstallPath;
80041
+ const getInstallHealth = deps.getInstallHealth || getDesktopInstallHealth;
79913
80042
  const getUpdateStatus = deps.getUpdateStatus || getDesktopUpdateStatus;
79914
80043
  const downloadBinary = deps.downloadBinary || ((opts) => downloadDesktopBinary(undefined, { channel: opts?.channel }));
79915
80044
  const installBinary = deps.installBinary || installDesktopBinary;
@@ -79918,6 +80047,7 @@ async function appCommand(options2 = {}, deps = {}) {
79918
80047
  const info = deps.info || output.info.bind(output);
79919
80048
  const success = deps.success || output.success.bind(output);
79920
80049
  const printLine = deps.printLine || console.log;
80050
+ let repairingInstall = false;
79921
80051
  if (options2.web) {
79922
80052
  info("Opening ClaudeKit web dashboard...");
79923
80053
  await launchWeb();
@@ -79942,9 +80072,20 @@ async function appCommand(options2 = {}, deps = {}) {
79942
80072
  }
79943
80073
  const existingBinary = getBinaryPath();
79944
80074
  if (existingBinary && !options2.update) {
79945
- success("Launching ClaudeKit Control Center...");
79946
- launchBinary(existingBinary);
79947
- return;
80075
+ try {
80076
+ const installHealth = await getInstallHealth({ binaryPath: existingBinary });
80077
+ if (installHealth.healthy || installHealth.reason === "missing-metadata") {
80078
+ success("Launching ClaudeKit Control Center...");
80079
+ launchBinary(existingBinary);
80080
+ return;
80081
+ }
80082
+ info(installHealth.currentVersion ? `Installed ClaudeKit Control Center build (${installHealth.currentVersion}) needs repair. Downloading the latest build...` : "Installed ClaudeKit Control Center needs repair. Downloading the latest build...");
80083
+ repairingInstall = true;
80084
+ } catch {
80085
+ success("Launching ClaudeKit Control Center...");
80086
+ launchBinary(existingBinary);
80087
+ return;
80088
+ }
79948
80089
  }
79949
80090
  if (options2.update && existingBinary) {
79950
80091
  const updateStatus = await getUpdateStatus({ channel, binaryPath: existingBinary });
@@ -79955,7 +80096,11 @@ async function appCommand(options2 = {}, deps = {}) {
79955
80096
  return;
79956
80097
  }
79957
80098
  }
79958
- info(options2.update ? "Downloading and installing the latest ClaudeKit Control Center build..." : "ClaudeKit Control Center not found. Downloading...");
80099
+ if (options2.update) {
80100
+ info("Downloading and installing the latest ClaudeKit Control Center build...");
80101
+ } else if (!repairingInstall) {
80102
+ info("ClaudeKit Control Center not found. Downloading...");
80103
+ }
79959
80104
  const downloadedBinary = await downloadBinary({ channel });
79960
80105
  const installedBinary = await installBinary(downloadedBinary);
79961
80106
  success(`Installed ClaudeKit Control Center to ${installedBinary}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "3.41.4-dev.42",
3
+ "version": "3.41.4-dev.43",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {
@@ -24,6 +24,8 @@
24
24
  "tauri:dev": "tauri dev",
25
25
  "tauri:build": "tauri build",
26
26
  "desktop:validate-config": "bun scripts/validate-desktop-bundle-config.ts",
27
+ "desktop:sync-version": "bun scripts/sync-desktop-bundle-config.ts",
28
+ "desktop:check-sync": "bun scripts/sync-desktop-bundle-config.ts --check",
27
29
  "desktop:validate-icons": "bun scripts/validate-desktop-icon-source.ts",
28
30
  "icons:regen": "./scripts/regen-desktop-icons.sh",
29
31
  "dev": "bun run src/index.ts",
@@ -48,7 +50,7 @@
48
50
  "dev:quick": "./scripts/dev-quick-start.sh",
49
51
  "dev:all": "./scripts/dev-quick-start.sh all",
50
52
  "metrics": "bun run scripts/workflow-metrics.ts",
51
- "validate": "bun run typecheck && bun run lint && bun run desktop:validate-config && bun run desktop:validate-icons && bun test && bun run ui:test && bun run build",
53
+ "validate": "bun run typecheck && bun run lint && bun run desktop:check-sync && bun run desktop:validate-config && bun run desktop:validate-icons && bun test && bun run ui:test && bun run build",
52
54
  "install:hooks": "./.githooks/install.sh",
53
55
  "prepare": "node -e \"try{require('child_process').execSync('git rev-parse --git-dir',{stdio:'ignore'});require('child_process').execSync('bash .githooks/install.sh',{stdio:'inherit'})}catch(e){console.warn('[i] Hook install skipped:',e.message)}\""
54
56
  },