@fourier-labs/harbour-contracts 0.47.0 → 0.51.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/README.md +35 -3
- package/contract-release.json +25 -22
- package/dist/gen-ts/app-integrations-declaration.d.ts +1 -0
- package/dist/gen-ts/contract-release.d.ts +6 -6
- package/dist/gen-ts/contract-release.js +6 -6
- package/dist/gen-ts/data-grant-v2.d.ts +1 -1
- package/dist/gen-ts/index.d.ts +1 -0
- package/dist/gen-ts/index.js +1 -0
- package/dist/gen-ts/integration-execute-input-rules.d.ts +187 -0
- package/dist/gen-ts/integration-execute-input-rules.js +114 -0
- package/dist/gen-ts/integration-execute-request.d.ts +46 -4
- package/dist/gen-ts/integration-execute-result.d.ts +1 -1
- package/dist/gen-ts/saas-connection.d.ts +3 -3
- package/dist/gen-ts/transform-report.d.ts +2 -0
- package/dist/gen-ts/well-defined-app-manifest.d.ts +31 -0
- package/gen-ts/app-integrations-declaration.ts +1 -0
- package/gen-ts/contract-release.ts +6 -6
- package/gen-ts/data-grant-v2.ts +6 -1
- package/gen-ts/index.ts +1 -0
- package/gen-ts/integration-execute-input-rules.ts +119 -0
- package/gen-ts/integration-execute-request.ts +51 -4
- package/gen-ts/integration-execute-result.ts +1 -1
- package/gen-ts/saas-connection.ts +8 -3
- package/gen-ts/transform-report.ts +2 -0
- package/gen-ts/well-defined-app-manifest.ts +31 -0
- package/package.json +1 -1
- package/schema/app-integrations-declaration.schema.json +6 -4
- package/schema/integration-execute-request.schema.json +35 -10
- package/schema/integration-execute-result.schema.json +3 -1
- package/schema/saas-connection.schema.json +3 -3
- package/schema/transform-report.schema.json +2 -1
- package/schema/well-defined-app-manifest.schema.json +61 -0
|
@@ -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 NOT a caller field: it is fixed by the operation and enforced from the approved grant; any identity/userId/scope field is rejected by additionalProperties. Per-operation input bounds: slack.channel.history {limit? 1..15, default 15}; slack.message.post {text 1..4000 chars} and a required idempotencyKey retained across retries of one explicit Send; gmail.thread.list {limit? 1..15, default 15}; gmail.message.read {messageId}; warehouse.view.read {columns 1..32, limit? 1..1000, default 100}. Transport limits (not expressible here): request <= 16 KiB, 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 NOT a caller field: it is fixed by the operation and enforced from the approved grant; any identity/userId/scope field is rejected by additionalProperties. Per-operation input bounds: slack.channel.history {limit? 1..15, default 15}; slack.message.post {text 1..4000 chars} and a required idempotencyKey retained across retries of one explicit Send; gmail.thread.list {limit? 1..15, default 15}; gmail.message.read {messageId}; gmail.message.send {to 1..50 addresses (to+cc+bcc <= 50 in total), cc?, bcc?, subject 1..256 chars, text 1..4000 chars, replyToMessageId?} and a required idempotencyKey like slack.message.post (plain text only, no attachments); warehouse.view.read {columns 1..32, limit? 1..1000, default 100}. 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, total deadline 10 s.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["connection", "operation", "resource", "input"],
|
|
@@ -14,9 +14,14 @@
|
|
|
14
14
|
"type": "object",
|
|
15
15
|
"additionalProperties": false,
|
|
16
16
|
"properties": {
|
|
17
|
-
"limit": { "type": "integer", "minimum": 1, "maximum": 1000 },
|
|
18
|
-
"text": { "type": "string", "minLength": 1, "maxLength": 4000 },
|
|
17
|
+
"limit": { "type": "integer", "minimum": 1, "maximum": 1000, "description": "Read operations: how many items to return; the per-operation then-block sets the real ceiling (15 for slack.channel.history and gmail.thread.list, 1000 for warehouse.view.read) and the default." },
|
|
18
|
+
"text": { "type": "string", "minLength": 1, "maxLength": 4000, "description": "slack.message.post / gmail.message.send: the plain-text body, 1..4000 characters." },
|
|
19
19
|
"messageId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{1,64}$", "description": "gmail.message.read: the Gmail message id a gmail.thread.list result named." },
|
|
20
|
+
"to": { "$ref": "#/definitions/EmailAddressList", "description": "gmail.message.send: primary recipients." },
|
|
21
|
+
"cc": { "$ref": "#/definitions/EmailAddressList", "description": "gmail.message.send: carbon-copy recipients." },
|
|
22
|
+
"bcc": { "$ref": "#/definitions/EmailAddressList", "description": "gmail.message.send: blind-copy recipients." },
|
|
23
|
+
"subject": { "type": "string", "minLength": 1, "maxLength": 256, "description": "gmail.message.send: one-line subject (no line breaks)." },
|
|
24
|
+
"replyToMessageId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{1,64}$", "description": "gmail.message.send: a Gmail message id (from gmail.thread.list / gmail.message.read) the email replies to; the send joins that thread." },
|
|
20
25
|
"columns": {
|
|
21
26
|
"type": "array",
|
|
22
27
|
"minItems": 1,
|
|
@@ -28,7 +33,7 @@
|
|
|
28
33
|
"idempotencyKey": {
|
|
29
34
|
"type": "string",
|
|
30
35
|
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
|
31
|
-
"description": "UUID of one explicit Send action; required for slack.message.post only. The same key with the same payload and subject returns the recorded outcome for seven days."
|
|
36
|
+
"description": "UUID of one explicit Send action; required for slack.message.post and gmail.message.send only. The same key with the same payload and subject returns the recorded outcome for seven days."
|
|
32
37
|
}
|
|
33
38
|
},
|
|
34
39
|
"allOf": [
|
|
@@ -37,7 +42,7 @@
|
|
|
37
42
|
"then": {
|
|
38
43
|
"not": { "required": ["idempotencyKey"] },
|
|
39
44
|
"properties": {
|
|
40
|
-
"input": { "
|
|
45
|
+
"input": { "propertyNames": { "enum": ["limit"] }, "properties": { "limit": { "maximum": 15, "default": 15 } } }
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
},
|
|
@@ -46,7 +51,7 @@
|
|
|
46
51
|
"then": {
|
|
47
52
|
"required": ["idempotencyKey"],
|
|
48
53
|
"properties": {
|
|
49
|
-
"input": { "
|
|
54
|
+
"input": { "propertyNames": { "enum": ["text"] }, "required": ["text"] }
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
},
|
|
@@ -55,7 +60,7 @@
|
|
|
55
60
|
"then": {
|
|
56
61
|
"not": { "required": ["idempotencyKey"] },
|
|
57
62
|
"properties": {
|
|
58
|
-
"input": { "
|
|
63
|
+
"input": { "propertyNames": { "enum": ["columns", "limit"] }, "required": ["columns"], "properties": { "limit": { "maximum": 1000, "default": 100 } } }
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
},
|
|
@@ -64,7 +69,7 @@
|
|
|
64
69
|
"then": {
|
|
65
70
|
"not": { "required": ["idempotencyKey"] },
|
|
66
71
|
"properties": {
|
|
67
|
-
"input": { "
|
|
72
|
+
"input": { "propertyNames": { "enum": ["limit"] }, "properties": { "limit": { "maximum": 15, "default": 15 } } }
|
|
68
73
|
}
|
|
69
74
|
}
|
|
70
75
|
},
|
|
@@ -73,11 +78,24 @@
|
|
|
73
78
|
"then": {
|
|
74
79
|
"not": { "required": ["idempotencyKey"] },
|
|
75
80
|
"properties": {
|
|
76
|
-
"input": { "
|
|
81
|
+
"input": { "propertyNames": { "enum": ["messageId"] }, "required": ["messageId"] }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"if": { "properties": { "operation": { "const": "gmail.message.send" } } },
|
|
87
|
+
"then": {
|
|
88
|
+
"required": ["idempotencyKey"],
|
|
89
|
+
"properties": {
|
|
90
|
+
"input": { "propertyNames": { "enum": ["to", "cc", "bcc", "subject", "text", "replyToMessageId"] }, "required": ["to", "subject", "text"] }
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
94
|
],
|
|
95
|
+
"definitions": {
|
|
96
|
+
"EmailAddress": { "type": "string", "minLength": 3, "maxLength": 254, "pattern": "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$", "description": "One bare email address (no display name)." },
|
|
97
|
+
"EmailAddressList": { "type": "array", "minItems": 1, "maxItems": 50, "uniqueItems": true, "items": { "$ref": "#/definitions/EmailAddress" } }
|
|
98
|
+
},
|
|
81
99
|
"examples": [
|
|
82
100
|
{ "connection": "company-slack", "operation": "slack.channel.history", "resource": "team-updates", "input": { "limit": 15 } },
|
|
83
101
|
{
|
|
@@ -89,6 +107,13 @@
|
|
|
89
107
|
},
|
|
90
108
|
{ "connection": "sales-warehouse", "operation": "warehouse.view.read", "resource": "weekly_sales", "input": { "columns": ["week", "total"], "limit": 100 } },
|
|
91
109
|
{ "connection": "gmail", "operation": "gmail.thread.list", "resource": "inbox", "input": { "limit": 15 } },
|
|
92
|
-
{ "connection": "gmail", "operation": "gmail.message.read", "resource": "inbox", "input": { "messageId": "18f2c3a4b5d6e7f8" } }
|
|
110
|
+
{ "connection": "gmail", "operation": "gmail.message.read", "resource": "inbox", "input": { "messageId": "18f2c3a4b5d6e7f8" } },
|
|
111
|
+
{
|
|
112
|
+
"connection": "gmail",
|
|
113
|
+
"operation": "gmail.message.send",
|
|
114
|
+
"resource": "inbox",
|
|
115
|
+
"input": { "to": ["jane@example.com"], "subject": "Weekly report", "text": "Shipped the weekly report.", "replyToMessageId": "18f2c3a4b5d6e7f8" },
|
|
116
|
+
"idempotencyKey": "6f1c2c1e-3a1c-4c3e-9f0e-2b6f7d8a9c11"
|
|
117
|
+
}
|
|
93
118
|
]
|
|
94
119
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://schemas.harbour.dev/integration-execute-result.schema.json",
|
|
4
4
|
"title": "IntegrationExecuteResult",
|
|
5
|
-
"description": "The `data` member of a successful execute response `{data, requestId}`; the SDK unwraps `data`. Exactly one of the
|
|
5
|
+
"description": "The `data` member of a successful execute response `{data, requestId}`; the SDK unwraps `data`. Exactly one of the six operation results: slack.channel.history -> {messages, hasMore} (plain text/IDs/time only, never the provider payload); slack.message.post -> {messageId}; gmail.thread.list -> {threads, hasMore} (newest inbox threads; each `messageId` is the thread's newest message, what gmail.message.read takes); gmail.message.read -> {id, threadId, subject, from, to, date, text, truncated} (headers plus the plain text with html stripped, cut at 16 KiB with `truncated` set); gmail.message.send -> {messageId, threadId} (the Gmail ids of the sent message and its thread); warehouse.view.read -> {rows} (decimal/64-bit values as strings, dates as ISO strings). An unknown send outcome is never a result; it is an error with details.code SEND_OUTCOME_UNKNOWN. Results over 1 MiB are rejected, not truncated.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"properties": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"oneOf": [
|
|
36
36
|
{ "required": ["messages", "hasMore"], "maxProperties": 2 },
|
|
37
37
|
{ "required": ["messageId"], "maxProperties": 1 },
|
|
38
|
+
{ "required": ["messageId", "threadId"], "maxProperties": 2 },
|
|
38
39
|
{ "required": ["threads", "hasMore"], "maxProperties": 2 },
|
|
39
40
|
{ "required": ["id", "threadId", "subject", "from", "to", "date", "text", "truncated"], "maxProperties": 8 },
|
|
40
41
|
{ "required": ["rows"], "maxProperties": 1 }
|
|
@@ -71,6 +72,7 @@
|
|
|
71
72
|
"hasMore": false
|
|
72
73
|
},
|
|
73
74
|
{ "messageId": "1725840100.000200" },
|
|
75
|
+
{ "messageId": "18f2c3a4b5d6e7f9", "threadId": "18f2c3a4b5d6e7f0" },
|
|
74
76
|
{
|
|
75
77
|
"threads": [{ "id": "18f2c3a4b5d6e7f0", "messageId": "18f2c3a4b5d6e7f8", "subject": "Weekly report", "from": "Jane <jane@example.com>", "date": "2026-09-11T08:00:00Z", "snippet": "Shipped the weekly report." }],
|
|
76
78
|
"hasMore": false
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"operations": {
|
|
27
27
|
"type": "array",
|
|
28
28
|
"minItems": 1,
|
|
29
|
-
"maxItems":
|
|
29
|
+
"maxItems": 5,
|
|
30
30
|
"uniqueItems": true,
|
|
31
31
|
"items": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationOperation" },
|
|
32
32
|
"not": { "contains": { "const": "warehouse.view.read" } },
|
|
33
|
-
"description": "Operations IT allows apps to request on this connection: the provider's own SaaS operations (Slack: slack.channel.history, slack.message.post; Gmail: gmail.thread.list, gmail.message.read). Never warehouse.view.read."
|
|
33
|
+
"description": "Operations IT allows apps to request on this connection: the provider's own SaaS operations (Slack: slack.channel.history, slack.message.post; Gmail: gmail.thread.list, gmail.message.read, gmail.message.send). Never warehouse.view.read."
|
|
34
34
|
},
|
|
35
35
|
"developmentPreapproval": { "$ref": "#/definitions/DevelopmentPreapproval" },
|
|
36
36
|
"blockedDevelopmentBuilders": {
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
"status": "ACTIVE",
|
|
162
162
|
"version": 1,
|
|
163
163
|
"channels": {},
|
|
164
|
-
"operations": ["gmail.thread.list", "gmail.message.read"],
|
|
164
|
+
"operations": ["gmail.thread.list", "gmail.message.read", "gmail.message.send"],
|
|
165
165
|
"developmentPreapproval": { "enabled": false, "hours": 24 },
|
|
166
166
|
"blockedDevelopmentBuilders": ["contractor@example.com"],
|
|
167
167
|
"createdAt": "2026-09-11T08:00:00Z",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"required": ["application"],
|
|
10
10
|
"properties": {
|
|
11
11
|
"applied_playbooks": {
|
|
12
|
+
"description": "Optional self-report of the playbooks the agent says it applied. A claim about its own behaviour is not evidence, so nothing validates it; a deployable report may omit it, and one that still carries it stays valid.",
|
|
12
13
|
"type": "array",
|
|
13
14
|
"minItems": 1,
|
|
14
15
|
"items": { "type": "string", "minLength": 1 }
|
|
@@ -666,7 +667,7 @@
|
|
|
666
667
|
],
|
|
667
668
|
"oneOf": [
|
|
668
669
|
{
|
|
669
|
-
"required": ["
|
|
670
|
+
"required": ["static_rebuild_required", "analysis", "database", "preservation_plan", "validation_fixtures", "application"],
|
|
670
671
|
"properties": {
|
|
671
672
|
"application": {
|
|
672
673
|
"required": ["runtime", "buildKind", "topology", "entrypoint", "build", "basePath", "evidence", "unsupportedFeatures"],
|
|
@@ -26,9 +26,70 @@
|
|
|
26
26
|
"type": "array",
|
|
27
27
|
"maxItems": 128,
|
|
28
28
|
"items": { "$ref": "#/definitions/WorkloadBinding" }
|
|
29
|
+
},
|
|
30
|
+
"runtime": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"pattern": "^(?:nodejs|python)[0-9]+(?:\\.[0-9]+)?$",
|
|
33
|
+
"description": "Optional language runtime the application container is built on, spelled exactly as transform-report.application.runtime does (nodejs22, python3.11). Optional so a manifest written before the producer emits it still validates."
|
|
34
|
+
},
|
|
35
|
+
"capabilities": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"maxItems": 5,
|
|
38
|
+
"uniqueItems": true,
|
|
39
|
+
"items": { "$ref": "#/definitions/Capability" },
|
|
40
|
+
"description": "Optional declaration of the Harbour browser capabilities the application uses. Same closed set as the app-capabilities document; this is the declaration, not a source scan."
|
|
41
|
+
},
|
|
42
|
+
"routeAuth": {
|
|
43
|
+
"$ref": "#/definitions/RouteAuth",
|
|
44
|
+
"description": "Optional method/path authentication policy. Omitted means every route requires Harbour SSO, the same default the policy states explicitly."
|
|
29
45
|
}
|
|
30
46
|
},
|
|
31
47
|
"definitions": {
|
|
48
|
+
"Capability": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"enum": ["data", "files", "actions", "realtime", "telemetry"]
|
|
51
|
+
},
|
|
52
|
+
"RouteAuth": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"required": ["default", "public"],
|
|
56
|
+
"description": "Harbour SSO is required by default; the public list is the only way a route escapes it, and each entry names the methods and the application-authorized mode so the artifact contract's route_auth can be built from this document alone.",
|
|
57
|
+
"properties": {
|
|
58
|
+
"default": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"const": "sso_required",
|
|
61
|
+
"default": "sso_required"
|
|
62
|
+
},
|
|
63
|
+
"public": {
|
|
64
|
+
"type": "array",
|
|
65
|
+
"maxItems": 64,
|
|
66
|
+
"default": [],
|
|
67
|
+
"items": { "$ref": "#/definitions/PublicRoute" }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"PublicRoute": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"additionalProperties": false,
|
|
74
|
+
"required": ["path", "methods", "mode"],
|
|
75
|
+
"properties": {
|
|
76
|
+
"path": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"pattern": "^/(?:[A-Za-z0-9._~-]+/)*(?:[A-Za-z0-9._~-]+|\\*)$"
|
|
79
|
+
},
|
|
80
|
+
"methods": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"minItems": 1,
|
|
83
|
+
"maxItems": 7,
|
|
84
|
+
"uniqueItems": true,
|
|
85
|
+
"items": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"enum": ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"mode": { "type": "string", "const": "application_authorized" }
|
|
91
|
+
}
|
|
92
|
+
},
|
|
32
93
|
"ResourceProfile": {
|
|
33
94
|
"type": "string",
|
|
34
95
|
"enum": ["standard", "memory-1g", "memory-2g"]
|