@inneranimalmedia/agentsam-sdk 1.9.0 → 2.0.0-alpha.identity

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.
Files changed (66) hide show
  1. package/DEVELOPMENT.md +20 -4
  2. package/README.md +11 -3
  3. package/docs/RELEASES.iam-mirror.md +2 -2
  4. package/docs/RELEASES.md +2 -2
  5. package/docs/identity/README.md +11 -0
  6. package/examples/gorilla-shell/README.md +7 -14
  7. package/package.json +8 -3
  8. package/packages/identity/README.md +68 -0
  9. package/packages/identity/backend/README.md +26 -0
  10. package/packages/identity/docs/getting-started.md +16 -0
  11. package/packages/identity/examples/cloudflare-worker/README.md +5 -0
  12. package/packages/identity/examples/legendary-os/README.md +27 -0
  13. package/packages/identity/package.json +29 -0
  14. package/packages/identity/src/adapters/cloudflare-d1/index.js +4 -0
  15. package/packages/identity/src/adapters/inneranimalmedia/README.md +7 -0
  16. package/packages/identity/src/contracts/external-identity.js +35 -0
  17. package/packages/identity/src/contracts/identity.js +17 -0
  18. package/packages/identity/src/contracts/provider.js +8 -0
  19. package/packages/identity/src/contracts/session.js +9 -0
  20. package/packages/identity/src/core/accounts.js +4 -0
  21. package/packages/identity/src/core/browser-paths.js +34 -0
  22. package/packages/identity/src/core/constants.js +9 -0
  23. package/packages/identity/src/core/errors.js +8 -0
  24. package/packages/identity/src/core/http-json.js +10 -0
  25. package/packages/identity/src/core/identity.js +4 -0
  26. package/packages/identity/src/core/index.js +10 -0
  27. package/packages/identity/src/core/password-crypto.js +53 -0
  28. package/packages/identity/src/core/recovery.js +4 -0
  29. package/packages/identity/src/core/sessions.js +59 -0
  30. package/packages/identity/src/frontend/auth-portal/README.md +30 -0
  31. package/packages/identity/src/frontend/auth-portal/pages/login.html +821 -0
  32. package/packages/identity/src/frontend/auth-portal/pages/reset.html +582 -0
  33. package/packages/identity/src/frontend/auth-portal/pages/signup.html +835 -0
  34. package/packages/identity/src/index.js +118 -0
  35. package/packages/identity/src/migrations/README.md +5 -0
  36. package/packages/identity/src/oauth/README.md +4 -0
  37. package/packages/identity/src/oauth/callback.js +24 -0
  38. package/packages/identity/src/provider-contract.js +61 -0
  39. package/packages/identity/src/providers/email/index.js +39 -0
  40. package/packages/identity/src/providers/gcp/index.js +58 -0
  41. package/packages/identity/src/providers/github/index.js +14 -0
  42. package/packages/identity/src/providers/github/mapper.js +22 -0
  43. package/packages/identity/src/providers/github/oauth.js +43 -0
  44. package/packages/identity/src/providers/github/profile.js +34 -0
  45. package/packages/identity/src/providers/google/index.js +14 -0
  46. package/packages/identity/src/providers/google/mapper.js +32 -0
  47. package/packages/identity/src/providers/google/oauth.js +41 -0
  48. package/packages/identity/src/providers/google/profile.js +14 -0
  49. package/packages/identity/src/providers/index.js +25 -0
  50. package/packages/identity/tests/identity.test.mjs +74 -0
  51. package/python/README.md +8 -1
  52. package/python/agentsam_sdk/repository/__init__.py +4 -18
  53. package/python/docs/gaps.md +9 -0
  54. package/python/docs/tooling.md +5 -1
  55. package/python/tests/test_d1_bloat.py +1 -1
  56. package/src/index.js +16 -0
  57. package/src/lib/core-client.js +5 -0
  58. package/templates/gorilla-shell/App.tsx +28 -28
  59. package/templates/gorilla-shell/README.md +15 -3
  60. package/test/smoke.mjs +5 -0
  61. package/examples/gorilla-shell/App.tsx +0 -540
  62. package/examples/gorilla-shell/index.html +0 -15
  63. package/examples/gorilla-shell/main.jsx +0 -9
  64. package/examples/gorilla-shell/package.json +0 -19
  65. package/examples/gorilla-shell/vite.config.js +0 -10
  66. package/src/commands/http-server.js +0 -97
