@hasna/contacts 0.7.0 → 0.8.1

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 (39) hide show
  1. package/README.md +83 -13
  2. package/dist/cli/commands/core.d.ts.map +1 -1
  3. package/dist/cli/index.js +82 -33
  4. package/dist/cloud/http-storage.d.ts +20 -12
  5. package/dist/cloud/http-storage.d.ts.map +1 -1
  6. package/dist/cloud/resolver-inputs.d.ts +51 -0
  7. package/dist/cloud/resolver-inputs.d.ts.map +1 -0
  8. package/dist/db/paths.d.ts +4 -4
  9. package/dist/db/paths.d.ts.map +1 -1
  10. package/dist/generated/storage-kit/backend.d.ts +19 -0
  11. package/dist/generated/storage-kit/backend.d.ts.map +1 -0
  12. package/dist/generated/storage-kit/index.d.ts +2 -1
  13. package/dist/generated/storage-kit/index.d.ts.map +1 -1
  14. package/dist/generated/storage-kit/migrations.d.ts +21 -0
  15. package/dist/generated/storage-kit/migrations.d.ts.map +1 -1
  16. package/dist/generated/storage-kit/own.d.ts +11 -0
  17. package/dist/generated/storage-kit/own.d.ts.map +1 -0
  18. package/dist/generated/storage-kit/pool.d.ts +5 -17
  19. package/dist/generated/storage-kit/pool.d.ts.map +1 -1
  20. package/dist/generated/storage-kit/query.d.ts +1 -1
  21. package/dist/generated/storage-kit/query.d.ts.map +1 -1
  22. package/dist/generated/storage-kit/tls.d.ts +30 -3
  23. package/dist/generated/storage-kit/tls.d.ts.map +1 -1
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +105 -24
  27. package/dist/mcp/index.d.ts +9 -0
  28. package/dist/mcp/index.d.ts.map +1 -1
  29. package/dist/mcp/index.js +112 -18
  30. package/dist/mcp/startup-gate.d.ts +48 -0
  31. package/dist/mcp/startup-gate.d.ts.map +1 -0
  32. package/dist/sdk/index.d.ts +70 -3
  33. package/dist/sdk/index.d.ts.map +1 -1
  34. package/dist/sdk/index.js +990 -7
  35. package/dist/server/index.js +201 -43
  36. package/hasna.contract.json +20 -6
  37. package/package.json +3 -3
  38. package/dist/lib/config.d.ts +0 -7
  39. package/dist/lib/config.d.ts.map +0 -1
