@fourier-labs/harbour-contracts 0.104.0 → 0.105.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.
- package/contract-release.json +12 -12
- package/dist/gen-ts/contract-release.d.ts +6 -6
- package/dist/gen-ts/contract-release.js +6 -6
- package/dist/gen-ts/integration-execute-input-rules.d.ts +27 -9
- package/dist/gen-ts/integration-execute-input-rules.js +12 -9
- package/dist/gen-ts/integration-execute-request.d.ts +10 -6
- package/gen-ts/contract-release.ts +6 -6
- package/gen-ts/integration-execute-input-rules.ts +12 -9
- package/gen-ts/integration-execute-request.ts +10 -6
- package/package.json +1 -1
- package/schema/integration-execute-request.schema.json +9 -8
package/contract-release.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "harbour.contract-release/1.0",
|
|
3
|
-
"repositoryVersion": "0.
|
|
4
|
-
"publishVersion": "0.
|
|
5
|
-
"publishTag": "v0.
|
|
6
|
-
"schemasDigest": "sha256:
|
|
3
|
+
"repositoryVersion": "0.105.0.0",
|
|
4
|
+
"publishVersion": "0.105.0",
|
|
5
|
+
"publishTag": "v0.105.0",
|
|
6
|
+
"schemasDigest": "sha256:8844b241b17bcd774b50653cd753c379b432f5d67c49d69a36658bdfa1392c8e",
|
|
7
7
|
"aiFixturesDigest": "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6",
|
|
8
|
-
"generatedGoDigest": "sha256:
|
|
9
|
-
"generatedTypeScriptDigest": "sha256:
|
|
8
|
+
"generatedGoDigest": "sha256:4773bbad6c269967b7c9b1619340e78be54565979dfa7128902a79700038313d",
|
|
9
|
+
"generatedTypeScriptDigest": "sha256:dd162dae0a43118ff104a9fec66bd48b834c90de90c8dfc6bac5c6a8933e94a4",
|
|
10
10
|
"generatedExclusions": {
|
|
11
11
|
"gen-go/contract_release.go": "contains the generated aggregate digests (self-reference)",
|
|
12
12
|
"gen-ts/contract-release.ts": "contains the generated aggregate digests (self-reference)"
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"schema/identity-context.schema.json": "sha256:a0be3d3499b70f1641887ec66ab5b21ca5ab71063e9209290905738943a36e59",
|
|
59
59
|
"schema/integration-connect-result.schema.json": "sha256:3f254b67ef739dc1fa395bdc8c4dc93359b16d481dba2a2d777aa5b6f34b7af8",
|
|
60
60
|
"schema/integration-error-details.schema.json": "sha256:dd997865b6061c37e298a0ad8e7bcee6186422e04e3ccf63d24d64ef860036d3",
|
|
61
|
-
"schema/integration-execute-request.schema.json": "sha256:
|
|
61
|
+
"schema/integration-execute-request.schema.json": "sha256:c20616bcde10990be79cb40f4545c78e6d0c0bad4700098717ddf31ba43e536f",
|
|
62
62
|
"schema/integration-execute-result.schema.json": "sha256:057b787c28b5f7339a95184fafa5deb7ce2c6441254ad8423ce3238c7dd6996c",
|
|
63
63
|
"schema/internal-configuration-request.schema.json": "sha256:229d44a3458f68dceddfba802e5c1adcd95523a45af29b93b972706b88461a7d",
|
|
64
64
|
"schema/kit-bundle-manifest.schema.json": "sha256:d8fc6ca9c30d791e785b021687e084e9171c0ea1a3a176d0f1582fcccf2c0e19",
|
|
@@ -114,11 +114,11 @@
|
|
|
114
114
|
"gen-go/contracts_test.go": "sha256:56376287cb1548be1b35ab4c65d6f0b558c5d34885f7a47cb3d9e8d786607bf5",
|
|
115
115
|
"gen-go/doc.go": "sha256:316d610ce568597a2ce6f38e9782e34fa5920f6f51ab0866e6d061b8a41cd822",
|
|
116
116
|
"gen-go/error_category.go": "sha256:b2ddea22dcfa030f1a45e721e635485b0bd816cf1f38e7654628a653f5ed4583",
|
|
117
|
-
"gen-go/integration_execute_input_rules.go": "sha256:
|
|
118
|
-
"gen-go/integration_execute_input_rules_test.go": "sha256:
|
|
117
|
+
"gen-go/integration_execute_input_rules.go": "sha256:fb789d2875e701d5b3c57fc7f4c6a2df46beb6daa1faf4ccd1f1d4b8bb1dbe2a",
|
|
118
|
+
"gen-go/integration_execute_input_rules_test.go": "sha256:9d4b541fbbd8ba08a18af627ac0fae035b6bdf72d78885687fc99bff5e9cde01",
|
|
119
119
|
"gen-go/integration_operations.go": "sha256:a60ac73a7860b1b330b022ddde0c82e7ef146cc40ad22821013c8bdb73f9db28",
|
|
120
120
|
"gen-go/schema_documents.go": "sha256:c19c0d540f602ee997a84b90e20702d9bc63ed7d27aec267e76c04655fc182e0",
|
|
121
|
-
"gen-go/types.go": "sha256:
|
|
121
|
+
"gen-go/types.go": "sha256:9086b46f8bb308e0090b7f42bcff8add777dde1f7a66a798b369d04b2adbc7f8",
|
|
122
122
|
"gen-go/version_state.go": "sha256:4ef8abb126414c0025cf2c59bc9c79a74ef4a2ff89e8f316cbf05acc872371c1"
|
|
123
123
|
},
|
|
124
124
|
"generatedTypeScript": {
|
|
@@ -173,8 +173,8 @@
|
|
|
173
173
|
"gen-ts/index.ts": "sha256:5e903bb40fbf6599dc0d65171eaef7bc62b52d4cea090124a0c51ef876ad2909",
|
|
174
174
|
"gen-ts/integration-connect-result.ts": "sha256:4b10f169b6e1af364e9b418de21caa9fadbfb77b43cbff77d741cfae0b52180c",
|
|
175
175
|
"gen-ts/integration-error-details.ts": "sha256:ec082f2d302c1ef6c0bf768134c2bde4efa5fd5acf9cc0d3ccc4676e857ae43c",
|
|
176
|
-
"gen-ts/integration-execute-input-rules.ts": "sha256:
|
|
177
|
-
"gen-ts/integration-execute-request.ts": "sha256:
|
|
176
|
+
"gen-ts/integration-execute-input-rules.ts": "sha256:f82a02cd108037364a4c7ce91faf53dc5b8d9b6c5f3e2dbc7592f572ef9c7500",
|
|
177
|
+
"gen-ts/integration-execute-request.ts": "sha256:44eca4395eaf3597552f0c4405622bec0388bd3777d346a99d207d0bb5955bf0",
|
|
178
178
|
"gen-ts/integration-execute-result.ts": "sha256:e4772c27615de03284f7686aa49f92ff83b79a12587292129783443cf3ca0f80",
|
|
179
179
|
"gen-ts/integration-operations.ts": "sha256:fc468da58ab6177e755bb21b0907d7c0a510d6dd291476f0abc1c832c7820734",
|
|
180
180
|
"gen-ts/internal-configuration-request.ts": "sha256:7032437797473fa1c581d134adef2f3d4b4f4132f482cd17743e9d47097886bc",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export declare const ContractRepositoryVersion: "0.
|
|
2
|
-
export declare const ContractPublishVersion: "0.
|
|
3
|
-
export declare const ContractPublishTag: "v0.
|
|
4
|
-
export declare const ContractSchemasDigest: "sha256:
|
|
1
|
+
export declare const ContractRepositoryVersion: "0.105.0.0";
|
|
2
|
+
export declare const ContractPublishVersion: "0.105.0";
|
|
3
|
+
export declare const ContractPublishTag: "v0.105.0";
|
|
4
|
+
export declare const ContractSchemasDigest: "sha256:8844b241b17bcd774b50653cd753c379b432f5d67c49d69a36658bdfa1392c8e";
|
|
5
5
|
export declare const ContractAiFixturesDigest: "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
|
|
6
|
-
export declare const ContractGeneratedGoDigest: "sha256:
|
|
7
|
-
export declare const ContractGeneratedTypeScriptDigest: "sha256:
|
|
6
|
+
export declare const ContractGeneratedGoDigest: "sha256:4773bbad6c269967b7c9b1619340e78be54565979dfa7128902a79700038313d";
|
|
7
|
+
export declare const ContractGeneratedTypeScriptDigest: "sha256:dd162dae0a43118ff104a9fec66bd48b834c90de90c8dfc6bac5c6a8933e94a4";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
-
export const ContractRepositoryVersion = "0.
|
|
4
|
-
export const ContractPublishVersion = "0.
|
|
5
|
-
export const ContractPublishTag = "v0.
|
|
6
|
-
export const ContractSchemasDigest = "sha256:
|
|
3
|
+
export const ContractRepositoryVersion = "0.105.0.0";
|
|
4
|
+
export const ContractPublishVersion = "0.105.0";
|
|
5
|
+
export const ContractPublishTag = "v0.105.0";
|
|
6
|
+
export const ContractSchemasDigest = "sha256:8844b241b17bcd774b50653cd753c379b432f5d67c49d69a36658bdfa1392c8e";
|
|
7
7
|
export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
|
|
8
|
-
export const ContractGeneratedGoDigest = "sha256:
|
|
9
|
-
export const ContractGeneratedTypeScriptDigest = "sha256:
|
|
8
|
+
export const ContractGeneratedGoDigest = "sha256:4773bbad6c269967b7c9b1619340e78be54565979dfa7128902a79700038313d";
|
|
9
|
+
export const ContractGeneratedTypeScriptDigest = "sha256:dd162dae0a43118ff104a9fec66bd48b834c90de90c8dfc6bac5c6a8933e94a4";
|
|
@@ -342,21 +342,27 @@ export declare const IntegrationExecuteInputRules: {
|
|
|
342
342
|
readonly minItems: 1;
|
|
343
343
|
readonly maxItems: 5;
|
|
344
344
|
readonly fields: {
|
|
345
|
+
readonly file: {
|
|
346
|
+
readonly kind: "string";
|
|
347
|
+
readonly required: false;
|
|
348
|
+
readonly minLength: 1;
|
|
349
|
+
readonly maxLength: 1024;
|
|
350
|
+
};
|
|
345
351
|
readonly filename: {
|
|
346
352
|
readonly kind: "string";
|
|
347
|
-
readonly required:
|
|
353
|
+
readonly required: false;
|
|
348
354
|
readonly minLength: 1;
|
|
349
355
|
readonly maxLength: 255;
|
|
350
356
|
};
|
|
351
357
|
readonly contentType: {
|
|
352
358
|
readonly kind: "string";
|
|
353
|
-
readonly required:
|
|
359
|
+
readonly required: false;
|
|
354
360
|
readonly minLength: 1;
|
|
355
361
|
readonly maxLength: 128;
|
|
356
362
|
};
|
|
357
363
|
readonly dataBase64: {
|
|
358
364
|
readonly kind: "string";
|
|
359
|
-
readonly required:
|
|
365
|
+
readonly required: false;
|
|
360
366
|
readonly minLength: 1;
|
|
361
367
|
readonly maxLength: 2796203;
|
|
362
368
|
};
|
|
@@ -1484,21 +1490,27 @@ export declare const IntegrationExecuteInputRules: {
|
|
|
1484
1490
|
readonly minItems: 1;
|
|
1485
1491
|
readonly maxItems: 5;
|
|
1486
1492
|
readonly fields: {
|
|
1493
|
+
readonly file: {
|
|
1494
|
+
readonly kind: "string";
|
|
1495
|
+
readonly required: false;
|
|
1496
|
+
readonly minLength: 1;
|
|
1497
|
+
readonly maxLength: 1024;
|
|
1498
|
+
};
|
|
1487
1499
|
readonly filename: {
|
|
1488
1500
|
readonly kind: "string";
|
|
1489
|
-
readonly required:
|
|
1501
|
+
readonly required: false;
|
|
1490
1502
|
readonly minLength: 1;
|
|
1491
1503
|
readonly maxLength: 255;
|
|
1492
1504
|
};
|
|
1493
1505
|
readonly contentType: {
|
|
1494
1506
|
readonly kind: "string";
|
|
1495
|
-
readonly required:
|
|
1507
|
+
readonly required: false;
|
|
1496
1508
|
readonly minLength: 1;
|
|
1497
1509
|
readonly maxLength: 128;
|
|
1498
1510
|
};
|
|
1499
1511
|
readonly dataBase64: {
|
|
1500
1512
|
readonly kind: "string";
|
|
1501
|
-
readonly required:
|
|
1513
|
+
readonly required: false;
|
|
1502
1514
|
readonly minLength: 1;
|
|
1503
1515
|
readonly maxLength: 2796203;
|
|
1504
1516
|
};
|
|
@@ -1527,21 +1539,27 @@ export declare const IntegrationExecuteInputRules: {
|
|
|
1527
1539
|
readonly minItems: 1;
|
|
1528
1540
|
readonly maxItems: 5;
|
|
1529
1541
|
readonly fields: {
|
|
1542
|
+
readonly file: {
|
|
1543
|
+
readonly kind: "string";
|
|
1544
|
+
readonly required: false;
|
|
1545
|
+
readonly minLength: 1;
|
|
1546
|
+
readonly maxLength: 1024;
|
|
1547
|
+
};
|
|
1530
1548
|
readonly filename: {
|
|
1531
1549
|
readonly kind: "string";
|
|
1532
|
-
readonly required:
|
|
1550
|
+
readonly required: false;
|
|
1533
1551
|
readonly minLength: 1;
|
|
1534
1552
|
readonly maxLength: 255;
|
|
1535
1553
|
};
|
|
1536
1554
|
readonly contentType: {
|
|
1537
1555
|
readonly kind: "string";
|
|
1538
|
-
readonly required:
|
|
1556
|
+
readonly required: false;
|
|
1539
1557
|
readonly minLength: 1;
|
|
1540
1558
|
readonly maxLength: 128;
|
|
1541
1559
|
};
|
|
1542
1560
|
readonly dataBase64: {
|
|
1543
1561
|
readonly kind: "string";
|
|
1544
|
-
readonly required:
|
|
1562
|
+
readonly required: false;
|
|
1545
1563
|
readonly minLength: 1;
|
|
1546
1564
|
readonly maxLength: 2796203;
|
|
1547
1565
|
};
|
|
@@ -77,9 +77,10 @@ export const IntegrationExecuteInputRules = {
|
|
|
77
77
|
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
78
78
|
html: { kind: "string", required: false, minLength: 1, maxLength: 65536 },
|
|
79
79
|
attachments: { kind: "object[]", required: false, minItems: 1, maxItems: 5, fields: {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
file: { kind: "string", required: false, minLength: 1, maxLength: 1024 },
|
|
81
|
+
filename: { kind: "string", required: false, minLength: 1, maxLength: 255 },
|
|
82
|
+
contentType: { kind: "string", required: false, minLength: 1, maxLength: 128 },
|
|
83
|
+
dataBase64: { kind: "string", required: false, minLength: 1, maxLength: 2796203 },
|
|
83
84
|
} },
|
|
84
85
|
replyToMessageId: { kind: "string", required: false, minLength: 0, pattern: "^[A-Za-z0-9_-]{1,64}$" },
|
|
85
86
|
replyTo: { kind: "string", required: false, minLength: 3, maxLength: 254, pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$" },
|
|
@@ -461,9 +462,10 @@ export const IntegrationExecuteInputRules = {
|
|
|
461
462
|
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
462
463
|
html: { kind: "string", required: false, minLength: 1, maxLength: 65536 },
|
|
463
464
|
attachments: { kind: "object[]", required: false, minItems: 1, maxItems: 5, fields: {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
465
|
+
file: { kind: "string", required: false, minLength: 1, maxLength: 1024 },
|
|
466
|
+
filename: { kind: "string", required: false, minLength: 1, maxLength: 255 },
|
|
467
|
+
contentType: { kind: "string", required: false, minLength: 1, maxLength: 128 },
|
|
468
|
+
dataBase64: { kind: "string", required: false, minLength: 1, maxLength: 2796203 },
|
|
467
469
|
} },
|
|
468
470
|
},
|
|
469
471
|
},
|
|
@@ -473,9 +475,10 @@ export const IntegrationExecuteInputRules = {
|
|
|
473
475
|
messageId: { kind: "string", required: true, minLength: 0, pattern: "^[A-Za-z0-9._=-]{1,256}$" },
|
|
474
476
|
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
475
477
|
attachments: { kind: "object[]", required: false, minItems: 1, maxItems: 5, fields: {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
478
|
+
file: { kind: "string", required: false, minLength: 1, maxLength: 1024 },
|
|
479
|
+
filename: { kind: "string", required: false, minLength: 1, maxLength: 255 },
|
|
480
|
+
contentType: { kind: "string", required: false, minLength: 1, maxLength: 128 },
|
|
481
|
+
dataBase64: { kind: "string", required: false, minLength: 1, maxLength: 2796203 },
|
|
479
482
|
} },
|
|
480
483
|
},
|
|
481
484
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Body of POST /_harbour/integrations/execute (app SDK), forwarded unchanged to the governance development and runtime execute routes. Exactly one declared operation against one declared resource. The identity mode is fixed by the operation for every operation but the declared-mode Slack operations (slack.channel.history, slack.message.post, slack.message.update, slack.message.delete; 0.71.0 widened history), and always enforced from an approved grant; any identity/userId/scope field is rejected by additionalProperties. A declared-mode operation runs as the app (for a post: the IT-approved per-app name from the grant, plus a "Posted by <app> on Isomorph" footer) or as the signed-in person (no name, no footer) — the same input either way — and since 0.55.0 the call names which of the app's approved modes it runs under (`mode`): required when IT approved both modes for the app (MODE_REQUIRED otherwise), optional when it approved one (that one is used), and a named mode without an approved grant is refused (MODE_NOT_GRANTED) rather than served under the other, so approval order never decides what a call does. Per-operation input bounds (0.71.0, ADR 0018): slack.channel.history {limit? 1..100, default 15, cursor?, oldest?, latest?}; slack.message.post {text 1..4000 chars, threadId?, format? plain|mrkdwn} and a required idempotencyKey retained across retries of one explicit Send; slack.message.update {messageId, text, format?}; slack.message.delete {messageId}; slack.dm.send {to 1..8 addresses, text, format?} (app mode only) with a required idempotencyKey; slack.user.lookup {email}; gmail.thread.list {limit? 1..50, default 15, q?, labelIds? default [INBOX], pageToken?}; gmail.thread.read {threadId}; gmail.message.read {messageId}; gmail.attachment.read {messageId, attachmentId}; gmail.message.send {to? 1..50 addresses (to+cc+bcc <= 50 in total; since 0.104.0 `to` may be absent when cc or bcc names a recipient — governance refuses a send with no recipient at all), cc?, bcc?, subject 1..256 chars, text 1..4000 chars, html? <= 64 KiB, attachments? 1..5 of <= 2 MiB each as base64, replyToMessageId?, replyTo? (one address), fromName? 1..120 chars, inReplyTo? (an RFC Message-ID; not with replyToMessageId)} and a required idempotencyKey like slack.message.post; warehouse.view.read {columns? 1..32, where? 1..16 filters, groupBy? 1..8 columns, aggregates? 1..16, orderBy? 1..8, limit? 1..1000, default 100} (0.70.0: a plain read names columns, an aggregate read names aggregates and optionally groupBy; the cross-field rules — exactly one of columns or aggregates, groupBy needs aggregates, a filter value that fits its op, orderBy naming a returned column, every named column approved — are enforced by governance, not expressible here; ADR 0017). Basecamp (0.76.0, ADR 0022; every id a string of 1..20 digits, every operation user mode): basecamp.project.list {pageToken?, projectStatus? active|archived} on the fixed `account` alias; on a project alias basecamp.todolist.list {pageToken?}; basecamp.todo.list {todolistId, completed?, pageToken?}; basecamp.todo.create {todolistId, content 1..1000 (the to-do title), description? <= 16384, contentFormat? plain|html, dueOn? YYYY-MM-DD, assigneeIds? 1..20, notify?} with a required idempotencyKey; basecamp.todo.complete {todoId} (idempotent at Basecamp, so no key); basecamp.message.list {pageToken?}; basecamp.message.post {subject 1..256, content 1..65536, contentFormat?} with a required idempotencyKey; basecamp.comment.list {recordingId, pageToken?}; basecamp.comment.post {recordingId, content 1..16384, contentFormat?} with a required idempotencyKey; basecamp.campfire.post {content 1..4000, contentFormat?} with a required idempotencyKey; basecamp.people.list {pageToken?}. `pageToken` is the previous page's `nextPageToken` (a Basecamp page number as digits). SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id — GraphId, 1..256 characters — every operation user mode on a site alias): sharepoint.list.list {pageToken?}; sharepoint.item.list {listId, filter? 1..512 (an OData filter on `fields/...`), pageToken?}; sharepoint.item.create {listId, fields (1..32 columns, each a string <= 4000, a number, a boolean or null)} with a required idempotencyKey; sharepoint.item.update {listId, itemId, fields} (a PATCH, idempotent at Graph, so no key); sharepoint.drive.list {pageToken?}; sharepoint.file.list {driveId?, folderId?, pageToken?} (no driveId: the site's default drive; no folderId: its root); sharepoint.file.search {q 1..256, driveId?, pageToken?}; sharepoint.file.read {fileId, driveId?}; sharepoint.file.upload {filename 1..255, contentType, dataBase64 <= 2 MiB, driveId?, folderId?, conflict? rename|replace|fail} with a required idempotencyKey. A SharePoint `pageToken` is Graph's `$skiptoken` (up to 2048 characters; the shared field is that wide, the Gmail and Basecamp operations narrow it back to 256). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every operation user mode; the seven personal providers run on their fixed alias, teams.* on a channel alias): calendar.calendar.list {pageToken?}; calendar.event.list {calendarId?, timeMin, timeMax, q?, pageToken?} (RFC 3339 date-times with a zone); calendar.event.read {calendarId?, eventId}; calendar.event.create {calendarId?, title 1..256, start, end, timeZone?, allDay?, description? <= 8000, location? <= 512, attendees? 1..100 emails, notify?} with a required idempotencyKey; calendar.event.update {calendarId?, eventId, title?, start?, end?, timeZone?, description?, location?, attendees?, notify?} and calendar.event.cancel {calendarId?, eventId, notify?} (both idempotent at Google, so no key); drive.file.list {folderId?, pageToken?}; drive.file.search {q, pageToken?}; drive.file.read {fileId, exportAs? text|csv|pdf} (exportAs renders a Google Docs, Sheets or Slides file; a binary file is downloaded as is); drive.file.upload {filename, contentType, dataBase64 <= 2 MiB, folderId?, conflict?} and drive.folder.create {title, folderId?} with a required idempotencyKey; docs.document.read {documentId}; docs.document.create {title, text? <= 64 KiB} with a required idempotencyKey; docs.document.append {documentId, text <= 64 KiB} and docs.text.replace {documentId, replacements 1..64 of {find 1..512, replace <= 4000, matchCase?}} (no key: the app owns the document's state); sheets.spreadsheet.read {spreadsheetId}; sheets.values.read {spreadsheetId, range} (A1 notation, 1..256); sheets.values.append {spreadsheetId, range, values, valueInput? raw|user} with a required idempotencyKey; sheets.values.update {spreadsheetId, range, values, valueInput?} and sheets.values.clear {spreadsheetId, range} (no key); sheets.spreadsheet.create {title, sheets? 1..20 titles} with a required idempotencyKey (`values` is 1..1000 rows of 1..100 cells, each a string <= 4000, a number, a boolean or null); outlook.message.list {folder?, q?, pageToken?}; outlook.message.read {messageId}; outlook.attachment.read {messageId, attachmentId}; outlook.message.send {to, cc?, bcc?, subject, text 1..4000, html?, attachments?} and outlook.message.reply {messageId, text 1..4000, attachments?} (0.95.0: existing EmailAttachment bounds; recipients remain provider-resolved) with a required idempotencyKey like gmail.message.send; outlook.event.list {start, end, pageToken?}; outlook.event.read {eventId}; outlook.event.create (as calendar.event.create without calendarId) with a required idempotencyKey; outlook.event.update (as calendar.event.update without calendarId) and outlook.event.cancel {eventId, comment? <= 4000} (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read {fileId}, onedrive.file.upload and onedrive.folder.create as the drive.* operations (Graph ids, no exportAs); excel.worksheet.list {fileId, driveId?}; excel.range.read {fileId, driveId?, worksheet, address? 1..64} (no address: the used range); excel.range.update {fileId, driveId?, worksheet, address, values} (no key: a PATCH); excel.table.list {fileId, driveId?, worksheet?}; excel.row.add {fileId, driveId?, tableName, values} and excel.worksheet.add {fileId, driveId?, title} with a required idempotencyKey; teams.message.list {pageToken?}; teams.message.post {text 1..4000, html?} and teams.reply.post {messageId, text, html?} with a required idempotencyKey. An Outlook or Teams `messageId` is a Graph id (the shared field widens to ^[A-Za-z0-9._=-]{1,256}$; the Gmail and Slack operations narrow it back to ^[A-Za-z0-9._-]{1,64}$ in their then-blocks, so their rules are unchanged), and `text` widens to 64 KiB for the Docs body (every other operation that takes it narrows it back to 4000). Each operation's then-block is the one statement of its input rules: propertyNames.enum names the fields it accepts, required the ones it needs, and per-field overrides (maximum, default) narrow the shared property. Codegen derives IntegrationExecuteInputRules and validateIntegrationExecuteInput (gen-ts/integration-execute-input-rules.ts, gen-go/integration_execute_input_rules.go) from exactly these blocks, so the governance execute route, the app SDK and the kit's local check refuse the same request with the same message. Transport limits (not expressible here): request <= 16 KiB except a gmail.message.send carrying attachments (governance sizes that route for the attachment bound) and a basecamp.message.post (its 64 KiB body; governance allows 80 KiB there) and a sharepoint.file.upload, drive.file.upload or onedrive.file.upload (its 2 MiB file as base64; governance sizes it like the Gmail attachment route), an outlook.message.send or outlook.message.reply with attachments (the Gmail route), a docs.document.create or docs.document.append (its 64 KiB body; governance allows 80 KiB there, as for basecamp.message.post) and a sheets.values.append, sheets.values.update, excel.range.update or excel.row.add (its 64 KiB `values`), total deadline 10 s.
|
|
2
|
+
* Body of POST /_harbour/integrations/execute (app SDK), forwarded unchanged to the governance development and runtime execute routes. Exactly one declared operation against one declared resource. The identity mode is fixed by the operation for every operation but the declared-mode Slack operations (slack.channel.history, slack.message.post, slack.message.update, slack.message.delete; 0.71.0 widened history), and always enforced from an approved grant; any identity/userId/scope field is rejected by additionalProperties. A declared-mode operation runs as the app (for a post: the IT-approved per-app name from the grant, plus a "Posted by <app> on Isomorph" footer) or as the signed-in person (no name, no footer) — the same input either way — and since 0.55.0 the call names which of the app's approved modes it runs under (`mode`): required when IT approved both modes for the app (MODE_REQUIRED otherwise), optional when it approved one (that one is used), and a named mode without an approved grant is refused (MODE_NOT_GRANTED) rather than served under the other, so approval order never decides what a call does. Per-operation input bounds (0.71.0, ADR 0018): slack.channel.history {limit? 1..100, default 15, cursor?, oldest?, latest?}; slack.message.post {text 1..4000 chars, threadId?, format? plain|mrkdwn} and a required idempotencyKey retained across retries of one explicit Send; slack.message.update {messageId, text, format?}; slack.message.delete {messageId}; slack.dm.send {to 1..8 addresses, text, format?} (app mode only) with a required idempotencyKey; slack.user.lookup {email}; gmail.thread.list {limit? 1..50, default 15, q?, labelIds? default [INBOX], pageToken?}; gmail.thread.read {threadId}; gmail.message.read {messageId}; gmail.attachment.read {messageId, attachmentId}; gmail.message.send {to? 1..50 addresses (to+cc+bcc <= 50 in total; since 0.104.0 `to` may be absent when cc or bcc names a recipient — governance refuses a send with no recipient at all), cc?, bcc?, subject 1..256 chars, text 1..4000 chars, html? <= 64 KiB, attachments? 1..5, each {filename, contentType, dataBase64} (inline, 2 MiB in total) or since 0.105.0 {file, filename?, contentType?} (a private app file by path, deployed apps only; 18 MiB in total), replyToMessageId?, replyTo? (one address), fromName? 1..120 chars, inReplyTo? (an RFC Message-ID; not with replyToMessageId)} and a required idempotencyKey like slack.message.post; warehouse.view.read {columns? 1..32, where? 1..16 filters, groupBy? 1..8 columns, aggregates? 1..16, orderBy? 1..8, limit? 1..1000, default 100} (0.70.0: a plain read names columns, an aggregate read names aggregates and optionally groupBy; the cross-field rules — exactly one of columns or aggregates, groupBy needs aggregates, a filter value that fits its op, orderBy naming a returned column, every named column approved — are enforced by governance, not expressible here; ADR 0017). Basecamp (0.76.0, ADR 0022; every id a string of 1..20 digits, every operation user mode): basecamp.project.list {pageToken?, projectStatus? active|archived} on the fixed `account` alias; on a project alias basecamp.todolist.list {pageToken?}; basecamp.todo.list {todolistId, completed?, pageToken?}; basecamp.todo.create {todolistId, content 1..1000 (the to-do title), description? <= 16384, contentFormat? plain|html, dueOn? YYYY-MM-DD, assigneeIds? 1..20, notify?} with a required idempotencyKey; basecamp.todo.complete {todoId} (idempotent at Basecamp, so no key); basecamp.message.list {pageToken?}; basecamp.message.post {subject 1..256, content 1..65536, contentFormat?} with a required idempotencyKey; basecamp.comment.list {recordingId, pageToken?}; basecamp.comment.post {recordingId, content 1..16384, contentFormat?} with a required idempotencyKey; basecamp.campfire.post {content 1..4000, contentFormat?} with a required idempotencyKey; basecamp.people.list {pageToken?}. `pageToken` is the previous page's `nextPageToken` (a Basecamp page number as digits). SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id — GraphId, 1..256 characters — every operation user mode on a site alias): sharepoint.list.list {pageToken?}; sharepoint.item.list {listId, filter? 1..512 (an OData filter on `fields/...`), pageToken?}; sharepoint.item.create {listId, fields (1..32 columns, each a string <= 4000, a number, a boolean or null)} with a required idempotencyKey; sharepoint.item.update {listId, itemId, fields} (a PATCH, idempotent at Graph, so no key); sharepoint.drive.list {pageToken?}; sharepoint.file.list {driveId?, folderId?, pageToken?} (no driveId: the site's default drive; no folderId: its root); sharepoint.file.search {q 1..256, driveId?, pageToken?}; sharepoint.file.read {fileId, driveId?}; sharepoint.file.upload {filename 1..255, contentType, dataBase64 <= 2 MiB, driveId?, folderId?, conflict? rename|replace|fail} with a required idempotencyKey. A SharePoint `pageToken` is Graph's `$skiptoken` (up to 2048 characters; the shared field is that wide, the Gmail and Basecamp operations narrow it back to 256). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every operation user mode; the seven personal providers run on their fixed alias, teams.* on a channel alias): calendar.calendar.list {pageToken?}; calendar.event.list {calendarId?, timeMin, timeMax, q?, pageToken?} (RFC 3339 date-times with a zone); calendar.event.read {calendarId?, eventId}; calendar.event.create {calendarId?, title 1..256, start, end, timeZone?, allDay?, description? <= 8000, location? <= 512, attendees? 1..100 emails, notify?} with a required idempotencyKey; calendar.event.update {calendarId?, eventId, title?, start?, end?, timeZone?, description?, location?, attendees?, notify?} and calendar.event.cancel {calendarId?, eventId, notify?} (both idempotent at Google, so no key); drive.file.list {folderId?, pageToken?}; drive.file.search {q, pageToken?}; drive.file.read {fileId, exportAs? text|csv|pdf} (exportAs renders a Google Docs, Sheets or Slides file; a binary file is downloaded as is); drive.file.upload {filename, contentType, dataBase64 <= 2 MiB, folderId?, conflict?} and drive.folder.create {title, folderId?} with a required idempotencyKey; docs.document.read {documentId}; docs.document.create {title, text? <= 64 KiB} with a required idempotencyKey; docs.document.append {documentId, text <= 64 KiB} and docs.text.replace {documentId, replacements 1..64 of {find 1..512, replace <= 4000, matchCase?}} (no key: the app owns the document's state); sheets.spreadsheet.read {spreadsheetId}; sheets.values.read {spreadsheetId, range} (A1 notation, 1..256); sheets.values.append {spreadsheetId, range, values, valueInput? raw|user} with a required idempotencyKey; sheets.values.update {spreadsheetId, range, values, valueInput?} and sheets.values.clear {spreadsheetId, range} (no key); sheets.spreadsheet.create {title, sheets? 1..20 titles} with a required idempotencyKey (`values` is 1..1000 rows of 1..100 cells, each a string <= 4000, a number, a boolean or null); outlook.message.list {folder?, q?, pageToken?}; outlook.message.read {messageId}; outlook.attachment.read {messageId, attachmentId}; outlook.message.send {to, cc?, bcc?, subject, text 1..4000, html?, attachments?} and outlook.message.reply {messageId, text 1..4000, attachments?} (0.95.0: existing EmailAttachment bounds; recipients remain provider-resolved) with a required idempotencyKey like gmail.message.send; outlook.event.list {start, end, pageToken?}; outlook.event.read {eventId}; outlook.event.create (as calendar.event.create without calendarId) with a required idempotencyKey; outlook.event.update (as calendar.event.update without calendarId) and outlook.event.cancel {eventId, comment? <= 4000} (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read {fileId}, onedrive.file.upload and onedrive.folder.create as the drive.* operations (Graph ids, no exportAs); excel.worksheet.list {fileId, driveId?}; excel.range.read {fileId, driveId?, worksheet, address? 1..64} (no address: the used range); excel.range.update {fileId, driveId?, worksheet, address, values} (no key: a PATCH); excel.table.list {fileId, driveId?, worksheet?}; excel.row.add {fileId, driveId?, tableName, values} and excel.worksheet.add {fileId, driveId?, title} with a required idempotencyKey; teams.message.list {pageToken?}; teams.message.post {text 1..4000, html?} and teams.reply.post {messageId, text, html?} with a required idempotencyKey. An Outlook or Teams `messageId` is a Graph id (the shared field widens to ^[A-Za-z0-9._=-]{1,256}$; the Gmail and Slack operations narrow it back to ^[A-Za-z0-9._-]{1,64}$ in their then-blocks, so their rules are unchanged), and `text` widens to 64 KiB for the Docs body (every other operation that takes it narrows it back to 4000). Each operation's then-block is the one statement of its input rules: propertyNames.enum names the fields it accepts, required the ones it needs, and per-field overrides (maximum, default) narrow the shared property. Codegen derives IntegrationExecuteInputRules and validateIntegrationExecuteInput (gen-ts/integration-execute-input-rules.ts, gen-go/integration_execute_input_rules.go) from exactly these blocks, so the governance execute route, the app SDK and the kit's local check refuse the same request with the same message. Transport limits (not expressible here): request <= 16 KiB except a gmail.message.send carrying attachments (governance sizes that route for the attachment bound) and a basecamp.message.post (its 64 KiB body; governance allows 80 KiB there) and a sharepoint.file.upload, drive.file.upload or onedrive.file.upload (its 2 MiB file as base64; governance sizes it like the Gmail attachment route), an outlook.message.send or outlook.message.reply with attachments (the Gmail route), a docs.document.create or docs.document.append (its 64 KiB body; governance allows 80 KiB there, as for basecamp.message.post) and a sheets.values.append, sheets.values.update, excel.range.update or excel.row.add (its 64 KiB `values`), total deadline 10 s.
|
|
3
3
|
*/
|
|
4
4
|
export type IntegrationExecuteRequest = {
|
|
5
5
|
/**
|
|
@@ -98,7 +98,7 @@ export type IntegrationExecuteRequest = {
|
|
|
98
98
|
*/
|
|
99
99
|
html?: string;
|
|
100
100
|
/**
|
|
101
|
-
* gmail.message.send (0.71.0): files to attach, 1..5
|
|
101
|
+
* gmail.message.send (0.71.0): files to attach, 1..5; inline ones (`dataBase64`) total at most 2 MiB, and since 0.105.0 one may instead name a private app file (`file`), 18 MiB in total. outlook.message.send (0.86.0): inline files only, 1..5.
|
|
102
102
|
*
|
|
103
103
|
* @minItems 1
|
|
104
104
|
* @maxItems 5
|
|
@@ -461,21 +461,25 @@ export type WarehouseAggregateFn = 'count' | 'sum' | 'avg' | 'min' | 'max';
|
|
|
461
461
|
*/
|
|
462
462
|
export type WarehouseOrderDirection = 'asc' | 'desc';
|
|
463
463
|
/**
|
|
464
|
-
* One file attached to a gmail.message.send (0.71.0) or an outlook.message.send (0.86.0)
|
|
464
|
+
* One file attached to a gmail.message.send (0.71.0) or an outlook.message.send (0.86.0). Either carried inline as base64 — `filename`, `contentType` and `dataBase64`, at most 2 MiB of bytes, which is 2,796,203 base64 characters — or, on a gmail.message.send from a deployed app (0.105.0), named by `file`: the path of a private file of the app (isomorph.files), which the app's gateway checks the caller may read and hands to governance as a short-lived link, so no file bytes ride the request; `filename` and `contentType` then default to the file's own. Exactly one of `dataBase64` and `file`: a cross-field rule governance enforces (no field is required here). The Outlook sends take the inline form only.
|
|
465
465
|
*/
|
|
466
466
|
export interface EmailAttachment {
|
|
467
|
+
/**
|
|
468
|
+
* gmail.message.send (0.105.0): the path of one of the app's private files, as isomorph.files names it (e.g. letters/offer.pdf). The inline and referenced attachments of one email total at most 18 MiB decoded.
|
|
469
|
+
*/
|
|
470
|
+
file?: string;
|
|
467
471
|
/**
|
|
468
472
|
* The name the recipient sees.
|
|
469
473
|
*/
|
|
470
|
-
filename
|
|
474
|
+
filename?: string;
|
|
471
475
|
/**
|
|
472
476
|
* The MIME type, e.g. application/pdf.
|
|
473
477
|
*/
|
|
474
|
-
contentType
|
|
478
|
+
contentType?: string;
|
|
475
479
|
/**
|
|
476
480
|
* The file bytes, standard base64.
|
|
477
481
|
*/
|
|
478
|
-
dataBase64
|
|
482
|
+
dataBase64?: string;
|
|
479
483
|
}
|
|
480
484
|
/**
|
|
481
485
|
* One warehouse.view.read filter (0.70.0): <column> <op> <value>. The value is always bound as a query parameter, never written into the SQL. Which value an op takes is a governance rule: isNull/isNotNull take none; in/notIn take a non-empty list of one scalar type; every other op takes one scalar, and a boolean only with eq/ne.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
3
|
|
|
4
|
-
export const ContractRepositoryVersion = "0.
|
|
5
|
-
export const ContractPublishVersion = "0.
|
|
6
|
-
export const ContractPublishTag = "v0.
|
|
7
|
-
export const ContractSchemasDigest = "sha256:
|
|
4
|
+
export const ContractRepositoryVersion = "0.105.0.0" as const;
|
|
5
|
+
export const ContractPublishVersion = "0.105.0" as const;
|
|
6
|
+
export const ContractPublishTag = "v0.105.0" as const;
|
|
7
|
+
export const ContractSchemasDigest = "sha256:8844b241b17bcd774b50653cd753c379b432f5d67c49d69a36658bdfa1392c8e" as const;
|
|
8
8
|
export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6" as const;
|
|
9
|
-
export const ContractGeneratedGoDigest = "sha256:
|
|
10
|
-
export const ContractGeneratedTypeScriptDigest = "sha256:
|
|
9
|
+
export const ContractGeneratedGoDigest = "sha256:4773bbad6c269967b7c9b1619340e78be54565979dfa7128902a79700038313d" as const;
|
|
10
|
+
export const ContractGeneratedTypeScriptDigest = "sha256:dd162dae0a43118ff104a9fec66bd48b834c90de90c8dfc6bac5c6a8933e94a4" as const;
|
|
@@ -106,9 +106,10 @@ export const IntegrationExecuteInputRules = {
|
|
|
106
106
|
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
107
107
|
html: { kind: "string", required: false, minLength: 1, maxLength: 65536 },
|
|
108
108
|
attachments: { kind: "object[]", required: false, minItems: 1, maxItems: 5, fields: {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
file: { kind: "string", required: false, minLength: 1, maxLength: 1024 },
|
|
110
|
+
filename: { kind: "string", required: false, minLength: 1, maxLength: 255 },
|
|
111
|
+
contentType: { kind: "string", required: false, minLength: 1, maxLength: 128 },
|
|
112
|
+
dataBase64: { kind: "string", required: false, minLength: 1, maxLength: 2796203 },
|
|
112
113
|
} },
|
|
113
114
|
replyToMessageId: { kind: "string", required: false, minLength: 0, pattern: "^[A-Za-z0-9_-]{1,64}$" },
|
|
114
115
|
replyTo: { kind: "string", required: false, minLength: 3, maxLength: 254, pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$" },
|
|
@@ -490,9 +491,10 @@ export const IntegrationExecuteInputRules = {
|
|
|
490
491
|
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
491
492
|
html: { kind: "string", required: false, minLength: 1, maxLength: 65536 },
|
|
492
493
|
attachments: { kind: "object[]", required: false, minItems: 1, maxItems: 5, fields: {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
494
|
+
file: { kind: "string", required: false, minLength: 1, maxLength: 1024 },
|
|
495
|
+
filename: { kind: "string", required: false, minLength: 1, maxLength: 255 },
|
|
496
|
+
contentType: { kind: "string", required: false, minLength: 1, maxLength: 128 },
|
|
497
|
+
dataBase64: { kind: "string", required: false, minLength: 1, maxLength: 2796203 },
|
|
496
498
|
} },
|
|
497
499
|
},
|
|
498
500
|
},
|
|
@@ -502,9 +504,10 @@ export const IntegrationExecuteInputRules = {
|
|
|
502
504
|
messageId: { kind: "string", required: true, minLength: 0, pattern: "^[A-Za-z0-9._=-]{1,256}$" },
|
|
503
505
|
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
504
506
|
attachments: { kind: "object[]", required: false, minItems: 1, maxItems: 5, fields: {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
507
|
+
file: { kind: "string", required: false, minLength: 1, maxLength: 1024 },
|
|
508
|
+
filename: { kind: "string", required: false, minLength: 1, maxLength: 255 },
|
|
509
|
+
contentType: { kind: "string", required: false, minLength: 1, maxLength: 128 },
|
|
510
|
+
dataBase64: { kind: "string", required: false, minLength: 1, maxLength: 2796203 },
|
|
508
511
|
} },
|
|
509
512
|
},
|
|
510
513
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Body of POST /_harbour/integrations/execute (app SDK), forwarded unchanged to the governance development and runtime execute routes. Exactly one declared operation against one declared resource. The identity mode is fixed by the operation for every operation but the declared-mode Slack operations (slack.channel.history, slack.message.post, slack.message.update, slack.message.delete; 0.71.0 widened history), and always enforced from an approved grant; any identity/userId/scope field is rejected by additionalProperties. A declared-mode operation runs as the app (for a post: the IT-approved per-app name from the grant, plus a "Posted by <app> on Isomorph" footer) or as the signed-in person (no name, no footer) — the same input either way — and since 0.55.0 the call names which of the app's approved modes it runs under (`mode`): required when IT approved both modes for the app (MODE_REQUIRED otherwise), optional when it approved one (that one is used), and a named mode without an approved grant is refused (MODE_NOT_GRANTED) rather than served under the other, so approval order never decides what a call does. Per-operation input bounds (0.71.0, ADR 0018): slack.channel.history {limit? 1..100, default 15, cursor?, oldest?, latest?}; slack.message.post {text 1..4000 chars, threadId?, format? plain|mrkdwn} and a required idempotencyKey retained across retries of one explicit Send; slack.message.update {messageId, text, format?}; slack.message.delete {messageId}; slack.dm.send {to 1..8 addresses, text, format?} (app mode only) with a required idempotencyKey; slack.user.lookup {email}; gmail.thread.list {limit? 1..50, default 15, q?, labelIds? default [INBOX], pageToken?}; gmail.thread.read {threadId}; gmail.message.read {messageId}; gmail.attachment.read {messageId, attachmentId}; gmail.message.send {to? 1..50 addresses (to+cc+bcc <= 50 in total; since 0.104.0 `to` may be absent when cc or bcc names a recipient — governance refuses a send with no recipient at all), cc?, bcc?, subject 1..256 chars, text 1..4000 chars, html? <= 64 KiB, attachments? 1..5 of <= 2 MiB each as base64, replyToMessageId?, replyTo? (one address), fromName? 1..120 chars, inReplyTo? (an RFC Message-ID; not with replyToMessageId)} and a required idempotencyKey like slack.message.post; warehouse.view.read {columns? 1..32, where? 1..16 filters, groupBy? 1..8 columns, aggregates? 1..16, orderBy? 1..8, limit? 1..1000, default 100} (0.70.0: a plain read names columns, an aggregate read names aggregates and optionally groupBy; the cross-field rules — exactly one of columns or aggregates, groupBy needs aggregates, a filter value that fits its op, orderBy naming a returned column, every named column approved — are enforced by governance, not expressible here; ADR 0017). Basecamp (0.76.0, ADR 0022; every id a string of 1..20 digits, every operation user mode): basecamp.project.list {pageToken?, projectStatus? active|archived} on the fixed `account` alias; on a project alias basecamp.todolist.list {pageToken?}; basecamp.todo.list {todolistId, completed?, pageToken?}; basecamp.todo.create {todolistId, content 1..1000 (the to-do title), description? <= 16384, contentFormat? plain|html, dueOn? YYYY-MM-DD, assigneeIds? 1..20, notify?} with a required idempotencyKey; basecamp.todo.complete {todoId} (idempotent at Basecamp, so no key); basecamp.message.list {pageToken?}; basecamp.message.post {subject 1..256, content 1..65536, contentFormat?} with a required idempotencyKey; basecamp.comment.list {recordingId, pageToken?}; basecamp.comment.post {recordingId, content 1..16384, contentFormat?} with a required idempotencyKey; basecamp.campfire.post {content 1..4000, contentFormat?} with a required idempotencyKey; basecamp.people.list {pageToken?}. `pageToken` is the previous page's `nextPageToken` (a Basecamp page number as digits). SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id — GraphId, 1..256 characters — every operation user mode on a site alias): sharepoint.list.list {pageToken?}; sharepoint.item.list {listId, filter? 1..512 (an OData filter on `fields/...`), pageToken?}; sharepoint.item.create {listId, fields (1..32 columns, each a string <= 4000, a number, a boolean or null)} with a required idempotencyKey; sharepoint.item.update {listId, itemId, fields} (a PATCH, idempotent at Graph, so no key); sharepoint.drive.list {pageToken?}; sharepoint.file.list {driveId?, folderId?, pageToken?} (no driveId: the site's default drive; no folderId: its root); sharepoint.file.search {q 1..256, driveId?, pageToken?}; sharepoint.file.read {fileId, driveId?}; sharepoint.file.upload {filename 1..255, contentType, dataBase64 <= 2 MiB, driveId?, folderId?, conflict? rename|replace|fail} with a required idempotencyKey. A SharePoint `pageToken` is Graph's `$skiptoken` (up to 2048 characters; the shared field is that wide, the Gmail and Basecamp operations narrow it back to 256). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every operation user mode; the seven personal providers run on their fixed alias, teams.* on a channel alias): calendar.calendar.list {pageToken?}; calendar.event.list {calendarId?, timeMin, timeMax, q?, pageToken?} (RFC 3339 date-times with a zone); calendar.event.read {calendarId?, eventId}; calendar.event.create {calendarId?, title 1..256, start, end, timeZone?, allDay?, description? <= 8000, location? <= 512, attendees? 1..100 emails, notify?} with a required idempotencyKey; calendar.event.update {calendarId?, eventId, title?, start?, end?, timeZone?, description?, location?, attendees?, notify?} and calendar.event.cancel {calendarId?, eventId, notify?} (both idempotent at Google, so no key); drive.file.list {folderId?, pageToken?}; drive.file.search {q, pageToken?}; drive.file.read {fileId, exportAs? text|csv|pdf} (exportAs renders a Google Docs, Sheets or Slides file; a binary file is downloaded as is); drive.file.upload {filename, contentType, dataBase64 <= 2 MiB, folderId?, conflict?} and drive.folder.create {title, folderId?} with a required idempotencyKey; docs.document.read {documentId}; docs.document.create {title, text? <= 64 KiB} with a required idempotencyKey; docs.document.append {documentId, text <= 64 KiB} and docs.text.replace {documentId, replacements 1..64 of {find 1..512, replace <= 4000, matchCase?}} (no key: the app owns the document's state); sheets.spreadsheet.read {spreadsheetId}; sheets.values.read {spreadsheetId, range} (A1 notation, 1..256); sheets.values.append {spreadsheetId, range, values, valueInput? raw|user} with a required idempotencyKey; sheets.values.update {spreadsheetId, range, values, valueInput?} and sheets.values.clear {spreadsheetId, range} (no key); sheets.spreadsheet.create {title, sheets? 1..20 titles} with a required idempotencyKey (`values` is 1..1000 rows of 1..100 cells, each a string <= 4000, a number, a boolean or null); outlook.message.list {folder?, q?, pageToken?}; outlook.message.read {messageId}; outlook.attachment.read {messageId, attachmentId}; outlook.message.send {to, cc?, bcc?, subject, text 1..4000, html?, attachments?} and outlook.message.reply {messageId, text 1..4000, attachments?} (0.95.0: existing EmailAttachment bounds; recipients remain provider-resolved) with a required idempotencyKey like gmail.message.send; outlook.event.list {start, end, pageToken?}; outlook.event.read {eventId}; outlook.event.create (as calendar.event.create without calendarId) with a required idempotencyKey; outlook.event.update (as calendar.event.update without calendarId) and outlook.event.cancel {eventId, comment? <= 4000} (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read {fileId}, onedrive.file.upload and onedrive.folder.create as the drive.* operations (Graph ids, no exportAs); excel.worksheet.list {fileId, driveId?}; excel.range.read {fileId, driveId?, worksheet, address? 1..64} (no address: the used range); excel.range.update {fileId, driveId?, worksheet, address, values} (no key: a PATCH); excel.table.list {fileId, driveId?, worksheet?}; excel.row.add {fileId, driveId?, tableName, values} and excel.worksheet.add {fileId, driveId?, title} with a required idempotencyKey; teams.message.list {pageToken?}; teams.message.post {text 1..4000, html?} and teams.reply.post {messageId, text, html?} with a required idempotencyKey. An Outlook or Teams `messageId` is a Graph id (the shared field widens to ^[A-Za-z0-9._=-]{1,256}$; the Gmail and Slack operations narrow it back to ^[A-Za-z0-9._-]{1,64}$ in their then-blocks, so their rules are unchanged), and `text` widens to 64 KiB for the Docs body (every other operation that takes it narrows it back to 4000). Each operation's then-block is the one statement of its input rules: propertyNames.enum names the fields it accepts, required the ones it needs, and per-field overrides (maximum, default) narrow the shared property. Codegen derives IntegrationExecuteInputRules and validateIntegrationExecuteInput (gen-ts/integration-execute-input-rules.ts, gen-go/integration_execute_input_rules.go) from exactly these blocks, so the governance execute route, the app SDK and the kit's local check refuse the same request with the same message. Transport limits (not expressible here): request <= 16 KiB except a gmail.message.send carrying attachments (governance sizes that route for the attachment bound) and a basecamp.message.post (its 64 KiB body; governance allows 80 KiB there) and a sharepoint.file.upload, drive.file.upload or onedrive.file.upload (its 2 MiB file as base64; governance sizes it like the Gmail attachment route), an outlook.message.send or outlook.message.reply with attachments (the Gmail route), a docs.document.create or docs.document.append (its 64 KiB body; governance allows 80 KiB there, as for basecamp.message.post) and a sheets.values.append, sheets.values.update, excel.range.update or excel.row.add (its 64 KiB `values`), total deadline 10 s.
|
|
5
|
+
* Body of POST /_harbour/integrations/execute (app SDK), forwarded unchanged to the governance development and runtime execute routes. Exactly one declared operation against one declared resource. The identity mode is fixed by the operation for every operation but the declared-mode Slack operations (slack.channel.history, slack.message.post, slack.message.update, slack.message.delete; 0.71.0 widened history), and always enforced from an approved grant; any identity/userId/scope field is rejected by additionalProperties. A declared-mode operation runs as the app (for a post: the IT-approved per-app name from the grant, plus a "Posted by <app> on Isomorph" footer) or as the signed-in person (no name, no footer) — the same input either way — and since 0.55.0 the call names which of the app's approved modes it runs under (`mode`): required when IT approved both modes for the app (MODE_REQUIRED otherwise), optional when it approved one (that one is used), and a named mode without an approved grant is refused (MODE_NOT_GRANTED) rather than served under the other, so approval order never decides what a call does. Per-operation input bounds (0.71.0, ADR 0018): slack.channel.history {limit? 1..100, default 15, cursor?, oldest?, latest?}; slack.message.post {text 1..4000 chars, threadId?, format? plain|mrkdwn} and a required idempotencyKey retained across retries of one explicit Send; slack.message.update {messageId, text, format?}; slack.message.delete {messageId}; slack.dm.send {to 1..8 addresses, text, format?} (app mode only) with a required idempotencyKey; slack.user.lookup {email}; gmail.thread.list {limit? 1..50, default 15, q?, labelIds? default [INBOX], pageToken?}; gmail.thread.read {threadId}; gmail.message.read {messageId}; gmail.attachment.read {messageId, attachmentId}; gmail.message.send {to? 1..50 addresses (to+cc+bcc <= 50 in total; since 0.104.0 `to` may be absent when cc or bcc names a recipient — governance refuses a send with no recipient at all), cc?, bcc?, subject 1..256 chars, text 1..4000 chars, html? <= 64 KiB, attachments? 1..5, each {filename, contentType, dataBase64} (inline, 2 MiB in total) or since 0.105.0 {file, filename?, contentType?} (a private app file by path, deployed apps only; 18 MiB in total), replyToMessageId?, replyTo? (one address), fromName? 1..120 chars, inReplyTo? (an RFC Message-ID; not with replyToMessageId)} and a required idempotencyKey like slack.message.post; warehouse.view.read {columns? 1..32, where? 1..16 filters, groupBy? 1..8 columns, aggregates? 1..16, orderBy? 1..8, limit? 1..1000, default 100} (0.70.0: a plain read names columns, an aggregate read names aggregates and optionally groupBy; the cross-field rules — exactly one of columns or aggregates, groupBy needs aggregates, a filter value that fits its op, orderBy naming a returned column, every named column approved — are enforced by governance, not expressible here; ADR 0017). Basecamp (0.76.0, ADR 0022; every id a string of 1..20 digits, every operation user mode): basecamp.project.list {pageToken?, projectStatus? active|archived} on the fixed `account` alias; on a project alias basecamp.todolist.list {pageToken?}; basecamp.todo.list {todolistId, completed?, pageToken?}; basecamp.todo.create {todolistId, content 1..1000 (the to-do title), description? <= 16384, contentFormat? plain|html, dueOn? YYYY-MM-DD, assigneeIds? 1..20, notify?} with a required idempotencyKey; basecamp.todo.complete {todoId} (idempotent at Basecamp, so no key); basecamp.message.list {pageToken?}; basecamp.message.post {subject 1..256, content 1..65536, contentFormat?} with a required idempotencyKey; basecamp.comment.list {recordingId, pageToken?}; basecamp.comment.post {recordingId, content 1..16384, contentFormat?} with a required idempotencyKey; basecamp.campfire.post {content 1..4000, contentFormat?} with a required idempotencyKey; basecamp.people.list {pageToken?}. `pageToken` is the previous page's `nextPageToken` (a Basecamp page number as digits). SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id — GraphId, 1..256 characters — every operation user mode on a site alias): sharepoint.list.list {pageToken?}; sharepoint.item.list {listId, filter? 1..512 (an OData filter on `fields/...`), pageToken?}; sharepoint.item.create {listId, fields (1..32 columns, each a string <= 4000, a number, a boolean or null)} with a required idempotencyKey; sharepoint.item.update {listId, itemId, fields} (a PATCH, idempotent at Graph, so no key); sharepoint.drive.list {pageToken?}; sharepoint.file.list {driveId?, folderId?, pageToken?} (no driveId: the site's default drive; no folderId: its root); sharepoint.file.search {q 1..256, driveId?, pageToken?}; sharepoint.file.read {fileId, driveId?}; sharepoint.file.upload {filename 1..255, contentType, dataBase64 <= 2 MiB, driveId?, folderId?, conflict? rename|replace|fail} with a required idempotencyKey. A SharePoint `pageToken` is Graph's `$skiptoken` (up to 2048 characters; the shared field is that wide, the Gmail and Basecamp operations narrow it back to 256). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every operation user mode; the seven personal providers run on their fixed alias, teams.* on a channel alias): calendar.calendar.list {pageToken?}; calendar.event.list {calendarId?, timeMin, timeMax, q?, pageToken?} (RFC 3339 date-times with a zone); calendar.event.read {calendarId?, eventId}; calendar.event.create {calendarId?, title 1..256, start, end, timeZone?, allDay?, description? <= 8000, location? <= 512, attendees? 1..100 emails, notify?} with a required idempotencyKey; calendar.event.update {calendarId?, eventId, title?, start?, end?, timeZone?, description?, location?, attendees?, notify?} and calendar.event.cancel {calendarId?, eventId, notify?} (both idempotent at Google, so no key); drive.file.list {folderId?, pageToken?}; drive.file.search {q, pageToken?}; drive.file.read {fileId, exportAs? text|csv|pdf} (exportAs renders a Google Docs, Sheets or Slides file; a binary file is downloaded as is); drive.file.upload {filename, contentType, dataBase64 <= 2 MiB, folderId?, conflict?} and drive.folder.create {title, folderId?} with a required idempotencyKey; docs.document.read {documentId}; docs.document.create {title, text? <= 64 KiB} with a required idempotencyKey; docs.document.append {documentId, text <= 64 KiB} and docs.text.replace {documentId, replacements 1..64 of {find 1..512, replace <= 4000, matchCase?}} (no key: the app owns the document's state); sheets.spreadsheet.read {spreadsheetId}; sheets.values.read {spreadsheetId, range} (A1 notation, 1..256); sheets.values.append {spreadsheetId, range, values, valueInput? raw|user} with a required idempotencyKey; sheets.values.update {spreadsheetId, range, values, valueInput?} and sheets.values.clear {spreadsheetId, range} (no key); sheets.spreadsheet.create {title, sheets? 1..20 titles} with a required idempotencyKey (`values` is 1..1000 rows of 1..100 cells, each a string <= 4000, a number, a boolean or null); outlook.message.list {folder?, q?, pageToken?}; outlook.message.read {messageId}; outlook.attachment.read {messageId, attachmentId}; outlook.message.send {to, cc?, bcc?, subject, text 1..4000, html?, attachments?} and outlook.message.reply {messageId, text 1..4000, attachments?} (0.95.0: existing EmailAttachment bounds; recipients remain provider-resolved) with a required idempotencyKey like gmail.message.send; outlook.event.list {start, end, pageToken?}; outlook.event.read {eventId}; outlook.event.create (as calendar.event.create without calendarId) with a required idempotencyKey; outlook.event.update (as calendar.event.update without calendarId) and outlook.event.cancel {eventId, comment? <= 4000} (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read {fileId}, onedrive.file.upload and onedrive.folder.create as the drive.* operations (Graph ids, no exportAs); excel.worksheet.list {fileId, driveId?}; excel.range.read {fileId, driveId?, worksheet, address? 1..64} (no address: the used range); excel.range.update {fileId, driveId?, worksheet, address, values} (no key: a PATCH); excel.table.list {fileId, driveId?, worksheet?}; excel.row.add {fileId, driveId?, tableName, values} and excel.worksheet.add {fileId, driveId?, title} with a required idempotencyKey; teams.message.list {pageToken?}; teams.message.post {text 1..4000, html?} and teams.reply.post {messageId, text, html?} with a required idempotencyKey. An Outlook or Teams `messageId` is a Graph id (the shared field widens to ^[A-Za-z0-9._=-]{1,256}$; the Gmail and Slack operations narrow it back to ^[A-Za-z0-9._-]{1,64}$ in their then-blocks, so their rules are unchanged), and `text` widens to 64 KiB for the Docs body (every other operation that takes it narrows it back to 4000). Each operation's then-block is the one statement of its input rules: propertyNames.enum names the fields it accepts, required the ones it needs, and per-field overrides (maximum, default) narrow the shared property. Codegen derives IntegrationExecuteInputRules and validateIntegrationExecuteInput (gen-ts/integration-execute-input-rules.ts, gen-go/integration_execute_input_rules.go) from exactly these blocks, so the governance execute route, the app SDK and the kit's local check refuse the same request with the same message. Transport limits (not expressible here): request <= 16 KiB except a gmail.message.send carrying attachments (governance sizes that route for the attachment bound) and a basecamp.message.post (its 64 KiB body; governance allows 80 KiB there) and a sharepoint.file.upload, drive.file.upload or onedrive.file.upload (its 2 MiB file as base64; governance sizes it like the Gmail attachment route), an outlook.message.send or outlook.message.reply with attachments (the Gmail route), a docs.document.create or docs.document.append (its 64 KiB body; governance allows 80 KiB there, as for basecamp.message.post) and a sheets.values.append, sheets.values.update, excel.range.update or excel.row.add (its 64 KiB `values`), total deadline 10 s.
|
|
6
6
|
*/
|
|
7
7
|
export type IntegrationExecuteRequest = {
|
|
8
8
|
/**
|
|
@@ -197,7 +197,7 @@ export type IntegrationExecuteRequest = {
|
|
|
197
197
|
*/
|
|
198
198
|
html?: string;
|
|
199
199
|
/**
|
|
200
|
-
* gmail.message.send (0.71.0): files to attach, 1..5
|
|
200
|
+
* gmail.message.send (0.71.0): files to attach, 1..5; inline ones (`dataBase64`) total at most 2 MiB, and since 0.105.0 one may instead name a private app file (`file`), 18 MiB in total. outlook.message.send (0.86.0): inline files only, 1..5.
|
|
201
201
|
*
|
|
202
202
|
* @minItems 1
|
|
203
203
|
* @maxItems 5
|
|
@@ -561,21 +561,25 @@ export type WarehouseAggregateFn = 'count' | 'sum' | 'avg' | 'min' | 'max';
|
|
|
561
561
|
export type WarehouseOrderDirection = 'asc' | 'desc';
|
|
562
562
|
|
|
563
563
|
/**
|
|
564
|
-
* One file attached to a gmail.message.send (0.71.0) or an outlook.message.send (0.86.0)
|
|
564
|
+
* One file attached to a gmail.message.send (0.71.0) or an outlook.message.send (0.86.0). Either carried inline as base64 — `filename`, `contentType` and `dataBase64`, at most 2 MiB of bytes, which is 2,796,203 base64 characters — or, on a gmail.message.send from a deployed app (0.105.0), named by `file`: the path of a private file of the app (isomorph.files), which the app's gateway checks the caller may read and hands to governance as a short-lived link, so no file bytes ride the request; `filename` and `contentType` then default to the file's own. Exactly one of `dataBase64` and `file`: a cross-field rule governance enforces (no field is required here). The Outlook sends take the inline form only.
|
|
565
565
|
*/
|
|
566
566
|
export interface EmailAttachment {
|
|
567
|
+
/**
|
|
568
|
+
* gmail.message.send (0.105.0): the path of one of the app's private files, as isomorph.files names it (e.g. letters/offer.pdf). The inline and referenced attachments of one email total at most 18 MiB decoded.
|
|
569
|
+
*/
|
|
570
|
+
file?: string;
|
|
567
571
|
/**
|
|
568
572
|
* The name the recipient sees.
|
|
569
573
|
*/
|
|
570
|
-
filename
|
|
574
|
+
filename?: string;
|
|
571
575
|
/**
|
|
572
576
|
* The MIME type, e.g. application/pdf.
|
|
573
577
|
*/
|
|
574
|
-
contentType
|
|
578
|
+
contentType?: string;
|
|
575
579
|
/**
|
|
576
580
|
* The file bytes, standard base64.
|
|
577
581
|
*/
|
|
578
|
-
dataBase64
|
|
582
|
+
dataBase64?: string;
|
|
579
583
|
}
|
|
580
584
|
/**
|
|
581
585
|
* One warehouse.view.read filter (0.70.0): <column> <op> <value>. The value is always bound as a query parameter, never written into the SQL. Which value an op takes is a governance rule: isNull/isNotNull take none; in/notIn take a non-empty list of one scalar type; every other op takes one scalar, and a boolean only with eq/ne.
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://schemas.harbour.dev/integration-execute-request.schema.json",
|
|
4
4
|
"title": "IntegrationExecuteRequest",
|
|
5
|
-
"description": "Body of POST /_harbour/integrations/execute (app SDK), forwarded unchanged to the governance development and runtime execute routes. Exactly one declared operation against one declared resource. The identity mode is fixed by the operation for every operation but the declared-mode Slack operations (slack.channel.history, slack.message.post, slack.message.update, slack.message.delete; 0.71.0 widened history), and always enforced from an approved grant; any identity/userId/scope field is rejected by additionalProperties. A declared-mode operation runs as the app (for a post: the IT-approved per-app name from the grant, plus a \"Posted by <app> on Isomorph\" footer) or as the signed-in person (no name, no footer) \u2014 the same input either way \u2014 and since 0.55.0 the call names which of the app's approved modes it runs under (`mode`): required when IT approved both modes for the app (MODE_REQUIRED otherwise), optional when it approved one (that one is used), and a named mode without an approved grant is refused (MODE_NOT_GRANTED) rather than served under the other, so approval order never decides what a call does. Per-operation input bounds (0.71.0, ADR 0018): slack.channel.history {limit? 1..100, default 15, cursor?, oldest?, latest?}; slack.message.post {text 1..4000 chars, threadId?, format? plain|mrkdwn} and a required idempotencyKey retained across retries of one explicit Send; slack.message.update {messageId, text, format?}; slack.message.delete {messageId}; slack.dm.send {to 1..8 addresses, text, format?} (app mode only) with a required idempotencyKey; slack.user.lookup {email}; gmail.thread.list {limit? 1..50, default 15, q?, labelIds? default [INBOX], pageToken?}; gmail.thread.read {threadId}; gmail.message.read {messageId}; gmail.attachment.read {messageId, attachmentId}; gmail.message.send {to? 1..50 addresses (to+cc+bcc <= 50 in total; since 0.104.0 `to` may be absent when cc or bcc names a recipient \u2014 governance refuses a send with no recipient at all), cc?, bcc?, subject 1..256 chars, text 1..4000 chars, html? <= 64 KiB, attachments? 1..5 of <= 2 MiB each as base64, replyToMessageId?, replyTo? (one address), fromName? 1..120 chars, inReplyTo? (an RFC Message-ID; not with replyToMessageId)} and a required idempotencyKey like slack.message.post; warehouse.view.read {columns? 1..32, where? 1..16 filters, groupBy? 1..8 columns, aggregates? 1..16, orderBy? 1..8, limit? 1..1000, default 100} (0.70.0: a plain read names columns, an aggregate read names aggregates and optionally groupBy; the cross-field rules \u2014 exactly one of columns or aggregates, groupBy needs aggregates, a filter value that fits its op, orderBy naming a returned column, every named column approved \u2014 are enforced by governance, not expressible here; ADR 0017). Basecamp (0.76.0, ADR 0022; every id a string of 1..20 digits, every operation user mode): basecamp.project.list {pageToken?, projectStatus? active|archived} on the fixed `account` alias; on a project alias basecamp.todolist.list {pageToken?}; basecamp.todo.list {todolistId, completed?, pageToken?}; basecamp.todo.create {todolistId, content 1..1000 (the to-do title), description? <= 16384, contentFormat? plain|html, dueOn? YYYY-MM-DD, assigneeIds? 1..20, notify?} with a required idempotencyKey; basecamp.todo.complete {todoId} (idempotent at Basecamp, so no key); basecamp.message.list {pageToken?}; basecamp.message.post {subject 1..256, content 1..65536, contentFormat?} with a required idempotencyKey; basecamp.comment.list {recordingId, pageToken?}; basecamp.comment.post {recordingId, content 1..16384, contentFormat?} with a required idempotencyKey; basecamp.campfire.post {content 1..4000, contentFormat?} with a required idempotencyKey; basecamp.people.list {pageToken?}. `pageToken` is the previous page's `nextPageToken` (a Basecamp page number as digits). SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id \u2014 GraphId, 1..256 characters \u2014 every operation user mode on a site alias): sharepoint.list.list {pageToken?}; sharepoint.item.list {listId, filter? 1..512 (an OData filter on `fields/...`), pageToken?}; sharepoint.item.create {listId, fields (1..32 columns, each a string <= 4000, a number, a boolean or null)} with a required idempotencyKey; sharepoint.item.update {listId, itemId, fields} (a PATCH, idempotent at Graph, so no key); sharepoint.drive.list {pageToken?}; sharepoint.file.list {driveId?, folderId?, pageToken?} (no driveId: the site's default drive; no folderId: its root); sharepoint.file.search {q 1..256, driveId?, pageToken?}; sharepoint.file.read {fileId, driveId?}; sharepoint.file.upload {filename 1..255, contentType, dataBase64 <= 2 MiB, driveId?, folderId?, conflict? rename|replace|fail} with a required idempotencyKey. A SharePoint `pageToken` is Graph's `$skiptoken` (up to 2048 characters; the shared field is that wide, the Gmail and Basecamp operations narrow it back to 256). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every operation user mode; the seven personal providers run on their fixed alias, teams.* on a channel alias): calendar.calendar.list {pageToken?}; calendar.event.list {calendarId?, timeMin, timeMax, q?, pageToken?} (RFC 3339 date-times with a zone); calendar.event.read {calendarId?, eventId}; calendar.event.create {calendarId?, title 1..256, start, end, timeZone?, allDay?, description? <= 8000, location? <= 512, attendees? 1..100 emails, notify?} with a required idempotencyKey; calendar.event.update {calendarId?, eventId, title?, start?, end?, timeZone?, description?, location?, attendees?, notify?} and calendar.event.cancel {calendarId?, eventId, notify?} (both idempotent at Google, so no key); drive.file.list {folderId?, pageToken?}; drive.file.search {q, pageToken?}; drive.file.read {fileId, exportAs? text|csv|pdf} (exportAs renders a Google Docs, Sheets or Slides file; a binary file is downloaded as is); drive.file.upload {filename, contentType, dataBase64 <= 2 MiB, folderId?, conflict?} and drive.folder.create {title, folderId?} with a required idempotencyKey; docs.document.read {documentId}; docs.document.create {title, text? <= 64 KiB} with a required idempotencyKey; docs.document.append {documentId, text <= 64 KiB} and docs.text.replace {documentId, replacements 1..64 of {find 1..512, replace <= 4000, matchCase?}} (no key: the app owns the document's state); sheets.spreadsheet.read {spreadsheetId}; sheets.values.read {spreadsheetId, range} (A1 notation, 1..256); sheets.values.append {spreadsheetId, range, values, valueInput? raw|user} with a required idempotencyKey; sheets.values.update {spreadsheetId, range, values, valueInput?} and sheets.values.clear {spreadsheetId, range} (no key); sheets.spreadsheet.create {title, sheets? 1..20 titles} with a required idempotencyKey (`values` is 1..1000 rows of 1..100 cells, each a string <= 4000, a number, a boolean or null); outlook.message.list {folder?, q?, pageToken?}; outlook.message.read {messageId}; outlook.attachment.read {messageId, attachmentId}; outlook.message.send {to, cc?, bcc?, subject, text 1..4000, html?, attachments?} and outlook.message.reply {messageId, text 1..4000, attachments?} (0.95.0: existing EmailAttachment bounds; recipients remain provider-resolved) with a required idempotencyKey like gmail.message.send; outlook.event.list {start, end, pageToken?}; outlook.event.read {eventId}; outlook.event.create (as calendar.event.create without calendarId) with a required idempotencyKey; outlook.event.update (as calendar.event.update without calendarId) and outlook.event.cancel {eventId, comment? <= 4000} (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read {fileId}, onedrive.file.upload and onedrive.folder.create as the drive.* operations (Graph ids, no exportAs); excel.worksheet.list {fileId, driveId?}; excel.range.read {fileId, driveId?, worksheet, address? 1..64} (no address: the used range); excel.range.update {fileId, driveId?, worksheet, address, values} (no key: a PATCH); excel.table.list {fileId, driveId?, worksheet?}; excel.row.add {fileId, driveId?, tableName, values} and excel.worksheet.add {fileId, driveId?, title} with a required idempotencyKey; teams.message.list {pageToken?}; teams.message.post {text 1..4000, html?} and teams.reply.post {messageId, text, html?} with a required idempotencyKey. An Outlook or Teams `messageId` is a Graph id (the shared field widens to ^[A-Za-z0-9._=-]{1,256}$; the Gmail and Slack operations narrow it back to ^[A-Za-z0-9._-]{1,64}$ in their then-blocks, so their rules are unchanged), and `text` widens to 64 KiB for the Docs body (every other operation that takes it narrows it back to 4000). Each operation's then-block is the one statement of its input rules: propertyNames.enum names the fields it accepts, required the ones it needs, and per-field overrides (maximum, default) narrow the shared property. Codegen derives IntegrationExecuteInputRules and validateIntegrationExecuteInput (gen-ts/integration-execute-input-rules.ts, gen-go/integration_execute_input_rules.go) from exactly these blocks, so the governance execute route, the app SDK and the kit's local check refuse the same request with the same message. Transport limits (not expressible here): request <= 16 KiB except a gmail.message.send carrying attachments (governance sizes that route for the attachment bound) and a basecamp.message.post (its 64 KiB body; governance allows 80 KiB there) and a sharepoint.file.upload, drive.file.upload or onedrive.file.upload (its 2 MiB file as base64; governance sizes it like the Gmail attachment route), an outlook.message.send or outlook.message.reply with attachments (the Gmail route), a docs.document.create or docs.document.append (its 64 KiB body; governance allows 80 KiB there, as for basecamp.message.post) and a sheets.values.append, sheets.values.update, excel.range.update or excel.row.add (its 64 KiB `values`), total deadline 10 s.",
|
|
5
|
+
"description": "Body of POST /_harbour/integrations/execute (app SDK), forwarded unchanged to the governance development and runtime execute routes. Exactly one declared operation against one declared resource. The identity mode is fixed by the operation for every operation but the declared-mode Slack operations (slack.channel.history, slack.message.post, slack.message.update, slack.message.delete; 0.71.0 widened history), and always enforced from an approved grant; any identity/userId/scope field is rejected by additionalProperties. A declared-mode operation runs as the app (for a post: the IT-approved per-app name from the grant, plus a \"Posted by <app> on Isomorph\" footer) or as the signed-in person (no name, no footer) \u2014 the same input either way \u2014 and since 0.55.0 the call names which of the app's approved modes it runs under (`mode`): required when IT approved both modes for the app (MODE_REQUIRED otherwise), optional when it approved one (that one is used), and a named mode without an approved grant is refused (MODE_NOT_GRANTED) rather than served under the other, so approval order never decides what a call does. Per-operation input bounds (0.71.0, ADR 0018): slack.channel.history {limit? 1..100, default 15, cursor?, oldest?, latest?}; slack.message.post {text 1..4000 chars, threadId?, format? plain|mrkdwn} and a required idempotencyKey retained across retries of one explicit Send; slack.message.update {messageId, text, format?}; slack.message.delete {messageId}; slack.dm.send {to 1..8 addresses, text, format?} (app mode only) with a required idempotencyKey; slack.user.lookup {email}; gmail.thread.list {limit? 1..50, default 15, q?, labelIds? default [INBOX], pageToken?}; gmail.thread.read {threadId}; gmail.message.read {messageId}; gmail.attachment.read {messageId, attachmentId}; gmail.message.send {to? 1..50 addresses (to+cc+bcc <= 50 in total; since 0.104.0 `to` may be absent when cc or bcc names a recipient \u2014 governance refuses a send with no recipient at all), cc?, bcc?, subject 1..256 chars, text 1..4000 chars, html? <= 64 KiB, attachments? 1..5, each {filename, contentType, dataBase64} (inline, 2 MiB in total) or since 0.105.0 {file, filename?, contentType?} (a private app file by path, deployed apps only; 18 MiB in total), replyToMessageId?, replyTo? (one address), fromName? 1..120 chars, inReplyTo? (an RFC Message-ID; not with replyToMessageId)} and a required idempotencyKey like slack.message.post; warehouse.view.read {columns? 1..32, where? 1..16 filters, groupBy? 1..8 columns, aggregates? 1..16, orderBy? 1..8, limit? 1..1000, default 100} (0.70.0: a plain read names columns, an aggregate read names aggregates and optionally groupBy; the cross-field rules \u2014 exactly one of columns or aggregates, groupBy needs aggregates, a filter value that fits its op, orderBy naming a returned column, every named column approved \u2014 are enforced by governance, not expressible here; ADR 0017). Basecamp (0.76.0, ADR 0022; every id a string of 1..20 digits, every operation user mode): basecamp.project.list {pageToken?, projectStatus? active|archived} on the fixed `account` alias; on a project alias basecamp.todolist.list {pageToken?}; basecamp.todo.list {todolistId, completed?, pageToken?}; basecamp.todo.create {todolistId, content 1..1000 (the to-do title), description? <= 16384, contentFormat? plain|html, dueOn? YYYY-MM-DD, assigneeIds? 1..20, notify?} with a required idempotencyKey; basecamp.todo.complete {todoId} (idempotent at Basecamp, so no key); basecamp.message.list {pageToken?}; basecamp.message.post {subject 1..256, content 1..65536, contentFormat?} with a required idempotencyKey; basecamp.comment.list {recordingId, pageToken?}; basecamp.comment.post {recordingId, content 1..16384, contentFormat?} with a required idempotencyKey; basecamp.campfire.post {content 1..4000, contentFormat?} with a required idempotencyKey; basecamp.people.list {pageToken?}. `pageToken` is the previous page's `nextPageToken` (a Basecamp page number as digits). SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id \u2014 GraphId, 1..256 characters \u2014 every operation user mode on a site alias): sharepoint.list.list {pageToken?}; sharepoint.item.list {listId, filter? 1..512 (an OData filter on `fields/...`), pageToken?}; sharepoint.item.create {listId, fields (1..32 columns, each a string <= 4000, a number, a boolean or null)} with a required idempotencyKey; sharepoint.item.update {listId, itemId, fields} (a PATCH, idempotent at Graph, so no key); sharepoint.drive.list {pageToken?}; sharepoint.file.list {driveId?, folderId?, pageToken?} (no driveId: the site's default drive; no folderId: its root); sharepoint.file.search {q 1..256, driveId?, pageToken?}; sharepoint.file.read {fileId, driveId?}; sharepoint.file.upload {filename 1..255, contentType, dataBase64 <= 2 MiB, driveId?, folderId?, conflict? rename|replace|fail} with a required idempotencyKey. A SharePoint `pageToken` is Graph's `$skiptoken` (up to 2048 characters; the shared field is that wide, the Gmail and Basecamp operations narrow it back to 256). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every operation user mode; the seven personal providers run on their fixed alias, teams.* on a channel alias): calendar.calendar.list {pageToken?}; calendar.event.list {calendarId?, timeMin, timeMax, q?, pageToken?} (RFC 3339 date-times with a zone); calendar.event.read {calendarId?, eventId}; calendar.event.create {calendarId?, title 1..256, start, end, timeZone?, allDay?, description? <= 8000, location? <= 512, attendees? 1..100 emails, notify?} with a required idempotencyKey; calendar.event.update {calendarId?, eventId, title?, start?, end?, timeZone?, description?, location?, attendees?, notify?} and calendar.event.cancel {calendarId?, eventId, notify?} (both idempotent at Google, so no key); drive.file.list {folderId?, pageToken?}; drive.file.search {q, pageToken?}; drive.file.read {fileId, exportAs? text|csv|pdf} (exportAs renders a Google Docs, Sheets or Slides file; a binary file is downloaded as is); drive.file.upload {filename, contentType, dataBase64 <= 2 MiB, folderId?, conflict?} and drive.folder.create {title, folderId?} with a required idempotencyKey; docs.document.read {documentId}; docs.document.create {title, text? <= 64 KiB} with a required idempotencyKey; docs.document.append {documentId, text <= 64 KiB} and docs.text.replace {documentId, replacements 1..64 of {find 1..512, replace <= 4000, matchCase?}} (no key: the app owns the document's state); sheets.spreadsheet.read {spreadsheetId}; sheets.values.read {spreadsheetId, range} (A1 notation, 1..256); sheets.values.append {spreadsheetId, range, values, valueInput? raw|user} with a required idempotencyKey; sheets.values.update {spreadsheetId, range, values, valueInput?} and sheets.values.clear {spreadsheetId, range} (no key); sheets.spreadsheet.create {title, sheets? 1..20 titles} with a required idempotencyKey (`values` is 1..1000 rows of 1..100 cells, each a string <= 4000, a number, a boolean or null); outlook.message.list {folder?, q?, pageToken?}; outlook.message.read {messageId}; outlook.attachment.read {messageId, attachmentId}; outlook.message.send {to, cc?, bcc?, subject, text 1..4000, html?, attachments?} and outlook.message.reply {messageId, text 1..4000, attachments?} (0.95.0: existing EmailAttachment bounds; recipients remain provider-resolved) with a required idempotencyKey like gmail.message.send; outlook.event.list {start, end, pageToken?}; outlook.event.read {eventId}; outlook.event.create (as calendar.event.create without calendarId) with a required idempotencyKey; outlook.event.update (as calendar.event.update without calendarId) and outlook.event.cancel {eventId, comment? <= 4000} (no key); onedrive.file.list, onedrive.file.search, onedrive.file.read {fileId}, onedrive.file.upload and onedrive.folder.create as the drive.* operations (Graph ids, no exportAs); excel.worksheet.list {fileId, driveId?}; excel.range.read {fileId, driveId?, worksheet, address? 1..64} (no address: the used range); excel.range.update {fileId, driveId?, worksheet, address, values} (no key: a PATCH); excel.table.list {fileId, driveId?, worksheet?}; excel.row.add {fileId, driveId?, tableName, values} and excel.worksheet.add {fileId, driveId?, title} with a required idempotencyKey; teams.message.list {pageToken?}; teams.message.post {text 1..4000, html?} and teams.reply.post {messageId, text, html?} with a required idempotencyKey. An Outlook or Teams `messageId` is a Graph id (the shared field widens to ^[A-Za-z0-9._=-]{1,256}$; the Gmail and Slack operations narrow it back to ^[A-Za-z0-9._-]{1,64}$ in their then-blocks, so their rules are unchanged), and `text` widens to 64 KiB for the Docs body (every other operation that takes it narrows it back to 4000). Each operation's then-block is the one statement of its input rules: propertyNames.enum names the fields it accepts, required the ones it needs, and per-field overrides (maximum, default) narrow the shared property. Codegen derives IntegrationExecuteInputRules and validateIntegrationExecuteInput (gen-ts/integration-execute-input-rules.ts, gen-go/integration_execute_input_rules.go) from exactly these blocks, so the governance execute route, the app SDK and the kit's local check refuse the same request with the same message. Transport limits (not expressible here): request <= 16 KiB except a gmail.message.send carrying attachments (governance sizes that route for the attachment bound) and a basecamp.message.post (its 64 KiB body; governance allows 80 KiB there) and a sharepoint.file.upload, drive.file.upload or onedrive.file.upload (its 2 MiB file as base64; governance sizes it like the Gmail attachment route), an outlook.message.send or outlook.message.reply with attachments (the Gmail route), a docs.document.create or docs.document.append (its 64 KiB body; governance allows 80 KiB there, as for basecamp.message.post) and a sheets.values.append, sheets.values.update, excel.range.update or excel.row.add (its 64 KiB `values`), total deadline 10 s.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": [
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
"items": {
|
|
157
157
|
"$ref": "#/definitions/EmailAttachment"
|
|
158
158
|
},
|
|
159
|
-
"description": "gmail.message.send (0.71.0): files to attach, 1..5
|
|
159
|
+
"description": "gmail.message.send (0.71.0): files to attach, 1..5; inline ones (`dataBase64`) total at most 2 MiB, and since 0.105.0 one may instead name a private app file (`file`), 18 MiB in total. outlook.message.send (0.86.0): inline files only, 1..5."
|
|
160
160
|
},
|
|
161
161
|
"todolistId": {
|
|
162
162
|
"$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId",
|
|
@@ -4283,13 +4283,14 @@
|
|
|
4283
4283
|
"EmailAttachment": {
|
|
4284
4284
|
"type": "object",
|
|
4285
4285
|
"additionalProperties": false,
|
|
4286
|
-
"
|
|
4287
|
-
"filename",
|
|
4288
|
-
"contentType",
|
|
4289
|
-
"dataBase64"
|
|
4290
|
-
],
|
|
4291
|
-
"description": "One file attached to a gmail.message.send (0.71.0) or an outlook.message.send (0.86.0), carried inline as base64. At most 2 MiB of bytes, which is 2,796,203 base64 characters.",
|
|
4286
|
+
"description": "One file attached to a gmail.message.send (0.71.0) or an outlook.message.send (0.86.0). Either carried inline as base64 \u2014 `filename`, `contentType` and `dataBase64`, at most 2 MiB of bytes, which is 2,796,203 base64 characters \u2014 or, on a gmail.message.send from a deployed app (0.105.0), named by `file`: the path of a private file of the app (isomorph.files), which the app's gateway checks the caller may read and hands to governance as a short-lived link, so no file bytes ride the request; `filename` and `contentType` then default to the file's own. Exactly one of `dataBase64` and `file`: a cross-field rule governance enforces (no field is required here). The Outlook sends take the inline form only.",
|
|
4292
4287
|
"properties": {
|
|
4288
|
+
"file": {
|
|
4289
|
+
"type": "string",
|
|
4290
|
+
"minLength": 1,
|
|
4291
|
+
"maxLength": 1024,
|
|
4292
|
+
"description": "gmail.message.send (0.105.0): the path of one of the app's private files, as isomorph.files names it (e.g. letters/offer.pdf). The inline and referenced attachments of one email total at most 18 MiB decoded."
|
|
4293
|
+
},
|
|
4293
4294
|
"filename": {
|
|
4294
4295
|
"type": "string",
|
|
4295
4296
|
"minLength": 1,
|