@dosu/cli 0.14.2 → 0.15.1
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/bin/dosu.js +1078 -8
- package/package.json +2 -1
package/bin/dosu.js
CHANGED
|
@@ -4341,7 +4341,7 @@ function getSupabaseAnonKey() {
|
|
|
4341
4341
|
function getVersionString() {
|
|
4342
4342
|
return `v${VERSION}`;
|
|
4343
4343
|
}
|
|
4344
|
-
var VERSION = "0.
|
|
4344
|
+
var VERSION = "0.15.1";
|
|
4345
4345
|
|
|
4346
4346
|
// src/debug/logger.ts
|
|
4347
4347
|
import {
|
|
@@ -6655,9 +6655,535 @@ var init_skill = __esm(() => {
|
|
|
6655
6655
|
import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
6656
6656
|
});
|
|
6657
6657
|
|
|
6658
|
+
// src/mcp/constants.ts
|
|
6659
|
+
var MCP_PROVIDER_SLUG = "dosu_mcp";
|
|
6660
|
+
|
|
6661
|
+
// node_modules/imurmurhash/imurmurhash.js
|
|
6662
|
+
var require_imurmurhash = __commonJS((exports, module) => {
|
|
6663
|
+
(function() {
|
|
6664
|
+
var cache;
|
|
6665
|
+
function MurmurHash3(key, seed) {
|
|
6666
|
+
var m2 = this instanceof MurmurHash3 ? this : cache;
|
|
6667
|
+
m2.reset(seed);
|
|
6668
|
+
if (typeof key === "string" && key.length > 0) {
|
|
6669
|
+
m2.hash(key);
|
|
6670
|
+
}
|
|
6671
|
+
if (m2 !== this) {
|
|
6672
|
+
return m2;
|
|
6673
|
+
}
|
|
6674
|
+
}
|
|
6675
|
+
MurmurHash3.prototype.hash = function(key) {
|
|
6676
|
+
var h1, k1, i, top, len;
|
|
6677
|
+
len = key.length;
|
|
6678
|
+
this.len += len;
|
|
6679
|
+
k1 = this.k1;
|
|
6680
|
+
i = 0;
|
|
6681
|
+
switch (this.rem) {
|
|
6682
|
+
case 0:
|
|
6683
|
+
k1 ^= len > i ? key.charCodeAt(i++) & 65535 : 0;
|
|
6684
|
+
case 1:
|
|
6685
|
+
k1 ^= len > i ? (key.charCodeAt(i++) & 65535) << 8 : 0;
|
|
6686
|
+
case 2:
|
|
6687
|
+
k1 ^= len > i ? (key.charCodeAt(i++) & 65535) << 16 : 0;
|
|
6688
|
+
case 3:
|
|
6689
|
+
k1 ^= len > i ? (key.charCodeAt(i) & 255) << 24 : 0;
|
|
6690
|
+
k1 ^= len > i ? (key.charCodeAt(i++) & 65280) >> 8 : 0;
|
|
6691
|
+
}
|
|
6692
|
+
this.rem = len + this.rem & 3;
|
|
6693
|
+
len -= this.rem;
|
|
6694
|
+
if (len > 0) {
|
|
6695
|
+
h1 = this.h1;
|
|
6696
|
+
while (true) {
|
|
6697
|
+
k1 = k1 * 11601 + (k1 & 65535) * 3432906752 & 4294967295;
|
|
6698
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
6699
|
+
k1 = k1 * 13715 + (k1 & 65535) * 461832192 & 4294967295;
|
|
6700
|
+
h1 ^= k1;
|
|
6701
|
+
h1 = h1 << 13 | h1 >>> 19;
|
|
6702
|
+
h1 = h1 * 5 + 3864292196 & 4294967295;
|
|
6703
|
+
if (i >= len) {
|
|
6704
|
+
break;
|
|
6705
|
+
}
|
|
6706
|
+
k1 = key.charCodeAt(i++) & 65535 ^ (key.charCodeAt(i++) & 65535) << 8 ^ (key.charCodeAt(i++) & 65535) << 16;
|
|
6707
|
+
top = key.charCodeAt(i++);
|
|
6708
|
+
k1 ^= (top & 255) << 24 ^ (top & 65280) >> 8;
|
|
6709
|
+
}
|
|
6710
|
+
k1 = 0;
|
|
6711
|
+
switch (this.rem) {
|
|
6712
|
+
case 3:
|
|
6713
|
+
k1 ^= (key.charCodeAt(i + 2) & 65535) << 16;
|
|
6714
|
+
case 2:
|
|
6715
|
+
k1 ^= (key.charCodeAt(i + 1) & 65535) << 8;
|
|
6716
|
+
case 1:
|
|
6717
|
+
k1 ^= key.charCodeAt(i) & 65535;
|
|
6718
|
+
}
|
|
6719
|
+
this.h1 = h1;
|
|
6720
|
+
}
|
|
6721
|
+
this.k1 = k1;
|
|
6722
|
+
return this;
|
|
6723
|
+
};
|
|
6724
|
+
MurmurHash3.prototype.result = function() {
|
|
6725
|
+
var k1, h1;
|
|
6726
|
+
k1 = this.k1;
|
|
6727
|
+
h1 = this.h1;
|
|
6728
|
+
if (k1 > 0) {
|
|
6729
|
+
k1 = k1 * 11601 + (k1 & 65535) * 3432906752 & 4294967295;
|
|
6730
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
6731
|
+
k1 = k1 * 13715 + (k1 & 65535) * 461832192 & 4294967295;
|
|
6732
|
+
h1 ^= k1;
|
|
6733
|
+
}
|
|
6734
|
+
h1 ^= this.len;
|
|
6735
|
+
h1 ^= h1 >>> 16;
|
|
6736
|
+
h1 = h1 * 51819 + (h1 & 65535) * 2246770688 & 4294967295;
|
|
6737
|
+
h1 ^= h1 >>> 13;
|
|
6738
|
+
h1 = h1 * 44597 + (h1 & 65535) * 3266445312 & 4294967295;
|
|
6739
|
+
h1 ^= h1 >>> 16;
|
|
6740
|
+
return h1 >>> 0;
|
|
6741
|
+
};
|
|
6742
|
+
MurmurHash3.prototype.reset = function(seed) {
|
|
6743
|
+
this.h1 = typeof seed === "number" ? seed : 0;
|
|
6744
|
+
this.rem = this.k1 = this.len = 0;
|
|
6745
|
+
return this;
|
|
6746
|
+
};
|
|
6747
|
+
cache = new MurmurHash3;
|
|
6748
|
+
if (typeof module != "undefined") {
|
|
6749
|
+
module.exports = MurmurHash3;
|
|
6750
|
+
} else {
|
|
6751
|
+
this.MurmurHash3 = MurmurHash3;
|
|
6752
|
+
}
|
|
6753
|
+
})();
|
|
6754
|
+
});
|
|
6755
|
+
|
|
6756
|
+
// node_modules/signal-exit/dist/cjs/signals.js
|
|
6757
|
+
var require_signals = __commonJS((exports) => {
|
|
6758
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6759
|
+
exports.signals = undefined;
|
|
6760
|
+
exports.signals = [];
|
|
6761
|
+
exports.signals.push("SIGHUP", "SIGINT", "SIGTERM");
|
|
6762
|
+
if (process.platform !== "win32") {
|
|
6763
|
+
exports.signals.push("SIGALRM", "SIGABRT", "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "SIGUSR2", "SIGTRAP", "SIGSYS", "SIGQUIT", "SIGIOT");
|
|
6764
|
+
}
|
|
6765
|
+
if (process.platform === "linux") {
|
|
6766
|
+
exports.signals.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT");
|
|
6767
|
+
}
|
|
6768
|
+
});
|
|
6769
|
+
|
|
6770
|
+
// node_modules/signal-exit/dist/cjs/index.js
|
|
6771
|
+
var require_cjs = __commonJS((exports) => {
|
|
6772
|
+
var _a;
|
|
6773
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6774
|
+
exports.unload = exports.load = exports.onExit = exports.signals = undefined;
|
|
6775
|
+
var signals_js_1 = require_signals();
|
|
6776
|
+
Object.defineProperty(exports, "signals", { enumerable: true, get: function() {
|
|
6777
|
+
return signals_js_1.signals;
|
|
6778
|
+
} });
|
|
6779
|
+
var processOk = (process3) => !!process3 && typeof process3 === "object" && typeof process3.removeListener === "function" && typeof process3.emit === "function" && typeof process3.reallyExit === "function" && typeof process3.listeners === "function" && typeof process3.kill === "function" && typeof process3.pid === "number" && typeof process3.on === "function";
|
|
6780
|
+
var kExitEmitter = Symbol.for("signal-exit emitter");
|
|
6781
|
+
var global = globalThis;
|
|
6782
|
+
var ObjectDefineProperty = Object.defineProperty.bind(Object);
|
|
6783
|
+
|
|
6784
|
+
class Emitter {
|
|
6785
|
+
emitted = {
|
|
6786
|
+
afterExit: false,
|
|
6787
|
+
exit: false
|
|
6788
|
+
};
|
|
6789
|
+
listeners = {
|
|
6790
|
+
afterExit: [],
|
|
6791
|
+
exit: []
|
|
6792
|
+
};
|
|
6793
|
+
count = 0;
|
|
6794
|
+
id = Math.random();
|
|
6795
|
+
constructor() {
|
|
6796
|
+
if (global[kExitEmitter]) {
|
|
6797
|
+
return global[kExitEmitter];
|
|
6798
|
+
}
|
|
6799
|
+
ObjectDefineProperty(global, kExitEmitter, {
|
|
6800
|
+
value: this,
|
|
6801
|
+
writable: false,
|
|
6802
|
+
enumerable: false,
|
|
6803
|
+
configurable: false
|
|
6804
|
+
});
|
|
6805
|
+
}
|
|
6806
|
+
on(ev, fn) {
|
|
6807
|
+
this.listeners[ev].push(fn);
|
|
6808
|
+
}
|
|
6809
|
+
removeListener(ev, fn) {
|
|
6810
|
+
const list = this.listeners[ev];
|
|
6811
|
+
const i = list.indexOf(fn);
|
|
6812
|
+
if (i === -1) {
|
|
6813
|
+
return;
|
|
6814
|
+
}
|
|
6815
|
+
if (i === 0 && list.length === 1) {
|
|
6816
|
+
list.length = 0;
|
|
6817
|
+
} else {
|
|
6818
|
+
list.splice(i, 1);
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
emit(ev, code, signal) {
|
|
6822
|
+
if (this.emitted[ev]) {
|
|
6823
|
+
return false;
|
|
6824
|
+
}
|
|
6825
|
+
this.emitted[ev] = true;
|
|
6826
|
+
let ret = false;
|
|
6827
|
+
for (const fn of this.listeners[ev]) {
|
|
6828
|
+
ret = fn(code, signal) === true || ret;
|
|
6829
|
+
}
|
|
6830
|
+
if (ev === "exit") {
|
|
6831
|
+
ret = this.emit("afterExit", code, signal) || ret;
|
|
6832
|
+
}
|
|
6833
|
+
return ret;
|
|
6834
|
+
}
|
|
6835
|
+
}
|
|
6836
|
+
|
|
6837
|
+
class SignalExitBase {
|
|
6838
|
+
}
|
|
6839
|
+
var signalExitWrap = (handler) => {
|
|
6840
|
+
return {
|
|
6841
|
+
onExit(cb, opts) {
|
|
6842
|
+
return handler.onExit(cb, opts);
|
|
6843
|
+
},
|
|
6844
|
+
load() {
|
|
6845
|
+
return handler.load();
|
|
6846
|
+
},
|
|
6847
|
+
unload() {
|
|
6848
|
+
return handler.unload();
|
|
6849
|
+
}
|
|
6850
|
+
};
|
|
6851
|
+
};
|
|
6852
|
+
|
|
6853
|
+
class SignalExitFallback extends SignalExitBase {
|
|
6854
|
+
onExit() {
|
|
6855
|
+
return () => {};
|
|
6856
|
+
}
|
|
6857
|
+
load() {}
|
|
6858
|
+
unload() {}
|
|
6859
|
+
}
|
|
6860
|
+
|
|
6861
|
+
class SignalExit extends SignalExitBase {
|
|
6862
|
+
#hupSig = process2.platform === "win32" ? "SIGINT" : "SIGHUP";
|
|
6863
|
+
#emitter = new Emitter;
|
|
6864
|
+
#process;
|
|
6865
|
+
#originalProcessEmit;
|
|
6866
|
+
#originalProcessReallyExit;
|
|
6867
|
+
#sigListeners = {};
|
|
6868
|
+
#loaded = false;
|
|
6869
|
+
constructor(process3) {
|
|
6870
|
+
super();
|
|
6871
|
+
this.#process = process3;
|
|
6872
|
+
this.#sigListeners = {};
|
|
6873
|
+
for (const sig of signals_js_1.signals) {
|
|
6874
|
+
this.#sigListeners[sig] = () => {
|
|
6875
|
+
const listeners = this.#process.listeners(sig);
|
|
6876
|
+
let { count } = this.#emitter;
|
|
6877
|
+
const p2 = process3;
|
|
6878
|
+
if (typeof p2.__signal_exit_emitter__ === "object" && typeof p2.__signal_exit_emitter__.count === "number") {
|
|
6879
|
+
count += p2.__signal_exit_emitter__.count;
|
|
6880
|
+
}
|
|
6881
|
+
if (listeners.length === count) {
|
|
6882
|
+
this.unload();
|
|
6883
|
+
const ret = this.#emitter.emit("exit", null, sig);
|
|
6884
|
+
const s = sig === "SIGHUP" ? this.#hupSig : sig;
|
|
6885
|
+
if (!ret)
|
|
6886
|
+
process3.kill(process3.pid, s);
|
|
6887
|
+
}
|
|
6888
|
+
};
|
|
6889
|
+
}
|
|
6890
|
+
this.#originalProcessReallyExit = process3.reallyExit;
|
|
6891
|
+
this.#originalProcessEmit = process3.emit;
|
|
6892
|
+
}
|
|
6893
|
+
onExit(cb, opts) {
|
|
6894
|
+
if (!processOk(this.#process)) {
|
|
6895
|
+
return () => {};
|
|
6896
|
+
}
|
|
6897
|
+
if (this.#loaded === false) {
|
|
6898
|
+
this.load();
|
|
6899
|
+
}
|
|
6900
|
+
const ev = opts?.alwaysLast ? "afterExit" : "exit";
|
|
6901
|
+
this.#emitter.on(ev, cb);
|
|
6902
|
+
return () => {
|
|
6903
|
+
this.#emitter.removeListener(ev, cb);
|
|
6904
|
+
if (this.#emitter.listeners["exit"].length === 0 && this.#emitter.listeners["afterExit"].length === 0) {
|
|
6905
|
+
this.unload();
|
|
6906
|
+
}
|
|
6907
|
+
};
|
|
6908
|
+
}
|
|
6909
|
+
load() {
|
|
6910
|
+
if (this.#loaded) {
|
|
6911
|
+
return;
|
|
6912
|
+
}
|
|
6913
|
+
this.#loaded = true;
|
|
6914
|
+
this.#emitter.count += 1;
|
|
6915
|
+
for (const sig of signals_js_1.signals) {
|
|
6916
|
+
try {
|
|
6917
|
+
const fn = this.#sigListeners[sig];
|
|
6918
|
+
if (fn)
|
|
6919
|
+
this.#process.on(sig, fn);
|
|
6920
|
+
} catch (_3) {}
|
|
6921
|
+
}
|
|
6922
|
+
this.#process.emit = (ev, ...a) => {
|
|
6923
|
+
return this.#processEmit(ev, ...a);
|
|
6924
|
+
};
|
|
6925
|
+
this.#process.reallyExit = (code) => {
|
|
6926
|
+
return this.#processReallyExit(code);
|
|
6927
|
+
};
|
|
6928
|
+
}
|
|
6929
|
+
unload() {
|
|
6930
|
+
if (!this.#loaded) {
|
|
6931
|
+
return;
|
|
6932
|
+
}
|
|
6933
|
+
this.#loaded = false;
|
|
6934
|
+
signals_js_1.signals.forEach((sig) => {
|
|
6935
|
+
const listener = this.#sigListeners[sig];
|
|
6936
|
+
if (!listener) {
|
|
6937
|
+
throw new Error("Listener not defined for signal: " + sig);
|
|
6938
|
+
}
|
|
6939
|
+
try {
|
|
6940
|
+
this.#process.removeListener(sig, listener);
|
|
6941
|
+
} catch (_3) {}
|
|
6942
|
+
});
|
|
6943
|
+
this.#process.emit = this.#originalProcessEmit;
|
|
6944
|
+
this.#process.reallyExit = this.#originalProcessReallyExit;
|
|
6945
|
+
this.#emitter.count -= 1;
|
|
6946
|
+
}
|
|
6947
|
+
#processReallyExit(code) {
|
|
6948
|
+
if (!processOk(this.#process)) {
|
|
6949
|
+
return 0;
|
|
6950
|
+
}
|
|
6951
|
+
this.#process.exitCode = code || 0;
|
|
6952
|
+
this.#emitter.emit("exit", this.#process.exitCode, null);
|
|
6953
|
+
return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
|
|
6954
|
+
}
|
|
6955
|
+
#processEmit(ev, ...args) {
|
|
6956
|
+
const og = this.#originalProcessEmit;
|
|
6957
|
+
if (ev === "exit" && processOk(this.#process)) {
|
|
6958
|
+
if (typeof args[0] === "number") {
|
|
6959
|
+
this.#process.exitCode = args[0];
|
|
6960
|
+
}
|
|
6961
|
+
const ret = og.call(this.#process, ev, ...args);
|
|
6962
|
+
this.#emitter.emit("exit", this.#process.exitCode, null);
|
|
6963
|
+
return ret;
|
|
6964
|
+
} else {
|
|
6965
|
+
return og.call(this.#process, ev, ...args);
|
|
6966
|
+
}
|
|
6967
|
+
}
|
|
6968
|
+
}
|
|
6969
|
+
var process2 = globalThis.process;
|
|
6970
|
+
_a = signalExitWrap(processOk(process2) ? new SignalExit(process2) : new SignalExitFallback), exports.onExit = _a.onExit, exports.load = _a.load, exports.unload = _a.unload;
|
|
6971
|
+
});
|
|
6972
|
+
|
|
6973
|
+
// node_modules/write-file-atomic/lib/index.js
|
|
6974
|
+
var require_lib = __commonJS((exports, module) => {
|
|
6975
|
+
var __filename = "/home/runner/work/dosu-cli/dosu-cli/node_modules/write-file-atomic/lib/index.js";
|
|
6976
|
+
module.exports = writeFile;
|
|
6977
|
+
module.exports.sync = writeFileSync4;
|
|
6978
|
+
module.exports._getTmpname = getTmpname;
|
|
6979
|
+
module.exports._cleanupOnExit = cleanupOnExit;
|
|
6980
|
+
var fs = __require("fs");
|
|
6981
|
+
var MurmurHash3 = require_imurmurhash();
|
|
6982
|
+
var { onExit } = require_cjs();
|
|
6983
|
+
var path = __require("path");
|
|
6984
|
+
var { promisify } = __require("util");
|
|
6985
|
+
var activeFiles = {};
|
|
6986
|
+
var threadId = function getId() {
|
|
6987
|
+
try {
|
|
6988
|
+
const workerThreads = __require("worker_threads");
|
|
6989
|
+
return workerThreads.threadId;
|
|
6990
|
+
} catch (e2) {
|
|
6991
|
+
return 0;
|
|
6992
|
+
}
|
|
6993
|
+
}();
|
|
6994
|
+
var invocations = 0;
|
|
6995
|
+
function getTmpname(filename) {
|
|
6996
|
+
return filename + "." + MurmurHash3(__filename).hash(String(process.pid)).hash(String(threadId)).hash(String(++invocations)).result();
|
|
6997
|
+
}
|
|
6998
|
+
function cleanupOnExit(tmpfile) {
|
|
6999
|
+
return () => {
|
|
7000
|
+
try {
|
|
7001
|
+
fs.unlinkSync(typeof tmpfile === "function" ? tmpfile() : tmpfile);
|
|
7002
|
+
} catch {}
|
|
7003
|
+
};
|
|
7004
|
+
}
|
|
7005
|
+
function serializeActiveFile(absoluteName) {
|
|
7006
|
+
return new Promise((resolve) => {
|
|
7007
|
+
if (!activeFiles[absoluteName]) {
|
|
7008
|
+
activeFiles[absoluteName] = [];
|
|
7009
|
+
}
|
|
7010
|
+
activeFiles[absoluteName].push(resolve);
|
|
7011
|
+
if (activeFiles[absoluteName].length === 1) {
|
|
7012
|
+
resolve();
|
|
7013
|
+
}
|
|
7014
|
+
});
|
|
7015
|
+
}
|
|
7016
|
+
function isChownErrOk(err) {
|
|
7017
|
+
if (err.code === "ENOSYS") {
|
|
7018
|
+
return true;
|
|
7019
|
+
}
|
|
7020
|
+
const nonroot = !process.getuid || process.getuid() !== 0;
|
|
7021
|
+
if (nonroot) {
|
|
7022
|
+
if (err.code === "EINVAL" || err.code === "EPERM") {
|
|
7023
|
+
return true;
|
|
7024
|
+
}
|
|
7025
|
+
}
|
|
7026
|
+
return false;
|
|
7027
|
+
}
|
|
7028
|
+
async function writeFileAsync(filename, data, options = {}) {
|
|
7029
|
+
if (typeof options === "string") {
|
|
7030
|
+
options = { encoding: options };
|
|
7031
|
+
}
|
|
7032
|
+
let fd;
|
|
7033
|
+
let tmpfile;
|
|
7034
|
+
const removeOnExitHandler = onExit(cleanupOnExit(() => tmpfile));
|
|
7035
|
+
const absoluteName = path.resolve(filename);
|
|
7036
|
+
try {
|
|
7037
|
+
await serializeActiveFile(absoluteName);
|
|
7038
|
+
const truename = await promisify(fs.realpath)(filename).catch(() => filename);
|
|
7039
|
+
tmpfile = getTmpname(truename);
|
|
7040
|
+
if (!options.mode || !options.chown) {
|
|
7041
|
+
const stats = await promisify(fs.stat)(truename).catch(() => {});
|
|
7042
|
+
if (stats) {
|
|
7043
|
+
if (options.mode == null) {
|
|
7044
|
+
options.mode = stats.mode;
|
|
7045
|
+
}
|
|
7046
|
+
if (options.chown == null && process.getuid) {
|
|
7047
|
+
options.chown = { uid: stats.uid, gid: stats.gid };
|
|
7048
|
+
}
|
|
7049
|
+
}
|
|
7050
|
+
}
|
|
7051
|
+
fd = await promisify(fs.open)(tmpfile, "w", options.mode);
|
|
7052
|
+
if (options.tmpfileCreated) {
|
|
7053
|
+
await options.tmpfileCreated(tmpfile);
|
|
7054
|
+
}
|
|
7055
|
+
if (ArrayBuffer.isView(data)) {
|
|
7056
|
+
await promisify(fs.write)(fd, data, 0, data.length, 0);
|
|
7057
|
+
} else if (data != null) {
|
|
7058
|
+
await promisify(fs.write)(fd, String(data), 0, String(options.encoding || "utf8"));
|
|
7059
|
+
}
|
|
7060
|
+
if (options.fsync !== false) {
|
|
7061
|
+
await promisify(fs.fsync)(fd);
|
|
7062
|
+
}
|
|
7063
|
+
await promisify(fs.close)(fd);
|
|
7064
|
+
fd = null;
|
|
7065
|
+
if (options.chown) {
|
|
7066
|
+
await promisify(fs.chown)(tmpfile, options.chown.uid, options.chown.gid).catch((err) => {
|
|
7067
|
+
if (!isChownErrOk(err)) {
|
|
7068
|
+
throw err;
|
|
7069
|
+
}
|
|
7070
|
+
});
|
|
7071
|
+
}
|
|
7072
|
+
if (options.mode) {
|
|
7073
|
+
await promisify(fs.chmod)(tmpfile, options.mode).catch((err) => {
|
|
7074
|
+
if (!isChownErrOk(err)) {
|
|
7075
|
+
throw err;
|
|
7076
|
+
}
|
|
7077
|
+
});
|
|
7078
|
+
}
|
|
7079
|
+
await promisify(fs.rename)(tmpfile, truename);
|
|
7080
|
+
} finally {
|
|
7081
|
+
if (fd) {
|
|
7082
|
+
await promisify(fs.close)(fd).catch(() => {});
|
|
7083
|
+
}
|
|
7084
|
+
removeOnExitHandler();
|
|
7085
|
+
await promisify(fs.unlink)(tmpfile).catch(() => {});
|
|
7086
|
+
activeFiles[absoluteName].shift();
|
|
7087
|
+
if (activeFiles[absoluteName].length > 0) {
|
|
7088
|
+
activeFiles[absoluteName][0]();
|
|
7089
|
+
} else {
|
|
7090
|
+
delete activeFiles[absoluteName];
|
|
7091
|
+
}
|
|
7092
|
+
}
|
|
7093
|
+
}
|
|
7094
|
+
async function writeFile(filename, data, options, callback) {
|
|
7095
|
+
if (options instanceof Function) {
|
|
7096
|
+
callback = options;
|
|
7097
|
+
options = {};
|
|
7098
|
+
}
|
|
7099
|
+
const promise = writeFileAsync(filename, data, options);
|
|
7100
|
+
if (callback) {
|
|
7101
|
+
try {
|
|
7102
|
+
const result = await promise;
|
|
7103
|
+
return callback(result);
|
|
7104
|
+
} catch (err) {
|
|
7105
|
+
return callback(err);
|
|
7106
|
+
}
|
|
7107
|
+
}
|
|
7108
|
+
return promise;
|
|
7109
|
+
}
|
|
7110
|
+
function writeFileSync4(filename, data, options) {
|
|
7111
|
+
if (typeof options === "string") {
|
|
7112
|
+
options = { encoding: options };
|
|
7113
|
+
} else if (!options) {
|
|
7114
|
+
options = {};
|
|
7115
|
+
}
|
|
7116
|
+
try {
|
|
7117
|
+
filename = fs.realpathSync(filename);
|
|
7118
|
+
} catch (ex) {}
|
|
7119
|
+
const tmpfile = getTmpname(filename);
|
|
7120
|
+
if (!options.mode || !options.chown) {
|
|
7121
|
+
try {
|
|
7122
|
+
const stats = fs.statSync(filename);
|
|
7123
|
+
options = Object.assign({}, options);
|
|
7124
|
+
if (!options.mode) {
|
|
7125
|
+
options.mode = stats.mode;
|
|
7126
|
+
}
|
|
7127
|
+
if (!options.chown && process.getuid) {
|
|
7128
|
+
options.chown = { uid: stats.uid, gid: stats.gid };
|
|
7129
|
+
}
|
|
7130
|
+
} catch (ex) {}
|
|
7131
|
+
}
|
|
7132
|
+
let fd;
|
|
7133
|
+
const cleanup = cleanupOnExit(tmpfile);
|
|
7134
|
+
const removeOnExitHandler = onExit(cleanup);
|
|
7135
|
+
let threw = true;
|
|
7136
|
+
try {
|
|
7137
|
+
fd = fs.openSync(tmpfile, "w", options.mode || 438);
|
|
7138
|
+
if (options.tmpfileCreated) {
|
|
7139
|
+
options.tmpfileCreated(tmpfile);
|
|
7140
|
+
}
|
|
7141
|
+
if (ArrayBuffer.isView(data)) {
|
|
7142
|
+
fs.writeSync(fd, data, 0, data.length, 0);
|
|
7143
|
+
} else if (data != null) {
|
|
7144
|
+
fs.writeSync(fd, String(data), 0, String(options.encoding || "utf8"));
|
|
7145
|
+
}
|
|
7146
|
+
if (options.fsync !== false) {
|
|
7147
|
+
fs.fsyncSync(fd);
|
|
7148
|
+
}
|
|
7149
|
+
fs.closeSync(fd);
|
|
7150
|
+
fd = null;
|
|
7151
|
+
if (options.chown) {
|
|
7152
|
+
try {
|
|
7153
|
+
fs.chownSync(tmpfile, options.chown.uid, options.chown.gid);
|
|
7154
|
+
} catch (err) {
|
|
7155
|
+
if (!isChownErrOk(err)) {
|
|
7156
|
+
throw err;
|
|
7157
|
+
}
|
|
7158
|
+
}
|
|
7159
|
+
}
|
|
7160
|
+
if (options.mode) {
|
|
7161
|
+
try {
|
|
7162
|
+
fs.chmodSync(tmpfile, options.mode);
|
|
7163
|
+
} catch (err) {
|
|
7164
|
+
if (!isChownErrOk(err)) {
|
|
7165
|
+
throw err;
|
|
7166
|
+
}
|
|
7167
|
+
}
|
|
7168
|
+
}
|
|
7169
|
+
fs.renameSync(tmpfile, filename);
|
|
7170
|
+
threw = false;
|
|
7171
|
+
} finally {
|
|
7172
|
+
if (fd) {
|
|
7173
|
+
try {
|
|
7174
|
+
fs.closeSync(fd);
|
|
7175
|
+
} catch (ex) {}
|
|
7176
|
+
}
|
|
7177
|
+
removeOnExitHandler();
|
|
7178
|
+
if (threw) {
|
|
7179
|
+
cleanup();
|
|
7180
|
+
}
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
});
|
|
7184
|
+
|
|
6658
7185
|
// src/mcp/config-helpers.ts
|
|
6659
7186
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
6660
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
6661
7187
|
import { dirname } from "node:path";
|
|
6662
7188
|
function mcpURL(deploymentID) {
|
|
6663
7189
|
return `${getBackendURL()}/v1/mcp/deployments/${deploymentID}`;
|
|
@@ -6772,10 +7298,10 @@ function removeJSONServer(configPath, topKey) {
|
|
|
6772
7298
|
}
|
|
6773
7299
|
saveJSONConfig(configPath, jsonCfg);
|
|
6774
7300
|
}
|
|
6775
|
-
var
|
|
7301
|
+
var import_write_file_atomic, writeFileAtomic;
|
|
6776
7302
|
var init_config_helpers = __esm(() => {
|
|
6777
|
-
|
|
6778
|
-
writeFileAtomic =
|
|
7303
|
+
import_write_file_atomic = __toESM(require_lib(), 1);
|
|
7304
|
+
writeFileAtomic = import_write_file_atomic.default;
|
|
6779
7305
|
});
|
|
6780
7306
|
|
|
6781
7307
|
// src/mcp/detect.ts
|
|
@@ -10325,7 +10851,7 @@ ${dim(targetOrg.name)}`);
|
|
|
10325
10851
|
async function resolveOnboardingDeployment(apiClient, targetOrg) {
|
|
10326
10852
|
const deployments = await fetchDeployments(apiClient);
|
|
10327
10853
|
const orgDeployments = deployments.filter((deployment) => deployment.org_id === targetOrg.org_id);
|
|
10328
|
-
return orgDeployments.find((deployment) => deployment.provider_slug ===
|
|
10854
|
+
return orgDeployments.find((deployment) => deployment.provider_slug === MCP_PROVIDER_SLUG) ?? orgDeployments[0] ?? null;
|
|
10329
10855
|
}
|
|
10330
10856
|
async function resolveDeployment(apiClient, cfg, opts) {
|
|
10331
10857
|
if (opts.deploymentID) {
|
|
@@ -10982,6 +11508,509 @@ var init_tui = __esm(() => {
|
|
|
10982
11508
|
import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
10983
11509
|
});
|
|
10984
11510
|
|
|
11511
|
+
// src/auth/ticket.ts
|
|
11512
|
+
function buildTicketAuthURL(ticket) {
|
|
11513
|
+
const base = getWebAppURL();
|
|
11514
|
+
const params = new URLSearchParams({ ticket });
|
|
11515
|
+
return `${base}/cli/auth?${params}`;
|
|
11516
|
+
}
|
|
11517
|
+
async function mintTicket() {
|
|
11518
|
+
const url = `${getBackendURL()}/v1/cli/auth/tickets`;
|
|
11519
|
+
logger.debug("auth.ticket", `Minting ticket via ${url}`);
|
|
11520
|
+
const resp = await fetchWithTimeout(url, { method: "POST" });
|
|
11521
|
+
if (resp.status !== 200 && resp.status !== 201) {
|
|
11522
|
+
const detail = await readErrorBody2(resp);
|
|
11523
|
+
throw new Error(`failed to mint ticket (status ${resp.status}): ${detail}`);
|
|
11524
|
+
}
|
|
11525
|
+
const data = await resp.json();
|
|
11526
|
+
logger.info("auth.ticket", `Minted ticket (ttl=${data.expires_in}s)`);
|
|
11527
|
+
return {
|
|
11528
|
+
ticket: data.ticket,
|
|
11529
|
+
expires_in: data.expires_in,
|
|
11530
|
+
url: buildTicketAuthURL(data.ticket)
|
|
11531
|
+
};
|
|
11532
|
+
}
|
|
11533
|
+
async function exchangeTicket(ticket) {
|
|
11534
|
+
const path2 = `/v1/cli/auth/tickets/${encodeURIComponent(ticket)}/exchange`;
|
|
11535
|
+
const url = `${getBackendURL()}${path2}`;
|
|
11536
|
+
logger.debug("auth.ticket", `Exchanging ticket via ${url}`);
|
|
11537
|
+
const resp = await fetchWithTimeout(url, { method: "POST" });
|
|
11538
|
+
if (resp.status !== 200) {
|
|
11539
|
+
const detail = await readErrorBody2(resp);
|
|
11540
|
+
throw new Error(`ticket exchange failed (status ${resp.status}): ${detail}`);
|
|
11541
|
+
}
|
|
11542
|
+
const data = await resp.json();
|
|
11543
|
+
return {
|
|
11544
|
+
status: data.status,
|
|
11545
|
+
access_token: data.access_token ?? undefined,
|
|
11546
|
+
refresh_token: data.refresh_token ?? undefined,
|
|
11547
|
+
expires_in: data.expires_in ?? undefined,
|
|
11548
|
+
email: data.email ?? undefined
|
|
11549
|
+
};
|
|
11550
|
+
}
|
|
11551
|
+
async function fetchWithTimeout(url, init) {
|
|
11552
|
+
const controller = new AbortController;
|
|
11553
|
+
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
11554
|
+
try {
|
|
11555
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
11556
|
+
} finally {
|
|
11557
|
+
clearTimeout(timeout);
|
|
11558
|
+
}
|
|
11559
|
+
}
|
|
11560
|
+
async function readErrorBody2(resp) {
|
|
11561
|
+
try {
|
|
11562
|
+
return (await resp.text()).slice(0, 512);
|
|
11563
|
+
} catch {
|
|
11564
|
+
return "";
|
|
11565
|
+
}
|
|
11566
|
+
}
|
|
11567
|
+
var init_ticket = __esm(() => {
|
|
11568
|
+
init_logger();
|
|
11569
|
+
});
|
|
11570
|
+
|
|
11571
|
+
// src/agent/output.ts
|
|
11572
|
+
var exports_output = {};
|
|
11573
|
+
__export(exports_output, {
|
|
11574
|
+
emitStep: () => emitStep,
|
|
11575
|
+
emitNeedUserAction: () => emitNeedUserAction,
|
|
11576
|
+
emitJSONLine: () => emitJSONLine,
|
|
11577
|
+
emitError: () => emitError
|
|
11578
|
+
});
|
|
11579
|
+
function emitJSONLine(value) {
|
|
11580
|
+
console.log(JSON.stringify(value));
|
|
11581
|
+
}
|
|
11582
|
+
function emitNeedUserAction(opts) {
|
|
11583
|
+
emitJSONLine({
|
|
11584
|
+
step: opts.step,
|
|
11585
|
+
status: "need_user_action",
|
|
11586
|
+
url: opts.url,
|
|
11587
|
+
ticket: opts.ticket,
|
|
11588
|
+
resume_command: opts.resume_command,
|
|
11589
|
+
expires_in: opts.expires_in,
|
|
11590
|
+
agent_next_steps: opts.agent_next_steps
|
|
11591
|
+
});
|
|
11592
|
+
}
|
|
11593
|
+
function emitError(opts) {
|
|
11594
|
+
const { step, reason, agent_next_steps, ...rest } = opts;
|
|
11595
|
+
emitJSONLine({
|
|
11596
|
+
step,
|
|
11597
|
+
status: "error",
|
|
11598
|
+
reason,
|
|
11599
|
+
...rest,
|
|
11600
|
+
agent_next_steps
|
|
11601
|
+
});
|
|
11602
|
+
}
|
|
11603
|
+
function emitStep(opts) {
|
|
11604
|
+
const { step, status = "ok", ...rest } = opts;
|
|
11605
|
+
emitJSONLine({ step, status, ...rest });
|
|
11606
|
+
}
|
|
11607
|
+
|
|
11608
|
+
// src/agent/login-commands.ts
|
|
11609
|
+
var exports_login_commands = {};
|
|
11610
|
+
__export(exports_login_commands, {
|
|
11611
|
+
runLoginRequest: () => runLoginRequest,
|
|
11612
|
+
runLoginCheck: () => runLoginCheck
|
|
11613
|
+
});
|
|
11614
|
+
async function runLoginRequest(opts) {
|
|
11615
|
+
try {
|
|
11616
|
+
const minted = await mintTicket();
|
|
11617
|
+
const checkCommand = `${CHECK_COMMAND_PREFIX} ${minted.ticket}${opts.json ? " --json" : ""}`;
|
|
11618
|
+
if (opts.json) {
|
|
11619
|
+
emitJSONLine({
|
|
11620
|
+
ticket: minted.ticket,
|
|
11621
|
+
url: minted.url,
|
|
11622
|
+
check_command: checkCommand,
|
|
11623
|
+
expires_in: minted.expires_in,
|
|
11624
|
+
agent_next_steps: "Give the URL to the user so they can authorize. Once the user confirms they've signed in, run check_command to retrieve the token."
|
|
11625
|
+
});
|
|
11626
|
+
} else {
|
|
11627
|
+
console.log("Open this URL in your browser to authorize:");
|
|
11628
|
+
console.log(` ${minted.url}`);
|
|
11629
|
+
console.log("");
|
|
11630
|
+
console.log("After signing in, complete the login with:");
|
|
11631
|
+
console.log(` ${checkCommand}`);
|
|
11632
|
+
console.log("");
|
|
11633
|
+
console.log(`Ticket expires in ${Math.floor(minted.expires_in / 60)} minutes.`);
|
|
11634
|
+
}
|
|
11635
|
+
return 0;
|
|
11636
|
+
} catch (err) {
|
|
11637
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11638
|
+
if (opts.json) {
|
|
11639
|
+
emitError({
|
|
11640
|
+
step: "request",
|
|
11641
|
+
reason: "mint_failed",
|
|
11642
|
+
agent_next_steps: `Could not mint a login ticket: ${msg}. Check connectivity and retry.`
|
|
11643
|
+
});
|
|
11644
|
+
} else {
|
|
11645
|
+
console.error(`Failed to mint login ticket: ${msg}`);
|
|
11646
|
+
}
|
|
11647
|
+
return 1;
|
|
11648
|
+
}
|
|
11649
|
+
}
|
|
11650
|
+
async function runLoginCheck(opts) {
|
|
11651
|
+
let result;
|
|
11652
|
+
try {
|
|
11653
|
+
result = await exchangeTicket(opts.ticket);
|
|
11654
|
+
} catch (err) {
|
|
11655
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11656
|
+
if (opts.json) {
|
|
11657
|
+
emitError({
|
|
11658
|
+
step: "check",
|
|
11659
|
+
reason: "exchange_failed",
|
|
11660
|
+
agent_next_steps: `Ticket exchange failed: ${msg}. Retry, or run 'dosu login --request' to get a fresh ticket.`
|
|
11661
|
+
});
|
|
11662
|
+
} else {
|
|
11663
|
+
console.error(`Ticket exchange failed: ${msg}`);
|
|
11664
|
+
}
|
|
11665
|
+
return 1;
|
|
11666
|
+
}
|
|
11667
|
+
if (result.status === "authenticated") {
|
|
11668
|
+
const cfg = loadConfig();
|
|
11669
|
+
cfg.access_token = result.access_token ?? "";
|
|
11670
|
+
cfg.refresh_token = result.refresh_token ?? "";
|
|
11671
|
+
cfg.expires_at = Math.floor(Date.now() / 1000) + (result.expires_in ?? 3600);
|
|
11672
|
+
saveConfig(cfg);
|
|
11673
|
+
logger.info("auth.ticket", "Ticket redeemed via dosu login --check");
|
|
11674
|
+
if (opts.json) {
|
|
11675
|
+
emitStep({
|
|
11676
|
+
step: "check",
|
|
11677
|
+
status: "authenticated",
|
|
11678
|
+
email: result.email,
|
|
11679
|
+
agent_next_steps: "Authentication complete. You can now run authenticated commands like 'dosu setup' or 'dosu status'."
|
|
11680
|
+
});
|
|
11681
|
+
} else {
|
|
11682
|
+
console.log("Successfully authenticated!");
|
|
11683
|
+
if (result.email)
|
|
11684
|
+
console.log(`Signed in as ${result.email}`);
|
|
11685
|
+
}
|
|
11686
|
+
return 0;
|
|
11687
|
+
}
|
|
11688
|
+
if (result.status === "pending") {
|
|
11689
|
+
if (opts.json) {
|
|
11690
|
+
emitStep({
|
|
11691
|
+
step: "check",
|
|
11692
|
+
status: "pending",
|
|
11693
|
+
ticket: opts.ticket,
|
|
11694
|
+
agent_next_steps: "User hasn't completed sign-in yet. Ask the user to confirm they've signed in via the URL from --request, then run this check command again."
|
|
11695
|
+
});
|
|
11696
|
+
} else {
|
|
11697
|
+
console.log("Still waiting for the user to complete sign-in. Run --check again later.");
|
|
11698
|
+
}
|
|
11699
|
+
return 0;
|
|
11700
|
+
}
|
|
11701
|
+
if (opts.json) {
|
|
11702
|
+
emitStep({
|
|
11703
|
+
step: "check",
|
|
11704
|
+
status: "expired",
|
|
11705
|
+
agent_next_steps: "Ticket has expired or was already redeemed. Run 'dosu login --request --json' to get a fresh ticket."
|
|
11706
|
+
});
|
|
11707
|
+
} else {
|
|
11708
|
+
console.error("Ticket has expired or was already used. Run 'dosu login --request' for a new one.");
|
|
11709
|
+
}
|
|
11710
|
+
return 1;
|
|
11711
|
+
}
|
|
11712
|
+
var CHECK_COMMAND_PREFIX = "dosu login --check";
|
|
11713
|
+
var init_login_commands = __esm(() => {
|
|
11714
|
+
init_ticket();
|
|
11715
|
+
init_config();
|
|
11716
|
+
init_logger();
|
|
11717
|
+
});
|
|
11718
|
+
|
|
11719
|
+
// src/agent/flow.ts
|
|
11720
|
+
var exports_flow3 = {};
|
|
11721
|
+
__export(exports_flow3, {
|
|
11722
|
+
runAgentSetup: () => runAgentSetup,
|
|
11723
|
+
listAgentSupportedToolIDs: () => listAgentSupportedToolIDs,
|
|
11724
|
+
buildResumeCommand: () => buildResumeCommand
|
|
11725
|
+
});
|
|
11726
|
+
async function runAgentSetup(opts) {
|
|
11727
|
+
const provider = allSetupProviders().find((p2) => p2.id() === opts.tool.toLowerCase());
|
|
11728
|
+
if (!provider) {
|
|
11729
|
+
const available = allSetupProviders().filter((p2) => !isStdioOnly(p2)).map((p2) => p2.id()).join(", ");
|
|
11730
|
+
emitError({
|
|
11731
|
+
step: "setup",
|
|
11732
|
+
reason: "unknown_tool",
|
|
11733
|
+
agent_next_steps: `'${opts.tool}' is not a supported tool. Choose one of: ${available}. Re-run with --tool <id>.`
|
|
11734
|
+
});
|
|
11735
|
+
return 2;
|
|
11736
|
+
}
|
|
11737
|
+
if (isStdioOnly(provider)) {
|
|
11738
|
+
emitError({
|
|
11739
|
+
step: "setup",
|
|
11740
|
+
reason: "tool_unsupported_in_agent_mode",
|
|
11741
|
+
agent_next_steps: `${provider.name()} is not supported by agent setup. Tell the user to run 'dosu mcp add ${provider.id()}' manually after signing in.`
|
|
11742
|
+
});
|
|
11743
|
+
return 2;
|
|
11744
|
+
}
|
|
11745
|
+
let cfg = loadConfig();
|
|
11746
|
+
if (opts.loginTicket) {
|
|
11747
|
+
const redeemed = await redeemTicket(opts.loginTicket, cfg);
|
|
11748
|
+
if (redeemed.code !== 0 || redeemed.exit)
|
|
11749
|
+
return redeemed.code;
|
|
11750
|
+
cfg = redeemed.cfg;
|
|
11751
|
+
} else {
|
|
11752
|
+
const verified = await verifyOrMint(cfg, opts);
|
|
11753
|
+
if (verified.code !== 0 || verified.exit)
|
|
11754
|
+
return verified.code;
|
|
11755
|
+
cfg = verified.cfg;
|
|
11756
|
+
}
|
|
11757
|
+
const client = new Client(cfg);
|
|
11758
|
+
const deploymentResult = await resolveDeployment2(client, cfg, opts);
|
|
11759
|
+
if (deploymentResult.code !== 0)
|
|
11760
|
+
return deploymentResult.code;
|
|
11761
|
+
cfg = deploymentResult.cfg;
|
|
11762
|
+
const keyResult = await ensureAPIKey(client, cfg);
|
|
11763
|
+
if (keyResult.code !== 0)
|
|
11764
|
+
return keyResult.code;
|
|
11765
|
+
cfg = keyResult.cfg;
|
|
11766
|
+
try {
|
|
11767
|
+
provider.install(cfg, true);
|
|
11768
|
+
emitStep({
|
|
11769
|
+
step: "mcp_install",
|
|
11770
|
+
tool: provider.id(),
|
|
11771
|
+
tool_name: provider.name(),
|
|
11772
|
+
config_path: provider.globalConfigPath()
|
|
11773
|
+
});
|
|
11774
|
+
} catch (err) {
|
|
11775
|
+
emitError({
|
|
11776
|
+
step: "mcp_install",
|
|
11777
|
+
reason: "install_failed",
|
|
11778
|
+
agent_next_steps: `Failed to install Dosu MCP into ${provider.name()}: ${err instanceof Error ? err.message : String(err)}. Tell the user to retry or run 'dosu mcp add ${provider.id()}' manually.`
|
|
11779
|
+
});
|
|
11780
|
+
return 1;
|
|
11781
|
+
}
|
|
11782
|
+
emitStep({
|
|
11783
|
+
step: "done",
|
|
11784
|
+
agent_next_steps: `Dosu MCP is configured for ${provider.name()}. Tell the user setup is complete and they can ask their agent a Dosu question. Run 'dosu status' to verify.`
|
|
11785
|
+
});
|
|
11786
|
+
return 0;
|
|
11787
|
+
}
|
|
11788
|
+
async function redeemTicket(ticket, cfg) {
|
|
11789
|
+
try {
|
|
11790
|
+
const result = await exchangeTicket(ticket);
|
|
11791
|
+
if (result.status === "expired") {
|
|
11792
|
+
emitError({
|
|
11793
|
+
step: "auth",
|
|
11794
|
+
reason: "ticket_expired",
|
|
11795
|
+
agent_next_steps: "Ticket expired or already redeemed. Re-run the agent setup command without --login-ticket to mint a fresh one."
|
|
11796
|
+
});
|
|
11797
|
+
return { code: 1, cfg };
|
|
11798
|
+
}
|
|
11799
|
+
if (result.status === "pending") {
|
|
11800
|
+
emitStep({
|
|
11801
|
+
step: "auth",
|
|
11802
|
+
status: "pending",
|
|
11803
|
+
agent_next_steps: "User hasn't completed sign-in yet. Ask the user to confirm they've signed in, then run the same command again."
|
|
11804
|
+
});
|
|
11805
|
+
return { code: 0, cfg, exit: true };
|
|
11806
|
+
}
|
|
11807
|
+
cfg.access_token = result.access_token ?? "";
|
|
11808
|
+
cfg.refresh_token = result.refresh_token ?? "";
|
|
11809
|
+
cfg.expires_at = Math.floor(Date.now() / 1000) + (result.expires_in ?? 3600);
|
|
11810
|
+
saveConfig(cfg);
|
|
11811
|
+
logger.info("agent.flow", "Ticket redeemed; session saved");
|
|
11812
|
+
emitStep({ step: "auth", email: result.email });
|
|
11813
|
+
return { code: 0, cfg };
|
|
11814
|
+
} catch (err) {
|
|
11815
|
+
emitError({
|
|
11816
|
+
step: "auth",
|
|
11817
|
+
reason: "ticket_exchange_failed",
|
|
11818
|
+
agent_next_steps: `Failed to exchange ticket: ${err instanceof Error ? err.message : String(err)}. Re-run without --login-ticket to start over.`
|
|
11819
|
+
});
|
|
11820
|
+
return { code: 1, cfg };
|
|
11821
|
+
}
|
|
11822
|
+
}
|
|
11823
|
+
async function verifyOrMint(cfg, opts) {
|
|
11824
|
+
if (cfg.access_token) {
|
|
11825
|
+
try {
|
|
11826
|
+
const client = new Client(cfg);
|
|
11827
|
+
const resp = await client.doRequestRaw("GET", "/v1/mcp/deployments");
|
|
11828
|
+
if (resp.status === 200) {
|
|
11829
|
+
emitStep({ step: "auth" });
|
|
11830
|
+
return { code: 0, cfg };
|
|
11831
|
+
}
|
|
11832
|
+
try {
|
|
11833
|
+
await client.refreshToken();
|
|
11834
|
+
const fresh = loadConfig();
|
|
11835
|
+
emitStep({ step: "auth" });
|
|
11836
|
+
return { code: 0, cfg: fresh };
|
|
11837
|
+
} catch {}
|
|
11838
|
+
} catch {}
|
|
11839
|
+
}
|
|
11840
|
+
try {
|
|
11841
|
+
const minted = await mintTicket();
|
|
11842
|
+
emitNeedUserAction({
|
|
11843
|
+
step: "auth",
|
|
11844
|
+
url: minted.url,
|
|
11845
|
+
ticket: minted.ticket,
|
|
11846
|
+
resume_command: buildResumeCommand(opts.tool, minted.ticket, opts.deploymentID),
|
|
11847
|
+
expires_in: minted.expires_in,
|
|
11848
|
+
agent_next_steps: "Give the URL to the user so they can sign in. Wait for the user to confirm they've signed in, then run resume_command to finish setup."
|
|
11849
|
+
});
|
|
11850
|
+
return { code: 0, cfg, exit: true };
|
|
11851
|
+
} catch (err) {
|
|
11852
|
+
emitError({
|
|
11853
|
+
step: "auth",
|
|
11854
|
+
reason: "ticket_mint_failed",
|
|
11855
|
+
agent_next_steps: `Could not mint a login ticket: ${err instanceof Error ? err.message : String(err)}. Check connectivity and retry.`
|
|
11856
|
+
});
|
|
11857
|
+
return { code: 1, cfg };
|
|
11858
|
+
}
|
|
11859
|
+
}
|
|
11860
|
+
async function resolveDeployment2(client, cfg, opts) {
|
|
11861
|
+
if (opts.deploymentID) {
|
|
11862
|
+
try {
|
|
11863
|
+
const deployments = await client.getDeployments();
|
|
11864
|
+
const d3 = deployments.find((dep) => dep.deployment_id === opts.deploymentID);
|
|
11865
|
+
if (!d3) {
|
|
11866
|
+
emitError({
|
|
11867
|
+
step: "deployment",
|
|
11868
|
+
reason: "not_found",
|
|
11869
|
+
agent_next_steps: `Deployment '${opts.deploymentID}' is not accessible. Run 'dosu deployments list --json' to see options and try again with a valid id.`
|
|
11870
|
+
});
|
|
11871
|
+
return { code: 1, cfg };
|
|
11872
|
+
}
|
|
11873
|
+
cfg.deployment_id = d3.deployment_id;
|
|
11874
|
+
cfg.deployment_name = d3.name;
|
|
11875
|
+
cfg.org_id = d3.org_id;
|
|
11876
|
+
cfg.space_id = d3.space_id;
|
|
11877
|
+
cfg.mode = undefined;
|
|
11878
|
+
saveConfig(cfg);
|
|
11879
|
+
emitStep({
|
|
11880
|
+
step: "deployment",
|
|
11881
|
+
deployment_id: d3.deployment_id,
|
|
11882
|
+
name: d3.name
|
|
11883
|
+
});
|
|
11884
|
+
return { code: 0, cfg };
|
|
11885
|
+
} catch (err) {
|
|
11886
|
+
emitError({
|
|
11887
|
+
step: "deployment",
|
|
11888
|
+
reason: "fetch_failed",
|
|
11889
|
+
agent_next_steps: `Failed to load deployments: ${err instanceof Error ? err.message : String(err)}.`
|
|
11890
|
+
});
|
|
11891
|
+
return { code: 1, cfg };
|
|
11892
|
+
}
|
|
11893
|
+
}
|
|
11894
|
+
if (cfg.deployment_id) {
|
|
11895
|
+
emitStep({
|
|
11896
|
+
step: "deployment",
|
|
11897
|
+
deployment_id: cfg.deployment_id,
|
|
11898
|
+
name: cfg.deployment_name
|
|
11899
|
+
});
|
|
11900
|
+
return { code: 0, cfg };
|
|
11901
|
+
}
|
|
11902
|
+
try {
|
|
11903
|
+
const allDeployments = await client.getDeployments();
|
|
11904
|
+
if (allDeployments.length === 0) {
|
|
11905
|
+
emitError({
|
|
11906
|
+
step: "deployment",
|
|
11907
|
+
reason: "no_deployments",
|
|
11908
|
+
agent_next_steps: "No Dosu deployments are accessible to this account. Tell the user to create one at https://app.dosu.dev before retrying."
|
|
11909
|
+
});
|
|
11910
|
+
return { code: 1, cfg };
|
|
11911
|
+
}
|
|
11912
|
+
const mcpDeployments = allDeployments.filter((d3) => d3.provider_slug === MCP_PROVIDER_SLUG);
|
|
11913
|
+
if (mcpDeployments.length === 0) {
|
|
11914
|
+
emitError({
|
|
11915
|
+
step: "deployment",
|
|
11916
|
+
reason: "no_mcp_deployment",
|
|
11917
|
+
agent_next_steps: "Account has deployments but none of them back an MCP server. Tell the user to create an MCP deployment at https://app.dosu.dev, or pass --deployment <id> to target a specific deployment."
|
|
11918
|
+
});
|
|
11919
|
+
return { code: 1, cfg };
|
|
11920
|
+
}
|
|
11921
|
+
if (mcpDeployments.length === 1) {
|
|
11922
|
+
const d3 = mcpDeployments[0];
|
|
11923
|
+
cfg.deployment_id = d3.deployment_id;
|
|
11924
|
+
cfg.deployment_name = d3.name;
|
|
11925
|
+
cfg.org_id = d3.org_id;
|
|
11926
|
+
cfg.space_id = d3.space_id;
|
|
11927
|
+
cfg.mode = undefined;
|
|
11928
|
+
saveConfig(cfg);
|
|
11929
|
+
emitStep({
|
|
11930
|
+
step: "deployment",
|
|
11931
|
+
deployment_id: d3.deployment_id,
|
|
11932
|
+
name: d3.name
|
|
11933
|
+
});
|
|
11934
|
+
return { code: 0, cfg };
|
|
11935
|
+
}
|
|
11936
|
+
const candidates = mcpDeployments.map((d3) => ({
|
|
11937
|
+
deployment_id: d3.deployment_id,
|
|
11938
|
+
name: d3.name,
|
|
11939
|
+
org_id: d3.org_id,
|
|
11940
|
+
org_name: d3.org_name
|
|
11941
|
+
}));
|
|
11942
|
+
emitError({
|
|
11943
|
+
step: "deployment",
|
|
11944
|
+
reason: "multiple_deployments",
|
|
11945
|
+
candidates,
|
|
11946
|
+
agent_next_steps: `User has ${mcpDeployments.length} MCP deployments. Show these options to the user and re-run the same command with \`--deployment <id>\`:
|
|
11947
|
+
${formatCandidates(mcpDeployments)}`
|
|
11948
|
+
});
|
|
11949
|
+
return { code: 1, cfg };
|
|
11950
|
+
} catch (err) {
|
|
11951
|
+
emitError({
|
|
11952
|
+
step: "deployment",
|
|
11953
|
+
reason: "fetch_failed",
|
|
11954
|
+
agent_next_steps: `Failed to load deployments: ${err instanceof Error ? err.message : String(err)}.`
|
|
11955
|
+
});
|
|
11956
|
+
return { code: 1, cfg };
|
|
11957
|
+
}
|
|
11958
|
+
}
|
|
11959
|
+
function formatCandidates(deployments) {
|
|
11960
|
+
return deployments.map((d3) => ` - ${d3.name} (${d3.org_name}) — ${d3.deployment_id}`).join(`
|
|
11961
|
+
`);
|
|
11962
|
+
}
|
|
11963
|
+
async function ensureAPIKey(client, cfg) {
|
|
11964
|
+
if (!cfg.deployment_id) {
|
|
11965
|
+
emitError({
|
|
11966
|
+
step: "api_key",
|
|
11967
|
+
reason: "no_deployment",
|
|
11968
|
+
agent_next_steps: "Internal error: tried to mint an API key without a deployment. Re-run setup from scratch."
|
|
11969
|
+
});
|
|
11970
|
+
return { code: 1, cfg };
|
|
11971
|
+
}
|
|
11972
|
+
try {
|
|
11973
|
+
if (cfg.api_key) {
|
|
11974
|
+
const valid = await client.validateAPIKey(cfg.api_key, cfg.deployment_id);
|
|
11975
|
+
if (valid) {
|
|
11976
|
+
emitStep({ step: "api_key", reused: true });
|
|
11977
|
+
return { code: 0, cfg };
|
|
11978
|
+
}
|
|
11979
|
+
}
|
|
11980
|
+
const resp = await client.createAPIKey(cfg.deployment_id, "dosu-cli");
|
|
11981
|
+
cfg.api_key = resp.api_key;
|
|
11982
|
+
saveConfig(cfg);
|
|
11983
|
+
emitStep({ step: "api_key", reused: false });
|
|
11984
|
+
return { code: 0, cfg };
|
|
11985
|
+
} catch (err) {
|
|
11986
|
+
emitError({
|
|
11987
|
+
step: "api_key",
|
|
11988
|
+
reason: "create_failed",
|
|
11989
|
+
agent_next_steps: `Failed to create an API key: ${err instanceof Error ? err.message : String(err)}.`
|
|
11990
|
+
});
|
|
11991
|
+
return { code: 1, cfg };
|
|
11992
|
+
}
|
|
11993
|
+
}
|
|
11994
|
+
function buildResumeCommand(tool, ticket, deploymentID) {
|
|
11995
|
+
const parts = [NPX_INVOCATION, "setup", "--agent", "--tool", tool, "--login-ticket", ticket];
|
|
11996
|
+
if (deploymentID) {
|
|
11997
|
+
parts.push("--deployment", deploymentID);
|
|
11998
|
+
}
|
|
11999
|
+
return parts.join(" ");
|
|
12000
|
+
}
|
|
12001
|
+
function listAgentSupportedToolIDs() {
|
|
12002
|
+
return allSetupProviders().filter((p2) => !isStdioOnly(p2)).map((p2) => p2.id());
|
|
12003
|
+
}
|
|
12004
|
+
var NPX_INVOCATION = "npx @dosu/cli@latest";
|
|
12005
|
+
var init_flow3 = __esm(() => {
|
|
12006
|
+
init_ticket();
|
|
12007
|
+
init_client();
|
|
12008
|
+
init_config();
|
|
12009
|
+
init_logger();
|
|
12010
|
+
init_providers();
|
|
12011
|
+
init_flow2();
|
|
12012
|
+
});
|
|
12013
|
+
|
|
10985
12014
|
// src/cli/cli.ts
|
|
10986
12015
|
init_esm();
|
|
10987
12016
|
import { readFileSync as readFileSync8, unlinkSync as unlinkSync2 } from "node:fs";
|
|
@@ -12603,7 +13632,25 @@ function createProgram() {
|
|
|
12603
13632
|
const { runTUI: runTUI2 } = await Promise.resolve().then(() => (init_tui(), exports_tui));
|
|
12604
13633
|
await runTUI2();
|
|
12605
13634
|
});
|
|
12606
|
-
program2.command("login").description("Authenticate with Dosu via OAuth").action(async () => {
|
|
13635
|
+
program2.command("login").description("Authenticate with Dosu via OAuth").option("--request", "Mint a login ticket for agent / human-in-the-loop auth (prints URL and exits)").option("--check <ticket>", "Exchange a login ticket created with --request for a token").option("--json", "Emit machine-readable JSON output (use with --request or --check)").action(async (opts) => {
|
|
13636
|
+
if (opts.request && opts.check !== undefined) {
|
|
13637
|
+
console.error("--request and --check cannot be combined.");
|
|
13638
|
+
process.exitCode = 2;
|
|
13639
|
+
return;
|
|
13640
|
+
}
|
|
13641
|
+
if (opts.request) {
|
|
13642
|
+
const { runLoginRequest: runLoginRequest2 } = await Promise.resolve().then(() => (init_login_commands(), exports_login_commands));
|
|
13643
|
+
process.exitCode = await runLoginRequest2({ json: opts.json === true });
|
|
13644
|
+
return;
|
|
13645
|
+
}
|
|
13646
|
+
if (opts.check !== undefined) {
|
|
13647
|
+
const { runLoginCheck: runLoginCheck2 } = await Promise.resolve().then(() => (init_login_commands(), exports_login_commands));
|
|
13648
|
+
process.exitCode = await runLoginCheck2({
|
|
13649
|
+
ticket: opts.check,
|
|
13650
|
+
json: opts.json === true
|
|
13651
|
+
});
|
|
13652
|
+
return;
|
|
13653
|
+
}
|
|
12607
13654
|
const cfg = loadConfig();
|
|
12608
13655
|
if (isAuthenticated(cfg) && !isTokenExpired(cfg)) {
|
|
12609
13656
|
console.log("You are already logged in.");
|
|
@@ -12745,7 +13792,30 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
|
|
|
12745
13792
|
program2.addCommand(tagsCommand());
|
|
12746
13793
|
program2.addCommand(threadsCommand());
|
|
12747
13794
|
program2.addCommand(skillCommand());
|
|
12748
|
-
program2.command("setup").description("Set up Dosu MCP for your AI tools").option("--deployment <id>", "Skip to tool configuration for a specific MCP").option("--mode <mode>", "Force OSS or Cloud mode, skipping the interactive prompt (oss|cloud)").action(async (opts) => {
|
|
13795
|
+
program2.command("setup").description("Set up Dosu MCP for your AI tools").option("--deployment <id>", "Skip to tool configuration for a specific MCP").option("--mode <mode>", "Force OSS or Cloud mode, skipping the interactive prompt (oss|cloud)").option("--agent", "Run non-interactive setup designed for coding agents (requires --tool)").option("--tool <id>", "Configure a single AI tool by id (claude, cursor, codex, …). Required with --agent.").option("--login-ticket <ticket>", "Resume an --agent setup by redeeming a ticket from a previous run").action(async (opts) => {
|
|
13796
|
+
if (opts.agent) {
|
|
13797
|
+
if (!opts.tool) {
|
|
13798
|
+
const { emitError: emitError2 } = await Promise.resolve().then(() => exports_output);
|
|
13799
|
+
const { listAgentSupportedToolIDs: listAgentSupportedToolIDs2 } = await Promise.resolve().then(() => (init_flow3(), exports_flow3));
|
|
13800
|
+
emitError2({
|
|
13801
|
+
step: "setup",
|
|
13802
|
+
reason: "missing_tool",
|
|
13803
|
+
agent_next_steps: `Pass --tool <id> when using --agent. Supported ids: ${listAgentSupportedToolIDs2().join(", ")}.`
|
|
13804
|
+
});
|
|
13805
|
+
process.exitCode = 2;
|
|
13806
|
+
return;
|
|
13807
|
+
}
|
|
13808
|
+
const { runAgentSetup: runAgentSetup2 } = await Promise.resolve().then(() => (init_flow3(), exports_flow3));
|
|
13809
|
+
process.exitCode = await runAgentSetup2({
|
|
13810
|
+
tool: opts.tool,
|
|
13811
|
+
loginTicket: opts.loginTicket,
|
|
13812
|
+
deploymentID: opts.deployment
|
|
13813
|
+
});
|
|
13814
|
+
return;
|
|
13815
|
+
}
|
|
13816
|
+
if (opts.tool || opts.loginTicket) {
|
|
13817
|
+
throw new Error("--tool and --login-ticket require --agent");
|
|
13818
|
+
}
|
|
12749
13819
|
const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_flow2(), exports_flow2));
|
|
12750
13820
|
let mode;
|
|
12751
13821
|
if (opts.mode !== undefined) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dosu/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dosu CLI - Manage MCP servers for AI tools",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"dev": "bun run src/index.ts",
|
|
21
21
|
"dev:local": "DOSU_DEV=true bun run src/index.ts",
|
|
22
22
|
"build": "bun --env-file=.env.production run scripts/build-compile.ts",
|
|
23
|
+
"build:dev": "bun --env-file=.env.development run scripts/build-compile.ts",
|
|
23
24
|
"build:npm": "bun --env-file=.env.production run scripts/build-npm.ts",
|
|
24
25
|
"build:all": "bun --env-file=.env.production run scripts/build-all.ts",
|
|
25
26
|
"test": "bunx vitest run",
|