@withone/cli 1.21.0 → 1.23.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/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
loadFlowWithMeta,
|
|
17
17
|
resolveFlowPath,
|
|
18
18
|
saveFlow
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-Z45IBT5P.js";
|
|
20
20
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
import { createRequire as createRequire2 } from "module";
|
|
@@ -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) {
|
|
@@ -2034,6 +2243,9 @@ function colorMethod(method) {
|
|
|
2034
2243
|
import pc7 from "picocolors";
|
|
2035
2244
|
|
|
2036
2245
|
// src/lib/flow-validator.ts
|
|
2246
|
+
import fs6 from "fs";
|
|
2247
|
+
import path6 from "path";
|
|
2248
|
+
import { spawnSync } from "child_process";
|
|
2037
2249
|
function validateFlowSchema(flow2) {
|
|
2038
2250
|
const errors = [];
|
|
2039
2251
|
if (!flow2 || typeof flow2 !== "object") {
|
|
@@ -2092,26 +2304,26 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2092
2304
|
const validTypes = FLOW_SCHEMA.stepTypes.map((st) => st.type);
|
|
2093
2305
|
for (let i = 0; i < steps.length; i++) {
|
|
2094
2306
|
const step = steps[i];
|
|
2095
|
-
const
|
|
2307
|
+
const path8 = `${pathPrefix}[${i}]`;
|
|
2096
2308
|
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
2097
|
-
errors.push({ path:
|
|
2309
|
+
errors.push({ path: path8, message: "Step must be an object" });
|
|
2098
2310
|
continue;
|
|
2099
2311
|
}
|
|
2100
2312
|
const s = step;
|
|
2101
2313
|
if (!s.id || typeof s.id !== "string") {
|
|
2102
|
-
errors.push({ path: `${
|
|
2314
|
+
errors.push({ path: `${path8}.id`, message: 'Step must have a string "id"' });
|
|
2103
2315
|
}
|
|
2104
2316
|
if (!s.name || typeof s.name !== "string") {
|
|
2105
|
-
errors.push({ path: `${
|
|
2317
|
+
errors.push({ path: `${path8}.name`, message: 'Step must have a string "name"' });
|
|
2106
2318
|
}
|
|
2107
2319
|
if (!s.type || !validTypes.includes(s.type)) {
|
|
2108
|
-
errors.push({ path: `${
|
|
2320
|
+
errors.push({ path: `${path8}.type`, message: `Step type must be one of: ${validTypes.join(", ")}` });
|
|
2109
2321
|
continue;
|
|
2110
2322
|
}
|
|
2111
2323
|
if (s.onError && typeof s.onError === "object") {
|
|
2112
2324
|
const oe = s.onError;
|
|
2113
2325
|
if (!FLOW_SCHEMA.errorStrategies.includes(oe.strategy)) {
|
|
2114
|
-
errors.push({ path: `${
|
|
2326
|
+
errors.push({ path: `${path8}.onError.strategy`, message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
|
|
2115
2327
|
}
|
|
2116
2328
|
}
|
|
2117
2329
|
const descriptor = getStepTypeDescriptor(s.type);
|
|
@@ -2121,15 +2333,15 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2121
2333
|
if (!configObj || typeof configObj !== "object") {
|
|
2122
2334
|
const hint = detectFlatConfigHint(s, descriptor);
|
|
2123
2335
|
errors.push({
|
|
2124
|
-
path: `${
|
|
2336
|
+
path: `${path8}.${configKey}`,
|
|
2125
2337
|
message: `${capitalize(descriptor.type)} step must have a "${configKey}" config object${hint}`
|
|
2126
2338
|
});
|
|
2127
2339
|
continue;
|
|
2128
2340
|
}
|
|
2129
|
-
const
|
|
2341
|
+
const config2 = configObj;
|
|
2130
2342
|
for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
|
|
2131
|
-
const fieldPath = `${
|
|
2132
|
-
const value =
|
|
2343
|
+
const fieldPath = `${path8}.${configKey}.${fieldName}`;
|
|
2344
|
+
const value = config2[fieldName];
|
|
2133
2345
|
if (fd.required && (value === void 0 || value === null || value === "")) {
|
|
2134
2346
|
errors.push({ path: fieldPath, message: `${capitalize(descriptor.type)} must have ${fd.type === "string" ? "a string" : fd.type === "array" ? "a" : "a"} "${fieldName}"` });
|
|
2135
2347
|
continue;
|
|
@@ -2161,21 +2373,27 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
2161
2373
|
}
|
|
2162
2374
|
}
|
|
2163
2375
|
if (descriptor.type === "code") {
|
|
2164
|
-
const hasSource = typeof
|
|
2165
|
-
const hasModule = typeof
|
|
2376
|
+
const hasSource = typeof config2.source === "string" && config2.source.length > 0;
|
|
2377
|
+
const hasModule = typeof config2.module === "string" && config2.module.length > 0;
|
|
2166
2378
|
if (!hasSource && !hasModule) {
|
|
2167
|
-
errors.push({ path: `${
|
|
2379
|
+
errors.push({ path: `${path8}.${configKey}`, message: 'Code step must define either "source" (inline JS) or "module" (path to .mjs file)' });
|
|
2168
2380
|
} else if (hasSource && hasModule) {
|
|
2169
|
-
errors.push({ path: `${
|
|
2381
|
+
errors.push({ path: `${path8}.${configKey}`, message: 'Code step cannot define both "source" and "module" \u2014 pick one' });
|
|
2170
2382
|
}
|
|
2171
2383
|
if (hasModule) {
|
|
2172
|
-
const m =
|
|
2384
|
+
const m = config2.module;
|
|
2173
2385
|
if (m.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(m)) {
|
|
2174
|
-
errors.push({ path: `${
|
|
2386
|
+
errors.push({ path: `${path8}.${configKey}.module`, message: "Code module path must be relative to the flow folder (no absolute paths)" });
|
|
2175
2387
|
} else if (m.split(/[\\/]/).includes("..")) {
|
|
2176
|
-
errors.push({ path: `${
|
|
2388
|
+
errors.push({ path: `${path8}.${configKey}.module`, message: 'Code module path must not escape the flow folder ("..")' });
|
|
2177
2389
|
} else if (!m.endsWith(".mjs")) {
|
|
2178
|
-
errors.push({ path: `${
|
|
2390
|
+
errors.push({ path: `${path8}.${configKey}.module`, message: "Code module must be a .mjs file" });
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
if (hasSource) {
|
|
2394
|
+
const syntaxError = checkCodeSourceSyntax(config2.source);
|
|
2395
|
+
if (syntaxError) {
|
|
2396
|
+
errors.push({ path: `${path8}.${configKey}.source`, message: `Syntax error in code step: ${syntaxError}` });
|
|
2179
2397
|
}
|
|
2180
2398
|
}
|
|
2181
2399
|
}
|
|
@@ -2192,6 +2410,17 @@ function detectFlatConfigHint(step, descriptor) {
|
|
|
2192
2410
|
function capitalize(s) {
|
|
2193
2411
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
2194
2412
|
}
|
|
2413
|
+
var AsyncFunctionCtor = Object.getPrototypeOf(async function() {
|
|
2414
|
+
}).constructor;
|
|
2415
|
+
function checkCodeSourceSyntax(source) {
|
|
2416
|
+
try {
|
|
2417
|
+
new AsyncFunctionCtor("$", "require", source);
|
|
2418
|
+
return null;
|
|
2419
|
+
} catch (err) {
|
|
2420
|
+
if (err instanceof SyntaxError) return err.message;
|
|
2421
|
+
return err.message;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2195
2424
|
function validateStepIds(flow2) {
|
|
2196
2425
|
const errors = [];
|
|
2197
2426
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2199,16 +2428,16 @@ function validateStepIds(flow2) {
|
|
|
2199
2428
|
function collectIds(steps, pathPrefix) {
|
|
2200
2429
|
for (let i = 0; i < steps.length; i++) {
|
|
2201
2430
|
const step = steps[i];
|
|
2202
|
-
const
|
|
2431
|
+
const path8 = `${pathPrefix}[${i}]`;
|
|
2203
2432
|
if (seen.has(step.id)) {
|
|
2204
|
-
errors.push({ path: `${
|
|
2433
|
+
errors.push({ path: `${path8}.id`, message: `Duplicate step ID: "${step.id}"` });
|
|
2205
2434
|
} else {
|
|
2206
2435
|
seen.add(step.id);
|
|
2207
2436
|
}
|
|
2208
2437
|
for (const { configKey, fieldName } of nestedKeys) {
|
|
2209
|
-
const
|
|
2210
|
-
if (
|
|
2211
|
-
collectIds(
|
|
2438
|
+
const config2 = step[configKey];
|
|
2439
|
+
if (config2 && Array.isArray(config2[fieldName])) {
|
|
2440
|
+
collectIds(config2[fieldName], `${path8}.${configKey}.${fieldName}`);
|
|
2212
2441
|
}
|
|
2213
2442
|
}
|
|
2214
2443
|
}
|
|
@@ -2225,9 +2454,9 @@ function validateSelectorReferences(flow2) {
|
|
|
2225
2454
|
for (const step of steps) {
|
|
2226
2455
|
ids.add(step.id);
|
|
2227
2456
|
for (const { configKey, fieldName } of nestedKeys) {
|
|
2228
|
-
const
|
|
2229
|
-
if (
|
|
2230
|
-
for (const id of getAllStepIds(
|
|
2457
|
+
const config2 = step[configKey];
|
|
2458
|
+
if (config2 && Array.isArray(config2[fieldName])) {
|
|
2459
|
+
for (const id of getAllStepIds(config2[fieldName])) ids.add(id);
|
|
2231
2460
|
}
|
|
2232
2461
|
}
|
|
2233
2462
|
}
|
|
@@ -2256,7 +2485,7 @@ function validateSelectorReferences(flow2) {
|
|
|
2256
2485
|
}
|
|
2257
2486
|
return selectors;
|
|
2258
2487
|
}
|
|
2259
|
-
function checkSelectors(selectors,
|
|
2488
|
+
function checkSelectors(selectors, path8, precedingStepIds) {
|
|
2260
2489
|
for (const selector of selectors) {
|
|
2261
2490
|
const parts = selector.split(".");
|
|
2262
2491
|
if (parts.length < 3) continue;
|
|
@@ -2264,83 +2493,142 @@ function validateSelectorReferences(flow2) {
|
|
|
2264
2493
|
if (root === "input") {
|
|
2265
2494
|
const inputName = parts[2];
|
|
2266
2495
|
if (!inputNames.has(inputName)) {
|
|
2267
|
-
errors.push({ path:
|
|
2496
|
+
errors.push({ path: path8, message: `Selector "${selector}" references undefined input "${inputName}"` });
|
|
2268
2497
|
}
|
|
2269
2498
|
} else if (root === "steps") {
|
|
2270
|
-
const stepId = parts[2];
|
|
2499
|
+
const stepId = parts[2].replace(/[\[\]]/g, "").split(/[\[\]]/)[0];
|
|
2271
2500
|
if (!allStepIds.has(stepId)) {
|
|
2272
|
-
errors.push({ path:
|
|
2501
|
+
errors.push({ path: path8, message: `Selector "${selector}" references undefined step "${stepId}"` });
|
|
2502
|
+
} else if (precedingStepIds && !precedingStepIds.has(stepId)) {
|
|
2503
|
+
errors.push({
|
|
2504
|
+
path: path8,
|
|
2505
|
+
message: `Selector "${selector}" references step "${stepId}" which is declared after the current step. Steps execute in declaration order, so this will always resolve to undefined at runtime \u2014 move the dependency earlier in the steps array.`
|
|
2506
|
+
});
|
|
2273
2507
|
}
|
|
2274
2508
|
}
|
|
2275
2509
|
}
|
|
2276
2510
|
}
|
|
2277
2511
|
const EXPRESSION_FIELDS = /* @__PURE__ */ new Set(["condition.expression", "while.condition"]);
|
|
2278
|
-
function checkOperatorsInSelectorField(value,
|
|
2512
|
+
function checkOperatorsInSelectorField(value, path8) {
|
|
2279
2513
|
if (typeof value === "string" && value.startsWith("$.")) {
|
|
2280
2514
|
if (value.includes("||")) {
|
|
2281
|
-
errors.push({ path:
|
|
2515
|
+
errors.push({ path: path8, 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
2516
|
} else if (value.includes("&&")) {
|
|
2283
|
-
errors.push({ path:
|
|
2517
|
+
errors.push({ path: path8, 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
2518
|
}
|
|
2285
2519
|
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2286
2520
|
for (const [k, v] of Object.entries(value)) {
|
|
2287
|
-
checkOperatorsInSelectorField(v, `${
|
|
2521
|
+
checkOperatorsInSelectorField(v, `${path8}.${k}`);
|
|
2288
2522
|
}
|
|
2289
2523
|
} else if (Array.isArray(value)) {
|
|
2290
2524
|
for (let i = 0; i < value.length; i++) {
|
|
2291
|
-
checkOperatorsInSelectorField(value[i], `${
|
|
2525
|
+
checkOperatorsInSelectorField(value[i], `${path8}[${i}]`);
|
|
2292
2526
|
}
|
|
2293
2527
|
}
|
|
2294
2528
|
}
|
|
2295
|
-
function checkStep(step, pathPrefix) {
|
|
2296
|
-
if (step.if) checkSelectors(extractSelectors(step.if), `${pathPrefix}.if
|
|
2297
|
-
if (step.unless) checkSelectors(extractSelectors(step.unless), `${pathPrefix}.unless
|
|
2529
|
+
function checkStep(step, pathPrefix, preceding2) {
|
|
2530
|
+
if (step.if) checkSelectors(extractSelectors(step.if), `${pathPrefix}.if`, preceding2);
|
|
2531
|
+
if (step.unless) checkSelectors(extractSelectors(step.unless), `${pathPrefix}.unless`, preceding2);
|
|
2298
2532
|
const descriptor = getStepTypeDescriptor(step.type);
|
|
2299
2533
|
if (descriptor) {
|
|
2300
|
-
const
|
|
2301
|
-
if (
|
|
2534
|
+
const config2 = step[descriptor.configKey];
|
|
2535
|
+
if (config2 && typeof config2 === "object") {
|
|
2302
2536
|
if (step.type !== "transform" && step.type !== "code") {
|
|
2303
2537
|
for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
|
|
2304
2538
|
if (fd.stepsArray) continue;
|
|
2305
|
-
const value =
|
|
2539
|
+
const value = config2[fieldName];
|
|
2306
2540
|
if (value !== void 0) {
|
|
2307
2541
|
const fieldKey = `${descriptor.configKey}.${fieldName}`;
|
|
2308
2542
|
const fieldPath = `${pathPrefix}.${fieldKey}`;
|
|
2309
|
-
checkSelectors(extractSelectors(value), fieldPath);
|
|
2543
|
+
checkSelectors(extractSelectors(value), fieldPath, preceding2);
|
|
2310
2544
|
if (!EXPRESSION_FIELDS.has(fieldKey)) {
|
|
2311
2545
|
checkOperatorsInSelectorField(value, fieldPath);
|
|
2312
2546
|
}
|
|
2313
2547
|
}
|
|
2314
2548
|
}
|
|
2549
|
+
} else {
|
|
2550
|
+
const c = config2;
|
|
2551
|
+
const codeSource = step.type === "code" ? c.source : void 0;
|
|
2552
|
+
const transformExpr = step.type === "transform" ? c.expression : void 0;
|
|
2553
|
+
const text4 = codeSource ?? transformExpr;
|
|
2554
|
+
if (typeof text4 === "string") {
|
|
2555
|
+
const fieldName = step.type === "code" ? "source" : "expression";
|
|
2556
|
+
checkSelectors(extractSelectors(text4), `${pathPrefix}.${descriptor.configKey}.${fieldName}`, preceding2);
|
|
2557
|
+
}
|
|
2315
2558
|
}
|
|
2316
2559
|
}
|
|
2317
2560
|
for (const { configKey, fieldName } of nestedKeys) {
|
|
2318
2561
|
if (configKey === descriptor.configKey) {
|
|
2319
2562
|
const c = step[configKey];
|
|
2320
2563
|
if (c && Array.isArray(c[fieldName])) {
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2564
|
+
const childPreceding = new Set(preceding2);
|
|
2565
|
+
c[fieldName].forEach((s, i) => {
|
|
2566
|
+
checkStep(s, `${pathPrefix}.${configKey}.${fieldName}[${i}]`, childPreceding);
|
|
2567
|
+
childPreceding.add(s.id);
|
|
2568
|
+
});
|
|
2324
2569
|
}
|
|
2325
2570
|
}
|
|
2326
2571
|
}
|
|
2327
2572
|
}
|
|
2328
2573
|
}
|
|
2329
|
-
|
|
2574
|
+
const preceding = /* @__PURE__ */ new Set();
|
|
2575
|
+
flow2.steps.forEach((step, i) => {
|
|
2576
|
+
checkStep(step, `steps[${i}]`, preceding);
|
|
2577
|
+
preceding.add(step.id);
|
|
2578
|
+
});
|
|
2330
2579
|
return errors;
|
|
2331
2580
|
}
|
|
2332
|
-
function validateFlow(flow2) {
|
|
2581
|
+
function validateFlow(flow2, rootDir) {
|
|
2333
2582
|
const schemaErrors = validateFlowSchema(flow2);
|
|
2334
2583
|
if (schemaErrors.length > 0) return schemaErrors;
|
|
2335
2584
|
const f = flow2;
|
|
2336
2585
|
return [
|
|
2337
2586
|
...validateStepIds(f),
|
|
2338
|
-
...validateSelectorReferences(f)
|
|
2587
|
+
...validateSelectorReferences(f),
|
|
2588
|
+
...rootDir ? validateCodeModules(f, rootDir) : []
|
|
2339
2589
|
];
|
|
2340
2590
|
}
|
|
2591
|
+
function validateCodeModules(flow2, rootDir) {
|
|
2592
|
+
const errors = [];
|
|
2593
|
+
const nestedKeys = getNestedStepsKeys();
|
|
2594
|
+
function walk(steps, pathPrefix) {
|
|
2595
|
+
for (let i = 0; i < steps.length; i++) {
|
|
2596
|
+
const step = steps[i];
|
|
2597
|
+
const stepPath = `${pathPrefix}[${i}]`;
|
|
2598
|
+
if (step.type === "code" && step.code?.module) {
|
|
2599
|
+
const m = step.code.module;
|
|
2600
|
+
const abs = path6.resolve(rootDir, m);
|
|
2601
|
+
if (!fs6.existsSync(abs)) {
|
|
2602
|
+
errors.push({
|
|
2603
|
+
path: `${stepPath}.code.module`,
|
|
2604
|
+
message: `Code module "${m}" not found at ${abs}`
|
|
2605
|
+
});
|
|
2606
|
+
} else {
|
|
2607
|
+
const res = spawnSync(process.execPath, ["--check", abs], { encoding: "utf-8" });
|
|
2608
|
+
if (res.status !== 0) {
|
|
2609
|
+
const msg = (res.stderr || "").split("\n").find((l) => l.includes("SyntaxError") || l.includes("Error")) || res.stderr || "Syntax check failed";
|
|
2610
|
+
errors.push({
|
|
2611
|
+
path: `${stepPath}.code.module`,
|
|
2612
|
+
message: `Syntax error in code module "${m}": ${msg.trim()}`
|
|
2613
|
+
});
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
for (const { configKey, fieldName } of nestedKeys) {
|
|
2618
|
+
const config2 = step[configKey];
|
|
2619
|
+
if (config2 && Array.isArray(config2[fieldName])) {
|
|
2620
|
+
walk(config2[fieldName], `${stepPath}.${configKey}.${fieldName}`);
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
walk(flow2.steps, "steps");
|
|
2626
|
+
return errors;
|
|
2627
|
+
}
|
|
2341
2628
|
|
|
2342
2629
|
// src/commands/flow.ts
|
|
2343
|
-
import
|
|
2630
|
+
import fs7 from "fs";
|
|
2631
|
+
import path7 from "path";
|
|
2344
2632
|
function getConfig2() {
|
|
2345
2633
|
const apiKey = getApiKey();
|
|
2346
2634
|
if (!apiKey) {
|
|
@@ -2398,7 +2686,7 @@ async function flowCreateCommand(key, options) {
|
|
|
2398
2686
|
if (raw.startsWith("@")) {
|
|
2399
2687
|
const filePath = raw.slice(1);
|
|
2400
2688
|
try {
|
|
2401
|
-
raw =
|
|
2689
|
+
raw = fs7.readFileSync(filePath, "utf-8");
|
|
2402
2690
|
} catch (err) {
|
|
2403
2691
|
error(`Cannot read file "${filePath}": ${err.message}`);
|
|
2404
2692
|
}
|
|
@@ -2463,6 +2751,15 @@ async function flowExecuteCommand(keyOrPath, options) {
|
|
|
2463
2751
|
return;
|
|
2464
2752
|
}
|
|
2465
2753
|
spinner5.stop(`Workflow: ${flow2.name} (${flow2.steps.length} steps)`);
|
|
2754
|
+
const preflightErrors = validateFlow(flow2, rootDir);
|
|
2755
|
+
if (preflightErrors.length > 0) {
|
|
2756
|
+
if (isAgentMode()) {
|
|
2757
|
+
json({ error: "Validation failed", errors: preflightErrors });
|
|
2758
|
+
process.exit(1);
|
|
2759
|
+
}
|
|
2760
|
+
error(`Validation failed:
|
|
2761
|
+
${preflightErrors.map((e) => ` ${e.path}: ${e.message}`).join("\n")}`);
|
|
2762
|
+
}
|
|
2466
2763
|
if (flowFilePath.endsWith(".flow.json")) {
|
|
2467
2764
|
const msg = `Workflow "${flow2.key}" uses the deprecated single-file layout. Migrate to .one/flows/${flow2.key}/flow.json (see: one guide flows).`;
|
|
2468
2765
|
if (isAgentMode()) {
|
|
@@ -2603,15 +2900,23 @@ async function flowValidateCommand(keyOrPath) {
|
|
|
2603
2900
|
const spinner5 = createSpinner();
|
|
2604
2901
|
spinner5.start(`Validating "${keyOrPath}"...`);
|
|
2605
2902
|
let flowData;
|
|
2903
|
+
let rootDir;
|
|
2606
2904
|
try {
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2905
|
+
try {
|
|
2906
|
+
const loaded = loadFlowWithMeta(keyOrPath);
|
|
2907
|
+
flowData = loaded.flow;
|
|
2908
|
+
rootDir = loaded.rootDir;
|
|
2909
|
+
} catch {
|
|
2910
|
+
const flowPath = resolveFlowPath(keyOrPath);
|
|
2911
|
+
const content = fs7.readFileSync(flowPath, "utf-8");
|
|
2912
|
+
flowData = JSON.parse(content);
|
|
2913
|
+
rootDir = path7.dirname(flowPath);
|
|
2914
|
+
}
|
|
2610
2915
|
} catch (err) {
|
|
2611
2916
|
spinner5.stop("Validation failed");
|
|
2612
2917
|
error(`Could not read workflow: ${err instanceof Error ? err.message : String(err)}`);
|
|
2613
2918
|
}
|
|
2614
|
-
const errors = validateFlow(flowData);
|
|
2919
|
+
const errors = validateFlow(flowData, rootDir);
|
|
2615
2920
|
if (errors.length > 0) {
|
|
2616
2921
|
spinner5.stop("Validation failed");
|
|
2617
2922
|
if (isAgentMode()) {
|
|
@@ -3363,11 +3668,11 @@ async function cacheUpdateAllCommand() {
|
|
|
3363
3668
|
if (e.type === "knowledge") {
|
|
3364
3669
|
const result = await api.getActionKnowledgeWithMeta(e.entry.key);
|
|
3365
3670
|
const newEntry = makeCacheEntry(e.entry.key, result.data, result.etag);
|
|
3366
|
-
|
|
3671
|
+
writeCache2(e.filePath, newEntry);
|
|
3367
3672
|
updated++;
|
|
3368
3673
|
} else {
|
|
3369
3674
|
const refreshed = { ...e.entry, cachedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3370
|
-
|
|
3675
|
+
writeCache2(e.filePath, refreshed);
|
|
3371
3676
|
updated++;
|
|
3372
3677
|
}
|
|
3373
3678
|
} catch (err) {
|
|
@@ -3495,6 +3800,7 @@ Request specific sections:
|
|
|
3495
3800
|
- Always use the **exact action ID** from search results \u2014 don't guess
|
|
3496
3801
|
- Always read **knowledge** before executing any action
|
|
3497
3802
|
- Connection keys come from \`one connection list\` \u2014 don't hardcode them
|
|
3803
|
+
- 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
3804
|
`;
|
|
3499
3805
|
var GUIDE_ACTIONS = `# One Actions \u2014 Reference
|
|
3500
3806
|
|
|
@@ -4098,118 +4404,6 @@ function buildWorkflowIdeas(connections) {
|
|
|
4098
4404
|
return lines.join("\n");
|
|
4099
4405
|
}
|
|
4100
4406
|
|
|
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
4407
|
// src/index.ts
|
|
4214
4408
|
var require3 = createRequire2(import.meta.url);
|
|
4215
4409
|
var { version } = require3("../package.json");
|
|
@@ -4275,6 +4469,12 @@ program.hook("preAction", (thisCommand) => {
|
|
|
4275
4469
|
if (commandName !== "update") {
|
|
4276
4470
|
updateCheckPromise = checkLatestVersionCached();
|
|
4277
4471
|
}
|
|
4472
|
+
if (commandName !== "init" && commandName !== "update") {
|
|
4473
|
+
try {
|
|
4474
|
+
syncSkillsIfStale();
|
|
4475
|
+
} catch {
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4278
4478
|
});
|
|
4279
4479
|
program.hook("postAction", async () => {
|
|
4280
4480
|
if (!updateCheckPromise) return;
|
|
@@ -4287,9 +4487,48 @@ program.hook("postAction", async () => {
|
|
|
4287
4487
|
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
4488
|
await initCommand(options);
|
|
4289
4489
|
});
|
|
4290
|
-
program.command("config").description("Configure
|
|
4490
|
+
var config = program.command("config").description("Configure the CLI (access control, skills, ...)").action(async () => {
|
|
4291
4491
|
await configCommand();
|
|
4292
4492
|
});
|
|
4493
|
+
var configSkills = config.command("skills").description("Manage locally-installed skill files");
|
|
4494
|
+
configSkills.command("sync").description("Re-copy packaged skill files over the local install (runs automatically after CLI upgrades)").action(async () => {
|
|
4495
|
+
const result = forceSyncSkills();
|
|
4496
|
+
if (isAgentMode()) {
|
|
4497
|
+
json({ command: "config skills sync", ...result });
|
|
4498
|
+
return;
|
|
4499
|
+
}
|
|
4500
|
+
if (result.reason === "not-installed") {
|
|
4501
|
+
console.log("No skill is installed yet. Run 'one init' first and opt in to skill installation.");
|
|
4502
|
+
return;
|
|
4503
|
+
}
|
|
4504
|
+
if (result.synced) {
|
|
4505
|
+
console.log(`\u2713 Skills synced to v${result.to}`);
|
|
4506
|
+
return;
|
|
4507
|
+
}
|
|
4508
|
+
if (result.reason === "source-missing") {
|
|
4509
|
+
console.log("\u2717 Packaged skill source not found in this CLI build");
|
|
4510
|
+
return;
|
|
4511
|
+
}
|
|
4512
|
+
console.log(`\u2717 Sync failed${result.error ? ": " + result.error : ""}`);
|
|
4513
|
+
});
|
|
4514
|
+
configSkills.command("status").description("Show installed skill version and whether it matches the current CLI").action(async () => {
|
|
4515
|
+
const status = getSkillStatus();
|
|
4516
|
+
if (isAgentMode()) {
|
|
4517
|
+
json({ command: "config skills status", ...status });
|
|
4518
|
+
return;
|
|
4519
|
+
}
|
|
4520
|
+
if (!status.installed) {
|
|
4521
|
+
console.log("Skill is not installed. Run 'one init' to install it.");
|
|
4522
|
+
console.log(`Canonical path (empty): ${status.canonicalPath}`);
|
|
4523
|
+
return;
|
|
4524
|
+
}
|
|
4525
|
+
const marker = status.installedVersion ?? "(no marker \u2014 pre-sync install)";
|
|
4526
|
+
const state = status.upToDate ? "\u2713 up to date" : "\u26A0 stale \u2014 will sync on next command";
|
|
4527
|
+
console.log(`Skill: ${state}`);
|
|
4528
|
+
console.log(` installed: ${marker}`);
|
|
4529
|
+
console.log(` current: ${status.currentVersion}`);
|
|
4530
|
+
console.log(` path: ${status.canonicalPath}`);
|
|
4531
|
+
});
|
|
4293
4532
|
var connection = program.command("connection").description("Manage connections");
|
|
4294
4533
|
connection.command("add [platform]").alias("a").description("Add a new connection").action(async (platform) => {
|
|
4295
4534
|
await connectionAddCommand(platform);
|