@tangle-network/blueprint-ui 0.1.2 → 0.3.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.
@@ -1,4 +1,8 @@
1
1
  import { Address } from 'viem';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import * as class_variance_authority_types from 'class-variance-authority/types';
5
+ import { VariantProps } from 'class-variance-authority';
2
6
 
3
7
  /**
4
8
  * Blueprint Registry — defines the metadata layer for Tangle blueprints.
@@ -84,4 +88,37 @@ declare function getAllBlueprints(): BlueprintDefinition[];
84
88
  declare function getBlueprintJobs(blueprintId: string, category?: JobCategory): JobDefinition[];
85
89
  declare function getJobById(blueprintId: string, jobId: number): JobDefinition | undefined;
86
90
 
87
- export { type AbiContextParam as A, type BlueprintDefinition as B, type JobDefinition as J, type JobCategory as a, type JobFieldDef as b, getBlueprint as c, getBlueprintJobs as d, getJobById as e, getAllBlueprints as g, registerBlueprint as r };
91
+ declare const buttonVariants: (props?: ({
92
+ variant?: "default" | "link" | "success" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
93
+ size?: "default" | "sm" | "lg" | "icon" | "icon-sm" | null | undefined;
94
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
95
+ declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<'button'> & VariantProps<typeof buttonVariants> & {
96
+ asChild?: boolean;
97
+ }): react_jsx_runtime.JSX.Element;
98
+
99
+ type Action = {
100
+ label: string;
101
+ href?: string;
102
+ onClick?: () => void;
103
+ variant?: React.ComponentProps<typeof Button>['variant'];
104
+ disabled?: boolean;
105
+ };
106
+ type BlueprintHostHeroProps = {
107
+ title: string;
108
+ tagline?: string;
109
+ description?: string;
110
+ badges?: string[];
111
+ actions?: Action[];
112
+ children?: React.ReactNode;
113
+ className?: string;
114
+ };
115
+ declare function BlueprintHostHero({ title, tagline, description, badges, actions, children, className, }: BlueprintHostHeroProps): react_jsx_runtime.JSX.Element;
116
+
117
+ type BlueprintHostPanelProps = {
118
+ title: string;
119
+ children: React.ReactNode;
120
+ className?: string;
121
+ };
122
+ declare function BlueprintHostPanel({ title, children, className, }: BlueprintHostPanelProps): react_jsx_runtime.JSX.Element;
123
+
124
+ export { type AbiContextParam as A, type BlueprintDefinition as B, type JobDefinition as J, BlueprintHostHero as a, type BlueprintHostHeroProps as b, BlueprintHostPanel as c, type BlueprintHostPanelProps as d, type JobCategory as e, type JobFieldDef as f, getAllBlueprints as g, getBlueprint as h, getBlueprintJobs as i, getJobById as j, Button as k, buttonVariants as l, registerBlueprint as r };
@@ -1,3 +1,10 @@
1
+ // src/utils.ts
2
+ import { clsx } from "clsx";
3
+ import { twMerge } from "tailwind-merge";
4
+ function cn(...inputs) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+
1
8
  // src/utils/resolveOperatorRpc.ts
2
9
  function resolveOperatorRpc(raw) {
3
10
  if (typeof window === "undefined") return raw;
@@ -305,6 +312,9 @@ var tangleServicesAbi = [
305
312
  name: "details",
306
313
  type: "tuple",
307
314
  components: [
315
+ // tnt-core v0.13.0: `requester` is the first field of QuoteDetails.
316
+ // The contract enforces `requester == msg.sender` and rejects address(0).
317
+ { name: "requester", type: "address" },
308
318
  { name: "blueprintId", type: "uint64" },
309
319
  { name: "ttlBlocks", type: "uint64" },
310
320
  { name: "totalCost", type: "uint256" },
@@ -595,6 +605,295 @@ function coerceValue(value, abiType) {
595
605
  }
596
606
  }
597
607
 
608
+ // src/host/resolver.ts
609
+ var EXPERIENCE_TIER_LABELS = {
610
+ generic: "Protocol fallback",
611
+ declarative: "Declarative blueprint UI",
612
+ "curated-module": "Curated app module",
613
+ "external-app": "External app handoff"
614
+ };
615
+ var SLUG_POLICY_LABELS = {
616
+ reserved: "Reserved slug",
617
+ "publisher-scoped": "Publisher-scoped slug",
618
+ "public-requested": "Public requested slug"
619
+ };
620
+ var SURFACE_LABELS = {
621
+ "generic-overview": "Generic overview",
622
+ "service-explorer": "Service explorer",
623
+ "service-console": "Service console",
624
+ "actions-panel": "Actions panel",
625
+ resources: "Resources",
626
+ chat: "Chat",
627
+ vaults: "Vaults",
628
+ metrics: "Metrics",
629
+ permissions: "Permissions"
630
+ };
631
+ var PUBLISHER_VERIFICATION_LABELS = {
632
+ "first-party": "First-party publisher",
633
+ verified: "Verified publisher",
634
+ unverified: "Unverified publisher"
635
+ };
636
+ var EXTERNAL_APP_TRUST_LABELS = {
637
+ trusted: "Trusted external app",
638
+ restricted: "Restricted external app"
639
+ };
640
+ function buildCanonicalBlueprintSlug(entry) {
641
+ if (entry.canonicalSlug) {
642
+ return entry.canonicalSlug;
643
+ }
644
+ if (entry.slugPolicy === "reserved" || !entry.publisher.namespace) {
645
+ return entry.slug;
646
+ }
647
+ return `@${entry.publisher.namespace}/${entry.slug}`;
648
+ }
649
+ function resolveBlueprintAppView(entry) {
650
+ return {
651
+ slug: entry.slug,
652
+ canonicalSlug: buildCanonicalBlueprintSlug(entry),
653
+ blueprintId: entry.blueprintId,
654
+ publisher: entry.publisher,
655
+ tier: entry.tier,
656
+ slugPolicy: entry.slugPolicy,
657
+ manifest: entry.manifest,
658
+ module: entry.module,
659
+ fallbackEnabled: true
660
+ };
661
+ }
662
+ function toBlueprintAppEntry(view) {
663
+ return {
664
+ slug: view.slug,
665
+ canonicalSlug: view.canonicalSlug,
666
+ blueprintId: view.blueprintId,
667
+ publisher: view.publisher,
668
+ tier: view.tier,
669
+ slugPolicy: view.slugPolicy,
670
+ manifest: view.manifest,
671
+ module: view.module
672
+ };
673
+ }
674
+ function getBlueprintExperienceTierLabel(tier) {
675
+ return EXPERIENCE_TIER_LABELS[tier];
676
+ }
677
+ function getBlueprintSlugPolicyLabel(policy) {
678
+ return SLUG_POLICY_LABELS[policy];
679
+ }
680
+ function getBlueprintSurfaceLabel(surface) {
681
+ return SURFACE_LABELS[surface];
682
+ }
683
+ function getBlueprintPublisherVerificationLabel(verification) {
684
+ return PUBLISHER_VERIFICATION_LABELS[verification];
685
+ }
686
+ function getExternalAppTrustLabel(trust) {
687
+ return EXTERNAL_APP_TRUST_LABELS[trust];
688
+ }
689
+ function isVerifiedBlueprintPublisher(publisher) {
690
+ return publisher.verification === "first-party" || publisher.verification === "verified";
691
+ }
692
+ function canPublisherClaimSlug(slug, publisher, reservedSlugs = /* @__PURE__ */ new Set()) {
693
+ if (reservedSlugs.has(slug)) {
694
+ return false;
695
+ }
696
+ return publisher?.namespace !== void 0 && publisher.namespace.trim().length > 0 && (publisher.verification === "verified" || publisher.verification === "first-party");
697
+ }
698
+ function isTrustedExternalAppHost(host, trustedHosts = []) {
699
+ const normalizedHost = host.trim().toLowerCase();
700
+ return trustedHosts.some(
701
+ (trustedHost) => normalizedHost === trustedHost || normalizedHost.endsWith(`.${trustedHost}`)
702
+ );
703
+ }
704
+ function getBlueprintPath(view) {
705
+ if (view.tier === "curated-module" || view.tier === "external-app") {
706
+ return `/blueprints/${view.canonicalSlug}`;
707
+ }
708
+ if (view.blueprintId !== void 0) {
709
+ return `/blueprints/${view.blueprintId.toString()}`;
710
+ }
711
+ return `/blueprints/${view.slug}`;
712
+ }
713
+ function getBlueprintServicePath(view, serviceId) {
714
+ if (view.tier === "curated-module" || view.tier === "external-app") {
715
+ return `${getBlueprintPath(view)}/${serviceId}`;
716
+ }
717
+ return `${getBlueprintPath(view)}/services/${serviceId}`;
718
+ }
719
+ function sanitizeBlueprintSlugPart(value) {
720
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
721
+ }
722
+ function deriveBlueprintRequestedSlug(blueprint) {
723
+ const fromName = sanitizeBlueprintSlugPart(blueprint.name);
724
+ if (fromName.length > 0) {
725
+ return fromName;
726
+ }
727
+ const fromAuthor = sanitizeBlueprintSlugPart(blueprint.author);
728
+ if (fromAuthor.length > 0) {
729
+ return `${fromAuthor}-${blueprint.id.toString()}`;
730
+ }
731
+ return `blueprint-${blueprint.id.toString()}`;
732
+ }
733
+
734
+ // src/components/ui/card.tsx
735
+ import { jsx } from "react/jsx-runtime";
736
+ function Card({ className, ...props }) {
737
+ return /* @__PURE__ */ jsx(
738
+ "div",
739
+ {
740
+ "data-slot": "card",
741
+ className: cn("glass-card rounded-xl text-bp-elements-textPrimary", className),
742
+ ...props
743
+ }
744
+ );
745
+ }
746
+ function CardHeader({ className, ...props }) {
747
+ return /* @__PURE__ */ jsx("div", { "data-slot": "card-header", className: cn("flex flex-col gap-1.5 p-6", className), ...props });
748
+ }
749
+ function CardTitle({ className, ...props }) {
750
+ return /* @__PURE__ */ jsx("div", { "data-slot": "card-title", className: cn("leading-none font-semibold font-display", className), ...props });
751
+ }
752
+ function CardDescription({ className, ...props }) {
753
+ return /* @__PURE__ */ jsx("div", { "data-slot": "card-description", className: cn("text-bp-elements-textSecondary text-sm", className), ...props });
754
+ }
755
+ function CardContent({ className, ...props }) {
756
+ return /* @__PURE__ */ jsx("div", { "data-slot": "card-content", className: cn("px-6 pb-6", className), ...props });
757
+ }
758
+ function CardFooter({ className, ...props }) {
759
+ return /* @__PURE__ */ jsx("div", { "data-slot": "card-footer", className: cn("flex items-center px-6 pb-6", className), ...props });
760
+ }
761
+
762
+ // src/components/ui/badge.tsx
763
+ import { Slot } from "@radix-ui/react-slot";
764
+ import { cva } from "class-variance-authority";
765
+ import { jsx as jsx2 } from "react/jsx-runtime";
766
+ var badgeVariants = cva(
767
+ "inline-flex items-center justify-center rounded-md border px-2.5 py-0.5 text-xs font-semibold font-data uppercase tracking-wider w-fit whitespace-nowrap shrink-0 gap-1 transition-colors",
768
+ {
769
+ variants: {
770
+ variant: {
771
+ default: "border-bp-elements-borderColor bg-bp-elements-background-depth-3 text-bp-elements-textPrimary",
772
+ secondary: "border-bp-elements-dividerColor bg-bp-elements-background-depth-2 text-bp-elements-textSecondary",
773
+ destructive: "border-crimson-500/20 bg-crimson-500/10 text-bp-elements-icon-error",
774
+ success: "border-teal-500/20 bg-teal-500/10 text-bp-elements-icon-success",
775
+ outline: "text-bp-elements-textPrimary border-bp-elements-borderColor bg-transparent",
776
+ accent: "border-violet-500/20 bg-violet-500/10 text-violet-700 dark:text-violet-400",
777
+ amber: "border-amber-500/20 bg-amber-500/10 text-amber-700 dark:text-amber-400",
778
+ running: "border-teal-500/20 bg-teal-500/10 text-teal-600 dark:text-teal-400",
779
+ stopped: "border-amber-500/20 bg-amber-500/10 text-amber-600 dark:text-amber-400",
780
+ cold: "border-blue-500/20 bg-blue-500/10 text-blue-600 dark:text-blue-400"
781
+ }
782
+ },
783
+ defaultVariants: { variant: "default" }
784
+ }
785
+ );
786
+ function Badge({
787
+ className,
788
+ variant,
789
+ asChild = false,
790
+ ...props
791
+ }) {
792
+ const Comp = asChild ? Slot : "span";
793
+ return /* @__PURE__ */ jsx2(Comp, { "data-slot": "badge", className: cn(badgeVariants({ variant }), className), ...props });
794
+ }
795
+
796
+ // src/components/ui/button.tsx
797
+ import { Slot as Slot2 } from "@radix-ui/react-slot";
798
+ import { cva as cva2 } from "class-variance-authority";
799
+ import { jsx as jsx3 } from "react/jsx-runtime";
800
+ var buttonVariants = cva2(
801
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium font-display transition-all duration-200 disabled:pointer-events-none disabled:opacity-40 [&_svg]:pointer-events-none [&_svg:not([class*="size-"])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-violet-400/50 focus-visible:ring-offset-2 focus-visible:ring-offset-bp-elements-background-depth-1',
802
+ {
803
+ variants: {
804
+ variant: {
805
+ default: "bg-violet-600 text-white font-semibold hover:bg-violet-500 shadow-[0_0_20px_rgba(142,89,255,0.25)] hover:shadow-[0_0_30px_rgba(142,89,255,0.35)]",
806
+ destructive: "bg-crimson-500/15 text-crimson-400 border border-crimson-500/20 hover:bg-crimson-500/25 hover:border-crimson-500/30",
807
+ outline: "glass glass-hover text-bp-elements-textPrimary",
808
+ secondary: "bg-bp-elements-background-depth-3 text-bp-elements-textSecondary border border-bp-elements-borderColor hover:bg-bp-elements-background-depth-4 hover:text-bp-elements-textPrimary",
809
+ ghost: "text-bp-elements-textSecondary hover:text-bp-elements-textPrimary hover:bg-bp-elements-item-backgroundHover",
810
+ link: "text-violet-700 dark:text-violet-400 underline-offset-4 hover:underline",
811
+ success: "bg-teal-600/15 text-teal-400 border border-teal-500/20 hover:bg-teal-600/25"
812
+ },
813
+ size: {
814
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
815
+ sm: "h-8 rounded-md gap-1.5 px-3 text-xs has-[>svg]:px-2.5",
816
+ lg: "h-11 rounded-xl px-7 text-base has-[>svg]:px-5",
817
+ icon: "size-9",
818
+ "icon-sm": "size-8"
819
+ }
820
+ },
821
+ defaultVariants: {
822
+ variant: "default",
823
+ size: "default"
824
+ }
825
+ }
826
+ );
827
+ function Button({
828
+ className,
829
+ variant = "default",
830
+ size = "default",
831
+ asChild = false,
832
+ ...props
833
+ }) {
834
+ const Comp = asChild ? Slot2 : "button";
835
+ return /* @__PURE__ */ jsx3(
836
+ Comp,
837
+ {
838
+ "data-slot": "button",
839
+ className: cn(buttonVariants({ variant, size, className })),
840
+ ...props
841
+ }
842
+ );
843
+ }
844
+
845
+ // src/host/components/BlueprintHostHero.tsx
846
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
847
+ function BlueprintHostHero({
848
+ title,
849
+ tagline,
850
+ description,
851
+ badges = [],
852
+ actions = [],
853
+ children,
854
+ className
855
+ }) {
856
+ return /* @__PURE__ */ jsxs(Card, { className: cn("rounded-3xl border-bp-elements-borderColor/70 bg-bp-elements-background-depth-2", className), children: [
857
+ /* @__PURE__ */ jsxs(CardHeader, { className: "space-y-4", children: [
858
+ badges.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-2", children: badges.map((badge) => /* @__PURE__ */ jsx4(Badge, { variant: "secondary", children: badge }, badge)) }) : null,
859
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
860
+ /* @__PURE__ */ jsx4(CardTitle, { className: "text-3xl", children: title }),
861
+ tagline ? /* @__PURE__ */ jsx4(CardDescription, { className: "text-base text-bp-elements-textPrimary/80", children: tagline }) : null
862
+ ] }),
863
+ description ? /* @__PURE__ */ jsx4("p", { className: "max-w-3xl text-sm leading-6 text-bp-elements-textSecondary", children: description }) : null
864
+ ] }),
865
+ (actions.length > 0 || children) && /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
866
+ actions.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-3", children: actions.map((action) => {
867
+ const button = /* @__PURE__ */ jsx4(
868
+ Button,
869
+ {
870
+ variant: action.variant ?? "default",
871
+ onClick: action.onClick,
872
+ disabled: action.disabled,
873
+ children: action.label
874
+ },
875
+ action.label
876
+ );
877
+ return action.href ? /* @__PURE__ */ jsx4("a", { href: action.href, children: button }, action.label) : button;
878
+ }) }) : null,
879
+ children
880
+ ] })
881
+ ] });
882
+ }
883
+
884
+ // src/host/components/BlueprintHostPanel.tsx
885
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
886
+ function BlueprintHostPanel({
887
+ title,
888
+ children,
889
+ className
890
+ }) {
891
+ return /* @__PURE__ */ jsxs2(Card, { className: cn("rounded-3xl border-bp-elements-borderColor/70 bg-bp-elements-background-depth-2", className), children: [
892
+ /* @__PURE__ */ jsx5(CardHeader, { children: /* @__PURE__ */ jsx5(CardTitle, { className: "text-xl", children: title }) }),
893
+ /* @__PURE__ */ jsx5(CardContent, { children })
894
+ ] });
895
+ }
896
+
598
897
  // src/hooks/useJobForm.ts
