@hsuite/smart-engines-cli 1.2.1 → 1.4.2

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 (109) hide show
  1. package/README.md +272 -58
  2. package/dist/_lib/attestation.d.ts +45 -0
  3. package/dist/_lib/attestation.d.ts.map +1 -0
  4. package/dist/_lib/attestation.js +76 -0
  5. package/dist/_lib/attestation.js.map +1 -0
  6. package/dist/_lib/baas.d.ts +147 -0
  7. package/dist/_lib/baas.d.ts.map +1 -0
  8. package/dist/_lib/baas.js +260 -0
  9. package/dist/_lib/baas.js.map +1 -0
  10. package/dist/_lib/cluster.d.ts +6 -6
  11. package/dist/_lib/cluster.js +6 -6
  12. package/dist/_lib/deploy-manifest.d.ts +69 -0
  13. package/dist/_lib/deploy-manifest.d.ts.map +1 -0
  14. package/dist/_lib/deploy-manifest.js +87 -0
  15. package/dist/_lib/deploy-manifest.js.map +1 -0
  16. package/dist/_lib/docker.d.ts +82 -0
  17. package/dist/_lib/docker.d.ts.map +1 -0
  18. package/dist/_lib/docker.js +164 -0
  19. package/dist/_lib/docker.js.map +1 -0
  20. package/dist/_lib/faucet-client.d.ts +64 -0
  21. package/dist/_lib/faucet-client.d.ts.map +1 -0
  22. package/dist/_lib/faucet-client.js +92 -0
  23. package/dist/_lib/faucet-client.js.map +1 -0
  24. package/dist/_lib/framework.d.ts +59 -0
  25. package/dist/_lib/framework.d.ts.map +1 -0
  26. package/dist/_lib/framework.js +149 -0
  27. package/dist/_lib/framework.js.map +1 -0
  28. package/dist/_lib/frontend-guard.d.ts +13 -0
  29. package/dist/_lib/frontend-guard.d.ts.map +1 -0
  30. package/dist/_lib/frontend-guard.js +41 -0
  31. package/dist/_lib/frontend-guard.js.map +1 -0
  32. package/dist/_lib/registry-fanout.d.ts +178 -0
  33. package/dist/_lib/registry-fanout.d.ts.map +1 -0
  34. package/dist/_lib/registry-fanout.js +545 -0
  35. package/dist/_lib/registry-fanout.js.map +1 -0
  36. package/dist/_lib/runtime-env.d.ts +126 -0
  37. package/dist/_lib/runtime-env.d.ts.map +1 -0
  38. package/dist/_lib/runtime-env.js +172 -0
  39. package/dist/_lib/runtime-env.js.map +1 -0
  40. package/dist/_lib/subscription-client.d.ts +129 -18
  41. package/dist/_lib/subscription-client.d.ts.map +1 -1
  42. package/dist/_lib/subscription-client.js +268 -40
  43. package/dist/_lib/subscription-client.js.map +1 -1
  44. package/dist/_lib/subscription-env.d.ts +35 -0
  45. package/dist/_lib/subscription-env.d.ts.map +1 -1
  46. package/dist/_lib/subscription-env.js +46 -2
  47. package/dist/_lib/subscription-env.js.map +1 -1
  48. package/dist/commands/app-lifecycle.d.ts +36 -0
  49. package/dist/commands/app-lifecycle.d.ts.map +1 -0
  50. package/dist/commands/app-lifecycle.js +145 -0
  51. package/dist/commands/app-lifecycle.js.map +1 -0
  52. package/dist/commands/ci.d.ts +45 -0
  53. package/dist/commands/ci.d.ts.map +1 -0
  54. package/dist/commands/ci.js +174 -0
  55. package/dist/commands/ci.js.map +1 -0
  56. package/dist/commands/deploy-manifest.d.ts +11 -0
  57. package/dist/commands/deploy-manifest.d.ts.map +1 -0
  58. package/dist/commands/deploy-manifest.js +17 -0
  59. package/dist/commands/deploy-manifest.js.map +1 -0
  60. package/dist/commands/deploy.d.ts +70 -11
  61. package/dist/commands/deploy.d.ts.map +1 -1
  62. package/dist/commands/deploy.js +273 -150
  63. package/dist/commands/deploy.js.map +1 -1
  64. package/dist/commands/downgrade.js +1 -1
  65. package/dist/commands/downgrade.js.map +1 -1
  66. package/dist/commands/faucet.d.ts +78 -0
  67. package/dist/commands/faucet.d.ts.map +1 -0
  68. package/dist/commands/faucet.js +184 -0
  69. package/dist/commands/faucet.js.map +1 -0
  70. package/dist/commands/governance.js +1 -1
  71. package/dist/commands/governance.js.map +1 -1
  72. package/dist/commands/hist-balance.d.ts +1 -1
  73. package/dist/commands/hist-balance.js +1 -1
  74. package/dist/commands/init.js +1 -1
  75. package/dist/commands/list.d.ts +22 -0
  76. package/dist/commands/list.d.ts.map +1 -0
  77. package/dist/commands/list.js +81 -0
  78. package/dist/commands/list.js.map +1 -0
  79. package/dist/commands/personhood.js +2 -2
  80. package/dist/commands/personhood.js.map +1 -1
  81. package/dist/commands/redeploy.d.ts +62 -0
  82. package/dist/commands/redeploy.d.ts.map +1 -0
  83. package/dist/commands/redeploy.js +322 -0
  84. package/dist/commands/redeploy.js.map +1 -0
  85. package/dist/commands/status.d.ts +51 -0
  86. package/dist/commands/status.d.ts.map +1 -0
  87. package/dist/commands/status.js +138 -0
  88. package/dist/commands/status.js.map +1 -0
  89. package/dist/commands/subscribe.d.ts +94 -17
  90. package/dist/commands/subscribe.d.ts.map +1 -1
  91. package/dist/commands/subscribe.js +487 -81
  92. package/dist/commands/subscribe.js.map +1 -1
  93. package/dist/commands/transactions.d.ts +60 -0
  94. package/dist/commands/transactions.d.ts.map +1 -0
  95. package/dist/commands/transactions.js +101 -0
  96. package/dist/commands/transactions.js.map +1 -0
  97. package/dist/commands/update.d.ts +40 -0
  98. package/dist/commands/update.d.ts.map +1 -0
  99. package/dist/commands/update.js +114 -0
  100. package/dist/commands/update.js.map +1 -0
  101. package/dist/commands/verify.d.ts +10 -19
  102. package/dist/commands/verify.d.ts.map +1 -1
  103. package/dist/commands/verify.js +14 -23
  104. package/dist/commands/verify.js.map +1 -1
  105. package/dist/index.d.ts +12 -3
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +32 -3
  108. package/dist/index.js.map +1 -1
  109. package/package.json +11 -4
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Thin, testable wrappers around the `docker` CLI for the trusted-deploy flow.
3
+ *
4
+ * Before the trusted-deploy arc the CLI only PRINTED the `docker login` /
5
+ * `docker push` commands and waited for the developer to run them by hand.
6
+ * Now the CLI wraps the push so it can RECORD the pushed image's registry
7
+ * manifest digest — the `imageDigest` field every {@link BuildAttestation}
8
+ * binds to. The digest must be the value the registry returns for the pushed
9
+ * tag, not a locally-computed one, so the attestation references the exact
10
+ * bytes the cluster will pull.
11
+ *
12
+ * Every function shells out via `child_process.spawn` with an argv array (never
13
+ * a shell string), so registry passwords and tags can't be shell-injected.
14
+ * Secrets (the one-time push password) are fed on stdin, never on argv.
15
+ */
16
+ import { spawn } from 'child_process';
17
+ /** Registry coordinates returned by `deployment.init` (`BaasInitResponse`). */
18
+ export type RegistryCreds = {
19
+ server: string;
20
+ username: string;
21
+ password: string;
22
+ repository: string;
23
+ };
24
+ /**
25
+ * The fully-qualified image reference for a pushed artifact:
26
+ * `<server>/<repository>:<tag>`.
27
+ */
28
+ export declare function imageRef(registry: RegistryCreds, tag: string): string;
29
+ /**
30
+ * `docker login` to the per-app Harbor registry using the one-time push robot
31
+ * credentials. Password is fed on stdin (`--password-stdin`).
32
+ *
33
+ * @throws if docker exits non-zero (login failed).
34
+ */
35
+ export declare function dockerLogin(registry: RegistryCreds, spawnFn?: typeof spawn): Promise<void>;
36
+ /**
37
+ * Parse the registry manifest digest from `docker push` output.
38
+ *
39
+ * A successful `docker push` prints a summary line of the shape:
40
+ * `v0.1.0: digest: sha256:<64hex> size: 1786`
41
+ * We extract the `sha256:<64hex>` token. This is the registry's MANIFEST
42
+ * digest for the pushed tag — exactly what the attestation's `imageDigest`
43
+ * must carry.
44
+ *
45
+ * @returns the `sha256:<64hex>` digest, or `null` if no digest line is present.
46
+ */
47
+ export declare function parsePushDigest(output: string): string | null;
48
+ /**
49
+ * Pick the `sha256:<64hex>` digest for `<server>/<repository>` out of a parsed
50
+ * `docker inspect ... .RepoDigests` array. Pure — shared by both the
51
+ * `spawn`-based {@link inspectImageDigest} and the `execFileSync`-based
52
+ * `buildAndPush` inspect-fallback in `_lib/baas.ts`, so the matching rule lives
53
+ * in exactly one place.
54
+ *
55
+ * `stdout` is the raw `{{json .RepoDigests}}` output (a JSON string array).
56
+ * Returns the lower-cased `sha256:<64hex>` of the first entry whose ref matches
57
+ * `<server>/<repository>@`, or `null` if the JSON is unparseable / no entry
58
+ * matches.
59
+ */
60
+ export declare function parseRepoDigests(stdout: string, registry: RegistryCreds): string | null;
61
+ /**
62
+ * `docker inspect` fallback to recover a pushed image's registry digest from
63
+ * its `RepoDigests` when the push output didn't surface one (older docker,
64
+ * buildx drivers, or an externally-run push we're inspecting after the fact).
65
+ *
66
+ * Returns the `sha256:<64hex>` portion of the matching `RepoDigests` entry, or
67
+ * `null` if none matches `<server>/<repository>`.
68
+ */
69
+ export declare function inspectImageDigest(registry: RegistryCreds, tag: string, spawnFn?: typeof spawn): Promise<string | null>;
70
+ /**
71
+ * Push the backend image and RECORD its registry manifest digest.
72
+ *
73
+ * Runs `docker push <server>/<repository>:<tag>`, parses the digest from the
74
+ * push summary, and falls back to {@link inspectImageDigest} if the summary
75
+ * didn't carry one. Throws if the push fails OR if no digest can be recovered
76
+ * (a deploy must never proceed with an unverifiable artifact reference).
77
+ *
78
+ * @returns the `sha256:<64hex>` manifest digest of the pushed tag.
79
+ * @throws if push exits non-zero, or if no manifest digest can be determined.
80
+ */
81
+ export declare function dockerPush(registry: RegistryCreds, tag: string, spawnFn?: typeof spawn): Promise<string>;
82
+ //# sourceMappingURL=docker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../src/_lib/docker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAwCF;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,OAAO,KAAK,GACrB,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI7D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAiBvF;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,OAAO,KAAK,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQxB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,OAAO,KAAK,GACrB,OAAO,CAAC,MAAM,CAAC,CAiBjB"}
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.imageRef = imageRef;
4
+ exports.dockerLogin = dockerLogin;
5
+ exports.parsePushDigest = parsePushDigest;
6
+ exports.parseRepoDigests = parseRepoDigests;
7
+ exports.inspectImageDigest = inspectImageDigest;
8
+ exports.dockerPush = dockerPush;
9
+ /**
10
+ * Thin, testable wrappers around the `docker` CLI for the trusted-deploy flow.
11
+ *
12
+ * Before the trusted-deploy arc the CLI only PRINTED the `docker login` /
13
+ * `docker push` commands and waited for the developer to run them by hand.
14
+ * Now the CLI wraps the push so it can RECORD the pushed image's registry
15
+ * manifest digest — the `imageDigest` field every {@link BuildAttestation}
16
+ * binds to. The digest must be the value the registry returns for the pushed
17
+ * tag, not a locally-computed one, so the attestation references the exact
18
+ * bytes the cluster will pull.
19
+ *
20
+ * Every function shells out via `child_process.spawn` with an argv array (never
21
+ * a shell string), so registry passwords and tags can't be shell-injected.
22
+ * Secrets (the one-time push password) are fed on stdin, never on argv.
23
+ */
24
+ const child_process_1 = require("child_process");
25
+ /**
26
+ * Run `docker <args>` to completion, capturing stdout/stderr. `passwordStdin`,
27
+ * when set, is written to the child's stdin then the stream is closed — used to
28
+ * feed `docker login --password-stdin` without the secret ever touching argv or
29
+ * the process table.
30
+ *
31
+ * Injectable `spawnFn` keeps this unit-testable: specs pass a fake that returns
32
+ * canned output without a real docker daemon.
33
+ */
34
+ async function runDocker(args, opts = {}) {
35
+ const spawnImpl = opts.spawnFn ?? child_process_1.spawn;
36
+ return new Promise((resolvePromise, reject) => {
37
+ const child = spawnImpl('docker', args, { stdio: ['pipe', 'pipe', 'pipe'] });
38
+ let stdout = '';
39
+ let stderr = '';
40
+ child.stdout?.on('data', (d) => {
41
+ stdout += d.toString();
42
+ });
43
+ child.stderr?.on('data', (d) => {
44
+ stderr += d.toString();
45
+ });
46
+ child.on('error', (err) => reject(err));
47
+ child.on('close', (code) => resolvePromise({ code: code ?? 0, stdout, stderr }));
48
+ if (opts.passwordStdin !== undefined) {
49
+ child.stdin?.write(opts.passwordStdin);
50
+ }
51
+ child.stdin?.end();
52
+ });
53
+ }
54
+ /**
55
+ * The fully-qualified image reference for a pushed artifact:
56
+ * `<server>/<repository>:<tag>`.
57
+ */
58
+ function imageRef(registry, tag) {
59
+ return `${registry.server}/${registry.repository}:${tag}`;
60
+ }
61
+ /**
62
+ * `docker login` to the per-app Harbor registry using the one-time push robot
63
+ * credentials. Password is fed on stdin (`--password-stdin`).
64
+ *
65
+ * @throws if docker exits non-zero (login failed).
66
+ */
67
+ async function dockerLogin(registry, spawnFn) {
68
+ const { code, stderr } = await runDocker(['login', registry.server, '-u', registry.username, '--password-stdin'], { passwordStdin: registry.password, spawnFn });
69
+ if (code !== 0) {
70
+ throw new Error(`docker login failed (exit ${code}): ${stderr.trim()}`);
71
+ }
72
+ }
73
+ /**
74
+ * Parse the registry manifest digest from `docker push` output.
75
+ *
76
+ * A successful `docker push` prints a summary line of the shape:
77
+ * `v0.1.0: digest: sha256:<64hex> size: 1786`
78
+ * We extract the `sha256:<64hex>` token. This is the registry's MANIFEST
79
+ * digest for the pushed tag — exactly what the attestation's `imageDigest`
80
+ * must carry.
81
+ *
82
+ * @returns the `sha256:<64hex>` digest, or `null` if no digest line is present.
83
+ */
84
+ function parsePushDigest(output) {
85
+ // `digest:` precedes the manifest digest in docker's push summary.
86
+ const m = output.match(/digest:\s*(sha256:[a-f0-9]{64})/i);
87
+ return m ? m[1].toLowerCase() : null;
88
+ }
89
+ /**
90
+ * Pick the `sha256:<64hex>` digest for `<server>/<repository>` out of a parsed
91
+ * `docker inspect ... .RepoDigests` array. Pure — shared by both the
92
+ * `spawn`-based {@link inspectImageDigest} and the `execFileSync`-based
93
+ * `buildAndPush` inspect-fallback in `_lib/baas.ts`, so the matching rule lives
94
+ * in exactly one place.
95
+ *
96
+ * `stdout` is the raw `{{json .RepoDigests}}` output (a JSON string array).
97
+ * Returns the lower-cased `sha256:<64hex>` of the first entry whose ref matches
98
+ * `<server>/<repository>@`, or `null` if the JSON is unparseable / no entry
99
+ * matches.
100
+ */
101
+ function parseRepoDigests(stdout, registry) {
102
+ let repoDigests;
103
+ try {
104
+ repoDigests = JSON.parse(stdout.trim());
105
+ }
106
+ catch {
107
+ return null;
108
+ }
109
+ if (!Array.isArray(repoDigests))
110
+ return null;
111
+ const repoPrefix = `${registry.server}/${registry.repository}@`;
112
+ for (const rd of repoDigests) {
113
+ if (typeof rd === 'string' && rd.startsWith(repoPrefix)) {
114
+ const at = rd.indexOf('@');
115
+ const digest = rd.slice(at + 1);
116
+ if (/^sha256:[a-f0-9]{64}$/i.test(digest))
117
+ return digest.toLowerCase();
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+ /**
123
+ * `docker inspect` fallback to recover a pushed image's registry digest from
124
+ * its `RepoDigests` when the push output didn't surface one (older docker,
125
+ * buildx drivers, or an externally-run push we're inspecting after the fact).
126
+ *
127
+ * Returns the `sha256:<64hex>` portion of the matching `RepoDigests` entry, or
128
+ * `null` if none matches `<server>/<repository>`.
129
+ */
130
+ async function inspectImageDigest(registry, tag, spawnFn) {
131
+ const ref = imageRef(registry, tag);
132
+ const { code, stdout } = await runDocker(['inspect', '--format', '{{json .RepoDigests}}', ref], { spawnFn });
133
+ if (code !== 0)
134
+ return null;
135
+ return parseRepoDigests(stdout, registry);
136
+ }
137
+ /**
138
+ * Push the backend image and RECORD its registry manifest digest.
139
+ *
140
+ * Runs `docker push <server>/<repository>:<tag>`, parses the digest from the
141
+ * push summary, and falls back to {@link inspectImageDigest} if the summary
142
+ * didn't carry one. Throws if the push fails OR if no digest can be recovered
143
+ * (a deploy must never proceed with an unverifiable artifact reference).
144
+ *
145
+ * @returns the `sha256:<64hex>` manifest digest of the pushed tag.
146
+ * @throws if push exits non-zero, or if no manifest digest can be determined.
147
+ */
148
+ async function dockerPush(registry, tag, spawnFn) {
149
+ const ref = imageRef(registry, tag);
150
+ const { code, stdout, stderr } = await runDocker(['push', ref], { spawnFn });
151
+ if (code !== 0) {
152
+ throw new Error(`docker push failed (exit ${code}): ${stderr.trim()}`);
153
+ }
154
+ const fromPush = parsePushDigest(stdout + '\n' + stderr);
155
+ if (fromPush)
156
+ return fromPush;
157
+ const fromInspect = await inspectImageDigest(registry, tag, spawnFn);
158
+ if (fromInspect)
159
+ return fromInspect;
160
+ throw new Error(`pushed ${ref} but could not determine its registry manifest digest ` +
161
+ '(no `digest: sha256:...` in push output and no matching RepoDigests). ' +
162
+ 'Re-run with --image-digest <sha256:...> to supply it explicitly.');
163
+ }
164
+ //# sourceMappingURL=docker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/_lib/docker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ export type FaucetChallengeResponse = {
2
+ challengeId: string;
3
+ message: string;
4
+ expiresInSeconds: number;
5
+ };
6
+ export type FaucetDispensedResponse = {
7
+ status: 'dispensed';
8
+ amount: string;
9
+ txHash: string;
10
+ remainingDailyAllowance: string;
11
+ };
12
+ export type FaucetTrustLineRequiredResponse = {
13
+ status: 'trustline_required';
14
+ trustLine: {
15
+ currency: string;
16
+ issuer: string;
17
+ limit: string;
18
+ };
19
+ };
20
+ export type FaucetRateLimitedResponse = {
21
+ status: 'rate_limited';
22
+ retryAfterSeconds: number;
23
+ };
24
+ /**
25
+ * Discriminated union of the three documented `POST /faucet/hsuite` outcomes.
26
+ * Switch on `.status`.
27
+ */
28
+ export type FaucetDispenseResponse = FaucetDispensedResponse | FaucetTrustLineRequiredResponse | FaucetRateLimitedResponse;
29
+ export type FaucetStatusResponse = {
30
+ dispensedToday: string;
31
+ [key: string]: unknown;
32
+ };
33
+ export type FetchLike = (input: string, init?: any) => Promise<{
34
+ ok: boolean;
35
+ status: number;
36
+ text(): Promise<string>;
37
+ json(): Promise<unknown>;
38
+ }>;
39
+ export type FaucetClientOptions = {
40
+ baseUrl: string;
41
+ fetchImpl?: FetchLike;
42
+ };
43
+ /**
44
+ * Validator-side HSUITE-testnet faucet HTTP client.
45
+ *
46
+ * Adapter over the SDK's {@link SdkFaucetClient}. All HTTP/fetch logic lives in
47
+ * the SDK's `createHttpClient`; this class only adapts method names + base URL
48
+ * and (for tests) bridges the injectable `fetchImpl` seam onto the SDK's
49
+ * `HttpClient` interface.
50
+ */
51
+ export declare class FaucetClient {
52
+ private readonly sdk;
53
+ constructor(opts: FaucetClientOptions);
54
+ challenge(chain: string, recipientAddress: string): Promise<FaucetChallengeResponse>;
55
+ dispense(payload: {
56
+ chain: string;
57
+ recipientAddress: string;
58
+ challengeId: string;
59
+ signature: string;
60
+ publicKey: string;
61
+ }): Promise<FaucetDispenseResponse>;
62
+ status(chain: string, recipientAddress: string): Promise<FaucetStatusResponse>;
63
+ }
64
+ //# sourceMappingURL=faucet-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faucet-client.d.ts","sourceRoot":"","sources":["../../src/_lib/faucet-client.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,cAAc,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAC9B,uBAAuB,GACvB,+BAA+B,GAC/B,yBAAyB,CAAC;AAE9B,MAAM,MAAM,oBAAoB,GAAG;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC;IAC7D,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B,CAAC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;gBAE1B,IAAI,EAAE,mBAAmB;IAW/B,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAIpF,QAAQ,CAAC,OAAO,EAAE;QACtB,KAAK,EAAE,MAAM,CAAC;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAI7B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAGrF"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FaucetClient = void 0;
4
+ /**
5
+ * Thin CLI adapter over the SDK's faucet client.
6
+ *
7
+ * The validator-side `/api/v3/faucet/hsuite/*` surface is already covered by
8
+ * `FaucetClient` in `@hsuite/smart-engines-sdk`. To avoid the CLI drifting from
9
+ * the SDK, this file no longer hand-rolls its own `fetch`/HTTP plumbing — it
10
+ * delegates every call to the SDK client, built on the SDK's shared
11
+ * `createHttpClient` (whose `baseUrl` carries the `/api/v3` prefix the SDK paths
12
+ * omit).
13
+ *
14
+ * The public shape (class name, method names, response types) is preserved so
15
+ * the CLI command bodies (`commands/faucet.ts`) and their unit tests are
16
+ * unchanged. Tests still inject a fake via the constructor's `fetchImpl` seam.
17
+ */
18
+ const smart_engines_sdk_1 = require("@hsuite/smart-engines-sdk");
19
+ /**
20
+ * Validator-side HSUITE-testnet faucet HTTP client.
21
+ *
22
+ * Adapter over the SDK's {@link SdkFaucetClient}. All HTTP/fetch logic lives in
23
+ * the SDK's `createHttpClient`; this class only adapts method names + base URL
24
+ * and (for tests) bridges the injectable `fetchImpl` seam onto the SDK's
25
+ * `HttpClient` interface.
26
+ */
27
+ class FaucetClient {
28
+ sdk;
29
+ constructor(opts) {
30
+ // The SDK faucet paths are `/faucet/hsuite/...` — carry the `/api/v3`
31
+ // prefix on the HttpClient baseUrl so the full URL matches the validator
32
+ // controller mount.
33
+ const baseUrl = `${opts.baseUrl.replace(/\/$/, '')}/api/v3`;
34
+ const http = opts.fetchImpl
35
+ ? makeFetchBackedHttpClient(baseUrl, opts.fetchImpl)
36
+ : (0, smart_engines_sdk_1.createHttpClient)({ baseUrl });
37
+ this.sdk = new smart_engines_sdk_1.FaucetClient(http);
38
+ }
39
+ async challenge(chain, recipientAddress) {
40
+ return this.sdk.requestChallenge(chain, recipientAddress);
41
+ }
42
+ async dispense(payload) {
43
+ return this.sdk.dispense(payload);
44
+ }
45
+ async status(chain, recipientAddress) {
46
+ return this.sdk.getStatus(chain, recipientAddress);
47
+ }
48
+ }
49
+ exports.FaucetClient = FaucetClient;
50
+ /**
51
+ * Adapt an injected `fetchImpl` (the CLI's test seam) onto the SDK's
52
+ * {@link HttpClient} interface so the SDK client can be exercised with a fake
53
+ * transport. Only the verbs the faucet client uses (`get`/`post`) are
54
+ * implemented; the rest throw if called.
55
+ *
56
+ * Mirrors the SDK's own `createHttpClient` request/error semantics (JSON body,
57
+ * `${method} ${path} → ${status}` error on non-2xx) so behavior is identical.
58
+ */
59
+ function makeFetchBackedHttpClient(baseUrl, fetchImpl) {
60
+ async function send(method, path, body) {
61
+ const url = `${baseUrl}${path}`;
62
+ const init = {
63
+ method,
64
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
65
+ };
66
+ if (body !== undefined)
67
+ init.body = JSON.stringify(body);
68
+ const res = await fetchImpl(url, init);
69
+ if (!res.ok) {
70
+ const text = await res.text().catch(() => '');
71
+ throw new Error(`${method} ${path} → ${res.status}: ${text.slice(0, 500)}`);
72
+ }
73
+ return (await res.json());
74
+ }
75
+ const unsupported = (verb) => () => {
76
+ throw new Error(`fetch-backed HttpClient: ${verb} not supported in CLI faucet adapter`);
77
+ };
78
+ return {
79
+ get: (path) => send('GET', path),
80
+ post: (path, b) => send('POST', path, b),
81
+ put: unsupported('put'),
82
+ patch: unsupported('patch'),
83
+ delete: unsupported('delete'),
84
+ getText: unsupported('getText'),
85
+ getBinary: unsupported('getBinary'),
86
+ getBinaryWithMeta: unsupported('getBinaryWithMeta'),
87
+ upload: unsupported('upload'),
88
+ setAuthToken: () => undefined,
89
+ getAuthToken: () => undefined,
90
+ };
91
+ }
92
+ //# sourceMappingURL=faucet-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faucet-client.js","sourceRoot":"","sources":["../../src/_lib/faucet-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /** The frameworks the SDK can build, mirroring `BuildAttestation['framework']`. */
2
+ export type Framework = 'ionic' | 'nestjs';
3
+ /**
4
+ * The result of classifying a project directory.
5
+ *
6
+ * `framework` is the attestation framework; `sdkPackage` is the specific
7
+ * HSUITE dependency that proved SDK usage (recorded for diagnostics);
8
+ * `sdkVersion` is the declared semver range of that dependency, used as the
9
+ * attestation's `sdkVersion` when the installed version can't be resolved.
10
+ */
11
+ export type FrameworkDetection = {
12
+ framework: Framework;
13
+ sdkPackage: string;
14
+ sdkVersionRange: string;
15
+ };
16
+ /**
17
+ * Thrown when a deploy target carries no HSUITE SDK dependency. Carrying a
18
+ * distinct error type lets the deploy command surface the actionable message
19
+ * AND lets specs assert the abort without string-matching console output.
20
+ */
21
+ export declare class NoSdkError extends Error {
22
+ constructor(message: string);
23
+ }
24
+ /**
25
+ * Classify a project directory's framework, or abort.
26
+ *
27
+ * Classification order (most-specific dependency wins):
28
+ * 1. `@hsuite/native-connect-*` (+ `@ionic/angular`) → `ionic` frontend.
29
+ * 2. `@hsuite/smart-engines-sdk` (+ `@nestjs/core`) → `nestjs` backend.
30
+ * 3. neither HSUITE dependency present → {@link NoSdkError}.
31
+ *
32
+ * The `@ionic/angular` / `@nestjs/core` co-presence is REQUIRED so a project
33
+ * that merely lists an HSUITE package without the matching framework runtime
34
+ * is reported precisely rather than silently mis-bucketed.
35
+ *
36
+ * @param projectDir - Smart-app project root (the dir containing package.json).
37
+ * @returns The detected framework + the proving SDK dependency.
38
+ * @throws {NoSdkError} if no HSUITE SDK dependency is present.
39
+ * @throws {Error} if the framework runtime that pairs with the HSUITE dep is
40
+ * missing (actionable: "add @nestjs/core" / "add @ionic/angular").
41
+ */
42
+ export declare function detectFramework(projectDir: string): FrameworkDetection;
43
+ /**
44
+ * Resolve the SDK version the project is actually built against, for the
45
+ * attestation's `sdkVersion` field.
46
+ *
47
+ * Prefers the INSTALLED version (`node_modules/<pkg>/package.json`'s `version`)
48
+ * because that is the exact code the artifact links; falls back to the declared
49
+ * semver range from the project `package.json` when `node_modules` isn't
50
+ * present (e.g. a CI image that built elsewhere). The fallback range is still a
51
+ * meaningful, signable claim about the intended SDK.
52
+ *
53
+ * @param projectDir - Smart-app project root.
54
+ * @param detection - The result of {@link detectFramework} (carries the package
55
+ * name + declared range).
56
+ * @returns The installed exact version if resolvable, else the declared range.
57
+ */
58
+ export declare function resolveSdkVersion(projectDir: string, detection: FrameworkDetection): string;
59
+ //# sourceMappingURL=framework.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework.d.ts","sourceRoot":"","sources":["../../src/_lib/framework.ts"],"names":[],"mappings":"AAqBA,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAQ3C;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAOF;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI5B;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CA4CtE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,kBAAkB,GAC5B,MAAM,CAgBR"}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoSdkError = void 0;
4
+ exports.detectFramework = detectFramework;
5
+ exports.resolveSdkVersion = resolveSdkVersion;
6
+ /**
7
+ * Client-side half of the SDK restriction: prove a deploy target was built
8
+ * with the HSUITE Smart Engines SDK and classify which framework it uses.
9
+ *
10
+ * The trusted-deploy arc signs a {@link BuildAttestation} over every deploy;
11
+ * one of its fields is `framework: 'ionic' | 'nestjs'`. This module derives
12
+ * that field — and, critically, ABORTS the deploy when the project carries NO
13
+ * HSUITE SDK dependency at all. A smart-app that wasn't built against the SDK
14
+ * has nothing to attest and must never reach the signing path.
15
+ *
16
+ * Detection is dependency-driven (reads the project's `package.json`), matching
17
+ * the real showcase apps:
18
+ * - backend (`apps/showcase/smart-app`): `@hsuite/smart-engines-sdk` + `@nestjs/core`
19
+ * - frontend (`apps/showcase/ionic-dapp`): `@hsuite/native-connect-*` + `@ionic/angular`
20
+ *
21
+ * This module is PURE I/O-at-the-edge: it reads one file and classifies. No
22
+ * network, no clock.
23
+ */
24
+ const fs_1 = require("fs");
25
+ const path_1 = require("path");
26
+ /** The `@hsuite/native-connect-*` packages an Ionic smart-app frontend pulls in. */
27
+ const NATIVE_CONNECT_PREFIX = '@hsuite/native-connect-';
28
+ /** The backend SDK package. Its presence is the nestjs (backend) signal. */
29
+ const BACKEND_SDK = '@hsuite/smart-engines-sdk';
30
+ /**
31
+ * Thrown when a deploy target carries no HSUITE SDK dependency. Carrying a
32
+ * distinct error type lets the deploy command surface the actionable message
33
+ * AND lets specs assert the abort without string-matching console output.
34
+ */
35
+ class NoSdkError extends Error {
36
+ constructor(message) {
37
+ super(message);
38
+ this.name = 'NoSdkError';
39
+ }
40
+ }
41
+ exports.NoSdkError = NoSdkError;
42
+ /**
43
+ * Merge `dependencies` + `devDependencies` into one lookup. Smart-apps declare
44
+ * the SDK under `dependencies`, but tooling/CI templates occasionally place a
45
+ * build-time SDK under `devDependencies`; treat both as "the project uses the
46
+ * SDK" for detection purposes.
47
+ */
48
+ function allDeps(pkg) {
49
+ return { ...(pkg.devDependencies ?? {}), ...(pkg.dependencies ?? {}) };
50
+ }
51
+ /**
52
+ * Read + parse the `package.json` at `projectDir`. Throws a clear error if the
53
+ * directory has no `package.json` (deploy target must be a real project root).
54
+ */
55
+ function readPackageJson(projectDir) {
56
+ const pkgPath = (0, path_1.join)((0, path_1.resolve)(projectDir), 'package.json');
57
+ if (!(0, fs_1.existsSync)(pkgPath)) {
58
+ throw new Error(`no package.json found at ${pkgPath} — point --project at the smart-app root`);
59
+ }
60
+ try {
61
+ return JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf8'));
62
+ }
63
+ catch (err) {
64
+ throw new Error(`failed to parse ${pkgPath}: ${err.message}`);
65
+ }
66
+ }
67
+ /**
68
+ * Classify a project directory's framework, or abort.
69
+ *
70
+ * Classification order (most-specific dependency wins):
71
+ * 1. `@hsuite/native-connect-*` (+ `@ionic/angular`) → `ionic` frontend.
72
+ * 2. `@hsuite/smart-engines-sdk` (+ `@nestjs/core`) → `nestjs` backend.
73
+ * 3. neither HSUITE dependency present → {@link NoSdkError}.
74
+ *
75
+ * The `@ionic/angular` / `@nestjs/core` co-presence is REQUIRED so a project
76
+ * that merely lists an HSUITE package without the matching framework runtime
77
+ * is reported precisely rather than silently mis-bucketed.
78
+ *
79
+ * @param projectDir - Smart-app project root (the dir containing package.json).
80
+ * @returns The detected framework + the proving SDK dependency.
81
+ * @throws {NoSdkError} if no HSUITE SDK dependency is present.
82
+ * @throws {Error} if the framework runtime that pairs with the HSUITE dep is
83
+ * missing (actionable: "add @nestjs/core" / "add @ionic/angular").
84
+ */
85
+ function detectFramework(projectDir) {
86
+ const pkg = readPackageJson(projectDir);
87
+ const deps = allDeps(pkg);
88
+ const names = Object.keys(deps);
89
+ const nativeConnect = names.find((n) => n.startsWith(NATIVE_CONNECT_PREFIX));
90
+ const hasBackendSdk = BACKEND_SDK in deps;
91
+ // No HSUITE SDK dependency of either flavor → abort before any signing.
92
+ if (!nativeConnect && !hasBackendSdk) {
93
+ throw new NoSdkError('smart-apps must be built with the HSUITE SDK — install ' +
94
+ `\`${BACKEND_SDK}\` (NestJS backend) or the \`${NATIVE_CONNECT_PREFIX}*\` ` +
95
+ 'packages (Ionic frontend). See https://docs.hsuite.network/sdk');
96
+ }
97
+ // Ionic frontend: native-connect packages present.
98
+ if (nativeConnect) {
99
+ if (!('@ionic/angular' in deps)) {
100
+ throw new Error(`found ${nativeConnect} but no \`@ionic/angular\` — an Ionic smart-app ` +
101
+ 'frontend must depend on @ionic/angular');
102
+ }
103
+ return {
104
+ framework: 'ionic',
105
+ sdkPackage: nativeConnect,
106
+ sdkVersionRange: deps[nativeConnect],
107
+ };
108
+ }
109
+ // NestJS backend: smart-engines-sdk present.
110
+ if (!('@nestjs/core' in deps)) {
111
+ throw new Error(`found ${BACKEND_SDK} but no \`@nestjs/core\` — a NestJS smart-app ` +
112
+ 'backend must depend on @nestjs/core');
113
+ }
114
+ return {
115
+ framework: 'nestjs',
116
+ sdkPackage: BACKEND_SDK,
117
+ sdkVersionRange: deps[BACKEND_SDK],
118
+ };
119
+ }
120
+ /**
121
+ * Resolve the SDK version the project is actually built against, for the
122
+ * attestation's `sdkVersion` field.
123
+ *
124
+ * Prefers the INSTALLED version (`node_modules/<pkg>/package.json`'s `version`)
125
+ * because that is the exact code the artifact links; falls back to the declared
126
+ * semver range from the project `package.json` when `node_modules` isn't
127
+ * present (e.g. a CI image that built elsewhere). The fallback range is still a
128
+ * meaningful, signable claim about the intended SDK.
129
+ *
130
+ * @param projectDir - Smart-app project root.
131
+ * @param detection - The result of {@link detectFramework} (carries the package
132
+ * name + declared range).
133
+ * @returns The installed exact version if resolvable, else the declared range.
134
+ */
135
+ function resolveSdkVersion(projectDir, detection) {
136
+ const installed = (0, path_1.join)((0, path_1.resolve)(projectDir), 'node_modules', detection.sdkPackage, 'package.json');
137
+ if ((0, fs_1.existsSync)(installed)) {
138
+ try {
139
+ const pkg = JSON.parse((0, fs_1.readFileSync)(installed, 'utf8'));
140
+ if (pkg.version)
141
+ return pkg.version;
142
+ }
143
+ catch {
144
+ // fall through to the declared range
145
+ }
146
+ }
147
+ return detection.sdkVersionRange;
148
+ }
149
+ //# sourceMappingURL=framework.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework.js","sourceRoot":"","sources":["../../src/_lib/framework.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Fail-closed guard shared by `deploy` and `redeploy`: refuse to ship a
3
+ * frontend build that still carries unfilled `REPLACE_BEFORE_DEPLOY:<key>`
4
+ * placeholders (per-deployment config that was never set).
5
+ *
6
+ * Such a build refuses to bootstrap in the browser (the SPA's `main.ts` asserts
7
+ * on the sentinel prefix), and the CLI would otherwise tar+upload it silently —
8
+ * which is exactly how a non-bootstrapping bundle reached production. Matches
9
+ * the sentinel VALUE form (`:` followed by a letter) so it does NOT
10
+ * false-positive on the guard's own comparison literal (`REPLACE_BEFORE_DEPLOY:"`).
11
+ */
12
+ export declare function assertNoDeploySentinels(dir: string): Promise<void>;
13
+ //# sourceMappingURL=frontend-guard.d.ts.map