@mootup/moot-cli 0.1.0-rc.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +37 -30
  2. package/dist/auth/archetypes.d.ts +10 -0
  3. package/dist/auth/archetypes.d.ts.map +1 -0
  4. package/dist/auth/archetypes.js +39 -0
  5. package/dist/auth/archetypes.js.map +1 -0
  6. package/dist/auth/credentials.d.ts +21 -0
  7. package/dist/auth/credentials.d.ts.map +1 -0
  8. package/dist/auth/credentials.js +97 -0
  9. package/dist/auth/credentials.js.map +1 -0
  10. package/dist/auth/oauth.d.ts +42 -0
  11. package/dist/auth/oauth.d.ts.map +1 -0
  12. package/dist/auth/oauth.js +200 -0
  13. package/dist/auth/oauth.js.map +1 -0
  14. package/dist/auth/profile.d.ts +3 -0
  15. package/dist/auth/profile.d.ts.map +1 -0
  16. package/dist/auth/profile.js +7 -0
  17. package/dist/auth/profile.js.map +1 -0
  18. package/dist/bin.js +21 -3
  19. package/dist/bin.js.map +1 -1
  20. package/dist/commands/init.d.ts +7 -0
  21. package/dist/commands/init.d.ts.map +1 -1
  22. package/dist/commands/init.js +261 -69
  23. package/dist/commands/init.js.map +1 -1
  24. package/dist/commands/login.d.ts +1 -0
  25. package/dist/commands/login.d.ts.map +1 -1
  26. package/dist/commands/login.js +5 -1
  27. package/dist/commands/login.js.map +1 -1
  28. package/dist/commands/logout.d.ts +7 -0
  29. package/dist/commands/logout.d.ts.map +1 -0
  30. package/dist/commands/logout.js +42 -0
  31. package/dist/commands/logout.js.map +1 -0
  32. package/dist/commands/refresh.d.ts +6 -0
  33. package/dist/commands/refresh.d.ts.map +1 -0
  34. package/dist/commands/refresh.js +53 -0
  35. package/dist/commands/refresh.js.map +1 -0
  36. package/dist/credential.d.ts +6 -0
  37. package/dist/credential.d.ts.map +1 -1
  38. package/dist/credential.js +30 -1
  39. package/dist/credential.js.map +1 -1
  40. package/dist/docker.js +1 -1
  41. package/dist/docker.js.map +1 -1
  42. package/dist/harness/claude-code.d.ts +37 -0
  43. package/dist/harness/claude-code.d.ts.map +1 -0
  44. package/dist/harness/claude-code.js +66 -0
  45. package/dist/harness/claude-code.js.map +1 -0
  46. package/dist/harness/cursor-agent.d.ts +3 -0
  47. package/dist/harness/cursor-agent.d.ts.map +1 -0
  48. package/dist/harness/cursor-agent.js +17 -0
  49. package/dist/harness/cursor-agent.js.map +1 -0
  50. package/dist/harness/cursor-ide.d.ts +10 -0
  51. package/dist/harness/cursor-ide.d.ts.map +1 -0
  52. package/dist/harness/cursor-ide.js +65 -0
  53. package/dist/harness/cursor-ide.js.map +1 -0
  54. package/dist/harness/index.d.ts +16 -0
  55. package/dist/harness/index.d.ts.map +1 -0
  56. package/dist/harness/index.js +65 -0
  57. package/dist/harness/index.js.map +1 -0
  58. package/dist/harness/sdk.d.ts +7 -0
  59. package/dist/harness/sdk.d.ts.map +1 -0
  60. package/dist/harness/sdk.js +18 -0
  61. package/dist/harness/sdk.js.map +1 -0
  62. package/dist/index.d.ts +12 -1
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +12 -1
  65. package/dist/index.js.map +1 -1
  66. package/package.json +11 -3
package/README.md CHANGED
@@ -1,21 +1,28 @@
1
1
  # @mootup/moot-cli
2
2
 
3
- Host-side operator CLI for the Moot agent team workflow. Bin name: `mootup`.
3
+ Host-side operator CLI for the Moot agent team workflow. Bin name: `moot`.
4
4
 
5
5
  ## Install
6
6
 
7
- One-off via `npx`:
7
+ ### Recommended: user-local install (no sudo)
8
8
 
9
- npx @mootup/moot-cli login
10
- npx @mootup/moot-cli init
9
+ npm i -g --prefix ~/.local @mootup/moot-cli
10
+ moot --version
11
+
12
+ Most Ubuntu/Debian systems already have `~/.local/bin` in PATH. If `moot --version` is not found after install, add the export to `~/.bashrc`:
13
+
14
+ export PATH="$HOME/.local/bin:$PATH"
11
15
 
12
- Or global install:
16
+ ### Alternative: system-wide install (requires sudo)
13
17
 