@@ -0,0 +1,48 @@
1
+ import { type CredentialChainOptions, type ResolvedCredential } from "@hasna/contracts/client";
2
+ import { type Env } from "../cloud/resolver-inputs.js";
3
+ /**
4
+ * The fail-closed startup gate for `contacts-mcp` (hasna/apps#1720 validation,
5
+ * round 2).
6
+ *
7
+ * A hosted-only client with no credential must fail LOUD before it serves:
8
+ * non-zero exit before the stdio transport is connected or the HTTP port is
9
+ * bound, so an agent that registered `contacts-mcp` on a station without the
10
+ * fleet credential sees the refusal at startup instead of an `initialize`
11
+ * answered by a server whose every tool call would fail. Previously main()
12
+ * built the server and connected the transport without ever resolving the
13
+ * credential: the negative control (empty HASNA_HOME, absent Keychain
14
+ * account, no env key) printed "running on stdio", answered `initialize`,
15
+ * and exited 0.
16
+ *
17
+ * The gate is ONE pass down the same @hasna/contracts chain the tools resolve
18
+ * through per call (`resolveContactsClientTransport`): the live process
19
+ * environment keeps its ambient Keychain/disk tiers, a caller-built env is the
20
+ * hermetic seam. A DELIBERATE tier that cannot produce a key is a refusal,
21
+ * never resolved around: `HASNA_PROFILE` naming a missing profile file, an
22
+ * unsafe credentials file, a Keychain item that exists but cannot be read, and
23
+ * the secrets-vault pointer `HASNA_CONTACTS_API_KEY_REF`. The chain validates
24
+ * only the pointer's SHAPE, so the gate dereferences it once through the vault
25
+ * exactly as the transport does at request time; a pointer that cannot be
26
+ * completed (SDK absent, vault unconfigured or unreachable, item missing or
27
+ * empty) refuses the start instead of answering `initialize` with a server
28
+ * whose every call would fail.
29
+ *
30
+ * Nothing here opens, reads or creates a local store, and the message names
31
+ * WHERE the credential should live (an item reference, a file path, an env key
32
+ * name) — never a value. A dereferenced key is dropped on the spot: every tool
33
+ * still re-resolves per call.
34
+ */
35
+ export type McpStartupGate = {
36
+ ok: true;
37
+ apiUrlSource: string | null;
38
+ apiKeySource: string | null;
39
+ } | {
40
+ ok: false;
41
+ message: string;
42
+ };
43
+ /** The vault dereference, injectable so the pointer path is testable without a vault. */
44
+ export interface McpStartupGateOptions {
45
+ completePointer?: (name: string, pointer: ResolvedCredential, env: Env) => Promise<ResolvedCredential>;
46
+ }
47
+ export declare function resolveMcpStartupGate(env?: Env, credentials?: CredentialChainOptions, options?: McpStartupGateOptions): Promise<McpStartupGate>;
48
+ //# sourceMappingURL=startup-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup-gate.d.ts","sourceRoot":"","sources":["../../src/mcp/startup-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAA+B,KAAK,GAAG,EAAE,MAAM,6BAA6B,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACtE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,yFAAyF;AACzF,MAAM,WAAW,qBAAqB;IACpC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACxG;AAOD,wBAAsB,qBAAqB,CACzC,GAAG,GAAE,GAAiB,EACtB,WAAW,GAAE,sBAA2B,EACxC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,cAAc,CAAC,CAmCzB"}
@@ -1,10 +1,37 @@
1
- /** @hasna/contacts SDK — explicit authenticated HTTPS `/v1` client. */
1
+ /**
2
+ * @hasna/contacts SDK — the hosted `/v1` client surface.
3
+ *
4
+ * Two constructors, one rule (hasna/apps#1720, #1794):
5
+ *
6
+ * - `new ContactsV1Client({ baseUrl, apiKey })` is the EXPLICIT pin: a
7
+ * caller-supplied authority is a deliberate selection, it always requires a
8
+ * caller-supplied key, and the ambient fleet credential is never attached to
9
+ * it.
10
+ * - `createContactsClient()` goes through the ONE fleet resolver in
11
+ * `@hasna/contracts/client`, exactly like the CLI and the MCP server: the
12
+ * credential comes from an explicit argument, the deliberate env pointers
13
+ * (`HASNA_CONTACTS_API_KEY_OVERRIDE`, `HASNA_PROFILE`,
14
+ * `HASNA_CONTACTS_API_KEY_REF`), the macOS Keychain item
15
+ * `hasna.credentials.contacts.api-key`, `~/.hasna/contacts/config/credentials`
16
+ * (owner-only 0400/0600), then `HASNA_CONTACTS_API_KEY`; the authority follows
17
+ * `HASNA_CONTACTS_API_URL`, the Keychain `api-url` item, the credentials
18
+ * file, and defaults to the fleet gateway `https://api.hasna.com/contacts`
19
+ * once a credential resolves. The KEY is re-resolved on every request (a
20
+ * rotation heals a long-lived agent); the AUTHORITY is pinned for the life
21
+ * of the client, so a credential written for one service is never sent to
22
+ * another. Nothing resolving THROWS — there is no local fallback and no
23
+ * unauthenticated client.
24
+ */
25
+ import { type KeychainTierOptions } from "@hasna/contracts/client";
26
+ import { ContactsClientConfigurationError } from "../cloud/http-storage.js";
27
+ import { type Env } from "../cloud/resolver-inputs.js";
2
28
  import { ContactsV1Client as GeneratedContactsV1Client, ApiError, type ContactsV1ClientOptions as GeneratedContactsV1ClientOptions } from "./v1.generated.js";
3
29
  export interface ContactsV1ClientOptions extends Omit<GeneratedContactsV1ClientOptions, "baseUrl" | "apiKey"> {
4
30
  /** Explicit HTTPS service authority. No default is composed. */
5
31
  baseUrl: string;
6
- /** API key sent to the configured authority. Required and never logged. */
7
- apiKey: string;
32
+ /** API key sent to the configured authority. Required and never logged.
33
+ * An explicit baseUrl never falls back to an ambient fleet key (#1794). */
34
+ apiKey?: string;
8
35
  }
9
36
  /**
10
37
  * Validated wrapper around the generated API surface. Redirects are never
@@ -13,6 +40,46 @@ export interface ContactsV1ClientOptions extends Omit<GeneratedContactsV1ClientO
13
40
  export declare class ContactsV1Client extends GeneratedContactsV1Client {
14
41
  constructor(options: ContactsV1ClientOptions);
15
42
  }
43
+ /** The app slug the shared client seam resolves credentials and authority for. */
44
+ export declare const CONTACTS_APP_NAME: "contacts";
45
+ /** Options for {@link createContactsClient}. */
46
+ export interface CreateContactsClientOptions extends Omit<GeneratedContactsV1ClientOptions, "baseUrl" | "apiKey"> {
47
+ /**
48
+ * Explicit HTTPS authority — a deliberate pin. Requires `apiKey`; the ambient
49
+ * chain is never consulted on its behalf (#1794).
50
+ */
51
+ baseUrl?: string;
52
+ /**
53
+ * Explicit API key (tier 1). With `baseUrl` it is the whole configuration;
54
+ * alone, the authority still resolves through the chain.
55
+ */
56
+ apiKey?: string;
57
+ /** The environment to resolve through instead of `process.env` — the hermetic seam. */
58
+ env?: Env;
59
+ /** Tier-1 identity selection (`--profile`), passed through to the chain. */
60
+ profile?: string;
61
+ /**
62
+ * Tier-3 controls: a fake `security` runner in tests, an opt-out on a CI
63
+ * Mac. Production callers pass nothing — the tier is ambient for
64
+ * `process.env` and off for a caller-built env.
65
+ */
66
+ keychain?: KeychainTierOptions;
67
+ }
68
+ /** The SDK's refusal for an explicit authority without an explicit key (#1794). */
69
+ export declare function contactsSdkAuthorityPinMessage(): string;
70
+ /**
71
+ * Build a ContactsV1Client through the fleet resolver.
72
+ *
73
+ * - explicit `baseUrl` + `apiKey` → a deliberate pin, used verbatim; the
74
+ * ambient chain is never consulted (hasna/apps#1794).
75
+ * - explicit `baseUrl` without `apiKey` → throws (no ambient key attach).
76
+ * - otherwise the @hasna/contracts chain resolves credential + authority, and
77
+ * every request re-resolves the KEY on a fresh snapshot while the AUTHORITY
78
+ * stays pinned (a changed authority is a new client, never a key sent to the
79
+ * wrong server). Any refusal throws — there is no local fallback.
80
+ */
81
+ export declare function createContactsClient(options?: CreateContactsClientOptions): ContactsV1Client;
82
+ export { ContactsClientConfigurationError };
16
83
  export { ApiError as ContactsV1ApiError };
