magic-spells 0.1.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.
- package/README.md +41 -17
- package/dist/{chunk-YT2Z756Z.js → chunk-JZ7XESF4.js} +733 -32
- package/dist/{chunk-S5IUP7G2.js → chunk-PQFOO5A7.js} +1 -1
- package/dist/main.js +618 -64
- package/dist/{operations-P3DUAVPP.js → operations-GGMLIYEW.js} +2 -2
- package/dist/src-DJYPPJFM.js +16 -0
- package/dist/src-KJGFPRQP.js +17 -0
- package/dist/{src-4S3XHPWY.js → src-L2I546M7.js} +7 -234
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// ../sites/src/index.ts
|
|
2
|
+
var sites = {
|
|
3
|
+
id: "sites",
|
|
4
|
+
title: "Sites",
|
|
5
|
+
credential: "sites_",
|
|
6
|
+
apiUrl: "https://api.sites.magicspells.io",
|
|
7
|
+
keyHelp: "Sites \u2192 Settings \u2192 Access keys",
|
|
8
|
+
sessions: { path: "/v1/cli/sessions" },
|
|
9
|
+
load: async () => [],
|
|
10
|
+
client: () => null
|
|
11
|
+
};
|
|
12
|
+
var src_default = sites;
|
|
13
|
+
export {
|
|
14
|
+
src_default as default,
|
|
15
|
+
sites
|
|
16
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// ../marketing/src/index.ts
|
|
2
|
+
var marketing = {
|
|
3
|
+
id: "marketing",
|
|
4
|
+
title: "Marketing",
|
|
5
|
+
credential: "marketing_",
|
|
6
|
+
apiUrl: "https://marketing.magicspells.io",
|
|
7
|
+
keyHelp: "Marketing \u2192 Settings \u2192 API keys",
|
|
8
|
+
// Marketing serves its API under /api/v1 on the app host.
|
|
9
|
+
sessions: { path: "/api/v1/cli/sessions" },
|
|
10
|
+
load: async () => [],
|
|
11
|
+
client: () => null
|
|
12
|
+
};
|
|
13
|
+
var src_default = marketing;
|
|
14
|
+
export {
|
|
15
|
+
src_default as default,
|
|
16
|
+
marketing
|
|
17
|
+
};
|
|
@@ -3,16 +3,13 @@ import {
|
|
|
3
3
|
operationsByName,
|
|
4
4
|
pyramidError,
|
|
5
5
|
toPyramidError
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PQFOO5A7.js";
|
|
7
7
|
import {
|
|
8
8
|
MagicError,
|
|
9
9
|
TtlCache,
|
|
10
|
-
createCredentialStore,
|
|
11
|
-
maskKey,
|
|
12
10
|
readField,
|
|
13
|
-
readStr
|
|
14
|
-
|
|
15
|
-
} from "./chunk-YT2Z756Z.js";
|
|
11
|
+
readStr
|
|
12
|
+
} from "./chunk-JZ7XESF4.js";
|
|
16
13
|
|
|
17
14
|
// ../pyramid/src/cache/resolver.ts
|
|
18
15
|
var TTL_MS = 6e4;
|
|
@@ -890,7 +887,7 @@ var PYRAMID_CREDENTIAL = {
|
|
|
890
887
|
var DEFAULT_API_URL = "https://api.pyramid.magicspells.io";
|
|
891
888
|
|
|
892
889
|
// ../pyramid/src/doctor.ts
|
|
893
|
-
var whoamiOp = async () => (await import("./operations-
|
|
890
|
+
var whoamiOp = async () => (await import("./operations-GGMLIYEW.js")).operationsByName.get("pyramid.whoami");
|
|
894
891
|
var doctorChecks = [
|
|
895
892
|
{
|
|
896
893
|
name: "pyramid auth",
|
|
@@ -923,226 +920,6 @@ var doctorChecks = [
|
|
|
923
920
|
}
|
|
924
921
|
];
|
|
925
922
|
|
|
926
|
-
// ../pyramid/src/login.ts
|
|
927
|
-
import { spawn } from "node:child_process";
|
|
928
|
-
import { randomBytes } from "node:crypto";
|
|
929
|
-
import { createServer } from "node:http";
|
|
930
|
-
var DEFAULT_WEB_URL = "https://pyramid.magicspells.io";
|
|
931
|
-
var DEFAULT_LOGIN_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
932
|
-
var MAX_CALLBACK_BODY_BYTES = 4 * 1024;
|
|
933
|
-
var realIO = {
|
|
934
|
-
out: (line) => process.stderr.write(`${line}
|
|
935
|
-
`),
|
|
936
|
-
setKey: async (key) => {
|
|
937
|
-
await saveProductKey(PYRAMID_CREDENTIAL, createCredentialStore(), key);
|
|
938
|
-
},
|
|
939
|
-
openBrowser,
|
|
940
|
-
env: process.env,
|
|
941
|
-
randomState: () => randomBytes(24).toString("base64url"),
|
|
942
|
-
loginTimeoutMs: DEFAULT_LOGIN_TIMEOUT_MS
|
|
943
|
-
};
|
|
944
|
-
var LOGIN_USAGE = "usage: magic pyramid login [--web-url <url>] [--name <key name>]";
|
|
945
|
-
function parseLoginOptions(argv, env) {
|
|
946
|
-
let webUrl = env.MAGIC_PYRAMID_WEB_URL?.trim() || DEFAULT_WEB_URL;
|
|
947
|
-
let name = "Magic CLI";
|
|
948
|
-
const args = argv;
|
|
949
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
950
|
-
const arg = args[i];
|
|
951
|
-
if (arg === "--help" || arg === "-h") return "help";
|
|
952
|
-
if (arg === "--web-url") {
|
|
953
|
-
const next = args[++i];
|
|
954
|
-
if (!next) return LOGIN_USAGE;
|
|
955
|
-
webUrl = next;
|
|
956
|
-
continue;
|
|
957
|
-
}
|
|
958
|
-
if (arg.startsWith("--web-url=")) {
|
|
959
|
-
webUrl = arg.slice("--web-url=".length);
|
|
960
|
-
continue;
|
|
961
|
-
}
|
|
962
|
-
if (arg === "--name") {
|
|
963
|
-
const next = args[++i];
|
|
964
|
-
if (!next) return LOGIN_USAGE;
|
|
965
|
-
name = next;
|
|
966
|
-
continue;
|
|
967
|
-
}
|
|
968
|
-
if (arg.startsWith("--name=")) {
|
|
969
|
-
name = arg.slice("--name=".length);
|
|
970
|
-
continue;
|
|
971
|
-
}
|
|
972
|
-
return `unknown option for magic pyramid login: ${arg}`;
|
|
973
|
-
}
|
|
974
|
-
try {
|
|
975
|
-
new URL(webUrl);
|
|
976
|
-
} catch {
|
|
977
|
-
return `MAGIC_PYRAMID_WEB_URL is not a valid URL: "${webUrl}".`;
|
|
978
|
-
}
|
|
979
|
-
return { webUrl, name: name.trim() || "Magic CLI" };
|
|
980
|
-
}
|
|
981
|
-
function authURL(webUrl, callbackURL, state, name) {
|
|
982
|
-
const target = new URL("/auth/cli", webUrl);
|
|
983
|
-
target.searchParams.set("redirect_uri", callbackURL);
|
|
984
|
-
target.searchParams.set("state", state);
|
|
985
|
-
target.searchParams.set("name", name);
|
|
986
|
-
return target.toString();
|
|
987
|
-
}
|
|
988
|
-
function readCallbackForm(req) {
|
|
989
|
-
return new Promise((resolve, reject) => {
|
|
990
|
-
const chunks = [];
|
|
991
|
-
let size = 0;
|
|
992
|
-
const onData = (chunk) => {
|
|
993
|
-
size += chunk.length;
|
|
994
|
-
if (size > MAX_CALLBACK_BODY_BYTES) {
|
|
995
|
-
req.off("data", onData);
|
|
996
|
-
req.resume();
|
|
997
|
-
resolve(null);
|
|
998
|
-
return;
|
|
999
|
-
}
|
|
1000
|
-
chunks.push(chunk);
|
|
1001
|
-
};
|
|
1002
|
-
req.on("data", onData);
|
|
1003
|
-
req.once("end", () => resolve(new URLSearchParams(Buffer.concat(chunks).toString("utf8"))));
|
|
1004
|
-
req.once("error", reject);
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
function closeServer(server) {
|
|
1008
|
-
return new Promise((resolve) => {
|
|
1009
|
-
server.close(() => resolve());
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
async function runLogin(argv, io = realIO) {
|
|
1013
|
-
const opts = parseLoginOptions(argv, io.env);
|
|
1014
|
-
if (opts === "help") {
|
|
1015
|
-
io.out(LOGIN_USAGE);
|
|
1016
|
-
io.out("Opens Pyramid in the browser and stores the workspace key it mints.");
|
|
1017
|
-
return 0;
|
|
1018
|
-
}
|
|
1019
|
-
if (typeof opts === "string") {
|
|
1020
|
-
io.out(opts);
|
|
1021
|
-
return 2;
|
|
1022
|
-
}
|
|
1023
|
-
const state = io.randomState();
|
|
1024
|
-
let settled = false;
|
|
1025
|
-
let timeout;
|
|
1026
|
-
let finish;
|
|
1027
|
-
const done = new Promise((resolve) => {
|
|
1028
|
-
finish = (code) => {
|
|
1029
|
-
if (settled) return;
|
|
1030
|
-
settled = true;
|
|
1031
|
-
if (timeout) clearTimeout(timeout);
|
|
1032
|
-
resolve(code);
|
|
1033
|
-
};
|
|
1034
|
-
});
|
|
1035
|
-
const server = createServer(async (req, res) => {
|
|
1036
|
-
const requestURL = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
1037
|
-
if (requestURL.pathname !== "/callback") {
|
|
1038
|
-
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
1039
|
-
res.end("Not found.\n");
|
|
1040
|
-
return;
|
|
1041
|
-
}
|
|
1042
|
-
if (req.method !== "POST" || requestURL.searchParams.has("key")) {
|
|
1043
|
-
res.writeHead(405, { "content-type": "text/plain; charset=utf-8", allow: "POST" });
|
|
1044
|
-
res.end("Method not allowed. Return to your terminal.\n");
|
|
1045
|
-
io.out("Ignored a Pyramid login callback that did not POST the key.");
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
let form;
|
|
1049
|
-
try {
|
|
1050
|
-
form = await readCallbackForm(req);
|
|
1051
|
-
} catch {
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
if (!form) {
|
|
1055
|
-
res.writeHead(413, { "content-type": "text/plain; charset=utf-8", connection: "close" });
|
|
1056
|
-
res.end("Login callback too large. Return to your terminal.\n");
|
|
1057
|
-
io.out("Ignored an oversized Pyramid login callback.");
|
|
1058
|
-
return;
|
|
1059
|
-
}
|
|
1060
|
-
const gotState = form.get("state") ?? "";
|
|
1061
|
-
if (gotState !== state) {
|
|
1062
|
-
res.writeHead(400, { "content-type": "text/plain; charset=utf-8" });
|
|
1063
|
-
res.end("Invalid login state. Return to your terminal.\n");
|
|
1064
|
-
io.out("Rejected Pyramid login callback with an invalid state.");
|
|
1065
|
-
void closeServer(server).then(() => finish(1));
|
|
1066
|
-
return;
|
|
1067
|
-
}
|
|
1068
|
-
const error = form.get("error");
|
|
1069
|
-
if (error) {
|
|
1070
|
-
res.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
|
|
1071
|
-
res.end("Pyramid login cancelled. Return to your terminal.\n");
|
|
1072
|
-
io.out(
|
|
1073
|
-
error === "access_denied" ? "Pyramid login cancelled." : `Pyramid login failed: ${error}`
|
|
1074
|
-
);
|
|
1075
|
-
void closeServer(server).then(() => finish(1));
|
|
1076
|
-
return;
|
|
1077
|
-
}
|
|
1078
|
-
const key = form.get("key")?.trim() ?? "";
|
|
1079
|
-
if (!key.startsWith(PYRAMID_CREDENTIAL.credential)) {
|
|
1080
|
-
res.writeHead(400, { "content-type": "text/plain; charset=utf-8" });
|
|
1081
|
-
res.end("Invalid Pyramid key. Return to your terminal.\n");
|
|
1082
|
-
io.out("Pyramid login returned an invalid key.");
|
|
1083
|
-
void closeServer(server).then(() => finish(1));
|
|
1084
|
-
return;
|
|
1085
|
-
}
|
|
1086
|
-
try {
|
|
1087
|
-
await io.setKey(key);
|
|
1088
|
-
} catch (err) {
|
|
1089
|
-
res.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
|
|
1090
|
-
res.end("Could not store the key. Return to your terminal.\n");
|
|
1091
|
-
io.out(
|
|
1092
|
-
`Could not store the Pyramid key: ${err instanceof Error ? err.message : String(err)}`
|
|
1093
|
-
);
|
|
1094
|
-
void closeServer(server).then(() => finish(1));
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
res.writeHead(200, { "content-type": "text/plain; charset=utf-8" });
|
|
1098
|
-
res.end("Pyramid CLI connected. You can close this tab.\n");
|
|
1099
|
-
io.out(
|
|
1100
|
-
`Saved ${maskKey(key, PYRAMID_CREDENTIAL.credential)} \u2014 magic and magic mcp will use it automatically.`
|
|
1101
|
-
);
|
|
1102
|
-
void closeServer(server).then(() => finish(0));
|
|
1103
|
-
});
|
|
1104
|
-
try {
|
|
1105
|
-
await new Promise((resolve, reject) => {
|
|
1106
|
-
server.once("error", reject);
|
|
1107
|
-
server.listen(0, "127.0.0.1", () => {
|
|
1108
|
-
server.off("error", reject);
|
|
1109
|
-
resolve();
|
|
1110
|
-
});
|
|
1111
|
-
});
|
|
1112
|
-
} catch (err) {
|
|
1113
|
-
io.out(
|
|
1114
|
-
`Could not start the local Pyramid login server: ${err instanceof Error ? err.message : String(err)}`
|
|
1115
|
-
);
|
|
1116
|
-
return 1;
|
|
1117
|
-
}
|
|
1118
|
-
const address = server.address();
|
|
1119
|
-
const callbackURL = `http://127.0.0.1:${address.port}/callback`;
|
|
1120
|
-
const loginURL = authURL(opts.webUrl, callbackURL, state, opts.name);
|
|
1121
|
-
timeout = setTimeout(() => {
|
|
1122
|
-
io.out("Pyramid login timed out before the browser completed the handoff.");
|
|
1123
|
-
void closeServer(server).then(() => finish(1));
|
|
1124
|
-
}, io.loginTimeoutMs);
|
|
1125
|
-
const opened = await io.openBrowser(loginURL);
|
|
1126
|
-
if (opened) {
|
|
1127
|
-
io.out("Opened Pyramid in your browser. Authorize the CLI to finish login.");
|
|
1128
|
-
} else {
|
|
1129
|
-
io.out("Could not open your browser automatically. Open this URL to finish login:");
|
|
1130
|
-
io.out(loginURL);
|
|
1131
|
-
}
|
|
1132
|
-
return done;
|
|
1133
|
-
}
|
|
1134
|
-
function openBrowser(url) {
|
|
1135
|
-
return new Promise((resolve) => {
|
|
1136
|
-
const platform = process.platform;
|
|
1137
|
-
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
1138
|
-
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
1139
|
-
const child = spawn(command, args, { detached: true, stdio: "ignore" });
|
|
1140
|
-
child.once("error", () => resolve(false));
|
|
1141
|
-
child.once("close", (code) => resolve(code === 0));
|
|
1142
|
-
child.unref();
|
|
1143
|
-
});
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
923
|
// ../pyramid/src/mcp/prompts.ts
|
|
1147
924
|
var DOCTOR_DESCRIPTION = "Check your Pyramid setup: confirm the API key authenticates and show the user, workspace, and accessible projects.";
|
|
1148
925
|
var DOCTOR_INSTRUCTION = [
|
|
@@ -1269,8 +1046,10 @@ function registerPyramidMcp(server, context) {
|
|
|
1269
1046
|
var pyramid = {
|
|
1270
1047
|
...PYRAMID_CREDENTIAL,
|
|
1271
1048
|
apiUrl: DEFAULT_API_URL,
|
|
1049
|
+
// `magic login` mints workspace keys here from the Account grant (API-PYRAMID-CLI-SESSIONS).
|
|
1050
|
+
sessions: { path: "/v1/cli/sessions" },
|
|
1272
1051
|
// The operations graph loads only when a Pyramid command runs.
|
|
1273
|
-
load: async () => (await import("./operations-
|
|
1052
|
+
load: async () => (await import("./operations-GGMLIYEW.js")).operations,
|
|
1274
1053
|
client(session, config) {
|
|
1275
1054
|
const api = new PyramidClient({
|
|
1276
1055
|
apiKey: session.key,
|
|
@@ -1280,12 +1059,6 @@ var pyramid = {
|
|
|
1280
1059
|
},
|
|
1281
1060
|
// `--project` stays a Pyramid flag, usable anywhere in argv (pyramid-cli's global).
|
|
1282
1061
|
globalFlags: ["project"],
|
|
1283
|
-
commands: {
|
|
1284
|
-
login: {
|
|
1285
|
-
summary: "Sign in through Pyramid in the browser and store a workspace key (until R1)",
|
|
1286
|
-
run: (argv) => runLogin(argv)
|
|
1287
|
-
}
|
|
1288
|
-
},
|
|
1289
1062
|
doctor: doctorChecks,
|
|
1290
1063
|
mcp: { instructions, register: registerPyramidMcp }
|
|
1291
1064
|
};
|