@atypica-ai/cli 0.1.0-alpha.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.
Files changed (63) hide show
  1. package/README.md +231 -0
  2. package/README.zh-CN.md +227 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +108 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/auth.d.ts +3 -0
  7. package/dist/commands/auth.js +76 -0
  8. package/dist/commands/auth.js.map +1 -0
  9. package/dist/commands/pulse.d.ts +3 -0
  10. package/dist/commands/pulse.js +170 -0
  11. package/dist/commands/pulse.js.map +1 -0
  12. package/dist/commands/self-update.d.ts +2 -0
  13. package/dist/commands/self-update.js +20 -0
  14. package/dist/commands/self-update.js.map +1 -0
  15. package/dist/lib/api.d.ts +15 -0
  16. package/dist/lib/api.js +48 -0
  17. package/dist/lib/api.js.map +1 -0
  18. package/dist/lib/auth.d.ts +4 -0
  19. package/dist/lib/auth.js +51 -0
  20. package/dist/lib/auth.js.map +1 -0
  21. package/dist/lib/config.d.ts +7 -0
  22. package/dist/lib/config.js +59 -0
  23. package/dist/lib/config.js.map +1 -0
  24. package/dist/lib/constants.d.ts +5 -0
  25. package/dist/lib/constants.js +6 -0
  26. package/dist/lib/constants.js.map +1 -0
  27. package/dist/lib/errors.d.ts +9 -0
  28. package/dist/lib/errors.js +19 -0
  29. package/dist/lib/errors.js.map +1 -0
  30. package/dist/lib/json.d.ts +1 -0
  31. package/dist/lib/json.js +9 -0
  32. package/dist/lib/json.js.map +1 -0
  33. package/dist/lib/open.d.ts +1 -0
  34. package/dist/lib/open.js +23 -0
  35. package/dist/lib/open.js.map +1 -0
  36. package/dist/lib/output.d.ts +16 -0
  37. package/dist/lib/output.js +104 -0
  38. package/dist/lib/output.js.map +1 -0
  39. package/dist/lib/pulse.d.ts +9 -0
  40. package/dist/lib/pulse.js +140 -0
  41. package/dist/lib/pulse.js.map +1 -0
  42. package/dist/lib/update.d.ts +7 -0
  43. package/dist/lib/update.js +115 -0
  44. package/dist/lib/update.js.map +1 -0
  45. package/dist/tests/api.test.d.ts +1 -0
  46. package/dist/tests/api.test.js +23 -0
  47. package/dist/tests/api.test.js.map +1 -0
  48. package/dist/tests/auth.test.d.ts +1 -0
  49. package/dist/tests/auth.test.js +10 -0
  50. package/dist/tests/auth.test.js.map +1 -0
  51. package/dist/tests/config.test.d.ts +1 -0
  52. package/dist/tests/config.test.js +26 -0
  53. package/dist/tests/config.test.js.map +1 -0
  54. package/dist/tests/pulse-render.test.d.ts +1 -0
  55. package/dist/tests/pulse-render.test.js +63 -0
  56. package/dist/tests/pulse-render.test.js.map +1 -0
  57. package/dist/tests/update.test.d.ts +1 -0
  58. package/dist/tests/update.test.js +20 -0
  59. package/dist/tests/update.test.js.map +1 -0
  60. package/dist/types.d.ts +51 -0
  61. package/dist/types.js +2 -0
  62. package/dist/types.js.map +1 -0
  63. package/package.json +32 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { PulseDetail, PulseListResponse } from "../types.js";
