@arbidocs/cli 0.3.16 → 0.3.18
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/CHANGELOG.md +43 -0
- package/dist/index.js +686 -387
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
- package/scripts/postinstall.js +56 -0
- package/scripts/preuninstall.js +35 -0
package/dist/index.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var commander = require('commander');
|
|
5
|
-
var
|
|
6
|
-
var os = require('os');
|
|
5
|
+
var fs2 = require('fs');
|
|
7
6
|
var path = require('path');
|
|
7
|
+
var os = require('os');
|
|
8
8
|
var chalk2 = require('chalk');
|
|
9
9
|
var sdk = require('@arbidocs/sdk');
|
|
10
10
|
var prompts = require('@inquirer/prompts');
|
|
@@ -15,11 +15,104 @@ var module$1 = require('module');
|
|
|
15
15
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
16
16
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
17
|
|
|
18
|
-
var
|
|
19
|
-
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
18
|
+
var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
20
19
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
20
|
+
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
21
21
|
var chalk2__default = /*#__PURE__*/_interopDefault(chalk2);
|
|
22
22
|
|
|
23
|
+
function getCacheFile() {
|
|
24
|
+
const configDir = process.env.ARBI_CONFIG_DIR ?? path__default.default.join(os__default.default.homedir(), ".arbi");
|
|
25
|
+
return path__default.default.join(configDir, "completions.json");
|
|
26
|
+
}
|
|
27
|
+
function ensureDir(filePath) {
|
|
28
|
+
const dir = path__default.default.dirname(filePath);
|
|
29
|
+
if (!fs2__default.default.existsSync(dir)) {
|
|
30
|
+
fs2__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function getCachedWorkspaceIds() {
|
|
34
|
+
try {
|
|
35
|
+
const content = fs2__default.default.readFileSync(getCacheFile(), "utf-8");
|
|
36
|
+
const cache = JSON.parse(content);
|
|
37
|
+
return cache.workspaces.map((w) => w.id);
|
|
38
|
+
} catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function updateCompletionCache(workspaces3) {
|
|
43
|
+
const cache = {
|
|
44
|
+
workspaces: workspaces3.filter((w) => w.external_id).map((w) => ({ id: w.external_id, name: w.name ?? "" })),
|
|
45
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
46
|
+
};
|
|
47
|
+
const filePath = getCacheFile();
|
|
48
|
+
ensureDir(filePath);
|
|
49
|
+
fs2__default.default.writeFileSync(filePath, JSON.stringify(cache, null, 2) + "\n", { mode: 384 });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/completion.ts
|
|
53
|
+
var WORKSPACE_ID_OPTIONS = /* @__PURE__ */ new Set(["-w", "--workspace"]);
|
|
54
|
+
function getOptionFlags(cmd) {
|
|
55
|
+
const flags = [];
|
|
56
|
+
for (const opt of cmd.options) {
|
|
57
|
+
if (opt.short) flags.push(opt.short);
|
|
58
|
+
if (opt.long) flags.push(opt.long);
|
|
59
|
+
}
|
|
60
|
+
return flags;
|
|
61
|
+
}
|
|
62
|
+
function findOption(cmd, flag) {
|
|
63
|
+
return cmd.options.find((o) => o.short === flag || o.long === flag);
|
|
64
|
+
}
|
|
65
|
+
function getCompletions(program2, line) {
|
|
66
|
+
const parts = line.trim().split(/\s+/);
|
|
67
|
+
if (parts.length > 0) {
|
|
68
|
+
const first = parts[0];
|
|
69
|
+
if (first === program2.name() || first.endsWith("/" + program2.name())) {
|
|
70
|
+
parts.shift();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const endsWithSpace = line.endsWith(" ");
|
|
74
|
+
let current = program2;
|
|
75
|
+
let consumed = 0;
|
|
76
|
+
for (let i = 0; i < parts.length; i++) {
|
|
77
|
+
const word = parts[i];
|
|
78
|
+
const sub = current.commands.find(
|
|
79
|
+
(c) => c.name() === word || c.aliases().includes(word)
|
|
80
|
+
);
|
|
81
|
+
if (sub) {
|
|
82
|
+
current = sub;
|
|
83
|
+
consumed = i + 1;
|
|
84
|
+
} else {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const remaining = parts.slice(consumed);
|
|
89
|
+
const lastWord = remaining.length > 0 ? remaining[remaining.length - 1] : "";
|
|
90
|
+
const prevWord = endsWithSpace ? remaining[remaining.length - 1] : remaining[remaining.length - 2];
|
|
91
|
+
if (prevWord && WORKSPACE_ID_OPTIONS.has(prevWord)) {
|
|
92
|
+
return getCachedWorkspaceIds();
|
|
93
|
+
}
|
|
94
|
+
const subcommands = current.commands.map((c) => c.name());
|
|
95
|
+
const options = getOptionFlags(current);
|
|
96
|
+
const allCandidates = [...subcommands, ...options];
|
|
97
|
+
if (remaining.length === 0 || endsWithSpace && remaining.length >= 0) {
|
|
98
|
+
if (endsWithSpace) {
|
|
99
|
+
const used = /* @__PURE__ */ new Set();
|
|
100
|
+
for (const part of remaining) {
|
|
101
|
+
if (part.startsWith("-")) {
|
|
102
|
+
used.add(part);
|
|
103
|
+
const opt = findOption(current, part);
|
|
104
|
+
if (opt) {
|
|
105
|
+
if (opt.short) used.add(opt.short);
|
|
106
|
+
if (opt.long) used.add(opt.long);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return allCandidates.filter((c) => !used.has(c));
|
|
111
|
+
}
|
|
112
|
+
return allCandidates;
|
|
113
|
+
}
|
|
114
|
+
return allCandidates.filter((c) => c.startsWith(lastWord));
|
|
115
|
+
}
|
|
23
116
|
var store = new sdk.FileConfigStore();
|
|
24
117
|
function getConfig() {
|
|
25
118
|
return store.getConfig();
|
|
@@ -51,6 +144,12 @@ function getCredentials() {
|
|
|
51
144
|
function deleteCredentials() {
|
|
52
145
|
store.deleteCredentials();
|
|
53
146
|
}
|
|
147
|
+
function saveLastMetadata(metadata) {
|
|
148
|
+
store.saveLastMetadata?.(metadata);
|
|
149
|
+
}
|
|
150
|
+
function loadLastMetadata() {
|
|
151
|
+
return store.loadLastMetadata?.() ?? null;
|
|
152
|
+
}
|
|
54
153
|
function getChatSession() {
|
|
55
154
|
return store.getChatSession();
|
|
56
155
|
}
|
|
@@ -98,8 +197,8 @@ function getShellRcPath() {
|
|
|
98
197
|
return path.join(os.homedir(), ".bashrc");
|
|
99
198
|
}
|
|
100
199
|
function isAliasInstalled(rcPath) {
|
|
101
|
-
if (!
|
|
102
|
-
const content =
|
|
200
|
+
if (!fs2.existsSync(rcPath)) return false;
|
|
201
|
+
const content = fs2.readFileSync(rcPath, "utf-8");
|
|
103
202
|
return content.includes(ALIAS_LINE) || content.includes(ALIAS_MARKER);
|
|
104
203
|
}
|
|
105
204
|
function registerConfigCommand(program2) {
|
|
@@ -178,7 +277,7 @@ function registerConfigCommand(program2) {
|
|
|
178
277
|
dim("Usage: A what is the meaning of life");
|
|
179
278
|
return;
|
|
180
279
|
}
|
|
181
|
-
|
|
280
|
+
fs2.appendFileSync(rcPath, `
|
|
182
281
|
${ALIAS_MARKER}
|
|
183
282
|
${ALIAS_LINE}
|
|
184
283
|
`);
|
|
@@ -3504,7 +3603,7 @@ var CACHE_FILE = path__default.default.join(os__default.default.homedir(), ".arb
|
|
|
3504
3603
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3505
3604
|
function readCache() {
|
|
3506
3605
|
try {
|
|
3507
|
-
const data = JSON.parse(
|
|
3606
|
+
const data = JSON.parse(fs2__default.default.readFileSync(CACHE_FILE, "utf8"));
|
|
3508
3607
|
if (data.latest && data.checkedAt && Date.now() - data.checkedAt < CACHE_TTL_MS) {
|
|
3509
3608
|
return data;
|
|
3510
3609
|
}
|
|
@@ -3515,8 +3614,8 @@ function readCache() {
|
|
|
3515
3614
|
function writeCache(latest) {
|
|
3516
3615
|
try {
|
|
3517
3616
|
const dir = path__default.default.dirname(CACHE_FILE);
|
|
3518
|
-
if (!
|
|
3519
|
-
|
|
3617
|
+
if (!fs2__default.default.existsSync(dir)) fs2__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
3618
|
+
fs2__default.default.writeFileSync(CACHE_FILE, JSON.stringify({ latest, checkedAt: Date.now() }) + "\n");
|
|
3520
3619
|
} catch {
|
|
3521
3620
|
}
|
|
3522
3621
|
}
|
|
@@ -3537,13 +3636,13 @@ function getLatestVersion(skipCache = false) {
|
|
|
3537
3636
|
}
|
|
3538
3637
|
}
|
|
3539
3638
|
function getCurrentVersion() {
|
|
3540
|
-
return "0.3.
|
|
3639
|
+
return "0.3.18";
|
|
3541
3640
|
}
|
|
3542
3641
|
function readChangelog(fromVersion, toVersion) {
|
|
3543
3642
|
try {
|
|
3544
3643
|
const globalRoot = child_process.execSync("npm root -g", { encoding: "utf8", timeout: 5e3 }).trim();
|
|
3545
3644
|
const changelogPath = path__default.default.join(globalRoot, "@arbidocs", "cli", "CHANGELOG.md");
|
|
3546
|
-
const text =
|
|
3645
|
+
const text = fs2__default.default.readFileSync(changelogPath, "utf8");
|
|
3547
3646
|
return extractSections(text, fromVersion, toVersion);
|
|
3548
3647
|
} catch {
|
|
3549
3648
|
return null;
|
|
@@ -3590,17 +3689,17 @@ function showChangelog(fromVersion, toVersion) {
|
|
|
3590
3689
|
async function checkForUpdates(autoUpdate) {
|
|
3591
3690
|
try {
|
|
3592
3691
|
const latest = getLatestVersion();
|
|
3593
|
-
if (!latest || latest === "0.3.
|
|
3692
|
+
if (!latest || latest === "0.3.18") return;
|
|
3594
3693
|
if (autoUpdate) {
|
|
3595
3694
|
warn(`
|
|
3596
|
-
Your arbi version is out of date (${"0.3.
|
|
3695
|
+
Your arbi version is out of date (${"0.3.18"} \u2192 ${latest}). Updating...`);
|
|
3597
3696
|
child_process.execSync("npm install -g @arbidocs/cli@latest", { stdio: "inherit" });
|
|
3598
|
-
showChangelog("0.3.
|
|
3697
|
+
showChangelog("0.3.18", latest);
|
|
3599
3698
|
console.log(`Updated to ${latest}.`);
|
|
3600
3699
|
} else {
|
|
3601
3700
|
warn(
|
|
3602
3701
|
`
|
|
3603
|
-
Your arbi version is out of date (${"0.3.
|
|
3702
|
+
Your arbi version is out of date (${"0.3.18"} \u2192 ${latest}).
|
|
3604
3703
|
Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
|
|
3605
3704
|
);
|
|
3606
3705
|
}
|
|
@@ -3610,150 +3709,380 @@ Run "arbi update" to upgrade, or "arbi update auto" to always stay up to date.`
|
|
|
3610
3709
|
function hintUpdateOnError() {
|
|
3611
3710
|
try {
|
|
3612
3711
|
const cached = readCache();
|
|
3613
|
-
if (cached && cached.latest !== "0.3.
|
|
3712
|
+
if (cached && cached.latest !== "0.3.18") {
|
|
3614
3713
|
warn(
|
|
3615
|
-
`Your arbi version is out of date (${"0.3.
|
|
3714
|
+
`Your arbi version is out of date (${"0.3.18"} \u2192 ${cached.latest}). Run "arbi update".`
|
|
3616
3715
|
);
|
|
3617
3716
|
}
|
|
3618
3717
|
} catch {
|
|
3619
3718
|
}
|
|
3620
3719
|
}
|
|
3720
|
+
var MAX_TASKS = 50;
|
|
3721
|
+
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3722
|
+
function getTasksFile() {
|
|
3723
|
+
const configDir = process.env.ARBI_CONFIG_DIR ?? path__default.default.join(os__default.default.homedir(), ".arbi");
|
|
3724
|
+
return path__default.default.join(configDir, "tasks.json");
|
|
3725
|
+
}
|
|
3726
|
+
function ensureDir2(filePath) {
|
|
3727
|
+
const dir = path__default.default.dirname(filePath);
|
|
3728
|
+
if (!fs2__default.default.existsSync(dir)) {
|
|
3729
|
+
fs2__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
function readTasks() {
|
|
3733
|
+
try {
|
|
3734
|
+
const content = fs2__default.default.readFileSync(getTasksFile(), "utf-8");
|
|
3735
|
+
return JSON.parse(content);
|
|
3736
|
+
} catch {
|
|
3737
|
+
return [];
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
function writeTasks(tasks) {
|
|
3741
|
+
const filePath = getTasksFile();
|
|
3742
|
+
ensureDir2(filePath);
|
|
3743
|
+
fs2__default.default.writeFileSync(filePath, JSON.stringify(tasks, null, 2) + "\n", { mode: 384 });
|
|
3744
|
+
}
|
|
3745
|
+
function getTasks() {
|
|
3746
|
+
const now = Date.now();
|
|
3747
|
+
const tasks = readTasks().filter((t) => now - new Date(t.submittedAt).getTime() < MAX_AGE_MS);
|
|
3748
|
+
return tasks;
|
|
3749
|
+
}
|
|
3750
|
+
function addTask(task) {
|
|
3751
|
+
const tasks = [task, ...getTasks().filter((t) => t.id !== task.id)].slice(0, MAX_TASKS);
|
|
3752
|
+
writeTasks(tasks);
|
|
3753
|
+
}
|
|
3754
|
+
function updateTaskStatus(id, status2) {
|
|
3755
|
+
const tasks = readTasks();
|
|
3756
|
+
const task = tasks.find((t) => t.id === id);
|
|
3757
|
+
if (task) {
|
|
3758
|
+
task.status = status2;
|
|
3759
|
+
writeTasks(tasks);
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
function getLatestTask() {
|
|
3763
|
+
const tasks = getTasks();
|
|
3764
|
+
return tasks[0] ?? null;
|
|
3765
|
+
}
|
|
3621
3766
|
|
|
3622
|
-
// src/
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
if (
|
|
3641
|
-
|
|
3642
|
-
process.exit(1);
|
|
3767
|
+
// src/notifications.ts
|
|
3768
|
+
var activeConnection = null;
|
|
3769
|
+
function timestamp() {
|
|
3770
|
+
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", { hour12: false });
|
|
3771
|
+
}
|
|
3772
|
+
function colorize(level, text) {
|
|
3773
|
+
if (level === "success") return chalk2__default.default.green(text);
|
|
3774
|
+
if (level === "error") return chalk2__default.default.red(text);
|
|
3775
|
+
if (level === "warning") return chalk2__default.default.yellow(text);
|
|
3776
|
+
return text;
|
|
3777
|
+
}
|
|
3778
|
+
async function startBackgroundNotifications(baseUrl, accessToken) {
|
|
3779
|
+
if (activeConnection) return;
|
|
3780
|
+
try {
|
|
3781
|
+
activeConnection = await sdk.connectWithReconnect({
|
|
3782
|
+
baseUrl,
|
|
3783
|
+
accessToken,
|
|
3784
|
+
onMessage: (msg) => {
|
|
3785
|
+
if (client.isMessageType(msg, "response_complete")) {
|
|
3786
|
+
updateTaskStatus(msg.response_id, msg.status);
|
|
3643
3787
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3788
|
+
const { text, level } = sdk.formatWsMessage(msg);
|
|
3789
|
+
process.stderr.write(`
|
|
3790
|
+
${colorize(level, `[${timestamp()}] ${text}`)}
|
|
3791
|
+
`);
|
|
3792
|
+
},
|
|
3793
|
+
onClose: () => {
|
|
3794
|
+
activeConnection = null;
|
|
3795
|
+
},
|
|
3796
|
+
onReconnecting: (attempt, maxRetries) => {
|
|
3797
|
+
process.stderr.write(
|
|
3798
|
+
`
|
|
3799
|
+
${chalk2__default.default.yellow(`[${timestamp()}] Reconnecting... (${attempt}/${maxRetries})`)}
|
|
3800
|
+
`
|
|
3652
3801
|
);
|
|
3653
|
-
|
|
3802
|
+
},
|
|
3803
|
+
onReconnected: () => {
|
|
3804
|
+
process.stderr.write(`
|
|
3805
|
+
${chalk2__default.default.green(`[${timestamp()}] Reconnected`)}
|
|
3806
|
+
`);
|
|
3807
|
+
},
|
|
3808
|
+
onReconnectFailed: () => {
|
|
3809
|
+
process.stderr.write(`
|
|
3810
|
+
${chalk2__default.default.red(`[${timestamp()}] Reconnection failed`)}
|
|
3811
|
+
`);
|
|
3654
3812
|
}
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
updateConfig({ selectedWorkspaceId: selected });
|
|
3658
|
-
const ws = memberWorkspaces.find((w) => w.external_id === selected);
|
|
3659
|
-
success(`Workspace: ${ws.name} (${ref(selected)})`);
|
|
3660
|
-
dim('\nTip: Run "arbi config alias" to use A as a shortcut for "arbi ask"');
|
|
3661
|
-
} catch (err) {
|
|
3662
|
-
error(`Login failed: ${sdk.getErrorMessage(err)}`);
|
|
3663
|
-
process.exit(1);
|
|
3664
|
-
} finally {
|
|
3665
|
-
await checkForUpdates(getConfig()?.autoUpdate);
|
|
3666
|
-
}
|
|
3667
|
-
});
|
|
3668
|
-
}
|
|
3669
|
-
var CENTRAL_API_URL = "https://central.arbi.work";
|
|
3670
|
-
async function getVerificationCode(email, apiKey) {
|
|
3671
|
-
const params = new URLSearchParams({ email });
|
|
3672
|
-
const res = await fetch(`${CENTRAL_API_URL}/license-management/verify-ci?${params.toString()}`, {
|
|
3673
|
-
method: "GET",
|
|
3674
|
-
headers: { "x-api-key": apiKey }
|
|
3675
|
-
});
|
|
3676
|
-
if (!res.ok) {
|
|
3677
|
-
const body = await res.text().catch(() => "");
|
|
3678
|
-
throw new Error(`Failed to get verification code: ${res.status} ${body}`);
|
|
3813
|
+
});
|
|
3814
|
+
} catch {
|
|
3679
3815
|
}
|
|
3680
|
-
const data = await res.json();
|
|
3681
|
-
const words = data?.verification_words ?? data?.verification_code ?? null;
|
|
3682
|
-
if (!words) throw new Error("No verification code in response");
|
|
3683
|
-
return Array.isArray(words) ? words.join(" ") : String(words);
|
|
3684
3816
|
}
|
|
3685
|
-
function
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
if (opts.nonInteractive) {
|
|
3689
|
-
await nonInteractiveRegister(config, opts);
|
|
3690
|
-
} else {
|
|
3691
|
-
await smartRegister(config, opts);
|
|
3692
|
-
}
|
|
3693
|
-
});
|
|
3817
|
+
function stopBackgroundNotifications() {
|
|
3818
|
+
activeConnection?.close();
|
|
3819
|
+
activeConnection = null;
|
|
3694
3820
|
}
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3821
|
+
process.on("exit", stopBackgroundNotifications);
|
|
3822
|
+
process.on("SIGINT", () => {
|
|
3823
|
+
stopBackgroundNotifications();
|
|
3824
|
+
process.exit(0);
|
|
3825
|
+
});
|
|
3826
|
+
|
|
3827
|
+
// src/helpers.ts
|
|
3828
|
+
var CONNECTION_ERROR_HINTS = {
|
|
3829
|
+
ECONNREFUSED: "Connection refused. Is the backend running?",
|
|
3830
|
+
ECONNRESET: "Connection reset by server. The backend may have restarted.",
|
|
3831
|
+
ENOTFOUND: "DNS resolution failed. Check the server URL.",
|
|
3832
|
+
ETIMEDOUT: "Connection timed out. Check network connectivity.",
|
|
3833
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: "TLS certificate cannot be verified. The cert may be expired or self-signed.",
|
|
3834
|
+
CERT_HAS_EXPIRED: "TLS certificate has expired. Renew with manage-deployment.",
|
|
3835
|
+
ERR_TLS_CERT_ALTNAME_INVALID: "TLS certificate hostname mismatch. Check the server URL.",
|
|
3836
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: "Self-signed TLS certificate. The cert may need to be renewed.",
|
|
3837
|
+
SELF_SIGNED_CERT_IN_CHAIN: "Self-signed certificate in chain. The cert may need to be renewed."
|
|
3838
|
+
};
|
|
3839
|
+
function diagnoseConnectionError(err) {
|
|
3840
|
+
const code = sdk.getErrorCode(err);
|
|
3841
|
+
if (code && code in CONNECTION_ERROR_HINTS) {
|
|
3842
|
+
return CONNECTION_ERROR_HINTS[code];
|
|
3700
3843
|
}
|
|
3701
|
-
const
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
const
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
verificationCode = await promptInput("Invitation code");
|
|
3717
|
-
} else {
|
|
3718
|
-
console.log("Sending verification email...");
|
|
3719
|
-
const verifyResponse = await arbi.fetch.POST("/v1/user/verify-email", {
|
|
3720
|
-
body: { email }
|
|
3721
|
-
});
|
|
3722
|
-
if (verifyResponse.error) {
|
|
3723
|
-
error(`Failed to send verification email: ${JSON.stringify(verifyResponse.error)}`);
|
|
3724
|
-
process.exit(1);
|
|
3725
|
-
}
|
|
3726
|
-
success("Verification email sent. Check your inbox.");
|
|
3727
|
-
verificationCode = await promptInput("Verification code");
|
|
3844
|
+
const msg = err instanceof Error ? err.message : "";
|
|
3845
|
+
if (msg === "fetch failed" || msg.includes("fetch failed")) {
|
|
3846
|
+
return "Network error connecting to the server. Run `arbi health` to diagnose.";
|
|
3847
|
+
}
|
|
3848
|
+
return void 0;
|
|
3849
|
+
}
|
|
3850
|
+
function formatCliError(err) {
|
|
3851
|
+
const connectionHint = diagnoseConnectionError(err);
|
|
3852
|
+
if (connectionHint) return connectionHint;
|
|
3853
|
+
if (err instanceof sdk.ArbiApiError && err.apiError && typeof err.apiError === "object") {
|
|
3854
|
+
const base = err.message;
|
|
3855
|
+
const apiErr = err.apiError;
|
|
3856
|
+
const detail = apiErr.detail ?? apiErr.message ?? apiErr.error;
|
|
3857
|
+
if (typeof detail === "string" && detail && !base.includes(detail)) {
|
|
3858
|
+
return `${base} \u2014 ${detail}`;
|
|
3728
3859
|
}
|
|
3860
|
+
return base;
|
|
3729
3861
|
}
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
error(
|
|
3862
|
+
return sdk.getErrorMessage(err);
|
|
3863
|
+
}
|
|
3864
|
+
function runAction(fn) {
|
|
3865
|
+
return async () => {
|
|
3866
|
+
try {
|
|
3867
|
+
await fn();
|
|
3868
|
+
process.exit(0);
|
|
3869
|
+
} catch (err) {
|
|
3870
|
+
error(`Error: ${formatCliError(err)}`);
|
|
3871
|
+
hintUpdateOnError();
|
|
3739
3872
|
process.exit(1);
|
|
3740
3873
|
}
|
|
3741
|
-
}
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
const lastName = opts.lastName || (hasAllCoreFlags ? "" : await promptInput("Last name", false) || "");
|
|
3874
|
+
};
|
|
3875
|
+
}
|
|
3876
|
+
async function resolveAuth() {
|
|
3745
3877
|
try {
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3878
|
+
resolveConfig();
|
|
3879
|
+
return await sdk.resolveAuth(store);
|
|
3880
|
+
} catch (err) {
|
|
3881
|
+
if (err instanceof sdk.ArbiError) {
|
|
3882
|
+
error(err.message);
|
|
3883
|
+
process.exit(1);
|
|
3884
|
+
}
|
|
3885
|
+
throw err;
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
async function resolveWorkspace(workspaceOpt) {
|
|
3889
|
+
try {
|
|
3890
|
+
resolveConfig();
|
|
3891
|
+
const ctx = await sdk.resolveWorkspace(store, workspaceOpt);
|
|
3892
|
+
if (getConfig()?.notifications !== false) {
|
|
3893
|
+
startBackgroundNotifications(ctx.config.baseUrl, ctx.accessToken).catch(() => {
|
|
3894
|
+
});
|
|
3895
|
+
}
|
|
3896
|
+
return ctx;
|
|
3897
|
+
} catch (err) {
|
|
3898
|
+
if (err instanceof sdk.ArbiError) {
|
|
3899
|
+
error(err.message);
|
|
3900
|
+
process.exit(1);
|
|
3901
|
+
}
|
|
3902
|
+
throw err;
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
function printTable(columns, rows) {
|
|
3906
|
+
console.log(chalk2__default.default.bold(columns.map((c) => c.header.padEnd(c.width)).join("")));
|
|
3907
|
+
for (const row of rows) {
|
|
3908
|
+
console.log(
|
|
3909
|
+
columns.map((c) => {
|
|
3910
|
+
const val = c.value(row);
|
|
3911
|
+
return val.slice(0, c.width - 2).padEnd(c.width);
|
|
3912
|
+
}).join("")
|
|
3913
|
+
);
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
function parseJsonArg(input2, example) {
|
|
3917
|
+
try {
|
|
3918
|
+
return JSON.parse(input2);
|
|
3919
|
+
} catch {
|
|
3920
|
+
error(`Invalid JSON. Example: ${example}`);
|
|
3921
|
+
process.exit(1);
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
// src/commands/login.ts
|
|
3926
|
+
function registerLoginCommand(program2) {
|
|
3927
|
+
program2.command("login").description("Log in to ARBI").option("-e, --email <email>", "Email address (or ARBI_EMAIL env var)").option("-p, --password <password>", "Password (or ARBI_PASSWORD env var)").option("-w, --workspace <id>", "Workspace ID to select after login").option("--sso", "Log in with Auth0 SSO (device flow)").action(
|
|
3928
|
+
(opts) => runAction(async () => {
|
|
3929
|
+
const config = store.requireConfig();
|
|
3930
|
+
const email = opts.email || process.env.ARBI_EMAIL || await promptInput("Email");
|
|
3931
|
+
const pw = opts.password || process.env.ARBI_PASSWORD || await promptPassword("Password");
|
|
3932
|
+
try {
|
|
3933
|
+
let ssoPolling = false;
|
|
3934
|
+
const { arbi } = opts.sso ? await (async () => {
|
|
3935
|
+
const result = await sdk.performSsoDeviceFlowLogin(config, email, pw, store, {
|
|
3936
|
+
onUserCode: (userCode, verificationUri) => {
|
|
3937
|
+
console.log(`
|
|
3938
|
+
Open this URL in your browser:
|
|
3939
|
+
${verificationUri}
|
|
3940
|
+
`);
|
|
3941
|
+
console.log(`And enter code: ${userCode}
|
|
3942
|
+
`);
|
|
3943
|
+
console.log("Waiting for authorization...");
|
|
3944
|
+
ssoPolling = true;
|
|
3945
|
+
},
|
|
3946
|
+
onPoll: () => {
|
|
3947
|
+
process.stdout.write(".");
|
|
3948
|
+
}
|
|
3949
|
+
});
|
|
3950
|
+
if (ssoPolling) console.log("\n");
|
|
3951
|
+
return result;
|
|
3952
|
+
})() : await sdk.performPasswordLogin(config, email, pw, store);
|
|
3953
|
+
clearChatSession();
|
|
3954
|
+
const { data: workspaces3 } = await arbi.fetch.GET("/v1/user/workspaces");
|
|
3955
|
+
const wsList = workspaces3 || [];
|
|
3956
|
+
updateCompletionCache(wsList);
|
|
3957
|
+
const memberWorkspaces = wsList.filter(
|
|
3958
|
+
(ws2) => ws2.users?.some((u) => u.user.email === email)
|
|
3959
|
+
);
|
|
3960
|
+
if (memberWorkspaces.length === 0) {
|
|
3961
|
+
console.log("No workspaces found. Create one with: arbi workspace create <name>");
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3964
|
+
if (opts.workspace) {
|
|
3965
|
+
const ws2 = memberWorkspaces.find((w) => w.external_id === opts.workspace);
|
|
3966
|
+
if (!ws2) {
|
|
3967
|
+
error(`Workspace ${opts.workspace} not found or you don't have access.`);
|
|
3968
|
+
process.exit(1);
|
|
3969
|
+
}
|
|
3970
|
+
updateConfig({ selectedWorkspaceId: ws2.external_id });
|
|
3971
|
+
success(`Workspace: ${ws2.name} (${ref(ws2.external_id)})`);
|
|
3972
|
+
return;
|
|
3973
|
+
}
|
|
3974
|
+
if (memberWorkspaces.length === 1) {
|
|
3975
|
+
updateConfig({ selectedWorkspaceId: memberWorkspaces[0].external_id });
|
|
3976
|
+
success(
|
|
3977
|
+
`Workspace: ${memberWorkspaces[0].name} (${ref(memberWorkspaces[0].external_id)})`
|
|
3978
|
+
);
|
|
3979
|
+
return;
|
|
3980
|
+
}
|
|
3981
|
+
const choices = sdk.formatWorkspaceChoices(memberWorkspaces);
|
|
3982
|
+
const selected = await promptSelect("Select workspace", choices);
|
|
3983
|
+
updateConfig({ selectedWorkspaceId: selected });
|
|
3984
|
+
const ws = memberWorkspaces.find((w) => w.external_id === selected);
|
|
3985
|
+
success(`Workspace: ${ws.name} (${ref(selected)})`);
|
|
3986
|
+
dim('\nTip: Run "arbi config alias" to use A as a shortcut for "arbi ask"');
|
|
3987
|
+
} catch (err) {
|
|
3988
|
+
error(`Login failed: ${formatCliError(err)}`);
|
|
3989
|
+
process.exit(1);
|
|
3990
|
+
} finally {
|
|
3991
|
+
await checkForUpdates(getConfig()?.autoUpdate);
|
|
3992
|
+
}
|
|
3993
|
+
})()
|
|
3994
|
+
);
|
|
3995
|
+
}
|
|
3996
|
+
var CENTRAL_API_URL = "https://central.arbi.work";
|
|
3997
|
+
async function getVerificationCode(email, apiKey) {
|
|
3998
|
+
const params = new URLSearchParams({ email });
|
|
3999
|
+
const res = await fetch(`${CENTRAL_API_URL}/license-management/verify-ci?${params.toString()}`, {
|
|
4000
|
+
method: "GET",
|
|
4001
|
+
headers: { "x-api-key": apiKey }
|
|
4002
|
+
});
|
|
4003
|
+
if (!res.ok) {
|
|
4004
|
+
const body = await res.text().catch(() => "");
|
|
4005
|
+
throw new Error(`Failed to get verification code: ${res.status} ${body}`);
|
|
4006
|
+
}
|
|
4007
|
+
const data = await res.json();
|
|
4008
|
+
const words = data?.verification_words ?? data?.verification_code ?? null;
|
|
4009
|
+
if (!words) throw new Error("No verification code in response");
|
|
4010
|
+
return Array.isArray(words) ? words.join(" ") : String(words);
|
|
4011
|
+
}
|
|
4012
|
+
function registerRegisterCommand(program2) {
|
|
4013
|
+
program2.command("register").description("Register a new ARBI account").option("--non-interactive", "CI/automation mode (requires SUPPORT_API_KEY env var)").option("-e, --email <email>", "Email address (or ARBI_EMAIL env var)").option("-p, --password <password>", "Password (or ARBI_PASSWORD env var)").option("-c, --verification-code <code>", "Verification code (skip prompt)").option("--first-name <name>", "First name").option("--last-name <name>", "Last name").action(
|
|
4014
|
+
(opts) => runAction(async () => {
|
|
4015
|
+
const config = requireConfig();
|
|
4016
|
+
if (opts.nonInteractive) {
|
|
4017
|
+
await nonInteractiveRegister(config, opts);
|
|
4018
|
+
} else {
|
|
4019
|
+
await smartRegister(config, opts);
|
|
4020
|
+
}
|
|
4021
|
+
})()
|
|
4022
|
+
);
|
|
4023
|
+
}
|
|
4024
|
+
async function smartRegister(config, opts) {
|
|
4025
|
+
let email = opts.email || process.env.ARBI_EMAIL || await promptInput("Email");
|
|
4026
|
+
if ((opts.email || process.env.ARBI_EMAIL) && !email.includes("@")) {
|
|
4027
|
+
email = `${email}@${config.deploymentDomain}`;
|
|
4028
|
+
console.log(`Using email: ${email}`);
|
|
4029
|
+
}
|
|
4030
|
+
const arbi = client.createArbiClient({
|
|
4031
|
+
baseUrl: config.baseUrl,
|
|
4032
|
+
deploymentDomain: config.deploymentDomain,
|
|
4033
|
+
credentials: "omit"
|
|
4034
|
+
});
|
|
4035
|
+
await arbi.crypto.initSodium();
|
|
4036
|
+
let verificationCode;
|
|
4037
|
+
if (opts.verificationCode) {
|
|
4038
|
+
verificationCode = opts.verificationCode;
|
|
4039
|
+
} else {
|
|
4040
|
+
const codeMethod = await promptSelect("Verification method", [
|
|
4041
|
+
{ name: "I have an invitation code", value: "code" },
|
|
4042
|
+
{ name: "Send me a verification email", value: "email" }
|
|
4043
|
+
]);
|
|
4044
|
+
if (codeMethod === "code") {
|
|
4045
|
+
verificationCode = await promptInput("Invitation code");
|
|
4046
|
+
} else {
|
|
4047
|
+
console.log("Sending verification email...");
|
|
4048
|
+
const verifyResponse = await arbi.fetch.POST("/v1/user/verify-email", {
|
|
4049
|
+
body: { email }
|
|
4050
|
+
});
|
|
4051
|
+
if (verifyResponse.error) {
|
|
4052
|
+
error(`Failed to send verification email: ${JSON.stringify(verifyResponse.error)}`);
|
|
4053
|
+
process.exit(1);
|
|
4054
|
+
}
|
|
4055
|
+
success("Verification email sent. Check your inbox.");
|
|
4056
|
+
verificationCode = await promptInput("Verification code");
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
let pw;
|
|
4060
|
+
const flagOrEnvPassword = opts.password || process.env.ARBI_PASSWORD;
|
|
4061
|
+
if (flagOrEnvPassword) {
|
|
4062
|
+
pw = flagOrEnvPassword;
|
|
4063
|
+
} else {
|
|
4064
|
+
pw = await promptPassword("Password");
|
|
4065
|
+
const confirmPw = await promptPassword("Confirm password");
|
|
4066
|
+
if (pw !== confirmPw) {
|
|
4067
|
+
error("Passwords do not match.");
|
|
4068
|
+
process.exit(1);
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
const hasAllCoreFlags = !!(opts.email || process.env.ARBI_EMAIL) && !!(opts.password || process.env.ARBI_PASSWORD) && !!opts.verificationCode;
|
|
4072
|
+
const firstName = opts.firstName || (hasAllCoreFlags ? "User" : await promptInput("First name", false) || "User");
|
|
4073
|
+
const lastName = opts.lastName || (hasAllCoreFlags ? "" : await promptInput("Last name", false) || "");
|
|
4074
|
+
try {
|
|
4075
|
+
await arbi.auth.register({
|
|
4076
|
+
email,
|
|
4077
|
+
password: pw,
|
|
4078
|
+
verificationCode,
|
|
4079
|
+
firstName,
|
|
3751
4080
|
lastName
|
|
3752
4081
|
});
|
|
3753
4082
|
success(`
|
|
3754
4083
|
Registered successfully as ${email}`);
|
|
3755
4084
|
} catch (err) {
|
|
3756
|
-
error(`Registration failed: ${
|
|
4085
|
+
error(`Registration failed: ${formatCliError(err)}`);
|
|
3757
4086
|
process.exit(1);
|
|
3758
4087
|
}
|
|
3759
4088
|
const allFlagsProvided = !!(opts.email || process.env.ARBI_EMAIL) && !!(opts.password || process.env.ARBI_PASSWORD) && !!opts.verificationCode;
|
|
@@ -3813,7 +4142,7 @@ async function nonInteractiveRegister(config, opts) {
|
|
|
3813
4142
|
});
|
|
3814
4143
|
success(`Registered: ${email}`);
|
|
3815
4144
|
} catch (err) {
|
|
3816
|
-
error(`Registration failed: ${
|
|
4145
|
+
error(`Registration failed: ${formatCliError(err)}`);
|
|
3817
4146
|
process.exit(1);
|
|
3818
4147
|
}
|
|
3819
4148
|
await loginAfterRegister(config, email, password2);
|
|
@@ -3822,8 +4151,9 @@ async function loginAfterRegister(config, email, password2) {
|
|
|
3822
4151
|
try {
|
|
3823
4152
|
const { arbi } = await sdk.performPasswordLogin(config, email, password2, store);
|
|
3824
4153
|
success(`Logged in as ${email}`);
|
|
3825
|
-
const { data:
|
|
3826
|
-
const wsList =
|
|
4154
|
+
const { data: workspaces3 } = await arbi.fetch.GET("/v1/user/workspaces");
|
|
4155
|
+
const wsList = workspaces3 || [];
|
|
4156
|
+
updateCompletionCache(wsList);
|
|
3827
4157
|
const memberWorkspaces = wsList.filter((ws2) => ws2.users?.some((u) => u.email === email));
|
|
3828
4158
|
if (memberWorkspaces.length === 0) {
|
|
3829
4159
|
console.log("Creating your first workspace...");
|
|
@@ -3843,7 +4173,7 @@ async function loginAfterRegister(config, email, password2) {
|
|
|
3843
4173
|
const ws = memberWorkspaces.find((w) => w.external_id === selected);
|
|
3844
4174
|
success(`Workspace: ${ws.name} (${ref(selected)})`);
|
|
3845
4175
|
} catch (err) {
|
|
3846
|
-
error(`Login failed: ${
|
|
4176
|
+
error(`Login failed: ${formatCliError(err)}`);
|
|
3847
4177
|
error("You can log in later with: arbi login");
|
|
3848
4178
|
}
|
|
3849
4179
|
}
|
|
@@ -3880,217 +4210,12 @@ function registerStatusCommand(program2) {
|
|
|
3880
4210
|
}
|
|
3881
4211
|
});
|
|
3882
4212
|
}
|
|
3883
|
-
var MAX_TASKS = 50;
|
|
3884
|
-
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3885
|
-
function getTasksFile() {
|
|
3886
|
-
const configDir = process.env.ARBI_CONFIG_DIR ?? path__default.default.join(os__default.default.homedir(), ".arbi");
|
|
3887
|
-
return path__default.default.join(configDir, "tasks.json");
|
|
3888
|
-
}
|
|
3889
|
-
function ensureDir(filePath) {
|
|
3890
|
-
const dir = path__default.default.dirname(filePath);
|
|
3891
|
-
if (!fs__default.default.existsSync(dir)) {
|
|
3892
|
-
fs__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
3893
|
-
}
|
|
3894
|
-
}
|
|
3895
|
-
function readTasks() {
|
|
3896
|
-
try {
|
|
3897
|
-
const content = fs__default.default.readFileSync(getTasksFile(), "utf-8");
|
|
3898
|
-
return JSON.parse(content);
|
|
3899
|
-
} catch {
|
|
3900
|
-
return [];
|
|
3901
|
-
}
|
|
3902
|
-
}
|
|
3903
|
-
function writeTasks(tasks) {
|
|
3904
|
-
const filePath = getTasksFile();
|
|
3905
|
-
ensureDir(filePath);
|
|
3906
|
-
fs__default.default.writeFileSync(filePath, JSON.stringify(tasks, null, 2) + "\n", { mode: 384 });
|
|
3907
|
-
}
|
|
3908
|
-
function getTasks() {
|
|
3909
|
-
const now = Date.now();
|
|
3910
|
-
const tasks = readTasks().filter((t) => now - new Date(t.submittedAt).getTime() < MAX_AGE_MS);
|
|
3911
|
-
return tasks;
|
|
3912
|
-
}
|
|
3913
|
-
function addTask(task) {
|
|
3914
|
-
const tasks = [task, ...getTasks().filter((t) => t.id !== task.id)].slice(0, MAX_TASKS);
|
|
3915
|
-
writeTasks(tasks);
|
|
3916
|
-
}
|
|
3917
|
-
function updateTaskStatus(id, status2) {
|
|
3918
|
-
const tasks = readTasks();
|
|
3919
|
-
const task = tasks.find((t) => t.id === id);
|
|
3920
|
-
if (task) {
|
|
3921
|
-
task.status = status2;
|
|
3922
|
-
writeTasks(tasks);
|
|
3923
|
-
}
|
|
3924
|
-
}
|
|
3925
|
-
function getLatestTask() {
|
|
3926
|
-
const tasks = getTasks();
|
|
3927
|
-
return tasks[0] ?? null;
|
|
3928
|
-
}
|
|
3929
|
-
|
|
3930
|
-
// src/notifications.ts
|
|
3931
|
-
var activeConnection = null;
|
|
3932
|
-
function timestamp() {
|
|
3933
|
-
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", { hour12: false });
|
|
3934
|
-
}
|
|
3935
|
-
function colorize(level, text) {
|
|
3936
|
-
if (level === "success") return chalk2__default.default.green(text);
|
|
3937
|
-
if (level === "error") return chalk2__default.default.red(text);
|
|
3938
|
-
if (level === "warning") return chalk2__default.default.yellow(text);
|
|
3939
|
-
return text;
|
|
3940
|
-
}
|
|
3941
|
-
async function startBackgroundNotifications(baseUrl, accessToken) {
|
|
3942
|
-
if (activeConnection) return;
|
|
3943
|
-
try {
|
|
3944
|
-
activeConnection = await sdk.connectWithReconnect({
|
|
3945
|
-
baseUrl,
|
|
3946
|
-
accessToken,
|
|
3947
|
-
onMessage: (msg) => {
|
|
3948
|
-
if (client.isMessageType(msg, "response_complete")) {
|
|
3949
|
-
updateTaskStatus(msg.response_id, msg.status);
|
|
3950
|
-
}
|
|
3951
|
-
const { text, level } = sdk.formatWsMessage(msg);
|
|
3952
|
-
process.stderr.write(`
|
|
3953
|
-
${colorize(level, `[${timestamp()}] ${text}`)}
|
|
3954
|
-
`);
|
|
3955
|
-
},
|
|
3956
|
-
onClose: () => {
|
|
3957
|
-
activeConnection = null;
|
|
3958
|
-
},
|
|
3959
|
-
onReconnecting: (attempt, maxRetries) => {
|
|
3960
|
-
process.stderr.write(
|
|
3961
|
-
`
|
|
3962
|
-
${chalk2__default.default.yellow(`[${timestamp()}] Reconnecting... (${attempt}/${maxRetries})`)}
|
|
3963
|
-
`
|
|
3964
|
-
);
|
|
3965
|
-
},
|
|
3966
|
-
onReconnected: () => {
|
|
3967
|
-
process.stderr.write(`
|
|
3968
|
-
${chalk2__default.default.green(`[${timestamp()}] Reconnected`)}
|
|
3969
|
-
`);
|
|
3970
|
-
},
|
|
3971
|
-
onReconnectFailed: () => {
|
|
3972
|
-
process.stderr.write(`
|
|
3973
|
-
${chalk2__default.default.red(`[${timestamp()}] Reconnection failed`)}
|
|
3974
|
-
`);
|
|
3975
|
-
}
|
|
3976
|
-
});
|
|
3977
|
-
} catch {
|
|
3978
|
-
}
|
|
3979
|
-
}
|
|
3980
|
-
function stopBackgroundNotifications() {
|
|
3981
|
-
activeConnection?.close();
|
|
3982
|
-
activeConnection = null;
|
|
3983
|
-
}
|
|
3984
|
-
process.on("exit", stopBackgroundNotifications);
|
|
3985
|
-
process.on("SIGINT", () => {
|
|
3986
|
-
stopBackgroundNotifications();
|
|
3987
|
-
process.exit(0);
|
|
3988
|
-
});
|
|
3989
|
-
|
|
3990
|
-
// src/helpers.ts
|
|
3991
|
-
var CONNECTION_ERROR_HINTS = {
|
|
3992
|
-
ECONNREFUSED: "Connection refused. Is the backend running?",
|
|
3993
|
-
ECONNRESET: "Connection reset by server. The backend may have restarted.",
|
|
3994
|
-
ENOTFOUND: "DNS resolution failed. Check the server URL.",
|
|
3995
|
-
ETIMEDOUT: "Connection timed out. Check network connectivity.",
|
|
3996
|
-
UNABLE_TO_VERIFY_LEAF_SIGNATURE: "TLS certificate cannot be verified. The cert may be expired or self-signed.",
|
|
3997
|
-
CERT_HAS_EXPIRED: "TLS certificate has expired. Renew with manage-deployment.",
|
|
3998
|
-
ERR_TLS_CERT_ALTNAME_INVALID: "TLS certificate hostname mismatch. Check the server URL.",
|
|
3999
|
-
DEPTH_ZERO_SELF_SIGNED_CERT: "Self-signed TLS certificate. The cert may need to be renewed.",
|
|
4000
|
-
SELF_SIGNED_CERT_IN_CHAIN: "Self-signed certificate in chain. The cert may need to be renewed."
|
|
4001
|
-
};
|
|
4002
|
-
function diagnoseConnectionError(err) {
|
|
4003
|
-
const code = sdk.getErrorCode(err);
|
|
4004
|
-
if (code && code in CONNECTION_ERROR_HINTS) {
|
|
4005
|
-
return CONNECTION_ERROR_HINTS[code];
|
|
4006
|
-
}
|
|
4007
|
-
const msg = err instanceof Error ? err.message : "";
|
|
4008
|
-
if (msg === "fetch failed" || msg.includes("fetch failed")) {
|
|
4009
|
-
return "Network error connecting to the server. Run `arbi health` to diagnose.";
|
|
4010
|
-
}
|
|
4011
|
-
return void 0;
|
|
4012
|
-
}
|
|
4013
|
-
function formatCliError(err) {
|
|
4014
|
-
const connectionHint = diagnoseConnectionError(err);
|
|
4015
|
-
if (connectionHint) return connectionHint;
|
|
4016
|
-
if (err instanceof sdk.ArbiApiError && err.apiError && typeof err.apiError === "object") {
|
|
4017
|
-
const base = err.message;
|
|
4018
|
-
const apiErr = err.apiError;
|
|
4019
|
-
const detail = apiErr.detail ?? apiErr.message ?? apiErr.error;
|
|
4020
|
-
if (typeof detail === "string" && detail && !base.includes(detail)) {
|
|
4021
|
-
return `${base} \u2014 ${detail}`;
|
|
4022
|
-
}
|
|
4023
|
-
return base;
|
|
4024
|
-
}
|
|
4025
|
-
return sdk.getErrorMessage(err);
|
|
4026
|
-
}
|
|
4027
|
-
function runAction(fn) {
|
|
4028
|
-
return async () => {
|
|
4029
|
-
try {
|
|
4030
|
-
await fn();
|
|
4031
|
-
process.exit(0);
|
|
4032
|
-
} catch (err) {
|
|
4033
|
-
error(`Error: ${formatCliError(err)}`);
|
|
4034
|
-
hintUpdateOnError();
|
|
4035
|
-
process.exit(1);
|
|
4036
|
-
}
|
|
4037
|
-
};
|
|
4038
|
-
}
|
|
4039
|
-
async function resolveAuth() {
|
|
4040
|
-
try {
|
|
4041
|
-
resolveConfig();
|
|
4042
|
-
return await sdk.resolveAuth(store);
|
|
4043
|
-
} catch (err) {
|
|
4044
|
-
if (err instanceof sdk.ArbiError) {
|
|
4045
|
-
error(err.message);
|
|
4046
|
-
process.exit(1);
|
|
4047
|
-
}
|
|
4048
|
-
throw err;
|
|
4049
|
-
}
|
|
4050
|
-
}
|
|
4051
|
-
async function resolveWorkspace(workspaceOpt) {
|
|
4052
|
-
try {
|
|
4053
|
-
resolveConfig();
|
|
4054
|
-
const ctx = await sdk.resolveWorkspace(store, workspaceOpt);
|
|
4055
|
-
if (getConfig()?.notifications !== false) {
|
|
4056
|
-
startBackgroundNotifications(ctx.config.baseUrl, ctx.accessToken).catch(() => {
|
|
4057
|
-
});
|
|
4058
|
-
}
|
|
4059
|
-
return ctx;
|
|
4060
|
-
} catch (err) {
|
|
4061
|
-
if (err instanceof sdk.ArbiError) {
|
|
4062
|
-
error(err.message);
|
|
4063
|
-
process.exit(1);
|
|
4064
|
-
}
|
|
4065
|
-
throw err;
|
|
4066
|
-
}
|
|
4067
|
-
}
|
|
4068
|
-
function printTable(columns, rows) {
|
|
4069
|
-
console.log(chalk2__default.default.bold(columns.map((c) => c.header.padEnd(c.width)).join("")));
|
|
4070
|
-
for (const row of rows) {
|
|
4071
|
-
console.log(
|
|
4072
|
-
columns.map((c) => {
|
|
4073
|
-
const val = c.value(row);
|
|
4074
|
-
return val.slice(0, c.width - 2).padEnd(c.width);
|
|
4075
|
-
}).join("")
|
|
4076
|
-
);
|
|
4077
|
-
}
|
|
4078
|
-
}
|
|
4079
|
-
function parseJsonArg(input2, example) {
|
|
4080
|
-
try {
|
|
4081
|
-
return JSON.parse(input2);
|
|
4082
|
-
} catch {
|
|
4083
|
-
error(`Invalid JSON. Example: ${example}`);
|
|
4084
|
-
process.exit(1);
|
|
4085
|
-
}
|
|
4086
|
-
}
|
|
4087
|
-
|
|
4088
|
-
// src/commands/workspaces.ts
|
|
4089
4213
|
function registerWorkspacesCommand(program2) {
|
|
4090
4214
|
program2.command("workspaces").description("List workspaces").action(
|
|
4091
4215
|
runAction(async () => {
|
|
4092
4216
|
const { arbi } = await resolveAuth();
|
|
4093
4217
|
const data = await sdk.workspaces.listWorkspaces(arbi);
|
|
4218
|
+
updateCompletionCache(data);
|
|
4094
4219
|
if (data.length === 0) {
|
|
4095
4220
|
console.log("No workspaces found.");
|
|
4096
4221
|
return;
|
|
@@ -4119,6 +4244,7 @@ function registerWorkspacesCommand(program2) {
|
|
|
4119
4244
|
(id) => runAction(async () => {
|
|
4120
4245
|
const { arbi } = await resolveAuth();
|
|
4121
4246
|
const data = await sdk.workspaces.listWorkspaces(arbi);
|
|
4247
|
+
updateCompletionCache(data);
|
|
4122
4248
|
if (data.length === 0) {
|
|
4123
4249
|
console.log("No workspaces found.");
|
|
4124
4250
|
return;
|
|
@@ -4171,17 +4297,17 @@ function registerWorkspacesCommand(program2) {
|
|
|
4171
4297
|
success(`Deleted workspace ${targetId}`);
|
|
4172
4298
|
})()
|
|
4173
4299
|
);
|
|
4174
|
-
workspace.command("update <
|
|
4175
|
-
(
|
|
4176
|
-
const body = parseJsonArg(json, `arbi workspace update
|
|
4177
|
-
const { arbi } = await resolveWorkspace(
|
|
4300
|
+
workspace.command("update <json>").description("Update workspace properties (pass JSON)").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(
|
|
4301
|
+
(json, opts) => runAction(async () => {
|
|
4302
|
+
const body = parseJsonArg(json, `arbi workspace update '{"name": "New Name"}'`);
|
|
4303
|
+
const { arbi } = await resolveWorkspace(opts.workspace);
|
|
4178
4304
|
const data = await sdk.workspaces.updateWorkspace(arbi, body);
|
|
4179
4305
|
success(`Updated: ${data.name} (${ref(data.external_id)})`);
|
|
4180
4306
|
})()
|
|
4181
4307
|
);
|
|
4182
4308
|
workspace.command("users").description("List users in the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(
|
|
4183
4309
|
(opts) => runAction(async () => {
|
|
4184
|
-
const { arbi
|
|
4310
|
+
const { arbi } = await resolveWorkspace(opts.workspace);
|
|
4185
4311
|
const data = await sdk.workspaces.listWorkspaceUsers(arbi);
|
|
4186
4312
|
if (data.length === 0) {
|
|
4187
4313
|
console.log("No users found.");
|
|
@@ -4217,7 +4343,7 @@ function registerWorkspacesCommand(program2) {
|
|
|
4217
4343
|
);
|
|
4218
4344
|
workspace.command("add-user <emails...>").description("Add users to the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("-r, --role <role>", "Role: owner, collaborator, guest", "collaborator").action(
|
|
4219
4345
|
(emails, opts) => runAction(async () => {
|
|
4220
|
-
const { arbi
|
|
4346
|
+
const { arbi } = await resolveWorkspace(opts.workspace);
|
|
4221
4347
|
const role = opts.role ?? "collaborator";
|
|
4222
4348
|
const data = await sdk.workspaces.addWorkspaceUsers(arbi, emails, role);
|
|
4223
4349
|
for (const u of data) success(`Added: ${u.user.email} as ${u.role}`);
|
|
@@ -4225,14 +4351,14 @@ function registerWorkspacesCommand(program2) {
|
|
|
4225
4351
|
);
|
|
4226
4352
|
workspace.command("remove-user <user-ids...>").description("Remove users from the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(
|
|
4227
4353
|
(userIds, opts) => runAction(async () => {
|
|
4228
|
-
const { arbi
|
|
4354
|
+
const { arbi } = await resolveWorkspace(opts.workspace);
|
|
4229
4355
|
await sdk.workspaces.removeWorkspaceUsers(arbi, userIds);
|
|
4230
4356
|
success(`Removed ${userIds.length} user(s).`);
|
|
4231
4357
|
})()
|
|
4232
4358
|
);
|
|
4233
4359
|
workspace.command("set-role <role> <user-ids...>").description("Update user roles (owner, collaborator, guest)").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").action(
|
|
4234
4360
|
(role, userIds, opts) => runAction(async () => {
|
|
4235
|
-
const { arbi
|
|
4361
|
+
const { arbi } = await resolveWorkspace(opts.workspace);
|
|
4236
4362
|
const data = await sdk.workspaces.setUserRole(
|
|
4237
4363
|
arbi,
|
|
4238
4364
|
userIds,
|
|
@@ -4243,14 +4369,15 @@ function registerWorkspacesCommand(program2) {
|
|
|
4243
4369
|
);
|
|
4244
4370
|
workspace.command("copy <target-workspace-id> <doc-ids...>").description("Copy documents to another workspace").option("-w, --workspace <id>", "Source workspace ID (defaults to selected workspace)").action(
|
|
4245
4371
|
(targetId, docIds, opts) => runAction(async () => {
|
|
4246
|
-
const { arbi
|
|
4247
|
-
const
|
|
4248
|
-
const wsList = await sdk.workspaces.listWorkspaces(arbi);
|
|
4372
|
+
const { arbi: userArbi } = await resolveAuth();
|
|
4373
|
+
const wsList = await sdk.workspaces.listWorkspaces(userArbi);
|
|
4249
4374
|
const targetWs = wsList.find((w) => w.external_id === targetId);
|
|
4250
4375
|
if (!targetWs || !targetWs.wrapped_key) {
|
|
4251
4376
|
error(`Target workspace ${targetId} not found or has no encryption key`);
|
|
4252
4377
|
process.exit(1);
|
|
4253
4378
|
}
|
|
4379
|
+
const { arbi, loginResult } = await resolveWorkspace(opts.workspace);
|
|
4380
|
+
const signingPrivateKeyBase64 = arbi.crypto.bytesToBase64(loginResult.signingPrivateKey);
|
|
4254
4381
|
const targetKey = await sdk.generateEncryptedWorkspaceKey(
|
|
4255
4382
|
arbi,
|
|
4256
4383
|
targetWs.wrapped_key,
|
|
@@ -4449,7 +4576,7 @@ function registerUploadCommand(program2) {
|
|
|
4449
4576
|
program2.command("add <paths...>").alias("upload").description("Add files, directories, or zip archives to the active workspace").option("-w, --workspace <id>", "Workspace ID (defaults to selected workspace)").option("-W, --watch", "Watch document processing progress after upload").option("--no-watch", "Skip watching document processing").action(
|
|
4450
4577
|
(paths, opts) => runAction(async () => {
|
|
4451
4578
|
for (const p of paths) {
|
|
4452
|
-
if (!
|
|
4579
|
+
if (!fs2__default.default.existsSync(p)) {
|
|
4453
4580
|
error(`Path not found: ${p}`);
|
|
4454
4581
|
process.exit(1);
|
|
4455
4582
|
}
|
|
@@ -4460,7 +4587,7 @@ function registerUploadCommand(program2) {
|
|
|
4460
4587
|
const uploadedDocs = /* @__PURE__ */ new Map();
|
|
4461
4588
|
const auth = { baseUrl: config.baseUrl, accessToken, workspaceKeyHeader };
|
|
4462
4589
|
for (const filePath of paths) {
|
|
4463
|
-
const stat =
|
|
4590
|
+
const stat = fs2__default.default.statSync(filePath);
|
|
4464
4591
|
if (stat.isDirectory()) {
|
|
4465
4592
|
const result = await sdk.documentsNode.uploadDirectory(auth, workspaceId, filePath);
|
|
4466
4593
|
if (result.doc_ext_ids.length === 0) {
|
|
@@ -4596,7 +4723,7 @@ function registerDownloadCommand(program2) {
|
|
|
4596
4723
|
}
|
|
4597
4724
|
const outputPath = opts.output || path__default.default.join(process.cwd(), filename);
|
|
4598
4725
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
4599
|
-
|
|
4726
|
+
fs2__default.default.writeFileSync(outputPath, buffer);
|
|
4600
4727
|
success(
|
|
4601
4728
|
`Downloaded: ${path__default.default.basename(outputPath)} (${(buffer.length / (1024 * 1024)).toFixed(1)} MB)`
|
|
4602
4729
|
);
|
|
@@ -4682,13 +4809,22 @@ function registerAskCommand(program2) {
|
|
|
4682
4809
|
}
|
|
4683
4810
|
const verbose = opts.quiet === true ? false : getConfig()?.verbose !== false;
|
|
4684
4811
|
let elapsedTime = null;
|
|
4812
|
+
let firstToken = true;
|
|
4685
4813
|
const result = await sdk.streamSSE(res, {
|
|
4686
|
-
onToken: (content) =>
|
|
4814
|
+
onToken: (content) => {
|
|
4815
|
+
if (firstToken) {
|
|
4816
|
+
process.stderr.write(chalk2__default.default.dim("[ARBI] "));
|
|
4817
|
+
firstToken = false;
|
|
4818
|
+
}
|
|
4819
|
+
process.stdout.write(content);
|
|
4820
|
+
},
|
|
4687
4821
|
onAgentStep: (data) => {
|
|
4688
4822
|
if (verbose) {
|
|
4689
4823
|
const label2 = sdk.formatAgentStepLabel(data);
|
|
4690
|
-
if (label2)
|
|
4691
|
-
|
|
4824
|
+
if (label2 && data.step !== "answering") {
|
|
4825
|
+
console.error(chalk2__default.default.dim(`
|
|
4826
|
+
[ARBI] ${label2}`));
|
|
4827
|
+
}
|
|
4692
4828
|
}
|
|
4693
4829
|
},
|
|
4694
4830
|
onElapsedTime: (t) => {
|
|
@@ -4698,29 +4834,23 @@ function registerAskCommand(program2) {
|
|
|
4698
4834
|
Error: ${message}`))
|
|
4699
4835
|
});
|
|
4700
4836
|
process.stdout.write("\n");
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4837
|
+
if (result.metadata) {
|
|
4838
|
+
saveLastMetadata(result.metadata);
|
|
4839
|
+
}
|
|
4840
|
+
const refs = sdk.countCitations(result.metadata);
|
|
4841
|
+
const summary = opts.quiet !== true ? sdk.formatStreamSummary(result, elapsedTime) : "";
|
|
4842
|
+
if (summary) {
|
|
4843
|
+
const refSuffix = refs > 0 ? ` \xB7 ${refs} ref${refs === 1 ? "" : "s"}` : "";
|
|
4844
|
+
const text = `[${summary}${refSuffix}]`;
|
|
4845
|
+
const cols = process.stderr.columns || 80;
|
|
4846
|
+
const pad = Math.max(0, cols - text.length);
|
|
4847
|
+
console.error(chalk2__default.default.dim(" ".repeat(pad) + text));
|
|
4848
|
+
}
|
|
4849
|
+
if (refs > 0 && opts.quiet !== true) {
|
|
4850
|
+
console.error(
|
|
4851
|
+
chalk2__default.default.dim(`[${refs} citation${refs === 1 ? "" : "s"} \u2014 arbi cite to browse]`)
|
|
4716
4852
|
);
|
|
4717
4853
|
}
|
|
4718
|
-
if (elapsedTime != null) {
|
|
4719
|
-
parts.push(`${elapsedTime.toFixed(1)}s`);
|
|
4720
|
-
}
|
|
4721
|
-
if (parts.length > 0) {
|
|
4722
|
-
console.error(chalk2__default.default.dim(`[${parts.join(" \xB7 ")}]`));
|
|
4723
|
-
}
|
|
4724
4854
|
if (result.assistantMessageExtId) {
|
|
4725
4855
|
const updates = {
|
|
4726
4856
|
lastMessageExtId: result.assistantMessageExtId,
|
|
@@ -4735,6 +4865,87 @@ Error: ${message}`))
|
|
|
4735
4865
|
})()
|
|
4736
4866
|
);
|
|
4737
4867
|
}
|
|
4868
|
+
var MAX_PASSAGE_LINES = 40;
|
|
4869
|
+
function registerCiteCommand(program2) {
|
|
4870
|
+
program2.command("cite [number]").description("Browse citations from the last response").option("-a, --all", "Show all citations with full passages").action(
|
|
4871
|
+
(number, opts) => runAction(async () => {
|
|
4872
|
+
const raw = loadLastMetadata();
|
|
4873
|
+
if (!raw) {
|
|
4874
|
+
console.error(chalk2__default.default.dim("No citation data available. Run `arbi ask` first."));
|
|
4875
|
+
return;
|
|
4876
|
+
}
|
|
4877
|
+
const metadata = raw;
|
|
4878
|
+
const count = sdk.countCitations(metadata);
|
|
4879
|
+
if (count === 0) {
|
|
4880
|
+
console.log(chalk2__default.default.dim("The last response contained no citations."));
|
|
4881
|
+
return;
|
|
4882
|
+
}
|
|
4883
|
+
const resolved = sdk.resolveCitations(metadata);
|
|
4884
|
+
if (opts.all) {
|
|
4885
|
+
for (const r of resolved) {
|
|
4886
|
+
printCitationDetail(r.citationNum, resolved);
|
|
4887
|
+
console.log();
|
|
4888
|
+
}
|
|
4889
|
+
return;
|
|
4890
|
+
}
|
|
4891
|
+
if (number) {
|
|
4892
|
+
printCitationDetail(number, resolved);
|
|
4893
|
+
return;
|
|
4894
|
+
}
|
|
4895
|
+
const summaries = sdk.summarizeCitations(resolved);
|
|
4896
|
+
console.log(chalk2__default.default.bold(`Citations (${summaries.length}):
|
|
4897
|
+
`));
|
|
4898
|
+
for (const s of summaries) {
|
|
4899
|
+
const page = s.pageNumber != null ? `, p${s.pageNumber}` : "";
|
|
4900
|
+
const chunks = s.chunkCount > 1 ? ` (${s.chunkCount} passages)` : "";
|
|
4901
|
+
console.log(
|
|
4902
|
+
` ${chalk2__default.default.cyan(`[${s.citationNum}]`)} ${chalk2__default.default.bold(s.docTitle)}${page}${chunks}`
|
|
4903
|
+
);
|
|
4904
|
+
const truncated = s.statement.length > 120 ? s.statement.slice(0, 120).replace(/\n/g, " ").trim() + "..." : s.statement.replace(/\n/g, " ").trim();
|
|
4905
|
+
console.log(` ${chalk2__default.default.dim(truncated)}`);
|
|
4906
|
+
}
|
|
4907
|
+
console.log(chalk2__default.default.dim(`
|
|
4908
|
+
Use \`arbi cite <N>\` to view full passage.`));
|
|
4909
|
+
})()
|
|
4910
|
+
);
|
|
4911
|
+
}
|
|
4912
|
+
function printCitationDetail(num, resolved) {
|
|
4913
|
+
const citation = resolved.find((r) => r.citationNum === num);
|
|
4914
|
+
if (!citation) {
|
|
4915
|
+
console.error(chalk2__default.default.red(`Citation [${num}] not found.`));
|
|
4916
|
+
return;
|
|
4917
|
+
}
|
|
4918
|
+
const firstChunk = citation.chunks[0];
|
|
4919
|
+
const docTitle = firstChunk?.metadata?.doc_title ?? "Unknown document";
|
|
4920
|
+
const page = firstChunk?.metadata?.page_number;
|
|
4921
|
+
console.log(chalk2__default.default.bold.cyan(`[Citation ${citation.citationNum}]`) + " " + chalk2__default.default.bold(docTitle));
|
|
4922
|
+
if (page != null) {
|
|
4923
|
+
console.log(chalk2__default.default.dim(`Page ${page}`));
|
|
4924
|
+
}
|
|
4925
|
+
console.log();
|
|
4926
|
+
console.log(chalk2__default.default.bold("Statement:"));
|
|
4927
|
+
console.log(` ${citation.citationData.statement}`);
|
|
4928
|
+
console.log();
|
|
4929
|
+
for (let i = 0; i < citation.chunks.length; i++) {
|
|
4930
|
+
const chunk = citation.chunks[i];
|
|
4931
|
+
if (citation.chunks.length > 1) {
|
|
4932
|
+
console.log(chalk2__default.default.bold(`Passage ${i + 1}/${citation.chunks.length}:`));
|
|
4933
|
+
} else {
|
|
4934
|
+
console.log(chalk2__default.default.bold("Passage:"));
|
|
4935
|
+
}
|
|
4936
|
+
const lines = chunk.content.split("\n");
|
|
4937
|
+
if (lines.length > MAX_PASSAGE_LINES) {
|
|
4938
|
+
console.log(lines.slice(0, MAX_PASSAGE_LINES).join("\n"));
|
|
4939
|
+
console.log(chalk2__default.default.dim(` ... (${lines.length - MAX_PASSAGE_LINES} more lines)`));
|
|
4940
|
+
} else {
|
|
4941
|
+
console.log(chunk.content);
|
|
4942
|
+
}
|
|
4943
|
+
if (i < citation.chunks.length - 1) console.log();
|
|
4944
|
+
}
|
|
4945
|
+
if (citation.chunks.length === 0) {
|
|
4946
|
+
console.log(chalk2__default.default.dim(" (no passage data available)"));
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
4738
4949
|
function chunkScore(chunk) {
|
|
4739
4950
|
return chunk.metadata.rerank_score ?? chunk.metadata.score ?? 0;
|
|
4740
4951
|
}
|
|
@@ -5819,8 +6030,8 @@ function registerQuickstartCommand(program2) {
|
|
|
5819
6030
|
try {
|
|
5820
6031
|
const { arbi } = await sdk.performPasswordLogin(config, email, password2, store);
|
|
5821
6032
|
success(`Logged in as ${email}`);
|
|
5822
|
-
const { data:
|
|
5823
|
-
const wsList =
|
|
6033
|
+
const { data: workspaces3 } = await arbi.fetch.GET("/v1/user/workspaces");
|
|
6034
|
+
const wsList = workspaces3 || [];
|
|
5824
6035
|
const memberWorkspaces = wsList.filter((ws) => ws.users?.some((u) => u.email === email));
|
|
5825
6036
|
let workspaceId;
|
|
5826
6037
|
let workspaceName;
|
|
@@ -6052,6 +6263,85 @@ function registerTaskCommand(program2) {
|
|
|
6052
6263
|
})()
|
|
6053
6264
|
);
|
|
6054
6265
|
}
|
|
6266
|
+
var MARKER_START = "# arbi-cli completion start";
|
|
6267
|
+
var MARKER_END = "# arbi-cli completion end";
|
|
6268
|
+
var BASH_COMPLETION = `
|
|
6269
|
+
${MARKER_START}
|
|
6270
|
+
_arbi_completions() {
|
|
6271
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
6272
|
+
local candidates
|
|
6273
|
+
candidates=$(arbi --get-completions "\${COMP_LINE}" 2>/dev/null)
|
|
6274
|
+
COMPREPLY=($(compgen -W "$candidates" -- "$cur"))
|
|
6275
|
+
}
|
|
6276
|
+
complete -o default -F _arbi_completions arbi
|
|
6277
|
+
${MARKER_END}`;
|
|
6278
|
+
var ZSH_COMPLETION = `
|
|
6279
|
+
${MARKER_START}
|
|
6280
|
+
_arbi_completions() {
|
|
6281
|
+
local candidates
|
|
6282
|
+
candidates=(\${(f)"$(arbi --get-completions "\${words[*]}" 2>/dev/null)"})
|
|
6283
|
+
compadd -a candidates
|
|
6284
|
+
}
|
|
6285
|
+
compdef _arbi_completions arbi
|
|
6286
|
+
${MARKER_END}`;
|
|
6287
|
+
function getShellRcPath2() {
|
|
6288
|
+
const shell = process.env.SHELL || "";
|
|
6289
|
+
if (shell.includes("zsh")) return path.join(os.homedir(), ".zshrc");
|
|
6290
|
+
return path.join(os.homedir(), ".bashrc");
|
|
6291
|
+
}
|
|
6292
|
+
function isZsh() {
|
|
6293
|
+
return (process.env.SHELL || "").includes("zsh");
|
|
6294
|
+
}
|
|
6295
|
+
function isCompletionInstalled(rcPath) {
|
|
6296
|
+
if (!fs2.existsSync(rcPath)) return false;
|
|
6297
|
+
const content = fs2.readFileSync(rcPath, "utf-8");
|
|
6298
|
+
return content.includes(MARKER_START);
|
|
6299
|
+
}
|
|
6300
|
+
function removeCompletionBlock(content) {
|
|
6301
|
+
const startIdx = content.indexOf(MARKER_START);
|
|
6302
|
+
const endIdx = content.indexOf(MARKER_END);
|
|
6303
|
+
if (startIdx === -1 || endIdx === -1) return content;
|
|
6304
|
+
const before = content.substring(0, startIdx).replace(/\n+$/, "");
|
|
6305
|
+
const after = content.substring(endIdx + MARKER_END.length);
|
|
6306
|
+
return before + after;
|
|
6307
|
+
}
|
|
6308
|
+
function registerCompletionCommand(program2) {
|
|
6309
|
+
const completion = program2.command("completion").description("Manage shell tab completion");
|
|
6310
|
+
completion.command("install").description("Install shell tab completion (bash/zsh)").action(() => {
|
|
6311
|
+
const rcPath = getShellRcPath2();
|
|
6312
|
+
if (isCompletionInstalled(rcPath)) {
|
|
6313
|
+
success(`Completion already installed in ${rcPath}`);
|
|
6314
|
+
dim("If it's not working, run: source " + rcPath);
|
|
6315
|
+
return;
|
|
6316
|
+
}
|
|
6317
|
+
const snippet = isZsh() ? ZSH_COMPLETION : BASH_COMPLETION;
|
|
6318
|
+
fs2.appendFileSync(rcPath, snippet + "\n");
|
|
6319
|
+
success(`Installed tab completion in ${rcPath}`);
|
|
6320
|
+
dim("");
|
|
6321
|
+
dim(`Run: source ${rcPath}`);
|
|
6322
|
+
dim("Then try: arbi <TAB>");
|
|
6323
|
+
});
|
|
6324
|
+
completion.command("uninstall").description("Remove shell tab completion").action(() => {
|
|
6325
|
+
const rcPath = getShellRcPath2();
|
|
6326
|
+
if (!isCompletionInstalled(rcPath)) {
|
|
6327
|
+
dim("Completion is not installed.");
|
|
6328
|
+
return;
|
|
6329
|
+
}
|
|
6330
|
+
const content = fs2.readFileSync(rcPath, "utf-8");
|
|
6331
|
+
const cleaned = removeCompletionBlock(content);
|
|
6332
|
+
fs2.writeFileSync(rcPath, cleaned);
|
|
6333
|
+
success(`Removed tab completion from ${rcPath}`);
|
|
6334
|
+
dim(`Run: source ${rcPath}`);
|
|
6335
|
+
});
|
|
6336
|
+
completion.command("refresh").description("Refresh cached workspace IDs for tab completion").action(
|
|
6337
|
+
runAction(async () => {
|
|
6338
|
+
const { arbi } = await resolveAuth();
|
|
6339
|
+
const data = await sdk.workspaces.listWorkspaces(arbi);
|
|
6340
|
+
updateCompletionCache(data);
|
|
6341
|
+
success(`Cached ${data.length} workspace(s) for tab completion.`);
|
|
6342
|
+
})
|
|
6343
|
+
);
|
|
6344
|
+
}
|
|
6055
6345
|
|
|
6056
6346
|
// src/index.ts
|
|
6057
6347
|
console.debug = () => {
|
|
@@ -6062,7 +6352,7 @@ console.info = (...args) => {
|
|
|
6062
6352
|
_origInfo(...args);
|
|
6063
6353
|
};
|
|
6064
6354
|
var program = new commander.Command();
|
|
6065
|
-
program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.3.
|
|
6355
|
+
program.name("arbi").description("ARBI CLI \u2014 interact with ARBI from the terminal").version("0.3.18");
|
|
6066
6356
|
registerConfigCommand(program);
|
|
6067
6357
|
registerLoginCommand(program);
|
|
6068
6358
|
registerRegisterCommand(program);
|
|
@@ -6073,6 +6363,7 @@ registerDocsCommand(program);
|
|
|
6073
6363
|
registerUploadCommand(program);
|
|
6074
6364
|
registerDownloadCommand(program);
|
|
6075
6365
|
registerAskCommand(program);
|
|
6366
|
+
registerCiteCommand(program);
|
|
6076
6367
|
registerFindCommand(program);
|
|
6077
6368
|
registerWatchCommand(program);
|
|
6078
6369
|
registerContactsCommand(program);
|
|
@@ -6088,6 +6379,14 @@ registerUpdateCommand(program);
|
|
|
6088
6379
|
registerQuickstartCommand(program);
|
|
6089
6380
|
registerAgentCreateCommand(program);
|
|
6090
6381
|
registerTaskCommand(program);
|
|
6382
|
+
registerCompletionCommand(program);
|
|
6383
|
+
var completionIdx = process.argv.indexOf("--get-completions");
|
|
6384
|
+
if (completionIdx !== -1) {
|
|
6385
|
+
const line = process.argv[completionIdx + 1] ?? "";
|
|
6386
|
+
const candidates = getCompletions(program, line);
|
|
6387
|
+
if (candidates.length > 0) process.stdout.write(candidates.join("\n") + "\n");
|
|
6388
|
+
process.exit(0);
|
|
6389
|
+
}
|
|
6091
6390
|
program.parse();
|
|
6092
6391
|
//# sourceMappingURL=index.js.map
|
|
6093
6392
|
//# sourceMappingURL=index.js.map
|