@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
|
@@ -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
|
package/src/hooks/types.ts
CHANGED
|
@@ -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
|
/**
|