599
898
  import { useState, useCallback, useEffect, useMemo } from "react";
600
899
  function buildDefaults(job) {
@@ -754,7 +1053,13 @@ function mapJsonResourceCommitment(resource) {
754
1053
  count: BigInt(resource.count ?? 0)
755
1054
  };
756
1055
  }
757
- function useQuotes(operators, blueprintId, ttlBlocks, enabled, requireTee = false) {
1056
+ var ZERO_ADDRESS_LOWER = ZERO_ADDRESS.toLowerCase();
1057
+ function useQuotes(operators, blueprintId, ttlBlocks, enabled, requester, requireTee = false) {
1058
+ if (enabled && (!requester || requester.toLowerCase() === ZERO_ADDRESS_LOWER)) {
1059
+ throw new Error(
1060
+ "useQuotes: `requester` is required and must be a non-zero address when `enabled=true`. Pass `useAccount().address` from wagmi. tnt-core v0.13.0 contracts reject quotes whose requester is address(0) or != msg.sender."
1061
+ );
1062
+ }
758
1063
  const [quotes, setQuotes] = useState2([]);
759
1064
  const [isLoading, setIsLoading] = useState2(false);
760
1065
  const [isSolvingPow, setIsSolvingPow] = useState2(false);
@@ -788,7 +1093,8 @@ function useQuotes(operators, blueprintId, ttlBlocks, enabled, requireTee = fals
788
1093
  ttlBlocks,
789
1094
  proofOfWork: proof,
790
1095
  challengeTimestamp: timestamp,
791
- requireTee
1096
+ requireTee,
1097
+ requester
792
1098
  });
793
1099
  if (!response) throw new Error("No quote returned from operator");
794
1100
  if (!cancelled) results.push(response);
@@ -810,7 +1116,7 @@ function useQuotes(operators, blueprintId, ttlBlocks, enabled, requireTee = fals
810
1116
  return () => {
811
1117
  cancelled = true;
812
1118
  };
813
- }, [operators, blueprintId, ttlBlocks, enabled, fetchKey, requireTee]);
1119
+ }, [operators, blueprintId, ttlBlocks, enabled, fetchKey, requireTee, requester]);
814
1120
  const totalCost = quotes.reduce((sum, q) => sum + q.totalCost, 0n);
