@withone/cli 2.9.1 → 2.10.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 +19 -4
- package/bin/cli.js +7 -1
- package/dist/{chunk-UP2L4XJH.js → chunk-5CPQHXTO.js} +17 -1
- package/dist/chunk-FYTWXGPJ.js +79 -0
- package/dist/{flow-runner-K73VDSS3.js → flow-runner-P23IZDWM.js} +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +872 -375
- package/dist/node-check.d.ts +36 -0
- package/dist/node-check.js +18 -0
- package/package.json +2 -2
- package/skills/one/SKILL.md +11 -0
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
nodeHasTlsFix
|
|
3
|
+
} from "./chunk-FYTWXGPJ.js";
|
|
1
4
|
import {
|
|
2
5
|
ApiError,
|
|
3
6
|
FLOW_SCHEMA,
|
|
@@ -39,7 +42,7 @@ import {
|
|
|
39
42
|
validateActionInput,
|
|
40
43
|
walkSteps,
|
|
41
44
|
writeCache
|
|
42
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-5CPQHXTO.js";
|
|
43
46
|
import {
|
|
44
47
|
memSqlCommand
|
|
45
48
|
} from "./chunk-O3HDWHHS.js";
|
|
@@ -149,7 +152,7 @@ import {
|
|
|
149
152
|
|
|
150
153
|
// src/cli.ts
|
|
151
154
|
import { createRequire } from "module";
|
|
152
|
-
import
|
|
155
|
+
import path14 from "path";
|
|
153
156
|
import { Command } from "commander";
|
|
154
157
|
|
|
155
158
|
// src/commands/init.ts
|
|
@@ -177,6 +180,27 @@ function getConnectionUrl(platform, params) {
|
|
|
177
180
|
function getApiKeyUrl() {
|
|
178
181
|
return `${oneAppUrl()}/settings/api-keys`;
|
|
179
182
|
}
|
|
183
|
+
function connectSetupParams(scope, extra) {
|
|
184
|
+
const params = new URLSearchParams();
|
|
185
|
+
if (scope.orgId) params.set("orgId", scope.orgId);
|
|
186
|
+
if (scope.projectId) params.set("projectId", scope.projectId);
|
|
187
|
+
if (scope.envFile) params.set("env", scope.envFile);
|
|
188
|
+
for (const [key, value] of extra) if (value) params.set(key, value);
|
|
189
|
+
const qs = params.toString();
|
|
190
|
+
return qs ? `?${qs}` : "";
|
|
191
|
+
}
|
|
192
|
+
function getConnectSetupUrl(opts) {
|
|
193
|
+
const target = opts.clientId ? encodeURIComponent(opts.clientId) : "new";
|
|
194
|
+
const qs = connectSetupParams(opts, [
|
|
195
|
+
["set", opts.permissionSetId],
|
|
196
|
+
["port", String(opts.port)],
|
|
197
|
+
["state", opts.state]
|
|
198
|
+
]);
|
|
199
|
+
return `${oneAppUrl()}/cli/connect/${target}${qs}#z=${opts.handoff}`;
|
|
200
|
+
}
|
|
201
|
+
function getConnectDoneUrl(clientId, outcome, scope) {
|
|
202
|
+
return `${oneAppUrl()}/cli/connect/${encodeURIComponent(clientId)}${connectSetupParams(scope, [["outcome", outcome]])}`;
|
|
203
|
+
}
|
|
180
204
|
async function openConnectionPage(platform, params) {
|
|
181
205
|
await open(getConnectionUrl(platform, params));
|
|
182
206
|
}
|
|
@@ -1062,25 +1086,25 @@ async function loginCommand() {
|
|
|
1062
1086
|
let targetScope = "global";
|
|
1063
1087
|
const existingKey = getApiKey();
|
|
1064
1088
|
if (existingKey) {
|
|
1065
|
-
const
|
|
1089
|
+
const pc18 = (await import("picocolors")).default;
|
|
1066
1090
|
const resolved = resolveConfig();
|
|
1067
1091
|
const whoami2 = resolved.config?.whoami;
|
|
1068
1092
|
const env2 = getEnvFromApiKey(existingKey);
|
|
1069
|
-
const envLabel2 = env2 === "test" ?
|
|
1070
|
-
const currentScope = resolved.scope === "project" ?
|
|
1093
|
+
const envLabel2 = env2 === "test" ? pc18.yellow("test") : pc18.green("live");
|
|
1094
|
+
const currentScope = resolved.scope === "project" ? pc18.cyan("local config") : pc18.magenta("global config");
|
|
1071
1095
|
const lines = ["You are already logged in.", ""];
|
|
1072
1096
|
if (whoami2) {
|
|
1073
1097
|
const contextParts2 = [];
|
|
1074
1098
|
if (whoami2.organization) contextParts2.push(whoami2.organization.name);
|
|
1075
1099
|
if (whoami2.project) contextParts2.push(whoami2.project.name);
|
|
1076
1100
|
const scopeDisplay2 = contextParts2.length > 0 ? contextParts2.join(" / ") : "Personal";
|
|
1077
|
-
lines.push(`${
|
|
1078
|
-
lines.push(`${whoami2.user.name} ${
|
|
1079
|
-
if (whoami2.organization) lines.push(`${
|
|
1080
|
-
if (whoami2.project) lines.push(`${
|
|
1101
|
+
lines.push(`${pc18.bold(scopeDisplay2)} ${pc18.dim("\xB7")} ${envLabel2}`);
|
|
1102
|
+
lines.push(`${whoami2.user.name} ${pc18.dim(`(${whoami2.user.email})`)}`);
|
|
1103
|
+
if (whoami2.organization) lines.push(`${pc18.dim("Org:")} ${whoami2.organization.name}`);
|
|
1104
|
+
if (whoami2.project) lines.push(`${pc18.dim("Project:")} ${whoami2.project.name}`);
|
|
1081
1105
|
}
|
|
1082
1106
|
lines.push("");
|
|
1083
|
-
lines.push(`${
|
|
1107
|
+
lines.push(`${pc18.dim("Stored in")} ${currentScope}`);
|
|
1084
1108
|
p2.note(lines.join("\n"));
|
|
1085
1109
|
const scopeChoice = await p2.select({
|
|
1086
1110
|
message: "Where would you like to log in?",
|
|
@@ -1099,42 +1123,42 @@ async function loginCommand() {
|
|
|
1099
1123
|
if (!result) return;
|
|
1100
1124
|
const { apiKey, whoami, keyName, grant, knowledgeOnly } = result;
|
|
1101
1125
|
saveCredentials(apiKey, targetScope, { keyName, whoami, knowledgeOnly });
|
|
1102
|
-
const
|
|
1126
|
+
const pc17 = (await import("picocolors")).default;
|
|
1103
1127
|
const env = getEnvFromApiKey(apiKey);
|
|
1104
1128
|
const contextParts = [];
|
|
1105
1129
|
if (whoami.organization) contextParts.push(whoami.organization.name);
|
|
1106
1130
|
if (whoami.project) contextParts.push(whoami.project.name);
|
|
1107
1131
|
const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
|
|
1108
|
-
const envLabel = env === "test" ?
|
|
1109
|
-
const configLabel = targetScope === "project" ?
|
|
1132
|
+
const envLabel = env === "test" ? pc17.yellow("test") : pc17.green("live");
|
|
1133
|
+
const configLabel = targetScope === "project" ? pc17.cyan("local config") : pc17.magenta("global config");
|
|
1110
1134
|
const infoLines = [
|
|
1111
|
-
`${
|
|
1112
|
-
`${whoami.user.name} ${
|
|
1135
|
+
`${pc17.bold(scopeDisplay)} ${pc17.dim("\xB7")} ${envLabel}`,
|
|
1136
|
+
`${whoami.user.name} ${pc17.dim(`(${whoami.user.email})`)}`
|
|
1113
1137
|
];
|
|
1114
|
-
if (keyName) infoLines.push(`${
|
|
1115
|
-
infoLines.push(...grantNoteLines(grant,
|
|
1116
|
-
if (whoami.organization) infoLines.push(`${
|
|
1117
|
-
if (whoami.project) infoLines.push(`${
|
|
1138
|
+
if (keyName) infoLines.push(`${pc17.dim("Key:")} ${keyName}`);
|
|
1139
|
+
infoLines.push(...grantNoteLines(grant, pc17.dim("Access:")));
|
|
1140
|
+
if (whoami.organization) infoLines.push(`${pc17.dim("Org:")} ${whoami.organization.name}`);
|
|
1141
|
+
if (whoami.project) infoLines.push(`${pc17.dim("Project:")} ${whoami.project.name}`);
|
|
1118
1142
|
infoLines.push("");
|
|
1119
|
-
infoLines.push(`${
|
|
1143
|
+
infoLines.push(`${pc17.dim("Stored in")} ${configLabel}`);
|
|
1120
1144
|
p2.note(infoLines.join("\n"), "Logged in");
|
|
1121
1145
|
console.log();
|
|
1122
|
-
console.log(` ${
|
|
1123
|
-
console.log(` ${
|
|
1146
|
+
console.log(` ${pc17.dim("Next steps:")}`);
|
|
1147
|
+
console.log(` ${pc17.cyan("one add <platform>")} ${pc17.dim("\u2014")} Connect a platform (e.g. gmail, slack, stripe)`);
|
|
1124
1148
|
console.log();
|
|
1125
|
-
console.log(
|
|
1149
|
+
console.log(pc17.bold(" Copy-paste this to your AI agent:"));
|
|
1126
1150
|
console.log();
|
|
1127
|
-
console.log(
|
|
1128
|
-
console.log(
|
|
1129
|
-
console.log(
|
|
1130
|
-
console.log(
|
|
1131
|
-
console.log(
|
|
1132
|
-
console.log(
|
|
1133
|
-
console.log(
|
|
1134
|
-
console.log(
|
|
1135
|
-
console.log(
|
|
1136
|
-
console.log(
|
|
1137
|
-
console.log(
|
|
1151
|
+
console.log(pc17.cyan(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1152
|
+
console.log(pc17.cyan(" \u2502"));
|
|
1153
|
+
console.log(pc17.cyan(" \u2502 The One CLI (`one`) is now installed on this machine. It"));
|
|
1154
|
+
console.log(pc17.cyan(" \u2502 lets you interact with 1000+ third-party platforms (Gmail,"));
|
|
1155
|
+
console.log(pc17.cyan(" \u2502 Slack, Shopify, HubSpot, Stripe, GitHub, etc.) through"));
|
|
1156
|
+
console.log(pc17.cyan(" \u2502 their APIs. Whenever you need to interact with any"));
|
|
1157
|
+
console.log(pc17.cyan(" \u2502 third-party platform or external service, use the `one`"));
|
|
1158
|
+
console.log(pc17.cyan(" \u2502 skill. Please save this to your project instructions or"));
|
|
1159
|
+
console.log(pc17.cyan(" \u2502 memory so you remember it in future conversations."));
|
|
1160
|
+
console.log(pc17.cyan(" \u2502"));
|
|
1161
|
+
console.log(pc17.cyan(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1138
1162
|
console.log();
|
|
1139
1163
|
p2.outro("Happy building!");
|
|
1140
1164
|
}
|
|
@@ -1319,8 +1343,8 @@ function tildify(filePath) {
|
|
|
1319
1343
|
function scopeLabel(scope) {
|
|
1320
1344
|
return scope === "project" ? pc3.cyan("[project]") : pc3.magenta("[global]");
|
|
1321
1345
|
}
|
|
1322
|
-
function scopedMessage(scope,
|
|
1323
|
-
return `${scopeLabel(scope)} ${
|
|
1346
|
+
function scopedMessage(scope, message2) {
|
|
1347
|
+
return `${scopeLabel(scope)} ${message2}`;
|
|
1324
1348
|
}
|
|
1325
1349
|
async function handleExistingConfig(apiKey, scope, options) {
|
|
1326
1350
|
const statuses = getAgentStatuses();
|
|
@@ -1907,8 +1931,8 @@ async function promptConnectIntegrations(apiKey, connParams) {
|
|
|
1907
1931
|
{ value: "more", label: "Browse all 1000+ platforms" },
|
|
1908
1932
|
{ value: "skip", label: "Skip for now", hint: "you can always run one add later" }
|
|
1909
1933
|
];
|
|
1910
|
-
const
|
|
1911
|
-
const choice = await p3.select({ message, options });
|
|
1934
|
+
const message2 = first ? "Connect your first integration?" : "Connect another?";
|
|
1935
|
+
const choice = await p3.select({ message: message2, options });
|
|
1912
1936
|
if (p3.isCancel(choice) || choice === "skip") {
|
|
1913
1937
|
break;
|
|
1914
1938
|
}
|
|
@@ -3292,7 +3316,7 @@ var CUSTOM_ACTION_NOTE = '\n\nThis is a custom action: ALSO include "connectionK
|
|
|
3292
3316
|
function codeGuideAction(action, knowledge, platform, tags, apiBase) {
|
|
3293
3317
|
const base = apiBase.replace(/\/$/, "");
|
|
3294
3318
|
const method = action.method.toUpperCase();
|
|
3295
|
-
const
|
|
3319
|
+
const path15 = action.path.startsWith("/") ? action.path : `/${action.path}`;
|
|
3296
3320
|
const connEnv = `ONE_${platformEnvSegment(platform)}_CONNECTION_KEY`;
|
|
3297
3321
|
const customNote = tags.includes("custom") ? CUSTOM_ACTION_NOTE : "";
|
|
3298
3322
|
return `### ${actionHeading(action)}
|
|
@@ -3301,7 +3325,7 @@ function codeGuideAction(action, knowledge, platform, tags, apiBase) {
|
|
|
3301
3325
|
${knowledge ?? NO_KNOWLEDGE}
|
|
3302
3326
|
|
|
3303
3327
|
## Calling this action from code
|
|
3304
|
-
URL: ${base}/passthrough${
|
|
3328
|
+
URL: ${base}/passthrough${path15}
|
|
3305
3329
|
Method: ${method}
|
|
3306
3330
|
Headers:
|
|
3307
3331
|
- x-one-secret: the value of the ONE_SECRET env var
|
|
@@ -3312,7 +3336,7 @@ Headers:
|
|
|
3312
3336
|
Environment variables: ONE_SECRET (their One API key) and ${connEnv} (the key of their ${platform} connection), both from the One dashboard.
|
|
3313
3337
|
|
|
3314
3338
|
\`\`\`typescript
|
|
3315
|
-
const response = await fetch("${base}/passthrough${
|
|
3339
|
+
const response = await fetch("${base}/passthrough${path15}", {
|
|
3316
3340
|
method: "${method}",
|
|
3317
3341
|
headers: {
|
|
3318
3342
|
"x-one-secret": process.env.ONE_SECRET,
|
|
@@ -4062,32 +4086,32 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
4062
4086
|
const validTypes = FLOW_SCHEMA.stepTypes.map((st) => st.type);
|
|
4063
4087
|
for (let i = 0; i < steps.length; i++) {
|
|
4064
4088
|
const step = steps[i];
|
|
4065
|
-
const
|
|
4089
|
+
const path15 = `${pathPrefix}[${i}]`;
|
|
4066
4090
|
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
4067
|
-
errors.push({ path:
|
|
4091
|
+
errors.push({ path: path15, message: "Step must be an object" });
|
|
4068
4092
|
continue;
|
|
4069
4093
|
}
|
|
4070
4094
|
const s = step;
|
|
4071
4095
|
if (!s.id || typeof s.id !== "string") {
|
|
4072
|
-
errors.push({ path: `${
|
|
4096
|
+
errors.push({ path: `${path15}.id`, message: 'Step must have a string "id"' });
|
|
4073
4097
|
}
|
|
4074
4098
|
if (!s.name || typeof s.name !== "string") {
|
|
4075
|
-
errors.push({ path: `${
|
|
4099
|
+
errors.push({ path: `${path15}.name`, message: 'Step must have a string "name"' });
|
|
4076
4100
|
}
|
|
4077
4101
|
if (!s.type || !validTypes.includes(s.type)) {
|
|
4078
|
-
errors.push({ path: `${
|
|
4102
|
+
errors.push({ path: `${path15}.type`, message: `Step type must be one of: ${validTypes.join(", ")}` });
|
|
4079
4103
|
continue;
|
|
4080
4104
|
}
|
|
4081
4105
|
if (s.requires !== void 0) {
|
|
4082
4106
|
if (!Array.isArray(s.requires)) {
|
|
4083
|
-
errors.push({ path: `${
|
|
4107
|
+
errors.push({ path: `${path15}.requires`, message: '"requires" must be an array of selector strings (e.g. ["$.steps.foo.output.bar"])' });
|
|
4084
4108
|
} else {
|
|
4085
4109
|
for (let r = 0; r < s.requires.length; r++) {
|
|
4086
4110
|
const sel = s.requires[r];
|
|
4087
4111
|
if (typeof sel !== "string") {
|
|
4088
|
-
errors.push({ path: `${
|
|
4112
|
+
errors.push({ path: `${path15}.requires[${r}]`, message: '"requires" entry must be a selector string' });
|
|
4089
4113
|
} else if (!sel.startsWith("$.")) {
|
|
4090
|
-
errors.push({ path: `${
|
|
4114
|
+
errors.push({ path: `${path15}.requires[${r}]`, message: `"requires" entry "${sel}" must be a selector starting with "$." (e.g. "$.steps.foo.output.bar")` });
|
|
4091
4115
|
}
|
|
4092
4116
|
}
|
|
4093
4117
|
}
|
|
@@ -4095,7 +4119,7 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
4095
4119
|
if (s.onError && typeof s.onError === "object") {
|
|
4096
4120
|
const oe = s.onError;
|
|
4097
4121
|
if (!FLOW_SCHEMA.errorStrategies.includes(oe.strategy)) {
|
|
4098
|
-
errors.push({ path: `${
|
|
4122
|
+
errors.push({ path: `${path15}.onError.strategy`, message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
|
|
4099
4123
|
}
|
|
4100
4124
|
}
|
|
4101
4125
|
const descriptor = getStepTypeDescriptor(s.type);
|
|
@@ -4105,14 +4129,14 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
4105
4129
|
if (!configObj || typeof configObj !== "object") {
|
|
4106
4130
|
const hint = detectFlatConfigHint(s, descriptor);
|
|
4107
4131
|
errors.push({
|
|
4108
|
-
path: `${
|
|
4132
|
+
path: `${path15}.${configKey}`,
|
|
4109
4133
|
message: `${capitalize(descriptor.type)} step must have a "${configKey}" config object${hint}`
|
|
4110
4134
|
});
|
|
4111
4135
|
continue;
|
|
4112
4136
|
}
|
|
4113
4137
|
const config2 = configObj;
|
|
4114
4138
|
for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
|
|
4115
|
-
const fieldPath = `${
|
|
4139
|
+
const fieldPath = `${path15}.${configKey}.${fieldName}`;
|
|
4116
4140
|
const value = config2[fieldName];
|
|
4117
4141
|
if (fd.required && (value === void 0 || value === null || value === "")) {
|
|
4118
4142
|
errors.push({ path: fieldPath, message: `${capitalize(descriptor.type)} must have ${fd.type === "string" ? "a string" : fd.type === "array" ? "a" : "a"} "${fieldName}"` });
|
|
@@ -4145,30 +4169,30 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
4145
4169
|
}
|
|
4146
4170
|
}
|
|
4147
4171
|
if (descriptor.type === "action") {
|
|
4148
|
-
validateConnectionForm(config2, `${
|
|
4172
|
+
validateConnectionForm(config2, `${path15}.${configKey}`, errors);
|
|
4149
4173
|
}
|
|
4150
4174
|
if (descriptor.type === "code") {
|
|
4151
4175
|
const hasSource = typeof config2.source === "string" && config2.source.length > 0;
|
|
4152
4176
|
const hasModule = typeof config2.module === "string" && config2.module.length > 0;
|
|
4153
4177
|
if (!hasSource && !hasModule) {
|
|
4154
|
-
errors.push({ path: `${
|
|
4178
|
+
errors.push({ path: `${path15}.${configKey}`, message: 'Code step must define either "source" (inline JS) or "module" (path to .mjs file)' });
|
|
4155
4179
|
} else if (hasSource && hasModule) {
|
|
4156
|
-
errors.push({ path: `${
|
|
4180
|
+
errors.push({ path: `${path15}.${configKey}`, message: 'Code step cannot define both "source" and "module" \u2014 pick one' });
|
|
4157
4181
|
}
|
|
4158
4182
|
if (hasModule) {
|
|
4159
4183
|
const m = config2.module;
|
|
4160
4184
|
if (m.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(m)) {
|
|
4161
|
-
errors.push({ path: `${
|
|
4185
|
+
errors.push({ path: `${path15}.${configKey}.module`, message: "Code module path must be relative to the flow folder (no absolute paths)" });
|
|
4162
4186
|
} else if (m.split(/[\\/]/).includes("..")) {
|
|
4163
|
-
errors.push({ path: `${
|
|
4187
|
+
errors.push({ path: `${path15}.${configKey}.module`, message: 'Code module path must not escape the flow folder ("..")' });
|
|
4164
4188
|
} else if (!m.endsWith(".mjs")) {
|
|
4165
|
-
errors.push({ path: `${
|
|
4189
|
+
errors.push({ path: `${path15}.${configKey}.module`, message: "Code module must be a .mjs file" });
|
|
4166
4190
|
}
|
|
4167
4191
|
}
|
|
4168
4192
|
if (hasSource) {
|
|
4169
4193
|
const syntaxError = checkCodeSourceSyntax(config2.source);
|
|
4170
4194
|
if (syntaxError) {
|
|
4171
|
-
errors.push({ path: `${
|
|
4195
|
+
errors.push({ path: `${path15}.${configKey}.source`, message: `Syntax error in code step: ${syntaxError}` });
|
|
4172
4196
|
}
|
|
4173
4197
|
}
|
|
4174
4198
|
}
|
|
@@ -4240,16 +4264,16 @@ function validateStepIds(flow2) {
|
|
|
4240
4264
|
function collectIds(steps, pathPrefix) {
|
|
4241
4265
|
for (let i = 0; i < steps.length; i++) {
|
|
4242
4266
|
const step = steps[i];
|
|
4243
|
-
const
|
|
4267
|
+
const path15 = `${pathPrefix}[${i}]`;
|
|
4244
4268
|
if (seen.has(step.id)) {
|
|
4245
|
-
errors.push({ path: `${
|
|
4269
|
+
errors.push({ path: `${path15}.id`, message: `Duplicate step ID: "${step.id}"` });
|
|
4246
4270
|
} else {
|
|
4247
4271
|
seen.add(step.id);
|
|
4248
4272
|
}
|
|
4249
4273
|
for (const { configKey, fieldName } of nestedKeys) {
|
|
4250
4274
|
const config2 = step[configKey];
|
|
4251
4275
|
if (config2 && Array.isArray(config2[fieldName])) {
|
|
4252
|
-
collectIds(config2[fieldName], `${
|
|
4276
|
+
collectIds(config2[fieldName], `${path15}.${configKey}.${fieldName}`);
|
|
4253
4277
|
}
|
|
4254
4278
|
}
|
|
4255
4279
|
}
|
|
@@ -4297,7 +4321,7 @@ function validateSelectorReferences(flow2, rootDir) {
|
|
|
4297
4321
|
}
|
|
4298
4322
|
return selectors;
|
|
4299
4323
|
}
|
|
4300
|
-
function checkSelectors(selectors,
|
|
4324
|
+
function checkSelectors(selectors, path15, precedingStepIds) {
|
|
4301
4325
|
for (const selector of selectors) {
|
|
4302
4326
|
const parts = selector.split(".");
|
|
4303
4327
|
if (parts.length < 3) continue;
|
|
@@ -4305,15 +4329,15 @@ function validateSelectorReferences(flow2, rootDir) {
|
|
|
4305
4329
|
if (root === "input") {
|
|
4306
4330
|
const inputName = parts[2];
|
|
4307
4331
|
if (!inputNames.has(inputName)) {
|
|
4308
|
-
errors.push({ path:
|
|
4332
|
+
errors.push({ path: path15, message: `Selector "${selector}" references undefined input "${inputName}"` });
|
|
4309
4333
|
}
|
|
4310
4334
|
} else if (root === "steps") {
|
|
4311
4335
|
const stepId = parts[2].replace(/[\[\]]/g, "").split(/[\[\]]/)[0];
|
|
4312
4336
|
if (!allStepIds.has(stepId)) {
|
|
4313
|
-
errors.push({ path:
|
|
4337
|
+
errors.push({ path: path15, message: `Selector "${selector}" references undefined step "${stepId}"` });
|
|
4314
4338
|
} else if (precedingStepIds && !precedingStepIds.has(stepId)) {
|
|
4315
4339
|
errors.push({
|
|
4316
|
-
path:
|
|
4340
|
+
path: path15,
|
|
4317
4341
|
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.`
|
|
4318
4342
|
});
|
|
4319
4343
|
}
|
|
@@ -4321,20 +4345,20 @@ function validateSelectorReferences(flow2, rootDir) {
|
|
|
4321
4345
|
}
|
|
4322
4346
|
}
|
|
4323
4347
|
const EXPRESSION_FIELDS = /* @__PURE__ */ new Set(["condition.expression", "while.condition"]);
|
|
4324
|
-
function checkOperatorsInSelectorField(value,
|
|
4348
|
+
function checkOperatorsInSelectorField(value, path15) {
|
|
4325
4349
|
if (typeof value === "string" && value.startsWith("$.")) {
|
|
4326
4350
|
if (value.includes("||")) {
|
|
4327
|
-
errors.push({ path:
|
|
4351
|
+
errors.push({ path: path15, 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.` });
|
|
4328
4352
|
} else if (value.includes("&&")) {
|
|
4329
|
-
errors.push({ path:
|
|
4353
|
+
errors.push({ path: path15, 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.` });
|
|
4330
4354
|
}
|
|
4331
4355
|
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
4332
4356
|
for (const [k, v] of Object.entries(value)) {
|
|
4333
|
-
checkOperatorsInSelectorField(v, `${
|
|
4357
|
+
checkOperatorsInSelectorField(v, `${path15}.${k}`);
|
|
4334
4358
|
}
|
|
4335
4359
|
} else if (Array.isArray(value)) {
|
|
4336
4360
|
for (let i = 0; i < value.length; i++) {
|
|
4337
|
-
checkOperatorsInSelectorField(value[i], `${
|
|
4361
|
+
checkOperatorsInSelectorField(value[i], `${path15}[${i}]`);
|
|
4338
4362
|
}
|
|
4339
4363
|
}
|
|
4340
4364
|
}
|
|
@@ -4419,10 +4443,10 @@ var VALID_OUTPUT_SCHEMA_TYPES = /* @__PURE__ */ new Set(["string", "number", "bo
|
|
|
4419
4443
|
function isOutputSchemaObject(v) {
|
|
4420
4444
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
4421
4445
|
}
|
|
4422
|
-
function walkOutputSchema(schema,
|
|
4446
|
+
function walkOutputSchema(schema, path15) {
|
|
4423
4447
|
let current = schema;
|
|
4424
|
-
for (let i = 0; i <
|
|
4425
|
-
const seg =
|
|
4448
|
+
for (let i = 0; i < path15.length; i++) {
|
|
4449
|
+
const seg = path15[i];
|
|
4426
4450
|
if (typeof current === "string") {
|
|
4427
4451
|
return current === "unknown" || current === "object" || current === "array" ? "opaque" : "opaque";
|
|
4428
4452
|
}
|
|
@@ -5889,6 +5913,463 @@ async function relayEventTypesCommand(platform) {
|
|
|
5889
5913
|
}
|
|
5890
5914
|
}
|
|
5891
5915
|
|
|
5916
|
+
// src/commands/connect.ts
|
|
5917
|
+
import crypto2 from "crypto";
|
|
5918
|
+
import fs6 from "fs";
|
|
5919
|
+
import path6 from "path";
|
|
5920
|
+
import open4 from "open";
|
|
5921
|
+
import pc10 from "picocolors";
|
|
5922
|
+
|
|
5923
|
+
// src/lib/connect-callback.ts
|
|
5924
|
+
import http2 from "http";
|
|
5925
|
+
var PORT_RANGE_START2 = 49152;
|
|
5926
|
+
var PORT_RANGE_END2 = 65535;
|
|
5927
|
+
var MAX_PORT_ATTEMPTS2 = 5;
|
|
5928
|
+
var MAX_ID_LENGTH = 200;
|
|
5929
|
+
var MAX_ERROR_LENGTH2 = 200;
|
|
5930
|
+
var ID_PATTERN = /^[A-Za-z0-9_:.-]+$/;
|
|
5931
|
+
function statusPage2(title, body) {
|
|
5932
|
+
return `<!DOCTYPE html>
|
|
5933
|
+
<html><head><title>One CLI</title></head>
|
|
5934
|
+
<body style="font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#0a0a0a;color:#fafafa">
|
|
5935
|
+
<div style="text-align:center">
|
|
5936
|
+
<h1 style="font-size:20px;margin:0 0 8px">${title}</h1>
|
|
5937
|
+
<p style="color:#a1a1aa;font-size:14px">${body}</p>
|
|
5938
|
+
</div></body></html>`;
|
|
5939
|
+
}
|
|
5940
|
+
var CANCELLED_HTML2 = statusPage2("Nothing was changed", "You can close this tab and run <code>one connect create</code> again.");
|
|
5941
|
+
var FAILED_HTML2 = statusPage2("The app wasn't set up", "Return to your terminal for details.");
|
|
5942
|
+
function cleanId(value) {
|
|
5943
|
+
if (!value) return void 0;
|
|
5944
|
+
const trimmed = value.trim();
|
|
5945
|
+
return trimmed.length <= MAX_ID_LENGTH && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
5946
|
+
}
|
|
5947
|
+
function cleanText(value) {
|
|
5948
|
+
if (value === null) return void 0;
|
|
5949
|
+
const cleaned = value.replace(/[\u0000-\u001f\u007f-\u009f]/g, "").trim();
|
|
5950
|
+
return cleaned ? Array.from(cleaned).slice(0, MAX_ERROR_LENGTH2).join("") : void 0;
|
|
5951
|
+
}
|
|
5952
|
+
function parseConnectCallback(url, expectedState) {
|
|
5953
|
+
if (url.pathname !== "/callback") return { status: 404 };
|
|
5954
|
+
const state = url.searchParams.get("state");
|
|
5955
|
+
if (!state) return { status: 400 };
|
|
5956
|
+
if (state !== expectedState) return { status: 403 };
|
|
5957
|
+
const error2 = cleanText(url.searchParams.get("error"));
|
|
5958
|
+
if (error2 === "cancelled") return { status: 200, outcome: { kind: "cancelled" } };
|
|
5959
|
+
if (error2) return { status: 200, outcome: { kind: "failed", reason: error2 } };
|
|
5960
|
+
const outcome = url.searchParams.get("outcome");
|
|
5961
|
+
const clientId = cleanId(url.searchParams.get("clientId"));
|
|
5962
|
+
const rawSet = url.searchParams.get("permissionSetId");
|
|
5963
|
+
const permissionSetId = rawSet ? cleanId(rawSet) : null;
|
|
5964
|
+
if (outcome !== "created" && outcome !== "updated" && outcome !== "unchanged" || !clientId || permissionSetId === void 0) {
|
|
5965
|
+
return { status: 400 };
|
|
5966
|
+
}
|
|
5967
|
+
return { status: 200, outcome: { kind: "done", outcome, clientId, permissionSetId } };
|
|
5968
|
+
}
|
|
5969
|
+
function randomPort2() {
|
|
5970
|
+
return PORT_RANGE_START2 + Math.floor(Math.random() * (PORT_RANGE_END2 - PORT_RANGE_START2));
|
|
5971
|
+
}
|
|
5972
|
+
function startConnectCallbackServer(expectedState, doneUrl) {
|
|
5973
|
+
return new Promise((resolveSetup, rejectSetup) => {
|
|
5974
|
+
let attempts = 0;
|
|
5975
|
+
let resolveResult;
|
|
5976
|
+
const result = new Promise((res) => {
|
|
5977
|
+
resolveResult = res;
|
|
5978
|
+
});
|
|
5979
|
+
function tryListen() {
|
|
5980
|
+
const port = randomPort2();
|
|
5981
|
+
attempts++;
|
|
5982
|
+
const server = http2.createServer((req, res) => {
|
|
5983
|
+
const { status: status2, outcome } = parseConnectCallback(new URL(req.url || "/", `http://localhost:${port}`), expectedState);
|
|
5984
|
+
if (!outcome) {
|
|
5985
|
+
res.writeHead(status2, { "Content-Type": "text/plain" });
|
|
5986
|
+
res.end(status2 === 403 ? "State mismatch" : status2 === 404 ? "Not found" : "Missing required parameters");
|
|
5987
|
+
return;
|
|
5988
|
+
}
|
|
5989
|
+
if (outcome.kind === "done") {
|
|
5990
|
+
res.writeHead(302, { Location: doneUrl(outcome) });
|
|
5991
|
+
res.end();
|
|
5992
|
+
} else {
|
|
5993
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
5994
|
+
res.end(outcome.kind === "cancelled" ? CANCELLED_HTML2 : FAILED_HTML2);
|
|
5995
|
+
}
|
|
5996
|
+
resolveResult(outcome);
|
|
5997
|
+
});
|
|
5998
|
+
server.on("error", (err) => {
|
|
5999
|
+
if ((err.code === "EADDRINUSE" || err.code === "EACCES") && attempts < MAX_PORT_ATTEMPTS2) {
|
|
6000
|
+
tryListen();
|
|
6001
|
+
return;
|
|
6002
|
+
}
|
|
6003
|
+
rejectSetup(err);
|
|
6004
|
+
});
|
|
6005
|
+
server.listen(port, "127.0.0.1", () => resolveSetup({ server, port, result }));
|
|
6006
|
+
}
|
|
6007
|
+
tryListen();
|
|
6008
|
+
});
|
|
6009
|
+
}
|
|
6010
|
+
|
|
6011
|
+
// src/lib/connect-app.ts
|
|
6012
|
+
import fs5 from "fs";
|
|
6013
|
+
import path5 from "path";
|
|
6014
|
+
import zlib from "zlib";
|
|
6015
|
+
import { pathToFileURL } from "url";
|
|
6016
|
+
var CONFIG_FILES = ["one.connect.ts", "one.connect.mts", "one.connect.mjs", "one.connect.js"];
|
|
6017
|
+
var TYPES_FILE = "one-connect-env.d.ts";
|
|
6018
|
+
var ACCESS_METHODS = {
|
|
6019
|
+
"read-only": ["GET"],
|
|
6020
|
+
"read-write": ["GET", "POST", "PUT", "PATCH"],
|
|
6021
|
+
"full-access": null
|
|
6022
|
+
};
|
|
6023
|
+
var HEX_COLOR = /^#[0-9a-fA-F]{6}$/;
|
|
6024
|
+
var ACTION_ID_PREFIX = "conn_mod_def::";
|
|
6025
|
+
function findConfigFile(cwd, explicit) {
|
|
6026
|
+
if (explicit) {
|
|
6027
|
+
const resolved = path5.resolve(cwd, explicit);
|
|
6028
|
+
return fs5.existsSync(resolved) ? resolved : null;
|
|
6029
|
+
}
|
|
6030
|
+
for (const name of CONFIG_FILES) {
|
|
6031
|
+
const candidate = path5.join(cwd, name);
|
|
6032
|
+
if (fs5.existsSync(candidate)) return candidate;
|
|
6033
|
+
}
|
|
6034
|
+
return null;
|
|
6035
|
+
}
|
|
6036
|
+
async function loadConfigFile(file) {
|
|
6037
|
+
try {
|
|
6038
|
+
const mod = await import(pathToFileURL(file).href);
|
|
6039
|
+
return mod.default ?? mod;
|
|
6040
|
+
} catch (error2) {
|
|
6041
|
+
const { code, message: message2 = "" } = error2;
|
|
6042
|
+
if (code === "ERR_MODULE_NOT_FOUND" && message2.includes("'@withone/connect'")) {
|
|
6043
|
+
throw new Error(
|
|
6044
|
+
`it imports @withone/connect, which isn't installed in this project. Run \`npm i @withone/connect\` first.`
|
|
6045
|
+
);
|
|
6046
|
+
}
|
|
6047
|
+
if (code === "ERR_PACKAGE_PATH_NOT_EXPORTED" && /@withone[\\/]connect/.test(message2)) {
|
|
6048
|
+
throw new Error(
|
|
6049
|
+
`this project's @withone/connect is older than 0.19.0, which added @withone/connect/config. Run \`npm i @withone/connect@latest\` first.`
|
|
6050
|
+
);
|
|
6051
|
+
}
|
|
6052
|
+
if (code === "ERR_UNKNOWN_FILE_EXTENSION" && /\.m?ts$/.test(file)) {
|
|
6053
|
+
throw new Error(
|
|
6054
|
+
`Node ${process.versions.node} can't load ${path5.basename(file)}. Use Node 22.18 or later, or rename it to one.connect.mjs and type it with /** @type {import("@withone/connect/config").ConnectAppConfig} */.`
|
|
6055
|
+
);
|
|
6056
|
+
}
|
|
6057
|
+
throw error2;
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
function validateConfig(value) {
|
|
6061
|
+
const errors = [];
|
|
6062
|
+
const v = value;
|
|
6063
|
+
if (!v || typeof v !== "object") return { errors: ["The config must export an object (use defineConnectApp)."] };
|
|
6064
|
+
if (typeof v.name !== "string" || !v.name.trim()) errors.push("`name` is required.");
|
|
6065
|
+
if (!Array.isArray(v.redirectUris) || v.redirectUris.length === 0) {
|
|
6066
|
+
errors.push("`redirectUris` needs at least one callback URL.");
|
|
6067
|
+
} else {
|
|
6068
|
+
for (const uri of v.redirectUris) {
|
|
6069
|
+
if (typeof uri !== "string" || !/^https?:\/\//.test(uri)) errors.push(`Redirect URI "${uri}" must be an http(s) URL.`);
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
if (v.reason !== void 0 && (typeof v.reason !== "string" || v.reason.trim().length === 0 || v.reason.length > 200)) {
|
|
6073
|
+
errors.push("`reason` must be one sentence of at most 200 characters.");
|
|
6074
|
+
}
|
|
6075
|
+
if (v.branding) {
|
|
6076
|
+
for (const key of ["panelColor", "buttonColor"]) {
|
|
6077
|
+
if (!HEX_COLOR.test(String(v.branding[key] ?? ""))) errors.push(`\`branding.${key}\` must be a #RRGGBB colour.`);
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
for (const [tool, ask] of Object.entries(v.ask ?? {})) {
|
|
6081
|
+
const access = ask?.access;
|
|
6082
|
+
if (access === "custom") {
|
|
6083
|
+
const actions2 = ask.actions;
|
|
6084
|
+
if (!Array.isArray(actions2) || actions2.length === 0) errors.push(`\`ask.${tool}\` is custom, so it needs at least one action.`);
|
|
6085
|
+
} else if (typeof access !== "string" || !(access in ACCESS_METHODS)) {
|
|
6086
|
+
errors.push(`\`ask.${tool}.access\` must be read-only, read-write, full-access or custom.`);
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
return errors.length > 0 ? { errors } : { config: v };
|
|
6090
|
+
}
|
|
6091
|
+
function actionName(title) {
|
|
6092
|
+
const words = title.split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
6093
|
+
return words.map((word, i) => i === 0 ? word.toLowerCase() : word[0].toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
6094
|
+
}
|
|
6095
|
+
function actionNames(actions2) {
|
|
6096
|
+
const byName = /* @__PURE__ */ new Map();
|
|
6097
|
+
const shared = /* @__PURE__ */ new Set();
|
|
6098
|
+
for (const action of actions2) {
|
|
6099
|
+
const name = actionName(action.title);
|
|
6100
|
+
if (!name) continue;
|
|
6101
|
+
if (byName.has(name)) shared.add(name);
|
|
6102
|
+
else byName.set(name, action.systemId);
|
|
6103
|
+
}
|
|
6104
|
+
for (const name of shared) byName.delete(name);
|
|
6105
|
+
return byName;
|
|
6106
|
+
}
|
|
6107
|
+
function resolveActions(refs, names, ids) {
|
|
6108
|
+
const resolved = [];
|
|
6109
|
+
const unknown = [];
|
|
6110
|
+
for (const ref2 of refs) {
|
|
6111
|
+
const id = ref2.startsWith(ACTION_ID_PREFIX) ? ids.has(ref2) ? ref2 : void 0 : names.get(ref2);
|
|
6112
|
+
if (id) resolved.push(id);
|
|
6113
|
+
else unknown.push(ref2);
|
|
6114
|
+
}
|
|
6115
|
+
return unknown.length > 0 ? { unknown } : { ids: [...new Set(resolved)] };
|
|
6116
|
+
}
|
|
6117
|
+
function connectorRule(connectionDefinitionId, ask, actionIds = []) {
|
|
6118
|
+
if (ask.access === "custom") return { type: "connectionDefinition", connectionDefinitionId, methods: null, actionIds };
|
|
6119
|
+
return { type: "connectionDefinition", connectionDefinitionId, methods: ACCESS_METHODS[ask.access], actionIds: null };
|
|
6120
|
+
}
|
|
6121
|
+
function clientFields(config2) {
|
|
6122
|
+
const fields = { name: config2.name.trim(), redirectUris: config2.redirectUris };
|
|
6123
|
+
if (config2.description) fields.description = config2.description;
|
|
6124
|
+
if (config2.logoUrl) fields.logoUrl = config2.logoUrl;
|
|
6125
|
+
if (config2.homepageUrl) fields.homepageUrl = config2.homepageUrl;
|
|
6126
|
+
if (config2.reason) fields.consentReason = config2.reason.trim();
|
|
6127
|
+
if (config2.branding) {
|
|
6128
|
+
fields.frontend = {
|
|
6129
|
+
panelColor: config2.branding.panelColor,
|
|
6130
|
+
buttonColor: config2.branding.buttonColor,
|
|
6131
|
+
cobrandEmail: config2.branding.cobrandEmail ?? false
|
|
6132
|
+
};
|
|
6133
|
+
}
|
|
6134
|
+
return fields;
|
|
6135
|
+
}
|
|
6136
|
+
function buildHandoff(config2, rules) {
|
|
6137
|
+
return { v: 1, app: clientFields(config2), ask: rules.length > 0 ? rules : null };
|
|
6138
|
+
}
|
|
6139
|
+
function encodeHandoff(handoff) {
|
|
6140
|
+
return zlib.deflateRawSync(Buffer.from(JSON.stringify(handoff), "utf8"), { level: 9 }).toString("base64url");
|
|
6141
|
+
}
|
|
6142
|
+
var MAX_HANDOFF_LENGTH = 32e3;
|
|
6143
|
+
var MAX_WINDOWS_OPEN_LENGTH = 11e3;
|
|
6144
|
+
function defaultEnvFile(cwd) {
|
|
6145
|
+
const local = path5.join(cwd, ".env.local");
|
|
6146
|
+
return fs5.existsSync(local) ? local : path5.join(cwd, ".env");
|
|
6147
|
+
}
|
|
6148
|
+
function readEnvValue(text5, key) {
|
|
6149
|
+
const match = text5.match(new RegExp(`^${key}=(.*)$`, "m"));
|
|
6150
|
+
return match ? match[1].trim().replace(/^["']|["']$/g, "") : void 0;
|
|
6151
|
+
}
|
|
6152
|
+
function upsertEnv(text5, values) {
|
|
6153
|
+
let next = text5;
|
|
6154
|
+
const appended = [];
|
|
6155
|
+
for (const [key, value] of Object.entries(values)) {
|
|
6156
|
+
const line = new RegExp(`^${key}=.*$`, "m");
|
|
6157
|
+
if (line.test(next)) {
|
|
6158
|
+
if (value !== null) next = next.replace(line, `${key}=${value}`);
|
|
6159
|
+
} else {
|
|
6160
|
+
appended.push(`${key}=${value ?? ""}`);
|
|
6161
|
+
}
|
|
6162
|
+
}
|
|
6163
|
+
if (appended.length === 0) return next;
|
|
6164
|
+
const separator = next.length === 0 || next.endsWith("\n") ? "" : "\n";
|
|
6165
|
+
return `${next}${separator}${appended.join("\n")}
|
|
6166
|
+
`;
|
|
6167
|
+
}
|
|
6168
|
+
function renderTypes(tools, actionsByTool) {
|
|
6169
|
+
const literal = (value) => JSON.stringify(value);
|
|
6170
|
+
const union = (values) => values.length > 0 ? values.map(literal).join(" | ") : "never";
|
|
6171
|
+
const actionLines = Object.entries(actionsByTool).sort(([a], [b]) => a.localeCompare(b)).map(([tool, names]) => ` ${literal(tool)}: ${union([...names].sort())};`);
|
|
6172
|
+
return [
|
|
6173
|
+
"// Generated by `one connect types`. Run it again after changing the tools in one.connect.ts.",
|
|
6174
|
+
'import "@withone/connect/config";',
|
|
6175
|
+
"",
|
|
6176
|
+
'declare module "@withone/connect/config" {',
|
|
6177
|
+
" interface ConnectRegistry {",
|
|
6178
|
+
` tools: ${union([...tools].sort())};`,
|
|
6179
|
+
" actions: {",
|
|
6180
|
+
...actionLines,
|
|
6181
|
+
" };",
|
|
6182
|
+
" }",
|
|
6183
|
+
"}",
|
|
6184
|
+
""
|
|
6185
|
+
].join("\n");
|
|
6186
|
+
}
|
|
6187
|
+
|
|
6188
|
+
// src/commands/connect.ts
|
|
6189
|
+
var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
6190
|
+
function requireApi() {
|
|
6191
|
+
const apiKey = getApiKey();
|
|
6192
|
+
if (!apiKey) error("Not configured. Run `one init` first.");
|
|
6193
|
+
return new OneApi(apiKey, getApiBase());
|
|
6194
|
+
}
|
|
6195
|
+
async function readConfig2(cwd, explicit) {
|
|
6196
|
+
const file = findConfigFile(cwd, explicit);
|
|
6197
|
+
if (!file) {
|
|
6198
|
+
error(
|
|
6199
|
+
explicit ? `No config at ${explicit}.` : "No one.connect.ts here. Create one with defineConnectApp from @withone/connect/config."
|
|
6200
|
+
);
|
|
6201
|
+
}
|
|
6202
|
+
let loaded;
|
|
6203
|
+
try {
|
|
6204
|
+
loaded = await loadConfigFile(file);
|
|
6205
|
+
} catch (error2) {
|
|
6206
|
+
error(`Couldn't load ${path6.basename(file)}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
6207
|
+
}
|
|
6208
|
+
const checked = validateConfig(loaded);
|
|
6209
|
+
if ("errors" in checked) error(`${path6.basename(file)} has problems:
|
|
6210
|
+
${checked.errors.join("\n ")}`);
|
|
6211
|
+
return { file, config: checked.config };
|
|
6212
|
+
}
|
|
6213
|
+
function message(error2) {
|
|
6214
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
6215
|
+
}
|
|
6216
|
+
async function buildRules(api, config2) {
|
|
6217
|
+
const asked = Object.entries(config2.ask ?? {});
|
|
6218
|
+
if (asked.length === 0) return [];
|
|
6219
|
+
const platforms = await api.listPlatforms();
|
|
6220
|
+
const idBySlug = new Map(platforms.map((p9) => [p9.platform, p9.id]));
|
|
6221
|
+
const unknownTools = asked.map(([slug]) => slug).filter((slug) => !idBySlug.has(slug));
|
|
6222
|
+
if (unknownTools.length > 0) {
|
|
6223
|
+
error(`Not in One's catalog: ${unknownTools.join(", ")}. Run \`one platforms\` for the slugs.`);
|
|
6224
|
+
}
|
|
6225
|
+
const rules = [];
|
|
6226
|
+
for (const [slug, ask] of asked) {
|
|
6227
|
+
const definitionId = idBySlug.get(slug);
|
|
6228
|
+
if (ask.access !== "custom") {
|
|
6229
|
+
rules.push(connectorRule(definitionId, ask));
|
|
6230
|
+
continue;
|
|
6231
|
+
}
|
|
6232
|
+
const actions2 = await api.listAllAvailableActions(slug);
|
|
6233
|
+
const resolved = resolveActions(ask.actions, actionNames(actions2), new Set(actions2.map((a) => a.systemId)));
|
|
6234
|
+
if ("unknown" in resolved) {
|
|
6235
|
+
error(
|
|
6236
|
+
`${slug} has no action named ${resolved.unknown.join(", ")}. Run \`one connect types\` for the names, or \`one actions find ${slug} "<what it does>" --no-knowledge\` for an id.`
|
|
6237
|
+
);
|
|
6238
|
+
}
|
|
6239
|
+
rules.push(connectorRule(definitionId, ask, resolved.ids));
|
|
6240
|
+
}
|
|
6241
|
+
return rules;
|
|
6242
|
+
}
|
|
6243
|
+
async function connectCreateCommand(options) {
|
|
6244
|
+
const cwd = process.cwd();
|
|
6245
|
+
const api = requireApi();
|
|
6246
|
+
const { file, config: config2 } = await readConfig2(cwd, options.config);
|
|
6247
|
+
const envFile = options.envFile ? path6.resolve(cwd, options.envFile) : defaultEnvFile(cwd);
|
|
6248
|
+
const envText = fs6.existsSync(envFile) ? fs6.readFileSync(envFile, "utf8") : "";
|
|
6249
|
+
const existingId = readEnvValue(envText, "ONE_CLIENT_ID") || void 0;
|
|
6250
|
+
const spinner = createSpinner();
|
|
6251
|
+
spinner.start(`Reading ${path6.basename(file)}...`);
|
|
6252
|
+
let server;
|
|
6253
|
+
let timer;
|
|
6254
|
+
try {
|
|
6255
|
+
const rules = await buildRules(api, config2);
|
|
6256
|
+
const whoami = await ensureWhoAmI(api);
|
|
6257
|
+
const scope = {
|
|
6258
|
+
orgId: whoami?.organization?.id,
|
|
6259
|
+
projectId: whoami?.project?.id,
|
|
6260
|
+
envFile: path6.basename(envFile)
|
|
6261
|
+
};
|
|
6262
|
+
const handoff = encodeHandoff(buildHandoff(config2, rules));
|
|
6263
|
+
if (handoff.length > MAX_HANDOFF_LENGTH) {
|
|
6264
|
+
error(
|
|
6265
|
+
`${path6.basename(file)} is too big to hand to the browser (${Math.ceil(handoff.length / 1024)} KB compressed, limit ${Math.floor(MAX_HANDOFF_LENGTH / 1024)} KB). Give tools with long custom lists an access level instead.`
|
|
6266
|
+
);
|
|
6267
|
+
}
|
|
6268
|
+
const state = crypto2.randomBytes(16).toString("hex");
|
|
6269
|
+
const started = await startConnectCallbackServer(
|
|
6270
|
+
state,
|
|
6271
|
+
(done) => getConnectDoneUrl(done.clientId, done.outcome, scope)
|
|
6272
|
+
);
|
|
6273
|
+
server = started.server;
|
|
6274
|
+
const url = getConnectSetupUrl({
|
|
6275
|
+
...scope,
|
|
6276
|
+
clientId: existingId,
|
|
6277
|
+
permissionSetId: readEnvValue(envText, "ONE_PERMISSION_SET") || void 0,
|
|
6278
|
+
port: started.port,
|
|
6279
|
+
state,
|
|
6280
|
+
handoff
|
|
6281
|
+
});
|
|
6282
|
+
const tooLongToOpen = process.platform === "win32" && url.length > MAX_WINDOWS_OPEN_LENGTH;
|
|
6283
|
+
const opening = options.open !== false && !tooLongToOpen;
|
|
6284
|
+
if (opening) await open4(url).catch(() => void 0);
|
|
6285
|
+
const verb = existingId ? "Approve the update" : "Create the app";
|
|
6286
|
+
if (isAgentMode()) {
|
|
6287
|
+
process.stderr.write(`${verb} in the browser: ${url}
|
|
6288
|
+
`);
|
|
6289
|
+
} else {
|
|
6290
|
+
spinner.stop(opening ? `${verb} in your browser` : `${verb}: open this link in your browser`);
|
|
6291
|
+
console.log(` ${pc10.underline(url)}`);
|
|
6292
|
+
console.log();
|
|
6293
|
+
spinner.start("Waiting for you to approve it...");
|
|
6294
|
+
}
|
|
6295
|
+
const outcome = await Promise.race([
|
|
6296
|
+
started.result,
|
|
6297
|
+
new Promise((resolve) => {
|
|
6298
|
+
timer = setTimeout(() => resolve(null), SETUP_TIMEOUT_MS);
|
|
6299
|
+
})
|
|
6300
|
+
]);
|
|
6301
|
+
if (!outcome) error("Timed out waiting for the browser. Run `one connect create` again.");
|
|
6302
|
+
if (outcome.kind === "cancelled") error("Cancelled in the browser. Nothing was changed.");
|
|
6303
|
+
if (outcome.kind === "failed") error(`The setup page reported: ${outcome.reason}`);
|
|
6304
|
+
const created = outcome.outcome === "created";
|
|
6305
|
+
fs6.writeFileSync(
|
|
6306
|
+
envFile,
|
|
6307
|
+
upsertEnv(envText, {
|
|
6308
|
+
ONE_CLIENT_ID: outcome.clientId,
|
|
6309
|
+
ONE_REDIRECT_URI: config2.redirectUris[0],
|
|
6310
|
+
...outcome.permissionSetId ? { ONE_PERMISSION_SET: outcome.permissionSetId } : {},
|
|
6311
|
+
...created ? { ONE_CLIENT_SECRET: null, ONE_CONNECT_KEY: null } : {}
|
|
6312
|
+
})
|
|
6313
|
+
);
|
|
6314
|
+
const result = {
|
|
6315
|
+
outcome: outcome.outcome,
|
|
6316
|
+
clientId: outcome.clientId,
|
|
6317
|
+
permissionSetId: outcome.permissionSetId,
|
|
6318
|
+
redirectUris: config2.redirectUris,
|
|
6319
|
+
envFile: path6.relative(cwd, envFile) || envFile
|
|
6320
|
+
};
|
|
6321
|
+
if (isAgentMode()) {
|
|
6322
|
+
json(result);
|
|
6323
|
+
return;
|
|
6324
|
+
}
|
|
6325
|
+
spinner.stop(
|
|
6326
|
+
created ? `Created ${config2.name}` : outcome.outcome === "updated" ? `Updated ${config2.name}` : `${config2.name} is up to date`
|
|
6327
|
+
);
|
|
6328
|
+
console.log();
|
|
6329
|
+
console.log(` ${pc10.dim("Client ID:")} ${outcome.clientId}`);
|
|
6330
|
+
if (outcome.permissionSetId) console.log(` ${pc10.dim("Permission set:")} ${outcome.permissionSetId}`);
|
|
6331
|
+
console.log(` ${pc10.dim("Wrote")} ${result.envFile}`);
|
|
6332
|
+
if (created) {
|
|
6333
|
+
console.log(` ${pc10.dim("The two secrets came from the browser page, never through here.")}`);
|
|
6334
|
+
}
|
|
6335
|
+
console.log();
|
|
6336
|
+
} catch (error2) {
|
|
6337
|
+
spinner.stop("Failed");
|
|
6338
|
+
error(`Error: ${message(error2)}`);
|
|
6339
|
+
} finally {
|
|
6340
|
+
if (timer) clearTimeout(timer);
|
|
6341
|
+
server?.close();
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
6344
|
+
async function connectTypesCommand(options) {
|
|
6345
|
+
const cwd = process.cwd();
|
|
6346
|
+
const api = requireApi();
|
|
6347
|
+
const spinner = createSpinner();
|
|
6348
|
+
spinner.start("Reading One's catalog...");
|
|
6349
|
+
try {
|
|
6350
|
+
const file = findConfigFile(cwd, options.config);
|
|
6351
|
+
const config2 = file ? validateConfig(await loadConfigFile(file)) : null;
|
|
6352
|
+
const tools = Object.keys(config2 && "config" in config2 ? config2.config.ask ?? {} : {});
|
|
6353
|
+
const platforms = await api.listPlatforms();
|
|
6354
|
+
const known = new Set(platforms.map((p9) => p9.platform));
|
|
6355
|
+
const actionsByTool = {};
|
|
6356
|
+
for (const tool of tools.filter((t) => known.has(t))) {
|
|
6357
|
+
actionsByTool[tool] = [...actionNames(await api.listAllAvailableActions(tool)).keys()];
|
|
6358
|
+
}
|
|
6359
|
+
const target = path6.join(file ? path6.dirname(file) : cwd, TYPES_FILE);
|
|
6360
|
+
fs6.writeFileSync(target, renderTypes([...known], actionsByTool));
|
|
6361
|
+
if (isAgentMode()) {
|
|
6362
|
+
json({ file: target, tools: known.size, actions: actionsByTool });
|
|
6363
|
+
return;
|
|
6364
|
+
}
|
|
6365
|
+
spinner.stop(`Wrote ${path6.relative(cwd, target) || target}`);
|
|
6366
|
+
console.log(` ${pc10.dim(`${known.size} tools; action names for ${Object.keys(actionsByTool).join(", ") || "none yet (add tools to ask, then run it again)"}`)}`);
|
|
6367
|
+
} catch (error2) {
|
|
6368
|
+
spinner.stop("Failed");
|
|
6369
|
+
error(`Error: ${message(error2)}`);
|
|
6370
|
+
}
|
|
6371
|
+
}
|
|
6372
|
+
|
|
5892
6373
|
// src/lib/memory/sync/models.ts
|
|
5893
6374
|
function parseActionType(actionKey) {
|
|
5894
6375
|
const parts = actionKey.split("::");
|
|
@@ -6010,11 +6491,11 @@ function handleId(response, config2, records) {
|
|
|
6010
6491
|
}
|
|
6011
6492
|
|
|
6012
6493
|
// src/lib/memory/sync/state.ts
|
|
6013
|
-
import
|
|
6014
|
-
import
|
|
6015
|
-
var SYNC_DIR =
|
|
6016
|
-
var STATE_DIR =
|
|
6017
|
-
var LEGACY_SINGLE_FILE =
|
|
6494
|
+
import fs7 from "fs";
|
|
6495
|
+
import path7 from "path";
|
|
6496
|
+
var SYNC_DIR = path7.join(".one", "sync");
|
|
6497
|
+
var STATE_DIR = path7.join(SYNC_DIR, "state");
|
|
6498
|
+
var LEGACY_SINGLE_FILE = path7.join(SYNC_DIR, "sync_state.json");
|
|
6018
6499
|
var legacyMigrationDone = false;
|
|
6019
6500
|
function rowToState(row) {
|
|
6020
6501
|
return {
|
|
@@ -6042,9 +6523,9 @@ function stateToRow(platform, model, state, lastError) {
|
|
|
6042
6523
|
async function migrateLegacyOnce() {
|
|
6043
6524
|
if (legacyMigrationDone) return;
|
|
6044
6525
|
legacyMigrationDone = true;
|
|
6045
|
-
if (
|
|
6526
|
+
if (fs7.existsSync(LEGACY_SINGLE_FILE)) {
|
|
6046
6527
|
try {
|
|
6047
|
-
const raw =
|
|
6528
|
+
const raw = fs7.readFileSync(LEGACY_SINGLE_FILE, "utf-8");
|
|
6048
6529
|
const legacy = JSON.parse(raw);
|
|
6049
6530
|
const backend = await getBackend();
|
|
6050
6531
|
for (const [platform, models] of Object.entries(legacy)) {
|
|
@@ -6054,32 +6535,32 @@ async function migrateLegacyOnce() {
|
|
|
6054
6535
|
await backend.setSyncState(stateToRow(platform, model, modelState));
|
|
6055
6536
|
}
|
|
6056
6537
|
}
|
|
6057
|
-
|
|
6538
|
+
fs7.unlinkSync(LEGACY_SINGLE_FILE);
|
|
6058
6539
|
} catch {
|
|
6059
6540
|
try {
|
|
6060
|
-
|
|
6541
|
+
fs7.unlinkSync(LEGACY_SINGLE_FILE);
|
|
6061
6542
|
} catch {
|
|
6062
6543
|
}
|
|
6063
6544
|
}
|
|
6064
6545
|
}
|
|
6065
|
-
if (
|
|
6546
|
+
if (fs7.existsSync(STATE_DIR)) {
|
|
6066
6547
|
try {
|
|
6067
6548
|
const backend = await getBackend();
|
|
6068
|
-
const platforms =
|
|
6549
|
+
const platforms = fs7.readdirSync(STATE_DIR);
|
|
6069
6550
|
for (const platform of platforms) {
|
|
6070
|
-
const platformDir =
|
|
6551
|
+
const platformDir = path7.join(STATE_DIR, platform);
|
|
6071
6552
|
let entries;
|
|
6072
6553
|
try {
|
|
6073
|
-
entries =
|
|
6554
|
+
entries = fs7.readdirSync(platformDir);
|
|
6074
6555
|
} catch {
|
|
6075
6556
|
continue;
|
|
6076
6557
|
}
|
|
6077
6558
|
for (const entry of entries) {
|
|
6078
6559
|
if (!entry.endsWith(".json")) continue;
|
|
6079
6560
|
const model = entry.slice(0, -".json".length);
|
|
6080
|
-
const filePath =
|
|
6561
|
+
const filePath = path7.join(platformDir, entry);
|
|
6081
6562
|
try {
|
|
6082
|
-
const raw =
|
|
6563
|
+
const raw = fs7.readFileSync(filePath, "utf-8");
|
|
6083
6564
|
const modelState = JSON.parse(raw);
|
|
6084
6565
|
const existing = await backend.getSyncState(platform, model);
|
|
6085
6566
|
if (!existing) {
|
|
@@ -6089,7 +6570,7 @@ async function migrateLegacyOnce() {
|
|
|
6089
6570
|
}
|
|
6090
6571
|
}
|
|
6091
6572
|
}
|
|
6092
|
-
|
|
6573
|
+
fs7.rmSync(STATE_DIR, { recursive: true, force: true });
|
|
6093
6574
|
} catch {
|
|
6094
6575
|
}
|
|
6095
6576
|
}
|
|
@@ -6133,12 +6614,12 @@ async function removeModelState(platform, model) {
|
|
|
6133
6614
|
}
|
|
6134
6615
|
|
|
6135
6616
|
// src/lib/memory/sync/lock.ts
|
|
6136
|
-
import
|
|
6137
|
-
import
|
|
6138
|
-
var LOCK_DIR_REL =
|
|
6617
|
+
import fs8 from "fs";
|
|
6618
|
+
import path8 from "path";
|
|
6619
|
+
var LOCK_DIR_REL = path8.join(".one", "sync", "locks");
|
|
6139
6620
|
var STALE_MS = 30 * 60 * 1e3;
|
|
6140
6621
|
function lockPath(platform, model) {
|
|
6141
|
-
return
|
|
6622
|
+
return path8.join(LOCK_DIR_REL, `${platform}_${model}`);
|
|
6142
6623
|
}
|
|
6143
6624
|
function isProcessAlive(pid) {
|
|
6144
6625
|
try {
|
|
@@ -6149,21 +6630,21 @@ function isProcessAlive(pid) {
|
|
|
6149
6630
|
}
|
|
6150
6631
|
}
|
|
6151
6632
|
var SyncLockError = class extends Error {
|
|
6152
|
-
constructor(
|
|
6153
|
-
super(
|
|
6633
|
+
constructor(message2) {
|
|
6634
|
+
super(message2);
|
|
6154
6635
|
this.name = "SyncLockError";
|
|
6155
6636
|
}
|
|
6156
6637
|
};
|
|
6157
6638
|
function acquireSyncLock(platform, model) {
|
|
6158
|
-
|
|
6639
|
+
fs8.mkdirSync(LOCK_DIR_REL, { recursive: true });
|
|
6159
6640
|
const dir = lockPath(platform, model);
|
|
6160
|
-
const pidFile =
|
|
6641
|
+
const pidFile = path8.join(dir, "pid");
|
|
6161
6642
|
try {
|
|
6162
|
-
|
|
6643
|
+
fs8.mkdirSync(dir);
|
|
6163
6644
|
} catch (err) {
|
|
6164
6645
|
const stat = (() => {
|
|
6165
6646
|
try {
|
|
6166
|
-
return
|
|
6647
|
+
return fs8.statSync(dir);
|
|
6167
6648
|
} catch {
|
|
6168
6649
|
return null;
|
|
6169
6650
|
}
|
|
@@ -6172,7 +6653,7 @@ function acquireSyncLock(platform, model) {
|
|
|
6172
6653
|
const age = Date.now() - stat.mtimeMs;
|
|
6173
6654
|
let ownerPid = null;
|
|
6174
6655
|
try {
|
|
6175
|
-
const raw =
|
|
6656
|
+
const raw = fs8.readFileSync(pidFile, "utf-8");
|
|
6176
6657
|
const parsed = parseInt(raw.trim(), 10);
|
|
6177
6658
|
if (!isNaN(parsed)) ownerPid = parsed;
|
|
6178
6659
|
} catch {
|
|
@@ -6181,8 +6662,8 @@ function acquireSyncLock(platform, model) {
|
|
|
6181
6662
|
const veryOld = age > STALE_MS;
|
|
6182
6663
|
if (ownerDead || veryOld) {
|
|
6183
6664
|
try {
|
|
6184
|
-
|
|
6185
|
-
|
|
6665
|
+
fs8.rmSync(dir, { recursive: true, force: true });
|
|
6666
|
+
fs8.mkdirSync(dir);
|
|
6186
6667
|
} catch {
|
|
6187
6668
|
throw new SyncLockError(
|
|
6188
6669
|
`Could not take over stale lock at ${dir}. Remove it manually if no sync is running.`
|
|
@@ -6199,13 +6680,13 @@ function acquireSyncLock(platform, model) {
|
|
|
6199
6680
|
}
|
|
6200
6681
|
}
|
|
6201
6682
|
try {
|
|
6202
|
-
|
|
6683
|
+
fs8.writeFileSync(pidFile, String(process.pid));
|
|
6203
6684
|
} catch {
|
|
6204
6685
|
}
|
|
6205
6686
|
return {
|
|
6206
6687
|
release() {
|
|
6207
6688
|
try {
|
|
6208
|
-
|
|
6689
|
+
fs8.rmSync(dir, { recursive: true, force: true });
|
|
6209
6690
|
} catch {
|
|
6210
6691
|
}
|
|
6211
6692
|
}
|
|
@@ -6214,9 +6695,9 @@ function acquireSyncLock(platform, model) {
|
|
|
6214
6695
|
|
|
6215
6696
|
// src/lib/memory/sync/hooks.ts
|
|
6216
6697
|
import { spawn as spawn2 } from "child_process";
|
|
6217
|
-
import
|
|
6218
|
-
import
|
|
6219
|
-
var EVENTS_DIR =
|
|
6698
|
+
import fs9 from "fs";
|
|
6699
|
+
import path9 from "path";
|
|
6700
|
+
var EVENTS_DIR = path9.join(".one", "sync", "events");
|
|
6220
6701
|
function classifyRecords(db, model, records, idField, tableExists2) {
|
|
6221
6702
|
if (!tableExists2 || records.length === 0) {
|
|
6222
6703
|
return { inserts: records, updates: [] };
|
|
@@ -6262,10 +6743,10 @@ async function fireHooks(hookCommand, events) {
|
|
|
6262
6743
|
function appendEventLog(events) {
|
|
6263
6744
|
if (events.length === 0) return;
|
|
6264
6745
|
const { platform, model } = events[0];
|
|
6265
|
-
|
|
6266
|
-
const logPath =
|
|
6746
|
+
fs9.mkdirSync(EVENTS_DIR, { recursive: true });
|
|
6747
|
+
const logPath = path9.join(EVENTS_DIR, `${platform}_${model}.jsonl`);
|
|
6267
6748
|
const lines = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
6268
|
-
|
|
6749
|
+
fs9.appendFileSync(logPath, lines);
|
|
6269
6750
|
}
|
|
6270
6751
|
function runShellHook(command, events) {
|
|
6271
6752
|
return new Promise((resolve) => {
|
|
@@ -6369,8 +6850,8 @@ function sleep(ms) {
|
|
|
6369
6850
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6370
6851
|
}
|
|
6371
6852
|
function interpolate(template, record) {
|
|
6372
|
-
return template.replace(/\{?\{(\w+(?:\.\w+)*)\}\}?/g, (_,
|
|
6373
|
-
const parts =
|
|
6853
|
+
return template.replace(/\{?\{(\w+(?:\.\w+)*)\}\}?/g, (_, path15) => {
|
|
6854
|
+
const parts = path15.split(".");
|
|
6374
6855
|
let value = record;
|
|
6375
6856
|
for (const part2 of parts) {
|
|
6376
6857
|
if (typeof value !== "object" || value === null) return "";
|
|
@@ -6398,8 +6879,8 @@ function deepMerge(target, source) {
|
|
|
6398
6879
|
}
|
|
6399
6880
|
return result;
|
|
6400
6881
|
}
|
|
6401
|
-
function getByDotPath2(obj,
|
|
6402
|
-
const parts =
|
|
6882
|
+
function getByDotPath2(obj, path15) {
|
|
6883
|
+
const parts = path15.split(".");
|
|
6403
6884
|
let current = obj;
|
|
6404
6885
|
for (const part2 of parts) {
|
|
6405
6886
|
if (current === null || current === void 0 || typeof current !== "object") return void 0;
|
|
@@ -6408,8 +6889,8 @@ function getByDotPath2(obj, path13) {
|
|
|
6408
6889
|
return current;
|
|
6409
6890
|
}
|
|
6410
6891
|
function stripExcludedFields(obj, paths) {
|
|
6411
|
-
for (const
|
|
6412
|
-
stripOnePath(obj,
|
|
6892
|
+
for (const path15 of paths) {
|
|
6893
|
+
stripOnePath(obj, path15.replace(/\[\]/g, ".*").split("."));
|
|
6413
6894
|
}
|
|
6414
6895
|
}
|
|
6415
6896
|
function stripOnePath(obj, parts) {
|
|
@@ -6830,8 +7311,8 @@ function sleep2(ms) {
|
|
|
6830
7311
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6831
7312
|
}
|
|
6832
7313
|
function stripFields(record, paths) {
|
|
6833
|
-
for (const
|
|
6834
|
-
stripOnePath2(record,
|
|
7314
|
+
for (const path15 of paths) {
|
|
7315
|
+
stripOnePath2(record, path15.split("."));
|
|
6835
7316
|
}
|
|
6836
7317
|
}
|
|
6837
7318
|
function stripOnePath2(obj, parts) {
|
|
@@ -7924,19 +8405,19 @@ function inferProfileFromKnowledge(knowledge, modelName, platform) {
|
|
|
7924
8405
|
|
|
7925
8406
|
// src/lib/memory/sync/schedule.ts
|
|
7926
8407
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
7927
|
-
import
|
|
8408
|
+
import fs11 from "fs";
|
|
7928
8409
|
import os from "os";
|
|
7929
|
-
import
|
|
8410
|
+
import path11 from "path";
|
|
7930
8411
|
|
|
7931
8412
|
// src/lib/memory/sync/schedule-registry.ts
|
|
7932
|
-
import
|
|
7933
|
-
import
|
|
7934
|
-
var REGISTRY_DIR = () =>
|
|
7935
|
-
var REGISTRY_FILE = () =>
|
|
8413
|
+
import fs10 from "fs";
|
|
8414
|
+
import path10 from "path";
|
|
8415
|
+
var REGISTRY_DIR = () => path10.join(homeDir(), ".one", "sync");
|
|
8416
|
+
var REGISTRY_FILE = () => path10.join(REGISTRY_DIR(), "schedules.json");
|
|
7936
8417
|
function readRaw() {
|
|
7937
8418
|
try {
|
|
7938
|
-
if (!
|
|
7939
|
-
const raw =
|
|
8419
|
+
if (!fs10.existsSync(REGISTRY_FILE())) return { schedules: [] };
|
|
8420
|
+
const raw = fs10.readFileSync(REGISTRY_FILE(), "utf-8");
|
|
7940
8421
|
const parsed = JSON.parse(raw);
|
|
7941
8422
|
if (!parsed || !Array.isArray(parsed.schedules)) return { schedules: [] };
|
|
7942
8423
|
return parsed;
|
|
@@ -7945,13 +8426,13 @@ function readRaw() {
|
|
|
7945
8426
|
}
|
|
7946
8427
|
}
|
|
7947
8428
|
function writeRaw(file) {
|
|
7948
|
-
|
|
8429
|
+
fs10.mkdirSync(REGISTRY_DIR(), { recursive: true });
|
|
7949
8430
|
const tmp = REGISTRY_FILE() + ".tmp";
|
|
7950
|
-
|
|
7951
|
-
|
|
8431
|
+
fs10.writeFileSync(tmp, JSON.stringify(file, null, 2));
|
|
8432
|
+
fs10.renameSync(tmp, REGISTRY_FILE());
|
|
7952
8433
|
}
|
|
7953
8434
|
function makeScheduleId(platform, cwd) {
|
|
7954
|
-
const slug =
|
|
8435
|
+
const slug = path10.basename(cwd).replace(/[^a-zA-Z0-9_-]/g, "-").toLowerCase();
|
|
7955
8436
|
return `${platform}-${slug}`;
|
|
7956
8437
|
}
|
|
7957
8438
|
function listRegistered() {
|
|
@@ -7985,7 +8466,7 @@ function removeRegistered(id) {
|
|
|
7985
8466
|
|
|
7986
8467
|
// src/lib/memory/sync/schedule.ts
|
|
7987
8468
|
var MARKER = "# one-sync";
|
|
7988
|
-
var LOG_DIR_REL =
|
|
8469
|
+
var LOG_DIR_REL = path11.join(".one", "sync", "logs");
|
|
7989
8470
|
function durationToCron(every) {
|
|
7990
8471
|
const match = every.match(/^(\d+)([mhd])$/);
|
|
7991
8472
|
if (!match) return null;
|
|
@@ -8023,8 +8504,8 @@ function isWindows() {
|
|
|
8023
8504
|
function resolveOneBinary() {
|
|
8024
8505
|
try {
|
|
8025
8506
|
const entry = process.argv[1];
|
|
8026
|
-
if (entry &&
|
|
8027
|
-
return
|
|
8507
|
+
if (entry && fs11.existsSync(entry)) {
|
|
8508
|
+
return fs11.realpathSync(entry);
|
|
8028
8509
|
}
|
|
8029
8510
|
} catch {
|
|
8030
8511
|
}
|
|
@@ -8102,7 +8583,7 @@ function migrateLegacyCronEntries() {
|
|
|
8102
8583
|
const modelsMatch = command.match(/--models\s+(\S+)/);
|
|
8103
8584
|
const models = modelsMatch ? modelsMatch[1].split(",") : void 0;
|
|
8104
8585
|
const logMatch = command.match(/>>\s+"([^"]+)"/);
|
|
8105
|
-
const logFile = logMatch ? logMatch[1] :
|
|
8586
|
+
const logFile = logMatch ? logMatch[1] : path11.resolve(cwd, LOG_DIR_REL, `${platform}.log`);
|
|
8106
8587
|
const id = makeScheduleId(platform, cwd);
|
|
8107
8588
|
if (registeredIds.has(id)) continue;
|
|
8108
8589
|
upsertRegistered({
|
|
@@ -8136,9 +8617,9 @@ function addSchedule(opts) {
|
|
|
8136
8617
|
const cwd = process.cwd();
|
|
8137
8618
|
const id = makeScheduleId(opts.platform, cwd);
|
|
8138
8619
|
const replaced = getRegistered(id) !== void 0;
|
|
8139
|
-
const logDir =
|
|
8140
|
-
|
|
8141
|
-
const logFile =
|
|
8620
|
+
const logDir = path11.join(cwd, LOG_DIR_REL);
|
|
8621
|
+
fs11.mkdirSync(logDir, { recursive: true });
|
|
8622
|
+
const logFile = path11.join(logDir, `${opts.platform}.log`);
|
|
8142
8623
|
const entry = {
|
|
8143
8624
|
id,
|
|
8144
8625
|
platform: opts.platform,
|
|
@@ -8192,15 +8673,15 @@ function removeSchedule(idOrPlatform, options) {
|
|
|
8192
8673
|
function scheduleStatus() {
|
|
8193
8674
|
const entries = listSchedules();
|
|
8194
8675
|
return entries.map((entry) => {
|
|
8195
|
-
const logExists =
|
|
8196
|
-
const logSize = logExists ?
|
|
8676
|
+
const logExists = fs11.existsSync(entry.logFile);
|
|
8677
|
+
const logSize = logExists ? fs11.statSync(entry.logFile).size : 0;
|
|
8197
8678
|
let logTail = [];
|
|
8198
8679
|
let lastRunAt = null;
|
|
8199
8680
|
if (logExists) {
|
|
8200
8681
|
try {
|
|
8201
|
-
lastRunAt =
|
|
8682
|
+
lastRunAt = fs11.statSync(entry.logFile).mtime.toISOString();
|
|
8202
8683
|
if (logSize > 0) {
|
|
8203
|
-
const content =
|
|
8684
|
+
const content = fs11.readFileSync(entry.logFile, "utf-8");
|
|
8204
8685
|
logTail = content.trim().split("\n").slice(-10);
|
|
8205
8686
|
}
|
|
8206
8687
|
} catch {
|
|
@@ -8208,8 +8689,8 @@ function scheduleStatus() {
|
|
|
8208
8689
|
}
|
|
8209
8690
|
let drift = "ok";
|
|
8210
8691
|
if (!entry.cronInstalled) drift = "missing-cron";
|
|
8211
|
-
else if (!
|
|
8212
|
-
else if (!
|
|
8692
|
+
else if (!fs11.existsSync(entry.nodeBin)) drift = "stale-node-bin";
|
|
8693
|
+
else if (!fs11.existsSync(entry.cliBin)) drift = "stale-cli-bin";
|
|
8213
8694
|
return { entry, logExists, logSize, logTail, lastRunAt, drift };
|
|
8214
8695
|
});
|
|
8215
8696
|
}
|
|
@@ -8482,11 +8963,11 @@ function isNoise(s) {
|
|
|
8482
8963
|
if (/^-?\d+(\.\d+)?([eE][-+]?\d+)?$/.test(s)) return true;
|
|
8483
8964
|
return false;
|
|
8484
8965
|
}
|
|
8485
|
-
function recordSample(stats,
|
|
8486
|
-
let s = stats.get(
|
|
8966
|
+
function recordSample(stats, path15, value, jsType, recordIndex, totalSamples) {
|
|
8967
|
+
let s = stats.get(path15);
|
|
8487
8968
|
if (!s) {
|
|
8488
8969
|
s = {
|
|
8489
|
-
path:
|
|
8970
|
+
path: path15,
|
|
8490
8971
|
total: totalSamples,
|
|
8491
8972
|
recordIndices: /* @__PURE__ */ new Set(),
|
|
8492
8973
|
lenSum: 0,
|
|
@@ -8495,7 +8976,7 @@ function recordSample(stats, path13, value, jsType, recordIndex, totalSamples) {
|
|
|
8495
8976
|
primaryType: jsType,
|
|
8496
8977
|
examples: []
|
|
8497
8978
|
};
|
|
8498
|
-
stats.set(
|
|
8979
|
+
stats.set(path15, s);
|
|
8499
8980
|
}
|
|
8500
8981
|
s.recordIndices.add(recordIndex);
|
|
8501
8982
|
s.lenSum += value.length;
|
|
@@ -8504,28 +8985,28 @@ function recordSample(stats, path13, value, jsType, recordIndex, totalSamples) {
|
|
|
8504
8985
|
if (s.primaryType !== jsType) s.primaryType = "mixed";
|
|
8505
8986
|
if (s.examples.length < 3 && value.length < 200) s.examples.push(value);
|
|
8506
8987
|
}
|
|
8507
|
-
function walkRecord(record,
|
|
8988
|
+
function walkRecord(record, path15, stats, recordIndex, totalSamples) {
|
|
8508
8989
|
if (record === null || record === void 0) return;
|
|
8509
8990
|
if (typeof record === "string") {
|
|
8510
8991
|
const trimmed = record.trim();
|
|
8511
|
-
if (trimmed) recordSample(stats,
|
|
8992
|
+
if (trimmed) recordSample(stats, path15, trimmed, "string", recordIndex, totalSamples);
|
|
8512
8993
|
return;
|
|
8513
8994
|
}
|
|
8514
8995
|
if (typeof record === "number" || typeof record === "boolean") {
|
|
8515
8996
|
const str = String(record);
|
|
8516
8997
|
const kind = typeof record === "number" ? "number" : "boolean";
|
|
8517
|
-
if (str) recordSample(stats,
|
|
8998
|
+
if (str) recordSample(stats, path15, str, kind, recordIndex, totalSamples);
|
|
8518
8999
|
return;
|
|
8519
9000
|
}
|
|
8520
9001
|
if (Array.isArray(record)) {
|
|
8521
|
-
const childPath =
|
|
9002
|
+
const childPath = path15 ? `${path15}[]` : "[]";
|
|
8522
9003
|
for (const item of record) walkRecord(item, childPath, stats, recordIndex, totalSamples);
|
|
8523
9004
|
return;
|
|
8524
9005
|
}
|
|
8525
9006
|
if (typeof record === "object") {
|
|
8526
9007
|
for (const [key, value] of Object.entries(record)) {
|
|
8527
9008
|
if (key.startsWith("_")) continue;
|
|
8528
|
-
const childPath =
|
|
9009
|
+
const childPath = path15 ? `${path15}.${key}` : key;
|
|
8529
9010
|
walkRecord(value, childPath, stats, recordIndex, totalSamples);
|
|
8530
9011
|
}
|
|
8531
9012
|
}
|
|
@@ -8563,7 +9044,7 @@ function suggestSearchablePaths(records, limit = 15) {
|
|
|
8563
9044
|
// src/lib/memory/sync/index.ts
|
|
8564
9045
|
import { spawn as spawn4 } from "child_process";
|
|
8565
9046
|
import * as p6 from "@clack/prompts";
|
|
8566
|
-
import
|
|
9047
|
+
import pc11 from "picocolors";
|
|
8567
9048
|
async function syncInstallCommand() {
|
|
8568
9049
|
if (await isSqliteAvailable()) {
|
|
8569
9050
|
if (isAgentMode()) {
|
|
@@ -8628,15 +9109,15 @@ async function syncDoctorCommand() {
|
|
|
8628
9109
|
return;
|
|
8629
9110
|
}
|
|
8630
9111
|
for (const c of checks) {
|
|
8631
|
-
const mark = c.ok ?
|
|
8632
|
-
console.log(` ${mark} ${c.name}${c.detail ?
|
|
9112
|
+
const mark = c.ok ? pc11.green("\u2713") : pc11.red("\u2717");
|
|
9113
|
+
console.log(` ${mark} ${c.name}${c.detail ? pc11.dim(` \u2014 ${c.detail}`) : ""}`);
|
|
8633
9114
|
}
|
|
8634
9115
|
if (!allOk) {
|
|
8635
9116
|
console.log(`
|
|
8636
|
-
${
|
|
9117
|
+
${pc11.yellow("Sync is not ready.")} Try: ${pc11.bold("one sync install")}`);
|
|
8637
9118
|
} else {
|
|
8638
9119
|
console.log(`
|
|
8639
|
-
${
|
|
9120
|
+
${pc11.green("Sync is ready.")}`);
|
|
8640
9121
|
}
|
|
8641
9122
|
}
|
|
8642
9123
|
function getApi() {
|
|
@@ -8679,11 +9160,11 @@ async function syncProfilesCommand(platform) {
|
|
|
8679
9160
|
if (p9.enrich) extras.push("enrich");
|
|
8680
9161
|
if (p9.identityKey || p9.identityKeys) extras.push("identity");
|
|
8681
9162
|
if (p9.dateFilter) extras.push("incremental");
|
|
8682
|
-
const tags = extras.length > 0 ? ` ${
|
|
8683
|
-
console.log(` ${
|
|
9163
|
+
const tags = extras.length > 0 ? ` ${pc11.dim(`[${extras.join(", ")}]`)}` : "";
|
|
9164
|
+
console.log(` ${pc11.bold(`${p9.platform}/${p9.model}`.padEnd(35))} ${p9.description}${tags}`);
|
|
8684
9165
|
}
|
|
8685
9166
|
console.log(`
|
|
8686
|
-
${profiles.length} built-in profile(s). Run ${
|
|
9167
|
+
${profiles.length} built-in profile(s). Run ${pc11.bold("one sync init <platform> <model>")} to use one.`);
|
|
8687
9168
|
}
|
|
8688
9169
|
async function syncModelsCommand(platform) {
|
|
8689
9170
|
const api = getApi();
|
|
@@ -8701,7 +9182,7 @@ async function syncModelsCommand(platform) {
|
|
|
8701
9182
|
return;
|
|
8702
9183
|
}
|
|
8703
9184
|
const lines = models.map(
|
|
8704
|
-
(m) => ` ${
|
|
9185
|
+
(m) => ` ${pc11.bold(m.name.padEnd(30))} ${pc11.dim(m.listAction.method)} ${pc11.dim(m.listAction.path)}`
|
|
8705
9186
|
);
|
|
8706
9187
|
note(lines.join("\n"), `${platform} \u2014 ${models.length} models`);
|
|
8707
9188
|
} catch (err) {
|
|
@@ -8805,15 +9286,15 @@ async function syncInitCommand(platform, model, options) {
|
|
|
8805
9286
|
note(JSON.stringify(template, null, 2), "Sync profile template");
|
|
8806
9287
|
if (inferred && inferred.reasoning.length > 0) {
|
|
8807
9288
|
console.log(`
|
|
8808
|
-
${
|
|
8809
|
-
for (const r of inferred.reasoning) console.log(` ${
|
|
9289
|
+
${pc11.bold("Inferred from knowledge:")}`);
|
|
9290
|
+
for (const r of inferred.reasoning) console.log(` ${pc11.dim("\u2022")} ${r}`);
|
|
8810
9291
|
}
|
|
8811
9292
|
if (testReport) {
|
|
8812
9293
|
console.log(`
|
|
8813
|
-
${
|
|
9294
|
+
${pc11.bold("Test results:")}`);
|
|
8814
9295
|
for (const c of testReport.checks) {
|
|
8815
|
-
const mark = c.ok ?
|
|
8816
|
-
console.log(` ${mark} ${c.name}${c.detail ?
|
|
9296
|
+
const mark = c.ok ? pc11.green("\u2713") : pc11.red("\u2717");
|
|
9297
|
+
console.log(` ${mark} ${c.name}${c.detail ? pc11.dim(` \u2014 ${c.detail}`) : ""}`);
|
|
8817
9298
|
}
|
|
8818
9299
|
}
|
|
8819
9300
|
console.log(`
|
|
@@ -8896,17 +9377,17 @@ async function syncTestCommand(platformModel, options = {}) {
|
|
|
8896
9377
|
return;
|
|
8897
9378
|
}
|
|
8898
9379
|
for (const c of report2.checks) {
|
|
8899
|
-
const mark = c.ok ?
|
|
8900
|
-
console.log(` ${mark} ${c.name}${c.detail ?
|
|
9380
|
+
const mark = c.ok ? pc11.green("\u2713") : pc11.red("\u2717");
|
|
9381
|
+
console.log(` ${mark} ${c.name}${c.detail ? pc11.dim(` \u2014 ${c.detail}`) : ""}`);
|
|
8901
9382
|
}
|
|
8902
9383
|
if (report2.detectedColumns && report2.detectedColumns.length > 0) {
|
|
8903
9384
|
console.log(`
|
|
8904
|
-
${
|
|
9385
|
+
${pc11.bold("Detected columns:")}`);
|
|
8905
9386
|
for (const col of report2.detectedColumns.slice(0, 20)) {
|
|
8906
|
-
console.log(` ${col.name.padEnd(30)} ${
|
|
9387
|
+
console.log(` ${col.name.padEnd(30)} ${pc11.dim(col.type)}`);
|
|
8907
9388
|
}
|
|
8908
9389
|
if (report2.detectedColumns.length > 20) {
|
|
8909
|
-
console.log(
|
|
9390
|
+
console.log(pc11.dim(` ... and ${report2.detectedColumns.length - 20} more`));
|
|
8910
9391
|
}
|
|
8911
9392
|
}
|
|
8912
9393
|
if (report2.identityKeysPreview) {
|
|
@@ -8914,45 +9395,45 @@ async function syncTestCommand(platformModel, options = {}) {
|
|
|
8914
9395
|
const total = perRecord.reduce((a, b) => a + b, 0);
|
|
8915
9396
|
const min = perRecord.length ? Math.min(...perRecord) : 0;
|
|
8916
9397
|
const max = perRecord.length ? Math.max(...perRecord) : 0;
|
|
8917
|
-
const mark = total === 0 ?
|
|
9398
|
+
const mark = total === 0 ? pc11.yellow("~") : pc11.green("\u2713");
|
|
8918
9399
|
console.log(`
|
|
8919
|
-
${
|
|
9400
|
+
${pc11.bold("Merge + identity keys")} ${pc11.dim(`(cross-platform \u2014 #128)`)}`);
|
|
8920
9401
|
console.log(` ${mark} ${perRecord.length} sample${perRecord.length === 1 ? "" : "s"}, ${min}\u2013${max} key${max === 1 ? "" : "s"} per record`);
|
|
8921
9402
|
if (sampleKeys.length > 0) {
|
|
8922
|
-
console.log(` ${
|
|
9403
|
+
console.log(` ${pc11.dim("e.g.")} ${sampleKeys.slice(0, 8).map((k) => pc11.cyan(k)).join(", ")}`);
|
|
8923
9404
|
} else if (resolvesAfterEnrich) {
|
|
8924
|
-
console.log(` ${
|
|
9405
|
+
console.log(` ${pc11.dim("note:")} 0 on these list-shape samples \u2014 this profile enriches, and its identityKeys paths resolve in the enrich phase.`);
|
|
8925
9406
|
} else {
|
|
8926
|
-
console.log(` ${
|
|
9407
|
+
console.log(` ${pc11.yellow("note:")} no identity keys resolved on these samples \u2014 check the identityKey/identityKeys paths.`);
|
|
8927
9408
|
}
|
|
8928
9409
|
if (entityFanOut) {
|
|
8929
|
-
console.log(` ${
|
|
8930
|
-
console.log(` ${
|
|
8931
|
-
console.log(` ${
|
|
8932
|
-
console.log(` ${
|
|
9410
|
+
console.log(` ${pc11.yellow("warn:")} identityKey resolved to MULTIPLE values on ${entityFanOut.count} of ${perRecord.length} samples \u2014 those records get NO merge key.`);
|
|
9411
|
+
console.log(` ${pc11.dim("saw:")} ${entityFanOut.sampleValues.map((v) => pc11.cyan(v)).join(", ")}`);
|
|
9412
|
+
console.log(` ${pc11.dim('A singular identityKey means "this record IS this entity", so a fan-out has no safe answer.')}`);
|
|
9413
|
+
console.log(` ${pc11.dim("Point it at a single-valued path, or move it to identityKeys[] for participant associations.")}`);
|
|
8933
9414
|
}
|
|
8934
9415
|
}
|
|
8935
9416
|
if (searchablePreview) {
|
|
8936
9417
|
console.log(`
|
|
8937
|
-
${
|
|
8938
|
-
console.log(` ${
|
|
8939
|
-
console.log(` ${
|
|
9418
|
+
${pc11.bold("Searchable preview")} ${pc11.dim(`(${searchablePreview.mode}, ${searchablePreview.sampledRecords} sample${searchablePreview.sampledRecords === 1 ? "" : "s"})`)}`);
|
|
9419
|
+
console.log(` ${pc11.dim("length:")} ${searchablePreview.length} chars (first sample)`);
|
|
9420
|
+
console.log(` ${pc11.dim("text:")} ${searchablePreview.text.slice(0, 300)}${searchablePreview.length > 300 ? pc11.dim(" \u2026") : ""}`);
|
|
8940
9421
|
if (searchablePreview.paths) {
|
|
8941
|
-
console.log(` ${
|
|
9422
|
+
console.log(` ${pc11.dim("paths:")} ${pc11.dim('(hit rate across all samples \u2014 differentiates "wrong path" from "field sometimes missing")')}`);
|
|
8942
9423
|
for (const p9 of searchablePreview.paths) {
|
|
8943
9424
|
const rate = `${p9.hits}/${p9.total}`;
|
|
8944
|
-
const mark = p9.hits === p9.total ?
|
|
8945
|
-
const trailer = p9.sample ?
|
|
9425
|
+
const mark = p9.hits === p9.total ? pc11.green("\u2713") : p9.hits === 0 ? pc11.red("\u2717") : pc11.yellow("~");
|
|
9426
|
+
const trailer = p9.sample ? pc11.dim(` \u2192 "${p9.sample}"`) : p9.hits === 0 ? pc11.dim(" (no sample matched \u2014 typo, or field never populated in this page)") : "";
|
|
8946
9427
|
console.log(` ${mark} ${rate.padStart(5)} ${p9.path}${trailer}`);
|
|
8947
9428
|
}
|
|
8948
9429
|
} else {
|
|
8949
|
-
console.log(` ${
|
|
8950
|
-
console.log(` ${
|
|
9430
|
+
console.log(` ${pc11.yellow("note:")} no memory.searchable declared \u2014 using the default walker (walks every field, often noisy).`);
|
|
9431
|
+
console.log(` ${pc11.dim("tip:")} run \`one sync suggest-searchable ${platform}/${model}\` for an auto-ranked starter list, or pick paths by hand and add them to profile.memory.searchable.`);
|
|
8951
9432
|
}
|
|
8952
9433
|
}
|
|
8953
9434
|
console.log(
|
|
8954
9435
|
`
|
|
8955
|
-
${report2.ok ?
|
|
9436
|
+
${report2.ok ? pc11.green("Profile looks good.") : pc11.red("Profile has issues.")} ` + (report2.ok ? `Run: ${pc11.bold(`one sync run ${platform} --models ${model}`)}` : "Fix the issues above and test again.")
|
|
8956
9437
|
);
|
|
8957
9438
|
}
|
|
8958
9439
|
function buildSearchablePreview(profile, samples) {
|
|
@@ -8960,7 +9441,7 @@ function buildSearchablePreview(profile, samples) {
|
|
|
8960
9441
|
const first = samples[0];
|
|
8961
9442
|
const paths = getSearchablePaths(profile);
|
|
8962
9443
|
if (paths) {
|
|
8963
|
-
const perPathAgg = paths.map((
|
|
9444
|
+
const perPathAgg = paths.map((path15) => ({ path: path15, hits: 0, total: samples.length, sample: "" }));
|
|
8964
9445
|
for (const record of samples) {
|
|
8965
9446
|
const { paths: perPath } = extractSearchableFromPaths(record, paths);
|
|
8966
9447
|
perPath.forEach((p9, i) => {
|
|
@@ -8993,8 +9474,8 @@ async function syncRunCommand(platform, options) {
|
|
|
8993
9474
|
if (profileDrift.length > 0 && !isAgentMode()) {
|
|
8994
9475
|
for (const d of profileDrift) {
|
|
8995
9476
|
console.log(
|
|
8996
|
-
` ${
|
|
8997
|
-
Run ${
|
|
9477
|
+
` ${pc11.yellow("!")} ${platform}/${d.model} is missing ${d.missing.map((f) => pc11.bold(f)).join(", ")} from the current built-in profile.
|
|
9478
|
+
Run ${pc11.bold(`one sync init ${platform} ${d.model}`)} to pick ${d.missing.length > 1 ? "them" : "it"} up.`
|
|
8998
9479
|
);
|
|
8999
9480
|
}
|
|
9000
9481
|
}
|
|
@@ -9037,21 +9518,21 @@ async function syncRunCommand(platform, options) {
|
|
|
9037
9518
|
return;
|
|
9038
9519
|
}
|
|
9039
9520
|
for (const r of results) {
|
|
9040
|
-
const status2 = r.status === "complete" ?
|
|
9041
|
-
console.log(` ${
|
|
9521
|
+
const status2 = r.status === "complete" ? pc11.green("complete") : r.status === "dry-run" ? pc11.yellow("dry-run") : pc11.red("failed");
|
|
9522
|
+
console.log(` ${pc11.bold(r.model)} \u2014 ${r.recordsSynced} records, ${r.pagesProcessed} pages, ${r.duration} [${status2}]`);
|
|
9042
9523
|
if (r.reconcileSkipped) {
|
|
9043
|
-
console.log(` ${
|
|
9524
|
+
console.log(` ${pc11.yellow("--full-refresh reconcile skipped \u2014 pagination truncated (e.g. --max-pages). Re-run without the cap to prune stale rows.")}`);
|
|
9044
9525
|
}
|
|
9045
9526
|
const sc = r.statusCounts;
|
|
9046
9527
|
if (sc && (sc.archived > 0 || sc.active > 0)) {
|
|
9047
|
-
const archivedColor = sc.archived > sc.active ?
|
|
9048
|
-
console.log(` memory: ${
|
|
9528
|
+
const archivedColor = sc.archived > sc.active ? pc11.red : pc11.dim;
|
|
9529
|
+
console.log(` memory: ${pc11.green(String(sc.active))} active, ${archivedColor(String(sc.archived))} archived`);
|
|
9049
9530
|
}
|
|
9050
9531
|
if (r.error) {
|
|
9051
9532
|
const errParts = [r.error.message];
|
|
9052
9533
|
if (r.error.httpStatus) errParts.push(`HTTP ${r.error.httpStatus}`);
|
|
9053
9534
|
if (r.error.retryAfter) errParts.push(`retry after ${r.error.retryAfter}s`);
|
|
9054
|
-
console.log(` ${
|
|
9535
|
+
console.log(` ${pc11.red(errParts.join(" \u2014 "))}`);
|
|
9055
9536
|
}
|
|
9056
9537
|
}
|
|
9057
9538
|
}
|
|
@@ -9077,8 +9558,8 @@ async function syncQueryCommand(platformModel, options) {
|
|
|
9077
9558
|
json(result);
|
|
9078
9559
|
return;
|
|
9079
9560
|
}
|
|
9080
|
-
console.log(
|
|
9081
|
-
console.log(
|
|
9561
|
+
console.log(pc11.dim(`Query: ${result.query}`));
|
|
9562
|
+
console.log(pc11.dim(`Source: local | Last sync: ${result.lastSync ?? "never"} | Age: ${result.syncAge ?? "n/a"}`));
|
|
9082
9563
|
console.log(JSON.stringify(result.results, null, 2));
|
|
9083
9564
|
console.log(`
|
|
9084
9565
|
${result.total} results`);
|
|
@@ -9100,7 +9581,7 @@ async function syncSearchCommand(query, options) {
|
|
|
9100
9581
|
return;
|
|
9101
9582
|
}
|
|
9102
9583
|
for (const r of result.results) {
|
|
9103
|
-
console.log(` ${
|
|
9584
|
+
console.log(` ${pc11.bold(`${r.platform}/${r.model}`)} ${pc11.dim(`(rank: ${r.rank.toFixed(2)})`)}`);
|
|
9104
9585
|
console.log(` ${JSON.stringify(r.record)}`);
|
|
9105
9586
|
}
|
|
9106
9587
|
console.log(`
|
|
@@ -9251,25 +9732,25 @@ async function syncSuggestSearchableCommand(platformModel, options = {}) {
|
|
|
9251
9732
|
return;
|
|
9252
9733
|
}
|
|
9253
9734
|
console.log(`
|
|
9254
|
-
${
|
|
9735
|
+
${pc11.bold("memory.searchable \u2014 ranked suggestions")} ${pc11.dim(`(${samples.length} samples)`)}`);
|
|
9255
9736
|
if (suggestions.length === 0) {
|
|
9256
|
-
console.log(` ${
|
|
9737
|
+
console.log(` ${pc11.yellow("no high-signal leaves found")} \u2014 page may be all UUIDs / timestamps / enum markers. Inspect the sample with \`sync test\` and pick paths manually.`);
|
|
9257
9738
|
return;
|
|
9258
9739
|
}
|
|
9259
9740
|
for (const s of suggestions) {
|
|
9260
9741
|
const hitPct = Math.round(s.hitRate * 100);
|
|
9261
9742
|
const noisePct = Math.round(s.noiseFraction * 100);
|
|
9262
|
-
const noiseBadge = noisePct > 0 ?
|
|
9743
|
+
const noiseBadge = noisePct > 0 ? pc11.dim(` noise=${noisePct}%`) : "";
|
|
9263
9744
|
console.log(
|
|
9264
|
-
` ${
|
|
9745
|
+
` ${pc11.green(String(s.score).padStart(5))} ${pc11.cyan(s.path.padEnd(52))}` + pc11.dim(` ${hitPct}% hit`) + pc11.dim(` avg=${s.avgLength}ch`) + noiseBadge + (s.sampleValue ? pc11.dim(`
|
|
9265
9746
|
\u2192 "${s.sampleValue.slice(0, 140)}"`) : "")
|
|
9266
9747
|
);
|
|
9267
9748
|
}
|
|
9268
9749
|
console.log(`
|
|
9269
|
-
${
|
|
9270
|
-
console.log(" " +
|
|
9750
|
+
${pc11.bold("Paste-ready:")}`);
|
|
9751
|
+
console.log(" " + pc11.cyan(`one sync init ${platform} ${model} --config '${JSON.stringify(configPatch)}'`));
|
|
9271
9752
|
console.log(`
|
|
9272
|
-
${
|
|
9753
|
+
${pc11.dim("then preview: ")}${pc11.cyan(`one sync test ${platform}/${model} --show-searchable`)}`);
|
|
9273
9754
|
}
|
|
9274
9755
|
async function syncListCommand(platform) {
|
|
9275
9756
|
const profiles = listProfiles(platform);
|
|
@@ -9303,10 +9784,10 @@ async function syncListCommand(platform) {
|
|
|
9303
9784
|
return;
|
|
9304
9785
|
}
|
|
9305
9786
|
for (const s of syncs) {
|
|
9306
|
-
const status2 = s.status === "idle" ?
|
|
9307
|
-
const legacy = s.legacyDbSize && s.legacyDbSize !== "0 B" ?
|
|
9787
|
+
const status2 = s.status === "idle" ? pc11.green("idle") : s.status === "syncing" ? pc11.yellow(`syncing \u2014 page ${s.pagesProcessed}`) : pc11.red("failed");
|
|
9788
|
+
const legacy = s.legacyDbSize && s.legacyDbSize !== "0 B" ? pc11.yellow(` legacy .db ${s.legacyDbSize}`) : "";
|
|
9308
9789
|
console.log(
|
|
9309
|
-
` ${
|
|
9790
|
+
` ${pc11.bold(`${s.platform}/${s.model}`.padEnd(35))} ${String(s.totalRecords).padStart(8)} records ${status2} ${pc11.dim(s.lastSync ? `last: ${s.lastSync}` : "never synced")}` + legacy
|
|
9310
9791
|
);
|
|
9311
9792
|
}
|
|
9312
9793
|
}
|
|
@@ -9385,8 +9866,8 @@ async function syncScheduleAddCommand(platform, options) {
|
|
|
9385
9866
|
}
|
|
9386
9867
|
const verb = replaced ? "Replaced existing schedule" : "Scheduled sync";
|
|
9387
9868
|
outro(
|
|
9388
|
-
`${verb} ${
|
|
9389
|
-
Logs: ${
|
|
9869
|
+
`${verb} ${pc11.bold(entry.id)} \u2014 every ${pc11.bold(entry.every)} (cron: ${pc11.dim(entry.cronExpr)})
|
|
9870
|
+
Logs: ${pc11.dim(entry.logFile)}`
|
|
9390
9871
|
);
|
|
9391
9872
|
} catch (err) {
|
|
9392
9873
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -9405,14 +9886,14 @@ async function syncScheduleListCommand() {
|
|
|
9405
9886
|
}
|
|
9406
9887
|
for (const e of entries) {
|
|
9407
9888
|
const modelsStr = e.models ? ` [${e.models.join(",")}]` : "";
|
|
9408
|
-
const installed = e.cronInstalled ?
|
|
9889
|
+
const installed = e.cronInstalled ? pc11.green("\u25CF") : pc11.red("\u2717");
|
|
9409
9890
|
console.log(
|
|
9410
|
-
` ${installed} ${
|
|
9891
|
+
` ${installed} ${pc11.bold(e.id.padEnd(32))} every ${pc11.bold(e.every.padEnd(5))} ${pc11.dim(e.cronExpr.padEnd(13))}${modelsStr}`
|
|
9411
9892
|
);
|
|
9412
|
-
console.log(` ${
|
|
9893
|
+
console.log(` ${pc11.dim("cwd:")} ${e.cwd}`);
|
|
9413
9894
|
}
|
|
9414
9895
|
console.log(`
|
|
9415
|
-
${
|
|
9896
|
+
${pc11.dim("\u25CF = cron line installed \u2717 = registry drift, run `sync schedule repair <id>`")}`);
|
|
9416
9897
|
} catch (err) {
|
|
9417
9898
|
error(err instanceof Error ? err.message : String(err));
|
|
9418
9899
|
}
|
|
@@ -9436,7 +9917,7 @@ async function syncScheduleRemoveCommand(idOrPlatform, options) {
|
|
|
9436
9917
|
return;
|
|
9437
9918
|
}
|
|
9438
9919
|
for (const r of result.removed) {
|
|
9439
|
-
console.log(` ${
|
|
9920
|
+
console.log(` ${pc11.green("\u2713")} removed ${pc11.bold(r.id)} ${pc11.dim(`(${r.cwd})`)}`);
|
|
9440
9921
|
}
|
|
9441
9922
|
} catch (err) {
|
|
9442
9923
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -9454,21 +9935,21 @@ async function syncScheduleStatusCommand() {
|
|
|
9454
9935
|
return;
|
|
9455
9936
|
}
|
|
9456
9937
|
for (const s of statuses) {
|
|
9457
|
-
const driftMarker = s.drift === "ok" ?
|
|
9458
|
-
console.log(` ${driftMarker} ${
|
|
9459
|
-
console.log(` ${
|
|
9460
|
-
console.log(` ${
|
|
9461
|
-
console.log(` ${
|
|
9938
|
+
const driftMarker = s.drift === "ok" ? pc11.green("\u25CF") : s.drift === "missing-cron" ? pc11.red("\u2717 missing cron line") : pc11.yellow(`\u26A0 ${s.drift}`);
|
|
9939
|
+
console.log(` ${driftMarker} ${pc11.bold(s.entry.id)} \u2014 every ${s.entry.every} (${pc11.dim(s.entry.cronExpr)})`);
|
|
9940
|
+
console.log(` ${pc11.dim("cwd:")} ${s.entry.cwd}`);
|
|
9941
|
+
console.log(` ${pc11.dim("last run:")} ${s.lastRunAt ?? pc11.yellow("never")}`);
|
|
9942
|
+
console.log(` ${pc11.dim("log:")} ${s.entry.logFile} ${s.logExists ? pc11.dim(`(${s.logSize} bytes)`) : pc11.yellow("(empty)")}`);
|
|
9462
9943
|
if (s.logTail.length > 0) {
|
|
9463
|
-
console.log(
|
|
9944
|
+
console.log(pc11.dim(" last lines:"));
|
|
9464
9945
|
for (const line of s.logTail.slice(-3)) {
|
|
9465
|
-
console.log(
|
|
9946
|
+
console.log(pc11.dim(` ${line}`));
|
|
9466
9947
|
}
|
|
9467
9948
|
}
|
|
9468
9949
|
}
|
|
9469
9950
|
if (statuses.some((s) => s.drift !== "ok")) {
|
|
9470
9951
|
console.log(`
|
|
9471
|
-
${
|
|
9952
|
+
${pc11.yellow("Drift detected.")} Run ${pc11.bold("one sync schedule repair <id>")} to heal.`);
|
|
9472
9953
|
}
|
|
9473
9954
|
} catch (err) {
|
|
9474
9955
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -9481,7 +9962,7 @@ async function syncScheduleRepairCommand(id) {
|
|
|
9481
9962
|
json({ status: "repaired", ...healed });
|
|
9482
9963
|
return;
|
|
9483
9964
|
}
|
|
9484
|
-
outro(`Repaired ${
|
|
9965
|
+
outro(`Repaired ${pc11.bold(healed.id)}: re-installed cron line with current node/cli paths.`);
|
|
9485
9966
|
} catch (err) {
|
|
9486
9967
|
error(err instanceof Error ? err.message : String(err));
|
|
9487
9968
|
}
|
|
@@ -9917,16 +10398,16 @@ function projectEmbeddingApiKey(cfg) {
|
|
|
9917
10398
|
}
|
|
9918
10399
|
function redactSecrets(cfg) {
|
|
9919
10400
|
const copy = JSON.parse(JSON.stringify(cfg));
|
|
9920
|
-
for (const
|
|
9921
|
-
const val = getPath(copy,
|
|
10401
|
+
for (const path15 of SECRET_PATHS) {
|
|
10402
|
+
const val = getPath(copy, path15);
|
|
9922
10403
|
if (typeof val === "string" && val.length > 0) {
|
|
9923
|
-
setPath(copy,
|
|
10404
|
+
setPath(copy, path15, `${val.slice(0, 6)}\u2026(redacted, use --show-secrets)`);
|
|
9924
10405
|
}
|
|
9925
10406
|
}
|
|
9926
10407
|
return copy;
|
|
9927
10408
|
}
|
|
9928
|
-
function getPath(obj,
|
|
9929
|
-
const parts =
|
|
10409
|
+
function getPath(obj, path15) {
|
|
10410
|
+
const parts = path15.split(".");
|
|
9930
10411
|
let cur = obj;
|
|
9931
10412
|
for (const part2 of parts) {
|
|
9932
10413
|
if (cur == null || typeof cur !== "object") return void 0;
|
|
@@ -9934,8 +10415,8 @@ function getPath(obj, path13) {
|
|
|
9934
10415
|
}
|
|
9935
10416
|
return cur;
|
|
9936
10417
|
}
|
|
9937
|
-
function setPath(obj,
|
|
9938
|
-
const parts =
|
|
10418
|
+
function setPath(obj, path15, value) {
|
|
10419
|
+
const parts = path15.split(".");
|
|
9939
10420
|
let cur = obj;
|
|
9940
10421
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
9941
10422
|
const part2 = parts[i];
|
|
@@ -9945,8 +10426,8 @@ function setPath(obj, path13, value) {
|
|
|
9945
10426
|
cur[parts[parts.length - 1]] = value;
|
|
9946
10427
|
return obj;
|
|
9947
10428
|
}
|
|
9948
|
-
function unsetPath(obj,
|
|
9949
|
-
const parts =
|
|
10429
|
+
function unsetPath(obj, path15) {
|
|
10430
|
+
const parts = path15.split(".");
|
|
9950
10431
|
let cur = obj;
|
|
9951
10432
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
9952
10433
|
const part2 = parts[i];
|
|
@@ -9972,7 +10453,7 @@ function parseValue(raw) {
|
|
|
9972
10453
|
}
|
|
9973
10454
|
|
|
9974
10455
|
// src/commands/mem/records.ts
|
|
9975
|
-
import
|
|
10456
|
+
import pc12 from "picocolors";
|
|
9976
10457
|
async function memAddCommand(type, dataRaw, flags) {
|
|
9977
10458
|
requireMemoryInit();
|
|
9978
10459
|
const data = parseJsonArg(dataRaw, "data");
|
|
@@ -10171,7 +10652,7 @@ function summarizeRecord(r) {
|
|
|
10171
10652
|
const v = d[field];
|
|
10172
10653
|
if (typeof v === "string" && v.trim()) return v.trim().slice(0, 80);
|
|
10173
10654
|
}
|
|
10174
|
-
return
|
|
10655
|
+
return pc12.dim("(untitled)");
|
|
10175
10656
|
}
|
|
10176
10657
|
function relativeTime(iso) {
|
|
10177
10658
|
if (!iso) return "";
|
|
@@ -10234,7 +10715,7 @@ async function memFindByKeyCommand(key, secondKey, flags) {
|
|
|
10234
10715
|
});
|
|
10235
10716
|
return;
|
|
10236
10717
|
}
|
|
10237
|
-
const label = matchedKeys.map((k) =>
|
|
10718
|
+
const label = matchedKeys.map((k) => pc12.cyan(k)).join(pc12.dim(" + "));
|
|
10238
10719
|
if (records.length === 0) {
|
|
10239
10720
|
console.log(`
|
|
10240
10721
|
No records linked to ${label}.
|
|
@@ -10242,27 +10723,27 @@ async function memFindByKeyCommand(key, secondKey, flags) {
|
|
|
10242
10723
|
return;
|
|
10243
10724
|
}
|
|
10244
10725
|
console.log();
|
|
10245
|
-
console.log(` ${label} ${
|
|
10726
|
+
console.log(` ${label} ${pc12.dim("\u2014")} ${records.length}${truncated ? "+" : ""} record${records.length === 1 ? "" : "s"} across ${byType.size} type${byType.size === 1 ? "" : "s"}`);
|
|
10246
10727
|
console.log();
|
|
10247
10728
|
const typeWidth = Math.min(30, Math.max(...[...byType.keys()].map((t) => t.length)));
|
|
10248
10729
|
for (const [type, list] of byType) {
|
|
10249
|
-
console.log(` ${
|
|
10730
|
+
console.log(` ${pc12.bold(type.padEnd(typeWidth))} ${pc12.dim(`${list.length} record${list.length === 1 ? "" : "s"}`)}`);
|
|
10250
10731
|
for (const r of list.slice(0, perType)) {
|
|
10251
|
-
console.log(` ${
|
|
10732
|
+
console.log(` ${pc12.dim("\xB7")} ${summarizeRecord(r)} ${pc12.dim(relativeTime(r.updated_at))} ${pc12.dim(r.id)}`);
|
|
10252
10733
|
}
|
|
10253
10734
|
if (list.length > perType) {
|
|
10254
|
-
console.log(` ${
|
|
10735
|
+
console.log(` ${pc12.dim(`\u2026 and ${list.length - perType} more (raise --limit)`)}`);
|
|
10255
10736
|
}
|
|
10256
10737
|
}
|
|
10257
10738
|
if (truncated) {
|
|
10258
10739
|
console.log();
|
|
10259
|
-
console.log(` ${
|
|
10740
|
+
console.log(` ${pc12.yellow("\u26A0")} Stopped at ${FIND_BY_KEY_FETCH_CAP} matches \u2014 there are more, and types sorting after the last one shown are missing entirely. Re-run with --type <type> to see them.`);
|
|
10260
10741
|
}
|
|
10261
10742
|
console.log();
|
|
10262
10743
|
}
|
|
10263
10744
|
|
|
10264
10745
|
// src/commands/mem/doctor.ts
|
|
10265
|
-
import
|
|
10746
|
+
import pc13 from "picocolors";
|
|
10266
10747
|
async function memDoctorCommand() {
|
|
10267
10748
|
const checks = [];
|
|
10268
10749
|
const cfg = getMemoryConfig();
|
|
@@ -10369,23 +10850,23 @@ function emit(checks, capInfo) {
|
|
|
10369
10850
|
return;
|
|
10370
10851
|
}
|
|
10371
10852
|
for (const c of checks) {
|
|
10372
|
-
const mark = c.ok ?
|
|
10373
|
-
const detail = c.detail ?
|
|
10853
|
+
const mark = c.ok ? pc13.green("\u2713") : pc13.red("\u2717");
|
|
10854
|
+
const detail = c.detail ? pc13.dim(` \u2014 ${c.detail}`) : "";
|
|
10374
10855
|
console.log(` ${mark} ${c.name}${detail}`);
|
|
10375
10856
|
}
|
|
10376
10857
|
if (!allOk) {
|
|
10377
|
-
console.log("\n" +
|
|
10858
|
+
console.log("\n" + pc13.yellow("Memory is not fully healthy."));
|
|
10378
10859
|
process.exitCode = 1;
|
|
10379
10860
|
} else {
|
|
10380
|
-
console.log("\n" +
|
|
10861
|
+
console.log("\n" + pc13.green("Memory is healthy."));
|
|
10381
10862
|
}
|
|
10382
10863
|
const line = semanticSearchUpgradeLine({ vectorSearchAvailable: capInfo.vectorSearchAvailable });
|
|
10383
10864
|
if (line) console.log(`
|
|
10384
|
-
${
|
|
10865
|
+
${pc13.dim(line)}`);
|
|
10385
10866
|
}
|
|
10386
10867
|
|
|
10387
10868
|
// src/commands/mem/export.ts
|
|
10388
|
-
import
|
|
10869
|
+
import fs12 from "fs";
|
|
10389
10870
|
import { once } from "events";
|
|
10390
10871
|
async function writeLine(stream, line) {
|
|
10391
10872
|
if (!stream.write(line)) {
|
|
@@ -10397,7 +10878,7 @@ async function memExportCommand(outfile) {
|
|
|
10397
10878
|
const backend = await getBackend();
|
|
10398
10879
|
const stats = await backend.stats();
|
|
10399
10880
|
const toFile = !!outfile && outfile !== "-";
|
|
10400
|
-
const stream = toFile ?
|
|
10881
|
+
const stream = toFile ? fs12.createWriteStream(outfile, "utf-8") : process.stdout;
|
|
10401
10882
|
const all = await listAllTypes(backend);
|
|
10402
10883
|
const pageSize = 500;
|
|
10403
10884
|
let written = 0;
|
|
@@ -10428,9 +10909,9 @@ async function memExportCommand(outfile) {
|
|
|
10428
10909
|
}
|
|
10429
10910
|
async function memImportCommand(file) {
|
|
10430
10911
|
requireMemoryInit();
|
|
10431
|
-
if (!
|
|
10912
|
+
if (!fs12.existsSync(file)) error(`File not found: ${file}`);
|
|
10432
10913
|
const backend = await getBackend();
|
|
10433
|
-
const raw =
|
|
10914
|
+
const raw = fs12.readFileSync(file, "utf-8");
|
|
10434
10915
|
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
10435
10916
|
let inserted = 0;
|
|
10436
10917
|
let updated = 0;
|
|
@@ -10676,7 +11157,7 @@ function registerMemoryCommands(program2) {
|
|
|
10676
11157
|
}
|
|
10677
11158
|
|
|
10678
11159
|
// src/commands/cache.ts
|
|
10679
|
-
import
|
|
11160
|
+
import pc14 from "picocolors";
|
|
10680
11161
|
async function cacheClearCommand(actionId) {
|
|
10681
11162
|
if (actionId) {
|
|
10682
11163
|
const deleted = clearEntry(actionId);
|
|
@@ -10685,9 +11166,9 @@ async function cacheClearCommand(actionId) {
|
|
|
10685
11166
|
return;
|
|
10686
11167
|
}
|
|
10687
11168
|
if (deleted) {
|
|
10688
|
-
console.log(`Cleared cache for ${
|
|
11169
|
+
console.log(`Cleared cache for ${pc14.cyan(actionId)}`);
|
|
10689
11170
|
} else {
|
|
10690
|
-
console.log(`No cache entry found for ${
|
|
11171
|
+
console.log(`No cache entry found for ${pc14.dim(actionId)}`);
|
|
10691
11172
|
}
|
|
10692
11173
|
} else {
|
|
10693
11174
|
const count = clearAll();
|
|
@@ -10724,7 +11205,7 @@ async function cacheListCommand(options) {
|
|
|
10724
11205
|
type: e.type,
|
|
10725
11206
|
key: e.entry.key,
|
|
10726
11207
|
age: formatAge(getAge(e.entry)),
|
|
10727
|
-
status: isFresh(e.entry) ?
|
|
11208
|
+
status: isFresh(e.entry) ? pc14.green("fresh") : pc14.yellow("expired")
|
|
10728
11209
|
}));
|
|
10729
11210
|
printTable(
|
|
10730
11211
|
[
|
|
@@ -10777,13 +11258,13 @@ async function cacheUpdateAllCommand() {
|
|
|
10777
11258
|
if (errors.length > 0) {
|
|
10778
11259
|
console.log();
|
|
10779
11260
|
for (const e of errors) {
|
|
10780
|
-
console.log(` ${
|
|
11261
|
+
console.log(` ${pc14.red("\u2717")} ${e.key}: ${pc14.dim(e.error)}`);
|
|
10781
11262
|
}
|
|
10782
11263
|
}
|
|
10783
11264
|
}
|
|
10784
11265
|
|
|
10785
11266
|
// src/commands/guide.ts
|
|
10786
|
-
import
|
|
11267
|
+
import pc15 from "picocolors";
|
|
10787
11268
|
|
|
10788
11269
|
// src/lib/guide-content.ts
|
|
10789
11270
|
var GUIDE_OVERVIEW = `# One CLI \u2014 Agent Guide
|
|
@@ -10898,6 +11379,9 @@ one --agent relay deliveries --endpoint-id <id> # Check delivery
|
|
|
10898
11379
|
- \`--create-webhook\` auto-registers the webhook URL with the source platform
|
|
10899
11380
|
- Use \`actions find\` (and \`actions load\` for more) to learn both the incoming payload shape AND the destination API shape before building templates
|
|
10900
11381
|
|
|
11382
|
+
### One Connect apps \u2014 created from \`one.connect.ts\`
|
|
11383
|
+
\`one connect create\` reads \`one.connect.ts\`, typed by \`defineConnectApp\` from \`@withone/connect/config\` (install \`@withone/connect\` 0.19.0 or later in the project first), and opens a setup page where the human reviews the app (or, once .env holds its \`ONE_CLIENT_ID\`, its new version) and approves it with their dashboard session; an API key never creates or changes a Connect app. A new app's client secret and connect key are made and shown there once; the CLI never receives them. It waits up to 10 minutes, then writes \`ONE_CLIENT_ID\`, \`ONE_PERMISSION_SET\` and \`ONE_REDIRECT_URI\` to the env file. \`one connect types\` writes \`one-connect-env.d.ts\` so tool slugs and action names autocomplete. Each tool's \`access\` is \`read-only\`, \`read-write\`, \`full-access\`, or \`custom\` with \`actions\` (names, or ids from \`actions find\`).
|
|
11384
|
+
|
|
10901
11385
|
### 4. Memory + Sync \u2014 Unified store with hybrid FTS + semantic search
|
|
10902
11386
|
One ships a local memory store (a real Postgres process bootstrapped on demand via the bundled \`embedded-postgres\` plugin, with a \`postgres\` plugin for remote/self-hosted) that backs both user-authored notes (\`one mem add\`) and synced platform data (\`one sync run\`). Auto-initializes on first use \u2014 no separate install step. Run \`one guide memory\` and \`one guide sync\` for the full references.
|
|
10903
11387
|
|
|
@@ -10917,6 +11401,7 @@ Request specific sections:
|
|
|
10917
11401
|
- **Always use \`--agent\` flag** for structured JSON output
|
|
10918
11402
|
- Platform names are **lowercase**; multi-word names use dashes (e.g., \`hubspot\`, \`google-calendar\`)
|
|
10919
11403
|
- Always use the **exact action ID** from a find answer \u2014 don't guess
|
|
11404
|
+
- Requires Node.js 22.23.4+, 24.22.1+ or 26.12.1+. Older Node trusts the compromised eMudhra CA certificates, so every command exits 1 there with \`"code": "NODE_UPGRADE_REQUIRED"\` and an \`upgrade\` field holding the command for the user's setup. Don't retry: show the user that command and ask them to update Node.js
|
|
10920
11405
|
- Always read **knowledge** before executing any action
|
|
10921
11406
|
- Connection keys come from \`one connection list\` \u2014 don't hardcode them
|
|
10922
11407
|
- \`connection list\` also reports an \`access\` field per connection (\`full\` / \`methods\` / \`actions\`, plus a \`level\`: full, read-write, read-only, custom, none) \u2014 what One enforces for this key, narrowed by \`one config\`. Read it before planning so you don't propose an action that will be refused. \`accessSource: "local"\` means One did not report it, so it is unverified
|
|
@@ -11988,14 +12473,14 @@ async function guideCommand(topic = "all") {
|
|
|
11988
12473
|
json({ topic, title, content, availableTopics });
|
|
11989
12474
|
return;
|
|
11990
12475
|
}
|
|
11991
|
-
intro(
|
|
12476
|
+
intro(pc15.bgCyan(pc15.black(" One Guide ")));
|
|
11992
12477
|
console.log();
|
|
11993
12478
|
console.log(content);
|
|
11994
|
-
console.log(
|
|
12479
|
+
console.log(pc15.dim("\u2500".repeat(60)));
|
|
11995
12480
|
console.log(
|
|
11996
|
-
|
|
12481
|
+
pc15.dim("Available topics: ") + availableTopics.map((t) => pc15.cyan(t.topic)).join(", ")
|
|
11997
12482
|
);
|
|
11998
|
-
console.log(
|
|
12483
|
+
console.log(pc15.dim(`Run ${pc15.cyan("one guide <topic>")} for a specific section.`));
|
|
11999
12484
|
}
|
|
12000
12485
|
|
|
12001
12486
|
// src/lib/platform-meta.ts
|
|
@@ -12315,24 +12800,24 @@ function buildWorkflowIdeas(connections) {
|
|
|
12315
12800
|
}
|
|
12316
12801
|
|
|
12317
12802
|
// src/commands/logout.ts
|
|
12318
|
-
import
|
|
12803
|
+
import fs13 from "fs";
|
|
12319
12804
|
import * as p8 from "@clack/prompts";
|
|
12320
|
-
function formatWhoami(config2, apiKey,
|
|
12805
|
+
function formatWhoami(config2, apiKey, pc17) {
|
|
12321
12806
|
const whoami = config2.whoami;
|
|
12322
12807
|
const env = getEnvFromApiKey(apiKey);
|
|
12323
|
-
const envLabel = env === "test" ?
|
|
12808
|
+
const envLabel = env === "test" ? pc17.yellow("test") : pc17.green("live");
|
|
12324
12809
|
const lines = [];
|
|
12325
12810
|
if (whoami) {
|
|
12326
12811
|
const contextParts = [];
|
|
12327
12812
|
if (whoami.organization) contextParts.push(whoami.organization.name);
|
|
12328
12813
|
if (whoami.project) contextParts.push(whoami.project.name);
|
|
12329
12814
|
const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
|
|
12330
|
-
lines.push(`${
|
|
12331
|
-
lines.push(`${whoami.user.name} ${
|
|
12332
|
-
if (config2.apiKeyName) lines.push(`${
|
|
12815
|
+
lines.push(`${pc17.bold(scopeDisplay)} ${pc17.dim("\xB7")} ${envLabel}`);
|
|
12816
|
+
lines.push(`${whoami.user.name} ${pc17.dim(`(${whoami.user.email})`)}`);
|
|
12817
|
+
if (config2.apiKeyName) lines.push(`${pc17.dim("Key:")} ${config2.apiKeyName}`);
|
|
12333
12818
|
} else {
|
|
12334
12819
|
const keyLabel = config2.apiKeyName ?? `${apiKey.slice(0, 8)}...`;
|
|
12335
|
-
lines.push(`${
|
|
12820
|
+
lines.push(`${pc17.dim("Key:")} ${keyLabel} ${pc17.dim("\xB7")} ${envLabel}`);
|
|
12336
12821
|
}
|
|
12337
12822
|
return lines;
|
|
12338
12823
|
}
|
|
@@ -12350,18 +12835,18 @@ async function logoutCommand() {
|
|
|
12350
12835
|
const globalPath = getGlobalConfigPath();
|
|
12351
12836
|
const projectPath = getProjectConfigPath();
|
|
12352
12837
|
let cleared = false;
|
|
12353
|
-
if (
|
|
12354
|
-
|
|
12838
|
+
if (fs13.existsSync(projectPath)) {
|
|
12839
|
+
fs13.unlinkSync(projectPath);
|
|
12355
12840
|
cleared = true;
|
|
12356
12841
|
}
|
|
12357
|
-
if (
|
|
12358
|
-
|
|
12842
|
+
if (fs13.existsSync(globalPath)) {
|
|
12843
|
+
fs13.unlinkSync(globalPath);
|
|
12359
12844
|
cleared = true;
|
|
12360
12845
|
}
|
|
12361
12846
|
json({ status: cleared ? "logged_out" : "not_logged_in", message: cleared ? "Credentials cleared." : "No config found." });
|
|
12362
12847
|
return;
|
|
12363
12848
|
}
|
|
12364
|
-
const
|
|
12849
|
+
const pc17 = (await import("picocolors")).default;
|
|
12365
12850
|
const globalConfig = readGlobalConfig();
|
|
12366
12851
|
const projectConfig = readProjectConfig();
|
|
12367
12852
|
const hasGlobal = globalConfig?.apiKey != null;
|
|
@@ -12370,13 +12855,13 @@ async function logoutCommand() {
|
|
|
12370
12855
|
if (hasGlobal && hasProject) {
|
|
12371
12856
|
const infoLines = ["You are logged in with multiple configs.", ""];
|
|
12372
12857
|
if (projectConfig) {
|
|
12373
|
-
infoLines.push(`${
|
|
12374
|
-
infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey,
|
|
12858
|
+
infoLines.push(`${pc17.cyan("Local config:")}`);
|
|
12859
|
+
infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey, pc17));
|
|
12375
12860
|
infoLines.push("");
|
|
12376
12861
|
}
|
|
12377
12862
|
if (globalConfig) {
|
|
12378
|
-
infoLines.push(`${
|
|
12379
|
-
infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey,
|
|
12863
|
+
infoLines.push(`${pc17.magenta("Global config:")}`);
|
|
12864
|
+
infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey, pc17));
|
|
12380
12865
|
}
|
|
12381
12866
|
p8.note(infoLines.join("\n"));
|
|
12382
12867
|
const choice = await p8.select({
|
|
@@ -12394,14 +12879,14 @@ async function logoutCommand() {
|
|
|
12394
12879
|
targetScope = choice;
|
|
12395
12880
|
} else if (hasProject) {
|
|
12396
12881
|
const infoLines = ["You are logged in.", ""];
|
|
12397
|
-
infoLines.push(`${
|
|
12398
|
-
infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey,
|
|
12882
|
+
infoLines.push(`${pc17.dim("Stored in")} ${pc17.cyan("local config")}`);
|
|
12883
|
+
infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey, pc17));
|
|
12399
12884
|
p8.note(infoLines.join("\n"));
|
|
12400
12885
|
targetScope = "project";
|
|
12401
12886
|
} else if (hasGlobal) {
|
|
12402
12887
|
const infoLines = ["You are logged in.", ""];
|
|
12403
|
-
infoLines.push(`${
|
|
12404
|
-
infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey,
|
|
12888
|
+
infoLines.push(`${pc17.dim("Stored in")} ${pc17.magenta("global config")}`);
|
|
12889
|
+
infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey, pc17));
|
|
12405
12890
|
p8.note(infoLines.join("\n"));
|
|
12406
12891
|
targetScope = "global";
|
|
12407
12892
|
} else {
|
|
@@ -12424,11 +12909,11 @@ async function logoutCommand() {
|
|
|
12424
12909
|
}
|
|
12425
12910
|
if (targetScope === "project" || targetScope === "both") {
|
|
12426
12911
|
const projectPath = getProjectConfigPath();
|
|
12427
|
-
if (
|
|
12912
|
+
if (fs13.existsSync(projectPath)) fs13.unlinkSync(projectPath);
|
|
12428
12913
|
}
|
|
12429
12914
|
if (targetScope === "global" || targetScope === "both") {
|
|
12430
12915
|
const globalPath = getGlobalConfigPath();
|
|
12431
|
-
if (
|
|
12916
|
+
if (fs13.existsSync(globalPath)) fs13.unlinkSync(globalPath);
|
|
12432
12917
|
}
|
|
12433
12918
|
p8.log.success("Credentials cleared.");
|
|
12434
12919
|
p8.log.info("Your API key is still active. Manage keys at app.withone.ai/settings");
|
|
@@ -12436,11 +12921,11 @@ async function logoutCommand() {
|
|
|
12436
12921
|
}
|
|
12437
12922
|
|
|
12438
12923
|
// src/lib/project-skills.ts
|
|
12439
|
-
import
|
|
12440
|
-
import
|
|
12924
|
+
import crypto3 from "crypto";
|
|
12925
|
+
import fs14 from "fs";
|
|
12441
12926
|
import os2 from "os";
|
|
12442
|
-
import
|
|
12443
|
-
import
|
|
12927
|
+
import path12 from "path";
|
|
12928
|
+
import zlib2 from "zlib";
|
|
12444
12929
|
var OPTIONAL_SKILLS = [
|
|
12445
12930
|
{
|
|
12446
12931
|
name: "connect",
|
|
@@ -12471,42 +12956,42 @@ function findAgentTarget(id) {
|
|
|
12471
12956
|
return SKILL_AGENT_TARGETS.find((a) => a.id === id.trim().toLowerCase());
|
|
12472
12957
|
}
|
|
12473
12958
|
function findProjectRoot(start) {
|
|
12474
|
-
let dir =
|
|
12959
|
+
let dir = path12.resolve(start);
|
|
12475
12960
|
for (; ; ) {
|
|
12476
|
-
if (
|
|
12477
|
-
const parent =
|
|
12961
|
+
if (fs14.existsSync(path12.join(dir, "package.json")) || fs14.existsSync(path12.join(dir, ".git"))) return dir;
|
|
12962
|
+
const parent = path12.dirname(dir);
|
|
12478
12963
|
if (parent === dir) return null;
|
|
12479
12964
|
dir = parent;
|
|
12480
12965
|
}
|
|
12481
12966
|
}
|
|
12482
12967
|
function findInstalledPackage(start, packageName) {
|
|
12483
|
-
let dir =
|
|
12968
|
+
let dir = path12.resolve(start);
|
|
12484
12969
|
for (; ; ) {
|
|
12485
|
-
const pkgDir =
|
|
12486
|
-
const manifest =
|
|
12487
|
-
if (
|
|
12970
|
+
const pkgDir = path12.join(dir, "node_modules", ...packageName.split("/"));
|
|
12971
|
+
const manifest = path12.join(pkgDir, "package.json");
|
|
12972
|
+
if (fs14.existsSync(manifest)) {
|
|
12488
12973
|
try {
|
|
12489
|
-
const { version: version2 } = JSON.parse(
|
|
12974
|
+
const { version: version2 } = JSON.parse(fs14.readFileSync(manifest, "utf-8"));
|
|
12490
12975
|
if (version2) return { dir: pkgDir, version: version2 };
|
|
12491
12976
|
} catch {
|
|
12492
12977
|
}
|
|
12493
12978
|
}
|
|
12494
|
-
const parent =
|
|
12979
|
+
const parent = path12.dirname(dir);
|
|
12495
12980
|
if (parent === dir) return null;
|
|
12496
12981
|
dir = parent;
|
|
12497
12982
|
}
|
|
12498
12983
|
}
|
|
12499
12984
|
var SkillSourceError = class extends Error {
|
|
12500
|
-
constructor(
|
|
12501
|
-
super(
|
|
12985
|
+
constructor(message2, code) {
|
|
12986
|
+
super(message2);
|
|
12502
12987
|
this.code = code;
|
|
12503
12988
|
}
|
|
12504
12989
|
};
|
|
12505
12990
|
function projectSkillSource(skill, projectDir) {
|
|
12506
12991
|
const installed = findInstalledPackage(projectDir, skill.packageName);
|
|
12507
12992
|
if (!installed) return null;
|
|
12508
|
-
const dir =
|
|
12509
|
-
if (!
|
|
12993
|
+
const dir = path12.join(installed.dir, ...skill.packagePath.split("/"));
|
|
12994
|
+
if (!fs14.existsSync(path12.join(dir, "SKILL.md"))) {
|
|
12510
12995
|
throw new SkillSourceError(
|
|
12511
12996
|
`${skill.packageName} ${installed.version} does not include the ${skill.title} skill. Upgrade it: npm install ${skill.packageName}@latest`,
|
|
12512
12997
|
"skill-missing"
|
|
@@ -12545,15 +13030,15 @@ async function registrySkillSource(skill, fetchImpl = fetch) {
|
|
|
12545
13030
|
if (!matchesIntegrity(bytes, integrity)) {
|
|
12546
13031
|
throw new SkillSourceError(`${skill.packageName}@${version2} failed its integrity check; nothing was installed.`, "integrity");
|
|
12547
13032
|
}
|
|
12548
|
-
const files = readTarEntries(
|
|
13033
|
+
const files = readTarEntries(zlib2.gunzipSync(bytes), `package/${skill.packagePath}/`);
|
|
12549
13034
|
if (!files.some((f) => f.path === "SKILL.md")) {
|
|
12550
13035
|
throw new SkillSourceError(`${skill.packageName}@${version2} does not include the ${skill.title} skill.`, "not-in-package");
|
|
12551
13036
|
}
|
|
12552
|
-
const tmp =
|
|
13037
|
+
const tmp = fs14.mkdtempSync(path12.join(os2.tmpdir(), "one-skill-"));
|
|
12553
13038
|
for (const file of files) {
|
|
12554
|
-
const dest =
|
|
12555
|
-
|
|
12556
|
-
|
|
13039
|
+
const dest = path12.join(tmp, ...file.path.split("/"));
|
|
13040
|
+
fs14.mkdirSync(path12.dirname(dest), { recursive: true });
|
|
13041
|
+
fs14.writeFileSync(dest, file.data);
|
|
12557
13042
|
}
|
|
12558
13043
|
return {
|
|
12559
13044
|
kind: "registry",
|
|
@@ -12562,7 +13047,7 @@ async function registrySkillSource(skill, fetchImpl = fetch) {
|
|
|
12562
13047
|
origin: tarball,
|
|
12563
13048
|
cleanup: () => {
|
|
12564
13049
|
try {
|
|
12565
|
-
|
|
13050
|
+
fs14.rmSync(tmp, { recursive: true, force: true });
|
|
12566
13051
|
} catch {
|
|
12567
13052
|
}
|
|
12568
13053
|
}
|
|
@@ -12574,7 +13059,7 @@ function matchesIntegrity(bytes, integrity) {
|
|
|
12574
13059
|
if (dash < 0) return false;
|
|
12575
13060
|
const algorithm = entry.slice(0, dash);
|
|
12576
13061
|
if (!["sha512", "sha384", "sha256"].includes(algorithm)) return false;
|
|
12577
|
-
return
|
|
13062
|
+
return crypto3.createHash(algorithm).update(bytes).digest("base64") === entry.slice(dash + 1);
|
|
12578
13063
|
});
|
|
12579
13064
|
}
|
|
12580
13065
|
function readTarEntries(tar, prefix) {
|
|
@@ -12606,7 +13091,7 @@ function readTarEntries(tar, prefix) {
|
|
|
12606
13091
|
if ((type === "0" || type === "\0") && full.startsWith(prefix)) {
|
|
12607
13092
|
const rel = full.slice(prefix.length);
|
|
12608
13093
|
const parts = rel.split("/");
|
|
12609
|
-
if (rel && !
|
|
13094
|
+
if (rel && !path12.isAbsolute(rel) && !parts.includes("..") && !parts.includes("")) {
|
|
12610
13095
|
out.push({ path: rel, data: Buffer.from(body) });
|
|
12611
13096
|
}
|
|
12612
13097
|
}
|
|
@@ -12617,11 +13102,11 @@ function readTarEntries(tar, prefix) {
|
|
|
12617
13102
|
}
|
|
12618
13103
|
var SKILL_STAMP = ".one-skill.json";
|
|
12619
13104
|
function canonicalSkillDir(skill, plan) {
|
|
12620
|
-
return
|
|
13105
|
+
return path12.join(plan.root, ...UNIVERSAL_PROJECT_DIR.split("/"), skill.dirName);
|
|
12621
13106
|
}
|
|
12622
13107
|
function readStamp(dir) {
|
|
12623
13108
|
try {
|
|
12624
|
-
return JSON.parse(
|
|
13109
|
+
return JSON.parse(fs14.readFileSync(path12.join(dir, SKILL_STAMP), "utf-8"));
|
|
12625
13110
|
} catch {
|
|
12626
13111
|
return null;
|
|
12627
13112
|
}
|
|
@@ -12631,9 +13116,9 @@ function resolveAgentFolders(plan) {
|
|
|
12631
13116
|
const chosen = SKILL_AGENT_TARGETS.filter((agent) => {
|
|
12632
13117
|
if (explicit) return plan.agents.includes(agent.id);
|
|
12633
13118
|
if (plan.scope === "global") {
|
|
12634
|
-
return
|
|
13119
|
+
return fs14.existsSync(path12.join(plan.root, path12.dirname(agent.homeDir)));
|
|
12635
13120
|
}
|
|
12636
|
-
return agent.projectMarker === null ||
|
|
13121
|
+
return agent.projectMarker === null || fs14.existsSync(path12.join(plan.root, agent.projectMarker));
|
|
12637
13122
|
});
|
|
12638
13123
|
const readers = [];
|
|
12639
13124
|
const byDir = /* @__PURE__ */ new Map();
|
|
@@ -12648,33 +13133,33 @@ function resolveAgentFolders(plan) {
|
|
|
12648
13133
|
return { readers, folders: [...byDir].map(([dir, agents]) => ({ dir, agents })) };
|
|
12649
13134
|
}
|
|
12650
13135
|
function copyDir(src, dest) {
|
|
12651
|
-
|
|
12652
|
-
for (const entry of
|
|
13136
|
+
fs14.mkdirSync(dest, { recursive: true });
|
|
13137
|
+
for (const entry of fs14.readdirSync(src, { withFileTypes: true })) {
|
|
12653
13138
|
if (entry.name === SKILL_STAMP) continue;
|
|
12654
|
-
const from =
|
|
12655
|
-
const to =
|
|
13139
|
+
const from = path12.join(src, entry.name);
|
|
13140
|
+
const to = path12.join(dest, entry.name);
|
|
12656
13141
|
if (entry.isDirectory()) copyDir(from, to);
|
|
12657
|
-
else
|
|
13142
|
+
else fs14.copyFileSync(from, to);
|
|
12658
13143
|
}
|
|
12659
13144
|
}
|
|
12660
13145
|
function removePath(target) {
|
|
12661
13146
|
try {
|
|
12662
|
-
const stat =
|
|
12663
|
-
if (stat.isSymbolicLink())
|
|
12664
|
-
else
|
|
13147
|
+
const stat = fs14.lstatSync(target);
|
|
13148
|
+
if (stat.isSymbolicLink()) fs14.unlinkSync(target);
|
|
13149
|
+
else fs14.rmSync(target, { recursive: true, force: true });
|
|
12665
13150
|
} catch {
|
|
12666
13151
|
}
|
|
12667
13152
|
}
|
|
12668
13153
|
function linkDir(target, linkPath) {
|
|
12669
13154
|
removePath(linkPath);
|
|
12670
|
-
|
|
13155
|
+
fs14.mkdirSync(path12.dirname(linkPath), { recursive: true });
|
|
12671
13156
|
try {
|
|
12672
|
-
|
|
13157
|
+
fs14.symlinkSync(path12.relative(path12.dirname(linkPath), target), linkPath, "dir");
|
|
12673
13158
|
return "symlink";
|
|
12674
13159
|
} catch {
|
|
12675
13160
|
}
|
|
12676
13161
|
try {
|
|
12677
|
-
|
|
13162
|
+
fs14.symlinkSync(target, linkPath, "junction");
|
|
12678
13163
|
return "junction";
|
|
12679
13164
|
} catch {
|
|
12680
13165
|
}
|
|
@@ -12705,17 +13190,17 @@ function installSkill(skill, source, plan, cliVersion2) {
|
|
|
12705
13190
|
installedBy: `@withone/cli@${cliVersion2}`,
|
|
12706
13191
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12707
13192
|
};
|
|
12708
|
-
|
|
13193
|
+
fs14.writeFileSync(path12.join(canonical, SKILL_STAMP), JSON.stringify(stamp, null, 2) + "\n");
|
|
12709
13194
|
return { skill, version: source.version, source: source.kind, origin: source.origin, scope: plan.scope, root: plan.root, canonical, readers, links };
|
|
12710
13195
|
}
|
|
12711
13196
|
function removeSkill(skill, plan) {
|
|
12712
13197
|
const canonical = canonicalSkillDir(skill, plan);
|
|
12713
13198
|
const stamp = readStamp(canonical);
|
|
12714
|
-
if (!stamp && !
|
|
13199
|
+
if (!stamp && !fs14.existsSync(path12.join(canonical, "SKILL.md"))) return { removed: false, paths: [] };
|
|
12715
13200
|
const paths = [];
|
|
12716
13201
|
for (const link of stamp?.links ?? []) {
|
|
12717
13202
|
const full = fromStamp(plan.root, link.path);
|
|
12718
|
-
if (
|
|
13203
|
+
if (fs14.existsSync(full) || isDanglingLink(full)) {
|
|
12719
13204
|
removePath(full);
|
|
12720
13205
|
paths.push(full);
|
|
12721
13206
|
}
|
|
@@ -12728,18 +13213,18 @@ function toPosix(p9) {
|
|
|
12728
13213
|
return p9.split("\\").join("/");
|
|
12729
13214
|
}
|
|
12730
13215
|
function fromStamp(root, rel) {
|
|
12731
|
-
return
|
|
13216
|
+
return path12.join(root, ...toPosix(rel).split("/"));
|
|
12732
13217
|
}
|
|
12733
13218
|
function isDanglingLink(p9) {
|
|
12734
13219
|
try {
|
|
12735
|
-
return
|
|
13220
|
+
return fs14.lstatSync(p9).isSymbolicLink();
|
|
12736
13221
|
} catch {
|
|
12737
13222
|
return false;
|
|
12738
13223
|
}
|
|
12739
13224
|
}
|
|
12740
13225
|
function installedSkill(skill, plan) {
|
|
12741
13226
|
const canonical = canonicalSkillDir(skill, plan);
|
|
12742
|
-
if (!
|
|
13227
|
+
if (!fs14.existsSync(path12.join(canonical, "SKILL.md"))) return null;
|
|
12743
13228
|
const stamp = readStamp(canonical);
|
|
12744
13229
|
return { scope: plan.scope, root: plan.root, canonical, version: stamp?.version ?? null, source: stamp?.source ?? null, links: stamp?.links ?? [] };
|
|
12745
13230
|
}
|
|
@@ -12748,7 +13233,7 @@ function globalRoot() {
|
|
|
12748
13233
|
}
|
|
12749
13234
|
function refreshProjectSkills(cwd, cliVersion2) {
|
|
12750
13235
|
const root = findProjectRoot(cwd);
|
|
12751
|
-
if (!root ||
|
|
13236
|
+
if (!root || path12.resolve(root) === path12.resolve(homeDir())) return [];
|
|
12752
13237
|
const results = [];
|
|
12753
13238
|
for (const skill of OPTIONAL_SKILLS) {
|
|
12754
13239
|
const canonical = canonicalSkillDir(skill, { scope: "project", root });
|
|
@@ -12771,8 +13256,8 @@ function refreshProjectSkills(cwd, cliVersion2) {
|
|
|
12771
13256
|
}
|
|
12772
13257
|
|
|
12773
13258
|
// src/commands/skills.ts
|
|
12774
|
-
import
|
|
12775
|
-
import
|
|
13259
|
+
import path13 from "path";
|
|
13260
|
+
import pc16 from "picocolors";
|
|
12776
13261
|
function requireSkill(name) {
|
|
12777
13262
|
const skill = findOptionalSkill(name);
|
|
12778
13263
|
if (!skill) {
|
|
@@ -12795,7 +13280,7 @@ function resolvePlan(options) {
|
|
|
12795
13280
|
const agents = parseAgents(options.agents);
|
|
12796
13281
|
if (options.global) return { scope: "global", root: globalRoot(), agents };
|
|
12797
13282
|
const root = findProjectRoot(process.cwd());
|
|
12798
|
-
if (!root ||
|
|
13283
|
+
if (!root || path13.resolve(root) === path13.resolve(homeDir())) {
|
|
12799
13284
|
error("No project here: run this inside your app (a folder with package.json or .git), or pass --global.");
|
|
12800
13285
|
}
|
|
12801
13286
|
return { scope: "project", root, agents };
|
|
@@ -12811,7 +13296,7 @@ async function resolveSource(skill, plan) {
|
|
|
12811
13296
|
}
|
|
12812
13297
|
}
|
|
12813
13298
|
function display(p9, root) {
|
|
12814
|
-
const rel =
|
|
13299
|
+
const rel = path13.relative(root, p9);
|
|
12815
13300
|
return rel && !rel.startsWith("..") ? rel : p9;
|
|
12816
13301
|
}
|
|
12817
13302
|
function report(command, result, packageInProject) {
|
|
@@ -12827,7 +13312,7 @@ function report(command, result, packageInProject) {
|
|
|
12827
13312
|
root: result.root,
|
|
12828
13313
|
installedTo: [
|
|
12829
13314
|
{ path: result.canonical, kind: "folder", agents: result.readers },
|
|
12830
|
-
...result.links.map((l) => ({ path:
|
|
13315
|
+
...result.links.map((l) => ({ path: path13.join(result.root, ...l.path.split("/")), kind: l.kind, agents: l.agents }))
|
|
12831
13316
|
],
|
|
12832
13317
|
next: nextSteps(result, packageInProject)
|
|
12833
13318
|
});
|
|
@@ -12835,20 +13320,20 @@ function report(command, result, packageInProject) {
|
|
|
12835
13320
|
}
|
|
12836
13321
|
const scopeLabel2 = result.scope === "project" ? "this project" : "every project on this machine";
|
|
12837
13322
|
console.log();
|
|
12838
|
-
console.log(` ${
|
|
13323
|
+
console.log(` ${pc16.green("\u2713")} ${pc16.bold(`${result.skill.title} skill ${result.version}`)} installed for ${scopeLabel2}`);
|
|
12839
13324
|
console.log();
|
|
12840
|
-
const from = result.source === "project" ? `${where(result.origin)} ${
|
|
12841
|
-
console.log(` ${
|
|
13325
|
+
const from = result.source === "project" ? `${where(result.origin)} ${pc16.dim("(the version your code uses)")}` : `npm ${pc16.dim(result.scope === "project" ? `(latest ${result.skill.packageName}; not installed in this project yet)` : `(latest ${result.skill.packageName})`)}`;
|
|
13326
|
+
console.log(` ${pc16.dim("From")} ${from}`);
|
|
12842
13327
|
const rows = [
|
|
12843
13328
|
{ path: where(result.canonical), agents: result.readers },
|
|
12844
|
-
...result.links.map((l) => ({ path: `${l.path}${l.kind === "copy" ?
|
|
13329
|
+
...result.links.map((l) => ({ path: `${l.path}${l.kind === "copy" ? pc16.dim(" (copy)") : ""}`, agents: l.agents }))
|
|
12845
13330
|
];
|
|
12846
13331
|
rows.forEach((row, i) => {
|
|
12847
|
-
const agents = row.agents.length ?
|
|
12848
|
-
console.log(` ${
|
|
13332
|
+
const agents = row.agents.length ? pc16.dim(row.agents.join(", ")) : pc16.dim("shared copy the links point to");
|
|
13333
|
+
console.log(` ${pc16.dim(i === 0 ? "To " : " ")} ${row.path} ${agents}`);
|
|
12849
13334
|
});
|
|
12850
13335
|
console.log();
|
|
12851
|
-
console.log(` ${
|
|
13336
|
+
console.log(` ${pc16.bold("Next")}`);
|
|
12852
13337
|
nextSteps(result, packageInProject).forEach((step, i) => console.log(` ${i + 1}. ${step}`));
|
|
12853
13338
|
console.log();
|
|
12854
13339
|
}
|
|
@@ -12911,7 +13396,7 @@ async function skillsUpdateCommand(name, options) {
|
|
|
12911
13396
|
console.log("No optional skills are installed here. See: one skills list");
|
|
12912
13397
|
return;
|
|
12913
13398
|
}
|
|
12914
|
-
for (const r of updated) console.log(` ${
|
|
13399
|
+
for (const r of updated) console.log(` ${pc16.green("\u2713")} ${r.skill.title} skill ${r.version} ${pc16.dim(display(r.canonical, r.root))}`);
|
|
12915
13400
|
}
|
|
12916
13401
|
async function skillsRemoveCommand(name, options) {
|
|
12917
13402
|
const skill = requireSkill(name);
|
|
@@ -12925,12 +13410,12 @@ async function skillsRemoveCommand(name, options) {
|
|
|
12925
13410
|
console.log(`${skill.title} skill is not installed ${plan.scope === "global" ? "globally" : "in this project"}.`);
|
|
12926
13411
|
return;
|
|
12927
13412
|
}
|
|
12928
|
-
console.log(` ${
|
|
12929
|
-
for (const p9 of result.paths) console.log(` ${
|
|
13413
|
+
console.log(` ${pc16.green("\u2713")} Removed the ${skill.title} skill`);
|
|
13414
|
+
for (const p9 of result.paths) console.log(` ${pc16.dim(display(p9, plan.root))}`);
|
|
12930
13415
|
}
|
|
12931
13416
|
async function skillsListCommand() {
|
|
12932
13417
|
const projectRoot = findProjectRoot(process.cwd());
|
|
12933
|
-
const inProject = projectRoot &&
|
|
13418
|
+
const inProject = projectRoot && path13.resolve(projectRoot) !== path13.resolve(homeDir()) ? projectRoot : null;
|
|
12934
13419
|
const optional = OPTIONAL_SKILLS.map((skill) => {
|
|
12935
13420
|
const project = inProject ? installedSkill(skill, { scope: "project", root: inProject }) : null;
|
|
12936
13421
|
const global = installedSkill(skill, { scope: "global", root: globalRoot() });
|
|
@@ -12958,11 +13443,11 @@ async function skillsListCommand() {
|
|
|
12958
13443
|
return;
|
|
12959
13444
|
}
|
|
12960
13445
|
console.log();
|
|
12961
|
-
console.log(` ${
|
|
13446
|
+
console.log(` ${pc16.bold("one")} ${oneSkill.installed ? pc16.green(`installed ${oneSkill.version ?? ""}`.trim()) : pc16.yellow("not installed")} ${pc16.dim("managed by one init")}`);
|
|
12962
13447
|
for (const s of optional) {
|
|
12963
|
-
const state = s.project ?
|
|
12964
|
-
console.log(` ${
|
|
12965
|
-
console.log(` ${" ".repeat(8)} ${
|
|
13448
|
+
const state = s.project ? pc16.green(`project ${s.project.version ?? ""}`.trim()) + (s.project.upToDate ? "" : pc16.yellow(` (package is ${s.packageInProject}: run one skills update ${s.skill})`)) : s.global ? pc16.green(`global ${s.global.version ?? ""}`.trim()) : pc16.dim(`not installed \u2014 one skills add ${s.skill}`);
|
|
13449
|
+
console.log(` ${pc16.bold(s.skill.padEnd(8))} ${state}`);
|
|
13450
|
+
console.log(` ${" ".repeat(8)} ${pc16.dim(s.description)}`);
|
|
12966
13451
|
}
|
|
12967
13452
|
console.log();
|
|
12968
13453
|
}
|
|
@@ -12995,6 +13480,10 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
|
|
|
12995
13480
|
one skills update [connect] Re-copy from the installed @withone/connect after an upgrade
|
|
12996
13481
|
one skills remove connect Remove it
|
|
12997
13482
|
|
|
13483
|
+
Connect apps (one.connect.ts, typed by @withone/connect/config):
|
|
13484
|
+
one connect create Approve the app (or its new version) in the browser, then write .env
|
|
13485
|
+
one connect types Write one-connect-env.d.ts so tool slugs and action names autocomplete
|
|
13486
|
+
|
|
12998
13487
|
Guide:
|
|
12999
13488
|
one guide [topic] Full CLI guide (topics: overview, actions, workflows, memory, sync, all)
|
|
13000
13489
|
|
|
@@ -13208,8 +13697,8 @@ config.command("reset").description("Remove the project config for the current d
|
|
|
13208
13697
|
return;
|
|
13209
13698
|
}
|
|
13210
13699
|
}
|
|
13211
|
-
const
|
|
13212
|
-
|
|
13700
|
+
const fs16 = await import("fs");
|
|
13701
|
+
fs16.unlinkSync(globalPath);
|
|
13213
13702
|
if (isAgentMode()) {
|
|
13214
13703
|
json({ deleted: true, scope: "global" });
|
|
13215
13704
|
} else {
|
|
@@ -13226,15 +13715,15 @@ config.command("reset").description("Remove the project config for the current d
|
|
|
13226
13715
|
}
|
|
13227
13716
|
return;
|
|
13228
13717
|
}
|
|
13229
|
-
const
|
|
13230
|
-
const configContent =
|
|
13231
|
-
|
|
13718
|
+
const fs15 = await import("fs");
|
|
13719
|
+
const configContent = fs15.readFileSync(resolved.path, "utf-8");
|
|
13720
|
+
fs15.unlinkSync(resolved.path);
|
|
13232
13721
|
const next = resolveConfig();
|
|
13233
|
-
|
|
13234
|
-
|
|
13722
|
+
fs15.mkdirSync(path14.dirname(resolved.path), { recursive: true });
|
|
13723
|
+
fs15.writeFileSync(resolved.path, configContent);
|
|
13235
13724
|
let fallbackLabel;
|
|
13236
13725
|
if (next.scope === "project") {
|
|
13237
|
-
fallbackLabel = `parent project config (${
|
|
13726
|
+
fallbackLabel = `parent project config (${path14.basename(next.projectRoot)})`;
|
|
13238
13727
|
} else if (next.scope === "global") {
|
|
13239
13728
|
fallbackLabel = "global config";
|
|
13240
13729
|
} else {
|
|
@@ -13243,7 +13732,7 @@ config.command("reset").description("Remove the project config for the current d
|
|
|
13243
13732
|
if (!isAgentMode()) {
|
|
13244
13733
|
const p9 = await import("@clack/prompts");
|
|
13245
13734
|
const confirmed = await p9.confirm({
|
|
13246
|
-
message: `Delete project config for ${
|
|
13735
|
+
message: `Delete project config for ${path14.basename(resolved.projectRoot)}? Will fall back to ${fallbackLabel}.`,
|
|
13247
13736
|
initialValue: false
|
|
13248
13737
|
});
|
|
13249
13738
|
if (p9.isCancel(confirmed) || !confirmed) {
|
|
@@ -13251,9 +13740,9 @@ config.command("reset").description("Remove the project config for the current d
|
|
|
13251
13740
|
return;
|
|
13252
13741
|
}
|
|
13253
13742
|
}
|
|
13254
|
-
|
|
13743
|
+
fs15.unlinkSync(resolved.path);
|
|
13255
13744
|
try {
|
|
13256
|
-
|
|
13745
|
+
fs15.rmdirSync(path14.dirname(resolved.path));
|
|
13257
13746
|
} catch {
|
|
13258
13747
|
}
|
|
13259
13748
|
if (isAgentMode()) {
|
|
@@ -13329,6 +13818,13 @@ flow.command("inspect <runId>").description("Inspect a past run's per-step outpu
|
|
|
13329
13818
|
flow.command("scaffold [template]").description("Generate a workflow scaffold (templates: basic, conditional, loop, ai)").action(async (template) => {
|
|
13330
13819
|
await flowScaffoldCommand(template);
|
|
13331
13820
|
});
|
|
13821
|
+
var connectApp = program.command("connect").description("Create and update a One Connect app from one.connect.ts");
|
|
13822
|
+
connectApp.command("create").description("Open the setup page to approve the app (or, once .env holds its ONE_CLIENT_ID, its new version); a new app gets its two secrets there. Then write .env").option("--config <file>", "Config file (default: one.connect.ts, .mts, .mjs or .js in this directory)").option("--env-file <file>", "Env file to write (default: .env.local if it exists, else .env)").option("--no-open", "Print the setup page link instead of opening it").action(async (options) => {
|
|
13823
|
+
await connectCreateCommand(options);
|
|
13824
|
+
});
|
|
13825
|
+
connectApp.command("types").description("Write one-connect-env.d.ts: every tool slug, and action names for the tools the config asks for").option("--config <file>", "Config file (default: one.connect.ts, .mts, .mjs or .js in this directory)").action(async (options) => {
|
|
13826
|
+
await connectTypesCommand(options);
|
|
13827
|
+
});
|
|
13332
13828
|
var relay = program.command("relay").alias("r").description("Receive webhooks from platforms and relay them via passthrough actions");
|
|
13333
13829
|
relay.command("create").description("Create a new relay endpoint for a connection").requiredOption("--connection-key <key>", "Connection key for the source platform").option("--description <desc>", "Description of the relay endpoint").option("--event-filters <json>", `JSON array of event types to filter (e.g. '["customer.created"]')`).option("--tags <json>", "JSON array of tags").option("--metadata <json>", `JSON object of platform-specific metadata required to register the webhook (e.g. GitHub: '{"GITHUB_OWNER":"org","GITHUB_REPOSITORY":"repo"}', Typeform: '{"TYPEFORM_FORM_ID":"abc"}')`).option("--create-webhook", "Automatically register the webhook with the source platform").action(async (options) => {
|
|
13334
13830
|
await relayCreateCommand(options);
|
|
@@ -13416,22 +13912,22 @@ program.command("whoami").description("Show the user, organization, and project
|
|
|
13416
13912
|
});
|
|
13417
13913
|
return;
|
|
13418
13914
|
}
|
|
13419
|
-
const
|
|
13915
|
+
const pc17 = (await import("picocolors")).default;
|
|
13420
13916
|
const contextParts = [];
|
|
13421
13917
|
if (whoami.organization) contextParts.push(whoami.organization.name);
|
|
13422
13918
|
if (whoami.project) contextParts.push(whoami.project.name);
|
|
13423
13919
|
const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
|
|
13424
|
-
const envLabel = env === "test" ?
|
|
13425
|
-
const configLabel = configScope === "project" ?
|
|
13920
|
+
const envLabel = env === "test" ? pc17.yellow("test") : pc17.green("live");
|
|
13921
|
+
const configLabel = configScope === "project" ? pc17.cyan("project config") : pc17.magenta("global config");
|
|
13426
13922
|
console.log();
|
|
13427
|
-
console.log(` ${
|
|
13428
|
-
console.log(` ${whoami.user.name} ${
|
|
13429
|
-
if (keyName) console.log(` ${
|
|
13430
|
-
if (whoami.organization) console.log(` ${
|
|
13431
|
-
if (whoami.project) console.log(` ${
|
|
13923
|
+
console.log(` ${pc17.bold(scopeDisplay)} ${pc17.dim("\xB7")} ${envLabel}`);
|
|
13924
|
+
console.log(` ${whoami.user.name} ${pc17.dim(`(${whoami.user.email})`)}`);
|
|
13925
|
+
if (keyName) console.log(` ${pc17.dim("Key:")} ${keyName}`);
|
|
13926
|
+
if (whoami.organization) console.log(` ${pc17.dim("Org:")} ${whoami.organization.name} ${pc17.dim(`(${whoami.organization.id})`)}`);
|
|
13927
|
+
if (whoami.project) console.log(` ${pc17.dim("Project:")} ${whoami.project.name} ${pc17.dim(`(${whoami.project.id})`)}`);
|
|
13432
13928
|
console.log();
|
|
13433
|
-
console.log(` ${
|
|
13434
|
-
console.log(` ${
|
|
13929
|
+
console.log(` ${pc17.dim("Using")} ${configLabel}`);
|
|
13930
|
+
console.log(` ${pc17.dim("API:")} ${apiBase}`);
|
|
13435
13931
|
console.log();
|
|
13436
13932
|
});
|
|
13437
13933
|
program.command("add [platform]").description("Shortcut for: connection add").option("--tag <name>", "Tag the new connection (disambiguates multiple connections per platform in sync/flow profiles)").action(async (platform, options) => {
|
|
@@ -13441,5 +13937,6 @@ program.command("list").alias("ls").description("Shortcut for: connection list")
|
|
|
13441
13937
|
await connectionListCommand(options);
|
|
13442
13938
|
});
|
|
13443
13939
|
export {
|
|
13940
|
+
nodeHasTlsFix,
|
|
13444
13941
|
program
|
|
13445
13942
|
};
|