@hasna/skills 0.4.0 → 0.5.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 +176 -5
- package/bin/index.js +6518 -4950
- package/bin/mcp.js +1173 -410
- package/bin/migrate.js +148 -40
- package/bin/server.js +53 -83
- package/bin/worker.js +41 -73
- package/dist/admin-contract.d.ts +37 -19
- package/dist/admin-contract.js +1 -1
- package/dist/cli/cli.test-utils.d.ts +10 -8
- package/dist/cli/commands/customer-profile.d.ts +2 -0
- package/dist/cli/commands/customer-verification.d.ts +5 -0
- package/dist/cli/commands/tool-primitives.d.ts +1 -1
- package/dist/cli/commands/workspace-member-mutations.d.ts +2 -0
- package/dist/cli/commands/workspace-members.d.ts +2 -0
- package/dist/cli/env-assignment.d.ts +9 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +613 -153
- package/dist/lib/agent-sync.d.ts +13 -8
- package/dist/lib/api-url.d.ts +4 -3
- package/dist/lib/app-home.d.ts +0 -1
- package/dist/lib/client-types.d.ts +75 -0
- package/dist/lib/credential-state.d.ts +12 -0
- package/dist/lib/fleet-credentials.d.ts +41 -15
- package/dist/lib/home-adoption.d.ts +2 -0
- package/dist/lib/home-census.d.ts +3 -1
- package/dist/lib/local-opt-in.d.ts +24 -0
- package/dist/lib/portable-skills-files.d.ts +6 -2
- package/dist/lib/read-access.d.ts +83 -0
- package/dist/lib/remote-auth.d.ts +14 -0
- package/dist/lib/remote-client.d.ts +34 -5
- package/dist/lib/remote-profile.d.ts +26 -0
- package/dist/lib/remote-registry.d.ts +7 -3
- package/dist/lib/remote-workspace.d.ts +76 -0
- package/dist/lib/skillinfo.d.ts +1 -1
- package/dist/mcp/helpers.d.ts +22 -0
- package/dist/mcp/index.d.ts +16 -0
- package/dist/sdk/governance-store.d.ts +1 -0
- package/dist/sdk/index.d.ts +5 -2
- package/dist/sdk/index.js +1084 -283
- package/dist/sdk/outputs.d.ts +0 -11
- package/dist/sdk/runs.d.ts +1 -1
- package/dist/storage.js +6 -40
- package/docs/skill-standard.md +30 -2
- package/package.json +6 -4
- package/dist/lib/instance-credentials-race.fixture.d.ts +0 -1
package/bin/mcp.js
CHANGED
|
@@ -6932,58 +6932,14 @@ var require_dist = __commonJS((exports, module) => {
|
|
|
6932
6932
|
exports.default = formatsPlugin;
|
|
6933
6933
|
});
|
|
6934
6934
|
|
|
6935
|
-
// src/lib/remote-run-contract.ts
|
|
6936
|
-
function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
6937
|
-
const record3 = isRecord3(payload) ? payload : {};
|
|
6938
|
-
return {
|
|
6939
|
-
contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
|
|
6940
|
-
...pickString(record3, "id"),
|
|
6941
|
-
skill: pickStringValue(record3, "skill") ?? fallbackSkill,
|
|
6942
|
-
...pickString(record3, "requestedSlug"),
|
|
6943
|
-
...pickString(record3, "status"),
|
|
6944
|
-
...pickNumber(record3, "exitCode"),
|
|
6945
|
-
...pickString(record3, "correlationId"),
|
|
6946
|
-
...pickString(record3, "createdAt"),
|
|
6947
|
-
...pickString(record3, "startedAt"),
|
|
6948
|
-
...pickString(record3, "completedAt"),
|
|
6949
|
-
...pickNumber(record3, "durationMs"),
|
|
6950
|
-
...pickString(record3, "outputType"),
|
|
6951
|
-
...hasOwn(record3, "outputPreview") ? { outputPreview: record3.outputPreview } : {},
|
|
6952
|
-
...pickString(record3, "errorCode"),
|
|
6953
|
-
...pickString(record3, "errorMessage"),
|
|
6954
|
-
...pickString(record3, "error"),
|
|
6955
|
-
...pickString(record3, "code"),
|
|
6956
|
-
...hasOwn(record3, "details") ? { details: record3.details } : {}
|
|
6957
|
-
};
|
|
6958
|
-
}
|
|
6959
|
-
function isRecord3(value) {
|
|
6960
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
6961
|
-
}
|
|
6962
|
-
function hasOwn(record3, key) {
|
|
6963
|
-
return Object.prototype.hasOwnProperty.call(record3, key);
|
|
6964
|
-
}
|
|
6965
|
-
function pickString(record3, key) {
|
|
6966
|
-
const value = pickStringValue(record3, key);
|
|
6967
|
-
return value === undefined ? {} : { [key]: value };
|
|
6968
|
-
}
|
|
6969
|
-
function pickStringValue(record3, key) {
|
|
6970
|
-
const value = record3[key];
|
|
6971
|
-
return typeof value === "string" ? value : undefined;
|
|
6972
|
-
}
|
|
6973
|
-
function pickNumber(record3, key) {
|
|
6974
|
-
const value = record3[key];
|
|
6975
|
-
return typeof value === "number" && Number.isFinite(value) ? { [key]: value } : {};
|
|
6976
|
-
}
|
|
6977
|
-
var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
|
|
6978
|
-
|
|
6979
6935
|
// ../contracts/dist/client/transport.js
|
|
6980
6936
|
import { isIP } from "net";
|
|
6981
6937
|
import { spawnSync } from "child_process";
|
|
6982
|
-
import { closeSync, fstatSync, openSync, readFileSync as
|
|
6938
|
+
import { closeSync, fstatSync, openSync, readFileSync as readFileSync7 } from "fs";
|
|
6983
6939
|
import { O_NOFOLLOW, O_NONBLOCK, O_RDONLY } from "constants";
|
|
6984
6940
|
import { createRequire } from "module";
|
|
6985
6941
|
import { hostname as osHostname } from "os";
|
|
6986
|
-
import { isAbsolute as isAbsolute3, join as
|
|
6942
|
+
import { isAbsolute as isAbsolute3, join as join9 } from "path";
|
|
6987
6943
|
function envToken(name) {
|
|
6988
6944
|
return name.toUpperCase().replace(/-/g, "_");
|
|
6989
6945
|
}
|
|
@@ -7013,14 +6969,14 @@ function hasnaHomeDir(env) {
|
|
|
7013
6969
|
if (override)
|
|
7014
6970
|
return override;
|
|
7015
6971
|
const home = homeDir(env);
|
|
7016
|
-
return home ?
|
|
6972
|
+
return home ? join9(home, HASNA_HOME_DIR) : null;
|
|
7017
6973
|
}
|
|
7018
6974
|
function appConfigDir(name, env) {
|
|
7019
6975
|
const configRoot = absoluteOverride(env, HASNA_CONFIG_HOME_ENV_KEY);
|
|
7020
6976
|
if (configRoot)
|
|
7021
|
-
return
|
|
6977
|
+
return join9(configRoot, name);
|
|
7022
6978
|
const root = hasnaHomeDir(env);
|
|
7023
|
-
return root ?
|
|
6979
|
+
return root ? join9(root, name, CONFIG_SUBDIR) : null;
|
|
7024
6980
|
}
|
|
7025
6981
|
function credentialDiskSourceList(name, env, profile = null) {
|
|
7026
6982
|
if (!SAFE_APP_SLUG.test(name))
|
|
@@ -7029,7 +6985,7 @@ function credentialDiskSourceList(name, env, profile = null) {
|
|
|
7029
6985
|
if (!directory)
|
|
7030
6986
|
return [];
|
|
7031
6987
|
const file = profile ? `${CREDENTIALS_FILE}-${profile}` : CREDENTIALS_FILE;
|
|
7032
|
-
return [{ path:
|
|
6988
|
+
return [{ path: join9(directory, file), tier: "disk" }];
|
|
7033
6989
|
}
|
|
7034
6990
|
function credentialDiskSources(name, env) {
|
|
7035
6991
|
return credentialDiskSourceList(name, env, null).map((s) => s.path);
|
|
@@ -7104,7 +7060,7 @@ function readAppConfigFile(path) {
|
|
|
7104
7060
|
unsafe("the file is not owned by the current user");
|
|
7105
7061
|
if (before.size > MAX_CREDENTIAL_FILE_BYTES)
|
|
7106
7062
|
unsafe("the file exceeds the size limit");
|
|
7107
|
-
const bytes =
|
|
7063
|
+
const bytes = readFileSync7(fd);
|
|
7108
7064
|
const after = fstatSync(fd);
|
|
7109
7065
|
if (!configFileReadsCoherent(before, after)) {
|
|
7110
7066
|
unsafe("the file changed while being read");
|
|
@@ -7742,12 +7698,39 @@ var init_instance_credentials = __esm(() => {
|
|
|
7742
7698
|
init_transport();
|
|
7743
7699
|
});
|
|
7744
7700
|
|
|
7701
|
+
// src/lib/local-opt-in.ts
|
|
7702
|
+
function isSkillsLocalOptIn(env = process.env) {
|
|
7703
|
+
return SKILLS_LOCAL_OPT_IN_ENV_KEYS.some((key) => (env[key] ?? "").trim() !== "");
|
|
7704
|
+
}
|
|
7705
|
+
function skillsAuthorityEnvKeys() {
|
|
7706
|
+
const keys = clientTransportEnvKeys("skills");
|
|
7707
|
+
return [
|
|
7708
|
+
...keys.apiUrlKeys,
|
|
7709
|
+
...keys.apiKeyKeys,
|
|
7710
|
+
credentialOverrideEnvKey("skills"),
|
|
7711
|
+
credentialPointerEnvKey("skills"),
|
|
7712
|
+
CREDENTIAL_PROFILE_ENV_KEY
|
|
7713
|
+
];
|
|
7714
|
+
}
|
|
7715
|
+
function hasSkillsEnvAuthorityIntent(env = process.env) {
|
|
7716
|
+
return skillsAuthorityEnvKeys().some((key) => (env[key] ?? "").trim() !== "");
|
|
7717
|
+
}
|
|
7718
|
+
function selectsSkillsLocalMode(env = process.env) {
|
|
7719
|
+
return !hasSkillsEnvAuthorityIntent(env) && isSkillsLocalOptIn(env);
|
|
7720
|
+
}
|
|
7721
|
+
var SKILLS_LOCAL_OPT_IN_ENV_KEYS;
|
|
7722
|
+
var init_local_opt_in = __esm(() => {
|
|
7723
|
+
init_transport();
|
|
7724
|
+
SKILLS_LOCAL_OPT_IN_ENV_KEYS = ["HASNA_SKILLS_LOCAL", "SKILLS_LOCAL"];
|
|
7725
|
+
});
|
|
7726
|
+
|
|
7745
7727
|
// src/lib/fleet-credentials.ts
|
|
7746
7728
|
var exports_fleet_credentials = {};
|
|
7747
7729
|
__export(exports_fleet_credentials, {
|
|
7748
7730
|
skillsCredentialOrReason: () => skillsCredentialOrReason,
|
|
7749
7731
|
skillsCredentialFiles: () => skillsCredentialFiles,
|
|
7750
7732
|
skillsCredentialFilePath: () => skillsCredentialFilePath,
|
|
7733
|
+
selectsSkillsLocalMode: () => selectsSkillsLocalMode,
|
|
7751
7734
|
resolveSkillsFleet: () => resolveSkillsFleet,
|
|
7752
7735
|
resolveSkillsConnection: () => resolveSkillsConnection,
|
|
7753
7736
|
resolveSkillsApiOrigin: () => resolveSkillsApiOrigin,
|
|
@@ -7758,8 +7741,11 @@ __export(exports_fleet_credentials, {
|
|
|
7758
7741
|
requireSkillsApiKey: () => requireSkillsApiKey,
|
|
7759
7742
|
noticeLocalSkillsMode: () => noticeLocalSkillsMode,
|
|
7760
7743
|
normalizeSkillsApiOrigin: () => normalizeSkillsApiOrigin,
|
|
7744
|
+
isSkillsLocalOptIn: () => isSkillsLocalOptIn,
|
|
7745
|
+
isSkillsFleetCredentialError: () => isSkillsFleetCredentialError,
|
|
7761
7746
|
configuredSkillsApiUrl: () => configuredSkillsApiUrl,
|
|
7762
7747
|
SkillsFleetCredentialError: () => SkillsFleetCredentialError,
|
|
7748
|
+
SKILLS_LOCAL_OPT_IN_ENV_KEYS: () => SKILLS_LOCAL_OPT_IN_ENV_KEYS,
|
|
7763
7749
|
SKILLS_APP: () => SKILLS_APP,
|
|
7764
7750
|
SKILLS_API_URL_ENV_KEYS: () => SKILLS_API_URL_ENV_KEYS,
|
|
7765
7751
|
SKILLS_API_URL_ENV: () => SKILLS_API_URL_ENV,
|
|
@@ -7770,6 +7756,9 @@ __export(exports_fleet_credentials, {
|
|
|
7770
7756
|
function isCredentialResolutionError(error2) {
|
|
7771
7757
|
return error2 instanceof CredentialResolutionError || typeof error2 === "object" && error2 !== null && error2.name === "CredentialResolutionError";
|
|
7772
7758
|
}
|
|
7759
|
+
function isSkillsFleetCredentialError(error2) {
|
|
7760
|
+
return error2 instanceof SkillsFleetCredentialError || typeof error2 === "object" && error2 !== null && error2.name === "SkillsFleetCredentialError";
|
|
7761
|
+
}
|
|
7773
7762
|
function asSkillsFleetCredentialError(error2) {
|
|
7774
7763
|
if (!isCredentialResolutionError(error2))
|
|
7775
7764
|
return null;
|
|
@@ -7835,7 +7824,7 @@ function noticeLocalSkillsMode(write = (line) => console.error(line)) {
|
|
|
7835
7824
|
if (localNoticePrinted)
|
|
7836
7825
|
return;
|
|
7837
7826
|
localNoticePrinted = true;
|
|
7838
|
-
write(`skills: local mode
|
|
7827
|
+
write(`skills: local mode (${SKILLS_LOCAL_OPT_IN_ENV_KEYS[0]}=1) \u2014 running on this machine against the bundled corpus.`);
|
|
7839
7828
|
}
|
|
7840
7829
|
function resetLocalSkillsModeNotice() {
|
|
7841
7830
|
localNoticePrinted = false;
|
|
@@ -7875,13 +7864,16 @@ function snapshotSkillsOptions(env, options) {
|
|
|
7875
7864
|
} } };
|
|
7876
7865
|
}
|
|
7877
7866
|
function resolveSkillsFleetOrThrow(env, options) {
|
|
7867
|
+
if (selectsSkillsLocalMode(env))
|
|
7868
|
+
return { mode: "local", apiOrigin: null, apiKey: null };
|
|
7878
7869
|
const assertFilesUnchanged = captureSkillsCredentialFiles(skillsProfileCredentialFiles(env, options.credentials?.profile));
|
|
7879
7870
|
const configured = configuredSkillsApiUrl(env, options.credentials?.keychain, options.credentials?.profile);
|
|
7880
7871
|
const credential = resolveCredential(SKILLS_APP, env, options.credentials);
|
|
7881
7872
|
if (!credential) {
|
|
7882
|
-
if (!configured)
|
|
7883
|
-
|
|
7884
|
-
|
|
7873
|
+
if (!configured) {
|
|
7874
|
+
throw new SkillsFleetCredentialError(`No API key resolved and no Skills API URL is configured \u2014 failing closed ` + `(local mode is opt-in only: set ${SKILLS_LOCAL_OPT_IN_ENV_KEYS[0]}=1 to run on this machine). ` + `Looked in ${credentialLocations(env)}. Sign in with: skills auth login`);
|
|
7875
|
+
}
|
|
7876
|
+
throw new SkillsFleetCredentialError(`${configured.source} points this CLI at a Skills service but no API key resolved \u2014 refusing to run locally instead. ` + `Looked in ${credentialLocations(env)}. Sign in with: skills auth login`);
|
|
7885
7877
|
}
|
|
7886
7878
|
const apiOrigin = normalizeSkillsApiOrigin(configured?.value ?? defaultFleetGatewayBaseUrl(SKILLS_APP));
|
|
7887
7879
|
toV1BaseUrl(apiOrigin);
|
|
@@ -7903,6 +7895,10 @@ function resolveSkillsFleetOrThrow(env, options) {
|
|
|
7903
7895
|
}
|
|
7904
7896
|
return { ...base, apiKey: credential.apiKey, apiKeyPointer: null };
|
|
7905
7897
|
}
|
|
7898
|
+
function credentialLocations(env) {
|
|
7899
|
+
const files = skillsCredentialFiles(env).join(" or ") || "no credentials file (HOME is unset)";
|
|
7900
|
+
return `hasna.credentials.${SKILLS_APP}.api-key (macOS Keychain, account HASNA_STATION or the host name), ${files}, and ${SKILLS_API_KEY_ENV}`;
|
|
7901
|
+
}
|
|
7906
7902
|
function assertCredentialInstance(credential, apiOrigin, env, options) {
|
|
7907
7903
|
let bound;
|
|
7908
7904
|
if (credential.tier === "disk" || credential.tier === "profile") {
|
|
@@ -7956,7 +7952,7 @@ async function skillsCredentialOrReason(env = process.env, options = {}) {
|
|
|
7956
7952
|
const connection = await resolveSkillsConnection(env, options);
|
|
7957
7953
|
return connection ? { apiKey: connection.apiKey, apiOrigin: connection.apiOrigin, reason: null } : { apiKey: null, apiOrigin: null, reason: null };
|
|
7958
7954
|
} catch (error2) {
|
|
7959
|
-
if (error2
|
|
7955
|
+
if (isSkillsFleetCredentialError(error2)) {
|
|
7960
7956
|
return { apiKey: null, apiOrigin: null, reason: error2.message };
|
|
7961
7957
|
}
|
|
7962
7958
|
throw error2;
|
|
@@ -7968,7 +7964,14 @@ function resolveSkillsApiOrigin(env = process.env, options = {}) {
|
|
|
7968
7964
|
toV1BaseUrl(configured.value);
|
|
7969
7965
|
return { origin: normalizeSkillsApiOrigin(configured.value), source: configured.source };
|
|
7970
7966
|
}
|
|
7971
|
-
|
|
7967
|
+
let fleet;
|
|
7968
|
+
try {
|
|
7969
|
+
fleet = resolveSkillsFleet(env, options);
|
|
7970
|
+
} catch (error2) {
|
|
7971
|
+
if (isSkillsFleetCredentialError(error2))
|
|
7972
|
+
return null;
|
|
7973
|
+
throw error2;
|
|
7974
|
+
}
|
|
7972
7975
|
return fleet.mode === "hosted" ? { origin: fleet.apiOrigin, source: fleet.apiUrlSource } : null;
|
|
7973
7976
|
}
|
|
7974
7977
|
function requireSkillsApiOrigin(action = "This command", env = process.env, options = {}) {
|
|
@@ -7987,6 +7990,8 @@ var SKILLS_APP = "skills", ENV_KEYS, SKILLS_API_URL_ENV_KEYS, SKILLS_API_KEY_ENV
|
|
|
7987
7990
|
var init_fleet_credentials = __esm(() => {
|
|
7988
7991
|
init_transport();
|
|
7989
7992
|
init_instance_credentials();
|
|
7993
|
+
init_local_opt_in();
|
|
7994
|
+
init_local_opt_in();
|
|
7990
7995
|
ENV_KEYS = clientTransportEnvKeys(SKILLS_APP);
|
|
7991
7996
|
SKILLS_API_URL_ENV_KEYS = ENV_KEYS.apiUrlKeys;
|
|
7992
7997
|
SKILLS_API_KEY_ENV_KEYS = ENV_KEYS.apiKeyKeys;
|
|
@@ -8009,6 +8014,73 @@ var init_fleet_credentials = __esm(() => {
|
|
|
8009
8014
|
};
|
|
8010
8015
|
});
|
|
8011
8016
|
|
|
8017
|
+
// src/lib/auth-store.ts
|
|
8018
|
+
import { chmodSync, existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, renameSync as renameSync2, statSync as statSync7, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
|
|
8019
|
+
function getAuthFilePath(env = process.env) {
|
|
8020
|
+
return skillsCredentialFilePath(env);
|
|
8021
|
+
}
|
|
8022
|
+
function getApiUrl(action, env = process.env, options = {}) {
|
|
8023
|
+
return requireSkillsApiOrigin(action, env, options);
|
|
8024
|
+
}
|
|
8025
|
+
function credentialFileMode(env = process.env) {
|
|
8026
|
+
try {
|
|
8027
|
+
return statSync7(skillsCredentialFilePath(env)).mode & 511;
|
|
8028
|
+
} catch {
|
|
8029
|
+
return null;
|
|
8030
|
+
}
|
|
8031
|
+
}
|
|
8032
|
+
var init_auth_store = __esm(() => {
|
|
8033
|
+
init_transport();
|
|
8034
|
+
init_instance_credentials();
|
|
8035
|
+
init_fleet_credentials();
|
|
8036
|
+
init_transport();
|
|
8037
|
+
init_fleet_credentials();
|
|
8038
|
+
});
|
|
8039
|
+
|
|
8040
|
+
// src/lib/remote-run-contract.ts
|
|
8041
|
+
function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
8042
|
+
const record3 = isRecord3(payload) ? payload : {};
|
|
8043
|
+
return {
|
|
8044
|
+
contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
|
|
8045
|
+
...pickString(record3, "id"),
|
|
8046
|
+
skill: pickStringValue(record3, "skill") ?? fallbackSkill,
|
|
8047
|
+
...pickString(record3, "requestedSlug"),
|
|
8048
|
+
...pickString(record3, "status"),
|
|
8049
|
+
...pickNumber(record3, "exitCode"),
|
|
8050
|
+
...pickString(record3, "correlationId"),
|
|
8051
|
+
...pickString(record3, "createdAt"),
|
|
8052
|
+
...pickString(record3, "startedAt"),
|
|
8053
|
+
...pickString(record3, "completedAt"),
|
|
8054
|
+
...pickNumber(record3, "durationMs"),
|
|
8055
|
+
...pickString(record3, "outputType"),
|
|
8056
|
+
...hasOwn(record3, "outputPreview") ? { outputPreview: record3.outputPreview } : {},
|
|
8057
|
+
...pickString(record3, "errorCode"),
|
|
8058
|
+
...pickString(record3, "errorMessage"),
|
|
8059
|
+
...pickString(record3, "error"),
|
|
8060
|
+
...pickString(record3, "code"),
|
|
8061
|
+
...hasOwn(record3, "details") ? { details: record3.details } : {}
|
|
8062
|
+
};
|
|
8063
|
+
}
|
|
8064
|
+
function isRecord3(value) {
|
|
8065
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
8066
|
+
}
|
|
8067
|
+
function hasOwn(record3, key) {
|
|
8068
|
+
return Object.prototype.hasOwnProperty.call(record3, key);
|
|
8069
|
+
}
|
|
8070
|
+
function pickString(record3, key) {
|
|
8071
|
+
const value = pickStringValue(record3, key);
|
|
8072
|
+
return value === undefined ? {} : { [key]: value };
|
|
8073
|
+
}
|
|
8074
|
+
function pickStringValue(record3, key) {
|
|
8075
|
+
const value = record3[key];
|
|
8076
|
+
return typeof value === "string" ? value : undefined;
|
|
8077
|
+
}
|
|
8078
|
+
function pickNumber(record3, key) {
|
|
8079
|
+
const value = record3[key];
|
|
8080
|
+
return typeof value === "number" && Number.isFinite(value) ? { [key]: value } : {};
|
|
8081
|
+
}
|
|
8082
|
+
var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
|
|
8083
|
+
|
|
8012
8084
|
// src/lib/blog-article.ts
|
|
8013
8085
|
var exports_blog_article = {};
|
|
8014
8086
|
__export(exports_blog_article, {
|
|
@@ -8223,16 +8295,104 @@ var init_remote_files = __esm(() => {
|
|
|
8223
8295
|
MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
8224
8296
|
});
|
|
8225
8297
|
|
|
8226
|
-
// src/lib/
|
|
8227
|
-
function
|
|
8228
|
-
|
|
8298
|
+
// src/lib/remote-workspace.ts
|
|
8299
|
+
function workspaceMembersQuery(options = {}) {
|
|
8300
|
+
if (!record3(options) || Object.keys(options).some((key) => key !== "limit" && key !== "cursor") || options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 100) || options.cursor !== undefined && !cursor(options.cursor))
|
|
8301
|
+
throw new Error("Use a roster limit from 1 to 100 and an unchanged continuation cursor.");
|
|
8302
|
+
const query = new URLSearchParams;
|
|
8303
|
+
if (options.limit !== undefined)
|
|
8304
|
+
query.set("limit", String(options.limit));
|
|
8305
|
+
if (options.cursor !== undefined)
|
|
8306
|
+
query.set("cursor", options.cursor);
|
|
8307
|
+
return query.size ? `?${query}` : "";
|
|
8229
8308
|
}
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8309
|
+
function timestamp(value) {
|
|
8310
|
+
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z$/.test(value))
|
|
8311
|
+
return false;
|
|
8312
|
+
const time3 = Date.parse(value);
|
|
8313
|
+
return Number.isFinite(time3) && new Date(time3).toISOString().slice(0, 23) === value.slice(0, 23);
|
|
8314
|
+
}
|
|
8315
|
+
function parseMember(row, fail) {
|
|
8316
|
+
if (!record3(row) || !uuid2(row.membershipId) || !uuid2(row.userId) || typeof row.email !== "string" || !row.email || !(row.displayName === null || typeof row.displayName === "string") || !isRole(row.role) || !timestamp(row.createdAt))
|
|
8317
|
+
return fail();
|
|
8318
|
+
return {
|
|
8319
|
+
membershipId: row.membershipId,
|
|
8320
|
+
userId: row.userId,
|
|
8321
|
+
email: row.email,
|
|
8322
|
+
displayName: row.displayName,
|
|
8323
|
+
role: row.role,
|
|
8324
|
+
createdAt: row.createdAt
|
|
8325
|
+
};
|
|
8326
|
+
}
|
|
8327
|
+
function mutationInput(membershipId, input, roleChange) {
|
|
8328
|
+
if (typeof membershipId !== "string" || !uuid2(membershipId) || membershipId !== membershipId.toLowerCase() || !record3(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
|
|
8329
|
+
throw new WorkspaceMemberInputError;
|
|
8330
|
+
const expectedRole = input.expectedRole, role = roleChange ? input.role : undefined;
|
|
8331
|
+
if (!isRole(expectedRole) || roleChange && !isRole(role))
|
|
8332
|
+
throw new WorkspaceMemberInputError;
|
|
8333
|
+
return { membershipId, role, expectedRole };
|
|
8334
|
+
}
|
|
8335
|
+
function workspaceMemberRoleInput(membershipId, input) {
|
|
8336
|
+
const value = mutationInput(membershipId, input, true);
|
|
8337
|
+
return { membershipId: value.membershipId, body: { role: value.role, expectedRole: value.expectedRole } };
|
|
8338
|
+
}
|
|
8339
|
+
function workspaceMemberRemovalInput(membershipId, input) {
|
|
8340
|
+
const value = mutationInput(membershipId, input, false);
|
|
8341
|
+
return { membershipId: value.membershipId, body: { expectedRole: value.expectedRole } };
|
|
8342
|
+
}
|
|
8343
|
+
function parseWorkspaceMemberRoleResult(value, membershipId, role) {
|
|
8344
|
+
const fail = () => {
|
|
8345
|
+
throw new Error(invalidMemberResult);
|
|
8346
|
+
};
|
|
8347
|
+
if (!record3(value) || !uuid2(value.organizationId) || typeof value.changed !== "boolean")
|
|
8348
|
+
return fail();
|
|
8349
|
+
const member = parseMember(value.member, fail);
|
|
8350
|
+
if (member.membershipId !== membershipId || member.role !== role)
|
|
8351
|
+
return fail();
|
|
8352
|
+
return { organizationId: value.organizationId, member, changed: value.changed };
|
|
8353
|
+
}
|
|
8354
|
+
function parseWorkspaceMemberRemovalResult(value, membershipId) {
|
|
8355
|
+
if (!record3(value) || !uuid2(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
|
|
8356
|
+
throw new Error(invalidMemberResult);
|
|
8357
|
+
return { organizationId: value.organizationId, membershipId, removed: true, alreadyRemoved: value.alreadyRemoved };
|
|
8358
|
+
}
|
|
8359
|
+
function workspaceMemberFailure(value, status) {
|
|
8360
|
+
if (!record3(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
|
|
8361
|
+
return null;
|
|
8362
|
+
const code = value.code;
|
|
8363
|
+
return workspaceMemberFailures[code][0] === status ? code : null;
|
|
8364
|
+
}
|
|
8365
|
+
function parseWorkspaceMembersPage(value) {
|
|
8366
|
+
const fail = () => {
|
|
8367
|
+
throw new Error("The server returned an invalid workspace roster.");
|
|
8368
|
+
};
|
|
8369
|
+
if (!record3(value) || !uuid2(value.organizationId) || !Array.isArray(value.members) || value.members.length > 100 || !(value.nextCursor === null || cursor(value.nextCursor)) || !value.members.length && value.nextCursor !== null)
|
|
8370
|
+
return fail();
|
|
8371
|
+
const members = value.members.map((row) => parseMember(row, fail));
|
|
8372
|
+
if (new Set(members.map((row) => row.membershipId)).size !== members.length)
|
|
8373
|
+
return fail();
|
|
8374
|
+
return { organizationId: value.organizationId, members, nextCursor: value.nextCursor };
|
|
8375
|
+
}
|
|
8376
|
+
var record3 = (value) => !!value && typeof value === "object" && !Array.isArray(value), cursor = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,512}$/.test(value), uuid2 = (value) => typeof value === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value), isRole = (value) => typeof value === "string" && ["owner", "admin", "member", "viewer"].includes(value), WorkspaceMemberInputError, invalidMemberResult = "The server returned an invalid workspace member result. Refresh the roster before another action.", workspaceMemberFailures;
|
|
8377
|
+
var init_remote_workspace = __esm(() => {
|
|
8378
|
+
WorkspaceMemberInputError = class WorkspaceMemberInputError extends Error {
|
|
8379
|
+
constructor() {
|
|
8380
|
+
super("Use an unchanged lowercase membership ID and the exact role and expected-role parameters from the roster.");
|
|
8381
|
+
this.name = "WorkspaceMemberInputError";
|
|
8382
|
+
}
|
|
8383
|
+
};
|
|
8384
|
+
workspaceMemberFailures = {
|
|
8385
|
+
INVALID_REQUEST: [400, "Provide the exact membership role parameters."],
|
|
8386
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
8387
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Fresh interactive sign-in is required."],
|
|
8388
|
+
WORKSPACE_ADMIN_REQUIRED: [403, "A current workspace owner or admin is required."],
|
|
8389
|
+
MEMBERSHIP_ACTION_FORBIDDEN: [403, "Your current workspace role cannot perform this membership action."],
|
|
8390
|
+
MEMBERSHIP_NOT_FOUND: [404, "Membership was not found in the current workspace."],
|
|
8391
|
+
SELF_REMOVAL_UNAVAILABLE: [409, "Leaving your own workspace is not available through member removal."],
|
|
8392
|
+
MEMBERSHIP_ROLE_CHANGED: [409, "The member role changed. Refresh the roster before another action."],
|
|
8393
|
+
LAST_OWNER_REQUIRED: [409, "The workspace must retain at least one active owner."],
|
|
8394
|
+
MEMBERSHIP_BUSY: [503, "Membership is busy. Refresh the roster before another action."]
|
|
8395
|
+
};
|
|
8236
8396
|
});
|
|
8237
8397
|
|
|
8238
8398
|
// src/lib/remote-account.ts
|
|
@@ -8314,14 +8474,47 @@ var init_remote_account = __esm(() => {
|
|
|
8314
8474
|
};
|
|
8315
8475
|
});
|
|
8316
8476
|
|
|
8477
|
+
// src/lib/remote-profile.ts
|
|
8478
|
+
function customerNamePatch(input, field) {
|
|
8479
|
+
if (!isRecord4(input) || Object.keys(input).length !== 1 || !Object.hasOwn(input, field))
|
|
8480
|
+
throw new Error("Provide only the requested name field.");
|
|
8481
|
+
const value = input[field];
|
|
8482
|
+
if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
|
|
8483
|
+
throw new Error("Use a name of 1\u2013100 characters without control characters or newlines.");
|
|
8484
|
+
}
|
|
8485
|
+
return { [field]: value.trim() };
|
|
8486
|
+
}
|
|
8487
|
+
function isRecord4(value) {
|
|
8488
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8489
|
+
}
|
|
8490
|
+
function string4(value) {
|
|
8491
|
+
return typeof value === "string" && value.length > 0;
|
|
8492
|
+
}
|
|
8493
|
+
function parseUpdatedProfile(value) {
|
|
8494
|
+
const user = isRecord4(value) && value.user;
|
|
8495
|
+
if (!isRecord4(user) || !string4(user.id) || !string4(user.email) || !(user.displayName === null || typeof user.displayName === "string") || typeof user.role !== "string" || !["owner", "admin", "member", "viewer"].includes(user.role)) {
|
|
8496
|
+
throw new Error("The server returned an invalid account profile.");
|
|
8497
|
+
}
|
|
8498
|
+
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
8499
|
+
}
|
|
8500
|
+
function parseUpdatedWorkspace(value) {
|
|
8501
|
+
const organization = isRecord4(value) && value.organization;
|
|
8502
|
+
if (!isRecord4(organization) || !string4(organization.id) || !string4(organization.slug) || !string4(organization.name)) {
|
|
8503
|
+
throw new Error("The server returned an invalid workspace.");
|
|
8504
|
+
}
|
|
8505
|
+
return { organization: { id: organization.id, slug: organization.slug, name: organization.name } };
|
|
8506
|
+
}
|
|
8507
|
+
|
|
8317
8508
|
// src/lib/remote-client.ts
|
|
8318
8509
|
var exports_remote_client = {};
|
|
8319
8510
|
__export(exports_remote_client, {
|
|
8320
8511
|
createRemoteSkillsClientReadOnly: () => createRemoteSkillsClientReadOnly,
|
|
8321
8512
|
createRemoteSkillsClient: () => createRemoteSkillsClient,
|
|
8513
|
+
RemoteWorkspaceMemberError: () => RemoteWorkspaceMemberError,
|
|
8322
8514
|
RemoteSkillsClient: () => RemoteSkillsClient,
|
|
8323
8515
|
RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
|
|
8324
|
-
RemoteRequestError: () => RemoteRequestError
|
|
8516
|
+
RemoteRequestError: () => RemoteRequestError,
|
|
8517
|
+
RemoteCapabilityUnavailableError: () => RemoteCapabilityUnavailableError
|
|
8325
8518
|
});
|
|
8326
8519
|
|
|
8327
8520
|
class RemoteSkillsClient {
|
|
@@ -8351,9 +8544,14 @@ class RemoteSkillsClient {
|
|
|
8351
8544
|
if (response.status === 404 && opts.domainNotFoundCodes?.length && await responseBodyCarriesCode(response, opts.domainNotFoundCodes)) {
|
|
8352
8545
|
return response;
|
|
8353
8546
|
}
|
|
8547
|
+
response.body?.cancel().catch(() => {});
|
|
8354
8548
|
throw new RemoteRouteUnsupportedError(routePath, response.status, this.apiUrl);
|
|
8355
8549
|
}
|
|
8356
8550
|
if (!response.ok) {
|
|
8551
|
+
if (path === "/api/v1/billing/checkout" && options?.method === "POST" && response.status === 503 && await responseBodyCarriesCode(response, ["SUBSCRIPTION_CHECKOUT_UNAVAILABLE"])) {
|
|
8552
|
+
throw new RemoteCapabilityUnavailableError;
|
|
8553
|
+
}
|
|
8554
|
+
response.body?.cancel().catch(() => {});
|
|
8357
8555
|
throw new RemoteRequestError(routePath, response.status, response.statusText);
|
|
8358
8556
|
}
|
|
8359
8557
|
return response;
|
|
@@ -8437,6 +8635,58 @@ class RemoteSkillsClient {
|
|
|
8437
8635
|
async getIdentity() {
|
|
8438
8636
|
return (await this.requestNewRoute("/api/auth/whoami")).json();
|
|
8439
8637
|
}
|
|
8638
|
+
async updateProfile(input) {
|
|
8639
|
+
const body = customerNamePatch(input, "displayName");
|
|
8640
|
+
return parseUpdatedProfile(await (await this.requestNewRoute("/api/v1/account/profile", { method: "PATCH", body: JSON.stringify(body) })).json());
|
|
8641
|
+
}
|
|
8642
|
+
async updateCurrentWorkspace(input) {
|
|
8643
|
+
const body = customerNamePatch(input, "name");
|
|
8644
|
+
return parseUpdatedWorkspace(await (await this.requestNewRoute("/api/v1/workspaces/current", { method: "PATCH", body: JSON.stringify(body) })).json());
|
|
8645
|
+
}
|
|
8646
|
+
async listWorkspaceMembers(options = {}) {
|
|
8647
|
+
const query = workspaceMembersQuery(options);
|
|
8648
|
+
const requestedCursor = options.cursor;
|
|
8649
|
+
const response = await this.requestNewRoute(`/api/v1/workspace/members${query}`);
|
|
8650
|
+
let value;
|
|
8651
|
+
try {
|
|
8652
|
+
value = await response.json();
|
|
8653
|
+
} catch {
|
|
8654
|
+
throw new Error("The server returned an invalid workspace roster.");
|
|
8655
|
+
}
|
|
8656
|
+
const page = parseWorkspaceMembersPage(value);
|
|
8657
|
+
if (requestedCursor !== undefined && page.nextCursor === requestedCursor)
|
|
8658
|
+
throw new Error("The server returned an invalid workspace roster.");
|
|
8659
|
+
return page;
|
|
8660
|
+
}
|
|
8661
|
+
async setWorkspaceMemberRole(membershipId, input) {
|
|
8662
|
+
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
8663
|
+
const value = await this.requestWorkspaceMember(captured.membershipId, "PATCH", captured.body);
|
|
8664
|
+
return parseWorkspaceMemberRoleResult(value, captured.membershipId, captured.body.role);
|
|
8665
|
+
}
|
|
8666
|
+
async removeWorkspaceMember(membershipId, input) {
|
|
8667
|
+
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
8668
|
+
return parseWorkspaceMemberRemovalResult(await this.requestWorkspaceMember(captured.membershipId, "DELETE", captured.body), captured.membershipId);
|
|
8669
|
+
}
|
|
8670
|
+
async requestWorkspaceMember(membershipId, method, body) {
|
|
8671
|
+
const path = `/api/v1/workspace/members/${membershipId}`;
|
|
8672
|
+
const response = await this.request(path, { method, body: JSON.stringify(body) });
|
|
8673
|
+
let value;
|
|
8674
|
+
try {
|
|
8675
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 64 * 1024 : 4096)));
|
|
8676
|
+
} catch {
|
|
8677
|
+
if (response.ok)
|
|
8678
|
+
throw new Error(invalidMemberResult);
|
|
8679
|
+
}
|
|
8680
|
+
if (!response.ok) {
|
|
8681
|
+
const code = workspaceMemberFailure(value, response.status);
|
|
8682
|
+
if (code)
|
|
8683
|
+
throw new RemoteWorkspaceMemberError(path, code);
|
|
8684
|
+
if (response.status === 404 || response.status === 405)
|
|
8685
|
+
throw new RemoteRouteUnsupportedError(path, response.status, this.apiUrl);
|
|
8686
|
+
throw new RemoteRequestError(path, response.status);
|
|
8687
|
+
}
|
|
8688
|
+
return value;
|
|
8689
|
+
}
|
|
8440
8690
|
async listApiKeys() {
|
|
8441
8691
|
return this.arrayResponse("/api/auth/keys");
|
|
8442
8692
|
}
|
|
@@ -8628,8 +8878,10 @@ class RemoteSkillsClient {
|
|
|
8628
8878
|
return [];
|
|
8629
8879
|
if (!response.ok)
|
|
8630
8880
|
throw new Error(`versions request failed: ${response.status}`);
|
|
8631
|
-
const body = await response
|
|
8632
|
-
|
|
8881
|
+
const body = await readSkillVersionPayload(response);
|
|
8882
|
+
if (!isVersionRecord(body) || !Array.isArray(body.versions) || body.slug !== undefined && body.slug !== slug)
|
|
8883
|
+
throw new Error(INVALID_SKILL_VERSION_RESPONSE);
|
|
8884
|
+
return body.versions.map((entry) => normalizeSkillVersion(entry, slug));
|
|
8633
8885
|
}
|
|
8634
8886
|
async getSkillVersion(slug, version2) {
|
|
8635
8887
|
const response = await this.requestNewRoute(`/api/v1/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version2)}`, undefined, { domainNotFoundCodes: ["SKILL_NOT_FOUND", "SKILL_VERSION_NOT_FOUND"] });
|
|
@@ -8637,7 +8889,7 @@ class RemoteSkillsClient {
|
|
|
8637
8889
|
return null;
|
|
8638
8890
|
if (!response.ok)
|
|
8639
8891
|
throw new Error(`version request failed: ${response.status}`);
|
|
8640
|
-
return await response
|
|
8892
|
+
return normalizeSkillVersion(await readSkillVersionPayload(response), slug, version2);
|
|
8641
8893
|
}
|
|
8642
8894
|
async listPins() {
|
|
8643
8895
|
const response = await this.requestNewRoute("/api/v1/pins");
|
|
@@ -8685,31 +8937,47 @@ class RemoteSkillsClient {
|
|
|
8685
8937
|
return normalizeUpdatedSincePage(await response.json());
|
|
8686
8938
|
}
|
|
8687
8939
|
}
|
|
8688
|
-
function requireOptionalString(
|
|
8689
|
-
if (
|
|
8940
|
+
function requireOptionalString(record4, field) {
|
|
8941
|
+
if (record4[field] === undefined)
|
|
8690
8942
|
return;
|
|
8691
|
-
if (typeof
|
|
8943
|
+
if (typeof record4[field] !== "string") {
|
|
8692
8944
|
throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
|
|
8693
8945
|
}
|
|
8694
|
-
return
|
|
8946
|
+
return record4[field];
|
|
8947
|
+
}
|
|
8948
|
+
function isVersionRecord(value) {
|
|
8949
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
8950
|
+
}
|
|
8951
|
+
async function readSkillVersionPayload(response) {
|
|
8952
|
+
try {
|
|
8953
|
+
return await response.json();
|
|
8954
|
+
} catch {
|
|
8955
|
+
throw new Error(INVALID_SKILL_VERSION_RESPONSE);
|
|
8956
|
+
}
|
|
8957
|
+
}
|
|
8958
|
+
function normalizeSkillVersion(entry, slug, version2) {
|
|
8959
|
+
if (!isVersionRecord(entry) || typeof entry.slug !== "string" || !entry.slug.trim() || entry.slug !== slug || typeof entry.version !== "string" || !entry.version.trim() || version2 !== undefined && entry.version !== version2 || typeof entry.bundleSha256 !== "string" || !/^[a-f0-9]{64}$/i.test(entry.bundleSha256) || typeof entry.bundleByteSize !== "number" || !Number.isSafeInteger(entry.bundleByteSize) || entry.bundleByteSize < 0 || typeof entry.createdAt !== "string" || !entry.createdAt.trim() || entry.current !== undefined && typeof entry.current !== "boolean" || entry.storageKind !== undefined && typeof entry.storageKind !== "string" || entry.manifest !== undefined && !isVersionRecord(entry.manifest)) {
|
|
8960
|
+
throw new Error(INVALID_SKILL_VERSION_RESPONSE);
|
|
8961
|
+
}
|
|
8962
|
+
return entry;
|
|
8695
8963
|
}
|
|
8696
8964
|
function normalizePin(entry) {
|
|
8697
8965
|
if (!entry || typeof entry !== "object") {
|
|
8698
8966
|
throw new Error("Remote pin payload did not match the expected contract (expected an object)");
|
|
8699
8967
|
}
|
|
8700
|
-
const
|
|
8701
|
-
const slug = typeof
|
|
8968
|
+
const record4 = entry;
|
|
8969
|
+
const slug = typeof record4.slug === "string" && record4.slug.trim() ? record4.slug.trim() : undefined;
|
|
8702
8970
|
if (!slug) {
|
|
8703
8971
|
throw new Error("Remote pin payload did not match the expected contract (missing slug)");
|
|
8704
8972
|
}
|
|
8705
8973
|
let metadata;
|
|
8706
|
-
if (
|
|
8707
|
-
if (!
|
|
8974
|
+
if (record4.metadata !== undefined) {
|
|
8975
|
+
if (!record4.metadata || typeof record4.metadata !== "object" || Array.isArray(record4.metadata)) {
|
|
8708
8976
|
throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
|
|
8709
8977
|
}
|
|
8710
|
-
metadata =
|
|
8978
|
+
metadata = record4.metadata;
|
|
8711
8979
|
}
|
|
8712
|
-
const pinnedAt = requireOptionalString(
|
|
8980
|
+
const pinnedAt = requireOptionalString(record4, "pinnedAt");
|
|
8713
8981
|
return {
|
|
8714
8982
|
slug,
|
|
8715
8983
|
...pinnedAt !== undefined ? { pinnedAt } : {},
|
|
@@ -8726,16 +8994,16 @@ function normalizeSkillSummary(entry) {
|
|
|
8726
8994
|
if (!entry || typeof entry !== "object") {
|
|
8727
8995
|
throw new Error("Remote skill payload did not match the expected contract (expected an object)");
|
|
8728
8996
|
}
|
|
8729
|
-
const
|
|
8730
|
-
const slug = typeof
|
|
8997
|
+
const record4 = entry;
|
|
8998
|
+
const slug = typeof record4.slug === "string" && record4.slug.trim() ? record4.slug.trim() : undefined;
|
|
8731
8999
|
if (!slug) {
|
|
8732
9000
|
throw new Error("Remote skill payload did not match the expected contract (missing slug)");
|
|
8733
9001
|
}
|
|
8734
9002
|
return {
|
|
8735
9003
|
slug,
|
|
8736
|
-
...requireOptionalString(
|
|
8737
|
-
...requireOptionalString(
|
|
8738
|
-
...requireOptionalString(
|
|
9004
|
+
...requireOptionalString(record4, "name") !== undefined ? { name: requireOptionalString(record4, "name") } : {},
|
|
9005
|
+
...requireOptionalString(record4, "version") !== undefined ? { version: requireOptionalString(record4, "version") } : {},
|
|
9006
|
+
...requireOptionalString(record4, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record4, "updatedAt") } : {}
|
|
8739
9007
|
};
|
|
8740
9008
|
}
|
|
8741
9009
|
function normalizeSkillSummaryList(payload) {
|
|
@@ -8745,26 +9013,55 @@ function normalizeSkillSummaryList(payload) {
|
|
|
8745
9013
|
return payload.map(normalizeSkillSummary);
|
|
8746
9014
|
}
|
|
8747
9015
|
async function responseBodyCarriesCode(response, codes) {
|
|
9016
|
+
const reader = response.body?.getReader();
|
|
9017
|
+
if (!reader)
|
|
9018
|
+
return false;
|
|
9019
|
+
const maximum = 8 * 1024;
|
|
9020
|
+
let deadline;
|
|
9021
|
+
const expired = new Promise((_, reject) => {
|
|
9022
|
+
deadline = setTimeout(() => reject(new Error("Error response read deadline exceeded")), 1000);
|
|
9023
|
+
});
|
|
8748
9024
|
try {
|
|
8749
|
-
const
|
|
8750
|
-
|
|
9025
|
+
const chunks = [];
|
|
9026
|
+
let size = 0;
|
|
9027
|
+
while (true) {
|
|
9028
|
+
const next = await Promise.race([reader.read(), expired]);
|
|
9029
|
+
if (next.done)
|
|
9030
|
+
break;
|
|
9031
|
+
size += next.value.byteLength;
|
|
9032
|
+
if (size > maximum)
|
|
9033
|
+
return false;
|
|
9034
|
+
chunks.push(next.value);
|
|
9035
|
+
}
|
|
9036
|
+
const bytes = new Uint8Array(size);
|
|
9037
|
+
let offset = 0;
|
|
9038
|
+
for (const chunk of chunks) {
|
|
9039
|
+
bytes.set(chunk, offset);
|
|
9040
|
+
offset += chunk.byteLength;
|
|
9041
|
+
}
|
|
9042
|
+
const payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
9043
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload) || !Object.hasOwn(payload, "code"))
|
|
8751
9044
|
return false;
|
|
8752
9045
|
const code = payload.code;
|
|
8753
9046
|
return typeof code === "string" && codes.includes(code);
|
|
8754
9047
|
} catch {
|
|
8755
9048
|
return false;
|
|
9049
|
+
} finally {
|
|
9050
|
+
clearTimeout(deadline);
|
|
9051
|
+
reader.cancel().catch(() => {});
|
|
9052
|
+
reader.releaseLock();
|
|
8756
9053
|
}
|
|
8757
9054
|
}
|
|
8758
9055
|
function normalizeUpdatedSincePage(payload) {
|
|
8759
9056
|
if (!payload || typeof payload !== "object") {
|
|
8760
9057
|
throw new Error("Updated-since payload did not match the expected contract (expected an object)");
|
|
8761
9058
|
}
|
|
8762
|
-
const
|
|
8763
|
-
if (!Array.isArray(
|
|
9059
|
+
const record4 = payload;
|
|
9060
|
+
if (!Array.isArray(record4.skills)) {
|
|
8764
9061
|
throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
|
|
8765
9062
|
}
|
|
8766
|
-
const skills =
|
|
8767
|
-
const nextCursor =
|
|
9063
|
+
const skills = record4.skills.map(normalizeSkillSummary);
|
|
9064
|
+
const nextCursor = record4.nextCursor === undefined || record4.nextCursor === null ? null : record4.nextCursor;
|
|
8768
9065
|
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
8769
9066
|
throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
|
|
8770
9067
|
}
|
|
@@ -8777,8 +9074,10 @@ async function createRemoteSkillsClient(env = process.env) {
|
|
|
8777
9074
|
function createRemoteSkillsClientReadOnly(env = process.env) {
|
|
8778
9075
|
return createRemoteSkillsClient(env);
|
|
8779
9076
|
}
|
|
8780
|
-
var RemoteRouteUnsupportedError, RemoteRequestError;
|
|
9077
|
+
var RemoteRouteUnsupportedError, RemoteRequestError, RemoteWorkspaceMemberError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
8781
9078
|
var init_remote_client = __esm(() => {
|
|
9079
|
+
init_remote_workspace();
|
|
9080
|
+
init_remote_workspace();
|
|
8782
9081
|
init_auth_store();
|
|
8783
9082
|
init_fleet_credentials();
|
|
8784
9083
|
init_remote_account();
|
|
@@ -8798,13 +9097,30 @@ var init_remote_client = __esm(() => {
|
|
|
8798
9097
|
RemoteRequestError = class RemoteRequestError extends Error {
|
|
8799
9098
|
path;
|
|
8800
9099
|
status;
|
|
8801
|
-
constructor(path, status,
|
|
8802
|
-
super(`Remote request to ${path} failed: HTTP ${status}
|
|
9100
|
+
constructor(path, status, _statusText) {
|
|
9101
|
+
super(`Remote request to ${path} failed: HTTP ${status}`);
|
|
8803
9102
|
this.path = path;
|
|
8804
9103
|
this.status = status;
|
|
8805
9104
|
this.name = "RemoteRequestError";
|
|
8806
9105
|
}
|
|
8807
9106
|
};
|
|
9107
|
+
RemoteWorkspaceMemberError = class RemoteWorkspaceMemberError extends RemoteRequestError {
|
|
9108
|
+
code;
|
|
9109
|
+
constructor(path, code) {
|
|
9110
|
+
super(path, workspaceMemberFailures[code][0]);
|
|
9111
|
+
this.code = code;
|
|
9112
|
+
this.name = "RemoteWorkspaceMemberError";
|
|
9113
|
+
this.message = workspaceMemberFailures[code][1];
|
|
9114
|
+
}
|
|
9115
|
+
};
|
|
9116
|
+
RemoteCapabilityUnavailableError = class RemoteCapabilityUnavailableError extends RemoteRequestError {
|
|
9117
|
+
code = "SUBSCRIPTION_CHECKOUT_UNAVAILABLE";
|
|
9118
|
+
constructor() {
|
|
9119
|
+
super("/api/v1/billing/checkout", 503);
|
|
9120
|
+
this.name = "RemoteCapabilityUnavailableError";
|
|
9121
|
+
this.message = "Subscription checkout is unavailable on the configured Skills server. " + "Use skills credits packs to view credit packs, or skills billing portal to manage an existing subscription.";
|
|
9122
|
+
}
|
|
9123
|
+
};
|
|
8808
9124
|
});
|
|
8809
9125
|
|
|
8810
9126
|
// ../../node_modules/.bun/content-type@1.0.5/node_modules/content-type/index.js
|
|
@@ -8831,7 +9147,7 @@ var require_content_type = __commonJS((exports) => {
|
|
|
8831
9147
|
if (!type || !TYPE_REGEXP.test(type)) {
|
|
8832
9148
|
throw new TypeError("invalid type");
|
|
8833
9149
|
}
|
|
8834
|
-
var
|
|
9150
|
+
var string5 = type;
|
|
8835
9151
|
if (parameters && typeof parameters === "object") {
|
|
8836
9152
|
var param;
|
|
8837
9153
|
var params = Object.keys(parameters).sort();
|
|
@@ -8840,16 +9156,16 @@ var require_content_type = __commonJS((exports) => {
|
|
|
8840
9156
|
if (!TOKEN_REGEXP.test(param)) {
|
|
8841
9157
|
throw new TypeError("invalid parameter name");
|
|
8842
9158
|
}
|
|
8843
|
-
|
|
9159
|
+
string5 += "; " + param + "=" + qstring(parameters[param]);
|
|
8844
9160
|
}
|
|
8845
9161
|
}
|
|
8846
|
-
return
|
|
9162
|
+
return string5;
|
|
8847
9163
|
}
|
|
8848
|
-
function parse6(
|
|
8849
|
-
if (!
|
|
9164
|
+
function parse6(string5) {
|
|
9165
|
+
if (!string5) {
|
|
8850
9166
|
throw new TypeError("argument string is required");
|
|
8851
9167
|
}
|
|
8852
|
-
var header = typeof
|
|
9168
|
+
var header = typeof string5 === "object" ? getcontenttype(string5) : string5;
|
|
8853
9169
|
if (typeof header !== "string") {
|
|
8854
9170
|
throw new TypeError("argument string is required to be a string");
|
|
8855
9171
|
}
|
|
@@ -14323,7 +14639,7 @@ class StdioServerTransport {
|
|
|
14323
14639
|
// package.json
|
|
14324
14640
|
var package_default = {
|
|
14325
14641
|
name: "@hasna/skills",
|
|
14326
|
-
version: "0.
|
|
14642
|
+
version: "0.5.0",
|
|
14327
14643
|
description: "Skills library for AI coding agents",
|
|
14328
14644
|
type: "module",
|
|
14329
14645
|
bin: {
|
|
@@ -14355,6 +14671,7 @@ var package_default = {
|
|
|
14355
14671
|
files: [
|
|
14356
14672
|
"dist/",
|
|
14357
14673
|
"!dist/**/*.test.d.ts",
|
|
14674
|
+
"!dist/**/*.fixture.d.ts",
|
|
14358
14675
|
"!dist/test-preload.d.ts",
|
|
14359
14676
|
"!dist/platform",
|
|
14360
14677
|
"bin/",
|
|
@@ -14380,8 +14697,9 @@ var package_default = {
|
|
|
14380
14697
|
migrate: "bun run ./src/server/migrate.ts",
|
|
14381
14698
|
typecheck: "tsc --noEmit",
|
|
14382
14699
|
"verify:release": "bun run scripts/release-guard.ts",
|
|
14700
|
+
"verify:consumer-types": "bun run scripts/consumer-types.ts",
|
|
14383
14701
|
prepare: "bun run build:js",
|
|
14384
|
-
prepack: "bun run build && bun run verify:release",
|
|
14702
|
+
prepack: "bun run build && bun run verify:release && bun run verify:consumer-types",
|
|
14385
14703
|
prepublishOnly: "bun run typecheck && bun run test"
|
|
14386
14704
|
},
|
|
14387
14705
|
keywords: [
|
|
@@ -14403,6 +14721,7 @@ var package_default = {
|
|
|
14403
14721
|
author: "Hasna",
|
|
14404
14722
|
license: "Apache-2.0",
|
|
14405
14723
|
devDependencies: {
|
|
14724
|
+
"@hasna/contracts": "1.0.2",
|
|
14406
14725
|
"@types/bun": "1.3.14",
|
|
14407
14726
|
"@types/node": "25.2.3",
|
|
14408
14727
|
"@types/react": "^18.2.0",
|
|
@@ -14414,8 +14733,7 @@ var package_default = {
|
|
|
14414
14733
|
dependencies: {
|
|
14415
14734
|
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
14416
14735
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
14417
|
-
"@hasna/
|
|
14418
|
-
"@hasna/events": "0.1.16",
|
|
14736
|
+
"@hasna/events": "0.1.18",
|
|
14419
14737
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
14420
14738
|
chalk: "^5.3.0",
|
|
14421
14739
|
commander: "^12.1.0",
|
|
@@ -22331,12 +22649,7 @@ import { homedir } from "os";
|
|
|
22331
22649
|
import { join, resolve } from "path";
|
|
22332
22650
|
import { homedir as pathsResolverHomedir } from "os";
|
|
22333
22651
|
import { join as pathsResolverJoin } from "path";
|
|
22334
|
-
var
|
|
22335
|
-
config: "HASNA_CONFIG_HOME",
|
|
22336
|
-
data: "HASNA_DATA_HOME",
|
|
22337
|
-
state: "HASNA_STATE_HOME",
|
|
22338
|
-
cache: "HASNA_CACHE_HOME"
|
|
22339
|
-
};
|
|
22652
|
+
var PATHS_RESOLVER_DATA_ENV = "HASNA_DATA_HOME";
|
|
22340
22653
|
var PATHS_RESOLVER_APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
22341
22654
|
function pathsResolverAssertApp(app) {
|
|
22342
22655
|
if (typeof app !== "string" || app.length === 0) {
|
|
@@ -22346,48 +22659,19 @@ function pathsResolverAssertApp(app) {
|
|
|
22346
22659
|
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
22347
22660
|
}
|
|
22348
22661
|
}
|
|
22349
|
-
function
|
|
22350
|
-
if (!Object.keys(PATHS_RESOLVER_KIND_ENV).includes(kind)) {
|
|
22351
|
-
throw new TypeError(`paths: invalid path kind "${kind}" \u2014 expected one of ${Object.keys(PATHS_RESOLVER_KIND_ENV).join(", ")}`);
|
|
22352
|
-
}
|
|
22353
|
-
}
|
|
22354
|
-
function pathsResolverBaseDir(kind, options) {
|
|
22355
|
-
pathsResolverAssertKind(kind);
|
|
22662
|
+
function pathsResolverDataBaseDir(options) {
|
|
22356
22663
|
const env = options.env ?? process.env;
|
|
22357
|
-
const override = env[
|
|
22664
|
+
const override = env[PATHS_RESOLVER_DATA_ENV];
|
|
22358
22665
|
if (typeof override === "string" && override.length > 0)
|
|
22359
22666
|
return override;
|
|
22360
22667
|
const home = options.home ?? pathsResolverHomedir();
|
|
22361
22668
|
const platform = options.platform ?? process.platform;
|
|
22362
|
-
|
|
22363
|
-
switch (kind) {
|
|
22364
|
-
case "config":
|
|
22365
|
-
case "data":
|
|
22366
|
-
return pathsResolverJoin(home, "Library", "Application Support", "Hasna");
|
|
22367
|
-
case "cache":
|
|
22368
|
-
return pathsResolverJoin(home, "Library", "Caches", "Hasna");
|
|
22369
|
-
case "state":
|
|
22370
|
-
return pathsResolverJoin(home, "Library", "Logs", "Hasna");
|
|
22371
|
-
}
|
|
22372
|
-
}
|
|
22373
|
-
switch (kind) {
|
|
22374
|
-
case "config":
|
|
22375
|
-
return pathsResolverJoin(home, ".config", "hasna");
|
|
22376
|
-
case "data":
|
|
22377
|
-
return pathsResolverJoin(home, ".local", "share", "hasna");
|
|
22378
|
-
case "state":
|
|
22379
|
-
return pathsResolverJoin(home, ".local", "state", "hasna");
|
|
22380
|
-
case "cache":
|
|
22381
|
-
return pathsResolverJoin(home, ".cache", "hasna");
|
|
22382
|
-
}
|
|
22383
|
-
}
|
|
22384
|
-
function pathsResolverResolve(kind, options) {
|
|
22385
|
-
pathsResolverAssertApp(options.app);
|
|
22386
|
-
const appSegment = options.internal === true ? pathsResolverJoin("internal", options.app) : options.app;
|
|
22387
|
-
return pathsResolverJoin(pathsResolverBaseDir(kind, options), appSegment);
|
|
22669
|
+
return platform === "darwin" ? pathsResolverJoin(home, "Library", "Application Support", "Hasna") : pathsResolverJoin(home, ".local", "share", "hasna");
|
|
22388
22670
|
}
|
|
22389
22671
|
function dataDir(options) {
|
|
22390
|
-
|
|
22672
|
+
pathsResolverAssertApp(options.app);
|
|
22673
|
+
const appSegment = options.internal === true ? pathsResolverJoin("internal", options.app) : options.app;
|
|
22674
|
+
return pathsResolverJoin(pathsResolverDataBaseDir(options), appSegment);
|
|
22391
22675
|
}
|
|
22392
22676
|
var DATA_DIR_ENV = "HASNA_SKILLS_DIR";
|
|
22393
22677
|
var HASNA_SKILLS_HOME_ENV = "HASNA_SKILLS_HOME";
|
|
@@ -23967,14 +24251,27 @@ function normalizePortableSkillName(name) {
|
|
|
23967
24251
|
}
|
|
23968
24252
|
return normalized;
|
|
23969
24253
|
}
|
|
24254
|
+
function normalizeNewPortableSkillName(name) {
|
|
24255
|
+
normalizePortableSkillName(name);
|
|
24256
|
+
const normalized = name.trim().replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
24257
|
+
if (!normalized)
|
|
24258
|
+
throw new Error(`Invalid skill name '${name}'. Include letters or numbers.`);
|
|
24259
|
+
return normalized;
|
|
24260
|
+
}
|
|
23970
24261
|
function readPortableSkillManifest(skillPath, fallbackName = basename(skillPath)) {
|
|
24262
|
+
return readManifest(skillPath, fallbackName, normalizePortableSkillName);
|
|
24263
|
+
}
|
|
24264
|
+
function readPortableSkillManifestForImport(skillPath) {
|
|
24265
|
+
return readManifest(skillPath, basename(skillPath), normalizeNewPortableSkillName);
|
|
24266
|
+
}
|
|
24267
|
+
function readManifest(skillPath, fallbackName, normalizeName) {
|
|
23971
24268
|
const skillJsonPath = join6(skillPath, "skill.json");
|
|
23972
24269
|
const skillMdPath = join6(skillPath, "SKILL.md");
|
|
23973
24270
|
const pkgPath = join6(skillPath, "package.json");
|
|
23974
24271
|
const jsonManifest = existsSync6(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
23975
24272
|
const frontmatter = existsSync6(skillMdPath) ? parseSkillFrontmatter(readFileSync5(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
23976
24273
|
const pkg = existsSync6(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
23977
|
-
const name =
|
|
24274
|
+
const name = normalizeName(stringField(jsonManifest, "name") ?? frontmatter?.name ?? stringValue(pkg?.name) ?? fallbackName);
|
|
23978
24275
|
const description = stringField(jsonManifest, "description") ?? frontmatter?.description ?? stringValue(pkg?.description) ?? `${name} skill`;
|
|
23979
24276
|
const version2 = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
23980
24277
|
const kind = parseSkillKind(stringField(jsonManifest, "kind") ?? frontmatter?.kind);
|
|
@@ -24222,10 +24519,45 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
24222
24519
|
};
|
|
24223
24520
|
if (!existsSync6(join6(skillPath, "SKILL.md"))) {
|
|
24224
24521
|
writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
24522
|
+
} else {
|
|
24523
|
+
const path = join6(skillPath, "SKILL.md");
|
|
24524
|
+
const content = readFileSync5(path, "utf8");
|
|
24525
|
+
const declaredName = parseSkillFrontmatter(content)?.name;
|
|
24526
|
+
if (declaredName && declaredName !== next.name) {
|
|
24527
|
+
writeFileSync2(path, renameInstructionFrontmatter(content, next.name));
|
|
24528
|
+
}
|
|
24529
|
+
}
|
|
24530
|
+
const packagePath = join6(skillPath, "package.json");
|
|
24531
|
+
if (existsSync6(packagePath)) {
|
|
24532
|
+
const pkg = readJsonObject(packagePath);
|
|
24533
|
+
if (typeof pkg.name === "string" && pkg.name !== next.name) {
|
|
24534
|
+
writeFileSync2(packagePath, `${JSON.stringify({ ...pkg, name: next.name }, null, 2)}
|
|
24535
|
+
`);
|
|
24536
|
+
}
|
|
24225
24537
|
}
|
|
24226
24538
|
writeSkillJsonWithHash(skillPath, next);
|
|
24227
24539
|
return readPortableSkillManifest(skillPath, next.name);
|
|
24228
24540
|
}
|
|
24541
|
+
function renameInstructionFrontmatter(content, name) {
|
|
24542
|
+
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?=\r?\n|$)/);
|
|
24543
|
+
const names = frontmatter?.[1]?.match(/^[ \t]*name[ \t]*:[^\r\n]*/gm) ?? [];
|
|
24544
|
+
const declaration = names.length === 1 ? names[0].match(/^(name[ \t]*:[ \t]*)(.*?)([ \t]*)$/) : null;
|
|
24545
|
+
const scalar = declaration?.[2] ?? "";
|
|
24546
|
+
let simple = /^[a-zA-Z0-9_.@/ -]+$/.test(scalar);
|
|
24547
|
+
if (scalar.startsWith('"')) {
|
|
24548
|
+
try {
|
|
24549
|
+
simple = typeof JSON.parse(scalar) === "string";
|
|
24550
|
+
} catch {
|
|
24551
|
+
simple = false;
|
|
24552
|
+
}
|
|
24553
|
+
} else if (scalar.startsWith("'"))
|
|
24554
|
+
simple = /^'[^'\r\n]*'$/.test(scalar);
|
|
24555
|
+
if (!frontmatter || !declaration || !simple) {
|
|
24556
|
+
throw new Error("Cannot rename instruction SKILL.md: use one unambiguous top-level name scalar in frontmatter.");
|
|
24557
|
+
}
|
|
24558
|
+
const renamed = frontmatter[0].replace(/^name[ \t]*:[^\r\n]*/m, () => `${declaration[1]}${name}${declaration[3]}`);
|
|
24559
|
+
return renamed + content.slice(frontmatter[0].length);
|
|
24560
|
+
}
|
|
24229
24561
|
function copySkillDirectory(source, destination) {
|
|
24230
24562
|
const resolvedSource = lstatSync2(source).isSymbolicLink() ? realpathSync(source) : source;
|
|
24231
24563
|
mkdirSync2(destination, { recursive: true });
|
|
@@ -24624,7 +24956,7 @@ function isOfficialSkillName(name) {
|
|
|
24624
24956
|
return OFFICIAL_SKILL_NAMES.has(name);
|
|
24625
24957
|
}
|
|
24626
24958
|
function scaffoldPortableSkill(name, options = {}) {
|
|
24627
|
-
const skillName =
|
|
24959
|
+
const skillName = normalizeNewPortableSkillName(name);
|
|
24628
24960
|
const root = getPortableSkillsRoot(options);
|
|
24629
24961
|
const skillPath = join7(root, skillName);
|
|
24630
24962
|
if (existsSync7(skillPath)) {
|
|
@@ -24648,9 +24980,9 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
24648
24980
|
if (!existsSync7(absoluteSource) || !statSync6(absoluteSource).isDirectory()) {
|
|
24649
24981
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
24650
24982
|
}
|
|
24651
|
-
const inferred =
|
|
24983
|
+
const inferred = readPortableSkillManifestForImport(absoluteSource);
|
|
24652
24984
|
const explicitName = options.name != null;
|
|
24653
|
-
const skillName =
|
|
24985
|
+
const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
|
|
24654
24986
|
if (isOfficialSkillName(skillName) && !options.allowShadow) {
|
|
24655
24987
|
const sourceSlug = safeNormalizeName(basename2(absoluteSource));
|
|
24656
24988
|
const via = explicitName ? `Name '${skillName}' matches a bundled official skill.` : `Inferred name '${skillName}'${sourceSlug && sourceSlug !== skillName ? ` (from source folder '${basename2(absoluteSource)}')` : ""} matches a bundled official skill.`;
|
|
@@ -25131,44 +25463,321 @@ function mergeCustomSkills(skills) {
|
|
|
25131
25463
|
return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
25132
25464
|
}
|
|
25133
25465
|
|
|
25134
|
-
// src/lib/
|
|
25135
|
-
|
|
25136
|
-
import { dirname as dirname4, join as join10 } from "path";
|
|
25137
|
-
import { homedir as homedir2 } from "os";
|
|
25138
|
-
import { fileURLToPath } from "url";
|
|
25139
|
-
// src/lib/utils.ts
|
|
25140
|
-
function normalizeSkillName(name) {
|
|
25141
|
-
return name;
|
|
25142
|
-
}
|
|
25466
|
+
// src/lib/read-access.ts
|
|
25467
|
+
init_fleet_credentials();
|
|
25143
25468
|
|
|
25144
|
-
// src/lib/
|
|
25145
|
-
|
|
25146
|
-
|
|
25147
|
-
var
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25151
|
-
"
|
|
25152
|
-
"private-hosted",
|
|
25153
|
-
"upstream",
|
|
25154
|
-
"extension",
|
|
25155
|
-
"local"
|
|
25156
|
-
];
|
|
25157
|
-
var SKILLS_PROJECT_DIR = ".skills";
|
|
25158
|
-
var PROJECT_CONFIG_FILE = "project.json";
|
|
25159
|
-
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
25160
|
-
function getProjectStateDir(targetDir = process.cwd()) {
|
|
25161
|
-
return join9(targetDir, SKILLS_PROJECT_DIR);
|
|
25162
|
-
}
|
|
25163
|
-
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
25164
|
-
return join9(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
25469
|
+
// src/lib/api-url.ts
|
|
25470
|
+
init_fleet_credentials();
|
|
25471
|
+
init_fleet_credentials();
|
|
25472
|
+
var API_URL_ENV_VAR = SKILLS_API_URL_ENV;
|
|
25473
|
+
var 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>`;
|
|
25474
|
+
function resolveApiUrl(env = process.env, options = {}) {
|
|
25475
|
+
const fleet = resolveSkillsFleet(env, options);
|
|
25476
|
+
return fleet.mode === "hosted" ? fleet.apiOrigin : undefined;
|
|
25165
25477
|
}
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25169
|
-
|
|
25478
|
+
|
|
25479
|
+
// src/lib/remote-registry.ts
|
|
25480
|
+
init_fleet_credentials();
|
|
25481
|
+
|
|
25482
|
+
// src/lib/discovery.ts
|
|
25483
|
+
var VENDOR_TERMS = [
|
|
25484
|
+
"Google Gemini",
|
|
25485
|
+
"OpenAI Sora",
|
|
25486
|
+
"MiniMax Hailuo",
|
|
25487
|
+
"Claude Code",
|
|
25488
|
+
"Claude Vision",
|
|
25489
|
+
"DALL-E 3",
|
|
25490
|
+
"GPT-4o Mini",
|
|
25491
|
+
"Cerebras",
|
|
25492
|
+
"OpenRouter",
|
|
25493
|
+
"Firecrawl",
|
|
25494
|
+
"ElevenLabs",
|
|
25495
|
+
"Anthropic",
|
|
25496
|
+
"OpenAI",
|
|
25497
|
+
"Minimax",
|
|
25498
|
+
"MiniMax",
|
|
25499
|
+
"Gemini",
|
|
25500
|
+
"Claude",
|
|
25501
|
+
"Whisper",
|
|
25502
|
+
"Seedance",
|
|
25503
|
+
"Lyria",
|
|
25504
|
+
"Sora",
|
|
25505
|
+
"Veo",
|
|
25506
|
+
"Exa.ai",
|
|
25507
|
+
"Exa",
|
|
25508
|
+
"XAI",
|
|
25509
|
+
"xAI"
|
|
25510
|
+
];
|
|
25511
|
+
var VENDOR_TAGS = new Set([
|
|
25512
|
+
"anthropic",
|
|
25513
|
+
"cerebras",
|
|
25514
|
+
"claude",
|
|
25515
|
+
"exa",
|
|
25516
|
+
"firecrawl",
|
|
25517
|
+
"gemini",
|
|
25518
|
+
"google",
|
|
25519
|
+
"minimax",
|
|
25520
|
+
"openai",
|
|
25521
|
+
"openrouter",
|
|
25522
|
+
"seedance",
|
|
25523
|
+
"whisper",
|
|
25524
|
+
"xai"
|
|
25525
|
+
]);
|
|
25526
|
+
var vendorPattern = new RegExp(`\\b(${VENDOR_TERMS.map(escapeRegExp).join("|")})\\b`, "gi");
|
|
25527
|
+
function getCompactSkillDiscovery(skill) {
|
|
25528
|
+
return {
|
|
25529
|
+
name: skill.name,
|
|
25530
|
+
category: skill.category,
|
|
25531
|
+
description: sanitizePublicDiscoveryText(skill.description)
|
|
25532
|
+
};
|
|
25533
|
+
}
|
|
25534
|
+
function getPublicSkillDiscovery(skill) {
|
|
25535
|
+
return {
|
|
25536
|
+
...skill,
|
|
25537
|
+
description: sanitizePublicDiscoveryText(skill.description),
|
|
25538
|
+
tags: publicDiscoveryTags(skill.tags)
|
|
25539
|
+
};
|
|
25540
|
+
}
|
|
25541
|
+
function publicDiscoveryTags(tags) {
|
|
25542
|
+
return tags.filter((tag) => !VENDOR_TAGS.has(tag.toLowerCase()));
|
|
25543
|
+
}
|
|
25544
|
+
function sanitizePublicDiscoveryText(text) {
|
|
25545
|
+
let sanitized = text.replace(vendorPattern, "hosted AI").replace(/\bLLM\b/g, "AI").replace(/\s{2,}/g, " ");
|
|
25546
|
+
let previous;
|
|
25547
|
+
do {
|
|
25548
|
+
previous = sanitized;
|
|
25549
|
+
sanitized = sanitized.replace(/\bhosted AI(?: providers)?\s*,\s*hosted AI(?: providers)?\b/gi, "hosted AI providers").replace(/\bhosted AI(?: providers)?\s*,?\s*and\s*hosted AI(?: providers)?\b/gi, "hosted AI providers").replace(/\bhosted AI(?: providers)?\s+or\s+hosted AI(?: providers)?\b/gi, "hosted AI providers").replace(/\bhosted AI providers\s+hosted AI\b/gi, "hosted AI providers").replace(/\bhosted AI providers\s+providers\b/gi, "hosted AI providers");
|
|
25550
|
+
} while (sanitized !== previous);
|
|
25551
|
+
return sanitized.trim();
|
|
25552
|
+
}
|
|
25553
|
+
function publicDiscoveryEnvVars(_skillName, envVars) {
|
|
25554
|
+
return envVars;
|
|
25555
|
+
}
|
|
25556
|
+
function publicDiscoveryDependencies(_skillName, dependencies) {
|
|
25557
|
+
return dependencies;
|
|
25558
|
+
}
|
|
25559
|
+
function publicDiscoveryDocumentation(_skill, documentation) {
|
|
25560
|
+
return documentation;
|
|
25561
|
+
}
|
|
25562
|
+
function escapeRegExp(value) {
|
|
25563
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25564
|
+
}
|
|
25565
|
+
|
|
25566
|
+
// src/lib/remote-registry.ts
|
|
25567
|
+
var remoteAvailabilitySchema = exports_external.object({
|
|
25568
|
+
status: exports_external.enum(["available", "unavailable"]),
|
|
25569
|
+
code: exports_external.string().optional(),
|
|
25570
|
+
message: exports_external.string().optional(),
|
|
25571
|
+
details: exports_external.array(exports_external.string()).optional()
|
|
25572
|
+
}).passthrough();
|
|
25573
|
+
var remoteSkillSchema = exports_external.object({
|
|
25574
|
+
name: exports_external.string().min(1).optional(),
|
|
25575
|
+
slug: exports_external.string().min(1).optional(),
|
|
25576
|
+
displayName: exports_external.string().optional(),
|
|
25577
|
+
description: exports_external.string().optional(),
|
|
25578
|
+
category: exports_external.string().optional(),
|
|
25579
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
25580
|
+
dependencies: exports_external.array(exports_external.string()).optional(),
|
|
25581
|
+
version: exports_external.string().optional(),
|
|
25582
|
+
availability: remoteAvailabilitySchema.optional()
|
|
25583
|
+
}).passthrough().refine((skill) => skill.name || skill.slug, {
|
|
25584
|
+
message: "Remote skill requires name or slug"
|
|
25585
|
+
});
|
|
25586
|
+
var secretValuePatterns = [
|
|
25587
|
+
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
25588
|
+
/\bgh[opsur]_[A-Za-z0-9_]{8,}\b/g,
|
|
25589
|
+
/\bgithub_pat_[A-Za-z0-9_]{8,}\b/g,
|
|
25590
|
+
/\bnpm_[A-Za-z0-9_]{8,}\b/g,
|
|
25591
|
+
/\bAKIA[A-Z0-9]{12,}\b/g,
|
|
25592
|
+
/\bAIza[A-Za-z0-9_-]{10,}\b/g,
|
|
25593
|
+
new RegExp("\\bsecret" + "-token:\\s*[A-Za-z0-9._-]+", "gi"),
|
|
25594
|
+
/\bctx7sk\-[A-Za-z0-9_-]{8,}\b/g,
|
|
25595
|
+
/\bxai\-[A-Za-z0-9_-]{8,}\b/g
|
|
25596
|
+
];
|
|
25597
|
+
var remoteSkillDetailSchema = exports_external.union([
|
|
25598
|
+
remoteSkillSchema,
|
|
25599
|
+
exports_external.object({ skill: remoteSkillSchema }),
|
|
25600
|
+
exports_external.object({ data: remoteSkillSchema })
|
|
25601
|
+
]);
|
|
25602
|
+
var remoteRegistrySchema = exports_external.union([
|
|
25603
|
+
exports_external.array(remoteSkillSchema),
|
|
25604
|
+
exports_external.object({ skills: exports_external.array(remoteSkillSchema) }),
|
|
25605
|
+
exports_external.object({ data: exports_external.array(remoteSkillSchema) })
|
|
25606
|
+
]);
|
|
25607
|
+
function getConfiguredApiUrl(env = process.env) {
|
|
25608
|
+
return resolveApiUrl(env);
|
|
25609
|
+
}
|
|
25610
|
+
function buildSkillsApiUrl(apiUrl, endpoint = "/skills") {
|
|
25611
|
+
const url = new URL(apiUrl);
|
|
25612
|
+
const cleanEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
25613
|
+
const pathname = url.pathname.replace(/\/+$/, "");
|
|
25614
|
+
const apiBase = /\/api(?:\/v1)?\/skills$/.test(pathname) ? pathname.slice(0, -"/skills".length) : pathname;
|
|
25615
|
+
if (/\/api(?:\/v1)?$/.test(apiBase)) {
|
|
25616
|
+
url.pathname = `${apiBase}${cleanEndpoint}`;
|
|
25617
|
+
return url.toString();
|
|
25618
|
+
}
|
|
25619
|
+
url.pathname = `${apiBase}/api/v1${cleanEndpoint}`.replace(/\/{2,}/g, "/");
|
|
25620
|
+
return url.toString();
|
|
25621
|
+
}
|
|
25622
|
+
function titleize(name) {
|
|
25623
|
+
return name.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
25624
|
+
}
|
|
25625
|
+
function normalizeRemoteSkill(skill) {
|
|
25626
|
+
const name = skill.name || skill.slug;
|
|
25627
|
+
if (!name)
|
|
25628
|
+
throw new Error("Remote skill requires name or slug");
|
|
25629
|
+
return {
|
|
25630
|
+
name,
|
|
25631
|
+
displayName: skill.displayName || titleize(name),
|
|
25632
|
+
description: skill.description || "",
|
|
25633
|
+
category: skill.category || "Remote",
|
|
25634
|
+
tags: skill.tags || ["remote"],
|
|
25635
|
+
dependencies: skill.dependencies,
|
|
25636
|
+
...skill.version ? { version: skill.version } : {},
|
|
25637
|
+
availability: normalizeRemoteAvailability(skill.availability),
|
|
25638
|
+
source: "remote"
|
|
25639
|
+
};
|
|
25640
|
+
}
|
|
25641
|
+
function normalizeRemoteAvailability(availability) {
|
|
25642
|
+
if (!availability)
|
|
25643
|
+
return { status: "available" };
|
|
25644
|
+
if (availability.status === "available")
|
|
25645
|
+
return { status: "available" };
|
|
25646
|
+
return {
|
|
25647
|
+
status: availability.status,
|
|
25648
|
+
...safeAvailabilityCode(availability.code) ? { code: safeAvailabilityCode(availability.code) } : {},
|
|
25649
|
+
...availability.message ? { message: sanitizeAvailabilityText(availability.message) } : {},
|
|
25650
|
+
...availability.details ? { details: availability.details.map(sanitizeAvailabilityText).filter(Boolean) } : {}
|
|
25651
|
+
};
|
|
25652
|
+
}
|
|
25653
|
+
function safeAvailabilityCode(code) {
|
|
25654
|
+
if (!code)
|
|
25655
|
+
return;
|
|
25656
|
+
return /^[A-Z0-9_]+$/.test(code) ? code : undefined;
|
|
25657
|
+
}
|
|
25658
|
+
function sanitizeAvailabilityText(text) {
|
|
25659
|
+
return secretValuePatterns.reduce((value, pattern) => value.replace(pattern, "credential"), sanitizePublicDiscoveryText(text).replace(/\b[A-Z0-9_]*(?:API_KEY|SECRET|TOKEN|CREDENTIAL)[A-Z0-9_]*\b/g, "credential")).replace(/\s{2,}/g, " ").trim();
|
|
25660
|
+
}
|
|
25661
|
+
function parseRemoteRegistryPayload(payload) {
|
|
25662
|
+
const parsed = parseRemoteContract(remoteRegistrySchema, payload, "Remote registry payload did not match the expected skills contract");
|
|
25663
|
+
const rawSkills = Array.isArray(parsed) ? parsed : ("skills" in parsed) ? parsed.skills : parsed.data;
|
|
25664
|
+
return rawSkills.map(normalizeRemoteSkill);
|
|
25665
|
+
}
|
|
25666
|
+
function parseRemoteContract(schema, payload, message) {
|
|
25667
|
+
try {
|
|
25668
|
+
return schema.parse(payload);
|
|
25669
|
+
} catch (error2) {
|
|
25670
|
+
if (error2 instanceof exports_external.ZodError)
|
|
25671
|
+
throw new Error(message, { cause: error2 });
|
|
25672
|
+
throw error2;
|
|
25673
|
+
}
|
|
25674
|
+
}
|
|
25675
|
+
async function remoteRequestHeaders(options) {
|
|
25676
|
+
const headers = new Headers({ Accept: "application/json" });
|
|
25677
|
+
const token = options.authToken !== undefined ? options.authToken : await ambientTokenFor(options.apiUrl);
|
|
25678
|
+
const trimmed = token?.trim();
|
|
25679
|
+
if (trimmed)
|
|
25680
|
+
headers.set("Authorization", `Bearer ${trimmed}`);
|
|
25681
|
+
return headers;
|
|
25682
|
+
}
|
|
25683
|
+
async function ambientTokenFor(callerApiUrl) {
|
|
25684
|
+
const connection = await resolveSkillsConnection();
|
|
25685
|
+
if (!connection)
|
|
25686
|
+
return null;
|
|
25687
|
+
if (callerApiUrl !== undefined && normalizeSkillsApiOrigin(callerApiUrl) !== connection.apiOrigin) {
|
|
25688
|
+
throw new SkillsFleetCredentialError(`The Skills credential resolved for ${connection.apiOrigin} is never sent to a caller-supplied apiUrl ` + `(${normalizeSkillsApiOrigin(callerApiUrl)}). Pass an explicit authToken for that instance, or authToken: null ` + `for an unauthenticated read; no credential was sent.`, "INSTANCE_CREDENTIAL_MISMATCH");
|
|
25689
|
+
}
|
|
25690
|
+
return connection.apiKey;
|
|
25691
|
+
}
|
|
25692
|
+
async function fetchRemoteJson(url, options) {
|
|
25693
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
25694
|
+
const headers = await remoteRequestHeaders(options);
|
|
25695
|
+
const controller = new AbortController;
|
|
25696
|
+
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 1e4);
|
|
25697
|
+
try {
|
|
25698
|
+
const response = await fetchImpl(url, {
|
|
25699
|
+
headers,
|
|
25700
|
+
signal: controller.signal
|
|
25701
|
+
});
|
|
25702
|
+
if (!response.ok) {
|
|
25703
|
+
throw new Error(`Remote registry request failed: ${response.status} ${response.statusText}`);
|
|
25704
|
+
}
|
|
25705
|
+
return response.json();
|
|
25706
|
+
} finally {
|
|
25707
|
+
clearTimeout(timeout);
|
|
25708
|
+
}
|
|
25709
|
+
}
|
|
25710
|
+
async function loadRemoteRegistry(options = {}) {
|
|
25711
|
+
const apiUrl = options.apiUrl || getConfiguredApiUrl();
|
|
25712
|
+
if (!apiUrl) {
|
|
25713
|
+
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}`);
|
|
25714
|
+
}
|
|
25715
|
+
const url = buildSkillsApiUrl(apiUrl, options.endpoint);
|
|
25716
|
+
return parseRemoteRegistryPayload(await fetchRemoteJson(url, options));
|
|
25717
|
+
}
|
|
25718
|
+
async function mergeRemoteRegistry(local, options = {}) {
|
|
25719
|
+
const apiUrl = options.apiUrl || getConfiguredApiUrl();
|
|
25720
|
+
if (!apiUrl)
|
|
25721
|
+
return local;
|
|
25722
|
+
if (options.authToken !== undefined && !options.authToken?.trim())
|
|
25723
|
+
return local;
|
|
25724
|
+
const remote = await loadRemoteRegistry({ ...options, apiUrl });
|
|
25725
|
+
return mergeSkillRegistryLists(local, remote);
|
|
25726
|
+
}
|
|
25727
|
+
|
|
25728
|
+
// src/lib/read-access.ts
|
|
25729
|
+
async function requireSkillsReadAccess(env = process.env, options = {}) {
|
|
25730
|
+
const connection = await resolveSkillsConnection(env, options);
|
|
25731
|
+
return connection ? { mode: "hosted", apiOrigin: connection.apiOrigin } : { mode: "local" };
|
|
25732
|
+
}
|
|
25733
|
+
async function getBrowseRegistry(options = {}) {
|
|
25734
|
+
const profile = options.all ? "all" : "basic";
|
|
25735
|
+
const local = loadRegistryProfile(profile);
|
|
25736
|
+
if (options.remote) {
|
|
25737
|
+
const remote = await loadRemoteRegistry();
|
|
25738
|
+
return mergeSkillRegistryLists(local, remote);
|
|
25739
|
+
}
|
|
25740
|
+
return mergeRemoteRegistry(local);
|
|
25741
|
+
}
|
|
25742
|
+
|
|
25743
|
+
// src/lib/installer.ts
|
|
25744
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9, rmSync as rmSync2 } from "fs";
|
|
25745
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
25746
|
+
import { homedir as homedir2 } from "os";
|
|
25747
|
+
import { fileURLToPath } from "url";
|
|
25748
|
+
// src/lib/utils.ts
|
|
25749
|
+
function normalizeSkillName(name) {
|
|
25750
|
+
return name;
|
|
25751
|
+
}
|
|
25752
|
+
|
|
25753
|
+
// src/lib/project-state.ts
|
|
25754
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
25755
|
+
import { join as join10 } from "path";
|
|
25756
|
+
var VALID_PIN_SOURCES = [
|
|
25757
|
+
"official",
|
|
25758
|
+
"custom",
|
|
25759
|
+
"remote",
|
|
25760
|
+
"private",
|
|
25761
|
+
"private-hosted",
|
|
25762
|
+
"upstream",
|
|
25763
|
+
"extension",
|
|
25764
|
+
"local"
|
|
25765
|
+
];
|
|
25766
|
+
var SKILLS_PROJECT_DIR = ".skills";
|
|
25767
|
+
var PROJECT_CONFIG_FILE = "project.json";
|
|
25768
|
+
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
25769
|
+
function getProjectStateDir(targetDir = process.cwd()) {
|
|
25770
|
+
return join10(targetDir, SKILLS_PROJECT_DIR);
|
|
25771
|
+
}
|
|
25772
|
+
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
25773
|
+
return join10(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
25774
|
+
}
|
|
25775
|
+
function loadProjectConfig(targetDir = process.cwd()) {
|
|
25776
|
+
const path = getProjectConfigPath(targetDir);
|
|
25777
|
+
if (!existsSync9(path))
|
|
25778
|
+
return null;
|
|
25170
25779
|
try {
|
|
25171
|
-
return normalizeProjectConfig(JSON.parse(
|
|
25780
|
+
return normalizeProjectConfig(JSON.parse(readFileSync8(path, "utf-8")));
|
|
25172
25781
|
} catch {
|
|
25173
25782
|
return null;
|
|
25174
25783
|
}
|
|
@@ -25267,12 +25876,12 @@ var __dirname2 = dirname4(fileURLToPath(import.meta.url));
|
|
|
25267
25876
|
function findSkillsDir() {
|
|
25268
25877
|
let dir = __dirname2;
|
|
25269
25878
|
for (let i = 0;i < 5; i++) {
|
|
25270
|
-
const candidate =
|
|
25879
|
+
const candidate = join11(dir, "skills");
|
|
25271
25880
|
if (existsSync10(candidate) && !dir.includes(".skills"))
|
|
25272
25881
|
return candidate;
|
|
25273
25882
|
dir = dirname4(dir);
|
|
25274
25883
|
}
|
|
25275
|
-
return
|
|
25884
|
+
return join11(__dirname2, "..", "skills");
|
|
25276
25885
|
}
|
|
25277
25886
|
var SKILLS_DIR = findSkillsDir();
|
|
25278
25887
|
function getSkillPath(name) {
|
|
@@ -25280,13 +25889,13 @@ function getSkillPath(name) {
|
|
|
25280
25889
|
const portable = findPortableSkill(skillName);
|
|
25281
25890
|
if (portable)
|
|
25282
25891
|
return portable.path;
|
|
25283
|
-
const legacyCustomPath =
|
|
25892
|
+
const legacyCustomPath = join11(getDataDir(), "custom", skillName);
|
|
25284
25893
|
if (existsSync10(legacyCustomPath))
|
|
25285
25894
|
return legacyCustomPath;
|
|
25286
25895
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
25287
25896
|
if (extensionPath)
|
|
25288
25897
|
return extensionPath;
|
|
25289
|
-
return
|
|
25898
|
+
return join11(SKILLS_DIR, skillName);
|
|
25290
25899
|
}
|
|
25291
25900
|
function getCanonicalSkillName(name) {
|
|
25292
25901
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
@@ -25350,11 +25959,11 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
25350
25959
|
const base = projectDir || process.cwd();
|
|
25351
25960
|
switch (agent) {
|
|
25352
25961
|
case "pi":
|
|
25353
|
-
return scope === "project" ?
|
|
25962
|
+
return scope === "project" ? join11(base, ".pi", "skills") : join11(homedir2(), ".pi", "agent", "skills");
|
|
25354
25963
|
case "opencode":
|
|
25355
|
-
return scope === "project" ?
|
|
25964
|
+
return scope === "project" ? join11(base, ".opencode", "skills") : join11(homedir2(), ".config", "opencode", "skills");
|
|
25356
25965
|
default:
|
|
25357
|
-
return scope === "project" ?
|
|
25966
|
+
return scope === "project" ? join11(base, `.${agent}`, "skills") : join11(homedir2(), `.${agent}`, "skills");
|
|
25358
25967
|
}
|
|
25359
25968
|
}
|
|
25360
25969
|
function warnMissingDependencies(name, targetDir) {
|
|
@@ -25369,11 +25978,11 @@ function warnMissingDependencies(name, targetDir) {
|
|
|
25369
25978
|
}
|
|
25370
25979
|
}
|
|
25371
25980
|
function readBundledSkillVersion(name) {
|
|
25372
|
-
const pkgPath =
|
|
25981
|
+
const pkgPath = join11(getSkillPath(name), "package.json");
|
|
25373
25982
|
if (!existsSync10(pkgPath))
|
|
25374
25983
|
return "unknown";
|
|
25375
25984
|
try {
|
|
25376
|
-
const pkg = JSON.parse(
|
|
25985
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
25377
25986
|
return pkg.version || "unknown";
|
|
25378
25987
|
} catch {
|
|
25379
25988
|
return "unknown";
|
|
@@ -25381,16 +25990,16 @@ function readBundledSkillVersion(name) {
|
|
|
25381
25990
|
}
|
|
25382
25991
|
|
|
25383
25992
|
// src/lib/skillinfo.ts
|
|
25384
|
-
import { existsSync as existsSync11, readFileSync as
|
|
25385
|
-
import { join as
|
|
25993
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
25994
|
+
import { join as join12 } from "path";
|
|
25386
25995
|
function isInstructionSkillDir(skillPath, meta) {
|
|
25387
25996
|
if (meta?.kind === "instruction")
|
|
25388
25997
|
return true;
|
|
25389
|
-
const skillMdPath =
|
|
25998
|
+
const skillMdPath = join12(skillPath, "SKILL.md");
|
|
25390
25999
|
if (!existsSync11(skillMdPath))
|
|
25391
26000
|
return false;
|
|
25392
26001
|
try {
|
|
25393
|
-
return parseSkillFrontmatter(
|
|
26002
|
+
return parseSkillFrontmatter(readFileSync10(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
25394
26003
|
} catch {
|
|
25395
26004
|
return false;
|
|
25396
26005
|
}
|
|
@@ -25416,9 +26025,9 @@ function getSkillDocs(name) {
|
|
|
25416
26025
|
if (!existsSync11(skillPath))
|
|
25417
26026
|
return null;
|
|
25418
26027
|
return {
|
|
25419
|
-
skillMd: readIfExists(
|
|
25420
|
-
readme: readIfExists(
|
|
25421
|
-
claudeMd: readIfExists(
|
|
26028
|
+
skillMd: readIfExists(join12(skillPath, "SKILL.md")),
|
|
26029
|
+
readme: readIfExists(join12(skillPath, "README.md")),
|
|
26030
|
+
claudeMd: readIfExists(join12(skillPath, "CLAUDE.md"))
|
|
25422
26031
|
};
|
|
25423
26032
|
}
|
|
25424
26033
|
function getSkillBestDoc(name) {
|
|
@@ -25433,7 +26042,7 @@ function getSkillRequirements(name) {
|
|
|
25433
26042
|
return null;
|
|
25434
26043
|
const texts = [];
|
|
25435
26044
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
25436
|
-
const content = readIfExists(
|
|
26045
|
+
const content = readIfExists(join12(skillPath, file));
|
|
25437
26046
|
if (content)
|
|
25438
26047
|
texts.push(content);
|
|
25439
26048
|
}
|
|
@@ -25473,10 +26082,10 @@ function getSkillRequirements(name) {
|
|
|
25473
26082
|
const skillName = normalizeSkillName(name);
|
|
25474
26083
|
let cliCommand = `skills run ${skillName}`;
|
|
25475
26084
|
let dependencies = {};
|
|
25476
|
-
const pkgPath =
|
|
26085
|
+
const pkgPath = join12(skillPath, "package.json");
|
|
25477
26086
|
if (existsSync11(pkgPath)) {
|
|
25478
26087
|
try {
|
|
25479
|
-
const pkg = JSON.parse(
|
|
26088
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
25480
26089
|
dependencies = pkg.dependencies || {};
|
|
25481
26090
|
} catch {}
|
|
25482
26091
|
}
|
|
@@ -25503,13 +26112,13 @@ async function runSkill(name, args, options = {}) {
|
|
|
25503
26112
|
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'.`
|
|
25504
26113
|
};
|
|
25505
26114
|
}
|
|
25506
|
-
const pkgPath =
|
|
26115
|
+
const pkgPath = join12(skillPath, "package.json");
|
|
25507
26116
|
if (!existsSync11(pkgPath)) {
|
|
25508
26117
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
25509
26118
|
}
|
|
25510
26119
|
let entryPoint;
|
|
25511
26120
|
try {
|
|
25512
|
-
const pkg = JSON.parse(
|
|
26121
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
25513
26122
|
if (pkg.bin) {
|
|
25514
26123
|
const binValues = Object.values(pkg.bin);
|
|
25515
26124
|
entryPoint = binValues[0];
|
|
@@ -25523,11 +26132,11 @@ async function runSkill(name, args, options = {}) {
|
|
|
25523
26132
|
} catch {
|
|
25524
26133
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
25525
26134
|
}
|
|
25526
|
-
const entryPath =
|
|
26135
|
+
const entryPath = join12(skillPath, entryPoint);
|
|
25527
26136
|
if (!existsSync11(entryPath)) {
|
|
25528
26137
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
25529
26138
|
}
|
|
25530
|
-
const nodeModules =
|
|
26139
|
+
const nodeModules = join12(skillPath, "node_modules");
|
|
25531
26140
|
if (!existsSync11(nodeModules)) {
|
|
25532
26141
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
25533
26142
|
cwd: skillPath,
|
|
@@ -25538,7 +26147,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
25538
26147
|
}
|
|
25539
26148
|
const proc = Bun.spawn(["bun", "run", entryPath, ...args], {
|
|
25540
26149
|
cwd: skillPath,
|
|
25541
|
-
stdout: options.stdio === "pipe" ? "pipe" : "inherit",
|
|
26150
|
+
stdout: options.stdio === "pipe" ? "pipe" : options.stdio === "stderr" ? 2 : "inherit",
|
|
25542
26151
|
stderr: options.stdio === "pipe" ? "pipe" : "inherit",
|
|
25543
26152
|
stdin: "inherit",
|
|
25544
26153
|
env: { ...process.env, ...options.env }
|
|
@@ -25555,7 +26164,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
25555
26164
|
return { exitCode };
|
|
25556
26165
|
}
|
|
25557
26166
|
function detectProjectSkills(cwd = process.cwd()) {
|
|
25558
|
-
const pkgPath =
|
|
26167
|
+
const pkgPath = join12(cwd, "package.json");
|
|
25559
26168
|
if (!existsSync11(pkgPath)) {
|
|
25560
26169
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
25561
26170
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
@@ -25563,7 +26172,7 @@ function detectProjectSkills(cwd = process.cwd()) {
|
|
|
25563
26172
|
}
|
|
25564
26173
|
let pkg;
|
|
25565
26174
|
try {
|
|
25566
|
-
pkg = JSON.parse(
|
|
26175
|
+
pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
25567
26176
|
} catch {
|
|
25568
26177
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
25569
26178
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
@@ -25653,7 +26262,7 @@ function extractEnvVars(text) {
|
|
|
25653
26262
|
function readIfExists(path) {
|
|
25654
26263
|
try {
|
|
25655
26264
|
if (existsSync11(path)) {
|
|
25656
|
-
return
|
|
26265
|
+
return readFileSync10(path, "utf-8");
|
|
25657
26266
|
}
|
|
25658
26267
|
} catch {}
|
|
25659
26268
|
return null;
|
|
@@ -26068,6 +26677,104 @@ var toolContracts = [
|
|
|
26068
26677
|
dependencies: objectSchema({}, [], "Package dependencies.", true)
|
|
26069
26678
|
})
|
|
26070
26679
|
},
|
|
26680
|
+
{
|
|
26681
|
+
name: "update_account_profile",
|
|
26682
|
+
title: "Update Account Display Name",
|
|
26683
|
+
description: "Update your display name with fresh email verification on the configured server.",
|
|
26684
|
+
params: ["name", "email", "code"],
|
|
26685
|
+
category: "execution",
|
|
26686
|
+
sideEffects: "local-process-or-remote-run",
|
|
26687
|
+
stable: true,
|
|
26688
|
+
inputSchema: objectSchema({ name: stringSchema("Display name, 1\u2013100 characters"), email: { type: "string", format: "email" }, code: { type: "string", pattern: "^\\d{6}$" } }, ["name", "email", "code"]),
|
|
26689
|
+
outputSchema: objectSchema({ user: objectSchema({ id: stringSchema("Account identifier."), email: stringSchema("Account email."), displayName: stringSchema("Display name."), role: stringSchema("Current workspace role.") }, ["id", "email", "displayName", "role"]) }, ["user"])
|
|
26690
|
+
},
|
|
26691
|
+
{
|
|
26692
|
+
name: "update_workspace_name",
|
|
26693
|
+
title: "Update Workspace Name",
|
|
26694
|
+
description: "Update the current workspace name as an owner/admin with fresh email verification.",
|
|
26695
|
+
params: ["name", "email", "code"],
|
|
26696
|
+
category: "execution",
|
|
26697
|
+
sideEffects: "local-process-or-remote-run",
|
|
26698
|
+
stable: true,
|
|
26699
|
+
inputSchema: objectSchema({ name: stringSchema("Workspace name, 1\u2013100 characters"), email: { type: "string", format: "email" }, code: { type: "string", pattern: "^\\d{6}$" } }, ["name", "email", "code"]),
|
|
26700
|
+
outputSchema: objectSchema({ organization: objectSchema({ id: stringSchema("Workspace identifier."), slug: stringSchema("Stable workspace slug."), name: stringSchema("Workspace name.") }, ["id", "slug", "name"]) }, ["organization"])
|
|
26701
|
+
},
|
|
26702
|
+
{
|
|
26703
|
+
name: "set_workspace_member_role",
|
|
26704
|
+
title: "Set Current Workspace Member Role",
|
|
26705
|
+
description: "Set an exact membership incarnation's role with its observed expectedRole and fresh verification; no automatic retry.",
|
|
26706
|
+
params: ["membershipId", "role", "expectedRole", "email", "code"],
|
|
26707
|
+
category: "execution",
|
|
26708
|
+
sideEffects: "local-process-or-remote-run",
|
|
26709
|
+
stable: true,
|
|
26710
|
+
inputSchema: objectSchema({
|
|
26711
|
+
membershipId: { type: "string", pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$" },
|
|
26712
|
+
role: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26713
|
+
expectedRole: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26714
|
+
email: { type: "string", format: "email" },
|
|
26715
|
+
code: { type: "string", pattern: "^\\d{6}$" }
|
|
26716
|
+
}, ["membershipId", "role", "expectedRole", "email", "code"]),
|
|
26717
|
+
outputSchema: objectSchema({
|
|
26718
|
+
organizationId: stringSchema("Current workspace identifier."),
|
|
26719
|
+
changed: { type: "boolean" },
|
|
26720
|
+
member: objectSchema({
|
|
26721
|
+
membershipId: stringSchema("Membership incarnation."),
|
|
26722
|
+
userId: stringSchema("Account identifier."),
|
|
26723
|
+
email: stringSchema("Member email."),
|
|
26724
|
+
displayName: { oneOf: [{ type: "string" }, { type: "null" }] },
|
|
26725
|
+
role: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26726
|
+
createdAt: stringSchema("Exact server timestamp including microseconds.")
|
|
26727
|
+
}, ["membershipId", "userId", "email", "displayName", "role", "createdAt"])
|
|
26728
|
+
}, ["organizationId", "member", "changed"])
|
|
26729
|
+
},
|
|
26730
|
+
{
|
|
26731
|
+
name: "remove_workspace_member",
|
|
26732
|
+
title: "Remove Current Workspace Member",
|
|
26733
|
+
description: "Remove exactly this membership incarnation using its observed expectedRole and fresh verification; self-removal is unavailable.",
|
|
26734
|
+
params: ["membershipId", "expectedRole", "email", "code"],
|
|
26735
|
+
category: "execution",
|
|
26736
|
+
sideEffects: "local-process-or-remote-run",
|
|
26737
|
+
stable: true,
|
|
26738
|
+
inputSchema: objectSchema({
|
|
26739
|
+
membershipId: { type: "string", pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$" },
|
|
26740
|
+
expectedRole: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26741
|
+
email: { type: "string", format: "email" },
|
|
26742
|
+
code: { type: "string", pattern: "^\\d{6}$" }
|
|
26743
|
+
}, ["membershipId", "expectedRole", "email", "code"]),
|
|
26744
|
+
outputSchema: objectSchema({
|
|
26745
|
+
organizationId: stringSchema("Current workspace identifier."),
|
|
26746
|
+
membershipId: stringSchema("Removed membership incarnation."),
|
|
26747
|
+
removed: { type: "boolean", const: true },
|
|
26748
|
+
alreadyRemoved: { type: "boolean" }
|
|
26749
|
+
}, ["organizationId", "membershipId", "removed", "alreadyRemoved"])
|
|
26750
|
+
},
|
|
26751
|
+
{
|
|
26752
|
+
name: "list_workspace_members",
|
|
26753
|
+
title: "List Current Workspace Members",
|
|
26754
|
+
description: "Read one current-workspace roster page with fresh owner/admin email verification; saved credentials stay unchanged.",
|
|
26755
|
+
params: ["email", "code", "limit?", "cursor?"],
|
|
26756
|
+
category: "execution",
|
|
26757
|
+
sideEffects: "local-process-or-remote-run",
|
|
26758
|
+
stable: true,
|
|
26759
|
+
inputSchema: objectSchema({
|
|
26760
|
+
email: { type: "string", format: "email" },
|
|
26761
|
+
code: { type: "string", pattern: "^\\d{6}$" },
|
|
26762
|
+
limit: { type: "integer", minimum: 1, maximum: 100 },
|
|
26763
|
+
cursor: { type: "string", pattern: "^[A-Za-z0-9_-]{1,512}$" }
|
|
26764
|
+
}, ["email", "code"]),
|
|
26765
|
+
outputSchema: objectSchema({
|
|
26766
|
+
organizationId: stringSchema("Current workspace identifier."),
|
|
26767
|
+
members: arraySchema(objectSchema({
|
|
26768
|
+
membershipId: stringSchema("Membership incarnation."),
|
|
26769
|
+
userId: stringSchema("Account identifier."),
|
|
26770
|
+
email: stringSchema("Member email."),
|
|
26771
|
+
displayName: { oneOf: [{ type: "string" }, { type: "null" }] },
|
|
26772
|
+
role: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26773
|
+
createdAt: stringSchema("Exact server timestamp including microseconds.")
|
|
26774
|
+
}, ["membershipId", "userId", "email", "displayName", "role", "createdAt"])),
|
|
26775
|
+
nextCursor: { oneOf: [{ type: "string" }, { type: "null" }] }
|
|
26776
|
+
}, ["organizationId", "members", "nextCursor"])
|
|
26777
|
+
},
|
|
26071
26778
|
{
|
|
26072
26779
|
name: "list_api_keys",
|
|
26073
26780
|
title: "List API Keys",
|
|
@@ -26577,90 +27284,6 @@ function clone2(value) {
|
|
|
26577
27284
|
return JSON.parse(JSON.stringify(value));
|
|
26578
27285
|
}
|
|
26579
27286
|
|
|
26580
|
-
// src/lib/discovery.ts
|
|
26581
|
-
var VENDOR_TERMS = [
|
|
26582
|
-
"Google Gemini",
|
|
26583
|
-
"OpenAI Sora",
|
|
26584
|
-
"MiniMax Hailuo",
|
|
26585
|
-
"Claude Code",
|
|
26586
|
-
"Claude Vision",
|
|
26587
|
-
"DALL-E 3",
|
|
26588
|
-
"GPT-4o Mini",
|
|
26589
|
-
"Cerebras",
|
|
26590
|
-
"OpenRouter",
|
|
26591
|
-
"Firecrawl",
|
|
26592
|
-
"ElevenLabs",
|
|
26593
|
-
"Anthropic",
|
|
26594
|
-
"OpenAI",
|
|
26595
|
-
"Minimax",
|
|
26596
|
-
"MiniMax",
|
|
26597
|
-
"Gemini",
|
|
26598
|
-
"Claude",
|
|
26599
|
-
"Whisper",
|
|
26600
|
-
"Seedance",
|
|
26601
|
-
"Lyria",
|
|
26602
|
-
"Sora",
|
|
26603
|
-
"Veo",
|
|
26604
|
-
"Exa.ai",
|
|
26605
|
-
"Exa",
|
|
26606
|
-
"XAI",
|
|
26607
|
-
"xAI"
|
|
26608
|
-
];
|
|
26609
|
-
var VENDOR_TAGS = new Set([
|
|
26610
|
-
"anthropic",
|
|
26611
|
-
"cerebras",
|
|
26612
|
-
"claude",
|
|
26613
|
-
"exa",
|
|
26614
|
-
"firecrawl",
|
|
26615
|
-
"gemini",
|
|
26616
|
-
"google",
|
|
26617
|
-
"minimax",
|
|
26618
|
-
"openai",
|
|
26619
|
-
"openrouter",
|
|
26620
|
-
"seedance",
|
|
26621
|
-
"whisper",
|
|
26622
|
-
"xai"
|
|
26623
|
-
]);
|
|
26624
|
-
var vendorPattern = new RegExp(`\\b(${VENDOR_TERMS.map(escapeRegExp).join("|")})\\b`, "gi");
|
|
26625
|
-
function getCompactSkillDiscovery(skill) {
|
|
26626
|
-
return {
|
|
26627
|
-
name: skill.name,
|
|
26628
|
-
category: skill.category,
|
|
26629
|
-
description: sanitizePublicDiscoveryText(skill.description)
|
|
26630
|
-
};
|
|
26631
|
-
}
|
|
26632
|
-
function getPublicSkillDiscovery(skill) {
|
|
26633
|
-
return {
|
|
26634
|
-
...skill,
|
|
26635
|
-
description: sanitizePublicDiscoveryText(skill.description),
|
|
26636
|
-
tags: publicDiscoveryTags(skill.tags)
|
|
26637
|
-
};
|
|
26638
|
-
}
|
|
26639
|
-
function publicDiscoveryTags(tags) {
|
|
26640
|
-
return tags.filter((tag) => !VENDOR_TAGS.has(tag.toLowerCase()));
|
|
26641
|
-
}
|
|
26642
|
-
function sanitizePublicDiscoveryText(text) {
|
|
26643
|
-
let sanitized = text.replace(vendorPattern, "hosted AI").replace(/\bLLM\b/g, "AI").replace(/\s{2,}/g, " ");
|
|
26644
|
-
let previous;
|
|
26645
|
-
do {
|
|
26646
|
-
previous = sanitized;
|
|
26647
|
-
sanitized = sanitized.replace(/\bhosted AI(?: providers)?\s*,\s*hosted AI(?: providers)?\b/gi, "hosted AI providers").replace(/\bhosted AI(?: providers)?\s*,?\s*and\s*hosted AI(?: providers)?\b/gi, "hosted AI providers").replace(/\bhosted AI(?: providers)?\s+or\s+hosted AI(?: providers)?\b/gi, "hosted AI providers").replace(/\bhosted AI providers\s+hosted AI\b/gi, "hosted AI providers").replace(/\bhosted AI providers\s+providers\b/gi, "hosted AI providers");
|
|
26648
|
-
} while (sanitized !== previous);
|
|
26649
|
-
return sanitized.trim();
|
|
26650
|
-
}
|
|
26651
|
-
function publicDiscoveryEnvVars(_skillName, envVars) {
|
|
26652
|
-
return envVars;
|
|
26653
|
-
}
|
|
26654
|
-
function publicDiscoveryDependencies(_skillName, dependencies) {
|
|
26655
|
-
return dependencies;
|
|
26656
|
-
}
|
|
26657
|
-
function publicDiscoveryDocumentation(_skill, documentation) {
|
|
26658
|
-
return documentation;
|
|
26659
|
-
}
|
|
26660
|
-
function escapeRegExp(value) {
|
|
26661
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26662
|
-
}
|
|
26663
|
-
|
|
26664
27287
|
// src/lib/tool-primitives.ts
|
|
26665
27288
|
var TOOL_PRIMITIVE_SCHEMA_VERSION = 1;
|
|
26666
27289
|
var TOOL_PRIMITIVES = [
|
|
@@ -27119,22 +27742,20 @@ function compactRemoteRun(run) {
|
|
|
27119
27742
|
}
|
|
27120
27743
|
|
|
27121
27744
|
// src/mcp/helpers.ts
|
|
27745
|
+
init_fleet_credentials();
|
|
27746
|
+
async function readSurface(body) {
|
|
27747
|
+
try {
|
|
27748
|
+
return await body();
|
|
27749
|
+
} catch (error2) {
|
|
27750
|
+
if (isSkillsFleetCredentialError(error2))
|
|
27751
|
+
return mcpError("AUTH_REQUIRED", error2.message, ["skills auth login"]);
|
|
27752
|
+
throw error2;
|
|
27753
|
+
}
|
|
27754
|
+
}
|
|
27122
27755
|
function stripNulls(obj) {
|
|
27123
27756
|
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null && v !== undefined && !(Array.isArray(v) && v.length === 0)));
|
|
27124
27757
|
}
|
|
27125
27758
|
var searchCache = new Map;
|
|
27126
|
-
var CACHE_MAX = 100;
|
|
27127
|
-
function cacheGet(key) {
|
|
27128
|
-
return searchCache.get(key);
|
|
27129
|
-
}
|
|
27130
|
-
function cacheSet(key, value) {
|
|
27131
|
-
if (searchCache.size >= CACHE_MAX) {
|
|
27132
|
-
const first = searchCache.keys().next().value;
|
|
27133
|
-
if (first !== undefined)
|
|
27134
|
-
searchCache.delete(first);
|
|
27135
|
-
}
|
|
27136
|
-
searchCache.set(key, value);
|
|
27137
|
-
}
|
|
27138
27759
|
function cacheClear() {
|
|
27139
27760
|
searchCache.clear();
|
|
27140
27761
|
}
|
|
@@ -27174,9 +27795,10 @@ function registerDiscoveryTools(server) {
|
|
|
27174
27795
|
limit: exports_external.number().optional(),
|
|
27175
27796
|
offset: exports_external.number().optional()
|
|
27176
27797
|
}
|
|
27177
|
-
}, async ({ category, profile, detail, limit, offset }) => {
|
|
27798
|
+
}, async ({ category, profile, detail, limit, offset }) => readSurface(async () => {
|
|
27178
27799
|
const selectedProfile = profile || "basic";
|
|
27179
|
-
const
|
|
27800
|
+
const registry2 = await getBrowseRegistry({ all: selectedProfile === "all" });
|
|
27801
|
+
const skills = category ? registry2.filter((s) => s.category === category) : registry2;
|
|
27180
27802
|
const mapped = detail ? skills.map(getPublicSkillDiscovery) : skills.map(getCompactSkillDiscovery);
|
|
27181
27803
|
const page = paginate(mapped, {
|
|
27182
27804
|
limit: parsePageLimit(limit, DEFAULT_MCP_LIMIT, { max: 100 }),
|
|
@@ -27192,7 +27814,7 @@ function registerDiscoveryTools(server) {
|
|
|
27192
27814
|
nextArguments: page.hasMore ? { profile: selectedProfile, category, detail: Boolean(detail), limit: page.limit, offset: page.nextOffset } : null,
|
|
27193
27815
|
detailHint: detail ? undefined : "Set detail:true for full public skill objects, or call get_skill_info for one skill."
|
|
27194
27816
|
});
|
|
27195
|
-
});
|
|
27817
|
+
}));
|
|
27196
27818
|
server.registerTool("list_pinned_skills", {
|
|
27197
27819
|
title: "List Pinned Skills",
|
|
27198
27820
|
description: "List skills pinned in the current project's .skills/project.json.",
|
|
@@ -27216,13 +27838,9 @@ function registerDiscoveryTools(server) {
|
|
|
27216
27838
|
limit: exports_external.number().optional(),
|
|
27217
27839
|
offset: exports_external.number().optional()
|
|
27218
27840
|
}
|
|
27219
|
-
}, async ({ query, profile, detail, limit, offset }) => {
|
|
27841
|
+
}, async ({ query, profile, detail, limit, offset }) => readSurface(async () => {
|
|
27220
27842
|
const selectedProfile = profile || "basic";
|
|
27221
|
-
const
|
|
27222
|
-
const cached2 = cacheGet(cacheKey);
|
|
27223
|
-
const results = cached2 ? cached2 : searchSkills(query, loadRegistryProfile(selectedProfile));
|
|
27224
|
-
if (!cached2)
|
|
27225
|
-
cacheSet(cacheKey, results);
|
|
27843
|
+
const results = searchSkills(query, await getBrowseRegistry({ all: selectedProfile === "all" }));
|
|
27226
27844
|
const out = detail ? results.map(getPublicSkillDiscovery) : results.map(getCompactSkillDiscovery);
|
|
27227
27845
|
const page = paginate(out, {
|
|
27228
27846
|
limit: parsePageLimit(limit, DEFAULT_MCP_LIMIT, { max: 100 }),
|
|
@@ -27238,14 +27856,15 @@ function registerDiscoveryTools(server) {
|
|
|
27238
27856
|
nextArguments: page.hasMore ? { query, profile: selectedProfile, detail: Boolean(detail), limit: page.limit, offset: page.nextOffset } : null,
|
|
27239
27857
|
detailHint: detail ? undefined : "Set detail:true for full public skill objects, or call get_skill_info for one skill."
|
|
27240
27858
|
});
|
|
27241
|
-
});
|
|
27859
|
+
}));
|
|
27242
27860
|
server.registerTool("get_skill_info", {
|
|
27243
27861
|
title: "Get Skill Info",
|
|
27244
27862
|
description: "Get skill metadata, env vars, and dependencies.",
|
|
27245
27863
|
inputSchema: {
|
|
27246
27864
|
name: exports_external.string()
|
|
27247
27865
|
}
|
|
27248
|
-
}, async ({ name }) => {
|
|
27866
|
+
}, async ({ name }) => readSurface(async () => {
|
|
27867
|
+
await requireSkillsReadAccess();
|
|
27249
27868
|
const skill = getSkill(name);
|
|
27250
27869
|
if (!skill) {
|
|
27251
27870
|
return mcpError("SKILL_NOT_FOUND", `Skill '${name}' not found`, findSimilarSkills(name));
|
|
@@ -27265,20 +27884,21 @@ function registerDiscoveryTools(server) {
|
|
|
27265
27884
|
return {
|
|
27266
27885
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
27267
27886
|
};
|
|
27268
|
-
});
|
|
27887
|
+
}));
|
|
27269
27888
|
server.registerTool("get_skill_docs", {
|
|
27270
27889
|
title: "Get Skill Docs",
|
|
27271
27890
|
description: "Get skill documentation (SKILL.md > README.md > CLAUDE.md).",
|
|
27272
27891
|
inputSchema: {
|
|
27273
27892
|
name: exports_external.string()
|
|
27274
27893
|
}
|
|
27275
|
-
}, async ({ name }) => {
|
|
27894
|
+
}, async ({ name }) => readSurface(async () => {
|
|
27895
|
+
await requireSkillsReadAccess();
|
|
27276
27896
|
const doc2 = getSkillBestDoc(name);
|
|
27277
27897
|
if (!doc2) {
|
|
27278
27898
|
return mcpError("NO_DOCS", `No documentation found for '${name}'`);
|
|
27279
27899
|
}
|
|
27280
27900
|
return { content: [{ type: "text", text: doc2 }] };
|
|
27281
|
-
});
|
|
27901
|
+
}));
|
|
27282
27902
|
server.registerTool("list_tool_primitives", {
|
|
27283
27903
|
title: "List Tool Primitives",
|
|
27284
27904
|
description: "List primitive tools that skills depend on across CLI, MCP, API, and hosted worker execution.",
|
|
@@ -27324,25 +27944,74 @@ function registerDiscoveryTools(server) {
|
|
|
27324
27944
|
}
|
|
27325
27945
|
|
|
27326
27946
|
// src/mcp/operation-tools.ts
|
|
27327
|
-
import { existsSync as
|
|
27947
|
+
import { existsSync as existsSync14, readdirSync as readdirSync8, statSync as statSync9 } from "fs";
|
|
27328
27948
|
import { join as join14 } from "path";
|
|
27329
27949
|
|
|
27950
|
+
// src/lib/credential-state.ts
|
|
27951
|
+
init_auth_store();
|
|
27952
|
+
init_fleet_credentials();
|
|
27953
|
+
function describeCredentialState() {
|
|
27954
|
+
let credentialsFile = null;
|
|
27955
|
+
let mode = null;
|
|
27956
|
+
try {
|
|
27957
|
+
credentialsFile = getAuthFilePath();
|
|
27958
|
+
const bits = credentialFileMode();
|
|
27959
|
+
mode = bits === null ? null : `0${bits.toString(8).padStart(3, "0")}`;
|
|
27960
|
+
} catch {}
|
|
27961
|
+
try {
|
|
27962
|
+
const fleet = resolveSkillsFleet();
|
|
27963
|
+
if (fleet.mode === "hosted") {
|
|
27964
|
+
return {
|
|
27965
|
+
mode: "hosted",
|
|
27966
|
+
apiUrl: fleet.apiOrigin,
|
|
27967
|
+
apiUrlSource: fleet.apiUrlSource,
|
|
27968
|
+
apiKeySource: fleet.apiKeySource,
|
|
27969
|
+
apiKeyTier: fleet.apiKeyTier,
|
|
27970
|
+
credentialsFile,
|
|
27971
|
+
credentialsFileMode: mode,
|
|
27972
|
+
error: null
|
|
27973
|
+
};
|
|
27974
|
+
}
|
|
27975
|
+
return {
|
|
27976
|
+
mode: "local",
|
|
27977
|
+
apiUrl: null,
|
|
27978
|
+
apiUrlSource: null,
|
|
27979
|
+
apiKeySource: null,
|
|
27980
|
+
apiKeyTier: null,
|
|
27981
|
+
credentialsFile,
|
|
27982
|
+
credentialsFileMode: mode,
|
|
27983
|
+
error: null
|
|
27984
|
+
};
|
|
27985
|
+
} catch (error2) {
|
|
27986
|
+
return {
|
|
27987
|
+
mode: "misconfigured",
|
|
27988
|
+
apiUrl: null,
|
|
27989
|
+
apiUrlSource: null,
|
|
27990
|
+
apiKeySource: null,
|
|
27991
|
+
apiKeyTier: null,
|
|
27992
|
+
credentialsFile,
|
|
27993
|
+
credentialsFileMode: mode,
|
|
27994
|
+
error: error2.message
|
|
27995
|
+
};
|
|
27996
|
+
}
|
|
27997
|
+
}
|
|
27998
|
+
|
|
27330
27999
|
// src/lib/run-state.ts
|
|
27331
28000
|
import { createHash as createHash2, randomBytes } from "crypto";
|
|
27332
|
-
import { existsSync as
|
|
27333
|
-
import { extname, join as
|
|
28001
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync12, readdirSync as readdirSync7, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
28002
|
+
import { extname, join as join13, relative as relative2 } from "path";
|
|
27334
28003
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
27335
28004
|
const now = new Date;
|
|
27336
28005
|
const id = createRunId(now);
|
|
27337
28006
|
const day = now.toISOString().slice(0, 10);
|
|
27338
28007
|
const skillName = normalizeSkillName(params.skill);
|
|
27339
28008
|
const root = getProjectStateDir(targetDir);
|
|
27340
|
-
const runDir =
|
|
27341
|
-
const logsDir =
|
|
27342
|
-
const exportDir =
|
|
27343
|
-
|
|
27344
|
-
|
|
27345
|
-
|
|
28009
|
+
const runDir = join13(root, "runs", day, id);
|
|
28010
|
+
const logsDir = join13(runDir, "logs");
|
|
28011
|
+
const exportDir = join13(root, "exports", skillName, id);
|
|
28012
|
+
mkdirSync6(logsDir, { recursive: true });
|
|
28013
|
+
mkdirSync6(exportDir, { recursive: true });
|
|
28014
|
+
mkdirSync6(join13(root, "tmp"), { recursive: true });
|
|
27346
28015
|
const record3 = {
|
|
27347
28016
|
id,
|
|
27348
28017
|
skill: skillName,
|
|
@@ -27394,22 +28063,22 @@ function updateSkillRun(context, patch) {
|
|
|
27394
28063
|
return context.record;
|
|
27395
28064
|
}
|
|
27396
28065
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
27397
|
-
|
|
27398
|
-
|
|
28066
|
+
writeFileSync6(join13(context.logsDir, "stdout.log"), stdout);
|
|
28067
|
+
writeFileSync6(join13(context.logsDir, "stderr.log"), stderr);
|
|
27399
28068
|
}
|
|
27400
28069
|
function appendRunEvent(context, event, data = {}) {
|
|
27401
28070
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
27402
28071
|
`;
|
|
27403
|
-
const path =
|
|
27404
|
-
const previous =
|
|
27405
|
-
|
|
28072
|
+
const path = join13(context.runDir, "events.ndjson");
|
|
28073
|
+
const previous = existsSync13(path) ? readFileSync12(path, "utf-8") : "";
|
|
28074
|
+
writeFileSync6(path, previous + line);
|
|
27406
28075
|
}
|
|
27407
28076
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
27408
|
-
const runsRoot =
|
|
27409
|
-
if (!
|
|
28077
|
+
const runsRoot = join13(getProjectStateDir(targetDir), "runs");
|
|
28078
|
+
if (!existsSync13(runsRoot))
|
|
27410
28079
|
return null;
|
|
27411
28080
|
for (const day of readdirSync7(runsRoot)) {
|
|
27412
|
-
const record3 = readRunRecord(
|
|
28081
|
+
const record3 = readRunRecord(join13(runsRoot, day, runId));
|
|
27413
28082
|
if (record3)
|
|
27414
28083
|
return record3;
|
|
27415
28084
|
}
|
|
@@ -27426,20 +28095,20 @@ function skillRunEnv(context) {
|
|
|
27426
28095
|
};
|
|
27427
28096
|
}
|
|
27428
28097
|
function writeRunRecord(context) {
|
|
27429
|
-
|
|
28098
|
+
writeFileSync6(join13(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
27430
28099
|
`);
|
|
27431
28100
|
}
|
|
27432
28101
|
function writeArtifactsManifest(context, artifacts) {
|
|
27433
|
-
|
|
28102
|
+
writeFileSync6(join13(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
27434
28103
|
`);
|
|
27435
28104
|
}
|
|
27436
28105
|
function collectRunArtifacts(context) {
|
|
27437
|
-
if (!
|
|
28106
|
+
if (!existsSync13(context.exportDir))
|
|
27438
28107
|
return [];
|
|
27439
28108
|
const artifacts = [];
|
|
27440
28109
|
for (const path of walkFiles(context.exportDir)) {
|
|
27441
|
-
const stat =
|
|
27442
|
-
const bytes =
|
|
28110
|
+
const stat = statSync8(path);
|
|
28111
|
+
const bytes = readFileSync12(path);
|
|
27443
28112
|
artifacts.push({
|
|
27444
28113
|
path: toProjectRelative(context.targetDir, path),
|
|
27445
28114
|
mime: mimeForPath(path),
|
|
@@ -27450,11 +28119,11 @@ function collectRunArtifacts(context) {
|
|
|
27450
28119
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
27451
28120
|
}
|
|
27452
28121
|
function readRunRecord(runDir) {
|
|
27453
|
-
const path =
|
|
27454
|
-
if (!
|
|
28122
|
+
const path = join13(runDir, "run.json");
|
|
28123
|
+
if (!existsSync13(path))
|
|
27455
28124
|
return null;
|
|
27456
28125
|
try {
|
|
27457
|
-
return JSON.parse(
|
|
28126
|
+
return JSON.parse(readFileSync12(path, "utf-8"));
|
|
27458
28127
|
} catch {
|
|
27459
28128
|
return null;
|
|
27460
28129
|
}
|
|
@@ -27462,8 +28131,8 @@ function readRunRecord(runDir) {
|
|
|
27462
28131
|
function walkFiles(dir) {
|
|
27463
28132
|
const files = [];
|
|
27464
28133
|
for (const entry of readdirSync7(dir)) {
|
|
27465
|
-
const full =
|
|
27466
|
-
if (
|
|
28134
|
+
const full = join13(dir, entry);
|
|
28135
|
+
if (statSync8(full).isDirectory())
|
|
27467
28136
|
files.push(...walkFiles(full));
|
|
27468
28137
|
else
|
|
27469
28138
|
files.push(full);
|
|
@@ -27536,17 +28205,20 @@ async function resolveConfiguredRunRouting(skill, env = process.env) {
|
|
|
27536
28205
|
try {
|
|
27537
28206
|
connection = await resolveSkillsConnection(env);
|
|
27538
28207
|
} catch (error2) {
|
|
27539
|
-
const isMissingCredential = (error2
|
|
28208
|
+
const isMissingCredential = isSkillsFleetCredentialError2(error2) && error2.code === "MISSING_API_CREDENTIAL";
|
|
27540
28209
|
if (!isMissingCredential)
|
|
27541
28210
|
throw error2;
|
|
27542
28211
|
return {
|
|
27543
28212
|
route: "error",
|
|
27544
28213
|
code: "REMOTE_REQUIRES_CREDENTIAL",
|
|
27545
|
-
error: `${skill.name} is a server-owned skill. ${error2.message}`
|
|
28214
|
+
error: skill.serverOwned ? `${skill.name} is a server-owned skill. ${error2.message}` : `${skill.name} cannot run: ${error2.message}`
|
|
27546
28215
|
};
|
|
27547
28216
|
}
|
|
27548
28217
|
return resolveRunRouting(skill, connection?.apiKey, connection?.apiOrigin);
|
|
27549
28218
|
}
|
|
28219
|
+
function isSkillsFleetCredentialError2(error2) {
|
|
28220
|
+
return typeof error2 === "object" && error2 !== null && error2.name === "SkillsFleetCredentialError";
|
|
28221
|
+
}
|
|
27550
28222
|
|
|
27551
28223
|
// src/mcp/operation-tools.ts
|
|
27552
28224
|
function registerOperationTools(server) {
|
|
@@ -27709,39 +28381,42 @@ function registerOperationTools(server) {
|
|
|
27709
28381
|
server.registerTool("list_categories", {
|
|
27710
28382
|
title: "List Categories",
|
|
27711
28383
|
description: "List all 17 skill categories with skill counts."
|
|
27712
|
-
}, async () => {
|
|
27713
|
-
const
|
|
28384
|
+
}, async () => readSurface(async () => {
|
|
28385
|
+
const registry2 = await getBrowseRegistry({ all: true });
|
|
28386
|
+
const extras = Array.from(new Set(registry2.map((skill) => skill.category))).filter((category) => !CATEGORIES.includes(category)).sort();
|
|
28387
|
+
const cats = [...CATEGORIES, ...extras].map((category) => ({
|
|
27714
28388
|
name: category,
|
|
27715
|
-
count:
|
|
28389
|
+
count: registry2.filter((skill) => skill.category === category).length
|
|
27716
28390
|
}));
|
|
27717
28391
|
return { content: [{ type: "text", text: JSON.stringify(cats, null, 2) }] };
|
|
27718
|
-
});
|
|
28392
|
+
}));
|
|
27719
28393
|
server.registerTool("list_tags", {
|
|
27720
28394
|
title: "List Tags",
|
|
27721
28395
|
description: "List all unique skill tags with occurrence counts."
|
|
27722
|
-
}, async () => {
|
|
28396
|
+
}, async () => readSurface(async () => {
|
|
27723
28397
|
const tagCounts = new Map;
|
|
27724
|
-
for (const skill of
|
|
28398
|
+
for (const skill of await getBrowseRegistry({ all: true })) {
|
|
27725
28399
|
for (const tag of skill.tags) {
|
|
27726
28400
|
tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
|
|
27727
28401
|
}
|
|
27728
28402
|
}
|
|
27729
28403
|
const sorted = Array.from(tagCounts.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([name, count]) => ({ name, count }));
|
|
27730
28404
|
return { content: [{ type: "text", text: JSON.stringify(sorted, null, 2) }] };
|
|
27731
|
-
});
|
|
28405
|
+
}));
|
|
27732
28406
|
server.registerTool("get_requirements", {
|
|
27733
28407
|
title: "Get Requirements",
|
|
27734
28408
|
description: "Get env vars, system deps, and npm dependencies for a skill.",
|
|
27735
28409
|
inputSchema: {
|
|
27736
28410
|
name: exports_external.string()
|
|
27737
28411
|
}
|
|
27738
|
-
}, async ({ name }) => {
|
|
28412
|
+
}, async ({ name }) => readSurface(async () => {
|
|
28413
|
+
await requireSkillsReadAccess();
|
|
27739
28414
|
const reqs = getSkillRequirements(name);
|
|
27740
28415
|
if (!reqs) {
|
|
27741
28416
|
return mcpError("SKILL_NOT_FOUND", `Skill '${name}' not found`, findSimilarSkills(name));
|
|
27742
28417
|
}
|
|
27743
28418
|
return { content: [{ type: "text", text: JSON.stringify(reqs, null, 2) }] };
|
|
27744
|
-
});
|
|
28419
|
+
}));
|
|
27745
28420
|
server.registerTool("run_skill", {
|
|
27746
28421
|
title: "Run Skill",
|
|
27747
28422
|
description: "Run a skill by name with optional arguments.",
|
|
@@ -27783,20 +28458,16 @@ function registerOperationTools(server) {
|
|
|
27783
28458
|
} catch (error2) {
|
|
27784
28459
|
return mcpError("INVALID_INPUT_FILES", error2.message);
|
|
27785
28460
|
}
|
|
28461
|
+
if (routing.route === "error") {
|
|
28462
|
+
const suggestions = routing.code === "REMOTE_REQUIRES_ORIGIN" ? ["skills setup --api-url <url>", "skills auth login"] : ["skills auth login"];
|
|
28463
|
+
return mcpError(routing.code, routing.error, suggestions);
|
|
28464
|
+
}
|
|
27786
28465
|
const runContext = createSkillRun({
|
|
27787
28466
|
skill: skillName,
|
|
27788
28467
|
args: runArgs,
|
|
27789
28468
|
remote: routing.route === "remote",
|
|
27790
28469
|
...routing.route === "remote" ? { remoteApiOrigin: routing.apiOrigin } : {}
|
|
27791
28470
|
});
|
|
27792
|
-
if (routing.route === "error") {
|
|
27793
|
-
const error2 = routing.error;
|
|
27794
|
-
writeRunLogs(runContext, "", error2 + `
|
|
27795
|
-
`);
|
|
27796
|
-
const run = completeSkillRun(runContext, { status: "failed", error: error2 });
|
|
27797
|
-
const suggestions = routing.code === "REMOTE_REQUIRES_ORIGIN" ? ["skills setup --api-url <url>", "skills auth login"] : ["skills auth login"];
|
|
27798
|
-
return mcpError(routing.code, `${error2}. Local run metadata: ${run.paths.runDir}/run.json`, suggestions);
|
|
27799
|
-
}
|
|
27800
28471
|
if (routing.route === "remote") {
|
|
27801
28472
|
try {
|
|
27802
28473
|
const { RemoteSkillsClient: RemoteSkillsClient2 } = await Promise.resolve().then(() => (init_remote_client(), exports_remote_client));
|
|
@@ -27953,21 +28624,22 @@ function registerOperationTools(server) {
|
|
|
27953
28624
|
});
|
|
27954
28625
|
server.registerTool("whoami", {
|
|
27955
28626
|
title: "Skills Whoami",
|
|
27956
|
-
description: "Show setup summary: version, pinned skills, agent configs, cwd."
|
|
28627
|
+
description: "Show setup summary: version, pinned skills, agent configs, cwd, and the credential/transport SOURCES (never values)."
|
|
27957
28628
|
}, async () => {
|
|
27958
28629
|
const version2 = package_default.version;
|
|
27959
28630
|
const cwd = process.cwd();
|
|
28631
|
+
const credential = describeCredentialState();
|
|
27960
28632
|
const installed = getInstalledSkills();
|
|
27961
28633
|
const agents = [];
|
|
27962
28634
|
for (const agent of AGENT_TARGETS) {
|
|
27963
28635
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
27964
|
-
const exists =
|
|
28636
|
+
const exists = existsSync14(agentSkillsPath);
|
|
27965
28637
|
let skillCount = 0;
|
|
27966
28638
|
if (exists) {
|
|
27967
28639
|
try {
|
|
27968
28640
|
skillCount = readdirSync8(agentSkillsPath).filter((f) => {
|
|
27969
28641
|
const full = join14(agentSkillsPath, f);
|
|
27970
|
-
return !f.startsWith(".") &&
|
|
28642
|
+
return !f.startsWith(".") && statSync9(full).isDirectory();
|
|
27971
28643
|
}).length;
|
|
27972
28644
|
} catch {}
|
|
27973
28645
|
}
|
|
@@ -27980,7 +28652,8 @@ function registerOperationTools(server) {
|
|
|
27980
28652
|
installed,
|
|
27981
28653
|
agents,
|
|
27982
28654
|
skillsDir,
|
|
27983
|
-
cwd
|
|
28655
|
+
cwd,
|
|
28656
|
+
credential
|
|
27984
28657
|
};
|
|
27985
28658
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
27986
28659
|
});
|
|
@@ -28012,29 +28685,17 @@ function compactRunToolPayload(payload, detailHint) {
|
|
|
28012
28685
|
}
|
|
28013
28686
|
|
|
28014
28687
|
// src/lib/feedback.ts
|
|
28015
|
-
import { appendFileSync, existsSync as
|
|
28688
|
+
import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "fs";
|
|
28016
28689
|
import { dirname as dirname5, join as join15 } from "path";
|
|
28017
28690
|
import { Database } from "bun:sqlite";
|
|
28018
|
-
|
|
28019
|
-
// src/lib/api-url.ts
|
|
28020
|
-
init_fleet_credentials();
|
|
28021
|
-
init_fleet_credentials();
|
|
28022
|
-
var API_URL_ENV_VAR = SKILLS_API_URL_ENV;
|
|
28023
|
-
var 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>`;
|
|
28024
|
-
function resolveApiUrl(env = process.env, options = {}) {
|
|
28025
|
-
const fleet = resolveSkillsFleet(env, options);
|
|
28026
|
-
return fleet.mode === "hosted" ? fleet.apiOrigin : undefined;
|
|
28027
|
-
}
|
|
28028
|
-
|
|
28029
|
-
// src/lib/feedback.ts
|
|
28030
28691
|
function getFeedbackDbPath() {
|
|
28031
28692
|
return join15(getDataDir(), "skills.db");
|
|
28032
28693
|
}
|
|
28033
28694
|
function getFeedbackDb() {
|
|
28034
28695
|
const dbPath = getFeedbackDbPath();
|
|
28035
28696
|
const dir = dirname5(dbPath);
|
|
28036
|
-
if (!
|
|
28037
|
-
|
|
28697
|
+
if (!existsSync15(dir))
|
|
28698
|
+
mkdirSync7(dir, { recursive: true });
|
|
28038
28699
|
const db = new Database(dbPath);
|
|
28039
28700
|
db.exec("PRAGMA journal_mode = WAL");
|
|
28040
28701
|
db.exec([
|
|
@@ -28062,8 +28723,8 @@ function saveFeedback(input) {
|
|
|
28062
28723
|
if (isApiMode()) {
|
|
28063
28724
|
const path = join15(getDataDir(), "feedback.jsonl");
|
|
28064
28725
|
const dir = dirname5(path);
|
|
28065
|
-
if (!
|
|
28066
|
-
|
|
28726
|
+
if (!existsSync15(dir))
|
|
28727
|
+
mkdirSync7(dir, { recursive: true });
|
|
28067
28728
|
appendFileSync(path, JSON.stringify({ message, category, email: input.email ?? null, agent: input.agent ?? null, version: input.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
28068
28729
|
`);
|
|
28069
28730
|
return { saved: true, category, path };
|
|
@@ -28208,16 +28869,16 @@ function registerResourceMetaTools(server) {
|
|
|
28208
28869
|
}
|
|
28209
28870
|
|
|
28210
28871
|
// src/lib/scheduler.ts
|
|
28211
|
-
import { existsSync as
|
|
28872
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
|
|
28212
28873
|
import { join as join16 } from "path";
|
|
28213
28874
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
28214
28875
|
return join16(targetDir, ".skills", "schedules.json");
|
|
28215
28876
|
}
|
|
28216
28877
|
function loadSchedules(targetDir = process.cwd()) {
|
|
28217
28878
|
const path = getSchedulesPath(targetDir);
|
|
28218
|
-
if (
|
|
28879
|
+
if (existsSync16(path)) {
|
|
28219
28880
|
try {
|
|
28220
|
-
return JSON.parse(
|
|
28881
|
+
return JSON.parse(readFileSync13(path, "utf-8"));
|
|
28221
28882
|
} catch {}
|
|
28222
28883
|
}
|
|
28223
28884
|
return { version: 1, schedules: [] };
|
|
@@ -28225,9 +28886,9 @@ function loadSchedules(targetDir = process.cwd()) {
|
|
|
28225
28886
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
28226
28887
|
const path = getSchedulesPath(targetDir);
|
|
28227
28888
|
const dir = join16(targetDir, ".skills");
|
|
28228
|
-
if (!
|
|
28229
|
-
|
|
28230
|
-
|
|
28889
|
+
if (!existsSync16(dir))
|
|
28890
|
+
mkdirSync8(dir, { recursive: true });
|
|
28891
|
+
writeFileSync7(path, JSON.stringify(data, null, 2));
|
|
28231
28892
|
}
|
|
28232
28893
|
function validateCronField(expr, min, max, label) {
|
|
28233
28894
|
for (const part of expr.split(",")) {
|
|
@@ -28487,12 +29148,12 @@ function registerScheduleTools(server) {
|
|
|
28487
29148
|
// src/lib/native-storage.ts
|
|
28488
29149
|
import { createHash as createHash4, createHmac } from "crypto";
|
|
28489
29150
|
import {
|
|
28490
|
-
existsSync as
|
|
28491
|
-
mkdirSync as
|
|
28492
|
-
readFileSync as
|
|
29151
|
+
existsSync as existsSync17,
|
|
29152
|
+
mkdirSync as mkdirSync9,
|
|
29153
|
+
readFileSync as readFileSync14,
|
|
28493
29154
|
readdirSync as readdirSync9,
|
|
28494
|
-
statSync as
|
|
28495
|
-
writeFileSync as
|
|
29155
|
+
statSync as statSync10,
|
|
29156
|
+
writeFileSync as writeFileSync8
|
|
28496
29157
|
} from "fs";
|
|
28497
29158
|
import { dirname as dirname6, join as join17, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
|
|
28498
29159
|
var SKILLS_STORAGE_TABLES = [
|
|
@@ -28590,9 +29251,9 @@ function getStorageStatus(options = {}) {
|
|
|
28590
29251
|
function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
28591
29252
|
const projectStateDir = getProjectStateDir(targetDir);
|
|
28592
29253
|
const files = [];
|
|
28593
|
-
if (
|
|
29254
|
+
if (existsSync17(projectStateDir)) {
|
|
28594
29255
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
28595
|
-
const bytes =
|
|
29256
|
+
const bytes = readFileSync14(filePath);
|
|
28596
29257
|
const relativePath = toPosix(relative3(targetDir, filePath));
|
|
28597
29258
|
files.push({
|
|
28598
29259
|
path: relativePath,
|
|
@@ -28675,7 +29336,7 @@ function walkFiles2(dir) {
|
|
|
28675
29336
|
const files = [];
|
|
28676
29337
|
for (const entry of readdirSync9(dir)) {
|
|
28677
29338
|
const full = join17(dir, entry);
|
|
28678
|
-
const stats =
|
|
29339
|
+
const stats = statSync10(full);
|
|
28679
29340
|
if (stats.isDirectory())
|
|
28680
29341
|
files.push(...walkFiles2(full));
|
|
28681
29342
|
else
|
|
@@ -28730,6 +29391,8 @@ function registerStorageTools(server) {
|
|
|
28730
29391
|
}
|
|
28731
29392
|
|
|
28732
29393
|
// src/lib/remote-auth.ts
|
|
29394
|
+
init_remote_workspace();
|
|
29395
|
+
init_remote_workspace();
|
|
28733
29396
|
init_remote_client();
|
|
28734
29397
|
init_fleet_credentials();
|
|
28735
29398
|
var MAX_ERROR_DETAIL_LENGTH = 200;
|
|
@@ -28771,10 +29434,10 @@ async function requestAuthApi(instance, path, options) {
|
|
|
28771
29434
|
const text = await res.text();
|
|
28772
29435
|
const body = text ? parseJsonBody(text) : {};
|
|
28773
29436
|
if (!res.ok) {
|
|
28774
|
-
const
|
|
28775
|
-
const detail = typeof
|
|
28776
|
-
const error2 = typeof
|
|
28777
|
-
const code = typeof
|
|
29437
|
+
const record4 = isRecord5(body) ? body : {};
|
|
29438
|
+
const detail = typeof record4.detail === "string" ? record4.detail : undefined;
|
|
29439
|
+
const error2 = typeof record4.error === "string" ? record4.error : undefined;
|
|
29440
|
+
const code = typeof record4.code === "string" ? record4.code : undefined;
|
|
28778
29441
|
throw new HostedApiError(detail || error2 || `${res.status} ${res.statusText}`, {
|
|
28779
29442
|
status: res.status,
|
|
28780
29443
|
code,
|
|
@@ -28799,7 +29462,7 @@ function condenseErrorBody(text) {
|
|
|
28799
29462
|
return collapsed;
|
|
28800
29463
|
return `${collapsed.slice(0, MAX_ERROR_DETAIL_LENGTH - 1).trimEnd()}\u2026`;
|
|
28801
29464
|
}
|
|
28802
|
-
function
|
|
29465
|
+
function isRecord5(value) {
|
|
28803
29466
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
28804
29467
|
}
|
|
28805
29468
|
|
|
@@ -28821,12 +29484,13 @@ class RemoteSkillsAuthClient {
|
|
|
28821
29484
|
return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
|
|
28822
29485
|
}
|
|
28823
29486
|
async sessionClient(email2, code) {
|
|
29487
|
+
const apiOrigin = this.apiOrigin;
|
|
28824
29488
|
if (!email2.includes("@") || !/^\d{6}$/.test(code))
|
|
28825
|
-
throw new Error("Fresh email and six-digit verification code are required to manage
|
|
29489
|
+
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
28826
29490
|
const login = await this.verifyCode(email2, code);
|
|
28827
29491
|
if (!login || typeof login.token !== "string" || !login.token)
|
|
28828
29492
|
throw new Error("The server did not return an authorized account session");
|
|
28829
|
-
return new RemoteSkillsClient(login.token,
|
|
29493
|
+
return new RemoteSkillsClient(login.token, apiOrigin);
|
|
28830
29494
|
}
|
|
28831
29495
|
async createApiKey(email2, code, name, scopes) {
|
|
28832
29496
|
return (await this.sessionClient(email2, code)).createApiKey(name, scopes);
|
|
@@ -28837,6 +29501,26 @@ class RemoteSkillsAuthClient {
|
|
|
28837
29501
|
async revokeApiKey(email2, code, keyId) {
|
|
28838
29502
|
return (await this.sessionClient(email2, code)).revokeApiKey(keyId);
|
|
28839
29503
|
}
|
|
29504
|
+
async updateProfile(email2, code, input) {
|
|
29505
|
+
customerNamePatch(input, "displayName");
|
|
29506
|
+
return (await this.sessionClient(email2, code)).updateProfile(input);
|
|
29507
|
+
}
|
|
29508
|
+
async updateCurrentWorkspace(email2, code, input) {
|
|
29509
|
+
customerNamePatch(input, "name");
|
|
29510
|
+
return (await this.sessionClient(email2, code)).updateCurrentWorkspace(input);
|
|
29511
|
+
}
|
|
29512
|
+
async listWorkspaceMembers(email2, code, options = {}) {
|
|
29513
|
+
workspaceMembersQuery(options);
|
|
29514
|
+
return (await this.sessionClient(email2, code)).listWorkspaceMembers(options);
|
|
29515
|
+
}
|
|
29516
|
+
async setWorkspaceMemberRole(email2, code, membershipId, input) {
|
|
29517
|
+
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
29518
|
+
return (await this.sessionClient(email2, code)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
29519
|
+
}
|
|
29520
|
+
async removeWorkspaceMember(email2, code, membershipId, input) {
|
|
29521
|
+
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
29522
|
+
return (await this.sessionClient(email2, code)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
29523
|
+
}
|
|
28840
29524
|
request(path, options) {
|
|
28841
29525
|
if (!["/api/auth/login", "/api/auth/verify", "/api/auth/device/start", "/api/auth/device/token", "/api/auth/keys", "/api/auth/whoami"].includes(path))
|
|
28842
29526
|
throw new Error("Unsupported authentication operation");
|
|
@@ -28848,6 +29532,65 @@ class RemoteSkillsAuthClient {
|
|
|
28848
29532
|
init_auth_store();
|
|
28849
29533
|
init_remote_client();
|
|
28850
29534
|
function registerRemoteCustomerTools(server) {
|
|
29535
|
+
const memberRole = exports_external.enum(["owner", "admin", "member", "viewer"]);
|
|
29536
|
+
const memberInput = {
|
|
29537
|
+
membershipId: exports_external.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/),
|
|
29538
|
+
expectedRole: memberRole,
|
|
29539
|
+
email: exports_external.string().email(),
|
|
29540
|
+
code: exports_external.string().regex(/^\d{6}$/)
|
|
29541
|
+
};
|
|
29542
|
+
server.registerTool("set_workspace_member_role", {
|
|
29543
|
+
title: "Set Current Workspace Member Role",
|
|
29544
|
+
description: "Change exactly this membership incarnation with its observed expectedRole and fresh verification. The server enforces owner/admin policy. No automatic refresh or retry; saved credentials stay unchanged.",
|
|
29545
|
+
inputSchema: exports_external.object({ ...memberInput, role: memberRole }).strict()
|
|
29546
|
+
}, async ({ membershipId, role, expectedRole, email: email2, code }) => {
|
|
29547
|
+
try {
|
|
29548
|
+
return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Set workspace member role")).setWorkspaceMemberRole(email2, code, membershipId, { role, expectedRole }));
|
|
29549
|
+
} catch (error2) {
|
|
29550
|
+
return memberError(error2);
|
|
29551
|
+
}
|
|
29552
|
+
});
|
|
29553
|
+
server.registerTool("remove_workspace_member", {
|
|
29554
|
+
title: "Remove Current Workspace Member",
|
|
29555
|
+
description: "Remove exactly this membership incarnation using its observed expectedRole and fresh verification. Self-removal is unavailable. A retry cannot remove a later replacement membership; saved credentials stay unchanged.",
|
|
29556
|
+
inputSchema: exports_external.object(memberInput).strict()
|
|
29557
|
+
}, async ({ membershipId, expectedRole, email: email2, code }) => {
|
|
29558
|
+
try {
|
|
29559
|
+
return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("Remove workspace member")).removeWorkspaceMember(email2, code, membershipId, { expectedRole }));
|
|
29560
|
+
} catch (error2) {
|
|
29561
|
+
return memberError(error2);
|
|
29562
|
+
}
|
|
29563
|
+
});
|
|
29564
|
+
server.registerTool("list_workspace_members", {
|
|
29565
|
+
title: "List Current Workspace Members",
|
|
29566
|
+
description: "Read one roster page on the selected Skills server using fresh owner/admin email verification. Saved credentials are unchanged. This does not invite, change or switch members/workspaces.",
|
|
29567
|
+
inputSchema: exports_external.object({
|
|
29568
|
+
email: exports_external.string().email(),
|
|
29569
|
+
code: exports_external.string().regex(/^\d{6}$/),
|
|
29570
|
+
limit: exports_external.number().int().min(1).max(100).optional(),
|
|
29571
|
+
cursor: exports_external.string().regex(/^[A-Za-z0-9_-]{1,512}$/).optional()
|
|
29572
|
+
}).strict()
|
|
29573
|
+
}, async ({ email: email2, code, limit, cursor: cursor2 }) => {
|
|
29574
|
+
try {
|
|
29575
|
+
return mcpJson(await new RemoteSkillsAuthClient(getApiUrl("List workspace members")).listWorkspaceMembers(email2, code, { limit, cursor: cursor2 }));
|
|
29576
|
+
} catch {
|
|
29577
|
+
return mcpError("WORKSPACE_MEMBERS_FAILED", "Unable to list workspace members. Check the selected server, owner/admin permissions, pagination and fresh verification code.");
|
|
29578
|
+
}
|
|
29579
|
+
});
|
|
29580
|
+
for (const kind of ["profile", "workspace"]) {
|
|
29581
|
+
server.registerTool(kind === "profile" ? "update_account_profile" : "update_workspace_name", {
|
|
29582
|
+
title: kind === "profile" ? "Update Account Display Name" : "Update Workspace Name",
|
|
29583
|
+
description: "Update only the name on the explicitly selected Skills server using fresh email OTP. Workspace changes require an owner/admin. Saved credentials are unchanged.",
|
|
29584
|
+
inputSchema: exports_external.object({ name: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }).strict()
|
|
29585
|
+
}, async ({ name, email: email2, code }) => {
|
|
29586
|
+
try {
|
|
29587
|
+
const client = new RemoteSkillsAuthClient(getApiUrl("Update customer name"));
|
|
29588
|
+
return mcpJson(kind === "profile" ? await client.updateProfile(email2, code, { displayName: name }) : await client.updateCurrentWorkspace(email2, code, { name }));
|
|
29589
|
+
} catch {
|
|
29590
|
+
return mcpError("NAME_UPDATE_FAILED", "Unable to update the name. Check the selected server, name, permissions and fresh verification code.");
|
|
29591
|
+
}
|
|
29592
|
+
});
|
|
29593
|
+
}
|
|
28851
29594
|
for (const operation of REMOTE_CUSTOMER_OPERATIONS) {
|
|
28852
29595
|
const inputSchema = {};
|
|
28853
29596
|
if (operation.parameter)
|
|
@@ -28906,6 +29649,9 @@ function registerRemoteCustomerTools(server) {
|
|
|
28906
29649
|
return { ...metadata, base64: Buffer.from(bytes).toString("base64") };
|
|
28907
29650
|
}));
|
|
28908
29651
|
}
|
|
29652
|
+
function memberError(error2) {
|
|
29653
|
+
return error2 instanceof RemoteWorkspaceMemberError ? mcpError(error2.code, error2.message) : mcpError("WORKSPACE_MEMBER_FAILED", "Unable to manage workspace member. Check the selected server and fresh verification, then refresh the roster before another action.");
|
|
29654
|
+
}
|
|
28909
29655
|
async function callRemote(action) {
|
|
28910
29656
|
try {
|
|
28911
29657
|
const client = await createRemoteSkillsClient();
|
|
@@ -28913,6 +29659,9 @@ async function callRemote(action) {
|
|
|
28913
29659
|
return mcpError("AUTH_REQUIRED", "Configure a Skills API and sign in with skills auth login");
|
|
28914
29660
|
return mcpJson(await action(client));
|
|
28915
29661
|
} catch (error2) {
|
|
29662
|
+
if (error2 instanceof RemoteCapabilityUnavailableError) {
|
|
29663
|
+
return { ...mcpJson({ code: error2.code, message: error2.message, status: error2.status }), isError: true };
|
|
29664
|
+
}
|
|
28916
29665
|
return mcpError("REMOTE_REQUEST_FAILED", error2 instanceof Error ? error2.message : "Skills server request failed");
|
|
28917
29666
|
}
|
|
28918
29667
|
}
|
|
@@ -30383,6 +31132,7 @@ async function startSkillsMcpHttpServer(options = {}) {
|
|
|
30383
31132
|
}
|
|
30384
31133
|
|
|
30385
31134
|
// src/mcp/index.ts
|
|
31135
|
+
init_fleet_credentials();
|
|
30386
31136
|
var args = process.argv.slice(2);
|
|
30387
31137
|
function printHelp() {
|
|
30388
31138
|
console.log(`Usage: skills-mcp [options]
|
|
@@ -30404,7 +31154,18 @@ if (args.includes("--version") || args.includes("-V")) {
|
|
|
30404
31154
|
console.log(package_default.version);
|
|
30405
31155
|
process.exit(0);
|
|
30406
31156
|
}
|
|
31157
|
+
function assertSkillsMcpConfigured(env = process.env) {
|
|
31158
|
+
try {
|
|
31159
|
+
resolveSkillsFleet(env);
|
|
31160
|
+
} catch (error2) {
|
|
31161
|
+
if (!isSkillsFleetCredentialError(error2))
|
|
31162
|
+
throw error2;
|
|
31163
|
+
console.error(error2.message);
|
|
31164
|
+
process.exit(1);
|
|
31165
|
+
}
|
|
31166
|
+
}
|
|
30407
31167
|
async function startMcpStdio() {
|
|
31168
|
+
assertSkillsMcpConfigured();
|
|
30408
31169
|
const server2 = buildServer();
|
|
30409
31170
|
await server2.connect(new StdioServerTransport);
|
|
30410
31171
|
}
|
|
@@ -30413,6 +31174,7 @@ async function main() {
|
|
|
30413
31174
|
await startMcpStdio();
|
|
30414
31175
|
return;
|
|
30415
31176
|
}
|
|
31177
|
+
assertSkillsMcpConfigured();
|
|
30416
31178
|
const port = parseMcpHttpPort(args);
|
|
30417
31179
|
await startSkillsMcpHttpServer({ port, hostname: "127.0.0.1" });
|
|
30418
31180
|
}
|
|
@@ -30424,5 +31186,6 @@ if (import.meta.main) {
|
|
|
30424
31186
|
}
|
|
30425
31187
|
export {
|
|
30426
31188
|
startMcpStdio,
|
|
30427
|
-
buildServer
|
|
31189
|
+
buildServer,
|
|
31190
|
+
assertSkillsMcpConfigured
|
|
30428
31191
|
};
|