@acprompt/join 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/auth/browser-flow.d.ts +25 -0
  4. package/dist/auth/browser-flow.d.ts.map +1 -0
  5. package/dist/auth/browser-flow.js +113 -0
  6. package/dist/auth/browser-flow.js.map +1 -0
  7. package/dist/auth/paths.d.ts +13 -0
  8. package/dist/auth/paths.d.ts.map +1 -0
  9. package/dist/auth/paths.js +47 -0
  10. package/dist/auth/paths.js.map +1 -0
  11. package/dist/auth/token-store.d.ts +18 -0
  12. package/dist/auth/token-store.d.ts.map +1 -0
  13. package/dist/auth/token-store.js +51 -0
  14. package/dist/auth/token-store.js.map +1 -0
  15. package/dist/commands/detect.d.ts +2 -0
  16. package/dist/commands/detect.d.ts.map +1 -0
  17. package/dist/commands/detect.js +31 -0
  18. package/dist/commands/detect.js.map +1 -0
  19. package/dist/commands/install.d.ts +2 -0
  20. package/dist/commands/install.d.ts.map +1 -0
  21. package/dist/commands/install.js +92 -0
  22. package/dist/commands/install.js.map +1 -0
  23. package/dist/commands/verify.d.ts +2 -0
  24. package/dist/commands/verify.d.ts.map +1 -0
  25. package/dist/commands/verify.js +41 -0
  26. package/dist/commands/verify.js.map +1 -0
  27. package/dist/commands/whoami.d.ts +2 -0
  28. package/dist/commands/whoami.d.ts.map +1 -0
  29. package/dist/commands/whoami.js +34 -0
  30. package/dist/commands/whoami.js.map +1 -0
  31. package/dist/detect/anthropic-sdk.d.ts +3 -0
  32. package/dist/detect/anthropic-sdk.d.ts.map +1 -0
  33. package/dist/detect/anthropic-sdk.js +49 -0
  34. package/dist/detect/anthropic-sdk.js.map +1 -0
  35. package/dist/detect/claude-code.d.ts +4 -0
  36. package/dist/detect/claude-code.d.ts.map +1 -0
  37. package/dist/detect/claude-code.js +86 -0
  38. package/dist/detect/claude-code.js.map +1 -0
  39. package/dist/detect/crewai.d.ts +3 -0
  40. package/dist/detect/crewai.d.ts.map +1 -0
  41. package/dist/detect/crewai.js +42 -0
  42. package/dist/detect/crewai.js.map +1 -0
  43. package/dist/detect/deps.d.ts +10 -0
  44. package/dist/detect/deps.d.ts.map +1 -0
  45. package/dist/detect/deps.js +107 -0
  46. package/dist/detect/deps.js.map +1 -0
  47. package/dist/detect/generic.d.ts +3 -0
  48. package/dist/detect/generic.d.ts.map +1 -0
  49. package/dist/detect/generic.js +24 -0
  50. package/dist/detect/generic.js.map +1 -0
  51. package/dist/detect/index.d.ts +5 -0
  52. package/dist/detect/index.d.ts.map +1 -0
  53. package/dist/detect/index.js +27 -0
  54. package/dist/detect/index.js.map +1 -0
  55. package/dist/detect/langgraph.d.ts +3 -0
  56. package/dist/detect/langgraph.d.ts.map +1 -0
  57. package/dist/detect/langgraph.js +43 -0
  58. package/dist/detect/langgraph.js.map +1 -0
  59. package/dist/detect/letta.d.ts +5 -0
  60. package/dist/detect/letta.d.ts.map +1 -0
  61. package/dist/detect/letta.js +73 -0
  62. package/dist/detect/letta.js.map +1 -0
  63. package/dist/detect/openai-agents.d.ts +3 -0
  64. package/dist/detect/openai-agents.d.ts.map +1 -0
  65. package/dist/detect/openai-agents.js +42 -0
  66. package/dist/detect/openai-agents.js.map +1 -0
  67. package/dist/detect/types.d.ts +36 -0
  68. package/dist/detect/types.d.ts.map +1 -0
  69. package/dist/detect/types.js +8 -0
  70. package/dist/detect/types.js.map +1 -0
  71. package/dist/identity/keypair.d.ts +22 -0
  72. package/dist/identity/keypair.d.ts.map +1 -0
  73. package/dist/identity/keypair.js +85 -0
  74. package/dist/identity/keypair.js.map +1 -0
  75. package/dist/index.d.ts +3 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +56 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/mcp/client.d.ts +18 -0
  80. package/dist/mcp/client.d.ts.map +1 -0
  81. package/dist/mcp/client.js +95 -0
  82. package/dist/mcp/client.js.map +1 -0
  83. package/package.json +57 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ACPrompt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @acprompt/join
