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 CHANGED
@@ -1,3 +1,209 @@
1
- # Temporary Holding Version
1
+ # GIK service providers
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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 };