implto-tos-axi 0.1.1

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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +255 -0
  3. package/dist/bin/implto-tos-axi.d.ts +2 -0
  4. package/dist/bin/implto-tos-axi.js +16 -0
  5. package/dist/bin/implto-tos-axi.js.map +1 -0
  6. package/dist/src/auth/loopback.d.ts +29 -0
  7. package/dist/src/auth/loopback.js +110 -0
  8. package/dist/src/auth/loopback.js.map +1 -0
  9. package/dist/src/auth/open-browser.d.ts +9 -0
  10. package/dist/src/auth/open-browser.js +24 -0
  11. package/dist/src/auth/open-browser.js.map +1 -0
  12. package/dist/src/auth/provider.d.ts +39 -0
  13. package/dist/src/auth/provider.js +106 -0
  14. package/dist/src/auth/provider.js.map +1 -0
  15. package/dist/src/cli.d.ts +2 -0
  16. package/dist/src/cli.js +183 -0
  17. package/dist/src/cli.js.map +1 -0
  18. package/dist/src/commands/components.d.ts +40 -0
  19. package/dist/src/commands/components.js +417 -0
  20. package/dist/src/commands/components.js.map +1 -0
  21. package/dist/src/commands/context.d.ts +10 -0
  22. package/dist/src/commands/context.js +46 -0
  23. package/dist/src/commands/context.js.map +1 -0
  24. package/dist/src/commands/guide.d.ts +16 -0
  25. package/dist/src/commands/guide.js +203 -0
  26. package/dist/src/commands/guide.js.map +1 -0
  27. package/dist/src/commands/home.d.ts +9 -0
  28. package/dist/src/commands/home.js +33 -0
  29. package/dist/src/commands/home.js.map +1 -0
  30. package/dist/src/commands/init.d.ts +12 -0
  31. package/dist/src/commands/init.js +65 -0
  32. package/dist/src/commands/init.js.map +1 -0
  33. package/dist/src/commands/login.d.ts +13 -0
  34. package/dist/src/commands/login.js +137 -0
  35. package/dist/src/commands/login.js.map +1 -0
  36. package/dist/src/commands/tasks.d.ts +8 -0
  37. package/dist/src/commands/tasks.js +344 -0
  38. package/dist/src/commands/tasks.js.map +1 -0
  39. package/dist/src/commands/tools.d.ts +66 -0
  40. package/dist/src/commands/tools.js +401 -0
  41. package/dist/src/commands/tools.js.map +1 -0
  42. package/dist/src/commands/whoami.d.ts +5 -0
  43. package/dist/src/commands/whoami.js +66 -0
  44. package/dist/src/commands/whoami.js.map +1 -0
  45. package/dist/src/config/binding.d.ts +29 -0
  46. package/dist/src/config/binding.js +42 -0
  47. package/dist/src/config/binding.js.map +1 -0
  48. package/dist/src/config/credentials.d.ts +39 -0
  49. package/dist/src/config/credentials.js +62 -0
  50. package/dist/src/config/credentials.js.map +1 -0
  51. package/dist/src/config/profiles.d.ts +24 -0
  52. package/dist/src/config/profiles.js +64 -0
  53. package/dist/src/config/profiles.js.map +1 -0
  54. package/dist/src/errors.d.ts +21 -0
  55. package/dist/src/errors.js +40 -0
  56. package/dist/src/errors.js.map +1 -0
  57. package/dist/src/flags.d.ts +50 -0
  58. package/dist/src/flags.js +143 -0
  59. package/dist/src/flags.js.map +1 -0
  60. package/dist/src/index.d.ts +6 -0
  61. package/dist/src/index.js +7 -0
  62. package/dist/src/index.js.map +1 -0
  63. package/dist/src/mcp/client.d.ts +85 -0
  64. package/dist/src/mcp/client.js +130 -0
  65. package/dist/src/mcp/client.js.map +1 -0
  66. package/dist/src/output.d.ts +8 -0
  67. package/dist/src/output.js +2 -0
  68. package/dist/src/output.js.map +1 -0
  69. package/dist/src/resolve.d.ts +16 -0
  70. package/dist/src/resolve.js +48 -0
  71. package/dist/src/resolve.js.map +1 -0
  72. package/dist/src/scopes.d.ts +9 -0
  73. package/dist/src/scopes.js +16 -0
  74. package/dist/src/scopes.js.map +1 -0
  75. package/dist/src/session.d.ts +40 -0
  76. package/dist/src/session.js +75 -0
  77. package/dist/src/session.js.map +1 -0
  78. package/dist/src/skill.d.ts +32 -0
  79. package/dist/src/skill.js +149 -0
  80. package/dist/src/skill.js.map +1 -0
  81. package/dist/src/version.d.ts +5 -0
  82. package/dist/src/version.js +6 -0
  83. package/dist/src/version.js.map +1 -0
  84. package/package.json +70 -0
  85. package/skills/implto-tos-delivery/SKILL.md +45 -0
  86. package/skills/implto-tos-tasks/SKILL.md +48 -0
  87. package/skills/implto-tos-workspace/SKILL.md +45 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Valentyn Morenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # implto-tos-axi
