@withone/cli 2.9.2 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
validateActionInput,
|
|
43
43
|
walkSteps,
|
|
44
44
|
writeCache
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-5CPQHXTO.js";
|
|
46
46
|
import {
|
|
47
47
|
memSqlCommand
|
|
48
48
|
} from "./chunk-O3HDWHHS.js";
|
|
@@ -152,7 +152,7 @@ import {
|
|
|
152
152
|
|
|
153
153
|
// src/cli.ts
|
|
154
154
|
import { createRequire } from "module";
|
|
155
|
-
import
|
|
155
|
+
import path14 from "path";
|
|
156
156
|
import { Command } from "commander";
|
|
157
157
|
|
|
158
158
|
// src/commands/init.ts
|
|
@@ -180,6 +180,27 @@ function getConnectionUrl(platform, params) {
|
|
|
180
180
|
function getApiKeyUrl() {
|
|
181
181
|
return `${oneAppUrl()}/settings/api-keys`;
|
|
182
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
|
+
}
|
|
183
204
|
async function openConnectionPage(platform, params) {
|
|
184
205
|
await open(getConnectionUrl(platform, params));
|
|
185
206
|
}
|
|
@@ -1065,25 +1086,25 @@ async function loginCommand() {
|
|
|
1065
1086
|
let targetScope = "global";
|
|
1066
1087
|
const existingKey = getApiKey();
|
|
1067
1088
|
if (existingKey) {
|
|
1068
|
-
const
|
|
1089
|
+
const pc18 = (await import("picocolors")).default;
|
|
1069
1090
|
const resolved = resolveConfig();
|
|
1070
1091
|
const whoami2 = resolved.config?.whoami;
|
|
1071
1092
|
const env2 = getEnvFromApiKey(existingKey);
|
|
1072
|
-
const envLabel2 = env2 === "test" ?
|
|
1073
|
-
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");
|
|
1074
1095
|
const lines = ["You are already logged in.", ""];
|
|
1075
1096
|
if (whoami2) {
|
|
1076
1097
|
const contextParts2 = [];
|
|
1077
1098
|
if (whoami2.organization) contextParts2.push(whoami2.organization.name);
|
|
1078
1099
|
if (whoami2.project) contextParts2.push(whoami2.project.name);
|
|
1079
1100
|
const scopeDisplay2 = contextParts2.length > 0 ? contextParts2.join(" / ") : "Personal";
|
|
1080
|
-
lines.push(`${
|
|
1081
|
-
lines.push(`${whoami2.user.name} ${
|
|
1082
|
-
if (whoami2.organization) lines.push(`${
|
|
1083
|
-
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}`);
|
|
1084
1105
|
}
|
|
1085
1106
|
lines.push("");
|
|
1086
|
-
lines.push(`${
|
|
1107
|
+
lines.push(`${pc18.dim("Stored in")} ${currentScope}`);
|
|
1087
1108
|
p2.note(lines.join("\n"));
|
|
1088
1109
|
const scopeChoice = await p2.select({
|
|
1089
1110
|
message: "Where would you like to log in?",
|
|
@@ -1102,42 +1123,42 @@ async function loginCommand() {
|
|
|
1102
1123
|
if (!result) return;
|
|
1103
1124
|
const { apiKey, whoami, keyName, grant, knowledgeOnly } = result;
|
|
1104
1125
|
saveCredentials(apiKey, targetScope, { keyName, whoami, knowledgeOnly });
|
|
1105
|
-
const
|
|
1126
|
+
const pc17 = (await import("picocolors")).default;
|
|
1106
1127
|
const env = getEnvFromApiKey(apiKey);
|
|
1107
1128
|
const contextParts = [];
|
|
1108
1129
|
if (whoami.organization) contextParts.push(whoami.organization.name);
|
|
1109
1130
|
if (whoami.project) contextParts.push(whoami.project.name);
|
|
1110
1131
|
const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
|
|
1111
|
-
const envLabel = env === "test" ?
|
|
1112
|
-
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");
|
|
1113
1134
|
const infoLines = [
|
|
1114
|
-
`${
|
|
1115
|
-
`${whoami.user.name} ${
|
|
1135
|
+
`${pc17.bold(scopeDisplay)} ${pc17.dim("\xB7")} ${envLabel}`,
|
|
1136
|
+
`${whoami.user.name} ${pc17.dim(`(${whoami.user.email})`)}`
|
|
1116
1137
|
];
|
|
1117
|
-
if (keyName) infoLines.push(`${
|
|
1118
|
-
infoLines.push(...grantNoteLines(grant,
|
|
1119
|
-
if (whoami.organization) infoLines.push(`${
|
|
1120
|
-
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}`);
|
|
1121
1142
|
infoLines.push("");
|
|
1122
|
-
infoLines.push(`${
|
|
1143
|
+
infoLines.push(`${pc17.dim("Stored in")} ${configLabel}`);
|
|
1123
1144
|
p2.note(infoLines.join("\n"), "Logged in");
|
|
1124
1145
|
console.log();
|
|
1125
|
-
console.log(` ${
|
|
1126
|
-
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)`);
|
|
1127
1148
|
console.log();
|
|
1128
|
-
console.log(
|
|
1149
|
+
console.log(pc17.bold(" Copy-paste this to your AI agent:"));
|
|
1129
1150
|
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(
|
|
1138
|
-
console.log(
|
|
1139
|
-
console.log(
|
|
1140
|
-
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"));
|
|
1141
1162
|
console.log();
|
|
1142
1163
|
p2.outro("Happy building!");
|
|
1143
1164
|
}
|
|
@@ -1322,8 +1343,8 @@ function tildify(filePath) {
|
|
|
1322
1343
|
function scopeLabel(scope) {
|
|
1323
1344
|
return scope === "project" ? pc3.cyan("[project]") : pc3.magenta("[global]");
|
|
1324
1345
|
}
|
|
1325
|
-
function scopedMessage(scope,
|
|
1326
|
-
return `${scopeLabel(scope)} ${
|
|
1346
|
+
function scopedMessage(scope, message2) {
|
|
1347
|
+
return `${scopeLabel(scope)} ${message2}`;
|
|
1327
1348
|
}
|
|
1328
1349
|
async function handleExistingConfig(apiKey, scope, options) {
|
|
1329
1350
|
const statuses = getAgentStatuses();
|
|
@@ -1910,8 +1931,8 @@ async function promptConnectIntegrations(apiKey, connParams) {
|
|
|
1910
1931
|
{ value: "more", label: "Browse all 1000+ platforms" },
|
|
1911
1932
|
{ value: "skip", label: "Skip for now", hint: "you can always run one add later" }
|
|
1912
1933
|
];
|
|
1913
|
-
const
|
|
1914
|
-
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 });
|
|
1915
1936
|
if (p3.isCancel(choice) || choice === "skip") {
|
|
1916
1937
|
break;
|
|
1917
1938
|
}
|
|
@@ -3295,7 +3316,7 @@ var CUSTOM_ACTION_NOTE = '\n\nThis is a custom action: ALSO include "connectionK
|
|
|
3295
3316
|
function codeGuideAction(action, knowledge, platform, tags, apiBase) {
|
|
3296
3317
|
const base = apiBase.replace(/\/$/, "");
|
|
3297
3318
|
const method = action.method.toUpperCase();
|
|
3298
|
-
const
|
|
3319
|
+
const path15 = action.path.startsWith("/") ? action.path : `/${action.path}`;
|
|
3299
3320
|
const connEnv = `ONE_${platformEnvSegment(platform)}_CONNECTION_KEY`;
|
|
3300
3321
|
const customNote = tags.includes("custom") ? CUSTOM_ACTION_NOTE : "";
|
|
3301
3322
|
return `### ${actionHeading(action)}
|
|
@@ -3304,7 +3325,7 @@ function codeGuideAction(action, knowledge, platform, tags, apiBase) {
|
|
|
3304
3325
|
${knowledge ?? NO_KNOWLEDGE}
|
|
3305
3326
|
|
|
3306
3327
|
## Calling this action from code
|
|
3307
|
-
URL: ${base}/passthrough${
|
|
3328
|
+
URL: ${base}/passthrough${path15}
|
|
3308
3329
|
Method: ${method}
|
|
3309
3330
|
Headers:
|
|
3310
3331
|
- x-one-secret: the value of the ONE_SECRET env var
|
|
@@ -3315,7 +3336,7 @@ Headers:
|
|
|
3315
3336
|
Environment variables: ONE_SECRET (their One API key) and ${connEnv} (the key of their ${platform} connection), both from the One dashboard.
|
|
3316
3337
|
|
|
3317
3338
|
\`\`\`typescript
|
|
3318
|
-
const response = await fetch("${base}/passthrough${
|
|
3339
|
+
const response = await fetch("${base}/passthrough${path15}", {
|
|
3319
3340
|
method: "${method}",
|
|
3320
3341
|
headers: {
|
|
3321
3342
|
"x-one-secret": process.env.ONE_SECRET,
|
|
@@ -4065,32 +4086,32 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
4065
4086
|
const validTypes = FLOW_SCHEMA.stepTypes.map((st) => st.type);
|
|
4066
4087
|
for (let i = 0; i < steps.length; i++) {
|
|
4067
4088
|
const step = steps[i];
|
|
4068
|
-
const
|
|
4089
|
+
const path15 = `${pathPrefix}[${i}]`;
|
|
4069
4090
|
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
4070
|
-
errors.push({ path:
|
|
4091
|
+
errors.push({ path: path15, message: "Step must be an object" });
|
|
4071
4092
|
continue;
|
|
4072
4093
|
}
|
|
4073
4094
|
const s = step;
|
|
4074
4095
|
if (!s.id || typeof s.id !== "string") {
|
|
4075
|
-
errors.push({ path: `${
|
|
4096
|
+
errors.push({ path: `${path15}.id`, message: 'Step must have a string "id"' });
|
|
4076
4097
|
}
|
|
4077
4098
|
if (!s.name || typeof s.name !== "string") {
|
|
4078
|
-
errors.push({ path: `${
|
|
4099
|
+
errors.push({ path: `${path15}.name`, message: 'Step must have a string "name"' });
|
|
4079
4100
|
}
|
|
4080
4101
|
if (!s.type || !validTypes.includes(s.type)) {
|
|
4081
|
-
errors.push({ path: `${
|
|
4102
|
+
errors.push({ path: `${path15}.type`, message: `Step type must be one of: ${validTypes.join(", ")}` });
|
|
4082
4103
|
continue;
|
|
4083
4104
|
}
|
|
4084
4105
|
if (s.requires !== void 0) {
|
|
4085
4106
|
if (!Array.isArray(s.requires)) {
|
|
4086
|
-
errors.push({ path: `${
|
|
4107
|
+
errors.push({ path: `${path15}.requires`, message: '"requires" must be an array of selector strings (e.g. ["$.steps.foo.output.bar"])' });
|
|
4087
4108
|
} else {
|
|
4088
4109
|
for (let r = 0; r < s.requires.length; r++) {
|
|
4089
4110
|
const sel = s.requires[r];
|
|
4090
4111
|
if (typeof sel !== "string") {
|
|
4091
|
-
errors.push({ path: `${
|
|
4112
|
+
errors.push({ path: `${path15}.requires[${r}]`, message: '"requires" entry must be a selector string' });
|
|
4092
4113
|
} else if (!sel.startsWith("$.")) {
|
|
4093
|
-
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")` });
|
|
4094
4115
|
}
|
|
4095
4116
|
}
|
|
4096
4117
|
}
|
|
@@ -4098,7 +4119,7 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
4098
4119
|
if (s.onError && typeof s.onError === "object") {
|
|
4099
4120
|
const oe = s.onError;
|
|
4100
4121
|
if (!FLOW_SCHEMA.errorStrategies.includes(oe.strategy)) {
|
|
4101
|
-
errors.push({ path: `${
|
|
4122
|
+
errors.push({ path: `${path15}.onError.strategy`, message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
|
|
4102
4123
|
}
|
|
4103
4124
|
}
|
|
4104
4125
|
const descriptor = getStepTypeDescriptor(s.type);
|
|
@@ -4108,14 +4129,14 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
4108
4129
|
if (!configObj || typeof configObj !== "object") {
|
|
4109
4130
|
const hint = detectFlatConfigHint(s, descriptor);
|
|
4110
4131
|
errors.push({
|
|
4111
|
-
path: `${
|
|
4132
|
+
path: `${path15}.${configKey}`,
|
|
4112
4133
|
message: `${capitalize(descriptor.type)} step must have a "${configKey}" config object${hint}`
|
|
4113
4134
|
});
|
|
4114
4135
|
continue;
|
|
4115
4136
|
}
|
|
4116
4137
|
const config2 = configObj;
|
|
4117
4138
|
for (const [fieldName, fd] of Object.entries(descriptor.fields)) {
|
|
4118
|
-
const fieldPath = `${
|
|
4139
|
+
const fieldPath = `${path15}.${configKey}.${fieldName}`;
|
|
4119
4140
|
const value = config2[fieldName];
|
|
4120
4141
|
if (fd.required && (value === void 0 || value === null || value === "")) {
|
|
4121
4142
|
errors.push({ path: fieldPath, message: `${capitalize(descriptor.type)} must have ${fd.type === "string" ? "a string" : fd.type === "array" ? "a" : "a"} "${fieldName}"` });
|
|
@@ -4148,30 +4169,30 @@ function validateStepsArray(steps, pathPrefix, errors) {
|
|
|
4148
4169
|
}
|
|
4149
4170
|
}
|
|
4150
4171
|
if (descriptor.type === "action") {
|
|
4151
|
-
validateConnectionForm(config2, `${
|
|
4172
|
+
validateConnectionForm(config2, `${path15}.${configKey}`, errors);
|
|
4152
4173
|
}
|
|
4153
4174
|
if (descriptor.type === "code") {
|
|
4154
4175
|
const hasSource = typeof config2.source === "string" && config2.source.length > 0;
|
|
4155
4176
|
const hasModule = typeof config2.module === "string" && config2.module.length > 0;
|
|
4156
4177
|
if (!hasSource && !hasModule) {
|
|
4157
|
-
errors.push({ path: `${
|
|
4178
|
+
errors.push({ path: `${path15}.${configKey}`, message: 'Code step must define either "source" (inline JS) or "module" (path to .mjs file)' });
|
|
4158
4179
|
} else if (hasSource && hasModule) {
|
|
4159
|
-
errors.push({ path: `${
|
|
4180
|
+
errors.push({ path: `${path15}.${configKey}`, message: 'Code step cannot define both "source" and "module" \u2014 pick one' });
|
|
4160
4181
|
}
|
|
4161
4182
|
if (hasModule) {
|
|
4162
4183
|
const m = config2.module;
|
|
4163
4184
|
if (m.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(m)) {
|
|
4164
|
-
errors.push({ path: `${
|
|
4185
|
+
errors.push({ path: `${path15}.${configKey}.module`, message: "Code module path must be relative to the flow folder (no absolute paths)" });
|
|
4165
4186
|
} else if (m.split(/[\\/]/).includes("..")) {
|
|
4166
|
-
errors.push({ path: `${
|
|
4187
|
+
errors.push({ path: `${path15}.${configKey}.module`, message: 'Code module path must not escape the flow folder ("..")' });
|
|
4167
4188
|
} else if (!m.endsWith(".mjs")) {
|
|
4168
|
-
errors.push({ path: `${
|
|
4189
|
+
errors.push({ path: `${path15}.${configKey}.module`, message: "Code module must be a .mjs file" });
|
|
4169
4190
|
}
|
|
4170
4191
|
}
|
|
4171
4192
|
if (hasSource) {
|
|
4172
4193
|
const syntaxError = checkCodeSourceSyntax(config2.source);
|
|
4173
4194
|
if (syntaxError) {
|
|
4174
|
-
errors.push({ path: `${
|
|
4195
|
+
errors.push({ path: `${path15}.${configKey}.source`, message: `Syntax error in code step: ${syntaxError}` });
|
|
4175
4196
|
}
|
|
4176
4197
|
}
|
|
4177
4198
|
}
|
|
@@ -4243,16 +4264,16 @@ function validateStepIds(flow2) {
|
|
|
4243
4264
|
function collectIds(steps, pathPrefix) {
|
|
4244
4265
|
for (let i = 0; i < steps.length; i++) {
|
|
4245
4266
|
const step = steps[i];
|
|
4246
|
-
const
|
|
4267
|
+
const path15 = `${pathPrefix}[${i}]`;
|
|
4247
4268
|
if (seen.has(step.id)) {
|
|
4248
|
-
errors.push({ path: `${
|
|
4269
|
+
errors.push({ path: `${path15}.id`, message: `Duplicate step ID: "${step.id}"` });
|
|
4249
4270
|
} else {
|
|
4250
4271
|
seen.add(step.id);
|
|
4251
4272
|
}
|
|
4252
4273
|
for (const { configKey, fieldName } of nestedKeys) {
|
|
4253
4274
|
const config2 = step[configKey];
|
|
4254
4275
|
if (config2 && Array.isArray(config2[fieldName])) {
|
|
4255
|
-
collectIds(config2[fieldName], `${
|
|
4276
|
+
collectIds(config2[fieldName], `${path15}.${configKey}.${fieldName}`);
|
|
4256
4277
|
}
|
|
4257
4278
|
}
|
|
4258
4279
|
}
|
|
@@ -4300,7 +4321,7 @@ function validateSelectorReferences(flow2, rootDir) {
|
|
|
4300
4321
|
}
|
|
4301
4322
|
return selectors;
|
|
4302
4323
|
}
|
|
4303
|
-
function checkSelectors(selectors,
|
|
4324
|
+
function checkSelectors(selectors, path15, precedingStepIds) {
|
|
4304
4325
|
for (const selector of selectors) {
|
|
4305
4326
|
const parts = selector.split(".");
|
|
4306
4327
|
if (parts.length < 3) continue;
|
|
@@ -4308,15 +4329,15 @@ function validateSelectorReferences(flow2, rootDir) {
|
|
|
4308
4329
|
if (root === "input") {
|
|
4309
4330
|
const inputName = parts[2];
|
|
4310
4331
|
if (!inputNames.has(inputName)) {
|
|
4311
|
-
errors.push({ path:
|
|
4332
|
+
errors.push({ path: path15, message: `Selector "${selector}" references undefined input "${inputName}"` });
|
|
4312
4333
|
}
|
|
4313
4334
|
} else if (root === "steps") {
|
|
4314
4335
|
const stepId = parts[2].replace(/[\[\]]/g, "").split(/[\[\]]/)[0];
|
|
4315
4336
|
if (!allStepIds.has(stepId)) {
|
|
4316
|
-
errors.push({ path:
|
|
4337
|
+
errors.push({ path: path15, message: `Selector "${selector}" references undefined step "${stepId}"` });
|
|
4317
4338
|
} else if (precedingStepIds && !precedingStepIds.has(stepId)) {
|
|
4318
4339
|
errors.push({
|
|
4319
|
-
path:
|
|
4340
|
+
path: path15,
|
|
4320
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.`
|
|
4321
4342
|
});
|
|
4322
4343
|
}
|
|
@@ -4324,20 +4345,20 @@ function validateSelectorReferences(flow2, rootDir) {
|
|
|
4324
4345
|
}
|
|
4325
4346
|
}
|
|
4326
4347
|
const EXPRESSION_FIELDS = /* @__PURE__ */ new Set(["condition.expression", "while.condition"]);
|
|
4327
|
-
function checkOperatorsInSelectorField(value,
|
|
4348
|
+
function checkOperatorsInSelectorField(value, path15) {
|
|
4328
4349
|
if (typeof value === "string" && value.startsWith("$.")) {
|
|
4329
4350
|
if (value.includes("||")) {
|
|
4330
|
-
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.` });
|
|
4331
4352
|
} else if (value.includes("&&")) {
|
|
4332
|
-
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.` });
|
|
4333
4354
|
}
|
|
4334
4355
|
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
4335
4356
|
for (const [k, v] of Object.entries(value)) {
|
|
4336
|
-
checkOperatorsInSelectorField(v, `${
|
|
4357
|
+
checkOperatorsInSelectorField(v, `${path15}.${k}`);
|
|
4337
4358
|
}
|
|
4338
4359
|
} else if (Array.isArray(value)) {
|
|
4339
4360
|
for (let i = 0; i < value.length; i++) {
|
|
4340
|
-
checkOperatorsInSelectorField(value[i], `${
|
|
4361
|
+
checkOperatorsInSelectorField(value[i], `${path15}[${i}]`);
|
|
4341
4362
|
}
|
|
4342
4363
|
}
|
|
4343
4364
|
}
|
|
@@ -4422,10 +4443,10 @@ var VALID_OUTPUT_SCHEMA_TYPES = /* @__PURE__ */ new Set(["string", "number", "bo
|
|
|
4422
4443
|
function isOutputSchemaObject(v) {
|
|
4423
4444
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
4424
4445
|
}
|
|
4425
|
-
function walkOutputSchema(schema,
|
|
4446
|
+
function walkOutputSchema(schema, path15) {
|
|
4426
4447
|
let current = schema;
|
|
4427
|
-
for (let i = 0; i <
|
|
4428
|
-
const seg =
|
|
4448
|
+
for (let i = 0; i < path15.length; i++) {
|
|
4449
|
+
const seg = path15[i];
|
|
4429
4450
|
if (typeof current === "string") {
|
|
4430
4451
|
return current === "unknown" || current === "object" || current === "array" ? "opaque" : "opaque";
|
|
4431
4452
|
}
|
|
@@ -5892,6 +5913,488 @@ async function relayEventTypesCommand(platform) {
|
|
|
5892
5913
|
}
|
|
5893
5914
|
}
|
|
5894
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
|
+
function askedTools(config2) {
|
|
6019
|
+
return config2.ask === "everything" ? [] : Object.entries(config2.ask ?? {});
|
|
6020
|
+
}
|
|
6021
|
+
var ACCESS_METHODS = {
|
|
6022
|
+
"read-only": ["GET"],
|
|
6023
|
+
"read-write": ["GET", "POST", "PUT", "PATCH"],
|
|
6024
|
+
"full-access": null
|
|
6025
|
+
};
|
|
6026
|
+
var HEX_COLOR = /^#[0-9a-fA-F]{6}$/;
|
|
6027
|
+
var ACTION_ID_PREFIX = "conn_mod_def::";
|
|
6028
|
+
function findConfigFile(cwd, explicit) {
|
|
6029
|
+
if (explicit) {
|
|
6030
|
+
const resolved = path5.resolve(cwd, explicit);
|
|
6031
|
+
return fs5.existsSync(resolved) ? resolved : null;
|
|
6032
|
+
}
|
|
6033
|
+
for (const name of CONFIG_FILES) {
|
|
6034
|
+
const candidate = path5.join(cwd, name);
|
|
6035
|
+
if (fs5.existsSync(candidate)) return candidate;
|
|
6036
|
+
}
|
|
6037
|
+
return null;
|
|
6038
|
+
}
|
|
6039
|
+
async function loadConfigFile(file) {
|
|
6040
|
+
try {
|
|
6041
|
+
const mod = await import(pathToFileURL(file).href);
|
|
6042
|
+
return mod.default ?? mod;
|
|
6043
|
+
} catch (error2) {
|
|
6044
|
+
const { code, message: message2 = "" } = error2;
|
|
6045
|
+
if (code === "ERR_MODULE_NOT_FOUND" && message2.includes("'@withone/connect'")) {
|
|
6046
|
+
throw new Error(
|
|
6047
|
+
`it imports @withone/connect, which isn't installed in this project. Run \`npm i @withone/connect\` first.`
|
|
6048
|
+
);
|
|
6049
|
+
}
|
|
6050
|
+
if (code === "ERR_PACKAGE_PATH_NOT_EXPORTED" && /@withone[\\/]connect/.test(message2)) {
|
|
6051
|
+
throw new Error(
|
|
6052
|
+
`this project's @withone/connect is older than 0.19.0, which added @withone/connect/config. Run \`npm i @withone/connect@latest\` first.`
|
|
6053
|
+
);
|
|
6054
|
+
}
|
|
6055
|
+
if (code === "ERR_UNKNOWN_FILE_EXTENSION" && /\.m?ts$/.test(file)) {
|
|
6056
|
+
throw new Error(
|
|
6057
|
+
`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} */.`
|
|
6058
|
+
);
|
|
6059
|
+
}
|
|
6060
|
+
throw error2;
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
function validateConfig(value) {
|
|
6064
|
+
const errors = [];
|
|
6065
|
+
const v = value;
|
|
6066
|
+
if (!v || typeof v !== "object") return { errors: ["The config must export an object (use defineConnectApp)."] };
|
|
6067
|
+
if (typeof v.name !== "string" || !v.name.trim()) errors.push("`name` is required.");
|
|
6068
|
+
if (!Array.isArray(v.redirectUris) || v.redirectUris.length === 0) {
|
|
6069
|
+
errors.push("`redirectUris` needs at least one callback URL.");
|
|
6070
|
+
} else {
|
|
6071
|
+
for (const uri of v.redirectUris) {
|
|
6072
|
+
if (typeof uri !== "string" || !/^https?:\/\//.test(uri)) errors.push(`Redirect URI "${uri}" must be an http(s) URL.`);
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
if (v.reason !== void 0 && (typeof v.reason !== "string" || v.reason.trim().length === 0 || v.reason.length > 200)) {
|
|
6076
|
+
errors.push("`reason` must be one sentence of at most 200 characters.");
|
|
6077
|
+
}
|
|
6078
|
+
if (v.branding) {
|
|
6079
|
+
for (const key of ["panelColor", "buttonColor"]) {
|
|
6080
|
+
if (!HEX_COLOR.test(String(v.branding[key] ?? ""))) errors.push(`\`branding.${key}\` must be a #RRGGBB colour.`);
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
const asked = v.ask;
|
|
6084
|
+
if (asked === void 0) {
|
|
6085
|
+
errors.push('`ask` is required: name the tools the app asks for, or write `ask: "everything"`.');
|
|
6086
|
+
} else if (typeof asked === "string") {
|
|
6087
|
+
if (asked !== "everything") errors.push(`\`ask\` is "${asked}"; it must be a map of tools or "everything".`);
|
|
6088
|
+
} else if (!asked || typeof asked !== "object" || Array.isArray(asked)) {
|
|
6089
|
+
errors.push('`ask` must be a map of tools or "everything".');
|
|
6090
|
+
} else if (Object.keys(asked).length === 0) {
|
|
6091
|
+
errors.push('`ask` names no tools. Name at least one, or write `ask: "everything"`.');
|
|
6092
|
+
}
|
|
6093
|
+
const tools = asked && typeof asked === "object" && !Array.isArray(asked) ? Object.entries(asked) : [];
|
|
6094
|
+
for (const [tool, ask] of tools) {
|
|
6095
|
+
const access = ask?.access;
|
|
6096
|
+
if (access === "custom") {
|
|
6097
|
+
const actions2 = ask.actions;
|
|
6098
|
+
if (!Array.isArray(actions2) || actions2.length === 0) errors.push(`\`ask.${tool}\` is custom, so it needs at least one action.`);
|
|
6099
|
+
} else if (typeof access !== "string" || !(access in ACCESS_METHODS)) {
|
|
6100
|
+
errors.push(`\`ask.${tool}.access\` must be read-only, read-write, full-access or custom.`);
|
|
6101
|
+
}
|
|
6102
|
+
}
|
|
6103
|
+
return errors.length > 0 ? { errors } : { config: v };
|
|
6104
|
+
}
|
|
6105
|
+
function actionName(title) {
|
|
6106
|
+
const words = title.split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
6107
|
+
return words.map((word, i) => i === 0 ? word.toLowerCase() : word[0].toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
6108
|
+
}
|
|
6109
|
+
function actionNames(actions2) {
|
|
6110
|
+
const byName = /* @__PURE__ */ new Map();
|
|
6111
|
+
const shared = /* @__PURE__ */ new Set();
|
|
6112
|
+
for (const action of actions2) {
|
|
6113
|
+
const name = actionName(action.title);
|
|
6114
|
+
if (!name) continue;
|
|
6115
|
+
if (byName.has(name)) shared.add(name);
|
|
6116
|
+
else byName.set(name, action.systemId);
|
|
6117
|
+
}
|
|
6118
|
+
for (const name of shared) byName.delete(name);
|
|
6119
|
+
return byName;
|
|
6120
|
+
}
|
|
6121
|
+
function resolveActions(refs, names, ids) {
|
|
6122
|
+
const resolved = [];
|
|
6123
|
+
const unknown = [];
|
|
6124
|
+
for (const ref2 of refs) {
|
|
6125
|
+
const id = ref2.startsWith(ACTION_ID_PREFIX) ? ids.has(ref2) ? ref2 : void 0 : names.get(ref2);
|
|
6126
|
+
if (id) resolved.push(id);
|
|
6127
|
+
else unknown.push(ref2);
|
|
6128
|
+
}
|
|
6129
|
+
return unknown.length > 0 ? { unknown } : { ids: [...new Set(resolved)] };
|
|
6130
|
+
}
|
|
6131
|
+
function connectorRule(connectionDefinitionId, ask, actionIds = []) {
|
|
6132
|
+
if (ask.access === "custom") return { type: "connectionDefinition", connectionDefinitionId, methods: null, actionIds };
|
|
6133
|
+
return { type: "connectionDefinition", connectionDefinitionId, methods: ACCESS_METHODS[ask.access], actionIds: null };
|
|
6134
|
+
}
|
|
6135
|
+
function clientFields(config2) {
|
|
6136
|
+
const fields = { name: config2.name.trim(), redirectUris: config2.redirectUris };
|
|
6137
|
+
if (config2.description) fields.description = config2.description;
|
|
6138
|
+
if (config2.logoUrl) fields.logoUrl = config2.logoUrl;
|
|
6139
|
+
if (config2.homepageUrl) fields.homepageUrl = config2.homepageUrl;
|
|
6140
|
+
if (config2.reason) fields.consentReason = config2.reason.trim();
|
|
6141
|
+
if (config2.branding) {
|
|
6142
|
+
fields.frontend = {
|
|
6143
|
+
panelColor: config2.branding.panelColor,
|
|
6144
|
+
buttonColor: config2.branding.buttonColor,
|
|
6145
|
+
cobrandEmail: config2.branding.cobrandEmail ?? false
|
|
6146
|
+
};
|
|
6147
|
+
}
|
|
6148
|
+
return fields;
|
|
6149
|
+
}
|
|
6150
|
+
function buildHandoff(config2, rules) {
|
|
6151
|
+
return {
|
|
6152
|
+
v: 2,
|
|
6153
|
+
app: clientFields(config2),
|
|
6154
|
+
ask: config2.ask === "everything" ? { kind: "everything" } : { kind: "tools", rules }
|
|
6155
|
+
};
|
|
6156
|
+
}
|
|
6157
|
+
function encodeHandoff(handoff) {
|
|
6158
|
+
return zlib.deflateRawSync(Buffer.from(JSON.stringify(handoff), "utf8"), { level: 9 }).toString("base64url");
|
|
6159
|
+
}
|
|
6160
|
+
var MAX_HANDOFF_LENGTH = 32e3;
|
|
6161
|
+
var MAX_WINDOWS_OPEN_LENGTH = 11e3;
|
|
6162
|
+
function defaultEnvFile(cwd) {
|
|
6163
|
+
const local = path5.join(cwd, ".env.local");
|
|
6164
|
+
return fs5.existsSync(local) ? local : path5.join(cwd, ".env");
|
|
6165
|
+
}
|
|
6166
|
+
function readEnvValue(text5, key) {
|
|
6167
|
+
const match = text5.match(new RegExp(`^${key}=(.*)$`, "m"));
|
|
6168
|
+
return match ? match[1].trim().replace(/^["']|["']$/g, "") : void 0;
|
|
6169
|
+
}
|
|
6170
|
+
function upsertEnv(text5, values) {
|
|
6171
|
+
let next = text5;
|
|
6172
|
+
const appended = [];
|
|
6173
|
+
for (const [key, value] of Object.entries(values)) {
|
|
6174
|
+
const line = new RegExp(`^${key}=.*$`, "m");
|
|
6175
|
+
if (line.test(next)) {
|
|
6176
|
+
if (value !== null) next = next.replace(line, `${key}=${value}`);
|
|
6177
|
+
} else {
|
|
6178
|
+
appended.push(`${key}=${value ?? ""}`);
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
if (appended.length === 0) return next;
|
|
6182
|
+
const separator = next.length === 0 || next.endsWith("\n") ? "" : "\n";
|
|
6183
|
+
return `${next}${separator}${appended.join("\n")}
|
|
6184
|
+
`;
|
|
6185
|
+
}
|
|
6186
|
+
function removeEnvKeys(text5, keys) {
|
|
6187
|
+
return keys.reduce((next, key) => next.replace(new RegExp(`^${key}=.*(?:\r?
|
|
6188
|
+
|$)`, "m"), ""), text5);
|
|
6189
|
+
}
|
|
6190
|
+
function renderTypes(tools, actionsByTool) {
|
|
6191
|
+
const literal = (value) => JSON.stringify(value);
|
|
6192
|
+
const union = (values) => values.length > 0 ? values.map(literal).join(" | ") : "never";
|
|
6193
|
+
const actionLines = Object.entries(actionsByTool).sort(([a], [b]) => a.localeCompare(b)).map(([tool, names]) => ` ${literal(tool)}: ${union([...names].sort())};`);
|
|
6194
|
+
return [
|
|
6195
|
+
"// Generated by `one connect types`. Run it again after changing the tools in one.connect.ts.",
|
|
6196
|
+
'import "@withone/connect/config";',
|
|
6197
|
+
"",
|
|
6198
|
+
'declare module "@withone/connect/config" {',
|
|
6199
|
+
" interface ConnectRegistry {",
|
|
6200
|
+
` tools: ${union([...tools].sort())};`,
|
|
6201
|
+
" actions: {",
|
|
6202
|
+
...actionLines,
|
|
6203
|
+
" };",
|
|
6204
|
+
" }",
|
|
6205
|
+
"}",
|
|
6206
|
+
""
|
|
6207
|
+
].join("\n");
|
|
6208
|
+
}
|
|
6209
|
+
|
|
6210
|
+
// src/commands/connect.ts
|
|
6211
|
+
var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
6212
|
+
function requireApi() {
|
|
6213
|
+
const apiKey = getApiKey();
|
|
6214
|
+
if (!apiKey) error("Not configured. Run `one init` first.");
|
|
6215
|
+
return new OneApi(apiKey, getApiBase());
|
|
6216
|
+
}
|
|
6217
|
+
async function readConfig2(cwd, explicit) {
|
|
6218
|
+
const file = findConfigFile(cwd, explicit);
|
|
6219
|
+
if (!file) {
|
|
6220
|
+
error(
|
|
6221
|
+
explicit ? `No config at ${explicit}.` : "No one.connect.ts here. Create one with defineConnectApp from @withone/connect/config."
|
|
6222
|
+
);
|
|
6223
|
+
}
|
|
6224
|
+
let loaded;
|
|
6225
|
+
try {
|
|
6226
|
+
loaded = await loadConfigFile(file);
|
|
6227
|
+
} catch (error2) {
|
|
6228
|
+
error(`Couldn't load ${path6.basename(file)}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
6229
|
+
}
|
|
6230
|
+
const checked = validateConfig(loaded);
|
|
6231
|
+
if ("errors" in checked) error(`${path6.basename(file)} has problems:
|
|
6232
|
+
${checked.errors.join("\n ")}`);
|
|
6233
|
+
return { file, config: checked.config };
|
|
6234
|
+
}
|
|
6235
|
+
function message(error2) {
|
|
6236
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
6237
|
+
}
|
|
6238
|
+
async function buildRules(api, config2) {
|
|
6239
|
+
const asked = askedTools(config2);
|
|
6240
|
+
if (asked.length === 0) return [];
|
|
6241
|
+
const platforms = await api.listPlatforms();
|
|
6242
|
+
const idBySlug = new Map(platforms.map((p9) => [p9.platform, p9.id]));
|
|
6243
|
+
const unknownTools = asked.map(([slug]) => slug).filter((slug) => !idBySlug.has(slug));
|
|
6244
|
+
if (unknownTools.length > 0) {
|
|
6245
|
+
error(`Not in One's catalog: ${unknownTools.join(", ")}. Run \`one platforms\` for the slugs.`);
|
|
6246
|
+
}
|
|
6247
|
+
const rules = [];
|
|
6248
|
+
for (const [slug, ask] of asked) {
|
|
6249
|
+
const definitionId = idBySlug.get(slug);
|
|
6250
|
+
if (ask.access !== "custom") {
|
|
6251
|
+
rules.push(connectorRule(definitionId, ask));
|
|
6252
|
+
continue;
|
|
6253
|
+
}
|
|
6254
|
+
const actions2 = await api.listAllAvailableActions(slug);
|
|
6255
|
+
const resolved = resolveActions(ask.actions, actionNames(actions2), new Set(actions2.map((a) => a.systemId)));
|
|
6256
|
+
if ("unknown" in resolved) {
|
|
6257
|
+
error(
|
|
6258
|
+
`${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.`
|
|
6259
|
+
);
|
|
6260
|
+
}
|
|
6261
|
+
rules.push(connectorRule(definitionId, ask, resolved.ids));
|
|
6262
|
+
}
|
|
6263
|
+
return rules;
|
|
6264
|
+
}
|
|
6265
|
+
async function connectCreateCommand(options) {
|
|
6266
|
+
const cwd = process.cwd();
|
|
6267
|
+
const api = requireApi();
|
|
6268
|
+
const { file, config: config2 } = await readConfig2(cwd, options.config);
|
|
6269
|
+
const envFile = options.envFile ? path6.resolve(cwd, options.envFile) : defaultEnvFile(cwd);
|
|
6270
|
+
const envText = fs6.existsSync(envFile) ? fs6.readFileSync(envFile, "utf8") : "";
|
|
6271
|
+
const existingId = readEnvValue(envText, "ONE_CLIENT_ID") || void 0;
|
|
6272
|
+
const spinner = createSpinner();
|
|
6273
|
+
spinner.start(`Reading ${path6.basename(file)}...`);
|
|
6274
|
+
let server;
|
|
6275
|
+
let timer;
|
|
6276
|
+
try {
|
|
6277
|
+
const rules = await buildRules(api, config2);
|
|
6278
|
+
const whoami = await ensureWhoAmI(api);
|
|
6279
|
+
const scope = {
|
|
6280
|
+
orgId: whoami?.organization?.id,
|
|
6281
|
+
projectId: whoami?.project?.id,
|
|
6282
|
+
envFile: path6.basename(envFile)
|
|
6283
|
+
};
|
|
6284
|
+
const handoff = encodeHandoff(buildHandoff(config2, rules));
|
|
6285
|
+
if (handoff.length > MAX_HANDOFF_LENGTH) {
|
|
6286
|
+
error(
|
|
6287
|
+
`${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.`
|
|
6288
|
+
);
|
|
6289
|
+
}
|
|
6290
|
+
const state = crypto2.randomBytes(16).toString("hex");
|
|
6291
|
+
const started = await startConnectCallbackServer(
|
|
6292
|
+
state,
|
|
6293
|
+
(done) => getConnectDoneUrl(done.clientId, done.outcome, scope)
|
|
6294
|
+
);
|
|
6295
|
+
server = started.server;
|
|
6296
|
+
const url = getConnectSetupUrl({
|
|
6297
|
+
...scope,
|
|
6298
|
+
clientId: existingId,
|
|
6299
|
+
permissionSetId: readEnvValue(envText, "ONE_PERMISSION_SET") || void 0,
|
|
6300
|
+
port: started.port,
|
|
6301
|
+
state,
|
|
6302
|
+
handoff
|
|
6303
|
+
});
|
|
6304
|
+
const tooLongToOpen = process.platform === "win32" && url.length > MAX_WINDOWS_OPEN_LENGTH;
|
|
6305
|
+
const opening = options.open !== false && !tooLongToOpen;
|
|
6306
|
+
if (opening) await open4(url).catch(() => void 0);
|
|
6307
|
+
const verb = existingId ? "Approve the update" : "Create the app";
|
|
6308
|
+
if (isAgentMode()) {
|
|
6309
|
+
process.stderr.write(`${verb} in the browser: ${url}
|
|
6310
|
+
`);
|
|
6311
|
+
} else {
|
|
6312
|
+
spinner.stop(opening ? `${verb} in your browser` : `${verb}: open this link in your browser`);
|
|
6313
|
+
console.log(` ${pc10.underline(url)}`);
|
|
6314
|
+
console.log();
|
|
6315
|
+
spinner.start("Waiting for you to approve it...");
|
|
6316
|
+
}
|
|
6317
|
+
const outcome = await Promise.race([
|
|
6318
|
+
started.result,
|
|
6319
|
+
new Promise((resolve) => {
|
|
6320
|
+
timer = setTimeout(() => resolve(null), SETUP_TIMEOUT_MS);
|
|
6321
|
+
})
|
|
6322
|
+
]);
|
|
6323
|
+
if (!outcome) error("Timed out waiting for the browser. Run `one connect create` again.");
|
|
6324
|
+
if (outcome.kind === "cancelled") error("Cancelled in the browser. Nothing was changed.");
|
|
6325
|
+
if (outcome.kind === "failed") error(`The setup page reported: ${outcome.reason}`);
|
|
6326
|
+
const created = outcome.outcome === "created";
|
|
6327
|
+
const asksForEverything = config2.ask === "everything";
|
|
6328
|
+
fs6.writeFileSync(
|
|
6329
|
+
envFile,
|
|
6330
|
+
upsertEnv(asksForEverything ? removeEnvKeys(envText, ["ONE_PERMISSION_SET"]) : envText, {
|
|
6331
|
+
ONE_CLIENT_ID: outcome.clientId,
|
|
6332
|
+
ONE_REDIRECT_URI: config2.redirectUris[0],
|
|
6333
|
+
...!asksForEverything && outcome.permissionSetId ? { ONE_PERMISSION_SET: outcome.permissionSetId } : {},
|
|
6334
|
+
...created ? { ONE_CLIENT_SECRET: null, ONE_CONNECT_KEY: null } : {}
|
|
6335
|
+
})
|
|
6336
|
+
);
|
|
6337
|
+
const result = {
|
|
6338
|
+
outcome: outcome.outcome,
|
|
6339
|
+
clientId: outcome.clientId,
|
|
6340
|
+
permissionSetId: asksForEverything ? null : outcome.permissionSetId,
|
|
6341
|
+
asksForEverything,
|
|
6342
|
+
redirectUris: config2.redirectUris,
|
|
6343
|
+
envFile: path6.relative(cwd, envFile) || envFile
|
|
6344
|
+
};
|
|
6345
|
+
if (isAgentMode()) {
|
|
6346
|
+
json(result);
|
|
6347
|
+
return;
|
|
6348
|
+
}
|
|
6349
|
+
spinner.stop(
|
|
6350
|
+
created ? `Created ${config2.name}` : outcome.outcome === "updated" ? `Updated ${config2.name}` : `${config2.name} is up to date`
|
|
6351
|
+
);
|
|
6352
|
+
console.log();
|
|
6353
|
+
console.log(` ${pc10.dim("Client ID:")} ${outcome.clientId}`);
|
|
6354
|
+
if (asksForEverything) console.log(` ${pc10.dim("Asks for:")} everything the user has connected`);
|
|
6355
|
+
else if (outcome.permissionSetId) console.log(` ${pc10.dim("Permission set:")} ${outcome.permissionSetId}`);
|
|
6356
|
+
console.log(` ${pc10.dim("Wrote")} ${result.envFile}`);
|
|
6357
|
+
if (created) {
|
|
6358
|
+
console.log(` ${pc10.dim("The two secrets came from the browser page, never through here.")}`);
|
|
6359
|
+
}
|
|
6360
|
+
console.log();
|
|
6361
|
+
} catch (error2) {
|
|
6362
|
+
spinner.stop("Failed");
|
|
6363
|
+
error(`Error: ${message(error2)}`);
|
|
6364
|
+
} finally {
|
|
6365
|
+
if (timer) clearTimeout(timer);
|
|
6366
|
+
server?.close();
|
|
6367
|
+
}
|
|
6368
|
+
}
|
|
6369
|
+
async function connectTypesCommand(options) {
|
|
6370
|
+
const cwd = process.cwd();
|
|
6371
|
+
const api = requireApi();
|
|
6372
|
+
const spinner = createSpinner();
|
|
6373
|
+
spinner.start("Reading One's catalog...");
|
|
6374
|
+
try {
|
|
6375
|
+
const file = findConfigFile(cwd, options.config);
|
|
6376
|
+
const config2 = file ? validateConfig(await loadConfigFile(file)) : null;
|
|
6377
|
+
const tools = config2 && "config" in config2 ? askedTools(config2.config).map(([slug]) => slug) : [];
|
|
6378
|
+
const platforms = await api.listPlatforms();
|
|
6379
|
+
const known = new Set(platforms.map((p9) => p9.platform));
|
|
6380
|
+
const actionsByTool = {};
|
|
6381
|
+
for (const tool of tools.filter((t) => known.has(t))) {
|
|
6382
|
+
actionsByTool[tool] = [...actionNames(await api.listAllAvailableActions(tool)).keys()];
|
|
6383
|
+
}
|
|
6384
|
+
const target = path6.join(file ? path6.dirname(file) : cwd, TYPES_FILE);
|
|
6385
|
+
fs6.writeFileSync(target, renderTypes([...known], actionsByTool));
|
|
6386
|
+
if (isAgentMode()) {
|
|
6387
|
+
json({ file: target, tools: known.size, actions: actionsByTool });
|
|
6388
|
+
return;
|
|
6389
|
+
}
|
|
6390
|
+
spinner.stop(`Wrote ${path6.relative(cwd, target) || target}`);
|
|
6391
|
+
console.log(` ${pc10.dim(`${known.size} tools; action names for ${Object.keys(actionsByTool).join(", ") || "none yet (add tools to ask, then run it again)"}`)}`);
|
|
6392
|
+
} catch (error2) {
|
|
6393
|
+
spinner.stop("Failed");
|
|
6394
|
+
error(`Error: ${message(error2)}`);
|
|
6395
|
+
}
|
|
6396
|
+
}
|
|
6397
|
+
|
|
5895
6398
|
// src/lib/memory/sync/models.ts
|
|
5896
6399
|
function parseActionType(actionKey) {
|
|
5897
6400
|
const parts = actionKey.split("::");
|
|
@@ -6013,11 +6516,11 @@ function handleId(response, config2, records) {
|
|
|
6013
6516
|
}
|
|
6014
6517
|
|
|
6015
6518
|
// src/lib/memory/sync/state.ts
|
|
6016
|
-
import
|
|
6017
|
-
import
|
|
6018
|
-
var SYNC_DIR =
|
|
6019
|
-
var STATE_DIR =
|
|
6020
|
-
var LEGACY_SINGLE_FILE =
|
|
6519
|
+
import fs7 from "fs";
|
|
6520
|
+
import path7 from "path";
|
|
6521
|
+
var SYNC_DIR = path7.join(".one", "sync");
|
|
6522
|
+
var STATE_DIR = path7.join(SYNC_DIR, "state");
|
|
6523
|
+
var LEGACY_SINGLE_FILE = path7.join(SYNC_DIR, "sync_state.json");
|
|
6021
6524
|
var legacyMigrationDone = false;
|
|
6022
6525
|
function rowToState(row) {
|
|
6023
6526
|
return {
|
|
@@ -6045,9 +6548,9 @@ function stateToRow(platform, model, state, lastError) {
|
|
|
6045
6548
|
async function migrateLegacyOnce() {
|
|
6046
6549
|
if (legacyMigrationDone) return;
|
|
6047
6550
|
legacyMigrationDone = true;
|
|
6048
|
-
if (
|
|
6551
|
+
if (fs7.existsSync(LEGACY_SINGLE_FILE)) {
|
|
6049
6552
|
try {
|
|
6050
|
-
const raw =
|
|
6553
|
+
const raw = fs7.readFileSync(LEGACY_SINGLE_FILE, "utf-8");
|
|
6051
6554
|
const legacy = JSON.parse(raw);
|
|
6052
6555
|
const backend = await getBackend();
|
|
6053
6556
|
for (const [platform, models] of Object.entries(legacy)) {
|
|
@@ -6057,32 +6560,32 @@ async function migrateLegacyOnce() {
|
|
|
6057
6560
|
await backend.setSyncState(stateToRow(platform, model, modelState));
|
|
6058
6561
|
}
|
|
6059
6562
|
}
|
|
6060
|
-
|
|
6563
|
+
fs7.unlinkSync(LEGACY_SINGLE_FILE);
|
|
6061
6564
|
} catch {
|
|
6062
6565
|
try {
|
|
6063
|
-
|
|
6566
|
+
fs7.unlinkSync(LEGACY_SINGLE_FILE);
|
|
6064
6567
|
} catch {
|
|
6065
6568
|
}
|
|
6066
6569
|
}
|
|
6067
6570
|
}
|
|
6068
|
-
if (
|
|
6571
|
+
if (fs7.existsSync(STATE_DIR)) {
|
|
6069
6572
|
try {
|
|
6070
6573
|
const backend = await getBackend();
|
|
6071
|
-
const platforms =
|
|
6574
|
+
const platforms = fs7.readdirSync(STATE_DIR);
|
|
6072
6575
|
for (const platform of platforms) {
|
|
6073
|
-
const platformDir =
|
|
6576
|
+
const platformDir = path7.join(STATE_DIR, platform);
|
|
6074
6577
|
let entries;
|
|
6075
6578
|
try {
|
|
6076
|
-
entries =
|
|
6579
|
+
entries = fs7.readdirSync(platformDir);
|
|
6077
6580
|
} catch {
|
|
6078
6581
|
continue;
|
|
6079
6582
|
}
|
|
6080
6583
|
for (const entry of entries) {
|
|
6081
6584
|
if (!entry.endsWith(".json")) continue;
|
|
6082
6585
|
const model = entry.slice(0, -".json".length);
|
|
6083
|
-
const filePath =
|
|
6586
|
+
const filePath = path7.join(platformDir, entry);
|
|
6084
6587
|
try {
|
|
6085
|
-
const raw =
|
|
6588
|
+
const raw = fs7.readFileSync(filePath, "utf-8");
|
|
6086
6589
|
const modelState = JSON.parse(raw);
|
|
6087
6590
|
const existing = await backend.getSyncState(platform, model);
|
|
6088
6591
|
if (!existing) {
|
|
@@ -6092,7 +6595,7 @@ async function migrateLegacyOnce() {
|
|
|
6092
6595
|
}
|
|
6093
6596
|
}
|
|
6094
6597
|
}
|
|
6095
|
-
|
|
6598
|
+
fs7.rmSync(STATE_DIR, { recursive: true, force: true });
|
|
6096
6599
|
} catch {
|
|
6097
6600
|
}
|
|
6098
6601
|
}
|
|
@@ -6136,12 +6639,12 @@ async function removeModelState(platform, model) {
|
|
|
6136
6639
|
}
|
|
6137
6640
|
|
|
6138
6641
|
// src/lib/memory/sync/lock.ts
|
|
6139
|
-
import
|
|
6140
|
-
import
|
|
6141
|
-
var LOCK_DIR_REL =
|
|
6642
|
+
import fs8 from "fs";
|
|
6643
|
+
import path8 from "path";
|
|
6644
|
+
var LOCK_DIR_REL = path8.join(".one", "sync", "locks");
|
|
6142
6645
|
var STALE_MS = 30 * 60 * 1e3;
|
|
6143
6646
|
function lockPath(platform, model) {
|
|
6144
|
-
return
|
|
6647
|
+
return path8.join(LOCK_DIR_REL, `${platform}_${model}`);
|
|
6145
6648
|
}
|
|
6146
6649
|
function isProcessAlive(pid) {
|
|
6147
6650
|
try {
|
|
@@ -6152,21 +6655,21 @@ function isProcessAlive(pid) {
|
|
|
6152
6655
|
}
|
|
6153
6656
|
}
|
|
6154
6657
|
var SyncLockError = class extends Error {
|
|
6155
|
-
constructor(
|
|
6156
|
-
super(
|
|
6658
|
+
constructor(message2) {
|
|
6659
|
+
super(message2);
|
|
6157
6660
|
this.name = "SyncLockError";
|
|
6158
6661
|
}
|
|
6159
6662
|
};
|
|
6160
6663
|
function acquireSyncLock(platform, model) {
|
|
6161
|
-
|
|
6664
|
+
fs8.mkdirSync(LOCK_DIR_REL, { recursive: true });
|
|
6162
6665
|
const dir = lockPath(platform, model);
|
|
6163
|
-
const pidFile =
|
|
6666
|
+
const pidFile = path8.join(dir, "pid");
|
|
6164
6667
|
try {
|
|
6165
|
-
|
|
6668
|
+
fs8.mkdirSync(dir);
|
|
6166
6669
|
} catch (err) {
|
|
6167
6670
|
const stat = (() => {
|
|
6168
6671
|
try {
|
|
6169
|
-
return
|
|
6672
|
+
return fs8.statSync(dir);
|
|
6170
6673
|
} catch {
|
|
6171
6674
|
return null;
|
|
6172
6675
|
}
|
|
@@ -6175,7 +6678,7 @@ function acquireSyncLock(platform, model) {
|
|
|
6175
6678
|
const age = Date.now() - stat.mtimeMs;
|
|
6176
6679
|
let ownerPid = null;
|
|
6177
6680
|
try {
|
|
6178
|
-
const raw =
|
|
6681
|
+
const raw = fs8.readFileSync(pidFile, "utf-8");
|
|
6179
6682
|
const parsed = parseInt(raw.trim(), 10);
|
|
6180
6683
|
if (!isNaN(parsed)) ownerPid = parsed;
|
|
6181
6684
|
} catch {
|
|
@@ -6184,8 +6687,8 @@ function acquireSyncLock(platform, model) {
|
|
|
6184
6687
|
const veryOld = age > STALE_MS;
|
|
6185
6688
|
if (ownerDead || veryOld) {
|
|
6186
6689
|
try {
|
|
6187
|
-
|
|
6188
|
-
|
|
6690
|
+
fs8.rmSync(dir, { recursive: true, force: true });
|
|
6691
|
+
fs8.mkdirSync(dir);
|
|
6189
6692
|
} catch {
|
|
6190
6693
|
throw new SyncLockError(
|
|
6191
6694
|
`Could not take over stale lock at ${dir}. Remove it manually if no sync is running.`
|
|
@@ -6202,13 +6705,13 @@ function acquireSyncLock(platform, model) {
|
|
|
6202
6705
|
}
|
|
6203
6706
|
}
|
|
6204
6707
|
try {
|
|
6205
|
-
|
|
6708
|
+
fs8.writeFileSync(pidFile, String(process.pid));
|
|
6206
6709
|
} catch {
|
|
6207
6710
|
}
|
|
6208
6711
|
return {
|
|
6209
6712
|
release() {
|
|
6210
6713
|
try {
|
|
6211
|
-
|
|
6714
|
+
fs8.rmSync(dir, { recursive: true, force: true });
|
|
6212
6715
|
} catch {
|
|
6213
6716
|
}
|
|
6214
6717
|
}
|
|
@@ -6217,9 +6720,9 @@ function acquireSyncLock(platform, model) {
|
|
|
6217
6720
|
|
|
6218
6721
|
// src/lib/memory/sync/hooks.ts
|
|
6219
6722
|
import { spawn as spawn2 } from "child_process";
|
|
6220
|
-
import
|
|
6221
|
-
import
|
|
6222
|
-
var EVENTS_DIR =
|
|
6723
|
+
import fs9 from "fs";
|
|
6724
|
+
import path9 from "path";
|
|
6725
|
+
var EVENTS_DIR = path9.join(".one", "sync", "events");
|
|
6223
6726
|
function classifyRecords(db, model, records, idField, tableExists2) {
|
|
6224
6727
|
if (!tableExists2 || records.length === 0) {
|
|
6225
6728
|
return { inserts: records, updates: [] };
|
|
@@ -6265,10 +6768,10 @@ async function fireHooks(hookCommand, events) {
|
|
|
6265
6768
|
function appendEventLog(events) {
|
|
6266
6769
|
if (events.length === 0) return;
|
|
6267
6770
|
const { platform, model } = events[0];
|
|
6268
|
-
|
|
6269
|
-
const logPath =
|
|
6771
|
+
fs9.mkdirSync(EVENTS_DIR, { recursive: true });
|
|
6772
|
+
const logPath = path9.join(EVENTS_DIR, `${platform}_${model}.jsonl`);
|
|
6270
6773
|
const lines = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
6271
|
-
|
|
6774
|
+
fs9.appendFileSync(logPath, lines);
|
|
6272
6775
|
}
|
|
6273
6776
|
function runShellHook(command, events) {
|
|
6274
6777
|
return new Promise((resolve) => {
|
|
@@ -6372,8 +6875,8 @@ function sleep(ms) {
|
|
|
6372
6875
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6373
6876
|
}
|
|
6374
6877
|
function interpolate(template, record) {
|
|
6375
|
-
return template.replace(/\{?\{(\w+(?:\.\w+)*)\}\}?/g, (_,
|
|
6376
|
-
const parts =
|
|
6878
|
+
return template.replace(/\{?\{(\w+(?:\.\w+)*)\}\}?/g, (_, path15) => {
|
|
6879
|
+
const parts = path15.split(".");
|
|
6377
6880
|
let value = record;
|
|
6378
6881
|
for (const part2 of parts) {
|
|
6379
6882
|
if (typeof value !== "object" || value === null) return "";
|
|
@@ -6401,8 +6904,8 @@ function deepMerge(target, source) {
|
|
|
6401
6904
|
}
|
|
6402
6905
|
return result;
|
|
6403
6906
|
}
|
|
6404
|
-
function getByDotPath2(obj,
|
|
6405
|
-
const parts =
|
|
6907
|
+
function getByDotPath2(obj, path15) {
|
|
6908
|
+
const parts = path15.split(".");
|
|
6406
6909
|
let current = obj;
|
|
6407
6910
|
for (const part2 of parts) {
|
|
6408
6911
|
if (current === null || current === void 0 || typeof current !== "object") return void 0;
|
|
@@ -6411,8 +6914,8 @@ function getByDotPath2(obj, path13) {
|
|
|
6411
6914
|
return current;
|
|
6412
6915
|
}
|
|
6413
6916
|
function stripExcludedFields(obj, paths) {
|
|
6414
|
-
for (const
|
|
6415
|
-
stripOnePath(obj,
|
|
6917
|
+
for (const path15 of paths) {
|
|
6918
|
+
stripOnePath(obj, path15.replace(/\[\]/g, ".*").split("."));
|
|
6416
6919
|
}
|
|
6417
6920
|
}
|
|
6418
6921
|
function stripOnePath(obj, parts) {
|
|
@@ -6833,8 +7336,8 @@ function sleep2(ms) {
|
|
|
6833
7336
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6834
7337
|
}
|
|
6835
7338
|
function stripFields(record, paths) {
|
|
6836
|
-
for (const
|
|
6837
|
-
stripOnePath2(record,
|
|
7339
|
+
for (const path15 of paths) {
|
|
7340
|
+
stripOnePath2(record, path15.split("."));
|
|
6838
7341
|
}
|
|
6839
7342
|
}
|
|
6840
7343
|
function stripOnePath2(obj, parts) {
|
|
@@ -7927,19 +8430,19 @@ function inferProfileFromKnowledge(knowledge, modelName, platform) {
|
|
|
7927
8430
|
|
|
7928
8431
|
// src/lib/memory/sync/schedule.ts
|
|
7929
8432
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
7930
|
-
import
|
|
8433
|
+
import fs11 from "fs";
|
|
7931
8434
|
import os from "os";
|
|
7932
|
-
import
|
|
8435
|
+
import path11 from "path";
|
|
7933
8436
|
|
|
7934
8437
|
// src/lib/memory/sync/schedule-registry.ts
|
|
7935
|
-
import
|
|
7936
|
-
import
|
|
7937
|
-
var REGISTRY_DIR = () =>
|
|
7938
|
-
var REGISTRY_FILE = () =>
|
|
8438
|
+
import fs10 from "fs";
|
|
8439
|
+
import path10 from "path";
|
|
8440
|
+
var REGISTRY_DIR = () => path10.join(homeDir(), ".one", "sync");
|
|
8441
|
+
var REGISTRY_FILE = () => path10.join(REGISTRY_DIR(), "schedules.json");
|
|
7939
8442
|
function readRaw() {
|
|
7940
8443
|
try {
|
|
7941
|
-
if (!
|
|
7942
|
-
const raw =
|
|
8444
|
+
if (!fs10.existsSync(REGISTRY_FILE())) return { schedules: [] };
|
|
8445
|
+
const raw = fs10.readFileSync(REGISTRY_FILE(), "utf-8");
|
|
7943
8446
|
const parsed = JSON.parse(raw);
|
|
7944
8447
|
if (!parsed || !Array.isArray(parsed.schedules)) return { schedules: [] };
|
|
7945
8448
|
return parsed;
|
|
@@ -7948,13 +8451,13 @@ function readRaw() {
|
|
|
7948
8451
|
}
|
|
7949
8452
|
}
|
|
7950
8453
|
function writeRaw(file) {
|
|
7951
|
-
|
|
8454
|
+
fs10.mkdirSync(REGISTRY_DIR(), { recursive: true });
|
|
7952
8455
|
const tmp = REGISTRY_FILE() + ".tmp";
|
|
7953
|
-
|
|
7954
|
-
|
|
8456
|
+
fs10.writeFileSync(tmp, JSON.stringify(file, null, 2));
|
|
8457
|
+
fs10.renameSync(tmp, REGISTRY_FILE());
|
|
7955
8458
|
}
|
|
7956
8459
|
function makeScheduleId(platform, cwd) {
|
|
7957
|
-
const slug =
|
|
8460
|
+
const slug = path10.basename(cwd).replace(/[^a-zA-Z0-9_-]/g, "-").toLowerCase();
|
|
7958
8461
|
return `${platform}-${slug}`;
|
|
7959
8462
|
}
|
|
7960
8463
|
function listRegistered() {
|
|
@@ -7988,7 +8491,7 @@ function removeRegistered(id) {
|
|
|
7988
8491
|
|
|
7989
8492
|
// src/lib/memory/sync/schedule.ts
|
|
7990
8493
|
var MARKER = "# one-sync";
|
|
7991
|
-
var LOG_DIR_REL =
|
|
8494
|
+
var LOG_DIR_REL = path11.join(".one", "sync", "logs");
|
|
7992
8495
|
function durationToCron(every) {
|
|
7993
8496
|
const match = every.match(/^(\d+)([mhd])$/);
|
|
7994
8497
|
if (!match) return null;
|
|
@@ -8026,8 +8529,8 @@ function isWindows() {
|
|
|
8026
8529
|
function resolveOneBinary() {
|
|
8027
8530
|
try {
|
|
8028
8531
|
const entry = process.argv[1];
|
|
8029
|
-
if (entry &&
|
|
8030
|
-
return
|
|
8532
|
+
if (entry && fs11.existsSync(entry)) {
|
|
8533
|
+
return fs11.realpathSync(entry);
|
|
8031
8534
|
}
|
|
8032
8535
|
} catch {
|
|
8033
8536
|
}
|
|
@@ -8105,7 +8608,7 @@ function migrateLegacyCronEntries() {
|
|
|
8105
8608
|
const modelsMatch = command.match(/--models\s+(\S+)/);
|
|
8106
8609
|
const models = modelsMatch ? modelsMatch[1].split(",") : void 0;
|
|
8107
8610
|
const logMatch = command.match(/>>\s+"([^"]+)"/);
|
|
8108
|
-
const logFile = logMatch ? logMatch[1] :
|
|
8611
|
+
const logFile = logMatch ? logMatch[1] : path11.resolve(cwd, LOG_DIR_REL, `${platform}.log`);
|
|
8109
8612
|
const id = makeScheduleId(platform, cwd);
|
|
8110
8613
|
if (registeredIds.has(id)) continue;
|
|
8111
8614
|
upsertRegistered({
|
|
@@ -8139,9 +8642,9 @@ function addSchedule(opts) {
|
|
|
8139
8642
|
const cwd = process.cwd();
|
|
8140
8643
|
const id = makeScheduleId(opts.platform, cwd);
|
|
8141
8644
|
const replaced = getRegistered(id) !== void 0;
|
|
8142
|
-
const logDir =
|
|
8143
|
-
|
|
8144
|
-
const logFile =
|
|
8645
|
+
const logDir = path11.join(cwd, LOG_DIR_REL);
|
|
8646
|
+
fs11.mkdirSync(logDir, { recursive: true });
|
|
8647
|
+
const logFile = path11.join(logDir, `${opts.platform}.log`);
|
|
8145
8648
|
const entry = {
|
|
8146
8649
|
id,
|
|
8147
8650
|
platform: opts.platform,
|
|
@@ -8195,15 +8698,15 @@ function removeSchedule(idOrPlatform, options) {
|
|
|
8195
8698
|
function scheduleStatus() {
|
|
8196
8699
|
const entries = listSchedules();
|
|
8197
8700
|
return entries.map((entry) => {
|
|
8198
|
-
const logExists =
|
|
8199
|
-
const logSize = logExists ?
|
|
8701
|
+
const logExists = fs11.existsSync(entry.logFile);
|
|
8702
|
+
const logSize = logExists ? fs11.statSync(entry.logFile).size : 0;
|
|
8200
8703
|
let logTail = [];
|
|
8201
8704
|
let lastRunAt = null;
|
|
8202
8705
|
if (logExists) {
|
|
8203
8706
|
try {
|
|
8204
|
-
lastRunAt =
|
|
8707
|
+
lastRunAt = fs11.statSync(entry.logFile).mtime.toISOString();
|
|
8205
8708
|
if (logSize > 0) {
|
|
8206
|
-
const content =
|
|
8709
|
+
const content = fs11.readFileSync(entry.logFile, "utf-8");
|
|
8207
8710
|
logTail = content.trim().split("\n").slice(-10);
|
|
8208
8711
|
}
|
|
8209
8712
|
} catch {
|
|
@@ -8211,8 +8714,8 @@ function scheduleStatus() {
|
|
|
8211
8714
|
}
|
|
8212
8715
|
let drift = "ok";
|
|
8213
8716
|
if (!entry.cronInstalled) drift = "missing-cron";
|
|
8214
|
-
else if (!
|
|
8215
|
-
else if (!
|
|
8717
|
+
else if (!fs11.existsSync(entry.nodeBin)) drift = "stale-node-bin";
|
|
8718
|
+
else if (!fs11.existsSync(entry.cliBin)) drift = "stale-cli-bin";
|
|
8216
8719
|
return { entry, logExists, logSize, logTail, lastRunAt, drift };
|
|
8217
8720
|
});
|
|
8218
8721
|
}
|
|
@@ -8485,11 +8988,11 @@ function isNoise(s) {
|
|
|
8485
8988
|
if (/^-?\d+(\.\d+)?([eE][-+]?\d+)?$/.test(s)) return true;
|
|
8486
8989
|
return false;
|
|
8487
8990
|
}
|
|
8488
|
-
function recordSample(stats,
|
|
8489
|
-
let s = stats.get(
|
|
8991
|
+
function recordSample(stats, path15, value, jsType, recordIndex, totalSamples) {
|
|
8992
|
+
let s = stats.get(path15);
|
|
8490
8993
|
if (!s) {
|
|
8491
8994
|
s = {
|
|
8492
|
-
path:
|
|
8995
|
+
path: path15,
|
|
8493
8996
|
total: totalSamples,
|
|
8494
8997
|
recordIndices: /* @__PURE__ */ new Set(),
|
|
8495
8998
|
lenSum: 0,
|
|
@@ -8498,7 +9001,7 @@ function recordSample(stats, path13, value, jsType, recordIndex, totalSamples) {
|
|
|
8498
9001
|
primaryType: jsType,
|
|
8499
9002
|
examples: []
|
|
8500
9003
|
};
|
|
8501
|
-
stats.set(
|
|
9004
|
+
stats.set(path15, s);
|
|
8502
9005
|
}
|
|
8503
9006
|
s.recordIndices.add(recordIndex);
|
|
8504
9007
|
s.lenSum += value.length;
|
|
@@ -8507,28 +9010,28 @@ function recordSample(stats, path13, value, jsType, recordIndex, totalSamples) {
|
|
|
8507
9010
|
if (s.primaryType !== jsType) s.primaryType = "mixed";
|
|
8508
9011
|
if (s.examples.length < 3 && value.length < 200) s.examples.push(value);
|
|
8509
9012
|
}
|
|
8510
|
-
function walkRecord(record,
|
|
9013
|
+
function walkRecord(record, path15, stats, recordIndex, totalSamples) {
|
|
8511
9014
|
if (record === null || record === void 0) return;
|
|
8512
9015
|
if (typeof record === "string") {
|
|
8513
9016
|
const trimmed = record.trim();
|
|
8514
|
-
if (trimmed) recordSample(stats,
|
|
9017
|
+
if (trimmed) recordSample(stats, path15, trimmed, "string", recordIndex, totalSamples);
|
|
8515
9018
|
return;
|
|
8516
9019
|
}
|
|
8517
9020
|
if (typeof record === "number" || typeof record === "boolean") {
|
|
8518
9021
|
const str = String(record);
|
|
8519
9022
|
const kind = typeof record === "number" ? "number" : "boolean";
|
|
8520
|
-
if (str) recordSample(stats,
|
|
9023
|
+
if (str) recordSample(stats, path15, str, kind, recordIndex, totalSamples);
|
|
8521
9024
|
return;
|
|
8522
9025
|
}
|
|
8523
9026
|
if (Array.isArray(record)) {
|
|
8524
|
-
const childPath =
|
|
9027
|
+
const childPath = path15 ? `${path15}[]` : "[]";
|
|
8525
9028
|
for (const item of record) walkRecord(item, childPath, stats, recordIndex, totalSamples);
|
|
8526
9029
|
return;
|
|
8527
9030
|
}
|
|
8528
9031
|
if (typeof record === "object") {
|
|
8529
9032
|
for (const [key, value] of Object.entries(record)) {
|
|
8530
9033
|
if (key.startsWith("_")) continue;
|
|
8531
|
-
const childPath =
|
|
9034
|
+
const childPath = path15 ? `${path15}.${key}` : key;
|
|
8532
9035
|
walkRecord(value, childPath, stats, recordIndex, totalSamples);
|
|
8533
9036
|
}
|
|
8534
9037
|
}
|
|
@@ -8566,7 +9069,7 @@ function suggestSearchablePaths(records, limit = 15) {
|
|
|
8566
9069
|
// src/lib/memory/sync/index.ts
|
|
8567
9070
|
import { spawn as spawn4 } from "child_process";
|
|
8568
9071
|
import * as p6 from "@clack/prompts";
|
|
8569
|
-
import
|
|
9072
|
+
import pc11 from "picocolors";
|
|
8570
9073
|
async function syncInstallCommand() {
|
|
8571
9074
|
if (await isSqliteAvailable()) {
|
|
8572
9075
|
if (isAgentMode()) {
|
|
@@ -8631,15 +9134,15 @@ async function syncDoctorCommand() {
|
|
|
8631
9134
|
return;
|
|
8632
9135
|
}
|
|
8633
9136
|
for (const c of checks) {
|
|
8634
|
-
const mark = c.ok ?
|
|
8635
|
-
console.log(` ${mark} ${c.name}${c.detail ?
|
|
9137
|
+
const mark = c.ok ? pc11.green("\u2713") : pc11.red("\u2717");
|
|
9138
|
+
console.log(` ${mark} ${c.name}${c.detail ? pc11.dim(` \u2014 ${c.detail}`) : ""}`);
|
|
8636
9139
|
}
|
|
8637
9140
|
if (!allOk) {
|
|
8638
9141
|
console.log(`
|
|
8639
|
-
${
|
|
9142
|
+
${pc11.yellow("Sync is not ready.")} Try: ${pc11.bold("one sync install")}`);
|
|
8640
9143
|
} else {
|
|
8641
9144
|
console.log(`
|
|
8642
|
-
${
|
|
9145
|
+
${pc11.green("Sync is ready.")}`);
|
|
8643
9146
|
}
|
|
8644
9147
|
}
|
|
8645
9148
|
function getApi() {
|
|
@@ -8682,11 +9185,11 @@ async function syncProfilesCommand(platform) {
|
|
|
8682
9185
|
if (p9.enrich) extras.push("enrich");
|
|
8683
9186
|
if (p9.identityKey || p9.identityKeys) extras.push("identity");
|
|
8684
9187
|
if (p9.dateFilter) extras.push("incremental");
|
|
8685
|
-
const tags = extras.length > 0 ? ` ${
|
|
8686
|
-
console.log(` ${
|
|
9188
|
+
const tags = extras.length > 0 ? ` ${pc11.dim(`[${extras.join(", ")}]`)}` : "";
|
|
9189
|
+
console.log(` ${pc11.bold(`${p9.platform}/${p9.model}`.padEnd(35))} ${p9.description}${tags}`);
|
|
8687
9190
|
}
|
|
8688
9191
|
console.log(`
|
|
8689
|
-
${profiles.length} built-in profile(s). Run ${
|
|
9192
|
+
${profiles.length} built-in profile(s). Run ${pc11.bold("one sync init <platform> <model>")} to use one.`);
|
|
8690
9193
|
}
|
|
8691
9194
|
async function syncModelsCommand(platform) {
|
|
8692
9195
|
const api = getApi();
|
|
@@ -8704,7 +9207,7 @@ async function syncModelsCommand(platform) {
|
|
|
8704
9207
|
return;
|
|
8705
9208
|
}
|
|
8706
9209
|
const lines = models.map(
|
|
8707
|
-
(m) => ` ${
|
|
9210
|
+
(m) => ` ${pc11.bold(m.name.padEnd(30))} ${pc11.dim(m.listAction.method)} ${pc11.dim(m.listAction.path)}`
|
|
8708
9211
|
);
|
|
8709
9212
|
note(lines.join("\n"), `${platform} \u2014 ${models.length} models`);
|
|
8710
9213
|
} catch (err) {
|
|
@@ -8808,15 +9311,15 @@ async function syncInitCommand(platform, model, options) {
|
|
|
8808
9311
|
note(JSON.stringify(template, null, 2), "Sync profile template");
|
|
8809
9312
|
if (inferred && inferred.reasoning.length > 0) {
|
|
8810
9313
|
console.log(`
|
|
8811
|
-
${
|
|
8812
|
-
for (const r of inferred.reasoning) console.log(` ${
|
|
9314
|
+
${pc11.bold("Inferred from knowledge:")}`);
|
|
9315
|
+
for (const r of inferred.reasoning) console.log(` ${pc11.dim("\u2022")} ${r}`);
|
|
8813
9316
|
}
|
|
8814
9317
|
if (testReport) {
|
|
8815
9318
|
console.log(`
|
|
8816
|
-
${
|
|
9319
|
+
${pc11.bold("Test results:")}`);
|
|
8817
9320
|
for (const c of testReport.checks) {
|
|
8818
|
-
const mark = c.ok ?
|
|
8819
|
-
console.log(` ${mark} ${c.name}${c.detail ?
|
|
9321
|
+
const mark = c.ok ? pc11.green("\u2713") : pc11.red("\u2717");
|
|
9322
|
+
console.log(` ${mark} ${c.name}${c.detail ? pc11.dim(` \u2014 ${c.detail}`) : ""}`);
|
|
8820
9323
|
}
|
|
8821
9324
|
}
|
|
8822
9325
|
console.log(`
|
|
@@ -8899,17 +9402,17 @@ async function syncTestCommand(platformModel, options = {}) {
|
|
|
8899
9402
|
return;
|
|
8900
9403
|
}
|
|
8901
9404
|
for (const c of report2.checks) {
|
|
8902
|
-
const mark = c.ok ?
|
|
8903
|
-
console.log(` ${mark} ${c.name}${c.detail ?
|
|
9405
|
+
const mark = c.ok ? pc11.green("\u2713") : pc11.red("\u2717");
|
|
9406
|
+
console.log(` ${mark} ${c.name}${c.detail ? pc11.dim(` \u2014 ${c.detail}`) : ""}`);
|
|
8904
9407
|
}
|
|
8905
9408
|
if (report2.detectedColumns && report2.detectedColumns.length > 0) {
|
|
8906
9409
|
console.log(`
|
|
8907
|
-
${
|
|
9410
|
+
${pc11.bold("Detected columns:")}`);
|
|
8908
9411
|
for (const col of report2.detectedColumns.slice(0, 20)) {
|
|
8909
|
-
console.log(` ${col.name.padEnd(30)} ${
|
|
9412
|
+
console.log(` ${col.name.padEnd(30)} ${pc11.dim(col.type)}`);
|
|
8910
9413
|
}
|
|
8911
9414
|
if (report2.detectedColumns.length > 20) {
|
|
8912
|
-
console.log(
|
|
9415
|
+
console.log(pc11.dim(` ... and ${report2.detectedColumns.length - 20} more`));
|
|
8913
9416
|
}
|
|
8914
9417
|
}
|
|
8915
9418
|
if (report2.identityKeysPreview) {
|
|
@@ -8917,45 +9420,45 @@ async function syncTestCommand(platformModel, options = {}) {
|
|
|
8917
9420
|
const total = perRecord.reduce((a, b) => a + b, 0);
|
|
8918
9421
|
const min = perRecord.length ? Math.min(...perRecord) : 0;
|
|
8919
9422
|
const max = perRecord.length ? Math.max(...perRecord) : 0;
|
|
8920
|
-
const mark = total === 0 ?
|
|
9423
|
+
const mark = total === 0 ? pc11.yellow("~") : pc11.green("\u2713");
|
|
8921
9424
|
console.log(`
|
|
8922
|
-
${
|
|
9425
|
+
${pc11.bold("Merge + identity keys")} ${pc11.dim(`(cross-platform \u2014 #128)`)}`);
|
|
8923
9426
|
console.log(` ${mark} ${perRecord.length} sample${perRecord.length === 1 ? "" : "s"}, ${min}\u2013${max} key${max === 1 ? "" : "s"} per record`);
|
|
8924
9427
|
if (sampleKeys.length > 0) {
|
|
8925
|
-
console.log(` ${
|
|
9428
|
+
console.log(` ${pc11.dim("e.g.")} ${sampleKeys.slice(0, 8).map((k) => pc11.cyan(k)).join(", ")}`);
|
|
8926
9429
|
} else if (resolvesAfterEnrich) {
|
|
8927
|
-
console.log(` ${
|
|
9430
|
+
console.log(` ${pc11.dim("note:")} 0 on these list-shape samples \u2014 this profile enriches, and its identityKeys paths resolve in the enrich phase.`);
|
|
8928
9431
|
} else {
|
|
8929
|
-
console.log(` ${
|
|
9432
|
+
console.log(` ${pc11.yellow("note:")} no identity keys resolved on these samples \u2014 check the identityKey/identityKeys paths.`);
|
|
8930
9433
|
}
|
|
8931
9434
|
if (entityFanOut) {
|
|
8932
|
-
console.log(` ${
|
|
8933
|
-
console.log(` ${
|
|
8934
|
-
console.log(` ${
|
|
8935
|
-
console.log(` ${
|
|
9435
|
+
console.log(` ${pc11.yellow("warn:")} identityKey resolved to MULTIPLE values on ${entityFanOut.count} of ${perRecord.length} samples \u2014 those records get NO merge key.`);
|
|
9436
|
+
console.log(` ${pc11.dim("saw:")} ${entityFanOut.sampleValues.map((v) => pc11.cyan(v)).join(", ")}`);
|
|
9437
|
+
console.log(` ${pc11.dim('A singular identityKey means "this record IS this entity", so a fan-out has no safe answer.')}`);
|
|
9438
|
+
console.log(` ${pc11.dim("Point it at a single-valued path, or move it to identityKeys[] for participant associations.")}`);
|
|
8936
9439
|
}
|
|
8937
9440
|
}
|
|
8938
9441
|
if (searchablePreview) {
|
|
8939
9442
|
console.log(`
|
|
8940
|
-
${
|
|
8941
|
-
console.log(` ${
|
|
8942
|
-
console.log(` ${
|
|
9443
|
+
${pc11.bold("Searchable preview")} ${pc11.dim(`(${searchablePreview.mode}, ${searchablePreview.sampledRecords} sample${searchablePreview.sampledRecords === 1 ? "" : "s"})`)}`);
|
|
9444
|
+
console.log(` ${pc11.dim("length:")} ${searchablePreview.length} chars (first sample)`);
|
|
9445
|
+
console.log(` ${pc11.dim("text:")} ${searchablePreview.text.slice(0, 300)}${searchablePreview.length > 300 ? pc11.dim(" \u2026") : ""}`);
|
|
8943
9446
|
if (searchablePreview.paths) {
|
|
8944
|
-
console.log(` ${
|
|
9447
|
+
console.log(` ${pc11.dim("paths:")} ${pc11.dim('(hit rate across all samples \u2014 differentiates "wrong path" from "field sometimes missing")')}`);
|
|
8945
9448
|
for (const p9 of searchablePreview.paths) {
|
|
8946
9449
|
const rate = `${p9.hits}/${p9.total}`;
|
|
8947
|
-
const mark = p9.hits === p9.total ?
|
|
8948
|
-
const trailer = p9.sample ?
|
|
9450
|
+
const mark = p9.hits === p9.total ? pc11.green("\u2713") : p9.hits === 0 ? pc11.red("\u2717") : pc11.yellow("~");
|
|
9451
|
+
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)") : "";
|
|
8949
9452
|
console.log(` ${mark} ${rate.padStart(5)} ${p9.path}${trailer}`);
|
|
8950
9453
|
}
|
|
8951
9454
|
} else {
|
|
8952
|
-
console.log(` ${
|
|
8953
|
-
console.log(` ${
|
|
9455
|
+
console.log(` ${pc11.yellow("note:")} no memory.searchable declared \u2014 using the default walker (walks every field, often noisy).`);
|
|
9456
|
+
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.`);
|
|
8954
9457
|
}
|
|
8955
9458
|
}
|
|
8956
9459
|
console.log(
|
|
8957
9460
|
`
|
|
8958
|
-
${report2.ok ?
|
|
9461
|
+
${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.")
|
|
8959
9462
|
);
|
|
8960
9463
|
}
|
|
8961
9464
|
function buildSearchablePreview(profile, samples) {
|
|
@@ -8963,7 +9466,7 @@ function buildSearchablePreview(profile, samples) {
|
|
|
8963
9466
|
const first = samples[0];
|
|
8964
9467
|
const paths = getSearchablePaths(profile);
|
|
8965
9468
|
if (paths) {
|
|
8966
|
-
const perPathAgg = paths.map((
|
|
9469
|
+
const perPathAgg = paths.map((path15) => ({ path: path15, hits: 0, total: samples.length, sample: "" }));
|
|
8967
9470
|
for (const record of samples) {
|
|
8968
9471
|
const { paths: perPath } = extractSearchableFromPaths(record, paths);
|
|
8969
9472
|
perPath.forEach((p9, i) => {
|
|
@@ -8996,8 +9499,8 @@ async function syncRunCommand(platform, options) {
|
|
|
8996
9499
|
if (profileDrift.length > 0 && !isAgentMode()) {
|
|
8997
9500
|
for (const d of profileDrift) {
|
|
8998
9501
|
console.log(
|
|
8999
|
-
` ${
|
|
9000
|
-
Run ${
|
|
9502
|
+
` ${pc11.yellow("!")} ${platform}/${d.model} is missing ${d.missing.map((f) => pc11.bold(f)).join(", ")} from the current built-in profile.
|
|
9503
|
+
Run ${pc11.bold(`one sync init ${platform} ${d.model}`)} to pick ${d.missing.length > 1 ? "them" : "it"} up.`
|
|
9001
9504
|
);
|
|
9002
9505
|
}
|
|
9003
9506
|
}
|
|
@@ -9040,21 +9543,21 @@ async function syncRunCommand(platform, options) {
|
|
|
9040
9543
|
return;
|
|
9041
9544
|
}
|
|
9042
9545
|
for (const r of results) {
|
|
9043
|
-
const status2 = r.status === "complete" ?
|
|
9044
|
-
console.log(` ${
|
|
9546
|
+
const status2 = r.status === "complete" ? pc11.green("complete") : r.status === "dry-run" ? pc11.yellow("dry-run") : pc11.red("failed");
|
|
9547
|
+
console.log(` ${pc11.bold(r.model)} \u2014 ${r.recordsSynced} records, ${r.pagesProcessed} pages, ${r.duration} [${status2}]`);
|
|
9045
9548
|
if (r.reconcileSkipped) {
|
|
9046
|
-
console.log(` ${
|
|
9549
|
+
console.log(` ${pc11.yellow("--full-refresh reconcile skipped \u2014 pagination truncated (e.g. --max-pages). Re-run without the cap to prune stale rows.")}`);
|
|
9047
9550
|
}
|
|
9048
9551
|
const sc = r.statusCounts;
|
|
9049
9552
|
if (sc && (sc.archived > 0 || sc.active > 0)) {
|
|
9050
|
-
const archivedColor = sc.archived > sc.active ?
|
|
9051
|
-
console.log(` memory: ${
|
|
9553
|
+
const archivedColor = sc.archived > sc.active ? pc11.red : pc11.dim;
|
|
9554
|
+
console.log(` memory: ${pc11.green(String(sc.active))} active, ${archivedColor(String(sc.archived))} archived`);
|
|
9052
9555
|
}
|
|
9053
9556
|
if (r.error) {
|
|
9054
9557
|
const errParts = [r.error.message];
|
|
9055
9558
|
if (r.error.httpStatus) errParts.push(`HTTP ${r.error.httpStatus}`);
|
|
9056
9559
|
if (r.error.retryAfter) errParts.push(`retry after ${r.error.retryAfter}s`);
|
|
9057
|
-
console.log(` ${
|
|
9560
|
+
console.log(` ${pc11.red(errParts.join(" \u2014 "))}`);
|
|
9058
9561
|
}
|
|
9059
9562
|
}
|
|
9060
9563
|
}
|
|
@@ -9080,8 +9583,8 @@ async function syncQueryCommand(platformModel, options) {
|
|
|
9080
9583
|
json(result);
|
|
9081
9584
|
return;
|
|
9082
9585
|
}
|
|
9083
|
-
console.log(
|
|
9084
|
-
console.log(
|
|
9586
|
+
console.log(pc11.dim(`Query: ${result.query}`));
|
|
9587
|
+
console.log(pc11.dim(`Source: local | Last sync: ${result.lastSync ?? "never"} | Age: ${result.syncAge ?? "n/a"}`));
|
|
9085
9588
|
console.log(JSON.stringify(result.results, null, 2));
|
|
9086
9589
|
console.log(`
|
|
9087
9590
|
${result.total} results`);
|
|
@@ -9103,7 +9606,7 @@ async function syncSearchCommand(query, options) {
|
|
|
9103
9606
|
return;
|
|
9104
9607
|
}
|
|
9105
9608
|
for (const r of result.results) {
|
|
9106
|
-
console.log(` ${
|
|
9609
|
+
console.log(` ${pc11.bold(`${r.platform}/${r.model}`)} ${pc11.dim(`(rank: ${r.rank.toFixed(2)})`)}`);
|
|
9107
9610
|
console.log(` ${JSON.stringify(r.record)}`);
|
|
9108
9611
|
}
|
|
9109
9612
|
console.log(`
|
|
@@ -9254,25 +9757,25 @@ async function syncSuggestSearchableCommand(platformModel, options = {}) {
|
|
|
9254
9757
|
return;
|
|
9255
9758
|
}
|
|
9256
9759
|
console.log(`
|
|
9257
|
-
${
|
|
9760
|
+
${pc11.bold("memory.searchable \u2014 ranked suggestions")} ${pc11.dim(`(${samples.length} samples)`)}`);
|
|
9258
9761
|
if (suggestions.length === 0) {
|
|
9259
|
-
console.log(` ${
|
|
9762
|
+
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.`);
|
|
9260
9763
|
return;
|
|
9261
9764
|
}
|
|
9262
9765
|
for (const s of suggestions) {
|
|
9263
9766
|
const hitPct = Math.round(s.hitRate * 100);
|
|
9264
9767
|
const noisePct = Math.round(s.noiseFraction * 100);
|
|
9265
|
-
const noiseBadge = noisePct > 0 ?
|
|
9768
|
+
const noiseBadge = noisePct > 0 ? pc11.dim(` noise=${noisePct}%`) : "";
|
|
9266
9769
|
console.log(
|
|
9267
|
-
` ${
|
|
9770
|
+
` ${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(`
|
|
9268
9771
|
\u2192 "${s.sampleValue.slice(0, 140)}"`) : "")
|
|
9269
9772
|
);
|
|
9270
9773
|
}
|
|
9271
9774
|
console.log(`
|
|
9272
|
-
${
|
|
9273
|
-
console.log(" " +
|
|
9775
|
+
${pc11.bold("Paste-ready:")}`);
|
|
9776
|
+
console.log(" " + pc11.cyan(`one sync init ${platform} ${model} --config '${JSON.stringify(configPatch)}'`));
|
|
9274
9777
|
console.log(`
|
|
9275
|
-
${
|
|
9778
|
+
${pc11.dim("then preview: ")}${pc11.cyan(`one sync test ${platform}/${model} --show-searchable`)}`);
|
|
9276
9779
|
}
|
|
9277
9780
|
async function syncListCommand(platform) {
|
|
9278
9781
|
const profiles = listProfiles(platform);
|
|
@@ -9306,10 +9809,10 @@ async function syncListCommand(platform) {
|
|
|
9306
9809
|
return;
|
|
9307
9810
|
}
|
|
9308
9811
|
for (const s of syncs) {
|
|
9309
|
-
const status2 = s.status === "idle" ?
|
|
9310
|
-
const legacy = s.legacyDbSize && s.legacyDbSize !== "0 B" ?
|
|
9812
|
+
const status2 = s.status === "idle" ? pc11.green("idle") : s.status === "syncing" ? pc11.yellow(`syncing \u2014 page ${s.pagesProcessed}`) : pc11.red("failed");
|
|
9813
|
+
const legacy = s.legacyDbSize && s.legacyDbSize !== "0 B" ? pc11.yellow(` legacy .db ${s.legacyDbSize}`) : "";
|
|
9311
9814
|
console.log(
|
|
9312
|
-
` ${
|
|
9815
|
+
` ${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
|
|
9313
9816
|
);
|
|
9314
9817
|
}
|
|
9315
9818
|
}
|
|
@@ -9388,8 +9891,8 @@ async function syncScheduleAddCommand(platform, options) {
|
|
|
9388
9891
|
}
|
|
9389
9892
|
const verb = replaced ? "Replaced existing schedule" : "Scheduled sync";
|
|
9390
9893
|
outro(
|
|
9391
|
-
`${verb} ${
|
|
9392
|
-
Logs: ${
|
|
9894
|
+
`${verb} ${pc11.bold(entry.id)} \u2014 every ${pc11.bold(entry.every)} (cron: ${pc11.dim(entry.cronExpr)})
|
|
9895
|
+
Logs: ${pc11.dim(entry.logFile)}`
|
|
9393
9896
|
);
|
|
9394
9897
|
} catch (err) {
|
|
9395
9898
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -9408,14 +9911,14 @@ async function syncScheduleListCommand() {
|
|
|
9408
9911
|
}
|
|
9409
9912
|
for (const e of entries) {
|
|
9410
9913
|
const modelsStr = e.models ? ` [${e.models.join(",")}]` : "";
|
|
9411
|
-
const installed = e.cronInstalled ?
|
|
9914
|
+
const installed = e.cronInstalled ? pc11.green("\u25CF") : pc11.red("\u2717");
|
|
9412
9915
|
console.log(
|
|
9413
|
-
` ${installed} ${
|
|
9916
|
+
` ${installed} ${pc11.bold(e.id.padEnd(32))} every ${pc11.bold(e.every.padEnd(5))} ${pc11.dim(e.cronExpr.padEnd(13))}${modelsStr}`
|
|
9414
9917
|
);
|
|
9415
|
-
console.log(` ${
|
|
9918
|
+
console.log(` ${pc11.dim("cwd:")} ${e.cwd}`);
|
|
9416
9919
|
}
|
|
9417
9920
|
console.log(`
|
|
9418
|
-
${
|
|
9921
|
+
${pc11.dim("\u25CF = cron line installed \u2717 = registry drift, run `sync schedule repair <id>`")}`);
|
|
9419
9922
|
} catch (err) {
|
|
9420
9923
|
error(err instanceof Error ? err.message : String(err));
|
|
9421
9924
|
}
|
|
@@ -9439,7 +9942,7 @@ async function syncScheduleRemoveCommand(idOrPlatform, options) {
|
|
|
9439
9942
|
return;
|
|
9440
9943
|
}
|
|
9441
9944
|
for (const r of result.removed) {
|
|
9442
|
-
console.log(` ${
|
|
9945
|
+
console.log(` ${pc11.green("\u2713")} removed ${pc11.bold(r.id)} ${pc11.dim(`(${r.cwd})`)}`);
|
|
9443
9946
|
}
|
|
9444
9947
|
} catch (err) {
|
|
9445
9948
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -9457,21 +9960,21 @@ async function syncScheduleStatusCommand() {
|
|
|
9457
9960
|
return;
|
|
9458
9961
|
}
|
|
9459
9962
|
for (const s of statuses) {
|
|
9460
|
-
const driftMarker = s.drift === "ok" ?
|
|
9461
|
-
console.log(` ${driftMarker} ${
|
|
9462
|
-
console.log(` ${
|
|
9463
|
-
console.log(` ${
|
|
9464
|
-
console.log(` ${
|
|
9963
|
+
const driftMarker = s.drift === "ok" ? pc11.green("\u25CF") : s.drift === "missing-cron" ? pc11.red("\u2717 missing cron line") : pc11.yellow(`\u26A0 ${s.drift}`);
|
|
9964
|
+
console.log(` ${driftMarker} ${pc11.bold(s.entry.id)} \u2014 every ${s.entry.every} (${pc11.dim(s.entry.cronExpr)})`);
|
|
9965
|
+
console.log(` ${pc11.dim("cwd:")} ${s.entry.cwd}`);
|
|
9966
|
+
console.log(` ${pc11.dim("last run:")} ${s.lastRunAt ?? pc11.yellow("never")}`);
|
|
9967
|
+
console.log(` ${pc11.dim("log:")} ${s.entry.logFile} ${s.logExists ? pc11.dim(`(${s.logSize} bytes)`) : pc11.yellow("(empty)")}`);
|
|
9465
9968
|
if (s.logTail.length > 0) {
|
|
9466
|
-
console.log(
|
|
9969
|
+
console.log(pc11.dim(" last lines:"));
|
|
9467
9970
|
for (const line of s.logTail.slice(-3)) {
|
|
9468
|
-
console.log(
|
|
9971
|
+
console.log(pc11.dim(` ${line}`));
|
|
9469
9972
|
}
|
|
9470
9973
|
}
|
|
9471
9974
|
}
|
|
9472
9975
|
if (statuses.some((s) => s.drift !== "ok")) {
|
|
9473
9976
|
console.log(`
|
|
9474
|
-
${
|
|
9977
|
+
${pc11.yellow("Drift detected.")} Run ${pc11.bold("one sync schedule repair <id>")} to heal.`);
|
|
9475
9978
|
}
|
|
9476
9979
|
} catch (err) {
|
|
9477
9980
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -9484,7 +9987,7 @@ async function syncScheduleRepairCommand(id) {
|
|
|
9484
9987
|
json({ status: "repaired", ...healed });
|
|
9485
9988
|
return;
|
|
9486
9989
|
}
|
|
9487
|
-
outro(`Repaired ${
|
|
9990
|
+
outro(`Repaired ${pc11.bold(healed.id)}: re-installed cron line with current node/cli paths.`);
|
|
9488
9991
|
} catch (err) {
|
|
9489
9992
|
error(err instanceof Error ? err.message : String(err));
|
|
9490
9993
|
}
|
|
@@ -9920,16 +10423,16 @@ function projectEmbeddingApiKey(cfg) {
|
|
|
9920
10423
|
}
|
|
9921
10424
|
function redactSecrets(cfg) {
|
|
9922
10425
|
const copy = JSON.parse(JSON.stringify(cfg));
|
|
9923
|
-
for (const
|
|
9924
|
-
const val = getPath(copy,
|
|
10426
|
+
for (const path15 of SECRET_PATHS) {
|
|
10427
|
+
const val = getPath(copy, path15);
|
|
9925
10428
|
if (typeof val === "string" && val.length > 0) {
|
|
9926
|
-
setPath(copy,
|
|
10429
|
+
setPath(copy, path15, `${val.slice(0, 6)}\u2026(redacted, use --show-secrets)`);
|
|
9927
10430
|
}
|
|
9928
10431
|
}
|
|
9929
10432
|
return copy;
|
|
9930
10433
|
}
|
|
9931
|
-
function getPath(obj,
|
|
9932
|
-
const parts =
|
|
10434
|
+
function getPath(obj, path15) {
|
|
10435
|
+
const parts = path15.split(".");
|
|
9933
10436
|
let cur = obj;
|
|
9934
10437
|
for (const part2 of parts) {
|
|
9935
10438
|
if (cur == null || typeof cur !== "object") return void 0;
|
|
@@ -9937,8 +10440,8 @@ function getPath(obj, path13) {
|
|
|
9937
10440
|
}
|
|
9938
10441
|
return cur;
|
|
9939
10442
|
}
|
|
9940
|
-
function setPath(obj,
|
|
9941
|
-
const parts =
|
|
10443
|
+
function setPath(obj, path15, value) {
|
|
10444
|
+
const parts = path15.split(".");
|
|
9942
10445
|
let cur = obj;
|
|
9943
10446
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
9944
10447
|
const part2 = parts[i];
|
|
@@ -9948,8 +10451,8 @@ function setPath(obj, path13, value) {
|
|
|
9948
10451
|
cur[parts[parts.length - 1]] = value;
|
|
9949
10452
|
return obj;
|
|
9950
10453
|
}
|
|
9951
|
-
function unsetPath(obj,
|
|
9952
|
-
const parts =
|
|
10454
|
+
function unsetPath(obj, path15) {
|
|
10455
|
+
const parts = path15.split(".");
|
|
9953
10456
|
let cur = obj;
|
|
9954
10457
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
9955
10458
|
const part2 = parts[i];
|
|
@@ -9975,7 +10478,7 @@ function parseValue(raw) {
|
|
|
9975
10478
|
}
|
|
9976
10479
|
|
|
9977
10480
|
// src/commands/mem/records.ts
|
|
9978
|
-
import
|
|
10481
|
+
import pc12 from "picocolors";
|
|
9979
10482
|
async function memAddCommand(type, dataRaw, flags) {
|
|
9980
10483
|
requireMemoryInit();
|
|
9981
10484
|
const data = parseJsonArg(dataRaw, "data");
|
|
@@ -10174,7 +10677,7 @@ function summarizeRecord(r) {
|
|
|
10174
10677
|
const v = d[field];
|
|
10175
10678
|
if (typeof v === "string" && v.trim()) return v.trim().slice(0, 80);
|
|
10176
10679
|
}
|
|
10177
|
-
return
|
|
10680
|
+
return pc12.dim("(untitled)");
|
|
10178
10681
|
}
|
|
10179
10682
|
function relativeTime(iso) {
|
|
10180
10683
|
if (!iso) return "";
|
|
@@ -10237,7 +10740,7 @@ async function memFindByKeyCommand(key, secondKey, flags) {
|
|
|
10237
10740
|
});
|
|
10238
10741
|
return;
|
|
10239
10742
|
}
|
|
10240
|
-
const label = matchedKeys.map((k) =>
|
|
10743
|
+
const label = matchedKeys.map((k) => pc12.cyan(k)).join(pc12.dim(" + "));
|
|
10241
10744
|
if (records.length === 0) {
|
|
10242
10745
|
console.log(`
|
|
10243
10746
|
No records linked to ${label}.
|
|
@@ -10245,27 +10748,27 @@ async function memFindByKeyCommand(key, secondKey, flags) {
|
|
|
10245
10748
|
return;
|
|
10246
10749
|
}
|
|
10247
10750
|
console.log();
|
|
10248
|
-
console.log(` ${label} ${
|
|
10751
|
+
console.log(` ${label} ${pc12.dim("\u2014")} ${records.length}${truncated ? "+" : ""} record${records.length === 1 ? "" : "s"} across ${byType.size} type${byType.size === 1 ? "" : "s"}`);
|
|
10249
10752
|
console.log();
|
|
10250
10753
|
const typeWidth = Math.min(30, Math.max(...[...byType.keys()].map((t) => t.length)));
|
|
10251
10754
|
for (const [type, list] of byType) {
|
|
10252
|
-
console.log(` ${
|
|
10755
|
+
console.log(` ${pc12.bold(type.padEnd(typeWidth))} ${pc12.dim(`${list.length} record${list.length === 1 ? "" : "s"}`)}`);
|
|
10253
10756
|
for (const r of list.slice(0, perType)) {
|
|
10254
|
-
console.log(` ${
|
|
10757
|
+
console.log(` ${pc12.dim("\xB7")} ${summarizeRecord(r)} ${pc12.dim(relativeTime(r.updated_at))} ${pc12.dim(r.id)}`);
|
|
10255
10758
|
}
|
|
10256
10759
|
if (list.length > perType) {
|
|
10257
|
-
console.log(` ${
|
|
10760
|
+
console.log(` ${pc12.dim(`\u2026 and ${list.length - perType} more (raise --limit)`)}`);
|
|
10258
10761
|
}
|
|
10259
10762
|
}
|
|
10260
10763
|
if (truncated) {
|
|
10261
10764
|
console.log();
|
|
10262
|
-
console.log(` ${
|
|
10765
|
+
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.`);
|
|
10263
10766
|
}
|
|
10264
10767
|
console.log();
|
|
10265
10768
|
}
|
|
10266
10769
|
|
|
10267
10770
|
// src/commands/mem/doctor.ts
|
|
10268
|
-
import
|
|
10771
|
+
import pc13 from "picocolors";
|
|
10269
10772
|
async function memDoctorCommand() {
|
|
10270
10773
|
const checks = [];
|
|
10271
10774
|
const cfg = getMemoryConfig();
|
|
@@ -10372,23 +10875,23 @@ function emit(checks, capInfo) {
|
|
|
10372
10875
|
return;
|
|
10373
10876
|
}
|
|
10374
10877
|
for (const c of checks) {
|
|
10375
|
-
const mark = c.ok ?
|
|
10376
|
-
const detail = c.detail ?
|
|
10878
|
+
const mark = c.ok ? pc13.green("\u2713") : pc13.red("\u2717");
|
|
10879
|
+
const detail = c.detail ? pc13.dim(` \u2014 ${c.detail}`) : "";
|
|
10377
10880
|
console.log(` ${mark} ${c.name}${detail}`);
|
|
10378
10881
|
}
|
|
10379
10882
|
if (!allOk) {
|
|
10380
|
-
console.log("\n" +
|
|
10883
|
+
console.log("\n" + pc13.yellow("Memory is not fully healthy."));
|
|
10381
10884
|
process.exitCode = 1;
|
|
10382
10885
|
} else {
|
|
10383
|
-
console.log("\n" +
|
|
10886
|
+
console.log("\n" + pc13.green("Memory is healthy."));
|
|
10384
10887
|
}
|
|
10385
10888
|
const line = semanticSearchUpgradeLine({ vectorSearchAvailable: capInfo.vectorSearchAvailable });
|
|
10386
10889
|
if (line) console.log(`
|
|
10387
|
-
${
|
|
10890
|
+
${pc13.dim(line)}`);
|
|
10388
10891
|
}
|
|
10389
10892
|
|
|
10390
10893
|
// src/commands/mem/export.ts
|
|
10391
|
-
import
|
|
10894
|
+
import fs12 from "fs";
|
|
10392
10895
|
import { once } from "events";
|
|
10393
10896
|
async function writeLine(stream, line) {
|
|
10394
10897
|
if (!stream.write(line)) {
|
|
@@ -10400,7 +10903,7 @@ async function memExportCommand(outfile) {
|
|
|
10400
10903
|
const backend = await getBackend();
|
|
10401
10904
|
const stats = await backend.stats();
|
|
10402
10905
|
const toFile = !!outfile && outfile !== "-";
|
|
10403
|
-
const stream = toFile ?
|
|
10906
|
+
const stream = toFile ? fs12.createWriteStream(outfile, "utf-8") : process.stdout;
|
|
10404
10907
|
const all = await listAllTypes(backend);
|
|
10405
10908
|
const pageSize = 500;
|
|
10406
10909
|
let written = 0;
|
|
@@ -10431,9 +10934,9 @@ async function memExportCommand(outfile) {
|
|
|
10431
10934
|
}
|
|
10432
10935
|
async function memImportCommand(file) {
|
|
10433
10936
|
requireMemoryInit();
|
|
10434
|
-
if (!
|
|
10937
|
+
if (!fs12.existsSync(file)) error(`File not found: ${file}`);
|
|
10435
10938
|
const backend = await getBackend();
|
|
10436
|
-
const raw =
|
|
10939
|
+
const raw = fs12.readFileSync(file, "utf-8");
|
|
10437
10940
|
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
10438
10941
|
let inserted = 0;
|
|
10439
10942
|
let updated = 0;
|
|
@@ -10679,7 +11182,7 @@ function registerMemoryCommands(program2) {
|
|
|
10679
11182
|
}
|
|
10680
11183
|
|
|
10681
11184
|
// src/commands/cache.ts
|
|
10682
|
-
import
|
|
11185
|
+
import pc14 from "picocolors";
|
|
10683
11186
|
async function cacheClearCommand(actionId) {
|
|
10684
11187
|
if (actionId) {
|
|
10685
11188
|
const deleted = clearEntry(actionId);
|
|
@@ -10688,9 +11191,9 @@ async function cacheClearCommand(actionId) {
|
|
|
10688
11191
|
return;
|
|
10689
11192
|
}
|
|
10690
11193
|
if (deleted) {
|
|
10691
|
-
console.log(`Cleared cache for ${
|
|
11194
|
+
console.log(`Cleared cache for ${pc14.cyan(actionId)}`);
|
|
10692
11195
|
} else {
|
|
10693
|
-
console.log(`No cache entry found for ${
|
|
11196
|
+
console.log(`No cache entry found for ${pc14.dim(actionId)}`);
|
|
10694
11197
|
}
|
|
10695
11198
|
} else {
|
|
10696
11199
|
const count = clearAll();
|
|
@@ -10727,7 +11230,7 @@ async function cacheListCommand(options) {
|
|
|
10727
11230
|
type: e.type,
|
|
10728
11231
|
key: e.entry.key,
|
|
10729
11232
|
age: formatAge(getAge(e.entry)),
|
|
10730
|
-
status: isFresh(e.entry) ?
|
|
11233
|
+
status: isFresh(e.entry) ? pc14.green("fresh") : pc14.yellow("expired")
|
|
10731
11234
|
}));
|
|
10732
11235
|
printTable(
|
|
10733
11236
|
[
|
|
@@ -10780,13 +11283,13 @@ async function cacheUpdateAllCommand() {
|
|
|
10780
11283
|
if (errors.length > 0) {
|
|
10781
11284
|
console.log();
|
|
10782
11285
|
for (const e of errors) {
|
|
10783
|
-
console.log(` ${
|
|
11286
|
+
console.log(` ${pc14.red("\u2717")} ${e.key}: ${pc14.dim(e.error)}`);
|
|
10784
11287
|
}
|
|
10785
11288
|
}
|
|
10786
11289
|
}
|
|
10787
11290
|
|
|
10788
11291
|
// src/commands/guide.ts
|
|
10789
|
-
import
|
|
11292
|
+
import pc15 from "picocolors";
|
|
10790
11293
|
|
|
10791
11294
|
// src/lib/guide-content.ts
|
|
10792
11295
|
var GUIDE_OVERVIEW = `# One CLI \u2014 Agent Guide
|
|
@@ -10901,6 +11404,9 @@ one --agent relay deliveries --endpoint-id <id> # Check delivery
|
|
|
10901
11404
|
- \`--create-webhook\` auto-registers the webhook URL with the source platform
|
|
10902
11405
|
- Use \`actions find\` (and \`actions load\` for more) to learn both the incoming payload shape AND the destination API shape before building templates
|
|
10903
11406
|
|
|
11407
|
+
### One Connect apps \u2014 created from \`one.connect.ts\`
|
|
11408
|
+
\`one connect create\` reads \`one.connect.ts\`, typed by \`defineConnectApp\` from \`@withone/connect/config\` (install \`@withone/connect\` 0.20.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_REDIRECT_URI\` and, for a list of tools, \`ONE_PERMISSION_SET\` to the env file. \`one connect types\` writes \`one-connect-env.d.ts\` so tool slugs and action names autocomplete. \`ask\` is required: the tools, each with \`access\` \`read-only\`, \`read-write\`, \`full-access\`, or \`custom\` with \`actions\` (names, or ids from \`actions find\`), or \`"everything"\`, which asks for every connection the user has and removes \`ONE_PERMISSION_SET\` from the env file.
|
|
11409
|
+
|
|
10904
11410
|
### 4. Memory + Sync \u2014 Unified store with hybrid FTS + semantic search
|
|
10905
11411
|
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.
|
|
10906
11412
|
|
|
@@ -11992,14 +12498,14 @@ async function guideCommand(topic = "all") {
|
|
|
11992
12498
|
json({ topic, title, content, availableTopics });
|
|
11993
12499
|
return;
|
|
11994
12500
|
}
|
|
11995
|
-
intro(
|
|
12501
|
+
intro(pc15.bgCyan(pc15.black(" One Guide ")));
|
|
11996
12502
|
console.log();
|
|
11997
12503
|
console.log(content);
|
|
11998
|
-
console.log(
|
|
12504
|
+
console.log(pc15.dim("\u2500".repeat(60)));
|
|
11999
12505
|
console.log(
|
|
12000
|
-
|
|
12506
|
+
pc15.dim("Available topics: ") + availableTopics.map((t) => pc15.cyan(t.topic)).join(", ")
|
|
12001
12507
|
);
|
|
12002
|
-
console.log(
|
|
12508
|
+
console.log(pc15.dim(`Run ${pc15.cyan("one guide <topic>")} for a specific section.`));
|
|
12003
12509
|
}
|
|
12004
12510
|
|
|
12005
12511
|
// src/lib/platform-meta.ts
|
|
@@ -12319,24 +12825,24 @@ function buildWorkflowIdeas(connections) {
|
|
|
12319
12825
|
}
|
|
12320
12826
|
|
|
12321
12827
|
// src/commands/logout.ts
|
|
12322
|
-
import
|
|
12828
|
+
import fs13 from "fs";
|
|
12323
12829
|
import * as p8 from "@clack/prompts";
|
|
12324
|
-
function formatWhoami(config2, apiKey,
|
|
12830
|
+
function formatWhoami(config2, apiKey, pc17) {
|
|
12325
12831
|
const whoami = config2.whoami;
|
|
12326
12832
|
const env = getEnvFromApiKey(apiKey);
|
|
12327
|
-
const envLabel = env === "test" ?
|
|
12833
|
+
const envLabel = env === "test" ? pc17.yellow("test") : pc17.green("live");
|
|
12328
12834
|
const lines = [];
|
|
12329
12835
|
if (whoami) {
|
|
12330
12836
|
const contextParts = [];
|
|
12331
12837
|
if (whoami.organization) contextParts.push(whoami.organization.name);
|
|
12332
12838
|
if (whoami.project) contextParts.push(whoami.project.name);
|
|
12333
12839
|
const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
|
|
12334
|
-
lines.push(`${
|
|
12335
|
-
lines.push(`${whoami.user.name} ${
|
|
12336
|
-
if (config2.apiKeyName) lines.push(`${
|
|
12840
|
+
lines.push(`${pc17.bold(scopeDisplay)} ${pc17.dim("\xB7")} ${envLabel}`);
|
|
12841
|
+
lines.push(`${whoami.user.name} ${pc17.dim(`(${whoami.user.email})`)}`);
|
|
12842
|
+
if (config2.apiKeyName) lines.push(`${pc17.dim("Key:")} ${config2.apiKeyName}`);
|
|
12337
12843
|
} else {
|
|
12338
12844
|
const keyLabel = config2.apiKeyName ?? `${apiKey.slice(0, 8)}...`;
|
|
12339
|
-
lines.push(`${
|
|
12845
|
+
lines.push(`${pc17.dim("Key:")} ${keyLabel} ${pc17.dim("\xB7")} ${envLabel}`);
|
|
12340
12846
|
}
|
|
12341
12847
|
return lines;
|
|
12342
12848
|
}
|
|
@@ -12354,18 +12860,18 @@ async function logoutCommand() {
|
|
|
12354
12860
|
const globalPath = getGlobalConfigPath();
|
|
12355
12861
|
const projectPath = getProjectConfigPath();
|
|
12356
12862
|
let cleared = false;
|
|
12357
|
-
if (
|
|
12358
|
-
|
|
12863
|
+
if (fs13.existsSync(projectPath)) {
|
|
12864
|
+
fs13.unlinkSync(projectPath);
|
|
12359
12865
|
cleared = true;
|
|
12360
12866
|
}
|
|
12361
|
-
if (
|
|
12362
|
-
|
|
12867
|
+
if (fs13.existsSync(globalPath)) {
|
|
12868
|
+
fs13.unlinkSync(globalPath);
|
|
12363
12869
|
cleared = true;
|
|
12364
12870
|
}
|
|
12365
12871
|
json({ status: cleared ? "logged_out" : "not_logged_in", message: cleared ? "Credentials cleared." : "No config found." });
|
|
12366
12872
|
return;
|
|
12367
12873
|
}
|
|
12368
|
-
const
|
|
12874
|
+
const pc17 = (await import("picocolors")).default;
|
|
12369
12875
|
const globalConfig = readGlobalConfig();
|
|
12370
12876
|
const projectConfig = readProjectConfig();
|
|
12371
12877
|
const hasGlobal = globalConfig?.apiKey != null;
|
|
@@ -12374,13 +12880,13 @@ async function logoutCommand() {
|
|
|
12374
12880
|
if (hasGlobal && hasProject) {
|
|
12375
12881
|
const infoLines = ["You are logged in with multiple configs.", ""];
|
|
12376
12882
|
if (projectConfig) {
|
|
12377
|
-
infoLines.push(`${
|
|
12378
|
-
infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey,
|
|
12883
|
+
infoLines.push(`${pc17.cyan("Local config:")}`);
|
|
12884
|
+
infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey, pc17));
|
|
12379
12885
|
infoLines.push("");
|
|
12380
12886
|
}
|
|
12381
12887
|
if (globalConfig) {
|
|
12382
|
-
infoLines.push(`${
|
|
12383
|
-
infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey,
|
|
12888
|
+
infoLines.push(`${pc17.magenta("Global config:")}`);
|
|
12889
|
+
infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey, pc17));
|
|
12384
12890
|
}
|
|
12385
12891
|
p8.note(infoLines.join("\n"));
|
|
12386
12892
|
const choice = await p8.select({
|
|
@@ -12398,14 +12904,14 @@ async function logoutCommand() {
|
|
|
12398
12904
|
targetScope = choice;
|
|
12399
12905
|
} else if (hasProject) {
|
|
12400
12906
|
const infoLines = ["You are logged in.", ""];
|
|
12401
|
-
infoLines.push(`${
|
|
12402
|
-
infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey,
|
|
12907
|
+
infoLines.push(`${pc17.dim("Stored in")} ${pc17.cyan("local config")}`);
|
|
12908
|
+
infoLines.push(...formatWhoami(projectConfig, projectConfig.apiKey, pc17));
|
|
12403
12909
|
p8.note(infoLines.join("\n"));
|
|
12404
12910
|
targetScope = "project";
|
|
12405
12911
|
} else if (hasGlobal) {
|
|
12406
12912
|
const infoLines = ["You are logged in.", ""];
|
|
12407
|
-
infoLines.push(`${
|
|
12408
|
-
infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey,
|
|
12913
|
+
infoLines.push(`${pc17.dim("Stored in")} ${pc17.magenta("global config")}`);
|
|
12914
|
+
infoLines.push(...formatWhoami(globalConfig, globalConfig.apiKey, pc17));
|
|
12409
12915
|
p8.note(infoLines.join("\n"));
|
|
12410
12916
|
targetScope = "global";
|
|
12411
12917
|
} else {
|
|
@@ -12428,11 +12934,11 @@ async function logoutCommand() {
|
|
|
12428
12934
|
}
|
|
12429
12935
|
if (targetScope === "project" || targetScope === "both") {
|
|
12430
12936
|
const projectPath = getProjectConfigPath();
|
|
12431
|
-
if (
|
|
12937
|
+
if (fs13.existsSync(projectPath)) fs13.unlinkSync(projectPath);
|
|
12432
12938
|
}
|
|
12433
12939
|
if (targetScope === "global" || targetScope === "both") {
|
|
12434
12940
|
const globalPath = getGlobalConfigPath();
|
|
12435
|
-
if (
|
|
12941
|
+
if (fs13.existsSync(globalPath)) fs13.unlinkSync(globalPath);
|
|
12436
12942
|
}
|
|
12437
12943
|
p8.log.success("Credentials cleared.");
|
|
12438
12944
|
p8.log.info("Your API key is still active. Manage keys at app.withone.ai/settings");
|
|
@@ -12440,11 +12946,11 @@ async function logoutCommand() {
|
|
|
12440
12946
|
}
|
|
12441
12947
|
|
|
12442
12948
|
// src/lib/project-skills.ts
|
|
12443
|
-
import
|
|
12444
|
-
import
|
|
12949
|
+
import crypto3 from "crypto";
|
|
12950
|
+
import fs14 from "fs";
|
|
12445
12951
|
import os2 from "os";
|
|
12446
|
-
import
|
|
12447
|
-
import
|
|
12952
|
+
import path12 from "path";
|
|
12953
|
+
import zlib2 from "zlib";
|
|
12448
12954
|
var OPTIONAL_SKILLS = [
|
|
12449
12955
|
{
|
|
12450
12956
|
name: "connect",
|
|
@@ -12475,42 +12981,42 @@ function findAgentTarget(id) {
|
|
|
12475
12981
|
return SKILL_AGENT_TARGETS.find((a) => a.id === id.trim().toLowerCase());
|
|
12476
12982
|
}
|
|
12477
12983
|
function findProjectRoot(start) {
|
|
12478
|
-
let dir =
|
|
12984
|
+
let dir = path12.resolve(start);
|
|
12479
12985
|
for (; ; ) {
|
|
12480
|
-
if (
|
|
12481
|
-
const parent =
|
|
12986
|
+
if (fs14.existsSync(path12.join(dir, "package.json")) || fs14.existsSync(path12.join(dir, ".git"))) return dir;
|
|
12987
|
+
const parent = path12.dirname(dir);
|
|
12482
12988
|
if (parent === dir) return null;
|
|
12483
12989
|
dir = parent;
|
|
12484
12990
|
}
|
|
12485
12991
|
}
|
|
12486
12992
|
function findInstalledPackage(start, packageName) {
|
|
12487
|
-
let dir =
|
|
12993
|
+
let dir = path12.resolve(start);
|
|
12488
12994
|
for (; ; ) {
|
|
12489
|
-
const pkgDir =
|
|
12490
|
-
const manifest =
|
|
12491
|
-
if (
|
|
12995
|
+
const pkgDir = path12.join(dir, "node_modules", ...packageName.split("/"));
|
|
12996
|
+
const manifest = path12.join(pkgDir, "package.json");
|
|
12997
|
+
if (fs14.existsSync(manifest)) {
|
|
12492
12998
|
try {
|
|
12493
|
-
const { version: version2 } = JSON.parse(
|
|
12999
|
+
const { version: version2 } = JSON.parse(fs14.readFileSync(manifest, "utf-8"));
|
|
12494
13000
|
if (version2) return { dir: pkgDir, version: version2 };
|
|
12495
13001
|
} catch {
|
|
12496
13002
|
}
|
|
12497
13003
|
}
|
|
12498
|
-
const parent =
|
|
13004
|
+
const parent = path12.dirname(dir);
|
|
12499
13005
|
if (parent === dir) return null;
|
|
12500
13006
|
dir = parent;
|
|
12501
13007
|
}
|
|
12502
13008
|
}
|
|
12503
13009
|
var SkillSourceError = class extends Error {
|
|
12504
|
-
constructor(
|
|
12505
|
-
super(
|
|
13010
|
+
constructor(message2, code) {
|
|
13011
|
+
super(message2);
|
|
12506
13012
|
this.code = code;
|
|
12507
13013
|
}
|
|
12508
13014
|
};
|
|
12509
13015
|
function projectSkillSource(skill, projectDir) {
|
|
12510
13016
|
const installed = findInstalledPackage(projectDir, skill.packageName);
|
|
12511
13017
|
if (!installed) return null;
|
|
12512
|
-
const dir =
|
|
12513
|
-
if (!
|
|
13018
|
+
const dir = path12.join(installed.dir, ...skill.packagePath.split("/"));
|
|
13019
|
+
if (!fs14.existsSync(path12.join(dir, "SKILL.md"))) {
|
|
12514
13020
|
throw new SkillSourceError(
|
|
12515
13021
|
`${skill.packageName} ${installed.version} does not include the ${skill.title} skill. Upgrade it: npm install ${skill.packageName}@latest`,
|
|
12516
13022
|
"skill-missing"
|
|
@@ -12549,15 +13055,15 @@ async function registrySkillSource(skill, fetchImpl = fetch) {
|
|
|
12549
13055
|
if (!matchesIntegrity(bytes, integrity)) {
|
|
12550
13056
|
throw new SkillSourceError(`${skill.packageName}@${version2} failed its integrity check; nothing was installed.`, "integrity");
|
|
12551
13057
|
}
|
|
12552
|
-
const files = readTarEntries(
|
|
13058
|
+
const files = readTarEntries(zlib2.gunzipSync(bytes), `package/${skill.packagePath}/`);
|
|
12553
13059
|
if (!files.some((f) => f.path === "SKILL.md")) {
|
|
12554
13060
|
throw new SkillSourceError(`${skill.packageName}@${version2} does not include the ${skill.title} skill.`, "not-in-package");
|
|
12555
13061
|
}
|
|
12556
|
-
const tmp =
|
|
13062
|
+
const tmp = fs14.mkdtempSync(path12.join(os2.tmpdir(), "one-skill-"));
|
|
12557
13063
|
for (const file of files) {
|
|
12558
|
-
const dest =
|
|
12559
|
-
|
|
12560
|
-
|
|
13064
|
+
const dest = path12.join(tmp, ...file.path.split("/"));
|
|
13065
|
+
fs14.mkdirSync(path12.dirname(dest), { recursive: true });
|
|
13066
|
+
fs14.writeFileSync(dest, file.data);
|
|
12561
13067
|
}
|
|
12562
13068
|
return {
|
|
12563
13069
|
kind: "registry",
|
|
@@ -12566,7 +13072,7 @@ async function registrySkillSource(skill, fetchImpl = fetch) {
|
|
|
12566
13072
|
origin: tarball,
|
|
12567
13073
|
cleanup: () => {
|
|
12568
13074
|
try {
|
|
12569
|
-
|
|
13075
|
+
fs14.rmSync(tmp, { recursive: true, force: true });
|
|
12570
13076
|
} catch {
|
|
12571
13077
|
}
|
|
12572
13078
|
}
|
|
@@ -12578,7 +13084,7 @@ function matchesIntegrity(bytes, integrity) {
|
|
|
12578
13084
|
if (dash < 0) return false;
|
|
12579
13085
|
const algorithm = entry.slice(0, dash);
|
|
12580
13086
|
if (!["sha512", "sha384", "sha256"].includes(algorithm)) return false;
|
|
12581
|
-
return
|
|
13087
|
+
return crypto3.createHash(algorithm).update(bytes).digest("base64") === entry.slice(dash + 1);
|
|
12582
13088
|
});
|
|
12583
13089
|
}
|
|
12584
13090
|
function readTarEntries(tar, prefix) {
|
|
@@ -12610,7 +13116,7 @@ function readTarEntries(tar, prefix) {
|
|
|
12610
13116
|
if ((type === "0" || type === "\0") && full.startsWith(prefix)) {
|
|
12611
13117
|
const rel = full.slice(prefix.length);
|
|
12612
13118
|
const parts = rel.split("/");
|
|
12613
|
-
if (rel && !
|
|
13119
|
+
if (rel && !path12.isAbsolute(rel) && !parts.includes("..") && !parts.includes("")) {
|
|
12614
13120
|
out.push({ path: rel, data: Buffer.from(body) });
|
|
12615
13121
|
}
|
|
12616
13122
|
}
|
|
@@ -12621,11 +13127,11 @@ function readTarEntries(tar, prefix) {
|
|
|
12621
13127
|
}
|
|
12622
13128
|
var SKILL_STAMP = ".one-skill.json";
|
|
12623
13129
|
function canonicalSkillDir(skill, plan) {
|
|
12624
|
-
return
|
|
13130
|
+
return path12.join(plan.root, ...UNIVERSAL_PROJECT_DIR.split("/"), skill.dirName);
|
|
12625
13131
|
}
|
|
12626
13132
|
function readStamp(dir) {
|
|
12627
13133
|
try {
|
|
12628
|
-
return JSON.parse(
|
|
13134
|
+
return JSON.parse(fs14.readFileSync(path12.join(dir, SKILL_STAMP), "utf-8"));
|
|
12629
13135
|
} catch {
|
|
12630
13136
|
return null;
|
|
12631
13137
|
}
|
|
@@ -12635,9 +13141,9 @@ function resolveAgentFolders(plan) {
|
|
|
12635
13141
|
const chosen = SKILL_AGENT_TARGETS.filter((agent) => {
|
|
12636
13142
|
if (explicit) return plan.agents.includes(agent.id);
|
|
12637
13143
|
if (plan.scope === "global") {
|
|
12638
|
-
return
|
|
13144
|
+
return fs14.existsSync(path12.join(plan.root, path12.dirname(agent.homeDir)));
|
|
12639
13145
|
}
|
|
12640
|
-
return agent.projectMarker === null ||
|
|
13146
|
+
return agent.projectMarker === null || fs14.existsSync(path12.join(plan.root, agent.projectMarker));
|
|
12641
13147
|
});
|
|
12642
13148
|
const readers = [];
|
|
12643
13149
|
const byDir = /* @__PURE__ */ new Map();
|
|
@@ -12652,33 +13158,33 @@ function resolveAgentFolders(plan) {
|
|
|
12652
13158
|
return { readers, folders: [...byDir].map(([dir, agents]) => ({ dir, agents })) };
|
|
12653
13159
|
}
|
|
12654
13160
|
function copyDir(src, dest) {
|
|
12655
|
-
|
|
12656
|
-
for (const entry of
|
|
13161
|
+
fs14.mkdirSync(dest, { recursive: true });
|
|
13162
|
+
for (const entry of fs14.readdirSync(src, { withFileTypes: true })) {
|
|
12657
13163
|
if (entry.name === SKILL_STAMP) continue;
|
|
12658
|
-
const from =
|
|
12659
|
-
const to =
|
|
13164
|
+
const from = path12.join(src, entry.name);
|
|
13165
|
+
const to = path12.join(dest, entry.name);
|
|
12660
13166
|
if (entry.isDirectory()) copyDir(from, to);
|
|
12661
|
-
else
|
|
13167
|
+
else fs14.copyFileSync(from, to);
|
|
12662
13168
|
}
|
|
12663
13169
|
}
|
|
12664
13170
|
function removePath(target) {
|
|
12665
13171
|
try {
|
|
12666
|
-
const stat =
|
|
12667
|
-
if (stat.isSymbolicLink())
|
|
12668
|
-
else
|
|
13172
|
+
const stat = fs14.lstatSync(target);
|
|
13173
|
+
if (stat.isSymbolicLink()) fs14.unlinkSync(target);
|
|
13174
|
+
else fs14.rmSync(target, { recursive: true, force: true });
|
|
12669
13175
|
} catch {
|
|
12670
13176
|
}
|
|
12671
13177
|
}
|
|
12672
13178
|
function linkDir(target, linkPath) {
|
|
12673
13179
|
removePath(linkPath);
|
|
12674
|
-
|
|
13180
|
+
fs14.mkdirSync(path12.dirname(linkPath), { recursive: true });
|
|
12675
13181
|
try {
|
|
12676
|
-
|
|
13182
|
+
fs14.symlinkSync(path12.relative(path12.dirname(linkPath), target), linkPath, "dir");
|
|
12677
13183
|
return "symlink";
|
|
12678
13184
|
} catch {
|
|
12679
13185
|
}
|
|
12680
13186
|
try {
|
|
12681
|
-
|
|
13187
|
+
fs14.symlinkSync(target, linkPath, "junction");
|
|
12682
13188
|
return "junction";
|
|
12683
13189
|
} catch {
|
|
12684
13190
|
}
|
|
@@ -12709,17 +13215,17 @@ function installSkill(skill, source, plan, cliVersion2) {
|
|
|
12709
13215
|
installedBy: `@withone/cli@${cliVersion2}`,
|
|
12710
13216
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12711
13217
|
};
|
|
12712
|
-
|
|
13218
|
+
fs14.writeFileSync(path12.join(canonical, SKILL_STAMP), JSON.stringify(stamp, null, 2) + "\n");
|
|
12713
13219
|
return { skill, version: source.version, source: source.kind, origin: source.origin, scope: plan.scope, root: plan.root, canonical, readers, links };
|
|
12714
13220
|
}
|
|
12715
13221
|
function removeSkill(skill, plan) {
|
|
12716
13222
|
const canonical = canonicalSkillDir(skill, plan);
|
|
12717
13223
|
const stamp = readStamp(canonical);
|
|
12718
|
-
if (!stamp && !
|
|
13224
|
+
if (!stamp && !fs14.existsSync(path12.join(canonical, "SKILL.md"))) return { removed: false, paths: [] };
|
|
12719
13225
|
const paths = [];
|
|
12720
13226
|
for (const link of stamp?.links ?? []) {
|
|
12721
13227
|
const full = fromStamp(plan.root, link.path);
|
|
12722
|
-
if (
|
|
13228
|
+
if (fs14.existsSync(full) || isDanglingLink(full)) {
|
|
12723
13229
|
removePath(full);
|
|
12724
13230
|
paths.push(full);
|
|
12725
13231
|
}
|
|
@@ -12732,18 +13238,18 @@ function toPosix(p9) {
|
|
|
12732
13238
|
return p9.split("\\").join("/");
|
|
12733
13239
|
}
|
|
12734
13240
|
function fromStamp(root, rel) {
|
|
12735
|
-
return
|
|
13241
|
+
return path12.join(root, ...toPosix(rel).split("/"));
|
|
12736
13242
|
}
|
|
12737
13243
|
function isDanglingLink(p9) {
|
|
12738
13244
|
try {
|
|
12739
|
-
return
|
|
13245
|
+
return fs14.lstatSync(p9).isSymbolicLink();
|
|
12740
13246
|
} catch {
|
|
12741
13247
|
return false;
|
|
12742
13248
|
}
|
|
12743
13249
|
}
|
|
12744
13250
|
function installedSkill(skill, plan) {
|
|
12745
13251
|
const canonical = canonicalSkillDir(skill, plan);
|
|
12746
|
-
if (!
|
|
13252
|
+
if (!fs14.existsSync(path12.join(canonical, "SKILL.md"))) return null;
|
|
12747
13253
|
const stamp = readStamp(canonical);
|
|
12748
13254
|
return { scope: plan.scope, root: plan.root, canonical, version: stamp?.version ?? null, source: stamp?.source ?? null, links: stamp?.links ?? [] };
|
|
12749
13255
|
}
|
|
@@ -12752,7 +13258,7 @@ function globalRoot() {
|
|
|
12752
13258
|
}
|
|
12753
13259
|
function refreshProjectSkills(cwd, cliVersion2) {
|
|
12754
13260
|
const root = findProjectRoot(cwd);
|
|
12755
|
-
if (!root ||
|
|
13261
|
+
if (!root || path12.resolve(root) === path12.resolve(homeDir())) return [];
|
|
12756
13262
|
const results = [];
|
|
12757
13263
|
for (const skill of OPTIONAL_SKILLS) {
|
|
12758
13264
|
const canonical = canonicalSkillDir(skill, { scope: "project", root });
|
|
@@ -12775,8 +13281,8 @@ function refreshProjectSkills(cwd, cliVersion2) {
|
|
|
12775
13281
|
}
|
|
12776
13282
|
|
|
12777
13283
|
// src/commands/skills.ts
|
|
12778
|
-
import
|
|
12779
|
-
import
|
|
13284
|
+
import path13 from "path";
|
|
13285
|
+
import pc16 from "picocolors";
|
|
12780
13286
|
function requireSkill(name) {
|
|
12781
13287
|
const skill = findOptionalSkill(name);
|
|
12782
13288
|
if (!skill) {
|
|
@@ -12799,7 +13305,7 @@ function resolvePlan(options) {
|
|
|
12799
13305
|
const agents = parseAgents(options.agents);
|
|
12800
13306
|
if (options.global) return { scope: "global", root: globalRoot(), agents };
|
|
12801
13307
|
const root = findProjectRoot(process.cwd());
|
|
12802
|
-
if (!root ||
|
|
13308
|
+
if (!root || path13.resolve(root) === path13.resolve(homeDir())) {
|
|
12803
13309
|
error("No project here: run this inside your app (a folder with package.json or .git), or pass --global.");
|
|
12804
13310
|
}
|
|
12805
13311
|
return { scope: "project", root, agents };
|
|
@@ -12815,7 +13321,7 @@ async function resolveSource(skill, plan) {
|
|
|
12815
13321
|
}
|
|
12816
13322
|
}
|
|
12817
13323
|
function display(p9, root) {
|
|
12818
|
-
const rel =
|
|
13324
|
+
const rel = path13.relative(root, p9);
|
|
12819
13325
|
return rel && !rel.startsWith("..") ? rel : p9;
|
|
12820
13326
|
}
|
|
12821
13327
|
function report(command, result, packageInProject) {
|
|
@@ -12831,7 +13337,7 @@ function report(command, result, packageInProject) {
|
|
|
12831
13337
|
root: result.root,
|
|
12832
13338
|
installedTo: [
|
|
12833
13339
|
{ path: result.canonical, kind: "folder", agents: result.readers },
|
|
12834
|
-
...result.links.map((l) => ({ path:
|
|
13340
|
+
...result.links.map((l) => ({ path: path13.join(result.root, ...l.path.split("/")), kind: l.kind, agents: l.agents }))
|
|
12835
13341
|
],
|
|
12836
13342
|
next: nextSteps(result, packageInProject)
|
|
12837
13343
|
});
|
|
@@ -12839,20 +13345,20 @@ function report(command, result, packageInProject) {
|
|
|
12839
13345
|
}
|
|
12840
13346
|
const scopeLabel2 = result.scope === "project" ? "this project" : "every project on this machine";
|
|
12841
13347
|
console.log();
|
|
12842
|
-
console.log(` ${
|
|
13348
|
+
console.log(` ${pc16.green("\u2713")} ${pc16.bold(`${result.skill.title} skill ${result.version}`)} installed for ${scopeLabel2}`);
|
|
12843
13349
|
console.log();
|
|
12844
|
-
const from = result.source === "project" ? `${where(result.origin)} ${
|
|
12845
|
-
console.log(` ${
|
|
13350
|
+
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})`)}`;
|
|
13351
|
+
console.log(` ${pc16.dim("From")} ${from}`);
|
|
12846
13352
|
const rows = [
|
|
12847
13353
|
{ path: where(result.canonical), agents: result.readers },
|
|
12848
|
-
...result.links.map((l) => ({ path: `${l.path}${l.kind === "copy" ?
|
|
13354
|
+
...result.links.map((l) => ({ path: `${l.path}${l.kind === "copy" ? pc16.dim(" (copy)") : ""}`, agents: l.agents }))
|
|
12849
13355
|
];
|
|
12850
13356
|
rows.forEach((row, i) => {
|
|
12851
|
-
const agents = row.agents.length ?
|
|
12852
|
-
console.log(` ${
|
|
13357
|
+
const agents = row.agents.length ? pc16.dim(row.agents.join(", ")) : pc16.dim("shared copy the links point to");
|
|
13358
|
+
console.log(` ${pc16.dim(i === 0 ? "To " : " ")} ${row.path} ${agents}`);
|
|
12853
13359
|
});
|
|
12854
13360
|
console.log();
|
|
12855
|
-
console.log(` ${
|
|
13361
|
+
console.log(` ${pc16.bold("Next")}`);
|
|
12856
13362
|
nextSteps(result, packageInProject).forEach((step, i) => console.log(` ${i + 1}. ${step}`));
|
|
12857
13363
|
console.log();
|
|
12858
13364
|
}
|
|
@@ -12915,7 +13421,7 @@ async function skillsUpdateCommand(name, options) {
|
|
|
12915
13421
|
console.log("No optional skills are installed here. See: one skills list");
|
|
12916
13422
|
return;
|
|
12917
13423
|
}
|
|
12918
|
-
for (const r of updated) console.log(` ${
|
|
13424
|
+
for (const r of updated) console.log(` ${pc16.green("\u2713")} ${r.skill.title} skill ${r.version} ${pc16.dim(display(r.canonical, r.root))}`);
|
|
12919
13425
|
}
|
|
12920
13426
|
async function skillsRemoveCommand(name, options) {
|
|
12921
13427
|
const skill = requireSkill(name);
|
|
@@ -12929,12 +13435,12 @@ async function skillsRemoveCommand(name, options) {
|
|
|
12929
13435
|
console.log(`${skill.title} skill is not installed ${plan.scope === "global" ? "globally" : "in this project"}.`);
|
|
12930
13436
|
return;
|
|
12931
13437
|
}
|
|
12932
|
-
console.log(` ${
|
|
12933
|
-
for (const p9 of result.paths) console.log(` ${
|
|
13438
|
+
console.log(` ${pc16.green("\u2713")} Removed the ${skill.title} skill`);
|
|
13439
|
+
for (const p9 of result.paths) console.log(` ${pc16.dim(display(p9, plan.root))}`);
|
|
12934
13440
|
}
|
|
12935
13441
|
async function skillsListCommand() {
|
|
12936
13442
|
const projectRoot = findProjectRoot(process.cwd());
|
|
12937
|
-
const inProject = projectRoot &&
|
|
13443
|
+
const inProject = projectRoot && path13.resolve(projectRoot) !== path13.resolve(homeDir()) ? projectRoot : null;
|
|
12938
13444
|
const optional = OPTIONAL_SKILLS.map((skill) => {
|
|
12939
13445
|
const project = inProject ? installedSkill(skill, { scope: "project", root: inProject }) : null;
|
|
12940
13446
|
const global = installedSkill(skill, { scope: "global", root: globalRoot() });
|
|
@@ -12962,11 +13468,11 @@ async function skillsListCommand() {
|
|
|
12962
13468
|
return;
|
|
12963
13469
|
}
|
|
12964
13470
|
console.log();
|
|
12965
|
-
console.log(` ${
|
|
13471
|
+
console.log(` ${pc16.bold("one")} ${oneSkill.installed ? pc16.green(`installed ${oneSkill.version ?? ""}`.trim()) : pc16.yellow("not installed")} ${pc16.dim("managed by one init")}`);
|
|
12966
13472
|
for (const s of optional) {
|
|
12967
|
-
const state = s.project ?
|
|
12968
|
-
console.log(` ${
|
|
12969
|
-
console.log(` ${" ".repeat(8)} ${
|
|
13473
|
+
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}`);
|
|
13474
|
+
console.log(` ${pc16.bold(s.skill.padEnd(8))} ${state}`);
|
|
13475
|
+
console.log(` ${" ".repeat(8)} ${pc16.dim(s.description)}`);
|
|
12970
13476
|
}
|
|
12971
13477
|
console.log();
|
|
12972
13478
|
}
|
|
@@ -12999,6 +13505,10 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
|
|
|
12999
13505
|
one skills update [connect] Re-copy from the installed @withone/connect after an upgrade
|
|
13000
13506
|
one skills remove connect Remove it
|
|
13001
13507
|
|
|
13508
|
+
Connect apps (one.connect.ts, typed by @withone/connect/config):
|
|
13509
|
+
one connect create Approve the app (or its new version) in the browser, then write .env
|
|
13510
|
+
one connect types Write one-connect-env.d.ts so tool slugs and action names autocomplete
|
|
13511
|
+
|
|
13002
13512
|
Guide:
|
|
13003
13513
|
one guide [topic] Full CLI guide (topics: overview, actions, workflows, memory, sync, all)
|
|
13004
13514
|
|
|
@@ -13212,8 +13722,8 @@ config.command("reset").description("Remove the project config for the current d
|
|
|
13212
13722
|
return;
|
|
13213
13723
|
}
|
|
13214
13724
|
}
|
|
13215
|
-
const
|
|
13216
|
-
|
|
13725
|
+
const fs16 = await import("fs");
|
|
13726
|
+
fs16.unlinkSync(globalPath);
|
|
13217
13727
|
if (isAgentMode()) {
|
|
13218
13728
|
json({ deleted: true, scope: "global" });
|
|
13219
13729
|
} else {
|
|
@@ -13230,15 +13740,15 @@ config.command("reset").description("Remove the project config for the current d
|
|
|
13230
13740
|
}
|
|
13231
13741
|
return;
|
|
13232
13742
|
}
|
|
13233
|
-
const
|
|
13234
|
-
const configContent =
|
|
13235
|
-
|
|
13743
|
+
const fs15 = await import("fs");
|
|
13744
|
+
const configContent = fs15.readFileSync(resolved.path, "utf-8");
|
|
13745
|
+
fs15.unlinkSync(resolved.path);
|
|
13236
13746
|
const next = resolveConfig();
|
|
13237
|
-
|
|
13238
|
-
|
|
13747
|
+
fs15.mkdirSync(path14.dirname(resolved.path), { recursive: true });
|
|
13748
|
+
fs15.writeFileSync(resolved.path, configContent);
|
|
13239
13749
|
let fallbackLabel;
|
|
13240
13750
|
if (next.scope === "project") {
|
|
13241
|
-
fallbackLabel = `parent project config (${
|
|
13751
|
+
fallbackLabel = `parent project config (${path14.basename(next.projectRoot)})`;
|
|
13242
13752
|
} else if (next.scope === "global") {
|
|
13243
13753
|
fallbackLabel = "global config";
|
|
13244
13754
|
} else {
|
|
@@ -13247,7 +13757,7 @@ config.command("reset").description("Remove the project config for the current d
|
|
|
13247
13757
|
if (!isAgentMode()) {
|
|
13248
13758
|
const p9 = await import("@clack/prompts");
|
|
13249
13759
|
const confirmed = await p9.confirm({
|
|
13250
|
-
message: `Delete project config for ${
|
|
13760
|
+
message: `Delete project config for ${path14.basename(resolved.projectRoot)}? Will fall back to ${fallbackLabel}.`,
|
|
13251
13761
|
initialValue: false
|
|
13252
13762
|
});
|
|
13253
13763
|
if (p9.isCancel(confirmed) || !confirmed) {
|
|
@@ -13255,9 +13765,9 @@ config.command("reset").description("Remove the project config for the current d
|
|
|
13255
13765
|
return;
|
|
13256
13766
|
}
|
|
13257
13767
|
}
|
|
13258
|
-
|
|
13768
|
+
fs15.unlinkSync(resolved.path);
|
|
13259
13769
|
try {
|
|
13260
|
-
|
|
13770
|
+
fs15.rmdirSync(path14.dirname(resolved.path));
|
|
13261
13771
|
} catch {
|
|
13262
13772
|
}
|
|
13263
13773
|
if (isAgentMode()) {
|
|
@@ -13333,6 +13843,13 @@ flow.command("inspect <runId>").description("Inspect a past run's per-step outpu
|
|
|
13333
13843
|
flow.command("scaffold [template]").description("Generate a workflow scaffold (templates: basic, conditional, loop, ai)").action(async (template) => {
|
|
13334
13844
|
await flowScaffoldCommand(template);
|
|
13335
13845
|
});
|
|
13846
|
+
var connectApp = program.command("connect").description("Create and update a One Connect app from one.connect.ts");
|
|
13847
|
+
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) => {
|
|
13848
|
+
await connectCreateCommand(options);
|
|
13849
|
+
});
|
|
13850
|
+
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) => {
|
|
13851
|
+
await connectTypesCommand(options);
|
|
13852
|
+
});
|
|
13336
13853
|
var relay = program.command("relay").alias("r").description("Receive webhooks from platforms and relay them via passthrough actions");
|
|
13337
13854
|
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) => {
|
|
13338
13855
|
await relayCreateCommand(options);
|
|
@@ -13420,22 +13937,22 @@ program.command("whoami").description("Show the user, organization, and project
|
|
|
13420
13937
|
});
|
|
13421
13938
|
return;
|
|
13422
13939
|
}
|
|
13423
|
-
const
|
|
13940
|
+
const pc17 = (await import("picocolors")).default;
|
|
13424
13941
|
const contextParts = [];
|
|
13425
13942
|
if (whoami.organization) contextParts.push(whoami.organization.name);
|
|
13426
13943
|
if (whoami.project) contextParts.push(whoami.project.name);
|
|
13427
13944
|
const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
|
|
13428
|
-
const envLabel = env === "test" ?
|
|
13429
|
-
const configLabel = configScope === "project" ?
|
|
13945
|
+
const envLabel = env === "test" ? pc17.yellow("test") : pc17.green("live");
|
|
13946
|
+
const configLabel = configScope === "project" ? pc17.cyan("project config") : pc17.magenta("global config");
|
|
13430
13947
|
console.log();
|
|
13431
|
-
console.log(` ${
|
|
13432
|
-
console.log(` ${whoami.user.name} ${
|
|
13433
|
-
if (keyName) console.log(` ${
|
|
13434
|
-
if (whoami.organization) console.log(` ${
|
|
13435
|
-
if (whoami.project) console.log(` ${
|
|
13948
|
+
console.log(` ${pc17.bold(scopeDisplay)} ${pc17.dim("\xB7")} ${envLabel}`);
|
|
13949
|
+
console.log(` ${whoami.user.name} ${pc17.dim(`(${whoami.user.email})`)}`);
|
|
13950
|
+
if (keyName) console.log(` ${pc17.dim("Key:")} ${keyName}`);
|
|
13951
|
+
if (whoami.organization) console.log(` ${pc17.dim("Org:")} ${whoami.organization.name} ${pc17.dim(`(${whoami.organization.id})`)}`);
|
|
13952
|
+
if (whoami.project) console.log(` ${pc17.dim("Project:")} ${whoami.project.name} ${pc17.dim(`(${whoami.project.id})`)}`);
|
|
13436
13953
|
console.log();
|
|
13437
|
-
console.log(` ${
|
|
13438
|
-
console.log(` ${
|
|
13954
|
+
console.log(` ${pc17.dim("Using")} ${configLabel}`);
|
|
13955
|
+
console.log(` ${pc17.dim("API:")} ${apiBase}`);
|
|
13439
13956
|
console.log();
|
|
13440
13957
|
});
|
|
13441
13958
|
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) => {
|