@simonfestl/husky-cli 0.8.2 → 0.9.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 (44) hide show
  1. package/README.md +3 -116
  2. package/dist/commands/biz/customers.d.ts +8 -0
  3. package/dist/commands/biz/customers.js +181 -0
  4. package/dist/commands/biz/orders.d.ts +8 -0
  5. package/dist/commands/biz/orders.js +226 -0
  6. package/dist/commands/biz/products.d.ts +8 -0
  7. package/dist/commands/biz/products.js +255 -0
  8. package/dist/commands/biz/qdrant.d.ts +8 -0
  9. package/dist/commands/biz/qdrant.js +170 -0
  10. package/dist/commands/biz/seatable.d.ts +8 -0
  11. package/dist/commands/biz/seatable.js +449 -0
  12. package/dist/commands/biz/tickets.d.ts +8 -0
  13. package/dist/commands/biz/tickets.js +600 -0
  14. package/dist/commands/biz.d.ts +9 -0
  15. package/dist/commands/biz.js +22 -0
  16. package/dist/commands/config.d.ts +13 -0
  17. package/dist/commands/config.js +43 -16
  18. package/dist/commands/explain.js +12 -595
  19. package/dist/commands/idea.js +2 -50
  20. package/dist/commands/project.js +2 -47
  21. package/dist/commands/roadmap.js +0 -107
  22. package/dist/commands/task.js +11 -17
  23. package/dist/commands/vm.js +0 -225
  24. package/dist/commands/workflow.js +4 -60
  25. package/dist/index.js +5 -1
  26. package/dist/lib/biz/billbee-types.d.ts +259 -0
  27. package/dist/lib/biz/billbee-types.js +41 -0
  28. package/dist/lib/biz/billbee.d.ts +37 -0
  29. package/dist/lib/biz/billbee.js +165 -0
  30. package/dist/lib/biz/embeddings.d.ts +45 -0
  31. package/dist/lib/biz/embeddings.js +115 -0
  32. package/dist/lib/biz/index.d.ts +13 -0
  33. package/dist/lib/biz/index.js +11 -0
  34. package/dist/lib/biz/qdrant.d.ts +52 -0
  35. package/dist/lib/biz/qdrant.js +158 -0
  36. package/dist/lib/biz/seatable-types.d.ts +115 -0
  37. package/dist/lib/biz/seatable-types.js +27 -0
  38. package/dist/lib/biz/seatable.d.ts +49 -0
  39. package/dist/lib/biz/seatable.js +210 -0
  40. package/dist/lib/biz/zendesk-types.d.ts +136 -0
  41. package/dist/lib/biz/zendesk-types.js +28 -0
  42. package/dist/lib/biz/zendesk.d.ts +45 -0
  43. package/dist/lib/biz/zendesk.js +206 -0
  44. package/package.json +2 -2
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Biz Library Exports
3
+ */
4
+ export { BillbeeClient } from './billbee.js';
5
+ export { ZendeskClient } from './zendesk.js';
6
+ export { SeaTableClient } from './seatable.js';
7
+ export { QdrantClient } from './qdrant.js';
8
+ export { EmbeddingService, EMBEDDING_MODELS } from './embeddings.js';
9
+ export type { Point, SearchResult as QdrantSearchResult, SearchOptions, CollectionInfo, QdrantConfig } from './qdrant.js';
10
+ export type { EmbeddingConfig, EmbeddingResult } from './embeddings.js';
11
+ export * from './billbee-types.js';
12
+ export * from './zendesk-types.js';
13
+ export * from './seatable-types.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Biz Library Exports
3
+ */
4
+ export { BillbeeClient } from './billbee.js';
5
+ export { ZendeskClient } from './zendesk.js';
6
+ export { SeaTableClient } from './seatable.js';
7
+ export { QdrantClient } from './qdrant.js';
8
+ export { EmbeddingService, EMBEDDING_MODELS } from './embeddings.js';
9
+ export * from './billbee-types.js';
10
+ export * from './zendesk-types.js';
11
+ export * from './seatable-types.js';
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Qdrant Vector Database Client
3
+ * Ported from TigerV0 with Husky Biz CLI integration
4
+ */
5
+ export interface QdrantConfig {
6
+ url: string;
7
+ apiKey?: string;
8
+ }
9
+ export interface Point {
10
+ id: string | number;
11
+ vector: number[];
12
+ payload?: Record<string, unknown>;
13
+ }
14
+ export interface SearchResult {
15
+ id: string | number;
16
+ score: number;
17
+ payload?: Record<string, unknown>;
18
+ }
19
+ export interface SearchOptions {
20
+ limit?: number;
21
+ filter?: Record<string, unknown>;
22
+ scoreThreshold?: number;
23
+ offset?: number;
24
+ }
25
+ export interface CollectionInfo {
26
+ name: string;
27
+ vectorsCount: number;
28
+ pointsCount: number;
29
+ }
30
+ export declare class QdrantClient {
31
+ private url;
32
+ private apiKey?;
33
+ constructor(config: QdrantConfig);
34
+ /**
35
+ * Create client from Husky config
36
+ */
37
+ static fromConfig(): QdrantClient;
38
+ private request;
39
+ listCollections(): Promise<string[]>;
40
+ getCollection(name: string): Promise<CollectionInfo>;
41
+ createCollection(name: string, vectorSize: number): Promise<void>;
42
+ deleteCollection(name: string): Promise<void>;
43
+ search(collectionName: string, vector: number[], limit?: number, options?: Omit<SearchOptions, 'limit'> & {
44
+ vectorName?: string;
45
+ }): Promise<SearchResult[]>;
46
+ upsert(collectionName: string, points: Point[]): Promise<void>;
47
+ upsertOne(collectionName: string, id: string | number, vector: number[], payload?: Record<string, unknown>): Promise<void>;
48
+ getPoint(collectionName: string, id: string | number): Promise<Point | null>;
49
+ deletePoints(collectionName: string, ids: (string | number)[]): Promise<void>;
50
+ count(collectionName: string): Promise<number>;
51
+ }
52
+ export default QdrantClient;
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Qdrant Vector Database Client
3
+ * Ported from TigerV0 with Husky Biz CLI integration
4
+ */
5
+ import { getConfig } from '../../commands/config.js';
6
+ // ============================================================================
7
+ // Qdrant Service (REST API based)
8
+ // ============================================================================
9
+ export class QdrantClient {
10
+ url;
11
+ apiKey;
12
+ constructor(config) {
13
+ this.url = config.url.replace(/\/+$/, '');
14
+ this.apiKey = config.apiKey;
15
+ }
16
+ /**
17
+ * Create client from Husky config
18
+ */
19
+ static fromConfig() {
20
+ const config = getConfig();
21
+ const qdrantConfig = {
22
+ url: config.qdrantUrl || process.env.QDRANT_URL || 'http://localhost:6333',
23
+ apiKey: config.qdrantApiKey || process.env.QDRANT_API_KEY,
24
+ };
25
+ if (!qdrantConfig.url || qdrantConfig.url === 'http://localhost:6333') {
26
+ if (!process.env.QDRANT_URL) {
27
+ throw new Error('Missing Qdrant URL. Configure with:\n' +
28
+ ' husky config set qdrant-url <url>\n' +
29
+ ' husky config set qdrant-api-key <key>');
30
+ }
31
+ }
32
+ return new QdrantClient(qdrantConfig);
33
+ }
34
+ async request(path, options = {}) {
35
+ const url = `${this.url}${path}`;
36
+ const headers = {
37
+ 'Content-Type': 'application/json',
38
+ };
39
+ if (this.apiKey) {
40
+ headers['api-key'] = this.apiKey;
41
+ }
42
+ const response = await fetch(url, {
43
+ ...options,
44
+ headers: {
45
+ ...headers,
46
+ ...options.headers,
47
+ },
48
+ });
49
+ if (!response.ok) {
50
+ const error = await response.text();
51
+ throw new Error(`Qdrant API Error ${response.status}: ${error}`);
52
+ }
53
+ return response.json();
54
+ }
55
+ // =========================================================================
56
+ // Collections
57
+ // =========================================================================
58
+ async listCollections() {
59
+ const response = await this.request('/collections');
60
+ return response.result.collections.map(c => c.name);
61
+ }
62
+ async getCollection(name) {
63
+ const response = await this.request(`/collections/${name}`);
64
+ return {
65
+ name,
66
+ vectorsCount: response.result.vectors_count,
67
+ pointsCount: response.result.points_count,
68
+ };
69
+ }
70
+ async createCollection(name, vectorSize) {
71
+ await this.request(`/collections/${name}`, {
72
+ method: 'PUT',
73
+ body: JSON.stringify({
74
+ vectors: {
75
+ size: vectorSize,
76
+ distance: 'Cosine',
77
+ },
78
+ }),
79
+ });
80
+ }
81
+ async deleteCollection(name) {
82
+ await this.request(`/collections/${name}`, { method: 'DELETE' });
83
+ }
84
+ // =========================================================================
85
+ // Points
86
+ // =========================================================================
87
+ async search(collectionName, vector, limit = 10, options) {
88
+ // Build request body - support named vectors
89
+ const body = {
90
+ limit,
91
+ filter: options?.filter,
92
+ score_threshold: options?.scoreThreshold,
93
+ offset: options?.offset,
94
+ with_payload: true,
95
+ };
96
+ // If named vector, use object format
97
+ if (options?.vectorName) {
98
+ body.vector = { name: options.vectorName, vector };
99
+ }
100
+ else {
101
+ body.vector = vector;
102
+ }
103
+ const response = await this.request(`/collections/${collectionName}/points/search`, {
104
+ method: 'POST',
105
+ body: JSON.stringify(body),
106
+ });
107
+ return response.result.map(r => ({
108
+ id: r.id,
109
+ score: r.score,
110
+ payload: r.payload,
111
+ }));
112
+ }
113
+ async upsert(collectionName, points) {
114
+ await this.request(`/collections/${collectionName}/points?wait=true`, {
115
+ method: 'PUT',
116
+ body: JSON.stringify({
117
+ points: points.map(p => ({
118
+ id: p.id,
119
+ vector: p.vector,
120
+ payload: p.payload || {},
121
+ })),
122
+ }),
123
+ });
124
+ }
125
+ async upsertOne(collectionName, id, vector, payload) {
126
+ await this.upsert(collectionName, [{ id, vector, payload }]);
127
+ }
128
+ async getPoint(collectionName, id) {
129
+ try {
130
+ const response = await this.request(`/collections/${collectionName}/points`, {
131
+ method: 'POST',
132
+ body: JSON.stringify({
133
+ ids: [id],
134
+ with_payload: true,
135
+ with_vector: true,
136
+ }),
137
+ });
138
+ if (response.result.length === 0)
139
+ return null;
140
+ const p = response.result[0];
141
+ return { id: p.id, vector: p.vector, payload: p.payload };
142
+ }
143
+ catch {
144
+ return null;
145
+ }
146
+ }
147
+ async deletePoints(collectionName, ids) {
148
+ await this.request(`/collections/${collectionName}/points/delete?wait=true`, {
149
+ method: 'POST',
150
+ body: JSON.stringify({ points: ids }),
151
+ });
152
+ }
153
+ async count(collectionName) {
154
+ const info = await this.getCollection(collectionName);
155
+ return info.pointsCount;
156
+ }
157
+ }
158
+ export default QdrantClient;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * SeaTable API Types
3
+ */
4
+ /**
5
+ * Client configuration
6
+ */
7
+ export interface SeaTableConfig {
8
+ apiToken: string;
9
+ serverUrl?: string;
10
+ }
11
+ /**
12
+ * Column Types in SeaTable
13
+ */
14
+ export declare enum ColumnType {
15
+ Text = "text",
16
+ Number = "number",
17
+ Checkbox = "checkbox",
18
+ Date = "date",
19
+ SingleSelect = "single-select",
20
+ MultipleSelect = "multiple-select",
21
+ Image = "image",
22
+ File = "file",
23
+ Email = "email",
24
+ URL = "url",
25
+ Duration = "duration",
26
+ Rating = "rating",
27
+ Formula = "formula",
28
+ Link = "link",
29
+ Creator = "creator",
30
+ CreatedTime = "ctime",
31
+ LastModifier = "last-modifier",
32
+ ModifiedTime = "mtime"
33
+ }
34
+ /**
35
+ * SeaTable Row
36
+ */
37
+ export interface SeaTableRow {
38
+ _id: string;
39
+ _ctime?: string;
40
+ _mtime?: string;
41
+ _creator?: string;
42
+ _last_modifier?: string;
43
+ [key: string]: unknown;
44
+ }
45
+ /**
46
+ * SeaTable Column
47
+ */
48
+ export interface SeaTableColumn {
49
+ key: string;
50
+ name: string;
51
+ type: ColumnType;
52
+ width?: number;
53
+ editable?: boolean;
54
+ resizable?: boolean;
55
+ data?: {
56
+ link_id?: string;
57
+ other_table_id?: string;
58
+ options?: Array<{
59
+ name: string;
60
+ color: string;
61
+ textColor?: string;
62
+ }>;
63
+ };
64
+ }
65
+ /**
66
+ * SeaTable Table
67
+ */
68
+ export interface SeaTableTable {
69
+ _id: string;
70
+ name: string;
71
+ columns: SeaTableColumn[];
72
+ }
73
+ /**
74
+ * SeaTable Base Metadata
75
+ */
76
+ export interface SeaTableMetadata {
77
+ tables: SeaTableTable[];
78
+ }
79
+ /**
80
+ * Row Query Parameters
81
+ */
82
+ export interface QueryRowsParams {
83
+ table_name: string;
84
+ view_name?: string;
85
+ order_by?: string;
86
+ direction?: 'asc' | 'desc';
87
+ start?: number;
88
+ limit?: number;
89
+ }
90
+ /**
91
+ * Filter Parameters
92
+ */
93
+ export interface FilterParams {
94
+ filters?: Array<{
95
+ column_name: string;
96
+ filter_predicate: 'is' | 'is_not' | 'contains' | 'does_not_contain' | 'is_empty' | 'is_not_empty' | 'equal' | 'not_equal' | 'greater' | 'greater_or_equal' | 'less' | 'less_or_equal';
97
+ filter_term?: string | number | boolean;
98
+ }>;
99
+ filter_conjunction?: 'And' | 'Or';
100
+ }
101
+ /**
102
+ * Row Creation/Update Data
103
+ */
104
+ export interface RowData {
105
+ [key: string]: string | number | boolean | string[] | null;
106
+ }
107
+ /**
108
+ * Query Response
109
+ */
110
+ export interface QueryResponse {
111
+ rows: SeaTableRow[];
112
+ metadata?: {
113
+ total_count?: number;
114
+ };
115
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * SeaTable API Types
3
+ */
4
+ /**
5
+ * Column Types in SeaTable
6
+ */
7
+ export var ColumnType;
8
+ (function (ColumnType) {
9
+ ColumnType["Text"] = "text";
10
+ ColumnType["Number"] = "number";
11
+ ColumnType["Checkbox"] = "checkbox";
12
+ ColumnType["Date"] = "date";
13
+ ColumnType["SingleSelect"] = "single-select";
14
+ ColumnType["MultipleSelect"] = "multiple-select";
15
+ ColumnType["Image"] = "image";
16
+ ColumnType["File"] = "file";
17
+ ColumnType["Email"] = "email";
18
+ ColumnType["URL"] = "url";
19
+ ColumnType["Duration"] = "duration";
20
+ ColumnType["Rating"] = "rating";
21
+ ColumnType["Formula"] = "formula";
22
+ ColumnType["Link"] = "link";
23
+ ColumnType["Creator"] = "creator";
24
+ ColumnType["CreatedTime"] = "ctime";
25
+ ColumnType["LastModifier"] = "last-modifier";
26
+ ColumnType["ModifiedTime"] = "mtime";
27
+ })(ColumnType || (ColumnType = {}));
@@ -0,0 +1,49 @@
1
+ /**
2
+ * SeaTable API Client
3
+ * Ported from TigerV0 with Husky Biz CLI integration
4
+ *
5
+ * IMPORTANT: SeaTable uses a 2-stage token system:
6
+ * 1. API Token (permanent) - generated in SeaTable UI
7
+ * 2. Base Token (3 days TTL) - generated from API Token automatically
8
+ */
9
+ import type { SeaTableConfig, SeaTableRow, SeaTableMetadata, QueryRowsParams, FilterParams, RowData, QueryResponse } from './seatable-types.js';
10
+ export declare class SeaTableClient {
11
+ private serverUrl;
12
+ private apiToken;
13
+ private baseToken;
14
+ private baseTokenExpiry;
15
+ private dtableServer;
16
+ private dtableUuid;
17
+ constructor(config: SeaTableConfig);
18
+ /**
19
+ * Create client from Husky config
20
+ */
21
+ static fromConfig(): SeaTableClient;
22
+ private isApiGateway;
23
+ private buildEndpoint;
24
+ /**
25
+ * Get a Base Token from the API Token
26
+ * Base Tokens are valid for 3 days
27
+ */
28
+ private getBaseToken;
29
+ /**
30
+ * Make an authenticated API request
31
+ */
32
+ private request;
33
+ getMetadata(): Promise<SeaTableMetadata>;
34
+ listRows(params: QueryRowsParams): Promise<SeaTableRow[]>;
35
+ getRow(tableName: string, rowId: string): Promise<SeaTableRow | null>;
36
+ queryRows(tableName: string, filters: FilterParams, params?: Partial<QueryRowsParams>): Promise<QueryResponse>;
37
+ searchRows(tableName: string, searchQuery: string): Promise<SeaTableRow[]>;
38
+ appendRow(tableName: string, row: RowData): Promise<SeaTableRow>;
39
+ updateRow(tableName: string, rowId: string, data: RowData): Promise<{
40
+ success: boolean;
41
+ }>;
42
+ deleteRow(tableName: string, rowId: string): Promise<{
43
+ success: boolean;
44
+ }>;
45
+ deleteRows(tableName: string, rowIds: string[]): Promise<{
46
+ success: boolean;
47
+ }>;
48
+ }
49
+ export default SeaTableClient;
@@ -0,0 +1,210 @@
1
+ /**
2
+ * SeaTable API Client
3
+ * Ported from TigerV0 with Husky Biz CLI integration
4
+ *
5
+ * IMPORTANT: SeaTable uses a 2-stage token system:
6
+ * 1. API Token (permanent) - generated in SeaTable UI
7
+ * 2. Base Token (3 days TTL) - generated from API Token automatically
8
+ */
9
+ import { getConfig } from '../../commands/config.js';
10
+ export class SeaTableClient {
11
+ serverUrl;
12
+ apiToken;
13
+ baseToken = null;
14
+ baseTokenExpiry = null;
15
+ dtableServer = null;
16
+ dtableUuid = null;
17
+ constructor(config) {
18
+ this.apiToken = config.apiToken;
19
+ this.serverUrl = config.serverUrl || 'https://cloud.seatable.io';
20
+ }
21
+ /**
22
+ * Create client from Husky config
23
+ */
24
+ static fromConfig() {
25
+ const config = getConfig();
26
+ const seaTableConfig = {
27
+ apiToken: config.seatableApiToken || process.env.SEATABLE_API_TOKEN || '',
28
+ serverUrl: config.seatableServerUrl || process.env.SEATABLE_SERVER_URL || 'https://cloud.seatable.io',
29
+ };
30
+ if (!seaTableConfig.apiToken) {
31
+ throw new Error('Missing SeaTable API Token. Configure with:\n' +
32
+ ' husky config set seatable-api-token <token>\n\n' +
33
+ 'Get your token from SeaTable: Base → Advanced → API Token');
34
+ }
35
+ return new SeaTableClient(seaTableConfig);
36
+ }
37
+ isApiGateway() {
38
+ return (this.dtableServer || '').includes('api-gateway');
39
+ }
40
+ buildEndpoint(path) {
41
+ const base = this.isApiGateway()
42
+ ? '/api/v2/dtables/'
43
+ : '/dtable-server/api/v1/dtables/';
44
+ return `${base}${this.dtableUuid || ''}${path}`;
45
+ }
46
+ /**
47
+ * Get a Base Token from the API Token
48
+ * Base Tokens are valid for 3 days
49
+ */
50
+ async getBaseToken() {
51
+ const now = Date.now();
52
+ if (this.baseToken && this.baseTokenExpiry && now < this.baseTokenExpiry) {
53
+ return;
54
+ }
55
+ const response = await fetch(`${this.serverUrl}/api/v2.1/dtable/app-access-token/`, {
56
+ method: 'GET',
57
+ headers: {
58
+ 'Authorization': `Token ${this.apiToken}`,
59
+ 'Accept': 'application/json',
60
+ },
61
+ });
62
+ if (!response.ok) {
63
+ const error = await response.text();
64
+ throw new Error(`Failed to get SeaTable Base Token: ${response.status} ${error}`);
65
+ }
66
+ const data = await response.json();
67
+ this.baseToken = data.access_token;
68
+ this.dtableServer = data.dtable_server;
69
+ this.dtableUuid = data.dtable_uuid;
70
+ // Set expiry to 2.5 days from now (to be safe)
71
+ this.baseTokenExpiry = now + (2.5 * 24 * 60 * 60 * 1000);
72
+ }
73
+ /**
74
+ * Make an authenticated API request
75
+ */
76
+ async request(path, options = {}) {
77
+ await this.getBaseToken();
78
+ if (!this.dtableServer || !this.baseToken || !this.dtableUuid) {
79
+ throw new Error('Failed to obtain SeaTable Base Token');
80
+ }
81
+ const endpoint = this.buildEndpoint(path);
82
+ const baseUrl = (this.dtableServer || '').replace(/\/+$/, '');
83
+ const url = `${baseUrl}${endpoint}`;
84
+ const response = await fetch(url, {
85
+ ...options,
86
+ headers: {
87
+ 'Authorization': `Bearer ${this.baseToken}`,
88
+ 'Content-Type': 'application/json',
89
+ ...options.headers,
90
+ },
91
+ });
92
+ if (!response.ok) {
93
+ const error = await response.text();
94
+ throw new Error(`SeaTable API Error ${response.status}: ${error}`);
95
+ }
96
+ return response.json();
97
+ }
98
+ // =========================================================================
99
+ // METADATA
100
+ // =========================================================================
101
+ async getMetadata() {
102
+ const response = await this.request('/metadata/');
103
+ // Response may be { metadata: { tables: [...] } } or directly { tables: [...] }
104
+ if ('metadata' in response && response.metadata) {
105
+ return response.metadata;
106
+ }
107
+ return response;
108
+ }
109
+ // =========================================================================
110
+ // ROWS
111
+ // =========================================================================
112
+ async listRows(params) {
113
+ const query = new URLSearchParams({
114
+ table_name: params.table_name,
115
+ });
116
+ if (params.view_name)
117
+ query.set('view_name', params.view_name);
118
+ if (params.order_by)
119
+ query.set('order_by', params.order_by);
120
+ if (params.direction)
121
+ query.set('direction', params.direction);
122
+ if (params.start !== undefined)
123
+ query.set('start', String(params.start));
124
+ if (params.limit !== undefined)
125
+ query.set('limit', String(params.limit));
126
+ const response = await this.request(`/rows/?${query}`);
127
+ return response.rows;
128
+ }
129
+ async getRow(tableName, rowId) {
130
+ try {
131
+ const query = new URLSearchParams({
132
+ table_name: tableName,
133
+ row_id: rowId,
134
+ });
135
+ return await this.request(`/rows/?${query}`);
136
+ }
137
+ catch {
138
+ return null;
139
+ }
140
+ }
141
+ async queryRows(tableName, filters, params) {
142
+ const body = {
143
+ table_name: tableName,
144
+ filters: filters.filters || [],
145
+ filter_conjunction: filters.filter_conjunction || 'And',
146
+ ...params,
147
+ };
148
+ return this.request('/filtered-rows/', {
149
+ method: 'POST',
150
+ body: JSON.stringify(body),
151
+ });
152
+ }
153
+ async searchRows(tableName, searchQuery) {
154
+ const query = new URLSearchParams({
155
+ table_name: tableName,
156
+ q: searchQuery,
157
+ });
158
+ const response = await this.request(`/search/?${query}`);
159
+ return response.rows;
160
+ }
161
+ async appendRow(tableName, row) {
162
+ const response = await this.request('/rows/', {
163
+ method: 'POST',
164
+ body: JSON.stringify({
165
+ table_name: tableName,
166
+ rows: [row],
167
+ }),
168
+ });
169
+ if (response.rows?.[0])
170
+ return response.rows[0];
171
+ if (response.first_row)
172
+ return response.first_row;
173
+ let rowId = response.row_ids?.[0];
174
+ if (rowId && typeof rowId === 'object' && '_id' in rowId) {
175
+ const fetchedRow = await this.getRow(tableName, rowId._id);
176
+ if (fetchedRow)
177
+ return fetchedRow;
178
+ }
179
+ throw new Error('Unexpected append response from SeaTable');
180
+ }
181
+ async updateRow(tableName, rowId, data) {
182
+ return this.request('/rows/', {
183
+ method: 'PUT',
184
+ body: JSON.stringify({
185
+ table_name: tableName,
186
+ row_id: rowId,
187
+ row: data,
188
+ }),
189
+ });
190
+ }
191
+ async deleteRow(tableName, rowId) {
192
+ return this.request('/rows/', {
193
+ method: 'DELETE',
194
+ body: JSON.stringify({
195
+ table_name: tableName,
196
+ row_id: rowId,
197
+ }),
198
+ });
199
+ }
200
+ async deleteRows(tableName, rowIds) {
201
+ return this.request('/batch-delete-rows/', {
202
+ method: 'DELETE',
203
+ body: JSON.stringify({
204
+ table_name: tableName,
205
+ row_ids: rowIds,
206
+ }),
207
+ });
208
+ }
209
+ }
210
+ export default SeaTableClient;