@alphafox/cli 0.1.5 → 0.3.2

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 (104) hide show
  1. package/README.md +42 -4
  2. package/dist/auth/browser-login.d.ts +30 -0
  3. package/dist/auth/browser-login.js +193 -0
  4. package/dist/auth/loopback-callback.d.ts +32 -0
  5. package/dist/auth/loopback-callback.js +175 -0
  6. package/dist/auth/open-browser.d.ts +15 -0
  7. package/dist/auth/open-browser.js +54 -0
  8. package/dist/auth/refresh.d.ts +27 -2
  9. package/dist/auth/refresh.js +52 -15
  10. package/dist/catalog/allowlist.d.ts +20 -4
  11. package/dist/catalog/allowlist.js +126 -25
  12. package/dist/catalog/command-tree.d.ts +35 -0
  13. package/dist/catalog/command-tree.js +119 -0
  14. package/dist/catalog/compatibility.d.ts +23 -0
  15. package/dist/catalog/compatibility.js +58 -0
  16. package/dist/catalog/generated/registry.json +6137 -0
  17. package/dist/catalog/generated/schemas.json +31036 -0
  18. package/dist/catalog/operations.d.ts +76 -3
  19. package/dist/catalog/operations.js +87 -213
  20. package/dist/catalog/validate-body.d.ts +22 -0
  21. package/dist/catalog/validate-body.js +98 -0
  22. package/dist/commands/request-body.d.ts +21 -0
  23. package/dist/commands/request-body.js +92 -0
  24. package/dist/commands/run.js +283 -162
  25. package/dist/config/profiles.js +3 -3
  26. package/dist/engine-backtest/errors.d.ts +18 -0
  27. package/dist/engine-backtest/errors.js +26 -0
  28. package/dist/engine-backtest/fetch-runtime.d.ts +38 -0
  29. package/dist/engine-backtest/fetch-runtime.js +170 -0
  30. package/dist/engine-backtest/native-import.d.ts +1 -0
  31. package/dist/engine-backtest/native-import.js +10 -0
  32. package/dist/engine-backtest/parse-args.d.ts +6 -0
  33. package/dist/engine-backtest/parse-args.js +281 -0
  34. package/dist/engine-backtest/persist.d.ts +32 -0
  35. package/dist/engine-backtest/persist.js +94 -0
  36. package/dist/engine-backtest/replay-timeframe.d.ts +16 -0
  37. package/dist/engine-backtest/replay-timeframe.js +48 -0
  38. package/dist/engine-backtest/resolve-packages.d.ts +27 -0
  39. package/dist/engine-backtest/resolve-packages.js +288 -0
  40. package/dist/engine-backtest/run-command.d.ts +38 -0
  41. package/dist/engine-backtest/run-command.js +540 -0
  42. package/dist/engine-backtest/types.d.ts +249 -0
  43. package/dist/engine-backtest/types.js +2 -0
  44. package/dist/envelope.d.ts +3 -0
  45. package/dist/envelope.js +43 -3
  46. package/dist/http/client.js +18 -15
  47. package/dist/index.d.ts +16 -5
  48. package/dist/index.js +47 -1
  49. package/dist/install/exec.d.ts +13 -0
  50. package/dist/install/exec.js +73 -0
  51. package/dist/install/package-root.d.ts +4 -0
  52. package/dist/install/package-root.js +59 -0
  53. package/dist/install/types.d.ts +69 -0
  54. package/dist/install/types.js +26 -0
  55. package/dist/install/wizard.d.ts +21 -0
  56. package/dist/install/wizard.js +332 -0
  57. package/dist/keychain/linux-secret-service.d.ts +11 -0
  58. package/dist/keychain/linux-secret-service.js +93 -0
  59. package/dist/keychain/store.d.ts +20 -1
  60. package/dist/keychain/store.js +94 -6
  61. package/dist/keychain/windows-credential.d.ts +13 -0
  62. package/dist/keychain/windows-credential.js +176 -0
  63. package/dist/safety/confirmation.d.ts +10 -3
  64. package/dist/safety/confirmation.js +27 -4
  65. package/dist/version.d.ts +2 -2
  66. package/dist/version.js +3 -2
  67. package/docs/.nojekyll +0 -0
  68. package/docs/agents/domain.md +51 -0
  69. package/docs/agents/issue-tracker.md +156 -0
  70. package/docs/agents/triage-labels.md +18 -0
  71. package/docs/alphafox-cli-installation-guide.md +105 -0
  72. package/docs/e2e-staging.md +76 -6
  73. package/docs/favicon.svg +4 -0
  74. package/docs/index.html +748 -0
  75. package/docs/logo/alphafox-mark.svg +4 -0
  76. package/docs/logo/alphafox-wordmark-black-en.svg +17 -0
  77. package/docs/logo/alphafox-wordmark-white-en.svg +16 -0
  78. package/docs/release-supply-chain.md +107 -26
  79. package/package.json +10 -3
  80. package/skills/account/SKILL.md +8 -6
  81. package/skills/admin/SKILL.md +9 -4
  82. package/skills/alphafox-shared/SKILL.md +44 -14
  83. package/skills/auth/SKILL.md +21 -6
  84. package/skills/engine-backtest/SKILL.md +71 -0
  85. package/skills/exchange/SKILL.md +10 -3
  86. package/skills/market/SKILL.md +9 -4
  87. package/skills/notification/SKILL.md +8 -3
  88. package/skills/strategy/SKILL.md +23 -9
  89. package/skills/trading/SKILL.md +18 -5
  90. package/vendor/backtest-runner/NOTICE.md +1 -0
  91. package/vendor/backtest-runner/README.md +97 -0
  92. package/vendor/backtest-runner/index.d.ts +423 -0
  93. package/vendor/backtest-runner/index.mjs +59 -0
  94. package/vendor/backtest-runner/lib/abortable.mjs +23 -0
  95. package/vendor/backtest-runner/lib/cache.mjs +159 -0
  96. package/vendor/backtest-runner/lib/coverage.mjs +238 -0
  97. package/vendor/backtest-runner/lib/encode.mjs +28 -0
  98. package/vendor/backtest-runner/lib/exchanges.mjs +143 -0
  99. package/vendor/backtest-runner/lib/proxy.mjs +78 -0
  100. package/vendor/backtest-runner/lib/scenario.mjs +66 -0
  101. package/vendor/backtest-runner/lib/series.mjs +370 -0
  102. package/vendor/backtest-runner/lib/tape-loader.mjs +614 -0
  103. package/vendor/backtest-runner/lib/timeframes.mjs +55 -0
  104. package/vendor/backtest-runner/package.json +13 -0
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findAlphafoxPackageRoot = findAlphafoxPackageRoot;
4
+ exports.packageHasSkills = packageHasSkills;
5
+ exports.globalPackageRoot = globalPackageRoot;
6
+ exports.globalBinPath = globalBinPath;
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = require("node:path");
9
+ const version_1 = require("../version");
10
+ function findAlphafoxPackageRoot(startDirs) {
11
+ for (const start of startDirs) {
12
+ const found = walkForPackage(start);
13
+ if (found)
14
+ return found;
15
+ }
16
+ return null;
17
+ }
18
+ function packageHasSkills(packageRoot) {
19
+ const skillsDir = (0, node_path_1.join)(packageRoot, "skills");
20
+ if (!(0, node_fs_1.existsSync)(skillsDir))
21
+ return false;
22
+ try {
23
+ return (0, node_fs_1.readdirSync)(skillsDir, { withFileTypes: true }).some((entry) => entry.isDirectory() &&
24
+ (0, node_fs_1.existsSync)((0, node_path_1.join)(skillsDir, entry.name, "SKILL.md")));
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ function globalPackageRoot(npmRootGlobal) {
31
+ return (0, node_path_1.join)(npmRootGlobal, "@alphafox", "cli");
32
+ }
33
+ function globalBinPath(npmPrefixGlobal, platform = process.platform) {
34
+ if (platform === "win32") {
35
+ return (0, node_path_1.join)(npmPrefixGlobal, "alphafox.cmd");
36
+ }
37
+ return (0, node_path_1.join)(npmPrefixGlobal, "bin", "alphafox");
38
+ }
39
+ function walkForPackage(startDir) {
40
+ let dir = startDir;
41
+ for (let i = 0; i < 10; i += 1) {
42
+ const pkgPath = (0, node_path_1.join)(dir, "package.json");
43
+ if ((0, node_fs_1.existsSync)(pkgPath) && packageHasSkills(dir)) {
44
+ try {
45
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)(pkgPath, "utf8"));
46
+ if (parsed.name === version_1.CLI_PACKAGE)
47
+ return dir;
48
+ }
49
+ catch {
50
+ // keep walking
51
+ }
52
+ }
53
+ const parent = (0, node_path_1.dirname)(dir);
54
+ if (parent === dir)
55
+ break;
56
+ dir = parent;
57
+ }
58
+ return null;
59
+ }
@@ -0,0 +1,69 @@
1
+ export declare const SKILLS_GITHUB_SOURCE = "alphafoxai/alphafox-cli";
2
+ export declare const SKILLS_NAME_PREFIX = "alphafox-";
3
+ export declare const AGENT_INSTALL_GUIDE_URL = "https://raw.githubusercontent.com/alphafoxai/alphafox-cli/main/docs/alphafox-cli-installation-guide.md";
4
+ export declare const AGENT_INSTALL_GUIDE_BLOB_URL = "https://github.com/alphafoxai/alphafox-cli/blob/main/docs/alphafox-cli-installation-guide.md";
5
+ export type InstallCliAction = "installed" | "upgraded" | "skipped" | "planned";
6
+ export type InstallSkillsAction = "installed" | "skipped" | "planned" | "failed";
7
+ export type InstallAuthAction = "completed" | "skipped" | "failed" | "planned";
8
+ export interface InstallCliStep {
9
+ readonly action: InstallCliAction;
10
+ readonly version?: string;
11
+ readonly previousVersion?: string;
12
+ readonly latestVersion?: string;
13
+ }
14
+ export interface InstallSkillsStep {
15
+ readonly action: InstallSkillsAction;
16
+ readonly source?: string;
17
+ readonly scope: "global";
18
+ readonly alreadyPresent?: boolean;
19
+ }
20
+ export interface InstallAuthStep {
21
+ readonly action: InstallAuthAction;
22
+ readonly reason?: string;
23
+ }
24
+ export interface InstallResult {
25
+ readonly cli: InstallCliStep;
26
+ readonly skills: InstallSkillsStep;
27
+ readonly auth: InstallAuthStep;
28
+ readonly next: readonly string[];
29
+ }
30
+ export interface InstallFlags {
31
+ readonly format: "json" | "jsonl" | "text";
32
+ readonly yes: boolean;
33
+ readonly dryRun: boolean;
34
+ readonly noInput: boolean;
35
+ readonly jq?: string;
36
+ readonly noAuth: boolean;
37
+ readonly help: boolean;
38
+ }
39
+ export interface ExecResult {
40
+ readonly stdout: string;
41
+ readonly stderr: string;
42
+ }
43
+ export interface InstallRunner {
44
+ readonly exec: (command: string, args: readonly string[], options?: {
45
+ readonly timeoutMs?: number;
46
+ }) => Promise<ExecResult>;
47
+ readonly execInherit: (command: string, args: readonly string[], options?: {
48
+ readonly timeoutMs?: number;
49
+ }) => Promise<void>;
50
+ readonly isTty: () => boolean;
51
+ readonly confirm: (message: string) => Promise<boolean>;
52
+ readonly log: (message: string) => void;
53
+ readonly packageSearchDirs: () => readonly string[];
54
+ readonly env: NodeJS.ProcessEnv;
55
+ }
56
+ export declare class InstallError extends Error {
57
+ readonly type: string;
58
+ readonly subtype?: string;
59
+ readonly hint?: string;
60
+ readonly details?: unknown;
61
+ constructor(input: {
62
+ readonly message: string;
63
+ readonly type?: string;
64
+ readonly subtype?: string;
65
+ readonly hint?: string;
66
+ readonly details?: unknown;
67
+ });
68
+ }
69
+ export declare function isInstallError(value: unknown): value is InstallError;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InstallError = exports.AGENT_INSTALL_GUIDE_BLOB_URL = exports.AGENT_INSTALL_GUIDE_URL = exports.SKILLS_NAME_PREFIX = exports.SKILLS_GITHUB_SOURCE = void 0;
4
+ exports.isInstallError = isInstallError;
5
+ exports.SKILLS_GITHUB_SOURCE = "alphafoxai/alphafox-cli";
6
+ exports.SKILLS_NAME_PREFIX = "alphafox-";
7
+ exports.AGENT_INSTALL_GUIDE_URL = "https://raw.githubusercontent.com/alphafoxai/alphafox-cli/main/docs/alphafox-cli-installation-guide.md";
8
+ exports.AGENT_INSTALL_GUIDE_BLOB_URL = "https://github.com/alphafoxai/alphafox-cli/blob/main/docs/alphafox-cli-installation-guide.md";
9
+ class InstallError extends Error {
10
+ type;
11
+ subtype;
12
+ hint;
13
+ details;
14
+ constructor(input) {
15
+ super(input.message);
16
+ this.name = "InstallError";
17
+ this.type = input.type ?? "runtime";
18
+ this.subtype = input.subtype;
19
+ this.hint = input.hint;
20
+ this.details = input.details;
21
+ }
22
+ }
23
+ exports.InstallError = InstallError;
24
+ function isInstallError(value) {
25
+ return value instanceof InstallError;
26
+ }
@@ -0,0 +1,21 @@
1
+ import { type InstallAuthStep, type InstallFlags, type InstallResult, type InstallRunner } from "./types";
2
+ export declare function parseInstallArgs(args: readonly string[]): {
3
+ readonly noAuth: boolean;
4
+ readonly help: boolean;
5
+ readonly unknown: readonly string[];
6
+ };
7
+ export declare function semverLessThan(a: string, b: string): boolean;
8
+ export declare function parseNpmListVersion(output: string): string | null;
9
+ export declare function skillsListHasAlphafox(output: string): boolean;
10
+ export declare function nextSteps(input: {
11
+ readonly auth: InstallAuthStep;
12
+ readonly dryRun: boolean;
13
+ }): string[];
14
+ export declare function runInstallWizard(flags: InstallFlags, env?: NodeJS.ProcessEnv, runner?: InstallRunner): Promise<InstallResult>;
15
+ export declare function installHelpData(): {
16
+ readonly name: string;
17
+ readonly usage: readonly string[];
18
+ readonly description: string;
19
+ readonly agentGuide: string;
20
+ };
21
+ export declare function resolveLocalSkillsDir(searchDirs: readonly string[]): string | null;
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseInstallArgs = parseInstallArgs;
4
+ exports.semverLessThan = semverLessThan;
5
+ exports.parseNpmListVersion = parseNpmListVersion;
6
+ exports.skillsListHasAlphafox = skillsListHasAlphafox;
7
+ exports.nextSteps = nextSteps;
8
+ exports.runInstallWizard = runInstallWizard;
9
+ exports.installHelpData = installHelpData;
10
+ exports.resolveLocalSkillsDir = resolveLocalSkillsDir;
11
+ const node_fs_1 = require("node:fs");
12
+ const node_path_1 = require("node:path");
13
+ const version_1 = require("../version");
14
+ const exec_1 = require("./exec");
15
+ const package_root_1 = require("./package-root");
16
+ const types_1 = require("./types");
17
+ const NPM_TIMEOUT_MS = 120_000;
18
+ const SKILLS_TIMEOUT_MS = 120_000;
19
+ function parseInstallArgs(args) {
20
+ let noAuth = false;
21
+ let help = false;
22
+ const unknown = [];
23
+ for (const arg of args) {
24
+ if (arg === "--no-auth") {
25
+ noAuth = true;
26
+ }
27
+ else if (arg === "--help" || arg === "-h") {
28
+ help = true;
29
+ }
30
+ else {
31
+ unknown.push(arg);
32
+ }
33
+ }
34
+ return { noAuth, help, unknown };
35
+ }
36
+ function semverLessThan(a, b) {
37
+ const pa = stripPrerelease(a).split(".").map((part) => Number(part) || 0);
38
+ const pb = stripPrerelease(b).split(".").map((part) => Number(part) || 0);
39
+ for (let i = 0; i < 3; i += 1) {
40
+ if ((pa[i] ?? 0) < (pb[i] ?? 0))
41
+ return true;
42
+ if ((pa[i] ?? 0) > (pb[i] ?? 0))
43
+ return false;
44
+ }
45
+ return false;
46
+ }
47
+ function parseNpmListVersion(output) {
48
+ const match = output.match(/@(\d+\.\d+\.\d+[^\s]*)/);
49
+ return match?.[1] ?? null;
50
+ }
51
+ function skillsListHasAlphafox(output) {
52
+ const prefix = types_1.SKILLS_NAME_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
53
+ return new RegExp(`(^|\\s)${prefix}[\\w-]+`, "m").test(output);
54
+ }
55
+ function nextSteps(input) {
56
+ const steps = [
57
+ "Restart the AI tool so newly installed Skills are loaded.",
58
+ ];
59
+ if (input.auth.action === "skipped" || input.auth.action === "planned") {
60
+ steps.push("alphafox auth login --browser --format json --no-input", "alphafox auth login --no-wait --format json --no-input");
61
+ }
62
+ steps.push("alphafox doctor --format json --no-input", `Agent install guide: ${types_1.AGENT_INSTALL_GUIDE_BLOB_URL}`);
63
+ if (input.dryRun) {
64
+ steps.unshift("This was --dry-run; re-run without it to apply changes.");
65
+ }
66
+ return steps;
67
+ }
68
+ async function runInstallWizard(flags, env = process.env, runner = (0, exec_1.createDefaultInstallRunner)(env, defaultSearchDirs())) {
69
+ runner.log("Setting up Alphafox CLI...");
70
+ const installedVer = await readGloballyInstalledVersion(runner);
71
+ const latestVer = await readLatestVersion(runner);
72
+ const needsUpgrade = Boolean(installedVer) &&
73
+ Boolean(latestVer) &&
74
+ semverLessThan(installedVer, latestVer);
75
+ const cli = await stepInstallCli(flags, runner, {
76
+ installedVer,
77
+ latestVer,
78
+ needsUpgrade,
79
+ });
80
+ const skills = await stepInstallSkills(flags, runner);
81
+ const auth = await stepAuth(flags, runner);
82
+ return {
83
+ cli,
84
+ skills,
85
+ auth,
86
+ next: nextSteps({ auth, dryRun: flags.dryRun }),
87
+ };
88
+ }
89
+ function defaultSearchDirs() {
90
+ return [__dirname, process.cwd()];
91
+ }
92
+ function stripPrerelease(version) {
93
+ return version.replace(/-.*$/, "");
94
+ }
95
+ async function readGloballyInstalledVersion(runner) {
96
+ try {
97
+ const { stdout, stderr } = await runner.exec("npm", ["list", "-g", version_1.CLI_PACKAGE, "--depth=0"], { timeoutMs: 15_000 });
98
+ return parseNpmListVersion(`${stdout}\n${stderr}`);
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ async function readLatestVersion(runner) {
105
+ try {
106
+ const { stdout } = await runner.exec("npm", ["view", version_1.CLI_PACKAGE, "version"], {
107
+ timeoutMs: 15_000,
108
+ });
109
+ const ver = stdout.trim();
110
+ return /^\d+\.\d+\.\d+/.test(ver) ? ver : null;
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ }
116
+ async function npmRootGlobal(runner) {
117
+ try {
118
+ const { stdout } = await runner.exec("npm", ["root", "-g"], {
119
+ timeoutMs: 15_000,
120
+ });
121
+ const root = stdout.trim();
122
+ return root.length > 0 ? root : null;
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ }
128
+ async function npmPrefixGlobal(runner) {
129
+ try {
130
+ const { stdout } = await runner.exec("npm", ["prefix", "-g"], {
131
+ timeoutMs: 15_000,
132
+ });
133
+ const prefix = stdout.trim();
134
+ return prefix.length > 0 ? prefix : null;
135
+ }
136
+ catch {
137
+ return null;
138
+ }
139
+ }
140
+ async function stepInstallCli(flags, runner, versions) {
141
+ const { installedVer, latestVer, needsUpgrade } = versions;
142
+ if (installedVer && !needsUpgrade) {
143
+ runner.log(`Already installed (${installedVer}). Skipped`);
144
+ return {
145
+ action: flags.dryRun ? "planned" : "skipped",
146
+ version: installedVer,
147
+ latestVersion: latestVer ?? undefined,
148
+ };
149
+ }
150
+ if (flags.dryRun) {
151
+ runner.log(needsUpgrade
152
+ ? `Would upgrade ${version_1.CLI_PACKAGE} (${installedVer} → ${latestVer ?? "latest"})`
153
+ : `Would install ${version_1.CLI_PACKAGE} globally`);
154
+ return {
155
+ action: "planned",
156
+ previousVersion: installedVer ?? undefined,
157
+ latestVersion: latestVer ?? undefined,
158
+ };
159
+ }
160
+ runner.log(needsUpgrade
161
+ ? `Upgrading ${version_1.CLI_PACKAGE} (${installedVer} → ${latestVer})...`
162
+ : `Installing ${version_1.CLI_PACKAGE} globally...`);
163
+ try {
164
+ await runner.exec("npm", ["install", "-g", version_1.CLI_PACKAGE], {
165
+ timeoutMs: NPM_TIMEOUT_MS,
166
+ });
167
+ }
168
+ catch (err) {
169
+ throw new types_1.InstallError({
170
+ type: "install",
171
+ subtype: "npm_global_failed",
172
+ message: `Failed to install ${version_1.CLI_PACKAGE} globally.`,
173
+ hint: `npm install -g ${version_1.CLI_PACKAGE}`,
174
+ details: err instanceof Error ? err.message : String(err),
175
+ });
176
+ }
177
+ const after = (await readGloballyInstalledVersion(runner)) ?? latestVer ?? version_1.CLI_VERSION;
178
+ runner.log(needsUpgrade ? `Upgraded to ${after}` : "Installed globally");
179
+ return {
180
+ action: needsUpgrade ? "upgraded" : "installed",
181
+ version: after,
182
+ previousVersion: installedVer ?? undefined,
183
+ latestVersion: latestVer ?? undefined,
184
+ };
185
+ }
186
+ async function resolveSkillsSources(runner) {
187
+ const override = runner.env.ALPHAFOX_SKILLS_SOURCE?.trim();
188
+ if (override)
189
+ return [override];
190
+ const sources = [];
191
+ const npmRoot = await npmRootGlobal(runner);
192
+ if (npmRoot) {
193
+ const globalRoot = (0, package_root_1.globalPackageRoot)(npmRoot);
194
+ if ((0, package_root_1.packageHasSkills)(globalRoot))
195
+ sources.push(globalRoot);
196
+ }
197
+ const localRoot = (0, package_root_1.findAlphafoxPackageRoot)(runner.packageSearchDirs());
198
+ if (localRoot && !sources.includes(localRoot) && (0, package_root_1.packageHasSkills)(localRoot)) {
199
+ sources.push(localRoot);
200
+ }
201
+ sources.push(types_1.SKILLS_GITHUB_SOURCE);
202
+ return sources;
203
+ }
204
+ async function skillsAlreadyInstalled(runner) {
205
+ try {
206
+ const { stdout, stderr } = await runner.exec("npx", ["-y", "skills", "ls", "-g"], { timeoutMs: SKILLS_TIMEOUT_MS });
207
+ return skillsListHasAlphafox(`${stdout}\n${stderr}`);
208
+ }
209
+ catch {
210
+ return false;
211
+ }
212
+ }
213
+ async function stepInstallSkills(flags, runner) {
214
+ const sources = await resolveSkillsSources(runner);
215
+ const already = flags.dryRun ? false : await skillsAlreadyInstalled(runner);
216
+ if (already) {
217
+ runner.log("Skills already installed. Skipped");
218
+ return {
219
+ action: "skipped",
220
+ source: sources[0],
221
+ scope: "global",
222
+ alreadyPresent: true,
223
+ };
224
+ }
225
+ if (flags.dryRun) {
226
+ runner.log(`Would install Skills from ${sources[0] ?? types_1.SKILLS_GITHUB_SOURCE}`);
227
+ return {
228
+ action: "planned",
229
+ source: sources[0] ?? types_1.SKILLS_GITHUB_SOURCE,
230
+ scope: "global",
231
+ };
232
+ }
233
+ runner.log("Installing AI Skills...");
234
+ let lastError;
235
+ for (const source of sources) {
236
+ try {
237
+ await runner.exec("npx", ["-y", "skills", "add", source, "-y", "-g"], { timeoutMs: SKILLS_TIMEOUT_MS });
238
+ runner.log(`Skills installed (${source})`);
239
+ return { action: "installed", source, scope: "global" };
240
+ }
241
+ catch (err) {
242
+ lastError = err;
243
+ }
244
+ }
245
+ throw new types_1.InstallError({
246
+ type: "install",
247
+ subtype: "skills_add_failed",
248
+ message: "Failed to install Agent Skills.",
249
+ hint: `npx skills add ${types_1.SKILLS_GITHUB_SOURCE} -y -g`,
250
+ details: lastError instanceof Error ? lastError.message : String(lastError),
251
+ });
252
+ }
253
+ async function resolveAlphafoxBin(runner) {
254
+ const prefix = await npmPrefixGlobal(runner);
255
+ if (prefix) {
256
+ const bin = (0, package_root_1.globalBinPath)(prefix);
257
+ if ((0, node_fs_1.existsSync)(bin))
258
+ return bin;
259
+ }
260
+ try {
261
+ const { stdout } = await runner.exec(process.platform === "win32" ? "where" : "which", ["alphafox"], { timeoutMs: 10_000 });
262
+ const first = stdout.split(/\r?\n/)[0]?.trim();
263
+ return first || null;
264
+ }
265
+ catch {
266
+ return null;
267
+ }
268
+ }
269
+ async function stepAuth(flags, runner) {
270
+ if (flags.noAuth) {
271
+ return { action: "skipped", reason: "no-auth" };
272
+ }
273
+ if (flags.noInput || !runner.isTty()) {
274
+ if (flags.dryRun) {
275
+ return { action: "planned", reason: "non-interactive" };
276
+ }
277
+ runner.log("To finish setup, run:\n alphafox auth login --browser\n alphafox auth login --no-wait --format json --no-input");
278
+ return { action: "skipped", reason: "non-interactive" };
279
+ }
280
+ if (flags.dryRun) {
281
+ runner.log("Would prompt for alphafox auth login --browser");
282
+ return { action: "planned", reason: "tty" };
283
+ }
284
+ const yes = await runner.confirm("Log in now so Agents can call the public Application API?");
285
+ if (!yes) {
286
+ runner.log("Skipped login. Run alphafox auth login later.");
287
+ return { action: "skipped", reason: "declined" };
288
+ }
289
+ const bin = await resolveAlphafoxBin(runner);
290
+ if (!bin) {
291
+ throw new types_1.InstallError({
292
+ type: "install",
293
+ subtype: "cli_bin_missing",
294
+ message: "alphafox binary not found after install.",
295
+ hint: "Ensure the npm global bin directory is on PATH, then run alphafox auth login --browser.",
296
+ });
297
+ }
298
+ try {
299
+ await runner.execInherit(bin, ["auth", "login", "--browser"], {
300
+ timeoutMs: 10 * 60_000,
301
+ });
302
+ runner.log("Login complete");
303
+ return { action: "completed" };
304
+ }
305
+ catch (err) {
306
+ runner.log("Login failed. Run alphafox auth login --browser to retry.");
307
+ return {
308
+ action: "failed",
309
+ reason: err instanceof Error ? err.message : String(err),
310
+ };
311
+ }
312
+ }
313
+ function installHelpData() {
314
+ return {
315
+ name: "install",
316
+ usage: [
317
+ "npx @alphafox/cli@latest install",
318
+ "alphafox install",
319
+ "alphafox install --no-auth",
320
+ "alphafox install --dry-run",
321
+ ],
322
+ description: "Install @alphafox/cli globally and copy co-versioned Agent Skills into detected agents (Cursor, Claude Code, Codex, …) via npx skills add.",
323
+ agentGuide: types_1.AGENT_INSTALL_GUIDE_BLOB_URL,
324
+ };
325
+ }
326
+ function resolveLocalSkillsDir(searchDirs) {
327
+ const root = (0, package_root_1.findAlphafoxPackageRoot)(searchDirs);
328
+ if (!root)
329
+ return null;
330
+ const skillsDir = (0, node_path_1.join)(root, "skills");
331
+ return (0, package_root_1.packageHasSkills)(root) ? skillsDir : null;
332
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Linux Secret Service via `secret-tool` (libsecret).
3
+ * Tokens travel on stdin/stdout, never as argv.
4
+ */
5
+ export declare const LINUX_SECRET_LABEL_PREFIX = "alphafox-cli";
6
+ export declare function linuxSecretServiceArgs(action: "store" | "lookup" | "clear", service: string, account: string): readonly string[];
7
+ export declare function linuxSecretToolBin(env?: NodeJS.ProcessEnv): string;
8
+ export declare function linuxSecretServiceWrite(service: string, account: string, payload: string, env?: NodeJS.ProcessEnv): boolean;
9
+ export declare function linuxSecretServiceRead(service: string, account: string, env?: NodeJS.ProcessEnv): string | null;
10
+ export declare function linuxSecretServiceDelete(service: string, account: string, env?: NodeJS.ProcessEnv): void;
11
+ export declare function linuxSecretServiceAvailable(env?: NodeJS.ProcessEnv): boolean;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ /**
3
+ * Linux Secret Service via `secret-tool` (libsecret).
4
+ * Tokens travel on stdin/stdout, never as argv.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.LINUX_SECRET_LABEL_PREFIX = void 0;
8
+ exports.linuxSecretServiceArgs = linuxSecretServiceArgs;
9
+ exports.linuxSecretToolBin = linuxSecretToolBin;
10
+ exports.linuxSecretServiceWrite = linuxSecretServiceWrite;
11
+ exports.linuxSecretServiceRead = linuxSecretServiceRead;
12
+ exports.linuxSecretServiceDelete = linuxSecretServiceDelete;
13
+ exports.linuxSecretServiceAvailable = linuxSecretServiceAvailable;
14
+ const node_child_process_1 = require("node:child_process");
15
+ exports.LINUX_SECRET_LABEL_PREFIX = "alphafox-cli";
16
+ function linuxSecretServiceArgs(action, service, account) {
17
+ if (action === "store") {
18
+ return [
19
+ "store",
20
+ "--label",
21
+ `${exports.LINUX_SECRET_LABEL_PREFIX} ${service}`,
22
+ "service",
23
+ service,
24
+ "account",
25
+ account,
26
+ ];
27
+ }
28
+ if (action === "lookup") {
29
+ return ["lookup", "service", service, "account", account];
30
+ }
31
+ return ["clear", "service", service, "account", account];
32
+ }
33
+ function linuxSecretToolBin(env = process.env) {
34
+ return env.ALPHAFOX_SECRET_TOOL?.trim() || "secret-tool";
35
+ }
36
+ function childEnv(env) {
37
+ return { ...process.env, ...env };
38
+ }
39
+ function linuxSecretServiceWrite(service, account, payload, env = process.env) {
40
+ try {
41
+ (0, node_child_process_1.execFileSync)(linuxSecretToolBin(env), [...linuxSecretServiceArgs("store", service, account)], {
42
+ input: payload,
43
+ encoding: "utf8",
44
+ stdio: ["pipe", "ignore", "ignore"],
45
+ timeout: 10_000,
46
+ env: childEnv(env),
47
+ });
48
+ return true;
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }
54
+ function linuxSecretServiceRead(service, account, env = process.env) {
55
+ try {
56
+ const out = (0, node_child_process_1.execFileSync)(linuxSecretToolBin(env), [...linuxSecretServiceArgs("lookup", service, account)], {
57
+ encoding: "utf8",
58
+ stdio: ["ignore", "pipe", "ignore"],
59
+ timeout: 10_000,
60
+ env: childEnv(env),
61
+ });
62
+ const text = out.replace(/\n$/, "");
63
+ return text.length > 0 ? text : null;
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ function linuxSecretServiceDelete(service, account, env = process.env) {
70
+ try {
71
+ (0, node_child_process_1.execFileSync)(linuxSecretToolBin(env), [...linuxSecretServiceArgs("clear", service, account)], {
72
+ stdio: "ignore",
73
+ timeout: 10_000,
74
+ env: childEnv(env),
75
+ });
76
+ }
77
+ catch {
78
+ // none
79
+ }
80
+ }
81
+ function linuxSecretServiceAvailable(env = process.env) {
82
+ try {
83
+ (0, node_child_process_1.execFileSync)(linuxSecretToolBin(env), ["--help"], {
84
+ stdio: "ignore",
85
+ timeout: 5_000,
86
+ env: childEnv(env),
87
+ });
88
+ return true;
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
@@ -13,7 +13,26 @@ export interface StoredTokens {
13
13
  readonly clientId: string;
14
14
  readonly scopes: readonly string[];
15
15
  }
16
- export declare function saveTokens(profile: string, tokens: StoredTokens, env?: NodeJS.ProcessEnv): void;
16
+ export type TokenStorageBackend = "keychain" | "file" | "test-injection";
17
+ export type OsKeychainKind = "macos-security" | "linux-secret-service" | "windows-credential-manager" | "none";
18
+ export interface TokenStorageResult {
19
+ readonly backend: TokenStorageBackend;
20
+ readonly kind?: OsKeychainKind;
21
+ /** Absolute path when backend is file. */
22
+ readonly path?: string;
23
+ /** True when OS keychain failed/unavailable and file fallback was used. */
24
+ readonly degraded: boolean;
25
+ }
26
+ export declare function getLastTokenSaveResult(): TokenStorageResult | null;
27
+ export declare function keychainServiceName(profile: string): string;
28
+ export declare function keychainAccountName(): string;
29
+ /** Test-only override. Production code uses process.platform. */
30
+ export declare function keychainPlatform(env?: NodeJS.ProcessEnv): NodeJS.Platform;
31
+ export declare function probeOsKeychain(env?: NodeJS.ProcessEnv): {
32
+ readonly kind: OsKeychainKind;
33
+ readonly available: boolean;
34
+ };
35
+ export declare function saveTokens(profile: string, tokens: StoredTokens, env?: NodeJS.ProcessEnv): TokenStorageResult;
17
36
  export declare function loadTokens(profile: string, env?: NodeJS.ProcessEnv): StoredTokens | null;
18
37
  export declare function deleteTokens(profile: string, env?: NodeJS.ProcessEnv): void;
19
38
  export declare function tokenFingerprint(token: string): string;