@nexod/twist
v0.2.0
Published
Cross-product Twist client. Wraps `@doist/twist-sdk` with a Nexod-flavored factory (`createTwistClient`) and OAuth helpers (`buildAuthorizationUrl`, `exchangeAuthorizationCode`).
Readme
@nexod/twist
Cross-product Twist client. Wraps @doist/twist-sdk with a Nexod-flavored factory (createTwistClient) and OAuth helpers (buildAuthorizationUrl, exchangeAuthorizationCode).
This package is engine code per engine.2026-05-02.001. It owns client construction, env-var resolution, error normalization, and a thin set of high-level helpers (postThread, postComment). Consuming products own which channels to post to, what content to format, and which OAuth credentials to use.
Engine README: ~/work/nexod/engine/README.md. Knowledge bundle (API + CLI + SDK + example): ~/work/nexod/.knowledge-bundles/twist/.
Install
pnpm add @nexod/twistIn the studio workspace, the package resolves locally via pnpm.
Token-based usage (server-side)
Set env vars in the consuming app:
TWIST_ACCESS_TOKEN=<long-lived OAuth token>
TWIST_WORKSPACE_ID=<workspace id, integer>Then:
import { createTwistClient } from "@nexod/twist";
const twist = createTwistClient();
await twist.postThread({
channelId: 12345,
title: "BOI pipeline run #402: Trending Social Product Scout",
content: "Run completed. 18 candidates, 3 flagged for review.",
});
await twist.postComment({
threadId: 67890,
content: "Editorial gate decision: APPROVE.",
});For raw API calls beyond the helpers, use twist.api.* directly:
const channels = await twist.api.channels.getChannels({ workspace_id: twist.workspaceId });OAuth flow
import { buildAuthorizationUrl, exchangeAuthorizationCode } from "@nexod/twist";
// 1. Build redirect URL on /twist/install
const authUrl = buildAuthorizationUrl({
clientId: process.env.TWIST_CLIENT_ID!,
scopes: ["user:read", "channels:read", "threads:write", "comments:write"],
state: cryptoRandomState,
redirectUri: "https://app.example.com/twist/callback",
});
// 2. In /twist/callback after verifying state matches
const { accessToken } = await exchangeAuthorizationCode({
clientId: process.env.TWIST_CLIENT_ID!,
clientSecret: process.env.TWIST_CLIENT_SECRET!,
code: callbackCode,
redirectUri: "https://app.example.com/twist/callback",
});
// Persist accessToken in Supabase (per-tenant) before instantiating the clientConfiguration responsibility
Per engine.2026-05-02.001:
- Engine owns: SDK construction, env-var reading at instantiation, error normalization, OAuth URL/token primitives, the
postThread/postCommenthelpers. - App owns: env var population, OAuth credential storage (Supabase via
@nexod/infra's state client), channel selection per pipeline stage, content formatting, retry policy beyond what the underlying SDK provides.
Bypass policy
Code in ~/work/overwatch/ or ~/work/boi/ that instantiates TwistApi directly is a bypass and requires a justification comment citing the gap in @nexod/twist. Repeated bypasses trigger an engine decision proposing a contract extension.
