@hasna/skills 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -36
- package/bin/index.js +2151 -650
- package/bin/mcp.js +1787 -625
- package/bin/migrate.js +5 -0
- package/bin/server.js +365 -198
- package/bin/worker.js +346 -179
- package/dist/cli/cli.test-utils.d.ts +14 -0
- package/dist/cli/commands/publish.d.ts +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1100 -152
- package/dist/lib/api-url.d.ts +32 -23
- package/dist/lib/auth-store.d.ts +111 -42
- package/dist/lib/config.d.ts +19 -21
- package/dist/lib/feedback.d.ts +8 -3
- package/dist/lib/fleet-credentials.d.ts +246 -0
- package/dist/lib/portable-skills-files.d.ts +9 -0
- package/dist/lib/portable-skills.d.ts +2 -2
- package/dist/lib/remote-client.d.ts +20 -10
- package/dist/lib/remote-registry.d.ts +31 -11
- package/dist/lib/run-routing.d.ts +6 -4
- package/dist/lib/vendor-host-policy.d.ts +31 -0
- package/dist/sdk/index.d.ts +6 -0
- package/dist/sdk/index.js +1738 -572
- package/dist/server/artifact-storage.d.ts +11 -0
- package/dist/server/skills-api.d.ts +13 -2
- package/dist/storage.js +9 -14
- package/package.json +3 -1
package/bin/index.js
CHANGED
|
@@ -36860,12 +36860,13 @@ var package_default;
|
|
|
36860
36860
|
var init_package = __esm(() => {
|
|
36861
36861
|
package_default = {
|
|
36862
36862
|
name: "@hasna/skills",
|
|
36863
|
-
version: "0.
|
|
36863
|
+
version: "0.3.0",
|
|
36864
36864
|
description: "Skills library for AI coding agents",
|
|
36865
36865
|
type: "module",
|
|
36866
36866
|
bin: {
|
|
36867
36867
|
skills: "bin/index.js",
|
|
36868
36868
|
"skills-mcp": "bin/mcp.js",
|
|
36869
|
+
"skills-serve": "bin/server.js",
|
|
36869
36870
|
"skills-server": "bin/server.js",
|
|
36870
36871
|
"skills-worker": "bin/worker.js",
|
|
36871
36872
|
"skills-migrate": "bin/migrate.js"
|
|
@@ -36951,6 +36952,7 @@ var init_package = __esm(() => {
|
|
|
36951
36952
|
dependencies: {
|
|
36952
36953
|
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
36953
36954
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
36955
|
+
"@hasna/contracts": "1.0.1",
|
|
36954
36956
|
"@hasna/events": "0.1.16",
|
|
36955
36957
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
36956
36958
|
chalk: "^5.3.0",
|
|
@@ -37897,14 +37899,19 @@ function assertNoRetiredConfigKeys(config, source) {
|
|
|
37897
37899
|
for (const [key, replacement] of Object.entries(RETIRED_CONFIG_KEYS)) {
|
|
37898
37900
|
if (!(key in config))
|
|
37899
37901
|
continue;
|
|
37900
|
-
throw new RetiredSettingError(key, `${source}: "${key}" is no longer a configuration key. ` +
|
|
37902
|
+
throw new RetiredSettingError(key, `${source}: "${key}" is no longer a configuration key. ` + `${RETIRED_CONFIG_KEY_REASONS[key] ?? ""} ` + `Use ${replacement} instead, and remove the old key with: skills config unset ${key}. ` + "Refused rather than ignored, because silently dropping it would leave an " + "operator believing they had pointed this install at a server.");
|
|
37901
37903
|
}
|
|
37902
37904
|
}
|
|
37903
|
-
var RETIRED_ENV_SUFFIXES, RETIRED_CONFIG_KEYS, RetiredSettingError;
|
|
37905
|
+
var RETIRED_ENV_SUFFIXES, RETIRED_CONFIG_KEYS, RETIRED_CONFIG_KEY_REASONS, RetiredSettingError;
|
|
37904
37906
|
var init_retired_settings = __esm(() => {
|
|
37905
37907
|
RETIRED_ENV_SUFFIXES = ["_STORAGE_MODE", "_DEPLOYMENT_MODE", "_CLOUD_MODE"];
|
|
37906
37908
|
RETIRED_CONFIG_KEYS = {
|
|
37907
|
-
mode: "
|
|
37909
|
+
mode: "a configured API origin",
|
|
37910
|
+
apiUrl: "skills setup --api-url <origin>"
|
|
37911
|
+
};
|
|
37912
|
+
RETIRED_CONFIG_KEY_REASONS = {
|
|
37913
|
+
mode: "Deployment modes were removed: a Skills client either resolves a credential " + "or it does not, and that is the whole of it.",
|
|
37914
|
+
apiUrl: "The service address is no longer kept in this app's config file: it is read from " + "HASNA_SKILLS_API_URL, then the macOS Keychain item hasna.credentials.skills.api-url, " + "then ~/.hasna/skills/config/credentials, then the fleet gateway."
|
|
37908
37915
|
};
|
|
37909
37916
|
RetiredSettingError = class RetiredSettingError extends Error {
|
|
37910
37917
|
code = "RETIRED_SETTING";
|
|
@@ -38036,7 +38043,6 @@ var init_app_home = __esm(() => {
|
|
|
38036
38043
|
// src/lib/config.ts
|
|
38037
38044
|
import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
38038
38045
|
import { join as join4, dirname } from "path";
|
|
38039
|
-
import { homedir as homedir3 } from "os";
|
|
38040
38046
|
function validKeys() {
|
|
38041
38047
|
return [...Object.keys(ENUM_KEYS), ...STRING_KEYS];
|
|
38042
38048
|
}
|
|
@@ -38067,16 +38073,6 @@ function normalizeConfigValue(key, value) {
|
|
|
38067
38073
|
const allowed = allowedValues(key);
|
|
38068
38074
|
if (allowed)
|
|
38069
38075
|
return allowed.includes(value) ? value : undefined;
|
|
38070
|
-
if (key === "apiUrl") {
|
|
38071
|
-
try {
|
|
38072
|
-
const url = new URL(value);
|
|
38073
|
-
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
38074
|
-
return;
|
|
38075
|
-
return value.replace(/\/+$/, "");
|
|
38076
|
-
} catch {
|
|
38077
|
-
return;
|
|
38078
|
-
}
|
|
38079
|
-
}
|
|
38080
38076
|
if (key === "extensionsDir")
|
|
38081
38077
|
return value.trim() ? value : undefined;
|
|
38082
38078
|
return;
|
|
@@ -38107,27 +38103,6 @@ function getDataDir() {
|
|
|
38107
38103
|
function getDataDirReadOnly() {
|
|
38108
38104
|
return getDataRoot();
|
|
38109
38105
|
}
|
|
38110
|
-
function getConfigPathReadOnly(scope) {
|
|
38111
|
-
if (scope === "global")
|
|
38112
|
-
return join4(getDataDirReadOnly(), "config.json");
|
|
38113
|
-
return join4(process.cwd(), "skills.config.json");
|
|
38114
|
-
}
|
|
38115
|
-
function loadConfigReadOnly() {
|
|
38116
|
-
const canonicalConfigPath = getConfigPathReadOnly("global");
|
|
38117
|
-
let globalConfig;
|
|
38118
|
-
if (existsSync4(canonicalConfigPath)) {
|
|
38119
|
-
globalConfig = readConfigFile(canonicalConfigPath);
|
|
38120
|
-
} else if (hasOperatorOverride()) {
|
|
38121
|
-
globalConfig = {};
|
|
38122
|
-
} else {
|
|
38123
|
-
globalConfig = readConfigFile(legacyConfigFilePath());
|
|
38124
|
-
}
|
|
38125
|
-
const projectConfig = readConfigFile(getConfigPathReadOnly("project"));
|
|
38126
|
-
return { ...globalConfig, ...projectConfig };
|
|
38127
|
-
}
|
|
38128
|
-
function legacyConfigFilePath() {
|
|
38129
|
-
return join4(process.env["HOME"] || process.env["USERPROFILE"] || homedir3(), ".skillsrc");
|
|
38130
|
-
}
|
|
38131
38106
|
function getConfigPath(scope) {
|
|
38132
38107
|
if (scope === "global") {
|
|
38133
38108
|
return join4(getDataDir(), "config.json");
|
|
@@ -38167,7 +38142,7 @@ function saveConfig(key, value, scope = "project") {
|
|
|
38167
38142
|
const normalized = normalizeConfigValue(key, value);
|
|
38168
38143
|
if (normalized === undefined) {
|
|
38169
38144
|
const allowed = allowedValues(key);
|
|
38170
|
-
throw new Error(allowed ? `Invalid value '${value}' for ${key}. Allowed: ${allowed.join(", ")}` : `Invalid value '${value}' for ${key}. Expected
|
|
38145
|
+
throw new Error(allowed ? `Invalid value '${value}' for ${key}. Allowed: ${allowed.join(", ")}` : `Invalid value '${value}' for ${key}. Expected a non-empty path`);
|
|
38171
38146
|
}
|
|
38172
38147
|
const filePath = getConfigPath(scope);
|
|
38173
38148
|
let existing = {};
|
|
@@ -38224,7 +38199,7 @@ var init_config = __esm(() => {
|
|
|
38224
38199
|
defaultScope: ["global", "project"],
|
|
38225
38200
|
format: ["compact", "json", "csv"]
|
|
38226
38201
|
};
|
|
38227
|
-
STRING_KEYS = ["
|
|
38202
|
+
STRING_KEYS = ["extensionsDir"];
|
|
38228
38203
|
});
|
|
38229
38204
|
|
|
38230
38205
|
// src/lib/registry-data/development-tools.ts
|
|
@@ -39729,7 +39704,7 @@ function readPortableSkillManifest(skillPath, fallbackName = basename(skillPath)
|
|
|
39729
39704
|
const pkg = existsSync8(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
39730
39705
|
const name = normalizePortableSkillName(stringField(jsonManifest, "name") ?? frontmatter?.name ?? stringValue(pkg?.name) ?? fallbackName);
|
|
39731
39706
|
const description = stringField(jsonManifest, "description") ?? frontmatter?.description ?? stringValue(pkg?.description) ?? `${name} skill`;
|
|
39732
|
-
const version =
|
|
39707
|
+
const version = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
39733
39708
|
const kind = parseSkillKind(stringField(jsonManifest, "kind") ?? frontmatter?.kind);
|
|
39734
39709
|
const commands = parseManifestCommands(jsonManifest) ?? (kind === "instruction" ? [] : inferPackageCommands(pkg, name)) ?? [];
|
|
39735
39710
|
return {
|
|
@@ -39753,6 +39728,15 @@ function parseSkillKind(value) {
|
|
|
39753
39728
|
return value;
|
|
39754
39729
|
return;
|
|
39755
39730
|
}
|
|
39731
|
+
function readDeclaredSkillVersion(skillPath) {
|
|
39732
|
+
const skillJsonPath = join8(skillPath, "skill.json");
|
|
39733
|
+
const skillMdPath = join8(skillPath, "SKILL.md");
|
|
39734
|
+
const pkgPath = join8(skillPath, "package.json");
|
|
39735
|
+
const jsonManifest = existsSync8(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
39736
|
+
const frontmatter = existsSync8(skillMdPath) ? parseSkillFrontmatter(readFileSync6(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
39737
|
+
const pkg = existsSync8(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
39738
|
+
return stringField(jsonManifest, "version") ?? frontmatter?.version ?? stringValue(pkg?.version);
|
|
39739
|
+
}
|
|
39756
39740
|
function createInstructionManifest(name, options) {
|
|
39757
39741
|
return {
|
|
39758
39742
|
$schema: PORTABLE_SKILL_SCHEMA,
|
|
@@ -42858,7 +42842,7 @@ import {
|
|
|
42858
42842
|
statSync as statSync8,
|
|
42859
42843
|
writeFileSync as writeFileSync5
|
|
42860
42844
|
} from "fs";
|
|
42861
|
-
import { homedir as
|
|
42845
|
+
import { homedir as homedir3 } from "os";
|
|
42862
42846
|
import { basename as basename3, dirname as dirname4, join as join12 } from "path";
|
|
42863
42847
|
function isSyncAgent(value) {
|
|
42864
42848
|
return SYNC_AGENTS.includes(value);
|
|
@@ -42871,7 +42855,7 @@ function resolveSyncAgents(arg) {
|
|
|
42871
42855
|
}
|
|
42872
42856
|
return [arg];
|
|
42873
42857
|
}
|
|
42874
|
-
function agentGlobalSkillsDir(agent, homeDir =
|
|
42858
|
+
function agentGlobalSkillsDir(agent, homeDir = homedir3()) {
|
|
42875
42859
|
switch (agent) {
|
|
42876
42860
|
case "opencode":
|
|
42877
42861
|
return join12(homeDir, ".config", "opencode", "skills");
|
|
@@ -42966,7 +42950,7 @@ function isDirectory(path) {
|
|
|
42966
42950
|
function syncSkillsToAgents(options = {}) {
|
|
42967
42951
|
const requested = normalizeRequested(options.names);
|
|
42968
42952
|
const agents = options.agents?.length ? options.agents : [...SYNC_AGENTS];
|
|
42969
|
-
const homeDir = options.homeDir ??
|
|
42953
|
+
const homeDir = options.homeDir ?? homedir3();
|
|
42970
42954
|
const { roots, source } = resolveSyncCorpus(options);
|
|
42971
42955
|
const corpus = listPortableSkillsAcrossRoots(roots);
|
|
42972
42956
|
const byName = new Map(corpus.map((skill) => [skill.name, skill]));
|
|
@@ -43021,7 +43005,7 @@ function syncSkillsToAgents(options = {}) {
|
|
|
43021
43005
|
return { actions };
|
|
43022
43006
|
}
|
|
43023
43007
|
function writeManagedAgentSkill(params) {
|
|
43024
|
-
const homeDir = params.homeDir ??
|
|
43008
|
+
const homeDir = params.homeDir ?? homedir3();
|
|
43025
43009
|
const dir = join12(agentGlobalSkillsDir(params.agent, homeDir), params.skill);
|
|
43026
43010
|
const result2 = writeManagedSkillDir(dir, params.skillMd, {
|
|
43027
43011
|
skill: params.skill,
|
|
@@ -43339,7 +43323,7 @@ __export(exports_installer, {
|
|
|
43339
43323
|
});
|
|
43340
43324
|
import { existsSync as existsSync14, readFileSync as readFileSync10, rmSync as rmSync4 } from "fs";
|
|
43341
43325
|
import { dirname as dirname5, join as join14 } from "path";
|
|
43342
|
-
import { homedir as
|
|
43326
|
+
import { homedir as homedir4 } from "os";
|
|
43343
43327
|
import { fileURLToPath } from "url";
|
|
43344
43328
|
function findSkillsDir() {
|
|
43345
43329
|
let dir = __dirname2;
|
|
@@ -43530,11 +43514,11 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
43530
43514
|
const base2 = projectDir || process.cwd();
|
|
43531
43515
|
switch (agent) {
|
|
43532
43516
|
case "pi":
|
|
43533
|
-
return scope === "project" ? join14(base2, ".pi", "skills") : join14(
|
|
43517
|
+
return scope === "project" ? join14(base2, ".pi", "skills") : join14(homedir4(), ".pi", "agent", "skills");
|
|
43534
43518
|
case "opencode":
|
|
43535
|
-
return scope === "project" ? join14(base2, ".opencode", "skills") : join14(
|
|
43519
|
+
return scope === "project" ? join14(base2, ".opencode", "skills") : join14(homedir4(), ".config", "opencode", "skills");
|
|
43536
43520
|
default:
|
|
43537
|
-
return scope === "project" ? join14(base2, `.${agent}`, "skills") : join14(
|
|
43521
|
+
return scope === "project" ? join14(base2, `.${agent}`, "skills") : join14(homedir4(), `.${agent}`, "skills");
|
|
43538
43522
|
}
|
|
43539
43523
|
}
|
|
43540
43524
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
@@ -47719,116 +47703,811 @@ var init_zod = __esm(() => {
|
|
|
47719
47703
|
init_external();
|
|
47720
47704
|
});
|
|
47721
47705
|
|
|
47722
|
-
//
|
|
47723
|
-
|
|
47724
|
-
|
|
47725
|
-
|
|
47726
|
-
|
|
47706
|
+
// ../contracts/dist/client/transport.js
|
|
47707
|
+
import { isIP } from "net";
|
|
47708
|
+
import { spawnSync } from "child_process";
|
|
47709
|
+
import { closeSync, fstatSync, openSync, readFileSync as readFileSync11 } from "fs";
|
|
47710
|
+
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
47711
|
+
import { createRequire } from "module";
|
|
47712
|
+
import { hostname as osHostname } from "os";
|
|
47713
|
+
import { isAbsolute as isAbsolute3, join as join15 } from "path";
|
|
47714
|
+
function envToken(name) {
|
|
47715
|
+
return name.toUpperCase().replace(/-/g, "_");
|
|
47716
|
+
}
|
|
47717
|
+
function clientTransportEnvKeys(name) {
|
|
47718
|
+
const envSegment = envToken(name);
|
|
47719
|
+
return {
|
|
47720
|
+
apiUrlKeys: [`HASNA_${envSegment}_API_URL`, `${envSegment}_API_URL`],
|
|
47721
|
+
apiKeyKeys: [`HASNA_${envSegment}_API_KEY`, `${envSegment}_API_KEY`]
|
|
47722
|
+
};
|
|
47727
47723
|
}
|
|
47728
|
-
function
|
|
47729
|
-
|
|
47730
|
-
if (!resolved)
|
|
47731
|
-
throw new MissingApiUrlError(action);
|
|
47732
|
-
return resolved;
|
|
47724
|
+
function credentialOverrideEnvKey(name) {
|
|
47725
|
+
return `HASNA_${envToken(name)}_API_KEY_OVERRIDE`;
|
|
47733
47726
|
}
|
|
47734
|
-
|
|
47735
|
-
|
|
47736
|
-
|
|
47737
|
-
|
|
47738
|
-
|
|
47739
|
-
|
|
47740
|
-
|
|
47741
|
-
|
|
47742
|
-
|
|
47727
|
+
function credentialPointerEnvKey(name) {
|
|
47728
|
+
return `HASNA_${envToken(name)}_API_KEY_REF`;
|
|
47729
|
+
}
|
|
47730
|
+
function homeDir(env3) {
|
|
47731
|
+
const home = env3.HOME?.trim();
|
|
47732
|
+
return home ? home : null;
|
|
47733
|
+
}
|
|
47734
|
+
function absoluteOverride(env3, key) {
|
|
47735
|
+
const value = env3[key]?.trim();
|
|
47736
|
+
return value && isAbsolute3(value) ? value : null;
|
|
47737
|
+
}
|
|
47738
|
+
function hasnaHomeDir(env3) {
|
|
47739
|
+
const override = absoluteOverride(env3, HASNA_HOME_ENV_KEY);
|
|
47740
|
+
if (override)
|
|
47741
|
+
return override;
|
|
47742
|
+
const home = homeDir(env3);
|
|
47743
|
+
return home ? join15(home, HASNA_HOME_DIR) : null;
|
|
47744
|
+
}
|
|
47745
|
+
function appConfigDir(name, env3) {
|
|
47746
|
+
const configRoot = absoluteOverride(env3, HASNA_CONFIG_HOME_ENV_KEY);
|
|
47747
|
+
if (configRoot)
|
|
47748
|
+
return join15(configRoot, name);
|
|
47749
|
+
const root = hasnaHomeDir(env3);
|
|
47750
|
+
return root ? join15(root, name, CONFIG_SUBDIR) : null;
|
|
47751
|
+
}
|
|
47752
|
+
function credentialDiskSourceList(name, env3, profile = null) {
|
|
47753
|
+
if (!SAFE_APP_SLUG.test(name))
|
|
47754
|
+
return [];
|
|
47755
|
+
const directory = appConfigDir(name, env3);
|
|
47756
|
+
if (!directory)
|
|
47757
|
+
return [];
|
|
47758
|
+
const file = profile ? `${CREDENTIALS_FILE}-${profile}` : CREDENTIALS_FILE;
|
|
47759
|
+
return [{ path: join15(directory, file), tier: "disk" }];
|
|
47760
|
+
}
|
|
47761
|
+
function credentialDiskSources(name, env3) {
|
|
47762
|
+
return credentialDiskSourceList(name, env3, null).map((s) => s.path);
|
|
47763
|
+
}
|
|
47764
|
+
function profileDiskSources(name, env3, profile) {
|
|
47765
|
+
return credentialDiskSourceList(name, env3, profile).map((s) => s.path);
|
|
47766
|
+
}
|
|
47767
|
+
function parseEnvFile(text) {
|
|
47768
|
+
const values2 = new Map;
|
|
47769
|
+
const unusable = new Set;
|
|
47770
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
47771
|
+
const line = rawLine.trim();
|
|
47772
|
+
if (line.length === 0 || line.startsWith("#"))
|
|
47773
|
+
continue;
|
|
47774
|
+
const withoutExport = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
|
|
47775
|
+
const equals = withoutExport.indexOf("=");
|
|
47776
|
+
if (equals <= 0)
|
|
47777
|
+
continue;
|
|
47778
|
+
const key = withoutExport.slice(0, equals).trim();
|
|
47779
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
47780
|
+
continue;
|
|
47781
|
+
let value = withoutExport.slice(equals + 1).trim();
|
|
47782
|
+
const quote = value[0];
|
|
47783
|
+
if (quote === '"' || quote === "'") {
|
|
47784
|
+
if (value.length < 2 || !value.endsWith(quote)) {
|
|
47785
|
+
unusable.add(key);
|
|
47786
|
+
continue;
|
|
47787
|
+
}
|
|
47788
|
+
value = value.slice(1, -1);
|
|
47743
47789
|
}
|
|
47744
|
-
|
|
47745
|
-
|
|
47746
|
-
|
|
47747
|
-
|
|
47748
|
-
|
|
47749
|
-
|
|
47750
|
-
|
|
47751
|
-
|
|
47752
|
-
|
|
47753
|
-
getAuthFilePath: () => getAuthFilePath,
|
|
47754
|
-
getAuthConfigReadOnly: () => getAuthConfigReadOnly,
|
|
47755
|
-
getAuthConfig: () => getAuthConfig,
|
|
47756
|
-
getApiUrl: () => getApiUrl,
|
|
47757
|
-
getApiKeyReadOnly: () => getApiKeyReadOnly,
|
|
47758
|
-
getApiKey: () => getApiKey,
|
|
47759
|
-
clearAuthConfig: () => clearAuthConfig
|
|
47760
|
-
});
|
|
47761
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync7, unlinkSync } from "fs";
|
|
47762
|
-
import { dirname as dirname6, join as join15 } from "path";
|
|
47763
|
-
import { homedir as homedir6 } from "os";
|
|
47764
|
-
function getAuthFilePath() {
|
|
47765
|
-
return join15(getDataDir(), "auth.json");
|
|
47790
|
+
if (value.trim().length === 0) {
|
|
47791
|
+
unusable.add(key);
|
|
47792
|
+
continue;
|
|
47793
|
+
}
|
|
47794
|
+
if (values2.has(key) && values2.get(key) !== value)
|
|
47795
|
+
unusable.add(key);
|
|
47796
|
+
values2.set(key, value);
|
|
47797
|
+
}
|
|
47798
|
+
return { values: values2, unusable };
|
|
47766
47799
|
}
|
|
47767
|
-
function
|
|
47768
|
-
|
|
47800
|
+
function configFileModeAllowed(mode) {
|
|
47801
|
+
const permissions = mode & 4095;
|
|
47802
|
+
return permissions === 256 || permissions === 384;
|
|
47769
47803
|
}
|
|
47770
|
-
function
|
|
47771
|
-
return
|
|
47804
|
+
function configFileReadsCoherent(before2, after2) {
|
|
47805
|
+
return before2.dev === after2.dev && before2.ino === after2.ino && before2.size === after2.size && before2.mtimeMs === after2.mtimeMs && before2.ctimeMs === after2.ctimeMs;
|
|
47772
47806
|
}
|
|
47773
|
-
function
|
|
47774
|
-
|
|
47775
|
-
|
|
47807
|
+
function readAppConfigFile(path) {
|
|
47808
|
+
const unsafe = (reason) => {
|
|
47809
|
+
throw new CredentialFileUnsafeError(path, reason);
|
|
47810
|
+
};
|
|
47811
|
+
let fd = -1;
|
|
47776
47812
|
try {
|
|
47777
|
-
|
|
47778
|
-
|
|
47779
|
-
const
|
|
47780
|
-
if (
|
|
47781
|
-
cachedConfig = null;
|
|
47813
|
+
fd = openSync(path, O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
|
|
47814
|
+
} catch (error) {
|
|
47815
|
+
const code = error.code;
|
|
47816
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
47782
47817
|
return null;
|
|
47818
|
+
if (code === "ELOOP")
|
|
47819
|
+
unsafe("the path is a symlink");
|
|
47820
|
+
unsafe(`the path could not be opened (${code ?? "unknown error"})`);
|
|
47821
|
+
}
|
|
47822
|
+
try {
|
|
47823
|
+
const before2 = fstatSync(fd);
|
|
47824
|
+
if (!before2.isFile())
|
|
47825
|
+
unsafe("the path is not a regular file");
|
|
47826
|
+
if (!configFileModeAllowed(before2.mode)) {
|
|
47827
|
+
unsafe(`permission mode ${(before2.mode & 4095).toString(8).padStart(4, "0")} is not owner-only 0400 or 0600`);
|
|
47828
|
+
}
|
|
47829
|
+
const uid = process.getuid?.() ?? process.geteuid?.();
|
|
47830
|
+
if (uid !== undefined && before2.uid !== uid)
|
|
47831
|
+
unsafe("the file is not owned by the current user");
|
|
47832
|
+
if (before2.size > MAX_CREDENTIAL_FILE_BYTES)
|
|
47833
|
+
unsafe("the file exceeds the size limit");
|
|
47834
|
+
const bytes = readFileSync11(fd);
|
|
47835
|
+
const after2 = fstatSync(fd);
|
|
47836
|
+
if (!configFileReadsCoherent(before2, after2)) {
|
|
47837
|
+
unsafe("the file changed while being read");
|
|
47838
|
+
}
|
|
47839
|
+
return parseEnvFile(bytes.toString("utf8"));
|
|
47840
|
+
} finally {
|
|
47841
|
+
if (fd !== -1)
|
|
47842
|
+
closeSync(fd);
|
|
47843
|
+
}
|
|
47844
|
+
}
|
|
47845
|
+
function readCredentialFile(path, apiKeyKeys) {
|
|
47846
|
+
const parsed = readAppConfigFile(path);
|
|
47847
|
+
if (!parsed)
|
|
47848
|
+
return null;
|
|
47849
|
+
for (const key of apiKeyKeys) {
|
|
47850
|
+
if (parsed.unusable.has(key)) {
|
|
47851
|
+
throw new CredentialFileUnsafeError(path, `${key} is declared but blank or malformed`);
|
|
47783
47852
|
}
|
|
47784
|
-
|
|
47785
|
-
|
|
47786
|
-
|
|
47787
|
-
|
|
47853
|
+
}
|
|
47854
|
+
const values2 = apiKeyKeys.map((key) => parsed.values.get(key)?.trim()).filter((value) => Boolean(value));
|
|
47855
|
+
if (new Set(values2).size > 1) {
|
|
47856
|
+
throw new CredentialFileUnsafeError(path, "credential aliases disagree");
|
|
47857
|
+
}
|
|
47858
|
+
return values2[0] ?? null;
|
|
47859
|
+
}
|
|
47860
|
+
function appConfigDiskValue(name, env3, keys2) {
|
|
47861
|
+
const wanted = keys2.filter((key) => !CREDENTIAL_SHAPED_KEY.test(key));
|
|
47862
|
+
if (wanted.length === 0)
|
|
47788
47863
|
return null;
|
|
47864
|
+
for (const path of credentialDiskSources(name, env3)) {
|
|
47865
|
+
const parsed = readAppConfigFile(path);
|
|
47866
|
+
if (!parsed)
|
|
47867
|
+
continue;
|
|
47868
|
+
if (wanted.some((key) => parsed.unusable.has(key))) {
|
|
47869
|
+
return { key: wanted.find((key) => parsed.unusable.has(key)), value: "", path, unusable: true };
|
|
47870
|
+
}
|
|
47871
|
+
const values2 = wanted.map((key) => parsed.values.get(key)?.trim()).filter((value) => Boolean(value));
|
|
47872
|
+
if (new Set(values2).size > 1)
|
|
47873
|
+
throw new CredentialFileUnsafeError(path, "configuration aliases disagree");
|
|
47874
|
+
for (const key of wanted) {
|
|
47875
|
+
if (parsed.unusable.has(key))
|
|
47876
|
+
return { key, value: "", path, unusable: true };
|
|
47877
|
+
const value = parsed.values.get(key)?.trim();
|
|
47878
|
+
if (value)
|
|
47879
|
+
return { key, value, path };
|
|
47880
|
+
}
|
|
47789
47881
|
}
|
|
47882
|
+
return null;
|
|
47790
47883
|
}
|
|
47791
|
-
function
|
|
47792
|
-
|
|
47793
|
-
|
|
47794
|
-
|
|
47795
|
-
|
|
47796
|
-
|
|
47884
|
+
function assertUsableCredential(appName, source, value) {
|
|
47885
|
+
if (VAULT_POINTER_SHAPE.test(value)) {
|
|
47886
|
+
throw new CredentialResolutionError(appName, `The credential from ${source} looks like a secrets-vault pointer (a path-shaped reference like ` + `'namespace/app/live/api_key'). A vault path is NEVER accepted as a literal API key. ` + `Use ${credentialPointerEnvKey(appName)} to resolve the key through the vault, or provide the actual key value.`, [source]);
|
|
47887
|
+
}
|
|
47888
|
+
if (!ILLEGAL_IN_HEADER_VALUE.test(value))
|
|
47889
|
+
return;
|
|
47890
|
+
throw new CredentialResolutionError(appName, `The credential from ${source} contains characters that cannot be sent in an HTTP header ` + `(a control character or non-ASCII byte). A file written with CR-only line endings is the usual ` + `cause. Rewrite that credential file with one LF-terminated KEY=value line. ` + `The value is not shown here, and is deliberately never logged.`, [source]);
|
|
47891
|
+
}
|
|
47892
|
+
function sealCredential(fields) {
|
|
47893
|
+
const { apiKey } = fields;
|
|
47894
|
+
const visible = {
|
|
47895
|
+
tier: fields.tier,
|
|
47896
|
+
source: fields.source,
|
|
47897
|
+
deliberate: fields.deliberate,
|
|
47898
|
+
diskCandidates: Object.freeze([...fields.diskCandidates]),
|
|
47899
|
+
warning: fields.warning
|
|
47900
|
+
};
|
|
47901
|
+
const sealed = { ...visible };
|
|
47902
|
+
Object.defineProperty(sealed, "apiKey", {
|
|
47903
|
+
value: apiKey,
|
|
47904
|
+
enumerable: false,
|
|
47905
|
+
writable: false,
|
|
47906
|
+
configurable: false
|
|
47907
|
+
});
|
|
47908
|
+
if (fields.pointerVaultKey !== undefined) {
|
|
47909
|
+
Object.defineProperty(sealed, "pointerVaultKey", {
|
|
47910
|
+
value: fields.pointerVaultKey,
|
|
47911
|
+
enumerable: false,
|
|
47912
|
+
writable: false,
|
|
47913
|
+
configurable: false
|
|
47914
|
+
});
|
|
47915
|
+
}
|
|
47916
|
+
Object.defineProperty(sealed, INSPECT_CUSTOM, {
|
|
47917
|
+
value: () => ({ ...visible, apiKey: "[redacted]" }),
|
|
47918
|
+
enumerable: false,
|
|
47919
|
+
writable: false,
|
|
47920
|
+
configurable: false
|
|
47921
|
+
});
|
|
47922
|
+
Object.defineProperty(sealed, CREDENTIAL_SEAL, {
|
|
47923
|
+
value: true,
|
|
47924
|
+
enumerable: false,
|
|
47925
|
+
writable: false,
|
|
47926
|
+
configurable: false
|
|
47927
|
+
});
|
|
47928
|
+
return Object.freeze(sealed);
|
|
47929
|
+
}
|
|
47930
|
+
function firstEnvValue(env3, keys2) {
|
|
47931
|
+
for (const key of keys2) {
|
|
47932
|
+
if (!Object.prototype.hasOwnProperty.call(env3, key))
|
|
47933
|
+
continue;
|
|
47934
|
+
const value = env3[key]?.trim();
|
|
47935
|
+
if (value)
|
|
47936
|
+
return { key, value };
|
|
47937
|
+
}
|
|
47938
|
+
return null;
|
|
47939
|
+
}
|
|
47940
|
+
function isAmbientEnvironment(env3) {
|
|
47941
|
+
return env3 === process.env || env3[AMBIENT_ENVIRONMENT] === true;
|
|
47942
|
+
}
|
|
47943
|
+
function defaultKeychainRunner(argv) {
|
|
47944
|
+
const result2 = spawnSync(KEYCHAIN_SECURITY_BIN, [...argv], {
|
|
47945
|
+
encoding: "utf8",
|
|
47946
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
47947
|
+
timeout: KEYCHAIN_SPAWN_TIMEOUT_MS
|
|
47948
|
+
});
|
|
47949
|
+
return {
|
|
47950
|
+
status: result2.status,
|
|
47951
|
+
stdout: result2.stdout ?? "",
|
|
47952
|
+
stderr: result2.error ? result2.error.message : result2.stderr ?? ""
|
|
47953
|
+
};
|
|
47797
47954
|
}
|
|
47798
|
-
function
|
|
47955
|
+
function keychainTierEnabled(env3, options) {
|
|
47956
|
+
if ((options.platform ?? process.platform) !== "darwin")
|
|
47957
|
+
return false;
|
|
47958
|
+
if (options.enabled !== undefined)
|
|
47959
|
+
return options.enabled;
|
|
47960
|
+
return options.run !== undefined || isAmbientEnvironment(env3);
|
|
47961
|
+
}
|
|
47962
|
+
function keychainAccount(env3, options) {
|
|
47963
|
+
const station = env3[KEYCHAIN_STATION_ENV_KEY]?.trim();
|
|
47964
|
+
if (station)
|
|
47965
|
+
return station;
|
|
47966
|
+
const host = (options.hostname ?? osHostname)().split(".")[0]?.trim() ?? "";
|
|
47967
|
+
if (host)
|
|
47968
|
+
return host;
|
|
47969
|
+
const user = env3.USER?.trim();
|
|
47970
|
+
return user || null;
|
|
47971
|
+
}
|
|
47972
|
+
function keychainFailureHint(text) {
|
|
47973
|
+
const line = text.split(/\r?\n/).find((entry) => entry.trim().length > 0)?.trim() ?? "";
|
|
47974
|
+
const clean = line.replace(/[\u0000-\u001f\u007f]/g, "").slice(0, 200);
|
|
47975
|
+
return clean ? `: ${clean}` : "";
|
|
47976
|
+
}
|
|
47977
|
+
function readKeychainItem(name, env3, kind, options) {
|
|
47978
|
+
if (!SAFE_APP_SLUG.test(name) || !keychainTierEnabled(env3, options))
|
|
47979
|
+
return null;
|
|
47980
|
+
const account = keychainAccount(env3, options);
|
|
47981
|
+
if (!account)
|
|
47982
|
+
return null;
|
|
47983
|
+
const service = `${KEYCHAIN_SERVICE_PREFIX}.${name}.${kind}`;
|
|
47984
|
+
const source = `keychain:${service}@${account}`;
|
|
47985
|
+
const run = options.run ?? defaultKeychainRunner;
|
|
47986
|
+
let result2;
|
|
47799
47987
|
try {
|
|
47800
|
-
|
|
47801
|
-
} catch {
|
|
47988
|
+
result2 = run(["find-generic-password", "-a", account, "-s", service, "-w"]);
|
|
47989
|
+
} catch (error) {
|
|
47990
|
+
const reason = keychainFailureHint(error instanceof Error ? error.message : String(error));
|
|
47991
|
+
throw new CredentialResolutionError(name, `The Keychain lookup for ${source} could not run${reason}. A Keychain failure is never resolved ` + `around: fix the keychain, or delete the item to fall through to the credential on disk.`, [source]);
|
|
47992
|
+
}
|
|
47993
|
+
if (result2.status === KEYCHAIN_ITEM_NOT_FOUND_STATUS)
|
|
47994
|
+
return null;
|
|
47995
|
+
if (result2.status !== 0) {
|
|
47996
|
+
throw new CredentialResolutionError(name, `The Keychain lookup for ${source} failed (security exited ` + `${result2.status ?? "without a status"}${keychainFailureHint(result2.stderr)}). A Keychain item that ` + `exists but cannot be read is never resolved around: unlock the keychain, run from a session that ` + `may use it, or delete the item to fall through to the credential on disk.`, [source]);
|
|
47997
|
+
}
|
|
47998
|
+
const value = result2.stdout.trim();
|
|
47999
|
+
if (!value) {
|
|
48000
|
+
throw new CredentialResolutionError(name, `${source} exists but holds an empty value; a declared item never falls through to another ` + `identity. Store a value in it or delete the item.`, [source]);
|
|
48001
|
+
}
|
|
48002
|
+
return { value, source };
|
|
48003
|
+
}
|
|
48004
|
+
function keychainConfigValue(name, env3, options = {}) {
|
|
48005
|
+
return readKeychainItem(name, env3, "api-url", options);
|
|
48006
|
+
}
|
|
48007
|
+
function snapshotClientEnvironment(name, env3) {
|
|
48008
|
+
const keys2 = clientTransportEnvKeys(name);
|
|
48009
|
+
const ambient = isAmbientEnvironment(env3);
|
|
48010
|
+
const snapshot = Object.create(null);
|
|
48011
|
+
for (const key of [
|
|
48012
|
+
...keys2.apiUrlKeys,
|
|
48013
|
+
...keys2.apiKeyKeys,
|
|
48014
|
+
credentialOverrideEnvKey(name),
|
|
48015
|
+
credentialPointerEnvKey(name),
|
|
48016
|
+
CREDENTIAL_PROFILE_ENV_KEY,
|
|
48017
|
+
"HOME",
|
|
48018
|
+
HASNA_HOME_ENV_KEY,
|
|
48019
|
+
HASNA_CONFIG_HOME_ENV_KEY,
|
|
48020
|
+
KEYCHAIN_STATION_ENV_KEY,
|
|
48021
|
+
"USER"
|
|
48022
|
+
]) {
|
|
48023
|
+
const descriptor = Object.getOwnPropertyDescriptor(env3, key);
|
|
48024
|
+
if (!descriptor)
|
|
48025
|
+
continue;
|
|
48026
|
+
if (!("value" in descriptor)) {
|
|
48027
|
+
throw new CredentialResolutionError(name, `${key} is accessor-backed; client configuration requires own data properties.`, [key]);
|
|
48028
|
+
}
|
|
48029
|
+
if (descriptor.value !== undefined && typeof descriptor.value !== "string") {
|
|
48030
|
+
throw new CredentialResolutionError(name, `${key} must be a string data property.`, [key]);
|
|
48031
|
+
}
|
|
48032
|
+
snapshot[key] = descriptor.value;
|
|
48033
|
+
}
|
|
48034
|
+
if (ambient) {
|
|
48035
|
+
Object.defineProperty(snapshot, AMBIENT_ENVIRONMENT, {
|
|
48036
|
+
value: true,
|
|
48037
|
+
enumerable: false,
|
|
48038
|
+
writable: false,
|
|
48039
|
+
configurable: false
|
|
48040
|
+
});
|
|
48041
|
+
}
|
|
48042
|
+
return Object.freeze(snapshot);
|
|
48043
|
+
}
|
|
48044
|
+
function resolveCredential(name, env3, options = {}) {
|
|
48045
|
+
env3 = snapshotClientEnvironment(name, env3);
|
|
48046
|
+
const { apiKeyKeys } = clientTransportEnvKeys(name);
|
|
48047
|
+
const diskPaths = credentialDiskSources(name, env3);
|
|
48048
|
+
if (options.apiKey !== undefined) {
|
|
48049
|
+
const explicitKey = options.apiKey.trim();
|
|
48050
|
+
if (!explicitKey) {
|
|
48051
|
+
throw new CredentialResolutionError(name, "The explicit apiKey argument is blank; an explicit credential never falls through to another identity.", ["explicit apiKey argument"]);
|
|
48052
|
+
}
|
|
48053
|
+
assertUsableCredential(name, "the explicit apiKey argument", explicitKey);
|
|
48054
|
+
return sealCredential({
|
|
48055
|
+
apiKey: explicitKey,
|
|
48056
|
+
tier: "argument",
|
|
48057
|
+
source: "explicit apiKey argument",
|
|
48058
|
+
deliberate: true,
|
|
48059
|
+
diskCandidates: diskPaths,
|
|
48060
|
+
warning: null
|
|
48061
|
+
});
|
|
48062
|
+
}
|
|
48063
|
+
const overrideKeyName = credentialOverrideEnvKey(name);
|
|
48064
|
+
const overrideRaw = Object.prototype.hasOwnProperty.call(env3, overrideKeyName) ? env3[overrideKeyName] : undefined;
|
|
48065
|
+
if (overrideRaw !== undefined) {
|
|
48066
|
+
const override = overrideRaw.trim();
|
|
48067
|
+
if (!override) {
|
|
48068
|
+
throw new CredentialResolutionError(name, `${overrideKeyName} is set but empty. It is a deliberate override, so it is not resolved around: ` + `either give it a real key or unset it to fall back to the credential on disk.`, [overrideKeyName]);
|
|
48069
|
+
}
|
|
48070
|
+
assertUsableCredential(name, overrideKeyName, override);
|
|
48071
|
+
return sealCredential({
|
|
48072
|
+
apiKey: override,
|
|
48073
|
+
tier: "override",
|
|
48074
|
+
source: overrideKeyName,
|
|
48075
|
+
deliberate: true,
|
|
48076
|
+
diskCandidates: diskPaths,
|
|
48077
|
+
warning: null
|
|
48078
|
+
});
|
|
48079
|
+
}
|
|
48080
|
+
const pointerKeyName = credentialPointerEnvKey(name);
|
|
48081
|
+
const pointerRaw = Object.prototype.hasOwnProperty.call(env3, pointerKeyName) ? env3[pointerKeyName] : undefined;
|
|
48082
|
+
if (pointerRaw !== undefined) {
|
|
48083
|
+
const pointer = pointerRaw.trim();
|
|
48084
|
+
if (!pointer) {
|
|
48085
|
+
throw new CredentialResolutionError(name, `${pointerKeyName} is set but empty. It is a deliberate vault pointer, so it is not resolved around: ` + `either give it a vault item key or unset it to fall back to the credential on disk.`, [pointerKeyName]);
|
|
48086
|
+
}
|
|
48087
|
+
if (!VAULT_POINTER_SHAPE.test(pointer)) {
|
|
48088
|
+
throw new CredentialResolutionError(name, `${pointerKeyName} must name a vault ITEM KEY (a path-shaped reference like ` + `'namespace/app/live/api_key'), not a credential value. A pointer that carries a literal is refused.`, [pointerKeyName]);
|
|
48089
|
+
}
|
|
48090
|
+
return sealCredential({
|
|
48091
|
+
apiKey: "",
|
|
48092
|
+
pointerVaultKey: pointer,
|
|
48093
|
+
tier: "pointer",
|
|
48094
|
+
source: pointerKeyName,
|
|
48095
|
+
deliberate: true,
|
|
48096
|
+
diskCandidates: diskPaths,
|
|
48097
|
+
warning: null
|
|
48098
|
+
});
|
|
48099
|
+
}
|
|
48100
|
+
if (options.profile !== undefined && !options.profile.trim()) {
|
|
48101
|
+
throw new CredentialResolutionError(name, "The explicit profile argument is blank; an explicit identity selection never falls through.", ["explicit profile argument"]);
|
|
48102
|
+
}
|
|
48103
|
+
const profileRaw = Object.prototype.hasOwnProperty.call(env3, CREDENTIAL_PROFILE_ENV_KEY) ? env3[CREDENTIAL_PROFILE_ENV_KEY] : undefined;
|
|
48104
|
+
if (profileRaw !== undefined && !profileRaw.trim()) {
|
|
48105
|
+
throw new CredentialResolutionError(name, `${CREDENTIAL_PROFILE_ENV_KEY} is set but blank.`, [CREDENTIAL_PROFILE_ENV_KEY]);
|
|
48106
|
+
}
|
|
48107
|
+
const profile = options.profile?.trim() || profileRaw?.trim();
|
|
48108
|
+
if (profile) {
|
|
48109
|
+
const profileSource = options.profile?.trim() ? "explicit profile argument" : CREDENTIAL_PROFILE_ENV_KEY;
|
|
48110
|
+
if (!SAFE_PROFILE.test(profile)) {
|
|
48111
|
+
throw new CredentialResolutionError(name, `Profile name from ${profileSource} is not usable in a path. ` + `Use letters, digits, dot, dash, or underscore.`, [profileSource]);
|
|
48112
|
+
}
|
|
48113
|
+
const paths = profileDiskSources(name, env3, profile);
|
|
48114
|
+
for (const path of paths) {
|
|
48115
|
+
const value = readCredentialFile(path, apiKeyKeys);
|
|
48116
|
+
if (value) {
|
|
48117
|
+
assertUsableCredential(name, path, value);
|
|
48118
|
+
return sealCredential({
|
|
48119
|
+
apiKey: value,
|
|
48120
|
+
tier: "profile",
|
|
48121
|
+
source: path,
|
|
48122
|
+
deliberate: true,
|
|
48123
|
+
diskCandidates: paths,
|
|
48124
|
+
warning: null
|
|
48125
|
+
});
|
|
48126
|
+
}
|
|
48127
|
+
}
|
|
48128
|
+
throw new CredentialResolutionError(name, `Profile '${profile}' (from ${profileSource}) has no ${apiKeyKeys[0]} for '${name}'. ` + `Looked in: ${paths.join(", ") || "<no HOME in this environment>"}. ` + `A profile names WHICH identity to use, so it is never resolved around \u2014 ` + `create the profile's credential file or unset ${CREDENTIAL_PROFILE_ENV_KEY}.`, paths);
|
|
48129
|
+
}
|
|
48130
|
+
const definedEnvEntries = apiKeyKeys.filter((key) => Object.prototype.hasOwnProperty.call(env3, key) && env3[key] !== undefined).map((key) => ({ key, value: String(env3[key]).trim() }));
|
|
48131
|
+
const blankEnv = definedEnvEntries.find((entry) => entry.value.length === 0);
|
|
48132
|
+
if (blankEnv) {
|
|
48133
|
+
throw new CredentialResolutionError(name, `${blankEnv.key} is set but blank; a declared credential never falls through to another alias or identity.`, [blankEnv.key]);
|
|
48134
|
+
}
|
|
48135
|
+
if (definedEnvEntries.length > 1 && new Set(definedEnvEntries.map((entry) => entry.value)).size > 1) {
|
|
48136
|
+
throw new CredentialResolutionError(name, `${definedEnvEntries.map((entry) => entry.key).join(" and ")} disagree; credential aliases must be identical or only one may be set.`, definedEnvEntries.map((entry) => entry.key));
|
|
48137
|
+
}
|
|
48138
|
+
const envHit = firstEnvValue(env3, apiKeyKeys);
|
|
48139
|
+
const keychainHit = readKeychainItem(name, env3, "api-key", options.keychain ?? {});
|
|
48140
|
+
if (keychainHit) {
|
|
48141
|
+
assertUsableCredential(name, keychainHit.source, keychainHit.value);
|
|
48142
|
+
const warning = envHit && envHit.value !== keychainHit.value ? `Credential sources disagree for '${name}': ${keychainHit.source} and ${envHit.key} hold ` + `different keys. ${keychainHit.source} wins, because the Keychain is re-read on every call while ` + `an environment variable is a snapshot. Reconcile them \u2014 a rotation that updated only one leaves ` + `the other to fail 401 wherever it is loaded first.` : null;
|
|
48143
|
+
return sealCredential({
|
|
48144
|
+
apiKey: keychainHit.value,
|
|
48145
|
+
tier: "keychain",
|
|
48146
|
+
source: keychainHit.source,
|
|
48147
|
+
deliberate: false,
|
|
48148
|
+
diskCandidates: diskPaths,
|
|
48149
|
+
warning
|
|
48150
|
+
});
|
|
48151
|
+
}
|
|
48152
|
+
const diskSourceList = credentialDiskSourceList(name, env3, null);
|
|
48153
|
+
const diskHits = diskSourceList.map((src) => ({ src, value: readCredentialFile(src.path, apiKeyKeys) })).filter((hit) => hit.value !== null);
|
|
48154
|
+
if (diskHits.length > 0) {
|
|
48155
|
+
const winner = diskHits[0];
|
|
48156
|
+
assertUsableCredential(name, winner.src.path, winner.value);
|
|
48157
|
+
const divergentSources = [
|
|
48158
|
+
...diskHits.slice(1).filter((hit) => hit.value !== winner.value).map((hit) => hit.src.path),
|
|
48159
|
+
...envHit && envHit.value !== winner.value ? [envHit.key] : []
|
|
48160
|
+
];
|
|
48161
|
+
const warning = divergentSources.length > 0 ? `Credential sources disagree for '${name}': ${winner.src.path} and ` + `${divergentSources.join(", ")} hold different keys. ${winner.src.path} wins, because a file on ` + `disk is re-read on every call while an environment variable is a snapshot. Reconcile them \u2014 ` + `a rotation that updated only one leaves the other to fail 401 wherever it is loaded first.` : null;
|
|
48162
|
+
return sealCredential({
|
|
48163
|
+
apiKey: winner.value,
|
|
48164
|
+
tier: winner.src.tier,
|
|
48165
|
+
source: winner.src.path,
|
|
48166
|
+
deliberate: false,
|
|
48167
|
+
diskCandidates: diskPaths,
|
|
48168
|
+
warning
|
|
48169
|
+
});
|
|
48170
|
+
}
|
|
48171
|
+
if (envHit) {
|
|
48172
|
+
assertUsableCredential(name, envHit.key, envHit.value);
|
|
48173
|
+
return sealCredential({
|
|
48174
|
+
apiKey: envHit.value,
|
|
48175
|
+
tier: "env",
|
|
48176
|
+
source: envHit.key,
|
|
48177
|
+
deliberate: false,
|
|
48178
|
+
diskCandidates: diskPaths,
|
|
48179
|
+
warning: null
|
|
48180
|
+
});
|
|
48181
|
+
}
|
|
48182
|
+
return null;
|
|
48183
|
+
}
|
|
48184
|
+
async function completePointerCredential(name, pointerResolution, env3 = process.env) {
|
|
48185
|
+
const vaultKey = pointerResolution.pointerVaultKey;
|
|
48186
|
+
const pointerEnvKey = pointerResolution.source;
|
|
48187
|
+
if (!vaultKey) {
|
|
48188
|
+
throw new CredentialResolutionError(name, `Pointer resolution from ${pointerEnvKey} carries no vault item key; this is a defect in the resolver.`, [pointerEnvKey]);
|
|
48189
|
+
}
|
|
48190
|
+
let secretsSdk;
|
|
47802
48191
|
try {
|
|
47803
|
-
|
|
47804
|
-
} catch {
|
|
47805
|
-
|
|
48192
|
+
secretsSdk = requireSecretsSdk(SECRETS_PACKAGE_SPECIFIER);
|
|
48193
|
+
} catch {
|
|
48194
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the secrets SDK (@hasna/secrets) is not installed ` + `in this process. A vault pointer is TERMINAL: install @hasna/secrets to resolve it, or unset ${pointerEnvKey}.`, [pointerEnvKey]);
|
|
48195
|
+
}
|
|
48196
|
+
let client;
|
|
48197
|
+
try {
|
|
48198
|
+
client = secretsSdk.createSecretsClientFromEnv(env3);
|
|
48199
|
+
} catch {
|
|
48200
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the secrets client could not be configured from this ` + `environment (the secrets service URL and key env are missing or invalid). A vault pointer is TERMINAL and ` + `never falls through to a literal or disk credential.`, [pointerEnvKey]);
|
|
48201
|
+
}
|
|
48202
|
+
let secret;
|
|
48203
|
+
try {
|
|
48204
|
+
secret = await client.getSecret({ key: vaultKey });
|
|
48205
|
+
} catch {
|
|
48206
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} names vault item '${vaultKey}', but the vault could not be reached or the item is ` + `unavailable. A vault pointer is TERMINAL and never falls through to a literal or disk credential.`, [pointerEnvKey]);
|
|
48207
|
+
}
|
|
48208
|
+
const value = secret.value;
|
|
48209
|
+
if (!value) {
|
|
48210
|
+
throw new CredentialResolutionError(name, `${pointerEnvKey} resolved vault item '${vaultKey}', but it holds no value. A vault pointer is TERMINAL.`, [pointerEnvKey]);
|
|
48211
|
+
}
|
|
48212
|
+
assertUsableCredential(name, `${pointerEnvKey} -> vault:${vaultKey}`, value);
|
|
48213
|
+
return sealCredential({
|
|
48214
|
+
apiKey: value,
|
|
48215
|
+
tier: "pointer",
|
|
48216
|
+
source: `${pointerEnvKey} -> vault:${vaultKey}`,
|
|
48217
|
+
deliberate: true,
|
|
48218
|
+
diskCandidates: pointerResolution.diskCandidates,
|
|
48219
|
+
warning: null
|
|
48220
|
+
});
|
|
47806
48221
|
}
|
|
47807
|
-
function
|
|
47808
|
-
|
|
47809
|
-
return process.env.SKILLS_API_KEY;
|
|
47810
|
-
if (process.env.SKILL_API_KEY)
|
|
47811
|
-
return process.env.SKILL_API_KEY;
|
|
47812
|
-
return getAuthConfig()?.apiKey || null;
|
|
48222
|
+
function defaultFleetGatewayBaseUrl(name) {
|
|
48223
|
+
return `${DEFAULT_FLEET_GATEWAY_ORIGIN}/${validateAppSlug(name)}`;
|
|
47813
48224
|
}
|
|
47814
|
-
function
|
|
48225
|
+
function isValidDnsDomain(value) {
|
|
48226
|
+
if (value.length === 0 || value.length > 253 || ASCII_CONTROL_PATTERN.test(value) || /[^\x00-\x7f]/.test(value)) {
|
|
48227
|
+
return false;
|
|
48228
|
+
}
|
|
48229
|
+
return value.split(".").every((label) => label.length <= 63 && !label.startsWith("xn--") && DNS_LABEL_PATTERN.test(label));
|
|
48230
|
+
}
|
|
48231
|
+
function validateAppSlug(name) {
|
|
48232
|
+
if (name.length > 63 || !DNS_LABEL_PATTERN.test(name)) {
|
|
48233
|
+
throw new Error("App name must be one lowercase DNS label.");
|
|
48234
|
+
}
|
|
48235
|
+
return name;
|
|
48236
|
+
}
|
|
48237
|
+
function rawAuthority(value) {
|
|
48238
|
+
const match = /^[a-z][a-z0-9+.-]*:\/\//i.exec(value);
|
|
48239
|
+
if (!match)
|
|
48240
|
+
throw new Error("API URL must be absolute.");
|
|
48241
|
+
const afterScheme = value.slice(match[0].length);
|
|
48242
|
+
const boundary = afterScheme.search(/[/?#]/);
|
|
48243
|
+
const authority = boundary === -1 ? afterScheme : afterScheme.slice(0, boundary);
|
|
48244
|
+
if (!authority)
|
|
48245
|
+
throw new Error("API URL must include a hostname.");
|
|
48246
|
+
return authority;
|
|
48247
|
+
}
|
|
48248
|
+
function assertCanonicalPort(port) {
|
|
48249
|
+
if (!/^[0-9]+$/.test(port) || port.length > 1 && port.startsWith("0")) {
|
|
48250
|
+
throw new Error("API URL authority must contain a canonical port between 1 and 65535.");
|
|
48251
|
+
}
|
|
48252
|
+
const numericPort = Number(port);
|
|
48253
|
+
if (!Number.isSafeInteger(numericPort) || numericPort < 1 || numericPort > 65535) {
|
|
48254
|
+
throw new Error("API URL authority must contain a canonical port between 1 and 65535.");
|
|
48255
|
+
}
|
|
48256
|
+
}
|
|
48257
|
+
function canonicalAuthorityHostname(authority) {
|
|
48258
|
+
let rawHostname;
|
|
48259
|
+
if (authority.startsWith("[")) {
|
|
48260
|
+
const closingBracket = authority.indexOf("]");
|
|
48261
|
+
if (closingBracket === -1) {
|
|
48262
|
+
throw new Error("API URL authority must contain a canonical hostname.");
|
|
48263
|
+
}
|
|
48264
|
+
rawHostname = authority.slice(0, closingBracket + 1);
|
|
48265
|
+
const portSuffix = authority.slice(closingBracket + 1);
|
|
48266
|
+
if (portSuffix) {
|
|
48267
|
+
if (!portSuffix.startsWith(":")) {
|
|
48268
|
+
throw new Error("API URL authority must contain a canonical hostname and port.");
|
|
48269
|
+
}
|
|
48270
|
+
assertCanonicalPort(portSuffix.slice(1));
|
|
48271
|
+
}
|
|
48272
|
+
if (isIP(rawHostname.slice(1, -1)) !== 6) {
|
|
48273
|
+
throw new Error("API URL authority must contain a canonical IPv6 literal.");
|
|
48274
|
+
}
|
|
48275
|
+
} else {
|
|
48276
|
+
const firstColon = authority.indexOf(":");
|
|
48277
|
+
const lastColon = authority.lastIndexOf(":");
|
|
48278
|
+
if (firstColon !== lastColon) {
|
|
48279
|
+
throw new Error("IPv6 API URL authorities must use brackets.");
|
|
48280
|
+
}
|
|
48281
|
+
if (lastColon !== -1) {
|
|
48282
|
+
const port = authority.slice(lastColon + 1);
|
|
48283
|
+
assertCanonicalPort(port);
|
|
48284
|
+
rawHostname = authority.slice(0, lastColon);
|
|
48285
|
+
} else {
|
|
48286
|
+
rawHostname = authority;
|
|
48287
|
+
}
|
|
48288
|
+
const ipVersion = isIP(rawHostname);
|
|
48289
|
+
const numericAddressParts = rawHostname.split(".");
|
|
48290
|
+
const looksLikeNonCanonicalIpv4 = numericAddressParts.every((part) => /^(?:0x[0-9a-f]+|[0-9]+)$/i.test(part));
|
|
48291
|
+
if (ipVersion !== 4 && looksLikeNonCanonicalIpv4 || ipVersion !== 4 && !isValidDnsDomain(rawHostname.toLowerCase())) {
|
|
48292
|
+
throw new Error("API URL authority must contain a canonical ASCII hostname.");
|
|
48293
|
+
}
|
|
48294
|
+
}
|
|
48295
|
+
return rawHostname.toLowerCase();
|
|
48296
|
+
}
|
|
48297
|
+
function isDeliberateLoopbackHttpAuthority(authority) {
|
|
48298
|
+
return /^(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?$/i.test(authority);
|
|
48299
|
+
}
|
|
48300
|
+
function toV1BaseUrl(apiUrl) {
|
|
48301
|
+
if (ASCII_CONTROL_PATTERN.test(apiUrl)) {
|
|
48302
|
+
throw new Error("API URL must not contain ASCII control characters.");
|
|
48303
|
+
}
|
|
48304
|
+
const input = apiUrl.trim();
|
|
48305
|
+
const authority = rawAuthority(input);
|
|
48306
|
+
if (authority.includes("@") || authority.includes("\\") || authority.includes("%") || /[^\x00-\x7f]/.test(authority)) {
|
|
48307
|
+
throw new Error("API URL authority must be canonical ASCII without credentials.");
|
|
48308
|
+
}
|
|
48309
|
+
const canonicalHostname = canonicalAuthorityHostname(authority);
|
|
48310
|
+
const url = new URL(input);
|
|
48311
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
48312
|
+
throw new Error("API URL must use http or https.");
|
|
48313
|
+
}
|
|
48314
|
+
if (url.username || url.password) {
|
|
48315
|
+
throw new Error("API URL must not include credentials.");
|
|
48316
|
+
}
|
|
48317
|
+
if (!url.hostname || url.hostname.endsWith(".")) {
|
|
48318
|
+
throw new Error("API URL must include a canonical hostname.");
|
|
48319
|
+
}
|
|
48320
|
+
if (url.hostname.toLowerCase() !== canonicalHostname) {
|
|
48321
|
+
throw new Error("API URL authority must not rely on parser hostname normalization.");
|
|
48322
|
+
}
|
|
48323
|
+
if (url.hostname.split(".").some((label) => label.toLowerCase().startsWith("xn--"))) {
|
|
48324
|
+
throw new Error("API URL must not use IDN or punycode hostnames.");
|
|
48325
|
+
}
|
|
48326
|
+
if (url.protocol === "http:" && !isDeliberateLoopbackHttpAuthority(authority)) {
|
|
48327
|
+
throw new Error("API URL may use http only for an exact loopback authority.");
|
|
48328
|
+
}
|
|
48329
|
+
if (url.search || url.hash) {
|
|
48330
|
+
throw new Error("API URL must not include a query string or fragment.");
|
|
48331
|
+
}
|
|
48332
|
+
let path = url.pathname.replace(/\/+$/, "");
|
|
48333
|
+
if (path.endsWith("/v1"))
|
|
48334
|
+
path = path.slice(0, -"/v1".length);
|
|
48335
|
+
url.pathname = `${path}/v1`;
|
|
48336
|
+
return url.toString().replace(/\/+$/, "");
|
|
48337
|
+
}
|
|
48338
|
+
function resolveClientTransportSnapshot(name, env3 = process.env, options = {}) {
|
|
48339
|
+
env3 = snapshotClientEnvironment(name, env3);
|
|
48340
|
+
const keys2 = clientTransportEnvKeys(name);
|
|
48341
|
+
const definedUrlEntries = keys2.apiUrlKeys.filter((key) => Object.prototype.hasOwnProperty.call(env3, key) && env3[key] !== undefined).map((key) => ({ key, raw: String(env3[key]) }));
|
|
48342
|
+
const blankUrl = definedUrlEntries.find((entry) => entry.raw.trim().length === 0);
|
|
48343
|
+
if (blankUrl) {
|
|
48344
|
+
throw new ClientTransportConfigurationError(name, `${blankUrl.key} is set but blank; public clients require an explicit HTTPS API URL and never select local storage.`, [blankUrl.key]);
|
|
48345
|
+
}
|
|
48346
|
+
const controlledUrl = definedUrlEntries.find((entry) => ASCII_CONTROL_PATTERN.test(entry.raw));
|
|
48347
|
+
if (controlledUrl) {
|
|
48348
|
+
throw new ClientTransportConfigurationError(name, `${controlledUrl.key} contains ASCII control characters.`, [controlledUrl.key]);
|
|
48349
|
+
}
|
|
48350
|
+
const usableUrlEntries = definedUrlEntries.map((entry) => ({ key: entry.key, value: entry.raw.trim() }));
|
|
48351
|
+
if (usableUrlEntries.length > 1 && new Set(usableUrlEntries.map((entry) => entry.value)).size > 1) {
|
|
48352
|
+
throw new ClientTransportConfigurationError(name, `${usableUrlEntries.map((entry) => entry.key).join(" and ")} disagree; client authority aliases must be identical or only one may be set.`, usableUrlEntries.map((entry) => entry.key));
|
|
48353
|
+
}
|
|
48354
|
+
const envUrlHit = usableUrlEntries[0] ?? null;
|
|
48355
|
+
const keychainUrlHit = keychainConfigValue(name, env3, options.credentials?.keychain);
|
|
48356
|
+
const diskConfigUrlHit = appConfigDiskValue(name, env3, keys2.apiUrlKeys);
|
|
48357
|
+
if (diskConfigUrlHit?.unusable) {
|
|
48358
|
+
throw new ClientTransportConfigurationError(name, `${diskConfigUrlHit.key} in ${diskConfigUrlHit.path} is declared but blank or malformed; public clients require a valid HTTPS service authority.`, [diskConfigUrlHit.path]);
|
|
48359
|
+
}
|
|
48360
|
+
const urlCandidates = [
|
|
48361
|
+
...envUrlHit ? [envUrlHit] : [],
|
|
48362
|
+
...keychainUrlHit ? [{ key: keychainUrlHit.source, value: keychainUrlHit.value }] : [],
|
|
48363
|
+
...diskConfigUrlHit ? [{ key: diskConfigUrlHit.path, value: diskConfigUrlHit.value.trim() }] : []
|
|
48364
|
+
];
|
|
48365
|
+
const configuredUrl = urlCandidates[0] ?? null;
|
|
48366
|
+
const divergentUrls = urlCandidates.filter((candidate) => candidate.value !== configuredUrl?.value);
|
|
48367
|
+
if (configuredUrl && divergentUrls.length > 0) {
|
|
48368
|
+
throw new ClientTransportConfigurationError(name, `${configuredUrl.key} and ${divergentUrls.map((candidate) => candidate.key).join(" and ")} select different service authorities; refusing to send a credential written for one authority to the other.`, urlCandidates.map((candidate) => candidate.key));
|
|
48369
|
+
}
|
|
48370
|
+
const warnings = [];
|
|
48371
|
+
if (configuredUrl && !envUrlHit) {
|
|
48372
|
+
warnings.push(`No ${keys2.apiUrlKeys[0]} in the environment; the server URL in ${configuredUrl.key} was used, so this client connects to the server. ` + `Keep that entry aligned with the intended service authority.`);
|
|
48373
|
+
}
|
|
48374
|
+
const credential = resolveCredential(name, env3, options.credentials);
|
|
48375
|
+
if (!credential) {
|
|
48376
|
+
const diskHint = credentialDiskSourcesForMessage(name, env3);
|
|
48377
|
+
const lead = configuredUrl ? `${configuredUrl.key} selects the HTTP server for '${name}', but no API key could be resolved` : `${keys2.apiUrlKeys[0]} is not set and no API key could be resolved for '${name}'; a credential is required before the default fleet gateway authority applies`;
|
|
48378
|
+
warnings.push(`${lead}; refusing to create an unauthenticated client \u2014 public clients never fall back to SQLite or another local store. ` + `Looked in the Keychain (macOS only), then for a credential file at ${diskHint}, then for ${keys2.apiKeyKeys[0]} in the environment.`);
|
|
48379
|
+
throw new ClientTransportConfigurationError(name, warnings.join(" "), [configuredUrl?.key ?? keys2.apiUrlKeys[0]]);
|
|
48380
|
+
}
|
|
48381
|
+
if (credential.warning)
|
|
48382
|
+
warnings.push(credential.warning);
|
|
48383
|
+
let urlHit;
|
|
48384
|
+
if (configuredUrl) {
|
|
48385
|
+
urlHit = configuredUrl;
|
|
48386
|
+
} else {
|
|
48387
|
+
try {
|
|
48388
|
+
urlHit = { key: DEFAULT_AUTHORITY_SOURCE, value: defaultFleetGatewayBaseUrl(name) };
|
|
48389
|
+
} catch (error) {
|
|
48390
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48391
|
+
throw new ClientTransportConfigurationError(name, `No ${keys2.apiUrlKeys[0]} is configured and the default fleet gateway authority cannot be composed for '${name}': ${message}`, [keys2.apiUrlKeys[0]]);
|
|
48392
|
+
}
|
|
48393
|
+
}
|
|
48394
|
+
const apiUrlSource = urlHit.key;
|
|
48395
|
+
let baseUrl;
|
|
47815
48396
|
try {
|
|
47816
|
-
|
|
47817
|
-
|
|
47818
|
-
const
|
|
47819
|
-
|
|
47820
|
-
return null;
|
|
47821
|
-
return config;
|
|
47822
|
-
} catch {
|
|
47823
|
-
return null;
|
|
48397
|
+
baseUrl = toV1BaseUrl(urlHit.value);
|
|
48398
|
+
} catch (error) {
|
|
48399
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48400
|
+
throw new ClientTransportConfigurationError(name, `Invalid API URL from ${apiUrlSource}: ${message}`, [apiUrlSource]);
|
|
47824
48401
|
}
|
|
48402
|
+
return {
|
|
48403
|
+
resolution: {
|
|
48404
|
+
transport: "http",
|
|
48405
|
+
transportSource: urlHit.key,
|
|
48406
|
+
baseUrl,
|
|
48407
|
+
apiUrlSource,
|
|
48408
|
+
apiKeyPresent: true,
|
|
48409
|
+
apiKeySource: credential.source,
|
|
48410
|
+
apiKeyTier: credential.tier,
|
|
48411
|
+
misconfigured: false,
|
|
48412
|
+
warning: warnings.length > 0 ? warnings.join(" ") : null
|
|
48413
|
+
},
|
|
48414
|
+
credential
|
|
48415
|
+
};
|
|
48416
|
+
}
|
|
48417
|
+
function resolveClientTransport(name, env3 = process.env, options = {}) {
|
|
48418
|
+
return resolveClientTransportSnapshot(name, env3, options).resolution;
|
|
47825
48419
|
}
|
|
47826
|
-
function
|
|
47827
|
-
|
|
47828
|
-
|
|
47829
|
-
|
|
47830
|
-
|
|
47831
|
-
|
|
48420
|
+
function credentialDiskSourcesForMessage(name, env3) {
|
|
48421
|
+
const paths = credentialDiskSources(name, env3);
|
|
48422
|
+
return paths.length > 0 ? paths.join(" or ") : "<no HOME or HASNA_HOME set in this environment, so no credential file was consulted>";
|
|
48423
|
+
}
|
|
48424
|
+
var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE", CredentialResolutionError, CredentialFileUnsafeError, HASNA_HOME_ENV_KEY = "HASNA_HOME", HASNA_CONFIG_HOME_ENV_KEY = "HASNA_CONFIG_HOME", KEYCHAIN_STATION_ENV_KEY = "HASNA_STATION", HASNA_HOME_DIR = ".hasna", CONFIG_SUBDIR = "config", CREDENTIALS_FILE = "credentials", KEYCHAIN_SECURITY_BIN = "/usr/bin/security", KEYCHAIN_SERVICE_PREFIX = "hasna.credentials", KEYCHAIN_ITEM_NOT_FOUND_STATUS = 44, KEYCHAIN_SPAWN_TIMEOUT_MS = 1e4, MAX_CREDENTIAL_FILE_BYTES, SAFE_APP_SLUG, SAFE_PROFILE, ILLEGAL_IN_HEADER_VALUE, VAULT_POINTER_SHAPE, CREDENTIAL_SHAPED_KEY, INSPECT_CUSTOM, CREDENTIAL_SEAL, AMBIENT_ENVIRONMENT, SECRETS_PACKAGE_SPECIFIER, requireSecretsSdk, DEFAULT_FLEET_GATEWAY_ORIGIN = "https://api.hasna.com", DEFAULT_AUTHORITY_SOURCE = "default", ASCII_CONTROL_PATTERN, DNS_LABEL_PATTERN, ClientTransportConfigurationError, IDEMPOTENT_METHODS, AUTHORITY_OVERRIDE_HEADERS;
|
|
48425
|
+
var init_transport = __esm(() => {
|
|
48426
|
+
CredentialResolutionError = class CredentialResolutionError extends Error {
|
|
48427
|
+
appName;
|
|
48428
|
+
attempted;
|
|
48429
|
+
constructor(appName, message, attempted) {
|
|
48430
|
+
super(message);
|
|
48431
|
+
this.name = "CredentialResolutionError";
|
|
48432
|
+
this.appName = appName;
|
|
48433
|
+
this.attempted = attempted;
|
|
48434
|
+
}
|
|
48435
|
+
};
|
|
48436
|
+
CredentialFileUnsafeError = class CredentialFileUnsafeError extends Error {
|
|
48437
|
+
path;
|
|
48438
|
+
constructor(path, reason) {
|
|
48439
|
+
super(`Refusing unsafe credential/config file ${path}: ${reason}.`);
|
|
48440
|
+
this.name = "CredentialFileUnsafeError";
|
|
48441
|
+
this.path = path;
|
|
48442
|
+
}
|
|
48443
|
+
};
|
|
48444
|
+
MAX_CREDENTIAL_FILE_BYTES = 64 * 1024;
|
|
48445
|
+
SAFE_APP_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
48446
|
+
SAFE_PROFILE = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
|
|
48447
|
+
ILLEGAL_IN_HEADER_VALUE = /[^\t\x20-\x7e]/;
|
|
48448
|
+
VAULT_POINTER_SHAPE = /^[a-z0-9][a-z0-9-]*(?:\/[a-z0-9][a-z0-9-_.]*){2,}$/;
|
|
48449
|
+
CREDENTIAL_SHAPED_KEY = /(?:^|_)(?:API_KEY|KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)(?:_|$)/;
|
|
48450
|
+
INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
|
|
48451
|
+
CREDENTIAL_SEAL = Symbol.for("hasna:contracts:sealedCredential");
|
|
48452
|
+
AMBIENT_ENVIRONMENT = Symbol.for("hasna:contracts:ambientClientEnvironment");
|
|
48453
|
+
SECRETS_PACKAGE_SPECIFIER = "@hasna/" + "secrets";
|
|
48454
|
+
requireSecretsSdk = createRequire(import.meta.url);
|
|
48455
|
+
ASCII_CONTROL_PATTERN = /[\u0000-\u001f\u007f]/;
|
|
48456
|
+
DNS_LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
48457
|
+
ClientTransportConfigurationError = class ClientTransportConfigurationError extends Error {
|
|
48458
|
+
appName;
|
|
48459
|
+
sources;
|
|
48460
|
+
constructor(appName, message, sources = []) {
|
|
48461
|
+
super(message);
|
|
48462
|
+
this.name = "ClientTransportConfigurationError";
|
|
48463
|
+
this.appName = appName;
|
|
48464
|
+
this.sources = Object.freeze([...sources]);
|
|
48465
|
+
}
|
|
48466
|
+
};
|
|
48467
|
+
IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
48468
|
+
AUTHORITY_OVERRIDE_HEADERS = new Set([
|
|
48469
|
+
"host",
|
|
48470
|
+
":authority",
|
|
48471
|
+
"forwarded",
|
|
48472
|
+
"x-forwarded-host",
|
|
48473
|
+
"x-original-host"
|
|
48474
|
+
]);
|
|
48475
|
+
});
|
|
48476
|
+
|
|
48477
|
+
// src/lib/fleet-credentials.ts
|
|
48478
|
+
var exports_fleet_credentials = {};
|
|
48479
|
+
__export(exports_fleet_credentials, {
|
|
48480
|
+
skillsCredentialOrReason: () => skillsCredentialOrReason,
|
|
48481
|
+
skillsCredentialFiles: () => skillsCredentialFiles,
|
|
48482
|
+
skillsCredentialFilePath: () => skillsCredentialFilePath,
|
|
48483
|
+
resolveSkillsFleet: () => resolveSkillsFleet,
|
|
48484
|
+
resolveSkillsApiOrigin: () => resolveSkillsApiOrigin,
|
|
48485
|
+
resolveSkillsApiKey: () => resolveSkillsApiKey,
|
|
48486
|
+
resetLocalSkillsModeNotice: () => resetLocalSkillsModeNotice,
|
|
48487
|
+
requireSkillsFleet: () => requireSkillsFleet,
|
|
48488
|
+
requireSkillsApiOrigin: () => requireSkillsApiOrigin,
|
|
48489
|
+
requireSkillsApiKey: () => requireSkillsApiKey,
|
|
48490
|
+
noticeLocalSkillsMode: () => noticeLocalSkillsMode,
|
|
48491
|
+
normalizeSkillsApiOrigin: () => normalizeSkillsApiOrigin,
|
|
48492
|
+
configuredSkillsApiUrl: () => configuredSkillsApiUrl,
|
|
48493
|
+
SkillsFleetCredentialError: () => SkillsFleetCredentialError,
|
|
48494
|
+
SKILLS_APP: () => SKILLS_APP,
|
|
48495
|
+
SKILLS_API_URL_ENV_KEYS: () => SKILLS_API_URL_ENV_KEYS,
|
|
48496
|
+
SKILLS_API_URL_ENV: () => SKILLS_API_URL_ENV,
|
|
48497
|
+
SKILLS_API_KEY_ENV_KEYS: () => SKILLS_API_KEY_ENV_KEYS,
|
|
48498
|
+
SKILLS_API_KEY_ENV: () => SKILLS_API_KEY_ENV,
|
|
48499
|
+
MissingSkillsFleetError: () => MissingSkillsFleetError
|
|
48500
|
+
});
|
|
48501
|
+
function isClientTransportConfigurationError(error) {
|
|
48502
|
+
return error instanceof ClientTransportConfigurationError || typeof error === "object" && error !== null && error.name === "ClientTransportConfigurationError";
|
|
48503
|
+
}
|
|
48504
|
+
function isCredentialResolutionError(error) {
|
|
48505
|
+
return error instanceof CredentialResolutionError || typeof error === "object" && error !== null && error.name === "CredentialResolutionError";
|
|
48506
|
+
}
|
|
48507
|
+
function asSkillsFleetCredentialError(error) {
|
|
48508
|
+
if (!isCredentialResolutionError(error))
|
|
48509
|
+
return null;
|
|
48510
|
+
return new SkillsFleetCredentialError(error.message, "MISSING_API_CREDENTIAL");
|
|
47832
48511
|
}
|
|
47833
48512
|
function normalizeSkillsApiOrigin(apiUrl) {
|
|
47834
48513
|
const url = new URL(apiUrl);
|
|
@@ -47842,36 +48521,211 @@ function normalizeSkillsApiOrigin(apiUrl) {
|
|
|
47842
48521
|
}
|
|
47843
48522
|
return url.toString().replace(/\/+$/, "");
|
|
47844
48523
|
}
|
|
47845
|
-
function
|
|
47846
|
-
|
|
48524
|
+
function configuredSkillsApiUrl(env3 = process.env, keychain) {
|
|
48525
|
+
for (const key of SKILLS_API_URL_ENV_KEYS) {
|
|
48526
|
+
const value = env3[key]?.trim();
|
|
48527
|
+
if (value)
|
|
48528
|
+
return { value, source: key };
|
|
48529
|
+
}
|
|
48530
|
+
const fromKeychain = keychainConfigValue(SKILLS_APP, env3, keychain);
|
|
48531
|
+
if (fromKeychain)
|
|
48532
|
+
return { value: fromKeychain.value.trim(), source: fromKeychain.source };
|
|
48533
|
+
const fromDisk = appConfigDiskValue(SKILLS_APP, env3, SKILLS_API_URL_ENV_KEYS);
|
|
48534
|
+
if (fromDisk?.unusable) {
|
|
48535
|
+
throw new SkillsFleetCredentialError(`${fromDisk.key} in ${fromDisk.path} is declared but blank or malformed; ` + `a Skills authority must be a valid https URL (or an exact loopback http URL).`, "INVALID_API_URL");
|
|
48536
|
+
}
|
|
48537
|
+
if (fromDisk)
|
|
48538
|
+
return { value: fromDisk.value.trim(), source: fromDisk.path };
|
|
48539
|
+
return null;
|
|
47847
48540
|
}
|
|
47848
|
-
|
|
47849
|
-
|
|
47850
|
-
|
|
47851
|
-
|
|
48541
|
+
function skillsCredentialFiles(env3 = process.env) {
|
|
48542
|
+
return credentialDiskSources(SKILLS_APP, env3);
|
|
48543
|
+
}
|
|
48544
|
+
function skillsCredentialFilePath(env3 = process.env) {
|
|
48545
|
+
const paths = skillsCredentialFiles(env3);
|
|
48546
|
+
const path = paths[0];
|
|
48547
|
+
if (!path) {
|
|
48548
|
+
throw new Error("No home directory is set (HOME or HASNA_HOME), so there is nowhere to store a Skills credential.");
|
|
48549
|
+
}
|
|
48550
|
+
return path;
|
|
48551
|
+
}
|
|
48552
|
+
function noticeLocalSkillsMode(write = (line) => console.error(line)) {
|
|
48553
|
+
if (localNoticePrinted)
|
|
48554
|
+
return;
|
|
48555
|
+
localNoticePrinted = true;
|
|
48556
|
+
write(`skills: local mode \u2014 no ${SKILLS_API_KEY_ENV} and no ${SKILLS_API_URL_ENV} resolved, ` + `so this runs on this machine against the bundled corpus. ` + `Sign in with: skills auth login`);
|
|
48557
|
+
}
|
|
48558
|
+
function resetLocalSkillsModeNotice() {
|
|
48559
|
+
localNoticePrinted = false;
|
|
48560
|
+
}
|
|
48561
|
+
function resolveSkillsFleet(env3 = process.env, options = {}) {
|
|
48562
|
+
try {
|
|
48563
|
+
return resolveSkillsFleetOrThrow(env3, options);
|
|
48564
|
+
} catch (error) {
|
|
48565
|
+
const translated = asSkillsFleetCredentialError(error);
|
|
48566
|
+
if (translated)
|
|
48567
|
+
throw translated;
|
|
48568
|
+
throw error;
|
|
48569
|
+
}
|
|
48570
|
+
}
|
|
48571
|
+
function resolveSkillsFleetOrThrow(env3, options) {
|
|
48572
|
+
let resolution;
|
|
48573
|
+
try {
|
|
48574
|
+
resolution = resolveClientTransport(SKILLS_APP, env3, { credentials: options.credentials });
|
|
48575
|
+
} catch (error) {
|
|
48576
|
+
if (!isClientTransportConfigurationError(error))
|
|
48577
|
+
throw error;
|
|
48578
|
+
const configured2 = configuredSkillsApiUrl(env3, options.credentials?.keychain);
|
|
48579
|
+
const credential2 = resolveCredential(SKILLS_APP, env3, options.credentials);
|
|
48580
|
+
if (!configured2 && !credential2) {
|
|
48581
|
+
if (env3 === process.env)
|
|
48582
|
+
noticeLocalSkillsMode();
|
|
48583
|
+
return { mode: "local", apiOrigin: null, apiKey: null };
|
|
48584
|
+
}
|
|
48585
|
+
if (configured2 && !credential2) {
|
|
48586
|
+
throw new SkillsFleetCredentialError(`${configured2.source} points this CLI at a Skills service but no API key resolved \u2014 ` + `refusing to run locally instead. Looked in the Keychain item ` + `hasna.credentials.${SKILLS_APP}.api-key, then ${skillsCredentialFiles(env3).join(" or ") || "no credentials file (no HOME)"}, ` + `then ${SKILLS_API_KEY_ENV}. Sign in with: skills auth login`);
|
|
48587
|
+
}
|
|
48588
|
+
throw error;
|
|
48589
|
+
}
|
|
48590
|
+
const configured = configuredSkillsApiUrl(env3, options.credentials?.keychain);
|
|
48591
|
+
const apiOrigin = configured ? normalizeSkillsApiOrigin(configured.value) : stripV1(resolution.baseUrl);
|
|
48592
|
+
const credential = resolveCredential(SKILLS_APP, env3, options.credentials);
|
|
48593
|
+
if (!credential) {
|
|
48594
|
+
throw new SkillsFleetCredentialError(`A Skills authority resolved but no API key did. Sign in with: skills auth login`);
|
|
48595
|
+
}
|
|
48596
|
+
const base2 = {
|
|
48597
|
+
mode: "hosted",
|
|
48598
|
+
apiOrigin,
|
|
48599
|
+
apiUrlSource: resolution.apiUrlSource ?? (configured?.source ?? "default"),
|
|
48600
|
+
apiKeySource: resolution.apiKeySource ?? credential.source,
|
|
48601
|
+
apiKeyTier: resolution.apiKeyTier,
|
|
48602
|
+
warning: resolution.warning
|
|
48603
|
+
};
|
|
48604
|
+
if (credential.tier === "pointer") {
|
|
48605
|
+
return { ...base2, apiKey: null, apiKeyPointer: credential };
|
|
48606
|
+
}
|
|
48607
|
+
if (!credential.apiKey.trim()) {
|
|
48608
|
+
throw new SkillsFleetCredentialError(`The Skills API key from ${credential.source} is empty \u2014 refusing to send an unauthenticated request. ` + `Sign in with: skills auth login`);
|
|
48609
|
+
}
|
|
48610
|
+
return { ...base2, apiKey: credential.apiKey, apiKeyPointer: null };
|
|
48611
|
+
}
|
|
48612
|
+
async function resolveSkillsApiKey(env3 = process.env, options = {}) {
|
|
48613
|
+
const fleet = resolveSkillsFleet(env3, options);
|
|
48614
|
+
if (fleet.mode !== "hosted")
|
|
48615
|
+
return null;
|
|
48616
|
+
if (fleet.apiKey)
|
|
48617
|
+
return fleet.apiKey;
|
|
48618
|
+
const pointer = fleet.apiKeyPointer;
|
|
48619
|
+
if (!pointer) {
|
|
48620
|
+
throw new SkillsFleetCredentialError(`A Skills authority resolved but no API key did. Sign in with: skills auth login`);
|
|
48621
|
+
}
|
|
48622
|
+
let completed;
|
|
48623
|
+
try {
|
|
48624
|
+
completed = await completePointerCredential(SKILLS_APP, pointer, env3);
|
|
48625
|
+
} catch (error) {
|
|
48626
|
+
const translated = asSkillsFleetCredentialError(error);
|
|
48627
|
+
if (translated)
|
|
48628
|
+
throw translated;
|
|
48629
|
+
throw error;
|
|
48630
|
+
}
|
|
48631
|
+
if (!completed.apiKey?.trim()) {
|
|
48632
|
+
throw new SkillsFleetCredentialError(`${credentialPointerEnvKey(SKILLS_APP)} names a vault item that produced an empty Skills API key \u2014 ` + `refusing to send an unauthenticated request.`);
|
|
48633
|
+
}
|
|
48634
|
+
return completed.apiKey;
|
|
48635
|
+
}
|
|
48636
|
+
async function requireSkillsApiKey(action = "This command", env3 = process.env, options = {}) {
|
|
48637
|
+
const apiKey = await resolveSkillsApiKey(env3, options);
|
|
48638
|
+
if (!apiKey)
|
|
48639
|
+
throw new MissingSkillsFleetError(action);
|
|
48640
|
+
return apiKey;
|
|
48641
|
+
}
|
|
48642
|
+
function stripV1(baseUrl) {
|
|
48643
|
+
return baseUrl.replace(/\/v1$/, "").replace(/\/+$/, "");
|
|
48644
|
+
}
|
|
48645
|
+
async function skillsCredentialOrReason(env3 = process.env, options = {}) {
|
|
48646
|
+
try {
|
|
48647
|
+
const apiKey = await resolveSkillsApiKey(env3, options);
|
|
48648
|
+
return apiKey ? { apiKey, reason: null } : { apiKey: null, reason: null };
|
|
48649
|
+
} catch (error) {
|
|
48650
|
+
if (error instanceof SkillsFleetCredentialError || error?.name === "SkillsFleetCredentialError") {
|
|
48651
|
+
return { apiKey: null, reason: error.message };
|
|
48652
|
+
}
|
|
48653
|
+
throw error;
|
|
48654
|
+
}
|
|
48655
|
+
}
|
|
48656
|
+
function resolveSkillsApiOrigin(env3 = process.env, options = {}) {
|
|
48657
|
+
const configured = configuredSkillsApiUrl(env3, options.credentials?.keychain);
|
|
48658
|
+
if (configured) {
|
|
48659
|
+
toV1BaseUrl(configured.value);
|
|
48660
|
+
return { origin: normalizeSkillsApiOrigin(configured.value), source: configured.source };
|
|
48661
|
+
}
|
|
48662
|
+
const fleet = resolveSkillsFleet(env3, options);
|
|
48663
|
+
return fleet.mode === "hosted" ? { origin: fleet.apiOrigin, source: fleet.apiUrlSource } : null;
|
|
48664
|
+
}
|
|
48665
|
+
function requireSkillsApiOrigin(action = "This command", env3 = process.env, options = {}) {
|
|
48666
|
+
const resolved = resolveSkillsApiOrigin(env3, options);
|
|
48667
|
+
if (!resolved)
|
|
48668
|
+
throw new MissingSkillsFleetError(action);
|
|
48669
|
+
return resolved.origin;
|
|
48670
|
+
}
|
|
48671
|
+
function requireSkillsFleet(action = "This command", env3 = process.env, options = {}) {
|
|
48672
|
+
const fleet = resolveSkillsFleet(env3, options);
|
|
48673
|
+
if (fleet.mode === "hosted")
|
|
48674
|
+
return fleet;
|
|
48675
|
+
throw new MissingSkillsFleetError(action);
|
|
48676
|
+
}
|
|
48677
|
+
var SKILLS_APP = "skills", ENV_KEYS, SKILLS_API_URL_ENV_KEYS, SKILLS_API_KEY_ENV_KEYS, SKILLS_API_URL_ENV, SKILLS_API_KEY_ENV, SkillsFleetCredentialError, localNoticePrinted = false, MissingSkillsFleetError;
|
|
48678
|
+
var init_fleet_credentials = __esm(() => {
|
|
48679
|
+
init_transport();
|
|
48680
|
+
ENV_KEYS = clientTransportEnvKeys(SKILLS_APP);
|
|
48681
|
+
SKILLS_API_URL_ENV_KEYS = ENV_KEYS.apiUrlKeys;
|
|
48682
|
+
SKILLS_API_KEY_ENV_KEYS = ENV_KEYS.apiKeyKeys;
|
|
48683
|
+
SKILLS_API_URL_ENV = SKILLS_API_URL_ENV_KEYS[0];
|
|
48684
|
+
SKILLS_API_KEY_ENV = SKILLS_API_KEY_ENV_KEYS[0];
|
|
48685
|
+
SkillsFleetCredentialError = class SkillsFleetCredentialError extends Error {
|
|
48686
|
+
code;
|
|
48687
|
+
constructor(message, code = "MISSING_API_CREDENTIAL") {
|
|
48688
|
+
super(message);
|
|
48689
|
+
this.name = "SkillsFleetCredentialError";
|
|
48690
|
+
this.code = code;
|
|
48691
|
+
}
|
|
48692
|
+
};
|
|
48693
|
+
MissingSkillsFleetError = class MissingSkillsFleetError extends Error {
|
|
48694
|
+
code = "MISSING_API_URL";
|
|
48695
|
+
constructor(action = "This command") {
|
|
48696
|
+
super(`${action} requires a Skills API credential and none is configured \u2014 ` + `run: skills auth login, or set ${SKILLS_API_KEY_ENV} ` + `(add the Keychain item hasna.credentials.${SKILLS_APP}.api-key, or write ~/.hasna/skills/config/credentials). ` + `Point at your own instance with ${SKILLS_API_URL_ENV}, or run: skills setup --api-url <your Skills instance origin>`);
|
|
48697
|
+
this.name = "MissingSkillsFleetError";
|
|
48698
|
+
}
|
|
48699
|
+
};
|
|
48700
|
+
});
|
|
48701
|
+
|
|
48702
|
+
// src/lib/api-url.ts
|
|
48703
|
+
function resolveApiUrl(env3 = process.env, options = {}) {
|
|
48704
|
+
const fleet = resolveSkillsFleet(env3, options);
|
|
48705
|
+
return fleet.mode === "hosted" ? fleet.apiOrigin : undefined;
|
|
48706
|
+
}
|
|
48707
|
+
var API_URL_ENV_VAR, MISSING_API_URL_HINT;
|
|
48708
|
+
var init_api_url = __esm(() => {
|
|
48709
|
+
init_fleet_credentials();
|
|
48710
|
+
init_fleet_credentials();
|
|
48711
|
+
API_URL_ENV_VAR = SKILLS_API_URL_ENV;
|
|
48712
|
+
MISSING_API_URL_HINT = `run: skills auth login, or set ${API_URL_ENV_VAR}=<your Skills instance origin>, ` + `or run: skills setup --api-url <your Skills instance origin>`;
|
|
47852
48713
|
});
|
|
47853
48714
|
|
|
47854
48715
|
// src/lib/remote-registry.ts
|
|
47855
|
-
function getConfiguredApiUrl(
|
|
47856
|
-
return resolveApiUrl(
|
|
48716
|
+
function getConfiguredApiUrl(env3 = process.env) {
|
|
48717
|
+
return resolveApiUrl(env3);
|
|
47857
48718
|
}
|
|
47858
48719
|
function buildSkillsApiUrl(apiUrl, endpoint = "/skills") {
|
|
47859
48720
|
const url = new URL(apiUrl);
|
|
47860
48721
|
const cleanEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
47861
48722
|
const pathname = url.pathname.replace(/\/+$/, "");
|
|
47862
|
-
|
|
47863
|
-
|
|
47864
|
-
|
|
47865
|
-
} else {
|
|
47866
|
-
url.pathname = `${pathname.slice(0, -"/skills".length)}${cleanEndpoint}` || cleanEndpoint;
|
|
47867
|
-
}
|
|
48723
|
+
const apiBase = /\/api(?:\/v1)?\/skills$/.test(pathname) ? pathname.slice(0, -"/skills".length) : pathname;
|
|
48724
|
+
if (/\/api(?:\/v1)?$/.test(apiBase)) {
|
|
48725
|
+
url.pathname = `${apiBase}${cleanEndpoint}`;
|
|
47868
48726
|
return url.toString();
|
|
47869
48727
|
}
|
|
47870
|
-
|
|
47871
|
-
url.pathname = `${pathname}${cleanEndpoint}`;
|
|
47872
|
-
return url.toString();
|
|
47873
|
-
}
|
|
47874
|
-
url.pathname = `${pathname}/api/v1${cleanEndpoint}`.replace(/\/{2,}/g, "/");
|
|
48728
|
+
url.pathname = `${apiBase}/api/v1${cleanEndpoint}`.replace(/\/{2,}/g, "/");
|
|
47875
48729
|
return url.toString();
|
|
47876
48730
|
}
|
|
47877
48731
|
function titleize(name) {
|
|
@@ -47932,9 +48786,9 @@ function parseRemoteContract(schema, payload, message) {
|
|
|
47932
48786
|
throw error;
|
|
47933
48787
|
}
|
|
47934
48788
|
}
|
|
47935
|
-
function remoteRequestHeaders(options) {
|
|
48789
|
+
async function remoteRequestHeaders(options) {
|
|
47936
48790
|
const headers = new Headers({ Accept: "application/json" });
|
|
47937
|
-
const token = options.authToken !== undefined ? options.authToken :
|
|
48791
|
+
const token = options.authToken !== undefined ? options.authToken : await resolveSkillsApiKey();
|
|
47938
48792
|
const trimmed = token?.trim();
|
|
47939
48793
|
if (trimmed)
|
|
47940
48794
|
headers.set("Authorization", `Bearer ${trimmed}`);
|
|
@@ -47942,11 +48796,12 @@ function remoteRequestHeaders(options) {
|
|
|
47942
48796
|
}
|
|
47943
48797
|
async function fetchRemoteJson(url, options) {
|
|
47944
48798
|
const fetchImpl = options.fetchImpl || fetch;
|
|
48799
|
+
const headers = await remoteRequestHeaders(options);
|
|
47945
48800
|
const controller = new AbortController;
|
|
47946
48801
|
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 1e4);
|
|
47947
48802
|
try {
|
|
47948
48803
|
const response = await fetchImpl(url, {
|
|
47949
|
-
headers
|
|
48804
|
+
headers,
|
|
47950
48805
|
signal: controller.signal
|
|
47951
48806
|
});
|
|
47952
48807
|
if (!response.ok) {
|
|
@@ -47960,7 +48815,7 @@ async function fetchRemoteJson(url, options) {
|
|
|
47960
48815
|
async function loadRemoteRegistry(options = {}) {
|
|
47961
48816
|
const apiUrl = options.apiUrl || getConfiguredApiUrl();
|
|
47962
48817
|
if (!apiUrl) {
|
|
47963
|
-
throw new Error(
|
|
48818
|
+
throw new Error(`Remote registry requires a Skills credential (${SKILLS_API_KEY_ENV}, the Keychain item, or ~/.hasna/skills/config/credentials) and, for your own instance, ${SKILLS_API_URL_ENV}`);
|
|
47964
48819
|
}
|
|
47965
48820
|
const url = buildSkillsApiUrl(apiUrl, options.endpoint);
|
|
47966
48821
|
return parseRemoteRegistryPayload(await fetchRemoteJson(url, options));
|
|
@@ -47969,16 +48824,15 @@ async function mergeRemoteRegistry(local, options = {}) {
|
|
|
47969
48824
|
const apiUrl = options.apiUrl || getConfiguredApiUrl();
|
|
47970
48825
|
if (!apiUrl)
|
|
47971
48826
|
return local;
|
|
47972
|
-
|
|
47973
|
-
if (!token?.trim())
|
|
48827
|
+
if (options.authToken !== undefined && !options.authToken?.trim())
|
|
47974
48828
|
return local;
|
|
47975
|
-
const remote = await loadRemoteRegistry({ ...options, apiUrl
|
|
48829
|
+
const remote = await loadRemoteRegistry({ ...options, apiUrl });
|
|
47976
48830
|
return mergeSkillRegistryLists(local, remote);
|
|
47977
48831
|
}
|
|
47978
48832
|
async function loadRemoteSkill(name, options = {}) {
|
|
47979
48833
|
const apiUrl = options.apiUrl || getConfiguredApiUrl();
|
|
47980
48834
|
if (!apiUrl) {
|
|
47981
|
-
throw new Error(
|
|
48835
|
+
throw new Error(`Remote registry requires a Skills credential (${SKILLS_API_KEY_ENV}, the Keychain item, or ~/.hasna/skills/config/credentials) and, for your own instance, ${SKILLS_API_URL_ENV}`);
|
|
47982
48836
|
}
|
|
47983
48837
|
const slug = encodeURIComponent(name);
|
|
47984
48838
|
const url = buildSkillsApiUrl(apiUrl, options.endpoint ?? `/skills/${slug}`);
|
|
@@ -47988,8 +48842,7 @@ var remoteAvailabilitySchema, remoteSkillSchema, secretValuePatterns, remoteSkil
|
|
|
47988
48842
|
var init_remote_registry = __esm(() => {
|
|
47989
48843
|
init_zod();
|
|
47990
48844
|
init_api_url();
|
|
47991
|
-
|
|
47992
|
-
init_config();
|
|
48845
|
+
init_fleet_credentials();
|
|
47993
48846
|
init_discovery();
|
|
47994
48847
|
init_registry_merge();
|
|
47995
48848
|
remoteAvailabilitySchema = exports_external.object({
|
|
@@ -48034,6 +48887,162 @@ var init_remote_registry = __esm(() => {
|
|
|
48034
48887
|
]);
|
|
48035
48888
|
});
|
|
48036
48889
|
|
|
48890
|
+
// src/lib/auth-store.ts
|
|
48891
|
+
import { chmodSync, existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync12, renameSync as renameSync4, statSync as statSync9, unlinkSync, writeFileSync as writeFileSync7 } from "fs";
|
|
48892
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
48893
|
+
function getAuthFilePath(env3 = process.env) {
|
|
48894
|
+
return skillsCredentialFilePath(env3);
|
|
48895
|
+
}
|
|
48896
|
+
function getIdentityFilePath(env3 = process.env) {
|
|
48897
|
+
return join16(dirname6(skillsCredentialFilePath(env3)), "identity.json");
|
|
48898
|
+
}
|
|
48899
|
+
function getAuthIdentity(env3 = process.env) {
|
|
48900
|
+
return readIdentity(env3);
|
|
48901
|
+
}
|
|
48902
|
+
function readIdentity(env3 = process.env) {
|
|
48903
|
+
try {
|
|
48904
|
+
const parsed = JSON.parse(readFileSync12(getIdentityFilePath(env3), "utf-8"));
|
|
48905
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
48906
|
+
return {};
|
|
48907
|
+
const record = parsed;
|
|
48908
|
+
const identity2 = {};
|
|
48909
|
+
for (const field of ["email", "orgId", "orgSlug", "userId"]) {
|
|
48910
|
+
const value = record[field];
|
|
48911
|
+
if (typeof value === "string" && value.length > 0)
|
|
48912
|
+
identity2[field] = value;
|
|
48913
|
+
}
|
|
48914
|
+
return identity2;
|
|
48915
|
+
} catch {
|
|
48916
|
+
return {};
|
|
48917
|
+
}
|
|
48918
|
+
}
|
|
48919
|
+
function getAuthConfig(env3 = process.env, options = {}) {
|
|
48920
|
+
const fleet = resolveSkillsFleet(env3, options);
|
|
48921
|
+
if (fleet.mode !== "hosted")
|
|
48922
|
+
return null;
|
|
48923
|
+
return { apiKey: fleet.apiKey, ...readIdentity(env3) };
|
|
48924
|
+
}
|
|
48925
|
+
function writeCredentialValues(values2, env3 = process.env) {
|
|
48926
|
+
const file = skillsCredentialFilePath(env3);
|
|
48927
|
+
mkdirSync7(dirname6(file), { recursive: true, mode: 448 });
|
|
48928
|
+
const lines = [];
|
|
48929
|
+
const written = new Set;
|
|
48930
|
+
if (existsSync15(file)) {
|
|
48931
|
+
for (const raw of readFileSync12(file, "utf-8").split(/\r?\n/)) {
|
|
48932
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(raw);
|
|
48933
|
+
const key = match?.[1];
|
|
48934
|
+
if (key && key in values2) {
|
|
48935
|
+
const next = values2[key];
|
|
48936
|
+
if (next !== null && next !== undefined) {
|
|
48937
|
+
lines.push(`${key}=${next}`);
|
|
48938
|
+
written.add(key);
|
|
48939
|
+
}
|
|
48940
|
+
continue;
|
|
48941
|
+
}
|
|
48942
|
+
lines.push(raw);
|
|
48943
|
+
}
|
|
48944
|
+
}
|
|
48945
|
+
for (const [key, value] of Object.entries(values2)) {
|
|
48946
|
+
if (value === null || value === undefined || written.has(key))
|
|
48947
|
+
continue;
|
|
48948
|
+
lines.push(`${key}=${value}`);
|
|
48949
|
+
}
|
|
48950
|
+
const body = lines.filter((line, index) => !(line.trim() === "" && index === lines.length - 1)).join(`
|
|
48951
|
+
`) + `
|
|
48952
|
+
`;
|
|
48953
|
+
const temp = `${file}.tmp-${process.pid}`;
|
|
48954
|
+
writeFileSync7(temp, body, { mode: 384 });
|
|
48955
|
+
chmodSync(temp, 384);
|
|
48956
|
+
renameSync4(temp, file);
|
|
48957
|
+
return file;
|
|
48958
|
+
}
|
|
48959
|
+
function readCredentialValue(key, env3 = process.env) {
|
|
48960
|
+
let file;
|
|
48961
|
+
try {
|
|
48962
|
+
file = skillsCredentialFilePath(env3);
|
|
48963
|
+
} catch {
|
|
48964
|
+
return null;
|
|
48965
|
+
}
|
|
48966
|
+
if (!existsSync15(file))
|
|
48967
|
+
return null;
|
|
48968
|
+
try {
|
|
48969
|
+
for (const raw of readFileSync12(file, "utf-8").split(/\r?\n/)) {
|
|
48970
|
+
const match = new RegExp(`^\\s*(?:export\\s+)?${key}\\s*=\\s*(.*)$`).exec(raw);
|
|
48971
|
+
if (!match)
|
|
48972
|
+
continue;
|
|
48973
|
+
let value = (match[1] ?? "").trim();
|
|
48974
|
+
const quote = value[0];
|
|
48975
|
+
if ((quote === '"' || quote === "'") && value.length >= 2 && value.endsWith(quote)) {
|
|
48976
|
+
value = value.slice(1, -1);
|
|
48977
|
+
}
|
|
48978
|
+
return value || null;
|
|
48979
|
+
}
|
|
48980
|
+
} catch {
|
|
48981
|
+
return null;
|
|
48982
|
+
}
|
|
48983
|
+
return null;
|
|
48984
|
+
}
|
|
48985
|
+
function saveAuthConfig(config, env3 = process.env) {
|
|
48986
|
+
const apiKey = config.apiKey.trim();
|
|
48987
|
+
if (!apiKey)
|
|
48988
|
+
throw new Error("Refusing to store an empty Skills API key.");
|
|
48989
|
+
if (/[^\t\x20-\x7e]/.test(apiKey)) {
|
|
48990
|
+
throw new Error("Refusing to store a Skills API key containing control characters or non-ASCII bytes.");
|
|
48991
|
+
}
|
|
48992
|
+
const file = writeCredentialValues({ [SKILLS_API_KEY_ENV]: apiKey }, env3);
|
|
48993
|
+
const identity2 = {};
|
|
48994
|
+
for (const field of ["email", "orgId", "orgSlug", "userId"]) {
|
|
48995
|
+
const value = config[field];
|
|
48996
|
+
if (typeof value === "string" && value.length > 0)
|
|
48997
|
+
identity2[field] = value;
|
|
48998
|
+
}
|
|
48999
|
+
const identityFile = getIdentityFilePath(env3);
|
|
49000
|
+
if (Object.keys(identity2).length > 0) {
|
|
49001
|
+
writeFileSync7(identityFile, JSON.stringify(identity2, null, 2) + `
|
|
49002
|
+
`, { mode: 384 });
|
|
49003
|
+
} else {
|
|
49004
|
+
try {
|
|
49005
|
+
unlinkSync(identityFile);
|
|
49006
|
+
} catch {}
|
|
49007
|
+
}
|
|
49008
|
+
return file;
|
|
49009
|
+
}
|
|
49010
|
+
function saveApiUrl(apiUrl, env3 = process.env) {
|
|
49011
|
+
return writeCredentialValues({ [SKILLS_API_URL_ENV]: apiUrl }, env3);
|
|
49012
|
+
}
|
|
49013
|
+
function readStoredApiUrl(env3 = process.env) {
|
|
49014
|
+
return readCredentialValue(SKILLS_API_URL_ENV, env3);
|
|
49015
|
+
}
|
|
49016
|
+
function clearAuthConfig(env3 = process.env) {
|
|
49017
|
+
try {
|
|
49018
|
+
writeCredentialValues({ [SKILLS_API_KEY_ENV]: null }, env3);
|
|
49019
|
+
} catch {}
|
|
49020
|
+
try {
|
|
49021
|
+
unlinkSync(getIdentityFilePath(env3));
|
|
49022
|
+
} catch {}
|
|
49023
|
+
let stillResolves;
|
|
49024
|
+
try {
|
|
49025
|
+
stillResolves = resolveSkillsFleet(env3).mode === "hosted";
|
|
49026
|
+
} catch {
|
|
49027
|
+
stillResolves = true;
|
|
49028
|
+
}
|
|
49029
|
+
return { stillResolves };
|
|
49030
|
+
}
|
|
49031
|
+
function getApiUrl(action, env3 = process.env, options = {}) {
|
|
49032
|
+
return requireSkillsApiOrigin(action, env3, options);
|
|
49033
|
+
}
|
|
49034
|
+
function credentialFileMode(env3 = process.env) {
|
|
49035
|
+
try {
|
|
49036
|
+
return statSync9(skillsCredentialFilePath(env3)).mode & 511;
|
|
49037
|
+
} catch {
|
|
49038
|
+
return null;
|
|
49039
|
+
}
|
|
49040
|
+
}
|
|
49041
|
+
var init_auth_store = __esm(() => {
|
|
49042
|
+
init_fleet_credentials();
|
|
49043
|
+
init_fleet_credentials();
|
|
49044
|
+
});
|
|
49045
|
+
|
|
48037
49046
|
// src/lib/remote-run-contract.ts
|
|
48038
49047
|
function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
48039
49048
|
const record = isRecord3(payload) ? payload : {};
|
|
@@ -48351,26 +49360,23 @@ function normalizeUpdatedSincePage(payload) {
|
|
|
48351
49360
|
}
|
|
48352
49361
|
return { skills, nextCursor };
|
|
48353
49362
|
}
|
|
48354
|
-
function createRemoteSkillsClient() {
|
|
48355
|
-
const
|
|
48356
|
-
if (
|
|
49363
|
+
async function createRemoteSkillsClient(env3 = process.env) {
|
|
49364
|
+
const fleet = resolveSkillsFleet(env3);
|
|
49365
|
+
if (fleet.mode !== "hosted")
|
|
48357
49366
|
return null;
|
|
48358
|
-
|
|
49367
|
+
const apiKey = await resolveSkillsApiKey(env3);
|
|
49368
|
+
if (!apiKey) {
|
|
49369
|
+
throw new Error("A Skills authority resolved but no API key did. Sign in with: skills auth login");
|
|
49370
|
+
}
|
|
49371
|
+
return new RemoteSkillsClient(apiKey, fleet.apiOrigin);
|
|
48359
49372
|
}
|
|
48360
|
-
function createRemoteSkillsClientReadOnly() {
|
|
48361
|
-
|
|
48362
|
-
if (!apiKey)
|
|
48363
|
-
return null;
|
|
48364
|
-
const apiUrl = resolveApiUrl(loadConfigReadOnly(), process.env);
|
|
48365
|
-
if (!apiUrl)
|
|
48366
|
-
throw new MissingApiUrlError("the cloud group's sync verb (--dry-run)");
|
|
48367
|
-
return new RemoteSkillsClient(apiKey, apiUrl);
|
|
49373
|
+
function createRemoteSkillsClientReadOnly(env3 = process.env) {
|
|
49374
|
+
return createRemoteSkillsClient(env3);
|
|
48368
49375
|
}
|
|
48369
49376
|
var RemoteRouteUnsupportedError, RemoteRequestError;
|
|
48370
49377
|
var init_remote_client = __esm(() => {
|
|
48371
49378
|
init_auth_store();
|
|
48372
|
-
|
|
48373
|
-
init_config();
|
|
49379
|
+
init_fleet_credentials();
|
|
48374
49380
|
RemoteRouteUnsupportedError = class RemoteRouteUnsupportedError extends Error {
|
|
48375
49381
|
path;
|
|
48376
49382
|
status;
|
|
@@ -48420,8 +49426,8 @@ var init_revision = __esm(() => {
|
|
|
48420
49426
|
|
|
48421
49427
|
// src/lib/skill-bundle.ts
|
|
48422
49428
|
import { createHash as createHash3 } from "crypto";
|
|
48423
|
-
import { readFileSync as
|
|
48424
|
-
import { join as
|
|
49429
|
+
import { readFileSync as readFileSync13, readdirSync as readdirSync9, statSync as statSync10 } from "fs";
|
|
49430
|
+
import { join as join17, relative as relative2 } from "path";
|
|
48425
49431
|
function isDotenvFile(lower) {
|
|
48426
49432
|
if (lower === ".env" || lower.startsWith(".env."))
|
|
48427
49433
|
return true;
|
|
@@ -48459,7 +49465,7 @@ function collectSkillBundleEntries(dir) {
|
|
|
48459
49465
|
}
|
|
48460
49466
|
function walk(root, current, out) {
|
|
48461
49467
|
for (const entry of readdirSync9(current, { withFileTypes: true })) {
|
|
48462
|
-
const absolute =
|
|
49468
|
+
const absolute = join17(current, entry.name);
|
|
48463
49469
|
const rel = relative2(root, absolute).split("\\").join("/");
|
|
48464
49470
|
const isRootLevel = !rel.includes("/");
|
|
48465
49471
|
if (ANY_SEGMENT_EXCLUDES.has(entry.name.toLowerCase()))
|
|
@@ -48480,10 +49486,10 @@ function walk(root, current, out) {
|
|
|
48480
49486
|
continue;
|
|
48481
49487
|
if (isCredentialFile(entry.name))
|
|
48482
49488
|
continue;
|
|
48483
|
-
const stats =
|
|
49489
|
+
const stats = statSync10(absolute);
|
|
48484
49490
|
out.push({
|
|
48485
49491
|
path: rel,
|
|
48486
|
-
bytes: ownBytes(
|
|
49492
|
+
bytes: ownBytes(readFileSync13(absolute)),
|
|
48487
49493
|
mode: stats.mode & 64 ? 493 : 420
|
|
48488
49494
|
});
|
|
48489
49495
|
}
|
|
@@ -48778,12 +49784,12 @@ var init_skill_bundles = __esm(() => {
|
|
|
48778
49784
|
});
|
|
48779
49785
|
|
|
48780
49786
|
// src/lib/pull.ts
|
|
48781
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync3, readFileSync as
|
|
48782
|
-
import { dirname as dirname7, join as
|
|
49787
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync3, readFileSync as readFileSync14, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
49788
|
+
import { dirname as dirname7, join as join18 } from "path";
|
|
48783
49789
|
async function pullSkills(options = {}) {
|
|
48784
|
-
const client = options.client !== undefined ? options.client : createRemoteSkillsClient();
|
|
49790
|
+
const client = options.client !== undefined ? options.client : await createRemoteSkillsClient();
|
|
48785
49791
|
if (!client) {
|
|
48786
|
-
throw new PullSkillError("No API key configured, so there is no instance to pull from.", ["Run `skills login`, or set
|
|
49792
|
+
throw new PullSkillError("No API key configured, so there is no instance to pull from.", ["Run `skills auth login`, or set HASNA_SKILLS_API_KEY (and HASNA_SKILLS_API_URL for your own instance)."]);
|
|
48787
49793
|
}
|
|
48788
49794
|
const signingKey = options.signingKey ?? resolveSigningKey() ?? undefined;
|
|
48789
49795
|
const targets = await resolveTargetSlugs(client, options);
|
|
@@ -48835,8 +49841,8 @@ async function pullOne(client, rawName, corpusOptions, verify) {
|
|
|
48835
49841
|
return reconcileTombstone(slug, corpusOptions);
|
|
48836
49842
|
}
|
|
48837
49843
|
if (bundleResponse.status === 404) {
|
|
48838
|
-
const marker = readPullMarker(
|
|
48839
|
-
if (marker
|
|
49844
|
+
const marker = readPullMarker(join18(getPortableSkillsRoot(corpusOptions), slug));
|
|
49845
|
+
if (isPublishedInstallMarker(marker)) {
|
|
48840
49846
|
return { name: slug, success: true, purged: true, removed: false };
|
|
48841
49847
|
}
|
|
48842
49848
|
}
|
|
@@ -48874,8 +49880,8 @@ async function pullOne(client, rawName, corpusOptions, verify) {
|
|
|
48874
49880
|
return { name: slug, success: false, error: `Skill '${slug}' was not found on the configured Skills instance.` };
|
|
48875
49881
|
}
|
|
48876
49882
|
if (!meta?.revisionId) {
|
|
48877
|
-
const marker = readPullMarker(
|
|
48878
|
-
if (marker
|
|
49883
|
+
const marker = readPullMarker(join18(getPortableSkillsRoot(corpusOptions), slug));
|
|
49884
|
+
if (isPublishedInstallMarker(marker)) {
|
|
48879
49885
|
return { name: slug, success: true, purged: true, removed: false };
|
|
48880
49886
|
}
|
|
48881
49887
|
}
|
|
@@ -48931,8 +49937,8 @@ function provenRevision(meta, slug, bundle) {
|
|
|
48931
49937
|
return declared;
|
|
48932
49938
|
}
|
|
48933
49939
|
function reconcileTombstone(slug, corpusOptions) {
|
|
48934
|
-
const target =
|
|
48935
|
-
if (!existsSync16(
|
|
49940
|
+
const target = join18(getPortableSkillsRoot(corpusOptions), slug);
|
|
49941
|
+
if (!existsSync16(join18(target, PULL_MARKER_FILE))) {
|
|
48936
49942
|
return { name: slug, success: true, tombstoned: true, removed: false, leftInPlace: true };
|
|
48937
49943
|
}
|
|
48938
49944
|
rmSync5(target, { recursive: true, force: true });
|
|
@@ -48940,11 +49946,18 @@ function reconcileTombstone(slug, corpusOptions) {
|
|
|
48940
49946
|
}
|
|
48941
49947
|
function readPullMarker(dir) {
|
|
48942
49948
|
try {
|
|
48943
|
-
return JSON.parse(
|
|
49949
|
+
return JSON.parse(readFileSync14(join18(dir, PULL_MARKER_FILE), "utf-8"));
|
|
48944
49950
|
} catch {
|
|
48945
49951
|
return null;
|
|
48946
49952
|
}
|
|
48947
49953
|
}
|
|
49954
|
+
function isPublishedInstallMarker(marker) {
|
|
49955
|
+
if (!marker)
|
|
49956
|
+
return false;
|
|
49957
|
+
const revisionId = typeof marker.revisionId === "string" ? marker.revisionId : "";
|
|
49958
|
+
const contentHash = typeof marker.contentHash === "string" ? marker.contentHash : "";
|
|
49959
|
+
return revisionId.length > 0 || contentHash.length > 0;
|
|
49960
|
+
}
|
|
48948
49961
|
function installVerifiedBundle(slug, response, meta, corpusOptions, verify, exact) {
|
|
48949
49962
|
return response.arrayBuffer().then((buffer) => {
|
|
48950
49963
|
const verified = verifyBundleResponseBytes(buffer, response, verify);
|
|
@@ -49068,14 +50081,14 @@ function verifyBundleResponseBytes(buffer, response, verify = {}) {
|
|
|
49068
50081
|
function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
49069
50082
|
const root = getPortableSkillsRoot(options);
|
|
49070
50083
|
mkdirSync8(root, { recursive: true });
|
|
49071
|
-
const target =
|
|
50084
|
+
const target = join18(root, name);
|
|
49072
50085
|
const created = !existsSync16(target);
|
|
49073
|
-
const staging = mkdtempSync3(
|
|
50086
|
+
const staging = mkdtempSync3(join18(root, `.pull-${name}-`));
|
|
49074
50087
|
let moved = false;
|
|
49075
50088
|
let backup = null;
|
|
49076
50089
|
try {
|
|
49077
50090
|
for (const entry of entries) {
|
|
49078
|
-
const destination =
|
|
50091
|
+
const destination = join18(staging, entry.path);
|
|
49079
50092
|
mkdirSync8(dirname7(destination), { recursive: true });
|
|
49080
50093
|
writeFileSync8(destination, entry.bytes, { mode: entry.mode });
|
|
49081
50094
|
}
|
|
@@ -49088,18 +50101,18 @@ function installBundleAtomically(name, entries, options = {}, marker = {}) {
|
|
|
49088
50101
|
...marker.revisionId ? { revisionId: marker.revisionId } : {}
|
|
49089
50102
|
});
|
|
49090
50103
|
if (existsSync16(target)) {
|
|
49091
|
-
backup = mkdtempSync3(
|
|
49092
|
-
|
|
50104
|
+
backup = mkdtempSync3(join18(root, `.pull-backup-${name}-`));
|
|
50105
|
+
renameSync5(target, join18(backup, name));
|
|
49093
50106
|
moved = true;
|
|
49094
50107
|
}
|
|
49095
|
-
|
|
50108
|
+
renameSync5(staging, target);
|
|
49096
50109
|
if (moved && backup)
|
|
49097
50110
|
rmSync5(backup, { recursive: true, force: true });
|
|
49098
50111
|
} catch (error) {
|
|
49099
50112
|
rmSync5(staging, { recursive: true, force: true });
|
|
49100
|
-
if (moved && backup && existsSync16(
|
|
50113
|
+
if (moved && backup && existsSync16(join18(backup, name))) {
|
|
49101
50114
|
try {
|
|
49102
|
-
|
|
50115
|
+
renameSync5(join18(backup, name), target);
|
|
49103
50116
|
} catch {}
|
|
49104
50117
|
}
|
|
49105
50118
|
throw error;
|
|
@@ -49118,7 +50131,7 @@ function writePullMarker(dir, record) {
|
|
|
49118
50131
|
...record.revisionId ? { revisionId: record.revisionId } : {},
|
|
49119
50132
|
syncedAt: new Date().toISOString()
|
|
49120
50133
|
};
|
|
49121
|
-
writeFileSync8(
|
|
50134
|
+
writeFileSync8(join18(dir, PULL_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
49122
50135
|
`);
|
|
49123
50136
|
}
|
|
49124
50137
|
async function safeMeta(client, slug) {
|
|
@@ -49197,7 +50210,7 @@ __export(exports_install, {
|
|
|
49197
50210
|
});
|
|
49198
50211
|
function registerInstall(parent) {
|
|
49199
50212
|
parent.command("install").argument("[args...]", "Deprecated. Use 'skills pin <name>' for project skill pins.").option("--json", "Output result as JSON", false).description("Deprecated. Render skills into native agent folders with 'skills render'.").action((args, options) => handleDeprecatedInstall(args, options));
|
|
49200
|
-
parent.command("pin").argument("[skills...]", "Skills to pin").option("-o, --overwrite", "Refresh existing pins", false).option("--json", "Output results as JSON", false).option("--dry-run", "Print what would happen without actually pinning", false).option("--category <category>", "Pin all skills in a category (case-insensitive)").option("--remote", "Pin skills from the remote registry
|
|
50213
|
+
parent.command("pin").argument("[skills...]", "Skills to pin").option("-o, --overwrite", "Refresh existing pins", false).option("--json", "Output results as JSON", false).option("--dry-run", "Print what would happen without actually pinning", false).option("--category <category>", "Pin all skills in a category (case-insensitive)").option("--remote", "Pin skills from the remote registry (the resolved Skills credential; HASNA_SKILLS_API_URL for your own instance). Implied for name@version pins", false).description("Pin skills in .skills/project.json without copying source").action((skills, options) => {
|
|
49201
50214
|
handlePin(skills, options).catch(handlePinError);
|
|
49202
50215
|
});
|
|
49203
50216
|
parent.command("unpin").argument("<skill>", "Skill to unpin").option("--json", "Output as JSON", false).option("--dry-run", "Print what would happen without actually unpinning", false).description("Remove a skill from project pins").action((skill, options) => handleUnpin(skill, options));
|
|
@@ -49280,7 +50293,10 @@ Pinning ${skills.length} skills from "${matchedCategory}"...
|
|
|
49280
50293
|
const { name: pinName, version: pinnedVersion } = splitNameVersion(skills[i]);
|
|
49281
50294
|
if (total > 1 && !options.json)
|
|
49282
50295
|
process.stdout.write(`[${i + 1}/${total}] Pinning ${skills[i]}...`);
|
|
49283
|
-
const result2 = pinnedVersion ? await pinExactVersion(pinName, pinnedVersion, {
|
|
50296
|
+
const result2 = pinnedVersion ? await pinExactVersion(pinName, pinnedVersion, {
|
|
50297
|
+
useRemote: true,
|
|
50298
|
+
overwrite: options.overwrite
|
|
50299
|
+
}) : useRemote ? pinRemoteSkill(pinName, remoteByName, options.overwrite) : installSkill(pinName, { overwrite: options.overwrite });
|
|
49284
50300
|
results.push(result2);
|
|
49285
50301
|
if (total > 1 && !options.json)
|
|
49286
50302
|
console.log(result2.success ? " done" : ` ${source_default.red("failed")}`);
|
|
@@ -49389,7 +50405,12 @@ Pinned skills (${pins.length}):
|
|
|
49389
50405
|
}
|
|
49390
50406
|
async function pinExactVersion(name, version, options) {
|
|
49391
50407
|
if (!options.useRemote) {
|
|
49392
|
-
return {
|
|
50408
|
+
return {
|
|
50409
|
+
skill: name,
|
|
50410
|
+
success: false,
|
|
50411
|
+
error: `Pinning '${name}@${version}' needs a configured Skills instance: an exact version only exists on an instance, so the fetch is implied for name@version pins (skills auth login, or HASNA_SKILLS_API_URL for your own instance).`,
|
|
50412
|
+
mode: "pin"
|
|
50413
|
+
};
|
|
49393
50414
|
}
|
|
49394
50415
|
const pull2 = options.pull ?? (async (spec) => (await pullSkills({ names: [spec] })).results[0] ?? { success: false, error: "pull returned no result" });
|
|
49395
50416
|
const pulled = await pull2(`${name}@${version}`);
|
|
@@ -49507,16 +50528,16 @@ __export(exports_list, {
|
|
|
49507
50528
|
registerBrowse: () => registerBrowse
|
|
49508
50529
|
});
|
|
49509
50530
|
function registerBrowse(parent) {
|
|
49510
|
-
parent.command("list").alias("ls").option("-c, --category <category>", "Filter by category").option("-p, --pinned", "Show only pinned skills", false).option("-t, --tags <tags>", "Filter by comma-separated tags (OR logic, case-insensitive)").option("--tag <tags>", "Filter by comma-separated tags (alias for --tags)").option("--all", "Show the full skill registry instead of the default basic set", false).option("--remote", "Use remote registry
|
|
50531
|
+
parent.command("list").alias("ls").option("-c, --category <category>", "Filter by category").option("-p, --pinned", "Show only pinned skills", false).option("-t, --tags <tags>", "Filter by comma-separated tags (OR logic, case-insensitive)").option("--tag <tags>", "Filter by comma-separated tags (alias for --tags)").option("--all", "Show the full skill registry instead of the default basic set", false).option("--remote", "Use the remote registry (the resolved Skills credential; HASNA_SKILLS_API_URL for your own instance)", false).option("--json", "Output as JSON", false).option("--brief", "One line per skill: name \u2014 description [category]", false).option("--format <format>", "Output format: compact (names only) or csv (name,category,price,description)").option("--limit <n>", "Maximum rows to print for human output (default: 30, use 0 or all for every row)").option("--cursor <n>", "Numeric offset for human-output pagination", "0").option("--verbose", "Show longer descriptions and tags in human output", false).action((options) => {
|
|
49511
50532
|
return handleList(options).catch(handleBrowseError);
|
|
49512
50533
|
});
|
|
49513
|
-
parent.command("search").alias("s").argument("<query>", "Search term").option("--json", "Output as JSON", false).option("--brief", "One line per skill: name \u2014 description [category]", false).option("--format <format>", "Output format: compact (names only) or csv (name,category,price,description)").option("-c, --category <category>", "Filter results by category").option("-t, --tags <tags>", "Filter results by comma-separated tags (OR logic, case-insensitive)").option("--all", "Search the full skill registry instead of the default basic set", false).option("--remote", "Use remote registry
|
|
50534
|
+
parent.command("search").alias("s").argument("<query>", "Search term").option("--json", "Output as JSON", false).option("--brief", "One line per skill: name \u2014 description [category]", false).option("--format <format>", "Output format: compact (names only) or csv (name,category,price,description)").option("-c, --category <category>", "Filter results by category").option("-t, --tags <tags>", "Filter results by comma-separated tags (OR logic, case-insensitive)").option("--all", "Search the full skill registry instead of the default basic set", false).option("--remote", "Use the remote registry (the resolved Skills credential; HASNA_SKILLS_API_URL for your own instance)", false).option("--limit <n>", "Maximum rows to print for human output (default: 20, use 0 or all for every row)").option("--cursor <n>", "Numeric offset for human-output pagination", "0").option("--verbose", "Show longer descriptions and tags in human output", false).description("Search for skills").action((query, options) => {
|
|
49514
50535
|
return handleSearch(query, options).catch(handleBrowseError);
|
|
49515
50536
|
});
|
|
49516
|
-
parent.command("categories").option("--json", "Output as JSON", false).option("--remote", "Use remote registry
|
|
50537
|
+
parent.command("categories").option("--json", "Output as JSON", false).option("--remote", "Use the remote registry (the resolved Skills credential; HASNA_SKILLS_API_URL for your own instance)", false).description("List all categories").action((options) => {
|
|
49517
50538
|
return handleCategories(options).catch(handleBrowseError);
|
|
49518
50539
|
});
|
|
49519
|
-
parent.command("tags").option("--json", "Output as JSON", false).option("--remote", "Use remote registry
|
|
50540
|
+
parent.command("tags").option("--json", "Output as JSON", false).option("--remote", "Use the remote registry (the resolved Skills credential; HASNA_SKILLS_API_URL for your own instance)", false).option("--limit <n>", "Maximum rows to print for human output (default: 80, use 0 or all for every row)").option("--cursor <n>", "Numeric offset for human-output pagination", "0").description("List all unique tags with counts").action((options) => {
|
|
49520
50541
|
return handleTags(options).catch(handleBrowseError);
|
|
49521
50542
|
});
|
|
49522
50543
|
}
|
|
@@ -49883,16 +50904,16 @@ __export(exports_skillinfo, {
|
|
|
49883
50904
|
generateEnvExample: () => generateEnvExample,
|
|
49884
50905
|
detectProjectSkills: () => detectProjectSkills
|
|
49885
50906
|
});
|
|
49886
|
-
import { existsSync as existsSync17, readFileSync as
|
|
49887
|
-
import { join as
|
|
50907
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
|
|
50908
|
+
import { join as join19 } from "path";
|
|
49888
50909
|
function isInstructionSkillDir(skillPath, meta) {
|
|
49889
50910
|
if (meta?.kind === "instruction")
|
|
49890
50911
|
return true;
|
|
49891
|
-
const skillMdPath =
|
|
50912
|
+
const skillMdPath = join19(skillPath, "SKILL.md");
|
|
49892
50913
|
if (!existsSync17(skillMdPath))
|
|
49893
50914
|
return false;
|
|
49894
50915
|
try {
|
|
49895
|
-
return parseSkillFrontmatter(
|
|
50916
|
+
return parseSkillFrontmatter(readFileSync15(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
49896
50917
|
} catch {
|
|
49897
50918
|
return false;
|
|
49898
50919
|
}
|
|
@@ -49902,9 +50923,9 @@ function getSkillDocs(name) {
|
|
|
49902
50923
|
if (!existsSync17(skillPath))
|
|
49903
50924
|
return null;
|
|
49904
50925
|
return {
|
|
49905
|
-
skillMd: readIfExists(
|
|
49906
|
-
readme: readIfExists(
|
|
49907
|
-
claudeMd: readIfExists(
|
|
50926
|
+
skillMd: readIfExists(join19(skillPath, "SKILL.md")),
|
|
50927
|
+
readme: readIfExists(join19(skillPath, "README.md")),
|
|
50928
|
+
claudeMd: readIfExists(join19(skillPath, "CLAUDE.md"))
|
|
49908
50929
|
};
|
|
49909
50930
|
}
|
|
49910
50931
|
function getSkillBestDoc(name) {
|
|
@@ -49919,7 +50940,7 @@ function getSkillRequirements(name) {
|
|
|
49919
50940
|
return null;
|
|
49920
50941
|
const texts = [];
|
|
49921
50942
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
49922
|
-
const content = readIfExists(
|
|
50943
|
+
const content = readIfExists(join19(skillPath, file));
|
|
49923
50944
|
if (content)
|
|
49924
50945
|
texts.push(content);
|
|
49925
50946
|
}
|
|
@@ -49935,7 +50956,8 @@ function getSkillRequirements(name) {
|
|
|
49935
50956
|
}
|
|
49936
50957
|
}
|
|
49937
50958
|
envVars.delete("SKILL_API_KEY");
|
|
49938
|
-
envVars.
|
|
50959
|
+
envVars.delete("SKILLS_API_KEY");
|
|
50960
|
+
envVars.add("HASNA_SKILLS_API_KEY");
|
|
49939
50961
|
}
|
|
49940
50962
|
const systemDeps = new Set;
|
|
49941
50963
|
const depPatterns = [
|
|
@@ -49958,10 +50980,10 @@ function getSkillRequirements(name) {
|
|
|
49958
50980
|
const skillName = normalizeSkillName(name);
|
|
49959
50981
|
let cliCommand = `skills run ${skillName}`;
|
|
49960
50982
|
let dependencies = {};
|
|
49961
|
-
const pkgPath =
|
|
50983
|
+
const pkgPath = join19(skillPath, "package.json");
|
|
49962
50984
|
if (existsSync17(pkgPath)) {
|
|
49963
50985
|
try {
|
|
49964
|
-
const pkg = JSON.parse(
|
|
50986
|
+
const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
|
|
49965
50987
|
dependencies = pkg.dependencies || {};
|
|
49966
50988
|
} catch {}
|
|
49967
50989
|
}
|
|
@@ -49978,9 +51000,9 @@ function isHostedPremiumSkill(skillName, meta) {
|
|
|
49978
51000
|
function isPackageResolvable(pkgName, fromDir) {
|
|
49979
51001
|
let dir = fromDir;
|
|
49980
51002
|
while (true) {
|
|
49981
|
-
if (existsSync17(
|
|
51003
|
+
if (existsSync17(join19(dir, "node_modules", pkgName, "package.json")))
|
|
49982
51004
|
return true;
|
|
49983
|
-
const parent =
|
|
51005
|
+
const parent = join19(dir, "..");
|
|
49984
51006
|
if (parent === dir)
|
|
49985
51007
|
return false;
|
|
49986
51008
|
dir = parent;
|
|
@@ -50009,13 +51031,13 @@ async function runSkill(name, args, options = {}) {
|
|
|
50009
51031
|
error: `Skill '${name}' is an instruction skill (kind: instruction) and is not runnable. Instruction skills are consumed by coding agents via SKILL.md, not executed with 'skills run'.`
|
|
50010
51032
|
};
|
|
50011
51033
|
}
|
|
50012
|
-
const pkgPath =
|
|
51034
|
+
const pkgPath = join19(skillPath, "package.json");
|
|
50013
51035
|
if (!existsSync17(pkgPath)) {
|
|
50014
51036
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
50015
51037
|
}
|
|
50016
51038
|
let entryPoint;
|
|
50017
51039
|
try {
|
|
50018
|
-
const pkg = JSON.parse(
|
|
51040
|
+
const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
|
|
50019
51041
|
if (pkg.bin) {
|
|
50020
51042
|
const binValues = Object.values(pkg.bin);
|
|
50021
51043
|
entryPoint = binValues[0];
|
|
@@ -50029,11 +51051,11 @@ async function runSkill(name, args, options = {}) {
|
|
|
50029
51051
|
} catch {
|
|
50030
51052
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
50031
51053
|
}
|
|
50032
|
-
const entryPath =
|
|
51054
|
+
const entryPath = join19(skillPath, entryPoint);
|
|
50033
51055
|
if (!existsSync17(entryPath)) {
|
|
50034
51056
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
50035
51057
|
}
|
|
50036
|
-
const nodeModules =
|
|
51058
|
+
const nodeModules = join19(skillPath, "node_modules");
|
|
50037
51059
|
if (!existsSync17(nodeModules)) {
|
|
50038
51060
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
50039
51061
|
cwd: skillPath,
|
|
@@ -50061,7 +51083,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
50061
51083
|
return { exitCode };
|
|
50062
51084
|
}
|
|
50063
51085
|
function detectProjectSkills(cwd2 = process.cwd()) {
|
|
50064
|
-
const pkgPath =
|
|
51086
|
+
const pkgPath = join19(cwd2, "package.json");
|
|
50065
51087
|
if (!existsSync17(pkgPath)) {
|
|
50066
51088
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
50067
51089
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
@@ -50069,7 +51091,7 @@ function detectProjectSkills(cwd2 = process.cwd()) {
|
|
|
50069
51091
|
}
|
|
50070
51092
|
let pkg;
|
|
50071
51093
|
try {
|
|
50072
|
-
pkg = JSON.parse(
|
|
51094
|
+
pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
|
|
50073
51095
|
} catch {
|
|
50074
51096
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
50075
51097
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
@@ -50198,13 +51220,13 @@ function generateSkillMd(name) {
|
|
|
50198
51220
|
"---"
|
|
50199
51221
|
].join(`
|
|
50200
51222
|
`);
|
|
50201
|
-
const readme = readIfExists(
|
|
50202
|
-
const claudeMd = readIfExists(
|
|
51223
|
+
const readme = readIfExists(join19(skillPath, "README.md"));
|
|
51224
|
+
const claudeMd = readIfExists(join19(skillPath, "CLAUDE.md"));
|
|
50203
51225
|
let cliCommand = null;
|
|
50204
|
-
const pkgPath =
|
|
51226
|
+
const pkgPath = join19(skillPath, "package.json");
|
|
50205
51227
|
if (existsSync17(pkgPath)) {
|
|
50206
51228
|
try {
|
|
50207
|
-
const pkg = JSON.parse(
|
|
51229
|
+
const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
|
|
50208
51230
|
if (pkg.bin) {
|
|
50209
51231
|
const binKeys = Object.keys(pkg.bin);
|
|
50210
51232
|
if (binKeys.length > 0)
|
|
@@ -50278,7 +51300,7 @@ function extractEnvVars(text) {
|
|
|
50278
51300
|
function readIfExists(path) {
|
|
50279
51301
|
try {
|
|
50280
51302
|
if (existsSync17(path)) {
|
|
50281
|
-
return
|
|
51303
|
+
return readFileSync15(path, "utf-8");
|
|
50282
51304
|
}
|
|
50283
51305
|
} catch {}
|
|
50284
51306
|
return null;
|
|
@@ -50313,11 +51335,11 @@ var exports_introspect = {};
|
|
|
50313
51335
|
__export(exports_introspect, {
|
|
50314
51336
|
registerIntrospect: () => registerIntrospect
|
|
50315
51337
|
});
|
|
50316
|
-
import { existsSync as existsSync18, readFileSync as
|
|
50317
|
-
import { join as
|
|
51338
|
+
import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
|
|
51339
|
+
import { join as join20 } from "path";
|
|
50318
51340
|
import { execSync } from "child_process";
|
|
50319
51341
|
function registerIntrospect(parent) {
|
|
50320
|
-
parent.command("info").argument("<skill>", "Skill name").option("--json", "Output as JSON", false).option("--brief", "Single line: name \u2014 description [category] (tags: ...)", false).option("--remote", "Use remote registry
|
|
51342
|
+
parent.command("info").argument("<skill>", "Skill name").option("--json", "Output as JSON", false).option("--brief", "Single line: name \u2014 description [category] (tags: ...)", false).option("--remote", "Use the remote registry (the resolved Skills credential; HASNA_SKILLS_API_URL for your own instance)", false).description("Show details about a specific skill").action((name, options) => {
|
|
50321
51343
|
return handleInfo(name, options).catch(async (error) => {
|
|
50322
51344
|
const notFound = await resolveRemoteNotFound(name, options.remote, error.message);
|
|
50323
51345
|
if (options.json)
|
|
@@ -50327,7 +51349,7 @@ function registerIntrospect(parent) {
|
|
|
50327
51349
|
process.exitCode = 1;
|
|
50328
51350
|
});
|
|
50329
51351
|
});
|
|
50330
|
-
parent.command("show").argument("<skill>", "Skill name").option("--json", "Output as JSON", false).option("--brief", "Single line: name \u2014 description [category] (tags: ...)", false).option("--remote", "Use remote registry
|
|
51352
|
+
parent.command("show").argument("<skill>", "Skill name").option("--json", "Output as JSON", false).option("--brief", "Single line: name \u2014 description [category] (tags: ...)", false).option("--remote", "Use the remote registry (the resolved Skills credential; HASNA_SKILLS_API_URL for your own instance)", false).description("Show details about a specific skill").action((name, options) => {
|
|
50331
51353
|
return handleInfo(name, options).catch(async (error) => {
|
|
50332
51354
|
const notFound = await resolveRemoteNotFound(name, options.remote, error.message);
|
|
50333
51355
|
if (options.json)
|
|
@@ -50535,36 +51557,36 @@ function handleDiff(name, options) {
|
|
|
50535
51557
|
const bare = name;
|
|
50536
51558
|
const normalized = normalizePortableSkillName(bare);
|
|
50537
51559
|
const sourcePath = getSkillPath(bare);
|
|
50538
|
-
const canonicalDir =
|
|
50539
|
-
const canonicalSkillMd =
|
|
51560
|
+
const canonicalDir = join20(resolveCorpusRoot(), normalized);
|
|
51561
|
+
const canonicalSkillMd = join20(canonicalDir, "SKILL.md");
|
|
50540
51562
|
const canonical = {
|
|
50541
51563
|
present: existsSync18(canonicalSkillMd),
|
|
50542
51564
|
path: canonicalDir,
|
|
50543
51565
|
...existsSync18(canonicalSkillMd) ? { hash: hashSkillMarkdownFile(canonicalSkillMd) } : {},
|
|
50544
|
-
...existsSync18(canonicalSkillMd) ? { stub: isPointerSkillMd(
|
|
51566
|
+
...existsSync18(canonicalSkillMd) ? { stub: isPointerSkillMd(readFileSync16(canonicalSkillMd, "utf-8")) } : {}
|
|
50545
51567
|
};
|
|
50546
51568
|
const pinned = getInstalledSkills().includes(bare);
|
|
50547
51569
|
const installMeta = getInstallMeta();
|
|
50548
51570
|
const installedVersion = installMeta.skills[bare]?.version ?? "unknown";
|
|
50549
|
-
const registryPkgPath =
|
|
51571
|
+
const registryPkgPath = join20(sourcePath, "package.json");
|
|
50550
51572
|
let registryVersion = "unknown";
|
|
50551
51573
|
if (existsSync18(registryPkgPath)) {
|
|
50552
51574
|
try {
|
|
50553
|
-
registryVersion = JSON.parse(
|
|
51575
|
+
registryVersion = JSON.parse(readFileSync16(registryPkgPath, "utf-8")).version || "unknown";
|
|
50554
51576
|
} catch {}
|
|
50555
51577
|
}
|
|
50556
51578
|
const upToDate = installedVersion === registryVersion;
|
|
50557
51579
|
const homes = [];
|
|
50558
51580
|
for (const agent of SYNC_AGENTS) {
|
|
50559
|
-
const dir =
|
|
51581
|
+
const dir = join20(agentGlobalSkillsDir(agent), normalized);
|
|
50560
51582
|
const present = existsSync18(dir);
|
|
50561
|
-
const managed = existsSync18(
|
|
50562
|
-
const skillMdPath =
|
|
51583
|
+
const managed = existsSync18(join20(dir, SYNC_MARKER_FILE));
|
|
51584
|
+
const skillMdPath = join20(dir, "SKILL.md");
|
|
50563
51585
|
const hash = present && existsSync18(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : undefined;
|
|
50564
51586
|
let stub;
|
|
50565
51587
|
if (present && existsSync18(skillMdPath)) {
|
|
50566
51588
|
try {
|
|
50567
|
-
stub = isPointerSkillMd(
|
|
51589
|
+
stub = isPointerSkillMd(readFileSync16(skillMdPath, "utf-8"));
|
|
50568
51590
|
} catch {
|
|
50569
51591
|
stub = undefined;
|
|
50570
51592
|
}
|
|
@@ -50816,7 +51838,7 @@ var init_tool_primitives = __esm(() => {
|
|
|
50816
51838
|
cliCommands: ["skills tools deps <skill>", "skills run <skill>"],
|
|
50817
51839
|
mcpTools: ["get_skill_tool_dependencies", "run_skill"],
|
|
50818
51840
|
apiSurfaces: ["runSkill", "SkillRunRecord", "RemoteSkillRunContract"],
|
|
50819
|
-
envVars: ["
|
|
51841
|
+
envVars: ["HASNA_SKILLS_API_KEY"],
|
|
50820
51842
|
outputTypes: ["text", "json", "markdown", "artifact"],
|
|
50821
51843
|
capabilities: ["completion", "reasoning", "tool-calling", "vision-input", "structured-output"]
|
|
50822
51844
|
},
|
|
@@ -50872,7 +51894,7 @@ var init_tool_primitives = __esm(() => {
|
|
|
50872
51894
|
cliCommands: ["skills tools deps <skill>", "skills run <skill>"],
|
|
50873
51895
|
mcpTools: ["get_skill_tool_dependencies", "run_skill"],
|
|
50874
51896
|
apiSurfaces: ["runSkill", "RemoteSkillRunContract"],
|
|
50875
|
-
envVars: ["
|
|
51897
|
+
envVars: ["HASNA_SKILLS_API_KEY"],
|
|
50876
51898
|
outputTypes: ["png", "jpeg", "webp", "svg", "zip"],
|
|
50877
51899
|
capabilities: ["image-generation", "image-analysis", "image-editing", "asset-packaging"]
|
|
50878
51900
|
},
|
|
@@ -50886,7 +51908,7 @@ var init_tool_primitives = __esm(() => {
|
|
|
50886
51908
|
cliCommands: ["skills tools deps <skill>", "skills run <skill>"],
|
|
50887
51909
|
mcpTools: ["get_skill_tool_dependencies", "run_skill"],
|
|
50888
51910
|
apiSurfaces: ["runSkill", "RemoteSkillRunContract"],
|
|
50889
|
-
envVars: ["
|
|
51911
|
+
envVars: ["HASNA_SKILLS_API_KEY"],
|
|
50890
51912
|
outputTypes: ["mp3", "wav", "txt", "srt", "json", "zip"],
|
|
50891
51913
|
capabilities: ["transcription", "audio-generation", "voiceover", "audio-cleanup"]
|
|
50892
51914
|
},
|
|
@@ -50900,7 +51922,7 @@ var init_tool_primitives = __esm(() => {
|
|
|
50900
51922
|
cliCommands: ["skills tools deps <skill>", "skills run <skill>"],
|
|
50901
51923
|
mcpTools: ["get_skill_tool_dependencies", "run_skill"],
|
|
50902
51924
|
apiSurfaces: ["runSkill", "RemoteSkillRunContract"],
|
|
50903
|
-
envVars: ["
|
|
51925
|
+
envVars: ["HASNA_SKILLS_API_KEY"],
|
|
50904
51926
|
outputTypes: ["mp4", "mov", "srt", "png", "json", "zip"],
|
|
50905
51927
|
capabilities: ["video-generation", "video-analysis", "captioning", "highlight-extraction"]
|
|
50906
51928
|
},
|
|
@@ -50928,7 +51950,7 @@ var init_tool_primitives = __esm(() => {
|
|
|
50928
51950
|
cliCommands: ["skills tools deps <skill>", "skills run <skill>"],
|
|
50929
51951
|
mcpTools: ["get_skill_tool_dependencies", "run_skill"],
|
|
50930
51952
|
apiSurfaces: ["SkillRunContext", "RemoteSkillRunContract"],
|
|
50931
|
-
envVars: ["
|
|
51953
|
+
envVars: ["HASNA_SKILLS_API_KEY"],
|
|
50932
51954
|
outputTypes: ["json", "artifact"],
|
|
50933
51955
|
capabilities: ["approval", "external-api", "account-scoped-execution"]
|
|
50934
51956
|
},
|
|
@@ -50970,7 +51992,7 @@ var init_tool_primitives = __esm(() => {
|
|
|
50970
51992
|
cliCommands: ["skills auth login", "skills run <skill>"],
|
|
50971
51993
|
mcpTools: ["run_skill"],
|
|
50972
51994
|
apiSurfaces: ["RemoteSkillsClient", "RemoteSkillRunContract"],
|
|
50973
|
-
envVars: ["
|
|
51995
|
+
envVars: ["HASNA_SKILLS_API_KEY"],
|
|
50974
51996
|
outputTypes: ["json"],
|
|
50975
51997
|
capabilities: ["account-auth", "remote-run-submit"]
|
|
50976
51998
|
}
|
|
@@ -51097,8 +52119,8 @@ var exports_init = {};
|
|
|
51097
52119
|
__export(exports_init, {
|
|
51098
52120
|
registerSetup: () => registerSetup
|
|
51099
52121
|
});
|
|
51100
|
-
import { existsSync as existsSync19, readFileSync as
|
|
51101
|
-
import { join as
|
|
52122
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17, writeFileSync as writeFileSync9, appendFileSync } from "fs";
|
|
52123
|
+
import { join as join21 } from "path";
|
|
51102
52124
|
function registerSetup(parent) {
|
|
51103
52125
|
parent.command("init").option("--json", "Output as JSON", false).option("--for <agent>", "Detect project type and show MCP registration guidance for agent").option("--scope <scope>", "Deprecated; agent skill-folder installs are disabled", "global").description("Initialize project for pinned skills (.env.example, .gitignore)").action((options) => handleInit(options));
|
|
51104
52126
|
parent.command("export").option("--json", "Output as JSON (default behavior)", false).description("Export pinned skills to JSON for sharing or backup").action((_options) => handleExport());
|
|
@@ -51182,7 +52204,7 @@ Use: skills render`));
|
|
|
51182
52204
|
lines.push(`# Used by: ${skills.join(", ")}`);
|
|
51183
52205
|
lines.push(`${envVar}=`);
|
|
51184
52206
|
}
|
|
51185
|
-
writeFileSync9(
|
|
52207
|
+
writeFileSync9(join21(cwd2, ".env.example"), lines.join(`
|
|
51186
52208
|
`) + `
|
|
51187
52209
|
`);
|
|
51188
52210
|
envVarCount = envMap.size;
|
|
@@ -51190,9 +52212,9 @@ Use: skills render`));
|
|
|
51190
52212
|
console.log(source_default.green(`\u2713 Generated .env.example (${envVarCount} variables from ${installed.length} skills)`));
|
|
51191
52213
|
} else if (!options.json)
|
|
51192
52214
|
console.log(source_default.dim(" No environment variables detected across pinned skills"));
|
|
51193
|
-
const gitignorePath =
|
|
52215
|
+
const gitignorePath = join21(cwd2, ".gitignore");
|
|
51194
52216
|
const gitignoreEntries = [".skills/runs/", ".skills/exports/", ".skills/tmp/"];
|
|
51195
|
-
let gitignoreContent = existsSync19(gitignorePath) ?
|
|
52217
|
+
let gitignoreContent = existsSync19(gitignorePath) ? readFileSync17(gitignorePath, "utf-8") : "";
|
|
51196
52218
|
let gitignoreUpdated = false;
|
|
51197
52219
|
const missingEntries = gitignoreEntries.filter((entry) => !gitignoreContent.includes(entry));
|
|
51198
52220
|
if (missingEntries.length > 0) {
|
|
@@ -51247,7 +52269,7 @@ async function handleImport(file, options) {
|
|
|
51247
52269
|
process.exitCode = 1;
|
|
51248
52270
|
return;
|
|
51249
52271
|
}
|
|
51250
|
-
raw =
|
|
52272
|
+
raw = readFileSync17(file, "utf-8");
|
|
51251
52273
|
}
|
|
51252
52274
|
} catch (err) {
|
|
51253
52275
|
const error = `Failed to read file: ${err.message}`;
|
|
@@ -51344,9 +52366,9 @@ var init_init = __esm(() => {
|
|
|
51344
52366
|
});
|
|
51345
52367
|
|
|
51346
52368
|
// src/lib/home-adoption.ts
|
|
51347
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync10, readFileSync as
|
|
51348
|
-
import { homedir as
|
|
51349
|
-
import { join as
|
|
52369
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync10, readFileSync as readFileSync18, rmSync as rmSync6, statSync as statSync11, writeFileSync as writeFileSync10 } from "fs";
|
|
52370
|
+
import { homedir as homedir5 } from "os";
|
|
52371
|
+
import { join as join22 } from "path";
|
|
51350
52372
|
function indexCanonicalCorpus(corpusRoot) {
|
|
51351
52373
|
const byName = new Map;
|
|
51352
52374
|
if (!existsSync20(corpusRoot))
|
|
@@ -51360,7 +52382,7 @@ function indexCanonicalCorpus(corpusRoot) {
|
|
|
51360
52382
|
for (const entry of entries.sort()) {
|
|
51361
52383
|
if (entry.startsWith("."))
|
|
51362
52384
|
continue;
|
|
51363
|
-
const skillMd =
|
|
52385
|
+
const skillMd = join22(corpusRoot, entry, "SKILL.md");
|
|
51364
52386
|
if (!existsSync20(skillMd))
|
|
51365
52387
|
continue;
|
|
51366
52388
|
try {
|
|
@@ -51370,13 +52392,13 @@ function indexCanonicalCorpus(corpusRoot) {
|
|
|
51370
52392
|
return byName;
|
|
51371
52393
|
}
|
|
51372
52394
|
function scanUnmarkedHomes(options = {}) {
|
|
51373
|
-
const
|
|
52395
|
+
const homeDir2 = options.homeDir ?? homedir5();
|
|
51374
52396
|
const corpusRoot = resolveCorpusRoot(options);
|
|
51375
52397
|
const index = indexCanonicalCorpus(corpusRoot);
|
|
51376
52398
|
const agents = options.agents?.length ? options.agents : [...SYNC_AGENTS];
|
|
51377
52399
|
const scan = { adoptable: [], conflicts: [], unknown: [], managed: 0 };
|
|
51378
52400
|
for (const agent of agents) {
|
|
51379
|
-
const home = agentGlobalSkillsDir(agent,
|
|
52401
|
+
const home = agentGlobalSkillsDir(agent, homeDir2);
|
|
51380
52402
|
if (!existsSync20(home))
|
|
51381
52403
|
continue;
|
|
51382
52404
|
let entries = [];
|
|
@@ -51388,22 +52410,22 @@ function scanUnmarkedHomes(options = {}) {
|
|
|
51388
52410
|
for (const skill of entries.sort()) {
|
|
51389
52411
|
if (skill.startsWith("."))
|
|
51390
52412
|
continue;
|
|
51391
|
-
const dir =
|
|
52413
|
+
const dir = join22(home, skill);
|
|
51392
52414
|
try {
|
|
51393
|
-
if (!
|
|
52415
|
+
if (!statSync11(dir).isDirectory())
|
|
51394
52416
|
continue;
|
|
51395
52417
|
} catch {
|
|
51396
52418
|
continue;
|
|
51397
52419
|
}
|
|
51398
|
-
if (existsSync20(
|
|
52420
|
+
if (existsSync20(join22(dir, SYNC_MARKER_FILE))) {
|
|
51399
52421
|
scan.managed += 1;
|
|
51400
52422
|
continue;
|
|
51401
52423
|
}
|
|
51402
|
-
const skillMdPath =
|
|
52424
|
+
const skillMdPath = join22(dir, "SKILL.md");
|
|
51403
52425
|
if (!existsSync20(skillMdPath))
|
|
51404
52426
|
continue;
|
|
51405
52427
|
const hash = hashSkillMarkdownFile(skillMdPath);
|
|
51406
|
-
const mtime =
|
|
52428
|
+
const mtime = statSync11(skillMdPath).mtime.toISOString();
|
|
51407
52429
|
const entry = { agent, skill, home, path: dir, hash, mtime };
|
|
51408
52430
|
const canonicalHash = index.get(skill);
|
|
51409
52431
|
if (canonicalHash === undefined) {
|
|
@@ -51420,11 +52442,11 @@ function scanUnmarkedHomes(options = {}) {
|
|
|
51420
52442
|
function appendConflictsLedger(appDir, conflicts) {
|
|
51421
52443
|
if (conflicts.length === 0)
|
|
51422
52444
|
return;
|
|
51423
|
-
const ledgerPath =
|
|
52445
|
+
const ledgerPath = join22(appDir, CONFLICTS_LEDGER_FILE);
|
|
51424
52446
|
let ledger = { version: 1, entries: [] };
|
|
51425
52447
|
if (existsSync20(ledgerPath)) {
|
|
51426
52448
|
try {
|
|
51427
|
-
const parsed = JSON.parse(
|
|
52449
|
+
const parsed = JSON.parse(readFileSync18(ledgerPath, "utf-8"));
|
|
51428
52450
|
if (parsed && typeof parsed === "object" && Array.isArray(parsed.entries)) {
|
|
51429
52451
|
ledger = parsed;
|
|
51430
52452
|
}
|
|
@@ -51440,9 +52462,9 @@ function appendConflictsLedger(appDir, conflicts) {
|
|
|
51440
52462
|
`);
|
|
51441
52463
|
}
|
|
51442
52464
|
function writeRollbackRecord(mode, entries, appDir = getDataDir()) {
|
|
51443
|
-
const dir =
|
|
52465
|
+
const dir = join22(appDir, ROLLBACK_DIRNAME);
|
|
51444
52466
|
mkdirSync9(dir, { recursive: true });
|
|
51445
|
-
const file =
|
|
52467
|
+
const file = join22(dir, `${mode}-${Date.now()}.json`);
|
|
51446
52468
|
const record = { version: 1, mode, timestamp: new Date().toISOString(), entries };
|
|
51447
52469
|
writeFileSync10(file, `${JSON.stringify(record, null, 2)}
|
|
51448
52470
|
`);
|
|
@@ -51453,7 +52475,7 @@ function adoptUnmarkedHomes(options = {}) {
|
|
|
51453
52475
|
if (!options.apply) {
|
|
51454
52476
|
return { ...scan, applied: false };
|
|
51455
52477
|
}
|
|
51456
|
-
const appDir = options.homeDir ?
|
|
52478
|
+
const appDir = options.homeDir ? join22(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
51457
52479
|
const markers = scan.adoptable.map((entry) => {
|
|
51458
52480
|
const marker = {
|
|
51459
52481
|
managedBy: SYNC_MARKER_MANAGED_BY,
|
|
@@ -51461,7 +52483,7 @@ function adoptUnmarkedHomes(options = {}) {
|
|
|
51461
52483
|
source: "adopted",
|
|
51462
52484
|
syncedAt: new Date().toISOString()
|
|
51463
52485
|
};
|
|
51464
|
-
writeFileSync10(
|
|
52486
|
+
writeFileSync10(join22(entry.path, SYNC_MARKER_FILE), `${JSON.stringify(marker, null, 2)}
|
|
51465
52487
|
`);
|
|
51466
52488
|
return { agent: entry.agent, skill: entry.skill, path: entry.path, hash: entry.hash, marker };
|
|
51467
52489
|
});
|
|
@@ -51473,13 +52495,13 @@ function adoptUnmarkedHomes(options = {}) {
|
|
|
51473
52495
|
return { ...scan, applied: true, rollbackFile };
|
|
51474
52496
|
}
|
|
51475
52497
|
function pruneStrayHomes(options = {}) {
|
|
51476
|
-
const
|
|
52498
|
+
const homeDir2 = options.homeDir ?? homedir5();
|
|
51477
52499
|
const corpusRoot = resolveCorpusRoot(options);
|
|
51478
52500
|
const index = indexCanonicalCorpus(corpusRoot);
|
|
51479
52501
|
const agents = options.agents?.length ? options.agents : [...SYNC_AGENTS];
|
|
51480
52502
|
const candidates = [];
|
|
51481
52503
|
for (const agent of agents) {
|
|
51482
|
-
const home = agentGlobalSkillsDir(agent,
|
|
52504
|
+
const home = agentGlobalSkillsDir(agent, homeDir2);
|
|
51483
52505
|
if (!existsSync20(home))
|
|
51484
52506
|
continue;
|
|
51485
52507
|
let entries = [];
|
|
@@ -51491,28 +52513,28 @@ function pruneStrayHomes(options = {}) {
|
|
|
51491
52513
|
for (const skill of entries.sort()) {
|
|
51492
52514
|
if (skill.startsWith("."))
|
|
51493
52515
|
continue;
|
|
51494
|
-
const dir =
|
|
52516
|
+
const dir = join22(home, skill);
|
|
51495
52517
|
try {
|
|
51496
|
-
if (!
|
|
52518
|
+
if (!statSync11(dir).isDirectory())
|
|
51497
52519
|
continue;
|
|
51498
52520
|
} catch {
|
|
51499
52521
|
continue;
|
|
51500
52522
|
}
|
|
51501
|
-
const markerPath =
|
|
52523
|
+
const markerPath = join22(dir, SYNC_MARKER_FILE);
|
|
51502
52524
|
if (!existsSync20(markerPath))
|
|
51503
52525
|
continue;
|
|
51504
52526
|
if (index.has(skill))
|
|
51505
52527
|
continue;
|
|
51506
52528
|
let marker;
|
|
51507
52529
|
try {
|
|
51508
|
-
const parsed = JSON.parse(
|
|
52530
|
+
const parsed = JSON.parse(readFileSync18(markerPath, "utf-8"));
|
|
51509
52531
|
if (!parsed || typeof parsed !== "object" || typeof parsed.managedBy !== "string")
|
|
51510
52532
|
continue;
|
|
51511
52533
|
marker = parsed;
|
|
51512
52534
|
} catch {
|
|
51513
52535
|
continue;
|
|
51514
52536
|
}
|
|
51515
|
-
const skillMdPath =
|
|
52537
|
+
const skillMdPath = join22(dir, "SKILL.md");
|
|
51516
52538
|
const hash = existsSync20(skillMdPath) ? hashSkillMarkdownFile(skillMdPath) : "";
|
|
51517
52539
|
candidates.push({ agent, skill, home, path: dir, hash, marker });
|
|
51518
52540
|
}
|
|
@@ -51520,7 +52542,7 @@ function pruneStrayHomes(options = {}) {
|
|
|
51520
52542
|
if (!options.apply) {
|
|
51521
52543
|
return { candidates, pruned: 0, dryRun: true };
|
|
51522
52544
|
}
|
|
51523
|
-
const appDir = options.homeDir ?
|
|
52545
|
+
const appDir = options.homeDir ? join22(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
51524
52546
|
const rollbackFile = writeRollbackRecord("prune", candidates.map(({ agent, skill, path, hash, marker }) => ({ agent, skill, path, hash, marker })), appDir);
|
|
51525
52547
|
for (const candidate of candidates) {
|
|
51526
52548
|
rmSync6(candidate.path, { recursive: true, force: true });
|
|
@@ -51536,9 +52558,9 @@ var init_home_adoption = __esm(() => {
|
|
|
51536
52558
|
});
|
|
51537
52559
|
|
|
51538
52560
|
// src/lib/home-census.ts
|
|
51539
|
-
import { existsSync as existsSync21, readFileSync as
|
|
51540
|
-
import { homedir as
|
|
51541
|
-
import { join as
|
|
52561
|
+
import { existsSync as existsSync21, readFileSync as readFileSync19, readdirSync as readdirSync11, statSync as statSync12 } from "fs";
|
|
52562
|
+
import { homedir as homedir6 } from "os";
|
|
52563
|
+
import { join as join23 } from "path";
|
|
51542
52564
|
function sortEntries(entries) {
|
|
51543
52565
|
return entries.sort((a, b) => {
|
|
51544
52566
|
const left = `${a.agent}/${a.skill}/${a.kind}`;
|
|
@@ -51547,7 +52569,7 @@ function sortEntries(entries) {
|
|
|
51547
52569
|
});
|
|
51548
52570
|
}
|
|
51549
52571
|
function censusHomeDrift(options = {}) {
|
|
51550
|
-
const
|
|
52572
|
+
const homeDir2 = options.homeDir ?? homedir6();
|
|
51551
52573
|
const corpusRoot = resolveCorpusRoot(options);
|
|
51552
52574
|
const index = indexCanonicalCorpus(corpusRoot);
|
|
51553
52575
|
const agents = options.agents?.length ? options.agents : [...SYNC_AGENTS];
|
|
@@ -51556,7 +52578,7 @@ function censusHomeDrift(options = {}) {
|
|
|
51556
52578
|
let managed = 0;
|
|
51557
52579
|
let homesChecked = 0;
|
|
51558
52580
|
for (const agent of agents) {
|
|
51559
|
-
const home = agentGlobalSkillsDir(agent,
|
|
52581
|
+
const home = agentGlobalSkillsDir(agent, homeDir2);
|
|
51560
52582
|
if (!existsSync21(home))
|
|
51561
52583
|
continue;
|
|
51562
52584
|
homesChecked += 1;
|
|
@@ -51570,15 +52592,15 @@ function censusHomeDrift(options = {}) {
|
|
|
51570
52592
|
for (const skill of dirEntries.sort()) {
|
|
51571
52593
|
if (skill.startsWith("."))
|
|
51572
52594
|
continue;
|
|
51573
|
-
const dir =
|
|
52595
|
+
const dir = join23(home, skill);
|
|
51574
52596
|
try {
|
|
51575
|
-
if (!
|
|
52597
|
+
if (!statSync12(dir).isDirectory())
|
|
51576
52598
|
continue;
|
|
51577
52599
|
} catch {
|
|
51578
52600
|
continue;
|
|
51579
52601
|
}
|
|
51580
52602
|
present.add(skill);
|
|
51581
|
-
const markerPath =
|
|
52603
|
+
const markerPath = join23(dir, SYNC_MARKER_FILE);
|
|
51582
52604
|
if (!existsSync21(markerPath)) {
|
|
51583
52605
|
unmarked += 1;
|
|
51584
52606
|
continue;
|
|
@@ -51589,7 +52611,7 @@ function censusHomeDrift(options = {}) {
|
|
|
51589
52611
|
entries.push({ agent, skill, kind: "stray-in-home", path: dir });
|
|
51590
52612
|
continue;
|
|
51591
52613
|
}
|
|
51592
|
-
const skillMdPath =
|
|
52614
|
+
const skillMdPath = join23(dir, "SKILL.md");
|
|
51593
52615
|
if (!existsSync21(skillMdPath)) {
|
|
51594
52616
|
entries.push({ agent, skill, kind: "diverged", path: dir, canonicalHash });
|
|
51595
52617
|
continue;
|
|
@@ -51598,14 +52620,14 @@ function censusHomeDrift(options = {}) {
|
|
|
51598
52620
|
if (homeHash !== canonicalHash) {
|
|
51599
52621
|
let homeStub;
|
|
51600
52622
|
try {
|
|
51601
|
-
homeStub = isPointerSkillMd(
|
|
52623
|
+
homeStub = isPointerSkillMd(readFileSync19(skillMdPath, "utf-8"));
|
|
51602
52624
|
} catch {
|
|
51603
52625
|
homeStub = undefined;
|
|
51604
52626
|
}
|
|
51605
52627
|
let canonicalStub;
|
|
51606
|
-
const canonicalSkillMd =
|
|
52628
|
+
const canonicalSkillMd = join23(corpusRoot, skill, "SKILL.md");
|
|
51607
52629
|
try {
|
|
51608
|
-
canonicalStub = isPointerSkillMd(
|
|
52630
|
+
canonicalStub = isPointerSkillMd(readFileSync19(canonicalSkillMd, "utf-8"));
|
|
51609
52631
|
} catch {
|
|
51610
52632
|
canonicalStub = undefined;
|
|
51611
52633
|
}
|
|
@@ -51618,7 +52640,7 @@ function censusHomeDrift(options = {}) {
|
|
|
51618
52640
|
agent,
|
|
51619
52641
|
skill: name,
|
|
51620
52642
|
kind: "missing-from-home",
|
|
51621
|
-
path:
|
|
52643
|
+
path: join23(home, name),
|
|
51622
52644
|
canonicalHash
|
|
51623
52645
|
});
|
|
51624
52646
|
}
|
|
@@ -51644,8 +52666,8 @@ var exports_diagnostic = {};
|
|
|
51644
52666
|
__export(exports_diagnostic, {
|
|
51645
52667
|
registerDiagnostic: () => registerDiagnostic
|
|
51646
52668
|
});
|
|
51647
|
-
import { existsSync as existsSync22, readFileSync as
|
|
51648
|
-
import { join as
|
|
52669
|
+
import { existsSync as existsSync22, readFileSync as readFileSync20, readdirSync as readdirSync12, statSync as statSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
52670
|
+
import { join as join24 } from "path";
|
|
51649
52671
|
import { execSync as execSync2 } from "child_process";
|
|
51650
52672
|
function registerDiagnostic(parent) {
|
|
51651
52673
|
parent.command("doctor").option("--json", "Output as JSON", false).description("Check env vars, system deps, and readiness for pinned skills").action((options) => handleDoctor(options));
|
|
@@ -51761,7 +52783,7 @@ Skills Test (${results.length} skill${results.length === 1 ? "" : "s"}):
|
|
|
51761
52783
|
}
|
|
51762
52784
|
function handleAuth(name, options) {
|
|
51763
52785
|
const cwd2 = process.cwd();
|
|
51764
|
-
const envFilePath =
|
|
52786
|
+
const envFilePath = join24(cwd2, ".env");
|
|
51765
52787
|
if (options.set) {
|
|
51766
52788
|
const eqIdx = options.set.indexOf("=");
|
|
51767
52789
|
if (eqIdx === -1) {
|
|
@@ -51783,7 +52805,7 @@ function handleAuth(name, options) {
|
|
|
51783
52805
|
process.exitCode = 1;
|
|
51784
52806
|
return;
|
|
51785
52807
|
}
|
|
51786
|
-
let existing = existsSync22(envFilePath) ?
|
|
52808
|
+
let existing = existsSync22(envFilePath) ? readFileSync20(envFilePath, "utf-8") : "";
|
|
51787
52809
|
const keyPattern = new RegExp(`^${key}=.*$`, "m");
|
|
51788
52810
|
const updated = keyPattern.test(existing) ? existing.replace(keyPattern, `${key}=${value}`) : existing.endsWith(`
|
|
51789
52811
|
`) || existing === "" ? existing + `${key}=${value}
|
|
@@ -51853,13 +52875,14 @@ function handleWhoami(options) {
|
|
|
51853
52875
|
let skillCount = 0;
|
|
51854
52876
|
if (exists)
|
|
51855
52877
|
try {
|
|
51856
|
-
skillCount = readdirSync12(agentSkillsPath).filter((f) => !f.startsWith(".") &&
|
|
52878
|
+
skillCount = readdirSync12(agentSkillsPath).filter((f) => !f.startsWith(".") && statSync13(join24(agentSkillsPath, f)).isDirectory()).length;
|
|
51857
52879
|
} catch {}
|
|
51858
52880
|
agentConfigs.push({ agent, label: AGENT_LABELS[agent], path: agentSkillsPath, exists, skillCount });
|
|
51859
52881
|
}
|
|
51860
52882
|
const skillsDir = resolveCorpusRoot();
|
|
52883
|
+
const credential = describeCredentialState();
|
|
51861
52884
|
if (options.json) {
|
|
51862
|
-
console.log(JSON.stringify({ version: package_default.version, installedCount: installed.length, installed, agents: agentConfigs, skillsDir, cwd: process.cwd() }, null, 2));
|
|
52885
|
+
console.log(JSON.stringify({ version: package_default.version, installedCount: installed.length, installed, agents: agentConfigs, skillsDir, cwd: process.cwd(), credential }, null, 2));
|
|
51863
52886
|
return;
|
|
51864
52887
|
}
|
|
51865
52888
|
console.log(source_default.bold(`
|
|
@@ -51867,6 +52890,13 @@ skills v${package_default.version}
|
|
|
51867
52890
|
`));
|
|
51868
52891
|
console.log(`${source_default.dim("Working directory:")} ${process.cwd()}`);
|
|
51869
52892
|
console.log(`${source_default.dim("Corpus directory:")} ${skillsDir}`);
|
|
52893
|
+
console.log(`${source_default.dim("Mode:")} ${credential.mode}`);
|
|
52894
|
+
if (credential.apiUrl)
|
|
52895
|
+
console.log(`${source_default.dim("API:")} ${credential.apiUrl} (${credential.apiUrlSource})`);
|
|
52896
|
+
if (credential.apiKeySource)
|
|
52897
|
+
console.log(`${source_default.dim("Credential:")} ${credential.apiKeySource}`);
|
|
52898
|
+
if (credential.error)
|
|
52899
|
+
console.log(source_default.red(`${source_default.dim("Credential:")} ${credential.error}`));
|
|
51870
52900
|
console.log();
|
|
51871
52901
|
if (!installed.length)
|
|
51872
52902
|
console.log(source_default.dim("No pinned skills in current project"));
|
|
@@ -51880,6 +52910,51 @@ skills v${package_default.version}
|
|
|
51880
52910
|
for (const cfg of agentConfigs)
|
|
51881
52911
|
console.log(cfg.exists ? ` ${source_default.green("\u2713")} ${cfg.agent} \u2014 ${cfg.skillCount} skill(s) at ${cfg.path}` : ` ${source_default.dim("\u2717")} ${cfg.agent} \u2014 not configured`);
|
|
51882
52912
|
}
|
|
52913
|
+
function describeCredentialState() {
|
|
52914
|
+
let credentialsFile = null;
|
|
52915
|
+
let mode = null;
|
|
52916
|
+
try {
|
|
52917
|
+
credentialsFile = getAuthFilePath();
|
|
52918
|
+
const bits = credentialFileMode();
|
|
52919
|
+
mode = bits === null ? null : `0${bits.toString(8).padStart(3, "0")}`;
|
|
52920
|
+
} catch {}
|
|
52921
|
+
try {
|
|
52922
|
+
const fleet = resolveSkillsFleet();
|
|
52923
|
+
if (fleet.mode === "hosted") {
|
|
52924
|
+
return {
|
|
52925
|
+
mode: "hosted",
|
|
52926
|
+
apiUrl: fleet.apiOrigin,
|
|
52927
|
+
apiUrlSource: fleet.apiUrlSource,
|
|
52928
|
+
apiKeySource: fleet.apiKeySource,
|
|
52929
|
+
apiKeyTier: fleet.apiKeyTier,
|
|
52930
|
+
credentialsFile,
|
|
52931
|
+
credentialsFileMode: mode,
|
|
52932
|
+
error: null
|
|
52933
|
+
};
|
|
52934
|
+
}
|
|
52935
|
+
return {
|
|
52936
|
+
mode: "local",
|
|
52937
|
+
apiUrl: null,
|
|
52938
|
+
apiUrlSource: null,
|
|
52939
|
+
apiKeySource: null,
|
|
52940
|
+
apiKeyTier: null,
|
|
52941
|
+
credentialsFile,
|
|
52942
|
+
credentialsFileMode: mode,
|
|
52943
|
+
error: null
|
|
52944
|
+
};
|
|
52945
|
+
} catch (error) {
|
|
52946
|
+
return {
|
|
52947
|
+
mode: "misconfigured",
|
|
52948
|
+
apiUrl: null,
|
|
52949
|
+
apiUrlSource: null,
|
|
52950
|
+
apiKeySource: null,
|
|
52951
|
+
apiKeyTier: null,
|
|
52952
|
+
credentialsFile,
|
|
52953
|
+
credentialsFileMode: mode,
|
|
52954
|
+
error: error.message
|
|
52955
|
+
};
|
|
52956
|
+
}
|
|
52957
|
+
}
|
|
51883
52958
|
function handleOutdated(options) {
|
|
51884
52959
|
const installed = getInstalledSkills();
|
|
51885
52960
|
const pins = [];
|
|
@@ -51887,11 +52962,11 @@ function handleOutdated(options) {
|
|
|
51887
52962
|
for (const name of installed) {
|
|
51888
52963
|
const installedVersion = meta.skills[name]?.version ?? "unknown";
|
|
51889
52964
|
const registryPath = getSkillPath(name);
|
|
51890
|
-
const registryPkgPath =
|
|
52965
|
+
const registryPkgPath = join24(registryPath, "package.json");
|
|
51891
52966
|
let registryVersion = "unknown";
|
|
51892
52967
|
if (existsSync22(registryPkgPath))
|
|
51893
52968
|
try {
|
|
51894
|
-
registryVersion = JSON.parse(
|
|
52969
|
+
registryVersion = JSON.parse(readFileSync20(registryPkgPath, "utf-8")).version || "unknown";
|
|
51895
52970
|
} catch {}
|
|
51896
52971
|
if (installedVersion !== registryVersion)
|
|
51897
52972
|
pins.push({ skill: name, installedVersion, registryVersion });
|
|
@@ -51940,6 +53015,8 @@ var init_diagnostic = __esm(() => {
|
|
|
51940
53015
|
init_installer();
|
|
51941
53016
|
init_home_census();
|
|
51942
53017
|
init_home_migration();
|
|
53018
|
+
init_auth_store();
|
|
53019
|
+
init_fleet_credentials();
|
|
51943
53020
|
});
|
|
51944
53021
|
|
|
51945
53022
|
// src/lib/blog-article.ts
|
|
@@ -52112,20 +53189,20 @@ var init_runs = __esm(() => {
|
|
|
52112
53189
|
|
|
52113
53190
|
// src/lib/run-state.ts
|
|
52114
53191
|
import { createHash as createHash4, randomBytes } from "crypto";
|
|
52115
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as
|
|
52116
|
-
import { extname, join as
|
|
53192
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as readFileSync21, readdirSync as readdirSync13, statSync as statSync14, writeFileSync as writeFileSync12 } from "fs";
|
|
53193
|
+
import { extname, join as join25, relative as relative3 } from "path";
|
|
52117
53194
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
52118
53195
|
const now3 = new Date;
|
|
52119
53196
|
const id = createRunId(now3);
|
|
52120
53197
|
const day = now3.toISOString().slice(0, 10);
|
|
52121
53198
|
const skillName = normalizeSkillName(params.skill);
|
|
52122
53199
|
const root = getProjectStateDir(targetDir);
|
|
52123
|
-
const runDir =
|
|
52124
|
-
const logsDir =
|
|
52125
|
-
const exportDir =
|
|
53200
|
+
const runDir = join25(root, "runs", day, id);
|
|
53201
|
+
const logsDir = join25(runDir, "logs");
|
|
53202
|
+
const exportDir = join25(root, "exports", skillName, id);
|
|
52126
53203
|
mkdirSync10(logsDir, { recursive: true });
|
|
52127
53204
|
mkdirSync10(exportDir, { recursive: true });
|
|
52128
|
-
mkdirSync10(
|
|
53205
|
+
mkdirSync10(join25(root, "tmp"), { recursive: true });
|
|
52129
53206
|
const record = {
|
|
52130
53207
|
id,
|
|
52131
53208
|
skill: skillName,
|
|
@@ -52176,27 +53253,27 @@ function updateSkillRun(context, patch) {
|
|
|
52176
53253
|
return context.record;
|
|
52177
53254
|
}
|
|
52178
53255
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
52179
|
-
writeFileSync12(
|
|
52180
|
-
writeFileSync12(
|
|
53256
|
+
writeFileSync12(join25(context.logsDir, "stdout.log"), stdout);
|
|
53257
|
+
writeFileSync12(join25(context.logsDir, "stderr.log"), stderr);
|
|
52181
53258
|
}
|
|
52182
53259
|
function appendRunEvent(context, event, data = {}) {
|
|
52183
53260
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
52184
53261
|
`;
|
|
52185
|
-
const path =
|
|
52186
|
-
const previous = existsSync23(path) ?
|
|
53262
|
+
const path = join25(context.runDir, "events.ndjson");
|
|
53263
|
+
const previous = existsSync23(path) ? readFileSync21(path, "utf-8") : "";
|
|
52187
53264
|
writeFileSync12(path, previous + line);
|
|
52188
53265
|
}
|
|
52189
53266
|
function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
52190
|
-
const runsRoot =
|
|
53267
|
+
const runsRoot = join25(getProjectStateDir(targetDir), "runs");
|
|
52191
53268
|
if (!existsSync23(runsRoot))
|
|
52192
53269
|
return [];
|
|
52193
53270
|
const records = [];
|
|
52194
53271
|
for (const day of readdirSync13(runsRoot).sort().reverse()) {
|
|
52195
|
-
const dayDir =
|
|
52196
|
-
if (!
|
|
53272
|
+
const dayDir = join25(runsRoot, day);
|
|
53273
|
+
if (!statSync14(dayDir).isDirectory())
|
|
52197
53274
|
continue;
|
|
52198
53275
|
for (const runId of readdirSync13(dayDir).sort().reverse()) {
|
|
52199
|
-
const record = readRunRecord(
|
|
53276
|
+
const record = readRunRecord(join25(dayDir, runId));
|
|
52200
53277
|
if (record)
|
|
52201
53278
|
records.push(record);
|
|
52202
53279
|
if (records.length >= limit)
|
|
@@ -52206,11 +53283,11 @@ function listSkillRuns(targetDir = process.cwd(), limit = 50) {
|
|
|
52206
53283
|
return records;
|
|
52207
53284
|
}
|
|
52208
53285
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
52209
|
-
const runsRoot =
|
|
53286
|
+
const runsRoot = join25(getProjectStateDir(targetDir), "runs");
|
|
52210
53287
|
if (!existsSync23(runsRoot))
|
|
52211
53288
|
return null;
|
|
52212
53289
|
for (const day of readdirSync13(runsRoot)) {
|
|
52213
|
-
const record = readRunRecord(
|
|
53290
|
+
const record = readRunRecord(join25(runsRoot, day, runId));
|
|
52214
53291
|
if (record)
|
|
52215
53292
|
return record;
|
|
52216
53293
|
}
|
|
@@ -52227,14 +53304,14 @@ function skillRunEnv(context) {
|
|
|
52227
53304
|
};
|
|
52228
53305
|
}
|
|
52229
53306
|
function getRunExportDir(runId, skill, targetDir = process.cwd()) {
|
|
52230
|
-
return
|
|
53307
|
+
return join25(getProjectStateDir(targetDir), "exports", normalizeSkillName(skill), runId);
|
|
52231
53308
|
}
|
|
52232
53309
|
function writeRunRecord(context) {
|
|
52233
|
-
writeFileSync12(
|
|
53310
|
+
writeFileSync12(join25(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
52234
53311
|
`);
|
|
52235
53312
|
}
|
|
52236
53313
|
function writeArtifactsManifest(context, artifacts) {
|
|
52237
|
-
writeFileSync12(
|
|
53314
|
+
writeFileSync12(join25(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
52238
53315
|
`);
|
|
52239
53316
|
}
|
|
52240
53317
|
function collectRunArtifacts(context) {
|
|
@@ -52242,8 +53319,8 @@ function collectRunArtifacts(context) {
|
|
|
52242
53319
|
return [];
|
|
52243
53320
|
const artifacts = [];
|
|
52244
53321
|
for (const path of walkFiles(context.exportDir)) {
|
|
52245
|
-
const stat =
|
|
52246
|
-
const bytes =
|
|
53322
|
+
const stat = statSync14(path);
|
|
53323
|
+
const bytes = readFileSync21(path);
|
|
52247
53324
|
artifacts.push({
|
|
52248
53325
|
path: toProjectRelative(context.targetDir, path),
|
|
52249
53326
|
mime: mimeForPath(path),
|
|
@@ -52254,11 +53331,11 @@ function collectRunArtifacts(context) {
|
|
|
52254
53331
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
52255
53332
|
}
|
|
52256
53333
|
function readRunRecord(runDir) {
|
|
52257
|
-
const path =
|
|
53334
|
+
const path = join25(runDir, "run.json");
|
|
52258
53335
|
if (!existsSync23(path))
|
|
52259
53336
|
return null;
|
|
52260
53337
|
try {
|
|
52261
|
-
return JSON.parse(
|
|
53338
|
+
return JSON.parse(readFileSync21(path, "utf-8"));
|
|
52262
53339
|
} catch {
|
|
52263
53340
|
return null;
|
|
52264
53341
|
}
|
|
@@ -52266,8 +53343,8 @@ function readRunRecord(runDir) {
|
|
|
52266
53343
|
function walkFiles(dir) {
|
|
52267
53344
|
const files = [];
|
|
52268
53345
|
for (const entry of readdirSync13(dir)) {
|
|
52269
|
-
const full =
|
|
52270
|
-
if (
|
|
53346
|
+
const full = join25(dir, entry);
|
|
53347
|
+
if (statSync14(full).isDirectory())
|
|
52271
53348
|
files.push(...walkFiles(full));
|
|
52272
53349
|
else
|
|
52273
53350
|
files.push(full);
|
|
@@ -56901,7 +57978,7 @@ var init_v4 = __esm(() => {
|
|
|
56901
57978
|
init_classic();
|
|
56902
57979
|
});
|
|
56903
57980
|
|
|
56904
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
|
|
57981
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
|
|
56905
57982
|
function assertCompleteRequestPrompt(request) {
|
|
56906
57983
|
if (request.params.ref.type !== "ref/prompt") {
|
|
56907
57984
|
throw new TypeError(`Expected CompleteRequestPrompt, but got ${request.params.ref.type}`);
|
|
@@ -57739,7 +58816,7 @@ var init_types2 = __esm(() => {
|
|
|
57739
58816
|
};
|
|
57740
58817
|
});
|
|
57741
58818
|
|
|
57742
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
58819
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
57743
58820
|
class ReadBuffer {
|
|
57744
58821
|
constructor(options) {
|
|
57745
58822
|
this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
|
|
@@ -57782,7 +58859,7 @@ var init_stdio = __esm(() => {
|
|
|
57782
58859
|
STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
|
|
57783
58860
|
});
|
|
57784
58861
|
|
|
57785
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
58862
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
57786
58863
|
import process14 from "process";
|
|
57787
58864
|
|
|
57788
58865
|
class StdioServerTransport {
|
|
@@ -57940,7 +59017,7 @@ var init_v4_mini = __esm(() => {
|
|
|
57940
59017
|
init_mini();
|
|
57941
59018
|
});
|
|
57942
59019
|
|
|
57943
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
59020
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
57944
59021
|
function isZ4Schema(s) {
|
|
57945
59022
|
const schema = s;
|
|
57946
59023
|
return !!schema._zod;
|
|
@@ -58105,7 +59182,7 @@ var init_zod_compat = __esm(() => {
|
|
|
58105
59182
|
init_v4_mini();
|
|
58106
59183
|
});
|
|
58107
59184
|
|
|
58108
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
|
|
59185
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
|
|
58109
59186
|
function isTerminal(status) {
|
|
58110
59187
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
58111
59188
|
}
|
|
@@ -59509,7 +60586,7 @@ var init_esm2 = __esm(() => {
|
|
|
59509
60586
|
init_zodToJsonSchema();
|
|
59510
60587
|
});
|
|
59511
60588
|
|
|
59512
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
|
|
60589
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
|
|
59513
60590
|
function mapMiniTarget(t) {
|
|
59514
60591
|
if (!t)
|
|
59515
60592
|
return "draft-7";
|
|
@@ -59556,7 +60633,7 @@ var init_zod_json_schema_compat = __esm(() => {
|
|
|
59556
60633
|
init_esm2();
|
|
59557
60634
|
});
|
|
59558
60635
|
|
|
59559
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
|
|
60636
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
|
|
59560
60637
|
class Protocol {
|
|
59561
60638
|
constructor(_options) {
|
|
59562
60639
|
this._options = _options;
|
|
@@ -63403,13 +64480,32 @@ var require_data = __commonJS((exports, module) => {
|
|
|
63403
64480
|
};
|
|
63404
64481
|
});
|
|
63405
64482
|
|
|
63406
|
-
// ../../node_modules/.bun/fast-uri@3.1.
|
|
64483
|
+
// ../../node_modules/.bun/fast-uri@3.1.7/node_modules/fast-uri/lib/utils.js
|
|
63407
64484
|
var require_utils = __commonJS((exports, module) => {
|
|
63408
64485
|
var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
|
|
63409
64486
|
var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
64487
|
+
var isPort = RegExp.prototype.test.bind(/^\d*$/u);
|
|
63410
64488
|
var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
63411
64489
|
var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
63412
|
-
var isPathCharacter = RegExp.prototype.test.bind(/^[
|
|
64490
|
+
var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
|
|
64491
|
+
var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
|
|
64492
|
+
var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
|
|
64493
|
+
var BYTE_HEX = new Array(256);
|
|
64494
|
+
{
|
|
64495
|
+
const HEX_DIGITS = "0123456789ABCDEF";
|
|
64496
|
+
for (let i = 0;i < 256; i++) {
|
|
64497
|
+
BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
|
|
64498
|
+
}
|
|
64499
|
+
}
|
|
64500
|
+
function percentEncodeNonAscii(cp) {
|
|
64501
|
+
if (cp < 2048) {
|
|
64502
|
+
return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
|
|
64503
|
+
}
|
|
64504
|
+
if (cp < 65536) {
|
|
64505
|
+
return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
64506
|
+
}
|
|
64507
|
+
return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
64508
|
+
}
|
|
63413
64509
|
function stringArrayToHexStripped(input) {
|
|
63414
64510
|
let acc = "";
|
|
63415
64511
|
let code = 0;
|
|
@@ -63434,91 +64530,122 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
63434
64530
|
}
|
|
63435
64531
|
return acc;
|
|
63436
64532
|
}
|
|
64533
|
+
var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
|
|
64534
|
+
var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
|
|
64535
|
+
var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
|
|
63437
64536
|
var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
|
|
63438
|
-
function
|
|
63439
|
-
|
|
63440
|
-
|
|
63441
|
-
|
|
63442
|
-
|
|
63443
|
-
|
|
63444
|
-
|
|
63445
|
-
|
|
63446
|
-
|
|
63447
|
-
} else {
|
|
63448
|
-
output.error = true;
|
|
63449
|
-
return false;
|
|
64537
|
+
function isZoneIdentifier(zone) {
|
|
64538
|
+
if (zone.length === 0)
|
|
64539
|
+
return false;
|
|
64540
|
+
for (let i = 0;i < zone.length; i++) {
|
|
64541
|
+
if (isZoneCharacter(zone[i]))
|
|
64542
|
+
continue;
|
|
64543
|
+
if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
|
|
64544
|
+
i += 2;
|
|
64545
|
+
continue;
|
|
63450
64546
|
}
|
|
63451
|
-
|
|
64547
|
+
return false;
|
|
63452
64548
|
}
|
|
63453
64549
|
return true;
|
|
63454
64550
|
}
|
|
63455
|
-
function
|
|
63456
|
-
let
|
|
63457
|
-
|
|
63458
|
-
|
|
63459
|
-
|
|
63460
|
-
let
|
|
63461
|
-
|
|
63462
|
-
|
|
63463
|
-
|
|
63464
|
-
|
|
63465
|
-
|
|
63466
|
-
|
|
63467
|
-
|
|
63468
|
-
if (cursor === ":") {
|
|
63469
|
-
if (endipv6Encountered === true) {
|
|
63470
|
-
endIpv6 = true;
|
|
63471
|
-
}
|
|
63472
|
-
if (!consume(buffer, address, output)) {
|
|
63473
|
-
break;
|
|
63474
|
-
}
|
|
63475
|
-
if (++tokenCount > 7) {
|
|
63476
|
-
output.error = true;
|
|
63477
|
-
break;
|
|
63478
|
-
}
|
|
63479
|
-
if (i > 0 && input[i - 1] === ":") {
|
|
63480
|
-
endipv6Encountered = true;
|
|
63481
|
-
}
|
|
63482
|
-
address.push(":");
|
|
63483
|
-
continue;
|
|
63484
|
-
} else if (cursor === "%") {
|
|
63485
|
-
if (!consume(buffer, address, output)) {
|
|
63486
|
-
break;
|
|
64551
|
+
function compressIPv6ZeroRun(hextets) {
|
|
64552
|
+
let bestStart = -1;
|
|
64553
|
+
let bestLength = 0;
|
|
64554
|
+
let runStart = -1;
|
|
64555
|
+
let runLength = 0;
|
|
64556
|
+
for (let i = 0;i < hextets.length; i++) {
|
|
64557
|
+
if (hextets[i] === "0") {
|
|
64558
|
+
if (runStart === -1)
|
|
64559
|
+
runStart = i;
|
|
64560
|
+
runLength++;
|
|
64561
|
+
if (runLength > bestLength) {
|
|
64562
|
+
bestLength = runLength;
|
|
64563
|
+
bestStart = runStart;
|
|
63487
64564
|
}
|
|
63488
|
-
consume = consumeIsZone;
|
|
63489
64565
|
} else {
|
|
63490
|
-
|
|
63491
|
-
|
|
64566
|
+
runStart = -1;
|
|
64567
|
+
runLength = 0;
|
|
63492
64568
|
}
|
|
63493
64569
|
}
|
|
63494
|
-
if (
|
|
63495
|
-
|
|
63496
|
-
|
|
63497
|
-
|
|
63498
|
-
|
|
63499
|
-
|
|
63500
|
-
|
|
64570
|
+
if (bestLength < 2)
|
|
64571
|
+
return hextets.join(":");
|
|
64572
|
+
const head2 = hextets.slice(0, bestStart).join(":");
|
|
64573
|
+
const tail2 = hextets.slice(bestStart + bestLength).join(":");
|
|
64574
|
+
return head2 + "::" + tail2;
|
|
64575
|
+
}
|
|
64576
|
+
function normalizeIPv6Address(input) {
|
|
64577
|
+
const compression = input.indexOf("::");
|
|
64578
|
+
if (compression !== -1 && input.indexOf("::", compression + 1) !== -1)
|
|
64579
|
+
return;
|
|
64580
|
+
const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
|
|
64581
|
+
const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
|
|
64582
|
+
if (compression !== -1) {
|
|
64583
|
+
if (left.length === 1 && left[0] === "")
|
|
64584
|
+
left.length = 0;
|
|
64585
|
+
if (right.length === 1 && right[0] === "")
|
|
64586
|
+
right.length = 0;
|
|
64587
|
+
}
|
|
64588
|
+
const parts = left.concat(right);
|
|
64589
|
+
let hextetCount = 0;
|
|
64590
|
+
for (let i = 0;i < parts.length; i++) {
|
|
64591
|
+
const part = parts[i];
|
|
64592
|
+
if (part === "")
|
|
64593
|
+
return;
|
|
64594
|
+
if (part.indexOf(".") !== -1) {
|
|
64595
|
+
if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part))
|
|
64596
|
+
return;
|
|
64597
|
+
hextetCount += 2;
|
|
64598
|
+
continue;
|
|
63501
64599
|
}
|
|
64600
|
+
if (!isHextet(part))
|
|
64601
|
+
return;
|
|
64602
|
+
parts[i] = parseInt(part, 16).toString(16);
|
|
64603
|
+
hextetCount++;
|
|
63502
64604
|
}
|
|
63503
|
-
|
|
63504
|
-
|
|
64605
|
+
if (compression === -1) {
|
|
64606
|
+
if (hextetCount !== 8)
|
|
64607
|
+
return;
|
|
64608
|
+
return compressIPv6ZeroRun(parts);
|
|
64609
|
+
}
|
|
64610
|
+
if (hextetCount >= 8)
|
|
64611
|
+
return;
|
|
64612
|
+
const expanded = parts.slice(0, left.length);
|
|
64613
|
+
for (let i = hextetCount;i < 8; i++)
|
|
64614
|
+
expanded.push("0");
|
|
64615
|
+
for (let i = left.length;i < parts.length; i++)
|
|
64616
|
+
expanded.push(parts[i]);
|
|
64617
|
+
return compressIPv6ZeroRun(expanded);
|
|
63505
64618
|
}
|
|
63506
64619
|
function normalizeIPv6(host) {
|
|
63507
|
-
|
|
63508
|
-
|
|
63509
|
-
|
|
63510
|
-
|
|
63511
|
-
|
|
63512
|
-
|
|
63513
|
-
|
|
63514
|
-
|
|
63515
|
-
|
|
63516
|
-
|
|
63517
|
-
}
|
|
63518
|
-
|
|
63519
|
-
|
|
63520
|
-
|
|
63521
|
-
|
|
64620
|
+
const bracketed = host[0] === "[" && host[host.length - 1] === "]";
|
|
64621
|
+
const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
|
|
64622
|
+
if (hasBracket && !bracketed)
|
|
64623
|
+
return { host, isIPV6: false, error: true };
|
|
64624
|
+
let input = bracketed ? host.slice(1, -1) : host;
|
|
64625
|
+
if (bracketed && isIPvFuture(input)) {
|
|
64626
|
+
input = input.toLowerCase();
|
|
64627
|
+
return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
|
|
64628
|
+
}
|
|
64629
|
+
if (findToken(input, ":") < 2) {
|
|
64630
|
+
return { host, isIPV6: false, error: bracketed };
|
|
64631
|
+
}
|
|
64632
|
+
let zoneIdentifier = "";
|
|
64633
|
+
const zoneSeparator = input.indexOf("%");
|
|
64634
|
+
if (zoneSeparator !== -1) {
|
|
64635
|
+
const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
|
|
64636
|
+
zoneIdentifier = input.slice(zoneSeparator + separatorLength);
|
|
64637
|
+
if (!isZoneIdentifier(zoneIdentifier))
|
|
64638
|
+
return { host, isIPV6: false, error: true };
|
|
64639
|
+
input = input.slice(0, zoneSeparator);
|
|
64640
|
+
}
|
|
64641
|
+
const address = normalizeIPv6Address(input);
|
|
64642
|
+
if (address === undefined)
|
|
64643
|
+
return { host, isIPV6: false, error: true };
|
|
64644
|
+
return {
|
|
64645
|
+
host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
|
|
64646
|
+
escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
|
|
64647
|
+
isIPV6: true
|
|
64648
|
+
};
|
|
63522
64649
|
}
|
|
63523
64650
|
function findToken(str2, token) {
|
|
63524
64651
|
let ind = 0;
|
|
@@ -63606,8 +64733,8 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
63606
64733
|
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
63607
64734
|
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
63608
64735
|
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
63609
|
-
function reescapeHostDelimiters(host,
|
|
63610
|
-
const re =
|
|
64736
|
+
function reescapeHostDelimiters(host, isIP2) {
|
|
64737
|
+
const re = isIP2 ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
|
|
63611
64738
|
re.lastIndex = 0;
|
|
63612
64739
|
return host.replace(re, (ch) => HOST_DELIMS[ch]);
|
|
63613
64740
|
}
|
|
@@ -63638,7 +64765,8 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
63638
64765
|
function normalizePathEncoding(input) {
|
|
63639
64766
|
let output = "";
|
|
63640
64767
|
for (let i = 0;i < input.length; i++) {
|
|
63641
|
-
|
|
64768
|
+
const ch = input[i];
|
|
64769
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
63642
64770
|
const hex = input.slice(i + 1, i + 3);
|
|
63643
64771
|
if (isHexPair(hex)) {
|
|
63644
64772
|
const normalizedHex = hex.toUpperCase();
|
|
@@ -63652,10 +64780,152 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
63652
64780
|
continue;
|
|
63653
64781
|
}
|
|
63654
64782
|
}
|
|
63655
|
-
if (isPathCharacter(
|
|
63656
|
-
output +=
|
|
64783
|
+
if (isPathCharacter(ch)) {
|
|
64784
|
+
output += ch;
|
|
63657
64785
|
} else {
|
|
63658
|
-
|
|
64786
|
+
const code = input.charCodeAt(i);
|
|
64787
|
+
if (code < 128) {
|
|
64788
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
64789
|
+
} else if (code < 55296 || code > 57343) {
|
|
64790
|
+
output += percentEncodeNonAscii(code);
|
|
64791
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
64792
|
+
const low = input.charCodeAt(i + 1);
|
|
64793
|
+
if (low >= 56320 && low <= 57343) {
|
|
64794
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
64795
|
+
i++;
|
|
64796
|
+
} else {
|
|
64797
|
+
output += percentEncodeNonAscii(65533);
|
|
64798
|
+
}
|
|
64799
|
+
} else {
|
|
64800
|
+
output += percentEncodeNonAscii(65533);
|
|
64801
|
+
}
|
|
64802
|
+
}
|
|
64803
|
+
}
|
|
64804
|
+
return output;
|
|
64805
|
+
}
|
|
64806
|
+
function serializePathEncoding(input, pathNoScheme = false) {
|
|
64807
|
+
let output = "";
|
|
64808
|
+
let firstSegment = pathNoScheme && input[0] !== "/";
|
|
64809
|
+
for (let i = 0;i < input.length; i++) {
|
|
64810
|
+
const ch = input[i];
|
|
64811
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
64812
|
+
const hex = input.slice(i + 1, i + 3);
|
|
64813
|
+
if (isHexPair(hex)) {
|
|
64814
|
+
output += "%" + hex.toUpperCase();
|
|
64815
|
+
i += 2;
|
|
64816
|
+
continue;
|
|
64817
|
+
}
|
|
64818
|
+
}
|
|
64819
|
+
if (ch === "/") {
|
|
64820
|
+
firstSegment = false;
|
|
64821
|
+
}
|
|
64822
|
+
if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) {
|
|
64823
|
+
output += ch;
|
|
64824
|
+
} else {
|
|
64825
|
+
const code = input.charCodeAt(i);
|
|
64826
|
+
if (code < 128) {
|
|
64827
|
+
output += BYTE_HEX[code];
|
|
64828
|
+
} else if (code < 55296 || code > 57343) {
|
|
64829
|
+
output += percentEncodeNonAscii(code);
|
|
64830
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
64831
|
+
const low = input.charCodeAt(i + 1);
|
|
64832
|
+
if (low >= 56320 && low <= 57343) {
|
|
64833
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
64834
|
+
i++;
|
|
64835
|
+
} else {
|
|
64836
|
+
output += percentEncodeNonAscii(65533);
|
|
64837
|
+
}
|
|
64838
|
+
} else {
|
|
64839
|
+
output += percentEncodeNonAscii(65533);
|
|
64840
|
+
}
|
|
64841
|
+
}
|
|
64842
|
+
}
|
|
64843
|
+
return output;
|
|
64844
|
+
}
|
|
64845
|
+
function encodeComponent(input, isAllowed) {
|
|
64846
|
+
let output = "";
|
|
64847
|
+
for (let i = 0;i < input.length; i++) {
|
|
64848
|
+
const ch = input[i];
|
|
64849
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
64850
|
+
const hex = input.slice(i + 1, i + 3);
|
|
64851
|
+
if (isHexPair(hex)) {
|
|
64852
|
+
output += "%" + hex.toUpperCase();
|
|
64853
|
+
i += 2;
|
|
64854
|
+
continue;
|
|
64855
|
+
}
|
|
64856
|
+
}
|
|
64857
|
+
if (isAllowed(ch)) {
|
|
64858
|
+
output += ch;
|
|
64859
|
+
} else {
|
|
64860
|
+
const code = input.charCodeAt(i);
|
|
64861
|
+
if (code < 128) {
|
|
64862
|
+
output += BYTE_HEX[code];
|
|
64863
|
+
} else if (code < 55296 || code > 57343) {
|
|
64864
|
+
output += percentEncodeNonAscii(code);
|
|
64865
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
64866
|
+
const low = input.charCodeAt(i + 1);
|
|
64867
|
+
if (low >= 56320 && low <= 57343) {
|
|
64868
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
64869
|
+
i++;
|
|
64870
|
+
} else {
|
|
64871
|
+
output += percentEncodeNonAscii(65533);
|
|
64872
|
+
}
|
|
64873
|
+
} else {
|
|
64874
|
+
output += percentEncodeNonAscii(65533);
|
|
64875
|
+
}
|
|
64876
|
+
}
|
|
64877
|
+
}
|
|
64878
|
+
return output;
|
|
64879
|
+
}
|
|
64880
|
+
function encodeUserinfo(input) {
|
|
64881
|
+
return encodeComponent(input, isUserinfoCharacter);
|
|
64882
|
+
}
|
|
64883
|
+
function encodeQuery(input) {
|
|
64884
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
64885
|
+
}
|
|
64886
|
+
function encodeFragment(input) {
|
|
64887
|
+
return encodeComponent(input, isQueryFragmentCharacter);
|
|
64888
|
+
}
|
|
64889
|
+
function isEscapeSafe(cp) {
|
|
64890
|
+
return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
|
|
64891
|
+
}
|
|
64892
|
+
function normalizeQueryFragmentEncoding(input) {
|
|
64893
|
+
let output = "";
|
|
64894
|
+
for (let i = 0;i < input.length; i++) {
|
|
64895
|
+
const ch = input[i];
|
|
64896
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
64897
|
+
const hex = input.slice(i + 1, i + 3);
|
|
64898
|
+
if (isHexPair(hex)) {
|
|
64899
|
+
const normalizedHex = hex.toUpperCase();
|
|
64900
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
64901
|
+
if (isUnreserved(decoded)) {
|
|
64902
|
+
output += decoded;
|
|
64903
|
+
} else {
|
|
64904
|
+
output += "%" + normalizedHex;
|
|
64905
|
+
}
|
|
64906
|
+
i += 2;
|
|
64907
|
+
continue;
|
|
64908
|
+
}
|
|
64909
|
+
}
|
|
64910
|
+
if (isQueryFragmentCharacter(ch)) {
|
|
64911
|
+
output += ch;
|
|
64912
|
+
} else {
|
|
64913
|
+
const code = input.charCodeAt(i);
|
|
64914
|
+
if (code < 128) {
|
|
64915
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
64916
|
+
} else if (code < 55296 || code > 57343) {
|
|
64917
|
+
output += percentEncodeNonAscii(code);
|
|
64918
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
64919
|
+
const low = input.charCodeAt(i + 1);
|
|
64920
|
+
if (low >= 56320 && low <= 57343) {
|
|
64921
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
64922
|
+
i++;
|
|
64923
|
+
} else {
|
|
64924
|
+
output += percentEncodeNonAscii(65533);
|
|
64925
|
+
}
|
|
64926
|
+
} else {
|
|
64927
|
+
output += percentEncodeNonAscii(65533);
|
|
64928
|
+
}
|
|
63659
64929
|
}
|
|
63660
64930
|
}
|
|
63661
64931
|
return output;
|
|
@@ -63678,14 +64948,18 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
63678
64948
|
function recomposeAuthority(component) {
|
|
63679
64949
|
const uriTokens = [];
|
|
63680
64950
|
if (component.userinfo !== undefined) {
|
|
63681
|
-
uriTokens.push(component.userinfo);
|
|
64951
|
+
uriTokens.push(encodeUserinfo(component.userinfo));
|
|
63682
64952
|
uriTokens.push("@");
|
|
63683
64953
|
}
|
|
63684
64954
|
if (component.host !== undefined) {
|
|
63685
|
-
let host =
|
|
64955
|
+
let host = component.host;
|
|
63686
64956
|
if (!isIPv4(host)) {
|
|
63687
|
-
|
|
63688
|
-
if (ipV6res.isIPV6
|
|
64957
|
+
let ipV6res = normalizeIPv6(host);
|
|
64958
|
+
if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
|
|
64959
|
+
host = normalizePercentEncoding(host, true);
|
|
64960
|
+
ipV6res = normalizeIPv6(host);
|
|
64961
|
+
}
|
|
64962
|
+
if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
|
|
63689
64963
|
host = `[${ipV6res.escapedHost}]`;
|
|
63690
64964
|
} else {
|
|
63691
64965
|
host = reescapeHostDelimiters(host, false);
|
|
@@ -63694,8 +64968,12 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
63694
64968
|
uriTokens.push(host);
|
|
63695
64969
|
}
|
|
63696
64970
|
if (typeof component.port === "number" || typeof component.port === "string") {
|
|
64971
|
+
const port = String(component.port);
|
|
64972
|
+
if (!isPort(port)) {
|
|
64973
|
+
throw new TypeError("URI port is malformed.");
|
|
64974
|
+
}
|
|
63697
64975
|
uriTokens.push(":");
|
|
63698
|
-
uriTokens.push(
|
|
64976
|
+
uriTokens.push(port);
|
|
63699
64977
|
}
|
|
63700
64978
|
return uriTokens.length ? uriTokens.join("") : undefined;
|
|
63701
64979
|
}
|
|
@@ -63705,6 +64983,11 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
63705
64983
|
reescapeHostDelimiters,
|
|
63706
64984
|
normalizePercentEncoding,
|
|
63707
64985
|
normalizePathEncoding,
|
|
64986
|
+
serializePathEncoding,
|
|
64987
|
+
normalizeQueryFragmentEncoding,
|
|
64988
|
+
encodeUserinfo,
|
|
64989
|
+
encodeQuery,
|
|
64990
|
+
encodeFragment,
|
|
63708
64991
|
escapePreservingEscapes,
|
|
63709
64992
|
removeDotSegments,
|
|
63710
64993
|
isIPv4,
|
|
@@ -63714,10 +64997,10 @@ var require_utils = __commonJS((exports, module) => {
|
|
|
63714
64997
|
};
|
|
63715
64998
|
});
|
|
63716
64999
|
|
|
63717
|
-
// ../../node_modules/.bun/fast-uri@3.1.
|
|
65000
|
+
// ../../node_modules/.bun/fast-uri@3.1.7/node_modules/fast-uri/lib/schemes.js
|
|
63718
65001
|
var require_schemes = __commonJS((exports, module) => {
|
|
63719
65002
|
var { isUUID } = require_utils();
|
|
63720
|
-
var URN_REG =
|
|
65003
|
+
var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
|
|
63721
65004
|
var supportedSchemeNames = [
|
|
63722
65005
|
"http",
|
|
63723
65006
|
"https",
|
|
@@ -63772,9 +65055,10 @@ var require_schemes = __commonJS((exports, module) => {
|
|
|
63772
65055
|
wsComponent.secure = undefined;
|
|
63773
65056
|
}
|
|
63774
65057
|
if (wsComponent.resourceName) {
|
|
63775
|
-
const
|
|
65058
|
+
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
65059
|
+
const path = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
63776
65060
|
wsComponent.path = path && path !== "/" ? path : undefined;
|
|
63777
|
-
wsComponent.query =
|
|
65061
|
+
wsComponent.query = queryIndex === -1 ? undefined : wsComponent.resourceName.slice(queryIndex + 1);
|
|
63778
65062
|
wsComponent.resourceName = undefined;
|
|
63779
65063
|
}
|
|
63780
65064
|
wsComponent.fragment = undefined;
|
|
@@ -63786,7 +65070,7 @@ var require_schemes = __commonJS((exports, module) => {
|
|
|
63786
65070
|
return urnComponent;
|
|
63787
65071
|
}
|
|
63788
65072
|
const matches2 = urnComponent.path.match(URN_REG);
|
|
63789
|
-
if (matches2) {
|
|
65073
|
+
if (matches2 && matches2[0] === urnComponent.path) {
|
|
63790
65074
|
const scheme = options.scheme || urnComponent.scheme || "urn";
|
|
63791
65075
|
urnComponent.nid = matches2[1].toLowerCase();
|
|
63792
65076
|
urnComponent.nss = matches2[2];
|
|
@@ -63888,10 +65172,19 @@ var require_schemes = __commonJS((exports, module) => {
|
|
|
63888
65172
|
};
|
|
63889
65173
|
});
|
|
63890
65174
|
|
|
63891
|
-
// ../../node_modules/.bun/fast-uri@3.1.
|
|
65175
|
+
// ../../node_modules/.bun/fast-uri@3.1.7/node_modules/fast-uri/index.js
|
|
63892
65176
|
var require_fast_uri = __commonJS((exports, module) => {
|
|
63893
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding,
|
|
65177
|
+
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
63894
65178
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
65179
|
+
var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
65180
|
+
var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
|
|
65181
|
+
function decodeValidScheme(scheme) {
|
|
65182
|
+
const decodedScheme = unescape(String(scheme));
|
|
65183
|
+
if (!VALID_SCHEME.test(decodedScheme)) {
|
|
65184
|
+
throw new TypeError(MALFORMED_SCHEME_ERROR);
|
|
65185
|
+
}
|
|
65186
|
+
return decodedScheme;
|
|
65187
|
+
}
|
|
63895
65188
|
function normalize3(uri, options) {
|
|
63896
65189
|
if (typeof uri === "string") {
|
|
63897
65190
|
uri = normalizeString(uri, options);
|
|
@@ -63902,12 +65195,34 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
63902
65195
|
}
|
|
63903
65196
|
function resolve2(baseURI, relativeURI, options) {
|
|
63904
65197
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
63905
|
-
const {
|
|
63906
|
-
|
|
63907
|
-
|
|
65198
|
+
const {
|
|
65199
|
+
parsed: baseParsed,
|
|
65200
|
+
malformedAuthorityOrPort: baseMalformed,
|
|
65201
|
+
malformedPercentEncoding: baseMalformedPercentEncoding,
|
|
65202
|
+
malformedSchemeSpecific: baseMalformedSchemeSpecific,
|
|
65203
|
+
malformedHost: baseMalformedHost,
|
|
65204
|
+
malformedScheme: baseMalformedScheme
|
|
65205
|
+
} = parseWithStatus(baseURI, schemelessOptions);
|
|
65206
|
+
const {
|
|
65207
|
+
parsed: relativeParsed,
|
|
65208
|
+
malformedAuthorityOrPort: relativeMalformed,
|
|
65209
|
+
malformedPercentEncoding: relativeMalformedPercentEncoding,
|
|
65210
|
+
malformedSchemeSpecific: relativeMalformedSchemeSpecific,
|
|
65211
|
+
malformedHost: relativeMalformedHost,
|
|
65212
|
+
malformedScheme: relativeMalformedScheme
|
|
65213
|
+
} = parseWithStatus(relativeURI, schemelessOptions);
|
|
65214
|
+
if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
|
|
63908
65215
|
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
63909
65216
|
}
|
|
63910
65217
|
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
65218
|
+
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
|
|
65219
|
+
const resolvedHost = resolved.host;
|
|
65220
|
+
const resolvedHostIsIP = resolvedHost !== undefined && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
65221
|
+
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
65222
|
+
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !/\P{ASCII}/u.test(resolvedHost);
|
|
65223
|
+
if (resolved.error && !encodedASCIIHost) {
|
|
65224
|
+
throw new Error(resolved.error);
|
|
65225
|
+
}
|
|
63911
65226
|
schemelessOptions.skipEscape = true;
|
|
63912
65227
|
return serialize(resolved, schemelessOptions);
|
|
63913
65228
|
}
|
|
@@ -63967,7 +65282,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
63967
65282
|
function equal(uriA, uriB, options) {
|
|
63968
65283
|
const normalizedA = normalizeComparableURI(uriA, options);
|
|
63969
65284
|
const normalizedB = normalizeComparableURI(uriB, options);
|
|
63970
|
-
return normalizedA !== undefined && normalizedB !== undefined && normalizedA
|
|
65285
|
+
return normalizedA !== undefined && normalizedB !== undefined && normalizedA === normalizedB;
|
|
63971
65286
|
}
|
|
63972
65287
|
function serialize(cmpts, opts) {
|
|
63973
65288
|
const component = {
|
|
@@ -63988,20 +65303,23 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
63988
65303
|
};
|
|
63989
65304
|
const options = Object.assign({}, opts);
|
|
63990
65305
|
const uriTokens = [];
|
|
65306
|
+
if (component.scheme) {
|
|
65307
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
65308
|
+
}
|
|
63991
65309
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
63992
65310
|
if (schemeHandler && schemeHandler.serialize)
|
|
63993
65311
|
schemeHandler.serialize(component, options);
|
|
65312
|
+
const hasAuthority = component.userinfo !== undefined || component.host !== undefined || component.port !== undefined;
|
|
65313
|
+
const pathNoScheme = !options.skipEscape && component.scheme === undefined && !hasAuthority;
|
|
63994
65314
|
if (component.path !== undefined) {
|
|
63995
65315
|
if (!options.skipEscape) {
|
|
63996
|
-
component.path =
|
|
63997
|
-
if (component.scheme !== undefined) {
|
|
63998
|
-
component.path = component.path.split("%3A").join(":");
|
|
63999
|
-
}
|
|
65316
|
+
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
64000
65317
|
} else {
|
|
64001
65318
|
component.path = normalizePercentEncoding(component.path);
|
|
64002
65319
|
}
|
|
64003
65320
|
}
|
|
64004
65321
|
if (options.reference !== "suffix" && component.scheme) {
|
|
65322
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
64005
65323
|
uriTokens.push(component.scheme, ":");
|
|
64006
65324
|
}
|
|
64007
65325
|
const authority = recomposeAuthority(component);
|
|
@@ -64019,16 +65337,19 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
64019
65337
|
if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
|
|
64020
65338
|
s = removeDotSegments(s);
|
|
64021
65339
|
}
|
|
65340
|
+
if (pathNoScheme) {
|
|
65341
|
+
s = serializePathEncoding(s, true);
|
|
65342
|
+
}
|
|
64022
65343
|
if (authority === undefined && s[0] === "/" && s[1] === "/") {
|
|
64023
65344
|
s = "/%2F" + s.slice(2);
|
|
64024
65345
|
}
|
|
64025
65346
|
uriTokens.push(s);
|
|
64026
65347
|
}
|
|
64027
65348
|
if (component.query !== undefined) {
|
|
64028
|
-
uriTokens.push("?", component.query);
|
|
65349
|
+
uriTokens.push("?", encodeQuery(component.query));
|
|
64029
65350
|
}
|
|
64030
65351
|
if (component.fragment !== undefined) {
|
|
64031
|
-
uriTokens.push("#", component.fragment);
|
|
65352
|
+
uriTokens.push("#", encodeFragment(component.fragment));
|
|
64032
65353
|
}
|
|
64033
65354
|
return uriTokens.join("");
|
|
64034
65355
|
}
|
|
@@ -64044,6 +65365,36 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
64044
65365
|
}
|
|
64045
65366
|
return;
|
|
64046
65367
|
}
|
|
65368
|
+
function hasMalformedPercentEncoding(component) {
|
|
65369
|
+
if (component === undefined)
|
|
65370
|
+
return false;
|
|
65371
|
+
let percent = component.indexOf("%");
|
|
65372
|
+
while (percent !== -1) {
|
|
65373
|
+
if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
|
|
65374
|
+
return true;
|
|
65375
|
+
}
|
|
65376
|
+
percent = component.indexOf("%", percent + 3);
|
|
65377
|
+
}
|
|
65378
|
+
return false;
|
|
65379
|
+
}
|
|
65380
|
+
function isIPLiteral(host) {
|
|
65381
|
+
return host[0] === "[" && host[host.length - 1] === "]";
|
|
65382
|
+
}
|
|
65383
|
+
function hasMalformedComponentPercentEncoding(matches2) {
|
|
65384
|
+
const host = matches2[4];
|
|
65385
|
+
return hasMalformedPercentEncoding(matches2[3]) || host !== undefined && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches2[6]) || hasMalformedPercentEncoding(matches2[7]) || hasMalformedPercentEncoding(matches2[8]);
|
|
65386
|
+
}
|
|
65387
|
+
function canonicalizeHost(parsed, options, schemeHandler, isIP2) {
|
|
65388
|
+
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP2 === false && nonSimpleDomain(parsed.host)) {
|
|
65389
|
+
try {
|
|
65390
|
+
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
65391
|
+
} catch (e) {
|
|
65392
|
+
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
65393
|
+
return true;
|
|
65394
|
+
}
|
|
65395
|
+
}
|
|
65396
|
+
return false;
|
|
65397
|
+
}
|
|
64047
65398
|
function parseWithStatus(uri, opts) {
|
|
64048
65399
|
const options = Object.assign({}, opts);
|
|
64049
65400
|
const parsed = {
|
|
@@ -64056,7 +65407,12 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
64056
65407
|
fragment: undefined
|
|
64057
65408
|
};
|
|
64058
65409
|
let malformedAuthorityOrPort = false;
|
|
64059
|
-
let
|
|
65410
|
+
let malformedPercentEncoding = false;
|
|
65411
|
+
let malformedSchemeSpecific = false;
|
|
65412
|
+
let malformedHost = false;
|
|
65413
|
+
let malformedIPLiteral = false;
|
|
65414
|
+
let malformedScheme = false;
|
|
65415
|
+
let isIP2 = false;
|
|
64060
65416
|
if (options.reference === "suffix") {
|
|
64061
65417
|
if (options.scheme) {
|
|
64062
65418
|
uri = options.scheme + ":" + uri;
|
|
@@ -64092,6 +65448,19 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
64092
65448
|
parsed.path = matches2[6] || "";
|
|
64093
65449
|
parsed.query = matches2[7];
|
|
64094
65450
|
parsed.fragment = matches2[8];
|
|
65451
|
+
if (parsed.scheme !== undefined) {
|
|
65452
|
+
const decodedScheme = unescape(parsed.scheme);
|
|
65453
|
+
if (VALID_SCHEME.test(decodedScheme)) {
|
|
65454
|
+
parsed.scheme = decodedScheme.toLowerCase();
|
|
65455
|
+
} else {
|
|
65456
|
+
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
65457
|
+
malformedScheme = true;
|
|
65458
|
+
}
|
|
65459
|
+
}
|
|
65460
|
+
malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches2);
|
|
65461
|
+
if (malformedPercentEncoding) {
|
|
65462
|
+
parsed.error = parsed.error || "URI contains malformed percent-encoding.";
|
|
65463
|
+
}
|
|
64095
65464
|
if (isNaN(parsed.port)) {
|
|
64096
65465
|
parsed.port = matches2[5];
|
|
64097
65466
|
}
|
|
@@ -64103,11 +65472,18 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
64103
65472
|
if (parsed.host) {
|
|
64104
65473
|
const ipv4result = isIPv4(parsed.host);
|
|
64105
65474
|
if (ipv4result === false) {
|
|
65475
|
+
const bracketedIPLiteral = isIPLiteral(parsed.host);
|
|
65476
|
+
const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
|
|
64106
65477
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
64107
|
-
|
|
64108
|
-
|
|
65478
|
+
isIP2 = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
65479
|
+
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
65480
|
+
parsed.host = isIP2 ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
65481
|
+
if (malformedIPLiteral) {
|
|
65482
|
+
parsed.error = parsed.error || "URI host is malformed.";
|
|
65483
|
+
malformedAuthorityOrPort = true;
|
|
65484
|
+
}
|
|
64109
65485
|
} else {
|
|
64110
|
-
|
|
65486
|
+
isIP2 = true;
|
|
64111
65487
|
}
|
|
64112
65488
|
}
|
|
64113
65489
|
if (parsed.scheme === undefined && parsed.userinfo === undefined && parsed.host === undefined && parsed.port === undefined && parsed.query === undefined && !parsed.path) {
|
|
@@ -64123,42 +65499,36 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
64123
65499
|
parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
|
|
64124
65500
|
}
|
|
64125
65501
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
64126
|
-
if (!
|
|
64127
|
-
|
|
64128
|
-
try {
|
|
64129
|
-
parsed.host = new URL("http://" + parsed.host).hostname;
|
|
64130
|
-
} catch (e) {
|
|
64131
|
-
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
64132
|
-
}
|
|
64133
|
-
}
|
|
65502
|
+
if (!malformedIPLiteral) {
|
|
65503
|
+
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP2);
|
|
64134
65504
|
}
|
|
64135
65505
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
64136
65506
|
if (uri.indexOf("%") !== -1) {
|
|
64137
|
-
if (parsed.
|
|
64138
|
-
parsed.
|
|
64139
|
-
|
|
64140
|
-
if (parsed.host !== undefined) {
|
|
64141
|
-
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
|
|
65507
|
+
if (parsed.host !== undefined && !malformedIPLiteral) {
|
|
65508
|
+
const host = isIP2 ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
65509
|
+
parsed.host = reescapeHostDelimiters(host, isIP2);
|
|
64142
65510
|
}
|
|
64143
65511
|
}
|
|
64144
65512
|
if (parsed.path) {
|
|
64145
65513
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
64146
65514
|
}
|
|
65515
|
+
if (parsed.query) {
|
|
65516
|
+
parsed.query = normalizeQueryFragmentEncoding(parsed.query);
|
|
65517
|
+
}
|
|
64147
65518
|
if (parsed.fragment) {
|
|
64148
|
-
|
|
64149
|
-
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
64150
|
-
} catch {
|
|
64151
|
-
parsed.error = parsed.error || "URI malformed";
|
|
64152
|
-
}
|
|
65519
|
+
parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
|
|
64153
65520
|
}
|
|
64154
65521
|
}
|
|
64155
65522
|
if (schemeHandler && schemeHandler.parse) {
|
|
64156
65523
|
schemeHandler.parse(parsed, options);
|
|
65524
|
+
if (schemeHandler === SCHEMES.urn && parsed.nid === undefined) {
|
|
65525
|
+
malformedSchemeSpecific = true;
|
|
65526
|
+
}
|
|
64157
65527
|
}
|
|
64158
65528
|
} else {
|
|
64159
65529
|
parsed.error = parsed.error || "URI can not be parsed.";
|
|
64160
65530
|
}
|
|
64161
|
-
return { parsed, malformedAuthorityOrPort };
|
|
65531
|
+
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
64162
65532
|
}
|
|
64163
65533
|
function parse6(uri, opts) {
|
|
64164
65534
|
return parseWithStatus(uri, opts).parsed;
|
|
@@ -64167,20 +65537,28 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
64167
65537
|
return normalizeStringWithStatus(uri, opts).normalized;
|
|
64168
65538
|
}
|
|
64169
65539
|
function normalizeStringWithStatus(uri, opts) {
|
|
64170
|
-
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
65540
|
+
const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
|
|
64171
65541
|
return {
|
|
64172
|
-
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
|
64173
|
-
malformedAuthorityOrPort
|
|
65542
|
+
normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
|
|
65543
|
+
malformedAuthorityOrPort,
|
|
65544
|
+
malformedPercentEncoding,
|
|
65545
|
+
malformedSchemeSpecific,
|
|
65546
|
+
malformedHost,
|
|
65547
|
+
malformedScheme
|
|
64174
65548
|
};
|
|
64175
65549
|
}
|
|
64176
65550
|
function normalizeComparableURI(uri, opts) {
|
|
64177
|
-
if (typeof uri
|
|
64178
|
-
|
|
64179
|
-
return malformedAuthorityOrPort ? undefined : normalized;
|
|
65551
|
+
if (typeof uri !== "string" && typeof uri !== "object") {
|
|
65552
|
+
return;
|
|
64180
65553
|
}
|
|
64181
|
-
|
|
64182
|
-
|
|
65554
|
+
let value;
|
|
65555
|
+
try {
|
|
65556
|
+
value = typeof uri === "string" ? uri : serialize(uri, opts);
|
|
65557
|
+
} catch {
|
|
65558
|
+
return;
|
|
64183
65559
|
}
|
|
65560
|
+
const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
|
|
65561
|
+
return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? undefined : normalized;
|
|
64184
65562
|
}
|
|
64185
65563
|
var fastUri = {
|
|
64186
65564
|
SCHEMES,
|
|
@@ -66986,7 +68364,7 @@ var require_dist = __commonJS((exports, module) => {
|
|
|
66986
68364
|
exports.default = formatsPlugin;
|
|
66987
68365
|
});
|
|
66988
68366
|
|
|
66989
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
|
|
68367
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
|
|
66990
68368
|
function createDefaultAjvInstance() {
|
|
66991
68369
|
const ajv = new import_ajv.default({
|
|
66992
68370
|
strict: false,
|
|
@@ -67029,7 +68407,7 @@ var init_ajv_provider = __esm(() => {
|
|
|
67029
68407
|
import_ajv_formats = __toESM(require_dist(), 1);
|
|
67030
68408
|
});
|
|
67031
68409
|
|
|
67032
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
|
|
68410
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
|
|
67033
68411
|
class ExperimentalServerTasks {
|
|
67034
68412
|
constructor(_server) {
|
|
67035
68413
|
this._server = _server;
|
|
@@ -67110,7 +68488,7 @@ var init_server = __esm(() => {
|
|
|
67110
68488
|
init_types2();
|
|
67111
68489
|
});
|
|
67112
68490
|
|
|
67113
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
|
|
68491
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
|
|
67114
68492
|
function assertToolsCallTaskCapability(requests, method2, entityName) {
|
|
67115
68493
|
if (!requests) {
|
|
67116
68494
|
throw new Error(`${entityName} does not support task creation (required for ${method2})`);
|
|
@@ -67145,7 +68523,7 @@ function assertClientRequestTaskCapability(requests, method2, entityName) {
|
|
|
67145
68523
|
}
|
|
67146
68524
|
}
|
|
67147
68525
|
|
|
67148
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
|
|
68526
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
|
|
67149
68527
|
var Server;
|
|
67150
68528
|
var init_server2 = __esm(() => {
|
|
67151
68529
|
init_protocol();
|
|
@@ -67477,7 +68855,7 @@ var init_server2 = __esm(() => {
|
|
|
67477
68855
|
};
|
|
67478
68856
|
});
|
|
67479
68857
|
|
|
67480
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
|
|
68858
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
|
|
67481
68859
|
function isCompletable(schema) {
|
|
67482
68860
|
return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
|
|
67483
68861
|
}
|
|
@@ -67493,7 +68871,7 @@ var init_completable = __esm(() => {
|
|
|
67493
68871
|
})(McpZodTypeKind || (McpZodTypeKind = {}));
|
|
67494
68872
|
});
|
|
67495
68873
|
|
|
67496
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/uriTemplate.js
|
|
68874
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/uriTemplate.js
|
|
67497
68875
|
class UriTemplate {
|
|
67498
68876
|
static isTemplate(str2) {
|
|
67499
68877
|
return /\{[^}\s]+\}/.test(str2);
|
|
@@ -67707,7 +69085,7 @@ class UriTemplate {
|
|
|
67707
69085
|
}
|
|
67708
69086
|
var MAX_TEMPLATE_LENGTH = 1e6, MAX_VARIABLE_LENGTH = 1e6, MAX_TEMPLATE_EXPRESSIONS = 1e4, MAX_REGEX_LENGTH = 1e6;
|
|
67709
69087
|
|
|
67710
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
|
|
69088
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
|
|
67711
69089
|
function validateToolName(name) {
|
|
67712
69090
|
const warnings = [];
|
|
67713
69091
|
if (name.length === 0) {
|
|
@@ -67768,7 +69146,7 @@ var init_toolNameValidation = __esm(() => {
|
|
|
67768
69146
|
TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
|
|
67769
69147
|
});
|
|
67770
69148
|
|
|
67771
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
|
|
69149
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
|
|
67772
69150
|
class ExperimentalMcpServerTasks {
|
|
67773
69151
|
constructor(_mcpServer) {
|
|
67774
69152
|
this._mcpServer = _mcpServer;
|
|
@@ -67783,7 +69161,7 @@ class ExperimentalMcpServerTasks {
|
|
|
67783
69161
|
}
|
|
67784
69162
|
}
|
|
67785
69163
|
|
|
67786
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
|
|
69164
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
|
|
67787
69165
|
class McpServer {
|
|
67788
69166
|
constructor(serverInfo, options) {
|
|
67789
69167
|
this._registeredResources = {};
|
|
@@ -69597,7 +70975,7 @@ function resolveRunRouting(skill, apiKey, apiUrl) {
|
|
|
69597
70975
|
return {
|
|
69598
70976
|
route: "error",
|
|
69599
70977
|
code: "REMOTE_REQUIRES_ORIGIN",
|
|
69600
|
-
error: `${skill.name} is a server-owned skill. Point the CLI at a Skills API: ` + `skills setup --api-url <url> (or export
|
|
70978
|
+
error: `${skill.name} is a server-owned skill. Point the CLI at a Skills API: ` + `skills setup --api-url <url> (or export HASNA_SKILLS_API_URL)`
|
|
69601
70979
|
};
|
|
69602
70980
|
}
|
|
69603
70981
|
if (!apiKey) {
|
|
@@ -69609,17 +70987,31 @@ function resolveRunRouting(skill, apiKey, apiUrl) {
|
|
|
69609
70987
|
}
|
|
69610
70988
|
return { route: "remote", apiKey };
|
|
69611
70989
|
}
|
|
69612
|
-
function resolveConfiguredRunRouting(skill) {
|
|
69613
|
-
|
|
70990
|
+
async function resolveConfiguredRunRouting(skill, env3 = process.env) {
|
|
70991
|
+
let fleet;
|
|
70992
|
+
let apiKey;
|
|
70993
|
+
try {
|
|
70994
|
+
fleet = resolveSkillsFleet(env3);
|
|
70995
|
+
apiKey = fleet.mode === "hosted" ? await resolveSkillsApiKey(env3) : null;
|
|
70996
|
+
} catch (error2) {
|
|
70997
|
+
const isMissingCredential = (error2 instanceof SkillsFleetCredentialError || error2?.name === "SkillsFleetCredentialError") && error2.code === "MISSING_API_CREDENTIAL";
|
|
70998
|
+
if (!isMissingCredential)
|
|
70999
|
+
throw error2;
|
|
71000
|
+
return {
|
|
71001
|
+
route: "error",
|
|
71002
|
+
code: "REMOTE_REQUIRES_CREDENTIAL",
|
|
71003
|
+
error: `${skill.name} is a server-owned skill. ${error2.message}`
|
|
71004
|
+
};
|
|
71005
|
+
}
|
|
71006
|
+
return resolveRunRouting(skill, apiKey, fleet.apiOrigin ?? undefined);
|
|
69614
71007
|
}
|
|
69615
71008
|
var init_run_routing = __esm(() => {
|
|
69616
|
-
|
|
69617
|
-
init_auth_store();
|
|
71009
|
+
init_fleet_credentials();
|
|
69618
71010
|
});
|
|
69619
71011
|
|
|
69620
71012
|
// src/mcp/operation-tools.ts
|
|
69621
|
-
import { existsSync as existsSync24, readdirSync as readdirSync14, statSync as
|
|
69622
|
-
import { join as
|
|
71013
|
+
import { existsSync as existsSync24, readdirSync as readdirSync14, statSync as statSync15 } from "fs";
|
|
71014
|
+
import { join as join26 } from "path";
|
|
69623
71015
|
function registerOperationTools(server) {
|
|
69624
71016
|
server.registerTool("scaffold_skill", {
|
|
69625
71017
|
title: "Scaffold Skill",
|
|
@@ -69840,7 +71232,7 @@ function registerOperationTools(server) {
|
|
|
69840
71232
|
return mcpError("INVALID_BLOG_ARTICLE_OPTIONS", validation.errors.join(" "));
|
|
69841
71233
|
}
|
|
69842
71234
|
}
|
|
69843
|
-
const routing = resolveConfiguredRunRouting(skill);
|
|
71235
|
+
const routing = await resolveConfiguredRunRouting(skill);
|
|
69844
71236
|
const runContext = createSkillRun({
|
|
69845
71237
|
skill: skillName,
|
|
69846
71238
|
args: runArgs,
|
|
@@ -69919,10 +71311,10 @@ function registerOperationTools(server) {
|
|
|
69919
71311
|
detail: exports_external.boolean().optional()
|
|
69920
71312
|
}
|
|
69921
71313
|
}, async ({ run_id, detail }) => {
|
|
69922
|
-
const {
|
|
69923
|
-
const apiKey =
|
|
71314
|
+
const { skillsCredentialOrReason: skillsCredentialOrReason2 } = await Promise.resolve().then(() => (init_fleet_credentials(), exports_fleet_credentials));
|
|
71315
|
+
const { apiKey, reason } = await skillsCredentialOrReason2();
|
|
69924
71316
|
if (!apiKey) {
|
|
69925
|
-
return mcpError("AUTH_REQUIRED", "Remote run status requires API access. Run: skills auth login", ["skills auth login"]);
|
|
71317
|
+
return mcpError("AUTH_REQUIRED", reason ?? "Remote run status requires API access. Run: skills auth login", ["skills auth login"]);
|
|
69926
71318
|
}
|
|
69927
71319
|
const localRun = findSkillRun(run_id);
|
|
69928
71320
|
const remoteRunId = localRun?.remoteRunId || run_id;
|
|
@@ -70021,8 +71413,8 @@ function registerOperationTools(server) {
|
|
|
70021
71413
|
if (exists) {
|
|
70022
71414
|
try {
|
|
70023
71415
|
skillCount = readdirSync14(agentSkillsPath).filter((f) => {
|
|
70024
|
-
const full =
|
|
70025
|
-
return !f.startsWith(".") &&
|
|
71416
|
+
const full = join26(agentSkillsPath, f);
|
|
71417
|
+
return !f.startsWith(".") && statSync15(full).isDirectory();
|
|
70026
71418
|
}).length;
|
|
70027
71419
|
} catch {}
|
|
70028
71420
|
}
|
|
@@ -70079,10 +71471,10 @@ var init_operation_tools = __esm(() => {
|
|
|
70079
71471
|
|
|
70080
71472
|
// src/lib/feedback.ts
|
|
70081
71473
|
import { appendFileSync as appendFileSync2, existsSync as existsSync25, mkdirSync as mkdirSync11 } from "fs";
|
|
70082
|
-
import { dirname as dirname8, join as
|
|
71474
|
+
import { dirname as dirname8, join as join27 } from "path";
|
|
70083
71475
|
import { Database } from "bun:sqlite";
|
|
70084
71476
|
function getFeedbackDbPath() {
|
|
70085
|
-
return
|
|
71477
|
+
return join27(getDataDir(), "skills.db");
|
|
70086
71478
|
}
|
|
70087
71479
|
function getFeedbackDb() {
|
|
70088
71480
|
const dbPath = getFeedbackDbPath();
|
|
@@ -70114,7 +71506,7 @@ function saveFeedback(input) {
|
|
|
70114
71506
|
throw new Error("Feedback message is required");
|
|
70115
71507
|
const category = input.category ?? "general";
|
|
70116
71508
|
if (isApiMode()) {
|
|
70117
|
-
const path =
|
|
71509
|
+
const path = join27(getDataDir(), "feedback.jsonl");
|
|
70118
71510
|
const dir = dirname8(path);
|
|
70119
71511
|
if (!existsSync25(dir))
|
|
70120
71512
|
mkdirSync11(dir, { recursive: true });
|
|
@@ -70131,12 +71523,10 @@ function saveFeedback(input) {
|
|
|
70131
71523
|
return { saved: true, category, path: getFeedbackDbPath() };
|
|
70132
71524
|
}
|
|
70133
71525
|
function isApiMode(env3 = process.env) {
|
|
70134
|
-
if (env3.HASNA_SKILLS_API_URL?.trim())
|
|
70135
|
-
return true;
|
|
70136
71526
|
try {
|
|
70137
|
-
return Boolean(resolveApiUrl(
|
|
71527
|
+
return Boolean(resolveApiUrl(env3));
|
|
70138
71528
|
} catch {
|
|
70139
|
-
return
|
|
71529
|
+
return true;
|
|
70140
71530
|
}
|
|
70141
71531
|
}
|
|
70142
71532
|
var init_feedback = __esm(() => {
|
|
@@ -70280,23 +71670,23 @@ var init_resource_meta_tools = __esm(() => {
|
|
|
70280
71670
|
});
|
|
70281
71671
|
|
|
70282
71672
|
// src/lib/scheduler.ts
|
|
70283
|
-
import { existsSync as existsSync26, readFileSync as
|
|
70284
|
-
import { join as
|
|
71673
|
+
import { existsSync as existsSync26, readFileSync as readFileSync22, writeFileSync as writeFileSync13, mkdirSync as mkdirSync12 } from "fs";
|
|
71674
|
+
import { join as join28 } from "path";
|
|
70285
71675
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
70286
|
-
return
|
|
71676
|
+
return join28(targetDir, ".skills", "schedules.json");
|
|
70287
71677
|
}
|
|
70288
71678
|
function loadSchedules(targetDir = process.cwd()) {
|
|
70289
71679
|
const path = getSchedulesPath(targetDir);
|
|
70290
71680
|
if (existsSync26(path)) {
|
|
70291
71681
|
try {
|
|
70292
|
-
return JSON.parse(
|
|
71682
|
+
return JSON.parse(readFileSync22(path, "utf-8"));
|
|
70293
71683
|
} catch {}
|
|
70294
71684
|
}
|
|
70295
71685
|
return { version: 1, schedules: [] };
|
|
70296
71686
|
}
|
|
70297
71687
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
70298
71688
|
const path = getSchedulesPath(targetDir);
|
|
70299
|
-
const dir =
|
|
71689
|
+
const dir = join28(targetDir, ".skills");
|
|
70300
71690
|
if (!existsSync26(dir))
|
|
70301
71691
|
mkdirSync12(dir, { recursive: true });
|
|
70302
71692
|
writeFileSync13(path, JSON.stringify(data, null, 2));
|
|
@@ -70599,12 +71989,12 @@ import { createHash as createHash5, createHmac as createHmac3 } from "crypto";
|
|
|
70599
71989
|
import {
|
|
70600
71990
|
existsSync as existsSync27,
|
|
70601
71991
|
mkdirSync as mkdirSync13,
|
|
70602
|
-
readFileSync as
|
|
71992
|
+
readFileSync as readFileSync23,
|
|
70603
71993
|
readdirSync as readdirSync15,
|
|
70604
|
-
statSync as
|
|
71994
|
+
statSync as statSync16,
|
|
70605
71995
|
writeFileSync as writeFileSync14
|
|
70606
71996
|
} from "fs";
|
|
70607
|
-
import { dirname as dirname9, join as
|
|
71997
|
+
import { dirname as dirname9, join as join29, normalize as normalize3, relative as relative4, sep as sep2 } from "path";
|
|
70608
71998
|
function resolveSkillsNativeStorageConfig(env3 = process.env) {
|
|
70609
71999
|
assertNoRetiredModeEnvVars(env3, {
|
|
70610
72000
|
app: SKILLS_ENV_NAMESPACE,
|
|
@@ -70645,7 +72035,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
70645
72035
|
local: {
|
|
70646
72036
|
dataDir: getDataDir(),
|
|
70647
72037
|
projectStateDir: getProjectStateDir(targetDir),
|
|
70648
|
-
feedbackDbPath:
|
|
72038
|
+
feedbackDbPath: join29(getDataDir(), "skills.db")
|
|
70649
72039
|
},
|
|
70650
72040
|
remote: {
|
|
70651
72041
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -70667,7 +72057,7 @@ function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
|
70667
72057
|
const files = [];
|
|
70668
72058
|
if (existsSync27(projectStateDir)) {
|
|
70669
72059
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
70670
|
-
const bytes =
|
|
72060
|
+
const bytes = readFileSync23(filePath);
|
|
70671
72061
|
const relativePath = toPosix(relative4(targetDir, filePath));
|
|
70672
72062
|
files.push({
|
|
70673
72063
|
path: relativePath,
|
|
@@ -70726,8 +72116,8 @@ function parsePositiveInteger(value) {
|
|
|
70726
72116
|
function walkFiles2(dir) {
|
|
70727
72117
|
const files = [];
|
|
70728
72118
|
for (const entry of readdirSync15(dir)) {
|
|
70729
|
-
const full =
|
|
70730
|
-
const stats =
|
|
72119
|
+
const full = join29(dir, entry);
|
|
72120
|
+
const stats = statSync16(full);
|
|
70731
72121
|
if (stats.isDirectory())
|
|
70732
72122
|
files.push(...walkFiles2(full));
|
|
70733
72123
|
else
|
|
@@ -71203,26 +72593,26 @@ var require_content_type = __commonJS((exports) => {
|
|
|
71203
72593
|
}
|
|
71204
72594
|
});
|
|
71205
72595
|
|
|
71206
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/mediaType.js
|
|
72596
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/mediaType.js
|
|
71207
72597
|
var import_content_type;
|
|
71208
72598
|
var init_mediaType = __esm(() => {
|
|
71209
72599
|
import_content_type = __toESM(require_content_type(), 1);
|
|
71210
72600
|
});
|
|
71211
72601
|
|
|
71212
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/sseKeepAlive.js
|
|
72602
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/server/sseKeepAlive.js
|
|
71213
72603
|
var MAX_TIMER_DELAY_MS;
|
|
71214
72604
|
var init_sseKeepAlive = __esm(() => {
|
|
71215
72605
|
MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
|
|
71216
72606
|
});
|
|
71217
72607
|
|
|
71218
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.js
|
|
72608
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.js
|
|
71219
72609
|
var init_webStandardStreamableHttp = __esm(() => {
|
|
71220
72610
|
init_mediaType();
|
|
71221
72611
|
init_sseKeepAlive();
|
|
71222
72612
|
init_types2();
|
|
71223
72613
|
});
|
|
71224
72614
|
|
|
71225
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0/node_modules/@modelcontextprotocol/sdk/dist/esm/server/streamableHttp.js
|
|
72615
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.30.0+2b91fc17bf64bdfd/node_modules/@modelcontextprotocol/sdk/dist/esm/server/streamableHttp.js
|
|
71226
72616
|
var init_streamableHttp = __esm(() => {
|
|
71227
72617
|
init_dist();
|
|
71228
72618
|
init_webStandardStreamableHttp();
|
|
@@ -71275,9 +72665,9 @@ var init_mcp2 = __esm(() => {
|
|
|
71275
72665
|
});
|
|
71276
72666
|
|
|
71277
72667
|
// src/cli/commands/runtime-mcp.ts
|
|
71278
|
-
import { existsSync as existsSync28, mkdirSync as mkdirSync14, readFileSync as
|
|
71279
|
-
import { homedir as
|
|
71280
|
-
import { dirname as dirname10, join as
|
|
72668
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync14, readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "fs";
|
|
72669
|
+
import { homedir as homedir7 } from "os";
|
|
72670
|
+
import { dirname as dirname10, join as join30 } from "path";
|
|
71281
72671
|
async function handleMcp(options) {
|
|
71282
72672
|
if (options.register) {
|
|
71283
72673
|
let agents;
|
|
@@ -71320,24 +72710,24 @@ async function registerMcpForAgent(agent, command) {
|
|
|
71320
72710
|
case "codex":
|
|
71321
72711
|
return registerCodexMcp(command);
|
|
71322
72712
|
case "gemini":
|
|
71323
|
-
return registerJsonMcpServer(agent,
|
|
72713
|
+
return registerJsonMcpServer(agent, join30(homedir7(), ".gemini", "settings.json"), "mcpServers", {
|
|
71324
72714
|
command,
|
|
71325
72715
|
args: []
|
|
71326
72716
|
});
|
|
71327
72717
|
case "pi":
|
|
71328
|
-
return registerJsonMcpServer(agent,
|
|
72718
|
+
return registerJsonMcpServer(agent, join30(homedir7(), ".pi", "agent", "mcp.json"), "mcpServers", {
|
|
71329
72719
|
command,
|
|
71330
72720
|
args: []
|
|
71331
72721
|
});
|
|
71332
72722
|
case "opencode":
|
|
71333
72723
|
return registerOpenCodeMcp(command);
|
|
71334
72724
|
case "cursor":
|
|
71335
|
-
return registerJsonMcpServer(agent,
|
|
72725
|
+
return registerJsonMcpServer(agent, join30(homedir7(), ".cursor", "mcp.json"), "mcpServers", {
|
|
71336
72726
|
command,
|
|
71337
72727
|
args: []
|
|
71338
72728
|
});
|
|
71339
72729
|
case "windsurf":
|
|
71340
|
-
return registerJsonMcpServer(agent,
|
|
72730
|
+
return registerJsonMcpServer(agent, join30(homedir7(), ".windsurf", "mcp.json"), "mcpServers", {
|
|
71341
72731
|
command,
|
|
71342
72732
|
args: []
|
|
71343
72733
|
});
|
|
@@ -71360,7 +72750,7 @@ async function registerClaudeMcp(command) {
|
|
|
71360
72750
|
if (exitCode === 0) {
|
|
71361
72751
|
return { agent: "claude", success: true, command: cliCommand };
|
|
71362
72752
|
}
|
|
71363
|
-
const fallback = registerJsonMcpServer("claude",
|
|
72753
|
+
const fallback = registerJsonMcpServer("claude", join30(homedir7(), ".claude", ".mcp.json"), "mcpServers", {
|
|
71364
72754
|
command,
|
|
71365
72755
|
args: []
|
|
71366
72756
|
});
|
|
@@ -71370,7 +72760,7 @@ async function registerClaudeMcp(command) {
|
|
|
71370
72760
|
error: fallback.success ? undefined : `claude exited with ${exitCode}: ${(stderr || stdout).trim()}`
|
|
71371
72761
|
};
|
|
71372
72762
|
} catch (err) {
|
|
71373
|
-
const fallback = registerJsonMcpServer("claude",
|
|
72763
|
+
const fallback = registerJsonMcpServer("claude", join30(homedir7(), ".claude", ".mcp.json"), "mcpServers", {
|
|
71374
72764
|
command,
|
|
71375
72765
|
args: []
|
|
71376
72766
|
});
|
|
@@ -71382,11 +72772,11 @@ async function registerClaudeMcp(command) {
|
|
|
71382
72772
|
}
|
|
71383
72773
|
}
|
|
71384
72774
|
function registerCodexMcp(command) {
|
|
71385
|
-
const path =
|
|
72775
|
+
const path = join30(homedir7(), ".codex", "config.toml");
|
|
71386
72776
|
const config2 = `[mcp_servers.${MCP_SERVER_NAME}]
|
|
71387
72777
|
command = ${JSON.stringify(command)}`;
|
|
71388
72778
|
try {
|
|
71389
|
-
const current = existsSync28(path) ?
|
|
72779
|
+
const current = existsSync28(path) ? readFileSync24(path, "utf-8") : "";
|
|
71390
72780
|
writeTextFile(path, upsertTomlSection(current, `[mcp_servers.${MCP_SERVER_NAME}]`, `command = ${JSON.stringify(command)}`));
|
|
71391
72781
|
return { agent: "codex", success: true, path, config: config2 };
|
|
71392
72782
|
} catch (err) {
|
|
@@ -71394,7 +72784,7 @@ command = ${JSON.stringify(command)}`;
|
|
|
71394
72784
|
}
|
|
71395
72785
|
}
|
|
71396
72786
|
function registerOpenCodeMcp(command) {
|
|
71397
|
-
const path =
|
|
72787
|
+
const path = join30(homedir7(), ".config", "opencode", "opencode.json");
|
|
71398
72788
|
const config2 = JSON.stringify({
|
|
71399
72789
|
$schema: "https://opencode.ai/config.json",
|
|
71400
72790
|
mcp: {
|
|
@@ -71438,7 +72828,7 @@ function registerJsonMcpServer(agent, path, containerKey, server2) {
|
|
|
71438
72828
|
function readJsonObject2(path) {
|
|
71439
72829
|
if (!existsSync28(path))
|
|
71440
72830
|
return {};
|
|
71441
|
-
const raw =
|
|
72831
|
+
const raw = readFileSync24(path, "utf-8").trim();
|
|
71442
72832
|
if (!raw)
|
|
71443
72833
|
return {};
|
|
71444
72834
|
const parsed = JSON.parse(raw);
|
|
@@ -71482,7 +72872,7 @@ function findCommandOnPath(command) {
|
|
|
71482
72872
|
for (const dir of pathValue.split(":")) {
|
|
71483
72873
|
if (!dir)
|
|
71484
72874
|
continue;
|
|
71485
|
-
const candidate =
|
|
72875
|
+
const candidate = join30(dir, command);
|
|
71486
72876
|
if (existsSync28(candidate))
|
|
71487
72877
|
return candidate;
|
|
71488
72878
|
}
|
|
@@ -71503,7 +72893,7 @@ __export(exports_runtime, {
|
|
|
71503
72893
|
registerRuntime: () => registerRuntime
|
|
71504
72894
|
});
|
|
71505
72895
|
import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync16 } from "fs";
|
|
71506
|
-
import { dirname as dirname11, join as
|
|
72896
|
+
import { dirname as dirname11, join as join31 } from "path";
|
|
71507
72897
|
import { createInterface } from "readline";
|
|
71508
72898
|
function registerRuntime(parent) {
|
|
71509
72899
|
parent.command("run").argument("<skill>", "Skill name").argument("[args...]", "Arguments to pass to the skill").allowUnknownOption(true).passThroughOptions(true).option("--json", "Output result as JSON", false).option("--wait", "Poll remote runs until a terminal status", false).option("--poll-interval-ms <ms>", "Remote polling interval in milliseconds", "1000").option("--poll-timeout-ms <ms>", "Maximum time to wait for a remote run", "300000").description("Run a skill directly").action(async (name, args2, options) => handleRun(name, args2, options));
|
|
@@ -71522,7 +72912,7 @@ function registerRuntime(parent) {
|
|
|
71522
72912
|
}
|
|
71523
72913
|
await handleMcp(options);
|
|
71524
72914
|
});
|
|
71525
|
-
const setup = parent.command("setup").description("Point this CLI at a Skills API server, or register agent integrations").option("--api-url <url>", "Skills API origin to send remote work to").option("--global", "
|
|
72915
|
+
const setup = parent.command("setup").description("Point this CLI at a Skills API server, or register agent integrations").option("--api-url <url>", "Skills API origin to send remote work to").option("--global", "Accepted for compatibility; the API origin is always per-user", false).option("--json", "Output setup result as JSON", false).action(async (options) => handleSetup(options));
|
|
71526
72916
|
setup.command("agents").option("--json", "Output registration result as JSON", false).description("Register the Skills MCP server with all supported agents").action(async (options) => handleMcp({ register: "all", json: options.json }));
|
|
71527
72917
|
parent.command("self-update").description("Update @hasna/skills to the latest version").option("--json", "Output result as JSON", false).action(async (options) => {
|
|
71528
72918
|
if (process.env.SKILLS_TEST_MODE === "1") {
|
|
@@ -71568,13 +72958,12 @@ Updating ${name}...
|
|
|
71568
72958
|
});
|
|
71569
72959
|
}
|
|
71570
72960
|
async function handleSetup(options) {
|
|
71571
|
-
const scope = options.global ? "global" : "project";
|
|
71572
72961
|
if (options.apiUrl !== undefined && !options.apiUrl.trim()) {
|
|
71573
|
-
const
|
|
72962
|
+
const error3 = "Invalid value '' for --api-url. Expected an http(s) URL";
|
|
71574
72963
|
if (options.json)
|
|
71575
|
-
console.log(JSON.stringify({ saved: null,
|
|
72964
|
+
console.log(JSON.stringify({ saved: null, error: error3 }, null, 2));
|
|
71576
72965
|
else
|
|
71577
|
-
console.error(source_default.red(
|
|
72966
|
+
console.error(source_default.red(error3));
|
|
71578
72967
|
process.exitCode = 1;
|
|
71579
72968
|
return;
|
|
71580
72969
|
}
|
|
@@ -71583,49 +72972,89 @@ async function handleSetup(options) {
|
|
|
71583
72972
|
requested = (await promptLine("Skills API URL (blank to leave unchanged): ")).trim();
|
|
71584
72973
|
}
|
|
71585
72974
|
let saved = null;
|
|
72975
|
+
let credentialsFile = null;
|
|
71586
72976
|
if (requested) {
|
|
71587
72977
|
try {
|
|
71588
|
-
|
|
71589
|
-
|
|
72978
|
+
const normalized = normalizeSkillsApiOrigin(requireHttpUrl(requested));
|
|
72979
|
+
credentialsFile = saveApiUrl(normalized);
|
|
72980
|
+
saved = normalized;
|
|
71590
72981
|
} catch (err) {
|
|
71591
|
-
const
|
|
72982
|
+
const error3 = err.message;
|
|
71592
72983
|
if (options.json)
|
|
71593
|
-
console.log(JSON.stringify({ saved: null, requested,
|
|
72984
|
+
console.log(JSON.stringify({ saved: null, requested, error: error3 }, null, 2));
|
|
71594
72985
|
else
|
|
71595
|
-
console.error(source_default.red(
|
|
72986
|
+
console.error(source_default.red(error3));
|
|
71596
72987
|
process.exitCode = 1;
|
|
71597
72988
|
return;
|
|
71598
72989
|
}
|
|
71599
72990
|
}
|
|
71600
|
-
|
|
71601
|
-
|
|
71602
|
-
|
|
72991
|
+
let configured = null;
|
|
72992
|
+
let source = null;
|
|
72993
|
+
let authenticated = false;
|
|
72994
|
+
let error2 = null;
|
|
72995
|
+
try {
|
|
72996
|
+
const fleet = resolveSkillsFleet();
|
|
72997
|
+
if (fleet.mode === "hosted") {
|
|
72998
|
+
configured = fleet.apiOrigin;
|
|
72999
|
+
source = fleet.apiUrlSource;
|
|
73000
|
+
authenticated = true;
|
|
73001
|
+
}
|
|
73002
|
+
} catch (err) {
|
|
73003
|
+
error2 = err.message;
|
|
73004
|
+
configured = saved;
|
|
73005
|
+
}
|
|
73006
|
+
const next = error2 ? ["skills auth login"] : configured ? ["skills auth login", "skills list --remote"] : ["skills list", "skills run <skill>"];
|
|
71603
73007
|
const payload = {
|
|
71604
73008
|
apiUrl: configured,
|
|
73009
|
+
source,
|
|
71605
73010
|
saved,
|
|
71606
|
-
|
|
71607
|
-
|
|
73011
|
+
credentialsFile,
|
|
73012
|
+
authenticated,
|
|
73013
|
+
...error2 ? { error: error2 } : {},
|
|
73014
|
+
config: loadConfig(),
|
|
71608
73015
|
next
|
|
71609
73016
|
};
|
|
71610
73017
|
if (options.json) {
|
|
71611
73018
|
console.log(JSON.stringify(payload, null, 2));
|
|
73019
|
+
if (error2)
|
|
73020
|
+
process.exitCode = 1;
|
|
73021
|
+
return;
|
|
73022
|
+
}
|
|
73023
|
+
if (error2) {
|
|
73024
|
+
if (saved)
|
|
73025
|
+
console.log(source_default.green(`Skills API set to ${saved}`));
|
|
73026
|
+
console.error(source_default.red(error2));
|
|
73027
|
+
process.exitCode = 1;
|
|
71612
73028
|
return;
|
|
71613
73029
|
}
|
|
71614
73030
|
if (saved) {
|
|
71615
73031
|
console.log(source_default.green(`Skills API set to ${saved}`));
|
|
71616
|
-
|
|
73032
|
+
if (credentialsFile)
|
|
73033
|
+
console.log(source_default.dim(` Saved in: ${credentialsFile}`));
|
|
71617
73034
|
console.log(source_default.dim(" Next: skills auth login"));
|
|
71618
73035
|
} else if (configured) {
|
|
71619
73036
|
console.log(source_default.green(`Skills API already configured: ${configured}`));
|
|
73037
|
+
console.log(source_default.dim(` Source: ${source}`));
|
|
71620
73038
|
console.log(source_default.dim(" Change it with: skills setup --api-url <url>"));
|
|
71621
|
-
console.log(source_default.dim(
|
|
71622
|
-
console.log(source_default.dim(" Next: skills auth login"));
|
|
73039
|
+
console.log(source_default.dim(` Clear it with: skills config unset apiUrl (or unset ${SKILLS_API_URL_ENV})`));
|
|
71623
73040
|
} else {
|
|
71624
73041
|
console.log(source_default.green("No Skills API configured; skills run on this machine."));
|
|
71625
73042
|
console.log(source_default.dim(" Point at a server with: skills setup --api-url <url>"));
|
|
71626
73043
|
console.log(source_default.dim(" Next: skills list"));
|
|
71627
73044
|
}
|
|
71628
73045
|
}
|
|
73046
|
+
function requireHttpUrl(value) {
|
|
73047
|
+
let url;
|
|
73048
|
+
try {
|
|
73049
|
+
url = new URL(value);
|
|
73050
|
+
} catch {
|
|
73051
|
+
throw new Error(`Invalid value '${value}' for --api-url. Expected an http(s) URL`);
|
|
73052
|
+
}
|
|
73053
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
73054
|
+
throw new Error(`Invalid value '${value}' for --api-url. Expected an http(s) URL`);
|
|
73055
|
+
}
|
|
73056
|
+
return value.replace(/\/+$/, "");
|
|
73057
|
+
}
|
|
71629
73058
|
function promptLine(question) {
|
|
71630
73059
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
71631
73060
|
return new Promise((resolve2) => {
|
|
@@ -71657,7 +73086,7 @@ async function handleRun(name, args2, options) {
|
|
|
71657
73086
|
return;
|
|
71658
73087
|
}
|
|
71659
73088
|
}
|
|
71660
|
-
const routing = resolveConfiguredRunRouting(skill);
|
|
73089
|
+
const routing = await resolveConfiguredRunRouting(skill);
|
|
71661
73090
|
const runContext = createSkillRun({
|
|
71662
73091
|
skill: skill.name,
|
|
71663
73092
|
args: args2,
|
|
@@ -71853,10 +73282,10 @@ ${run.id}
|
|
|
71853
73282
|
}
|
|
71854
73283
|
}
|
|
71855
73284
|
async function handleRunsStatus(runId, options) {
|
|
71856
|
-
const {
|
|
71857
|
-
const apiKey =
|
|
73285
|
+
const { skillsCredentialOrReason: skillsCredentialOrReason2 } = await Promise.resolve().then(() => (init_fleet_credentials(), exports_fleet_credentials));
|
|
73286
|
+
const { apiKey, reason } = await skillsCredentialOrReason2();
|
|
71858
73287
|
if (!apiKey) {
|
|
71859
|
-
const error2 = "Remote run status requires API access. Run: skills auth login";
|
|
73288
|
+
const error2 = reason ?? "Remote run status requires API access. Run: skills auth login";
|
|
71860
73289
|
if (options.json)
|
|
71861
73290
|
console.log(JSON.stringify({ contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION, error: error2 }, null, 2));
|
|
71862
73291
|
else
|
|
@@ -71954,10 +73383,10 @@ async function handleExportsOpen(runId, options) {
|
|
|
71954
73383
|
} catch {}
|
|
71955
73384
|
}
|
|
71956
73385
|
async function handleExportsDownload(runId, options) {
|
|
71957
|
-
const {
|
|
71958
|
-
const apiKey =
|
|
73386
|
+
const { skillsCredentialOrReason: skillsCredentialOrReason2 } = await Promise.resolve().then(() => (init_fleet_credentials(), exports_fleet_credentials));
|
|
73387
|
+
const { apiKey, reason } = await skillsCredentialOrReason2();
|
|
71959
73388
|
if (!apiKey) {
|
|
71960
|
-
const error2 = "Remote artifact downloads require API access. Run: skills auth login";
|
|
73389
|
+
const error2 = reason ?? "Remote artifact downloads require API access. Run: skills auth login";
|
|
71961
73390
|
if (options.json)
|
|
71962
73391
|
console.log(JSON.stringify({ error: error2 }, null, 2));
|
|
71963
73392
|
else
|
|
@@ -71992,7 +73421,7 @@ async function handleExportsDownload(runId, options) {
|
|
|
71992
73421
|
if (!response.ok)
|
|
71993
73422
|
throw new Error(`download failed for artifact ${artifactId}: ${response.status}`);
|
|
71994
73423
|
const relativePath = safeArtifactRelativePath(typeof artifact.relativePath === "string" ? artifact.relativePath : artifact.fileName, String(artifact.fileName || artifactId));
|
|
71995
|
-
const outputPath =
|
|
73424
|
+
const outputPath = join31(exportDir, relativePath);
|
|
71996
73425
|
mkdirSync15(dirname11(outputPath), { recursive: true });
|
|
71997
73426
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
71998
73427
|
writeFileSync16(outputPath, bytes);
|
|
@@ -72208,6 +73637,8 @@ var init_runtime = __esm(() => {
|
|
|
72208
73637
|
init_skillinfo();
|
|
72209
73638
|
init_blog_article();
|
|
72210
73639
|
init_config();
|
|
73640
|
+
init_auth_store();
|
|
73641
|
+
init_fleet_credentials();
|
|
72211
73642
|
init_runs();
|
|
72212
73643
|
init_run_state();
|
|
72213
73644
|
init_runtime_mcp();
|
|
@@ -72354,9 +73785,9 @@ var init_completion = __esm(() => {
|
|
|
72354
73785
|
});
|
|
72355
73786
|
|
|
72356
73787
|
// src/lib/portable-snapshot-filter.ts
|
|
72357
|
-
import { readdirSync as readdirSync16, statSync as
|
|
72358
|
-
import { homedir as
|
|
72359
|
-
import { join as
|
|
73788
|
+
import { readdirSync as readdirSync16, statSync as statSync17 } from "fs";
|
|
73789
|
+
import { homedir as homedir8 } from "os";
|
|
73790
|
+
import { join as join32, sep as sep3 } from "path";
|
|
72360
73791
|
function isExcludedSkillFileName(fileName) {
|
|
72361
73792
|
if (EXCLUDE_FILE_NAMES.has(fileName)) {
|
|
72362
73793
|
return true;
|
|
@@ -72377,18 +73808,18 @@ function isPortableWithinSkill(relativeParts) {
|
|
|
72377
73808
|
return PORTABLE_SUBDIRS.has(second);
|
|
72378
73809
|
}
|
|
72379
73810
|
function homePathFor(definition, homesRoot) {
|
|
72380
|
-
const home = homesRoot ??
|
|
73811
|
+
const home = homesRoot ?? homedir8();
|
|
72381
73812
|
if (definition.subClass === "skills" || definition.subClass === "custom") {
|
|
72382
|
-
return
|
|
73813
|
+
return join32(skillsDataRootForHome(home), definition.name);
|
|
72383
73814
|
}
|
|
72384
73815
|
if (definition.agent === "opencode") {
|
|
72385
|
-
return
|
|
73816
|
+
return join32(home, ".config", "opencode", "skills");
|
|
72386
73817
|
}
|
|
72387
|
-
return
|
|
73818
|
+
return join32(home, `.${definition.agent}`, "skills");
|
|
72388
73819
|
}
|
|
72389
73820
|
function destinationFor(definition, stationId, relativePath) {
|
|
72390
|
-
const category = definition.subClass === "agent-homes" ?
|
|
72391
|
-
return
|
|
73821
|
+
const category = definition.subClass === "agent-homes" ? join32("agent-homes", definition.agent ?? "") : definition.name;
|
|
73822
|
+
return join32("resources", stationId, "skills", category, ...relativePath.split(sep3));
|
|
72392
73823
|
}
|
|
72393
73824
|
function walkEntries(absoluteRoot) {
|
|
72394
73825
|
let entries;
|
|
@@ -72399,7 +73830,7 @@ function walkEntries(absoluteRoot) {
|
|
|
72399
73830
|
}
|
|
72400
73831
|
const output = [];
|
|
72401
73832
|
for (const entry of entries) {
|
|
72402
|
-
const childFull =
|
|
73833
|
+
const childFull = join32(absoluteRoot, entry.name);
|
|
72403
73834
|
if (entry.isSymbolicLink()) {
|
|
72404
73835
|
output.push({ kind: "symlink", relativePath: entry.name, fullPath: childFull });
|
|
72405
73836
|
continue;
|
|
@@ -72410,7 +73841,7 @@ function walkEntries(absoluteRoot) {
|
|
|
72410
73841
|
}
|
|
72411
73842
|
const nested = walkEntries(childFull);
|
|
72412
73843
|
for (const item of nested) {
|
|
72413
|
-
output.push({ ...item, relativePath:
|
|
73844
|
+
output.push({ ...item, relativePath: join32(entry.name, item.relativePath) });
|
|
72414
73845
|
}
|
|
72415
73846
|
continue;
|
|
72416
73847
|
}
|
|
@@ -72422,7 +73853,7 @@ function walkEntries(absoluteRoot) {
|
|
|
72422
73853
|
}
|
|
72423
73854
|
function isRegularFile(filePath) {
|
|
72424
73855
|
try {
|
|
72425
|
-
return
|
|
73856
|
+
return statSync17(filePath).isFile();
|
|
72426
73857
|
} catch {
|
|
72427
73858
|
return false;
|
|
72428
73859
|
}
|
|
@@ -72507,18 +73938,18 @@ import { createHash as createHash6 } from "crypto";
|
|
|
72507
73938
|
import {
|
|
72508
73939
|
copyFileSync as copyFileSync2,
|
|
72509
73940
|
mkdirSync as mkdirSync16,
|
|
72510
|
-
readFileSync as
|
|
72511
|
-
statSync as
|
|
73941
|
+
readFileSync as readFileSync25,
|
|
73942
|
+
statSync as statSync18,
|
|
72512
73943
|
writeFileSync as writeFileSync17
|
|
72513
73944
|
} from "fs";
|
|
72514
|
-
import { dirname as dirname12, isAbsolute as
|
|
73945
|
+
import { dirname as dirname12, isAbsolute as isAbsolute4, relative as relative5, resolve as resolve2, sep as sep4 } from "path";
|
|
72515
73946
|
function validateStationId(stationId) {
|
|
72516
73947
|
if (!/^[a-z0-9-]+$/.test(stationId)) {
|
|
72517
73948
|
throw new StationSnapshotError("INVALID_STATION", `station id must be a slug, got: ${stationId}`);
|
|
72518
73949
|
}
|
|
72519
73950
|
}
|
|
72520
73951
|
function sha256File(filePath) {
|
|
72521
|
-
return createHash6("sha256").update(
|
|
73952
|
+
return createHash6("sha256").update(readFileSync25(filePath)).digest("hex");
|
|
72522
73953
|
}
|
|
72523
73954
|
function scanHome(definition, homesRoot) {
|
|
72524
73955
|
const homePath = homePathFor(definition, homesRoot);
|
|
@@ -72548,7 +73979,7 @@ function scanHome(definition, homesRoot) {
|
|
|
72548
73979
|
skipped.push({ relativePath: entry.relativePath, reason: "not-regular-file" });
|
|
72549
73980
|
continue;
|
|
72550
73981
|
}
|
|
72551
|
-
const info =
|
|
73982
|
+
const info = statSync18(entry.fullPath);
|
|
72552
73983
|
portable.push({
|
|
72553
73984
|
relativePath: entry.relativePath,
|
|
72554
73985
|
fullPath: entry.fullPath,
|
|
@@ -72617,7 +74048,7 @@ function writeStationSnapshot(options) {
|
|
|
72617
74048
|
for (const plan of plans) {
|
|
72618
74049
|
const destination = resolve2(repoRoot, plan.destination);
|
|
72619
74050
|
const destinationRelative = relative5(repoRoot, destination);
|
|
72620
|
-
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep4) ||
|
|
74051
|
+
if (destinationRelative.startsWith("..") || destinationRelative.startsWith(sep4) || isAbsolute4(destinationRelative)) {
|
|
72621
74052
|
throw new StationSnapshotError("DESTINATION_ESCAPE", `destination escapes repo root: ${plan.destination}`);
|
|
72622
74053
|
}
|
|
72623
74054
|
let existingDigest = null;
|
|
@@ -72691,7 +74122,7 @@ __export(exports_create_sync_config, {
|
|
|
72691
74122
|
registerCreateSync: () => registerCreateSync
|
|
72692
74123
|
});
|
|
72693
74124
|
import { existsSync as existsSync29, writeFileSync as writeFileSync18, mkdirSync as mkdirSync17 } from "fs";
|
|
72694
|
-
import { join as
|
|
74125
|
+
import { join as join33 } from "path";
|
|
72695
74126
|
function registerCreateSync(parent) {
|
|
72696
74127
|
const configCmd = parent.command("config").description("Manage skills configuration");
|
|
72697
74128
|
configCmd.command("show", { isDefault: true }).option("--json", "Output as JSON", false).description("Show current merged configuration").action((options) => {
|
|
@@ -72728,6 +74159,20 @@ function registerCreateSync(parent) {
|
|
|
72728
74159
|
configCmd.command("unset <key>").option("--global", "Remove from the global config instead of the project config", false).option("--json", "Output as JSON", false).description("Remove a configuration value").action((key, options) => {
|
|
72729
74160
|
const scope = options.global ? "global" : "project";
|
|
72730
74161
|
try {
|
|
74162
|
+
if (key === "apiUrl") {
|
|
74163
|
+
const hadStored = Boolean(readStoredApiUrl());
|
|
74164
|
+
if (hadStored)
|
|
74165
|
+
saveApiUrl(null);
|
|
74166
|
+
const removedStaleKey = unsetConfig(key, scope);
|
|
74167
|
+
const removed2 = hadStored || removedStaleKey;
|
|
74168
|
+
if (options.json)
|
|
74169
|
+
console.log(JSON.stringify({ key, removed: removed2, scope, path: getConfigPath(scope) }));
|
|
74170
|
+
else if (removed2)
|
|
74171
|
+
console.log(source_default.green(`Unset ${key}`));
|
|
74172
|
+
else
|
|
74173
|
+
console.log(source_default.dim(`${key} was not set`));
|
|
74174
|
+
return;
|
|
74175
|
+
}
|
|
72731
74176
|
const removed = unsetConfig(key, scope);
|
|
72732
74177
|
if (options.json)
|
|
72733
74178
|
console.log(JSON.stringify({ key, removed, scope, path: getConfigPath(scope) }));
|
|
@@ -72772,7 +74217,7 @@ function handleCreate(name, options) {
|
|
|
72772
74217
|
const bare = name.trim();
|
|
72773
74218
|
const dirName = bare;
|
|
72774
74219
|
const baseDir = getPortableSkillsRoot();
|
|
72775
|
-
const skillDir =
|
|
74220
|
+
const skillDir = join33(baseDir, dirName);
|
|
72776
74221
|
if (existsSync29(skillDir)) {
|
|
72777
74222
|
console.log(options.json ? JSON.stringify({ error: `Skill '${bare}' already exists at ${skillDir}` }) : source_default.red(`Skill '${bare}' already exists at ${skillDir}`));
|
|
72778
74223
|
process.exitCode = 1;
|
|
@@ -72781,8 +74226,8 @@ function handleCreate(name, options) {
|
|
|
72781
74226
|
const description = options.description || `${bare} skill`;
|
|
72782
74227
|
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()).filter(Boolean) : [bare];
|
|
72783
74228
|
const displayName2 = bare.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
72784
|
-
mkdirSync17(
|
|
72785
|
-
writeFileSync18(
|
|
74229
|
+
mkdirSync17(join33(skillDir, "src"), { recursive: true });
|
|
74230
|
+
writeFileSync18(join33(skillDir, "SKILL.md"), [
|
|
72786
74231
|
"---",
|
|
72787
74232
|
`name: ${bare}`,
|
|
72788
74233
|
`description: ${description}`,
|
|
@@ -72802,11 +74247,11 @@ function handleCreate(name, options) {
|
|
|
72802
74247
|
""
|
|
72803
74248
|
].join(`
|
|
72804
74249
|
`));
|
|
72805
|
-
writeFileSync18(
|
|
74250
|
+
writeFileSync18(join33(skillDir, "src", "index.ts"), [`#!/usr/bin/env bun`, `/**`, ` * ${displayName2} \u2014 ${description}`, ` */`, "", `console.log("${displayName2}");`, ""].join(`
|
|
72806
74251
|
`));
|
|
72807
|
-
writeFileSync18(
|
|
74252
|
+
writeFileSync18(join33(skillDir, "package.json"), JSON.stringify({ name: bare, version: "0.1.0", description, bin: { [bare]: "./src/index.ts" }, scripts: { dev: `bun src/index.ts` }, dependencies: {} }, null, 2) + `
|
|
72808
74253
|
`);
|
|
72809
|
-
writeFileSync18(
|
|
74254
|
+
writeFileSync18(join33(skillDir, "tsconfig.json"), JSON.stringify({ compilerOptions: { target: "ES2022", module: "ESNext", moduleResolution: "bundler", strict: true, outDir: "dist" }, include: ["src/**/*.ts"] }, null, 2) + `
|
|
72810
74255
|
`);
|
|
72811
74256
|
clearRegistryCache();
|
|
72812
74257
|
if (options.json)
|
|
@@ -72815,8 +74260,8 @@ function handleCreate(name, options) {
|
|
|
72815
74260
|
console.log(source_default.green(`\u2713 Created custom skill '${bare}' at ${skillDir}`));
|
|
72816
74261
|
console.log(source_default.dim(` Category: ${options.category}`));
|
|
72817
74262
|
console.log(source_default.dim(` Tags: ${tags.join(", ")}`));
|
|
72818
|
-
console.log(` ${source_default.cyan("Edit:")} ${
|
|
72819
|
-
console.log(` ${source_default.cyan("Run:")} bun ${
|
|
74263
|
+
console.log(` ${source_default.cyan("Edit:")} ${join33(skillDir, "src", "index.ts")}`);
|
|
74264
|
+
console.log(` ${source_default.cyan("Run:")} bun ${join33(skillDir, "src", "index.ts")}`);
|
|
72820
74265
|
}
|
|
72821
74266
|
}
|
|
72822
74267
|
function handleSync(names, options) {
|
|
@@ -73052,6 +74497,7 @@ ${written}/${actions.length} ${dryRun ? "would be written" : "written"}`));
|
|
|
73052
74497
|
var init_create_sync_config = __esm(() => {
|
|
73053
74498
|
init_source();
|
|
73054
74499
|
init_config();
|
|
74500
|
+
init_auth_store();
|
|
73055
74501
|
init_portable_skills();
|
|
73056
74502
|
init_registry();
|
|
73057
74503
|
init_agent_sync();
|
|
@@ -73066,23 +74512,23 @@ import {
|
|
|
73066
74512
|
copyFileSync as copyFileSync3,
|
|
73067
74513
|
mkdirSync as mkdirSync18,
|
|
73068
74514
|
readdirSync as readdirSync17,
|
|
73069
|
-
readFileSync as
|
|
73070
|
-
statSync as
|
|
74515
|
+
readFileSync as readFileSync26,
|
|
74516
|
+
statSync as statSync19,
|
|
73071
74517
|
writeFileSync as writeFileSync19
|
|
73072
74518
|
} from "fs";
|
|
73073
|
-
import { dirname as dirname13, join as
|
|
74519
|
+
import { dirname as dirname13, join as join34, resolve as resolve3, sep as sep5 } from "path";
|
|
73074
74520
|
function fail2(code, message, detail = []) {
|
|
73075
74521
|
throw new StationSnapshotError(code, message, detail);
|
|
73076
74522
|
}
|
|
73077
74523
|
function snapshotRootFor(repoRoot, stationId) {
|
|
73078
|
-
return
|
|
74524
|
+
return join34(repoRoot, "resources", stationId, "skills");
|
|
73079
74525
|
}
|
|
73080
74526
|
function readSnapshotManifest(repoRoot, stationId) {
|
|
73081
74527
|
const snapshotRoot = snapshotRootFor(repoRoot, stationId);
|
|
73082
|
-
const manifestPath =
|
|
74528
|
+
const manifestPath = join34(snapshotRoot, "sync-manifest.json");
|
|
73083
74529
|
let manifest;
|
|
73084
74530
|
try {
|
|
73085
|
-
manifest = JSON.parse(
|
|
74531
|
+
manifest = JSON.parse(readFileSync26(manifestPath, "utf8"));
|
|
73086
74532
|
} catch (error2) {
|
|
73087
74533
|
fail2("MANIFEST_UNREADABLE", `cannot read snapshot manifest: ${manifestPath}: ${error2.message}`);
|
|
73088
74534
|
}
|
|
@@ -73106,7 +74552,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
73106
74552
|
const hashMismatches = [];
|
|
73107
74553
|
const skippedByRule = [];
|
|
73108
74554
|
for (const agent of SYNC_AGENTS) {
|
|
73109
|
-
const agentRoot =
|
|
74555
|
+
const agentRoot = join34(snapshotRoot, "agent-homes", agent);
|
|
73110
74556
|
let identEntries;
|
|
73111
74557
|
try {
|
|
73112
74558
|
identEntries = readdirSync17(agentRoot, { withFileTypes: true });
|
|
@@ -73117,7 +74563,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
73117
74563
|
if (!identEntry.isDirectory() || identEntry.name.startsWith(".")) {
|
|
73118
74564
|
continue;
|
|
73119
74565
|
}
|
|
73120
|
-
const identRoot =
|
|
74566
|
+
const identRoot = join34(agentRoot, identEntry.name);
|
|
73121
74567
|
const entries = walkEntries(identRoot);
|
|
73122
74568
|
for (const entry of entries) {
|
|
73123
74569
|
const relativeParts = [identEntry.name, ...entry.relativePath.split(sep5)];
|
|
@@ -73164,7 +74610,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
73164
74610
|
});
|
|
73165
74611
|
continue;
|
|
73166
74612
|
}
|
|
73167
|
-
const info =
|
|
74613
|
+
const info = statSync19(entry.fullPath);
|
|
73168
74614
|
const manifestHash = manifestHashes.get(`${agent}${MANIFEST_HASH_KEY_SEP}${homeRelative}`) ?? null;
|
|
73169
74615
|
let verified = false;
|
|
73170
74616
|
if (manifestHash !== null) {
|
|
@@ -73218,7 +74664,7 @@ function planStationHydration(stationId, repoRoot) {
|
|
|
73218
74664
|
for (const copy of copies) {
|
|
73219
74665
|
let isStub = false;
|
|
73220
74666
|
try {
|
|
73221
|
-
isStub = isPointerSkillMd(
|
|
74667
|
+
isStub = isPointerSkillMd(readFileSync26(copy.fullPath, "utf8"));
|
|
73222
74668
|
} catch {
|
|
73223
74669
|
isStub = false;
|
|
73224
74670
|
}
|
|
@@ -73302,7 +74748,7 @@ function writeStationHydration(options) {
|
|
|
73302
74748
|
const toWrite = [];
|
|
73303
74749
|
for (const skill of plan.winners) {
|
|
73304
74750
|
for (const file of skill.files) {
|
|
73305
|
-
const destination =
|
|
74751
|
+
const destination = join34(cacheRoot, skill.ident, file.withinIdent);
|
|
73306
74752
|
const digest = sha256File(file.winner.fullPath);
|
|
73307
74753
|
let existingDigest = null;
|
|
73308
74754
|
try {
|
|
@@ -73344,7 +74790,7 @@ function writeStationHydration(options) {
|
|
|
73344
74790
|
},
|
|
73345
74791
|
skills: resultSkills
|
|
73346
74792
|
};
|
|
73347
|
-
const hydrationManifestPath =
|
|
74793
|
+
const hydrationManifestPath = join34(dirname13(cacheRoot), `hydration-${options.stationId}.json`);
|
|
73348
74794
|
mkdirSync18(dirname13(hydrationManifestPath), { recursive: true });
|
|
73349
74795
|
writeFileSync19(hydrationManifestPath, `${JSON.stringify(hydration, null, 2)}
|
|
73350
74796
|
`);
|
|
@@ -73706,7 +75152,7 @@ async function executeScheduledSkill(skillName, args2, options) {
|
|
|
73706
75152
|
if (!skill)
|
|
73707
75153
|
throw new Error(`Skill '${skillName}' not found`);
|
|
73708
75154
|
const { resolveConfiguredRunRouting: resolveConfiguredRunRouting2 } = await Promise.resolve().then(() => (init_run_routing(), exports_run_routing));
|
|
73709
|
-
const routing = resolveConfiguredRunRouting2(skill);
|
|
75155
|
+
const routing = await resolveConfiguredRunRouting2(skill);
|
|
73710
75156
|
if (routing.route === "error") {
|
|
73711
75157
|
throw new Error(`${routing.code}: ${routing.error}`);
|
|
73712
75158
|
}
|
|
@@ -73925,7 +75371,7 @@ ${ok}/${results.length} pulled into ~/.hasna/skills/installed`));
|
|
|
73925
75371
|
}
|
|
73926
75372
|
function registerVersions(parent) {
|
|
73927
75373
|
parent.command("versions").argument("<name>", "Skill name on the configured instance").option("--json", "Output as JSON", false).description("List the published versions of a skill on the configured instance").action(async (name, options) => {
|
|
73928
|
-
const client = createRemoteSkillsClient();
|
|
75374
|
+
const client = await createRemoteSkillsClient();
|
|
73929
75375
|
if (!client) {
|
|
73930
75376
|
const message = "No API key configured, so there is no instance to list versions from.";
|
|
73931
75377
|
if (options.json)
|
|
@@ -73972,6 +75418,7 @@ var init_registry2 = __esm(() => {
|
|
|
73972
75418
|
// src/cli/commands/publish.ts
|
|
73973
75419
|
var exports_publish = {};
|
|
73974
75420
|
__export(exports_publish, {
|
|
75421
|
+
sanitizeGitRemote: () => sanitizeGitRemote,
|
|
73975
75422
|
registerPublish: () => registerPublish,
|
|
73976
75423
|
pushSkill: () => pushSkill,
|
|
73977
75424
|
bumpPatch: () => bumpPatch,
|
|
@@ -73979,9 +75426,9 @@ __export(exports_publish, {
|
|
|
73979
75426
|
PushSkillError: () => PushSkillError
|
|
73980
75427
|
});
|
|
73981
75428
|
import { execFileSync } from "child_process";
|
|
73982
|
-
import { existsSync as existsSync30, readFileSync as
|
|
75429
|
+
import { existsSync as existsSync30, readFileSync as readFileSync27 } from "fs";
|
|
73983
75430
|
import { hostname as hostname2 } from "os";
|
|
73984
|
-
import { join as
|
|
75431
|
+
import { join as join35 } from "path";
|
|
73985
75432
|
function registerPublish(parent) {
|
|
73986
75433
|
parent.command("push").argument("<name>", "Name of a skill in the local corpus (~/.hasna/skills/installed or the migrated ~/.hasna/skills/skills)").option("--version <version>", "Override the version recorded on the instance").option("--force-new-version", "If name@version already exists with different content, publish as the next patch version", false).option("--dry-run", "Pack and validate without uploading", false).option("--json", "Output result as JSON", false).description("Publish a local skill to the configured skills instance").action(async (name, options) => {
|
|
73987
75434
|
try {
|
|
@@ -74022,10 +75469,14 @@ async function pushSkill(name, options = {}) {
|
|
|
74022
75469
|
throw new PushSkillError(`Skill '${skill.name}' is not valid and was not published.`, validation.issues.map((issue2) => `${issue2.code}: ${issue2.message}`));
|
|
74023
75470
|
}
|
|
74024
75471
|
const manifest = readPortableSkillManifest(skill.path, skill.name);
|
|
75472
|
+
const declaredVersion = options.version ?? readDeclaredSkillVersion(skill.path);
|
|
75473
|
+
if (!declaredVersion) {
|
|
75474
|
+
throw new PushSkillError(`Skill '${skill.name}' declares no version, so it was not published as an invented '0.1.0'.`, ["Declare a version in skill.json (or the SKILL.md frontmatter / package.json), or pass --version <version> to this push."]);
|
|
75475
|
+
}
|
|
74025
75476
|
const packed = packSkillBundle(skill.path, { maxUnpackedBytes: MAX_UNPACKED_BYTES });
|
|
74026
75477
|
const versionManifest = buildVersionManifest(skill.path, packed);
|
|
74027
|
-
const skillMdPath =
|
|
74028
|
-
const skillMd = existsSync30(skillMdPath) ?
|
|
75478
|
+
const skillMdPath = join35(skill.path, "SKILL.md");
|
|
75479
|
+
const skillMd = existsSync30(skillMdPath) ? readFileSync27(skillMdPath, "utf-8") : undefined;
|
|
74029
75480
|
const base2 = {
|
|
74030
75481
|
slug: skill.name,
|
|
74031
75482
|
path: skill.path,
|
|
@@ -74040,13 +75491,13 @@ async function pushSkill(name, options = {}) {
|
|
|
74040
75491
|
};
|
|
74041
75492
|
if (options.dryRun)
|
|
74042
75493
|
return base2;
|
|
74043
|
-
const client = options.client !== undefined ? options.client : createRemoteSkillsClient();
|
|
75494
|
+
const client = options.client !== undefined ? options.client : await createRemoteSkillsClient();
|
|
74044
75495
|
if (!client) {
|
|
74045
|
-
throw new PushSkillError("No API key configured, so there is nowhere to publish to.", ["Run `skills login`, or set
|
|
75496
|
+
throw new PushSkillError("No API key configured, so there is nowhere to publish to.", ["Run `skills auth login`, or set HASNA_SKILLS_API_KEY (and HASNA_SKILLS_API_URL for your own instance)."]);
|
|
74046
75497
|
}
|
|
74047
75498
|
const current = await client.getSkill(skill.name);
|
|
74048
75499
|
const ifMatch = current && typeof current.revisionId === "string" && current.revisionId ? current.revisionId : undefined;
|
|
74049
|
-
let version2 = options.version ?? manifest.version
|
|
75500
|
+
let version2 = options.version ?? manifest.version;
|
|
74050
75501
|
let response = await publishOnce(client, skill, manifest, packed, skillMd, versionManifest, version2, ifMatch);
|
|
74051
75502
|
let payload = await readBody(response);
|
|
74052
75503
|
if (response.status === 409 && codeOf(payload) === "SKILL_VERSION_EXISTS") {
|
|
@@ -74059,6 +75510,9 @@ async function pushSkill(name, options = {}) {
|
|
|
74059
75510
|
version2 = bumpPatch(version2);
|
|
74060
75511
|
response = await publishOnce(client, skill, manifest, packed, skillMd, versionManifest, version2, ifMatch);
|
|
74061
75512
|
payload = await readBody(response);
|
|
75513
|
+
if (response.status === 409 && codeOf(payload) === "SKILL_VERSION_EXISTS") {
|
|
75514
|
+
throw new PushSkillError(`Publishing '${skill.name}@${version2}' failed: even the bumped version already exists on the instance with different content.`, ["code: SKILL_VERSION_EXISTS", "Pick an explicit --version <new> that is free, and push again."]);
|
|
75515
|
+
}
|
|
74062
75516
|
}
|
|
74063
75517
|
if (!response.ok) {
|
|
74064
75518
|
const code = codeOf(payload);
|
|
@@ -74070,7 +75524,15 @@ async function pushSkill(name, options = {}) {
|
|
|
74070
75524
|
}
|
|
74071
75525
|
throw new PushSkillError(`Publishing '${skill.name}' failed: ${response.status} ${describeError(payload)}`, code ? [`code: ${code}`] : undefined);
|
|
74072
75526
|
}
|
|
74073
|
-
|
|
75527
|
+
const alreadyPublished = isAlreadyPublishedPayload(payload);
|
|
75528
|
+
return {
|
|
75529
|
+
...base2,
|
|
75530
|
+
published: true,
|
|
75531
|
+
status: response.status,
|
|
75532
|
+
response: payload,
|
|
75533
|
+
version: version2,
|
|
75534
|
+
...alreadyPublished ? { alreadyPublished: true } : {}
|
|
75535
|
+
};
|
|
74074
75536
|
}
|
|
74075
75537
|
async function publishOnce(client, skill, manifest, packed, skillMd, versionManifest, version2, ifMatch) {
|
|
74076
75538
|
return client.publishSkill({
|
|
@@ -74091,6 +75553,9 @@ async function publishOnce(client, skill, manifest, packed, skillMd, versionMani
|
|
|
74091
75553
|
function codeOf(payload) {
|
|
74092
75554
|
return typeof payload === "object" && payload && "code" in payload ? String(payload.code) : undefined;
|
|
74093
75555
|
}
|
|
75556
|
+
function isAlreadyPublishedPayload(payload) {
|
|
75557
|
+
return typeof payload === "object" && payload !== null && payload.alreadyPublished === true;
|
|
75558
|
+
}
|
|
74094
75559
|
function bumpPatch(version2) {
|
|
74095
75560
|
const match = /^(\d+)\.(\d+)\.(\d+)(.*)$/.exec(version2);
|
|
74096
75561
|
if (!match)
|
|
@@ -74108,12 +75573,28 @@ function buildVersionManifest(skillDir, packed) {
|
|
|
74108
75573
|
machine: hostname2(),
|
|
74109
75574
|
agent: process.env.SKILLS_AGENT_ID ?? process.env.HASNA_AGENT_ID ?? process.env.AGENT_ID ?? null,
|
|
74110
75575
|
cliVersion: package_default.version,
|
|
74111
|
-
gitRemote: gitValue(skillDir, ["remote", "get-url", "origin"]),
|
|
75576
|
+
gitRemote: sanitizeGitRemote(gitValue(skillDir, ["remote", "get-url", "origin"])),
|
|
74112
75577
|
gitSha: gitValue(skillDir, ["rev-parse", "HEAD"]),
|
|
74113
75578
|
packedAt: new Date().toISOString()
|
|
74114
75579
|
}
|
|
74115
75580
|
};
|
|
74116
75581
|
}
|
|
75582
|
+
function sanitizeGitRemote(url) {
|
|
75583
|
+
if (!url)
|
|
75584
|
+
return null;
|
|
75585
|
+
try {
|
|
75586
|
+
const parsed = new URL(url);
|
|
75587
|
+
if (!parsed.username && !parsed.password)
|
|
75588
|
+
return url;
|
|
75589
|
+
if (parsed.protocol === "ssh:")
|
|
75590
|
+
return url;
|
|
75591
|
+
parsed.username = "";
|
|
75592
|
+
parsed.password = "";
|
|
75593
|
+
return parsed.toString();
|
|
75594
|
+
} catch {
|
|
75595
|
+
return url;
|
|
75596
|
+
}
|
|
75597
|
+
}
|
|
74117
75598
|
function gitValue(dir, args2) {
|
|
74118
75599
|
try {
|
|
74119
75600
|
const out = execFileSync("git", ["-C", dir, ...args2], { stdio: ["ignore", "pipe", "ignore"], encoding: "utf-8" }).trim();
|
|
@@ -74126,10 +75607,14 @@ function printHuman(result2) {
|
|
|
74126
75607
|
if (!result2.published) {
|
|
74127
75608
|
console.log(source_default.bold(`
|
|
74128
75609
|
Dry run: '${result2.slug}' would be published
|
|
75610
|
+
`));
|
|
75611
|
+
} else if (result2.alreadyPublished) {
|
|
75612
|
+
console.log(source_default.green(`
|
|
75613
|
+
\u2713 '${result2.slug}@${result2.version}' was already published \u2014 no new version created
|
|
74129
75614
|
`));
|
|
74130
75615
|
} else {
|
|
74131
75616
|
console.log(source_default.green(`
|
|
74132
|
-
\u2713 Published '${result2.slug}'
|
|
75617
|
+
\u2713 Published '${result2.slug}'${result2.version ? ` as ${result2.version}` : ""}
|
|
74133
75618
|
`));
|
|
74134
75619
|
}
|
|
74135
75620
|
console.log(` ${source_default.dim("source")} ${result2.path}`);
|
|
@@ -74294,14 +75779,17 @@ function writeCommandError(err, fallback, json) {
|
|
|
74294
75779
|
if (payload.endpoint)
|
|
74295
75780
|
console.error(source_default.dim(`Endpoint: ${payload.endpoint}`));
|
|
74296
75781
|
if (status !== undefined && CONFIG_HINT_STATUSES.has(status)) {
|
|
74297
|
-
console.error(source_default.dim(`Hint: check
|
|
75782
|
+
console.error(source_default.dim(`Hint: check ${SKILLS_API_URL_ENV} (currently ${payload.apiUrl}) or run: skills setup`));
|
|
74298
75783
|
}
|
|
74299
75784
|
process.exitCode = 1;
|
|
74300
75785
|
}
|
|
74301
|
-
function
|
|
74302
|
-
|
|
74303
|
-
|
|
74304
|
-
|
|
75786
|
+
function credentialSource() {
|
|
75787
|
+
try {
|
|
75788
|
+
const fleet = resolveSkillsFleet();
|
|
75789
|
+
return fleet.mode === "hosted" ? fleet.apiKeySource : null;
|
|
75790
|
+
} catch {
|
|
75791
|
+
return null;
|
|
75792
|
+
}
|
|
74305
75793
|
}
|
|
74306
75794
|
function stringField2(value) {
|
|
74307
75795
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
@@ -74341,8 +75829,8 @@ function printWhoami(payload) {
|
|
|
74341
75829
|
console.log(source_default.bold("Role: ") + payload.role);
|
|
74342
75830
|
if (payload.organizationName)
|
|
74343
75831
|
console.log(source_default.bold("Name: ") + payload.organizationName);
|
|
74344
|
-
if (payload.authSource
|
|
74345
|
-
console.log(source_default.dim(
|
|
75832
|
+
if (payload.authSource)
|
|
75833
|
+
console.log(source_default.dim(`Auth: ${payload.authSource}`));
|
|
74346
75834
|
if (payload.offline)
|
|
74347
75835
|
console.log(source_default.dim("(offline \u2014 showing cached info)"));
|
|
74348
75836
|
}
|
|
@@ -74644,35 +76132,46 @@ function registerAuth(parent) {
|
|
|
74644
76132
|
console.log(source_default.dim("Not signed in"));
|
|
74645
76133
|
return;
|
|
74646
76134
|
}
|
|
74647
|
-
clearAuthConfig();
|
|
74648
|
-
console.log(source_default.green(`\u2713 Signed out (was ${existing.email})`));
|
|
76135
|
+
const { stillResolves } = clearAuthConfig();
|
|
76136
|
+
console.log(source_default.green(`\u2713 Signed out${existing.email ? ` (was ${existing.email})` : ""}`));
|
|
76137
|
+
if (stillResolves) {
|
|
76138
|
+
console.log(source_default.yellow(`A Skills credential still resolves from ${credentialSource() ?? "another source"}; ` + `clear it there to finish signing out.`));
|
|
76139
|
+
}
|
|
74649
76140
|
});
|
|
74650
76141
|
auth.command("whoami").description("Show current account info").option("--json", "Output as JSON", false).action(async (options) => {
|
|
74651
|
-
|
|
74652
|
-
|
|
74653
|
-
|
|
74654
|
-
|
|
74655
|
-
|
|
76142
|
+
let fleet;
|
|
76143
|
+
try {
|
|
76144
|
+
fleet = resolveSkillsFleet();
|
|
76145
|
+
} catch (err) {
|
|
76146
|
+
writeCommandError(err, "Failed to resolve the Skills credential", options.json);
|
|
76147
|
+
return;
|
|
76148
|
+
}
|
|
76149
|
+
if (fleet.mode !== "hosted") {
|
|
76150
|
+
const payload = {
|
|
76151
|
+
status: "unauthenticated",
|
|
76152
|
+
error: `Not signed in. Run: skills auth login, or set ${SKILLS_API_KEY_ENV}`
|
|
76153
|
+
};
|
|
74656
76154
|
if (options.json)
|
|
74657
76155
|
console.log(JSON.stringify(payload, null, 2));
|
|
74658
76156
|
else
|
|
74659
76157
|
console.log(source_default.dim(payload.error));
|
|
74660
76158
|
return;
|
|
74661
76159
|
}
|
|
74662
|
-
const
|
|
76160
|
+
const cached2 = fleet.apiKeyTier === "disk" ? getAuthIdentity() : null;
|
|
76161
|
+
const authSource = fleet.apiKeySource;
|
|
74663
76162
|
try {
|
|
74664
76163
|
const res = await apiRequest("/api/auth/whoami", {
|
|
74665
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
76164
|
+
headers: { Authorization: `Bearer ${fleet.apiKey}` }
|
|
74666
76165
|
});
|
|
74667
|
-
const payload = authIdentityPayload(authSource, res,
|
|
76166
|
+
const payload = authIdentityPayload(authSource, res, cached2);
|
|
74668
76167
|
if (options.json) {
|
|
74669
76168
|
console.log(JSON.stringify(payload, null, 2));
|
|
74670
76169
|
} else {
|
|
74671
76170
|
printWhoami(payload);
|
|
74672
76171
|
}
|
|
74673
76172
|
} catch (err) {
|
|
74674
|
-
if (
|
|
74675
|
-
const payload = authIdentityPayload(
|
|
76173
|
+
if (cached2 && Object.keys(cached2).length > 0) {
|
|
76174
|
+
const payload = authIdentityPayload(authSource, {}, cached2, true);
|
|
74676
76175
|
if (options.json)
|
|
74677
76176
|
console.log(JSON.stringify(payload, null, 2));
|
|
74678
76177
|
else
|
|
@@ -74687,6 +76186,7 @@ var isTTY, DEFAULT_DEVICE_POLL_TIMEOUT_MS, MAX_ERROR_DETAIL_LENGTH = 200, CONFIG
|
|
|
74687
76186
|
var init_auth = __esm(() => {
|
|
74688
76187
|
init_source();
|
|
74689
76188
|
init_auth_store();
|
|
76189
|
+
init_fleet_credentials();
|
|
74690
76190
|
isTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
74691
76191
|
DEFAULT_DEVICE_POLL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
74692
76192
|
CONFIG_HINT_STATUSES = new Set([401, 403, 404, 405, 501]);
|
|
@@ -74848,11 +76348,11 @@ var init_storage = __esm(() => {
|
|
|
74848
76348
|
});
|
|
74849
76349
|
|
|
74850
76350
|
// src/lib/registry-reconcile.ts
|
|
74851
|
-
import { existsSync as existsSync31, readFileSync as
|
|
74852
|
-
import { join as
|
|
76351
|
+
import { existsSync as existsSync31, readFileSync as readFileSync28, statSync as statSync20, writeFileSync as writeFileSync21 } from "fs";
|
|
76352
|
+
import { join as join36 } from "path";
|
|
74853
76353
|
function isDirectory2(path) {
|
|
74854
76354
|
try {
|
|
74855
|
-
return
|
|
76355
|
+
return statSync20(path).isDirectory();
|
|
74856
76356
|
} catch {
|
|
74857
76357
|
return false;
|
|
74858
76358
|
}
|
|
@@ -74860,15 +76360,15 @@ function isDirectory2(path) {
|
|
|
74860
76360
|
function migrationNeeded(options) {
|
|
74861
76361
|
if (options.rootDir)
|
|
74862
76362
|
return false;
|
|
74863
|
-
const appDir = options.homeDir ?
|
|
74864
|
-
return !(isOwnerLayoutMigrated(appDir) && isDirectory2(
|
|
76363
|
+
const appDir = options.homeDir ? join36(options.homeDir, ".hasna", "skills") : getDataDir();
|
|
76364
|
+
return !(isOwnerLayoutMigrated(appDir) && isDirectory2(join36(appDir, SKILLS_CACHE_DIRNAME)));
|
|
74865
76365
|
}
|
|
74866
76366
|
function readBaseline(skillDir) {
|
|
74867
|
-
const markerPath =
|
|
76367
|
+
const markerPath = join36(skillDir, PULL_MARKER_FILE);
|
|
74868
76368
|
if (!existsSync31(markerPath))
|
|
74869
76369
|
return;
|
|
74870
76370
|
try {
|
|
74871
|
-
const marker = JSON.parse(
|
|
76371
|
+
const marker = JSON.parse(readFileSync28(markerPath, "utf-8"));
|
|
74872
76372
|
return {
|
|
74873
76373
|
...typeof marker.contentHash === "string" && marker.contentHash ? { contentHash: marker.contentHash } : {},
|
|
74874
76374
|
...typeof marker.version === "string" && marker.version ? { version: marker.version } : {}
|
|
@@ -74878,11 +76378,11 @@ function readBaseline(skillDir) {
|
|
|
74878
76378
|
}
|
|
74879
76379
|
}
|
|
74880
76380
|
function readCursor(root) {
|
|
74881
|
-
const path =
|
|
76381
|
+
const path = join36(root, SYNC_CURSOR_FILE);
|
|
74882
76382
|
if (!existsSync31(path))
|
|
74883
76383
|
return { runCount: 0 };
|
|
74884
76384
|
try {
|
|
74885
|
-
const cursor = JSON.parse(
|
|
76385
|
+
const cursor = JSON.parse(readFileSync28(path, "utf-8"));
|
|
74886
76386
|
return { runCount: typeof cursor.runCount === "number" ? cursor.runCount : 0 };
|
|
74887
76387
|
} catch {
|
|
74888
76388
|
return { runCount: 0 };
|
|
@@ -74891,12 +76391,12 @@ function readCursor(root) {
|
|
|
74891
76391
|
function resolveCorpusRootReadOnly(options) {
|
|
74892
76392
|
if (options.rootDir)
|
|
74893
76393
|
return { root: options.rootDir, migrationPending: false };
|
|
74894
|
-
const appDir = options.homeDir ?
|
|
74895
|
-
const cache3 =
|
|
76394
|
+
const appDir = options.homeDir ? join36(options.homeDir, ".hasna", "skills") : getDataDirReadOnly();
|
|
76395
|
+
const cache3 = join36(appDir, SKILLS_CACHE_DIRNAME);
|
|
74896
76396
|
if (isOwnerLayoutMigrated(appDir) && isDirectory2(cache3)) {
|
|
74897
76397
|
return { root: cache3, migrationPending: false };
|
|
74898
76398
|
}
|
|
74899
|
-
return { root:
|
|
76399
|
+
return { root: join36(appDir, INSTALLED_SKILLS_DIRNAME), migrationPending: true };
|
|
74900
76400
|
}
|
|
74901
76401
|
function remoteRowToSkill(record3) {
|
|
74902
76402
|
const slug = typeof record3.slug === "string" ? record3.slug : typeof record3.name === "string" ? record3.name : undefined;
|
|
@@ -74994,9 +76494,9 @@ async function reconcileRegistry(options = {}) {
|
|
|
74994
76494
|
}
|
|
74995
76495
|
const direction = options.pull && !options.push && !options.all ? "pull" : options.push && !options.pull && !options.all ? "push" : "all";
|
|
74996
76496
|
const dryRun = options.dryRun ?? false;
|
|
74997
|
-
const client = options.client !== undefined ? options.client : dryRun ? createRemoteSkillsClientReadOnly() : createRemoteSkillsClient();
|
|
76497
|
+
const client = options.client !== undefined ? options.client : dryRun ? await createRemoteSkillsClientReadOnly() : await createRemoteSkillsClient();
|
|
74998
76498
|
if (!client) {
|
|
74999
|
-
throw new ReconcileRegistryError("No API key configured, so there is nowhere to sync to.", ["Run `skills login`, or set
|
|
76499
|
+
throw new ReconcileRegistryError("No API key configured, so there is nowhere to sync to.", ["Run `skills auth login`, or set HASNA_SKILLS_API_KEY (and HASNA_SKILLS_API_URL for your own instance)."]);
|
|
75000
76500
|
}
|
|
75001
76501
|
const { root, migrationPending } = dryRun ? resolveCorpusRootReadOnly(options) : { root: resolveCorpusRoot(options), migrationPending: migrationNeeded(options) };
|
|
75002
76502
|
const localSkills = listPortableSkillMetas({ rootDir: root });
|
|
@@ -75013,7 +76513,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
75013
76513
|
const remoteRowsPayload = await client.listSkills();
|
|
75014
76514
|
if (!Array.isArray(remoteRowsPayload)) {
|
|
75015
76515
|
const shape = remoteRowsPayload && typeof remoteRowsPayload === "object" ? `object with keys [${Object.keys(remoteRowsPayload).join(", ")}]` : typeof remoteRowsPayload;
|
|
75016
|
-
throw new ReconcileRegistryError(`Registry listing failed: expected an array of skills, got ${shape}.`, ["Check
|
|
76516
|
+
throw new ReconcileRegistryError(`Registry listing failed: expected an array of skills, got ${shape}.`, ["Check HASNA_SKILLS_API_URL and the resolved credential; a failed listing must not be read as an empty registry."]);
|
|
75017
76517
|
}
|
|
75018
76518
|
const remotes = new Map;
|
|
75019
76519
|
for (const row of remoteRowsPayload) {
|
|
@@ -75028,7 +76528,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
75028
76528
|
for (const slug of allSlugs) {
|
|
75029
76529
|
const local = locals.get(slug);
|
|
75030
76530
|
const remote = remotes.get(slug);
|
|
75031
|
-
const baseline = local ? readBaseline(
|
|
76531
|
+
const baseline = local ? readBaseline(join36(root, slug)) : undefined;
|
|
75032
76532
|
const { state, reason } = classifySkill(local, remote, baseline);
|
|
75033
76533
|
let { action, reason: actionReason } = resolveAction(state, direction, conflict);
|
|
75034
76534
|
if (state === "remote-only" && isDigestless(remote)) {
|
|
@@ -75087,7 +76587,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
75087
76587
|
try {
|
|
75088
76588
|
await pushSkill(slug, { rootDir: root, client });
|
|
75089
76589
|
const pushed = locals.get(slug);
|
|
75090
|
-
writePullMarker(
|
|
76590
|
+
writePullMarker(join36(root, slug), {
|
|
75091
76591
|
skill: slug,
|
|
75092
76592
|
...pushed?.version ? { version: pushed.version } : {},
|
|
75093
76593
|
...pushed?.sha256 ? { contentHash: pushed.sha256 } : {},
|
|
@@ -75159,7 +76659,7 @@ async function reconcileRegistry(options = {}) {
|
|
|
75159
76659
|
runCount: readCursor(root).runCount + 1,
|
|
75160
76660
|
summary
|
|
75161
76661
|
};
|
|
75162
|
-
writeFileSync21(
|
|
76662
|
+
writeFileSync21(join36(root, SYNC_CURSOR_FILE), `${JSON.stringify(cursor, null, 2)}
|
|
75163
76663
|
`);
|
|
75164
76664
|
return {
|
|
75165
76665
|
corpusRoot: root,
|
|
@@ -75276,7 +76776,7 @@ import process12 from "process";
|
|
|
75276
76776
|
// ../../node_modules/.bun/ink@5.2.1+6e93904c7bfe2418/node_modules/ink/build/ink.js
|
|
75277
76777
|
var import_react10 = __toESM(require_react(), 1);
|
|
75278
76778
|
import process11 from "process";
|
|
75279
|
-
// ../../node_modules/.bun/es-toolkit@1.
|
|
76779
|
+
// ../../node_modules/.bun/es-toolkit@1.52.0/node_modules/es-toolkit/dist/function/debounce.mjs
|
|
75280
76780
|
function debounce(func, debounceMs, { signal, edges } = {}) {
|
|
75281
76781
|
let pendingThis = undefined;
|
|
75282
76782
|
let pendingArgs = null;
|
|
@@ -75334,7 +76834,7 @@ function debounce(func, debounceMs, { signal, edges } = {}) {
|
|
|
75334
76834
|
return debounced;
|
|
75335
76835
|
}
|
|
75336
76836
|
|
|
75337
|
-
// ../../node_modules/.bun/es-toolkit@1.
|
|
76837
|
+
// ../../node_modules/.bun/es-toolkit@1.52.0/node_modules/es-toolkit/dist/compat/function/debounce.mjs
|
|
75338
76838
|
function debounce2(func, debounceMs = 0, options = {}) {
|
|
75339
76839
|
if (typeof options !== "object")
|
|
75340
76840
|
options = {};
|
|
@@ -75355,7 +76855,8 @@ function debounce2(func, debounceMs = 0, options = {}) {
|
|
|
75355
76855
|
if (pendingAt === null)
|
|
75356
76856
|
pendingAt = Date.now();
|
|
75357
76857
|
if (Date.now() - pendingAt >= maxWait) {
|
|
75358
|
-
|
|
76858
|
+
if (leading || trailing)
|
|
76859
|
+
result = func.apply(this, args);
|
|
75359
76860
|
pendingAt = Date.now();
|
|
75360
76861
|
_debounced.cancel();
|
|
75361
76862
|
_debounced.schedule();
|
|
@@ -75374,7 +76875,7 @@ function debounce2(func, debounceMs = 0, options = {}) {
|
|
|
75374
76875
|
return debounced;
|
|
75375
76876
|
}
|
|
75376
76877
|
|
|
75377
|
-
// ../../node_modules/.bun/es-toolkit@1.
|
|
76878
|
+
// ../../node_modules/.bun/es-toolkit@1.52.0/node_modules/es-toolkit/dist/compat/function/throttle.mjs
|
|
75378
76879
|
function throttle(func, throttleMs = 0, options = {}) {
|
|
75379
76880
|
const { leading = true, trailing = true } = options;
|
|
75380
76881
|
return debounce2(func, throttleMs, {
|