@joshualelon/clawdbot-skill-flow 2.3.2 → 2.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshualelon/clawdbot-skill-flow",
3
- "version": "2.3.2",
3
+ "version": "2.3.3",
4
4
  "type": "module",
5
5
  "description": "Multi-step workflow orchestration plugin for Clawdbot",
6
6
  "keywords": [
@@ -49,6 +49,9 @@ const sheetsAppendSchema = z.object({
49
49
  privateKey: z.string(),
50
50
  })
51
51
  .optional(),
52
+ useGogOAuth: z.boolean().default(true).describe(
53
+ "Use gog CLI OAuth instead of service account (recommended to avoid quota issues)"
54
+ ),
52
55
  });
53
56
 
54
57
  async function sheetsAppendExecute(
@@ -85,7 +88,8 @@ async function sheetsAppendExecute(
85
88
  cfg.worksheetName,
86
89
  [row],
87
90
  cfg.credentials as GoogleServiceAccountCredentials | undefined,
88
- cfg.headerMode
91
+ cfg.headerMode,
92
+ cfg.useGogOAuth
89
93
  ),
90
94
  { maxAttempts: 3, delayMs: 1000, backoff: true }
91
95
  );
@@ -68,6 +68,7 @@ export function createSheetsLogger(
68
68
  includeMetadata = true,
69
69
  credentials,
70
70
  headerMode = 'append',
71
+ useGogOAuth,
71
72
  } = options;
72
73
 