815
1121
  return { quotes, isLoading, isSolvingPow, errors, totalCost, refetch };
816
1122
  }
@@ -825,6 +1131,10 @@ async function fetchPriceFromOperator(rpcUrl2, params) {
825
1131
  proof_of_work: toHex(params.proofOfWork),
826
1132
  challenge_timestamp: String(params.challengeTimestamp),
827
1133
  require_tee: params.requireTee,
1134
+ // tnt-core v0.13.0: bind the quote to the future caller. Operators
1135
+ // sign this address into QuoteDetails; the contract enforces
1136
+ // `requester == msg.sender`.
1137
+ requester: params.requester,
828
1138
  resource_requirements: DEFAULT_RESOURCE_REQUIREMENTS
829
1139
  }),
830
1140
  signal: AbortSignal.timeout(1e4)
@@ -839,6 +1149,10 @@ async function fetchPriceFromOperator(rpcUrl2, params) {
839
1149
  teeAttested: Boolean(data.tee_attested),
840
1150
  teeProvider: data.tee_provider || void 0,
841
1151
  details: {
1152
+ // Prefer the operator-signed value; fall back to the hook's input.
1153
+ // If the operator returns a mismatched requester the contract will
1154
+ // revert at submission, so callers should still verify equality.
1155
+ requester: data.details?.requester ?? params.requester,
842
1156
  blueprintId: BigInt(data.details?.blueprint_id ?? params.blueprintId),
843
1157
  ttlBlocks: BigInt(data.details?.ttl_blocks ?? params.ttlBlocks),
844
1158
  totalCost: BigInt(data.details?.total_cost ?? "0"),
@@ -872,7 +1186,17 @@ function resolveOperatorRpc2(raw) {
872
1186
  return withProto;
873
1187
  }
874
1188
  }
875
- function useJobPrice(operatorRpcUrl, serviceId, jobIndex, blueprintId, enabled) {
1189
+ var ZERO_ADDRESS_LOWER2 = "0x0000000000000000000000000000000000000000";
1190
+ function assertRequester(requester, hookName, enabled) {
1191
+ if (!enabled) return;
1192
+ if (!requester || requester.toLowerCase() === ZERO_ADDRESS_LOWER2) {
1193
+ throw new Error(
1194
+ `${hookName}: \`requester\` is required and must be a non-zero address when \`enabled=true\`. Pass \`useAccount().address\` from wagmi. tnt-core v0.13.0 contracts reject quotes whose requester is address(0) or != msg.sender.`
1195
+ );
1196
+ }
1197
+ }
1198
+ function useJobPrice(operatorRpcUrl, serviceId, jobIndex, blueprintId, enabled, requester) {
1199
+ assertRequester(requester, "useJobPrice", enabled);
876
1200
  const [quote, setQuote] = useState3(null);
877
1201
  const [isLoading, setIsLoading] = useState3(false);
878
1202
  const [isSolvingPow, setIsSolvingPow] = useState3(false);
@@ -905,7 +1229,10 @@ function useJobPrice(operatorRpcUrl, serviceId, jobIndex, blueprintId, enabled)
905
1229
  service_id: String(serviceId),
906
1230
  job_index: jobIndex,
907
1231
  proof_of_work: toHex2(proof),
908
- challenge_timestamp: String(timestamp)
1232
+ challenge_timestamp: String(timestamp),
1233
+ // tnt-core v0.13.0: operators sign `requester` into JobQuoteDetails;
1234
+ // the contract enforces `requester == msg.sender` on submission.
1235
+ requester
909
1236
  }),
910
1237
  signal: AbortSignal.timeout(1e4)
911
1238
  });
