diff --git a/.gitignore b/.gitignore index eadb76bf..1701fb4f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ node_modules oclif.manifest.json .env .codify-files +.npm-readme-backup.md diff --git a/.npmrc b/.npmrc index 9b4b2b0d..7253a5ce 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1 @@ -min-release-age=10080 \ No newline at end of file +min-release-age=7 diff --git a/README.md b/README.md index a3c6d044..c78b722c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +> ⚠️ **This package has been migrated and re-purposed.** The original package by [Andrew Stone](https://www.npmjs.com/~andrewjstone) +> for transforming integers to base36 strings. To continue to use that functionality, please pin to version 0.3.0 or below. + # Codify - Your Development Environment as Code **Stop manually setting up your development environment. Define it once, replicate it everywhere.** diff --git a/README.npm.md b/README.npm.md new file mode 100644 index 00000000..041825b8 --- /dev/null +++ b/README.npm.md @@ -0,0 +1,2 @@ +> ⚠️ **This package has been migrated and re-purposed.** The original package by [Andrew Stone](https://www.npmjs.com/~andrewjstone) +> for transforming integers to base36 strings. To continue to use that functionality, please pin to version 0.3.0 or below. diff --git a/package.json b/package.json index 8eb20865..748a3d46 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,10 @@ "start:dev": "./bin/dev.js", "start:vm": "npm run build && npm run pack:macos && npm run start:vm", "deploy": "npm run pkg && npm run notarize && npm run upload", - "prepublishOnly": "npm run build" + "build:npm": "tsx ./scripts/build-npm.ts", + "restore-npm": "tsx ./scripts/restore-npm.ts", + "prepublishOnly": "npm run build && npm run build:npm", + "postpublish": "npm run restore-npm" }, "version": "1.2.2", "bugs": "https://github.com/codifycli/codify/issues", diff --git a/scripts/build-npm.ts b/scripts/build-npm.ts new file mode 100644 index 00000000..001c1e16 --- /dev/null +++ b/scripts/build-npm.ts @@ -0,0 +1,62 @@ +// npm-publish prep. Runs from `prepublishOnly` (after `npm run build`). +// +// Two npm-specific concerns the plain `tsc` build doesn't handle: +// +// 1. dist/patch-ink.mjs — the postinstall hook runs `node dist/patch-ink.mjs` +// to patch node_modules/ink at the user's install time (needed for the raw-PTY +// handoff, e.g. `gh auth login` — see scripts/patch-ink.ts). `tsc -b` only +// compiles src/**/*, so this file is otherwise absent from the npm tarball and +// the postinstall guard silently skips it. Compile it here (same command as +// scripts/pkg.ts uses for the binary build). +// +// 2. README.md — npm should show a "migrated and re-purposed" blurb that the +// git-tracked README must NOT carry. README.npm.md holds ONLY the blurb; we +// back up the real README and prepend the blurb to it (so the body is never +// duplicated). `npm run restore-npm` (via postpublish, and defensively on +// failure) restores it from the backup. +// +// The backup file (README.md.orig) is the single source of truth for restoration, +// so a failed publish never leaves README.md dirty. + +import chalk from 'chalk' +import { execSync } from 'node:child_process' +import { existsSync } from 'node:fs' +import fs from 'node:fs/promises' + +const README = 'README.md' +const README_BLURB = 'README.npm.md' +// The transient backup is intentionally NOT named README* and is a dotfile — npm +// force-includes any README* file into the tarball regardless of the `files` +// allowlist, and this backup is just build scratch that shouldn't ship. +const README_BACKUP = '.npm-readme-backup.md' + +// ── 1. Compile patch-ink.ts → dist/patch-ink.mjs ───────────────────────────── +console.log(chalk.magenta('Compiling patch-ink.ts to dist/patch-ink.mjs')) +execSync( + 'tsc --module nodenext --moduleResolution nodenext --target es2022 --outDir dist scripts/patch-ink.ts', + { shell: 'zsh' }, +) +await fs.rename('dist/patch-ink.js', 'dist/patch-ink.mjs') + +// ── 2. Prepend the npm blurb to README.md ──────────────────────────────────── +// README.npm.md holds ONLY the blurb; we prepend it to the real README so the +// body never has to be duplicated/kept in sync. `npm run restore-npm` puts the +// original README.md back from the backup. +if (!existsSync(README_BLURB)) { + console.error(chalk.red(`ERROR: ${README_BLURB} not found. Cannot build npm README.`)) + process.exit(1) +} + +// If a stale backup exists (previous publish aborted before restore), the real +// README was already saved there — use it as the base so we don't prepend twice. +if (!existsSync(README_BACKUP)) { + console.log(chalk.magenta(`Backing up ${README} → ${README_BACKUP}`)) + await fs.copyFile(README, README_BACKUP) +} else { + console.log(chalk.yellow(`${README_BACKUP} already exists (stale?); using it as the base`)) +} + +console.log(chalk.magenta(`Prepending ${README_BLURB} to ${README}`)) +const blurb = await fs.readFile(README_BLURB, 'utf8') +const body = await fs.readFile(README_BACKUP, 'utf8') +await fs.writeFile(README, `${blurb.trimEnd()}\n\n${body}`, 'utf8') diff --git a/scripts/patch-ink.ts b/scripts/patch-ink.ts index fb357ae5..15844464 100644 --- a/scripts/patch-ink.ts +++ b/scripts/patch-ink.ts @@ -16,10 +16,35 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import { createRequire } from 'node:module'; import { existsSync } from 'node:fs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const INK_DIR = path.join(__dirname, '../node_modules/ink/build'); + +// Locate ink's install dir via Node module resolution rather than a fixed +// relative path. Under an npm install, ink is hoisted to the top-level +// node_modules (not nested under codify), so `../node_modules/ink` misses it and +// the patch silently skips. Resolve ink's entry point (ink/package.json can't be +// resolved directly — ink's "exports" map doesn't expose it) and walk up to the +// package root, which finds ink wherever npm/pnpm placed it (hoisted, nested, or +// symlinked). Falls back to the old relative path (used by the self-contained +// binary build) if resolution somehow fails. +function resolveInkDir(): string { + try { + const require = createRequire(import.meta.url); + let dir = path.dirname(require.resolve('ink')); // .../ink/build/index.js → .../ink/build + while (dir !== path.dirname(dir)) { + if (existsSync(path.join(dir, 'package.json'))) return path.join(dir, 'build'); + dir = path.dirname(dir); + } + } catch { + // fall through to the relative-path default below + } + + return path.join(__dirname, '../node_modules/ink/build'); +} + +const INK_DIR = resolveInkDir(); const APP_JS = path.join(INK_DIR, 'components/App.js'); const INK_JS = path.join(INK_DIR, 'ink.js'); const RENDER_JS = path.join(INK_DIR, 'render.js'); diff --git a/scripts/restore-npm.ts b/scripts/restore-npm.ts new file mode 100644 index 00000000..d2d60df5 --- /dev/null +++ b/scripts/restore-npm.ts @@ -0,0 +1,21 @@ +// Restores the git-tracked README.md after an npm publish. +// +// scripts/build-npm.ts swaps in README.npm.md and saves the real README to +// README.md.orig. This restores it from that backup and removes the backup. +// Runs via `postpublish` (success) and can be run manually if a publish aborts. +// Idempotent: a no-op if there's no backup to restore. + +import chalk from 'chalk' +import { existsSync } from 'node:fs' +import fs from 'node:fs/promises' + +const README = 'README.md' +const README_BACKUP = '.npm-readme-backup.md' + +if (existsSync(README_BACKUP)) { + console.log(chalk.magenta(`Restoring ${README} from ${README_BACKUP}`)) + await fs.copyFile(README_BACKUP, README) + await fs.rm(README_BACKUP) +} else { + console.log(chalk.gray(`No ${README_BACKUP} to restore; nothing to do.`)) +} diff --git a/src/api/dashboard/index.ts b/src/api/dashboard/index.ts index 3c9cc787..5cf99b01 100644 --- a/src/api/dashboard/index.ts +++ b/src/api/dashboard/index.ts @@ -10,8 +10,9 @@ export const DashboardApiClient = { throw new Error('Not logged in'); } + // The api worker owns the Yjs decode and returns { id, contents }. const res = await fetch( - `${config.dashboardUrl}/api/v1/documents/${id}`, + `${config.apiUrl}/v1/documents/${id}/contents`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'authorization': `Bearer ${login.accessToken}` } @@ -24,7 +25,7 @@ export const DashboardApiClient = { } const json = await res.json(); - return json.defaultDocumentId; + return json as CloudDocument; }, async getDefaultDocumentId(): Promise { diff --git a/src/config.ts b/src/config.ts index 2bbbb09a..7b674c83 100644 --- a/src/config.ts +++ b/src/config.ts @@ -11,6 +11,7 @@ export const config = { ], dashboardUrl: 'https://dashboard.codifycli.com', + apiUrl: 'https://api.codifycli.com', supabaseUrl: 'https://kdctbvqvqjfquplxhqrm.supabase.co', isBeta: VERSION.includes('beta'),