@getxflow/cli 1.1.0 → 1.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.
@@ -1,100 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.refreshAgentKey = refreshAgentKey;
4
- exports.mcpInstall = mcpInstall;
5
- const node_os_1 = require("node:os");
6
- const node_path_1 = require("node:path");
7
- const api_1 = require("../api");
8
- const args_1 = require("../args");
9
- const config_1 = require("../config");
10
- const session_1 = require("../session");
11
- const spawn_1 = require("../spawn");
12
- const ui_1 = require("../ui");
13
- /** Clients with their own server-add command. */
14
- const CLIENTS = [
15
- {
16
- binary: 'claude',
17
- label: 'Claude Code',
18
- args: (url, token) => [
19
- 'mcp',
20
- 'add',
21
- '--transport',
22
- 'http',
23
- 'xflow',
24
- url,
25
- '--header',
26
- `Authorization: Bearer ${token}`,
27
- ],
28
- // Re-running must update the entry, not fail on it.
29
- reset: ['mcp', 'remove', 'xflow', '-s', 'local'],
30
- // Succeeds only when an xflow entry is visible from this folder.
31
- probe: ['mcp', 'get', 'xflow'],
32
- },
33
- ];
34
- // Output is captured, not inherited: clients may echo the added header together
35
- // with the key.
36
- function hasBinary(binary) {
37
- return (0, spawn_1.run)(binary, ['--version']).status === 0;
38
- }
39
- /**
40
- * Rewrite the key in clients that already hold an xflow entry visible from this
41
- * folder. Entries are never created here: that stays with xflow mcp install.
42
- */
43
- function refreshAgentKey(client) {
44
- const url = `${client.apiUrl.replace(/\/+$/, '')}/api/mcp`;
45
- let updated = false;
46
- for (const target of CLIENTS) {
47
- if (!hasBinary(target.binary))
48
- continue;
49
- if ((0, spawn_1.run)(target.binary, [...target.probe]).status !== 0)
50
- continue;
51
- (0, spawn_1.run)(target.binary, [...target.reset]);
52
- if ((0, spawn_1.run)(target.binary, target.args(url, client.token)).status === 0)
53
- updated = true;
54
- }
55
- return updated;
56
- }
57
- async function mcpInstall(args) {
58
- // The server covers the organization: the command works outside a project folder too.
59
- const root = (0, config_1.findProjectRoot)();
60
- const client = (0, session_1.connect)(root ? (0, config_1.readConfig)(root) : undefined);
61
- const url = `${client.apiUrl.replace(/\/+$/, '')}/api/mcp`;
62
- const identity = await (0, api_1.apiJson)(client, '/api/v1/me');
63
- (0, ui_1.out)(`${(0, ui_1.bold)('MCP server')} ${url}`);
64
- (0, ui_1.note)((0, ui_1.dim)(` Organization: ${identity.organization.name ?? 'unnamed'}`));
65
- (0, ui_1.note)((0, ui_1.dim)(' The key covers the whole organization: the agent names the project itself'));
66
- let found = 0;
67
- let installed = 0;
68
- for (const target of CLIENTS) {
69
- if (!hasBinary(target.binary))
70
- continue;
71
- found++;
72
- (0, ui_1.step)(`Writing into ${target.label}`);
73
- (0, spawn_1.run)(target.binary, [...target.reset]);
74
- const result = (0, spawn_1.run)(target.binary, target.args(url, client.token));
75
- if (result.status === 0) {
76
- installed++;
77
- (0, ui_1.ok)(`${target.label}: the xflow server is connected`);
78
- }
79
- else {
80
- const reason = `${result.stderr ?? ''}${result.stdout ?? ''}`.trim();
81
- (0, ui_1.note)(`${target.label}: the command exited with an error`);
82
- if (reason)
83
- (0, ui_1.out)((0, ui_1.dim)(` ${reason.split(client.token).join('<key>').split('\n').slice(0, 5).join('\n ')}`));
84
- }
85
- }
86
- if (installed === 0) {
87
- (0, ui_1.note)(found === 0 ? 'Found no client with its own connect command.' : 'Add the server by hand:');
88
- (0, ui_1.out)(` type: HTTP (streamable)`);
89
- (0, ui_1.out)(` address: ${url}`);
90
- (0, ui_1.out)(` header: Authorization: Bearer <key>`);
91
- }
92
- if ((0, args_1.flagBool)(args, 'show-token')) {
93
- (0, ui_1.out)('');
94
- (0, ui_1.out)(`${(0, ui_1.bold)('Key')} ${client.token}`);
95
- (0, ui_1.note)((0, ui_1.dim)(' Do not leave it in the chat with the agent'));
96
- }
97
- else {
98
- (0, ui_1.note)((0, ui_1.dim)(` The key lives in ${(0, node_path_1.join)((0, node_os_1.homedir)(), '.xflow', 'credentials.json')} and is not printed`));
99
- }
100
- }
package/dist/prompt.js DELETED
@@ -1,113 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.multiSelect = multiSelect;
4
- exports.select = select;
5
- const node_readline_1 = require("node:readline");
6
- const errors_1 = require("./errors");
7
- const ui_1 = require("./ui");
8
- function render(lines, first) {
9
- const up = first || lines.length === 1 ? '' : `[${lines.length - 1}A`;
10
- process.stderr.write(`${up}\r${lines.map((line) => `${line}`).join('\n')}`);
11
- }
12
- function listen(onKey) {
13
- if (process.stdin.isTTY !== true) {
14
- throw new errors_1.CliError('An interactive prompt needs a terminal', 'Pass the choice through flags instead');
15
- }
16
- (0, node_readline_1.emitKeypressEvents)(process.stdin);
17
- const wasRaw = process.stdin.isRaw === true;
18
- process.stdin.setRawMode(true);
19
- process.stdin.resume();
20
- process.stderr.write('[?25l');
21
- const handler = (_, key) => {
22
- if (key)
23
- onKey(key);
24
- };
25
- process.stdin.on('keypress', handler);
26
- return () => {
27
- process.stdin.off('keypress', handler);
28
- process.stdin.setRawMode(wasRaw);
29
- process.stdin.pause();
30
- process.stderr.write('[?25h\n');
31
- };
32
- }
33
- const isConfirm = (key) => key.name === 'return' || key.name === 'enter';
34
- const isCancel = (key) => key.name === 'escape' || (key.ctrl === true && key.name === 'c');
35
- /** Checkbox list. Resolves with the picked indexes, or null when cancelled. */
36
- function multiSelect(title, choices) {
37
- return new Promise((resolve) => {
38
- let cursor = 0;
39
- let first = true;
40
- const checked = choices.map((choice) => choice.checked === true);
41
- const draw = () => {
42
- render([
43
- (0, ui_1.bold)(title),
44
- ...choices.map((choice, i) => {
45
- const hint = choice.hint ? ` ${(0, ui_1.dim)(choice.hint)}` : '';
46
- return `${i === cursor ? '>' : ' '} ${checked[i] ? '[x]' : '[ ]'} ${choice.label}${hint}`;
47
- }),
48
- (0, ui_1.dim)(' up/down move, space toggle, enter confirm, esc cancel'),
49
- ], first);
50
- first = false;
51
- };
52
- const stop = listen((key) => {
53
- if (key.name === 'up')
54
- cursor = (cursor + choices.length - 1) % choices.length;
55
- else if (key.name === 'down')
56
- cursor = (cursor + 1) % choices.length;
57
- else if (key.name === 'space')
58
- checked[cursor] = !checked[cursor];
59
- else if (isConfirm(key)) {
60
- stop();
61
- resolve(checked.flatMap((on, i) => (on ? [i] : [])));
62
- return;
63
- }
64
- else if (isCancel(key)) {
65
- stop();
66
- resolve(null);
67
- return;
68
- }
69
- else
70
- return;
71
- draw();
72
- });
73
- draw();
74
- });
75
- }
76
- /** Radio list. Resolves with the picked index, or null when cancelled. */
77
- function select(title, choices) {
78
- return new Promise((resolve) => {
79
- let cursor = 0;
80
- let first = true;
81
- const draw = () => {
82
- render([
83
- (0, ui_1.bold)(title),
84
- ...choices.map((choice, i) => {
85
- const hint = choice.hint ? ` ${(0, ui_1.dim)(choice.hint)}` : '';
86
- return `${i === cursor ? '>' : ' '} ${choice.label}${hint}`;
87
- }),
88
- (0, ui_1.dim)(' up/down move, enter confirm, esc cancel'),
89
- ], first);
90
- first = false;
91
- };
92
- const stop = listen((key) => {
93
- if (key.name === 'up')
94
- cursor = (cursor + choices.length - 1) % choices.length;
95
- else if (key.name === 'down')
96
- cursor = (cursor + 1) % choices.length;
97
- else if (isConfirm(key)) {
98
- stop();
99
- resolve(cursor);
100
- return;
101
- }
102
- else if (isCancel(key)) {
103
- stop();
104
- resolve(null);
105
- return;
106
- }
107
- else
108
- return;
109
- draw();
110
- });
111
- draw();
112
- });
113
- }