crewly 1.20.29 → 1.20.30
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/config/skills/agent/core/canva-create/SKILL.md +53 -0
- package/config/skills/agent/core/canva-create/execute.sh +88 -0
- package/config/skills/agent/core/canva-create/execute.test.sh +54 -0
- package/config/skills/agent/core/canva-designs/SKILL.md +51 -0
- package/config/skills/agent/core/canva-designs/execute.sh +96 -0
- package/config/skills/agent/core/canva-designs/execute.test.sh +54 -0
- package/config/skills/agent/core/canva-export/SKILL.md +52 -0
- package/config/skills/agent/core/canva-export/execute.sh +111 -0
- package/config/skills/agent/core/canva-export/execute.test.sh +55 -0
- package/config/skills/agent/core/canva-upload/SKILL.md +49 -0
- package/config/skills/agent/core/canva-upload/execute.sh +72 -0
- package/config/skills/agent/core/canva-upload/execute.test.sh +54 -0
- package/config/skills/agent/core/docs-read/SKILL.md +50 -0
- package/config/skills/agent/core/docs-read/execute.sh +68 -0
- package/config/skills/agent/core/docs-read/execute.test.sh +52 -0
- package/config/skills/agent/core/docs-write/SKILL.md +55 -0
- package/config/skills/agent/core/docs-write/execute.sh +90 -0
- package/config/skills/agent/core/docs-write/execute.test.sh +54 -0
- package/config/skills/agent/core/drive-read/SKILL.md +53 -0
- package/config/skills/agent/core/drive-read/execute.sh +79 -0
- package/config/skills/agent/core/drive-read/execute.test.sh +55 -0
- package/config/skills/agent/core/drive-search/SKILL.md +55 -0
- package/config/skills/agent/core/drive-search/execute.sh +87 -0
- package/config/skills/agent/core/drive-search/execute.test.sh +54 -0
- package/config/skills/agent/core/drive-upload/SKILL.md +54 -0
- package/config/skills/agent/core/drive-upload/execute.sh +117 -0
- package/config/skills/agent/core/drive-upload/execute.test.sh +57 -0
- package/config/skills/agent/core/sheets-read/SKILL.md +53 -0
- package/config/skills/agent/core/sheets-read/execute.sh +81 -0
- package/config/skills/agent/core/sheets-read/execute.test.sh +52 -0
- package/config/skills/agent/core/sheets-write/SKILL.md +56 -0
- package/config/skills/agent/core/sheets-write/execute.sh +103 -0
- package/config/skills/agent/core/sheets-write/execute.test.sh +57 -0
- package/config/skills/agent/core/slides-create/SKILL.md +61 -0
- package/config/skills/agent/core/slides-create/execute.sh +92 -0
- package/config/skills/agent/core/slides-create/execute.test.sh +54 -0
- package/config/skills/agent/core/slides-read/SKILL.md +49 -0
- package/config/skills/agent/core/slides-read/execute.sh +67 -0
- package/config/skills/agent/core/slides-read/execute.test.sh +50 -0
- package/dist/backend/backend/src/constants.d.ts +69 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +73 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/canva/canva.controller.d.ts +48 -0
- package/dist/backend/backend/src/controllers/canva/canva.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/canva/canva.controller.js +183 -0
- package/dist/backend/backend/src/controllers/canva/canva.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/canva/canva.routes.d.ts +22 -0
- package/dist/backend/backend/src/controllers/canva/canva.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/canva/canva.routes.js +34 -0
- package/dist/backend/backend/src/controllers/canva/canva.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/google/google.controller.d.ts +100 -0
- package/dist/backend/backend/src/controllers/google/google.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.controller.js +237 -1
- package/dist/backend/backend/src/controllers/google/google.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.routes.d.ts +13 -0
- package/dist/backend/backend/src/controllers/google/google.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.routes.js +27 -1
- package/dist/backend/backend/src/controllers/google/google.routes.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +3 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/canva/canva-token.service.d.ts +98 -0
- package/dist/backend/backend/src/services/canva/canva-token.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/canva/canva-token.service.js +198 -0
- package/dist/backend/backend/src/services/canva/canva-token.service.js.map +1 -0
- package/dist/backend/backend/src/services/canva/canva.service.d.ts +184 -0
- package/dist/backend/backend/src/services/canva/canva.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/canva/canva.service.js +274 -0
- package/dist/backend/backend/src/services/canva/canva.service.js.map +1 -0
- package/dist/backend/backend/src/services/google/docs.service.d.ts +117 -0
- package/dist/backend/backend/src/services/google/docs.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/google/docs.service.js +152 -0
- package/dist/backend/backend/src/services/google/docs.service.js.map +1 -0
- package/dist/backend/backend/src/services/google/drive.service.d.ts +157 -0
- package/dist/backend/backend/src/services/google/drive.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/google/drive.service.js +203 -0
- package/dist/backend/backend/src/services/google/drive.service.js.map +1 -0
- package/dist/backend/backend/src/services/google/google-api.client.d.ts +15 -7
- package/dist/backend/backend/src/services/google/google-api.client.d.ts.map +1 -1
- package/dist/backend/backend/src/services/google/google-api.client.js +13 -6
- package/dist/backend/backend/src/services/google/google-api.client.js.map +1 -1
- package/dist/backend/backend/src/services/google/sheets.service.d.ts +118 -0
- package/dist/backend/backend/src/services/google/sheets.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/google/sheets.service.js +158 -0
- package/dist/backend/backend/src/services/google/sheets.service.js.map +1 -0
- package/dist/backend/backend/src/services/google/slides.service.d.ts +136 -0
- package/dist/backend/backend/src/services/google/slides.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/google/slides.service.js +153 -0
- package/dist/backend/backend/src/services/google/slides.service.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +69 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +73 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/frontend/dist/assets/{index-d1bd0dec.js → index-96b51cd9.js} +243 -243
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SheetsService — Google Sheets over the owner's Workspace grant.
|
|
3
|
+
*
|
|
4
|
+
* Reads any spreadsheet the owner can see (`drive.readonly`), creates new
|
|
5
|
+
* spreadsheets and writes to the ones Crewly created (`drive.file`).
|
|
6
|
+
* Writing to a spreadsheet made elsewhere needs the `spreadsheets` scope
|
|
7
|
+
* the grant does not carry yet; Google answers 403 and the error says so.
|
|
8
|
+
*
|
|
9
|
+
* @module services/google/sheets.service
|
|
10
|
+
*/
|
|
11
|
+
import { type GoogleApiDeps } from './google-api.client.js';
|
|
12
|
+
/** A cell value as the API returns it (strings/numbers/booleans). */
|
|
13
|
+
export type SheetCell = string | number | boolean;
|
|
14
|
+
/** A rectangular block of values. */
|
|
15
|
+
export interface SheetValues {
|
|
16
|
+
spreadsheetId: string;
|
|
17
|
+
range: string;
|
|
18
|
+
rows: SheetCell[][];
|
|
19
|
+
}
|
|
20
|
+
/** Spreadsheet metadata. */
|
|
21
|
+
export interface SpreadsheetInfo {
|
|
22
|
+
id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
sheets: Array<{
|
|
25
|
+
title: string;
|
|
26
|
+
rowCount?: number;
|
|
27
|
+
columnCount?: number;
|
|
28
|
+
}>;
|
|
29
|
+
webViewLink: string;
|
|
30
|
+
}
|
|
31
|
+
/** Create input. */
|
|
32
|
+
export interface SheetCreateInput {
|
|
33
|
+
title: string;
|
|
34
|
+
/** Name of the first sheet tab (default "Sheet1"). */
|
|
35
|
+
sheetTitle?: string;
|
|
36
|
+
/** Initial rows written from A1. */
|
|
37
|
+
rows?: SheetCell[][];
|
|
38
|
+
}
|
|
39
|
+
/** Write input (append or overwrite). */
|
|
40
|
+
export interface SheetWriteInput {
|
|
41
|
+
spreadsheetId: string;
|
|
42
|
+
/** A1 range; for append the table it belongs to, for update the top-left anchor. */
|
|
43
|
+
range?: string;
|
|
44
|
+
rows: SheetCell[][];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate rows for a write.
|
|
48
|
+
*
|
|
49
|
+
* @param rows - Candidate rows
|
|
50
|
+
* @returns The rows
|
|
51
|
+
* @throws GoogleWorkspaceError(400, validation) when empty, not rectangular-ish, or too many
|
|
52
|
+
*/
|
|
53
|
+
export declare function requireRows(rows: unknown): SheetCell[][];
|
|
54
|
+
/**
|
|
55
|
+
* Sheets: read, create, append, update.
|
|
56
|
+
*/
|
|
57
|
+
export declare class SheetsService {
|
|
58
|
+
private readonly deps;
|
|
59
|
+
private readonly base;
|
|
60
|
+
/**
|
|
61
|
+
* @param deps - Token provider and optional fetch override
|
|
62
|
+
*/
|
|
63
|
+
constructor(deps: GoogleApiDeps);
|
|
64
|
+
/**
|
|
65
|
+
* `spreadsheets.get` (properties only) — title and the sheet tabs.
|
|
66
|
+
*
|
|
67
|
+
* @param id - Spreadsheet id
|
|
68
|
+
* @returns Metadata
|
|
69
|
+
*/
|
|
70
|
+
info(id: string): Promise<SpreadsheetInfo>;
|
|
71
|
+
/**
|
|
72
|
+
* `spreadsheets.values.get`.
|
|
73
|
+
*
|
|
74
|
+
* @param id - Spreadsheet id
|
|
75
|
+
* @param range - A1 range (default `A1:Z1000` on the first sheet)
|
|
76
|
+
* @returns The values (rows may be ragged)
|
|
77
|
+
*/
|
|
78
|
+
read(id: string, range?: string): Promise<SheetValues>;
|
|
79
|
+
/**
|
|
80
|
+
* `spreadsheets.create`, then `values.update` from A1 when rows are given.
|
|
81
|
+
*
|
|
82
|
+
* @param input - Title, first-sheet name, initial rows
|
|
83
|
+
* @returns The new spreadsheet
|
|
84
|
+
* @throws GoogleWorkspaceError(400, validation) without a title
|
|
85
|
+
*/
|
|
86
|
+
create(input: SheetCreateInput): Promise<SpreadsheetInfo>;
|
|
87
|
+
/**
|
|
88
|
+
* `spreadsheets.values.append` — rows go below the last row of the
|
|
89
|
+
* table that contains `range` (default: the first sheet).
|
|
90
|
+
*
|
|
91
|
+
* @param input - Spreadsheet, range, rows
|
|
92
|
+
* @returns Updated range and counts
|
|
93
|
+
*/
|
|
94
|
+
append(input: SheetWriteInput): Promise<{
|
|
95
|
+
spreadsheetId: string;
|
|
96
|
+
updatedRange: string;
|
|
97
|
+
updatedRows: number;
|
|
98
|
+
}>;
|
|
99
|
+
/**
|
|
100
|
+
* `spreadsheets.values.update` — overwrite starting at `range`.
|
|
101
|
+
*
|
|
102
|
+
* @param input - Spreadsheet, anchor range, rows
|
|
103
|
+
* @returns Updated range and counts
|
|
104
|
+
*/
|
|
105
|
+
update(input: SheetWriteInput): Promise<{
|
|
106
|
+
spreadsheetId: string;
|
|
107
|
+
updatedRange: string;
|
|
108
|
+
updatedRows: number;
|
|
109
|
+
}>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Quote a sheet name for an A1 range when it needs it.
|
|
113
|
+
*
|
|
114
|
+
* @param name - Sheet title
|
|
115
|
+
* @returns `'My Sheet'` or `Sheet1`
|
|
116
|
+
*/
|
|
117
|
+
export declare function quoteSheet(name: string): string;
|
|
118
|
+
//# sourceMappingURL=sheets.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sheets.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/google/sheets.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAiC,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAG3F,qEAAqE;AACrE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAElD,qCAAqC;AACrC,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;CACrB;AAED,4BAA4B;AAC5B,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,oBAAoB;AACpB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC;CACtB;AAED,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;CACrB;AASD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,EAAE,CAexD;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA8C;IAEnE;;OAEG;gBACS,IAAI,EAAE,aAAa;IAI/B;;;;;OAKG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAWhD;;;;;;OAMG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAa5D;;;;;;OAMG;IACG,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAe/D;;;;;;OAMG;IACG,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAenH;;;;;OAKG;IACG,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAapH;AAgBD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SheetsService — Google Sheets over the owner's Workspace grant.
|
|
3
|
+
*
|
|
4
|
+
* Reads any spreadsheet the owner can see (`drive.readonly`), creates new
|
|
5
|
+
* spreadsheets and writes to the ones Crewly created (`drive.file`).
|
|
6
|
+
* Writing to a spreadsheet made elsewhere needs the `spreadsheets` scope
|
|
7
|
+
* the grant does not carry yet; Google answers 403 and the error says so.
|
|
8
|
+
*
|
|
9
|
+
* @module services/google/sheets.service
|
|
10
|
+
*/
|
|
11
|
+
import { GOOGLE_WORKSPACE_CONSTANTS } from '../../constants.js';
|
|
12
|
+
import { GoogleWorkspaceError } from './google-workspace-token.service.js';
|
|
13
|
+
import { buildGoogleUrl, googleRequest } from './google-api.client.js';
|
|
14
|
+
import { requireId } from './drive.service.js';
|
|
15
|
+
/**
|
|
16
|
+
* Validate rows for a write.
|
|
17
|
+
*
|
|
18
|
+
* @param rows - Candidate rows
|
|
19
|
+
* @returns The rows
|
|
20
|
+
* @throws GoogleWorkspaceError(400, validation) when empty, not rectangular-ish, or too many
|
|
21
|
+
*/
|
|
22
|
+
export function requireRows(rows) {
|
|
23
|
+
const CODES = GOOGLE_WORKSPACE_CONSTANTS.ERROR_CODES;
|
|
24
|
+
if (!Array.isArray(rows) || rows.length === 0)
|
|
25
|
+
throw new GoogleWorkspaceError(400, CODES.VALIDATION, '"rows" must be a non-empty array of arrays');
|
|
26
|
+
if (rows.length > GOOGLE_WORKSPACE_CONSTANTS.SHEETS_MAX_ROWS) {
|
|
27
|
+
throw new GoogleWorkspaceError(400, CODES.VALIDATION, `"rows" exceeds ${GOOGLE_WORKSPACE_CONSTANTS.SHEETS_MAX_ROWS}`);
|
|
28
|
+
}
|
|
29
|
+
for (const row of rows) {
|
|
30
|
+
if (!Array.isArray(row))
|
|
31
|
+
throw new GoogleWorkspaceError(400, CODES.VALIDATION, 'each row must be an array');
|
|
32
|
+
for (const cell of row) {
|
|
33
|
+
if (cell !== null && cell !== undefined && !['string', 'number', 'boolean'].includes(typeof cell)) {
|
|
34
|
+
throw new GoogleWorkspaceError(400, CODES.VALIDATION, 'cells must be strings, numbers or booleans');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return rows.map((row) => row.map((cell) => (cell === null || cell === undefined ? '' : cell)));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sheets: read, create, append, update.
|
|
42
|
+
*/
|
|
43
|
+
export class SheetsService {
|
|
44
|
+
deps;
|
|
45
|
+
base = GOOGLE_WORKSPACE_CONSTANTS.SHEETS_API_BASE;
|
|
46
|
+
/**
|
|
47
|
+
* @param deps - Token provider and optional fetch override
|
|
48
|
+
*/
|
|
49
|
+
constructor(deps) {
|
|
50
|
+
this.deps = deps;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* `spreadsheets.get` (properties only) — title and the sheet tabs.
|
|
54
|
+
*
|
|
55
|
+
* @param id - Spreadsheet id
|
|
56
|
+
* @returns Metadata
|
|
57
|
+
*/
|
|
58
|
+
async info(id) {
|
|
59
|
+
const spreadsheetId = requireId(id);
|
|
60
|
+
const wire = await googleRequest(this.deps, buildGoogleUrl(`${this.base}/spreadsheets/${encodeURIComponent(spreadsheetId)}`, {
|
|
61
|
+
fields: 'spreadsheetId,properties.title,spreadsheetUrl,sheets.properties(title,gridProperties(rowCount,columnCount))',
|
|
62
|
+
}));
|
|
63
|
+
return toInfo(wire);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* `spreadsheets.values.get`.
|
|
67
|
+
*
|
|
68
|
+
* @param id - Spreadsheet id
|
|
69
|
+
* @param range - A1 range (default `A1:Z1000` on the first sheet)
|
|
70
|
+
* @returns The values (rows may be ragged)
|
|
71
|
+
*/
|
|
72
|
+
async read(id, range) {
|
|
73
|
+
const spreadsheetId = requireId(id);
|
|
74
|
+
const a1 = (range ?? '').trim() || GOOGLE_WORKSPACE_CONSTANTS.SHEETS_DEFAULT_RANGE;
|
|
75
|
+
const data = await googleRequest(this.deps, buildGoogleUrl(`${this.base}/spreadsheets/${encodeURIComponent(spreadsheetId)}/values/${encodeURIComponent(a1)}`, {
|
|
76
|
+
valueRenderOption: 'UNFORMATTED_VALUE',
|
|
77
|
+
dateTimeRenderOption: 'FORMATTED_STRING',
|
|
78
|
+
}));
|
|
79
|
+
return { spreadsheetId, range: data.range ?? a1, rows: data.values ?? [] };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* `spreadsheets.create`, then `values.update` from A1 when rows are given.
|
|
83
|
+
*
|
|
84
|
+
* @param input - Title, first-sheet name, initial rows
|
|
85
|
+
* @returns The new spreadsheet
|
|
86
|
+
* @throws GoogleWorkspaceError(400, validation) without a title
|
|
87
|
+
*/
|
|
88
|
+
async create(input) {
|
|
89
|
+
const title = (input.title ?? '').trim();
|
|
90
|
+
if (!title)
|
|
91
|
+
throw new GoogleWorkspaceError(400, GOOGLE_WORKSPACE_CONSTANTS.ERROR_CODES.VALIDATION, '"title" is required');
|
|
92
|
+
const sheetTitle = (input.sheetTitle ?? '').trim() || 'Sheet1';
|
|
93
|
+
const created = await googleRequest(this.deps, `${this.base}/spreadsheets`, {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
body: { properties: { title }, sheets: [{ properties: { title: sheetTitle } }] },
|
|
96
|
+
});
|
|
97
|
+
const id = created.spreadsheetId ?? '';
|
|
98
|
+
if (input.rows && input.rows.length > 0) {
|
|
99
|
+
await this.update({ spreadsheetId: id, range: `${quoteSheet(sheetTitle)}!A1`, rows: input.rows });
|
|
100
|
+
}
|
|
101
|
+
return toInfo(created);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* `spreadsheets.values.append` — rows go below the last row of the
|
|
105
|
+
* table that contains `range` (default: the first sheet).
|
|
106
|
+
*
|
|
107
|
+
* @param input - Spreadsheet, range, rows
|
|
108
|
+
* @returns Updated range and counts
|
|
109
|
+
*/
|
|
110
|
+
async append(input) {
|
|
111
|
+
const spreadsheetId = requireId(input.spreadsheetId);
|
|
112
|
+
const rows = requireRows(input.rows);
|
|
113
|
+
const a1 = (input.range ?? '').trim() || 'A1';
|
|
114
|
+
const data = await googleRequest(this.deps, buildGoogleUrl(`${this.base}/spreadsheets/${encodeURIComponent(spreadsheetId)}/values/${encodeURIComponent(a1)}:append`, {
|
|
115
|
+
valueInputOption: 'USER_ENTERED',
|
|
116
|
+
insertDataOption: 'INSERT_ROWS',
|
|
117
|
+
}), { method: 'POST', body: { values: rows } });
|
|
118
|
+
return { spreadsheetId, updatedRange: data.updates?.updatedRange ?? a1, updatedRows: data.updates?.updatedRows ?? rows.length };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* `spreadsheets.values.update` — overwrite starting at `range`.
|
|
122
|
+
*
|
|
123
|
+
* @param input - Spreadsheet, anchor range, rows
|
|
124
|
+
* @returns Updated range and counts
|
|
125
|
+
*/
|
|
126
|
+
async update(input) {
|
|
127
|
+
const spreadsheetId = requireId(input.spreadsheetId);
|
|
128
|
+
const rows = requireRows(input.rows);
|
|
129
|
+
const a1 = (input.range ?? '').trim() || 'A1';
|
|
130
|
+
const data = await googleRequest(this.deps, buildGoogleUrl(`${this.base}/spreadsheets/${encodeURIComponent(spreadsheetId)}/values/${encodeURIComponent(a1)}`, {
|
|
131
|
+
valueInputOption: 'USER_ENTERED',
|
|
132
|
+
}), { method: 'PUT', body: { values: rows } });
|
|
133
|
+
return { spreadsheetId, updatedRange: data.updatedRange ?? a1, updatedRows: data.updatedRows ?? rows.length };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function toInfo(wire) {
|
|
137
|
+
const id = wire.spreadsheetId ?? '';
|
|
138
|
+
return {
|
|
139
|
+
id,
|
|
140
|
+
title: wire.properties?.title ?? '',
|
|
141
|
+
sheets: (wire.sheets ?? []).map((s) => ({
|
|
142
|
+
title: s.properties?.title ?? '',
|
|
143
|
+
...(s.properties?.gridProperties?.rowCount !== undefined ? { rowCount: s.properties.gridProperties.rowCount } : {}),
|
|
144
|
+
...(s.properties?.gridProperties?.columnCount !== undefined ? { columnCount: s.properties.gridProperties.columnCount } : {}),
|
|
145
|
+
})),
|
|
146
|
+
webViewLink: wire.spreadsheetUrl ?? `https://docs.google.com/spreadsheets/d/${id}/edit`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Quote a sheet name for an A1 range when it needs it.
|
|
151
|
+
*
|
|
152
|
+
* @param name - Sheet title
|
|
153
|
+
* @returns `'My Sheet'` or `Sheet1`
|
|
154
|
+
*/
|
|
155
|
+
export function quoteSheet(name) {
|
|
156
|
+
return /^[A-Za-z0-9_]+$/.test(name) ? name : `'${name.replace(/'/g, "''")}'`;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=sheets.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sheets.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/google/sheets.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SlidesService — Google Slides over the owner's Workspace grant.
|
|
3
|
+
*
|
|
4
|
+
* Reads any presentation the owner can see (`drive.readonly`) and builds
|
|
5
|
+
* new decks from an outline (`drive.file`). Each outline slide becomes a
|
|
6
|
+
* TITLE_AND_BODY slide with the title and bullet lines; the deck's default
|
|
7
|
+
* first slide is removed so the outline is the whole deck.
|
|
8
|
+
*
|
|
9
|
+
* @module services/google/slides.service
|
|
10
|
+
*/
|
|
11
|
+
import { type GoogleApiDeps } from './google-api.client.js';
|
|
12
|
+
/** One slide, flattened. */
|
|
13
|
+
export interface SlideText {
|
|
14
|
+
index: number;
|
|
15
|
+
objectId: string;
|
|
16
|
+
/** Every text line on the slide, in reading order. */
|
|
17
|
+
lines: string[];
|
|
18
|
+
notes?: string;
|
|
19
|
+
}
|
|
20
|
+
/** A presentation, flattened. */
|
|
21
|
+
export interface PresentationText {
|
|
22
|
+
id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
slideCount: number;
|
|
25
|
+
slides: SlideText[];
|
|
26
|
+
webViewLink: string;
|
|
27
|
+
}
|
|
28
|
+
/** One slide of an outline. */
|
|
29
|
+
export interface SlideOutline {
|
|
30
|
+
title: string;
|
|
31
|
+
/** Body lines; each becomes one paragraph (bulleting is the layout's default). */
|
|
32
|
+
bullets?: string[];
|
|
33
|
+
/** Speaker notes are not written (the Slides API needs the notes shape id) — kept for callers' bookkeeping. */
|
|
34
|
+
notes?: string;
|
|
35
|
+
}
|
|
36
|
+
/** Create input. */
|
|
37
|
+
export interface PresentationCreateInput {
|
|
38
|
+
title: string;
|
|
39
|
+
slides: SlideOutline[];
|
|
40
|
+
}
|
|
41
|
+
interface WireTextElement {
|
|
42
|
+
textRun?: {
|
|
43
|
+
content?: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
interface WirePageElement {
|
|
47
|
+
shape?: {
|
|
48
|
+
text?: {
|
|
49
|
+
textElements?: WireTextElement[];
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
table?: {
|
|
53
|
+
tableRows?: Array<{
|
|
54
|
+
tableCells?: Array<{
|
|
55
|
+
text?: {
|
|
56
|
+
textElements?: WireTextElement[];
|
|
57
|
+
};
|
|
58
|
+
}>;
|
|
59
|
+
}>;
|
|
60
|
+
};
|
|
61
|
+
elementGroup?: {
|
|
62
|
+
children?: WirePageElement[];
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
interface WirePage {
|
|
66
|
+
objectId?: string;
|
|
67
|
+
pageElements?: WirePageElement[];
|
|
68
|
+
slideProperties?: {
|
|
69
|
+
notesPage?: {
|
|
70
|
+
pageElements?: WirePageElement[];
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
interface WirePresentation {
|
|
75
|
+
presentationId?: string;
|
|
76
|
+
title?: string;
|
|
77
|
+
slides?: WirePage[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Lines of text in a page element (shape text, table cells, groups).
|
|
81
|
+
*
|
|
82
|
+
* @param el - Page element
|
|
83
|
+
* @returns Non-empty lines
|
|
84
|
+
*/
|
|
85
|
+
export declare function elementLines(el: WirePageElement): string[];
|
|
86
|
+
/**
|
|
87
|
+
* Flatten a presentation.
|
|
88
|
+
*
|
|
89
|
+
* @param wire - `presentations.get` payload
|
|
90
|
+
* @returns Title + slides as text
|
|
91
|
+
*/
|
|
92
|
+
export declare function toPresentationText(wire: WirePresentation): PresentationText;
|
|
93
|
+
/**
|
|
94
|
+
* The batchUpdate requests that turn an outline into slides.
|
|
95
|
+
*
|
|
96
|
+
* @param outline - Slides to add
|
|
97
|
+
* @param removeSlideId - Object id of the default slide to delete, if any
|
|
98
|
+
* @returns Requests in order (creates, inserts, then the delete)
|
|
99
|
+
*/
|
|
100
|
+
export declare function buildSlideRequests(outline: SlideOutline[], removeSlideId?: string): unknown[];
|
|
101
|
+
/**
|
|
102
|
+
* Slides: read, create from outline.
|
|
103
|
+
*/
|
|
104
|
+
export declare class SlidesService {
|
|
105
|
+
private readonly deps;
|
|
106
|
+
private readonly base;
|
|
107
|
+
/**
|
|
108
|
+
* @param deps - Token provider and optional fetch override
|
|
109
|
+
*/
|
|
110
|
+
constructor(deps: GoogleApiDeps);
|
|
111
|
+
/**
|
|
112
|
+
* `presentations.get`, flattened to text per slide.
|
|
113
|
+
*
|
|
114
|
+
* @param id - Presentation id
|
|
115
|
+
* @returns Title + slides
|
|
116
|
+
*/
|
|
117
|
+
read(id: string): Promise<PresentationText>;
|
|
118
|
+
/**
|
|
119
|
+
* `presentations.create` + one `batchUpdate` that adds every outline
|
|
120
|
+
* slide and removes the default blank one.
|
|
121
|
+
*
|
|
122
|
+
* @param input - Title and outline
|
|
123
|
+
* @returns The new deck, flattened (as read back from Google)
|
|
124
|
+
* @throws GoogleWorkspaceError(400, validation) without a title / slides, or too many slides
|
|
125
|
+
*/
|
|
126
|
+
create(input: PresentationCreateInput): Promise<PresentationText>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Browser link for a presentation.
|
|
130
|
+
*
|
|
131
|
+
* @param id - Presentation id
|
|
132
|
+
* @returns URL
|
|
133
|
+
*/
|
|
134
|
+
export declare function slidesLink(id: string): string;
|
|
135
|
+
export {};
|
|
136
|
+
//# sourceMappingURL=slides.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slides.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/google/slides.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SlidesService — Google Slides over the owner's Workspace grant.
|
|
3
|
+
*
|
|
4
|
+
* Reads any presentation the owner can see (`drive.readonly`) and builds
|
|
5
|
+
* new decks from an outline (`drive.file`). Each outline slide becomes a
|
|
6
|
+
* TITLE_AND_BODY slide with the title and bullet lines; the deck's default
|
|
7
|
+
* first slide is removed so the outline is the whole deck.
|
|
8
|
+
*
|
|
9
|
+
* @module services/google/slides.service
|
|
10
|
+
*/
|
|
11
|
+
import { GOOGLE_WORKSPACE_CONSTANTS } from '../../constants.js';
|
|
12
|
+
import { GoogleWorkspaceError } from './google-workspace-token.service.js';
|
|
13
|
+
import { buildGoogleUrl, googleRequest } from './google-api.client.js';
|
|
14
|
+
import { requireId } from './drive.service.js';
|
|
15
|
+
/**
|
|
16
|
+
* Lines of text in a page element (shape text, table cells, groups).
|
|
17
|
+
*
|
|
18
|
+
* @param el - Page element
|
|
19
|
+
* @returns Non-empty lines
|
|
20
|
+
*/
|
|
21
|
+
export function elementLines(el) {
|
|
22
|
+
const out = [];
|
|
23
|
+
const pushText = (elements) => {
|
|
24
|
+
const raw = (elements ?? []).map((t) => t.textRun?.content ?? '').join('');
|
|
25
|
+
for (const line of raw.split('\n'))
|
|
26
|
+
if (line.trim())
|
|
27
|
+
out.push(line.trim());
|
|
28
|
+
};
|
|
29
|
+
if (el.shape?.text)
|
|
30
|
+
pushText(el.shape.text.textElements);
|
|
31
|
+
for (const row of el.table?.tableRows ?? []) {
|
|
32
|
+
const cells = (row.tableCells ?? []).map((c) => (c.text?.textElements ?? []).map((t) => t.textRun?.content ?? '').join('').trim());
|
|
33
|
+
if (cells.some(Boolean))
|
|
34
|
+
out.push(cells.join(' | '));
|
|
35
|
+
}
|
|
36
|
+
for (const child of el.elementGroup?.children ?? [])
|
|
37
|
+
out.push(...elementLines(child));
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Flatten a presentation.
|
|
42
|
+
*
|
|
43
|
+
* @param wire - `presentations.get` payload
|
|
44
|
+
* @returns Title + slides as text
|
|
45
|
+
*/
|
|
46
|
+
export function toPresentationText(wire) {
|
|
47
|
+
const id = wire.presentationId ?? '';
|
|
48
|
+
const slides = (wire.slides ?? []).map((page, i) => {
|
|
49
|
+
const lines = (page.pageElements ?? []).flatMap(elementLines);
|
|
50
|
+
const notes = (page.slideProperties?.notesPage?.pageElements ?? []).flatMap(elementLines).join('\n');
|
|
51
|
+
return { index: i + 1, objectId: page.objectId ?? '', lines, ...(notes ? { notes } : {}) };
|
|
52
|
+
});
|
|
53
|
+
return { id, title: wire.title ?? '', slideCount: slides.length, slides, webViewLink: slidesLink(id) };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The batchUpdate requests that turn an outline into slides.
|
|
57
|
+
*
|
|
58
|
+
* @param outline - Slides to add
|
|
59
|
+
* @param removeSlideId - Object id of the default slide to delete, if any
|
|
60
|
+
* @returns Requests in order (creates, inserts, then the delete)
|
|
61
|
+
*/
|
|
62
|
+
export function buildSlideRequests(outline, removeSlideId) {
|
|
63
|
+
const requests = [];
|
|
64
|
+
outline.forEach((slide, i) => {
|
|
65
|
+
const slideId = `crewly_slide_${i + 1}`;
|
|
66
|
+
const titleId = `${slideId}_title`;
|
|
67
|
+
const bodyId = `${slideId}_body`;
|
|
68
|
+
requests.push({
|
|
69
|
+
createSlide: {
|
|
70
|
+
objectId: slideId,
|
|
71
|
+
insertionIndex: i,
|
|
72
|
+
slideLayoutReference: { predefinedLayout: 'TITLE_AND_BODY' },
|
|
73
|
+
placeholderIdMappings: [
|
|
74
|
+
{ layoutPlaceholder: { type: 'TITLE', index: 0 }, objectId: titleId },
|
|
75
|
+
{ layoutPlaceholder: { type: 'BODY', index: 0 }, objectId: bodyId },
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
const title = (slide.title ?? '').trim();
|
|
80
|
+
if (title)
|
|
81
|
+
requests.push({ insertText: { objectId: titleId, insertionIndex: 0, text: title } });
|
|
82
|
+
const body = (slide.bullets ?? []).map((b) => String(b).trim()).filter(Boolean).join('\n');
|
|
83
|
+
if (body)
|
|
84
|
+
requests.push({ insertText: { objectId: bodyId, insertionIndex: 0, text: body } });
|
|
85
|
+
});
|
|
86
|
+
if (removeSlideId)
|
|
87
|
+
requests.push({ deleteObject: { objectId: removeSlideId } });
|
|
88
|
+
return requests;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Slides: read, create from outline.
|
|
92
|
+
*/
|
|
93
|
+
export class SlidesService {
|
|
94
|
+
deps;
|
|
95
|
+
base = GOOGLE_WORKSPACE_CONSTANTS.SLIDES_API_BASE;
|
|
96
|
+
/**
|
|
97
|
+
* @param deps - Token provider and optional fetch override
|
|
98
|
+
*/
|
|
99
|
+
constructor(deps) {
|
|
100
|
+
this.deps = deps;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* `presentations.get`, flattened to text per slide.
|
|
104
|
+
*
|
|
105
|
+
* @param id - Presentation id
|
|
106
|
+
* @returns Title + slides
|
|
107
|
+
*/
|
|
108
|
+
async read(id) {
|
|
109
|
+
const presentationId = requireId(id);
|
|
110
|
+
const wire = await googleRequest(this.deps, buildGoogleUrl(`${this.base}/presentations/${encodeURIComponent(presentationId)}`, {
|
|
111
|
+
fields: 'presentationId,title,slides(objectId,pageElements,slideProperties.notesPage.pageElements)',
|
|
112
|
+
}));
|
|
113
|
+
return toPresentationText(wire);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* `presentations.create` + one `batchUpdate` that adds every outline
|
|
117
|
+
* slide and removes the default blank one.
|
|
118
|
+
*
|
|
119
|
+
* @param input - Title and outline
|
|
120
|
+
* @returns The new deck, flattened (as read back from Google)
|
|
121
|
+
* @throws GoogleWorkspaceError(400, validation) without a title / slides, or too many slides
|
|
122
|
+
*/
|
|
123
|
+
async create(input) {
|
|
124
|
+
const CODES = GOOGLE_WORKSPACE_CONSTANTS.ERROR_CODES;
|
|
125
|
+
const title = (input.title ?? '').trim();
|
|
126
|
+
if (!title)
|
|
127
|
+
throw new GoogleWorkspaceError(400, CODES.VALIDATION, '"title" is required');
|
|
128
|
+
const outline = Array.isArray(input.slides) ? input.slides.filter((s) => s && typeof s === 'object') : [];
|
|
129
|
+
if (outline.length === 0)
|
|
130
|
+
throw new GoogleWorkspaceError(400, CODES.VALIDATION, '"slides" must be a non-empty array of {title, bullets[]}');
|
|
131
|
+
if (outline.length > GOOGLE_WORKSPACE_CONSTANTS.SLIDES_MAX_SLIDES) {
|
|
132
|
+
throw new GoogleWorkspaceError(400, CODES.VALIDATION, `"slides" exceeds ${GOOGLE_WORKSPACE_CONSTANTS.SLIDES_MAX_SLIDES}`);
|
|
133
|
+
}
|
|
134
|
+
const created = await googleRequest(this.deps, `${this.base}/presentations`, { method: 'POST', body: { title } });
|
|
135
|
+
const presentationId = created.presentationId ?? '';
|
|
136
|
+
const defaultSlide = created.slides?.[0]?.objectId;
|
|
137
|
+
await googleRequest(this.deps, `${this.base}/presentations/${encodeURIComponent(presentationId)}:batchUpdate`, {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
body: { requests: buildSlideRequests(outline, defaultSlide) },
|
|
140
|
+
});
|
|
141
|
+
return this.read(presentationId);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Browser link for a presentation.
|
|
146
|
+
*
|
|
147
|
+
* @param id - Presentation id
|
|
148
|
+
* @returns URL
|
|
149
|
+
*/
|
|
150
|
+
export function slidesLink(id) {
|
|
151
|
+
return `https://docs.google.com/presentation/d/${id}/edit`;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=slides.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slides.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/google/slides.service.ts"],"names":[],"mappings":"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"}
|