@hasna/skills 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -5
- package/bin/index.js +7747 -5645
- package/bin/mcp.js +1493 -431
- 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/commands/workspace-selection.d.ts +11 -0
- package/dist/cli/env-assignment.d.ts +9 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +841 -167
- 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 +23 -3
- package/dist/lib/remote-client.d.ts +45 -5
- package/dist/lib/remote-profile.d.ts +26 -0
- package/dist/lib/remote-registry.d.ts +7 -3
- package/dist/lib/remote-workspace-selection.d.ts +58 -0
- package/dist/lib/remote-workspace.d.ts +76 -0
- package/dist/lib/skillinfo.d.ts +1 -1
- package/dist/lib/workspace-profile.d.ts +49 -0
- 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 +8 -2
- package/dist/sdk/index.js +1312 -297
- 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,78 @@ 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
|
+
import { basename as basename3, dirname as dirname5, join as join13 } from "path";
|
|
8020
|
+
function getAuthFilePath(env = process.env) {
|
|
8021
|
+
return skillsCredentialFilePath(env);
|
|
8022
|
+
}
|
|
8023
|
+
function getIdentityFilePath(env = process.env) {
|
|
8024
|
+
const file = skillsCredentialFilePath(env);
|
|
8025
|
+
return join13(dirname5(file), basename3(file).replace(/^credentials/, "identity") + ".json");
|
|
8026
|
+
}
|
|
8027
|
+
function getApiUrl(action, env = process.env, options = {}) {
|
|
8028
|
+
return requireSkillsApiOrigin(action, env, options);
|
|
8029
|
+
}
|
|
8030
|
+
function credentialFileMode(env = process.env) {
|
|
8031
|
+
try {
|
|
8032
|
+
return statSync7(skillsCredentialFilePath(env)).mode & 511;
|
|
8033
|
+
} catch {
|
|
8034
|
+
return null;
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
8037
|
+
var init_auth_store = __esm(() => {
|
|
8038
|
+
init_transport();
|
|
8039
|
+
init_instance_credentials();
|
|
8040
|
+
init_fleet_credentials();
|
|
8041
|
+
init_transport();
|
|
8042
|
+
init_fleet_credentials();
|
|
8043
|
+
});
|
|
8044
|
+
|
|
8045
|
+
// src/lib/remote-run-contract.ts
|
|
8046
|
+
function normalizeRemoteSkillRunContract(payload, fallbackSkill) {
|
|
8047
|
+
const record3 = isRecord3(payload) ? payload : {};
|
|
8048
|
+
return {
|
|
8049
|
+
contractVersion: REMOTE_SKILL_RUN_CONTRACT_VERSION,
|
|
8050
|
+
...pickString(record3, "id"),
|
|
8051
|
+
skill: pickStringValue(record3, "skill") ?? fallbackSkill,
|
|
8052
|
+
...pickString(record3, "requestedSlug"),
|
|
8053
|
+
...pickString(record3, "status"),
|
|
8054
|
+
...pickNumber(record3, "exitCode"),
|
|
8055
|
+
...pickString(record3, "correlationId"),
|
|
8056
|
+
...pickString(record3, "createdAt"),
|
|
8057
|
+
...pickString(record3, "startedAt"),
|
|
8058
|
+
...pickString(record3, "completedAt"),
|
|
8059
|
+
...pickNumber(record3, "durationMs"),
|
|
8060
|
+
...pickString(record3, "outputType"),
|
|
8061
|
+
...hasOwn(record3, "outputPreview") ? { outputPreview: record3.outputPreview } : {},
|
|
8062
|
+
...pickString(record3, "errorCode"),
|
|
8063
|
+
...pickString(record3, "errorMessage"),
|
|
8064
|
+
...pickString(record3, "error"),
|
|
8065
|
+
...pickString(record3, "code"),
|
|
8066
|
+
...hasOwn(record3, "details") ? { details: record3.details } : {}
|
|
8067
|
+
};
|
|
8068
|
+
}
|
|
8069
|
+
function isRecord3(value) {
|
|
8070
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
8071
|
+
}
|
|
8072
|
+
function hasOwn(record3, key) {
|
|
8073
|
+
return Object.prototype.hasOwnProperty.call(record3, key);
|
|
8074
|
+
}
|
|
8075
|
+
function pickString(record3, key) {
|
|
8076
|
+
const value = pickStringValue(record3, key);
|
|
8077
|
+
return value === undefined ? {} : { [key]: value };
|
|
8078
|
+
}
|
|
8079
|
+
function pickStringValue(record3, key) {
|
|
8080
|
+
const value = record3[key];
|
|
8081
|
+
return typeof value === "string" ? value : undefined;
|
|
8082
|
+
}
|
|
8083
|
+
function pickNumber(record3, key) {
|
|
8084
|
+
const value = record3[key];
|
|
8085
|
+
return typeof value === "number" && Number.isFinite(value) ? { [key]: value } : {};
|
|
8086
|
+
}
|
|
8087
|
+
var REMOTE_SKILL_RUN_CONTRACT_VERSION = 1;
|
|
8088
|
+
|
|
8012
8089
|
// src/lib/blog-article.ts
|
|
8013
8090
|
var exports_blog_article = {};
|
|
8014
8091
|
__export(exports_blog_article, {
|
|
@@ -8223,16 +8300,194 @@ var init_remote_files = __esm(() => {
|
|
|
8223
8300
|
MAX_REMOTE_FILE_BYTES = 64 * 1024 * 1024;
|
|
8224
8301
|
});
|
|
8225
8302
|
|
|
8226
|
-
// src/lib/
|
|
8227
|
-
function
|
|
8228
|
-
|
|
8303
|
+
// src/lib/remote-workspace-selection.ts
|
|
8304
|
+
function workspaceExpectedUserId(value) {
|
|
8305
|
+
if (!uuid2(value))
|
|
8306
|
+
throw new WorkspaceContextInputError;
|
|
8307
|
+
return value;
|
|
8229
8308
|
}
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8309
|
+
function workspaceContext(value) {
|
|
8310
|
+
if (!record3(value) || Object.keys(value).sort().join(",") !== "membershipId,userId" || !uuid2(value.userId) || !uuid2(value.membershipId))
|
|
8311
|
+
throw new WorkspaceContextInputError;
|
|
8312
|
+
return { userId: value.userId, membershipId: value.membershipId };
|
|
8313
|
+
}
|
|
8314
|
+
function invalid() {
|
|
8315
|
+
throw new Error(invalidWorkspaceResult);
|
|
8316
|
+
}
|
|
8317
|
+
function organization(v) {
|
|
8318
|
+
if (!record3(v) || !uuid2(v.id) || !text(v.slug) || !text(v.name))
|
|
8319
|
+
return invalid();
|
|
8320
|
+
return { id: v.id, slug: v.slug, name: v.name };
|
|
8321
|
+
}
|
|
8322
|
+
function parseAccountWorkspaces(value) {
|
|
8323
|
+
if (!record3(value) || !Array.isArray(value.workspaces) || !value.workspaces.length || value.workspaces.length > 1000)
|
|
8324
|
+
return invalid();
|
|
8325
|
+
const workspaces = value.workspaces.map((v) => {
|
|
8326
|
+
if (!record3(v) || !uuid2(v.membershipId) || !role(v.role) || typeof v.current !== "boolean")
|
|
8327
|
+
return invalid();
|
|
8328
|
+
return { membershipId: v.membershipId, organization: organization(v.organization), role: v.role, current: v.current };
|
|
8329
|
+
});
|
|
8330
|
+
if (workspaces.filter((w) => w.current).length !== 1 || new Set(workspaces.map((w) => w.membershipId)).size !== workspaces.length || new Set(workspaces.map((w) => w.organization.id)).size !== workspaces.length)
|
|
8331
|
+
return invalid();
|
|
8332
|
+
return { workspaces };
|
|
8333
|
+
}
|
|
8334
|
+
function parseWorkspaceIdentity(value, expectedUserId) {
|
|
8335
|
+
if (!record3(value))
|
|
8336
|
+
return invalid();
|
|
8337
|
+
const user = value.user;
|
|
8338
|
+
if (!record3(user) || !uuid2(user.id) || !uuid2(user.membershipId) || !text(user.email, 320) || !(user.displayName === null || text(user.displayName)) || !role(user.role))
|
|
8339
|
+
return invalid();
|
|
8340
|
+
if (user.id !== expectedUserId)
|
|
8341
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8342
|
+
return { user: { id: user.id, membershipId: user.membershipId, email: user.email, displayName: user.displayName, role: user.role }, organization: organization(value.organization) };
|
|
8343
|
+
}
|
|
8344
|
+
function sessionToken(value) {
|
|
8345
|
+
if (typeof value !== "string" || !value || value.length > 8192 || /[^\x21-\x7e]/.test(value) || value.startsWith("sk_"))
|
|
8346
|
+
return invalid();
|
|
8347
|
+
return value;
|
|
8348
|
+
}
|
|
8349
|
+
function parseWorkspaceSession(value, expected) {
|
|
8350
|
+
const identity = parseWorkspaceIdentity(value, expected.userId);
|
|
8351
|
+
if (identity.user.membershipId !== expected.membershipId)
|
|
8352
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8353
|
+
return { token: sessionToken(value.token), ...identity };
|
|
8354
|
+
}
|
|
8355
|
+
function parseWorkspaceLogin(value, expectedUserId) {
|
|
8356
|
+
const user = record3(value) && value.user;
|
|
8357
|
+
if (!record3(value) || !record3(user) || !uuid2(user.id))
|
|
8358
|
+
return invalid();
|
|
8359
|
+
if (expectedUserId !== undefined && user.id !== expectedUserId)
|
|
8360
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8361
|
+
return { token: sessionToken(value.token), userId: user.id };
|
|
8362
|
+
}
|
|
8363
|
+
function workspaceSelectionFailure(value, status) {
|
|
8364
|
+
if (!record3(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceSelectionFailures, value.code))
|
|
8365
|
+
return null;
|
|
8366
|
+
const code = value.code;
|
|
8367
|
+
return workspaceSelectionFailures[code][0] === status ? code : null;
|
|
8368
|
+
}
|
|
8369
|
+
var record3 = (v) => !!v && typeof v === "object" && !Array.isArray(v), uuid2 = (v) => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(v), text = (v, max = 1024) => typeof v === "string" && !!v.trim() && v.length <= max && !/[\p{Cc}\p{Cs}\u2028\u2029]/u.test(v), role = (v) => typeof v === "string" && ["owner", "admin", "member", "viewer"].includes(v), invalidWorkspaceResult = "The server returned an invalid workspace selection result.", WorkspaceContextInputError, WorkspaceIdentityMismatchError, workspaceSelectionFailures;
|
|
8370
|
+
var init_remote_workspace_selection = __esm(() => {
|
|
8371
|
+
WorkspaceContextInputError = class WorkspaceContextInputError extends Error {
|
|
8372
|
+
constructor() {
|
|
8373
|
+
super("Provide the observed user ID and exact lowercase membership ID.");
|
|
8374
|
+
this.name = "WorkspaceContextInputError";
|
|
8375
|
+
}
|
|
8376
|
+
};
|
|
8377
|
+
WorkspaceIdentityMismatchError = class WorkspaceIdentityMismatchError extends Error {
|
|
8378
|
+
constructor() {
|
|
8379
|
+
super("The verified account does not match the requested workspace context.");
|
|
8380
|
+
this.name = "WorkspaceIdentityMismatchError";
|
|
8381
|
+
}
|
|
8382
|
+
};
|
|
8383
|
+
workspaceSelectionFailures = {
|
|
8384
|
+
INVALID_WORKSPACE_SELECTION: [400, "Provide only the exact membership ID from your workspace list."],
|
|
8385
|
+
SESSION_EXPIRED: [401, "Sign in again before selecting a workspace."],
|
|
8386
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
8387
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Interactive sign-in is required to select a workspace."],
|
|
8388
|
+
WORKSPACE_UNAVAILABLE: [404, "Workspace is unavailable. Refresh your workspace list."],
|
|
8389
|
+
WORKSPACE_BUSY: [503, "Workspace is busy. Refresh before retrying."]
|
|
8390
|
+
};
|
|
8391
|
+
});
|
|
8392
|
+
|
|
8393
|
+
// src/lib/remote-workspace.ts
|
|
8394
|
+
function workspaceMembersQuery(options = {}) {
|
|
8395
|
+
if (!record4(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))
|
|
8396
|
+
throw new Error("Use a roster limit from 1 to 100 and an unchanged continuation cursor.");
|
|
8397
|
+
const query = new URLSearchParams;
|
|
8398
|
+
if (options.limit !== undefined)
|
|
8399
|
+
query.set("limit", String(options.limit));
|
|
8400
|
+
if (options.cursor !== undefined)
|
|
8401
|
+
query.set("cursor", options.cursor);
|
|
8402
|
+
return query.size ? `?${query}` : "";
|
|
8403
|
+
}
|
|
8404
|
+
function timestamp(value) {
|
|
8405
|
+
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z$/.test(value))
|
|
8406
|
+
return false;
|
|
8407
|
+
const time3 = Date.parse(value);
|
|
8408
|
+
return Number.isFinite(time3) && new Date(time3).toISOString().slice(0, 23) === value.slice(0, 23);
|
|
8409
|
+
}
|
|
8410
|
+
function parseMember(row, fail) {
|
|
8411
|
+
if (!record4(row) || !uuid3(row.membershipId) || !uuid3(row.userId) || typeof row.email !== "string" || !row.email || !(row.displayName === null || typeof row.displayName === "string") || !isRole(row.role) || !timestamp(row.createdAt))
|
|
8412
|
+
return fail();
|
|
8413
|
+
return {
|
|
8414
|
+
membershipId: row.membershipId,
|
|
8415
|
+
userId: row.userId,
|
|
8416
|
+
email: row.email,
|
|
8417
|
+
displayName: row.displayName,
|
|
8418
|
+
role: row.role,
|
|
8419
|
+
createdAt: row.createdAt
|
|
8420
|
+
};
|
|
8421
|
+
}
|
|
8422
|
+
function mutationInput(membershipId, input, roleChange) {
|
|
8423
|
+
if (typeof membershipId !== "string" || !uuid3(membershipId) || membershipId !== membershipId.toLowerCase() || !record4(input) || Object.keys(input).sort().join(",") !== (roleChange ? "expectedRole,role" : "expectedRole"))
|
|
8424
|
+
throw new WorkspaceMemberInputError;
|
|
8425
|
+
const expectedRole = input.expectedRole, role2 = roleChange ? input.role : undefined;
|
|
8426
|
+
if (!isRole(expectedRole) || roleChange && !isRole(role2))
|
|
8427
|
+
throw new WorkspaceMemberInputError;
|
|
8428
|
+
return { membershipId, role: role2, expectedRole };
|
|
8429
|
+
}
|
|
8430
|
+
function workspaceMemberRoleInput(membershipId, input) {
|
|
8431
|
+
const value = mutationInput(membershipId, input, true);
|
|
8432
|
+
return { membershipId: value.membershipId, body: { role: value.role, expectedRole: value.expectedRole } };
|
|
8433
|
+
}
|
|
8434
|
+
function workspaceMemberRemovalInput(membershipId, input) {
|
|
8435
|
+
const value = mutationInput(membershipId, input, false);
|
|
8436
|
+
return { membershipId: value.membershipId, body: { expectedRole: value.expectedRole } };
|
|
8437
|
+
}
|
|
8438
|
+
function parseWorkspaceMemberRoleResult(value, membershipId, role2) {
|
|
8439
|
+
const fail = () => {
|
|
8440
|
+
throw new Error(invalidMemberResult);
|
|
8441
|
+
};
|
|
8442
|
+
if (!record4(value) || !uuid3(value.organizationId) || typeof value.changed !== "boolean")
|
|
8443
|
+
return fail();
|
|
8444
|
+
const member = parseMember(value.member, fail);
|
|
8445
|
+
if (member.membershipId !== membershipId || member.role !== role2)
|
|
8446
|
+
return fail();
|
|
8447
|
+
return { organizationId: value.organizationId, member, changed: value.changed };
|
|
8448
|
+
}
|
|
8449
|
+
function parseWorkspaceMemberRemovalResult(value, membershipId) {
|
|
8450
|
+
if (!record4(value) || !uuid3(value.organizationId) || value.membershipId !== membershipId || value.removed !== true || typeof value.alreadyRemoved !== "boolean")
|
|
8451
|
+
throw new Error(invalidMemberResult);
|
|
8452
|
+
return { organizationId: value.organizationId, membershipId, removed: true, alreadyRemoved: value.alreadyRemoved };
|
|
8453
|
+
}
|
|
8454
|
+
function workspaceMemberFailure(value, status) {
|
|
8455
|
+
if (!record4(value) || typeof value.code !== "string" || !Object.hasOwn(workspaceMemberFailures, value.code))
|
|
8456
|
+
return null;
|
|
8457
|
+
const code = value.code;
|
|
8458
|
+
return workspaceMemberFailures[code][0] === status ? code : null;
|
|
8459
|
+
}
|
|
8460
|
+
function parseWorkspaceMembersPage(value) {
|
|
8461
|
+
const fail = () => {
|
|
8462
|
+
throw new Error("The server returned an invalid workspace roster.");
|
|
8463
|
+
};
|
|
8464
|
+
if (!record4(value) || !uuid3(value.organizationId) || !Array.isArray(value.members) || value.members.length > 100 || !(value.nextCursor === null || cursor(value.nextCursor)) || !value.members.length && value.nextCursor !== null)
|
|
8465
|
+
return fail();
|
|
8466
|
+
const members = value.members.map((row) => parseMember(row, fail));
|
|
8467
|
+
if (new Set(members.map((row) => row.membershipId)).size !== members.length)
|
|
8468
|
+
return fail();
|
|
8469
|
+
return { organizationId: value.organizationId, members, nextCursor: value.nextCursor };
|
|
8470
|
+
}
|
|
8471
|
+
var record4 = (value) => !!value && typeof value === "object" && !Array.isArray(value), cursor = (value) => typeof value === "string" && /^[A-Za-z0-9_-]{1,512}$/.test(value), uuid3 = (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;
|
|
8472
|
+
var init_remote_workspace = __esm(() => {
|
|
8473
|
+
WorkspaceMemberInputError = class WorkspaceMemberInputError extends Error {
|
|
8474
|
+
constructor() {
|
|
8475
|
+
super("Use an unchanged lowercase membership ID and the exact role and expected-role parameters from the roster.");
|
|
8476
|
+
this.name = "WorkspaceMemberInputError";
|
|
8477
|
+
}
|
|
8478
|
+
};
|
|
8479
|
+
workspaceMemberFailures = {
|
|
8480
|
+
INVALID_REQUEST: [400, "Provide the exact membership role parameters."],
|
|
8481
|
+
ACCOUNT_UNAVAILABLE: [403, "Account is unavailable."],
|
|
8482
|
+
INTERACTIVE_SESSION_REQUIRED: [403, "Fresh interactive sign-in is required."],
|
|
8483
|
+
WORKSPACE_ADMIN_REQUIRED: [403, "A current workspace owner or admin is required."],
|
|
8484
|
+
MEMBERSHIP_ACTION_FORBIDDEN: [403, "Your current workspace role cannot perform this membership action."],
|
|
8485
|
+
MEMBERSHIP_NOT_FOUND: [404, "Membership was not found in the current workspace."],
|
|
8486
|
+
SELF_REMOVAL_UNAVAILABLE: [409, "Leaving your own workspace is not available through member removal."],
|
|
8487
|
+
MEMBERSHIP_ROLE_CHANGED: [409, "The member role changed. Refresh the roster before another action."],
|
|
8488
|
+
LAST_OWNER_REQUIRED: [409, "The workspace must retain at least one active owner."],
|
|
8489
|
+
MEMBERSHIP_BUSY: [503, "Membership is busy. Refresh the roster before another action."]
|
|
8490
|
+
};
|
|
8236
8491
|
});
|
|
8237
8492
|
|
|
8238
8493
|
// src/lib/remote-account.ts
|
|
@@ -8314,14 +8569,48 @@ var init_remote_account = __esm(() => {
|
|
|
8314
8569
|
};
|
|
8315
8570
|
});
|
|
8316
8571
|
|
|
8572
|
+
// src/lib/remote-profile.ts
|
|
8573
|
+
function customerNamePatch(input, field) {
|
|
8574
|
+
if (!isRecord4(input) || Object.keys(input).length !== 1 || !Object.hasOwn(input, field))
|
|
8575
|
+
throw new Error("Provide only the requested name field.");
|
|
8576
|
+
const value = input[field];
|
|
8577
|
+
if (typeof value !== "string" || /[\p{Cc}\p{Cs}\u2028\u2029]/u.test(value) || !value.trim() || [...value.trim()].length > 100) {
|
|
8578
|
+
throw new Error("Use a name of 1\u2013100 characters without control characters or newlines.");
|
|
8579
|
+
}
|
|
8580
|
+
return { [field]: value.trim() };
|
|
8581
|
+
}
|
|
8582
|
+
function isRecord4(value) {
|
|
8583
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8584
|
+
}
|
|
8585
|
+
function string4(value) {
|
|
8586
|
+
return typeof value === "string" && value.length > 0;
|
|
8587
|
+
}
|
|
8588
|
+
function parseUpdatedProfile(value) {
|
|
8589
|
+
const user = isRecord4(value) && value.user;
|
|
8590
|
+
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)) {
|
|
8591
|
+
throw new Error("The server returned an invalid account profile.");
|
|
8592
|
+
}
|
|
8593
|
+
return { user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role } };
|
|
8594
|
+
}
|
|
8595
|
+
function parseUpdatedWorkspace(value) {
|
|
8596
|
+
const organization2 = isRecord4(value) && value.organization;
|
|
8597
|
+
if (!isRecord4(organization2) || !string4(organization2.id) || !string4(organization2.slug) || !string4(organization2.name)) {
|
|
8598
|
+
throw new Error("The server returned an invalid workspace.");
|
|
8599
|
+
}
|
|
8600
|
+
return { organization: { id: organization2.id, slug: organization2.slug, name: organization2.name } };
|
|
8601
|
+
}
|
|
8602
|
+
|
|
8317
8603
|
// src/lib/remote-client.ts
|
|
8318
8604
|
var exports_remote_client = {};
|
|
8319
8605
|
__export(exports_remote_client, {
|
|
8320
8606
|
createRemoteSkillsClientReadOnly: () => createRemoteSkillsClientReadOnly,
|
|
8321
8607
|
createRemoteSkillsClient: () => createRemoteSkillsClient,
|
|
8608
|
+
RemoteWorkspaceSelectionError: () => RemoteWorkspaceSelectionError,
|
|
8609
|
+
RemoteWorkspaceMemberError: () => RemoteWorkspaceMemberError,
|
|
8322
8610
|
RemoteSkillsClient: () => RemoteSkillsClient,
|
|
8323
8611
|
RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
|
|
8324
|
-
RemoteRequestError: () => RemoteRequestError
|
|
8612
|
+
RemoteRequestError: () => RemoteRequestError,
|
|
8613
|
+
RemoteCapabilityUnavailableError: () => RemoteCapabilityUnavailableError
|
|
8325
8614
|
});
|
|
8326
8615
|
|
|
8327
8616
|
class RemoteSkillsClient {
|
|
@@ -8336,6 +8625,7 @@ class RemoteSkillsClient {
|
|
|
8336
8625
|
return fetch(`${this.apiUrl}${path}`, {
|
|
8337
8626
|
...options,
|
|
8338
8627
|
redirect: "error",
|
|
8628
|
+
credentials: "omit",
|
|
8339
8629
|
signal: options?.signal ?? AbortSignal.timeout(15000),
|
|
8340
8630
|
headers: {
|
|
8341
8631
|
Authorization: `Bearer ${this.apiKey}`,
|
|
@@ -8351,9 +8641,14 @@ class RemoteSkillsClient {
|
|
|
8351
8641
|
if (response.status === 404 && opts.domainNotFoundCodes?.length && await responseBodyCarriesCode(response, opts.domainNotFoundCodes)) {
|
|
8352
8642
|
return response;
|
|
8353
8643
|
}
|
|
8644
|
+
response.body?.cancel().catch(() => {});
|
|
8354
8645
|
throw new RemoteRouteUnsupportedError(routePath, response.status, this.apiUrl);
|
|
8355
8646
|
}
|
|
8356
8647
|
if (!response.ok) {
|
|
8648
|
+
if (path === "/api/v1/billing/checkout" && options?.method === "POST" && response.status === 503 && await responseBodyCarriesCode(response, ["SUBSCRIPTION_CHECKOUT_UNAVAILABLE"])) {
|
|
8649
|
+
throw new RemoteCapabilityUnavailableError;
|
|
8650
|
+
}
|
|
8651
|
+
response.body?.cancel().catch(() => {});
|
|
8357
8652
|
throw new RemoteRequestError(routePath, response.status, response.statusText);
|
|
8358
8653
|
}
|
|
8359
8654
|
return response;
|
|
@@ -8437,6 +8732,117 @@ class RemoteSkillsClient {
|
|
|
8437
8732
|
async getIdentity() {
|
|
8438
8733
|
return (await this.requestNewRoute("/api/auth/whoami")).json();
|
|
8439
8734
|
}
|
|
8735
|
+
async listAccountWorkspaces(expectedUserId) {
|
|
8736
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
8737
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
8738
|
+
let identity;
|
|
8739
|
+
if (expected !== undefined) {
|
|
8740
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
8741
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
8742
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces", "INTERACTIVE_SESSION_REQUIRED");
|
|
8743
|
+
identity = parseWorkspaceIdentity(value, expected);
|
|
8744
|
+
}
|
|
8745
|
+
const result = parseAccountWorkspaces(await connection.requestWorkspaceSelection("/api/v1/account/workspaces"));
|
|
8746
|
+
const current = result.workspaces.find((workspace) => workspace.current);
|
|
8747
|
+
if (identity && (current.membershipId !== identity.user.membershipId || current.organization.id !== identity.organization.id))
|
|
8748
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8749
|
+
return result;
|
|
8750
|
+
}
|
|
8751
|
+
async switchWorkspace(context) {
|
|
8752
|
+
const target = workspaceContext(context);
|
|
8753
|
+
const connection = new RemoteSkillsClient(this.apiKey, this.apiUrl);
|
|
8754
|
+
const value = await connection.requestWorkspaceSelection("/api/auth/whoami");
|
|
8755
|
+
if (!value || typeof value !== "object" || value.authMethod !== "jwt")
|
|
8756
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
8757
|
+
parseWorkspaceIdentity(value, target.userId);
|
|
8758
|
+
const selected = parseWorkspaceSession(await connection.requestWorkspaceSelection("/api/v1/account/workspaces/switch", {
|
|
8759
|
+
method: "POST",
|
|
8760
|
+
body: JSON.stringify({ membershipId: target.membershipId })
|
|
8761
|
+
}), target);
|
|
8762
|
+
const verified = await new RemoteSkillsClient(selected.token, connection.apiUrl).requestWorkspaceSelection("/api/auth/whoami");
|
|
8763
|
+
if (!verified || typeof verified !== "object" || verified.authMethod !== "jwt")
|
|
8764
|
+
throw new RemoteWorkspaceSelectionError("/api/v1/account/workspaces/switch", "INTERACTIVE_SESSION_REQUIRED");
|
|
8765
|
+
const identity = parseWorkspaceIdentity(verified, target.userId);
|
|
8766
|
+
if (identity.user.membershipId !== target.membershipId || identity.organization.id !== selected.organization.id)
|
|
8767
|
+
throw new WorkspaceIdentityMismatchError;
|
|
8768
|
+
return { token: selected.token, ...identity };
|
|
8769
|
+
}
|
|
8770
|
+
async requestWorkspaceSelection(path, options) {
|
|
8771
|
+
let response;
|
|
8772
|
+
try {
|
|
8773
|
+
response = await this.request(path, { ...options, credentials: "omit" });
|
|
8774
|
+
} catch {
|
|
8775
|
+
throw new Error("Unable to reach the Skills workspace API.");
|
|
8776
|
+
}
|
|
8777
|
+
let value;
|
|
8778
|
+
try {
|
|
8779
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 1024 * 1024 : 4096)));
|
|
8780
|
+
} catch {
|
|
8781
|
+
if (response.ok)
|
|
8782
|
+
throw new Error(invalidWorkspaceResult);
|
|
8783
|
+
}
|
|
8784
|
+
if (!response.ok) {
|
|
8785
|
+
const code = workspaceSelectionFailure(value, response.status);
|
|
8786
|
+
if (code)
|
|
8787
|
+
throw new RemoteWorkspaceSelectionError(path, code);
|
|
8788
|
+
if (response.status === 404 || response.status === 405)
|
|
8789
|
+
throw new RemoteRouteUnsupportedError(path, response.status, this.apiUrl);
|
|
8790
|
+
throw new RemoteRequestError(path, response.status);
|
|
8791
|
+
}
|
|
8792
|
+
return value;
|
|
8793
|
+
}
|
|
8794
|
+
async updateProfile(input) {
|
|
8795
|
+
const body = customerNamePatch(input, "displayName");
|
|
8796
|
+
return parseUpdatedProfile(await (await this.requestNewRoute("/api/v1/account/profile", { method: "PATCH", body: JSON.stringify(body) })).json());
|
|
8797
|
+
}
|
|
8798
|
+
async updateCurrentWorkspace(input) {
|
|
8799
|
+
const body = customerNamePatch(input, "name");
|
|
8800
|
+
return parseUpdatedWorkspace(await (await this.requestNewRoute("/api/v1/workspaces/current", { method: "PATCH", body: JSON.stringify(body) })).json());
|
|
8801
|
+
}
|
|
8802
|
+
async listWorkspaceMembers(options = {}) {
|
|
8803
|
+
const query = workspaceMembersQuery(options);
|
|
8804
|
+
const requestedCursor = options.cursor;
|
|
8805
|
+
const response = await this.requestNewRoute(`/api/v1/workspace/members${query}`);
|
|
8806
|
+
let value;
|
|
8807
|
+
try {
|
|
8808
|
+
value = await response.json();
|
|
8809
|
+
} catch {
|
|
8810
|
+
throw new Error("The server returned an invalid workspace roster.");
|
|
8811
|
+
}
|
|
8812
|
+
const page = parseWorkspaceMembersPage(value);
|
|
8813
|
+
if (requestedCursor !== undefined && page.nextCursor === requestedCursor)
|
|
8814
|
+
throw new Error("The server returned an invalid workspace roster.");
|
|
8815
|
+
return page;
|
|
8816
|
+
}
|
|
8817
|
+
async setWorkspaceMemberRole(membershipId, input) {
|
|
8818
|
+
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
8819
|
+
const value = await this.requestWorkspaceMember(captured.membershipId, "PATCH", captured.body);
|
|
8820
|
+
return parseWorkspaceMemberRoleResult(value, captured.membershipId, captured.body.role);
|
|
8821
|
+
}
|
|
8822
|
+
async removeWorkspaceMember(membershipId, input) {
|
|
8823
|
+
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
8824
|
+
return parseWorkspaceMemberRemovalResult(await this.requestWorkspaceMember(captured.membershipId, "DELETE", captured.body), captured.membershipId);
|
|
8825
|
+
}
|
|
8826
|
+
async requestWorkspaceMember(membershipId, method, body) {
|
|
8827
|
+
const path = `/api/v1/workspace/members/${membershipId}`;
|
|
8828
|
+
const response = await this.request(path, { method, body: JSON.stringify(body) });
|
|
8829
|
+
let value;
|
|
8830
|
+
try {
|
|
8831
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, response.ok ? 64 * 1024 : 4096)));
|
|
8832
|
+
} catch {
|
|
8833
|
+
if (response.ok)
|
|
8834
|
+
throw new Error(invalidMemberResult);
|
|
8835
|
+
}
|
|
8836
|
+
if (!response.ok) {
|
|
8837
|
+
const code = workspaceMemberFailure(value, response.status);
|
|
8838
|
+
if (code)
|
|
8839
|
+
throw new RemoteWorkspaceMemberError(path, code);
|
|
8840
|
+
if (response.status === 404 || response.status === 405)
|
|
8841
|
+
throw new RemoteRouteUnsupportedError(path, response.status, this.apiUrl);
|
|
8842
|
+
throw new RemoteRequestError(path, response.status);
|
|
8843
|
+
}
|
|
8844
|
+
return value;
|
|
8845
|
+
}
|
|
8440
8846
|
async listApiKeys() {
|
|
8441
8847
|
return this.arrayResponse("/api/auth/keys");
|
|
8442
8848
|
}
|
|
@@ -8628,8 +9034,10 @@ class RemoteSkillsClient {
|
|
|
8628
9034
|
return [];
|
|
8629
9035
|
if (!response.ok)
|
|
8630
9036
|
throw new Error(`versions request failed: ${response.status}`);
|
|
8631
|
-
const body = await response
|
|
8632
|
-
|
|
9037
|
+
const body = await readSkillVersionPayload(response);
|
|
9038
|
+
if (!isVersionRecord(body) || !Array.isArray(body.versions) || body.slug !== undefined && body.slug !== slug)
|
|
9039
|
+
throw new Error(INVALID_SKILL_VERSION_RESPONSE);
|
|
9040
|
+
return body.versions.map((entry) => normalizeSkillVersion(entry, slug));
|
|
8633
9041
|
}
|
|
8634
9042
|
async getSkillVersion(slug, version2) {
|
|
8635
9043
|
const response = await this.requestNewRoute(`/api/v1/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version2)}`, undefined, { domainNotFoundCodes: ["SKILL_NOT_FOUND", "SKILL_VERSION_NOT_FOUND"] });
|
|
@@ -8637,7 +9045,7 @@ class RemoteSkillsClient {
|
|
|
8637
9045
|
return null;
|
|
8638
9046
|
if (!response.ok)
|
|
8639
9047
|
throw new Error(`version request failed: ${response.status}`);
|
|
8640
|
-
return await response
|
|
9048
|
+
return normalizeSkillVersion(await readSkillVersionPayload(response), slug, version2);
|
|
8641
9049
|
}
|
|
8642
9050
|
async listPins() {
|
|
8643
9051
|
const response = await this.requestNewRoute("/api/v1/pins");
|
|
@@ -8685,31 +9093,47 @@ class RemoteSkillsClient {
|
|
|
8685
9093
|
return normalizeUpdatedSincePage(await response.json());
|
|
8686
9094
|
}
|
|
8687
9095
|
}
|
|
8688
|
-
function requireOptionalString(
|
|
8689
|
-
if (
|
|
9096
|
+
function requireOptionalString(record5, field) {
|
|
9097
|
+
if (record5[field] === undefined)
|
|
8690
9098
|
return;
|
|
8691
|
-
if (typeof
|
|
9099
|
+
if (typeof record5[field] !== "string") {
|
|
8692
9100
|
throw new Error(`Remote payload did not match the expected contract (${field} must be a string when present)`);
|
|
8693
9101
|
}
|
|
8694
|
-
return
|
|
9102
|
+
return record5[field];
|
|
9103
|
+
}
|
|
9104
|
+
function isVersionRecord(value) {
|
|
9105
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9106
|
+
}
|
|
9107
|
+
async function readSkillVersionPayload(response) {
|
|
9108
|
+
try {
|
|
9109
|
+
return await response.json();
|
|
9110
|
+
} catch {
|
|
9111
|
+
throw new Error(INVALID_SKILL_VERSION_RESPONSE);
|
|
9112
|
+
}
|
|
9113
|
+
}
|
|
9114
|
+
function normalizeSkillVersion(entry, slug, version2) {
|
|
9115
|
+
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)) {
|
|
9116
|
+
throw new Error(INVALID_SKILL_VERSION_RESPONSE);
|
|
9117
|
+
}
|
|
9118
|
+
return entry;
|
|
8695
9119
|
}
|
|
8696
9120
|
function normalizePin(entry) {
|
|
8697
9121
|
if (!entry || typeof entry !== "object") {
|
|
8698
9122
|
throw new Error("Remote pin payload did not match the expected contract (expected an object)");
|
|
8699
9123
|
}
|
|
8700
|
-
const
|
|
8701
|
-
const slug = typeof
|
|
9124
|
+
const record5 = entry;
|
|
9125
|
+
const slug = typeof record5.slug === "string" && record5.slug.trim() ? record5.slug.trim() : undefined;
|
|
8702
9126
|
if (!slug) {
|
|
8703
9127
|
throw new Error("Remote pin payload did not match the expected contract (missing slug)");
|
|
8704
9128
|
}
|
|
8705
9129
|
let metadata;
|
|
8706
|
-
if (
|
|
8707
|
-
if (!
|
|
9130
|
+
if (record5.metadata !== undefined) {
|
|
9131
|
+
if (!record5.metadata || typeof record5.metadata !== "object" || Array.isArray(record5.metadata)) {
|
|
8708
9132
|
throw new Error("Remote pin payload did not match the expected contract (metadata must be a JSON object when present)");
|
|
8709
9133
|
}
|
|
8710
|
-
metadata =
|
|
9134
|
+
metadata = record5.metadata;
|
|
8711
9135
|
}
|
|
8712
|
-
const pinnedAt = requireOptionalString(
|
|
9136
|
+
const pinnedAt = requireOptionalString(record5, "pinnedAt");
|
|
8713
9137
|
return {
|
|
8714
9138
|
slug,
|
|
8715
9139
|
...pinnedAt !== undefined ? { pinnedAt } : {},
|
|
@@ -8726,16 +9150,16 @@ function normalizeSkillSummary(entry) {
|
|
|
8726
9150
|
if (!entry || typeof entry !== "object") {
|
|
8727
9151
|
throw new Error("Remote skill payload did not match the expected contract (expected an object)");
|
|
8728
9152
|
}
|
|
8729
|
-
const
|
|
8730
|
-
const slug = typeof
|
|
9153
|
+
const record5 = entry;
|
|
9154
|
+
const slug = typeof record5.slug === "string" && record5.slug.trim() ? record5.slug.trim() : undefined;
|
|
8731
9155
|
if (!slug) {
|
|
8732
9156
|
throw new Error("Remote skill payload did not match the expected contract (missing slug)");
|
|
8733
9157
|
}
|
|
8734
9158
|
return {
|
|
8735
9159
|
slug,
|
|
8736
|
-
...requireOptionalString(
|
|
8737
|
-
...requireOptionalString(
|
|
8738
|
-
...requireOptionalString(
|
|
9160
|
+
...requireOptionalString(record5, "name") !== undefined ? { name: requireOptionalString(record5, "name") } : {},
|
|
9161
|
+
...requireOptionalString(record5, "version") !== undefined ? { version: requireOptionalString(record5, "version") } : {},
|
|
9162
|
+
...requireOptionalString(record5, "updatedAt") !== undefined ? { updatedAt: requireOptionalString(record5, "updatedAt") } : {}
|
|
8739
9163
|
};
|
|
8740
9164
|
}
|
|
8741
9165
|
function normalizeSkillSummaryList(payload) {
|
|
@@ -8745,26 +9169,55 @@ function normalizeSkillSummaryList(payload) {
|
|
|
8745
9169
|
return payload.map(normalizeSkillSummary);
|
|
8746
9170
|
}
|
|
8747
9171
|
async function responseBodyCarriesCode(response, codes) {
|
|
9172
|
+
const reader = response.body?.getReader();
|
|
9173
|
+
if (!reader)
|
|
9174
|
+
return false;
|
|
9175
|
+
const maximum = 8 * 1024;
|
|
9176
|
+
let deadline;
|
|
9177
|
+
const expired = new Promise((_, reject) => {
|
|
9178
|
+
deadline = setTimeout(() => reject(new Error("Error response read deadline exceeded")), 1000);
|
|
9179
|
+
});
|
|
8748
9180
|
try {
|
|
8749
|
-
const
|
|
8750
|
-
|
|
9181
|
+
const chunks = [];
|
|
9182
|
+
let size = 0;
|
|
9183
|
+
while (true) {
|
|
9184
|
+
const next = await Promise.race([reader.read(), expired]);
|
|
9185
|
+
if (next.done)
|
|
9186
|
+
break;
|
|
9187
|
+
size += next.value.byteLength;
|
|
9188
|
+
if (size > maximum)
|
|
9189
|
+
return false;
|
|
9190
|
+
chunks.push(next.value);
|
|
9191
|
+
}
|
|
9192
|
+
const bytes = new Uint8Array(size);
|
|
9193
|
+
let offset = 0;
|
|
9194
|
+
for (const chunk of chunks) {
|
|
9195
|
+
bytes.set(chunk, offset);
|
|
9196
|
+
offset += chunk.byteLength;
|
|
9197
|
+
}
|
|
9198
|
+
const payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
9199
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload) || !Object.hasOwn(payload, "code"))
|
|
8751
9200
|
return false;
|
|
8752
9201
|
const code = payload.code;
|
|
8753
9202
|
return typeof code === "string" && codes.includes(code);
|
|
8754
9203
|
} catch {
|
|
8755
9204
|
return false;
|
|
9205
|
+
} finally {
|
|
9206
|
+
clearTimeout(deadline);
|
|
9207
|
+
reader.cancel().catch(() => {});
|
|
9208
|
+
reader.releaseLock();
|
|
8756
9209
|
}
|
|
8757
9210
|
}
|
|
8758
9211
|
function normalizeUpdatedSincePage(payload) {
|
|
8759
9212
|
if (!payload || typeof payload !== "object") {
|
|
8760
9213
|
throw new Error("Updated-since payload did not match the expected contract (expected an object)");
|
|
8761
9214
|
}
|
|
8762
|
-
const
|
|
8763
|
-
if (!Array.isArray(
|
|
9215
|
+
const record5 = payload;
|
|
9216
|
+
if (!Array.isArray(record5.skills)) {
|
|
8764
9217
|
throw new Error("Updated-since payload did not match the expected contract (missing skills array)");
|
|
8765
9218
|
}
|
|
8766
|
-
const skills =
|
|
8767
|
-
const nextCursor =
|
|
9219
|
+
const skills = record5.skills.map(normalizeSkillSummary);
|
|
9220
|
+
const nextCursor = record5.nextCursor === undefined || record5.nextCursor === null ? null : record5.nextCursor;
|
|
8768
9221
|
if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
8769
9222
|
throw new Error("Updated-since payload did not match the expected contract (nextCursor must be a string or absent)");
|
|
8770
9223
|
}
|
|
@@ -8777,8 +9230,11 @@ async function createRemoteSkillsClient(env = process.env) {
|
|
|
8777
9230
|
function createRemoteSkillsClientReadOnly(env = process.env) {
|
|
8778
9231
|
return createRemoteSkillsClient(env);
|
|
8779
9232
|
}
|
|
8780
|
-
var RemoteRouteUnsupportedError, RemoteRequestError;
|
|
9233
|
+
var RemoteRouteUnsupportedError, RemoteRequestError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
8781
9234
|
var init_remote_client = __esm(() => {
|
|
9235
|
+
init_remote_workspace_selection();
|
|
9236
|
+
init_remote_workspace();
|
|
9237
|
+
init_remote_workspace();
|
|
8782
9238
|
init_auth_store();
|
|
8783
9239
|
init_fleet_credentials();
|
|
8784
9240
|
init_remote_account();
|
|
@@ -8798,13 +9254,39 @@ var init_remote_client = __esm(() => {
|
|
|
8798
9254
|
RemoteRequestError = class RemoteRequestError extends Error {
|
|
8799
9255
|
path;
|
|
8800
9256
|
status;
|
|
8801
|
-
constructor(path, status,
|
|
8802
|
-
super(`Remote request to ${path} failed: HTTP ${status}
|
|
9257
|
+
constructor(path, status, _statusText) {
|
|
9258
|
+
super(`Remote request to ${path} failed: HTTP ${status}`);
|
|
8803
9259
|
this.path = path;
|
|
8804
9260
|
this.status = status;
|
|
8805
9261
|
this.name = "RemoteRequestError";
|
|
8806
9262
|
}
|
|
8807
9263
|
};
|
|
9264
|
+
RemoteWorkspaceMemberError = class RemoteWorkspaceMemberError extends RemoteRequestError {
|
|
9265
|
+
code;
|
|
9266
|
+
constructor(path, code) {
|
|
9267
|
+
super(path, workspaceMemberFailures[code][0]);
|
|
9268
|
+
this.code = code;
|
|
9269
|
+
this.name = "RemoteWorkspaceMemberError";
|
|
9270
|
+
this.message = workspaceMemberFailures[code][1];
|
|
9271
|
+
}
|
|
9272
|
+
};
|
|
9273
|
+
RemoteWorkspaceSelectionError = class RemoteWorkspaceSelectionError extends RemoteRequestError {
|
|
9274
|
+
code;
|
|
9275
|
+
constructor(path, code) {
|
|
9276
|
+
super(path, workspaceSelectionFailures[code][0]);
|
|
9277
|
+
this.code = code;
|
|
9278
|
+
this.name = "RemoteWorkspaceSelectionError";
|
|
9279
|
+
this.message = workspaceSelectionFailures[code][1];
|
|
9280
|
+
}
|
|
9281
|
+
};
|
|
9282
|
+
RemoteCapabilityUnavailableError = class RemoteCapabilityUnavailableError extends RemoteRequestError {
|
|
9283
|
+
code = "SUBSCRIPTION_CHECKOUT_UNAVAILABLE";
|
|
9284
|
+
constructor() {
|
|
9285
|
+
super("/api/v1/billing/checkout", 503);
|
|
9286
|
+
this.name = "RemoteCapabilityUnavailableError";
|
|
9287
|
+
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.";
|
|
9288
|
+
}
|
|
9289
|
+
};
|
|
8808
9290
|
});
|
|
8809
9291
|
|
|
8810
9292
|
// ../../node_modules/.bun/content-type@1.0.5/node_modules/content-type/index.js
|
|
@@ -8831,7 +9313,7 @@ var require_content_type = __commonJS((exports) => {
|
|
|
8831
9313
|
if (!type || !TYPE_REGEXP.test(type)) {
|
|
8832
9314
|
throw new TypeError("invalid type");
|
|
8833
9315
|
}
|
|
8834
|
-
var
|
|
9316
|
+
var string5 = type;
|
|
8835
9317
|
if (parameters && typeof parameters === "object") {
|
|
8836
9318
|
var param;
|
|
8837
9319
|
var params = Object.keys(parameters).sort();
|
|
@@ -8840,16 +9322,16 @@ var require_content_type = __commonJS((exports) => {
|
|
|
8840
9322
|
if (!TOKEN_REGEXP.test(param)) {
|
|
8841
9323
|
throw new TypeError("invalid parameter name");
|
|
8842
9324
|
}
|
|
8843
|
-
|
|
9325
|
+
string5 += "; " + param + "=" + qstring(parameters[param]);
|
|
8844
9326
|
}
|
|
8845
9327
|
}
|
|
8846
|
-
return
|
|
9328
|
+
return string5;
|
|
8847
9329
|
}
|
|
8848
|
-
function parse6(
|
|
8849
|
-
if (!
|
|
9330
|
+
function parse6(string5) {
|
|
9331
|
+
if (!string5) {
|
|
8850
9332
|
throw new TypeError("argument string is required");
|
|
8851
9333
|
}
|
|
8852
|
-
var header = typeof
|
|
9334
|
+
var header = typeof string5 === "object" ? getcontenttype(string5) : string5;
|
|
8853
9335
|
if (typeof header !== "string") {
|
|
8854
9336
|
throw new TypeError("argument string is required to be a string");
|
|
8855
9337
|
}
|
|
@@ -14323,7 +14805,7 @@ class StdioServerTransport {
|
|
|
14323
14805
|
// package.json
|
|
14324
14806
|
var package_default = {
|
|
14325
14807
|
name: "@hasna/skills",
|
|
14326
|
-
version: "0.
|
|
14808
|
+
version: "0.5.1",
|
|
14327
14809
|
description: "Skills library for AI coding agents",
|
|
14328
14810
|
type: "module",
|
|
14329
14811
|
bin: {
|
|
@@ -14355,6 +14837,7 @@ var package_default = {
|
|
|
14355
14837
|
files: [
|
|
14356
14838
|
"dist/",
|
|
14357
14839
|
"!dist/**/*.test.d.ts",
|
|
14840
|
+
"!dist/**/*.fixture.d.ts",
|
|
14358
14841
|
"!dist/test-preload.d.ts",
|
|
14359
14842
|
"!dist/platform",
|
|
14360
14843
|
"bin/",
|
|
@@ -14380,8 +14863,9 @@ var package_default = {
|
|
|
14380
14863
|
migrate: "bun run ./src/server/migrate.ts",
|
|
14381
14864
|
typecheck: "tsc --noEmit",
|
|
14382
14865
|
"verify:release": "bun run scripts/release-guard.ts",
|
|
14866
|
+
"verify:consumer-types": "bun run scripts/consumer-types.ts",
|
|
14383
14867
|
prepare: "bun run build:js",
|
|
14384
|
-
prepack: "bun run build && bun run verify:release",
|
|
14868
|
+
prepack: "bun run build && bun run verify:release && bun run verify:consumer-types",
|
|
14385
14869
|
prepublishOnly: "bun run typecheck && bun run test"
|
|
14386
14870
|
},
|
|
14387
14871
|
keywords: [
|
|
@@ -14403,6 +14887,7 @@ var package_default = {
|
|
|
14403
14887
|
author: "Hasna",
|
|
14404
14888
|
license: "Apache-2.0",
|
|
14405
14889
|
devDependencies: {
|
|
14890
|
+
"@hasna/contracts": "1.0.2",
|
|
14406
14891
|
"@types/bun": "1.3.14",
|
|
14407
14892
|
"@types/node": "25.2.3",
|
|
14408
14893
|
"@types/react": "^18.2.0",
|
|
@@ -14414,8 +14899,7 @@ var package_default = {
|
|
|
14414
14899
|
dependencies: {
|
|
14415
14900
|
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
14416
14901
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
14417
|
-
"@hasna/
|
|
14418
|
-
"@hasna/events": "0.1.16",
|
|
14902
|
+
"@hasna/events": "0.1.18",
|
|
14419
14903
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
14420
14904
|
chalk: "^5.3.0",
|
|
14421
14905
|
commander: "^12.1.0",
|
|
@@ -22331,12 +22815,7 @@ import { homedir } from "os";
|
|
|
22331
22815
|
import { join, resolve } from "path";
|
|
22332
22816
|
import { homedir as pathsResolverHomedir } from "os";
|
|
22333
22817
|
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
|
-
};
|
|
22818
|
+
var PATHS_RESOLVER_DATA_ENV = "HASNA_DATA_HOME";
|
|
22340
22819
|
var PATHS_RESOLVER_APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
22341
22820
|
function pathsResolverAssertApp(app) {
|
|
22342
22821
|
if (typeof app !== "string" || app.length === 0) {
|
|
@@ -22346,48 +22825,19 @@ function pathsResolverAssertApp(app) {
|
|
|
22346
22825
|
throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
|
|
22347
22826
|
}
|
|
22348
22827
|
}
|
|
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);
|
|
22828
|
+
function pathsResolverDataBaseDir(options) {
|
|
22356
22829
|
const env = options.env ?? process.env;
|
|
22357
|
-
const override = env[
|
|
22830
|
+
const override = env[PATHS_RESOLVER_DATA_ENV];
|
|
22358
22831
|
if (typeof override === "string" && override.length > 0)
|
|
22359
22832
|
return override;
|
|
22360
22833
|
const home = options.home ?? pathsResolverHomedir();
|
|
22361
22834
|
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);
|
|
22835
|
+
return platform === "darwin" ? pathsResolverJoin(home, "Library", "Application Support", "Hasna") : pathsResolverJoin(home, ".local", "share", "hasna");
|
|
22388
22836
|
}
|
|
22389
22837
|
function dataDir(options) {
|
|
22390
|
-
|
|
22838
|
+
pathsResolverAssertApp(options.app);
|
|
22839
|
+
const appSegment = options.internal === true ? pathsResolverJoin("internal", options.app) : options.app;
|
|
22840
|
+
return pathsResolverJoin(pathsResolverDataBaseDir(options), appSegment);
|
|
22391
22841
|
}
|
|
22392
22842
|
var DATA_DIR_ENV = "HASNA_SKILLS_DIR";
|
|
22393
22843
|
var HASNA_SKILLS_HOME_ENV = "HASNA_SKILLS_HOME";
|
|
@@ -23967,14 +24417,27 @@ function normalizePortableSkillName(name) {
|
|
|
23967
24417
|
}
|
|
23968
24418
|
return normalized;
|
|
23969
24419
|
}
|
|
24420
|
+
function normalizeNewPortableSkillName(name) {
|
|
24421
|
+
normalizePortableSkillName(name);
|
|
24422
|
+
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, "");
|
|
24423
|
+
if (!normalized)
|
|
24424
|
+
throw new Error(`Invalid skill name '${name}'. Include letters or numbers.`);
|
|
24425
|
+
return normalized;
|
|
24426
|
+
}
|
|
23970
24427
|
function readPortableSkillManifest(skillPath, fallbackName = basename(skillPath)) {
|
|
24428
|
+
return readManifest(skillPath, fallbackName, normalizePortableSkillName);
|
|
24429
|
+
}
|
|
24430
|
+
function readPortableSkillManifestForImport(skillPath) {
|
|
24431
|
+
return readManifest(skillPath, basename(skillPath), normalizeNewPortableSkillName);
|
|
24432
|
+
}
|
|
24433
|
+
function readManifest(skillPath, fallbackName, normalizeName) {
|
|
23971
24434
|
const skillJsonPath = join6(skillPath, "skill.json");
|
|
23972
24435
|
const skillMdPath = join6(skillPath, "SKILL.md");
|
|
23973
24436
|
const pkgPath = join6(skillPath, "package.json");
|
|
23974
24437
|
const jsonManifest = existsSync6(skillJsonPath) ? readJsonObject(skillJsonPath) : undefined;
|
|
23975
24438
|
const frontmatter = existsSync6(skillMdPath) ? parseSkillFrontmatter(readFileSync5(skillMdPath, "utf-8")) ?? undefined : undefined;
|
|
23976
24439
|
const pkg = existsSync6(pkgPath) ? readJsonObject(pkgPath) : undefined;
|
|
23977
|
-
const name =
|
|
24440
|
+
const name = normalizeName(stringField(jsonManifest, "name") ?? frontmatter?.name ?? stringValue(pkg?.name) ?? fallbackName);
|
|
23978
24441
|
const description = stringField(jsonManifest, "description") ?? frontmatter?.description ?? stringValue(pkg?.description) ?? `${name} skill`;
|
|
23979
24442
|
const version2 = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
23980
24443
|
const kind = parseSkillKind(stringField(jsonManifest, "kind") ?? frontmatter?.kind);
|
|
@@ -24222,10 +24685,45 @@ function ensureInstructionSkillFiles(skillPath, manifest) {
|
|
|
24222
24685
|
};
|
|
24223
24686
|
if (!existsSync6(join6(skillPath, "SKILL.md"))) {
|
|
24224
24687
|
writeFileSync2(join6(skillPath, "SKILL.md"), renderSkillMd(next));
|
|
24688
|
+
} else {
|
|
24689
|
+
const path = join6(skillPath, "SKILL.md");
|
|
24690
|
+
const content = readFileSync5(path, "utf8");
|
|
24691
|
+
const declaredName = parseSkillFrontmatter(content)?.name;
|
|
24692
|
+
if (declaredName && declaredName !== next.name) {
|
|
24693
|
+
writeFileSync2(path, renameInstructionFrontmatter(content, next.name));
|
|
24694
|
+
}
|
|
24695
|
+
}
|
|
24696
|
+
const packagePath = join6(skillPath, "package.json");
|
|
24697
|
+
if (existsSync6(packagePath)) {
|
|
24698
|
+
const pkg = readJsonObject(packagePath);
|
|
24699
|
+
if (typeof pkg.name === "string" && pkg.name !== next.name) {
|
|
24700
|
+
writeFileSync2(packagePath, `${JSON.stringify({ ...pkg, name: next.name }, null, 2)}
|
|
24701
|
+
`);
|
|
24702
|
+
}
|
|
24225
24703
|
}
|
|
24226
24704
|
writeSkillJsonWithHash(skillPath, next);
|
|
24227
24705
|
return readPortableSkillManifest(skillPath, next.name);
|
|
24228
24706
|
}
|
|
24707
|
+
function renameInstructionFrontmatter(content, name) {
|
|
24708
|
+
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?=\r?\n|$)/);
|
|
24709
|
+
const names = frontmatter?.[1]?.match(/^[ \t]*name[ \t]*:[^\r\n]*/gm) ?? [];
|
|
24710
|
+
const declaration = names.length === 1 ? names[0].match(/^(name[ \t]*:[ \t]*)(.*?)([ \t]*)$/) : null;
|
|
24711
|
+
const scalar = declaration?.[2] ?? "";
|
|
24712
|
+
let simple = /^[a-zA-Z0-9_.@/ -]+$/.test(scalar);
|
|
24713
|
+
if (scalar.startsWith('"')) {
|
|
24714
|
+
try {
|
|
24715
|
+
simple = typeof JSON.parse(scalar) === "string";
|
|
24716
|
+
} catch {
|
|
24717
|
+
simple = false;
|
|
24718
|
+
}
|
|
24719
|
+
} else if (scalar.startsWith("'"))
|
|
24720
|
+
simple = /^'[^'\r\n]*'$/.test(scalar);
|
|
24721
|
+
if (!frontmatter || !declaration || !simple) {
|
|
24722
|
+
throw new Error("Cannot rename instruction SKILL.md: use one unambiguous top-level name scalar in frontmatter.");
|
|
24723
|
+
}
|
|
24724
|
+
const renamed = frontmatter[0].replace(/^name[ \t]*:[^\r\n]*/m, () => `${declaration[1]}${name}${declaration[3]}`);
|
|
24725
|
+
return renamed + content.slice(frontmatter[0].length);
|
|
24726
|
+
}
|
|
24229
24727
|
function copySkillDirectory(source, destination) {
|
|
24230
24728
|
const resolvedSource = lstatSync2(source).isSymbolicLink() ? realpathSync(source) : source;
|
|
24231
24729
|
mkdirSync2(destination, { recursive: true });
|
|
@@ -24624,7 +25122,7 @@ function isOfficialSkillName(name) {
|
|
|
24624
25122
|
return OFFICIAL_SKILL_NAMES.has(name);
|
|
24625
25123
|
}
|
|
24626
25124
|
function scaffoldPortableSkill(name, options = {}) {
|
|
24627
|
-
const skillName =
|
|
25125
|
+
const skillName = normalizeNewPortableSkillName(name);
|
|
24628
25126
|
const root = getPortableSkillsRoot(options);
|
|
24629
25127
|
const skillPath = join7(root, skillName);
|
|
24630
25128
|
if (existsSync7(skillPath)) {
|
|
@@ -24648,9 +25146,9 @@ function portPortableSkill(sourcePath, options = {}) {
|
|
|
24648
25146
|
if (!existsSync7(absoluteSource) || !statSync6(absoluteSource).isDirectory()) {
|
|
24649
25147
|
throw new Error(`Skill source directory not found: ${sourcePath}`);
|
|
24650
25148
|
}
|
|
24651
|
-
const inferred =
|
|
25149
|
+
const inferred = readPortableSkillManifestForImport(absoluteSource);
|
|
24652
25150
|
const explicitName = options.name != null;
|
|
24653
|
-
const skillName =
|
|
25151
|
+
const skillName = normalizeNewPortableSkillName(options.name ?? inferred.name);
|
|
24654
25152
|
if (isOfficialSkillName(skillName) && !options.allowShadow) {
|
|
24655
25153
|
const sourceSlug = safeNormalizeName(basename2(absoluteSource));
|
|
24656
25154
|
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,23 +25629,300 @@ function mergeCustomSkills(skills) {
|
|
|
25131
25629
|
return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
25132
25630
|
}
|
|
25133
25631
|
|
|
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
|
-
}
|
|
25632
|
+
// src/lib/read-access.ts
|
|
25633
|
+
init_fleet_credentials();
|
|
25143
25634
|
|
|
25144
|
-
// src/lib/
|
|
25145
|
-
|
|
25146
|
-
|
|
25147
|
-
var
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25635
|
+
// src/lib/api-url.ts
|
|
25636
|
+
init_fleet_credentials();
|
|
25637
|
+
init_fleet_credentials();
|
|
25638
|
+
var API_URL_ENV_VAR = SKILLS_API_URL_ENV;
|
|
25639
|
+
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>`;
|
|
25640
|
+
function resolveApiUrl(env = process.env, options = {}) {
|
|
25641
|
+
const fleet = resolveSkillsFleet(env, options);
|
|
25642
|
+
return fleet.mode === "hosted" ? fleet.apiOrigin : undefined;
|
|
25643
|
+
}
|
|
25644
|
+
|
|
25645
|
+
// src/lib/remote-registry.ts
|
|
25646
|
+
init_fleet_credentials();
|
|
25647
|
+
|
|
25648
|
+
// src/lib/discovery.ts
|
|
25649
|
+
var VENDOR_TERMS = [
|
|
25650
|
+
"Google Gemini",
|
|
25651
|
+
"OpenAI Sora",
|
|
25652
|
+
"MiniMax Hailuo",
|
|
25653
|
+
"Claude Code",
|
|
25654
|
+
"Claude Vision",
|
|
25655
|
+
"DALL-E 3",
|
|
25656
|
+
"GPT-4o Mini",
|
|
25657
|
+
"Cerebras",
|
|
25658
|
+
"OpenRouter",
|
|
25659
|
+
"Firecrawl",
|
|
25660
|
+
"ElevenLabs",
|
|
25661
|
+
"Anthropic",
|
|
25662
|
+
"OpenAI",
|
|
25663
|
+
"Minimax",
|
|
25664
|
+
"MiniMax",
|
|
25665
|
+
"Gemini",
|
|
25666
|
+
"Claude",
|
|
25667
|
+
"Whisper",
|
|
25668
|
+
"Seedance",
|
|
25669
|
+
"Lyria",
|
|
25670
|
+
"Sora",
|
|
25671
|
+
"Veo",
|
|
25672
|
+
"Exa.ai",
|
|
25673
|
+
"Exa",
|
|
25674
|
+
"XAI",
|
|
25675
|
+
"xAI"
|
|
25676
|
+
];
|
|
25677
|
+
var VENDOR_TAGS = new Set([
|
|
25678
|
+
"anthropic",
|
|
25679
|
+
"cerebras",
|
|
25680
|
+
"claude",
|
|
25681
|
+
"exa",
|
|
25682
|
+
"firecrawl",
|
|
25683
|
+
"gemini",
|
|
25684
|
+
"google",
|
|
25685
|
+
"minimax",
|
|
25686
|
+
"openai",
|
|
25687
|
+
"openrouter",
|
|
25688
|
+
"seedance",
|
|
25689
|
+
"whisper",
|
|
25690
|
+
"xai"
|
|
25691
|
+
]);
|
|
25692
|
+
var vendorPattern = new RegExp(`\\b(${VENDOR_TERMS.map(escapeRegExp).join("|")})\\b`, "gi");
|
|
25693
|
+
function getCompactSkillDiscovery(skill) {
|
|
25694
|
+
return {
|
|
25695
|
+
name: skill.name,
|
|
25696
|
+
category: skill.category,
|
|
25697
|
+
description: sanitizePublicDiscoveryText(skill.description)
|
|
25698
|
+
};
|
|
25699
|
+
}
|
|
25700
|
+
function getPublicSkillDiscovery(skill) {
|
|
25701
|
+
return {
|
|
25702
|
+
...skill,
|
|
25703
|
+
description: sanitizePublicDiscoveryText(skill.description),
|
|
25704
|
+
tags: publicDiscoveryTags(skill.tags)
|
|
25705
|
+
};
|
|
25706
|
+
}
|
|
25707
|
+
function publicDiscoveryTags(tags) {
|
|
25708
|
+
return tags.filter((tag) => !VENDOR_TAGS.has(tag.toLowerCase()));
|
|
25709
|
+
}
|
|
25710
|
+
function sanitizePublicDiscoveryText(text) {
|
|
25711
|
+
let sanitized = text.replace(vendorPattern, "hosted AI").replace(/\bLLM\b/g, "AI").replace(/\s{2,}/g, " ");
|
|
25712
|
+
let previous;
|
|
25713
|
+
do {
|
|
25714
|
+
previous = sanitized;
|
|
25715
|
+
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");
|
|
25716
|
+
} while (sanitized !== previous);
|
|
25717
|
+
return sanitized.trim();
|
|
25718
|
+
}
|
|
25719
|
+
function publicDiscoveryEnvVars(_skillName, envVars) {
|
|
25720
|
+
return envVars;
|
|
25721
|
+
}
|
|
25722
|
+
function publicDiscoveryDependencies(_skillName, dependencies) {
|
|
25723
|
+
return dependencies;
|
|
25724
|
+
}
|
|
25725
|
+
function publicDiscoveryDocumentation(_skill, documentation) {
|
|
25726
|
+
return documentation;
|
|
25727
|
+
}
|
|
25728
|
+
function escapeRegExp(value) {
|
|
25729
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25730
|
+
}
|
|
25731
|
+
|
|
25732
|
+
// src/lib/remote-registry.ts
|
|
25733
|
+
var remoteAvailabilitySchema = exports_external.object({
|
|
25734
|
+
status: exports_external.enum(["available", "unavailable"]),
|
|
25735
|
+
code: exports_external.string().optional(),
|
|
25736
|
+
message: exports_external.string().optional(),
|
|
25737
|
+
details: exports_external.array(exports_external.string()).optional()
|
|
25738
|
+
}).passthrough();
|
|
25739
|
+
var remoteSkillSchema = exports_external.object({
|
|
25740
|
+
name: exports_external.string().min(1).optional(),
|
|
25741
|
+
slug: exports_external.string().min(1).optional(),
|
|
25742
|
+
displayName: exports_external.string().optional(),
|
|
25743
|
+
description: exports_external.string().optional(),
|
|
25744
|
+
category: exports_external.string().optional(),
|
|
25745
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
25746
|
+
dependencies: exports_external.array(exports_external.string()).optional(),
|
|
25747
|
+
version: exports_external.string().optional(),
|
|
25748
|
+
availability: remoteAvailabilitySchema.optional()
|
|
25749
|
+
}).passthrough().refine((skill) => skill.name || skill.slug, {
|
|
25750
|
+
message: "Remote skill requires name or slug"
|
|
25751
|
+
});
|
|
25752
|
+
var secretValuePatterns = [
|
|
25753
|
+
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
25754
|
+
/\bgh[opsur]_[A-Za-z0-9_]{8,}\b/g,
|
|
25755
|
+
/\bgithub_pat_[A-Za-z0-9_]{8,}\b/g,
|
|
25756
|
+
/\bnpm_[A-Za-z0-9_]{8,}\b/g,
|
|
25757
|
+
/\bAKIA[A-Z0-9]{12,}\b/g,
|
|
25758
|
+
/\bAIza[A-Za-z0-9_-]{10,}\b/g,
|
|
25759
|
+
new RegExp("\\bsecret" + "-token:\\s*[A-Za-z0-9._-]+", "gi"),
|
|
25760
|
+
/\bctx7sk\-[A-Za-z0-9_-]{8,}\b/g,
|
|
25761
|
+
/\bxai\-[A-Za-z0-9_-]{8,}\b/g
|
|
25762
|
+
];
|
|
25763
|
+
var remoteSkillDetailSchema = exports_external.union([
|
|
25764
|
+
remoteSkillSchema,
|
|
25765
|
+
exports_external.object({ skill: remoteSkillSchema }),
|
|
25766
|
+
exports_external.object({ data: remoteSkillSchema })
|
|
25767
|
+
]);
|
|
25768
|
+
var remoteRegistrySchema = exports_external.union([
|
|
25769
|
+
exports_external.array(remoteSkillSchema),
|
|
25770
|
+
exports_external.object({ skills: exports_external.array(remoteSkillSchema) }),
|
|
25771
|
+
exports_external.object({ data: exports_external.array(remoteSkillSchema) })
|
|
25772
|
+
]);
|
|
25773
|
+
function getConfiguredApiUrl(env = process.env) {
|
|
25774
|
+
return resolveApiUrl(env);
|
|
25775
|
+
}
|
|
25776
|
+
function buildSkillsApiUrl(apiUrl, endpoint = "/skills") {
|
|
25777
|
+
const url = new URL(apiUrl);
|
|
25778
|
+
const cleanEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
25779
|
+
const pathname = url.pathname.replace(/\/+$/, "");
|
|
25780
|
+
const apiBase = /\/api(?:\/v1)?\/skills$/.test(pathname) ? pathname.slice(0, -"/skills".length) : pathname;
|
|
25781
|
+
if (/\/api(?:\/v1)?$/.test(apiBase)) {
|
|
25782
|
+
url.pathname = `${apiBase}${cleanEndpoint}`;
|
|
25783
|
+
return url.toString();
|
|
25784
|
+
}
|
|
25785
|
+
url.pathname = `${apiBase}/api/v1${cleanEndpoint}`.replace(/\/{2,}/g, "/");
|
|
25786
|
+
return url.toString();
|
|
25787
|
+
}
|
|
25788
|
+
function titleize(name) {
|
|
25789
|
+
return name.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
25790
|
+
}
|
|
25791
|
+
function normalizeRemoteSkill(skill) {
|
|
25792
|
+
const name = skill.name || skill.slug;
|
|
25793
|
+
if (!name)
|
|
25794
|
+
throw new Error("Remote skill requires name or slug");
|
|
25795
|
+
return {
|
|
25796
|
+
name,
|
|
25797
|
+
displayName: skill.displayName || titleize(name),
|
|
25798
|
+
description: skill.description || "",
|
|
25799
|
+
category: skill.category || "Remote",
|
|
25800
|
+
tags: skill.tags || ["remote"],
|
|
25801
|
+
dependencies: skill.dependencies,
|
|
25802
|
+
...skill.version ? { version: skill.version } : {},
|
|
25803
|
+
availability: normalizeRemoteAvailability(skill.availability),
|
|
25804
|
+
source: "remote"
|
|
25805
|
+
};
|
|
25806
|
+
}
|
|
25807
|
+
function normalizeRemoteAvailability(availability) {
|
|
25808
|
+
if (!availability)
|
|
25809
|
+
return { status: "available" };
|
|
25810
|
+
if (availability.status === "available")
|
|
25811
|
+
return { status: "available" };
|
|
25812
|
+
return {
|
|
25813
|
+
status: availability.status,
|
|
25814
|
+
...safeAvailabilityCode(availability.code) ? { code: safeAvailabilityCode(availability.code) } : {},
|
|
25815
|
+
...availability.message ? { message: sanitizeAvailabilityText(availability.message) } : {},
|
|
25816
|
+
...availability.details ? { details: availability.details.map(sanitizeAvailabilityText).filter(Boolean) } : {}
|
|
25817
|
+
};
|
|
25818
|
+
}
|
|
25819
|
+
function safeAvailabilityCode(code) {
|
|
25820
|
+
if (!code)
|
|
25821
|
+
return;
|
|
25822
|
+
return /^[A-Z0-9_]+$/.test(code) ? code : undefined;
|
|
25823
|
+
}
|
|
25824
|
+
function sanitizeAvailabilityText(text) {
|
|
25825
|
+
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();
|
|
25826
|
+
}
|
|
25827
|
+
function parseRemoteRegistryPayload(payload) {
|
|
25828
|
+
const parsed = parseRemoteContract(remoteRegistrySchema, payload, "Remote registry payload did not match the expected skills contract");
|
|
25829
|
+
const rawSkills = Array.isArray(parsed) ? parsed : ("skills" in parsed) ? parsed.skills : parsed.data;
|
|
25830
|
+
return rawSkills.map(normalizeRemoteSkill);
|
|
25831
|
+
}
|
|
25832
|
+
function parseRemoteContract(schema, payload, message) {
|
|
25833
|
+
try {
|
|
25834
|
+
return schema.parse(payload);
|
|
25835
|
+
} catch (error2) {
|
|
25836
|
+
if (error2 instanceof exports_external.ZodError)
|
|
25837
|
+
throw new Error(message, { cause: error2 });
|
|
25838
|
+
throw error2;
|
|
25839
|
+
}
|
|
25840
|
+
}
|
|
25841
|
+
async function remoteRequestHeaders(options) {
|
|
25842
|
+
const headers = new Headers({ Accept: "application/json" });
|
|
25843
|
+
const token = options.authToken !== undefined ? options.authToken : await ambientTokenFor(options.apiUrl);
|
|
25844
|
+
const trimmed = token?.trim();
|
|
25845
|
+
if (trimmed)
|
|
25846
|
+
headers.set("Authorization", `Bearer ${trimmed}`);
|
|
25847
|
+
return headers;
|
|
25848
|
+
}
|
|
25849
|
+
async function ambientTokenFor(callerApiUrl) {
|
|
25850
|
+
const connection = await resolveSkillsConnection();
|
|
25851
|
+
if (!connection)
|
|
25852
|
+
return null;
|
|
25853
|
+
if (callerApiUrl !== undefined && normalizeSkillsApiOrigin(callerApiUrl) !== connection.apiOrigin) {
|
|
25854
|
+
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");
|
|
25855
|
+
}
|
|
25856
|
+
return connection.apiKey;
|
|
25857
|
+
}
|
|
25858
|
+
async function fetchRemoteJson(url, options) {
|
|
25859
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
25860
|
+
const headers = await remoteRequestHeaders(options);
|
|
25861
|
+
const controller = new AbortController;
|
|
25862
|
+
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 1e4);
|
|
25863
|
+
try {
|
|
25864
|
+
const response = await fetchImpl(url, {
|
|
25865
|
+
headers,
|
|
25866
|
+
signal: controller.signal
|
|
25867
|
+
});
|
|
25868
|
+
if (!response.ok) {
|
|
25869
|
+
throw new Error(`Remote registry request failed: ${response.status} ${response.statusText}`);
|
|
25870
|
+
}
|
|
25871
|
+
return response.json();
|
|
25872
|
+
} finally {
|
|
25873
|
+
clearTimeout(timeout);
|
|
25874
|
+
}
|
|
25875
|
+
}
|
|
25876
|
+
async function loadRemoteRegistry(options = {}) {
|
|
25877
|
+
const apiUrl = options.apiUrl || getConfiguredApiUrl();
|
|
25878
|
+
if (!apiUrl) {
|
|
25879
|
+
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}`);
|
|
25880
|
+
}
|
|
25881
|
+
const url = buildSkillsApiUrl(apiUrl, options.endpoint);
|
|
25882
|
+
return parseRemoteRegistryPayload(await fetchRemoteJson(url, options));
|
|
25883
|
+
}
|
|
25884
|
+
async function mergeRemoteRegistry(local, options = {}) {
|
|
25885
|
+
const apiUrl = options.apiUrl || getConfiguredApiUrl();
|
|
25886
|
+
if (!apiUrl)
|
|
25887
|
+
return local;
|
|
25888
|
+
if (options.authToken !== undefined && !options.authToken?.trim())
|
|
25889
|
+
return local;
|
|
25890
|
+
const remote = await loadRemoteRegistry({ ...options, apiUrl });
|
|
25891
|
+
return mergeSkillRegistryLists(local, remote);
|
|
25892
|
+
}
|
|
25893
|
+
|
|
25894
|
+
// src/lib/read-access.ts
|
|
25895
|
+
async function requireSkillsReadAccess(env = process.env, options = {}) {
|
|
25896
|
+
const connection = await resolveSkillsConnection(env, options);
|
|
25897
|
+
return connection ? { mode: "hosted", apiOrigin: connection.apiOrigin } : { mode: "local" };
|
|
25898
|
+
}
|
|
25899
|
+
async function getBrowseRegistry(options = {}) {
|
|
25900
|
+
const profile = options.all ? "all" : "basic";
|
|
25901
|
+
const local = loadRegistryProfile(profile);
|
|
25902
|
+
if (options.remote) {
|
|
25903
|
+
const remote = await loadRemoteRegistry();
|
|
25904
|
+
return mergeSkillRegistryLists(local, remote);
|
|
25905
|
+
}
|
|
25906
|
+
return mergeRemoteRegistry(local);
|
|
25907
|
+
}
|
|
25908
|
+
|
|
25909
|
+
// src/lib/installer.ts
|
|
25910
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9, rmSync as rmSync2 } from "fs";
|
|
25911
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
25912
|
+
import { homedir as homedir2 } from "os";
|
|
25913
|
+
import { fileURLToPath } from "url";
|
|
25914
|
+
// src/lib/utils.ts
|
|
25915
|
+
function normalizeSkillName(name) {
|
|
25916
|
+
return name;
|
|
25917
|
+
}
|
|
25918
|
+
|
|
25919
|
+
// src/lib/project-state.ts
|
|
25920
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
25921
|
+
import { join as join10 } from "path";
|
|
25922
|
+
var VALID_PIN_SOURCES = [
|
|
25923
|
+
"official",
|
|
25924
|
+
"custom",
|
|
25925
|
+
"remote",
|
|
25151
25926
|
"private",
|
|
25152
25927
|
"private-hosted",
|
|
25153
25928
|
"upstream",
|
|
@@ -25158,17 +25933,17 @@ var SKILLS_PROJECT_DIR = ".skills";
|
|
|
25158
25933
|
var PROJECT_CONFIG_FILE = "project.json";
|
|
25159
25934
|
var DEFAULT_EXPORT_DIR = ".skills/exports";
|
|
25160
25935
|
function getProjectStateDir(targetDir = process.cwd()) {
|
|
25161
|
-
return
|
|
25936
|
+
return join10(targetDir, SKILLS_PROJECT_DIR);
|
|
25162
25937
|
}
|
|
25163
25938
|
function getProjectConfigPath(targetDir = process.cwd()) {
|
|
25164
|
-
return
|
|
25939
|
+
return join10(getProjectStateDir(targetDir), PROJECT_CONFIG_FILE);
|
|
25165
25940
|
}
|
|
25166
25941
|
function loadProjectConfig(targetDir = process.cwd()) {
|
|
25167
25942
|
const path = getProjectConfigPath(targetDir);
|
|
25168
25943
|
if (!existsSync9(path))
|
|
25169
25944
|
return null;
|
|
25170
25945
|
try {
|
|
25171
|
-
return normalizeProjectConfig(JSON.parse(
|
|
25946
|
+
return normalizeProjectConfig(JSON.parse(readFileSync8(path, "utf-8")));
|
|
25172
25947
|
} catch {
|
|
25173
25948
|
return null;
|
|
25174
25949
|
}
|
|
@@ -25267,12 +26042,12 @@ var __dirname2 = dirname4(fileURLToPath(import.meta.url));
|
|
|
25267
26042
|
function findSkillsDir() {
|
|
25268
26043
|
let dir = __dirname2;
|
|
25269
26044
|
for (let i = 0;i < 5; i++) {
|
|
25270
|
-
const candidate =
|
|
26045
|
+
const candidate = join11(dir, "skills");
|
|
25271
26046
|
if (existsSync10(candidate) && !dir.includes(".skills"))
|
|
25272
26047
|
return candidate;
|
|
25273
26048
|
dir = dirname4(dir);
|
|
25274
26049
|
}
|
|
25275
|
-
return
|
|
26050
|
+
return join11(__dirname2, "..", "skills");
|
|
25276
26051
|
}
|
|
25277
26052
|
var SKILLS_DIR = findSkillsDir();
|
|
25278
26053
|
function getSkillPath(name) {
|
|
@@ -25280,13 +26055,13 @@ function getSkillPath(name) {
|
|
|
25280
26055
|
const portable = findPortableSkill(skillName);
|
|
25281
26056
|
if (portable)
|
|
25282
26057
|
return portable.path;
|
|
25283
|
-
const legacyCustomPath =
|
|
26058
|
+
const legacyCustomPath = join11(getDataDir(), "custom", skillName);
|
|
25284
26059
|
if (existsSync10(legacyCustomPath))
|
|
25285
26060
|
return legacyCustomPath;
|
|
25286
26061
|
const extensionPath = findExtensionSkillPath(skillName);
|
|
25287
26062
|
if (extensionPath)
|
|
25288
26063
|
return extensionPath;
|
|
25289
|
-
return
|
|
26064
|
+
return join11(SKILLS_DIR, skillName);
|
|
25290
26065
|
}
|
|
25291
26066
|
function getCanonicalSkillName(name) {
|
|
25292
26067
|
return getSkill(name)?.name ?? resolveSkillAlias(normalizeSkillSlug(name));
|
|
@@ -25350,11 +26125,11 @@ function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
|
25350
26125
|
const base = projectDir || process.cwd();
|
|
25351
26126
|
switch (agent) {
|
|
25352
26127
|
case "pi":
|
|
25353
|
-
return scope === "project" ?
|
|
26128
|
+
return scope === "project" ? join11(base, ".pi", "skills") : join11(homedir2(), ".pi", "agent", "skills");
|
|
25354
26129
|
case "opencode":
|
|
25355
|
-
return scope === "project" ?
|
|
26130
|
+
return scope === "project" ? join11(base, ".opencode", "skills") : join11(homedir2(), ".config", "opencode", "skills");
|
|
25356
26131
|
default:
|
|
25357
|
-
return scope === "project" ?
|
|
26132
|
+
return scope === "project" ? join11(base, `.${agent}`, "skills") : join11(homedir2(), `.${agent}`, "skills");
|
|
25358
26133
|
}
|
|
25359
26134
|
}
|
|
25360
26135
|
function warnMissingDependencies(name, targetDir) {
|
|
@@ -25369,11 +26144,11 @@ function warnMissingDependencies(name, targetDir) {
|
|
|
25369
26144
|
}
|
|
25370
26145
|
}
|
|
25371
26146
|
function readBundledSkillVersion(name) {
|
|
25372
|
-
const pkgPath =
|
|
26147
|
+
const pkgPath = join11(getSkillPath(name), "package.json");
|
|
25373
26148
|
if (!existsSync10(pkgPath))
|
|
25374
26149
|
return "unknown";
|
|
25375
26150
|
try {
|
|
25376
|
-
const pkg = JSON.parse(
|
|
26151
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
25377
26152
|
return pkg.version || "unknown";
|
|
25378
26153
|
} catch {
|
|
25379
26154
|
return "unknown";
|
|
@@ -25381,16 +26156,16 @@ function readBundledSkillVersion(name) {
|
|
|
25381
26156
|
}
|
|
25382
26157
|
|
|
25383
26158
|
// src/lib/skillinfo.ts
|
|
25384
|
-
import { existsSync as existsSync11, readFileSync as
|
|
25385
|
-
import { join as
|
|
26159
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
26160
|
+
import { join as join12 } from "path";
|
|
25386
26161
|
function isInstructionSkillDir(skillPath, meta) {
|
|
25387
26162
|
if (meta?.kind === "instruction")
|
|
25388
26163
|
return true;
|
|
25389
|
-
const skillMdPath =
|
|
26164
|
+
const skillMdPath = join12(skillPath, "SKILL.md");
|
|
25390
26165
|
if (!existsSync11(skillMdPath))
|
|
25391
26166
|
return false;
|
|
25392
26167
|
try {
|
|
25393
|
-
return parseSkillFrontmatter(
|
|
26168
|
+
return parseSkillFrontmatter(readFileSync10(skillMdPath, "utf-8"))?.kind === "instruction";
|
|
25394
26169
|
} catch {
|
|
25395
26170
|
return false;
|
|
25396
26171
|
}
|
|
@@ -25416,9 +26191,9 @@ function getSkillDocs(name) {
|
|
|
25416
26191
|
if (!existsSync11(skillPath))
|
|
25417
26192
|
return null;
|
|
25418
26193
|
return {
|
|
25419
|
-
skillMd: readIfExists(
|
|
25420
|
-
readme: readIfExists(
|
|
25421
|
-
claudeMd: readIfExists(
|
|
26194
|
+
skillMd: readIfExists(join12(skillPath, "SKILL.md")),
|
|
26195
|
+
readme: readIfExists(join12(skillPath, "README.md")),
|
|
26196
|
+
claudeMd: readIfExists(join12(skillPath, "CLAUDE.md"))
|
|
25422
26197
|
};
|
|
25423
26198
|
}
|
|
25424
26199
|
function getSkillBestDoc(name) {
|
|
@@ -25433,7 +26208,7 @@ function getSkillRequirements(name) {
|
|
|
25433
26208
|
return null;
|
|
25434
26209
|
const texts = [];
|
|
25435
26210
|
for (const file of ["SKILL.md", "README.md", "CLAUDE.md", ".env.example", ".env.local.example"]) {
|
|
25436
|
-
const content = readIfExists(
|
|
26211
|
+
const content = readIfExists(join12(skillPath, file));
|
|
25437
26212
|
if (content)
|
|
25438
26213
|
texts.push(content);
|
|
25439
26214
|
}
|
|
@@ -25473,10 +26248,10 @@ function getSkillRequirements(name) {
|
|
|
25473
26248
|
const skillName = normalizeSkillName(name);
|
|
25474
26249
|
let cliCommand = `skills run ${skillName}`;
|
|
25475
26250
|
let dependencies = {};
|
|
25476
|
-
const pkgPath =
|
|
26251
|
+
const pkgPath = join12(skillPath, "package.json");
|
|
25477
26252
|
if (existsSync11(pkgPath)) {
|
|
25478
26253
|
try {
|
|
25479
|
-
const pkg = JSON.parse(
|
|
26254
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
25480
26255
|
dependencies = pkg.dependencies || {};
|
|
25481
26256
|
} catch {}
|
|
25482
26257
|
}
|
|
@@ -25503,13 +26278,13 @@ async function runSkill(name, args, options = {}) {
|
|
|
25503
26278
|
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
26279
|
};
|
|
25505
26280
|
}
|
|
25506
|
-
const pkgPath =
|
|
26281
|
+
const pkgPath = join12(skillPath, "package.json");
|
|
25507
26282
|
if (!existsSync11(pkgPath)) {
|
|
25508
26283
|
return { exitCode: 1, error: `No package.json in skill '${name}'` };
|
|
25509
26284
|
}
|
|
25510
26285
|
let entryPoint;
|
|
25511
26286
|
try {
|
|
25512
|
-
const pkg = JSON.parse(
|
|
26287
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
25513
26288
|
if (pkg.bin) {
|
|
25514
26289
|
const binValues = Object.values(pkg.bin);
|
|
25515
26290
|
entryPoint = binValues[0];
|
|
@@ -25523,11 +26298,11 @@ async function runSkill(name, args, options = {}) {
|
|
|
25523
26298
|
} catch {
|
|
25524
26299
|
return { exitCode: 1, error: `Failed to parse package.json for skill '${name}'` };
|
|
25525
26300
|
}
|
|
25526
|
-
const entryPath =
|
|
26301
|
+
const entryPath = join12(skillPath, entryPoint);
|
|
25527
26302
|
if (!existsSync11(entryPath)) {
|
|
25528
26303
|
return { exitCode: 1, error: `Entry point '${entryPoint}' not found in skill '${name}'` };
|
|
25529
26304
|
}
|
|
25530
|
-
const nodeModules =
|
|
26305
|
+
const nodeModules = join12(skillPath, "node_modules");
|
|
25531
26306
|
if (!existsSync11(nodeModules)) {
|
|
25532
26307
|
const install = Bun.spawn(["bun", "install", "--no-save"], {
|
|
25533
26308
|
cwd: skillPath,
|
|
@@ -25538,7 +26313,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
25538
26313
|
}
|
|
25539
26314
|
const proc = Bun.spawn(["bun", "run", entryPath, ...args], {
|
|
25540
26315
|
cwd: skillPath,
|
|
25541
|
-
stdout: options.stdio === "pipe" ? "pipe" : "inherit",
|
|
26316
|
+
stdout: options.stdio === "pipe" ? "pipe" : options.stdio === "stderr" ? 2 : "inherit",
|
|
25542
26317
|
stderr: options.stdio === "pipe" ? "pipe" : "inherit",
|
|
25543
26318
|
stdin: "inherit",
|
|
25544
26319
|
env: { ...process.env, ...options.env }
|
|
@@ -25555,7 +26330,7 @@ async function runSkill(name, args, options = {}) {
|
|
|
25555
26330
|
return { exitCode };
|
|
25556
26331
|
}
|
|
25557
26332
|
function detectProjectSkills(cwd = process.cwd()) {
|
|
25558
|
-
const pkgPath =
|
|
26333
|
+
const pkgPath = join12(cwd, "package.json");
|
|
25559
26334
|
if (!existsSync11(pkgPath)) {
|
|
25560
26335
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
25561
26336
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
@@ -25563,7 +26338,7 @@ function detectProjectSkills(cwd = process.cwd()) {
|
|
|
25563
26338
|
}
|
|
25564
26339
|
let pkg;
|
|
25565
26340
|
try {
|
|
25566
|
-
pkg = JSON.parse(
|
|
26341
|
+
pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
25567
26342
|
} catch {
|
|
25568
26343
|
const alwaysRecommend = ["market-research-report", "repo-onboarding-report", "blog-article"];
|
|
25569
26344
|
const recommended2 = alwaysRecommend.map((name) => loadRegistry().find((s) => s.name === name)).filter((s) => s !== undefined);
|
|
@@ -25653,7 +26428,7 @@ function extractEnvVars(text) {
|
|
|
25653
26428
|
function readIfExists(path) {
|
|
25654
26429
|
try {
|
|
25655
26430
|
if (existsSync11(path)) {
|
|
25656
|
-
return
|
|
26431
|
+
return readFileSync10(path, "utf-8");
|
|
25657
26432
|
}
|
|
25658
26433
|
} catch {}
|
|
25659
26434
|
return null;
|
|
@@ -26068,6 +26843,104 @@ var toolContracts = [
|
|
|
26068
26843
|
dependencies: objectSchema({}, [], "Package dependencies.", true)
|
|
26069
26844
|
})
|
|
26070
26845
|
},
|
|
26846
|
+
{
|
|
26847
|
+
name: "update_account_profile",
|
|
26848
|
+
title: "Update Account Display Name",
|
|
26849
|
+
description: "Update your display name with fresh email verification on the configured server.",
|
|
26850
|
+
params: ["name", "email", "code"],
|
|
26851
|
+
category: "execution",
|
|
26852
|
+
sideEffects: "local-process-or-remote-run",
|
|
26853
|
+
stable: true,
|
|
26854
|
+
inputSchema: objectSchema({ name: stringSchema("Display name, 1\u2013100 characters"), email: { type: "string", format: "email" }, code: { type: "string", pattern: "^\\d{6}$" } }, ["name", "email", "code"]),
|
|
26855
|
+
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"])
|
|
26856
|
+
},
|
|
26857
|
+
{
|
|
26858
|
+
name: "update_workspace_name",
|
|
26859
|
+
title: "Update Workspace Name",
|
|
26860
|
+
description: "Update the current workspace name as an owner/admin with fresh email verification.",
|
|
26861
|
+
params: ["name", "email", "code"],
|
|
26862
|
+
category: "execution",
|
|
26863
|
+
sideEffects: "local-process-or-remote-run",
|
|
26864
|
+
stable: true,
|
|
26865
|
+
inputSchema: objectSchema({ name: stringSchema("Workspace name, 1\u2013100 characters"), email: { type: "string", format: "email" }, code: { type: "string", pattern: "^\\d{6}$" } }, ["name", "email", "code"]),
|
|
26866
|
+
outputSchema: objectSchema({ organization: objectSchema({ id: stringSchema("Workspace identifier."), slug: stringSchema("Stable workspace slug."), name: stringSchema("Workspace name.") }, ["id", "slug", "name"]) }, ["organization"])
|
|
26867
|
+
},
|
|
26868
|
+
{
|
|
26869
|
+
name: "set_workspace_member_role",
|
|
26870
|
+
title: "Set Current Workspace Member Role",
|
|
26871
|
+
description: "Set an exact membership incarnation's role with its observed expectedRole and fresh verification; no automatic retry.",
|
|
26872
|
+
params: ["membershipId", "role", "expectedRole", "email", "code"],
|
|
26873
|
+
category: "execution",
|
|
26874
|
+
sideEffects: "local-process-or-remote-run",
|
|
26875
|
+
stable: true,
|
|
26876
|
+
inputSchema: objectSchema({
|
|
26877
|
+
membershipId: { type: "string", pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$" },
|
|
26878
|
+
role: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26879
|
+
expectedRole: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26880
|
+
email: { type: "string", format: "email" },
|
|
26881
|
+
code: { type: "string", pattern: "^\\d{6}$" }
|
|
26882
|
+
}, ["membershipId", "role", "expectedRole", "email", "code"]),
|
|
26883
|
+
outputSchema: objectSchema({
|
|
26884
|
+
organizationId: stringSchema("Current workspace identifier."),
|
|
26885
|
+
changed: { type: "boolean" },
|
|
26886
|
+
member: objectSchema({
|
|
26887
|
+
membershipId: stringSchema("Membership incarnation."),
|
|
26888
|
+
userId: stringSchema("Account identifier."),
|
|
26889
|
+
email: stringSchema("Member email."),
|
|
26890
|
+
displayName: { oneOf: [{ type: "string" }, { type: "null" }] },
|
|
26891
|
+
role: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26892
|
+
createdAt: stringSchema("Exact server timestamp including microseconds.")
|
|
26893
|
+
}, ["membershipId", "userId", "email", "displayName", "role", "createdAt"])
|
|
26894
|
+
}, ["organizationId", "member", "changed"])
|
|
26895
|
+
},
|
|
26896
|
+
{
|
|
26897
|
+
name: "remove_workspace_member",
|
|
26898
|
+
title: "Remove Current Workspace Member",
|
|
26899
|
+
description: "Remove exactly this membership incarnation using its observed expectedRole and fresh verification; self-removal is unavailable.",
|
|
26900
|
+
params: ["membershipId", "expectedRole", "email", "code"],
|
|
26901
|
+
category: "execution",
|
|
26902
|
+
sideEffects: "local-process-or-remote-run",
|
|
26903
|
+
stable: true,
|
|
26904
|
+
inputSchema: objectSchema({
|
|
26905
|
+
membershipId: { type: "string", pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$" },
|
|
26906
|
+
expectedRole: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26907
|
+
email: { type: "string", format: "email" },
|
|
26908
|
+
code: { type: "string", pattern: "^\\d{6}$" }
|
|
26909
|
+
}, ["membershipId", "expectedRole", "email", "code"]),
|
|
26910
|
+
outputSchema: objectSchema({
|
|
26911
|
+
organizationId: stringSchema("Current workspace identifier."),
|
|
26912
|
+
membershipId: stringSchema("Removed membership incarnation."),
|
|
26913
|
+
removed: { type: "boolean", const: true },
|
|
26914
|
+
alreadyRemoved: { type: "boolean" }
|
|
26915
|
+
}, ["organizationId", "membershipId", "removed", "alreadyRemoved"])
|
|
26916
|
+
},
|
|
26917
|
+
{
|
|
26918
|
+
name: "list_workspace_members",
|
|
26919
|
+
title: "List Current Workspace Members",
|
|
26920
|
+
description: "Read one current-workspace roster page with fresh owner/admin email verification; saved credentials stay unchanged.",
|
|
26921
|
+
params: ["email", "code", "limit?", "cursor?"],
|
|
26922
|
+
category: "execution",
|
|
26923
|
+
sideEffects: "local-process-or-remote-run",
|
|
26924
|
+
stable: true,
|
|
26925
|
+
inputSchema: objectSchema({
|
|
26926
|
+
email: { type: "string", format: "email" },
|
|
26927
|
+
code: { type: "string", pattern: "^\\d{6}$" },
|
|
26928
|
+
limit: { type: "integer", minimum: 1, maximum: 100 },
|
|
26929
|
+
cursor: { type: "string", pattern: "^[A-Za-z0-9_-]{1,512}$" }
|
|
26930
|
+
}, ["email", "code"]),
|
|
26931
|
+
outputSchema: objectSchema({
|
|
26932
|
+
organizationId: stringSchema("Current workspace identifier."),
|
|
26933
|
+
members: arraySchema(objectSchema({
|
|
26934
|
+
membershipId: stringSchema("Membership incarnation."),
|
|
26935
|
+
userId: stringSchema("Account identifier."),
|
|
26936
|
+
email: stringSchema("Member email."),
|
|
26937
|
+
displayName: { oneOf: [{ type: "string" }, { type: "null" }] },
|
|
26938
|
+
role: { type: "string", enum: ["owner", "admin", "member", "viewer"] },
|
|
26939
|
+
createdAt: stringSchema("Exact server timestamp including microseconds.")
|
|
26940
|
+
}, ["membershipId", "userId", "email", "displayName", "role", "createdAt"])),
|
|
26941
|
+
nextCursor: { oneOf: [{ type: "string" }, { type: "null" }] }
|
|
26942
|
+
}, ["organizationId", "members", "nextCursor"])
|
|
26943
|
+
},
|
|
26071
26944
|
{
|
|
26072
26945
|
name: "list_api_keys",
|
|
26073
26946
|
title: "List API Keys",
|
|
@@ -26577,90 +27450,6 @@ function clone2(value) {
|
|
|
26577
27450
|
return JSON.parse(JSON.stringify(value));
|
|
26578
27451
|
}
|
|
26579
27452
|
|
|
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
27453
|
// src/lib/tool-primitives.ts
|
|
26665
27454
|
var TOOL_PRIMITIVE_SCHEMA_VERSION = 1;
|
|
26666
27455
|
var TOOL_PRIMITIVES = [
|
|
@@ -27119,22 +27908,20 @@ function compactRemoteRun(run) {
|
|
|
27119
27908
|
}
|
|
27120
27909
|
|
|
27121
27910
|
// src/mcp/helpers.ts
|
|
27911
|
+
init_fleet_credentials();
|
|
27912
|
+
async function readSurface(body) {
|
|
27913
|
+
try {
|
|
27914
|
+
return await body();
|
|
27915
|
+
} catch (error2) {
|
|
27916
|
+
if (isSkillsFleetCredentialError(error2))
|
|
27917
|
+
return mcpError("AUTH_REQUIRED", error2.message, ["skills auth login"]);
|
|
27918
|
+
throw error2;
|
|
27919
|
+
}
|
|
27920
|
+
}
|
|
27122
27921
|
function stripNulls(obj) {
|
|
27123
27922
|
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null && v !== undefined && !(Array.isArray(v) && v.length === 0)));
|
|
27124
27923
|
}
|
|
27125
27924
|
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
27925
|
function cacheClear() {
|
|
27139
27926
|
searchCache.clear();
|
|
27140
27927
|
}
|
|
@@ -27174,9 +27961,10 @@ function registerDiscoveryTools(server) {
|
|
|
27174
27961
|
limit: exports_external.number().optional(),
|
|
27175
27962
|
offset: exports_external.number().optional()
|
|
27176
27963
|
}
|
|
27177
|
-
}, async ({ category, profile, detail, limit, offset }) => {
|
|
27964
|
+
}, async ({ category, profile, detail, limit, offset }) => readSurface(async () => {
|
|
27178
27965
|
const selectedProfile = profile || "basic";
|
|
27179
|
-
const
|
|
27966
|
+
const registry2 = await getBrowseRegistry({ all: selectedProfile === "all" });
|
|
27967
|
+
const skills = category ? registry2.filter((s) => s.category === category) : registry2;
|
|
27180
27968
|
const mapped = detail ? skills.map(getPublicSkillDiscovery) : skills.map(getCompactSkillDiscovery);
|
|
27181
27969
|
const page = paginate(mapped, {
|
|
27182
27970
|
limit: parsePageLimit(limit, DEFAULT_MCP_LIMIT, { max: 100 }),
|
|
@@ -27192,7 +27980,7 @@ function registerDiscoveryTools(server) {
|
|
|
27192
27980
|
nextArguments: page.hasMore ? { profile: selectedProfile, category, detail: Boolean(detail), limit: page.limit, offset: page.nextOffset } : null,
|
|
27193
27981
|
detailHint: detail ? undefined : "Set detail:true for full public skill objects, or call get_skill_info for one skill."
|
|
27194
27982
|
});
|
|
27195
|
-
});
|
|
27983
|
+
}));
|
|
27196
27984
|
server.registerTool("list_pinned_skills", {
|
|
27197
27985
|
title: "List Pinned Skills",
|
|
27198
27986
|
description: "List skills pinned in the current project's .skills/project.json.",
|
|
@@ -27216,13 +28004,9 @@ function registerDiscoveryTools(server) {
|
|
|
27216
28004
|
limit: exports_external.number().optional(),
|
|
27217
28005
|
offset: exports_external.number().optional()
|
|
27218
28006
|
}
|
|
27219
|
-
}, async ({ query, profile, detail, limit, offset }) => {
|
|
28007
|
+
}, async ({ query, profile, detail, limit, offset }) => readSurface(async () => {
|
|
27220
28008
|
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);
|
|
28009
|
+
const results = searchSkills(query, await getBrowseRegistry({ all: selectedProfile === "all" }));
|
|
27226
28010
|
const out = detail ? results.map(getPublicSkillDiscovery) : results.map(getCompactSkillDiscovery);
|
|
27227
28011
|
const page = paginate(out, {
|
|
27228
28012
|
limit: parsePageLimit(limit, DEFAULT_MCP_LIMIT, { max: 100 }),
|
|
@@ -27238,14 +28022,15 @@ function registerDiscoveryTools(server) {
|
|
|
27238
28022
|
nextArguments: page.hasMore ? { query, profile: selectedProfile, detail: Boolean(detail), limit: page.limit, offset: page.nextOffset } : null,
|
|
27239
28023
|
detailHint: detail ? undefined : "Set detail:true for full public skill objects, or call get_skill_info for one skill."
|
|
27240
28024
|
});
|
|
27241
|
-
});
|
|
28025
|
+
}));
|
|
27242
28026
|
server.registerTool("get_skill_info", {
|
|
27243
28027
|
title: "Get Skill Info",
|
|
27244
28028
|
description: "Get skill metadata, env vars, and dependencies.",
|
|
27245
28029
|
inputSchema: {
|
|
27246
28030
|
name: exports_external.string()
|
|
27247
28031
|
}
|
|
27248
|
-
}, async ({ name }) => {
|
|
28032
|
+
}, async ({ name }) => readSurface(async () => {
|
|
28033
|
+
await requireSkillsReadAccess();
|
|
27249
28034
|
const skill = getSkill(name);
|
|
27250
28035
|
if (!skill) {
|
|
27251
28036
|
return mcpError("SKILL_NOT_FOUND", `Skill '${name}' not found`, findSimilarSkills(name));
|
|
@@ -27265,20 +28050,21 @@ function registerDiscoveryTools(server) {
|
|
|
27265
28050
|
return {
|
|
27266
28051
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
27267
28052
|
};
|
|
27268
|
-
});
|
|
28053
|
+
}));
|
|
27269
28054
|
server.registerTool("get_skill_docs", {
|
|
27270
28055
|
title: "Get Skill Docs",
|
|
27271
28056
|
description: "Get skill documentation (SKILL.md > README.md > CLAUDE.md).",
|
|
27272
28057
|
inputSchema: {
|
|
27273
28058
|
name: exports_external.string()
|
|
27274
28059
|
}
|
|
27275
|
-
}, async ({ name }) => {
|
|
28060
|
+
}, async ({ name }) => readSurface(async () => {
|
|
28061
|
+
await requireSkillsReadAccess();
|
|
27276
28062
|
const doc2 = getSkillBestDoc(name);
|
|
27277
28063
|
if (!doc2) {
|
|
27278
28064
|
return mcpError("NO_DOCS", `No documentation found for '${name}'`);
|
|
27279
28065
|
}
|
|
27280
28066
|
return { content: [{ type: "text", text: doc2 }] };
|
|
27281
|
-
});
|
|
28067
|
+
}));
|
|
27282
28068
|
server.registerTool("list_tool_primitives", {
|
|
27283
28069
|
title: "List Tool Primitives",
|
|
27284
28070
|
description: "List primitive tools that skills depend on across CLI, MCP, API, and hosted worker execution.",
|
|
@@ -27324,25 +28110,74 @@ function registerDiscoveryTools(server) {
|
|
|
27324
28110
|
}
|
|
27325
28111
|
|
|
27326
28112
|
// src/mcp/operation-tools.ts
|
|
27327
|
-
import { existsSync as
|
|
27328
|
-
import { join as
|
|
28113
|
+
import { existsSync as existsSync14, readdirSync as readdirSync8, statSync as statSync9 } from "fs";
|
|
28114
|
+
import { join as join15 } from "path";
|
|
28115
|
+
|
|
28116
|
+
// src/lib/credential-state.ts
|
|
28117
|
+
init_auth_store();
|
|
28118
|
+
init_fleet_credentials();
|
|
28119
|
+
function describeCredentialState() {
|
|
28120
|
+
let credentialsFile = null;
|
|
28121
|
+
let mode = null;
|
|
28122
|
+
try {
|
|
28123
|
+
credentialsFile = getAuthFilePath();
|
|
28124
|
+
const bits = credentialFileMode();
|
|
28125
|
+
mode = bits === null ? null : `0${bits.toString(8).padStart(3, "0")}`;
|
|
28126
|
+
} catch {}
|
|
28127
|
+
try {
|
|
28128
|
+
const fleet = resolveSkillsFleet();
|
|
28129
|
+
if (fleet.mode === "hosted") {
|
|
28130
|
+
return {
|
|
28131
|
+
mode: "hosted",
|
|
28132
|
+
apiUrl: fleet.apiOrigin,
|
|
28133
|
+
apiUrlSource: fleet.apiUrlSource,
|
|
28134
|
+
apiKeySource: fleet.apiKeySource,
|
|
28135
|
+
apiKeyTier: fleet.apiKeyTier,
|
|
28136
|
+
credentialsFile,
|
|
28137
|
+
credentialsFileMode: mode,
|
|
28138
|
+
error: null
|
|
28139
|
+
};
|
|
28140
|
+
}
|
|
28141
|
+
return {
|
|
28142
|
+
mode: "local",
|
|
28143
|
+
apiUrl: null,
|
|
28144
|
+
apiUrlSource: null,
|
|
28145
|
+
apiKeySource: null,
|
|
28146
|
+
apiKeyTier: null,
|
|
28147
|
+
credentialsFile,
|
|
28148
|
+
credentialsFileMode: mode,
|
|
28149
|
+
error: null
|
|
28150
|
+
};
|
|
28151
|
+
} catch (error2) {
|
|
28152
|
+
return {
|
|
28153
|
+
mode: "misconfigured",
|
|
28154
|
+
apiUrl: null,
|
|
28155
|
+
apiUrlSource: null,
|
|
28156
|
+
apiKeySource: null,
|
|
28157
|
+
apiKeyTier: null,
|
|
28158
|
+
credentialsFile,
|
|
28159
|
+
credentialsFileMode: mode,
|
|
28160
|
+
error: error2.message
|
|
28161
|
+
};
|
|
28162
|
+
}
|
|
28163
|
+
}
|
|
27329
28164
|
|
|
27330
28165
|
// src/lib/run-state.ts
|
|
27331
28166
|
import { createHash as createHash2, randomBytes } from "crypto";
|
|
27332
|
-
import { existsSync as
|
|
27333
|
-
import { extname, join as
|
|
28167
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync12, readdirSync as readdirSync7, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
28168
|
+
import { extname, join as join14, relative as relative2 } from "path";
|
|
27334
28169
|
function createSkillRun(params, targetDir = process.cwd()) {
|
|
27335
28170
|
const now = new Date;
|
|
27336
28171
|
const id = createRunId(now);
|
|
27337
28172
|
const day = now.toISOString().slice(0, 10);
|
|
27338
28173
|
const skillName = normalizeSkillName(params.skill);
|
|
27339
28174
|
const root = getProjectStateDir(targetDir);
|
|
27340
|
-
const runDir =
|
|
27341
|
-
const logsDir =
|
|
27342
|
-
const exportDir =
|
|
27343
|
-
|
|
27344
|
-
|
|
27345
|
-
|
|
28175
|
+
const runDir = join14(root, "runs", day, id);
|
|
28176
|
+
const logsDir = join14(runDir, "logs");
|
|
28177
|
+
const exportDir = join14(root, "exports", skillName, id);
|
|
28178
|
+
mkdirSync6(logsDir, { recursive: true });
|
|
28179
|
+
mkdirSync6(exportDir, { recursive: true });
|
|
28180
|
+
mkdirSync6(join14(root, "tmp"), { recursive: true });
|
|
27346
28181
|
const record3 = {
|
|
27347
28182
|
id,
|
|
27348
28183
|
skill: skillName,
|
|
@@ -27394,22 +28229,22 @@ function updateSkillRun(context, patch) {
|
|
|
27394
28229
|
return context.record;
|
|
27395
28230
|
}
|
|
27396
28231
|
function writeRunLogs(context, stdout = "", stderr = "") {
|
|
27397
|
-
|
|
27398
|
-
|
|
28232
|
+
writeFileSync6(join14(context.logsDir, "stdout.log"), stdout);
|
|
28233
|
+
writeFileSync6(join14(context.logsDir, "stderr.log"), stderr);
|
|
27399
28234
|
}
|
|
27400
28235
|
function appendRunEvent(context, event, data = {}) {
|
|
27401
28236
|
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + `
|
|
27402
28237
|
`;
|
|
27403
|
-
const path =
|
|
27404
|
-
const previous =
|
|
27405
|
-
|
|
28238
|
+
const path = join14(context.runDir, "events.ndjson");
|
|
28239
|
+
const previous = existsSync13(path) ? readFileSync12(path, "utf-8") : "";
|
|
28240
|
+
writeFileSync6(path, previous + line);
|
|
27406
28241
|
}
|
|
27407
28242
|
function findSkillRun(runId, targetDir = process.cwd()) {
|
|
27408
|
-
const runsRoot =
|
|
27409
|
-
if (!
|
|
28243
|
+
const runsRoot = join14(getProjectStateDir(targetDir), "runs");
|
|
28244
|
+
if (!existsSync13(runsRoot))
|
|
27410
28245
|
return null;
|
|
27411
28246
|
for (const day of readdirSync7(runsRoot)) {
|
|
27412
|
-
const record3 = readRunRecord(
|
|
28247
|
+
const record3 = readRunRecord(join14(runsRoot, day, runId));
|
|
27413
28248
|
if (record3)
|
|
27414
28249
|
return record3;
|
|
27415
28250
|
}
|
|
@@ -27426,20 +28261,20 @@ function skillRunEnv(context) {
|
|
|
27426
28261
|
};
|
|
27427
28262
|
}
|
|
27428
28263
|
function writeRunRecord(context) {
|
|
27429
|
-
|
|
28264
|
+
writeFileSync6(join14(context.runDir, "run.json"), JSON.stringify(context.record, null, 2) + `
|
|
27430
28265
|
`);
|
|
27431
28266
|
}
|
|
27432
28267
|
function writeArtifactsManifest(context, artifacts) {
|
|
27433
|
-
|
|
28268
|
+
writeFileSync6(join14(context.runDir, "artifacts.json"), JSON.stringify({ runId: context.record.id, artifacts }, null, 2) + `
|
|
27434
28269
|
`);
|
|
27435
28270
|
}
|
|
27436
28271
|
function collectRunArtifacts(context) {
|
|
27437
|
-
if (!
|
|
28272
|
+
if (!existsSync13(context.exportDir))
|
|
27438
28273
|
return [];
|
|
27439
28274
|
const artifacts = [];
|
|
27440
28275
|
for (const path of walkFiles(context.exportDir)) {
|
|
27441
|
-
const stat =
|
|
27442
|
-
const bytes =
|
|
28276
|
+
const stat = statSync8(path);
|
|
28277
|
+
const bytes = readFileSync12(path);
|
|
27443
28278
|
artifacts.push({
|
|
27444
28279
|
path: toProjectRelative(context.targetDir, path),
|
|
27445
28280
|
mime: mimeForPath(path),
|
|
@@ -27450,11 +28285,11 @@ function collectRunArtifacts(context) {
|
|
|
27450
28285
|
return artifacts.sort((a, b) => a.path.localeCompare(b.path));
|
|
27451
28286
|
}
|
|
27452
28287
|
function readRunRecord(runDir) {
|
|
27453
|
-
const path =
|
|
27454
|
-
if (!
|
|
28288
|
+
const path = join14(runDir, "run.json");
|
|
28289
|
+
if (!existsSync13(path))
|
|
27455
28290
|
return null;
|
|
27456
28291
|
try {
|
|
27457
|
-
return JSON.parse(
|
|
28292
|
+
return JSON.parse(readFileSync12(path, "utf-8"));
|
|
27458
28293
|
} catch {
|
|
27459
28294
|
return null;
|
|
27460
28295
|
}
|
|
@@ -27462,8 +28297,8 @@ function readRunRecord(runDir) {
|
|
|
27462
28297
|
function walkFiles(dir) {
|
|
27463
28298
|
const files = [];
|
|
27464
28299
|
for (const entry of readdirSync7(dir)) {
|
|
27465
|
-
const full =
|
|
27466
|
-
if (
|
|
28300
|
+
const full = join14(dir, entry);
|
|
28301
|
+
if (statSync8(full).isDirectory())
|
|
27467
28302
|
files.push(...walkFiles(full));
|
|
27468
28303
|
else
|
|
27469
28304
|
files.push(full);
|
|
@@ -27536,17 +28371,20 @@ async function resolveConfiguredRunRouting(skill, env = process.env) {
|
|
|
27536
28371
|
try {
|
|
27537
28372
|
connection = await resolveSkillsConnection(env);
|
|
27538
28373
|
} catch (error2) {
|
|
27539
|
-
const isMissingCredential = (error2
|
|
28374
|
+
const isMissingCredential = isSkillsFleetCredentialError2(error2) && error2.code === "MISSING_API_CREDENTIAL";
|
|
27540
28375
|
if (!isMissingCredential)
|
|
27541
28376
|
throw error2;
|
|
27542
28377
|
return {
|
|
27543
28378
|
route: "error",
|
|
27544
28379
|
code: "REMOTE_REQUIRES_CREDENTIAL",
|
|
27545
|
-
error: `${skill.name} is a server-owned skill. ${error2.message}`
|
|
28380
|
+
error: skill.serverOwned ? `${skill.name} is a server-owned skill. ${error2.message}` : `${skill.name} cannot run: ${error2.message}`
|
|
27546
28381
|
};
|
|
27547
28382
|
}
|
|
27548
28383
|
return resolveRunRouting(skill, connection?.apiKey, connection?.apiOrigin);
|
|
27549
28384
|
}
|
|
28385
|
+
function isSkillsFleetCredentialError2(error2) {
|
|
28386
|
+
return typeof error2 === "object" && error2 !== null && error2.name === "SkillsFleetCredentialError";
|
|
28387
|
+
}
|
|
27550
28388
|
|
|
27551
28389
|
// src/mcp/operation-tools.ts
|
|
27552
28390
|
function registerOperationTools(server) {
|
|
@@ -27709,39 +28547,42 @@ function registerOperationTools(server) {
|
|
|
27709
28547
|
server.registerTool("list_categories", {
|
|
27710
28548
|
title: "List Categories",
|
|
27711
28549
|
description: "List all 17 skill categories with skill counts."
|
|
27712
|
-
}, async () => {
|
|
27713
|
-
const
|
|
28550
|
+
}, async () => readSurface(async () => {
|
|
28551
|
+
const registry2 = await getBrowseRegistry({ all: true });
|
|
28552
|
+
const extras = Array.from(new Set(registry2.map((skill) => skill.category))).filter((category) => !CATEGORIES.includes(category)).sort();
|
|
28553
|
+
const cats = [...CATEGORIES, ...extras].map((category) => ({
|
|
27714
28554
|
name: category,
|
|
27715
|
-
count:
|
|
28555
|
+
count: registry2.filter((skill) => skill.category === category).length
|
|
27716
28556
|
}));
|
|
27717
28557
|
return { content: [{ type: "text", text: JSON.stringify(cats, null, 2) }] };
|
|
27718
|
-
});
|
|
28558
|
+
}));
|
|
27719
28559
|
server.registerTool("list_tags", {
|
|
27720
28560
|
title: "List Tags",
|
|
27721
28561
|
description: "List all unique skill tags with occurrence counts."
|
|
27722
|
-
}, async () => {
|
|
28562
|
+
}, async () => readSurface(async () => {
|
|
27723
28563
|
const tagCounts = new Map;
|
|
27724
|
-
for (const skill of
|
|
28564
|
+
for (const skill of await getBrowseRegistry({ all: true })) {
|
|
27725
28565
|
for (const tag of skill.tags) {
|
|
27726
28566
|
tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
|
|
27727
28567
|
}
|
|
27728
28568
|
}
|
|
27729
28569
|
const sorted = Array.from(tagCounts.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([name, count]) => ({ name, count }));
|
|
27730
28570
|
return { content: [{ type: "text", text: JSON.stringify(sorted, null, 2) }] };
|
|
27731
|
-
});
|
|
28571
|
+
}));
|
|
27732
28572
|
server.registerTool("get_requirements", {
|
|
27733
28573
|
title: "Get Requirements",
|
|
27734
28574
|
description: "Get env vars, system deps, and npm dependencies for a skill.",
|
|
27735
28575
|
inputSchema: {
|
|
27736
28576
|
name: exports_external.string()
|
|
27737
28577
|
}
|
|
27738
|
-
}, async ({ name }) => {
|
|
28578
|
+
}, async ({ name }) => readSurface(async () => {
|
|
28579
|
+
await requireSkillsReadAccess();
|
|
27739
28580
|
const reqs = getSkillRequirements(name);
|
|
27740
28581
|
if (!reqs) {
|
|
27741
28582
|
return mcpError("SKILL_NOT_FOUND", `Skill '${name}' not found`, findSimilarSkills(name));
|
|
27742
28583
|
}
|
|
27743
28584
|
return { content: [{ type: "text", text: JSON.stringify(reqs, null, 2) }] };
|
|
27744
|
-
});
|
|
28585
|
+
}));
|
|
27745
28586
|
server.registerTool("run_skill", {
|
|
27746
28587
|
title: "Run Skill",
|
|
27747
28588
|
description: "Run a skill by name with optional arguments.",
|
|
@@ -27783,20 +28624,16 @@ function registerOperationTools(server) {
|
|
|
27783
28624
|
} catch (error2) {
|
|
27784
28625
|
return mcpError("INVALID_INPUT_FILES", error2.message);
|
|
27785
28626
|
}
|
|
28627
|
+
if (routing.route === "error") {
|
|
28628
|
+
const suggestions = routing.code === "REMOTE_REQUIRES_ORIGIN" ? ["skills setup --api-url <url>", "skills auth login"] : ["skills auth login"];
|
|
28629
|
+
return mcpError(routing.code, routing.error, suggestions);
|
|
28630
|
+
}
|
|
27786
28631
|
const runContext = createSkillRun({
|
|
27787
28632
|
skill: skillName,
|
|
27788
28633
|
args: runArgs,
|
|
27789
28634
|
remote: routing.route === "remote",
|
|
27790
28635
|
...routing.route === "remote" ? { remoteApiOrigin: routing.apiOrigin } : {}
|
|
27791
28636
|
});
|
|
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
28637
|
if (routing.route === "remote") {
|
|
27801
28638
|
try {
|
|
27802
28639
|
const { RemoteSkillsClient: RemoteSkillsClient2 } = await Promise.resolve().then(() => (init_remote_client(), exports_remote_client));
|
|
@@ -27953,21 +28790,22 @@ function registerOperationTools(server) {
|
|
|
27953
28790
|
});
|
|
27954
28791
|
server.registerTool("whoami", {
|
|
27955
28792
|
title: "Skills Whoami",
|
|
27956
|
-
description: "Show setup summary: version, pinned skills, agent configs, cwd."
|
|
28793
|
+
description: "Show setup summary: version, pinned skills, agent configs, cwd, and the credential/transport SOURCES (never values)."
|
|
27957
28794
|
}, async () => {
|
|
27958
28795
|
const version2 = package_default.version;
|
|
27959
28796
|
const cwd = process.cwd();
|
|
28797
|
+
const credential = describeCredentialState();
|
|
27960
28798
|
const installed = getInstalledSkills();
|
|
27961
28799
|
const agents = [];
|
|
27962
28800
|
for (const agent of AGENT_TARGETS) {
|
|
27963
28801
|
const agentSkillsPath = getAgentSkillsDir(agent, "global");
|
|
27964
|
-
const exists =
|
|
28802
|
+
const exists = existsSync14(agentSkillsPath);
|
|
27965
28803
|
let skillCount = 0;
|
|
27966
28804
|
if (exists) {
|
|
27967
28805
|
try {
|
|
27968
28806
|
skillCount = readdirSync8(agentSkillsPath).filter((f) => {
|
|
27969
|
-
const full =
|
|
27970
|
-
return !f.startsWith(".") &&
|
|
28807
|
+
const full = join15(agentSkillsPath, f);
|
|
28808
|
+
return !f.startsWith(".") && statSync9(full).isDirectory();
|
|
27971
28809
|
}).length;
|
|
27972
28810
|
} catch {}
|
|
27973
28811
|
}
|
|
@@ -27980,7 +28818,8 @@ function registerOperationTools(server) {
|
|
|
27980
28818
|
installed,
|
|
27981
28819
|
agents,
|
|
27982
28820
|
skillsDir,
|
|
27983
|
-
cwd
|
|
28821
|
+
cwd,
|
|
28822
|
+
credential
|
|
27984
28823
|
};
|
|
27985
28824
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
27986
28825
|
});
|
|
@@ -28012,29 +28851,17 @@ function compactRunToolPayload(payload, detailHint) {
|
|
|
28012
28851
|
}
|
|
28013
28852
|
|
|
28014
28853
|
// src/lib/feedback.ts
|
|
28015
|
-
import { appendFileSync, existsSync as
|
|
28016
|
-
import { dirname as
|
|
28854
|
+
import { appendFileSync, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "fs";
|
|
28855
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
28017
28856
|
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
28857
|
function getFeedbackDbPath() {
|
|
28031
|
-
return
|
|
28858
|
+
return join16(getDataDir(), "skills.db");
|
|
28032
28859
|
}
|
|
28033
28860
|
function getFeedbackDb() {
|
|
28034
28861
|
const dbPath = getFeedbackDbPath();
|
|
28035
|
-
const dir =
|
|
28036
|
-
if (!
|
|
28037
|
-
|
|
28862
|
+
const dir = dirname6(dbPath);
|
|
28863
|
+
if (!existsSync15(dir))
|
|
28864
|
+
mkdirSync7(dir, { recursive: true });
|
|
28038
28865
|
const db = new Database(dbPath);
|
|
28039
28866
|
db.exec("PRAGMA journal_mode = WAL");
|
|
28040
28867
|
db.exec([
|
|
@@ -28060,10 +28887,10 @@ function saveFeedback(input) {
|
|
|
28060
28887
|
throw new Error("Feedback message is required");
|
|
28061
28888
|
const category = input.category ?? "general";
|
|
28062
28889
|
if (isApiMode()) {
|
|
28063
|
-
const path =
|
|
28064
|
-
const dir =
|
|
28065
|
-
if (!
|
|
28066
|
-
|
|
28890
|
+
const path = join16(getDataDir(), "feedback.jsonl");
|
|
28891
|
+
const dir = dirname6(path);
|
|
28892
|
+
if (!existsSync15(dir))
|
|
28893
|
+
mkdirSync7(dir, { recursive: true });
|
|
28067
28894
|
appendFileSync(path, JSON.stringify({ message, category, email: input.email ?? null, agent: input.agent ?? null, version: input.version ?? null, createdAt: new Date().toISOString() }) + `
|
|
28068
28895
|
`);
|
|
28069
28896
|
return { saved: true, category, path };
|
|
@@ -28208,26 +29035,26 @@ function registerResourceMetaTools(server) {
|
|
|
28208
29035
|
}
|
|
28209
29036
|
|
|
28210
29037
|
// src/lib/scheduler.ts
|
|
28211
|
-
import { existsSync as
|
|
28212
|
-
import { join as
|
|
29038
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "fs";
|
|
29039
|
+
import { join as join17 } from "path";
|
|
28213
29040
|
function getSchedulesPath(targetDir = process.cwd()) {
|
|
28214
|
-
return
|
|
29041
|
+
return join17(targetDir, ".skills", "schedules.json");
|
|
28215
29042
|
}
|
|
28216
29043
|
function loadSchedules(targetDir = process.cwd()) {
|
|
28217
29044
|
const path = getSchedulesPath(targetDir);
|
|
28218
|
-
if (
|
|
29045
|
+
if (existsSync16(path)) {
|
|
28219
29046
|
try {
|
|
28220
|
-
return JSON.parse(
|
|
29047
|
+
return JSON.parse(readFileSync13(path, "utf-8"));
|
|
28221
29048
|
} catch {}
|
|
28222
29049
|
}
|
|
28223
29050
|
return { version: 1, schedules: [] };
|
|
28224
29051
|
}
|
|
28225
29052
|
function saveSchedules(data, targetDir = process.cwd()) {
|
|
28226
29053
|
const path = getSchedulesPath(targetDir);
|
|
28227
|
-
const dir =
|
|
28228
|
-
if (!
|
|
28229
|
-
|
|
28230
|
-
|
|
29054
|
+
const dir = join17(targetDir, ".skills");
|
|
29055
|
+
if (!existsSync16(dir))
|
|
29056
|
+
mkdirSync8(dir, { recursive: true });
|
|
29057
|
+
writeFileSync7(path, JSON.stringify(data, null, 2));
|
|
28231
29058
|
}
|
|
28232
29059
|
function validateCronField(expr, min, max, label) {
|
|
28233
29060
|
for (const part of expr.split(",")) {
|
|
@@ -28487,14 +29314,14 @@ function registerScheduleTools(server) {
|
|
|
28487
29314
|
// src/lib/native-storage.ts
|
|
28488
29315
|
import { createHash as createHash4, createHmac } from "crypto";
|
|
28489
29316
|
import {
|
|
28490
|
-
existsSync as
|
|
28491
|
-
mkdirSync as
|
|
28492
|
-
readFileSync as
|
|
29317
|
+
existsSync as existsSync17,
|
|
29318
|
+
mkdirSync as mkdirSync9,
|
|
29319
|
+
readFileSync as readFileSync14,
|
|
28493
29320
|
readdirSync as readdirSync9,
|
|
28494
|
-
statSync as
|
|
28495
|
-
writeFileSync as
|
|
29321
|
+
statSync as statSync10,
|
|
29322
|
+
writeFileSync as writeFileSync8
|
|
28496
29323
|
} from "fs";
|
|
28497
|
-
import { dirname as
|
|
29324
|
+
import { dirname as dirname7, join as join18, normalize as normalize3, relative as relative3, sep as sep2 } from "path";
|
|
28498
29325
|
var SKILLS_STORAGE_TABLES = [
|
|
28499
29326
|
"skills_sync_records",
|
|
28500
29327
|
"skills_sync_cursors"
|
|
@@ -28570,7 +29397,7 @@ function getSkillsNativeStorageStatus(options = {}) {
|
|
|
28570
29397
|
local: {
|
|
28571
29398
|
dataDir: getDataDir(),
|
|
28572
29399
|
projectStateDir: getProjectStateDir(targetDir),
|
|
28573
|
-
feedbackDbPath:
|
|
29400
|
+
feedbackDbPath: join18(getDataDir(), "skills.db")
|
|
28574
29401
|
},
|
|
28575
29402
|
remote: {
|
|
28576
29403
|
databaseConfigured: Boolean(config2.databaseUrl),
|
|
@@ -28590,9 +29417,9 @@ function getStorageStatus(options = {}) {
|
|
|
28590
29417
|
function exportSkillsLocalSnapshot(targetDir = process.cwd(), options = {}) {
|
|
28591
29418
|
const projectStateDir = getProjectStateDir(targetDir);
|
|
28592
29419
|
const files = [];
|
|
28593
|
-
if (
|
|
29420
|
+
if (existsSync17(projectStateDir)) {
|
|
28594
29421
|
for (const filePath of walkFiles2(projectStateDir)) {
|
|
28595
|
-
const bytes =
|
|
29422
|
+
const bytes = readFileSync14(filePath);
|
|
28596
29423
|
const relativePath = toPosix(relative3(targetDir, filePath));
|
|
28597
29424
|
files.push({
|
|
28598
29425
|
path: relativePath,
|
|
@@ -28674,8 +29501,8 @@ function parsePositiveInteger(value) {
|
|
|
28674
29501
|
function walkFiles2(dir) {
|
|
28675
29502
|
const files = [];
|
|
28676
29503
|
for (const entry of readdirSync9(dir)) {
|
|
28677
|
-
const full =
|
|
28678
|
-
const stats =
|
|
29504
|
+
const full = join18(dir, entry);
|
|
29505
|
+
const stats = statSync10(full);
|
|
28679
29506
|
if (stats.isDirectory())
|
|
28680
29507
|
files.push(...walkFiles2(full));
|
|
28681
29508
|
else
|
|
@@ -28730,6 +29557,10 @@ function registerStorageTools(server) {
|
|
|
28730
29557
|
}
|
|
28731
29558
|
|
|
28732
29559
|
// src/lib/remote-auth.ts
|
|
29560
|
+
init_remote_workspace_selection();
|
|
29561
|
+
init_remote_files();
|
|
29562
|
+
init_remote_workspace();
|
|
29563
|
+
init_remote_workspace();
|
|
28733
29564
|
init_remote_client();
|
|
28734
29565
|
init_fleet_credentials();
|
|
28735
29566
|
var MAX_ERROR_DETAIL_LENGTH = 200;
|
|
@@ -28768,13 +29599,13 @@ async function requestAuthApi(instance, path, options) {
|
|
|
28768
29599
|
apiUrl: safeUrl
|
|
28769
29600
|
});
|
|
28770
29601
|
}
|
|
28771
|
-
const
|
|
28772
|
-
const body =
|
|
29602
|
+
const text2 = await res.text();
|
|
29603
|
+
const body = text2 ? parseJsonBody(text2) : {};
|
|
28773
29604
|
if (!res.ok) {
|
|
28774
|
-
const
|
|
28775
|
-
const detail = typeof
|
|
28776
|
-
const error2 = typeof
|
|
28777
|
-
const code = typeof
|
|
29605
|
+
const record5 = isRecord5(body) ? body : {};
|
|
29606
|
+
const detail = typeof record5.detail === "string" ? record5.detail : undefined;
|
|
29607
|
+
const error2 = typeof record5.error === "string" ? record5.error : undefined;
|
|
29608
|
+
const code = typeof record5.code === "string" ? record5.code : undefined;
|
|
28778
29609
|
throw new HostedApiError(detail || error2 || `${res.status} ${res.statusText}`, {
|
|
28779
29610
|
status: res.status,
|
|
28780
29611
|
code,
|
|
@@ -28785,21 +29616,21 @@ async function requestAuthApi(instance, path, options) {
|
|
|
28785
29616
|
}
|
|
28786
29617
|
return body;
|
|
28787
29618
|
}
|
|
28788
|
-
function parseJsonBody(
|
|
29619
|
+
function parseJsonBody(text2) {
|
|
28789
29620
|
try {
|
|
28790
|
-
return JSON.parse(
|
|
29621
|
+
return JSON.parse(text2);
|
|
28791
29622
|
} catch {
|
|
28792
|
-
return { detail: condenseErrorBody(
|
|
29623
|
+
return { detail: condenseErrorBody(text2) };
|
|
28793
29624
|
}
|
|
28794
29625
|
}
|
|
28795
|
-
function condenseErrorBody(
|
|
28796
|
-
const stripped = /<[a-z!/]/i.test(
|
|
29626
|
+
function condenseErrorBody(text2) {
|
|
29627
|
+
const stripped = /<[a-z!/]/i.test(text2) ? text2.replace(/<(script|style)[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ") : text2;
|
|
28797
29628
|
const collapsed = stripped.replace(/\s+/g, " ").trim();
|
|
28798
29629
|
if (collapsed.length <= MAX_ERROR_DETAIL_LENGTH)
|
|
28799
29630
|
return collapsed;
|
|
28800
29631
|
return `${collapsed.slice(0, MAX_ERROR_DETAIL_LENGTH - 1).trimEnd()}\u2026`;
|
|
28801
29632
|
}
|
|
28802
|
-
function
|
|
29633
|
+
function isRecord5(value) {
|
|
28803
29634
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
28804
29635
|
}
|
|
28805
29636
|
|
|
@@ -28820,22 +29651,90 @@ class RemoteSkillsAuthClient {
|
|
|
28820
29651
|
pollDevice(deviceCode) {
|
|
28821
29652
|
return this.request("/api/auth/device/token", { method: "POST", body: JSON.stringify({ deviceCode }) });
|
|
28822
29653
|
}
|
|
28823
|
-
async sessionClient(email2, code) {
|
|
29654
|
+
async sessionClient(email2, code, context) {
|
|
29655
|
+
if (context !== undefined) {
|
|
29656
|
+
const target = workspaceContext(context), apiOrigin2 = this.apiOrigin;
|
|
29657
|
+
const session = await this.switchWorkspace(email2, code, target);
|
|
29658
|
+
return new RemoteSkillsClient(session.token, apiOrigin2);
|
|
29659
|
+
}
|
|
29660
|
+
const apiOrigin = this.apiOrigin;
|
|
28824
29661
|
if (!email2.includes("@") || !/^\d{6}$/.test(code))
|
|
28825
|
-
throw new Error("Fresh email and six-digit verification code are required to manage
|
|
29662
|
+
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
28826
29663
|
const login = await this.verifyCode(email2, code);
|
|
28827
29664
|
if (!login || typeof login.token !== "string" || !login.token)
|
|
28828
29665
|
throw new Error("The server did not return an authorized account session");
|
|
28829
|
-
return new RemoteSkillsClient(login.token,
|
|
29666
|
+
return new RemoteSkillsClient(login.token, apiOrigin);
|
|
29667
|
+
}
|
|
29668
|
+
async listAccountWorkspaces(email2, code, expectedUserId) {
|
|
29669
|
+
const login = await this.workspaceLogin(email2, code, expectedUserId);
|
|
29670
|
+
const result = await new RemoteSkillsClient(login.token, login.apiOrigin).listAccountWorkspaces(login.userId);
|
|
29671
|
+
return { userId: login.userId, ...result };
|
|
29672
|
+
}
|
|
29673
|
+
async switchWorkspace(email2, code, context) {
|
|
29674
|
+
const target = workspaceContext(context);
|
|
29675
|
+
const login = await this.workspaceLogin(email2, code, target.userId);
|
|
29676
|
+
return new RemoteSkillsClient(login.token, login.apiOrigin).switchWorkspace(target);
|
|
29677
|
+
}
|
|
29678
|
+
async workspaceLogin(email2, code, expectedUserId) {
|
|
29679
|
+
const expected = expectedUserId === undefined ? undefined : workspaceExpectedUserId(expectedUserId);
|
|
29680
|
+
const apiOrigin = this.apiOrigin;
|
|
29681
|
+
if (typeof email2 !== "string" || !email2.includes("@") || typeof code !== "string" || !/^\d{6}$/.test(code))
|
|
29682
|
+
throw new Error("Fresh email and six-digit verification code are required to manage this account");
|
|
29683
|
+
let response;
|
|
29684
|
+
try {
|
|
29685
|
+
response = await fetch(`${apiOrigin}/api/auth/verify`, {
|
|
29686
|
+
method: "POST",
|
|
29687
|
+
redirect: "error",
|
|
29688
|
+
credentials: "omit",
|
|
29689
|
+
signal: AbortSignal.timeout(15000),
|
|
29690
|
+
headers: { "Content-Type": "application/json" },
|
|
29691
|
+
body: JSON.stringify({ email: email2, code })
|
|
29692
|
+
});
|
|
29693
|
+
} catch {
|
|
29694
|
+
throw new HostedApiError("Unable to verify the Skills account.");
|
|
29695
|
+
}
|
|
29696
|
+
if (!response.ok) {
|
|
29697
|
+
response.body?.cancel().catch(() => {});
|
|
29698
|
+
throw new HostedApiError("Unable to verify the Skills account.", { status: response.status });
|
|
29699
|
+
}
|
|
29700
|
+
let value;
|
|
29701
|
+
try {
|
|
29702
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedResponse(response, 64 * 1024)));
|
|
29703
|
+
} catch {
|
|
29704
|
+
throw new HostedApiError("The server returned an invalid account verification result.");
|
|
29705
|
+
}
|
|
29706
|
+
return { ...parseWorkspaceLogin(value, expected), apiOrigin };
|
|
28830
29707
|
}
|
|
28831
|
-
async createApiKey(email2, code, name, scopes) {
|
|
28832
|
-
|
|
29708
|
+
async createApiKey(email2, code, name, scopes, context) {
|
|
29709
|
+
const capturedScopes = scopes === undefined ? undefined : [...scopes];
|
|
29710
|
+
return (await this.sessionClient(email2, code, context)).createApiKey(name, capturedScopes);
|
|
28833
29711
|
}
|
|
28834
|
-
async listApiKeys(email2, code) {
|
|
28835
|
-
return (await this.sessionClient(email2, code)).listApiKeys();
|
|
29712
|
+
async listApiKeys(email2, code, context) {
|
|
29713
|
+
return (await this.sessionClient(email2, code, context)).listApiKeys();
|
|
28836
29714
|
}
|
|
28837
|
-
async revokeApiKey(email2, code, keyId) {
|
|
28838
|
-
return (await this.sessionClient(email2, code)).revokeApiKey(keyId);
|
|
29715
|
+
async revokeApiKey(email2, code, keyId, context) {
|
|
29716
|
+
return (await this.sessionClient(email2, code, context)).revokeApiKey(keyId);
|
|
29717
|
+
}
|
|
29718
|
+
async updateProfile(email2, code, input, context) {
|
|
29719
|
+
const body = customerNamePatch(input, "displayName");
|
|
29720
|
+
return (await this.sessionClient(email2, code, context)).updateProfile({ displayName: body.displayName });
|
|
29721
|
+
}
|
|
29722
|
+
async updateCurrentWorkspace(email2, code, input, context) {
|
|
29723
|
+
const body = customerNamePatch(input, "name");
|
|
29724
|
+
return (await this.sessionClient(email2, code, context)).updateCurrentWorkspace({ name: body.name });
|
|
29725
|
+
}
|
|
29726
|
+
async listWorkspaceMembers(email2, code, options = {}, context) {
|
|
29727
|
+
workspaceMembersQuery(options);
|
|
29728
|
+
const captured = { ...options };
|
|
29729
|
+
return (await this.sessionClient(email2, code, context)).listWorkspaceMembers(captured);
|
|
29730
|
+
}
|
|
29731
|
+
async setWorkspaceMemberRole(email2, code, membershipId, input, context) {
|
|
29732
|
+
const captured = workspaceMemberRoleInput(membershipId, input);
|
|
29733
|
+
return (await this.sessionClient(email2, code, context)).setWorkspaceMemberRole(captured.membershipId, captured.body);
|
|
29734
|
+
}
|
|
29735
|
+
async removeWorkspaceMember(email2, code, membershipId, input, context) {
|
|
29736
|
+
const captured = workspaceMemberRemovalInput(membershipId, input);
|
|
29737
|
+
return (await this.sessionClient(email2, code, context)).removeWorkspaceMember(captured.membershipId, captured.body);
|
|
28839
29738
|
}
|
|
28840
29739
|
request(path, options) {
|
|
28841
29740
|
if (!["/api/auth/login", "/api/auth/verify", "/api/auth/device/start", "/api/auth/device/token", "/api/auth/keys", "/api/auth/whoami"].includes(path))
|
|
@@ -28844,10 +29743,147 @@ class RemoteSkillsAuthClient {
|
|
|
28844
29743
|
}
|
|
28845
29744
|
}
|
|
28846
29745
|
|
|
28847
|
-
// src/
|
|
29746
|
+
// src/lib/workspace-profile.ts
|
|
29747
|
+
import { constants as constants2, closeSync as closeSync3, fstatSync as fstatSync3, lstatSync as lstatSync4, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, openSync as openSync3, readFileSync as readFileSync15, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
28848
29748
|
init_auth_store();
|
|
29749
|
+
init_fleet_credentials();
|
|
29750
|
+
init_instance_credentials();
|
|
29751
|
+
init_remote_client();
|
|
29752
|
+
init_remote_workspace_selection();
|
|
29753
|
+
|
|
29754
|
+
class WorkspaceProfileError extends Error {
|
|
29755
|
+
}
|
|
29756
|
+
var fail = (message) => {
|
|
29757
|
+
throw new WorkspaceProfileError(message);
|
|
29758
|
+
};
|
|
29759
|
+
function stat(path) {
|
|
29760
|
+
try {
|
|
29761
|
+
return lstatSync4(path);
|
|
29762
|
+
} catch (error2) {
|
|
29763
|
+
if (error2.code === "ENOENT")
|
|
29764
|
+
return null;
|
|
29765
|
+
throw error2;
|
|
29766
|
+
}
|
|
29767
|
+
}
|
|
29768
|
+
function safeText(file) {
|
|
29769
|
+
if (stat(file) === null)
|
|
29770
|
+
return null;
|
|
29771
|
+
const fd = openSync3(file, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
|
|
29772
|
+
try {
|
|
29773
|
+
const s = fstatSync3(fd);
|
|
29774
|
+
if (!s.isFile() || s.size > 65536 || ![256, 384].includes(s.mode & 4095) || process.getuid && s.uid !== process.getuid())
|
|
29775
|
+
return fail("The selected profile must use bounded owner-only regular files.");
|
|
29776
|
+
return readFileSync15(fd, "utf8");
|
|
29777
|
+
} finally {
|
|
29778
|
+
closeSync3(fd);
|
|
29779
|
+
}
|
|
29780
|
+
}
|
|
29781
|
+
function checkIdentityMetadata(file, identity) {
|
|
29782
|
+
const text2 = safeText(file);
|
|
29783
|
+
if (text2 === null)
|
|
29784
|
+
return;
|
|
29785
|
+
let value;
|
|
29786
|
+
try {
|
|
29787
|
+
value = JSON.parse(text2);
|
|
29788
|
+
} catch {
|
|
29789
|
+
return fail("The profile identity metadata is invalid. Sign in again before managing this workspace.");
|
|
29790
|
+
}
|
|
29791
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
29792
|
+
return fail("The profile identity metadata is invalid.");
|
|
29793
|
+
for (const [key, expected] of Object.entries({ userId: identity.user.id, orgId: identity.organization.id })) {
|
|
29794
|
+
if (value[key] !== undefined && value[key] !== expected)
|
|
29795
|
+
return fail("The profile identity metadata does not match its authenticated key. Sign in again before managing this workspace.");
|
|
29796
|
+
}
|
|
29797
|
+
}
|
|
29798
|
+
async function keyIdentity(key, origin) {
|
|
29799
|
+
const value = await new RemoteSkillsClient(key, origin).getIdentity();
|
|
29800
|
+
if (value.authMethod !== "api_key")
|
|
29801
|
+
return fail("The selected credential is not a workspace API key.");
|
|
29802
|
+
const user = value.user;
|
|
29803
|
+
return parseWorkspaceIdentity(value, workspaceExpectedUserId(user?.id));
|
|
29804
|
+
}
|
|
29805
|
+
function prepareProfileWorkspace(action, source = process.env) {
|
|
29806
|
+
const env = { ...source }, origin = getApiUrl(action, env), profile = selectedSkillsProfile(env);
|
|
29807
|
+
const unchanged2 = captureSkillsCredentialFiles(skillsProfileCredentialFiles(env));
|
|
29808
|
+
return { origin, async resolve() {
|
|
29809
|
+
unchanged2();
|
|
29810
|
+
if (!profile)
|
|
29811
|
+
return { origin, context: undefined, unchanged: unchanged2 };
|
|
29812
|
+
const connection = await resolveSkillsConnection(env);
|
|
29813
|
+
if (!connection || connection.apiOrigin !== origin)
|
|
29814
|
+
return fail("The selected profile has no usable credential for this server.");
|
|
29815
|
+
const identity = await keyIdentity(connection.apiKey, origin);
|
|
29816
|
+
checkIdentityMetadata(getIdentityFilePath(env), identity);
|
|
29817
|
+
unchanged2();
|
|
29818
|
+
const context = { userId: identity.user.id, membershipId: identity.user.membershipId };
|
|
29819
|
+
return { origin, context, unchanged: unchanged2 };
|
|
29820
|
+
} };
|
|
29821
|
+
}
|
|
29822
|
+
async function captureProfileWorkspace(action, source = process.env) {
|
|
29823
|
+
return prepareProfileWorkspace(action, source).resolve();
|
|
29824
|
+
}
|
|
29825
|
+
|
|
29826
|
+
// src/mcp/remote-customer-tools.ts
|
|
28849
29827
|
init_remote_client();
|
|
28850
29828
|
function registerRemoteCustomerTools(server) {
|
|
29829
|
+
const memberRole = exports_external.enum(["owner", "admin", "member", "viewer"]);
|
|
29830
|
+
const memberInput = {
|
|
29831
|
+
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}$/),
|
|
29832
|
+
expectedRole: memberRole,
|
|
29833
|
+
email: exports_external.string().email(),
|
|
29834
|
+
code: exports_external.string().regex(/^\d{6}$/)
|
|
29835
|
+
};
|
|
29836
|
+
server.registerTool("set_workspace_member_role", {
|
|
29837
|
+
title: "Set Current Workspace Member Role",
|
|
29838
|
+
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.",
|
|
29839
|
+
inputSchema: exports_external.object({ ...memberInput, role: memberRole }).strict()
|
|
29840
|
+
}, async ({ membershipId, role: role2, expectedRole, email: email2, code }) => {
|
|
29841
|
+
try {
|
|
29842
|
+
return mcpJson(await freshAccount("Set workspace member role", (client, context) => client.setWorkspaceMemberRole(email2, code, membershipId, { role: role2, expectedRole }, context)));
|
|
29843
|
+
} catch (error2) {
|
|
29844
|
+
return memberError(error2);
|
|
29845
|
+
}
|
|
29846
|
+
});
|
|
29847
|
+
server.registerTool("remove_workspace_member", {
|
|
29848
|
+
title: "Remove Current Workspace Member",
|
|
29849
|
+
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.",
|
|
29850
|
+
inputSchema: exports_external.object(memberInput).strict()
|
|
29851
|
+
}, async ({ membershipId, expectedRole, email: email2, code }) => {
|
|
29852
|
+
try {
|
|
29853
|
+
return mcpJson(await freshAccount("Remove workspace member", (client, context) => client.removeWorkspaceMember(email2, code, membershipId, { expectedRole }, context)));
|
|
29854
|
+
} catch (error2) {
|
|
29855
|
+
return memberError(error2);
|
|
29856
|
+
}
|
|
29857
|
+
});
|
|
29858
|
+
server.registerTool("list_workspace_members", {
|
|
29859
|
+
title: "List Current Workspace Members",
|
|
29860
|
+
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.",
|
|
29861
|
+
inputSchema: exports_external.object({
|
|
29862
|
+
email: exports_external.string().email(),
|
|
29863
|
+
code: exports_external.string().regex(/^\d{6}$/),
|
|
29864
|
+
limit: exports_external.number().int().min(1).max(100).optional(),
|
|
29865
|
+
cursor: exports_external.string().regex(/^[A-Za-z0-9_-]{1,512}$/).optional()
|
|
29866
|
+
}).strict()
|
|
29867
|
+
}, async ({ email: email2, code, limit, cursor: cursor2 }) => {
|
|
29868
|
+
try {
|
|
29869
|
+
return mcpJson(await freshAccount("List workspace members", (client, context) => client.listWorkspaceMembers(email2, code, { limit, cursor: cursor2 }, context)));
|
|
29870
|
+
} catch {
|
|
29871
|
+
return mcpError("WORKSPACE_MEMBERS_FAILED", "Unable to list workspace members. Check the selected server, owner/admin permissions, pagination and fresh verification code.");
|
|
29872
|
+
}
|
|
29873
|
+
});
|
|
29874
|
+
for (const kind of ["profile", "workspace"]) {
|
|
29875
|
+
server.registerTool(kind === "profile" ? "update_account_profile" : "update_workspace_name", {
|
|
29876
|
+
title: kind === "profile" ? "Update Account Display Name" : "Update Workspace Name",
|
|
29877
|
+
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.",
|
|
29878
|
+
inputSchema: exports_external.object({ name: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }).strict()
|
|
29879
|
+
}, async ({ name, email: email2, code }) => {
|
|
29880
|
+
try {
|
|
29881
|
+
return mcpJson(await freshAccount("Update customer name", async (client, context) => kind === "profile" ? client.updateProfile(email2, code, { displayName: name }, context) : client.updateCurrentWorkspace(email2, code, { name }, context)));
|
|
29882
|
+
} catch {
|
|
29883
|
+
return mcpError("NAME_UPDATE_FAILED", "Unable to update the name. Check the selected server, name, permissions and fresh verification code.");
|
|
29884
|
+
}
|
|
29885
|
+
});
|
|
29886
|
+
}
|
|
28851
29887
|
for (const operation of REMOTE_CUSTOMER_OPERATIONS) {
|
|
28852
29888
|
const inputSchema = {};
|
|
28853
29889
|
if (operation.parameter)
|
|
@@ -28864,9 +29900,9 @@ function registerRemoteCustomerTools(server) {
|
|
|
28864
29900
|
inputSchema: { email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }
|
|
28865
29901
|
}, async ({ email: email2, code }) => {
|
|
28866
29902
|
try {
|
|
28867
|
-
return mcpJson(await
|
|
28868
|
-
} catch
|
|
28869
|
-
return mcpError("KEY_LIST_FAILED",
|
|
29903
|
+
return mcpJson(await freshAccount("List API keys", (client, context) => client.listApiKeys(email2, code, context)));
|
|
29904
|
+
} catch {
|
|
29905
|
+
return mcpError("KEY_LIST_FAILED", "Unable to list API keys. Check the selected profile, server, account and fresh verification code.");
|
|
28870
29906
|
}
|
|
28871
29907
|
});
|
|
28872
29908
|
server.registerTool("revoke_api_key", {
|
|
@@ -28875,9 +29911,9 @@ function registerRemoteCustomerTools(server) {
|
|
|
28875
29911
|
inputSchema: { key_id: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/) }
|
|
28876
29912
|
}, async ({ key_id, email: email2, code }) => {
|
|
28877
29913
|
try {
|
|
28878
|
-
return mcpJson(await
|
|
28879
|
-
} catch
|
|
28880
|
-
return mcpError("KEY_REVOKE_FAILED",
|
|
29914
|
+
return mcpJson(await freshAccount("Revoke API key", (client, context) => client.revokeApiKey(email2, code, key_id, context)));
|
|
29915
|
+
} catch {
|
|
29916
|
+
return mcpError("KEY_REVOKE_FAILED", "Unable to revoke this API key. Check the selected profile, key, account and fresh verification code.");
|
|
28881
29917
|
}
|
|
28882
29918
|
});
|
|
28883
29919
|
server.registerTool("create_api_key", {
|
|
@@ -28885,10 +29921,11 @@ function registerRemoteCustomerTools(server) {
|
|
|
28885
29921
|
description: "Create an API key using fresh email OTP reauthentication; returns its secret once. A stored API key cannot grant this authority.",
|
|
28886
29922
|
inputSchema: { name: exports_external.string().min(1), email: exports_external.string().email(), code: exports_external.string().regex(/^\d{6}$/), scopes: exports_external.array(exports_external.string()).optional() }
|
|
28887
29923
|
}, async ({ name, email: email2, code, scopes }) => {
|
|
29924
|
+
const capturedScopes = scopes === undefined ? undefined : [...scopes];
|
|
28888
29925
|
try {
|
|
28889
|
-
return mcpJson(await
|
|
28890
|
-
} catch
|
|
28891
|
-
return mcpError("KEY_CREATION_FAILED",
|
|
29926
|
+
return mcpJson(await freshAccount("Create API key", (client, context) => client.createApiKey(email2, code, name, capturedScopes, context)));
|
|
29927
|
+
} catch {
|
|
29928
|
+
return mcpError("KEY_CREATION_FAILED", "API key creation could not be confirmed. Check the selected profile and workspace keys before retrying; a lost response may still have created a key.");
|
|
28892
29929
|
}
|
|
28893
29930
|
});
|
|
28894
29931
|
server.registerTool("quote_skill", {
|
|
@@ -28906,6 +29943,9 @@ function registerRemoteCustomerTools(server) {
|
|
|
28906
29943
|
return { ...metadata, base64: Buffer.from(bytes).toString("base64") };
|
|
28907
29944
|
}));
|
|
28908
29945
|
}
|
|
29946
|
+
function memberError(error2) {
|
|
29947
|
+
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.");
|
|
29948
|
+
}
|
|
28909
29949
|
async function callRemote(action) {
|
|
28910
29950
|
try {
|
|
28911
29951
|
const client = await createRemoteSkillsClient();
|
|
@@ -28913,9 +29953,17 @@ async function callRemote(action) {
|
|
|
28913
29953
|
return mcpError("AUTH_REQUIRED", "Configure a Skills API and sign in with skills auth login");
|
|
28914
29954
|
return mcpJson(await action(client));
|
|
28915
29955
|
} catch (error2) {
|
|
29956
|
+
if (error2 instanceof RemoteCapabilityUnavailableError) {
|
|
29957
|
+
return { ...mcpJson({ code: error2.code, message: error2.message, status: error2.status }), isError: true };
|
|
29958
|
+
}
|
|
28916
29959
|
return mcpError("REMOTE_REQUEST_FAILED", error2 instanceof Error ? error2.message : "Skills server request failed");
|
|
28917
29960
|
}
|
|
28918
29961
|
}
|
|
29962
|
+
async function freshAccount(action, operation) {
|
|
29963
|
+
const target = await captureProfileWorkspace(action);
|
|
29964
|
+
target.unchanged();
|
|
29965
|
+
return operation(new RemoteSkillsAuthClient(target.origin), target.context);
|
|
29966
|
+
}
|
|
28919
29967
|
|
|
28920
29968
|
// src/mcp/server.ts
|
|
28921
29969
|
function buildServer() {
|
|
@@ -30383,6 +31431,7 @@ async function startSkillsMcpHttpServer(options = {}) {
|
|
|
30383
31431
|
}
|
|
30384
31432
|
|
|
30385
31433
|
// src/mcp/index.ts
|
|
31434
|
+
init_fleet_credentials();
|
|
30386
31435
|
var args = process.argv.slice(2);
|
|
30387
31436
|
function printHelp() {
|
|
30388
31437
|
console.log(`Usage: skills-mcp [options]
|
|
@@ -30404,7 +31453,18 @@ if (args.includes("--version") || args.includes("-V")) {
|
|
|
30404
31453
|
console.log(package_default.version);
|
|
30405
31454
|
process.exit(0);
|
|
30406
31455
|
}
|
|
31456
|
+
function assertSkillsMcpConfigured(env = process.env) {
|
|
31457
|
+
try {
|
|
31458
|
+
resolveSkillsFleet(env);
|
|
31459
|
+
} catch (error2) {
|
|
31460
|
+
if (!isSkillsFleetCredentialError(error2))
|
|
31461
|
+
throw error2;
|
|
31462
|
+
console.error(error2.message);
|
|
31463
|
+
process.exit(1);
|
|
31464
|
+
}
|
|
31465
|
+
}
|
|
30407
31466
|
async function startMcpStdio() {
|
|
31467
|
+
assertSkillsMcpConfigured();
|
|
30408
31468
|
const server2 = buildServer();
|
|
30409
31469
|
await server2.connect(new StdioServerTransport);
|
|
30410
31470
|
}
|
|
@@ -30413,6 +31473,7 @@ async function main() {
|
|
|
30413
31473
|
await startMcpStdio();
|
|
30414
31474
|
return;
|
|
30415
31475
|
}
|
|
31476
|
+
assertSkillsMcpConfigured();
|
|
30416
31477
|
const port = parseMcpHttpPort(args);
|
|
30417
31478
|
await startSkillsMcpHttpServer({ port, hostname: "127.0.0.1" });
|
|
30418
31479
|
}
|
|
@@ -30424,5 +31485,6 @@ if (import.meta.main) {
|
|
|
30424
31485
|
}
|
|
30425
31486
|
export {
|
|
30426
31487
|
startMcpStdio,
|
|
30427
|
-
buildServer
|
|
31488
|
+
buildServer,
|
|
31489
|
+
assertSkillsMcpConfigured
|
|
30428
31490
|
};
|