railcode 0.1.25 → 0.1.26
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 +16 -1
- package/dist/index.js +54 -37
- package/package.json +1 -1
- package/static/sdk.js +3 -3
package/README.md
CHANGED
|
@@ -89,7 +89,13 @@ your saved org and are gated server-side by the same capabilities as the console
|
|
|
89
89
|
manifest for editing.
|
|
90
90
|
- `railcode agent create --file agent.json` creates an agent; `update <agent>
|
|
91
91
|
--file agent.json` replaces an existing agent manifest; `delete <agent> --yes`
|
|
92
|
-
archives it.
|
|
92
|
+
archives it. `--visibility <org|personal>` on `create`/`update`/`test` sets or
|
|
93
|
+
changes who the agent belongs to (default `org` on create; omitted on `update`
|
|
94
|
+
leaves it alone). A `personal` agent is invokable and manageable by its
|
|
95
|
+
creator alone — no grant makes it shared — and `personal -> org` is blocked
|
|
96
|
+
once created. Requires `agent:create` (personal) or `agent:create_org` (org).
|
|
97
|
+
`agent show`/`list` print `visibility`, and `show` prints `Owner:` for a
|
|
98
|
+
personal agent.
|
|
93
99
|
- `railcode agent test --file agent.json --input '{"k":"v"}'` runs a draft
|
|
94
100
|
manifest without saving; `railcode agent run <agent> --input '{"k":"v"}'`
|
|
95
101
|
invokes a saved agent and prints the result. Use `--trace` for the step trace
|
|
@@ -97,6 +103,15 @@ your saved org and are gated server-side by the same capabilities as the console
|
|
|
97
103
|
- `railcode agent schedule set <agent> --cron "0 9 * * *" --timezone UTC`
|
|
98
104
|
creates or updates the agent's one schedule. `schedule show`, `pause`,
|
|
99
105
|
`resume`, `delete --yes`, and `run-now` operate on that single schedule.
|
|
106
|
+
- **`personal-connectors`** (aliases `personal-connector`, `pc`) manages **your
|
|
107
|
+
own** connected third-party accounts (Gmail, Slack, …), brokered by Composio —
|
|
108
|
+
distinct from org-admin **service connectors** and from an agent's
|
|
109
|
+
`tools.personal_connectors`. `list` shows toolkits this deployment brokers and
|
|
110
|
+
your connection status; `tools <toolkit>` prints that toolkit's callable tools
|
|
111
|
+
and input schemas; `connect <toolkit>` prints an OAuth URL to open in a
|
|
112
|
+
browser; `call <toolkit> <tool> [--args '<json>']` runs one tool on your own
|
|
113
|
+
connected account. Hits the non-org-scoped `/api/personal-connections/*` plane
|
|
114
|
+
— a personal connection belongs to the human, not the org.
|
|
100
115
|
|
|
101
116
|
## Configuration
|
|
102
117
|
|
package/dist/index.js
CHANGED
|
@@ -3809,6 +3809,12 @@ Agent options:
|
|
|
3809
3809
|
--input-file <path> Read invoke/test input JSON from a file
|
|
3810
3810
|
--trace Include the run step trace in human output
|
|
3811
3811
|
--yes Confirm delete without a prompt
|
|
3812
|
+
--visibility <org|personal> create/update/test only (default: org). A personal
|
|
3813
|
+
agent is owned/invoked by its creator alone — no
|
|
3814
|
+
grant makes it shared, and "personal -> org" is
|
|
3815
|
+
blocked once created. Requires the matching
|
|
3816
|
+
capability: agent:create for personal,
|
|
3817
|
+
agent:create_org for org.
|
|
3812
3818
|
|
|
3813
3819
|
Schedule options:
|
|
3814
3820
|
--name <name> Schedule name (default: default on create)
|
|
@@ -3881,9 +3887,10 @@ async function commandAgentList(args) {
|
|
|
3881
3887
|
console.log("No managed agents in this organization.");
|
|
3882
3888
|
return;
|
|
3883
3889
|
}
|
|
3884
|
-
console.log(formatTable(["name", "model", "uuid", "updated"], agents.map((agent) => [
|
|
3890
|
+
console.log(formatTable(["name", "model", "visibility", "uuid", "updated"], agents.map((agent) => [
|
|
3885
3891
|
agent.name,
|
|
3886
3892
|
typeof agent.manifest.model === "string" ? agent.manifest.model : "-",
|
|
3893
|
+
agent.visibility,
|
|
3887
3894
|
agent.uuid,
|
|
3888
3895
|
shortDate(agent.updated_at),
|
|
3889
3896
|
])));
|
|
@@ -3918,13 +3925,14 @@ async function commandAgentPull(args) {
|
|
|
3918
3925
|
process.stdout.write(manifest);
|
|
3919
3926
|
}
|
|
3920
3927
|
async function commandAgentCreate(args) {
|
|
3921
|
-
assertKnownAgentOptions(args, ["file", "json", "apiUrl"]);
|
|
3928
|
+
assertKnownAgentOptions(args, ["file", "json", "visibility", "apiUrl"]);
|
|
3922
3929
|
const manifest = readAgentManifest(args);
|
|
3930
|
+
const visibility = agentVisibilityOption(args);
|
|
3923
3931
|
const config = requireLogin(args);
|
|
3924
3932
|
const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/agents`, {
|
|
3925
3933
|
method: "POST",
|
|
3926
3934
|
headers: { "Content-Type": "application/json" },
|
|
3927
|
-
body: JSON.stringify({ manifest }),
|
|
3935
|
+
body: JSON.stringify({ manifest, ...(visibility !== undefined ? { visibility } : {}) }),
|
|
3928
3936
|
});
|
|
3929
3937
|
if (resp.status === 401)
|
|
3930
3938
|
await reLoginOrThrow();
|
|
@@ -3932,14 +3940,15 @@ async function commandAgentCreate(args) {
|
|
|
3932
3940
|
printAgentMutation(result, args.options.json === true, "created");
|
|
3933
3941
|
}
|
|
3934
3942
|
async function commandAgentUpdate(args) {
|
|
3935
|
-
assertKnownAgentOptions(args, ["file", "json", "apiUrl"]);
|
|
3943
|
+
assertKnownAgentOptions(args, ["file", "json", "visibility", "apiUrl"]);
|
|
3936
3944
|
const ref = agentArg(args, "update");
|
|
3937
3945
|
const manifest = readAgentManifest(args);
|
|
3946
|
+
const visibility = agentVisibilityOption(args);
|
|
3938
3947
|
const config = requireLogin(args);
|
|
3939
3948
|
const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/agents/${encodeURIComponent(ref)}`, {
|
|
3940
3949
|
method: "PUT",
|
|
3941
3950
|
headers: { "Content-Type": "application/json" },
|
|
3942
|
-
body: JSON.stringify({ manifest }),
|
|
3951
|
+
body: JSON.stringify({ manifest, ...(visibility !== undefined ? { visibility } : {}) }),
|
|
3943
3952
|
});
|
|
3944
3953
|
if (resp.status === 401)
|
|
3945
3954
|
await reLoginOrThrow();
|
|
@@ -3968,14 +3977,15 @@ async function commandAgentDelete(args) {
|
|
|
3968
3977
|
console.log(`Agent "${ref}" deleted.`);
|
|
3969
3978
|
}
|
|
3970
3979
|
async function commandAgentTest(args) {
|
|
3971
|
-
assertKnownAgentOptions(args, ["file", "input", "inputFile", "json", "trace", "apiUrl"]);
|
|
3980
|
+
assertKnownAgentOptions(args, ["file", "input", "inputFile", "json", "trace", "visibility", "apiUrl"]);
|
|
3972
3981
|
const manifest = readAgentManifest(args);
|
|
3973
3982
|
const input = readJsonInput(args);
|
|
3983
|
+
const visibility = agentVisibilityOption(args);
|
|
3974
3984
|
const config = requireLogin(args);
|
|
3975
3985
|
const resp = await authedFetch(config, `/api/organizations/${config.orgUuid}/agents/test`, {
|
|
3976
3986
|
method: "POST",
|
|
3977
3987
|
headers: { "Content-Type": "application/json" },
|
|
3978
|
-
body: JSON.stringify({ manifest, input }),
|
|
3988
|
+
body: JSON.stringify({ manifest, input, ...(visibility !== undefined ? { visibility } : {}) }),
|
|
3979
3989
|
});
|
|
3980
3990
|
if (resp.status === 401)
|
|
3981
3991
|
await reLoginOrThrow();
|
|
@@ -4178,6 +4188,17 @@ function readAgentManifest(args) {
|
|
|
4178
4188
|
throw new CliError("Pass --file <agent-manifest.json>.", 2);
|
|
4179
4189
|
return readJsonObjectFile(path, "Agent manifest");
|
|
4180
4190
|
}
|
|
4191
|
+
// Omitted means "use the server default" on create/test (org) and "leave alone"
|
|
4192
|
+
// on update — never send the key unless the caller passed it explicitly.
|
|
4193
|
+
function agentVisibilityOption(args) {
|
|
4194
|
+
const value = optionString(args, "visibility");
|
|
4195
|
+
if (value === undefined)
|
|
4196
|
+
return undefined;
|
|
4197
|
+
if (value !== "org" && value !== "personal") {
|
|
4198
|
+
throw new CliError(`Invalid --visibility: ${value} (expected "org" or "personal").`, 2);
|
|
4199
|
+
}
|
|
4200
|
+
return value;
|
|
4201
|
+
}
|
|
4181
4202
|
function readJsonObjectFile(path, label) {
|
|
4182
4203
|
const resolved = resolve(process.cwd(), path);
|
|
4183
4204
|
if (!existsSync(resolved))
|
|
@@ -4264,6 +4285,7 @@ function printAgentMutation(result, json, verb) {
|
|
|
4264
4285
|
}
|
|
4265
4286
|
console.log(`Agent "${result.agent.name}" ${verb}.`);
|
|
4266
4287
|
console.log(`UUID: ${result.agent.uuid}`);
|
|
4288
|
+
console.log(`Visibility: ${result.agent.visibility}`);
|
|
4267
4289
|
if (result.warnings.length > 0) {
|
|
4268
4290
|
console.log("");
|
|
4269
4291
|
console.log("Warnings:");
|
|
@@ -4276,6 +4298,9 @@ function printAgent(agent) {
|
|
|
4276
4298
|
console.log(`UUID: ${agent.uuid}`);
|
|
4277
4299
|
console.log(`Description: ${agent.description || "-"}`);
|
|
4278
4300
|
console.log(`Model: ${typeof agent.manifest.model === "string" ? agent.manifest.model : "-"}`);
|
|
4301
|
+
console.log(`Visibility: ${agent.visibility}`);
|
|
4302
|
+
if (agent.visibility === "personal")
|
|
4303
|
+
console.log(`Owner: ${agent.owner_uuid ?? "-"}`);
|
|
4279
4304
|
console.log(`Updated: ${agent.updated_at}`);
|
|
4280
4305
|
console.log(`Content hash: ${agent.content_hash}`);
|
|
4281
4306
|
}
|
|
@@ -5124,13 +5149,13 @@ Usage:
|
|
|
5124
5149
|
railcode personal-connectors list Toolkits this deployment brokers + your status
|
|
5125
5150
|
railcode personal-connectors tools <toolkit> The callable tools of a toolkit (and their inputs)
|
|
5126
5151
|
railcode personal-connectors connect <toolkit> Print the OAuth URL to connect it (open in a browser)
|
|
5127
|
-
railcode personal-connectors call <tool> [--args '<json>'] Run one tool on your own account
|
|
5152
|
+
railcode personal-connectors call <toolkit> <tool> [--args '<json>'] Run one tool on your own account
|
|
5128
5153
|
|
|
5129
5154
|
Aliases: personal-connector, pc
|
|
5130
5155
|
|
|
5131
5156
|
Discovery is what you build against: \`tools <toolkit> --json\` shows each tool's
|
|
5132
5157
|
input schema, so you know what to put in an app's \`personal_connectors:\` and what
|
|
5133
|
-
\`personalConnections.call('<tool>', {...})\` expects.
|
|
5158
|
+
\`personalConnections.call('<toolkit>', '<tool>', {...})\` expects.
|
|
5134
5159
|
|
|
5135
5160
|
Options:
|
|
5136
5161
|
--json Print raw JSON instead of a table
|
|
@@ -5235,9 +5260,12 @@ async function commandPcConnect(args) {
|
|
|
5235
5260
|
}
|
|
5236
5261
|
async function commandPcCall(args) {
|
|
5237
5262
|
rejectUnknownOptions(args, ["json", "args", "apiUrl"], "personal-connectors call", PC_HELP);
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5263
|
+
// Scoped by connector: name the toolkit AND the tool.
|
|
5264
|
+
const toolkit = args.rest[1];
|
|
5265
|
+
const tool = args.rest[2];
|
|
5266
|
+
if (!toolkit || !tool) {
|
|
5267
|
+
throw new CliError("Usage: railcode personal-connectors call <toolkit> <tool> [--args '<json>']", 2);
|
|
5268
|
+
}
|
|
5241
5269
|
let toolArgs = {};
|
|
5242
5270
|
if (typeof args.options.args === "string") {
|
|
5243
5271
|
try {
|
|
@@ -5251,7 +5279,7 @@ async function commandPcCall(args) {
|
|
|
5251
5279
|
const resp = await authedFetch(config, `/api/personal-connections/call`, {
|
|
5252
5280
|
method: "POST",
|
|
5253
5281
|
headers: { "Content-Type": "application/json" },
|
|
5254
|
-
body: JSON.stringify({ tool, arguments: toolArgs }),
|
|
5282
|
+
body: JSON.stringify({ toolkit, tool, arguments: toolArgs }),
|
|
5255
5283
|
});
|
|
5256
5284
|
if (resp.status === 401)
|
|
5257
5285
|
await reLoginOrThrow();
|
|
@@ -5264,7 +5292,7 @@ async function commandPcCall(args) {
|
|
|
5264
5292
|
if (resp.status === 409) {
|
|
5265
5293
|
throw new CliError(await errorDetail(resp), 1);
|
|
5266
5294
|
}
|
|
5267
|
-
const body = await readJsonOrThrow(resp, `Call ${tool}`);
|
|
5295
|
+
const body = await readJsonOrThrow(resp, `Call ${toolkit}/${tool}`);
|
|
5268
5296
|
console.log(JSON.stringify(body, null, 2));
|
|
5269
5297
|
}
|
|
5270
5298
|
// ---------------------------------------------------------------------------
|
|
@@ -7286,17 +7314,6 @@ async function pcForward(ctx, method, apiPath, body) {
|
|
|
7286
7314
|
}
|
|
7287
7315
|
return { status: upstream.status, body: parsed };
|
|
7288
7316
|
}
|
|
7289
|
-
// Which declared toolkit owns `tool` — asked of the user-plane catalog, scanning
|
|
7290
|
-
// only the app's declared toolkits (as the server's app-plane resolver does).
|
|
7291
|
-
async function pcResolveToolkit(ctx, entries, tool) {
|
|
7292
|
-
for (const toolkit of pcDeclaredToolkits(entries)) {
|
|
7293
|
-
const { status, body } = await pcForward(ctx, "GET", `/personal-connections/toolkits/${encodeURIComponent(toolkit)}/tools`);
|
|
7294
|
-
if (status === 200 && Array.isArray(body) && body.some((t) => t.slug === tool)) {
|
|
7295
|
-
return toolkit;
|
|
7296
|
-
}
|
|
7297
|
-
}
|
|
7298
|
-
return null;
|
|
7299
|
-
}
|
|
7300
7317
|
const PC_NOT_IN_MANIFEST = (label) => ({
|
|
7301
7318
|
detail: `personal connector "${label}" was not in the manifest`,
|
|
7302
7319
|
});
|
|
@@ -7336,10 +7353,11 @@ async function handlePersonalConnections(ctx, req, res, path, url) {
|
|
|
7336
7353
|
const { status, body } = await pcForward(ctx, "POST", `/personal-connections/${encodeURIComponent(toolkit)}/connect`);
|
|
7337
7354
|
return sendJson(res, status, body);
|
|
7338
7355
|
}
|
|
7339
|
-
// call(tool, args) — the load-bearing gate
|
|
7340
|
-
//
|
|
7341
|
-
//
|
|
7342
|
-
// prod chokepoint's "no manifest =
|
|
7356
|
+
// call(toolkit, tool, args) — the load-bearing gate. Scoped by connector, so
|
|
7357
|
+
// the bound is a direct match against the local manifest: the toolkit must be
|
|
7358
|
+
// declared AND the specific tool permitted, else 403 (never forwarded). No
|
|
7359
|
+
// declaration at all ⇒ refused, matching the prod chokepoint's "no manifest =
|
|
7360
|
+
// refuse". The user plane then verifies the (toolkit, tool) pair is real.
|
|
7343
7361
|
if (rest === "/call" && req.method === "POST") {
|
|
7344
7362
|
const raw = await readBody(req);
|
|
7345
7363
|
let payload;
|
|
@@ -7349,16 +7367,15 @@ async function handlePersonalConnections(ctx, req, res, path, url) {
|
|
|
7349
7367
|
catch {
|
|
7350
7368
|
return sendJson(res, 400, { detail: "body must be JSON" });
|
|
7351
7369
|
}
|
|
7352
|
-
const tool = payload
|
|
7353
|
-
if (!tool)
|
|
7354
|
-
return sendJson(res, 400, { detail: '"tool"
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
if (!toolkit || !pcAllows(entries, toolkit, tool)) {
|
|
7359
|
-
return sendJson(res, 403, PC_NOT_IN_MANIFEST(tool));
|
|
7370
|
+
const { toolkit, tool } = payload;
|
|
7371
|
+
if (!toolkit || !tool) {
|
|
7372
|
+
return sendJson(res, 400, { detail: '"toolkit" and "tool" are required' });
|
|
7373
|
+
}
|
|
7374
|
+
if (!pcAllows(entries, toolkit, tool)) {
|
|
7375
|
+
return sendJson(res, 403, PC_NOT_IN_MANIFEST(`${toolkit}:${tool}`));
|
|
7360
7376
|
}
|
|
7361
7377
|
const { status, body } = await pcForward(ctx, "POST", "/personal-connections/call", {
|
|
7378
|
+
toolkit,
|
|
7362
7379
|
tool,
|
|
7363
7380
|
arguments: payload.arguments ?? {},
|
|
7364
7381
|
});
|
package/package.json
CHANGED
package/static/sdk.js
CHANGED
|
@@ -736,12 +736,12 @@
|
|
|
736
736
|
() => call("GET", `/personal-connections/${encodeURIComponent(toolkit)}/tools`)
|
|
737
737
|
);
|
|
738
738
|
}
|
|
739
|
-
function callTool(tool, args = {}) {
|
|
739
|
+
function callTool(toolkit, tool, args = {}) {
|
|
740
740
|
return track(
|
|
741
741
|
"personalConnections",
|
|
742
|
-
`personalConnections.call(${q(tool)})`,
|
|
742
|
+
`personalConnections.call(${q(toolkit)}, ${q(tool)})`,
|
|
743
743
|
() => call("POST", "/personal-connections/call", {
|
|
744
|
-
body: { tool, arguments: args }
|
|
744
|
+
body: { toolkit, tool, arguments: args }
|
|
745
745
|
})
|
|
746
746
|
);
|
|
747
747
|
}
|