@zauso-ai/capstan-auth 0.1.2
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/dist/api-key.d.ts +27 -0
- package/dist/api-key.d.ts.map +1 -0
- package/dist/api-key.js +60 -0
- package/dist/api-key.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +14 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +86 -0
- package/dist/middleware.js.map +1 -0
- package/dist/permissions.d.ts +37 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +52 -0
- package/dist/permissions.js.map +1 -0
- package/dist/session.d.ts +15 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +95 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a new API key suitable for agent authentication.
|
|
3
|
+
*
|
|
4
|
+
* Returns:
|
|
5
|
+
* - `key` — the full plaintext key (show once to the user, never store)
|
|
6
|
+
* - `hash` — SHA-256 hex digest of the key (store in the database)
|
|
7
|
+
* - `prefix` — short prefix of the key for fast DB look-up
|
|
8
|
+
*/
|
|
9
|
+
export declare function generateApiKey(prefix?: string): {
|
|
10
|
+
key: string;
|
|
11
|
+
hash: string;
|
|
12
|
+
prefix: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Verify a plaintext API key against a stored SHA-256 hash.
|
|
16
|
+
*
|
|
17
|
+
* Uses timing-safe comparison to prevent timing side-channel attacks.
|
|
18
|
+
*/
|
|
19
|
+
export declare function verifyApiKey(key: string, storedHash: string): Promise<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* Extract the lookup prefix from a full plaintext API key.
|
|
22
|
+
*
|
|
23
|
+
* This prefix can be used to find the matching credential row in the
|
|
24
|
+
* database without hashing first (the hash is checked afterwards).
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractApiKeyPrefix(key: string): string;
|
|
27
|
+
//# sourceMappingURL=api-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key.d.ts","sourceRoot":"","sources":["../src/api-key.ts"],"names":[],"mappings":"AAsBA;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAYA;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CASlB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CASvD"}
|
package/dist/api-key.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { randomBytes, createHash, timingSafeEqual, } from "node:crypto";
|
|
2
|
+
const DEFAULT_PREFIX = "cap_ak_";
|
|
3
|
+
/**
|
|
4
|
+
* The number of random hex characters appended after the prefix.
|
|
5
|
+
* 32 hex chars = 16 bytes = 128 bits of entropy.
|
|
6
|
+
*/
|
|
7
|
+
const RANDOM_HEX_LENGTH = 32;
|
|
8
|
+
/**
|
|
9
|
+
* Number of characters from the full key used as a short "prefix" for
|
|
10
|
+
* database look-up (prefix of the *random* portion, after the key prefix).
|
|
11
|
+
*/
|
|
12
|
+
const LOOKUP_PREFIX_LENGTH = 8;
|
|
13
|
+
// ── Public API ─────────────────────────────────────────────────────
|
|
14
|
+
/**
|
|
15
|
+
* Generate a new API key suitable for agent authentication.
|
|
16
|
+
*
|
|
17
|
+
* Returns:
|
|
18
|
+
* - `key` — the full plaintext key (show once to the user, never store)
|
|
19
|
+
* - `hash` — SHA-256 hex digest of the key (store in the database)
|
|
20
|
+
* - `prefix` — short prefix of the key for fast DB look-up
|
|
21
|
+
*/
|
|
22
|
+
export function generateApiKey(prefix) {
|
|
23
|
+
const keyPrefix = prefix ?? DEFAULT_PREFIX;
|
|
24
|
+
const randomPart = randomBytes(RANDOM_HEX_LENGTH / 2).toString("hex");
|
|
25
|
+
const key = `${keyPrefix}${randomPart}`;
|
|
26
|
+
const hash = createHash("sha256").update(key).digest("hex");
|
|
27
|
+
// The lookup prefix is the key prefix + the first LOOKUP_PREFIX_LENGTH
|
|
28
|
+
// chars of the random portion, giving enough uniqueness for a DB index.
|
|
29
|
+
const lookupPrefix = `${keyPrefix}${randomPart.slice(0, LOOKUP_PREFIX_LENGTH)}`;
|
|
30
|
+
return { key, hash, prefix: lookupPrefix };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Verify a plaintext API key against a stored SHA-256 hash.
|
|
34
|
+
*
|
|
35
|
+
* Uses timing-safe comparison to prevent timing side-channel attacks.
|
|
36
|
+
*/
|
|
37
|
+
export async function verifyApiKey(key, storedHash) {
|
|
38
|
+
const candidateHash = createHash("sha256").update(key).digest("hex");
|
|
39
|
+
const a = Buffer.from(candidateHash, "utf-8");
|
|
40
|
+
const b = Buffer.from(storedHash, "utf-8");
|
|
41
|
+
if (a.length !== b.length)
|
|
42
|
+
return false;
|
|
43
|
+
return timingSafeEqual(a, b);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Extract the lookup prefix from a full plaintext API key.
|
|
47
|
+
*
|
|
48
|
+
* This prefix can be used to find the matching credential row in the
|
|
49
|
+
* database without hashing first (the hash is checked afterwards).
|
|
50
|
+
*/
|
|
51
|
+
export function extractApiKeyPrefix(key) {
|
|
52
|
+
// Detect the structural prefix (everything before the random hex).
|
|
53
|
+
// We look for the pattern: one or more segments ending with "_" followed
|
|
54
|
+
// by hex characters.
|
|
55
|
+
const structuralMatch = key.match(/^([a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*_)/);
|
|
56
|
+
const structuralPrefix = structuralMatch?.[1] ?? DEFAULT_PREFIX;
|
|
57
|
+
const randomPart = key.slice(structuralPrefix.length);
|
|
58
|
+
return `${structuralPrefix}${randomPart.slice(0, LOOKUP_PREFIX_LENGTH)}`;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=api-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key.js","sourceRoot":"","sources":["../src/api-key.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,UAAU,EACV,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,MAAM,cAAc,GAAG,SAAS,CAAC;AAEjC;;;GAGG;AACH,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B;;;GAGG;AACH,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAE/B,sEAAsE;AAEtE;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,MAAe;IAK5C,MAAM,SAAS,GAAG,MAAM,IAAI,cAAc,CAAC;IAC3C,MAAM,UAAU,GAAG,WAAW,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtE,MAAM,GAAG,GAAG,GAAG,SAAS,GAAG,UAAU,EAAE,CAAC;IAExC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE5D,uEAAuE;IACvE,wEAAwE;IACxE,MAAM,YAAY,GAAG,GAAG,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,EAAE,CAAC;IAEhF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,UAAkB;IAElB,MAAM,aAAa,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAErE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAE3C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAExC,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,mEAAmE;IACnE,yEAAyE;IACzE,qBAAqB;IACrB,MAAM,eAAe,GAAG,GAAG,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxE,MAAM,gBAAgB,GAAG,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC;IAChE,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAEtD,OAAO,GAAG,gBAAgB,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,EAAE,CAAC;AAC3E,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { signSession, verifySession } from "./session.js";
|
|
2
|
+
export { generateApiKey, verifyApiKey, extractApiKeyPrefix, } from "./api-key.js";
|
|
3
|
+
export { createAuthMiddleware } from "./middleware.js";
|
|
4
|
+
export { checkPermission, derivePermission } from "./permissions.js";
|
|
5
|
+
export type { AuthConfig, SessionPayload, AgentCredential, AuthContext, AuthResolverDeps, } from "./types.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EACL,cAAc,EACd,YAAY,EACZ,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,YAAY,EACV,UAAU,EACV,cAAc,EACd,eAAe,EACf,WAAW,EACX,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { signSession, verifySession } from "./session.js";
|
|
2
|
+
export { generateApiKey, verifyApiKey, extractApiKeyPrefix, } from "./api-key.js";
|
|
3
|
+
export { createAuthMiddleware } from "./middleware.js";
|
|
4
|
+
export { checkPermission, derivePermission } from "./permissions.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EACL,cAAc,EACd,YAAY,EACZ,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AuthConfig, AuthContext, AuthResolverDeps } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Create a middleware function that resolves an `AuthContext` from an
|
|
4
|
+
* incoming `Request`.
|
|
5
|
+
*
|
|
6
|
+
* Resolution order:
|
|
7
|
+
* 1. Session cookie (`capstan_session`) — verifies JWT, returns human context.
|
|
8
|
+
* 2. `Authorization: Bearer <token>` header — if the token matches the
|
|
9
|
+
* configured API key prefix, looks up the agent credential and verifies
|
|
10
|
+
* the key hash.
|
|
11
|
+
* 3. Falls back to `{ type: "anonymous", isAuthenticated: false }`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createAuthMiddleware(config: AuthConfig, deps: AuthResolverDeps): (request: Request) => Promise<AuthContext>;
|
|
14
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EACjB,MAAM,YAAY,CAAC;AA2BpB;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,gBAAgB,GACrB,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,WAAW,CAAC,CAuD5C"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { verifySession } from "./session.js";
|
|
2
|
+
import { verifyApiKey, extractApiKeyPrefix } from "./api-key.js";
|
|
3
|
+
const SESSION_COOKIE_NAME = "capstan_session";
|
|
4
|
+
const DEFAULT_API_KEY_PREFIX = "cap_ak_";
|
|
5
|
+
const ANONYMOUS_CONTEXT = {
|
|
6
|
+
isAuthenticated: false,
|
|
7
|
+
type: "anonymous",
|
|
8
|
+
};
|
|
9
|
+
// ── Cookie helpers ─────────────────────────────────────────────────
|
|
10
|
+
function parseCookies(header) {
|
|
11
|
+
const cookies = new Map();
|
|
12
|
+
for (const pair of header.split(";")) {
|
|
13
|
+
const eqIndex = pair.indexOf("=");
|
|
14
|
+
if (eqIndex === -1)
|
|
15
|
+
continue;
|
|
16
|
+
const name = pair.slice(0, eqIndex).trim();
|
|
17
|
+
const value = pair.slice(eqIndex + 1).trim();
|
|
18
|
+
cookies.set(name, value);
|
|
19
|
+
}
|
|
20
|
+
return cookies;
|
|
21
|
+
}
|
|
22
|
+
// ── Middleware factory ──────────────────────────────────────────────
|
|
23
|
+
/**
|
|
24
|
+
* Create a middleware function that resolves an `AuthContext` from an
|
|
25
|
+
* incoming `Request`.
|
|
26
|
+
*
|
|
27
|
+
* Resolution order:
|
|
28
|
+
* 1. Session cookie (`capstan_session`) — verifies JWT, returns human context.
|
|
29
|
+
* 2. `Authorization: Bearer <token>` header — if the token matches the
|
|
30
|
+
* configured API key prefix, looks up the agent credential and verifies
|
|
31
|
+
* the key hash.
|
|
32
|
+
* 3. Falls back to `{ type: "anonymous", isAuthenticated: false }`.
|
|
33
|
+
*/
|
|
34
|
+
export function createAuthMiddleware(config, deps) {
|
|
35
|
+
const apiKeyPrefix = config.apiKeys?.prefix ?? DEFAULT_API_KEY_PREFIX;
|
|
36
|
+
const authHeaderName = config.apiKeys?.headerName ?? "Authorization";
|
|
37
|
+
return async (request) => {
|
|
38
|
+
// ── 1. Session cookie ────────────────────────────────────────
|
|
39
|
+
const cookieHeader = request.headers.get("cookie");
|
|
40
|
+
if (cookieHeader) {
|
|
41
|
+
const cookies = parseCookies(cookieHeader);
|
|
42
|
+
const sessionToken = cookies.get(SESSION_COOKIE_NAME);
|
|
43
|
+
if (sessionToken) {
|
|
44
|
+
const payload = verifySession(sessionToken, config.session.secret);
|
|
45
|
+
if (payload) {
|
|
46
|
+
const ctx = {
|
|
47
|
+
isAuthenticated: true,
|
|
48
|
+
type: "human",
|
|
49
|
+
userId: payload.userId,
|
|
50
|
+
};
|
|
51
|
+
if (payload.role !== undefined)
|
|
52
|
+
ctx.role = payload.role;
|
|
53
|
+
if (payload.email !== undefined)
|
|
54
|
+
ctx.email = payload.email;
|
|
55
|
+
return ctx;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// ── 2. API key via Authorization header ──────────────────────
|
|
60
|
+
const authHeader = request.headers.get(authHeaderName);
|
|
61
|
+
if (authHeader) {
|
|
62
|
+
const token = authHeader.startsWith("Bearer ")
|
|
63
|
+
? authHeader.slice(7)
|
|
64
|
+
: null;
|
|
65
|
+
if (token && token.startsWith(apiKeyPrefix) && deps.findAgentByKeyPrefix) {
|
|
66
|
+
const prefix = extractApiKeyPrefix(token);
|
|
67
|
+
const credential = await deps.findAgentByKeyPrefix(prefix);
|
|
68
|
+
if (credential && !credential.revokedAt) {
|
|
69
|
+
const valid = await verifyApiKey(token, credential.apiKeyHash);
|
|
70
|
+
if (valid) {
|
|
71
|
+
return {
|
|
72
|
+
isAuthenticated: true,
|
|
73
|
+
type: "agent",
|
|
74
|
+
agentId: credential.id,
|
|
75
|
+
agentName: credential.name,
|
|
76
|
+
permissions: credential.permissions,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// ── 3. Anonymous ─────────────────────────────────────────────
|
|
83
|
+
return ANONYMOUS_CONTEXT;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEjE,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAC9C,MAAM,sBAAsB,GAAG,SAAS,CAAC;AACzC,MAAM,iBAAiB,GAAgB;IACrC,eAAe,EAAE,KAAK;IACtB,IAAI,EAAE,WAAW;CAClB,CAAC;AAEF,sEAAsE;AAEtE,SAAS,YAAY,CAAC,MAAc;IAClC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,OAAO,KAAK,CAAC,CAAC;YAAE,SAAS;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uEAAuE;AAEvE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAkB,EAClB,IAAsB;IAEtB,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,sBAAsB,CAAC;IACtE,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,EAAE,UAAU,IAAI,eAAe,CAAC;IAErE,OAAO,KAAK,EAAE,OAAgB,EAAwB,EAAE;QACtD,gEAAgE;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;YAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;YAEtD,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,OAAO,GAAG,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACnE,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,GAAG,GAAgB;wBACvB,eAAe,EAAE,IAAI;wBACrB,IAAI,EAAE,OAAO;wBACb,MAAM,EAAE,OAAO,CAAC,MAAM;qBACvB,CAAC;oBACF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;wBAAE,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;oBACxD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;wBAAE,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;oBAC3D,OAAO,GAAG,CAAC;gBACb,CAAC;YACH,CAAC;QACH,CAAC;QAED,gEAAgE;QAChE,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACvD,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC;gBAC5C,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;gBACrB,CAAC,CAAC,IAAI,CAAC;YAET,IAAI,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBACzE,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;gBAC1C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;gBAE3D,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;oBACxC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;oBAC/D,IAAI,KAAK,EAAE,CAAC;wBACV,OAAO;4BACL,eAAe,EAAE,IAAI;4BACrB,IAAI,EAAE,OAAO;4BACb,OAAO,EAAE,UAAU,CAAC,EAAE;4BACtB,SAAS,EAAE,UAAU,CAAC,IAAI;4BAC1B,WAAW,EAAE,UAAU,CAAC,WAAW;yBACpC,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,gEAAgE;QAChE,OAAO,iBAAiB,CAAC;IAC3B,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a required permission is satisfied by at least one entry in
|
|
3
|
+
* the `granted` permission set.
|
|
4
|
+
*
|
|
5
|
+
* Permission strings follow the `resource:action` pattern.
|
|
6
|
+
*
|
|
7
|
+
* Wildcards:
|
|
8
|
+
* - `*:read` — allows `read` on any resource
|
|
9
|
+
* - `ticket:*` — allows any action on `ticket`
|
|
10
|
+
* - `*:*` — full access (superuser)
|
|
11
|
+
*
|
|
12
|
+
* Examples:
|
|
13
|
+
* checkPermission({ resource: "ticket", action: "read" }, ["ticket:read"]) // true
|
|
14
|
+
* checkPermission({ resource: "ticket", action: "write" }, ["*:write"]) // true
|
|
15
|
+
* checkPermission({ resource: "ticket", action: "delete" }, ["ticket:*"]) // true
|
|
16
|
+
* checkPermission({ resource: "ticket", action: "delete" }, ["*:*"]) // true
|
|
17
|
+
*/
|
|
18
|
+
export declare function checkPermission(required: {
|
|
19
|
+
resource: string;
|
|
20
|
+
action: "read" | "write" | "delete";
|
|
21
|
+
}, granted: string[]): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Derive a `{ resource, action }` pair from an agent capability mode and
|
|
24
|
+
* an optional resource name.
|
|
25
|
+
*
|
|
26
|
+
* Mapping:
|
|
27
|
+
* - `"read"` → `{ resource, action: "read" }`
|
|
28
|
+
* - `"write"` → `{ resource, action: "write" }`
|
|
29
|
+
* - `"external"` → `{ resource: "external", action: "write" }`
|
|
30
|
+
*
|
|
31
|
+
* When `resource` is omitted the wildcard `"*"` is used.
|
|
32
|
+
*/
|
|
33
|
+
export declare function derivePermission(capability: "read" | "write" | "external", resource?: string): {
|
|
34
|
+
resource: string;
|
|
35
|
+
action: string;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;CAAE,EACnE,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAiBT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,EACzC,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAStC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a required permission is satisfied by at least one entry in
|
|
3
|
+
* the `granted` permission set.
|
|
4
|
+
*
|
|
5
|
+
* Permission strings follow the `resource:action` pattern.
|
|
6
|
+
*
|
|
7
|
+
* Wildcards:
|
|
8
|
+
* - `*:read` — allows `read` on any resource
|
|
9
|
+
* - `ticket:*` — allows any action on `ticket`
|
|
10
|
+
* - `*:*` — full access (superuser)
|
|
11
|
+
*
|
|
12
|
+
* Examples:
|
|
13
|
+
* checkPermission({ resource: "ticket", action: "read" }, ["ticket:read"]) // true
|
|
14
|
+
* checkPermission({ resource: "ticket", action: "write" }, ["*:write"]) // true
|
|
15
|
+
* checkPermission({ resource: "ticket", action: "delete" }, ["ticket:*"]) // true
|
|
16
|
+
* checkPermission({ resource: "ticket", action: "delete" }, ["*:*"]) // true
|
|
17
|
+
*/
|
|
18
|
+
export function checkPermission(required, granted) {
|
|
19
|
+
for (const perm of granted) {
|
|
20
|
+
const sepIndex = perm.indexOf(":");
|
|
21
|
+
if (sepIndex === -1)
|
|
22
|
+
continue; // malformed entry, skip
|
|
23
|
+
const grantedResource = perm.slice(0, sepIndex);
|
|
24
|
+
const grantedAction = perm.slice(sepIndex + 1);
|
|
25
|
+
const resourceMatch = grantedResource === "*" || grantedResource === required.resource;
|
|
26
|
+
const actionMatch = grantedAction === "*" || grantedAction === required.action;
|
|
27
|
+
if (resourceMatch && actionMatch)
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Derive a `{ resource, action }` pair from an agent capability mode and
|
|
34
|
+
* an optional resource name.
|
|
35
|
+
*
|
|
36
|
+
* Mapping:
|
|
37
|
+
* - `"read"` → `{ resource, action: "read" }`
|
|
38
|
+
* - `"write"` → `{ resource, action: "write" }`
|
|
39
|
+
* - `"external"` → `{ resource: "external", action: "write" }`
|
|
40
|
+
*
|
|
41
|
+
* When `resource` is omitted the wildcard `"*"` is used.
|
|
42
|
+
*/
|
|
43
|
+
export function derivePermission(capability, resource) {
|
|
44
|
+
if (capability === "external") {
|
|
45
|
+
return { resource: "external", action: "write" };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
resource: resource ?? "*",
|
|
49
|
+
action: capability,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAmE,EACnE,OAAiB;IAEjB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,QAAQ,KAAK,CAAC,CAAC;YAAE,SAAS,CAAC,wBAAwB;QAEvD,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAChD,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QAE/C,MAAM,aAAa,GACjB,eAAe,KAAK,GAAG,IAAI,eAAe,KAAK,QAAQ,CAAC,QAAQ,CAAC;QACnE,MAAM,WAAW,GACf,aAAa,KAAK,GAAG,IAAI,aAAa,KAAK,QAAQ,CAAC,MAAM,CAAC;QAE7D,IAAI,aAAa,IAAI,WAAW;YAAE,OAAO,IAAI,CAAC;IAChD,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAyC,EACzC,QAAiB;IAEjB,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;QAC9B,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACnD,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,QAAQ,IAAI,GAAG;QACzB,MAAM,EAAE,UAAU;KACnB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SessionPayload } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Create a signed JWT containing the given session data.
|
|
4
|
+
*
|
|
5
|
+
* `maxAge` defaults to `"7d"` (7 days) when omitted.
|
|
6
|
+
*/
|
|
7
|
+
export declare function signSession(payload: Omit<SessionPayload, "iat" | "exp">, secret: string, maxAge?: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Verify a JWT's HMAC-SHA256 signature and expiration.
|
|
10
|
+
*
|
|
11
|
+
* Returns the decoded payload on success, or `null` when the token is
|
|
12
|
+
* invalid, tampered with, or expired.
|
|
13
|
+
*/
|
|
14
|
+
export declare function verifySession(token: string, secret: string): SessionPayload | null;
|
|
15
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA+DjD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,KAAK,CAAC,EAC5C,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAWR;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,cAAc,GAAG,IAAI,CA6BvB"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
// ── Base64url helpers ──────────────────────────────────────────────
|
|
3
|
+
function base64urlEncode(data) {
|
|
4
|
+
const buf = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
|
|
5
|
+
return buf.toString("base64url");
|
|
6
|
+
}
|
|
7
|
+
function base64urlDecode(str) {
|
|
8
|
+
return Buffer.from(str, "base64url").toString("utf-8");
|
|
9
|
+
}
|
|
10
|
+
// ── Duration parsing ───────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Parse a human-friendly duration string into seconds.
|
|
13
|
+
*
|
|
14
|
+
* Supported suffixes:
|
|
15
|
+
* "s" — seconds "m" — minutes "h" — hours "d" — days "w" — weeks
|
|
16
|
+
*
|
|
17
|
+
* Examples: "7d" → 604800, "1h" → 3600, "30m" → 1800
|
|
18
|
+
*/
|
|
19
|
+
function parseDuration(duration) {
|
|
20
|
+
const match = duration.match(/^(\d+)\s*(s|m|h|d|w)$/);
|
|
21
|
+
if (!match) {
|
|
22
|
+
throw new Error(`Invalid duration format: "${duration}". Expected a number followed by s, m, h, d, or w.`);
|
|
23
|
+
}
|
|
24
|
+
const value = Number(match[1]);
|
|
25
|
+
const unit = match[2];
|
|
26
|
+
const multipliers = {
|
|
27
|
+
s: 1,
|
|
28
|
+
m: 60,
|
|
29
|
+
h: 3600,
|
|
30
|
+
d: 86_400,
|
|
31
|
+
w: 604_800,
|
|
32
|
+
};
|
|
33
|
+
return value * multipliers[unit];
|
|
34
|
+
}
|
|
35
|
+
// ── JWT implementation (HS256) ─────────────────────────────────────
|
|
36
|
+
function sign(payload, secret) {
|
|
37
|
+
const header = base64urlEncode(JSON.stringify({ alg: "HS256", typ: "JWT" }));
|
|
38
|
+
const body = base64urlEncode(payload);
|
|
39
|
+
const signingInput = `${header}.${body}`;
|
|
40
|
+
const signature = createHmac("sha256", secret)
|
|
41
|
+
.update(signingInput)
|
|
42
|
+
.digest();
|
|
43
|
+
return `${signingInput}.${base64urlEncode(signature)}`;
|
|
44
|
+
}
|
|
45
|
+
// ── Public API ─────────────────────────────────────────────────────
|
|
46
|
+
/**
|
|
47
|
+
* Create a signed JWT containing the given session data.
|
|
48
|
+
*
|
|
49
|
+
* `maxAge` defaults to `"7d"` (7 days) when omitted.
|
|
50
|
+
*/
|
|
51
|
+
export function signSession(payload, secret, maxAge) {
|
|
52
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
53
|
+
const ttl = parseDuration(maxAge ?? "7d");
|
|
54
|
+
const full = {
|
|
55
|
+
...payload,
|
|
56
|
+
iat: nowSeconds,
|
|
57
|
+
exp: nowSeconds + ttl,
|
|
58
|
+
};
|
|
59
|
+
return sign(JSON.stringify(full), secret);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Verify a JWT's HMAC-SHA256 signature and expiration.
|
|
63
|
+
*
|
|
64
|
+
* Returns the decoded payload on success, or `null` when the token is
|
|
65
|
+
* invalid, tampered with, or expired.
|
|
66
|
+
*/
|
|
67
|
+
export function verifySession(token, secret) {
|
|
68
|
+
const parts = token.split(".");
|
|
69
|
+
if (parts.length !== 3)
|
|
70
|
+
return null;
|
|
71
|
+
const [header, body, sig] = parts;
|
|
72
|
+
// Recompute the expected signature.
|
|
73
|
+
const expectedSig = createHmac("sha256", secret)
|
|
74
|
+
.update(`${header}.${body}`)
|
|
75
|
+
.digest();
|
|
76
|
+
const actualSig = Buffer.from(sig, "base64url");
|
|
77
|
+
// Timing-safe comparison to prevent timing attacks.
|
|
78
|
+
if (expectedSig.length !== actualSig.length)
|
|
79
|
+
return null;
|
|
80
|
+
if (!timingSafeEqual(expectedSig, actualSig))
|
|
81
|
+
return null;
|
|
82
|
+
// Decode payload.
|
|
83
|
+
try {
|
|
84
|
+
const payload = JSON.parse(base64urlDecode(body));
|
|
85
|
+
// Check expiration.
|
|
86
|
+
const now = Math.floor(Date.now() / 1000);
|
|
87
|
+
if (typeof payload.exp !== "number" || payload.exp <= now)
|
|
88
|
+
return null;
|
|
89
|
+
return payload;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface AuthConfig {
|
|
2
|
+
session: {
|
|
3
|
+
secret: string;
|
|
4
|
+
maxAge?: string;
|
|
5
|
+
};
|
|
6
|
+
apiKeys?: {
|
|
7
|
+
prefix?: string;
|
|
8
|
+
headerName?: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export interface SessionPayload {
|
|
12
|
+
userId: string;
|
|
13
|
+
email?: string;
|
|
14
|
+
role?: string;
|
|
15
|
+
iat: number;
|
|
16
|
+
exp: number;
|
|
17
|
+
}
|
|
18
|
+
export interface AgentCredential {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
apiKeyHash: string;
|
|
22
|
+
apiKeyPrefix: string;
|
|
23
|
+
permissions: string[];
|
|
24
|
+
revokedAt?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface AuthContext {
|
|
27
|
+
isAuthenticated: boolean;
|
|
28
|
+
type: "human" | "agent" | "anonymous";
|
|
29
|
+
userId?: string;
|
|
30
|
+
role?: string;
|
|
31
|
+
email?: string;
|
|
32
|
+
agentId?: string;
|
|
33
|
+
agentName?: string;
|
|
34
|
+
permissions?: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface AuthResolverDeps {
|
|
37
|
+
/** Look up an agent credential by API key prefix */
|
|
38
|
+
findAgentByKeyPrefix?: (prefix: string) => Promise<AgentCredential | null>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,WAAW,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oDAAoD;IACpD,oBAAoB,CAAC,EAAE,CACrB,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;CACtC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zauso-ai/capstan-auth",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc -p tsconfig.json",
|
|
15
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"description": "Authentication for Capstan — JWT sessions, API key auth for AI agents, permissions",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/barry3406/capstan.git",
|
|
24
|
+
"directory": "packages/auth"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "barry3406",
|
|
28
|
+
"homepage": "https://github.com/barry3406/capstan",
|
|
29
|
+
"keywords": [
|
|
30
|
+
"capstan",
|
|
31
|
+
"ai-agent",
|
|
32
|
+
"full-stack",
|
|
33
|
+
"framework",
|
|
34
|
+
"mcp",
|
|
35
|
+
"a2a",
|
|
36
|
+
"typescript"
|
|
37
|
+
],
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
}
|
|
41
|
+
}
|