@trillioncore/cli 1.0.0-next.10 → 1.0.0-next.12
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 +17 -1
- package/dist/index.js +75 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,20 @@ The `tc` CLI signs you into Trillioncore and reads the organizations and integra
|
|
|
4
4
|
|
|
5
5
|
**Requirements:** Node.js 22+, a browser on any device, and a Trillioncore deployment with CLI device login enabled. This is a fresh v1 preview, not v0 command parity; v0 configuration is not imported.
|
|
6
6
|
|
|
7
|
+
## For agents: install and set up Trillioncore for a person
|
|
8
|
+
|
|
9
|
+
When a person asks you to install or set up Trillioncore, run these steps in order:
|
|
10
|
+
|
|
11
|
+
1. Ask whether they already have a Trillioncore account. For a new organization, ask what to call it.
|
|
12
|
+
2. New account: `npm exec --yes --package=@trillioncore/cli@next -- tc signup --organization-name '<name>'`.
|
|
13
|
+
Existing account, new organization: `... tc create-organization --name '<name>'`.
|
|
14
|
+
Existing account and organization: `... tc login`.
|
|
15
|
+
3. The command waits up to five minutes for the browser. Run it in the background with output to a file, and add `--no-browser` when the person's browser is on another machine. Read the URL and the separate eight-character code from the output and relay both to the person. They sign up with their provider (for example Google or Microsoft), confirm the organization name, enter the code, and choose access. Wait for the command to print `Signed in`.
|
|
16
|
+
4. Run `tc whoami --json` to confirm the identity.
|
|
17
|
+
5. Relay the printed `Next:` link. The person connects data sources under Integrations in the app. Then use `tc index`, `tc search`, and `tc get`.
|
|
18
|
+
|
|
19
|
+
Do not ask the person for tokens, passwords, or the verification code in chat. They enter the code only in their browser. Creating an organization makes the person its administrator; it does not approve CLI access by itself.
|
|
20
|
+
|
|
7
21
|
## Get started
|
|
8
22
|
|
|
9
23
|
Once `@next` is published and the hosted login rollout is enabled:
|
|
@@ -20,13 +34,15 @@ npm exec --yes --package=@trillioncore/cli@next -- tc logout
|
|
|
20
34
|
|
|
21
35
|
`tc login` registers a five-minute request and prints a browser URL plus a separate eight-character code. Open the URL on any device and enter the code shown in your terminal. Keep the code private and approve only a login you started. The CLI polls without a callback listener, tunnel, or copied token. After approval it verifies your identity, saves the private session, and acknowledges delivery.
|
|
22
36
|
|
|
23
|
-
Browser sign-in lets you choose one or more organizations and explicitly select enabled accounts within each. Membership alone does not authorize a CLI connection; current organization and account permissions still apply. For a brand-new account,
|
|
37
|
+
Browser sign-in lets you choose one or more organizations and explicitly select enabled accounts within each. Membership alone does not authorize a CLI connection; current organization and account permissions still apply. For a brand-new account, use `tc signup`; the browser asks for an organization name and continues without a rerun. No provider reconnection is required just to authorize the CLI.
|
|
24
38
|
|
|
25
39
|
Prefer `npm exec` during evaluation. `npm install -g @trillioncore/cli@next` replaces your local `tc` executable, even though publishing `next` does not change the npm `latest` tag.
|
|
26
40
|
|
|
27
41
|
## Commands
|
|
28
42
|
|
|
29
43
|
- `login [--no-browser] [--api-url <origin>]`: browser authorization, organization/account selection, and durable local session storage.
|
|
44
|
+
- `signup [--organization-name <name>] [--no-browser] [--api-url <origin>]`: create an account on the sign-up screen, name a new organization (you become its administrator), then finish the same device approval as `login`.
|
|
45
|
+
- `create-organization [--name <name>] [--no-browser] [--api-url <origin>]`: an existing account creates another organization in the browser and becomes its administrator. The CLI then signs in through the same device approval, because a new organization needs its own approval. `tc login` also asks a signed-in person with no organization to name one.
|
|
30
46
|
- `whoami [--json]`: verify the signed-in identity without printing credentials.
|
|
31
47
|
- `logout`: revoke the CLI connection on the server and remove its local session. `--local-only` only forgets local credentials; use the app's **Tokens** page to revoke server access if offline.
|
|
32
48
|
- `organizations [--json]`: list only currently approved organizations, with `--limit` and `--cursor` pagination.
|
package/dist/index.js
CHANGED
|
@@ -3405,10 +3405,22 @@ function verificationUrl(value, requestId) {
|
|
|
3405
3405
|
);
|
|
3406
3406
|
}
|
|
3407
3407
|
}
|
|
3408
|
+
function organizationSetupUrl(url, options) {
|
|
3409
|
+
if (!options.signUp && !options.createOrganization) return url;
|
|
3410
|
+
const next = new URL(url);
|
|
3411
|
+
if (options.signUp) next.pathname = "/connect/cli/sign-up";
|
|
3412
|
+
else next.searchParams.set("create", "organization");
|
|
3413
|
+
if (options.organizationName)
|
|
3414
|
+
next.searchParams.set("name", options.organizationName);
|
|
3415
|
+
return next.toString();
|
|
3416
|
+
}
|
|
3408
3417
|
async function login(options = {}, environment = process.env) {
|
|
3409
3418
|
const issuer = loginOrigin(
|
|
3410
3419
|
options.apiUrl ?? environment.TRILLIONCORE_CLI_API_URL ?? environment.TRILLIONCORE_API_URL ?? DEFAULT_API
|
|
3411
3420
|
);
|
|
3421
|
+
const organizationName = options.organizationName?.trim();
|
|
3422
|
+
if (organizationName !== void 0 && (!organizationName || organizationName.length > 100 || /[\u0000-\u001f\u007f-\u009f]/.test(organizationName)))
|
|
3423
|
+
throw new Error("Organization name must be 1-100 printable characters.");
|
|
3412
3424
|
const abort = new AbortController();
|
|
3413
3425
|
const verifier = randomBytes2(32).toString("base64url");
|
|
3414
3426
|
const alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
|
@@ -3475,9 +3487,9 @@ async function login(options = {}, environment = process.env) {
|
|
|
3475
3487
|
() => stop("Sign-in timed out. Run tc login again."),
|
|
3476
3488
|
deadline - Date.now()
|
|
3477
3489
|
);
|
|
3478
|
-
const authorization =
|
|
3479
|
-
registration.verification_uri,
|
|
3480
|
-
|
|
3490
|
+
const authorization = organizationSetupUrl(
|
|
3491
|
+
verificationUrl(registration.verification_uri, requestId),
|
|
3492
|
+
{ ...options, ...organizationName ? { organizationName } : {} }
|
|
3481
3493
|
);
|
|
3482
3494
|
if (options.onAuthorizationUrl)
|
|
3483
3495
|
await options.onAuthorizationUrl(authorization, userCode);
|
|
@@ -3551,7 +3563,7 @@ Only approve a login you started. Keep the code private.`
|
|
|
3551
3563
|
"Signed in, but delivery acknowledgement failed. The temporary delivery expires automatically."
|
|
3552
3564
|
)
|
|
3553
3565
|
);
|
|
3554
|
-
return { issuer, ...who };
|
|
3566
|
+
return { issuer, appUrl: new URL(authorization).origin, ...who };
|
|
3555
3567
|
} catch (error) {
|
|
3556
3568
|
if (!persisted)
|
|
3557
3569
|
await revoke(credentials).catch(
|
|
@@ -3682,6 +3694,14 @@ var INTEGRATION_PROVIDER_DEFINITIONS = {
|
|
|
3682
3694
|
modelVisibility: "declared",
|
|
3683
3695
|
identity: { source: "externalAccountId", kind: "email", prefix: "", unavailable: "Account email unavailable" }
|
|
3684
3696
|
},
|
|
3697
|
+
slack: {
|
|
3698
|
+
// Slack workspace identity is retained in the Airbyte binding. The visible local account
|
|
3699
|
+
// identifier is deliberately unique so an organization can keep separate connections.
|
|
3700
|
+
description: "Slack channels and messages",
|
|
3701
|
+
nativeRecordKinds: [],
|
|
3702
|
+
modelVisibility: "declared",
|
|
3703
|
+
identity: { source: "unavailable", unavailable: "Slack workspace identity unavailable" }
|
|
3704
|
+
},
|
|
3685
3705
|
postgres: {
|
|
3686
3706
|
// Legacy stored projections remain discoverable; tables use approved manifests.
|
|
3687
3707
|
description: "PostgreSQL tables",
|
|
@@ -3711,7 +3731,7 @@ function integrationProviderDefinition(provider) {
|
|
|
3711
3731
|
}
|
|
3712
3732
|
|
|
3713
3733
|
// ../../packages/types/dist/agent.js
|
|
3714
|
-
var AGENT_SOURCE_TYPES = ["fathom", "fireflies", "harvest", "outlook", "gmail", "postgres", "github", "google_analytics"];
|
|
3734
|
+
var AGENT_SOURCE_TYPES = ["fathom", "fireflies", "harvest", "outlook", "gmail", "slack", "postgres", "github", "google_analytics"];
|
|
3715
3735
|
var AGENT_SEARCH_FILTER_TYPES = [...AGENT_SOURCE_TYPES, "email"];
|
|
3716
3736
|
function presentAgentIndex(result, resourceKey) {
|
|
3717
3737
|
if (resourceKey !== void 0)
|
|
@@ -3908,6 +3928,7 @@ function agentHelp(primitive) {
|
|
|
3908
3928
|
},
|
|
3909
3929
|
{ name: "create-document", description: "Create a native Markdown/JSON document private to its creator using --organization, --path, --format and --file (UTF8). Requires document read/write opt-in. No integration writes." },
|
|
3910
3930
|
{ name: "update-document", description: "Replace the whole native document text using --ref and --file (UTF8); optional --path/--format/--description. Ref survives path changes. Requires document read/write opt-in." },
|
|
3931
|
+
{ name: "act", description: "Perform one provider action on an authorized account, such as sending an email. Syntax: tc act <account-id> <action> --input '<json>' --organization <org-id>. tc index on the account lists its actions and input fields. Requires the connection's actions opt-in (Settings) and the account's admin-granted permission. Actions cannot be undone; confirm with the user before sending to other people." },
|
|
3911
3932
|
{ name: "help", description: "Show supported CLI primitives." }
|
|
3912
3933
|
];
|
|
3913
3934
|
if (!primitive)
|
|
@@ -4058,7 +4079,8 @@ async function request(command, path, init, input) {
|
|
|
4058
4079
|
}
|
|
4059
4080
|
if (!response.ok) {
|
|
4060
4081
|
const authenticationFailure = response.status === 401 || response.status === 403;
|
|
4061
|
-
const
|
|
4082
|
+
const actionMessage = command === "act" && authenticationFailure ? body?.error?.message : void 0;
|
|
4083
|
+
const envelope = typeof actionMessage === "string" ? agentError("forbidden", actionMessage.slice(0, 300)) : authenticationFailure ? null : parseErrorEnvelope(body);
|
|
4062
4084
|
throw new CliAgentError(
|
|
4063
4085
|
envelope ?? agentError(
|
|
4064
4086
|
authenticationFailure ? "forbidden" : response.status === 404 ? "not_found" : "unavailable",
|
|
@@ -4246,6 +4268,22 @@ async function writeDocument(command, input) {
|
|
|
4246
4268
|
}, input);
|
|
4247
4269
|
}
|
|
4248
4270
|
var createDocument = (input) => writeDocument("create-document", input);
|
|
4271
|
+
async function act(input) {
|
|
4272
|
+
let value;
|
|
4273
|
+
try {
|
|
4274
|
+
value = JSON.parse(input.input);
|
|
4275
|
+
} catch {
|
|
4276
|
+
throw new CliAgentError(agentError("invalid_input", "--input must be a JSON object of the action's fields."));
|
|
4277
|
+
}
|
|
4278
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new CliAgentError(agentError("invalid_input", "--input must be a JSON object of the action's fields."));
|
|
4279
|
+
const config = configuration(input);
|
|
4280
|
+
const parameters = new URLSearchParams();
|
|
4281
|
+
if (!config.pinned && input.organization) parameters.set("organizationId", input.organization);
|
|
4282
|
+
return request("act", config.pinned ? "/api/organizations/:organizationId/agent/act" : `/cli/act?${parameters}`, {
|
|
4283
|
+
method: "POST",
|
|
4284
|
+
body: JSON.stringify({ integrationAccountId: input.accountId, action: input.action, input: value })
|
|
4285
|
+
}, input);
|
|
4286
|
+
}
|
|
4249
4287
|
var updateDocument = (input) => writeDocument("update-document", input);
|
|
4250
4288
|
function help(primitive) {
|
|
4251
4289
|
return agentHelp(primitive);
|
|
@@ -4320,6 +4358,10 @@ function renderIndex(result) {
|
|
|
4320
4358
|
])
|
|
4321
4359
|
];
|
|
4322
4360
|
}),
|
|
4361
|
+
...catalog.actions?.length ? [
|
|
4362
|
+
`Actions${catalog.actions.some((action) => !action.available) ? " (turn on actions for this connection in Settings to use)" : ""}: tc act ${accountIdFromPath(catalog.connectionRef) ?? "<account-id>"} <action> --input '<json>'`,
|
|
4363
|
+
...catalog.actions.flatMap((action) => [` ${action.name}: ${action.description}`, ...Object.entries(action.input).map(([field, type]) => ` ${field}: ${type}`)])
|
|
4364
|
+
] : [],
|
|
4323
4365
|
// The catalog's own details line already names this account; add the generic one only without it.
|
|
4324
4366
|
...details.length ? details : ["Details: tc index <account-path> --resource-key <key>"],
|
|
4325
4367
|
'Search every source at once: tc search "<words>" --grouped',
|
|
@@ -4564,6 +4606,7 @@ function createProgram(overrides = {}) {
|
|
|
4564
4606
|
createDocument,
|
|
4565
4607
|
updateDocument,
|
|
4566
4608
|
agentRequestTraces,
|
|
4609
|
+
act,
|
|
4567
4610
|
organizations,
|
|
4568
4611
|
index,
|
|
4569
4612
|
search,
|
|
@@ -4573,17 +4616,19 @@ function createProgram(overrides = {}) {
|
|
|
4573
4616
|
};
|
|
4574
4617
|
const program2 = new Command();
|
|
4575
4618
|
program2.name("tc").description("Trillioncore CLI \u2014 authorized integration record access").version(CLI_VERSION).helpCommand(false);
|
|
4576
|
-
|
|
4577
|
-
"Sign in through your browser and choose an organization and accounts"
|
|
4578
|
-
).option("--api-url <origin>", "Trillioncore API origin").option("--no-browser", "Print the sign-in URL without opening a browser").action(async (options) => {
|
|
4619
|
+
const signIn = async (options, setup = {}) => {
|
|
4579
4620
|
try {
|
|
4580
4621
|
const result = await login({
|
|
4581
|
-
apiUrl: options.apiUrl,
|
|
4582
|
-
noBrowser: options.browser === false
|
|
4622
|
+
...options.apiUrl !== void 0 ? { apiUrl: options.apiUrl } : {},
|
|
4623
|
+
noBrowser: options.browser === false,
|
|
4624
|
+
...setup,
|
|
4625
|
+
...options.organizationName !== void 0 ? { organizationName: options.organizationName } : {}
|
|
4583
4626
|
});
|
|
4584
4627
|
console.log(
|
|
4585
4628
|
`Signed in as ${result.email.replace(/[\u0000-\u001f\u007f-\u009f]/g, "")} to ${(result.organizationName ?? "your approved organizations").replace(/[\u0000-\u001f\u007f-\u009f]/g, "")}.`
|
|
4586
4629
|
);
|
|
4630
|
+
if (setup.signUp || setup.createOrganization)
|
|
4631
|
+
console.log(`Next: connect your data sources under Integrations at ${result.appUrl}`);
|
|
4587
4632
|
if (process.env.TRILLIONCORE_TOKEN || process.env.TRILLIONCORE_ORG_ID)
|
|
4588
4633
|
console.error(
|
|
4589
4634
|
"Manual token/organization environment overrides are set. Unset both to use this browser session."
|
|
@@ -4591,7 +4636,16 @@ function createProgram(overrides = {}) {
|
|
|
4591
4636
|
} catch (error) {
|
|
4592
4637
|
writeError(error, false);
|
|
4593
4638
|
}
|
|
4594
|
-
}
|
|
4639
|
+
};
|
|
4640
|
+
program2.command("login").description(
|
|
4641
|
+
"Sign in through your browser and choose an organization and accounts"
|
|
4642
|
+
).option("--api-url <origin>", "Trillioncore API origin").option("--no-browser", "Print the sign-in URL without opening a browser").action((options) => signIn(options));
|
|
4643
|
+
program2.command("signup").description(
|
|
4644
|
+
"Create a Trillioncore account and organization in your browser, then sign in this CLI"
|
|
4645
|
+
).option("--api-url <origin>", "Trillioncore API origin").option("--no-browser", "Print the sign-up URL without opening a browser").option("--organization-name <name>", "Suggested organization name; the browser asks to confirm it").action((options) => signIn(options, { signUp: true }));
|
|
4646
|
+
program2.command("create-organization").description(
|
|
4647
|
+
"Create another organization in your browser; you become its administrator and this CLI signs in to it"
|
|
4648
|
+
).option("--name <name>", "Suggested organization name; the browser asks to confirm it").option("--api-url <origin>", "Trillioncore API origin").option("--no-browser", "Print the URL without opening a browser").action((options) => signIn({ ...options, organizationName: options.name }, { createOrganization: true }));
|
|
4595
4649
|
program2.command("whoami").description("Verify your current CLI identity").option("-j, --json", "Output identity without credentials").action(async (options) => {
|
|
4596
4650
|
try {
|
|
4597
4651
|
const result = await whoami();
|
|
@@ -4696,6 +4750,14 @@ ${AGENT_DISCOVERY_GUIDANCE}`).option("--organization <id>", "Target an approved
|
|
|
4696
4750
|
}
|
|
4697
4751
|
});
|
|
4698
4752
|
}
|
|
4753
|
+
program2.command("act <account-id> <action>").description("Perform one provider action, such as gmail.send_email; tc index on the account lists its actions. Requires the connection's actions opt-in. Actions cannot be undone.").requiredOption("--input <json>", "JSON object of the action's input fields").option("--organization <id>", "Explicit approved organization; must match any configured pin").option("-j, --json", "Output shared DTO JSON").action(async (accountId, action, options) => {
|
|
4754
|
+
try {
|
|
4755
|
+
const result = await commands.act({ accountId, action, input: options.input, ...options.organization ? { organization: options.organization } : {} });
|
|
4756
|
+
writeResult(result, Boolean(options.json), JSON.stringify(result, null, 2));
|
|
4757
|
+
} catch (error) {
|
|
4758
|
+
writeError(error, Boolean(options.json));
|
|
4759
|
+
}
|
|
4760
|
+
});
|
|
4699
4761
|
program2.command("help [primitive]").description("Show supported CLI primitives").option("-j, --json", "Output shared DTO JSON").action((primitive, options) => {
|
|
4700
4762
|
const result = commands.help(primitive);
|
|
4701
4763
|
if (isAgentError(result)) {
|
|
@@ -4710,7 +4772,7 @@ if (process.argv[1] !== void 0 && fileURLToPath(import.meta.url) === realpathSyn
|
|
|
4710
4772
|
try {
|
|
4711
4773
|
const program2 = createProgram();
|
|
4712
4774
|
program2.hook("preAction", async (_program, command) => {
|
|
4713
|
-
if (!["organizations", "index", "search", "get", "sql", "create-document", "update-document", "agent-request-traces"].includes(command.name())) return;
|
|
4775
|
+
if (!["organizations", "index", "search", "get", "sql", "create-document", "update-document", "act", "agent-request-traces"].includes(command.name())) return;
|
|
4714
4776
|
const effective = await authenticatedEnvironment();
|
|
4715
4777
|
for (const key of [
|
|
4716
4778
|
"TRILLIONCORE_CLI_API_URL",
|