@newtype-ai/nit-sdk 0.3.2 → 0.4.0
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 +40 -2
- package/dist/index.d.ts +10 -1
- package/dist/index.js +94 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,11 +11,12 @@ npm install @newtype-ai/nit-sdk
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import { verifyAgent } from '@newtype-ai/nit-sdk';
|
|
14
|
+
import { verifyAgent, NitSdkError } from '@newtype-ai/nit-sdk';
|
|
15
15
|
|
|
16
16
|
// The agent sends you a login payload (generated by `nit sign --login your-app.com`)
|
|
17
17
|
const result = await verifyAgent(payload, {
|
|
18
|
-
policy: { max_identities_per_machine: 10, min_age_seconds: 3600 }
|
|
18
|
+
policy: { max_identities_per_machine: 10, min_age_seconds: 3600 },
|
|
19
|
+
timeoutMs: 5_000, // optional, default 10s
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
if (result.verified && result.admitted) {
|
|
@@ -50,6 +51,7 @@ The server acts as an **identity registry** — it stores identity metadata, eva
|
|
|
50
51
|
| `payload` | `LoginPayload` | `{ agent_id, domain, timestamp, signature }` from the agent |
|
|
51
52
|
| `options.apiUrl` | `string` | Override API URL (default: `https://api.newtype-ai.org`) |
|
|
52
53
|
| `options.policy` | `VerifyPolicy` | Trust rules the server evaluates (all optional) |
|
|
54
|
+
| `options.timeoutMs` | `number` | Fetch timeout in milliseconds (default: `10000`) |
|
|
53
55
|
|
|
54
56
|
**Policy fields:**
|
|
55
57
|
|
|
@@ -74,6 +76,42 @@ The server acts as an **identity registry** — it stores identity metadata, eva
|
|
|
74
76
|
| `identity` | `IdentityMetadata` | Registration time, machine/IP counts, login history |
|
|
75
77
|
| `attestation` | `ServerAttestation` | Server's Ed25519 signature over the result |
|
|
76
78
|
|
|
79
|
+
### `NitSdkError`
|
|
80
|
+
|
|
81
|
+
Typed error thrown by `fetchAgentCard()` on non-404 HTTP failures and malformed responses.
|
|
82
|
+
|
|
83
|
+
| Property | Type | Description |
|
|
84
|
+
|----------|------|-------------|
|
|
85
|
+
| `message` | `string` | Human-readable error description |
|
|
86
|
+
| `status` | `number` | HTTP status code (0 for shape validation failures) |
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { fetchAgentCard, NitSdkError } from '@newtype-ai/nit-sdk';
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const card = await fetchAgentCard(agentId, 'your-app.com', readToken);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
if (err instanceof NitSdkError) {
|
|
95
|
+
console.error(`SDK error (HTTP ${err.status}): ${err.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `fetchAgentCard(agentId, domain, readToken, options?)`
|
|
101
|
+
|
|
102
|
+
| Parameter | Type | Description |
|
|
103
|
+
|-----------|------|-------------|
|
|
104
|
+
| `options.baseUrl` | `string` | Override card hosting URL (default: `https://agent-{id}.newtype-ai.org`) |
|
|
105
|
+
| `options.timeoutMs` | `number` | Fetch timeout in milliseconds (default: `10000`) |
|
|
106
|
+
|
|
107
|
+
Returns `Promise<AgentCard | null>` — `null` for 404, throws `NitSdkError` for other HTTP errors.
|
|
108
|
+
|
|
109
|
+
## Security
|
|
110
|
+
|
|
111
|
+
- **HTTPS enforcement:** Custom `apiUrl` and `baseUrl` must use `https://`. Localhost (`127.0.0.1`, `localhost`) is exempt for development. Non-HTTPS URLs throw `TypeError`.
|
|
112
|
+
- **Input validation:** `verifyAgent` validates the payload before sending: `agent_id` must be a valid UUID, `domain` must be non-empty (max 253 chars), `timestamp` must be a finite positive number, `signature` must be non-empty.
|
|
113
|
+
- **Response shape checks:** After parsing JSON responses, the SDK verifies the expected shape (`verified` must be boolean, card must have `name` string) before returning. Malformed responses are returned as `{ verified: false, error: '...' }`.
|
|
114
|
+
|
|
77
115
|
## Full Integration Guide
|
|
78
116
|
|
|
79
117
|
See [docs/app-integration.md](docs/app-integration.md) for the complete flow, endpoint spec, code examples in multiple languages, fetching updated cards, and security notes.
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
* Apps receive a login payload from an agent (via nit) and call
|
|
5
5
|
* verifyAgent() to confirm the agent's identity. No crypto needed.
|
|
6
6
|
*/
|
|
7
|
+
/** Typed error for HTTP and shape failures in the SDK. */
|
|
8
|
+
declare class NitSdkError extends Error {
|
|
9
|
+
readonly status: number;
|
|
10
|
+
constructor(message: string, status: number);
|
|
11
|
+
}
|
|
7
12
|
/** The login payload an agent sends to your app. */
|
|
8
13
|
interface LoginPayload {
|
|
9
14
|
agent_id: string;
|
|
@@ -100,10 +105,14 @@ interface VerifyOptions {
|
|
|
100
105
|
apiUrl?: string;
|
|
101
106
|
/** App-defined trust policy. Server evaluates and returns admitted: true/false. */
|
|
102
107
|
policy?: VerifyPolicy;
|
|
108
|
+
/** Fetch timeout in milliseconds. Defaults to 10 000. */
|
|
109
|
+
timeoutMs?: number;
|
|
103
110
|
}
|
|
104
111
|
interface FetchCardOptions {
|
|
105
112
|
/** Override the base URL for agent card hosting. Defaults to https://agent-{agent_id}.newtype-ai.org */
|
|
106
113
|
baseUrl?: string;
|
|
114
|
+
/** Fetch timeout in milliseconds. Defaults to 10 000. */
|
|
115
|
+
timeoutMs?: number;
|
|
107
116
|
}
|
|
108
117
|
/**
|
|
109
118
|
* Verify an agent's login payload against the newtype-ai.org server.
|
|
@@ -139,4 +148,4 @@ declare function verifyAgent(payload: LoginPayload, options?: VerifyOptions): Pr
|
|
|
139
148
|
*/
|
|
140
149
|
declare function fetchAgentCard(agentId: string, domain: string, readToken: string, options?: FetchCardOptions): Promise<AgentCard | null>;
|
|
141
150
|
|
|
142
|
-
export { type AgentCard, type AgentCardSkill, type FetchCardOptions, type IdentityMetadata, type LoginPayload, type ServerAttestation, type VerifyFailure, type VerifyOptions, type VerifyPolicy, type VerifyResult, type VerifySuccess, fetchAgentCard, verifyAgent };
|
|
151
|
+
export { type AgentCard, type AgentCardSkill, type FetchCardOptions, type IdentityMetadata, type LoginPayload, NitSdkError, type ServerAttestation, type VerifyFailure, type VerifyOptions, type VerifyPolicy, type VerifyResult, type VerifySuccess, fetchAgentCard, verifyAgent };
|
package/dist/index.js
CHANGED
|
@@ -1,30 +1,107 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
var NitSdkError = class extends Error {
|
|
3
|
+
constructor(message, status) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "NitSdkError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
11
|
+
function assertHttps(url, label) {
|
|
12
|
+
try {
|
|
13
|
+
const parsed = new URL(url);
|
|
14
|
+
if (parsed.protocol === "https:") return;
|
|
15
|
+
if (parsed.protocol === "http:" && (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1"))
|
|
16
|
+
return;
|
|
17
|
+
throw new TypeError(
|
|
18
|
+
`${label} must use HTTPS (got ${parsed.protocol}//${parsed.hostname})`
|
|
19
|
+
);
|
|
20
|
+
} catch (e) {
|
|
21
|
+
if (e instanceof TypeError) throw e;
|
|
22
|
+
throw new TypeError(`${label} is not a valid URL: ${url}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function validatePayload(payload) {
|
|
26
|
+
if (typeof payload.agent_id !== "string" || !UUID_RE.test(payload.agent_id)) {
|
|
27
|
+
throw new TypeError(
|
|
28
|
+
"payload.agent_id must be a UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (typeof payload.domain !== "string" || payload.domain.length === 0 || payload.domain.length > 253) {
|
|
32
|
+
throw new TypeError(
|
|
33
|
+
"payload.domain must be a non-empty string (max 253 chars)"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
if (typeof payload.timestamp !== "number" || !Number.isFinite(payload.timestamp) || payload.timestamp <= 0) {
|
|
37
|
+
throw new TypeError("payload.timestamp must be a finite positive number");
|
|
38
|
+
}
|
|
39
|
+
if (typeof payload.signature !== "string" || payload.signature.length === 0) {
|
|
40
|
+
throw new TypeError("payload.signature must be a non-empty string");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function fetchWithTimeout(url, init, timeoutMs) {
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
46
|
+
return fetch(url, { ...init, signal: controller.signal }).finally(
|
|
47
|
+
() => clearTimeout(timer)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
2
50
|
var DEFAULT_API_URL = "https://api.newtype-ai.org";
|
|
3
51
|
async function verifyAgent(payload, options) {
|
|
52
|
+
validatePayload(payload);
|
|
4
53
|
const apiUrl = options?.apiUrl ?? DEFAULT_API_URL;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
54
|
+
if (options?.apiUrl) assertHttps(apiUrl, "options.apiUrl");
|
|
55
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
56
|
+
const res = await fetchWithTimeout(
|
|
57
|
+
`${apiUrl}/agent-card/verify`,
|
|
58
|
+
{
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: { "Content-Type": "application/json" },
|
|
61
|
+
body: JSON.stringify({
|
|
62
|
+
agent_id: payload.agent_id,
|
|
63
|
+
domain: payload.domain,
|
|
64
|
+
timestamp: payload.timestamp,
|
|
65
|
+
signature: payload.signature,
|
|
66
|
+
...options?.policy ? { policy: options.policy } : {}
|
|
67
|
+
})
|
|
68
|
+
},
|
|
69
|
+
timeoutMs
|
|
70
|
+
);
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
return { verified: false, error: `Server error (HTTP ${res.status})` };
|
|
73
|
+
}
|
|
74
|
+
const data = await res.json();
|
|
75
|
+
if (typeof data !== "object" || data === null || typeof data.verified !== "boolean") {
|
|
76
|
+
return { verified: false, error: "Malformed server response (missing verified field)" };
|
|
77
|
+
}
|
|
78
|
+
return data;
|
|
17
79
|
}
|
|
18
80
|
async function fetchAgentCard(agentId, domain, readToken, options) {
|
|
19
81
|
const baseUrl = options?.baseUrl ?? `https://agent-${agentId}.newtype-ai.org`;
|
|
82
|
+
if (options?.baseUrl) assertHttps(baseUrl, "options.baseUrl");
|
|
83
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
20
84
|
const url = `${baseUrl}/.well-known/agent-card.json?branch=${encodeURIComponent(domain)}`;
|
|
21
|
-
const res = await
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
85
|
+
const res = await fetchWithTimeout(
|
|
86
|
+
url,
|
|
87
|
+
{ headers: { Authorization: `Bearer ${readToken}` } },
|
|
88
|
+
timeoutMs
|
|
89
|
+
);
|
|
90
|
+
if (res.status === 404) return null;
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
throw new NitSdkError(
|
|
93
|
+
`Failed to fetch agent card (HTTP ${res.status})`,
|
|
94
|
+
res.status
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const data = await res.json();
|
|
98
|
+
if (typeof data !== "object" || data === null || typeof data.name !== "string") {
|
|
99
|
+
throw new NitSdkError("Malformed agent card response (missing name field)", 0);
|
|
100
|
+
}
|
|
101
|
+
return data;
|
|
26
102
|
}
|
|
27
103
|
export {
|
|
104
|
+
NitSdkError,
|
|
28
105
|
fetchAgentCard,
|
|
29
106
|
verifyAgent
|
|
30
107
|
};
|