@duckmind/dm 0.26.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/bin/dm.js +256 -0
- package/package.json +23 -0
package/README.md
ADDED
package/bin/dm.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require("node:child_process");
|
|
3
|
+
const { homedir } = require("node:os");
|
|
4
|
+
const { dirname, join } = require("node:path");
|
|
5
|
+
const { existsSync, readFileSync } = require("node:fs");
|
|
6
|
+
|
|
7
|
+
const targets = {
|
|
8
|
+
darwin: { arm64: "@duckmind/dm-darwin-arm64", x64: "@duckmind/dm-darwin-x64" },
|
|
9
|
+
linux: { arm64: "@duckmind/dm-linux-arm64", x64: "@duckmind/dm-linux-x64" },
|
|
10
|
+
win32: { x64: "@duckmind/dm-windows-x64" },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const packageName = targets[process.platform] && targets[process.platform][process.arch];
|
|
14
|
+
if (!packageName) {
|
|
15
|
+
console.error(`Unsupported platform ${process.platform}/${process.arch}.`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let packageJsonPath;
|
|
20
|
+
try {
|
|
21
|
+
packageJsonPath = require.resolve(`${packageName}/package.json`);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error(`Missing optional dependency ${packageName}. Reinstall @duckmind/dm for ${process.platform}/${process.arch}.`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const binaryName = process.platform === "win32" ? "dm.exe" : "dm";
|
|
28
|
+
const binaryPath = join(dirname(packageJsonPath), binaryName);
|
|
29
|
+
if (!existsSync(binaryPath)) {
|
|
30
|
+
console.error(`Expected binary not found at ${binaryPath}.`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const DM_FREE_MODEL = process.env.DM_FREE_MODEL || "openai/gpt-4o-mini";
|
|
35
|
+
const DM_LITE_MODEL = process.env.DM_LITE_MODEL || "google/gemini-2.5-flash";
|
|
36
|
+
const DM_SMART_MODEL = process.env.DM_SMART_MODEL || "anthropic/claude-sonnet-4.5";
|
|
37
|
+
const DM_DEEP_MODEL = process.env.DM_DEEP_MODEL || "openai/o3";
|
|
38
|
+
|
|
39
|
+
function readOpenRouterKey(configPath) {
|
|
40
|
+
if (!existsSync(configPath)) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const rows = readFileSync(configPath, "utf8").split(/\r?\n/);
|
|
45
|
+
for (const raw of rows) {
|
|
46
|
+
const line = raw.trim();
|
|
47
|
+
if (!line || line.startsWith("#") || !line.includes("=")) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const [key, ...rest] = line.split("=");
|
|
51
|
+
const value = rest.join("=").trim();
|
|
52
|
+
if ((key.trim() === "OPENROUTER_API_KEY" || key.trim() === "OPENROUTER_KEY") && value) {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function findRepoConfigKey() {
|
|
63
|
+
const candidates = [];
|
|
64
|
+
let currentDir = process.cwd();
|
|
65
|
+
while (true) {
|
|
66
|
+
candidates.push(join(currentDir, "config", "openrouter.cnf"));
|
|
67
|
+
const parentDir = dirname(currentDir);
|
|
68
|
+
if (parentDir === currentDir) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
currentDir = parentDir;
|
|
72
|
+
}
|
|
73
|
+
const homeDir = homedir();
|
|
74
|
+
candidates.push(join(homeDir, ".dm", "openrouter.cnf"));
|
|
75
|
+
candidates.push(join(homeDir, ".config", "dm", "openrouter.cnf"));
|
|
76
|
+
for (const candidate of candidates) {
|
|
77
|
+
const value = readOpenRouterKey(candidate);
|
|
78
|
+
if (value) {
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const OPENROUTER_API_KEY =
|
|
86
|
+
process.env.OPENROUTER_API_KEY || process.env.OPENROUTER_KEY || findRepoConfigKey();
|
|
87
|
+
|
|
88
|
+
function resolveDuckMindModel(value) {
|
|
89
|
+
switch (value) {
|
|
90
|
+
case "free":
|
|
91
|
+
case "@preset/free":
|
|
92
|
+
case "duckmind/free":
|
|
93
|
+
case "duckmind/@preset/free":
|
|
94
|
+
case "openrouter/free":
|
|
95
|
+
case "openrouter/@preset/free":
|
|
96
|
+
return DM_FREE_MODEL;
|
|
97
|
+
case "lite":
|
|
98
|
+
case "@preset/lite":
|
|
99
|
+
case "duckmind/lite":
|
|
100
|
+
case "duckmind/@preset/lite":
|
|
101
|
+
case "openrouter/lite":
|
|
102
|
+
case "openrouter/@preset/lite":
|
|
103
|
+
return DM_LITE_MODEL;
|
|
104
|
+
case "smart":
|
|
105
|
+
case "@preset/smart":
|
|
106
|
+
case "duckmind/smart":
|
|
107
|
+
case "duckmind/@preset/smart":
|
|
108
|
+
case "openrouter/smart":
|
|
109
|
+
case "openrouter/@preset/smart":
|
|
110
|
+
return DM_SMART_MODEL;
|
|
111
|
+
case "deep":
|
|
112
|
+
case "@preset/deep":
|
|
113
|
+
case "duckmind/deep":
|
|
114
|
+
case "duckmind/@preset/deep":
|
|
115
|
+
case "openrouter/deep":
|
|
116
|
+
case "openrouter/@preset/deep":
|
|
117
|
+
return DM_DEEP_MODEL;
|
|
118
|
+
default:
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const args = process.argv.slice(2);
|
|
124
|
+
function printUpgradeHelp() {
|
|
125
|
+
console.log(`Usage:
|
|
126
|
+
dm upgrade
|
|
127
|
+
|
|
128
|
+
Install the latest @duckmind/dm release into the active global prefix.`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function resolveUpgradeCommand() {
|
|
132
|
+
const userAgent = process.env.npm_config_user_agent || process.env.NPM_CONFIG_USER_AGENT || "";
|
|
133
|
+
const isWindows = process.platform === "win32";
|
|
134
|
+
if (userAgent.startsWith("pnpm/")) {
|
|
135
|
+
return { file: isWindows ? "pnpm.cmd" : "pnpm", args: ["install", "-g", "@duckmind/dm@latest"] };
|
|
136
|
+
}
|
|
137
|
+
if (userAgent.startsWith("yarn/")) {
|
|
138
|
+
return { file: isWindows ? "yarn.cmd" : "yarn", args: ["global", "add", "@duckmind/dm@latest"] };
|
|
139
|
+
}
|
|
140
|
+
if (userAgent.startsWith("bun/")) {
|
|
141
|
+
return { file: isWindows ? "bun.cmd" : "bun", args: ["install", "-g", "@duckmind/dm@latest"] };
|
|
142
|
+
}
|
|
143
|
+
return { file: isWindows ? "npm.cmd" : "npm", args: ["install", "-g", "@duckmind/dm@latest"] };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function runUpgrade(extraArgs) {
|
|
147
|
+
if (extraArgs.length > 0) {
|
|
148
|
+
if (extraArgs.length === 1 && (extraArgs[0] === "-h" || extraArgs[0] === "--help")) {
|
|
149
|
+
printUpgradeHelp();
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
console.error(`Unknown option ${extraArgs[0]} for "upgrade".`);
|
|
153
|
+
console.error('Use "dm upgrade" or "dm upgrade --help".');
|
|
154
|
+
return 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const command = resolveUpgradeCommand();
|
|
158
|
+
const child = spawnSync(command.file, command.args, {
|
|
159
|
+
stdio: "inherit",
|
|
160
|
+
env: process.env,
|
|
161
|
+
});
|
|
162
|
+
if (child.error) {
|
|
163
|
+
console.error(child.error.message);
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
return typeof child.status === "number" ? child.status : 1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (args[0] === "upgrade") {
|
|
170
|
+
process.exit(runUpgrade(args.slice(1)));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function isDuckmindUltraAlias(value) {
|
|
174
|
+
return value === "ultra" || value === "@preset/ultra" || value === "duckmind/ultra" || value === "duckmind/@preset/ultra";
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let openrouterProvider = false;
|
|
178
|
+
let duckmindProvider = false;
|
|
179
|
+
let requestedModel;
|
|
180
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
181
|
+
const value = args[index];
|
|
182
|
+
if (value === "--provider" && index + 1 < args.length) {
|
|
183
|
+
if (args[index + 1] === "openrouter") {
|
|
184
|
+
args[index + 1] = "openrouter";
|
|
185
|
+
openrouterProvider = true;
|
|
186
|
+
}
|
|
187
|
+
if (args[index + 1] === "duckmind") {
|
|
188
|
+
duckmindProvider = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (value === "--provider=openrouter") {
|
|
192
|
+
args[index] = "--provider=openrouter";
|
|
193
|
+
openrouterProvider = true;
|
|
194
|
+
}
|
|
195
|
+
if (value === "--provider=duckmind") {
|
|
196
|
+
duckmindProvider = true;
|
|
197
|
+
}
|
|
198
|
+
if (value === "--model" && index + 1 < args.length) {
|
|
199
|
+
requestedModel = args[index + 1];
|
|
200
|
+
}
|
|
201
|
+
if (value.startsWith("--model=")) {
|
|
202
|
+
requestedModel = value.slice("--model=".length);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (duckmindProvider && !isDuckmindUltraAlias(requestedModel)) {
|
|
207
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
208
|
+
const value = args[index];
|
|
209
|
+
if (value === "--provider" && index + 1 < args.length && args[index + 1] === "duckmind") {
|
|
210
|
+
args[index + 1] = "openrouter";
|
|
211
|
+
openrouterProvider = true;
|
|
212
|
+
}
|
|
213
|
+
if (value === "--provider=duckmind") {
|
|
214
|
+
args[index] = "--provider=openrouter";
|
|
215
|
+
openrouterProvider = true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (openrouterProvider) {
|
|
221
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
222
|
+
const value = args[index];
|
|
223
|
+
if (value === "--model" && index + 1 < args.length) {
|
|
224
|
+
const resolved = resolveDuckMindModel(args[index + 1]);
|
|
225
|
+
if (resolved) {
|
|
226
|
+
args[index + 1] = resolved;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (value.startsWith("--model=")) {
|
|
230
|
+
const resolved = resolveDuckMindModel(value.slice("--model=".length));
|
|
231
|
+
if (resolved) {
|
|
232
|
+
args[index] = `--model=${resolved}`;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const child = spawnSync(binaryPath, args, {
|
|
239
|
+
stdio: "inherit",
|
|
240
|
+
env: {
|
|
241
|
+
...process.env,
|
|
242
|
+
...(OPENROUTER_API_KEY ? { OPENROUTER_API_KEY } : {}),
|
|
243
|
+
DM_FREE_MODEL,
|
|
244
|
+
DM_LITE_MODEL,
|
|
245
|
+
DM_SMART_MODEL,
|
|
246
|
+
DM_DEEP_MODEL,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
if (child.error) {
|
|
250
|
+
console.error(child.error.message);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
if (typeof child.status === "number") {
|
|
254
|
+
process.exit(child.status);
|
|
255
|
+
}
|
|
256
|
+
process.exit(1);
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@duckmind/dm",
|
|
3
|
+
"version": "0.26.9",
|
|
4
|
+
"description": "DuckMind (dm) terminal AI coworker",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dm": "bin/dm.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"README.md",
|
|
11
|
+
"bin"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20.0.0"
|
|
15
|
+
},
|
|
16
|
+
"optionalDependencies": {
|
|
17
|
+
"@duckmind/dm-darwin-arm64": "0.26.9",
|
|
18
|
+
"@duckmind/dm-darwin-x64": "0.26.9",
|
|
19
|
+
"@duckmind/dm-linux-arm64": "0.26.9",
|
|
20
|
+
"@duckmind/dm-linux-x64": "0.26.9",
|
|
21
|
+
"@duckmind/dm-windows-x64": "0.26.9"
|
|
22
|
+
}
|
|
23
|
+
}
|