@hasna/skills 0.9.1 → 0.9.3

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.
@@ -69,6 +69,47 @@ export type ApiKeyScopeUpdateResult = {
69
69
  kind: "stale";
70
70
  scopes: string[];
71
71
  };
72
+ /**
73
+ * A maintenance task's provenance is deliberately separate from ApiPrincipal.
74
+ * An ECS task is not a user and must never be represented by impersonating an
75
+ * API key, user id, or role in the audit row.
76
+ */
77
+ export interface OperatorScopeEnrollmentInput {
78
+ keyId: string;
79
+ stationId: string;
80
+ orgId: string;
81
+ expectedScopes: string[];
82
+ operationId: string;
83
+ manifestDigest: string;
84
+ operatorJobId: string;
85
+ operatorTaskArn: string;
86
+ }
87
+ export declare function validOperatorScopeEnrollmentInput(input: OperatorScopeEnrollmentInput): boolean;
88
+ export declare function validOperatorScopeList(scopes: unknown): scopes is string[];
89
+ export type OperatorScopeEnrollmentResult = {
90
+ kind: "updated";
91
+ scopes: string[];
92
+ } | {
93
+ kind: "already_applied";
94
+ scopes: string[];
95
+ } | {
96
+ kind: "not_found";
97
+ } | {
98
+ kind: "target_mismatch";
99
+ } | {
100
+ kind: "invalid";
101
+ } | {
102
+ kind: "stale";
103
+ scopes: string[];
104
+ };
105
+ export type OperatorScopeTargetSnapshot = {
106
+ kind: "not_found";
107
+ } | {
108
+ kind: "target_mismatch";
109
+ } | {
110
+ kind: "found";
111
+ scopes: string[];
112
+ };
72
113
  export interface ServerRunRecord {
73
114
  id: string;
74
115
  orgId: string;
@@ -339,6 +380,9 @@ export interface SkillsProductStore {
339
380
  authenticateApiKeyHash(hash: string): Promise<ApiPrincipal | null>;
340
381
  /** Guarded metadata-only scope admission for an existing key. */
341
382
  updateApiKeyScopes?(actor: ApiPrincipal, keyId: string, expectedScopes: string[], addScopes: string[]): Promise<ApiKeyScopeUpdateResult>;
383
+ /** Add only skills:publish to an existing key from a protected maintenance task. */
384
+ enrollPublishScopeByOperator?(input: OperatorScopeEnrollmentInput): Promise<OperatorScopeEnrollmentResult>;
385
+ inspectOperatorScopeTarget?(keyId: string, orgId: string): Promise<OperatorScopeTargetSnapshot>;
342
386
  ensureBootstrapApiKey?(token: string, principal?: Partial<ApiPrincipal>): Promise<void>;
343
387
  createRun(input: CreateRunInput): Promise<ServerRunRecord>;
344
388
  listRuns(principal: ApiPrincipal, limit: number): Promise<ServerRunRecord[]>;
@@ -0,0 +1,9 @@
1
+ ALTER TABLE skills_audit_events ADD COLUMN operator_operation_id text;
2
+
3
+ -- One operation may be retried, but it must produce one immutable receipt per
4
+ -- tenant/key. The partial expression index makes that property atomic under
5
+ -- concurrent maintenance tasks; application pre-reads are not the guard.
6
+ CREATE UNIQUE INDEX IF NOT EXISTS skills_operator_scope_receipt_idx
7
+ ON skills_audit_events (operator_operation_id)
8
+ WHERE action = 'api_key_scopes_added'
9
+ AND operator_operation_id IS NOT NULL;
@@ -0,0 +1,7 @@
1
+ ALTER TABLE skills_audit_events ADD COLUMN operator_operation_id text;
2
+
3
+ -- SQLite equivalent of the PostgreSQL operation-receipt uniqueness fence.
4
+ CREATE UNIQUE INDEX IF NOT EXISTS skills_operator_scope_receipt_idx
5
+ ON skills_audit_events (operator_operation_id)
6
+ WHERE action = 'api_key_scopes_added'
7
+ AND operator_operation_id IS NOT NULL;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/skills",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "Skills library for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,6 +9,7 @@
9
9
  "skills-serve": "bin/server.js",
10
10
  "skills-server": "bin/server.js",
11
11
  "skills-worker": "bin/worker.js",
12
+ "skills-maintenance": "bin/maintenance.js",
12
13
  "skills-migrate": "bin/migrate.js"
13
14
  },
14
15
  "exports": {
@@ -49,7 +50,7 @@
49
50
  "types": "./dist/index.d.ts",
50
51
  "scripts": {
51
52
  "clean": "rm -rf bin/ dist/",
52
- "build": "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
53
+ "build": "bun run clean && bun build ./src/cli/index.tsx --outdir ./bin --target bun && bun build ./src/mcp/index.ts --outfile ./bin/mcp.js --target bun && bun build ./src/server/index.ts --outfile ./bin/server.js --target bun && bun build ./src/server/worker.ts --outfile ./bin/worker.js --target bun && bun build ./src/server/migrate.ts --outfile ./bin/migrate.js --target bun && bun build ./src/server/maintenance.ts --outfile ./bin/maintenance.js --target bun && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
53
54
  "build:js": "rm -rf dist && bun build ./src/index.ts ./src/storage.ts ./src/sdk/index.ts ./src/admin-contract.ts --outdir ./dist --target bun && tsc --emitDeclarationOnly --declaration --outDir dist",
54
55
  "test": "bun test --timeout 30000",
55
56
  "dev": "bun run ./src/cli/index.tsx",