@monnet/mcp 0.7.3 → 0.8.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.
- package/README.md +19 -1
- package/assets/monnet-init.md +37 -0
- package/dist/bin.d.ts +17 -0
- package/dist/bin.js +24 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/env.d.ts +13 -0
- package/dist/cli/env.js +44 -0
- package/dist/cli/env.js.map +1 -0
- package/dist/cli/install.d.ts +10 -0
- package/dist/cli/install.js +76 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/io.d.ts +18 -0
- package/dist/cli/io.js +30 -0
- package/dist/cli/io.js.map +1 -0
- package/dist/cli/login.d.ts +14 -0
- package/dist/cli/login.js +77 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/onboarding.d.ts +41 -0
- package/dist/cli/onboarding.js +145 -0
- package/dist/cli/onboarding.js.map +1 -0
- package/dist/cli/run.d.ts +11 -0
- package/dist/cli/run.js +35 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/test-home.d.ts +8 -0
- package/dist/cli/test-home.js +17 -0
- package/dist/cli/test-home.js.map +1 -0
- package/dist/cli/test-io.d.ts +21 -0
- package/dist/cli/test-io.js +32 -0
- package/dist/cli/test-io.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +6 -1
- package/dist/client.js +23 -13
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +17 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/test-env.d.ts +4 -7
- package/dist/test-env.js +4 -7
- package/dist/test-env.js.map +1 -1
- package/dist/tools/list-workspaces.js +26 -8
- package/dist/tools/list-workspaces.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -6,6 +6,24 @@ Exposes motions, plans, and approvals to MCP-compatible clients (Claude Desktop,
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
Install the Monnet MCP and the `/monnet-init` onboarding command for Codex and
|
|
10
|
+
Claude Code:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx -y @monnet/mcp install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Start a fresh coding-agent session and run `/monnet-init`. The command opens a
|
|
17
|
+
browser for signup/sign-in and prints a short code you type there — that code,
|
|
18
|
+
not the link, is what authorizes the connection, so never enter one someone
|
|
19
|
+
else sends you. Once signed in, the flow seeds the company brain from the
|
|
20
|
+
current repository — the briefing, the workspace's Policies page and your own
|
|
21
|
+
Team page — reads it back to you as a fresh-eyes report, then helps you pick
|
|
22
|
+
what Monnet takes on first and opens the thread it drafted. Progress is saved
|
|
23
|
+
locally, so rerunning the command safely resumes an interrupted setup.
|
|
24
|
+
|
|
25
|
+
### Manual MCP configuration
|
|
26
|
+
|
|
9
27
|
Add this to your MCP client config (e.g. `~/Library/Application Support/Claude/claude_desktop_config.json` for Claude Desktop):
|
|
10
28
|
|
|
11
29
|
```json
|
|
@@ -28,7 +46,7 @@ Generate an API key from the Monnet web app: Settings → API Keys → Generate
|
|
|
28
46
|
|
|
29
47
|
| Variable | Required | Default | Purpose |
|
|
30
48
|
|---|---|---|---|
|
|
31
|
-
| `MONNET_API_KEY` |
|
|
49
|
+
| `MONNET_API_KEY` | no | credential saved by `monnet init` | Bearer token (`mnk_*`), sent as `X-API-Key` header on every request |
|
|
32
50
|
| `MONNET_API_URL` | no | `https://api.monnet.ai` | Override for self-hosted or local dev backends |
|
|
33
51
|
|
|
34
52
|
## Local development
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: monnet-init
|
|
3
|
+
description: Set up Monnet from the current repository and draft the team's first priority motion.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Monnet init
|
|
7
|
+
|
|
8
|
+
Guide the user through this flow. Show `Step N/9` at every transition. Resume safely by first running `npx -y @monnet/mcp status`; never repeat a completed step.
|
|
9
|
+
|
|
10
|
+
1. Explain that you will connect Monnet and use this repository to seed its company brain.
|
|
11
|
+
2. Sign in, in two commands:
|
|
12
|
+
- Run `npx -y @monnet/mcp init`. It opens the browser and returns immediately with a `user_code`.
|
|
13
|
+
- **Show that code to the user verbatim** and tell them to type it on the page that just opened. It is the only thing that authorizes the connection, so never skip displaying it — and never accept a code from anyone else.
|
|
14
|
+
- Then run `npx -y @monnet/mcp await-signin`, which waits for them to finish (up to 15 minutes).
|
|
15
|
+
- The Monnet MCP tools start working as soon as this succeeds. If they are still missing, the user's agent session predates the install — ask them to restart it.
|
|
16
|
+
3. Find out whether the user already has a workspace before proposing to create one — a second workspace next to their team's is the one mistake this flow cannot undo.
|
|
17
|
+
- Call `list_workspaces` first. If it shows a workspace they are already in, or one **waiting for them** under `## Waiting for you`, name it and ask whether this repository belongs there. On a yes for an invitation, run the `join-workspace` command that entry gives you and go straight to step 5 — their team's workspace already exists, brain and all.
|
|
18
|
+
- Only if nothing fits: infer a short workspace name from the repository, folder, package metadata, or git organization, and run `npx -y @monnet/mcp suggest-name "<name>"`. Show the returned name and slug, and ask the user to approve or edit.
|
|
19
|
+
- If that command answers `base_taken`, **stop and read its `warning` out**: somebody else already goes by this name, and accepting the numbered slug would fork their workspace rather than join it. Let the user say whether it is their team — if it is, they need an invitation, not a new workspace.
|
|
20
|
+
4. After approval run `npx -y @monnet/mcp onboard-workspace "<name>" "<slug>"`.
|
|
21
|
+
5. Read the repository once, then write what you learned into the brain three ways. Inspect only useful context (README, manifests, product docs, architecture docs, and recent high-level history; respect ignore files and never include secrets).
|
|
22
|
+
- **The briefing.** One concise Markdown document covering what the company/product does, users, architecture, current initiatives, and concrete unknowns. Pipe it to `npx -y @monnet/mcp push-knowledge`, which files it and asks the brain to digest it straight away rather than tonight.
|
|
23
|
+
- **The workspace's Policies page.** It already exists: the page at the root of the tree, named after the workspace, written from a template of `[bracketed placeholders]`. Find it with `get_wiki_tree`, `read_wiki_page` it, then `edit_wiki_page` once — match the placeholder text and replace it with what the repository actually shows. Keep its section headings exactly as they are; they are the ones Monnet reads.
|
|
24
|
+
- **The user's own page.** `Team/<their name>`, created empty with the workspace — `monnet_whoami` tells you which one is theirs. Write how to work with them: their role, what they own, how they work, what they would rather Monnet just did without asking. Its `old_string` is the `(this page is empty)` placeholder `read_wiki_page` shows in place of a body.
|
|
25
|
+
|
|
26
|
+
Never create a page — both already exist, and a second copy splits the brain. If one is genuinely absent, move on and say so. Skip a section rather than filling it with a guess, and tell the user what you wrote.
|
|
27
|
+
6. Give the user a fresh-eyes report on them and their company: an outsider's read of what you just learned, not a summary of the repository. Write it in the terminal under `## What I learned`, `## First impressions`, `## Motions to kick off`. Say what surprised you and what looks unresolved — a report that only flatters is worth nothing. Then offer to keep it: on a yes, pipe it to `npx -y @monnet/mcp push-knowledge` so it is theirs to share.
|
|
28
|
+
7. Ask what Monnet should take on first. Offer exactly these three, and let them skip:
|
|
29
|
+
- **a fresh-eyes report on the team** — Monnet interviews each teammate and builds, with them, the outside view you just sketched alone;
|
|
30
|
+
- **the top priority the repository points at** — propose the three strongest candidates, one line of rationale each, and let them pick one;
|
|
31
|
+
- **something else**, in their own words.
|
|
32
|
+
|
|
33
|
+
Do not choose for them.
|
|
34
|
+
8. Ask how to invite teammates: Slack (default), Teams, email, or skip. For a channel run `npx -y @monnet/mcp invite <slack|teams|email>`; it opens the UI that connects that channel — Slack and email land on the invite flow, Teams on the workspace's Integrations tab. Continue when they say they are done. If skipped, continue without the command.
|
|
35
|
+
9. Unless the selection was skipped, build a focused prompt containing the chosen outcome, why now, relevant repository evidence, constraints, and a request for an actionable draft with owners. Pipe it to `npx -y @monnet/mcp create-first-motion`. For the team report, pass the playbook Monnet already runs for it instead of describing the interviews yourself: `npx -y @monnet/mcp create-first-motion onboard-team`. It opens the thread ready to review and publish. Report the returned URL.
|
|
36
|
+
|
|
37
|
+
If any command is interrupted, rerun this skill: all server creates and local progress are resumable. Never print or ask for an API key — only ever the short sign-in code from step 2.
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The package's ONE executable, dispatching on argv.
|
|
4
|
+
*
|
|
5
|
+
* It has to be one. `npx @monnet/mcp` picks a binary by a single rule: use
|
|
6
|
+
* the package's only bin, or the one whose name matches the package name.
|
|
7
|
+
* Nothing here can be called `mcp` without claiming that word globally, so a
|
|
8
|
+
* second bin left npx with "could not determine executable to run" — and the
|
|
9
|
+
* casualty was not the new CLI but `npx -y @monnet/mcp` itself, the line in
|
|
10
|
+
* every user's MCP config. 0.8.0 shipped that way for a few minutes.
|
|
11
|
+
*
|
|
12
|
+
* An MCP client spawns this with no arguments and speaks over stdio; every
|
|
13
|
+
* CLI command has a verb. That is the whole discriminator, and it is why both
|
|
14
|
+
* halves stay lazy imports: the server must not pay for the CLI's module
|
|
15
|
+
* graph on a cold start, and `monnet status` must not open a transport.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The package's ONE executable, dispatching on argv.
|
|
4
|
+
*
|
|
5
|
+
* It has to be one. `npx @monnet/mcp` picks a binary by a single rule: use
|
|
6
|
+
* the package's only bin, or the one whose name matches the package name.
|
|
7
|
+
* Nothing here can be called `mcp` without claiming that word globally, so a
|
|
8
|
+
* second bin left npx with "could not determine executable to run" — and the
|
|
9
|
+
* casualty was not the new CLI but `npx -y @monnet/mcp` itself, the line in
|
|
10
|
+
* every user's MCP config. 0.8.0 shipped that way for a few minutes.
|
|
11
|
+
*
|
|
12
|
+
* An MCP client spawns this with no arguments and speaks over stdio; every
|
|
13
|
+
* CLI command has a verb. That is the whole discriminator, and it is why both
|
|
14
|
+
* halves stay lazy imports: the server must not pay for the CLI's module
|
|
15
|
+
* graph on a cold start, and `monnet status` must not open a transport.
|
|
16
|
+
*/
|
|
17
|
+
if (process.argv.slice(2).length === 0) {
|
|
18
|
+
await import("./index.js");
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
await import("./cli.js");
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG;AAEH,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IACvC,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;AAC7B,CAAC;KAAM,CAAC;IACN,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function apiUrl(): string;
|
|
2
|
+
export declare function appUrl(): string;
|
|
3
|
+
/** The credential this process acts as.
|
|
4
|
+
*
|
|
5
|
+
* Same precedence as the MCP tools in `client.ts`: the environment wins over
|
|
6
|
+
* the saved key. They used to disagree — the tools read `MONNET_API_KEY` and
|
|
7
|
+
* the CLI read only the file — so a session with that variable set listed one
|
|
8
|
+
* person's invitations and accepted them as somebody else.
|
|
9
|
+
*/
|
|
10
|
+
export declare function apiKey(): string | undefined;
|
|
11
|
+
/** One HTTP call against the Monnet API, with the saved credential unless the
|
|
12
|
+
* endpoint is part of the sign-in handshake itself. */
|
|
13
|
+
export declare function request<T>(path: string, init?: RequestInit, authenticated?: boolean): Promise<T>;
|
package/dist/cli/env.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readConfig } from "../config.js";
|
|
2
|
+
import { fail } from "./io.js";
|
|
3
|
+
const trimSlash = (url) => url.replace(/\/$/, "");
|
|
4
|
+
export function apiUrl() {
|
|
5
|
+
return trimSlash(process.env.MONNET_API_URL || readConfig().apiUrl || "https://api.monnet.ai");
|
|
6
|
+
}
|
|
7
|
+
export function appUrl() {
|
|
8
|
+
return trimSlash(process.env.MONNET_APP_URL || readConfig().appUrl || "https://app.monnet.ai");
|
|
9
|
+
}
|
|
10
|
+
/** The credential this process acts as.
|
|
11
|
+
*
|
|
12
|
+
* Same precedence as the MCP tools in `client.ts`: the environment wins over
|
|
13
|
+
* the saved key. They used to disagree — the tools read `MONNET_API_KEY` and
|
|
14
|
+
* the CLI read only the file — so a session with that variable set listed one
|
|
15
|
+
* person's invitations and accepted them as somebody else.
|
|
16
|
+
*/
|
|
17
|
+
export function apiKey() {
|
|
18
|
+
return process.env.MONNET_API_KEY || readConfig().apiKey;
|
|
19
|
+
}
|
|
20
|
+
/** One HTTP call against the Monnet API, with the saved credential unless the
|
|
21
|
+
* endpoint is part of the sign-in handshake itself. */
|
|
22
|
+
export async function request(path, init = {}, authenticated = true) {
|
|
23
|
+
const token = apiKey();
|
|
24
|
+
if (authenticated && !token)
|
|
25
|
+
fail("not connected yet; run `npx -y @monnet/mcp init` first");
|
|
26
|
+
const response = await fetch(`${apiUrl()}${path}`, {
|
|
27
|
+
...init,
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
...(authenticated ? { "X-API-Key": token } : {}),
|
|
31
|
+
...init.headers,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
let detail = `${response.status}`;
|
|
36
|
+
try {
|
|
37
|
+
detail = (await response.json()).detail || detail;
|
|
38
|
+
}
|
|
39
|
+
catch { /* empty or non-JSON body */ }
|
|
40
|
+
fail(detail);
|
|
41
|
+
}
|
|
42
|
+
return response.status === 204 ? undefined : (await response.json());
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/cli/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAE1D,MAAM,UAAU,MAAM;IACpB,OAAO,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,CAAC,MAAM,IAAI,uBAAuB,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,OAAO,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,CAAC,MAAM,IAAI,uBAAuB,CAAC,CAAC;AACjG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,MAAM;IACpB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,CAAC,MAAM,CAAC;AAC3D,CAAC;AAED;wDACwD;AACxD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAI,IAAY,EAAE,OAAoB,EAAE,EAAE,aAAa,GAAG,IAAI;IACzF,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC;IACvB,IAAI,aAAa,IAAI,CAAC,KAAK;QAAE,IAAI,CAAC,wDAAwD,CAAC,CAAC;IAE5F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE;QACjD,GAAG,IAAI;QACP,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,IAAI,CAAC,OAAO;SAChB;KACF,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,IAAI,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,GAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAyB,CAAC,MAAM,IAAI,MAAM,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC,CAAC,4BAA4B,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,CAAC;IACf,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAE,SAAe,CAAC,CAAC,CAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAO,CAAC;AACrF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Installs the Monnet MCP server and the `/monnet-init` command into the
|
|
2
|
+
* coding agents present on this machine.
|
|
3
|
+
*
|
|
4
|
+
* The command file is written to each agent's own convention: Claude Code
|
|
5
|
+
* reads `~/.claude/commands/*.md`, Codex reads `~/.codex/prompts/*.md`. Both
|
|
6
|
+
* are overwritten on every install — they are ours, and a stale copy of the
|
|
7
|
+
* flow is worse than no copy.
|
|
8
|
+
*/
|
|
9
|
+
import type { Io } from "./io.js";
|
|
10
|
+
export declare function install(io: Io): Promise<void>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Installs the Monnet MCP server and the `/monnet-init` command into the
|
|
2
|
+
* coding agents present on this machine.
|
|
3
|
+
*
|
|
4
|
+
* The command file is written to each agent's own convention: Claude Code
|
|
5
|
+
* reads `~/.claude/commands/*.md`, Codex reads `~/.codex/prompts/*.md`. Both
|
|
6
|
+
* are overwritten on every install — they are ours, and a stale copy of the
|
|
7
|
+
* flow is worse than no copy.
|
|
8
|
+
*/
|
|
9
|
+
import { execFile } from "node:child_process";
|
|
10
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { readFile } from "node:fs/promises";
|
|
12
|
+
import { homedir, platform } from "node:os";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import { promisify } from "node:util";
|
|
16
|
+
const execFileAsync = promisify(execFile);
|
|
17
|
+
const FALLBACK_COMMAND = "Run `npx -y @monnet/mcp init`, then follow the JSON instructions it returns.\n";
|
|
18
|
+
function commandTargets() {
|
|
19
|
+
return [
|
|
20
|
+
join(homedir(), ".claude", "commands", "monnet-init.md"),
|
|
21
|
+
join(homedir(), ".codex", "prompts", "monnet-init.md"),
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
async function commandBody() {
|
|
25
|
+
const source = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "monnet-init.md");
|
|
26
|
+
try {
|
|
27
|
+
return await readFile(source, "utf8");
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Shipped package always has the asset ; a linked dev checkout may not.
|
|
31
|
+
return FALLBACK_COMMAND;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function isExecutable(name) {
|
|
35
|
+
try {
|
|
36
|
+
await execFileAsync(platform() === "win32" ? "where" : "which", [name]);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function registerMcp(agent) {
|
|
44
|
+
if (!(await isExecutable(agent)))
|
|
45
|
+
return false;
|
|
46
|
+
const scope = agent === "claude" ? ["--scope", "user"] : [];
|
|
47
|
+
try {
|
|
48
|
+
await execFileAsync(agent, ["mcp", "add", ...scope, "monnet", "--", "npx", "-y", "@monnet/mcp"]);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Already registered — an idempotent install, not a failure.
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
export async function install(io) {
|
|
56
|
+
const body = await commandBody();
|
|
57
|
+
const targets = commandTargets();
|
|
58
|
+
for (const target of targets) {
|
|
59
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
60
|
+
writeFileSync(target, body);
|
|
61
|
+
}
|
|
62
|
+
const agents = [];
|
|
63
|
+
for (const agent of ["claude", "codex"]) {
|
|
64
|
+
if (await registerMcp(agent))
|
|
65
|
+
agents.push(agent);
|
|
66
|
+
}
|
|
67
|
+
io.out({
|
|
68
|
+
installed: true,
|
|
69
|
+
commands: targets,
|
|
70
|
+
agents,
|
|
71
|
+
next: agents.length
|
|
72
|
+
? "Start a fresh coding-agent session and run /monnet-init."
|
|
73
|
+
: "No claude or codex CLI found on PATH — add the MCP server manually (see the README), then run /monnet-init.",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/cli/install.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/io.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Everything the commands do to the outside world, in one injectable shape.
|
|
2
|
+
* Tests pass a recording double ; nothing else in the CLI touches stdio,
|
|
3
|
+
* the clock or the browser directly. */
|
|
4
|
+
export interface Io {
|
|
5
|
+
/** Machine-readable result, for the coding agent parsing stdout. */
|
|
6
|
+
out(value: unknown): void;
|
|
7
|
+
/** Human-readable progress, for the terminal the user is looking at. */
|
|
8
|
+
err(text: string): void;
|
|
9
|
+
open(url: string): void;
|
|
10
|
+
sleep(ms: number): Promise<void>;
|
|
11
|
+
readStdin(): Promise<string>;
|
|
12
|
+
}
|
|
13
|
+
/** A message meant for the user, not a stack trace. The entry point turns it
|
|
14
|
+
* into a clean stderr line and exit code 1. */
|
|
15
|
+
export declare class CliFailure extends Error {
|
|
16
|
+
}
|
|
17
|
+
export declare function fail(message: string): never;
|
|
18
|
+
export declare function defaultIo(): Io;
|
package/dist/cli/io.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { platform } from "node:os";
|
|
3
|
+
/** A message meant for the user, not a stack trace. The entry point turns it
|
|
4
|
+
* into a clean stderr line and exit code 1. */
|
|
5
|
+
export class CliFailure extends Error {
|
|
6
|
+
}
|
|
7
|
+
export function fail(message) {
|
|
8
|
+
throw new CliFailure(message);
|
|
9
|
+
}
|
|
10
|
+
function openInBrowser(url) {
|
|
11
|
+
const [command, args] = platform() === "darwin" ? ["open", [url]]
|
|
12
|
+
: platform() === "win32" ? ["cmd", ["/c", "start", "", url]]
|
|
13
|
+
: ["xdg-open", [url]];
|
|
14
|
+
spawn(command, args, { detached: true, stdio: "ignore" }).unref();
|
|
15
|
+
}
|
|
16
|
+
export function defaultIo() {
|
|
17
|
+
return {
|
|
18
|
+
out: (value) => process.stdout.write(`${typeof value === "string" ? value : JSON.stringify(value)}\n`),
|
|
19
|
+
err: (text) => process.stderr.write(text),
|
|
20
|
+
open: openInBrowser,
|
|
21
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
22
|
+
readStdin: async () => {
|
|
23
|
+
const chunks = [];
|
|
24
|
+
for await (const chunk of process.stdin)
|
|
25
|
+
chunks.push(chunk);
|
|
26
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../../src/cli/io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAenC;gDACgD;AAChD,MAAM,OAAO,UAAW,SAAQ,KAAK;CAAG;AAExC,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GACnB,QAAQ,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC,CAAC,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;YAC1D,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO;QACL,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;QACtG,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QACzC,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAChE,SAAS,EAAE,KAAK,IAAI,EAAE;YACpB,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;YACtE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser sign-in, in two commands on purpose.
|
|
3
|
+
*
|
|
4
|
+
* `init` opens the browser and returns immediately with the code the user
|
|
5
|
+
* must type there, so the coding agent can show it before anything blocks.
|
|
6
|
+
* `await-signin` then waits for the browser step to complete.
|
|
7
|
+
*
|
|
8
|
+
* The session id stays local — it is what redeems the key, so it never goes
|
|
9
|
+
* into a URL. What the browser gets is the short code, which is useless to
|
|
10
|
+
* anyone who is not looking at this terminal.
|
|
11
|
+
*/
|
|
12
|
+
import { type Io } from "./io.js";
|
|
13
|
+
export declare function init(io: Io): Promise<void>;
|
|
14
|
+
export declare function awaitSignin(io: Io): Promise<void>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser sign-in, in two commands on purpose.
|
|
3
|
+
*
|
|
4
|
+
* `init` opens the browser and returns immediately with the code the user
|
|
5
|
+
* must type there, so the coding agent can show it before anything blocks.
|
|
6
|
+
* `await-signin` then waits for the browser step to complete.
|
|
7
|
+
*
|
|
8
|
+
* The session id stays local — it is what redeems the key, so it never goes
|
|
9
|
+
* into a URL. What the browser gets is the short code, which is useless to
|
|
10
|
+
* anyone who is not looking at this terminal.
|
|
11
|
+
*/
|
|
12
|
+
import { readConfig, updateOnboarding, writeConfig } from "../config.js";
|
|
13
|
+
import { apiKey, apiUrl, appUrl, request } from "./env.js";
|
|
14
|
+
import { fail } from "./io.js";
|
|
15
|
+
const POLL_INTERVAL_MS = 2000;
|
|
16
|
+
const DEFAULT_TTL_SECONDS = 15 * 60;
|
|
17
|
+
export async function init(io) {
|
|
18
|
+
if (apiKey()) {
|
|
19
|
+
io.out({ authenticated: true, resumed: true, next: "propose_workspace" });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const session = await request("/api/cli-auth/mcp/start", { method: "POST" }, false);
|
|
23
|
+
const url = `${appUrl()}/cli-auth?kind=mcp`;
|
|
24
|
+
updateOnboarding({ pendingDevice: session.device, step: 2 });
|
|
25
|
+
io.err(`\nMonnet sign-in code: ${session.user_code}\n` +
|
|
26
|
+
`Opened ${url} — sign in, then type that code.\n` +
|
|
27
|
+
"It expires in 15 minutes, and only this terminal can use it.\n\n");
|
|
28
|
+
io.open(url);
|
|
29
|
+
io.out({
|
|
30
|
+
authenticated: false,
|
|
31
|
+
user_code: session.user_code,
|
|
32
|
+
url,
|
|
33
|
+
expires_in: session.expires_in,
|
|
34
|
+
next: "await-signin",
|
|
35
|
+
show_user: `Sign in at ${url} and enter the code ${session.user_code}`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export async function awaitSignin(io) {
|
|
39
|
+
const config = readConfig();
|
|
40
|
+
if (apiKey()) {
|
|
41
|
+
io.out({ authenticated: true, resumed: true, next: "propose_workspace" });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const device = config.onboarding?.pendingDevice;
|
|
45
|
+
if (!device)
|
|
46
|
+
fail("no sign-in in progress; run `npx -y @monnet/mcp init` first");
|
|
47
|
+
const attempts = Math.ceil((DEFAULT_TTL_SECONDS * 1000) / POLL_INTERVAL_MS);
|
|
48
|
+
io.err("Waiting for the browser");
|
|
49
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
50
|
+
await io.sleep(POLL_INTERVAL_MS);
|
|
51
|
+
const response = await fetch(`${apiUrl()}/api/cli-auth/mcp/poll`, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: { "Content-Type": "application/json" },
|
|
54
|
+
body: JSON.stringify({ device }),
|
|
55
|
+
});
|
|
56
|
+
if (response.status === 202) {
|
|
57
|
+
io.err(".");
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (!response.ok)
|
|
61
|
+
fail(`sign-in failed (${response.status}); run /monnet-init again to restart it`);
|
|
62
|
+
const { token } = (await response.json());
|
|
63
|
+
const current = readConfig();
|
|
64
|
+
writeConfig({
|
|
65
|
+
...current,
|
|
66
|
+
apiKey: token,
|
|
67
|
+
apiUrl: apiUrl(),
|
|
68
|
+
appUrl: appUrl(),
|
|
69
|
+
onboarding: { ...current.onboarding, pendingDevice: undefined, step: 3 },
|
|
70
|
+
});
|
|
71
|
+
io.err("\n");
|
|
72
|
+
io.out({ authenticated: true, next: "propose_workspace" });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
fail("sign-in timed out after 15 minutes; run /monnet-init again to resume");
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/cli/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** The workspace/knowledge/invite/motion steps of `/monnet-init`.
|
|
2
|
+
*
|
|
3
|
+
* Every one of them writes its progress to the local config, so an
|
|
4
|
+
* interrupted run resumes where it stopped instead of creating a second
|
|
5
|
+
* workspace.
|
|
6
|
+
*/
|
|
7
|
+
import { type Io } from "./io.js";
|
|
8
|
+
/** Where each channel actually connects teammates, as the web app has it. */
|
|
9
|
+
declare const INVITE_CHANNELS: {
|
|
10
|
+
readonly slack: (slug: string) => string;
|
|
11
|
+
readonly email: (slug: string) => string;
|
|
12
|
+
readonly teams: (slug: string) => string;
|
|
13
|
+
};
|
|
14
|
+
export type InviteChannel = keyof typeof INVITE_CHANNELS;
|
|
15
|
+
export declare function inviteUrl(channel: InviteChannel, slug: string): string;
|
|
16
|
+
export declare function suggestName(io: Io, args: string[], fallbackName: string): Promise<void>;
|
|
17
|
+
/** Accept an invitation and adopt that workspace as this run's workspace.
|
|
18
|
+
*
|
|
19
|
+
* The token comes from `list_workspaces`, which reads it off the sidebar's
|
|
20
|
+
* pending invitations. Joining is what a teammate needs instead of step 4:
|
|
21
|
+
* their team's workspace already exists, brain and all.
|
|
22
|
+
*/
|
|
23
|
+
export declare function joinWorkspace(io: Io, args: string[]): Promise<void>;
|
|
24
|
+
export declare function onboardWorkspace(io: Io, args: string[]): Promise<void>;
|
|
25
|
+
/** Queue the briefing, then ask the brain to digest it now.
|
|
26
|
+
*
|
|
27
|
+
* Pushing alone only files a SOURCE: the pages a person recognises as their
|
|
28
|
+
* company brain are written by the ingest job, which otherwise runs tonight —
|
|
29
|
+
* long after the user has closed the terminal and decided the wiki is empty.
|
|
30
|
+
* So we press the same "Run now" button the Brain header offers.
|
|
31
|
+
*/
|
|
32
|
+
export declare function pushKnowledge(io: Io): Promise<void>;
|
|
33
|
+
export declare function invite(io: Io, args: string[]): void;
|
|
34
|
+
/** `args[0]` is an optional workspace skill to pre-apply — the flow passes
|
|
35
|
+
* `onboard-team` when the user asked for a fresh-eyes report on the team, so
|
|
36
|
+
* the run opens with the playbook Monnet already uses for that, instead of a
|
|
37
|
+
* prompt reinventing it. An unknown name degrades to a plain thread server
|
|
38
|
+
* side, so it is never worth validating twice. */
|
|
39
|
+
export declare function createFirstMotion(io: Io, args?: string[]): Promise<void>;
|
|
40
|
+
export declare function status(io: Io): void;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/** The workspace/knowledge/invite/motion steps of `/monnet-init`.
|
|
2
|
+
*
|
|
3
|
+
* Every one of them writes its progress to the local config, so an
|
|
4
|
+
* interrupted run resumes where it stopped instead of creating a second
|
|
5
|
+
* workspace.
|
|
6
|
+
*/
|
|
7
|
+
import { configPath, readConfig, updateOnboarding } from "../config.js";
|
|
8
|
+
import { apiKey, appUrl, request } from "./env.js";
|
|
9
|
+
import { CliFailure, fail } from "./io.js";
|
|
10
|
+
/** Where each channel actually connects teammates, as the web app has it. */
|
|
11
|
+
const INVITE_CHANNELS = {
|
|
12
|
+
slack: (slug) => `/onboarding/invite?slug=${encodeURIComponent(slug)}`,
|
|
13
|
+
email: (slug) => `/onboarding/invite/email?slug=${encodeURIComponent(slug)}`,
|
|
14
|
+
// Teams is connected from the workspace's Integrations tab — the onboarding
|
|
15
|
+
// invite chooser only offers Slack and email.
|
|
16
|
+
teams: (slug) => `/${encodeURIComponent(slug)}/edit?tab=integrations`,
|
|
17
|
+
};
|
|
18
|
+
export function inviteUrl(channel, slug) {
|
|
19
|
+
return `${appUrl()}${INVITE_CHANNELS[channel](slug)}`;
|
|
20
|
+
}
|
|
21
|
+
function workspace() {
|
|
22
|
+
const state = readConfig().onboarding;
|
|
23
|
+
if (!state?.workspaceId || !state.workspaceSlug)
|
|
24
|
+
fail("create the workspace first");
|
|
25
|
+
return { workspaceId: state.workspaceId, workspaceSlug: state.workspaceSlug };
|
|
26
|
+
}
|
|
27
|
+
export async function suggestName(io, args, fallbackName) {
|
|
28
|
+
const name = args.join(" ").trim() || fallbackName;
|
|
29
|
+
const { slug, base, base_taken } = await request(`/workspaces/generate-slug?name=${encodeURIComponent(name)}`);
|
|
30
|
+
// The returned SLUG is free by construction — that is the one thing it can
|
|
31
|
+
// never tell you. `base_taken` can: somebody else already goes by this name,
|
|
32
|
+
// and taking `acme-2` would fork their workspace rather than join it. Say so
|
|
33
|
+
// instead of quietly reserving the next number.
|
|
34
|
+
const warning = base_taken
|
|
35
|
+
? `A workspace already goes by "${base}" and it is not yours. If that is your team, have ` +
|
|
36
|
+
"them invite you (Settings → Members) and run `join-workspace` instead of creating a " +
|
|
37
|
+
"second one. Otherwise pick a different name."
|
|
38
|
+
: undefined;
|
|
39
|
+
io.out({ name, slug, slug_available: true, base, base_taken, ...(warning ? { warning } : {}) });
|
|
40
|
+
}
|
|
41
|
+
/** Accept an invitation and adopt that workspace as this run's workspace.
|
|
42
|
+
*
|
|
43
|
+
* The token comes from `list_workspaces`, which reads it off the sidebar's
|
|
44
|
+
* pending invitations. Joining is what a teammate needs instead of step 4:
|
|
45
|
+
* their team's workspace already exists, brain and all.
|
|
46
|
+
*/
|
|
47
|
+
export async function joinWorkspace(io, args) {
|
|
48
|
+
const token = args[0]?.trim();
|
|
49
|
+
if (!token)
|
|
50
|
+
fail("usage: join-workspace <invitation-token>");
|
|
51
|
+
const accepted = await request(`/invitations/${encodeURIComponent(token)}/accept`, { method: "POST" });
|
|
52
|
+
// The rest of the flow addresses the workspace by id, and acceptance only
|
|
53
|
+
// hands back the slug.
|
|
54
|
+
const workspace = await request(`/workspaces/by-slug/${encodeURIComponent(accepted.workspace_slug)}`);
|
|
55
|
+
updateOnboarding({ workspaceId: workspace.id, workspaceSlug: workspace.slug, step: 5 });
|
|
56
|
+
io.out({ id: workspace.id, slug: workspace.slug, name: workspace.name, joined: true });
|
|
57
|
+
}
|
|
58
|
+
export async function onboardWorkspace(io, args) {
|
|
59
|
+
const [name, slug] = args;
|
|
60
|
+
if (!name || !slug)
|
|
61
|
+
fail("usage: onboard-workspace <name> <available-slug>");
|
|
62
|
+
const existing = readConfig().onboarding;
|
|
63
|
+
if (existing?.workspaceId && existing.workspaceSlug) {
|
|
64
|
+
// Resume only the workspace this config is actually about. The state lives
|
|
65
|
+
// in ~/.config/monnet, so it is per MACHINE, not per repository: without
|
|
66
|
+
// this check, running the flow in a second repo silently adopted the first
|
|
67
|
+
// one's workspace and ignored the name the user had just approved.
|
|
68
|
+
if (existing.workspaceSlug !== slug) {
|
|
69
|
+
fail(`this machine is already onboarding "${existing.workspaceSlug}", not "${slug}". ` +
|
|
70
|
+
`Finish that run, or start over by removing the "onboarding" block from ${configPath}.`);
|
|
71
|
+
}
|
|
72
|
+
io.out({ id: existing.workspaceId, slug: existing.workspaceSlug, resumed: true });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const created = await request("/workspaces", { method: "POST", body: JSON.stringify({ name, slug }) });
|
|
76
|
+
updateOnboarding({ workspaceId: created.id, workspaceSlug: created.slug, step: 5 });
|
|
77
|
+
io.out(created);
|
|
78
|
+
}
|
|
79
|
+
/** Queue the briefing, then ask the brain to digest it now.
|
|
80
|
+
*
|
|
81
|
+
* Pushing alone only files a SOURCE: the pages a person recognises as their
|
|
82
|
+
* company brain are written by the ingest job, which otherwise runs tonight —
|
|
83
|
+
* long after the user has closed the terminal and decided the wiki is empty.
|
|
84
|
+
* So we press the same "Run now" button the Brain header offers.
|
|
85
|
+
*/
|
|
86
|
+
export async function pushKnowledge(io) {
|
|
87
|
+
const { workspaceId } = workspace();
|
|
88
|
+
const content = await io.readStdin();
|
|
89
|
+
if (!content.trim())
|
|
90
|
+
fail("pipe the generated knowledge document on stdin");
|
|
91
|
+
const source = await request(`/workspaces/${workspaceId}/wiki/sources/text`, { method: "POST", body: JSON.stringify({ filename: "initial-repository-context.md", content }) });
|
|
92
|
+
updateOnboarding({ step: 6 });
|
|
93
|
+
io.out({ ...source, ...(await startIngest(io, workspaceId)), next: "fresh_eyes_report" });
|
|
94
|
+
}
|
|
95
|
+
/** Best-effort: the source is filed either way, and every refusal here is a
|
|
96
|
+
* reason to say "tonight" rather than to fail the step — the job is switched
|
|
97
|
+
* off, or a run is already going (409), or the network blipped. */
|
|
98
|
+
async function startIngest(io, workspaceId) {
|
|
99
|
+
try {
|
|
100
|
+
await request(`/workspaces/${workspaceId}/scheduled-tasks/brain_ingest/run`, { method: "POST" });
|
|
101
|
+
return {
|
|
102
|
+
ingest_started: true,
|
|
103
|
+
note: "The brain is digesting it now — pages appear over the next few minutes.",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const detail = error instanceof CliFailure ? error.message : String(error);
|
|
108
|
+
io.err(`could not start the brain ingest (${detail}); it will run tonight\n`);
|
|
109
|
+
return { ingest_started: false, note: "Filed as a source; the nightly ingest will turn it into pages." };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export function invite(io, args) {
|
|
113
|
+
const requested = (args[0] || "slack");
|
|
114
|
+
if (!(requested in INVITE_CHANNELS)) {
|
|
115
|
+
fail(`unknown invite channel "${requested}"; use one of ${Object.keys(INVITE_CHANNELS).join(", ")}`);
|
|
116
|
+
}
|
|
117
|
+
const { workspaceSlug } = workspace();
|
|
118
|
+
const url = inviteUrl(requested, workspaceSlug);
|
|
119
|
+
io.open(url);
|
|
120
|
+
updateOnboarding({ step: 7 });
|
|
121
|
+
io.out({ channel: requested, url, opened: true, can_skip: true });
|
|
122
|
+
}
|
|
123
|
+
/** `args[0]` is an optional workspace skill to pre-apply — the flow passes
|
|
124
|
+
* `onboard-team` when the user asked for a fresh-eyes report on the team, so
|
|
125
|
+
* the run opens with the playbook Monnet already uses for that, instead of a
|
|
126
|
+
* prompt reinventing it. An unknown name degrades to a plain thread server
|
|
127
|
+
* side, so it is never worth validating twice. */
|
|
128
|
+
export async function createFirstMotion(io, args = []) {
|
|
129
|
+
const { workspaceId, workspaceSlug } = workspace();
|
|
130
|
+
const playbook = args[0]?.trim() || undefined;
|
|
131
|
+
const prompt = await io.readStdin();
|
|
132
|
+
if (!prompt.trim())
|
|
133
|
+
fail("pipe the chosen priority and supporting context on stdin");
|
|
134
|
+
const motion = await request(`/workspaces/${workspaceId}/motions/generate`, { method: "POST", body: JSON.stringify({ prompt, ...(playbook ? { playbook } : {}) }) });
|
|
135
|
+
const shortId = motion.id.slice(0, 8);
|
|
136
|
+
const url = `${appUrl()}/${workspaceSlug}/motions/${shortId}`;
|
|
137
|
+
io.open(url);
|
|
138
|
+
updateOnboarding({ motionId: motion.id, step: 8 });
|
|
139
|
+
io.out({ id: motion.id, short_id: shortId, url, playbook: playbook ?? null, opened: true, complete: true });
|
|
140
|
+
}
|
|
141
|
+
export function status(io) {
|
|
142
|
+
const config = readConfig();
|
|
143
|
+
io.out({ connected: Boolean(apiKey()), ...(config.onboarding || { step: 1 }) });
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=onboarding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../../src/cli/onboarding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Command registry for the `monnet` CLI.
|
|
2
|
+
*
|
|
3
|
+
* Adding a step to /monnet-init means adding one entry here, never editing a
|
|
4
|
+
* dispatch chain.
|
|
5
|
+
*/
|
|
6
|
+
import { type Io } from "./io.js";
|
|
7
|
+
type Command = (io: Io, args: string[]) => void | Promise<void>;
|
|
8
|
+
export declare const COMMANDS: Record<string, Command>;
|
|
9
|
+
export declare const USAGE: string;
|
|
10
|
+
export declare function run(argv: string[], io?: Io): Promise<void>;
|
|
11
|
+
export {};
|
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Command registry for the `monnet` CLI.
|
|
2
|
+
*
|
|
3
|
+
* Adding a step to /monnet-init means adding one entry here, never editing a
|
|
4
|
+
* dispatch chain.
|
|
5
|
+
*/
|
|
6
|
+
import { hostname } from "node:os";
|
|
7
|
+
import { install } from "./install.js";
|
|
8
|
+
import { awaitSignin, init } from "./login.js";
|
|
9
|
+
import { defaultIo, fail } from "./io.js";
|
|
10
|
+
import { createFirstMotion, invite, joinWorkspace, onboardWorkspace, pushKnowledge, status, suggestName, } from "./onboarding.js";
|
|
11
|
+
export const COMMANDS = {
|
|
12
|
+
install: (io) => install(io),
|
|
13
|
+
init: (io) => init(io),
|
|
14
|
+
"await-signin": (io) => awaitSignin(io),
|
|
15
|
+
"suggest-name": (io, args) => suggestName(io, args, hostname().split(".")[0]),
|
|
16
|
+
"onboard-workspace": (io, args) => onboardWorkspace(io, args),
|
|
17
|
+
"join-workspace": (io, args) => joinWorkspace(io, args),
|
|
18
|
+
"push-knowledge": (io) => pushKnowledge(io),
|
|
19
|
+
invite: (io, args) => invite(io, args),
|
|
20
|
+
"create-first-motion": (io, args) => createFirstMotion(io, args),
|
|
21
|
+
status: (io) => status(io),
|
|
22
|
+
};
|
|
23
|
+
export const USAGE = `Usage: monnet <${Object.keys(COMMANDS).join("|")}>`;
|
|
24
|
+
export async function run(argv, io = defaultIo()) {
|
|
25
|
+
const [command, ...args] = argv;
|
|
26
|
+
if (!command || command === "help" || command === "--help") {
|
|
27
|
+
io.out(USAGE);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const handler = COMMANDS[command];
|
|
31
|
+
if (!handler)
|
|
32
|
+
fail(`unknown command "${command}"\n${USAGE}`);
|
|
33
|
+
await handler(io, args);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,IAAI,EAAW,MAAM,SAAS,CAAC;AACnD,OAAO,EACL,iBAAiB,EACjB,MAAM,EACN,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,MAAM,EACN,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAIzB,MAAM,CAAC,MAAM,QAAQ,GAA4B;IAC/C,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;IACtB,cAAc,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;IACvC,cAAc,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,mBAAmB,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,IAAI,CAAC;IAC7D,gBAAgB,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC;IACvD,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;IAC3C,MAAM,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC;IACtC,qBAAqB,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,IAAI,CAAC;IAChE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,kBAAkB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAE1E,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,KAAS,SAAS,EAAE;IAC5D,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC3D,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACd,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,OAAO;QAAE,IAAI,CAAC,oBAAoB,OAAO,MAAM,KAAK,EAAE,CAAC,CAAC;IAC7D,MAAM,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Imported FIRST by every CLI test file.
|
|
3
|
+
*
|
|
4
|
+
* `config.ts` resolves ~/.config/monnet at import time, so the home
|
|
5
|
+
* directory has to be redirected before it loads — otherwise a test run
|
|
6
|
+
* would read, and overwrite, the developer's real Monnet credential.
|
|
7
|
+
*/
|
|
8
|
+
export declare const TEST_HOME: string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Imported FIRST by every CLI test file.
|
|
3
|
+
*
|
|
4
|
+
* `config.ts` resolves ~/.config/monnet at import time, so the home
|
|
5
|
+
* directory has to be redirected before it loads — otherwise a test run
|
|
6
|
+
* would read, and overwrite, the developer's real Monnet credential.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdtempSync } from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
export const TEST_HOME = mkdtempSync(join(tmpdir(), "monnet-cli-home-"));
|
|
12
|
+
process.env.HOME = TEST_HOME;
|
|
13
|
+
process.env.USERPROFILE = TEST_HOME;
|
|
14
|
+
process.env.MONNET_API_URL = "https://api.monnet.test";
|
|
15
|
+
process.env.MONNET_APP_URL = "https://app.monnet.test";
|
|
16
|
+
delete process.env.MONNET_API_KEY;
|
|
17
|
+
//# sourceMappingURL=test-home.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-home.js","sourceRoot":"","sources":["../../src/cli/test-home.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAEzE,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,SAAS,CAAC;AAC7B,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,SAAS,CAAC;AACpC,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,yBAAyB,CAAC;AACvD,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,yBAAyB,CAAC;AACvD,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Io } from "./io.js";
|
|
2
|
+
export interface RecordingIo extends Io {
|
|
3
|
+
outputs: unknown[];
|
|
4
|
+
errors: string[];
|
|
5
|
+
opened: string[];
|
|
6
|
+
stdin: string;
|
|
7
|
+
/** Last JSON payload the command emitted on stdout. */
|
|
8
|
+
last(): Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export declare function recordingIo(stdin?: string): RecordingIo;
|
|
11
|
+
export interface StubbedCall {
|
|
12
|
+
method: string;
|
|
13
|
+
path: string;
|
|
14
|
+
body: unknown;
|
|
15
|
+
headers: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
/** Replaces global fetch with a scripted responder, and records every call. */
|
|
18
|
+
export declare function stubFetch(responder: (call: StubbedCall) => {
|
|
19
|
+
status?: number;
|
|
20
|
+
body?: unknown;
|
|
21
|
+
}): StubbedCall[];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function recordingIo(stdin = "") {
|
|
2
|
+
const io = {
|
|
3
|
+
outputs: [],
|
|
4
|
+
errors: [],
|
|
5
|
+
opened: [],
|
|
6
|
+
stdin,
|
|
7
|
+
out: (value) => { io.outputs.push(value); },
|
|
8
|
+
err: (text) => { io.errors.push(text); },
|
|
9
|
+
open: (url) => { io.opened.push(url); },
|
|
10
|
+
sleep: async () => { },
|
|
11
|
+
readStdin: async () => io.stdin,
|
|
12
|
+
last: () => io.outputs[io.outputs.length - 1],
|
|
13
|
+
};
|
|
14
|
+
return io;
|
|
15
|
+
}
|
|
16
|
+
/** Replaces global fetch with a scripted responder, and records every call. */
|
|
17
|
+
export function stubFetch(responder) {
|
|
18
|
+
const calls = [];
|
|
19
|
+
globalThis.fetch = (async (url, options = {}) => {
|
|
20
|
+
const call = {
|
|
21
|
+
method: options.method || "GET",
|
|
22
|
+
path: String(url).replace(/^https:\/\/api\.monnet\.test/, ""),
|
|
23
|
+
body: options.body ? JSON.parse(String(options.body)) : undefined,
|
|
24
|
+
headers: (options.headers || {}),
|
|
25
|
+
};
|
|
26
|
+
calls.push(call);
|
|
27
|
+
const { status = 200, body = {} } = responder(call);
|
|
28
|
+
return { ok: status < 400, status, json: async () => body };
|
|
29
|
+
});
|
|
30
|
+
return calls;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=test-io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-io.js","sourceRoot":"","sources":["../../src/cli/test-io.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,WAAW,CAAC,KAAK,GAAG,EAAE;IACpC,MAAM,EAAE,GAAgB;QACtB,OAAO,EAAE,EAAE;QACX,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,EAAE;QACV,KAAK;QACL,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3C,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACvC,KAAK,EAAE,KAAK,IAAI,EAAE,GAA0B,CAAC;QAC7C,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC,KAAK;QAC/B,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAA4B;KACzE,CAAC;IACF,OAAO,EAAE,CAAC;AACZ,CAAC;AASD,+EAA+E;AAC/E,MAAM,UAAU,SAAS,CACvB,SAAqE;IAErE,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,UAAU,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,GAAW,EAAE,UAAuB,EAAE,EAAE,EAAE;QACnE,MAAM,IAAI,GAAgB;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;YAC/B,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,8BAA8B,EAAE,EAAE,CAAC;YAC7D,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;YACjE,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAA2B;SAC3D,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QACpD,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;IAC9D,CAAC,CAAU,CAAC;IACZ,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { CliFailure } from "./cli/io.js";
|
|
3
|
+
import { run } from "./cli/run.js";
|
|
4
|
+
try {
|
|
5
|
+
await run(process.argv.slice(2));
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
const message = error instanceof CliFailure || error instanceof Error ? error.message : String(error);
|
|
9
|
+
process.stderr.write(`monnet: ${message}\n`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,IAAI,CAAC;IACH,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,MAAM,OAAO,GAAG,KAAK,YAAY,UAAU,IAAI,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACtG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,OAAO,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Thin HTTP client that wraps the Monnet backend with X-API-Key auth.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The env vars come from the MCP client's config JSON, not from a shell
|
|
5
5
|
* .env — the MCP server is spawned by Claude Desktop / Cursor with this env.
|
|
6
|
+
* When they are absent we fall back to the credential `monnet init` saved,
|
|
7
|
+
* read on every call rather than at import: the server is usually spawned
|
|
8
|
+
* BEFORE `/monnet-init` runs, and a key written afterwards has to be picked
|
|
9
|
+
* up by a live process. Exiting at import instead would leave the user with
|
|
10
|
+
* a dead MCP server for the rest of their session.
|
|
6
11
|
*/
|
|
7
12
|
export declare class MonnetApiError extends Error {
|
|
8
13
|
status: number;
|
package/dist/client.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Thin HTTP client that wraps the Monnet backend with X-API-Key auth.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The env vars come from the MCP client's config JSON, not from a shell
|
|
5
5
|
* .env — the MCP server is spawned by Claude Desktop / Cursor with this env.
|
|
6
|
+
* When they are absent we fall back to the credential `monnet init` saved,
|
|
7
|
+
* read on every call rather than at import: the server is usually spawned
|
|
8
|
+
* BEFORE `/monnet-init` runs, and a key written afterwards has to be picked
|
|
9
|
+
* up by a live process. Exiting at import instead would leave the user with
|
|
10
|
+
* a dead MCP server for the rest of their session.
|
|
6
11
|
*/
|
|
7
|
-
|
|
8
|
-
const MONNET_API_KEY = process.env.MONNET_API_KEY;
|
|
9
|
-
if (!MONNET_API_KEY) {
|
|
10
|
-
process.stderr.write("Error: MONNET_API_KEY environment variable is not set.\n" +
|
|
11
|
-
"Generate one at https://app.monnet.ai/settings and add it to your MCP client config.\n");
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
12
|
+
import { readConfig } from "./config.js";
|
|
14
13
|
export class MonnetApiError extends Error {
|
|
15
14
|
status;
|
|
16
15
|
constructor(status, message) {
|
|
@@ -19,6 +18,17 @@ export class MonnetApiError extends Error {
|
|
|
19
18
|
this.name = "MonnetApiError";
|
|
20
19
|
}
|
|
21
20
|
}
|
|
21
|
+
const NOT_CONNECTED = "This machine is not connected to Monnet yet. Run `/monnet-init` (or `npx -y @monnet/mcp init`) " +
|
|
22
|
+
"to connect it, or set MONNET_API_KEY to a key from https://app.monnet.ai/settings.";
|
|
23
|
+
function apiUrl() {
|
|
24
|
+
return (process.env.MONNET_API_URL || readConfig().apiUrl || "https://api.monnet.ai").replace(/\/$/, "");
|
|
25
|
+
}
|
|
26
|
+
function apiKey() {
|
|
27
|
+
const key = process.env.MONNET_API_KEY || readConfig().apiKey;
|
|
28
|
+
if (!key)
|
|
29
|
+
throw new MonnetApiError(401, NOT_CONNECTED);
|
|
30
|
+
return key;
|
|
31
|
+
}
|
|
22
32
|
function friendlyMessage(status, detail) {
|
|
23
33
|
// Backend-supplied detail is always preferred for client errors that carry
|
|
24
34
|
// actionable info (permission denials, validation errors, not-found hints).
|
|
@@ -83,11 +93,11 @@ export async function apiFetch(path, options = {}, timeoutMs = DEFAULT_TIMEOUT_M
|
|
|
83
93
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
84
94
|
let response;
|
|
85
95
|
try {
|
|
86
|
-
response = await fetch(`${
|
|
96
|
+
response = await fetch(`${apiUrl()}${path}`, {
|
|
87
97
|
...options,
|
|
88
98
|
signal: controller.signal,
|
|
89
99
|
headers: {
|
|
90
|
-
"X-API-Key":
|
|
100
|
+
"X-API-Key": apiKey(),
|
|
91
101
|
"Content-Type": "application/json",
|
|
92
102
|
...options.headers,
|
|
93
103
|
},
|
|
@@ -128,11 +138,11 @@ export async function apiPostWithSSE(path, body) {
|
|
|
128
138
|
const timer = setTimeout(() => controller.abort(), 120_000);
|
|
129
139
|
let response;
|
|
130
140
|
try {
|
|
131
|
-
response = await fetch(`${
|
|
141
|
+
response = await fetch(`${apiUrl()}${path}`, {
|
|
132
142
|
method: "POST",
|
|
133
143
|
signal: controller.signal,
|
|
134
144
|
headers: {
|
|
135
|
-
"X-API-Key":
|
|
145
|
+
"X-API-Key": apiKey(),
|
|
136
146
|
"Content-Type": "application/json",
|
|
137
147
|
},
|
|
138
148
|
body: JSON.stringify(body),
|
|
@@ -214,7 +224,7 @@ export async function apiPostWithSSE(path, body) {
|
|
|
214
224
|
* three, and this is this surface's one place to change.
|
|
215
225
|
*/
|
|
216
226
|
export function motionFileRenderUrl(workspaceId, motionId, fileId) {
|
|
217
|
-
return `${
|
|
227
|
+
return `${apiUrl()}/workspaces/${workspaceId}/motions/${motionId}/files/${fileId}/render`;
|
|
218
228
|
}
|
|
219
229
|
/**
|
|
220
230
|
* Resolve a workspace slug to its workspace_id via GET /workspaces/by-slug/{slug}.
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,OAAO,cAAe,SAAQ,KAAK;IACpB;IAAnB,YAAmB,MAAc,EAAE,OAAe;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;QADE,WAAM,GAAN,MAAM,CAAQ;QAE/B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,aAAa,GACjB,iGAAiG;IACjG,oFAAoF,CAAC;AAEvF,SAAS,MAAM;IACb,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,CAAC,MAAM,IAAI,uBAAuB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3G,CAAC;AAED,SAAS,MAAM;IACb,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,CAAC,MAAM,CAAC;IAC9D,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IACvD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,eAAe,CAAC,MAAc,EAAE,MAAqB;IAC5D,2EAA2E;IAC3E,4EAA4E;IAC5E,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,GAAG;YACN,OAAO,CACL,sDAAsD;gBACtD,qEAAqE,CACtE,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,mDAAmD,QAAQ,qEAAqE,CAAC;QAC1I,KAAK,GAAG;YACN,OAAO,YAAY,QAAQ,iDAAiD,CAAC;QAC/E,KAAK,GAAG;YACN,OAAO,WAAW,QAAQ,GAAG,CAAC;QAChC,KAAK,GAAG;YACN,OAAO,gBAAgB,QAAQ,GAAG,CAAC;QACrC,KAAK,GAAG;YACN,OAAO,8DAA8D,CAAC;QACxE,KAAK,GAAG,CAAC;QACT,KAAK,GAAG,CAAC;QACT,KAAK,GAAG;YACN,OAAO,wEAAwE,CAAC;QAClF;YACE,OAAO,cAAc,MAAM,GAAG,QAAQ,EAAE,CAAC;IAC7C,CAAC;AACH,CAAC;AAOD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe;IACxC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,MAAM,GAAI,MAA+B,CAAC,MAAM,CAAC;IACvD,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM;SAClB,MAAM,CAAC,CAAC,CAAC,EAAwB,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC;SACjE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,IAAI,SAAS,CAAC;QACvC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,OAAO,EAAE,CAAC;IACxE,CAAC,CAAC,CAAC;IACL,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC;AAED,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAI,IAAY,EAAE,UAAuB,EAAE,EAAE,SAAS,GAAG,kBAAkB;IACvG,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAE9D,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE;YAC3C,GAAG,OAAO;YACV,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE;gBACP,WAAW,EAAE,MAAM,EAAE;gBACrB,cAAc,EAAE,kBAAkB;gBAClC,GAAG,OAAO,CAAC,OAAO;aACnB;SACF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACtD,MAAM,IAAI,cAAc,CAAC,CAAC,EAAE,mGAAmG,CAAC,CAAC;QACnI,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,GAAG,UAAU,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,0BAA0B;QAC5B,CAAC;QACD,MAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtF,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,SAAc,CAAC;IACnD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,IAAa;IAC9D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,oEAAoE;IACpE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;IAE5D,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE;YAC3C,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE;gBACP,WAAW,EAAE,MAAM,EAAE;gBACrB,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACtD,MAAM,IAAI,cAAc,CAAC,CAAC,EAAE,sFAAsF,CAAC,CAAC;QACtH,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACrC,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;gBAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC1E,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QACxB,MAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IAE/D,0EAA0E;IAC1E,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;IACjF,CAAC;IAED,oEAAoE;IACpE,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACvC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,IAAI,SAAS,GAAG,EAAE,CAAC;IAEnB,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QACpD,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;YAAE,SAAS;QAC/B,IAAI,SAAS,GAAG,SAAS,CAAC;QAC1B,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;gBAAE,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBAC3D,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBAAE,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;QACvE,CAAC;QACD,IAAI,SAAS,KAAK,oBAAoB,IAAI,IAAI,EAAE,CAAC;YAC/C,IAAI,CAAC;gBAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAC9E,CAAC;aAAM,IAAI,SAAS,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC;YACxC,IAAI,CAAC;gBAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAC7E,CAAC;aAAM,IAAI,SAAS,KAAK,OAAO,IAAI,IAAI,EAAE,CAAC;YACzC,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IAED,IAAI,SAAS,IAAI,CAAC,UAAU,EAAE,CAAC;QAC7B,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,EAAE,EAAE,CAAC;IACrE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,IAAI,IAAI,EAAE,CAAC;AACtE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAAmB,EAAE,QAAgB,EAAE,MAAc;IACvF,OAAO,GAAG,MAAM,EAAE,eAAe,WAAW,YAAY,QAAQ,UAAU,MAAM,SAAS,CAAC;AAC5F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY;IACnD,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAiB,uBAAuB,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7F,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5B,OAAO,EAAE,CAAC,EAAE,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,IAAI,aAAa,GAAkB,IAAI,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IACxC,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAkB,KAAK,CAAC,CAAC;IAClD,aAAa,GAAG,EAAE,CAAC,GAAG,CAAC;IACvB,OAAO,EAAE,CAAC,GAAG,CAAC;AAChB,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface MonnetConfig {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
apiUrl?: string;
|
|
4
|
+
appUrl?: string;
|
|
5
|
+
onboarding?: {
|
|
6
|
+
workspaceId?: string;
|
|
7
|
+
workspaceSlug?: string;
|
|
8
|
+
motionId?: string;
|
|
9
|
+
/** Private handle of a sign-in started but not yet completed. */
|
|
10
|
+
pendingDevice?: string;
|
|
11
|
+
step?: number;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare const configPath: string;
|
|
15
|
+
export declare function readConfig(): MonnetConfig;
|
|
16
|
+
export declare function writeConfig(next: MonnetConfig): void;
|
|
17
|
+
export declare function updateOnboarding(patch: NonNullable<MonnetConfig["onboarding"]>): void;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
const configDir = join(homedir(), ".config", "monnet");
|
|
5
|
+
export const configPath = join(configDir, "config.json");
|
|
6
|
+
export function readConfig() {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(readFileSync(configPath, "utf8"));
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function writeConfig(next) {
|
|
15
|
+
mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
16
|
+
writeFileSync(configPath, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
|
|
17
|
+
chmodSync(configPath, 0o600);
|
|
18
|
+
}
|
|
19
|
+
export function updateOnboarding(patch) {
|
|
20
|
+
const current = readConfig();
|
|
21
|
+
writeConfig({ ...current, onboarding: { ...current.onboarding, ...patch } });
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgBjC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AACvD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAEzD,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAiB,CAAC;IAAC,CAAC;IAC5E,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC5C,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACvD,aAAa,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjF,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAA8C;IAC7E,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,WAAW,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,GAAG,KAAK,EAAE,EAAE,CAAC,CAAC;AAC/E,CAAC"}
|
package/dist/test-env.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Imported first by every test file.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* suite passes on their machine and runs zero tests everywhere else. That is
|
|
9
|
-
* how the approval gate — the one thing standing between a model and an
|
|
10
|
-
* irreversible delete — came to have tests that never ran in CI.
|
|
4
|
+
* Tool calls throw MonnetApiError(401) when no credential can be found, and
|
|
5
|
+
* a suite that stubs `fetch` still goes through that check. Setting a key
|
|
6
|
+
* here keeps the tests exercising the tools instead of the missing-credential
|
|
7
|
+
* path (which has its own test in client.test.ts).
|
|
11
8
|
*
|
|
12
9
|
* `??=` so a real key in the environment still wins; nothing here reaches the
|
|
13
10
|
* network anyway, since every test stubs `fetch`.
|
package/dist/test-env.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Imported first by every test file.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* suite passes on their machine and runs zero tests everywhere else. That is
|
|
9
|
-
* how the approval gate — the one thing standing between a model and an
|
|
10
|
-
* irreversible delete — came to have tests that never ran in CI.
|
|
4
|
+
* Tool calls throw MonnetApiError(401) when no credential can be found, and
|
|
5
|
+
* a suite that stubs `fetch` still goes through that check. Setting a key
|
|
6
|
+
* here keeps the tests exercising the tools instead of the missing-credential
|
|
7
|
+
* path (which has its own test in client.test.ts).
|
|
11
8
|
*
|
|
12
9
|
* `??=` so a real key in the environment still wins; nothing here reaches the
|
|
13
10
|
* network anyway, since every test stubs `fetch`.
|
package/dist/test-env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-env.js","sourceRoot":"","sources":["../src/test-env.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"test-env.js","sourceRoot":"","sources":["../src/test-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,yBAAyB,CAAC"}
|
|
@@ -1,20 +1,38 @@
|
|
|
1
1
|
import { apiFetch } from "../client.js";
|
|
2
2
|
export async function handleListWorkspaces() {
|
|
3
3
|
const data = await apiFetch("/workspaces/sidebar");
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
const invitations = data.pending_invitations || [];
|
|
5
|
+
// An invitation is the answer to "does my team already have a workspace?",
|
|
6
|
+
// and the sidebar has carried it all along. Reporting memberships only told
|
|
7
|
+
// an invited teammate they had nothing — which is how a second workspace got
|
|
8
|
+
// created next to the one that was waiting for them.
|
|
9
|
+
if (data.workspaces.length === 0 && invitations.length === 0) {
|
|
10
|
+
return "You are not a member of any Monnet workspace, and nothing is waiting for you.";
|
|
6
11
|
}
|
|
7
|
-
const lines = [
|
|
8
|
-
|
|
9
|
-
lines.push(
|
|
12
|
+
const lines = [];
|
|
13
|
+
if (data.workspaces.length > 0) {
|
|
14
|
+
lines.push("## Your workspaces", "");
|
|
15
|
+
for (const ws of data.workspaces) {
|
|
16
|
+
lines.push(`- **${ws.name}** (slug: \`${ws.slug}\`)`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (invitations.length > 0) {
|
|
20
|
+
if (lines.length > 0)
|
|
21
|
+
lines.push("");
|
|
22
|
+
lines.push("## Waiting for you", "");
|
|
23
|
+
for (const inv of invitations) {
|
|
24
|
+
lines.push(`- **${inv.workspace_name}** (slug: \`${inv.workspace_slug}\`) — invited, not joined yet. ` +
|
|
25
|
+
`Accept with \`npx -y @monnet/mcp join-workspace ${inv.token}\`.`);
|
|
26
|
+
}
|
|
10
27
|
}
|
|
11
28
|
return lines.join("\n");
|
|
12
29
|
}
|
|
13
30
|
export const LIST_WORKSPACES_TOOL_DEFINITION = {
|
|
14
31
|
name: "list_workspaces",
|
|
15
|
-
description: "List
|
|
16
|
-
"needed by other tools (get_motion,
|
|
17
|
-
"Call this first if you don't know the workspace slug
|
|
32
|
+
description: "List the Monnet workspaces you are a member of, plus any you have been invited to and not " +
|
|
33
|
+
"joined yet. Returns the workspace name and slug needed by other tools (get_motion, " +
|
|
34
|
+
"list_motions, create_thread, etc.). Call this first if you don't know the workspace slug — " +
|
|
35
|
+
"and before creating one, since an invitation means the team already has a workspace.",
|
|
18
36
|
inputSchema: {
|
|
19
37
|
type: "object",
|
|
20
38
|
properties: {},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-workspaces.js","sourceRoot":"","sources":["../../src/tools/list-workspaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"list-workspaces.js","sourceRoot":"","sources":["../../src/tools/list-workspaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAmBxC,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAkB,qBAAqB,CAAC,CAAC;IACpE,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,IAAI,EAAE,CAAC;IAEnD,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7E,qDAAqD;IACrD,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7D,OAAO,+EAA+E,CAAC;IACzF,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;QACrC,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,eAAe,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IACD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;QACrC,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CACR,OAAO,GAAG,CAAC,cAAc,eAAe,GAAG,CAAC,cAAc,iCAAiC;gBACzF,mDAAmD,GAAG,CAAC,KAAK,KAAK,CACpE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,4FAA4F;QAC5F,qFAAqF;QACrF,6FAA6F;QAC7F,sFAAsF;IACxF,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE,EAAE;QACd,QAAQ,EAAE,EAAE;KACb;CACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monnet/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"mcpName": "ai.monnet/mcp",
|
|
5
|
-
"description": "Model Context Protocol server for Monnet
|
|
5
|
+
"description": "Model Context Protocol server for Monnet \u2014 exposes motions, plans, and approvals to MCP-compatible clients like Claude Desktop and Cursor.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
12
|
"bin": {
|
|
13
|
-
"monnet-mcp": "dist/
|
|
13
|
+
"monnet-mcp": "dist/bin.js"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
|
-
"README.md"
|
|
17
|
+
"README.md",
|
|
18
|
+
"assets"
|
|
18
19
|
],
|
|
19
20
|
"scripts": {
|
|
20
21
|
"build": "tsc -p .",
|