@knowttl/artium-axi 0.0.0 → 0.0.3
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 +48 -6
- package/bin/artium-axi.mjs +274 -69
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ See the [evidence ledger](docs/evidence-ledger.md) for the acceptance boundaries
|
|
|
20
20
|
## Agent Ergonomics
|
|
21
21
|
|
|
22
22
|
- Running without arguments shows identity, roles, available tools, cached health and pinned versions.
|
|
23
|
-
- Tool lists
|
|
23
|
+
- Tool lists show cached health, pinned versions and write-access status and suggest a detail view and first call.
|
|
24
24
|
- Empty lists explain why no enabled tool is available and direct you to an administrator.
|
|
25
25
|
- Local help works before sign-in and includes usage, purpose, flags and an example.
|
|
26
26
|
- Artium errors use TOON on stdout; upstream calls preserve separate stdout, stderr and exit status.
|
|
@@ -369,6 +369,7 @@ After signing in, run `node dist/cli/artium-axi.mjs setup --host codex` to insta
|
|
|
369
369
|
The root package remains private for server development; `pnpm build:npm` stages the separate public client package in `dist/npm` using the root package version.
|
|
370
370
|
Run `npm pack --dry-run ./dist/npm` to inspect the client tarball before release.
|
|
371
371
|
Only the bundled CLI, skill, README, LICENSE, NOTICE and client package metadata are included.
|
|
372
|
+
See [npm client releases](docs/runbook.md#npm-client-releases) for automated publishing and owner setup.
|
|
372
373
|
|
|
373
374
|
## Usage
|
|
374
375
|
|
|
@@ -377,6 +378,7 @@ Only the bundled CLI, skill, README, LICENSE, NOTICE and client package metadata
|
|
|
377
378
|
| Command | Purpose |
|
|
378
379
|
| --- | --- |
|
|
379
380
|
| `artium-axi` | Show identity, roles and available tools. |
|
|
381
|
+
| `artium-axi --version` | Print only the client package version and a newline, then exit without configuration, sign-in or network access. |
|
|
380
382
|
| `artium-axi login --username synthetic-user` | Sign in with a local account. |
|
|
381
383
|
| `artium-axi login --sso` | Sign in through browser approval with an enabled SSO provider; works on headless and SSH hosts. |
|
|
382
384
|
| `artium-axi passwd` | Change your password, then sign in again. |
|
|
@@ -411,9 +413,8 @@ Other Azure commands, including the dashboard and `home`, leave stdin unread; he
|
|
|
411
413
|
Local upload paths become leaf names in the request; duplicate leaf names and oversized input are refused.
|
|
412
414
|
The API supports bounded stdin and named uploads through the [shared API contract](src/shared/api.ts).
|
|
413
415
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
Falcon has no editable native write settings.
|
|
416
|
+
See [write-access administration](docs/runbook.md#roles-credentials-and-health) for per-tool switches, native write gates and the current Falcon limitation.
|
|
417
|
+
Artium does not add a universal confirmation flag.
|
|
417
418
|
|
|
418
419
|
Azure writes whose outcome is unknown are not retried and return `UNKNOWN_OUTCOME` with state-check guidance; reads may still retry.
|
|
419
420
|
Artium also returns this error when its runner times out, cancels or receives a process signal after starting an executed Azure write; read timeouts remain `TIMEOUT`.
|
|
@@ -659,11 +660,12 @@ See the [API contract](src/shared/api.ts) and [retention and export runbook](doc
|
|
|
659
660
|
|
|
660
661
|
Use `login --password-stdin` or `passwd --password-stdin` to supply the login or new password through stdin.
|
|
661
662
|
Stdin removes one final LF or CRLF and preserves all other spaces and Unicode.
|
|
662
|
-
Password arguments are refused; tokens are never printed.
|
|
663
|
+
Password arguments are refused; tokens are never printed to stdout or stderr.
|
|
663
664
|
Ordinary password changes also require the current password through the terminal prompt or `ARTIUM_AXI_CURRENT_PASSWORD`.
|
|
664
665
|
Clear password environment values after use.
|
|
665
666
|
|
|
666
|
-
|
|
667
|
+
By default, sessions live in `artium-axi/session.json` under the applicable base directory and are bound to the normalized server URL.
|
|
668
|
+
Callers that manage their own tokens can instead use [externally managed client sessions](#externally-managed-client-sessions).
|
|
667
669
|
POSIX directories use mode 0700 and files mode 0600; Windows uses a restricted ACL and refuses storage if it cannot establish it.
|
|
668
670
|
Do not export or share the session file.
|
|
669
671
|
CLI processes serialize refreshes through `session.lock`; after `SESSION_BUSY`, remove that lock directory only when every CLI process has exited.
|
|
@@ -674,6 +676,46 @@ Certificate verification remains enforced even with `NODE_TLS_REJECT_UNAUTHORIZE
|
|
|
674
676
|
Only an explicit pre-execution `TOKEN_EXPIRED` response permits one serialized refresh and retry.
|
|
675
677
|
A network failure or uncertain command outcome is never retried automatically.
|
|
676
678
|
|
|
679
|
+
### Externally managed client sessions
|
|
680
|
+
|
|
681
|
+
Callers that keep credentials outside the CLI can opt in with one leading flag: `artium-axi --managed-session <command> <args>`.
|
|
682
|
+
The caller supplies inherited pipes or sockets: fd 3 for session input and fd 4 for private rotation output.
|
|
683
|
+
Stdin remains available for tool input; stdout and stderr retain their normal command formats.
|
|
684
|
+
Do not pass tokens in arguments or environment variables, connect fd 4 to a log or public output, or redirect either private channel to a disk file.
|
|
685
|
+
The CLI refuses regular files as private channels and never reads, creates, locks, updates or deletes `session.json` in this mode.
|
|
686
|
+
|
|
687
|
+
Send one UTF-8 JSON object of at most 64 KiB to fd 3, then close the input pipe:
|
|
688
|
+
|
|
689
|
+
```json
|
|
690
|
+
{"serverUrl":"https://artium.example.test","accessToken":"CANARY-access","refreshToken":"CANARY-refresh"}
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
`serverUrl` must match the normalized `ARTIUM_AXI_URL`, allowing a final slash.
|
|
694
|
+
`accessToken` is required and nonempty; `refreshToken` is optional and nonempty when supplied.
|
|
695
|
+
The optional fields `status: "authenticated"`, `accessExpiresAt` and `familyExpiresAt` (integer Unix milliseconds) allow a rotation record to be reused as input; other fields are refused.
|
|
696
|
+
The caller owns token acquisition, expiry policy, refresh serialization across processes and secure persistence.
|
|
697
|
+
Managed mode supports the home view, `whoami`, `tools`, `tools show`, `doctor` and tool commands.
|
|
698
|
+
`login`, `passwd`, `logout` and `setup` are refused before any network call; ordinary sign-in and its server-version warning remain unchanged without the flag.
|
|
699
|
+
Help is available without private channels through `artium-axi --help`.
|
|
700
|
+
|
|
701
|
+
Only an explicit `TOKEN_EXPIRED` response permits a single refresh and retry of that request.
|
|
702
|
+
Without a refresh token, or if refresh is refused, the CLI returns the authentication error for the caller to handle.
|
|
703
|
+
Network failures and uncertain outcomes are never automatically retried.
|
|
704
|
+
After a successful refresh, fd 4 receives one newline-terminated JSON object before the request is retried:
|
|
705
|
+
|
|
706
|
+
```json
|
|
707
|
+
{"serverUrl":"https://artium.example.test","status":"authenticated","accessToken":"CANARY-new-access","refreshToken":"CANARY-new-refresh","accessExpiresAt":1800000000000,"familyExpiresAt":1800003600000}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
Every rotation is delivered even if the subsequent request fails; an invocation without rotation writes no record.
|
|
711
|
+
Consume fd 4 concurrently with stdout and stderr, and retain rotations regardless of the command's exit status.
|
|
712
|
+
If rotation delivery fails, the CLI refuses to retry the request and returns `INVALID_INPUT` (exit 2); the caller must recover the session rather than reuse the consumed refresh token.
|
|
713
|
+
Managed public output is buffered and scanned against supplied and rotated tokens and their canonical encodings before printing; a match withholds both streams and returns `SECRET_IN_OUTPUT`.
|
|
714
|
+
|
|
715
|
+
Exit codes are 0 for success, 2 for invalid input, client configuration or private channels and refused commands, and 1 for authentication, server-binding, transport or output-scanning failures.
|
|
716
|
+
Tool exit codes pass through as usual.
|
|
717
|
+
Missing or closed descriptors, invalid JSON and mismatched server bindings fail before network access; no failure falls back to a session file.
|
|
718
|
+
|
|
677
719
|
### Server settings and custody
|
|
678
720
|
|
|
679
721
|
| Variable | Meaning |
|
package/bin/artium-axi.mjs
CHANGED
|
@@ -4498,9 +4498,9 @@ var $ZodTransform = /* @__PURE__ */ $constructor("$ZodTransform", (inst, def) =>
|
|
|
4498
4498
|
}
|
|
4499
4499
|
const _out = def.transform(payload.value, payload);
|
|
4500
4500
|
if (ctx.async) {
|
|
4501
|
-
const
|
|
4502
|
-
return
|
|
4503
|
-
payload.value =
|
|
4501
|
+
const output3 = _out instanceof Promise ? _out : Promise.resolve(_out);
|
|
4502
|
+
return output3.then((output4) => {
|
|
4503
|
+
payload.value = output4;
|
|
4504
4504
|
return payload;
|
|
4505
4505
|
});
|
|
4506
4506
|
}
|
|
@@ -4950,12 +4950,12 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
|
|
|
4950
4950
|
output: inst._def.output
|
|
4951
4951
|
});
|
|
4952
4952
|
};
|
|
4953
|
-
inst.output = (
|
|
4953
|
+
inst.output = (output3) => {
|
|
4954
4954
|
const F = inst.constructor;
|
|
4955
4955
|
return new F({
|
|
4956
4956
|
type: "function",
|
|
4957
4957
|
input: inst._def.input,
|
|
4958
|
-
output:
|
|
4958
|
+
output: output3
|
|
4959
4959
|
});
|
|
4960
4960
|
};
|
|
4961
4961
|
return inst;
|
|
@@ -18364,14 +18364,14 @@ var ZodTransform = /* @__PURE__ */ $constructor("ZodTransform", (inst, def) => {
|
|
|
18364
18364
|
payload.issues.push(util_exports.issue(_issue));
|
|
18365
18365
|
}
|
|
18366
18366
|
};
|
|
18367
|
-
const
|
|
18368
|
-
if (
|
|
18369
|
-
return
|
|
18370
|
-
payload.value =
|
|
18367
|
+
const output3 = def.transform(payload.value, payload);
|
|
18368
|
+
if (output3 instanceof Promise) {
|
|
18369
|
+
return output3.then((output4) => {
|
|
18370
|
+
payload.value = output4;
|
|
18371
18371
|
return payload;
|
|
18372
18372
|
});
|
|
18373
18373
|
}
|
|
18374
|
-
payload.value =
|
|
18374
|
+
payload.value = output3;
|
|
18375
18375
|
return payload;
|
|
18376
18376
|
};
|
|
18377
18377
|
});
|
|
@@ -20048,7 +20048,7 @@ var nativeSettingsSchemas = {
|
|
|
20048
20048
|
allowWrites: external_exports.boolean(),
|
|
20049
20049
|
operations: external_exports.array(external_exports.string().min(1).max(256)).min(1).max(64)
|
|
20050
20050
|
}).optional() }),
|
|
20051
|
-
"falcon-axi": external_exports.strictObject({}),
|
|
20051
|
+
"falcon-axi": external_exports.strictObject({ allowWrites: external_exports.boolean().optional() }),
|
|
20052
20052
|
"vectra-axi": external_exports.strictObject({ writes: external_exports.strictObject({
|
|
20053
20053
|
allowWrites: external_exports.boolean(),
|
|
20054
20054
|
operations: external_exports.array(external_exports.string().min(1).refine((value) => value.trim().length > 0)).min(1)
|
|
@@ -20063,6 +20063,7 @@ var nativeSettingsSchemas = {
|
|
|
20063
20063
|
};
|
|
20064
20064
|
var healthSchema = external_exports.strictObject({
|
|
20065
20065
|
status: external_exports.enum(["unconfigured", "unknown", "ok", "error", "disabled"]),
|
|
20066
|
+
readOnly: external_exports.boolean().default(true),
|
|
20066
20067
|
checkedAt: external_exports.number().int().nullable(),
|
|
20067
20068
|
credentialRevision: external_exports.number().int().nullable(),
|
|
20068
20069
|
errorCode: external_exports.enum(Object.keys(errors)).nullable()
|
|
@@ -20071,6 +20072,7 @@ var adminToolSchema = external_exports.strictObject({
|
|
|
20071
20072
|
name: toolSchema,
|
|
20072
20073
|
enabled: external_exports.boolean(),
|
|
20073
20074
|
version: external_exports.string(),
|
|
20075
|
+
readOnly: external_exports.boolean().default(true),
|
|
20074
20076
|
nativeSettings: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
20075
20077
|
revision: external_exports.number().int().positive(),
|
|
20076
20078
|
updatedAt: external_exports.number().int().nullable(),
|
|
@@ -20394,6 +20396,7 @@ var toolsResponseSchema = external_exports.strictObject({
|
|
|
20394
20396
|
tools: external_exports.array(external_exports.strictObject({
|
|
20395
20397
|
name: toolSchema,
|
|
20396
20398
|
version: external_exports.string(),
|
|
20399
|
+
readOnly: external_exports.boolean().default(true),
|
|
20397
20400
|
status: external_exports.enum(["unconfigured", "unknown", "ok", "error", "disabled"])
|
|
20398
20401
|
}))
|
|
20399
20402
|
});
|
|
@@ -20449,7 +20452,7 @@ import { rootCertificates } from "node:tls";
|
|
|
20449
20452
|
// package.json
|
|
20450
20453
|
var package_default = {
|
|
20451
20454
|
name: "artium-axi",
|
|
20452
|
-
version: "0.0.
|
|
20455
|
+
version: "0.0.3",
|
|
20453
20456
|
private: true,
|
|
20454
20457
|
license: "MIT",
|
|
20455
20458
|
type: "module",
|
|
@@ -20633,20 +20636,27 @@ var SessionStore = class {
|
|
|
20633
20636
|
// src/cli/http.ts
|
|
20634
20637
|
var DEFAULT_REQUEST_DEADLINE_MS = 9e4;
|
|
20635
20638
|
var Client = class {
|
|
20636
|
-
constructor(environment = process.env) {
|
|
20639
|
+
constructor(environment = process.env, managed2) {
|
|
20637
20640
|
this.environment = environment;
|
|
20641
|
+
this.managed = managed2;
|
|
20638
20642
|
try {
|
|
20639
20643
|
const url2 = new URL(environment.ARTIUM_AXI_URL);
|
|
20640
20644
|
if (url2.username || url2.password || url2.href.includes("?") || url2.href.includes("#") || url2.protocol !== "https:" && !(url2.protocol === "http:" && environment.ARTIUM_AXI_TEST_HTTP === "1" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname))) throw new Error();
|
|
20641
20645
|
this.serverUrl = url2.href.replace(/\/$/, "");
|
|
20642
|
-
this.
|
|
20646
|
+
if (!managed2) this.storedSessions = new SessionStore(this.serverUrl, sessionPath(process.platform, environment));
|
|
20643
20647
|
} catch {
|
|
20644
20648
|
throw new ArtiumError("CLIENT_CONFIG_INVALID");
|
|
20645
20649
|
}
|
|
20650
|
+
managed2?.checkServer(this.serverUrl);
|
|
20646
20651
|
}
|
|
20647
20652
|
environment;
|
|
20653
|
+
managed;
|
|
20648
20654
|
serverUrl;
|
|
20649
|
-
|
|
20655
|
+
storedSessions;
|
|
20656
|
+
get sessions() {
|
|
20657
|
+
if (!this.storedSessions) throw new ArtiumError("INVALID_INPUT", "Session-file operations are unavailable in managed mode.");
|
|
20658
|
+
return this.storedSessions;
|
|
20659
|
+
}
|
|
20650
20660
|
certificateVerified = false;
|
|
20651
20661
|
async request(path, method, input2, token) {
|
|
20652
20662
|
this.certificateVerified = false;
|
|
@@ -20722,6 +20732,10 @@ var Client = class {
|
|
|
20722
20732
|
return result.body;
|
|
20723
20733
|
}
|
|
20724
20734
|
async authenticated(path, method, input2, previous) {
|
|
20735
|
+
if (this.managed) return this.managed.authenticated(
|
|
20736
|
+
(token) => this.request(path, method, input2, token),
|
|
20737
|
+
(refreshToken) => this.request("/v1/auth/refresh", "POST", { refreshToken })
|
|
20738
|
+
);
|
|
20725
20739
|
const session = previous ? this.reload(previous) : this.sessions.read();
|
|
20726
20740
|
if (!session) throw new ArtiumError("TOKEN_INVALID", void 0, "Run artium-axi login --username <username> --password-stdin.");
|
|
20727
20741
|
if (session.status === "password-change-required") throw new ArtiumError("PASSWORD_CHANGE_REQUIRED");
|
|
@@ -20805,16 +20819,18 @@ async function readPassword(stdin, environmentVariable) {
|
|
|
20805
20819
|
}
|
|
20806
20820
|
|
|
20807
20821
|
// src/cli/doctor.ts
|
|
20808
|
-
async function doctor() {
|
|
20822
|
+
async function doctor(createClient = () => new Client(), write = (text2) => {
|
|
20823
|
+
process.stdout.write(text2);
|
|
20824
|
+
}) {
|
|
20809
20825
|
let failure2;
|
|
20810
20826
|
const line = (name, pass, detail, error62) => {
|
|
20811
20827
|
if (!pass) failure2 ??= error62 ?? new ArtiumError("INVALID_RESPONSE");
|
|
20812
|
-
|
|
20828
|
+
write(`${pass ? "pass" : "fail"}: ${name}${detail ? ` (${detail})` : ""}
|
|
20813
20829
|
`);
|
|
20814
20830
|
};
|
|
20815
20831
|
let client;
|
|
20816
20832
|
try {
|
|
20817
|
-
client =
|
|
20833
|
+
client = createClient();
|
|
20818
20834
|
} catch (error62) {
|
|
20819
20835
|
for (const name of ["server address", "certificate", "token", "CLI version"]) line(name, false, "CLIENT_CONFIG_INVALID");
|
|
20820
20836
|
throw error62;
|
|
@@ -22830,7 +22846,7 @@ var ldap_axi_default = {
|
|
|
22830
22846
|
|
|
22831
22847
|
// src/cli/help.ts
|
|
22832
22848
|
var commands = {
|
|
22833
|
-
home: { usage: "artium-axi [tools [show <tool>]|whoami|login|passwd|logout|doctor|setup|<tool> <args>]", purpose: "Show your identity, roles and available tools.", flags: [], example: "artium-axi" },
|
|
22849
|
+
home: { usage: "artium-axi [tools [show <tool>]|whoami|login|passwd|logout|doctor|setup|<tool> <args>]", purpose: "Show your identity, roles and available tools.", flags: ["--version (default false; print the client package version and exit)"], example: "artium-axi" },
|
|
22834
22850
|
setup: {
|
|
22835
22851
|
usage: "artium-axi setup --host <claude|codex|opencode|pi> [--hook] [--force]",
|
|
22836
22852
|
purpose: "Install skills for your signed-in account\u2019s granted tools and an optional Claude Code session hook.",
|
|
@@ -22875,7 +22891,13 @@ var commands = {
|
|
|
22875
22891
|
};
|
|
22876
22892
|
function commandHelp(command) {
|
|
22877
22893
|
const entry = commands[command];
|
|
22878
|
-
const flags = [
|
|
22894
|
+
const flags = [
|
|
22895
|
+
command === "falcon-axi" || command === "vectra-axi" || command === "ldap-axi" ? "--help before the tool name (default false)" : "--help (default false)",
|
|
22896
|
+
...entry.flags,
|
|
22897
|
+
"--managed-session before the command (default false; authenticated calls only; session JSON on inherited pipe fd 3, rotations as JSON lines on private pipe fd 4; no session file)",
|
|
22898
|
+
"Managed input: {serverUrl, accessToken, refreshToken?}; close fd 3 after JSON (64 KiB maximum). Output: {serverUrl, status, accessToken, refreshToken, accessExpiresAt, familyExpiresAt} per rotation, before retry, including on later failure.",
|
|
22899
|
+
"Managed exits: 0 success; 2 invalid JSON/descriptors or refused login/passwd/logout/setup; 1 authentication/transport failure; tool exit codes pass through. Only TOKEN_EXPIRED permits one refresh/retry; without refreshToken or on refusal, return control to the caller."
|
|
22900
|
+
];
|
|
22879
22901
|
return `usage: ${entry.usage}
|
|
22880
22902
|
purpose: ${entry.purpose}
|
|
22881
22903
|
flags[${flags.length}]:
|
|
@@ -33710,25 +33732,25 @@ function rewriteDiscoveryHelp(help, tool) {
|
|
|
33710
33732
|
examples: help.examples.map((example) => hostedHelp(example, tool, flags, true))
|
|
33711
33733
|
});
|
|
33712
33734
|
}
|
|
33713
|
-
function rewriteSuggestions(
|
|
33714
|
-
if (truncated) return
|
|
33735
|
+
function rewriteSuggestions(output3, tool, truncated = false) {
|
|
33736
|
+
if (truncated) return output3;
|
|
33715
33737
|
const flags = managedFlags[tool];
|
|
33716
|
-
if (!flags) return
|
|
33738
|
+
if (!flags) return output3;
|
|
33717
33739
|
const executable = new RegExp(`^(?:artium-axi |npx -y @knowttl/)?${tool}(?=\\s|$)`);
|
|
33718
|
-
if (executable.test(
|
|
33740
|
+
if (executable.test(output3.replace(/^[ \t]*(?:(?:Command|Native operation|Examples): )?/, "")) || tool === "ldap-axi" && ldapHelp.test(output3)) return hostedHelp(output3, tool, flags, true);
|
|
33719
33741
|
let spans;
|
|
33720
33742
|
let unknownOutcome = false;
|
|
33721
33743
|
try {
|
|
33722
|
-
const json2 =
|
|
33723
|
-
let parsed = json2 ? jsonHelp(
|
|
33744
|
+
const json2 = output3.trimStart().startsWith("{");
|
|
33745
|
+
let parsed = json2 ? jsonHelp(output3) : toonHelp(output3);
|
|
33724
33746
|
if (tool === "ldap-axi" && parsed.document.description === "Read Active Directory and LDAPv3 directories safely with ldap-axi (read-only)" && Array.isArray(parsed.document.commands) && parsed.spans.length) {
|
|
33725
33747
|
const home = {
|
|
33726
33748
|
...parsed.document,
|
|
33727
33749
|
commands: parsed.document.commands.filter((command) => ldapCommands.includes(command)),
|
|
33728
33750
|
help: [...parsed.spans.map((span) => span.value).filter((value) => !/^(?:Authentication auto selects|Set LDAP_AXI_URL)/.test(value) && value !== ldapCredentialGuidance), ldapCredentialGuidance]
|
|
33729
33751
|
};
|
|
33730
|
-
|
|
33731
|
-
parsed = json2 ? jsonHelp(
|
|
33752
|
+
output3 = (json2 ? JSON.stringify(home) : encode3(home)) + "\n";
|
|
33753
|
+
parsed = json2 ? jsonHelp(output3) : toonHelp(output3);
|
|
33732
33754
|
}
|
|
33733
33755
|
const { spans: suggestions, document } = parsed;
|
|
33734
33756
|
spans = suggestions;
|
|
@@ -33736,15 +33758,15 @@ function rewriteSuggestions(output2, tool, truncated = false) {
|
|
|
33736
33758
|
unknownOutcome = document?.code === "UNKNOWN_OUTCOME" || document?.error?.code === "UNKNOWN_OUTCOME";
|
|
33737
33759
|
}
|
|
33738
33760
|
} catch {
|
|
33739
|
-
return
|
|
33761
|
+
return output3;
|
|
33740
33762
|
}
|
|
33741
33763
|
for (const span of [...spans].reverse()) {
|
|
33742
33764
|
const value = hostedHelp(span.value, tool, flags, /^[ \t]*(?:Command|Native operation|Examples):/m.test(span.value));
|
|
33743
33765
|
if (value === span.value) continue;
|
|
33744
|
-
|
|
33766
|
+
output3 = output3.slice(0, span.start) + (span.quoted || tool === "ldap-axi" && /[,\r\n]/.test(value) ? JSON.stringify(value) : value) + output3.slice(span.start + span.raw.length);
|
|
33745
33767
|
}
|
|
33746
|
-
spans = (
|
|
33747
|
-
if (spans.some((span) => /\\[`'"]/.test(span.value))) return
|
|
33768
|
+
spans = (output3.trimStart().startsWith("{") ? jsonHelp(output3) : toonHelp(output3)).spans;
|
|
33769
|
+
if (spans.some((span) => /\\[`'"]/.test(span.value))) return output3;
|
|
33748
33770
|
const insertions = /* @__PURE__ */ new Map();
|
|
33749
33771
|
const removals = /* @__PURE__ */ new Map();
|
|
33750
33772
|
for (const span of spans) {
|
|
@@ -33778,28 +33800,28 @@ function rewriteSuggestions(output2, tool, truncated = false) {
|
|
|
33778
33800
|
if (!quote2 && /^(?:artium-axi |npx -y @knowttl\/)?\S+-axi(?:\s|$)/.test(command)) commandRanges.push([match.index, match.index + match[0].length]);
|
|
33779
33801
|
const wrapped = command.startsWith(`artium-axi ${tool}`);
|
|
33780
33802
|
if (!executable.test(quote2 ? command.split(quote2)[0] : command)) continue;
|
|
33781
|
-
if (quote2 && !command.includes(quote2)) return
|
|
33803
|
+
if (quote2 && !command.includes(quote2)) return output3;
|
|
33782
33804
|
const start = match.index + match[0].length - command.length;
|
|
33783
33805
|
commandRanges.push([match.index, quote2 ? start + command.indexOf(quote2) + 1 : match.index + match[0].length]);
|
|
33784
33806
|
commandEdit2(start, quote2 ? start + command.indexOf(quote2) : start + command.length, wrapped);
|
|
33785
33807
|
}
|
|
33786
33808
|
for (const match of span.value.matchAll(/(?<![^\s(])([`'"])([^\n]*?)(\1|(?=\n|$))/g)) {
|
|
33787
33809
|
if (commandRanges.some(([start, end]) => match.index >= start && match.index < end)) continue;
|
|
33788
|
-
if (!/^(?:artium-axi |npx -y @knowttl\/)?[^\s`'"]+-axi(?:\s|$)/.test(match[2])) return
|
|
33810
|
+
if (!/^(?:artium-axi |npx -y @knowttl\/)?[^\s`'"]+-axi(?:\s|$)/.test(match[2])) return output3;
|
|
33789
33811
|
const preceding = span.value.slice(0, match.index);
|
|
33790
33812
|
if (/(?:^|\n)[ \t]*Valid flags for $/.test(preceding) && match[1] === "`" && match[3] === "`") continue;
|
|
33791
|
-
if (!/(?:^|\n|\S[`'"])[ \t]*(?:(?:Run|Use|run|use|Verify with|If you're unsure, run|Check current state before trying again:|or(?:, if you're unsure, run)?)[ \t]+)?$/.test(preceding)) return
|
|
33813
|
+
if (!/(?:^|\n|\S[`'"])[ \t]*(?:(?:Run|Use|run|use|Verify with|If you're unsure, run|Check current state before trying again:|or(?:, if you're unsure, run)?)[ \t]+)?$/.test(preceding)) return output3;
|
|
33792
33814
|
const wrapped = match[2].startsWith(`artium-axi ${tool}`);
|
|
33793
33815
|
if (executable.test(match[2])) {
|
|
33794
|
-
if (!match[3]) return
|
|
33816
|
+
if (!match[3]) return output3;
|
|
33795
33817
|
commandEdit2(match.index + 1, match.index + 1 + match[2].length, wrapped);
|
|
33796
33818
|
}
|
|
33797
33819
|
}
|
|
33798
33820
|
}
|
|
33799
33821
|
for (const offset of [.../* @__PURE__ */ new Set([...insertions.keys(), ...removals.keys()])].sort((a, b) => b - a)) {
|
|
33800
|
-
|
|
33822
|
+
output3 = output3.slice(0, offset) + (insertions.get(offset) ?? "") + output3.slice(removals.get(offset) ?? offset);
|
|
33801
33823
|
}
|
|
33802
|
-
return
|
|
33824
|
+
return output3;
|
|
33803
33825
|
}
|
|
33804
33826
|
function managedFlagRanges(command, tool) {
|
|
33805
33827
|
const tokens = [...command.matchAll(/(?:[^\s;'"`]+|'[^']*'|"(?:\\.|[^"\\])*")+|;/g)];
|
|
@@ -33845,10 +33867,10 @@ function hostedHelp(help, tool, flags, commands2) {
|
|
|
33845
33867
|
(match, separator) => separator ? `; artium-axi ${tool}` : match
|
|
33846
33868
|
)).replace(new RegExp("`(?:npx -y @knowttl/)?" + tool + "(?=\\s|`)([^\\r\\n`]*`)", "g"), "`artium-axi " + tool + "$1").replace(new RegExp(`^([ ]*cat [^\\r\\n|]+ \\| )(?:npx -y @knowttl/)?${tool}(?=\\s|$)`, "gm"), `$1artium-axi ${tool}`).replace(new RegExp(`\\buse ${tool} entry search\\b`, "g"), `use artium-axi ${tool} entry search`);
|
|
33847
33869
|
}
|
|
33848
|
-
function jsonHelp(
|
|
33849
|
-
const parsed = JSON.parse(
|
|
33870
|
+
function jsonHelp(output3) {
|
|
33871
|
+
const parsed = JSON.parse(output3);
|
|
33850
33872
|
if (typeof parsed.help !== "string" && !(Array.isArray(parsed.help) && parsed.help.every((item) => typeof item === "string"))) return { spans: [], document: parsed };
|
|
33851
|
-
const tokens = [...
|
|
33873
|
+
const tokens = [...output3.matchAll(/"(?:\\[\s\S]|[^"\\])*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null|[{}\[\]:,]/g)];
|
|
33852
33874
|
const spans = [];
|
|
33853
33875
|
let index = 0;
|
|
33854
33876
|
let helpFields = 0;
|
|
@@ -33876,8 +33898,8 @@ function jsonHelp(output2) {
|
|
|
33876
33898
|
value(false, true);
|
|
33877
33899
|
return { spans: helpFields === 1 ? spans : [], document: parsed };
|
|
33878
33900
|
}
|
|
33879
|
-
function toonHelp(
|
|
33880
|
-
const lines = [...
|
|
33901
|
+
function toonHelp(output3) {
|
|
33902
|
+
const lines = [...output3.matchAll(/[^\r\n]*(?:\r\n|\n|$)/g)].filter((line) => line[0]);
|
|
33881
33903
|
const spans = [];
|
|
33882
33904
|
const validation = lines.map((line) => line[0].replace(/\r?\n$/, ""));
|
|
33883
33905
|
let helpFields = 0;
|
|
@@ -33958,8 +33980,8 @@ accessExpiresAt: ${user.accessExpiresAt}
|
|
|
33958
33980
|
}
|
|
33959
33981
|
function toolsView(tools) {
|
|
33960
33982
|
if (!tools.length) return "tools: 0 available\nreason: No enabled runtime tool is available through your assigned roles.\ncontact: Ask an administrator to check your role assignments and enabled tools.\nnext[1]:\n - artium-axi whoami\n";
|
|
33961
|
-
return `tools[${tools.length}]{name,status,version}:
|
|
33962
|
-
${tools.map((tool) => ` ${tool.name},${tool.status},${JSON.stringify(tool.version)}`).join("\n")}
|
|
33983
|
+
return `tools[${tools.length}]{name,status,version,readOnly}:
|
|
33984
|
+
${tools.map((tool) => ` ${tool.name},${tool.status},${JSON.stringify(tool.version)},${tool.readOnly ?? true}`).join("\n")}
|
|
33963
33985
|
next[2]:
|
|
33964
33986
|
- artium-axi tools show ${tools[0].name}
|
|
33965
33987
|
- artium-axi ${tools[0].name} ${toolManifests[tools[0].name].discovery.example.join(" ")}
|
|
@@ -33973,6 +33995,7 @@ function toolView(tool) {
|
|
|
33973
33995
|
return `tool: ${tool.name}
|
|
33974
33996
|
version: ${JSON.stringify(tool.version)}
|
|
33975
33997
|
status: ${tool.status}
|
|
33998
|
+
readOnly: ${tool.readOnly ?? true}
|
|
33976
33999
|
commands[${commands2.length}]: ${commands2.join(",")}
|
|
33977
34000
|
example: artium-axi ${tool.name} ${manifest.discovery.example.join(" ")}
|
|
33978
34001
|
upstreamHelp: ${JSON.stringify(help)}
|
|
@@ -34055,15 +34078,15 @@ function claudeSettings(path, executable) {
|
|
|
34055
34078
|
if (!Array.isArray(sessions) || sessions.some((entry) => !object2(entry) || !Array.isArray(entry.hooks) || entry.hooks.some((hook) => !object2(hook)))) {
|
|
34056
34079
|
throw new ArtiumError("INVALID_INPUT", "Claude SessionStart hooks have an unsupported shape; no installation was written.");
|
|
34057
34080
|
}
|
|
34058
|
-
const
|
|
34081
|
+
const managed2 = { type: "command", command: process.execPath, args: [executable, "tools"], statusMessage: hookName };
|
|
34059
34082
|
let found = false;
|
|
34060
34083
|
const merged = sessions.map((entry) => ({ ...entry, hooks: entry.hooks.flatMap((hook) => {
|
|
34061
34084
|
if (hook.statusMessage !== hookName) return [hook];
|
|
34062
34085
|
if (found) return [];
|
|
34063
34086
|
found = true;
|
|
34064
|
-
return [
|
|
34087
|
+
return [managed2];
|
|
34065
34088
|
}) }));
|
|
34066
|
-
if (!found) merged.push({ hooks: [
|
|
34089
|
+
if (!found) merged.push({ hooks: [managed2] });
|
|
34067
34090
|
settings.hooks = { ...hooks, SessionStart: merged };
|
|
34068
34091
|
return JSON.stringify(settings, null, 2) + "\n";
|
|
34069
34092
|
}
|
|
@@ -34224,24 +34247,199 @@ next: artium-axi tools
|
|
|
34224
34247
|
throw new ArtiumError("DEVICE_CODE_EXPIRED");
|
|
34225
34248
|
}
|
|
34226
34249
|
|
|
34250
|
+
// src/cli/managed-session.ts
|
|
34251
|
+
import { fstatSync, writeSync } from "node:fs";
|
|
34252
|
+
import { Socket } from "node:net";
|
|
34253
|
+
|
|
34254
|
+
// src/server/runner/scan.ts
|
|
34255
|
+
function prefixLengths(bytes) {
|
|
34256
|
+
const lengths = new Uint32Array(bytes.length);
|
|
34257
|
+
let length = 0;
|
|
34258
|
+
for (let index = 1; index < bytes.length; index++) {
|
|
34259
|
+
while (length > 0 && bytes[index] !== bytes[length]) length = lengths[length - 1];
|
|
34260
|
+
if (bytes[index] === bytes[length]) length++;
|
|
34261
|
+
lengths[index] = length;
|
|
34262
|
+
}
|
|
34263
|
+
return lengths;
|
|
34264
|
+
}
|
|
34265
|
+
var SecretScanner = class {
|
|
34266
|
+
#patterns;
|
|
34267
|
+
#positions;
|
|
34268
|
+
#matched = false;
|
|
34269
|
+
constructor(secrets) {
|
|
34270
|
+
const forms = /* @__PURE__ */ new Set();
|
|
34271
|
+
for (const secret of secrets) {
|
|
34272
|
+
if (secret.length === 0) throw new Error("Secret values must be nonempty");
|
|
34273
|
+
const bytes = Buffer.from(secret, "utf8");
|
|
34274
|
+
forms.add(secret);
|
|
34275
|
+
forms.add(bytes.toString("base64"));
|
|
34276
|
+
forms.add(bytes.toString("base64url"));
|
|
34277
|
+
forms.add(encodeURIComponent(secret));
|
|
34278
|
+
forms.add(JSON.stringify(secret).slice(1, -1));
|
|
34279
|
+
}
|
|
34280
|
+
this.#patterns = [...forms].map((form) => {
|
|
34281
|
+
const bytes = Buffer.from(form, "utf8");
|
|
34282
|
+
return { bytes, prefixes: prefixLengths(bytes) };
|
|
34283
|
+
});
|
|
34284
|
+
this.#positions = {
|
|
34285
|
+
stdout: new Uint32Array(this.#patterns.length),
|
|
34286
|
+
stderr: new Uint32Array(this.#patterns.length)
|
|
34287
|
+
};
|
|
34288
|
+
}
|
|
34289
|
+
scan(stream, chunk) {
|
|
34290
|
+
if (this.#matched) return true;
|
|
34291
|
+
const positions = this.#positions[stream];
|
|
34292
|
+
for (let index = 0; index < this.#patterns.length; index++) {
|
|
34293
|
+
const { bytes, prefixes } = this.#patterns[index];
|
|
34294
|
+
let position = positions[index];
|
|
34295
|
+
for (const byte of chunk) {
|
|
34296
|
+
while (position > 0 && byte !== bytes[position]) position = prefixes[position - 1];
|
|
34297
|
+
if (byte === bytes[position]) position++;
|
|
34298
|
+
if (position === bytes.length) {
|
|
34299
|
+
this.#matched = true;
|
|
34300
|
+
return true;
|
|
34301
|
+
}
|
|
34302
|
+
}
|
|
34303
|
+
positions[index] = position;
|
|
34304
|
+
}
|
|
34305
|
+
return false;
|
|
34306
|
+
}
|
|
34307
|
+
};
|
|
34308
|
+
|
|
34309
|
+
// src/cli/managed-session.ts
|
|
34310
|
+
var sessionSchema = external_exports.strictObject({
|
|
34311
|
+
serverUrl: external_exports.string().url(),
|
|
34312
|
+
accessToken: external_exports.string().min(1),
|
|
34313
|
+
refreshToken: external_exports.string().min(1).optional(),
|
|
34314
|
+
status: external_exports.literal("authenticated").optional(),
|
|
34315
|
+
accessExpiresAt: external_exports.number().int().optional(),
|
|
34316
|
+
familyExpiresAt: external_exports.number().int().optional()
|
|
34317
|
+
});
|
|
34318
|
+
var ManagedSession = class _ManagedSession {
|
|
34319
|
+
constructor(session, channel) {
|
|
34320
|
+
this.session = session;
|
|
34321
|
+
this.channel = channel;
|
|
34322
|
+
this.secrets = [session.accessToken, ...session.refreshToken ? [session.refreshToken] : []];
|
|
34323
|
+
}
|
|
34324
|
+
session;
|
|
34325
|
+
channel;
|
|
34326
|
+
secrets;
|
|
34327
|
+
output = { stdout: "", stderr: "" };
|
|
34328
|
+
static async read() {
|
|
34329
|
+
try {
|
|
34330
|
+
for (const fd of [3, 4]) {
|
|
34331
|
+
const stat = fstatSync(fd);
|
|
34332
|
+
if (!stat.isFIFO() && !stat.isSocket()) throw new Error();
|
|
34333
|
+
}
|
|
34334
|
+
writeSync(4, Buffer.alloc(0));
|
|
34335
|
+
const input2 = new Socket({ fd: 3, readable: true, writable: false });
|
|
34336
|
+
const chunks = [];
|
|
34337
|
+
let bytes = 0;
|
|
34338
|
+
for await (const chunk of input2) {
|
|
34339
|
+
bytes += chunk.length;
|
|
34340
|
+
if (bytes > 64 * 1024) {
|
|
34341
|
+
input2.destroy();
|
|
34342
|
+
throw new Error();
|
|
34343
|
+
}
|
|
34344
|
+
chunks.push(chunk);
|
|
34345
|
+
}
|
|
34346
|
+
const session = sessionSchema.parse(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
34347
|
+
const channel = new Socket({ fd: 4, readable: false, writable: true });
|
|
34348
|
+
channel.on("error", () => {
|
|
34349
|
+
});
|
|
34350
|
+
return new _ManagedSession(session, channel);
|
|
34351
|
+
} catch {
|
|
34352
|
+
throw new ArtiumError(
|
|
34353
|
+
"INVALID_INPUT",
|
|
34354
|
+
"Managed session input or private descriptors are invalid.",
|
|
34355
|
+
"Supply session JSON on inherited pipe fd 3 and a writable private pipe on fd 4; close fd 3 after sending JSON."
|
|
34356
|
+
);
|
|
34357
|
+
}
|
|
34358
|
+
}
|
|
34359
|
+
checkServer(serverUrl) {
|
|
34360
|
+
if (this.session.serverUrl.replace(/\/$/, "") !== serverUrl) throw new ArtiumError("SESSION_SERVER_MISMATCH");
|
|
34361
|
+
}
|
|
34362
|
+
async authenticated(request, refresh) {
|
|
34363
|
+
try {
|
|
34364
|
+
return await request(this.session.accessToken);
|
|
34365
|
+
} catch (error62) {
|
|
34366
|
+
if (!(error62 instanceof ArtiumError) || error62.code !== "TOKEN_EXPIRED" || !this.session.refreshToken) throw error62;
|
|
34367
|
+
}
|
|
34368
|
+
const parsed = loginResponseSchema.safeParse(await refresh(this.session.refreshToken));
|
|
34369
|
+
if (!parsed.success || parsed.data.status !== "authenticated" || !parsed.data.accessToken || !parsed.data.refreshToken) {
|
|
34370
|
+
throw new ArtiumError("INVALID_RESPONSE");
|
|
34371
|
+
}
|
|
34372
|
+
this.session = { serverUrl: this.session.serverUrl, ...parsed.data };
|
|
34373
|
+
this.secrets.push(parsed.data.accessToken, parsed.data.refreshToken);
|
|
34374
|
+
try {
|
|
34375
|
+
await new Promise((resolve3, reject) => {
|
|
34376
|
+
this.channel.write(`${JSON.stringify(this.session)}
|
|
34377
|
+
`, (error62) => error62 ? reject(error62) : resolve3());
|
|
34378
|
+
});
|
|
34379
|
+
} catch {
|
|
34380
|
+
throw new ArtiumError(
|
|
34381
|
+
"INVALID_INPUT",
|
|
34382
|
+
"Managed session rotation could not be returned.",
|
|
34383
|
+
"Recover the session through the caller; do not retry using the old refresh token."
|
|
34384
|
+
);
|
|
34385
|
+
}
|
|
34386
|
+
return request(this.session.accessToken);
|
|
34387
|
+
}
|
|
34388
|
+
write(stream, text2) {
|
|
34389
|
+
this.output[stream] += text2;
|
|
34390
|
+
}
|
|
34391
|
+
flush() {
|
|
34392
|
+
this.channel.destroy();
|
|
34393
|
+
const scanner = new SecretScanner(this.secrets);
|
|
34394
|
+
if (scanner.scan("stdout", Buffer.from(this.output.stdout)) || scanner.scan("stderr", Buffer.from(this.output.stderr))) {
|
|
34395
|
+
throw new ArtiumError("SECRET_IN_OUTPUT");
|
|
34396
|
+
}
|
|
34397
|
+
process.stdout.write(this.output.stdout);
|
|
34398
|
+
process.stderr.write(this.output.stderr);
|
|
34399
|
+
}
|
|
34400
|
+
};
|
|
34401
|
+
|
|
34227
34402
|
// src/cli/main.ts
|
|
34403
|
+
var managed;
|
|
34404
|
+
function output2(stream, text2) {
|
|
34405
|
+
if (managed) managed.write(stream, text2);
|
|
34406
|
+
else process[stream].write(text2);
|
|
34407
|
+
}
|
|
34228
34408
|
async function main(args) {
|
|
34409
|
+
const managedMode = args[0] === "--managed-session";
|
|
34410
|
+
if (managedMode) {
|
|
34411
|
+
args = args.slice(1);
|
|
34412
|
+
if (["login", "passwd", "logout", "setup"].includes(args[0])) {
|
|
34413
|
+
throw new ArtiumError(
|
|
34414
|
+
"INVALID_INPUT",
|
|
34415
|
+
"This command is unavailable in managed session mode.",
|
|
34416
|
+
"The caller must acquire and manage tokens; use managed mode for authenticated calls."
|
|
34417
|
+
);
|
|
34418
|
+
}
|
|
34419
|
+
managed = await ManagedSession.read();
|
|
34420
|
+
}
|
|
34421
|
+
if (args.length === 1 && args[0] === "--version") {
|
|
34422
|
+
output2("stdout", `${cliVersion}
|
|
34423
|
+
`);
|
|
34424
|
+
return;
|
|
34425
|
+
}
|
|
34229
34426
|
const prefixHelp = args[0] === "--help";
|
|
34230
34427
|
if (prefixHelp) args = args.slice(1);
|
|
34231
34428
|
const command = args[0];
|
|
34232
34429
|
const help = helpCommand(prefixHelp ? [...args, "--help"] : args, prefixHelp);
|
|
34233
34430
|
if (prefixHelp && !help) throw new ArtiumError("INVALID_INPUT", void 0, "Use artium-axi --help for commands.");
|
|
34234
34431
|
if (help) {
|
|
34235
|
-
|
|
34432
|
+
output2("stdout", commandHelp(help));
|
|
34236
34433
|
return;
|
|
34237
34434
|
}
|
|
34238
34435
|
if (command === "setup") {
|
|
34239
|
-
|
|
34436
|
+
output2("stdout", await setup(args.slice(1)));
|
|
34240
34437
|
return;
|
|
34241
34438
|
}
|
|
34242
34439
|
if (command === "doctor") {
|
|
34243
34440
|
if (args.length !== 1) throw new ArtiumError("INVALID_INPUT", void 0, "Use artium-axi doctor.");
|
|
34244
|
-
await doctor();
|
|
34441
|
+
if (managed) await doctor(() => new Client(process.env, managed), (text2) => output2("stdout", text2));
|
|
34442
|
+
else await doctor();
|
|
34245
34443
|
return;
|
|
34246
34444
|
}
|
|
34247
34445
|
if (command === "login" || command === "passwd") {
|
|
@@ -34276,14 +34474,14 @@ async function main(args) {
|
|
|
34276
34474
|
return parsed.data;
|
|
34277
34475
|
});
|
|
34278
34476
|
if (session.status === "password-change-required") throw new ArtiumError("PASSWORD_CHANGE_REQUIRED");
|
|
34279
|
-
|
|
34477
|
+
output2("stdout", "status: authenticated\nnext: artium-axi tools\n");
|
|
34280
34478
|
} else {
|
|
34281
34479
|
await client2.changePassword(async (session) => {
|
|
34282
34480
|
const newPassword = await readPassword(stdin);
|
|
34283
34481
|
const currentPassword = session.status === "authenticated" ? await readPassword(false, "ARTIUM_AXI_CURRENT_PASSWORD") : void 0;
|
|
34284
34482
|
return { newPassword, ...currentPassword === void 0 ? {} : { currentPassword } };
|
|
34285
34483
|
});
|
|
34286
|
-
|
|
34484
|
+
output2("stdout", "status: password-changed\nnext: artium-axi login --username <username> --password-stdin\n");
|
|
34287
34485
|
}
|
|
34288
34486
|
return;
|
|
34289
34487
|
}
|
|
@@ -34308,10 +34506,10 @@ async function main(args) {
|
|
|
34308
34506
|
return false;
|
|
34309
34507
|
}
|
|
34310
34508
|
});
|
|
34311
|
-
|
|
34509
|
+
output2("stdout", `status: logged-out
|
|
34312
34510
|
remoteRevocation: ${verified ? "verified" : "unverified"}
|
|
34313
34511
|
`);
|
|
34314
|
-
if (!verified)
|
|
34512
|
+
if (!verified) output2("stdout", "next: Remote revocation is unverified. The local tokens were removed.\n");
|
|
34315
34513
|
return;
|
|
34316
34514
|
}
|
|
34317
34515
|
const home = args.length === 0;
|
|
@@ -34322,25 +34520,25 @@ remoteRevocation: ${verified ? "verified" : "unverified"}
|
|
|
34322
34520
|
throw new ArtiumError("INVALID_INPUT", "Unknown command, argument or flag.", "Use artium-axi --help for commands or artium-axi <command> --help for valid flags.");
|
|
34323
34521
|
}
|
|
34324
34522
|
const input2 = home || listing || whoami ? void 0 : await toolInput(command, args.slice(1));
|
|
34325
|
-
const client = new Client();
|
|
34326
|
-
const homeSession = home ? client.sessions.read() : void 0;
|
|
34327
|
-
if (home && !homeSession) throw new ArtiumError("TOKEN_INVALID", void 0, "Run artium-axi login --username <username> --password-stdin.");
|
|
34523
|
+
const client = new Client(process.env, managed);
|
|
34524
|
+
const homeSession = home && !managed ? client.sessions.read() : void 0;
|
|
34525
|
+
if (home && !managed && !homeSession) throw new ArtiumError("TOKEN_INVALID", void 0, "Run artium-axi login --username <username> --password-stdin.");
|
|
34328
34526
|
const result = await client.authenticated(home || whoami ? "/v1/me" : `/v1/tools${listing ? "" : `/${command}/run`}`, home || listing || whoami ? "GET" : "POST", input2, homeSession);
|
|
34329
34527
|
if (home || whoami) {
|
|
34330
34528
|
const parsed = meResponseSchema.safeParse(result);
|
|
34331
34529
|
if (!parsed.success) throw new ArtiumError("INVALID_RESPONSE");
|
|
34332
|
-
let
|
|
34530
|
+
let view = identityView(parsed.data, client.serverUrl);
|
|
34333
34531
|
if (home) {
|
|
34334
34532
|
const tools = toolsResponseSchema.safeParse(await client.authenticated("/v1/tools", "GET", void 0, homeSession));
|
|
34335
34533
|
if (!tools.success) throw new ArtiumError("INVALID_RESPONSE");
|
|
34336
34534
|
const executable = resolve2(process.argv[1]);
|
|
34337
34535
|
const local = relative(homedir3(), executable);
|
|
34338
34536
|
const bin = local && !isAbsolute(local) && !local.startsWith(`..${sep}`) && local !== ".." ? `~${sep}${local}` : executable;
|
|
34339
|
-
|
|
34537
|
+
view = `bin: ${JSON.stringify(bin)}
|
|
34340
34538
|
description: Run managed upstream tools through one local sign-in.
|
|
34341
|
-
${
|
|
34539
|
+
${view}${toolsView(tools.data.tools)}`;
|
|
34342
34540
|
}
|
|
34343
|
-
|
|
34541
|
+
output2("stdout", view);
|
|
34344
34542
|
} else if (listing) {
|
|
34345
34543
|
const parsed = toolsResponseSchema.safeParse(result);
|
|
34346
34544
|
if (!parsed.success) throw new ArtiumError("INVALID_RESPONSE");
|
|
@@ -34348,23 +34546,30 @@ ${output2}${toolsView(tools.data.tools)}`;
|
|
|
34348
34546
|
if (detail) {
|
|
34349
34547
|
const tool = tools.find((tool2) => tool2.name === args[2]);
|
|
34350
34548
|
if (!tool) throw new ArtiumError("TOOL_NOT_GRANTED", "The tool is not available to your account.", "Ask an administrator to check your role assignments and enabled tools.");
|
|
34351
|
-
|
|
34352
|
-
} else
|
|
34549
|
+
output2("stdout", toolView(tool));
|
|
34550
|
+
} else output2("stdout", toolsView(tools));
|
|
34353
34551
|
} else {
|
|
34354
34552
|
const parsed = runSuccessSchema.safeParse(result);
|
|
34355
34553
|
if (!parsed.success || parsed.data.source !== command) throw new ArtiumError("INVALID_RESPONSE");
|
|
34356
34554
|
const { stdout, stderr, exitCode, truncated } = parsed.data;
|
|
34357
|
-
if (truncated.stdout || truncated.stderr)
|
|
34555
|
+
if (truncated.stdout || truncated.stderr) output2("stderr", `artium-axi: output truncated (${["stdout", "stderr"].filter((stream) => truncated[stream]).join(", ")}).
|
|
34358
34556
|
`);
|
|
34359
|
-
|
|
34360
|
-
|
|
34557
|
+
output2("stdout", rewriteSuggestions(stdout, command, truncated.stdout));
|
|
34558
|
+
output2("stderr", rewriteSuggestions(stderr, command, truncated.stderr));
|
|
34361
34559
|
process.exitCode = exitCode;
|
|
34362
34560
|
}
|
|
34363
34561
|
}
|
|
34364
34562
|
function printError(response) {
|
|
34365
|
-
|
|
34563
|
+
output2("stdout", `error:
|
|
34366
34564
|
${Object.entries(response.error).map(([key, value]) => ` ${key}: ${JSON.stringify(value)}`).join("\n")}
|
|
34367
34565
|
`);
|
|
34368
34566
|
process.exitCode = errors[response.error.code].exitCode;
|
|
34369
34567
|
}
|
|
34370
|
-
main(process.argv.slice(2)).catch((error62) => printError(errorResponse(error62 instanceof ArtiumError ? error62 : new ArtiumError("INTERNAL_ERROR"), randomUUID3())))
|
|
34568
|
+
main(process.argv.slice(2)).catch((error62) => printError(errorResponse(error62 instanceof ArtiumError ? error62 : new ArtiumError("INTERNAL_ERROR"), randomUUID3()))).then(() => {
|
|
34569
|
+
try {
|
|
34570
|
+
managed?.flush();
|
|
34571
|
+
} catch {
|
|
34572
|
+
managed = void 0;
|
|
34573
|
+
printError(errorResponse(new ArtiumError("SECRET_IN_OUTPUT"), randomUUID3()));
|
|
34574
|
+
}
|
|
34575
|
+
});
|