@aipm-registry/cli 0.3.1 → 0.3.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/README.md +8 -0
- package/dist/bin.cjs +349 -77
- package/dist/bin.cjs.map +3 -3
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -4160,7 +4160,19 @@ function expandTargets(targets) {
|
|
|
4160
4160
|
return [...ALL_TOOLS];
|
|
4161
4161
|
return targets.filter((t) => t !== "*");
|
|
4162
4162
|
}
|
|
4163
|
-
|
|
4163
|
+
function isSafeRelativePath(value) {
|
|
4164
|
+
const trimmed = value.trim();
|
|
4165
|
+
if (!trimmed || trimmed.startsWith("/") || trimmed.startsWith("\\") || /^[A-Za-z]:/.test(trimmed)) {
|
|
4166
|
+
return false;
|
|
4167
|
+
}
|
|
4168
|
+
const normalized = trimmed.replace(/\\/g, "/");
|
|
4169
|
+
return normalized.split("/").filter(Boolean).every((segment) => segment !== "." && segment !== "..");
|
|
4170
|
+
}
|
|
4171
|
+
function basename(value) {
|
|
4172
|
+
const parts = value.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
4173
|
+
return parts[parts.length - 1] ?? value;
|
|
4174
|
+
}
|
|
4175
|
+
var AiToolSchema, ALL_TOOLS, PackageExampleSchema, unsafePathMessage, InstallPathSchema, InstallOverwriteSchema, PackageInstallSchema, PackageManifestSchema;
|
|
4164
4176
|
var init_manifest = __esm({
|
|
4165
4177
|
"../../packages/schemas/dist/manifest.js"() {
|
|
4166
4178
|
"use strict";
|
|
@@ -4173,6 +4185,36 @@ var init_manifest = __esm({
|
|
|
4173
4185
|
description: external_exports.string().trim().min(1).max(240).optional(),
|
|
4174
4186
|
prompt: external_exports.string().trim().min(1).max(1e3)
|
|
4175
4187
|
});
|
|
4188
|
+
unsafePathMessage = "path must be relative and must not contain .. segments";
|
|
4189
|
+
InstallPathSchema = external_exports.string().trim().min(1).refine(isSafeRelativePath, unsafePathMessage);
|
|
4190
|
+
InstallOverwriteSchema = external_exports.enum(["fail", "skip", "replace"]);
|
|
4191
|
+
PackageInstallSchema = external_exports.object({
|
|
4192
|
+
mainFiles: external_exports.array(external_exports.object({
|
|
4193
|
+
from: InstallPathSchema,
|
|
4194
|
+
to: InstallPathSchema,
|
|
4195
|
+
overwrite: InstallOverwriteSchema.optional()
|
|
4196
|
+
})).optional(),
|
|
4197
|
+
helperFiles: external_exports.array(external_exports.object({
|
|
4198
|
+
from: InstallPathSchema,
|
|
4199
|
+
to: InstallPathSchema.optional()
|
|
4200
|
+
})).optional(),
|
|
4201
|
+
postInstall: external_exports.object({
|
|
4202
|
+
mode: external_exports.literal("manual_prompt"),
|
|
4203
|
+
promptFile: InstallPathSchema,
|
|
4204
|
+
cleanup: external_exports.enum(["manual", "after_user_confirmation"]).optional()
|
|
4205
|
+
}).optional()
|
|
4206
|
+
}).superRefine((install, ctx) => {
|
|
4207
|
+
if (!install.postInstall)
|
|
4208
|
+
return;
|
|
4209
|
+
const helperTargets = new Set((install.helperFiles ?? []).map((file) => file.to ?? basename(file.from)));
|
|
4210
|
+
if (!helperTargets.has(install.postInstall.promptFile)) {
|
|
4211
|
+
ctx.addIssue({
|
|
4212
|
+
code: external_exports.ZodIssueCode.custom,
|
|
4213
|
+
path: ["postInstall", "promptFile"],
|
|
4214
|
+
message: "postInstall.promptFile must point to an installed helper file"
|
|
4215
|
+
});
|
|
4216
|
+
}
|
|
4217
|
+
});
|
|
4176
4218
|
PackageManifestSchema = external_exports.object({
|
|
4177
4219
|
schemaVersion: external_exports.literal("0.1"),
|
|
4178
4220
|
name: external_exports.string().regex(SCOPE_NAME_REGEX, "name must be @scope/name"),
|
|
@@ -4183,11 +4225,13 @@ var init_manifest = __esm({
|
|
|
4183
4225
|
targets: external_exports.array(AiToolSchema).min(1),
|
|
4184
4226
|
license: external_exports.string().optional(),
|
|
4185
4227
|
usage: external_exports.string().trim().min(1).max(2e3).optional(),
|
|
4228
|
+
agentDescription: external_exports.string().trim().min(1).max(12e3).optional(),
|
|
4186
4229
|
tags: external_exports.array(external_exports.string().trim().min(1).max(40)).max(12).optional(),
|
|
4187
4230
|
categories: external_exports.array(external_exports.string().trim().min(1).max(40)).max(4).optional(),
|
|
4188
4231
|
sourceUrl: external_exports.string().trim().url().max(500).optional(),
|
|
4189
4232
|
examples: external_exports.array(PackageExampleSchema).max(5).optional(),
|
|
4190
|
-
releaseNotes: external_exports.string().trim().min(1).max(2e3).optional()
|
|
4233
|
+
releaseNotes: external_exports.string().trim().min(1).max(2e3).optional(),
|
|
4234
|
+
install: PackageInstallSchema.optional()
|
|
4191
4235
|
});
|
|
4192
4236
|
}
|
|
4193
4237
|
});
|
|
@@ -4225,7 +4269,17 @@ var init_lockfile = __esm({
|
|
|
4225
4269
|
integrity: external_exports.string(),
|
|
4226
4270
|
registry: external_exports.string().url(),
|
|
4227
4271
|
resolvedTools: external_exports.array(AiToolSchema),
|
|
4228
|
-
installed: InstalledPathsSchema
|
|
4272
|
+
installed: InstalledPathsSchema,
|
|
4273
|
+
installedAssets: external_exports.object({
|
|
4274
|
+
main: external_exports.array(external_exports.string()),
|
|
4275
|
+
helper: external_exports.array(external_exports.string())
|
|
4276
|
+
}).optional(),
|
|
4277
|
+
postInstall: external_exports.object({
|
|
4278
|
+
mode: external_exports.literal("manual_prompt"),
|
|
4279
|
+
status: external_exports.enum(["pending", "cleaned"]),
|
|
4280
|
+
promptFile: external_exports.string(),
|
|
4281
|
+
cleanup: external_exports.enum(["manual", "after_user_confirmation"])
|
|
4282
|
+
}).optional()
|
|
4229
4283
|
});
|
|
4230
4284
|
LockfileSchema = external_exports.object({
|
|
4231
4285
|
schemaVersion: external_exports.literal("0.1"),
|
|
@@ -7776,16 +7830,15 @@ var import_node_crypto2 = require("node:crypto");
|
|
|
7776
7830
|
var import_node_child_process4 = require("node:child_process");
|
|
7777
7831
|
var import_node_http = require("node:http");
|
|
7778
7832
|
var import_promises10 = require("node:fs/promises");
|
|
7779
|
-
var
|
|
7833
|
+
var import_node_path13 = require("node:path");
|
|
7780
7834
|
var import_node_process5 = require("node:process");
|
|
7781
7835
|
init_dist();
|
|
7782
7836
|
init_dist4();
|
|
7783
7837
|
|
|
7784
7838
|
// src/pack.ts
|
|
7785
7839
|
var import_node_child_process2 = require("node:child_process");
|
|
7786
|
-
var import_promises5 = require("node:fs/promises");
|
|
7787
7840
|
var import_node_util3 = require("node:util");
|
|
7788
|
-
var
|
|
7841
|
+
var import_promises5 = require("node:fs/promises");
|
|
7789
7842
|
var import_node_path6 = require("node:path");
|
|
7790
7843
|
var import_node_os = require("node:os");
|
|
7791
7844
|
|
|
@@ -7938,6 +7991,14 @@ async function readManifest(root) {
|
|
|
7938
7991
|
throw error;
|
|
7939
7992
|
}
|
|
7940
7993
|
}
|
|
7994
|
+
function installReferencedFiles(manifest) {
|
|
7995
|
+
const install = manifest.install;
|
|
7996
|
+
if (!install) return [];
|
|
7997
|
+
return [
|
|
7998
|
+
...(install.mainFiles ?? []).map((file) => file.from),
|
|
7999
|
+
...(install.helperFiles ?? []).map((file) => file.from)
|
|
8000
|
+
];
|
|
8001
|
+
}
|
|
7941
8002
|
async function validatePublishState(root) {
|
|
7942
8003
|
const manifest = await readManifest(root);
|
|
7943
8004
|
const state = await readPublishState(root);
|
|
@@ -7945,6 +8006,9 @@ async function validatePublishState(root) {
|
|
|
7945
8006
|
const staged = new Set(state.files.map((entry) => entry.path));
|
|
7946
8007
|
if (!staged.has("aipm.manifest.json")) throw new Error("aipm.manifest.json must be staged.");
|
|
7947
8008
|
if (!staged.has(manifest.entry)) throw new Error(`Manifest entry must be staged: ${manifest.entry}`);
|
|
8009
|
+
for (const file of installReferencedFiles(manifest)) {
|
|
8010
|
+
if (!staged.has(file)) throw new Error(`Manifest install file must be staged: ${file}`);
|
|
8011
|
+
}
|
|
7948
8012
|
let size = 0;
|
|
7949
8013
|
for (const entry of state.files) {
|
|
7950
8014
|
if (isExcludedPublishPath(entry.path)) throw new Error(`Refusing to publish excluded path: ${entry.path}`);
|
|
@@ -7992,27 +8056,23 @@ async function packDirectory(dir) {
|
|
|
7992
8056
|
return Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout);
|
|
7993
8057
|
}
|
|
7994
8058
|
async function packStagedFiles(root) {
|
|
7995
|
-
const tempDir = await (0,
|
|
8059
|
+
const tempDir = await (0, import_promises5.mkdtemp)((0, import_node_path6.join)((0, import_node_os.tmpdir)(), "aipm-publish-stage-"));
|
|
7996
8060
|
try {
|
|
7997
8061
|
await copyStagedFiles(root, tempDir);
|
|
7998
8062
|
return await packDirectory(tempDir);
|
|
7999
8063
|
} finally {
|
|
8000
|
-
await (0,
|
|
8064
|
+
await (0, import_promises5.rm)(tempDir, { recursive: true, force: true });
|
|
8001
8065
|
}
|
|
8002
8066
|
}
|
|
8003
|
-
async function
|
|
8004
|
-
const { mkdtemp: mkdtemp2,
|
|
8005
|
-
const { join:
|
|
8067
|
+
async function unpackTarballToDirectory(tarball) {
|
|
8068
|
+
const { mkdtemp: mkdtemp2, writeFile: writeFile7 } = await import("node:fs/promises");
|
|
8069
|
+
const { join: join13 } = await import("node:path");
|
|
8006
8070
|
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
8007
|
-
const tempDir = await mkdtemp2(
|
|
8008
|
-
const tgzPath =
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
return (0, import_promises5.readFile)(join12(tempDir, entry), "utf8");
|
|
8013
|
-
} finally {
|
|
8014
|
-
await rm3(tempDir, { recursive: true, force: true });
|
|
8015
|
-
}
|
|
8071
|
+
const tempDir = await mkdtemp2(join13(tmpdir2(), "aipm-install-"));
|
|
8072
|
+
const tgzPath = join13(tempDir, "pkg.tgz");
|
|
8073
|
+
await writeFile7(tgzPath, tarball);
|
|
8074
|
+
await execFileAsync("tar", ["-xzf", tgzPath, "-C", tempDir]);
|
|
8075
|
+
return tempDir;
|
|
8016
8076
|
}
|
|
8017
8077
|
|
|
8018
8078
|
// src/registry-client.ts
|
|
@@ -8155,7 +8215,7 @@ async function recordPackageInstall(registry, name, token) {
|
|
|
8155
8215
|
}
|
|
8156
8216
|
|
|
8157
8217
|
// src/auth-store.ts
|
|
8158
|
-
var
|
|
8218
|
+
var import_promises6 = require("node:fs/promises");
|
|
8159
8219
|
var import_node_os2 = require("node:os");
|
|
8160
8220
|
var import_node_path7 = require("node:path");
|
|
8161
8221
|
var AUTH_FILE = (0, import_node_path7.join)((0, import_node_os2.homedir)(), ".aipm", "auth.json");
|
|
@@ -8164,7 +8224,7 @@ function normalizeRegistry(registry) {
|
|
|
8164
8224
|
}
|
|
8165
8225
|
async function readAuthStore() {
|
|
8166
8226
|
try {
|
|
8167
|
-
const raw = await (0,
|
|
8227
|
+
const raw = await (0, import_promises6.readFile)(AUTH_FILE, "utf8");
|
|
8168
8228
|
const parsed = JSON.parse(raw);
|
|
8169
8229
|
return { registries: parsed.registries ?? {} };
|
|
8170
8230
|
} catch {
|
|
@@ -8172,9 +8232,9 @@ async function readAuthStore() {
|
|
|
8172
8232
|
}
|
|
8173
8233
|
}
|
|
8174
8234
|
async function writeAuthStore(store) {
|
|
8175
|
-
await (0,
|
|
8176
|
-
await (0,
|
|
8177
|
-
await (0,
|
|
8235
|
+
await (0, import_promises6.mkdir)((0, import_node_path7.dirname)(AUTH_FILE), { recursive: true });
|
|
8236
|
+
await (0, import_promises6.writeFile)(AUTH_FILE, JSON.stringify(store, null, 2) + "\n", "utf8");
|
|
8237
|
+
await (0, import_promises6.chmod)(AUTH_FILE, 384).catch(() => void 0);
|
|
8178
8238
|
}
|
|
8179
8239
|
async function getStoredRegistryAuth(registry) {
|
|
8180
8240
|
const store = await readAuthStore();
|
|
@@ -8204,23 +8264,25 @@ function isAccessTokenFresh(auth, skewMs = 6e4) {
|
|
|
8204
8264
|
|
|
8205
8265
|
// src/install-one.ts
|
|
8206
8266
|
init_dist4();
|
|
8267
|
+
var import_promises8 = require("node:fs/promises");
|
|
8268
|
+
var import_node_path9 = require("node:path");
|
|
8207
8269
|
|
|
8208
8270
|
// src/project-files.ts
|
|
8209
|
-
var
|
|
8271
|
+
var import_promises7 = require("node:fs/promises");
|
|
8210
8272
|
var import_node_path8 = require("node:path");
|
|
8211
8273
|
init_dist();
|
|
8212
8274
|
var PACKAGE_JSON = "aipm.package.json";
|
|
8213
8275
|
var LOCKFILE = "aipm-lock.json";
|
|
8214
8276
|
async function readProjectPackageJson(projectRoot) {
|
|
8215
8277
|
try {
|
|
8216
|
-
const raw = await (0,
|
|
8278
|
+
const raw = await (0, import_promises7.readFile)((0, import_node_path8.join)(projectRoot, PACKAGE_JSON), "utf8");
|
|
8217
8279
|
return ProjectPackageJsonSchema.parse(JSON.parse(raw));
|
|
8218
8280
|
} catch {
|
|
8219
8281
|
return null;
|
|
8220
8282
|
}
|
|
8221
8283
|
}
|
|
8222
8284
|
async function writeProjectPackageJson(projectRoot, data) {
|
|
8223
|
-
await (0,
|
|
8285
|
+
await (0, import_promises7.writeFile)(
|
|
8224
8286
|
(0, import_node_path8.join)(projectRoot, PACKAGE_JSON),
|
|
8225
8287
|
JSON.stringify(data, null, 2) + "\n",
|
|
8226
8288
|
"utf8"
|
|
@@ -8228,14 +8290,14 @@ async function writeProjectPackageJson(projectRoot, data) {
|
|
|
8228
8290
|
}
|
|
8229
8291
|
async function readLockfile(projectRoot) {
|
|
8230
8292
|
try {
|
|
8231
|
-
const raw = await (0,
|
|
8293
|
+
const raw = await (0, import_promises7.readFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), "utf8");
|
|
8232
8294
|
return LockfileSchema.parse(JSON.parse(raw));
|
|
8233
8295
|
} catch {
|
|
8234
8296
|
return null;
|
|
8235
8297
|
}
|
|
8236
8298
|
}
|
|
8237
8299
|
async function writeLockfile(projectRoot, data) {
|
|
8238
|
-
await (0,
|
|
8300
|
+
await (0, import_promises7.writeFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
8239
8301
|
}
|
|
8240
8302
|
function upsertLockEntry(lock, name, entry) {
|
|
8241
8303
|
return {
|
|
@@ -8289,8 +8351,113 @@ async function promptForTool() {
|
|
|
8289
8351
|
rl.close();
|
|
8290
8352
|
}
|
|
8291
8353
|
}
|
|
8354
|
+
async function promptForConfirmation(question) {
|
|
8355
|
+
const rl = readline.createInterface({ input: import_node_process2.stdin, output: import_node_process2.stdout });
|
|
8356
|
+
try {
|
|
8357
|
+
const answer = await rl.question(`${question} (y/N): `);
|
|
8358
|
+
const normalized = answer.trim().toLowerCase();
|
|
8359
|
+
return normalized === "y" || normalized === "yes";
|
|
8360
|
+
} finally {
|
|
8361
|
+
rl.close();
|
|
8362
|
+
}
|
|
8363
|
+
}
|
|
8292
8364
|
|
|
8293
8365
|
// src/install-one.ts
|
|
8366
|
+
function normalizeRelativePath(path2) {
|
|
8367
|
+
return (0, import_node_path9.normalize)(path2).split(import_node_path9.sep).join("/");
|
|
8368
|
+
}
|
|
8369
|
+
function assertSafeRelativePath(path2) {
|
|
8370
|
+
const rel = normalizeRelativePath(path2.trim());
|
|
8371
|
+
if (!rel || rel === "." || rel.startsWith("/") || rel === ".." || rel.startsWith("../") || rel.includes("/../")) {
|
|
8372
|
+
throw new Error(`Unsafe install path: ${path2}`);
|
|
8373
|
+
}
|
|
8374
|
+
return rel;
|
|
8375
|
+
}
|
|
8376
|
+
function safeJoin(root, relPath) {
|
|
8377
|
+
const rel = assertSafeRelativePath(relPath);
|
|
8378
|
+
const target = (0, import_node_path9.resolve)(root, rel);
|
|
8379
|
+
const fromRoot = normalizeRelativePath((0, import_node_path9.relative)(root, target));
|
|
8380
|
+
if (fromRoot === ".." || fromRoot.startsWith("../") || fromRoot.startsWith("/")) {
|
|
8381
|
+
throw new Error(`Unsafe install path: ${relPath}`);
|
|
8382
|
+
}
|
|
8383
|
+
return target;
|
|
8384
|
+
}
|
|
8385
|
+
function packageHelperSlug(name) {
|
|
8386
|
+
const [scope = "", pkg = ""] = name.replace(/^@/, "").split("/");
|
|
8387
|
+
return `${scope}__${pkg}`;
|
|
8388
|
+
}
|
|
8389
|
+
function helperRootFor(configRoot, packageName, version) {
|
|
8390
|
+
const base = (0, import_node_path9.basename)(configRoot) === ".aipm" ? (0, import_node_path9.join)(configRoot, "helpers") : (0, import_node_path9.join)(configRoot, ".aipm", "helpers");
|
|
8391
|
+
return (0, import_node_path9.join)(base, packageHelperSlug(packageName), version);
|
|
8392
|
+
}
|
|
8393
|
+
async function assertSourceFile(packageRoot, relPath) {
|
|
8394
|
+
const source = safeJoin(packageRoot, relPath);
|
|
8395
|
+
const info = await (0, import_promises8.stat)(source).catch(() => null);
|
|
8396
|
+
if (!info?.isFile()) throw new Error(`Package install file not found: ${relPath}`);
|
|
8397
|
+
return source;
|
|
8398
|
+
}
|
|
8399
|
+
async function copyMainFile(input2) {
|
|
8400
|
+
const source = await assertSourceFile(input2.packageRoot, input2.file.from);
|
|
8401
|
+
const target = safeJoin(input2.installRoot, input2.file.to);
|
|
8402
|
+
const overwrite = input2.file.overwrite ?? "fail";
|
|
8403
|
+
const exists = await (0, import_promises8.stat)(target).catch(() => null);
|
|
8404
|
+
if (exists) {
|
|
8405
|
+
if (overwrite === "skip") return null;
|
|
8406
|
+
if (overwrite === "fail") throw new Error(`Install target already exists: ${target}`);
|
|
8407
|
+
}
|
|
8408
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
|
|
8409
|
+
await (0, import_promises8.cp)(source, target, { force: overwrite === "replace" });
|
|
8410
|
+
return target;
|
|
8411
|
+
}
|
|
8412
|
+
async function copyHelperFile(input2) {
|
|
8413
|
+
const source = await assertSourceFile(input2.packageRoot, input2.file.from);
|
|
8414
|
+
const target = safeJoin(input2.helperRoot, input2.file.to ?? (0, import_node_path9.basename)(input2.file.from));
|
|
8415
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
|
|
8416
|
+
await (0, import_promises8.cp)(source, target, { force: true });
|
|
8417
|
+
return target;
|
|
8418
|
+
}
|
|
8419
|
+
async function installPackageAssets(options) {
|
|
8420
|
+
const assets = { main: [], helper: [] };
|
|
8421
|
+
const install = options.install;
|
|
8422
|
+
if (!install) return { assets };
|
|
8423
|
+
for (const file of install.mainFiles ?? []) {
|
|
8424
|
+
const copied = await copyMainFile({
|
|
8425
|
+
packageRoot: options.packageRoot,
|
|
8426
|
+
installRoot: options.installRoot,
|
|
8427
|
+
file
|
|
8428
|
+
});
|
|
8429
|
+
if (copied) assets.main.push(copied);
|
|
8430
|
+
}
|
|
8431
|
+
const helperRoot = helperRootFor(options.configRoot, options.packageName, options.version);
|
|
8432
|
+
if ((install.helperFiles ?? []).length > 0) {
|
|
8433
|
+
await (0, import_promises8.rm)(helperRoot, { recursive: true, force: true });
|
|
8434
|
+
}
|
|
8435
|
+
for (const file of install.helperFiles ?? []) {
|
|
8436
|
+
assets.helper.push(await copyHelperFile({ packageRoot: options.packageRoot, helperRoot, file }));
|
|
8437
|
+
}
|
|
8438
|
+
if (!install.postInstall) return { assets };
|
|
8439
|
+
const promptFile = safeJoin(helperRoot, install.postInstall.promptFile);
|
|
8440
|
+
if (!assets.helper.includes(promptFile)) {
|
|
8441
|
+
throw new Error(`Post-install prompt was not installed: ${install.postInstall.promptFile}`);
|
|
8442
|
+
}
|
|
8443
|
+
return {
|
|
8444
|
+
assets,
|
|
8445
|
+
postInstall: {
|
|
8446
|
+
mode: "manual_prompt",
|
|
8447
|
+
status: "pending",
|
|
8448
|
+
promptFile,
|
|
8449
|
+
cleanup: install.postInstall.cleanup ?? "manual"
|
|
8450
|
+
}
|
|
8451
|
+
};
|
|
8452
|
+
}
|
|
8453
|
+
function printPostInstallNotice(name, postInstall) {
|
|
8454
|
+
if (!postInstall || postInstall.mode !== "manual_prompt") return;
|
|
8455
|
+
console.log("");
|
|
8456
|
+
console.log("This package requires AI-assisted setup.");
|
|
8457
|
+
console.log(`Prompt saved to: ${postInstall.promptFile}`);
|
|
8458
|
+
console.log(`Run: aipm show-prompt ${name}`);
|
|
8459
|
+
console.log(`After setup, run: aipm cleanup ${name}`);
|
|
8460
|
+
}
|
|
8294
8461
|
async function installOnePackage(options) {
|
|
8295
8462
|
const configRoot = options.configRoot;
|
|
8296
8463
|
const installRoot = options.installRoot ?? configRoot;
|
|
@@ -8337,49 +8504,65 @@ async function installOnePackage(options) {
|
|
|
8337
8504
|
options.version,
|
|
8338
8505
|
options.token
|
|
8339
8506
|
);
|
|
8340
|
-
const
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
if (paths) installed[tool] = paths;
|
|
8356
|
-
}
|
|
8357
|
-
await writeLockfile(
|
|
8358
|
-
configRoot,
|
|
8359
|
-
upsertLockEntry(lock, options.name, {
|
|
8507
|
+
const packageRoot = await unpackTarballToDirectory(tarball);
|
|
8508
|
+
try {
|
|
8509
|
+
const skillMarkdown = await (0, import_promises8.readFile)(safeJoin(packageRoot, manifest.entry), "utf8");
|
|
8510
|
+
const result = await installSkillPackage({
|
|
8511
|
+
projectRoot: installRoot,
|
|
8512
|
+
manifest,
|
|
8513
|
+
skillMarkdown,
|
|
8514
|
+
preferredTools,
|
|
8515
|
+
explicitTarget
|
|
8516
|
+
});
|
|
8517
|
+
const { assets, postInstall } = await installPackageAssets({
|
|
8518
|
+
configRoot,
|
|
8519
|
+
installRoot,
|
|
8520
|
+
packageRoot,
|
|
8521
|
+
packageName: options.name,
|
|
8360
8522
|
version: options.version,
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8523
|
+
install: manifest.install
|
|
8524
|
+
});
|
|
8525
|
+
const lock = await readLockfile(configRoot) ?? {
|
|
8526
|
+
schemaVersion: "0.1",
|
|
8527
|
+
packages: {}
|
|
8528
|
+
};
|
|
8529
|
+
const installed = {};
|
|
8530
|
+
for (const tool of result.resolvedTools) {
|
|
8531
|
+
const paths = result.installed[tool];
|
|
8532
|
+
if (paths) installed[tool] = paths;
|
|
8533
|
+
}
|
|
8534
|
+
await writeLockfile(
|
|
8535
|
+
configRoot,
|
|
8536
|
+
upsertLockEntry(lock, options.name, {
|
|
8537
|
+
version: options.version,
|
|
8538
|
+
integrity: remoteIntegrity,
|
|
8539
|
+
registry: options.registry,
|
|
8540
|
+
resolvedTools: result.resolvedTools,
|
|
8541
|
+
installed,
|
|
8542
|
+
...assets.main.length || assets.helper.length ? { installedAssets: assets } : {},
|
|
8543
|
+
...postInstall ? { postInstall } : {}
|
|
8544
|
+
})
|
|
8545
|
+
);
|
|
8546
|
+
console.log(`Installed ${options.name}@${options.version} \u2192 ${result.resolvedTools.join(", ")}`);
|
|
8547
|
+
printPostInstallNotice(options.name, postInstall);
|
|
8548
|
+
} finally {
|
|
8549
|
+
await (0, import_promises8.rm)(packageRoot, { recursive: true, force: true });
|
|
8550
|
+
}
|
|
8368
8551
|
}
|
|
8369
8552
|
|
|
8370
8553
|
// src/doctor.ts
|
|
8371
8554
|
var import_node_child_process3 = require("node:child_process");
|
|
8372
8555
|
var import_promises9 = require("node:fs/promises");
|
|
8373
|
-
var
|
|
8556
|
+
var import_node_path11 = require("node:path");
|
|
8374
8557
|
var import_node_util4 = require("node:util");
|
|
8375
8558
|
|
|
8376
8559
|
// src/project-root.ts
|
|
8377
8560
|
var import_node_os3 = require("node:os");
|
|
8378
|
-
var
|
|
8561
|
+
var import_node_path10 = require("node:path");
|
|
8379
8562
|
var import_node_process3 = require("node:process");
|
|
8380
8563
|
function globalConfigDir(env2 = process.env) {
|
|
8381
8564
|
const fromEnv = env2.AIPM_HOME?.trim();
|
|
8382
|
-
return fromEnv ? fromEnv.replace(/\/$/, "") : (0,
|
|
8565
|
+
return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path10.join)((0, import_node_os3.homedir)(), ".aipm");
|
|
8383
8566
|
}
|
|
8384
8567
|
function resolveConfigRoot(options = {}) {
|
|
8385
8568
|
return options.global ? globalConfigDir() : (0, import_node_process3.cwd)();
|
|
@@ -8410,11 +8593,11 @@ async function commandOutput(command, args) {
|
|
|
8410
8593
|
}
|
|
8411
8594
|
function npmGlobalBin(prefix) {
|
|
8412
8595
|
if (!prefix) return null;
|
|
8413
|
-
return process.platform === "win32" ? prefix : `${prefix}${
|
|
8596
|
+
return process.platform === "win32" ? prefix : `${prefix}${import_node_path11.sep}bin`;
|
|
8414
8597
|
}
|
|
8415
8598
|
function pathIncludes(pathDir) {
|
|
8416
8599
|
if (!pathDir) return false;
|
|
8417
|
-
return (process.env.PATH ?? "").split(
|
|
8600
|
+
return (process.env.PATH ?? "").split(import_node_path11.delimiter).includes(pathDir);
|
|
8418
8601
|
}
|
|
8419
8602
|
function shellPathHint(binDir) {
|
|
8420
8603
|
if (!binDir) return "Run npm prefix -g and add its bin folder to PATH.";
|
|
@@ -8467,11 +8650,11 @@ async function runDoctor(options) {
|
|
|
8467
8650
|
if (!options.publish) {
|
|
8468
8651
|
const configLabel = options.global ? "Global config" : "Project config";
|
|
8469
8652
|
try {
|
|
8470
|
-
await (0, import_promises9.access)((0,
|
|
8653
|
+
await (0, import_promises9.access)((0, import_node_path11.join)(configRoot, "aipm.package.json"));
|
|
8471
8654
|
checks.push({
|
|
8472
8655
|
name: configLabel,
|
|
8473
8656
|
ok: true,
|
|
8474
|
-
detail: `${(0,
|
|
8657
|
+
detail: `${(0, import_node_path11.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
|
|
8475
8658
|
});
|
|
8476
8659
|
} catch {
|
|
8477
8660
|
checks.push({
|
|
@@ -8541,9 +8724,9 @@ async function runDoctor(options) {
|
|
|
8541
8724
|
|
|
8542
8725
|
// src/version.ts
|
|
8543
8726
|
var import_node_fs2 = require("node:fs");
|
|
8544
|
-
var
|
|
8727
|
+
var import_node_path12 = require("node:path");
|
|
8545
8728
|
function getCliVersion() {
|
|
8546
|
-
const pkgPath = (0,
|
|
8729
|
+
const pkgPath = (0, import_node_path12.join)(__dirname, "..", "package.json");
|
|
8547
8730
|
const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
|
|
8548
8731
|
return pkg.version;
|
|
8549
8732
|
}
|
|
@@ -8682,7 +8865,7 @@ async function writeStarterFile(path2, content) {
|
|
|
8682
8865
|
}
|
|
8683
8866
|
async function inferEntryFromSource(source, fallback) {
|
|
8684
8867
|
const sourceInfo = await (0, import_promises10.stat)(source);
|
|
8685
|
-
if (sourceInfo.isFile()) return (0,
|
|
8868
|
+
if (sourceInfo.isFile()) return (0, import_node_path13.basename)(source);
|
|
8686
8869
|
if (!sourceInfo.isDirectory()) return fallback;
|
|
8687
8870
|
const entries = await (0, import_promises10.readdir)(source);
|
|
8688
8871
|
if (entries.includes(fallback)) return fallback;
|
|
@@ -8694,7 +8877,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
|
8694
8877
|
if (sourceInfo.isDirectory()) {
|
|
8695
8878
|
const entries = await (0, import_promises10.readdir)(source);
|
|
8696
8879
|
for (const entry of entries) {
|
|
8697
|
-
await (0, import_promises10.cp)((0,
|
|
8880
|
+
await (0, import_promises10.cp)((0, import_node_path13.join)(source, entry), (0, import_node_path13.join)(root, entry), {
|
|
8698
8881
|
recursive: true,
|
|
8699
8882
|
force: false,
|
|
8700
8883
|
errorOnExist: true
|
|
@@ -8703,7 +8886,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
|
8703
8886
|
return;
|
|
8704
8887
|
}
|
|
8705
8888
|
if (sourceInfo.isFile()) {
|
|
8706
|
-
await (0, import_promises10.cp)(source, (0,
|
|
8889
|
+
await (0, import_promises10.cp)(source, (0, import_node_path13.join)(root, (0, import_node_path13.basename)(source)), { force: false, errorOnExist: true });
|
|
8707
8890
|
return;
|
|
8708
8891
|
}
|
|
8709
8892
|
throw new Error(`Unsupported source path: ${sourceLabel}`);
|
|
@@ -8736,6 +8919,73 @@ function packageDashboardUrl(name) {
|
|
|
8736
8919
|
if (!name) return DASHBOARD_URL;
|
|
8737
8920
|
return `${DASHBOARD_URL}/packages/${name.replace(/^@/, "")}`;
|
|
8738
8921
|
}
|
|
8922
|
+
function helperRootFromTrackedPath(path2) {
|
|
8923
|
+
const parts = path2.split(import_node_path13.sep).filter(Boolean);
|
|
8924
|
+
const helpersIndex = parts.lastIndexOf("helpers");
|
|
8925
|
+
if (helpersIndex === -1 || parts.length <= helpersIndex + 2) return null;
|
|
8926
|
+
const prefix = path2.startsWith(import_node_path13.sep) ? import_node_path13.sep : "";
|
|
8927
|
+
return (0, import_node_path13.join)(prefix, ...parts.slice(0, helpersIndex + 3));
|
|
8928
|
+
}
|
|
8929
|
+
async function showInstalledPrompt(configRoot, packageArg) {
|
|
8930
|
+
const { name } = parsePackageArg(packageArg);
|
|
8931
|
+
const lock = await readLockfile(configRoot);
|
|
8932
|
+
const entry = lock?.packages[name];
|
|
8933
|
+
if (!entry) throw new Error(`Package is not installed: ${name}`);
|
|
8934
|
+
if (!entry.postInstall || entry.postInstall.mode !== "manual_prompt") {
|
|
8935
|
+
throw new Error(`Package has no setup prompt: ${name}`);
|
|
8936
|
+
}
|
|
8937
|
+
if (entry.postInstall.status === "cleaned") {
|
|
8938
|
+
throw new Error(`Setup helper files were cleaned for ${name}. Reinstall the package to restore them.`);
|
|
8939
|
+
}
|
|
8940
|
+
const content = await (0, import_promises10.readFile)(entry.postInstall.promptFile, "utf8").catch(() => {
|
|
8941
|
+
throw new Error(`Setup prompt file is missing: ${entry.postInstall?.promptFile}`);
|
|
8942
|
+
});
|
|
8943
|
+
console.log(`Prompt file: ${entry.postInstall.promptFile}`);
|
|
8944
|
+
console.log("");
|
|
8945
|
+
console.log(content.trimEnd());
|
|
8946
|
+
}
|
|
8947
|
+
async function cleanupInstalledHelpers(options) {
|
|
8948
|
+
const { name } = parsePackageArg(options.packageArg);
|
|
8949
|
+
const lock = await readLockfile(options.configRoot);
|
|
8950
|
+
const entry = lock?.packages[name];
|
|
8951
|
+
if (!lock || !entry) throw new Error(`Package is not installed: ${name}`);
|
|
8952
|
+
if (!entry.installedAssets?.helper?.length) {
|
|
8953
|
+
throw new Error(`Package has no helper files to clean: ${name}`);
|
|
8954
|
+
}
|
|
8955
|
+
if (!entry.postInstall) {
|
|
8956
|
+
throw new Error(`Package has no post-install helper state: ${name}`);
|
|
8957
|
+
}
|
|
8958
|
+
if (entry.postInstall.status === "cleaned") {
|
|
8959
|
+
console.log(`Helper files already cleaned for ${name}.`);
|
|
8960
|
+
return;
|
|
8961
|
+
}
|
|
8962
|
+
if (!options.yes) {
|
|
8963
|
+
const confirmed = await promptForConfirmation(`Delete tracked helper files for ${name}?`);
|
|
8964
|
+
if (!confirmed) {
|
|
8965
|
+
console.log("Cleanup cancelled.");
|
|
8966
|
+
return;
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8969
|
+
const roots = new Set(
|
|
8970
|
+
entry.installedAssets.helper.map((path2) => helperRootFromTrackedPath(path2)).filter((path2) => Boolean(path2))
|
|
8971
|
+
);
|
|
8972
|
+
if (roots.size > 0) {
|
|
8973
|
+
for (const root of roots) await (0, import_promises10.rm)(root, { recursive: true, force: true });
|
|
8974
|
+
} else {
|
|
8975
|
+
for (const helperPath of entry.installedAssets.helper) await (0, import_promises10.rm)(helperPath, { force: true });
|
|
8976
|
+
}
|
|
8977
|
+
await writeLockfile(options.configRoot, {
|
|
8978
|
+
...lock,
|
|
8979
|
+
packages: {
|
|
8980
|
+
...lock.packages,
|
|
8981
|
+
[name]: {
|
|
8982
|
+
...entry,
|
|
8983
|
+
postInstall: { ...entry.postInstall, status: "cleaned" }
|
|
8984
|
+
}
|
|
8985
|
+
}
|
|
8986
|
+
});
|
|
8987
|
+
console.log(`Cleaned helper files for ${name}.`);
|
|
8988
|
+
}
|
|
8739
8989
|
async function openUrl(url) {
|
|
8740
8990
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
8741
8991
|
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
@@ -8874,6 +9124,11 @@ async function printPublishPreview(root, json) {
|
|
|
8874
9124
|
description: manifest.description,
|
|
8875
9125
|
targets: manifest.targets,
|
|
8876
9126
|
entry: manifest.entry,
|
|
9127
|
+
install: manifest.install ? {
|
|
9128
|
+
mainFiles: manifest.install.mainFiles ?? [],
|
|
9129
|
+
helperFiles: manifest.install.helperFiles ?? [],
|
|
9130
|
+
postInstall: manifest.install.postInstall ?? null
|
|
9131
|
+
} : null,
|
|
8877
9132
|
files: rows.map((row) => ({ path: row.path, size: row.size, changed: row.changed })),
|
|
8878
9133
|
totalBytes: size,
|
|
8879
9134
|
warnings: [
|
|
@@ -8891,6 +9146,13 @@ async function printPublishPreview(root, json) {
|
|
|
8891
9146
|
console.log(`Description: ${preview.description}`);
|
|
8892
9147
|
console.log(`Targets: ${preview.targets.join(", ")}`);
|
|
8893
9148
|
console.log(`Entry: ${preview.entry}`);
|
|
9149
|
+
if (preview.install) {
|
|
9150
|
+
console.log(`Main files: ${preview.install.mainFiles.length}`);
|
|
9151
|
+
console.log(`Helper files: ${preview.install.helperFiles.length}`);
|
|
9152
|
+
if (preview.install.postInstall) {
|
|
9153
|
+
console.log(`Post-install: ${preview.install.postInstall.mode} (${preview.install.postInstall.promptFile})`);
|
|
9154
|
+
}
|
|
9155
|
+
}
|
|
8894
9156
|
console.log(`Total: ${preview.files.length} file${preview.files.length === 1 ? "" : "s"}, ${preview.totalBytes} bytes`);
|
|
8895
9157
|
for (const file of preview.files) {
|
|
8896
9158
|
console.log(`${file.changed ? "modified" : "staged"} ${file.path} (${file.size} bytes)`);
|
|
@@ -9004,9 +9266,9 @@ function buildStarterQualityMetadata(options) {
|
|
|
9004
9266
|
}
|
|
9005
9267
|
async function initSkill(opts) {
|
|
9006
9268
|
if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/name");
|
|
9007
|
-
const root = opts.here ? (0, import_node_process5.cwd)() : (0,
|
|
9269
|
+
const root = opts.here ? (0, import_node_process5.cwd)() : (0, import_node_path13.resolve)((0, import_node_process5.cwd)(), opts.dir ?? skillFolderName(opts.name));
|
|
9008
9270
|
const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
|
|
9009
|
-
const source = opts.from ? (0,
|
|
9271
|
+
const source = opts.from ? (0, import_node_path13.resolve)(opts.from) : null;
|
|
9010
9272
|
const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
|
|
9011
9273
|
const template = parseSkillTemplate(opts.template ?? "blank");
|
|
9012
9274
|
const manifest = PackageManifestSchema.parse({
|
|
@@ -9029,15 +9291,15 @@ async function initSkill(opts) {
|
|
|
9029
9291
|
} else {
|
|
9030
9292
|
await (0, import_promises10.mkdir)(root, { recursive: true });
|
|
9031
9293
|
}
|
|
9032
|
-
await writeStarterFile((0,
|
|
9294
|
+
await writeStarterFile((0, import_node_path13.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
9033
9295
|
if (!opts.from) {
|
|
9034
9296
|
await writeStarterFile(
|
|
9035
|
-
(0,
|
|
9297
|
+
(0, import_node_path13.join)(root, entry),
|
|
9036
9298
|
SKILL_TEMPLATES[template](opts.name)
|
|
9037
9299
|
);
|
|
9038
9300
|
}
|
|
9039
9301
|
await writeStarterFile(
|
|
9040
|
-
(0,
|
|
9302
|
+
(0, import_node_path13.join)(root, ".aipmignore"),
|
|
9041
9303
|
[
|
|
9042
9304
|
"# Files that should never be published with this skill",
|
|
9043
9305
|
".env*",
|
|
@@ -9055,7 +9317,7 @@ async function initSkill(opts) {
|
|
|
9055
9317
|
""
|
|
9056
9318
|
].join("\n")
|
|
9057
9319
|
);
|
|
9058
|
-
await (0, import_promises10.mkdir)((0,
|
|
9320
|
+
await (0, import_promises10.mkdir)((0, import_node_path13.join)(root, ".aipm"), { recursive: true });
|
|
9059
9321
|
console.log(`Created ${opts.name} skill folder: ${root}`);
|
|
9060
9322
|
printPublishGuide(
|
|
9061
9323
|
opts.from ? `Copied ${opts.from} into ${root} and created aipm.manifest.json, .aipmignore, and the local publish state folder.` : `Created ${root} with aipm.manifest.json, ${entry}, .aipmignore, and the local publish state folder.`,
|
|
@@ -9254,6 +9516,16 @@ program2.command("install").description("Install all packages from aipm.package.
|
|
|
9254
9516
|
});
|
|
9255
9517
|
}
|
|
9256
9518
|
});
|
|
9519
|
+
program2.command("show-prompt <package>").description("Show the manual setup prompt installed with a package").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (pkgArg, opts) => {
|
|
9520
|
+
await showInstalledPrompt(resolveConfigRoot({ global: opts.global }), pkgArg);
|
|
9521
|
+
});
|
|
9522
|
+
program2.command("cleanup <package>").description("Delete temporary helper files installed with a package").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--yes", "Delete helper files without asking for confirmation").action(async (pkgArg, opts) => {
|
|
9523
|
+
await cleanupInstalledHelpers({
|
|
9524
|
+
configRoot: resolveConfigRoot({ global: opts.global }),
|
|
9525
|
+
packageArg: pkgArg,
|
|
9526
|
+
yes: opts.yes
|
|
9527
|
+
});
|
|
9528
|
+
});
|
|
9257
9529
|
program2.command("skill").description("Create and manage local skill packages").command("init").description("Create aipm.manifest.json and SKILL.md").requiredOption("--name <name>", "Package name, e.g. @org/skill").option("--version <version>", "Initial version", "1.0.0").option("--description <description>", "Skill description", "AIPM skill").option("--targets <targets>", "Comma-separated targets", "cursor").option("--entry <entry>", "Entry file", "SKILL.md").option("--template <template>", "Starter SKILL.md template: blank, code-review, issue-summary, release-notes", "blank").option("--dir <dir>", "Create files in this folder").option("--here", "Create files in the current folder instead of a skill-named folder").option("--from <path>", "Copy an existing AI-tool skill file or folder into the package folder").action(
|
|
9258
9530
|
async (opts) => initSkill(opts)
|
|
9259
9531
|
);
|
|
@@ -9262,8 +9534,8 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
|
|
|
9262
9534
|
publish.help();
|
|
9263
9535
|
return;
|
|
9264
9536
|
}
|
|
9265
|
-
const abs = (0,
|
|
9266
|
-
const manifestRaw = await (0, import_promises10.readFile)((0,
|
|
9537
|
+
const abs = (0, import_node_path13.resolve)(dir);
|
|
9538
|
+
const manifestRaw = await (0, import_promises10.readFile)((0, import_node_path13.join)(abs, "aipm.manifest.json"), "utf8");
|
|
9267
9539
|
const manifest = PackageManifestSchema.parse(JSON.parse(manifestRaw));
|
|
9268
9540
|
const tarball = await packDirectory(abs);
|
|
9269
9541
|
const registry = registryFromEnvOrDefault(opts.registry);
|