openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
170 lines (169 loc) • 8.62 kB
JavaScript
import { i as openOpenClawStateDatabase } from "./openclaw-state-db-BRTnL-D8.js";
import { g as registerAgentRunDelegatedAuthorityClosedHandler } from "./agent-run-registry-CKYKdfNd.js";
import { s as isOperatorUiClient } from "./message-channel-BQrhwUEA.js";
import { o as selectResolvedUserProfileById } from "./user-profiles-internal-D0HRUN8X.js";
import { i as getAdmittedRunDelegatedAuthority } from "./admitted-run-context-CHY5SdVF.js";
import { r as getGatewayToolCallerIdentity } from "./gateway-caller-context-BxVUFvkR.js";
import { M as SkillLibraryError, a as seedSkillLibrarySelection, h as resolveSkillLibraryActor, j as validateSkillLibraryPath } from "./selection-NYtFFG08.js";
import { a as mutateSkillLibrary, c as saveSkillLibrary, i as listSkillLibrary, n as libraryAuthority, o as readSkillLibrary, s as resolveSkillLibraryPresentation, t as activateLibrarySelection } from "./skills-library-BzfO7efV.js";
//#region src/skills/library/authoring.ts
/** Model edits are named changes; unread binary resources retain their exact bytes and modes. */
function mergeSkillLibrarySupportFiles(current, upserts = [], deletes = []) {
const changed = /* @__PURE__ */ new Set();
for (const filePath of [...upserts.map((file) => file.path), ...deletes]) {
validateSkillLibraryPath(filePath);
const folded = filePath.toLowerCase();
if (folded === "skill.md" || changed.has(folded)) throw new SkillLibraryError("INVALID_BUNDLE", "Support edits cannot include SKILL.md, duplicate paths, or conflicting upserts and deletes. Use proposal_content for SKILL.md.");
changed.add(folded);
}
const files = new Map(current.map((file) => [file.path, file]));
for (const filePath of deletes) if (!files.delete(filePath)) throw new SkillLibraryError("INVALID_BUNDLE", `Support file not found: ${filePath}`);
for (const file of upserts) files.set(file.path, {
...file,
executable: file.executable ?? files.get(file.path)?.executable
});
return [...files.values()];
}
//#endregion
//#region src/gateway/skill-library-authoring.ts
const active = /* @__PURE__ */ new Map();
/** A mixed-person steer revokes mutation authority without changing the session's committed pins. */
function invalidateSkillAuthoringForOtherRequester(sessionKey, profileId) {
for (const grant of active.get(sessionKey) ?? []) if (grant.profileId !== profileId) {
const db = openOpenClawStateDatabase().db;
if (!profileId || selectResolvedUserProfileById(db, grant.profileId)?.id !== selectResolvedUserProfileById(db, profileId)?.id) grant.revoke();
}
}
/** Only ordinary attributed human ingress may mint a namespace; actions remain normal tool policy. */
function prepareGatewaySkillAuthoring(options, sessionKey, isHumanTurn) {
const client = options.client;
if (!isHumanTurn || !client?.authenticatedUserProfile || client.internal?.syntheticClient || client.internal?.agentRuntimeIdentity || client.internal?.senderAttribution || client.internal?.pluginRuntimeOwnerId || client.internal?.approvalRuntime || client.internal?.cronRunContinuation || client.internal?.delegatedToolPolicyHandoffId) return;
const authority = libraryAuthority(options);
const library = resolveSkillLibraryPresentation(authority);
if (!library.profileId || library.defaultTarget === "unavailable") return;
const profileId = library.profileId;
invalidateSkillAuthoringForOtherRequester(sessionKey, profileId);
let bound;
let revoked = false;
let owner;
const assertCurrent = () => {
authority.assertCurrent();
const caller = getGatewayToolCallerIdentity();
if (revoked || !bound || !owner || getAdmittedRunDelegatedAuthority(bound) !== owner || caller?.operationalRunInstance !== bound.operationalRunInstance || !caller.receiptAuthority || caller.receiptAuthority() === false) throw new SkillLibraryError("AUTHORITY_EXPIRED", "Personal authoring authority expired or this turn has mixed requesters. Send a fresh attributed message requesting the change.");
};
const currentAuthority = {
...authority,
assertCurrent,
namespace: "personal"
};
return {
target: "personal",
defaultTarget: library.defaultTarget,
multipleProfiles: library.multipleProfiles,
bind(context) {
if (bound) {
if (context !== bound) throw new SkillLibraryError("AUTHORITY_EXPIRED", "Personal authoring cannot move to a replacement run. Send a fresh message.");
return;
}
bound = context;
owner = getAdmittedRunDelegatedAuthority(context);
if (!owner) throw new SkillLibraryError("AUTHORITY_EXPIRED", "Personal authoring run was not admitted.");
const grant = {
profileId,
revoke: () => {
revoked = true;
}
};
const grants = active.get(sessionKey) ?? /* @__PURE__ */ new Set();
grants.add(grant);
active.set(sessionKey, grants);
const stop = registerAgentRunDelegatedAuthorityClosedHandler((closed) => {
if (closed !== owner) return;
revoked = true;
grants.delete(grant);
if (!grants.size) active.delete(sessionKey);
stop();
});
},
assertWorkspaceCurrent() {
assertCurrent();
if (!resolveSkillLibraryActor(openOpenClawStateDatabase().db, authority).admin) throw new SkillLibraryError("FORBIDDEN", "Workspace authoring requires current administrator authority.");
},
async invoke(input) {
assertCurrent();
if (input.action === "list") return listSkillLibrary(currentAuthority);
if (input.action === "read") {
if (!input.skillId) throw new SkillLibraryError("INVALID_BUNDLE", "Choose skill_id from list.");
return readSkillLibrary(currentAuthority, input.skillId, input.revision);
}
if (input.action === "activate") {
if (!input.skillId) throw new SkillLibraryError("INVALID_BUNDLE", "Choose skill_id from list.");
return activateLibrarySelection({
...options,
sessionMutationCommitGuard: assertCurrent
}, {
sessionKey,
action: "attach",
skillId: input.skillId,
revision: input.revision
});
}
if (input.action !== "create" && (!input.skillId || !input.expectedRevision)) throw new SkillLibraryError("INVALID_BUNDLE", "Read the skill first; supply skill_id and expected_revision from that read.");
if (input.action === "create" || input.action === "update") {
const current = input.action === "update" ? await readSkillLibrary(currentAuthority, input.skillId, input.expectedRevision) : void 0;
const slug = input.slug ?? current?.entry.slug;
const content = input.content ?? current?.content;
if (!slug || !content) throw new SkillLibraryError("INVALID_BUNDLE", "Creating a skill requires name (the human slug) and complete proposal_content.");
return saveSkillLibrary(currentAuthority, {
slug,
content,
files: mergeSkillLibrarySupportFiles(current?.files ?? [], input.files, input.deleteFiles),
skillId: input.action === "update" ? input.skillId : void 0,
expectedRevision: input.action === "create" ? null : input.expectedRevision
});
}
return mutateSkillLibrary(currentAuthority, {
action: input.action,
skillId: input.skillId,
expectedRevision: input.expectedRevision,
revision: input.revision
});
}
};
}
//#endregion
//#region src/gateway/session-input-participant.ts
/** Only the live authenticated profile establishes a person; client metadata stays unresolved. */
function resolveGatewayInputParticipant(client, provenance) {
if (!client || client.internal?.syntheticClient || provenance && provenance.kind !== "external_user") return;
const profileId = client.authenticatedUserProfile?.profileId;
if (profileId) return {
type: "profile",
id: profileId
};
const clientInfo = client.connect?.client;
return !clientInfo || isOperatorUiClient(clientInfo) ? void 0 : {
type: "observation",
pluginId: null,
accountId: null,
senderKind: "unknown",
id: clientInfo.id
};
}
//#endregion
//#region src/gateway/skill-library-session.ts
/** Selection is prepared from this request's real principal, never reconstructed from provenance. */
function prepareSkillLibrarySessionCreation(client, cfg, creation) {
if (!client?.authenticatedUserProfile || client.internal?.syntheticClient || creation.via === "spawn") return creation;
return {
...creation,
skillLibrarySelections: seedSkillLibrarySelection({
profileId: client.authenticatedUserProfile.profileId,
scopes: client.connect.scopes ?? [],
getConfig: typeof cfg === "function" ? cfg : () => cfg,
assertCurrent: () => {}
})
};
}
//#endregion
export { prepareGatewaySkillAuthoring as i, resolveGatewayInputParticipant as n, invalidateSkillAuthoringForOtherRequester as r, prepareSkillLibrarySessionCreation as t };