@lazyingart/agintiflow 0.8.6 → 0.8.8
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 +8 -0
- package/package.json +1 -1
- package/src/auth-onboarding.js +62 -0
- package/src/cli.js +32 -10
- package/src/interactive-cli.js +72 -2
- package/src/project.js +5 -0
package/README.md
CHANGED
|
@@ -46,6 +46,14 @@ aginti --list-profiles
|
|
|
46
46
|
aginti --sandbox-status
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
On first interactive use, if no DeepSeek key is detected, `aginti` asks you to paste it and saves it to the project-local ignored file `.aginti/.env` with `0600` permissions. You can also set it explicitly:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
aginti login deepseek
|
|
53
|
+
# or non-interactively:
|
|
54
|
+
printf '%s' "$DEEPSEEK_API_KEY" | aginti keys set deepseek --stdin
|
|
55
|
+
```
|
|
56
|
+
|
|
49
57
|
Start an interactive Codex-style CLI chat from any project folder:
|
|
50
58
|
|
|
51
59
|
```bash
|
package/package.json
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import readline from "node:readline/promises";
|
|
2
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
+
import { Writable } from "node:stream";
|
|
4
|
+
import { providerKeyStatus, setProviderKey } from "./project.js";
|
|
5
|
+
|
|
6
|
+
class MutedWritable extends Writable {
|
|
7
|
+
constructor(target) {
|
|
8
|
+
super();
|
|
9
|
+
this.target = target;
|
|
10
|
+
this.muted = false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
_write(chunk, encoding, callback) {
|
|
14
|
+
if (!this.muted) this.target.write(chunk, encoding);
|
|
15
|
+
callback();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function promptHidden(promptText) {
|
|
20
|
+
if (!input.isTTY || !output.isTTY) return "";
|
|
21
|
+
|
|
22
|
+
const mutedOutput = new MutedWritable(output);
|
|
23
|
+
const rl = readline.createInterface({
|
|
24
|
+
input,
|
|
25
|
+
output: mutedOutput,
|
|
26
|
+
terminal: true,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
output.write(promptText);
|
|
31
|
+
mutedOutput.muted = true;
|
|
32
|
+
const value = await rl.question("");
|
|
33
|
+
output.write("\n");
|
|
34
|
+
return String(value || "").trim();
|
|
35
|
+
} finally {
|
|
36
|
+
mutedOutput.muted = false;
|
|
37
|
+
rl.close();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function shouldPromptForDeepSeek(args = {}, projectRoot = process.cwd()) {
|
|
42
|
+
const provider = String(args.provider || "").toLowerCase();
|
|
43
|
+
if (provider === "mock" || provider === "openai") return false;
|
|
44
|
+
if (process.env.AGINTIFLOW_NO_AUTH_PROMPT === "1") return false;
|
|
45
|
+
if (!input.isTTY || !output.isTTY) return false;
|
|
46
|
+
return !providerKeyStatus(projectRoot).deepseek;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function promptAndSaveDeepSeekKey(projectRoot = process.cwd(), options = {}) {
|
|
50
|
+
const key = await promptHidden(
|
|
51
|
+
options.promptText || "DeepSeek API key not found. Paste DEEPSEEK_API_KEY to save locally, or press Enter to skip: "
|
|
52
|
+
);
|
|
53
|
+
if (!key) return { saved: false, skipped: true };
|
|
54
|
+
|
|
55
|
+
const result = await setProviderKey(projectRoot, "deepseek", key);
|
|
56
|
+
return {
|
|
57
|
+
saved: true,
|
|
58
|
+
provider: result.provider,
|
|
59
|
+
keyName: result.keyName,
|
|
60
|
+
path: result.path,
|
|
61
|
+
};
|
|
62
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
showProjectSession,
|
|
16
16
|
} from "./project.js";
|
|
17
17
|
import { listTaskProfiles } from "./task-profiles.js";
|
|
18
|
+
import { promptAndSaveDeepSeekKey, promptHidden, shouldPromptForDeepSeek } from "./auth-onboarding.js";
|
|
18
19
|
import fs from "node:fs/promises";
|
|
19
20
|
import path from "node:path";
|
|
20
21
|
import { fileURLToPath } from "node:url";
|
|
@@ -219,7 +220,7 @@ export function parseArgs(argv) {
|
|
|
219
220
|
|
|
220
221
|
function printUsage() {
|
|
221
222
|
console.log(
|
|
222
|
-
'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti resume [latest|<session-id>] ["prompt"] OR aginti queue <session-id> "message" OR aginti [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|mock] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-destructive] [--allow-file-tools|--no-file-tools] [--allow-wrappers --wrapper codex] [--sandbox-status|--sandbox-preflight] "your task"'
|
|
223
|
+
'Usage: aginti [chat] OR aginti web [--port 3210] OR aginti login deepseek OR aginti resume [latest|<session-id>] ["prompt"] OR aginti queue <session-id> "message" OR aginti [--latex] [--routing smart|fast|complex|manual] [--provider deepseek|openai|mock] [--sandbox-mode host|docker-readonly|docker-workspace] [--package-install-policy block|prompt|allow] [--approve-package-installs] [--allow-shell|--no-shell] [--allow-destructive] [--allow-file-tools|--no-file-tools] [--allow-wrappers --wrapper codex] [--sandbox-status|--sandbox-preflight] "your task"'
|
|
223
224
|
);
|
|
224
225
|
}
|
|
225
226
|
|
|
@@ -298,6 +299,21 @@ async function readStdin() {
|
|
|
298
299
|
return input.trim();
|
|
299
300
|
}
|
|
300
301
|
|
|
302
|
+
async function ensureDeepSeekKeyForOneShot(args) {
|
|
303
|
+
if (!shouldPromptForDeepSeek(args, process.cwd())) return true;
|
|
304
|
+
console.log("DeepSeek API key is not configured for this project.");
|
|
305
|
+
console.log("Paste it once to save it in `.aginti/.env` with 0600 permissions, or press Enter to cancel.");
|
|
306
|
+
const result = await promptAndSaveDeepSeekKey(process.cwd(), {
|
|
307
|
+
promptText: "DeepSeek API key: ",
|
|
308
|
+
});
|
|
309
|
+
if (result.saved) {
|
|
310
|
+
console.log(`saved ${result.keyName} to project-local ignored env`);
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
console.error("No DeepSeek key saved. Run `aginti login deepseek` later, or use `--provider mock` for local tests.");
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
301
317
|
async function handleKeyCommand(argv) {
|
|
302
318
|
const [verb = "status", provider = ""] = argv;
|
|
303
319
|
if (verb === "status") {
|
|
@@ -313,17 +329,17 @@ async function handleKeyCommand(argv) {
|
|
|
313
329
|
|
|
314
330
|
if (verb === "set") {
|
|
315
331
|
const target = provider || "deepseek";
|
|
316
|
-
|
|
317
|
-
|
|
332
|
+
const key = argv.includes("--stdin") ? await readStdin() : await promptHidden(`${target === "openai" ? "OpenAI" : "DeepSeek"} API key: `);
|
|
333
|
+
if (!key) {
|
|
334
|
+
console.error("No key saved.");
|
|
318
335
|
process.exit(1);
|
|
319
336
|
}
|
|
320
|
-
const key = await readStdin();
|
|
321
337
|
const result = await setProviderKey(process.cwd(), target, key);
|
|
322
338
|
console.log(`saved ${result.provider} key to project-local ignored env (${result.keyName})`);
|
|
323
339
|
return;
|
|
324
340
|
}
|
|
325
341
|
|
|
326
|
-
console.error("Usage: aginti keys status OR aginti keys set deepseek --stdin");
|
|
342
|
+
console.error("Usage: aginti keys status OR aginti keys set deepseek [--stdin]");
|
|
327
343
|
process.exit(1);
|
|
328
344
|
}
|
|
329
345
|
|
|
@@ -442,11 +458,13 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
442
458
|
|
|
443
459
|
if (argv[0] === "login") {
|
|
444
460
|
const provider = argv[1] || "deepseek";
|
|
445
|
-
|
|
446
|
-
|
|
461
|
+
const key = argv.includes("--stdin") || !process.stdin.isTTY
|
|
462
|
+
? await readStdin()
|
|
463
|
+
: await promptHidden(`${provider === "openai" ? "OpenAI" : "DeepSeek"} API key: `);
|
|
464
|
+
if (!key) {
|
|
465
|
+
console.error("No key saved.");
|
|
447
466
|
process.exit(1);
|
|
448
467
|
}
|
|
449
|
-
const key = await readStdin();
|
|
450
468
|
const result = await setProviderKey(process.cwd(), provider, key);
|
|
451
469
|
console.log(`saved ${result.provider} key to project-local ignored env (${result.keyName})`);
|
|
452
470
|
return;
|
|
@@ -478,7 +496,9 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
478
496
|
});
|
|
479
497
|
return;
|
|
480
498
|
}
|
|
481
|
-
const
|
|
499
|
+
const resumeArgs = agentDefaults({ ...parseArgs([prompt]), resume: sessionId, goal: prompt });
|
|
500
|
+
if (!(await ensureDeepSeekKeyForOneShot(resumeArgs))) process.exit(1);
|
|
501
|
+
const config = loadConfig(resumeArgs, { packageDir });
|
|
482
502
|
await runAgent(config);
|
|
483
503
|
return;
|
|
484
504
|
}
|
|
@@ -527,6 +547,8 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
527
547
|
process.exit(1);
|
|
528
548
|
}
|
|
529
549
|
|
|
530
|
-
const
|
|
550
|
+
const finalArgs = agentDefaults(args);
|
|
551
|
+
if (!(await ensureDeepSeekKeyForOneShot(finalArgs))) process.exit(1);
|
|
552
|
+
const config = loadConfig(finalArgs, { packageDir });
|
|
531
553
|
await runAgent(config);
|
|
532
554
|
}
|
package/src/interactive-cli.js
CHANGED
|
@@ -6,8 +6,9 @@ import { loadConfig } from "./config.js";
|
|
|
6
6
|
import { initProject, listProjectSessions } from "./project.js";
|
|
7
7
|
import { normalizePackageInstallPolicy, normalizeSandboxMode } from "./command-policy.js";
|
|
8
8
|
import { normalizeTaskProfile } from "./task-profiles.js";
|
|
9
|
+
import { promptAndSaveDeepSeekKey, shouldPromptForDeepSeek } from "./auth-onboarding.js";
|
|
9
10
|
|
|
10
|
-
const useColor = Boolean(input.isTTY && output.isTTY &&
|
|
11
|
+
const useColor = Boolean(input.isTTY && output.isTTY && process.env.AGINTIFLOW_NO_COLOR !== "1");
|
|
11
12
|
const ansi = {
|
|
12
13
|
reset: "\x1b[0m",
|
|
13
14
|
bold: "\x1b[1m",
|
|
@@ -16,16 +17,24 @@ const ansi = {
|
|
|
16
17
|
green: "\x1b[32m",
|
|
17
18
|
yellow: "\x1b[33m",
|
|
18
19
|
red: "\x1b[31m",
|
|
20
|
+
clearLine: "\x1b[2K",
|
|
21
|
+
cursorHide: "\x1b[?25l",
|
|
22
|
+
cursorShow: "\x1b[?25h",
|
|
19
23
|
userBg: "\x1b[48;5;24m\x1b[38;5;231m",
|
|
20
24
|
agentBg: "\x1b[48;5;29m\x1b[38;5;231m",
|
|
21
25
|
systemBg: "\x1b[48;5;236m\x1b[38;5;245m",
|
|
22
26
|
};
|
|
27
|
+
const brandPalette = ["\x1b[38;5;45m", "\x1b[38;5;81m", "\x1b[38;5;86m", "\x1b[38;5;118m", "\x1b[38;5;226m"];
|
|
23
28
|
|
|
24
29
|
function color(value, ...codes) {
|
|
25
30
|
if (!useColor || codes.length === 0) return String(value);
|
|
26
31
|
return `${codes.join("")}${value}${ansi.reset}`;
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
function sleep(ms) {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
function label(name, bgCode) {
|
|
30
39
|
return color(` ${name} `, bgCode, ansi.bold);
|
|
31
40
|
}
|
|
@@ -95,6 +104,43 @@ function printHeading(text) {
|
|
|
95
104
|
console.log(color(stripMarkdown(text), ansi.bold, ansi.cyan));
|
|
96
105
|
}
|
|
97
106
|
|
|
107
|
+
function shimmerText(text, frame) {
|
|
108
|
+
if (!useColor) return text;
|
|
109
|
+
return [...text]
|
|
110
|
+
.map((char, index) => {
|
|
111
|
+
if (char === " ") return char;
|
|
112
|
+
const code = brandPalette[(index + frame) % brandPalette.length];
|
|
113
|
+
return `${code}${ansi.bold}${char}${ansi.reset}`;
|
|
114
|
+
})
|
|
115
|
+
.join("");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function renderLaunchHeader(packageVersion = "") {
|
|
119
|
+
const title = "AgInTi Flow";
|
|
120
|
+
const subtitle = "web-first agent workspace";
|
|
121
|
+
const version = packageVersion ? `v${packageVersion}` : "";
|
|
122
|
+
const line = "+--------------------------------------------------+";
|
|
123
|
+
|
|
124
|
+
if (!useColor || process.env.AGINTIFLOW_NO_ANIMATION === "1") {
|
|
125
|
+
console.log(` AgInTiFlow ${packageVersion || ""}`.trim());
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
output.write(ansi.cursorHide);
|
|
130
|
+
for (let frame = 0; frame < 18; frame += 1) {
|
|
131
|
+
output.write(`\r${ansi.clearLine}${shimmerText(title, frame)} ${color("is starting", ansi.dim)}`);
|
|
132
|
+
await sleep(32);
|
|
133
|
+
}
|
|
134
|
+
output.write(`\r${ansi.clearLine}`);
|
|
135
|
+
output.write(ansi.cursorShow);
|
|
136
|
+
|
|
137
|
+
console.log(color(line, "\x1b[38;5;45m"));
|
|
138
|
+
console.log(`${color("|", "\x1b[38;5;45m")} ${shimmerText(title, 2)} ${color(version.padStart(36 - title.length), ansi.dim)} ${color("|", "\x1b[38;5;45m")}`);
|
|
139
|
+
console.log(`${color("|", "\x1b[38;5;45m")} ${color(subtitle.padEnd(48), ansi.dim)} ${color("|", "\x1b[38;5;45m")}`);
|
|
140
|
+
console.log(`${color("|", "\x1b[38;5;45m")} ${color("browser + shell + files + docker + canvas".padEnd(48), ansi.cyan)} ${color("|", "\x1b[38;5;45m")}`);
|
|
141
|
+
console.log(color(line, "\x1b[38;5;45m"));
|
|
142
|
+
}
|
|
143
|
+
|
|
98
144
|
function printHelp() {
|
|
99
145
|
printAgentMessage(
|
|
100
146
|
[
|
|
@@ -223,6 +269,29 @@ function createState(args = {}) {
|
|
|
223
269
|
};
|
|
224
270
|
}
|
|
225
271
|
|
|
272
|
+
async function maybeOnboardDeepSeekKey(state) {
|
|
273
|
+
if (!shouldPromptForDeepSeek(state, process.cwd())) return;
|
|
274
|
+
|
|
275
|
+
printAgentMessage(
|
|
276
|
+
[
|
|
277
|
+
"DeepSeek API key is not configured for this project.",
|
|
278
|
+
"Paste it once to save it in `.aginti/.env` with 0600 permissions, or press Enter to continue in mock mode.",
|
|
279
|
+
].join("\n")
|
|
280
|
+
);
|
|
281
|
+
const result = await promptAndSaveDeepSeekKey(process.cwd(), {
|
|
282
|
+
promptText: "DeepSeek API key: ",
|
|
283
|
+
});
|
|
284
|
+
if (result.saved) {
|
|
285
|
+
printAgentMessage(`Saved ${result.keyName} to project-local ignored env.`);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
state.provider = "mock";
|
|
290
|
+
state.routingMode = "manual";
|
|
291
|
+
state.model = "mock-agent";
|
|
292
|
+
printAgentMessage("No key saved. Continuing in local mock mode. Use `/provider deepseek` after running `aginti login deepseek`.");
|
|
293
|
+
}
|
|
294
|
+
|
|
226
295
|
async function handleCommand(line, state, packageDir) {
|
|
227
296
|
const [command, ...rest] = line.slice(1).trim().split(/\s+/);
|
|
228
297
|
const value = rest.join(" ").trim();
|
|
@@ -433,8 +502,9 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
|
|
|
433
502
|
const state = createState(args);
|
|
434
503
|
const rl = readline.createInterface({ input, output, terminal: Boolean(input.isTTY && output.isTTY) });
|
|
435
504
|
|
|
436
|
-
|
|
505
|
+
await renderLaunchHeader(packageVersion);
|
|
437
506
|
printSystemLine(`Project: ${process.cwd()}`);
|
|
507
|
+
await maybeOnboardDeepSeekKey(state);
|
|
438
508
|
printAgentMessage("Interactive agent chat. Type /help for commands, /exit to quit.");
|
|
439
509
|
printStatus(state);
|
|
440
510
|
|
package/src/project.js
CHANGED
|
@@ -199,6 +199,11 @@ export async function setProviderKey(projectRoot, provider, value) {
|
|
|
199
199
|
|
|
200
200
|
const paths = projectPaths(projectRoot);
|
|
201
201
|
await fsp.mkdir(paths.controlDir, { recursive: true });
|
|
202
|
+
await ensureLine(paths.gitignorePath, [
|
|
203
|
+
".aginti/.env",
|
|
204
|
+
".aginti/.env.*",
|
|
205
|
+
"!.aginti/.env.example",
|
|
206
|
+
]);
|
|
202
207
|
let parsed = {};
|
|
203
208
|
try {
|
|
204
209
|
parsed = parseEnvText(await fsp.readFile(paths.envPath, "utf8"));
|