@hasna/domains 0.0.47 → 0.1.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 +58 -34
- package/dist/cli/commands/dns.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/serve.d.ts +1 -0
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/index.js +4754 -2105
- package/dist/db/database.d.ts +6 -23
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/domains.d.ts +3 -14
- package/dist/db/domains.d.ts.map +1 -1
- package/dist/db/store.d.ts +36 -32
- package/dist/db/store.d.ts.map +1 -1
- package/dist/generated/storage-kit/backend.d.ts +4 -4
- package/dist/generated/storage-kit/backend.d.ts.map +1 -1
- package/dist/generated/storage-kit/index.d.ts +1 -1
- package/dist/generated/storage-kit/index.d.ts.map +1 -1
- package/dist/generated/storage-kit/migrations.d.ts.map +1 -1
- package/dist/generated/storage-kit/pool.d.ts +2 -5
- package/dist/generated/storage-kit/pool.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1127 -1734
- package/dist/lib/app-home.d.ts +32 -44
- package/dist/lib/app-home.d.ts.map +1 -1
- package/dist/lib/client-storage-policy.d.ts +10 -0
- package/dist/lib/client-storage-policy.d.ts.map +1 -0
- package/dist/lib/client-types.d.ts +184 -0
- package/dist/lib/client-types.d.ts.map +1 -0
- package/dist/lib/cloudflare.d.ts.map +1 -1
- package/dist/lib/cloudflare.js +23 -0
- package/dist/lib/config.d.ts +6 -16
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/dns-plan.d.ts +1 -1
- package/dist/lib/dns-plan.d.ts.map +1 -1
- package/dist/lib/domains-resolver.d.ts +96 -0
- package/dist/lib/domains-resolver.d.ts.map +1 -0
- package/dist/lib/registrar.d.ts +8 -0
- package/dist/lib/registrar.d.ts.map +1 -1
- package/dist/lib/registrar.js +23 -0
- package/dist/mcp/harness.d.ts +46 -0
- package/dist/mcp/harness.d.ts.map +1 -0
- package/dist/mcp/http.d.ts +3 -2
- package/dist/mcp/http.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +24162 -25378
- package/dist/mcp/tool-filter.d.ts +6 -0
- package/dist/mcp/tool-filter.d.ts.map +1 -1
- package/dist/sdk/index.d.ts +21 -10
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +813 -275
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +84 -29
- package/package.json +5 -7
- package/postinstall.js +13 -14
package/dist/server/index.d.ts
CHANGED
|
@@ -15,4 +15,10 @@ export declare const SIGNING_KEY_ENVS: readonly ["HASNA_DOMAINS_API_SIGNING_KEY"
|
|
|
15
15
|
/** Normalize the module-injected env names to the kit/auth conventions. */
|
|
16
16
|
export declare function normalizeEnv(env?: NodeJS.ProcessEnv): void;
|
|
17
17
|
export declare function resolveSigningSecret(env?: NodeJS.ProcessEnv): string;
|
|
18
|
+
export declare const USAGE = "domains-serve \u2014 standalone HTTP API server for @hasna/domains (PostgreSQL backend)\n\nUsage: domains-serve [--port <n>] [--host <addr>]\n\nOptions:\n --port <n> Port to listen on (default: $PORT or 8080)\n --host <addr> Address to bind (default: $HOST or 0.0.0.0)\n -h, --help Print this help and exit\n -V, --version Print the package version and exit\n\nEnvironment:\n HASNA_DOMAINS_DATABASE_URL PostgreSQL DSN (falls back to DATABASE_URL)\n HASNA_DOMAINS_API_SIGNING_KEY HMAC signing secret for API keys\n (falls back to API_KEY_SIGNING_SECRET)";
|
|
19
|
+
/** Shared authenticated server for both domains serve and domains-serve. */
|
|
20
|
+
export declare function startDomainsServer(options: {
|
|
21
|
+
port: number;
|
|
22
|
+
host: string;
|
|
23
|
+
}): Promise<void>;
|
|
18
24
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AASH,eAAO,MAAM,gBAAgB,+FAInB,CAAC;AAEX,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAIvE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAQjF"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AASH,eAAO,MAAM,gBAAgB,+FAInB,CAAC;AAEX,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAIvE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAQjF;AAUD,eAAO,MAAM,KAAK,0mBAasD,CAAC;AAwBzE,4EAA4E;AAC5E,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgD/F"}
|
package/dist/server/index.js
CHANGED
|
@@ -79,7 +79,7 @@ function viewWindow(view) {
|
|
|
79
79
|
}
|
|
80
80
|
function toBuffer(secret) {
|
|
81
81
|
if (typeof secret === "string")
|
|
82
|
-
return Buffer.from(secret, "utf8");
|
|
82
|
+
return Buffer.from(secret.trim(), "utf8");
|
|
83
83
|
if (ArrayBuffer.isView(secret)) {
|
|
84
84
|
const [store, byteOffset, byteLength] = viewWindow(secret);
|
|
85
85
|
return Buffer.from(store, byteOffset, byteLength);
|
|
@@ -386,11 +386,17 @@ class ApiKeyStore {
|
|
|
386
386
|
return status !== "active";
|
|
387
387
|
};
|
|
388
388
|
}
|
|
389
|
-
async revoke(kid, reason, atMs = Date.now()) {
|
|
389
|
+
async revoke(kid, reason, atMs = Date.now(), options = {}) {
|
|
390
|
+
const params = [kid, new Date(atMs).toISOString(), reason ?? null];
|
|
391
|
+
let scope = "";
|
|
392
|
+
if (options.app !== undefined) {
|
|
393
|
+
params.push(options.app);
|
|
394
|
+
scope = ` AND app = $${params.length}`;
|
|
395
|
+
}
|
|
390
396
|
const row = await this.client.get(`UPDATE ${this.table}
|
|
391
397
|
SET revoked_at = COALESCE(revoked_at, $2), revoked_reason = COALESCE(revoked_reason, $3)
|
|
392
|
-
WHERE kid = $1
|
|
393
|
-
RETURNING kid`,
|
|
398
|
+
WHERE kid = $1${scope}
|
|
399
|
+
RETURNING kid`, params);
|
|
394
400
|
return row !== null;
|
|
395
401
|
}
|
|
396
402
|
async touchLastUsed(kid, atMs = Date.now()) {
|
|
@@ -629,24 +635,43 @@ function serverDataBackendEnvKeys(name) {
|
|
|
629
635
|
databaseUrlKeys: [`HASNA_${token}_DATABASE_URL`, `${token}_DATABASE_URL`]
|
|
630
636
|
};
|
|
631
637
|
}
|
|
632
|
-
function
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
638
|
+
function definedDatabaseUrlEntries(env, keys) {
|
|
639
|
+
return keys.map((key) => ({ key, value: ownString(env, key) })).filter((entry) => entry.value !== undefined);
|
|
640
|
+
}
|
|
641
|
+
function assertPostgresqlDatabaseUrl(name, entries) {
|
|
642
|
+
const canonicalKey = serverDataBackendEnvKeys(name).databaseUrlKeys[0];
|
|
643
|
+
if (entries.length === 0) {
|
|
644
|
+
throw new Error(`${canonicalKey} is required; servers use PostgreSQL and never default to SQLite.`);
|
|
637
645
|
}
|
|
638
|
-
|
|
646
|
+
const blank = entries.filter((entry) => entry.value.trim().length === 0);
|
|
647
|
+
if (blank.length > 0) {
|
|
648
|
+
throw new Error(`${blank.map((entry) => entry.key).join(" and ")} is set but blank; a PostgreSQL database URL is required.`);
|
|
649
|
+
}
|
|
650
|
+
const controlled = entries.find((entry) => /[\u0000-\u001f\u007f]/.test(entry.value));
|
|
651
|
+
if (controlled)
|
|
652
|
+
throw new Error(`${controlled.key} must not contain ASCII control characters.`);
|
|
653
|
+
const normalized = entries.map((entry) => ({ key: entry.key, value: entry.value.trim() }));
|
|
654
|
+
if (normalized.length > 1 && new Set(normalized.map((entry) => entry.value)).size > 1) {
|
|
655
|
+
throw new Error(`${normalized.map((entry) => entry.key).join(" and ")} disagree; database URL aliases must be identical or only one may be set.`);
|
|
656
|
+
}
|
|
657
|
+
const selected = normalized[0];
|
|
658
|
+
let parsed;
|
|
659
|
+
try {
|
|
660
|
+
parsed = new URL(selected.value);
|
|
661
|
+
} catch {
|
|
662
|
+
throw new Error(`${selected.key} must be an absolute PostgreSQL connection URL.`);
|
|
663
|
+
}
|
|
664
|
+
if (parsed.protocol !== "postgres:" && parsed.protocol !== "postgresql:") {
|
|
665
|
+
throw new Error(`${selected.key} must use the postgres or postgresql scheme.`);
|
|
666
|
+
}
|
|
667
|
+
if (!parsed.hostname || parsed.pathname.length <= 1) {
|
|
668
|
+
throw new Error(`${selected.key} must name a PostgreSQL host and database.`);
|
|
669
|
+
}
|
|
670
|
+
return selected;
|
|
639
671
|
}
|
|
640
672
|
function resolveServerDataBackend(name, env = process.env) {
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
return {
|
|
644
|
-
backend: "sqlite",
|
|
645
|
-
source: "default",
|
|
646
|
-
databaseUrlPresent: false,
|
|
647
|
-
databaseUrlSource: null
|
|
648
|
-
};
|
|
649
|
-
}
|
|
673
|
+
const keys = serverDataBackendEnvKeys(name).databaseUrlKeys;
|
|
674
|
+
const databaseUrl = assertPostgresqlDatabaseUrl(name, definedDatabaseUrlEntries(env, keys));
|
|
650
675
|
return {
|
|
651
676
|
backend: "postgresql",
|
|
652
677
|
source: databaseUrl.key,
|
|
@@ -655,8 +680,8 @@ function resolveServerDataBackend(name, env = process.env) {
|
|
|
655
680
|
};
|
|
656
681
|
}
|
|
657
682
|
function resolveDatabaseUrl(name, env = process.env) {
|
|
658
|
-
const
|
|
659
|
-
return
|
|
683
|
+
const keys = serverDataBackendEnvKeys(name).databaseUrlKeys;
|
|
684
|
+
return assertPostgresqlDatabaseUrl(name, definedDatabaseUrlEntries(env, keys)).value;
|
|
660
685
|
}
|
|
661
686
|
// src/generated/storage-kit/tls.ts
|
|
662
687
|
import { readFileSync } from "fs";
|
|
@@ -903,9 +928,6 @@ function createServerPoolFromEnv(appName, options = {}) {
|
|
|
903
928
|
const env = own.env ?? process.env;
|
|
904
929
|
const resolution = resolveServerDataBackend(appName, env);
|
|
905
930
|
const connectionString = resolveDatabaseUrl(appName, env);
|
|
906
|
-
if (!connectionString) {
|
|
907
|
-
throw new Error(`postgresql storage for ${appName} needs a database URL. Set ` + `HASNA_${appName.toUpperCase().replace(/-/g, "_")}_DATABASE_URL.`);
|
|
908
|
-
}
|
|
909
931
|
const pool = createPgPool({
|
|
910
932
|
...own,
|
|
911
933
|
connectionString,
|
|
@@ -913,7 +935,7 @@ function createServerPoolFromEnv(appName, options = {}) {
|
|
|
913
935
|
});
|
|
914
936
|
return {
|
|
915
937
|
client: createQueryClient(pool),
|
|
916
|
-
connectionSource: resolution.databaseUrlSource
|
|
938
|
+
connectionSource: resolution.databaseUrlSource
|
|
917
939
|
};
|
|
918
940
|
}
|
|
919
941
|
// src/generated/storage-kit/migrations.ts
|
|
@@ -2680,11 +2702,44 @@ function parseArg(name, fallback) {
|
|
|
2680
2702
|
return process.argv[idx + 1];
|
|
2681
2703
|
return fallback;
|
|
2682
2704
|
}
|
|
2705
|
+
var USAGE = `domains-serve \u2014 standalone HTTP API server for @hasna/domains (PostgreSQL backend)
|
|
2706
|
+
|
|
2707
|
+
Usage: domains-serve [--port <n>] [--host <addr>]
|
|
2708
|
+
|
|
2709
|
+
Options:
|
|
2710
|
+
--port <n> Port to listen on (default: $PORT or ${DEFAULT_PORT})
|
|
2711
|
+
--host <addr> Address to bind (default: $HOST or 0.0.0.0)
|
|
2712
|
+
-h, --help Print this help and exit
|
|
2713
|
+
-V, --version Print the package version and exit
|
|
2714
|
+
|
|
2715
|
+
Environment:
|
|
2716
|
+
HASNA_DOMAINS_DATABASE_URL PostgreSQL DSN (falls back to DATABASE_URL)
|
|
2717
|
+
HASNA_DOMAINS_API_SIGNING_KEY HMAC signing secret for API keys
|
|
2718
|
+
(falls back to API_KEY_SIGNING_SECRET)`;
|
|
2719
|
+
function earlyArgAnswer(argv) {
|
|
2720
|
+
if (argv.includes("--help") || argv.includes("-h"))
|
|
2721
|
+
return USAGE;
|
|
2722
|
+
if (argv.includes("--version") || argv.includes("-V"))
|
|
2723
|
+
return getPackageVersion();
|
|
2724
|
+
return;
|
|
2725
|
+
}
|
|
2683
2726
|
async function main() {
|
|
2684
|
-
|
|
2685
|
-
|
|
2727
|
+
const early = earlyArgAnswer(process.argv);
|
|
2728
|
+
if (early !== undefined) {
|
|
2729
|
+
console.log(early);
|
|
2686
2730
|
return;
|
|
2687
2731
|
}
|
|
2732
|
+
await startDomainsServer({
|
|
2733
|
+
port: Number(parseArg("--port", process.env["PORT"]) ?? DEFAULT_PORT),
|
|
2734
|
+
host: parseArg("--host", process.env["HOST"]) ?? "0.0.0.0"
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
async function startDomainsServer(options) {
|
|
2738
|
+
const { port, host } = options;
|
|
2739
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
2740
|
+
throw new Error("Port must be an integer from 1 to 65535.");
|
|
2741
|
+
if (!host.trim())
|
|
2742
|
+
throw new Error("Host must not be blank.");
|
|
2688
2743
|
normalizeEnv();
|
|
2689
2744
|
const version = getPackageVersion();
|
|
2690
2745
|
const signingSecret = resolveSigningSecret();
|
|
@@ -2704,8 +2759,6 @@ async function main() {
|
|
|
2704
2759
|
}
|
|
2705
2760
|
}
|
|
2706
2761
|
});
|
|
2707
|
-
const port = Number(parseArg("--port", process.env["PORT"]) ?? DEFAULT_PORT) || DEFAULT_PORT;
|
|
2708
|
-
const host = parseArg("--host", process.env["HOST"]) ?? "0.0.0.0";
|
|
2709
2762
|
Bun.serve({
|
|
2710
2763
|
port,
|
|
2711
2764
|
hostname: host,
|
|
@@ -2728,7 +2781,9 @@ if (import.meta.main) {
|
|
|
2728
2781
|
});
|
|
2729
2782
|
}
|
|
2730
2783
|
export {
|
|
2784
|
+
startDomainsServer,
|
|
2731
2785
|
resolveSigningSecret,
|
|
2732
2786
|
normalizeEnv,
|
|
2787
|
+
USAGE,
|
|
2733
2788
|
SIGNING_KEY_ENVS
|
|
2734
2789
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/domains",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "Domain portfolio, registrar, marketplace, and DNS management for AI agents — CLI + MCP + HTTP API + SDK,
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Domain portfolio, registrar, marketplace, and DNS management for AI agents — CLI + MCP + HTTP API + SDK, shared account API and PostgreSQL",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"prepublishOnly": "bun run build",
|
|
64
64
|
"postinstall": "node postinstall.js",
|
|
65
65
|
"prepack": "bun run build && bun run artifact-scan",
|
|
66
|
-
"artifact-scan": "bun pm pack --ignore-scripts --quiet --filename \"$PWD/domains-artifact-scan.tgz\" && bunx @hasna/contracts@0.
|
|
66
|
+
"artifact-scan": "bun pm pack --ignore-scripts --quiet --filename \"$PWD/domains-artifact-scan.tgz\" && bunx @hasna/contracts@1.0.2 artifact-scan domains-artifact-scan.tgz; rc=$?; rm -f \"$PWD/domains-artifact-scan.tgz\"; exit $rc"
|
|
67
67
|
},
|
|
68
68
|
"keywords": [
|
|
69
69
|
"domains",
|
|
@@ -98,8 +98,6 @@
|
|
|
98
98
|
"@aws-sdk/client-route-53": "^3.1067.0",
|
|
99
99
|
"@aws-sdk/client-route-53-domains": "^3.1067.0",
|
|
100
100
|
"@aws-sdk/credential-provider-ini": "3.972.53",
|
|
101
|
-
"@hasna/contracts": "0.14.1",
|
|
102
|
-
"@hasna/paths": "0.1.0",
|
|
103
101
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
104
102
|
"chalk": "^5.4.1",
|
|
105
103
|
"commander": "^13.1.0",
|
|
@@ -121,8 +119,8 @@
|
|
|
121
119
|
}
|
|
122
120
|
},
|
|
123
121
|
"devDependencies": {
|
|
124
|
-
"@hasna/
|
|
125
|
-
"@types/bun": "
|
|
122
|
+
"@hasna/contracts": "1.0.2",
|
|
123
|
+
"@types/bun": "1.3.14",
|
|
126
124
|
"@types/pg": "^8.20.0",
|
|
127
125
|
"@types/react": "^18.3.18",
|
|
128
126
|
"bun-types": "1.3.14",
|
package/postinstall.js
CHANGED
|
@@ -1,34 +1,33 @@
|
|
|
1
|
-
// Best-effort install-time creation of the domains home directory,
|
|
2
|
-
// the SAME effective home the runtime uses (src/lib/app-home.ts):
|
|
3
|
-
// override (HASNA_DOMAINS_HOME /
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// ~/.hasna/domains default.
|
|
7
|
-
//
|
|
1
|
+
// Best-effort install-time creation of the domains LOCAL DATA home directory,
|
|
2
|
+
// resolving the SAME effective home the runtime uses (src/lib/app-home.ts):
|
|
3
|
+
// an exact-app override (HASNA_DOMAINS_HOME / HASNA_DOMAINS_DIR, then the
|
|
4
|
+
// legacy unprefixed DOMAINS_HOME / DOMAINS_DIR aliases) wins; otherwise
|
|
5
|
+
// $HASNA_HOME/domains (the shared root override); otherwise the legacy
|
|
6
|
+
// ~/.hasna/domains default. Faithfully mirrors the runtime module — no
|
|
7
|
+
// @hasna/paths, no XDG, no HASNA_*_HOME kind overrides (stripped, hasna/apps#1720
|
|
8
|
+
// class B). Failures are non-fatal: the runtime creates the same directory on
|
|
9
|
+
// first use.
|
|
8
10
|
import { chmodSync, existsSync, mkdirSync } from "node:fs";
|
|
9
11
|
import { homedir } from "node:os";
|
|
10
12
|
import { join } from "node:path";
|
|
11
13
|
|
|
12
14
|
try {
|
|
13
|
-
const { dataDir } = await import("@hasna/paths");
|
|
14
15
|
const env = process.env;
|
|
15
16
|
const override = (
|
|
16
17
|
env["HASNA_DOMAINS_HOME"] ||
|
|
17
|
-
env["DOMAINS_HOME"] ||
|
|
18
18
|
env["HASNA_DOMAINS_DIR"] ||
|
|
19
|
+
env["DOMAINS_HOME"] ||
|
|
19
20
|
env["DOMAINS_DIR"] ||
|
|
20
21
|
""
|
|
21
22
|
).trim();
|
|
22
|
-
const dataHomeOverride = (env["HASNA_DATA_HOME"] || "").trim();
|
|
23
23
|
const home = env["HOME"] || env["USERPROFILE"] || homedir();
|
|
24
24
|
|
|
25
25
|
let dir;
|
|
26
26
|
if (override) {
|
|
27
27
|
dir = override;
|
|
28
28
|
} else {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
dir = adopted ? resolved : join(home, ".hasna", "domains");
|
|
29
|
+
const hasnaHome = (env["HASNA_HOME"] || "").trim();
|
|
30
|
+
dir = hasnaHome ? join(hasnaHome, "domains") : join(home, ".hasna", "domains");
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
@@ -39,4 +38,4 @@ try {
|
|
|
39
38
|
}
|
|
40
39
|
} catch {
|
|
41
40
|
// never fail an install over pre-created directories
|
|
42
|
-
}
|
|
41
|
+
}
|