gik-service-providers 0.0.0-stage → 0.2.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 +208 -2
- package/dist/github-context-store.d.ts +26 -0
- package/dist/github-context-store.js +647 -0
- package/dist/github-context-store.js.map +1 -0
- package/dist/gmail.d.ts +25 -0
- package/dist/gmail.js +306 -0
- package/dist/gmail.js.map +1 -0
- package/dist/scheduler.d.ts +5 -0
- package/dist/scheduler.js +129 -0
- package/dist/scheduler.js.map +1 -0
- package/examples/host.ts +84 -0
- package/examples/settings.ts +106 -0
- package/package.json +45 -4
package/README.md
CHANGED
|
@@ -1,3 +1,209 @@
|
|
|
1
|
-
#
|
|
1
|
+
# GIK service providers
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
```sh
|
|
4
|
+
npm install gik-service-providers
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createSchedulerServiceKind } from "gik-service-providers/scheduler";
|
|
9
|
+
import { createGmailServiceKind } from "gik-service-providers/gmail";
|
|
10
|
+
import { createGitHubContextStoreServiceKind } from "gik-service-providers/github-context-store";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
There is deliberately **no package-root export or index barrel**. Applications
|
|
14
|
+
explicitly import and register the providers they choose. Gmail does not import
|
|
15
|
+
Croner or scheduler code. Both use the published `gik-controlface/services`
|
|
16
|
+
contracts; there are no private sibling-checkout dependencies.
|
|
17
|
+
|
|
18
|
+
## Ownership and setup
|
|
19
|
+
|
|
20
|
+
Native Blueprint declarations own concrete non-secret configuration, such as
|
|
21
|
+
the cron expression/timezone and Gmail endpoint/opaque `credentialRef`. Providers
|
|
22
|
+
own config schemas and operation semantics. Hosts own provider enablement,
|
|
23
|
+
literal secrets, OAuth sessions/refresh, endpoint admission and invocation
|
|
24
|
+
authority. Do not substitute environment-based endpoint bindings for Blueprint
|
|
25
|
+
config, or put tokens in config, events, journals, setup reports or logs.
|
|
26
|
+
|
|
27
|
+
`registry.describe()` supplies provider config schemas.
|
|
28
|
+
`host.describeServiceSetup(id)` supplies non-secret dependencies, readiness and
|
|
29
|
+
host-implemented setup action descriptions without fetching credentials.
|
|
30
|
+
Gmail derives OAuth scopes from declared operations; send also declares a
|
|
31
|
+
`gmail-send-authority` dependency. Hosts inspect these using the full service
|
|
32
|
+
identity, not a global credential-reference string. Disabling a provider is a
|
|
33
|
+
host readiness/authorization decision, not an inferred Blueprint state change.
|
|
34
|
+
|
|
35
|
+
[Host/headless wiring](examples/host.ts) explicitly registers both chosen
|
|
36
|
+
providers and requires a host authorization evaluator. [Settings rendering](examples/settings.ts)
|
|
37
|
+
uses the same setup reports and trusted action handlers. It supports string
|
|
38
|
+
action inputs (including transient password fields); extend unsupported schemas
|
|
39
|
+
explicitly, never silently ignore them. The example displays the provider config
|
|
40
|
+
schema; an application's authoring UI persists non-secret config in its Blueprint,
|
|
41
|
+
not a parallel host override table.
|
|
42
|
+
Enablement checkboxes call trusted application handlers. On disable, those
|
|
43
|
+
handlers must stop the old host's active Senses and deny further invocations;
|
|
44
|
+
when enabling, rebuild/register the chosen host providers before rerendering.
|
|
45
|
+
Pass the selected service IDs to headless inspection as well. Toggling a checkbox
|
|
46
|
+
alone does not cancel runtime work or confer execution authority.
|
|
47
|
+
|
|
48
|
+
Browser OAuth action handlers can use Google Identity Services and retain the
|
|
49
|
+
user gesture. A headless host provisions an appropriately consented, refreshable
|
|
50
|
+
connection through an operator CLI/deployment tool. Neither provider opens login
|
|
51
|
+
windows or stores refresh tokens. Gmail scopes may require Google's sensitive/
|
|
52
|
+
restricted-scope verification; see [Google's scope guide](https://developers.google.com/workspace/gmail/api/auth/scopes).
|
|
53
|
+
The examples are integration seams, not a bundled OAuth application or token vault.
|
|
54
|
+
|
|
55
|
+
## Scheduler
|
|
56
|
+
|
|
57
|
+
Kind `cron-scheduler`, version `1`, config:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{ "expression": "0 9 * * *", "timezone": "Asia/Kolkata" }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Declare `tick`, contract `scheduler.cron/v1`, as an immediate timer Sense with a
|
|
64
|
+
`due` signal. Input is `{}`. Signals contain `firedAt`, `expression`, and
|
|
65
|
+
`timezone`. Croner interprets the explicit timezone including daylight saving.
|
|
66
|
+
|
|
67
|
+
This is **process-local**, not a durable OS scheduler. Restarting schedules only
|
|
68
|
+
future occurrences; offline occurrences are skipped, never caught up. Busy
|
|
69
|
+
deliveries do not overlap. Closing/aborting cancels the timer, and close waits for
|
|
70
|
+
an in-flight delivery. The Blueprint's committed Sense intent can be reconciled
|
|
71
|
+
after restart through the normal GIK host.
|
|
72
|
+
|
|
73
|
+
## Gmail
|
|
74
|
+
|
|
75
|
+
Kind `gmail`, version `1`, config:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"endpoint": "https://gmail.googleapis.com",
|
|
80
|
+
"credentialRef": "google/main",
|
|
81
|
+
"pollIntervalMs": 60000
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The endpoint is intentionally limited to Google's canonical Gmail origin.
|
|
86
|
+
`authorizeEndpoint` must admit every concrete request before credential
|
|
87
|
+
resolution. `resolveCredential` returns a `GmailConnection`, not a raw token.
|
|
88
|
+
Its `request` method adds authorization inside host state and honors the supplied
|
|
89
|
+
abort signal. `clearCredential` invalidates a session on HTTP 401.
|
|
90
|
+
|
|
91
|
+
| Operation | Contract | Input |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| `search` | `gmail.search/v1` | Optional `query`, `maxResults` (1-100), `pageToken` |
|
|
94
|
+
| `get` | `gmail.get/v1` | `id`; optional `format`: metadata/minimal/full/raw |
|
|
95
|
+
| `send` | `gmail.send/v1` | `raw`: RFC 2822 message encoded as base64url |
|
|
96
|
+
| `watch` | `gmail.watch/v1` | Optional `query`, `maxResults` (1-100) |
|
|
97
|
+
|
|
98
|
+
`search`/`get` return Gmail JSON data. `watch` is an immediate feed Sense with a
|
|
99
|
+
`message` signal containing message ID and optional thread ID; fetch bodies with
|
|
100
|
+
`get`. Initial polling emits currently matching IDs. Polls are serialized and
|
|
101
|
+
bounded to one page; no complete mailbox-history, webhook, exactly-once or gap-free
|
|
102
|
+
guarantee is implied. The latest 256 emitted IDs are deduplicated in memory;
|
|
103
|
+
restart can emit existing IDs again. Use normal durable GIK delivery IDs and
|
|
104
|
+
application-level deduplication/cursors when stronger guarantees are required.
|
|
105
|
+
Close aborts outstanding work, suppresses late signals, and waits for the poll.
|
|
106
|
+
Expiry/revocation is rechecked for the actual service on every request/delivery.
|
|
107
|
+
|
|
108
|
+
### Sending is not authorized by credential readiness
|
|
109
|
+
|
|
110
|
+
The host must supply the **separate `GmailConnection.send` port**. It receives the
|
|
111
|
+
full invocation context and must use the host's existing sole authorization
|
|
112
|
+
evaluator, including confirmation and idempotency policy, before issuing POST.
|
|
113
|
+
The host example wires this port back through `DefaultServiceHost.authorizeInvocation`.
|
|
114
|
+
Keep the same evaluator on the outer host so `confirmation-required` is a normal
|
|
115
|
+
typed settlement before provider execution, not a surprise login or hidden write.
|
|
116
|
+
The host must inspect recipients/content in the decoded raw message when deciding
|
|
117
|
+
authority. Possessing Gmail OAuth send scope alone is not permission to send.
|
|
118
|
+
|
|
119
|
+
Send declarations must be immediate and must not request transport/guardrail
|
|
120
|
+
retries or correction attempts. The provider requires ControlFace 0.7.0 or newer
|
|
121
|
+
and declares `send` non-queueable and non-retryable. The host rejects explicit
|
|
122
|
+
enqueue calls before credential acquisition, including when a Blueprint uses a
|
|
123
|
+
different invocation alias, and suppresses automatic transport/guardrail retries.
|
|
124
|
+
Read operations remain queueable. The provider never retries POST. Network failure,
|
|
125
|
+
HTTP 408/5xx, or an unreadable success response produces
|
|
126
|
+
`GmailSendUncertainError` (`gmail-send-uncertain`), not a safe-to-retry result.
|
|
127
|
+
Investigate Sent mail and require fresh host authority before a new attempt;
|
|
128
|
+
Gmail does not provide a general idempotent-send guarantee.
|
|
129
|
+
|
|
130
|
+
## GitHub context store
|
|
131
|
+
|
|
132
|
+
Kind `github-context-store`, version `1`, implements the standard
|
|
133
|
+
`context-store.query/v1` and `context-store.ingest/v1` contracts:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"endpoint": "https://api.github.com",
|
|
138
|
+
"repository": "owner/private-backing-store",
|
|
139
|
+
"ref": "refs/heads/context-v1",
|
|
140
|
+
"prefix": ".gik-state/context/v1",
|
|
141
|
+
"credentialRef": "github/context-store",
|
|
142
|
+
"maxRecordsPerScope": 1000,
|
|
143
|
+
"maxWriteAttempts": 3
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The configured ref must already exist. The provider never creates repositories,
|
|
148
|
+
refs, credentials or authorization. The host resolves `credentialRef` to a
|
|
149
|
+
`GitHubContextStoreConnection` that adds authentication internally, and must
|
|
150
|
+
authorize every concrete `https://api.github.com` URL before the provider asks
|
|
151
|
+
for that connection. Query-only declarations require GitHub Contents read
|
|
152
|
+
permission; a declaration containing ingest requires Contents write permission.
|
|
153
|
+
|
|
154
|
+
Scopes and record identities are SHA-256 encoded into repository paths.
|
|
155
|
+
Ingestion creates immutable record blobs and a bounded recent-record index in
|
|
156
|
+
one Git commit, then advances the declared ref without force. Reusing an
|
|
157
|
+
identity with different content fails. A non-fast-forward ref race is rebuilt
|
|
158
|
+
from the new head up to `maxWriteAttempts`; exhaustion is explicit. Query reads
|
|
159
|
+
one ref revision and returns at most the requested standard limit. The Git
|
|
160
|
+
revision used or created is returned in `ServiceExecutionResult.detail`, while
|
|
161
|
+
the standard context-store output shape remains unchanged.
|
|
162
|
+
|
|
163
|
+
This provider stores only bounded, non-sensitive context. It is not a token
|
|
164
|
+
vault, GitHub event archive, durable-runtime journal, or substitute for host
|
|
165
|
+
authorization. Actions artifacts and Copilot Memory have different retention
|
|
166
|
+
and consistency semantics and are not silently used as fallbacks.
|
|
167
|
+
|
|
168
|
+
## Distribution and release
|
|
169
|
+
|
|
170
|
+
All subpaths are independently built and smoke-tested from the actual npm
|
|
171
|
+
tarball in a clean consumer, including TypeScript and the absent root export.
|
|
172
|
+
GitHub Actions performs all installation, build, typecheck and tests for this
|
|
173
|
+
repository. Publishing remains a protected maintainer-approved action; see the
|
|
174
|
+
provider release workflow and its first-package bootstrap instructions.
|
|
175
|
+
|
|
176
|
+
The first release cannot use npm trusted publishing until the package exists.
|
|
177
|
+
A maintainer must provision a narrowly scoped `NPM_TOKEN` in the protected
|
|
178
|
+
`npm-publish` environment, dispatch `release-service-providers.yml` **from the
|
|
179
|
+
immutable `providers-v0.1.0` workflow ref**, with the same tag input, `bootstrap: true` and
|
|
180
|
+
`confirm: PUBLISH gik-service-providers`, then approve the environment. Validation
|
|
181
|
+
and tarball creation run without that token; publication consumes the inspected
|
|
182
|
+
bytes with lifecycle scripts disabled. Signature/provenance and clean-consumer
|
|
183
|
+
verification remain mandatory. Afterward configure npm's trusted publisher for
|
|
184
|
+
this repository's `release-service-providers.yml` workflow and `npm-publish`
|
|
185
|
+
environment, and revoke the bootstrap token. Normal releases use OIDC only.
|
|
186
|
+
|
|
187
|
+
The workflow event SHA must equal the release candidate commit: checking out the
|
|
188
|
+
tag from an unrelated dispatch branch does not change npm's provenance source.
|
|
189
|
+
For example:
|
|
190
|
+
|
|
191
|
+
```sh
|
|
192
|
+
gh workflow run release-service-providers.yml --ref providers-v0.1.0 \
|
|
193
|
+
-f tag=providers-v0.1.0 -F bootstrap=true \
|
|
194
|
+
-f 'confirm=PUBLISH gik-service-providers'
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
An ordinary release of an absent package reports `bootstrap-required` before
|
|
198
|
+
requesting protected publication approval and completes without uploading. This
|
|
199
|
+
frees the shared concurrency slot for the explicit bootstrap run. Recovery skips
|
|
200
|
+
an already-published version only when its integrity matches the reviewed
|
|
201
|
+
candidate; differing bytes fail closed. Skipping upload never skips independent
|
|
202
|
+
registry signature/provenance and clean-consumer verification.
|
|
203
|
+
|
|
204
|
+
Future production/package changes are versioned in the same PR by a trusted-base
|
|
205
|
+
App workflow (patch by default, explicit `release:minor`/`release:major` labels).
|
|
206
|
+
Test-only changes do not bump this package. Immutable `providers-v` release tags
|
|
207
|
+
must match the package version and current main commit. Publishing never builds
|
|
208
|
+
or repacks while credentials are available and always rechecks source/tag/digest
|
|
209
|
+
after the protected approval gate.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ServiceKindFactory } from 'gik-controlface/services';
|
|
2
|
+
|
|
3
|
+
interface GitHubContextStoreHttpRequest {
|
|
4
|
+
url: string;
|
|
5
|
+
method: "GET" | "POST" | "PATCH";
|
|
6
|
+
headers: Readonly<Record<string, string>>;
|
|
7
|
+
body?: string;
|
|
8
|
+
signal: AbortSignal;
|
|
9
|
+
}
|
|
10
|
+
/** The host owns authentication and adds it inside this connection. */
|
|
11
|
+
interface GitHubContextStoreConnection {
|
|
12
|
+
request(request: Readonly<GitHubContextStoreHttpRequest>): Promise<Response>;
|
|
13
|
+
}
|
|
14
|
+
declare class GitHubContextRecordConflictError extends Error {
|
|
15
|
+
readonly code = "github-context-record-conflict";
|
|
16
|
+
constructor(recordId: string);
|
|
17
|
+
}
|
|
18
|
+
declare class GitHubContextStoreRevisionConflictError extends Error {
|
|
19
|
+
readonly ref: string;
|
|
20
|
+
readonly attempts: number;
|
|
21
|
+
readonly code = "github-context-store-revision-conflict";
|
|
22
|
+
constructor(ref: string, attempts: number);
|
|
23
|
+
}
|
|
24
|
+
declare function createGitHubContextStoreServiceKind(): ServiceKindFactory;
|
|
25
|
+
|
|
26
|
+
export { GitHubContextRecordConflictError, type GitHubContextStoreConnection, type GitHubContextStoreHttpRequest, GitHubContextStoreRevisionConflictError, createGitHubContextStoreServiceKind };
|