@doist/twist-cli 1.0.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 (110) hide show
  1. package/README.md +76 -0
  2. package/dist/__tests__/auth.test.d.ts +2 -0
  3. package/dist/__tests__/auth.test.d.ts.map +1 -0
  4. package/dist/__tests__/auth.test.js +300 -0
  5. package/dist/__tests__/auth.test.js.map +1 -0
  6. package/dist/__tests__/lib/dates.test.d.ts +2 -0
  7. package/dist/__tests__/lib/dates.test.d.ts.map +1 -0
  8. package/dist/__tests__/lib/dates.test.js +58 -0
  9. package/dist/__tests__/lib/dates.test.js.map +1 -0
  10. package/dist/__tests__/lib/refs.test.d.ts +2 -0
  11. package/dist/__tests__/lib/refs.test.d.ts.map +1 -0
  12. package/dist/__tests__/lib/refs.test.js +121 -0
  13. package/dist/__tests__/lib/refs.test.js.map +1 -0
  14. package/dist/__tests__/spinner.test.d.ts +2 -0
  15. package/dist/__tests__/spinner.test.d.ts.map +1 -0
  16. package/dist/__tests__/spinner.test.js +153 -0
  17. package/dist/__tests__/spinner.test.js.map +1 -0
  18. package/dist/commands/auth.d.ts +3 -0
  19. package/dist/commands/auth.d.ts.map +1 -0
  20. package/dist/commands/auth.js +93 -0
  21. package/dist/commands/auth.js.map +1 -0
  22. package/dist/commands/channel.d.ts +3 -0
  23. package/dist/commands/channel.d.ts.map +1 -0
  24. package/dist/commands/channel.js +49 -0
  25. package/dist/commands/channel.js.map +1 -0
  26. package/dist/commands/inbox.d.ts +3 -0
  27. package/dist/commands/inbox.d.ts.map +1 -0
  28. package/dist/commands/inbox.js +121 -0
  29. package/dist/commands/inbox.js.map +1 -0
  30. package/dist/commands/msg.d.ts +3 -0
  31. package/dist/commands/msg.d.ts.map +1 -0
  32. package/dist/commands/msg.js +205 -0
  33. package/dist/commands/msg.js.map +1 -0
  34. package/dist/commands/react.d.ts +3 -0
  35. package/dist/commands/react.d.ts.map +1 -0
  36. package/dist/commands/react.js +100 -0
  37. package/dist/commands/react.js.map +1 -0
  38. package/dist/commands/search.d.ts +3 -0
  39. package/dist/commands/search.d.ts.map +1 -0
  40. package/dist/commands/search.js +134 -0
  41. package/dist/commands/search.js.map +1 -0
  42. package/dist/commands/thread.d.ts +3 -0
  43. package/dist/commands/thread.d.ts.map +1 -0
  44. package/dist/commands/thread.js +219 -0
  45. package/dist/commands/thread.js.map +1 -0
  46. package/dist/commands/user.d.ts +3 -0
  47. package/dist/commands/user.d.ts.map +1 -0
  48. package/dist/commands/user.js +67 -0
  49. package/dist/commands/user.js.map +1 -0
  50. package/dist/commands/workspace.d.ts +3 -0
  51. package/dist/commands/workspace.d.ts.map +1 -0
  52. package/dist/commands/workspace.js +48 -0
  53. package/dist/commands/workspace.js.map +1 -0
  54. package/dist/index.d.ts +3 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +36 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/lib/api.d.ts +10 -0
  59. package/dist/lib/api.d.ts.map +1 -0
  60. package/dist/lib/api.js +143 -0
  61. package/dist/lib/api.js.map +1 -0
  62. package/dist/lib/auth.d.ts +4 -0
  63. package/dist/lib/auth.d.ts.map +1 -0
  64. package/dist/lib/auth.js +40 -0
  65. package/dist/lib/auth.js.map +1 -0
  66. package/dist/lib/config.d.ts +9 -0
  67. package/dist/lib/config.d.ts.map +1 -0
  68. package/dist/lib/config.js +26 -0
  69. package/dist/lib/config.js.map +1 -0
  70. package/dist/lib/dates.d.ts +3 -0
  71. package/dist/lib/dates.d.ts.map +1 -0
  72. package/dist/lib/dates.js +44 -0
  73. package/dist/lib/dates.js.map +1 -0
  74. package/dist/lib/input.d.ts +3 -0
  75. package/dist/lib/input.d.ts.map +1 -0
  76. package/dist/lib/input.js +52 -0
  77. package/dist/lib/input.js.map +1 -0
  78. package/dist/lib/markdown.d.ts +2 -0
  79. package/dist/lib/markdown.d.ts.map +1 -0
  80. package/dist/lib/markdown.js +12 -0
  81. package/dist/lib/markdown.js.map +1 -0
  82. package/dist/lib/oauth-server.d.ts +13 -0
  83. package/dist/lib/oauth-server.d.ts.map +1 -0
  84. package/dist/lib/oauth-server.js +168 -0
  85. package/dist/lib/oauth-server.js.map +1 -0
  86. package/dist/lib/oauth.d.ts +28 -0
  87. package/dist/lib/oauth.d.ts.map +1 -0
  88. package/dist/lib/oauth.js +125 -0
  89. package/dist/lib/oauth.js.map +1 -0
  90. package/dist/lib/output.d.ts +22 -0
  91. package/dist/lib/output.d.ts.map +1 -0
  92. package/dist/lib/output.js +104 -0
  93. package/dist/lib/output.js.map +1 -0
  94. package/dist/lib/pkce.d.ts +16 -0
  95. package/dist/lib/pkce.d.ts.map +1 -0
  96. package/dist/lib/pkce.js +35 -0
  97. package/dist/lib/pkce.js.map +1 -0
  98. package/dist/lib/refs.d.ts +29 -0
  99. package/dist/lib/refs.d.ts.map +1 -0
  100. package/dist/lib/refs.js +168 -0
  101. package/dist/lib/refs.js.map +1 -0
  102. package/dist/lib/search-api.d.ts +25 -0
  103. package/dist/lib/search-api.d.ts.map +1 -0
  104. package/dist/lib/search-api.js +85 -0
  105. package/dist/lib/search-api.js.map +1 -0
  106. package/dist/lib/spinner.d.ts +20 -0
  107. package/dist/lib/spinner.d.ts.map +1 -0
  108. package/dist/lib/spinner.js +70 -0
  109. package/dist/lib/spinner.js.map +1 -0
  110. package/package.json +71 -0
