macaw-ai 0.9.10 → 0.9.12
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 +4 -2
- package/bin/macaw.js +74 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,8 +42,10 @@ because it has no path to the place approvals are given.
|
|
|
42
42
|
Anthropic, LangChain, MCP, LiteLLM and Pydantic AI, with the examples ready to
|
|
43
43
|
run.
|
|
44
44
|
|
|
45
|
-
Both are free
|
|
46
|
-
|
|
45
|
+
Both are free. The first install creates a workspace: a link appears in your
|
|
46
|
+
terminal, you approve it in a browser, and that is the account. Later installs
|
|
47
|
+
find that key and join the same workspace, so choosing both surfaces above
|
|
48
|
+
asks you once, not twice.
|
|
47
49
|
|
|
48
50
|
## Licence
|
|
49
51
|
|
package/bin/macaw.js
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// `
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// `macaw-ai install` - the one name to remember. It installs nothing itself;
|
|
3
|
+
// it asks which surface you want governed and hands off to the package that
|
|
4
|
+
// does that one thing.
|
|
5
5
|
//
|
|
6
6
|
// Kept separate from those packages so this name never has to narrow. It
|
|
7
7
|
// means "MACAW" today and will still mean it when there are six surfaces.
|
|
8
8
|
|
|
9
9
|
import { spawnSync } from "node:child_process";
|
|
10
10
|
import { createInterface } from "node:readline/promises";
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
11
13
|
|
|
12
14
|
const SURFACES = [
|
|
13
15
|
{
|
|
14
16
|
key: "1",
|
|
15
17
|
pkg: "@macaw-ai/coding-agents",
|
|
16
|
-
label: "Claude Code and Codex
|
|
18
|
+
label: "Claude Code and Codex",
|
|
17
19
|
detail: "every tool call stops at a hook and waits for a decision",
|
|
20
|
+
command: "macaw-sidecar",
|
|
21
|
+
subcommand: true,
|
|
18
22
|
},
|
|
19
23
|
{
|
|
20
24
|
key: "2",
|
|
21
25
|
pkg: "@macaw-ai/client",
|
|
22
|
-
label: "
|
|
23
|
-
detail: "
|
|
26
|
+
label: "the MACAW SDK Client Library and secureAI adapters",
|
|
27
|
+
detail: "drop-in adapters for popular agentic frameworks",
|
|
28
|
+
command: "macaw-client",
|
|
29
|
+
subcommand: true,
|
|
24
30
|
},
|
|
25
31
|
];
|
|
26
32
|
|
|
@@ -28,12 +34,58 @@ const C = process.stdout.isTTY
|
|
|
28
34
|
? { b: "\x1b[1m", dim: "\x1b[2m", c: "\x1b[36m", x: "\x1b[0m" }
|
|
29
35
|
: { b: "", dim: "", c: "", x: "" };
|
|
30
36
|
|
|
31
|
-
function
|
|
32
|
-
console.log(`\n${C.dim} →
|
|
33
|
-
const r = spawnSync(
|
|
37
|
+
function sh(cmd, args, env) {
|
|
38
|
+
console.log(`\n${C.dim} → ${cmd} ${args.join(" ")}${C.x}\n`);
|
|
39
|
+
const r = spawnSync(cmd, args, { stdio: "inherit", env: { ...process.env, ...env } });
|
|
34
40
|
return r.status ?? 1;
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
// Installed, then run by name. npx links no commands, so the launchers it
|
|
44
|
+
// leaves behind can only be reached by their full path inside an npm cache
|
|
45
|
+
// directory - which is how an install ended up telling people to type ninety
|
|
46
|
+
// characters.
|
|
47
|
+
function run(surface, args, env) {
|
|
48
|
+
const code = sh("npm", ["install", "-g", surface.pkg], env);
|
|
49
|
+
if (code !== 0) return code;
|
|
50
|
+
const rest = surface.subcommand ? args : args.filter((a) => a !== "install");
|
|
51
|
+
return sh(surface.command, rest, env);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// The coding-agents installer writes its key here. Reading it back lets the
|
|
55
|
+
// second surface join that workspace without asking for a key the user
|
|
56
|
+
// approved thirty seconds earlier.
|
|
57
|
+
function keyFromThisDirectory() {
|
|
58
|
+
try {
|
|
59
|
+
const cfg = JSON.parse(fs.readFileSync(path.join(process.cwd(), ".macaw", "config.json"), "utf8"));
|
|
60
|
+
if (typeof cfg?.api_key === "string" && cfg.api_key.startsWith("mw_")) {
|
|
61
|
+
return { MACAW_API_KEY: cfg.api_key, MACAW_WORKSPACE: cfg.workspace || "" };
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// No install here, or not readable. The next installer asks, as it would.
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Both surfaces, in this order. Coding agents first because that install is
|
|
70
|
+
// the one that creates the workspace, and the key it writes is then handed to
|
|
71
|
+
// the client rather than asked for again.
|
|
72
|
+
//
|
|
73
|
+
// The order is load-bearing the other way too: coding-agents writes
|
|
74
|
+
// $PWD/.macaw/config.json, which the client's lookup reads. The client writes
|
|
75
|
+
// into a subdirectory it chooses, which nothing else can be expected to find.
|
|
76
|
+
function both(args) {
|
|
77
|
+
const steps = args.length ? args : ["install"];
|
|
78
|
+
for (const [i, s] of SURFACES.entries()) {
|
|
79
|
+
// Two installers each printing their own banner reads as the thing having
|
|
80
|
+
// restarted. Say which step this is.
|
|
81
|
+
console.log("");
|
|
82
|
+
console.log(`${C.b} ── ${i + 1} of ${SURFACES.length} · ${s.label} ──${C.x}`);
|
|
83
|
+
const code = run(s, steps, i === 0 ? undefined : keyFromThisDirectory());
|
|
84
|
+
if (code !== 0) return code;
|
|
85
|
+
}
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
37
89
|
async function main() {
|
|
38
90
|
const args = process.argv.slice(2);
|
|
39
91
|
|
|
@@ -41,7 +93,14 @@ async function main() {
|
|
|
41
93
|
// macaw-ai install --coding-agents
|
|
42
94
|
const named = SURFACES.find((s) => args.includes(`--${s.pkg.split("/")[1]}`));
|
|
43
95
|
if (named) {
|
|
44
|
-
process.exit(run(named
|
|
96
|
+
process.exit(run(named, args.filter((a) => !a.startsWith("--macaw") && a !== `--${named.pkg.split("/")[1]}`)));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// macaw-ai install --both
|
|
100
|
+
// The menu's third choice, as a flag. The other two already have one, and
|
|
101
|
+
// without this there is no way to ask for both without a terminal.
|
|
102
|
+
if (args.includes("--both")) {
|
|
103
|
+
process.exit(both(args.filter((a) => a !== "--both")));
|
|
45
104
|
}
|
|
46
105
|
|
|
47
106
|
console.log("");
|
|
@@ -60,7 +119,9 @@ async function main() {
|
|
|
60
119
|
// something nobody asked for. Say what to type instead.
|
|
61
120
|
if (!process.stdin.isTTY) {
|
|
62
121
|
console.log(" Nothing is attached to answer, so pick one directly:");
|
|
63
|
-
for (const s of SURFACES)
|
|
122
|
+
for (const s of SURFACES) {
|
|
123
|
+
console.log(` npm install -g ${s.pkg} && ${s.command}${s.subcommand ? " install" : ""}`);
|
|
124
|
+
}
|
|
64
125
|
console.log("");
|
|
65
126
|
process.exit(1);
|
|
66
127
|
}
|
|
@@ -70,20 +131,14 @@ async function main() {
|
|
|
70
131
|
rl.close();
|
|
71
132
|
|
|
72
133
|
const rest = args.length ? args : ["install"];
|
|
73
|
-
if (choice === "3")
|
|
74
|
-
for (const s of SURFACES) {
|
|
75
|
-
const code = run(s.pkg, rest);
|
|
76
|
-
if (code !== 0) process.exit(code);
|
|
77
|
-
}
|
|
78
|
-
process.exit(0);
|
|
79
|
-
}
|
|
134
|
+
if (choice === "3") process.exit(both(rest));
|
|
80
135
|
|
|
81
136
|
const picked = SURFACES.find((s) => s.key === choice);
|
|
82
137
|
if (!picked) {
|
|
83
138
|
console.log(" Not a choice.");
|
|
84
139
|
process.exit(1);
|
|
85
140
|
}
|
|
86
|
-
process.exit(run(picked
|
|
141
|
+
process.exit(run(picked, rest));
|
|
87
142
|
}
|
|
88
143
|
|
|
89
144
|
main().catch((e) => { console.error(e.message); process.exit(1); });
|
package/package.json
CHANGED