@@ -915,6 +1242,7 @@ function useJobPrice(operatorRpcUrl, serviceId, jobIndex, blueprintId, enabled)
915
1242
  const data = await response.json();
916
1243
  if (cancelledRef.current) return;
917
1244
  setQuote({
1245
+ requester: data.requester ?? requester,
918
1246
  serviceId: BigInt(data.service_id ?? serviceId),
919
1247
  jobIndex: data.job_index ?? jobIndex,
920
1248
  price: BigInt(data.price ?? "0"),
@@ -938,11 +1266,12 @@ function useJobPrice(operatorRpcUrl, serviceId, jobIndex, blueprintId, enabled)
938
1266
  return () => {
939
1267
  cancelledRef.current = true;
940
1268
  };
941
- }, [operatorRpcUrl, serviceId, jobIndex, blueprintId, enabled, fetchKey]);
1269
+ }, [operatorRpcUrl, serviceId, jobIndex, blueprintId, enabled, fetchKey, requester]);
942
1270
  const formattedPrice = quote ? formatCost(quote.price) : "--";
943
1271
  return { quote, isLoading, isSolvingPow, error, formattedPrice, refetch };
944
1272
  }
945
- function useJobPrices(operatorRpcUrl, serviceId, blueprintId, jobIndexes, enabled) {
1273
+ function useJobPrices(operatorRpcUrl, serviceId, blueprintId, jobIndexes, enabled, requester) {
1274
+ assertRequester(requester, "useJobPrices", enabled);
946
1275
  const [prices, setPrices] = useState3([]);
947
1276
  const [isLoading, setIsLoading] = useState3(false);
948
1277
  const [error, setError] = useState3(null);
@@ -972,7 +1301,9 @@ function useJobPrices(operatorRpcUrl, serviceId, blueprintId, jobIndexes, enable
972
1301
  service_id: String(serviceId),
973
1302
  job_index: job.index,
974
1303
  proof_of_work: toHex2(proof),
975
- challenge_timestamp: String(timestamp)
1304
+ challenge_timestamp: String(timestamp),
1305
+ // tnt-core v0.13.0: see useJobPrice notes.
1306
+ requester
976
1307
  }),
977
1308
  signal: AbortSignal.timeout(1e4)
978
1309
  });