73
74
  return async (variable: string, value: string | number, session: FlowSession) => {
@@ -99,7 +100,7 @@ export function createSheetsLogger(
99
100
 
100
101
  // Append to sheet with retry
101
102
  await withRetry(
102
- () => appendToSheet(spreadsheetId, worksheetName, [row], credentials, headerMode),
103
+ () => appendToSheet(spreadsheetId, worksheetName, [row], credentials, headerMode, useGogOAuth),
103
104
  { maxAttempts: 3, delayMs: 1000, backoff: true }
104
105
  );
105
106
  } catch (error) {
@@ -114,6 +115,120 @@ export function createSheetsLogger(
114
115
  };
115
116
  }
116
117
 
118
+ /**
119
+ * Append rows to Google Sheet using gog OAuth credentials
120
+ */
121
+ async function appendToSheetWithGogOAuth(
122
+ spreadsheetId: string,
123
+ worksheetName: string,
124
+ rows: Array<Record<string, unknown>>,
125
+ headerMode: HeaderMode = 'append'
126
+ ): Promise<void> {
127
+ if (rows.length === 0) {
128
+ return;
129
+ }
130
+
131
+ const accessToken = await getGogAccessToken();
132
+ const rowKeys = Object.keys(rows[0]!);
133
+
134
+ // Get existing data to check headers
135
+ const getUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${worksheetName}!A1:ZZ1`;
136
+ const getResponse = await fetch(getUrl, {
137
+ headers: { 'Authorization': `Bearer ${accessToken}` },
138
+ });
139
+
140
+ let existingHeaders: string[] = [];
141
+ if (getResponse.ok) {
142
+ const getData = await getResponse.json() as { values?: string[][] };
143
+ existingHeaders = getData.values?.[0] || [];
144
+ }
145
+
146
+ // Handle headers based on mode
147
+ if (existingHeaders.length === 0) {
148
+ // Empty sheet - write headers
149
+ const updateUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${worksheetName}!A1?valueInputOption=RAW`;
150
+ await fetch(updateUrl, {
151
+ method: 'PUT',
152
+ headers: {
153
+ 'Authorization': `Bearer ${accessToken}`,
154
+ 'Content-Type': 'application/json',
155
+ },
156
+ body: JSON.stringify({
157
+ values: [rowKeys],
158
+ }),
159
+ });
160
+ } else if (!arraysEqual(existingHeaders, rowKeys)) {
161
+ // Headers don't match - apply mode
162
+ switch (headerMode) {
163
+ case 'strict':
164
+ throw new Error(
165
+ `Header mismatch in sheet "${worksheetName}". ` +
166
+ `Expected: ${existingHeaders.join(', ')}. ` +
167
+ `Got: ${rowKeys.join(', ')}. ` +
168
+ `Set headerMode to 'append' or 'overwrite' to handle this.`
169
+ );
170
+
171
+ case 'append': {
172
+ // Find new columns not in existing headers
173
+ const newColumns = rowKeys.filter(key => !existingHeaders.includes(key));
174
+ if (newColumns.length > 0) {
175
+ // Append new columns to the right
176
+ const updatedHeaders = [...existingHeaders, ...newColumns];
177
+ const updateUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${worksheetName}!A1?valueInputOption=RAW`;
178
+ await fetch(updateUrl, {
179
+ method: 'PUT',
180
+ headers: {
181
+ 'Authorization': `Bearer ${accessToken}`,
182
+ 'Content-Type': 'application/json',
183
+ },
184
+ body: JSON.stringify({
185
+ values: [updatedHeaders],
186
+ }),
187
+ });
188
+ }
189
+ break;
190
+ }
191
+
192
+ case 'overwrite': {
193
+ // Replace headers completely
194
+ const updateUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${worksheetName}!A1?valueInputOption=RAW`;
195
+ await fetch(updateUrl, {
196
+ method: 'PUT',
197
+ headers: {
198
+ 'Authorization': `Bearer ${accessToken}`,
199
+ 'Content-Type': 'application/json',
200
+ },
201
+ body: JSON.stringify({
202
+ values: [rowKeys],
203
+ }),
204
+ });
205
+ break;
206
+ }
207
+ }
208
+ }
209
+
210
+ // Convert rows to 2D array
211
+ const values = rows.map((row) => rowKeys.map((key) => row[key] ?? ""));
212
+
213
+ // Append data
214
+ const appendUrl = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${worksheetName}!A:A:append?valueInputOption=RAW`;
215
+ const appendResponse = await fetch(appendUrl, {
216
+ method: 'POST',
217
+ headers: {
218
+ 'Authorization': `Bearer ${accessToken}`,
219
+ 'Content-Type': 'application/json',
220
+ },
221
+ body: JSON.stringify({
222
+ values,
223
+ }),
224
+ });
225
+
226
+ if (!appendResponse.ok) {
227
+ const errorText = await appendResponse.text();
228
+ throw new Error(`Failed to append to sheet: ${appendResponse.statusText} - ${errorText}`);
229
+ }
230
+ }
231
+
117
232
  /**
118
233
  * Low-level utility to append rows to a Google Sheet.
119
234
  * Creates the worksheet if it doesn't exist, and adds headers on first write.
@@ -131,12 +246,23 @@ export async function appendToSheet(
131
246
  worksheetName: string,
132
247
  rows: Array<Record<string, unknown>>,
133
248
  credentials?: GoogleServiceAccountCredentials,
134
- headerMode: HeaderMode = 'append'
249
+ headerMode: HeaderMode = 'append',
250
+ useGogOAuth?: boolean
135
251
  ): Promise<void> {
136
252
  if (rows.length === 0) {
137
253
  return;
138
254
  }
139
255
 
256
+ // Use gog OAuth if requested
257
+ if (useGogOAuth) {
258
+ return await appendToSheetWithGogOAuth(
259
+ spreadsheetId,
260
+ worksheetName,
261
+ rows,
262
+ headerMode
263
+ );
264
+ }
265
+
140
266
  const sheets = await createSheetsClient(credentials);
141
267
 
142
268
  // Ensure worksheet exists
@@ -27,6 +27,7 @@ export interface SheetsLogOptions {
27
27
  includeMetadata?: boolean; // Add timestamp, userId, flowName
28
28
  credentials?: GoogleServiceAccountCredentials;
29
29
  headerMode?: HeaderMode; // How to handle header mismatches (default: 'append')
30
+ useGogOAuth?: boolean; // Use gog CLI OAuth instead of service account (default: true)
30
31
  }
31
32
 
32
33
  /**