kloudy 0.4.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/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/MCP-DEFAULT.md ADDED
@@ -0,0 +1,12 @@
1
+ # MCP Default — Bot Profile Creation
2
+
3
+ **Public-facing note for kloudy-open (2026-09-28)**
4
+
5
+ When an MCP is registered with Kloudy or built with Kloudy's help, the **default** is that bots may create profiles on it. This is disclosed to providers at registration and in partner docs; any provider can opt out at any time from their Kloudy settings.
6
+
7
+ - Registry listing is metadata only — Kloudy still authenticates per provider through its own vault.
8
+ - The open default makes converted and registered MCPs maximally useful to the bot economy, which is the audience Kloudy is built for.
9
+ - Opt-out is immediate for new requests; in-flight actions complete or fail cleanly.
10
+ - Spam and abuse are handled by per-bot and per-provider rate limits and the session kill switch — not by closing the default.
11
+
12
+ See the full decision in kloudy-shared: `decisions/2026-09-28-mcp-registry-default-bot-profile-creation.md`.
package/README.md ADDED
@@ -0,0 +1,286 @@
1
+ # Kloudy — add it to your editor
2
+
3
+ ```sh
4
+ npx kloudy
5
+ npx kloudy find github MCP tools
6
+ npx kloudy mcp
7
+ ```
8
+
9
+ Node.js 20.19+ is required. Run `npx kloudy` to install, or `npx kloudy@0.4.0` to pin this release. A downloadable tarball is also available at https://kloudy.ai/downloads/kloudy-open-0.4.0.tgz.
10
+
11
+ No arguments detects Cursor, VS Code, Claude Desktop, Claude Code, Windsurf and Codex configurations and asks before writing **each** one. Declining or non-interactive input changes nothing. Existing servers and existing Kloudy entries are preserved. JSONC comments in VS Code and TOML comments in Codex remain intact. Backups are created; malformed or concurrently changed configs are left untouched. Normal OS file permissions apply on Windows; protect config files that contain keys.
12
+
13
+ The remote endpoint is https://kloudy.ai/mcp. Public catalog discovery works without a key and makes no model call. It returns up to five public metadata candidates, not execution grants. Account actions still require an existing authorized Kloudy key; self-service engine-key issuance is not connected. If you already have one, set `KLOUDY_MCP_TOKEN` privately in your environment; the installer asks before storing it in a client's headers (or Claude Desktop's stdio environment). Keys are never accepted as command-line arguments or printed by the installer.
14
+
15
+ `mcp` is a local stdio-to-Streamable-HTTP proxy; stdout contains MCP messages only. Direct questions and the proxy call the same remote server. No retries of tool calls. Network failures and invalid keys fail explicitly.
16
+
17
+ The existing SDK exports remain available under `kloudy/*`. For the pre-0.4 specialized engine CLI commands documented below, use `kloudy legacy <command>` (for example, `kloudy legacy init my-app`). The original CLI is retained in `bin/kloudy.mjs`.
18
+
19
+ One-click Cursor / VS Code setup: https://kloudy.ai/install. Browser deep links ask the editor to install; opening a link is not proof that installation completed. ChatGPT requires its directory program; this package does not modify ChatGPT. MCP Registry identity: `io.github.Gio300/kloudy`.
20
+
21
+ ## Earlier engine adapter documentation
22
+
23
+ # Ask for Kloudy
24
+
25
+ One CLI, one MCP, tools appear when needed. CLI, MCP and the Node.js SDK use the same scoped Blackbox session. This repository contains the public client adapters and local HTTPK wrapper, not the private router, vault or account service.
26
+
27
+ ## Build a first app without an account
28
+
29
+ ```sh
30
+ npm install -g https://kloudy.ai/downloads/kloudy-open-0.3.14.tgz
31
+ kloudy init my-kloudy-app
32
+ cd my-kloudy-app
33
+ npm install
34
+ npm start
35
+ npm run card -- /world
36
+ ```
37
+
38
+ `init` creates a new folder and refuses to overwrite an existing one. `kloudy read https://example.com` and `kloudy card /world` also work directly. These are real public reads, with no model or account call; shared rate limits and website restrictions apply. The public Node exports are `readPublicPage` and `readCard` from `kloudy/public`. Source HTML stays data, never executable UI. The starter’s separate toolbox example requires an existing private connection and selects definitions without executing them.
39
+
40
+ Developer guide and availability: https://kloudy.ai/build. Customer self-registration is separate from these working public examples.
41
+
42
+ ## Install and run
43
+
44
+ Node.js 20.19 or newer is required. The tested package is available directly from Kloudy; the source package is distributed by Kloudy, and the hosted door has an MCP Registry listing. It is not an npm registry publication.
45
+
46
+ ```sh
47
+ npm install -g https://kloudy.ai/downloads/kloudy-open-0.3.14.tgz
48
+ kloudy
49
+ ```
50
+
51
+ From a source checkout: `npm ci`, `npm test`, `npm link` (or `node bin/kloudy.mjs help`). There is no install-time script that edits your IDE.
52
+
53
+ Ask for Kloudy and register with email plus short 2FA; the intended account service then supplies a session cookie, tag or OAuth connection. **That Core registration endpoint is not delivered yet.** `kloudy login` reports this clearly, collects no email/code and exits 3. This package does not create fake accounts or mint production credentials.
54
+
55
+ An existing scoped Blackbox connection can be supplied through a private connection file created by the native host/operator. It contains the engine origin and access token; do not put it in Git, a prompt or public page. `KLOUDY_CONNECTION` can refer to its path so you do not repeat `--connection`.
56
+
57
+ ```sh
58
+ kloudy ask "Build an app to create private notes" --connection /private/connection.json --candidates discovery-cards.json
59
+ kloudy call bbe_notes_create --connection /private/connection.json --inputs note-inputs.json
60
+ kloudy status --connection /private/connection.json
61
+ kloudy cancel --connection /private/connection.json
62
+ ```
63
+
64
+ `ask` accepts up to 16 supplied KLDY/FHIR metadata candidates. Blackbox ranks them and supplies at most one authorized definition. No client-side scoring or whole-server catalog download substitutes for the engine. With no candidates, the client asks the existing destination route for an information card and returns an empty Toolbox; automatic catalog/web discovery needs the engine feed. No match, expired grants and missing registration remain explicit. Only `call` submits the chosen tool inputs, and the engine retains exact approval authority.
65
+
66
+ ## One MCP in the IDE
67
+
68
+ ```sh
69
+ kloudy # introduce Kloudy and this project; ask once to install
70
+ kloudy introduce --decision no # stay chat-only
71
+ kloudy install --ide codex # explicit consent; user-level installation
72
+ kloudy install --ide cursor --connection /private/connection.json
73
+ # Once a hosted MCP endpoint and scoped user grant are provisioned:
74
+ kloudy install --ide codex --url https://kloudy.ai/mcp --token-env KLOUDY_MCP_TOKEN
75
+ ```
76
+
77
+ One user installation covers projects. Cursor, Claude Code, VS Code and Codex receive their own supported **user-level** MCP config, without modifying project files. The installer preserves other entries, backs up changes, and leaves conflicting Kloudy entries untouched. Codex TOML comments are preserved. JSONC/non-JSON configs are left unchanged. Cursor and Claude receive a user slash command; every MCP host receives a `kloudy` prompt and default introduction operation. Host approval/trust policies remain in force. The local installation record is not an account or permission grant.
78
+
79
+ Bare `kloudy`, `Kloudy`, `/kloudy`, and `ask Kloudy` in this CLI return a project-aware introduction before opening an engine session. The MCP tool does the same for `introduce` or an exact attach intent. It classifies known manifest names/dependencies, looks up at most three relevant public catalog names, and asks one install/connect question. Catalog candidates are explicitly non-executable until granted. It never reads `.env`, source contents or another project's checkpoint. With an HTTPK connection, the introduction comes from the real engine's `bbe.attach.v1` intent payload. Without a reachable account/session, the client provides a clearly marked deterministic public-catalog fallback. An unconfigured IDE cannot discover a CLI just from an ordinary chat word: install the MCP adapter first, then invoke the tool or `/kloudy` prompt.
80
+
81
+ The remote option stores only a bearer **environment-variable reference**. `https://kloudy.ai/mcp` now serves authenticated MCP alongside its human guide. The current hosted engine is a finite development service with separately scoped Notes and bounded Base chain-head read clients; it is not open customer registration. The default local stdio adapter works without hosting. With no explicit connection path, all three clients use `~/.kloudy/connection.json`, the same user session across projects.
82
+
83
+ The MCP tool list initially has one `kloudy` entry. An `ask` operation adds only the selected tool as `selected_<engine-name>`. Selecting a new task replaces it; running it clears the selection. Status/cancellation use the Kloudy entry and the same engine request. Model-visible outputs never contain the bearer.
84
+
85
+ ## SDK
86
+
87
+ ```js
88
+ import {withConnection} from 'kloudy/session';
89
+ const result = await withConnection('/private/connection.json', client =>
90
+ client.ask('Find a tool for this task', {candidates, lane: 'non_medical'})
91
+ );
92
+ console.log(result.toolbox); // zero or one engine-selected definition
93
+ ```
94
+
95
+ `KloudyClient` and `httpTransport` are also exported for trusted Node/native hosts with their own credential storage. Never embed a provider token in browser code. Voice, text, exchange, listing and hosting tool packs use this same ask/call boundary as their capabilities are registered; this client does not claim those providers are all live.
96
+
97
+ The connection adapter stores only session/selection/request references in a private local checkpoint. CLI, MCP and SDK using the same connection file share that checkpoint. A lock prevents simultaneous mutation; no blind mutation retry is performed. If a process is forcibly killed, first verify it is stopped, then remove the empty `kloudy-open-session-<connection-hash>.json.lock` directory next to its connection file. Do not remove a lock held by a live client. Backend expiry, reauthorization, cross-device delegation and HTTPK token refresh remain engine/Core operations.
98
+
99
+ ## Local HTTPK wrapper
100
+
101
+ `kloudy wrap --config /private/wrap.json` runs an operator-configured stdio MCP behind an authenticated, stateless Streamable HTTP endpoint. The config supplies `command`, `args` and `tools` (the exact selected tool names). An optional `env` contains only the deliberately passed upstream environment. Supply the wrapper bearer through `KLOUDY_WRAP_TOKEN`, obtained from the owning host/grant service. The token is never printed.
102
+
103
+ The listener binds loopback. It rejects browser Origin requests, non-loopback Host headers, invalid/missing bearer, oversized bodies and tools outside its allowlist. Upstream stderr is not forwarded. Upstream crashes return 502 and timeouts return 504. Maximum two simultaneous upstream processes; each closes after its request. Local-file tools stay local. Public HTTPS hosting, Core identity, VaultKeeper token validation/rotation and managed reconnect need their owning edge services; this package does not expose a local bearer endpoint publicly or create permanent grants.
104
+
105
+ MCP transport compatibility is pinned to the official SDK 1.31.0 and its 2025-11-25 handshake. Newer transport revisions require a tested upgrade; this is not an unversioned protocol claim.
106
+
107
+ ## Verification
108
+
109
+ `npm test` covers real HTTP/stdio round trips, selected-tool isolation, auth/origin rejection, crash handling, shared SDK behavior, explicit approval and safe IDE installation.
110
+
111
+ The real-engine acceptance script uses a separately provisioned local Blackbox instance and synthetic public metadata, never a mock engine. It exercises CLI → MCP → SDK → autonomous completed Notes result, with no extra approval call. It intentionally requires an explicit test flag:
112
+
113
+ ```sh
114
+ node scripts/prove.mjs --connection /private/test-connection.json --candidates /path/to/test-cards.json --approve-synthetic
115
+ ```
116
+
117
+ The proof writes `.cache/live-engine-proof.json` without credentials. A completed synthetic Notes result is not a claim of hosted registration, live provider discovery or unrelated integrations.
118
+
119
+ Protocol references: [MCP SDK](https://ts.sdk.modelcontextprotocol.io/), [Cursor MCP](https://prod.cursor.com/help/customization/mcp), [Claude MCP](https://code.claude.com/docs/en/mcp), [VS Code MCP](https://code.visualstudio.com/docs/agent-customization/mcp-servers). Private coordination source: Gio300/kloudy-shared session-routing and passive-narrowing contracts.
120
+
121
+ ## Approval defaults
122
+
123
+ CLI and IDE MCP are autonomous: the host permission policy and scoped engine grant govern execution. There is no CLI approve command or confirm option. The SDK accepts `mode: 'autonomous'` (default) or `mode: 'confirm'`; for example `withConnection(file, work, {mode: 'confirm'})` for a trusted confirm-mode embed. Do not use CLI-issued credentials for a browser embed. The engine binds the surface to the grant; clients cannot mint or relabel credentials.
124
+
125
+ The client verifies engine mode acknowledgment before routing. An older engine that still returns `awaiting_approval` in autonomous mode produces `engine_autonomy_not_supported`; it never fabricates a tap or calls approval automatically. Upgrade the engine or cancel that request. Hosted OAuth, credential refresh and public MCP deployment remain owned engine/Core capabilities.
126
+
127
+ ## Hosted HTTPK (0.3.0)
128
+
129
+ A private connection may contain `endpoint: "https://kloudy.ai/mcp"`, `access_token` and `expires_at`, obtained through the owning grant service. The same CLI/MCP/SDK adapter then uses the deployed engine's HTTPK protocol. No user key belongs in this README, an install command or a prompt. The hosted development engine currently understands Notes actions such as `save note: ...` and `read note ID`; external discovery candidates are explicitly rejected on this limited transport.
130
+
131
+ A standard remote MCP host sees one `kloudy` tool. `introduce` gets the engine greeting; `ask` returns the selected singular tool and exact arguments; `call` carries that selection; `status`/`cancel` use its receipt. The adapter forwards authority to Blackbox and never invents approvals, risk classifications, model retries or provider access. The hosted protocol is MCP 2025-06-18; the pinned official SDK negotiates it.
132
+
133
+ `node scripts/prove-hosted.mjs` uses an in-memory, expiring test grant and a temporary user config to prove the real public MCP handshake, engine greeting, one selected tool, completed Notes receipt, idempotent replay and SDK readback. It does not claim a visual IDE session or the provider-model failure ladder was tested. Those remaining checks are recorded in Shared.
134
+
135
+ ## Selected Toolboxes — 0.3.1
136
+
137
+ With a scoped HTTPK connection, each ask returns its engine selection_id. Collect 1–16 distinct IDs from that same session. Assembly is passive: no model, action or approval runs.
138
+
139
+ ```sh
140
+ kloudy ask "read note NOTE_ID" --connection /private/httpk.json
141
+ kloudy ask "save note: proposed draft" --connection /private/httpk.json
142
+ # Put only the returned selection IDs into selections.json as a JSON array.
143
+ kloudy assemble --selections selections.json --connection /private/httpk.json
144
+ kloudy call TOOL_NAME --selection SELECTION_ID --inputs exact-inputs.json --connection /private/httpk.json
145
+ ```
146
+
147
+ SDK: client.assemble(ids), then client.call(name, exactArguments, {selectionId}). Local MCP and the public hosted MCP keep one kloudy gateway with operation assemble and selection_ids; operation call uses the exact selection_id, name and arguments. Assembly returns deduplicated tools and exact per-selection metadata, so two selections of one tool retain their distinct arguments. The engine validates ownership, grant scope and per-action authorization. No bulk execution or blind retry is added. Legacy local-engine connections retain their previous single-selection interface and explicitly reject HTTPK-only assembly.
148
+
149
+ This consumes deployed BBE 0.3.46. Executable sources are registered Notes today, not arbitrary external MCP/SDK wrappers. Permanent grants and dormant-connection lifecycle still follow the owning GlassBreak/provider services.
150
+
151
+ ## Explicit confirmation for embedded browser hosts
152
+
153
+ Open 0.3.4 adds `HttpkClient.confirm({selectionId, token, decision: 'approve', source: 'tap'})` for an existing confirm-mode connection. Pass the exact pending `approval.token`, or its integer `revision` instead, never both. Preserve the actual approval source (`tap`, `typed`, `os-voice`); do not label model text or external content as a user's approval. The engine still enforces risk, source, expiry and request binding. Autonomous IDE/CLI behavior is unchanged.
154
+
155
+ The same public MCP gateway accepts `operation: 'confirm'`, `selection_id`, `decision: 'approve'`, `source`, and exactly one of `token` or `revision`. Missing binding/source fails closed. `scripts/prove-confirmation.mjs` verifies a real bounded Base read, wrong-token denial, zero execution before approval and receipt replay using an existing finite grant from memory. No provider key is distributed.
156
+
157
+ ## IDE experience — 0.3.4
158
+
159
+ An already attached MCP host receives a welcome and current-project guidance, not another install question. Kloudy or /kloudy explicitly addresses the service; the IDE may use the same tools for relevant tasks without a prefix. The current task selects a small toolbox; a persistent account connection does not load its tools into every project.
160
+
161
+ The host guidance describes natural keep/cleanup requests and the authoritative lifecycle boundary. It does not pretend that saved mixed-tool workflows, dormant pruning, permanent grants, OAuth signup or user-key transfer have already shipped. Those require the existing engine/Core/GlassBreak interfaces. No synthetic authorization button or password/code collection is added. Kloudy.ai is the use/research surface; building remains in the user's IDE or Bot Boozle.
162
+ # SDF yield layer
163
+
164
+ `kloudy convert https://example.com` extracts a public page through Kloudy's bounded reader without a model call. Add `--resolution standard` for more detail; compact cards default to at most 8,192 UTF-8 JSON bytes. Try the same converter at [kloudy.ai/build](https://kloudy.ai/build#convert).
165
+
166
+ ```js
167
+ import {convertPage, convertPages} from 'kloudy/public';
168
+ import {validateSDF} from 'kloudy/sdf';
169
+
170
+ const card = await convertPage('https://example.com');
171
+ console.log(validateSDF(card), card.summary.brief);
172
+ const list = await convertPages(['https://example.com', 'https://example.com/missing']);
173
+ console.log(list.cards, list.reports); // Each unreadable source is reported.
174
+ ```
175
+
176
+ MCP's single `kloudy` door also accepts `operation: "convert", url` or `operation: "yield_list", urls` (1–8). No account is needed for these public reads. The engine owns KLDY query matching and ranking. This client returns content and per-source conversion reports, never a ranked recommendation or permission to execute tools.
177
+
178
+ Cards validate against the unmodified SDF v0.2 root schema pinned to `sdfprotocol/sdf@e4619ad0b951ced8542692c6b624cb86794cb863` and the additional Kloudy webpage profile. This upstream machine-readable schema differs from its website examples: it uses `sdf:<sha256>`, `summary.brief`, `source.fetched_at`, and section `title`/`summary`. Schemas and upstream MIT notice ship in `schemas/` and at [kloudy.ai/schemas/sdf-webpage-v1.schema.json](https://kloudy.ai/schemas/sdf-webpage-v1.schema.json). `article.x-kloudy-webpage` is our explicit extractive subtype, not a claim that an arbitrary page is a news article. Empty entities mean no semantic entity extraction was performed.
179
+
180
+ The `extensions["x-kloudy"]` object declares informational-only behavior, no capabilities, resolution, truncation, and unverified ownership/publication. Validation proves structure, not factual accuracy or source ownership. Source content remains untrusted. The reader does not sign in, run page scripts, bypass access controls, or guarantee extraction of text hidden behind JavaScript. Errors are reported instead. Compact output can exceed the size of an already-tiny source; no universal savings percentage is claimed.
181
+
182
+ Unknown protocol/profile versions are rejected. Breaking card changes require a new profile major version; optional compatible additions keep the version. Card identity binds source URL and raw source hash. Retrieval timestamps can change without changing identity. Existing browser support for the older documented `sdf_`/`one_line` display format remains available.
183
+
184
+ ## Saved mixed strings
185
+
186
+ The private local `~/.kloudy/sources.json` maps trusted aliases to existing HTTPK connection files. `kind` describes the registered provider adapter; this does not create a missing SDK/CLI/MCP adapter or grant credentials.
187
+
188
+ ```json
189
+ {"notes":{"kind":"mcp","connection":"notes-connection.json"},"catalog":{"kind":"sdk","connection":"catalog-connection.json"},"build":{"kind":"cli","connection":"build-connection.json"}}
190
+ ```
191
+
192
+ Save a selection file containing only references and goals, for example `[{"source":"notes","kind":"mcp","goal":"Read my notes"},{"source":"catalog","kind":"sdk","goal":"Find this resource"}]`.
193
+
194
+ ```sh
195
+ kloudy strings save workspace --selections choices.json
196
+ kloudy strings use workspace
197
+ kloudy strings call workspace --tool s0_bbe_notes_read --inputs inputs.json
198
+ kloudy strings keep workspace
199
+ kloudy strings list
200
+ kloudy strings prune
201
+ kloudy strings remove workspace
202
+ ```
203
+
204
+ `use` reselects through each source and returns up to eight distinct tools with source-bound call routes. It never automatically executes the tools. The engine validates exact owned selections at call time; a changed session requires reselection. Strings are project-scoped and stored privately, with no provider credentials or schema dumps. An explicit `prune` removes non-kept choices idle for 30 days. Normal reuse never silently deletes a saved configuration. Authoritative production-phase dormancy events are a separate Core/engine integration. This removes local saved choices, not shared provider keys. `keep` preserves reuse intent; it does **not** mint a permanent grant. GlassBreak service wiring remains an external dependency.
205
+
206
+ SDK: `StringStore`/`localStrings` from `kloudy/strings`. MCP operations: `strings_save`, `strings_list`, `strings_use`, `strings_call`, `strings_keep`, `strings_remove`, `strings_prune` with `string_name`, `choices`, and, for calls, `name` and `arguments`. Mixed-adapter composition is covered by fixtures; production execution still requires each source's registered adapter and current grant.
207
+
208
+ ## Inline action approval
209
+
210
+ The stdio MCP server negotiates MCP Apps support. Supported hosts receive a resource with the engine's exact summary, risk and expiry, plus Approve/Cancel buttons. App-only tickets bind an exact engine selection; the engine approval token stays out of model-visible content. Expired, reused and missing tickets fail closed. Approval dispatch is not silently retried after an ambiguous failure. Restarting the server invalidates its ephemeral tickets; fetch the current engine state again.
211
+
212
+ This is action consent, not customer OAuth. Unsupported hosts do not gain an inline button by returning Markdown. The host must support MCP Apps and its app-only tool visibility rules. CLI autonomous behavior remains unchanged. Tests cover the protocol exchange and exact confirmation binding; acceptance in every third-party IDE and the hosted remote MCP surface is separate work.
213
+
214
+
215
+ ## Yield and query boundary
216
+
217
+ `kloudy convert URL` and `convertPage` return SDF content; `kloudy query "need"` and `HttpkClient.query` ask Blackbox for ranked card references. Search grants no permission. The SDK `sdfIndex(cards, {discoveredVia: 'geo'})` from `kloudy/query` prepares bounded metadata for the existing operator-only engine import; it does not upload, register a tool, or grant access. Supply the actual SEO/GEO provenance, never guess it. `content_sha256` is the raw source body hash. Empty capability IDs deliberately avoid inferring tool access from prose.
218
+
219
+ Compact cards fit **750 cl100k_base tokens**, measured over compact JSON including attribution and metadata, and at most 8 KiB UTF-8. The tokenizer runs locally with bundled ranks; no model or tokenizer service is called. Token counts vary across model families. Standard resolution is explicitly larger. Oversized attribution fails with a readable report rather than losing the source URL. Updated content gets a new deterministic ID; fetching the same source keeps its ID. The schema remains pinned to SDF 0.2.0 plus `kloudy.sdf.webpage/1`; unsupported versions fail validation. An index lifetime is distinct from long-term content recheck policy.
220
+
221
+ Type Kloudy in a connected IDE to receive the same old-school cloud welcome. Dot and Grok Boi use this same normal door. The host still must install/attach the client first and controls how text visuals and MCP Apps are rendered. Bot Boozle is the native ecosystem IDE; this client does not claim to deploy its sentence-navigation UI.
222
+
223
+ A string deduplicates tool definitions, not owned selections. If two choices select the same tool with different arguments, both exact selection IDs remain in `routes`/`selections`; pass `--selection ID` on CLI calls or `{selectionId}` to `StringStore.call`. An ambiguous call fails without executing. MCP uses `selection_id`.
224
+
225
+ ## IDE activity indicator
226
+
227
+ Version 0.3.11 supplies the public host adapter `kloudy/activity` and stylesheet `kloudy/status-indicator.css`. A compatible host mounts the purple orb with Kloudy above it in its own status surface. Load the stylesheet with your app bundler, then:
228
+
229
+ ```js
230
+ import {mountStatusIndicator, observeClient} from 'kloudy/activity';
231
+ const indicator = mountStatusIndicator(statusBarElement);
232
+ const visibleClient = observeClient(client, event => indicator.update(event));
233
+ await visibleClient.call(selectedTool, inputs);
234
+ // When this status surface closes:
235
+ indicator.dispose();
236
+ ```
237
+
238
+ Node hosts can use `withConnection(file, work, {onActivity})` and forward these bounded events over their existing trusted webview bridge. Events contain only schema/source/sequence/state/operation, never tool inputs, tokens or receipts. Pending work pulses; approval waits hold still; completed calls settle; failures visibly report failure. Concurrent reads keep the indicator busy until all outstanding work returns. Observer failures cannot retry or break an engine call. Reduced motion is honored.
239
+
240
+ Native hosts may feed listening/speaking events only while their real OS voice operation is active. This client never records speech. Generic MCP cannot inject an orb into an arbitrary IDE status bar: actual integration in Bot Boozle or another IDE requires that host to mount the adapter. The preview at `node scripts/preview-indicator.mjs` labels its state buttons as simulations; it is not proof of deployment in every IDE.
241
+
242
+ Wallet preview: `kloudy wallet balance --connection FILE` or `kloudy wallet top_up --amount 10.00 --connection FILE`. The SDK exposes `HttpkClient.wallet({operation, amount_usd})`; MCP uses the same single Kloudy tool with `operation: wallet`. These call the existing `kloudy/wallet` method. A preview never signs a transfer, starts payment or asserts a usable balance. Transfer adapters remain engine capabilities; no separate HTTPK or private signing key is added. Terminal activity uses stderr only when attached to a TTY.
243
+
244
+
245
+ ### Chain, wallet, BWW and hosting (BBE 0.3.48)
246
+
247
+ All operations reuse the same initialized HTTPK session and scoped customer grant.
248
+ `client.action(name, inputs, {idempotencyKey})` selects exactly one tool. It does
249
+ not execute, approve, sign or broadcast. Use the returned tool/arguments with
250
+ `client.call`, then inspect the receipt; retain the same key after an uncertain
251
+ selection response. Supported product actions: `chain.head`, `wallet.prepare`,
252
+ `bww.fetch`, `bww.publish`. BWW publication is owner-scoped storage, not public
253
+ customer hosting. The provider opts into HTML; callers request it explicitly.
254
+
255
+ ```sh
256
+ kloudy action chain.head --inputs head.json --idempotency-key inspect-1 --connection FILE
257
+ kloudy wallet-connect ethereum --connection FILE
258
+ kloudy hosting --connection FILE
259
+ kloudy hosting --budget-micros 100000000 --connection FILE
260
+ ```
261
+
262
+ `head.json` is `{"chain":"ethereum"}`. SDK equivalents are
263
+ `walletConnection('ethereum')` and `hosting({budgetUsdMicros:100000000})`.
264
+ MCP uses the existing `kloudy` tool with `operation: action`, `wallet_connection`
265
+ or `hosting`. Budget values are integer USD micros. Preserve the authoritative
266
+ `customer_budget_active`, `tax_included` and enforcement fields: a development
267
+ meter budget is not an active full customer spending limit. Wallet connection
268
+ returns namespace requirements, not a QR or ownership proof. The surface SDK
269
+ creates pairing using its configured project ID. The user's wallet signs each
270
+ transaction; API credentials are not wallet private keys. No cross-chain
271
+ settlement is claimed by these adapters. Public customer grant exchange,
272
+ fee-recipient configuration and Reown live pairing remain deployment gates.
273
+
274
+ ### Your own models and local compute
275
+
276
+ `kloudy models list` reads the existing local Ollama model catalog without inference or downloads. `kloudy models run MODEL --prompt-file prompt.txt --max-tokens 128` makes one bounded request to an installed local model. Ollama cloud aliases are not treated as free local models. No account or key is required for installed local Ollama models; machine resources still belong to you. If Ollama is absent, use its official https://ollama.com/download installer. No model is silently downloaded.
277
+
278
+ For your own LiteLLM/OpenAI-compatible gateway, configure `KLOUDY_MODEL_ENDPOINT` and `KLOUDY_MODEL_KEY_ENV` (the environment variable name containing the scoped virtual key). Use `--allow-provider-charge` for an explicitly requested provider-backed completion. Your provider bills you directly; this flag does not grant Kloudy a spending budget. Never put actual keys in URLs, arguments, source or chat. The SDK `ModelClient` accepts an asynchronous `resolveKey` for a host vault and reads it on every request. This supports an already managed key rotating without a client rewrite; it does not issue a key, create an account or implement GlassBreak itself.
279
+
280
+ The single MCP door adds `models_list` using host environment configuration, with no endpoint or secret supplied by model arguments. It does not expose provider generation as an automatically billable tool. Listed models are advertised, not guaranteed compatible; a successful bounded completion provides inference evidence. The server must enforce provider budgets and authorization.
281
+
282
+ Sources: https://docs.ollama.com/api/tags ; https://docs.ollama.com/api/openai-compatibility ; https://docs.litellm.ai/docs/proxy/virtual_keys .
283
+
284
+ ### Host interface and deterministic replies
285
+
286
+ HTTPK initialization negotiates chat/text/speech presentation with BBE 0.3.50. It never changes scopes, the credential surface or the approval policy. Older engines remain usable without invented negotiation. `renderReply(state,{action})` from `kloudy/replies` renders bounded JSON templates for observed operation state. Unknown state stays unconfirmed; output is plain text. The browser uses the same templates in its one conversation. Kloudy’s subscription NVIDIA ladder is the text/planning upgrade; supported local Siri/Nano remains voice. Bot Boozle is a separate optional IDE.
package/bin/entry.mjs ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import {createInterface} from 'node:readline/promises';
3
+ import {installEditors} from '../src/editor-install.mjs';
4
+ import {askRemote,serveRemote} from '../src/remote.mjs';
5
+ const args=process.argv.slice(2);
6
+ try{
7
+ if(!args.length||args[0]==='install'&&args.length===1){
8
+ console.log('Kloudy · Add to your editor\nFree public discovery. Account actions require an authorized Kloudy key.');
9
+ let rl;
10
+ const confirm=async({client,path,authenticated})=>{if(!process.stdin.isTTY){console.log(`${client}: skipped — run in an interactive terminal to approve ${path}`);return false;}rl??=createInterface({input:process.stdin,output:process.stdout});return /^(y|yes)$/i.test((await rl.question(`Add Kloudy to ${client}? ${path}${authenticated?' (stores your key)':''} [y/N] `)).trim());};
11
+ let results;try{results=await installEditors({confirm,key:process.env.KLOUDY_MCP_TOKEN,onResult:r=>console.log(`${r.client}: ${r.state}`)});}finally{rl?.close();}
12
+ if(!results.length)console.log('No supported editor configuration found. Open https://kloudy.ai/install for one-click Cursor and VS Code links.');
13
+ else console.log('Existing servers were preserved. Restart configured clients to connect.');
14
+ if(results.some(r=>r.state==='unchanged_error'))process.exitCode=1;
15
+ }else if(args[0]==='mcp'&&args.length===1){await serveRemote();}
16
+ else if(args[0]==='--version'){console.log('0.4.0');}
17
+ else if(['--help','help'].includes(args[0]))console.log('npx kloudy Add to detected editors (asks before each write)\nnpx kloudy <question> Ask the public Kloudy MCP endpoint\nnpx kloudy mcp Remote MCP over local stdio\nnpx kloudy legacy <args> Existing SDK/engine CLI commands\n\nFree discovery needs no key. For authorized account actions set KLOUDY_MCP_TOKEN privately; never put a key in command arguments. Node 20.19+ required.');
18
+ else if(args[0]==='legacy'){process.argv.splice(2,1);await import('./kloudy.mjs');}
19
+ else{if(args[0]?.startsWith('--'))throw Error('Unknown option');const result=await askRemote(args.join(' '));for(const c of result.content||[])if(c.type==='text')process.stdout.write((process.env.KLOUDY_MCP_TOKEN?c.text.replaceAll(process.env.KLOUDY_MCP_TOKEN,'[redacted]'):c.text)+'\n');if(result.isError)process.exitCode=1;}
20
+ }catch{process.stderr.write('Kloudy could not complete the request. Check your connection or authorized key. No action was retried. Run npx kloudy --help.\n');process.exitCode=1;}
package/bin/kloudy.mjs ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import {configuredModels} from '../src/models.mjs';
3
+ import {terminalStatus} from '../src/terminal-status.mjs';
4
+ import {readFile} from 'node:fs/promises';
5
+ import {withConnection} from '../src/connection.mjs';
6
+ import {serveMcp} from '../src/mcp.mjs';
7
+ import {createWrapper} from '../src/wrap.mjs';
8
+ import {install} from '../src/install.mjs';
9
+ import {introduce,isAttachIntent} from '../src/introduce.mjs';
10
+ import {readPublicPage,readCard,convertPage} from '../src/public.mjs';
11
+ import {localStrings} from '../src/strings.mjs';
12
+ import {createStarter} from '../src/starter.mjs';
13
+ const indicator=terminalStatus();
14
+ const args=process.argv.slice(2),command=args.shift()||'introduce';
15
+ function option(name){const index=args.indexOf('--'+name);if(index<0)return null;if(index===args.length-1||args[index+1].startsWith('--'))throw new Error('Option value required');const value=args[index+1];args.splice(index,2);return value;}
16
+ const output=value=>process.stdout.write(JSON.stringify(value,null,2)+'\n');
17
+ try{
18
+ if(args.includes('--mode')||args.includes('--revision')||command==='approve')throw new Error('CLI is autonomous-only');
19
+ const connection=option('connection')||process.env.KLOUDY_CONNECTION;
20
+ const project=option('project')||process.cwd(),decision=option('decision');
21
+ if(command==='help'||command==='--help'){
22
+ console.log('Ask for Kloudy. One CLI, one MCP, tools appear when needed.\n\nkloudy models list [--endpoint URL] [--key-env NAME]\nkloudy models run NAME --prompt-file FILE [--max-tokens 128] [--allow-provider-charge]\nkloudy init my-app\nkloudy read https://example.com\nkloudy card /world\nkloudy convert https://example.com\nkloudy query "need" --connection FILE\nkloudy ask "goal" --connection FILE [--candidates FILE]\nkloudy assemble --selections FILE --connection FILE\nkloudy call NAME --inputs FILE [--selection ID] --connection FILE\nkloudy action chain.head --inputs FILE --connection FILE [--idempotency-key KEY]\nkloudy wallet-connect ethereum --connection FILE\nkloudy hosting [--budget-micros 100000000] --connection FILE\nkloudy wallet balance --connection FILE\nkloudy wallet top_up --amount 10.00 --connection FILE\nkloudy host --connection FILE\nkloudy status --connection FILE\nkloudy cancel --connection FILE\nkloudy mcp --connection FILE\nkloudy introduce [--project DIR] [--decision yes|no]\nkloudy install [--ide cursor|claude|vscode|codex] [--url HTTPS_URL] [--token-env NAME] [--connection FILE]\nkloudy wrap --config FILE [--port 8796]\nkloudy strings save NAME --selections FILE\nkloudy strings use NAME\nkloudy strings call NAME --tool ALIAS --inputs FILE\nkloudy strings list|prune\nkloudy strings keep|remove NAME\nkloudy login\n\nRegister with email + short 2FA through the Core account service. Existing engine connections remain usable. Hosted registration is not supplied by this client package.');
23
+ }else if(command==='models'){
24
+ const action=args.shift(),endpoint=option('endpoint'),keyEnv=option('key-env'),file=option('prompt-file'),max=option('max-tokens');
25
+ const allow=args.includes('--allow-provider-charge');if(allow)args.splice(args.indexOf('--allow-provider-charge'),1);
26
+ const client=configuredModels({...process.env,...(endpoint?{KLOUDY_MODEL_ENDPOINT:endpoint}:{}),...(keyEnv?{KLOUDY_MODEL_KEY_ENV:keyEnv}:{})});
27
+ if(action==='list'&&!args.length&&!file&&!max&&!allow)output(await client.list());
28
+ else if(action==='run'&&args.length===1&&file&&(!max||/^[1-9]\d{0,3}$/.test(max)))output(await client.complete({model:args[0],prompt:await readFile(file,'utf8'),maxTokens:max?Number(max):128,allowProviderCharge:allow}));
29
+ else throw Error('Invalid models arguments');
30
+ }else if(['init','read','card','convert'].includes(command)){const resolution=option('resolution')||'compact';if(args.length!==1||args[0].startsWith('--'))throw Error('One argument required');output(command==='init'?await createStarter(args[0]):command==='read'?await readPublicPage(args[0]):command==='convert'?await convertPage(args[0],{resolution}):await readCard(args[0]));}
31
+ else if(command==='introduce'||isAttachIntent(command)||(command==='ask'&&isAttachIntent(args.join(' ')))){if(decision&&!['yes','no'].includes(decision))throw Error('Choose yes or no');if(decision==='yes')output(await install({ide:option('ide')||'auto',connection,url:option('url')||undefined,tokenEnv:option('token-env')||undefined}));else output(await introduce({project,decision,connection}));}
32
+ else if(command==='install'){option('project');output(await install({ide:option('ide')||'auto',connection,url:option('url')||undefined,tokenEnv:option('token-env')||undefined}));}
33
+ else if(command==='mcp'){await serveMcp(connection);}
34
+ else if(command==='wrap'){
35
+ const file=option('config'),port=Number(option('port')||8796);if(!file||!Number.isSafeInteger(port)||port<1024||port>65535)throw new Error('Invalid wrapper options');
36
+ const config=JSON.parse(await readFile(file,'utf8'));const token=process.env.KLOUDY_WRAP_TOKEN;
37
+ const server=createWrapper({...config,token});server.listen(port,'127.0.0.1',()=>process.stderr.write('Kloudy local HTTPK wrapper listening.\n'));
38
+ }else if(command==='strings'){
39
+ const action=args.shift(),name=args.shift(),file=option('selections'),tool=option('tool'),inputs=option('inputs'),selectionId=option('selection'),store=localStrings({project});
40
+ if(args.length)throw Error('Unexpected strings arguments');
41
+ if(action==='save'){if(!file)throw Error('Selections file required');output(await store.save(name,JSON.parse(await readFile(file,'utf8'))));}
42
+ else if(action==='list')output(await store.list());
43
+ else if(action==='use'){output(await store.use(name));}
44
+ else if(action==='call'){if(!inputs||!tool)throw Error('Tool and inputs required');output(await store.call(name,tool,JSON.parse(await readFile(inputs,'utf8')),{selectionId}));}
45
+ else if(action==='keep')output(await store.keep(name));
46
+ else if(action==='remove')output(await store.remove(name));
47
+ else if(action==='prune')output(await store.prune());
48
+ else throw Error('Unknown strings action');
49
+ }else if(command==='login'){
50
+ output({status:'registration_unavailable',message:'Ask for Kloudy. Email + short 2FA, then a session cookie/tag or OAuth, belongs to the Core account service. No registration endpoint is published yet. No email or code was collected.',url:'https://kloudy.ai/connect'});process.exitCode=3;
51
+ }else{
52
+ const walletAmount=option('amount'),budgetMicros=option('budget-micros'),idempotencyKey=option('idempotency-key');
53
+ const candidatesFile=option('candidates'),inputsFile=option('inputs'),selectionsFile=option('selections'),selectionId=option('selection'),lane=option('lane')||'non_medical';
54
+ if(args.some(x=>x.startsWith('--')))throw new Error('Unknown option');
55
+ const result=await withConnection(connection,async client=>{
56
+ if(command==='action'){if(!client.action||args.length!==1||!inputsFile)throw Error('Action name and inputs file required');return client.action(args[0],JSON.parse(await readFile(inputsFile,'utf8')),{idempotencyKey:idempotencyKey??undefined});}
57
+ if(command==='wallet-connect'){if(!client.walletConnection||args.length!==1)throw Error('Chain required');return client.walletConnection(args[0]);}
58
+ if(command==='hosting'){if(!client.hosting||args.length||budgetMicros!==null&&!/^[1-9]\d*$/.test(budgetMicros))throw Error('Use an integer USD-micros budget');return client.hosting({budgetUsdMicros:budgetMicros===null?undefined:Number(budgetMicros)});}
59
+ if(command==='wallet'){if(!client.wallet||args.length!==1)throw Error('HTTPK wallet operation required');return client.wallet({operation:args[0],amount_usd:walletAmount??undefined});}
60
+ if(command==='query'){if(!client.query)throw Error('Query needs an HTTPK connection');return client.query(args.join(' '));}
61
+ if(command==='ask'){const supplied=candidatesFile?JSON.parse(await readFile(candidatesFile,'utf8')):[];return client.ask(args.join(' '),{candidates:Array.isArray(supplied)?supplied:supplied.candidates,lane});}
62
+ if(command==='assemble'){if(!client.assemble||!selectionsFile)throw Error('HTTPK connection and selections file required');return client.assemble(JSON.parse(await readFile(selectionsFile,'utf8')));}
63
+ if(command==='call'){if(args.length!==1||!inputsFile)throw new Error('Tool and inputs file required');if(selectionId&&!client.assemble)throw Error('Explicit selection requires HTTPK');return client.call(args[0],JSON.parse(await readFile(inputsFile,'utf8')),{selectionId});}
64
+ if(command==='host'){if(args.length||!client.negotiateHost)throw Error('HTTPK host required');return client.negotiateHost();}
65
+ if(command==='status')return client.status();
66
+ if(command==='cancel')return client.decide('cancel');
67
+ throw new Error('Unknown command');
68
+ },{onActivity:event=>indicator.update(event)});output(result);
69
+ }
70
+ }catch(error){output({error:error.code||'invalid_request',message:error.code?error.message:'The request could not be completed. Run kloudy help.'});process.exitCode=1;}finally{indicator.dispose();}