@withone/cli 1.49.0 → 1.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -179,7 +179,7 @@ Opens your browser, you authorize, done. The CLI polls until the connection is l
|
|
|
179
179
|
|
|
180
180
|
### `one list`
|
|
181
181
|
|
|
182
|
-
List your active connections with their status and
|
|
182
|
+
List your active connections with their status, connection keys, and what your access config lets you run on each.
|
|
183
183
|
|
|
184
184
|
```bash
|
|
185
185
|
one list
|
|
@@ -193,6 +193,37 @@ one list
|
|
|
193
193
|
|
|
194
194
|
You need the connection key (rightmost column) when executing actions.
|
|
195
195
|
|
|
196
|
+
**Access reporting.** Every connection carries an `access` field describing what the current [access control](#one-config) config permits — so an agent knows its reach up front instead of discovering it as a failure mid-workflow. Three policies:
|
|
197
|
+
|
|
198
|
+
| Policy | Means | Shown when |
|
|
199
|
+
|--------|-------|-----------|
|
|
200
|
+
| `{"policy": "full"}` | Every action on the connection | Default (admin, no action allowlist) |
|
|
201
|
+
| `{"policy": "methods", "methods": ["GET"]}` | Only actions with those HTTP methods | Permission level is `read` or `write` |
|
|
202
|
+
| `{"policy": "actions", "actions": [{"actionId": "...", "title": "...", "method": "..."}]}` | Only these specific actions | An action allowlist is configured |
|
|
203
|
+
|
|
204
|
+
An action allowlist wins over the permission level (and is then method-filtered by it). When the policy is `actions`, those actions are exactly what may run — no `actions search` needed.
|
|
205
|
+
|
|
206
|
+
```jsonc
|
|
207
|
+
// one --agent list
|
|
208
|
+
{
|
|
209
|
+
"total": 2,
|
|
210
|
+
"showing": 2,
|
|
211
|
+
"connections": [
|
|
212
|
+
{
|
|
213
|
+
"platform": "gmail",
|
|
214
|
+
"state": "operational",
|
|
215
|
+
"key": "live::gmail::default::abc123",
|
|
216
|
+
"access": { "policy": "methods", "methods": ["GET"] }
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
"accessHint": "Permission level \"read\": only GET actions will execute."
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`knowledgeOnly: true` appears when knowledge-only mode is on (execution disabled), and `unresolvedActionIds` lists any allowlisted action ids that could not be looked up. In human output, an `Access` column and a summary note appear only when access is actually scoped.
|
|
224
|
+
|
|
225
|
+
This mirrors the `access` field on the One MCP server's `list_one_integrations` tool, so both surfaces report access the same way.
|
|
226
|
+
|
|
196
227
|
### `one connection delete <connection-key>`
|
|
197
228
|
|
|
198
229
|
Remove a connection by its key.
|
|
@@ -576,6 +607,8 @@ one config
|
|
|
576
607
|
|
|
577
608
|
Settings propagate automatically to all installed agent configs.
|
|
578
609
|
|
|
610
|
+
Run [`one list`](#one-list) to see the effect: each connection reports an `access` field describing exactly what these settings let you run on it.
|
|
611
|
+
|
|
579
612
|
#### `one config skills status` / `one config skills sync`
|
|
580
613
|
|
|
581
614
|
`one init` copies the packaged skill files (`SKILL.md`, `references/`) into `~/.agents/skills/one/` and symlinks per-agent paths to that canonical directory. When the CLI self-updates, the skill files in the canonical dir would normally stay frozen at the version that was installed. To prevent stale docs, every CLI command checks a `.one-cli-version` marker in the canonical dir and silently refreshes the skill files if they don't match the running CLI version. No user action required.
|
|
@@ -2319,7 +2319,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
|
|
|
2319
2319
|
if (flowStack.includes(resolvedKey)) {
|
|
2320
2320
|
throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
|
|
2321
2321
|
}
|
|
2322
|
-
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-
|
|
2322
|
+
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-YDKAYGZW.js");
|
|
2323
2323
|
const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
|
|
2324
2324
|
const subContext = await executeFlow(
|
|
2325
2325
|
subFlow,
|
|
@@ -3238,11 +3238,11 @@ export {
|
|
|
3238
3238
|
ApiError,
|
|
3239
3239
|
OneApi,
|
|
3240
3240
|
TimeoutError,
|
|
3241
|
+
PERMISSION_METHODS,
|
|
3241
3242
|
filterByPermissions,
|
|
3242
3243
|
isMethodAllowed,
|
|
3243
3244
|
isActionAllowed,
|
|
3244
3245
|
buildActionKnowledgeWithGuidance,
|
|
3245
|
-
validateActionInput,
|
|
3246
3246
|
knowledgeCachePath,
|
|
3247
3247
|
searchCachePath,
|
|
3248
3248
|
readCache,
|
|
@@ -3256,6 +3256,7 @@ export {
|
|
|
3256
3256
|
clearEntry,
|
|
3257
3257
|
makeCacheEntry,
|
|
3258
3258
|
resolveActionDetails,
|
|
3259
|
+
validateActionInput,
|
|
3259
3260
|
FLOW_SCHEMA,
|
|
3260
3261
|
getStepTypeDescriptor,
|
|
3261
3262
|
getNestedStepsKeys,
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
FLOW_SCHEMA,
|
|
4
4
|
FlowRunner,
|
|
5
5
|
OneApi,
|
|
6
|
+
PERMISSION_METHODS,
|
|
6
7
|
TimeoutError,
|
|
7
8
|
buildActionKnowledgeWithGuidance,
|
|
8
9
|
buildCacheMeta,
|
|
@@ -31,7 +32,7 @@ import {
|
|
|
31
32
|
validateActionInput,
|
|
32
33
|
walkSteps,
|
|
33
34
|
writeCache
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-D6W756DN.js";
|
|
35
36
|
import {
|
|
36
37
|
memSqlCommand
|
|
37
38
|
} from "./chunk-4WWK3GO5.js";
|
|
@@ -1942,6 +1943,54 @@ function maskApiKey(key) {
|
|
|
1942
1943
|
import * as p4 from "@clack/prompts";
|
|
1943
1944
|
import pc4 from "picocolors";
|
|
1944
1945
|
|
|
1946
|
+
// src/lib/access.ts
|
|
1947
|
+
async function resolveAllowedActions(api, actionIds) {
|
|
1948
|
+
if (actionIds.includes("*")) return [];
|
|
1949
|
+
const resolved = await Promise.all(
|
|
1950
|
+
actionIds.map(async (actionId) => {
|
|
1951
|
+
try {
|
|
1952
|
+
const { details } = await resolveActionDetails(api, actionId, { warn: () => {
|
|
1953
|
+
} });
|
|
1954
|
+
if (!details.connectionPlatform) return null;
|
|
1955
|
+
return {
|
|
1956
|
+
actionId,
|
|
1957
|
+
title: details.title,
|
|
1958
|
+
method: details.method,
|
|
1959
|
+
platform: details.connectionPlatform
|
|
1960
|
+
};
|
|
1961
|
+
} catch {
|
|
1962
|
+
return null;
|
|
1963
|
+
}
|
|
1964
|
+
})
|
|
1965
|
+
);
|
|
1966
|
+
return resolved.filter((a) => a !== null);
|
|
1967
|
+
}
|
|
1968
|
+
function computeConnectionAccess(platform, permissions, allowedActionIds, grantedActions) {
|
|
1969
|
+
if (!allowedActionIds.includes("*")) {
|
|
1970
|
+
const actions2 = grantedActions.filter((a) => a.platform === platform).map(({ actionId, title, method }) => ({ actionId, title, method }));
|
|
1971
|
+
return { policy: "actions", actions: actions2 };
|
|
1972
|
+
}
|
|
1973
|
+
const methods = PERMISSION_METHODS[permissions];
|
|
1974
|
+
if (methods !== null) {
|
|
1975
|
+
return { policy: "methods", methods };
|
|
1976
|
+
}
|
|
1977
|
+
return { policy: "full" };
|
|
1978
|
+
}
|
|
1979
|
+
function formatAccess(access, maxActions = 2) {
|
|
1980
|
+
switch (access.policy) {
|
|
1981
|
+
case "full":
|
|
1982
|
+
return "full";
|
|
1983
|
+
case "methods":
|
|
1984
|
+
return access.methods.join(", ");
|
|
1985
|
+
case "actions": {
|
|
1986
|
+
if (access.actions.length === 0) return "none";
|
|
1987
|
+
const shown = access.actions.slice(0, maxActions).map((a) => `${a.title} (${a.method})`);
|
|
1988
|
+
const rest = access.actions.length - shown.length;
|
|
1989
|
+
return rest > 0 ? `${shown.join(", ")} +${rest} more` : shown.join(", ");
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1945
1994
|
// src/lib/platforms.ts
|
|
1946
1995
|
function findPlatform(platforms, query) {
|
|
1947
1996
|
const normalizedQuery = query.toLowerCase().trim();
|
|
@@ -2155,8 +2204,16 @@ async function connectionListCommand(options) {
|
|
|
2155
2204
|
try {
|
|
2156
2205
|
const allConnections = await api.listConnections();
|
|
2157
2206
|
const ac = getAccessControlFromAllSources();
|
|
2207
|
+
const permissions = ac.permissions || "admin";
|
|
2158
2208
|
const allowedKeys = ac.connectionKeys || ["*"];
|
|
2209
|
+
const actionIds = ac.actionIds || ["*"];
|
|
2210
|
+
const knowledgeOnly = ac.knowledgeAgent || false;
|
|
2159
2211
|
const accessFiltered = allowedKeys.includes("*") ? allConnections : allConnections.filter((conn) => allowedKeys.includes(conn.key));
|
|
2212
|
+
const resolvedActions = await resolveAllowedActions(api, actionIds);
|
|
2213
|
+
const unresolvedActionIds = actionIds.includes("*") ? [] : actionIds.filter((id) => !resolvedActions.some((a) => a.actionId === id));
|
|
2214
|
+
const grantedActions = resolvedActions.filter((a) => isMethodAllowed(a.method, permissions));
|
|
2215
|
+
const accessFor = (platform) => computeConnectionAccess(platform, permissions, actionIds, grantedActions);
|
|
2216
|
+
const hintText = accessHint(permissions, actionIds, knowledgeOnly);
|
|
2160
2217
|
const searchQuery = options?.search?.toLowerCase();
|
|
2161
2218
|
const filtered = searchQuery ? accessFiltered.filter((conn) => conn.platform.toLowerCase().includes(searchQuery)) : accessFiltered;
|
|
2162
2219
|
const limitArg = options?.limit ? parseInt(options.limit, 10) : void 0;
|
|
@@ -2172,8 +2229,12 @@ async function connectionListCommand(options) {
|
|
|
2172
2229
|
state: conn.state,
|
|
2173
2230
|
key: conn.key,
|
|
2174
2231
|
...conn.name && { name: conn.name },
|
|
2175
|
-
...conn.tags?.length && { tags: conn.tags }
|
|
2232
|
+
...conn.tags?.length && { tags: conn.tags },
|
|
2233
|
+
access: accessFor(conn.platform)
|
|
2176
2234
|
})),
|
|
2235
|
+
...knowledgeOnly && { knowledgeOnly: true },
|
|
2236
|
+
...unresolvedActionIds.length > 0 && { unresolvedActionIds },
|
|
2237
|
+
...hintText && { accessHint: hintText },
|
|
2177
2238
|
...limited.length < filtered.length && {
|
|
2178
2239
|
hint: `Showing ${limited.length} of ${filtered.length} connections. Use --search <query> to filter by platform or --limit <n> to see more.`
|
|
2179
2240
|
}
|
|
@@ -2206,20 +2267,34 @@ Add one with: ${pc4.cyan("one connection add gmail")}`,
|
|
|
2206
2267
|
platform: conn.platform,
|
|
2207
2268
|
state: conn.state,
|
|
2208
2269
|
key: conn.key,
|
|
2209
|
-
tags: conn.tags?.length ? conn.tags.join(", ") : ""
|
|
2270
|
+
tags: conn.tags?.length ? conn.tags.join(", ") : "",
|
|
2271
|
+
access: formatAccess(accessFor(conn.platform))
|
|
2210
2272
|
}));
|
|
2211
2273
|
const hasTags = rows.some((r) => r.tags);
|
|
2274
|
+
const hasScopedAccess = rows.some((r) => r.access !== "full");
|
|
2212
2275
|
printTable(
|
|
2213
2276
|
[
|
|
2214
2277
|
{ key: "status", label: "" },
|
|
2215
2278
|
{ key: "platform", label: "Platform" },
|
|
2216
2279
|
{ key: "state", label: "Status" },
|
|
2217
2280
|
{ key: "key", label: "Connection Key", color: pc4.dim },
|
|
2218
|
-
...hasTags ? [{ key: "tags", label: "Tags", color: pc4.dim }] : []
|
|
2281
|
+
...hasTags ? [{ key: "tags", label: "Tags", color: pc4.dim }] : [],
|
|
2282
|
+
...hasScopedAccess ? [{ key: "access", label: "Access", color: pc4.yellow }] : []
|
|
2219
2283
|
],
|
|
2220
2284
|
rows
|
|
2221
2285
|
);
|
|
2222
2286
|
console.log();
|
|
2287
|
+
if (hintText) {
|
|
2288
|
+
const lines = [hintText];
|
|
2289
|
+
if (unresolvedActionIds.length > 0) {
|
|
2290
|
+
lines.push(
|
|
2291
|
+
`Could not resolve ${unresolvedActionIds.length} allowlisted action id(s): ${unresolvedActionIds.join(", ")}`
|
|
2292
|
+
);
|
|
2293
|
+
}
|
|
2294
|
+
p4.note(`${wrapText(lines.join("\n"))}
|
|
2295
|
+
|
|
2296
|
+
Change it with: ${pc4.cyan("one config")}`, "Access");
|
|
2297
|
+
}
|
|
2223
2298
|
if (displayed.length < filtered.length) {
|
|
2224
2299
|
p4.note(
|
|
2225
2300
|
`Showing ${displayed.length} of ${filtered.length}. Increase --limit or run without it to see all.`,
|
|
@@ -2292,6 +2367,37 @@ async function connectionDeleteCommand(connectionKey, options) {
|
|
|
2292
2367
|
error(`Error: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
|
|
2293
2368
|
}
|
|
2294
2369
|
}
|
|
2370
|
+
function accessHint(permissions, actionIds, knowledgeOnly) {
|
|
2371
|
+
const parts = [];
|
|
2372
|
+
if (!actionIds.includes("*")) {
|
|
2373
|
+
parts.push(
|
|
2374
|
+
"Action-scoped: each connection's `access.actions` are the only actions you may run \u2014 use them directly, no `actions search` needed."
|
|
2375
|
+
);
|
|
2376
|
+
} else if (permissions !== "admin") {
|
|
2377
|
+
const methods = PERMISSION_METHODS[permissions]?.join(", ") ?? "";
|
|
2378
|
+
parts.push(`Permission level "${permissions}": only ${methods} actions will execute.`);
|
|
2379
|
+
}
|
|
2380
|
+
if (knowledgeOnly) {
|
|
2381
|
+
parts.push("Knowledge-only mode: `actions execute` is disabled \u2014 read knowledge and write code instead.");
|
|
2382
|
+
}
|
|
2383
|
+
return parts.length > 0 ? parts.join(" ") : null;
|
|
2384
|
+
}
|
|
2385
|
+
function wrapText(text5, width = 72) {
|
|
2386
|
+
return text5.split("\n").map((paragraph) => {
|
|
2387
|
+
const lines = [];
|
|
2388
|
+
let line = "";
|
|
2389
|
+
for (const word of paragraph.split(/\s+/)) {
|
|
2390
|
+
if (line && line.length + 1 + word.length > width) {
|
|
2391
|
+
lines.push(line);
|
|
2392
|
+
line = word;
|
|
2393
|
+
} else {
|
|
2394
|
+
line = line ? `${line} ${word}` : word;
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
if (line) lines.push(line);
|
|
2398
|
+
return lines.join("\n");
|
|
2399
|
+
}).join("\n");
|
|
2400
|
+
}
|
|
2295
2401
|
function getStatusIndicator(state) {
|
|
2296
2402
|
switch (state) {
|
|
2297
2403
|
case "operational":
|
|
@@ -9777,7 +9883,7 @@ Search for actions, read their docs, and execute them. This is the core workflow
|
|
|
9777
9883
|
|
|
9778
9884
|
**Quick start:**
|
|
9779
9885
|
\`\`\`bash
|
|
9780
|
-
one --agent connection list # See connected platforms
|
|
9886
|
+
one --agent connection list # See connected platforms + your access on each
|
|
9781
9887
|
one --agent connection delete <connection-key> # Remove a connection
|
|
9782
9888
|
one --agent actions search <platform> "<query>" -t execute # Find an action
|
|
9783
9889
|
one --agent actions knowledge <platform> <actionId> # Read docs (REQUIRED)
|
|
@@ -9866,6 +9972,7 @@ Request specific sections:
|
|
|
9866
9972
|
- Always use the **exact action ID** from search results \u2014 don't guess
|
|
9867
9973
|
- Always read **knowledge** before executing any action
|
|
9868
9974
|
- Connection keys come from \`one connection list\` \u2014 don't hardcode them
|
|
9975
|
+
- \`connection list\` also reports an \`access\` field per connection (\`full\` / \`methods\` / \`actions\`) \u2014 read it before planning so you don't propose an action the access config will reject
|
|
9869
9976
|
- Skills stay in lockstep with the CLI version automatically \u2014 every command checks a \`.one-cli-version\` marker in the canonical skill dir and refreshes the files if the CLI has been upgraded. Check manually with \`one config skills status\`; force a resync with \`one config skills sync\`
|
|
9870
9977
|
`;
|
|
9871
9978
|
var GUIDE_ACTIONS = `# One Actions \u2014 Reference
|
|
@@ -9880,7 +9987,17 @@ Always follow this sequence. Never skip the knowledge step.
|
|
|
9880
9987
|
one --agent connection list
|
|
9881
9988
|
\`\`\`
|
|
9882
9989
|
|
|
9883
|
-
Returns platforms, status, connection keys, and
|
|
9990
|
+
Returns platforms, status, connection keys, tags, and an \`access\` field per connection describing what the current access config lets you run there:
|
|
9991
|
+
|
|
9992
|
+
| \`access\` | Meaning |
|
|
9993
|
+
|----------|---------|
|
|
9994
|
+
| \`{"policy": "full"}\` | Every action on the connection |
|
|
9995
|
+
| \`{"policy": "methods", "methods": ["GET"]}\` | Only actions with these HTTP methods will execute |
|
|
9996
|
+
| \`{"policy": "actions", "actions": [{"actionId", "title", "method"}]}\` | Only these exact actions \u2014 use them directly, skip \`actions search\` |
|
|
9997
|
+
|
|
9998
|
+
Also present when relevant: \`knowledgeOnly: true\` (execution disabled \u2014 read knowledge and write code instead), \`unresolvedActionIds\` (allowlisted ids that could not be looked up), and \`accessHint\` (a one-line summary of the restriction).
|
|
9999
|
+
|
|
10000
|
+
Read \`access\` before planning \u2014 it prevents proposing an action the config will reject. Change it with \`one config\`.
|
|
9884
10001
|
|
|
9885
10002
|
### 1b. Delete a Connection
|
|
9886
10003
|
|
|
@@ -11456,7 +11573,7 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
|
|
|
11456
11573
|
one whoami Show current user, organization, and project
|
|
11457
11574
|
|
|
11458
11575
|
Workflow (use these in order):
|
|
11459
|
-
1. one list List
|
|
11576
|
+
1. one list List connected platforms, keys, and your access on each
|
|
11460
11577
|
2. one actions search <platform> <q> Search for actions using natural language
|
|
11461
11578
|
3. one actions knowledge <plat> <id> Get full docs for an action (ALWAYS do this before execute)
|
|
11462
11579
|
4. one actions execute <p> <id> <key> Execute the action
|
|
@@ -11714,7 +11831,7 @@ var connection = program.command("connection").description("Manage connections")
|
|
|
11714
11831
|
connection.command("add [platform]").alias("a").description("Add a new connection").option("--tag <name>", "Tag the new connection (disambiguates multiple connections per platform in sync/flow profiles)").action(async (platform, options) => {
|
|
11715
11832
|
await connectionAddCommand(platform, { tag: options.tag });
|
|
11716
11833
|
});
|
|
11717
|
-
connection.command("list").alias("ls").description("List your connections").option("-s, --search <query>", "Filter connections by platform name").option("-l, --limit <n>", "Max connections to return (agent mode default: 20)").action(async (options) => {
|
|
11834
|
+
connection.command("list").alias("ls").description("List your connections and what your access config lets you run on each").option("-s, --search <query>", "Filter connections by platform name").option("-l, --limit <n>", "Max connections to return (agent mode default: 20)").action(async (options) => {
|
|
11718
11835
|
await connectionListCommand(options);
|
|
11719
11836
|
});
|
|
11720
11837
|
connection.command("delete <connection-key>").alias("rm").description("Delete a connection").option("-f, --force", "Skip confirmation prompt").action(async (connectionKey, options) => {
|
package/package.json
CHANGED
package/skills/one/SKILL.md
CHANGED
|
@@ -45,7 +45,21 @@ Always follow this sequence when the user wants to do something on a connected p
|
|
|
45
45
|
one --agent connection list
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
Returns connected platforms with their connection keys (needed for execution)
|
|
48
|
+
Returns connected platforms with their connection keys (needed for execution), platform names in kebab-case (needed for searching), and an `access` field per connection telling you what you may run there.
|
|
49
|
+
|
|
50
|
+
**Read `access` before you plan a workflow** — it saves you from discovering a restriction as a 403 halfway through:
|
|
51
|
+
|
|
52
|
+
| `access` | What it means |
|
|
53
|
+
|----------|---------------|
|
|
54
|
+
| `{"policy": "full"}` | Every action on this connection is available |
|
|
55
|
+
| `{"policy": "methods", "methods": ["GET"]}` | Only actions with these HTTP methods will execute — don't propose writes |
|
|
56
|
+
| `{"policy": "actions", "actions": [...]}` | Only these exact actions may run. Each has `actionId`, `title`, `method` — **use them directly and skip `actions search`** |
|
|
57
|
+
|
|
58
|
+
Two more fields appear only when relevant:
|
|
59
|
+
- `"knowledgeOnly": true` — `actions execute` is disabled. Read knowledge and write integration code instead of executing.
|
|
60
|
+
- `"unresolvedActionIds": [...]` — allowlisted ids that couldn't be looked up; treat them as unavailable and tell the user.
|
|
61
|
+
|
|
62
|
+
An empty `actions` array means the allowlist grants nothing on that connection — say so rather than searching for alternatives.
|
|
49
63
|
|
|
50
64
|
### 1b. Delete a connection
|
|
51
65
|
|