@@ -993,6 +1324,7 @@ function useJobPrices(operatorRpcUrl, serviceId, blueprintId, jobIndexes, enable
993
1324
  formattedPrice: formatCost(price),
994
1325
  mode: data.mode ?? "flat",
995
1326
  quote: {
1327
+ requester: data.requester ?? requester,
996
1328
  serviceId: BigInt(data.service_id ?? serviceId),
997
1329
  jobIndex: job.index,
998
1330
  price,
@@ -1030,7 +1362,7 @@ function useJobPrices(operatorRpcUrl, serviceId, blueprintId, jobIndexes, enable
1030
1362
  return () => {
1031
1363
  cancelled = true;
1032
1364
  };
1033
- }, [operatorRpcUrl, serviceId, blueprintId, jobIndexes, enabled, fetchKey]);
1365
+ }, [operatorRpcUrl, serviceId, blueprintId, jobIndexes, enabled, fetchKey, requester]);
1034
1366
  return { prices, isLoading, error, refetch };
1035
1367
  }
1036
1368
 
@@ -1130,6 +1462,7 @@ function useThemeValue() {
1130
1462
  }
1131
1463
 
1132
1464
  export {
1465
+ cn,
1133
1466
  resolveOperatorRpc,
1134
1467
  getEnvVar,
1135
1468
  mainnet,
@@ -1168,6 +1501,33 @@ export {
1168
1501
  publicClient,
1169
1502
  getAddresses,
1170
1503
  encodeJobArgs,
1504
+ buildCanonicalBlueprintSlug,
1505
+ resolveBlueprintAppView,
1506
+ toBlueprintAppEntry,
1507
+ getBlueprintExperienceTierLabel,
1508
+ getBlueprintSlugPolicyLabel,
1509
+ getBlueprintSurfaceLabel,
1510
+ getBlueprintPublisherVerificationLabel,
1511
+ getExternalAppTrustLabel,
1512
+ isVerifiedBlueprintPublisher,
1513
+ canPublisherClaimSlug,
1514
+ isTrustedExternalAppHost,
1515
+ getBlueprintPath,
1516
+ getBlueprintServicePath,
1517
+ sanitizeBlueprintSlugPart,
1518
+ deriveBlueprintRequestedSlug,
1519
+ Card,
1520
+ CardHeader,
1521
+ CardTitle,
1522
+ CardDescription,
1523
+ CardContent,
1524
+ CardFooter,
1525
+ badgeVariants,
1526
+ Badge,
1527
+ buttonVariants,
1528
+ Button,
1529
+ BlueprintHostHero,
1530
+ BlueprintHostPanel,
1171
1531
  useJobForm,
1172
1532
  solvePoW,
1173
1533
  formatCost,
@@ -1177,4 +1537,4 @@ export {
1177
1537
  useSubmitJob,
1178
1538
  useThemeValue
1179
1539
  };
1180
- //# sourceMappingURL=chunk-A6PJT5YQ.js.map
1540
+ //# sourceMappingURL=chunk-5PCH2RJF.js.map