@meistrari/tela-cli 0.7.23 → 0.7.25
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 +14 -0
- package/dist/commands/ingredients.d.ts +38 -0
- package/dist/index.js +779 -40
- package/package.json +1 -1
- package/skills/tela-cli/SKILL.md +26 -3
package/README.md
CHANGED
|
@@ -19,6 +19,14 @@ tela context list --json
|
|
|
19
19
|
tela context show <context-id> --json
|
|
20
20
|
tela context use <context-id>
|
|
21
21
|
tela env list --json
|
|
22
|
+
tela update --json
|
|
23
|
+
tela api <slug> <METHOD> <PATH> [args...] --env <slug> --body <json> --pretty
|
|
24
|
+
tela docs endpoints <slug> --pretty
|
|
25
|
+
tela docs reference <slug> [METHOD PATH|file-path] --pretty
|
|
26
|
+
tela docs guide <slug> [path]
|
|
27
|
+
tela env list <ingredient-slug> --pretty
|
|
28
|
+
tela env set <ingredient-slug> <environment>
|
|
29
|
+
tela whoami --pretty
|
|
22
30
|
tela doctor auth --json
|
|
23
31
|
tela agent sync [repo] --current --all --dry-run --json --verbose
|
|
24
32
|
tela agent run --input <name=path>... --plain --json --verbose
|
|
@@ -31,6 +39,12 @@ may refresh a user-session credential in Keychain before returning metadata or
|
|
|
31
39
|
printing a raw token. Creating, removing, editing, and repairing contexts are
|
|
32
40
|
Dock-first workflows.
|
|
33
41
|
|
|
42
|
+
The legacy `telactl` Pantry/service workflows are ported into this binary as
|
|
43
|
+
top-level `update`, `api`, `docs`, `whoami`, and `token` commands. Ingredient
|
|
44
|
+
cache and per-ingredient environment defaults live under `TELA_HOME` next to
|
|
45
|
+
the Dock config, while credential material still comes from the selected Tela
|
|
46
|
+
context through `@meistrari/tela-local-config`.
|
|
47
|
+
|
|
34
48
|
## Development
|
|
35
49
|
|
|
36
50
|
From the repository root:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CliIo, GlobalOptions } from '../output';
|
|
2
|
+
export type CachedEnvironment = {
|
|
3
|
+
slug: string;
|
|
4
|
+
url: string;
|
|
5
|
+
};
|
|
6
|
+
export type CachedIngredient = {
|
|
7
|
+
slug: string;
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
type: 'api' | 'library' | 'skill';
|
|
11
|
+
status: string;
|
|
12
|
+
environments: CachedEnvironment[];
|
|
13
|
+
};
|
|
14
|
+
type ParsedArg = {
|
|
15
|
+
kind: 'query';
|
|
16
|
+
key: string;
|
|
17
|
+
value: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'header';
|
|
20
|
+
key: string;
|
|
21
|
+
value: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'field';
|
|
24
|
+
key: string;
|
|
25
|
+
value: string;
|
|
26
|
+
} | {
|
|
27
|
+
kind: 'rawField';
|
|
28
|
+
key: string;
|
|
29
|
+
value: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function runTelactlCommand(command: string, args: string[], options: GlobalOptions, io: CliIo): Promise<number>;
|
|
32
|
+
export declare function runIngredientEnvCommand(args: string[], options: GlobalOptions, io: CliIo): Promise<number | null>;
|
|
33
|
+
export declare function fetchIngredientCatalog(headers: Record<string, string>, fetchImpl?: typeof fetch): Promise<CachedIngredient[]>;
|
|
34
|
+
export declare function joinUrl(base: string, routePath: string): string;
|
|
35
|
+
export declare function parseHttpieArg(arg: string): ParsedArg | null;
|
|
36
|
+
export declare function extractGitHubFilePath(url: string): string | null;
|
|
37
|
+
export declare function resolveGuidePath(rootFilePath: string, providedPath?: string): string;
|
|
38
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -133,14 +133,14 @@ var require_auth = __commonJS({
|
|
|
133
133
|
"use strict";
|
|
134
134
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
135
135
|
exports2.shouldRefreshUserSession = shouldRefreshUserSession;
|
|
136
|
-
exports2.refreshUserSessionCredential =
|
|
136
|
+
exports2.refreshUserSessionCredential = refreshUserSessionCredential3;
|
|
137
137
|
exports2.createStoredUserSession = createStoredUserSession;
|
|
138
138
|
exports2.getJwtExpiresAt = getJwtExpiresAt;
|
|
139
139
|
var credentials_1 = require_credentials();
|
|
140
140
|
function shouldRefreshUserSession(credential, now = refreshSkewDate()) {
|
|
141
141
|
return credential.kind === "user-session" && Boolean(credential.refreshToken) && (0, credentials_1.isExpired)(credential, now);
|
|
142
142
|
}
|
|
143
|
-
async function
|
|
143
|
+
async function refreshUserSessionCredential3(environment, credentialRef, credential, credentials, options = {}) {
|
|
144
144
|
if (!shouldRefreshUserSession(credential, options.now ?? refreshSkewDate())) {
|
|
145
145
|
return credential;
|
|
146
146
|
}
|
|
@@ -315,7 +315,7 @@ var require_paths = __commonJS({
|
|
|
315
315
|
"use strict";
|
|
316
316
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
317
317
|
exports2.expandHome = expandHome;
|
|
318
|
-
exports2.getTelaLocalPaths =
|
|
318
|
+
exports2.getTelaLocalPaths = getTelaLocalPaths2;
|
|
319
319
|
exports2.configBackupPath = configBackupPath;
|
|
320
320
|
var node_os_1 = require("node:os");
|
|
321
321
|
var node_path_1 = require("node:path");
|
|
@@ -326,7 +326,7 @@ var require_paths = __commonJS({
|
|
|
326
326
|
return (0, node_path_1.join)((0, node_os_1.homedir)(), path.slice(2));
|
|
327
327
|
return path;
|
|
328
328
|
}
|
|
329
|
-
function
|
|
329
|
+
function getTelaLocalPaths2(rootDir = process.env.TELA_HOME ?? "~/.tela") {
|
|
330
330
|
const root = (0, node_path_1.resolve)(expandHome(rootDir));
|
|
331
331
|
return {
|
|
332
332
|
rootDir: root,
|
|
@@ -346,7 +346,7 @@ var require_config = __commonJS({
|
|
|
346
346
|
"../../packages/local-config/dist/config.js"(exports2) {
|
|
347
347
|
"use strict";
|
|
348
348
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
349
|
-
exports2.loadConfig =
|
|
349
|
+
exports2.loadConfig = loadConfig6;
|
|
350
350
|
exports2.saveConfig = saveConfig;
|
|
351
351
|
exports2.updateConfig = updateConfig2;
|
|
352
352
|
exports2.upsertEnvironment = upsertEnvironment;
|
|
@@ -367,7 +367,7 @@ var require_config = __commonJS({
|
|
|
367
367
|
"Authorization",
|
|
368
368
|
"x-data-token"
|
|
369
369
|
]);
|
|
370
|
-
async function
|
|
370
|
+
async function loadConfig6(configFile = (0, paths_1.getTelaLocalPaths)().configFile) {
|
|
371
371
|
let raw;
|
|
372
372
|
try {
|
|
373
373
|
raw = await (0, promises_1.readFile)(configFile, "utf8");
|
|
@@ -388,7 +388,7 @@ var require_config = __commonJS({
|
|
|
388
388
|
await (0, promises_1.rename)(tempFile, configFile);
|
|
389
389
|
}
|
|
390
390
|
async function updateConfig2(mutator, configFile = (0, paths_1.getTelaLocalPaths)().configFile) {
|
|
391
|
-
const current = await
|
|
391
|
+
const current = await loadConfig6(configFile);
|
|
392
392
|
const next = mutator(current) ?? current;
|
|
393
393
|
await saveConfig(next, configFile);
|
|
394
394
|
return next;
|
|
@@ -558,8 +558,8 @@ var require_context = __commonJS({
|
|
|
558
558
|
"../../packages/local-config/dist/context.js"(exports2) {
|
|
559
559
|
"use strict";
|
|
560
560
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
561
|
-
exports2.resolveContext =
|
|
562
|
-
function
|
|
561
|
+
exports2.resolveContext = resolveContext3;
|
|
562
|
+
function resolveContext3(config, input = {}) {
|
|
563
563
|
const env = input.env ?? process.env;
|
|
564
564
|
const contextId = input.contextId ?? env.TELA_CONTEXT ?? resolveFromEnvironmentAndWorkspace(config, input.environmentId, input.workspace) ?? config.activeContextId;
|
|
565
565
|
if (!contextId) {
|
|
@@ -1518,7 +1518,7 @@ var require_agent_api = __commonJS({
|
|
|
1518
1518
|
var tela_local_config_1 = require_dist();
|
|
1519
1519
|
var auth_1 = require_auth2();
|
|
1520
1520
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
1521
|
-
function
|
|
1521
|
+
function joinUrl2(baseUrl, path) {
|
|
1522
1522
|
return new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
|
|
1523
1523
|
}
|
|
1524
1524
|
function parseJsonBody(responseText) {
|
|
@@ -1589,7 +1589,7 @@ var require_agent_api = __commonJS({
|
|
|
1589
1589
|
exports2.AgentApiError = AgentApiError;
|
|
1590
1590
|
async function listOrganizations(config, fetchFn = fetch) {
|
|
1591
1591
|
const headers = await (0, auth_1.buildAgentApiHeaders)(config, { fetchFn });
|
|
1592
|
-
const response = await fetchFn(
|
|
1592
|
+
const response = await fetchFn(joinUrl2(config.agentApiUrl, "/v3/organizations"), {
|
|
1593
1593
|
method: "GET",
|
|
1594
1594
|
headers,
|
|
1595
1595
|
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
|
|
@@ -1606,7 +1606,7 @@ var require_agent_api = __commonJS({
|
|
|
1606
1606
|
}
|
|
1607
1607
|
async function provisionForgejoUser(input, config, fetchFn = fetch) {
|
|
1608
1608
|
const headers = await (0, auth_1.buildAgentApiHeaders)(config, { fetchFn });
|
|
1609
|
-
const response = await fetchFn(
|
|
1609
|
+
const response = await fetchFn(joinUrl2(config.agentApiUrl, "/v3/forgejo/users"), {
|
|
1610
1610
|
method: "POST",
|
|
1611
1611
|
headers: {
|
|
1612
1612
|
...headers,
|
|
@@ -1630,7 +1630,7 @@ var require_agent_api = __commonJS({
|
|
|
1630
1630
|
}
|
|
1631
1631
|
async function fetchRepositoryLookup(repository, config, fetchFn = fetch) {
|
|
1632
1632
|
const headers = await (0, auth_1.buildAgentApiHeaders)(config, { fetchFn });
|
|
1633
|
-
const response = await fetchFn(
|
|
1633
|
+
const response = await fetchFn(joinUrl2(config.agentApiUrl, `/v3/agents/repositories/${encodeURIComponent(repository)}`), {
|
|
1634
1634
|
method: "GET",
|
|
1635
1635
|
headers,
|
|
1636
1636
|
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
|
|
@@ -1652,7 +1652,7 @@ var require_agent_api = __commonJS({
|
|
|
1652
1652
|
}
|
|
1653
1653
|
async function fetchCommitFeedback(organizationName, repository, commitHash, config, fetchFn = fetch) {
|
|
1654
1654
|
const headers = await (0, auth_1.buildAgentApiHeaders)(config, { fetchFn });
|
|
1655
|
-
const response = await fetchFn(
|
|
1655
|
+
const response = await fetchFn(joinUrl2(config.agentApiUrl, `/v3/agents/${encodeURIComponent(organizationName)}/${encodeURIComponent(repository)}/feedback?commitHash=${encodeURIComponent(commitHash)}`), {
|
|
1656
1656
|
method: "GET",
|
|
1657
1657
|
headers,
|
|
1658
1658
|
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
|
|
@@ -1674,7 +1674,7 @@ var require_agent_api = __commonJS({
|
|
|
1674
1674
|
if (!config.credential) {
|
|
1675
1675
|
throw new AgentApiError("Tela API agent listing requires a resolved credential", 0, "");
|
|
1676
1676
|
}
|
|
1677
|
-
const url = new URL(
|
|
1677
|
+
const url = new URL(joinUrl2(config.telaApiUrl, "/agent"));
|
|
1678
1678
|
url.searchParams.set("includeProjects", "true");
|
|
1679
1679
|
const response = await fetchFn(url.toString(), {
|
|
1680
1680
|
method: "GET",
|
|
@@ -1696,7 +1696,7 @@ var require_agent_api = __commonJS({
|
|
|
1696
1696
|
}
|
|
1697
1697
|
async function listAvailableRepositories(config, fetchFn = fetch) {
|
|
1698
1698
|
const headers = await (0, auth_1.buildAgentApiHeaders)(config, { fetchFn });
|
|
1699
|
-
const response = await fetchFn(
|
|
1699
|
+
const response = await fetchFn(joinUrl2(config.agentApiUrl, "/v3/agents/repositories"), {
|
|
1700
1700
|
method: "GET",
|
|
1701
1701
|
headers,
|
|
1702
1702
|
signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
|
|
@@ -1803,13 +1803,13 @@ var require_claude = __commonJS({
|
|
|
1803
1803
|
minor: 1,
|
|
1804
1804
|
patch: 108
|
|
1805
1805
|
};
|
|
1806
|
-
function
|
|
1806
|
+
function isRecord2(value) {
|
|
1807
1807
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1808
1808
|
}
|
|
1809
1809
|
function parseClaudeSettings(path, content) {
|
|
1810
1810
|
try {
|
|
1811
1811
|
const parsed = JSON.parse(content);
|
|
1812
|
-
if (
|
|
1812
|
+
if (isRecord2(parsed)) {
|
|
1813
1813
|
return parsed;
|
|
1814
1814
|
}
|
|
1815
1815
|
} catch (error) {
|
|
@@ -1831,7 +1831,7 @@ var require_claude = __commonJS({
|
|
|
1831
1831
|
}
|
|
1832
1832
|
function readAllowedPermissionRules(settings, settingsPath) {
|
|
1833
1833
|
const permissions = settings.permissions;
|
|
1834
|
-
if (!
|
|
1834
|
+
if (!isRecord2(permissions) || !Array.isArray(permissions.allow) || permissions.allow.length === 0) {
|
|
1835
1835
|
throw (0, errors_1.createUserFacingError)({
|
|
1836
1836
|
code: "claude_project_permissions_missing",
|
|
1837
1837
|
summary: "Agent repository is missing Claude project tool permissions.",
|
|
@@ -3300,7 +3300,7 @@ var require_run = __commonJS({
|
|
|
3300
3300
|
function safeNumber(value) {
|
|
3301
3301
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3302
3302
|
}
|
|
3303
|
-
function
|
|
3303
|
+
function isRecord2(value) {
|
|
3304
3304
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3305
3305
|
}
|
|
3306
3306
|
function formatDurationMs(durationMs) {
|
|
@@ -3327,11 +3327,11 @@ var require_run = __commonJS({
|
|
|
3327
3327
|
return `${minutes}m ${remainder}s`;
|
|
3328
3328
|
}
|
|
3329
3329
|
function summarizePermissionDenial(value) {
|
|
3330
|
-
if (!
|
|
3330
|
+
if (!isRecord2(value)) {
|
|
3331
3331
|
return "Permission denied for a tool call.";
|
|
3332
3332
|
}
|
|
3333
3333
|
const toolName = safeString(value.tool_name) ?? "tool";
|
|
3334
|
-
if (!
|
|
3334
|
+
if (!isRecord2(value.tool_input)) {
|
|
3335
3335
|
return `Permission denied: ${toolName}`;
|
|
3336
3336
|
}
|
|
3337
3337
|
const inputSummary = summarizeToolInput(toolName, value.tool_input);
|
|
@@ -3361,7 +3361,7 @@ var require_run = __commonJS({
|
|
|
3361
3361
|
return value !== void 0;
|
|
3362
3362
|
}
|
|
3363
3363
|
function summarizeToolInput(toolName, input) {
|
|
3364
|
-
if (!
|
|
3364
|
+
if (!isRecord2(input)) {
|
|
3365
3365
|
return void 0;
|
|
3366
3366
|
}
|
|
3367
3367
|
const pathValue = [
|
|
@@ -3443,7 +3443,7 @@ var require_run = __commonJS({
|
|
|
3443
3443
|
return "";
|
|
3444
3444
|
}
|
|
3445
3445
|
return content.flatMap((block) => {
|
|
3446
|
-
if (!
|
|
3446
|
+
if (!isRecord2(block) || block.type !== "text") {
|
|
3447
3447
|
return [];
|
|
3448
3448
|
}
|
|
3449
3449
|
return typeof block.text === "string" ? [block.text] : [];
|
|
@@ -3458,7 +3458,7 @@ var require_run = __commonJS({
|
|
|
3458
3458
|
return [];
|
|
3459
3459
|
}
|
|
3460
3460
|
return content.flatMap((block, index) => {
|
|
3461
|
-
if (!
|
|
3461
|
+
if (!isRecord2(block) || block.type !== "tool_use") {
|
|
3462
3462
|
return [];
|
|
3463
3463
|
}
|
|
3464
3464
|
const name = safeString(block.name);
|
|
@@ -3613,7 +3613,7 @@ var require_run = __commonJS({
|
|
|
3613
3613
|
}];
|
|
3614
3614
|
}
|
|
3615
3615
|
case "files_persisted": {
|
|
3616
|
-
const failed = Array.isArray(event.failed) ? event.failed.filter(
|
|
3616
|
+
const failed = Array.isArray(event.failed) ? event.failed.filter(isRecord2) : [];
|
|
3617
3617
|
if (failed.length === 0) {
|
|
3618
3618
|
const persistedCount = Array.isArray(event.files) ? event.files.length : 0;
|
|
3619
3619
|
return [{
|
|
@@ -3673,7 +3673,7 @@ var require_run = __commonJS({
|
|
|
3673
3673
|
return [];
|
|
3674
3674
|
const events = [];
|
|
3675
3675
|
for (const block of content) {
|
|
3676
|
-
if (!
|
|
3676
|
+
if (!isRecord2(block) || block.type !== "tool_result")
|
|
3677
3677
|
continue;
|
|
3678
3678
|
const toolUseId = safeString(block.tool_use_id);
|
|
3679
3679
|
if (!toolUseId)
|
|
@@ -3684,7 +3684,7 @@ var require_run = __commonJS({
|
|
|
3684
3684
|
output = blockContent;
|
|
3685
3685
|
} else if (Array.isArray(blockContent)) {
|
|
3686
3686
|
output = blockContent.flatMap((part) => {
|
|
3687
|
-
if (!
|
|
3687
|
+
if (!isRecord2(part))
|
|
3688
3688
|
return [];
|
|
3689
3689
|
if (typeof part.text === "string")
|
|
3690
3690
|
return [part.text];
|
|
@@ -3777,7 +3777,7 @@ var require_run = __commonJS({
|
|
|
3777
3777
|
return `<step${elapsed}>${escapeReasoningStepContent(content)}</step>`;
|
|
3778
3778
|
}
|
|
3779
3779
|
function normalizeToolInput(input) {
|
|
3780
|
-
if (
|
|
3780
|
+
if (isRecord2(input)) {
|
|
3781
3781
|
return input;
|
|
3782
3782
|
}
|
|
3783
3783
|
if (input === void 0) {
|
|
@@ -3870,7 +3870,7 @@ var require_run = __commonJS({
|
|
|
3870
3870
|
}
|
|
3871
3871
|
}
|
|
3872
3872
|
registerSubagentBucket(tool) {
|
|
3873
|
-
const input =
|
|
3873
|
+
const input = isRecord2(tool.input) ? tool.input : {};
|
|
3874
3874
|
const agentType = safeString(input.subagent_type) ?? safeString(input.agent_type) ?? "subagent";
|
|
3875
3875
|
const description = safeString(input.description) ?? "";
|
|
3876
3876
|
const prompt = safeString(input.prompt);
|
|
@@ -3889,7 +3889,7 @@ var require_run = __commonJS({
|
|
|
3889
3889
|
isSubagentTool(name, input) {
|
|
3890
3890
|
if (name === "Task" || name === "Agent")
|
|
3891
3891
|
return true;
|
|
3892
|
-
if (
|
|
3892
|
+
if (isRecord2(input) && typeof input.subagent_type === "string")
|
|
3893
3893
|
return true;
|
|
3894
3894
|
return false;
|
|
3895
3895
|
}
|
|
@@ -5472,7 +5472,7 @@ var require_setup = __commonJS({
|
|
|
5472
5472
|
function toObject(value) {
|
|
5473
5473
|
return value && typeof value === "object" ? value : null;
|
|
5474
5474
|
}
|
|
5475
|
-
function
|
|
5475
|
+
function stringValue2(value) {
|
|
5476
5476
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
5477
5477
|
}
|
|
5478
5478
|
function loadSshPublicKey(filePath) {
|
|
@@ -5555,9 +5555,9 @@ var require_setup = __commonJS({
|
|
|
5555
5555
|
const raw = toObject(credential?.raw);
|
|
5556
5556
|
const rawUser = toObject(raw?.user);
|
|
5557
5557
|
const user = {
|
|
5558
|
-
id:
|
|
5559
|
-
email:
|
|
5560
|
-
name:
|
|
5558
|
+
id: stringValue2(rawUser?.id),
|
|
5559
|
+
email: stringValue2(rawUser?.email) ?? fallbackEmail,
|
|
5560
|
+
name: stringValue2(rawUser?.name) ?? stringValue2(rawUser?.title) ?? stringValue2(rawUser?.email) ?? fallbackEmail
|
|
5561
5561
|
};
|
|
5562
5562
|
return user.id || user.email || user.name ? user : void 0;
|
|
5563
5563
|
}
|
|
@@ -6640,8 +6640,8 @@ var require_dist2 = __commonJS({
|
|
|
6640
6640
|
});
|
|
6641
6641
|
|
|
6642
6642
|
// src/program.ts
|
|
6643
|
-
var
|
|
6644
|
-
var
|
|
6643
|
+
var import_node_fs2 = require("node:fs");
|
|
6644
|
+
var import_node_path2 = require("node:path");
|
|
6645
6645
|
var import_tela_agent = __toESM(require_dist2());
|
|
6646
6646
|
|
|
6647
6647
|
// src/output.ts
|
|
@@ -6930,20 +6930,745 @@ function writeDoctorHelp(io) {
|
|
|
6930
6930
|
}
|
|
6931
6931
|
|
|
6932
6932
|
// src/commands/env.ts
|
|
6933
|
+
var import_tela_local_config6 = __toESM(require_dist());
|
|
6934
|
+
|
|
6935
|
+
// src/commands/ingredients.ts
|
|
6936
|
+
var import_node_fs = require("node:fs");
|
|
6937
|
+
var import_promises = require("node:fs/promises");
|
|
6938
|
+
var import_node_path = require("node:path");
|
|
6933
6939
|
var import_tela_local_config5 = __toESM(require_dist());
|
|
6940
|
+
var PANTRY_BASE_URL = "https://pantry-api.tela.tools";
|
|
6941
|
+
var INGREDIENT_PAGE_LIMIT = 100;
|
|
6942
|
+
var HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
6943
|
+
var FALLBACK_ENVIRONMENT_ORDER = ["tooling", "production", "staging", "development"];
|
|
6944
|
+
var PANTRY_INGREDIENT = {
|
|
6945
|
+
slug: "pantry",
|
|
6946
|
+
name: "Pantry",
|
|
6947
|
+
description: "Ingredient registry and API catalog",
|
|
6948
|
+
type: "api",
|
|
6949
|
+
status: "active",
|
|
6950
|
+
environments: [{ slug: "production", url: PANTRY_BASE_URL }]
|
|
6951
|
+
};
|
|
6952
|
+
async function runTelactlCommand(command, args, options, io) {
|
|
6953
|
+
try {
|
|
6954
|
+
if (command === "update") return updateIngredients(args, options, io);
|
|
6955
|
+
if (command === "api") return callIngredientApi(args, options, io);
|
|
6956
|
+
if (command === "docs") return runDocsCommand(args, options, io);
|
|
6957
|
+
if (command === "whoami") return whoami(args, options, io);
|
|
6958
|
+
if (command === "token") return tokenAlias(args, options, io);
|
|
6959
|
+
writeError(io, `Unknown telactl command: ${command}`);
|
|
6960
|
+
return 1;
|
|
6961
|
+
} catch (error) {
|
|
6962
|
+
writeError(io, error instanceof Error ? error.message : String(error));
|
|
6963
|
+
return 1;
|
|
6964
|
+
}
|
|
6965
|
+
}
|
|
6966
|
+
async function runIngredientEnvCommand(args, options, io) {
|
|
6967
|
+
const [action, ...rest] = args;
|
|
6968
|
+
if (action === "set") return setIngredientEnvironment(rest, io);
|
|
6969
|
+
if (action === "list" && rest.some((arg) => !arg.startsWith("-"))) return listIngredientEnvironments(rest, options, io);
|
|
6970
|
+
return null;
|
|
6971
|
+
}
|
|
6972
|
+
async function updateIngredients(args, options, io) {
|
|
6973
|
+
const flags = parseCommonFlags(args);
|
|
6974
|
+
if ("error" in flags) return usageError(flags.error, io);
|
|
6975
|
+
if (flags.rest.length > 0) return usageError(`Unknown update argument: ${flags.rest[0]}`, io);
|
|
6976
|
+
const session = await loadSession(flags.contextId);
|
|
6977
|
+
const cache = {
|
|
6978
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6979
|
+
ingredients: await fetchIngredientCatalog(requestHeaders(session.credential))
|
|
6980
|
+
};
|
|
6981
|
+
await writeCache(cache);
|
|
6982
|
+
if (options.json) {
|
|
6983
|
+
writeJson(io, {
|
|
6984
|
+
cache: {
|
|
6985
|
+
updatedAt: cache.updatedAt,
|
|
6986
|
+
ingredientCount: cache.ingredients.length,
|
|
6987
|
+
contextId: session.resolved.id,
|
|
6988
|
+
refreshed: session.refreshed
|
|
6989
|
+
}
|
|
6990
|
+
});
|
|
6991
|
+
return 0;
|
|
6992
|
+
}
|
|
6993
|
+
writeLine(io, `Cached ${cache.ingredients.length} ingredients`);
|
|
6994
|
+
return 0;
|
|
6995
|
+
}
|
|
6996
|
+
async function callIngredientApi(args, _options, io) {
|
|
6997
|
+
const flags = parseApiFlags(args);
|
|
6998
|
+
if ("error" in flags) return usageError(flags.error, io);
|
|
6999
|
+
const [slug, method, routePath, ...httpieArgs] = flags.rest;
|
|
7000
|
+
if (!slug || !method || !routePath) {
|
|
7001
|
+
return usageError("Usage: tela api <slug> <METHOD> <PATH> [args...] [--env <slug>] [--body <json>] [-p|--pretty]", io);
|
|
7002
|
+
}
|
|
7003
|
+
if (!HTTP_METHODS.has(method.toUpperCase())) return usageError(`Unsupported HTTP method: ${method}`, io);
|
|
7004
|
+
const ingredient = requireIngredient(slug, "api");
|
|
7005
|
+
const environment = resolveIngredientEnvironment(ingredient, flags.env);
|
|
7006
|
+
if (!environment) {
|
|
7007
|
+
return usageError(`Ingredient '${slug}' has no environments configured. Run \`tela update\` after Pantry is updated.`, io);
|
|
7008
|
+
}
|
|
7009
|
+
const headers = new Headers();
|
|
7010
|
+
const queryParams = [];
|
|
7011
|
+
const fields = {};
|
|
7012
|
+
let hasFields = false;
|
|
7013
|
+
for (const arg of httpieArgs) {
|
|
7014
|
+
const parsed = parseHttpieArg(arg);
|
|
7015
|
+
if (!parsed) return usageError(`Invalid argument '${arg}'. Use Key:Value, name=value, name==value, or name:=rawJson.`, io);
|
|
7016
|
+
if (parsed.kind === "query") {
|
|
7017
|
+
queryParams.push([parsed.key, parsed.value]);
|
|
7018
|
+
continue;
|
|
7019
|
+
}
|
|
7020
|
+
if (parsed.kind === "header") {
|
|
7021
|
+
headers.set(parsed.key.trim(), parsed.value.trim());
|
|
7022
|
+
continue;
|
|
7023
|
+
}
|
|
7024
|
+
if (parsed.kind === "field") {
|
|
7025
|
+
fields[parsed.key] = parsed.value;
|
|
7026
|
+
hasFields = true;
|
|
7027
|
+
continue;
|
|
7028
|
+
}
|
|
7029
|
+
try {
|
|
7030
|
+
fields[parsed.key] = JSON.parse(parsed.value);
|
|
7031
|
+
} catch {
|
|
7032
|
+
return usageError(`Invalid JSON for raw field '${parsed.key}': ${parsed.value}`, io);
|
|
7033
|
+
}
|
|
7034
|
+
hasFields = true;
|
|
7035
|
+
}
|
|
7036
|
+
const url = new URL(joinUrl(environment.url, routePath));
|
|
7037
|
+
for (const [key, value] of queryParams) url.searchParams.append(key, value);
|
|
7038
|
+
const session = await loadSession(flags.contextId);
|
|
7039
|
+
for (const [key, value] of Object.entries(requestHeaders(session.credential))) {
|
|
7040
|
+
if (!headers.has(key)) headers.set(key, value);
|
|
7041
|
+
}
|
|
7042
|
+
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
7043
|
+
let requestBody;
|
|
7044
|
+
if (flags.body !== void 0) {
|
|
7045
|
+
requestBody = flags.body;
|
|
7046
|
+
} else if (hasFields) {
|
|
7047
|
+
requestBody = JSON.stringify(fields);
|
|
7048
|
+
}
|
|
7049
|
+
if (requestBody !== void 0 && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
|
|
7050
|
+
const response = await fetch(url, {
|
|
7051
|
+
method: method.toUpperCase(),
|
|
7052
|
+
headers,
|
|
7053
|
+
body: requestBody
|
|
7054
|
+
});
|
|
7055
|
+
const text = await response.text();
|
|
7056
|
+
assertHttpOk(response, text);
|
|
7057
|
+
if (flags.pretty) {
|
|
7058
|
+
const parsed = tryParseJson(text);
|
|
7059
|
+
if (parsed !== null) {
|
|
7060
|
+
writeJson(io, parsed);
|
|
7061
|
+
return 0;
|
|
7062
|
+
}
|
|
7063
|
+
}
|
|
7064
|
+
writeLine(io, text);
|
|
7065
|
+
return 0;
|
|
7066
|
+
}
|
|
7067
|
+
async function runDocsCommand(args, options, io) {
|
|
7068
|
+
const [action, ...rest] = args;
|
|
7069
|
+
if (!action || action === "help" || action === "--help" || action === "-h") {
|
|
7070
|
+
writeDocsHelp(io);
|
|
7071
|
+
return 0;
|
|
7072
|
+
}
|
|
7073
|
+
if (action === "endpoints") return docsEndpoints(rest, options, io);
|
|
7074
|
+
if (action === "reference") return docsReference(rest, options, io);
|
|
7075
|
+
if (action === "guide") return docsGuide(rest, options, io);
|
|
7076
|
+
return usageError(`Unknown docs command: ${action}`, io);
|
|
7077
|
+
}
|
|
7078
|
+
async function docsEndpoints(args, options, io) {
|
|
7079
|
+
const flags = parseCommonFlags(args);
|
|
7080
|
+
if ("error" in flags) return usageError(flags.error, io);
|
|
7081
|
+
const [slug] = flags.rest;
|
|
7082
|
+
if (!slug) return usageError("Usage: tela docs endpoints <slug> [-p|--pretty]", io);
|
|
7083
|
+
requireIngredient(slug, "api");
|
|
7084
|
+
const spec = await fetchOpenApiSpec(slug, flags.contextId);
|
|
7085
|
+
const paths = isRecord(spec.paths) ? spec.paths : {};
|
|
7086
|
+
if (flags.pretty && !options.json) {
|
|
7087
|
+
writeLine(io, formatEndpointsTable(paths));
|
|
7088
|
+
return 0;
|
|
7089
|
+
}
|
|
7090
|
+
writeJson(io, paths);
|
|
7091
|
+
return 0;
|
|
7092
|
+
}
|
|
7093
|
+
async function docsReference(args, options, io) {
|
|
7094
|
+
const flags = parseCommonFlags(args);
|
|
7095
|
+
if ("error" in flags) return usageError(flags.error, io);
|
|
7096
|
+
const [slug, first, second] = flags.rest;
|
|
7097
|
+
if (!slug) return usageError("Usage: tela docs reference <slug> [METHOD PATH|file-path] [-p|--pretty]", io);
|
|
7098
|
+
const ingredient = requireIngredient(slug);
|
|
7099
|
+
if (ingredient.type === "api") {
|
|
7100
|
+
if (first && !second && HTTP_METHODS.has(first.toUpperCase())) {
|
|
7101
|
+
return usageError("Both METHOD and PATH are required to show a specific endpoint", io);
|
|
7102
|
+
}
|
|
7103
|
+
const spec = await fetchOpenApiSpec(slug, flags.contextId);
|
|
7104
|
+
if (first && second) {
|
|
7105
|
+
if (!HTTP_METHODS.has(first.toUpperCase())) return usageError(`Unsupported HTTP method: ${first}`, io);
|
|
7106
|
+
const pathEntry = isRecord(spec.paths) && isRecord(spec.paths[second]) ? spec.paths[second] : null;
|
|
7107
|
+
if (!pathEntry) return usageError(`Path not found: ${second}`, io);
|
|
7108
|
+
const op = pathEntry[first.toLowerCase()];
|
|
7109
|
+
if (!op) return usageError(`Method ${first.toUpperCase()} not found for ${second}`, io);
|
|
7110
|
+
if (flags.pretty && !options.json) {
|
|
7111
|
+
writeLine(io, formatEndpoint(spec, op, first, second));
|
|
7112
|
+
return 0;
|
|
7113
|
+
}
|
|
7114
|
+
writeJson(io, op);
|
|
7115
|
+
return 0;
|
|
7116
|
+
}
|
|
7117
|
+
if (flags.pretty && !options.json) {
|
|
7118
|
+
writeJson(io, spec);
|
|
7119
|
+
return 0;
|
|
7120
|
+
}
|
|
7121
|
+
writeLine(io, JSON.stringify(spec));
|
|
7122
|
+
return 0;
|
|
7123
|
+
}
|
|
7124
|
+
if (ingredient.type !== "library") return usageError(`Ingredient '${slug}' is not an API or library ingredient.`, io);
|
|
7125
|
+
const ingredientBody = await fetchIngredient(slug, flags.contextId);
|
|
7126
|
+
const apiRef = isRecord(ingredientBody.docs) ? stringValue(ingredientBody.docs.api) : "";
|
|
7127
|
+
if (!apiRef) return usageError(`Library ${slug} has no API reference`, io);
|
|
7128
|
+
const filePath = extractGitHubFilePath(apiRef);
|
|
7129
|
+
if (!filePath) return usageError(`Could not extract file path from API reference URL: ${apiRef}`, io);
|
|
7130
|
+
const slashIndex = filePath.lastIndexOf("/");
|
|
7131
|
+
const dir = slashIndex === -1 ? "" : filePath.slice(0, slashIndex + 1);
|
|
7132
|
+
const relativePath = first ? first.replace(/^\/+/, "") : "globals.md";
|
|
7133
|
+
const guide = await fetchGuide(slug, `${dir}${relativePath}`, flags.contextId);
|
|
7134
|
+
writeLine(io, stringValue(guide.content));
|
|
7135
|
+
return 0;
|
|
7136
|
+
}
|
|
7137
|
+
async function docsGuide(args, _options, io) {
|
|
7138
|
+
const flags = parseCommonFlags(args);
|
|
7139
|
+
if ("error" in flags) return usageError(flags.error, io);
|
|
7140
|
+
const [slug, providedPath] = flags.rest;
|
|
7141
|
+
if (!slug) return usageError("Usage: tela docs guide <slug> [path] [-p|--pretty]", io);
|
|
7142
|
+
const ingredient = requireIngredient(slug);
|
|
7143
|
+
if (ingredient.type !== "api" && ingredient.type !== "library") {
|
|
7144
|
+
return usageError(`Ingredient '${slug}' is not an API or library ingredient.`, io);
|
|
7145
|
+
}
|
|
7146
|
+
const ingredientBody = await fetchIngredient(slug, flags.contextId);
|
|
7147
|
+
const guidesRef = isRecord(ingredientBody.docs) ? stringValue(ingredientBody.docs.guides) : "";
|
|
7148
|
+
if (!guidesRef) return usageError(`Ingredient ${slug} has no guide`, io);
|
|
7149
|
+
const filePath = extractGitHubFilePath(guidesRef);
|
|
7150
|
+
if (!filePath) return usageError(`Could not extract file path from guide URL: ${guidesRef}`, io);
|
|
7151
|
+
const guide = await fetchGuide(slug, resolveGuidePath(filePath, providedPath), flags.contextId);
|
|
7152
|
+
writeLine(io, stringValue(guide.content));
|
|
7153
|
+
return 0;
|
|
7154
|
+
}
|
|
7155
|
+
async function listIngredientEnvironments(args, options, io) {
|
|
7156
|
+
const flags = parseCommonFlags(args);
|
|
7157
|
+
if ("error" in flags) return usageError(flags.error, io);
|
|
7158
|
+
const [slug] = flags.rest;
|
|
7159
|
+
if (!slug) return usageError("Usage: tela env list <ingredient-slug> [-p|--pretty|--json]", io);
|
|
7160
|
+
const ingredient = requireIngredient(slug);
|
|
7161
|
+
const environments = ingredient.environments ?? [];
|
|
7162
|
+
if (options.json || !flags.pretty) {
|
|
7163
|
+
writeJson(io, { environments });
|
|
7164
|
+
return 0;
|
|
7165
|
+
}
|
|
7166
|
+
if (environments.length === 0) {
|
|
7167
|
+
writeLine(io, "(no environments configured)");
|
|
7168
|
+
return 0;
|
|
7169
|
+
}
|
|
7170
|
+
const slugWidth = Math.max(...environments.map((environment) => environment.slug.length));
|
|
7171
|
+
for (const environment of environments) {
|
|
7172
|
+
writeLine(io, `${environment.slug.padEnd(slugWidth)} ${environment.url}`);
|
|
7173
|
+
}
|
|
7174
|
+
return 0;
|
|
7175
|
+
}
|
|
7176
|
+
async function setIngredientEnvironment(args, io) {
|
|
7177
|
+
const [slug, environment] = args;
|
|
7178
|
+
if (!slug || !environment) return usageError("Usage: tela env set <ingredient-slug> <environment>", io);
|
|
7179
|
+
const ingredient = requireIngredient(slug);
|
|
7180
|
+
if (!ingredient.environments.some((candidate) => candidate.slug === environment)) {
|
|
7181
|
+
const available = ingredient.environments.map((candidate) => candidate.slug).join(", ") || "(none)";
|
|
7182
|
+
return usageError(`Environment '${environment}' not found for '${slug}'. Available: ${available}`, io);
|
|
7183
|
+
}
|
|
7184
|
+
const defaults = await readEnvironmentDefaults();
|
|
7185
|
+
defaults[slug] = environment;
|
|
7186
|
+
await writeEnvironmentDefaults(defaults);
|
|
7187
|
+
writeLine(io, `Set default environment for '${slug}' to '${environment}'`);
|
|
7188
|
+
return 0;
|
|
7189
|
+
}
|
|
7190
|
+
async function whoami(args, _options, io) {
|
|
7191
|
+
const flags = parseCommonFlags(args);
|
|
7192
|
+
if ("error" in flags) return usageError(flags.error, io);
|
|
7193
|
+
if (flags.rest.length > 0) return usageError(`Unknown whoami argument: ${flags.rest[0]}`, io);
|
|
7194
|
+
const session = await loadSession(flags.contextId);
|
|
7195
|
+
if (!session.credential.accessToken) {
|
|
7196
|
+
return usageError(`Context ${session.resolved.id} does not have an access token credential.`, io);
|
|
7197
|
+
}
|
|
7198
|
+
const response = await fetch(new URL("/api/auth/applications/whoami", session.resolved.environment.authApiUrl), {
|
|
7199
|
+
headers: {
|
|
7200
|
+
Accept: "application/json",
|
|
7201
|
+
"x-tela-access-token": session.credential.accessToken
|
|
7202
|
+
}
|
|
7203
|
+
});
|
|
7204
|
+
const text = await response.text();
|
|
7205
|
+
assertHttpOk(response, text);
|
|
7206
|
+
if (flags.pretty) {
|
|
7207
|
+
const parsed = tryParseJson(text);
|
|
7208
|
+
if (parsed !== null) {
|
|
7209
|
+
writeJson(io, parsed);
|
|
7210
|
+
return 0;
|
|
7211
|
+
}
|
|
7212
|
+
}
|
|
7213
|
+
writeLine(io, text);
|
|
7214
|
+
return 0;
|
|
7215
|
+
}
|
|
7216
|
+
async function tokenAlias(args, _options, io) {
|
|
7217
|
+
const flags = parseCommonFlags(args);
|
|
7218
|
+
if ("error" in flags) return usageError(flags.error, io);
|
|
7219
|
+
if (flags.rest.length > 0) return usageError(`Unknown token argument: ${flags.rest[0]}`, io);
|
|
7220
|
+
const session = await loadSession(flags.contextId);
|
|
7221
|
+
if (!session.credential.accessToken) return usageError(`Context ${session.resolved.id} does not have an access token credential.`, io);
|
|
7222
|
+
writeLine(io, session.credential.accessToken);
|
|
7223
|
+
return 0;
|
|
7224
|
+
}
|
|
7225
|
+
async function fetchOpenApiSpec(slug, contextId) {
|
|
7226
|
+
const session = await loadSession(contextId);
|
|
7227
|
+
const response = await fetch(getPantryOpenapiUrl(slug), {
|
|
7228
|
+
headers: requestHeaders(session.credential)
|
|
7229
|
+
});
|
|
7230
|
+
const text = await response.text();
|
|
7231
|
+
assertHttpOk(response, text, "Failed to fetch OpenAPI spec");
|
|
7232
|
+
const parsed = JSON.parse(text);
|
|
7233
|
+
if (!isRecord(parsed)) throw new Error("OpenAPI response was not a JSON object");
|
|
7234
|
+
return parsed;
|
|
7235
|
+
}
|
|
7236
|
+
async function fetchIngredient(slug, contextId) {
|
|
7237
|
+
const session = await loadSession(contextId);
|
|
7238
|
+
const response = await fetch(getPantryIngredientUrl(slug), {
|
|
7239
|
+
headers: requestHeaders(session.credential)
|
|
7240
|
+
});
|
|
7241
|
+
const text = await response.text();
|
|
7242
|
+
assertHttpOk(response, text, "Failed to fetch ingredient");
|
|
7243
|
+
const parsed = JSON.parse(text);
|
|
7244
|
+
if (!isRecord(parsed)) throw new Error("Ingredient response was not a JSON object");
|
|
7245
|
+
return parsed;
|
|
7246
|
+
}
|
|
7247
|
+
async function fetchGuide(slug, filePath, contextId) {
|
|
7248
|
+
const session = await loadSession(contextId);
|
|
7249
|
+
const response = await fetch(getPantryGuideUrl(slug, filePath), {
|
|
7250
|
+
headers: requestHeaders(session.credential)
|
|
7251
|
+
});
|
|
7252
|
+
const text = await response.text();
|
|
7253
|
+
assertHttpOk(response, text, "Failed to fetch guide");
|
|
7254
|
+
const parsed = JSON.parse(text);
|
|
7255
|
+
if (!isRecord(parsed)) throw new Error("Guide response was not a JSON object");
|
|
7256
|
+
return parsed;
|
|
7257
|
+
}
|
|
7258
|
+
async function loadSession(contextId) {
|
|
7259
|
+
const config = await (0, import_tela_local_config5.loadConfig)();
|
|
7260
|
+
const resolved = (0, import_tela_local_config5.resolveContext)(config, { contextId });
|
|
7261
|
+
const credentials = createCliCredentialAdapter();
|
|
7262
|
+
const credentialRef = resolved.context.credentialRef;
|
|
7263
|
+
const originalCredential = await credentials.get(credentialRef);
|
|
7264
|
+
if (!originalCredential) throw new Error(`Missing credential for Tela context ${resolved.id}`);
|
|
7265
|
+
const credential = originalCredential.kind === "user-session" ? await (0, import_tela_local_config5.refreshUserSessionCredential)(resolved.environment, credentialRef, originalCredential, credentials) : originalCredential;
|
|
7266
|
+
return {
|
|
7267
|
+
resolved,
|
|
7268
|
+
credentialRef,
|
|
7269
|
+
credential,
|
|
7270
|
+
refreshed: credential !== originalCredential
|
|
7271
|
+
};
|
|
7272
|
+
}
|
|
7273
|
+
function requestHeaders(credential) {
|
|
7274
|
+
if (credential.kind === "data-token" && credential.dataToken) {
|
|
7275
|
+
return {
|
|
7276
|
+
Accept: "application/json",
|
|
7277
|
+
"x-data-token": credential.dataToken
|
|
7278
|
+
};
|
|
7279
|
+
}
|
|
7280
|
+
if (credential.accessToken) {
|
|
7281
|
+
return {
|
|
7282
|
+
Accept: "application/json",
|
|
7283
|
+
Authorization: `Bearer ${credential.accessToken}`
|
|
7284
|
+
};
|
|
7285
|
+
}
|
|
7286
|
+
throw new Error(`Credential ${credential.kind} does not contain usable token material`);
|
|
7287
|
+
}
|
|
7288
|
+
function requireIngredient(slug, type) {
|
|
7289
|
+
const ingredient = readModules().find((candidate) => candidate.slug === slug);
|
|
7290
|
+
if (!ingredient) throw new Error(`Ingredient '${slug}' not found. Run \`tela update\` first.`);
|
|
7291
|
+
if (type && ingredient.type !== type) throw new Error(`Ingredient '${slug}' is not a ${type} ingredient.`);
|
|
7292
|
+
return ingredient;
|
|
7293
|
+
}
|
|
7294
|
+
async function fetchIngredientCatalog(headers, fetchImpl = fetch) {
|
|
7295
|
+
const ingredients = [];
|
|
7296
|
+
let total = null;
|
|
7297
|
+
for (let offset = 0; ; offset += INGREDIENT_PAGE_LIMIT) {
|
|
7298
|
+
const url = new URL("/ingredients", PANTRY_BASE_URL);
|
|
7299
|
+
url.searchParams.set("limit", String(INGREDIENT_PAGE_LIMIT));
|
|
7300
|
+
url.searchParams.set("offset", String(offset));
|
|
7301
|
+
const response = await fetchImpl(url.toString(), { headers });
|
|
7302
|
+
const text = await response.text();
|
|
7303
|
+
assertHttpOk(response, text, "Failed to fetch ingredients");
|
|
7304
|
+
const data = JSON.parse(text);
|
|
7305
|
+
const rawPageIngredients = data.ingredients ?? [];
|
|
7306
|
+
const pageIngredients = rawPageIngredients.map(normalizeCachedIngredient).filter((ingredient) => ingredient.slug);
|
|
7307
|
+
ingredients.push(...pageIngredients);
|
|
7308
|
+
total = typeof data.total === "number" ? data.total : total;
|
|
7309
|
+
if (total !== null && ingredients.length >= total) break;
|
|
7310
|
+
if (rawPageIngredients.length < INGREDIENT_PAGE_LIMIT) break;
|
|
7311
|
+
}
|
|
7312
|
+
return ingredients;
|
|
7313
|
+
}
|
|
7314
|
+
function normalizeCachedIngredient(ingredient) {
|
|
7315
|
+
return {
|
|
7316
|
+
slug: stringValue(ingredient.slug),
|
|
7317
|
+
name: stringValue(ingredient.name),
|
|
7318
|
+
description: stringValue(ingredient.description),
|
|
7319
|
+
type: normalizeIngredientType(ingredient.type),
|
|
7320
|
+
status: stringValue(ingredient.status),
|
|
7321
|
+
environments: (ingredient.environments ?? []).map((environment) => ({
|
|
7322
|
+
slug: environment.slug,
|
|
7323
|
+
url: environment.url
|
|
7324
|
+
})).filter((environment) => environment.slug && environment.url)
|
|
7325
|
+
};
|
|
7326
|
+
}
|
|
7327
|
+
function readModules() {
|
|
7328
|
+
const cache = readCache();
|
|
7329
|
+
const ingredients = cache?.ingredients ?? [];
|
|
7330
|
+
return ingredients.some((ingredient) => ingredient.slug === PANTRY_INGREDIENT.slug) ? ingredients : [...ingredients, PANTRY_INGREDIENT];
|
|
7331
|
+
}
|
|
7332
|
+
function readCache() {
|
|
7333
|
+
try {
|
|
7334
|
+
const text = (0, import_node_fs.readFileSync)(cachePath(), "utf8");
|
|
7335
|
+
const parsed = JSON.parse(text);
|
|
7336
|
+
if (!Array.isArray(parsed.ingredients)) return null;
|
|
7337
|
+
return parsed;
|
|
7338
|
+
} catch {
|
|
7339
|
+
return null;
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
async function writeCache(cache) {
|
|
7343
|
+
const filePath = cachePath();
|
|
7344
|
+
await (0, import_promises.mkdir)((0, import_node_path.dirname)(filePath), { recursive: true });
|
|
7345
|
+
await (0, import_promises.writeFile)(filePath, `${JSON.stringify(cache, null, 2)}
|
|
7346
|
+
`, "utf8");
|
|
7347
|
+
}
|
|
7348
|
+
function cachePath() {
|
|
7349
|
+
return (0, import_node_path.join)((0, import_tela_local_config5.getTelaLocalPaths)().rootDir, "ingredients.json");
|
|
7350
|
+
}
|
|
7351
|
+
async function readEnvironmentDefaults() {
|
|
7352
|
+
try {
|
|
7353
|
+
return JSON.parse(await (0, import_promises.readFile)(environmentDefaultsPath(), "utf8"));
|
|
7354
|
+
} catch {
|
|
7355
|
+
return {};
|
|
7356
|
+
}
|
|
7357
|
+
}
|
|
7358
|
+
async function writeEnvironmentDefaults(defaults) {
|
|
7359
|
+
const filePath = environmentDefaultsPath();
|
|
7360
|
+
await (0, import_promises.mkdir)((0, import_node_path.dirname)(filePath), { recursive: true });
|
|
7361
|
+
await (0, import_promises.writeFile)(filePath, `${JSON.stringify(defaults, null, 2)}
|
|
7362
|
+
`, "utf8");
|
|
7363
|
+
}
|
|
7364
|
+
function environmentDefaultsPath() {
|
|
7365
|
+
return (0, import_node_path.join)((0, import_tela_local_config5.getTelaLocalPaths)().rootDir, "ingredient-environments.json");
|
|
7366
|
+
}
|
|
7367
|
+
function resolveIngredientEnvironment(ingredient, override) {
|
|
7368
|
+
const environments = ingredient.environments ?? [];
|
|
7369
|
+
if (override) {
|
|
7370
|
+
const found = environments.find((environment) => environment.slug === override);
|
|
7371
|
+
if (!found) {
|
|
7372
|
+
const available2 = environments.map((environment) => environment.slug).join(", ") || "(none)";
|
|
7373
|
+
throw new Error(`Environment '${override}' not found for ingredient '${ingredient.slug}'. Available: ${available2}`);
|
|
7374
|
+
}
|
|
7375
|
+
return found;
|
|
7376
|
+
}
|
|
7377
|
+
if (environments.length === 0) return null;
|
|
7378
|
+
if (environments.length === 1) return environments[0] ?? null;
|
|
7379
|
+
const defaults = readEnvironmentDefaultsSync();
|
|
7380
|
+
const saved = defaults[ingredient.slug];
|
|
7381
|
+
if (saved) {
|
|
7382
|
+
const found = environments.find((environment) => environment.slug === saved);
|
|
7383
|
+
if (found) return found;
|
|
7384
|
+
}
|
|
7385
|
+
for (const preferred of FALLBACK_ENVIRONMENT_ORDER) {
|
|
7386
|
+
const found = environments.find((environment) => environment.slug === preferred);
|
|
7387
|
+
if (found) return found;
|
|
7388
|
+
}
|
|
7389
|
+
const available = environments.map((environment) => environment.slug).join(", ");
|
|
7390
|
+
throw new Error(
|
|
7391
|
+
`Multiple environments configured for '${ingredient.slug}' and no default matches.
|
|
7392
|
+
Available: ${available}
|
|
7393
|
+
Use --env <slug> to pick one, or run \`tela env set ${ingredient.slug} <environment>\` to save a default.`
|
|
7394
|
+
);
|
|
7395
|
+
}
|
|
7396
|
+
function readEnvironmentDefaultsSync() {
|
|
7397
|
+
try {
|
|
7398
|
+
return JSON.parse((0, import_node_fs.readFileSync)(environmentDefaultsPath(), "utf8"));
|
|
7399
|
+
} catch {
|
|
7400
|
+
return {};
|
|
7401
|
+
}
|
|
7402
|
+
}
|
|
7403
|
+
function parseCommonFlags(args) {
|
|
7404
|
+
const flags = {
|
|
7405
|
+
pretty: false,
|
|
7406
|
+
rest: []
|
|
7407
|
+
};
|
|
7408
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
7409
|
+
const arg = args[index];
|
|
7410
|
+
if (arg === "--context") {
|
|
7411
|
+
const value = args[index + 1];
|
|
7412
|
+
if (!value) return { error: "Expected value after --context" };
|
|
7413
|
+
flags.contextId = value;
|
|
7414
|
+
index += 1;
|
|
7415
|
+
continue;
|
|
7416
|
+
}
|
|
7417
|
+
if (arg === "--pretty" || arg === "-p") {
|
|
7418
|
+
flags.pretty = true;
|
|
7419
|
+
continue;
|
|
7420
|
+
}
|
|
7421
|
+
flags.rest.push(arg);
|
|
7422
|
+
}
|
|
7423
|
+
return flags;
|
|
7424
|
+
}
|
|
7425
|
+
function parseApiFlags(args) {
|
|
7426
|
+
const flags = {
|
|
7427
|
+
pretty: false,
|
|
7428
|
+
rest: []
|
|
7429
|
+
};
|
|
7430
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
7431
|
+
const arg = args[index];
|
|
7432
|
+
if (arg === "--context") {
|
|
7433
|
+
const value = args[index + 1];
|
|
7434
|
+
if (!value) return { error: "Expected value after --context" };
|
|
7435
|
+
flags.contextId = value;
|
|
7436
|
+
index += 1;
|
|
7437
|
+
continue;
|
|
7438
|
+
}
|
|
7439
|
+
if (arg === "--env") {
|
|
7440
|
+
const value = args[index + 1];
|
|
7441
|
+
if (!value) return { error: "Expected value after --env" };
|
|
7442
|
+
flags.env = value;
|
|
7443
|
+
index += 1;
|
|
7444
|
+
continue;
|
|
7445
|
+
}
|
|
7446
|
+
if (arg === "--body") {
|
|
7447
|
+
const value = args[index + 1];
|
|
7448
|
+
if (value === void 0) return { error: "Expected value after --body" };
|
|
7449
|
+
flags.body = value;
|
|
7450
|
+
index += 1;
|
|
7451
|
+
continue;
|
|
7452
|
+
}
|
|
7453
|
+
if (arg === "--pretty" || arg === "-p") {
|
|
7454
|
+
flags.pretty = true;
|
|
7455
|
+
continue;
|
|
7456
|
+
}
|
|
7457
|
+
flags.rest.push(arg);
|
|
7458
|
+
}
|
|
7459
|
+
return flags;
|
|
7460
|
+
}
|
|
7461
|
+
function joinUrl(base, routePath) {
|
|
7462
|
+
const trimmedBase = base.replace(/\/$/, "");
|
|
7463
|
+
const prefixedPath = routePath.startsWith("/") ? routePath : `/${routePath}`;
|
|
7464
|
+
return `${trimmedBase}${prefixedPath}`;
|
|
7465
|
+
}
|
|
7466
|
+
function parseHttpieArg(arg) {
|
|
7467
|
+
for (let index = 0; index < arg.length; index += 1) {
|
|
7468
|
+
const two = arg.slice(index, index + 2);
|
|
7469
|
+
if (two === "==") return { kind: "query", key: arg.slice(0, index), value: arg.slice(index + 2) };
|
|
7470
|
+
if (two === ":=") return { kind: "rawField", key: arg.slice(0, index), value: arg.slice(index + 2) };
|
|
7471
|
+
if (arg[index] === ":") return { kind: "header", key: arg.slice(0, index), value: arg.slice(index + 1) };
|
|
7472
|
+
if (arg[index] === "=") return { kind: "field", key: arg.slice(0, index), value: arg.slice(index + 1) };
|
|
7473
|
+
}
|
|
7474
|
+
return null;
|
|
7475
|
+
}
|
|
7476
|
+
function getPantryOpenapiUrl(slug) {
|
|
7477
|
+
return slug === "pantry" ? `${PANTRY_BASE_URL}/openapi` : `${PANTRY_BASE_URL}/ingredients/${slug}/openapi`;
|
|
7478
|
+
}
|
|
7479
|
+
function getPantryIngredientUrl(slug) {
|
|
7480
|
+
return `${PANTRY_BASE_URL}/ingredients/${slug}`;
|
|
7481
|
+
}
|
|
7482
|
+
function getPantryGuideUrl(slug, filePath) {
|
|
7483
|
+
const base = `${PANTRY_BASE_URL}/ingredients/${slug}/guide`;
|
|
7484
|
+
if (!filePath) return base;
|
|
7485
|
+
const params = new URLSearchParams({ path: filePath });
|
|
7486
|
+
return `${base}?${params.toString()}`;
|
|
7487
|
+
}
|
|
7488
|
+
function formatEndpointsTable(paths) {
|
|
7489
|
+
const lines = [];
|
|
7490
|
+
for (const [routePath, methods] of Object.entries(paths)) {
|
|
7491
|
+
for (const [method, op] of Object.entries(methods)) {
|
|
7492
|
+
if (method.startsWith("x-")) continue;
|
|
7493
|
+
const summary = op.summary ?? op.operationId ?? "";
|
|
7494
|
+
lines.push(`${method.toUpperCase().padEnd(7)} ${routePath} ${summary}`);
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7497
|
+
return lines.join("\n");
|
|
7498
|
+
}
|
|
7499
|
+
function formatEndpoint(spec, op, method, routePath) {
|
|
7500
|
+
if (!isRecord(op)) return "";
|
|
7501
|
+
const lines = [`${method.toUpperCase()} ${routePath}`];
|
|
7502
|
+
if (typeof op.summary === "string") lines.push(`
|
|
7503
|
+
${op.summary}`);
|
|
7504
|
+
if (typeof op.description === "string" && op.description !== op.summary) lines.push(op.description);
|
|
7505
|
+
const parameters = Array.isArray(op.parameters) ? op.parameters : [];
|
|
7506
|
+
if (parameters.length > 0) {
|
|
7507
|
+
lines.push("\n## Parameters\n");
|
|
7508
|
+
for (const parameter of parameters) {
|
|
7509
|
+
const resolved = isRecord(parameter) && typeof parameter.$ref === "string" ? resolveRef(spec, parameter.$ref) : parameter;
|
|
7510
|
+
if (!isRecord(resolved)) continue;
|
|
7511
|
+
const required = resolved.required ? " (required)" : "";
|
|
7512
|
+
const description = typeof resolved.description === "string" ? ` - ${resolved.description}` : "";
|
|
7513
|
+
const schema = isRecord(resolved.schema) ? resolved.schema : {};
|
|
7514
|
+
const type = typeof schema.type === "string" ? schema.type : "";
|
|
7515
|
+
lines.push(` ${stringValue(resolved.in)} ${stringValue(resolved.name)}: ${type}${required}${description}`);
|
|
7516
|
+
}
|
|
7517
|
+
}
|
|
7518
|
+
if (isRecord(op.requestBody)) {
|
|
7519
|
+
lines.push("\n## Request Body\n");
|
|
7520
|
+
const content = isRecord(op.requestBody.content) ? op.requestBody.content : {};
|
|
7521
|
+
for (const [contentType, media] of Object.entries(content)) {
|
|
7522
|
+
if (!isRecord(media)) continue;
|
|
7523
|
+
lines.push(` Content-Type: ${contentType}`);
|
|
7524
|
+
const schema = resolveSchema(spec, media.schema);
|
|
7525
|
+
if (schema) lines.push(formatSchema(schema, 1));
|
|
7526
|
+
}
|
|
7527
|
+
}
|
|
7528
|
+
if (isRecord(op.responses)) {
|
|
7529
|
+
lines.push("\n## Responses\n");
|
|
7530
|
+
for (const [status, response] of Object.entries(op.responses)) {
|
|
7531
|
+
const resolved = isRecord(response) && typeof response.$ref === "string" ? resolveRef(spec, response.$ref) : response;
|
|
7532
|
+
if (!isRecord(resolved)) continue;
|
|
7533
|
+
lines.push(` ${status}: ${stringValue(resolved.description)}`);
|
|
7534
|
+
const content = isRecord(resolved.content) ? resolved.content : {};
|
|
7535
|
+
for (const media of Object.values(content)) {
|
|
7536
|
+
if (!isRecord(media)) continue;
|
|
7537
|
+
const schema = resolveSchema(spec, media.schema);
|
|
7538
|
+
if (schema) lines.push(formatSchema(schema, 2));
|
|
7539
|
+
}
|
|
7540
|
+
}
|
|
7541
|
+
}
|
|
7542
|
+
return lines.join("\n");
|
|
7543
|
+
}
|
|
7544
|
+
function resolveRef(spec, ref) {
|
|
7545
|
+
const parts = ref.replace("#/", "").split("/");
|
|
7546
|
+
let current = spec;
|
|
7547
|
+
for (const part of parts) {
|
|
7548
|
+
current = isRecord(current) ? current[part] : void 0;
|
|
7549
|
+
}
|
|
7550
|
+
return current;
|
|
7551
|
+
}
|
|
7552
|
+
function resolveSchema(spec, schema) {
|
|
7553
|
+
if (!isRecord(schema)) return null;
|
|
7554
|
+
if (typeof schema.$ref === "string") return resolveSchema(spec, resolveRef(spec, schema.$ref));
|
|
7555
|
+
if (schema.items) return { ...schema, items: resolveSchema(spec, schema.items) };
|
|
7556
|
+
if (isRecord(schema.properties)) {
|
|
7557
|
+
const resolved = {};
|
|
7558
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
7559
|
+
resolved[key] = resolveSchema(spec, value) ?? value;
|
|
7560
|
+
}
|
|
7561
|
+
return { ...schema, properties: resolved };
|
|
7562
|
+
}
|
|
7563
|
+
return schema;
|
|
7564
|
+
}
|
|
7565
|
+
function formatSchema(schema, indent = 0) {
|
|
7566
|
+
if (!schema) return "";
|
|
7567
|
+
const pad = " ".repeat(indent);
|
|
7568
|
+
if (schema.type === "object" && isRecord(schema.properties)) {
|
|
7569
|
+
const requiredFields = Array.isArray(schema.required) ? schema.required.filter((item) => typeof item === "string") : [];
|
|
7570
|
+
return Object.entries(schema.properties).map(([key, prop]) => {
|
|
7571
|
+
if (!isRecord(prop)) return `${pad}${key}: unknown`;
|
|
7572
|
+
const required = requiredFields.includes(key) ? "" : "?";
|
|
7573
|
+
const type = typeof prop.type === "string" ? prop.type : "object";
|
|
7574
|
+
const description = typeof prop.description === "string" ? ` - ${prop.description}` : "";
|
|
7575
|
+
if (prop.type === "object" && isRecord(prop.properties)) {
|
|
7576
|
+
return `${pad}${key}${required}: {${description}
|
|
7577
|
+
${formatSchema(prop, indent + 1)}
|
|
7578
|
+
${pad}}`;
|
|
7579
|
+
}
|
|
7580
|
+
if (prop.type === "array" && isRecord(prop.items)) {
|
|
7581
|
+
const itemType = typeof prop.items.type === "string" ? prop.items.type : "object";
|
|
7582
|
+
return `${pad}${key}${required}: ${itemType}[]${description}`;
|
|
7583
|
+
}
|
|
7584
|
+
return `${pad}${key}${required}: ${type}${description}`;
|
|
7585
|
+
}).join("\n");
|
|
7586
|
+
}
|
|
7587
|
+
return `${pad}${typeof schema.type === "string" ? schema.type : "unknown"}`;
|
|
7588
|
+
}
|
|
7589
|
+
function extractGitHubFilePath(url) {
|
|
7590
|
+
let parsed;
|
|
7591
|
+
try {
|
|
7592
|
+
parsed = new URL(url);
|
|
7593
|
+
} catch {
|
|
7594
|
+
return null;
|
|
7595
|
+
}
|
|
7596
|
+
const parts = parsed.pathname.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean).map((part) => decodeURIComponent(part));
|
|
7597
|
+
if (parsed.hostname === "github.com") {
|
|
7598
|
+
const [owner, repo, scope, ref, ...filePathParts] = parts;
|
|
7599
|
+
if (!owner || !repo || scope !== "blob" || !ref || filePathParts.length === 0) return null;
|
|
7600
|
+
return filePathParts.join("/");
|
|
7601
|
+
}
|
|
7602
|
+
if (parsed.hostname === "raw.githubusercontent.com") {
|
|
7603
|
+
const [owner, repo, firstRefPart, ...rest] = parts;
|
|
7604
|
+
if (!owner || !repo || !firstRefPart) return null;
|
|
7605
|
+
if (firstRefPart === "refs") {
|
|
7606
|
+
const [, , ...filePathParts] = rest;
|
|
7607
|
+
return filePathParts.length > 0 ? filePathParts.join("/") : null;
|
|
7608
|
+
}
|
|
7609
|
+
return rest.length > 0 ? rest.join("/") : null;
|
|
7610
|
+
}
|
|
7611
|
+
return null;
|
|
7612
|
+
}
|
|
7613
|
+
function resolveGuidePath(rootFilePath, providedPath) {
|
|
7614
|
+
const slashIndex = rootFilePath.lastIndexOf("/");
|
|
7615
|
+
const dir = slashIndex === -1 ? "" : rootFilePath.slice(0, slashIndex + 1);
|
|
7616
|
+
const basename = slashIndex === -1 ? rootFilePath : rootFilePath.slice(slashIndex + 1);
|
|
7617
|
+
const relativePath = providedPath ? providedPath.replace(/^\/+/, "") : basename;
|
|
7618
|
+
return `${dir}${relativePath}`;
|
|
7619
|
+
}
|
|
7620
|
+
function assertHttpOk(response, bodyText, context) {
|
|
7621
|
+
if (response.ok) return;
|
|
7622
|
+
const prefix = context ? `${context}: ` : "";
|
|
7623
|
+
throw new Error(`${prefix}${response.status} ${response.statusText}${bodyText ? `
|
|
7624
|
+
${bodyText}` : ""}`);
|
|
7625
|
+
}
|
|
7626
|
+
function usageError(message, io) {
|
|
7627
|
+
writeError(io, message);
|
|
7628
|
+
return 1;
|
|
7629
|
+
}
|
|
7630
|
+
function writeDocsHelp(io) {
|
|
7631
|
+
writeLine(io, "Usage: tela docs <command> [args]");
|
|
7632
|
+
writeLine(io);
|
|
7633
|
+
writeLine(io, "Commands:");
|
|
7634
|
+
writeLine(io, " docs endpoints <slug> [-p|--pretty] [--json]");
|
|
7635
|
+
writeLine(io, " docs reference <slug> [METHOD PATH|file-path] [-p|--pretty] [--json]");
|
|
7636
|
+
writeLine(io, " docs guide <slug> [path] [-p|--pretty]");
|
|
7637
|
+
}
|
|
7638
|
+
function tryParseJson(text) {
|
|
7639
|
+
try {
|
|
7640
|
+
return JSON.parse(text);
|
|
7641
|
+
} catch {
|
|
7642
|
+
return null;
|
|
7643
|
+
}
|
|
7644
|
+
}
|
|
7645
|
+
function isRecord(value) {
|
|
7646
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7647
|
+
}
|
|
7648
|
+
function stringValue(value) {
|
|
7649
|
+
return typeof value === "string" ? value : "";
|
|
7650
|
+
}
|
|
7651
|
+
function normalizeIngredientType(value) {
|
|
7652
|
+
if (value === "library" || value === "skill") return value;
|
|
7653
|
+
return "api";
|
|
7654
|
+
}
|
|
7655
|
+
|
|
7656
|
+
// src/commands/env.ts
|
|
6934
7657
|
async function runEnvCommand(args, options, io) {
|
|
6935
7658
|
const [action] = args;
|
|
6936
7659
|
if (!action || action === "help" || action === "--help" || action === "-h") {
|
|
6937
7660
|
writeEnvHelp(io);
|
|
6938
7661
|
return 0;
|
|
6939
7662
|
}
|
|
7663
|
+
const ingredientResult = await runIngredientEnvCommand(args, options, io);
|
|
7664
|
+
if (ingredientResult !== null) return ingredientResult;
|
|
6940
7665
|
if (action === "list") return listEnvironments(options, io);
|
|
6941
7666
|
writeError(io, `Unknown env command: ${action}`);
|
|
6942
7667
|
return 1;
|
|
6943
7668
|
}
|
|
6944
7669
|
async function listEnvironments(options, io) {
|
|
6945
|
-
const config = await (0,
|
|
6946
|
-
const publicConfig = await (0,
|
|
7670
|
+
const config = await (0, import_tela_local_config6.loadConfig)();
|
|
7671
|
+
const publicConfig = await (0, import_tela_local_config6.toPublicConfig)(config, createCliCredentialAdapter());
|
|
6947
7672
|
const environments = Object.values(publicConfig.environments);
|
|
6948
7673
|
if (options.json) {
|
|
6949
7674
|
writeJson(io, { environments });
|
|
@@ -6957,6 +7682,8 @@ async function listEnvironments(options, io) {
|
|
|
6957
7682
|
}
|
|
6958
7683
|
function writeEnvHelp(io) {
|
|
6959
7684
|
writeLine(io, "Usage: tela env list [--json]");
|
|
7685
|
+
writeLine(io, "Usage: tela env list <ingredient-slug> [-p|--pretty|--json]");
|
|
7686
|
+
writeLine(io, "Usage: tela env set <ingredient-slug> <environment>");
|
|
6960
7687
|
}
|
|
6961
7688
|
|
|
6962
7689
|
// src/program.ts
|
|
@@ -6976,6 +7703,9 @@ async function runCli(argv, io) {
|
|
|
6976
7703
|
if (command === "env") return runEnvCommand(rest, options, io);
|
|
6977
7704
|
if (command === "doctor") return runDoctorCommand(rest, options, io);
|
|
6978
7705
|
if (command === "agent") return (0, import_tela_agent.runAgentCli)(rest, io, options);
|
|
7706
|
+
if (command === "update" || command === "api" || command === "docs" || command === "whoami" || command === "token") {
|
|
7707
|
+
return runTelactlCommand(command, rest, options, io);
|
|
7708
|
+
}
|
|
6979
7709
|
writeError(io, `Unknown command: ${command}`);
|
|
6980
7710
|
return 1;
|
|
6981
7711
|
}
|
|
@@ -7005,7 +7735,16 @@ function writeHelp(io) {
|
|
|
7005
7735
|
writeLine(io, " context show <context-id> [--json]");
|
|
7006
7736
|
writeLine(io, " context use <context-id> [--json]");
|
|
7007
7737
|
writeLine(io, " env list [--json]");
|
|
7738
|
+
writeLine(io, " env list <ingredient-slug> [-p|--pretty|--json]");
|
|
7739
|
+
writeLine(io, " env set <ingredient-slug> <environment>");
|
|
7008
7740
|
writeLine(io, " doctor auth [--json]");
|
|
7741
|
+
writeLine(io, " update [--context <context-id>] [--json]");
|
|
7742
|
+
writeLine(io, " api <slug> <METHOD> <PATH> [args...] [--env <slug>] [--body <json>] [-p|--pretty]");
|
|
7743
|
+
writeLine(io, " docs endpoints <slug> [-p|--pretty] [--json]");
|
|
7744
|
+
writeLine(io, " docs reference <slug> [METHOD PATH|file-path] [-p|--pretty] [--json]");
|
|
7745
|
+
writeLine(io, " docs guide <slug> [path] [-p|--pretty]");
|
|
7746
|
+
writeLine(io, " whoami [--context <context-id>] [-p|--pretty]");
|
|
7747
|
+
writeLine(io, " token [--context <context-id>]");
|
|
7009
7748
|
writeLine(io, " version");
|
|
7010
7749
|
writeLine(io, " agent sync [repo] [--current] [--all] [--dry-run] [--json] [--verbose]");
|
|
7011
7750
|
writeLine(io, " agent run [--input <name=path>...] [--mode one-shot|chat] [--input-conflict fail|overwrite|rename] [--plain] [--json] [--verbose]");
|
|
@@ -7014,7 +7753,7 @@ function writeHelp(io) {
|
|
|
7014
7753
|
writeLine(io, " agent setup [--list-options] [--context <context-id>] [--organization <id-or-name>] [--clone-root <path>] [--ssh-public-key <path>] [--ssh-private-key <path>] [--dry-run] [--json]");
|
|
7015
7754
|
}
|
|
7016
7755
|
function getPackageVersion() {
|
|
7017
|
-
const metadata = JSON.parse((0,
|
|
7756
|
+
const metadata = JSON.parse((0, import_node_fs2.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8"));
|
|
7018
7757
|
return typeof metadata.version === "string" ? metadata.version : "0.0.0";
|
|
7019
7758
|
}
|
|
7020
7759
|
|
package/package.json
CHANGED
package/skills/tela-cli/SKILL.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tela-cli
|
|
3
|
-
description: Use when a coding agent needs to operate Tela Agent repositories through the local `tela` CLI with non-interactive, flag-driven commands and JSON output.
|
|
3
|
+
description: Use when a coding agent needs to operate Tela Agent repositories or Tela Pantry/service ingredients through the local `tela` CLI with non-interactive, flag-driven commands and JSON output.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Tela CLI
|
|
7
7
|
|
|
8
|
-
Use the local `tela` binary for Tela Agent repository workflows
|
|
9
|
-
flags and `--json` so commands are
|
|
8
|
+
Use the local `tela` binary for Tela Agent repository workflows and Pantry-backed
|
|
9
|
+
service/library discovery. Prefer explicit flags and `--json` so commands are
|
|
10
|
+
safe in non-interactive agent sessions.
|
|
10
11
|
|
|
11
12
|
## Subpaths
|
|
12
13
|
|
|
@@ -15,6 +16,28 @@ flags and `--json` so commands are safe in non-interactive agent sessions.
|
|
|
15
16
|
## Core Rules
|
|
16
17
|
|
|
17
18
|
- Use `tela agent ...` commands, not legacy top-level `sync`, `run`, `push`, or `feedback`.
|
|
19
|
+
- Run `tela update` before relying on ingredient slugs; it refreshes the local Pantry cache under `TELA_HOME`.
|
|
20
|
+
- Query Pantry before building a service integration from scratch: `tela api pantry GET /ingredients query==<term>`.
|
|
21
|
+
- Read endpoint or library docs before calling a service: `tela docs endpoints <slug>` then `tela docs reference <slug> <METHOD> <PATH>`.
|
|
18
22
|
- Prefer `--json` for machine-readable output and errors.
|
|
19
23
|
- Do not rely on prompts in coding-agent sessions; pass every required value by flag.
|
|
20
24
|
- Do not print token material. Use `tela auth token --json` for metadata and `--raw` only when a shell command explicitly needs the token.
|
|
25
|
+
|
|
26
|
+
## Pantry and Service Commands
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
tela update --json
|
|
30
|
+
tela api <slug> <METHOD> <PATH> [args...] --env <slug> --body <json> --pretty
|
|
31
|
+
tela docs endpoints <slug> --pretty
|
|
32
|
+
tela docs reference <slug> [METHOD PATH|file-path] --pretty
|
|
33
|
+
tela docs guide <slug> [path]
|
|
34
|
+
tela env list <ingredient-slug> --pretty
|
|
35
|
+
tela env set <ingredient-slug> <environment>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
HTTPie-style args for `tela api`:
|
|
39
|
+
|
|
40
|
+
- `name=value` sends a JSON string field.
|
|
41
|
+
- `name:=value` sends a raw JSON field.
|
|
42
|
+
- `name==value` sends a query parameter.
|
|
43
|
+
- `Header:value` sends a request header.
|