@recursiv/sdk 0.7.51 → 0.7.52
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 +23 -2
- package/dist/client.js +3 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/resources/admin.d.ts +13 -0
- package/dist/resources/agents.d.ts +13 -2
- package/dist/resources/agents.js +7 -1
- package/dist/resources/app-subscriptions.d.ts +48 -2
- package/dist/resources/app-subscriptions.js +18 -2
- package/dist/resources/boosts.d.ts +23 -1
- package/dist/resources/boosts.js +6 -1
- package/dist/resources/communities.d.ts +1 -1
- package/dist/resources/communities.js +1 -1
- package/dist/resources/curator.d.ts +24 -6
- package/dist/resources/curator.js +2 -1
- package/dist/resources/discovery.d.ts +15 -1
- package/dist/resources/discovery.js +29 -1
- package/dist/resources/invite-codes-admin.d.ts +13 -0
- package/dist/resources/invite-codes.d.ts +10 -0
- package/dist/resources/invite-codes.js +6 -6
- package/dist/resources/posts.d.ts +24 -3
- package/dist/resources/posts.js +8 -3
- package/dist/resources/protocols.d.ts +10 -4
- package/dist/resources/protocols.js +10 -4
- package/dist/types.d.ts +16 -3
- package/llm.md +9 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -140,6 +140,7 @@ deployment, throw the normal SDK errors; do not present them as an empty queue.
|
|
|
140
140
|
```typescript
|
|
141
141
|
const r = new Recursiv({
|
|
142
142
|
apiKey: 'sk_live_...', // or set RECURSIV_API_KEY env var
|
|
143
|
+
inheritEnvironmentApiKey: true, // default; false guarantees keyless operation
|
|
143
144
|
baseUrl: 'https://api.recursiv.io/api/v1', // default
|
|
144
145
|
timeout: 30000, // request timeout in ms
|
|
145
146
|
maxRetries: 2, // auto-retry on 429/5xx
|
|
@@ -148,9 +149,12 @@ const r = new Recursiv({
|
|
|
148
149
|
// Zero-arg works when RECURSIV_API_KEY is set
|
|
149
150
|
const r = new Recursiv()
|
|
150
151
|
|
|
151
|
-
// Anonymous sandbox (no API key
|
|
152
|
+
// Anonymous sandbox (no API key required; inherits RECURSIV_API_KEY when set)
|
|
152
153
|
const r = new Recursiv({ anonymous: true })
|
|
153
154
|
|
|
155
|
+
// Guaranteed keyless public client, even inside a credentialed server process
|
|
156
|
+
const visitor = new Recursiv({ anonymous: true, inheritEnvironmentApiKey: false })
|
|
157
|
+
|
|
154
158
|
// Self-hosted
|
|
155
159
|
const r = new Recursiv({ baseUrl: 'https://my-instance.com/api/v1' })
|
|
156
160
|
```
|
|
@@ -613,7 +617,10 @@ state and obtain a fresh review before a new write.
|
|
|
613
617
|
Visitors can read a public conversation in an explicitly configured app without signing in:
|
|
614
618
|
|
|
615
619
|
```ts
|
|
616
|
-
const visitor = new Recursiv({
|
|
620
|
+
const visitor = new Recursiv({
|
|
621
|
+
anonymous: true,
|
|
622
|
+
inheritEnvironmentApiKey: false,
|
|
623
|
+
});
|
|
617
624
|
const { data: networks } = await visitor.protocols.sharedNetworks(projectId);
|
|
618
625
|
const { data: conversation } = await visitor.protocols.getSharedConversation(projectId, {
|
|
619
626
|
protocol: 'nostr', post: publicEventId,
|
|
@@ -643,6 +650,20 @@ module specs and native or protocol post objects. Preserve their existing source
|
|
|
643
650
|
authority and interactive controls. `retryFailed()` explicitly retries unavailable
|
|
644
651
|
sources; create a new session to refresh exhausted source windows.
|
|
645
652
|
|
|
653
|
+
`sdk.discovery.createPersonalFeed({ networks, category?, learning?, recommend? })`
|
|
654
|
+
builds the same kind of session for a member's For you tab: its Minds slot reads
|
|
655
|
+
`sdk.curator.forYou({ surface: 'discover', discovery: true, cursor })`, which ranks
|
|
656
|
+
fresher and past the member's follows, pages a stored ranking by `meta.next_cursor`,
|
|
657
|
+
and says whether the member is personalized yet (`meta.personalized`). With a
|
|
658
|
+
`category` (the member's first), the session also reads open-network posts from it;
|
|
659
|
+
without, it is Minds only. It never carries the radar.
|
|
660
|
+
|
|
661
|
+
`sdk.posts.categories()` answers the app's categories in the order this member
|
|
662
|
+
should see them. `meta.order` is `personal`, `popular` or `static`; `meta.reason`
|
|
663
|
+
says why an answer is not personal (`low_signal`, `deadline`, `requested`,
|
|
664
|
+
`experiment_off`), and `meta.order_id` changes only when the order does. The order
|
|
665
|
+
is private to the member; show it as returned rather than sorting it again.
|
|
666
|
+
|
|
646
667
|
Optional `DiscoveryLearning` consumes real visible impressions and explicit
|
|
647
668
|
engagement/hide outcomes. Persist its count-only snapshot per viewer; never share
|
|
648
669
|
a session or learning state across accounts. It starts conservatively and does
|
package/dist/client.js
CHANGED
|
@@ -11,7 +11,9 @@ export class HttpClient {
|
|
|
11
11
|
clientName;
|
|
12
12
|
constructor(config) {
|
|
13
13
|
const apiKey = config?.apiKey ||
|
|
14
|
-
(typeof process !== 'undefined'
|
|
14
|
+
(config?.inheritEnvironmentApiKey !== false && typeof process !== 'undefined'
|
|
15
|
+
? process.env?.RECURSIV_API_KEY
|
|
16
|
+
: undefined);
|
|
15
17
|
this.anonymous = config?.anonymous ?? false;
|
|
16
18
|
if (!apiKey && !this.anonymous && !config?.allowNoKey) {
|
|
17
19
|
throw new RecursivError(0, {
|
package/dist/index.d.ts
CHANGED
|
@@ -143,6 +143,7 @@ export type { RealtimeConnectOptions } from './realtime.js';
|
|
|
143
143
|
export { AuthResource } from './resources/auth.js';
|
|
144
144
|
export type { AuthSession, SignUpInput, SignInInput, UsernameSignInInput, AppContext, ResetPasswordInput, TwoFactorConfirmation, TwoFactorEnrollment, TwoFactorChallenge, CompleteTwoFactorSignInInput, OAuthClientInfo, SendOtpInput, VerifyOtpInput, CreateApiKeyInput, ApiKeyResult, } from './resources/auth.js';
|
|
145
145
|
export { PostsResource } from './resources/posts.js';
|
|
146
|
+
export type { CategoryOrder, CategoryOrderMeta, CategoryOrderReason } from './resources/posts.js';
|
|
146
147
|
export { UsersResource } from './resources/users.js';
|
|
147
148
|
export { CommunitiesResource } from './resources/communities.js';
|
|
148
149
|
export { ChatResource } from './resources/chat.js';
|
|
@@ -182,8 +183,8 @@ export { CuratorResource } from './resources/curator.js';
|
|
|
182
183
|
export { VideoResource } from './resources/video.js';
|
|
183
184
|
export type { VideoUploadCreds, CreateVideoUploadResult, VideoStatusInfo, VideoPlaybackToken, VideoEntitlement, } from './resources/video.js';
|
|
184
185
|
export { AppSubscriptionsResource } from './resources/app-subscriptions.js';
|
|
185
|
-
export type { AppSubscriptionCheckoutResult, AppSubscriptionPortalResult, AppSubscriptionStatusResult, AppSubscriptionTierChangePreview, AppSubscriptionTierChangeResult, } from './resources/app-subscriptions.js';
|
|
186
|
-
export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, CuratorTodayEdition, CuratorTodayExcerpt, CuratorTodayLeadImage, CuratorTodayRadar, CuratorTodayReaction, CuratorTodayResponse, CuratorTodaySource, CuratorTodayStory, } from './resources/curator.js';
|
|
186
|
+
export type { AppSubscriptionBoostCredit, AppSubscriptionCheckoutResult, AppSubscriptionPortalResult, AppSubscriptionStatusResult, AppSubscriptionTierChangePreview, AppSubscriptionTierChangeResult, } from './resources/app-subscriptions.js';
|
|
187
|
+
export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, CuratorTodayEdition, CuratorTodayExcerpt, CuratorTodayLeadImage, CuratorTodayRadar, CuratorTodayReaction, CuratorTodayResponse, CuratorTodaySource, CuratorTodayStory, ForYouMeta, ForYouParams, ForYouSurface, } from './resources/curator.js';
|
|
187
188
|
export { ReportsResource } from './resources/reports.js';
|
|
188
189
|
export type { ReportTargetType, ReportStatus, CreateReportInput, ReportRecord, ResolveReportInput, } from './resources/reports.js';
|
|
189
190
|
export { ModerationResource } from './resources/moderation.js';
|
|
@@ -201,7 +202,7 @@ export type { UploadMediaInput, UploadUrlInput, UploadUrlResult } from './resour
|
|
|
201
202
|
export { WalletResource } from './resources/wallet.js';
|
|
202
203
|
export type { LinkedTokenWallet, PayoutState, TokenBalance, TokenLedgerEntry } from './resources/wallet.js';
|
|
203
204
|
export { BoostsResource } from './resources/boosts.js';
|
|
204
|
-
export type { BoostBalance, BoostBalanceResponse, BoostCampaign, BoostCampaignResponse, BoostEventResult, BoostLedgerEntry, BoostLedgerKind, BoostRejectionReason, BoostReviewStatus, BoostStatus, BoostTopupCheckout, BoostTopupConfirmation, BoostTopupLimits, } from './resources/boosts.js';
|
|
205
|
+
export type { BoostBalance, BoostBalanceResponse, BoostCampaign, BoostCampaignResponse, BoostEventResult, BoostLedgerEntry, BoostLedgerKind, BoostPlanCredit, BoostRejectionReason, BoostReviewStatus, BoostStatus, BoostTopupCheckout, BoostTopupConfirmation, BoostTopupLimits, } from './resources/boosts.js';
|
|
205
206
|
export { ProtocolsResource } from './resources/protocols.js';
|
|
206
207
|
export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, } from './resources/protocols.js';
|
|
207
208
|
export type { SocialProfile, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialRadar, SocialRadarCluster, SocialRadarMember, SocialRadarParams, SocialRadarPosts, SocialRadarStory, SocialNetwork, SocialPost, SocialConversation, SocialTrend, SocialTrendProvenance, SocialTrends, SocialTrendSourceIndex, SocialTrendFeed, SocialPopularProfiles, SocialAuthorFeed, SocialFollowingFeed, SocialFollowingSource, SearchPublicProfilesParams, SearchPublicPostsParams, SocialPostSearch, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeSocialCallbackInput, NativeReplyPrepareInput, NativePostPrepareInput, NativeMediaInput, NativeMediaItem, NativePostBatch, NativePostHistory, NativeDelegatedPostHistoryItem, NativeNostrPostHistoryItem, NativeFollowSourceInput, NativeFollowPrepareInput, NativeFollowTarget, NativeFollowReceipt, NativeFollowState, NativeFollowAccount, NativeFollowStates, NativeFollowReview, NativeLikeSourceInput, NativeRepostSourceInput, NativeLikePrepareInput, NativeRepostPrepareInput, NativeLikeTarget, NativeRepostTarget, NativeLikeReceipt, NativeRepostReceipt, NativeLikeState, NativeRepostState, NativeLikeReview, NativeRepostReview, NativeReplyReceipt, NativeReplyBatch, NativeReplyHistory, NativeDelegatedReplyHistoryItem, NativeNostrReplyResult, NativeNostrReplyHistoryItem, NativeReplyRemovalReceipt, NativeReplyRemovalPreview, SocialSourceRef, SocialSourceControl, SocialSourceControlDisplay, SocialSourceControlChange, HostedPostPreview, ConnectHostedIdentityInput, CreateHostedIdentityInput, CreatedHostedProtocolIdentity, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolIngestionReplayResult, ProtocolIngestionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, ReplayProtocolIngestionJobInput, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
|
package/dist/index.js
CHANGED
|
@@ -181,7 +181,7 @@ export class Recursiv {
|
|
|
181
181
|
this.webhooks = new WebhooksResource(client);
|
|
182
182
|
this.jobs = new JobsResource(client);
|
|
183
183
|
this.curator = new CuratorResource(client);
|
|
184
|
-
this.discovery = new DiscoveryResource(this.posts, this.protocols);
|
|
184
|
+
this.discovery = new DiscoveryResource(this.posts, this.protocols, this.curator);
|
|
185
185
|
this.video = new VideoResource(client);
|
|
186
186
|
this.appSubscriptions = new AppSubscriptionsResource(client);
|
|
187
187
|
this.boosts = new BoostsResource(client);
|
|
@@ -158,12 +158,25 @@ export interface InviteSystemStats {
|
|
|
158
158
|
used: number;
|
|
159
159
|
expired: number;
|
|
160
160
|
revoked: number;
|
|
161
|
+
/** @deprecated Use `total`. */
|
|
162
|
+
total_codes: number;
|
|
163
|
+
/** @deprecated Use `active`. */
|
|
164
|
+
active_codes: number;
|
|
165
|
+
/** @deprecated Use `used`. */
|
|
166
|
+
used_codes: number;
|
|
167
|
+
/** @deprecated Use `expired`. */
|
|
168
|
+
expired_codes: number;
|
|
169
|
+
/** @deprecated Use `revoked`. */
|
|
170
|
+
revoked_codes: number;
|
|
161
171
|
};
|
|
162
172
|
waitlist: Record<string, number>;
|
|
163
173
|
current_wave: {
|
|
164
174
|
id: string;
|
|
165
175
|
wave: number;
|
|
166
176
|
name: string;
|
|
177
|
+
max_codes: number;
|
|
178
|
+
invites_per_user: number;
|
|
179
|
+
reward_tokens: number;
|
|
167
180
|
is_active: boolean;
|
|
168
181
|
} | null;
|
|
169
182
|
}
|
|
@@ -65,7 +65,7 @@ export interface SalesLead {
|
|
|
65
65
|
* Validation happens server-side too; this list lets apps validate or
|
|
66
66
|
* surface choices client-side without round-tripping the API.
|
|
67
67
|
*/
|
|
68
|
-
export declare const SUPPORTED_MODELS: readonly ["anthropic/claude-fable-5", "anthropic/claude-opus-4.6", "anthropic/claude-opus-4.8", "anthropic/claude-sonnet-4.6", "anthropic/claude-sonnet-5", "deepseek/deepseek-v3.2", "deepseek/deepseek-v4-pro", "google/gemini-3-flash-preview", "google/gemini-3.1-pro-preview", "google/gemini-3.5-flash", "minimax/minimax-m2.5", "minimax/minimax-m3", "moonshotai/kimi-k3", "openai/gpt-4o-mini", "openai/gpt-5.5", "openai/gpt-5.6-sol", "openai/gpt-5.6-terra", "x-ai/grok-4.3"];
|
|
68
|
+
export declare const SUPPORTED_MODELS: readonly ["anthropic/claude-fable-5", "anthropic/claude-fable-5.1", "anthropic/claude-opus-4.6", "anthropic/claude-opus-4.8", "anthropic/claude-opus-5", "anthropic/claude-opus-5.5", "anthropic/claude-sonnet-4.6", "anthropic/claude-sonnet-5", "deepseek/deepseek-v3.2", "deepseek/deepseek-v4-pro", "google/gemini-3-flash-preview", "google/gemini-3.1-pro-preview", "google/gemini-3.5-flash", "google/gemini-3.8-flash", "minimax/minimax-m2.5", "minimax/minimax-m3", "moonshotai/kimi-k3", "openai/gpt-4o-mini", "openai/gpt-5.5", "openai/gpt-5.6-sol", "openai/gpt-5.6-terra", "openai/gpt-6-astra", "openai/gpt-6-sol", "x-ai/grok-4.3"];
|
|
69
69
|
export type SupportedModel = (typeof SUPPORTED_MODELS)[number];
|
|
70
70
|
export interface EnsurePersonalAgentOverrides {
|
|
71
71
|
/** Override the agent's display name. Defaults to 'Agent'. */
|
|
@@ -74,6 +74,17 @@ export interface EnsurePersonalAgentOverrides {
|
|
|
74
74
|
model?: string;
|
|
75
75
|
/** Override the system prompt. Apps like Minds can set their own voice. */
|
|
76
76
|
system_prompt?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Whether the agent may post publicly (reply to @mentions). The platform
|
|
79
|
+
* default for a new personal agent is 'chat_post'; pass 'chat_only' to keep
|
|
80
|
+
* it to private chat.
|
|
81
|
+
*/
|
|
82
|
+
social_mode?: 'chat_only' | 'chat_post';
|
|
83
|
+
/**
|
|
84
|
+
* How the agent uses tools. The platform default for a new personal agent is
|
|
85
|
+
* 'autonomous'; 'permission' asks the owner before a tool acts.
|
|
86
|
+
*/
|
|
87
|
+
tool_mode?: 'chat_only' | 'permission' | 'autonomous';
|
|
77
88
|
}
|
|
78
89
|
export interface EnsurePersonalAgentInput {
|
|
79
90
|
/**
|
|
@@ -105,7 +116,7 @@ export declare class AgentsResource {
|
|
|
105
116
|
}): Promise<PaginatedResponse<Agent>>;
|
|
106
117
|
/** Get an agent by ID */
|
|
107
118
|
get(id: string): Promise<SingleResponse<AgentDetail>>;
|
|
108
|
-
/** Create a new AI agent */
|
|
119
|
+
/** Create a new AI agent. Omit username for a server-generated collision-resistant handle. */
|
|
109
120
|
create(input: CreateAgentInput): Promise<SingleResponse<CreatedAgent>>;
|
|
110
121
|
/** Update an agent's configuration */
|
|
111
122
|
update(id: string, input: UpdateAgentInput): Promise<SingleResponse<UpdatedAgent>>;
|
package/dist/resources/agents.js
CHANGED
|
@@ -12,8 +12,11 @@ import { parseSSE } from '../lib/sse.js';
|
|
|
12
12
|
export const SUPPORTED_MODELS = [
|
|
13
13
|
// <<< GENERATED: openrouter-sync — do not edit by hand (npm run models:sync)
|
|
14
14
|
'anthropic/claude-fable-5',
|
|
15
|
+
'anthropic/claude-fable-5.1',
|
|
15
16
|
'anthropic/claude-opus-4.6',
|
|
16
17
|
'anthropic/claude-opus-4.8',
|
|
18
|
+
'anthropic/claude-opus-5',
|
|
19
|
+
'anthropic/claude-opus-5.5',
|
|
17
20
|
'anthropic/claude-sonnet-4.6',
|
|
18
21
|
'anthropic/claude-sonnet-5',
|
|
19
22
|
'deepseek/deepseek-v3.2',
|
|
@@ -21,6 +24,7 @@ export const SUPPORTED_MODELS = [
|
|
|
21
24
|
'google/gemini-3-flash-preview',
|
|
22
25
|
'google/gemini-3.1-pro-preview',
|
|
23
26
|
'google/gemini-3.5-flash',
|
|
27
|
+
'google/gemini-3.8-flash',
|
|
24
28
|
'minimax/minimax-m2.5',
|
|
25
29
|
'minimax/minimax-m3',
|
|
26
30
|
'moonshotai/kimi-k3',
|
|
@@ -28,6 +32,8 @@ export const SUPPORTED_MODELS = [
|
|
|
28
32
|
'openai/gpt-5.5',
|
|
29
33
|
'openai/gpt-5.6-sol',
|
|
30
34
|
'openai/gpt-5.6-terra',
|
|
35
|
+
'openai/gpt-6-astra',
|
|
36
|
+
'openai/gpt-6-sol',
|
|
31
37
|
'x-ai/grok-4.3',
|
|
32
38
|
// >>> GENERATED
|
|
33
39
|
];
|
|
@@ -51,7 +57,7 @@ export class AgentsResource {
|
|
|
51
57
|
get(id) {
|
|
52
58
|
return this.client.get(`/agents/${id}`);
|
|
53
59
|
}
|
|
54
|
-
/** Create a new AI agent */
|
|
60
|
+
/** Create a new AI agent. Omit username for a server-generated collision-resistant handle. */
|
|
55
61
|
create(input) {
|
|
56
62
|
return this.client.post('/agents', input);
|
|
57
63
|
}
|
|
@@ -6,6 +6,30 @@ export interface AppSubscriptionCheckoutResult {
|
|
|
6
6
|
export interface AppSubscriptionPortalResult {
|
|
7
7
|
/** Stripe Billing Portal URL for the current app user. */
|
|
8
8
|
url: string;
|
|
9
|
+
/**
|
|
10
|
+
* The page the portal opens on: `cancel` for the plan's cancel confirmation, null for the
|
|
11
|
+
* portal home. A plan already set to end opens the home, where the member can renew it.
|
|
12
|
+
* Absent from older servers.
|
|
13
|
+
*/
|
|
14
|
+
flow?: 'cancel' | null;
|
|
15
|
+
}
|
|
16
|
+
/** The boost credit a paid plan on this app adds to the member's boost balance. */
|
|
17
|
+
export interface AppSubscriptionBoostCredit {
|
|
18
|
+
/** Micro-USD (1 USD = 1,000,000) each paid billing period of a plan adds, by tier key (e.g. `plus`, `pro`). */
|
|
19
|
+
perPeriodMicros: Record<string, number>;
|
|
20
|
+
/** What the member's current plan adds each paid period; 0 without a paid plan here. */
|
|
21
|
+
currentMicros: number;
|
|
22
|
+
/**
|
|
23
|
+
* When the next paid period starts and adds `currentMicros`. Null without a plan that adds
|
|
24
|
+
* credit, or while a payment is overdue. A plan set to end at the period's end still shows it.
|
|
25
|
+
*/
|
|
26
|
+
nextGrantAt: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Plan credit the member owes: taken back after their plan dropped or ended inside a period, but
|
|
29
|
+
* already spent. The next plan credit repays it before adding anything. 0 when nothing is owed
|
|
30
|
+
* (absent from older servers).
|
|
31
|
+
*/
|
|
32
|
+
owedMicros?: number;
|
|
9
33
|
}
|
|
10
34
|
export interface AppSubscriptionStatusResult {
|
|
11
35
|
tier: string;
|
|
@@ -15,6 +39,11 @@ export interface AppSubscriptionStatusResult {
|
|
|
15
39
|
currentPeriodEnd: string | null;
|
|
16
40
|
/** Where the entitlement comes from: a Stripe subscription here, or the legacy import. */
|
|
17
41
|
source?: 'stripe' | 'legacy';
|
|
42
|
+
/**
|
|
43
|
+
* The boost credit each paid period of a plan adds (once per period, from the payment that
|
|
44
|
+
* starts it). Null when this app's plans include none; absent from older servers.
|
|
45
|
+
*/
|
|
46
|
+
boostCredit?: AppSubscriptionBoostCredit | null;
|
|
18
47
|
}
|
|
19
48
|
export interface AppSubscriptionTierChangePreview {
|
|
20
49
|
/** The plan the preview prices. */
|
|
@@ -47,6 +76,13 @@ export declare class AppSubscriptionsResource {
|
|
|
47
76
|
* Start a Stripe Checkout for the current user to subscribe to a plan tier ('plus' | 'pro').
|
|
48
77
|
* Answers 409 `subscription_exists` when the member already has a subscription:
|
|
49
78
|
* use `changeTier` for them instead, so they are never billed twice.
|
|
79
|
+
*
|
|
80
|
+
* Only one checkout can be paid at a time. Repeating the same request (same tier and
|
|
81
|
+
* `return_url`) returns the checkout already open; any other request expires the
|
|
82
|
+
* member's earlier open checkouts for this app. Answers 503 `checkout_busy` when
|
|
83
|
+
* Stripe is too slow to finish safely: retry. `return_url` keeps its own query and fragment;
|
|
84
|
+
* `sub=success&tier=<tier>` or `sub=cancelled` is added to its query (to the
|
|
85
|
+
* fragment's query for a hash route such as `#/billing`).
|
|
50
86
|
*/
|
|
51
87
|
checkout(input: {
|
|
52
88
|
tier: string;
|
|
@@ -79,13 +115,23 @@ export declare class AppSubscriptionsResource {
|
|
|
79
115
|
}): Promise<{
|
|
80
116
|
data: AppSubscriptionTierChangeResult;
|
|
81
117
|
}>;
|
|
82
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Open the current app user's Stripe Billing Portal for this project.
|
|
120
|
+
*
|
|
121
|
+
* `flow: 'cancel'` opens it straight on the cancel confirmation for the member's plan, and
|
|
122
|
+
* returns to `return_url` once they confirm. Answers 409 `no_subscription` when there is no
|
|
123
|
+
* plan to cancel, and 409 `legacy_membership` for a carried-over member with nothing to manage.
|
|
124
|
+
*/
|
|
83
125
|
createPortalSession(input: {
|
|
84
126
|
return_url: string;
|
|
127
|
+
flow?: 'cancel';
|
|
85
128
|
}): Promise<{
|
|
86
129
|
data: AppSubscriptionPortalResult;
|
|
87
130
|
}>;
|
|
88
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Read the current user's app-subscription status for this project-scoped key, with the
|
|
133
|
+
* boost credit the app's plans include (`boostCredit`).
|
|
134
|
+
*/
|
|
89
135
|
status(): Promise<{
|
|
90
136
|
data: AppSubscriptionStatusResult;
|
|
91
137
|
}>;
|
|
@@ -11,6 +11,13 @@ export class AppSubscriptionsResource {
|
|
|
11
11
|
* Start a Stripe Checkout for the current user to subscribe to a plan tier ('plus' | 'pro').
|
|
12
12
|
* Answers 409 `subscription_exists` when the member already has a subscription:
|
|
13
13
|
* use `changeTier` for them instead, so they are never billed twice.
|
|
14
|
+
*
|
|
15
|
+
* Only one checkout can be paid at a time. Repeating the same request (same tier and
|
|
16
|
+
* `return_url`) returns the checkout already open; any other request expires the
|
|
17
|
+
* member's earlier open checkouts for this app. Answers 503 `checkout_busy` when
|
|
18
|
+
* Stripe is too slow to finish safely: retry. `return_url` keeps its own query and fragment;
|
|
19
|
+
* `sub=success&tier=<tier>` or `sub=cancelled` is added to its query (to the
|
|
20
|
+
* fragment's query for a hash route such as `#/billing`).
|
|
14
21
|
*/
|
|
15
22
|
checkout(input) {
|
|
16
23
|
return this.client.post('/app-subscriptions/checkout', input);
|
|
@@ -35,11 +42,20 @@ export class AppSubscriptionsResource {
|
|
|
35
42
|
changeTier(input) {
|
|
36
43
|
return this.client.post('/app-subscriptions/change-tier', input, { retry: false });
|
|
37
44
|
}
|
|
38
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Open the current app user's Stripe Billing Portal for this project.
|
|
47
|
+
*
|
|
48
|
+
* `flow: 'cancel'` opens it straight on the cancel confirmation for the member's plan, and
|
|
49
|
+
* returns to `return_url` once they confirm. Answers 409 `no_subscription` when there is no
|
|
50
|
+
* plan to cancel, and 409 `legacy_membership` for a carried-over member with nothing to manage.
|
|
51
|
+
*/
|
|
39
52
|
createPortalSession(input) {
|
|
40
53
|
return this.client.post('/app-subscriptions/portal', input);
|
|
41
54
|
}
|
|
42
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Read the current user's app-subscription status for this project-scoped key, with the
|
|
57
|
+
* boost credit the app's plans include (`boostCredit`).
|
|
58
|
+
*/
|
|
43
59
|
status() {
|
|
44
60
|
return this.client.get('/app-subscriptions/status');
|
|
45
61
|
}
|
|
@@ -44,10 +44,27 @@ export interface BoostBalance {
|
|
|
44
44
|
/** The 50 most recent balance movements, newest first. */
|
|
45
45
|
ledger: BoostLedgerEntry[];
|
|
46
46
|
}
|
|
47
|
+
/** The boost credit the key's app includes with its paid plans. */
|
|
48
|
+
export interface BoostPlanCredit {
|
|
49
|
+
/** Micro-USD each paid billing period of a plan adds, by tier key (e.g. `plus`, `pro`). */
|
|
50
|
+
per_period_micros: Record<string, number>;
|
|
51
|
+
/** What your current plan on this app adds each paid period; 0 without one. */
|
|
52
|
+
current_micros: number;
|
|
53
|
+
/** When your next paid period starts and adds `current_micros`; null when none is due. */
|
|
54
|
+
next_grant_at: string | null;
|
|
55
|
+
/**
|
|
56
|
+
* Plan credit you owe: taken back after your plan dropped or ended inside a period, but already
|
|
57
|
+
* spent. Your next plan credit repays it before adding anything. 0 when nothing is owed
|
|
58
|
+
* (absent from older servers).
|
|
59
|
+
*/
|
|
60
|
+
owed_micros?: number;
|
|
61
|
+
}
|
|
47
62
|
export interface BoostBalanceResponse extends SingleResponse<BoostBalance> {
|
|
63
|
+
/** `plan_credit` is null when the app's plans include no boost credit (absent from older servers). */
|
|
48
64
|
meta: {
|
|
49
65
|
min_cpm_micros: number;
|
|
50
66
|
topup?: BoostTopupLimits;
|
|
67
|
+
plan_credit?: BoostPlanCredit | null;
|
|
51
68
|
};
|
|
52
69
|
}
|
|
53
70
|
export interface BoostCampaignResponse extends SingleResponse<BoostCampaign> {
|
|
@@ -98,7 +115,10 @@ export declare class BoostsResource {
|
|
|
98
115
|
limit?: number;
|
|
99
116
|
offset?: number;
|
|
100
117
|
}): Promise<PaginatedResponse<BoostCampaign>>;
|
|
101
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Your balance, recent ledger, the CPM floor, the top-up limits, and the boost credit your
|
|
120
|
+
* plan on this app adds each paid period (`meta.plan_credit`, a `grant` ledger row per period).
|
|
121
|
+
*/
|
|
102
122
|
balance(): Promise<BoostBalanceResponse>;
|
|
103
123
|
/** Pause one of your active campaigns. */
|
|
104
124
|
pause(campaignId: string): Promise<BoostCampaignResponse>;
|
|
@@ -112,6 +132,8 @@ export declare class BoostsResource {
|
|
|
112
132
|
* Buy boost credit: opens a one-time Stripe Checkout for `amount_cents` (USD) on the app's
|
|
113
133
|
* own Stripe account. The balance is credited once the payment succeeds. Limits are in
|
|
114
134
|
* `balance().meta.topup`. Requires a project-scoped key with `billing:write`.
|
|
135
|
+
* `return_url` keeps its own query and fragment; `topup=success&session_id=<id>` or
|
|
136
|
+
* `topup=cancelled` is added to its query (to the fragment's query for a hash route).
|
|
115
137
|
*/
|
|
116
138
|
topup(input: {
|
|
117
139
|
amount_cents: number;
|
package/dist/resources/boosts.js
CHANGED
|
@@ -20,7 +20,10 @@ export class BoostsResource {
|
|
|
20
20
|
mine(params) {
|
|
21
21
|
return this.client.get('/boosts/mine', params);
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Your balance, recent ledger, the CPM floor, the top-up limits, and the boost credit your
|
|
25
|
+
* plan on this app adds each paid period (`meta.plan_credit`, a `grant` ledger row per period).
|
|
26
|
+
*/
|
|
24
27
|
balance() {
|
|
25
28
|
return this.client.get('/boosts/balance');
|
|
26
29
|
}
|
|
@@ -44,6 +47,8 @@ export class BoostsResource {
|
|
|
44
47
|
* Buy boost credit: opens a one-time Stripe Checkout for `amount_cents` (USD) on the app's
|
|
45
48
|
* own Stripe account. The balance is credited once the payment succeeds. Limits are in
|
|
46
49
|
* `balance().meta.topup`. Requires a project-scoped key with `billing:write`.
|
|
50
|
+
* `return_url` keeps its own query and fragment; `topup=success&session_id=<id>` or
|
|
51
|
+
* `topup=cancelled` is added to its query (to the fragment's query for a hash route).
|
|
47
52
|
*/
|
|
48
53
|
topup(input) {
|
|
49
54
|
return this.client.post('/boosts/topup', input, { retry: false });
|
|
@@ -25,7 +25,7 @@ export declare class CommunitiesResource {
|
|
|
25
25
|
}): Promise<PaginatedResponse<RelatedCommunity>>;
|
|
26
26
|
/** List members of a community. `q` searches by name or username. */
|
|
27
27
|
members(id: string, params?: CommunityPeopleParams): Promise<PaginatedResponse<CommunityMember>>;
|
|
28
|
-
/** Create a new community */
|
|
28
|
+
/** Create a new community. Omit slug for a server-generated collision-resistant slug. */
|
|
29
29
|
create(input: CreateCommunityInput): Promise<SingleResponse<CreatedCommunity>>;
|
|
30
30
|
/** Join a community */
|
|
31
31
|
join(id: string): Promise<SingleResponse<JoinResult>>;
|
|
@@ -33,7 +33,7 @@ export class CommunitiesResource {
|
|
|
33
33
|
members(id, params) {
|
|
34
34
|
return this.client.get(`/communities/${id}/members`, params);
|
|
35
35
|
}
|
|
36
|
-
/** Create a new community */
|
|
36
|
+
/** Create a new community. Omit slug for a server-generated collision-resistant slug. */
|
|
37
37
|
create(input) {
|
|
38
38
|
return this.client.post('/communities', input);
|
|
39
39
|
}
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
|
-
import type { PaginatedResponse, Post, SingleResponse } from '../types.js';
|
|
2
|
+
import type { DiscoveryLayout, PaginatedResponse, PaginationMeta, Post, SingleResponse } from '../types.js';
|
|
3
|
+
/** Which surface is asking: Home's For You (default) or Discover's For you. */
|
|
4
|
+
export type ForYouSurface = 'home' | 'discover';
|
|
5
|
+
export type ForYouParams = {
|
|
6
|
+
limit?: number;
|
|
7
|
+
offset?: number;
|
|
8
|
+
/** The previous page's `meta.next_cursor`: pages a stored ranking, so posts read meanwhile never shift the next page. */
|
|
9
|
+
cursor?: string;
|
|
10
|
+
/** Adds `meta.discovery` (layouts) and the public safety floor; limit <= 25. */
|
|
11
|
+
discovery?: boolean;
|
|
12
|
+
/** `discover` ranks for Discover: fresher, beyond the member's follows, with their category order. */
|
|
13
|
+
surface?: ForYouSurface;
|
|
14
|
+
};
|
|
15
|
+
export interface ForYouMeta extends PaginationMeta {
|
|
16
|
+
discovery?: DiscoveryLayout;
|
|
17
|
+
next_cursor?: string;
|
|
18
|
+
/** Whether the ranker can personalize for this member yet (3 follows or 3 upvoted authors). */
|
|
19
|
+
personalized?: boolean;
|
|
20
|
+
}
|
|
3
21
|
/** RSS / Atom source. Default — `type` may be omitted for back-compat. */
|
|
4
22
|
export interface CuratorRssSource {
|
|
5
23
|
type?: 'rss';
|
|
@@ -250,10 +268,10 @@ export declare class CuratorResource {
|
|
|
250
268
|
* existing posts for the signed-in user (engagement + recency + social +
|
|
251
269
|
* affinity, no model calls). Returns posts in the same shape as
|
|
252
270
|
* `sdk.posts.list()`, so any feed UI renders it unchanged. Requires a
|
|
253
|
-
* project-scoped key (the feed is per-app).
|
|
271
|
+
* project-scoped key (the feed is per-app). Servers that predate `cursor`,
|
|
272
|
+
* `discovery` and `surface` ignore them.
|
|
254
273
|
*/
|
|
255
|
-
forYou(params?: {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}): Promise<PaginatedResponse<Post>>;
|
|
274
|
+
forYou(params?: ForYouParams): Promise<PaginatedResponse<Post> & {
|
|
275
|
+
meta: ForYouMeta;
|
|
276
|
+
}>;
|
|
259
277
|
}
|
|
@@ -47,7 +47,8 @@ export class CuratorResource {
|
|
|
47
47
|
* existing posts for the signed-in user (engagement + recency + social +
|
|
48
48
|
* affinity, no model calls). Returns posts in the same shape as
|
|
49
49
|
* `sdk.posts.list()`, so any feed UI renders it unchanged. Requires a
|
|
50
|
-
* project-scoped key (the feed is per-app).
|
|
50
|
+
* project-scoped key (the feed is per-app). Servers that predate `cursor`,
|
|
51
|
+
* `discovery` and `surface` ignore them.
|
|
51
52
|
*/
|
|
52
53
|
forYou(params = {}) {
|
|
53
54
|
return this.client.get('/curator/foryou', params);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DiscoveryLayout, Post } from '../types.js';
|
|
2
|
+
import type { CuratorResource } from './curator.js';
|
|
2
3
|
import type { DiscoveryLearning } from './discovery-learning.js';
|
|
3
4
|
import type { PostsResource } from './posts.js';
|
|
4
5
|
import type { ProtocolsResource, SocialNetwork, SocialPost, SocialRadar, SocialRadarPosts } from './protocols.js';
|
|
@@ -201,7 +202,20 @@ export declare class DiscoveryFeedSession {
|
|
|
201
202
|
export declare class DiscoveryResource {
|
|
202
203
|
private posts;
|
|
203
204
|
private protocols;
|
|
204
|
-
|
|
205
|
+
private curator?;
|
|
206
|
+
constructor(posts: PostsResource, protocols: ProtocolsResource, curator?: Pick<CuratorResource, "forYou"> | undefined);
|
|
207
|
+
/**
|
|
208
|
+
* A For you Discover feed: the member's For You ranking (asked for Discover's surface, with layouts and a
|
|
209
|
+
* snapshot cursor) in a session's Minds slot. With a category, the session also reads open-network posts
|
|
210
|
+
* from that category (the member's top one); without, it is Minds only. Never the radar. The page-0
|
|
211
|
+
* arrangement and when to use which are the app's choice.
|
|
212
|
+
*/
|
|
213
|
+
createPersonalFeed(options: {
|
|
214
|
+
networks: SocialNetwork[];
|
|
215
|
+
learning?: DiscoveryLearning;
|
|
216
|
+
recommend?: DiscoveryRecommendationSources;
|
|
217
|
+
category?: string;
|
|
218
|
+
}): DiscoveryFeedSession;
|
|
205
219
|
createFeed(options: {
|
|
206
220
|
networks: SocialNetwork[];
|
|
207
221
|
category?: string;
|
|
@@ -160,6 +160,9 @@ export class DiscoveryFeedSession {
|
|
|
160
160
|
return this.pending;
|
|
161
161
|
}
|
|
162
162
|
async read(stream) {
|
|
163
|
+
// `discovery` asks only for the layout judgment (and, for Minds posts, the public-only floor). The
|
|
164
|
+
// server's safety floor does not depend on it, so a reader that drops it to skip layout work (a clip
|
|
165
|
+
// pool) is screened exactly like this session.
|
|
163
166
|
const params = { limit: 25, ...(this.category ? { category: this.category } : {}), discovery: true };
|
|
164
167
|
if (stream.protocol === 'minds') {
|
|
165
168
|
// Discover leads with what is moving now: the REST hot order (engagement
|
|
@@ -462,9 +465,34 @@ export class DiscoveryFeedSession {
|
|
|
462
465
|
export class DiscoveryResource {
|
|
463
466
|
posts;
|
|
464
467
|
protocols;
|
|
465
|
-
|
|
468
|
+
curator;
|
|
469
|
+
constructor(posts, protocols, curator) {
|
|
466
470
|
this.posts = posts;
|
|
467
471
|
this.protocols = protocols;
|
|
472
|
+
this.curator = curator;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* A For you Discover feed: the member's For You ranking (asked for Discover's surface, with layouts and a
|
|
476
|
+
* snapshot cursor) in a session's Minds slot. With a category, the session also reads open-network posts
|
|
477
|
+
* from that category (the member's top one); without, it is Minds only. Never the radar. The page-0
|
|
478
|
+
* arrangement and when to use which are the app's choice.
|
|
479
|
+
*/
|
|
480
|
+
createPersonalFeed(options) {
|
|
481
|
+
const curator = this.curator;
|
|
482
|
+
if (!curator)
|
|
483
|
+
throw new Error('createPersonalFeed needs the curator resource');
|
|
484
|
+
const list = {
|
|
485
|
+
// The session's `sort` and `category` are Discover's hot-order knobs; the ranker ignores them.
|
|
486
|
+
list: async (params) => {
|
|
487
|
+
const response = await curator.forYou({ limit: params?.limit, offset: params?.offset, cursor: params?.cursor, discovery: true, surface: 'discover' });
|
|
488
|
+
// A server that predates the cursor says has_more: false on a partial ranked page although its
|
|
489
|
+
// continuation serves the next offset. A cursor-aware server always says `personalized`, and its
|
|
490
|
+
// has_more is the end of the feed.
|
|
491
|
+
const legacy = typeof response.meta.personalized !== 'boolean';
|
|
492
|
+
return { ...response, meta: { ...response.meta, has_more: response.meta.has_more || (legacy && response.data.length > 0) } };
|
|
493
|
+
},
|
|
494
|
+
};
|
|
495
|
+
return new DiscoveryFeedSession(list, this.protocols, options.category ? options.networks : [], options.category, options.category ? 'all' : 'minds', options.learning, options.recommend);
|
|
468
496
|
}
|
|
469
497
|
createFeed(options) {
|
|
470
498
|
// The radar rides along by default; an older server answers 404 and the session simply shows no story module.
|
|
@@ -18,12 +18,25 @@ export interface InviteCodeStats {
|
|
|
18
18
|
used: number;
|
|
19
19
|
expired: number;
|
|
20
20
|
revoked: number;
|
|
21
|
+
/** @deprecated Use `total`. */
|
|
22
|
+
total_codes: number;
|
|
23
|
+
/** @deprecated Use `active`. */
|
|
24
|
+
active_codes: number;
|
|
25
|
+
/** @deprecated Use `used`. */
|
|
26
|
+
used_codes: number;
|
|
27
|
+
/** @deprecated Use `expired`. */
|
|
28
|
+
expired_codes: number;
|
|
29
|
+
/** @deprecated Use `revoked`. */
|
|
30
|
+
revoked_codes: number;
|
|
21
31
|
};
|
|
22
32
|
waitlist: Record<string, number>;
|
|
23
33
|
current_wave: {
|
|
24
34
|
id: string;
|
|
25
35
|
wave: number;
|
|
26
36
|
name: string;
|
|
37
|
+
max_codes: number;
|
|
38
|
+
invites_per_user: number;
|
|
39
|
+
reward_tokens: number;
|
|
27
40
|
is_active: boolean;
|
|
28
41
|
} | null;
|
|
29
42
|
}
|
|
@@ -90,10 +90,20 @@ export interface BulkGenerateResult {
|
|
|
90
90
|
}
|
|
91
91
|
export interface InviteStats {
|
|
92
92
|
codes: {
|
|
93
|
+
total: number;
|
|
94
|
+
active: number;
|
|
95
|
+
used: number;
|
|
96
|
+
expired: number;
|
|
97
|
+
revoked: number;
|
|
98
|
+
/** @deprecated Use `total`. Retained for legacy admin clients. */
|
|
93
99
|
total_codes: number;
|
|
100
|
+
/** @deprecated Use `active`. Retained for legacy admin clients. */
|
|
94
101
|
active_codes: number;
|
|
102
|
+
/** @deprecated Use `used`. Retained for legacy admin clients. */
|
|
95
103
|
used_codes: number;
|
|
104
|
+
/** @deprecated Use `expired`. Retained for legacy admin clients. */
|
|
96
105
|
expired_codes: number;
|
|
106
|
+
/** @deprecated Use `revoked`. Retained for legacy admin clients. */
|
|
97
107
|
revoked_codes: number;
|
|
98
108
|
};
|
|
99
109
|
waitlist: Record<string, number>;
|
|
@@ -43,26 +43,26 @@ export class InviteCodesResource {
|
|
|
43
43
|
// ── Admin Methods ──
|
|
44
44
|
/** Admin: Initialize wave configs for the network */
|
|
45
45
|
initializeWaves() {
|
|
46
|
-
return this.client.post('/invite-codes
|
|
46
|
+
return this.client.post('/invite-codes-admin/waves/initialize');
|
|
47
47
|
}
|
|
48
48
|
/** Admin: Get all wave configs */
|
|
49
49
|
getWaves() {
|
|
50
|
-
return this.client.get('/invite-codes
|
|
50
|
+
return this.client.get('/invite-codes-admin/waves');
|
|
51
51
|
}
|
|
52
52
|
/** Admin: Activate a specific wave */
|
|
53
53
|
activateWave(wave) {
|
|
54
|
-
return this.client.post('/invite-codes
|
|
54
|
+
return this.client.post('/invite-codes-admin/waves/activate', { wave });
|
|
55
55
|
}
|
|
56
56
|
/** Admin: Bulk generate invite codes */
|
|
57
57
|
bulkGenerate(input) {
|
|
58
|
-
return this.client.post('/invite-codes
|
|
58
|
+
return this.client.post('/invite-codes-admin/bulk-generate', input);
|
|
59
59
|
}
|
|
60
60
|
/** Admin: Get invite system stats */
|
|
61
61
|
getStats() {
|
|
62
|
-
return this.client.get('/invite-codes
|
|
62
|
+
return this.client.get('/invite-codes-admin/stats');
|
|
63
63
|
}
|
|
64
64
|
/** Admin: Get waitlist entries */
|
|
65
65
|
getWaitlist(params) {
|
|
66
|
-
return this.client.get('/invite-codes
|
|
66
|
+
return this.client.get('/invite-codes-admin/waitlist', params);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
@@ -1,15 +1,36 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
2
|
import type { PaginatedResponse, SingleResponse, DeleteResponse, Post, PostDetail, CreatePostInput, CreatedPost, UpdatePostInput, ListPostsParams, ListPostRepliesParams, PostRepliesPage, SearchPostsParams, ReactionType } from '../types.js';
|
|
3
|
+
/** How GET /posts/categories ordered its answer: the member's own order, the app's popularity order, or the fixed order. */
|
|
4
|
+
export type CategoryOrder = 'personal' | 'popular' | 'static';
|
|
5
|
+
/** Why an answer is not `personal`. */
|
|
6
|
+
export type CategoryOrderReason = 'low_signal' | 'deadline' | 'requested' | 'experiment_off';
|
|
7
|
+
export interface CategoryOrderMeta {
|
|
8
|
+
order: CategoryOrder;
|
|
9
|
+
/** 16 hex; equal ids mean the same order. */
|
|
10
|
+
order_id: string;
|
|
11
|
+
as_of: string;
|
|
12
|
+
reason?: CategoryOrderReason;
|
|
13
|
+
/** Whether For You can personalize for this member yet; absent when the member's order was not read. */
|
|
14
|
+
personalized?: boolean;
|
|
15
|
+
}
|
|
3
16
|
export declare class PostsResource {
|
|
4
17
|
private client;
|
|
5
18
|
constructor(client: HttpClient);
|
|
6
19
|
/** List posts with optional filters */
|
|
7
20
|
list(params?: ListPostsParams): Promise<PaginatedResponse<Post>>;
|
|
8
|
-
/**
|
|
9
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Broad categories currently enabled for public posts in this app, in the order this member should see
|
|
23
|
+
* them: their own order once the app serves personal orders, otherwise the app's popularity order. The
|
|
24
|
+
* order is private to the member. `order` asks for a specific one (default `personal`).
|
|
25
|
+
*/
|
|
26
|
+
categories(params?: {
|
|
27
|
+
order?: CategoryOrder;
|
|
28
|
+
}): Promise<SingleResponse<Array<{
|
|
10
29
|
id: string;
|
|
11
30
|
name: string;
|
|
12
|
-
}
|
|
31
|
+
}>> & {
|
|
32
|
+
meta?: CategoryOrderMeta;
|
|
33
|
+
}>;
|
|
13
34
|
/** List posts the authenticated caller has upvoted. */
|
|
14
35
|
liked(params?: Pick<ListPostsParams, 'limit' | 'offset' | 'cursor'>): Promise<PaginatedResponse<Post>>;
|
|
15
36
|
/** Posts similar to this one (embedding neighbours), in the app's scope. */
|
package/dist/resources/posts.js
CHANGED
|
@@ -7,9 +7,14 @@ export class PostsResource {
|
|
|
7
7
|
list(params) {
|
|
8
8
|
return this.client.get('/posts', params);
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Broad categories currently enabled for public posts in this app, in the order this member should see
|
|
12
|
+
* them: their own order once the app serves personal orders, otherwise the app's popularity order. The
|
|
13
|
+
* order is private to the member. `order` asks for a specific one (default `personal`).
|
|
14
|
+
*/
|
|
15
|
+
categories(params) {
|
|
16
|
+
// No params, no query: the same request older SDKs make.
|
|
17
|
+
return params ? this.client.get('/posts/categories', params) : this.client.get('/posts/categories');
|
|
13
18
|
}
|
|
14
19
|
/** List posts the authenticated caller has upvoted. */
|
|
15
20
|
liked(params) {
|
|
@@ -1255,7 +1255,10 @@ export declare class ProtocolsResource {
|
|
|
1255
1255
|
limit?: number;
|
|
1256
1256
|
cursor?: string;
|
|
1257
1257
|
}): Promise<SingleResponse<SocialAuthorFeed>>;
|
|
1258
|
-
/**
|
|
1258
|
+
/**
|
|
1259
|
+
* Public, app-moderated discovery without account authority. Enrichment uses cached judgments only.
|
|
1260
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
1261
|
+
*/
|
|
1259
1262
|
getSharedTrends(projectId: string, params: {
|
|
1260
1263
|
protocol: string;
|
|
1261
1264
|
limit?: number;
|
|
@@ -1264,7 +1267,7 @@ export declare class ProtocolsResource {
|
|
|
1264
1267
|
category?: string;
|
|
1265
1268
|
discovery?: boolean;
|
|
1266
1269
|
}): Promise<SingleResponse<SocialTrends>>;
|
|
1267
|
-
/** Open a provider-listed topic through the same public-only app boundary. */
|
|
1270
|
+
/** Open a provider-listed topic through the same public-only app boundary, with the same safety floor. */
|
|
1268
1271
|
getSharedTrendFeed(projectId: string, params: {
|
|
1269
1272
|
protocol: string;
|
|
1270
1273
|
topic: string;
|
|
@@ -1314,7 +1317,10 @@ export declare class ProtocolsResource {
|
|
|
1314
1317
|
post: string;
|
|
1315
1318
|
readRef?: string;
|
|
1316
1319
|
}): Promise<SingleResponse<SocialConversation>>;
|
|
1317
|
-
/**
|
|
1320
|
+
/**
|
|
1321
|
+
* Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content.
|
|
1322
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
1323
|
+
*/
|
|
1318
1324
|
getPublicTrends(params: {
|
|
1319
1325
|
protocol: string;
|
|
1320
1326
|
limit?: number;
|
|
@@ -1323,7 +1329,7 @@ export declare class ProtocolsResource {
|
|
|
1323
1329
|
category?: string;
|
|
1324
1330
|
discovery?: boolean;
|
|
1325
1331
|
}, options?: SocialReadOptions): Promise<SingleResponse<SocialTrends>>;
|
|
1326
|
-
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. */
|
|
1332
|
+
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. Screened like `getPublicTrends`. */
|
|
1327
1333
|
getPublicTrendFeed(params: {
|
|
1328
1334
|
protocol: string;
|
|
1329
1335
|
topic: string;
|
|
@@ -89,12 +89,15 @@ export class ProtocolsResource {
|
|
|
89
89
|
return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/author-feed`, { protocol: params.protocol, actor: params.actor,
|
|
90
90
|
limit: params.limit, cursor: params.cursor });
|
|
91
91
|
}
|
|
92
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Public, app-moderated discovery without account authority. Enrichment uses cached judgments only.
|
|
94
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
95
|
+
*/
|
|
93
96
|
getSharedTrends(projectId, params) {
|
|
94
97
|
return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/trends`, { protocol: params.protocol, limit: params.limit, cursor: params.cursor,
|
|
95
98
|
includePreviews: params.includePreviews, category: params.category, discovery: params.discovery });
|
|
96
99
|
}
|
|
97
|
-
/** Open a provider-listed topic through the same public-only app boundary. */
|
|
100
|
+
/** Open a provider-listed topic through the same public-only app boundary, with the same safety floor. */
|
|
98
101
|
getSharedTrendFeed(projectId, params) {
|
|
99
102
|
return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/trend-feed`, { protocol: params.protocol, topic: params.topic,
|
|
100
103
|
limit: params.limit, cursor: params.cursor, category: params.category, discovery: params.discovery });
|
|
@@ -148,14 +151,17 @@ export class ProtocolsResource {
|
|
|
148
151
|
getPublicConversation(params) {
|
|
149
152
|
return this.client.get('/protocols/social/conversation', { protocol: params.protocol, post: params.post, readRef: params.readRef });
|
|
150
153
|
}
|
|
151
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content.
|
|
156
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
157
|
+
*/
|
|
152
158
|
getPublicTrends(params, options) {
|
|
153
159
|
return this.client.get('/protocols/social/trends', { protocol: params.protocol, limit: params.limit, cursor: params.cursor,
|
|
154
160
|
...(params.includePreviews !== undefined ? { includePreviews: params.includePreviews } : {}),
|
|
155
161
|
...(params.category !== undefined ? { category: params.category } : {}),
|
|
156
162
|
...(params.discovery !== undefined ? { discovery: params.discovery } : {}) }, ...readOptions(options));
|
|
157
163
|
}
|
|
158
|
-
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. */
|
|
164
|
+
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. Screened like `getPublicTrends`. */
|
|
159
165
|
getPublicTrendFeed(params, options) {
|
|
160
166
|
return this.client.get('/protocols/social/trend-feed', { protocol: params.protocol, topic: params.topic, limit: params.limit, cursor: params.cursor,
|
|
161
167
|
...(params.category !== undefined ? { category: params.category } : {}),
|
package/dist/types.d.ts
CHANGED
|
@@ -226,7 +226,11 @@ export interface UpdatePostInput {
|
|
|
226
226
|
}
|
|
227
227
|
export type ReactionType = 'like' | 'heart' | 'fire' | 'laugh' | 'sad' | 'angry' | 'upvote' | 'downvote';
|
|
228
228
|
export interface ListPostsParams extends PaginationParams {
|
|
229
|
-
/**
|
|
229
|
+
/**
|
|
230
|
+
* Public-only feed with a bounded Jev presentation hint. Limit <=25.
|
|
231
|
+
* The safety floor does not depend on it: a category page and every app-wide
|
|
232
|
+
* `sort: 'hot'` or `'score'` read are screened with or without a layout.
|
|
233
|
+
*/
|
|
230
234
|
discovery?: boolean;
|
|
231
235
|
/** Public-only broad category. Requires an enabled app experiment; limit <=25. */
|
|
232
236
|
category?: string;
|
|
@@ -366,7 +370,8 @@ export interface CommunityBannedMember {
|
|
|
366
370
|
}
|
|
367
371
|
export interface CreateCommunityInput {
|
|
368
372
|
name: string;
|
|
369
|
-
slug
|
|
373
|
+
/** Optional vanity slug. Omit it for a server-generated collision-resistant default. */
|
|
374
|
+
slug?: string;
|
|
370
375
|
description?: string;
|
|
371
376
|
privacy?: 'public' | 'private' | 'hidden';
|
|
372
377
|
}
|
|
@@ -719,7 +724,8 @@ export interface AgentDetail extends Agent {
|
|
|
719
724
|
}
|
|
720
725
|
export interface CreateAgentInput {
|
|
721
726
|
name: string;
|
|
722
|
-
|
|
727
|
+
/** Optional public handle. Omit it for a server-generated collision-resistant default. */
|
|
728
|
+
username?: string;
|
|
723
729
|
bio?: string;
|
|
724
730
|
model?: string;
|
|
725
731
|
system_prompt?: string;
|
|
@@ -1082,6 +1088,13 @@ export interface LoginHistoryEntry {
|
|
|
1082
1088
|
}
|
|
1083
1089
|
export interface RecursivConfig {
|
|
1084
1090
|
apiKey?: string;
|
|
1091
|
+
/**
|
|
1092
|
+
* Whether to fall back to `RECURSIV_API_KEY` when `apiKey` is omitted.
|
|
1093
|
+
* Defaults to true for backwards compatibility and branded pre-auth flows.
|
|
1094
|
+
* Set to false for public clients that must remain keyless even in a
|
|
1095
|
+
* credentialed server process.
|
|
1096
|
+
*/
|
|
1097
|
+
inheritEnvironmentApiKey?: boolean;
|
|
1085
1098
|
baseUrl?: string;
|
|
1086
1099
|
timeout?: number;
|
|
1087
1100
|
maxRetries?: number;
|
package/llm.md
CHANGED
|
@@ -131,7 +131,7 @@ const { data } = await r.sandbox.execute({ code: '1 + 1', language: 'typescript'
|
|
|
131
131
|
```typescript
|
|
132
132
|
const { data: agent } = await r.agents.create({
|
|
133
133
|
name: 'Home Assistant',
|
|
134
|
-
username
|
|
134
|
+
// username is optional; the server generates a collision-resistant handle
|
|
135
135
|
model: 'anthropic/claude-sonnet-4.6', // see SUPPORTED_MODELS
|
|
136
136
|
system_prompt: 'You help homeowners maintain their property.',
|
|
137
137
|
project_id: projectId, // app-customer flow; works with per-user keys. Use organization_id only for team-owned agents.
|
|
@@ -647,7 +647,7 @@ Methods: `list`, `get`, `create`, `delete`
|
|
|
647
647
|
|
|
648
648
|
- `r.settings` — User preferences, sessions, password changes, account deletion. `revokeOtherSessions({ keep_session_token? })` signs out every other device and keeps this key (and the proven session); `revokeAllSessions()` also revokes this key. Accounts with no password confirm `requestEmailChange({ new_email, email_code })` with a code from `requestConfirmationCode('email_change')`. With a project-bound key, `getPreferences()` includes app-scoped `content`; persist changes with `updateContent({ show_nsfw?, autoplay_video?, ai_enabled?, default_feed? })`.
|
|
649
649
|
- `r.billing` — Usage tracking, checkout sessions, portal sessions, metered billing
|
|
650
|
-
- `r.appSubscriptions` — Project-scoped consumer plans: `checkout` (409 `subscription_exists` when the member already pays), `status`, `createPortalSession
|
|
650
|
+
- `r.appSubscriptions` — Project-scoped consumer plans: `checkout` (409 `subscription_exists` when the member already pays), `status`, `createPortalSession({ return_url, flow?: 'cancel' })` (`flow: 'cancel'` opens Stripe's cancel confirmation; 409 `no_subscription` when there is no plan to cancel), `previewTierChange({ tier })`, `changeTier({ tier, proration_date? })` (in-place, prorated, never a second subscription)
|
|
651
651
|
- `r.boosts` — Promoted posts (micro-USD): `create`, `mine`, `balance`, `pause`, `resume`, `cancel`, `recordEvent`; buy credit with `topup({ amount_cents, return_url })` then `confirmTopup(sessionId)` (credits once); admin `listPendingReview`, `review`, `grant`
|
|
652
652
|
- `r.notifications` — The notification stream: `list({ category: 'social' })` (rows carry `actor`, `reaction_type` and a `post_preview` with the viewer's `user_reaction`/`reposted_by_viewer_id` and counts), `unreadCount({ category })` for badges (`const { data } = await r.notifications.unreadCount({ category: 'social' })`), `markAsRead(id)`, `archive(id)` to dismiss, `markAllAsRead(category)` or `markAllAsRead({ category, organization_id })`, plus push token `registerToken`/`unregisterToken`. A push's `data.notificationId` is the row to mark read on tap. DMs are not in the stream.
|
|
653
653
|
- `r.uploads` — Legacy media upload URLs (prefer `r.storage` for new projects)
|
|
@@ -676,10 +676,10 @@ await r.appSubscriptions.changeTier({ tier: 'pro', proration_date: quote.prorati
|
|
|
676
676
|
- Native follows: `nativeFollowState({ accountId, protocol, nativeId })`, `prepareNativeFollow({ clientMutationId, accountId, protocol, nativeId, action: "follow" | "unfollow" })`, `submitNativeFollow(id, { approve: true, event? })`, `nativeFollowStatus(id)`. Show the acting identity and native following/requested/unfollowed state separately from the Minds feed subscription. Nostr must sign the complete review event without dropping other contacts; stale lists require a fresh review. Keep mutation IDs and reconcile ambiguous outcomes without replay. Requires current follow scopes and API `NATIVE_SOCIAL_FOLLOW_PROJECTS` plus source controls.
|
|
677
677
|
- Native reposts: `nativeRepostState({ accountId, protocol, nativeId })`, `prepareNativeRepost({ clientMutationId, accountId, protocol, nativeId, action: "repost" | "unrepost" })`, `submitNativeRepost(id, { approve: true, event? })`, `nativeRepostStatus(id)`. Requires `NATIVE_SOCIAL_REPOST_PROJECTS`, account capability and explicit approval; Nostr signs the exact review (kind 6 or own kind 5). Unknown attempts only reconcile. Bluesky connections need the new repost scope; ActivityPub uses write:statuses. Native likes: `nativeLikeState({ accountId, protocol, nativeId })`, `prepareNativeLike({ clientMutationId, accountId, protocol, nativeId, action: "like" | "unlike" })`, `submitNativeLike(id, { approve: true, event? })`, and `nativeLikeStatus(id)`. Display the selected account, source and action before approval. Only Nostr supplies `event`: sign the exact public `signing.event` in the review; never transmit a private key. Preserve mutation IDs on ambiguous responses and refresh unknown results instead of resending. Read capabilities before offering actions; old reply-only connections need reconnecting for likes. Nostr results describe the connected relay, not every public relay. The app-specific `NATIVE_SOCIAL_LIKE_PROJECTS` switch and source controls must be enabled on the API.
|
|
678
678
|
- Delegated social accounts: `r.protocols.nativeSocialAccounts()`, `startNativeSocialConnection({ protocol, identifier })`, `finishNativeSocialConnection({ protocol, state, code?, iss?, error? })`, `disconnectNativeSocialAccount(id)`. Example: `const { data } = await r.protocols.startNativeSocialConnection({ protocol: "bluesky", identifier: "alice.bsky.social" })`; open `data.authorizationUrl`, then complete with the same signed-in app user. Never log callback codes, send passwords, or auto-retry OAuth callbacks. Shared replies (off until schema/API deployment and live verification): `prepareNativeReply({ clientMutationId, text, parent: { protocol, nativeId, readRef? }, accountIds, media?, linkedPostAccountIds?, recoverFrom?: { batchId, deliveryIds } })`, `submitNativeReply(id, { approve: true, deliveryIds })`, `nativeReplyStatus(id)`, `nativeReplyHistory({ limit?, cursor? })`, `prepareNativeReplyRemoval(id, { deliveryIds })`, `removeNativeReply(id, { approve: true, removals })`. Review account, destination, `kind`, source, exact `wireText` and every reviewed `media` item with its alt text; connection is not publication approval. `media` takes the same `NativeMediaInput[]` as `prepareNativePost` and needs the account's `publish_media` capability. `linkedPostAccountIds` explicitly selects different-network standalone posts with a source link, never matching threaded replies or a same-network denial fallback. Preserve mutation IDs, show per-destination results and reconcile uncertain attempts without reposting. To review selected unsent or definitively rejected deliveries again, use recoverFrom with a new mutation ID, the same source/accounts and freshly reviewed text; preserve that ID on uncertain preparation. Only saved recoverable destinations qualify, current gates still apply, and replacedBy disables old sends atomically. Never restart unknown, accepted or published deliveries. The new preview still needs explicit approval. Saved delegated drafts/results: `const { data: history } = await r.protocols.nativeReplyHistory({ limit: 20 })`; use its opaque cursor for the next page and `nativeReplyStatus(item.id)` for details. This is a private read, not a write or provider refresh, and includes Nostr results marked type=nostr. Use nativeNostrReplyResult(item.id) to check that recorded event on its relay without signing or resending. Discard page state on owner/app changes. See README for examples and the callback, revocation and removal contracts.
|
|
679
|
-
- `r.protocols` — Private app safety: `sourceControls` returns current `enabled` plus paged saved choices; `setSourceControl`, `onSourceControlChange` and `reportSource` share app scope. Example: `const choices = new Map(); const stop = r.protocols.onSourceControlChange(change => choices.set(JSON.stringify([change.source, change.control]), change.enabled));` then `await r.protocols.setSourceControl({ source: { protocol: "bluesky", kind: "actor", nativeId: "did:plc:aaaaaaaaaaaaaaaaaaaaaaaa" }, control: "mute", enabled: true, scope: "minds" });` and call `stop(); choices.clear()` on account change/unmount. Notifications follow an exact server confirmation, never an uncertain response. These preferences and reports apply in Minds, not on the remote network. Keyless post links: `const visitor = new Recursiv({ anonymous: true }); const { data: thread } = await visitor.protocols.getSharedConversation(projectId, { protocol: 'nostr', post: publicEventId })`; `sharedNetworks(projectId)` lists public conversation capabilities. Only configured apps; app moderation and rate limits apply, no member state or write authority. Member public reading: `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. Example after selecting a returned source post: `const { data: thread } = await r.protocols.getPublicConversation({ protocol: post.protocol, post: post.nativeId, readRef: post.readRef })`. Render `focus`, oldest-first `ancestors` and direct `replies`; `public_provider_window` never promises every reply, and `limited` marks known omissions. Requires both `users:read` and `posts:read` on the current human app key. Protocol discovery/settings: `const { data: adapters } = await r.protocols.list()` returns bounded `{ id, name, icon, color, maturity, capabilities, limitations }` metadata; `supported` means implemented, not configured, healthy, authorized, or enabled. Methods: `list`, `status`, `listCandidates`, `getCandidate`, `search`, `getSettings`, `refresh`, `updateSettings`.
|
|
679
|
+
- `r.protocols` — Private app safety: `sourceControls` returns current `enabled` plus paged saved choices; `setSourceControl`, `onSourceControlChange` and `reportSource` share app scope. Example: `const choices = new Map(); const stop = r.protocols.onSourceControlChange(change => choices.set(JSON.stringify([change.source, change.control]), change.enabled));` then `await r.protocols.setSourceControl({ source: { protocol: "bluesky", kind: "actor", nativeId: "did:plc:aaaaaaaaaaaaaaaaaaaaaaaa" }, control: "mute", enabled: true, scope: "minds" });` and call `stop(); choices.clear()` on account change/unmount. Notifications follow an exact server confirmation, never an uncertain response. These preferences and reports apply in Minds, not on the remote network. Keyless post links: `const visitor = new Recursiv({ anonymous: true, inheritEnvironmentApiKey: false }); const { data: thread } = await visitor.protocols.getSharedConversation(projectId, { protocol: 'nostr', post: publicEventId })`; `sharedNetworks(projectId)` lists public conversation capabilities. Only configured apps; app moderation and rate limits apply, no member state or write authority. Member public reading: `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. Example after selecting a returned source post: `const { data: thread } = await r.protocols.getPublicConversation({ protocol: post.protocol, post: post.nativeId, readRef: post.readRef })`. Render `focus`, oldest-first `ancestors` and direct `replies`; `public_provider_window` never promises every reply, and `limited` marks known omissions. Requires both `users:read` and `posts:read` on the current human app key. Protocol discovery/settings: `const { data: adapters } = await r.protocols.list()` returns bounded `{ id, name, icon, color, maturity, capabilities, limitations }` metadata; `supported` means implemented, not configured, healthy, authorized, or enabled. Methods: `list`, `status`, `listCandidates`, `getCandidate`, `search`, `getSettings`, `refresh`, `updateSettings`.
|
|
680
680
|
- Native Nostr: `const { data: preview } = await r.protocols.prepareNativeNostr(identityId, { action: "publish", content: draft })`; review and sign the exact public event, then `submitNativeNostr(identityId, { intent: preview.intent, event, approve: true })`. Preparation/submission never auto-retry. After uncertainty, use `getNativeNostrPost(identityId, preview.postId ?? preview.eventId)` before a user-selected retry of the identical intent/event: deletion checks the original note's `postId`, publication checks `eventId`. Check `nativeNostrAccount()` enablement first; keys stay with the signer. For replies, after `nativeNostrAccount()` advertises `reply_text_note`, use `prepareNativeNostr(identityId, { action: "reply", content: draft, parentId: post.nativeId })`. Review returned parent/root/participants/relay and exact event, sign with the connected client signer, then `submitNativeNostr(identityId, { intent, event, approve: true })`. Requires users:read, posts:read and posts:write. Kind1 public notes only; reply enablement and shared UI/live proof remain pending. Do not inherit arbitrary notification tags or relay hints. Signing mutations never auto-retry; check `getNativeNostrPost(identityId, eventId)` after uncertainty before a user-selected identical-event retry.
|
|
681
681
|
- Hosted Bluesky account on the app's own PDS: `const { data } = await r.protocols.createHostedIdentity({ protocol: 'atproto', handle: 'alice', email })`. Only the label is sent; `data.handle` carries the domain and `data.password` is returned this once, never stored. Never auto-retried. Either the account is finished or it is removed again. 409 `handle_unavailable` = choose another label; 503 `handle_dns_unavailable`/`handle_unresolvable`/`handle_unverified` = operator setup, retry later unchanged.
|
|
682
|
-
- Anonymous discovery: `const visitor = new Recursiv({ anonymous: true }); const { data } = await visitor.protocols.getSharedTrends(projectId, { protocol: 'nostr', limit: 20, discovery: true })`. Use `sharedNetworks(projectId)` capabilities; open a returned topic with `getSharedTrendFeed(projectId, { protocol: 'bluesky', topic: trend.nativeId, cursor })`. Configured apps only; no account authority or writes. App moderation and per-IP/app rate limits apply. Categories and Jev layouts reuse cached judgments without initiating paid inference; handle partial categories and finite provider windows.
|
|
682
|
+
- Anonymous discovery: `const visitor = new Recursiv({ anonymous: true, inheritEnvironmentApiKey: false }); const { data } = await visitor.protocols.getSharedTrends(projectId, { protocol: 'nostr', limit: 20, discovery: true })`. Use `sharedNetworks(projectId)` capabilities; open a returned topic with `getSharedTrendFeed(projectId, { protocol: 'bluesky', topic: trend.nativeId, cursor })`. Configured apps only; no account authority or writes. App moderation and per-IP/app rate limits apply. Categories and Jev layouts reuse cached judgments without initiating paid inference; handle partial categories and finite provider windows.
|
|
683
683
|
- Public trends: require `public_trends` from `publicNetworks()`, then `getPublicTrends({ protocol, limit?, cursor? })`; render `kind: topics` as source topics and `kind: posts` as source posts. With `trend_feed`, open only a returned current topic using `getPublicTrendFeed({ protocol, topic: trend.nativeId, limit?, cursor? })`. Show provider, observedAt and provider_ranked_window coverage; null rankingWindow is unknown, not a daily/global ranking. Preserve source order, canonical IDs and readRef, and opaque cursors. Topic catalogs do not paginate; Mastodon trends stop at 200 source positions. Both methods require users:read plus posts:read on the current human app key; Nostr trends are unsupported. Propagate errors; trend_unavailable means refresh the topic list. No ingestion or native writes.
|
|
684
684
|
- `listCandidates({ protocol?, limit?, cursor? })` and `getCandidate(id)` are read-only quarantine review, not published-post search. Both require `admin` scope, a project-bound key, a live admin role, and existing project-admin access. The key supplies app/network scope; neither method accepts another project or network. They never invoke refresh, change settings, or publish.
|
|
685
685
|
- Lists return `{ data: ProtocolQuarantinedCandidate[], meta: { limit, has_more, next_cursor } }`; use the opaque `next_cursor`, not offset pagination. The API defaults `limit` to 20 and permits at most 50. Details return `{ data: ProtocolQuarantinedCandidate }`. Every candidate has `state: 'quarantined'` and `content_format: 'plain'`; render its bounded `content` as text, respect `content_truncated`, and distinguish upstream `published_at` from local `observed_at` / `created_at`.
|
|
@@ -790,6 +790,7 @@ The REST API (`/api/v1/*`) supports the `Idempotency-Key` header on POST/PUT/PAT
|
|
|
790
790
|
```typescript
|
|
791
791
|
const r = new Recursiv({
|
|
792
792
|
apiKey: 'sk_live_...', // or set RECURSIV_API_KEY env var
|
|
793
|
+
inheritEnvironmentApiKey: true, // default; false guarantees keyless operation
|
|
793
794
|
baseUrl: 'https://api.recursiv.io/api/v1', // default
|
|
794
795
|
timeout: 30000, // request timeout in ms
|
|
795
796
|
maxRetries: 2, // auto-retry on 429/5xx
|
|
@@ -798,9 +799,12 @@ const r = new Recursiv({
|
|
|
798
799
|
// Zero-arg works when RECURSIV_API_KEY is set
|
|
799
800
|
const r = new Recursiv()
|
|
800
801
|
|
|
801
|
-
// Anonymous sandbox (no API key
|
|
802
|
+
// Anonymous sandbox (no API key required; inherits RECURSIV_API_KEY when set)
|
|
802
803
|
const r = new Recursiv({ anonymous: true })
|
|
803
804
|
|
|
805
|
+
// Guaranteed keyless public client, even inside a credentialed server process
|
|
806
|
+
const visitor = new Recursiv({ anonymous: true, inheritEnvironmentApiKey: false })
|
|
807
|
+
|
|
804
808
|
// Self-hosted
|
|
805
809
|
const r = new Recursiv({ baseUrl: 'https://my-instance.com/api/v1' })
|
|
806
810
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@recursiv/sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.52",
|
|
4
4
|
"description": "TypeScript SDK for the Recursiv API",
|
|
5
5
|
"license": "FSL-1.1-ALv2",
|
|
6
6
|
"type": "module",
|
|
@@ -79,6 +79,6 @@
|
|
|
79
79
|
"build": "tsc",
|
|
80
80
|
"typecheck": "tsc --noEmit",
|
|
81
81
|
"test": "vitest run",
|
|
82
|
-
"test:unit": "vitest run"
|
|
82
|
+
"test:unit": "vitest run && tsc && node ../../scripts/security/verify-sdk-anonymous-package.mjs"
|
|
83
83
|
}
|
|
84
84
|
}
|