@tarileo/avileo-catalog-cli 0.3.1 → 0.3.2
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 +41 -10
- package/dist/index.js +103 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,24 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
The Avileo tenant-agent command line interface for a registered business with effective catalog-agent access.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The origin-pinning contract below describes this checkout. A package version in
|
|
6
|
+
`package.json` does not prove npm availability: `0.3.2` has not been published
|
|
7
|
+
by this release closure. Public handoff must wait for the operator's verified
|
|
8
|
+
package version/artifact and an activated destination. Never substitute an older
|
|
9
|
+
CLI that lacks these flags or silently pair with the integrated deployment.
|
|
10
|
+
|
|
11
|
+
For an authorized local-source session, build the workspace SDK first and run
|
|
12
|
+
the CLI from the repository root:
|
|
6
13
|
|
|
7
14
|
```sh
|
|
8
|
-
|
|
15
|
+
bun run --filter @tarileo/avileo-sdk build
|
|
16
|
+
bun apps/avileo/agent-cli/src/index.ts auth login --base-url <origin> --json
|
|
9
17
|
```
|
|
10
18
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
Hereafter `avileo` means that same verified CLI command prefix. Pass only the
|
|
20
|
+
explicit API origin supplied by the operator; package source and API destination
|
|
21
|
+
are independent decisions.
|
|
22
|
+
|
|
23
|
+
The CLI prints only the verification URI, user code, status and pinned
|
|
24
|
+
`baseUrl`, opens the link with a safe executable when available, polls until
|
|
25
|
+
approval, and stores the new Agent Key locally with `0600` permissions. The key
|
|
26
|
+
is never printed or placed in the URL. Use `auth status --json` to inspect the
|
|
27
|
+
source (`environment` or `local`) and the pinned origin without revealing the
|
|
28
|
+
key, and `auth logout --json` to remove only the local credential; remote
|
|
29
|
+
revocation remains an Avileo action.
|
|
30
|
+
|
|
31
|
+
Origins must use HTTPS, except plain HTTP for local development hosts
|
|
32
|
+
(loopback, private or Tailscale addresses). Without `--base-url` or
|
|
33
|
+
`AVILEO_AGENT_BASE_URL`, the CLI pairs with production
|
|
34
|
+
(`https://avileo.theelena.me`) and pins it. If the pinned origin fails, fix the
|
|
35
|
+
target; the CLI never falls back to another origin on its own.
|
|
17
36
|
|
|
18
37
|
If Avileo has already issued an Agent Key, configure it without putting the
|
|
19
38
|
secret in shell history, process arguments or JSON:
|
|
20
39
|
|
|
21
40
|
```sh
|
|
22
|
-
avileo auth configure --json
|
|
41
|
+
avileo auth configure --base-url <origin> --json
|
|
23
42
|
```
|
|
24
43
|
|
|
25
44
|
The command reads the key without echoing it and stores it locally. For CI/CD
|
|
@@ -27,7 +46,19 @@ and automated agents, continue injecting `AVILEO_AGENT_API_KEY`.
|
|
|
27
46
|
Environment credentials always take priority over the local credential file.
|
|
28
47
|
Never pass an Agent Key through flags, prompts or JSON input.
|
|
29
48
|
|
|
30
|
-
`AVILEO_AGENT_BASE_URL
|
|
49
|
+
`--base-url` wins over `AVILEO_AGENT_BASE_URL`, which remains the advanced
|
|
50
|
+
development/test override: set it before `auth login` or `auth configure` so
|
|
51
|
+
the origin is pinned with the credential. Commands reject
|
|
52
|
+
`CLI_ORIGIN_CONFLICT` when the variable points to a different origin than the
|
|
53
|
+
pinned credential — remove the override or re-authenticate with
|
|
54
|
+
`auth login --base-url <origin>`; an existing key is never sent to another
|
|
55
|
+
host. Invalid or unsafe origins fail with `CLI_ORIGIN_UNSUPPORTED`.
|
|
56
|
+
|
|
57
|
+
Local credentials created before origin pinning must be authorized again with
|
|
58
|
+
`auth login --base-url <origin>`. A stored key without a bound origin fails
|
|
59
|
+
with `CLI_CONFIGURATION_ERROR` before any request; the CLI cannot infer its
|
|
60
|
+
destination safely and does not migrate it to production or an environment
|
|
61
|
+
override.
|
|
31
62
|
|
|
32
63
|
Use `--from <json-file>` for structured mutation input. Direct mutations validate
|
|
33
64
|
locally by default; pass `--apply` only after reviewing the result. The CLI is
|
package/dist/index.js
CHANGED
|
@@ -15670,6 +15670,53 @@ import { chmodSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSy
|
|
|
15670
15670
|
import { randomUUID } from "crypto";
|
|
15671
15671
|
import { homedir } from "os";
|
|
15672
15672
|
import { dirname, join } from "path";
|
|
15673
|
+
import { isIP } from "net";
|
|
15674
|
+
var LOCAL_IPV4_HOST_PATTERN = /^(?:127\.|10\.|192\.168\.|169\.254\.|172\.(?:1[6-9]|2\d|3[01])\.|100\.(?:6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.)/;
|
|
15675
|
+
function isLocalHostname(hostname3) {
|
|
15676
|
+
const host = hostname3.replace(/^\[|\]$/g, "").toLowerCase();
|
|
15677
|
+
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) return true;
|
|
15678
|
+
if (host === "::1" || host === "::" || /^f[cd][0-9a-f]{2}:/.test(host)) return true;
|
|
15679
|
+
if (host.includes(":")) return false;
|
|
15680
|
+
if (!host.includes(".")) return true;
|
|
15681
|
+
return isIP(host) === 4 && LOCAL_IPV4_HOST_PATTERN.test(host);
|
|
15682
|
+
}
|
|
15683
|
+
function normalizeApiBaseUrl(rawUrl) {
|
|
15684
|
+
const candidate = rawUrl.trim().replace(/\/+$/, "");
|
|
15685
|
+
if (!candidate) {
|
|
15686
|
+
throw new CliOriginUnsupportedError("La URL base de la API est\xE1 vac\xEDa");
|
|
15687
|
+
}
|
|
15688
|
+
let parsed;
|
|
15689
|
+
try {
|
|
15690
|
+
parsed = new URL(candidate);
|
|
15691
|
+
} catch {
|
|
15692
|
+
throw new CliOriginUnsupportedError(`La URL base de la API no es v\xE1lida: ${candidate}`);
|
|
15693
|
+
}
|
|
15694
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
15695
|
+
throw new CliOriginUnsupportedError(
|
|
15696
|
+
`La URL base debe usar HTTPS (o HTTP local de desarrollo); se recibi\xF3 ${parsed.protocol}//`
|
|
15697
|
+
);
|
|
15698
|
+
}
|
|
15699
|
+
if (parsed.protocol === "http:" && !isLocalHostname(parsed.hostname)) {
|
|
15700
|
+
throw new CliOriginUnsupportedError(
|
|
15701
|
+
"Solo se permite HTTP para hosts locales de desarrollo; usa un origen HTTPS"
|
|
15702
|
+
);
|
|
15703
|
+
}
|
|
15704
|
+
if (parsed.username || parsed.password) {
|
|
15705
|
+
throw new CliOriginUnsupportedError("La URL base no debe incluir credenciales embebidas");
|
|
15706
|
+
}
|
|
15707
|
+
if (parsed.search || parsed.hash) {
|
|
15708
|
+
throw new CliOriginUnsupportedError("La URL base no debe incluir query ni fragmento");
|
|
15709
|
+
}
|
|
15710
|
+
return `${parsed.protocol}//${parsed.host}${parsed.pathname.replace(/\/+$/, "")}`;
|
|
15711
|
+
}
|
|
15712
|
+
function apiOrigin(baseUrl) {
|
|
15713
|
+
const parsed = new URL(baseUrl);
|
|
15714
|
+
return `${parsed.protocol}//${parsed.host}`;
|
|
15715
|
+
}
|
|
15716
|
+
function overrideBaseUrl(env) {
|
|
15717
|
+
const raw = env.AVILEO_AGENT_BASE_URL?.trim();
|
|
15718
|
+
return raw ? normalizeApiBaseUrl(raw) : void 0;
|
|
15719
|
+
}
|
|
15673
15720
|
var AGENT_KEY_PATTERN = /^avileo_sk_[^\s]+$/;
|
|
15674
15721
|
function normalizeAgentKey(value, message) {
|
|
15675
15722
|
const key = value.trim();
|
|
@@ -15700,10 +15747,15 @@ function readLocalCredential(env = process.env) {
|
|
|
15700
15747
|
throw new CliConfigurationError("La credencial local no es una Agent Key v\xE1lida");
|
|
15701
15748
|
}
|
|
15702
15749
|
const apiKey = normalizeAgentKey(record2.apiKey, "La credencial local no es una Agent Key v\xE1lida");
|
|
15703
|
-
|
|
15704
|
-
|
|
15750
|
+
if (typeof record2.baseUrl !== "string" || !record2.baseUrl.trim()) {
|
|
15751
|
+
throw new CliConfigurationError(
|
|
15752
|
+
"La credencial local no tiene un origen vinculado. Vuelve a autorizar con auth login --base-url <URL_DEL_PORTAL> antes de usarla."
|
|
15753
|
+
);
|
|
15754
|
+
}
|
|
15755
|
+
return { apiKey, baseUrl: normalizeApiBaseUrl(record2.baseUrl) };
|
|
15705
15756
|
}
|
|
15706
15757
|
function resolveCredential(env = process.env) {
|
|
15758
|
+
const override = overrideBaseUrl(env);
|
|
15707
15759
|
const environmentValue = env.AVILEO_AGENT_API_KEY;
|
|
15708
15760
|
if (environmentValue !== void 0) {
|
|
15709
15761
|
return {
|
|
@@ -15712,31 +15764,29 @@ function resolveCredential(env = process.env) {
|
|
|
15712
15764
|
"AVILEO_AGENT_API_KEY no es una Agent Key v\xE1lida"
|
|
15713
15765
|
),
|
|
15714
15766
|
source: "environment",
|
|
15715
|
-
|
|
15767
|
+
baseUrl: override ?? AVILEO_AGENT_API_URL
|
|
15716
15768
|
};
|
|
15717
15769
|
}
|
|
15718
15770
|
const local = readLocalCredential(env);
|
|
15719
15771
|
if (!local) return null;
|
|
15720
|
-
|
|
15721
|
-
|
|
15722
|
-
|
|
15723
|
-
|
|
15724
|
-
}
|
|
15772
|
+
if (override && apiOrigin(local.baseUrl) !== apiOrigin(override)) {
|
|
15773
|
+
throw new CliOriginConflictError(
|
|
15774
|
+
`La credencial local est\xE1 vinculada al origen ${apiOrigin(local.baseUrl)}, pero AVILEO_AGENT_BASE_URL apunta a ${apiOrigin(override)}. La clave nunca se env\xEDa a otro origen: quita la variable de entorno o vuelve a autenticar con auth login --base-url ${apiOrigin(override)} para emitir una clave en ese origen.`
|
|
15775
|
+
);
|
|
15776
|
+
}
|
|
15777
|
+
return { ...local, source: "local" };
|
|
15725
15778
|
}
|
|
15726
15779
|
function saveLocalCredential(credential, env = process.env) {
|
|
15727
15780
|
const apiKey = normalizeAgentKey(
|
|
15728
15781
|
credential.apiKey,
|
|
15729
15782
|
"Solo se pueden guardar Agent Keys de Avileo"
|
|
15730
15783
|
);
|
|
15784
|
+
const baseUrl = normalizeApiBaseUrl(credential.baseUrl);
|
|
15731
15785
|
const path = localCredentialPath(env);
|
|
15732
15786
|
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
15733
15787
|
const temporaryPath = `${path}.${randomUUID()}.tmp`;
|
|
15734
|
-
writeFileSync(
|
|
15735
|
-
|
|
15736
|
-
`${JSON.stringify({ apiKey, ...credential.baseUrl ? { baseUrl: credential.baseUrl } : {} })}
|
|
15737
|
-
`,
|
|
15738
|
-
{ mode: 384 }
|
|
15739
|
-
);
|
|
15788
|
+
writeFileSync(temporaryPath, `${JSON.stringify({ apiKey, baseUrl })}
|
|
15789
|
+
`, { mode: 384 });
|
|
15740
15790
|
chmodSync(temporaryPath, 384);
|
|
15741
15791
|
renameSync(temporaryPath, path);
|
|
15742
15792
|
chmodSync(path, 384);
|
|
@@ -15760,7 +15810,7 @@ function createClientFromEnvironment({
|
|
|
15760
15810
|
}
|
|
15761
15811
|
return clientFactory({
|
|
15762
15812
|
apiKey: credential.apiKey,
|
|
15763
|
-
|
|
15813
|
+
baseUrl: credential.baseUrl
|
|
15764
15814
|
});
|
|
15765
15815
|
}
|
|
15766
15816
|
var CliConfigurationError = class extends Error {
|
|
@@ -15770,6 +15820,20 @@ var CliConfigurationError = class extends Error {
|
|
|
15770
15820
|
this.name = "CliConfigurationError";
|
|
15771
15821
|
}
|
|
15772
15822
|
};
|
|
15823
|
+
var CliOriginUnsupportedError = class extends CliConfigurationError {
|
|
15824
|
+
code = "CLI_ORIGIN_UNSUPPORTED";
|
|
15825
|
+
constructor(message) {
|
|
15826
|
+
super(message);
|
|
15827
|
+
this.name = "CliOriginUnsupportedError";
|
|
15828
|
+
}
|
|
15829
|
+
};
|
|
15830
|
+
var CliOriginConflictError = class extends CliConfigurationError {
|
|
15831
|
+
code = "CLI_ORIGIN_CONFLICT";
|
|
15832
|
+
constructor(message) {
|
|
15833
|
+
super(message);
|
|
15834
|
+
this.name = "CliOriginConflictError";
|
|
15835
|
+
}
|
|
15836
|
+
};
|
|
15773
15837
|
|
|
15774
15838
|
// src/output.ts
|
|
15775
15839
|
var CliProtocolError = class extends Error {
|
|
@@ -16426,9 +16490,15 @@ async function openVerificationUri(uri) {
|
|
|
16426
16490
|
}
|
|
16427
16491
|
return false;
|
|
16428
16492
|
}
|
|
16493
|
+
function resolveSelectedBaseUrl(options) {
|
|
16494
|
+
const explicit = options.baseUrl?.trim();
|
|
16495
|
+
if (explicit) return normalizeApiBaseUrl(explicit);
|
|
16496
|
+
const fromEnvironment = options.env.AVILEO_AGENT_BASE_URL?.trim();
|
|
16497
|
+
return fromEnvironment ? normalizeApiBaseUrl(fromEnvironment) : AVILEO_AGENT_API_URL;
|
|
16498
|
+
}
|
|
16429
16499
|
async function login(options) {
|
|
16430
|
-
const baseUrl = options
|
|
16431
|
-
const deviceAuth = options.deviceAuthFactory(
|
|
16500
|
+
const baseUrl = resolveSelectedBaseUrl(options);
|
|
16501
|
+
const deviceAuth = options.deviceAuthFactory({ baseUrl });
|
|
16432
16502
|
const start = await deviceAuth.start({ name: "Avileo CLI" });
|
|
16433
16503
|
const browserOpened = await options.openBrowser(start.verificationUri);
|
|
16434
16504
|
process.stderr.write(
|
|
@@ -16445,7 +16515,7 @@ async function login(options) {
|
|
|
16445
16515
|
continue;
|
|
16446
16516
|
}
|
|
16447
16517
|
if (result.status === "denied" || result.status === "expired") {
|
|
16448
|
-
emitSuccess(safeFinalResult(start, browserOpened, result));
|
|
16518
|
+
emitSuccess({ ...safeFinalResult(start, browserOpened, result), baseUrl });
|
|
16449
16519
|
return;
|
|
16450
16520
|
}
|
|
16451
16521
|
if (!result.agentKey || !result.keyId || !result.businessId) {
|
|
@@ -16454,30 +16524,20 @@ async function login(options) {
|
|
|
16454
16524
|
"La autorizaci\xF3n aprobada no devolvi\xF3 una credencial v\xE1lida"
|
|
16455
16525
|
);
|
|
16456
16526
|
}
|
|
16457
|
-
saveLocalCredential(
|
|
16458
|
-
|
|
16459
|
-
apiKey: result.agentKey,
|
|
16460
|
-
...baseUrl ? { baseUrl } : {}
|
|
16461
|
-
},
|
|
16462
|
-
options.env
|
|
16463
|
-
);
|
|
16464
|
-
emitSuccess(safeFinalResult(start, browserOpened, result));
|
|
16527
|
+
saveLocalCredential({ apiKey: result.agentKey, baseUrl }, options.env);
|
|
16528
|
+
emitSuccess({ ...safeFinalResult(start, browserOpened, result), baseUrl });
|
|
16465
16529
|
return;
|
|
16466
16530
|
}
|
|
16467
16531
|
throw new CliProtocolError("CLI_AUTH_TIMEOUT", "La autorizaci\xF3n expir\xF3 antes de ser aprobada");
|
|
16468
16532
|
}
|
|
16469
16533
|
async function configure(options) {
|
|
16534
|
+
const baseUrl = resolveSelectedBaseUrl(options);
|
|
16470
16535
|
const apiKey = await options.readSecret("Agent Key de Avileo (no se mostrar\xE1): ");
|
|
16471
|
-
saveLocalCredential(
|
|
16472
|
-
{
|
|
16473
|
-
apiKey,
|
|
16474
|
-
...options.env.AVILEO_AGENT_BASE_URL?.trim() ? { baseUrl: options.env.AVILEO_AGENT_BASE_URL.trim() } : {}
|
|
16475
|
-
},
|
|
16476
|
-
options.env
|
|
16477
|
-
);
|
|
16536
|
+
saveLocalCredential({ apiKey, baseUrl }, options.env);
|
|
16478
16537
|
emitSuccess({
|
|
16479
16538
|
configured: true,
|
|
16480
16539
|
source: "local",
|
|
16540
|
+
baseUrl,
|
|
16481
16541
|
credentialPath: localCredentialPath(options.env)
|
|
16482
16542
|
});
|
|
16483
16543
|
}
|
|
@@ -16498,7 +16558,7 @@ async function status(options) {
|
|
|
16498
16558
|
emitSuccess({
|
|
16499
16559
|
configured: true,
|
|
16500
16560
|
source: credential.source,
|
|
16501
|
-
|
|
16561
|
+
baseUrl: credential.baseUrl ?? AVILEO_AGENT_API_URL,
|
|
16502
16562
|
business,
|
|
16503
16563
|
...businessLookupError ? { businessLookupError } : {}
|
|
16504
16564
|
});
|
|
@@ -16514,8 +16574,15 @@ async function logout(options) {
|
|
|
16514
16574
|
}
|
|
16515
16575
|
function registerAuthCommands(root, options) {
|
|
16516
16576
|
const auth = root.command("auth").description("Conecta la CLI con Avileo sin copiar claves en el chat");
|
|
16517
|
-
auth.command("configure").description("Guarda una Agent Key existente sin mostrarla en pantalla").
|
|
16518
|
-
|
|
16577
|
+
auth.command("configure").description("Guarda una Agent Key existente sin mostrarla en pantalla").option("--base-url <url>", "Origen de la API donde se usar\xE1 la clave").action(
|
|
16578
|
+
(commandOptions) => configure({ ...options, baseUrl: commandOptions.baseUrl })
|
|
16579
|
+
);
|
|
16580
|
+
auth.command("login").description("Autoriza esta CLI mediante un link temporal").option(
|
|
16581
|
+
"--base-url <url>",
|
|
16582
|
+
"Origen de la API a vincular; tiene prioridad sobre AVILEO_AGENT_BASE_URL"
|
|
16583
|
+
).action(
|
|
16584
|
+
(commandOptions) => login({ ...options, baseUrl: commandOptions.baseUrl })
|
|
16585
|
+
);
|
|
16519
16586
|
auth.command("status").description("Muestra el estado de la credencial sin revelar secretos").action(() => status(options));
|
|
16520
16587
|
auth.command("logout").description("Elimina solamente la credencial local").action(() => logout(options));
|
|
16521
16588
|
}
|