arcane-cli 1.11.5 → 1.12.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/dist/assets/sign-in/NOTICE.txt +16 -0
- package/dist/assets/sign-in/OFL-chakra-petch.txt +93 -0
- package/dist/assets/sign-in/OFL-rajdhani.txt +93 -0
- package/dist/assets/sign-in/sign-in-callback-denied.html +320 -0
- package/dist/assets/sign-in/sign-in-callback-failure.html +320 -0
- package/dist/assets/sign-in/sign-in-callback-success.html +320 -0
- package/dist/index.js +157 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ const require = __createRequire(import.meta.url);
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { createRequire as createRequire3 } from "module";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
|
-
import { dirname as dirname12, join as
|
|
9
|
+
import { dirname as dirname12, join as join22 } from "path";
|
|
10
10
|
|
|
11
11
|
// src/commands/init.ts
|
|
12
12
|
import { select as select3, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
@@ -7756,6 +7756,70 @@ function resolveIdentityEnvironment(env = process.env) {
|
|
|
7756
7756
|
import { createServer } from "http";
|
|
7757
7757
|
import { execFile as execFile3 } from "child_process";
|
|
7758
7758
|
import * as client from "openid-client";
|
|
7759
|
+
|
|
7760
|
+
// src/modules/result-pages.ts
|
|
7761
|
+
import { createHash as createHash3 } from "crypto";
|
|
7762
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
7763
|
+
import { join as join17 } from "path";
|
|
7764
|
+
var RESULT_KINDS = ["success", "denied", "failure"];
|
|
7765
|
+
function resultPagePath(kind) {
|
|
7766
|
+
return join17("sign-in", `sign-in-callback-${kind}.html`);
|
|
7767
|
+
}
|
|
7768
|
+
function styleHashes(html) {
|
|
7769
|
+
return [...html.matchAll(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi)].map((match) => {
|
|
7770
|
+
const text = match[1].replace(/\r\n?/g, "\n");
|
|
7771
|
+
return `'sha256-${createHash3("sha256").update(text, "utf8").digest("base64")}'`;
|
|
7772
|
+
});
|
|
7773
|
+
}
|
|
7774
|
+
function resultPageHeaders(html) {
|
|
7775
|
+
const hashes = [...new Set(styleHashes(html))];
|
|
7776
|
+
return {
|
|
7777
|
+
"cache-control": "no-store",
|
|
7778
|
+
"content-security-policy": [
|
|
7779
|
+
"default-src 'none'",
|
|
7780
|
+
`style-src ${hashes.length > 0 ? hashes.join(" ") : "'none'"}`,
|
|
7781
|
+
"img-src data:",
|
|
7782
|
+
"font-src data:",
|
|
7783
|
+
"base-uri 'none'",
|
|
7784
|
+
"form-action 'none'",
|
|
7785
|
+
"frame-ancestors 'none'"
|
|
7786
|
+
].join("; "),
|
|
7787
|
+
"content-type": "text/html; charset=utf-8",
|
|
7788
|
+
"referrer-policy": "no-referrer",
|
|
7789
|
+
"x-content-type-options": "nosniff"
|
|
7790
|
+
};
|
|
7791
|
+
}
|
|
7792
|
+
function resultPage(html) {
|
|
7793
|
+
return { html, headers: resultPageHeaders(html) };
|
|
7794
|
+
}
|
|
7795
|
+
function plainPage(heading, next) {
|
|
7796
|
+
return resultPage(
|
|
7797
|
+
[
|
|
7798
|
+
'<!doctype html><html lang="en"><meta charset="utf-8">',
|
|
7799
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
7800
|
+
`<title>${heading} \u2014 Arcane</title>`,
|
|
7801
|
+
"<style>:root{color-scheme:light dark}body{font-family:system-ui,sans-serif;margin:3rem;line-height:1.5}</style>",
|
|
7802
|
+
`<body><h1>${heading}</h1><p>${next}</p></body></html>`
|
|
7803
|
+
].join("")
|
|
7804
|
+
);
|
|
7805
|
+
}
|
|
7806
|
+
var PLAIN_RESULT_PAGES = {
|
|
7807
|
+
success: plainPage("Signed in to Arcane", "Return to your terminal. Your browser can be closed now."),
|
|
7808
|
+
denied: plainPage("Sign-in was not completed", "Return to your terminal to try again. No new session was stored."),
|
|
7809
|
+
failure: plainPage("Could not finish sign-in", "Return to your terminal for details. No new session was stored.")
|
|
7810
|
+
};
|
|
7811
|
+
async function loadResultPages(assetsDir) {
|
|
7812
|
+
try {
|
|
7813
|
+
const [success, denied, failure] = await Promise.all(
|
|
7814
|
+
RESULT_KINDS.map(async (kind) => resultPage(await readFile16(join17(assetsDir, resultPagePath(kind)), "utf8")))
|
|
7815
|
+
);
|
|
7816
|
+
return { success, denied, failure };
|
|
7817
|
+
} catch {
|
|
7818
|
+
return null;
|
|
7819
|
+
}
|
|
7820
|
+
}
|
|
7821
|
+
|
|
7822
|
+
// src/modules/oidc.ts
|
|
7759
7823
|
var IdentityUnreachableError = class extends Error {
|
|
7760
7824
|
constructor(host, cause) {
|
|
7761
7825
|
super(`Could not reach the identity service at ${host}.`);
|
|
@@ -7769,6 +7833,12 @@ var LoginTimeoutError = class extends Error {
|
|
|
7769
7833
|
this.name = "LoginTimeoutError";
|
|
7770
7834
|
}
|
|
7771
7835
|
};
|
|
7836
|
+
var LoginBrowserUnavailableError = class extends Error {
|
|
7837
|
+
constructor() {
|
|
7838
|
+
super("No browser could be opened. Run `spell login --device-code` and finish signing in on another device.");
|
|
7839
|
+
this.name = "LoginBrowserUnavailableError";
|
|
7840
|
+
}
|
|
7841
|
+
};
|
|
7772
7842
|
var LoginDeniedError = class extends Error {
|
|
7773
7843
|
constructor(code) {
|
|
7774
7844
|
super(`The identity service refused the sign-in (${code}).`);
|
|
@@ -7811,16 +7881,8 @@ function toTokenResult(tokens, previousRefreshToken) {
|
|
|
7811
7881
|
expiresAt: new Date(now.getTime() + seconds * 1e3)
|
|
7812
7882
|
};
|
|
7813
7883
|
}
|
|
7814
|
-
var
|
|
7815
|
-
|
|
7816
|
-
'<body style="font-family:system-ui;margin:3rem"><h1>Signed in to Arcane</h1>',
|
|
7817
|
-
"<p>You can close this window and go back to the terminal.</p></body>"
|
|
7818
|
-
].join("");
|
|
7819
|
-
var FAILED_PAGE = [
|
|
7820
|
-
'<!doctype html><meta charset="utf-8"><title>Arcane</title>',
|
|
7821
|
-
'<body style="font-family:system-ui;margin:3rem"><h1>Sign-in not completed</h1>',
|
|
7822
|
-
"<p>Go back to the terminal for what to do next.</p></body>"
|
|
7823
|
-
].join("");
|
|
7884
|
+
var resultPath = (kind) => `/result/${kind}`;
|
|
7885
|
+
var RESULT_PATHS = new Set(RESULT_KINDS.map(resultPath));
|
|
7824
7886
|
async function browserLogin(config, options) {
|
|
7825
7887
|
const codeVerifier = client.randomPKCECodeVerifier();
|
|
7826
7888
|
const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);
|
|
@@ -7845,8 +7907,11 @@ async function browserLogin(config, options) {
|
|
|
7845
7907
|
state,
|
|
7846
7908
|
nonce
|
|
7847
7909
|
});
|
|
7910
|
+
const pages = options.pages ?? PLAIN_RESULT_PAGES;
|
|
7848
7911
|
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1e3;
|
|
7849
7912
|
let timer;
|
|
7913
|
+
let callbackConsumed = false;
|
|
7914
|
+
let browserResult;
|
|
7850
7915
|
const close = () => {
|
|
7851
7916
|
if (timer) clearTimeout(timer);
|
|
7852
7917
|
server.closeAllConnections?.();
|
|
@@ -7855,14 +7920,53 @@ async function browserLogin(config, options) {
|
|
|
7855
7920
|
const tokens = new Promise((resolve5, reject) => {
|
|
7856
7921
|
server.on("request", (req, res) => {
|
|
7857
7922
|
const url = new URL(req.url ?? "/", redirectUri);
|
|
7923
|
+
if (RESULT_PATHS.has(url.pathname)) {
|
|
7924
|
+
if (!browserResult || url.pathname !== resultPath(browserResult.kind) || url.search !== "") {
|
|
7925
|
+
res.writeHead(404).end();
|
|
7926
|
+
return;
|
|
7927
|
+
}
|
|
7928
|
+
const served = browserResult;
|
|
7929
|
+
const page = pages[served.kind];
|
|
7930
|
+
const settle = () => {
|
|
7931
|
+
if (served.kind === "success") resolve5(served.result);
|
|
7932
|
+
else reject(served.error);
|
|
7933
|
+
};
|
|
7934
|
+
res.once("finish", settle).once("close", settle);
|
|
7935
|
+
res.writeHead(200, page.headers).end(page.html);
|
|
7936
|
+
return;
|
|
7937
|
+
}
|
|
7858
7938
|
if (url.pathname !== "/" || !url.searchParams.has("code") && !url.searchParams.has("error")) {
|
|
7859
7939
|
res.writeHead(404).end();
|
|
7860
7940
|
return;
|
|
7861
7941
|
}
|
|
7942
|
+
if (callbackConsumed) {
|
|
7943
|
+
res.writeHead(409, { "cache-control": "no-store" }).end();
|
|
7944
|
+
return;
|
|
7945
|
+
}
|
|
7946
|
+
if (url.searchParams.get("state") !== state) {
|
|
7947
|
+
res.writeHead(400, pages.failure.headers).end(pages.failure.html);
|
|
7948
|
+
return;
|
|
7949
|
+
}
|
|
7950
|
+
callbackConsumed = true;
|
|
7951
|
+
const redirectToResult = (result) => {
|
|
7952
|
+
browserResult = result;
|
|
7953
|
+
res.writeHead(303, {
|
|
7954
|
+
"cache-control": "no-store",
|
|
7955
|
+
location: resultPath(result.kind),
|
|
7956
|
+
"referrer-policy": "no-referrer"
|
|
7957
|
+
}).end();
|
|
7958
|
+
if (result.kind === "success") {
|
|
7959
|
+
if (timer) clearTimeout(timer);
|
|
7960
|
+
timer = setTimeout(() => resolve5(result.result), Math.min(timeoutMs, 5e3));
|
|
7961
|
+
}
|
|
7962
|
+
};
|
|
7862
7963
|
const errorCode = url.searchParams.get("error");
|
|
7863
7964
|
if (errorCode) {
|
|
7864
|
-
|
|
7865
|
-
|
|
7965
|
+
const code = errorCode.replace(/[^a-z_]/gi, "");
|
|
7966
|
+
redirectToResult({
|
|
7967
|
+
kind: code === "access_denied" ? "denied" : "failure",
|
|
7968
|
+
error: new LoginDeniedError(code)
|
|
7969
|
+
});
|
|
7866
7970
|
return;
|
|
7867
7971
|
}
|
|
7868
7972
|
client.authorizationCodeGrant(config, url, {
|
|
@@ -7873,11 +7977,9 @@ async function browserLogin(config, options) {
|
|
|
7873
7977
|
}).then(async (response) => {
|
|
7874
7978
|
const result = toTokenResult(response);
|
|
7875
7979
|
await options.onValidated?.(result);
|
|
7876
|
-
|
|
7877
|
-
resolve5(result);
|
|
7980
|
+
redirectToResult({ kind: "success", result });
|
|
7878
7981
|
}).catch((error) => {
|
|
7879
|
-
|
|
7880
|
-
reject(error);
|
|
7982
|
+
redirectToResult({ kind: "failure", error });
|
|
7881
7983
|
});
|
|
7882
7984
|
});
|
|
7883
7985
|
timer = setTimeout(() => reject(new LoginTimeoutError()), timeoutMs);
|
|
@@ -7886,7 +7988,10 @@ async function browserLogin(config, options) {
|
|
|
7886
7988
|
});
|
|
7887
7989
|
try {
|
|
7888
7990
|
const opened = await options.openUrl(authorizationUrl.href);
|
|
7889
|
-
if (!opened)
|
|
7991
|
+
if (!opened) {
|
|
7992
|
+
options.onCannotOpen?.();
|
|
7993
|
+
throw new LoginBrowserUnavailableError();
|
|
7994
|
+
}
|
|
7890
7995
|
return await tokens;
|
|
7891
7996
|
} finally {
|
|
7892
7997
|
close();
|
|
@@ -7918,7 +8023,7 @@ function openInDefaultBrowser(url) {
|
|
|
7918
8023
|
import { execFileSync } from "child_process";
|
|
7919
8024
|
import { mkdirSync, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
7920
8025
|
import { userInfo } from "os";
|
|
7921
|
-
import { dirname as dirname10, join as
|
|
8026
|
+
import { dirname as dirname10, join as join18 } from "path";
|
|
7922
8027
|
|
|
7923
8028
|
// src/modules/keychain-backend.ts
|
|
7924
8029
|
import { createRequire as createRequire2 } from "module";
|
|
@@ -8052,7 +8157,7 @@ function restrictToCurrentUser(filePath) {
|
|
|
8052
8157
|
});
|
|
8053
8158
|
}
|
|
8054
8159
|
function insecureSessionFilePath(homeDir) {
|
|
8055
|
-
return
|
|
8160
|
+
return join18(userTierRoot(homeDir), INSECURE_SESSION_FILE_NAME);
|
|
8056
8161
|
}
|
|
8057
8162
|
function createSessionStorage(options = {}) {
|
|
8058
8163
|
const keychain = new ChunkedSecretStore(options.keychainBackend ?? createKeychainBackend());
|
|
@@ -8118,7 +8223,7 @@ function createSessionStorage(options = {}) {
|
|
|
8118
8223
|
}
|
|
8119
8224
|
|
|
8120
8225
|
// src/commands/login.ts
|
|
8121
|
-
function defaultLoginDeps() {
|
|
8226
|
+
function defaultLoginDeps(assetsDir) {
|
|
8122
8227
|
return {
|
|
8123
8228
|
storage: createSessionStorage(),
|
|
8124
8229
|
environment: () => resolveIdentityEnvironment(),
|
|
@@ -8126,10 +8231,12 @@ function defaultLoginDeps() {
|
|
|
8126
8231
|
browserLogin,
|
|
8127
8232
|
deviceLogin,
|
|
8128
8233
|
openUrl: openInDefaultBrowser,
|
|
8129
|
-
isInteractive: () => Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
8234
|
+
isInteractive: () => Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
8235
|
+
resultPages: () => loadResultPages(assetsDir)
|
|
8130
8236
|
};
|
|
8131
8237
|
}
|
|
8132
8238
|
var INSECURE_STORAGE_WARNING = "Insecure storage: the session is kept in a file protected only by your account's file permissions, not in the OS keychain. Run `spell logout` to remove it.";
|
|
8239
|
+
var MISSING_RESULT_PAGES_WARNING = "This arcane-cli install is missing its sign-in result pages (assets/sign-in/), so the browser will show a plain page. Reinstall the package to restore them.";
|
|
8133
8240
|
function safeErrorText(error) {
|
|
8134
8241
|
const message = error instanceof Error ? error.message : String(error);
|
|
8135
8242
|
return message.split("\n")[0].replace(/([?&](?:code|state|id_token|access_token|refresh_token)=)[^&\s]+/gi, "$1[redacted]");
|
|
@@ -8149,7 +8256,7 @@ function describeAccount(record) {
|
|
|
8149
8256
|
const who = record.email ? `${record.email} (${record.sub})` : record.sub;
|
|
8150
8257
|
return record.environment === "development" ? `${who} [development]` : who;
|
|
8151
8258
|
}
|
|
8152
|
-
async function runLogin(options, deps
|
|
8259
|
+
async function runLogin(options, deps) {
|
|
8153
8260
|
let environment;
|
|
8154
8261
|
try {
|
|
8155
8262
|
environment = deps.environment();
|
|
@@ -8195,14 +8302,16 @@ async function runLogin(options, deps = defaultLoginDeps()) {
|
|
|
8195
8302
|
});
|
|
8196
8303
|
saveValidatedSession(result);
|
|
8197
8304
|
} else {
|
|
8305
|
+
const pages = await deps.resultPages();
|
|
8306
|
+
if (!pages) printWarning(MISSING_RESULT_PAGES_WARNING);
|
|
8198
8307
|
printInfo("Opening your browser to sign in to Arcane. Come back here when it says you are signed in.");
|
|
8199
8308
|
await deps.browserLogin(config, {
|
|
8200
8309
|
scopes: SIGN_IN_SCOPES,
|
|
8201
8310
|
openUrl: deps.openUrl,
|
|
8311
|
+
pages: pages ?? void 0,
|
|
8202
8312
|
onValidated: saveValidatedSession,
|
|
8203
|
-
onCannotOpen: (
|
|
8204
|
-
printWarning("No browser could be opened.
|
|
8205
|
-
printInfo(url);
|
|
8313
|
+
onCannotOpen: () => {
|
|
8314
|
+
printWarning("No browser could be opened. Run `spell login --device-code` to sign in on another device.");
|
|
8206
8315
|
}
|
|
8207
8316
|
});
|
|
8208
8317
|
if (!record) throw new Error("The browser sign-in ended without storing a session.");
|
|
@@ -8316,15 +8425,15 @@ async function runWhoami(options, deps = defaultWhoamiDeps()) {
|
|
|
8316
8425
|
|
|
8317
8426
|
// src/commands/report.ts
|
|
8318
8427
|
import { access as access5 } from "fs/promises";
|
|
8319
|
-
import { join as
|
|
8428
|
+
import { join as join21 } from "path";
|
|
8320
8429
|
|
|
8321
8430
|
// src/modules/show-report/generate.ts
|
|
8322
|
-
import { mkdir as mkdir8, readdir as readdir11, readFile as
|
|
8323
|
-
import { basename as basename3, dirname as dirname11, isAbsolute, join as
|
|
8431
|
+
import { mkdir as mkdir8, readdir as readdir11, readFile as readFile18, writeFile as writeFile12 } from "fs/promises";
|
|
8432
|
+
import { basename as basename3, dirname as dirname11, isAbsolute, join as join20, relative as relative3, resolve as resolve4 } from "path";
|
|
8324
8433
|
|
|
8325
8434
|
// src/modules/show-report/model.ts
|
|
8326
|
-
import { readFile as
|
|
8327
|
-
import { join as
|
|
8435
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
8436
|
+
import { join as join19 } from "path";
|
|
8328
8437
|
|
|
8329
8438
|
// src/modules/show-report/plan-parser.ts
|
|
8330
8439
|
function parseFrontmatter(content) {
|
|
@@ -8597,7 +8706,7 @@ async function getCast(cwd, fromRef, toRef, generatedOutputs = []) {
|
|
|
8597
8706
|
// src/modules/show-report/model.ts
|
|
8598
8707
|
async function readOptional(path4) {
|
|
8599
8708
|
try {
|
|
8600
|
-
return await
|
|
8709
|
+
return await readFile17(path4, "utf8");
|
|
8601
8710
|
} catch {
|
|
8602
8711
|
return null;
|
|
8603
8712
|
}
|
|
@@ -8693,13 +8802,13 @@ function buildCorrections(ledgerRows, programName) {
|
|
|
8693
8802
|
};
|
|
8694
8803
|
}
|
|
8695
8804
|
async function buildShowReportModel(options) {
|
|
8696
|
-
const planAbsPath =
|
|
8697
|
-
const queueAbsPath =
|
|
8805
|
+
const planAbsPath = join19(options.rootDir, options.planRelPath);
|
|
8806
|
+
const queueAbsPath = join19(options.rootDir, options.queueRelPath);
|
|
8698
8807
|
const ledgerRelPath = "docs/verification-ledger.md";
|
|
8699
|
-
const ledgerAbsPath =
|
|
8700
|
-
const decisionsAbsPath =
|
|
8808
|
+
const ledgerAbsPath = join19(options.rootDir, ledgerRelPath);
|
|
8809
|
+
const decisionsAbsPath = join19(options.rootDir, "DECISIONS.md");
|
|
8701
8810
|
const [planContent, queueContent, ledgerContent, decisionsContent] = await Promise.all([
|
|
8702
|
-
|
|
8811
|
+
readFile17(planAbsPath, "utf8"),
|
|
8703
8812
|
readOptional(queueAbsPath),
|
|
8704
8813
|
readOptional(ledgerAbsPath),
|
|
8705
8814
|
readOptional(decisionsAbsPath)
|
|
@@ -8895,7 +9004,7 @@ function toPosix(path4) {
|
|
|
8895
9004
|
async function programFromPlan(rootDir, slug, planRelPath) {
|
|
8896
9005
|
let planContent;
|
|
8897
9006
|
try {
|
|
8898
|
-
planContent = await
|
|
9007
|
+
planContent = await readFile18(join20(rootDir, planRelPath), "utf8");
|
|
8899
9008
|
} catch {
|
|
8900
9009
|
return { skipped: { slug, reason: `no PLAN.md at ${planRelPath}` } };
|
|
8901
9010
|
}
|
|
@@ -8907,13 +9016,13 @@ async function programFromPlan(rootDir, slug, planRelPath) {
|
|
|
8907
9016
|
program: {
|
|
8908
9017
|
slug,
|
|
8909
9018
|
planRelPath,
|
|
8910
|
-
queueRelPath: toPosix(
|
|
9019
|
+
queueRelPath: toPosix(join20(dirname11(planRelPath), "OPERATOR-QUEUE.md")),
|
|
8911
9020
|
ledgerProgramName: programNameFromTitle(frontmatter.title ?? slug)
|
|
8912
9021
|
}
|
|
8913
9022
|
};
|
|
8914
9023
|
}
|
|
8915
9024
|
async function discoverPrograms(rootDir) {
|
|
8916
|
-
const plansDir =
|
|
9025
|
+
const plansDir = join20(rootDir, "docs", "plans");
|
|
8917
9026
|
let entries;
|
|
8918
9027
|
try {
|
|
8919
9028
|
entries = (await readdir11(plansDir, { withFileTypes: true })).filter((e) => e.isDirectory());
|
|
@@ -8940,14 +9049,14 @@ function normalizeLineEndings2(content) {
|
|
|
8940
9049
|
}
|
|
8941
9050
|
async function readOptionalFile(path4) {
|
|
8942
9051
|
try {
|
|
8943
|
-
return await
|
|
9052
|
+
return await readFile18(path4, "utf8");
|
|
8944
9053
|
} catch (error) {
|
|
8945
9054
|
if (error.code === "ENOENT") return null;
|
|
8946
9055
|
throw error;
|
|
8947
9056
|
}
|
|
8948
9057
|
}
|
|
8949
9058
|
async function generateReport(rootDir, program2, templateHtml, theme = "auto") {
|
|
8950
|
-
const planContent = await
|
|
9059
|
+
const planContent = await readFile18(join20(rootDir, program2.planRelPath), "utf8");
|
|
8951
9060
|
const frontmatter = parseFrontmatter(planContent);
|
|
8952
9061
|
const model = await buildShowReportModel({
|
|
8953
9062
|
rootDir,
|
|
@@ -8967,7 +9076,7 @@ async function generateReport(rootDir, program2, templateHtml, theme = "auto") {
|
|
|
8967
9076
|
};
|
|
8968
9077
|
}
|
|
8969
9078
|
async function runReportGeneration(options) {
|
|
8970
|
-
const templateHtml = await
|
|
9079
|
+
const templateHtml = await readFile18(options.templatePath, "utf8");
|
|
8971
9080
|
const theme = options.theme ?? "auto";
|
|
8972
9081
|
const drifted = [];
|
|
8973
9082
|
const repaired = [];
|
|
@@ -8985,12 +9094,12 @@ async function runReportGeneration(options) {
|
|
|
8985
9094
|
[`${outRel}/show-report.html`, generated.html]
|
|
8986
9095
|
];
|
|
8987
9096
|
for (const [relPath, expected] of outputs) {
|
|
8988
|
-
const actual = await readOptionalFile(
|
|
9097
|
+
const actual = await readOptionalFile(join20(options.rootDir, relPath));
|
|
8989
9098
|
if (actual === null || normalizeLineEndings2(actual) !== normalizeLineEndings2(expected)) {
|
|
8990
9099
|
drifted.push(`${relPath} does not match a regeneration from its sources.`);
|
|
8991
9100
|
if (options.mode === "write") {
|
|
8992
9101
|
await mkdir8(outAbs, { recursive: true });
|
|
8993
|
-
await writeFile12(
|
|
9102
|
+
await writeFile12(join20(options.rootDir, relPath), expected, "utf8");
|
|
8994
9103
|
repaired.push(relPath);
|
|
8995
9104
|
}
|
|
8996
9105
|
}
|
|
@@ -9001,7 +9110,7 @@ async function runReportGeneration(options) {
|
|
|
9001
9110
|
|
|
9002
9111
|
// src/commands/report.ts
|
|
9003
9112
|
async function runReport(targetDir, options = {}, assetsDir) {
|
|
9004
|
-
const templatePath =
|
|
9113
|
+
const templatePath = join21(assetsDir, "report", "show-report.template.html");
|
|
9005
9114
|
try {
|
|
9006
9115
|
await access5(templatePath);
|
|
9007
9116
|
} catch {
|
|
@@ -9078,7 +9187,7 @@ async function runReport(targetDir, options = {}, assetsDir) {
|
|
|
9078
9187
|
import { realpathSync } from "fs";
|
|
9079
9188
|
var require3 = createRequire3(import.meta.url);
|
|
9080
9189
|
var pkg = require3("../package.json");
|
|
9081
|
-
var ASSETS_DIR = process.env["ARCANE_ASSETS_DIR"] ??
|
|
9190
|
+
var ASSETS_DIR = process.env["ARCANE_ASSETS_DIR"] ?? join22(dirname12(fileURLToPath(import.meta.url)), "assets");
|
|
9082
9191
|
var program = new Command();
|
|
9083
9192
|
var USER_FLAG_DESCRIPTION = "Operate on the per-user tier at ~/.arcane \u2014 spells shared by every repository on this machine, fanned out to ~/.agents/skills (Codex, Copilot) and ~/.claude/commands (Claude Code)";
|
|
9084
9193
|
var USER_TIER_AGENTS_DESCRIPTION = "Operate on the per-user tier at ~/.arcane \u2014 one agent roster shared by every repository on this machine, rendered to ~/.copilot/agents (VS Code agent modes)";
|
|
@@ -9205,7 +9314,7 @@ program.command("login").description("Sign in to your Arcane account (browser by
|
|
|
9205
9314
|
"--insecure-storage",
|
|
9206
9315
|
"Keep the session in a file under ~/.arcane instead of the OS keychain; protected only by your account's file permissions"
|
|
9207
9316
|
).action(async (opts) => {
|
|
9208
|
-
await runLogin({ deviceCode: opts.deviceCode, insecureStorage: opts.insecureStorage });
|
|
9317
|
+
await runLogin({ deviceCode: opts.deviceCode, insecureStorage: opts.insecureStorage }, defaultLoginDeps(ASSETS_DIR));
|
|
9209
9318
|
});
|
|
9210
9319
|
program.command("logout").description("Sign out on this machine: remove the stored session").action(() => {
|
|
9211
9320
|
runLogout();
|