2
+
3
+ Work with [Transformation OS](https://github.com/implementedtoday/Digital-Transformation-OS) from
4
+ the repository you are changing: find the task a change belongs to, keep the deliverables tree
5
+ honest, and reach every other tool the platform has - without carrying fifty tool schemas in
6
+ every model request. Built to [AXI](https://github.com/kunchenguid/axi) conventions, so an agent
7
+ can drive it as comfortably as a person can.
8
+
9
+ [![npm](https://img.shields.io/npm/v/implto-tos-axi)](https://www.npmjs.com/package/implto-tos-axi)
10
+
11
+ ## Why this exists
12
+
13
+ Transformation OS already speaks MCP, and an agent can mount the server directly. The cost is
14
+ that all 50 tool schemas travel in the prompt of every request the agent makes, whether or not
15
+ the task has anything to do with a ticket.
16
+
17
+ This CLI is the same server reached a different way. The schemas stay on the server; a ~2 KB
18
+ skill stub says when to reach for the tool; and the one tool a task turns out to need is fetched
19
+ on the spot with `tools show`. An agent that never touches a ticket pays for the stub and
20
+ nothing else.
21
+
22
+ The trade is real but small: a command is two round trips instead of one, because the server is
23
+ stateless and every invocation does its own `initialize`.
24
+
25
+ ## Install
26
+
27
+ ```sh
28
+ npm install -g implto-tos-axi
29
+ ```
30
+
31
+ Or run it without installing anything:
32
+
33
+ ```sh
34
+ npx -y implto-tos-axi whoami
35
+ ```
36
+
37
+ To let an agent drive it, add the skills. They are separate on purpose - a session that files a
38
+ ticket should not also load the guidance for process maps:
39
+
40
+ ```sh
41
+ npx skills add implementedtoday/implto-tos-axi --skill implto-tos-tasks -g
42
+ npx skills add implementedtoday/implto-tos-axi --skill implto-tos-delivery -g
43
+ npx skills add implementedtoday/implto-tos-axi --skill implto-tos-workspace -g
44
+ ```
45
+
46
+ | Skill | Loaded when the task is about |
47
+ | ---------------------- | ---------------------------------------------------------------------- |
48
+ | `implto-tos-tasks` | tracking work: matching a change to a task, filing one, closing it out |
49
+ | `implto-tos-delivery` | what is being built: the component tree, estimates, delivery phases |
50
+ | `implto-tos-workspace` | everything else: process maps, people, roles, systems, client threads |
51
+
52
+ ## Sign in
53
+
54
+ ```sh
55
+ implto-tos-axi login # opens a browser
56
+ implto-tos-axi login --profile dev
57
+ implto-tos-axi login --scopes tasks # ask for one area only
58
+ implto-tos-axi login --token tos_… # headless: paste a personal key
59
+ ```
60
+
61
+ The browser flow is OAuth 2.1 against the deployment itself: the CLI registers itself, you
62
+ approve it on a page that says what it is asking for, and the credential it gets acts as you -
63
+ your workspaces, your name on every write. It refreshes itself for an hour at a time without
64
+ anybody present.
65
+
66
+ `--scopes` narrows the grant to some of the server's areas, and the narrowing is real: a
67
+ credential minted for `tasks` is refused the rest, and `tools list` serves it only that area.
68
+
69
+ With no terminal attached the browser path refuses (`NEEDS_HUMAN`) rather than opening a window
70
+ nobody will see. For CI or a container, mint a key at **Settings → Personal credentials** and set `TOS_TOKEN`; it
71
+ outranks anything on disk. Add `TOS_URL` when the deployment is not prod.
72
+
73
+ ## Bind a repository
74
+
75
+ ```sh
76
+ implto-tos-axi init --workspace "Implemented Today" --project DT
77
+ ```
78
+
79
+ Writes `.implto-tos.json`, which every command finds by walking up from the working directory.
80
+ It is what lets the rest of the commands take names instead of ids, and what fills in
81
+ `workspaceId` on a raw tool call. Run `init` bare to list what you can reach.
82
+
83
+ ## What the tool does
84
+
85
+ ### Matches a change to the task it belongs to
86
+
87
+ ```sh
88
+ implto-tos-axi context # run this first
89
+ implto-tos-axi tasks search "login redirect" --open
90
+ implto-tos-axi tasks show TO-112
91
+ implto-tos-axi tasks create "Fix the import guard" --assign me
92
+ implto-tos-axi tasks update TO-112 --status "In Progress"
93
+ implto-tos-axi tasks comment TO-112 "Shipped in #204"
94
+ ```
95
+
96
+ `context` is the one call worth making at the start of a session: it answers which project this
97
+ directory is, what the statuses are called, and which of an issue's two assignee fields you
98
+ belong in. Everything after it takes names.
99
+
100
+ `implto-tos-axi guide workflow` has the whole match-or-create procedure, including the part
101
+ where you ask the person rather than deciding alone.
102
+
103
+ ### Keeps the deliverables tree honest
104
+
105
+ Components are what a workspace is delivering - services, modules, APIs - in a parent-child
106
+ tree, and tasks hang off them. A component that does not exist yet is a task with nowhere to go.
107
+
108
+ ```sh
109
+ implto-tos-axi components list --tree
110
+ implto-tos-axi components show "Billing API"
111
+ implto-tos-axi components create "Billing API" --parent Platform --type API --estimate 40
112
+ implto-tos-axi components update "Billing API" --parent "Payments"
113
+ implto-tos-axi components update "Billing API" --parent none # back to the top level
114
+ ```
115
+
116
+ `--tree` is the view worth having before creating anything: a flat list with a parent column
117
+ answers filtering questions, and the tree answers the question somebody actually has, which is
118
+ where the new thing belongs.
119
+
120
+ Omitting a link flag leaves it alone; `none` clears it. A component created inside a bound
121
+ checkout is linked to that checkout's project unless you say `--project none`.
122
+
123
+ ### Reaches every other tool, without knowing about it
124
+
125
+ ```sh
126
+ implto-tos-axi tools # the areas, and which you hold
127
+ implto-tos-axi tools domains organization # what one covers, and its rules
128
+ implto-tos-axi tools list --domain organization
129
+ implto-tos-axi tools show create_person
130
+ implto-tos-axi tools call create_person name="Ann Lee" email=ann@acme.com
131
+ implto-tos-axi tools call update_process_nodes --json-file nodes.json
132
+ ```
133
+
134
+ Nothing here is hard-coded: the areas, the tool names and their arguments all come from the
135
+ server, so a tool added on a Tuesday is callable on the Tuesday with no release of this package.
136
+
137
+ Arguments are `name=value`, typed against the tool's own schema - `estimateSecs=3600` arrives as
138
+ a number, and a nullable field is cleared with `field=null`. `key=@file` reads a value from a
139
+ file (which is how a markdown body or a mermaid diagram survives a shell), `key=@-` reads stdin,
140
+ and `--json` passes a nested argument whole.
141
+
142
+ A misspelled argument is refused here, naming the real ones. That matters more than it sounds:
143
+ the server ignores an argument it does not know, so the call would otherwise succeed without
144
+ doing what was asked.
145
+
146
+ ### Takes names, not ids
147
+
148
+ Every workspace, project, status, component, parent and system is matched by name,
149
+ case-insensitively, against what the server just returned. Two matches is an error naming both
150
+ rather than a guess, and the error carries the full list of what was available.
151
+
152
+ The one id you never type is `workspaceId`: it comes from the binding, or from
153
+ `--workspace <name>` anywhere else, and a raw `tools call` reports which one it used.
154
+
155
+ ### Says what to run next
156
+
157
+ Every payload ends with a `help` list, and so does every error - same field, same shape - so
158
+ recovering from a failure reads like reading a result:
159
+
160
+ ```
161
+ error: This directory is not bound to a Transformation OS project.
162
+ code: NO_BINDING
163
+ help[2]:
164
+ implto-tos-axi init --workspace <name> --project <key>
165
+ implto-tos-axi init # run it bare to list what you can reach
166
+ ```
167
+
168
+ ## Configuration
169
+
170
+ | Thing | Where |
171
+ | ----------------------- | -------------------------------------------------------------------------- |
172
+ | Credential | `~/.config/implto-tos-axi/credentials.json`, mode 0600 in a 0700 directory |
173
+ | | `$XDG_CONFIG_HOME` is honoured; `%APPDATA%` on Windows |
174
+ | Repository binding | `.implto-tos.json`, found by walking up from the working directory |
175
+ | `TOS_TOKEN` | A personal key. Outranks the file, and is never written back to it |
176
+ | `TOS_AXI_ALLOW_BROWSER` | Open a browser even with no TTY attached. For a wrapper that knows better |
177
+
178
+ Profiles are deployments, one origin each:
179
+
180
+ | Profile | Origin |
181
+ | ------- | --------------------------------------------- |
182
+ | `prod` | https://implto-transformation-os.web.app |
183
+ | `dev` | https://digital-transformation-os-dev.web.app |
184
+ | `local` | http://localhost:4000 |
185
+
186
+ `--url` points at a deployment by address instead, and `--mcp-url` at the MCP endpoint directly -
187
+ which is what a local MCP server running as its own process on port 3100 needs. Both have an
188
+ environment variable for the headless case, and a flag always beats one.
189
+
190
+ An address is named after the deployment it turns out to be, so a credential taken against dev is
191
+ filed under `dev` whichever way you got there.
192
+
193
+ The origin is not a convenience: it is the OAuth issuer and the resource a token is minted for,
194
+ so a client that talks to one address and authorizes against another ends up with a credential
195
+ for somebody else's server.
196
+
197
+ ## Command reference
198
+
199
+ | Command | What it does |
200
+ | ---------------------------------- | ---------------------------------------------------------------------- |
201
+ | `login` | Sign in through the browser, or `--token` to paste a key |
202
+ | `logout` | Forget this machine's credential (revoking it is a button in Settings) |
203
+ | `whoami` | Who this credential acts as, what it reaches, and where it came from |
204
+ | `init` | Bind this directory to a workspace and project |
205
+ | `context` | The project, its statuses and types, and who you are |
206
+ | `tasks search <words>` | Find tasks. `--open`, `--mine`, `--status`, `--all-projects` |
207
+ | `tasks show <key>` | One task in full |
208
+ | `tasks mine` | Your tasks |
209
+ | `tasks create <title>` | File one. `--assign me` by default |
210
+ | `tasks update <key>` | Status, assignee, title, description, priority, due date |
211
+ | `tasks comment <key> <text>` | Leave a note on it |
212
+ | `components list` | The deliverables. `--tree`, `--parent`, `--project`, `--search` |
213
+ | `components show <name>` | One in full, with its phase estimates |
214
+ | `components create <name>` | Add one. `--parent`, `--type`, `--estimate`, `--system`, `--project` |
215
+ | `components update <name>` | Rename, re-parent, re-link, re-estimate |
216
+ | `tools` | The areas the server offers, and which this credential holds |
217
+ | `tools domains <id>` | What one area covers, and the rules for using it |
218
+ | `tools list` | The tools in an area. `--domain`, `--search` |
219
+ | `tools show <name>` | One tool's arguments |
220
+ | `tools call <name> [name=value …]` | Call it |
221
+ | `guide [topic]` | Topic-sized guidance. Run bare to list topics |
222
+
223
+ `--help` on any of them is the current truth; this table is not.
224
+
225
+ Guide topics: `workflow`, `components`, `tools`, `auth`, `binding`, `statuses`, `output`.
226
+
227
+ ## Output and exit codes
228
+
229
+ Every command prints [TOON](https://github.com/toon-format/toon): compact, tabular where the
230
+ data is tabular, and readable without a parser. The two whose content is prose rather than data -
231
+ `guide <topic>` and `components list --tree` - print plain text, because a procedure rendered as
232
+ one escaped string is the format fighting the content.
233
+
234
+ `0` succeeded. `2` is the caller's mistake - a bad flag, a name that matches nothing or matches
235
+ several. Everything else is `1`. Every error carries a code (`AUTH_MISSING`, `AUTH_INVALID`,
236
+ `NO_BINDING`, `AMBIGUOUS`, `NOT_FOUND`, `FORBIDDEN`, `NEEDS_HUMAN`, `NETWORK_ERROR`,
237
+ `VALIDATION_ERROR`) and the command that fixes it.
238
+
239
+ ## Development
240
+
241
+ ```sh
242
+ pnpm install
243
+ pnpm build # tsc, and chmod the bin
244
+ pnpm test
245
+ pnpm typecheck
246
+ pnpm build:skill # regenerate the three SKILL.md stubs from src/skill.ts
247
+ ```
248
+
249
+ The stubs are generated, and CI fails if the files on disk have drifted from the generator or if
250
+ any of them grows past 2500 characters. That cap is the point of the package: a stub that grows
251
+ into a manual gives back exactly what reaching the server this way bought.
252
+
253
+ ## License
254
+
255
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { tryFastPath } from "axi-sdk-js/fast-path";
3
+ import { VERSION } from "../src/version.js";
4
+ // `implto-tos-axi --help | head` closes the pipe early, and an unhandled EPIPE
5
+ // turns that into a stack trace. Piping output somewhere that stops reading is
6
+ // ordinary shell use, not an error worth reporting.
7
+ process.stdout.on("error", (error) => {
8
+ if (error.code === "EPIPE")
9
+ process.exit(0);
10
+ throw error;
11
+ });
12
+ if (!tryFastPath(process.argv.slice(2), { version: VERSION })) {
13
+ const { main } = await import("../src/cli.js");
14
+ await main();
15
+ }
16
+ //# sourceMappingURL=implto-tos-axi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"implto-tos-axi.js","sourceRoot":"","sources":["../../bin/implto-tos-axi.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,+EAA+E;AAC/E,+EAA+E;AAC/E,oDAAoD;AACpD,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAA4B,EAAE,EAAE;IAC1D,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,KAAK,CAAC;AACd,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC9D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;IAC/C,MAAM,IAAI,EAAE,CAAC;AACf,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The one-shot listener that catches the authorization code.
3
+ *
4
+ * A desktop client has nowhere else to receive one, which is why RFC 8252 makes
5
+ * loopback the redirect for native applications and why the server matches a
6
+ * loopback address with the port ignored: the operating system picks the port
7
+ * at start-up, so it cannot be registered in advance.
8
+ *
9
+ * Bound to 127.0.0.1 only. `state` is compared in constant time, `iss` is
10
+ * compared against the deployment that was asked, and the server closes after
11
+ * the first answer - so another process on this machine that guessed the port
12
+ * still has to have the `state`, which only the browser was given.
13
+ */
14
+ export interface LoopbackResult {
15
+ code: string;
16
+ state: string;
17
+ }
18
+ export interface Loopback {
19
+ /** The address to register and to send the browser back to. */
20
+ redirectUri: string;
21
+ state: string;
22
+ /** Resolves with the code, or rejects with LOGIN_TIMEOUT. */
23
+ wait: Promise<LoopbackResult>;
24
+ close(): void;
25
+ }
26
+ export declare function startLoopback(options: {
27
+ issuer: string;
28
+ timeoutMs: number;
29
+ }): Promise<Loopback>;
@@ -0,0 +1,110 @@
1
+ import { createServer } from "node:http";
2
+ import { randomBytes, timingSafeEqual } from "node:crypto";
3
+ import { TosError } from "../errors.js";
4
+ const PAGE = (title, detail) => `<!doctype html><meta charset="utf-8"><title>${title}</title>` +
5
+ `<body style="font:16px/1.5 system-ui;margin:4rem auto;max-width:28rem;text-align:center">` +
6
+ `<h1 style="font-size:1.25rem">${title}</h1><p style="color:#555">${detail}</p></body>`;
7
+ export async function startLoopback(options) {
8
+ const state = randomBytes(32).toString("base64url");
9
+ let settle;
10
+ let fail;
11
+ const wait = new Promise((resolve, reject) => {
12
+ settle = resolve;
13
+ fail = reject;
14
+ });
15
+ let done = false;
16
+ const server = createServer((req, res) => {
17
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
18
+ if (url.pathname !== "/callback") {
19
+ res.writeHead(404).end();
20
+ return;
21
+ }
22
+ if (done) {
23
+ // A second arrival is not the client: the browser was answered already.
24
+ res.writeHead(409, { "Content-Type": "text/html; charset=utf-8" });
25
+ res.end(PAGE("Already answered", "This sign-in has already completed."));
26
+ return;
27
+ }
28
+ const error = url.searchParams.get("error");
29
+ if (error) {
30
+ done = true;
31
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
32
+ res.end(PAGE("Not connected", url.searchParams.get("error_description") ?? error));
33
+ server.close();
34
+ fail(new TosError(url.searchParams.get("error_description") ?? `Authorization was refused (${error}).`, "LOGIN_REFUSED"));
35
+ return;
36
+ }
37
+ const returned = url.searchParams.get("state") ?? "";
38
+ const code = url.searchParams.get("code") ?? "";
39
+ const iss = url.searchParams.get("iss");
40
+ if (!equal(returned, state) || !code) {
41
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
42
+ res.end(PAGE("Not connected", "This response did not match the request."));
43
+ return;
44
+ }
45
+ // `iss` is how a client holding authorizations from several servers knows
46
+ // which one answered. Ours promises it, so a mismatch is worth refusing.
47
+ if (iss && new URL(iss).origin !== new URL(options.issuer).origin) {
48
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
49
+ res.end(PAGE("Not connected", "This response came from another server."));
50
+ return;
51
+ }
52
+ done = true;
53
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
54
+ res.end(PAGE("Connected", "You can close this tab and go back to the terminal."));
55
+ server.close();
56
+ settle({ code, state: returned });
57
+ });
58
+ await new Promise((resolve, reject) => {
59
+ server.once("error", reject);
60
+ server.listen(0, "127.0.0.1", resolve);
61
+ });
62
+ const address = server.address();
63
+ if (!address || typeof address === "string") {
64
+ server.close();
65
+ throw new TosError("Could not open a local port to sign in on.", "LOGIN_FAILED");
66
+ }
67
+ const timer = setTimeout(() => {
68
+ if (done)
69
+ return;
70
+ done = true;
71
+ server.close();
72
+ fail(new TosError("Timed out waiting for the browser.", "LOGIN_TIMEOUT", [
73
+ "implto-tos-axi login --token <paste a key from Settings, Personal credentials>",
74
+ ]));
75
+ }, options.timeoutMs);
76
+ timer.unref?.();
77
+ // The promise a caller actually awaits, built once so the handler below
78
+ // attaches to the same object. A caller normally awaits it a moment after
79
+ // starting the flow, so a refusal arriving in between would surface as an
80
+ // unhandled rejection and, in a strict runtime, take the process down.
81
+ // Attaching a handler marks it handled without consuming it: it still
82
+ // rejects for whoever awaits it.
83
+ const settled = wait.finally(() => clearTimeout(timer));
84
+ settled.catch(() => undefined);
85
+ return {
86
+ redirectUri: `http://127.0.0.1:${address.port}/callback`,
87
+ state,
88
+ wait: settled,
89
+ // Settles the promise as well as closing the port. A `close` that only
90
+ // stopped listening left `wait` pending forever, so an abandoned sign-in
91
+ // hung whatever was awaiting it instead of failing.
92
+ close: () => {
93
+ clearTimeout(timer);
94
+ server.close();
95
+ if (done)
96
+ return;
97
+ done = true;
98
+ fail(new TosError("The sign-in was abandoned.", "LOGIN_ABANDONED"));
99
+ },
100
+ };
101
+ }
102
+ /** Constant time, and never throwing on a length the caller chose. */
103
+ function equal(a, b) {
104
+ const left = Buffer.from(a);
105
+ const right = Buffer.from(b);
106
+ if (left.length !== right.length)
107
+ return false;
108
+ return timingSafeEqual(left, right);
109
+ }
110
+ //# sourceMappingURL=loopback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loopback.js","sourceRoot":"","sources":["../../../src/auth/loopback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Hands a URL to whatever opens links on this machine.
3
+ *
4
+ * No dependency for three lines of `spawn`, and deliberately fire-and-forget:
5
+ * the URL is printed too, so a failure to open anything is survivable - the
6
+ * person pastes it. Detached and with stdio ignored so a browser that stays
7
+ * open does not hold the CLI's exit.
8
+ */
9
+ export declare function openBrowser(url: string): void;
@@ -0,0 +1,24 @@
1
+ import { spawn } from "node:child_process";
2
+ import { platform } from "node:os";
3
+ /**
4
+ * Hands a URL to whatever opens links on this machine.
5
+ *
6
+ * No dependency for three lines of `spawn`, and deliberately fire-and-forget:
7
+ * the URL is printed too, so a failure to open anything is survivable - the
8
+ * person pastes it. Detached and with stdio ignored so a browser that stays
9
+ * open does not hold the CLI's exit.
10
+ */
11
+ export function openBrowser(url) {
12
+ const [command, args] = platform() === "darwin"
13
+ ? ["open", [url]]
14
+ : platform() === "win32"
15
+ ? ["cmd", ["/c", "start", "", url]]
16
+ : ["xdg-open", [url]];
17
+ try {
18
+ spawn(command, args, { detached: true, stdio: "ignore" }).unref();
19
+ }
20
+ catch {
21
+ // The URL is on screen either way.
22
+ }
23
+ }
24
+ //# sourceMappingURL=open-browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-browser.js","sourceRoot":"","sources":["../../../src/auth/open-browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GACnB,QAAQ,EAAE,KAAK,QAAQ;QACrB,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC,CAAC,QAAQ,EAAE,KAAK,OAAO;YACtB,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,IAAI,CAAC;QACH,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;AACH,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { OAuthClientInformationMixed, OAuthClientMetadata, OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
2
+ import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
3
+ import type { Profile } from "../config/profiles.js";
4
+ /**
5
+ * The SDK's OAuth client, backed by the credentials file.
6
+ *
7
+ * Everything hard is the SDK's: discovery, registration, PKCE, the exchange and
8
+ * the renewal on a 401. What is here is only where the pieces are kept and how
9
+ * the browser is opened - the parts that are this machine's business.
10
+ *
11
+ * `redirectUrl` is the loopback address the listener is already on, so the
12
+ * registration and the authorization request name the same port this process is
13
+ * holding. The server matches loopback with the port ignored, so a registration
14
+ * made this morning still works this afternoon on a different port.
15
+ */
16
+ export declare class FileAuthProvider implements OAuthClientProvider {
17
+ private readonly profile;
18
+ private readonly redirect;
19
+ private readonly oauthState;
20
+ private readonly scope;
21
+ /** Called when the browser is sent somewhere, so `login` can print it. */
22
+ private readonly onRedirect;
23
+ private verifier;
24
+ constructor(profile: Profile, redirect: string, oauthState: string, scope: string,
25
+ /** Called when the browser is sent somewhere, so `login` can print it. */
26
+ onRedirect: (url: URL) => void);
27
+ get redirectUrl(): string;
28
+ get clientMetadata(): OAuthClientMetadata;
29
+ state(): string;
30
+ clientInformation(): OAuthClientInformationMixed | undefined;
31
+ saveClientInformation(info: OAuthClientInformationMixed): void;
32
+ tokens(): OAuthTokens | undefined;
33
+ saveTokens(tokens: OAuthTokens): void;
34
+ redirectToAuthorization(url: URL): void;
35
+ saveCodeVerifier(verifier: string): void;
36
+ codeVerifier(): string;
37
+ private read;
38
+ private merge;
39
+ }
@@ -0,0 +1,106 @@
1
+ import { hostname } from "node:os";
2
+ import { readCredential, writeCredential } from "../config/credentials.js";
3
+ import { openBrowser } from "./open-browser.js";
4
+ /**
5
+ * The SDK's OAuth client, backed by the credentials file.
6
+ *
7
+ * Everything hard is the SDK's: discovery, registration, PKCE, the exchange and
8
+ * the renewal on a 401. What is here is only where the pieces are kept and how
9
+ * the browser is opened - the parts that are this machine's business.
10
+ *
11
+ * `redirectUrl` is the loopback address the listener is already on, so the
12
+ * registration and the authorization request name the same port this process is
13
+ * holding. The server matches loopback with the port ignored, so a registration
14
+ * made this morning still works this afternoon on a different port.
15
+ */
16
+ export class FileAuthProvider {
17
+ profile;
18
+ redirect;
19
+ oauthState;
20
+ scope;
21
+ onRedirect;
22
+ verifier;
23
+ constructor(profile, redirect, oauthState, scope,
24
+ /** Called when the browser is sent somewhere, so `login` can print it. */
25
+ onRedirect) {
26
+ this.profile = profile;
27
+ this.redirect = redirect;
28
+ this.oauthState = oauthState;
29
+ this.scope = scope;
30
+ this.onRedirect = onRedirect;
31
+ }
32
+ get redirectUrl() {
33
+ return this.redirect;
34
+ }
35
+ get clientMetadata() {
36
+ return {
37
+ client_name: `implto-tos-axi on ${hostname()}`,
38
+ redirect_uris: [this.redirect],
39
+ grant_types: ["authorization_code", "refresh_token"],
40
+ response_types: ["code"],
41
+ // A CLI ships to laptops and cannot keep a secret. PKCE is what binds a
42
+ // code to this process; a client secret would be theatre.
43
+ token_endpoint_auth_method: "none",
44
+ scope: this.scope,
45
+ };
46
+ }
47
+ state() {
48
+ return this.oauthState;
49
+ }
50
+ clientInformation() {
51
+ const stored = this.read().client;
52
+ return stored ? stored : undefined;
53
+ }
54
+ saveClientInformation(info) {
55
+ this.merge({ client: info });
56
+ }
57
+ tokens() {
58
+ const stored = this.read().tokens;
59
+ if (!stored?.access_token)
60
+ return undefined;
61
+ return {
62
+ access_token: stored.access_token,
63
+ token_type: "Bearer",
64
+ ...(stored.refresh_token ? { refresh_token: stored.refresh_token } : {}),
65
+ ...(stored.expires_at
66
+ ? { expires_in: Math.max(0, Math.round((stored.expires_at - Date.now()) / 1000)) }
67
+ : {}),
68
+ };
69
+ }
70
+ saveTokens(tokens) {
71
+ this.merge({
72
+ tokens: {
73
+ access_token: tokens.access_token,
74
+ ...(tokens.refresh_token ? { refresh_token: tokens.refresh_token } : {}),
75
+ ...(tokens.expires_in ? { expires_at: Date.now() + tokens.expires_in * 1000 } : {}),
76
+ },
77
+ });
78
+ }
79
+ redirectToAuthorization(url) {
80
+ this.onRedirect(url);
81
+ openBrowser(url.toString());
82
+ }
83
+ saveCodeVerifier(verifier) {
84
+ // In memory only, and only for this process: a verifier outlives nothing.
85
+ // Writing it down would put the other half of the proof on disk beside the
86
+ // token it protects.
87
+ this.verifier = verifier;
88
+ }
89
+ codeVerifier() {
90
+ if (!this.verifier)
91
+ throw new Error("No PKCE verifier for this sign-in.");
92
+ return this.verifier;
93
+ }
94
+ read() {
95
+ return readCredential(this.profile.name) ?? { origin: this.profile.origin };
96
+ }
97
+ merge(patch) {
98
+ writeCredential(this.profile.name, {
99
+ ...this.read(),
100
+ origin: this.profile.origin,
101
+ mcpUrl: this.profile.mcpUrl,
102
+ ...patch,
103
+ });
104
+ }
105
+ }
106
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/auth/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAOnC,OAAO,EAAE,cAAc,EAAE,eAAe,EAA0B,MAAM,0BAA0B,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,gBAAgB;IAIR;IACA;IACA;IACA;IAEA;IARX,QAAQ,CAAqB;IAErC,YACmB,OAAgB,EAChB,QAAgB,EAChB,UAAkB,EAClB,KAAa;IAC9B,0EAA0E;IACzD,UAA8B;QAL9B,YAAO,GAAP,OAAO,CAAS;QAChB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,eAAU,GAAV,UAAU,CAAQ;QAClB,UAAK,GAAL,KAAK,CAAQ;QAEb,eAAU,GAAV,UAAU,CAAoB;IAC9C,CAAC;IAEJ,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,cAAc;QAChB,OAAO;YACL,WAAW,EAAE,qBAAqB,QAAQ,EAAE,EAAE;YAC9C,aAAa,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC9B,WAAW,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;YACpD,cAAc,EAAE,CAAC,MAAM,CAAC;YACxB,wEAAwE;YACxE,0DAA0D;YAC1D,0BAA0B,EAAE,MAAM;YAClC,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,iBAAiB;QACf,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC;QAClC,OAAO,MAAM,CAAC,CAAC,CAAE,MAAsC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,CAAC;IAED,qBAAqB,CAAC,IAAiC;QACrD,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAgD,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM;QACJ,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC;QAClC,IAAI,CAAC,MAAM,EAAE,YAAY;YAAE,OAAO,SAAS,CAAC;QAC5C,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,UAAU,EAAE,QAAQ;YACpB,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxE,GAAG,CAAC,MAAM,CAAC,UAAU;gBACnB,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE;gBAClF,CAAC,CAAC,EAAE,CAAC;SACO,CAAC;IACnB,CAAC;IAED,UAAU,CAAC,MAAmB;QAC5B,IAAI,CAAC,KAAK,CAAC;YACT,MAAM,EAAE;gBACN,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxE,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpF;SACF,CAAC,CAAC;IACL,CAAC;IAED,uBAAuB,CAAC,GAAQ;QAC9B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACrB,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,gBAAgB,CAAC,QAAgB;QAC/B,0EAA0E;QAC1E,2EAA2E;QAC3E,qBAAqB;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,YAAY;QACV,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAEO,IAAI;QACV,OAAO,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;IAC9E,CAAC;IAEO,KAAK,CAAC,KAAiC;QAC7C,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YACjC,GAAG,IAAI,CAAC,IAAI,EAAE;YACd,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;YAC3B,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;YAC3B,GAAG,KAAK;SACT,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export declare const DESCRIPTION = "Work with Transformation OS from a repository - tasks, deliverables, and the rest of the workspace";
2
+ export declare function main(argv?: string[]): Promise<void>;