14
- npm i -g @mootup/moot-cli
15
- mootup login
16
- mootup init
18
+ sudo npm i -g @mootup/moot-cli
19
+ moot --version
20
+
21
+ ### Ephemeral: npx (no install)
22
+
23
+ npx @mootup/moot-cli login
17
24
 
18
- Requires Node ≥ 20, Docker, and [`@devcontainers/cli`](https://github.com/devcontainers/cli) on `PATH`:
25
+ Requires Node ≥ 18, Docker, and [`@devcontainers/cli`](https://github.com/devcontainers/cli) on PATH:
19
26
 
20
27
  npm i -g @devcontainers/cli
21
28
 
@@ -24,41 +31,41 @@ Requires Node ≥ 20, Docker, and [`@devcontainers/cli`](https://github.com/devc
24
31
  ```bash
25
32
  # 1. Create a personal access token at https://mootup.io/settings/api-keys
26
33
  # 2. Authenticate (stored under ~/.mootup/credentials.json, mode 0600):
27
- mootup login
34
+ moot login
28
35
 
29
36
  # 3. Provision actors and install .devcontainer/ in your repo:
30
37
  cd my-project
31
- mootup init
38
+ moot init
32
39
 
33
40
  # 4. Bring the devcontainer up and start the agent team:
34
- mootup up
41
+ moot up
35
42
 
36
43
  # 5. Inspect, attach, compact as needed:
37
- mootup status
38
- mootup attach leader
39
- mootup compact spec
44
+ moot status
45
+ moot attach leader
46
+ moot compact spec
40
47
 
41
48
  # 6. Stop everything:
42
- mootup down
49
+ moot down
43
50
  ```
44
51
 
45
52
  ## Command reference
46
53
 
47
54
  | Command | Runs | Delegates to |
48
55
  |---|---|---|
49
- | `mootup login [--token <pat>] [--api-url <url>]` | host | writes `~/.mootup/credentials.json` |
50
- | `mootup init [--force] [--yes] [--api-url <url>]` | host | rotates actor keys, writes `.moot/actors.json`, copies `.devcontainer/` |
51
- | `mootup up` | host → container | `devcontainer up` + `docker exec <cid> moot up` |
52
- | `mootup down [role]` | container | `docker exec <cid> moot down [role]` |
53
- | `mootup status` | container | `docker exec <cid> moot status` |
54
- | `mootup attach <role>` | container | `docker exec -it <cid> moot attach <role>` |
55
- | `mootup compact [role]` | container | `docker exec <cid> moot compact [role]` |
56
+ | `moot login [--token <pat>] [--api-url <url>]` | host | writes `~/.mootup/credentials.json` |
57
+ | `moot init [--force] [--yes] [--api-url <url>]` | host | rotates actor keys, writes `.moot/actors.json`, copies `.devcontainer/` |
58
+ | `moot up` | host → container | `devcontainer up` + `docker exec <cid> moot up` |
59
+ | `moot down [role]` | container | `docker exec <cid> moot down [role]` |
60
+ | `moot status` | container | `docker exec <cid> moot status` |
61
+ | `moot attach <role>` | container | `docker exec -it <cid> moot attach <role>` |
62
+ | `moot compact [role]` | container | `docker exec <cid> moot compact [role]` |
56
63
 
57
- The `up`, `down`, `status`, `attach`, and `compact` commands look up the running container by the `devcontainer.local_folder` label the devcontainer CLI stamps on each container; no container → clear error prompting `mootup up`.
64
+ The `up`, `down`, `status`, `attach`, and `compact` commands look up the running container by the `devcontainer.local_folder` label the devcontainer CLI stamps on each container; no container → clear error prompting `moot up`.
58
65
 
59
66
  ## Scope vs the Python CLI
60
67
 
61
- `@mootup/moot-cli` covers only the host-side operator surface. The Python `moot` CLI inside the devcontainer remains canonical for in-container orchestration (tmux, MCP adapter, channel adapter, hooks, team profile). `mootup init` in v0.1.0-rc.0 installs `.moot/actors.json` + `.devcontainer/` only; skill / CLAUDE.md / hook bundle installation is tracked as a follow-up run (v0.2.0).
68
+ `@mootup/moot-cli` covers only the host-side operator surface. The Python `moot` CLI inside the devcontainer remains canonical for in-container orchestration (tmux, MCP adapter, channel adapter, hooks, team profile). `moot init` in v0.1.0 installs `.moot/actors.json` + `.devcontainer/` only; skill / CLAUDE.md / hook bundle installation is tracked as a follow-up run.
62
69
 
63
70
  ## Manual smoke test (operator)
64
71
 
@@ -66,11 +73,11 @@ Run from a fresh test directory after logging in:
66
73
 
67
74
  mkdir /tmp/mootup-smoke && cd /tmp/mootup-smoke
68
75
  git init
69
- mootup init
76
+ moot init
70
77
  test -f .moot/actors.json && echo "actors.json ✓"
71
78
  test -d .devcontainer && echo "devcontainer ✓"
72
- mootup up
73
- mootup status
74
- mootup down
79
+ moot up
80
+ moot status
81
+ moot down
75
82
 
76
- `mootup init` hits the authenticated `/api/actors/me`, `/api/actors/me/agents`, `/api/actors/{id}/rotate-key` endpoints against the API URL stored at login time. A reachable backend is required.
83
+ `moot init` hits the authenticated `/api/actors/me`, `/api/actors/me/agents`, `/api/actors/{id}/rotate-key` endpoints against the API URL stored at login time. A reachable backend is required.
@@ -0,0 +1,10 @@
1
+ export interface ArchetypeEntry {
2
+ id: string;
3
+ version: string;
4
+ description: string;
5
+ }
6
+ export declare const ARCHETYPE_CATALOG: readonly ArchetypeEntry[];
7
+ export declare const DEFAULT_ARCHETYPE = "mootup/loop-6";
8
+ export declare function findArchetype(id: string): ArchetypeEntry | null;
9
+ export declare function promptArchetype(prompt?: (q: string) => Promise<string>): Promise<ArchetypeEntry>;
10
+ //# sourceMappingURL=archetypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archetypes.d.ts","sourceRoot":"","sources":["../../src/auth/archetypes.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAO7C,CAAC;AAEX,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAE/D;AAED,wBAAsB,eAAe,CACnC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GACtC,OAAO,CAAC,cAAc,CAAC,CAkBzB"}
@@ -0,0 +1,39 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ export const ARCHETYPE_CATALOG = [
3
+ { id: 'mootup/loop-6', version: '1.0', description: 'Full pipeline (product/leader/spec/impl/qa/librarian)' },
4
+ { id: 'mootup/loop-4', version: '1.0', description: 'Core pipeline (product/spec/impl/qa)' },
5
+ { id: 'mootup/loop-4-observer', version: '1.0', description: 'Core + librarian' },
6
+ { id: 'mootup/loop-4-parallel', version: '1.0', description: 'Core + parallel impl-a/impl-b' },
7
+ { id: 'mootup/loop-4-split-leader', version: '1.0', description: 'Core + dedicated leader' },
8
+ { id: 'mootup/loop-3', version: '1.0', description: 'Minimal (leader/impl/qa)' },
9
+ ];
10
+ export const DEFAULT_ARCHETYPE = 'mootup/loop-6';
11
+ export function findArchetype(id) {
12
+ return ARCHETYPE_CATALOG.find((a) => a.id === id) ?? null;
13
+ }
14
+ export async function promptArchetype(prompt) {
15
+ const ask = prompt ?? defaultPrompt;
16
+ console.log('Available team archetypes:');
17
+ ARCHETYPE_CATALOG.forEach((a, i) => {
18
+ const marker = a.id === DEFAULT_ARCHETYPE ? ' (default)' : '';
19
+ console.log(` ${i + 1}. ${a.id}${marker} — ${a.description}`);
20
+ });
21
+ const answer = (await ask(`Select archetype [1-${ARCHETYPE_CATALOG.length}, default=${DEFAULT_ARCHETYPE}]: `)).trim();
22
+ if (!answer)
23
+ return findArchetype(DEFAULT_ARCHETYPE);
24
+ const idx = Number.parseInt(answer, 10);
25
+ if (Number.isFinite(idx) && idx >= 1 && idx <= ARCHETYPE_CATALOG.length) {
26
+ return ARCHETYPE_CATALOG[idx - 1];
27
+ }
28
+ const byId = findArchetype(answer);
29
+ if (byId)
30
+ return byId;
31
+ throw new Error(`Unknown archetype: ${answer}`);
32
+ }
33
+ async function defaultPrompt(q) {
34
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
35
+ const answer = await rl.question(q);
36
+ rl.close();
37
+ return answer;
38
+ }
39
+ //# sourceMappingURL=archetypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archetypes.js","sourceRoot":"","sources":["../../src/auth/archetypes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAQzD,MAAM,CAAC,MAAM,iBAAiB,GAA8B;IAC1D,EAAE,EAAE,EAAE,eAAe,EAAe,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,uDAAuD,EAAE;IAC1H,EAAE,EAAE,EAAE,eAAe,EAAe,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,sCAAsC,EAAE;IACzG,EAAE,EAAE,EAAE,wBAAwB,EAAM,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE;IACrF,EAAE,EAAE,EAAE,wBAAwB,EAAM,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,+BAA+B,EAAE;IAClG,EAAE,EAAE,EAAE,4BAA4B,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,yBAAyB,EAAE;IAC5F,EAAE,EAAE,EAAE,eAAe,EAAe,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,0BAA0B,EAAE;CACrF,CAAC;AAEX,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAEjD,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAuC;IAEvC,MAAM,GAAG,GAAG,MAAM,IAAI,aAAa,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC1C,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,KAAK,iBAAiB,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,MAAM,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,CACb,MAAM,GAAG,CAAC,uBAAuB,iBAAiB,CAAC,MAAM,aAAa,iBAAiB,KAAK,CAAC,CAC9F,CAAC,IAAI,EAAE,CAAC;IACT,IAAI,CAAC,MAAM;QAAE,OAAO,aAAa,CAAC,iBAAiB,CAAE,CAAC;IACtD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC;QACxE,OAAO,iBAAiB,CAAC,GAAG,GAAG,CAAC,CAAE,CAAC;IACrC,CAAC;IACD,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,IAAI,KAAK,CAAC,sBAAsB,MAAM,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,CAAS;IACpC,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpC,EAAE,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,21 @@
1
+ export declare const KEYTAR_SERVICE = "mootup-cli";
2
+ export interface OAuthCredentialBundle {
3
+ api_url: string;
4
+ user_id: string;
5
+ access_token: string;
6
+ refresh_token: string;
7
+ access_token_expires_at: number;
8
+ installation_id?: string;
9
+ }
10
+ type KeytarLike = {
11
+ setPassword: (service: string, account: string, password: string) => Promise<void>;
12
+ getPassword: (service: string, account: string) => Promise<string | null>;
13
+ deletePassword: (service: string, account: string) => Promise<boolean>;
14
+ };
15
+ export declare function __setKeytarForTest(k: KeytarLike | null): void;
16
+ export declare function storeOAuthCredential(profile: string, creds: OAuthCredentialBundle): Promise<void>;
17
+ export declare function loadRefreshToken(profile: string): Promise<string | null>;
18
+ export declare function deleteOAuthCredential(profile: string): Promise<void>;
19
+ export declare function __clearSessionMemoryForTest(): void;
20
+ export {};
21
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/auth/credentials.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAID,KAAK,UAAU,GAAG;IAChB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1E,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxE,CAAC;AAIF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAE7D;AAoBD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAYD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAY9E;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc1E;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAElD"}
@@ -0,0 +1,97 @@
1
+ import { loadCredential, storeCredential, deleteCredential, } from '../credential.js';
2
+ export const KEYTAR_SERVICE = 'mootup-cli';
3
+ const sessionRefreshTokenMemory = new Map();
4
+ let keytarOverride = null;
5
+ export function __setKeytarForTest(k) {
6
+ keytarOverride = k;
7
+ }
8
+ async function loadKeytar() {
9
+ if (keytarOverride)
10
+ return keytarOverride;
11
+ try {
12
+ // keytar is an optionalDependency; dynamic import keeps it optional at build time.
13
+ const modName = 'keytar';
14
+ const mod = (await import(/* @vite-ignore */ modName));
15
+ return mod.default ?? mod;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ function keychainRef(profile) {
22
+ return `${KEYTAR_SERVICE}:${profile}:refresh`;
23
+ }
24
+ export async function storeOAuthCredential(profile, creds) {
25
+ const keytar = await loadKeytar();
26
+ let refresh_token_ref;
27
+ if (keytar) {
28
+ const ref = keychainRef(profile);
29
+ try {
30
+ await keytar.setPassword(KEYTAR_SERVICE, ref, creds.refresh_token);
31
+ refresh_token_ref = ref;
32
+ }
33
+ catch (err) {
34
+ printKeychainFallback(err);
35
+ sessionRefreshTokenMemory.set(profile, creds.refresh_token);
36
+ refresh_token_ref = undefined;
37
+ }
38
+ }
39
+ else {
40
+ printKeychainFallback(new Error('keytar module not installed'));
41
+ sessionRefreshTokenMemory.set(profile, creds.refresh_token);
42
+ }
43
+ const cred = {
44
+ api_url: creds.api_url,
45
+ token: creds.access_token,
46
+ user_id: creds.user_id,
47
+ credential_type: 'oauth',
48
+ access_token_expires_at: creds.access_token_expires_at,
49
+ };
50
+ if (refresh_token_ref !== undefined)
51
+ cred.refresh_token_ref = refresh_token_ref;
52
+ if (creds.installation_id !== undefined)
53
+ cred.installation_id = creds.installation_id;
54
+ storeCredential(cred, profile);
55
+ }
56
+ function printKeychainFallback(err) {
57
+ const msg = err instanceof Error ? err.message : String(err);
58
+ const filePath = `${process.env.HOME ?? '~'}/.mootup/credentials.json`;
59
+ console.error(`note: keychain unavailable (${msg}); refresh-token lives in memory ` +
60
+ `for this session only. Re-authenticate on next session. ` +
61
+ `(using file-based storage at ${filePath} for non-secret fields only)`);
62
+ }
63
+ export async function loadRefreshToken(profile) {
64
+ const cred = loadCredential(profile);
65
+ if (!cred || cred.credential_type !== 'oauth' || !cred.refresh_token_ref) {
66
+ return sessionRefreshTokenMemory.get(profile) ?? null;
67
+ }
68
+ const keytar = await loadKeytar();
69
+ if (!keytar)
70
+ return sessionRefreshTokenMemory.get(profile) ?? null;
71
+ try {
72
+ return await keytar.getPassword(KEYTAR_SERVICE, cred.refresh_token_ref);
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ export async function deleteOAuthCredential(profile) {
79
+ const cred = loadCredential(profile);
80
+ if (cred?.refresh_token_ref) {
81
+ const keytar = await loadKeytar();
82
+ if (keytar) {
83
+ try {
84
+ await keytar.deletePassword(KEYTAR_SERVICE, cred.refresh_token_ref);
85
+ }
86
+ catch {
87
+ // best-effort
88
+ }
89
+ }
90
+ }
91
+ sessionRefreshTokenMemory.delete(profile);
92
+ deleteCredential(profile);
93
+ }
94
+ export function __clearSessionMemoryForTest() {
95
+ sessionRefreshTokenMemory.clear();
96
+ }
97
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/auth/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ export interface OAuthBrowserFlowOptions {
2
+ apiUrl: string;
3
+ clientId?: string;
4
+ scope?: string;
5
+ fetchImpl?: typeof globalThis.fetch;
6
+ openImpl?: (url: string) => Promise<void>;
7
+ waitForCallbackImpl?: (expectedState: string) => Promise<string>;
8
+ }
9
+ export interface OAuthTokenResponse {
10
+ access_token: string;
11
+ refresh_token: string;
12
+ expires_in: number;
13
+ token_type: string;
14
+ }
15
+ export interface BrowserFlowResult {
16
+ access_token: string;
17
+ refresh_token: string;
18
+ access_token_expires_at: number;
19
+ token_type: string;
20
+ }
21
+ export declare function shouldUseBrowser(): boolean;
22
+ export declare function base64UrlEncode(buf: Buffer): string;
23
+ export declare function mintPkcePair(): {
24
+ verifier: string;
25
+ challenge: string;
26
+ };
27
+ export declare function mintState(): string;
28
+ export declare function generateIdempotencyKey(): string;
29
+ export declare function runBrowserFlow(opts: OAuthBrowserFlowOptions): Promise<BrowserFlowResult>;
30
+ export declare function refreshAccessToken(opts: {
31
+ apiUrl: string;
32
+ refreshToken: string;
33
+ clientId?: string;
34
+ fetchImpl?: typeof globalThis.fetch;
35
+ }): Promise<BrowserFlowResult>;
36
+ export declare function revokeRefreshToken(opts: {
37
+ apiUrl: string;
38
+ refreshToken: string;
39
+ clientId?: string;
40
+ fetchImpl?: typeof globalThis.fetch;
41
+ }): Promise<void>;
42
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,mBAAmB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAClE;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,MAAM,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAI1C;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,YAAY,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAItE;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CA+D5B;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAuB7B;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhB"}
@@ -0,0 +1,200 @@
1
+ import { createHash, randomBytes, randomUUID } from 'node:crypto';
2
+ import { createServer } from 'node:net';
3
+ import { spawn } from 'node:child_process';
4
+ export function shouldUseBrowser() {
5
+ if (process.env.MOOTUP_FORCE_DEVICE_CODE === '1')
6
+ return false;
7
+ if (process.platform === 'darwin' || process.platform === 'win32')
8
+ return true;
9
+ return Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
10
+ }
11
+ export function base64UrlEncode(buf) {
12
+ return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
13
+ }
14
+ export function mintPkcePair() {
15
+ const verifier = base64UrlEncode(randomBytes(32));
16
+ const challenge = base64UrlEncode(createHash('sha256').update(verifier).digest());
17
+ return { verifier, challenge };
18
+ }
19
+ export function mintState() {
20
+ return randomBytes(16).toString('hex');
21
+ }
22
+ export function generateIdempotencyKey() {
23
+ return randomUUID();
24
+ }
25
+ export async function runBrowserFlow(opts) {
26
+ const clientId = opts.clientId ?? 'mootup-cli';
27
+ const scope = opts.scope ?? 'team:install';
28
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
29
+ const openFn = opts.openImpl ?? openBrowser;
30
+ const { verifier, challenge } = mintPkcePair();
31
+ const state = mintState();
32
+ let port;
33
+ let waitForCallback;
34
+ if (opts.waitForCallbackImpl) {
35
+ port = 0;
36
+ waitForCallback = opts.waitForCallbackImpl;
37
+ }
38
+ else {
39
+ const listener = await startCallbackListener(state);
40
+ port = listener.port;
41
+ waitForCallback = listener.wait;
42
+ }
43
+ const redirectUri = `http://localhost:${port}/callback`;
44
+ const authorizeUrl = `${opts.apiUrl}/oauth/authorize?response_type=code&client_id=${encodeURIComponent(clientId)}` +
45
+ `&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${encodeURIComponent(scope)}` +
46
+ `&state=${state}&code_challenge=${challenge}&code_challenge_method=S256`;
47
+ console.log(`Opening browser for authorization: ${authorizeUrl}`);
48
+ try {
49
+ await openFn(authorizeUrl);
50
+ }
51
+ catch (err) {
52
+ const msg = err instanceof Error ? err.message : String(err);
53
+ console.error(`Could not launch browser (${msg}). Open the URL above manually.`);
54
+ }
55
+ const code = await waitForCallback(state);
56
+ const tokenRes = await fetchImpl(`${opts.apiUrl}/oauth/token`, {
57
+ method: 'POST',
58
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
59
+ body: new URLSearchParams({
60
+ grant_type: 'authorization_code',
61
+ code,
62
+ code_verifier: verifier,
63
+ redirect_uri: redirectUri,
64
+ client_id: clientId,
65
+ }).toString(),
66
+ });
67
+ if (tokenRes.status !== 200) {
68
+ const body = await tokenRes.text();
69
+ throw new Error(`/oauth/token exchange failed (${tokenRes.status}): ${body}`);
70
+ }
71
+ const body = (await tokenRes.json());
72
+ if (!body.access_token || !body.refresh_token) {
73
+ throw new Error('/oauth/token response missing access_token or refresh_token');
74
+ }
75
+ return {
76
+ access_token: body.access_token,
77
+ refresh_token: body.refresh_token,
78
+ access_token_expires_at: Math.floor(Date.now() / 1000) + (body.expires_in ?? 0),
79
+ token_type: body.token_type ?? 'Bearer',
80
+ };
81
+ }
82
+ export async function refreshAccessToken(opts) {
83
+ const clientId = opts.clientId ?? 'mootup-cli';
84
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
85
+ const res = await fetchImpl(`${opts.apiUrl}/oauth/token`, {
86
+ method: 'POST',
87
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
88
+ body: new URLSearchParams({
89
+ grant_type: 'refresh_token',
90
+ refresh_token: opts.refreshToken,
91
+ client_id: clientId,
92
+ }).toString(),
93
+ });
94
+ if (res.status !== 200) {
95
+ const body = await res.text();
96
+ throw new Error(`/oauth/token refresh failed (${res.status}): ${body}`);
97
+ }
98
+ const body = (await res.json());
99
+ return {
100
+ access_token: body.access_token,
101
+ refresh_token: body.refresh_token,
102
+ access_token_expires_at: Math.floor(Date.now() / 1000) + (body.expires_in ?? 0),
103
+ token_type: body.token_type ?? 'Bearer',
104
+ };
105
+ }
106
+ export async function revokeRefreshToken(opts) {
107
+ const clientId = opts.clientId ?? 'mootup-cli';
108
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
109
+ await fetchImpl(`${opts.apiUrl}/oauth/revoke`, {
110
+ method: 'POST',
111
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
112
+ body: new URLSearchParams({
113
+ token: opts.refreshToken,
114
+ client_id: clientId,
115
+ }).toString(),
116
+ });
117
+ }
118
+ async function openBrowser(url) {
119
+ const cmd = process.platform === 'darwin'
120
+ ? 'open'
121
+ : process.platform === 'win32'
122
+ ? 'start'
123
+ : 'xdg-open';
124
+ const child = spawn(cmd, [url], { stdio: 'ignore', detached: true, shell: process.platform === 'win32' });
125
+ child.unref();
126
+ }
127
+ async function startCallbackListener(expectedState) {
128
+ let server;
129
+ let resolveWait;
130
+ let rejectWait;
131
+ const waitPromise = new Promise((resolve, reject) => {
132
+ resolveWait = resolve;
133
+ rejectWait = reject;
134
+ });
135
+ server = createServer((socket) => {
136
+ let buf = '';
137
+ socket.on('data', (chunk) => {
138
+ buf += chunk.toString('utf8');
139
+ const headerEnd = buf.indexOf('\r\n\r\n');
140
+ if (headerEnd === -1)
141
+ return;
142
+ const firstLine = buf.slice(0, buf.indexOf('\r\n'));
143
+ const match = firstLine.match(/^GET\s+(\S+)\s+HTTP/);
144
+ if (!match) {
145
+ socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
146
+ return;
147
+ }
148
+ const pathAndQuery = match[1];
149
+ const url = new URL(pathAndQuery, 'http://localhost');
150
+ const gotState = url.searchParams.get('state');
151
+ const gotCode = url.searchParams.get('code');
152
+ const gotError = url.searchParams.get('error');
153
+ if (gotError) {
154
+ sendResponse(socket, 400, `OAuth error: ${gotError}. You can close this window.`);
155
+ rejectWait(new Error(`OAuth error: ${gotError}`));
156
+ return;
157
+ }
158
+ if (!gotState || !gotCode || gotState !== expectedState) {
159
+ sendResponse(socket, 400, 'OAuth state mismatch. You can close this window.');
160
+ rejectWait(new Error('OAuth state mismatch'));
161
+ return;
162
+ }
163
+ sendResponse(socket, 200, 'Authorization received. You can close this window.');
164
+ resolveWait(gotCode);
165
+ });
166
+ socket.on('error', () => {
167
+ // ignore
168
+ });
169
+ });
170
+ await new Promise((resolve, reject) => {
171
+ server.once('error', reject);
172
+ server.listen(0, '127.0.0.1', resolve);
173
+ });
174
+ const address = server.address();
175
+ const port = typeof address === 'object' && address ? address.port : 0;
176
+ return {
177
+ port,
178
+ wait: async (expected) => {
179
+ try {
180
+ const code = await waitPromise;
181
+ return code;
182
+ }
183
+ finally {
184
+ server.close();
185
+ }
186
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
187
+ void expected;
188
+ },
189
+ };
190
+ }
191
+ function sendResponse(socket, status, body) {
192
+ const reason = status === 200 ? 'OK' : 'Bad Request';
193
+ socket.end(`HTTP/1.1 ${status} ${reason}\r\n` +
194
+ 'Content-Type: text/plain; charset=utf-8\r\n' +
195
+ `Content-Length: ${Buffer.byteLength(body)}\r\n` +
196
+ 'Connection: close\r\n' +
197
+ '\r\n' +
198
+ body);
199
+ }
200
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,YAAY,EAA4B,MAAM,UAAU,CAAC;AAClE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAyB3C,MAAM,UAAU,gBAAgB;IAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/D,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAC/E,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,QAAQ,GAAG,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,eAAe,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAClF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,UAAU,EAAE,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAA6B;IAE7B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,cAAc,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC;IAE5C,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,YAAY,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAE1B,IAAI,IAAY,CAAC;IACjB,IAAI,eAA2D,CAAC;IAChE,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,CAAC;QACT,eAAe,GAAG,IAAI,CAAC,mBAAmB,CAAC;IAC7C,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;QACrB,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC;IAClC,CAAC;IAED,MAAM,WAAW,GAAG,oBAAoB,IAAI,WAAW,CAAC;IACxD,MAAM,YAAY,GAChB,GAAG,IAAI,CAAC,MAAM,iDAAiD,kBAAkB,CAAC,QAAQ,CAAC,EAAE;QAC7F,iBAAiB,kBAAkB,CAAC,WAAW,CAAC,UAAU,kBAAkB,CAAC,KAAK,CAAC,EAAE;QACrF,UAAU,KAAK,mBAAmB,SAAS,6BAA6B,CAAC;IAE3E,OAAO,CAAC,GAAG,CAAC,sCAAsC,YAAY,EAAE,CAAC,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CACX,6BAA6B,GAAG,iCAAiC,CAClE,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM,cAAc,EAAE;QAC7D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,eAAe,CAAC;YACxB,UAAU,EAAE,oBAAoB;YAChC,IAAI;YACJ,aAAa,EAAE,QAAQ;YACvB,YAAY,EAAE,WAAW;YACzB,SAAS,EAAE,QAAQ;SACpB,CAAC,CAAC,QAAQ,EAAE;KACd,CAAC,CAAC;IACH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAC;IAC3D,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,OAAO;QACL,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,uBAAuB,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QAC/E,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,QAAQ;KACxC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAKxC;IACC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACrD,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM,cAAc,EAAE;QACxD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,eAAe,CAAC;YACxB,UAAU,EAAE,eAAe;YAC3B,aAAa,EAAE,IAAI,CAAC,YAAY;YAChC,SAAS,EAAE,QAAQ;SACpB,CAAC,CAAC,QAAQ,EAAE;KACd,CAAC,CAAC;IACH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;IACtD,OAAO;QACL,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,uBAAuB,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QAC/E,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,QAAQ;KACxC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAKxC;IACC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACrD,MAAM,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM,eAAe,EAAE;QAC7C,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,eAAe,CAAC;YACxB,KAAK,EAAE,IAAI,CAAC,YAAY;YACxB,SAAS,EAAE,QAAQ;SACpB,CAAC,CAAC,QAAQ,EAAE;KACd,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,GAAW;IACpC,MAAM,GAAG,GACP,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3B,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,UAAU,CAAC;IACnB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;IAC1G,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AAOD,KAAK,UAAU,qBAAqB,CAAC,aAAqB;IACxD,IAAI,MAAc,CAAC;IACnB,IAAI,WAAmC,CAAC;IACxC,IAAI,UAAgC,CAAC;IACrC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1D,WAAW,GAAG,OAAO,CAAC;QACtB,UAAU,GAAG,MAAM,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,MAAM,GAAG,YAAY,CAAC,CAAC,MAAc,EAAE,EAAE;QACvC,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAC1B,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC9B,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC1C,IAAI,SAAS,KAAK,CAAC,CAAC;gBAAE,OAAO;YAC7B,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YACpD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACrD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;gBAC/C,OAAO;YACT,CAAC;YACD,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC;YACtD,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC/C,IAAI,QAAQ,EAAE,CAAC;gBACb,YAAY,CAAC,MAAM,EAAE,GAAG,EAAE,gBAAgB,QAAQ,8BAA8B,CAAC,CAAC;gBAClF,UAAU,CAAC,IAAI,KAAK,CAAC,gBAAgB,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YACD,IAAI,CAAC,QAAQ,IAAI,CAAC,OAAO,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;gBACxD,YAAY,CAAC,MAAM,EAAE,GAAG,EAAE,kDAAkD,CAAC,CAAC;gBAC9E,UAAU,CAAC,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;gBAC9C,OAAO;YACT,CAAC;YACD,YAAY,CAAC,MAAM,EAAE,GAAG,EAAE,oDAAoD,CAAC,CAAC;YAChF,WAAW,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,SAAS;QACX,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvE,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,KAAK,EAAE,QAAgB,EAAE,EAAE;YAC/B,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC;gBAC/B,OAAO,IAAI,CAAC;YACd,CAAC;oBAAS,CAAC;gBACT,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;YACD,6DAA6D;YAC7D,KAAK,QAAQ,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,MAAc,EAAE,MAAc,EAAE,IAAY;IAChE,MAAM,MAAM,GAAG,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC;IACrD,MAAM,CAAC,GAAG,CACR,YAAY,MAAM,IAAI,MAAM,MAAM;QAClC,6CAA6C;QAC7C,mBAAmB,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM;QAChD,uBAAuB;QACvB,MAAM;QACN,IAAI,CACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const PROFILE_RE: RegExp;
2
+ export declare function validateProfile(profile: string): void;
3
+ //# sourceMappingURL=profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/auth/profile.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,QAAkB,CAAC;AAE1C,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMrD"}
@@ -0,0 +1,7 @@
1
+ export const PROFILE_RE = /^[a-z0-9_-]+$/;
2
+ export function validateProfile(profile) {
3
+ if (!PROFILE_RE.test(profile)) {
4
+ throw new Error(`invalid profile name '${profile}' (must match ${PROFILE_RE})`);
5
+ }
6
+ }
7
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/auth/profile.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAE1C,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,yBAAyB,OAAO,iBAAiB,UAAU,GAAG,CAC/D,CAAC;IACJ,CAAC;AACH,CAAC"}
package/dist/bin.js CHANGED
@@ -7,24 +7,42 @@ import { cmdDown } from './commands/down.js';
7
7
  import { cmdStatus } from './commands/status.js';
8
8
  import { cmdAttach } from './commands/attach.js';
9
9
  import { cmdCompact } from './commands/compact.js';
10
+ import { cmdLogout } from './commands/logout.js';
11
+ import { cmdRefresh } from './commands/refresh.js';
10
12
  const program = new Command();
11
13
  program
12
- .name('mootup')
14
+ .name('moot')
13
15
  .description('Host-side operator CLI for the Moot agent team workflow')
14
- .version('0.1.0-rc.0');
16
+ .version('0.1.0');
15
17
  program
16
18
  .command('login')
17
19
  .description('Authenticate against mootup.io and store credential')
18
20
  .option('--token <pat>', 'Personal access token (prompts if omitted)')
19
21
  .option('--api-url <url>', 'Moot API URL', 'https://mootup.io')
22
+ .option('--profile <name>', 'Named profile (default "default")')
20
23
  .action((opts) => cmdLogin(opts));
21
24
  program
22
25
  .command('init')
23
- .description('Rotate actor keys, write .moot/actors.json, install .devcontainer/')
26
+ .description('Authenticate (OAuth), select harness + archetype, install team, write .moot/actors.json')
24
27
  .option('--force', 'Rotate keys for already-keyed agents (destructive)', false)
25
28
  .option('--yes', 'Skip all confirmation prompts', false)
26
29
  .option('--api-url <url>', 'Moot API URL (overrides stored credential)')
30
+ .option('--profile <name>', 'Named profile (default "default")')
31
+ .option('--archetype <id>', 'Archetype to install (skips prompt)')
32
+ .option('--harness <name>', 'Harness integration (claude-code, cursor-agent, cursor-ide, sdk)', 'claude-code')
33
+ .option('--show-token', 'For --harness sdk, print the full PAT plaintext', false)
27
34
  .action((opts) => cmdInit(opts));
35
+ program
36
+ .command('logout')
37
+ .description('Revoke OAuth installation and clear local credential')
38
+ .option('--profile <name>', 'Named profile (default "default")')
39
+ .option('--all', 'Revoke all installations (alias for logout; multi-install deferred)', false)
40
+ .action((opts) => cmdLogout(opts));
41
+ program
42
+ .command('refresh')
43
+ .description('Refresh the stored OAuth access token')
44
+ .option('--profile <name>', 'Named profile (default "default")')
45
+ .action((opts) => cmdRefresh(opts));
28
46
  program
29
47
  .command('up')
30
48
  .description('Bring the devcontainer up and start the agent team')