@lifeaitools/clauth 1.7.2 → 1.8.0
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/README.md +16 -14
- package/cli/commands/npm.js +59 -0
- package/cli/index.js +22 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -200,32 +200,34 @@ Tests actual MCP tool calls (not just OAuth + listing).
|
|
|
200
200
|
|
|
201
201
|
## Releasing a New Version (maintainers)
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
Publishing is **manual** — there is no auto-publish. The GitHub Actions
|
|
204
|
+
`publish.yml` workflow was removed on 2026-04-27 (commit `08b7751`); trusted
|
|
205
|
+
publishing via OIDC was tried first (`b2bf08b`→`41d2d92`) and dropped. clauth is
|
|
206
|
+
a **private** repo and GitHub Actions bill for minutes on private repos, so we
|
|
207
|
+
don't run them here (reserve Actions for *public* repos, where they're free).
|
|
206
208
|
|
|
207
209
|
```bash
|
|
208
210
|
# 1. Bump version in package.json
|
|
209
|
-
# 2. Commit + tag + push
|
|
211
|
+
# 2. Commit + tag + push
|
|
210
212
|
git add -A && git commit -m "feat(...): description (vX.Y.Z)"
|
|
211
213
|
git tag vX.Y.Z
|
|
212
214
|
git push && git push --tags
|
|
213
215
|
|
|
214
|
-
# 3.
|
|
216
|
+
# 3. Publish manually with the vault npm token
|
|
217
|
+
clauth npm set-local # writes ~/.npmrc auth from the vault 'npm' service
|
|
218
|
+
npm publish --access public
|
|
219
|
+
|
|
220
|
+
# 4. Verify on the registry (direct check — bypasses npm's local cache)
|
|
215
221
|
curl -s https://registry.npmjs.org/@lifeaitools/clauth \
|
|
216
222
|
| python -c "import sys,json;print(json.load(sys.stdin)['dist-tags'])"
|
|
217
223
|
|
|
218
|
-
#
|
|
219
|
-
#
|
|
220
|
-
clauth npm set-local # writes ~/.npmrc auth from the vault 'npm' service
|
|
221
|
-
npm publish --access public
|
|
224
|
+
# 5. Update the running daemon
|
|
225
|
+
curl -s -X POST http://127.0.0.1:52437/restart # picks up new code, stays unlocked
|
|
222
226
|
```
|
|
223
227
|
|
|
224
|
-
**
|
|
225
|
-
|
|
226
|
-
npm
|
|
227
|
-
value) — re-sync the receiver's token to the current vault `npm` service, then
|
|
228
|
-
use the manual publish above to unblock the release.
|
|
228
|
+
**The tag push does NOT publish anything** — you must run step 3. A version bump
|
|
229
|
+
that is committed+tagged but never `npm publish`ed leaves the registry stale
|
|
230
|
+
(symptom: `npm view` still shows the old version after a push).
|
|
229
231
|
|
|
230
232
|
---
|
|
231
233
|
|
package/cli/commands/npm.js
CHANGED
|
@@ -121,3 +121,62 @@ export async function runNpm(action = "help", opts = {}) {
|
|
|
121
121
|
usage();
|
|
122
122
|
throw new Error(`unknown clauth npm action: ${action}`);
|
|
123
123
|
}
|
|
124
|
+
|
|
125
|
+
// ── Guarded publish ──────────────────────────────────────────────────────────
|
|
126
|
+
// Generic, package-agnostic publish that REFUSES to publish unless the package
|
|
127
|
+
// is already committed and pushed to GitHub — so the npm tarball can never be
|
|
128
|
+
// built from uncommitted "dev" code that isn't on the repo. Works for standalone
|
|
129
|
+
// repos and monorepo subpackages (clean-check is scoped to the package's dir).
|
|
130
|
+
export async function runPublish(target, opts = {}) {
|
|
131
|
+
const pkgDir = path.resolve(target || process.cwd());
|
|
132
|
+
const pkgJsonPath = path.join(pkgDir, "package.json");
|
|
133
|
+
if (!fs.existsSync(pkgJsonPath)) throw new Error(`No package.json found at ${pkgDir}`);
|
|
134
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
|
|
135
|
+
if (!pkg.name || !pkg.version) throw new Error(`package.json at ${pkgDir} is missing name or version`);
|
|
136
|
+
if (pkg.private === true) throw new Error(`${pkg.name} is marked "private": true — refusing to publish`);
|
|
137
|
+
const access = opts.access || pkg.publishConfig?.access || (pkg.name.startsWith("@") ? "public" : undefined);
|
|
138
|
+
|
|
139
|
+
const gitRoot = run("git", ["rev-parse", "--show-toplevel"], { cwd: pkgDir }).stdout?.trim();
|
|
140
|
+
if (!gitRoot) throw new Error(`${pkgDir} is not inside a git repository`);
|
|
141
|
+
const rel = path.relative(gitRoot, pkgDir) || ".";
|
|
142
|
+
|
|
143
|
+
// Guard 1 — nothing uncommitted in the package (else we'd pack dev code).
|
|
144
|
+
const dirty = run("git", ["status", "--porcelain", "--", rel], { cwd: gitRoot }).stdout?.trim();
|
|
145
|
+
if (dirty && !opts.allowDirty) {
|
|
146
|
+
throw new Error(`Refusing to publish ${pkg.name}@${pkg.version}: uncommitted changes in ${rel} would be packed but are NOT on GitHub:\n${dirty}\n\nCommit + push first (or pass --allow-dirty to override).`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Guard 2 — HEAD is on a remote (actually pushed to GitHub).
|
|
150
|
+
const head = run("git", ["rev-parse", "HEAD"], { cwd: gitRoot }).stdout?.trim();
|
|
151
|
+
const onRemote = run("git", ["branch", "-r", "--contains", head], { cwd: gitRoot }).stdout?.trim();
|
|
152
|
+
if (!onRemote && !opts.allowUnpushed) {
|
|
153
|
+
throw new Error(`Refusing to publish ${pkg.name}@${pkg.version}: HEAD ${head.slice(0, 9)} is not on any remote branch — push to GitHub first (or pass --allow-unpushed to override).`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Traceability — warn (don't block) if there's no v<version> tag at HEAD.
|
|
157
|
+
const tags = (run("git", ["tag", "--points-at", "HEAD"], { cwd: gitRoot }).stdout || "").split("\n").map((s) => s.trim()).filter(Boolean);
|
|
158
|
+
if (!tags.includes(`v${pkg.version}`)) {
|
|
159
|
+
console.log(`⚠ No git tag v${pkg.version} at HEAD (traceability only). Tags here: ${tags.join(", ") || "none"}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`${opts.dryRun ? "[dry-run] " : ""}Publishing ${pkg.name}@${pkg.version} from ${rel} @ ${head.slice(0, 9)} (pushed${access ? `, access=${access}` : ""})`);
|
|
163
|
+
|
|
164
|
+
const token = await fetchNpmToken();
|
|
165
|
+
const args = ["publish"];
|
|
166
|
+
if (access) args.push("--access", access);
|
|
167
|
+
if (opts.dryRun) args.push("--dry-run");
|
|
168
|
+
const result = withNpmAuth(token, (npmrc) => run("npm", ["--userconfig", npmrc, ...args], { cwd: pkgDir }));
|
|
169
|
+
printResult(result, { redact: true });
|
|
170
|
+
if (result.status !== 0) { process.exitCode = result.status; throw new Error(`npm publish failed for ${pkg.name}`); }
|
|
171
|
+
if (opts.dryRun) { console.log("Dry run complete — nothing published."); return; }
|
|
172
|
+
|
|
173
|
+
// Verify on the registry directly (bypasses npm's local cache lag).
|
|
174
|
+
try {
|
|
175
|
+
const reg = await fetch(`https://registry.npmjs.org/${pkg.name}`);
|
|
176
|
+
const meta = await reg.json();
|
|
177
|
+
if (meta.versions?.[pkg.version]) console.log(`✓ Verified ${pkg.name}@${pkg.version} on the registry (latest: ${meta["dist-tags"]?.latest}).`);
|
|
178
|
+
else console.log(`⚠ ${pkg.name}@${pkg.version} not visible on the registry yet (propagation lag) — re-check shortly.`);
|
|
179
|
+
} catch (e) {
|
|
180
|
+
console.log(`(could not verify on registry: ${e.message})`);
|
|
181
|
+
}
|
|
182
|
+
}
|
package/cli/index.js
CHANGED
|
@@ -150,7 +150,7 @@ import { runUninstall } from './commands/uninstall.js';
|
|
|
150
150
|
import { runScrub } from './commands/scrub.js';
|
|
151
151
|
import { runServe } from './commands/serve.js';
|
|
152
152
|
import { runCodevelop } from './commands/codevelop.js';
|
|
153
|
-
import { runNpm } from './commands/npm.js';
|
|
153
|
+
import { runNpm, runPublish } from './commands/npm.js';
|
|
154
154
|
|
|
155
155
|
program
|
|
156
156
|
.command('install')
|
|
@@ -229,6 +229,27 @@ program
|
|
|
229
229
|
await runNpm(action, { ...opts, args });
|
|
230
230
|
});
|
|
231
231
|
|
|
232
|
+
// ──────────────────────────────────────────────
|
|
233
|
+
// clauth publish [target]
|
|
234
|
+
// Guarded npm publish for ANY package — refuses to ship code that isn't
|
|
235
|
+
// committed AND pushed to GitHub (prevents npm/repo divergence from dev builds).
|
|
236
|
+
// ──────────────────────────────────────────────
|
|
237
|
+
program
|
|
238
|
+
.command("publish [target]")
|
|
239
|
+
.description("Safely publish an npm package (default: cwd). Refuses unless committed + pushed to GitHub.")
|
|
240
|
+
.option("--dry-run", "Run all guards and pack, but do not publish")
|
|
241
|
+
.option("--access <access>", "npm access: public | restricted")
|
|
242
|
+
.option("--allow-dirty", "Override the uncommitted-changes guard (NOT recommended)")
|
|
243
|
+
.option("--allow-unpushed", "Override the not-pushed-to-remote guard (NOT recommended)")
|
|
244
|
+
.action(async (target, opts) => {
|
|
245
|
+
try {
|
|
246
|
+
await runPublish(target, opts);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
console.error(chalk.red(err.message));
|
|
249
|
+
process.exitCode = 1;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
232
253
|
// ──────────────────────────────────────────────
|
|
233
254
|
// clauth setup
|
|
234
255
|
// ──────────────────────────────────────────────
|