@hasna/skills 0.7.3 → 0.8.1
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 +161 -19
- package/bin/index.js +5321 -4850
- package/bin/mcp.js +7841 -7538
- package/bin/migrate.js +22 -60
- package/bin/server.js +73576 -20339
- package/bin/worker.js +65159 -19251
- package/dist/admin-contract.js +8 -8
- package/dist/cli/commands/recurring.d.ts +2 -0
- package/dist/index.js +2617 -2747
- package/dist/lib/canonical-json.d.ts +8 -0
- package/dist/lib/cli-mcp-parity.d.ts +1 -1
- package/dist/lib/config.d.ts +3 -23
- package/dist/lib/installer.d.ts +1 -1
- package/dist/lib/mcp-contracts.d.ts +1 -1
- package/dist/lib/portable-skills.d.ts +1 -1
- package/dist/lib/recurring-customer.d.ts +29 -0
- package/dist/lib/recurring-recovery.d.ts +82 -0
- package/dist/lib/recurring-surface.d.ts +252 -0
- package/dist/lib/registry-data/index.d.ts +1 -0
- package/dist/lib/registry-types.d.ts +2 -1
- package/dist/lib/registry.d.ts +2 -12
- package/dist/lib/release-dependencies.d.ts +3 -0
- package/dist/lib/remote-client.d.ts +13 -0
- package/dist/lib/remote-customer-operations.d.ts +1 -0
- package/dist/lib/remote-invitations.d.ts +1 -1
- package/dist/lib/remote-recurring-contract.d.ts +165 -0
- package/dist/lib/remote-recurring.d.ts +95 -0
- package/dist/lib/skill-aliases.d.ts +2 -3
- package/dist/mcp/mcp-test-client.d.ts +2 -2
- package/dist/mcp/remote-recurring-tools.d.ts +2 -0
- package/dist/sdk/executor.d.ts +5 -5
- package/dist/sdk/index.d.ts +4 -0
- package/dist/sdk/index.js +114864 -46432
- package/dist/sdk/registry.d.ts +3 -3
- package/dist/server/config.d.ts +1 -2
- package/dist/server/handlers.d.ts +5 -2
- package/dist/server/registry.d.ts +6 -3
- package/dist/server/skills-api.d.ts +6 -38
- package/dist/storage.js +5 -42
- package/package.json +3 -2
- package/dist/lib/registry-data/business-marketing.d.ts +0 -2
- package/dist/lib/registry-data/communication.d.ts +0 -2
- package/dist/lib/registry-data/content-generation.d.ts +0 -2
- package/dist/lib/registry-data/data-analysis.d.ts +0 -2
- package/dist/lib/registry-data/design-branding.d.ts +0 -2
- package/dist/lib/registry-data/development-tools.d.ts +0 -2
- package/dist/lib/registry-data/education-learning.d.ts +0 -2
- package/dist/lib/registry-data/event-management.d.ts +0 -2
- package/dist/lib/registry-data/finance-compliance.d.ts +0 -2
- package/dist/lib/registry-data/health-wellness.d.ts +0 -2
- package/dist/lib/registry-data/media-processing.d.ts +0 -2
- package/dist/lib/registry-data/productivity-organization.d.ts +0 -2
- package/dist/lib/registry-data/project-management.d.ts +0 -2
- package/dist/lib/registry-data/research-writing.d.ts +0 -2
- package/dist/lib/registry-data/science-academic.d.ts +0 -2
- package/dist/lib/registry-data/travel-lifestyle.d.ts +0 -2
- package/dist/lib/registry-data/web-browser.d.ts +0 -2
- package/dist/server/seed-bundled.d.ts +0 -20
package/bin/migrate.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/server/migrate.ts
|
|
5
|
-
import { existsSync as existsSync4, readdirSync as
|
|
6
|
-
import { join as
|
|
5
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
|
|
6
|
+
import { join as join5 } from "path";
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "@hasna/skills",
|
|
10
|
-
version: "0.
|
|
10
|
+
version: "0.8.1",
|
|
11
11
|
description: "Skills library for AI coding agents",
|
|
12
12
|
type: "module",
|
|
13
13
|
bin: {
|
|
@@ -92,7 +92,8 @@ var package_default = {
|
|
|
92
92
|
author: "Hasna",
|
|
93
93
|
license: "Apache-2.0",
|
|
94
94
|
devDependencies: {
|
|
95
|
-
"@hasna/contracts": "1.0
|
|
95
|
+
"@hasna/contracts": "1.1.0",
|
|
96
|
+
"@hasna/secrets": "0.4.2",
|
|
96
97
|
"@types/bun": "1.3.14",
|
|
97
98
|
"@types/node": "25.2.3",
|
|
98
99
|
"@types/react": "^18.2.0",
|
|
@@ -189,7 +190,6 @@ function resolveServerConfig(env = process.env) {
|
|
|
189
190
|
port,
|
|
190
191
|
databaseUrl: env[DATABASE_URL_ENV] || env.DATABASE_URL || undefined,
|
|
191
192
|
bootstrapApiKey: env.HASNA_SKILLS_BOOTSTRAP_API_KEY || undefined,
|
|
192
|
-
seedBundledCorpus: (env.HASNA_SKILLS_SEED_BUNDLED_CORPUS ?? "1") !== "0",
|
|
193
193
|
artifactBucket: env.HASNA_SKILLS_S3_BUCKET || env.SKILLS_S3_BUCKET || undefined,
|
|
194
194
|
artifactPrefix: normalizePrefix(env.HASNA_SKILLS_S3_PREFIX || env.SKILLS_S3_PREFIX || "skills/artifacts"),
|
|
195
195
|
runArtifactPrefix: env.HASNA_SKILLS_S3_RUN_PREFIX ? normalizePrefix(env.HASNA_SKILLS_S3_RUN_PREFIX) : undefined,
|
|
@@ -216,12 +216,11 @@ function normalizePrefix(value) {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
// src/server/database-url.ts
|
|
219
|
-
import { isAbsolute, join as
|
|
219
|
+
import { isAbsolute, join as join2 } from "path";
|
|
220
220
|
import { fileURLToPath } from "url";
|
|
221
221
|
|
|
222
222
|
// src/lib/config.ts
|
|
223
|
-
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync
|
|
224
|
-
import { join as join2, dirname } from "path";
|
|
223
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
225
224
|
|
|
226
225
|
// src/lib/app-home.ts
|
|
227
226
|
import { existsSync } from "fs";
|
|
@@ -281,12 +280,6 @@ function exactDataRoot() {
|
|
|
281
280
|
}
|
|
282
281
|
return;
|
|
283
282
|
}
|
|
284
|
-
function hasExactOverride(env = process.env) {
|
|
285
|
-
return Boolean(env[DATA_DIR_ENV]?.trim()) || Boolean(env[HASNA_SKILLS_HOME_ENV]?.trim()) || Boolean(env[SKILLS_HOME_ENV]?.trim());
|
|
286
|
-
}
|
|
287
|
-
function hasOperatorOverride(env = process.env) {
|
|
288
|
-
return hasExactOverride(env) || Boolean(env.HASNA_DATA_HOME?.trim());
|
|
289
|
-
}
|
|
290
283
|
function getDataRoot() {
|
|
291
284
|
const exact = exactDataRoot();
|
|
292
285
|
if (exact)
|
|
@@ -295,42 +288,11 @@ function getDataRoot() {
|
|
|
295
288
|
return adoptResolverDataRoot(resolved) ? resolve(resolved) : resolve(legacyDataRoot());
|
|
296
289
|
}
|
|
297
290
|
// src/lib/config.ts
|
|
298
|
-
function mergeDirectoryContents(sourceDir, targetDir) {
|
|
299
|
-
if (!existsSync2(sourceDir))
|
|
300
|
-
return;
|
|
301
|
-
mkdirSync(targetDir, { recursive: true });
|
|
302
|
-
for (const entry of readdirSync(sourceDir)) {
|
|
303
|
-
const sourcePath = join2(sourceDir, entry);
|
|
304
|
-
const targetPath = join2(targetDir, entry);
|
|
305
|
-
try {
|
|
306
|
-
const sourceStat = statSync(sourcePath);
|
|
307
|
-
if (sourceStat.isDirectory()) {
|
|
308
|
-
mergeDirectoryContents(sourcePath, targetPath);
|
|
309
|
-
continue;
|
|
310
|
-
}
|
|
311
|
-
if (!existsSync2(targetPath))
|
|
312
|
-
copyFileSync(sourcePath, targetPath);
|
|
313
|
-
} catch {}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
291
|
function getDataDir() {
|
|
317
292
|
const root = getDataRoot();
|
|
318
293
|
try {
|
|
319
294
|
mkdirSync(root, { recursive: true });
|
|
320
295
|
} catch {}
|
|
321
|
-
if (hasOperatorOverride())
|
|
322
|
-
return root;
|
|
323
|
-
const home = effectiveHome();
|
|
324
|
-
const oldDir = join2(home, ".skills");
|
|
325
|
-
const oldConfigFile = join2(home, ".skillsrc");
|
|
326
|
-
try {
|
|
327
|
-
mergeDirectoryContents(oldDir, root);
|
|
328
|
-
} catch {}
|
|
329
|
-
if (existsSync2(oldConfigFile) && !existsSync2(join2(root, "config.json"))) {
|
|
330
|
-
try {
|
|
331
|
-
copyFileSync(oldConfigFile, join2(root, "config.json"));
|
|
332
|
-
} catch {}
|
|
333
|
-
}
|
|
334
296
|
return root;
|
|
335
297
|
}
|
|
336
298
|
|
|
@@ -342,7 +304,7 @@ var SQLITE_SCHEMES = new Set(["sqlite", "sqlite3", "file"]);
|
|
|
342
304
|
var MEMORY_SCHEMES = new Set(["memory"]);
|
|
343
305
|
var SQLITE_EXTENSIONS = [".db", ".sqlite", ".sqlite3", ".db3"];
|
|
344
306
|
function defaultSqlitePath() {
|
|
345
|
-
return
|
|
307
|
+
return join2(getDataDir(), DEFAULT_SQLITE_FILENAME2);
|
|
346
308
|
}
|
|
347
309
|
function resolveDatabaseTarget(raw) {
|
|
348
310
|
const value = raw?.trim();
|
|
@@ -369,7 +331,7 @@ function resolveDatabaseTarget(raw) {
|
|
|
369
331
|
throw new Error(`unsupported database scheme "${scheme}:". Supported: postgres://, postgresql://, sqlite:, file:, ` + `an absolute or relative path to a .db/.sqlite file, ":memory:", or "memory:" (non-durable, tests only). ` + `Leave the setting empty to use the default SQLite database at ${defaultSqlitePath()}.`);
|
|
370
332
|
}
|
|
371
333
|
if (looksLikeSqlitePath(value)) {
|
|
372
|
-
const path = isAbsolute(value) ? value :
|
|
334
|
+
const path = isAbsolute(value) ? value : join2(process.cwd(), value);
|
|
373
335
|
return { kind: "sqlite", path, durable: true, label: `sqlite (${path})` };
|
|
374
336
|
}
|
|
375
337
|
throw new Error(`could not resolve a database backend from "${value}". Use postgres://\u2026, sqlite:\u2026, an absolute or ` + `relative path ending in ${SQLITE_EXTENSIONS.join("/")}, ":memory:", or leave it empty for the ` + `default SQLite database at ${defaultSqlitePath()}.`);
|
|
@@ -393,7 +355,7 @@ function sqlitePathFromUrl(value, scheme) {
|
|
|
393
355
|
}
|
|
394
356
|
return rest.slice(2).replace(/^\/\/+/, "/");
|
|
395
357
|
}
|
|
396
|
-
return isAbsolute(rest) ? rest :
|
|
358
|
+
return isAbsolute(rest) ? rest : join2(process.cwd(), rest);
|
|
397
359
|
}
|
|
398
360
|
function looksLikeSqlitePath(value) {
|
|
399
361
|
if (value.includes("/"))
|
|
@@ -403,17 +365,17 @@ function looksLikeSqlitePath(value) {
|
|
|
403
365
|
|
|
404
366
|
// src/server/migrations-dir.ts
|
|
405
367
|
import { existsSync as existsSync3 } from "fs";
|
|
406
|
-
import { dirname
|
|
368
|
+
import { dirname, join as join3 } from "path";
|
|
407
369
|
var MIGRATION_DIALECTS = ["postgres", "sqlite"];
|
|
408
370
|
var MAX_WALK_UP = 6;
|
|
409
371
|
function findMigrationsRoot(startDirs = defaultStartDirs()) {
|
|
410
372
|
for (const start of startDirs) {
|
|
411
373
|
let dir = start;
|
|
412
374
|
for (let level = 0;level < MAX_WALK_UP; level += 1) {
|
|
413
|
-
const candidate =
|
|
414
|
-
if (MIGRATION_DIALECTS.some((dialect) => existsSync3(
|
|
375
|
+
const candidate = join3(dir, "migrations");
|
|
376
|
+
if (MIGRATION_DIALECTS.some((dialect) => existsSync3(join3(candidate, dialect))))
|
|
415
377
|
return candidate;
|
|
416
|
-
const parent =
|
|
378
|
+
const parent = dirname(dir);
|
|
417
379
|
if (parent === dir)
|
|
418
380
|
break;
|
|
419
381
|
dir = parent;
|
|
@@ -425,7 +387,7 @@ function resolveMigrationsDir(dialect, root = findMigrationsRoot()) {
|
|
|
425
387
|
if (!root) {
|
|
426
388
|
throw new Error(`could not locate the migrations directory for dialect "${dialect}". ` + `Expected a migrations/${dialect}/ folder alongside the package root.`);
|
|
427
389
|
}
|
|
428
|
-
const dir =
|
|
390
|
+
const dir = join3(root, dialect);
|
|
429
391
|
if (!existsSync3(dir)) {
|
|
430
392
|
throw new Error(`migrations directory not found: ${dir}`);
|
|
431
393
|
}
|
|
@@ -540,8 +502,8 @@ class SqliteSkillSelectionStore {
|
|
|
540
502
|
|
|
541
503
|
// src/server/sqlite-store.ts
|
|
542
504
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
543
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync
|
|
544
|
-
import { dirname as
|
|
505
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync } from "fs";
|
|
506
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
545
507
|
|
|
546
508
|
// src/server/auth.ts
|
|
547
509
|
import { createHash } from "crypto";
|
|
@@ -1101,7 +1063,7 @@ class SqliteSkillsStore {
|
|
|
1101
1063
|
constructor(path = SQLITE_MEMORY_PATH, options = {}) {
|
|
1102
1064
|
const inMemory = path === SQLITE_MEMORY_PATH;
|
|
1103
1065
|
if (!inMemory)
|
|
1104
|
-
mkdirSync2(
|
|
1066
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
1105
1067
|
this.db = openDatabase(path);
|
|
1106
1068
|
this.db.exec(`PRAGMA busy_timeout = ${Math.max(0, options.busyTimeoutMs ?? 5000)}`);
|
|
1107
1069
|
if (!inMemory)
|
|
@@ -1654,14 +1616,14 @@ function applySqliteMigrations(db, migrationsDir = resolveMigrationsDir("sqlite"
|
|
|
1654
1616
|
version text PRIMARY KEY,
|
|
1655
1617
|
applied_at text NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
1656
1618
|
)`);
|
|
1657
|
-
const files =
|
|
1619
|
+
const files = readdirSync(migrationsDir).filter((name) => name.endsWith(".sql")).sort();
|
|
1658
1620
|
if (files.length === 0) {
|
|
1659
1621
|
throw new Error(`no .sql migrations found in ${migrationsDir}`);
|
|
1660
1622
|
}
|
|
1661
1623
|
const appliedNow = [];
|
|
1662
1624
|
for (const file of files) {
|
|
1663
1625
|
const version = file.replace(/\.sql$/, "");
|
|
1664
|
-
const text = readFileSync2(
|
|
1626
|
+
const text = readFileSync2(join4(migrationsDir, file), "utf8");
|
|
1665
1627
|
const apply = db.transaction(() => {
|
|
1666
1628
|
const already = db.query("SELECT 1 AS present FROM schema_migrations WHERE version = ? LIMIT 1").get(version);
|
|
1667
1629
|
if (already)
|
|
@@ -1743,14 +1705,14 @@ async function runPostgresMigrations(databaseUrl, migrationsDir) {
|
|
|
1743
1705
|
const appliedRows = await sql`SELECT version FROM schema_migrations`;
|
|
1744
1706
|
const applied = new Set(appliedRows.map((row) => row.version));
|
|
1745
1707
|
const appliedNow = [];
|
|
1746
|
-
const files =
|
|
1708
|
+
const files = readdirSync2(migrationsDir).filter((name) => name.endsWith(".sql")).sort();
|
|
1747
1709
|
if (files.length === 0)
|
|
1748
1710
|
throw new Error(`no .sql migrations found in ${migrationsDir}`);
|
|
1749
1711
|
for (const file of files) {
|
|
1750
1712
|
const version = file.replace(/\.sql$/, "");
|
|
1751
1713
|
if (applied.has(version))
|
|
1752
1714
|
continue;
|
|
1753
|
-
const sqlText = readFileSync3(
|
|
1715
|
+
const sqlText = readFileSync3(join5(migrationsDir, file), "utf8");
|
|
1754
1716
|
await sql.unsafe("BEGIN");
|
|
1755
1717
|
try {
|
|
1756
1718
|
await sql.unsafe(sqlText);
|