droid-mode 0.0.10 → 0.0.11
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import { ensureDir, nowIsoCompact, safeIdentifier, writeJson, getDroidModeDataDir, serverNameToDirName } from "./util.mjs";
|
|
3
|
+
import { ensureDir, nowIsoCompact, safeIdentifier, uniqueSafeToolMap, writeJson, getDroidModeDataDir, serverNameToDirName } from "./util.mjs";
|
|
4
4
|
|
|
5
5
|
/** @param {string} s */
|
|
6
6
|
function toPascalCase(s) {
|
|
@@ -104,11 +104,9 @@ export function hydrateTools(opts) {
|
|
|
104
104
|
if (t) selected.push(t);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
toolmap[safe] = t?.name || safe;
|
|
111
|
-
}
|
|
107
|
+
// Build collision-free toolmap using shared function
|
|
108
|
+
const selectedNames = selected.map(t => t?.name || "tool");
|
|
109
|
+
const toolmap = uniqueSafeToolMap(selectedNames);
|
|
112
110
|
|
|
113
111
|
writeJson(path.join(baseOut, "tools.json"), selected);
|
|
114
112
|
writeJson(path.join(baseOut, "toolmap.json"), toolmap);
|
|
@@ -117,15 +115,22 @@ export function hydrateTools(opts) {
|
|
|
117
115
|
const toolmapModule = `// Auto-generated by droid-mode.\nexport default ${JSON.stringify(toolmap, null, 2)};\n`;
|
|
118
116
|
fs.writeFileSync(path.join(baseOut, "toolmap.mjs"), toolmapModule, "utf-8");
|
|
119
117
|
|
|
120
|
-
// types.d.ts
|
|
118
|
+
// types.d.ts - use toolmap keys (already collision-free)
|
|
121
119
|
const typeLines = [
|
|
122
120
|
`// Auto-generated by droid-mode (best-effort).`,
|
|
123
121
|
`// This file exists to improve IDE autocomplete; it is NOT a contract.`,
|
|
124
122
|
``,
|
|
125
123
|
];
|
|
126
124
|
|
|
125
|
+
// Build reverse map: toolName -> safeName for lookup
|
|
126
|
+
const nameToSafe = Object.create(null);
|
|
127
|
+
for (const [safe, name] of Object.entries(toolmap)) {
|
|
128
|
+
nameToSafe[name] = safe;
|
|
129
|
+
}
|
|
130
|
+
|
|
127
131
|
for (const t of selected) {
|
|
128
|
-
const
|
|
132
|
+
const toolName = t?.name || "tool";
|
|
133
|
+
const safe = nameToSafe[toolName] || safeIdentifier(toolName);
|
|
129
134
|
const pascal = toPascalCase(safe);
|
|
130
135
|
const inSchema = t?.inputSchema || t?.parameters || null;
|
|
131
136
|
const outSchema = t?.outputSchema || null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import vm from "node:vm";
|
|
4
|
-
import { nowIsoCompact, sha256Hex,
|
|
4
|
+
import { nowIsoCompact, sha256Hex, uniqueSafeToolMap, ensureDir, getDroidModeDataDir, writeJson, serverNameToDirName } from "./util.mjs";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Static, best-effort disallow list for sandboxed workflows.
|
|
@@ -100,13 +100,10 @@ export function createToolApi(opts) {
|
|
|
100
100
|
throw lastError;
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
-
//
|
|
103
|
+
// Build collision-free toolmap using shared function
|
|
104
|
+
const toolmap = uniqueSafeToolMap(opts.toolNames ?? []);
|
|
104
105
|
const t = {};
|
|
105
|
-
const toolmap
|
|
106
|
-
|
|
107
|
-
for (const toolName of opts.toolNames) {
|
|
108
|
-
const safe = safeIdentifier(toolName);
|
|
109
|
-
toolmap[safe] = toolName;
|
|
106
|
+
for (const [safe, toolName] of Object.entries(toolmap)) {
|
|
110
107
|
t[safe] = (args) => call(toolName, args);
|
|
111
108
|
}
|
|
112
109
|
|
|
@@ -56,6 +56,55 @@ export function safeIdentifier(toolName) {
|
|
|
56
56
|
return "_" + camel;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Build a collision-free safeName -> toolName map.
|
|
61
|
+
* All colliding tools get a hash suffix (not just the "losers").
|
|
62
|
+
* Guarantees deterministic output regardless of input order.
|
|
63
|
+
*
|
|
64
|
+
* @param {string[]} toolNames
|
|
65
|
+
* @returns {Record<string, string>} safeName -> originalToolName
|
|
66
|
+
*/
|
|
67
|
+
export function uniqueSafeToolMap(toolNames) {
|
|
68
|
+
const map = Object.create(null);
|
|
69
|
+
|
|
70
|
+
// Phase 1: Group by base identifier, caching base names
|
|
71
|
+
// Handle undefined/null names with fallback (matches prior behavior)
|
|
72
|
+
const groups = new Map(); // base -> [{name, base}, ...]
|
|
73
|
+
for (const rawName of toolNames) {
|
|
74
|
+
// Matches exact prior semantics: t?.name || "tool"
|
|
75
|
+
// Only null/undefined/empty-string fall back; whitespace-only IS kept
|
|
76
|
+
const toolName = rawName || "tool";
|
|
77
|
+
const base = safeIdentifier(toolName);
|
|
78
|
+
if (!groups.has(base)) groups.set(base, []);
|
|
79
|
+
groups.get(base).push({ name: toolName, base });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Phase 2: Assign safe names (sort colliding groups for determinism)
|
|
83
|
+
for (const [base, tools] of groups) {
|
|
84
|
+
if (tools.length === 1) {
|
|
85
|
+
// No collision: use base name directly
|
|
86
|
+
map[base] = tools[0].name;
|
|
87
|
+
} else {
|
|
88
|
+
// Collision: sort by string comparison for determinism
|
|
89
|
+
// (localeCompare varies by OS/locale; relational operators are consistent)
|
|
90
|
+
tools.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
91
|
+
for (const { name: toolName, base: cachedBase } of tools) {
|
|
92
|
+
const suffix = sha256Hex(toolName).slice(0, 8); // 8 chars = 4B combinations
|
|
93
|
+
let safe = `${cachedBase}_${suffix}`;
|
|
94
|
+
// Guarantee uniqueness even on hash collision (extremely rare)
|
|
95
|
+
let counter = 0;
|
|
96
|
+
while (map[safe] !== undefined) {
|
|
97
|
+
counter++;
|
|
98
|
+
safe = `${cachedBase}_${suffix}_${counter}`;
|
|
99
|
+
}
|
|
100
|
+
map[safe] = toolName;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return map;
|
|
106
|
+
}
|
|
107
|
+
|
|
59
108
|
/**
|
|
60
109
|
* Lightweight CLI args parser (supports: --k=v, --k v, flags, positional args).
|
|
61
110
|
* Returns { _: string[], flags: Record<string, string|boolean> }.
|