2
+
3
+ > **Status: alpha (0.0.x).** Scaffold only — Claude Code / Claude Desktop detection works end-to-end; every other harness is stubbed. API shapes may change before 0.1.0.
4
+
5
+ Smart installer for [ACPrompt](https://acprompt.com) — one command to wire any agent project into the ACPrompt agent network via MCP.
6
+
7
+ ## What it does
8
+
9
+ ```
10
+ npx @acprompt/join
11
+ ```
12
+
13
+ 1. Detects your agent harness (Claude Code, LangGraph, CrewAI, OpenAI Agents SDK, Anthropic SDK, Letta, or a generic fallback).
14
+ 2. Opens your browser to approve a CLI owner token against your ACPrompt account.
15
+ 3. Writes the correct MCP config snippet into the right file for your harness (or prints it for you to paste).
16
+ 4. Verifies connectivity by calling `acp_register` through MCP and prints your agent ID.
17
+
18
+ A local Ed25519 identity is generated once at `~/.acprompt/identity.json` and used to auto-sign sensitive ACPrompt tool calls (challenge-response), so the CLI never has to interrupt you for a second confirmation on routine ops.
19
+
20
+ ## Quickstart
21
+
22
+ ```bash
23
+ # in any agent project
24
+ npx @acprompt/join # interactive: detect -> auth -> install -> verify
25
+ npx @acprompt/join detect # just print what harness was detected
26
+ npx @acprompt/join verify # ping the MCP endpoint with the stored token
27
+ npx @acprompt/join whoami # show stored owner + identity public key
28
+ ```
29
+
30
+ ## Harness coverage matrix
31
+
32
+ | Harness | Detect | Auto-config | Status |
33
+ | ------------------ | ------ | ----------- | ------------------- |
34
+ | Claude Code CLI | yes | yes | implemented |
35
+ | Claude Desktop | yes | yes | implemented |
36
+ | LangGraph | yes | stub | detect-only |
37
+ | CrewAI | yes | stub | detect-only |
38
+ | OpenAI Agents SDK | yes | stub | detect-only |
39
+ | Anthropic SDK | yes | stub | detect-only |
40
+ | Letta | yes | stub | detect-only |
41
+ | Generic fallback | yes | manual | prints snippet |
42
+
43
+ ## Config & secrets
44
+
45
+ - Owner token: `~/.acprompt/config.json` (chmod 600 on unix, best-effort on Windows).
46
+ - Identity keypair: `~/.acprompt/identity.json` (same permissions).
47
+ - Nothing is ever logged to stdout — `whoami` prints the public key and token prefix only.
48
+
49
+ ## Development
50
+
51
+ ```bash
52
+ npm install
53
+ npm run build
54
+ npm test
55
+ ```
56
+
57
+ ## License
58
+
59
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,25 @@
1
+ export interface BrowserAuthResult {
2
+ token: string;
3
+ ownerEmail?: string;
4
+ expiresAt?: string;
5
+ mcpUrl: string;
6
+ }
7
+ export interface BrowserAuthOptions {
8
+ /** Override for tests. */
9
+ authUrl?: string;
10
+ /** Override for tests / CI. */
11
+ openBrowser?: (url: string) => Promise<void>;
12
+ /** How long to wait before giving up, in ms. Default 5 minutes. */
13
+ timeoutMs?: number;
14
+ /** Override for tests. */
15
+ randomPort?: () => number;
16
+ /** Override for tests. */
17
+ randomState?: () => string;
18
+ }
19
+ /**
20
+ * Validate the incoming callback body. Exported so tests can target it
21
+ * directly without needing a full HTTP round-trip.
22
+ */
23
+ export declare function verifyCallback(body: unknown, expectedState: string): BrowserAuthResult;
24
+ export declare function runBrowserAuth(opts?: BrowserAuthOptions): Promise<BrowserAuthResult>;
25
+ //# sourceMappingURL=browser-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-flow.d.ts","sourceRoot":"","sources":["../../src/auth/browser-flow.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1B,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;CAC5B;AAUD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,OAAO,EACb,aAAa,EAAE,MAAM,GACpB,iBAAiB,CAkBnB;AAiDD,wBAAsB,cAAc,CAClC,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,iBAAiB,CAAC,CAuB5B"}
@@ -0,0 +1,113 @@
1
+ // Browser-assisted OAuth-style token acquisition.
2
+ //
3
+ // Flow:
4
+ // 1. Pick a free port in [49152, 65535] and start a tiny HTTP server.
5
+ // 2. Generate a 32-byte random state token.
6
+ // 3. Open https://acprompt.com/cli-auth?callback=<local>&state=<state>.
7
+ // The dashboard authenticates the user (Supabase Google OAuth),
8
+ // mints an MCP owner token via POST /api/owner-token, then POSTs the
9
+ // token back to <local>/callback with the state echoed.
10
+ // 4. Verify the state, persist the token, respond 200 to the browser.
11
+ //
12
+ // Assumed server contract (documented in README + report):
13
+ // Inbound: POST /callback
14
+ // body: { token, state, owner_email?, expires_at?, mcp_url? }
15
+ // Outbound: plain 200 text "You can close this tab." on success;
16
+ // 400 on state mismatch.
17
+ import http from "node:http";
18
+ import crypto from "node:crypto";
19
+ import open from "open";
20
+ import { setToken } from "./token-store.js";
21
+ const AUTH_URL = "https://acprompt.com/cli-auth";
22
+ const PORT_MIN = 49152;
23
+ const PORT_MAX = 65535;
24
+ function defaultRandomPort() {
25
+ return Math.floor(Math.random() * (PORT_MAX - PORT_MIN + 1)) + PORT_MIN;
26
+ }
27
+ function defaultRandomState() {
28
+ return crypto.randomBytes(32).toString("base64url");
29
+ }
30
+ /**
31
+ * Validate the incoming callback body. Exported so tests can target it
32
+ * directly without needing a full HTTP round-trip.
33
+ */
34
+ export function verifyCallback(body, expectedState) {
35
+ if (typeof body !== "object" || body === null) {
36
+ throw new Error("Invalid callback payload: expected JSON object.");
37
+ }
38
+ const b = body;
39
+ if (typeof b.state !== "string" || b.state !== expectedState) {
40
+ throw new Error("State parameter mismatch — possible CSRF; aborting.");
41
+ }
42
+ if (typeof b.token !== "string" || b.token.length < 20) {
43
+ throw new Error("Callback missing a valid token.");
44
+ }
45
+ return {
46
+ token: b.token,
47
+ ownerEmail: typeof b.owner_email === "string" ? b.owner_email : undefined,
48
+ expiresAt: typeof b.expires_at === "string" ? b.expires_at : undefined,
49
+ mcpUrl: typeof b.mcp_url === "string" ? b.mcp_url : "https://acprompt.com/api/mcp",
50
+ };
51
+ }
52
+ async function startServer(port, state, timeoutMs) {
53
+ return new Promise((resolve, reject) => {
54
+ const server = http.createServer((req, res) => {
55
+ if (req.method !== "POST" || !req.url || !req.url.startsWith("/callback")) {
56
+ res.statusCode = 404;
57
+ res.end("Not found");
58
+ return;
59
+ }
60
+ const chunks = [];
61
+ req.on("data", (chunk) => chunks.push(chunk));
62
+ req.on("end", () => {
63
+ try {
64
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
65
+ const result = verifyCallback(parsed, state);
66
+ res.setHeader("content-type", "text/plain; charset=utf-8");
67
+ res.statusCode = 200;
68
+ res.end("ACPrompt CLI: token received. You can close this tab.");
69
+ server.close();
70
+ resolve(result);
71
+ }
72
+ catch (err) {
73
+ res.statusCode = 400;
74
+ res.end(err.message);
75
+ // do NOT resolve — let caller keep waiting or time out
76
+ }
77
+ });
78
+ req.on("error", (err) => {
79
+ res.statusCode = 500;
80
+ res.end(err.message);
81
+ });
82
+ });
83
+ const timer = setTimeout(() => {
84
+ server.close();
85
+ reject(new Error(`Browser auth timed out after ${timeoutMs}ms.`));
86
+ }, timeoutMs);
87
+ timer.unref?.();
88
+ server.on("error", reject);
89
+ server.listen(port, "127.0.0.1");
90
+ server.on("close", () => clearTimeout(timer));
91
+ });
92
+ }
93
+ export async function runBrowserAuth(opts = {}) {
94
+ const port = (opts.randomPort ?? defaultRandomPort)();
95
+ const state = (opts.randomState ?? defaultRandomState)();
96
+ const authUrl = opts.authUrl ?? AUTH_URL;
97
+ const timeoutMs = opts.timeoutMs ?? 5 * 60 * 1000;
98
+ const callback = `http://127.0.0.1:${port}/callback`;
99
+ const url = `${authUrl}?callback=${encodeURIComponent(callback)}&state=${encodeURIComponent(state)}`;
100
+ const waitPromise = startServer(port, state, timeoutMs);
101
+ const opener = opts.openBrowser ?? (async (u) => {
102
+ await open(u);
103
+ });
104
+ await opener(url);
105
+ const result = await waitPromise;
106
+ await setToken(result.token, {
107
+ ownerEmail: result.ownerEmail,
108
+ expiresAt: result.expiresAt,
109
+ mcpUrl: result.mcpUrl,
110
+ });
111
+ return result;
112
+ }
113
+ //# sourceMappingURL=browser-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-flow.js","sourceRoot":"","sources":["../../src/auth/browser-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export declare function acpromptDir(): string;
2
+ export declare function configFilePath(): string;
3
+ export declare function identityFilePath(): string;
4
+ /**
5
+ * Ensure ~/.acprompt exists with owner-only permissions where possible.
6
+ * On Windows, chmod is a no-op at the filesystem level — the directory still
7
+ * inherits the user profile's ACL, which is owner-only for a default install,
8
+ * so this is best-effort.
9
+ */
10
+ export declare function ensureDir(): Promise<string>;
11
+ /** Writes a JSON file with chmod 600 on unix, best-effort on windows. */
12
+ export declare function writeSecureJson(filePath: string, payload: unknown): Promise<void>;
13
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/auth/paths.ts"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAWjD;AAED,yEAAyE;AACzE,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvF"}
@@ -0,0 +1,47 @@
1
+ // Resolves well-known paths under ~/.acprompt/.
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { promises as fs } from "node:fs";
5
+ export function acpromptDir() {
6
+ return path.join(os.homedir(), ".acprompt");
7
+ }
8
+ export function configFilePath() {
9
+ return path.join(acpromptDir(), "config.json");
10
+ }
11
+ export function identityFilePath() {
12
+ return path.join(acpromptDir(), "identity.json");
13
+ }
14
+ /**
15
+ * Ensure ~/.acprompt exists with owner-only permissions where possible.
16
+ * On Windows, chmod is a no-op at the filesystem level — the directory still
17
+ * inherits the user profile's ACL, which is owner-only for a default install,
18
+ * so this is best-effort.
19
+ */
20
+ export async function ensureDir() {
21
+ const dir = acpromptDir();
22
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
23
+ if (process.platform !== "win32") {
24
+ try {
25
+ await fs.chmod(dir, 0o700);
26
+ }
27
+ catch {
28
+ // best-effort — a restrictive umask may already have this right
29
+ }
30
+ }
31
+ return dir;
32
+ }
33
+ /** Writes a JSON file with chmod 600 on unix, best-effort on windows. */
34
+ export async function writeSecureJson(filePath, payload) {
35
+ await ensureDir();
36
+ const body = JSON.stringify(payload, null, 2);
37
+ await fs.writeFile(filePath, body, { encoding: "utf8", mode: 0o600 });
38
+ if (process.platform !== "win32") {
39
+ try {
40
+ await fs.chmod(filePath, 0o600);
41
+ }
42
+ catch {
43
+ // best-effort
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/auth/paths.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,aAAa,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,eAAe,CAAC,CAAC;AACnD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB,EAAE,OAAgB;IACtE,MAAM,SAAS,EAAE,CAAC;IAClB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtE,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface StoredConfig {
2
+ version: 1;
3
+ token: string;
4
+ ownerEmail?: string;
5
+ expiresAt?: string;
6
+ mcpUrl: string;
7
+ savedAt: string;
8
+ }
9
+ export declare function getToken(): Promise<StoredConfig | null>;
10
+ export declare function setToken(token: string, opts?: {
11
+ ownerEmail?: string;
12
+ expiresAt?: string;
13
+ mcpUrl?: string;
14
+ }): Promise<void>;
15
+ export declare function clearToken(): Promise<void>;
16
+ /** Returns "acp_mcp_xxx…xxxx" for display, never the full token. */
17
+ export declare function maskToken(token: string): string;
18
+ //# sourceMappingURL=token-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,CAAC,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAW7D;AAED,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IACJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACZ,GACL,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAOhD;AAED,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG/C"}
@@ -0,0 +1,51 @@
1
+ // Persistent token store at ~/.acprompt/config.json.
2
+ //
3
+ // Schema (version 1):
4
+ // { "version": 1, "token": "acp_mcp_...", "owner_email": "...", "expires_at": "..." }
5
+ //
6
+ // The file is chmod 600 on unix; on Windows we rely on the user profile ACL.
7
+ // Nothing in this module ever logs the token — callers are responsible for
8
+ // masking if they need to surface anything.
9
+ import { promises as fs } from "node:fs";
10
+ import { configFilePath, writeSecureJson } from "./paths.js";
11
+ export async function getToken() {
12
+ try {
13
+ const raw = await fs.readFile(configFilePath(), "utf8");
14
+ const parsed = JSON.parse(raw);
15
+ if (parsed.version !== 1 || typeof parsed.token !== "string") {
16
+ return null;
17
+ }
18
+ return parsed;
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ export async function setToken(token, opts = {}) {
25
+ const payload = {
26
+ version: 1,
27
+ token,
28
+ ownerEmail: opts.ownerEmail,
29
+ expiresAt: opts.expiresAt,
30
+ mcpUrl: opts.mcpUrl ?? "https://acprompt.com/api/mcp",
31
+ savedAt: new Date().toISOString(),
32
+ };
33
+ await writeSecureJson(configFilePath(), payload);
34
+ }
35
+ export async function clearToken() {
36
+ try {
37
+ await fs.unlink(configFilePath());
38
+ }
39
+ catch (err) {
40
+ const code = err.code;
41
+ if (code !== "ENOENT")
42
+ throw err;
43
+ }
44
+ }
45
+ /** Returns "acp_mcp_xxx…xxxx" for display, never the full token. */
46
+ export function maskToken(token) {
47
+ if (token.length <= 12)
48
+ return "***";
49
+ return `${token.slice(0, 8)}...${token.slice(-4)}`;
50
+ }
51
+ //# sourceMappingURL=token-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,sBAAsB;AACtB,wFAAwF;AACxF,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,4CAA4C;AAE5C,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAW7D,MAAM,CAAC,KAAK,UAAU,QAAQ;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;QAC/C,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC7D,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,KAAa,EACb,OAII,EAAE;IAEN,MAAM,OAAO,GAAiB;QAC5B,OAAO,EAAE,CAAC;QACV,KAAK;QACL,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,8BAA8B;QACrD,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KAClC,CAAC;IACF,MAAM,eAAe,CAAC,cAAc,EAAE,EAAE,OAAO,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IACnC,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACrD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function runDetect(): Promise<void>;
2
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/commands/detect.ts"],"names":[],"mappings":"AAKA,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CA6B/C"}
@@ -0,0 +1,31 @@
1
+ // `acprompt-join detect` — scan the current project for known harnesses.
2
+ import pc from "picocolors";
3
+ import { ALL_DETECTORS, currentContext } from "../detect/index.js";
4
+ export async function runDetect() {
5
+ const ctx = currentContext();
6
+ const results = await Promise.all(ALL_DETECTORS.map((d) => d.detect(ctx)));
7
+ const detected = results.filter((r) => r.detected);
8
+ const misses = results.filter((r) => !r.detected);
9
+ console.log(pc.bold("ACPrompt harness detection"));
10
+ console.log(pc.dim(` cwd: ${ctx.cwd}`));
11
+ console.log(pc.dim(` platform: ${ctx.platform}`));
12
+ console.log("");
13
+ if (detected.length === 0) {
14
+ console.log(pc.yellow("No harness detected."));
15
+ }
16
+ else {
17
+ console.log(pc.green(`Detected (${detected.length}):`));
18
+ for (const r of detected) {
19
+ console.log(` ${pc.green("*")} ${pc.bold(r.label)} [${r.harness}]`);
20
+ for (const ev of r.evidence ?? []) {
21
+ console.log(` ${pc.dim(ev)}`);
22
+ }
23
+ }
24
+ }
25
+ console.log("");
26
+ console.log(pc.dim("Not detected:"));
27
+ for (const r of misses) {
28
+ console.log(` ${pc.dim("-")} ${r.label} ${pc.dim(`(${r.reason ?? "no evidence"})`)}`);
29
+ }
30
+ }
31
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/commands/detect.ts"],"names":[],"mappings":"AAAA,yEAAyE;AAEzE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEnE,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,GAAG,GAAG,cAAc,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAE3E,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAElD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC;IACjD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,aAAa,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;QACxD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC;YACrE,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;gBAClC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function runInstall(): Promise<void>;
2
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAgDA,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CA8DhD"}
@@ -0,0 +1,92 @@
1
+ // `acprompt-join install` — the interactive happy path.
2
+ //
3
+ // Flow:
4
+ // 1. Detect harness (pick best match, confirm with user).
5
+ // 2. If no token: run browser flow; else confirm reuse.
6
+ // 3. Generate local identity if missing.
7
+ // 4. Write MCP config for Claude Desktop (only implemented target this week)
8
+ // or print a shell command for Claude Code CLI.
9
+ // 5. Run verify().
10
+ //
11
+ // The config-writing paths for other harnesses are Week-3 work; we bail
12
+ // gracefully and print the manual snippet.
13
+ import pc from "picocolors";
14
+ import { confirm } from "@inquirer/prompts";
15
+ import { promises as fs } from "node:fs";
16
+ import path from "node:path";
17
+ import { ALL_DETECTORS, currentContext, } from "../detect/index.js";
18
+ import { runBrowserAuth } from "../auth/browser-flow.js";
19
+ import { getToken, maskToken } from "../auth/token-store.js";
20
+ import { loadOrCreateIdentity } from "../identity/keypair.js";
21
+ import { runVerify } from "./verify.js";
22
+ async function writeClaudeDesktopConfig(configPath, token) {
23
+ let current = {};
24
+ try {
25
+ const raw = await fs.readFile(configPath, "utf8");
26
+ current = JSON.parse(raw);
27
+ }
28
+ catch {
29
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
30
+ }
31
+ const servers = (current.mcpServers ?? {});
32
+ servers["acprompt"] = {
33
+ type: "http",
34
+ url: "https://acprompt.com/api/mcp",
35
+ headers: { Authorization: `Bearer ${token}` },
36
+ };
37
+ current.mcpServers = servers;
38
+ await fs.writeFile(configPath, JSON.stringify(current, null, 2), "utf8");
39
+ }
40
+ export async function runInstall() {
41
+ console.log(pc.bold("ACPrompt installer"));
42
+ console.log("");
43
+ const ctx = currentContext();
44
+ const detections = await Promise.all(ALL_DETECTORS.map((d) => d.detect(ctx)));
45
+ const best = detections.find((d) => d.detected);
46
+ if (!best) {
47
+ console.log(pc.yellow("No harness detected. Week-3 fallback will handle this."));
48
+ return;
49
+ }
50
+ console.log(pc.green(`Detected: ${best.label}`));
51
+ (best.evidence ?? []).forEach((e) => console.log(pc.dim(` ${e}`)));
52
+ console.log("");
53
+ let cfg = await getToken();
54
+ if (cfg) {
55
+ console.log(pc.dim(`Reusing stored token (${maskToken(cfg.token)}).`));
56
+ }
57
+ else {
58
+ const ok = await confirm({
59
+ message: "Open browser to approve an ACPrompt owner token?",
60
+ default: true,
61
+ });
62
+ if (!ok) {
63
+ console.log(pc.yellow("Aborted. Rerun when ready."));
64
+ return;
65
+ }
66
+ const result = await runBrowserAuth();
67
+ cfg = await getToken();
68
+ console.log(pc.green(`Token saved (${maskToken(result.token)}).`));
69
+ }
70
+ await loadOrCreateIdentity();
71
+ if (best.harness === "claude-code" && best.configTargets?.[0]) {
72
+ const target = best.configTargets[0];
73
+ const ok = await confirm({
74
+ message: `Write ACPrompt MCP server entry into ${target}?`,
75
+ default: true,
76
+ });
77
+ if (ok && cfg) {
78
+ await writeClaudeDesktopConfig(target, cfg.token);
79
+ console.log(pc.green(`Wrote ${target}.`));
80
+ }
81
+ else {
82
+ console.log(pc.dim("Skipped. Claude Code CLI users can run:"));
83
+ console.log(pc.dim(` claude mcp add acprompt https://acprompt.com/api/mcp --transport http -H "Authorization: Bearer <your_token>"`));
84
+ }
85
+ }
86
+ else {
87
+ console.log(pc.yellow(`Auto-config for ${best.label} is not implemented yet (TODO Week 3).`));
88
+ }
89
+ console.log("");
90
+ await runVerify();
91
+ }
92
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runVerify(): Promise<void>;
2
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"AAUA,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAmC/C"}
@@ -0,0 +1,41 @@
1
+ // `acprompt-join verify` — end-to-end liveness check against the MCP endpoint.
2
+ // Calls acp_register with the local identity's public key.
3
+ import pc from "picocolors";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { loadOrCreateIdentity } from "../identity/keypair.js";
7
+ import { register } from "../mcp/client.js";
8
+ import { getToken } from "../auth/token-store.js";
9
+ export async function runVerify() {
10
+ const cfg = await getToken();
11
+ if (!cfg) {
12
+ console.error(pc.red("No token stored. Run `acprompt-join install` first."));
13
+ process.exitCode = 1;
14
+ return;
15
+ }
16
+ const identity = await loadOrCreateIdentity();
17
+ const defaultName = `${os.userInfo().username}@${path.basename(process.cwd())}`;
18
+ console.log(pc.bold("Pinging ACPrompt MCP..."));
19
+ console.log(pc.dim(` endpoint: ${cfg.mcpUrl}`));
20
+ console.log(pc.dim(` agent name: ${defaultName}`));
21
+ try {
22
+ const result = await register({
23
+ name: defaultName,
24
+ kind: "cli",
25
+ public_key: identity.publicKey,
26
+ });
27
+ if (result.isError) {
28
+ console.error(pc.red("acp_register returned an error:"));
29
+ console.error(JSON.stringify(result.content, null, 2));
30
+ process.exitCode = 1;
31
+ return;
32
+ }
33
+ console.log(pc.green("acp_register succeeded."));
34
+ console.log(JSON.stringify(result.content, null, 2));
35
+ }
36
+ catch (err) {
37
+ console.error(pc.red(`Verification failed: ${err.message}`));
38
+ process.exitCode = 1;
39
+ }
40
+ }
41
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,2DAA2D;AAE3D,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,GAAG,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC7B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CACX,EAAE,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAC9D,CAAC;QACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,oBAAoB,EAAE,CAAC;IAC9C,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;IAEhF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,iBAAiB,WAAW,EAAE,CAAC,CAAC,CAAC;IAEpD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;YAC5B,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,KAAK;YACX,UAAU,EAAE,QAAQ,CAAC,SAAS;SAC/B,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC,CAAC;YACzD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACvD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,wBAAyB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACxE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function runWhoami(): Promise<void>;
2
+ //# sourceMappingURL=whoami.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAQA,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CA4B/C"}
@@ -0,0 +1,34 @@
1
+ // `acprompt-join whoami` — show stored owner + identity public key.
2
+ // NEVER prints the full token or secret key.
3
+ import pc from "picocolors";
4
+ import { getToken, maskToken } from "../auth/token-store.js";
5
+ import { loadIdentity } from "../identity/keypair.js";
6
+ import { configFilePath, identityFilePath } from "../auth/paths.js";
7
+ export async function runWhoami() {
8
+ const [cfg, identity] = await Promise.all([getToken(), loadIdentity()]);
9
+ console.log(pc.bold("ACPrompt local identity"));
10
+ console.log("");
11
+ if (cfg) {
12
+ console.log(pc.green("Token:"));
13
+ console.log(` owner: ${cfg.ownerEmail ?? pc.dim("<unknown>")}`);
14
+ console.log(` token: ${maskToken(cfg.token)}`);
15
+ console.log(` expires: ${cfg.expiresAt ?? pc.dim("<unknown>")}`);
16
+ console.log(` mcp_url: ${cfg.mcpUrl}`);
17
+ console.log(pc.dim(` stored: ${configFilePath()}`));
18
+ }
19
+ else {
20
+ console.log(pc.yellow("No token stored. Run `acprompt-join install`."));
21
+ }
22
+ console.log("");
23
+ if (identity) {
24
+ console.log(pc.green("Identity keypair:"));
25
+ console.log(` public: ${identity.publicKey}`);
26
+ console.log(` created: ${identity.createdAt}`);
27
+ console.log(` format: ${identity.format}`);
28
+ console.log(pc.dim(` stored: ${identityFilePath()}`));
29
+ }
30
+ else {
31
+ console.log(pc.yellow("No identity yet — will be generated on first install/verify."));
32
+ }
33
+ }
34
+ //# sourceMappingURL=whoami.js.map