run402-mcp 3.7.14 → 3.8.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 +7 -5
- package/core/dist/config.d.ts +3 -0
- package/core/dist/config.d.ts.map +1 -1
- package/core/dist/config.js +9 -0
- package/core/dist/config.js.map +1 -1
- package/core/dist/keystore.d.ts +11 -3
- package/core/dist/keystore.d.ts.map +1 -1
- package/core/dist/keystore.js +103 -68
- package/core/dist/keystore.js.map +1 -1
- package/core/dist/profile-state.d.ts +29 -0
- package/core/dist/profile-state.d.ts.map +1 -0
- package/core/dist/profile-state.js +137 -0
- package/core/dist/profile-state.js.map +1 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +19 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/tools/deploy.d.ts +16 -16
- package/dist/tools/{project-keys.d.ts → project-key-cache-export.d.ts} +5 -3
- package/dist/tools/project-key-cache-export.d.ts.map +1 -0
- package/dist/tools/project-key-cache-export.js +29 -0
- package/dist/tools/project-key-cache-export.js.map +1 -0
- package/dist/tools/{project-info.d.ts → project-key-cache-status.d.ts} +3 -3
- package/dist/tools/project-key-cache-status.d.ts.map +1 -0
- package/dist/tools/project-key-cache-status.js +34 -0
- package/dist/tools/project-key-cache-status.js.map +1 -0
- package/package.json +1 -1
- package/sdk/README.md +22 -4
- package/sdk/core-dist/config.d.ts +3 -0
- package/sdk/core-dist/config.js +9 -0
- package/sdk/core-dist/keystore.d.ts +11 -3
- package/sdk/core-dist/keystore.js +103 -68
- package/sdk/core-dist/profile-state.d.ts +29 -0
- package/sdk/core-dist/profile-state.js +137 -0
- package/sdk/dist/credentials.d.ts +24 -7
- package/sdk/dist/credentials.d.ts.map +1 -1
- package/sdk/dist/credentials.js +4 -4
- package/sdk/dist/errors.d.ts +27 -1
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +61 -3
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/index.d.ts +7 -2
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +8 -3
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/kernel.d.ts +2 -0
- package/sdk/dist/kernel.d.ts.map +1 -1
- package/sdk/dist/kernel.js +5 -1
- package/sdk/dist/kernel.js.map +1 -1
- package/sdk/dist/namespaces/ai.d.ts.map +1 -1
- package/sdk/dist/namespaces/ai.js +5 -10
- package/sdk/dist/namespaces/ai.js.map +1 -1
- package/sdk/dist/namespaces/apps.d.ts.map +1 -1
- package/sdk/dist/namespaces/apps.js +5 -13
- package/sdk/dist/namespaces/apps.js.map +1 -1
- package/sdk/dist/namespaces/assets.d.ts.map +1 -1
- package/sdk/dist/namespaces/assets.js +9 -22
- package/sdk/dist/namespaces/assets.js.map +1 -1
- package/sdk/dist/namespaces/auth.d.ts.map +1 -1
- package/sdk/dist/namespaces/auth.js +16 -43
- package/sdk/dist/namespaces/auth.js.map +1 -1
- package/sdk/dist/namespaces/contracts.d.ts.map +1 -1
- package/sdk/dist/namespaces/contracts.js +12 -31
- package/sdk/dist/namespaces/contracts.js.map +1 -1
- package/sdk/dist/namespaces/credentials.d.ts +48 -0
- package/sdk/dist/namespaces/credentials.d.ts.map +1 -0
- package/sdk/dist/namespaces/credentials.js +115 -0
- package/sdk/dist/namespaces/credentials.js.map +1 -0
- package/sdk/dist/namespaces/domains.d.ts +8 -2
- package/sdk/dist/namespaces/domains.d.ts.map +1 -1
- package/sdk/dist/namespaces/domains.js +41 -23
- package/sdk/dist/namespaces/domains.js.map +1 -1
- package/sdk/dist/namespaces/email.d.ts.map +1 -1
- package/sdk/dist/namespaces/email.js +8 -19
- package/sdk/dist/namespaces/email.js.map +1 -1
- package/sdk/dist/namespaces/functions.d.ts.map +1 -1
- package/sdk/dist/namespaces/functions.js +14 -37
- package/sdk/dist/namespaces/functions.js.map +1 -1
- package/sdk/dist/namespaces/jobs.d.ts.map +1 -1
- package/sdk/dist/namespaces/jobs.js +8 -19
- package/sdk/dist/namespaces/jobs.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +4 -4
- package/sdk/dist/namespaces/projects.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.js +18 -42
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/secrets.d.ts.map +1 -1
- package/sdk/dist/namespaces/secrets.js +5 -10
- package/sdk/dist/namespaces/secrets.js.map +1 -1
- package/sdk/dist/namespaces/sender-domain.d.ts.map +1 -1
- package/sdk/dist/namespaces/sender-domain.js +6 -16
- package/sdk/dist/namespaces/sender-domain.js.map +1 -1
- package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
- package/sdk/dist/namespaces/subdomains.js +5 -10
- package/sdk/dist/namespaces/subdomains.js.map +1 -1
- package/sdk/dist/node/credentials.d.ts +18 -4
- package/sdk/dist/node/credentials.d.ts.map +1 -1
- package/sdk/dist/node/credentials.js +37 -9
- package/sdk/dist/node/credentials.js.map +1 -1
- package/sdk/dist/node/index.d.ts +4 -2
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +2 -1
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/target-profile.d.ts.map +1 -1
- package/sdk/dist/node/target-profile.js +6 -4
- package/sdk/dist/node/target-profile.js.map +1 -1
- package/sdk/dist/project-auth-classification.d.ts +125 -0
- package/sdk/dist/project-auth-classification.d.ts.map +1 -0
- package/sdk/dist/project-auth-classification.js +135 -0
- package/sdk/dist/project-auth-classification.js.map +1 -0
- package/sdk/dist/project-credentials.d.ts +4 -0
- package/sdk/dist/project-credentials.d.ts.map +1 -0
- package/sdk/dist/project-credentials.js +9 -0
- package/sdk/dist/project-credentials.js.map +1 -0
- package/dist/tools/project-info.d.ts.map +0 -1
- package/dist/tools/project-info.js +0 -29
- package/dist/tools/project-info.js.map +0 -1
- package/dist/tools/project-keys.d.ts.map +0 -1
- package/dist/tools/project-keys.js +0 -24
- package/dist/tools/project-keys.js.map +0 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getSdk } from "../sdk.js";
|
|
3
|
+
import { mapSdkError } from "../errors.js";
|
|
4
|
+
export const projectKeyCacheStatusSchema = {
|
|
5
|
+
project_id: z.string().describe("Project ID to inspect in the local project-key credential cache"),
|
|
6
|
+
};
|
|
7
|
+
export async function handleProjectKeyCacheStatus(args) {
|
|
8
|
+
try {
|
|
9
|
+
const status = await getSdk().credentials.projectKeys.status(args.project_id);
|
|
10
|
+
const lines = [
|
|
11
|
+
`## Local Project-Key Cache: ${args.project_id}`,
|
|
12
|
+
``,
|
|
13
|
+
`| Field | Value |`,
|
|
14
|
+
`|-------|-------|`,
|
|
15
|
+
`| source | \`${status.source}\` |`,
|
|
16
|
+
`| configured | ${status.configured} |`,
|
|
17
|
+
`| has_anon_key | ${status.has_anon_key} |`,
|
|
18
|
+
`| has_service_key | ${status.has_service_key} |`,
|
|
19
|
+
`| anon_key_prefix | ${status.anon_key_prefix ? `\`${status.anon_key_prefix}\`` : "(none)"} |`,
|
|
20
|
+
`| service_key_prefix | ${status.service_key_prefix ? `\`${status.service_key_prefix}\`` : "(none)"} |`,
|
|
21
|
+
`| anon_key_fingerprint | ${status.anon_key_fingerprint ? `\`${status.anon_key_fingerprint}\`` : "(none)"} |`,
|
|
22
|
+
`| service_key_fingerprint | ${status.service_key_fingerprint ? `\`${status.service_key_fingerprint}\`` : "(none)"} |`,
|
|
23
|
+
`| site_url | ${status.site_url ? `\`${status.site_url}\`` : "(none)"} |`,
|
|
24
|
+
`| cached_at | ${status.cached_at ?? "(unknown)"} |`,
|
|
25
|
+
`| profile | ${status.profile ? `\`${status.profile}\`` : "(unknown)"} |`,
|
|
26
|
+
`| cache_path | ${status.cache_path ? `\`${status.cache_path}\`` : "(unknown)"} |`,
|
|
27
|
+
];
|
|
28
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
return mapSdkError(err, "reading local project-key cache status");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=project-key-cache-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-key-cache-status.js","sourceRoot":"","sources":["../../src/tools/project-key-cache-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iEAAiE,CAAC;CACnG,CAAC;AAIF,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,IAEjD;IACC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAE9E,MAAM,KAAK,GAAG;YACZ,+BAA+B,IAAI,CAAC,UAAU,EAAE;YAChD,EAAE;YACF,mBAAmB;YACnB,mBAAmB;YACnB,gBAAgB,MAAM,CAAC,MAAM,MAAM;YACnC,kBAAkB,MAAM,CAAC,UAAU,IAAI;YACvC,oBAAoB,MAAM,CAAC,YAAY,IAAI;YAC3C,uBAAuB,MAAM,CAAC,eAAe,IAAI;YACjD,uBAAuB,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,eAAe,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI;YAC9F,0BAA0B,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,kBAAkB,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI;YACvG,4BAA4B,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,oBAAoB,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI;YAC7G,+BAA+B,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,uBAAuB,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI;YACtH,gBAAgB,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI;YACzE,iBAAiB,MAAM,CAAC,SAAS,IAAI,WAAW,IAAI;YACpD,eAAe,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI;YACzE,kBAAkB,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI;SACnF,CAAC;QAEF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACjE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,EAAE,wCAAwC,CAAC,CAAC;IACpE,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "run402-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "MCP server for Run402 — AI-native Postgres databases with REST API, auth, storage, and row-level security. Pay with x402 USDC micropayments.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/sdk/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm install @run402/sdk
|
|
|
10
10
|
|
|
11
11
|
| Import | Use when |
|
|
12
12
|
|---|---|
|
|
13
|
-
| `@run402/sdk/node` | Running in Node 22 with the local
|
|
13
|
+
| `@run402/sdk/node` | Running in Node 22 with the local profile state, project-key credential cache, and allowance. Auto-loads the configured API base, profile `credentials/project-keys.v1.json`, and signs x402 payments from `~/.config/run402/allowance.json`. Includes `r.actions.run(...)`, `r.up(...)`, `r.sites.deployDir(dir)`, `fileSetFromDir(dir)`, `loadDeployManifest(path)`, `normalizeDeployManifest(input)`, and `resolveRun402TargetProfile()`. |
|
|
14
14
|
| `@run402/sdk` | Isomorphic — works in Node, Deno, Bun, V8 isolates. No filesystem access. Bring your own `CredentialsProvider` (a session-token shim, a remote vault, anything that resolves project keys + auth headers). |
|
|
15
15
|
|
|
16
16
|
## Quick start (Node)
|
|
@@ -58,7 +58,7 @@ await r.up(
|
|
|
58
58
|
|
|
59
59
|
For a self-hosted Run402 Core Gateway, run `run402 init --api-base=http://my-core:4020` once. The Node SDK then targets that API base by default; explicit `run402({ apiBase })` still wins.
|
|
60
60
|
|
|
61
|
-
App build scripts should use the same target/profile store instead of parsing `target.json` or
|
|
61
|
+
App build scripts should use the same target/profile store instead of parsing `target.json` or project-key cache files:
|
|
62
62
|
|
|
63
63
|
```ts
|
|
64
64
|
import { resolveRun402TargetProfile } from "@run402/sdk/node";
|
|
@@ -89,6 +89,8 @@ resolveRun402TargetProfile({
|
|
|
89
89
|
|
|
90
90
|
Most operations are project-scoped. Bind once and skip the id arg on every call:
|
|
91
91
|
|
|
92
|
+
`r.projects.list()` and `r.projects.get(id)` are server-authoritative project reads. `r.projects.use(id)` validates the project with the current principal and stores only an active project id in profile state; it does not require local project-key cache membership. `r.project(id)` binds the id without local lookup. Each namespace then follows its declared auth mode: control-plane operations such as custom domains default to principal/delegate auth with explicit `project_id`, while true data-plane/key operations use local project credentials and fail with `PROJECT_CREDENTIAL_NOT_FOUND` when the selected profile lacks cached keys.
|
|
93
|
+
|
|
92
94
|
```ts
|
|
93
95
|
const p = await r.useProject(projectId); // persists active project + returns scoped handle
|
|
94
96
|
await p.assets.put("hello.txt", { content: "hi" }); // no projectId arg
|
|
@@ -98,6 +100,18 @@ await p.apply({ site: { replace: files({ "index.html": "<h1>hi</h1>" }) } });
|
|
|
98
100
|
|
|
99
101
|
`r.useProject(id)` writes the active project to the keystore (shared with concurrent CLI runs). For transient in-script scoping that does NOT mutate that state, use `r.project(id)` (or `r.project()` with no arg to resolve from whatever the keystore currently considers active).
|
|
100
102
|
|
|
103
|
+
Local project keys live behind an explicit credential-cache namespace. These helpers are local/offline and are not authoritative project reads:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
const status = await r.credentials.projectKeys.status(projectId); // redacted
|
|
107
|
+
const serviceKey = process.env.RUN402_SERVICE_KEY!;
|
|
108
|
+
await r.credentials.projectKeys.import(projectId, { serviceKey });
|
|
109
|
+
const keys = await r.credentials.projectKeys.export(projectId, { reveal: true });
|
|
110
|
+
await r.credentials.projectKeys.remove(projectId);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`status`/`list` report `source: "local_cache"`, profile/cache-path provenance, key presence, prefixes, and fingerprints without full secrets. `export(..., { reveal: true })` is the only SDK helper that emits cached secret key material.
|
|
114
|
+
|
|
101
115
|
## Quick start (isomorphic)
|
|
102
116
|
|
|
103
117
|
```ts
|
|
@@ -119,7 +133,8 @@ The `CredentialsProvider` interface has two required methods (`getAuth`, `getPro
|
|
|
119
133
|
| Namespace | Highlights |
|
|
120
134
|
|---|---|
|
|
121
135
|
| `actions` | Node entry only (`@run402/sdk/node`). Generic recursive action runner: `actions.run({ type: Run402Action.Up | ProjectsProvision | TierSet, ... })`; `r.up(input, opts)` is the convenience for repo-level manifest deploys. Recursive mutations are approval-gated; `mode: "check" | "printSpec" | "plan" | { kind: "applyReviewed" }` distinguishes local validation, gateway review, and exact reviewed apply. Child gateway mutations derive idempotency keys from the root action. |
|
|
122
|
-
| `projects` | `provision`, `delete`, `list`, `sql`, `rest`, `validateExpose`, `applyExpose`, `getExpose`, `getUsage`, `getSchema`, `info`, `keys`, `
|
|
136
|
+
| `projects` | `provision`, `delete`, `list`, `get`, `use`, `active`, `sql`, `rest`, `validateExpose`, `applyExpose`, `getExpose`, `getUsage`, `getSchema`, `info`, `keys`, `pin`, `getQuote`. `list`/`get`/`use` are server-authoritative; local key reads are moving to `credentials.projectKeys`. |
|
|
137
|
+
| `credentials` | `projectKeys.list`, `projectKeys.status`, `projectKeys.import`, `projectKeys.export`, `projectKeys.remove` for explicit local project-key cache management. |
|
|
123
138
|
| `r.project(id).apply` | **The unified apply primitive.** Callable hero — `r.project(id).apply(spec)` for atomic mixed writes (release slices + assets slice). Sub-methods: `.plan`, `.start`, `.resume`, `.upload`, `.commit`, `.status`, `.list`, `.events`, `.resolve`, `.getRelease`, `.getActiveRelease`, `.diff`. Underlying engine routes to `/apply/v1/*`. |
|
|
124
139
|
| `ci` | GitHub Actions OIDC federation over `/ci/v1/*`: `createBinding`, `listBindings`, `getBinding`, `revokeBinding`, `exchangeToken`; plus canonical delegation helpers. `createBinding` accepts `asset_key_scopes` for per-key CI write authorization. |
|
|
125
140
|
| `r.project(id).sites` | `deployDir` — Node entry only (`@run402/sdk/node`); thin wrapper over `r.project(id).apply({ site: dir(...) })` |
|
|
@@ -577,13 +592,16 @@ All failures throw subclasses of `Run402Error`. Every subclass carries a stable
|
|
|
577
592
|
| Class | `kind` | When | Notable fields |
|
|
578
593
|
|---|---|---|---|
|
|
579
594
|
| `PaymentRequired` | `"payment_required"` | HTTP 402 | x402 payment requirements in `body` |
|
|
580
|
-
| `ProjectNotFound` | `"project_not_found"` |
|
|
595
|
+
| `ProjectNotFound` | `"project_not_found"` | Server-authoritative project lookup/authorization reports not found or hidden | `projectId` |
|
|
596
|
+
| `ProjectCredentialNotFound` | `"local_error"` | A local project-key cache entry is required but missing for the selected profile | `projectId`, `details.source="local_cache"`, `nextActions` |
|
|
581
597
|
| `Unauthorized` | `"unauthorized"` | HTTP 401 / 403 | — |
|
|
582
598
|
| `ApiError` | `"api_error"` | Other non-2xx responses | `status`, `body` |
|
|
583
599
|
| `NetworkError` | `"network_error"` | Fetch rejected with no HTTP response | `cause` |
|
|
584
600
|
| `LocalError` | `"local_error"` | Local-host issues (filesystem, signing) | `cause` |
|
|
585
601
|
| `Run402DeployError` | `"deploy_error"` | Structured envelope from the deploy state machine (v1.34+) | `code`, `phase`, `operationId`, `safeToRetry`, `mutationState`, `nextActions` |
|
|
586
602
|
|
|
603
|
+
Project credential codes are deliberately distinct from project existence/authz. Branch on `isProjectCredentialNotFound`, `isProjectCredentialInvalid`, `isProjectCredentialExpired`, `isProjectCredentialProjectMismatch`, or the broad `isProjectCredentialError`. Gateway-returned `PROJECT_CREDENTIAL_INVALID`, `PROJECT_CREDENTIAL_EXPIRED`, and `PROJECT_CREDENTIAL_PROJECT_MISMATCH` pass through unchanged; the SDK does not rewrite them to `PROJECT_CREDENTIAL_NOT_FOUND`.
|
|
604
|
+
|
|
587
605
|
**Branch with type guards, not `instanceof`.** `instanceof X` is an identity
|
|
588
606
|
check on the class object — it fails silently when the consumer's runtime
|
|
589
607
|
holds a different copy of the SDK (duplicate npm installs, bundler chunk
|
|
@@ -45,6 +45,9 @@ export declare function getProfilesDir(): string;
|
|
|
45
45
|
*/
|
|
46
46
|
export declare function getConfigDir(): string;
|
|
47
47
|
export declare function getKeystorePath(): string;
|
|
48
|
+
export declare function getLegacyProjectsPath(): string;
|
|
49
|
+
export declare function getProjectCredentialsPath(): string;
|
|
50
|
+
export declare function getProfileStatePath(): string;
|
|
48
51
|
export declare function getApiTargetConfigPath(): string;
|
|
49
52
|
export declare function readApiTargetConfig(path?: string): ApiTargetConfig | null;
|
|
50
53
|
export declare function saveApiTargetConfig(config: ApiTargetConfig, path?: string): void;
|
package/sdk/core-dist/config.js
CHANGED
|
@@ -119,8 +119,17 @@ export function getConfigDir() {
|
|
|
119
119
|
return profile === DEFAULT_PROFILE ? base : join(base, "profiles", profile);
|
|
120
120
|
}
|
|
121
121
|
export function getKeystorePath() {
|
|
122
|
+
return getProjectCredentialsPath();
|
|
123
|
+
}
|
|
124
|
+
export function getLegacyProjectsPath() {
|
|
122
125
|
return join(getConfigDir(), "projects.json");
|
|
123
126
|
}
|
|
127
|
+
export function getProjectCredentialsPath() {
|
|
128
|
+
return join(getConfigDir(), "credentials", "project-keys.v1.json");
|
|
129
|
+
}
|
|
130
|
+
export function getProfileStatePath() {
|
|
131
|
+
return join(getConfigDir(), "state.json");
|
|
132
|
+
}
|
|
124
133
|
export function getApiTargetConfigPath() {
|
|
125
134
|
return join(getConfigDir(), "target.json");
|
|
126
135
|
}
|
|
@@ -4,17 +4,25 @@ export interface StoredProject {
|
|
|
4
4
|
site_url?: string;
|
|
5
5
|
deployed_at?: string;
|
|
6
6
|
last_deployment_id?: string;
|
|
7
|
+
cached_at?: string;
|
|
8
|
+
source?: string;
|
|
7
9
|
}
|
|
8
10
|
export interface KeyStore {
|
|
11
|
+
version?: 1;
|
|
12
|
+
source?: "local_cache";
|
|
9
13
|
active_project_id?: string;
|
|
10
14
|
previous_active_project_id?: string;
|
|
11
15
|
projects: Record<string, StoredProject>;
|
|
16
|
+
migrated_from?: string;
|
|
17
|
+
migrated_at?: string;
|
|
12
18
|
}
|
|
13
19
|
/**
|
|
14
|
-
* Load the
|
|
15
|
-
* Auto-
|
|
20
|
+
* Load the project-key credential cache from disk.
|
|
21
|
+
* Auto-imports legacy `projects.json` formats into the new cache path when using
|
|
22
|
+
* the default location:
|
|
16
23
|
* - Array format (CLI legacy): [{project_id, ...}] → {projects: {id: {...}}}
|
|
17
|
-
* -
|
|
24
|
+
* - Object format: {active_project_id, projects} → credentials cache + state.json
|
|
25
|
+
* - Old metadata fields: tier/expires_at/lease_expires_at are stripped
|
|
18
26
|
*/
|
|
19
27
|
export declare function loadKeyStore(path?: string): KeyStore;
|
|
20
28
|
export declare function saveKeyStore(store: KeyStore, path?: string): void;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync, renameSync, chmodSync, rmdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, chmodSync, rmdirSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
|
-
import {
|
|
4
|
+
import { getLegacyProjectsPath, getProjectCredentialsPath } from "./config.js";
|
|
5
|
+
import { clearActiveProjectId as clearProfileActiveProjectId, getActiveProjectId as getProfileActiveProjectId, recordMigration, setActiveProjectId as setProfileActiveProjectId, } from "./profile-state.js";
|
|
5
6
|
function withFileLock(path, fn, { retries = 200, delayMs = 20 } = {}) {
|
|
6
7
|
const lockDir = path + ".lock";
|
|
7
8
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -29,56 +30,107 @@ function withFileLock(path, fn, { retries = 200, delayMs = 20 } = {}) {
|
|
|
29
30
|
}
|
|
30
31
|
throw new Error(`Could not acquire keystore lock after ${retries} retries: ${lockDir}`);
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
for (const item of parsed) {
|
|
46
|
-
if (item.project_id) {
|
|
47
|
-
projects[item.project_id] = {
|
|
48
|
-
anon_key: item.anon_key,
|
|
49
|
-
service_key: item.service_key,
|
|
50
|
-
...(item.site_url && { site_url: item.site_url }),
|
|
51
|
-
...(item.deployed_at && { deployed_at: item.deployed_at }),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
33
|
+
function normalizeParsedKeyStore(parsed) {
|
|
34
|
+
if (Array.isArray(parsed)) {
|
|
35
|
+
const projects = {};
|
|
36
|
+
for (const item of parsed) {
|
|
37
|
+
if (item.project_id) {
|
|
38
|
+
projects[item.project_id] = {
|
|
39
|
+
anon_key: item.anon_key,
|
|
40
|
+
service_key: item.service_key,
|
|
41
|
+
...(item.site_url && { site_url: item.site_url }),
|
|
42
|
+
...(item.deployed_at && { deployed_at: item.deployed_at }),
|
|
43
|
+
cached_at: new Date().toISOString(),
|
|
44
|
+
source: "legacy_projects_json",
|
|
45
|
+
};
|
|
54
46
|
}
|
|
55
|
-
return { projects };
|
|
56
47
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
48
|
+
return { version: 1, source: "local_cache", projects };
|
|
49
|
+
}
|
|
50
|
+
if (parsed && typeof parsed === "object" && "projects" in parsed) {
|
|
51
|
+
const obj = parsed;
|
|
52
|
+
const rawProjects = obj.projects && typeof obj.projects === "object" && !Array.isArray(obj.projects)
|
|
53
|
+
? obj.projects
|
|
54
|
+
: {};
|
|
55
|
+
const projects = {};
|
|
56
|
+
for (const [id, proj] of Object.entries(rawProjects)) {
|
|
57
|
+
const rec = { ...proj };
|
|
58
|
+
delete rec.tier;
|
|
59
|
+
delete rec.lease_expires_at;
|
|
60
|
+
delete rec.expires_at;
|
|
61
|
+
projects[id] = rec;
|
|
69
62
|
}
|
|
70
|
-
return {
|
|
63
|
+
return {
|
|
64
|
+
version: 1,
|
|
65
|
+
source: "local_cache",
|
|
66
|
+
...(typeof obj.active_project_id === "string" && { active_project_id: obj.active_project_id }),
|
|
67
|
+
...(typeof obj.previous_active_project_id === "string" && { previous_active_project_id: obj.previous_active_project_id }),
|
|
68
|
+
projects,
|
|
69
|
+
...(typeof obj.migrated_from === "string" && { migrated_from: obj.migrated_from }),
|
|
70
|
+
...(typeof obj.migrated_at === "string" && { migrated_at: obj.migrated_at }),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return { version: 1, source: "local_cache", projects: {} };
|
|
74
|
+
}
|
|
75
|
+
function loadParsedKeyStore(path) {
|
|
76
|
+
try {
|
|
77
|
+
return normalizeParsedKeyStore(JSON.parse(readFileSync(path, "utf-8")));
|
|
71
78
|
}
|
|
72
79
|
catch {
|
|
73
|
-
return { projects: {} };
|
|
80
|
+
return { version: 1, source: "local_cache", projects: {} };
|
|
74
81
|
}
|
|
75
82
|
}
|
|
83
|
+
function migrateLegacyProjectsJson(targetPath) {
|
|
84
|
+
const legacyPath = getLegacyProjectsPath();
|
|
85
|
+
if (existsSync(targetPath) || !existsSync(legacyPath))
|
|
86
|
+
return;
|
|
87
|
+
const legacy = loadParsedKeyStore(legacyPath);
|
|
88
|
+
const migratedAt = new Date().toISOString();
|
|
89
|
+
const cache = {
|
|
90
|
+
version: 1,
|
|
91
|
+
source: "local_cache",
|
|
92
|
+
projects: legacy.projects,
|
|
93
|
+
migrated_from: legacyPath,
|
|
94
|
+
migrated_at: migratedAt,
|
|
95
|
+
};
|
|
96
|
+
saveKeyStore(cache, targetPath);
|
|
97
|
+
if (legacy.active_project_id) {
|
|
98
|
+
setProfileActiveProjectId(legacy.active_project_id);
|
|
99
|
+
}
|
|
100
|
+
recordMigration("projects_json_import", {
|
|
101
|
+
legacy_path: legacyPath,
|
|
102
|
+
cache_path: targetPath,
|
|
103
|
+
project_count: Object.keys(legacy.projects).length,
|
|
104
|
+
imported_at: migratedAt,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Load the project-key credential cache from disk.
|
|
109
|
+
* Auto-imports legacy `projects.json` formats into the new cache path when using
|
|
110
|
+
* the default location:
|
|
111
|
+
* - Array format (CLI legacy): [{project_id, ...}] → {projects: {id: {...}}}
|
|
112
|
+
* - Object format: {active_project_id, projects} → credentials cache + state.json
|
|
113
|
+
* - Old metadata fields: tier/expires_at/lease_expires_at are stripped
|
|
114
|
+
*/
|
|
115
|
+
export function loadKeyStore(path) {
|
|
116
|
+
const p = path ?? getProjectCredentialsPath();
|
|
117
|
+
if (!path)
|
|
118
|
+
migrateLegacyProjectsJson(p);
|
|
119
|
+
return loadParsedKeyStore(p);
|
|
120
|
+
}
|
|
76
121
|
export function saveKeyStore(store, path) {
|
|
77
|
-
const p = path ??
|
|
122
|
+
const p = path ?? getProjectCredentialsPath();
|
|
78
123
|
const dir = dirname(p);
|
|
79
124
|
mkdirSync(dir, { recursive: true });
|
|
80
|
-
const
|
|
81
|
-
|
|
125
|
+
const cache = {
|
|
126
|
+
version: 1,
|
|
127
|
+
source: "local_cache",
|
|
128
|
+
...(store.migrated_from ? { migrated_from: store.migrated_from } : {}),
|
|
129
|
+
...(store.migrated_at ? { migrated_at: store.migrated_at } : {}),
|
|
130
|
+
projects: store.projects,
|
|
131
|
+
};
|
|
132
|
+
const tmp = join(dir, `.project-keys.${randomBytes(4).toString("hex")}.tmp`);
|
|
133
|
+
writeFileSync(tmp, JSON.stringify(cache, null, 2), { mode: 0o600 });
|
|
82
134
|
renameSync(tmp, p);
|
|
83
135
|
chmodSync(p, 0o600);
|
|
84
136
|
}
|
|
@@ -87,55 +139,38 @@ export function getProject(projectId, path) {
|
|
|
87
139
|
return store.projects[projectId];
|
|
88
140
|
}
|
|
89
141
|
export function saveProject(projectId, project, path) {
|
|
90
|
-
const p = path ??
|
|
142
|
+
const p = path ?? getProjectCredentialsPath();
|
|
91
143
|
withFileLock(p, () => {
|
|
92
144
|
const store = loadKeyStore(p);
|
|
93
|
-
store.projects[projectId] = project;
|
|
145
|
+
store.projects[projectId] = { ...project, cached_at: project.cached_at ?? new Date().toISOString() };
|
|
94
146
|
saveKeyStore(store, p);
|
|
95
147
|
});
|
|
96
148
|
}
|
|
97
149
|
export function updateProject(projectId, update, path) {
|
|
98
|
-
const p = path ??
|
|
150
|
+
const p = path ?? getProjectCredentialsPath();
|
|
99
151
|
withFileLock(p, () => {
|
|
100
152
|
const store = loadKeyStore(p);
|
|
101
153
|
const existing = store.projects[projectId];
|
|
102
154
|
if (existing) {
|
|
103
|
-
store.projects[projectId] = { ...existing, ...update };
|
|
155
|
+
store.projects[projectId] = { ...existing, ...update, cached_at: existing.cached_at ?? new Date().toISOString() };
|
|
104
156
|
saveKeyStore(store, p);
|
|
105
157
|
}
|
|
106
158
|
});
|
|
107
159
|
}
|
|
108
160
|
export function removeProject(projectId, path) {
|
|
109
|
-
const p = path ??
|
|
161
|
+
const p = path ?? getProjectCredentialsPath();
|
|
110
162
|
withFileLock(p, () => {
|
|
111
163
|
const store = loadKeyStore(p);
|
|
112
164
|
delete store.projects[projectId];
|
|
113
|
-
if (store.active_project_id === projectId) {
|
|
114
|
-
const fallback = store.previous_active_project_id;
|
|
115
|
-
if (fallback && fallback !== projectId && store.projects[fallback]) {
|
|
116
|
-
store.active_project_id = fallback;
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
delete store.active_project_id;
|
|
120
|
-
}
|
|
121
|
-
delete store.previous_active_project_id;
|
|
122
|
-
}
|
|
123
165
|
saveKeyStore(store, p);
|
|
124
166
|
});
|
|
167
|
+
if (!path)
|
|
168
|
+
clearProfileActiveProjectId(projectId);
|
|
125
169
|
}
|
|
126
170
|
export function getActiveProjectId(path) {
|
|
127
|
-
|
|
128
|
-
return store.active_project_id;
|
|
171
|
+
return getProfileActiveProjectId(path);
|
|
129
172
|
}
|
|
130
173
|
export function setActiveProjectId(projectId, path) {
|
|
131
|
-
|
|
132
|
-
withFileLock(p, () => {
|
|
133
|
-
const store = loadKeyStore(p);
|
|
134
|
-
if (store.active_project_id && store.active_project_id !== projectId) {
|
|
135
|
-
store.previous_active_project_id = store.active_project_id;
|
|
136
|
-
}
|
|
137
|
-
store.active_project_id = projectId;
|
|
138
|
-
saveKeyStore(store, p);
|
|
139
|
-
});
|
|
174
|
+
setProfileActiveProjectId(projectId, path);
|
|
140
175
|
}
|
|
141
176
|
//# sourceMappingURL=keystore.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface ActiveProjectScope {
|
|
2
|
+
api_base?: string;
|
|
3
|
+
principal?: string | null;
|
|
4
|
+
profile?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ActiveProjectState {
|
|
7
|
+
project_id: string;
|
|
8
|
+
previous_project_id?: string;
|
|
9
|
+
api_base: string;
|
|
10
|
+
principal?: string | null;
|
|
11
|
+
profile: string;
|
|
12
|
+
updated_at: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ProfileState {
|
|
15
|
+
version?: 1;
|
|
16
|
+
active_project_id?: string;
|
|
17
|
+
previous_active_project_id?: string;
|
|
18
|
+
active_projects?: Record<string, ActiveProjectState>;
|
|
19
|
+
migrations?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export declare function loadProfileState(path?: string): ProfileState;
|
|
22
|
+
export declare function saveProfileState(state: ProfileState, path?: string): void;
|
|
23
|
+
export declare function defaultActiveProjectScope(scope?: ActiveProjectScope): Required<Pick<ActiveProjectScope, "api_base" | "profile">> & Pick<ActiveProjectScope, "principal">;
|
|
24
|
+
export declare function activeProjectScopeKey(scope?: ActiveProjectScope): string;
|
|
25
|
+
export declare function getActiveProjectId(path?: string, scope?: ActiveProjectScope): string | undefined;
|
|
26
|
+
export declare function setActiveProjectId(projectId: string, path?: string, scope?: ActiveProjectScope): void;
|
|
27
|
+
export declare function clearActiveProjectId(projectId: string, path?: string, scope?: ActiveProjectScope): void;
|
|
28
|
+
export declare function recordMigration(marker: string, value: unknown, path?: string): void;
|
|
29
|
+
//# sourceMappingURL=profile-state.d.ts.map
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, renameSync, chmodSync, rmdirSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { getActiveProfile, getApiBase, getProfileStatePath } from "./config.js";
|
|
5
|
+
function withFileLock(path, fn, { retries = 200, delayMs = 20 } = {}) {
|
|
6
|
+
const lockDir = path + ".lock";
|
|
7
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
8
|
+
for (let i = 0; i < retries; i++) {
|
|
9
|
+
try {
|
|
10
|
+
mkdirSync(lockDir, { mode: 0o700 });
|
|
11
|
+
}
|
|
12
|
+
catch (e) {
|
|
13
|
+
const code = e.code;
|
|
14
|
+
if (code !== "EEXIST")
|
|
15
|
+
throw e;
|
|
16
|
+
const until = Date.now() + delayMs;
|
|
17
|
+
while (Date.now() < until) { /* spin */ }
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return fn();
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
try {
|
|
25
|
+
rmdirSync(lockDir);
|
|
26
|
+
}
|
|
27
|
+
catch { /* best-effort */ }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`Could not acquire profile-state lock after ${retries} retries: ${lockDir}`);
|
|
31
|
+
}
|
|
32
|
+
export function loadProfileState(path) {
|
|
33
|
+
const p = path ?? getProfileStatePath();
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(readFileSync(p, "utf-8"));
|
|
36
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
37
|
+
return {};
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function saveProfileState(state, path) {
|
|
45
|
+
const p = path ?? getProfileStatePath();
|
|
46
|
+
const dir = dirname(p);
|
|
47
|
+
mkdirSync(dir, { recursive: true });
|
|
48
|
+
const tmp = join(dir, `.state.${randomBytes(4).toString("hex")}.tmp`);
|
|
49
|
+
writeFileSync(tmp, JSON.stringify({ version: 1, ...state }, null, 2), { mode: 0o600 });
|
|
50
|
+
renameSync(tmp, p);
|
|
51
|
+
chmodSync(p, 0o600);
|
|
52
|
+
}
|
|
53
|
+
export function defaultActiveProjectScope(scope = {}) {
|
|
54
|
+
return {
|
|
55
|
+
api_base: scope.api_base ?? getApiBase(),
|
|
56
|
+
profile: scope.profile ?? getActiveProfile(),
|
|
57
|
+
principal: scope.principal ?? null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function activeProjectScopeKey(scope = {}) {
|
|
61
|
+
const resolved = defaultActiveProjectScope(scope);
|
|
62
|
+
return JSON.stringify({
|
|
63
|
+
api_base: resolved.api_base,
|
|
64
|
+
profile: resolved.profile,
|
|
65
|
+
principal: resolved.principal ?? "unknown",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export function getActiveProjectId(path, scope = {}) {
|
|
69
|
+
const state = loadProfileState(path);
|
|
70
|
+
const key = activeProjectScopeKey(scope);
|
|
71
|
+
return state.active_projects?.[key]?.project_id ?? state.active_project_id;
|
|
72
|
+
}
|
|
73
|
+
export function setActiveProjectId(projectId, path, scope = {}) {
|
|
74
|
+
const p = path ?? getProfileStatePath();
|
|
75
|
+
withFileLock(p, () => {
|
|
76
|
+
const state = loadProfileState(p);
|
|
77
|
+
const key = activeProjectScopeKey(scope);
|
|
78
|
+
const resolved = defaultActiveProjectScope(scope);
|
|
79
|
+
const current = state.active_projects?.[key]?.project_id ?? state.active_project_id;
|
|
80
|
+
const previous = current && current !== projectId ? current : undefined;
|
|
81
|
+
state.active_projects = state.active_projects ?? {};
|
|
82
|
+
state.active_projects[key] = {
|
|
83
|
+
project_id: projectId,
|
|
84
|
+
...(previous ? { previous_project_id: previous } : {}),
|
|
85
|
+
api_base: resolved.api_base,
|
|
86
|
+
principal: resolved.principal ?? null,
|
|
87
|
+
profile: resolved.profile,
|
|
88
|
+
updated_at: new Date().toISOString(),
|
|
89
|
+
};
|
|
90
|
+
state.active_project_id = projectId;
|
|
91
|
+
if (previous)
|
|
92
|
+
state.previous_active_project_id = previous;
|
|
93
|
+
else
|
|
94
|
+
delete state.previous_active_project_id;
|
|
95
|
+
saveProfileState(state, p);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
export function clearActiveProjectId(projectId, path, scope = {}) {
|
|
99
|
+
const p = path ?? getProfileStatePath();
|
|
100
|
+
withFileLock(p, () => {
|
|
101
|
+
const state = loadProfileState(p);
|
|
102
|
+
const key = activeProjectScopeKey(scope);
|
|
103
|
+
const scoped = state.active_projects?.[key];
|
|
104
|
+
if (scoped?.project_id === projectId && state.active_projects) {
|
|
105
|
+
if (scoped.previous_project_id) {
|
|
106
|
+
state.active_projects[key] = {
|
|
107
|
+
...scoped,
|
|
108
|
+
project_id: scoped.previous_project_id,
|
|
109
|
+
previous_project_id: undefined,
|
|
110
|
+
updated_at: new Date().toISOString(),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
delete state.active_projects[key];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (state.active_project_id === projectId) {
|
|
118
|
+
if (state.previous_active_project_id && state.previous_active_project_id !== projectId) {
|
|
119
|
+
state.active_project_id = state.previous_active_project_id;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
delete state.active_project_id;
|
|
123
|
+
}
|
|
124
|
+
delete state.previous_active_project_id;
|
|
125
|
+
}
|
|
126
|
+
saveProfileState(state, p);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
export function recordMigration(marker, value, path) {
|
|
130
|
+
const p = path ?? getProfileStatePath();
|
|
131
|
+
withFileLock(p, () => {
|
|
132
|
+
const state = loadProfileState(p);
|
|
133
|
+
state.migrations = { ...(state.migrations ?? {}), [marker]: value };
|
|
134
|
+
saveProfileState(state, p);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=profile-state.js.map
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* Credential provider interface for the Run402 SDK.
|
|
3
3
|
*
|
|
4
4
|
* The SDK's request kernel calls `getAuth` before each request to obtain
|
|
5
|
-
* signed auth headers, and `
|
|
6
|
-
* keys. All filesystem, environment, and session-state access
|
|
7
|
-
* provider implementations — never in the kernel.
|
|
5
|
+
* signed auth headers, and `getProjectCredentials` to resolve per-project
|
|
6
|
+
* anon/service keys. All filesystem, environment, and session-state access
|
|
7
|
+
* lives inside provider implementations — never in the kernel.
|
|
8
8
|
*
|
|
9
9
|
* Node consumers use {@link NodeCredentialsProvider} from `@run402/sdk/node`
|
|
10
10
|
* which wraps the local keystore + allowance. Sandbox consumers supply their
|
|
11
11
|
* own implementation bound to a session token issued by the supervisor.
|
|
12
12
|
*
|
|
13
|
-
* The two required methods (`getAuth`, `
|
|
13
|
+
* The two required methods (`getAuth`, `getProjectCredentials`) support every API call.
|
|
14
14
|
* The optional methods let providers opt in to local persistence (keystore
|
|
15
15
|
* writes, active-project tracking). Namespace methods that need a missing
|
|
16
16
|
* optional method throw a descriptive error at runtime.
|
|
@@ -23,6 +23,13 @@ export interface ProjectKeys {
|
|
|
23
23
|
last_deployment_id?: string;
|
|
24
24
|
mailbox_id?: string;
|
|
25
25
|
mailbox_address?: string;
|
|
26
|
+
cached_at?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ProjectCredentialCacheInfo {
|
|
29
|
+
source: "local_cache";
|
|
30
|
+
cache_path?: string;
|
|
31
|
+
wallet?: string;
|
|
32
|
+
profile?: string;
|
|
26
33
|
}
|
|
27
34
|
export interface AllowanceData {
|
|
28
35
|
address: string;
|
|
@@ -78,10 +85,18 @@ export interface CredentialsProvider {
|
|
|
78
85
|
*/
|
|
79
86
|
getAuth(path: string, metadata?: AuthRequestMeta): Promise<Record<string, string> | null>;
|
|
80
87
|
/**
|
|
81
|
-
* Resolve the anon/service keys for a project. Returns null
|
|
82
|
-
*
|
|
88
|
+
* Resolve the anon/service keys for a project. Returns null when local
|
|
89
|
+
* credentials are absent. This is a credential-cache lookup, not a project
|
|
90
|
+
* existence check.
|
|
91
|
+
*/
|
|
92
|
+
getProjectCredentials?(id: string): Promise<ProjectKeys | null>;
|
|
93
|
+
/**
|
|
94
|
+
* @deprecated Use `getProjectCredentials`. This alias is retained only for
|
|
95
|
+
* older custom providers; first-party SDK code must use the explicit name.
|
|
83
96
|
*/
|
|
84
|
-
getProject(id: string): Promise<ProjectKeys | null>;
|
|
97
|
+
getProject?(id: string): Promise<ProjectKeys | null>;
|
|
98
|
+
/** List locally cached project credentials. Optional and local-cache only; not project inventory. */
|
|
99
|
+
listProjectCredentials?(): Promise<Record<string, ProjectKeys>>;
|
|
85
100
|
/**
|
|
86
101
|
* Persist project keys after a successful provision or deploy. Optional:
|
|
87
102
|
* providers without local storage (pure session providers) may omit this.
|
|
@@ -103,6 +118,8 @@ export interface CredentialsProvider {
|
|
|
103
118
|
createAllowance?(): Promise<AllowanceData>;
|
|
104
119
|
/** Return the absolute path to the local allowance file, for diagnostic output. Optional. */
|
|
105
120
|
getAllowancePath?(): string;
|
|
121
|
+
/** Return safe provenance for the local project credential cache. Optional. */
|
|
122
|
+
getProjectCredentialCacheInfo?(): ProjectCredentialCacheInfo;
|
|
106
123
|
/**
|
|
107
124
|
* Return the active wallet's display identity (local name + address + cached
|
|
108
125
|
* server label). The Node provider derives this from the active profile;
|