@withone/cli 1.21.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/index.js +393 -250
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -380,6 +380,17 @@ one config
|
|
|
380
380
|
|
|
381
381
|
Settings propagate automatically to all installed agent configs.
|
|
382
382
|
|
|
383
|
+
#### `one config skills status` / `one config skills sync`
|
|
384
|
+
|
|
385
|
+
`one init` copies the packaged skill files (`SKILL.md`, `references/`) into `~/.agents/skills/one/` and symlinks per-agent paths to that canonical directory. When the CLI self-updates, the skill files in the canonical dir would normally stay frozen at the version that was installed. To prevent stale docs, every CLI command checks a `.one-cli-version` marker in the canonical dir and silently refreshes the skill files if they don't match the running CLI version. No user action required.
|
|
386
|
+
|
|
387
|
+
| Command | What it does |
|
|
388
|
+
|---------|--------------|
|
|
389
|
+
| `one config skills status` | Show installed skill version, current CLI version, and path |
|
|
390
|
+
| `one config skills sync` | Force a re-copy of packaged skill files (for troubleshooting) |
|
|
391
|
+
|
|
392
|
+
Auto-sync refuses to resurrect skills if you opted out of skill installation during `one init` — the canonical dir has to already exist.
|
|
393
|
+
|
|
383
394
|
## The workflow
|
|
384
395
|
|
|
385
396
|
The power of One is in the workflow. Every interaction follows the same pattern:
|
package/dist/index.js
CHANGED
|
@@ -25,10 +25,10 @@ import { Command } from "commander";
|
|
|
25
25
|
// src/commands/init.ts
|
|
26
26
|
import * as p3 from "@clack/prompts";
|
|
27
27
|
import pc2 from "picocolors";
|
|
28
|
-
import
|
|
29
|
-
import
|
|
30
|
-
import
|
|
31
|
-
import { fileURLToPath } from "url";
|
|
28
|
+
import fs4 from "fs";
|
|
29
|
+
import path4 from "path";
|
|
30
|
+
import os4 from "os";
|
|
31
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
32
32
|
|
|
33
33
|
// src/lib/config.ts
|
|
34
34
|
import fs from "fs";
|
|
@@ -53,11 +53,11 @@ function readConfig() {
|
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
function writeConfig(
|
|
56
|
+
function writeConfig(config2) {
|
|
57
57
|
if (!fs.existsSync(CONFIG_DIR)) {
|
|
58
58
|
fs.mkdirSync(CONFIG_DIR, { mode: 448 });
|
|
59
59
|
}
|
|
60
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(
|
|
60
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config2, null, 2), { mode: 384 });
|
|
61
61
|
}
|
|
62
62
|
function readOneRc() {
|
|
63
63
|
const rcPath = path.join(process.cwd(), ".onerc");
|
|
@@ -108,32 +108,32 @@ function getAccessControl() {
|
|
|
108
108
|
}
|
|
109
109
|
var DEFAULT_API_BASE = "https://api.withone.ai/v1";
|
|
110
110
|
function getApiBase() {
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
111
|
+
const config2 = readConfig();
|
|
112
|
+
if (config2?.apiBase) return `${config2.apiBase}/v1`;
|
|
113
113
|
return DEFAULT_API_BASE;
|
|
114
114
|
}
|
|
115
115
|
function updateApiBase(url) {
|
|
116
|
-
const
|
|
117
|
-
if (!
|
|
116
|
+
const config2 = readConfig();
|
|
117
|
+
if (!config2) return;
|
|
118
118
|
if (url) {
|
|
119
|
-
|
|
119
|
+
config2.apiBase = url;
|
|
120
120
|
} else {
|
|
121
|
-
delete
|
|
121
|
+
delete config2.apiBase;
|
|
122
122
|
}
|
|
123
|
-
writeConfig(
|
|
123
|
+
writeConfig(config2);
|
|
124
124
|
}
|
|
125
125
|
function getCacheTtl() {
|
|
126
126
|
if (process.env.ONE_CACHE_TTL) {
|
|
127
127
|
const val = parseInt(process.env.ONE_CACHE_TTL, 10);
|
|
128
128
|
if (!isNaN(val) && val > 0) return val;
|
|
129
129
|
}
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
130
|
+
const config2 = readConfig();
|
|
131
|
+
if (config2?.cacheTtl && config2.cacheTtl > 0) return config2.cacheTtl;
|
|
132
132
|
return 3600;
|
|
133
133
|
}
|
|
134
134
|
function updateAccessControl(settings) {
|
|
135
|
-
const
|
|
136
|
-
if (!
|
|
135
|
+
const config2 = readConfig();
|
|
136
|
+
if (!config2) return;
|
|
137
137
|
const cleaned = {};
|
|
138
138
|
if (settings.permissions && settings.permissions !== "admin") {
|
|
139
139
|
cleaned.permissions = settings.permissions;
|
|
@@ -148,11 +148,11 @@ function updateAccessControl(settings) {
|
|
|
148
148
|
cleaned.knowledgeAgent = true;
|
|
149
149
|
}
|
|
150
150
|
if (Object.keys(cleaned).length === 0) {
|
|
151
|
-
delete
|
|
151
|
+
delete config2.accessControl;
|
|
152
152
|
} else {
|
|
153
|
-
|
|
153
|
+
config2.accessControl = cleaned;
|
|
154
154
|
}
|
|
155
|
-
writeConfig(
|
|
155
|
+
writeConfig(config2);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// src/lib/agents.ts
|
|
@@ -274,16 +274,16 @@ function readAgentConfig(agent, scope = "global") {
|
|
|
274
274
|
return {};
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
|
-
function writeAgentConfig(agent,
|
|
277
|
+
function writeAgentConfig(agent, config2, scope = "global") {
|
|
278
278
|
const configPath = getAgentConfigPath(agent, scope);
|
|
279
279
|
const configDir = path2.dirname(configPath);
|
|
280
280
|
if (!fs2.existsSync(configDir)) {
|
|
281
281
|
fs2.mkdirSync(configDir, { recursive: true });
|
|
282
282
|
}
|
|
283
283
|
if (agent.configFormat === "toml") {
|
|
284
|
-
fs2.writeFileSync(configPath, stringifyToml(
|
|
284
|
+
fs2.writeFileSync(configPath, stringifyToml(config2));
|
|
285
285
|
} else {
|
|
286
|
-
fs2.writeFileSync(configPath, JSON.stringify(
|
|
286
|
+
fs2.writeFileSync(configPath, JSON.stringify(config2, null, 2));
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
function getMcpServerConfig(apiKey, accessControl) {
|
|
@@ -311,17 +311,17 @@ function getMcpServerConfig(apiKey, accessControl) {
|
|
|
311
311
|
};
|
|
312
312
|
}
|
|
313
313
|
function installMcpConfig(agent, apiKey, scope = "global", accessControl) {
|
|
314
|
-
const
|
|
314
|
+
const config2 = readAgentConfig(agent, scope);
|
|
315
315
|
const configKey = agent.configKey;
|
|
316
|
-
const mcpServers =
|
|
316
|
+
const mcpServers = config2[configKey] || {};
|
|
317
317
|
mcpServers["one"] = getMcpServerConfig(apiKey, accessControl);
|
|
318
|
-
|
|
319
|
-
writeAgentConfig(agent,
|
|
318
|
+
config2[configKey] = mcpServers;
|
|
319
|
+
writeAgentConfig(agent, config2, scope);
|
|
320
320
|
}
|
|
321
321
|
function isMcpInstalled(agent, scope = "global") {
|
|
322
|
-
const
|
|
322
|
+
const config2 = readAgentConfig(agent, scope);
|
|
323
323
|
const configKey = agent.configKey;
|
|
324
|
-
const mcpServers =
|
|
324
|
+
const mcpServers = config2[configKey];
|
|
325
325
|
return mcpServers?.["one"] !== void 0;
|
|
326
326
|
}
|
|
327
327
|
function getAgentStatuses() {
|
|
@@ -397,8 +397,8 @@ async function configCommand() {
|
|
|
397
397
|
if (isAgentMode()) {
|
|
398
398
|
error("This command requires interactive input. Run without --agent.");
|
|
399
399
|
}
|
|
400
|
-
const
|
|
401
|
-
if (!
|
|
400
|
+
const config2 = readConfig();
|
|
401
|
+
if (!config2) {
|
|
402
402
|
p2.log.error(`No One config found. Run ${pc.cyan("one init")} first.`);
|
|
403
403
|
return;
|
|
404
404
|
}
|
|
@@ -439,7 +439,7 @@ async function configCommand() {
|
|
|
439
439
|
}
|
|
440
440
|
let connectionKeys;
|
|
441
441
|
if (connectionMode === "specific") {
|
|
442
|
-
connectionKeys = await selectConnections(
|
|
442
|
+
connectionKeys = await selectConnections(config2.apiKey);
|
|
443
443
|
if (connectionKeys === void 0) {
|
|
444
444
|
p2.outro("No changes made.");
|
|
445
445
|
return;
|
|
@@ -500,7 +500,7 @@ async function configCommand() {
|
|
|
500
500
|
p2.outro("No changes made.");
|
|
501
501
|
return;
|
|
502
502
|
}
|
|
503
|
-
let newApiKey =
|
|
503
|
+
let newApiKey = config2.apiKey;
|
|
504
504
|
if (baseUrlMode === "custom") {
|
|
505
505
|
const customUrl = await p2.text({
|
|
506
506
|
message: "Enter API base URL:",
|
|
@@ -601,7 +601,7 @@ async function configCommand() {
|
|
|
601
601
|
};
|
|
602
602
|
updateAccessControl(settings);
|
|
603
603
|
const updatedConfig = readConfig();
|
|
604
|
-
if (updatedConfig && newApiKey !==
|
|
604
|
+
if (updatedConfig && newApiKey !== config2.apiKey) {
|
|
605
605
|
updatedConfig.apiKey = newApiKey;
|
|
606
606
|
writeConfig(updatedConfig);
|
|
607
607
|
}
|
|
@@ -667,6 +667,214 @@ function formatList(list) {
|
|
|
667
667
|
|
|
668
668
|
// src/commands/init.ts
|
|
669
669
|
import open2 from "open";
|
|
670
|
+
|
|
671
|
+
// src/lib/skill-sync.ts
|
|
672
|
+
import fs3 from "fs";
|
|
673
|
+
import os3 from "os";
|
|
674
|
+
import path3 from "path";
|
|
675
|
+
import { fileURLToPath } from "url";
|
|
676
|
+
|
|
677
|
+
// src/commands/update.ts
|
|
678
|
+
import { createRequire } from "module";
|
|
679
|
+
import { spawn } from "child_process";
|
|
680
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
681
|
+
import { homedir } from "os";
|
|
682
|
+
import { join } from "path";
|
|
683
|
+
var require2 = createRequire(import.meta.url);
|
|
684
|
+
var { version: currentVersion } = require2("../package.json");
|
|
685
|
+
var CACHE_PATH = join(homedir(), ".one", "update-check.json");
|
|
686
|
+
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
687
|
+
var AGE_GATE_MS = 30 * 60 * 1e3;
|
|
688
|
+
async function fetchLatestVersionInfo() {
|
|
689
|
+
try {
|
|
690
|
+
const res = await fetch("https://registry.npmjs.org/@withone/cli");
|
|
691
|
+
if (!res.ok) return null;
|
|
692
|
+
const data = await res.json();
|
|
693
|
+
const latest = data["dist-tags"]?.latest;
|
|
694
|
+
if (!latest) return null;
|
|
695
|
+
return { version: latest, publishedAt: data.time?.[latest] ?? null };
|
|
696
|
+
} catch {
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
function readCache() {
|
|
701
|
+
try {
|
|
702
|
+
return JSON.parse(readFileSync(CACHE_PATH, "utf8"));
|
|
703
|
+
} catch {
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
function writeCache(latestVersion, publishedAt) {
|
|
708
|
+
try {
|
|
709
|
+
mkdirSync(join(homedir(), ".one"), { recursive: true });
|
|
710
|
+
writeFileSync(CACHE_PATH, JSON.stringify({ lastCheck: Date.now(), latestVersion, publishedAt }));
|
|
711
|
+
} catch {
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
async function checkLatestVersion() {
|
|
715
|
+
const info = await fetchLatestVersionInfo();
|
|
716
|
+
if (info) writeCache(info.version, info.publishedAt);
|
|
717
|
+
return info?.version ?? null;
|
|
718
|
+
}
|
|
719
|
+
async function checkLatestVersionCached() {
|
|
720
|
+
const cache2 = readCache();
|
|
721
|
+
if (cache2 && Date.now() - cache2.lastCheck < CHECK_INTERVAL_MS) {
|
|
722
|
+
return { version: cache2.latestVersion, publishedAt: cache2.publishedAt ?? null };
|
|
723
|
+
}
|
|
724
|
+
const info = await fetchLatestVersionInfo();
|
|
725
|
+
if (info) writeCache(info.version, info.publishedAt);
|
|
726
|
+
return info;
|
|
727
|
+
}
|
|
728
|
+
function getCurrentVersion() {
|
|
729
|
+
return currentVersion;
|
|
730
|
+
}
|
|
731
|
+
async function updateCommand() {
|
|
732
|
+
const s = createSpinner();
|
|
733
|
+
s.start("Checking for updates...");
|
|
734
|
+
const latestVersion = await checkLatestVersion();
|
|
735
|
+
if (!latestVersion) {
|
|
736
|
+
s.stop("");
|
|
737
|
+
error("Failed to check for updates \u2014 could not reach npm registry");
|
|
738
|
+
}
|
|
739
|
+
if (currentVersion === latestVersion) {
|
|
740
|
+
s.stop("Already up to date");
|
|
741
|
+
if (isAgentMode()) {
|
|
742
|
+
json({ current: currentVersion, latest: latestVersion, updated: false, message: "Already up to date" });
|
|
743
|
+
} else {
|
|
744
|
+
console.log(`Already up to date (v${currentVersion})`);
|
|
745
|
+
}
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
s.stop(`Update available: v${currentVersion} \u2192 v${latestVersion}`);
|
|
749
|
+
console.log(`Updating @withone/cli: v${currentVersion} \u2192 v${latestVersion}...`);
|
|
750
|
+
const code = await new Promise((resolve) => {
|
|
751
|
+
const child = spawn("npm", ["install", "-g", "@withone/cli@latest", "--force"], {
|
|
752
|
+
stdio: isAgentMode() ? "pipe" : "inherit",
|
|
753
|
+
shell: true
|
|
754
|
+
});
|
|
755
|
+
child.on("close", resolve);
|
|
756
|
+
child.on("error", () => resolve(1));
|
|
757
|
+
});
|
|
758
|
+
if (code === 0) {
|
|
759
|
+
if (isAgentMode()) {
|
|
760
|
+
json({ current: currentVersion, latest: latestVersion, updated: true, message: "Updated successfully" });
|
|
761
|
+
} else {
|
|
762
|
+
console.log(`Successfully updated to v${latestVersion}`);
|
|
763
|
+
}
|
|
764
|
+
} else {
|
|
765
|
+
error("Update failed \u2014 try running: npm install -g @withone/cli@latest");
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function isNewerVersion(latest, current) {
|
|
769
|
+
const parse = (v) => v.split(".").map(Number);
|
|
770
|
+
const [lMaj, lMin, lPat] = parse(latest);
|
|
771
|
+
const [cMaj, cMin, cPat] = parse(current);
|
|
772
|
+
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
773
|
+
if (lMin !== cMin) return lMin > cMin;
|
|
774
|
+
return lPat > cPat;
|
|
775
|
+
}
|
|
776
|
+
function autoUpdate(targetVersion, publishedAt) {
|
|
777
|
+
if (publishedAt) {
|
|
778
|
+
const age = Date.now() - new Date(publishedAt).getTime();
|
|
779
|
+
if (age < AGE_GATE_MS) return;
|
|
780
|
+
}
|
|
781
|
+
const child = spawn("npm", ["install", "-g", `@withone/cli@${targetVersion}`], {
|
|
782
|
+
detached: true,
|
|
783
|
+
stdio: "ignore",
|
|
784
|
+
shell: true
|
|
785
|
+
});
|
|
786
|
+
child.unref();
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// src/lib/skill-sync.ts
|
|
790
|
+
var CANONICAL_SKILL_DIR = ".agents/skills";
|
|
791
|
+
var VERSION_MARKER = ".one-cli-version";
|
|
792
|
+
function getPackagedSkillDir() {
|
|
793
|
+
const here = path3.dirname(fileURLToPath(import.meta.url));
|
|
794
|
+
return path3.resolve(here, "..", "skills", "one");
|
|
795
|
+
}
|
|
796
|
+
function getCanonicalSkillPath() {
|
|
797
|
+
return path3.join(os3.homedir(), CANONICAL_SKILL_DIR, "one");
|
|
798
|
+
}
|
|
799
|
+
function getVersionMarkerPath() {
|
|
800
|
+
return path3.join(getCanonicalSkillPath(), VERSION_MARKER);
|
|
801
|
+
}
|
|
802
|
+
function isSkillInstalled() {
|
|
803
|
+
return fs3.existsSync(path3.join(getCanonicalSkillPath(), "SKILL.md"));
|
|
804
|
+
}
|
|
805
|
+
function readInstalledSkillVersion() {
|
|
806
|
+
try {
|
|
807
|
+
return fs3.readFileSync(getVersionMarkerPath(), "utf-8").trim() || null;
|
|
808
|
+
} catch {
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function writeInstalledSkillVersion(version2) {
|
|
813
|
+
try {
|
|
814
|
+
fs3.mkdirSync(getCanonicalSkillPath(), { recursive: true });
|
|
815
|
+
fs3.writeFileSync(getVersionMarkerPath(), `${version2}
|
|
816
|
+
`);
|
|
817
|
+
} catch {
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
function copyDirSync(src, dest) {
|
|
821
|
+
fs3.mkdirSync(dest, { recursive: true });
|
|
822
|
+
for (const entry of fs3.readdirSync(src, { withFileTypes: true })) {
|
|
823
|
+
const srcPath = path3.join(src, entry.name);
|
|
824
|
+
const destPath = path3.join(dest, entry.name);
|
|
825
|
+
if (entry.isDirectory()) {
|
|
826
|
+
copyDirSync(srcPath, destPath);
|
|
827
|
+
} else {
|
|
828
|
+
fs3.copyFileSync(srcPath, destPath);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
function syncSkillsIfStale() {
|
|
833
|
+
if (!isSkillInstalled()) {
|
|
834
|
+
return { synced: false, reason: "not-installed" };
|
|
835
|
+
}
|
|
836
|
+
const current = getCurrentVersion();
|
|
837
|
+
const installed = readInstalledSkillVersion();
|
|
838
|
+
if (installed === current) {
|
|
839
|
+
return { synced: false, reason: "up-to-date", from: installed, to: current };
|
|
840
|
+
}
|
|
841
|
+
return performSync(current, installed === null ? "missing-marker" : "stale");
|
|
842
|
+
}
|
|
843
|
+
function forceSyncSkills() {
|
|
844
|
+
if (!isSkillInstalled()) {
|
|
845
|
+
return { synced: false, reason: "not-installed" };
|
|
846
|
+
}
|
|
847
|
+
return performSync(getCurrentVersion(), "forced");
|
|
848
|
+
}
|
|
849
|
+
function performSync(current, reason) {
|
|
850
|
+
const source = getPackagedSkillDir();
|
|
851
|
+
if (!fs3.existsSync(path3.join(source, "SKILL.md"))) {
|
|
852
|
+
return { synced: false, reason: "source-missing" };
|
|
853
|
+
}
|
|
854
|
+
const canonical = getCanonicalSkillPath();
|
|
855
|
+
try {
|
|
856
|
+
copyDirSync(source, canonical);
|
|
857
|
+
writeInstalledSkillVersion(current);
|
|
858
|
+
return { synced: true, reason, from: readInstalledSkillVersion() ?? null, to: current };
|
|
859
|
+
} catch (err) {
|
|
860
|
+
return { synced: false, reason: "error", error: err instanceof Error ? err.message : String(err) };
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
function getSkillStatus() {
|
|
864
|
+
const installed = isSkillInstalled();
|
|
865
|
+
const installedVersion = readInstalledSkillVersion();
|
|
866
|
+
const currentVersion2 = getCurrentVersion();
|
|
867
|
+
return {
|
|
868
|
+
installed,
|
|
869
|
+
canonicalPath: getCanonicalSkillPath(),
|
|
870
|
+
installedVersion,
|
|
871
|
+
currentVersion: currentVersion2,
|
|
872
|
+
upToDate: installed && installedVersion === currentVersion2,
|
|
873
|
+
markerExists: installedVersion !== null
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// src/commands/init.ts
|
|
670
878
|
async function initCommand(options) {
|
|
671
879
|
if (isAgentMode()) {
|
|
672
880
|
error("This command requires interactive input. Run without --agent.");
|
|
@@ -682,7 +890,7 @@ async function initCommand(options) {
|
|
|
682
890
|
async function handleExistingConfig(apiKey, options) {
|
|
683
891
|
const statuses = getAgentStatuses();
|
|
684
892
|
const masked = maskApiKey(apiKey);
|
|
685
|
-
const skillInstalled =
|
|
893
|
+
const skillInstalled = isSkillInstalled2();
|
|
686
894
|
console.log();
|
|
687
895
|
console.log(` ${pc2.bold("Current Setup")}`);
|
|
688
896
|
console.log(` ${pc2.dim("\u2500".repeat(42))}`);
|
|
@@ -818,12 +1026,12 @@ ${pc2.cyan(getApiKeyUrl())}`, "API Key");
|
|
|
818
1026
|
reinstalled.push(`${s.agent.name} (project)`);
|
|
819
1027
|
}
|
|
820
1028
|
}
|
|
821
|
-
const
|
|
1029
|
+
const config2 = readConfig();
|
|
822
1030
|
writeConfig({
|
|
823
1031
|
apiKey: newKey,
|
|
824
|
-
installedAgents:
|
|
825
|
-
createdAt:
|
|
826
|
-
accessControl:
|
|
1032
|
+
installedAgents: config2?.installedAgents ?? [],
|
|
1033
|
+
createdAt: config2?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1034
|
+
accessControl: config2?.accessControl
|
|
827
1035
|
});
|
|
828
1036
|
if (reinstalled.length > 0) {
|
|
829
1037
|
p3.log.success(`Updated MCP configs: ${reinstalled.join(", ")}`);
|
|
@@ -842,48 +1050,49 @@ var SKILL_AGENTS = [
|
|
|
842
1050
|
{ id: "opencode", name: "OpenCode", skillDir: ".opencode/skills" },
|
|
843
1051
|
{ id: "roo", name: "Roo", skillDir: ".roo/skills" }
|
|
844
1052
|
];
|
|
845
|
-
var
|
|
1053
|
+
var CANONICAL_SKILL_DIR2 = ".agents/skills";
|
|
846
1054
|
function getSkillSourceDir() {
|
|
847
|
-
const __dirname2 =
|
|
848
|
-
return
|
|
1055
|
+
const __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
|
|
1056
|
+
return path4.resolve(__dirname2, "..", "skills", "one");
|
|
849
1057
|
}
|
|
850
|
-
function
|
|
851
|
-
return
|
|
1058
|
+
function getCanonicalSkillPath2() {
|
|
1059
|
+
return path4.join(os4.homedir(), CANONICAL_SKILL_DIR2, "one");
|
|
852
1060
|
}
|
|
853
1061
|
function getAgentSkillPath(agent) {
|
|
854
|
-
return
|
|
1062
|
+
return path4.join(os4.homedir(), agent.skillDir, "one");
|
|
855
1063
|
}
|
|
856
|
-
function
|
|
857
|
-
return
|
|
1064
|
+
function isSkillInstalled2() {
|
|
1065
|
+
return fs4.existsSync(path4.join(getCanonicalSkillPath2(), "SKILL.md"));
|
|
858
1066
|
}
|
|
859
1067
|
function isSkillInstalledForAgent(agent) {
|
|
860
|
-
return
|
|
1068
|
+
return fs4.existsSync(path4.join(getAgentSkillPath(agent), "SKILL.md"));
|
|
861
1069
|
}
|
|
862
|
-
function
|
|
863
|
-
|
|
864
|
-
for (const entry of
|
|
865
|
-
const srcPath =
|
|
866
|
-
const destPath =
|
|
1070
|
+
function copyDirSync2(src, dest) {
|
|
1071
|
+
fs4.mkdirSync(dest, { recursive: true });
|
|
1072
|
+
for (const entry of fs4.readdirSync(src, { withFileTypes: true })) {
|
|
1073
|
+
const srcPath = path4.join(src, entry.name);
|
|
1074
|
+
const destPath = path4.join(dest, entry.name);
|
|
867
1075
|
if (entry.isDirectory()) {
|
|
868
|
-
|
|
1076
|
+
copyDirSync2(srcPath, destPath);
|
|
869
1077
|
} else {
|
|
870
|
-
|
|
1078
|
+
fs4.copyFileSync(srcPath, destPath);
|
|
871
1079
|
}
|
|
872
1080
|
}
|
|
873
1081
|
}
|
|
874
1082
|
function installSkillForAgents(agentIds) {
|
|
875
1083
|
const source = getSkillSourceDir();
|
|
876
|
-
const canonical =
|
|
1084
|
+
const canonical = getCanonicalSkillPath2();
|
|
877
1085
|
const installed = [];
|
|
878
1086
|
const failed = [];
|
|
879
|
-
if (!
|
|
1087
|
+
if (!fs4.existsSync(path4.join(source, "SKILL.md"))) {
|
|
880
1088
|
return { installed: [], failed: ["skill source not found"] };
|
|
881
1089
|
}
|
|
882
1090
|
try {
|
|
883
|
-
if (
|
|
884
|
-
|
|
1091
|
+
if (fs4.existsSync(canonical)) {
|
|
1092
|
+
fs4.rmSync(canonical, { recursive: true });
|
|
885
1093
|
}
|
|
886
|
-
|
|
1094
|
+
copyDirSync2(source, canonical);
|
|
1095
|
+
writeInstalledSkillVersion(getCurrentVersion());
|
|
887
1096
|
} catch {
|
|
888
1097
|
return { installed: [], failed: ["canonical copy"] };
|
|
889
1098
|
}
|
|
@@ -897,15 +1106,15 @@ function installSkillForAgents(agentIds) {
|
|
|
897
1106
|
continue;
|
|
898
1107
|
}
|
|
899
1108
|
try {
|
|
900
|
-
const agentSkillsDir =
|
|
901
|
-
|
|
1109
|
+
const agentSkillsDir = path4.dirname(agentPath);
|
|
1110
|
+
fs4.mkdirSync(agentSkillsDir, { recursive: true });
|
|
902
1111
|
try {
|
|
903
|
-
|
|
904
|
-
|
|
1112
|
+
fs4.lstatSync(agentPath);
|
|
1113
|
+
fs4.rmSync(agentPath, { recursive: true });
|
|
905
1114
|
} catch {
|
|
906
1115
|
}
|
|
907
|
-
const relative =
|
|
908
|
-
|
|
1116
|
+
const relative = path4.relative(agentSkillsDir, canonical);
|
|
1117
|
+
fs4.symlinkSync(relative, agentPath);
|
|
909
1118
|
installed.push(agent.name);
|
|
910
1119
|
seen.set(agentPath, true);
|
|
911
1120
|
} catch {
|
|
@@ -1553,35 +1762,35 @@ import * as p6 from "@clack/prompts";
|
|
|
1553
1762
|
import pc6 from "picocolors";
|
|
1554
1763
|
|
|
1555
1764
|
// src/lib/cache.ts
|
|
1556
|
-
import
|
|
1557
|
-
import
|
|
1558
|
-
import
|
|
1559
|
-
var CACHE_BASE =
|
|
1560
|
-
var KNOWLEDGE_DIR =
|
|
1561
|
-
var SEARCH_DIR =
|
|
1765
|
+
import fs5 from "fs";
|
|
1766
|
+
import path5 from "path";
|
|
1767
|
+
import os5 from "os";
|
|
1768
|
+
var CACHE_BASE = path5.join(os5.homedir(), ".one", "cache");
|
|
1769
|
+
var KNOWLEDGE_DIR = path5.join(CACHE_BASE, "knowledge");
|
|
1770
|
+
var SEARCH_DIR = path5.join(CACHE_BASE, "search");
|
|
1562
1771
|
function sanitizeFilename(input) {
|
|
1563
1772
|
return input.replace(/[^a-zA-Z0-9_\-\.]/g, "_");
|
|
1564
1773
|
}
|
|
1565
1774
|
function knowledgeCachePath(actionId) {
|
|
1566
|
-
return
|
|
1775
|
+
return path5.join(KNOWLEDGE_DIR, `${sanitizeFilename(actionId)}.json`);
|
|
1567
1776
|
}
|
|
1568
1777
|
function searchCachePath(platform, query, type) {
|
|
1569
1778
|
const key = `${platform}_${sanitizeFilename(query)}_${type || "knowledge"}`;
|
|
1570
|
-
return
|
|
1779
|
+
return path5.join(SEARCH_DIR, `${key}.json`);
|
|
1571
1780
|
}
|
|
1572
|
-
function
|
|
1781
|
+
function readCache2(filePath) {
|
|
1573
1782
|
try {
|
|
1574
|
-
const content =
|
|
1783
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
1575
1784
|
return JSON.parse(content);
|
|
1576
1785
|
} catch {
|
|
1577
1786
|
return null;
|
|
1578
1787
|
}
|
|
1579
1788
|
}
|
|
1580
|
-
function
|
|
1789
|
+
function writeCache2(filePath, entry) {
|
|
1581
1790
|
try {
|
|
1582
|
-
const dir =
|
|
1583
|
-
|
|
1584
|
-
|
|
1791
|
+
const dir = path5.dirname(filePath);
|
|
1792
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
1793
|
+
fs5.writeFileSync(filePath, JSON.stringify(entry, null, 2));
|
|
1585
1794
|
} catch {
|
|
1586
1795
|
}
|
|
1587
1796
|
}
|
|
@@ -1619,11 +1828,11 @@ function listCacheEntries() {
|
|
|
1619
1828
|
const entries = [];
|
|
1620
1829
|
for (const [dir, type] of [[KNOWLEDGE_DIR, "knowledge"], [SEARCH_DIR, "search"]]) {
|
|
1621
1830
|
try {
|
|
1622
|
-
const files =
|
|
1831
|
+
const files = fs5.readdirSync(dir);
|
|
1623
1832
|
for (const file of files) {
|
|
1624
1833
|
if (!file.endsWith(".json")) continue;
|
|
1625
|
-
const filePath =
|
|
1626
|
-
const entry =
|
|
1834
|
+
const filePath = path5.join(dir, file);
|
|
1835
|
+
const entry = readCache2(filePath);
|
|
1627
1836
|
if (entry) {
|
|
1628
1837
|
entries.push({ type, filePath, entry });
|
|
1629
1838
|
}
|
|
@@ -1637,12 +1846,12 @@ function clearAll() {
|
|
|
1637
1846
|
let count = 0;
|
|
1638
1847
|
for (const dir of [KNOWLEDGE_DIR, SEARCH_DIR]) {
|
|
1639
1848
|
try {
|
|
1640
|
-
const files =
|
|
1849
|
+
const files = fs5.readdirSync(dir);
|
|
1641
1850
|
for (const file of files) {
|
|
1642
|
-
|
|
1851
|
+
fs5.unlinkSync(path5.join(dir, file));
|
|
1643
1852
|
count++;
|
|
1644
1853
|
}
|
|
1645
|
-
|
|
1854
|
+
fs5.rmdirSync(dir);
|
|
1646
1855
|
} catch {
|
|
1647
1856
|
}
|
|
1648
1857
|
}
|
|
@@ -1651,7 +1860,7 @@ function clearAll() {
|
|
|
1651
1860
|
function clearEntry(actionId) {
|
|
1652
1861
|
const filePath = knowledgeCachePath(actionId);
|
|
1653
1862
|
try {
|
|
1654
|
-
|
|
1863
|
+
fs5.unlinkSync(filePath);
|
|
1655
1864
|
return true;
|
|
1656
1865
|
} catch {
|
|
1657
1866
|
return false;
|
|
@@ -1697,7 +1906,7 @@ async function actionsSearchCommand(platform, query, options) {
|
|
|
1697
1906
|
const agentType = knowledgeAgent ? "knowledge" : options.type || "execute";
|
|
1698
1907
|
const useCache = options.cache !== false;
|
|
1699
1908
|
const cachePath = searchCachePath(platform, query, agentType || "knowledge");
|
|
1700
|
-
const cached = useCache ?
|
|
1909
|
+
const cached = useCache ? readCache2(cachePath) : null;
|
|
1701
1910
|
let cleanedActions;
|
|
1702
1911
|
let cacheHit = false;
|
|
1703
1912
|
if (cached && isFresh(cached)) {
|
|
@@ -1713,7 +1922,7 @@ async function actionsSearchCommand(platform, query, options) {
|
|
|
1713
1922
|
);
|
|
1714
1923
|
if (result.status === 304 && cached) {
|
|
1715
1924
|
cached.cachedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1716
|
-
|
|
1925
|
+
writeCache2(cachePath, cached);
|
|
1717
1926
|
cleanedActions = cached.data.actions;
|
|
1718
1927
|
cacheHit = true;
|
|
1719
1928
|
} else {
|
|
@@ -1726,7 +1935,7 @@ async function actionsSearchCommand(platform, query, options) {
|
|
|
1726
1935
|
method: action.method,
|
|
1727
1936
|
path: action.path
|
|
1728
1937
|
}));
|
|
1729
|
-
|
|
1938
|
+
writeCache2(cachePath, makeCacheEntry(
|
|
1730
1939
|
`${platform}_${query}_${agentType || "knowledge"}`,
|
|
1731
1940
|
{ actions: cleanedActions },
|
|
1732
1941
|
result.etag
|
|
@@ -1750,7 +1959,7 @@ async function actionsSearchCommand(platform, query, options) {
|
|
|
1750
1959
|
if (cacheHit && cached) {
|
|
1751
1960
|
response._cache = buildCacheMeta(cached, true);
|
|
1752
1961
|
} else {
|
|
1753
|
-
const freshEntry =
|
|
1962
|
+
const freshEntry = readCache2(cachePath);
|
|
1754
1963
|
response._cache = buildCacheMeta(freshEntry, false);
|
|
1755
1964
|
}
|
|
1756
1965
|
json(response);
|
|
@@ -1810,7 +2019,7 @@ Execute: ${pc6.cyan(`one actions execute ${platform} <actionId> <connectionK
|
|
|
1810
2019
|
async function actionsKnowledgeCommand(platform, actionId, options) {
|
|
1811
2020
|
const cachePath = knowledgeCachePath(actionId);
|
|
1812
2021
|
if (options.cacheStatus) {
|
|
1813
|
-
const entry =
|
|
2022
|
+
const entry = readCache2(cachePath);
|
|
1814
2023
|
if (!entry) {
|
|
1815
2024
|
json({
|
|
1816
2025
|
cached: false,
|
|
@@ -1858,7 +2067,7 @@ async function actionsKnowledgeCommand(platform, actionId, options) {
|
|
|
1858
2067
|
spinner5.start(`Loading knowledge for action ${pc6.dim(actionId)}...`);
|
|
1859
2068
|
try {
|
|
1860
2069
|
const useCache = options.cache !== false;
|
|
1861
|
-
const cached = useCache ?
|
|
2070
|
+
const cached = useCache ? readCache2(cachePath) : null;
|
|
1862
2071
|
let knowledgeData;
|
|
1863
2072
|
let cacheHit = false;
|
|
1864
2073
|
let cacheEntry = cached;
|
|
@@ -1873,13 +2082,13 @@ async function actionsKnowledgeCommand(platform, actionId, options) {
|
|
|
1873
2082
|
);
|
|
1874
2083
|
if (result.status === 304 && cached) {
|
|
1875
2084
|
cached.cachedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1876
|
-
|
|
2085
|
+
writeCache2(cachePath, cached);
|
|
1877
2086
|
knowledgeData = cached.data;
|
|
1878
2087
|
cacheHit = true;
|
|
1879
2088
|
} else {
|
|
1880
2089
|
knowledgeData = result.data;
|
|
1881
2090
|
const newEntry = makeCacheEntry(actionId, knowledgeData, result.etag);
|
|
1882
|
-
|
|
2091
|
+
writeCache2(cachePath, newEntry);
|
|
1883
2092
|
cacheEntry = newEntry;
|
|
1884
2093
|
}
|
|
1885
2094
|
} catch (fetchError) {
|
|
@@ -2092,26 +2301,26 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2092
2301
|
const validTypes = FLOW_SCHEMA.stepTypes.map((st) => st.type);
|
|
2093
2302
|
for (let i = 0; i < steps.length; i++) {
|
|
2094
2303
|
const step = steps[i];
|
|
2095
|
-
const
|
|
2304
|
+
const path6 = `${pathPrefix}[${i}]`;
|
|
2096
2305
|
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
2097
|
-
errors.push({ path:
|
|
2306
|
+
errors.push({ path: path6, message: "Step must be an object" });
|
|
2098
2307
|
continue;
|
|
2099
2308
|
}
|
|
2100
2309
|
const s = step;
|
|
2101
2310
|
if (!s.id || typeof s.id !== "string") {
|
|
2102
|
-
errors.push({ path: `${
|
|
2311
|
+
errors.push({ path: `${path6}.id`, message: 'Step must have a string "id"' });
|
|
2103
2312
|
}
|
|
2104
2313
|
if (!s.name || typeof s.name !== "string") {
|
|
2105
|
-
errors.push({ path: `${
|
|
2314
|
+
errors.push({ path: `${path6}.name`, message: 'Step must have a string "name"' });
|
|
2106
2315
|
}
|
|
2107
2316
|
if (!s.type || !validTypes.includes(s.type)) {
|
|
2108
|
-
errors.push({ path: `${
|
|
2317
|
+
errors.push({ path: `${path6}.type`, message: `Step type must be one of: ${validTypes.join(", ")}` });
|
|
2109
2318
|
continue;
|
|
2110
2319
|
}
|
|
2111
2320
|
if (s.onError && typeof s.onError === "object") {
|
|
2112
2321
|
const oe = s.onError;
|
|
2113
2322
|
if (!FLOW_SCHEMA.errorStrategies.includes(oe.strategy)) {
|
|
2114
|
-
errors.push({ path: `${
|
|
2323
|
+
errors.push({ path: `${path6}.onError.strategy`, message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
|
|
2115
2324
|
}
|
|
2116
2325
|
}
|
|
2117
2326
|
const descriptor = getStepTypeDescriptor(s.type);
|
|
@@ -2121,15 +2330,15 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2121
2330
|
if (!configObj || typeof configObj !== "object") {
|
|
2122
2331
|
const hint = detectFlatConfigHint(s, descriptor);
|
|
2123
2332
|
errors.push({
|
|
2124
|
-
path: `${
|
|
2333
|
+
path: `${path6}.${configKey}`,
|
|
2125
2334
|
message: `${capitalize(descriptor.type)} step must have a "${configKey}" config object${hint}`
|
|
2126
2335
|
});
|
|
2127
2336
|
continue;
|
|
2128
2337
|
}
|
|
2129
|
-
const
|
|
2338
|
+
const config2 = configObj;
|
|
2130
2339
|
for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
|
|
2131
|
-
const fieldPath = `${
|
|
2132
|
-
const value =
|
|
2340
|
+
const fieldPath = `${path6}.${configKey}.${fieldName}`;
|
|
2341
|
+
const value = config2[fieldName];
|
|
2133
2342
|
if (fd.required && (value === void 0 || value === null || value === "")) {
|
|
2134
2343
|
errors.push({ path: fieldPath, message: `${capitalize(descriptor.type)} must have ${fd.type === "string" ? "a string" : fd.type === "array" ? "a" : "a"} "${fieldName}"` });
|
|
2135
2344
|
continue;
|
|
@@ -2161,21 +2370,21 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2161
2370
|
}
|
|
2162
2371
|
}
|
|
2163
2372
|
if (descriptor.type === "code") {
|
|
2164
|
-
const hasSource = typeof
|
|
2165
|
-
const hasModule = typeof
|
|
2373
|
+
const hasSource = typeof config2.source === "string" && config2.source.length > 0;
|
|
2374
|
+
const hasModule = typeof config2.module === "string" && config2.module.length > 0;
|
|
2166
2375
|
if (!hasSource && !hasModule) {
|
|
2167
|
-
errors.push({ path: `${
|
|
2376
|
+
errors.push({ path: `${path6}.${configKey}`, message: 'Code step must define either "source" (inline JS) or "module" (path to .mjs file)' });
|
|
2168
2377
|
} else if (hasSource && hasModule) {
|
|
2169
|
-
errors.push({ path: `${
|
|
2378
|
+
errors.push({ path: `${path6}.${configKey}`, message: 'Code step cannot define both "source" and "module" \u2014 pick one' });
|
|
2170
2379
|
}
|
|
2171
2380
|
if (hasModule) {
|
|
2172
|
-
const m =
|
|
2381
|
+
const m = config2.module;
|
|
2173
2382
|
if (m.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(m)) {
|
|
2174
|
-
errors.push({ path: `${
|
|
2383
|
+
errors.push({ path: `${path6}.${configKey}.module`, message: "Code module path must be relative to the flow folder (no absolute paths)" });
|
|
2175
2384
|
} else if (m.split(/[\\/]/).includes("..")) {
|
|
2176
|
-
errors.push({ path: `${
|
|
2385
|
+
errors.push({ path: `${path6}.${configKey}.module`, message: 'Code module path must not escape the flow folder ("..")' });
|
|
2177
2386
|
} else if (!m.endsWith(".mjs")) {
|
|
2178
|
-
errors.push({ path: `${
|
|
2387
|
+
errors.push({ path: `${path6}.${configKey}.module`, message: "Code module must be a .mjs file" });
|
|
2179
2388
|
}
|
|
2180
2389
|
}
|
|
2181
2390
|
}
|
|
@@ -2199,16 +2408,16 @@ function validateStepIds(flow2) {
|
|
|
2199
2408
|
function collectIds(steps, pathPrefix) {
|
|
2200
2409
|
for (let i = 0; i < steps.length; i++) {
|
|
2201
2410
|
const step = steps[i];
|
|
2202
|
-
const
|
|
2411
|
+
const path6 = `${pathPrefix}[${i}]`;
|
|
2203
2412
|
if (seen.has(step.id)) {
|
|
2204
|
-
errors.push({ path: `${
|
|
2413
|
+
errors.push({ path: `${path6}.id`, message: `Duplicate step ID: "${step.id}"` });
|
|
2205
2414
|
} else {
|
|
2206
2415
|
seen.add(step.id);
|
|
2207
2416
|
}
|
|
2208
2417
|
for (const { configKey, fieldName } of nestedKeys) {
|
|
2209
|
-
const
|
|
2210
|
-
if (
|
|
2211
|
-
collectIds(
|
|
2418
|
+
const config2 = step[configKey];
|
|
2419
|
+
if (config2 && Array.isArray(config2[fieldName])) {
|
|
2420
|
+
collectIds(config2[fieldName], `${path6}.${configKey}.${fieldName}`);
|
|
2212
2421
|
}
|
|
2213
2422
|
}
|
|
2214
2423
|
}
|
|
@@ -2225,9 +2434,9 @@ function validateSelectorReferences(flow2) {
|
|
|
2225
2434
|
for (const step of steps) {
|
|
2226
2435
|
ids.add(step.id);
|
|
2227
2436
|
for (const { configKey, fieldName } of nestedKeys) {
|
|
2228
|
-
const
|
|
2229
|
-
if (
|
|
2230
|
-
for (const id of getAllStepIds(
|
|
2437
|
+
const config2 = step[configKey];
|
|
2438
|
+
if (config2 && Array.isArray(config2[fieldName])) {
|
|
2439
|
+
for (const id of getAllStepIds(config2[fieldName])) ids.add(id);
|
|
2231
2440
|
}
|
|
2232
2441
|
}
|
|
2233
2442
|
}
|
|
@@ -2256,7 +2465,7 @@ function validateSelectorReferences(flow2) {
|
|
|
2256
2465
|
}
|
|
2257
2466
|
return selectors;
|
|
2258
2467
|
}
|
|
2259
|
-
function checkSelectors(selectors,
|
|
2468
|
+
function checkSelectors(selectors, path6) {
|
|
2260
2469
|
for (const selector of selectors) {
|
|
2261
2470
|
const parts = selector.split(".");
|
|
2262
2471
|
if (parts.length < 3) continue;
|
|
@@ -2264,31 +2473,31 @@ function validateSelectorReferences(flow2) {
|
|
|
2264
2473
|
if (root === "input") {
|
|
2265
2474
|
const inputName = parts[2];
|
|
2266
2475
|
if (!inputNames.has(inputName)) {
|
|
2267
|
-
errors.push({ path:
|
|
2476
|
+
errors.push({ path: path6, message: `Selector "${selector}" references undefined input "${inputName}"` });
|
|
2268
2477
|
}
|
|
2269
2478
|
} else if (root === "steps") {
|
|
2270
2479
|
const stepId = parts[2];
|
|
2271
2480
|
if (!allStepIds.has(stepId)) {
|
|
2272
|
-
errors.push({ path:
|
|
2481
|
+
errors.push({ path: path6, message: `Selector "${selector}" references undefined step "${stepId}"` });
|
|
2273
2482
|
}
|
|
2274
2483
|
}
|
|
2275
2484
|
}
|
|
2276
2485
|
}
|
|
2277
2486
|
const EXPRESSION_FIELDS = /* @__PURE__ */ new Set(["condition.expression", "while.condition"]);
|
|
2278
|
-
function checkOperatorsInSelectorField(value,
|
|
2487
|
+
function checkOperatorsInSelectorField(value, path6) {
|
|
2279
2488
|
if (typeof value === "string" && value.startsWith("$.")) {
|
|
2280
2489
|
if (value.includes("||")) {
|
|
2281
|
-
errors.push({ path:
|
|
2490
|
+
errors.push({ path: path6, message: `Selector "${value}" contains unsupported operator "||". Selectors in data fields use dot-path resolution, not JS evaluation. Use the "default" field on the input definition instead, or use a "code" step for complex expressions.` });
|
|
2282
2491
|
} else if (value.includes("&&")) {
|
|
2283
|
-
errors.push({ path:
|
|
2492
|
+
errors.push({ path: path6, message: `Selector "${value}" contains unsupported operator "&&". Selectors in data fields use dot-path resolution, not JS evaluation. Use a "condition" step or "code" step for complex expressions.` });
|
|
2284
2493
|
}
|
|
2285
2494
|
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2286
2495
|
for (const [k, v] of Object.entries(value)) {
|
|
2287
|
-
checkOperatorsInSelectorField(v, `${
|
|
2496
|
+
checkOperatorsInSelectorField(v, `${path6}.${k}`);
|
|
2288
2497
|
}
|
|
2289
2498
|
} else if (Array.isArray(value)) {
|
|
2290
2499
|
for (let i = 0; i < value.length; i++) {
|
|
2291
|
-
checkOperatorsInSelectorField(value[i], `${
|
|
2500
|
+
checkOperatorsInSelectorField(value[i], `${path6}[${i}]`);
|
|
2292
2501
|
}
|
|
2293
2502
|
}
|
|
2294
2503
|
}
|
|
@@ -2297,12 +2506,12 @@ function validateSelectorReferences(flow2) {
|
|
|
2297
2506
|
if (step.unless) checkSelectors(extractSelectors(step.unless), `${pathPrefix}.unless`);
|
|
2298
2507
|
const descriptor = getStepTypeDescriptor(step.type);
|
|
2299
2508
|
if (descriptor) {
|
|
2300
|
-
const
|
|
2301
|
-
if (
|
|
2509
|
+
const config2 = step[descriptor.configKey];
|
|
2510
|
+
if (config2 && typeof config2 === "object") {
|
|
2302
2511
|
if (step.type !== "transform" && step.type !== "code") {
|
|
2303
2512
|
for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
|
|
2304
2513
|
if (fd.stepsArray) continue;
|
|
2305
|
-
const value =
|
|
2514
|
+
const value = config2[fieldName];
|
|
2306
2515
|
if (value !== void 0) {
|
|
2307
2516
|
const fieldKey = `${descriptor.configKey}.${fieldName}`;
|
|
2308
2517
|
const fieldPath = `${pathPrefix}.${fieldKey}`;
|
|
@@ -2340,7 +2549,7 @@ function validateFlow(flow2) {
|
|
|
2340
2549
|
}
|
|
2341
2550
|
|
|
2342
2551
|
// src/commands/flow.ts
|
|
2343
|
-
import
|
|
2552
|
+
import fs6 from "fs";
|
|
2344
2553
|
function getConfig2() {
|
|
2345
2554
|
const apiKey = getApiKey();
|
|
2346
2555
|
if (!apiKey) {
|
|
@@ -2398,7 +2607,7 @@ async function flowCreateCommand(key, options) {
|
|
|
2398
2607
|
if (raw.startsWith("@")) {
|
|
2399
2608
|
const filePath = raw.slice(1);
|
|
2400
2609
|
try {
|
|
2401
|
-
raw =
|
|
2610
|
+
raw = fs6.readFileSync(filePath, "utf-8");
|
|
2402
2611
|
} catch (err) {
|
|
2403
2612
|
error(`Cannot read file "${filePath}": ${err.message}`);
|
|
2404
2613
|
}
|
|
@@ -2605,7 +2814,7 @@ async function flowValidateCommand(keyOrPath) {
|
|
|
2605
2814
|
let flowData;
|
|
2606
2815
|
try {
|
|
2607
2816
|
const flowPath = resolveFlowPath(keyOrPath);
|
|
2608
|
-
const content =
|
|
2817
|
+
const content = fs6.readFileSync(flowPath, "utf-8");
|
|
2609
2818
|
flowData = JSON.parse(content);
|
|
2610
2819
|
} catch (err) {
|
|
2611
2820
|
spinner5.stop("Validation failed");
|
|
@@ -3363,11 +3572,11 @@ async function cacheUpdateAllCommand() {
|
|
|
3363
3572
|
if (e.type === "knowledge") {
|
|
3364
3573
|
const result = await api.getActionKnowledgeWithMeta(e.entry.key);
|
|
3365
3574
|
const newEntry = makeCacheEntry(e.entry.key, result.data, result.etag);
|
|
3366
|
-
|
|
3575
|
+
writeCache2(e.filePath, newEntry);
|
|
3367
3576
|
updated++;
|
|
3368
3577
|
} else {
|
|
3369
3578
|
const refreshed = { ...e.entry, cachedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3370
|
-
|
|
3579
|
+
writeCache2(e.filePath, refreshed);
|
|
3371
3580
|
updated++;
|
|
3372
3581
|
}
|
|
3373
3582
|
} catch (err) {
|
|
@@ -3495,6 +3704,7 @@ Request specific sections:
|
|
|
3495
3704
|
- Always use the **exact action ID** from search results \u2014 don't guess
|
|
3496
3705
|
- Always read **knowledge** before executing any action
|
|
3497
3706
|
- Connection keys come from \`one connection list\` \u2014 don't hardcode them
|
|
3707
|
+
- Skills stay in lockstep with the CLI version automatically \u2014 every command checks a \`.one-cli-version\` marker in the canonical skill dir and refreshes the files if the CLI has been upgraded. Check manually with \`one config skills status\`; force a resync with \`one config skills sync\`
|
|
3498
3708
|
`;
|
|
3499
3709
|
var GUIDE_ACTIONS = `# One Actions \u2014 Reference
|
|
3500
3710
|
|
|
@@ -4098,118 +4308,6 @@ function buildWorkflowIdeas(connections) {
|
|
|
4098
4308
|
return lines.join("\n");
|
|
4099
4309
|
}
|
|
4100
4310
|
|
|
4101
|
-
// src/commands/update.ts
|
|
4102
|
-
import { createRequire } from "module";
|
|
4103
|
-
import { spawn } from "child_process";
|
|
4104
|
-
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
4105
|
-
import { homedir } from "os";
|
|
4106
|
-
import { join } from "path";
|
|
4107
|
-
var require2 = createRequire(import.meta.url);
|
|
4108
|
-
var { version: currentVersion } = require2("../package.json");
|
|
4109
|
-
var CACHE_PATH = join(homedir(), ".one", "update-check.json");
|
|
4110
|
-
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
4111
|
-
var AGE_GATE_MS = 30 * 60 * 1e3;
|
|
4112
|
-
async function fetchLatestVersionInfo() {
|
|
4113
|
-
try {
|
|
4114
|
-
const res = await fetch("https://registry.npmjs.org/@withone/cli");
|
|
4115
|
-
if (!res.ok) return null;
|
|
4116
|
-
const data = await res.json();
|
|
4117
|
-
const latest = data["dist-tags"]?.latest;
|
|
4118
|
-
if (!latest) return null;
|
|
4119
|
-
return { version: latest, publishedAt: data.time?.[latest] ?? null };
|
|
4120
|
-
} catch {
|
|
4121
|
-
return null;
|
|
4122
|
-
}
|
|
4123
|
-
}
|
|
4124
|
-
function readCache3() {
|
|
4125
|
-
try {
|
|
4126
|
-
return JSON.parse(readFileSync(CACHE_PATH, "utf8"));
|
|
4127
|
-
} catch {
|
|
4128
|
-
return null;
|
|
4129
|
-
}
|
|
4130
|
-
}
|
|
4131
|
-
function writeCache2(latestVersion, publishedAt) {
|
|
4132
|
-
try {
|
|
4133
|
-
mkdirSync(join(homedir(), ".one"), { recursive: true });
|
|
4134
|
-
writeFileSync(CACHE_PATH, JSON.stringify({ lastCheck: Date.now(), latestVersion, publishedAt }));
|
|
4135
|
-
} catch {
|
|
4136
|
-
}
|
|
4137
|
-
}
|
|
4138
|
-
async function checkLatestVersion() {
|
|
4139
|
-
const info = await fetchLatestVersionInfo();
|
|
4140
|
-
if (info) writeCache2(info.version, info.publishedAt);
|
|
4141
|
-
return info?.version ?? null;
|
|
4142
|
-
}
|
|
4143
|
-
async function checkLatestVersionCached() {
|
|
4144
|
-
const cache2 = readCache3();
|
|
4145
|
-
if (cache2 && Date.now() - cache2.lastCheck < CHECK_INTERVAL_MS) {
|
|
4146
|
-
return { version: cache2.latestVersion, publishedAt: cache2.publishedAt ?? null };
|
|
4147
|
-
}
|
|
4148
|
-
const info = await fetchLatestVersionInfo();
|
|
4149
|
-
if (info) writeCache2(info.version, info.publishedAt);
|
|
4150
|
-
return info;
|
|
4151
|
-
}
|
|
4152
|
-
function getCurrentVersion() {
|
|
4153
|
-
return currentVersion;
|
|
4154
|
-
}
|
|
4155
|
-
async function updateCommand() {
|
|
4156
|
-
const s = createSpinner();
|
|
4157
|
-
s.start("Checking for updates...");
|
|
4158
|
-
const latestVersion = await checkLatestVersion();
|
|
4159
|
-
if (!latestVersion) {
|
|
4160
|
-
s.stop("");
|
|
4161
|
-
error("Failed to check for updates \u2014 could not reach npm registry");
|
|
4162
|
-
}
|
|
4163
|
-
if (currentVersion === latestVersion) {
|
|
4164
|
-
s.stop("Already up to date");
|
|
4165
|
-
if (isAgentMode()) {
|
|
4166
|
-
json({ current: currentVersion, latest: latestVersion, updated: false, message: "Already up to date" });
|
|
4167
|
-
} else {
|
|
4168
|
-
console.log(`Already up to date (v${currentVersion})`);
|
|
4169
|
-
}
|
|
4170
|
-
return;
|
|
4171
|
-
}
|
|
4172
|
-
s.stop(`Update available: v${currentVersion} \u2192 v${latestVersion}`);
|
|
4173
|
-
console.log(`Updating @withone/cli: v${currentVersion} \u2192 v${latestVersion}...`);
|
|
4174
|
-
const code = await new Promise((resolve) => {
|
|
4175
|
-
const child = spawn("npm", ["install", "-g", "@withone/cli@latest", "--force"], {
|
|
4176
|
-
stdio: isAgentMode() ? "pipe" : "inherit",
|
|
4177
|
-
shell: true
|
|
4178
|
-
});
|
|
4179
|
-
child.on("close", resolve);
|
|
4180
|
-
child.on("error", () => resolve(1));
|
|
4181
|
-
});
|
|
4182
|
-
if (code === 0) {
|
|
4183
|
-
if (isAgentMode()) {
|
|
4184
|
-
json({ current: currentVersion, latest: latestVersion, updated: true, message: "Updated successfully" });
|
|
4185
|
-
} else {
|
|
4186
|
-
console.log(`Successfully updated to v${latestVersion}`);
|
|
4187
|
-
}
|
|
4188
|
-
} else {
|
|
4189
|
-
error("Update failed \u2014 try running: npm install -g @withone/cli@latest");
|
|
4190
|
-
}
|
|
4191
|
-
}
|
|
4192
|
-
function isNewerVersion(latest, current) {
|
|
4193
|
-
const parse = (v) => v.split(".").map(Number);
|
|
4194
|
-
const [lMaj, lMin, lPat] = parse(latest);
|
|
4195
|
-
const [cMaj, cMin, cPat] = parse(current);
|
|
4196
|
-
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
4197
|
-
if (lMin !== cMin) return lMin > cMin;
|
|
4198
|
-
return lPat > cPat;
|
|
4199
|
-
}
|
|
4200
|
-
function autoUpdate(targetVersion, publishedAt) {
|
|
4201
|
-
if (publishedAt) {
|
|
4202
|
-
const age = Date.now() - new Date(publishedAt).getTime();
|
|
4203
|
-
if (age < AGE_GATE_MS) return;
|
|
4204
|
-
}
|
|
4205
|
-
const child = spawn("npm", ["install", "-g", `@withone/cli@${targetVersion}`], {
|
|
4206
|
-
detached: true,
|
|
4207
|
-
stdio: "ignore",
|
|
4208
|
-
shell: true
|
|
4209
|
-
});
|
|
4210
|
-
child.unref();
|
|
4211
|
-
}
|
|
4212
|
-
|
|
4213
4311
|
// src/index.ts
|
|
4214
4312
|
var require3 = createRequire2(import.meta.url);
|
|
4215
4313
|
var { version } = require3("../package.json");
|
|
@@ -4275,6 +4373,12 @@ program.hook("preAction", (thisCommand) => {
|
|
|
4275
4373
|
if (commandName !== "update") {
|
|
4276
4374
|
updateCheckPromise = checkLatestVersionCached();
|
|
4277
4375
|
}
|
|
4376
|
+
if (commandName !== "init" && commandName !== "update") {
|
|
4377
|
+
try {
|
|
4378
|
+
syncSkillsIfStale();
|
|
4379
|
+
} catch {
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4278
4382
|
});
|
|
4279
4383
|
program.hook("postAction", async () => {
|
|
4280
4384
|
if (!updateCheckPromise) return;
|
|
@@ -4287,9 +4391,48 @@ program.hook("postAction", async () => {
|
|
|
4287
4391
|
program.command("init").description("Set up One and install MCP to your AI agents").option("-y, --yes", "Skip confirmations").option("-g, --global", "Install MCP globally (available in all projects)").option("-p, --project", "Install MCP for this project only (creates .mcp.json)").action(async (options) => {
|
|
4288
4392
|
await initCommand(options);
|
|
4289
4393
|
});
|
|
4290
|
-
program.command("config").description("Configure
|
|
4394
|
+
var config = program.command("config").description("Configure the CLI (access control, skills, ...)").action(async () => {
|
|
4291
4395
|
await configCommand();
|
|
4292
4396
|
});
|
|
4397
|
+
var configSkills = config.command("skills").description("Manage locally-installed skill files");
|
|
4398
|
+
configSkills.command("sync").description("Re-copy packaged skill files over the local install (runs automatically after CLI upgrades)").action(async () => {
|
|
4399
|
+
const result = forceSyncSkills();
|
|
4400
|
+
if (isAgentMode()) {
|
|
4401
|
+
json({ command: "config skills sync", ...result });
|
|
4402
|
+
return;
|
|
4403
|
+
}
|
|
4404
|
+
if (result.reason === "not-installed") {
|
|
4405
|
+
console.log("No skill is installed yet. Run 'one init' first and opt in to skill installation.");
|
|
4406
|
+
return;
|
|
4407
|
+
}
|
|
4408
|
+
if (result.synced) {
|
|
4409
|
+
console.log(`\u2713 Skills synced to v${result.to}`);
|
|
4410
|
+
return;
|
|
4411
|
+
}
|
|
4412
|
+
if (result.reason === "source-missing") {
|
|
4413
|
+
console.log("\u2717 Packaged skill source not found in this CLI build");
|
|
4414
|
+
return;
|
|
4415
|
+
}
|
|
4416
|
+
console.log(`\u2717 Sync failed${result.error ? ": " + result.error : ""}`);
|
|
4417
|
+
});
|
|
4418
|
+
configSkills.command("status").description("Show installed skill version and whether it matches the current CLI").action(async () => {
|
|
4419
|
+
const status = getSkillStatus();
|
|
4420
|
+
if (isAgentMode()) {
|
|
4421
|
+
json({ command: "config skills status", ...status });
|
|
4422
|
+
return;
|
|
4423
|
+
}
|
|
4424
|
+
if (!status.installed) {
|
|
4425
|
+
console.log("Skill is not installed. Run 'one init' to install it.");
|
|
4426
|
+
console.log(`Canonical path (empty): ${status.canonicalPath}`);
|
|
4427
|
+
return;
|
|
4428
|
+
}
|
|
4429
|
+
const marker = status.installedVersion ?? "(no marker \u2014 pre-sync install)";
|
|
4430
|
+
const state = status.upToDate ? "\u2713 up to date" : "\u26A0 stale \u2014 will sync on next command";
|
|
4431
|
+
console.log(`Skill: ${state}`);
|
|
4432
|
+
console.log(` installed: ${marker}`);
|
|
4433
|
+
console.log(` current: ${status.currentVersion}`);
|
|
4434
|
+
console.log(` path: ${status.canonicalPath}`);
|
|
4435
|
+
});
|
|
4293
4436
|
var connection = program.command("connection").description("Manage connections");
|
|
4294
4437
|
connection.command("add [platform]").alias("a").description("Add a new connection").action(async (platform) => {
|
|
4295
4438
|
await connectionAddCommand(platform);
|