@hasna/skills 0.10.10 → 0.10.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +240 -12
- package/bin/maintenance.js +224 -1044
- package/bin/mcp.js +1 -1
- package/bin/migrate.js +1 -1
- package/bin/server.js +7 -7
- package/bin/worker.js +7 -7
- package/dist/index.js +237 -8
- package/dist/lib/agent-codex.d.ts +7 -3
- package/dist/lib/agent-integration.d.ts +2 -0
- package/dist/lib/codex-hook-rpc.d.ts +1 -1
- package/dist/sdk/index.js +243 -14
- package/dist/server/enrollment-inspection.d.ts +26 -0
- package/dist/server/maintenance-entry.d.ts +13 -0
- package/dist/server/store.d.ts +9 -1
- package/package.json +1 -1
package/dist/sdk/index.js
CHANGED
|
@@ -32373,7 +32373,7 @@ var init_event_sink = __esm(() => {
|
|
|
32373
32373
|
// package.json
|
|
32374
32374
|
var package_default = {
|
|
32375
32375
|
name: "@hasna/skills",
|
|
32376
|
-
version: "0.10.
|
|
32376
|
+
version: "0.10.13",
|
|
32377
32377
|
description: "Skills library for AI coding agents",
|
|
32378
32378
|
type: "module",
|
|
32379
32379
|
bin: {
|
|
@@ -40984,6 +40984,196 @@ export default async ({ directory }) => {
|
|
|
40984
40984
|
// src/lib/agent-codex.ts
|
|
40985
40985
|
import { isDeepStrictEqual as isDeepStrictEqual4 } from "util";
|
|
40986
40986
|
var CODEX_SKILL_CONFIG_SECTIONS = /^\[\[skills\.config\]\][^\n]*(?:\n(?!\s*\[)[^\n]*)*/gm;
|
|
40987
|
+
var codexPathConfigRefusal = () => new Error("Codex skill path controls require ordinary [[skills.config]] TOML tables or a native single-line inline array; preserve and review unsupported inline, dotted, or malformed definitions before running skills hook install");
|
|
40988
|
+
function splitTopLevel(value, delimiter) {
|
|
40989
|
+
const result = [];
|
|
40990
|
+
let start = 0, square = 0, curly = 0, quote2 = null, escaped = false;
|
|
40991
|
+
for (let index = 0;index < value.length; index++) {
|
|
40992
|
+
const char = value[index];
|
|
40993
|
+
if (quote2 === "double") {
|
|
40994
|
+
if (escaped)
|
|
40995
|
+
escaped = false;
|
|
40996
|
+
else if (char === "\\")
|
|
40997
|
+
escaped = true;
|
|
40998
|
+
else if (char === '"')
|
|
40999
|
+
quote2 = null;
|
|
41000
|
+
continue;
|
|
41001
|
+
}
|
|
41002
|
+
if (quote2 === "literal") {
|
|
41003
|
+
if (char === "'")
|
|
41004
|
+
quote2 = null;
|
|
41005
|
+
continue;
|
|
41006
|
+
}
|
|
41007
|
+
if (char === '"') {
|
|
41008
|
+
quote2 = "double";
|
|
41009
|
+
continue;
|
|
41010
|
+
}
|
|
41011
|
+
if (char === "'") {
|
|
41012
|
+
quote2 = "literal";
|
|
41013
|
+
continue;
|
|
41014
|
+
}
|
|
41015
|
+
if (char === "[")
|
|
41016
|
+
square++;
|
|
41017
|
+
else if (char === "]") {
|
|
41018
|
+
if (--square < 0)
|
|
41019
|
+
return null;
|
|
41020
|
+
} else if (char === "{")
|
|
41021
|
+
curly++;
|
|
41022
|
+
else if (char === "}") {
|
|
41023
|
+
if (--curly < 0)
|
|
41024
|
+
return null;
|
|
41025
|
+
} else if (char === delimiter && square === 0 && curly === 0) {
|
|
41026
|
+
result.push(value.slice(start, index));
|
|
41027
|
+
start = index + 1;
|
|
41028
|
+
}
|
|
41029
|
+
}
|
|
41030
|
+
if (quote2 !== null || square !== 0 || curly !== 0)
|
|
41031
|
+
return null;
|
|
41032
|
+
result.push(value.slice(start));
|
|
41033
|
+
return result;
|
|
41034
|
+
}
|
|
41035
|
+
function inlineArrayEnd(value) {
|
|
41036
|
+
if (!value.startsWith("["))
|
|
41037
|
+
return null;
|
|
41038
|
+
let square = 0, curly = 0, quote2 = null, escaped = false;
|
|
41039
|
+
for (let index = 0;index < value.length; index++) {
|
|
41040
|
+
const char = value[index];
|
|
41041
|
+
if (quote2 === "double") {
|
|
41042
|
+
if (escaped)
|
|
41043
|
+
escaped = false;
|
|
41044
|
+
else if (char === "\\")
|
|
41045
|
+
escaped = true;
|
|
41046
|
+
else if (char === '"')
|
|
41047
|
+
quote2 = null;
|
|
41048
|
+
continue;
|
|
41049
|
+
}
|
|
41050
|
+
if (quote2 === "literal") {
|
|
41051
|
+
if (char === "'")
|
|
41052
|
+
quote2 = null;
|
|
41053
|
+
continue;
|
|
41054
|
+
}
|
|
41055
|
+
if (char === '"') {
|
|
41056
|
+
quote2 = "double";
|
|
41057
|
+
continue;
|
|
41058
|
+
}
|
|
41059
|
+
if (char === "'") {
|
|
41060
|
+
quote2 = "literal";
|
|
41061
|
+
continue;
|
|
41062
|
+
}
|
|
41063
|
+
if (char === "[")
|
|
41064
|
+
square++;
|
|
41065
|
+
else if (char === "]") {
|
|
41066
|
+
if (--square < 0)
|
|
41067
|
+
return null;
|
|
41068
|
+
if (square === 0 && curly === 0)
|
|
41069
|
+
return index;
|
|
41070
|
+
} else if (char === "{")
|
|
41071
|
+
curly++;
|
|
41072
|
+
else if (char === "}") {
|
|
41073
|
+
if (--curly < 0)
|
|
41074
|
+
return null;
|
|
41075
|
+
}
|
|
41076
|
+
}
|
|
41077
|
+
return null;
|
|
41078
|
+
}
|
|
41079
|
+
function inlineRowsAsTables(value, newline) {
|
|
41080
|
+
const end = inlineArrayEnd(value);
|
|
41081
|
+
if (end === null || value.slice(end + 1).trim() !== "" || value.includes(`
|
|
41082
|
+
`) || value.includes("\r"))
|
|
41083
|
+
return null;
|
|
41084
|
+
const elements = splitTopLevel(value.slice(1, end), ",");
|
|
41085
|
+
if (!elements)
|
|
41086
|
+
return null;
|
|
41087
|
+
const rows = elements.map((element) => element.trim()).filter(Boolean);
|
|
41088
|
+
if (rows.length === 0)
|
|
41089
|
+
return "";
|
|
41090
|
+
const sections = [];
|
|
41091
|
+
for (const row of rows) {
|
|
41092
|
+
if (!row.startsWith("{") || !row.endsWith("}"))
|
|
41093
|
+
return null;
|
|
41094
|
+
const fields = splitTopLevel(row.slice(1, -1), ",");
|
|
41095
|
+
if (!fields)
|
|
41096
|
+
return null;
|
|
41097
|
+
const lines = fields.map((field2) => field2.trim()).filter(Boolean);
|
|
41098
|
+
if (lines.some((line) => !/^(?:path|name|enabled)\s*=/.test(line)))
|
|
41099
|
+
return null;
|
|
41100
|
+
const keys2 = lines.map((line) => line.slice(0, line.indexOf("=")).trim());
|
|
41101
|
+
if (new Set(keys2).size !== keys2.length)
|
|
41102
|
+
return null;
|
|
41103
|
+
sections.push(`[[skills.config]]${newline}${lines.join(newline)}${newline}`);
|
|
41104
|
+
}
|
|
41105
|
+
return sections.join("");
|
|
41106
|
+
}
|
|
41107
|
+
function normalizeCodexInlinePathConfig(text2) {
|
|
41108
|
+
const before = Bun.TOML.parse(text2);
|
|
41109
|
+
if (!Array.isArray(before.skills?.config))
|
|
41110
|
+
return text2;
|
|
41111
|
+
if (before.skills.config.length === 0)
|
|
41112
|
+
throw codexPathConfigRefusal();
|
|
41113
|
+
const expectedWithoutConfig = structuredClone(before);
|
|
41114
|
+
delete expectedWithoutConfig.skills.config;
|
|
41115
|
+
try {
|
|
41116
|
+
if (isDeepStrictEqual4(Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, "")), expectedWithoutConfig))
|
|
41117
|
+
return text2;
|
|
41118
|
+
} catch {}
|
|
41119
|
+
const matches = [];
|
|
41120
|
+
for (const assignment of text2.matchAll(/^([ \t]*config[ \t]*=[ \t]*)(.*?)(\r?)$/gm)) {
|
|
41121
|
+
const rawValue = assignment[2].trimEnd();
|
|
41122
|
+
let quote2 = null, escaped = false, comment = "";
|
|
41123
|
+
for (let index = 0;index < rawValue.length; index++) {
|
|
41124
|
+
const char = rawValue[index];
|
|
41125
|
+
if (quote2 === "double") {
|
|
41126
|
+
if (escaped)
|
|
41127
|
+
escaped = false;
|
|
41128
|
+
else if (char === "\\")
|
|
41129
|
+
escaped = true;
|
|
41130
|
+
else if (char === '"')
|
|
41131
|
+
quote2 = null;
|
|
41132
|
+
continue;
|
|
41133
|
+
}
|
|
41134
|
+
if (quote2 === "literal") {
|
|
41135
|
+
if (char === "'")
|
|
41136
|
+
quote2 = null;
|
|
41137
|
+
continue;
|
|
41138
|
+
}
|
|
41139
|
+
if (char === '"')
|
|
41140
|
+
quote2 = "double";
|
|
41141
|
+
else if (char === "'")
|
|
41142
|
+
quote2 = "literal";
|
|
41143
|
+
else if (char === "#") {
|
|
41144
|
+
comment = rawValue.slice(index);
|
|
41145
|
+
break;
|
|
41146
|
+
}
|
|
41147
|
+
}
|
|
41148
|
+
const value = comment ? rawValue.slice(0, rawValue.length - comment.length).trimEnd() : rawValue;
|
|
41149
|
+
const tableSections = inlineRowsAsTables(value, text2.includes(`\r
|
|
41150
|
+
`) ? `\r
|
|
41151
|
+
` : `
|
|
41152
|
+
`);
|
|
41153
|
+
if (tableSections === null)
|
|
41154
|
+
continue;
|
|
41155
|
+
const start = assignment.index;
|
|
41156
|
+
const end = start + assignment[0].length;
|
|
41157
|
+
const withoutAssignment = text2.slice(0, start) + (comment ? comment : "") + assignment[3] + text2.slice(end);
|
|
41158
|
+
const separator = withoutAssignment.endsWith(`
|
|
41159
|
+
`) ? withoutAssignment.endsWith(`
|
|
41160
|
+
|
|
41161
|
+
`) ? "" : `
|
|
41162
|
+
` : `
|
|
41163
|
+
|
|
41164
|
+
`;
|
|
41165
|
+
const candidate2 = withoutAssignment + (tableSections ? separator + tableSections : "");
|
|
41166
|
+
try {
|
|
41167
|
+
const after = Bun.TOML.parse(candidate2);
|
|
41168
|
+
const expected = structuredClone(before);
|
|
41169
|
+
if (isDeepStrictEqual4(after, expected))
|
|
41170
|
+
matches.push(candidate2);
|
|
41171
|
+
} catch {}
|
|
41172
|
+
}
|
|
41173
|
+
if (matches.length !== 1)
|
|
41174
|
+
throw codexPathConfigRefusal();
|
|
41175
|
+
return matches[0];
|
|
41176
|
+
}
|
|
40987
41177
|
function assertCodexPathConfigEditable(text2) {
|
|
40988
41178
|
const before = Bun.TOML.parse(text2);
|
|
40989
41179
|
if (before.skills?.config === undefined)
|
|
@@ -40994,8 +41184,12 @@ function assertCodexPathConfigEditable(text2) {
|
|
|
40994
41184
|
const withoutSections = Bun.TOML.parse(text2.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
40995
41185
|
if (isDeepStrictEqual4(withoutSections, expected))
|
|
40996
41186
|
return;
|
|
41187
|
+
const normalized = normalizeCodexInlinePathConfig(text2);
|
|
41188
|
+
const withoutNormalizedSections = Bun.TOML.parse(normalized.replace(CODEX_SKILL_CONFIG_SECTIONS, ""));
|
|
41189
|
+
if (isDeepStrictEqual4(withoutNormalizedSections, expected))
|
|
41190
|
+
return;
|
|
40997
41191
|
} catch {}
|
|
40998
|
-
throw
|
|
41192
|
+
throw codexPathConfigRefusal();
|
|
40999
41193
|
}
|
|
41000
41194
|
function disableCodexBundledSkills(text2) {
|
|
41001
41195
|
const before = Bun.TOML.parse(text2);
|
|
@@ -41100,6 +41294,33 @@ function canonicalAgentPath(path, aliases) {
|
|
|
41100
41294
|
const absolute = resolve14(path), binding = aliases.find((item) => absolute === item.alias || absolute.startsWith(item.alias + sep4));
|
|
41101
41295
|
return binding ? join21(binding.target, relative3(binding.alias, absolute)) : absolute;
|
|
41102
41296
|
}
|
|
41297
|
+
function disabledCodexSkillPaths(config, aliases) {
|
|
41298
|
+
const skills = config.skills;
|
|
41299
|
+
if (!skills || typeof skills !== "object" || Array.isArray(skills))
|
|
41300
|
+
return [];
|
|
41301
|
+
const rows = skills.config;
|
|
41302
|
+
if (!Array.isArray(rows))
|
|
41303
|
+
return [];
|
|
41304
|
+
if (rows.some((entry) => !entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.enabled !== "boolean" || entry.path !== undefined && (typeof entry.path !== "string" || !isAbsolute15(entry.path)) || entry.name !== undefined && typeof entry.name !== "string"))
|
|
41305
|
+
return [];
|
|
41306
|
+
if (rows.some((entry) => entry.name !== undefined))
|
|
41307
|
+
return [];
|
|
41308
|
+
const settings = new Map;
|
|
41309
|
+
for (const entry of rows) {
|
|
41310
|
+
if (typeof entry.path !== "string" || !isAbsolute15(entry.path))
|
|
41311
|
+
continue;
|
|
41312
|
+
let document2;
|
|
41313
|
+
try {
|
|
41314
|
+
document2 = realpathSync4(canonicalAgentPath(entry.path, aliases));
|
|
41315
|
+
} catch {
|
|
41316
|
+
continue;
|
|
41317
|
+
}
|
|
41318
|
+
if (!document2.endsWith(`${sep4}SKILL.md`))
|
|
41319
|
+
continue;
|
|
41320
|
+
settings.set(document2, [...settings.get(document2) ?? [], entry.enabled === false]);
|
|
41321
|
+
}
|
|
41322
|
+
return [...settings].filter(([, values]) => values.length === 1 && values[0] === true).map(([document2]) => dirname14(document2));
|
|
41323
|
+
}
|
|
41103
41324
|
function recheckRootAliases(aliases) {
|
|
41104
41325
|
for (const binding of aliases) {
|
|
41105
41326
|
if (!["claude", "codex"].includes(binding.agent) || binding.alias !== join21(binding.home, `.${binding.agent}`))
|
|
@@ -41209,6 +41430,9 @@ function projectAncestorDirectories(projects) {
|
|
|
41209
41430
|
function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
41210
41431
|
if (options.reviewedCacheAlias !== undefined && (!options.includeVendor || !isAbsolute15(options.reviewedCacheAlias) || resolve14(options.reviewedCacheAlias) !== options.reviewedCacheAlias))
|
|
41211
41432
|
throw new Error("A reviewed cache alias requires vendor inventory and an exact absolute path");
|
|
41433
|
+
if (options.disabledVendorPaths?.some((path) => !isAbsolute15(path) || resolve14(path) !== path))
|
|
41434
|
+
throw new Error("Disabled vendor paths must be exact absolute paths");
|
|
41435
|
+
const disabledVendorPaths = new Set(options.disabledVendorPaths ?? []);
|
|
41212
41436
|
const aliases = rootAliases(home, options.allowRootAliases);
|
|
41213
41437
|
const selectedAgents = options.agents ? new Set(options.agents) : undefined;
|
|
41214
41438
|
const includesAgent = (agent) => !selectedAgents || selectedAgents.has(agent);
|
|
@@ -41230,7 +41454,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
41230
41454
|
throw new Error("Native skill discovery metadata limit exceeded");
|
|
41231
41455
|
}
|
|
41232
41456
|
let reviewedCacheAliasFound = false;
|
|
41233
|
-
function verifiedSiblingCacheAlias(path, parent) {
|
|
41457
|
+
function verifiedSiblingCacheAlias(agent, path, parent) {
|
|
41234
41458
|
const link = readlinkSync3(path), target = resolve14(parent, link);
|
|
41235
41459
|
if (dirname14(target) !== parent || (isAbsolute15(link) ? link !== target : dirname14(link) !== "."))
|
|
41236
41460
|
return false;
|
|
@@ -41240,9 +41464,13 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
41240
41464
|
if (!scan?.complete || scan.entries > maxAliasProofEntries)
|
|
41241
41465
|
return false;
|
|
41242
41466
|
if (scan.hasSkills) {
|
|
41467
|
+
const targetSkills = entries.filter((entry) => entry.agent === agent && entry.vendor && (entry.path === target || entry.path.startsWith(target + sep4)));
|
|
41468
|
+
if (path === options.reviewedCacheAlias && targetSkills.length > 0)
|
|
41469
|
+
reviewedCacheAliasFound = true;
|
|
41470
|
+
if (agent === "codex" && targetSkills.length > 0 && targetSkills.every((entry) => disabledVendorPaths.has(entry.path)))
|
|
41471
|
+
return true;
|
|
41243
41472
|
if (path !== options.reviewedCacheAlias)
|
|
41244
41473
|
return false;
|
|
41245
|
-
reviewedCacheAliasFound = true;
|
|
41246
41474
|
}
|
|
41247
41475
|
return true;
|
|
41248
41476
|
}
|
|
@@ -41301,7 +41529,7 @@ function inventoryNativeSkills(home = homedir3(), options = {}) {
|
|
|
41301
41529
|
const child = join21(path, name);
|
|
41302
41530
|
if (isVendor && lstatSync18(child, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
41303
41531
|
const target = statSync5(child, { throwIfNoEntry: false });
|
|
41304
|
-
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(child, path)) {
|
|
41532
|
+
if (target?.isFile() || pluginCache && target?.isDirectory() && verifiedSiblingCacheAlias(agent, child, path)) {
|
|
41305
41533
|
scan.entries++;
|
|
41306
41534
|
continue;
|
|
41307
41535
|
}
|
|
@@ -41441,7 +41669,7 @@ function configureHooks(config, agent, command, profileId, nativeReady) {
|
|
|
41441
41669
|
}
|
|
41442
41670
|
function disableCodexSkills(text2, skills, aliases, bridgePath) {
|
|
41443
41671
|
const bundled = disableCodexBundledSkills(text2);
|
|
41444
|
-
let result = bundled;
|
|
41672
|
+
let result = normalizeCodexInlinePathConfig(bundled);
|
|
41445
41673
|
const bridgeSelector = (entry) => typeof entry.name === "string" && entry.name.trim() === CLI_BRIDGE_NAME || typeof entry.path === "string" && [resolve14(bridgePath), resolve14(join21(bridgePath, "SKILL.md"))].includes(canonicalAgentPath(entry.path, aliases));
|
|
41446
41674
|
const previousConfig = Bun.TOML.parse(bundled).skills?.config;
|
|
41447
41675
|
if (previousConfig !== undefined && (!Array.isArray(previousConfig) || previousConfig.some((entry) => !entry || typeof entry !== "object" || entry.path !== undefined && entry.name !== undefined)))
|
|
@@ -41509,7 +41737,8 @@ function planAgentIntegration(options) {
|
|
|
41509
41737
|
}
|
|
41510
41738
|
return resolveAgentDiscovery({ home, agent, reviewed: options.discoveryInputs, retainedReview, canonical: (path) => canonicalAgentPath(path, aliases) });
|
|
41511
41739
|
});
|
|
41512
|
-
const
|
|
41740
|
+
const codexConfig = options.agents.includes("codex") ? Bun.TOML.parse(readOptional(canonicalAgentPath(join21(home, ".codex", "config.toml"), aliases)) ?? "") : {};
|
|
41741
|
+
const nativeSkills = inventoryNativeSkills(home, { includeVendor: true, guardHermes: options.agents.includes("hermes"), agents: options.agents, projectDir: options.projectDir, agentRoots: discoveries.flatMap((binding) => binding.roots.map((path) => ({ agent: binding.agent, path }))), allowRootAliases: options.allowRootAliases, reviewedCacheAlias: options.reviewedCacheAlias, disabledVendorPaths: disabledCodexSkillPaths(codexConfig, aliases) });
|
|
41513
41742
|
const changes = [];
|
|
41514
41743
|
for (const agent of [...new Set(options.agents)]) {
|
|
41515
41744
|
if (!INTEGRATION_AGENTS.includes(agent))
|
|
@@ -42031,7 +42260,7 @@ function assertManagedAgentBridge(agent, options = {}) {
|
|
|
42031
42260
|
} catch (error) {
|
|
42032
42261
|
throw new Error(`NATIVE_SKILL_DRIFT: ${error.message}`);
|
|
42033
42262
|
}
|
|
42034
|
-
const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0 });
|
|
42263
|
+
const inventory = inventoryNativeSkills(home, { includeVendor: true, guardHermes: agent === "hermes", agents: [agent], projectDirs: [...roots], agentRoots: discovery.roots.map((path) => ({ agent, path })), allowRootAliases: aliases.length > 0, disabledVendorPaths: agent === "codex" ? disabledCodexSkillPaths(codexConfig, aliases) : [] });
|
|
42035
42264
|
const unexpected = inventory.filter((entry) => visible(entry) && !entry.bridge && !disabledVendorSkill(entry));
|
|
42036
42265
|
if (unexpected.length) {
|
|
42037
42266
|
const paths = unexpected.slice(0, 3).map((entry) => {
|
|
@@ -63971,9 +64200,9 @@ class PostgresSkillsStore {
|
|
|
63971
64200
|
}
|
|
63972
64201
|
backend = { kind: "postgres", durable: true, label: "postgres" };
|
|
63973
64202
|
sql;
|
|
63974
|
-
constructor(databaseUrl) {
|
|
64203
|
+
constructor(databaseUrl, connectionOptions = {}) {
|
|
63975
64204
|
const bunWithSql = Bun;
|
|
63976
|
-
this.sql = new bunWithSql.SQL(databaseUrl, { max: resolvePoolMax() });
|
|
64205
|
+
this.sql = new bunWithSql.SQL(databaseUrl, { max: resolvePoolMax(), ...connectionOptions });
|
|
63977
64206
|
}
|
|
63978
64207
|
async verifyConnectivity() {
|
|
63979
64208
|
try {
|
|
@@ -64068,7 +64297,7 @@ class PostgresSkillsStore {
|
|
|
64068
64297
|
}
|
|
64069
64298
|
await tx`
|
|
64070
64299
|
INSERT INTO skills_audit_events (org_id, user_id, api_key_id, action, target_type, target_id, metadata_json)
|
|
64071
|
-
VALUES (${actor.orgId}, ${actor.userId}, ${actor.apiKeyId}, ${"api_key_scopes_added"}, ${"api_key"}, ${keyId}, ${JSON.stringify({ added: addScopes, scopes: parseJsonArray(updated[0].scopes_json) })}::jsonb)
|
|
64300
|
+
VALUES (${actor.orgId}, ${actor.userId}, ${actor.apiKeyId}, ${"api_key_scopes_added"}, ${"api_key"}, ${keyId}, ${JSON.stringify({ added: addScopes, scopes: parseJsonArray(updated[0].scopes_json) })}::text::jsonb)
|
|
64072
64301
|
`;
|
|
64073
64302
|
return { kind: "updated", scopes: parseJsonArray(updated[0].scopes_json) };
|
|
64074
64303
|
});
|
|
@@ -64077,9 +64306,9 @@ class PostgresSkillsStore {
|
|
|
64077
64306
|
const current = expectedScopes;
|
|
64078
64307
|
const scopes = [...current, ...addScopes.filter((scope2) => !current.includes(scope2))];
|
|
64079
64308
|
return tx`
|
|
64080
|
-
UPDATE api_keys SET scopes_json = ${JSON.stringify(scopes)}::jsonb
|
|
64309
|
+
UPDATE api_keys SET scopes_json = ${JSON.stringify(scopes)}::text::jsonb
|
|
64081
64310
|
WHERE id = ${keyId} AND org_id = ${orgId} AND revoked_at IS NULL
|
|
64082
|
-
AND scopes_json = ${JSON.stringify(expectedScopes)}::jsonb
|
|
64311
|
+
AND scopes_json = ${JSON.stringify(expectedScopes)}::text::jsonb
|
|
64083
64312
|
RETURNING scopes_json
|
|
64084
64313
|
`;
|
|
64085
64314
|
}
|
|
@@ -64139,7 +64368,7 @@ class PostgresSkillsStore {
|
|
|
64139
64368
|
operator_task_arn: input.operatorTaskArn,
|
|
64140
64369
|
target_manifest_digest: input.manifestDigest,
|
|
64141
64370
|
station_id: input.stationId
|
|
64142
|
-
})}::jsonb)
|
|
64371
|
+
})}::text::jsonb)
|
|
64143
64372
|
`;
|
|
64144
64373
|
return { kind: "updated", scopes };
|
|
64145
64374
|
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface EnrollmentInspectionInput {
|
|
2
|
+
keyId: string;
|
|
3
|
+
orgId: string;
|
|
4
|
+
enrollmentOperationId: string;
|
|
5
|
+
enrollmentManifestDigest: string;
|
|
6
|
+
}
|
|
7
|
+
export interface EnrollmentInspectionResult {
|
|
8
|
+
status: "inspected";
|
|
9
|
+
target: {
|
|
10
|
+
state: "found";
|
|
11
|
+
scopes: string[];
|
|
12
|
+
} | {
|
|
13
|
+
state: "absent" | "revoked" | "mismatched" | "ambiguous";
|
|
14
|
+
};
|
|
15
|
+
audit: {
|
|
16
|
+
state: "absent" | "matching" | "mismatched" | "ambiguous";
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
type InspectionCode = "INVALID_INSPECTION_INPUT" | "POSTGRES_REQUIRED" | "INVALID_INSPECTION_RESULT" | "INSPECTION_FAILED" | "INSPECTION_SCHEMA_UNAVAILABLE" | "INSPECTION_ACCESS_DENIED" | "INSPECTION_WRITE_REFUSED" | "INSPECTION_TIMEOUT" | "INSPECTION_CONNECTION_FAILED";
|
|
20
|
+
export declare class EnrollmentInspectionError extends Error {
|
|
21
|
+
readonly code: InspectionCode;
|
|
22
|
+
constructor(code: InspectionCode);
|
|
23
|
+
}
|
|
24
|
+
/** Inspect metadata only. Deliberately does not import or initialize the application store. */
|
|
25
|
+
export declare function inspectEnrollment(databaseUrl: string, input: EnrollmentInspectionInput): Promise<EnrollmentInspectionResult>;
|
|
26
|
+
export {};
|
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
import { type EnrollmentInspectionInput } from "./enrollment-inspection.js";
|
|
3
|
+
type InspectionManifest = EnrollmentInspectionInput & {
|
|
4
|
+
app: "skills";
|
|
5
|
+
operation: "inspect-enrollment";
|
|
6
|
+
manifestVersion: 1;
|
|
7
|
+
operationId: string;
|
|
8
|
+
stationId: string;
|
|
9
|
+
expiresAt: string;
|
|
10
|
+
manifestDigest: string;
|
|
11
|
+
};
|
|
12
|
+
/** Inspection has its own authorization; a failed enrollment manifest cannot authorize it. */
|
|
13
|
+
export declare function readInspectionManifest(path: string, receiptPath: string): InspectionManifest;
|
|
2
14
|
export declare function registerMaintenance(parent: Command): void;
|
|
15
|
+
export {};
|
package/dist/server/store.d.ts
CHANGED
|
@@ -25,6 +25,9 @@ export interface StoreOptions {
|
|
|
25
25
|
* - anything else -> throws, naming what is supported
|
|
26
26
|
*/
|
|
27
27
|
export declare function createStore(options?: StoreOptions): Promise<SkillsProductStore>;
|
|
28
|
+
export type OperatorScopeMaintenanceStore = Required<Pick<SkillsProductStore, "inspectOperatorScopeTarget" | "enrollPublishScopeByOperator" | "close">>;
|
|
29
|
+
/** Open only the existing scope-maintenance surface; never initialize or backfill the application store. */
|
|
30
|
+
export declare function openOperatorScopeMaintenance(databaseUrl: string): OperatorScopeMaintenanceStore;
|
|
28
31
|
export declare class MemorySkillsStore implements SkillsProductStore {
|
|
29
32
|
readonly selectionStore: MemorySkillSelectionStore;
|
|
30
33
|
readonly executionGrantStore: MemoryExecutionGrantStore;
|
|
@@ -102,7 +105,12 @@ export declare class PostgresSkillsStore implements SkillsProductStore {
|
|
|
102
105
|
get executionGrantStore(): PostgresExecutionGrantStore;
|
|
103
106
|
readonly backend: StoreBackendInfo;
|
|
104
107
|
private sql;
|
|
105
|
-
constructor(databaseUrl: string
|
|
108
|
+
constructor(databaseUrl: string, connectionOptions?: {
|
|
109
|
+
max?: number;
|
|
110
|
+
connectionTimeout?: number;
|
|
111
|
+
idleTimeout?: number;
|
|
112
|
+
connection?: Record<string, string>;
|
|
113
|
+
});
|
|
106
114
|
/**
|
|
107
115
|
* Bun's SQL client connects lazily, so constructing this store proves nothing about
|
|
108
116
|
* the database being there. Without an explicit probe, a wrong host or a down
|