@codiac.io/codiac-cli 1.3.253 → 1.3.254
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 +502 -163
- package/dist/apis/codiac-relay/gen/client.d.ts +12 -1
- package/dist/apis/codiac-relay/gen/client.js +23 -0
- package/dist/apis/codiac-relay/gen/client.js.map +1 -1
- package/dist/apis/codiac-relay/gen/contracts/types.gen.d.ts +120 -0
- package/dist/apis/codiac-relay/gen/contracts/types.gen.js.map +1 -1
- package/dist/apis/codiac-relay/i-client.d.ts +11 -0
- package/dist/apis/codiac-relay-nats/gen/client.d.ts +15 -0
- package/dist/apis/codiac-relay-nats/gen/client.js +27 -0
- package/dist/apis/codiac-relay-nats/gen/client.js.map +1 -1
- package/dist/apis/codiac-relay-nats/gen/contracts/types.gen.d.ts +49 -0
- package/dist/commands/ai/model/attach.d.ts +37 -0
- package/dist/commands/ai/model/attach.js +173 -0
- package/dist/commands/ai/model/attach.js.map +1 -0
- package/dist/commands/ai/model/install.d.ts +105 -0
- package/dist/commands/ai/model/install.js +655 -0
- package/dist/commands/ai/model/install.js.map +1 -0
- package/dist/commands/ai/model/list.d.ts +37 -0
- package/dist/commands/ai/model/list.js +160 -0
- package/dist/commands/ai/model/list.js.map +1 -0
- package/dist/commands/ai/model/uninstall.d.ts +40 -0
- package/dist/commands/ai/model/uninstall.js +144 -0
- package/dist/commands/ai/model/uninstall.js.map +1 -0
- package/dist/commands/ai/model/view.d.ts +35 -0
- package/dist/commands/ai/model/view.js +143 -0
- package/dist/commands/ai/model/view.js.map +1 -0
- package/dist/uilogic/model-spec.d.ts +40 -0
- package/dist/uilogic/model-spec.js +71 -0
- package/dist/uilogic/model-spec.js.map +1 -0
- package/oclif.manifest.json +568 -1
- package/package.json +1 -1
|
@@ -16820,3 +16820,52 @@ export type AiAgentProvisionResult = {
|
|
|
16820
16820
|
agent?: ProvisionedAgent;
|
|
16821
16821
|
error?: ProvisionError;
|
|
16822
16822
|
};
|
|
16823
|
+
export type ModelServeMode = "served" | "byo";
|
|
16824
|
+
export type ModelRuntimeTier = "cpu" | "gpu";
|
|
16825
|
+
export type ModelAttachSpec = {
|
|
16826
|
+
kind: "existing" | "new" | "none";
|
|
16827
|
+
agentName?: string;
|
|
16828
|
+
framework?: string;
|
|
16829
|
+
};
|
|
16830
|
+
export type AiModelProvisionRequest = {
|
|
16831
|
+
enterprise: string;
|
|
16832
|
+
cabinet: string;
|
|
16833
|
+
mode: ModelServeMode;
|
|
16834
|
+
model?: string;
|
|
16835
|
+
runtime?: ModelRuntimeTier;
|
|
16836
|
+
baseUrl?: string;
|
|
16837
|
+
key?: string;
|
|
16838
|
+
attach?: ModelAttachSpec;
|
|
16839
|
+
name?: string;
|
|
16840
|
+
overrides?: Record<string, any>;
|
|
16841
|
+
inputs?: Record<string, string>;
|
|
16842
|
+
correlationId?: string;
|
|
16843
|
+
socketId?: string;
|
|
16844
|
+
};
|
|
16845
|
+
export type ProvisionedModel = {
|
|
16846
|
+
handle: string;
|
|
16847
|
+
cabinet: string;
|
|
16848
|
+
mode: ModelServeMode;
|
|
16849
|
+
endpoint: string;
|
|
16850
|
+
exposure: string;
|
|
16851
|
+
status: string;
|
|
16852
|
+
};
|
|
16853
|
+
export type AiModelProvisionResult = {
|
|
16854
|
+
status: "ok" | "needs_action" | "failed";
|
|
16855
|
+
steps: Array<StepResult>;
|
|
16856
|
+
nextAction?: Remediation;
|
|
16857
|
+
model?: ProvisionedModel;
|
|
16858
|
+
error?: ProvisionError;
|
|
16859
|
+
};
|
|
16860
|
+
export type AiModelListRequest = {
|
|
16861
|
+
enterprise: string;
|
|
16862
|
+
cabinet?: string;
|
|
16863
|
+
};
|
|
16864
|
+
export type AiModelAttachRequest = {
|
|
16865
|
+
enterprise: string;
|
|
16866
|
+
cabinet: string;
|
|
16867
|
+
modelHandle: string;
|
|
16868
|
+
agentName: string;
|
|
16869
|
+
correlationId?: string;
|
|
16870
|
+
socketId?: string;
|
|
16871
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AtLeast } from '@codiac.io/codiac-common/contracts';
|
|
2
|
+
import { RootMap } from '../../../apis/codiac-api/contracts';
|
|
3
|
+
import { CommandBaseExec, IEnterpriseEntity } from '../../../command-base-exec';
|
|
4
|
+
export type IModelAttachRequest = IEnterpriseEntity & {
|
|
5
|
+
enterprise: string;
|
|
6
|
+
cabinet: string;
|
|
7
|
+
/** Handle of the already-provisioned model to wire in. */
|
|
8
|
+
model: string;
|
|
9
|
+
/** Name of the already-installed agent to wire the model into. */
|
|
10
|
+
agent: string;
|
|
11
|
+
};
|
|
12
|
+
export default class AiModelAttach extends CommandBaseExec<IModelAttachRequest> {
|
|
13
|
+
static description: string;
|
|
14
|
+
static examples: {
|
|
15
|
+
description: string;
|
|
16
|
+
command: string;
|
|
17
|
+
}[];
|
|
18
|
+
static flags: {
|
|
19
|
+
help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
|
|
20
|
+
enterpriseCode: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
21
|
+
model: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
22
|
+
agent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
23
|
+
cabinet: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
24
|
+
silent: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
25
|
+
echo: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
26
|
+
"to-script": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
27
|
+
};
|
|
28
|
+
static args: {};
|
|
29
|
+
protected adminOnly: boolean;
|
|
30
|
+
captureArgs(): Promise<[Partial<IModelAttachRequest>, boolean]>;
|
|
31
|
+
buildCandidate_silently(partial: AtLeast<IModelAttachRequest, "enterprise">, _rootMap: RootMap): Promise<IModelAttachRequest | undefined>;
|
|
32
|
+
buildCandidate_interactively(partial: AtLeast<IModelAttachRequest, "enterprise">, rootMap: RootMap, takeDefaults: boolean): Promise<IModelAttachRequest | undefined>;
|
|
33
|
+
buildCommandString(candidate: IModelAttachRequest, cmd: string[]): string[];
|
|
34
|
+
executeTask(candidate: IModelAttachRequest): Promise<void>;
|
|
35
|
+
private printStepResults;
|
|
36
|
+
private isSilentMode;
|
|
37
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const _ = require("lodash");
|
|
6
|
+
const command_base_exec_1 = require("../../../command-base-exec");
|
|
7
|
+
class AiModelAttach extends command_base_exec_1.CommandBaseExec {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.adminOnly = true;
|
|
11
|
+
}
|
|
12
|
+
captureArgs() {
|
|
13
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
14
|
+
const { flags } = yield this.parse(AiModelAttach);
|
|
15
|
+
let partial = {};
|
|
16
|
+
if (flags.enterpriseCode)
|
|
17
|
+
partial.enterprise = flags.enterpriseCode;
|
|
18
|
+
if (flags.model)
|
|
19
|
+
partial.model = flags.model.trim();
|
|
20
|
+
if (flags.agent)
|
|
21
|
+
partial.agent = flags.agent.trim();
|
|
22
|
+
if (flags.cabinet)
|
|
23
|
+
partial.cabinet = flags.cabinet;
|
|
24
|
+
return [partial, false];
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
buildCandidate_silently(partial, _rootMap) {
|
|
28
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
if (partial.model == undefined || _.isEmpty(partial.model))
|
|
30
|
+
throw new Error("Must specify a model handle (--model <handle>).");
|
|
31
|
+
if (partial.agent == undefined || _.isEmpty(partial.agent))
|
|
32
|
+
throw new Error("Must specify an agent name (--agent <name>).");
|
|
33
|
+
if (partial.cabinet == undefined || _.isEmpty(partial.cabinet))
|
|
34
|
+
throw new Error("Must specify cabinet (-c <cabinet>).");
|
|
35
|
+
return { enterprise: partial.enterprise, cabinet: partial.cabinet, model: partial.model, agent: partial.agent };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
buildCandidate_interactively(partial, rootMap, takeDefaults) {
|
|
39
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
if (partial.model == undefined || _.isEmpty(partial.model)) {
|
|
41
|
+
const typed = yield this._ui.prompt('model', 'Handle of the model to wire in:', true);
|
|
42
|
+
partial.model = (typed !== null && typed !== void 0 ? typed : '').trim();
|
|
43
|
+
}
|
|
44
|
+
if (partial.agent == undefined || _.isEmpty(partial.agent)) {
|
|
45
|
+
const typed = yield this._ui.prompt('agent', 'Name of the agent to wire the model into:', true);
|
|
46
|
+
partial.agent = (typed !== null && typed !== void 0 ? typed : '').trim();
|
|
47
|
+
}
|
|
48
|
+
const [cabinet] = yield this.rootMapUtils.promptForCabinet(partial.enterprise, rootMap, partial.cabinet, takeDefaults, undefined, partial.cabinet);
|
|
49
|
+
partial.cabinet = cabinet.name;
|
|
50
|
+
// Redeploying the agent touches the cabinet's cluster — ensure the tenant's cloud creds first,
|
|
51
|
+
// the same gate `cod asset deploy` uses.
|
|
52
|
+
const cluster = rootMap.clusters.find(c => c.name === cabinet.cluster);
|
|
53
|
+
if (cluster == undefined)
|
|
54
|
+
throw new Error(`Cabinet [${cabinet.name}] has no recognized cluster [${cabinet.cluster}].`);
|
|
55
|
+
yield this.cspContextualizer.promptForCloudProviderCreds(this.ioc, cluster.provider, takeDefaults, undefined, undefined, cluster.providerSubscriptionId);
|
|
56
|
+
return this.buildCandidate_silently(partial, rootMap);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
buildCommandString(candidate, cmd) {
|
|
60
|
+
cmd.push("-e", candidate.enterprise);
|
|
61
|
+
cmd.push("-m", candidate.model);
|
|
62
|
+
cmd.push("--agent", candidate.agent);
|
|
63
|
+
cmd.push("-c", candidate.cabinet);
|
|
64
|
+
cmd.push("--silent");
|
|
65
|
+
return cmd;
|
|
66
|
+
}
|
|
67
|
+
// NOTE: thin client over the SYNC `post.ai-model-attach`. The relay writes modelBaseUrl/modelApiKey
|
|
68
|
+
// ScopedSettings onto the agent asset + redeploys it; this only sends the request and drives the
|
|
69
|
+
// needs_action loop (e.g. NEEDS_AGENT_NAME). Unlike provision this is request/reply — no watcher.
|
|
70
|
+
executeTask(candidate) {
|
|
71
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
72
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
let agentName = candidate.agent;
|
|
74
|
+
const attemptedCodes = new Set();
|
|
75
|
+
while (true) {
|
|
76
|
+
const result = yield this.genRelayClient.Call.aiModelAttach({
|
|
77
|
+
enterprise: candidate.enterprise,
|
|
78
|
+
cabinet: candidate.cabinet,
|
|
79
|
+
modelHandle: candidate.model,
|
|
80
|
+
agentName,
|
|
81
|
+
});
|
|
82
|
+
if (result.error || !result.output) {
|
|
83
|
+
const msg = `Error attaching model [${candidate.model}] to agent [${agentName}]: ${(_a = result.message) !== null && _a !== void 0 ? _a : ''} ${(_c = (_b = result.error) === null || _b === void 0 ? void 0 : _b.message) !== null && _c !== void 0 ? _c : ''}`.trimEnd();
|
|
84
|
+
this.logger.error(msg, result.error);
|
|
85
|
+
this._ui.userMessage(`${this._styler.colorCritical('ERROR!')} ${msg}`);
|
|
86
|
+
throw (_d = result.error) !== null && _d !== void 0 ? _d : new Error(msg);
|
|
87
|
+
}
|
|
88
|
+
const out = result.output;
|
|
89
|
+
this.printStepResults(out.steps);
|
|
90
|
+
if (out.status === 'ok') {
|
|
91
|
+
this._ui.userMessage(`${this._styler.colorGood('WIRED')} Model ${this._styler.hilite(candidate.model)} is attached to agent ${this._styler.hilite(agentName)} in cabinet [${candidate.cabinet}] (agent redeployed).`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (out.status === 'failed') {
|
|
95
|
+
const msg = `Failed attaching model [${candidate.model}] to agent [${agentName}]: ${(_f = (_e = out.error) === null || _e === void 0 ? void 0 : _e.message) !== null && _f !== void 0 ? _f : ''}`.trimEnd();
|
|
96
|
+
this.logger.notice(msg, out);
|
|
97
|
+
this._ui.userMessage(`${this._styler.colorCritical('FAILED!')} ${msg}`);
|
|
98
|
+
throw new Error(msg);
|
|
99
|
+
}
|
|
100
|
+
// status === 'needs_action' — the only expected code here is NEEDS_AGENT_NAME (bad/missing agent).
|
|
101
|
+
const rem = out.nextAction;
|
|
102
|
+
const needs = (_h = (_g = rem.resolvedBy) === null || _g === void 0 ? void 0 : _g.needs) !== null && _h !== void 0 ? _h : [];
|
|
103
|
+
if (this.isSilentMode()) {
|
|
104
|
+
const missing = needs.map(f => f.name).join(', ');
|
|
105
|
+
const hint = ((_j = rem.resolvedBy) === null || _j === void 0 ? void 0 : _j.cliCommand) ? ` Resolve with: ${rem.resolvedBy.cliCommand}` : '';
|
|
106
|
+
throw new Error(`Cannot proceed in --silent mode: ${rem.reason}${missing ? ` (missing: ${missing})` : ''}.${hint}`);
|
|
107
|
+
}
|
|
108
|
+
if (needs.length > 0) {
|
|
109
|
+
for (const f of needs) {
|
|
110
|
+
const value = yield this._ui.prompt(f.name, `${rem.reason}\n${f.label}:`, true);
|
|
111
|
+
// The one field attach can re-resolve is the agent name — update the request for the re-call.
|
|
112
|
+
if (value != undefined && value.trim() !== "" && (f.name === 'agentName' || f.name === 'agent')) {
|
|
113
|
+
agentName = value.trim();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
this._ui.userMessage(this._styler.colorWarning(`Action needed (${rem.code}): ${rem.reason}`));
|
|
119
|
+
if ((_k = rem.resolvedBy) === null || _k === void 0 ? void 0 : _k.cliCommand)
|
|
120
|
+
this._ui.userMessage(`Resolve with: ${this._styler.hilite(rem.resolvedBy.cliCommand)}`);
|
|
121
|
+
const proceed = yield this._ui.promptConfirm('recheck', "Re-check after you've resolved this?", true);
|
|
122
|
+
if (!proceed)
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Loop guard: same code twice with no new input → abort.
|
|
126
|
+
if (attemptedCodes.has(rem.code) && needs.length === 0) {
|
|
127
|
+
const msg = `Still blocked on [${rem.code}]: ${rem.reason}`;
|
|
128
|
+
this._ui.userMessage(this._styler.colorCritical(msg));
|
|
129
|
+
throw new Error(msg);
|
|
130
|
+
}
|
|
131
|
+
attemptedCodes.add(rem.code);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
printStepResults(steps) {
|
|
136
|
+
for (const s of steps !== null && steps !== void 0 ? steps : []) {
|
|
137
|
+
const line = s.detail ? `${s.step}: ${s.status} — ${s.detail}` : `${s.step}: ${s.status}`;
|
|
138
|
+
if (s.status === 'failed')
|
|
139
|
+
this._ui.userMessage(this._styler.colorCritical(line));
|
|
140
|
+
else if (s.status === 'blocked')
|
|
141
|
+
this._ui.userMessage(this._styler.colorWarning(line));
|
|
142
|
+
else
|
|
143
|
+
this._ui.userMessage(this._styler.colorGood(line));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
isSilentMode() {
|
|
147
|
+
return this.argv.includes('--silent') || this.pipedInput != undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.default = AiModelAttach;
|
|
151
|
+
AiModelAttach.description = 'Wires an already-provisioned model into an already-installed agent (writes the model endpoint onto the agent and redeploys it so it uses the private model).';
|
|
152
|
+
AiModelAttach.examples = [
|
|
153
|
+
{
|
|
154
|
+
description: 'Wire the qwen3-dev model into the hermes-agent in the dev cabinet.',
|
|
155
|
+
command: '<%= config.bin %> <%= command.id %> -e ben -m qwen3-dev --agent hermes-agent -c dev',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
description: 'Non-interactive (scripted) invocation.',
|
|
159
|
+
command: '<%= config.bin %> <%= command.id %> -e ben -m qwen3-dev --agent hermes-agent -c dev --silent',
|
|
160
|
+
},
|
|
161
|
+
];
|
|
162
|
+
AiModelAttach.flags = {
|
|
163
|
+
help: core_1.Flags.help({ char: 'h' }),
|
|
164
|
+
enterpriseCode: core_1.Flags.string({ char: 'e', required: false, description: 'Code name identifying the target enterprise.' }),
|
|
165
|
+
model: core_1.Flags.string({ char: 'm', description: 'Handle of the provisioned model to wire in.' }),
|
|
166
|
+
agent: core_1.Flags.string({ description: 'Name of the already-installed agent to wire the model into.' }),
|
|
167
|
+
cabinet: core_1.Flags.string({ char: 'c', description: 'Cabinet the model and agent are deployed in.' }),
|
|
168
|
+
silent: core_1.Flags.boolean({ exclusive: ["echo", "to-script"], description: 'Executes without any user interaction; fails on missing or invalid arguments.' }),
|
|
169
|
+
echo: core_1.Flags.boolean({ exclusive: ["to-script", "silent"], description: 'Renders the equivalent non-interactive command for future use before executing.' }),
|
|
170
|
+
"to-script": core_1.Flags.boolean({ exclusive: ["echo", "silent"], description: 'Renders the equivalent non-interactive command without executing it.' }),
|
|
171
|
+
};
|
|
172
|
+
AiModelAttach.args = {};
|
|
173
|
+
//# sourceMappingURL=attach.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attach.js","sourceRoot":"","sources":["../../../../src/commands/ai/model/attach.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { AtLeast } from '@codiac.io/codiac-common/contracts';
|
|
2
|
+
import { CommandBaseExec, IEnterpriseEntity } from '../../../command-base-exec';
|
|
3
|
+
import { RootMap } from '../../../apis/codiac-api/contracts';
|
|
4
|
+
import { RuntimeTier } from '../../../uilogic/model-spec';
|
|
5
|
+
export type ModelSource = 'served' | 'byo';
|
|
6
|
+
export type ModelAttachKind = 'existing' | 'new' | 'none';
|
|
7
|
+
export interface IModelInstallRequest extends IEnterpriseEntity {
|
|
8
|
+
enterprise: string;
|
|
9
|
+
cabinet: string;
|
|
10
|
+
/** Whether to serve a catalog model in-cluster, or register a BYO OpenAI-compatible endpoint. */
|
|
11
|
+
source: ModelSource;
|
|
12
|
+
/** Catalog model kind (e.g. "qwen3") — required when source==="served". */
|
|
13
|
+
model?: string;
|
|
14
|
+
/** Compute tier for a served model — required when source==="served". */
|
|
15
|
+
runtime?: RuntimeTier;
|
|
16
|
+
/** OpenAI-compatible endpoint — required when source==="byo". */
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
/** API key for the BYO endpoint (stored as a secret server-side). */
|
|
19
|
+
key?: string;
|
|
20
|
+
/** How (and whether) to wire the model to an agent once it is ready. */
|
|
21
|
+
attach: ModelAttachKind;
|
|
22
|
+
/** Name of the agent to attach to — required when attach==="existing". */
|
|
23
|
+
agent?: string;
|
|
24
|
+
/** Agent framework to install and wire — required when attach==="new". */
|
|
25
|
+
framework?: string;
|
|
26
|
+
/** Optional handle for the model (server-assigned when omitted). */
|
|
27
|
+
name?: string;
|
|
28
|
+
/** Pre-supplied needs_action inputs (from --input key=value), threaded onto the candidate so
|
|
29
|
+
* executeTask never re-parses flags. */
|
|
30
|
+
inputs?: Record<string, string>;
|
|
31
|
+
}
|
|
32
|
+
export default class AiModelInstall extends CommandBaseExec<IModelInstallRequest> {
|
|
33
|
+
static description: string;
|
|
34
|
+
static examples: {
|
|
35
|
+
description: string;
|
|
36
|
+
command: string;
|
|
37
|
+
}[];
|
|
38
|
+
static flags: {
|
|
39
|
+
help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
|
|
40
|
+
enterpriseCode: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
41
|
+
source: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
42
|
+
model: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
43
|
+
runtime: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
44
|
+
'base-url': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
45
|
+
key: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
46
|
+
cabinet: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
47
|
+
name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
48
|
+
attach: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
49
|
+
agent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
50
|
+
framework: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
51
|
+
input: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
52
|
+
"take-defaults": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
53
|
+
silent: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
54
|
+
echo: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
55
|
+
"to-script": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
56
|
+
};
|
|
57
|
+
static args: {};
|
|
58
|
+
protected adminOnly: boolean;
|
|
59
|
+
captureArgs(): Promise<[
|
|
60
|
+
partial: Partial<IModelInstallRequest>,
|
|
61
|
+
takeDefaults: boolean,
|
|
62
|
+
explicit?: Partial<IModelInstallRequest>
|
|
63
|
+
]>;
|
|
64
|
+
buildCandidate_silently(partial: AtLeast<IModelInstallRequest, "enterprise">, _rootMap: RootMap, _explicit?: Partial<IModelInstallRequest>): Promise<IModelInstallRequest | undefined>;
|
|
65
|
+
buildCandidate_interactively(partial: AtLeast<IModelInstallRequest, "enterprise">, rootMap: RootMap, takeDefaults: boolean, explicit?: Partial<IModelInstallRequest>): Promise<IModelInstallRequest | undefined>;
|
|
66
|
+
private setSource;
|
|
67
|
+
private setModel;
|
|
68
|
+
private setRuntime;
|
|
69
|
+
private setAttach;
|
|
70
|
+
private setAgent;
|
|
71
|
+
private setFramework;
|
|
72
|
+
private setBaseUrl;
|
|
73
|
+
private setKey;
|
|
74
|
+
private setName;
|
|
75
|
+
buildCommandString(candidate: IModelInstallRequest, cmd: string[]): string[];
|
|
76
|
+
executeTask(candidate: IModelInstallRequest): Promise<void>;
|
|
77
|
+
/** Maps the resolved candidate (plus any remediation inputs gathered so far) to the
|
|
78
|
+
* `post.ai-model-provision` request shape. */
|
|
79
|
+
private toProvisionRequest;
|
|
80
|
+
/** attach === 'new': installs a fresh agent already wired to the just-provisioned model, entirely
|
|
81
|
+
* CLI-side (no new relay endpoint) — it reuses the existing `aiAgentProvision` op, threading the
|
|
82
|
+
* model's endpoint through `overrides` as the `modelBaseUrl`/`modelApiKey` chart values the
|
|
83
|
+
* hermes chart consumes. Runs the same ack-then-stream needs_action loop shape as `cod ai agent
|
|
84
|
+
* install`. Never exposes ingress (`hasIngress:false`) so the wiring cannot demand a host-naming
|
|
85
|
+
* strategy — the wiring is env-only and must succeed headlessly; the agent still reaches the
|
|
86
|
+
* model in-cluster. */
|
|
87
|
+
private wireNewAgent;
|
|
88
|
+
/** Derives the fresh agent's name from its framework — the framework's catalog default name
|
|
89
|
+
* (e.g. hermes → "hermes-agent") when known, else "<framework>-agent". */
|
|
90
|
+
private deriveAgentName;
|
|
91
|
+
/** Best-effort note about the freshly wired agent — prefers the server-resolved URL, else states
|
|
92
|
+
* it is reachable in-cluster (attach=new never requests ingress). */
|
|
93
|
+
private printAgentInfo;
|
|
94
|
+
/** Adds code-specific guidance ahead of the generic needs/consent handling for the model-side
|
|
95
|
+
* remediation codes. */
|
|
96
|
+
private renderRemediationGuidance;
|
|
97
|
+
/** One line per step: `<step>: <status>` (+ detail when present). */
|
|
98
|
+
private printStepResults;
|
|
99
|
+
/** Prints the ready model's handle + in-cluster (or BYO) endpoint. */
|
|
100
|
+
private printModelInfo;
|
|
101
|
+
/** Silent mode disables all prompting (the base lifecycle rebinds `_ui` to a throwing
|
|
102
|
+
* provider) — mirrors the same detection `CommandBaseExec.runAsync()` uses, so a
|
|
103
|
+
* `needs_action` response can fail fast with a clear message. */
|
|
104
|
+
private isSilentMode;
|
|
105
|
+
}
|