2
+ export interface RenderPulseListOptions {
3
+ json: boolean;
4
+ sourceUrlsById?: Map<number, string | null>;
5
+ }
6
+ export declare function extractTwitterSourceUrls(posts: Array<Record<string, unknown>>): string[];
7
+ export declare function renderPulseList(response: PulseListResponse, options: RenderPulseListOptions): void;
8
+ export declare function renderPulseCategories(items: string[], json: boolean): void;
9
+ export declare function renderPulseDetail(item: PulseDetail, json: boolean): void;
@@ -0,0 +1,140 @@
1
+ import { formatValue, highlightCategory, highlightDate, highlightDelta, highlightHeat, highlightLink, highlightMuted, highlightTitle, printInfo, printJson, printTable, } from "./output.js";
2
+ const SUMMARY_LIMIT = 96;
3
+ function truncateText(value, maxLength) {
4
+ if (value.length <= maxLength)
5
+ return value;
6
+ return `${value.slice(0, maxLength - 1).trimEnd()}…`;
7
+ }
8
+ function formatDate(isoDate) {
9
+ const date = new Date(isoDate);
10
+ if (Number.isNaN(date.getTime()))
11
+ return isoDate;
12
+ return date.toISOString().slice(0, 10);
13
+ }
14
+ function formatHeat(value) {
15
+ if (value === null)
16
+ return "-";
17
+ return value.toFixed(2);
18
+ }
19
+ function formatDelta(value) {
20
+ if (value === null)
21
+ return { text: "-", positive: null };
22
+ const positive = value >= 0;
23
+ const sign = positive ? "+" : "";
24
+ return { text: `${sign}${value.toFixed(2)}`, positive };
25
+ }
26
+ function normalizeUrl(url) {
27
+ return url.replace(/\/$/, "");
28
+ }
29
+ function sortByShorterLength(urls) {
30
+ return [...urls].sort((left, right) => left.length - right.length);
31
+ }
32
+ export function extractTwitterSourceUrls(posts) {
33
+ const urlRegex = /https?:\/\/(?:x|twitter)\.com\/[^\s"')\]}]+/gi;
34
+ const discovered = new Set();
35
+ for (const post of posts) {
36
+ const payload = JSON.stringify(post);
37
+ const matches = payload.match(urlRegex);
38
+ if (!matches)
39
+ continue;
40
+ for (const rawUrl of matches) {
41
+ discovered.add(normalizeUrl(rawUrl));
42
+ }
43
+ }
44
+ return sortByShorterLength([...discovered]);
45
+ }
46
+ function formatSourceValue(itemId, sourceUrlsById) {
47
+ if (!sourceUrlsById)
48
+ return "-";
49
+ const source = sourceUrlsById.get(itemId) ?? null;
50
+ if (!source)
51
+ return "-";
52
+ return source.length > 42 ? `${source.slice(0, 39)}…` : source;
53
+ }
54
+ export function renderPulseList(response, options) {
55
+ if (options.json) {
56
+ printJson(response);
57
+ return;
58
+ }
59
+ const items = response.data;
60
+ if (items.length === 0) {
61
+ printInfo("No pulses found.");
62
+ return;
63
+ }
64
+ printTable(["ID", "Category", "Locale", "Date", "Heat", "Delta", "Source", "Title", "Summary"], items.map((item) => {
65
+ const delta = formatDelta(item.heatDelta);
66
+ return [
67
+ String(item.id),
68
+ highlightCategory(item.category),
69
+ item.locale,
70
+ highlightDate(formatDate(item.createdAt)),
71
+ highlightHeat(formatHeat(item.heatScore)),
72
+ highlightDelta(delta.text, delta.positive),
73
+ highlightLink(formatSourceValue(item.id, options.sourceUrlsById)),
74
+ highlightTitle(truncateText(item.title, 52)),
75
+ truncateText(item.content.replace(/\s+/g, " ").trim(), SUMMARY_LIMIT),
76
+ ];
77
+ }));
78
+ const totalPages = Math.max(1, Math.ceil(response.pagination.total / response.pagination.pageSize));
79
+ const hasPrev = response.pagination.page > 1;
80
+ const hasNext = response.pagination.page < totalPages;
81
+ const paginationSummary = [
82
+ `Page ${response.pagination.page}/${totalPages}`,
83
+ `Total ${response.pagination.total}`,
84
+ `PageSize ${response.pagination.pageSize}`,
85
+ `Prev ${hasPrev ? "yes" : "no"}`,
86
+ `Next ${hasNext ? "yes" : "no"}`,
87
+ ].join(" · ");
88
+ printInfo("");
89
+ printInfo(highlightMuted(paginationSummary));
90
+ if (hasNext) {
91
+ printInfo(highlightMuted(`Tip: atypica pulse list --page ${response.pagination.page + 1} --limit ${response.pagination.pageSize}`));
92
+ }
93
+ }
94
+ export function renderPulseCategories(items, json) {
95
+ if (json) {
96
+ printJson(items);
97
+ return;
98
+ }
99
+ if (items.length === 0) {
100
+ printInfo("No categories found.");
101
+ return;
102
+ }
103
+ for (const item of items) {
104
+ printInfo(item);
105
+ }
106
+ }
107
+ export function renderPulseDetail(item, json) {
108
+ if (json) {
109
+ printJson(item);
110
+ return;
111
+ }
112
+ printInfo(`ID: ${item.id}`);
113
+ printInfo(`Title: ${highlightTitle(item.title)}`);
114
+ printInfo(`Category: ${highlightCategory(item.category)}`);
115
+ printInfo(`Locale: ${item.locale}`);
116
+ printInfo(`Heat Score: ${highlightHeat(formatValue(item.heatScore))}`);
117
+ const delta = formatDelta(item.heatDelta);
118
+ printInfo(`Heat Delta: ${highlightDelta(delta.text, delta.positive)}`);
119
+ printInfo(`Created At: ${highlightDate(item.createdAt)}`);
120
+ printInfo("");
121
+ printInfo("Content:");
122
+ printInfo(item.content.trim());
123
+ const sourceUrls = extractTwitterSourceUrls(item.posts);
124
+ if (sourceUrls.length > 0) {
125
+ printInfo("");
126
+ printInfo(`Source URLs (${sourceUrls.length}):`);
127
+ sourceUrls.forEach((url, index) => {
128
+ printInfo(`${index + 1}. ${highlightLink(url)}`);
129
+ });
130
+ }
131
+ if (item.posts.length > 0) {
132
+ printInfo("");
133
+ printInfo(`Posts (${item.posts.length}):`);
134
+ item.posts.forEach((post, index) => {
135
+ const content = typeof post.content === "string" ? post.content : JSON.stringify(post);
136
+ printInfo(`${index + 1}. ${content}`);
137
+ });
138
+ }
139
+ }
140
+ //# sourceMappingURL=pulse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pulse.js","sourceRoot":"","sources":["../../src/lib/pulse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { CliConfig, PackageManagerInfo } from "../types.js";
2
+ export declare function detectPackageManager(userAgent?: string | undefined): PackageManagerInfo;
3
+ export declare function shouldCheckForUpdates(config: CliConfig): boolean;
4
+ export declare function compareVersions(current: string, latest: string): number;
5
+ export declare function fetchLatestVersion(packageName?: string): Promise<string>;
6
+ export declare function maybeNotifyForUpdates(currentVersion: string, config: CliConfig): Promise<void>;
7
+ export declare function runSelfUpdate(execute: boolean): Promise<void>;
@@ -0,0 +1,115 @@
1
+ import { spawn } from "node:child_process";
2
+ import { PACKAGE_NAME, UPDATE_CHECK_INTERVAL_MS } from "./constants.js";
3
+ import { loadConfig, saveConfig } from "./config.js";
4
+ import { printInfo, printWarning } from "./output.js";
5
+ export function detectPackageManager(userAgent = process.env.npm_config_user_agent) {
6
+ if (userAgent?.startsWith("pnpm/")) {
7
+ return {
8
+ name: "pnpm",
9
+ installCommand: `pnpm add -g ${PACKAGE_NAME}@latest`,
10
+ runCommand: ["pnpm", "add", "-g", `${PACKAGE_NAME}@latest`],
11
+ };
12
+ }
13
+ if (userAgent?.startsWith("yarn/")) {
14
+ return {
15
+ name: "yarn",
16
+ installCommand: `yarn global add ${PACKAGE_NAME}@latest`,
17
+ runCommand: ["yarn", "global", "add", `${PACKAGE_NAME}@latest`],
18
+ };
19
+ }
20
+ if (userAgent?.startsWith("bun/")) {
21
+ return {
22
+ name: "bun",
23
+ installCommand: `bun add -g ${PACKAGE_NAME}@latest`,
24
+ runCommand: ["bun", "add", "-g", `${PACKAGE_NAME}@latest`],
25
+ };
26
+ }
27
+ if (userAgent?.startsWith("npm/")) {
28
+ return {
29
+ name: "npm",
30
+ installCommand: `npm install -g ${PACKAGE_NAME}@latest`,
31
+ runCommand: ["npm", "install", "-g", `${PACKAGE_NAME}@latest`],
32
+ };
33
+ }
34
+ return {
35
+ name: "unknown",
36
+ installCommand: `npm install -g ${PACKAGE_NAME}@latest`,
37
+ };
38
+ }
39
+ export function shouldCheckForUpdates(config) {
40
+ if (config.updateCheck === false)
41
+ return false;
42
+ if (!config.lastUpdateCheckAt)
43
+ return true;
44
+ const lastChecked = new Date(config.lastUpdateCheckAt).getTime();
45
+ return Number.isNaN(lastChecked) || Date.now() - lastChecked >= UPDATE_CHECK_INTERVAL_MS;
46
+ }
47
+ export function compareVersions(current, latest) {
48
+ const normalize = (value) => value.replace(/^v/, "").split(".").map((part) => Number(part));
49
+ const a = normalize(current);
50
+ const b = normalize(latest);
51
+ const length = Math.max(a.length, b.length);
52
+ for (let index = 0; index < length; index += 1) {
53
+ const left = a[index] ?? 0;
54
+ const right = b[index] ?? 0;
55
+ if (left > right)
56
+ return 1;
57
+ if (left < right)
58
+ return -1;
59
+ }
60
+ return 0;
61
+ }
62
+ export async function fetchLatestVersion(packageName = PACKAGE_NAME) {
63
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, {
64
+ headers: { Accept: "application/json" },
65
+ });
66
+ if (!response.ok) {
67
+ throw new Error(`Failed to check npm registry: ${response.status}`);
68
+ }
69
+ const payload = (await response.json());
70
+ if (!payload.version) {
71
+ throw new Error("npm registry response missing version");
72
+ }
73
+ return payload.version;
74
+ }
75
+ export async function maybeNotifyForUpdates(currentVersion, config) {
76
+ if (!shouldCheckForUpdates(config))
77
+ return;
78
+ try {
79
+ const latestVersion = await fetchLatestVersion();
80
+ const nextConfig = {
81
+ ...loadConfig(),
82
+ lastUpdateCheckAt: new Date().toISOString(),
83
+ };
84
+ saveConfig(nextConfig);
85
+ if (compareVersions(currentVersion, latestVersion) < 0 && latestVersion !== config.skippedVersion) {
86
+ const pkgManager = detectPackageManager();
87
+ printWarning(`A newer version of atypica CLI is available (${currentVersion} -> ${latestVersion}). Run \`${pkgManager.installCommand}\` or \`atypica self-update\`.`);
88
+ }
89
+ }
90
+ catch {
91
+ // Ignore update failures during normal command execution.
92
+ }
93
+ }
94
+ export async function runSelfUpdate(execute) {
95
+ const pkgManager = detectPackageManager();
96
+ if (!execute || !pkgManager.runCommand) {
97
+ printInfo(`Upgrade command: ${pkgManager.installCommand}`);
98
+ return;
99
+ }
100
+ const [command, ...args] = pkgManager.runCommand;
101
+ await new Promise((resolve, reject) => {
102
+ const child = spawn(command, args, {
103
+ stdio: "inherit",
104
+ });
105
+ child.once("error", reject);
106
+ child.once("exit", (code) => {
107
+ if (code === 0) {
108
+ resolve();
109
+ return;
110
+ }
111
+ reject(new Error(`Self-update command exited with code ${code ?? "unknown"}`));
112
+ });
113
+ });
114
+ }
115
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/lib/update.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { ApiClient } from "../lib/api.js";
4
+ import { HttpError } from "../lib/errors.js";
5
+ test("ApiClient converts error payload into HttpError", async () => {
6
+ const originalFetch = globalThis.fetch;
7
+ globalThis.fetch = async () => new Response(JSON.stringify({ success: false, message: "Unauthorized: Invalid API key" }), {
8
+ status: 401,
9
+ headers: { "content-type": "application/json" },
10
+ });
11
+ const client = new ApiClient({
12
+ apiKey: "atypica_test",
13
+ baseUrl: "https://example.com/api",
14
+ });
15
+ await assert.rejects(() => client.getPulse("1"), (error) => {
16
+ assert.ok(error instanceof HttpError);
17
+ assert.equal(error.status, 401);
18
+ assert.equal(error.message, "Unauthorized: Invalid API key");
19
+ return true;
20
+ });
21
+ globalThis.fetch = originalFetch;
22
+ });
23
+ //# sourceMappingURL=api.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.test.js","sourceRoot":"","sources":["../../src/tests/api.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,IAAI,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;IACjE,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC;IACvC,UAAU,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE,CAC5B,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC,EAAE;QACzF,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;KAChD,CAAC,CAAC;IAEL,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,MAAM,EAAE,cAAc;QACtB,OAAO,EAAE,yBAAyB;KACnC,CAAC,CAAC;IAEH,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,KAAc,EAAE,EAAE;QAClE,MAAM,CAAC,EAAE,CAAC,KAAK,YAAY,SAAS,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,+BAA+B,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,KAAK,GAAG,aAAa,CAAC;AACnC,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { validateApiKeyFormat } from "../lib/auth.js";
4
+ test("validateApiKeyFormat accepts atypica-prefixed keys", () => {
5
+ assert.doesNotThrow(() => validateApiKeyFormat("atypica_123"));
6
+ });
7
+ test("validateApiKeyFormat rejects invalid keys", () => {
8
+ assert.throws(() => validateApiKeyFormat("wrong_123"));
9
+ });
10
+ //# sourceMappingURL=auth.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.test.js","sourceRoot":"","sources":["../../src/tests/auth.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,IAAI,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAC9D,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,aAAa,CAAC,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACrD,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ test("resolveConfig prefers env values over saved config", async () => {
7
+ const tempDir = mkdtempSync(join(tmpdir(), "atypica-cli-config-"));
8
+ process.env.XDG_CONFIG_HOME = tempDir;
9
+ process.env.ATYPICA_API_KEY = "atypica_env_key";
10
+ process.env.ATYPICA_BASE_URL = "https://example.com/api";
11
+ const { saveConfig, resolveConfig } = await import("../lib/config.js");
12
+ saveConfig({
13
+ apiKey: "atypica_file_key",
14
+ baseUrl: "https://file.example/api",
15
+ updateCheck: false,
16
+ });
17
+ const config = resolveConfig();
18
+ assert.equal(config.apiKey, "atypica_env_key");
19
+ assert.equal(config.baseUrl, "https://example.com/api");
20
+ assert.equal(config.updateCheck, false);
21
+ delete process.env.XDG_CONFIG_HOME;
22
+ delete process.env.ATYPICA_API_KEY;
23
+ delete process.env.ATYPICA_BASE_URL;
24
+ rmSync(tempDir, { recursive: true, force: true });
25
+ });
26
+ //# sourceMappingURL=config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/tests/config.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,IAAI,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;IACpE,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,OAAO,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,iBAAiB,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,yBAAyB,CAAC;IAEzD,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;IACvE,UAAU,CAAC;QACT,MAAM,EAAE,kBAAkB;QAC1B,OAAO,EAAE,0BAA0B;QACnC,WAAW,EAAE,KAAK;KACnB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,yBAAyB,CAAC,CAAC;IACxD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAExC,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACpC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,63 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { extractTwitterSourceUrls, renderPulseList } from "../lib/pulse.js";
4
+ function captureStdout(run) {
5
+ const chunks = [];
6
+ const originalWrite = process.stdout.write.bind(process.stdout);
7
+ process.stdout.write = ((chunk) => {
8
+ chunks.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8"));
9
+ return true;
10
+ });
11
+ try {
12
+ run();
13
+ return chunks.join("");
14
+ }
15
+ finally {
16
+ process.stdout.write = originalWrite;
17
+ }
18
+ }
19
+ test("extractTwitterSourceUrls deduplicates x/twitter URLs", () => {
20
+ const urls = extractTwitterSourceUrls([
21
+ {
22
+ url: "https://x.com/user/status/123",
23
+ content: "Primary source",
24
+ },
25
+ {
26
+ payload: {
27
+ source: "https://twitter.com/other/status/456",
28
+ repeated: "https://x.com/user/status/123/",
29
+ },
30
+ },
31
+ ]);
32
+ assert.deepEqual(urls, ["https://x.com/user/status/123", "https://twitter.com/other/status/456"]);
33
+ });
34
+ test("renderPulseList JSON mode keeps pagination shape", () => {
35
+ const response = {
36
+ success: true,
37
+ data: [
38
+ {
39
+ id: 1,
40
+ title: "Sample title",
41
+ content: "Detailed content",
42
+ category: "AI Tech",
43
+ locale: "en-US",
44
+ heatScore: 100,
45
+ heatDelta: 5,
46
+ createdAt: "2026-04-12T00:00:00.000Z",
47
+ },
48
+ ],
49
+ pagination: {
50
+ page: 2,
51
+ pageSize: 10,
52
+ total: 25,
53
+ },
54
+ };
55
+ const output = captureStdout(() => {
56
+ renderPulseList(response, { json: true });
57
+ });
58
+ const parsed = JSON.parse(output);
59
+ assert.equal(parsed.pagination.page, 2);
60
+ assert.equal(parsed.pagination.total, 25);
61
+ assert.equal(parsed.data[0]?.id, 1);
62
+ });
63
+ //# sourceMappingURL=pulse-render.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pulse-render.test.js","sourceRoot":"","sources":["../../src/tests/pulse-render.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAG5E,SAAS,aAAa,CAAC,GAAe;IACpC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,KAA0B,EAAE,EAAE;QACrD,MAAM,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACrF,OAAO,IAAI,CAAC;IACd,CAAC,CAAgC,CAAC;IAElC,IAAI,CAAC;QACH,GAAG,EAAE,CAAC;QACN,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,aAAa,CAAC;IACvC,CAAC;AACH,CAAC;AAED,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,MAAM,IAAI,GAAG,wBAAwB,CAAC;QACpC;YACE,GAAG,EAAE,+BAA+B;YACpC,OAAO,EAAE,gBAAgB;SAC1B;QACD;YACE,OAAO,EAAE;gBACP,MAAM,EAAE,sCAAsC;gBAC9C,QAAQ,EAAE,gCAAgC;aAC3C;SACF;KACF,CAAC,CAAC;IAEH,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,+BAA+B,EAAE,sCAAsC,CAAC,CAAC,CAAC;AACpG,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAC5D,MAAM,QAAQ,GAAsB;QAClC,OAAO,EAAE,IAAI;QACb,IAAI,EAAE;YACJ;gBACE,EAAE,EAAE,CAAC;gBACL,KAAK,EAAE,cAAc;gBACrB,OAAO,EAAE,kBAAkB;gBAC3B,QAAQ,EAAE,SAAS;gBACnB,MAAM,EAAE,OAAO;gBACf,SAAS,EAAE,GAAG;gBACd,SAAS,EAAE,CAAC;gBACZ,SAAS,EAAE,0BAA0B;aACtC;SACF;QACD,UAAU,EAAE;YACV,IAAI,EAAE,CAAC;YACP,QAAQ,EAAE,EAAE;YACZ,KAAK,EAAE,EAAE;SACV;KACF,CAAC;IAEF,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,EAAE;QAChC,eAAe,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAsB,CAAC;IACvD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { compareVersions, detectPackageManager, shouldCheckForUpdates } from "../lib/update.js";
4
+ test("compareVersions detects newer releases", () => {
5
+ assert.equal(compareVersions("0.1.0", "0.1.1"), -1);
6
+ assert.equal(compareVersions("0.1.1", "0.1.0"), 1);
7
+ assert.equal(compareVersions("0.1.0", "0.1.0"), 0);
8
+ });
9
+ test("detectPackageManager supports pnpm", () => {
10
+ const info = detectPackageManager("pnpm/10.24.0 node/v22.15.1 darwin arm64");
11
+ assert.equal(info.name, "pnpm");
12
+ assert.match(info.installCommand, /pnpm add -g/);
13
+ });
14
+ test("shouldCheckForUpdates skips fresh timestamps", () => {
15
+ assert.equal(shouldCheckForUpdates({
16
+ updateCheck: true,
17
+ lastUpdateCheckAt: new Date().toISOString(),
18
+ }), false);
19
+ });
20
+ //# sourceMappingURL=update.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.test.js","sourceRoot":"","sources":["../../src/tests/update.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEhG,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;IAClD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oCAAoC,EAAE,GAAG,EAAE;IAC9C,MAAM,IAAI,GAAG,oBAAoB,CAAC,yCAAyC,CAAC,CAAC;IAC7E,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,CAAC,KAAK,CACV,qBAAqB,CAAC;QACpB,WAAW,EAAE,IAAI;QACjB,iBAAiB,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KAC5C,CAAC,EACF,KAAK,CACN,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,51 @@
1
+ export interface CliContext {
2
+ json: boolean;
3
+ updateCheck: boolean;
4
+ }
5
+ export interface CliConfig {
6
+ apiKey?: string;
7
+ baseUrl?: string;
8
+ updateCheck?: boolean;
9
+ lastUpdateCheckAt?: string;
10
+ skippedVersion?: string;
11
+ }
12
+ export interface PulseListItem {
13
+ id: number;
14
+ title: string;
15
+ content: string;
16
+ category: string;
17
+ locale: "en-US";
18
+ heatScore: number | null;
19
+ heatDelta: number | null;
20
+ createdAt: string;
21
+ }
22
+ export interface PulseDetail extends PulseListItem {
23
+ posts: Array<Record<string, unknown>>;
24
+ }
25
+ export interface PulsePagination {
26
+ page: number;
27
+ pageSize: number;
28
+ total: number;
29
+ }
30
+ export interface PulseListResponse {
31
+ success: boolean;
32
+ data: PulseListItem[];
33
+ pagination: PulsePagination;
34
+ }
35
+ export interface PulseCategoriesResponse {
36
+ success: boolean;
37
+ data: string[];
38
+ }
39
+ export interface PulseDetailResponse {
40
+ success: boolean;
41
+ data: PulseDetail;
42
+ }
43
+ export interface ErrorResponse {
44
+ success: false;
45
+ message: string;
46
+ }
47
+ export interface PackageManagerInfo {
48
+ name: "npm" | "pnpm" | "yarn" | "bun" | "unknown";
49
+ installCommand: string;
50
+ runCommand?: string[];
51
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@atypica-ai/cli",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "CLI for atypica Pulse APIs",
5
+ "type": "module",
6
+ "bin": {
7
+ "atypica": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc -p tsconfig.json",
18
+ "dev": "npm run build && node dist/cli.js",
19
+ "install:local": "/bin/zsh -lc 'mkdir -p \"$HOME/.npm-global\" && npm run build && npm install -g --prefix \"$HOME/.npm-global\" . && if ! grep -Fq \"export PATH=\\\"$HOME/.npm-global/bin:$PATH\\\"\" \"$HOME/.zshrc\" 2>/dev/null; then printf \"\\nexport PATH=\\\"$HOME/.npm-global/bin:$PATH\\\"\\n\" >> \"$HOME/.zshrc\"; fi'",
20
+ "test": "node --test dist/tests/*.test.js",
21
+ "test:src": "npm run build && npm run test",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public",
26
+ "tag": "alpha"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^22.19.1",
30
+ "typescript": "^5.9.3"
31
+ }
32
+ }