universal-social-sdk 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.test.example +46 -0
- package/CHANGELOG.md +84 -0
- package/README.md +93 -0
- package/dist/cli/index.js +219 -23
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +556 -146
- package/dist/index.js +470 -74
- package/dist/index.js.map +1 -1
- package/examples/bluesky-post.mjs +14 -0
- package/examples/instagram-reel.mjs +15 -0
- package/examples/queue-in-memory.mjs +17 -0
- package/examples/x-post.mjs +14 -0
- package/package.json +17 -2
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Copy this file to .env.test and fill only sandbox credentials.
|
|
2
|
+
# Tests load .env.test first, then .env as fallback.
|
|
3
|
+
|
|
4
|
+
# X
|
|
5
|
+
X_API_KEY=
|
|
6
|
+
X_API_SECRET=
|
|
7
|
+
X_ACCESS_TOKEN=
|
|
8
|
+
X_ACCESS_SECRET=
|
|
9
|
+
X_TEST_TWEET_ID=
|
|
10
|
+
|
|
11
|
+
# Facebook
|
|
12
|
+
FB_PAGE_ACCESS_TOKEN=
|
|
13
|
+
FB_PAGE_ID=
|
|
14
|
+
|
|
15
|
+
# Instagram
|
|
16
|
+
IG_ACCESS_TOKEN=
|
|
17
|
+
IG_USER_ID=
|
|
18
|
+
|
|
19
|
+
# LinkedIn
|
|
20
|
+
LINKEDIN_ACCESS_TOKEN=
|
|
21
|
+
LINKEDIN_ORG_URN=
|
|
22
|
+
|
|
23
|
+
# YouTube
|
|
24
|
+
YOUTUBE_ACCESS_TOKEN=
|
|
25
|
+
YOUTUBE_CHANNEL_ID=
|
|
26
|
+
|
|
27
|
+
# TikTok
|
|
28
|
+
TIKTOK_ACCESS_TOKEN=
|
|
29
|
+
|
|
30
|
+
# Pinterest
|
|
31
|
+
PINTEREST_ACCESS_TOKEN=
|
|
32
|
+
PINTEREST_BOARD_ID=
|
|
33
|
+
|
|
34
|
+
# Bluesky
|
|
35
|
+
BLUESKY_IDENTIFIER=
|
|
36
|
+
BLUESKY_APP_PASSWORD=
|
|
37
|
+
BLUESKY_TEST_ACTOR=
|
|
38
|
+
|
|
39
|
+
# Mastodon
|
|
40
|
+
MASTODON_BASE_URL=
|
|
41
|
+
MASTODON_ACCESS_TOKEN=
|
|
42
|
+
MASTODON_ACCOUNT_ID=
|
|
43
|
+
|
|
44
|
+
# Threads
|
|
45
|
+
THREADS_ACCESS_TOKEN=
|
|
46
|
+
THREADS_USER_ID=
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Non-interactive updater mode for CI/automation (`--ci`, `--open-pr`, `--branch-prefix`, `--base`, `--artifacts-dir`).
|
|
13
|
+
- Structured updater artifacts (`.artifacts/update-plan.json`, `.artifacts/update-diff-summary.json`, `.artifacts/pr-title.txt`, `.artifacts/pr-body.md`).
|
|
14
|
+
- Strict Ollama patch-plan schema validation with typed `changes` metadata (platform, endpoint, change type, confidence).
|
|
15
|
+
- Scheduled PR automation workflow: `.github/workflows/auto-update-pr.yml`.
|
|
16
|
+
- Workflow-dispatch `dry_run` mode for updater automation (detect and generate artifacts without opening PRs).
|
|
17
|
+
- Unit tests covering updater plan validation and no-change detection behavior.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Updater CI mode now enforces `npm run build` and `npm run test:unit` before PR automation proceeds.
|
|
22
|
+
- Auto-update PR workflow now excludes `.artifacts/*` from commits while still using artifacts for PR metadata.
|
|
23
|
+
|
|
24
|
+
## [1.1.0] - 2026-03-08
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `src/webhooks` module with:
|
|
29
|
+
- Meta and X signature verification helpers
|
|
30
|
+
- normalized webhook event parsing helpers
|
|
31
|
+
- `WebhookRouter` for typed and wildcard event dispatch
|
|
32
|
+
- Unit tests for webhook verification, normalization, and routing.
|
|
33
|
+
- `src/queue` module with:
|
|
34
|
+
- `QueueAdapter` interface
|
|
35
|
+
- `InMemoryQueueAdapter`
|
|
36
|
+
- skeleton adapters for BullMQ and SQS
|
|
37
|
+
- queue adapter registry (`setQueueAdapter`, `getQueueAdapter`, `resetQueueAdapter`)
|
|
38
|
+
- Scheduler integration to route scheduled jobs through the active queue adapter.
|
|
39
|
+
- Unit tests for queue adapter registry and scheduler behavior.
|
|
40
|
+
- Added typed response interfaces and explicit return contracts across all public platform methods.
|
|
41
|
+
- Refined response contracts with platform-specific action/delete/result aliases for stronger API clarity.
|
|
42
|
+
- Added internal normalized result helpers so action/delete/mutation/detail responses remain stable even if upstream provider payloads drift.
|
|
43
|
+
- Added `docs/RESPONSE_CONTRACTS.md` with stable contract reference and integration guidance.
|
|
44
|
+
- Exported response interfaces from package root.
|
|
45
|
+
- Unit test coverage for typed response shapes.
|
|
46
|
+
|
|
47
|
+
## [1.0.1] - 2026-03-08
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- Runtime validation for public platform methods.
|
|
52
|
+
- Integration test scaffolding for all supported platforms.
|
|
53
|
+
- Repository governance files (`CODE_OF_CONDUCT.md`, `SECURITY.md`, `LICENSE`).
|
|
54
|
+
- Examples folder with runnable scripts.
|
|
55
|
+
- Package metadata fields (`homepage`, `bugs`, `funding`) in `package.json`.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- CI and release workflow gating behavior for clearer run outcomes.
|
|
60
|
+
- Documentation coverage for platform matrix, testing, and project structure.
|
|
61
|
+
|
|
62
|
+
## [1.0.0] - 2026-03-08
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- Initial public release of `universal-social-sdk`.
|
|
67
|
+
- Unified static async APIs for:
|
|
68
|
+
- X
|
|
69
|
+
- Facebook
|
|
70
|
+
- Instagram
|
|
71
|
+
- LinkedIn
|
|
72
|
+
- YouTube
|
|
73
|
+
- TikTok
|
|
74
|
+
- Pinterest
|
|
75
|
+
- Bluesky
|
|
76
|
+
- Mastodon
|
|
77
|
+
- Threads
|
|
78
|
+
- CLI:
|
|
79
|
+
- `universal-social-sdk init`
|
|
80
|
+
- `universal-social-sdk update`
|
|
81
|
+
- Build + type declarations via TypeScript + tsup.
|
|
82
|
+
- Unit and integration test scaffolding.
|
|
83
|
+
- GitHub Actions CI and npm release automation.
|
|
84
|
+
- Comprehensive setup and usage documentation.
|
package/README.md
CHANGED
|
@@ -9,9 +9,12 @@ TypeScript-first, ESM-only, zero-bloat Node.js SDK that provides one unified int
|
|
|
9
9
|
|
|
10
10
|
- [Project Structure](./docs/PROJECT_STRUCTURE.md)
|
|
11
11
|
- [NPM Package Guide](./docs/NPM_PACKAGE_GUIDE.md)
|
|
12
|
+
- [Response Contracts](./docs/RESPONSE_CONTRACTS.md)
|
|
12
13
|
- [Contributing](./docs/CONTRIBUTING.md)
|
|
14
|
+
- [Roadmap v1.1.0](./docs/ROADMAP_v1.1.0.md)
|
|
13
15
|
- [Code of Conduct](./CODE_OF_CONDUCT.md)
|
|
14
16
|
- [Security Policy](./SECURITY.md)
|
|
17
|
+
- [Changelog](./CHANGELOG.md)
|
|
15
18
|
|
|
16
19
|
## Install
|
|
17
20
|
|
|
@@ -46,6 +49,77 @@ await Mastodon.createStatus({ text: "Hello Fediverse!" });
|
|
|
46
49
|
await Threads.postText({ text: "Hello Threads!" });
|
|
47
50
|
```
|
|
48
51
|
|
|
52
|
+
## Examples
|
|
53
|
+
|
|
54
|
+
Run bundled examples:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm run example:x
|
|
58
|
+
npm run example:instagram
|
|
59
|
+
npm run example:bluesky
|
|
60
|
+
npm run example:queue
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Files:
|
|
64
|
+
|
|
65
|
+
- `examples/x-post.mjs`
|
|
66
|
+
- `examples/instagram-reel.mjs`
|
|
67
|
+
- `examples/bluesky-post.mjs`
|
|
68
|
+
- `examples/queue-in-memory.mjs`
|
|
69
|
+
|
|
70
|
+
## Queue Adapters
|
|
71
|
+
|
|
72
|
+
The scheduler uses a queue adapter. By default it uses an in-memory adapter.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { InMemoryQueueAdapter, setQueueAdapter } from "universal-social-sdk";
|
|
76
|
+
|
|
77
|
+
setQueueAdapter(new InMemoryQueueAdapter());
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Adapter exports:
|
|
81
|
+
|
|
82
|
+
- `InMemoryQueueAdapter` (default behavior)
|
|
83
|
+
- `BullMQAdapter` (skeleton)
|
|
84
|
+
- `SQSAdapter` (skeleton)
|
|
85
|
+
|
|
86
|
+
The scheduler APIs (for example `X.scheduleTweet`) will use the active adapter automatically.
|
|
87
|
+
|
|
88
|
+
## Typed Responses
|
|
89
|
+
|
|
90
|
+
All public SDK methods now return concrete TypeScript interfaces, including platform-specific action/delete aliases for clearer contracts.
|
|
91
|
+
Action/delete/mutation/detail responses are normalized into stable contracts (`success`, `action`/`targetId`/`resourceId`, `raw`) so provider endpoint changes are isolated to SDK internals.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { X } from "universal-social-sdk";
|
|
95
|
+
import type { XPostResult } from "universal-social-sdk";
|
|
96
|
+
|
|
97
|
+
const result: XPostResult = await X.postTweet({ text: "typed response" });
|
|
98
|
+
console.log(result.data.id);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Webhooks
|
|
102
|
+
|
|
103
|
+
The SDK includes webhook utilities for Meta-style and X-style signatures, normalized event parsing, and a lightweight router.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import {
|
|
107
|
+
WebhookRouter,
|
|
108
|
+
verifyMetaWebhookSignature,
|
|
109
|
+
verifyXWebhookSignature
|
|
110
|
+
} from "universal-social-sdk";
|
|
111
|
+
|
|
112
|
+
const router = new WebhookRouter();
|
|
113
|
+
|
|
114
|
+
router.on("meta.feed", async (event) => {
|
|
115
|
+
console.log("Meta feed event", event.payload);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
router.on("x.tweet_create_events", async (event) => {
|
|
119
|
+
console.log("X tweet event", event.payload);
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
49
123
|
## Required Environment Variables
|
|
50
124
|
|
|
51
125
|
`universal-social-sdk` auto-loads `.env` with `dotenv`.
|
|
@@ -145,6 +219,7 @@ npx universal-social-sdk update
|
|
|
145
219
|
npx universal-social-sdk update --dry-run
|
|
146
220
|
npx universal-social-sdk update --model llama3.2
|
|
147
221
|
npx universal-social-sdk update --yes
|
|
222
|
+
npx universal-social-sdk update --ci --open-pr --base main --branch-prefix chore/updater
|
|
148
223
|
```
|
|
149
224
|
|
|
150
225
|
Flow:
|
|
@@ -156,6 +231,13 @@ Flow:
|
|
|
156
231
|
5. Shows git-style diffs and asks for confirmation.
|
|
157
232
|
6. Applies patches and rebuilds package.
|
|
158
233
|
|
|
234
|
+
CI/PR mode writes deterministic artifacts in `.artifacts/`:
|
|
235
|
+
|
|
236
|
+
- `update-plan.json`
|
|
237
|
+
- `update-diff-summary.json`
|
|
238
|
+
- `pr-title.txt`
|
|
239
|
+
- `pr-body.md`
|
|
240
|
+
|
|
159
241
|
## Supported Methods
|
|
160
242
|
|
|
161
243
|
Method coverage by platform:
|
|
@@ -296,6 +378,12 @@ This repo includes:
|
|
|
296
378
|
|
|
297
379
|
- `.github/workflows/ci.yml` for build + unit tests on every push/PR.
|
|
298
380
|
- `.github/workflows/release.yml` for npm publish on tag (`v*`) or manual dispatch.
|
|
381
|
+
- `.github/workflows/auto-update-pr.yml` for scheduled doc crawling + updater PR generation.
|
|
382
|
+
- Generates `.artifacts/*` for PR metadata during the run but does not commit artifact files.
|
|
383
|
+
|
|
384
|
+
Manual dry-run option for updater workflow:
|
|
385
|
+
|
|
386
|
+
- In **Actions -> Auto Update PR -> Run workflow**, set `dry_run=true` to run detection and artifact generation only (no branch/PR).
|
|
299
387
|
|
|
300
388
|
Configure these repository secrets to enable integration CI:
|
|
301
389
|
|
|
@@ -327,3 +415,8 @@ Configure these repository secrets to enable integration CI:
|
|
|
327
415
|
Configure this repository secret for publishing:
|
|
328
416
|
|
|
329
417
|
- `NPM_TOKEN`
|
|
418
|
+
|
|
419
|
+
Configure this repository secret for scheduled updater PRs:
|
|
420
|
+
|
|
421
|
+
- `OLLAMA_HOST` (required; endpoint reachable from GitHub Actions runner)
|
|
422
|
+
- `OLLAMA_MODEL` (optional override)
|
package/dist/cli/index.js
CHANGED
|
@@ -155,7 +155,7 @@ async function runInitCommand(cwd) {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
// src/cli/commands/update.ts
|
|
158
|
-
import { readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
158
|
+
import { mkdir, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
159
159
|
import path3 from "path";
|
|
160
160
|
import chalk2 from "chalk";
|
|
161
161
|
import inquirer2 from "inquirer";
|
|
@@ -236,6 +236,7 @@ async function crawlAllDocs() {
|
|
|
236
236
|
|
|
237
237
|
// src/updater/ollama.ts
|
|
238
238
|
import axios2 from "axios";
|
|
239
|
+
import { z } from "zod";
|
|
239
240
|
|
|
240
241
|
// src/config/env.ts
|
|
241
242
|
import dotenv from "dotenv";
|
|
@@ -316,13 +317,33 @@ var env = {
|
|
|
316
317
|
};
|
|
317
318
|
|
|
318
319
|
// src/updater/ollama.ts
|
|
320
|
+
var ollamaPatchPlanSchema = z.object({
|
|
321
|
+
summary: z.string().min(1),
|
|
322
|
+
updatedMethods: z.record(z.array(z.string())),
|
|
323
|
+
changes: z.array(
|
|
324
|
+
z.object({
|
|
325
|
+
platform: z.string().min(1),
|
|
326
|
+
endpoint: z.string().min(1),
|
|
327
|
+
changeType: z.enum(["added", "modified", "deprecated", "removed"]),
|
|
328
|
+
confidence: z.number().min(0).max(1),
|
|
329
|
+
notes: z.string().optional()
|
|
330
|
+
})
|
|
331
|
+
).default([]),
|
|
332
|
+
files: z.array(
|
|
333
|
+
z.object({
|
|
334
|
+
path: z.string().min(1),
|
|
335
|
+
content: z.string()
|
|
336
|
+
})
|
|
337
|
+
),
|
|
338
|
+
readmeTable: z.string()
|
|
339
|
+
});
|
|
319
340
|
function buildPrompt(docs, existingMethodsJson) {
|
|
320
341
|
return [
|
|
321
342
|
"You are maintaining universal-social-sdk.",
|
|
322
343
|
"Compare the crawled docs with current methods and identify NEW or CHANGED endpoints.",
|
|
323
344
|
"Focus areas: content publishing, stories, reels, comments, DMs, analytics.",
|
|
324
345
|
"Output STRICT JSON with this shape only:",
|
|
325
|
-
'{"summary": string, "updatedMethods": Record<string,string[]>, "files": [{"path": string, "content": string}], "readmeTable": string}',
|
|
346
|
+
'{"summary": string, "updatedMethods": Record<string,string[]>, "changes": [{"platform": string, "endpoint": string, "changeType": "added|modified|deprecated|removed", "confidence": number, "notes"?: string}], "files": [{"path": string, "content": string}], "readmeTable": string}',
|
|
326
347
|
"Files must target src/platforms/*.ts and supported-methods.json updates when needed.",
|
|
327
348
|
"Each file.content must be complete TypeScript file content, not patch snippets.",
|
|
328
349
|
"Current supported-methods.json:",
|
|
@@ -339,7 +360,14 @@ function parseJsonOutput(raw) {
|
|
|
339
360
|
throw new Error("Ollama response did not contain a JSON object.");
|
|
340
361
|
}
|
|
341
362
|
const maybeJson = cleaned.slice(start, end + 1);
|
|
342
|
-
|
|
363
|
+
const parsed = JSON.parse(maybeJson);
|
|
364
|
+
const result = ollamaPatchPlanSchema.safeParse(parsed);
|
|
365
|
+
if (!result.success) {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`Ollama patch plan schema validation failed: ${result.error.message}`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
return result.data;
|
|
343
371
|
}
|
|
344
372
|
async function askOllamaForPatchPlan(params) {
|
|
345
373
|
const model = params.model || env.ollama.model || "llama3.2:3b";
|
|
@@ -413,9 +441,9 @@ async function applyPlannedDiffs(params) {
|
|
|
413
441
|
|
|
414
442
|
// src/cli/commands/update.ts
|
|
415
443
|
import { spawn } from "child_process";
|
|
416
|
-
function
|
|
444
|
+
function runNpmScript(cwd, script) {
|
|
417
445
|
return new Promise((resolve, reject) => {
|
|
418
|
-
const child = spawn("npm", ["run",
|
|
446
|
+
const child = spawn("npm", ["run", script], {
|
|
419
447
|
cwd,
|
|
420
448
|
stdio: "inherit",
|
|
421
449
|
shell: true
|
|
@@ -424,7 +452,9 @@ function runBuild(cwd) {
|
|
|
424
452
|
if (code === 0) {
|
|
425
453
|
resolve();
|
|
426
454
|
} else {
|
|
427
|
-
reject(
|
|
455
|
+
reject(
|
|
456
|
+
new Error(`npm run ${script} failed with exit code ${code ?? -1}`)
|
|
457
|
+
);
|
|
428
458
|
}
|
|
429
459
|
});
|
|
430
460
|
});
|
|
@@ -444,19 +474,134 @@ ${endMarker}`;
|
|
|
444
474
|
replacement
|
|
445
475
|
);
|
|
446
476
|
}
|
|
477
|
+
function normalizeMethods(methods) {
|
|
478
|
+
return Object.fromEntries(
|
|
479
|
+
Object.entries(methods).sort(([a], [b]) => a.localeCompare(b)).map(([platform, items]) => [platform, [...items].sort()])
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
function parseExistingMethods(methodsJson) {
|
|
483
|
+
try {
|
|
484
|
+
const parsed = JSON.parse(methodsJson);
|
|
485
|
+
return parsed.platforms ?? {};
|
|
486
|
+
} catch {
|
|
487
|
+
return {};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function hasMaterialChanges(params) {
|
|
491
|
+
const hasFileChanges = params.diffs.some((diff) => diff.before !== diff.after);
|
|
492
|
+
const methodsChanged = JSON.stringify(normalizeMethods(params.existingMethods)) !== JSON.stringify(normalizeMethods(params.updatedMethods));
|
|
493
|
+
return {
|
|
494
|
+
hasChanges: hasFileChanges || methodsChanged,
|
|
495
|
+
hasFileChanges,
|
|
496
|
+
methodsChanged
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
function inferRisk(params) {
|
|
500
|
+
const text = params.summary.toLowerCase();
|
|
501
|
+
if (text.includes("breaking") || text.includes("deprecat") || text.includes("remove")) {
|
|
502
|
+
return "high";
|
|
503
|
+
}
|
|
504
|
+
if (params.diffs.some((diff) => diff.path.startsWith("src/platforms/"))) {
|
|
505
|
+
return "medium";
|
|
506
|
+
}
|
|
507
|
+
return "low";
|
|
508
|
+
}
|
|
509
|
+
function buildPrArtifacts(params) {
|
|
510
|
+
const changedPlatforms = /* @__PURE__ */ new Set();
|
|
511
|
+
for (const file of params.plan.files) {
|
|
512
|
+
const parts = file.path.split("/");
|
|
513
|
+
if (parts[0] === "src" && parts[1] === "platforms" && parts[2]) {
|
|
514
|
+
changedPlatforms.add(parts[2].replace(/\.ts$/, ""));
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
const changeTypeCounts = params.plan.changes.reduce(
|
|
518
|
+
(acc, change) => {
|
|
519
|
+
acc[change.changeType] = (acc[change.changeType] ?? 0) + 1;
|
|
520
|
+
return acc;
|
|
521
|
+
},
|
|
522
|
+
{}
|
|
523
|
+
);
|
|
524
|
+
const branchName = `${params.branchPrefix}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
525
|
+
const title = "chore(updater): sync social API documentation changes";
|
|
526
|
+
const body = [
|
|
527
|
+
"## Summary",
|
|
528
|
+
`- ${params.plan.summary}`,
|
|
529
|
+
`- Risk classification: **${params.risk}**`,
|
|
530
|
+
`- Changed files: ${params.diffs.length}`,
|
|
531
|
+
`- Changed platforms: ${changedPlatforms.size > 0 ? [...changedPlatforms].sort().join(", ") : "none"}`,
|
|
532
|
+
`- Endpoint deltas: ${Object.keys(changeTypeCounts).length > 0 ? Object.entries(changeTypeCounts).map(([type, count]) => `${type}=${count}`).join(", ") : "none"}`,
|
|
533
|
+
"",
|
|
534
|
+
"## Validation",
|
|
535
|
+
"- [x] `npm run build`",
|
|
536
|
+
"- [x] `npm run test:unit`",
|
|
537
|
+
"",
|
|
538
|
+
"## Review checklist",
|
|
539
|
+
"- [ ] Confirm endpoint/scope changes match official docs",
|
|
540
|
+
"- [ ] Confirm method signatures are backward compatible",
|
|
541
|
+
"- [ ] Confirm normalized response contracts remain stable"
|
|
542
|
+
].join("\n");
|
|
543
|
+
return {
|
|
544
|
+
title,
|
|
545
|
+
body,
|
|
546
|
+
base: params.base,
|
|
547
|
+
branchName,
|
|
548
|
+
changedPlatforms: [...changedPlatforms].sort()
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
async function writeUpdaterArtifacts(params) {
|
|
552
|
+
const outDir = path3.join(params.cwd, params.artifactsDir);
|
|
553
|
+
await mkdir(outDir, { recursive: true });
|
|
554
|
+
await writeFile3(
|
|
555
|
+
path3.join(outDir, "update-plan.json"),
|
|
556
|
+
JSON.stringify(
|
|
557
|
+
{
|
|
558
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
559
|
+
docsCount: params.docsCount,
|
|
560
|
+
summary: params.plan.summary,
|
|
561
|
+
changes: params.plan.changes,
|
|
562
|
+
updatedMethods: params.plan.updatedMethods,
|
|
563
|
+
files: params.plan.files.map((file) => file.path)
|
|
564
|
+
},
|
|
565
|
+
null,
|
|
566
|
+
2
|
|
567
|
+
),
|
|
568
|
+
"utf8"
|
|
569
|
+
);
|
|
570
|
+
await writeFile3(
|
|
571
|
+
path3.join(outDir, "update-diff-summary.json"),
|
|
572
|
+
JSON.stringify(
|
|
573
|
+
{
|
|
574
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
575
|
+
hasChanges: params.hasChanges,
|
|
576
|
+
risk: params.risk,
|
|
577
|
+
changes: params.plan.changes,
|
|
578
|
+
changedFiles: params.diffs.filter((diff) => diff.before !== diff.after).map((diff) => diff.path),
|
|
579
|
+
pr: params.pr
|
|
580
|
+
},
|
|
581
|
+
null,
|
|
582
|
+
2
|
|
583
|
+
),
|
|
584
|
+
"utf8"
|
|
585
|
+
);
|
|
586
|
+
await writeFile3(path3.join(outDir, "pr-title.txt"), `${params.pr.title}
|
|
587
|
+
`, "utf8");
|
|
588
|
+
await writeFile3(path3.join(outDir, "pr-body.md"), `${params.pr.body}
|
|
589
|
+
`, "utf8");
|
|
590
|
+
}
|
|
447
591
|
async function runUpdateCommand(cwd, options = {}) {
|
|
448
592
|
const crawlSpinner = ora2("Crawling official social API docs...").start();
|
|
449
593
|
const docs = await crawlAllDocs();
|
|
450
594
|
crawlSpinner.succeed(`Crawled ${docs.length} documentation pages.`);
|
|
451
595
|
const methodsPath = path3.join(cwd, "supported-methods.json");
|
|
452
596
|
const methodsJson = await readFile2(methodsPath, "utf8");
|
|
453
|
-
const
|
|
597
|
+
const existingMethods = parseExistingMethods(methodsJson);
|
|
598
|
+
const planSpinner = ora2("Generating SDK update plan from crawled docs...").start();
|
|
454
599
|
const plan = await askOllamaForPatchPlan({
|
|
455
600
|
docs,
|
|
456
601
|
existingMethodsJson: methodsJson,
|
|
457
602
|
model: options.model
|
|
458
603
|
});
|
|
459
|
-
|
|
604
|
+
planSpinner.succeed("Update plan generated.");
|
|
460
605
|
const generatedFiles = [...plan.files];
|
|
461
606
|
const readmePath = path3.join(cwd, "README.md");
|
|
462
607
|
const readmeContent = await readFile2(readmePath, "utf8");
|
|
@@ -471,6 +616,20 @@ async function runUpdateCommand(cwd, options = {}) {
|
|
|
471
616
|
rootDir: cwd,
|
|
472
617
|
files: generatedFiles
|
|
473
618
|
});
|
|
619
|
+
const changeStats = hasMaterialChanges({
|
|
620
|
+
diffs,
|
|
621
|
+
existingMethods,
|
|
622
|
+
updatedMethods: plan.updatedMethods
|
|
623
|
+
});
|
|
624
|
+
const hasChanges = changeStats.hasChanges;
|
|
625
|
+
const risk = inferRisk({ summary: plan.summary, diffs });
|
|
626
|
+
const prArtifacts = buildPrArtifacts({
|
|
627
|
+
plan,
|
|
628
|
+
diffs,
|
|
629
|
+
risk,
|
|
630
|
+
base: options.base ?? "main",
|
|
631
|
+
branchPrefix: options.branchPrefix ?? "chore/updater"
|
|
632
|
+
});
|
|
474
633
|
console.log(chalk2.bold("\nProposed changes"));
|
|
475
634
|
console.log(chalk2.dim(plan.summary));
|
|
476
635
|
for (const planned of diffs) {
|
|
@@ -481,7 +640,24 @@ async function runUpdateCommand(cwd, options = {}) {
|
|
|
481
640
|
console.log(chalk2.dim("... diff truncated in terminal preview ..."));
|
|
482
641
|
}
|
|
483
642
|
}
|
|
484
|
-
|
|
643
|
+
if (options.openPr || options.ci) {
|
|
644
|
+
await writeUpdaterArtifacts({
|
|
645
|
+
cwd,
|
|
646
|
+
artifactsDir: options.artifactsDir ?? ".artifacts",
|
|
647
|
+
docsCount: docs.length,
|
|
648
|
+
plan,
|
|
649
|
+
diffs,
|
|
650
|
+
hasChanges,
|
|
651
|
+
risk,
|
|
652
|
+
pr: prArtifacts
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
if (!hasChanges) {
|
|
656
|
+
console.log(chalk2.green("No material documentation changes detected."));
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
const nonInteractive = options.ci || options.openPr;
|
|
660
|
+
const applyChanges = options.yes ? true : nonInteractive ? true : (await inquirer2.prompt([
|
|
485
661
|
{
|
|
486
662
|
type: "confirm",
|
|
487
663
|
name: "applyChanges",
|
|
@@ -513,13 +689,18 @@ async function runUpdateCommand(cwd, options = {}) {
|
|
|
513
689
|
);
|
|
514
690
|
applySpinner.succeed("Patch files applied.");
|
|
515
691
|
const buildSpinner = ora2("Rebuilding package...").start();
|
|
516
|
-
await
|
|
692
|
+
await runNpmScript(cwd, "build");
|
|
517
693
|
buildSpinner.succeed("Package rebuilt successfully.");
|
|
694
|
+
if (options.ci || options.openPr) {
|
|
695
|
+
const testSpinner = ora2("Running unit tests...").start();
|
|
696
|
+
await runNpmScript(cwd, "test:unit");
|
|
697
|
+
testSpinner.succeed("Unit tests passed.");
|
|
698
|
+
}
|
|
518
699
|
}
|
|
519
700
|
|
|
520
701
|
// src/cli/index.ts
|
|
521
702
|
var program = new Command();
|
|
522
|
-
program.name("universal-social-sdk").description("Universal social media SDK CLI").version("1.
|
|
703
|
+
program.name("universal-social-sdk").description("Universal social media SDK CLI").version("1.1.0");
|
|
523
704
|
program.command("init").description("Create .env.example and show OAuth setup links").action(async () => {
|
|
524
705
|
try {
|
|
525
706
|
await runInitCommand(process.cwd());
|
|
@@ -529,18 +710,33 @@ program.command("init").description("Create .env.example and show OAuth setup li
|
|
|
529
710
|
process.exitCode = 1;
|
|
530
711
|
}
|
|
531
712
|
});
|
|
532
|
-
program.command("update").description("Crawl docs + run local Ollama + patch SDK sources").option("--dry-run", "Preview changes without writing files").option("-y, --yes", "Apply changes without confirmation prompt").option("--model <name>", "Override Ollama model for this run").
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
713
|
+
program.command("update").description("Crawl docs + run local Ollama + patch SDK sources").option("--dry-run", "Preview changes without writing files").option("-y, --yes", "Apply changes without confirmation prompt").option("--model <name>", "Override Ollama model for this run").option("--ci", "Run in non-interactive CI mode").option("--open-pr", "Prepare PR artifacts for workflow automation").option(
|
|
714
|
+
"--branch-prefix <prefix>",
|
|
715
|
+
"Branch prefix for updater PR metadata",
|
|
716
|
+
"chore/updater"
|
|
717
|
+
).option("--base <branch>", "Base branch for PR metadata", "main").option(
|
|
718
|
+
"--artifacts-dir <path>",
|
|
719
|
+
"Directory for generated updater artifacts",
|
|
720
|
+
".artifacts"
|
|
721
|
+
).action(
|
|
722
|
+
async (options) => {
|
|
723
|
+
try {
|
|
724
|
+
await runUpdateCommand(process.cwd(), {
|
|
725
|
+
dryRun: options.dryRun,
|
|
726
|
+
yes: options.yes,
|
|
727
|
+
model: options.model,
|
|
728
|
+
ci: options.ci,
|
|
729
|
+
openPr: options.openPr,
|
|
730
|
+
branchPrefix: options.branchPrefix,
|
|
731
|
+
base: options.base,
|
|
732
|
+
artifactsDir: options.artifactsDir
|
|
733
|
+
});
|
|
734
|
+
} catch (error) {
|
|
735
|
+
console.error(chalk3.red("Update failed."));
|
|
736
|
+
console.error(error);
|
|
737
|
+
process.exitCode = 1;
|
|
738
|
+
}
|
|
543
739
|
}
|
|
544
|
-
|
|
740
|
+
);
|
|
545
741
|
program.parse(process.argv);
|
|
546
742
|
//# sourceMappingURL=index.js.map
|