@aipm-registry/cli 0.3.0 → 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 +350 -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
|
|
@@ -8071,6 +8131,7 @@ async function searchPackages(registry, query, limit = 20, token) {
|
|
|
8071
8131
|
const params = new URLSearchParams();
|
|
8072
8132
|
if (query) params.set("q", query);
|
|
8073
8133
|
params.set("limit", String(limit));
|
|
8134
|
+
if (token) params.set("includePrivate", "true");
|
|
8074
8135
|
const res = await registryFetch(`${base}/v1/packages?${params}`, base, {
|
|
8075
8136
|
headers: registryAuthHeaders(token)
|
|
8076
8137
|
});
|
|
@@ -8154,7 +8215,7 @@ async function recordPackageInstall(registry, name, token) {
|
|
|
8154
8215
|
}
|
|
8155
8216
|
|
|
8156
8217
|
// src/auth-store.ts
|
|
8157
|
-
var
|
|
8218
|
+
var import_promises6 = require("node:fs/promises");
|
|
8158
8219
|
var import_node_os2 = require("node:os");
|
|
8159
8220
|
var import_node_path7 = require("node:path");
|
|
8160
8221
|
var AUTH_FILE = (0, import_node_path7.join)((0, import_node_os2.homedir)(), ".aipm", "auth.json");
|
|
@@ -8163,7 +8224,7 @@ function normalizeRegistry(registry) {
|
|
|
8163
8224
|
}
|
|
8164
8225
|
async function readAuthStore() {
|
|
8165
8226
|
try {
|
|
8166
|
-
const raw = await (0,
|
|
8227
|
+
const raw = await (0, import_promises6.readFile)(AUTH_FILE, "utf8");
|
|
8167
8228
|
const parsed = JSON.parse(raw);
|
|
8168
8229
|
return { registries: parsed.registries ?? {} };
|
|
8169
8230
|
} catch {
|
|
@@ -8171,9 +8232,9 @@ async function readAuthStore() {
|
|
|
8171
8232
|
}
|
|
8172
8233
|
}
|
|
8173
8234
|
async function writeAuthStore(store) {
|
|
8174
|
-
await (0,
|
|
8175
|
-
await (0,
|
|
8176
|
-
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);
|
|
8177
8238
|
}
|
|
8178
8239
|
async function getStoredRegistryAuth(registry) {
|
|
8179
8240
|
const store = await readAuthStore();
|
|
@@ -8203,23 +8264,25 @@ function isAccessTokenFresh(auth, skewMs = 6e4) {
|
|
|
8203
8264
|
|
|
8204
8265
|
// src/install-one.ts
|
|
8205
8266
|
init_dist4();
|
|
8267
|
+
var import_promises8 = require("node:fs/promises");
|
|
8268
|
+
var import_node_path9 = require("node:path");
|
|
8206
8269
|
|
|
8207
8270
|
// src/project-files.ts
|
|
8208
|
-
var
|
|
8271
|
+
var import_promises7 = require("node:fs/promises");
|
|
8209
8272
|
var import_node_path8 = require("node:path");
|
|
8210
8273
|
init_dist();
|
|
8211
8274
|
var PACKAGE_JSON = "aipm.package.json";
|
|
8212
8275
|
var LOCKFILE = "aipm-lock.json";
|
|
8213
8276
|
async function readProjectPackageJson(projectRoot) {
|
|
8214
8277
|
try {
|
|
8215
|
-
const raw = await (0,
|
|
8278
|
+
const raw = await (0, import_promises7.readFile)((0, import_node_path8.join)(projectRoot, PACKAGE_JSON), "utf8");
|
|
8216
8279
|
return ProjectPackageJsonSchema.parse(JSON.parse(raw));
|
|
8217
8280
|
} catch {
|
|
8218
8281
|
return null;
|
|
8219
8282
|
}
|
|
8220
8283
|
}
|
|
8221
8284
|
async function writeProjectPackageJson(projectRoot, data) {
|
|
8222
|
-
await (0,
|
|
8285
|
+
await (0, import_promises7.writeFile)(
|
|
8223
8286
|
(0, import_node_path8.join)(projectRoot, PACKAGE_JSON),
|
|
8224
8287
|
JSON.stringify(data, null, 2) + "\n",
|
|
8225
8288
|
"utf8"
|
|
@@ -8227,14 +8290,14 @@ async function writeProjectPackageJson(projectRoot, data) {
|
|
|
8227
8290
|
}
|
|
8228
8291
|
async function readLockfile(projectRoot) {
|
|
8229
8292
|
try {
|
|
8230
|
-
const raw = await (0,
|
|
8293
|
+
const raw = await (0, import_promises7.readFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), "utf8");
|
|
8231
8294
|
return LockfileSchema.parse(JSON.parse(raw));
|
|
8232
8295
|
} catch {
|
|
8233
8296
|
return null;
|
|
8234
8297
|
}
|
|
8235
8298
|
}
|
|
8236
8299
|
async function writeLockfile(projectRoot, data) {
|
|
8237
|
-
await (0,
|
|
8300
|
+
await (0, import_promises7.writeFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
8238
8301
|
}
|
|
8239
8302
|
function upsertLockEntry(lock, name, entry) {
|
|
8240
8303
|
return {
|
|
@@ -8288,8 +8351,113 @@ async function promptForTool() {
|
|
|
8288
8351
|
rl.close();
|
|
8289
8352
|
}
|
|
8290
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
|
+
}
|
|
8291
8364
|
|
|
8292
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
|
+
}
|
|
8293
8461
|
async function installOnePackage(options) {
|
|
8294
8462
|
const configRoot = options.configRoot;
|
|
8295
8463
|
const installRoot = options.installRoot ?? configRoot;
|
|
@@ -8336,49 +8504,65 @@ async function installOnePackage(options) {
|
|
|
8336
8504
|
options.version,
|
|
8337
8505
|
options.token
|
|
8338
8506
|
);
|
|
8339
|
-
const
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
if (paths) installed[tool] = paths;
|
|
8355
|
-
}
|
|
8356
|
-
await writeLockfile(
|
|
8357
|
-
configRoot,
|
|
8358
|
-
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,
|
|
8359
8522
|
version: options.version,
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
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
|
+
}
|
|
8367
8551
|
}
|
|
8368
8552
|
|
|
8369
8553
|
// src/doctor.ts
|
|
8370
8554
|
var import_node_child_process3 = require("node:child_process");
|
|
8371
8555
|
var import_promises9 = require("node:fs/promises");
|
|
8372
|
-
var
|
|
8556
|
+
var import_node_path11 = require("node:path");
|
|
8373
8557
|
var import_node_util4 = require("node:util");
|
|
8374
8558
|
|
|
8375
8559
|
// src/project-root.ts
|
|
8376
8560
|
var import_node_os3 = require("node:os");
|
|
8377
|
-
var
|
|
8561
|
+
var import_node_path10 = require("node:path");
|
|
8378
8562
|
var import_node_process3 = require("node:process");
|
|
8379
8563
|
function globalConfigDir(env2 = process.env) {
|
|
8380
8564
|
const fromEnv = env2.AIPM_HOME?.trim();
|
|
8381
|
-
return fromEnv ? fromEnv.replace(/\/$/, "") : (0,
|
|
8565
|
+
return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path10.join)((0, import_node_os3.homedir)(), ".aipm");
|
|
8382
8566
|
}
|
|
8383
8567
|
function resolveConfigRoot(options = {}) {
|
|
8384
8568
|
return options.global ? globalConfigDir() : (0, import_node_process3.cwd)();
|
|
@@ -8409,11 +8593,11 @@ async function commandOutput(command, args) {
|
|
|
8409
8593
|
}
|
|
8410
8594
|
function npmGlobalBin(prefix) {
|
|
8411
8595
|
if (!prefix) return null;
|
|
8412
|
-
return process.platform === "win32" ? prefix : `${prefix}${
|
|
8596
|
+
return process.platform === "win32" ? prefix : `${prefix}${import_node_path11.sep}bin`;
|
|
8413
8597
|
}
|
|
8414
8598
|
function pathIncludes(pathDir) {
|
|
8415
8599
|
if (!pathDir) return false;
|
|
8416
|
-
return (process.env.PATH ?? "").split(
|
|
8600
|
+
return (process.env.PATH ?? "").split(import_node_path11.delimiter).includes(pathDir);
|
|
8417
8601
|
}
|
|
8418
8602
|
function shellPathHint(binDir) {
|
|
8419
8603
|
if (!binDir) return "Run npm prefix -g and add its bin folder to PATH.";
|
|
@@ -8466,11 +8650,11 @@ async function runDoctor(options) {
|
|
|
8466
8650
|
if (!options.publish) {
|
|
8467
8651
|
const configLabel = options.global ? "Global config" : "Project config";
|
|
8468
8652
|
try {
|
|
8469
|
-
await (0, import_promises9.access)((0,
|
|
8653
|
+
await (0, import_promises9.access)((0, import_node_path11.join)(configRoot, "aipm.package.json"));
|
|
8470
8654
|
checks.push({
|
|
8471
8655
|
name: configLabel,
|
|
8472
8656
|
ok: true,
|
|
8473
|
-
detail: `${(0,
|
|
8657
|
+
detail: `${(0, import_node_path11.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
|
|
8474
8658
|
});
|
|
8475
8659
|
} catch {
|
|
8476
8660
|
checks.push({
|
|
@@ -8540,9 +8724,9 @@ async function runDoctor(options) {
|
|
|
8540
8724
|
|
|
8541
8725
|
// src/version.ts
|
|
8542
8726
|
var import_node_fs2 = require("node:fs");
|
|
8543
|
-
var
|
|
8727
|
+
var import_node_path12 = require("node:path");
|
|
8544
8728
|
function getCliVersion() {
|
|
8545
|
-
const pkgPath = (0,
|
|
8729
|
+
const pkgPath = (0, import_node_path12.join)(__dirname, "..", "package.json");
|
|
8546
8730
|
const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
|
|
8547
8731
|
return pkg.version;
|
|
8548
8732
|
}
|
|
@@ -8681,7 +8865,7 @@ async function writeStarterFile(path2, content) {
|
|
|
8681
8865
|
}
|
|
8682
8866
|
async function inferEntryFromSource(source, fallback) {
|
|
8683
8867
|
const sourceInfo = await (0, import_promises10.stat)(source);
|
|
8684
|
-
if (sourceInfo.isFile()) return (0,
|
|
8868
|
+
if (sourceInfo.isFile()) return (0, import_node_path13.basename)(source);
|
|
8685
8869
|
if (!sourceInfo.isDirectory()) return fallback;
|
|
8686
8870
|
const entries = await (0, import_promises10.readdir)(source);
|
|
8687
8871
|
if (entries.includes(fallback)) return fallback;
|
|
@@ -8693,7 +8877,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
|
8693
8877
|
if (sourceInfo.isDirectory()) {
|
|
8694
8878
|
const entries = await (0, import_promises10.readdir)(source);
|
|
8695
8879
|
for (const entry of entries) {
|
|
8696
|
-
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), {
|
|
8697
8881
|
recursive: true,
|
|
8698
8882
|
force: false,
|
|
8699
8883
|
errorOnExist: true
|
|
@@ -8702,7 +8886,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
|
8702
8886
|
return;
|
|
8703
8887
|
}
|
|
8704
8888
|
if (sourceInfo.isFile()) {
|
|
8705
|
-
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 });
|
|
8706
8890
|
return;
|
|
8707
8891
|
}
|
|
8708
8892
|
throw new Error(`Unsupported source path: ${sourceLabel}`);
|
|
@@ -8735,6 +8919,73 @@ function packageDashboardUrl(name) {
|
|
|
8735
8919
|
if (!name) return DASHBOARD_URL;
|
|
8736
8920
|
return `${DASHBOARD_URL}/packages/${name.replace(/^@/, "")}`;
|
|
8737
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
|
+
}
|
|
8738
8989
|
async function openUrl(url) {
|
|
8739
8990
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
8740
8991
|
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
@@ -8873,6 +9124,11 @@ async function printPublishPreview(root, json) {
|
|
|
8873
9124
|
description: manifest.description,
|
|
8874
9125
|
targets: manifest.targets,
|
|
8875
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,
|
|
8876
9132
|
files: rows.map((row) => ({ path: row.path, size: row.size, changed: row.changed })),
|
|
8877
9133
|
totalBytes: size,
|
|
8878
9134
|
warnings: [
|
|
@@ -8890,6 +9146,13 @@ async function printPublishPreview(root, json) {
|
|
|
8890
9146
|
console.log(`Description: ${preview.description}`);
|
|
8891
9147
|
console.log(`Targets: ${preview.targets.join(", ")}`);
|
|
8892
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
|
+
}
|
|
8893
9156
|
console.log(`Total: ${preview.files.length} file${preview.files.length === 1 ? "" : "s"}, ${preview.totalBytes} bytes`);
|
|
8894
9157
|
for (const file of preview.files) {
|
|
8895
9158
|
console.log(`${file.changed ? "modified" : "staged"} ${file.path} (${file.size} bytes)`);
|
|
@@ -9003,9 +9266,9 @@ function buildStarterQualityMetadata(options) {
|
|
|
9003
9266
|
}
|
|
9004
9267
|
async function initSkill(opts) {
|
|
9005
9268
|
if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/name");
|
|
9006
|
-
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));
|
|
9007
9270
|
const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
|
|
9008
|
-
const source = opts.from ? (0,
|
|
9271
|
+
const source = opts.from ? (0, import_node_path13.resolve)(opts.from) : null;
|
|
9009
9272
|
const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
|
|
9010
9273
|
const template = parseSkillTemplate(opts.template ?? "blank");
|
|
9011
9274
|
const manifest = PackageManifestSchema.parse({
|
|
@@ -9028,15 +9291,15 @@ async function initSkill(opts) {
|
|
|
9028
9291
|
} else {
|
|
9029
9292
|
await (0, import_promises10.mkdir)(root, { recursive: true });
|
|
9030
9293
|
}
|
|
9031
|
-
await writeStarterFile((0,
|
|
9294
|
+
await writeStarterFile((0, import_node_path13.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
9032
9295
|
if (!opts.from) {
|
|
9033
9296
|
await writeStarterFile(
|
|
9034
|
-
(0,
|
|
9297
|
+
(0, import_node_path13.join)(root, entry),
|
|
9035
9298
|
SKILL_TEMPLATES[template](opts.name)
|
|
9036
9299
|
);
|
|
9037
9300
|
}
|
|
9038
9301
|
await writeStarterFile(
|
|
9039
|
-
(0,
|
|
9302
|
+
(0, import_node_path13.join)(root, ".aipmignore"),
|
|
9040
9303
|
[
|
|
9041
9304
|
"# Files that should never be published with this skill",
|
|
9042
9305
|
".env*",
|
|
@@ -9054,7 +9317,7 @@ async function initSkill(opts) {
|
|
|
9054
9317
|
""
|
|
9055
9318
|
].join("\n")
|
|
9056
9319
|
);
|
|
9057
|
-
await (0, import_promises10.mkdir)((0,
|
|
9320
|
+
await (0, import_promises10.mkdir)((0, import_node_path13.join)(root, ".aipm"), { recursive: true });
|
|
9058
9321
|
console.log(`Created ${opts.name} skill folder: ${root}`);
|
|
9059
9322
|
printPublishGuide(
|
|
9060
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.`,
|
|
@@ -9253,6 +9516,16 @@ program2.command("install").description("Install all packages from aipm.package.
|
|
|
9253
9516
|
});
|
|
9254
9517
|
}
|
|
9255
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
|
+
});
|
|
9256
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(
|
|
9257
9530
|
async (opts) => initSkill(opts)
|
|
9258
9531
|
);
|
|
@@ -9261,8 +9534,8 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
|
|
|
9261
9534
|
publish.help();
|
|
9262
9535
|
return;
|
|
9263
9536
|
}
|
|
9264
|
-
const abs = (0,
|
|
9265
|
-
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");
|
|
9266
9539
|
const manifest = PackageManifestSchema.parse(JSON.parse(manifestRaw));
|
|
9267
9540
|
const tarball = await packDirectory(abs);
|
|
9268
9541
|
const registry = registryFromEnvOrDefault(opts.registry);
|