package/DEVELOPMENT.md CHANGED
@@ -1,4 +1,4 @@
1
- # DEVELOPING AGENTSAM-SDK WITH INNER ANIMAL MEDIA
1
+ # Developing agentsam-sdk with Inner Animal Media
2
2
 
3
3
  ## Local smoke test
4
4
 
@@ -47,9 +47,25 @@ npx agentsam init \
47
47
  2. `npm test` passes
48
48
  3. Bump version in root `package.json`
49
49
  4. Confirm `files` includes consumer surfaces (`src`, `bin`, `python`, `protocol`, …)
50
- 5. `npm publish --access public` (manual; requires `npm login` to org)
51
- 6. Tag: `git tag vX.Y.Z && git push origin vX.Y.Z`
52
- 7. Record pairing in [`docs/RELEASES.md`](./docs/RELEASES.md): `iam@<40-sha> @inneranimalmedia/agentsam-sdk@X.Y.Z`
50
+ 5. `bash scripts/npm-publish-preflight.sh` must print `npm whoami: inneranimalmedia`
51
+ 6. `npm publish --tag alpha --access public` (or `--tag latest` for stable)
52
+ 7. Tag: `git tag vX.Y.Z && git push origin vX.Y.Z`
53
+ 8. Record pairing in [`docs/RELEASES.md`](./docs/RELEASES.md): `iam@<40-sha> ↔ @inneranimalmedia/agentsam-sdk@X.Y.Z`
54
+
55
+ ### Publish 404 (`PUT … Not found`)
56
+
57
+ npm returns **404** (not 403) when you are logged in as the **wrong user** for scope `@inneranimalmedia`.
58
+
59
+ - Registry maintainer for this package: **`inneranimalmedia`** (see `npm view @inneranimalmedia/agentsam-sdk maintainers`)
60
+ - Fix: `npm whoami` must be `inneranimalmedia`
61
+ ```bash
62
+ npm logout
63
+ npm login # npm user inneranimalmedia (org owner), not personal GitHub-linked account
64
+ bash scripts/npm-publish-preflight.sh
65
+ npm publish --tag alpha --access public
66
+ ```
67
+ - If you use a team org on npmjs.com, invite your daily npm user as **Developer** on the org with publish rights.
68
+ - Always pass `--access public` (already in `package.json` `publishConfig`; flag is harmless).
53
69
 
54
70
  Workspace packages under `packages/*` (e.g. `@inneranimalmedia/agentsam-shell-kit`) stay
55
71
  `private: true` until separately ready — they are **not** the root publish identity.
package/README.md CHANGED
@@ -6,7 +6,7 @@ Agent Sam is a full-stack autonomous agent SDK built on Cloudflare Workers, D1,
6
6
 
