@hasna/skills 0.1.70 → 0.1.72

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/bin/worker.js CHANGED
@@ -22940,7 +22940,6 @@ var init_dist_es9 = __esm(() => {
22940
22940
 
22941
22941
  // src/server/worker.ts
22942
22942
  import { randomUUID as randomUUID4 } from "crypto";
22943
-
22944
22943
  // src/lib/skill-bundle.ts
22945
22944
  var ANY_SEGMENT_EXCLUDES = new Set([
22946
22945
  ".git",
@@ -32876,13 +32875,12 @@ var DEFAULT_RUN_QUOTA = {
32876
32875
  };
32877
32876
 
32878
32877
  // src/server/database-url.ts
32879
- import { isAbsolute, join as join3 } from "path";
32878
+ import { isAbsolute, join as join5 } from "path";
32880
32879
  import { fileURLToPath } from "url";
32881
32880
 
32882
32881
  // src/lib/config.ts
32883
- import { existsSync, readFileSync as readFileSync2, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
32884
- import { join as join2, dirname as dirname2 } from "path";
32885
- import { homedir as homedir2 } from "os";
32882
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
32883
+ import { join as join4, dirname as dirname2 } from "path";
32886
32884
 
32887
32885
  // src/lib/retired-settings.ts
32888
32886
  var RETIRED_ENV_SUFFIXES = ["_STORAGE_MODE", "_DEPLOYMENT_MODE", "_CLOUD_MODE"];
@@ -32919,59 +32917,167 @@ function assertNoRetiredModeEnvVars(env, options) {
32919
32917
  throw new RetiredSettingError(names[0], `${names.join(", ")} ${names.length === 1 ? "is" : "are"} no longer read. ` + "Deployment modes were removed: where a server keeps its data is decided by the " + `database it is given, not by a declared label. Set ${options.replacement} to a ` + "postgres:// URL to use PostgreSQL, or leave it unset for the on-box SQLite database. " + `Then unset ${names.join(" and ")}. ` + "Refused rather than ignored, because a discarded setting looks exactly like a " + "working one until something needs the data.");
32920
32918
  }
32921
32919
 
32920
+ // src/lib/app-home.ts
32921
+ import { existsSync } from "fs";
32922
+ import { homedir as homedir3 } from "os";
32923
+ import { join as join3, resolve } from "path";
32924
+
32925
+ // ../../node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
32926
+ import { homedir as homedir2 } from "os";
32927
+ import { join as join2 } from "path";
32928
+ var KIND_ENV = {
32929
+ config: "HASNA_CONFIG_HOME",
32930
+ data: "HASNA_DATA_HOME",
32931
+ state: "HASNA_STATE_HOME",
32932
+ cache: "HASNA_CACHE_HOME"
32933
+ };
32934
+ var APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
32935
+ function assertApp(app) {
32936
+ if (typeof app !== "string" || app.length === 0) {
32937
+ throw new TypeError("paths: app must be a non-empty string");
32938
+ }
32939
+ if (!APP_SLUG_RE.test(app)) {
32940
+ throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
32941
+ }
32942
+ }
32943
+ function envOf(options) {
32944
+ return options.env ?? process.env;
32945
+ }
32946
+ function envValue(options, kind) {
32947
+ const value = envOf(options)[KIND_ENV[kind]];
32948
+ return typeof value === "string" && value.length > 0 ? value : undefined;
32949
+ }
32950
+ function isMacOS(platform) {
32951
+ return platform === "darwin";
32952
+ }
32953
+ function baseDir(kind, options) {
32954
+ const override = envValue(options, kind);
32955
+ if (override)
32956
+ return override;
32957
+ const home = options.home ?? homedir2();
32958
+ const platform = options.platform ?? process.platform;
32959
+ if (isMacOS(platform)) {
32960
+ switch (kind) {
32961
+ case "config":
32962
+ case "data":
32963
+ return join2(home, "Library", "Application Support", "Hasna");
32964
+ case "cache":
32965
+ return join2(home, "Library", "Caches", "Hasna");
32966
+ case "state":
32967
+ return join2(home, "Library", "Logs", "Hasna");
32968
+ }
32969
+ }
32970
+ switch (kind) {
32971
+ case "config":
32972
+ return join2(home, ".config", "hasna");
32973
+ case "data":
32974
+ return join2(home, ".local", "share", "hasna");
32975
+ case "state":
32976
+ return join2(home, ".local", "state", "hasna");
32977
+ case "cache":
32978
+ return join2(home, ".cache", "hasna");
32979
+ }
32980
+ }
32981
+ function resolvePath(kind, options) {
32982
+ assertApp(options.app);
32983
+ const appSegment = options.internal === true ? join2("internal", options.app) : options.app;
32984
+ return join2(baseDir(kind, options), appSegment);
32985
+ }
32986
+ function dataDir(options) {
32987
+ return resolvePath("data", options);
32988
+ }
32989
+
32990
+ // src/lib/app-home.ts
32991
+ var DATA_DIR_ENV = "HASNA_SKILLS_DIR";
32992
+ var HASNA_SKILLS_HOME_ENV = "HASNA_SKILLS_HOME";
32993
+ var SKILLS_HOME_ENV = "SKILLS_HOME";
32994
+ var DEFAULT_SQLITE_FILENAME = "server.db";
32995
+ var GLOBAL_CONFIG_FILENAME = "config.json";
32996
+ function effectiveHome() {
32997
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir3() || "/tmp";
32998
+ }
32999
+ function legacyDataRoot() {
33000
+ return join3(effectiveHome(), ".hasna", "skills");
33001
+ }
33002
+ function resolverDataRoot(home = effectiveHome()) {
33003
+ return dataDir({ app: "skills", home });
33004
+ }
33005
+ function adoptResolverDataRoot(resolved, env = process.env) {
33006
+ const dataOverride = env.HASNA_DATA_HOME;
33007
+ if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
33008
+ return true;
33009
+ return existsSync(join3(resolved, DEFAULT_SQLITE_FILENAME)) || existsSync(join3(resolved, GLOBAL_CONFIG_FILENAME));
33010
+ }
33011
+ function exactDataRoot() {
33012
+ for (const key of [DATA_DIR_ENV, HASNA_SKILLS_HOME_ENV, SKILLS_HOME_ENV]) {
33013
+ const dir = process.env[key]?.trim();
33014
+ if (dir)
33015
+ return resolve(dir);
33016
+ }
33017
+ return;
33018
+ }
33019
+ function hasExactOverride(env = process.env) {
33020
+ return Boolean(env[DATA_DIR_ENV]?.trim()) || Boolean(env[HASNA_SKILLS_HOME_ENV]?.trim()) || Boolean(env[SKILLS_HOME_ENV]?.trim());
33021
+ }
33022
+ function hasOperatorOverride(env = process.env) {
33023
+ return hasExactOverride(env) || Boolean(env.HASNA_DATA_HOME?.trim());
33024
+ }
33025
+ function getDataRoot() {
33026
+ const exact = exactDataRoot();
33027
+ if (exact)
33028
+ return exact;
33029
+ const resolved = resolverDataRoot();
33030
+ return adoptResolverDataRoot(resolved) ? resolve(resolved) : resolve(legacyDataRoot());
33031
+ }
32922
33032
  // src/lib/config.ts
32923
33033
  function mergeDirectoryContents(sourceDir, targetDir) {
32924
- if (!existsSync(sourceDir))
33034
+ if (!existsSync2(sourceDir))
32925
33035
  return;
32926
33036
  mkdirSync(targetDir, { recursive: true });
32927
33037
  for (const entry of readdirSync(sourceDir)) {
32928
- const sourcePath = join2(sourceDir, entry);
32929
- const targetPath = join2(targetDir, entry);
33038
+ const sourcePath = join4(sourceDir, entry);
33039
+ const targetPath = join4(targetDir, entry);
32930
33040
  try {
32931
33041
  const sourceStat = statSync(sourcePath);
32932
33042
  if (sourceStat.isDirectory()) {
32933
33043
  mergeDirectoryContents(sourcePath, targetPath);
32934
33044
  continue;
32935
33045
  }
32936
- if (!existsSync(targetPath))
33046
+ if (!existsSync2(targetPath))
32937
33047
  copyFileSync(sourcePath, targetPath);
32938
33048
  } catch {}
32939
33049
  }
32940
33050
  }
32941
- var DATA_DIR_ENV = "HASNA_SKILLS_DIR";
32942
33051
  function getDataDir() {
32943
- const override = process.env[DATA_DIR_ENV];
32944
- if (override) {
32945
- try {
32946
- mkdirSync(override, { recursive: true });
32947
- } catch {}
32948
- return override;
32949
- }
32950
- const home = process.env["HOME"] || process.env["USERPROFILE"] || homedir2();
32951
- const newDir = join2(home, ".hasna", "skills");
32952
- const oldDir = join2(home, ".skills");
32953
- const oldConfigFile = join2(home, ".skillsrc");
32954
- mkdirSync(newDir, { recursive: true });
33052
+ const root3 = getDataRoot();
33053
+ try {
33054
+ mkdirSync(root3, { recursive: true });
33055
+ } catch {}
33056
+ if (hasOperatorOverride())
33057
+ return root3;
33058
+ const home = effectiveHome();
33059
+ const oldDir = join4(home, ".skills");
33060
+ const oldConfigFile = join4(home, ".skillsrc");
32955
33061
  try {
32956
- mergeDirectoryContents(oldDir, newDir);
33062
+ mergeDirectoryContents(oldDir, root3);
32957
33063
  } catch {}
32958
- if (existsSync(oldConfigFile) && !existsSync(join2(newDir, "config.json"))) {
33064
+ if (existsSync2(oldConfigFile) && !existsSync2(join4(root3, "config.json"))) {
32959
33065
  try {
32960
- copyFileSync(oldConfigFile, join2(newDir, "config.json"));
33066
+ copyFileSync(oldConfigFile, join4(root3, "config.json"));
32961
33067
  } catch {}
32962
33068
  }
32963
- return newDir;
33069
+ return root3;
32964
33070
  }
32965
33071
 
32966
33072
  // src/server/database-url.ts
32967
- var DEFAULT_SQLITE_FILENAME = "server.db";
33073
+ var DEFAULT_SQLITE_FILENAME2 = "server.db";
32968
33074
  var SQLITE_MEMORY_PATH = ":memory:";
32969
33075
  var POSTGRES_SCHEMES = new Set(["postgres", "postgresql"]);
32970
33076
  var SQLITE_SCHEMES = new Set(["sqlite", "sqlite3", "file"]);
32971
33077
  var MEMORY_SCHEMES = new Set(["memory"]);
32972
33078
  var SQLITE_EXTENSIONS = [".db", ".sqlite", ".sqlite3", ".db3"];
32973
33079
  function defaultSqlitePath() {
32974
- return join3(getDataDir(), DEFAULT_SQLITE_FILENAME);
33080
+ return join5(getDataDir(), DEFAULT_SQLITE_FILENAME2);
32975
33081
  }
32976
33082
  function resolveDatabaseTarget(raw) {
32977
33083
  const value = raw?.trim();
@@ -32998,7 +33104,7 @@ function resolveDatabaseTarget(raw) {
32998
33104
  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()}.`);
32999
33105
  }
33000
33106
  if (looksLikeSqlitePath(value)) {
33001
- const path = isAbsolute(value) ? value : join3(process.cwd(), value);
33107
+ const path = isAbsolute(value) ? value : join5(process.cwd(), value);
33002
33108
  return { kind: "sqlite", path, durable: true, label: `sqlite (${path})` };
33003
33109
  }
33004
33110
  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()}.`);
@@ -33022,7 +33128,7 @@ function sqlitePathFromUrl(value, scheme) {
33022
33128
  }
33023
33129
  return rest.slice(2).replace(/^\/\/+/, "/");
33024
33130
  }
33025
- return isAbsolute(rest) ? rest : join3(process.cwd(), rest);
33131
+ return isAbsolute(rest) ? rest : join5(process.cwd(), rest);
33026
33132
  }
33027
33133
  function looksLikeSqlitePath(value) {
33028
33134
  if (value.includes("/"))
@@ -33034,7 +33140,7 @@ function looksLikeSqlitePath(value) {
33034
33140
  import { Database } from "bun:sqlite";
33035
33141
  import { randomUUID as randomUUID2 } from "crypto";
33036
33142
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
33037
- import { dirname as dirname4, join as join5 } from "path";
33143
+ import { dirname as dirname4, join as join7 } from "path";
33038
33144
 
33039
33145
  // src/server/auth.ts
33040
33146
  import { createHash as createHash3 } from "crypto";
@@ -33055,16 +33161,16 @@ function publicPrincipal(partial = {}) {
33055
33161
  }
33056
33162
 
33057
33163
  // src/server/migrations-dir.ts
33058
- import { existsSync as existsSync2 } from "fs";
33059
- import { dirname as dirname3, join as join4 } from "path";
33164
+ import { existsSync as existsSync3 } from "fs";
33165
+ import { dirname as dirname3, join as join6 } from "path";
33060
33166
  var MIGRATION_DIALECTS = ["postgres", "sqlite"];
33061
33167
  var MAX_WALK_UP = 6;
33062
33168
  function findMigrationsRoot(startDirs = defaultStartDirs()) {
33063
33169
  for (const start of startDirs) {
33064
33170
  let dir = start;
33065
33171
  for (let level = 0;level < MAX_WALK_UP; level += 1) {
33066
- const candidate = join4(dir, "migrations");
33067
- if (MIGRATION_DIALECTS.some((dialect) => existsSync2(join4(candidate, dialect))))
33172
+ const candidate = join6(dir, "migrations");
33173
+ if (MIGRATION_DIALECTS.some((dialect) => existsSync3(join6(candidate, dialect))))
33068
33174
  return candidate;
33069
33175
  const parent = dirname3(dir);
33070
33176
  if (parent === dir)
@@ -33078,8 +33184,8 @@ function resolveMigrationsDir(dialect, root3 = findMigrationsRoot()) {
33078
33184
  if (!root3) {
33079
33185
  throw new Error(`could not locate the migrations directory for dialect "${dialect}". ` + `Expected a migrations/${dialect}/ folder alongside the package root.`);
33080
33186
  }
33081
- const dir = join4(root3, dialect);
33082
- if (!existsSync2(dir)) {
33187
+ const dir = join6(root3, dialect);
33188
+ if (!existsSync3(dir)) {
33083
33189
  throw new Error(`migrations directory not found: ${dir}`);
33084
33190
  }
33085
33191
  return dir;
@@ -33823,7 +33929,7 @@ function applySqliteMigrations(db, migrationsDir = resolveMigrationsDir("sqlite"
33823
33929
  const appliedNow = [];
33824
33930
  for (const file of files) {
33825
33931
  const version2 = file.replace(/\.sql$/, "");
33826
- const text = readFileSync3(join5(migrationsDir, file), "utf8");
33932
+ const text = readFileSync3(join7(migrationsDir, file), "utf8");
33827
33933
  const apply = db.transaction(() => {
33828
33934
  const already = db.query("SELECT 1 AS present FROM schema_migrations WHERE version = ? LIMIT 1").get(version2);
33829
33935
  if (already)
@@ -35727,19 +35833,19 @@ var FIRST_SEGMENT_COPY_EXCLUDES = new Set([
35727
35833
  // src/lib/portable-skills.ts
35728
35834
  var OFFICIAL_SKILL_NAMES = new Set(SKILLS.map((skill) => skill.name));
35729
35835
  // src/lib/installer.ts
35730
- import { existsSync as existsSync3, readFileSync as readFileSync4, rmSync } from "fs";
35731
- import { dirname as dirname5, join as join6 } from "path";
35836
+ import { existsSync as existsSync4, readFileSync as readFileSync4, rmSync } from "fs";
35837
+ import { dirname as dirname5, join as join8 } from "path";
35732
35838
  import { fileURLToPath as fileURLToPath2 } from "url";
35733
35839
  var __dirname2 = dirname5(fileURLToPath2(import.meta.url));
35734
35840
  function findSkillsDir() {
35735
35841
  let dir = __dirname2;
35736
35842
  for (let i3 = 0;i3 < 5; i3++) {
35737
- const candidate = join6(dir, "skills");
35738
- if (existsSync3(candidate) && !dir.includes(".skills"))
35843
+ const candidate = join8(dir, "skills");
35844
+ if (existsSync4(candidate) && !dir.includes(".skills"))
35739
35845
  return candidate;
35740
35846
  dir = dirname5(dir);
35741
35847
  }
35742
- return join6(__dirname2, "..", "skills");
35848
+ return join8(__dirname2, "..", "skills");
35743
35849
  }
35744
35850
  var SKILLS_DIR = findSkillsDir();
35745
35851
 
@@ -35794,13 +35900,13 @@ if (import.meta.main) {
35794
35900
  console.error(`worker ${workerId}: claim/execute failed (${consecutiveErrors} in a row):`, error.message);
35795
35901
  if (once)
35796
35902
  process.exit(1);
35797
- await new Promise((resolve) => setTimeout(resolve, Math.min(pause * consecutiveErrors, 30000)));
35903
+ await new Promise((resolve2) => setTimeout(resolve2, Math.min(pause * consecutiveErrors, 30000)));
35798
35904
  continue;
35799
35905
  }
35800
35906
  if (once)
35801
35907
  process.exit(processed ? 0 : 2);
35802
35908
  if (!processed)
35803
- await new Promise((resolve) => setTimeout(resolve, pause));
35909
+ await new Promise((resolve2) => setTimeout(resolve2, pause));
35804
35910
  } while (true);
35805
35911
  }
35806
35912
  export {
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerHydrate(parent: Command): void;
package/dist/index.d.ts CHANGED
@@ -34,3 +34,6 @@ export { getFeedbackDbPath, saveFeedback, type FeedbackCategory, type FeedbackIn
34
34
  export { SKILLS_NATIVE_STORAGE_ENV, SKILLS_NATIVE_STORAGE_FALLBACK_ENV, SKILLS_STORAGE_ENV, SKILLS_STORAGE_FALLBACK_ENV, SKILLS_STORAGE_TABLES, STORAGE_TABLES, SkillsPostgresSyncStore, SkillsS3ObjectStore, buildSkillsS3ObjectUrl, createSkillsPostgresSyncStore, createSkillsS3ObjectStore, createSkillsSnapshotSyncRecord, exportSkillsLocalSnapshot, getSkillsNativeStorageStatus, getSkillsStorageDatabaseEnv, getSkillsStorageDatabaseUrl, getSkillsStorageStatus, getStorageDatabaseEnv, getStorageDatabaseUrl, getStorageStatus, importSkillsLocalSnapshot, planSkillsS3SnapshotUpload, resolveSkillsNativeStorageConfig, resolveStorageConfig, signSkillsAwsV4Request, skillsPostgresSyncSchemaSql, storageCapabilities, uploadSkillsSnapshotFilesToS3, type AwsCredentials, type SignSkillsAwsV4RequestOptions, type SkillsFetch, type SkillsLocalSnapshot, type SkillsNativeStorageConfig, type SkillsNativeStorageStatus, type SkillsPostgresQueryClient, type SkillsS3ObjectStoreOptions, type SkillsS3PutObjectOptions, type SkillsS3SnapshotPlanEntry, type SkillsS3StoredObject, type SkillsSnapshotFile, type SkillsStorageTable, type SkillsSyncRecord, } from "./lib/native-storage.js";
35
35
  export { SKILL_ALIASES, normalizeSkillSlug, resolveSkillAlias, type SkillAlias, } from "./lib/skill-aliases.js";
36
36
  export type { SkillResponse, SkillDetailResponse, CategoryResponse, TagResponse, InstallResponse, RemoveResponse, VersionResponse, ExportResponse, ImportResponse, SearchResponse, CategoryInstallResponse, ErrorResponse, } from "./types/api.js";
37
+ export { STATION_SYNC_MANIFEST_SCHEMA, StationSnapshotError, sha256File, planStationSnapshot, validateStationId, writeStationSnapshot, type PortableSnapshotFile, type ScannedHome, type SnapshotPlan, type StationSnapshotErrorCode, type StationSnapshotManifestFile, type StationSnapshotOptions, type StationSnapshotResult, } from "./lib/station-snapshot.js";
38
+ export { STATION_HYDRATION_MANIFEST_SCHEMA, planStationHydration, writeStationHydration, type HydrationCandidate, type HydrationWinnerFile, type HydrationWinnerSkill, type StationHydrationOptions, type StationHydrationResult, } from "./lib/station-hydrate.js";
39
+ export { REFUSED_SCANNER_FLAGGED, SYNC_HOMES, destinationFor, homePathFor, isExcludedSkillFileName, isPortableWithinSkill, isRegularFile, walkEntries, type SyncHomeDefinition, type WalkEntry, } from "./lib/portable-snapshot-filter.js";