17
84
  export type { Contact as ContactsV1Contact, Company as ContactsV1Company, Tag as ContactsV1Tag, CreateContactInput as ContactsV1CreateContactInput, UpdateContactInput as ContactsV1UpdateContactInput, CreateCompanyInput as ContactsV1CreateCompanyInput, UpdateCompanyInput as ContactsV1UpdateCompanyInput, CreateTagInput as ContactsV1CreateTagInput, UpdateTagInput as ContactsV1UpdateTagInput, ProjectIdsInput as ContactsV1ProjectIdsInput, ContactProjectMembershipSnapshot as ContactsV1ProjectMembershipSnapshot, ContactProjectMembershipMutationInput as ContactsV1ProjectMembershipMutationInput, ContactProjectMembershipMutationResult as ContactsV1ProjectMembershipMutationResult, ContactProjectMembershipListResult as ContactsV1ProjectMembershipListResult, } from "./v1.generated.js";
18
85
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EACL,gBAAgB,IAAI,yBAAyB,EAC7C,QAAQ,EACR,KAAK,uBAAuB,IAAI,gCAAgC,EACjE,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,uBACf,SAAQ,IAAI,CAAC,gCAAgC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACpE,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;CAChB;AA8BD;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,yBAAyB;gBACjD,OAAO,EAAE,uBAAuB;CAS7C;AAED,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,CAAC;AAC1C,YAAY,EACV,OAAO,IAAI,iBAAiB,EAC5B,OAAO,IAAI,iBAAiB,EAC5B,GAAG,IAAI,aAAa,EACpB,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,cAAc,IAAI,wBAAwB,EAC1C,cAAc,IAAI,wBAAwB,EAC1C,eAAe,IAAI,yBAAyB,EAC5C,gCAAgC,IAAI,mCAAmC,EACvE,qCAAqC,IAAI,wCAAwC,EACjF,sCAAsC,IAAI,yCAAyC,EACnF,kCAAkC,IAAI,qCAAqC,GAC5E,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAkD,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnH,OAAO,EAAE,gCAAgC,EAAkC,MAAM,0BAA0B,CAAC;AAC5G,OAAO,EAA+B,KAAK,GAAG,EAAE,MAAM,6BAA6B,CAAC;AACpF,OAAO,EACL,gBAAgB,IAAI,yBAAyB,EAC7C,QAAQ,EACR,KAAK,uBAAuB,IAAI,gCAAgC,EACjE,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,uBACf,SAAQ,IAAI,CAAC,gCAAgC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACpE,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB;+EAC2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA4BD;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,yBAAyB;gBACjD,OAAO,EAAE,uBAAuB;CAS7C;AAED,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,EAAG,UAAmB,CAAC;AAErD,gDAAgD;AAChD,MAAM,WAAW,2BACf,SAAQ,IAAI,CAAC,gCAAgC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACpE;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uFAAuF;IACvF,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC;AAED,mFAAmF;AACnF,wBAAgB,8BAA8B,IAAI,MAAM,CAMvD;AA0BD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,2BAAgC,GAAG,gBAAgB,CAiDhG;AAED,OAAO,EAAE,gCAAgC,EAAE,CAAC;AAC5C,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,CAAC;AAC1C,YAAY,EACV,OAAO,IAAI,iBAAiB,EAC5B,OAAO,IAAI,iBAAiB,EAC5B,GAAG,IAAI,aAAa,EACpB,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,cAAc,IAAI,wBAAwB,EAC1C,cAAc,IAAI,wBAAwB,EAC1C,eAAe,IAAI,yBAAyB,EAC5C,gCAAgC,IAAI,mCAAmC,EACvE,qCAAqC,IAAI,wCAAwC,EACjF,sCAAsC,IAAI,yCAAyC,EACnF,kCAAkC,IAAI,qCAAqC,GAC5E,MAAM,mBAAmB,CAAC"}