7
7
  **Repo:** [github.com/SamPrimeaux/agentsam-sdk](https://github.com/SamPrimeaux/agentsam-sdk) · **npm:** `@inneranimalmedia/agentsam-sdk`
8
8
 
9
- **Protocol (dual-home, no drift):** [`protocol/README.md`](./protocol/README.md) — every tool/feature lands in this repo **and** `inneranimalmedia/agentsam-sdk/`; npm publish is manual after mirror. Python lane: [`python/`](./python/).
9
+ **Protocol (dual-home, no drift):** [`protocol/README.md`](./protocol/README.md) — every tool/feature lands in this repo **and** `inneranimalmedia/agentsam-sdk/`; npm publish is manual after mirror. Python lane: [`python/`](./python/) (stdlib-only). Optional network Python package: [`packages/agentsam-site-scrape/`](./packages/agentsam-site-scrape/) (live experimental; does not break the stdlib core).
10
10
 
11
11
  ---
12
12
 
@@ -78,17 +78,25 @@ npx @inneranimalmedia/agentsam-sdk shell
78
78
  | Piece | Location |
79
79
  |-------|----------|
80
80
  | Slash command registry | `src/lib/slash-commands.js` |
81
- | Phase 0 visual prototype | `examples/gorilla-shell/` |
81
+ | Phase 0 visual prototype | `templates/gorilla-shell/` (canonical; `examples/gorilla-shell/` is a pointer) |
82
82
  | Architecture + phases | [docs/CLI_SHELL.md](./docs/CLI_SHELL.md) |
83
83
 
84
84
  Run the prototype:
85
85
 
86
86
  ```bash
87
- cd examples/gorilla-shell && npm install && npm run dev
87
+ cd templates/gorilla-shell && npm install && npm run dev
88
88
  ```
89
89
 
90
90
  ---
91
91
 
92
+ ## Architecture (CORE default)
93
+
94
+ `src/lib/core-client.js` defaults to `https://inneranimalmedia.com`. **Correct as designed** for serving many client projects: the npm package is a thin delivery/CLI client; intelligence, tool catalog, and policy live server-side on IAM CORE.
95
+
96
+ Without an IAM backend (or `IAM_CORE_URL` / `AGENTSAM_CORE_URL` override), strangers who `npm install` get **scaffold + local CLI only** — not a freestanding hosted Agent Sam.
97
+
98
+ ---
99
+
92
100
  ## Installation
93
101
 
94
102
  ```bash
@@ -8,10 +8,10 @@ Canonical npm-side table also lives in the SDK repo: `docs/RELEASES.md`.
8
8
  | npm version | Published (UTC) | IAM git SHA (40) | Notes |
9
9
  |-------------|-----------------|------------------|-------|
10
10
  | 1.7.0 | 2026-07-14 | _(pre-receipt)_ | Prior publish |
11
- | 1.9.0 | _(pending publish)_ | `fc1628505cb3c9946c149e4c468c2e264d6f381e` | `repository.inspect` + `--dupes`; mirrored from IAM agentsam-sdk |
11
+ | 1.9.0 | 2026-08-05T07:12:21.726Z | `fc1628505cb3c9946c149e4c468c2e264d6f381e` | `repository.inspect` + `--dupes`; mirrored from IAM agentsam-sdk |
12
12
  | 1.8.0 | 2026-08-02T13:57:12.080Z | `580547301e370b77ec26bd72558979d335feedd9` | `python/` + `protocol/` in tarball; `repository.scan_bloat`; shell-kit under `packages/` |
13
13
 
14
- **Latest pairing (ready to publish):** `iam@fc1628505cb3c9946c149e4c468c2e264d6f381e` ↔ `@inneranimalmedia/agentsam-sdk@1.9.0`
14
+ **Latest pairing :** `iam@fc1628505cb3c9946c149e4c468c2e264d6f381e` ↔ `@inneranimalmedia/agentsam-sdk@1.9.0`
15
15
 
16
16
  ### Receipt rules
17
17
 
package/docs/RELEASES.md CHANGED
@@ -3,7 +3,7 @@
3
3
  | npm version | Published (UTC) | IAM git SHA (40) | Notes |
4
4
  |-------------|-----------------|------------------|-------|
5
5
  | 1.7.0 | 2026-07-14 | _(pre-receipt)_ | Prior publish |
6
- | 1.9.0 | _(pending publish)_ | `fc1628505cb3c9946c149e4c468c2e264d6f381e` | `python/agentsam_sdk/repository/inspect.py` (`--dupes`); CLI `agentsam repository inspect` |
6
+ | 1.9.0 | 2026-08-05T07:12:21.726Z | `fc1628505cb3c9946c149e4c468c2e264d6f381e` | `python/agentsam_sdk/repository/inspect.py` (`--dupes`); CLI `agentsam repository inspect` |
7
7
  | 1.8.0 | 2026-08-02T13:57:12.080Z | `580547301e370b77ec26bd72558979d335feedd9` | `python/` + `protocol/` in npm `files`; `repository.scan_bloat`; shell-kit folded to `packages/agentsam-shell-kit` (private). SDK git at publish: `ac52669b89ddb16fad87c42fe53f2c93ef13bd1a`. |
8
8
 
9
- **Pairing (ready to publish):** `iam@fc1628505cb3c9946c149e4c468c2e264d6f381e` ↔ `@inneranimalmedia/agentsam-sdk@1.9.0`
9
+ **Pairing :** `iam@fc1628505cb3c9946c149e4c468c2e264d6f381e` ↔ `@inneranimalmedia/agentsam-sdk@1.9.0`
@@ -0,0 +1,11 @@
1
+ # AgentSam SDK — Identity module
2
+
3
+ See [`packages/identity/README.md`](../packages/identity/README.md).
4
+
5
+ Customer import:
6
+
7
+ ```js
8
+ import { createIdentityClient } from '@inneranimalmedia/agentsam-sdk';
9
+ ```
10
+
11
+ Alpha: `@inneranimalmedia/agentsam-sdk@2.0.0-alpha.identity`
@@ -1,22 +1,15 @@
1
- # Gorilla Shell (Phase 0)
1
+ # Gorilla Shell example (superseded)
2
2
 
3
- Game-feel terminal UI prototype — consolidated from [InnerAnimal/gorilla-mode](https://github.com/InnerAnimal/gorilla-mode) into **Agent Sam SDK**.
3
+ **Canonical scaffold:** [`../../templates/gorilla-shell/`](../../templates/gorilla-shell/)
4
4
 
5
- This is the **unique install experience** layer: pixel HUD, themed moods, slash-command demos, deploy/benchmark scenarios. Phase 1 connects real PTY via ExecOS.
6
-
7
- ## Run locally
5
+ This directory previously mirrored an older demo that hard-coded IAM D1/host
6
+ literals. Run and customize from `templates/gorilla-shell` instead:
8
7
 
9
8
  ```bash
10
- cd examples/gorilla-shell
9
+ cd templates/gorilla-shell
11
10
  npm install
12
11
  npm run dev
13
12
  ```
14
13
 
15
- ## What you see
16
-
17
- - Gorilla launch screen + sprite reactions
18
- - Themes: NIGHT, DAY, LAVA, VOID
19
- - Six demo scenarios (deploy, benchmark, D1, tail, samiam, wrangler)
20
- - Slash command registry lives in `../../src/lib/slash-commands.js`
21
-
22
- Full architecture: [docs/CLI_SHELL.md](../../docs/CLI_SHELL.md)
14
+ Demo CLI strings there use `{{PROJECT_NAME}}`, `{{D1_DATABASE_ID}}`,
15
+ `{{DASHBOARD_HOST}}`, and related placeholders — not live platform IDs.
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@inneranimalmedia/agentsam-sdk",
3
- "version": "1.9.0",
3
+ "version": "2.0.0-alpha.identity",
4
4
  "description": "Agent Sam is a full-stack AI agent SDK for autonomous task execution \u2014 covering data management, creative workflows, design commands, and multi-step agentic pipelines.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js",
9
+ "./identity": "./packages/identity/src/index.js",
10
+ "./identity/oauth/callback": "./packages/identity/src/oauth/callback.js",
9
11
  "./package.json": "./package.json"
10
12
  },
11
13
  "bin": {
@@ -14,6 +16,7 @@
14
16
  },
15
17
  "files": [
16
18
  "src",
19
+ "packages/identity",
17
20
  "templates",
18
21
  "docs",
19
22
  "examples",
@@ -26,10 +29,12 @@
26
29
  "DEVELOPMENT.md"
27
30
  ],
28
31
  "workspaces": [
29
- "packages/*"
32
+ "packages/agentsam-shell-kit",
33
+ "packages/identity"
30
34
  ],
31
35
  "scripts": {
32
- "test": "node test/smoke.mjs",
36
+ "test": "node test/smoke.mjs && npm --prefix packages/identity test",
37
+ "test:identity": "npm --prefix packages/identity test",
33
38
  "smoke": "node test/smoke.mjs",
34
39
  "prepublishOnly": "npm test"
35
40
  },
@@ -0,0 +1,68 @@
1
+ # Identity (`packages/identity`)
2
+
3
+ **Package #1** inside `@inneranimalmedia/agentsam-sdk` — not a separate npm product.
4
+
5
+ ```
6
+ inneranimalmedia (reference) → agentsam-sdk (product) → Legendary-OS (first external proof)
7
+ ```
8
+
9
+ ## Customer API
10
+
11
+ ```js
12
+ import { createIdentityClient } from '@inneranimalmedia/agentsam-sdk';
13
+
14
+ const identity = createIdentityClient({
15
+ portal: { brand: { name: 'Legendary OS', logoUrl: '/logo.svg' } },
16
+ });
17
+
18
+ identity.providers.google();
19
+ identity.login(request); // → Identity Service (server-side)
20
+ identity.session.fromRequest(request);
21
+ ```
22
+
23
+ ## Layout
24
+
25
+ ```
26
+ src/
27
+ core/ identity · sessions · accounts · recovery
28
+ providers/ google · github · gcp · email
29
+ oauth/ authorize/callback (extract from IAM — next sprint)
30
+ adapters/ inneranimalmedia · cloudflare-d1
31
+ contracts/
32
+ frontend/auth-portal/pages/ login · signup · reset (verbatim IAM HTML)
33
+ migrations/
34
+ examples/legendary-os/
35
+ ```
36
+
37
+ ## SDK philosophy
38
+
39
+ Ship: contracts, clients, adapters, scaffolding.
40
+
41
+ Do **not** ship in the client bundle: password DB logic, OAuth secrets, D1 assumptions.
42
+
43
+ ## Publish (alpha)
44
+
45
+ From `main` after tests pass:
46
+
47
+ ```bash
48
+ npm test
49
+ npm publish --tag alpha
50
+ ```
51
+
52
+ Install in a customer app:
53
+
54
+ ```bash
55
+ npm install @inneranimalmedia/agentsam-sdk@alpha
56
+ # or pin: @inneranimalmedia/agentsam-sdk@2.0.0-alpha.identity
57
+ ```
58
+
59
+ Subpath imports:
60
+
61
+ ```js
62
+ import { createIdentityClient } from '@inneranimalmedia/agentsam-sdk';
63
+ import { finalizeInboundOAuth } from '@inneranimalmedia/agentsam-sdk/identity/oauth/callback';
64
+ ```
65
+
66
+ ## IAM design lab
67
+
68
+ `inneranimalmedia/packages/agentsam-identity/` — contracts experiments only; **not** the package owner.
@@ -0,0 +1,26 @@
1
+ # Backend — identity runtime
2
+
3
+ Worker-side identity: providers, OAuth finalize, sessions, adapters.
4
+
5
+ ## IAM extraction map (stable production — do not rewrite)
6
+
7
+ | SDK target | IAM source today | Notes |
8
+ |------------|------------------|-------|
9
+ | `providers/google/*` | `src/core/auth/providers/google.js` | ✅ ported Sprint 2 |
10
+ | `providers/github/*` | `src/core/auth/providers/github.js` | ✅ ported Sprint 2 |
11
+ | `oauth/callback.js` | `src/api/oauth-login-callbacks.js` → `finalizeInboundOAuth` | **bulletproof path** |
12
+ | `oauth/authorize.js` | `src/api/auth.js` OAuth start handlers | PKCE + state |
13
+ | `core/session-service.js` | `src/core/auth.js` | cookie, `createLoginSession`, revoke |
14
+ | `core/account-linking.js` | `ensureIdentityPlaneBeforeSession`, `ensureAppUser` | account_identities |
15
+ | `adapters/inneranimalmedia/*` | D1 writes in auth side-effects | tenant/workspace stays adapter-only |
16
+
17
+ ## Rule
18
+
19
+ Move files; **do not** change OAuth behavior until IAM runs parallel proof (existing users, Google/GitHub callbacks, session cookie).
20
+
21
+ ## Public entry
22
+
23
+ ```js
24
+ import { createIdentity, GoogleProvider } from '@agentsam/identity';
25
+ // resolves to backend/index.js
26
+ ```
@@ -0,0 +1,16 @@
1
+ # Getting started with @agentsam/identity
2
+
3
+ 1. Install: `npm install @agentsam/identity`
4
+ 2. Configure providers and storage adapter
5
+ 3. Wire Worker routes for OAuth callback + session cookie
6
+
7
+ Full plan: Phase 1–9 in IAM `plans/active/AGENTSAM-IDENTITY-SDK-PRODUCTIZATION-2026-08.md`
8
+
9
+ ```js
10
+ import { createIdentity, GoogleProvider } from '@agentsam/identity';
11
+
12
+ const identity = createIdentity({
13
+ providers: [GoogleProvider],
14
+ storage: { adapter: 'cloudflare-d1', binding: 'DB' },
15
+ });
16
+ ```
@@ -0,0 +1,5 @@
1
+ # Cloudflare Worker + D1 example (Phase 7)
2
+
3
+ Minimal worker wiring for `@agentsam/identity` with `adapters/cloudflare-d1`.
4
+
5
+ Scaffold lands in Sprint 5 after IAM adapter proves the storage contract.
@@ -0,0 +1,27 @@
1
+ # Legendary-OS — @agentsam/identity customer proof
2
+
3
+ Target: fresh install path (Phase 7).
4
+
5
+ ## Day 1
6
+
7
+ ```bash
8
+ npm install @agentsam/identity
9
+ npx agentsam identity init --adapter cloudflare-d1
10
+ ```
11
+
12
+ ## Day 2 proof checklist
13
+
14
+ - [ ] Email signup → session cookie
15
+ - [ ] Email login
16
+ - [ ] Logout clears session
17
+ - [ ] Google OAuth round-trip
18
+ - [ ] GitHub OAuth round-trip
19
+
20
+ ## Day 3
21
+
22
+ - [ ] Workspace creation on first login
23
+ - [ ] Dashboard requires auth
24
+
25
+ Record curl transcripts and D1 row counts in `PROOF.md` when complete.
26
+
27
+ See: `inneranimalmedia/plans/active/AGENTSAM-IDENTITY-SDK-PRODUCTIZATION-2026-08.md`
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@inneranimalmedia/agentsam-sdk-identity",
3
+ "version": "2.0.0-alpha.identity",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "Identity module for @inneranimalmedia/agentsam-sdk (workspace — publish via root SDK)",
7
+ "main": "./src/index.js",
8
+ "exports": {
9
+ ".": "./src/index.js",
10
+ "./provider-contract": "./src/provider-contract.js",
11
+ "./providers": "./src/providers/index.js",
12
+ "./providers/google": "./src/providers/google/index.js",
13
+ "./providers/github": "./src/providers/github/index.js",
14
+ "./providers/gcp": "./src/providers/gcp/index.js",
15
+ "./providers/email": "./src/providers/email/index.js",
16
+ "./contracts/identity": "./src/contracts/identity.js",
17
+ "./contracts/session": "./src/contracts/session.js",
18
+ "./contracts/provider": "./src/contracts/provider.js",
19
+ "./contracts/external-identity": "./src/contracts/external-identity.js",
20
+ "./oauth/callback": "./src/oauth/callback.js",
21
+ "./frontend/auth-portal/*": "./src/frontend/auth-portal/*"
22
+ },
23
+ "scripts": {
24
+ "test": "node --test tests/*.test.mjs"
25
+ },
26
+ "engines": {
27
+ "node": ">=20"
28
+ }
29
+ }
@@ -0,0 +1,4 @@
1
+ /** Cloudflare D1 storage adapter — Sprint 5. */
2
+ export function createCloudflareD1Adapter(_env) {
3
+ throw new Error('cloudflare_d1_adapter_not_implemented');
4
+ }
@@ -0,0 +1,7 @@
1
+ # InnerAnimalMedia adapter (Sprint 3+)
2
+
3
+ Maps IAM D1 tables (`auth_users`, `accounts`, `account_identities`, `auth_sessions`, …)
4
+ into portable AgentSam Identity contracts.
5
+
6
+ IAM-specific behavior (tenant resolution, workspace membership, OAuth callback wiring)
7
+ stays here — not in `providers/`.
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Portable external identity shape — storage-agnostic.
3
+ * Adapters map this into application-specific account_identities rows.
4
+ */
5
+
6
+ /** @typedef {import('../provider-contract.js').NormalizedExternalIdentity} NormalizedExternalIdentity */
7
+
8
+ /**
9
+ * @param {Partial<NormalizedExternalIdentity> & { provider: string; subject: string }} input
10
+ * @returns {NormalizedExternalIdentity}
11
+ */
12
+ export function normalizeExternalIdentity(input) {
13
+ const provider = String(input?.provider || '').trim().toLowerCase();
14
+ const subject = String(input?.subject || '').trim();
15
+ if (!provider || !subject) {
16
+ throw new Error('external_identity_requires_provider_and_subject');
17
+ }
18
+
19
+ const email =
20
+ typeof input?.email === 'string' && input.email.trim()
21
+ ? input.email.trim().toLowerCase()
22
+ : null;
23
+
24
+ return {
25
+ provider,
26
+ subject,
27
+ email,
28
+ emailVerified: input?.emailVerified === true,
29
+ name: typeof input?.name === 'string' && input.name.trim() ? input.name.trim() : null,
30
+ avatar: typeof input?.avatar === 'string' && input.avatar.trim() ? input.avatar.trim() : null,
31
+ username:
32
+ typeof input?.username === 'string' && input.username.trim() ? input.username.trim() : null,
33
+ raw: input?.raw && typeof input.raw === 'object' ? input.raw : null,
34
+ };
35
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * AgentSam Identity contracts.
3
+ *
4
+ * These are intentionally storage-agnostic. The IAM adapter maps them to
5
+ * existing D1 tables (auth_users, accounts, memberships, sessions).
6
+ */
7
+
8
+ export const IdentityContractVersion = '1.0';
9
+
10
+ export function normalizeIdentityUser(user) {
11
+ return {
12
+ id: user?.id ?? null,
13
+ email: user?.email ?? null,
14
+ displayName: user?.displayName ?? user?.name ?? null,
15
+ type: user?.type ?? 'human',
16
+ };
17
+ }
@@ -0,0 +1,8 @@
1
+ /** External identity provider contract. */
2
+
3
+ export const IdentityProviders = Object.freeze([
4
+ 'email',
5
+ 'google',
6
+ 'github',
7
+ 'gcp',
8
+ ]);
@@ -0,0 +1,9 @@
1
+ /** Session contract shared by identity adapters. */
2
+ export function normalizeIdentitySession(session) {
3
+ return {
4
+ id: session?.id ?? null,
5
+ userId: session?.userId ?? session?.user_id ?? null,
6
+ expiresAt: session?.expiresAt ?? session?.expires_at ?? null,
7
+ revokedAt: session?.revokedAt ?? session?.revoked_at ?? null,
8
+ };
9
+ }
@@ -0,0 +1,4 @@
1
+ /** Account linking contract surface — implementation in adapters + Identity Service. */
2
+ export function accountLinkingNotConfigured() {
3
+ throw new Error('account_linking_requires_identity_service_adapter');
4
+ }
@@ -0,0 +1,34 @@
1
+ import { AUTH_LOGIN_PATH, AUTH_SIGNUP_PATH } from './constants.js';
2
+
3
+ export function sanitizeBrowserNextPath(raw) {
4
+ if (raw == null) return null;
5
+ const s = String(raw).trim();
6
+ if (!s.startsWith('/') || s.startsWith('//')) return null;
7
+ if (/^[a-zA-Z][a-zA-Z+\-.]*:/.test(s) || s.includes('://')) return null;
8
+
9
+ let pathname = s;
10
+ let search = '';
11
+ const q = s.indexOf('?');
12
+ if (q !== -1) {
13
+ pathname = s.slice(0, q);
14
+ search = s.slice(q);
15
+ }
16
+ const lower = pathname.toLowerCase();
17
+ if (lower === '/login' || lower === '/auth/signin') pathname = AUTH_LOGIN_PATH;
18
+ else if (lower === '/signup' || lower === '/auth/register') pathname = AUTH_SIGNUP_PATH;
19
+
20
+ return pathname + search;
21
+ }
22
+
23
+ /** Customer adapter may supply apexDomain override for Set-Cookie. */
24
+ export function getApexDomain(hostname, opts = {}) {
25
+ const override = opts?.apexDomain != null ? String(opts.apexDomain).trim() : '';
26
+ if (override) return override;
27
+ if (!hostname) return '';
28
+ const parts = hostname.split('.');
29
+ if (parts.length >= 2) {
30
+ if (hostname.endsWith('.workers.dev') || hostname.endsWith('.pages.dev')) return '';
31
+ return parts.slice(-2).join('.');
32
+ }
33
+ return hostname;
34
+ }
@@ -0,0 +1,9 @@
1
+ /** SDK-portable session/auth path constants. */
2
+ export const AUTH_COOKIE_NAME = 'session';
3
+ export const AUTH_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;
4
+ export const MIN_AGENT_SESSION_TTL_SECONDS = 60;
5
+ export const MAX_AGENT_SESSION_TTL_SECONDS = 86400;
6
+ export const DEFAULT_AGENT_SESSION_TTL_SECONDS = 900;
7
+ export const AUTH_LOGIN_PATH = '/auth/login';
8
+ export const AUTH_SIGNUP_PATH = '/auth/signup';
9
+ export const DASHBOARD_AFTER_LOGIN_PATH = '/dashboard/home';
@@ -0,0 +1,8 @@
1
+ export class AuthError extends Error {
2
+ constructor(message, opts = {}) {
3
+ super(message);
4
+ this.name = 'AuthError';
5
+ this.status = opts.status ?? 401;
6
+ this.code = opts.code ?? 'UNAUTHORIZED';
7
+ }
8
+ }
@@ -0,0 +1,10 @@
1
+ function jsonStringifySafe(value) {
2
+ return JSON.stringify(value, (_k, v) => (typeof v === 'bigint' ? v.toString() : v));
3
+ }
4
+
5
+ export function jsonResponse(body, status = 200) {
6
+ return new Response(jsonStringifySafe(body), {
7
+ status: Number(status) || 200,
8
+ headers: { 'Content-Type': 'application/json' },
9
+ });
10
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Identity core — user resolution (client surface).
3
+ */
4
+ export { normalizeIdentityUser, IdentityContractVersion } from '../contracts/identity.js';
@@ -0,0 +1,10 @@
1
+ export { AuthError } from './errors.js';
2
+ export { sanitizeBrowserNextPath, getApexDomain } from './browser-paths.js';
3
+ export { verifyPassword, hashPassword } from './password-crypto.js';
4
+ export { jsonResponse } from './http-json.js';
5
+ export {
6
+ trimSessionField,
7
+ buildSessionKvPayload,
8
+ normalizeIdentitySession,
9
+ isInboundOAuthSuccess,
10
+ } from './sessions.js';
@@ -0,0 +1,53 @@
1
+ function hexToBytes(hex) {
2
+ const arr = [];
3
+ for (let i = 0; i < hex.length; i += 2) arr.push(parseInt(hex.slice(i, i + 2), 16));
4
+ return new Uint8Array(arr);
5
+ }
6
+
7
+ export async function verifyPassword(password, saltHex, hashHex) {
8
+ try {
9
+ const salt = hexToBytes(saltHex);
10
+ const key = await crypto.subtle.importKey(
11
+ 'raw',
12
+ new TextEncoder().encode(password),
13
+ { name: 'PBKDF2' },
14
+ false,
15
+ ['deriveBits'],
16
+ );
17
+ const derived = await crypto.subtle.deriveBits(
18
+ { name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-256' },
19
+ key,
20
+ 256,
21
+ );
22
+ const derivedHex = Array.from(new Uint8Array(derived))
23
+ .map((b) => b.toString(16).padStart(2, '0'))
24
+ .join('');
25
+ return derivedHex === hashHex.toLowerCase();
26
+ } catch (e) {
27
+ console.warn('[verifyPassword] failed', e.message);
28
+ return false;
29
+ }
30
+ }
31
+
32
+ export async function hashPassword(password) {
33
+ const salt = crypto.getRandomValues(new Uint8Array(32));
34
+ const saltHex = Array.from(salt)
35
+ .map((b) => b.toString(16).padStart(2, '0'))
36
+ .join('');
37
+ const key = await crypto.subtle.importKey(
38
+ 'raw',
39
+ new TextEncoder().encode(password),
40
+ { name: 'PBKDF2' },
41
+ false,
42
+ ['deriveBits'],
43
+ );
44
+ const derived = await crypto.subtle.deriveBits(
45
+ { name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-256' },
46
+ key,
47
+ 256,
48
+ );
49
+ const hashHex = Array.from(new Uint8Array(derived))
50
+ .map((b) => b.toString(16).padStart(2, '0'))
51
+ .join('');
52
+ return { saltHex, hashHex };
53
+ }
@@ -0,0 +1,4 @@
1
+ /** Password reset / recovery — client routes only; tokens verified server-side. */
2
+ export function recoveryNotConfigured() {
3
+ throw new Error('recovery_requires_identity_service_adapter');
4
+ }