@@ -0,0 +1,40 @@
1
+ import { unlink } from 'node:fs/promises';
2
+ import { getConfig, getConfigPath, setConfig, updateConfig } from './config.js';
3
+ export async function getApiToken() {
4
+ const envToken = process.env.TWIST_API_TOKEN;
5
+ if (envToken) {
6
+ return envToken;
7
+ }
8
+ const config = await getConfig();
9
+ if (config.token) {
10
+ return config.token;
11
+ }
12
+ throw new Error(`No API token found. Set TWIST_API_TOKEN environment variable or add "token" to ${getConfigPath()}`);
13
+ }
14
+ export async function saveApiToken(token) {
15
+ // Validate token (non-empty, reasonable length)
16
+ if (!token || token.trim().length < 10) {
17
+ throw new Error('Invalid token: Token must be at least 10 characters');
18
+ }
19
+ // Update config with new token using the existing config system
20
+ await updateConfig({ token: token.trim() });
21
+ }
22
+ export async function clearApiToken() {
23
+ const config = await getConfig();
24
+ // Remove the token from config
25
+ delete config.token;
26
+ // If config is empty after removing the token, delete the config file
27
+ if (Object.keys(config).length === 0) {
28
+ try {
29
+ await unlink(getConfigPath());
30
+ }
31
+ catch {
32
+ // Config file doesn't exist, nothing to delete
33
+ }
34
+ }
35
+ else {
36
+ // Otherwise, save the config without the token
37
+ await setConfig(config);
38
+ }
39
+ }
40
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/E,MAAM,CAAC,KAAK,UAAU,WAAW;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;IAC5C,IAAI,QAAQ,EAAE,CAAC;QACX,OAAO,QAAQ,CAAA;IACnB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAA;IAChC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,MAAM,CAAC,KAAK,CAAA;IACvB,CAAC;IAED,MAAM,IAAI,KAAK,CACX,kFAAkF,aAAa,EAAE,EAAE,CACtG,CAAA;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa;IAC5C,gDAAgD;IAChD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IAC1E,CAAC;IAED,gEAAgE;IAChE,MAAM,YAAY,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IAC/B,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAA;IAEhC,+BAA+B;IAC/B,OAAO,MAAM,CAAC,KAAK,CAAA;IAEnB,sEAAsE;IACtE,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,IAAI,CAAC;YACD,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,CAAA;QACjC,CAAC;QAAC,MAAM,CAAC;YACL,+CAA+C;QACnD,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,+CAA+C;QAC/C,MAAM,SAAS,CAAC,MAAM,CAAC,CAAA;IAC3B,CAAC;AACL,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface Config {
2
+ token?: string;
3
+ currentWorkspace?: number;
4
+ }
5
+ export declare function getConfig(): Promise<Config>;
6
+ export declare function setConfig(config: Config): Promise<void>;
7
+ export declare function updateConfig(updates: Partial<Config>): Promise<void>;
8
+ export declare function getConfigPath(): string;
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,MAAM;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAOjD;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1E;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
@@ -0,0 +1,26 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
4
+ const CONFIG_PATH = join(homedir(), '.config', 'twist-cli', 'config.json');
5
+ export async function getConfig() {
6
+ try {
7
+ const content = await readFile(CONFIG_PATH, 'utf-8');
8
+ return JSON.parse(content);
9
+ }
10
+ catch {
11
+ return {};
12
+ }
13
+ }
14
+ export async function setConfig(config) {
15
+ const dir = dirname(CONFIG_PATH);
16
+ await mkdir(dir, { recursive: true });
17
+ await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
18
+ }
19
+ export async function updateConfig(updates) {
20
+ const config = await getConfig();
21
+ await setConfig({ ...config, ...updates });
22
+ }
23
+ export function getConfigPath() {
24
+ return CONFIG_PATH;
25
+ }
26
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,CAAC,CAAA;AAO1E,MAAM,CAAC,KAAK,UAAU,SAAS;IAC3B,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QACpD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAW,CAAA;IACxC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAA;IACb,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc;IAC1C,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAChC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACrC,MAAM,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;AACjE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAwB;IACvD,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAA;IAChC,MAAM,SAAS,CAAC,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,EAAE,CAAC,CAAA;AAC9C,CAAC;AAED,MAAM,UAAU,aAAa;IACzB,OAAO,WAAW,CAAA;AACtB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function formatRelativeDate(date: Date | string): string;
2
+ export declare function parseDate(dateStr: string): Date | null;
3
+ //# sourceMappingURL=dates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.d.ts","sourceRoot":"","sources":["../../src/lib/dates.ts"],"names":[],"mappings":"AAKA,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAiC9D;AAaD,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAGtD"}
@@ -0,0 +1,44 @@
1
+ const SECOND = 1000;
2
+ const MINUTE = 60 * SECOND;
3
+ const HOUR = 60 * MINUTE;
4
+ const DAY = 24 * HOUR;
5
+ export function formatRelativeDate(date) {
6
+ const d = typeof date === 'string' ? new Date(date) : date;
7
+ const now = new Date();
8
+ const diff = now.getTime() - d.getTime();
9
+ if (diff < 0) {
10
+ return formatAbsoluteDate(d);
11
+ }
12
+ if (diff < MINUTE) {
13
+ return 'just now';
14
+ }
15
+ if (diff < HOUR) {
16
+ const mins = Math.floor(diff / MINUTE);
17
+ return `${mins} minute${mins === 1 ? '' : 's'} ago`;
18
+ }
19
+ if (diff < DAY) {
20
+ const hours = Math.floor(diff / HOUR);
21
+ return `${hours} hour${hours === 1 ? '' : 's'} ago`;
22
+ }
23
+ if (diff < 2 * DAY) {
24
+ return 'yesterday';
25
+ }
26
+ if (diff < 7 * DAY) {
27
+ const days = Math.floor(diff / DAY);
28
+ return `${days} days ago`;
29
+ }
30
+ return formatAbsoluteDate(d);
31
+ }
32
+ function formatAbsoluteDate(date) {
33
+ const now = new Date();
34
+ const sameYear = date.getFullYear() === now.getFullYear();
35
+ if (sameYear) {
36
+ return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
37
+ }
38
+ return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
39
+ }
40
+ export function parseDate(dateStr) {
41
+ const d = new Date(dateStr);
42
+ return Number.isNaN(d.getTime()) ? null : d;
43
+ }
44
+ //# sourceMappingURL=dates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.js","sourceRoot":"","sources":["../../src/lib/dates.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,IAAI,CAAA;AACnB,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,CAAA;AAC1B,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,CAAA;AACxB,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAA;AAErB,MAAM,UAAU,kBAAkB,CAAC,IAAmB;IAClD,MAAM,CAAC,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC1D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAA;IAExC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACX,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAA;IAChC,CAAC;IAED,IAAI,IAAI,GAAG,MAAM,EAAE,CAAC;QAChB,OAAO,UAAU,CAAA;IACrB,CAAC;IAED,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC;QACd,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,CAAA;QACtC,OAAO,GAAG,IAAI,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAA;IACvD,CAAC;IAED,IAAI,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;QACrC,OAAO,GAAG,KAAK,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAA;IACvD,CAAC;IAED,IAAI,IAAI,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC;QACjB,OAAO,WAAW,CAAA;IACtB,CAAC;IAED,IAAI,IAAI,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,CAAA;QACnC,OAAO,GAAG,IAAI,WAAW,CAAA;IAC7B,CAAC;IAED,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAA;AAChC,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAU;IAClC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,WAAW,EAAE,CAAA;IAEzD,IAAI,QAAQ,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAA;IAC/E,CAAC;IAED,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA;AAChG,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe;IACrC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAA;IAC3B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;AAC/C,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function readStdin(): Promise<string | null>;
2
+ export declare function openEditor(): Promise<string | null>;
3
+ //# sourceMappingURL=input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/lib/input.ts"],"names":[],"mappings":"AAKA,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkBxD;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgCzD"}
@@ -0,0 +1,52 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { readFile, unlink, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ export async function readStdin() {
6
+ if (process.stdin.isTTY) {
7
+ return null;
8
+ }
9
+ return new Promise((resolve) => {
10
+ let data = '';
11
+ process.stdin.setEncoding('utf8');
12
+ process.stdin.on('data', (chunk) => {
13
+ data += chunk;
14
+ });
15
+ process.stdin.on('end', () => {
16
+ resolve(data.trim() || null);
17
+ });
18
+ process.stdin.on('error', () => {
19
+ resolve(null);
20
+ });
21
+ });
22
+ }
23
+ export async function openEditor() {
24
+ const editor = process.env.EDITOR || process.env.VISUAL || 'vi';
25
+ const tmpFile = join(tmpdir(), `twist-cli-${Date.now()}.md`);
26
+ await writeFile(tmpFile, '');
27
+ return new Promise((resolve) => {
28
+ const child = spawn(editor, [tmpFile], {
29
+ stdio: 'inherit',
30
+ });
31
+ child.on('exit', async (code) => {
32
+ if (code !== 0) {
33
+ await unlink(tmpFile).catch(() => { });
34
+ resolve(null);
35
+ return;
36
+ }
37
+ try {
38
+ const content = await readFile(tmpFile, 'utf8');
39
+ await unlink(tmpFile).catch(() => { });
40
+ resolve(content.trim() || null);
41
+ }
42
+ catch {
43
+ resolve(null);
44
+ }
45
+ });
46
+ child.on('error', async () => {
47
+ await unlink(tmpFile).catch(() => { });
48
+ resolve(null);
49
+ });
50
+ });
51
+ }
52
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/lib/input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,CAAC,KAAK,UAAU,SAAS;IAC3B,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,IAAI,CAAA;IACf,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QACjC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/B,IAAI,IAAI,KAAK,CAAA;QACjB,CAAC,CAAC,CAAA;QACF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACzB,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,CAAA;QAChC,CAAC,CAAC,CAAA;QACF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAC3B,OAAO,CAAC,IAAI,CAAC,CAAA;QACjB,CAAC,CAAC,CAAA;IACN,CAAC,CAAC,CAAA;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAA;IAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAE5D,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IAE5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE;YACnC,KAAK,EAAE,SAAS;SACnB,CAAC,CAAA;QAEF,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC5B,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACb,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;gBACrC,OAAO,CAAC,IAAI,CAAC,CAAA;gBACb,OAAM;YACV,CAAC;YAED,IAAI,CAAC;gBACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;gBAC/C,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;gBACrC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,CAAA;YACnC,CAAC;YAAC,MAAM,CAAC;gBACL,OAAO,CAAC,IAAI,CAAC,CAAA;YACjB,CAAC;QACL,CAAC,CAAC,CAAA;QAEF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YACzB,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACrC,OAAO,CAAC,IAAI,CAAC,CAAA;QACjB,CAAC,CAAC,CAAA;IACN,CAAC,CAAC,CAAA;AACN,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function renderMarkdown(content: string): string;
2
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/lib/markdown.ts"],"names":[],"mappings":"AASA,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAItD"}
@@ -0,0 +1,12 @@
1
+ import { marked } from 'marked';
2
+ import { markedTerminal } from 'marked-terminal';
3
+ marked.use(markedTerminal());
4
+ function preprocessMentions(content) {
5
+ return content.replace(/\[([^\]]+)\]\((twist-mention:\/\/\d+)\)/g, '[@$1]($2)');
6
+ }
7
+ export function renderMarkdown(content) {
8
+ const processed = preprocessMentions(content);
9
+ const rendered = marked.parse(processed, { async: false });
10
+ return rendered.trimEnd();
11
+ }
12
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/lib/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAA;AAE5B,SAAS,kBAAkB,CAAC,OAAe;IACvC,OAAO,OAAO,CAAC,OAAO,CAAC,0CAA0C,EAAE,WAAW,CAAC,CAAA;AACnF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC1C,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;IAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAW,CAAA;IACpE,OAAO,QAAQ,CAAC,OAAO,EAAE,CAAA;AAC7B,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare const PORT = 8766;
2
+ export declare const OAUTH_REDIRECT_URI = "http://localhost:8766/callback";
3
+ interface CallbackResult {
4
+ code: string;
5
+ cleanup: () => void;
6
+ }
7
+ /**
8
+ * Start a local HTTP server to handle OAuth callback
9
+ * Returns a promise that resolves when the callback is received with valid state
10
+ */
11
+ export declare function startCallbackServer(expectedState: string): Promise<CallbackResult>;
12
+ export {};
13
+ //# sourceMappingURL=oauth-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-server.d.ts","sourceRoot":"","sources":["../../src/lib/oauth-server.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,IAAI,OAAO,CAAA;AACxB,eAAO,MAAM,kBAAkB,mCAAmC,CAAA;AAKlE,UAAU,cAAc;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,IAAI,CAAA;CACtB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA2DxF"}
@@ -0,0 +1,168 @@
1
+ import { createServer } from 'node:http';
2
+ import { parse } from 'node:url';
3
+ export const PORT = 8766;
4
+ export const OAUTH_REDIRECT_URI = 'http://localhost:8766/callback';
5
+ // 3 minute timeout for OAuth flow
6
+ const TIMEOUT_MS = 3 * 60 * 1000;
7
+ /**
8
+ * Start a local HTTP server to handle OAuth callback
9
+ * Returns a promise that resolves when the callback is received with valid state
10
+ */
11
+ export async function startCallbackServer(expectedState) {
12
+ return new Promise((resolve, reject) => {
13
+ let server = null;
14
+ let timeoutId = null;
15
+ let resolved = false;
16
+ const cleanup = () => {
17
+ if (resolved)
18
+ return;
19
+ resolved = true;
20
+ if (timeoutId) {
21
+ clearTimeout(timeoutId);
22
+ timeoutId = null;
23
+ }
24
+ if (server) {
25
+ server.close();
26
+ server = null;
27
+ }
28
+ };
29
+ // Set up timeout
30
+ timeoutId = setTimeout(() => {
31
+ cleanup();
32
+ reject(new Error('OAuth flow timed out. Please try again.'));
33
+ }, TIMEOUT_MS);
34
+ // Create HTTP server
35
+ server = createServer((req, res) => {
36
+ const url = parse(req.url || '', true);
37
+ if (url.pathname === '/callback') {
38
+ handleCallback(req, res, expectedState, resolve, reject, cleanup);
39
+ }
40
+ else {
41
+ // Handle other paths
42
+ res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
43
+ res.end(getNotFoundPage());
44
+ }
45
+ });
46
+ // Handle server errors
47
+ server.on('error', (error) => {
48
+ cleanup();
49
+ if (error.message.includes('EADDRINUSE')) {
50
+ reject(new Error(`Port ${PORT} is already in use. Please close any other applications using this port and try again.`));
51
+ }
52
+ else {
53
+ reject(new Error(`Server error: ${error.message}`));
54
+ }
55
+ });
56
+ // Start listening
57
+ server.listen(PORT, 'localhost', () => {
58
+ console.log(`OAuth callback server listening on ${OAUTH_REDIRECT_URI}`);
59
+ });
60
+ });
61
+ }
62
+ function handleCallback(req, res, expectedState, resolve, reject, cleanup) {
63
+ const url = parse(req.url || '', true);
64
+ const { code, state, error, error_description } = url.query;
65
+ // Check for OAuth errors first
66
+ if (error) {
67
+ const errorMsg = error_description ? String(error_description) : String(error);
68
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
69
+ res.end(getErrorPage(`OAuth Error: ${errorMsg}`));
70
+ cleanup();
71
+ reject(new Error(`OAuth authorization failed: ${errorMsg}`));
72
+ return;
73
+ }
74
+ // Validate state parameter (CSRF protection)
75
+ if (!state || String(state) !== expectedState) {
76
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
77
+ res.end(getErrorPage('Invalid state parameter. This may be a security issue.'));
78
+ cleanup();
79
+ reject(new Error('Invalid state parameter received. Possible CSRF attack.'));
80
+ return;
81
+ }
82
+ // Validate authorization code
83
+ if (!code || typeof code !== 'string') {
84
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
85
+ res.end(getErrorPage('No authorization code received.'));
86
+ cleanup();
87
+ reject(new Error('No authorization code received from OAuth server'));
88
+ return;
89
+ }
90
+ // Success!
91
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
92
+ res.end(getSuccessPage());
93
+ resolve({
94
+ code: String(code),
95
+ cleanup,
96
+ });
97
+ }
98
+ function getSuccessPage() {
99
+ return `
100
+ <!DOCTYPE html>
101
+ <html>
102
+ <head>
103
+ <meta charset="utf-8">
104
+ <title>Authorization Successful - Twist CLI</title>
105
+ <style>
106
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; text-align: center; padding: 50px; background: #f5f5f5; }
107
+ .container { max-width: 500px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
108
+ .success { color: #28a745; font-size: 24px; margin-bottom: 20px; }
109
+ .message { color: #333; font-size: 16px; line-height: 1.5; }
110
+ </style>
111
+ </head>
112
+ <body>
113
+ <div class="container">
114
+ <div class="success">✅ Authorization Successful!</div>
115
+ <div class="message">
116
+ You have successfully authorized Twist CLI. You can now close this window and return to your terminal.
117
+ </div>
118
+ </div>
119
+ </body>
120
+ </html>`;
121
+ }
122
+ function getErrorPage(errorMessage) {
123
+ return `
124
+ <!DOCTYPE html>
125
+ <html>
126
+ <head>
127
+ <meta charset="utf-8">
128
+ <title>Authorization Error - Twist CLI</title>
129
+ <style>
130
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; text-align: center; padding: 50px; background: #f5f5f5; }
131
+ .container { max-width: 500px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
132
+ .error { color: #dc3545; font-size: 24px; margin-bottom: 20px; }
133
+ .message { color: #333; font-size: 16px; line-height: 1.5; margin-bottom: 20px; }
134
+ .instructions { color: #666; font-size: 14px; }
135
+ </style>
136
+ </head>
137
+ <body>
138
+ <div class="container">
139
+ <div class="error">❌ Authorization Failed</div>
140
+ <div class="message">${errorMessage}</div>
141
+ <div class="instructions">
142
+ Please close this window and try running the login command again in your terminal.
143
+ </div>
144
+ </div>
145
+ </body>
146
+ </html>`;
147
+ }
148
+ function getNotFoundPage() {
149
+ return `
150
+ <!DOCTYPE html>
151
+ <html>
152
+ <head>
153
+ <meta charset="utf-8">
154
+ <title>Page Not Found - Twist CLI</title>
155
+ <style>
156
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; text-align: center; padding: 50px; background: #f5f5f5; }
157
+ .container { max-width: 500px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
158
+ .message { color: #333; font-size: 16px; }
159
+ </style>
160
+ </head>
161
+ <body>
162
+ <div class="container">
163
+ <div class="message">This is the OAuth callback server for Twist CLI. This page should only be accessed during the login process.</div>
164
+ </div>
165
+ </body>
166
+ </html>`;
167
+ }
168
+ //# sourceMappingURL=oauth-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-server.js","sourceRoot":"","sources":["../../src/lib/oauth-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * OAuth flow coordination for Twist API authentication
3
+ */
4
+ export declare const AUTHORIZATION_URL = "https://twist.com/oauth/authorize";
5
+ export declare const TOKEN_URL = "https://twist.com/oauth/access_token";
6
+ export declare const REGISTRATION_URL = "https://twist.com/oauth/register";
7
+ export declare const OAUTH_REDIRECT_URI = "http://localhost:8766/callback";
8
+ export declare const OAUTH_SCOPES: string;
9
+ /**
10
+ * OAuth client credentials from dynamic registration
11
+ */
12
+ export interface OAuthClient {
13
+ client_id: string;
14
+ client_secret: string;
15
+ }
16
+ /**
17
+ * Register a dynamic OAuth client for this CLI session
18
+ */
19
+ export declare function registerDynamicClient(): Promise<OAuthClient>;
20
+ /**
21
+ * Build the authorization URL for the OAuth flow
22
+ */
23
+ export declare function buildAuthorizationUrl(clientId: string, codeChallenge: string, state: string): string;
24
+ /**
25
+ * Exchange authorization code for access token using PKCE
26
+ */
27
+ export declare function exchangeCodeForToken(code: string, codeVerifier: string, client: OAuthClient): Promise<string>;
28
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/lib/oauth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,eAAO,MAAM,iBAAiB,sCAAsC,CAAA;AACpE,eAAO,MAAM,SAAS,yCAAyC,CAAA;AAC/D,eAAO,MAAM,gBAAgB,qCAAqC,CAAA;AAClE,eAAO,MAAM,kBAAkB,mCAAmC,CAAA;AAGlE,eAAO,MAAM,YAAY,QAcd,CAAA;AAEX;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;CACxB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,WAAW,CAAC,CA8ClE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACjC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,GACd,MAAM,CAYR;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACtC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,WAAW,GACpB,OAAO,CAAC,MAAM,CAAC,CAiDjB"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * OAuth flow coordination for Twist API authentication
3
+ */
4
+ // OAuth configuration for Twist (using well-known endpoints with dynamic client registration)
5
+ export const AUTHORIZATION_URL = 'https://twist.com/oauth/authorize';
6
+ export const TOKEN_URL = 'https://twist.com/oauth/access_token';
7
+ export const REGISTRATION_URL = 'https://twist.com/oauth/register';
8
+ export const OAUTH_REDIRECT_URI = 'http://localhost:8766/callback';
9
+ // OAuth scopes needed for the CLI operations
10
+ export const OAUTH_SCOPES = [
11
+ 'user:read', // Read user information and session details
12
+ 'workspaces:read', // Read workspace information
13
+ 'channels:read', // Read channel information
14
+ 'threads:read', // Read thread information
15
+ 'threads:write', // Create and manage threads
16
+ 'comments:read', // Read comments/messages
17
+ 'comments:write', // Send comments/messages
18
+ 'messages:read', // Read messages
19
+ 'messages:write', // Send messages
20
+ 'reactions:read', // Read reactions
21
+ 'reactions:write', // Add reactions
22
+ 'search:read', // Search functionality
23
+ 'notifications:read', // Read notifications
24
+ ].join(' ');
25
+ /**
26
+ * Register a dynamic OAuth client for this CLI session
27
+ */
28
+ export async function registerDynamicClient() {
29
+ const clientData = {
30
+ client_name: 'Twist CLI',
31
+ client_uri: 'https://github.com/doist/twist-cli',
32
+ redirect_uris: [OAUTH_REDIRECT_URI],
33
+ grant_types: ['authorization_code'],
34
+ response_types: ['code'],
35
+ token_endpoint_auth_method: 'client_secret_basic', // Use Basic auth for token exchange
36
+ application_type: 'native', // CLI is a native application
37
+ };
38
+ try {
39
+ const response = await fetch(REGISTRATION_URL, {
40
+ method: 'POST',
41
+ headers: {
42
+ 'Content-Type': 'application/json',
43
+ Accept: 'application/json',
44
+ },
45
+ body: JSON.stringify(clientData),
46
+ });
47
+ if (!response.ok) {
48
+ const errorText = await response.text();
49
+ throw new Error(`Client registration failed: ${response.status} ${response.statusText} - ${errorText}`);
50
+ }
51
+ const result = await response.json();
52
+ if (!result.client_id || !result.client_secret) {
53
+ throw new Error('Invalid client registration response: missing client_id or client_secret');
54
+ }
55
+ return {
56
+ client_id: result.client_id,
57
+ client_secret: result.client_secret,
58
+ };
59
+ }
60
+ catch (error) {
61
+ if (error instanceof Error) {
62
+ throw new Error(`Failed to register OAuth client: ${error.message}`);
63
+ }
64
+ throw new Error('Failed to register OAuth client: Unknown error');
65
+ }
66
+ }
67
+ /**
68
+ * Build the authorization URL for the OAuth flow
69
+ */
70
+ export function buildAuthorizationUrl(clientId, codeChallenge, state) {
71
+ const params = new URLSearchParams({
72
+ client_id: clientId,
73
+ response_type: 'code',
74
+ redirect_uri: OAUTH_REDIRECT_URI,
75
+ scope: OAUTH_SCOPES,
76
+ state,
77
+ code_challenge: codeChallenge,
78
+ code_challenge_method: 'S256',
79
+ });
80
+ return `${AUTHORIZATION_URL}?${params.toString()}`;
81
+ }
82
+ /**
83
+ * Exchange authorization code for access token using PKCE
84
+ */
85
+ export async function exchangeCodeForToken(code, codeVerifier, client) {
86
+ const body = new URLSearchParams({
87
+ grant_type: 'authorization_code',
88
+ code,
89
+ redirect_uri: OAUTH_REDIRECT_URI,
90
+ code_verifier: codeVerifier,
91
+ });
92
+ // Use HTTP Basic Authentication for client credentials
93
+ const credentials = `${client.client_id}:${client.client_secret}`;
94
+ const encodedCredentials = btoa(credentials);
95
+ try {
96
+ const response = await fetch(TOKEN_URL, {
97
+ method: 'POST',
98
+ headers: {
99
+ 'Content-Type': 'application/x-www-form-urlencoded',
100
+ Accept: 'application/json',
101
+ Authorization: `Basic ${encodedCredentials}`,
102
+ },
103
+ body: body.toString(),
104
+ });
105
+ if (!response.ok) {
106
+ const errorText = await response.text();
107
+ throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${errorText}`);
108
+ }
109
+ const data = await response.json();
110
+ if (data.error) {
111
+ throw new Error(`OAuth error: ${data.error} - ${data.error_description || 'Unknown error'}`);
112
+ }
113
+ if (!data.access_token) {
114
+ throw new Error('No access token received from OAuth server');
115
+ }
116
+ return data.access_token;
117
+ }
118
+ catch (error) {
119
+ if (error instanceof Error) {
120
+ throw new Error(`Failed to exchange code for token: ${error.message}`);
121
+ }
122
+ throw new Error('Failed to exchange code for token: Unknown error');
123
+ }
124
+ }
125
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/lib/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export declare const colors: {
2
+ author: import("chalk").ChalkInstance;
3
+ timestamp: import("chalk").ChalkInstance;
4
+ channel: import("chalk").ChalkInstance;
5
+ unread: import("chalk").ChalkInstance;
6
+ url: import("chalk").ChalkInstance;
7
+ error: import("chalk").ChalkInstance;
8
+ };
9
+ export type EntityType = 'thread' | 'comment' | 'conversation' | 'message' | 'workspace' | 'user' | 'channel';
10
+ export declare function formatJson<T extends object>(data: T | T[], type?: EntityType, full?: boolean): string;
11
+ export declare function formatNdjson<T extends object>(items: T[], type?: EntityType, full?: boolean): string;
12
+ export interface PaginatedOutput<T> {
13
+ results: T[];
14
+ nextCursor: string | null;
15
+ }
16
+ export declare function formatPaginatedJson<T extends object>(data: PaginatedOutput<T>, type?: EntityType, full?: boolean): string;
17
+ export declare function formatPaginatedNdjson<T extends object>(data: PaginatedOutput<T>, type?: EntityType, full?: boolean): string;
18
+ export declare function formatError(message: string): string;
19
+ export declare function printError(message: string): void;
20
+ export declare function printJson<T extends object>(data: T | T[], type?: EntityType, full?: boolean): void;
21
+ export declare function printNdjson<T extends object>(items: T[], type?: EntityType, full?: boolean): void;
22
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;;;;;;;CAOlB,CAAA;AAiCD,MAAM,MAAM,UAAU,GAChB,QAAQ,GACR,SAAS,GACT,cAAc,GACd,SAAS,GACT,WAAW,GACX,MAAM,GACN,SAAS,CAAA;AA+Bf,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACvC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,EACb,IAAI,CAAC,EAAE,UAAU,EACjB,IAAI,UAAQ,GACb,MAAM,CAaR;AAED,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EACzC,KAAK,EAAE,CAAC,EAAE,EACV,IAAI,CAAC,EAAE,UAAU,EACjB,IAAI,UAAQ,GACb,MAAM,CAMR;AAED,MAAM,WAAW,eAAe,CAAC,CAAC;IAC9B,OAAO,EAAE,CAAC,EAAE,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,EAChD,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,EACxB,IAAI,CAAC,EAAE,UAAU,EACjB,IAAI,UAAQ,GACb,MAAM,CAIR;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,EAClD,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,EACxB,IAAI,CAAC,EAAE,UAAU,EACjB,IAAI,UAAQ,GACb,MAAM,CASR;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,UAAU,EAAE,IAAI,UAAQ,GAAG,IAAI,CAEhG;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,UAAU,EAAE,IAAI,UAAQ,GAAG,IAAI,CAE/F"}